@haven_ai/connect 0.1.11-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.d.cts CHANGED
@@ -64,6 +64,7 @@ interface ResolvedSetup {
64
64
  reset_period_min: number;
65
65
  }>;
66
66
  hosted_mcp_url: string;
67
+ x402_binding_signer?: string | null;
67
68
  challenge: {
68
69
  id: string;
69
70
  message: string;
@@ -112,6 +113,7 @@ interface WriteCredentialInput {
112
113
  }>;
113
114
  apiUrl: string;
114
115
  hostedMcpUrl: string;
116
+ x402BindingSigner?: string;
115
117
  warn?: (message: string) => void;
116
118
  }
117
119
  interface PreflightCredentialStorageInput {
@@ -158,6 +160,42 @@ interface PreparedLocalMcpRuntime {
158
160
  messages: string[];
159
161
  }
160
162
 
163
+ interface PrepareSignerRuntimeInput {
164
+ credentialDirectory: string;
165
+ signerPath: string;
166
+ homeDir?: string;
167
+ }
168
+ interface PreparedSignerRuntime {
169
+ /** Absolute command to register as the signer MCP `command`. */
170
+ command: string;
171
+ /** Args to register alongside `command` (credentials are baked into the wrapper). */
172
+ args: string[];
173
+ wrapperPath: string;
174
+ runtimeDirectory: string;
175
+ npmCacheDirectory: string;
176
+ cliPath: string;
177
+ messages: string[];
178
+ }
179
+ interface SignerRuntimeDeps {
180
+ runCommand?: (command: string, args: string[]) => Promise<void>;
181
+ }
182
+ /**
183
+ * Pre-install the edge signer into a version-pinned, connector-managed
184
+ * directory and write a stable wrapper that launches it with an absolute Node
185
+ * path. The signer MCP is then registered as `command: <wrapper>` instead of a
186
+ * runtime `npx -y @haven_ai/signer@…` invocation.
187
+ *
188
+ * Why: launching the signer via bare `npx` at every MCP spawn made it depend on
189
+ * the PATH/environment the agent runtime hands the stdio subprocess, which is
190
+ * not the user's interactive shell — the failure mode that left `haven-signer`
191
+ * stuck at "Failed to connect" while the hosted HTTP server connected fine. The
192
+ * local MCP topology already avoids this with the same pre-install + wrapper
193
+ * pattern (see prepareLocalMcpRuntime); this brings the default hosted+signer
194
+ * topology to parity. Version stays pinned (no unpinned `npm i -g`), and the
195
+ * wrapper lives under ~/.haven so the reset flow already cleans it up.
196
+ */
197
+ declare function prepareSignerRuntime(input: PrepareSignerRuntimeInput, deps?: SignerRuntimeDeps): Promise<PreparedSignerRuntime>;
198
+
161
199
  interface RuntimeInstallInput {
162
200
  runtime?: string;
163
201
  hostedMcpUrl: string;
@@ -191,6 +229,14 @@ interface RuntimeInstallResult {
191
229
  localMcpAcknowledged?: boolean;
192
230
  activationCommand?: string;
193
231
  skillInstalled?: boolean;
232
+ /**
233
+ * Hosted topology only: true when the signer was pre-installed and registered
234
+ * via its absolute wrapper, false when prep failed and it fell back to the
235
+ * fragile runtime-`npx` launch. Undefined for local/manual topologies that
236
+ * do not run the signer as a separate MCP. Lets callers/tests detect the
237
+ * silent npx fallback instead of treating every write as fully healthy.
238
+ */
239
+ signerRuntimePrepared?: boolean;
194
240
  messages: string[];
195
241
  }
196
242
  interface RuntimeInstallDeps {
@@ -199,6 +245,7 @@ interface RuntimeInstallDeps {
199
245
  fetch?: typeof fetch;
200
246
  runCommand?: (command: string, args: string[]) => Promise<void>;
201
247
  prepareLocalMcpRuntime?: (input: PrepareLocalMcpRuntimeInput) => Promise<PreparedLocalMcpRuntime>;
248
+ prepareSignerRuntime?: (input: PrepareSignerRuntimeInput) => Promise<PreparedSignerRuntime>;
202
249
  probeLocalMcpTools?: (command: string, args: string[], requiredTools: readonly string[]) => Promise<LocalMcpProbeResult>;
203
250
  }
204
251
  declare function installRuntime(input: RuntimeInstallInput, deps?: RuntimeInstallDeps): Promise<RuntimeInstallResult>;
@@ -207,7 +254,7 @@ declare function runtimeInstallCapabilities(runtime: string | undefined, env?: N
207
254
  restartRequired: boolean;
208
255
  };
209
256
 
210
- declare const CONNECTOR_VERSION = "0.1.11-alpha.0";
257
+ declare const CONNECTOR_VERSION = "0.1.13-alpha.0";
211
258
  interface ConnectOptions {
212
259
  setupToken: string;
213
260
  apiBaseUrl: string;
@@ -248,11 +295,11 @@ declare function shortAddress(address: string): string;
248
295
 
249
296
  declare const MCP_RUNTIME_MANIFEST: {
250
297
  readonly mcpPackage: "@haven_ai/mcp";
251
- readonly mcpVersion: "0.1.11-alpha.0";
298
+ readonly mcpVersion: "0.1.13-alpha.0";
252
299
  readonly sdkPackage: "@haven_ai/sdk";
253
- readonly sdkVersion: "0.1.11-alpha.0";
300
+ readonly sdkVersion: "0.1.13-alpha.0";
254
301
  readonly signerPackage: "@haven_ai/signer";
255
- readonly signerVersion: "0.1.11-alpha.0";
302
+ readonly signerVersion: "0.1.13-alpha.0";
256
303
  readonly minimumNodeVersion: "20.0.0";
257
304
  readonly supportedClients: readonly ["codex-cli", "codex-desktop", "claude-code"];
258
305
  readonly requiredTools: readonly string[];
@@ -261,4 +308,4 @@ declare function mcpPackageSpec(): string;
261
308
  declare function sdkPackageSpec(): string;
262
309
  declare function signerPackageSpec(): string;
263
310
 
264
- export { CONNECTOR_VERSION, type ConnectApiClient, type ConnectDeps, type ConnectOptions, type ConnectResult, type LocalDelegateKey, MCP_RUNTIME_MANIFEST, type RegisterSetupInput, type RegisterSetupResponse, type ResolveSetupInput, type ResolvedSetup, type RuntimeId, type RuntimeInstallInput, type RuntimeInstallResult, type RuntimeProfile, type StoredCredentialPaths, type UpdateInstallStatusInput, type WriteCredentialInput, createConnectApiClient, defaultAgentDirectory, delegateKeyFromPrivateKey, generateDelegateKey, helpText, installRuntime, mcpPackageSpec, normalizeRuntime, parseArgs, redactSecrets, runConnect, runtimeInstallCapabilities, runtimeProfile, sdkPackageSpec, shortAddress, signerPackageSpec, writeCredentialFiles };
311
+ export { CONNECTOR_VERSION, type ConnectApiClient, type ConnectDeps, type ConnectOptions, type ConnectResult, type LocalDelegateKey, MCP_RUNTIME_MANIFEST, type PrepareSignerRuntimeInput, type PreparedSignerRuntime, type RegisterSetupInput, type RegisterSetupResponse, type ResolveSetupInput, type ResolvedSetup, type RuntimeId, type RuntimeInstallInput, type RuntimeInstallResult, type RuntimeProfile, type StoredCredentialPaths, type UpdateInstallStatusInput, type WriteCredentialInput, createConnectApiClient, defaultAgentDirectory, delegateKeyFromPrivateKey, generateDelegateKey, helpText, installRuntime, mcpPackageSpec, normalizeRuntime, parseArgs, prepareSignerRuntime, redactSecrets, runConnect, runtimeInstallCapabilities, runtimeProfile, sdkPackageSpec, shortAddress, signerPackageSpec, writeCredentialFiles };
package/dist/index.d.ts CHANGED
@@ -64,6 +64,7 @@ interface ResolvedSetup {
64
64
  reset_period_min: number;
65
65
  }>;
66
66
  hosted_mcp_url: string;
67
+ x402_binding_signer?: string | null;
67
68
  challenge: {
68
69
  id: string;
69
70
  message: string;
@@ -112,6 +113,7 @@ interface WriteCredentialInput {
112
113
  }>;
113
114
  apiUrl: string;
114
115
  hostedMcpUrl: string;
116
+ x402BindingSigner?: string;
115
117
  warn?: (message: string) => void;
116
118
  }
117
119
  interface PreflightCredentialStorageInput {
@@ -158,6 +160,42 @@ interface PreparedLocalMcpRuntime {
158
160
  messages: string[];
159
161
  }
160
162
 
163
+ interface PrepareSignerRuntimeInput {
164
+ credentialDirectory: string;
165
+ signerPath: string;
166
+ homeDir?: string;
167
+ }
168
+ interface PreparedSignerRuntime {
169
+ /** Absolute command to register as the signer MCP `command`. */
170
+ command: string;
171
+ /** Args to register alongside `command` (credentials are baked into the wrapper). */
172
+ args: string[];
173
+ wrapperPath: string;
174
+ runtimeDirectory: string;
175
+ npmCacheDirectory: string;
176
+ cliPath: string;
177
+ messages: string[];
178
+ }
179
+ interface SignerRuntimeDeps {
180
+ runCommand?: (command: string, args: string[]) => Promise<void>;
181
+ }
182
+ /**
183
+ * Pre-install the edge signer into a version-pinned, connector-managed
184
+ * directory and write a stable wrapper that launches it with an absolute Node
185
+ * path. The signer MCP is then registered as `command: <wrapper>` instead of a
186
+ * runtime `npx -y @haven_ai/signer@…` invocation.
187
+ *
188
+ * Why: launching the signer via bare `npx` at every MCP spawn made it depend on
189
+ * the PATH/environment the agent runtime hands the stdio subprocess, which is
190
+ * not the user's interactive shell — the failure mode that left `haven-signer`
191
+ * stuck at "Failed to connect" while the hosted HTTP server connected fine. The
192
+ * local MCP topology already avoids this with the same pre-install + wrapper
193
+ * pattern (see prepareLocalMcpRuntime); this brings the default hosted+signer
194
+ * topology to parity. Version stays pinned (no unpinned `npm i -g`), and the
195
+ * wrapper lives under ~/.haven so the reset flow already cleans it up.
196
+ */
197
+ declare function prepareSignerRuntime(input: PrepareSignerRuntimeInput, deps?: SignerRuntimeDeps): Promise<PreparedSignerRuntime>;
198
+
161
199
  interface RuntimeInstallInput {
162
200
  runtime?: string;
163
201
  hostedMcpUrl: string;
@@ -191,6 +229,14 @@ interface RuntimeInstallResult {
191
229
  localMcpAcknowledged?: boolean;
192
230
  activationCommand?: string;
193
231
  skillInstalled?: boolean;
232
+ /**
233
+ * Hosted topology only: true when the signer was pre-installed and registered
234
+ * via its absolute wrapper, false when prep failed and it fell back to the
235
+ * fragile runtime-`npx` launch. Undefined for local/manual topologies that
236
+ * do not run the signer as a separate MCP. Lets callers/tests detect the
237
+ * silent npx fallback instead of treating every write as fully healthy.
238
+ */
239
+ signerRuntimePrepared?: boolean;
194
240
  messages: string[];
195
241
  }
196
242
  interface RuntimeInstallDeps {
@@ -199,6 +245,7 @@ interface RuntimeInstallDeps {
199
245
  fetch?: typeof fetch;
200
246
  runCommand?: (command: string, args: string[]) => Promise<void>;
201
247
  prepareLocalMcpRuntime?: (input: PrepareLocalMcpRuntimeInput) => Promise<PreparedLocalMcpRuntime>;
248
+ prepareSignerRuntime?: (input: PrepareSignerRuntimeInput) => Promise<PreparedSignerRuntime>;
202
249
  probeLocalMcpTools?: (command: string, args: string[], requiredTools: readonly string[]) => Promise<LocalMcpProbeResult>;
203
250
  }
204
251
  declare function installRuntime(input: RuntimeInstallInput, deps?: RuntimeInstallDeps): Promise<RuntimeInstallResult>;
@@ -207,7 +254,7 @@ declare function runtimeInstallCapabilities(runtime: string | undefined, env?: N
207
254
  restartRequired: boolean;
208
255
  };
209
256
 
210
- declare const CONNECTOR_VERSION = "0.1.11-alpha.0";
257
+ declare const CONNECTOR_VERSION = "0.1.13-alpha.0";
211
258
  interface ConnectOptions {
212
259
  setupToken: string;
213
260
  apiBaseUrl: string;
@@ -248,11 +295,11 @@ declare function shortAddress(address: string): string;
248
295
 
249
296
  declare const MCP_RUNTIME_MANIFEST: {
250
297
  readonly mcpPackage: "@haven_ai/mcp";
251
- readonly mcpVersion: "0.1.11-alpha.0";
298
+ readonly mcpVersion: "0.1.13-alpha.0";
252
299
  readonly sdkPackage: "@haven_ai/sdk";
253
- readonly sdkVersion: "0.1.11-alpha.0";
300
+ readonly sdkVersion: "0.1.13-alpha.0";
254
301
  readonly signerPackage: "@haven_ai/signer";
255
- readonly signerVersion: "0.1.11-alpha.0";
302
+ readonly signerVersion: "0.1.13-alpha.0";
256
303
  readonly minimumNodeVersion: "20.0.0";
257
304
  readonly supportedClients: readonly ["codex-cli", "codex-desktop", "claude-code"];
258
305
  readonly requiredTools: readonly string[];
@@ -261,4 +308,4 @@ declare function mcpPackageSpec(): string;
261
308
  declare function sdkPackageSpec(): string;
262
309
  declare function signerPackageSpec(): string;
263
310
 
264
- export { CONNECTOR_VERSION, type ConnectApiClient, type ConnectDeps, type ConnectOptions, type ConnectResult, type LocalDelegateKey, MCP_RUNTIME_MANIFEST, type RegisterSetupInput, type RegisterSetupResponse, type ResolveSetupInput, type ResolvedSetup, type RuntimeId, type RuntimeInstallInput, type RuntimeInstallResult, type RuntimeProfile, type StoredCredentialPaths, type UpdateInstallStatusInput, type WriteCredentialInput, createConnectApiClient, defaultAgentDirectory, delegateKeyFromPrivateKey, generateDelegateKey, helpText, installRuntime, mcpPackageSpec, normalizeRuntime, parseArgs, redactSecrets, runConnect, runtimeInstallCapabilities, runtimeProfile, sdkPackageSpec, shortAddress, signerPackageSpec, writeCredentialFiles };
311
+ export { CONNECTOR_VERSION, type ConnectApiClient, type ConnectDeps, type ConnectOptions, type ConnectResult, type LocalDelegateKey, MCP_RUNTIME_MANIFEST, type PrepareSignerRuntimeInput, type PreparedSignerRuntime, type RegisterSetupInput, type RegisterSetupResponse, type ResolveSetupInput, type ResolvedSetup, type RuntimeId, type RuntimeInstallInput, type RuntimeInstallResult, type RuntimeProfile, type StoredCredentialPaths, type UpdateInstallStatusInput, type WriteCredentialInput, createConnectApiClient, defaultAgentDirectory, delegateKeyFromPrivateKey, generateDelegateKey, helpText, installRuntime, mcpPackageSpec, normalizeRuntime, parseArgs, prepareSignerRuntime, redactSecrets, runConnect, runtimeInstallCapabilities, runtimeProfile, sdkPackageSpec, shortAddress, signerPackageSpec, writeCredentialFiles };
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { join, resolve, dirname } from 'path';
6
6
  import { execFile, spawn } from 'child_process';
7
7
  import { promisify } from 'util';
8
8
  import { registeredToolNames, MCP_VERSION, ensureConsent, computeConsentHash, loadCredentials, consentInputFromClient } from '@haven_ai/mcp';
9
+ import { SKILL_FOLDER_NAME, HAVEN_SKILL_MD } from '@haven_ai/sdk';
9
10
  import { ensureSignerConsent, computeSignerConsentHash, loadSignerCredentials, createEdgeSigner, toolSchemas } from '@haven_ai/signer';
10
11
 
11
12
  // src/api.ts
@@ -140,6 +141,7 @@ async function writeCredentialFiles(input) {
140
141
  safe_address: input.safeAddress,
141
142
  chain_id: input.chainId,
142
143
  network: input.network,
144
+ x402_binding_signer: input.x402BindingSigner,
143
145
  note: "Local signer credential. Haven backend never receives this private key."
144
146
  },
145
147
  input.warn
@@ -206,9 +208,9 @@ var MCP_RUNTIME_MANIFEST = {
206
208
  mcpPackage: "@haven_ai/mcp",
207
209
  mcpVersion: MCP_VERSION,
208
210
  sdkPackage: "@haven_ai/sdk",
209
- sdkVersion: "0.1.11-alpha.0",
211
+ sdkVersion: "0.1.13-alpha.0",
210
212
  signerPackage: "@haven_ai/signer",
211
- signerVersion: "0.1.11-alpha.0",
213
+ signerVersion: "0.1.13-alpha.0",
212
214
  minimumNodeVersion: "20.0.0",
213
215
  supportedClients: ["codex-cli", "codex-desktop", "claude-code"],
214
216
  requiredTools: registeredToolNames()
@@ -381,10 +383,16 @@ function buildHostedServer(hostedMcpUrl, apiKey, runtime) {
381
383
  headers: { Authorization: `Bearer ${apiKey}` }
382
384
  };
383
385
  }
384
- function buildSignerServer(signerPath, runtime) {
385
- const server = {
386
+ function resolveSignerLaunchSpec(input) {
387
+ return input.signerCommand ?? {
386
388
  command: "npx",
387
- args: ["-y", signerPackageName(), "--credentials", signerPath]
389
+ args: ["-y", signerPackageSpec(), "--credentials", input.signerPath]
390
+ };
391
+ }
392
+ function buildSignerServer(spec, runtime) {
393
+ const server = {
394
+ command: spec.command,
395
+ args: spec.args
388
396
  };
389
397
  if (runtime === "vscode" || runtime === "vscode-insiders") return { type: "stdio", ...server };
390
398
  return server;
@@ -417,7 +425,7 @@ function mergeCodexToml(existingToml, localMcpCommand) {
417
425
  `;
418
426
  return merged;
419
427
  }
420
- function mergeCodexTomlHosted(existingToml, hostedMcpUrl, apiKey, signerPath) {
428
+ function mergeCodexTomlHosted(existingToml, hostedMcpUrl, apiKey, signerSpec) {
421
429
  let next = removeTomlTableTree(removeTomlTableTree(existingToml, "mcp_servers.haven"), "mcp_servers.haven_signer");
422
430
  next = next.trimEnd();
423
431
  const block = [
@@ -426,8 +434,8 @@ function mergeCodexTomlHosted(existingToml, hostedMcpUrl, apiKey, signerPath) {
426
434
  `http_headers = { "Authorization" = ${tomlString(`Bearer ${apiKey}`)} }`,
427
435
  "",
428
436
  "[mcp_servers.haven_signer]",
429
- 'command = "npx"',
430
- `args = ["-y", ${tomlString(signerPackageSpec())}, "--credentials", ${tomlString(signerPath)}]`,
437
+ `command = ${tomlString(signerSpec.command)}`,
438
+ `args = [${signerSpec.args.map((arg) => tomlString(arg)).join(", ")}]`,
431
439
  "startup_timeout_sec = 120"
432
440
  ].join("\n");
433
441
  validateCodexToml(block, "Generated Codex Haven config");
@@ -444,7 +452,7 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
444
452
  existing,
445
453
  serverRoot,
446
454
  buildHostedServer(input.hostedMcpUrl, input.apiKey, input.runtime),
447
- buildSignerServer(input.signerPath, input.runtime)
455
+ buildSignerServer(resolveSignerLaunchSpec(input), input.runtime)
448
456
  );
449
457
  await writeOwnerOnlyText(target, merged);
450
458
  return {
@@ -500,7 +508,7 @@ async function writeCodexConfig(input) {
500
508
  ]
501
509
  };
502
510
  }
503
- const merged = mergeCodexTomlHosted(existing ?? "", input.hostedMcpUrl, input.apiKey, input.signerPath);
511
+ const merged = mergeCodexTomlHosted(existing ?? "", input.hostedMcpUrl, input.apiKey, resolveSignerLaunchSpec(input));
504
512
  await writeOwnerOnlyText(target, merged);
505
513
  return {
506
514
  hostedConfigured: true,
@@ -785,9 +793,6 @@ function configTargetLabel(runtime) {
785
793
  return "runtime MCP config";
786
794
  }
787
795
  }
788
- function signerPackageName() {
789
- return signerPackageSpec();
790
- }
791
796
  async function acknowledgeLocalMcpConsent(identityPath, signerPath, log) {
792
797
  try {
793
798
  const input = await buildLocalMcpConsentInput(identityPath, signerPath);
@@ -901,7 +906,7 @@ async function probeLocalSignerCredential(signerPath) {
901
906
  }
902
907
  }
903
908
  async function probeLocalMcpTools(command, args, requiredTools, timeoutMs = 1e4) {
904
- return new Promise((resolve7) => {
909
+ return new Promise((resolve8) => {
905
910
  const child = spawn(command, args, { stdio: ["pipe", "pipe", "ignore"] });
906
911
  let stdout = "";
907
912
  let settled = false;
@@ -911,7 +916,7 @@ async function probeLocalMcpTools(command, args, requiredTools, timeoutMs = 1e4)
911
916
  settled = true;
912
917
  clearTimeout(timeout);
913
918
  child.kill();
914
- resolve7(result);
919
+ resolve8(result);
915
920
  };
916
921
  const timeout = setTimeout(() => finish({ status: "timeout" }), timeoutMs);
917
922
  child.on("error", () => finish({ status: "process_error" }));
@@ -1149,75 +1154,126 @@ async function assertFileExists(path, label) {
1149
1154
  throw new Error(`Missing ${label}: ${path}`);
1150
1155
  }
1151
1156
  }
1152
-
1153
- // src/skill-content.ts
1154
- var HAVEN_SKILL_MD = `---
1155
- name: haven-pay
1156
- description: Pay for things from the user's Haven wallet within their agent rules. Use when the user asks to send, pay, tip, or transfer crypto \u2014 or when a request hits an HTTP 402 (x402) paywall.
1157
- ---
1158
-
1159
- # Haven: pay from a Haven wallet
1160
-
1161
- This skill lets the agent make payments from the user's Haven wallet through
1162
- the Haven MCP tools. Every payment is checked against the agent's on-chain
1163
- budget before money moves; payments above the remaining budget wait for the
1164
- user's approval in Haven.
1165
-
1166
- ## When to use this skill
1167
-
1168
- - The user asks to send money, pay someone, tip, donate, or transfer tokens.
1169
- - A request returns HTTP 402 (x402): use the Haven pay tools to settle it,
1170
- then retry the original request.
1171
-
1172
- ## Identity and budget come from the tools \u2014 never assume them
1173
-
1174
- Do not guess the wallet address, network, or budget. Read them live:
1175
-
1176
- - \`haven_get_agent\` \u2014 agent identity, Haven wallet address, network.
1177
- - \`haven_get_allowances\` \u2014 current per-token budgets and what remains.
1178
-
1179
- Budgets reset on a period the user chose. If a payment exceeds the remaining
1180
- budget it is queued for the user to approve in the Haven dashboard \u2014 this is
1181
- normal, not an error.
1182
-
1183
- ## Paying
1184
-
1185
- - **Direct transfer:** \`haven_pay\` with recipient, amount, and token.
1186
- - **x402 paywall:** \`haven_quote_x402\` to get a quote, then
1187
- \`haven_pay_x402_quote\`. In the hosted setup the signing step happens in
1188
- the local Haven signer; follow the tool results \u2014 they tell you the next
1189
- action at every step. Retry the original request only when the result says
1190
- \`retry_original_x402_request\`.
1191
- - **Status:** \`haven_get_payment_status\` with a \`payment_id\` to check on
1192
- queued or in-flight payments. Do not poll in a tight loop.
1193
-
1194
- ## Approval semantics
1195
-
1196
- - A result with \`pending_approval\` means the payment exceeded the remaining
1197
- budget and is waiting for the user in Haven. Tell the user, then check
1198
- status later.
1199
- - Never ask the user for private keys and never try to sign anything
1200
- yourself \u2014 Haven signs. If a tool reports a missing or invalid credential,
1201
- tell the user to re-run the Haven setup command.
1202
-
1203
- ## Failure handling
1204
-
1205
- Haven errors are shaped \`{ error, status, details? }\` and written for
1206
- humans \u2014 surface the message verbatim. Common cases:
1207
-
1208
- - \`pending_approval\`: queued for the user's approval (see above).
1209
- - \`insufficient_funds\`: the Haven wallet doesn't hold enough of that token.
1210
- Suggest the user add funds in the Haven dashboard.
1211
- - Budget exceeded: tell the user how much remains (from
1212
- \`haven_get_allowances\`) and that they can raise the budget in Haven.
1213
-
1214
- ## Revoke
1215
-
1216
- If this agent's credential may have leaked, tell the user to pause or revoke
1217
- the agent in the Haven dashboard under Agents. New requests stop immediately
1218
- for that credential.
1219
- `;
1220
- var SKILL_FOLDER_NAME = "haven-pay";
1157
+ var execFileAsync2 = promisify(execFile);
1158
+ async function prepareSignerRuntime(input, deps = {}) {
1159
+ const homeDir = input.homeDir ?? homedir();
1160
+ const runtimeDirectory = resolve(homeDir, ".haven", "signer-runtime", MCP_RUNTIME_MANIFEST.signerVersion);
1161
+ const npmCacheDirectory = resolve(homeDir, ".haven", "npm-cache");
1162
+ const cliPath = join(runtimeDirectory, "node_modules", "@haven_ai", "signer", "dist", "cli.js");
1163
+ const messages = [];
1164
+ await mkdir(runtimeDirectory, { recursive: true, mode: 448 });
1165
+ await chmod(runtimeDirectory, 448).catch(() => void 0);
1166
+ await mkdir(npmCacheDirectory, { recursive: true, mode: 448 });
1167
+ await chmod(npmCacheDirectory, 448).catch(() => void 0);
1168
+ if (await installedRuntimeMatches2(runtimeDirectory, cliPath)) {
1169
+ messages.push(`Using existing local Haven signer runtime ${signerPackageSpec()}.`);
1170
+ } else {
1171
+ await installRuntimePackages2(runtimeDirectory, npmCacheDirectory, deps.runCommand);
1172
+ messages.push(`Installed local Haven signer runtime ${signerPackageSpec()}.`);
1173
+ }
1174
+ await assertFileExists2(cliPath, "local Haven signer CLI");
1175
+ const wrapperPath = join(input.credentialDirectory, "bin", "haven-signer.mjs");
1176
+ await writeWrapper2({ wrapperPath, cliPath, signerPath: input.signerPath });
1177
+ await writeRuntimeSidecar2({
1178
+ path: join(input.credentialDirectory, "signer-runtime.json"),
1179
+ wrapperPath,
1180
+ runtimeDirectory,
1181
+ npmCacheDirectory,
1182
+ cliPath
1183
+ });
1184
+ messages.push(`Prepared stable local Haven signer wrapper: ${wrapperPath}`);
1185
+ return {
1186
+ command: wrapperPath,
1187
+ args: [],
1188
+ wrapperPath,
1189
+ runtimeDirectory,
1190
+ npmCacheDirectory,
1191
+ cliPath,
1192
+ messages
1193
+ };
1194
+ }
1195
+ async function installRuntimePackages2(runtimeDirectory, npmCacheDirectory, runCommand) {
1196
+ const args = [
1197
+ "install",
1198
+ "--prefix",
1199
+ runtimeDirectory,
1200
+ "--cache",
1201
+ npmCacheDirectory,
1202
+ "--no-audit",
1203
+ "--no-fund",
1204
+ "--omit=dev",
1205
+ signerPackageSpec(),
1206
+ sdkPackageSpec()
1207
+ ];
1208
+ try {
1209
+ if (runCommand) await runCommand("npm", args);
1210
+ else await execFileAsync2("npm", args, { timeout: 12e4, maxBuffer: 1024 * 1024 });
1211
+ } catch (err) {
1212
+ throw new Error(
1213
+ `Could not install local Haven signer runtime ${signerPackageSpec()}: ${err instanceof Error ? err.message : String(err)}`
1214
+ );
1215
+ }
1216
+ }
1217
+ async function installedRuntimeMatches2(runtimeDirectory, cliPath) {
1218
+ try {
1219
+ await assertFileExists2(cliPath, "local Haven signer CLI");
1220
+ const [signerPackage, sdkPackage] = await Promise.all([
1221
+ readPackageJson2(join(runtimeDirectory, "node_modules", "@haven_ai", "signer", "package.json")),
1222
+ readPackageJson2(join(runtimeDirectory, "node_modules", "@haven_ai", "sdk", "package.json"))
1223
+ ]);
1224
+ return signerPackage.version === MCP_RUNTIME_MANIFEST.signerVersion && sdkPackage.version === MCP_RUNTIME_MANIFEST.sdkVersion;
1225
+ } catch {
1226
+ return false;
1227
+ }
1228
+ }
1229
+ async function readPackageJson2(path) {
1230
+ return JSON.parse(await readFile(path, "utf8"));
1231
+ }
1232
+ async function writeWrapper2(input) {
1233
+ await mkdir(dirname(input.wrapperPath), { recursive: true, mode: 448 });
1234
+ await chmod(dirname(input.wrapperPath), 448).catch(() => void 0);
1235
+ const source = [
1236
+ "#!/usr/bin/env node",
1237
+ "import { spawn } from 'node:child_process'",
1238
+ "",
1239
+ `const cliPath = ${JSON.stringify(input.cliPath)}`,
1240
+ `const signerPath = ${JSON.stringify(input.signerPath)}`,
1241
+ "",
1242
+ "const child = spawn(process.execPath, [cliPath, '--credentials', signerPath, ...process.argv.slice(2)], {",
1243
+ " stdio: 'inherit',",
1244
+ "})",
1245
+ "",
1246
+ "child.on('exit', (code, signal) => {",
1247
+ " if (signal) process.kill(process.pid, signal)",
1248
+ " else process.exit(code ?? 1)",
1249
+ "})",
1250
+ ""
1251
+ ].join("\n");
1252
+ await writeFile(input.wrapperPath, source, { mode: 448 });
1253
+ await chmod(input.wrapperPath, 448).catch(() => void 0);
1254
+ }
1255
+ async function writeRuntimeSidecar2(input) {
1256
+ const value = {
1257
+ signer_package: MCP_RUNTIME_MANIFEST.signerPackage,
1258
+ signer_version: MCP_RUNTIME_MANIFEST.signerVersion,
1259
+ sdk_package: MCP_RUNTIME_MANIFEST.sdkPackage,
1260
+ sdk_version: MCP_RUNTIME_MANIFEST.sdkVersion,
1261
+ wrapper_path: input.wrapperPath,
1262
+ runtime_directory: input.runtimeDirectory,
1263
+ npm_cache_directory: input.npmCacheDirectory,
1264
+ cli_path: input.cliPath
1265
+ };
1266
+ await writeFile(input.path, `${JSON.stringify(value, null, 2)}
1267
+ `, { mode: 384 });
1268
+ await chmod(input.path, 384).catch(() => void 0);
1269
+ }
1270
+ async function assertFileExists2(path, label) {
1271
+ try {
1272
+ await access(path);
1273
+ } catch {
1274
+ throw new Error(`Missing ${label}: ${path}`);
1275
+ }
1276
+ }
1221
1277
  async function acknowledgeLocalSignerConsent(signerPath, log) {
1222
1278
  try {
1223
1279
  const input = await buildSignerConsentInput(signerPath);
@@ -1289,7 +1345,7 @@ function writeLogChunk2(log, chunk) {
1289
1345
  }
1290
1346
 
1291
1347
  // src/runtime-install.ts
1292
- var execFileAsync2 = promisify(execFile);
1348
+ var execFileAsync3 = promisify(execFile);
1293
1349
  async function installRuntime(input, deps = {}) {
1294
1350
  const runtime = normalizeRuntime(input.runtime, deps.env);
1295
1351
  const profile = runtimeProfile(runtime, deps.env);
@@ -1350,7 +1406,20 @@ async function installRuntime(input, deps = {}) {
1350
1406
  ]
1351
1407
  };
1352
1408
  }
1353
- const configResult = runtime === "claude-code" ? localRuntime ? await configureClaudeCode(deps, localRuntimeInstall?.command ?? "") : await configureClaudeCodeHosted(deps, input) : await writeRuntimeConfig({
1409
+ let signerCommand;
1410
+ if (!localRuntime) {
1411
+ try {
1412
+ const signerRuntime = await prepareSignerForRuntime(input, deps);
1413
+ signerCommand = { command: signerRuntime.command, args: signerRuntime.args };
1414
+ consentMessages.push(...signerRuntime.messages);
1415
+ } catch (err) {
1416
+ consentMessages.push(
1417
+ `Could not pre-install the local Haven signer; falling back to npx launch: ${err instanceof Error ? err.message : String(err)}`
1418
+ );
1419
+ }
1420
+ }
1421
+ const signerRuntimePrepared = localRuntime ? void 0 : signerCommand !== void 0;
1422
+ const configResult = runtime === "claude-code" ? localRuntime ? await configureClaudeCode(deps, localRuntimeInstall?.command ?? "") : await configureClaudeCodeHosted(deps, input, signerCommand) : await writeRuntimeConfig({
1354
1423
  runtime,
1355
1424
  hostedMcpUrl: input.hostedMcpUrl,
1356
1425
  apiKey: input.apiKey,
@@ -1358,6 +1427,7 @@ async function installRuntime(input, deps = {}) {
1358
1427
  signerPath: input.signerPath,
1359
1428
  credentialDirectory: input.credentialDirectory,
1360
1429
  localMcpCommand: localRuntimeInstall?.command,
1430
+ signerCommand,
1361
1431
  homeDir: deps.homeDir,
1362
1432
  mode: localRuntime ? "local" : "hosted"
1363
1433
  });
@@ -1389,6 +1459,7 @@ async function installRuntime(input, deps = {}) {
1389
1459
  localMcpAcknowledged: localMcpConsent?.acknowledged,
1390
1460
  activationCommand: configResult.activationCommand,
1391
1461
  skillInstalled: skillInstall?.installed,
1462
+ signerRuntimePrepared,
1392
1463
  messages: [...consentMessages, ...localRuntimeInstall?.messages ?? [], ...configResult.messages, ...localProbeMessages, ...skillInstall?.messages ?? []]
1393
1464
  };
1394
1465
  }
@@ -1445,7 +1516,7 @@ async function configureClaudeCode(deps, localMcpCommand) {
1445
1516
  };
1446
1517
  }
1447
1518
  }
1448
- async function configureClaudeCodeHosted(deps, input) {
1519
+ async function configureClaudeCodeHosted(deps, input, signerCommand) {
1449
1520
  const runCommand = deps.runCommand ?? defaultRunCommand;
1450
1521
  const hostedJson = JSON.stringify({
1451
1522
  type: "http",
@@ -1454,8 +1525,8 @@ async function configureClaudeCodeHosted(deps, input) {
1454
1525
  });
1455
1526
  const signerJson = JSON.stringify({
1456
1527
  type: "stdio",
1457
- command: "npx",
1458
- args: ["-y", signerPackageSpec(), "--credentials", input.signerPath],
1528
+ command: signerCommand?.command ?? "npx",
1529
+ args: signerCommand?.args ?? ["-y", signerPackageSpec(), "--credentials", input.signerPath],
1459
1530
  env: {}
1460
1531
  });
1461
1532
  try {
@@ -1496,7 +1567,7 @@ async function configureClaudeCodeHosted(deps, input) {
1496
1567
  }
1497
1568
  }
1498
1569
  async function defaultRunCommand(command, args) {
1499
- await execFileAsync2(command, args, { timeout: 1e4 });
1570
+ await execFileAsync3(command, args, { timeout: 1e4 });
1500
1571
  }
1501
1572
  async function installClaudeSkill(homeDir) {
1502
1573
  try {
@@ -1579,6 +1650,14 @@ async function prepareRuntimeForLocalMcp(input, deps) {
1579
1650
  homeDir: deps.homeDir
1580
1651
  });
1581
1652
  }
1653
+ async function prepareSignerForRuntime(input, deps) {
1654
+ const prepare = deps.prepareSignerRuntime ?? ((runtimeInput) => prepareSignerRuntime(runtimeInput, { runCommand: deps.runCommand }));
1655
+ return prepare({
1656
+ credentialDirectory: input.credentialDirectory,
1657
+ signerPath: input.signerPath,
1658
+ homeDir: deps.homeDir
1659
+ });
1660
+ }
1582
1661
  async function runLocalMcpProbe(runtimeInstall, deps) {
1583
1662
  const probe = deps.probeLocalMcpTools ?? probeLocalMcpTools;
1584
1663
  try {
@@ -1595,7 +1674,7 @@ function localRuntimePrepareErrorCode(err) {
1595
1674
  }
1596
1675
 
1597
1676
  // src/runtime.ts
1598
- var CONNECTOR_VERSION = "0.1.11-alpha.0";
1677
+ var CONNECTOR_VERSION = "0.1.13-alpha.0";
1599
1678
  async function runConnect(options, deps = {}) {
1600
1679
  const connectorVersion = options.connectorVersion ?? CONNECTOR_VERSION;
1601
1680
  const api = deps.api ?? createConnectApiClient(options.apiBaseUrl);
@@ -1660,10 +1739,18 @@ async function runConnect(options, deps = {}) {
1660
1739
  })),
1661
1740
  apiUrl: options.apiBaseUrl,
1662
1741
  hostedMcpUrl: registration.hosted_mcp_url,
1742
+ x402BindingSigner: setup.x402_binding_signer ?? void 0,
1663
1743
  warn: log
1664
1744
  });
1665
1745
  log(`Stored Haven identity credential locally: ${credentialPaths.identityPath}`);
1666
1746
  log(`Stored local signer credential locally: ${credentialPaths.signerPath}`);
1747
+ if (setup.x402_binding_signer) {
1748
+ log("Configured x402 binding signer for the local signer.");
1749
+ } else {
1750
+ log(
1751
+ "Warning: Haven did not provide an x402 binding signer, so x402 payments will not sign until HAVEN_X402_BINDING_SIGNER is set for the signer. Non-x402 payments are unaffected."
1752
+ );
1753
+ }
1667
1754
  const runtimeInstall = await runRuntimeInstall({
1668
1755
  runtime: options.runtime,
1669
1756
  hostedMcpUrl: registration.hosted_mcp_url,
@@ -1826,6 +1913,6 @@ function requireValue(argv, index, option) {
1826
1913
  return value;
1827
1914
  }
1828
1915
 
1829
- export { CONNECTOR_VERSION, MCP_RUNTIME_MANIFEST, createConnectApiClient, defaultAgentDirectory, delegateKeyFromPrivateKey, generateDelegateKey, helpText, installRuntime, mcpPackageSpec, normalizeRuntime, parseArgs, redactSecrets, runConnect, runtimeInstallCapabilities, runtimeProfile, sdkPackageSpec, shortAddress, signerPackageSpec, writeCredentialFiles };
1916
+ export { CONNECTOR_VERSION, MCP_RUNTIME_MANIFEST, createConnectApiClient, defaultAgentDirectory, delegateKeyFromPrivateKey, generateDelegateKey, helpText, installRuntime, mcpPackageSpec, normalizeRuntime, parseArgs, prepareSignerRuntime, redactSecrets, runConnect, runtimeInstallCapabilities, runtimeProfile, sdkPackageSpec, shortAddress, signerPackageSpec, writeCredentialFiles };
1830
1917
  //# sourceMappingURL=index.js.map
1831
1918
  //# sourceMappingURL=index.js.map