@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/index.cjs
CHANGED
|
@@ -7,6 +7,7 @@ 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');
|
|
10
11
|
var signer = require('@haven_ai/signer');
|
|
11
12
|
|
|
12
13
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -50,10 +51,13 @@ function createConnectApiClient(baseUrl, fetchImpl = fetch) {
|
|
|
50
51
|
body: JSON.stringify({
|
|
51
52
|
runtime: input.runtime,
|
|
52
53
|
connector_version: input.connectorVersion,
|
|
54
|
+
runtime_mcp_mode: input.runtimeMcpMode,
|
|
53
55
|
hosted_mcp_configured: input.hostedMcpConfigured,
|
|
54
56
|
local_signer_configured: input.localSignerConfigured,
|
|
57
|
+
local_mcp_configured: input.localMcpConfigured,
|
|
55
58
|
credential_files_written: input.credentialFilesWritten,
|
|
56
59
|
signer_acknowledged: input.signerAcknowledged,
|
|
60
|
+
local_mcp_acknowledged: input.localMcpAcknowledged,
|
|
57
61
|
activation_command_available: input.activationCommandAvailable,
|
|
58
62
|
probe_result: input.probeResult,
|
|
59
63
|
restart_required: input.restartRequired,
|
|
@@ -136,6 +140,7 @@ async function writeCredentialFiles(input) {
|
|
|
136
140
|
signerPath,
|
|
137
141
|
{
|
|
138
142
|
delegate_key: input.delegateKey,
|
|
143
|
+
delegate_address: input.delegateAddress,
|
|
139
144
|
agent_id: input.agentId,
|
|
140
145
|
safe_address: input.safeAddress,
|
|
141
146
|
chain_id: input.chainId,
|
|
@@ -155,6 +160,7 @@ async function writeCredentialFiles(input) {
|
|
|
155
160
|
network: input.network,
|
|
156
161
|
api_url: input.apiUrl,
|
|
157
162
|
hosted_mcp_url: input.hostedMcpUrl,
|
|
163
|
+
agent_budget: input.agentBudget,
|
|
158
164
|
note: "Haven API key identifies the agent only. It cannot spend without the local signer key and on-chain Haven wallet rules."
|
|
159
165
|
},
|
|
160
166
|
input.warn
|
|
@@ -215,6 +221,8 @@ async function writeRuntimeConfig(input) {
|
|
|
215
221
|
return {
|
|
216
222
|
hostedConfigured: false,
|
|
217
223
|
signerConfigured: false,
|
|
224
|
+
localMcpConfigured: false,
|
|
225
|
+
runtimeMcpMode: "manual",
|
|
218
226
|
target: "manual runtime setup",
|
|
219
227
|
changed: false,
|
|
220
228
|
restartRequired: true,
|
|
@@ -256,17 +264,13 @@ function mergeJsonMcpConfig(existingJson, serverRoot, hostedServer, signerServer
|
|
|
256
264
|
return `${JSON.stringify(config, null, 2)}
|
|
257
265
|
`;
|
|
258
266
|
}
|
|
259
|
-
function mergeCodexToml(existingToml,
|
|
267
|
+
function mergeCodexToml(existingToml, identityPath, signerPath) {
|
|
260
268
|
let next = removeTomlTable(removeTomlTable(existingToml, "mcp_servers.haven"), "mcp_servers.haven_signer");
|
|
261
269
|
next = next.trimEnd();
|
|
262
270
|
const block = [
|
|
263
271
|
"[mcp_servers.haven]",
|
|
264
|
-
`url = ${tomlString(hostedMcpUrl)}`,
|
|
265
|
-
'bearer_token_env_var = "HAVEN_TOKEN"',
|
|
266
|
-
"",
|
|
267
|
-
"[mcp_servers.haven_signer]",
|
|
268
272
|
'command = "npx"',
|
|
269
|
-
`args = ["-y", ${tomlString(
|
|
273
|
+
`args = ["-y", ${tomlString(localMcpPackageName())}, "--identity", ${tomlString(identityPath)}, "--signer", ${tomlString(signerPath)}]`
|
|
270
274
|
].join("\n");
|
|
271
275
|
return `${next ? `${next}
|
|
272
276
|
|
|
@@ -286,6 +290,8 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
|
|
|
286
290
|
return {
|
|
287
291
|
hostedConfigured: true,
|
|
288
292
|
signerConfigured: true,
|
|
293
|
+
localMcpConfigured: false,
|
|
294
|
+
runtimeMcpMode: "hosted_plus_signer",
|
|
289
295
|
target: configTargetLabel(input.runtime),
|
|
290
296
|
changed: existing !== merged,
|
|
291
297
|
restartRequired: input.runtime === "claude-desktop",
|
|
@@ -295,6 +301,8 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
|
|
|
295
301
|
return {
|
|
296
302
|
hostedConfigured: false,
|
|
297
303
|
signerConfigured: false,
|
|
304
|
+
localMcpConfigured: false,
|
|
305
|
+
runtimeMcpMode: "hosted_plus_signer",
|
|
298
306
|
target: configTargetLabel(input.runtime),
|
|
299
307
|
changed: false,
|
|
300
308
|
restartRequired: true,
|
|
@@ -305,41 +313,29 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
|
|
|
305
313
|
}
|
|
306
314
|
async function writeCodexConfig(input) {
|
|
307
315
|
const target = codexConfigPath(input.homeDir);
|
|
308
|
-
const envTarget = path.join(input.credentialDirectory, "identity.env");
|
|
309
|
-
const launchTarget = path.join(input.credentialDirectory, "start-codex.sh");
|
|
310
316
|
try {
|
|
311
317
|
const existing = await readOptional(target);
|
|
312
|
-
const merged = mergeCodexToml(existing ?? "", input.
|
|
318
|
+
const merged = mergeCodexToml(existing ?? "", input.identityPath, input.signerPath);
|
|
313
319
|
await writeOwnerOnlyText(target, merged);
|
|
314
|
-
await writeOwnerOnlyText(envTarget, `export HAVEN_TOKEN=${shellToken(input.apiKey)}
|
|
315
|
-
`);
|
|
316
|
-
await writeOwnerExecutableText(
|
|
317
|
-
launchTarget,
|
|
318
|
-
[
|
|
319
|
-
"#!/bin/sh",
|
|
320
|
-
"set -eu",
|
|
321
|
-
`. ${shellToken(envTarget)}`,
|
|
322
|
-
'exec codex "$@"',
|
|
323
|
-
""
|
|
324
|
-
].join("\n")
|
|
325
|
-
);
|
|
326
320
|
return {
|
|
327
|
-
hostedConfigured:
|
|
321
|
+
hostedConfigured: false,
|
|
328
322
|
signerConfigured: true,
|
|
323
|
+
localMcpConfigured: true,
|
|
324
|
+
runtimeMcpMode: "local_stdio",
|
|
329
325
|
target: "Codex CLI config",
|
|
330
326
|
changed: existing !== merged,
|
|
331
327
|
restartRequired: true,
|
|
332
|
-
activationCommand: shellToken(launchTarget),
|
|
333
328
|
messages: [
|
|
334
|
-
"Updated Haven MCP
|
|
335
|
-
"
|
|
336
|
-
`Restart Codex with: ${shellToken(launchTarget)}`
|
|
329
|
+
"Updated local Haven MCP entry in Codex CLI config.",
|
|
330
|
+
"After Haven approval, restart Codex normally so it can load Haven tools."
|
|
337
331
|
]
|
|
338
332
|
};
|
|
339
333
|
} catch (err) {
|
|
340
334
|
return {
|
|
341
335
|
hostedConfigured: false,
|
|
342
336
|
signerConfigured: false,
|
|
337
|
+
localMcpConfigured: false,
|
|
338
|
+
runtimeMcpMode: "local_stdio",
|
|
343
339
|
target: "Codex CLI config",
|
|
344
340
|
changed: false,
|
|
345
341
|
restartRequired: true,
|
|
@@ -361,11 +357,6 @@ async function writeOwnerOnlyText(path$1, value) {
|
|
|
361
357
|
await promises.writeFile(path$1, value, { mode: 384 });
|
|
362
358
|
await promises.chmod(path$1, 384).catch(() => void 0);
|
|
363
359
|
}
|
|
364
|
-
async function writeOwnerExecutableText(path$1, value) {
|
|
365
|
-
await promises.mkdir(path.dirname(path$1), { recursive: true, mode: 448 });
|
|
366
|
-
await promises.writeFile(path$1, value, { mode: 448 });
|
|
367
|
-
await promises.chmod(path$1, 448).catch(() => void 0);
|
|
368
|
-
}
|
|
369
360
|
function parseJsonObject(value) {
|
|
370
361
|
const parsed = JSON.parse(value);
|
|
371
362
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -394,9 +385,6 @@ function removeTomlTable(toml, table) {
|
|
|
394
385
|
function tomlString(value) {
|
|
395
386
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
396
387
|
}
|
|
397
|
-
function shellToken(value) {
|
|
398
|
-
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
399
|
-
}
|
|
400
388
|
function cursorConfigPath(homeDir = os.homedir()) {
|
|
401
389
|
return path.resolve(homeDir, ".cursor", "mcp.json");
|
|
402
390
|
}
|
|
@@ -434,6 +422,85 @@ function configTargetLabel(runtime) {
|
|
|
434
422
|
function signerPackageName() {
|
|
435
423
|
return `@haven_ai/signer@${signer.SIGNER_VERSION}`;
|
|
436
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
|
+
}
|
|
437
504
|
async function probeHostedMcpTools(apiKey, hostedMcpUrl, fetchImpl = fetch) {
|
|
438
505
|
let response;
|
|
439
506
|
try {
|
|
@@ -588,7 +655,7 @@ async function acknowledgeLocalSignerConsent(signerPath, log) {
|
|
|
588
655
|
const decision = await signer.ensureSignerConsent(input, {
|
|
589
656
|
credentialsPath: signerPath,
|
|
590
657
|
writeAck: true,
|
|
591
|
-
out: log ? { write: (chunk) =>
|
|
658
|
+
out: log ? { write: (chunk) => writeLogChunk2(log, chunk) } : void 0
|
|
592
659
|
});
|
|
593
660
|
return {
|
|
594
661
|
acknowledged: decision.ok,
|
|
@@ -647,7 +714,7 @@ async function readSignerAckFile(path) {
|
|
|
647
714
|
return null;
|
|
648
715
|
}
|
|
649
716
|
}
|
|
650
|
-
function
|
|
717
|
+
function writeLogChunk2(log, chunk) {
|
|
651
718
|
const message = String(chunk).trimEnd();
|
|
652
719
|
if (message) log(message);
|
|
653
720
|
}
|
|
@@ -657,23 +724,28 @@ var execFileAsync = util.promisify(child_process.execFile);
|
|
|
657
724
|
async function installRuntime(input, deps = {}) {
|
|
658
725
|
const runtime = normalizeRuntime(input.runtime, deps.env);
|
|
659
726
|
const profile = runtimeProfile(runtime, deps.env);
|
|
660
|
-
const
|
|
661
|
-
const
|
|
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);
|
|
662
731
|
if (runtime === "other") {
|
|
663
732
|
const signerCredentialReady2 = await probeLocalSignerCredential(input.signerPath);
|
|
664
|
-
const signerReady = signerCredentialReady2 && signerConsent
|
|
733
|
+
const signerReady = signerCredentialReady2 && signerConsent?.acknowledged;
|
|
665
734
|
return {
|
|
666
735
|
runtime,
|
|
736
|
+
runtimeMcpMode: "manual",
|
|
667
737
|
hostedMcpConfigured: false,
|
|
668
738
|
localSignerConfigured: false,
|
|
739
|
+
localMcpConfigured: false,
|
|
669
740
|
probeResult: signerReady ? "manual_runtime_setup_required_local_signer_ready" : "manual_runtime_setup_required_local_signer_unavailable",
|
|
670
741
|
restartRequired: true,
|
|
671
742
|
nextUserAction: "return_to_haven_for_wallet_approval_then_configure_runtime",
|
|
672
743
|
errorCode: "manual_runtime_setup_required",
|
|
673
744
|
configTarget: "manual runtime setup",
|
|
674
|
-
signerAcknowledged: signerConsent
|
|
745
|
+
signerAcknowledged: signerConsent?.acknowledged,
|
|
746
|
+
localMcpAcknowledged: false,
|
|
675
747
|
messages: [
|
|
676
|
-
...
|
|
748
|
+
...consentMessages,
|
|
677
749
|
"Runtime was not recognized. Keep the local credentials and add Haven MCP entries manually after wallet approval."
|
|
678
750
|
]
|
|
679
751
|
};
|
|
@@ -682,6 +754,7 @@ async function installRuntime(input, deps = {}) {
|
|
|
682
754
|
runtime,
|
|
683
755
|
hostedMcpUrl: input.hostedMcpUrl,
|
|
684
756
|
apiKey: input.apiKey,
|
|
757
|
+
identityPath: input.identityPath,
|
|
685
758
|
signerPath: input.signerPath,
|
|
686
759
|
credentialDirectory: input.credentialDirectory,
|
|
687
760
|
homeDir: deps.homeDir
|
|
@@ -691,21 +764,25 @@ async function installRuntime(input, deps = {}) {
|
|
|
691
764
|
probeLocalSignerCredential(input.signerPath)
|
|
692
765
|
]);
|
|
693
766
|
const hostedOk = configResult.hostedConfigured && hostedProbe.status !== "unauthorized";
|
|
694
|
-
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);
|
|
695
769
|
const restartRequired = configResult.restartRequired || restartRequiredForRuntime(runtime, deps.env);
|
|
696
|
-
const errorCode = configResult.errorCode ?? signerConsentErrorCode(signerCredentialReady, signerConsent);
|
|
770
|
+
const errorCode = configResult.errorCode ?? (configResult.runtimeMcpMode === "local_stdio" ? localMcpConsentErrorCode(signerCredentialReady, localMcpConsent) : signerConsentErrorCode(signerCredentialReady, signerConsent));
|
|
697
771
|
return {
|
|
698
772
|
runtime,
|
|
773
|
+
runtimeMcpMode: configResult.runtimeMcpMode,
|
|
699
774
|
hostedMcpConfigured: hostedOk,
|
|
700
775
|
localSignerConfigured: signerOk,
|
|
701
|
-
|
|
776
|
+
localMcpConfigured: localMcpOk,
|
|
777
|
+
probeResult: buildProbeResult(configResult.runtimeMcpMode, configResult.hostedConfigured, hostedProbe.status, signerOk, localMcpOk),
|
|
702
778
|
restartRequired,
|
|
703
779
|
nextUserAction: nextAction(runtime, profile.restartMode, errorCode),
|
|
704
780
|
errorCode,
|
|
705
781
|
configTarget: configResult.target,
|
|
706
|
-
signerAcknowledged: signerConsent
|
|
782
|
+
signerAcknowledged: signerConsent?.acknowledged,
|
|
783
|
+
localMcpAcknowledged: localMcpConsent?.acknowledged,
|
|
707
784
|
activationCommand: configResult.activationCommand,
|
|
708
|
-
messages: [...
|
|
785
|
+
messages: [...consentMessages, ...configResult.messages]
|
|
709
786
|
};
|
|
710
787
|
}
|
|
711
788
|
function runtimeInstallCapabilities(runtime, env = process.env) {
|
|
@@ -721,39 +798,36 @@ async function configureClaudeCode(input, deps) {
|
|
|
721
798
|
await runCommand("claude", [
|
|
722
799
|
"mcp",
|
|
723
800
|
"add",
|
|
724
|
-
"--transport",
|
|
725
|
-
"http",
|
|
726
801
|
"haven",
|
|
727
|
-
input.hostedMcpUrl,
|
|
728
|
-
"--header",
|
|
729
|
-
`Authorization: Bearer ${input.apiKey}`
|
|
730
|
-
]);
|
|
731
|
-
await runCommand("claude", [
|
|
732
|
-
"mcp",
|
|
733
|
-
"add",
|
|
734
|
-
"haven-signer",
|
|
735
802
|
"--",
|
|
736
803
|
"npx",
|
|
737
804
|
"-y",
|
|
738
|
-
|
|
739
|
-
"--
|
|
805
|
+
localMcpPackageName2(),
|
|
806
|
+
"--identity",
|
|
807
|
+
input.identityPath,
|
|
808
|
+
"--signer",
|
|
740
809
|
input.signerPath
|
|
741
810
|
]);
|
|
811
|
+
await runCommand("claude", ["mcp", "remove", "haven-signer"]).catch(() => void 0);
|
|
742
812
|
return {
|
|
743
|
-
hostedConfigured:
|
|
813
|
+
hostedConfigured: false,
|
|
744
814
|
signerConfigured: true,
|
|
815
|
+
localMcpConfigured: true,
|
|
816
|
+
runtimeMcpMode: "local_stdio",
|
|
745
817
|
target: "Claude Code MCP config",
|
|
746
818
|
changed: true,
|
|
747
819
|
restartRequired: true,
|
|
748
820
|
messages: [
|
|
749
|
-
"Updated Haven MCP
|
|
750
|
-
"
|
|
821
|
+
"Updated local Haven MCP entry with Claude Code.",
|
|
822
|
+
"After Haven approval, restart Claude Code normally so it can load Haven tools."
|
|
751
823
|
]
|
|
752
824
|
};
|
|
753
825
|
} catch (err) {
|
|
754
826
|
return {
|
|
755
827
|
hostedConfigured: false,
|
|
756
828
|
signerConfigured: false,
|
|
829
|
+
localMcpConfigured: false,
|
|
830
|
+
runtimeMcpMode: "local_stdio",
|
|
757
831
|
target: "Claude Code MCP config",
|
|
758
832
|
changed: false,
|
|
759
833
|
restartRequired: true,
|
|
@@ -768,13 +842,28 @@ async function configureClaudeCode(input, deps) {
|
|
|
768
842
|
async function defaultRunCommand(command, args) {
|
|
769
843
|
await execFileAsync(command, args, { timeout: 1e4 });
|
|
770
844
|
}
|
|
771
|
-
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
|
+
}
|
|
772
849
|
const hostedPart = hostedConfigured ? `hosted_${hostedStatus}` : "hosted_not_configured";
|
|
773
850
|
const signerPart = signerReady ? "local_signer_ready" : "local_signer_unavailable";
|
|
774
851
|
return `${hostedPart}_${signerPart}`.slice(0, 120);
|
|
775
852
|
}
|
|
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
|
+
}
|
|
776
865
|
async function resolveSignerConsent(input, messages) {
|
|
777
|
-
if (input.ackSigner) {
|
|
866
|
+
if (input.ackSigner || input.ackLocalTools) {
|
|
778
867
|
const status = await acknowledgeLocalSignerConsent(input.signerPath, (message) => messages.push(message));
|
|
779
868
|
if (status.acknowledged) {
|
|
780
869
|
messages.push("Prepared the local Haven signer acknowledgement.");
|
|
@@ -787,24 +876,32 @@ async function resolveSignerConsent(input, messages) {
|
|
|
787
876
|
}
|
|
788
877
|
function signerConsentErrorCode(signerCredentialReady, signerConsent) {
|
|
789
878
|
if (!signerCredentialReady) return "local_signer_credential_unavailable";
|
|
790
|
-
if (!signerConsent
|
|
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";
|
|
791
885
|
return void 0;
|
|
792
886
|
}
|
|
793
887
|
function nextAction(runtime, restartMode, errorCode) {
|
|
794
888
|
if (errorCode) return "return_to_haven_for_wallet_approval_then_finish_runtime_setup";
|
|
795
889
|
if (restartMode === "hot-reload") return "return_to_haven_for_wallet_approval";
|
|
796
|
-
if (runtime === "codex-cli") return "
|
|
890
|
+
if (runtime === "codex-cli") return "return_to_haven_for_wallet_approval_then_restart_codex";
|
|
797
891
|
if (runtime === "claude-code") return "return_to_haven_for_wallet_approval_then_restart_claude_code";
|
|
798
892
|
if (restartMode === "restart-app") return "return_to_haven_for_wallet_approval_then_restart_app";
|
|
799
893
|
if (restartMode === "restart-session") return "return_to_haven_for_wallet_approval_then_restart_agent_session";
|
|
800
894
|
return "return_to_haven_for_wallet_approval_then_configure_runtime";
|
|
801
895
|
}
|
|
802
|
-
function
|
|
803
|
-
return `@haven_ai/
|
|
896
|
+
function localMcpPackageName2() {
|
|
897
|
+
return `@haven_ai/mcp@${mcp.MCP_VERSION}`;
|
|
898
|
+
}
|
|
899
|
+
function usesLocalMcp(runtime) {
|
|
900
|
+
return runtime === "codex-cli" || runtime === "claude-code";
|
|
804
901
|
}
|
|
805
902
|
|
|
806
903
|
// src/runtime.ts
|
|
807
|
-
var CONNECTOR_VERSION = "0.1.
|
|
904
|
+
var CONNECTOR_VERSION = "0.1.2";
|
|
808
905
|
async function runConnect(options, deps = {}) {
|
|
809
906
|
const connectorVersion = options.connectorVersion ?? CONNECTOR_VERSION;
|
|
810
907
|
const api = deps.api ?? createConnectApiClient(options.apiBaseUrl);
|
|
@@ -850,9 +947,15 @@ async function runConnect(options, deps = {}) {
|
|
|
850
947
|
agentId: registration.agent_id,
|
|
851
948
|
apiKey: localApiKey,
|
|
852
949
|
delegateKey: localKey.privateKey,
|
|
950
|
+
delegateAddress: localKey.address,
|
|
853
951
|
safeAddress: setup.haven_wallet.address,
|
|
854
952
|
chainId: setup.haven_wallet.chain_id,
|
|
855
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
|
+
})),
|
|
856
959
|
apiUrl: options.apiBaseUrl,
|
|
857
960
|
hostedMcpUrl: registration.hosted_mcp_url,
|
|
858
961
|
warn: log
|
|
@@ -867,17 +970,21 @@ async function runConnect(options, deps = {}) {
|
|
|
867
970
|
identityPath: credentialPaths.identityPath,
|
|
868
971
|
credentialDirectory: credentialPaths.directory,
|
|
869
972
|
environmentLabel: options.environmentLabel ?? "Local workspace",
|
|
870
|
-
ackSigner: options.ackSigner
|
|
973
|
+
ackSigner: options.ackSigner,
|
|
974
|
+
ackLocalTools: options.ackLocalTools
|
|
871
975
|
});
|
|
872
976
|
printRuntimeInstall(runtimeInstall, log);
|
|
873
977
|
try {
|
|
874
978
|
await api.updateInstallStatus(registration.setup_id, localApiKey, {
|
|
875
979
|
runtime: runtimeInstall.runtime,
|
|
876
980
|
connectorVersion,
|
|
981
|
+
runtimeMcpMode: runtimeInstall.runtimeMcpMode,
|
|
877
982
|
hostedMcpConfigured: runtimeInstall.hostedMcpConfigured,
|
|
878
983
|
localSignerConfigured: runtimeInstall.localSignerConfigured,
|
|
984
|
+
localMcpConfigured: runtimeInstall.localMcpConfigured,
|
|
879
985
|
credentialFilesWritten: true,
|
|
880
986
|
signerAcknowledged: runtimeInstall.signerAcknowledged,
|
|
987
|
+
localMcpAcknowledged: runtimeInstall.localMcpAcknowledged,
|
|
881
988
|
activationCommandAvailable: Boolean(runtimeInstall.activationCommand),
|
|
882
989
|
probeResult: runtimeInstall.probeResult,
|
|
883
990
|
restartRequired: runtimeInstall.restartRequired,
|
|
@@ -890,7 +997,7 @@ async function runConnect(options, deps = {}) {
|
|
|
890
997
|
}
|
|
891
998
|
log("Return to Haven to approve the agent rules.");
|
|
892
999
|
if (runtimeInstall.restartRequired) {
|
|
893
|
-
log("
|
|
1000
|
+
log("After approval, restart this agent normally so it can load Haven tools.");
|
|
894
1001
|
}
|
|
895
1002
|
return {
|
|
896
1003
|
setupId: registration.setup_id,
|
|
@@ -916,10 +1023,12 @@ function secureLogger(log) {
|
|
|
916
1023
|
}
|
|
917
1024
|
function printRuntimeInstall(result, log) {
|
|
918
1025
|
for (const message of result.messages) log(message);
|
|
919
|
-
if (result.
|
|
1026
|
+
if (result.localMcpConfigured) {
|
|
1027
|
+
log("Configured local Haven MCP tools.");
|
|
1028
|
+
} else if (result.hostedMcpConfigured) {
|
|
920
1029
|
log("Configured hosted Haven MCP identity.");
|
|
921
1030
|
} else {
|
|
922
|
-
log("
|
|
1031
|
+
log("Haven MCP tools still need runtime setup.");
|
|
923
1032
|
}
|
|
924
1033
|
if (result.localSignerConfigured) {
|
|
925
1034
|
log("Configured local Haven signer.");
|
|
@@ -949,8 +1058,11 @@ function parseArgs(argv, env = process.env) {
|
|
|
949
1058
|
options.credentialsDir = requireValue(argv, ++i, arg);
|
|
950
1059
|
} else if (arg === "--environment-label") {
|
|
951
1060
|
options.environmentLabel = requireValue(argv, ++i, arg);
|
|
1061
|
+
} else if (arg === "--ack-local-tools") {
|
|
1062
|
+
options.ackLocalTools = true;
|
|
952
1063
|
} else if (arg === "--ack-signer") {
|
|
953
1064
|
options.ackSigner = true;
|
|
1065
|
+
options.ackLocalTools = true;
|
|
954
1066
|
} else if (arg === "--version") {
|
|
955
1067
|
process.stdout.write(`${CONNECTOR_VERSION}
|
|
956
1068
|
`);
|
|
@@ -979,7 +1091,7 @@ function helpText() {
|
|
|
979
1091
|
"sends Haven only the public signing address plus a proof signature.",
|
|
980
1092
|
"",
|
|
981
1093
|
"Usage:",
|
|
982
|
-
" npx -y @haven_ai/connect --setup hv_setup_... --api https://api.haven.example --ack-
|
|
1094
|
+
" npx -y @haven_ai/connect --setup hv_setup_... --api https://api.haven.example --ack-local-tools --runtime claude-code",
|
|
983
1095
|
"",
|
|
984
1096
|
"Options:",
|
|
985
1097
|
" --setup <token> Short-lived setup token from Haven.",
|
|
@@ -987,7 +1099,8 @@ function helpText() {
|
|
|
987
1099
|
" --runtime <name> Agent runtime hint, such as claude-code, codex-cli, cursor, vscode, or claude-desktop.",
|
|
988
1100
|
" --credentials-dir <path> Credential directory fallback. Defaults to ~/.haven/agents.",
|
|
989
1101
|
" --environment-label <text> Non-sensitive label shown in Haven setup review.",
|
|
990
|
-
" --ack-
|
|
1102
|
+
" --ack-local-tools Write the one-time local Haven tools acknowledgement during setup.",
|
|
1103
|
+
" --ack-signer Backward-compatible alias for --ack-local-tools.",
|
|
991
1104
|
" --help Show this help.",
|
|
992
1105
|
"",
|
|
993
1106
|
"The connector never prints the private key and never sends it to Haven."
|