cordon-cli
Advanced tools
+193
-16
@@ -58,8 +58,18 @@ #!/usr/bin/env node | ||
| // src/cli-state.ts | ||
| import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs"; | ||
| import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync, chmodSync, unlinkSync } from "fs"; | ||
| import { join as join2 } from "path"; | ||
| import { homedir as homedir2 } from "os"; | ||
| function cordonDir() { | ||
| return join2(homedir2(), ".cordon"); | ||
| } | ||
| function statePath() { | ||
| return join2(homedir2(), ".cordon", "state.json"); | ||
| return join2(cordonDir(), "state.json"); | ||
| } | ||
| function authPath() { | ||
| return join2(cordonDir(), "auth.json"); | ||
| } | ||
| function ensureCordonDir() { | ||
| const dir = cordonDir(); | ||
| if (!existsSync2(dir)) mkdirSync(dir, { recursive: true }); | ||
| } | ||
| function getState() { | ||
@@ -75,20 +85,64 @@ const path = statePath(); | ||
| function setState(patch) { | ||
| const path = statePath(); | ||
| const dir = join2(homedir2(), ".cordon"); | ||
| try { | ||
| if (!existsSync2(dir)) mkdirSync(dir, { recursive: true }); | ||
| ensureCordonDir(); | ||
| const merged = { ...getState(), ...patch }; | ||
| writeFileSync(path, JSON.stringify(merged, null, 2), "utf8"); | ||
| writeFileSync(statePath(), JSON.stringify(merged, null, 2), "utf8"); | ||
| } catch { | ||
| } | ||
| } | ||
| function getAuth() { | ||
| const path = authPath(); | ||
| if (!existsSync2(path)) return null; | ||
| try { | ||
| return JSON.parse(readFileSync(path, "utf8")); | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
| function setAuth(auth) { | ||
| ensureCordonDir(); | ||
| const path = authPath(); | ||
| writeFileSync(path, JSON.stringify(auth, null, 2), "utf8"); | ||
| if (process.platform !== "win32") { | ||
| try { | ||
| chmodSync(path, 384); | ||
| } catch { | ||
| } | ||
| } | ||
| } | ||
| function clearAuth() { | ||
| const path = authPath(); | ||
| if (!existsSync2(path)) return false; | ||
| try { | ||
| unlinkSync(path); | ||
| return true; | ||
| } catch { | ||
| return false; | ||
| } | ||
| } | ||
| // src/commands/start.ts | ||
| var DASHBOARD_URL = "https://cordon-server-production.up.railway.app/dashboard/"; | ||
| function applyAuthDefaults(config) { | ||
| const auth = getAuth(); | ||
| if (!auth) return config; | ||
| const audit = config.audit; | ||
| const usesHosted = audit && (audit.output === "hosted" || Array.isArray(audit.output) && audit.output.includes("hosted")); | ||
| if (!usesHosted) return config; | ||
| if (audit.endpoint && audit.apiKey) return config; | ||
| return { | ||
| ...config, | ||
| audit: { | ||
| ...audit, | ||
| endpoint: audit.endpoint ?? auth.endpoint, | ||
| apiKey: audit.apiKey ?? auth.apiKey | ||
| } | ||
| }; | ||
| } | ||
| async function startCommand(options) { | ||
| if (!getState().welcomed) { | ||
| if (!getState().welcomed && !getAuth()) { | ||
| process.stderr.write( | ||
| ` | ||
| \x1B[36m[cordon] Want centralized audit logs + Slack approvals?\x1B[0m | ||
| [cordon] Register at ${DASHBOARD_URL}?utm_source=cli_start | ||
| [cordon] Run \`cordon login\` or register at ${DASHBOARD_URL}?utm_source=cli_start | ||
@@ -124,2 +178,3 @@ ` | ||
| } | ||
| config = applyAuthDefaults(config); | ||
| const gateway = new CordonGateway(config); | ||
@@ -245,2 +300,11 @@ const shutdown = async () => { | ||
| // },`; | ||
| const auth = getAuth(); | ||
| const auditBlock = auth ? `audit: { | ||
| enabled: true, | ||
| output: 'hosted', | ||
| // endpoint + apiKey are auto-loaded from ~/.cordon/auth.json (cordon login) | ||
| },` : `audit: { | ||
| enabled: true, | ||
| output: 'stdout', | ||
| },`; | ||
| const content = `import { defineConfig } from 'cordon-sdk'; | ||
@@ -253,6 +317,3 @@ | ||
| audit: { | ||
| enabled: true, | ||
| output: 'stdout', | ||
| }, | ||
| ${auditBlock} | ||
@@ -314,11 +375,121 @@ approvals: { | ||
| } | ||
| process.stderr.write( | ||
| ` | ||
| if (auth) { | ||
| process.stderr.write( | ||
| ` | ||
| \x1B[32m\u2713\x1B[0m audit logs will stream to your Cordon account (${auth.endpoint}) | ||
| ` | ||
| ); | ||
| } else { | ||
| process.stderr.write( | ||
| ` | ||
| \x1B[36mWant centralized audit logs + Slack approvals?\x1B[0m | ||
| Register a free account at ${DASHBOARD_URL2}?utm_source=cli_init | ||
| Run \x1B[36mcordon login\x1B[0m to register a free account, or sign up at ${DASHBOARD_URL2}?utm_source=cli_init | ||
| ` | ||
| ); | ||
| ); | ||
| } | ||
| setState({ welcomed: true }); | ||
| } | ||
| // src/commands/login.ts | ||
| import { createServer } from "http"; | ||
| import { spawn } from "child_process"; | ||
| import { randomBytes } from "crypto"; | ||
| var DEFAULT_ENDPOINT = "https://cordon-server-production.up.railway.app"; | ||
| var PORT_RANGE = [53247, 53249, 53251]; | ||
| var TIMEOUT_MS = 5 * 60 * 1e3; | ||
| function openBrowser(url) { | ||
| const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open"; | ||
| const args = process.platform === "win32" ? ["/c", "start", '""', url] : [url]; | ||
| try { | ||
| spawn(cmd, args, { detached: true, stdio: "ignore" }).unref(); | ||
| } catch { | ||
| } | ||
| } | ||
| async function listenForCallback(port, expectedState) { | ||
| return new Promise((resolve, reject) => { | ||
| const server = createServer((req, res) => { | ||
| const url = new URL(req.url ?? "/", `http://localhost:${port}`); | ||
| if (url.pathname !== "/cb") { | ||
| res.writeHead(404).end("not found"); | ||
| return; | ||
| } | ||
| const token = url.searchParams.get("token") ?? ""; | ||
| const state = url.searchParams.get("state") ?? ""; | ||
| const signup = url.searchParams.get("signup") ?? "false"; | ||
| if (state !== expectedState || !token) { | ||
| res.writeHead(400, { "Content-Type": "text/html" }).end("<html><body><h2>Login failed</h2><p>State mismatch or missing token. You can close this tab.</p></body></html>"); | ||
| server.close(); | ||
| reject(new Error("Callback state mismatch")); | ||
| return; | ||
| } | ||
| res.writeHead(200, { "Content-Type": "text/html" }).end('<html><body style="font-family:system-ui;padding:40px;"><h2>Logged in to Cordon</h2><p>You can close this tab and return to your terminal.</p></body></html>'); | ||
| server.close(); | ||
| resolve({ token, state, signup }); | ||
| }); | ||
| server.listen(port, "127.0.0.1"); | ||
| server.on("error", (err) => reject(err)); | ||
| setTimeout(() => { | ||
| server.close(); | ||
| reject(new Error("Login timed out after 5 minutes")); | ||
| }, TIMEOUT_MS).unref(); | ||
| }); | ||
| } | ||
| async function loginCommand(options = {}) { | ||
| const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\/$/, ""); | ||
| const state = randomBytes(16).toString("hex"); | ||
| let port = null; | ||
| let result = null; | ||
| let lastErr = null; | ||
| for (const candidate of PORT_RANGE) { | ||
| try { | ||
| const callback = `http://localhost:${candidate}/cb`; | ||
| const authUrl = `${endpoint}/auth/cli/start?callback=${encodeURIComponent(callback)}&state=${state}`; | ||
| process.stderr.write( | ||
| ` | ||
| [cordon] opening browser to log in... | ||
| [cordon] if it doesn't open, visit: | ||
| ${authUrl} | ||
| ` | ||
| ); | ||
| openBrowser(authUrl); | ||
| const pending = listenForCallback(candidate, state); | ||
| port = candidate; | ||
| result = await pending; | ||
| break; | ||
| } catch (err) { | ||
| lastErr = err; | ||
| const code = err.code; | ||
| if (code === "EADDRINUSE") continue; | ||
| throw err; | ||
| } | ||
| } | ||
| if (!result) { | ||
| throw lastErr instanceof Error ? lastErr : new Error("Could not bind a local callback port"); | ||
| } | ||
| setAuth({ | ||
| endpoint, | ||
| apiKey: result.token, | ||
| loggedInAt: (/* @__PURE__ */ new Date()).toISOString() | ||
| }); | ||
| process.stderr.write(`\x1B[32m\u2713\x1B[0m logged in. API key saved to ~/.cordon/auth.json | ||
| `); | ||
| if (result.signup === "true") { | ||
| process.stderr.write(`Welcome to Cordon. Run \x1B[36mcordon init\x1B[0m next to wire up your MCP servers. | ||
| `); | ||
| } | ||
| void port; | ||
| } | ||
| // src/commands/logout.ts | ||
| function logoutCommand() { | ||
| if (clearAuth()) { | ||
| process.stderr.write(`\x1B[32m\u2713\x1B[0m logged out. ~/.cordon/auth.json removed | ||
| `); | ||
| } else { | ||
| process.stderr.write(`[cordon] not logged in | ||
| `); | ||
| } | ||
| } | ||
| // src/bin/cordon.ts | ||
@@ -329,3 +500,9 @@ var program = new Command(); | ||
| program.command("init").description("Generate cordon.config.ts and patch Claude Desktop config").action(initCommand); | ||
| program.command("login").description("Log in to Cordon (browser OAuth) and save an API key locally").option("--endpoint <url>", "Cordon server endpoint (defaults to the hosted instance)").action((opts) => loginCommand(opts).catch((err) => { | ||
| process.stderr.write(`\x1B[31merror\x1B[0m: login failed: ${String(err)} | ||
| `); | ||
| process.exit(1); | ||
| })); | ||
| program.command("logout").description("Remove the local Cordon credentials").action(logoutCommand); | ||
| program.parse(); | ||
| //# sourceMappingURL=cordon.js.map |
@@ -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"],"sourcesContent":["import { Command } from 'commander';\nimport { startCommand } from '../commands/start.js';\nimport { initCommand } from '../commands/init.js';\n\nconst program = new Command();\n\nprogram\n .name('cordon')\n .description('The security gateway for AI agents')\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 .action(startCommand);\n\nprogram\n .command('init')\n .description('Generate cordon.config.ts and patch Claude Desktop config')\n .action(initCommand);\n\nprogram.parse();\n","import { CordonGateway } from '@getcordon/core';\nimport type { ResolvedConfig } from 'cordon-sdk';\nimport { emptyConfig, findConfigPath, loadConfig } from '../config-loader.js';\nimport { getState, setState } from '../cli-state.js';\n\nconst DASHBOARD_URL = 'https://cordon-server-production.up.railway.app/dashboard/';\n\ninterface StartOptions {\n config?: string;\n}\n\nexport async function startCommand(options: StartOptions): Promise<void> {\n if (!getState().welcomed) {\n process.stderr.write(\n `\\n\\x1b[36m[cordon] Want centralized audit logs + Slack approvals?\\x1b[0m\\n` +\n `[cordon] 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 cordon-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 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 'cordon-sdk';\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 } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\ninterface CliState {\n welcomed?: boolean;\n}\n\nfunction statePath(): string {\n return join(homedir(), '.cordon', 'state.json');\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 const path = statePath();\n const dir = join(homedir(), '.cordon');\n try {\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n const merged = { ...getState(), ...patch };\n writeFileSync(path, 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","import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { spawnSync } from 'node:child_process';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { setState } from '../cli-state.js';\n\nconst DASHBOARD_URL = 'https://cordon-server-production.up.railway.app/dashboard/';\n\nfunction ensureCordonSdkInstalled(cwd: string): void {\n // Config imports from 'cordon-sdk', and jiti resolves it from the config\n // file's directory. Without a local install, `cordon start` dies with\n // \"Cannot find module 'cordon-sdk'\" even when the CLI is global.\n if (existsSync(join(cwd, 'node_modules', 'cordon-sdk', '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 cordon-sdk...\\n`);\n const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';\n const result = spawnSync(npmCmd, ['install', 'cordon-sdk'], {\n cwd,\n stdio: ['ignore', 'inherit', 'inherit'],\n });\n\n if (result.status === 0) {\n process.stderr.write(`\\x1b[32m✓\\x1b[0m installed cordon-sdk\\n`);\n } else {\n process.stderr.write(\n `\\x1b[33mwarn\\x1b[0m: could not auto-install cordon-sdk. ` +\n `Run 'npm install cordon-sdk' in this directory before 'cordon start'.\\n`,\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: '${name}',\n transport: 'stdio',\n command: '${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 const content = `import { defineConfig } from 'cordon-sdk';\n\nexport default defineConfig({\n servers: [\n${serverBlocks}\n ],\n\n audit: {\n enabled: true,\n output: 'stdout',\n },\n\n approvals: {\n channel: 'terminal',\n // timeoutMs: 60_000,\n },\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 'cordon-sdk'. 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 if (claudePath && servers.length > 0) {\n const cordonConfigPath = outputPath.replace(/\\\\/g, '/');\n // On Windows, Claude Desktop spawns MCP servers via CreateProcess which\n // won't auto-resolve `npx` without the .cmd extension.\n const npxCommand = process.platform === 'win32' ? 'npx.cmd' : 'npx';\n\n const newClaudeConfig: ClaudeDesktopConfig = {\n ...claudeConfig,\n mcpServers: {\n cordon: {\n command: npxCommand,\n args: ['-y', 'cordon-cli', 'start', '--config', cordonConfigPath],\n },\n },\n };\n\n // Backup the original\n const backupPath = `${claudePath}.cordon-backup`;\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 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 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\": \"${process.platform === 'win32' ? 'npx.cmd' : 'npx'}\",\\n` +\n ` \"args\": [\"-y\", \"cordon-cli\", \"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 process.stderr.write(\n `\\n\\x1b[36mWant centralized audit logs + Slack approvals?\\x1b[0m\\n` +\n `Register a free account at ${DASHBOARD_URL}?utm_source=cli_init\\n`,\n );\n setState({ welcomed: true });\n}\n"],"mappings":"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","names":["existsSync","join","homedir","readFileSync","writeFileSync","existsSync","join","homedir","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"],"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('The security gateway for AI agents')\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 .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 'cordon-sdk';\nimport { emptyConfig, findConfigPath, loadConfig } from '../config-loader.js';\nimport { getState, setState, getAuth } from '../cli-state.js';\n\nconst DASHBOARD_URL = 'https://cordon-server-production.up.railway.app/dashboard/';\n\nfunction applyAuthDefaults(config: ResolvedConfig): ResolvedConfig {\n const auth = getAuth();\n if (!auth) return config;\n\n const audit = config.audit;\n const usesHosted = audit && (audit.output === 'hosted' || (Array.isArray(audit.output) && audit.output.includes('hosted')));\n if (!usesHosted) return config;\n if (audit.endpoint && audit.apiKey) return config;\n\n return {\n ...config,\n audit: {\n ...audit,\n endpoint: audit.endpoint ?? auth.endpoint,\n apiKey: audit.apiKey ?? auth.apiKey,\n },\n };\n}\n\ninterface StartOptions {\n config?: string;\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 cordon-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\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 'cordon-sdk';\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 { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { setState, getAuth } from '../cli-state.js';\n\nconst DASHBOARD_URL = 'https://cordon-server-production.up.railway.app/dashboard/';\n\nfunction ensureCordonSdkInstalled(cwd: string): void {\n // Config imports from 'cordon-sdk', and jiti resolves it from the config\n // file's directory. Without a local install, `cordon start` dies with\n // \"Cannot find module 'cordon-sdk'\" even when the CLI is global.\n if (existsSync(join(cwd, 'node_modules', 'cordon-sdk', '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 cordon-sdk...\\n`);\n const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';\n const result = spawnSync(npmCmd, ['install', 'cordon-sdk'], {\n cwd,\n stdio: ['ignore', 'inherit', 'inherit'],\n });\n\n if (result.status === 0) {\n process.stderr.write(`\\x1b[32m✓\\x1b[0m installed cordon-sdk\\n`);\n } else {\n process.stderr.write(\n `\\x1b[33mwarn\\x1b[0m: could not auto-install cordon-sdk. ` +\n `Run 'npm install cordon-sdk' in this directory before 'cordon start'.\\n`,\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: '${name}',\n transport: 'stdio',\n command: '${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 const auth = getAuth();\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 output: 'stdout',\n },`;\n\n const content = `import { defineConfig } from 'cordon-sdk';\n\nexport default defineConfig({\n servers: [\n${serverBlocks}\n ],\n\n ${auditBlock}\n\n approvals: {\n channel: 'terminal',\n // timeoutMs: 60_000,\n },\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 'cordon-sdk'. 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 if (claudePath && servers.length > 0) {\n const cordonConfigPath = outputPath.replace(/\\\\/g, '/');\n // On Windows, Claude Desktop spawns MCP servers via CreateProcess which\n // won't auto-resolve `npx` without the .cmd extension.\n const npxCommand = process.platform === 'win32' ? 'npx.cmd' : 'npx';\n\n const newClaudeConfig: ClaudeDesktopConfig = {\n ...claudeConfig,\n mcpServers: {\n cordon: {\n command: npxCommand,\n args: ['-y', 'cordon-cli', 'start', '--config', cordonConfigPath],\n },\n },\n };\n\n // Backup the original\n const backupPath = `${claudePath}.cordon-backup`;\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 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 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\": \"${process.platform === 'win32' ? 'npx.cmd' : 'npx'}\",\\n` +\n ` \"args\": [\"-y\", \"cordon-cli\", \"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 {\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://cordon-server-production.up.railway.app';\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 const args = process.platform === 'win32' ? ['/c', 'start', '\"\"', url] : [url];\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\nasync function listenForCallback(port: number, expectedState: 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 res.writeHead(400, { 'Content-Type': 'text/html' })\n .end('<html><body><h2>Login failed</h2><p>State mismatch or missing token. You can close this tab.</p></body></html>');\n server.close();\n reject(new Error('Callback state mismatch'));\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' })\n .end('<html><body style=\"font-family:system-ui;padding:40px;\"><h2>Logged in to Cordon</h2><p>You can close this tab and return to your terminal.</p></body></html>');\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);\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(`Welcome to Cordon. Run \\x1b[36mcordon init\\x1b[0m next to wire up your MCP servers.\\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":";;;AAAA,SAAS,eAAe;;;ACAxB,SAAS,qBAAqB;;;ACA9B,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAG3B,IAAM,eAAe;AAAA,EACnB,MAAM,KAAK,QAAQ,IAAI,GAAG,kBAAkB;AAAA,EAC5C,MAAM,KAAK,QAAQ,IAAI,GAAG,kBAAkB;AAAA,EAC5C,MAAM,KAAK,QAAQ,GAAG,WAAW,WAAW;AAAA,EAC5C,MAAM,KAAK,QAAQ,GAAG,WAAW,WAAW;AAC9C;AAEA,eAAsB,eAAe,UAAoC;AACvE,MAAI,UAAU;AACZ,QAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,YAAM,IAAI,MAAM,0BAA0B,QAAQ,EAAE;AAAA,IACtD;AACA,WAAO;AAAA,EACT;AAEA,aAAW,aAAa,cAAc;AACpC,UAAM,IAAI,UAAU;AACpB,QAAI,WAAW,CAAC,EAAG,QAAO;AAAA,EAC5B;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,WAAW,YAA6C;AAE5E,QAAM,OAAO,WAAW,YAAY,GAAG;AACvC,QAAM,MAAM,MAAM,KAAK,OAAO,UAAU;AAExC,QAAM,MAAO,IAAmC,WAAY;AAE5D,MAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,IAAI,OAAO,GAAG;AAClE,UAAM,IAAI;AAAA,MACR,qBAAqB,UAAU;AAAA,IAEjC;AAAA,EACF;AAEA,SAAO,cAAc,GAAmB;AAC1C;AAEA,SAAS,cAAc,QAAsC;AAC3D,SAAO;AAAA,IACL,GAAG;AAAA,IACH,OAAO,OAAO,SAAS,EAAE,SAAS,MAAM,QAAQ,SAAS;AAAA,IACzD,WAAW,OAAO,aAAa,EAAE,SAAS,WAAW;AAAA,EACvD;AACF;AAEO,SAAS,cAA8B;AAC5C,SAAO,cAAc,EAAE,SAAS,CAAC,EAAE,CAAC;AACtC;;;AC1DA,SAAS,cAAc,eAAe,cAAAA,aAAY,WAAW,WAAW,kBAAkB;AAC1F,SAAS,QAAAC,aAAY;AACrB,SAAS,WAAAC,gBAAe;AAaxB,SAAS,YAAoB;AAC3B,SAAOD,MAAKC,SAAQ,GAAG,SAAS;AAClC;AAEA,SAAS,YAAoB;AAC3B,SAAOD,MAAK,UAAU,GAAG,YAAY;AACvC;AAEA,SAAS,WAAmB;AAC1B,SAAOA,MAAK,UAAU,GAAG,WAAW;AACtC;AAEA,SAAS,kBAAwB;AAC/B,QAAM,MAAM,UAAU;AACtB,MAAI,CAACD,YAAW,GAAG,EAAG,WAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAC1D;AAEO,SAAS,WAAqB;AACnC,QAAM,OAAO,UAAU;AACvB,MAAI,CAACA,YAAW,IAAI,EAAG,QAAO,CAAC;AAC/B,MAAI;AACF,WAAO,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC;AAAA,EAC9C,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEO,SAAS,SAAS,OAAuB;AAC9C,MAAI;AACF,oBAAgB;AAChB,UAAM,SAAS,EAAE,GAAG,SAAS,GAAG,GAAG,MAAM;AACzC,kBAAc,UAAU,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG,MAAM;AAAA,EACpE,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,UAA0B;AACxC,QAAM,OAAO,SAAS;AACtB,MAAI,CAACA,YAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AACF,WAAO,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC;AAAA,EAC9C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,QAAQ,MAAqB;AAC3C,kBAAgB;AAChB,QAAM,OAAO,SAAS;AACtB,gBAAc,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,MAAM;AACzD,MAAI,QAAQ,aAAa,SAAS;AAChC,QAAI;AAAE,gBAAU,MAAM,GAAK;AAAA,IAAG,QAAQ;AAAA,IAAoB;AAAA,EAC5D;AACF;AAEO,SAAS,YAAqB;AACnC,QAAM,OAAO,SAAS;AACtB,MAAI,CAACA,YAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AACF,eAAW,IAAI;AACf,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AF3EA,IAAM,gBAAgB;AAEtB,SAAS,kBAAkB,QAAwC;AACjE,QAAM,OAAO,QAAQ;AACrB,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,QAAQ,OAAO;AACrB,QAAM,aAAa,UAAU,MAAM,WAAW,YAAa,MAAM,QAAQ,MAAM,MAAM,KAAK,MAAM,OAAO,SAAS,QAAQ;AACxH,MAAI,CAAC,WAAY,QAAO;AACxB,MAAI,MAAM,YAAY,MAAM,OAAQ,QAAO;AAE3C,SAAO;AAAA,IACL,GAAG;AAAA,IACH,OAAO;AAAA,MACL,GAAG;AAAA,MACH,UAAU,MAAM,YAAY,KAAK;AAAA,MACjC,QAAQ,MAAM,UAAU,KAAK;AAAA,IAC/B;AAAA,EACF;AACF;AAMA,eAAsB,aAAa,SAAsC;AACvE,MAAI,CAAC,SAAS,EAAE,YAAY,CAAC,QAAQ,GAAG;AACtC,YAAQ,OAAO;AAAA,MACb;AAAA;AAAA,+CACgD,aAAa;AAAA;AAAA;AAAA,IAC/D;AACA,aAAS,EAAE,UAAU,KAAK,CAAC;AAAA,EAC7B;AAKA,MAAI,SAAyB,YAAY;AACzC,MAAI,aAA4B;AAEhC,MAAI;AACF,iBAAa,MAAM,eAAe,QAAQ,MAAM;AAAA,EAClD,SAAS,KAAK;AACZ,QAAI,QAAQ,QAAQ;AAClB,cAAQ,OAAO,MAAM,yBAAyB,OAAO,GAAG,CAAC;AAAA,CAAI;AAC7D,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,OAAO;AAAA,MACb;AAAA,IAEF;AAAA,EACF;AAEA,MAAI,YAAY;AACd,YAAQ,OAAO,MAAM,gCAAgC,UAAU;AAAA,CAAI;AACnE,QAAI;AACF,eAAS,MAAM,WAAW,UAAU;AAAA,IACtC,SAAS,KAAK;AACZ,cAAQ,OAAO,MAAM,yBAAyB,OAAO,GAAG,CAAC;AAAA,CAAI;AAC7D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,WAAS,kBAAkB,MAAM;AAEjC,QAAM,UAAU,IAAI,cAAc,MAAM;AAExC,QAAM,WAAW,YAAY;AAC3B,YAAQ,OAAO,MAAM,+BAA+B;AACpD,QAAI;AACF,YAAM,QAAQ,KAAK;AACnB,cAAQ,KAAK,CAAC;AAAA,IAChB,SAAS,KAAK;AACZ,cAAQ,OAAO,MAAM,0CAA0C,OAAO,GAAG,CAAC;AAAA,CAAI;AAC9E,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,UAAQ,GAAG,UAAU,QAAQ;AAC7B,UAAQ,GAAG,WAAW,QAAQ;AAE9B,MAAI;AACF,UAAM,QAAQ,MAAM;AAAA,EACtB,SAAS,KAAK;AACZ,YAAQ,OAAO,MAAM,yCAAyC,OAAO,GAAG,CAAC;AAAA,CAAI;AAC7E,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;AG5FA,SAAS,gBAAAG,eAAc,iBAAAC,gBAAe,cAAAC,mBAAkB;AACxD,SAAS,iBAAiB;AAC1B,SAAS,QAAAC,aAAY;AACrB,SAAS,WAAAC,gBAAe;AAGxB,IAAMC,iBAAgB;AAEtB,SAAS,yBAAyB,KAAmB;AAInD,MAAIC,YAAWC,MAAK,KAAK,gBAAgB,cAAc,cAAc,CAAC,GAAG;AACvE;AAAA,EACF;AAEA,MAAI,CAACD,YAAWC,MAAK,KAAK,cAAc,CAAC,GAAG;AAC1C,IAAAC;AAAA,MACED,MAAK,KAAK,cAAc;AAAA,MACxB,KAAK,UAAU,EAAE,MAAM,iBAAiB,SAAS,SAAS,SAAS,KAAK,GAAG,MAAM,CAAC,IAAI;AAAA,MACtF;AAAA,IACF;AACA,YAAQ,OAAO,MAAM;AAAA,CAAyC;AAAA,EAChE;AAEA,UAAQ,OAAO,MAAM;AAAA,CAAqC;AAC1D,QAAM,SAAS,QAAQ,aAAa,UAAU,YAAY;AAC1D,QAAM,SAAS,UAAU,QAAQ,CAAC,WAAW,YAAY,GAAG;AAAA,IAC1D;AAAA,IACA,OAAO,CAAC,UAAU,WAAW,SAAS;AAAA,EACxC,CAAC;AAED,MAAI,OAAO,WAAW,GAAG;AACvB,YAAQ,OAAO,MAAM;AAAA,CAAyC;AAAA,EAChE,OAAO;AACL,YAAQ,OAAO;AAAA,MACb;AAAA;AAAA,IAEF;AAAA,EACF;AACF;AAMA,SAAS,sBAAqC;AAC5C,QAAM,aAAuB,CAAC;AAE9B,MAAI,QAAQ,aAAa,UAAU;AACjC,eAAW;AAAA,MACTA,MAAKE,SAAQ,GAAG,WAAW,uBAAuB,UAAU,4BAA4B;AAAA,IAC1F;AAAA,EACF,WAAW,QAAQ,aAAa,SAAS;AACvC,UAAM,UAAU,QAAQ,IAAI,SAAS,KAAKF,MAAKE,SAAQ,GAAG,WAAW,SAAS;AAC9E,eAAW,KAAKF,MAAK,SAAS,UAAU,4BAA4B,CAAC;AAAA,EACvE,OAAO;AACL,eAAW,KAAKA,MAAKE,SAAQ,GAAG,WAAW,UAAU,4BAA4B,CAAC;AAAA,EACpF;AAEA,SAAO,WAAW,KAAK,CAAC,MAAMH,YAAW,CAAC,CAAC,KAAK;AAClD;AAEA,eAAsB,cAA6B;AACjD,QAAM,aAAaC,MAAK,QAAQ,IAAI,GAAG,kBAAkB;AAEzD,MAAID,YAAW,UAAU,GAAG;AAC1B,YAAQ,OAAO;AAAA,MACb;AAAA;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,aAAa,oBAAoB;AACvC,MAAI,eAAoC,CAAC;AAEzC,MAAI,YAAY;AACd,YAAQ,OAAO,MAAM,2CAA2C,UAAU;AAAA,CAAI;AAC9E,QAAI;AACF,qBAAe,KAAK,MAAMI,cAAa,YAAY,MAAM,CAAC;AAAA,IAC5D,QAAQ;AACN,cAAQ,OAAO,MAAM;AAAA,CAA8D;AAAA,IACrF;AAAA,EACF,OAAO;AACL,YAAQ,OAAO;AAAA,MACb;AAAA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,OAAO,QAAQ,aAAa,cAAc,CAAC,CAAC;AAG5D,QAAM,eAAe,QAAQ,SAAS,IAClC,QACG,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM;AACpB,UAAM,UAAU,IAAI,MAAM,SACtB,WAAW,KAAK,UAAU,IAAI,IAAI,CAAC,KACnC;AACJ,UAAM,SACJ,IAAI,OAAO,OAAO,KAAK,IAAI,GAAG,EAAE,SAC5B,UAAU,KAAK,UAAU,IAAI,GAAG,CAAC,KACnC;AACJ,WAAO;AAAA,eACF,IAAI;AAAA;AAAA,kBAED,IAAI,OAAO,IAAI,OAAO,GAAG,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOzC,CAAC,EACA,KAAK,IAAI,IACZ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQJ,QAAM,OAAO,QAAQ;AACrB,QAAM,aAAa,OACf;AAAA;AAAA;AAAA;AAAA,QAKA;AAAA;AAAA;AAAA;AAKJ,QAAM,UAAU;AAAA;AAAA;AAAA;AAAA,EAIhB,YAAY;AAAA;AAAA;AAAA,IAGV,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASZ,EAAAF,eAAc,YAAY,SAAS,MAAM;AACzC,UAAQ,OAAO,MAAM;AAAA,CAA2C;AAIhE,2BAAyB,QAAQ,IAAI,CAAC;AAGtC,MAAI,cAAc,QAAQ,SAAS,GAAG;AACpC,UAAM,mBAAmB,WAAW,QAAQ,OAAO,GAAG;AAGtD,UAAM,aAAa,QAAQ,aAAa,UAAU,YAAY;AAE9D,UAAM,kBAAuC;AAAA,MAC3C,GAAG;AAAA,MACH,YAAY;AAAA,QACV,QAAQ;AAAA,UACN,SAAS;AAAA,UACT,MAAM,CAAC,MAAM,cAAc,SAAS,YAAY,gBAAgB;AAAA,QAClE;AAAA,MACF;AAAA,IACF;AAGA,UAAM,aAAa,GAAG,UAAU;AAChC,IAAAA,eAAc,YAAYE,cAAa,YAAY,MAAM,GAAG,MAAM;AAClE,YAAQ,OAAO,MAAM,4DAAuD,UAAU;AAAA,CAAI;AAE1F,IAAAF,eAAc,YAAY,KAAK,UAAU,iBAAiB,MAAM,CAAC,GAAG,MAAM;AAC1E,YAAQ,OAAO,MAAM;AAAA,CAAkD;AACvE,YAAQ,OAAO;AAAA,MACb;AAAA;AAAA;AAAA,IACF;AAAA,EACF,WAAW,CAAC,YAAY;AACtB,YAAQ,OAAO;AAAA,MACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAIqB,QAAQ,aAAa,UAAU,YAAY,KAAK;AAAA,2DACT,WAAW,QAAQ,OAAO,GAAG,CAAC;AAAA;AAAA;AAAA;AAAA,IAG5F;AAAA,EACF,OAAO;AAEL,YAAQ,OAAO;AAAA,MACb;AAAA;AAAA;AAAA,IACF;AAAA,EACF;AAEA,MAAI,MAAM;AACR,YAAQ,OAAO;AAAA,MACb;AAAA,uEAAqE,KAAK,QAAQ;AAAA;AAAA,IACpF;AAAA,EACF,OAAO;AACL,YAAQ,OAAO;AAAA,MACb;AAAA;AAAA,4EAEiBH,cAAa;AAAA;AAAA,IAChC;AAAA,EACF;AACA,WAAS,EAAE,UAAU,KAAK,CAAC;AAC7B;;;ACvNA,SAAS,oBAAoB;AAC7B,SAAS,aAAa;AACtB,SAAS,mBAAmB;AAG5B,IAAM,mBAAmB;AACzB,IAAM,aAAa,CAAC,OAAO,OAAO,KAAK;AACvC,IAAM,aAAa,IAAI,KAAK;AAM5B,SAAS,YAAY,KAAmB;AACtC,QAAM,MAAM,QAAQ,aAAa,WAAW,SACxC,QAAQ,aAAa,UAAU,QAC/B;AACJ,QAAM,OAAO,QAAQ,aAAa,UAAU,CAAC,MAAM,SAAS,MAAM,GAAG,IAAI,CAAC,GAAG;AAC7E,MAAI;AACF,UAAM,KAAK,MAAM,EAAE,UAAU,MAAM,OAAO,SAAS,CAAC,EAAE,MAAM;AAAA,EAC9D,QAAQ;AAAA,EAAsE;AAChF;AAQA,eAAe,kBAAkB,MAAc,eAAgD;AAC7F,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAS,aAAa,CAAC,KAAK,QAAQ;AACxC,YAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,oBAAoB,IAAI,EAAE;AAC9D,UAAI,IAAI,aAAa,OAAO;AAC1B,YAAI,UAAU,GAAG,EAAE,IAAI,WAAW;AAClC;AAAA,MACF;AACA,YAAM,QAAQ,IAAI,aAAa,IAAI,OAAO,KAAK;AAC/C,YAAM,QAAQ,IAAI,aAAa,IAAI,OAAO,KAAK;AAC/C,YAAM,SAAS,IAAI,aAAa,IAAI,QAAQ,KAAK;AAEjD,UAAI,UAAU,iBAAiB,CAAC,OAAO;AACrC,YAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC,EAC/C,IAAI,gHAAgH;AACvH,eAAO,MAAM;AACb,eAAO,IAAI,MAAM,yBAAyB,CAAC;AAC3C;AAAA,MACF;AAEA,UAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC,EAC/C,IAAI,8JAA8J;AACrK,aAAO,MAAM;AACb,cAAQ,EAAE,OAAO,OAAO,OAAO,CAAC;AAAA,IAClC,CAAC;AAED,WAAO,OAAO,MAAM,WAAW;AAC/B,WAAO,GAAG,SAAS,CAAC,QAAQ,OAAO,GAAG,CAAC;AAEvC,eAAW,MAAM;AACf,aAAO,MAAM;AACb,aAAO,IAAI,MAAM,iCAAiC,CAAC;AAAA,IACrD,GAAG,UAAU,EAAE,MAAM;AAAA,EACvB,CAAC;AACH;AAEA,eAAsB,aAAa,UAAwB,CAAC,GAAkB;AAC5E,QAAM,YAAY,QAAQ,YAAY,kBAAkB,QAAQ,OAAO,EAAE;AACzE,QAAM,QAAQ,YAAY,EAAE,EAAE,SAAS,KAAK;AAE5C,MAAI,OAAsB;AAC1B,MAAI,SAAgC;AACpC,MAAI,UAAmB;AAEvB,aAAW,aAAa,YAAY;AAClC,QAAI;AACF,YAAM,WAAW,oBAAoB,SAAS;AAC9C,YAAM,UACJ,GAAG,QAAQ,4BAA4B,mBAAmB,QAAQ,CAAC,UAAU,KAAK;AAEpF,cAAQ,OAAO;AAAA,QACb;AAAA;AAAA;AAAA,IAC0C,OAAO;AAAA;AAAA;AAAA,MACnD;AACA,kBAAY,OAAO;AAEnB,YAAM,UAAU,kBAAkB,WAAW,KAAK;AAClD,aAAO;AACP,eAAS,MAAM;AACf;AAAA,IACF,SAAS,KAAK;AACZ,gBAAU;AACV,YAAM,OAAQ,IAA8B;AAC5C,UAAI,SAAS,aAAc;AAC3B,YAAM;AAAA,IACR;AAAA,EACF;AAEA,MAAI,CAAC,QAAQ;AACX,UAAM,mBAAmB,QAAQ,UAAU,IAAI,MAAM,sCAAsC;AAAA,EAC7F;AAEA,UAAQ;AAAA,IACN;AAAA,IACA,QAAQ,OAAO;AAAA,IACf,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,EACrC,CAAC;AAED,UAAQ,OAAO,MAAM;AAAA,CAAoE;AACzF,MAAI,OAAO,WAAW,QAAQ;AAC5B,YAAQ,OAAO,MAAM;AAAA,CAAuF;AAAA,EAC9G;AAEA,OAAK;AACP;;;AC/GO,SAAS,gBAAsB;AACpC,MAAI,UAAU,GAAG;AACf,YAAQ,OAAO,MAAM;AAAA,CAA4D;AAAA,EACnF,OAAO;AACL,YAAQ,OAAO,MAAM;AAAA,CAA0B;AAAA,EACjD;AACF;;;ANFA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,QAAQ,EACb,YAAY,oCAAoC,EAChD,QAAQ,OAAO;AAElB,QACG,QAAQ,OAAO,EACf,YAAY,0BAA0B,EACtC,OAAO,uBAAuB,0BAA0B,EACxD,OAAO,YAAY;AAEtB,QACG,QAAQ,MAAM,EACd,YAAY,2DAA2D,EACvE,OAAO,WAAW;AAErB,QACG,QAAQ,OAAO,EACf,YAAY,8DAA8D,EAC1E,OAAO,oBAAoB,0DAA0D,EACrF,OAAO,CAAC,SAAS,aAAa,IAAI,EAAE,MAAM,CAAC,QAAQ;AAClD,UAAQ,OAAO,MAAM,uCAAuC,OAAO,GAAG,CAAC;AAAA,CAAI;AAC3E,UAAQ,KAAK,CAAC;AAChB,CAAC,CAAC;AAEJ,QACG,QAAQ,QAAQ,EAChB,YAAY,qCAAqC,EACjD,OAAO,aAAa;AAEvB,QAAQ,MAAM;","names":["existsSync","join","homedir","readFileSync","writeFileSync","existsSync","join","homedir","DASHBOARD_URL","existsSync","join","writeFileSync","homedir","readFileSync"]} |
+64
-16
@@ -53,8 +53,18 @@ // src/commands/start.ts | ||
| // src/cli-state.ts | ||
| import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs"; | ||
| import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync, chmodSync, unlinkSync } from "fs"; | ||
| import { join as join2 } from "path"; | ||
| import { homedir as homedir2 } from "os"; | ||
| function cordonDir() { | ||
| return join2(homedir2(), ".cordon"); | ||
| } | ||
| function statePath() { | ||
| return join2(homedir2(), ".cordon", "state.json"); | ||
| return join2(cordonDir(), "state.json"); | ||
| } | ||
| function authPath() { | ||
| return join2(cordonDir(), "auth.json"); | ||
| } | ||
| function ensureCordonDir() { | ||
| const dir = cordonDir(); | ||
| if (!existsSync2(dir)) mkdirSync(dir, { recursive: true }); | ||
| } | ||
| function getState() { | ||
@@ -70,20 +80,43 @@ const path = statePath(); | ||
| function setState(patch) { | ||
| const path = statePath(); | ||
| const dir = join2(homedir2(), ".cordon"); | ||
| try { | ||
| if (!existsSync2(dir)) mkdirSync(dir, { recursive: true }); | ||
| ensureCordonDir(); | ||
| const merged = { ...getState(), ...patch }; | ||
| writeFileSync(path, JSON.stringify(merged, null, 2), "utf8"); | ||
| writeFileSync(statePath(), JSON.stringify(merged, null, 2), "utf8"); | ||
| } catch { | ||
| } | ||
| } | ||
| function getAuth() { | ||
| const path = authPath(); | ||
| if (!existsSync2(path)) return null; | ||
| try { | ||
| return JSON.parse(readFileSync(path, "utf8")); | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
| // src/commands/start.ts | ||
| var DASHBOARD_URL = "https://cordon-server-production.up.railway.app/dashboard/"; | ||
| function applyAuthDefaults(config) { | ||
| const auth = getAuth(); | ||
| if (!auth) return config; | ||
| const audit = config.audit; | ||
| const usesHosted = audit && (audit.output === "hosted" || Array.isArray(audit.output) && audit.output.includes("hosted")); | ||
| if (!usesHosted) return config; | ||
| if (audit.endpoint && audit.apiKey) return config; | ||
| return { | ||
| ...config, | ||
| audit: { | ||
| ...audit, | ||
| endpoint: audit.endpoint ?? auth.endpoint, | ||
| apiKey: audit.apiKey ?? auth.apiKey | ||
| } | ||
| }; | ||
| } | ||
| async function startCommand(options) { | ||
| if (!getState().welcomed) { | ||
| if (!getState().welcomed && !getAuth()) { | ||
| process.stderr.write( | ||
| ` | ||
| \x1B[36m[cordon] Want centralized audit logs + Slack approvals?\x1B[0m | ||
| [cordon] Register at ${DASHBOARD_URL}?utm_source=cli_start | ||
| [cordon] Run \`cordon login\` or register at ${DASHBOARD_URL}?utm_source=cli_start | ||
@@ -119,2 +152,3 @@ ` | ||
| } | ||
| config = applyAuthDefaults(config); | ||
| const gateway = new CordonGateway(config); | ||
@@ -240,2 +274,11 @@ const shutdown = async () => { | ||
| // },`; | ||
| const auth = getAuth(); | ||
| const auditBlock = auth ? `audit: { | ||
| enabled: true, | ||
| output: 'hosted', | ||
| // endpoint + apiKey are auto-loaded from ~/.cordon/auth.json (cordon login) | ||
| },` : `audit: { | ||
| enabled: true, | ||
| output: 'stdout', | ||
| },`; | ||
| const content = `import { defineConfig } from 'cordon-sdk'; | ||
@@ -248,6 +291,3 @@ | ||
| audit: { | ||
| enabled: true, | ||
| output: 'stdout', | ||
| }, | ||
| ${auditBlock} | ||
@@ -309,8 +349,16 @@ approvals: { | ||
| } | ||
| process.stderr.write( | ||
| ` | ||
| if (auth) { | ||
| process.stderr.write( | ||
| ` | ||
| \x1B[32m\u2713\x1B[0m audit logs will stream to your Cordon account (${auth.endpoint}) | ||
| ` | ||
| ); | ||
| } else { | ||
| process.stderr.write( | ||
| ` | ||
| \x1B[36mWant centralized audit logs + Slack approvals?\x1B[0m | ||
| Register a free account at ${DASHBOARD_URL2}?utm_source=cli_init | ||
| Run \x1B[36mcordon login\x1B[0m to register a free account, or sign up at ${DASHBOARD_URL2}?utm_source=cli_init | ||
| ` | ||
| ); | ||
| ); | ||
| } | ||
| setState({ welcomed: true }); | ||
@@ -317,0 +365,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/commands/start.ts","../src/config-loader.ts","../src/cli-state.ts","../src/commands/init.ts"],"sourcesContent":["import { CordonGateway } from '@getcordon/core';\nimport type { ResolvedConfig } from 'cordon-sdk';\nimport { emptyConfig, findConfigPath, loadConfig } from '../config-loader.js';\nimport { getState, setState } from '../cli-state.js';\n\nconst DASHBOARD_URL = 'https://cordon-server-production.up.railway.app/dashboard/';\n\ninterface StartOptions {\n config?: string;\n}\n\nexport async function startCommand(options: StartOptions): Promise<void> {\n if (!getState().welcomed) {\n process.stderr.write(\n `\\n\\x1b[36m[cordon] Want centralized audit logs + Slack approvals?\\x1b[0m\\n` +\n `[cordon] 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 cordon-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 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 'cordon-sdk';\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 } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\ninterface CliState {\n welcomed?: boolean;\n}\n\nfunction statePath(): string {\n return join(homedir(), '.cordon', 'state.json');\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 const path = statePath();\n const dir = join(homedir(), '.cordon');\n try {\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n const merged = { ...getState(), ...patch };\n writeFileSync(path, 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","import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { spawnSync } from 'node:child_process';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { setState } from '../cli-state.js';\n\nconst DASHBOARD_URL = 'https://cordon-server-production.up.railway.app/dashboard/';\n\nfunction ensureCordonSdkInstalled(cwd: string): void {\n // Config imports from 'cordon-sdk', and jiti resolves it from the config\n // file's directory. Without a local install, `cordon start` dies with\n // \"Cannot find module 'cordon-sdk'\" even when the CLI is global.\n if (existsSync(join(cwd, 'node_modules', 'cordon-sdk', '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 cordon-sdk...\\n`);\n const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';\n const result = spawnSync(npmCmd, ['install', 'cordon-sdk'], {\n cwd,\n stdio: ['ignore', 'inherit', 'inherit'],\n });\n\n if (result.status === 0) {\n process.stderr.write(`\\x1b[32m✓\\x1b[0m installed cordon-sdk\\n`);\n } else {\n process.stderr.write(\n `\\x1b[33mwarn\\x1b[0m: could not auto-install cordon-sdk. ` +\n `Run 'npm install cordon-sdk' in this directory before 'cordon start'.\\n`,\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: '${name}',\n transport: 'stdio',\n command: '${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 const content = `import { defineConfig } from 'cordon-sdk';\n\nexport default defineConfig({\n servers: [\n${serverBlocks}\n ],\n\n audit: {\n enabled: true,\n output: 'stdout',\n },\n\n approvals: {\n channel: 'terminal',\n // timeoutMs: 60_000,\n },\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 'cordon-sdk'. 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 if (claudePath && servers.length > 0) {\n const cordonConfigPath = outputPath.replace(/\\\\/g, '/');\n // On Windows, Claude Desktop spawns MCP servers via CreateProcess which\n // won't auto-resolve `npx` without the .cmd extension.\n const npxCommand = process.platform === 'win32' ? 'npx.cmd' : 'npx';\n\n const newClaudeConfig: ClaudeDesktopConfig = {\n ...claudeConfig,\n mcpServers: {\n cordon: {\n command: npxCommand,\n args: ['-y', 'cordon-cli', 'start', '--config', cordonConfigPath],\n },\n },\n };\n\n // Backup the original\n const backupPath = `${claudePath}.cordon-backup`;\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 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 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\": \"${process.platform === 'win32' ? 'npx.cmd' : 'npx'}\",\\n` +\n ` \"args\": [\"-y\", \"cordon-cli\", \"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 process.stderr.write(\n `\\n\\x1b[36mWant centralized audit logs + Slack approvals?\\x1b[0m\\n` +\n `Register a free account at ${DASHBOARD_URL}?utm_source=cli_init\\n`,\n );\n setState({ welcomed: true });\n}\n"],"mappings":"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","names":["existsSync","join","homedir","readFileSync","writeFileSync","existsSync","join","homedir","DASHBOARD_URL","existsSync","join","writeFileSync","homedir","readFileSync"]} | ||
| {"version":3,"sources":["../src/commands/start.ts","../src/config-loader.ts","../src/cli-state.ts","../src/commands/init.ts"],"sourcesContent":["import { CordonGateway } from '@getcordon/core';\nimport type { ResolvedConfig } from 'cordon-sdk';\nimport { emptyConfig, findConfigPath, loadConfig } from '../config-loader.js';\nimport { getState, setState, getAuth } from '../cli-state.js';\n\nconst DASHBOARD_URL = 'https://cordon-server-production.up.railway.app/dashboard/';\n\nfunction applyAuthDefaults(config: ResolvedConfig): ResolvedConfig {\n const auth = getAuth();\n if (!auth) return config;\n\n const audit = config.audit;\n const usesHosted = audit && (audit.output === 'hosted' || (Array.isArray(audit.output) && audit.output.includes('hosted')));\n if (!usesHosted) return config;\n if (audit.endpoint && audit.apiKey) return config;\n\n return {\n ...config,\n audit: {\n ...audit,\n endpoint: audit.endpoint ?? auth.endpoint,\n apiKey: audit.apiKey ?? auth.apiKey,\n },\n };\n}\n\ninterface StartOptions {\n config?: string;\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 cordon-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\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 'cordon-sdk';\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 { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { setState, getAuth } from '../cli-state.js';\n\nconst DASHBOARD_URL = 'https://cordon-server-production.up.railway.app/dashboard/';\n\nfunction ensureCordonSdkInstalled(cwd: string): void {\n // Config imports from 'cordon-sdk', and jiti resolves it from the config\n // file's directory. Without a local install, `cordon start` dies with\n // \"Cannot find module 'cordon-sdk'\" even when the CLI is global.\n if (existsSync(join(cwd, 'node_modules', 'cordon-sdk', '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 cordon-sdk...\\n`);\n const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';\n const result = spawnSync(npmCmd, ['install', 'cordon-sdk'], {\n cwd,\n stdio: ['ignore', 'inherit', 'inherit'],\n });\n\n if (result.status === 0) {\n process.stderr.write(`\\x1b[32m✓\\x1b[0m installed cordon-sdk\\n`);\n } else {\n process.stderr.write(\n `\\x1b[33mwarn\\x1b[0m: could not auto-install cordon-sdk. ` +\n `Run 'npm install cordon-sdk' in this directory before 'cordon start'.\\n`,\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: '${name}',\n transport: 'stdio',\n command: '${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 const auth = getAuth();\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 output: 'stdout',\n },`;\n\n const content = `import { defineConfig } from 'cordon-sdk';\n\nexport default defineConfig({\n servers: [\n${serverBlocks}\n ],\n\n ${auditBlock}\n\n approvals: {\n channel: 'terminal',\n // timeoutMs: 60_000,\n },\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 'cordon-sdk'. 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 if (claudePath && servers.length > 0) {\n const cordonConfigPath = outputPath.replace(/\\\\/g, '/');\n // On Windows, Claude Desktop spawns MCP servers via CreateProcess which\n // won't auto-resolve `npx` without the .cmd extension.\n const npxCommand = process.platform === 'win32' ? 'npx.cmd' : 'npx';\n\n const newClaudeConfig: ClaudeDesktopConfig = {\n ...claudeConfig,\n mcpServers: {\n cordon: {\n command: npxCommand,\n args: ['-y', 'cordon-cli', 'start', '--config', cordonConfigPath],\n },\n },\n };\n\n // Backup the original\n const backupPath = `${claudePath}.cordon-backup`;\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 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 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\": \"${process.platform === 'win32' ? 'npx.cmd' : 'npx'}\",\\n` +\n ` \"args\": [\"-y\", \"cordon-cli\", \"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 {\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"],"mappings":"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","names":["existsSync","join","homedir","readFileSync","writeFileSync","existsSync","join","homedir","DASHBOARD_URL","existsSync","join","writeFileSync","homedir","readFileSync"]} |
+1
-1
| { | ||
| "name": "cordon-cli", | ||
| "version": "0.1.12", | ||
| "version": "0.1.13", | ||
| "mcpName": "io.github.marras0914/cordon", | ||
@@ -5,0 +5,0 @@ "description": "The security gateway for AI agents — firewall, auditor, and remote control for MCP tool calls", |
Network access
Supply chain riskThis module accesses the network.
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 2 instances
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
81990
34.04%850
35.35%10
-9.09%4
100%