From 53ff340d9c338b2ca06acc1a277da5d2106f82a8 Mon Sep 17 00:00:00 2001 From: Shikhar Bhargava Date: Wed, 16 Sep 2026 15:22:10 -0700 Subject: [PATCH] SCAL-336249 Add whitelabled MCP server --- mcp/node-proxy-trusted-auth/.gitignore | 3 + mcp/node-proxy-trusted-auth/README.md | 162 ++ mcp/node-proxy-trusted-auth/env.template | 31 + mcp/node-proxy-trusted-auth/package-lock.json | 2414 +++++++++++++++++ mcp/node-proxy-trusted-auth/package.json | 25 + mcp/node-proxy-trusted-auth/src/index.ts | 223 ++ mcp/node-proxy-trusted-auth/src/login-page.ts | 151 ++ .../src/oauth-provider.ts | 216 ++ mcp/node-proxy-trusted-auth/src/sessions.ts | 139 + .../src/trusted-auth.ts | 26 + .../src/user-lookup.ts | 29 + mcp/node-proxy-trusted-auth/tsconfig.json | 12 + 12 files changed, 3431 insertions(+) create mode 100644 mcp/node-proxy-trusted-auth/.gitignore create mode 100644 mcp/node-proxy-trusted-auth/README.md create mode 100644 mcp/node-proxy-trusted-auth/env.template create mode 100644 mcp/node-proxy-trusted-auth/package-lock.json create mode 100644 mcp/node-proxy-trusted-auth/package.json create mode 100644 mcp/node-proxy-trusted-auth/src/index.ts create mode 100644 mcp/node-proxy-trusted-auth/src/login-page.ts create mode 100644 mcp/node-proxy-trusted-auth/src/oauth-provider.ts create mode 100644 mcp/node-proxy-trusted-auth/src/sessions.ts create mode 100644 mcp/node-proxy-trusted-auth/src/trusted-auth.ts create mode 100644 mcp/node-proxy-trusted-auth/src/user-lookup.ts create mode 100644 mcp/node-proxy-trusted-auth/tsconfig.json diff --git a/mcp/node-proxy-trusted-auth/.gitignore b/mcp/node-proxy-trusted-auth/.gitignore new file mode 100644 index 0000000..e6367a1 --- /dev/null +++ b/mcp/node-proxy-trusted-auth/.gitignore @@ -0,0 +1,3 @@ +node_modules +.env +dist diff --git a/mcp/node-proxy-trusted-auth/README.md b/mcp/node-proxy-trusted-auth/README.md new file mode 100644 index 0000000..11df13d --- /dev/null +++ b/mcp/node-proxy-trusted-auth/README.md @@ -0,0 +1,162 @@ + + +# White-Label MCP Proxy (Trusted Auth) + +A small, self-hosted reverse proxy that sits in front of ThoughtSpot's MCP server so your users never see "ThoughtSpot" — no `agent.thoughtspot.app` URL, no ThoughtSpot OAuth/SAML login page. Users sign in with just their email address on a page the proxy itself serves; the proxy resolves that email to a ThoughtSpot username and exchanges it for a ThoughtSpot session behind the scenes using [Trusted Authentication](https://developers.thoughtspot.com/docs/trusted-auth-secret-key) (a secret key configured once by a ThoughtSpot admin). + +The proxy does not reimplement any ThoughtSpot MCP tools. It forwards requests as-is to ThoughtSpot's hosted `/token/mcp` and `/token/sse` endpoints, rewriting only the `Authorization` header along the way. + +Because the login is a real OAuth 2.1 authorization flow (built on the official [`@modelcontextprotocol/sdk`](https://www.npmjs.com/package/@modelcontextprotocol/sdk) auth toolkit), it works natively from **claude.ai's connector UI** and Claude Desktop — no manual token pasting, no `mcp-remote` bridge required. + +## How it works + +``` +MCP client Your proxy ThoughtSpot +----------- ---------- ----------- +"Add connector" -----> GET /authorize + renders an email-only login page + (fully customizable, see below) + +user submits email ----> POST /login + 1. Look up ThoughtSpot ----> POST /api/rest/2.0/auth/token/full + username for that email { username: , secret_key } + <---- { token } (admin lookup) + 2. searchUsers({ email }) ----> GET /api/rest/2.0/users/search + <---- [{ name: , ... }] + 3. issues an authorization + code, redirects back to + the MCP client + +MCP client <----- redirect with ?code=... + +MCP client -----> POST /token + exchanges code for a proxy-issued access token + +MCP client -----> POST /mcp (Authorization: Bearer ) + 4. mint a ThoughtSpot ----> POST /api/rest/2.0/auth/token/full + token for the resolved { username, secret_key } + username via trusted auth <--- { token } + 5. forward the request ----> POST /token/mcp + Authorization: Authorization: Bearer @ + Bearer @ + <----------------------------- streamed MCP response +<-----------------------(piped straight back) +``` + +Two modes, chosen by which env vars you set: + +- **Trusted-auth mode** (`TS_SECRET_KEY` + `TS_HOST` + `BASE_URL`, optionally `TS_ADMIN_USERNAME`): the real OAuth + email-login flow described above. This is the mode that hides ThoughtSpot end-to-end, works from claude.ai directly, and supports multiple users. +- **Static-token mode** (`TS_TOKEN`): forwards every call using one fixed ThoughtSpot bearer token, with no OAuth and no per-user login — the proxy just checks that *some* bearer token is present. Simpler, single-tenant, useful for quick local testing or `mcp-remote`-style manual setups. If both `TS_TOKEN` and `TS_SECRET_KEY` are set, `TS_TOKEN` wins and a warning is logged. + +## Demo + +Open in [StackBlitz](https://stackblitz.com/github/thoughtspot/developer-examples/tree/main/mcp/node-proxy-trusted-auth) + +## Documentation + +- [Trusted Authentication (secret key)](https://developers.thoughtspot.com/docs/trusted-auth-secret-key) +- [ThoughtSpot MCP Server](https://github.com/thoughtspot/mcp-server) — the hosted server this proxy forwards to +- [Model Context Protocol](https://modelcontextprotocol.io/) / [MCP authorization spec](https://modelcontextprotocol.io/specification/draft/basic/authorization) + +## Setup + +Clone the developer examples repo. +```bash +git clone https://github.com/thoughtspot/developer-examples +cd developer-examples/mcp/node-proxy-trusted-auth +``` + +Install dependencies. +```bash +npm i +``` + +Copy the environment template and fill in your values. +```bash +cp env.template .env +``` + +```env +TS_HOST=your-instance.thoughtspot.cloud +TS_SECRET_KEY=your_thoughtspot_secret_key +BASE_URL=https://your-public-url.example.com + +PORT=3000 +``` + +**Getting `TS_SECRET_KEY`**: in ThoughtSpot, go to **Develop > Customizations > Security Settings** and enable Trusted Authentication to generate a secret key. Requires admin privileges. + +**`TS_ADMIN_USERNAME`** (optional, defaults to `tsadmin`): a ThoughtSpot username the proxy uses only to call the Users API and look up other users by email, before anyone has signed in. It doesn't need to be a real ThoughtSpot admin — just a username with rights to search users. Set it explicitly if your instance's service/admin account isn't named `tsadmin`. + +**`BASE_URL`**: OAuth requires a real, reachable HTTPS origin for its redirect flow — plain `http://localhost:3000` will not work with claude.ai. During local testing, tunnel your local server (e.g. `ngrok http 3000`) and set `BASE_URL` to the tunnel's HTTPS URL; in production, set it to your real deployed domain. + +Run it. +```bash +npm run dev +``` + +## Usage + +### With claude.ai + +In claude.ai, go to **Settings → Connectors → Add custom connector** and enter your proxy's URL with the `/mcp` path, e.g. `https://your-public-url.example.com/mcp`. claude.ai will discover the OAuth metadata automatically, redirect the user to the email-login page hosted by your proxy, and complete the connection once they submit a valid email — no token to copy or paste anywhere. + +### With curl (for testing the raw flow) + +```bash +curl -X POST http://localhost:3000/mcp \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -H "Accept: application/json, text/event-stream" \ + -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"my-client","version":"1.0.0"}}}' +``` + +The client only ever talks to your proxy's own URL — it never sees your real `TS_HOST`, `TS_SECRET_KEY`, or a ThoughtSpot-minted token. + +Available endpoints: +- `GET /authorize`, `POST /token`, `POST /register` — standard OAuth 2.1 endpoints (installed by `mcpAuthRouter`), plus the corresponding `/.well-known/oauth-authorization-server` and `/.well-known/oauth-protected-resource` metadata routes. +- `POST /login` — receives the email-form submission and completes the authorization-code redirect back to the MCP client. Not part of the OAuth spec itself — internal to this proxy's login page. +- `POST /mcp` — HTTP Streaming MCP endpoint (mirrors ThoughtSpot's `/token/mcp`) +- `GET /sse` — Server-Sent Events MCP endpoint (mirrors ThoughtSpot's `/token/sse`) + +## Customizing the login page + +Everything about how the email-entry page looks and reads lives in **`src/login-page.ts`** — it's the one file meant to be edited per customer/deployment. It exports two pure HTML-rendering functions (`renderLoginForm`, `renderLoginError`) with no OAuth logic in them at all; the rest of the flow (`src/oauth-provider.ts`) only calls them and never inlines markup itself. Replace the styling, logo, copy, or layout freely — the only contract to preserve is: +- the `
` posts to the `action` passed in, with `method="POST"` +- it has an `` +- it re-emits each entry in the `hidden` map as a matching ``, unchanged + +## Customizing identity lookup and token minting + +Two other functions are meant to be overridden for a real deployment: + +- **`findTsUsernameByEmail`** (`src/user-lookup.ts`) — resolves the email submitted on the login page to a ThoughtSpot username. The default implementation calls ThoughtSpot's `searchUsers` API directly. Replace this if you need a different lookup — e.g. matching against your own customer database, a different identity field, or additional validation — before falling back to (or instead of) ThoughtSpot's Users API. +- **`getAccessTokenForUser`** (`src/trusted-auth.ts`) — mints the actual ThoughtSpot access token for a resolved username via secret-key trusted auth. Replace this if you need a different token-issuance mechanism (e.g. a different auth API, additional claims/scopes, or custom validity handling) while keeping the rest of the OAuth flow (`src/oauth-provider.ts`) unchanged — it only calls this function and doesn't care how the token was actually obtained. + +Both are called from `src/oauth-provider.ts` (at login and on refresh) and `src/user-lookup.ts` respectively — neither contains any OAuth protocol logic itself, so they're safe to swap out independently of the rest of the flow. + +## Known limitations + +- Tool names and descriptions come from ThoughtSpot's MCP server as-is and are not rewritten by the proxy. Fully white-labeling tool text would require rewriting JSON-RPC response bodies, which this example does not do. +- OAuth clients, authorization codes, access tokens, and refresh tokens are stored in an in-memory `Map` (`src/sessions.ts`) — single-process and lost on restart. A real deployment should back this with a database or cache shared across instances. +- The ThoughtSpot token for the signed-in user is minted once at login (`exchangeAuthorizationCode`) — or again on refresh (`exchangeRefreshToken`) — with a validity matching the proxy's own 24h access-token TTL, and cached in `src/sessions.ts`. It is not re-minted on every `/mcp`/`/sse` call. +- Refresh tokens are single-use and rotated on every refresh: each refresh returns a new refresh token, and the old one stops working immediately. A refresh always re-derives the ThoughtSpot username strictly from server-side storage bound to that exact refresh token (never from anything the caller supplies), so refreshing can never hand back a different user's session — see `exchangeRefreshToken` in `src/oauth-provider.ts`. +- Email lookup assumes exactly one ThoughtSpot user per email address (uses the first match from `searchUsers`). +- `auto_create` is disabled when minting trusted-auth tokens — the ThoughtSpot username must already exist on your instance. +- The admin token used for email lookups (`TS_ADMIN_USERNAME`) is minted fresh on every login rather than cached; fine for a demo, worth caching until near-expiry in production. + +### Technology labels + +- TypeScript +- Node.js +- Express +- OAuth 2.1 diff --git a/mcp/node-proxy-trusted-auth/env.template b/mcp/node-proxy-trusted-auth/env.template new file mode 100644 index 0000000..cceecef --- /dev/null +++ b/mcp/node-proxy-trusted-auth/env.template @@ -0,0 +1,31 @@ +# --- Trusted-auth mode (recommended): real OAuth login, email address only --- +# Requires an admin to enable Trusted Authentication and generate a secret key in +# ThoughtSpot: Develop > Customizations > Security Settings. +TS_HOST=your-instance.thoughtspot.cloud +TS_SECRET_KEY=your_thoughtspot_secret_key + +# A ThoughtSpot username used ONLY to look up other users by email (calls the +# Users API on the proxy's behalf before anyone has logged in). Not used for +# anything else — does not need to be a ThoughtSpot admin, just needs rights +# to search users. Defaults to "tsadmin" if unset. +# TS_ADMIN_USERNAME=tsadmin + +# The proxy's own public HTTPS URL. Required for OAuth (clients like claude.ai +# discover /authorize and /token relative to this). During local testing this +# can be an ngrok/tunnel URL; in production it's your real deployed domain. +# Falls back to http://localhost:$PORT if unset, which only works for clients +# running on the same machine (e.g. mcp-remote in static-token mode below). +# BASE_URL=https://your-tunnel-or-domain.example.com + +# --- OR static-token mode: forward every call using one fixed ThoughtSpot token --- +# If TS_TOKEN is set, it takes priority over TS_SECRET_KEY/TS_ADMIN_USERNAME/BASE_URL +# and no OAuth/per-user login happens — the proxy just checks *some* bearer token is +# present and forwards using this fixed token for everyone. +# TS_TOKEN=your_thoughtspot_bearer_token + +# Port the proxy listens on. +PORT=3000 + +# Advanced: override the upstream ThoughtSpot MCP server the proxy forwards to. +# Only needed for a self-hosted ThoughtSpot MCP deployment. +# UPSTREAM_MCP_BASE_URL=https://agent.thoughtspot.app diff --git a/mcp/node-proxy-trusted-auth/package-lock.json b/mcp/node-proxy-trusted-auth/package-lock.json new file mode 100644 index 0000000..fa72d17 --- /dev/null +++ b/mcp/node-proxy-trusted-auth/package-lock.json @@ -0,0 +1,2414 @@ +{ + "name": "node-proxy-trusted-auth", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "node-proxy-trusted-auth", + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "1.30.0", + "@thoughtspot/rest-api-sdk": "2.28.0", + "dotenv": "17.2.3", + "express": "4.22.3", + "zod": "3.25.76" + }, + "devDependencies": { + "@types/express": "5.0.0", + "@types/node": "22.10.2", + "tsx": "4.20.6", + "typescript": "5.7.2" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@hono/node-server": { + "version": "2.1.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@hono/node-server/-/node-server-2.1.1.tgz", + "integrity": "sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.30.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz", + "integrity": "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9 || ^2.0.5", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/express": { + "version": "5.2.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/media-typer": { + "version": "1.1.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": { + "version": "1.1.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", + "license": "MIT", + "dependencies": { + "content-type": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/send": { + "version": "1.2.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/type-is/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@thoughtspot/rest-api-sdk": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/@thoughtspot/rest-api-sdk/-/rest-api-sdk-2.28.0.tgz", + "integrity": "sha512-iHOvSecnHB057+Lo+8cxOzECIYGwOlxNF0g+8fOUNCYcEpLHq3AEhU8jX5ejj1+zuTF/Q4BuKD/Ie1EZcVPvVQ==", + "license": "ThoughtSpot Development Tools End User License Agreement", + "dependencies": { + "es6-promise": "^4.2.4", + "url-parse": "^1.4.3", + "whatwg-fetch": "^3.0.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@types/express/-/express-5.0.0.tgz", + "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@types/express-serve-static-core/-/express-serve-static-core-5.1.3.tgz", + "integrity": "sha512-dPfW8NFiOF4wOHc7+N/QSxlY9cfSsenewGbAz8C8U/MULPd/YZ27LvJUIlzaXie7e6Ove9YunJGgC9tbHD2cKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.10.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/qs": { + "version": "6.15.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.8", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/body-parser/-/body-parser-1.20.8.tgz", + "integrity": "sha512-JNcyFQ64OiijEkPzUBTCe+hyPXUD/3LEldGQ6iF5LR1w00mx9o7xtDWHXBY2iItjdCFGoilOLNQbH943ut7pHA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.16.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.1.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/eventsource-parser/-/eventsource-parser-3.1.1.tgz", + "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "4.22.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/express/-/express-4.22.3.tgz", + "integrity": "sha512-Bdcs4+3qlpVlx2NRn6fgX2Ue2/gGRaPeawebgclM0ERSCqDpA+owF1fdPwjJUTAJWMTuAaxjDf+hzb0/4eKvvw==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.13", + "proxy-addr": "~2.0.7", + "qs": "~6.16.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.7.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/express-rate-limit/-/express-rate-limit-8.7.0.tgz", + "integrity": "sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express-rate-limit/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/express-rate-limit/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.8", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-tsconfig": { + "version": "4.14.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/get-tsconfig/-/get-tsconfig-4.14.3.tgz", + "integrity": "sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.13.8", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/hono/-/hono-4.13.8.tgz", + "integrity": "sha512-/Gng7NfoykZl2pjukW5Z6+8Yxm3BPRf86GTbQnt0SbySkvax4fyL4H3HhY1cCpBGmiW9XDRFzRV+CXK2W8QudQ==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.7.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/ip-address/-/ip-address-10.7.2.tgz", + "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.2.12", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/jose/-/jose-6.2.12.tgz", + "integrity": "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.8", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/qs": { + "version": "6.16.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/router/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tsx": { + "version": "4.20.6", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/tsx/-/tsx-4.20.6.tgz", + "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.7.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://jfrog-colo.corp.thoughtspot.com/artifactory/api/npm/npm-virtual/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + } + } +} diff --git a/mcp/node-proxy-trusted-auth/package.json b/mcp/node-proxy-trusted-auth/package.json new file mode 100644 index 0000000..b85a362 --- /dev/null +++ b/mcp/node-proxy-trusted-auth/package.json @@ -0,0 +1,25 @@ +{ + "name": "node-proxy-trusted-auth", + "private": true, + "version": "0.0.0", + "description": "White-label MCP proxy for ThoughtSpot using trusted auth (secret key) token exchange", + "type": "module", + "scripts": { + "dev": "tsx watch src/index.ts", + "start": "tsx src/index.ts" + }, + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "1.30.0", + "@thoughtspot/rest-api-sdk": "2.28.0", + "dotenv": "17.2.3", + "express": "4.22.3", + "zod": "3.25.76" + }, + "devDependencies": { + "@types/express": "5.0.0", + "@types/node": "22.10.2", + "tsx": "4.20.6", + "typescript": "5.7.2" + } +} diff --git a/mcp/node-proxy-trusted-auth/src/index.ts b/mcp/node-proxy-trusted-auth/src/index.ts new file mode 100644 index 0000000..6469db2 --- /dev/null +++ b/mcp/node-proxy-trusted-auth/src/index.ts @@ -0,0 +1,223 @@ +import "dotenv/config"; +import { Readable } from "node:stream"; +import express, { type Request, type Response } from "express"; +import { mcpAuthRouter } from "@modelcontextprotocol/sdk/server/auth/router.js"; +import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js"; +import { createLoginSubmitHandler, createThoughtSpotOAuthProvider } from "./oauth-provider.js"; + +// Headers that must not be blindly forwarded between hops (either because they +// describe *this* hop's transport, or because we are replacing them ourselves). +const HOP_BY_HOP_REQUEST_HEADERS = new Set([ + "host", + "connection", + "authorization", + "x-ts-host", + "content-length", +]); +const HOP_BY_HOP_RESPONSE_HEADERS = new Set([ + "connection", + "content-encoding", + "transfer-encoding", +]); + +function forwardableRequestHeaders(req: Request): HeadersInit { + const headers: Record = {}; + for (const [key, value] of Object.entries(req.headers)) { + if (HOP_BY_HOP_REQUEST_HEADERS.has(key.toLowerCase())) continue; + if (typeof value === "string") headers[key] = value; + } + return headers; +} + +function copyResponseHeaders(upstream: globalThis.Headers, res: Response) { + upstream.forEach((value, key) => { + if (HOP_BY_HOP_RESPONSE_HEADERS.has(key.toLowerCase())) return; + res.setHeader(key, value); + }); +} + +function getBearerToken(authorizationHeader: string | undefined): string | undefined { + if (!authorizationHeader) return undefined; + const [scheme, token] = authorizationHeader.split(" "); + if (scheme?.toLowerCase() !== "bearer" || !token) return undefined; + return token; +} + +// Accepts either a bare host ("my.thoughtspot.cloud") or a full URL and +// normalizes to a full https:// URL, which both the REST API SDK (trusted-auth +// token minting) and the upstream /token/mcp endpoint (Bearer token@host) accept. +function normalizeInstanceUrl(host: string): string { + return host.startsWith("http://") || host.startsWith("https://") + ? host + : `https://${host}`; +} + +export interface CreateProxyAppOptions { + tsHost: string; + tsSecretKey?: string; + tsAdminUsername?: string; + tsStaticToken?: string; + upstreamBaseUrl?: string; + baseUrl?: string; +} + +const DEFAULT_UPSTREAM_BASE_URL = "https://agent.thoughtspot.app"; +const DEFAULT_TS_ADMIN_USERNAME = "tsadmin"; + +export function createProxyApp(options: CreateProxyAppOptions) { + const { + tsHost: rawTsHost, + tsSecretKey, + tsAdminUsername, + tsStaticToken, + upstreamBaseUrl = DEFAULT_UPSTREAM_BASE_URL, + baseUrl, + } = options; + const tsHost = normalizeInstanceUrl(rawTsHost); + + if (!tsStaticToken && !tsSecretKey) { + throw new Error( + "Either TS_TOKEN (static-token mode) or TS_SECRET_KEY (trusted-auth mode) must be set.", + ); + } + + const app = express(); + const oauthEnabled = !tsStaticToken; + + if (oauthEnabled) { + if (!baseUrl) { + throw new Error("BASE_URL must be set when using trusted-auth mode (OAuth login)."); + } + + const providerOptions = { + tsHost, + tsSecretKey: tsSecretKey!, + tsAdminUsername: tsAdminUsername ?? DEFAULT_TS_ADMIN_USERNAME, + }; + const provider = createThoughtSpotOAuthProvider(providerOptions); + const issuerUrl = new URL(baseUrl); + + app.use( + mcpAuthRouter({ + provider, + issuerUrl, + resourceServerUrl: new URL("/mcp", baseUrl), + }), + ); + + // The email form (rendered by provider.authorize()) POSTs here — a + // separate path from /authorize, since that path is owned end-to-end by + // mcpAuthRouter's own authorizationHandler (mounted above), which expects + // full OAuth request params (response_type, code_challenge, ...) on every + // hit, not a plain email field. + app.post("/login", express.urlencoded({ extended: false }), createLoginSubmitHandler(providerOptions)); + + app.use(["/mcp", "/sse"], requireBearerAuth({ verifier: provider })); + } + + const forward = async (req: Request, res: Response, upstreamPath: string) => { + let tsAccessToken: string | undefined; + + if (tsStaticToken) { + // Static-token mode: one shared ThoughtSpot token, no per-user lookup, + // no OAuth — just a bearer token the caller must already know. + if (!getBearerToken(req.headers.authorization)) { + res.status(401).json({ error: "Missing bearer token" }); + return; + } + tsAccessToken = tsStaticToken; + } else { + // OAuth mode: requireBearerAuth() has already validated the token and + // attached the ThoughtSpot token minted at login time to req.auth — no + // re-minting happens here, so this is just a cache read. + tsAccessToken = (req.auth?.extra as { tsAccessToken?: string } | undefined)?.tsAccessToken; + if (!tsAccessToken) { + res.status(401).json({ error: "Unauthorized" }); + return; + } + } + + const upstreamUrl = new URL(upstreamPath, upstreamBaseUrl); + const queryIndex = req.originalUrl.indexOf("?"); + if (queryIndex !== -1) { + upstreamUrl.search = req.originalUrl.slice(queryIndex); + } + + const hasBody = !["GET", "HEAD"].includes(req.method); + const upstreamRes = await fetch(upstreamUrl, { + method: req.method, + headers: { + ...forwardableRequestHeaders(req), + authorization: `Bearer ${tsAccessToken}@${tsHost}`, + }, + body: hasBody ? (Readable.toWeb(req) as any) : undefined, + // Required by undici when streaming a request body. + duplex: hasBody ? "half" : undefined, + } as RequestInit); + + res.status(upstreamRes.status); + copyResponseHeaders(upstreamRes.headers, res); + + if (!upstreamRes.body) { + res.end(); + return; + } + Readable.fromWeb(upstreamRes.body as any).pipe(res); + }; + + app.all("/mcp", (req, res) => { + forward(req, res, "/token/mcp").catch((err) => { + console.error("Proxy error:", err); + if (!res.headersSent) res.status(500).json({ error: "Proxy error" }); + }); + }); + + app.all("/sse", (req, res) => { + forward(req, res, "/token/sse").catch((err) => { + console.error("Proxy error:", err); + if (!res.headersSent) res.status(500).json({ error: "Proxy error" }); + }); + }); + + return app; +} + +function main() { + const tsHost = process.env.TS_HOST; + if (!tsHost) { + throw new Error("TS_HOST environment variable is not set"); + } + + const tsSecretKey = process.env.TS_SECRET_KEY; + const tsAdminUsername = process.env.TS_ADMIN_USERNAME; + const tsStaticToken = process.env.TS_TOKEN; + if (tsStaticToken && tsSecretKey) { + console.warn( + "Both TS_TOKEN and TS_SECRET_KEY are set — using static-token mode (TS_TOKEN) and ignoring TS_SECRET_KEY.", + ); + } + + const port = process.env.PORT ? Number.parseInt(process.env.PORT, 10) : 3000; + const baseUrl = process.env.BASE_URL ?? `http://localhost:${port}`; + + const app = createProxyApp({ + tsHost, + tsSecretKey: tsStaticToken ? undefined : tsSecretKey, + tsAdminUsername, + tsStaticToken, + upstreamBaseUrl: process.env.UPSTREAM_MCP_BASE_URL, + baseUrl, + }); + + app.listen(port, () => { + const mode = tsStaticToken ? "static-token" : "trusted-auth (OAuth email login)"; + console.log(`MCP proxy listening on port ${port}`); + console.log(`Mode: ${mode}`); + console.log(`Endpoints: http://localhost:${port}/mcp, http://localhost:${port}/sse`); + if (!tsStaticToken) { + console.log(`OAuth base URL: ${baseUrl} (set BASE_URL to your public HTTPS URL for real clients)`); + } + }); +} + +main(); diff --git a/mcp/node-proxy-trusted-auth/src/login-page.ts b/mcp/node-proxy-trusted-auth/src/login-page.ts new file mode 100644 index 0000000..6a69577 --- /dev/null +++ b/mcp/node-proxy-trusted-auth/src/login-page.ts @@ -0,0 +1,151 @@ +/** + * Everything a customer needs to reskin the login screen lives in this one + * file. `renderLoginForm` / `renderLoginError` are pure HTML renderers with + * no OAuth logic — replace the markup/CSS/copy freely, just keep: + * - the posting to `action` with method="POST" + * - an the user fills in + * - one hidden per entry in `hidden`, verbatim (name + value), + * so the OAuth request survives the round trip through this page + */ + +export function renderLoginForm(opts: { + action: string; + hidden: Record; + error?: string; +}): string { + const hiddenInputs = Object.entries(opts.hidden) + .map(([name, value]) => ``) + .join("\n "); + + const errorBlock = opts.error + ? `

${escapeHtml(opts.error)}

` + : ""; + + return ` + + + + +Sign in + + + +
+

Sign in to continue

+ + ${errorBlock} + ${hiddenInputs} + + + + +
+ +`; +} + +export function renderLoginError(message: string): string { + return ` + + + + +Sign-in error + + + +
+

Sign-in failed

+

${escapeHtml(message)}

+
+ +`; +} + +function escapeHtml(value: string): string { + return value + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +} diff --git a/mcp/node-proxy-trusted-auth/src/oauth-provider.ts b/mcp/node-proxy-trusted-auth/src/oauth-provider.ts new file mode 100644 index 0000000..0681cb6 --- /dev/null +++ b/mcp/node-proxy-trusted-auth/src/oauth-provider.ts @@ -0,0 +1,216 @@ +import type { Request, Response } from "express"; +import type { + AuthorizationParams, + OAuthServerProvider, +} from "@modelcontextprotocol/sdk/server/auth/provider.js"; +import type { OAuthRegisteredClientsStore } from "@modelcontextprotocol/sdk/server/auth/clients.js"; +import type { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js"; +import type { + OAuthClientInformationFull, + OAuthTokens, +} from "@modelcontextprotocol/sdk/shared/auth.js"; +import { InvalidGrantError, InvalidTokenError } from "@modelcontextprotocol/sdk/server/auth/errors.js"; +import { renderLoginError, renderLoginForm } from "./login-page.js"; +import { getAccessTokenForUser } from "./trusted-auth.js"; +import { findTsUsernameByEmail } from "./user-lookup.js"; +import { + ACCESS_TOKEN_TTL_SEC, + consumeAuthorizationCode, + consumeRefreshToken, + createAuthorizationCode, + createSessionTokens, + getAccessTokenData, + getClient, + peekAuthorizationCode, + saveClient, +} from "./sessions.js"; + +export interface ThoughtSpotOAuthProviderOptions { + tsHost: string; + tsSecretKey: string; + tsAdminUsername: string; +} + +/** + * A minimal OAuth 2.1 authorization server whose "login page" is a single + * email field instead of a redirect to ThoughtSpot or a third-party IdP. See + * src/login-page.ts to change how that page looks. + */ +export function createThoughtSpotOAuthProvider( + options: ThoughtSpotOAuthProviderOptions, +): OAuthServerProvider { + const { tsHost, tsSecretKey } = options; + + const clientsStore: OAuthRegisteredClientsStore = { + getClient(clientId: string) { + return getClient(clientId); + }, + registerClient(client) { + // The SDK's registration handler has already generated client_id + // (and client_secret, if applicable) before calling this. + const full = client as OAuthClientInformationFull; + saveClient(full); + return full; + }, + }; + + return { + clientsStore, + + async authorize(client: OAuthClientInformationFull, params: AuthorizationParams, res: Response) { + const html = renderLoginForm({ + action: "/login", + hidden: { + client_id: client.client_id, + redirect_uri: params.redirectUri, + state: params.state ?? "", + code_challenge: params.codeChallenge, + }, + }); + res.status(200).type("html").send(html); + }, + + async challengeForAuthorizationCode( + _client: OAuthClientInformationFull, + authorizationCode: string, + ): Promise { + const data = peekAuthorizationCode(authorizationCode); + if (!data) { + throw new InvalidGrantError("Invalid or expired authorization code"); + } + return data.codeChallenge; + }, + + async exchangeAuthorizationCode( + client: OAuthClientInformationFull, + authorizationCode: string, + ): Promise { + const data = consumeAuthorizationCode(authorizationCode); + if (!data) { + throw new InvalidGrantError("Invalid or expired authorization code"); + } + if (data.clientId !== client.client_id) { + throw new InvalidGrantError("Authorization code was not issued to this client"); + } + // Mint the ThoughtSpot token once here, valid for as long as the proxy's + // own access token, and cache it — /mcp and /sse reuse this cached token + // instead of re-minting one from ThoughtSpot on every call. + const tsAccessToken = await getAccessTokenForUser( + tsHost, + data.tsUsername, + tsSecretKey, + ACCESS_TOKEN_TTL_SEC, + ); + const { accessToken, refreshToken, expiresIn } = createSessionTokens( + data.tsUsername, + tsAccessToken, + client.client_id, + ); + return { + access_token: accessToken, + refresh_token: refreshToken, + token_type: "bearer", + expires_in: expiresIn, + }; + }, + + async exchangeRefreshToken( + client: OAuthClientInformationFull, + refreshToken: string, + ): Promise { + // The ThoughtSpot username comes ONLY from what this refresh token + // resolves to in server-side storage — never from anything the caller + // supplies — so refreshing can never hand back a different user's + // ThoughtSpot session, no matter what a client requests. + const data = consumeRefreshToken(refreshToken); + if (!data) { + throw new InvalidGrantError("Invalid or expired refresh token"); + } + if (data.clientId !== client.client_id) { + throw new InvalidGrantError("Refresh token was not issued to this client"); + } + const tsAccessToken = await getAccessTokenForUser( + tsHost, + data.tsUsername, + tsSecretKey, + ACCESS_TOKEN_TTL_SEC, + ); + const { accessToken, refreshToken: newRefreshToken, expiresIn } = createSessionTokens( + data.tsUsername, + tsAccessToken, + client.client_id, + ); + return { + access_token: accessToken, + refresh_token: newRefreshToken, + token_type: "bearer", + expires_in: expiresIn, + }; + }, + + async verifyAccessToken(token: string): Promise { + const data = getAccessTokenData(token); + if (!data) { + throw new InvalidTokenError("Invalid or expired access token"); + } + return { + token, + clientId: data.clientId, + scopes: [], + expiresAt: Math.floor(data.expiresAt / 1000), + extra: { tsUsername: data.tsUsername, tsAccessToken: data.tsAccessToken }, + }; + }, + }; +} + +/** + * Handles the POST from the email form rendered by `authorize()` above: + * resolves the email to a ThoughtSpot username, mints an authorization code, + * and redirects back to the OAuth client's redirect_uri — completing the + * authorization-code step of the flow. + */ +export function createLoginSubmitHandler(options: ThoughtSpotOAuthProviderOptions) { + const { tsHost, tsSecretKey, tsAdminUsername } = options; + + return async (req: Request, res: Response) => { + const { email, client_id, redirect_uri, state, code_challenge } = req.body as Record< + string, + string | undefined + >; + + if (!email || !client_id || !redirect_uri || !code_challenge) { + res.status(400).type("html").send(renderLoginError("Missing required sign-in information.")); + return; + } + + let tsUsername: string | undefined; + try { + // If required get tsUsername and add here + tsUsername = await findTsUsernameByEmail(tsHost, email, tsAdminUsername, tsSecretKey); + } catch (err) { + console.error("Email lookup failed:", err); + } + + if (!tsUsername) { + // Deliberately generic: does not reveal whether the email exists. + res + .status(401) + .type("html") + .send(renderLoginError("We couldn't sign you in with that email address.")); + return; + } + + const code = createAuthorizationCode({ + tsUsername, + clientId: client_id, + codeChallenge: code_challenge, + redirectUri: redirect_uri, + }); + + const redirectUrl = new URL(redirect_uri); + redirectUrl.searchParams.set("code", code); + if (state) redirectUrl.searchParams.set("state", state); + res.redirect(302, redirectUrl.toString()); + }; +} diff --git a/mcp/node-proxy-trusted-auth/src/sessions.ts b/mcp/node-proxy-trusted-auth/src/sessions.ts new file mode 100644 index 0000000..67e91fb --- /dev/null +++ b/mcp/node-proxy-trusted-auth/src/sessions.ts @@ -0,0 +1,139 @@ +import { randomBytes } from "node:crypto"; +import type { OAuthClientInformationFull } from "@modelcontextprotocol/sdk/shared/auth.js"; + +/** + * All in-memory, single-process, non-persistent — fine for this example but + * not for a real deployment. Swap for Redis/a database if you need this to + * survive a restart or run across multiple instances. + */ + +// --- Registered OAuth clients (populated via Dynamic Client Registration) --- + +const clients = new Map(); + +export function getClient(clientId: string): OAuthClientInformationFull | undefined { + return clients.get(clientId); +} + +export function saveClient(client: OAuthClientInformationFull): void { + clients.set(client.client_id, client); +} + +// --- In-flight authorization codes (single-use, short TTL) --- + +interface AuthorizationCodeData { + tsUsername: string; + clientId: string; + codeChallenge: string; + redirectUri: string; + expiresAt: number; +} + +const AUTH_CODE_TTL_MS = 60_000; + +const authorizationCodes = new Map(); + +export function createAuthorizationCode( + data: Omit, +): string { + const code = randomBytes(32).toString("hex"); + authorizationCodes.set(code, { ...data, expiresAt: Date.now() + AUTH_CODE_TTL_MS }); + return code; +} + +// Read-only lookup (does not delete). Used to validate PKCE before the code +// is actually consumed via consumeAuthorizationCode(). +export function peekAuthorizationCode(code: string): AuthorizationCodeData | undefined { + const data = authorizationCodes.get(code); + if (!data || data.expiresAt < Date.now()) return undefined; + return data; +} + +// Authorization codes are single-use: this deletes the entry as it reads it. +export function consumeAuthorizationCode(code: string): AuthorizationCodeData | undefined { + const data = authorizationCodes.get(code); + if (!data) return undefined; + authorizationCodes.delete(code); + if (data.expiresAt < Date.now()) return undefined; + return data; +} + +// --- Access tokens (opaque, mapped to a ThoughtSpot username) --- +// +// The ThoughtSpot token itself is minted once per issuance (initial login, or +// a later refresh) and cached here — NOT re-minted on every /mcp or /sse +// call. Its validity is requested to match ACCESS_TOKEN_TTL_SEC (see +// trusted-auth.ts's validityTimeInSec param), so it stays usable for the +// proxy session's whole lifetime instead of expiring after ThoughtSpot's +// default (~5 min). +// +// Every access token is paired 1:1 with a refresh token at issuance (see +// createSessionTokens below) so a refresh always knows, from server-side +// storage alone, exactly which ThoughtSpot username and client it belongs to +// — the caller never gets to supply or influence that binding. + +interface AccessTokenData { + tsUsername: string; + tsAccessToken: string; + clientId: string; + expiresAt: number; +} + +interface RefreshTokenData { + tsUsername: string; + clientId: string; +} + +export const ACCESS_TOKEN_TTL_SEC = 24 * 60 * 60; + +const accessTokens = new Map(); +const refreshTokens = new Map(); + +/** + * Issues a fresh (access token, refresh token) pair bound to the given + * ThoughtSpot username and OAuth client. Used both at initial login + * (exchangeAuthorizationCode) and on every refresh (exchangeRefreshToken) — + * refreshing always re-derives the username from server-side storage, never + * from anything the caller passes in. + */ +export function createSessionTokens( + tsUsername: string, + tsAccessToken: string, + clientId: string, +): { + accessToken: string; + refreshToken: string; + expiresIn: number; +} { + const accessToken = randomBytes(32).toString("hex"); + const refreshToken = randomBytes(32).toString("hex"); + const expiresIn = ACCESS_TOKEN_TTL_SEC; + accessTokens.set(accessToken, { + tsUsername, + tsAccessToken, + clientId, + expiresAt: Date.now() + expiresIn * 1000, + }); + refreshTokens.set(refreshToken, { tsUsername, clientId }); + return { accessToken, refreshToken, expiresIn }; +} + +export function getAccessTokenData(token: string): AccessTokenData | undefined { + const data = accessTokens.get(token); + if (!data) return undefined; + if (data.expiresAt < Date.now()) { + accessTokens.delete(token); + return undefined; + } + return data; +} + +// Refresh tokens are single-use (rotated on every refresh, standard OAuth +// practice): this deletes the entry as it reads it, so a stolen-and-replayed +// refresh token stops working the moment the legitimate client refreshes. +export function consumeRefreshToken(token: string): RefreshTokenData | undefined { + const data = refreshTokens.get(token); + if (!data) return undefined; + refreshTokens.delete(token); + return data; +} diff --git a/mcp/node-proxy-trusted-auth/src/trusted-auth.ts b/mcp/node-proxy-trusted-auth/src/trusted-auth.ts new file mode 100644 index 0000000..d61dd65 --- /dev/null +++ b/mcp/node-proxy-trusted-auth/src/trusted-auth.ts @@ -0,0 +1,26 @@ +import { AuthenticationApi, createBasicConfig } from "@thoughtspot/rest-api-sdk"; + +/** + * Mints a ThoughtSpot full-access token for a given user via secret-key + * trusted authentication (https://developers.thoughtspot.com/docs/trusted-auth-secret-key). + * + * `auto_create` is left false: this example does not provision ThoughtSpot users on + * the fly. The username must already exist in the target ThoughtSpot instance. + * + * Consumers can edit this function to get Access Tokens for users as needed. + */ +export async function getAccessTokenForUser( + instanceUrl: string, + tsUsername: string, + secretKey: string, + validityTimeInSec?: number, +): Promise { + const authApi = new AuthenticationApi(createBasicConfig(instanceUrl)); + const { token } = await authApi.getFullAccessToken({ + username: tsUsername, + secret_key: secretKey, + auto_create: false, + ...(validityTimeInSec ? { validity_time_in_sec: validityTimeInSec } : {}), + }); + return token; +} diff --git a/mcp/node-proxy-trusted-auth/src/user-lookup.ts b/mcp/node-proxy-trusted-auth/src/user-lookup.ts new file mode 100644 index 0000000..ce56973 --- /dev/null +++ b/mcp/node-proxy-trusted-auth/src/user-lookup.ts @@ -0,0 +1,29 @@ +import { UsersApi, createBearerAuthenticationConfig } from "@thoughtspot/rest-api-sdk"; +import { getAccessTokenForUser } from "./trusted-auth.js"; + +/** + * Resolves a ThoughtSpot username from an email address. + * + * The Users API requires an authenticated ThoughtSpot session to call, even + * though the proxy has no logged-in user yet at this point in the flow — so + * we mint a short-lived trusted-auth token for a fixed admin/service username + * (TS_ADMIN_USERNAME) purely to perform this lookup. That username is never + * otherwise exposed or used for anything beyond this API call. + * + * This mints a fresh admin token on every lookup, which is simplest for an + * example. A real deployment should cache the admin token and only re-mint it + * once it's near expiry. + */ +export async function findTsUsernameByEmail( + instanceUrl: string, + email: string, + adminUsername: string, + secretKey: string, +): Promise { + const adminToken = await getAccessTokenForUser(instanceUrl, adminUsername, secretKey); + const config = createBearerAuthenticationConfig(instanceUrl, () => + Promise.resolve(adminToken), + ); + const users = await new UsersApi(config).searchUsers({ email }); + return users[0]?.name; +} diff --git a/mcp/node-proxy-trusted-auth/tsconfig.json b/mcp/node-proxy-trusted-auth/tsconfig.json new file mode 100644 index 0000000..3be673b --- /dev/null +++ b/mcp/node-proxy-trusted-auth/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "noEmit": true + }, + "include": ["src"] +}