@haven_ai/connect 0.1.22-alpha.0 → 0.1.23-alpha.0
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 +47 -7
- package/dist/cli.cjs +226 -62
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +7 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +225 -62
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +232 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +62 -6
- package/dist/index.d.ts +62 -6
- package/dist/index.js +230 -55
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/cli.d.cts
CHANGED
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { pathToFileURL } from 'url';
|
|
2
3
|
import crypto from 'crypto';
|
|
3
4
|
import { Wallet } from 'ethers';
|
|
4
5
|
import { mkdir, rm, chmod, access, writeFile, readFile, unlink } from 'fs/promises';
|
|
@@ -175,11 +176,12 @@ async function writeCredentialFiles(input) {
|
|
|
175
176
|
agentPath,
|
|
176
177
|
{
|
|
177
178
|
agent_id: input.agentId,
|
|
179
|
+
delegate_address: input.delegateAddress,
|
|
178
180
|
safe_address: input.safeAddress,
|
|
179
181
|
chain_id: input.chainId,
|
|
180
182
|
network: input.network,
|
|
181
183
|
agent_budget: input.agentBudget,
|
|
182
|
-
note: "Non-secret orientation for the agent: identity + configured budget. Contains no API key or signing key. For the live remaining budget, call haven_get_allowances."
|
|
184
|
+
note: "Non-secret orientation for the agent: public delegate/Haven wallet identity + configured budget. Contains no API key or signing key. For the live remaining budget, call haven_get_allowances."
|
|
183
185
|
},
|
|
184
186
|
input.warn
|
|
185
187
|
);
|
|
@@ -231,9 +233,9 @@ var MCP_RUNTIME_MANIFEST = {
|
|
|
231
233
|
mcpPackage: "@haven_ai/mcp",
|
|
232
234
|
mcpVersion: MCP_VERSION,
|
|
233
235
|
sdkPackage: "@haven_ai/sdk",
|
|
234
|
-
sdkVersion: "0.1.
|
|
236
|
+
sdkVersion: "0.1.23-alpha.0",
|
|
235
237
|
signerPackage: "@haven_ai/signer",
|
|
236
|
-
signerVersion: "0.1.
|
|
238
|
+
signerVersion: "0.1.23-alpha.0",
|
|
237
239
|
// Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
|
|
238
240
|
// while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
|
|
239
241
|
// so the guard that was supposed to enforce the floor waved Node v23 through
|
|
@@ -1395,55 +1397,64 @@ var RUNTIME_PROFILES = {
|
|
|
1395
1397
|
id: "claude-code",
|
|
1396
1398
|
label: "Claude Code",
|
|
1397
1399
|
restartMode: "restart-session",
|
|
1398
|
-
canWriteRuntimeConfig: true
|
|
1400
|
+
canWriteRuntimeConfig: true,
|
|
1401
|
+
activationInstruction: "Start a new Claude Code session so it loads the Haven MCP entries."
|
|
1399
1402
|
},
|
|
1400
1403
|
"codex-cli": {
|
|
1401
1404
|
id: "codex-cli",
|
|
1402
1405
|
label: "Codex CLI",
|
|
1403
1406
|
restartMode: "restart-session",
|
|
1404
|
-
canWriteRuntimeConfig: true
|
|
1407
|
+
canWriteRuntimeConfig: true,
|
|
1408
|
+
activationInstruction: "Start a fresh Codex CLI session (for example, run `codex resume --last`)."
|
|
1405
1409
|
},
|
|
1406
1410
|
"codex-desktop": {
|
|
1407
1411
|
id: "codex-desktop",
|
|
1408
1412
|
label: "Codex Desktop",
|
|
1409
1413
|
restartMode: "restart-session",
|
|
1410
|
-
canWriteRuntimeConfig: true
|
|
1414
|
+
canWriteRuntimeConfig: true,
|
|
1415
|
+
activationInstruction: "Quit and reopen Codex Desktop so it loads the Haven MCP entries."
|
|
1411
1416
|
},
|
|
1412
1417
|
cursor: {
|
|
1413
1418
|
id: "cursor",
|
|
1414
1419
|
label: "Cursor",
|
|
1415
1420
|
restartMode: "hot-reload",
|
|
1416
|
-
canWriteRuntimeConfig: true
|
|
1421
|
+
canWriteRuntimeConfig: true,
|
|
1422
|
+
activationInstruction: "Wait for Cursor to hot-reload the Haven MCP entries; no app restart is required."
|
|
1417
1423
|
},
|
|
1418
1424
|
vscode: {
|
|
1419
1425
|
id: "vscode",
|
|
1420
1426
|
label: "VS Code",
|
|
1421
1427
|
restartMode: "hot-reload",
|
|
1422
|
-
canWriteRuntimeConfig: true
|
|
1428
|
+
canWriteRuntimeConfig: true,
|
|
1429
|
+
activationInstruction: "Wait for VS Code to hot-reload the Haven MCP entries; no app restart is required."
|
|
1423
1430
|
},
|
|
1424
1431
|
"vscode-insiders": {
|
|
1425
1432
|
id: "vscode-insiders",
|
|
1426
1433
|
label: "VS Code Insiders",
|
|
1427
1434
|
restartMode: "hot-reload",
|
|
1428
|
-
canWriteRuntimeConfig: true
|
|
1435
|
+
canWriteRuntimeConfig: true,
|
|
1436
|
+
activationInstruction: "Wait for VS Code Insiders to hot-reload the Haven MCP entries; no app restart is required."
|
|
1429
1437
|
},
|
|
1430
1438
|
"claude-desktop": {
|
|
1431
1439
|
id: "claude-desktop",
|
|
1432
1440
|
label: "Claude Desktop",
|
|
1433
1441
|
restartMode: "restart-app",
|
|
1434
|
-
canWriteRuntimeConfig: true
|
|
1442
|
+
canWriteRuntimeConfig: true,
|
|
1443
|
+
activationInstruction: "Quit and reopen Claude Desktop so it loads the Haven MCP entries."
|
|
1435
1444
|
},
|
|
1436
1445
|
hermes: {
|
|
1437
1446
|
id: "hermes",
|
|
1438
1447
|
label: "Hermes Agent",
|
|
1439
1448
|
restartMode: "restart-session",
|
|
1440
|
-
canWriteRuntimeConfig: true
|
|
1449
|
+
canWriteRuntimeConfig: true,
|
|
1450
|
+
activationInstruction: "Start a new Hermes session; in Hermes Gateway, run `/restart` instead."
|
|
1441
1451
|
},
|
|
1442
1452
|
other: {
|
|
1443
1453
|
id: "other",
|
|
1444
1454
|
label: "Other agent runtime",
|
|
1445
1455
|
restartMode: "manual",
|
|
1446
|
-
canWriteRuntimeConfig: false
|
|
1456
|
+
canWriteRuntimeConfig: false,
|
|
1457
|
+
activationInstruction: "Finish the manual MCP setup shown above, then start a fresh session in that runtime."
|
|
1447
1458
|
}
|
|
1448
1459
|
};
|
|
1449
1460
|
var RUNTIME_ALIASES = {
|
|
@@ -1495,8 +1506,9 @@ function restartRequiredForRuntime(runtime, env = process.env) {
|
|
|
1495
1506
|
const mode = runtimeProfile(runtime, env).restartMode;
|
|
1496
1507
|
return mode === "restart-session" || mode === "restart-app";
|
|
1497
1508
|
}
|
|
1498
|
-
function
|
|
1499
|
-
|
|
1509
|
+
function runtimeVerificationInstruction(runtime) {
|
|
1510
|
+
const label = RUNTIME_PROFILES[runtime].label;
|
|
1511
|
+
return `In ${label}, run the read-only \`haven_get_agent\` and \`haven_get_allowances\` tools to confirm the Haven wallet and live budget. Do not sign, fund, or create a payment to verify setup.`;
|
|
1500
1512
|
}
|
|
1501
1513
|
function normalizeRuntimeName(runtime) {
|
|
1502
1514
|
const key = runtime?.trim().toLowerCase();
|
|
@@ -1682,11 +1694,12 @@ async function installRuntime(input, deps = {}) {
|
|
|
1682
1694
|
probeLocalSignerCredential(input.signerPath),
|
|
1683
1695
|
localProbePromise
|
|
1684
1696
|
]);
|
|
1685
|
-
const hostedOk = configResult.hostedConfigured && hostedProbe.status
|
|
1697
|
+
const hostedOk = configResult.hostedConfigured && hostedProbe.status === "ok";
|
|
1686
1698
|
const localMcpOk = configResult.runtimeMcpMode === "local_stdio" && configResult.localMcpConfigured && signerCredentialReady && Boolean(localMcpConsent?.acknowledged) && localMcpProbe?.status === "ok";
|
|
1687
1699
|
const signerOk = configResult.runtimeMcpMode === "local_stdio" ? localMcpOk : configResult.signerConfigured && signerCredentialReady && Boolean(signerConsent?.acknowledged);
|
|
1688
1700
|
const restartRequired = configResult.restartRequired || restartRequiredForRuntime(runtime, deps.env);
|
|
1689
|
-
const errorCode = configResult.errorCode ?? (configResult.runtimeMcpMode === "local_stdio" ? localMcpErrorCode(signerCredentialReady, localMcpConsent, localMcpProbe?.status) : signerConsentErrorCode(signerCredentialReady, signerConsent));
|
|
1701
|
+
const errorCode = configResult.errorCode ?? (configResult.runtimeMcpMode === "local_stdio" ? localMcpErrorCode(signerCredentialReady, localMcpConsent, localMcpProbe?.status) : hostedMcpErrorCode(configResult.hostedConfigured, hostedProbe.status) ?? signerConsentErrorCode(signerCredentialReady, signerConsent));
|
|
1702
|
+
const hostedProbeMessages = configResult.hostedConfigured && hostedProbe.status !== "ok" ? [`Hosted Haven MCP probe failed: ${hostedProbe.status}.`] : configResult.hostedConfigured ? ["Verified hosted Haven MCP tools with a read-only handshake."] : [];
|
|
1690
1703
|
const localProbeMessages = localMcpProbe && localMcpProbe.status !== "ok" ? [`Local Haven MCP handshake failed: ${localMcpProbe.status}.`] : localMcpProbe?.status === "ok" ? ["Verified local Haven MCP tools with a stdio handshake."] : [];
|
|
1691
1704
|
const skillInstall = runtime === "claude-code" && !configResult.errorCode ? await installClaudeSkill(deps.homeDir) : void 0;
|
|
1692
1705
|
return {
|
|
@@ -1705,7 +1718,7 @@ async function installRuntime(input, deps = {}) {
|
|
|
1705
1718
|
activationCommand: configResult.activationCommand,
|
|
1706
1719
|
skillInstalled: skillInstall?.installed,
|
|
1707
1720
|
signerRuntimePrepared,
|
|
1708
|
-
messages: [...consentMessages, ...localRuntimeInstall?.messages ?? [], ...configResult.messages, ...localProbeMessages, ...skillInstall?.messages ?? []]
|
|
1721
|
+
messages: [...consentMessages, ...localRuntimeInstall?.messages ?? [], ...configResult.messages, ...hostedProbeMessages, ...localProbeMessages, ...skillInstall?.messages ?? []]
|
|
1709
1722
|
};
|
|
1710
1723
|
}
|
|
1711
1724
|
function runtimeInstallCapabilities(runtime, env = process.env) {
|
|
@@ -1866,6 +1879,10 @@ function signerConsentErrorCode(signerCredentialReady, signerConsent) {
|
|
|
1866
1879
|
if (!signerConsent?.acknowledged) return "local_signer_ack_required";
|
|
1867
1880
|
return void 0;
|
|
1868
1881
|
}
|
|
1882
|
+
function hostedMcpErrorCode(hostedConfigured, hostedProbeStatus) {
|
|
1883
|
+
if (!hostedConfigured || hostedProbeStatus === "ok") return void 0;
|
|
1884
|
+
return `hosted_mcp_probe_${hostedProbeStatus}`;
|
|
1885
|
+
}
|
|
1869
1886
|
function localMcpErrorCode(signerCredentialReady, localMcpConsent, localMcpProbeStatus) {
|
|
1870
1887
|
if (!signerCredentialReady) return "local_signer_credential_unavailable";
|
|
1871
1888
|
if (!localMcpConsent?.acknowledged) return "local_mcp_ack_required";
|
|
@@ -1917,13 +1934,17 @@ function localRuntimePrepareErrorCode(err) {
|
|
|
1917
1934
|
}
|
|
1918
1935
|
|
|
1919
1936
|
// src/runtime.ts
|
|
1920
|
-
var CONNECTOR_VERSION = "0.1.
|
|
1937
|
+
var CONNECTOR_VERSION = "0.1.23-alpha.0";
|
|
1938
|
+
var CONNECT_OUTCOME_SCHEMA_VERSION = 1;
|
|
1921
1939
|
async function runConnect(options, deps = {}) {
|
|
1922
1940
|
assertSupportedNodeVersion(deps.nodeVersion, MCP_RUNTIME_MANIFEST.minimumNodeVersion);
|
|
1923
1941
|
const connectorVersion = options.connectorVersion ?? CONNECTOR_VERSION;
|
|
1924
1942
|
const api = deps.api ?? createConnectApiClient(options.apiBaseUrl);
|
|
1925
|
-
const log = secureLogger(
|
|
1926
|
-
|
|
1943
|
+
const log = secureLogger(
|
|
1944
|
+
deps.log ?? ((message) => process.stdout.write(`${message}
|
|
1945
|
+
`)),
|
|
1946
|
+
deps.redactPaths === true
|
|
1947
|
+
);
|
|
1927
1948
|
const writeCredentials = deps.writeCredentials ?? writeCredentialFiles;
|
|
1928
1949
|
const preflightStorage = deps.preflightStorage ?? preflightCredentialStorage;
|
|
1929
1950
|
const runRuntimeInstall = deps.installRuntime ?? installRuntime;
|
|
@@ -1944,6 +1965,7 @@ async function runConnect(options, deps = {}) {
|
|
|
1944
1965
|
connectorVersion,
|
|
1945
1966
|
runtime: options.runtime
|
|
1946
1967
|
});
|
|
1968
|
+
assertSetupChallengeIsUsable(setup.challenge.expires_at);
|
|
1947
1969
|
printSetupSummary(setup, log);
|
|
1948
1970
|
await preflightStorage({ baseDir: options.credentialsDir, warn: log });
|
|
1949
1971
|
log("Checked local credential storage \u2014 all clear.");
|
|
@@ -1952,21 +1974,31 @@ async function runConnect(options, deps = {}) {
|
|
|
1952
1974
|
log("Minting a fresh signing key and API key \u2014 both stay on this machine.");
|
|
1953
1975
|
const proofSignature = await localKey.signChallenge(setup.challenge.message);
|
|
1954
1976
|
log("Introducing your agent to Haven\u2026");
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1977
|
+
let registration;
|
|
1978
|
+
try {
|
|
1979
|
+
registration = await api.registerSetup({
|
|
1980
|
+
setupToken: options.setupToken,
|
|
1981
|
+
connectorVersion,
|
|
1982
|
+
runtime: options.runtime,
|
|
1983
|
+
challengeId: setup.challenge.id,
|
|
1984
|
+
delegateAddress: localKey.address,
|
|
1985
|
+
proofSignature,
|
|
1986
|
+
apiKeyHash: hashAgentApiKey(localApiKey),
|
|
1987
|
+
apiKeyPrefix: agentApiKeyPrefix(localApiKey),
|
|
1988
|
+
connectorContext: {
|
|
1989
|
+
environment_label: options.environmentLabel ?? "Local workspace",
|
|
1990
|
+
config_target: installCapabilities.canWriteRuntimeConfig ? "agent runtime MCP config" : "local credential files"
|
|
1991
|
+
},
|
|
1992
|
+
installCapabilities
|
|
1993
|
+
});
|
|
1994
|
+
} catch (err) {
|
|
1995
|
+
if (isExpiredSetupChallenge(err)) {
|
|
1996
|
+
throw new Error(
|
|
1997
|
+
"The Haven setup challenge expired while connecting. Return to Haven, start a fresh connection, and run its new Connect command. Do not reuse or paste credentials."
|
|
1998
|
+
);
|
|
1999
|
+
}
|
|
2000
|
+
throw err;
|
|
2001
|
+
}
|
|
1970
2002
|
log(`Registered signing address with Haven: ${shortAddress(registration.delegate_address)}.`);
|
|
1971
2003
|
log("Tucking your credentials away safely on disk\u2026");
|
|
1972
2004
|
const credentialPaths = await writeCredentials({
|
|
@@ -2043,7 +2075,69 @@ async function runConnect(options, deps = {}) {
|
|
|
2043
2075
|
setupId: registration.setup_id,
|
|
2044
2076
|
agentId: registration.agent_id,
|
|
2045
2077
|
delegateAddress: registration.delegate_address,
|
|
2046
|
-
credentialPaths
|
|
2078
|
+
credentialPaths,
|
|
2079
|
+
outcome: completionOutcome({
|
|
2080
|
+
runtimeInstall,
|
|
2081
|
+
delegateAddress: registration.delegate_address,
|
|
2082
|
+
setupChallengeExpiresAt: setup.challenge.expires_at,
|
|
2083
|
+
approvalRequired: registration.agent_status === "pending_approval"
|
|
2084
|
+
})
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2087
|
+
function completionOutcome(input) {
|
|
2088
|
+
const { runtimeInstall } = input;
|
|
2089
|
+
const manualSetup = runtimeInstall.errorCode === "manual_runtime_setup_required";
|
|
2090
|
+
const nextAction2 = runtimeInstall.nextUserAction;
|
|
2091
|
+
const outcome = {
|
|
2092
|
+
schema_version: CONNECT_OUTCOME_SCHEMA_VERSION,
|
|
2093
|
+
outcome: runtimeInstall.errorCode ? "action_required" : "complete",
|
|
2094
|
+
runtime: runtimeInstall.runtime,
|
|
2095
|
+
topology: runtimeInstall.runtimeMcpMode,
|
|
2096
|
+
configuration: {
|
|
2097
|
+
hosted_mcp: runtimeInstall.hostedMcpConfigured,
|
|
2098
|
+
local_signer: runtimeInstall.localSignerConfigured,
|
|
2099
|
+
local_mcp: runtimeInstall.localMcpConfigured
|
|
2100
|
+
},
|
|
2101
|
+
probe: { result: runtimeInstall.probeResult },
|
|
2102
|
+
activation: {
|
|
2103
|
+
restart_required: runtimeInstall.restartRequired,
|
|
2104
|
+
instruction: manualSetup ? "Finish the manual MCP setup using the secret-free references shown in normal Connect output, then start a fresh session." : runtimeProfile(runtimeInstall.runtime).activationInstruction
|
|
2105
|
+
},
|
|
2106
|
+
next_action: nextAction2,
|
|
2107
|
+
approval: { required: input.approvalRequired, expires_at: null },
|
|
2108
|
+
verification: {
|
|
2109
|
+
tools: ["haven_get_agent", "haven_get_allowances"],
|
|
2110
|
+
instruction: runtimeVerificationInstruction(runtimeInstall.runtime)
|
|
2111
|
+
},
|
|
2112
|
+
// The backend contract supplies a 20-byte address. If an unexpected
|
|
2113
|
+
// malformed value arrives, do not echo it into an automation-facing
|
|
2114
|
+
// record; the human log has already been redacted separately.
|
|
2115
|
+
delegate_address: /^0x[0-9a-fA-F]{40}$/.test(input.delegateAddress) ? shortAddress(input.delegateAddress) : "[delegate-address-redacted]",
|
|
2116
|
+
...input.setupChallengeExpiresAt ? { setup_challenge_expires_at: input.setupChallengeExpiresAt } : {},
|
|
2117
|
+
...runtimeInstall.errorCode ? { error: { code: runtimeInstall.errorCode, next_action: nextAction2 } } : {}
|
|
2118
|
+
};
|
|
2119
|
+
return outcome;
|
|
2120
|
+
}
|
|
2121
|
+
function failedConnectOutcome(runtimeHint, error) {
|
|
2122
|
+
const message = error instanceof Error ? error.message : "";
|
|
2123
|
+
const code = /Node\.js >=/i.test(message) ? "unsupported_node_version" : /setup challenge.*expired|expired or invalid/i.test(message) ? "setup_challenge_expired_or_invalid" : /only available for Claude Code and Codex/i.test(message) ? "local_mcp_unsupported_runtime" : "connect_failed";
|
|
2124
|
+
const runtime = normalizeRuntime(runtimeHint);
|
|
2125
|
+
const nextAction2 = code === "setup_challenge_expired_or_invalid" ? "return_to_haven_for_fresh_setup" : code === "unsupported_node_version" ? "install_supported_node_and_rerun_connect" : code === "local_mcp_unsupported_runtime" ? "rerun_without_local_mcp" : "review_the_safe_error_output_and_start_a_fresh_haven_setup_if_needed";
|
|
2126
|
+
return {
|
|
2127
|
+
schema_version: CONNECT_OUTCOME_SCHEMA_VERSION,
|
|
2128
|
+
outcome: "failed",
|
|
2129
|
+
runtime,
|
|
2130
|
+
topology: "unknown",
|
|
2131
|
+
configuration: { hosted_mcp: false, local_signer: false, local_mcp: false },
|
|
2132
|
+
probe: { result: "not_run" },
|
|
2133
|
+
activation: { restart_required: false, instruction: "Resolve the reported problem before activating Haven tools." },
|
|
2134
|
+
next_action: nextAction2,
|
|
2135
|
+
approval: { required: false, expires_at: null },
|
|
2136
|
+
verification: {
|
|
2137
|
+
tools: ["haven_get_agent", "haven_get_allowances"],
|
|
2138
|
+
instruction: "After a successful setup and activation, verify only with haven_get_agent and haven_get_allowances."
|
|
2139
|
+
},
|
|
2140
|
+
error: { code, next_action: nextAction2 }
|
|
2047
2141
|
};
|
|
2048
2142
|
}
|
|
2049
2143
|
function printSetupSummary(setup, log) {
|
|
@@ -2056,10 +2150,26 @@ function printSetupSummary(setup, log) {
|
|
|
2056
2150
|
);
|
|
2057
2151
|
}
|
|
2058
2152
|
}
|
|
2059
|
-
log(`Setup challenge expires at ${setup.challenge.expires_at}.`);
|
|
2153
|
+
log(`Setup challenge expires at ${setup.challenge.expires_at}. If it expires, return to Haven for a fresh setup and rerun Connect \u2014 do not reuse or paste credentials.`);
|
|
2060
2154
|
}
|
|
2061
|
-
function
|
|
2062
|
-
|
|
2155
|
+
function assertSetupChallengeIsUsable(expiresAt) {
|
|
2156
|
+
const expiresAtMs = Date.parse(expiresAt);
|
|
2157
|
+
if (!Number.isNaN(expiresAtMs) && expiresAtMs > Date.now()) return;
|
|
2158
|
+
throw new Error(
|
|
2159
|
+
"This Haven setup challenge is expired or invalid. Return to Haven, start a fresh connection, and rerun Connect. No local credentials were written."
|
|
2160
|
+
);
|
|
2161
|
+
}
|
|
2162
|
+
function isExpiredSetupChallenge(err) {
|
|
2163
|
+
return err instanceof Error && /(?:setup )?challenge.*expir|expir.*(?:setup )?challenge/i.test(err.message);
|
|
2164
|
+
}
|
|
2165
|
+
function secureLogger(log, redactPaths = false) {
|
|
2166
|
+
return (message) => {
|
|
2167
|
+
let safe = redactSecrets(message);
|
|
2168
|
+
if (redactPaths) {
|
|
2169
|
+
safe = safe.replace(/(?:~|\/)[^\s`"']*\/(?:identity|signer|agent)\.json\b/g, "[credential-file-redacted]").replace(/(?:~|\/)[^\s`"']*\/\.env\b/g, "[credential-env-redacted]");
|
|
2170
|
+
}
|
|
2171
|
+
log(safe);
|
|
2172
|
+
};
|
|
2063
2173
|
}
|
|
2064
2174
|
function printRuntimeInstall(result, log) {
|
|
2065
2175
|
for (const message of result.messages) log(message);
|
|
@@ -2076,17 +2186,30 @@ function printRuntimeInstall(result, log) {
|
|
|
2076
2186
|
log("Local Haven signer still needs runtime setup.");
|
|
2077
2187
|
}
|
|
2078
2188
|
}
|
|
2079
|
-
function
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
}
|
|
2088
|
-
|
|
2189
|
+
function completionHandoffLines(result) {
|
|
2190
|
+
if (result.errorCode === "manual_runtime_setup_required") {
|
|
2191
|
+
return [
|
|
2192
|
+
"Next steps:",
|
|
2193
|
+
"1. Return to Haven and approve the agent rules. Approval \u2014 not restarting \u2014 unlocks Haven tools.",
|
|
2194
|
+
"2. Finish the manual MCP setup using the secret-free file references printed above, then start a fresh session in your runtime.",
|
|
2195
|
+
`3. ${runtimeVerificationInstruction(result.runtime)}`
|
|
2196
|
+
];
|
|
2197
|
+
}
|
|
2198
|
+
if (result.errorCode) {
|
|
2199
|
+
return [
|
|
2200
|
+
"Recovery: runtime setup is not complete. Resolve the reported problem, then return to Haven for a fresh connection and run its new Connect command. Do not manually edit runtime config or paste credentials into prompts, logs, or config."
|
|
2201
|
+
];
|
|
2089
2202
|
}
|
|
2203
|
+
const profile = runtimeProfile(result.runtime);
|
|
2204
|
+
return [
|
|
2205
|
+
"Next steps:",
|
|
2206
|
+
"1. Return to Haven and approve the agent rules. Approval \u2014 not restarting \u2014 unlocks Haven tools.",
|
|
2207
|
+
`2. ${profile.activationInstruction}`,
|
|
2208
|
+
`3. ${runtimeVerificationInstruction(result.runtime)}`
|
|
2209
|
+
];
|
|
2210
|
+
}
|
|
2211
|
+
function printNextSteps(result, log) {
|
|
2212
|
+
for (const line of completionHandoffLines(result)) log(line);
|
|
2090
2213
|
}
|
|
2091
2214
|
|
|
2092
2215
|
// src/args.ts
|
|
@@ -2096,10 +2219,13 @@ function parseArgs(argv, env = process.env) {
|
|
|
2096
2219
|
connectorVersion: CONNECTOR_VERSION
|
|
2097
2220
|
};
|
|
2098
2221
|
let help = false;
|
|
2222
|
+
let json = false;
|
|
2099
2223
|
for (let i = 0; i < argv.length; i += 1) {
|
|
2100
2224
|
const arg = argv[i];
|
|
2101
2225
|
if (arg === "--help" || arg === "-h") {
|
|
2102
2226
|
help = true;
|
|
2227
|
+
} else if (arg === "--json") {
|
|
2228
|
+
json = true;
|
|
2103
2229
|
} else if (arg === "--setup" || arg === "--setup-token") {
|
|
2104
2230
|
options.setupToken = requireValue(argv, ++i, arg);
|
|
2105
2231
|
} else if (arg === "--api" || arg === "--api-url") {
|
|
@@ -2126,7 +2252,7 @@ function parseArgs(argv, env = process.env) {
|
|
|
2126
2252
|
}
|
|
2127
2253
|
}
|
|
2128
2254
|
if (help) {
|
|
2129
|
-
return { options, help };
|
|
2255
|
+
return { options, help, json };
|
|
2130
2256
|
}
|
|
2131
2257
|
if (!options.setupToken) {
|
|
2132
2258
|
throw new Error("Missing --setup <hv_setup_...> setup token.");
|
|
@@ -2135,7 +2261,7 @@ function parseArgs(argv, env = process.env) {
|
|
|
2135
2261
|
throw new Error("Missing --api <Haven API URL>.");
|
|
2136
2262
|
}
|
|
2137
2263
|
options.apiBaseUrl = options.apiBaseUrl.replace(/\/+$/, "");
|
|
2138
|
-
return { options, help };
|
|
2264
|
+
return { options, help, json };
|
|
2139
2265
|
}
|
|
2140
2266
|
function helpText() {
|
|
2141
2267
|
return [
|
|
@@ -2157,9 +2283,10 @@ function helpText() {
|
|
|
2157
2283
|
" --ack-signer Backward-compatible alias for --ack-local-tools.",
|
|
2158
2284
|
" --local Advanced: install the fully-local Haven MCP (no hosted dependency).",
|
|
2159
2285
|
" Only available for Claude Code and Codex. Default is hosted MCP + local signer.",
|
|
2286
|
+
" --json Emit one versioned, secret-free result object on stdout; progress stays on stderr.",
|
|
2160
2287
|
" --help Show this help.",
|
|
2161
2288
|
"",
|
|
2162
|
-
"The connector never prints the private key and never sends it to Haven."
|
|
2289
|
+
"The connector never prints the private key and never sends it to Haven. JSON output never includes credential contents or full credential paths."
|
|
2163
2290
|
].join("\n");
|
|
2164
2291
|
}
|
|
2165
2292
|
function requireValue(argv, index, option) {
|
|
@@ -2171,19 +2298,55 @@ function requireValue(argv, index, option) {
|
|
|
2171
2298
|
}
|
|
2172
2299
|
|
|
2173
2300
|
// src/cli.ts
|
|
2174
|
-
async function
|
|
2175
|
-
|
|
2301
|
+
async function runCli(argv, io = {
|
|
2302
|
+
stdout: (message) => process.stdout.write(message),
|
|
2303
|
+
stderr: (message) => process.stderr.write(message)
|
|
2304
|
+
}) {
|
|
2305
|
+
const wantsJson = argv.includes("--json");
|
|
2306
|
+
let parsed;
|
|
2307
|
+
try {
|
|
2308
|
+
parsed = parseArgs(argv);
|
|
2309
|
+
} catch (err) {
|
|
2310
|
+
if (wantsJson) {
|
|
2311
|
+
io.stdout(`${JSON.stringify(failedConnectOutcome(void 0, err))}
|
|
2312
|
+
`);
|
|
2313
|
+
} else {
|
|
2314
|
+
io.stderr(`${redactSecrets(err instanceof Error ? err.message : String(err))}
|
|
2315
|
+
`);
|
|
2316
|
+
}
|
|
2317
|
+
return 1;
|
|
2318
|
+
}
|
|
2176
2319
|
if (parsed.help) {
|
|
2177
|
-
|
|
2320
|
+
io.stdout(`${helpText()}
|
|
2178
2321
|
`);
|
|
2179
|
-
return;
|
|
2322
|
+
return 0;
|
|
2180
2323
|
}
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2324
|
+
try {
|
|
2325
|
+
const result = await runConnect(parsed.options, {
|
|
2326
|
+
log: (message) => (parsed.json ? io.stderr : io.stdout)(`${message}
|
|
2327
|
+
`),
|
|
2328
|
+
redactPaths: parsed.json
|
|
2329
|
+
});
|
|
2330
|
+
if (parsed.json) io.stdout(`${JSON.stringify(result.outcome)}
|
|
2331
|
+
`);
|
|
2332
|
+
return 0;
|
|
2333
|
+
} catch (err) {
|
|
2334
|
+
if (parsed.json) {
|
|
2335
|
+
io.stdout(`${JSON.stringify(failedConnectOutcome(parsed.options.runtime, err))}
|
|
2185
2336
|
`);
|
|
2186
|
-
|
|
2187
|
-
}
|
|
2337
|
+
} else {
|
|
2338
|
+
io.stderr(`${redactSecrets(err instanceof Error ? err.message : String(err))}
|
|
2339
|
+
`);
|
|
2340
|
+
}
|
|
2341
|
+
return 1;
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
async function main() {
|
|
2345
|
+
const exitCode = await runCli(process.argv.slice(2));
|
|
2346
|
+
if (exitCode !== 0) process.exitCode = exitCode;
|
|
2347
|
+
}
|
|
2348
|
+
if (process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url) void main();
|
|
2349
|
+
|
|
2350
|
+
export { runCli };
|
|
2188
2351
|
//# sourceMappingURL=cli.js.map
|
|
2189
2352
|
//# sourceMappingURL=cli.js.map
|