@haven_ai/connect 0.1.1-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 +4 -4
- package/dist/cli.cjs +187 -74
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +188 -75
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +187 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +188 -75
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,8 @@ import { homedir, platform } from 'os';
|
|
|
6
6
|
import { join, resolve, dirname } from 'path';
|
|
7
7
|
import { execFile } from 'child_process';
|
|
8
8
|
import { promisify } from 'util';
|
|
9
|
-
import {
|
|
9
|
+
import { ensureConsent, computeConsentHash, MCP_VERSION, loadCredentials, consentInputFromClient, registeredToolNames } from '@haven_ai/mcp';
|
|
10
|
+
import { ensureSignerConsent, computeSignerConsentHash, loadSignerCredentials, createEdgeSigner, toolSchemas, SIGNER_VERSION } from '@haven_ai/signer';
|
|
10
11
|
|
|
11
12
|
// src/api.ts
|
|
12
13
|
function createConnectApiClient(baseUrl, fetchImpl = fetch) {
|
|
@@ -45,10 +46,13 @@ function createConnectApiClient(baseUrl, fetchImpl = fetch) {
|
|
|
45
46
|
body: JSON.stringify({
|
|
46
47
|
runtime: input.runtime,
|
|
47
48
|
connector_version: input.connectorVersion,
|
|
49
|
+
runtime_mcp_mode: input.runtimeMcpMode,
|
|
48
50
|
hosted_mcp_configured: input.hostedMcpConfigured,
|
|
49
51
|
local_signer_configured: input.localSignerConfigured,
|
|
52
|
+
local_mcp_configured: input.localMcpConfigured,
|
|
50
53
|
credential_files_written: input.credentialFilesWritten,
|
|
51
54
|
signer_acknowledged: input.signerAcknowledged,
|
|
55
|
+
local_mcp_acknowledged: input.localMcpAcknowledged,
|
|
52
56
|
activation_command_available: input.activationCommandAvailable,
|
|
53
57
|
probe_result: input.probeResult,
|
|
54
58
|
restart_required: input.restartRequired,
|
|
@@ -131,6 +135,7 @@ async function writeCredentialFiles(input) {
|
|
|
131
135
|
signerPath,
|
|
132
136
|
{
|
|
133
137
|
delegate_key: input.delegateKey,
|
|
138
|
+
delegate_address: input.delegateAddress,
|
|
134
139
|
agent_id: input.agentId,
|
|
135
140
|
safe_address: input.safeAddress,
|
|
136
141
|
chain_id: input.chainId,
|
|
@@ -150,6 +155,7 @@ async function writeCredentialFiles(input) {
|
|
|
150
155
|
network: input.network,
|
|
151
156
|
api_url: input.apiUrl,
|
|
152
157
|
hosted_mcp_url: input.hostedMcpUrl,
|
|
158
|
+
agent_budget: input.agentBudget,
|
|
153
159
|
note: "Haven API key identifies the agent only. It cannot spend without the local signer key and on-chain Haven wallet rules."
|
|
154
160
|
},
|
|
155
161
|
input.warn
|
|
@@ -210,6 +216,8 @@ async function writeRuntimeConfig(input) {
|
|
|
210
216
|
return {
|
|
211
217
|
hostedConfigured: false,
|
|
212
218
|
signerConfigured: false,
|
|
219
|
+
localMcpConfigured: false,
|
|
220
|
+
runtimeMcpMode: "manual",
|
|
213
221
|
target: "manual runtime setup",
|
|
214
222
|
changed: false,
|
|
215
223
|
restartRequired: true,
|
|
@@ -251,17 +259,13 @@ function mergeJsonMcpConfig(existingJson, serverRoot, hostedServer, signerServer
|
|
|
251
259
|
return `${JSON.stringify(config, null, 2)}
|
|
252
260
|
`;
|
|
253
261
|
}
|
|
254
|
-
function mergeCodexToml(existingToml,
|
|
262
|
+
function mergeCodexToml(existingToml, identityPath, signerPath) {
|
|
255
263
|
let next = removeTomlTable(removeTomlTable(existingToml, "mcp_servers.haven"), "mcp_servers.haven_signer");
|
|
256
264
|
next = next.trimEnd();
|
|
257
265
|
const block = [
|
|
258
266
|
"[mcp_servers.haven]",
|
|
259
|
-
`url = ${tomlString(hostedMcpUrl)}`,
|
|
260
|
-
'bearer_token_env_var = "HAVEN_TOKEN"',
|
|
261
|
-
"",
|
|
262
|
-
"[mcp_servers.haven_signer]",
|
|
263
267
|
'command = "npx"',
|
|
264
|
-
`args = ["-y", ${tomlString(
|
|
268
|
+
`args = ["-y", ${tomlString(localMcpPackageName())}, "--identity", ${tomlString(identityPath)}, "--signer", ${tomlString(signerPath)}]`
|
|
265
269
|
].join("\n");
|
|
266
270
|
return `${next ? `${next}
|
|
267
271
|
|
|
@@ -281,6 +285,8 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
|
|
|
281
285
|
return {
|
|
282
286
|
hostedConfigured: true,
|
|
283
287
|
signerConfigured: true,
|
|
288
|
+
localMcpConfigured: false,
|
|
289
|
+
runtimeMcpMode: "hosted_plus_signer",
|
|
284
290
|
target: configTargetLabel(input.runtime),
|
|
285
291
|
changed: existing !== merged,
|
|
286
292
|
restartRequired: input.runtime === "claude-desktop",
|
|
@@ -290,6 +296,8 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
|
|
|
290
296
|
return {
|
|
291
297
|
hostedConfigured: false,
|
|
292
298
|
signerConfigured: false,
|
|
299
|
+
localMcpConfigured: false,
|
|
300
|
+
runtimeMcpMode: "hosted_plus_signer",
|
|
293
301
|
target: configTargetLabel(input.runtime),
|
|
294
302
|
changed: false,
|
|
295
303
|
restartRequired: true,
|
|
@@ -300,41 +308,29 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
|
|
|
300
308
|
}
|
|
301
309
|
async function writeCodexConfig(input) {
|
|
302
310
|
const target = codexConfigPath(input.homeDir);
|
|
303
|
-
const envTarget = join(input.credentialDirectory, "identity.env");
|
|
304
|
-
const launchTarget = join(input.credentialDirectory, "start-codex.sh");
|
|
305
311
|
try {
|
|
306
312
|
const existing = await readOptional(target);
|
|
307
|
-
const merged = mergeCodexToml(existing ?? "", input.
|
|
313
|
+
const merged = mergeCodexToml(existing ?? "", input.identityPath, input.signerPath);
|
|
308
314
|
await writeOwnerOnlyText(target, merged);
|
|
309
|
-
await writeOwnerOnlyText(envTarget, `export HAVEN_TOKEN=${shellToken(input.apiKey)}
|
|
310
|
-
`);
|
|
311
|
-
await writeOwnerExecutableText(
|
|
312
|
-
launchTarget,
|
|
313
|
-
[
|
|
314
|
-
"#!/bin/sh",
|
|
315
|
-
"set -eu",
|
|
316
|
-
`. ${shellToken(envTarget)}`,
|
|
317
|
-
'exec codex "$@"',
|
|
318
|
-
""
|
|
319
|
-
].join("\n")
|
|
320
|
-
);
|
|
321
315
|
return {
|
|
322
|
-
hostedConfigured:
|
|
316
|
+
hostedConfigured: false,
|
|
323
317
|
signerConfigured: true,
|
|
318
|
+
localMcpConfigured: true,
|
|
319
|
+
runtimeMcpMode: "local_stdio",
|
|
324
320
|
target: "Codex CLI config",
|
|
325
321
|
changed: existing !== merged,
|
|
326
322
|
restartRequired: true,
|
|
327
|
-
activationCommand: shellToken(launchTarget),
|
|
328
323
|
messages: [
|
|
329
|
-
"Updated Haven MCP
|
|
330
|
-
"
|
|
331
|
-
`Restart Codex with: ${shellToken(launchTarget)}`
|
|
324
|
+
"Updated local Haven MCP entry in Codex CLI config.",
|
|
325
|
+
"After Haven approval, restart Codex normally so it can load Haven tools."
|
|
332
326
|
]
|
|
333
327
|
};
|
|
334
328
|
} catch (err) {
|
|
335
329
|
return {
|
|
336
330
|
hostedConfigured: false,
|
|
337
331
|
signerConfigured: false,
|
|
332
|
+
localMcpConfigured: false,
|
|
333
|
+
runtimeMcpMode: "local_stdio",
|
|
338
334
|
target: "Codex CLI config",
|
|
339
335
|
changed: false,
|
|
340
336
|
restartRequired: true,
|
|
@@ -356,11 +352,6 @@ async function writeOwnerOnlyText(path, value) {
|
|
|
356
352
|
await writeFile(path, value, { mode: 384 });
|
|
357
353
|
await chmod(path, 384).catch(() => void 0);
|
|
358
354
|
}
|
|
359
|
-
async function writeOwnerExecutableText(path, value) {
|
|
360
|
-
await mkdir(dirname(path), { recursive: true, mode: 448 });
|
|
361
|
-
await writeFile(path, value, { mode: 448 });
|
|
362
|
-
await chmod(path, 448).catch(() => void 0);
|
|
363
|
-
}
|
|
364
355
|
function parseJsonObject(value) {
|
|
365
356
|
const parsed = JSON.parse(value);
|
|
366
357
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -389,9 +380,6 @@ function removeTomlTable(toml, table) {
|
|
|
389
380
|
function tomlString(value) {
|
|
390
381
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
391
382
|
}
|
|
392
|
-
function shellToken(value) {
|
|
393
|
-
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
394
|
-
}
|
|
395
383
|
function cursorConfigPath(homeDir = homedir()) {
|
|
396
384
|
return resolve(homeDir, ".cursor", "mcp.json");
|
|
397
385
|
}
|
|
@@ -429,6 +417,85 @@ function configTargetLabel(runtime) {
|
|
|
429
417
|
function signerPackageName() {
|
|
430
418
|
return `@haven_ai/signer@${SIGNER_VERSION}`;
|
|
431
419
|
}
|
|
420
|
+
function localMcpPackageName() {
|
|
421
|
+
return `@haven_ai/mcp@${MCP_VERSION}`;
|
|
422
|
+
}
|
|
423
|
+
async function acknowledgeLocalMcpConsent(identityPath, signerPath, log) {
|
|
424
|
+
try {
|
|
425
|
+
const input = await buildLocalMcpConsentInput(identityPath, signerPath);
|
|
426
|
+
const decision = await ensureConsent(input, {
|
|
427
|
+
credentialsPath: identityPath,
|
|
428
|
+
writeAck: true,
|
|
429
|
+
out: log ? { write: (chunk) => writeLogChunk(log, chunk) } : void 0
|
|
430
|
+
});
|
|
431
|
+
return {
|
|
432
|
+
acknowledged: decision.ok,
|
|
433
|
+
hash: decision.hash,
|
|
434
|
+
reason: decision.reason
|
|
435
|
+
};
|
|
436
|
+
} catch (err) {
|
|
437
|
+
return {
|
|
438
|
+
acknowledged: false,
|
|
439
|
+
error: err instanceof Error ? err.message : String(err)
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
async function getLocalMcpConsentStatus(identityPath, signerPath) {
|
|
444
|
+
try {
|
|
445
|
+
const input = await buildLocalMcpConsentInput(identityPath, signerPath);
|
|
446
|
+
const hash = computeConsentHash(input);
|
|
447
|
+
const stored = await readLocalMcpAckFile(localMcpAckPath(identityPath));
|
|
448
|
+
if (stored === hash) {
|
|
449
|
+
return { acknowledged: true, hash, reason: "ack_file_match" };
|
|
450
|
+
}
|
|
451
|
+
return {
|
|
452
|
+
acknowledged: false,
|
|
453
|
+
hash,
|
|
454
|
+
reason: stored ? "ack_file_mismatch" : "ack_file_missing"
|
|
455
|
+
};
|
|
456
|
+
} catch (err) {
|
|
457
|
+
return {
|
|
458
|
+
acknowledged: false,
|
|
459
|
+
error: err instanceof Error ? err.message : String(err)
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
function localMcpAckPath(identityPath) {
|
|
464
|
+
return resolve(`${identityPath}.ack.json`);
|
|
465
|
+
}
|
|
466
|
+
async function buildLocalMcpConsentInput(identityPath, signerPath) {
|
|
467
|
+
const credentials = await loadCredentials({ identityPath, signerPath });
|
|
468
|
+
const unavailableDuringSetup = {
|
|
469
|
+
getAllowances: async () => {
|
|
470
|
+
throw new Error("Haven approval is not complete yet.");
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
return consentInputFromClient(
|
|
474
|
+
unavailableDuringSetup,
|
|
475
|
+
{
|
|
476
|
+
apiKey: credentials.apiKey,
|
|
477
|
+
apiUrl: credentials.apiUrl,
|
|
478
|
+
agentId: credentials.agentId,
|
|
479
|
+
safeAddress: credentials.safeAddress,
|
|
480
|
+
delegateAddress: credentials.delegateAddress,
|
|
481
|
+
chainId: credentials.chainId,
|
|
482
|
+
allowanceSummary: credentials.allowanceSummary
|
|
483
|
+
},
|
|
484
|
+
registeredToolNames()
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
async function readLocalMcpAckFile(path) {
|
|
488
|
+
try {
|
|
489
|
+
const parsed = JSON.parse(await readFile(path, "utf8"));
|
|
490
|
+
return typeof parsed.ack === "string" ? parsed.ack : null;
|
|
491
|
+
} catch {
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function writeLogChunk(log, chunk) {
|
|
496
|
+
const message = String(chunk).trimEnd();
|
|
497
|
+
if (message) log(message);
|
|
498
|
+
}
|
|
432
499
|
async function probeHostedMcpTools(apiKey, hostedMcpUrl, fetchImpl = fetch) {
|
|
433
500
|
let response;
|
|
434
501
|
try {
|
|
@@ -583,7 +650,7 @@ async function acknowledgeLocalSignerConsent(signerPath, log) {
|
|
|
583
650
|
const decision = await ensureSignerConsent(input, {
|
|
584
651
|
credentialsPath: signerPath,
|
|
585
652
|
writeAck: true,
|
|
586
|
-
out: log ? { write: (chunk) =>
|
|
653
|
+
out: log ? { write: (chunk) => writeLogChunk2(log, chunk) } : void 0
|
|
587
654
|
});
|
|
588
655
|
return {
|
|
589
656
|
acknowledged: decision.ok,
|
|
@@ -642,7 +709,7 @@ async function readSignerAckFile(path) {
|
|
|
642
709
|
return null;
|
|
643
710
|
}
|
|
644
711
|
}
|
|
645
|
-
function
|
|
712
|
+
function writeLogChunk2(log, chunk) {
|
|
646
713
|
const message = String(chunk).trimEnd();
|
|
647
714
|
if (message) log(message);
|
|
648
715
|
}
|
|
@@ -652,23 +719,28 @@ var execFileAsync = promisify(execFile);
|
|
|
652
719
|
async function installRuntime(input, deps = {}) {
|
|
653
720
|
const runtime = normalizeRuntime(input.runtime, deps.env);
|
|
654
721
|
const profile = runtimeProfile(runtime, deps.env);
|
|
655
|
-
const
|
|
656
|
-
const
|
|
722
|
+
const localRuntime = usesLocalMcp(runtime);
|
|
723
|
+
const consentMessages = [];
|
|
724
|
+
const localMcpConsent = localRuntime ? await resolveLocalMcpConsent(input, consentMessages) : void 0;
|
|
725
|
+
const signerConsent = localRuntime ? void 0 : await resolveSignerConsent(input, consentMessages);
|
|
657
726
|
if (runtime === "other") {
|
|
658
727
|
const signerCredentialReady2 = await probeLocalSignerCredential(input.signerPath);
|
|
659
|
-
const signerReady = signerCredentialReady2 && signerConsent
|
|
728
|
+
const signerReady = signerCredentialReady2 && signerConsent?.acknowledged;
|
|
660
729
|
return {
|
|
661
730
|
runtime,
|
|
731
|
+
runtimeMcpMode: "manual",
|
|
662
732
|
hostedMcpConfigured: false,
|
|
663
733
|
localSignerConfigured: false,
|
|
734
|
+
localMcpConfigured: false,
|
|
664
735
|
probeResult: signerReady ? "manual_runtime_setup_required_local_signer_ready" : "manual_runtime_setup_required_local_signer_unavailable",
|
|
665
736
|
restartRequired: true,
|
|
666
737
|
nextUserAction: "return_to_haven_for_wallet_approval_then_configure_runtime",
|
|
667
738
|
errorCode: "manual_runtime_setup_required",
|
|
668
739
|
configTarget: "manual runtime setup",
|
|
669
|
-
signerAcknowledged: signerConsent
|
|
740
|
+
signerAcknowledged: signerConsent?.acknowledged,
|
|
741
|
+
localMcpAcknowledged: false,
|
|
670
742
|
messages: [
|
|
671
|
-
...
|
|
743
|
+
...consentMessages,
|
|
672
744
|
"Runtime was not recognized. Keep the local credentials and add Haven MCP entries manually after wallet approval."
|
|
673
745
|
]
|
|
674
746
|
};
|
|
@@ -677,6 +749,7 @@ async function installRuntime(input, deps = {}) {
|
|
|
677
749
|
runtime,
|
|
678
750
|
hostedMcpUrl: input.hostedMcpUrl,
|
|
679
751
|
apiKey: input.apiKey,
|
|
752
|
+
identityPath: input.identityPath,
|
|
680
753
|
signerPath: input.signerPath,
|
|
681
754
|
credentialDirectory: input.credentialDirectory,
|
|
682
755
|
homeDir: deps.homeDir
|
|
@@ -686,21 +759,25 @@ async function installRuntime(input, deps = {}) {
|
|
|
686
759
|
probeLocalSignerCredential(input.signerPath)
|
|
687
760
|
]);
|
|
688
761
|
const hostedOk = configResult.hostedConfigured && hostedProbe.status !== "unauthorized";
|
|
689
|
-
const
|
|
762
|
+
const localMcpOk = configResult.runtimeMcpMode === "local_stdio" && configResult.localMcpConfigured && signerCredentialReady && Boolean(localMcpConsent?.acknowledged);
|
|
763
|
+
const signerOk = configResult.runtimeMcpMode === "local_stdio" ? localMcpOk : configResult.signerConfigured && signerCredentialReady && Boolean(signerConsent?.acknowledged);
|
|
690
764
|
const restartRequired = configResult.restartRequired || restartRequiredForRuntime(runtime, deps.env);
|
|
691
|
-
const errorCode = configResult.errorCode ?? signerConsentErrorCode(signerCredentialReady, signerConsent);
|
|
765
|
+
const errorCode = configResult.errorCode ?? (configResult.runtimeMcpMode === "local_stdio" ? localMcpConsentErrorCode(signerCredentialReady, localMcpConsent) : signerConsentErrorCode(signerCredentialReady, signerConsent));
|
|
692
766
|
return {
|
|
693
767
|
runtime,
|
|
768
|
+
runtimeMcpMode: configResult.runtimeMcpMode,
|
|
694
769
|
hostedMcpConfigured: hostedOk,
|
|
695
770
|
localSignerConfigured: signerOk,
|
|
696
|
-
|
|
771
|
+
localMcpConfigured: localMcpOk,
|
|
772
|
+
probeResult: buildProbeResult(configResult.runtimeMcpMode, configResult.hostedConfigured, hostedProbe.status, signerOk, localMcpOk),
|
|
697
773
|
restartRequired,
|
|
698
774
|
nextUserAction: nextAction(runtime, profile.restartMode, errorCode),
|
|
699
775
|
errorCode,
|
|
700
776
|
configTarget: configResult.target,
|
|
701
|
-
signerAcknowledged: signerConsent
|
|
777
|
+
signerAcknowledged: signerConsent?.acknowledged,
|
|
778
|
+
localMcpAcknowledged: localMcpConsent?.acknowledged,
|
|
702
779
|
activationCommand: configResult.activationCommand,
|
|
703
|
-
messages: [...
|
|
780
|
+
messages: [...consentMessages, ...configResult.messages]
|
|
704
781
|
};
|
|
705
782
|
}
|
|
706
783
|
function runtimeInstallCapabilities(runtime, env = process.env) {
|
|
@@ -716,39 +793,36 @@ async function configureClaudeCode(input, deps) {
|
|
|
716
793
|
await runCommand("claude", [
|
|
717
794
|
"mcp",
|
|
718
795
|
"add",
|
|
719
|
-
"--transport",
|
|
720
|
-
"http",
|
|
721
796
|
"haven",
|
|
722
|
-
input.hostedMcpUrl,
|
|
723
|
-
"--header",
|
|
724
|
-
`Authorization: Bearer ${input.apiKey}`
|
|
725
|
-
]);
|
|
726
|
-
await runCommand("claude", [
|
|
727
|
-
"mcp",
|
|
728
|
-
"add",
|
|
729
|
-
"haven-signer",
|
|
730
797
|
"--",
|
|
731
798
|
"npx",
|
|
732
799
|
"-y",
|
|
733
|
-
|
|
734
|
-
"--
|
|
800
|
+
localMcpPackageName2(),
|
|
801
|
+
"--identity",
|
|
802
|
+
input.identityPath,
|
|
803
|
+
"--signer",
|
|
735
804
|
input.signerPath
|
|
736
805
|
]);
|
|
806
|
+
await runCommand("claude", ["mcp", "remove", "haven-signer"]).catch(() => void 0);
|
|
737
807
|
return {
|
|
738
|
-
hostedConfigured:
|
|
808
|
+
hostedConfigured: false,
|
|
739
809
|
signerConfigured: true,
|
|
810
|
+
localMcpConfigured: true,
|
|
811
|
+
runtimeMcpMode: "local_stdio",
|
|
740
812
|
target: "Claude Code MCP config",
|
|
741
813
|
changed: true,
|
|
742
814
|
restartRequired: true,
|
|
743
815
|
messages: [
|
|
744
|
-
"Updated Haven MCP
|
|
745
|
-
"
|
|
816
|
+
"Updated local Haven MCP entry with Claude Code.",
|
|
817
|
+
"After Haven approval, restart Claude Code normally so it can load Haven tools."
|
|
746
818
|
]
|
|
747
819
|
};
|
|
748
820
|
} catch (err) {
|
|
749
821
|
return {
|
|
750
822
|
hostedConfigured: false,
|
|
751
823
|
signerConfigured: false,
|
|
824
|
+
localMcpConfigured: false,
|
|
825
|
+
runtimeMcpMode: "local_stdio",
|
|
752
826
|
target: "Claude Code MCP config",
|
|
753
827
|
changed: false,
|
|
754
828
|
restartRequired: true,
|
|
@@ -763,13 +837,28 @@ async function configureClaudeCode(input, deps) {
|
|
|
763
837
|
async function defaultRunCommand(command, args) {
|
|
764
838
|
await execFileAsync(command, args, { timeout: 1e4 });
|
|
765
839
|
}
|
|
766
|
-
function buildProbeResult(hostedConfigured, hostedStatus, signerReady) {
|
|
840
|
+
function buildProbeResult(mode, hostedConfigured, hostedStatus, signerReady, localMcpReady) {
|
|
841
|
+
if (mode === "local_stdio") {
|
|
842
|
+
return localMcpReady ? "local_stdio_mcp_ready" : "local_stdio_mcp_unavailable";
|
|
843
|
+
}
|
|
767
844
|
const hostedPart = hostedConfigured ? `hosted_${hostedStatus}` : "hosted_not_configured";
|
|
768
845
|
const signerPart = signerReady ? "local_signer_ready" : "local_signer_unavailable";
|
|
769
846
|
return `${hostedPart}_${signerPart}`.slice(0, 120);
|
|
770
847
|
}
|
|
848
|
+
async function resolveLocalMcpConsent(input, messages) {
|
|
849
|
+
if (input.ackLocalTools || input.ackSigner) {
|
|
850
|
+
const status = await acknowledgeLocalMcpConsent(input.identityPath, input.signerPath, (message) => messages.push(message));
|
|
851
|
+
if (status.acknowledged) {
|
|
852
|
+
messages.push("Prepared the local Haven tools acknowledgement.");
|
|
853
|
+
} else {
|
|
854
|
+
messages.push("Local Haven tools acknowledgement still needs attention.");
|
|
855
|
+
}
|
|
856
|
+
return status;
|
|
857
|
+
}
|
|
858
|
+
return getLocalMcpConsentStatus(input.identityPath, input.signerPath);
|
|
859
|
+
}
|
|
771
860
|
async function resolveSignerConsent(input, messages) {
|
|
772
|
-
if (input.ackSigner) {
|
|
861
|
+
if (input.ackSigner || input.ackLocalTools) {
|
|
773
862
|
const status = await acknowledgeLocalSignerConsent(input.signerPath, (message) => messages.push(message));
|
|
774
863
|
if (status.acknowledged) {
|
|
775
864
|
messages.push("Prepared the local Haven signer acknowledgement.");
|
|
@@ -782,24 +871,32 @@ async function resolveSignerConsent(input, messages) {
|
|
|
782
871
|
}
|
|
783
872
|
function signerConsentErrorCode(signerCredentialReady, signerConsent) {
|
|
784
873
|
if (!signerCredentialReady) return "local_signer_credential_unavailable";
|
|
785
|
-
if (!signerConsent
|
|
874
|
+
if (!signerConsent?.acknowledged) return "local_signer_ack_required";
|
|
875
|
+
return void 0;
|
|
876
|
+
}
|
|
877
|
+
function localMcpConsentErrorCode(signerCredentialReady, localMcpConsent) {
|
|
878
|
+
if (!signerCredentialReady) return "local_signer_credential_unavailable";
|
|
879
|
+
if (!localMcpConsent?.acknowledged) return "local_mcp_ack_required";
|
|
786
880
|
return void 0;
|
|
787
881
|
}
|
|
788
882
|
function nextAction(runtime, restartMode, errorCode) {
|
|
789
883
|
if (errorCode) return "return_to_haven_for_wallet_approval_then_finish_runtime_setup";
|
|
790
884
|
if (restartMode === "hot-reload") return "return_to_haven_for_wallet_approval";
|
|
791
|
-
if (runtime === "codex-cli") return "
|
|
885
|
+
if (runtime === "codex-cli") return "return_to_haven_for_wallet_approval_then_restart_codex";
|
|
792
886
|
if (runtime === "claude-code") return "return_to_haven_for_wallet_approval_then_restart_claude_code";
|
|
793
887
|
if (restartMode === "restart-app") return "return_to_haven_for_wallet_approval_then_restart_app";
|
|
794
888
|
if (restartMode === "restart-session") return "return_to_haven_for_wallet_approval_then_restart_agent_session";
|
|
795
889
|
return "return_to_haven_for_wallet_approval_then_configure_runtime";
|
|
796
890
|
}
|
|
797
|
-
function
|
|
798
|
-
return `@haven_ai/
|
|
891
|
+
function localMcpPackageName2() {
|
|
892
|
+
return `@haven_ai/mcp@${MCP_VERSION}`;
|
|
893
|
+
}
|
|
894
|
+
function usesLocalMcp(runtime) {
|
|
895
|
+
return runtime === "codex-cli" || runtime === "claude-code";
|
|
799
896
|
}
|
|
800
897
|
|
|
801
898
|
// src/runtime.ts
|
|
802
|
-
var CONNECTOR_VERSION = "0.1.
|
|
899
|
+
var CONNECTOR_VERSION = "0.1.2";
|
|
803
900
|
async function runConnect(options, deps = {}) {
|
|
804
901
|
const connectorVersion = options.connectorVersion ?? CONNECTOR_VERSION;
|
|
805
902
|
const api = deps.api ?? createConnectApiClient(options.apiBaseUrl);
|
|
@@ -845,9 +942,15 @@ async function runConnect(options, deps = {}) {
|
|
|
845
942
|
agentId: registration.agent_id,
|
|
846
943
|
apiKey: localApiKey,
|
|
847
944
|
delegateKey: localKey.privateKey,
|
|
945
|
+
delegateAddress: localKey.address,
|
|
848
946
|
safeAddress: setup.haven_wallet.address,
|
|
849
947
|
chainId: setup.haven_wallet.chain_id,
|
|
850
948
|
network: setup.haven_wallet.network,
|
|
949
|
+
agentBudget: setup.agent_budget.map((budget) => ({
|
|
950
|
+
token_symbol: budget.token_symbol,
|
|
951
|
+
allowance_amount: budget.allowance_amount,
|
|
952
|
+
reset_period_min: budget.reset_period_min
|
|
953
|
+
})),
|
|
851
954
|
apiUrl: options.apiBaseUrl,
|
|
852
955
|
hostedMcpUrl: registration.hosted_mcp_url,
|
|
853
956
|
warn: log
|
|
@@ -862,17 +965,21 @@ async function runConnect(options, deps = {}) {
|
|
|
862
965
|
identityPath: credentialPaths.identityPath,
|
|
863
966
|
credentialDirectory: credentialPaths.directory,
|
|
864
967
|
environmentLabel: options.environmentLabel ?? "Local workspace",
|
|
865
|
-
ackSigner: options.ackSigner
|
|
968
|
+
ackSigner: options.ackSigner,
|
|
969
|
+
ackLocalTools: options.ackLocalTools
|
|
866
970
|
});
|
|
867
971
|
printRuntimeInstall(runtimeInstall, log);
|
|
868
972
|
try {
|
|
869
973
|
await api.updateInstallStatus(registration.setup_id, localApiKey, {
|
|
870
974
|
runtime: runtimeInstall.runtime,
|
|
871
975
|
connectorVersion,
|
|
976
|
+
runtimeMcpMode: runtimeInstall.runtimeMcpMode,
|
|
872
977
|
hostedMcpConfigured: runtimeInstall.hostedMcpConfigured,
|
|
873
978
|
localSignerConfigured: runtimeInstall.localSignerConfigured,
|
|
979
|
+
localMcpConfigured: runtimeInstall.localMcpConfigured,
|
|
874
980
|
credentialFilesWritten: true,
|
|
875
981
|
signerAcknowledged: runtimeInstall.signerAcknowledged,
|
|
982
|
+
localMcpAcknowledged: runtimeInstall.localMcpAcknowledged,
|
|
876
983
|
activationCommandAvailable: Boolean(runtimeInstall.activationCommand),
|
|
877
984
|
probeResult: runtimeInstall.probeResult,
|
|
878
985
|
restartRequired: runtimeInstall.restartRequired,
|
|
@@ -885,7 +992,7 @@ async function runConnect(options, deps = {}) {
|
|
|
885
992
|
}
|
|
886
993
|
log("Return to Haven to approve the agent rules.");
|
|
887
994
|
if (runtimeInstall.restartRequired) {
|
|
888
|
-
log("
|
|
995
|
+
log("After approval, restart this agent normally so it can load Haven tools.");
|
|
889
996
|
}
|
|
890
997
|
return {
|
|
891
998
|
setupId: registration.setup_id,
|
|
@@ -911,10 +1018,12 @@ function secureLogger(log) {
|
|
|
911
1018
|
}
|
|
912
1019
|
function printRuntimeInstall(result, log) {
|
|
913
1020
|
for (const message of result.messages) log(message);
|
|
914
|
-
if (result.
|
|
1021
|
+
if (result.localMcpConfigured) {
|
|
1022
|
+
log("Configured local Haven MCP tools.");
|
|
1023
|
+
} else if (result.hostedMcpConfigured) {
|
|
915
1024
|
log("Configured hosted Haven MCP identity.");
|
|
916
1025
|
} else {
|
|
917
|
-
log("
|
|
1026
|
+
log("Haven MCP tools still need runtime setup.");
|
|
918
1027
|
}
|
|
919
1028
|
if (result.localSignerConfigured) {
|
|
920
1029
|
log("Configured local Haven signer.");
|
|
@@ -944,8 +1053,11 @@ function parseArgs(argv, env = process.env) {
|
|
|
944
1053
|
options.credentialsDir = requireValue(argv, ++i, arg);
|
|
945
1054
|
} else if (arg === "--environment-label") {
|
|
946
1055
|
options.environmentLabel = requireValue(argv, ++i, arg);
|
|
1056
|
+
} else if (arg === "--ack-local-tools") {
|
|
1057
|
+
options.ackLocalTools = true;
|
|
947
1058
|
} else if (arg === "--ack-signer") {
|
|
948
1059
|
options.ackSigner = true;
|
|
1060
|
+
options.ackLocalTools = true;
|
|
949
1061
|
} else if (arg === "--version") {
|
|
950
1062
|
process.stdout.write(`${CONNECTOR_VERSION}
|
|
951
1063
|
`);
|
|
@@ -974,7 +1086,7 @@ function helpText() {
|
|
|
974
1086
|
"sends Haven only the public signing address plus a proof signature.",
|
|
975
1087
|
"",
|
|
976
1088
|
"Usage:",
|
|
977
|
-
" npx -y @haven_ai/connect --setup hv_setup_... --api https://api.haven.example --ack-
|
|
1089
|
+
" npx -y @haven_ai/connect --setup hv_setup_... --api https://api.haven.example --ack-local-tools --runtime claude-code",
|
|
978
1090
|
"",
|
|
979
1091
|
"Options:",
|
|
980
1092
|
" --setup <token> Short-lived setup token from Haven.",
|
|
@@ -982,7 +1094,8 @@ function helpText() {
|
|
|
982
1094
|
" --runtime <name> Agent runtime hint, such as claude-code, codex-cli, cursor, vscode, or claude-desktop.",
|
|
983
1095
|
" --credentials-dir <path> Credential directory fallback. Defaults to ~/.haven/agents.",
|
|
984
1096
|
" --environment-label <text> Non-sensitive label shown in Haven setup review.",
|
|
985
|
-
" --ack-
|
|
1097
|
+
" --ack-local-tools Write the one-time local Haven tools acknowledgement during setup.",
|
|
1098
|
+
" --ack-signer Backward-compatible alias for --ack-local-tools.",
|
|
986
1099
|
" --help Show this help.",
|
|
987
1100
|
"",
|
|
988
1101
|
"The connector never prints the private key and never sends it to Haven."
|