@haven_ai/connect 0.1.23-alpha.0 → 0.1.23-alpha.2

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
@@ -4,6 +4,23 @@ interface ConnectApiClient {
4
4
  resolveSetup(input: ResolveSetupInput): Promise<ResolvedSetup>;
5
5
  registerSetup(input: RegisterSetupInput): Promise<RegisterSetupResponse>;
6
6
  updateInstallStatus(setupId: string, apiKey: string, input: UpdateInstallStatusInput): Promise<void>;
7
+ /**
8
+ * #1377 D: the narrow status read the connector polls after registering,
9
+ * authenticated with the agent API key it just minted (works while the
10
+ * agent is still `setup_pending` — the endpoint exists for exactly that
11
+ * window). Returns status plus the approved budget once approval lands;
12
+ * never any secret material.
13
+ */
14
+ getConnectorStatus(setupId: string, apiKey: string): Promise<ConnectorStatusResponse>;
15
+ }
16
+ interface ConnectorStatusResponse {
17
+ status: string;
18
+ approved_budget: {
19
+ token_symbol: string;
20
+ token_address: string;
21
+ amount: string;
22
+ reset_period_min: number;
23
+ } | null;
7
24
  }
8
25
  interface ResolveSetupInput {
9
26
  setupToken: string;
@@ -268,7 +285,7 @@ declare function runtimeInstallCapabilities(runtime: string | undefined, env?: N
268
285
  restartRequired: boolean;
269
286
  };
270
287
 
271
- declare const CONNECTOR_VERSION = "0.1.23-alpha.0";
288
+ declare const CONNECTOR_VERSION = "0.1.23-alpha.2";
272
289
  interface ConnectOptions {
273
290
  setupToken: string;
274
291
  apiBaseUrl: string;
@@ -279,6 +296,14 @@ interface ConnectOptions {
279
296
  ackSigner?: boolean;
280
297
  ackLocalTools?: boolean;
281
298
  localMcp?: boolean;
299
+ /**
300
+ * #1377 D: keep the process alive after registering and poll for the
301
+ * user's budget approval (default). Set false for structured/automation
302
+ * runs (--json) where prompt output emission matters more than narration.
303
+ */
304
+ waitForApproval?: boolean;
305
+ /** Test/injection overrides for the approval poll cadence and clock. */
306
+ approvalWait?: ApprovalWaitOptions;
282
307
  }
283
308
  /** The stable machine-readable result emitted by `haven-connect --json`. */
284
309
  declare const CONNECT_OUTCOME_SCHEMA_VERSION: 1;
@@ -349,6 +374,20 @@ declare function completionOutcome(input: {
349
374
  * filesystem detail, while this public contract must remain safe to serialize.
350
375
  */
351
376
  declare function failedConnectOutcome(runtimeHint: string | undefined, error: unknown): ConnectOutcome;
377
+ /**
378
+ * #1377 D: after registering, the connector no longer goes dead while the
379
+ * user approves in Haven — it polls the narrow connector-status endpoint
380
+ * (agent-API-key auth, works during `setup_pending`) and narrates progress in
381
+ * the flow's voice, ending in a concrete celebration naming the granted
382
+ * authority. Bounds (stated per the issue): poll every 5 s, give up after
383
+ * 3 minutes — the connector ALWAYS terminates on its own; a timeout is a
384
+ * clean exit with guidance, never a hang. Injectable clock/cadence for tests.
385
+ */
386
+ interface ApprovalWaitOptions {
387
+ intervalMs?: number;
388
+ timeoutMs?: number;
389
+ sleep?: (ms: number) => Promise<void>;
390
+ }
352
391
 
353
392
  interface ParsedCli {
354
393
  options: ConnectOptions;
@@ -363,11 +402,11 @@ declare function shortAddress(address: string): string;
363
402
 
364
403
  declare const MCP_RUNTIME_MANIFEST: {
365
404
  readonly mcpPackage: "@haven_ai/mcp";
366
- readonly mcpVersion: "0.1.23-alpha.0";
405
+ readonly mcpVersion: "0.1.23-alpha.2";
367
406
  readonly sdkPackage: "@haven_ai/sdk";
368
- readonly sdkVersion: "0.1.23-alpha.0";
407
+ readonly sdkVersion: "0.1.23-alpha.2";
369
408
  readonly signerPackage: "@haven_ai/signer";
370
- readonly signerVersion: "0.1.23-alpha.0";
409
+ readonly signerVersion: "0.1.23-alpha.2";
371
410
  readonly minimumNodeVersion: "22.0.0";
372
411
  readonly supportedClients: readonly ["codex-cli", "codex-desktop", "claude-code"];
373
412
  readonly requiredTools: readonly string[];
package/dist/index.d.ts CHANGED
@@ -4,6 +4,23 @@ interface ConnectApiClient {
4
4
  resolveSetup(input: ResolveSetupInput): Promise<ResolvedSetup>;
5
5
  registerSetup(input: RegisterSetupInput): Promise<RegisterSetupResponse>;
6
6
  updateInstallStatus(setupId: string, apiKey: string, input: UpdateInstallStatusInput): Promise<void>;
7
+ /**
8
+ * #1377 D: the narrow status read the connector polls after registering,
9
+ * authenticated with the agent API key it just minted (works while the
10
+ * agent is still `setup_pending` — the endpoint exists for exactly that
11
+ * window). Returns status plus the approved budget once approval lands;
12
+ * never any secret material.
13
+ */
14
+ getConnectorStatus(setupId: string, apiKey: string): Promise<ConnectorStatusResponse>;
15
+ }
16
+ interface ConnectorStatusResponse {
17
+ status: string;
18
+ approved_budget: {
19
+ token_symbol: string;
20
+ token_address: string;
21
+ amount: string;
22
+ reset_period_min: number;
23
+ } | null;
7
24
  }
8
25
  interface ResolveSetupInput {
9
26
  setupToken: string;
@@ -268,7 +285,7 @@ declare function runtimeInstallCapabilities(runtime: string | undefined, env?: N
268
285
  restartRequired: boolean;
269
286
  };
270
287
 
271
- declare const CONNECTOR_VERSION = "0.1.23-alpha.0";
288
+ declare const CONNECTOR_VERSION = "0.1.23-alpha.2";
272
289
  interface ConnectOptions {
273
290
  setupToken: string;
274
291
  apiBaseUrl: string;
@@ -279,6 +296,14 @@ interface ConnectOptions {
279
296
  ackSigner?: boolean;
280
297
  ackLocalTools?: boolean;
281
298
  localMcp?: boolean;
299
+ /**
300
+ * #1377 D: keep the process alive after registering and poll for the
301
+ * user's budget approval (default). Set false for structured/automation
302
+ * runs (--json) where prompt output emission matters more than narration.
303
+ */
304
+ waitForApproval?: boolean;
305
+ /** Test/injection overrides for the approval poll cadence and clock. */
306
+ approvalWait?: ApprovalWaitOptions;
282
307
  }
283
308
  /** The stable machine-readable result emitted by `haven-connect --json`. */
284
309
  declare const CONNECT_OUTCOME_SCHEMA_VERSION: 1;
@@ -349,6 +374,20 @@ declare function completionOutcome(input: {
349
374
  * filesystem detail, while this public contract must remain safe to serialize.
350
375
  */
351
376
  declare function failedConnectOutcome(runtimeHint: string | undefined, error: unknown): ConnectOutcome;
377
+ /**
378
+ * #1377 D: after registering, the connector no longer goes dead while the
379
+ * user approves in Haven — it polls the narrow connector-status endpoint
380
+ * (agent-API-key auth, works during `setup_pending`) and narrates progress in
381
+ * the flow's voice, ending in a concrete celebration naming the granted
382
+ * authority. Bounds (stated per the issue): poll every 5 s, give up after
383
+ * 3 minutes — the connector ALWAYS terminates on its own; a timeout is a
384
+ * clean exit with guidance, never a hang. Injectable clock/cadence for tests.
385
+ */
386
+ interface ApprovalWaitOptions {
387
+ intervalMs?: number;
388
+ timeoutMs?: number;
389
+ sleep?: (ms: number) => Promise<void>;
390
+ }
352
391
 
353
392
  interface ParsedCli {
354
393
  options: ConnectOptions;
@@ -363,11 +402,11 @@ declare function shortAddress(address: string): string;
363
402
 
364
403
  declare const MCP_RUNTIME_MANIFEST: {
365
404
  readonly mcpPackage: "@haven_ai/mcp";
366
- readonly mcpVersion: "0.1.23-alpha.0";
405
+ readonly mcpVersion: "0.1.23-alpha.2";
367
406
  readonly sdkPackage: "@haven_ai/sdk";
368
- readonly sdkVersion: "0.1.23-alpha.0";
407
+ readonly sdkVersion: "0.1.23-alpha.2";
369
408
  readonly signerPackage: "@haven_ai/signer";
370
- readonly signerVersion: "0.1.23-alpha.0";
409
+ readonly signerVersion: "0.1.23-alpha.2";
371
410
  readonly minimumNodeVersion: "22.0.0";
372
411
  readonly supportedClients: readonly ["codex-cli", "codex-desktop", "claude-code"];
373
412
  readonly requiredTools: readonly string[];
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { HAVEN_MINIMUM_NODE_VERSION, isSupportedNodeVersion, unsupportedNodeVersionMessage, SKILL_FOLDER_NAME, HAVEN_SKILL_MD, resolveTokenFromAddress } from '@haven_ai/sdk';
1
2
  import crypto from 'crypto';
2
3
  import { Wallet } from 'ethers';
3
4
  import { mkdir, rm, chmod, access, writeFile, readFile, unlink } from 'fs/promises';
@@ -7,9 +8,9 @@ import { execFile, spawn } from 'child_process';
7
8
  import { promisify } from 'util';
8
9
  import { parseDocument, isMap, stringify } from 'yaml';
9
10
  import { registeredToolNames, MCP_VERSION, ensureConsent, computeConsentHash, loadCredentials, consentInputFromClient } from '@haven_ai/mcp';
10
- import { HAVEN_MINIMUM_NODE_VERSION, isSupportedNodeVersion, unsupportedNodeVersionMessage, SKILL_FOLDER_NAME, HAVEN_SKILL_MD } from '@haven_ai/sdk';
11
11
  import { ensureSignerConsent, computeSignerConsentHash, loadSignerCredentials, createEdgeSigner, toolSchemas } from '@haven_ai/signer';
12
- import { pathToFileURL } from 'url';
12
+ import { realpathSync } from 'fs';
13
+ import { fileURLToPath, pathToFileURL } from 'url';
13
14
 
14
15
  // src/api.ts
15
16
  function createConnectApiClient(baseUrl, fetchImpl = fetch) {
@@ -41,6 +42,10 @@ function createConnectApiClient(baseUrl, fetchImpl = fetch) {
41
42
  }
42
43
  })
43
44
  }),
45
+ getConnectorStatus: (setupId, apiKey) => request(fetchImpl, `${root}/agent-connection-setups/${encodeURIComponent(setupId)}/connector-status`, {
46
+ method: "GET",
47
+ headers: { Authorization: `Bearer ${apiKey}` }
48
+ }),
44
49
  updateInstallStatus: async (setupId, apiKey, input) => {
45
50
  await request(fetchImpl, `${root}/agent-connection-setups/${encodeURIComponent(setupId)}/install-status`, {
46
51
  method: "POST",
@@ -67,6 +72,14 @@ function createConnectApiClient(baseUrl, fetchImpl = fetch) {
67
72
  }
68
73
  };
69
74
  }
75
+ var ConnectRequestError = class extends Error {
76
+ constructor(message, status) {
77
+ super(message);
78
+ this.status = status;
79
+ this.name = "ConnectRequestError";
80
+ }
81
+ status;
82
+ };
70
83
  async function request(fetchImpl, url, init) {
71
84
  const response = await fetchImpl(url, {
72
85
  ...init,
@@ -79,7 +92,7 @@ async function request(fetchImpl, url, init) {
79
92
  const body = text ? JSON.parse(text) : null;
80
93
  if (!response.ok) {
81
94
  const message = body?.error ?? body?.message ?? `${response.status} ${response.statusText}`;
82
- throw new Error(`Haven setup request failed: ${message}`);
95
+ throw new ConnectRequestError(`Haven setup request failed: ${message}`, response.status);
83
96
  }
84
97
  return body;
85
98
  }
@@ -232,9 +245,9 @@ var MCP_RUNTIME_MANIFEST = {
232
245
  mcpPackage: "@haven_ai/mcp",
233
246
  mcpVersion: MCP_VERSION,
234
247
  sdkPackage: "@haven_ai/sdk",
235
- sdkVersion: "0.1.23-alpha.0",
248
+ sdkVersion: "0.1.23-alpha.2",
236
249
  signerPackage: "@haven_ai/signer",
237
- signerVersion: "0.1.23-alpha.0",
250
+ signerVersion: "0.1.23-alpha.2",
238
251
  // Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
239
252
  // while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
240
253
  // so the guard that was supposed to enforce the floor waved Node v23 through
@@ -1933,7 +1946,7 @@ function localRuntimePrepareErrorCode(err) {
1933
1946
  }
1934
1947
 
1935
1948
  // src/runtime.ts
1936
- var CONNECTOR_VERSION = "0.1.23-alpha.0";
1949
+ var CONNECTOR_VERSION = "0.1.23-alpha.2";
1937
1950
  var CONNECT_OUTCOME_SCHEMA_VERSION = 1;
1938
1951
  async function runConnect(options, deps = {}) {
1939
1952
  assertSupportedNodeVersion(deps.nodeVersion, MCP_RUNTIME_MANIFEST.minimumNodeVersion);
@@ -2047,7 +2060,6 @@ async function runConnect(options, deps = {}) {
2047
2060
  } else {
2048
2061
  log("Haven setup on this machine is complete.");
2049
2062
  }
2050
- printNextSteps(runtimeInstall, log);
2051
2063
  try {
2052
2064
  await api.updateInstallStatus(registration.setup_id, localApiKey, {
2053
2065
  runtime: runtimeInstall.runtime,
@@ -2070,6 +2082,10 @@ async function runConnect(options, deps = {}) {
2070
2082
  } catch (err) {
2071
2083
  log(`Could not report install status to Haven: ${err instanceof Error ? err.message : String(err)}`);
2072
2084
  }
2085
+ if (options.waitForApproval !== false && !runtimeInstall.errorCode) {
2086
+ await waitForBudgetApproval(api, registration.setup_id, localApiKey, log, options.approvalWait);
2087
+ }
2088
+ printNextSteps(runtimeInstall, log);
2073
2089
  return {
2074
2090
  setupId: registration.setup_id,
2075
2091
  agentId: registration.agent_id,
@@ -2185,6 +2201,61 @@ function printRuntimeInstall(result, log) {
2185
2201
  log("Local Haven signer still needs runtime setup.");
2186
2202
  }
2187
2203
  }
2204
+ function formatAtomicAmount(atomic, decimals) {
2205
+ const s = atomic.toString().padStart(decimals + 1, "0");
2206
+ const intPart = s.slice(0, s.length - decimals) || "0";
2207
+ const fracPart = s.slice(s.length - decimals).replace(/0+$/, "");
2208
+ return fracPart ? `${intPart}.${fracPart}` : intPart;
2209
+ }
2210
+ function describeResetPeriod(resetPeriodMin) {
2211
+ if (resetPeriodMin === 1440) return "per day";
2212
+ if (resetPeriodMin === 60) return "per hour";
2213
+ if (resetPeriodMin === 0) return "with no automatic reset";
2214
+ return `per ${resetPeriodMin} minutes`;
2215
+ }
2216
+ function describeApprovedBudget(budget) {
2217
+ const token = resolveTokenFromAddress(budget.token_address);
2218
+ const amount = token ? `${formatAtomicAmount(BigInt(budget.amount), token.decimals)} ${budget.token_symbol}` : `${budget.amount} ${budget.token_symbol} (atomic units)`;
2219
+ return `${amount} ${describeResetPeriod(budget.reset_period_min)}`;
2220
+ }
2221
+ async function waitForBudgetApproval(api, setupId, apiKey, log, options = {}) {
2222
+ const intervalMs = options.intervalMs ?? 5e3;
2223
+ const timeoutMs = options.timeoutMs ?? 18e4;
2224
+ const sleep = options.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
2225
+ const maxPolls = Math.max(1, Math.floor(timeoutMs / intervalMs));
2226
+ const remindEvery = Math.max(1, Math.floor(3e4 / intervalMs));
2227
+ log("Registered with Haven \u2014 waiting for you to approve the budget in the dashboard\u2026");
2228
+ for (let i = 0; i < maxPolls; i++) {
2229
+ await sleep(intervalMs);
2230
+ let status;
2231
+ try {
2232
+ status = await api.getConnectorStatus(setupId, apiKey);
2233
+ } catch (err) {
2234
+ if (err instanceof ConnectRequestError && (err.status === 401 || err.status === 404)) {
2235
+ log("This setup ended in Haven \u2014 start a fresh connection from the dashboard when ready.");
2236
+ return "ended";
2237
+ }
2238
+ continue;
2239
+ }
2240
+ if (status.status === "active") {
2241
+ log(
2242
+ status.approved_budget ? `Budget approved \u{1F389} \u2014 I can now spend up to ${describeApprovedBudget(status.approved_budget)} from your Haven wallet.` : "Budget approved \u{1F389} \u2014 the agent can now spend within its Haven rules."
2243
+ );
2244
+ return "approved";
2245
+ }
2246
+ if (status.status === "cancelled" || status.status === "expired" || status.status === "failed") {
2247
+ log(`This setup ended in Haven (${status.status}) \u2014 start a fresh connection from the dashboard when ready.`);
2248
+ return "ended";
2249
+ }
2250
+ if ((i + 1) % remindEvery === 0) {
2251
+ log("Still waiting for budget approval in Haven\u2026");
2252
+ }
2253
+ }
2254
+ log(
2255
+ "Budget approval is still pending in Haven. Approve it in the dashboard whenever you are ready \u2014 the agent tools unlock the moment you do. Verify later with the read-only haven_get_agent tool."
2256
+ );
2257
+ return "pending";
2258
+ }
2188
2259
  function completionHandoffLines(result) {
2189
2260
  if (result.errorCode === "manual_runtime_setup_required") {
2190
2261
  return [
@@ -2319,11 +2390,14 @@ async function runCli(argv, io = {
2319
2390
  return 0;
2320
2391
  }
2321
2392
  try {
2322
- const result = await runConnect(parsed.options, {
2323
- log: (message) => (parsed.json ? io.stderr : io.stdout)(`${message}
2393
+ const result = await runConnect(
2394
+ { ...parsed.options, waitForApproval: !parsed.json },
2395
+ {
2396
+ log: (message) => (parsed.json ? io.stderr : io.stdout)(`${message}
2324
2397
  `),
2325
- redactPaths: parsed.json
2326
- });
2398
+ redactPaths: parsed.json
2399
+ }
2400
+ );
2327
2401
  if (parsed.json) io.stdout(`${JSON.stringify(result.outcome)}
2328
2402
  `);
2329
2403
  return 0;
@@ -2342,7 +2416,15 @@ async function main() {
2342
2416
  const exitCode = await runCli(process.argv.slice(2));
2343
2417
  if (exitCode !== 0) process.exitCode = exitCode;
2344
2418
  }
2345
- if (process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url) void main();
2419
+ function isCliEntrypoint(argvPath = process.argv[1], moduleUrl = import.meta.url) {
2420
+ if (!argvPath) return false;
2421
+ try {
2422
+ return realpathSync(argvPath) === realpathSync(fileURLToPath(moduleUrl));
2423
+ } catch {
2424
+ return pathToFileURL(argvPath).href === moduleUrl;
2425
+ }
2426
+ }
2427
+ if (isCliEntrypoint()) void main();
2346
2428
 
2347
2429
  export { CONNECTOR_VERSION, CONNECT_OUTCOME_SCHEMA_VERSION, MCP_RUNTIME_MANIFEST, completionOutcome, createConnectApiClient, defaultAgentDirectory, delegateKeyFromPrivateKey, failedConnectOutcome, generateDelegateKey, helpText, installRuntime, mcpPackageSpec, normalizeRuntime, parseArgs, prepareSignerRuntime, redactSecrets, runCli, runConnect, runtimeInstallCapabilities, runtimeProfile, sdkPackageSpec, shortAddress, signerPackageSpec, writeCredentialFiles };
2348
2430
  //# sourceMappingURL=index.js.map