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