@haven_ai/connect 0.1.12-alpha.0 → 0.1.13-alpha.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.
package/dist/cli.js CHANGED
@@ -209,9 +209,9 @@ var MCP_RUNTIME_MANIFEST = {
209
209
  mcpPackage: "@haven_ai/mcp",
210
210
  mcpVersion: MCP_VERSION,
211
211
  sdkPackage: "@haven_ai/sdk",
212
- sdkVersion: "0.1.12-alpha.0",
212
+ sdkVersion: "0.1.13-alpha.0",
213
213
  signerPackage: "@haven_ai/signer",
214
- signerVersion: "0.1.12-alpha.0",
214
+ signerVersion: "0.1.13-alpha.0",
215
215
  minimumNodeVersion: "20.0.0",
216
216
  supportedClients: ["codex-cli", "codex-desktop", "claude-code"],
217
217
  requiredTools: registeredToolNames()
@@ -384,10 +384,16 @@ function buildHostedServer(hostedMcpUrl, apiKey, runtime) {
384
384
  headers: { Authorization: `Bearer ${apiKey}` }
385
385
  };
386
386
  }
387
- function buildSignerServer(signerPath, runtime) {
388
- const server = {
387
+ function resolveSignerLaunchSpec(input) {
388
+ return input.signerCommand ?? {
389
389
  command: "npx",
390
- args: ["-y", signerPackageName(), "--credentials", signerPath]
390
+ args: ["-y", signerPackageSpec(), "--credentials", input.signerPath]
391
+ };
392
+ }
393
+ function buildSignerServer(spec, runtime) {
394
+ const server = {
395
+ command: spec.command,
396
+ args: spec.args
391
397
  };
392
398
  if (runtime === "vscode" || runtime === "vscode-insiders") return { type: "stdio", ...server };
393
399
  return server;
@@ -420,7 +426,7 @@ function mergeCodexToml(existingToml, localMcpCommand) {
420
426
  `;
421
427
  return merged;
422
428
  }
423
- function mergeCodexTomlHosted(existingToml, hostedMcpUrl, apiKey, signerPath) {
429
+ function mergeCodexTomlHosted(existingToml, hostedMcpUrl, apiKey, signerSpec) {
424
430
  let next = removeTomlTableTree(removeTomlTableTree(existingToml, "mcp_servers.haven"), "mcp_servers.haven_signer");
425
431
  next = next.trimEnd();
426
432
  const block = [
@@ -429,8 +435,8 @@ function mergeCodexTomlHosted(existingToml, hostedMcpUrl, apiKey, signerPath) {
429
435
  `http_headers = { "Authorization" = ${tomlString(`Bearer ${apiKey}`)} }`,
430
436
  "",
431
437
  "[mcp_servers.haven_signer]",
432
- 'command = "npx"',
433
- `args = ["-y", ${tomlString(signerPackageSpec())}, "--credentials", ${tomlString(signerPath)}]`,
438
+ `command = ${tomlString(signerSpec.command)}`,
439
+ `args = [${signerSpec.args.map((arg) => tomlString(arg)).join(", ")}]`,
434
440
  "startup_timeout_sec = 120"
435
441
  ].join("\n");
436
442
  validateCodexToml(block, "Generated Codex Haven config");
@@ -447,7 +453,7 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
447
453
  existing,
448
454
  serverRoot,
449
455
  buildHostedServer(input.hostedMcpUrl, input.apiKey, input.runtime),
450
- buildSignerServer(input.signerPath, input.runtime)
456
+ buildSignerServer(resolveSignerLaunchSpec(input), input.runtime)
451
457
  );
452
458
  await writeOwnerOnlyText(target, merged);
453
459
  return {
@@ -503,7 +509,7 @@ async function writeCodexConfig(input) {
503
509
  ]
504
510
  };
505
511
  }
506
- const merged = mergeCodexTomlHosted(existing ?? "", input.hostedMcpUrl, input.apiKey, input.signerPath);
512
+ const merged = mergeCodexTomlHosted(existing ?? "", input.hostedMcpUrl, input.apiKey, resolveSignerLaunchSpec(input));
507
513
  await writeOwnerOnlyText(target, merged);
508
514
  return {
509
515
  hostedConfigured: true,
@@ -788,9 +794,6 @@ function configTargetLabel(runtime) {
788
794
  return "runtime MCP config";
789
795
  }
790
796
  }
791
- function signerPackageName() {
792
- return signerPackageSpec();
793
- }
794
797
  async function acknowledgeLocalMcpConsent(identityPath, signerPath, log) {
795
798
  try {
796
799
  const input = await buildLocalMcpConsentInput(identityPath, signerPath);
@@ -904,7 +907,7 @@ async function probeLocalSignerCredential(signerPath) {
904
907
  }
905
908
  }
906
909
  async function probeLocalMcpTools(command, args, requiredTools, timeoutMs = 1e4) {
907
- return new Promise((resolve7) => {
910
+ return new Promise((resolve8) => {
908
911
  const child = spawn(command, args, { stdio: ["pipe", "pipe", "ignore"] });
909
912
  let stdout = "";
910
913
  let settled = false;
@@ -914,7 +917,7 @@ async function probeLocalMcpTools(command, args, requiredTools, timeoutMs = 1e4)
914
917
  settled = true;
915
918
  clearTimeout(timeout);
916
919
  child.kill();
917
- resolve7(result);
920
+ resolve8(result);
918
921
  };
919
922
  const timeout = setTimeout(() => finish({ status: "timeout" }), timeoutMs);
920
923
  child.on("error", () => finish({ status: "process_error" }));
@@ -1152,6 +1155,126 @@ async function assertFileExists(path, label) {
1152
1155
  throw new Error(`Missing ${label}: ${path}`);
1153
1156
  }
1154
1157
  }
1158
+ var execFileAsync2 = promisify(execFile);
1159
+ async function prepareSignerRuntime(input, deps = {}) {
1160
+ const homeDir = input.homeDir ?? homedir();
1161
+ const runtimeDirectory = resolve(homeDir, ".haven", "signer-runtime", MCP_RUNTIME_MANIFEST.signerVersion);
1162
+ const npmCacheDirectory = resolve(homeDir, ".haven", "npm-cache");
1163
+ const cliPath = join(runtimeDirectory, "node_modules", "@haven_ai", "signer", "dist", "cli.js");
1164
+ const messages = [];
1165
+ await mkdir(runtimeDirectory, { recursive: true, mode: 448 });
1166
+ await chmod(runtimeDirectory, 448).catch(() => void 0);
1167
+ await mkdir(npmCacheDirectory, { recursive: true, mode: 448 });
1168
+ await chmod(npmCacheDirectory, 448).catch(() => void 0);
1169
+ if (await installedRuntimeMatches2(runtimeDirectory, cliPath)) {
1170
+ messages.push(`Using existing local Haven signer runtime ${signerPackageSpec()}.`);
1171
+ } else {
1172
+ await installRuntimePackages2(runtimeDirectory, npmCacheDirectory, deps.runCommand);
1173
+ messages.push(`Installed local Haven signer runtime ${signerPackageSpec()}.`);
1174
+ }
1175
+ await assertFileExists2(cliPath, "local Haven signer CLI");
1176
+ const wrapperPath = join(input.credentialDirectory, "bin", "haven-signer.mjs");
1177
+ await writeWrapper2({ wrapperPath, cliPath, signerPath: input.signerPath });
1178
+ await writeRuntimeSidecar2({
1179
+ path: join(input.credentialDirectory, "signer-runtime.json"),
1180
+ wrapperPath,
1181
+ runtimeDirectory,
1182
+ npmCacheDirectory,
1183
+ cliPath
1184
+ });
1185
+ messages.push(`Prepared stable local Haven signer wrapper: ${wrapperPath}`);
1186
+ return {
1187
+ command: wrapperPath,
1188
+ args: [],
1189
+ wrapperPath,
1190
+ runtimeDirectory,
1191
+ npmCacheDirectory,
1192
+ cliPath,
1193
+ messages
1194
+ };
1195
+ }
1196
+ async function installRuntimePackages2(runtimeDirectory, npmCacheDirectory, runCommand) {
1197
+ const args = [
1198
+ "install",
1199
+ "--prefix",
1200
+ runtimeDirectory,
1201
+ "--cache",
1202
+ npmCacheDirectory,
1203
+ "--no-audit",
1204
+ "--no-fund",
1205
+ "--omit=dev",
1206
+ signerPackageSpec(),
1207
+ sdkPackageSpec()
1208
+ ];
1209
+ try {
1210
+ if (runCommand) await runCommand("npm", args);
1211
+ else await execFileAsync2("npm", args, { timeout: 12e4, maxBuffer: 1024 * 1024 });
1212
+ } catch (err) {
1213
+ throw new Error(
1214
+ `Could not install local Haven signer runtime ${signerPackageSpec()}: ${err instanceof Error ? err.message : String(err)}`
1215
+ );
1216
+ }
1217
+ }
1218
+ async function installedRuntimeMatches2(runtimeDirectory, cliPath) {
1219
+ try {
1220
+ await assertFileExists2(cliPath, "local Haven signer CLI");
1221
+ const [signerPackage, sdkPackage] = await Promise.all([
1222
+ readPackageJson2(join(runtimeDirectory, "node_modules", "@haven_ai", "signer", "package.json")),
1223
+ readPackageJson2(join(runtimeDirectory, "node_modules", "@haven_ai", "sdk", "package.json"))
1224
+ ]);
1225
+ return signerPackage.version === MCP_RUNTIME_MANIFEST.signerVersion && sdkPackage.version === MCP_RUNTIME_MANIFEST.sdkVersion;
1226
+ } catch {
1227
+ return false;
1228
+ }
1229
+ }
1230
+ async function readPackageJson2(path) {
1231
+ return JSON.parse(await readFile(path, "utf8"));
1232
+ }
1233
+ async function writeWrapper2(input) {
1234
+ await mkdir(dirname(input.wrapperPath), { recursive: true, mode: 448 });
1235
+ await chmod(dirname(input.wrapperPath), 448).catch(() => void 0);
1236
+ const source = [
1237
+ "#!/usr/bin/env node",
1238
+ "import { spawn } from 'node:child_process'",
1239
+ "",
1240
+ `const cliPath = ${JSON.stringify(input.cliPath)}`,
1241
+ `const signerPath = ${JSON.stringify(input.signerPath)}`,
1242
+ "",
1243
+ "const child = spawn(process.execPath, [cliPath, '--credentials', signerPath, ...process.argv.slice(2)], {",
1244
+ " stdio: 'inherit',",
1245
+ "})",
1246
+ "",
1247
+ "child.on('exit', (code, signal) => {",
1248
+ " if (signal) process.kill(process.pid, signal)",
1249
+ " else process.exit(code ?? 1)",
1250
+ "})",
1251
+ ""
1252
+ ].join("\n");
1253
+ await writeFile(input.wrapperPath, source, { mode: 448 });
1254
+ await chmod(input.wrapperPath, 448).catch(() => void 0);
1255
+ }
1256
+ async function writeRuntimeSidecar2(input) {
1257
+ const value = {
1258
+ signer_package: MCP_RUNTIME_MANIFEST.signerPackage,
1259
+ signer_version: MCP_RUNTIME_MANIFEST.signerVersion,
1260
+ sdk_package: MCP_RUNTIME_MANIFEST.sdkPackage,
1261
+ sdk_version: MCP_RUNTIME_MANIFEST.sdkVersion,
1262
+ wrapper_path: input.wrapperPath,
1263
+ runtime_directory: input.runtimeDirectory,
1264
+ npm_cache_directory: input.npmCacheDirectory,
1265
+ cli_path: input.cliPath
1266
+ };
1267
+ await writeFile(input.path, `${JSON.stringify(value, null, 2)}
1268
+ `, { mode: 384 });
1269
+ await chmod(input.path, 384).catch(() => void 0);
1270
+ }
1271
+ async function assertFileExists2(path, label) {
1272
+ try {
1273
+ await access(path);
1274
+ } catch {
1275
+ throw new Error(`Missing ${label}: ${path}`);
1276
+ }
1277
+ }
1155
1278
  async function acknowledgeLocalSignerConsent(signerPath, log) {
1156
1279
  try {
1157
1280
  const input = await buildSignerConsentInput(signerPath);
@@ -1223,7 +1346,7 @@ function writeLogChunk2(log, chunk) {
1223
1346
  }
1224
1347
 
1225
1348
  // src/runtime-install.ts
1226
- var execFileAsync2 = promisify(execFile);
1349
+ var execFileAsync3 = promisify(execFile);
1227
1350
  async function installRuntime(input, deps = {}) {
1228
1351
  const runtime = normalizeRuntime(input.runtime, deps.env);
1229
1352
  const profile = runtimeProfile(runtime, deps.env);
@@ -1284,7 +1407,20 @@ async function installRuntime(input, deps = {}) {
1284
1407
  ]
1285
1408
  };
1286
1409
  }
1287
- const configResult = runtime === "claude-code" ? localRuntime ? await configureClaudeCode(deps, localRuntimeInstall?.command ?? "") : await configureClaudeCodeHosted(deps, input) : await writeRuntimeConfig({
1410
+ let signerCommand;
1411
+ if (!localRuntime) {
1412
+ try {
1413
+ const signerRuntime = await prepareSignerForRuntime(input, deps);
1414
+ signerCommand = { command: signerRuntime.command, args: signerRuntime.args };
1415
+ consentMessages.push(...signerRuntime.messages);
1416
+ } catch (err) {
1417
+ consentMessages.push(
1418
+ `Could not pre-install the local Haven signer; falling back to npx launch: ${err instanceof Error ? err.message : String(err)}`
1419
+ );
1420
+ }
1421
+ }
1422
+ const signerRuntimePrepared = localRuntime ? void 0 : signerCommand !== void 0;
1423
+ const configResult = runtime === "claude-code" ? localRuntime ? await configureClaudeCode(deps, localRuntimeInstall?.command ?? "") : await configureClaudeCodeHosted(deps, input, signerCommand) : await writeRuntimeConfig({
1288
1424
  runtime,
1289
1425
  hostedMcpUrl: input.hostedMcpUrl,
1290
1426
  apiKey: input.apiKey,
@@ -1292,6 +1428,7 @@ async function installRuntime(input, deps = {}) {
1292
1428
  signerPath: input.signerPath,
1293
1429
  credentialDirectory: input.credentialDirectory,
1294
1430
  localMcpCommand: localRuntimeInstall?.command,
1431
+ signerCommand,
1295
1432
  homeDir: deps.homeDir,
1296
1433
  mode: localRuntime ? "local" : "hosted"
1297
1434
  });
@@ -1323,6 +1460,7 @@ async function installRuntime(input, deps = {}) {
1323
1460
  localMcpAcknowledged: localMcpConsent?.acknowledged,
1324
1461
  activationCommand: configResult.activationCommand,
1325
1462
  skillInstalled: skillInstall?.installed,
1463
+ signerRuntimePrepared,
1326
1464
  messages: [...consentMessages, ...localRuntimeInstall?.messages ?? [], ...configResult.messages, ...localProbeMessages, ...skillInstall?.messages ?? []]
1327
1465
  };
1328
1466
  }
@@ -1379,7 +1517,7 @@ async function configureClaudeCode(deps, localMcpCommand) {
1379
1517
  };
1380
1518
  }
1381
1519
  }
1382
- async function configureClaudeCodeHosted(deps, input) {
1520
+ async function configureClaudeCodeHosted(deps, input, signerCommand) {
1383
1521
  const runCommand = deps.runCommand ?? defaultRunCommand;
1384
1522
  const hostedJson = JSON.stringify({
1385
1523
  type: "http",
@@ -1388,8 +1526,8 @@ async function configureClaudeCodeHosted(deps, input) {
1388
1526
  });
1389
1527
  const signerJson = JSON.stringify({
1390
1528
  type: "stdio",
1391
- command: "npx",
1392
- args: ["-y", signerPackageSpec(), "--credentials", input.signerPath],
1529
+ command: signerCommand?.command ?? "npx",
1530
+ args: signerCommand?.args ?? ["-y", signerPackageSpec(), "--credentials", input.signerPath],
1393
1531
  env: {}
1394
1532
  });
1395
1533
  try {
@@ -1430,7 +1568,7 @@ async function configureClaudeCodeHosted(deps, input) {
1430
1568
  }
1431
1569
  }
1432
1570
  async function defaultRunCommand(command, args) {
1433
- await execFileAsync2(command, args, { timeout: 1e4 });
1571
+ await execFileAsync3(command, args, { timeout: 1e4 });
1434
1572
  }
1435
1573
  async function installClaudeSkill(homeDir) {
1436
1574
  try {
@@ -1513,6 +1651,14 @@ async function prepareRuntimeForLocalMcp(input, deps) {
1513
1651
  homeDir: deps.homeDir
1514
1652
  });
1515
1653
  }
1654
+ async function prepareSignerForRuntime(input, deps) {
1655
+ const prepare = deps.prepareSignerRuntime ?? ((runtimeInput) => prepareSignerRuntime(runtimeInput, { runCommand: deps.runCommand }));
1656
+ return prepare({
1657
+ credentialDirectory: input.credentialDirectory,
1658
+ signerPath: input.signerPath,
1659
+ homeDir: deps.homeDir
1660
+ });
1661
+ }
1516
1662
  async function runLocalMcpProbe(runtimeInstall, deps) {
1517
1663
  const probe = deps.probeLocalMcpTools ?? probeLocalMcpTools;
1518
1664
  try {
@@ -1529,7 +1675,7 @@ function localRuntimePrepareErrorCode(err) {
1529
1675
  }
1530
1676
 
1531
1677
  // src/runtime.ts
1532
- var CONNECTOR_VERSION = "0.1.12-alpha.0";
1678
+ var CONNECTOR_VERSION = "0.1.13-alpha.0";
1533
1679
  async function runConnect(options, deps = {}) {
1534
1680
  const connectorVersion = options.connectorVersion ?? CONNECTOR_VERSION;
1535
1681
  const api = deps.api ?? createConnectApiClient(options.apiBaseUrl);