@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/cli.d.cts CHANGED
@@ -4,5 +4,12 @@ interface CliIo {
4
4
  stderr: (message: string) => void;
5
5
  }
6
6
  declare function runCli(argv: string[], io?: CliIo): Promise<number>;
7
+ /**
8
+ * npm executes package bins through a `node_modules/.bin` symlink. Node keeps
9
+ * that symlink in `process.argv[1]`, whereas `import.meta.url` identifies the
10
+ * real module path. Resolve both sides before comparing so a published
11
+ * `haven-connect` bin starts, while an ordinary `runCli` import remains inert.
12
+ */
13
+ declare function isCliEntrypoint(argvPath?: string | undefined, moduleUrl?: string): boolean;
7
14
 
8
- export { type CliIo, runCli };
15
+ export { type CliIo, isCliEntrypoint, runCli };
package/dist/cli.d.ts CHANGED
@@ -4,5 +4,12 @@ interface CliIo {
4
4
  stderr: (message: string) => void;
5
5
  }
6
6
  declare function runCli(argv: string[], io?: CliIo): Promise<number>;
7
+ /**
8
+ * npm executes package bins through a `node_modules/.bin` symlink. Node keeps
9
+ * that symlink in `process.argv[1]`, whereas `import.meta.url` identifies the
10
+ * real module path. Resolve both sides before comparing so a published
11
+ * `haven-connect` bin starts, while an ordinary `runCli` import remains inert.
12
+ */
13
+ declare function isCliEntrypoint(argvPath?: string | undefined, moduleUrl?: string): boolean;
7
14
 
8
- export { type CliIo, runCli };
15
+ export { type CliIo, isCliEntrypoint, runCli };
package/dist/cli.js CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { pathToFileURL } from 'url';
2
+ import { realpathSync } from 'fs';
3
+ import { fileURLToPath, pathToFileURL } from 'url';
4
+ import { HAVEN_MINIMUM_NODE_VERSION, isSupportedNodeVersion, unsupportedNodeVersionMessage, SKILL_FOLDER_NAME, HAVEN_SKILL_MD, resolveTokenFromAddress } from '@haven_ai/sdk';
3
5
  import crypto from 'crypto';
4
6
  import { Wallet } from 'ethers';
5
7
  import { mkdir, rm, chmod, access, writeFile, readFile, unlink } from 'fs/promises';
@@ -9,7 +11,6 @@ import { execFile, spawn } from 'child_process';
9
11
  import { promisify } from 'util';
10
12
  import { parseDocument, isMap, stringify } from 'yaml';
11
13
  import { registeredToolNames, MCP_VERSION, ensureConsent, computeConsentHash, loadCredentials, consentInputFromClient } from '@haven_ai/mcp';
12
- import { HAVEN_MINIMUM_NODE_VERSION, isSupportedNodeVersion, unsupportedNodeVersionMessage, SKILL_FOLDER_NAME, HAVEN_SKILL_MD } from '@haven_ai/sdk';
13
14
  import { ensureSignerConsent, computeSignerConsentHash, loadSignerCredentials, createEdgeSigner, toolSchemas } from '@haven_ai/signer';
14
15
 
15
16
  // src/api.ts
@@ -42,6 +43,10 @@ function createConnectApiClient(baseUrl, fetchImpl = fetch) {
42
43
  }
43
44
  })
44
45
  }),
46
+ getConnectorStatus: (setupId, apiKey) => request(fetchImpl, `${root}/agent-connection-setups/${encodeURIComponent(setupId)}/connector-status`, {
47
+ method: "GET",
48
+ headers: { Authorization: `Bearer ${apiKey}` }
49
+ }),
45
50
  updateInstallStatus: async (setupId, apiKey, input) => {
46
51
  await request(fetchImpl, `${root}/agent-connection-setups/${encodeURIComponent(setupId)}/install-status`, {
47
52
  method: "POST",
@@ -68,6 +73,14 @@ function createConnectApiClient(baseUrl, fetchImpl = fetch) {
68
73
  }
69
74
  };
70
75
  }
76
+ var ConnectRequestError = class extends Error {
77
+ constructor(message, status) {
78
+ super(message);
79
+ this.status = status;
80
+ this.name = "ConnectRequestError";
81
+ }
82
+ status;
83
+ };
71
84
  async function request(fetchImpl, url, init) {
72
85
  const response = await fetchImpl(url, {
73
86
  ...init,
@@ -80,7 +93,7 @@ async function request(fetchImpl, url, init) {
80
93
  const body = text ? JSON.parse(text) : null;
81
94
  if (!response.ok) {
82
95
  const message = body?.error ?? body?.message ?? `${response.status} ${response.statusText}`;
83
- throw new Error(`Haven setup request failed: ${message}`);
96
+ throw new ConnectRequestError(`Haven setup request failed: ${message}`, response.status);
84
97
  }
85
98
  return body;
86
99
  }
@@ -233,9 +246,9 @@ var MCP_RUNTIME_MANIFEST = {
233
246
  mcpPackage: "@haven_ai/mcp",
234
247
  mcpVersion: MCP_VERSION,
235
248
  sdkPackage: "@haven_ai/sdk",
236
- sdkVersion: "0.1.23-alpha.0",
249
+ sdkVersion: "0.1.23-alpha.2",
237
250
  signerPackage: "@haven_ai/signer",
238
- signerVersion: "0.1.23-alpha.0",
251
+ signerVersion: "0.1.23-alpha.2",
239
252
  // Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
240
253
  // while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
241
254
  // so the guard that was supposed to enforce the floor waved Node v23 through
@@ -1934,7 +1947,7 @@ function localRuntimePrepareErrorCode(err) {
1934
1947
  }
1935
1948
 
1936
1949
  // src/runtime.ts
1937
- var CONNECTOR_VERSION = "0.1.23-alpha.0";
1950
+ var CONNECTOR_VERSION = "0.1.23-alpha.2";
1938
1951
  var CONNECT_OUTCOME_SCHEMA_VERSION = 1;
1939
1952
  async function runConnect(options, deps = {}) {
1940
1953
  assertSupportedNodeVersion(deps.nodeVersion, MCP_RUNTIME_MANIFEST.minimumNodeVersion);
@@ -2048,7 +2061,6 @@ async function runConnect(options, deps = {}) {
2048
2061
  } else {
2049
2062
  log("Haven setup on this machine is complete.");
2050
2063
  }
2051
- printNextSteps(runtimeInstall, log);
2052
2064
  try {
2053
2065
  await api.updateInstallStatus(registration.setup_id, localApiKey, {
2054
2066
  runtime: runtimeInstall.runtime,
@@ -2071,6 +2083,10 @@ async function runConnect(options, deps = {}) {
2071
2083
  } catch (err) {
2072
2084
  log(`Could not report install status to Haven: ${err instanceof Error ? err.message : String(err)}`);
2073
2085
  }
2086
+ if (options.waitForApproval !== false && !runtimeInstall.errorCode) {
2087
+ await waitForBudgetApproval(api, registration.setup_id, localApiKey, log, options.approvalWait);
2088
+ }
2089
+ printNextSteps(runtimeInstall, log);
2074
2090
  return {
2075
2091
  setupId: registration.setup_id,
2076
2092
  agentId: registration.agent_id,
@@ -2186,6 +2202,61 @@ function printRuntimeInstall(result, log) {
2186
2202
  log("Local Haven signer still needs runtime setup.");
2187
2203
  }
2188
2204
  }
2205
+ function formatAtomicAmount(atomic, decimals) {
2206
+ const s = atomic.toString().padStart(decimals + 1, "0");
2207
+ const intPart = s.slice(0, s.length - decimals) || "0";
2208
+ const fracPart = s.slice(s.length - decimals).replace(/0+$/, "");
2209
+ return fracPart ? `${intPart}.${fracPart}` : intPart;
2210
+ }
2211
+ function describeResetPeriod(resetPeriodMin) {
2212
+ if (resetPeriodMin === 1440) return "per day";
2213
+ if (resetPeriodMin === 60) return "per hour";
2214
+ if (resetPeriodMin === 0) return "with no automatic reset";
2215
+ return `per ${resetPeriodMin} minutes`;
2216
+ }
2217
+ function describeApprovedBudget(budget) {
2218
+ const token = resolveTokenFromAddress(budget.token_address);
2219
+ const amount = token ? `${formatAtomicAmount(BigInt(budget.amount), token.decimals)} ${budget.token_symbol}` : `${budget.amount} ${budget.token_symbol} (atomic units)`;
2220
+ return `${amount} ${describeResetPeriod(budget.reset_period_min)}`;
2221
+ }
2222
+ async function waitForBudgetApproval(api, setupId, apiKey, log, options = {}) {
2223
+ const intervalMs = options.intervalMs ?? 5e3;
2224
+ const timeoutMs = options.timeoutMs ?? 18e4;
2225
+ const sleep = options.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
2226
+ const maxPolls = Math.max(1, Math.floor(timeoutMs / intervalMs));
2227
+ const remindEvery = Math.max(1, Math.floor(3e4 / intervalMs));
2228
+ log("Registered with Haven \u2014 waiting for you to approve the budget in the dashboard\u2026");
2229
+ for (let i = 0; i < maxPolls; i++) {
2230
+ await sleep(intervalMs);
2231
+ let status;
2232
+ try {
2233
+ status = await api.getConnectorStatus(setupId, apiKey);
2234
+ } catch (err) {
2235
+ if (err instanceof ConnectRequestError && (err.status === 401 || err.status === 404)) {
2236
+ log("This setup ended in Haven \u2014 start a fresh connection from the dashboard when ready.");
2237
+ return "ended";
2238
+ }
2239
+ continue;
2240
+ }
2241
+ if (status.status === "active") {
2242
+ log(
2243
+ 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."
2244
+ );
2245
+ return "approved";
2246
+ }
2247
+ if (status.status === "cancelled" || status.status === "expired" || status.status === "failed") {
2248
+ log(`This setup ended in Haven (${status.status}) \u2014 start a fresh connection from the dashboard when ready.`);
2249
+ return "ended";
2250
+ }
2251
+ if ((i + 1) % remindEvery === 0) {
2252
+ log("Still waiting for budget approval in Haven\u2026");
2253
+ }
2254
+ }
2255
+ log(
2256
+ "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."
2257
+ );
2258
+ return "pending";
2259
+ }
2189
2260
  function completionHandoffLines(result) {
2190
2261
  if (result.errorCode === "manual_runtime_setup_required") {
2191
2262
  return [
@@ -2322,11 +2393,14 @@ async function runCli(argv, io = {
2322
2393
  return 0;
2323
2394
  }
2324
2395
  try {
2325
- const result = await runConnect(parsed.options, {
2326
- log: (message) => (parsed.json ? io.stderr : io.stdout)(`${message}
2396
+ const result = await runConnect(
2397
+ { ...parsed.options, waitForApproval: !parsed.json },
2398
+ {
2399
+ log: (message) => (parsed.json ? io.stderr : io.stdout)(`${message}
2327
2400
  `),
2328
- redactPaths: parsed.json
2329
- });
2401
+ redactPaths: parsed.json
2402
+ }
2403
+ );
2330
2404
  if (parsed.json) io.stdout(`${JSON.stringify(result.outcome)}
2331
2405
  `);
2332
2406
  return 0;
@@ -2345,8 +2419,16 @@ async function main() {
2345
2419
  const exitCode = await runCli(process.argv.slice(2));
2346
2420
  if (exitCode !== 0) process.exitCode = exitCode;
2347
2421
  }
2348
- if (process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url) void main();
2422
+ function isCliEntrypoint(argvPath = process.argv[1], moduleUrl = import.meta.url) {
2423
+ if (!argvPath) return false;
2424
+ try {
2425
+ return realpathSync(argvPath) === realpathSync(fileURLToPath(moduleUrl));
2426
+ } catch {
2427
+ return pathToFileURL(argvPath).href === moduleUrl;
2428
+ }
2429
+ }
2430
+ if (isCliEntrypoint()) void main();
2349
2431
 
2350
- export { runCli };
2432
+ export { isCliEntrypoint, runCli };
2351
2433
  //# sourceMappingURL=cli.js.map
2352
2434
  //# sourceMappingURL=cli.js.map