@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/index.cjs CHANGED
@@ -214,9 +214,9 @@ var MCP_RUNTIME_MANIFEST = {
214
214
  mcpPackage: "@haven_ai/mcp",
215
215
  mcpVersion: mcp.MCP_VERSION,
216
216
  sdkPackage: "@haven_ai/sdk",
217
- sdkVersion: "0.1.12-alpha.0",
217
+ sdkVersion: "0.1.13-alpha.0",
218
218
  signerPackage: "@haven_ai/signer",
219
- signerVersion: "0.1.12-alpha.0",
219
+ signerVersion: "0.1.13-alpha.0",
220
220
  minimumNodeVersion: "20.0.0",
221
221
  supportedClients: ["codex-cli", "codex-desktop", "claude-code"],
222
222
  requiredTools: mcp.registeredToolNames()
@@ -389,10 +389,16 @@ function buildHostedServer(hostedMcpUrl, apiKey, runtime) {
389
389
  headers: { Authorization: `Bearer ${apiKey}` }
390
390
  };
391
391
  }
392
- function buildSignerServer(signerPath, runtime) {
393
- const server = {
392
+ function resolveSignerLaunchSpec(input) {
393
+ return input.signerCommand ?? {
394
394
  command: "npx",
395
- args: ["-y", signerPackageName(), "--credentials", signerPath]
395
+ args: ["-y", signerPackageSpec(), "--credentials", input.signerPath]
396
+ };
397
+ }
398
+ function buildSignerServer(spec, runtime) {
399
+ const server = {
400
+ command: spec.command,
401
+ args: spec.args
396
402
  };
397
403
  if (runtime === "vscode" || runtime === "vscode-insiders") return { type: "stdio", ...server };
398
404
  return server;
@@ -425,7 +431,7 @@ function mergeCodexToml(existingToml, localMcpCommand) {
425
431
  `;
426
432
  return merged;
427
433
  }
428
- function mergeCodexTomlHosted(existingToml, hostedMcpUrl, apiKey, signerPath) {
434
+ function mergeCodexTomlHosted(existingToml, hostedMcpUrl, apiKey, signerSpec) {
429
435
  let next = removeTomlTableTree(removeTomlTableTree(existingToml, "mcp_servers.haven"), "mcp_servers.haven_signer");
430
436
  next = next.trimEnd();
431
437
  const block = [
@@ -434,8 +440,8 @@ function mergeCodexTomlHosted(existingToml, hostedMcpUrl, apiKey, signerPath) {
434
440
  `http_headers = { "Authorization" = ${tomlString(`Bearer ${apiKey}`)} }`,
435
441
  "",
436
442
  "[mcp_servers.haven_signer]",
437
- 'command = "npx"',
438
- `args = ["-y", ${tomlString(signerPackageSpec())}, "--credentials", ${tomlString(signerPath)}]`,
443
+ `command = ${tomlString(signerSpec.command)}`,
444
+ `args = [${signerSpec.args.map((arg) => tomlString(arg)).join(", ")}]`,
439
445
  "startup_timeout_sec = 120"
440
446
  ].join("\n");
441
447
  validateCodexToml(block, "Generated Codex Haven config");
@@ -452,7 +458,7 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
452
458
  existing,
453
459
  serverRoot,
454
460
  buildHostedServer(input.hostedMcpUrl, input.apiKey, input.runtime),
455
- buildSignerServer(input.signerPath, input.runtime)
461
+ buildSignerServer(resolveSignerLaunchSpec(input), input.runtime)
456
462
  );
457
463
  await writeOwnerOnlyText(target, merged);
458
464
  return {
@@ -508,7 +514,7 @@ async function writeCodexConfig(input) {
508
514
  ]
509
515
  };
510
516
  }
511
- const merged = mergeCodexTomlHosted(existing ?? "", input.hostedMcpUrl, input.apiKey, input.signerPath);
517
+ const merged = mergeCodexTomlHosted(existing ?? "", input.hostedMcpUrl, input.apiKey, resolveSignerLaunchSpec(input));
512
518
  await writeOwnerOnlyText(target, merged);
513
519
  return {
514
520
  hostedConfigured: true,
@@ -793,9 +799,6 @@ function configTargetLabel(runtime) {
793
799
  return "runtime MCP config";
794
800
  }
795
801
  }
796
- function signerPackageName() {
797
- return signerPackageSpec();
798
- }
799
802
  async function acknowledgeLocalMcpConsent(identityPath, signerPath, log) {
800
803
  try {
801
804
  const input = await buildLocalMcpConsentInput(identityPath, signerPath);
@@ -909,7 +912,7 @@ async function probeLocalSignerCredential(signerPath) {
909
912
  }
910
913
  }
911
914
  async function probeLocalMcpTools(command, args, requiredTools, timeoutMs = 1e4) {
912
- return new Promise((resolve7) => {
915
+ return new Promise((resolve8) => {
913
916
  const child = child_process.spawn(command, args, { stdio: ["pipe", "pipe", "ignore"] });
914
917
  let stdout = "";
915
918
  let settled = false;
@@ -919,7 +922,7 @@ async function probeLocalMcpTools(command, args, requiredTools, timeoutMs = 1e4)
919
922
  settled = true;
920
923
  clearTimeout(timeout);
921
924
  child.kill();
922
- resolve7(result);
925
+ resolve8(result);
923
926
  };
924
927
  const timeout = setTimeout(() => finish({ status: "timeout" }), timeoutMs);
925
928
  child.on("error", () => finish({ status: "process_error" }));
@@ -1157,6 +1160,126 @@ async function assertFileExists(path, label) {
1157
1160
  throw new Error(`Missing ${label}: ${path}`);
1158
1161
  }
1159
1162
  }
1163
+ var execFileAsync2 = util.promisify(child_process.execFile);
1164
+ async function prepareSignerRuntime(input, deps = {}) {
1165
+ const homeDir = input.homeDir ?? os.homedir();
1166
+ const runtimeDirectory = path.resolve(homeDir, ".haven", "signer-runtime", MCP_RUNTIME_MANIFEST.signerVersion);
1167
+ const npmCacheDirectory = path.resolve(homeDir, ".haven", "npm-cache");
1168
+ const cliPath = path.join(runtimeDirectory, "node_modules", "@haven_ai", "signer", "dist", "cli.js");
1169
+ const messages = [];
1170
+ await promises.mkdir(runtimeDirectory, { recursive: true, mode: 448 });
1171
+ await promises.chmod(runtimeDirectory, 448).catch(() => void 0);
1172
+ await promises.mkdir(npmCacheDirectory, { recursive: true, mode: 448 });
1173
+ await promises.chmod(npmCacheDirectory, 448).catch(() => void 0);
1174
+ if (await installedRuntimeMatches2(runtimeDirectory, cliPath)) {
1175
+ messages.push(`Using existing local Haven signer runtime ${signerPackageSpec()}.`);
1176
+ } else {
1177
+ await installRuntimePackages2(runtimeDirectory, npmCacheDirectory, deps.runCommand);
1178
+ messages.push(`Installed local Haven signer runtime ${signerPackageSpec()}.`);
1179
+ }
1180
+ await assertFileExists2(cliPath, "local Haven signer CLI");
1181
+ const wrapperPath = path.join(input.credentialDirectory, "bin", "haven-signer.mjs");
1182
+ await writeWrapper2({ wrapperPath, cliPath, signerPath: input.signerPath });
1183
+ await writeRuntimeSidecar2({
1184
+ path: path.join(input.credentialDirectory, "signer-runtime.json"),
1185
+ wrapperPath,
1186
+ runtimeDirectory,
1187
+ npmCacheDirectory,
1188
+ cliPath
1189
+ });
1190
+ messages.push(`Prepared stable local Haven signer wrapper: ${wrapperPath}`);
1191
+ return {
1192
+ command: wrapperPath,
1193
+ args: [],
1194
+ wrapperPath,
1195
+ runtimeDirectory,
1196
+ npmCacheDirectory,
1197
+ cliPath,
1198
+ messages
1199
+ };
1200
+ }
1201
+ async function installRuntimePackages2(runtimeDirectory, npmCacheDirectory, runCommand) {
1202
+ const args = [
1203
+ "install",
1204
+ "--prefix",
1205
+ runtimeDirectory,
1206
+ "--cache",
1207
+ npmCacheDirectory,
1208
+ "--no-audit",
1209
+ "--no-fund",
1210
+ "--omit=dev",
1211
+ signerPackageSpec(),
1212
+ sdkPackageSpec()
1213
+ ];
1214
+ try {
1215
+ if (runCommand) await runCommand("npm", args);
1216
+ else await execFileAsync2("npm", args, { timeout: 12e4, maxBuffer: 1024 * 1024 });
1217
+ } catch (err) {
1218
+ throw new Error(
1219
+ `Could not install local Haven signer runtime ${signerPackageSpec()}: ${err instanceof Error ? err.message : String(err)}`
1220
+ );
1221
+ }
1222
+ }
1223
+ async function installedRuntimeMatches2(runtimeDirectory, cliPath) {
1224
+ try {
1225
+ await assertFileExists2(cliPath, "local Haven signer CLI");
1226
+ const [signerPackage, sdkPackage] = await Promise.all([
1227
+ readPackageJson2(path.join(runtimeDirectory, "node_modules", "@haven_ai", "signer", "package.json")),
1228
+ readPackageJson2(path.join(runtimeDirectory, "node_modules", "@haven_ai", "sdk", "package.json"))
1229
+ ]);
1230
+ return signerPackage.version === MCP_RUNTIME_MANIFEST.signerVersion && sdkPackage.version === MCP_RUNTIME_MANIFEST.sdkVersion;
1231
+ } catch {
1232
+ return false;
1233
+ }
1234
+ }
1235
+ async function readPackageJson2(path) {
1236
+ return JSON.parse(await promises.readFile(path, "utf8"));
1237
+ }
1238
+ async function writeWrapper2(input) {
1239
+ await promises.mkdir(path.dirname(input.wrapperPath), { recursive: true, mode: 448 });
1240
+ await promises.chmod(path.dirname(input.wrapperPath), 448).catch(() => void 0);
1241
+ const source = [
1242
+ "#!/usr/bin/env node",
1243
+ "import { spawn } from 'node:child_process'",
1244
+ "",
1245
+ `const cliPath = ${JSON.stringify(input.cliPath)}`,
1246
+ `const signerPath = ${JSON.stringify(input.signerPath)}`,
1247
+ "",
1248
+ "const child = spawn(process.execPath, [cliPath, '--credentials', signerPath, ...process.argv.slice(2)], {",
1249
+ " stdio: 'inherit',",
1250
+ "})",
1251
+ "",
1252
+ "child.on('exit', (code, signal) => {",
1253
+ " if (signal) process.kill(process.pid, signal)",
1254
+ " else process.exit(code ?? 1)",
1255
+ "})",
1256
+ ""
1257
+ ].join("\n");
1258
+ await promises.writeFile(input.wrapperPath, source, { mode: 448 });
1259
+ await promises.chmod(input.wrapperPath, 448).catch(() => void 0);
1260
+ }
1261
+ async function writeRuntimeSidecar2(input) {
1262
+ const value = {
1263
+ signer_package: MCP_RUNTIME_MANIFEST.signerPackage,
1264
+ signer_version: MCP_RUNTIME_MANIFEST.signerVersion,
1265
+ sdk_package: MCP_RUNTIME_MANIFEST.sdkPackage,
1266
+ sdk_version: MCP_RUNTIME_MANIFEST.sdkVersion,
1267
+ wrapper_path: input.wrapperPath,
1268
+ runtime_directory: input.runtimeDirectory,
1269
+ npm_cache_directory: input.npmCacheDirectory,
1270
+ cli_path: input.cliPath
1271
+ };
1272
+ await promises.writeFile(input.path, `${JSON.stringify(value, null, 2)}
1273
+ `, { mode: 384 });
1274
+ await promises.chmod(input.path, 384).catch(() => void 0);
1275
+ }
1276
+ async function assertFileExists2(path, label) {
1277
+ try {
1278
+ await promises.access(path);
1279
+ } catch {
1280
+ throw new Error(`Missing ${label}: ${path}`);
1281
+ }
1282
+ }
1160
1283
  async function acknowledgeLocalSignerConsent(signerPath, log) {
1161
1284
  try {
1162
1285
  const input = await buildSignerConsentInput(signerPath);
@@ -1228,7 +1351,7 @@ function writeLogChunk2(log, chunk) {
1228
1351
  }
1229
1352
 
1230
1353
  // src/runtime-install.ts
1231
- var execFileAsync2 = util.promisify(child_process.execFile);
1354
+ var execFileAsync3 = util.promisify(child_process.execFile);
1232
1355
  async function installRuntime(input, deps = {}) {
1233
1356
  const runtime = normalizeRuntime(input.runtime, deps.env);
1234
1357
  const profile = runtimeProfile(runtime, deps.env);
@@ -1289,7 +1412,20 @@ async function installRuntime(input, deps = {}) {
1289
1412
  ]
1290
1413
  };
1291
1414
  }
1292
- const configResult = runtime === "claude-code" ? localRuntime ? await configureClaudeCode(deps, localRuntimeInstall?.command ?? "") : await configureClaudeCodeHosted(deps, input) : await writeRuntimeConfig({
1415
+ let signerCommand;
1416
+ if (!localRuntime) {
1417
+ try {
1418
+ const signerRuntime = await prepareSignerForRuntime(input, deps);
1419
+ signerCommand = { command: signerRuntime.command, args: signerRuntime.args };
1420
+ consentMessages.push(...signerRuntime.messages);
1421
+ } catch (err) {
1422
+ consentMessages.push(
1423
+ `Could not pre-install the local Haven signer; falling back to npx launch: ${err instanceof Error ? err.message : String(err)}`
1424
+ );
1425
+ }
1426
+ }
1427
+ const signerRuntimePrepared = localRuntime ? void 0 : signerCommand !== void 0;
1428
+ const configResult = runtime === "claude-code" ? localRuntime ? await configureClaudeCode(deps, localRuntimeInstall?.command ?? "") : await configureClaudeCodeHosted(deps, input, signerCommand) : await writeRuntimeConfig({
1293
1429
  runtime,
1294
1430
  hostedMcpUrl: input.hostedMcpUrl,
1295
1431
  apiKey: input.apiKey,
@@ -1297,6 +1433,7 @@ async function installRuntime(input, deps = {}) {
1297
1433
  signerPath: input.signerPath,
1298
1434
  credentialDirectory: input.credentialDirectory,
1299
1435
  localMcpCommand: localRuntimeInstall?.command,
1436
+ signerCommand,
1300
1437
  homeDir: deps.homeDir,
1301
1438
  mode: localRuntime ? "local" : "hosted"
1302
1439
  });
@@ -1328,6 +1465,7 @@ async function installRuntime(input, deps = {}) {
1328
1465
  localMcpAcknowledged: localMcpConsent?.acknowledged,
1329
1466
  activationCommand: configResult.activationCommand,
1330
1467
  skillInstalled: skillInstall?.installed,
1468
+ signerRuntimePrepared,
1331
1469
  messages: [...consentMessages, ...localRuntimeInstall?.messages ?? [], ...configResult.messages, ...localProbeMessages, ...skillInstall?.messages ?? []]
1332
1470
  };
1333
1471
  }
@@ -1384,7 +1522,7 @@ async function configureClaudeCode(deps, localMcpCommand) {
1384
1522
  };
1385
1523
  }
1386
1524
  }
1387
- async function configureClaudeCodeHosted(deps, input) {
1525
+ async function configureClaudeCodeHosted(deps, input, signerCommand) {
1388
1526
  const runCommand = deps.runCommand ?? defaultRunCommand;
1389
1527
  const hostedJson = JSON.stringify({
1390
1528
  type: "http",
@@ -1393,8 +1531,8 @@ async function configureClaudeCodeHosted(deps, input) {
1393
1531
  });
1394
1532
  const signerJson = JSON.stringify({
1395
1533
  type: "stdio",
1396
- command: "npx",
1397
- args: ["-y", signerPackageSpec(), "--credentials", input.signerPath],
1534
+ command: signerCommand?.command ?? "npx",
1535
+ args: signerCommand?.args ?? ["-y", signerPackageSpec(), "--credentials", input.signerPath],
1398
1536
  env: {}
1399
1537
  });
1400
1538
  try {
@@ -1435,7 +1573,7 @@ async function configureClaudeCodeHosted(deps, input) {
1435
1573
  }
1436
1574
  }
1437
1575
  async function defaultRunCommand(command, args) {
1438
- await execFileAsync2(command, args, { timeout: 1e4 });
1576
+ await execFileAsync3(command, args, { timeout: 1e4 });
1439
1577
  }
1440
1578
  async function installClaudeSkill(homeDir) {
1441
1579
  try {
@@ -1518,6 +1656,14 @@ async function prepareRuntimeForLocalMcp(input, deps) {
1518
1656
  homeDir: deps.homeDir
1519
1657
  });
1520
1658
  }
1659
+ async function prepareSignerForRuntime(input, deps) {
1660
+ const prepare = deps.prepareSignerRuntime ?? ((runtimeInput) => prepareSignerRuntime(runtimeInput, { runCommand: deps.runCommand }));
1661
+ return prepare({
1662
+ credentialDirectory: input.credentialDirectory,
1663
+ signerPath: input.signerPath,
1664
+ homeDir: deps.homeDir
1665
+ });
1666
+ }
1521
1667
  async function runLocalMcpProbe(runtimeInstall, deps) {
1522
1668
  const probe = deps.probeLocalMcpTools ?? probeLocalMcpTools;
1523
1669
  try {
@@ -1534,7 +1680,7 @@ function localRuntimePrepareErrorCode(err) {
1534
1680
  }
1535
1681
 
1536
1682
  // src/runtime.ts
1537
- var CONNECTOR_VERSION = "0.1.12-alpha.0";
1683
+ var CONNECTOR_VERSION = "0.1.13-alpha.0";
1538
1684
  async function runConnect(options, deps = {}) {
1539
1685
  const connectorVersion = options.connectorVersion ?? CONNECTOR_VERSION;
1540
1686
  const api = deps.api ?? createConnectApiClient(options.apiBaseUrl);
@@ -1784,6 +1930,7 @@ exports.installRuntime = installRuntime;
1784
1930
  exports.mcpPackageSpec = mcpPackageSpec;
1785
1931
  exports.normalizeRuntime = normalizeRuntime;
1786
1932
  exports.parseArgs = parseArgs;
1933
+ exports.prepareSignerRuntime = prepareSignerRuntime;
1787
1934
  exports.redactSecrets = redactSecrets;
1788
1935
  exports.runConnect = runConnect;
1789
1936
  exports.runtimeInstallCapabilities = runtimeInstallCapabilities;