@haven_ai/connect 0.1.0-alpha → 0.1.2-alpha
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/README.md +5 -1
- package/dist/cli.cjs +300 -50
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +300 -50
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +300 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.js +300 -50
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
package/dist/index.cjs
CHANGED
|
@@ -7,6 +7,8 @@ var os = require('os');
|
|
|
7
7
|
var path = require('path');
|
|
8
8
|
var child_process = require('child_process');
|
|
9
9
|
var util = require('util');
|
|
10
|
+
var mcp = require('@haven_ai/mcp');
|
|
11
|
+
var signer = require('@haven_ai/signer');
|
|
10
12
|
|
|
11
13
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
14
|
|
|
@@ -49,9 +51,14 @@ function createConnectApiClient(baseUrl, fetchImpl = fetch) {
|
|
|
49
51
|
body: JSON.stringify({
|
|
50
52
|
runtime: input.runtime,
|
|
51
53
|
connector_version: input.connectorVersion,
|
|
54
|
+
runtime_mcp_mode: input.runtimeMcpMode,
|
|
52
55
|
hosted_mcp_configured: input.hostedMcpConfigured,
|
|
53
56
|
local_signer_configured: input.localSignerConfigured,
|
|
57
|
+
local_mcp_configured: input.localMcpConfigured,
|
|
54
58
|
credential_files_written: input.credentialFilesWritten,
|
|
59
|
+
signer_acknowledged: input.signerAcknowledged,
|
|
60
|
+
local_mcp_acknowledged: input.localMcpAcknowledged,
|
|
61
|
+
activation_command_available: input.activationCommandAvailable,
|
|
55
62
|
probe_result: input.probeResult,
|
|
56
63
|
restart_required: input.restartRequired,
|
|
57
64
|
next_user_action: input.nextUserAction,
|
|
@@ -133,6 +140,7 @@ async function writeCredentialFiles(input) {
|
|
|
133
140
|
signerPath,
|
|
134
141
|
{
|
|
135
142
|
delegate_key: input.delegateKey,
|
|
143
|
+
delegate_address: input.delegateAddress,
|
|
136
144
|
agent_id: input.agentId,
|
|
137
145
|
safe_address: input.safeAddress,
|
|
138
146
|
chain_id: input.chainId,
|
|
@@ -152,6 +160,7 @@ async function writeCredentialFiles(input) {
|
|
|
152
160
|
network: input.network,
|
|
153
161
|
api_url: input.apiUrl,
|
|
154
162
|
hosted_mcp_url: input.hostedMcpUrl,
|
|
163
|
+
agent_budget: input.agentBudget,
|
|
155
164
|
note: "Haven API key identifies the agent only. It cannot spend without the local signer key and on-chain Haven wallet rules."
|
|
156
165
|
},
|
|
157
166
|
input.warn
|
|
@@ -212,6 +221,8 @@ async function writeRuntimeConfig(input) {
|
|
|
212
221
|
return {
|
|
213
222
|
hostedConfigured: false,
|
|
214
223
|
signerConfigured: false,
|
|
224
|
+
localMcpConfigured: false,
|
|
225
|
+
runtimeMcpMode: "manual",
|
|
215
226
|
target: "manual runtime setup",
|
|
216
227
|
changed: false,
|
|
217
228
|
restartRequired: true,
|
|
@@ -236,7 +247,7 @@ function buildHostedServer(hostedMcpUrl, apiKey, runtime) {
|
|
|
236
247
|
function buildSignerServer(signerPath, runtime) {
|
|
237
248
|
const server = {
|
|
238
249
|
command: "npx",
|
|
239
|
-
args: ["-y",
|
|
250
|
+
args: ["-y", signerPackageName(), "--credentials", signerPath]
|
|
240
251
|
};
|
|
241
252
|
if (runtime === "vscode") return { type: "stdio", ...server };
|
|
242
253
|
return server;
|
|
@@ -253,17 +264,13 @@ function mergeJsonMcpConfig(existingJson, serverRoot, hostedServer, signerServer
|
|
|
253
264
|
return `${JSON.stringify(config, null, 2)}
|
|
254
265
|
`;
|
|
255
266
|
}
|
|
256
|
-
function mergeCodexToml(existingToml,
|
|
267
|
+
function mergeCodexToml(existingToml, identityPath, signerPath) {
|
|
257
268
|
let next = removeTomlTable(removeTomlTable(existingToml, "mcp_servers.haven"), "mcp_servers.haven_signer");
|
|
258
269
|
next = next.trimEnd();
|
|
259
270
|
const block = [
|
|
260
271
|
"[mcp_servers.haven]",
|
|
261
|
-
`url = ${tomlString(hostedMcpUrl)}`,
|
|
262
|
-
'bearer_token_env_var = "HAVEN_TOKEN"',
|
|
263
|
-
"",
|
|
264
|
-
"[mcp_servers.haven_signer]",
|
|
265
272
|
'command = "npx"',
|
|
266
|
-
`args = ["-y", "
|
|
273
|
+
`args = ["-y", ${tomlString(localMcpPackageName())}, "--identity", ${tomlString(identityPath)}, "--signer", ${tomlString(signerPath)}]`
|
|
267
274
|
].join("\n");
|
|
268
275
|
return `${next ? `${next}
|
|
269
276
|
|
|
@@ -283,6 +290,8 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
|
|
|
283
290
|
return {
|
|
284
291
|
hostedConfigured: true,
|
|
285
292
|
signerConfigured: true,
|
|
293
|
+
localMcpConfigured: false,
|
|
294
|
+
runtimeMcpMode: "hosted_plus_signer",
|
|
286
295
|
target: configTargetLabel(input.runtime),
|
|
287
296
|
changed: existing !== merged,
|
|
288
297
|
restartRequired: input.runtime === "claude-desktop",
|
|
@@ -292,6 +301,8 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
|
|
|
292
301
|
return {
|
|
293
302
|
hostedConfigured: false,
|
|
294
303
|
signerConfigured: false,
|
|
304
|
+
localMcpConfigured: false,
|
|
305
|
+
runtimeMcpMode: "hosted_plus_signer",
|
|
295
306
|
target: configTargetLabel(input.runtime),
|
|
296
307
|
changed: false,
|
|
297
308
|
restartRequired: true,
|
|
@@ -302,29 +313,29 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
|
|
|
302
313
|
}
|
|
303
314
|
async function writeCodexConfig(input) {
|
|
304
315
|
const target = codexConfigPath(input.homeDir);
|
|
305
|
-
const envTarget = path.join(input.credentialDirectory, "identity.env");
|
|
306
316
|
try {
|
|
307
317
|
const existing = await readOptional(target);
|
|
308
|
-
const merged = mergeCodexToml(existing ?? "", input.
|
|
318
|
+
const merged = mergeCodexToml(existing ?? "", input.identityPath, input.signerPath);
|
|
309
319
|
await writeOwnerOnlyText(target, merged);
|
|
310
|
-
await writeOwnerOnlyText(envTarget, `HAVEN_TOKEN=${shellToken(input.apiKey)}
|
|
311
|
-
`);
|
|
312
320
|
return {
|
|
313
321
|
hostedConfigured: false,
|
|
314
322
|
signerConfigured: true,
|
|
323
|
+
localMcpConfigured: true,
|
|
324
|
+
runtimeMcpMode: "local_stdio",
|
|
315
325
|
target: "Codex CLI config",
|
|
316
326
|
changed: existing !== merged,
|
|
317
327
|
restartRequired: true,
|
|
318
328
|
messages: [
|
|
319
|
-
"Updated Haven MCP
|
|
320
|
-
"
|
|
321
|
-
]
|
|
322
|
-
errorCode: "codex_env_activation_required"
|
|
329
|
+
"Updated local Haven MCP entry in Codex CLI config.",
|
|
330
|
+
"After Haven approval, restart Codex normally so it can load Haven tools."
|
|
331
|
+
]
|
|
323
332
|
};
|
|
324
333
|
} catch (err) {
|
|
325
334
|
return {
|
|
326
335
|
hostedConfigured: false,
|
|
327
336
|
signerConfigured: false,
|
|
337
|
+
localMcpConfigured: false,
|
|
338
|
+
runtimeMcpMode: "local_stdio",
|
|
328
339
|
target: "Codex CLI config",
|
|
329
340
|
changed: false,
|
|
330
341
|
restartRequired: true,
|
|
@@ -374,9 +385,6 @@ function removeTomlTable(toml, table) {
|
|
|
374
385
|
function tomlString(value) {
|
|
375
386
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
376
387
|
}
|
|
377
|
-
function shellToken(value) {
|
|
378
|
-
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
379
|
-
}
|
|
380
388
|
function cursorConfigPath(homeDir = os.homedir()) {
|
|
381
389
|
return path.resolve(homeDir, ".cursor", "mcp.json");
|
|
382
390
|
}
|
|
@@ -411,6 +419,88 @@ function configTargetLabel(runtime) {
|
|
|
411
419
|
return "runtime MCP config";
|
|
412
420
|
}
|
|
413
421
|
}
|
|
422
|
+
function signerPackageName() {
|
|
423
|
+
return `@haven_ai/signer@${signer.SIGNER_VERSION}`;
|
|
424
|
+
}
|
|
425
|
+
function localMcpPackageName() {
|
|
426
|
+
return `@haven_ai/mcp@${mcp.MCP_VERSION}`;
|
|
427
|
+
}
|
|
428
|
+
async function acknowledgeLocalMcpConsent(identityPath, signerPath, log) {
|
|
429
|
+
try {
|
|
430
|
+
const input = await buildLocalMcpConsentInput(identityPath, signerPath);
|
|
431
|
+
const decision = await mcp.ensureConsent(input, {
|
|
432
|
+
credentialsPath: identityPath,
|
|
433
|
+
writeAck: true,
|
|
434
|
+
out: log ? { write: (chunk) => writeLogChunk(log, chunk) } : void 0
|
|
435
|
+
});
|
|
436
|
+
return {
|
|
437
|
+
acknowledged: decision.ok,
|
|
438
|
+
hash: decision.hash,
|
|
439
|
+
reason: decision.reason
|
|
440
|
+
};
|
|
441
|
+
} catch (err) {
|
|
442
|
+
return {
|
|
443
|
+
acknowledged: false,
|
|
444
|
+
error: err instanceof Error ? err.message : String(err)
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
async function getLocalMcpConsentStatus(identityPath, signerPath) {
|
|
449
|
+
try {
|
|
450
|
+
const input = await buildLocalMcpConsentInput(identityPath, signerPath);
|
|
451
|
+
const hash = mcp.computeConsentHash(input);
|
|
452
|
+
const stored = await readLocalMcpAckFile(localMcpAckPath(identityPath));
|
|
453
|
+
if (stored === hash) {
|
|
454
|
+
return { acknowledged: true, hash, reason: "ack_file_match" };
|
|
455
|
+
}
|
|
456
|
+
return {
|
|
457
|
+
acknowledged: false,
|
|
458
|
+
hash,
|
|
459
|
+
reason: stored ? "ack_file_mismatch" : "ack_file_missing"
|
|
460
|
+
};
|
|
461
|
+
} catch (err) {
|
|
462
|
+
return {
|
|
463
|
+
acknowledged: false,
|
|
464
|
+
error: err instanceof Error ? err.message : String(err)
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
function localMcpAckPath(identityPath) {
|
|
469
|
+
return path.resolve(`${identityPath}.ack.json`);
|
|
470
|
+
}
|
|
471
|
+
async function buildLocalMcpConsentInput(identityPath, signerPath) {
|
|
472
|
+
const credentials = await mcp.loadCredentials({ identityPath, signerPath });
|
|
473
|
+
const unavailableDuringSetup = {
|
|
474
|
+
getAllowances: async () => {
|
|
475
|
+
throw new Error("Haven approval is not complete yet.");
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
return mcp.consentInputFromClient(
|
|
479
|
+
unavailableDuringSetup,
|
|
480
|
+
{
|
|
481
|
+
apiKey: credentials.apiKey,
|
|
482
|
+
apiUrl: credentials.apiUrl,
|
|
483
|
+
agentId: credentials.agentId,
|
|
484
|
+
safeAddress: credentials.safeAddress,
|
|
485
|
+
delegateAddress: credentials.delegateAddress,
|
|
486
|
+
chainId: credentials.chainId,
|
|
487
|
+
allowanceSummary: credentials.allowanceSummary
|
|
488
|
+
},
|
|
489
|
+
mcp.registeredToolNames()
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
async function readLocalMcpAckFile(path) {
|
|
493
|
+
try {
|
|
494
|
+
const parsed = JSON.parse(await promises.readFile(path, "utf8"));
|
|
495
|
+
return typeof parsed.ack === "string" ? parsed.ack : null;
|
|
496
|
+
} catch {
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
function writeLogChunk(log, chunk) {
|
|
501
|
+
const message = String(chunk).trimEnd();
|
|
502
|
+
if (message) log(message);
|
|
503
|
+
}
|
|
414
504
|
async function probeHostedMcpTools(apiKey, hostedMcpUrl, fetchImpl = fetch) {
|
|
415
505
|
let response;
|
|
416
506
|
try {
|
|
@@ -559,51 +649,140 @@ function detectRuntime(env) {
|
|
|
559
649
|
if (env.VSCODE_CWD || env.VSCODE_IPC_HOOK_CLI || env.TERM_PROGRAM === "vscode") return "vscode";
|
|
560
650
|
return null;
|
|
561
651
|
}
|
|
652
|
+
async function acknowledgeLocalSignerConsent(signerPath, log) {
|
|
653
|
+
try {
|
|
654
|
+
const input = await buildSignerConsentInput(signerPath);
|
|
655
|
+
const decision = await signer.ensureSignerConsent(input, {
|
|
656
|
+
credentialsPath: signerPath,
|
|
657
|
+
writeAck: true,
|
|
658
|
+
out: log ? { write: (chunk) => writeLogChunk2(log, chunk) } : void 0
|
|
659
|
+
});
|
|
660
|
+
return {
|
|
661
|
+
acknowledged: decision.ok,
|
|
662
|
+
hash: decision.hash,
|
|
663
|
+
reason: decision.reason
|
|
664
|
+
};
|
|
665
|
+
} catch (err) {
|
|
666
|
+
return {
|
|
667
|
+
acknowledged: false,
|
|
668
|
+
error: err instanceof Error ? err.message : String(err)
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
async function getLocalSignerConsentStatus(signerPath) {
|
|
673
|
+
try {
|
|
674
|
+
const input = await buildSignerConsentInput(signerPath);
|
|
675
|
+
const hash = signer.computeSignerConsentHash(input);
|
|
676
|
+
const stored = await readSignerAckFile(signerAckPath(signerPath));
|
|
677
|
+
if (stored === hash) {
|
|
678
|
+
return { acknowledged: true, hash, reason: "ack_file_match" };
|
|
679
|
+
}
|
|
680
|
+
return {
|
|
681
|
+
acknowledged: false,
|
|
682
|
+
hash,
|
|
683
|
+
reason: stored ? "ack_file_mismatch" : "ack_file_missing"
|
|
684
|
+
};
|
|
685
|
+
} catch (err) {
|
|
686
|
+
return {
|
|
687
|
+
acknowledged: false,
|
|
688
|
+
error: err instanceof Error ? err.message : String(err)
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
function signerAckPath(signerPath) {
|
|
693
|
+
return path.resolve(`${signerPath}.signer-ack.json`);
|
|
694
|
+
}
|
|
695
|
+
async function buildSignerConsentInput(signerPath) {
|
|
696
|
+
const credentials = await signer.loadSignerCredentials(signerPath);
|
|
697
|
+
const signer$1 = signer.createEdgeSigner(credentials.delegateKey, {
|
|
698
|
+
x402BindingSigner: credentials.x402BindingSigner
|
|
699
|
+
});
|
|
700
|
+
return {
|
|
701
|
+
delegateAddress: signer$1.delegateAddress,
|
|
702
|
+
safeAddress: credentials.safeAddress,
|
|
703
|
+
agentId: credentials.agentId,
|
|
704
|
+
chainId: credentials.chainId,
|
|
705
|
+
network: credentials.network,
|
|
706
|
+
toolNames: Object.keys(signer.toolSchemas)
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
async function readSignerAckFile(path) {
|
|
710
|
+
try {
|
|
711
|
+
const parsed = JSON.parse(await promises.readFile(path, "utf8"));
|
|
712
|
+
return typeof parsed.ack === "string" ? parsed.ack : null;
|
|
713
|
+
} catch {
|
|
714
|
+
return null;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
function writeLogChunk2(log, chunk) {
|
|
718
|
+
const message = String(chunk).trimEnd();
|
|
719
|
+
if (message) log(message);
|
|
720
|
+
}
|
|
562
721
|
|
|
563
722
|
// src/runtime-install.ts
|
|
564
723
|
var execFileAsync = util.promisify(child_process.execFile);
|
|
565
724
|
async function installRuntime(input, deps = {}) {
|
|
566
725
|
const runtime = normalizeRuntime(input.runtime, deps.env);
|
|
567
726
|
const profile = runtimeProfile(runtime, deps.env);
|
|
727
|
+
const localRuntime = usesLocalMcp(runtime);
|
|
728
|
+
const consentMessages = [];
|
|
729
|
+
const localMcpConsent = localRuntime ? await resolveLocalMcpConsent(input, consentMessages) : void 0;
|
|
730
|
+
const signerConsent = localRuntime ? void 0 : await resolveSignerConsent(input, consentMessages);
|
|
568
731
|
if (runtime === "other") {
|
|
569
|
-
const
|
|
732
|
+
const signerCredentialReady2 = await probeLocalSignerCredential(input.signerPath);
|
|
733
|
+
const signerReady = signerCredentialReady2 && signerConsent?.acknowledged;
|
|
570
734
|
return {
|
|
571
735
|
runtime,
|
|
736
|
+
runtimeMcpMode: "manual",
|
|
572
737
|
hostedMcpConfigured: false,
|
|
573
738
|
localSignerConfigured: false,
|
|
574
|
-
|
|
739
|
+
localMcpConfigured: false,
|
|
740
|
+
probeResult: signerReady ? "manual_runtime_setup_required_local_signer_ready" : "manual_runtime_setup_required_local_signer_unavailable",
|
|
575
741
|
restartRequired: true,
|
|
576
742
|
nextUserAction: "return_to_haven_for_wallet_approval_then_configure_runtime",
|
|
577
743
|
errorCode: "manual_runtime_setup_required",
|
|
578
744
|
configTarget: "manual runtime setup",
|
|
579
|
-
|
|
745
|
+
signerAcknowledged: signerConsent?.acknowledged,
|
|
746
|
+
localMcpAcknowledged: false,
|
|
747
|
+
messages: [
|
|
748
|
+
...consentMessages,
|
|
749
|
+
"Runtime was not recognized. Keep the local credentials and add Haven MCP entries manually after wallet approval."
|
|
750
|
+
]
|
|
580
751
|
};
|
|
581
752
|
}
|
|
582
753
|
const configResult = runtime === "claude-code" ? await configureClaudeCode(input, deps) : await writeRuntimeConfig({
|
|
583
754
|
runtime,
|
|
584
755
|
hostedMcpUrl: input.hostedMcpUrl,
|
|
585
756
|
apiKey: input.apiKey,
|
|
757
|
+
identityPath: input.identityPath,
|
|
586
758
|
signerPath: input.signerPath,
|
|
587
759
|
credentialDirectory: input.credentialDirectory,
|
|
588
760
|
homeDir: deps.homeDir
|
|
589
761
|
});
|
|
590
|
-
const [hostedProbe,
|
|
762
|
+
const [hostedProbe, signerCredentialReady] = await Promise.all([
|
|
591
763
|
configResult.hostedConfigured ? probeHostedMcpTools(input.apiKey, input.hostedMcpUrl, deps.fetch) : Promise.resolve({ status: "bad_response" }),
|
|
592
764
|
probeLocalSignerCredential(input.signerPath)
|
|
593
765
|
]);
|
|
594
766
|
const hostedOk = configResult.hostedConfigured && hostedProbe.status !== "unauthorized";
|
|
595
|
-
const
|
|
767
|
+
const localMcpOk = configResult.runtimeMcpMode === "local_stdio" && configResult.localMcpConfigured && signerCredentialReady && Boolean(localMcpConsent?.acknowledged);
|
|
768
|
+
const signerOk = configResult.runtimeMcpMode === "local_stdio" ? localMcpOk : configResult.signerConfigured && signerCredentialReady && Boolean(signerConsent?.acknowledged);
|
|
596
769
|
const restartRequired = configResult.restartRequired || restartRequiredForRuntime(runtime, deps.env);
|
|
770
|
+
const errorCode = configResult.errorCode ?? (configResult.runtimeMcpMode === "local_stdio" ? localMcpConsentErrorCode(signerCredentialReady, localMcpConsent) : signerConsentErrorCode(signerCredentialReady, signerConsent));
|
|
597
771
|
return {
|
|
598
772
|
runtime,
|
|
773
|
+
runtimeMcpMode: configResult.runtimeMcpMode,
|
|
599
774
|
hostedMcpConfigured: hostedOk,
|
|
600
775
|
localSignerConfigured: signerOk,
|
|
601
|
-
|
|
776
|
+
localMcpConfigured: localMcpOk,
|
|
777
|
+
probeResult: buildProbeResult(configResult.runtimeMcpMode, configResult.hostedConfigured, hostedProbe.status, signerOk, localMcpOk),
|
|
602
778
|
restartRequired,
|
|
603
|
-
nextUserAction: nextAction(profile.restartMode,
|
|
604
|
-
errorCode
|
|
779
|
+
nextUserAction: nextAction(runtime, profile.restartMode, errorCode),
|
|
780
|
+
errorCode,
|
|
605
781
|
configTarget: configResult.target,
|
|
606
|
-
|
|
782
|
+
signerAcknowledged: signerConsent?.acknowledged,
|
|
783
|
+
localMcpAcknowledged: localMcpConsent?.acknowledged,
|
|
784
|
+
activationCommand: configResult.activationCommand,
|
|
785
|
+
messages: [...consentMessages, ...configResult.messages]
|
|
607
786
|
};
|
|
608
787
|
}
|
|
609
788
|
function runtimeInstallCapabilities(runtime, env = process.env) {
|
|
@@ -619,39 +798,43 @@ async function configureClaudeCode(input, deps) {
|
|
|
619
798
|
await runCommand("claude", [
|
|
620
799
|
"mcp",
|
|
621
800
|
"add",
|
|
622
|
-
"--transport",
|
|
623
|
-
"http",
|
|
624
801
|
"haven",
|
|
625
|
-
|
|
626
|
-
"--header",
|
|
627
|
-
`Authorization: Bearer ${input.apiKey}`
|
|
628
|
-
]);
|
|
629
|
-
await runCommand("claude", [
|
|
630
|
-
"mcp",
|
|
631
|
-
"add",
|
|
632
|
-
"haven-signer",
|
|
802
|
+
"--",
|
|
633
803
|
"npx",
|
|
634
804
|
"-y",
|
|
635
|
-
|
|
636
|
-
"--
|
|
805
|
+
localMcpPackageName2(),
|
|
806
|
+
"--identity",
|
|
807
|
+
input.identityPath,
|
|
808
|
+
"--signer",
|
|
637
809
|
input.signerPath
|
|
638
810
|
]);
|
|
811
|
+
await runCommand("claude", ["mcp", "remove", "haven-signer"]).catch(() => void 0);
|
|
639
812
|
return {
|
|
640
|
-
hostedConfigured:
|
|
813
|
+
hostedConfigured: false,
|
|
641
814
|
signerConfigured: true,
|
|
815
|
+
localMcpConfigured: true,
|
|
816
|
+
runtimeMcpMode: "local_stdio",
|
|
642
817
|
target: "Claude Code MCP config",
|
|
643
818
|
changed: true,
|
|
644
819
|
restartRequired: true,
|
|
645
|
-
messages: [
|
|
820
|
+
messages: [
|
|
821
|
+
"Updated local Haven MCP entry with Claude Code.",
|
|
822
|
+
"After Haven approval, restart Claude Code normally so it can load Haven tools."
|
|
823
|
+
]
|
|
646
824
|
};
|
|
647
825
|
} catch (err) {
|
|
648
826
|
return {
|
|
649
827
|
hostedConfigured: false,
|
|
650
828
|
signerConfigured: false,
|
|
829
|
+
localMcpConfigured: false,
|
|
830
|
+
runtimeMcpMode: "local_stdio",
|
|
651
831
|
target: "Claude Code MCP config",
|
|
652
832
|
changed: false,
|
|
653
833
|
restartRequired: true,
|
|
654
|
-
messages: [
|
|
834
|
+
messages: [
|
|
835
|
+
`Could not update Claude Code MCP config: ${err instanceof Error ? err.message : String(err)}`,
|
|
836
|
+
"Install Claude Code or rerun the Haven setup command inside a Claude Code terminal."
|
|
837
|
+
],
|
|
655
838
|
errorCode: "claude_code_config_failed"
|
|
656
839
|
};
|
|
657
840
|
}
|
|
@@ -659,21 +842,66 @@ async function configureClaudeCode(input, deps) {
|
|
|
659
842
|
async function defaultRunCommand(command, args) {
|
|
660
843
|
await execFileAsync(command, args, { timeout: 1e4 });
|
|
661
844
|
}
|
|
662
|
-
function buildProbeResult(hostedConfigured, hostedStatus, signerReady) {
|
|
845
|
+
function buildProbeResult(mode, hostedConfigured, hostedStatus, signerReady, localMcpReady) {
|
|
846
|
+
if (mode === "local_stdio") {
|
|
847
|
+
return localMcpReady ? "local_stdio_mcp_ready" : "local_stdio_mcp_unavailable";
|
|
848
|
+
}
|
|
663
849
|
const hostedPart = hostedConfigured ? `hosted_${hostedStatus}` : "hosted_not_configured";
|
|
664
850
|
const signerPart = signerReady ? "local_signer_ready" : "local_signer_unavailable";
|
|
665
851
|
return `${hostedPart}_${signerPart}`.slice(0, 120);
|
|
666
852
|
}
|
|
667
|
-
function
|
|
853
|
+
async function resolveLocalMcpConsent(input, messages) {
|
|
854
|
+
if (input.ackLocalTools || input.ackSigner) {
|
|
855
|
+
const status = await acknowledgeLocalMcpConsent(input.identityPath, input.signerPath, (message) => messages.push(message));
|
|
856
|
+
if (status.acknowledged) {
|
|
857
|
+
messages.push("Prepared the local Haven tools acknowledgement.");
|
|
858
|
+
} else {
|
|
859
|
+
messages.push("Local Haven tools acknowledgement still needs attention.");
|
|
860
|
+
}
|
|
861
|
+
return status;
|
|
862
|
+
}
|
|
863
|
+
return getLocalMcpConsentStatus(input.identityPath, input.signerPath);
|
|
864
|
+
}
|
|
865
|
+
async function resolveSignerConsent(input, messages) {
|
|
866
|
+
if (input.ackSigner || input.ackLocalTools) {
|
|
867
|
+
const status = await acknowledgeLocalSignerConsent(input.signerPath, (message) => messages.push(message));
|
|
868
|
+
if (status.acknowledged) {
|
|
869
|
+
messages.push("Prepared the local Haven signer acknowledgement.");
|
|
870
|
+
} else {
|
|
871
|
+
messages.push("Local Haven signer acknowledgement still needs attention.");
|
|
872
|
+
}
|
|
873
|
+
return status;
|
|
874
|
+
}
|
|
875
|
+
return getLocalSignerConsentStatus(input.signerPath);
|
|
876
|
+
}
|
|
877
|
+
function signerConsentErrorCode(signerCredentialReady, signerConsent) {
|
|
878
|
+
if (!signerCredentialReady) return "local_signer_credential_unavailable";
|
|
879
|
+
if (!signerConsent?.acknowledged) return "local_signer_ack_required";
|
|
880
|
+
return void 0;
|
|
881
|
+
}
|
|
882
|
+
function localMcpConsentErrorCode(signerCredentialReady, localMcpConsent) {
|
|
883
|
+
if (!signerCredentialReady) return "local_signer_credential_unavailable";
|
|
884
|
+
if (!localMcpConsent?.acknowledged) return "local_mcp_ack_required";
|
|
885
|
+
return void 0;
|
|
886
|
+
}
|
|
887
|
+
function nextAction(runtime, restartMode, errorCode) {
|
|
668
888
|
if (errorCode) return "return_to_haven_for_wallet_approval_then_finish_runtime_setup";
|
|
669
889
|
if (restartMode === "hot-reload") return "return_to_haven_for_wallet_approval";
|
|
890
|
+
if (runtime === "codex-cli") return "return_to_haven_for_wallet_approval_then_restart_codex";
|
|
891
|
+
if (runtime === "claude-code") return "return_to_haven_for_wallet_approval_then_restart_claude_code";
|
|
670
892
|
if (restartMode === "restart-app") return "return_to_haven_for_wallet_approval_then_restart_app";
|
|
671
893
|
if (restartMode === "restart-session") return "return_to_haven_for_wallet_approval_then_restart_agent_session";
|
|
672
894
|
return "return_to_haven_for_wallet_approval_then_configure_runtime";
|
|
673
895
|
}
|
|
896
|
+
function localMcpPackageName2() {
|
|
897
|
+
return `@haven_ai/mcp@${mcp.MCP_VERSION}`;
|
|
898
|
+
}
|
|
899
|
+
function usesLocalMcp(runtime) {
|
|
900
|
+
return runtime === "codex-cli" || runtime === "claude-code";
|
|
901
|
+
}
|
|
674
902
|
|
|
675
903
|
// src/runtime.ts
|
|
676
|
-
var CONNECTOR_VERSION = "0.1.
|
|
904
|
+
var CONNECTOR_VERSION = "0.1.2";
|
|
677
905
|
async function runConnect(options, deps = {}) {
|
|
678
906
|
const connectorVersion = options.connectorVersion ?? CONNECTOR_VERSION;
|
|
679
907
|
const api = deps.api ?? createConnectApiClient(options.apiBaseUrl);
|
|
@@ -719,9 +947,15 @@ async function runConnect(options, deps = {}) {
|
|
|
719
947
|
agentId: registration.agent_id,
|
|
720
948
|
apiKey: localApiKey,
|
|
721
949
|
delegateKey: localKey.privateKey,
|
|
950
|
+
delegateAddress: localKey.address,
|
|
722
951
|
safeAddress: setup.haven_wallet.address,
|
|
723
952
|
chainId: setup.haven_wallet.chain_id,
|
|
724
953
|
network: setup.haven_wallet.network,
|
|
954
|
+
agentBudget: setup.agent_budget.map((budget) => ({
|
|
955
|
+
token_symbol: budget.token_symbol,
|
|
956
|
+
allowance_amount: budget.allowance_amount,
|
|
957
|
+
reset_period_min: budget.reset_period_min
|
|
958
|
+
})),
|
|
725
959
|
apiUrl: options.apiBaseUrl,
|
|
726
960
|
hostedMcpUrl: registration.hosted_mcp_url,
|
|
727
961
|
warn: log
|
|
@@ -735,16 +969,23 @@ async function runConnect(options, deps = {}) {
|
|
|
735
969
|
signerPath: credentialPaths.signerPath,
|
|
736
970
|
identityPath: credentialPaths.identityPath,
|
|
737
971
|
credentialDirectory: credentialPaths.directory,
|
|
738
|
-
environmentLabel: options.environmentLabel ?? "Local workspace"
|
|
972
|
+
environmentLabel: options.environmentLabel ?? "Local workspace",
|
|
973
|
+
ackSigner: options.ackSigner,
|
|
974
|
+
ackLocalTools: options.ackLocalTools
|
|
739
975
|
});
|
|
740
976
|
printRuntimeInstall(runtimeInstall, log);
|
|
741
977
|
try {
|
|
742
978
|
await api.updateInstallStatus(registration.setup_id, localApiKey, {
|
|
743
979
|
runtime: runtimeInstall.runtime,
|
|
744
980
|
connectorVersion,
|
|
981
|
+
runtimeMcpMode: runtimeInstall.runtimeMcpMode,
|
|
745
982
|
hostedMcpConfigured: runtimeInstall.hostedMcpConfigured,
|
|
746
983
|
localSignerConfigured: runtimeInstall.localSignerConfigured,
|
|
984
|
+
localMcpConfigured: runtimeInstall.localMcpConfigured,
|
|
747
985
|
credentialFilesWritten: true,
|
|
986
|
+
signerAcknowledged: runtimeInstall.signerAcknowledged,
|
|
987
|
+
localMcpAcknowledged: runtimeInstall.localMcpAcknowledged,
|
|
988
|
+
activationCommandAvailable: Boolean(runtimeInstall.activationCommand),
|
|
748
989
|
probeResult: runtimeInstall.probeResult,
|
|
749
990
|
restartRequired: runtimeInstall.restartRequired,
|
|
750
991
|
nextUserAction: runtimeInstall.nextUserAction,
|
|
@@ -756,7 +997,7 @@ async function runConnect(options, deps = {}) {
|
|
|
756
997
|
}
|
|
757
998
|
log("Return to Haven to approve the agent rules.");
|
|
758
999
|
if (runtimeInstall.restartRequired) {
|
|
759
|
-
log("
|
|
1000
|
+
log("After approval, restart this agent normally so it can load Haven tools.");
|
|
760
1001
|
}
|
|
761
1002
|
return {
|
|
762
1003
|
setupId: registration.setup_id,
|
|
@@ -782,10 +1023,12 @@ function secureLogger(log) {
|
|
|
782
1023
|
}
|
|
783
1024
|
function printRuntimeInstall(result, log) {
|
|
784
1025
|
for (const message of result.messages) log(message);
|
|
785
|
-
if (result.
|
|
1026
|
+
if (result.localMcpConfigured) {
|
|
1027
|
+
log("Configured local Haven MCP tools.");
|
|
1028
|
+
} else if (result.hostedMcpConfigured) {
|
|
786
1029
|
log("Configured hosted Haven MCP identity.");
|
|
787
1030
|
} else {
|
|
788
|
-
log("
|
|
1031
|
+
log("Haven MCP tools still need runtime setup.");
|
|
789
1032
|
}
|
|
790
1033
|
if (result.localSignerConfigured) {
|
|
791
1034
|
log("Configured local Haven signer.");
|
|
@@ -815,6 +1058,11 @@ function parseArgs(argv, env = process.env) {
|
|
|
815
1058
|
options.credentialsDir = requireValue(argv, ++i, arg);
|
|
816
1059
|
} else if (arg === "--environment-label") {
|
|
817
1060
|
options.environmentLabel = requireValue(argv, ++i, arg);
|
|
1061
|
+
} else if (arg === "--ack-local-tools") {
|
|
1062
|
+
options.ackLocalTools = true;
|
|
1063
|
+
} else if (arg === "--ack-signer") {
|
|
1064
|
+
options.ackSigner = true;
|
|
1065
|
+
options.ackLocalTools = true;
|
|
818
1066
|
} else if (arg === "--version") {
|
|
819
1067
|
process.stdout.write(`${CONNECTOR_VERSION}
|
|
820
1068
|
`);
|
|
@@ -843,7 +1091,7 @@ function helpText() {
|
|
|
843
1091
|
"sends Haven only the public signing address plus a proof signature.",
|
|
844
1092
|
"",
|
|
845
1093
|
"Usage:",
|
|
846
|
-
" npx -y @haven_ai/connect --setup hv_setup_... --api https://api.haven.example --runtime claude-code",
|
|
1094
|
+
" npx -y @haven_ai/connect --setup hv_setup_... --api https://api.haven.example --ack-local-tools --runtime claude-code",
|
|
847
1095
|
"",
|
|
848
1096
|
"Options:",
|
|
849
1097
|
" --setup <token> Short-lived setup token from Haven.",
|
|
@@ -851,6 +1099,8 @@ function helpText() {
|
|
|
851
1099
|
" --runtime <name> Agent runtime hint, such as claude-code, codex-cli, cursor, vscode, or claude-desktop.",
|
|
852
1100
|
" --credentials-dir <path> Credential directory fallback. Defaults to ~/.haven/agents.",
|
|
853
1101
|
" --environment-label <text> Non-sensitive label shown in Haven setup review.",
|
|
1102
|
+
" --ack-local-tools Write the one-time local Haven tools acknowledgement during setup.",
|
|
1103
|
+
" --ack-signer Backward-compatible alias for --ack-local-tools.",
|
|
854
1104
|
" --help Show this help.",
|
|
855
1105
|
"",
|
|
856
1106
|
"The connector never prints the private key and never sends it to Haven."
|