@tabai/sdk 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -9
- package/dist/_shared/abi.d.ts.map +1 -1
- package/dist/_shared/abi.js +6 -3
- package/dist/_shared/abi.js.map +1 -1
- package/dist/_shared/chains.d.ts +4 -6
- package/dist/_shared/chains.d.ts.map +1 -1
- package/dist/_shared/chains.js +1 -2
- package/dist/_shared/chains.js.map +1 -1
- package/dist/_shared/keccak256.d.ts +6 -6
- package/dist/_shared/keccak256.js +6 -6
- package/dist/_shared/keccak256.js.map +1 -1
- package/dist/_shared/result.d.ts +5 -6
- package/dist/_shared/result.d.ts.map +1 -1
- package/dist/_shared/result.js +5 -6
- package/dist/_shared/result.js.map +1 -1
- package/dist/cli/client-config.d.ts +4 -5
- package/dist/cli/client-config.d.ts.map +1 -1
- package/dist/cli/client-config.js +4 -5
- package/dist/cli/client-config.js.map +1 -1
- package/dist/cli/connect.d.ts +1 -3
- package/dist/cli/connect.d.ts.map +1 -1
- package/dist/cli/connect.js +5 -3
- package/dist/cli/connect.js.map +1 -1
- package/dist/cli/doctor.d.ts +1 -3
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +1 -3
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/main.d.ts +1 -3
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +7 -6
- package/dist/cli/main.js.map +1 -1
- package/dist/errors.d.ts +2 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -4
- package/dist/errors.js.map +1 -1
- package/dist/http/client-402.d.ts +11 -13
- package/dist/http/client-402.d.ts.map +1 -1
- package/dist/http/client-402.js +12 -14
- package/dist/http/client-402.js.map +1 -1
- package/dist/http/headers.d.ts +9 -11
- package/dist/http/headers.d.ts.map +1 -1
- package/dist/http/headers.js +9 -11
- package/dist/http/headers.js.map +1 -1
- package/dist/http/index.d.ts +0 -2
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +0 -2
- package/dist/http/index.js.map +1 -1
- package/dist/http/metering-claim.d.ts +0 -2
- package/dist/http/metering-claim.d.ts.map +1 -1
- package/dist/http/metering-claim.js +0 -2
- package/dist/http/metering-claim.js.map +1 -1
- package/dist/index.d.ts +15 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -12
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +1 -4
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +1 -4
- package/dist/logger.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/json-schema.d.ts +1 -3
- package/dist/mcp/json-schema.d.ts.map +1 -1
- package/dist/mcp/json-schema.js +1 -3
- package/dist/mcp/json-schema.js.map +1 -1
- package/dist/mcp/json.d.ts +0 -2
- package/dist/mcp/json.d.ts.map +1 -1
- package/dist/mcp/json.js +0 -2
- package/dist/mcp/json.js.map +1 -1
- package/dist/mcp/registry-client.d.ts +0 -2
- package/dist/mcp/registry-client.d.ts.map +1 -1
- package/dist/mcp/registry-client.js +3 -4
- package/dist/mcp/registry-client.js.map +1 -1
- package/dist/mcp/schemas.d.ts +10 -8
- package/dist/mcp/schemas.d.ts.map +1 -1
- package/dist/mcp/schemas.js +5 -7
- package/dist/mcp/schemas.js.map +1 -1
- package/dist/mcp/server.d.ts +0 -2
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +4 -7
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/settings.d.ts +5 -1
- package/dist/mcp/settings.d.ts.map +1 -1
- package/dist/mcp/settings.js +28 -3
- package/dist/mcp/settings.js.map +1 -1
- package/dist/mcp/toolset.d.ts +2 -5
- package/dist/mcp/toolset.d.ts.map +1 -1
- package/dist/mcp/toolset.js +3 -6
- package/dist/mcp/toolset.js.map +1 -1
- package/dist/payments/config.d.ts +6 -8
- package/dist/payments/config.d.ts.map +1 -1
- package/dist/payments/config.js +5 -7
- package/dist/payments/config.js.map +1 -1
- package/dist/payments/index.d.ts +0 -2
- package/dist/payments/index.d.ts.map +1 -1
- package/dist/payments/index.js +0 -2
- package/dist/payments/index.js.map +1 -1
- package/dist/payments/registry.d.ts +11 -12
- package/dist/payments/registry.d.ts.map +1 -1
- package/dist/payments/registry.js +11 -12
- package/dist/payments/registry.js.map +1 -1
- package/dist/payments/strategy.d.ts +9 -5
- package/dist/payments/strategy.d.ts.map +1 -1
- package/dist/payments/strategy.js +9 -5
- package/dist/payments/strategy.js.map +1 -1
- package/dist/proxy/hooks.d.ts +3 -5
- package/dist/proxy/hooks.d.ts.map +1 -1
- package/dist/proxy/hooks.js +2 -4
- package/dist/proxy/hooks.js.map +1 -1
- package/dist/proxy/index.d.ts +2 -2
- package/dist/proxy/index.js +2 -2
- package/dist/proxy/index.js.map +1 -1
- package/dist/proxy/proxy.d.ts +3 -5
- package/dist/proxy/proxy.d.ts.map +1 -1
- package/dist/proxy/proxy.js +2 -4
- package/dist/proxy/proxy.js.map +1 -1
- package/dist/server/adapters/express.d.ts +0 -2
- package/dist/server/adapters/express.d.ts.map +1 -1
- package/dist/server/adapters/express.js +0 -2
- package/dist/server/adapters/express.js.map +1 -1
- package/dist/server/adapters/hono.d.ts +0 -2
- package/dist/server/adapters/hono.d.ts.map +1 -1
- package/dist/server/adapters/hono.js +0 -2
- package/dist/server/adapters/hono.js.map +1 -1
- package/dist/server/adapters/next.d.ts +0 -2
- package/dist/server/adapters/next.d.ts.map +1 -1
- package/dist/server/adapters/next.js +0 -2
- package/dist/server/adapters/next.js.map +1 -1
- package/dist/server/index.d.ts +5 -5
- package/dist/server/index.js +5 -5
- package/dist/server/index.js.map +1 -1
- package/dist/server/metering.d.ts +9 -11
- package/dist/server/metering.d.ts.map +1 -1
- package/dist/server/metering.js +6 -8
- package/dist/server/metering.js.map +1 -1
- package/dist/server/post-paid.d.ts +8 -10
- package/dist/server/post-paid.d.ts.map +1 -1
- package/dist/server/post-paid.js +7 -9
- package/dist/server/post-paid.js.map +1 -1
- package/dist/x402/client.d.ts +5 -6
- package/dist/x402/client.d.ts.map +1 -1
- package/dist/x402/client.js +6 -7
- package/dist/x402/client.js.map +1 -1
- package/dist/x402/server.d.ts +5 -5
- package/dist/x402/server.d.ts.map +1 -1
- package/dist/x402/server.js +2 -2
- package/dist/x402/server.js.map +1 -1
- package/dist/x402/wire.d.ts +1 -1
- package/dist/x402/wire.js +1 -1
- package/dist/x402/wire.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-config.js","sourceRoot":"","sources":["../../src/cli/client-config.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Where an MCP client keeps its server list, and how `tab connect` edits it.\n *\n * ## The claim this file has to earn\n *\n * Adopting Tab costs one line of configuration. That line is a `mcpServers`\n * stanza in a file the client already owns, and `tab connect` writes it. The\n * claim is only worth making if the write is safe to run, so three properties\n * are enforced here rather than documented:\n *\n * - **Idempotent.** Running it twice writes once. The second run compares the\n * merged document against what is on disk and, finding no change, writes\n * nothing and takes no backup. A connect command that rewrote the file every\n * time would churn a file the client also writes.\n * - **Backed up.** Any run that does change the file copies the previous\n * contents beside it first, named with the instant. This is somebody's editor\n * configuration, and it may hold servers this package has never heard of.\n * - **Never a key.** {@link assertNoSecret} walks the stanza before it is\n * written and refuses on anything key-shaped. The settlement signing key is\n * read from the environment at the moment a Settlement is signed. It does not\n * belong in a JSON file that a desktop client rewrites, syncs, and quotes in\n * its own logs.\n *\n * ## Only `mcpServers.tab` is touched\n *\n * The merge is by key, and every other server in the document is carried through\n * untouched, as is every top-level key the client keeps beside `mcpServers`. A\n * file that fails to parse is not overwritten: it is reported, because a\n * half-written config file is a client that will not start and the caller is the\n * only one who knows what was in it.\n *\n * Requirements: 25.5, 25.6\n */\n\nimport { copyFileSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\n\nimport type { Result } from \"../_shared/index.js\";\nimport { ok } from \"../_shared/index.js\";\n\nimport { fail, notFoundError, validationError } from \"../errors.js\";\nimport { isRecord, type JsonRecord } from \"../mcp/json.js\";\n\n/** One MCP client this command knows how to find. */\nexport interface McpClientTarget {\n /** The `--client` value, for example `claude-desktop`. */\n readonly id: string;\n /** What to call it in a message a person reads. */\n readonly label: string;\n /** Absolute path to its server list on this platform, or undefined where it has none. */\n readonly configPath: string | undefined;\n /** True when the file exists. A known client with no file yet is still a valid target. */\n readonly present: boolean;\n /**\n * True when the directory the file would live in exists, which is the signal\n * that the client is installed even though it has never been configured.\n */\n readonly installed: boolean;\n}\n\nconst home = (): string => homedir();\n\n/**\n * The per-platform locations, one entry per client.\n *\n * Each path is the client's own documented location. A client absent from a\n * platform has `undefined` there rather than a guessed path, so `--client` names\n * something real or fails by name.\n */\nfunction configPathFor(id: string, os: string, env: NodeJS.ProcessEnv, cwd: string): string | undefined {\n const appData = env[\"APPDATA\"];\n switch (id) {\n case \"claude-desktop\":\n if (os === \"darwin\") return join(home(), \"Library\", \"Application Support\", \"Claude\", \"claude_desktop_config.json\");\n if (os === \"win32\") return appData === undefined ? undefined : join(appData, \"Claude\", \"claude_desktop_config.json\");\n return join(home(), \".config\", \"Claude\", \"claude_desktop_config.json\");\n case \"claude-code\":\n // Project-scoped and checked in beside the code, which is the form a team\n // shares. The user-scoped list lives in `~/.claude.json` and is edited by\n // the CLI itself, so this command does not reach into it.\n return resolve(cwd, \".mcp.json\");\n case \"cursor\":\n return join(home(), \".cursor\", \"mcp.json\");\n case \"windsurf\":\n return join(home(), \".codeium\", \"windsurf\", \"mcp_config.json\");\n case \"vscode\":\n if (os === \"darwin\") return join(home(), \"Library\", \"Application Support\", \"Code\", \"User\", \"mcp.json\");\n if (os === \"win32\") return appData === undefined ? undefined : join(appData, \"Code\", \"User\", \"mcp.json\");\n return join(home(), \".config\", \"Code\", \"User\", \"mcp.json\");\n default:\n return undefined;\n }\n}\n\n/** Every client `connect` can target, in the order detection prefers them. */\nexport const MCP_CLIENT_IDS = [\"claude-code\", \"claude-desktop\", \"cursor\", \"windsurf\", \"vscode\"] as const;\n\nconst LABELS: Readonly<Record<string, string>> = {\n \"claude-code\": \"Claude Code (project .mcp.json)\",\n \"claude-desktop\": \"Claude Desktop\",\n cursor: \"Cursor\",\n windsurf: \"Windsurf\",\n vscode: \"Visual Studio Code\",\n};\n\nexport interface DetectOptions {\n readonly os?: string;\n readonly env?: NodeJS.ProcessEnv;\n readonly cwd?: string;\n}\n\nconst exists = (path: string | undefined): boolean => {\n if (path === undefined) return false;\n try {\n return existsSync(path) && statSync(path).isFile();\n } catch {\n return false;\n }\n};\n\nconst directoryExists = (path: string | undefined): boolean => {\n if (path === undefined) return false;\n try {\n return existsSync(dirname(path));\n } catch {\n return false;\n }\n};\n\n/** Every known client on this platform, whether or not it is installed. */\nexport function detectMcpClients(options: DetectOptions = {}): readonly McpClientTarget[] {\n const os = options.os ?? platform();\n const env = options.env ?? process.env;\n const cwd = options.cwd ?? process.cwd();\n return MCP_CLIENT_IDS.map((id) => {\n const configPath = configPathFor(id, os, env, cwd);\n return {\n id,\n label: LABELS[id] ?? id,\n configPath,\n present: exists(configPath),\n installed: directoryExists(configPath),\n };\n });\n}\n\n/**\n * The client to write to when the caller named none.\n *\n * A client with an existing config file wins over one that is merely installed,\n * because an existing file is evidence somebody uses it. Detection never picks\n * between two files silently: with more than one candidate present, the caller\n * is asked to name one.\n */\nexport function chooseMcpClient(\n targets: readonly McpClientTarget[],\n): Result<McpClientTarget> {\n const present = targets.filter((target) => target.present);\n if (present.length === 1) return ok(present[0]!);\n if (present.length > 1) {\n return fail(\n \"CONFLICT\",\n \"CLIENT_AMBIGUOUS\",\n `more than one MCP client is configured on this machine (${present.map((t) => t.id).join(\", \")}); name one with --client`,\n { details: { candidates: present.map((t) => t.id).join(\", \") } },\n );\n }\n const installed = targets.filter((target) => target.installed);\n if (installed.length === 1) return ok(installed[0]!);\n if (installed.length > 1) {\n return fail(\n \"CONFLICT\",\n \"CLIENT_AMBIGUOUS\",\n `more than one MCP client is installed on this machine (${installed.map((t) => t.id).join(\", \")}); name one with --client`,\n { details: { candidates: installed.map((t) => t.id).join(\", \") } },\n );\n }\n return notFoundError(\n \"CLIENT_NOT_FOUND\",\n `no MCP client config was found on this machine; name one with --client (${MCP_CLIENT_IDS.join(\", \")}) or point at the file with --config`,\n );\n}\n\n// ---------------------------------------------------------------- the stanza\n\n/** The `env` block the stanza carries. Non-secret values only, by construction. */\nexport type StanzaEnv = Readonly<Record<string, string>>;\n\nexport interface TabStanza {\n readonly command: string;\n readonly args: readonly string[];\n readonly env?: StanzaEnv;\n}\n\n/** The key the stanza is written under. One server, one name, always this one. */\nexport const TAB_SERVER_KEY = \"tab\";\n\n/**\n * Builds the stanza.\n *\n * `npx -y @tabai/sdk mcp` rather than a path: the client launches it, `npx`\n * resolves the published package, and nothing has to be installed first. That is\n * the one line the adoption claim rests on.\n *\n * The `env` block carries only what the server reads and a person could publish:\n * an RPC endpoint, a chain id and a read API URL. The names are the ones in the\n * tracked environment contract, `MONAD_RPC_URL`, `MONAD_CHAIN_ID` and\n * `NEXT_PUBLIC_REGISTRY_API_URL`, so a stanza and a `.env` agree.\n */\nexport function buildTabStanza(env: StanzaEnv = {}): TabStanza {\n const entries = Object.entries(env).filter(([, value]) => typeof value === \"string\" && value.trim() !== \"\");\n return {\n command: \"npx\",\n args: [\"-y\", \"@tabai/sdk\", \"mcp\"],\n ...(entries.length === 0 ? {} : { env: Object.fromEntries(entries) }),\n };\n}\n\n/** Anything that looks like a signing key, by name or by shape. */\nconst SECRET_NAME = /(PRIVATE[_-]?KEY|SECRET|MNEMONIC|SEED[_-]?PHRASE|PASSWORD|API[_-]?KEY|TOKEN|CREDENTIAL)/i;\nconst SECRET_SHAPE = /^(0x)?[0-9a-fA-F]{64}$/;\n\n/**\n * Refuses a stanza that carries a secret.\n *\n * Both a name check and a shape check, because either alone is defeated by the\n * obvious mistake: a key under an innocuous name has the shape, and an empty\n * placeholder under `PRIVATE_KEY` has the name. This runs on the merged document\n * before it is written, so it also catches a secret that was already in the file\n * under the `tab` key from an earlier hand edit.\n */\nexport function assertNoSecret(stanza: unknown, where = \"the tab stanza\"): Result<void> {\n const problems: string[] = [];\n const walk = (value: unknown, path: string): void => {\n if (typeof value === \"string\") {\n if (SECRET_SHAPE.test(value.trim())) problems.push(`${path} holds a 32-byte hex value, which is the shape of a signing key`);\n return;\n }\n if (Array.isArray(value)) {\n value.forEach((entry, index) => walk(entry, `${path}[${index}]`));\n return;\n }\n if (isRecord(value)) {\n for (const [key, entry] of Object.entries(value)) {\n const child = path === \"\" ? key : `${path}.${key}`;\n if (SECRET_NAME.test(key)) problems.push(`${child} names a credential`);\n walk(entry, child);\n }\n }\n };\n walk(stanza, \"\");\n if (problems.length > 0) {\n return validationError(\n \"STANZA_CARRIES_SECRET\",\n `${where} would carry a credential and was not written: ${problems.join(\"; \")}. Settlement signing keys are read from the environment at run time and never from a client config file.`,\n { details: { problems: problems.join(\"; \") } },\n );\n }\n return ok(undefined);\n}\n\n// ---------------------------------------------------------------- merge\n\nexport interface MergeOutcome {\n /** The document as it should be on disk. */\n readonly merged: JsonRecord;\n /** The document that was on disk, or an empty object when the file did not exist. */\n readonly previous: JsonRecord;\n /** False when the file already said exactly this, which is the idempotent case. */\n readonly changed: boolean;\n /** Whether a `tab` stanza was already there, so a message can say added or updated. */\n readonly hadStanza: boolean;\n}\n\n/** Two-space JSON with a trailing newline, which is what every one of these files uses. */\nexport const renderConfig = (document: unknown): string => `${JSON.stringify(document, null, 2)}\\n`;\n\n/**\n * Merges the stanza into a document, leaving every other key alone.\n *\n * `changed` compares rendered text rather than object identity, so a run that\n * produces the same file is reported as no change even when the objects differ\n * by key order.\n */\nexport function mergeTabStanza(previous: JsonRecord, stanza: TabStanza): Result<MergeOutcome> {\n const existingServers = previous[\"mcpServers\"];\n if (existingServers !== undefined && !isRecord(existingServers)) {\n return validationError(\n \"CONFIG_MCP_SERVERS_INVALID\",\n \"the config file has an `mcpServers` key that is not an object, so it was left alone rather than replaced\",\n );\n }\n const servers: Record<string, unknown> = { ...(isRecord(existingServers) ? existingServers : {}) };\n const hadStanza = servers[TAB_SERVER_KEY] !== undefined;\n servers[TAB_SERVER_KEY] = stanza;\n\n // Only the `tab` stanza is checked. Another server's API key is that server's\n // business and carrying it through untouched is the whole point of merging\n // rather than replacing -- refusing on it would make `connect` fail on exactly\n // the machines that already use MCP. The guarantee this command owes is about\n // what it writes, and what it writes is the stanza.\n const secret = assertNoSecret(servers[TAB_SERVER_KEY], `the \\`${TAB_SERVER_KEY}\\` stanza`);\n if (!secret.ok) return secret;\n\n const merged: JsonRecord = { ...previous, mcpServers: servers };\n\n return ok({\n merged,\n previous,\n changed: renderConfig(previous) !== renderConfig(merged),\n hadStanza,\n });\n}\n\n// ---------------------------------------------------------------- the file\n\nexport interface ReadConfigOutcome {\n readonly document: JsonRecord;\n readonly existed: boolean;\n readonly raw: string;\n}\n\n/**\n * Reads a client config.\n *\n * A file that does not exist is an empty document and not an error: connecting a\n * client that has never been configured is the common case. A file that exists\n * and does not parse *is* an error, and nothing is written over it.\n */\nexport function readClientConfig(path: string): Result<ReadConfigOutcome> {\n if (!existsSync(path)) return ok({ document: {}, existed: false, raw: \"\" });\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch (error) {\n return fail(\"UNAVAILABLE\", \"CONFIG_UNREADABLE\", `\\`${path}\\` could not be read`, {\n details: { path },\n cause: { code: \"READ_FAILED\", message: error instanceof Error ? error.message : String(error) },\n });\n }\n if (raw.trim() === \"\") return ok({ document: {}, existed: true, raw });\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (error) {\n return validationError(\n \"CONFIG_MALFORMED\",\n `\\`${path}\\` is not valid JSON, so it was left untouched: ${error instanceof Error ? error.message : String(error)}`,\n { details: { path } },\n );\n }\n if (!isRecord(parsed)) {\n return validationError(\"CONFIG_MALFORMED\", `\\`${path}\\` must hold a JSON object at its top level`, {\n details: { path },\n });\n }\n return ok({ document: parsed, existed: true, raw });\n}\n\n/** Where a backup of `path` goes, named with the instant so two runs never collide. */\nexport const backupPathFor = (path: string, at: Date): string =>\n `${path}.tab-backup-${at.toISOString().replace(/[:.]/g, \"-\")}`;\n\nexport interface WriteOutcome {\n readonly path: string;\n /** Absent when nothing was written, which is the idempotent case. */\n readonly backupPath?: string;\n readonly wrote: boolean;\n}\n\n/** Writes the merged document, taking a backup first when a file was already there. */\nexport function writeClientConfig(\n path: string,\n outcome: MergeOutcome,\n existed: boolean,\n at: Date,\n): Result<WriteOutcome> {\n if (!outcome.changed) return ok({ path, wrote: false });\n try {\n mkdirSync(dirname(path), { recursive: true });\n } catch (error) {\n return fail(\"UNAVAILABLE\", \"CONFIG_DIRECTORY_UNWRITABLE\", `\\`${dirname(path)}\\` could not be created`, {\n details: { path },\n cause: { code: \"MKDIR_FAILED\", message: error instanceof Error ? error.message : String(error) },\n });\n }\n\n let backupPath: string | undefined;\n if (existed) {\n backupPath = backupPathFor(path, at);\n try {\n copyFileSync(path, backupPath);\n } catch (error) {\n return fail(\"UNAVAILABLE\", \"CONFIG_BACKUP_FAILED\", `\\`${path}\\` could not be backed up, so it was not changed`, {\n details: { path, backupPath },\n cause: { code: \"COPY_FAILED\", message: error instanceof Error ? error.message : String(error) },\n });\n }\n }\n\n try {\n writeFileSync(path, renderConfig(outcome.merged), \"utf8\");\n } catch (error) {\n return fail(\"UNAVAILABLE\", \"CONFIG_UNWRITABLE\", `\\`${path}\\` could not be written`, {\n details: { path },\n cause: { code: \"WRITE_FAILED\", message: error instanceof Error ? error.message : String(error) },\n });\n }\n return ok({ path, wrote: true, ...(backupPath === undefined ? {} : { backupPath }) });\n}\n\n// ---------------------------------------------------------------- the diff\n\n/**\n * A unified diff of two JSON documents, line by line.\n *\n * Written here rather than pulled in because it is a longest-common-subsequence\n * over at most a few hundred lines and the alternative is a dependency for a\n * command that prints one. `connect` shows this before it claims to have done\n * anything, because a command that edits a file the user did not open owes them\n * a look at what it changed.\n */\nexport function unifiedDiff(before: string, after: string, context = 2): string {\n const a = before === \"\" ? [] : before.replace(/\\n$/, \"\").split(\"\\n\");\n const b = after === \"\" ? [] : after.replace(/\\n$/, \"\").split(\"\\n\");\n\n // LCS table. Both sides are one JSON document, so the quadratic cost is a few\n // hundred thousand cells at worst and is paid once, on a command a person ran.\n const lengths: number[][] = Array.from({ length: a.length + 1 }, () => new Array<number>(b.length + 1).fill(0));\n for (let i = a.length - 1; i >= 0; i -= 1) {\n for (let j = b.length - 1; j >= 0; j -= 1) {\n lengths[i]![j] = a[i] === b[j] ? lengths[i + 1]![j + 1]! + 1 : Math.max(lengths[i + 1]![j]!, lengths[i]![j + 1]!);\n }\n }\n\n const lines: { readonly mark: \" \" | \"-\" | \"+\"; readonly text: string }[] = [];\n let i = 0;\n let j = 0;\n while (i < a.length && j < b.length) {\n if (a[i] === b[j]) {\n lines.push({ mark: \" \", text: a[i]! });\n i += 1;\n j += 1;\n } else if (lengths[i + 1]![j]! >= lengths[i]![j + 1]!) {\n lines.push({ mark: \"-\", text: a[i]! });\n i += 1;\n } else {\n lines.push({ mark: \"+\", text: b[j]! });\n j += 1;\n }\n }\n for (; i < a.length; i += 1) lines.push({ mark: \"-\", text: a[i]! });\n for (; j < b.length; j += 1) lines.push({ mark: \"+\", text: b[j]! });\n\n // Keep only the changed lines and `context` lines either side, so a large\n // config with one added server prints as one hunk.\n const keep = new Set<number>();\n lines.forEach((line, index) => {\n if (line.mark === \" \") return;\n for (let k = Math.max(0, index - context); k <= Math.min(lines.length - 1, index + context); k += 1) keep.add(k);\n });\n\n // Nothing changed is no diff at all, not one elision marker.\n if (keep.size === 0) return \"\";\n\n const rendered: string[] = [];\n let skipping = false;\n lines.forEach((line, index) => {\n if (keep.has(index)) {\n rendered.push(`${line.mark}${line.text}`);\n skipping = false;\n } else if (!skipping) {\n rendered.push(\"@@\");\n skipping = true;\n }\n });\n return rendered.join(\"\\n\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"client-config.js","sourceRoot":"","sources":["../../src/cli/client-config.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Where an MCP client keeps its server list, and how `tab connect` edits it.\n *\n * ## The claim this file has to earn\n *\n * Adopting Tab costs one line of configuration. That line is a `mcpServers`\n * stanza in a file the client already owns, and `tab connect` writes it. The\n * claim is only worth making if the write is safe to run, so three properties\n * are enforced here rather than documented:\n *\n * - **Idempotent.** Running it twice writes once. The second run compares the\n * merged document against what is on disk and, finding no change, writes\n * nothing and takes no backup. A connect command that rewrote the file every\n * time would churn a file the client also writes.\n * - **Backed up.** Any run that does change the file copies the previous\n * contents beside it first, named with the instant. This is somebody's editor\n * configuration, and it may hold servers this package has never heard of.\n * - **Never a key.** {@link assertNoSecret} walks the stanza before it is\n * written and refuses on anything key-shaped. The settlement signing key is\n * read from the environment at the moment a Settlement is signed. It does not\n * belong in a JSON file that a desktop client rewrites, syncs, and quotes in\n * its own logs.\n *\n * ## Only `mcpServers.tab` is touched\n *\n * The merge is by key, and every other server in the document is carried through\n * untouched, as is every top-level key the client keeps beside `mcpServers`. A\n * file that fails to parse is not overwritten: it is reported, because a\n * half-written config file is a client that will not start and the caller is the\n * only one who knows what was in it.\n */\n\nimport { copyFileSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\n\nimport type { Result } from \"../_shared/index.js\";\nimport { ok } from \"../_shared/index.js\";\n\nimport { fail, notFoundError, validationError } from \"../errors.js\";\nimport { isRecord, type JsonRecord } from \"../mcp/json.js\";\n\n/** One MCP client this command knows how to find. */\nexport interface McpClientTarget {\n /** The `--client` value, for example `claude-desktop`. */\n readonly id: string;\n /** What to call it in a message a person reads. */\n readonly label: string;\n /** Absolute path to its server list on this platform, or undefined where it has none. */\n readonly configPath: string | undefined;\n /** True when the file exists. A known client with no file yet is still a valid target. */\n readonly present: boolean;\n /**\n * True when the directory the file would live in exists, which is the signal\n * that the client is installed even though it has never been configured.\n */\n readonly installed: boolean;\n}\n\nconst home = (): string => homedir();\n\n/**\n * The per-platform locations, one entry per client.\n *\n * Each path is the client's own documented location. A client absent from a\n * platform has `undefined` there rather than a guessed path, so `--client` names\n * something real or fails by name.\n */\nfunction configPathFor(id: string, os: string, env: NodeJS.ProcessEnv, cwd: string): string | undefined {\n const appData = env[\"APPDATA\"];\n switch (id) {\n case \"claude-desktop\":\n if (os === \"darwin\") return join(home(), \"Library\", \"Application Support\", \"Claude\", \"claude_desktop_config.json\");\n if (os === \"win32\") return appData === undefined ? undefined : join(appData, \"Claude\", \"claude_desktop_config.json\");\n return join(home(), \".config\", \"Claude\", \"claude_desktop_config.json\");\n case \"claude-code\":\n // Project-scoped and checked in beside the code, which is the form a team\n // shares. The user-scoped list lives in `~/.claude.json` and is edited by\n // the CLI itself, so this command does not reach into it.\n return resolve(cwd, \".mcp.json\");\n case \"cursor\":\n return join(home(), \".cursor\", \"mcp.json\");\n case \"windsurf\":\n return join(home(), \".codeium\", \"windsurf\", \"mcp_config.json\");\n case \"vscode\":\n if (os === \"darwin\") return join(home(), \"Library\", \"Application Support\", \"Code\", \"User\", \"mcp.json\");\n if (os === \"win32\") return appData === undefined ? undefined : join(appData, \"Code\", \"User\", \"mcp.json\");\n return join(home(), \".config\", \"Code\", \"User\", \"mcp.json\");\n default:\n return undefined;\n }\n}\n\n/** Every client `connect` can target, in the order detection prefers them. */\nexport const MCP_CLIENT_IDS = [\"claude-code\", \"claude-desktop\", \"cursor\", \"windsurf\", \"vscode\"] as const;\n\nconst LABELS: Readonly<Record<string, string>> = {\n \"claude-code\": \"Claude Code (project .mcp.json)\",\n \"claude-desktop\": \"Claude Desktop\",\n cursor: \"Cursor\",\n windsurf: \"Windsurf\",\n vscode: \"Visual Studio Code\",\n};\n\nexport interface DetectOptions {\n readonly os?: string;\n readonly env?: NodeJS.ProcessEnv;\n readonly cwd?: string;\n}\n\nconst exists = (path: string | undefined): boolean => {\n if (path === undefined) return false;\n try {\n return existsSync(path) && statSync(path).isFile();\n } catch {\n return false;\n }\n};\n\nconst directoryExists = (path: string | undefined): boolean => {\n if (path === undefined) return false;\n try {\n return existsSync(dirname(path));\n } catch {\n return false;\n }\n};\n\n/** Every known client on this platform, whether or not it is installed. */\nexport function detectMcpClients(options: DetectOptions = {}): readonly McpClientTarget[] {\n const os = options.os ?? platform();\n const env = options.env ?? process.env;\n const cwd = options.cwd ?? process.cwd();\n return MCP_CLIENT_IDS.map((id) => {\n const configPath = configPathFor(id, os, env, cwd);\n return {\n id,\n label: LABELS[id] ?? id,\n configPath,\n present: exists(configPath),\n installed: directoryExists(configPath),\n };\n });\n}\n\n/**\n * The client to write to when the caller named none.\n *\n * A client with an existing config file wins over one that is merely installed,\n * because an existing file is evidence somebody uses it. Detection never picks\n * between two files silently: with more than one candidate present, the caller\n * is asked to name one.\n */\nexport function chooseMcpClient(\n targets: readonly McpClientTarget[],\n): Result<McpClientTarget> {\n const present = targets.filter((target) => target.present);\n if (present.length === 1) return ok(present[0]!);\n if (present.length > 1) {\n return fail(\n \"CONFLICT\",\n \"CLIENT_AMBIGUOUS\",\n `more than one MCP client is configured on this machine (${present.map((t) => t.id).join(\", \")}); name one with --client`,\n { details: { candidates: present.map((t) => t.id).join(\", \") } },\n );\n }\n const installed = targets.filter((target) => target.installed);\n if (installed.length === 1) return ok(installed[0]!);\n if (installed.length > 1) {\n return fail(\n \"CONFLICT\",\n \"CLIENT_AMBIGUOUS\",\n `more than one MCP client is installed on this machine (${installed.map((t) => t.id).join(\", \")}); name one with --client`,\n { details: { candidates: installed.map((t) => t.id).join(\", \") } },\n );\n }\n return notFoundError(\n \"CLIENT_NOT_FOUND\",\n `no MCP client config was found on this machine; name one with --client (${MCP_CLIENT_IDS.join(\", \")}) or point at the file with --config`,\n );\n}\n\n// ---------------------------------------------------------------- the stanza\n\n/** The `env` block the stanza carries. Non-secret values only, by construction. */\nexport type StanzaEnv = Readonly<Record<string, string>>;\n\nexport interface TabStanza {\n readonly command: string;\n readonly args: readonly string[];\n readonly env?: StanzaEnv;\n}\n\n/** The key the stanza is written under. One server, one name, always this one. */\nexport const TAB_SERVER_KEY = \"tab\";\n\n/**\n * Builds the stanza.\n *\n * `npx -y @tabai/sdk mcp` rather than a path: the client launches it, `npx`\n * resolves the published package, and nothing has to be installed first. That is\n * the one line the adoption claim rests on.\n *\n * The `env` block carries only what the server reads and a person could publish:\n * an RPC endpoint, a chain id and a read API URL. The names are the ones in the\n * tracked environment contract, `MONAD_RPC_URL`, `MONAD_CHAIN_ID` and\n * `NEXT_PUBLIC_REGISTRY_API_URL`, so a stanza and a `.env` agree.\n */\nexport function buildTabStanza(env: StanzaEnv = {}): TabStanza {\n const entries = Object.entries(env).filter(([, value]) => typeof value === \"string\" && value.trim() !== \"\");\n return {\n command: \"npx\",\n args: [\"-y\", \"@tabai/sdk\", \"mcp\"],\n ...(entries.length === 0 ? {} : { env: Object.fromEntries(entries) }),\n };\n}\n\n/** Anything that looks like a signing key, by name or by shape. */\nconst SECRET_NAME = /(PRIVATE[_-]?KEY|SECRET|MNEMONIC|SEED[_-]?PHRASE|PASSWORD|API[_-]?KEY|TOKEN|CREDENTIAL)/i;\nconst SECRET_SHAPE = /^(0x)?[0-9a-fA-F]{64}$/;\n\n/**\n * Refuses a stanza that carries a secret.\n *\n * Both a name check and a shape check, because either alone is defeated by the\n * obvious mistake: a key under an innocuous name has the shape, and an empty\n * placeholder under `PRIVATE_KEY` has the name. `connect` runs this on the `tab`\n * stanza it is about to write, before anything touches the disk, and\n * `tab doctor` runs it on the stanza already in the file, which is where a\n * secret added by hand shows up.\n */\nexport function assertNoSecret(stanza: unknown, where = \"the tab stanza\"): Result<void> {\n const problems: string[] = [];\n const walk = (value: unknown, path: string): void => {\n if (typeof value === \"string\") {\n if (SECRET_SHAPE.test(value.trim())) problems.push(`${path} holds a 32-byte hex value, which is the shape of a signing key`);\n return;\n }\n if (Array.isArray(value)) {\n value.forEach((entry, index) => walk(entry, `${path}[${index}]`));\n return;\n }\n if (isRecord(value)) {\n for (const [key, entry] of Object.entries(value)) {\n const child = path === \"\" ? key : `${path}.${key}`;\n if (SECRET_NAME.test(key)) problems.push(`${child} names a credential`);\n walk(entry, child);\n }\n }\n };\n walk(stanza, \"\");\n if (problems.length > 0) {\n return validationError(\n \"STANZA_CARRIES_SECRET\",\n `${where} would carry a credential and was not written: ${problems.join(\"; \")}. Settlement signing keys are read from the environment at run time and never from a client config file.`,\n { details: { problems: problems.join(\"; \") } },\n );\n }\n return ok(undefined);\n}\n\n// ---------------------------------------------------------------- merge\n\nexport interface MergeOutcome {\n /** The document as it should be on disk. */\n readonly merged: JsonRecord;\n /** The document that was on disk, or an empty object when the file did not exist. */\n readonly previous: JsonRecord;\n /** False when the file already said exactly this, which is the idempotent case. */\n readonly changed: boolean;\n /** Whether a `tab` stanza was already there, so a message can say added or updated. */\n readonly hadStanza: boolean;\n}\n\n/** Two-space JSON with a trailing newline, which is what every one of these files uses. */\nexport const renderConfig = (document: unknown): string => `${JSON.stringify(document, null, 2)}\\n`;\n\n/**\n * Merges the stanza into a document, leaving every other key alone.\n *\n * `changed` compares rendered text rather than object identity, so a run that\n * produces the same file is reported as no change even when the objects differ\n * by key order.\n */\nexport function mergeTabStanza(previous: JsonRecord, stanza: TabStanza): Result<MergeOutcome> {\n const existingServers = previous[\"mcpServers\"];\n if (existingServers !== undefined && !isRecord(existingServers)) {\n return validationError(\n \"CONFIG_MCP_SERVERS_INVALID\",\n \"the config file has an `mcpServers` key that is not an object, so it was left alone rather than replaced\",\n );\n }\n const servers: Record<string, unknown> = { ...(isRecord(existingServers) ? existingServers : {}) };\n const hadStanza = servers[TAB_SERVER_KEY] !== undefined;\n servers[TAB_SERVER_KEY] = stanza;\n\n // Only the `tab` stanza is checked. Another server's API key is that server's\n // business and carrying it through untouched is the whole point of merging\n // rather than replacing -- refusing on it would make `connect` fail on exactly\n // the machines that already use MCP. The guarantee this command owes is about\n // what it writes, and what it writes is the stanza.\n const secret = assertNoSecret(servers[TAB_SERVER_KEY], `the \\`${TAB_SERVER_KEY}\\` stanza`);\n if (!secret.ok) return secret;\n\n const merged: JsonRecord = { ...previous, mcpServers: servers };\n\n return ok({\n merged,\n previous,\n changed: renderConfig(previous) !== renderConfig(merged),\n hadStanza,\n });\n}\n\n// ---------------------------------------------------------------- the file\n\nexport interface ReadConfigOutcome {\n readonly document: JsonRecord;\n readonly existed: boolean;\n readonly raw: string;\n}\n\n/**\n * Reads a client config.\n *\n * A file that does not exist is an empty document and not an error: connecting a\n * client that has never been configured is the common case. A file that exists\n * and does not parse *is* an error, and nothing is written over it.\n */\nexport function readClientConfig(path: string): Result<ReadConfigOutcome> {\n if (!existsSync(path)) return ok({ document: {}, existed: false, raw: \"\" });\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch (error) {\n return fail(\"UNAVAILABLE\", \"CONFIG_UNREADABLE\", `\\`${path}\\` could not be read`, {\n details: { path },\n cause: { code: \"READ_FAILED\", message: error instanceof Error ? error.message : String(error) },\n });\n }\n if (raw.trim() === \"\") return ok({ document: {}, existed: true, raw });\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (error) {\n return validationError(\n \"CONFIG_MALFORMED\",\n `\\`${path}\\` is not valid JSON, so it was left untouched: ${error instanceof Error ? error.message : String(error)}`,\n { details: { path } },\n );\n }\n if (!isRecord(parsed)) {\n return validationError(\"CONFIG_MALFORMED\", `\\`${path}\\` must hold a JSON object at its top level`, {\n details: { path },\n });\n }\n return ok({ document: parsed, existed: true, raw });\n}\n\n/** Where a backup of `path` goes, named with the instant so two runs never collide. */\nexport const backupPathFor = (path: string, at: Date): string =>\n `${path}.tab-backup-${at.toISOString().replace(/[:.]/g, \"-\")}`;\n\nexport interface WriteOutcome {\n readonly path: string;\n /** Absent when nothing was written, which is the idempotent case. */\n readonly backupPath?: string;\n readonly wrote: boolean;\n}\n\n/** Writes the merged document, taking a backup first when a file was already there. */\nexport function writeClientConfig(\n path: string,\n outcome: MergeOutcome,\n existed: boolean,\n at: Date,\n): Result<WriteOutcome> {\n if (!outcome.changed) return ok({ path, wrote: false });\n try {\n mkdirSync(dirname(path), { recursive: true });\n } catch (error) {\n return fail(\"UNAVAILABLE\", \"CONFIG_DIRECTORY_UNWRITABLE\", `\\`${dirname(path)}\\` could not be created`, {\n details: { path },\n cause: { code: \"MKDIR_FAILED\", message: error instanceof Error ? error.message : String(error) },\n });\n }\n\n let backupPath: string | undefined;\n if (existed) {\n backupPath = backupPathFor(path, at);\n try {\n copyFileSync(path, backupPath);\n } catch (error) {\n return fail(\"UNAVAILABLE\", \"CONFIG_BACKUP_FAILED\", `\\`${path}\\` could not be backed up, so it was not changed`, {\n details: { path, backupPath },\n cause: { code: \"COPY_FAILED\", message: error instanceof Error ? error.message : String(error) },\n });\n }\n }\n\n try {\n writeFileSync(path, renderConfig(outcome.merged), \"utf8\");\n } catch (error) {\n return fail(\"UNAVAILABLE\", \"CONFIG_UNWRITABLE\", `\\`${path}\\` could not be written`, {\n details: { path },\n cause: { code: \"WRITE_FAILED\", message: error instanceof Error ? error.message : String(error) },\n });\n }\n return ok({ path, wrote: true, ...(backupPath === undefined ? {} : { backupPath }) });\n}\n\n// ---------------------------------------------------------------- the diff\n\n/**\n * A unified diff of two JSON documents, line by line.\n *\n * Written here rather than pulled in because it is a longest-common-subsequence\n * over at most a few hundred lines and the alternative is a dependency for a\n * command that prints one. `connect` shows this before it claims to have done\n * anything, because a command that edits a file the user did not open owes them\n * a look at what it changed.\n */\nexport function unifiedDiff(before: string, after: string, context = 2): string {\n const a = before === \"\" ? [] : before.replace(/\\n$/, \"\").split(\"\\n\");\n const b = after === \"\" ? [] : after.replace(/\\n$/, \"\").split(\"\\n\");\n\n // LCS table. Both sides are one JSON document, so the quadratic cost is a few\n // hundred thousand cells at worst and is paid once, on a command a person ran.\n const lengths: number[][] = Array.from({ length: a.length + 1 }, () => new Array<number>(b.length + 1).fill(0));\n for (let i = a.length - 1; i >= 0; i -= 1) {\n for (let j = b.length - 1; j >= 0; j -= 1) {\n lengths[i]![j] = a[i] === b[j] ? lengths[i + 1]![j + 1]! + 1 : Math.max(lengths[i + 1]![j]!, lengths[i]![j + 1]!);\n }\n }\n\n const lines: { readonly mark: \" \" | \"-\" | \"+\"; readonly text: string }[] = [];\n let i = 0;\n let j = 0;\n while (i < a.length && j < b.length) {\n if (a[i] === b[j]) {\n lines.push({ mark: \" \", text: a[i]! });\n i += 1;\n j += 1;\n } else if (lengths[i + 1]![j]! >= lengths[i]![j + 1]!) {\n lines.push({ mark: \"-\", text: a[i]! });\n i += 1;\n } else {\n lines.push({ mark: \"+\", text: b[j]! });\n j += 1;\n }\n }\n for (; i < a.length; i += 1) lines.push({ mark: \"-\", text: a[i]! });\n for (; j < b.length; j += 1) lines.push({ mark: \"+\", text: b[j]! });\n\n // Keep only the changed lines and `context` lines either side, so a large\n // config with one added server prints as one hunk.\n const keep = new Set<number>();\n lines.forEach((line, index) => {\n if (line.mark === \" \") return;\n for (let k = Math.max(0, index - context); k <= Math.min(lines.length - 1, index + context); k += 1) keep.add(k);\n });\n\n // Nothing changed is no diff at all, not one elision marker.\n if (keep.size === 0) return \"\";\n\n const rendered: string[] = [];\n let skipping = false;\n lines.forEach((line, index) => {\n if (keep.has(index)) {\n rendered.push(`${line.mark}${line.text}`);\n skipping = false;\n } else if (!skipping) {\n rendered.push(\"@@\");\n skipping = true;\n }\n });\n return rendered.join(\"\\n\");\n}\n"]}
|
package/dist/cli/connect.d.ts
CHANGED
|
@@ -18,11 +18,9 @@
|
|
|
18
18
|
* ## Nothing here is a secret
|
|
19
19
|
*
|
|
20
20
|
* The stanza carries a command, its arguments, and at most two URLs. Every value
|
|
21
|
-
* in it is publishable. {@link assertNoSecret} runs over the
|
|
21
|
+
* in it is publishable. {@link assertNoSecret} runs over the `tab` stanza
|
|
22
22
|
* before anything is written, so the property is enforced rather than asserted
|
|
23
23
|
* in a comment.
|
|
24
|
-
*
|
|
25
|
-
* Requirements: 25.5, 25.6
|
|
26
24
|
*/
|
|
27
25
|
import type { Result } from "../_shared/index.js";
|
|
28
26
|
import type { Logger } from "../logger.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/cli/connect.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/cli/connect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAI5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,EAA4B,KAAK,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACzF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAWL,KAAK,SAAS,EACf,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,yEAAyE;IACzE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,wDAAwD;IACxD,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,+EAA+E;AAC/E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,kCAAkC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAsFD;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAkDxF"}
|
package/dist/cli/connect.js
CHANGED
|
@@ -18,11 +18,9 @@
|
|
|
18
18
|
* ## Nothing here is a secret
|
|
19
19
|
*
|
|
20
20
|
* The stanza carries a command, its arguments, and at most two URLs. Every value
|
|
21
|
-
* in it is publishable. {@link assertNoSecret} runs over the
|
|
21
|
+
* in it is publishable. {@link assertNoSecret} runs over the `tab` stanza
|
|
22
22
|
* before anything is written, so the property is enforced rather than asserted
|
|
23
23
|
* in a comment.
|
|
24
|
-
*
|
|
25
|
-
* Requirements: 25.5, 25.6
|
|
26
24
|
*/
|
|
27
25
|
import { ok } from "../_shared/index.js";
|
|
28
26
|
import { validationError } from "../errors.js";
|
|
@@ -40,6 +38,10 @@ function stanzaEnv(settings, env) {
|
|
|
40
38
|
const registry = settings.registryUrl ?? env["NEXT_PUBLIC_REGISTRY_API_URL"];
|
|
41
39
|
if (registry !== undefined && registry.trim() !== "")
|
|
42
40
|
values["NEXT_PUBLIC_REGISTRY_API_URL"] = registry.trim();
|
|
41
|
+
// An address is public, and without it tab_call has no Agent to meter against.
|
|
42
|
+
const agent = env["AGENT_ADDRESS"];
|
|
43
|
+
if (agent !== undefined && agent.trim() !== "")
|
|
44
|
+
values["AGENT_ADDRESS"] = agent.trim();
|
|
43
45
|
return values;
|
|
44
46
|
}
|
|
45
47
|
/** Reads the deployment, keylessly, so `connect` can say what it connected to. */
|
package/dist/cli/connect.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/cli/connect.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `tab connect`: the one line of configuration.\n *\n * The whole adoption story is this command. It finds the MCP client's server\n * list, reads the deployment keylessly so it can tell the caller what they are\n * about to be connected to, merges one `mcpServers.tab` stanza, backs up what\n * was there, and prints the diff.\n *\n * ## Read before write\n *\n * The keyless read happens first and its result is reported whether it succeeded\n * or not. A `connect` that silently wrote a stanza pointing at a registry that\n * does not answer would look like success and behave like a broken install, and\n * the person running it would find out from a model instead of from the command.\n * The read never blocks the write: a deployment that is temporarily down is not\n * a reason to refuse to configure a client against it.\n *\n * ## Nothing here is a secret\n *\n * The stanza carries a command, its arguments, and at most two URLs. Every value\n * in it is publishable. {@link assertNoSecret} runs over the merged document\n * before anything is written, so the property is enforced rather than asserted\n * in a comment.\n *\n * Requirements: 25.5, 25.6\n */\n\nimport type { Result } from \"../_shared/index.js\";\nimport { ok } from \"../_shared/index.js\";\n\nimport { validationError } from \"../errors.js\";\nimport type { Logger } from \"../logger.js\";\nimport { silentLogger } from \"../logger.js\";\nimport { asArray, field } from \"../mcp/json.js\";\nimport { createRegistryReadClient, type RegistryFetch } from \"../mcp/registry-client.js\";\nimport type { TabMcpSettings } from \"../mcp/settings.js\";\nimport {\n buildTabStanza,\n chooseMcpClient,\n detectMcpClients,\n mergeTabStanza,\n readClientConfig,\n renderConfig,\n unifiedDiff,\n writeClientConfig,\n type McpClientTarget,\n type StanzaEnv,\n type TabStanza,\n} from \"./client-config.js\";\n\nexport interface ConnectOptions {\n readonly settings: TabMcpSettings;\n /** `claude-code`, `claude-desktop`, `cursor`, `windsurf` or `vscode`. */\n readonly client?: string;\n /** An explicit config file, which beats `client` and skips detection. */\n readonly configPath?: string;\n readonly env?: NodeJS.ProcessEnv;\n readonly os?: string;\n readonly cwd?: string;\n /** Injectable clock, so a backup name is testable. */\n readonly now?: () => Date;\n /** Work out and print everything, and write nothing. */\n readonly dryRun?: boolean;\n readonly fetchImpl?: RegistryFetch;\n readonly logger?: Logger;\n}\n\n/** What the deployment said when it was asked, before anything was written. */\nexport interface ConnectPreflight {\n readonly registryUrl: string | undefined;\n /** Services indexed, or null when the read API did not answer. */\n readonly services: number | null;\n readonly note: string;\n}\n\nexport interface ConnectReport {\n readonly client: string;\n readonly configPath: string;\n /** False when the file already said exactly this. */\n readonly changed: boolean;\n /** True when a `tab` stanza was already there, so a message can say updated. */\n readonly hadStanza: boolean;\n readonly backupPath: string | undefined;\n readonly stanza: TabStanza;\n /** Empty when nothing changed. */\n readonly diff: string;\n readonly preflight: ConnectPreflight;\n readonly dryRun: boolean;\n}\n\n/** The environment values that belong in a stanza. Publishable, all of them. */\nfunction stanzaEnv(settings: TabMcpSettings, env: NodeJS.ProcessEnv): StanzaEnv {\n const values: Record<string, string> = {};\n const rpc = settings.rpcUrl ?? env[\"MONAD_RPC_URL\"];\n if (rpc !== undefined && rpc.trim() !== \"\") values[\"MONAD_RPC_URL\"] = rpc.trim();\n values[\"MONAD_CHAIN_ID\"] = String(settings.chainId);\n const registry = settings.registryUrl ?? env[\"NEXT_PUBLIC_REGISTRY_API_URL\"];\n if (registry !== undefined && registry.trim() !== \"\") values[\"NEXT_PUBLIC_REGISTRY_API_URL\"] = registry.trim();\n return values;\n}\n\n/** Reads the deployment, keylessly, so `connect` can say what it connected to. */\nasync function preflight(\n settings: TabMcpSettings,\n fetchImpl: RegistryFetch | undefined,\n logger: Logger,\n): Promise<ConnectPreflight> {\n if (settings.registryUrl === undefined) {\n return {\n registryUrl: undefined,\n services: null,\n note: \"no registry read API is configured, so discovery and status will not work until one is. Calling and settling do not need it\",\n };\n }\n const registry = createRegistryReadClient({\n baseUrl: settings.registryUrl,\n logger,\n ...(fetchImpl === undefined ? {} : { fetchImpl }),\n });\n const services = await registry.services(50);\n if (!services.ok) {\n return {\n registryUrl: registry.baseUrl,\n services: null,\n note: `the read API did not answer: ${services.error.message}`,\n };\n }\n const count = asArray(field(services.value, \"services\")).length;\n const lastBlock = field(field(services.value, \"index\"), \"lastBlock\");\n return {\n registryUrl: registry.baseUrl,\n services: count,\n note:\n typeof lastBlock === \"number\"\n ? `${count} Service(s) indexed at Monad block ${lastBlock}`\n : `${count} Service(s) indexed`,\n };\n}\n\n/** Resolves which file to write, from `--config`, then `--client`, then detection. */\nfunction resolveTarget(options: ConnectOptions): Result<{ id: string; configPath: string }> {\n if (options.configPath !== undefined) {\n return ok({ id: options.client ?? \"custom\", configPath: options.configPath });\n }\n const detected = detectMcpClients({\n ...(options.os === undefined ? {} : { os: options.os }),\n ...(options.env === undefined ? {} : { env: options.env }),\n ...(options.cwd === undefined ? {} : { cwd: options.cwd }),\n });\n if (options.client !== undefined) {\n const named: McpClientTarget | undefined = detected.find((target) => target.id === options.client);\n if (named === undefined || named.configPath === undefined) {\n return validationError(\n \"CLIENT_UNKNOWN\",\n `\\`${options.client}\\` is not an MCP client this command knows on this platform; the known ones are ${detected\n .filter((target) => target.configPath !== undefined)\n .map((target) => target.id)\n .join(\", \")}`,\n { details: { client: options.client } },\n );\n }\n return ok({ id: named.id, configPath: named.configPath });\n }\n const chosen = chooseMcpClient(detected);\n if (!chosen.ok) return chosen;\n if (chosen.value.configPath === undefined) {\n return validationError(\"CLIENT_UNKNOWN\", `\\`${chosen.value.id}\\` has no config path on this platform`);\n }\n return ok({ id: chosen.value.id, configPath: chosen.value.configPath });\n}\n\n/**\n * Runs the command.\n *\n * Every failure is a `Result`, including a config file that will not parse. The\n * one thing this never does is write over something it could not read.\n */\nexport async function runConnect(options: ConnectOptions): Promise<Result<ConnectReport>> {\n const env = options.env ?? process.env;\n const logger = options.logger ?? silentLogger;\n const at = (options.now ?? (() => new Date()))();\n const dryRun = options.dryRun ?? false;\n\n const target = resolveTarget(options);\n if (!target.ok) return target;\n\n const read = readClientConfig(target.value.configPath);\n if (!read.ok) return read;\n\n const stanza = buildTabStanza(stanzaEnv(options.settings, env));\n const merged = mergeTabStanza(read.value.document, stanza);\n if (!merged.ok) return merged;\n\n const checked = await preflight(options.settings, options.fetchImpl, logger);\n\n const before = read.value.existed ? renderConfig(read.value.document) : \"\";\n const after = renderConfig(merged.value.merged);\n const diff = merged.value.changed ? unifiedDiff(before, after) : \"\";\n\n if (dryRun) {\n return ok({\n client: target.value.id,\n configPath: target.value.configPath,\n changed: merged.value.changed,\n hadStanza: merged.value.hadStanza,\n backupPath: undefined,\n stanza,\n diff,\n preflight: checked,\n dryRun: true,\n });\n }\n\n const written = writeClientConfig(target.value.configPath, merged.value, read.value.existed, at);\n if (!written.ok) return written;\n\n return ok({\n client: target.value.id,\n configPath: target.value.configPath,\n changed: written.value.wrote,\n hadStanza: merged.value.hadStanza,\n backupPath: written.value.backupPath,\n stanza,\n diff,\n preflight: checked,\n dryRun: false,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/cli/connect.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `tab connect`: the one line of configuration.\n *\n * The whole adoption story is this command. It finds the MCP client's server\n * list, reads the deployment keylessly so it can tell the caller what they are\n * about to be connected to, merges one `mcpServers.tab` stanza, backs up what\n * was there, and prints the diff.\n *\n * ## Read before write\n *\n * The keyless read happens first and its result is reported whether it succeeded\n * or not. A `connect` that silently wrote a stanza pointing at a registry that\n * does not answer would look like success and behave like a broken install, and\n * the person running it would find out from a model instead of from the command.\n * The read never blocks the write: a deployment that is temporarily down is not\n * a reason to refuse to configure a client against it.\n *\n * ## Nothing here is a secret\n *\n * The stanza carries a command, its arguments, and at most two URLs. Every value\n * in it is publishable. {@link assertNoSecret} runs over the `tab` stanza\n * before anything is written, so the property is enforced rather than asserted\n * in a comment.\n */\n\nimport type { Result } from \"../_shared/index.js\";\nimport { ok } from \"../_shared/index.js\";\n\nimport { validationError } from \"../errors.js\";\nimport type { Logger } from \"../logger.js\";\nimport { silentLogger } from \"../logger.js\";\nimport { asArray, field } from \"../mcp/json.js\";\nimport { createRegistryReadClient, type RegistryFetch } from \"../mcp/registry-client.js\";\nimport type { TabMcpSettings } from \"../mcp/settings.js\";\nimport {\n buildTabStanza,\n chooseMcpClient,\n detectMcpClients,\n mergeTabStanza,\n readClientConfig,\n renderConfig,\n unifiedDiff,\n writeClientConfig,\n type McpClientTarget,\n type StanzaEnv,\n type TabStanza,\n} from \"./client-config.js\";\n\nexport interface ConnectOptions {\n readonly settings: TabMcpSettings;\n /** `claude-code`, `claude-desktop`, `cursor`, `windsurf` or `vscode`. */\n readonly client?: string;\n /** An explicit config file, which beats `client` and skips detection. */\n readonly configPath?: string;\n readonly env?: NodeJS.ProcessEnv;\n readonly os?: string;\n readonly cwd?: string;\n /** Injectable clock, so a backup name is testable. */\n readonly now?: () => Date;\n /** Work out and print everything, and write nothing. */\n readonly dryRun?: boolean;\n readonly fetchImpl?: RegistryFetch;\n readonly logger?: Logger;\n}\n\n/** What the deployment said when it was asked, before anything was written. */\nexport interface ConnectPreflight {\n readonly registryUrl: string | undefined;\n /** Services indexed, or null when the read API did not answer. */\n readonly services: number | null;\n readonly note: string;\n}\n\nexport interface ConnectReport {\n readonly client: string;\n readonly configPath: string;\n /** False when the file already said exactly this. */\n readonly changed: boolean;\n /** True when a `tab` stanza was already there, so a message can say updated. */\n readonly hadStanza: boolean;\n readonly backupPath: string | undefined;\n readonly stanza: TabStanza;\n /** Empty when nothing changed. */\n readonly diff: string;\n readonly preflight: ConnectPreflight;\n readonly dryRun: boolean;\n}\n\n/** The environment values that belong in a stanza. Publishable, all of them. */\nfunction stanzaEnv(settings: TabMcpSettings, env: NodeJS.ProcessEnv): StanzaEnv {\n const values: Record<string, string> = {};\n const rpc = settings.rpcUrl ?? env[\"MONAD_RPC_URL\"];\n if (rpc !== undefined && rpc.trim() !== \"\") values[\"MONAD_RPC_URL\"] = rpc.trim();\n values[\"MONAD_CHAIN_ID\"] = String(settings.chainId);\n const registry = settings.registryUrl ?? env[\"NEXT_PUBLIC_REGISTRY_API_URL\"];\n if (registry !== undefined && registry.trim() !== \"\") values[\"NEXT_PUBLIC_REGISTRY_API_URL\"] = registry.trim();\n // An address is public, and without it tab_call has no Agent to meter against.\n const agent = env[\"AGENT_ADDRESS\"];\n if (agent !== undefined && agent.trim() !== \"\") values[\"AGENT_ADDRESS\"] = agent.trim();\n return values;\n}\n\n/** Reads the deployment, keylessly, so `connect` can say what it connected to. */\nasync function preflight(\n settings: TabMcpSettings,\n fetchImpl: RegistryFetch | undefined,\n logger: Logger,\n): Promise<ConnectPreflight> {\n if (settings.registryUrl === undefined) {\n return {\n registryUrl: undefined,\n services: null,\n note: \"no registry read API is configured, so discovery and status will not work until one is. Calling and settling do not need it\",\n };\n }\n const registry = createRegistryReadClient({\n baseUrl: settings.registryUrl,\n logger,\n ...(fetchImpl === undefined ? {} : { fetchImpl }),\n });\n const services = await registry.services(50);\n if (!services.ok) {\n return {\n registryUrl: registry.baseUrl,\n services: null,\n note: `the read API did not answer: ${services.error.message}`,\n };\n }\n const count = asArray(field(services.value, \"services\")).length;\n const lastBlock = field(field(services.value, \"index\"), \"lastBlock\");\n return {\n registryUrl: registry.baseUrl,\n services: count,\n note:\n typeof lastBlock === \"number\"\n ? `${count} Service(s) indexed at Monad block ${lastBlock}`\n : `${count} Service(s) indexed`,\n };\n}\n\n/** Resolves which file to write, from `--config`, then `--client`, then detection. */\nfunction resolveTarget(options: ConnectOptions): Result<{ id: string; configPath: string }> {\n if (options.configPath !== undefined) {\n return ok({ id: options.client ?? \"custom\", configPath: options.configPath });\n }\n const detected = detectMcpClients({\n ...(options.os === undefined ? {} : { os: options.os }),\n ...(options.env === undefined ? {} : { env: options.env }),\n ...(options.cwd === undefined ? {} : { cwd: options.cwd }),\n });\n if (options.client !== undefined) {\n const named: McpClientTarget | undefined = detected.find((target) => target.id === options.client);\n if (named === undefined || named.configPath === undefined) {\n return validationError(\n \"CLIENT_UNKNOWN\",\n `\\`${options.client}\\` is not an MCP client this command knows on this platform; the known ones are ${detected\n .filter((target) => target.configPath !== undefined)\n .map((target) => target.id)\n .join(\", \")}`,\n { details: { client: options.client } },\n );\n }\n return ok({ id: named.id, configPath: named.configPath });\n }\n const chosen = chooseMcpClient(detected);\n if (!chosen.ok) return chosen;\n if (chosen.value.configPath === undefined) {\n return validationError(\"CLIENT_UNKNOWN\", `\\`${chosen.value.id}\\` has no config path on this platform`);\n }\n return ok({ id: chosen.value.id, configPath: chosen.value.configPath });\n}\n\n/**\n * Runs the command.\n *\n * Every failure is a `Result`, including a config file that will not parse. The\n * one thing this never does is write over something it could not read.\n */\nexport async function runConnect(options: ConnectOptions): Promise<Result<ConnectReport>> {\n const env = options.env ?? process.env;\n const logger = options.logger ?? silentLogger;\n const at = (options.now ?? (() => new Date()))();\n const dryRun = options.dryRun ?? false;\n\n const target = resolveTarget(options);\n if (!target.ok) return target;\n\n const read = readClientConfig(target.value.configPath);\n if (!read.ok) return read;\n\n const stanza = buildTabStanza(stanzaEnv(options.settings, env));\n const merged = mergeTabStanza(read.value.document, stanza);\n if (!merged.ok) return merged;\n\n const checked = await preflight(options.settings, options.fetchImpl, logger);\n\n const before = read.value.existed ? renderConfig(read.value.document) : \"\";\n const after = renderConfig(merged.value.merged);\n const diff = merged.value.changed ? unifiedDiff(before, after) : \"\";\n\n if (dryRun) {\n return ok({\n client: target.value.id,\n configPath: target.value.configPath,\n changed: merged.value.changed,\n hadStanza: merged.value.hadStanza,\n backupPath: undefined,\n stanza,\n diff,\n preflight: checked,\n dryRun: true,\n });\n }\n\n const written = writeClientConfig(target.value.configPath, merged.value, read.value.existed, at);\n if (!written.ok) return written;\n\n return ok({\n client: target.value.id,\n configPath: target.value.configPath,\n changed: written.value.wrote,\n hadStanza: merged.value.hadStanza,\n backupPath: written.value.backupPath,\n stanza,\n diff,\n preflight: checked,\n dryRun: false,\n });\n}\n"]}
|
package/dist/cli/doctor.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* key configured" is the answer to why `tab_settle` will refuse, and printing the
|
|
11
11
|
* key would be the worst possible way to say it.
|
|
12
12
|
*
|
|
13
|
-
* ##
|
|
13
|
+
* ## Four outcomes, not two
|
|
14
14
|
*
|
|
15
15
|
* A check answers `pass`, `warn`, `fail` or `skip`, and the distinction carries
|
|
16
16
|
* weight. A missing registry read API is a `warn`: discovery and status will not
|
|
@@ -20,8 +20,6 @@
|
|
|
20
20
|
* of it would be wrong rather than absent. `skip` is for a check whose input was
|
|
21
21
|
* not configured, which is a statement about the configuration and not about the
|
|
22
22
|
* deployment.
|
|
23
|
-
*
|
|
24
|
-
* Requirements: 25.6, 28.6
|
|
25
23
|
*/
|
|
26
24
|
import type { Logger } from "../logger.js";
|
|
27
25
|
import { type RegistryFetch } from "../mcp/registry-client.js";
|
package/dist/cli/doctor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,EAA4B,KAAK,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACzF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGzD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5D,MAAM,WAAW,WAAW;IAC1B,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,uCAAuC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;IACxC,yEAAyE;IACzE,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;CACxD;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,sEAAsE;IACtE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAkCD,gFAAgF;AAChF,wBAAsB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CA4I7E"}
|
package/dist/cli/doctor.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* key configured" is the answer to why `tab_settle` will refuse, and printing the
|
|
11
11
|
* key would be the worst possible way to say it.
|
|
12
12
|
*
|
|
13
|
-
* ##
|
|
13
|
+
* ## Four outcomes, not two
|
|
14
14
|
*
|
|
15
15
|
* A check answers `pass`, `warn`, `fail` or `skip`, and the distinction carries
|
|
16
16
|
* weight. A missing registry read API is a `warn`: discovery and status will not
|
|
@@ -20,8 +20,6 @@
|
|
|
20
20
|
* of it would be wrong rather than absent. `skip` is for a check whose input was
|
|
21
21
|
* not configured, which is a statement about the configuration and not about the
|
|
22
22
|
* deployment.
|
|
23
|
-
*
|
|
24
|
-
* Requirements: 25.6, 28.6
|
|
25
23
|
*/
|
|
26
24
|
import { JsonRpcProvider, Network } from "ethers";
|
|
27
25
|
import { silentLogger } from "../logger.js";
|
package/dist/cli/doctor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `tab doctor`: every check that can be made without a key.\n *\n * The point of the command is that a person who has just run `tab connect` can\n * find out whether it worked before they ask a model to spend anything. So every\n * check here is a read -- an `eth_chainId`, an `eth_getCode`, a `GET /healthz`.\n * Nothing here signs, nothing here writes, and nothing here loads a private key.\n * The one thing it says about a signing key is whether the environment carries\n * one at all, reported as present or absent and never read, because \"you have no\n * key configured\" is the answer to why `tab_settle` will refuse, and printing the\n * key would be the worst possible way to say it.\n *\n * ## Three outcomes, not two\n *\n * A check answers `pass`, `warn`, `fail` or `skip`, and the distinction carries\n * weight. A missing registry read API is a `warn`: discovery and status will not\n * work, but calling and settling will, so it is not a broken installation. An\n * RPC endpoint that answers the wrong chain id is a `fail`: every address in the\n * configuration names a contract on a different chain, and anything built on top\n * of it would be wrong rather than absent. `skip` is for a check whose input was\n * not configured, which is a statement about the configuration and not about the\n * deployment.\n *\n * Requirements: 25.6, 28.6\n */\n\nimport { JsonRpcProvider, Network } from \"ethers\";\n\nimport type { Logger } from \"../logger.js\";\nimport { silentLogger } from \"../logger.js\";\nimport { asArray, field, isRecord } from \"../mcp/json.js\";\nimport { createRegistryReadClient, type RegistryFetch } from \"../mcp/registry-client.js\";\nimport type { TabMcpSettings } from \"../mcp/settings.js\";\nimport { readClientConfig, TAB_SERVER_KEY, assertNoSecret } from \"./client-config.js\";\n\nexport type CheckStatus = \"pass\" | \"warn\" | \"fail\" | \"skip\";\n\nexport interface DoctorCheck {\n /** Short and stable, so a script can grep for one. */\n readonly name: string;\n readonly status: CheckStatus;\n /** What was found, in one sentence. */\n readonly detail: string;\n /** What to do about it, when there is something to do. */\n readonly hint?: string;\n}\n\nexport interface DoctorReport {\n readonly checks: readonly DoctorCheck[];\n /** True when nothing failed. Warnings do not make a report unhealthy. */\n readonly ok: boolean;\n readonly counts: Readonly<Record<CheckStatus, number>>;\n}\n\nexport interface DoctorOptions {\n readonly settings: TabMcpSettings;\n readonly env?: NodeJS.ProcessEnv;\n /** The MCP client config to inspect, when the caller resolved one. */\n readonly clientConfigPath?: string;\n readonly fetchImpl?: RegistryFetch;\n readonly logger?: Logger;\n /** How long any one network read may take. Defaults to 10 seconds. */\n readonly timeoutMs?: number;\n}\n\n/** The contracts a working deployment has code at, under their environment names. */\nconst CONTRACTS = [\n [\"SERVICE_REGISTRY_ADDRESS\", \"ServiceRegistry\"],\n [\"BOND_ADDRESS\", \"Bond\"],\n [\"TAB_BOOK_ADDRESS\", \"TabBook\"],\n [\"TAB_SETTLEMENT_ADDRESS\", \"TabSettlement\"],\n] as const;\n\nconst ZERO_ADDRESS = \"0x0000000000000000000000000000000000000000\";\n\nconst pass = (name: string, detail: string): DoctorCheck => ({ name, status: \"pass\", detail });\nconst warn = (name: string, detail: string, hint?: string): DoctorCheck => ({\n name,\n status: \"warn\",\n detail,\n ...(hint === undefined ? {} : { hint }),\n});\nconst bad = (name: string, detail: string, hint?: string): DoctorCheck => ({\n name,\n status: \"fail\",\n detail,\n ...(hint === undefined ? {} : { hint }),\n});\nconst skip = (name: string, detail: string, hint?: string): DoctorCheck => ({\n name,\n status: \"skip\",\n detail,\n ...(hint === undefined ? {} : { hint }),\n});\n\nconst reason = (error: unknown): string => (error instanceof Error ? error.message : String(error));\n\n/** Runs every check and answers the whole report. Never throws, never signs. */\nexport async function runDoctor(options: DoctorOptions): Promise<DoctorReport> {\n const env = options.env ?? process.env;\n const settings = options.settings;\n const logger = options.logger ?? silentLogger;\n const timeoutMs = options.timeoutMs ?? 10_000;\n const checks: DoctorCheck[] = [];\n\n // ---- the runtime\n const major = Number.parseInt(process.versions.node.split(\".\")[0] ?? \"0\", 10);\n checks.push(\n major >= 20\n ? pass(\"node\", `Node ${process.versions.node}`)\n : bad(\"node\", `Node ${process.versions.node}`, \"the MCP server needs Node 20.10 or later for its global fetch\"),\n );\n\n // ---- what is configured\n checks.push(\n settings.agent === undefined\n ? warn(\n \"agent\",\n \"no Agent address is configured\",\n \"set `agent` in tab.config; tab_call and tab_settle need to know whose Open Tab to meter\",\n )\n : pass(\"agent\", `${settings.agent} (${settings.sources[\"agent\"] ?? \"unknown source\"})`),\n );\n checks.push(\n settings.services.length === 0\n ? warn(\n \"service-directory\",\n \"no Service endpoints are configured\",\n \"the chain records no URL for a Service, so add each one to the `services` list in tab.config or tab_call has nowhere to send a request\",\n )\n : pass(\"service-directory\", `${settings.services.length} Service endpoint(s) configured`),\n );\n\n // ---- Monad, keyless\n if (settings.rpcUrl === undefined) {\n checks.push(\n skip(\"monad-rpc\", \"no Monad RPC endpoint is configured\", \"set MONAD_RPC_URL\"),\n skip(\"monad-contracts\", \"skipped: there is no endpoint to read from\"),\n );\n } else {\n const expected = settings.chainId;\n // Pinned to the configured chain so ethers never probes the network on its\n // own, and `eth_chainId` is then asked directly: a static network would\n // otherwise answer the configured id without the endpoint being consulted,\n // which is the one check this exists to make.\n const provider = new JsonRpcProvider(settings.rpcUrl, Network.from(expected), { staticNetwork: true });\n try {\n const [answered, head] = await Promise.all([\n withTimeout(provider.send(\"eth_chainId\", []) as Promise<unknown>, timeoutMs, \"eth_chainId\"),\n withTimeout(provider.getBlockNumber(), timeoutMs, \"eth_blockNumber\"),\n ]);\n const chainId = typeof answered === \"string\" ? Number.parseInt(answered, 16) : Number.NaN;\n checks.push(\n chainId === expected\n ? pass(\"monad-rpc\", `chain id ${chainId} at block ${head}`)\n : bad(\n \"monad-rpc\",\n `the endpoint answers chain id ${chainId} and the configuration expects ${expected}`,\n \"every contract address in the configuration names a contract on one chain; reading a different one would produce confident wrong answers\",\n ),\n );\n checks.push(...(await contractChecks(provider, env, timeoutMs)));\n } catch (error) {\n checks.push(\n bad(\"monad-rpc\", `${settings.rpcUrl} could not be read: ${reason(error)}`, \"check MONAD_RPC_URL\"),\n skip(\"monad-contracts\", \"skipped: the endpoint did not answer\"),\n );\n } finally {\n provider.destroy();\n }\n }\n\n // ---- the registry read API, keyless\n if (settings.registryUrl === undefined) {\n checks.push(\n warn(\n \"registry-api\",\n \"no registry read API is configured\",\n \"tab_discover and tab_status read from it; set NEXT_PUBLIC_REGISTRY_API_URL or `registryUrl` in tab.config. tab_call and tab_settle work without it\",\n ),\n );\n } else {\n const registry = createRegistryReadClient({\n baseUrl: settings.registryUrl,\n timeoutMs,\n logger,\n ...(options.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),\n });\n const health = await registry.health();\n if (!health.ok) {\n checks.push(\n warn(\"registry-api\", `${registry.baseUrl} did not answer: ${health.error.message}`, \"start the registry read API, or point at a deployed one\"),\n );\n } else {\n checks.push(pass(\"registry-api\", `${registry.baseUrl} is healthy`));\n const services = await registry.services(5);\n if (!services.ok) {\n checks.push(warn(\"registry-services\", `the Service directory could not be read: ${services.error.message}`));\n } else {\n const count = asArray(field(services.value, \"services\")).length;\n const lastBlock = field(field(services.value, \"index\"), \"lastBlock\");\n checks.push(\n count === 0\n ? warn(\n \"registry-services\",\n \"the read API is healthy and no Service is registered yet\",\n \"register a Service, or wait for the indexer to catch up\",\n )\n : pass(\n \"registry-services\",\n `${count} Service(s) indexed${typeof lastBlock === \"number\" ? ` at Monad block ${lastBlock}` : \"\"}`,\n ),\n );\n }\n }\n }\n\n // ---- each configured Service endpoint\n checks.push(...(await endpointChecks(settings, options.fetchImpl, timeoutMs)));\n\n // ---- the MCP client config\n checks.push(clientConfigCheck(options.clientConfigPath));\n\n // ---- the signing key, named and never read\n const keyPresent = env[\"AGENT_PRIVATE_KEY\"] !== undefined;\n checks.push(\n keyPresent\n ? pass(\"settlement-key\", \"AGENT_PRIVATE_KEY is set in this environment; its value is never read here or written anywhere\")\n : warn(\n \"settlement-key\",\n \"AGENT_PRIVATE_KEY is not set\",\n \"tab_settle signs the Settlement with the Agent's own key, read from the environment at signing time. Discovery, calling and status need no key\",\n ),\n );\n\n const counts: Record<CheckStatus, number> = { pass: 0, warn: 0, fail: 0, skip: 0 };\n for (const check of checks) counts[check.status] += 1;\n return { checks, ok: counts.fail === 0, counts };\n}\n\n/** `eth_getCode` at each configured contract address. */\nasync function contractChecks(\n provider: JsonRpcProvider,\n env: NodeJS.ProcessEnv,\n timeoutMs: number,\n): Promise<readonly DoctorCheck[]> {\n const configured: { readonly variable: string; readonly label: string; readonly address: string }[] = [];\n for (const [variable, label] of CONTRACTS) {\n const address = env[variable];\n // The zero address is the placeholder `.env.example` ships before a\n // deployment fills it in, so it means \"not configured\" and not \"no code\".\n if (address === undefined) continue;\n if (!/^0x[a-fA-F0-9]{40}$/.test(address) || address.toLowerCase() === ZERO_ADDRESS) continue;\n configured.push({ variable, label, address });\n }\n\n if (configured.length === 0) {\n return [\n skip(\n \"monad-contracts\",\n \"no contract addresses are configured\",\n \"set SERVICE_REGISTRY_ADDRESS, BOND_ADDRESS, TAB_BOOK_ADDRESS and TAB_SETTLEMENT_ADDRESS\",\n ),\n ];\n }\n\n const results = await Promise.all(\n configured.map(async (entry) => {\n try {\n // Pinned to `latest`, deliberately. Monad finalises within seconds, but a\n // `finalized` read still lags the head, and a contract deployed within\n // the lag would read as absent: a false failure at exactly the moment\n // somebody is checking a fresh deployment.\n const code = await withTimeout(provider.getCode(entry.address, \"latest\"), timeoutMs, \"eth_getCode\");\n return { entry, hasCode: code !== undefined && code !== \"0x\" && code.length > 2, error: undefined };\n } catch (error) {\n return { entry, hasCode: false, error: reason(error) };\n }\n }),\n );\n\n const missing = results.filter((result) => !result.hasCode);\n if (missing.length === 0) {\n return [pass(\"monad-contracts\", `${results.length} contract(s) hold code: ${results.map((r) => r.entry.label).join(\", \")}`)];\n }\n return [\n bad(\n \"monad-contracts\",\n `no code at ${missing.map((result) => `${result.entry.label} (${result.entry.address})`).join(\", \")}`,\n \"the addresses name nothing on this chain: check the environment against the deployment, and that the RPC endpoint is the right network\",\n ),\n ];\n}\n\n/** A `GET /healthz` against each configured Service endpoint. */\nasync function endpointChecks(\n settings: TabMcpSettings,\n fetchImpl: RegistryFetch | undefined,\n timeoutMs: number,\n): Promise<readonly DoctorCheck[]> {\n if (settings.services.length === 0) return [];\n const send = fetchImpl ?? (globalThis as { fetch?: RegistryFetch }).fetch;\n if (send === undefined) return [skip(\"service-endpoints\", \"this host has no global fetch\")];\n\n const signal = (globalThis as { AbortSignal?: { timeout?: (ms: number) => unknown } }).AbortSignal?.timeout?.(timeoutMs);\n const results = await Promise.all(\n settings.services.map(async (entry) => {\n const url = `${entry.endpoint.replace(/\\/+$/, \"\")}/healthz`;\n try {\n const response = await send(url, { method: \"GET\", ...(signal === undefined ? {} : { signal }) });\n return { entry, reachable: response.status >= 200 && response.status < 500, status: response.status };\n } catch (error) {\n return { entry, reachable: false, status: reason(error) };\n }\n }),\n );\n\n const unreachable = results.filter((result) => !result.reachable);\n if (unreachable.length === 0) {\n return [pass(\"service-endpoints\", `${results.length} Service endpoint(s) answered`)];\n }\n return [\n warn(\n \"service-endpoints\",\n `${unreachable.length} of ${results.length} Service endpoint(s) did not answer: ${unreachable\n .map((result) => `${result.entry.serviceId} at ${result.entry.endpoint} (${String(result.status)})`)\n .join(\", \")}`,\n \"a Service that is down is not a broken installation, but tab_call against it will fail\",\n ),\n ];\n}\n\n/** Is the `tab` stanza in the client config, and is it free of credentials? */\nfunction clientConfigCheck(path: string | undefined): DoctorCheck {\n if (path === undefined) {\n return skip(\"mcp-client-config\", \"no MCP client config was named\", \"run `tab connect`, or pass --config\");\n }\n const read = readClientConfig(path);\n if (!read.ok) return bad(\"mcp-client-config\", read.error.message);\n if (!read.value.existed) {\n return warn(\"mcp-client-config\", `\\`${path}\\` does not exist yet`, \"run `tab connect` to write it\");\n }\n const servers = field(read.value.document, \"mcpServers\");\n const stanza = field(servers, TAB_SERVER_KEY);\n if (stanza === undefined) {\n return warn(\"mcp-client-config\", `\\`${path}\\` has no \\`${TAB_SERVER_KEY}\\` server`, \"run `tab connect`\");\n }\n const secret = assertNoSecret(stanza, `the \\`${TAB_SERVER_KEY}\\` stanza in ${path}`);\n if (!secret.ok) return bad(\"mcp-client-config\", secret.error.message);\n const command = isRecord(stanza) ? stanza[\"command\"] : undefined;\n return pass(\"mcp-client-config\", `\\`${path}\\` runs the tab server with \\`${String(command)}\\` and carries no credential`);\n}\n\n/** A promise with a deadline, so one slow endpoint does not hold the whole report. */\nfunction withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`${label} did not answer within ${ms}ms`)), ms);\n promise.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (error: unknown) => {\n clearTimeout(timer);\n reject(error instanceof Error ? error : new Error(String(error)));\n },\n );\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGlD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,wBAAwB,EAAsB,MAAM,2BAA2B,CAAC;AAEzF,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAgCtF,qFAAqF;AACrF,MAAM,SAAS,GAAG;IAChB,CAAC,0BAA0B,EAAE,iBAAiB,CAAC;IAC/C,CAAC,cAAc,EAAE,MAAM,CAAC;IACxB,CAAC,kBAAkB,EAAE,SAAS,CAAC;IAC/B,CAAC,wBAAwB,EAAE,eAAe,CAAC;CACnC,CAAC;AAEX,MAAM,YAAY,GAAG,4CAA4C,CAAC;AAElE,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,MAAc,EAAe,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;AAC/F,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,MAAc,EAAE,IAAa,EAAe,EAAE,CAAC,CAAC;IAC1E,IAAI;IACJ,MAAM,EAAE,MAAM;IACd,MAAM;IACN,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;CACxC,CAAC,CAAC;AACH,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,MAAc,EAAE,IAAa,EAAe,EAAE,CAAC,CAAC;IACzE,IAAI;IACJ,MAAM,EAAE,MAAM;IACd,MAAM;IACN,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;CACxC,CAAC,CAAC;AACH,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,MAAc,EAAE,IAAa,EAAe,EAAE,CAAC,CAAC;IAC1E,IAAI;IACJ,MAAM,EAAE,MAAM;IACd,MAAM;IACN,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;CACxC,CAAC,CAAC;AAEH,MAAM,MAAM,GAAG,CAAC,KAAc,EAAU,EAAE,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAEpG,gFAAgF;AAChF,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAsB;IACpD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,YAAY,CAAC;IAC9C,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC;IAC9C,MAAM,MAAM,GAAkB,EAAE,CAAC;IAEjC,mBAAmB;IACnB,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;IAC9E,MAAM,CAAC,IAAI,CACT,KAAK,IAAI,EAAE;QACT,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC/C,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,+DAA+D,CAAC,CAClH,CAAC;IAEF,0BAA0B;IAC1B,MAAM,CAAC,IAAI,CACT,QAAQ,CAAC,KAAK,KAAK,SAAS;QAC1B,CAAC,CAAC,IAAI,CACF,OAAO,EACP,gCAAgC,EAChC,yFAAyF,CAC1F;QACH,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,KAAK,KAAK,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,gBAAgB,GAAG,CAAC,CAC1F,CAAC;IACF,MAAM,CAAC,IAAI,CACT,QAAQ,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;QAC5B,CAAC,CAAC,IAAI,CACF,mBAAmB,EACnB,qCAAqC,EACrC,wIAAwI,CACzI;QACH,CAAC,CAAC,IAAI,CAAC,mBAAmB,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,iCAAiC,CAAC,CAC5F,CAAC;IAEF,sBAAsB;IACtB,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,CAAC,IAAI,CACT,IAAI,CAAC,WAAW,EAAE,qCAAqC,EAAE,mBAAmB,CAAC,EAC7E,IAAI,CAAC,iBAAiB,EAAE,4CAA4C,CAAC,CACtE,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC;QAClC,2EAA2E;QAC3E,wEAAwE;QACxE,2EAA2E;QAC3E,8CAA8C;QAC9C,MAAM,QAAQ,GAAG,IAAI,eAAe,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACvG,IAAI,CAAC;YACH,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBACzC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,CAAqB,EAAE,SAAS,EAAE,aAAa,CAAC;gBAC3F,WAAW,CAAC,QAAQ,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,iBAAiB,CAAC;aACrE,CAAC,CAAC;YACH,MAAM,OAAO,GAAG,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;YAC1F,MAAM,CAAC,IAAI,CACT,OAAO,KAAK,QAAQ;gBAClB,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,OAAO,aAAa,IAAI,EAAE,CAAC;gBAC3D,CAAC,CAAC,GAAG,CACD,WAAW,EACX,iCAAiC,OAAO,kCAAkC,QAAQ,EAAE,EACpF,0IAA0I,CAC3I,CACN,CAAC;YACF,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,cAAc,CAAC,QAAQ,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;QACnE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CACT,GAAG,CAAC,WAAW,EAAE,GAAG,QAAQ,CAAC,MAAM,uBAAuB,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,qBAAqB,CAAC,EACjG,IAAI,CAAC,iBAAiB,EAAE,sCAAsC,CAAC,CAChE,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,QAAQ,CAAC,OAAO,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;IAED,sCAAsC;IACtC,IAAI,QAAQ,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QACvC,MAAM,CAAC,IAAI,CACT,IAAI,CACF,cAAc,EACd,oCAAoC,EACpC,oJAAoJ,CACrJ,CACF,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,MAAM,QAAQ,GAAG,wBAAwB,CAAC;YACxC,OAAO,EAAE,QAAQ,CAAC,WAAW;YAC7B,SAAS;YACT,MAAM;YACN,GAAG,CAAC,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC;SAC7E,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAC;QACvC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CACT,IAAI,CAAC,cAAc,EAAE,GAAG,QAAQ,CAAC,OAAO,oBAAoB,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,yDAAyD,CAAC,CAC/I,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,GAAG,QAAQ,CAAC,OAAO,aAAa,CAAC,CAAC,CAAC;YACpE,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC5C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,4CAA4C,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC/G,CAAC;iBAAM,CAAC;gBACN,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC;gBAChE,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,WAAW,CAAC,CAAC;gBACrE,MAAM,CAAC,IAAI,CACT,KAAK,KAAK,CAAC;oBACT,CAAC,CAAC,IAAI,CACF,mBAAmB,EACnB,0DAA0D,EAC1D,yDAAyD,CAC1D;oBACH,CAAC,CAAC,IAAI,CACF,mBAAmB,EACnB,GAAG,KAAK,sBAAsB,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,mBAAmB,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CACpG,CACN,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,wCAAwC;IACxC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IAE/E,6BAA6B;IAC7B,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAEzD,6CAA6C;IAC7C,MAAM,UAAU,GAAG,GAAG,CAAC,mBAAmB,CAAC,KAAK,SAAS,CAAC;IAC1D,MAAM,CAAC,IAAI,CACT,UAAU;QACR,CAAC,CAAC,IAAI,CAAC,gBAAgB,EAAE,gGAAgG,CAAC;QAC1H,CAAC,CAAC,IAAI,CACF,gBAAgB,EAChB,8BAA8B,EAC9B,gJAAgJ,CACjJ,CACN,CAAC;IAEF,MAAM,MAAM,GAAgC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IACnF,KAAK,MAAM,KAAK,IAAI,MAAM;QAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;AACnD,CAAC;AAED,yDAAyD;AACzD,KAAK,UAAU,cAAc,CAC3B,QAAyB,EACzB,GAAsB,EACtB,SAAiB;IAEjB,MAAM,UAAU,GAAsF,EAAE,CAAC;IACzG,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC9B,oEAAoE;QACpE,0EAA0E;QAC1E,IAAI,OAAO,KAAK,SAAS;YAAE,SAAS;QACpC,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,WAAW,EAAE,KAAK,YAAY;YAAE,SAAS;QAC7F,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO;YACL,IAAI,CACF,iBAAiB,EACjB,sCAAsC,EACtC,yFAAyF,CAC1F;SACF,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAC7B,IAAI,CAAC;YACH,0EAA0E;YAC1E,uEAAuE;YACvE,sEAAsE;YACtE,2CAA2C;YAC3C,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;YACpG,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QACtG,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC5D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,GAAG,OAAO,CAAC,MAAM,2BAA2B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/H,CAAC;IACD,OAAO;QACL,GAAG,CACD,iBAAiB,EACjB,cAAc,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EACrG,wIAAwI,CACzI;KACF,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,KAAK,UAAU,cAAc,CAC3B,QAAwB,EACxB,SAAoC,EACpC,SAAiB;IAEjB,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9C,MAAM,IAAI,GAAG,SAAS,IAAK,UAAwC,CAAC,KAAK,CAAC;IAC1E,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,mBAAmB,EAAE,+BAA+B,CAAC,CAAC,CAAC;IAE5F,MAAM,MAAM,GAAI,UAAsE,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC;IACzH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACpC,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC;QAC5D,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;YACjG,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;QACxG,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5D,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClE,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,IAAI,CAAC,mBAAmB,EAAE,GAAG,OAAO,CAAC,MAAM,+BAA+B,CAAC,CAAC,CAAC;IACvF,CAAC;IACD,OAAO;QACL,IAAI,CACF,mBAAmB,EACnB,GAAG,WAAW,CAAC,MAAM,OAAO,OAAO,CAAC,MAAM,wCAAwC,WAAW;aAC1F,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,OAAO,MAAM,CAAC,KAAK,CAAC,QAAQ,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;aACnG,IAAI,CAAC,IAAI,CAAC,EAAE,EACf,wFAAwF,CACzF;KACF,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,SAAS,iBAAiB,CAAC,IAAwB;IACjD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC,mBAAmB,EAAE,gCAAgC,EAAE,qCAAqC,CAAC,CAAC;IAC5G,CAAC;IACD,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,GAAG,CAAC,mBAAmB,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAClE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC,mBAAmB,EAAE,KAAK,IAAI,uBAAuB,EAAE,+BAA+B,CAAC,CAAC;IACtG,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IAC9C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC,mBAAmB,EAAE,KAAK,IAAI,eAAe,cAAc,WAAW,EAAE,mBAAmB,CAAC,CAAC;IAC3G,CAAC;IACD,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,EAAE,SAAS,cAAc,gBAAgB,IAAI,EAAE,CAAC,CAAC;IACrF,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACtE,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjE,OAAO,IAAI,CAAC,mBAAmB,EAAE,KAAK,IAAI,iCAAiC,MAAM,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC;AAC5H,CAAC;AAED,sFAAsF;AACtF,SAAS,WAAW,CAAI,OAAmB,EAAE,EAAU,EAAE,KAAa;IACpE,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,KAAK,0BAA0B,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAChG,OAAO,CAAC,IAAI,CACV,CAAC,KAAK,EAAE,EAAE;YACR,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;YACjB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACpE,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * `tab doctor`: every check that can be made without a key.\n *\n * The point of the command is that a person who has just run `tab connect` can\n * find out whether it worked before they ask a model to spend anything. So every\n * check here is a read -- an `eth_chainId`, an `eth_getCode`, a `GET /healthz`.\n * Nothing here signs, nothing here writes, and nothing here loads a private key.\n * The one thing it says about a signing key is whether the environment carries\n * one at all, reported as present or absent and never read, because \"you have no\n * key configured\" is the answer to why `tab_settle` will refuse, and printing the\n * key would be the worst possible way to say it.\n *\n * ## Four outcomes, not two\n *\n * A check answers `pass`, `warn`, `fail` or `skip`, and the distinction carries\n * weight. A missing registry read API is a `warn`: discovery and status will not\n * work, but calling and settling will, so it is not a broken installation. An\n * RPC endpoint that answers the wrong chain id is a `fail`: every address in the\n * configuration names a contract on a different chain, and anything built on top\n * of it would be wrong rather than absent. `skip` is for a check whose input was\n * not configured, which is a statement about the configuration and not about the\n * deployment.\n */\n\nimport { JsonRpcProvider, Network } from \"ethers\";\n\nimport type { Logger } from \"../logger.js\";\nimport { silentLogger } from \"../logger.js\";\nimport { asArray, field, isRecord } from \"../mcp/json.js\";\nimport { createRegistryReadClient, type RegistryFetch } from \"../mcp/registry-client.js\";\nimport type { TabMcpSettings } from \"../mcp/settings.js\";\nimport { readClientConfig, TAB_SERVER_KEY, assertNoSecret } from \"./client-config.js\";\n\nexport type CheckStatus = \"pass\" | \"warn\" | \"fail\" | \"skip\";\n\nexport interface DoctorCheck {\n /** Short and stable, so a script can grep for one. */\n readonly name: string;\n readonly status: CheckStatus;\n /** What was found, in one sentence. */\n readonly detail: string;\n /** What to do about it, when there is something to do. */\n readonly hint?: string;\n}\n\nexport interface DoctorReport {\n readonly checks: readonly DoctorCheck[];\n /** True when nothing failed. Warnings do not make a report unhealthy. */\n readonly ok: boolean;\n readonly counts: Readonly<Record<CheckStatus, number>>;\n}\n\nexport interface DoctorOptions {\n readonly settings: TabMcpSettings;\n readonly env?: NodeJS.ProcessEnv;\n /** The MCP client config to inspect, when the caller resolved one. */\n readonly clientConfigPath?: string;\n readonly fetchImpl?: RegistryFetch;\n readonly logger?: Logger;\n /** How long any one network read may take. Defaults to 10 seconds. */\n readonly timeoutMs?: number;\n}\n\n/** The contracts a working deployment has code at, under their environment names. */\nconst CONTRACTS = [\n [\"SERVICE_REGISTRY_ADDRESS\", \"ServiceRegistry\"],\n [\"BOND_ADDRESS\", \"Bond\"],\n [\"TAB_BOOK_ADDRESS\", \"TabBook\"],\n [\"TAB_SETTLEMENT_ADDRESS\", \"TabSettlement\"],\n] as const;\n\nconst ZERO_ADDRESS = \"0x0000000000000000000000000000000000000000\";\n\nconst pass = (name: string, detail: string): DoctorCheck => ({ name, status: \"pass\", detail });\nconst warn = (name: string, detail: string, hint?: string): DoctorCheck => ({\n name,\n status: \"warn\",\n detail,\n ...(hint === undefined ? {} : { hint }),\n});\nconst bad = (name: string, detail: string, hint?: string): DoctorCheck => ({\n name,\n status: \"fail\",\n detail,\n ...(hint === undefined ? {} : { hint }),\n});\nconst skip = (name: string, detail: string, hint?: string): DoctorCheck => ({\n name,\n status: \"skip\",\n detail,\n ...(hint === undefined ? {} : { hint }),\n});\n\nconst reason = (error: unknown): string => (error instanceof Error ? error.message : String(error));\n\n/** Runs every check and answers the whole report. Never throws, never signs. */\nexport async function runDoctor(options: DoctorOptions): Promise<DoctorReport> {\n const env = options.env ?? process.env;\n const settings = options.settings;\n const logger = options.logger ?? silentLogger;\n const timeoutMs = options.timeoutMs ?? 10_000;\n const checks: DoctorCheck[] = [];\n\n // ---- the runtime\n const major = Number.parseInt(process.versions.node.split(\".\")[0] ?? \"0\", 10);\n checks.push(\n major >= 20\n ? pass(\"node\", `Node ${process.versions.node}`)\n : bad(\"node\", `Node ${process.versions.node}`, \"the MCP server needs Node 20.10 or later for its global fetch\"),\n );\n\n // ---- what is configured\n checks.push(\n settings.agent === undefined\n ? warn(\n \"agent\",\n \"no Agent address is configured\",\n \"set `agent` in tab.config; tab_call and tab_settle need to know whose Open Tab to meter\",\n )\n : pass(\"agent\", `${settings.agent} (${settings.sources[\"agent\"] ?? \"unknown source\"})`),\n );\n checks.push(\n settings.services.length === 0\n ? warn(\n \"service-directory\",\n \"no Service endpoints are configured\",\n \"the chain records no URL for a Service, so add each one to the `services` list in tab.config or tab_call has nowhere to send a request\",\n )\n : pass(\"service-directory\", `${settings.services.length} Service endpoint(s) configured`),\n );\n\n // ---- Monad, keyless\n if (settings.rpcUrl === undefined) {\n checks.push(\n skip(\"monad-rpc\", \"no Monad RPC endpoint is configured\", \"set MONAD_RPC_URL\"),\n skip(\"monad-contracts\", \"skipped: there is no endpoint to read from\"),\n );\n } else {\n const expected = settings.chainId;\n // Pinned to the configured chain so ethers never probes the network on its\n // own, and `eth_chainId` is then asked directly: a static network would\n // otherwise answer the configured id without the endpoint being consulted,\n // which is the one check this exists to make.\n const provider = new JsonRpcProvider(settings.rpcUrl, Network.from(expected), { staticNetwork: true });\n try {\n const [answered, head] = await Promise.all([\n withTimeout(provider.send(\"eth_chainId\", []) as Promise<unknown>, timeoutMs, \"eth_chainId\"),\n withTimeout(provider.getBlockNumber(), timeoutMs, \"eth_blockNumber\"),\n ]);\n const chainId = typeof answered === \"string\" ? Number.parseInt(answered, 16) : Number.NaN;\n checks.push(\n chainId === expected\n ? pass(\"monad-rpc\", `chain id ${chainId} at block ${head}`)\n : bad(\n \"monad-rpc\",\n `the endpoint answers chain id ${chainId} and the configuration expects ${expected}`,\n \"every contract address in the configuration names a contract on one chain; reading a different one would produce confident wrong answers\",\n ),\n );\n checks.push(...(await contractChecks(provider, env, timeoutMs)));\n } catch (error) {\n checks.push(\n bad(\"monad-rpc\", `${settings.rpcUrl} could not be read: ${reason(error)}`, \"check MONAD_RPC_URL\"),\n skip(\"monad-contracts\", \"skipped: the endpoint did not answer\"),\n );\n } finally {\n provider.destroy();\n }\n }\n\n // ---- the registry read API, keyless\n if (settings.registryUrl === undefined) {\n checks.push(\n warn(\n \"registry-api\",\n \"no registry read API is configured\",\n \"tab_discover and tab_status read from it; set NEXT_PUBLIC_REGISTRY_API_URL or `registryUrl` in tab.config. tab_call and tab_settle work without it\",\n ),\n );\n } else {\n const registry = createRegistryReadClient({\n baseUrl: settings.registryUrl,\n timeoutMs,\n logger,\n ...(options.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),\n });\n const health = await registry.health();\n if (!health.ok) {\n checks.push(\n warn(\"registry-api\", `${registry.baseUrl} did not answer: ${health.error.message}`, \"start the registry read API, or point at a deployed one\"),\n );\n } else {\n checks.push(pass(\"registry-api\", `${registry.baseUrl} is healthy`));\n const services = await registry.services(5);\n if (!services.ok) {\n checks.push(warn(\"registry-services\", `the Service directory could not be read: ${services.error.message}`));\n } else {\n const count = asArray(field(services.value, \"services\")).length;\n const lastBlock = field(field(services.value, \"index\"), \"lastBlock\");\n checks.push(\n count === 0\n ? warn(\n \"registry-services\",\n \"the read API is healthy and no Service is registered yet\",\n \"register a Service, or wait for the indexer to catch up\",\n )\n : pass(\n \"registry-services\",\n `${count} Service(s) indexed${typeof lastBlock === \"number\" ? ` at Monad block ${lastBlock}` : \"\"}`,\n ),\n );\n }\n }\n }\n\n // ---- each configured Service endpoint\n checks.push(...(await endpointChecks(settings, options.fetchImpl, timeoutMs)));\n\n // ---- the MCP client config\n checks.push(clientConfigCheck(options.clientConfigPath));\n\n // ---- the signing key, named and never read\n const keyPresent = env[\"AGENT_PRIVATE_KEY\"] !== undefined;\n checks.push(\n keyPresent\n ? pass(\"settlement-key\", \"AGENT_PRIVATE_KEY is set in this environment; its value is never read here or written anywhere\")\n : warn(\n \"settlement-key\",\n \"AGENT_PRIVATE_KEY is not set\",\n \"tab_settle signs the Settlement with the Agent's own key, read from the environment at signing time. Discovery, calling and status need no key\",\n ),\n );\n\n const counts: Record<CheckStatus, number> = { pass: 0, warn: 0, fail: 0, skip: 0 };\n for (const check of checks) counts[check.status] += 1;\n return { checks, ok: counts.fail === 0, counts };\n}\n\n/** `eth_getCode` at each configured contract address. */\nasync function contractChecks(\n provider: JsonRpcProvider,\n env: NodeJS.ProcessEnv,\n timeoutMs: number,\n): Promise<readonly DoctorCheck[]> {\n const configured: { readonly variable: string; readonly label: string; readonly address: string }[] = [];\n for (const [variable, label] of CONTRACTS) {\n const address = env[variable];\n // The zero address is the placeholder `.env.example` ships before a\n // deployment fills it in, so it means \"not configured\" and not \"no code\".\n if (address === undefined) continue;\n if (!/^0x[a-fA-F0-9]{40}$/.test(address) || address.toLowerCase() === ZERO_ADDRESS) continue;\n configured.push({ variable, label, address });\n }\n\n if (configured.length === 0) {\n return [\n skip(\n \"monad-contracts\",\n \"no contract addresses are configured\",\n \"set SERVICE_REGISTRY_ADDRESS, BOND_ADDRESS, TAB_BOOK_ADDRESS and TAB_SETTLEMENT_ADDRESS\",\n ),\n ];\n }\n\n const results = await Promise.all(\n configured.map(async (entry) => {\n try {\n // Pinned to `latest`, deliberately. Monad finalises within seconds, but a\n // `finalized` read still lags the head, and a contract deployed within\n // the lag would read as absent: a false failure at exactly the moment\n // somebody is checking a fresh deployment.\n const code = await withTimeout(provider.getCode(entry.address, \"latest\"), timeoutMs, \"eth_getCode\");\n return { entry, hasCode: code !== undefined && code !== \"0x\" && code.length > 2, error: undefined };\n } catch (error) {\n return { entry, hasCode: false, error: reason(error) };\n }\n }),\n );\n\n const missing = results.filter((result) => !result.hasCode);\n if (missing.length === 0) {\n return [pass(\"monad-contracts\", `${results.length} contract(s) hold code: ${results.map((r) => r.entry.label).join(\", \")}`)];\n }\n return [\n bad(\n \"monad-contracts\",\n `no code at ${missing.map((result) => `${result.entry.label} (${result.entry.address})`).join(\", \")}`,\n \"the addresses name nothing on this chain: check the environment against the deployment, and that the RPC endpoint is the right network\",\n ),\n ];\n}\n\n/** A `GET /healthz` against each configured Service endpoint. */\nasync function endpointChecks(\n settings: TabMcpSettings,\n fetchImpl: RegistryFetch | undefined,\n timeoutMs: number,\n): Promise<readonly DoctorCheck[]> {\n if (settings.services.length === 0) return [];\n const send = fetchImpl ?? (globalThis as { fetch?: RegistryFetch }).fetch;\n if (send === undefined) return [skip(\"service-endpoints\", \"this host has no global fetch\")];\n\n const signal = (globalThis as { AbortSignal?: { timeout?: (ms: number) => unknown } }).AbortSignal?.timeout?.(timeoutMs);\n const results = await Promise.all(\n settings.services.map(async (entry) => {\n const url = `${entry.endpoint.replace(/\\/+$/, \"\")}/healthz`;\n try {\n const response = await send(url, { method: \"GET\", ...(signal === undefined ? {} : { signal }) });\n return { entry, reachable: response.status >= 200 && response.status < 500, status: response.status };\n } catch (error) {\n return { entry, reachable: false, status: reason(error) };\n }\n }),\n );\n\n const unreachable = results.filter((result) => !result.reachable);\n if (unreachable.length === 0) {\n return [pass(\"service-endpoints\", `${results.length} Service endpoint(s) answered`)];\n }\n return [\n warn(\n \"service-endpoints\",\n `${unreachable.length} of ${results.length} Service endpoint(s) did not answer: ${unreachable\n .map((result) => `${result.entry.serviceId} at ${result.entry.endpoint} (${String(result.status)})`)\n .join(\", \")}`,\n \"a Service that is down is not a broken installation, but tab_call against it will fail\",\n ),\n ];\n}\n\n/** Is the `tab` stanza in the client config, and is it free of credentials? */\nfunction clientConfigCheck(path: string | undefined): DoctorCheck {\n if (path === undefined) {\n return skip(\"mcp-client-config\", \"no MCP client config was named\", \"run `tab connect`, or pass --config\");\n }\n const read = readClientConfig(path);\n if (!read.ok) return bad(\"mcp-client-config\", read.error.message);\n if (!read.value.existed) {\n return warn(\"mcp-client-config\", `\\`${path}\\` does not exist yet`, \"run `tab connect` to write it\");\n }\n const servers = field(read.value.document, \"mcpServers\");\n const stanza = field(servers, TAB_SERVER_KEY);\n if (stanza === undefined) {\n return warn(\"mcp-client-config\", `\\`${path}\\` has no \\`${TAB_SERVER_KEY}\\` server`, \"run `tab connect`\");\n }\n const secret = assertNoSecret(stanza, `the \\`${TAB_SERVER_KEY}\\` stanza in ${path}`);\n if (!secret.ok) return bad(\"mcp-client-config\", secret.error.message);\n const command = isRecord(stanza) ? stanza[\"command\"] : undefined;\n return pass(\"mcp-client-config\", `\\`${path}\\` runs the tab server with \\`${String(command)}\\` and carries no credential`);\n}\n\n/** A promise with a deadline, so one slow endpoint does not hold the whole report. */\nfunction withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`${label} did not answer within ${ms}ms`)), ms);\n promise.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (error: unknown) => {\n clearTimeout(timer);\n reject(error instanceof Error ? error : new Error(String(error)));\n },\n );\n });\n}\n"]}
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The CLI: the executable entry point of this package.
|
|
2
|
+
* The CLI: the executable entry point of this package.
|
|
3
3
|
*
|
|
4
4
|
* `connect` writes one `mcpServers` stanza into an MCP client's config, never a
|
|
5
5
|
* key. `doctor` checks the installation with reads alone. `status` and `settle`
|
package/dist/cli/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The CLI: the executable entry point of this package.
|
|
2
|
+
* The CLI: the executable entry point of this package.
|
|
3
3
|
*
|
|
4
4
|
* `connect` writes one `mcpServers` stanza into an MCP client's config, never a
|
|
5
5
|
* key. `doctor` checks the installation with reads alone. `status` and `settle`
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC","sourcesContent":["/**\n * The CLI: the executable entry point of this package
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC","sourcesContent":["/**\n * The CLI: the executable entry point of this package.\n *\n * `connect` writes one `mcpServers` stanza into an MCP client's config, never a\n * key. `doctor` checks the installation with reads alone. `status` and `settle`\n * are the two tools reachable without a model, and `settle` is a dry run unless\n * it is told otherwise.\n */\n\nexport * from \"./client-config.js\";\nexport * from \"./connect.js\";\nexport * from \"./doctor.js\";\nexport * from \"./main.js\";\n"]}
|
package/dist/cli/main.d.ts
CHANGED
|
@@ -22,8 +22,6 @@
|
|
|
22
22
|
* the surface, reports exactly what would be sent, and sends nothing. The MCP
|
|
23
23
|
* tool has the same `dryRun` field for the same reason, defaulted the other way
|
|
24
24
|
* because a model calling `tab_settle` has already decided.
|
|
25
|
-
*
|
|
26
|
-
* Requirements: 25.5, 25.6
|
|
27
25
|
*/
|
|
28
26
|
/** The name the help text uses. `npx @tabai/sdk` resolves to this. */
|
|
29
27
|
export declare const CLI_NAME = "tab";
|
|
@@ -37,7 +35,7 @@ export interface CliOptions {
|
|
|
37
35
|
readonly io?: CliIo;
|
|
38
36
|
readonly env?: NodeJS.ProcessEnv;
|
|
39
37
|
readonly cwd?: string;
|
|
40
|
-
/** Blocks the
|
|
38
|
+
/** Blocks `settle --broadcast`, the one command that spends, so a test can never broadcast. */
|
|
41
39
|
readonly allowBroadcast?: boolean;
|
|
42
40
|
}
|
|
43
41
|
/** Runs one invocation and answers a process exit code. Never throws. */
|
package/dist/cli/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAYH,sEAAsE;AACtE,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAE9B,MAAM,WAAW,KAAK;IACpB,sGAAsG;IACtG,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,iCAAiC;IACjC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAOD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC;IACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,+FAA+F;IAC/F,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AA6UD,yEAAyE;AACzE,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAyC/F"}
|
package/dist/cli/main.js
CHANGED
|
@@ -22,12 +22,10 @@
|
|
|
22
22
|
* the surface, reports exactly what would be sent, and sends nothing. The MCP
|
|
23
23
|
* tool has the same `dryRun` field for the same reason, defaulted the other way
|
|
24
24
|
* because a model calling `tab_settle` has already decided.
|
|
25
|
-
*
|
|
26
|
-
* Requirements: 25.5, 25.6
|
|
27
25
|
*/
|
|
28
26
|
import { ok } from "../_shared/index.js";
|
|
29
27
|
import { validationError } from "../errors.js";
|
|
30
|
-
import { createTabMcpServer, stderrLogger } from "../mcp/server.js";
|
|
28
|
+
import { TAB_MCP_SERVER_INFO, createTabMcpServer, stderrLogger } from "../mcp/server.js";
|
|
31
29
|
import { resolveTabMcpSettings } from "../mcp/settings.js";
|
|
32
30
|
import { detectMcpClients } from "./client-config.js";
|
|
33
31
|
import { runConnect } from "./connect.js";
|
|
@@ -114,12 +112,15 @@ Environment
|
|
|
114
112
|
MONAD_RPC_URL the Monad JSON-RPC endpoint
|
|
115
113
|
MONAD_CHAIN_ID 143 for Mainnet, 10143 for Testnet
|
|
116
114
|
NEXT_PUBLIC_REGISTRY_API_URL the Tab registry read API, which serves discovery and status
|
|
117
|
-
|
|
115
|
+
AGENT_ADDRESS the Agent tab_call meters against, when tab.config names none
|
|
116
|
+
AGENT_PRIVATE_KEY read only when a Settlement or a call to the hosted demo Service
|
|
117
|
+
is signed, and never written anywhere
|
|
118
118
|
|
|
119
119
|
Configuration
|
|
120
120
|
tab.config.ts beside your project declares the Agent address, the registry URL,
|
|
121
121
|
the Service endpoints, and the payment strategies. The chain records no Service
|
|
122
|
-
endpoint, so tab_call needs one from there
|
|
122
|
+
endpoint, so tab_call needs one from there, except for the hosted demo Service,
|
|
123
|
+
which is known on both networks.
|
|
123
124
|
`;
|
|
124
125
|
// ---------------------------------------------------------------- commands
|
|
125
126
|
async function settingsFor(flags, options) {
|
|
@@ -337,7 +338,7 @@ export async function runCli(argv, options = {}) {
|
|
|
337
338
|
const flags = parsed.value;
|
|
338
339
|
const command = flags.positional[0];
|
|
339
340
|
if (flags.booleans.has("version")) {
|
|
340
|
-
io.out(
|
|
341
|
+
io.out(TAB_MCP_SERVER_INFO.version);
|
|
341
342
|
return 0;
|
|
342
343
|
}
|
|
343
344
|
if (command === undefined || flags.booleans.has("help") || command === "help") {
|