🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@getcordon/cli

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@getcordon/cli - npm Package Compare versions

Comparing version
0.4.1
to
0.4.2
+108
-0
dist/bin/cordon.js

@@ -664,2 +664,105 @@ #!/usr/bin/env node

// src/commands/replay.ts
import { createInterface as createInterface2 } from "readline";
import { UpstreamManager, AuditLogger } from "@getcordon/core";
function fail(msg) {
process.stderr.write(`\x1B[31merror\x1B[0m: ${msg}
`);
process.exit(1);
}
async function confirm(question) {
if (!process.stdin.isTTY) return false;
const rl = createInterface2({ input: process.stdin, output: process.stderr });
return new Promise((resolve) => {
rl.question(question, (answer) => {
rl.close();
const t = answer.trim().toLowerCase();
resolve(t === "y" || t === "yes");
});
});
}
async function replayCommand(callId, options) {
const auth = getAuth();
if (!auth) fail("not logged in. Run `cordon login` first.");
const endpoint = (options.endpoint ?? auth.endpoint).replace(/\/$/, "");
const res = await fetch(`${endpoint}/approvals/${encodeURIComponent(callId)}`, {
headers: { "X-Cordon-Key": auth.apiKey }
});
if (res.status === 404) fail(`no approval found for callId '${callId}' on this account.`);
if (!res.ok) fail(`failed to fetch approval: HTTP ${res.status}`);
const record = await res.json();
if (!(record.status === "approved" && record.resolvedLate)) {
fail(
`call '${callId}' is not replayable (status=${record.status}, resolvedLate=${record.resolvedLate}). Only a call approved AFTER it timed out can be replayed \u2014 replaying anything else would double-execute or run an unauthorized call.`
);
}
if (!record.serverName || !record.toolName) {
fail(`approval '${callId}' is missing server/tool info; cannot replay.`);
}
const config = await loadConfig(await findConfigPath(options.config));
const server = config.servers.find((s) => s.name === record.serverName);
if (!server) {
fail(`server '${record.serverName}' is not in your cordon.config.ts. Add it (or pass --config) and retry.`);
}
process.stderr.write(
`
Replay this late-approved call?
server : ${record.serverName}
tool : ${record.toolName}
args : ${JSON.stringify(record.args)}
approved by ${record.approvedBy ?? "unknown"} (after timeout)
This re-executes the tool. Non-idempotent tools have real side effects.
`
);
if (!options.yes) {
const ok = await confirm("Proceed? [y/N] ");
if (!ok) {
process.stderr.write("[cordon] replay cancelled.\n");
return;
}
}
const mgr = new UpstreamManager([server]);
const started = Date.now();
let isError = false;
try {
await mgr.connect();
const result = await mgr.callTool(record.serverName, record.toolName, record.args);
isError = Boolean(result.isError);
process.stderr.write(
`[cordon] replay ${isError ? "returned an error" : "succeeded"} in ${Date.now() - started}ms
`
);
} catch (e) {
isError = true;
process.stderr.write(`[cordon] replay failed: ${String(e)}
`);
} finally {
await mgr.disconnect().catch(() => {
});
}
const audit = new AuditLogger({ enabled: true, output: "hosted", endpoint, apiKey: auth.apiKey });
audit.log({
event: isError ? "tool_call_errored" : "tool_call_completed",
callId,
serverName: record.serverName,
toolName: record.toolName,
args: record.args,
isError,
durationMs: Date.now() - started,
reason: "replay of late-approved call"
});
audit.close();
if (!isError) {
await fetch(`${endpoint}/approvals/${encodeURIComponent(callId)}`, {
method: "DELETE",
headers: { "X-Cordon-Key": auth.apiKey }
}).catch(() => {
});
process.stderr.write("[cordon] logged to the dashboard and cleared from recoverable.\n");
} else {
process.stderr.write("[cordon] left in the recoverable list so you can retry.\n");
}
}
// src/bin/cordon.ts

@@ -676,3 +779,8 @@ var program = new Command();

program.command("logout").description("Remove the local Cordon credentials").action(logoutCommand);
program.command("replay <callId>").description("Re-execute a tool call that was approved after it timed out").option("-c, --config <path>", "Path to cordon.config.ts").option("--endpoint <url>", "Cordon server endpoint (defaults to your logged-in instance)").option("--yes", "Skip the confirmation prompt").action((callId, opts) => replayCommand(callId, opts).catch((err) => {
process.stderr.write(`\x1B[31merror\x1B[0m: replay failed: ${String(err)}
`);
process.exit(1);
}));
program.parse();
//# sourceMappingURL=cordon.js.map
+1
-1

@@ -1,1 +0,1 @@

{"version":3,"sources":["../../src/bin/cordon.ts","../../src/commands/start.ts","../../src/config-loader.ts","../../src/cli-state.ts","../../src/commands/init.ts","../../src/commands/login.ts","../../src/commands/logout.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { startCommand } from '../commands/start.js';\nimport { initCommand } from '../commands/init.js';\nimport { loginCommand } from '../commands/login.js';\nimport { logoutCommand } from '../commands/logout.js';\n\nconst program = new Command();\n\nprogram\n .name('cordon')\n .description('Cordon for MCP — security gateway for MCP tool calls')\n .version('0.1.0');\n\nprogram\n .command('start')\n .description('Start the Cordon gateway')\n .option('-c, --config <path>', 'Path to cordon.config.ts')\n .option('--http', 'Enable HTTP transport (for n8n and other HTTP-speaking MCP clients). Requires CORDON_GATEWAY_TOKEN env var or gateway.authToken in config.')\n .option('--port <port>', 'Port for HTTP transport (default: 7777)', (v) => Number.parseInt(v, 10))\n .action(startCommand);\n\nprogram\n .command('init')\n .description('Generate cordon.config.ts and patch Claude Desktop config')\n .action(initCommand);\n\nprogram\n .command('login')\n .description('Log in to Cordon (browser OAuth) and save an API key locally')\n .option('--endpoint <url>', 'Cordon server endpoint (defaults to the hosted instance)')\n .action((opts) => loginCommand(opts).catch((err) => {\n process.stderr.write(`\\x1b[31merror\\x1b[0m: login failed: ${String(err)}\\n`);\n process.exit(1);\n }));\n\nprogram\n .command('logout')\n .description('Remove the local Cordon credentials')\n .action(logoutCommand);\n\nprogram.parse();\n","import { CordonGateway } from '@getcordon/core';\nimport type { ResolvedConfig } from '@getcordon/policy';\nimport { emptyConfig, findConfigPath, loadConfig } from '../config-loader.js';\nimport { getState, setState, getAuth } from '../cli-state.js';\n\nconst DASHBOARD_URL = 'https://app.getcordon.com/dashboard/';\n\nfunction applyAuthDefaults(config: ResolvedConfig): ResolvedConfig {\n const auth = getAuth();\n if (!auth) return config;\n\n let next = config;\n\n // Hosted audit: fill endpoint/apiKey from auth.json when not already set.\n const audit = next.audit;\n const usesHosted =\n audit && (audit.output === 'hosted' || (Array.isArray(audit.output) && audit.output.includes('hosted')));\n if (usesHosted && !(audit.endpoint && audit.apiKey)) {\n next = {\n ...next,\n audit: { ...audit, endpoint: audit.endpoint ?? auth.endpoint, apiKey: audit.apiKey ?? auth.apiKey },\n };\n }\n\n // Slack approvals are server-driven — fill endpoint/apiKey so the local side\n // can register + poll without the user hand-writing them into the config.\n const approvals = next.approvals;\n if (approvals?.channel === 'slack' && !(approvals.endpoint && approvals.apiKey)) {\n next = {\n ...next,\n approvals: { ...approvals, endpoint: approvals.endpoint ?? auth.endpoint, apiKey: approvals.apiKey ?? auth.apiKey },\n };\n }\n\n return next;\n}\n\ninterface StartOptions {\n config?: string;\n /** Enable HTTP/Streamable HTTP transport (overrides config.gateway.transport). */\n http?: boolean;\n /** Port for HTTP transport (overrides config.gateway.port). */\n port?: number;\n}\n\n/**\n * Apply CLI flags + env vars on top of the loaded config so the HTTP transport\n * can be enabled without editing the config file. Precedence:\n * - `--http` flag forces HTTP transport regardless of config.\n * - `--port` flag overrides config.gateway.port.\n * - authToken comes from config.gateway.authToken if present, otherwise\n * the `CORDON_GATEWAY_TOKEN` env var.\n * - host comes from config (defaults to localhost in core/transport/http.ts).\n */\nfunction applyHttpFlags(config: ResolvedConfig, options: StartOptions): ResolvedConfig {\n const wantHttp = options.http === true || config.gateway?.transport === 'http';\n if (!wantHttp) {\n if (options.port !== undefined) {\n process.stderr.write(\n '\\x1b[33m[cordon] --port has no effect without --http (or gateway config). Ignoring.\\x1b[0m\\n',\n );\n }\n return config;\n }\n\n const httpConfig = config.gateway?.transport === 'http' ? config.gateway : undefined;\n const authToken = httpConfig?.authToken ?? process.env.CORDON_GATEWAY_TOKEN;\n\n if (!authToken) {\n process.stderr.write(\n '\\x1b[31merror\\x1b[0m: HTTP transport requires an auth token.\\n' +\n ' Set `gateway: { transport: \"http\", authToken: process.env.CORDON_GATEWAY_TOKEN }` in\\n' +\n ' cordon.config.ts, or export `CORDON_GATEWAY_TOKEN` before running `cordon start --http`.\\n',\n );\n process.exit(1);\n }\n\n return {\n ...config,\n gateway: {\n transport: 'http',\n authToken,\n ...(options.port !== undefined ? { port: options.port } : httpConfig?.port !== undefined ? { port: httpConfig.port } : {}),\n ...(httpConfig?.host !== undefined ? { host: httpConfig.host } : {}),\n },\n };\n}\n\nexport async function startCommand(options: StartOptions): Promise<void> {\n if (!getState().welcomed && !getAuth()) {\n process.stderr.write(\n `\\n\\x1b[36m[cordon] Want centralized audit logs + Slack approvals?\\x1b[0m\\n` +\n `[cordon] Run \\`cordon login\\` or register at ${DASHBOARD_URL}?utm_source=cli_start\\n\\n`,\n );\n setState({ welcomed: true });\n }\n\n // Default to an empty server list so auto-install probes (Glama, MCP\n // registry, fresh `npx -y @getcordon/cli start`) succeed instead of crashing.\n // Real users get a loud stderr warning so they don't silently run a no-op.\n let config: ResolvedConfig = emptyConfig();\n let configPath: string | null = null;\n\n try {\n configPath = await findConfigPath(options.config);\n } catch (err) {\n if (options.config) {\n process.stderr.write(`\\x1b[31merror\\x1b[0m: ${String(err)}\\n`);\n process.exit(1);\n }\n process.stderr.write(\n '\\x1b[33m[cordon] no cordon.config.ts found — starting with zero upstream servers.\\x1b[0m\\n' +\n '[cordon] Run `cordon init` to generate a config and connect real MCP servers.\\n',\n );\n }\n\n if (configPath) {\n process.stderr.write(`[cordon] loading config from ${configPath}\\n`);\n try {\n config = await loadConfig(configPath);\n } catch (err) {\n process.stderr.write(`\\x1b[31merror\\x1b[0m: ${String(err)}\\n`);\n process.exit(1);\n }\n }\n\n config = applyAuthDefaults(config);\n config = applyHttpFlags(config, options);\n\n const gateway = new CordonGateway(config);\n\n const shutdown = async () => {\n process.stderr.write('\\n[cordon] shutting down...\\n');\n try {\n await gateway.stop();\n process.exit(0);\n } catch (err) {\n process.stderr.write(`\\x1b[31merror\\x1b[0m: shutdown failed: ${String(err)}\\n`);\n process.exit(1);\n }\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n try {\n await gateway.start();\n } catch (err) {\n process.stderr.write(`\\x1b[31merror\\x1b[0m: gateway failed: ${String(err)}\\n`);\n process.exit(1);\n }\n}\n","import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { createJiti } from 'jiti';\nimport type { CordonConfig, ResolvedConfig } from '@getcordon/policy';\n\nconst SEARCH_PATHS = [\n () => join(process.cwd(), 'cordon.config.ts'),\n () => join(process.cwd(), 'cordon.config.js'),\n () => join(homedir(), '.cordon', 'config.ts'),\n () => join(homedir(), '.cordon', 'config.js'),\n];\n\nexport async function findConfigPath(explicit?: string): Promise<string> {\n if (explicit) {\n if (!existsSync(explicit)) {\n throw new Error(`Config file not found: ${explicit}`);\n }\n return explicit;\n }\n\n for (const candidate of SEARCH_PATHS) {\n const p = candidate();\n if (existsSync(p)) return p;\n }\n\n throw new Error(\n 'No cordon config found. Run `cordon init` to create one, or pass --config <path>.',\n );\n}\n\nexport async function loadConfig(configPath: string): Promise<ResolvedConfig> {\n // jiti handles TypeScript config files without requiring a separate compile step\n const jiti = createJiti(import.meta.url);\n const mod = await jiti.import(configPath);\n\n const raw = (mod as { default?: CordonConfig }).default ?? (mod as CordonConfig);\n\n if (!raw || typeof raw !== 'object' || !Array.isArray(raw.servers)) {\n throw new Error(\n `Invalid config at ${configPath}: expected an object with a 'servers' array. ` +\n `Make sure you are using \\`export default defineConfig({...})\\`.`,\n );\n }\n\n return applyDefaults(raw as CordonConfig);\n}\n\nfunction applyDefaults(config: CordonConfig): ResolvedConfig {\n return {\n ...config,\n audit: config.audit ?? { enabled: true, output: 'stdout' },\n approvals: config.approvals ?? { channel: 'terminal' },\n };\n}\n\nexport function emptyConfig(): ResolvedConfig {\n return applyDefaults({ servers: [] });\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync, chmodSync, unlinkSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\ninterface CliState {\n welcomed?: boolean;\n}\n\nexport interface CliAuth {\n endpoint: string;\n apiKey: string;\n userLogin?: string;\n loggedInAt: string;\n}\n\nfunction cordonDir(): string {\n return join(homedir(), '.cordon');\n}\n\nfunction statePath(): string {\n return join(cordonDir(), 'state.json');\n}\n\nfunction authPath(): string {\n return join(cordonDir(), 'auth.json');\n}\n\nfunction ensureCordonDir(): void {\n const dir = cordonDir();\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n}\n\nexport function getState(): CliState {\n const path = statePath();\n if (!existsSync(path)) return {};\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as CliState;\n } catch {\n return {};\n }\n}\n\nexport function setState(patch: CliState): void {\n try {\n ensureCordonDir();\n const merged = { ...getState(), ...patch };\n writeFileSync(statePath(), JSON.stringify(merged, null, 2), 'utf8');\n } catch {\n // Non-fatal: missing state just means we re-show the banner next time.\n }\n}\n\nexport function getAuth(): CliAuth | null {\n const path = authPath();\n if (!existsSync(path)) return null;\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as CliAuth;\n } catch {\n return null;\n }\n}\n\nexport function setAuth(auth: CliAuth): void {\n ensureCordonDir();\n const path = authPath();\n writeFileSync(path, JSON.stringify(auth, null, 2), 'utf8');\n if (process.platform !== 'win32') {\n try { chmodSync(path, 0o600); } catch { /* best-effort */ }\n }\n}\n\nexport function clearAuth(): boolean {\n const path = authPath();\n if (!existsSync(path)) return false;\n try {\n unlinkSync(path);\n return true;\n } catch {\n return false;\n }\n}\n","import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { spawnSync } from 'node:child_process';\nimport { createInterface } from 'node:readline';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { setState, getAuth } from '../cli-state.js';\nimport { loginCommand } from './login.js';\n\nconst DASHBOARD_URL = 'https://app.getcordon.com/dashboard/';\n\nfunction ensureCordonSdkInstalled(cwd: string): void {\n // Config imports from '@getcordon/policy', and jiti resolves it from the config\n // file's directory. Without a local install, `cordon start` dies with\n // \"Cannot find module '@getcordon/policy'\" even when the CLI is global.\n if (existsSync(join(cwd, 'node_modules', '@getcordon/policy', 'package.json'))) {\n return;\n }\n\n if (!existsSync(join(cwd, 'package.json'))) {\n writeFileSync(\n join(cwd, 'package.json'),\n JSON.stringify({ name: 'cordon-config', version: '0.0.0', private: true }, null, 2) + '\\n',\n 'utf8',\n );\n process.stderr.write(`\\x1b[32m✓\\x1b[0m created package.json\\n`);\n }\n\n process.stderr.write(`[cordon] installing @getcordon/policy...\\n`);\n const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';\n const result = spawnSync(npmCmd, ['install', '@getcordon/policy'], {\n cwd,\n stdio: ['ignore', 'inherit', 'inherit'],\n });\n\n if (result.status === 0) {\n process.stderr.write(`\\x1b[32m✓\\x1b[0m installed @getcordon/policy\\n`);\n } else {\n process.stderr.write(\n `\\x1b[33mwarn\\x1b[0m: could not auto-install @getcordon/policy. ` +\n `Run 'npm install @getcordon/policy' in this directory before 'cordon start'.\\n`,\n );\n }\n}\n\nasync function promptYesNo(question: string): Promise<boolean> {\n // Non-interactive (CI, piped stdin): skip rather than surprise the operator\n // with a browser tab and a 5-minute callback timeout.\n if (!process.stdin.isTTY) return false;\n const rl = createInterface({ input: process.stdin, output: process.stderr });\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n const trimmed = answer.trim().toLowerCase();\n // Empty = default Yes\n resolve(trimmed === '' || trimmed === 'y' || trimmed === 'yes');\n });\n });\n}\n\ninterface ClaudeDesktopConfig {\n mcpServers?: Record<string, { command: string; args?: string[]; env?: Record<string, string> }>;\n}\n\nfunction getClaudeConfigPath(): string | null {\n const candidates: string[] = [];\n\n if (process.platform === 'darwin') {\n candidates.push(\n join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'),\n );\n } else if (process.platform === 'win32') {\n const appData = process.env['APPDATA'] ?? join(homedir(), 'AppData', 'Roaming');\n candidates.push(join(appData, 'Claude', 'claude_desktop_config.json'));\n } else {\n candidates.push(join(homedir(), '.config', 'Claude', 'claude_desktop_config.json'));\n }\n\n return candidates.find((p) => existsSync(p)) ?? null;\n}\n\nexport async function initCommand(): Promise<void> {\n const outputPath = join(process.cwd(), 'cordon.config.ts');\n\n if (existsSync(outputPath)) {\n process.stderr.write(\n `\\x1b[33mwarn\\x1b[0m: cordon.config.ts already exists — not overwriting.\\n`,\n );\n process.exit(1);\n }\n\n // Try to discover existing MCP servers from Claude Desktop config\n const claudePath = getClaudeConfigPath();\n let claudeConfig: ClaudeDesktopConfig = {};\n\n if (claudePath) {\n process.stderr.write(`[cordon] found Claude Desktop config at ${claudePath}\\n`);\n try {\n claudeConfig = JSON.parse(readFileSync(claudePath, 'utf8')) as ClaudeDesktopConfig;\n } catch {\n process.stderr.write(`\\x1b[33mwarn\\x1b[0m: could not parse Claude Desktop config\\n`);\n }\n } else {\n process.stderr.write(\n `[cordon] no Claude Desktop config found — generating a blank config\\n`,\n );\n }\n\n const servers = Object.entries(claudeConfig.mcpServers ?? {});\n\n // Generate cordon.config.ts\n const serverBlocks = servers.length > 0\n ? servers\n .map(([name, cfg]) => {\n const argsStr = cfg.args?.length\n ? `, args: ${JSON.stringify(cfg.args)}`\n : '';\n const envStr =\n cfg.env && Object.keys(cfg.env).length\n ? `, env: ${JSON.stringify(cfg.env)}`\n : '';\n return ` {\n name: ${JSON.stringify(name)},\n transport: 'stdio',\n command: ${JSON.stringify(cfg.command)}${argsStr}${envStr},\n policy: 'allow',\n // tools: {\n // execute: 'approve',\n // delete: 'block',\n // },\n },`;\n })\n .join('\\n')\n : ` // {\n // name: 'my-server',\n // transport: 'stdio',\n // command: 'npx',\n // args: ['-y', '@my-org/my-mcp-server'],\n // policy: 'allow',\n // },`;\n\n let auth = getAuth();\n let promptShown = false;\n if (!auth && process.stdin.isTTY) {\n promptShown = true;\n process.stderr.write(\n `\\n\\x1b[36mCordon works locally out of the box.\\x1b[0m ` +\n `Sign in to enable hosted audit logs + Slack approvals (free).\\n`,\n );\n const wantsLogin = await promptYesNo('Sign in via browser now? [Y/n] ');\n if (wantsLogin) {\n try {\n await loginCommand();\n auth = getAuth();\n } catch (err) {\n const msg = err instanceof Error ? err.message : 'unknown error';\n process.stderr.write(\n `\\x1b[33mwarn\\x1b[0m: login didn't complete (${msg}). ` +\n `Continuing with local stdout audit. Run \\x1b[36mcordon login\\x1b[0m later.\\n`,\n );\n }\n }\n }\n\n const auditBlock = auth\n ? `audit: {\n enabled: true,\n output: 'hosted',\n // endpoint + apiKey are auto-loaded from ~/.cordon/auth.json (cordon login)\n },`\n : `audit: {\n enabled: true,\n // 'auto' streams to stdout until you run \\`cordon login\\`, then auto-switches to hosted.\n output: 'auto',\n },`;\n\n const approvalsBlock = auth\n ? `approvals: {\n // Human-in-the-loop over Slack. Connect your workspace once via\n // \"Add to Slack\" in the dashboard — endpoint + apiKey are auto-loaded.\n channel: 'slack',\n // timeoutMs: 60_000,\n },`\n : `approvals: {\n channel: 'terminal',\n // timeoutMs: 60_000,\n },`;\n\n const content = `import { defineConfig } from '@getcordon/policy';\n\nexport default defineConfig({\n servers: [\n${serverBlocks}\n ],\n\n ${auditBlock}\n\n ${approvalsBlock}\n});\n`;\n\n writeFileSync(outputPath, content, 'utf8');\n process.stderr.write(`\\x1b[32m✓\\x1b[0m wrote cordon.config.ts\\n`);\n\n // The config imports from '@getcordon/policy'. Install it locally so jiti can\n // resolve it when `cordon start` runs.\n ensureCordonSdkInstalled(process.cwd());\n\n // Patch Claude Desktop config to route through cordon.\n //\n // We bind the spawn to the *exact* node + cordon.js paths that ran this\n // init. Earlier versions wrote `npx.cmd` (or `npx`), which relies on\n // PATH lookup at spawn time. Claude Desktop's spawned subprocesses\n // inherit a stripped PATH on Windows that often lacks the user's\n // nvm/Node bin dir, so `npx.cmd` fails with \"is not recognized as an\n // internal or external command\". Using full paths skips PATH and the\n // cmd.exe wrapper entirely.\n if (claudePath && servers.length > 0) {\n const cordonConfigPath = outputPath.replace(/\\\\/g, '/');\n const nodePath = process.execPath.replace(/\\\\/g, '/');\n const cordonScriptPath = (process.argv[1] ?? '').replace(/\\\\/g, '/');\n\n if (!cordonScriptPath) {\n process.stderr.write(\n `\\x1b[33mwarn\\x1b[0m: could not detect cordon CLI script path. ` +\n `Skipping Claude Desktop patch.\\n` +\n `Manually add this to your claude_desktop_config.json mcpServers:\\n` +\n ` \"cordon\": { \"command\": \"${nodePath}\", \"args\": [\"<path-to-cordon.js>\", \"start\", \"--config\", \"${cordonConfigPath}\"] }\\n`,\n );\n return;\n }\n\n const newClaudeConfig: ClaudeDesktopConfig = {\n ...claudeConfig,\n mcpServers: {\n cordon: {\n command: nodePath,\n args: [cordonScriptPath, 'start', '--config', cordonConfigPath],\n },\n },\n };\n\n // Backup the original — only if no backup exists yet, so re-running init\n // never overwrites the user's true pre-cordon config.\n const backupPath = `${claudePath}.cordon-backup`;\n if (existsSync(backupPath)) {\n process.stderr.write(\n `[cordon] existing backup at ${backupPath} preserved (won't overwrite)\\n`,\n );\n } else {\n writeFileSync(backupPath, readFileSync(claudePath, 'utf8'), 'utf8');\n process.stderr.write(`\\x1b[32m✓\\x1b[0m backed up Claude Desktop config to ${backupPath}\\n`);\n }\n\n writeFileSync(claudePath, JSON.stringify(newClaudeConfig, null, 2), 'utf8');\n process.stderr.write(`\\x1b[32m✓\\x1b[0m patched Claude Desktop config\\n`);\n process.stderr.write(\n `\\n\\x1b[36mRestart Claude Desktop to activate Cordon.\\x1b[0m\\n`,\n );\n } else if (!claudePath) {\n const nodePath = process.execPath.replace(/\\\\/g, '/');\n const cordonScriptPath = (process.argv[1] ?? '<path-to-cordon.js>').replace(/\\\\/g, '/');\n process.stderr.write(\n `\\n\\x1b[33mwarn\\x1b[0m: Claude Desktop config not found on this system.\\n` +\n `Edit cordon.config.ts, then manually add Cordon to your MCP client config:\\n\\n` +\n ` \"mcpServers\": {\\n` +\n ` \"cordon\": {\\n` +\n ` \"command\": \"${nodePath}\",\\n` +\n ` \"args\": [\"${cordonScriptPath}\", \"start\", \"--config\", \"${outputPath.replace(/\\\\/g, '/')}\"]\\n` +\n ` }\\n` +\n ` }\\n`,\n );\n } else {\n // claudePath found but no existing servers — config written, no patching needed\n process.stderr.write(\n `\\nEdit cordon.config.ts to add your MCP servers, then run \\x1b[36mnpx cordon start\\x1b[0m.\\n`,\n );\n }\n\n if (auth) {\n process.stderr.write(\n `\\n\\x1b[32m✓\\x1b[0m audit logs will stream to your Cordon account (${auth.endpoint})\\n`,\n );\n } else if (promptShown) {\n process.stderr.write(\n `\\n[cordon] running in local mode. Run \\x1b[36mcordon login\\x1b[0m later to enable hosted audit.\\n`,\n );\n } else {\n process.stderr.write(\n `\\n\\x1b[36mWant centralized audit logs + Slack approvals?\\x1b[0m\\n` +\n `Run \\x1b[36mcordon login\\x1b[0m to register a free account, ` +\n `or sign up at ${DASHBOARD_URL}?utm_source=cli_init\\n`,\n );\n }\n setState({ welcomed: true });\n}\n","import { createServer } from 'node:http';\nimport { spawn } from 'node:child_process';\nimport { randomBytes } from 'node:crypto';\nimport { setAuth } from '../cli-state.js';\n\nconst DEFAULT_ENDPOINT = 'https://app.getcordon.com';\nconst PORT_RANGE = [53247, 53249, 53251] as const;\nconst TIMEOUT_MS = 5 * 60 * 1000;\n\ninterface LoginOptions {\n endpoint?: string;\n}\n\nfunction openBrowser(url: string): void {\n const cmd = process.platform === 'darwin' ? 'open'\n : process.platform === 'win32' ? 'cmd'\n : 'xdg-open';\n // On Windows, `cmd /c start \"\" <url>` runs through cmd.exe, which treats\n // `&` and `^` as shell metacharacters. Without escaping them, query strings\n // like `?callback=…&state=…` get truncated at the first `&`, breaking the\n // OAuth state hand-off. Replace `^` first so we don't double-escape new ^&.\n const safeUrl = process.platform === 'win32'\n ? url.replace(/\\^/g, '^^').replace(/&/g, '^&')\n : url;\n const args = process.platform === 'win32' ? ['/c', 'start', '\"\"', safeUrl] : [safeUrl];\n try {\n spawn(cmd, args, { detached: true, stdio: 'ignore' }).unref();\n } catch { /* swallow — caller has already printed the URL for manual paste */ }\n}\n\ninterface CallbackResult {\n token: string;\n state: string;\n signup: string;\n}\n\nfunction renderCallbackPage(kind: 'success' | 'error', heading: string, body: string): string {\n const accent = kind === 'success' ? '#14b8a6' : '#f87171';\n const accentBg = kind === 'success' ? 'rgba(20, 184, 166, 0.15)' : 'rgba(248, 113, 113, 0.15)';\n const mark = kind === 'success' ? '✓' : '✗';\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${kind === 'success' ? 'Logged in' : 'Login failed'} — Cordon for MCP</title>\n <link rel=\"icon\" type=\"image/svg+xml\" href=\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90' fill='%2314b8a6'>◈</text></svg>\">\n <style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n body {\n background: #0d0d0d; color: #e8e8e8;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 1rem;\n }\n .card {\n background: #161616; border: 1px solid #2a2a2a; border-radius: 16px;\n padding: 48px; max-width: 420px; width: 100%; text-align: center;\n }\n .brand { display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 1.75rem; }\n .icon { font-size: 1.4rem; color: #14b8a6; }\n .name { font-weight: 600; font-size: 16px; letter-spacing: -0.3px; white-space: nowrap; }\n .mark {\n width: 56px; height: 56px; border-radius: 50%;\n background: ${accentBg}; color: ${accent};\n display: flex; align-items: center; justify-content: center;\n margin: 0 auto 1.25rem; font-size: 28px; font-weight: 700;\n }\n h1 { font-size: 1.4rem; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 0.6rem; }\n p { color: #888; font-size: 0.95rem; line-height: 1.5; }\n p + p { margin-top: 0.5rem; }\n code {\n background: rgba(255, 255, 255, 0.06); color: #e8e8e8;\n padding: 1px 6px; border-radius: 3px; font-size: 0.85em;\n font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;\n }\n .btn {\n display: inline-block;\n background: #14b8a6; color: #000;\n font-weight: 600; padding: 0.65rem 1.25rem;\n border-radius: 6px; text-decoration: none;\n margin-top: 1.25rem; transition: opacity 0.15s;\n }\n .btn:hover { opacity: 0.85; }\n .hint {\n margin-top: 1.75rem; padding-top: 1.5rem;\n border-top: 1px solid #2a2a2a; font-size: 0.85rem; color: #666;\n }\n </style>\n</head>\n<body>\n <div class=\"card\">\n <div class=\"brand\">\n <span class=\"icon\">◈</span>\n <span class=\"name\">Cordon for MCP</span>\n </div>\n <div class=\"mark\">${mark}</div>\n <h1>${heading}</h1>\n ${body}\n </div>\n</body>\n</html>`;\n}\n\nasync function listenForCallback(port: number, expectedState: string, dashboardUrl: string): Promise<CallbackResult> {\n return new Promise((resolve, reject) => {\n const server = createServer((req, res) => {\n const url = new URL(req.url ?? '/', `http://localhost:${port}`);\n if (url.pathname !== '/cb') {\n res.writeHead(404).end('not found');\n return;\n }\n const token = url.searchParams.get('token') ?? '';\n const state = url.searchParams.get('state') ?? '';\n const signup = url.searchParams.get('signup') ?? 'false';\n\n if (state !== expectedState || !token) {\n const body =\n `<p>The login link didn't carry a valid token, or the security state didn't match.</p>` +\n `<p class=\"hint\">Run <code>cordon login</code> again from your terminal. You can close this tab.</p>`;\n res.writeHead(400, { 'Content-Type': 'text/html' })\n .end(renderCallbackPage('error', \"Login didn't complete\", body));\n server.close();\n reject(new Error('Callback state mismatch'));\n return;\n }\n\n const body =\n `<p>Your API key is saved. Return to your terminal to continue,` +\n ` or jump straight to your dashboard.</p>` +\n `<a href=\"${dashboardUrl}\" class=\"btn\">Open dashboard →</a>` +\n `<p class=\"hint\">You can close this tab.</p>`;\n res.writeHead(200, { 'Content-Type': 'text/html' })\n .end(renderCallbackPage('success', \"You're logged in\", body));\n server.close();\n resolve({ token, state, signup });\n });\n\n server.listen(port, '127.0.0.1');\n server.on('error', (err) => reject(err));\n\n setTimeout(() => {\n server.close();\n reject(new Error('Login timed out after 5 minutes'));\n }, TIMEOUT_MS).unref();\n });\n}\n\nexport async function loginCommand(options: LoginOptions = {}): Promise<void> {\n const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/$/, '');\n const state = randomBytes(16).toString('hex');\n\n let port: number | null = null;\n let result: CallbackResult | null = null;\n let lastErr: unknown = null;\n\n for (const candidate of PORT_RANGE) {\n try {\n const callback = `http://localhost:${candidate}/cb`;\n const authUrl =\n `${endpoint}/auth/cli/start?callback=${encodeURIComponent(callback)}&state=${state}`;\n\n process.stderr.write(\n `\\n[cordon] opening browser to log in...\\n` +\n `[cordon] if it doesn't open, visit:\\n ${authUrl}\\n\\n`,\n );\n openBrowser(authUrl);\n\n const pending = listenForCallback(candidate, state, `${endpoint}/dashboard/`);\n port = candidate;\n result = await pending;\n break;\n } catch (err) {\n lastErr = err;\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'EADDRINUSE') continue;\n throw err;\n }\n }\n\n if (!result) {\n throw lastErr instanceof Error ? lastErr : new Error('Could not bind a local callback port');\n }\n\n setAuth({\n endpoint,\n apiKey: result.token,\n loggedInAt: new Date().toISOString(),\n });\n\n process.stderr.write(`\\x1b[32m✓\\x1b[0m logged in. API key saved to ~/.cordon/auth.json\\n`);\n if (result.signup === 'true') {\n process.stderr.write(\n `Welcome to Cordon for MCP.\\n` +\n `Dashboard: \\x1b[36m${endpoint}/dashboard/\\x1b[0m\\n` +\n `Run \\x1b[36mcordon init\\x1b[0m next to wire up your MCP servers.\\n`,\n );\n }\n // Suppress unused var warning when port is set but not otherwise used\n void port;\n}\n","import { clearAuth } from '../cli-state.js';\n\nexport function logoutCommand(): void {\n if (clearAuth()) {\n process.stderr.write(`\\x1b[32m✓\\x1b[0m logged out. ~/.cordon/auth.json removed\\n`);\n } else {\n process.stderr.write(`[cordon] not logged in\\n`);\n }\n}\n"],"mappings":"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","names":["existsSync","join","homedir","readFileSync","writeFileSync","existsSync","join","homedir","body","DASHBOARD_URL","existsSync","join","writeFileSync","homedir","readFileSync"]}
{"version":3,"sources":["../../src/bin/cordon.ts","../../src/commands/start.ts","../../src/config-loader.ts","../../src/cli-state.ts","../../src/commands/init.ts","../../src/commands/login.ts","../../src/commands/logout.ts","../../src/commands/replay.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { startCommand } from '../commands/start.js';\nimport { initCommand } from '../commands/init.js';\nimport { loginCommand } from '../commands/login.js';\nimport { logoutCommand } from '../commands/logout.js';\nimport { replayCommand } from '../commands/replay.js';\n\nconst program = new Command();\n\nprogram\n .name('cordon')\n .description('Cordon for MCP — security gateway for MCP tool calls')\n .version('0.1.0');\n\nprogram\n .command('start')\n .description('Start the Cordon gateway')\n .option('-c, --config <path>', 'Path to cordon.config.ts')\n .option('--http', 'Enable HTTP transport (for n8n and other HTTP-speaking MCP clients). Requires CORDON_GATEWAY_TOKEN env var or gateway.authToken in config.')\n .option('--port <port>', 'Port for HTTP transport (default: 7777)', (v) => Number.parseInt(v, 10))\n .action(startCommand);\n\nprogram\n .command('init')\n .description('Generate cordon.config.ts and patch Claude Desktop config')\n .action(initCommand);\n\nprogram\n .command('login')\n .description('Log in to Cordon (browser OAuth) and save an API key locally')\n .option('--endpoint <url>', 'Cordon server endpoint (defaults to the hosted instance)')\n .action((opts) => loginCommand(opts).catch((err) => {\n process.stderr.write(`\\x1b[31merror\\x1b[0m: login failed: ${String(err)}\\n`);\n process.exit(1);\n }));\n\nprogram\n .command('logout')\n .description('Remove the local Cordon credentials')\n .action(logoutCommand);\n\nprogram\n .command('replay <callId>')\n .description('Re-execute a tool call that was approved after it timed out')\n .option('-c, --config <path>', 'Path to cordon.config.ts')\n .option('--endpoint <url>', 'Cordon server endpoint (defaults to your logged-in instance)')\n .option('--yes', 'Skip the confirmation prompt')\n .action((callId, opts) => replayCommand(callId, opts).catch((err) => {\n process.stderr.write(`\\x1b[31merror\\x1b[0m: replay failed: ${String(err)}\\n`);\n process.exit(1);\n }));\n\nprogram.parse();\n","import { CordonGateway } from '@getcordon/core';\nimport type { ResolvedConfig } from '@getcordon/policy';\nimport { emptyConfig, findConfigPath, loadConfig } from '../config-loader.js';\nimport { getState, setState, getAuth } from '../cli-state.js';\n\nconst DASHBOARD_URL = 'https://app.getcordon.com/dashboard/';\n\nfunction applyAuthDefaults(config: ResolvedConfig): ResolvedConfig {\n const auth = getAuth();\n if (!auth) return config;\n\n let next = config;\n\n // Hosted audit: fill endpoint/apiKey from auth.json when not already set.\n const audit = next.audit;\n const usesHosted =\n audit && (audit.output === 'hosted' || (Array.isArray(audit.output) && audit.output.includes('hosted')));\n if (usesHosted && !(audit.endpoint && audit.apiKey)) {\n next = {\n ...next,\n audit: { ...audit, endpoint: audit.endpoint ?? auth.endpoint, apiKey: audit.apiKey ?? auth.apiKey },\n };\n }\n\n // Slack approvals are server-driven — fill endpoint/apiKey so the local side\n // can register + poll without the user hand-writing them into the config.\n const approvals = next.approvals;\n if (approvals?.channel === 'slack' && !(approvals.endpoint && approvals.apiKey)) {\n next = {\n ...next,\n approvals: { ...approvals, endpoint: approvals.endpoint ?? auth.endpoint, apiKey: approvals.apiKey ?? auth.apiKey },\n };\n }\n\n return next;\n}\n\ninterface StartOptions {\n config?: string;\n /** Enable HTTP/Streamable HTTP transport (overrides config.gateway.transport). */\n http?: boolean;\n /** Port for HTTP transport (overrides config.gateway.port). */\n port?: number;\n}\n\n/**\n * Apply CLI flags + env vars on top of the loaded config so the HTTP transport\n * can be enabled without editing the config file. Precedence:\n * - `--http` flag forces HTTP transport regardless of config.\n * - `--port` flag overrides config.gateway.port.\n * - authToken comes from config.gateway.authToken if present, otherwise\n * the `CORDON_GATEWAY_TOKEN` env var.\n * - host comes from config (defaults to localhost in core/transport/http.ts).\n */\nfunction applyHttpFlags(config: ResolvedConfig, options: StartOptions): ResolvedConfig {\n const wantHttp = options.http === true || config.gateway?.transport === 'http';\n if (!wantHttp) {\n if (options.port !== undefined) {\n process.stderr.write(\n '\\x1b[33m[cordon] --port has no effect without --http (or gateway config). Ignoring.\\x1b[0m\\n',\n );\n }\n return config;\n }\n\n const httpConfig = config.gateway?.transport === 'http' ? config.gateway : undefined;\n const authToken = httpConfig?.authToken ?? process.env.CORDON_GATEWAY_TOKEN;\n\n if (!authToken) {\n process.stderr.write(\n '\\x1b[31merror\\x1b[0m: HTTP transport requires an auth token.\\n' +\n ' Set `gateway: { transport: \"http\", authToken: process.env.CORDON_GATEWAY_TOKEN }` in\\n' +\n ' cordon.config.ts, or export `CORDON_GATEWAY_TOKEN` before running `cordon start --http`.\\n',\n );\n process.exit(1);\n }\n\n return {\n ...config,\n gateway: {\n transport: 'http',\n authToken,\n ...(options.port !== undefined ? { port: options.port } : httpConfig?.port !== undefined ? { port: httpConfig.port } : {}),\n ...(httpConfig?.host !== undefined ? { host: httpConfig.host } : {}),\n },\n };\n}\n\nexport async function startCommand(options: StartOptions): Promise<void> {\n if (!getState().welcomed && !getAuth()) {\n process.stderr.write(\n `\\n\\x1b[36m[cordon] Want centralized audit logs + Slack approvals?\\x1b[0m\\n` +\n `[cordon] Run \\`cordon login\\` or register at ${DASHBOARD_URL}?utm_source=cli_start\\n\\n`,\n );\n setState({ welcomed: true });\n }\n\n // Default to an empty server list so auto-install probes (Glama, MCP\n // registry, fresh `npx -y @getcordon/cli start`) succeed instead of crashing.\n // Real users get a loud stderr warning so they don't silently run a no-op.\n let config: ResolvedConfig = emptyConfig();\n let configPath: string | null = null;\n\n try {\n configPath = await findConfigPath(options.config);\n } catch (err) {\n if (options.config) {\n process.stderr.write(`\\x1b[31merror\\x1b[0m: ${String(err)}\\n`);\n process.exit(1);\n }\n process.stderr.write(\n '\\x1b[33m[cordon] no cordon.config.ts found — starting with zero upstream servers.\\x1b[0m\\n' +\n '[cordon] Run `cordon init` to generate a config and connect real MCP servers.\\n',\n );\n }\n\n if (configPath) {\n process.stderr.write(`[cordon] loading config from ${configPath}\\n`);\n try {\n config = await loadConfig(configPath);\n } catch (err) {\n process.stderr.write(`\\x1b[31merror\\x1b[0m: ${String(err)}\\n`);\n process.exit(1);\n }\n }\n\n config = applyAuthDefaults(config);\n config = applyHttpFlags(config, options);\n\n const gateway = new CordonGateway(config);\n\n const shutdown = async () => {\n process.stderr.write('\\n[cordon] shutting down...\\n');\n try {\n await gateway.stop();\n process.exit(0);\n } catch (err) {\n process.stderr.write(`\\x1b[31merror\\x1b[0m: shutdown failed: ${String(err)}\\n`);\n process.exit(1);\n }\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n try {\n await gateway.start();\n } catch (err) {\n process.stderr.write(`\\x1b[31merror\\x1b[0m: gateway failed: ${String(err)}\\n`);\n process.exit(1);\n }\n}\n","import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { createJiti } from 'jiti';\nimport type { CordonConfig, ResolvedConfig } from '@getcordon/policy';\n\nconst SEARCH_PATHS = [\n () => join(process.cwd(), 'cordon.config.ts'),\n () => join(process.cwd(), 'cordon.config.js'),\n () => join(homedir(), '.cordon', 'config.ts'),\n () => join(homedir(), '.cordon', 'config.js'),\n];\n\nexport async function findConfigPath(explicit?: string): Promise<string> {\n if (explicit) {\n if (!existsSync(explicit)) {\n throw new Error(`Config file not found: ${explicit}`);\n }\n return explicit;\n }\n\n for (const candidate of SEARCH_PATHS) {\n const p = candidate();\n if (existsSync(p)) return p;\n }\n\n throw new Error(\n 'No cordon config found. Run `cordon init` to create one, or pass --config <path>.',\n );\n}\n\nexport async function loadConfig(configPath: string): Promise<ResolvedConfig> {\n // jiti handles TypeScript config files without requiring a separate compile step\n const jiti = createJiti(import.meta.url);\n const mod = await jiti.import(configPath);\n\n const raw = (mod as { default?: CordonConfig }).default ?? (mod as CordonConfig);\n\n if (!raw || typeof raw !== 'object' || !Array.isArray(raw.servers)) {\n throw new Error(\n `Invalid config at ${configPath}: expected an object with a 'servers' array. ` +\n `Make sure you are using \\`export default defineConfig({...})\\`.`,\n );\n }\n\n return applyDefaults(raw as CordonConfig);\n}\n\nfunction applyDefaults(config: CordonConfig): ResolvedConfig {\n return {\n ...config,\n audit: config.audit ?? { enabled: true, output: 'stdout' },\n approvals: config.approvals ?? { channel: 'terminal' },\n };\n}\n\nexport function emptyConfig(): ResolvedConfig {\n return applyDefaults({ servers: [] });\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync, chmodSync, unlinkSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\ninterface CliState {\n welcomed?: boolean;\n}\n\nexport interface CliAuth {\n endpoint: string;\n apiKey: string;\n userLogin?: string;\n loggedInAt: string;\n}\n\nfunction cordonDir(): string {\n return join(homedir(), '.cordon');\n}\n\nfunction statePath(): string {\n return join(cordonDir(), 'state.json');\n}\n\nfunction authPath(): string {\n return join(cordonDir(), 'auth.json');\n}\n\nfunction ensureCordonDir(): void {\n const dir = cordonDir();\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n}\n\nexport function getState(): CliState {\n const path = statePath();\n if (!existsSync(path)) return {};\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as CliState;\n } catch {\n return {};\n }\n}\n\nexport function setState(patch: CliState): void {\n try {\n ensureCordonDir();\n const merged = { ...getState(), ...patch };\n writeFileSync(statePath(), JSON.stringify(merged, null, 2), 'utf8');\n } catch {\n // Non-fatal: missing state just means we re-show the banner next time.\n }\n}\n\nexport function getAuth(): CliAuth | null {\n const path = authPath();\n if (!existsSync(path)) return null;\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as CliAuth;\n } catch {\n return null;\n }\n}\n\nexport function setAuth(auth: CliAuth): void {\n ensureCordonDir();\n const path = authPath();\n writeFileSync(path, JSON.stringify(auth, null, 2), 'utf8');\n if (process.platform !== 'win32') {\n try { chmodSync(path, 0o600); } catch { /* best-effort */ }\n }\n}\n\nexport function clearAuth(): boolean {\n const path = authPath();\n if (!existsSync(path)) return false;\n try {\n unlinkSync(path);\n return true;\n } catch {\n return false;\n }\n}\n","import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { spawnSync } from 'node:child_process';\nimport { createInterface } from 'node:readline';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { setState, getAuth } from '../cli-state.js';\nimport { loginCommand } from './login.js';\n\nconst DASHBOARD_URL = 'https://app.getcordon.com/dashboard/';\n\nfunction ensureCordonSdkInstalled(cwd: string): void {\n // Config imports from '@getcordon/policy', and jiti resolves it from the config\n // file's directory. Without a local install, `cordon start` dies with\n // \"Cannot find module '@getcordon/policy'\" even when the CLI is global.\n if (existsSync(join(cwd, 'node_modules', '@getcordon/policy', 'package.json'))) {\n return;\n }\n\n if (!existsSync(join(cwd, 'package.json'))) {\n writeFileSync(\n join(cwd, 'package.json'),\n JSON.stringify({ name: 'cordon-config', version: '0.0.0', private: true }, null, 2) + '\\n',\n 'utf8',\n );\n process.stderr.write(`\\x1b[32m✓\\x1b[0m created package.json\\n`);\n }\n\n process.stderr.write(`[cordon] installing @getcordon/policy...\\n`);\n const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';\n const result = spawnSync(npmCmd, ['install', '@getcordon/policy'], {\n cwd,\n stdio: ['ignore', 'inherit', 'inherit'],\n });\n\n if (result.status === 0) {\n process.stderr.write(`\\x1b[32m✓\\x1b[0m installed @getcordon/policy\\n`);\n } else {\n process.stderr.write(\n `\\x1b[33mwarn\\x1b[0m: could not auto-install @getcordon/policy. ` +\n `Run 'npm install @getcordon/policy' in this directory before 'cordon start'.\\n`,\n );\n }\n}\n\nasync function promptYesNo(question: string): Promise<boolean> {\n // Non-interactive (CI, piped stdin): skip rather than surprise the operator\n // with a browser tab and a 5-minute callback timeout.\n if (!process.stdin.isTTY) return false;\n const rl = createInterface({ input: process.stdin, output: process.stderr });\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n const trimmed = answer.trim().toLowerCase();\n // Empty = default Yes\n resolve(trimmed === '' || trimmed === 'y' || trimmed === 'yes');\n });\n });\n}\n\ninterface ClaudeDesktopConfig {\n mcpServers?: Record<string, { command: string; args?: string[]; env?: Record<string, string> }>;\n}\n\nfunction getClaudeConfigPath(): string | null {\n const candidates: string[] = [];\n\n if (process.platform === 'darwin') {\n candidates.push(\n join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'),\n );\n } else if (process.platform === 'win32') {\n const appData = process.env['APPDATA'] ?? join(homedir(), 'AppData', 'Roaming');\n candidates.push(join(appData, 'Claude', 'claude_desktop_config.json'));\n } else {\n candidates.push(join(homedir(), '.config', 'Claude', 'claude_desktop_config.json'));\n }\n\n return candidates.find((p) => existsSync(p)) ?? null;\n}\n\nexport async function initCommand(): Promise<void> {\n const outputPath = join(process.cwd(), 'cordon.config.ts');\n\n if (existsSync(outputPath)) {\n process.stderr.write(\n `\\x1b[33mwarn\\x1b[0m: cordon.config.ts already exists — not overwriting.\\n`,\n );\n process.exit(1);\n }\n\n // Try to discover existing MCP servers from Claude Desktop config\n const claudePath = getClaudeConfigPath();\n let claudeConfig: ClaudeDesktopConfig = {};\n\n if (claudePath) {\n process.stderr.write(`[cordon] found Claude Desktop config at ${claudePath}\\n`);\n try {\n claudeConfig = JSON.parse(readFileSync(claudePath, 'utf8')) as ClaudeDesktopConfig;\n } catch {\n process.stderr.write(`\\x1b[33mwarn\\x1b[0m: could not parse Claude Desktop config\\n`);\n }\n } else {\n process.stderr.write(\n `[cordon] no Claude Desktop config found — generating a blank config\\n`,\n );\n }\n\n const servers = Object.entries(claudeConfig.mcpServers ?? {});\n\n // Generate cordon.config.ts\n const serverBlocks = servers.length > 0\n ? servers\n .map(([name, cfg]) => {\n const argsStr = cfg.args?.length\n ? `, args: ${JSON.stringify(cfg.args)}`\n : '';\n const envStr =\n cfg.env && Object.keys(cfg.env).length\n ? `, env: ${JSON.stringify(cfg.env)}`\n : '';\n return ` {\n name: ${JSON.stringify(name)},\n transport: 'stdio',\n command: ${JSON.stringify(cfg.command)}${argsStr}${envStr},\n policy: 'allow',\n // tools: {\n // execute: 'approve',\n // delete: 'block',\n // },\n },`;\n })\n .join('\\n')\n : ` // {\n // name: 'my-server',\n // transport: 'stdio',\n // command: 'npx',\n // args: ['-y', '@my-org/my-mcp-server'],\n // policy: 'allow',\n // },`;\n\n let auth = getAuth();\n let promptShown = false;\n if (!auth && process.stdin.isTTY) {\n promptShown = true;\n process.stderr.write(\n `\\n\\x1b[36mCordon works locally out of the box.\\x1b[0m ` +\n `Sign in to enable hosted audit logs + Slack approvals (free).\\n`,\n );\n const wantsLogin = await promptYesNo('Sign in via browser now? [Y/n] ');\n if (wantsLogin) {\n try {\n await loginCommand();\n auth = getAuth();\n } catch (err) {\n const msg = err instanceof Error ? err.message : 'unknown error';\n process.stderr.write(\n `\\x1b[33mwarn\\x1b[0m: login didn't complete (${msg}). ` +\n `Continuing with local stdout audit. Run \\x1b[36mcordon login\\x1b[0m later.\\n`,\n );\n }\n }\n }\n\n const auditBlock = auth\n ? `audit: {\n enabled: true,\n output: 'hosted',\n // endpoint + apiKey are auto-loaded from ~/.cordon/auth.json (cordon login)\n },`\n : `audit: {\n enabled: true,\n // 'auto' streams to stdout until you run \\`cordon login\\`, then auto-switches to hosted.\n output: 'auto',\n },`;\n\n const approvalsBlock = auth\n ? `approvals: {\n // Human-in-the-loop over Slack. Connect your workspace once via\n // \"Add to Slack\" in the dashboard — endpoint + apiKey are auto-loaded.\n channel: 'slack',\n // timeoutMs: 60_000,\n },`\n : `approvals: {\n channel: 'terminal',\n // timeoutMs: 60_000,\n },`;\n\n const content = `import { defineConfig } from '@getcordon/policy';\n\nexport default defineConfig({\n servers: [\n${serverBlocks}\n ],\n\n ${auditBlock}\n\n ${approvalsBlock}\n});\n`;\n\n writeFileSync(outputPath, content, 'utf8');\n process.stderr.write(`\\x1b[32m✓\\x1b[0m wrote cordon.config.ts\\n`);\n\n // The config imports from '@getcordon/policy'. Install it locally so jiti can\n // resolve it when `cordon start` runs.\n ensureCordonSdkInstalled(process.cwd());\n\n // Patch Claude Desktop config to route through cordon.\n //\n // We bind the spawn to the *exact* node + cordon.js paths that ran this\n // init. Earlier versions wrote `npx.cmd` (or `npx`), which relies on\n // PATH lookup at spawn time. Claude Desktop's spawned subprocesses\n // inherit a stripped PATH on Windows that often lacks the user's\n // nvm/Node bin dir, so `npx.cmd` fails with \"is not recognized as an\n // internal or external command\". Using full paths skips PATH and the\n // cmd.exe wrapper entirely.\n if (claudePath && servers.length > 0) {\n const cordonConfigPath = outputPath.replace(/\\\\/g, '/');\n const nodePath = process.execPath.replace(/\\\\/g, '/');\n const cordonScriptPath = (process.argv[1] ?? '').replace(/\\\\/g, '/');\n\n if (!cordonScriptPath) {\n process.stderr.write(\n `\\x1b[33mwarn\\x1b[0m: could not detect cordon CLI script path. ` +\n `Skipping Claude Desktop patch.\\n` +\n `Manually add this to your claude_desktop_config.json mcpServers:\\n` +\n ` \"cordon\": { \"command\": \"${nodePath}\", \"args\": [\"<path-to-cordon.js>\", \"start\", \"--config\", \"${cordonConfigPath}\"] }\\n`,\n );\n return;\n }\n\n const newClaudeConfig: ClaudeDesktopConfig = {\n ...claudeConfig,\n mcpServers: {\n cordon: {\n command: nodePath,\n args: [cordonScriptPath, 'start', '--config', cordonConfigPath],\n },\n },\n };\n\n // Backup the original — only if no backup exists yet, so re-running init\n // never overwrites the user's true pre-cordon config.\n const backupPath = `${claudePath}.cordon-backup`;\n if (existsSync(backupPath)) {\n process.stderr.write(\n `[cordon] existing backup at ${backupPath} preserved (won't overwrite)\\n`,\n );\n } else {\n writeFileSync(backupPath, readFileSync(claudePath, 'utf8'), 'utf8');\n process.stderr.write(`\\x1b[32m✓\\x1b[0m backed up Claude Desktop config to ${backupPath}\\n`);\n }\n\n writeFileSync(claudePath, JSON.stringify(newClaudeConfig, null, 2), 'utf8');\n process.stderr.write(`\\x1b[32m✓\\x1b[0m patched Claude Desktop config\\n`);\n process.stderr.write(\n `\\n\\x1b[36mRestart Claude Desktop to activate Cordon.\\x1b[0m\\n`,\n );\n } else if (!claudePath) {\n const nodePath = process.execPath.replace(/\\\\/g, '/');\n const cordonScriptPath = (process.argv[1] ?? '<path-to-cordon.js>').replace(/\\\\/g, '/');\n process.stderr.write(\n `\\n\\x1b[33mwarn\\x1b[0m: Claude Desktop config not found on this system.\\n` +\n `Edit cordon.config.ts, then manually add Cordon to your MCP client config:\\n\\n` +\n ` \"mcpServers\": {\\n` +\n ` \"cordon\": {\\n` +\n ` \"command\": \"${nodePath}\",\\n` +\n ` \"args\": [\"${cordonScriptPath}\", \"start\", \"--config\", \"${outputPath.replace(/\\\\/g, '/')}\"]\\n` +\n ` }\\n` +\n ` }\\n`,\n );\n } else {\n // claudePath found but no existing servers — config written, no patching needed\n process.stderr.write(\n `\\nEdit cordon.config.ts to add your MCP servers, then run \\x1b[36mnpx cordon start\\x1b[0m.\\n`,\n );\n }\n\n if (auth) {\n process.stderr.write(\n `\\n\\x1b[32m✓\\x1b[0m audit logs will stream to your Cordon account (${auth.endpoint})\\n`,\n );\n } else if (promptShown) {\n process.stderr.write(\n `\\n[cordon] running in local mode. Run \\x1b[36mcordon login\\x1b[0m later to enable hosted audit.\\n`,\n );\n } else {\n process.stderr.write(\n `\\n\\x1b[36mWant centralized audit logs + Slack approvals?\\x1b[0m\\n` +\n `Run \\x1b[36mcordon login\\x1b[0m to register a free account, ` +\n `or sign up at ${DASHBOARD_URL}?utm_source=cli_init\\n`,\n );\n }\n setState({ welcomed: true });\n}\n","import { createServer } from 'node:http';\nimport { spawn } from 'node:child_process';\nimport { randomBytes } from 'node:crypto';\nimport { setAuth } from '../cli-state.js';\n\nconst DEFAULT_ENDPOINT = 'https://app.getcordon.com';\nconst PORT_RANGE = [53247, 53249, 53251] as const;\nconst TIMEOUT_MS = 5 * 60 * 1000;\n\ninterface LoginOptions {\n endpoint?: string;\n}\n\nfunction openBrowser(url: string): void {\n const cmd = process.platform === 'darwin' ? 'open'\n : process.platform === 'win32' ? 'cmd'\n : 'xdg-open';\n // On Windows, `cmd /c start \"\" <url>` runs through cmd.exe, which treats\n // `&` and `^` as shell metacharacters. Without escaping them, query strings\n // like `?callback=…&state=…` get truncated at the first `&`, breaking the\n // OAuth state hand-off. Replace `^` first so we don't double-escape new ^&.\n const safeUrl = process.platform === 'win32'\n ? url.replace(/\\^/g, '^^').replace(/&/g, '^&')\n : url;\n const args = process.platform === 'win32' ? ['/c', 'start', '\"\"', safeUrl] : [safeUrl];\n try {\n spawn(cmd, args, { detached: true, stdio: 'ignore' }).unref();\n } catch { /* swallow — caller has already printed the URL for manual paste */ }\n}\n\ninterface CallbackResult {\n token: string;\n state: string;\n signup: string;\n}\n\nfunction renderCallbackPage(kind: 'success' | 'error', heading: string, body: string): string {\n const accent = kind === 'success' ? '#14b8a6' : '#f87171';\n const accentBg = kind === 'success' ? 'rgba(20, 184, 166, 0.15)' : 'rgba(248, 113, 113, 0.15)';\n const mark = kind === 'success' ? '✓' : '✗';\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${kind === 'success' ? 'Logged in' : 'Login failed'} — Cordon for MCP</title>\n <link rel=\"icon\" type=\"image/svg+xml\" href=\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90' fill='%2314b8a6'>◈</text></svg>\">\n <style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n body {\n background: #0d0d0d; color: #e8e8e8;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 1rem;\n }\n .card {\n background: #161616; border: 1px solid #2a2a2a; border-radius: 16px;\n padding: 48px; max-width: 420px; width: 100%; text-align: center;\n }\n .brand { display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 1.75rem; }\n .icon { font-size: 1.4rem; color: #14b8a6; }\n .name { font-weight: 600; font-size: 16px; letter-spacing: -0.3px; white-space: nowrap; }\n .mark {\n width: 56px; height: 56px; border-radius: 50%;\n background: ${accentBg}; color: ${accent};\n display: flex; align-items: center; justify-content: center;\n margin: 0 auto 1.25rem; font-size: 28px; font-weight: 700;\n }\n h1 { font-size: 1.4rem; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 0.6rem; }\n p { color: #888; font-size: 0.95rem; line-height: 1.5; }\n p + p { margin-top: 0.5rem; }\n code {\n background: rgba(255, 255, 255, 0.06); color: #e8e8e8;\n padding: 1px 6px; border-radius: 3px; font-size: 0.85em;\n font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;\n }\n .btn {\n display: inline-block;\n background: #14b8a6; color: #000;\n font-weight: 600; padding: 0.65rem 1.25rem;\n border-radius: 6px; text-decoration: none;\n margin-top: 1.25rem; transition: opacity 0.15s;\n }\n .btn:hover { opacity: 0.85; }\n .hint {\n margin-top: 1.75rem; padding-top: 1.5rem;\n border-top: 1px solid #2a2a2a; font-size: 0.85rem; color: #666;\n }\n </style>\n</head>\n<body>\n <div class=\"card\">\n <div class=\"brand\">\n <span class=\"icon\">◈</span>\n <span class=\"name\">Cordon for MCP</span>\n </div>\n <div class=\"mark\">${mark}</div>\n <h1>${heading}</h1>\n ${body}\n </div>\n</body>\n</html>`;\n}\n\nasync function listenForCallback(port: number, expectedState: string, dashboardUrl: string): Promise<CallbackResult> {\n return new Promise((resolve, reject) => {\n const server = createServer((req, res) => {\n const url = new URL(req.url ?? '/', `http://localhost:${port}`);\n if (url.pathname !== '/cb') {\n res.writeHead(404).end('not found');\n return;\n }\n const token = url.searchParams.get('token') ?? '';\n const state = url.searchParams.get('state') ?? '';\n const signup = url.searchParams.get('signup') ?? 'false';\n\n if (state !== expectedState || !token) {\n const body =\n `<p>The login link didn't carry a valid token, or the security state didn't match.</p>` +\n `<p class=\"hint\">Run <code>cordon login</code> again from your terminal. You can close this tab.</p>`;\n res.writeHead(400, { 'Content-Type': 'text/html' })\n .end(renderCallbackPage('error', \"Login didn't complete\", body));\n server.close();\n reject(new Error('Callback state mismatch'));\n return;\n }\n\n const body =\n `<p>Your API key is saved. Return to your terminal to continue,` +\n ` or jump straight to your dashboard.</p>` +\n `<a href=\"${dashboardUrl}\" class=\"btn\">Open dashboard →</a>` +\n `<p class=\"hint\">You can close this tab.</p>`;\n res.writeHead(200, { 'Content-Type': 'text/html' })\n .end(renderCallbackPage('success', \"You're logged in\", body));\n server.close();\n resolve({ token, state, signup });\n });\n\n server.listen(port, '127.0.0.1');\n server.on('error', (err) => reject(err));\n\n setTimeout(() => {\n server.close();\n reject(new Error('Login timed out after 5 minutes'));\n }, TIMEOUT_MS).unref();\n });\n}\n\nexport async function loginCommand(options: LoginOptions = {}): Promise<void> {\n const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/$/, '');\n const state = randomBytes(16).toString('hex');\n\n let port: number | null = null;\n let result: CallbackResult | null = null;\n let lastErr: unknown = null;\n\n for (const candidate of PORT_RANGE) {\n try {\n const callback = `http://localhost:${candidate}/cb`;\n const authUrl =\n `${endpoint}/auth/cli/start?callback=${encodeURIComponent(callback)}&state=${state}`;\n\n process.stderr.write(\n `\\n[cordon] opening browser to log in...\\n` +\n `[cordon] if it doesn't open, visit:\\n ${authUrl}\\n\\n`,\n );\n openBrowser(authUrl);\n\n const pending = listenForCallback(candidate, state, `${endpoint}/dashboard/`);\n port = candidate;\n result = await pending;\n break;\n } catch (err) {\n lastErr = err;\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'EADDRINUSE') continue;\n throw err;\n }\n }\n\n if (!result) {\n throw lastErr instanceof Error ? lastErr : new Error('Could not bind a local callback port');\n }\n\n setAuth({\n endpoint,\n apiKey: result.token,\n loggedInAt: new Date().toISOString(),\n });\n\n process.stderr.write(`\\x1b[32m✓\\x1b[0m logged in. API key saved to ~/.cordon/auth.json\\n`);\n if (result.signup === 'true') {\n process.stderr.write(\n `Welcome to Cordon for MCP.\\n` +\n `Dashboard: \\x1b[36m${endpoint}/dashboard/\\x1b[0m\\n` +\n `Run \\x1b[36mcordon init\\x1b[0m next to wire up your MCP servers.\\n`,\n );\n }\n // Suppress unused var warning when port is set but not otherwise used\n void port;\n}\n","import { clearAuth } from '../cli-state.js';\n\nexport function logoutCommand(): void {\n if (clearAuth()) {\n process.stderr.write(`\\x1b[32m✓\\x1b[0m logged out. ~/.cordon/auth.json removed\\n`);\n } else {\n process.stderr.write(`[cordon] not logged in\\n`);\n }\n}\n","import { createInterface } from 'node:readline';\nimport { UpstreamManager, AuditLogger } from '@getcordon/core';\nimport { getAuth } from '../cli-state.js';\nimport { findConfigPath, loadConfig } from '../config-loader.js';\n\ninterface ReplayOptions {\n config?: string;\n endpoint?: string;\n yes?: boolean;\n}\n\n// The full approval record returned by GET /approvals/:callId.\ninterface ApprovalRecord {\n status: string;\n approvedBy: string | null;\n serverName: string | null;\n toolName: string | null;\n args: unknown;\n resolvedLate: boolean;\n expiresAt: string | null;\n}\n\nfunction fail(msg: string): never {\n process.stderr.write(`\\x1b[31merror\\x1b[0m: ${msg}\\n`);\n process.exit(1);\n}\n\nasync function confirm(question: string): Promise<boolean> {\n if (!process.stdin.isTTY) return false; // non-interactive without --yes = decline\n const rl = createInterface({ input: process.stdin, output: process.stderr });\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n const t = answer.trim().toLowerCase();\n resolve(t === 'y' || t === 'yes');\n });\n });\n}\n\n/**\n * `cordon replay <callId>` — re-execute a tool call that was approved AFTER it\n * timed out (durable-context Phase 2). Cordon can recover the tool *call*; it\n * does not resume the agent's session. Guardrailed to approved-late records so a\n * call that already ran can't be double-executed.\n */\nexport async function replayCommand(callId: string, options: ReplayOptions): Promise<void> {\n const auth = getAuth();\n if (!auth) fail('not logged in. Run `cordon login` first.');\n const endpoint = (options.endpoint ?? auth.endpoint).replace(/\\/$/, '');\n\n // 1. Fetch the approval record from cordon-server.\n const res = await fetch(`${endpoint}/approvals/${encodeURIComponent(callId)}`, {\n headers: { 'X-Cordon-Key': auth.apiKey },\n });\n if (res.status === 404) fail(`no approval found for callId '${callId}' on this account.`);\n if (!res.ok) fail(`failed to fetch approval: HTTP ${res.status}`);\n const record = (await res.json()) as ApprovalRecord;\n\n // 2. Guardrail: only a call approved AFTER timing out is safe to replay — it\n // never actually ran. A normally-approved/denied/pending call must not be\n // re-executed (double-run / not authorized).\n if (!(record.status === 'approved' && record.resolvedLate)) {\n fail(\n `call '${callId}' is not replayable (status=${record.status}, resolvedLate=${record.resolvedLate}). ` +\n `Only a call approved AFTER it timed out can be replayed — replaying anything else would double-execute or run an unauthorized call.`,\n );\n }\n if (!record.serverName || !record.toolName) {\n fail(`approval '${callId}' is missing server/tool info; cannot replay.`);\n }\n\n // 3. Load config + find the upstream server the call belongs to.\n const config = await loadConfig(await findConfigPath(options.config));\n const server = config.servers.find((s) => s.name === record.serverName);\n if (!server) {\n fail(`server '${record.serverName}' is not in your cordon.config.ts. Add it (or pass --config) and retry.`);\n }\n\n // 4. Confirm — replaying re-runs the tool, which can have side effects.\n process.stderr.write(\n `\\nReplay this late-approved call?\\n` +\n ` server : ${record.serverName}\\n` +\n ` tool : ${record.toolName}\\n` +\n ` args : ${JSON.stringify(record.args)}\\n` +\n ` approved by ${record.approvedBy ?? 'unknown'} (after timeout)\\n\\n` +\n `This re-executes the tool. Non-idempotent tools have real side effects.\\n`,\n );\n if (!options.yes) {\n const ok = await confirm('Proceed? [y/N] ');\n if (!ok) {\n process.stderr.write('[cordon] replay cancelled.\\n');\n return;\n }\n }\n\n // 5. Execute against just this upstream (no gateway/transport, no policy loop —\n // this is a human-authorized recovery, so it bypasses re-approval).\n const mgr = new UpstreamManager([server]);\n const started = Date.now();\n let isError = false;\n try {\n await mgr.connect();\n const result = await mgr.callTool(record.serverName, record.toolName, record.args);\n isError = Boolean((result as { isError?: boolean }).isError);\n process.stderr.write(\n `[cordon] replay ${isError ? 'returned an error' : 'succeeded'} in ${Date.now() - started}ms\\n`,\n );\n } catch (e) {\n isError = true;\n process.stderr.write(`[cordon] replay failed: ${String(e)}\\n`);\n } finally {\n await mgr.disconnect().catch(() => {});\n }\n\n // 6. Log the outcome to the hosted audit — same stream/dashboard as the gateway.\n // HostedAuditOutput batches, so close() is required or the event is lost.\n const audit = new AuditLogger({ enabled: true, output: 'hosted', endpoint, apiKey: auth.apiKey });\n audit.log({\n event: isError ? 'tool_call_errored' : 'tool_call_completed',\n callId,\n serverName: record.serverName,\n toolName: record.toolName,\n args: record.args,\n isError,\n durationMs: Date.now() - started,\n reason: 'replay of late-approved call',\n });\n audit.close();\n\n // 7. On success, dismiss the record so it leaves the recoverable list (the\n // replay now lives in the audit stream). On error, leave it to retry.\n if (!isError) {\n await fetch(`${endpoint}/approvals/${encodeURIComponent(callId)}`, {\n method: 'DELETE',\n headers: { 'X-Cordon-Key': auth.apiKey },\n }).catch(() => {});\n process.stderr.write('[cordon] logged to the dashboard and cleared from recoverable.\\n');\n } else {\n process.stderr.write('[cordon] left in the recoverable list so you can retry.\\n');\n }\n}\n"],"mappings":"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","names":["existsSync","join","homedir","readFileSync","writeFileSync","existsSync","join","homedir","body","DASHBOARD_URL","existsSync","join","writeFileSync","homedir","readFileSync","createInterface","createInterface"]}
{
"name": "@getcordon/cli",
"version": "0.4.1",
"version": "0.4.2",
"mcpName": "io.github.marras0914/cordon",

@@ -5,0 +5,0 @@ "description": "Cordon for MCP — security gateway for MCP tool calls. Firewall, auditor, and human-in-the-loop approvals over any stdio MCP server.",