@haven_ai/connect 0.1.0-alpha → 0.1.1-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 +159 -22
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +159 -22
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +159 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +159 -22
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
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 signer = require('@haven_ai/signer');
|
|
10
11
|
|
|
11
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
13
|
|
|
@@ -52,6 +53,8 @@ function createConnectApiClient(baseUrl, fetchImpl = fetch) {
|
|
|
52
53
|
hosted_mcp_configured: input.hostedMcpConfigured,
|
|
53
54
|
local_signer_configured: input.localSignerConfigured,
|
|
54
55
|
credential_files_written: input.credentialFilesWritten,
|
|
56
|
+
signer_acknowledged: input.signerAcknowledged,
|
|
57
|
+
activation_command_available: input.activationCommandAvailable,
|
|
55
58
|
probe_result: input.probeResult,
|
|
56
59
|
restart_required: input.restartRequired,
|
|
57
60
|
next_user_action: input.nextUserAction,
|
|
@@ -236,7 +239,7 @@ function buildHostedServer(hostedMcpUrl, apiKey, runtime) {
|
|
|
236
239
|
function buildSignerServer(signerPath, runtime) {
|
|
237
240
|
const server = {
|
|
238
241
|
command: "npx",
|
|
239
|
-
args: ["-y",
|
|
242
|
+
args: ["-y", signerPackageName(), "--credentials", signerPath]
|
|
240
243
|
};
|
|
241
244
|
if (runtime === "vscode") return { type: "stdio", ...server };
|
|
242
245
|
return server;
|
|
@@ -263,7 +266,7 @@ function mergeCodexToml(existingToml, hostedMcpUrl, signerPath) {
|
|
|
263
266
|
"",
|
|
264
267
|
"[mcp_servers.haven_signer]",
|
|
265
268
|
'command = "npx"',
|
|
266
|
-
`args = ["-y",
|
|
269
|
+
`args = ["-y", ${tomlString(signerPackageName())}, "--credentials", ${tomlString(signerPath)}]`
|
|
267
270
|
].join("\n");
|
|
268
271
|
return `${next ? `${next}
|
|
269
272
|
|
|
@@ -303,23 +306,35 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
|
|
|
303
306
|
async function writeCodexConfig(input) {
|
|
304
307
|
const target = codexConfigPath(input.homeDir);
|
|
305
308
|
const envTarget = path.join(input.credentialDirectory, "identity.env");
|
|
309
|
+
const launchTarget = path.join(input.credentialDirectory, "start-codex.sh");
|
|
306
310
|
try {
|
|
307
311
|
const existing = await readOptional(target);
|
|
308
312
|
const merged = mergeCodexToml(existing ?? "", input.hostedMcpUrl, input.signerPath);
|
|
309
313
|
await writeOwnerOnlyText(target, merged);
|
|
310
|
-
await writeOwnerOnlyText(envTarget, `HAVEN_TOKEN=${shellToken(input.apiKey)}
|
|
314
|
+
await writeOwnerOnlyText(envTarget, `export HAVEN_TOKEN=${shellToken(input.apiKey)}
|
|
311
315
|
`);
|
|
316
|
+
await writeOwnerExecutableText(
|
|
317
|
+
launchTarget,
|
|
318
|
+
[
|
|
319
|
+
"#!/bin/sh",
|
|
320
|
+
"set -eu",
|
|
321
|
+
`. ${shellToken(envTarget)}`,
|
|
322
|
+
'exec codex "$@"',
|
|
323
|
+
""
|
|
324
|
+
].join("\n")
|
|
325
|
+
);
|
|
312
326
|
return {
|
|
313
|
-
hostedConfigured:
|
|
327
|
+
hostedConfigured: true,
|
|
314
328
|
signerConfigured: true,
|
|
315
329
|
target: "Codex CLI config",
|
|
316
330
|
changed: existing !== merged,
|
|
317
331
|
restartRequired: true,
|
|
332
|
+
activationCommand: shellToken(launchTarget),
|
|
318
333
|
messages: [
|
|
319
334
|
"Updated Haven MCP entries in Codex CLI config.",
|
|
320
|
-
"Wrote the hosted MCP token to a private env file.
|
|
321
|
-
|
|
322
|
-
|
|
335
|
+
"Wrote the hosted MCP token to a private env file.",
|
|
336
|
+
`Restart Codex with: ${shellToken(launchTarget)}`
|
|
337
|
+
]
|
|
323
338
|
};
|
|
324
339
|
} catch (err) {
|
|
325
340
|
return {
|
|
@@ -346,6 +361,11 @@ async function writeOwnerOnlyText(path$1, value) {
|
|
|
346
361
|
await promises.writeFile(path$1, value, { mode: 384 });
|
|
347
362
|
await promises.chmod(path$1, 384).catch(() => void 0);
|
|
348
363
|
}
|
|
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
|
+
}
|
|
349
369
|
function parseJsonObject(value) {
|
|
350
370
|
const parsed = JSON.parse(value);
|
|
351
371
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -411,6 +431,9 @@ function configTargetLabel(runtime) {
|
|
|
411
431
|
return "runtime MCP config";
|
|
412
432
|
}
|
|
413
433
|
}
|
|
434
|
+
function signerPackageName() {
|
|
435
|
+
return `@haven_ai/signer@${signer.SIGNER_VERSION}`;
|
|
436
|
+
}
|
|
414
437
|
async function probeHostedMcpTools(apiKey, hostedMcpUrl, fetchImpl = fetch) {
|
|
415
438
|
let response;
|
|
416
439
|
try {
|
|
@@ -559,24 +582,100 @@ function detectRuntime(env) {
|
|
|
559
582
|
if (env.VSCODE_CWD || env.VSCODE_IPC_HOOK_CLI || env.TERM_PROGRAM === "vscode") return "vscode";
|
|
560
583
|
return null;
|
|
561
584
|
}
|
|
585
|
+
async function acknowledgeLocalSignerConsent(signerPath, log) {
|
|
586
|
+
try {
|
|
587
|
+
const input = await buildSignerConsentInput(signerPath);
|
|
588
|
+
const decision = await signer.ensureSignerConsent(input, {
|
|
589
|
+
credentialsPath: signerPath,
|
|
590
|
+
writeAck: true,
|
|
591
|
+
out: log ? { write: (chunk) => writeLogChunk(log, chunk) } : void 0
|
|
592
|
+
});
|
|
593
|
+
return {
|
|
594
|
+
acknowledged: decision.ok,
|
|
595
|
+
hash: decision.hash,
|
|
596
|
+
reason: decision.reason
|
|
597
|
+
};
|
|
598
|
+
} catch (err) {
|
|
599
|
+
return {
|
|
600
|
+
acknowledged: false,
|
|
601
|
+
error: err instanceof Error ? err.message : String(err)
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
async function getLocalSignerConsentStatus(signerPath) {
|
|
606
|
+
try {
|
|
607
|
+
const input = await buildSignerConsentInput(signerPath);
|
|
608
|
+
const hash = signer.computeSignerConsentHash(input);
|
|
609
|
+
const stored = await readSignerAckFile(signerAckPath(signerPath));
|
|
610
|
+
if (stored === hash) {
|
|
611
|
+
return { acknowledged: true, hash, reason: "ack_file_match" };
|
|
612
|
+
}
|
|
613
|
+
return {
|
|
614
|
+
acknowledged: false,
|
|
615
|
+
hash,
|
|
616
|
+
reason: stored ? "ack_file_mismatch" : "ack_file_missing"
|
|
617
|
+
};
|
|
618
|
+
} catch (err) {
|
|
619
|
+
return {
|
|
620
|
+
acknowledged: false,
|
|
621
|
+
error: err instanceof Error ? err.message : String(err)
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
function signerAckPath(signerPath) {
|
|
626
|
+
return path.resolve(`${signerPath}.signer-ack.json`);
|
|
627
|
+
}
|
|
628
|
+
async function buildSignerConsentInput(signerPath) {
|
|
629
|
+
const credentials = await signer.loadSignerCredentials(signerPath);
|
|
630
|
+
const signer$1 = signer.createEdgeSigner(credentials.delegateKey, {
|
|
631
|
+
x402BindingSigner: credentials.x402BindingSigner
|
|
632
|
+
});
|
|
633
|
+
return {
|
|
634
|
+
delegateAddress: signer$1.delegateAddress,
|
|
635
|
+
safeAddress: credentials.safeAddress,
|
|
636
|
+
agentId: credentials.agentId,
|
|
637
|
+
chainId: credentials.chainId,
|
|
638
|
+
network: credentials.network,
|
|
639
|
+
toolNames: Object.keys(signer.toolSchemas)
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
async function readSignerAckFile(path) {
|
|
643
|
+
try {
|
|
644
|
+
const parsed = JSON.parse(await promises.readFile(path, "utf8"));
|
|
645
|
+
return typeof parsed.ack === "string" ? parsed.ack : null;
|
|
646
|
+
} catch {
|
|
647
|
+
return null;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
function writeLogChunk(log, chunk) {
|
|
651
|
+
const message = String(chunk).trimEnd();
|
|
652
|
+
if (message) log(message);
|
|
653
|
+
}
|
|
562
654
|
|
|
563
655
|
// src/runtime-install.ts
|
|
564
656
|
var execFileAsync = util.promisify(child_process.execFile);
|
|
565
657
|
async function installRuntime(input, deps = {}) {
|
|
566
658
|
const runtime = normalizeRuntime(input.runtime, deps.env);
|
|
567
659
|
const profile = runtimeProfile(runtime, deps.env);
|
|
660
|
+
const signerConsentMessages = [];
|
|
661
|
+
const signerConsent = await resolveSignerConsent(input, signerConsentMessages);
|
|
568
662
|
if (runtime === "other") {
|
|
569
|
-
const
|
|
663
|
+
const signerCredentialReady2 = await probeLocalSignerCredential(input.signerPath);
|
|
664
|
+
const signerReady = signerCredentialReady2 && signerConsent.acknowledged;
|
|
570
665
|
return {
|
|
571
666
|
runtime,
|
|
572
667
|
hostedMcpConfigured: false,
|
|
573
668
|
localSignerConfigured: false,
|
|
574
|
-
probeResult:
|
|
669
|
+
probeResult: signerReady ? "manual_runtime_setup_required_local_signer_ready" : "manual_runtime_setup_required_local_signer_unavailable",
|
|
575
670
|
restartRequired: true,
|
|
576
671
|
nextUserAction: "return_to_haven_for_wallet_approval_then_configure_runtime",
|
|
577
672
|
errorCode: "manual_runtime_setup_required",
|
|
578
673
|
configTarget: "manual runtime setup",
|
|
579
|
-
|
|
674
|
+
signerAcknowledged: signerConsent.acknowledged,
|
|
675
|
+
messages: [
|
|
676
|
+
...signerConsentMessages,
|
|
677
|
+
"Runtime was not recognized. Keep the local credentials and add Haven MCP entries manually after wallet approval."
|
|
678
|
+
]
|
|
580
679
|
};
|
|
581
680
|
}
|
|
582
681
|
const configResult = runtime === "claude-code" ? await configureClaudeCode(input, deps) : await writeRuntimeConfig({
|
|
@@ -587,23 +686,26 @@ async function installRuntime(input, deps = {}) {
|
|
|
587
686
|
credentialDirectory: input.credentialDirectory,
|
|
588
687
|
homeDir: deps.homeDir
|
|
589
688
|
});
|
|
590
|
-
const [hostedProbe,
|
|
689
|
+
const [hostedProbe, signerCredentialReady] = await Promise.all([
|
|
591
690
|
configResult.hostedConfigured ? probeHostedMcpTools(input.apiKey, input.hostedMcpUrl, deps.fetch) : Promise.resolve({ status: "bad_response" }),
|
|
592
691
|
probeLocalSignerCredential(input.signerPath)
|
|
593
692
|
]);
|
|
594
693
|
const hostedOk = configResult.hostedConfigured && hostedProbe.status !== "unauthorized";
|
|
595
|
-
const signerOk = configResult.signerConfigured &&
|
|
694
|
+
const signerOk = configResult.signerConfigured && signerCredentialReady && signerConsent.acknowledged;
|
|
596
695
|
const restartRequired = configResult.restartRequired || restartRequiredForRuntime(runtime, deps.env);
|
|
696
|
+
const errorCode = configResult.errorCode ?? signerConsentErrorCode(signerCredentialReady, signerConsent);
|
|
597
697
|
return {
|
|
598
698
|
runtime,
|
|
599
699
|
hostedMcpConfigured: hostedOk,
|
|
600
700
|
localSignerConfigured: signerOk,
|
|
601
701
|
probeResult: buildProbeResult(configResult.hostedConfigured, hostedProbe.status, signerOk),
|
|
602
702
|
restartRequired,
|
|
603
|
-
nextUserAction: nextAction(profile.restartMode,
|
|
604
|
-
errorCode
|
|
703
|
+
nextUserAction: nextAction(runtime, profile.restartMode, errorCode),
|
|
704
|
+
errorCode,
|
|
605
705
|
configTarget: configResult.target,
|
|
606
|
-
|
|
706
|
+
signerAcknowledged: signerConsent.acknowledged,
|
|
707
|
+
activationCommand: configResult.activationCommand,
|
|
708
|
+
messages: [...signerConsentMessages, ...configResult.messages]
|
|
607
709
|
};
|
|
608
710
|
}
|
|
609
711
|
function runtimeInstallCapabilities(runtime, env = process.env) {
|
|
@@ -630,9 +732,10 @@ async function configureClaudeCode(input, deps) {
|
|
|
630
732
|
"mcp",
|
|
631
733
|
"add",
|
|
632
734
|
"haven-signer",
|
|
735
|
+
"--",
|
|
633
736
|
"npx",
|
|
634
737
|
"-y",
|
|
635
|
-
|
|
738
|
+
signerPackageName2(),
|
|
636
739
|
"--credentials",
|
|
637
740
|
input.signerPath
|
|
638
741
|
]);
|
|
@@ -642,7 +745,10 @@ async function configureClaudeCode(input, deps) {
|
|
|
642
745
|
target: "Claude Code MCP config",
|
|
643
746
|
changed: true,
|
|
644
747
|
restartRequired: true,
|
|
645
|
-
messages: [
|
|
748
|
+
messages: [
|
|
749
|
+
"Updated Haven MCP entries with Claude Code.",
|
|
750
|
+
"Restart Claude Code after Haven approval so it can load the Haven tools."
|
|
751
|
+
]
|
|
646
752
|
};
|
|
647
753
|
} catch (err) {
|
|
648
754
|
return {
|
|
@@ -651,7 +757,10 @@ async function configureClaudeCode(input, deps) {
|
|
|
651
757
|
target: "Claude Code MCP config",
|
|
652
758
|
changed: false,
|
|
653
759
|
restartRequired: true,
|
|
654
|
-
messages: [
|
|
760
|
+
messages: [
|
|
761
|
+
`Could not update Claude Code MCP config: ${err instanceof Error ? err.message : String(err)}`,
|
|
762
|
+
"Install Claude Code or rerun the Haven setup command inside a Claude Code terminal."
|
|
763
|
+
],
|
|
655
764
|
errorCode: "claude_code_config_failed"
|
|
656
765
|
};
|
|
657
766
|
}
|
|
@@ -664,16 +773,38 @@ function buildProbeResult(hostedConfigured, hostedStatus, signerReady) {
|
|
|
664
773
|
const signerPart = signerReady ? "local_signer_ready" : "local_signer_unavailable";
|
|
665
774
|
return `${hostedPart}_${signerPart}`.slice(0, 120);
|
|
666
775
|
}
|
|
667
|
-
function
|
|
776
|
+
async function resolveSignerConsent(input, messages) {
|
|
777
|
+
if (input.ackSigner) {
|
|
778
|
+
const status = await acknowledgeLocalSignerConsent(input.signerPath, (message) => messages.push(message));
|
|
779
|
+
if (status.acknowledged) {
|
|
780
|
+
messages.push("Prepared the local Haven signer acknowledgement.");
|
|
781
|
+
} else {
|
|
782
|
+
messages.push("Local Haven signer acknowledgement still needs attention.");
|
|
783
|
+
}
|
|
784
|
+
return status;
|
|
785
|
+
}
|
|
786
|
+
return getLocalSignerConsentStatus(input.signerPath);
|
|
787
|
+
}
|
|
788
|
+
function signerConsentErrorCode(signerCredentialReady, signerConsent) {
|
|
789
|
+
if (!signerCredentialReady) return "local_signer_credential_unavailable";
|
|
790
|
+
if (!signerConsent.acknowledged) return "local_signer_ack_required";
|
|
791
|
+
return void 0;
|
|
792
|
+
}
|
|
793
|
+
function nextAction(runtime, restartMode, errorCode) {
|
|
668
794
|
if (errorCode) return "return_to_haven_for_wallet_approval_then_finish_runtime_setup";
|
|
669
795
|
if (restartMode === "hot-reload") return "return_to_haven_for_wallet_approval";
|
|
796
|
+
if (runtime === "codex-cli") return "return_to_haven_for_wallet_approval_then_restart_codex_with_haven_env";
|
|
797
|
+
if (runtime === "claude-code") return "return_to_haven_for_wallet_approval_then_restart_claude_code";
|
|
670
798
|
if (restartMode === "restart-app") return "return_to_haven_for_wallet_approval_then_restart_app";
|
|
671
799
|
if (restartMode === "restart-session") return "return_to_haven_for_wallet_approval_then_restart_agent_session";
|
|
672
800
|
return "return_to_haven_for_wallet_approval_then_configure_runtime";
|
|
673
801
|
}
|
|
802
|
+
function signerPackageName2() {
|
|
803
|
+
return `@haven_ai/signer@${signer.SIGNER_VERSION}`;
|
|
804
|
+
}
|
|
674
805
|
|
|
675
806
|
// src/runtime.ts
|
|
676
|
-
var CONNECTOR_VERSION = "0.1.
|
|
807
|
+
var CONNECTOR_VERSION = "0.1.1-alpha";
|
|
677
808
|
async function runConnect(options, deps = {}) {
|
|
678
809
|
const connectorVersion = options.connectorVersion ?? CONNECTOR_VERSION;
|
|
679
810
|
const api = deps.api ?? createConnectApiClient(options.apiBaseUrl);
|
|
@@ -735,7 +866,8 @@ async function runConnect(options, deps = {}) {
|
|
|
735
866
|
signerPath: credentialPaths.signerPath,
|
|
736
867
|
identityPath: credentialPaths.identityPath,
|
|
737
868
|
credentialDirectory: credentialPaths.directory,
|
|
738
|
-
environmentLabel: options.environmentLabel ?? "Local workspace"
|
|
869
|
+
environmentLabel: options.environmentLabel ?? "Local workspace",
|
|
870
|
+
ackSigner: options.ackSigner
|
|
739
871
|
});
|
|
740
872
|
printRuntimeInstall(runtimeInstall, log);
|
|
741
873
|
try {
|
|
@@ -745,6 +877,8 @@ async function runConnect(options, deps = {}) {
|
|
|
745
877
|
hostedMcpConfigured: runtimeInstall.hostedMcpConfigured,
|
|
746
878
|
localSignerConfigured: runtimeInstall.localSignerConfigured,
|
|
747
879
|
credentialFilesWritten: true,
|
|
880
|
+
signerAcknowledged: runtimeInstall.signerAcknowledged,
|
|
881
|
+
activationCommandAvailable: Boolean(runtimeInstall.activationCommand),
|
|
748
882
|
probeResult: runtimeInstall.probeResult,
|
|
749
883
|
restartRequired: runtimeInstall.restartRequired,
|
|
750
884
|
nextUserAction: runtimeInstall.nextUserAction,
|
|
@@ -815,6 +949,8 @@ function parseArgs(argv, env = process.env) {
|
|
|
815
949
|
options.credentialsDir = requireValue(argv, ++i, arg);
|
|
816
950
|
} else if (arg === "--environment-label") {
|
|
817
951
|
options.environmentLabel = requireValue(argv, ++i, arg);
|
|
952
|
+
} else if (arg === "--ack-signer") {
|
|
953
|
+
options.ackSigner = true;
|
|
818
954
|
} else if (arg === "--version") {
|
|
819
955
|
process.stdout.write(`${CONNECTOR_VERSION}
|
|
820
956
|
`);
|
|
@@ -843,7 +979,7 @@ function helpText() {
|
|
|
843
979
|
"sends Haven only the public signing address plus a proof signature.",
|
|
844
980
|
"",
|
|
845
981
|
"Usage:",
|
|
846
|
-
" npx -y @haven_ai/connect --setup hv_setup_... --api https://api.haven.example --runtime claude-code",
|
|
982
|
+
" npx -y @haven_ai/connect --setup hv_setup_... --api https://api.haven.example --ack-signer --runtime claude-code",
|
|
847
983
|
"",
|
|
848
984
|
"Options:",
|
|
849
985
|
" --setup <token> Short-lived setup token from Haven.",
|
|
@@ -851,6 +987,7 @@ function helpText() {
|
|
|
851
987
|
" --runtime <name> Agent runtime hint, such as claude-code, codex-cli, cursor, vscode, or claude-desktop.",
|
|
852
988
|
" --credentials-dir <path> Credential directory fallback. Defaults to ~/.haven/agents.",
|
|
853
989
|
" --environment-label <text> Non-sensitive label shown in Haven setup review.",
|
|
990
|
+
" --ack-signer Write the one-time local signer acknowledgement during setup.",
|
|
854
991
|
" --help Show this help.",
|
|
855
992
|
"",
|
|
856
993
|
"The connector never prints the private key and never sends it to Haven."
|