@schoolai/shipyard 3.7.0 → 3.8.0-rc.20260529.0

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.
Files changed (104) hide show
  1. package/dist/{auth-SS7LV5XK.js → auth-EXHO3AG5.js} +4 -4
  2. package/dist/capability-detector-worker.js +142 -0
  3. package/dist/capability-detector-worker.js.map +1 -0
  4. package/dist/{chunk-DKMDBOFU.js → chunk-2CNIEBKO.js} +21 -11
  5. package/dist/chunk-2CNIEBKO.js.map +1 -0
  6. package/dist/chunk-4T2OQAVL.js +51 -0
  7. package/dist/chunk-4T2OQAVL.js.map +1 -0
  8. package/dist/chunk-5ER6ZHA2.js +46 -0
  9. package/dist/chunk-5ER6ZHA2.js.map +1 -0
  10. package/dist/chunk-7LSEE26O.js +24227 -0
  11. package/dist/chunk-7LSEE26O.js.map +1 -0
  12. package/dist/{chunk-7AHRFPAL.js → chunk-7YOU7MBN.js} +183 -17
  13. package/dist/chunk-7YOU7MBN.js.map +1 -0
  14. package/dist/chunk-CMGJGK6R.js +382 -0
  15. package/dist/chunk-CMGJGK6R.js.map +1 -0
  16. package/dist/{chunk-VPMN47TL.js → chunk-CNR7O5YH.js} +1 -2
  17. package/dist/{chunk-2J3WSIAF.js → chunk-EF2DAODF.js} +18 -3
  18. package/dist/chunk-EF2DAODF.js.map +1 -0
  19. package/dist/chunk-HQ43PHOH.js +1203 -0
  20. package/dist/chunk-HQ43PHOH.js.map +1 -0
  21. package/dist/chunk-KITSAHTX.js +134 -0
  22. package/dist/chunk-KITSAHTX.js.map +1 -0
  23. package/dist/chunk-LESHN5J5.js +6898 -0
  24. package/dist/chunk-LESHN5J5.js.map +1 -0
  25. package/dist/{chunk-LW2MS4T5.js → chunk-LMJFHKRD.js} +15 -12
  26. package/dist/chunk-LMJFHKRD.js.map +1 -0
  27. package/dist/{chunk-SNYEQHUK.js → chunk-NACJENDW.js} +14 -21
  28. package/dist/chunk-NACJENDW.js.map +1 -0
  29. package/dist/{chunk-IISLTKYY.js → chunk-TU63KZFW.js} +2 -2
  30. package/dist/chunk-TX6DK4PK.js +186 -0
  31. package/dist/chunk-TX6DK4PK.js.map +1 -0
  32. package/dist/chunk-UQVXWOPT.js +48 -0
  33. package/dist/chunk-UQVXWOPT.js.map +1 -0
  34. package/dist/{chunk-3MNPDCO5.js → chunk-WBB4XHLH.js} +139 -140
  35. package/dist/chunk-WBB4XHLH.js.map +1 -0
  36. package/dist/chunk-X3MULCV5.js +11 -0
  37. package/dist/chunk-X3MULCV5.js.map +1 -0
  38. package/dist/chunk-YZ3Z3ZYI.js +787 -0
  39. package/dist/chunk-YZ3Z3ZYI.js.map +1 -0
  40. package/dist/{chunk-2UN5AR7V.js → chunk-ZAOPND5G.js} +2 -2
  41. package/dist/chunk-ZFKJAYAN.js +542 -0
  42. package/dist/chunk-ZFKJAYAN.js.map +1 -0
  43. package/dist/cursor-hook-shim.js +316 -0
  44. package/dist/cursor-hook-shim.js.map +1 -0
  45. package/dist/cursor-runner.js +358 -0
  46. package/dist/cursor-runner.js.map +1 -0
  47. package/dist/electron-utility.js +111 -0
  48. package/dist/electron-utility.js.map +1 -0
  49. package/dist/git-pool-V73Q53NX.js +18 -0
  50. package/dist/{git-repo-VRT57DGC.js → git-repo-TN3VZXQV.js} +9 -6
  51. package/dist/index.js +12 -12
  52. package/dist/index.js.map +1 -1
  53. package/dist/{logger-GQCSLSZH.js → logger-QHPTO22N.js} +4 -4
  54. package/dist/login-Q7SZI7JJ.js +20 -0
  55. package/dist/{logout-VUNCW5B2.js → logout-O4AVMO5S.js} +6 -6
  56. package/dist/mcp-servers-F64M5T4I.js +24 -0
  57. package/dist/{roi-Y3MX5UW4.js → roi-EYDLPOCS.js} +5 -5
  58. package/dist/rss-worker.js +159 -0
  59. package/dist/rss-worker.js.map +1 -0
  60. package/dist/{serve-O53FNK64.js → serve-6A7RJWEF.js} +89862 -102999
  61. package/dist/{serve-O53FNK64.js.map → serve-6A7RJWEF.js.map} +1 -1
  62. package/dist/skills-ZHEPSBHW.js +11 -0
  63. package/dist/{start-IDFDHRD6.js → start-YGYYIK53.js} +229 -27
  64. package/dist/start-YGYYIK53.js.map +1 -0
  65. package/dist/vault-crypto-BKDOA65F.js +13 -0
  66. package/dist/vault-crypto-BKDOA65F.js.map +1 -0
  67. package/dist/worker.js +6 -3
  68. package/dist/worker.js.map +1 -1
  69. package/package.json +17 -10
  70. package/dist/chunk-2J3WSIAF.js.map +0 -1
  71. package/dist/chunk-3MNPDCO5.js.map +0 -1
  72. package/dist/chunk-66OBOZ3X.js +0 -79
  73. package/dist/chunk-66OBOZ3X.js.map +0 -1
  74. package/dist/chunk-7AHRFPAL.js.map +0 -1
  75. package/dist/chunk-DKMDBOFU.js.map +0 -1
  76. package/dist/chunk-L2WQMPWS.js +0 -666
  77. package/dist/chunk-L2WQMPWS.js.map +0 -1
  78. package/dist/chunk-LW2MS4T5.js.map +0 -1
  79. package/dist/chunk-PI77CUEP.js +0 -49
  80. package/dist/chunk-PI77CUEP.js.map +0 -1
  81. package/dist/chunk-RXI4637N.js +0 -395
  82. package/dist/chunk-RXI4637N.js.map +0 -1
  83. package/dist/chunk-SNYEQHUK.js.map +0 -1
  84. package/dist/chunk-VBPHGPBR.js +0 -126
  85. package/dist/chunk-VBPHGPBR.js.map +0 -1
  86. package/dist/index.d.ts +0 -2
  87. package/dist/login-L4BBPUYO.js +0 -20
  88. package/dist/mcp-servers-MXS5VAWI.js +0 -18
  89. package/dist/shell-V36EX2IJ.js +0 -27
  90. package/dist/skills-GPGRNV4R.js +0 -9
  91. package/dist/start-IDFDHRD6.js.map +0 -1
  92. package/dist/worker.d.ts +0 -49
  93. /package/dist/{auth-SS7LV5XK.js.map → auth-EXHO3AG5.js.map} +0 -0
  94. /package/dist/{chunk-VPMN47TL.js.map → chunk-CNR7O5YH.js.map} +0 -0
  95. /package/dist/{chunk-IISLTKYY.js.map → chunk-TU63KZFW.js.map} +0 -0
  96. /package/dist/{chunk-2UN5AR7V.js.map → chunk-ZAOPND5G.js.map} +0 -0
  97. /package/dist/{git-repo-VRT57DGC.js.map → git-pool-V73Q53NX.js.map} +0 -0
  98. /package/dist/{logger-GQCSLSZH.js.map → git-repo-TN3VZXQV.js.map} +0 -0
  99. /package/dist/{login-L4BBPUYO.js.map → logger-QHPTO22N.js.map} +0 -0
  100. /package/dist/{mcp-servers-MXS5VAWI.js.map → login-Q7SZI7JJ.js.map} +0 -0
  101. /package/dist/{logout-VUNCW5B2.js.map → logout-O4AVMO5S.js.map} +0 -0
  102. /package/dist/{shell-V36EX2IJ.js.map → mcp-servers-F64M5T4I.js.map} +0 -0
  103. /package/dist/{roi-Y3MX5UW4.js.map → roi-EYDLPOCS.js.map} +0 -0
  104. /package/dist/{skills-GPGRNV4R.js.map → skills-ZHEPSBHW.js.map} +0 -0
package/dist/index.js CHANGED
@@ -4,11 +4,11 @@ import {
4
4
  } from "./chunk-7H34LI75.js";
5
5
  import {
6
6
  logger
7
- } from "./chunk-2UN5AR7V.js";
7
+ } from "./chunk-ZAOPND5G.js";
8
8
  import {
9
9
  validateEnv
10
- } from "./chunk-PI77CUEP.js";
11
- import "./chunk-VPMN47TL.js";
10
+ } from "./chunk-KITSAHTX.js";
11
+ import "./chunk-CNR7O5YH.js";
12
12
  import "./chunk-2H7UOFLK.js";
13
13
 
14
14
  // src/index.ts
@@ -58,11 +58,11 @@ function parseCliArgs() {
58
58
  " -h, --help Show this help",
59
59
  "",
60
60
  "Authentication:",
61
- " Run `claude auth login` to authenticate with Anthropic (primary method).",
62
- " Alternatively, set ANTHROPIC_API_KEY for CI/headless environments.",
61
+ " Run `claude auth login` or `codex auth` to authenticate your agent.",
62
+ " For CI/headless environments, set the agent-specific API key env var.",
63
63
  "",
64
64
  "Environment:",
65
- " ANTHROPIC_API_KEY API key for Claude (optional, overrides OAuth)",
65
+ " ANTHROPIC_API_KEY API key for Claude Code (optional, overrides OAuth)",
66
66
  " SHIPYARD_DEV Set to 1 for dev mode (uses ~/.shipyard-dev/)",
67
67
  " SHIPYARD_WEB_URL Override browser URL for auto-open",
68
68
  " LOG_LEVEL Log level: debug, info, warn, error (default: info)",
@@ -81,7 +81,7 @@ function parseCliArgs() {
81
81
  }
82
82
  async function loadAuthFromConfig(env) {
83
83
  if (env.SHIPYARD_USER_TOKEN) return;
84
- const { loadAuthToken } = await import("./auth-SS7LV5XK.js");
84
+ const { loadAuthToken } = await import("./auth-EXHO3AG5.js");
85
85
  const auth = await loadAuthToken();
86
86
  if (auth.status === "ok") {
87
87
  env.SHIPYARD_USER_TOKEN = auth.token;
@@ -101,23 +101,23 @@ async function loadAuthFromConfig(env) {
101
101
  async function handleSubcommand() {
102
102
  const subcommand = process.argv[2];
103
103
  if (subcommand === "login") {
104
- const { loginCommand } = await import("./login-L4BBPUYO.js");
104
+ const { loginCommand } = await import("./login-Q7SZI7JJ.js");
105
105
  const hasCheck = process.argv.includes("--check");
106
106
  await loginCommand({ check: hasCheck });
107
107
  return true;
108
108
  }
109
109
  if (subcommand === "logout") {
110
- const { logoutCommand } = await import("./logout-VUNCW5B2.js");
110
+ const { logoutCommand } = await import("./logout-O4AVMO5S.js");
111
111
  await logoutCommand();
112
112
  return true;
113
113
  }
114
114
  if (subcommand === "start") {
115
- const { startCommand } = await import("./start-IDFDHRD6.js");
115
+ const { startCommand } = await import("./start-YGYYIK53.js");
116
116
  await startCommand();
117
117
  return true;
118
118
  }
119
119
  if (subcommand === "roi") {
120
- const { roiCommand } = await import("./roi-Y3MX5UW4.js");
120
+ const { roiCommand } = await import("./roi-EYDLPOCS.js");
121
121
  await roiCommand();
122
122
  return true;
123
123
  }
@@ -129,7 +129,7 @@ async function main() {
129
129
  const args = parseCliArgs();
130
130
  if (args.serve) {
131
131
  await loadAuthFromConfig(env);
132
- const { serve } = await import("./serve-O53FNK64.js");
132
+ const { serve } = await import("./serve-6A7RJWEF.js");
133
133
  return serve({ isDev: env.SHIPYARD_DEV });
134
134
  }
135
135
  logger.error("Use `shipyard start` to run the daemon. Use --help for usage.");
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { parseArgs } from 'node:util';\nimport { type Env, validateEnv } from './shared/env.js';\nimport { logger } from './shared/logger.js';\nimport { installWasmPanicBuffer } from './shared/wasm-panic-buffer.js';\n\n/**\n * Node defaults to `Error.stackTraceLimit = 10`. Loro wasm panics produce\n * exactly 10-frame wasm chains, which means any JS frames *above* the wasm\n * boundary are silently truncated and never reach `panic-watch.log`.\n * Raising this lets the JS-stack capture (issue #2354) actually see\n * caller-side frames so we can distinguish JS-triggered re-entry from\n * pure-wasm cascade poison.\n */\nError.stackTraceLimit = 50;\n\n/**\n * Install the `console.error` ring buffer BEFORE any `loro-crdt` import\n * resolves. The vendored Loro fork has `console_error_panic_hook` already\n * installed in Rust, which writes the actual panic site (file:line) to\n * `console.error` before the wasm trap fires. Without this Node-side\n * capture, that message is lost and we are stuck guessing among the\n * multiple Rust panic classes that all surface as bare `unreachable`\n * (Invariant #14). The dynamic `await import('./services/serve.js')`\n * below is the first thing that pulls in `@loro-extended/repo` which in\n * turn loads the wasm module — so installing here is well-ordered.\n */\ninstallWasmPanicBuffer();\n\nfunction getVersion(): string {\n try {\n const thisFile = fileURLToPath(import.meta.url);\n const pkgPath = resolve(thisFile, '../../package.json');\n const pkg: { version?: string } = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n return pkg.version ?? 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\ninterface CliArgs {\n serve?: boolean;\n}\n\nfunction parseCliArgs(): CliArgs {\n const { values } = parseArgs({\n options: {\n serve: { type: 'boolean', short: 's' },\n version: { type: 'boolean', short: 'v' },\n help: { type: 'boolean', short: 'h' },\n },\n strict: true,\n });\n\n if (values.version) {\n process.stdout.write(`${getVersion()}\\n`);\n process.exit(0);\n }\n\n if (values.help) {\n logger.info(\n [\n `shipyard v${getVersion()} - Ship together with AI — locally, visibly, confidently`,\n '',\n 'Usage:',\n ' shipyard start [--code CODE] Start daemon (authenticates if needed, opens browser)',\n ' shipyard login Authenticate with Shipyard',\n ' shipyard login --check Check current auth status',\n ' shipyard logout Clear stored credentials',\n '',\n 'Options:',\n ' -v, --version Show version',\n ' -h, --help Show this help',\n '',\n 'Authentication:',\n ' Run `claude auth login` to authenticate with Anthropic (primary method).',\n ' Alternatively, set ANTHROPIC_API_KEY for CI/headless environments.',\n '',\n 'Environment:',\n ' ANTHROPIC_API_KEY API key for Claude (optional, overrides OAuth)',\n ' SHIPYARD_DEV Set to 1 for dev mode (uses ~/.shipyard-dev/)',\n ' SHIPYARD_WEB_URL Override browser URL for auto-open',\n ' LOG_LEVEL Log level: debug, info, warn, error (default: info)',\n ' SHIPYARD_SIGNALING_URL Signaling server WebSocket URL (optional)',\n ' SHIPYARD_USER_TOKEN JWT for signaling auth (optional)',\n ' SHIPYARD_USER_ID User ID for signaling path (optional, from login)',\n ' SHIPYARD_MACHINE_ID Machine identifier (default: os.hostname())',\n ' SHIPYARD_MACHINE_NAME Human-readable machine name (default: os.hostname())',\n ].join('\\n')\n );\n process.exit(0);\n }\n\n return {\n serve: values.serve,\n };\n}\n\nasync function loadAuthFromConfig(env: Env): Promise<void> {\n if (env.SHIPYARD_USER_TOKEN) return;\n\n const { loadAuthToken } = await import('./services/bootstrap/auth.js');\n const auth = await loadAuthToken();\n\n if (auth.status === 'ok') {\n env.SHIPYARD_USER_TOKEN = auth.token;\n env.SHIPYARD_USER_ID = auth.userId;\n env.SHIPYARD_USER_DISPLAY_NAME = auth.displayName;\n if (auth.signalingUrl) {\n env.SHIPYARD_SIGNALING_URL = auth.signalingUrl;\n }\n return;\n }\n\n if (auth.status === 'expired') {\n logger.warn('Auth token expired. Run `shipyard login` to re-authenticate.');\n return;\n }\n\n logger.warn('No auth token found. Run `shipyard login` to authenticate.');\n}\n\nasync function handleSubcommand(): Promise<boolean> {\n const subcommand = process.argv[2];\n\n if (subcommand === 'login') {\n const { loginCommand } = await import('./shared/commands/login.js');\n const hasCheck = process.argv.includes('--check');\n await loginCommand({ check: hasCheck });\n return true;\n }\n\n if (subcommand === 'logout') {\n const { logoutCommand } = await import('./shared/commands/logout.js');\n await logoutCommand();\n return true;\n }\n\n if (subcommand === 'start') {\n const { startCommand } = await import('./shared/commands/start.js');\n await startCommand();\n return true;\n }\n\n if (subcommand === 'roi') {\n const { roiCommand } = await import('./shared/commands/roi.js');\n await roiCommand();\n return true;\n }\n\n return false;\n}\n\nasync function main(): Promise<void> {\n if (await handleSubcommand()) return;\n\n const env = validateEnv();\n const args = parseCliArgs();\n\n if (args.serve) {\n await loadAuthFromConfig(env);\n const { serve } = await import('./services/serve.js');\n return serve({ isDev: env.SHIPYARD_DEV });\n }\n\n logger.error('Use `shipyard start` to run the daemon. Use --help for usage.');\n process.exit(1);\n}\n\nmain().catch((error: unknown) => {\n const errMsg = error instanceof Error ? error.message : String(error);\n const errStack = error instanceof Error ? error.stack : undefined;\n logger.error({ err: errMsg, stack: errStack }, 'Fatal error');\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { parseArgs } from 'node:util';\nimport { type Env, validateEnv } from './shared/env.js';\nimport { logger } from './shared/logger.js';\nimport { installWasmPanicBuffer } from './shared/wasm-panic-buffer.js';\n\n/**\n * Node defaults to `Error.stackTraceLimit = 10`. Loro wasm panics produce\n * exactly 10-frame wasm chains, which means any JS frames *above* the wasm\n * boundary are silently truncated and never reach `panic-watch.log`.\n * Raising this lets the JS-stack capture (issue #2354) actually see\n * caller-side frames so we can distinguish JS-triggered re-entry from\n * pure-wasm cascade poison.\n */\nError.stackTraceLimit = 50;\n\n/**\n * Install the `console.error` ring buffer BEFORE any `loro-crdt` import\n * resolves. The vendored Loro fork has `console_error_panic_hook` already\n * installed in Rust, which writes the actual panic site (file:line) to\n * `console.error` before the wasm trap fires. Without this Node-side\n * capture, that message is lost and we are stuck guessing among the\n * multiple Rust panic classes that all surface as bare `unreachable`\n * (Invariant #14). The dynamic `await import('./services/serve.js')`\n * below is the first thing that pulls in `@loro-extended/repo` which in\n * turn loads the wasm module — so installing here is well-ordered.\n */\ninstallWasmPanicBuffer();\n\nfunction getVersion(): string {\n try {\n const thisFile = fileURLToPath(import.meta.url);\n const pkgPath = resolve(thisFile, '../../package.json');\n const pkg: { version?: string } = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n return pkg.version ?? 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\ninterface CliArgs {\n serve?: boolean;\n}\n\nfunction parseCliArgs(): CliArgs {\n const { values } = parseArgs({\n options: {\n serve: { type: 'boolean', short: 's' },\n version: { type: 'boolean', short: 'v' },\n help: { type: 'boolean', short: 'h' },\n },\n strict: true,\n });\n\n if (values.version) {\n process.stdout.write(`${getVersion()}\\n`);\n process.exit(0);\n }\n\n if (values.help) {\n logger.info(\n [\n `shipyard v${getVersion()} - Ship together with AI — locally, visibly, confidently`,\n '',\n 'Usage:',\n ' shipyard start [--code CODE] Start daemon (authenticates if needed, opens browser)',\n ' shipyard login Authenticate with Shipyard',\n ' shipyard login --check Check current auth status',\n ' shipyard logout Clear stored credentials',\n '',\n 'Options:',\n ' -v, --version Show version',\n ' -h, --help Show this help',\n '',\n 'Authentication:',\n ' Run `claude auth login` or `codex auth` to authenticate your agent.',\n ' For CI/headless environments, set the agent-specific API key env var.',\n '',\n 'Environment:',\n ' ANTHROPIC_API_KEY API key for Claude Code (optional, overrides OAuth)',\n ' SHIPYARD_DEV Set to 1 for dev mode (uses ~/.shipyard-dev/)',\n ' SHIPYARD_WEB_URL Override browser URL for auto-open',\n ' LOG_LEVEL Log level: debug, info, warn, error (default: info)',\n ' SHIPYARD_SIGNALING_URL Signaling server WebSocket URL (optional)',\n ' SHIPYARD_USER_TOKEN JWT for signaling auth (optional)',\n ' SHIPYARD_USER_ID User ID for signaling path (optional, from login)',\n ' SHIPYARD_MACHINE_ID Machine identifier (default: os.hostname())',\n ' SHIPYARD_MACHINE_NAME Human-readable machine name (default: os.hostname())',\n ].join('\\n')\n );\n process.exit(0);\n }\n\n return {\n serve: values.serve,\n };\n}\n\nasync function loadAuthFromConfig(env: Env): Promise<void> {\n if (env.SHIPYARD_USER_TOKEN) return;\n\n const { loadAuthToken } = await import('./services/bootstrap/auth.js');\n const auth = await loadAuthToken();\n\n if (auth.status === 'ok') {\n env.SHIPYARD_USER_TOKEN = auth.token;\n env.SHIPYARD_USER_ID = auth.userId;\n env.SHIPYARD_USER_DISPLAY_NAME = auth.displayName;\n if (auth.signalingUrl) {\n env.SHIPYARD_SIGNALING_URL = auth.signalingUrl;\n }\n return;\n }\n\n if (auth.status === 'expired') {\n logger.warn('Auth token expired. Run `shipyard login` to re-authenticate.');\n return;\n }\n\n logger.warn('No auth token found. Run `shipyard login` to authenticate.');\n}\n\nasync function handleSubcommand(): Promise<boolean> {\n const subcommand = process.argv[2];\n\n if (subcommand === 'login') {\n const { loginCommand } = await import('./shared/commands/login.js');\n const hasCheck = process.argv.includes('--check');\n await loginCommand({ check: hasCheck });\n return true;\n }\n\n if (subcommand === 'logout') {\n const { logoutCommand } = await import('./shared/commands/logout.js');\n await logoutCommand();\n return true;\n }\n\n if (subcommand === 'start') {\n const { startCommand } = await import('./shared/commands/start.js');\n await startCommand();\n return true;\n }\n\n if (subcommand === 'roi') {\n const { roiCommand } = await import('./shared/commands/roi.js');\n await roiCommand();\n return true;\n }\n\n return false;\n}\n\nasync function main(): Promise<void> {\n if (await handleSubcommand()) return;\n\n const env = validateEnv();\n const args = parseCliArgs();\n\n if (args.serve) {\n await loadAuthFromConfig(env);\n const { serve } = await import('./services/serve.js');\n return serve({ isDev: env.SHIPYARD_DEV });\n }\n\n logger.error('Use `shipyard start` to run the daemon. Use --help for usage.');\n process.exit(1);\n}\n\nmain().catch((error: unknown) => {\n const errMsg = error instanceof Error ? error.message : String(error);\n const errStack = error instanceof Error ? error.stack : undefined;\n logger.error({ err: errMsg, stack: errStack }, 'Fatal error');\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
@@ -4,9 +4,9 @@ import {
4
4
  flushLogger,
5
5
  getLogFilePath,
6
6
  logger
7
- } from "./chunk-2UN5AR7V.js";
8
- import "./chunk-PI77CUEP.js";
9
- import "./chunk-VPMN47TL.js";
7
+ } from "./chunk-ZAOPND5G.js";
8
+ import "./chunk-KITSAHTX.js";
9
+ import "./chunk-CNR7O5YH.js";
10
10
  import "./chunk-2H7UOFLK.js";
11
11
  export {
12
12
  createChildLogger,
@@ -14,4 +14,4 @@ export {
14
14
  getLogFilePath,
15
15
  logger
16
16
  };
17
- //# sourceMappingURL=logger-GQCSLSZH.js.map
17
+ //# sourceMappingURL=logger-QHPTO22N.js.map
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ensureAuthenticated,
4
+ getSignalingUrl,
5
+ loginCommand
6
+ } from "./chunk-LMJFHKRD.js";
7
+ import "./chunk-TU63KZFW.js";
8
+ import "./chunk-EF2DAODF.js";
9
+ import "./chunk-7YOU7MBN.js";
10
+ import "./chunk-EHQITHQX.js";
11
+ import "./chunk-ZAOPND5G.js";
12
+ import "./chunk-KITSAHTX.js";
13
+ import "./chunk-CNR7O5YH.js";
14
+ import "./chunk-2H7UOFLK.js";
15
+ export {
16
+ ensureAuthenticated,
17
+ getSignalingUrl,
18
+ loginCommand
19
+ };
20
+ //# sourceMappingURL=login-Q7SZI7JJ.js.map
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  print
4
- } from "./chunk-IISLTKYY.js";
5
- import "./chunk-2UN5AR7V.js";
4
+ } from "./chunk-TU63KZFW.js";
6
5
  import {
7
6
  deleteConfig,
8
7
  getConfigPath
9
- } from "./chunk-2J3WSIAF.js";
10
- import "./chunk-PI77CUEP.js";
11
- import "./chunk-VPMN47TL.js";
8
+ } from "./chunk-EF2DAODF.js";
9
+ import "./chunk-ZAOPND5G.js";
10
+ import "./chunk-KITSAHTX.js";
11
+ import "./chunk-CNR7O5YH.js";
12
12
  import "./chunk-2H7UOFLK.js";
13
13
 
14
14
  // src/shared/commands/logout.ts
@@ -23,4 +23,4 @@ async function logoutCommand() {
23
23
  export {
24
24
  logoutCommand
25
25
  };
26
- //# sourceMappingURL=logout-VUNCW5B2.js.map
26
+ //# sourceMappingURL=logout-O4AVMO5S.js.map
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ detectCodexPluginMcps,
4
+ detectMCPServers,
5
+ readMCPConfig,
6
+ redactArgs,
7
+ redactEnv,
8
+ resetMCPConfigCaches,
9
+ shouldFetchClaudeAiIntegrations
10
+ } from "./chunk-HQ43PHOH.js";
11
+ import "./chunk-ZAOPND5G.js";
12
+ import "./chunk-KITSAHTX.js";
13
+ import "./chunk-CNR7O5YH.js";
14
+ import "./chunk-2H7UOFLK.js";
15
+ export {
16
+ detectCodexPluginMcps,
17
+ detectMCPServers,
18
+ readMCPConfig,
19
+ redactArgs,
20
+ redactEnv,
21
+ resetMCPConfigCaches,
22
+ shouldFetchClaudeAiIntegrations
23
+ };
24
+ //# sourceMappingURL=mcp-servers-F64M5T4I.js.map
@@ -4,13 +4,13 @@ import {
4
4
  ROI_EVENT_TYPE,
5
5
  RoiEventSchema,
6
6
  assertNeverEvent
7
- } from "./chunk-DKMDBOFU.js";
7
+ } from "./chunk-2CNIEBKO.js";
8
8
  import "./chunk-EHQITHQX.js";
9
9
  import {
10
10
  logger
11
- } from "./chunk-2UN5AR7V.js";
12
- import "./chunk-PI77CUEP.js";
13
- import "./chunk-VPMN47TL.js";
11
+ } from "./chunk-ZAOPND5G.js";
12
+ import "./chunk-KITSAHTX.js";
13
+ import "./chunk-CNR7O5YH.js";
14
14
  import "./chunk-2H7UOFLK.js";
15
15
 
16
16
  // src/shared/commands/roi.ts
@@ -678,4 +678,4 @@ function renderReport(report, format) {
678
678
  export {
679
679
  roiCommand
680
680
  };
681
- //# sourceMappingURL=roi-Y3MX5UW4.js.map
681
+ //# sourceMappingURL=roi-EYDLPOCS.js.map
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ assertNever
4
+ } from "./chunk-X3MULCV5.js";
5
+ import "./chunk-2H7UOFLK.js";
6
+
7
+ // src/services/session/rss-worker.ts
8
+ import { execFile } from "child_process";
9
+ import { readFile } from "fs/promises";
10
+ import { platform } from "os";
11
+ import { parentPort } from "worker_threads";
12
+ function runRssWorker(deps) {
13
+ const pids = /* @__PURE__ */ new Set();
14
+ let cancelTimer = null;
15
+ let shuttingDown = false;
16
+ let inFlight = null;
17
+ let resolveDone;
18
+ const done = new Promise((resolve) => {
19
+ resolveDone = resolve;
20
+ });
21
+ function startTimerIfNeeded() {
22
+ if (cancelTimer !== null) return;
23
+ if (pids.size === 0) return;
24
+ cancelTimer = deps.setInterval(() => {
25
+ void tick();
26
+ }, deps.intervalMs);
27
+ }
28
+ async function tick() {
29
+ if (pids.size === 0) return;
30
+ if (inFlight !== null) return;
31
+ const snapshot = Array.from(pids);
32
+ const tickStart = deps.now();
33
+ const work = (async () => {
34
+ try {
35
+ const rssByPid = await deps.readBatchedRss(snapshot);
36
+ const samples = [];
37
+ for (const [pid, rssBytes] of rssByPid) {
38
+ samples.push({ pid, rssBytes });
39
+ }
40
+ const errorCount = snapshot.length - rssByPid.size;
41
+ deps.post({
42
+ type: "samples",
43
+ samples,
44
+ durationMs: Math.round(deps.now() - tickStart),
45
+ nPids: snapshot.length,
46
+ errorCount
47
+ });
48
+ } catch (err) {
49
+ deps.post({
50
+ type: "failed",
51
+ durationMs: Math.round(deps.now() - tickStart),
52
+ nPids: snapshot.length,
53
+ error: err instanceof Error ? err.message : String(err)
54
+ });
55
+ }
56
+ })();
57
+ inFlight = work;
58
+ try {
59
+ await work;
60
+ } finally {
61
+ if (inFlight === work) inFlight = null;
62
+ }
63
+ }
64
+ function dispatch(cmd) {
65
+ if (shuttingDown) return;
66
+ switch (cmd.cmd) {
67
+ case "add_pid":
68
+ pids.add(cmd.pid);
69
+ startTimerIfNeeded();
70
+ return;
71
+ case "remove_pid":
72
+ pids.delete(cmd.pid);
73
+ return;
74
+ case "shutdown":
75
+ void handleShutdown();
76
+ return;
77
+ default:
78
+ assertNever(cmd);
79
+ }
80
+ }
81
+ async function handleShutdown() {
82
+ if (shuttingDown) return;
83
+ shuttingDown = true;
84
+ if (cancelTimer !== null) {
85
+ cancelTimer();
86
+ cancelTimer = null;
87
+ }
88
+ pids.clear();
89
+ if (inFlight) {
90
+ try {
91
+ await inFlight;
92
+ } catch {
93
+ }
94
+ }
95
+ resolveDone();
96
+ }
97
+ return { dispatch, done };
98
+ }
99
+ var MEMORY_SAMPLE_INTERVAL_MS = 3e4;
100
+ async function readBatchedRss(pids) {
101
+ if (pids.length === 0) return /* @__PURE__ */ new Map();
102
+ if (platform() === "linux") {
103
+ const results = await Promise.all(
104
+ pids.map(async (pid) => [pid, await readLinuxRss(pid)])
105
+ );
106
+ const map = /* @__PURE__ */ new Map();
107
+ for (const [pid, rss] of results) {
108
+ if (rss != null) map.set(pid, rss);
109
+ }
110
+ return map;
111
+ }
112
+ return new Promise((resolve) => {
113
+ execFile("ps", ["-o", "pid=,rss=", "-p", pids.join(",")], (_err, stdout) => {
114
+ const map = /* @__PURE__ */ new Map();
115
+ for (const line of stdout.split("\n")) {
116
+ const trimmed = line.trim();
117
+ if (!trimmed) continue;
118
+ const parts = trimmed.split(/\s+/);
119
+ const pid = Number(parts[0]);
120
+ const kb = Number(parts[1]);
121
+ if (Number.isFinite(pid) && Number.isFinite(kb)) {
122
+ map.set(pid, kb * 1024);
123
+ }
124
+ }
125
+ resolve(map);
126
+ });
127
+ });
128
+ }
129
+ function readLinuxRss(pid) {
130
+ return readFile(`/proc/${pid}/status`, "utf-8").then((content) => {
131
+ const match = content.match(/VmRSS:\s+(\d+)\s+kB/);
132
+ return match ? Number(match[1]) * 1024 : null;
133
+ }).catch(() => null);
134
+ }
135
+ if (parentPort !== null) {
136
+ const port = parentPort;
137
+ const handle = runRssWorker({
138
+ readBatchedRss,
139
+ setInterval: (fn, ms) => {
140
+ const t = setInterval(fn, ms);
141
+ return () => clearInterval(t);
142
+ },
143
+ now: () => performance.now(),
144
+ post: (reply) => port.postMessage(reply),
145
+ intervalMs: MEMORY_SAMPLE_INTERVAL_MS
146
+ });
147
+ port.on("message", (cmd) => {
148
+ handle.dispatch(cmd);
149
+ });
150
+ void handle.done.then(() => {
151
+ port.close();
152
+ });
153
+ }
154
+ export {
155
+ MEMORY_SAMPLE_INTERVAL_MS,
156
+ readBatchedRss,
157
+ runRssWorker
158
+ };
159
+ //# sourceMappingURL=rss-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/session/rss-worker.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { readFile } from 'node:fs/promises';\nimport { platform } from 'node:os';\nimport { parentPort } from 'node:worker_threads';\nimport { assertNever } from '../../shared/assert-never.js';\n\n/**\n * RSS sampling worker. Owns its own setInterval and per-tick subprocess work\n * so the main event loop cannot inflate `duration_ms` or starve the cadence\n * when it stalls on `detectCapabilities`, plan compaction, or other CPU-bound\n * bursts. The worker reports the duration it actually spent doing work, not\n * the wall-clock distance from `setInterval` firing to the `.then` callback\n * being unblocked on the main thread.\n */\n\nexport type WorkerCommand =\n | { cmd: 'add_pid'; pid: number }\n | { cmd: 'remove_pid'; pid: number }\n | { cmd: 'shutdown' };\n\nexport type WorkerReply =\n | {\n type: 'samples';\n samples: Array<{ pid: number; rssBytes: number }>;\n durationMs: number;\n nPids: number;\n errorCount: number;\n }\n | {\n type: 'failed';\n durationMs: number;\n nPids: number;\n error: string;\n };\n\nexport interface RssWorkerDeps {\n readBatchedRss: (pids: readonly number[]) => Promise<Map<number, number>>;\n /** Returns a cancel function — keeps the timer handle opaque to the worker. */\n setInterval: (fn: () => void, ms: number) => () => void;\n now: () => number;\n post: (reply: WorkerReply) => void;\n intervalMs: number;\n}\n\nexport interface RssWorkerHandle {\n dispatch: (cmd: WorkerCommand) => void;\n /** Resolves when the shutdown command has finished its final tick (if any) and torn down the timer. */\n done: Promise<void>;\n}\n\nexport function runRssWorker(deps: RssWorkerDeps): RssWorkerHandle {\n const pids = new Set<number>();\n let cancelTimer: (() => void) | null = null;\n let shuttingDown = false;\n let inFlight: Promise<void> | null = null;\n let resolveDone: () => void;\n const done = new Promise<void>((resolve) => {\n resolveDone = resolve;\n });\n\n function startTimerIfNeeded(): void {\n if (cancelTimer !== null) return;\n if (pids.size === 0) return;\n cancelTimer = deps.setInterval(() => {\n void tick();\n }, deps.intervalMs);\n }\n\n async function tick(): Promise<void> {\n if (pids.size === 0) return;\n /**\n * Skip the tick if a prior one is still running. The worker's setInterval\n * fires independently of the main thread, so on the rare cycle where `ps`\n * actually takes longer than `intervalMs` we'd otherwise stack subprocess\n * spawns. The original main-thread implementation got this for free —\n * the main loop being busy prevented re-entry — but the worker's loop is\n * unblocked by definition.\n */\n if (inFlight !== null) return;\n /** Snapshot the pid set so add/remove during the tick doesn't change `nPids`. */\n const snapshot = Array.from(pids);\n const tickStart = deps.now();\n const work = (async () => {\n try {\n const rssByPid = await deps.readBatchedRss(snapshot);\n const samples: Array<{ pid: number; rssBytes: number }> = [];\n for (const [pid, rssBytes] of rssByPid) {\n samples.push({ pid, rssBytes });\n }\n const errorCount = snapshot.length - rssByPid.size;\n deps.post({\n type: 'samples',\n samples,\n durationMs: Math.round(deps.now() - tickStart),\n nPids: snapshot.length,\n errorCount,\n });\n } catch (err) {\n deps.post({\n type: 'failed',\n durationMs: Math.round(deps.now() - tickStart),\n nPids: snapshot.length,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n })();\n inFlight = work;\n try {\n await work;\n } finally {\n if (inFlight === work) inFlight = null;\n }\n }\n\n function dispatch(cmd: WorkerCommand): void {\n if (shuttingDown) return;\n switch (cmd.cmd) {\n case 'add_pid':\n pids.add(cmd.pid);\n startTimerIfNeeded();\n return;\n case 'remove_pid':\n pids.delete(cmd.pid);\n /**\n * Keep the timer alive even at size 0 — the supervisor is the only\n * authority on worker lifetime. Stopping the timer here would create\n * a thrash window if a single agent is restarted quickly.\n */\n return;\n case 'shutdown':\n void handleShutdown();\n return;\n default:\n assertNever(cmd);\n }\n }\n\n async function handleShutdown(): Promise<void> {\n if (shuttingDown) return;\n shuttingDown = true;\n if (cancelTimer !== null) {\n cancelTimer();\n cancelTimer = null;\n }\n pids.clear();\n /** Let any in-flight tick finish so we don't post a sample after `done`. */\n if (inFlight) {\n try {\n await inFlight;\n } catch {\n /** Errors are already reported via the `failed` message. */\n }\n }\n resolveDone();\n }\n\n return { dispatch, done };\n}\n\n/** RSS sampling cadence — matches the legacy in-process tick. */\nexport const MEMORY_SAMPLE_INTERVAL_MS = 30_000;\n\n/** macOS amortizes a single `ps` spawn across all pids; Linux reads `/proc` concurrently. */\nexport async function readBatchedRss(pids: readonly number[]): Promise<Map<number, number>> {\n if (pids.length === 0) return new Map();\n if (platform() === 'linux') {\n const results = await Promise.all(\n pids.map(async (pid) => [pid, await readLinuxRss(pid)] as const)\n );\n const map = new Map<number, number>();\n for (const [pid, rss] of results) {\n if (rss != null) map.set(pid, rss);\n }\n return map;\n }\n return new Promise((resolve) => {\n /**\n * Argv form — never string concatenation. `ps -p` accepts a comma-joined\n * pid list as a single argv element, which keeps the pid list out of the\n * shell entirely. Numbers are coerced via .filter(Number.isFinite) at the\n * supervisor boundary before they reach this function.\n */\n execFile('ps', ['-o', 'pid=,rss=', '-p', pids.join(',')], (_err, stdout) => {\n const map = new Map<number, number>();\n for (const line of stdout.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n const parts = trimmed.split(/\\s+/);\n const pid = Number(parts[0]);\n const kb = Number(parts[1]);\n if (Number.isFinite(pid) && Number.isFinite(kb)) {\n map.set(pid, kb * 1024);\n }\n }\n resolve(map);\n });\n });\n}\n\nfunction readLinuxRss(pid: number): Promise<number | null> {\n return readFile(`/proc/${pid}/status`, 'utf-8')\n .then((content) => {\n const match = content.match(/VmRSS:\\s+(\\d+)\\s+kB/);\n return match ? Number(match[1]) * 1024 : null;\n })\n .catch(() => null);\n}\n\n/**\n * Production entry: when the daemon's bundled `dist/rss-worker.js` is loaded\n * by `new Worker(workerPath)`, `parentPort` is non-null. We wire the runtime\n * deps to the real `worker_threads` MessagePort, real timers, and the real\n * `readBatchedRss`. Tests import `runRssWorker` directly and skip this branch.\n */\nif (parentPort !== null) {\n const port = parentPort;\n const handle = runRssWorker({\n readBatchedRss,\n setInterval: (fn, ms) => {\n const t = setInterval(fn, ms);\n return () => clearInterval(t);\n },\n now: () => performance.now(),\n post: (reply) => port.postMessage(reply),\n intervalMs: MEMORY_SAMPLE_INTERVAL_MS,\n });\n port.on('message', (cmd: WorkerCommand) => {\n handle.dispatch(cmd);\n });\n /** Resolve the runtime when shutdown completes. The supervisor terminates the worker after this. */\n void handle.done.then(() => {\n port.close();\n });\n}\n"],"mappings":"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","names":[]}