@algosuite/vo-mcp 0.2.0-beta.2 → 0.2.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,15 @@ var PROVIDER_ALIAS = {
10
10
  anthropic: "anthropic",
11
11
  codex: "openai",
12
12
  openai: "openai",
13
- cursor: "cursor"
13
+ cursor: "cursor",
14
+ grok: "xai",
15
+ xai: "xai",
16
+ meta: "meta",
17
+ muse: "meta",
18
+ spark: "meta",
19
+ "muse-spark": "meta",
20
+ oai: "oai-compat",
21
+ "oai-compat": "oai-compat"
14
22
  };
15
23
  function resolveProvider(name) {
16
24
  const key = String(name || "").trim().toLowerCase();
@@ -68,7 +76,9 @@ function hasAgentKey(provider, opts = {}) {
68
76
  var PROVIDERS = {
69
77
  anthropic: { label: "Anthropic", cli: "claude", selector: "claude", envVar: "ANTHROPIC_API_KEY", hint: "sk-" },
70
78
  openai: { label: "OpenAI / Codex", cli: "codex", selector: "codex", envVar: "OPENAI_API_KEY", hint: "sk-" },
71
- cursor: { label: "Cursor", cli: "cursor-agent", selector: "cursor", envVar: "CURSOR_API_KEY", hint: "" }
79
+ cursor: { label: "Cursor", cli: "cursor-agent", selector: "cursor", envVar: "CURSOR_API_KEY", hint: "" },
80
+ xai: { label: "xAI / Grok", cli: "grok", selector: "grok", envVar: "XAI_API_KEY", hint: "xai-" },
81
+ meta: { label: "Meta Model API / Muse Spark", cli: "codex", selector: "meta", envVar: "MODEL_API_KEY", hint: "" }
72
82
  };
73
83
  function parseSetKeyArgs(argv = []) {
74
84
  const rest = argv.slice(3);
@@ -128,7 +138,7 @@ function readSecret(promptText) {
128
138
  async function main() {
129
139
  const { provider, providerArg, mode } = parseSetKeyArgs(process.argv);
130
140
  if (!provider) {
131
- log(`Unknown --provider "${providerArg}". Use one of: claude, codex, cursor.`);
141
+ log(`Unknown --provider "${providerArg}". Use one of: claude, codex, cursor, grok, meta.`);
132
142
  process.exitCode = 1;
133
143
  return;
134
144
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../scripts/virtual-office/code-runner/agent-key-store.mjs", "../src/set-key-args.mjs", "../src/set-key-cli.mjs"],
4
- "sourcesContent": ["/**\n * agent-key-store \u2014 BYO Phase-B multi-agent. A provider-keyed generalization of\n * [anthropic-key-store] so a friend can store the credential for whichever CLI\n * agent they run (Claude / Codex / Cursor) in the OPERATING-SYSTEM keychain\n * (@napi-rs/keyring). The key lives ONLY on the friend's machine and never\n * reaches Algosuite; the runner reads it at spawn time to authenticate the\n * headless agent.\n *\n * Relationship to anthropic-key-store: that module is the Claude-specific M4\n * path and is left UNTOUCHED. This store uses the SAME service ('algosuite-vo')\n * and the SAME account convention ('<provider>-api-key'), so a key written by\n * `vo-mcp set-key` (account 'anthropic-api-key') is readable here as provider\n * 'anthropic' \u2014 the two are interoperable, not competing stores.\n *\n * Design rules (identical posture to anthropic-key-store):\n * - PURELY graceful: if @napi-rs/keyring is absent, EVERY op no-ops\n * (null/false) so the runner falls back to the ambient env / CLI login.\n * - An explicit credential already in the environment ALWAYS wins over the\n * keychain (a manual override is never silently replaced).\n * - The key travels via process env to the spawned agent, never via argv.\n */\nimport { createRequire } from 'node:module';\n\nconst require = createRequire(import.meta.url);\n\nexport const KEY_SERVICE = 'algosuite-vo';\n\n/**\n * Provider \u2192 the env var(s) its CLI reads for its API credential. Filling more\n * than one is harmless (the CLI ignores vars it doesn't use); 'openai' covers\n * both names the Codex CLI accepts across versions.\n */\nexport const PROVIDER_ENV = {\n anthropic: ['ANTHROPIC_API_KEY'],\n openai: ['OPENAI_API_KEY', 'CODEX_API_KEY'],\n cursor: ['CURSOR_API_KEY'],\n};\n\n/** Canonical provider for a runner's logical name (claude\u2192anthropic, codex\u2192openai). */\nconst PROVIDER_ALIAS = {\n claude: 'anthropic',\n anthropic: 'anthropic',\n codex: 'openai',\n openai: 'openai',\n cursor: 'cursor',\n};\n\n/** Normalize a runner/provider label to its credential provider. Returns null if unknown. */\nexport function resolveProvider(name) {\n const key = String(name || '').trim().toLowerCase();\n return PROVIDER_ALIAS[key] || null;\n}\n\n/** Keychain account name for a provider (matches anthropic-key-store's 'anthropic-api-key'). */\nexport function accountFor(provider) {\n return `${provider}-api-key`;\n}\n\nlet _entryCtor;\nlet _loadTried = false;\n\n/** Lazily load @napi-rs/keyring's `Entry`; null when unavailable (\u2192 no-op ops). */\nfunction defaultEntryCtor() {\n if (_loadTried) return _entryCtor;\n _loadTried = true;\n try {\n _entryCtor = require('@napi-rs/keyring').Entry;\n } catch {\n _entryCtor = null; // not installed / unsupported platform \u2192 graceful fallback\n }\n return _entryCtor;\n}\n\n/** Store `key` for `provider` in the OS keychain. Returns true on success. */\nexport function setAgentKey(provider, key, { EntryCtor = defaultEntryCtor() } = {}) {\n const p = resolveProvider(provider);\n if (!p || !key || !EntryCtor) return false;\n try {\n new EntryCtor(KEY_SERVICE, accountFor(p)).setPassword(String(key));\n return true;\n } catch {\n return false;\n }\n}\n\n/** Read `provider`'s key from the OS keychain. Returns the key, or null. */\nexport function getAgentKey(provider, { EntryCtor = defaultEntryCtor() } = {}) {\n const p = resolveProvider(provider);\n if (!p || !EntryCtor) return null;\n try {\n // getPassword throws (keyring-rs NoEntry) when nothing is stored.\n return new EntryCtor(KEY_SERVICE, accountFor(p)).getPassword() || null;\n } catch {\n return null;\n }\n}\n\n/** Delete `provider`'s stored key. Returns true if a key was removed. */\nexport function clearAgentKey(provider, { EntryCtor = defaultEntryCtor() } = {}) {\n const p = resolveProvider(provider);\n if (!p || !EntryCtor) return false;\n try {\n new EntryCtor(KEY_SERVICE, accountFor(p)).deletePassword();\n return true;\n } catch {\n return false;\n }\n}\n\n/** True if a key is currently stored for `provider` (and the keychain is available). */\nexport function hasAgentKey(provider, opts = {}) {\n return getAgentKey(provider, opts) !== null;\n}\n\n/**\n * Return an env for the spawned agent with `provider`'s credential env var(s)\n * filled from the keychain \u2014 but ONLY for vars not already set (explicit env\n * wins) and only when a key is actually stored. Always returns a fresh object;\n * never mutates `baseEnv`. Unknown provider or no stored key \u2192 shallow copy.\n */\nexport function withAgentKey(provider, baseEnv = {}, { getKey = getAgentKey } = {}) {\n const p = resolveProvider(provider);\n const vars = (p && PROVIDER_ENV[p]) || [];\n const out = { ...baseEnv };\n if (!p || vars.length === 0) return out;\n // If any target var is already set, treat the credential as operator-provided.\n if (vars.some((v) => out[v])) return out;\n const key = getKey(p);\n if (!key) return out;\n for (const v of vars) out[v] = key;\n return out;\n}\n", "/**\n * set-key-args \u2014 the PURE arg-parsing + provider table for `vo-mcp set-key`.\n * Split out of set-key-cli.mjs so it can be unit-tested without importing the\n * CLI (whose module body reads stdin and runs on import). esbuild inlines it at\n * bundle time, same as the other runner-tree .mjs imports.\n */\nimport { resolveProvider } from '../../../scripts/virtual-office/code-runner/agent-key-store.mjs';\n\n/**\n * Per-provider metadata: display `label`, the agent `cli` binary, the\n * `selector` keyword the user types after `--provider` (NOT the binary \u2014 e.g.\n * cursor's binary is `cursor-agent`), the `envVar` fallback, and a typical\n * key-prefix `hint` for a gentle (never-blocking) format check.\n */\nexport const PROVIDERS = {\n anthropic: { label: 'Anthropic', cli: 'claude', selector: 'claude', envVar: 'ANTHROPIC_API_KEY', hint: 'sk-' },\n openai: { label: 'OpenAI / Codex', cli: 'codex', selector: 'codex', envVar: 'OPENAI_API_KEY', hint: 'sk-' },\n cursor: { label: 'Cursor', cli: 'cursor-agent', selector: 'cursor', envVar: 'CURSOR_API_KEY', hint: '' },\n};\n\n/**\n * Parse `vo-mcp set-key` argv (the FULL process.argv; the subcommand is argv[2]).\n * Supports `--provider <p>` / `--provider=p` (default anthropic), `--status`,\n * `--clear`. Returns `{ provider, providerArg, mode }` where `provider` is the\n * normalized credential provider (null if the value is unknown) and `mode` is\n * 'set' | 'status' | 'clear'.\n */\nexport function parseSetKeyArgs(argv = []) {\n const rest = argv.slice(3);\n let providerArg = 'anthropic';\n let mode = 'set';\n for (let i = 0; i < rest.length; i += 1) {\n const a = rest[i];\n if (a === '--status') mode = 'status';\n else if (a === '--clear') mode = 'clear';\n else if (a === '--provider') {\n providerArg = rest[i + 1] || '';\n i += 1; // consume the value token\n } else if (a.startsWith('--provider=')) {\n providerArg = a.slice('--provider='.length);\n }\n }\n return { provider: resolveProvider(providerArg), providerArg, mode };\n}\n", "#!/usr/bin/env node\r\n/**\r\n * `vo-mcp set-key` \u2014 BYO Phase-B M4 + multi-agent. Stores the friend's AI API key\r\n * in the OS keychain (Windows Credential Manager / macOS Keychain / libsecret) so\r\n * the runner can authenticate its CLI agent without an env var. The key lives ONLY\r\n * on the friend's machine \u2014 it never travels to Algosuite.\r\n *\r\n * vo-mcp set-key store the Anthropic (Claude) key [default]\r\n * vo-mcp set-key --provider codex store the OpenAI/Codex key\r\n * vo-mcp set-key --provider cursor store the Cursor key\r\n * vo-mcp set-key --status report whether the provider's key is stored\r\n * vo-mcp set-key --clear remove the provider's stored key\r\n *\r\n * The key may be piped (`echo $KEY | vo-mcp set-key --provider codex`) or typed at\r\n * the hidden prompt. .mjs (like runner-cli.mjs) because it imports the runner-tree\r\n * key store, outside tsconfig rootDir \u2014 tsc skips it; esbuild inlines it at bundle.\r\n */\r\nimport {\r\n setAgentKey,\r\n hasAgentKey,\r\n clearAgentKey,\r\n} from '../../../scripts/virtual-office/code-runner/agent-key-store.mjs';\r\nimport { PROVIDERS, parseSetKeyArgs } from './set-key-args.mjs';\r\n\r\nconst log = (m) => console.error(m);\r\nconst mask = (k) => (k && k.length > 8 ? `\u2026${k.slice(-4)}` : '****');\r\n\r\n/** Read a secret from a TTY without echoing; falls back to piped stdin. */\r\nfunction readSecret(promptText) {\r\n return new Promise((resolve) => {\r\n if (!process.stdin.isTTY) {\r\n // Piped: `echo $KEY | vo-mcp set-key` \u2014 read everything, take the first line.\r\n let buf = '';\r\n process.stdin.setEncoding('utf8');\r\n process.stdin.on('data', (d) => (buf += d));\r\n process.stdin.on('end', () => resolve(buf.split(/\\r?\\n/)[0].trim()));\r\n return;\r\n }\r\n // Interactive: print the prompt, then mute the echoed characters.\r\n process.stdout.write(promptText);\r\n const stdin = process.stdin;\r\n stdin.setRawMode?.(true);\r\n stdin.resume();\r\n stdin.setEncoding('utf8');\r\n let val = '';\r\n const onData = (ch) => {\r\n if (ch === '\\r' || ch === '\\n' || ch === '\u0004') {\r\n stdin.setRawMode?.(false);\r\n stdin.pause();\r\n stdin.removeListener('data', onData);\r\n process.stdout.write('\\n');\r\n resolve(val.trim());\r\n } else if (ch === '\u0003') {\r\n // Ctrl-C\r\n process.stdout.write('\\n');\r\n process.exit(130);\r\n } else if (ch === '\u007F' || ch === '\\b') {\r\n val = val.slice(0, -1);\r\n } else {\r\n val += ch;\r\n }\r\n };\r\n stdin.on('data', onData);\r\n });\r\n}\r\n\r\nasync function main() {\r\n const { provider, providerArg, mode } = parseSetKeyArgs(process.argv);\r\n if (!provider) {\r\n log(`Unknown --provider \"${providerArg}\". Use one of: claude, codex, cursor.`);\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const info = PROVIDERS[provider];\r\n\r\n if (mode === 'status') {\r\n log(\r\n hasAgentKey(provider)\r\n ? `\u2713 A ${info.label} API key is stored in your OS keychain.`\r\n : `No ${info.label} API key stored. Run \\`vo-mcp set-key${provider === 'anthropic' ? '' : ` --provider ${info.selector}`}\\` to add one.`,\r\n );\r\n return;\r\n }\r\n\r\n if (mode === 'clear') {\r\n log(\r\n clearAgentKey(provider)\r\n ? `\u2713 Removed the stored ${info.label} API key.`\r\n : `No stored ${info.label} key to remove (or keychain unavailable).`,\r\n );\r\n return;\r\n }\r\n\r\n log(`Paste your ${info.label} API key (for the \\`${info.cli}\\` agent). It is stored ONLY in`);\r\n log(\"your computer's keychain \u2014 it never leaves this machine and Algosuite never sees it.\");\r\n const key = await readSecret(`${info.label} API key: `);\r\n if (!key) {\r\n log('No key entered \u2014 nothing stored.');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n // A gentle format hint only \u2014 key formats change and Cursor's isn't `sk-`, so\r\n // never hard-reject a non-matching key (that would block a valid credential).\r\n if (info.hint && !key.startsWith(info.hint)) {\r\n log(`Heads up: ${info.label} keys usually start with \"${info.hint}\". Storing it anyway.`);\r\n }\r\n if (setAgentKey(provider, key)) {\r\n log(`\u2713 Stored your ${info.label} API key (${mask(key)}) in the OS keychain. The runner will use it automatically.`);\r\n } else {\r\n log(`Could not access the OS keychain on this machine. As a fallback, set ${info.envVar} in your environment before running \\`vo-mcp runner\\`.`);\r\n process.exitCode = 1;\r\n }\r\n}\r\n\r\nmain().catch((e) => {\r\n log(`set-key failed: ${e && e.message ? e.message : e}`);\r\n process.exitCode = 1;\r\n});\r\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * agent-key-store \u2014 BYO Phase-B multi-agent. A provider-keyed generalization of\n * [anthropic-key-store] so a friend can store the credential for whichever CLI\n * agent they run (Claude / Codex / Cursor / Grok) in the OPERATING-SYSTEM keychain\n * (@napi-rs/keyring). The key lives ONLY on the friend's machine and never\n * reaches Algosuite; the runner reads it at spawn time to authenticate the\n * headless agent.\n *\n * Relationship to anthropic-key-store: that module is the Claude-specific M4\n * path and is left UNTOUCHED. This store uses the SAME service ('algosuite-vo')\n * and the SAME account convention ('<provider>-api-key'), so a key written by\n * `vo-mcp set-key` (account 'anthropic-api-key') is readable here as provider\n * 'anthropic' \u2014 the two are interoperable, not competing stores.\n *\n * Design rules (identical posture to anthropic-key-store):\n * - PURELY graceful: if @napi-rs/keyring is absent, EVERY op no-ops\n * (null/false) so the runner falls back to the ambient env / CLI login.\n * - An explicit credential already in the environment ALWAYS wins over the\n * keychain (a manual override is never silently replaced).\n * - The key travels via process env to the spawned agent, never via argv.\n */\nimport { createRequire } from 'node:module';\n\nconst require = createRequire(import.meta.url);\n\nexport const KEY_SERVICE = 'algosuite-vo';\n\n/**\n * Provider \u2192 the env var(s) its CLI reads for its API credential. Filling more\n * than one is harmless (the CLI ignores vars it doesn't use); 'openai' covers\n * both names the Codex CLI accepts across versions.\n */\nexport const PROVIDER_ENV = {\n anthropic: ['ANTHROPIC_API_KEY'],\n openai: ['OPENAI_API_KEY', 'CODEX_API_KEY'],\n cursor: ['CURSOR_API_KEY'],\n xai: ['XAI_API_KEY'],\n meta: ['MODEL_API_KEY'],\n // Generic OpenAI-compatible runner (bring-your-own model + endpoint): its key\n // is a dedicated var so it never collides with a real OpenAI/Codex key.\n 'oai-compat': ['VO_CODE_RUNNER_OAI_API_KEY'],\n};\n\n/** Canonical provider for a runner's logical name (claude\u2192anthropic, codex\u2192openai). */\nconst PROVIDER_ALIAS = {\n claude: 'anthropic',\n anthropic: 'anthropic',\n codex: 'openai',\n openai: 'openai',\n cursor: 'cursor',\n grok: 'xai',\n xai: 'xai',\n meta: 'meta',\n muse: 'meta',\n spark: 'meta',\n 'muse-spark': 'meta',\n oai: 'oai-compat',\n 'oai-compat': 'oai-compat',\n};\n\n/** Normalize a runner/provider label to its credential provider. Returns null if unknown. */\nexport function resolveProvider(name) {\n const key = String(name || '').trim().toLowerCase();\n return PROVIDER_ALIAS[key] || null;\n}\n\n/** Keychain account name for a provider (matches anthropic-key-store's 'anthropic-api-key'). */\nexport function accountFor(provider) {\n return `${provider}-api-key`;\n}\n\nlet _entryCtor;\nlet _loadTried = false;\n\n/** Lazily load @napi-rs/keyring's `Entry`; null when unavailable (\u2192 no-op ops). */\nfunction defaultEntryCtor() {\n if (_loadTried) return _entryCtor;\n _loadTried = true;\n try {\n _entryCtor = require('@napi-rs/keyring').Entry;\n } catch {\n _entryCtor = null; // not installed / unsupported platform \u2192 graceful fallback\n }\n return _entryCtor;\n}\n\n/** Store `key` for `provider` in the OS keychain. Returns true on success. */\nexport function setAgentKey(provider, key, { EntryCtor = defaultEntryCtor() } = {}) {\n const p = resolveProvider(provider);\n if (!p || !key || !EntryCtor) return false;\n try {\n new EntryCtor(KEY_SERVICE, accountFor(p)).setPassword(String(key));\n return true;\n } catch {\n return false;\n }\n}\n\n/** Read `provider`'s key from the OS keychain. Returns the key, or null. */\nexport function getAgentKey(provider, { EntryCtor = defaultEntryCtor() } = {}) {\n const p = resolveProvider(provider);\n if (!p || !EntryCtor) return null;\n try {\n // getPassword throws (keyring-rs NoEntry) when nothing is stored.\n return new EntryCtor(KEY_SERVICE, accountFor(p)).getPassword() || null;\n } catch {\n return null;\n }\n}\n\n/** Delete `provider`'s stored key. Returns true if a key was removed. */\nexport function clearAgentKey(provider, { EntryCtor = defaultEntryCtor() } = {}) {\n const p = resolveProvider(provider);\n if (!p || !EntryCtor) return false;\n try {\n new EntryCtor(KEY_SERVICE, accountFor(p)).deletePassword();\n return true;\n } catch {\n return false;\n }\n}\n\n/** True if a key is currently stored for `provider` (and the keychain is available). */\nexport function hasAgentKey(provider, opts = {}) {\n return getAgentKey(provider, opts) !== null;\n}\n\n/**\n * Return an env for the spawned agent with `provider`'s credential env var(s)\n * filled from the keychain \u2014 but ONLY for vars not already set (explicit env\n * wins) and only when a key is actually stored. Always returns a fresh object;\n * never mutates `baseEnv`. Unknown provider or no stored key \u2192 shallow copy.\n */\nexport function withAgentKey(provider, baseEnv = {}, { getKey = getAgentKey } = {}) {\n const p = resolveProvider(provider);\n const vars = (p && PROVIDER_ENV[p]) || [];\n const out = { ...baseEnv };\n if (!p || vars.length === 0) return out;\n // If any target var is already set, treat the credential as operator-provided.\n if (vars.some((v) => out[v])) return out;\n const key = getKey(p);\n if (!key) return out;\n for (const v of vars) out[v] = key;\n return out;\n}\n", "/**\n * set-key-args \u2014 the PURE arg-parsing + provider table for `vo-mcp set-key`.\n * Split out of set-key-cli.mjs so it can be unit-tested without importing the\n * CLI (whose module body reads stdin and runs on import). esbuild inlines it at\n * bundle time, same as the other runner-tree .mjs imports.\n */\nimport { resolveProvider } from '../../../scripts/virtual-office/code-runner/agent-key-store.mjs';\n\n/**\n * Per-provider metadata: display `label`, the agent `cli` binary, the\n * `selector` keyword the user types after `--provider` (NOT the binary \u2014 e.g.\n * cursor's binary is `cursor-agent`), the `envVar` fallback, and a typical\n * key-prefix `hint` for a gentle (never-blocking) format check.\n */\nexport const PROVIDERS = {\n anthropic: { label: 'Anthropic', cli: 'claude', selector: 'claude', envVar: 'ANTHROPIC_API_KEY', hint: 'sk-' },\n openai: { label: 'OpenAI / Codex', cli: 'codex', selector: 'codex', envVar: 'OPENAI_API_KEY', hint: 'sk-' },\n cursor: { label: 'Cursor', cli: 'cursor-agent', selector: 'cursor', envVar: 'CURSOR_API_KEY', hint: '' },\n xai: { label: 'xAI / Grok', cli: 'grok', selector: 'grok', envVar: 'XAI_API_KEY', hint: 'xai-' },\n meta: { label: 'Meta Model API / Muse Spark', cli: 'codex', selector: 'meta', envVar: 'MODEL_API_KEY', hint: '' },\n};\n\n/**\n * Parse `vo-mcp set-key` argv (the FULL process.argv; the subcommand is argv[2]).\n * Supports `--provider <p>` / `--provider=p` (default anthropic), `--status`,\n * `--clear`. Returns `{ provider, providerArg, mode }` where `provider` is the\n * normalized credential provider (null if the value is unknown) and `mode` is\n * 'set' | 'status' | 'clear'.\n */\nexport function parseSetKeyArgs(argv = []) {\n const rest = argv.slice(3);\n let providerArg = 'anthropic';\n let mode = 'set';\n for (let i = 0; i < rest.length; i += 1) {\n const a = rest[i];\n if (a === '--status') mode = 'status';\n else if (a === '--clear') mode = 'clear';\n else if (a === '--provider') {\n providerArg = rest[i + 1] || '';\n i += 1; // consume the value token\n } else if (a.startsWith('--provider=')) {\n providerArg = a.slice('--provider='.length);\n }\n }\n return { provider: resolveProvider(providerArg), providerArg, mode };\n}\n", "#!/usr/bin/env node\n/**\n * `vo-mcp set-key` \u2014 BYO Phase-B M4 + multi-agent. Stores the friend's AI API key\n * in the OS keychain (Windows Credential Manager / macOS Keychain / libsecret) so\n * the runner can authenticate its CLI agent without an env var. The key lives ONLY\n * on the friend's machine \u2014 it never travels to Algosuite.\n *\n * vo-mcp set-key store the Anthropic (Claude) key [default]\n * vo-mcp set-key --provider codex store the OpenAI/Codex key\n * vo-mcp set-key --provider cursor store the Cursor key\n * vo-mcp set-key --provider grok store the xAI/Grok key\n * vo-mcp set-key --provider meta store the Meta Model API key\n * vo-mcp set-key --status report whether the provider's key is stored\n * vo-mcp set-key --clear remove the provider's stored key\n *\n * The key may be piped (`echo $KEY | vo-mcp set-key --provider codex`) or typed at\n * the hidden prompt. .mjs (like runner-cli.mjs) because it imports the runner-tree\n * key store, outside tsconfig rootDir \u2014 tsc skips it; esbuild inlines it at bundle.\n */\nimport {\n setAgentKey,\n hasAgentKey,\n clearAgentKey,\n} from '../../../scripts/virtual-office/code-runner/agent-key-store.mjs';\nimport { PROVIDERS, parseSetKeyArgs } from './set-key-args.mjs';\n\nconst log = (m) => console.error(m);\nconst mask = (k) => (k && k.length > 8 ? `\u2026${k.slice(-4)}` : '****');\n\n/** Read a secret from a TTY without echoing; falls back to piped stdin. */\nfunction readSecret(promptText) {\n return new Promise((resolve) => {\n if (!process.stdin.isTTY) {\n // Piped: `echo $KEY | vo-mcp set-key` \u2014 read everything, take the first line.\n let buf = '';\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', (d) => (buf += d));\n process.stdin.on('end', () => resolve(buf.split(/\\r?\\n/)[0].trim()));\n return;\n }\n // Interactive: print the prompt, then mute the echoed characters.\n process.stdout.write(promptText);\n const stdin = process.stdin;\n stdin.setRawMode?.(true);\n stdin.resume();\n stdin.setEncoding('utf8');\n let val = '';\n const onData = (ch) => {\n if (ch === '\\r' || ch === '\\n' || ch === '\u0004') {\n stdin.setRawMode?.(false);\n stdin.pause();\n stdin.removeListener('data', onData);\n process.stdout.write('\\n');\n resolve(val.trim());\n } else if (ch === '\u0003') {\n // Ctrl-C\n process.stdout.write('\\n');\n process.exit(130);\n } else if (ch === '\u007F' || ch === '\\b') {\n val = val.slice(0, -1);\n } else {\n val += ch;\n }\n };\n stdin.on('data', onData);\n });\n}\n\nasync function main() {\n const { provider, providerArg, mode } = parseSetKeyArgs(process.argv);\n if (!provider) {\n log(`Unknown --provider \"${providerArg}\". Use one of: claude, codex, cursor, grok, meta.`);\n process.exitCode = 1;\n return;\n }\n const info = PROVIDERS[provider];\n\n if (mode === 'status') {\n log(\n hasAgentKey(provider)\n ? `\u2713 A ${info.label} API key is stored in your OS keychain.`\n : `No ${info.label} API key stored. Run \\`vo-mcp set-key${provider === 'anthropic' ? '' : ` --provider ${info.selector}`}\\` to add one.`,\n );\n return;\n }\n\n if (mode === 'clear') {\n log(\n clearAgentKey(provider)\n ? `\u2713 Removed the stored ${info.label} API key.`\n : `No stored ${info.label} key to remove (or keychain unavailable).`,\n );\n return;\n }\n\n log(`Paste your ${info.label} API key (for the \\`${info.cli}\\` agent). It is stored ONLY in`);\n log(\"your computer's keychain \u2014 it never leaves this machine and Algosuite never sees it.\");\n const key = await readSecret(`${info.label} API key: `);\n if (!key) {\n log('No key entered \u2014 nothing stored.');\n process.exitCode = 1;\n return;\n }\n // A gentle format hint only \u2014 key formats change and Cursor's isn't `sk-`, so\n // never hard-reject a non-matching key (that would block a valid credential).\n if (info.hint && !key.startsWith(info.hint)) {\n log(`Heads up: ${info.label} keys usually start with \"${info.hint}\". Storing it anyway.`);\n }\n if (setAgentKey(provider, key)) {\n log(`\u2713 Stored your ${info.label} API key (${mask(key)}) in the OS keychain. The runner will use it automatically.`);\n } else {\n log(`Could not access the OS keychain on this machine. As a fallback, set ${info.envVar} in your environment before running \\`vo-mcp runner\\`.`);\n process.exitCode = 1;\n }\n}\n\nmain().catch((e) => {\n log(`set-key failed: ${e && e.message ? e.message : e}`);\n process.exitCode = 1;\n});\n"],
5
+ "mappings": "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",
6
6
  "names": ["require", "require"]
7
7
  }
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __cr } from 'module'; const require = __cr(import.meta.url);
3
+
4
+ // src/update.ts
5
+ import { spawnSync } from "node:child_process";
6
+ import { createRequire } from "node:module";
7
+ var DEFAULT_UPDATE_SPEC = "@algosuite/vo-mcp@beta";
8
+ function packageVersion() {
9
+ try {
10
+ return createRequire(import.meta.url)("../package.json").version || "unknown";
11
+ } catch {
12
+ return "unknown";
13
+ }
14
+ }
15
+ function npmCommand(platform) {
16
+ return platform === "win32" ? "npm.cmd" : "npm";
17
+ }
18
+ function buildUpdateCommand(packageSpec2 = DEFAULT_UPDATE_SPEC, platform = process.platform) {
19
+ return {
20
+ command: npmCommand(platform),
21
+ args: ["install", "-g", packageSpec2]
22
+ };
23
+ }
24
+ function updateVoMcp(opts = {}) {
25
+ const log = opts.log ?? ((message) => console.error(message));
26
+ const platform = opts.platform ?? process.platform;
27
+ const packageSpec2 = opts.packageSpec?.trim() || DEFAULT_UPDATE_SPEC;
28
+ const { command, args } = buildUpdateCommand(packageSpec2, platform);
29
+ log("=== vo-mcp updater ===");
30
+ log(`Current installed package version: ${packageVersion()}`);
31
+ log(`Updating MCP + runner with: ${command} ${args.join(" ")}`);
32
+ if (opts.dryRun) {
33
+ log("Dry run only - no package changes made.");
34
+ return { ok: true, status: 0, command, args, skipped: true };
35
+ }
36
+ const run = opts.spawn ?? spawnSync;
37
+ const result2 = run(command, args, {
38
+ stdio: "inherit",
39
+ shell: platform === "win32",
40
+ env: opts.env ?? process.env
41
+ });
42
+ const status = typeof result2.status === "number" ? result2.status : 1;
43
+ if (status !== 0) {
44
+ log(`Update failed with exit code ${status}.`);
45
+ return { ok: false, status, command, args, skipped: false };
46
+ }
47
+ log("\nUpdate complete.");
48
+ log("Restart any running `vo-mcp runner` process so it loads the new bundle.");
49
+ log("If Claude Desktop / Claude Code was open, restart it to reload the MCP server.");
50
+ return { ok: true, status: 0, command, args, skipped: false };
51
+ }
52
+
53
+ // src/update-cli.ts
54
+ function argValue(name) {
55
+ const index = process.argv.indexOf(name);
56
+ return index >= 0 ? process.argv[index + 1] : void 0;
57
+ }
58
+ var packageSpec = argValue("--package") || DEFAULT_UPDATE_SPEC;
59
+ var dryRun = process.argv.includes("--dry-run");
60
+ var result = updateVoMcp({ packageSpec, dryRun });
61
+ process.exit(result.ok ? 0 : result.status || 1);
62
+ //# sourceMappingURL=update-cli.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/update.ts", "../src/update-cli.ts"],
4
+ "sourcesContent": ["/**\n * `vo-mcp update` - product-level updater for the MCP + runner package.\n *\n * Users should not have to uninstall/reinstall to pick up a new bundled runner.\n * The package is installed globally, so updating the single npm package updates\n * both `vo-mcp` and `vo-mcp runner` together.\n */\nimport { spawnSync } from 'node:child_process';\nimport { createRequire } from 'node:module';\n\nexport const DEFAULT_UPDATE_SPEC = '@algosuite/vo-mcp@beta';\n\nexport interface UpdateOptions {\n readonly packageSpec?: string;\n readonly dryRun?: boolean;\n readonly log?: (message: string) => void;\n readonly spawn?: typeof spawnSync;\n readonly platform?: NodeJS.Platform;\n readonly env?: NodeJS.ProcessEnv;\n}\n\nexport interface UpdateResult {\n readonly ok: boolean;\n readonly status: number;\n readonly command: string;\n readonly args: readonly string[];\n readonly skipped: boolean;\n}\n\nfunction packageVersion(): string {\n try {\n return createRequire(import.meta.url)('../package.json').version || 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\nfunction npmCommand(platform: NodeJS.Platform): string {\n return platform === 'win32' ? 'npm.cmd' : 'npm';\n}\n\nexport function buildUpdateCommand(\n packageSpec = DEFAULT_UPDATE_SPEC,\n platform: NodeJS.Platform = process.platform,\n): { command: string; args: string[] } {\n return {\n command: npmCommand(platform),\n args: ['install', '-g', packageSpec],\n };\n}\n\nexport function updateVoMcp(opts: UpdateOptions = {}): UpdateResult {\n const log = opts.log ?? ((message: string) => console.error(message));\n const platform = opts.platform ?? process.platform;\n const packageSpec = opts.packageSpec?.trim() || DEFAULT_UPDATE_SPEC;\n const { command, args } = buildUpdateCommand(packageSpec, platform);\n\n log('=== vo-mcp updater ===');\n log(`Current installed package version: ${packageVersion()}`);\n log(`Updating MCP + runner with: ${command} ${args.join(' ')}`);\n\n if (opts.dryRun) {\n log('Dry run only - no package changes made.');\n return { ok: true, status: 0, command, args, skipped: true };\n }\n\n const run = opts.spawn ?? spawnSync;\n const result = run(command, args, {\n stdio: 'inherit',\n shell: platform === 'win32',\n env: opts.env ?? process.env,\n });\n const status = typeof result.status === 'number' ? result.status : 1;\n if (status !== 0) {\n log(`Update failed with exit code ${status}.`);\n return { ok: false, status, command, args, skipped: false };\n }\n\n log('\\nUpdate complete.');\n log('Restart any running `vo-mcp runner` process so it loads the new bundle.');\n log('If Claude Desktop / Claude Code was open, restart it to reload the MCP server.');\n return { ok: true, status: 0, command, args, skipped: false };\n}\n", "#!/usr/bin/env node\n/**\n * `vo-mcp update` CLI entry point.\n */\nimport { updateVoMcp, DEFAULT_UPDATE_SPEC } from './update.js';\n\nfunction argValue(name: string): string | undefined {\n const index = process.argv.indexOf(name);\n return index >= 0 ? process.argv[index + 1] : undefined;\n}\n\nconst packageSpec = argValue('--package') || DEFAULT_UPDATE_SPEC;\nconst dryRun = process.argv.includes('--dry-run');\nconst result = updateVoMcp({ packageSpec, dryRun });\n\nprocess.exit(result.ok ? 0 : result.status || 1);\n"],
5
+ "mappings": ";;;;AAOA,SAAS,iBAAiB;AAC1B,SAAS,qBAAqB;AAEvB,IAAM,sBAAsB;AAmBnC,SAAS,iBAAyB;AAChC,MAAI;AACF,WAAO,cAAc,YAAY,GAAG,EAAE,iBAAiB,EAAE,WAAW;AAAA,EACtE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,WAAW,UAAmC;AACrD,SAAO,aAAa,UAAU,YAAY;AAC5C;AAEO,SAAS,mBACdA,eAAc,qBACd,WAA4B,QAAQ,UACC;AACrC,SAAO;AAAA,IACL,SAAS,WAAW,QAAQ;AAAA,IAC5B,MAAM,CAAC,WAAW,MAAMA,YAAW;AAAA,EACrC;AACF;AAEO,SAAS,YAAY,OAAsB,CAAC,GAAiB;AAClE,QAAM,MAAM,KAAK,QAAQ,CAAC,YAAoB,QAAQ,MAAM,OAAO;AACnE,QAAM,WAAW,KAAK,YAAY,QAAQ;AAC1C,QAAMA,eAAc,KAAK,aAAa,KAAK,KAAK;AAChD,QAAM,EAAE,SAAS,KAAK,IAAI,mBAAmBA,cAAa,QAAQ;AAElE,MAAI,wBAAwB;AAC5B,MAAI,sCAAsC,eAAe,CAAC,EAAE;AAC5D,MAAI,+BAA+B,OAAO,IAAI,KAAK,KAAK,GAAG,CAAC,EAAE;AAE9D,MAAI,KAAK,QAAQ;AACf,QAAI,yCAAyC;AAC7C,WAAO,EAAE,IAAI,MAAM,QAAQ,GAAG,SAAS,MAAM,SAAS,KAAK;AAAA,EAC7D;AAEA,QAAM,MAAM,KAAK,SAAS;AAC1B,QAAMC,UAAS,IAAI,SAAS,MAAM;AAAA,IAChC,OAAO;AAAA,IACP,OAAO,aAAa;AAAA,IACpB,KAAK,KAAK,OAAO,QAAQ;AAAA,EAC3B,CAAC;AACD,QAAM,SAAS,OAAOA,QAAO,WAAW,WAAWA,QAAO,SAAS;AACnE,MAAI,WAAW,GAAG;AAChB,QAAI,gCAAgC,MAAM,GAAG;AAC7C,WAAO,EAAE,IAAI,OAAO,QAAQ,SAAS,MAAM,SAAS,MAAM;AAAA,EAC5D;AAEA,MAAI,oBAAoB;AACxB,MAAI,yEAAyE;AAC7E,MAAI,gFAAgF;AACpF,SAAO,EAAE,IAAI,MAAM,QAAQ,GAAG,SAAS,MAAM,SAAS,MAAM;AAC9D;;;AC5EA,SAAS,SAAS,MAAkC;AAClD,QAAM,QAAQ,QAAQ,KAAK,QAAQ,IAAI;AACvC,SAAO,SAAS,IAAI,QAAQ,KAAK,QAAQ,CAAC,IAAI;AAChD;AAEA,IAAM,cAAc,SAAS,WAAW,KAAK;AAC7C,IAAM,SAAS,QAAQ,KAAK,SAAS,WAAW;AAChD,IAAM,SAAS,YAAY,EAAE,aAAa,OAAO,CAAC;AAElD,QAAQ,KAAK,OAAO,KAAK,IAAI,OAAO,UAAU,CAAC;",
6
+ "names": ["packageSpec", "result"]
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algosuite/vo-mcp",
3
- "version": "0.2.0-beta.2",
3
+ "version": "0.2.0-beta.7",
4
4
  "description": "Virtual Office MCP server — open protocol surface for the VO consensus / ratchet tool family. Stdio transport, cross-vendor MCP client compatible (Claude Code, Claude Desktop, Cursor, Continue).",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",