@haven_ai/connect 0.6.0-alpha.0 → 0.8.0-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 +14 -1
- package/dist/cli.cjs +70 -31
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +70 -31
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +70 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +70 -31
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -619,9 +619,9 @@ var init_runtime_manifest = __esm({
|
|
|
619
619
|
mcpPackage: "@haven_ai/mcp",
|
|
620
620
|
mcpVersion: mcp.MCP_VERSION,
|
|
621
621
|
sdkPackage: "@haven_ai/sdk",
|
|
622
|
-
sdkVersion: "0.
|
|
622
|
+
sdkVersion: "0.8.0-alpha.0",
|
|
623
623
|
signerPackage: "@haven_ai/signer",
|
|
624
|
-
signerVersion: "0.
|
|
624
|
+
signerVersion: "0.8.0-alpha.0",
|
|
625
625
|
// Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
|
|
626
626
|
// while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
|
|
627
627
|
// so the guard that was supposed to enforce the floor waved Node v23 through
|
|
@@ -4450,7 +4450,7 @@ var init_runtime = __esm({
|
|
|
4450
4450
|
init_tombstone();
|
|
4451
4451
|
init_local_mcp_runtime();
|
|
4452
4452
|
init_runtime_manifest();
|
|
4453
|
-
exports.CONNECTOR_VERSION = "0.
|
|
4453
|
+
exports.CONNECTOR_VERSION = "0.8.0-alpha.0";
|
|
4454
4454
|
CONNECTOR_CLIENT_IDENTITY = `@haven_ai/connect/${exports.CONNECTOR_VERSION}`;
|
|
4455
4455
|
exports.CONNECT_OUTCOME_SCHEMA_VERSION = 1;
|
|
4456
4456
|
failureOutcomesByError = /* @__PURE__ */ new WeakMap();
|
|
@@ -4933,9 +4933,23 @@ function rollUpLevel(checks) {
|
|
|
4933
4933
|
function runtimeFlagFor(runtime) {
|
|
4934
4934
|
return normalizeRuntimeName(runtime) ? ` --runtime ${runtime}` : "";
|
|
4935
4935
|
}
|
|
4936
|
-
function
|
|
4937
|
-
|
|
4938
|
-
|
|
4936
|
+
async function pathExists3(path) {
|
|
4937
|
+
try {
|
|
4938
|
+
await promises.stat(path);
|
|
4939
|
+
return true;
|
|
4940
|
+
} catch {
|
|
4941
|
+
return false;
|
|
4942
|
+
}
|
|
4943
|
+
}
|
|
4944
|
+
function credentialsDirFlagFor(credentialsDir) {
|
|
4945
|
+
if (!credentialsDir) return "";
|
|
4946
|
+
const quoted = /^[\w@%+=:,./~-]+$/.test(credentialsDir) ? credentialsDir : `'${credentialsDir.replace(/'/g, `'\\''`)}'`;
|
|
4947
|
+
return ` --credentials-dir ${quoted}`;
|
|
4948
|
+
}
|
|
4949
|
+
function repairCommandFor(runtime, suffix = "", credentialsDir) {
|
|
4950
|
+
const dirFlag = credentialsDirFlagFor(credentialsDir);
|
|
4951
|
+
if (normalizeRuntimeName(runtime)) return `Run: ${RERUN} --doctor --repair --runtime ${runtime}${dirFlag}${suffix}`;
|
|
4952
|
+
return `Run: ${RERUN} --doctor --repair --runtime <runtime>${dirFlag}${suffix.replace(/\.$/, "")} \u2014 --repair needs the runtime named; one of: ${RUNTIME_FLAG_VALUE_LIST.join(", ")}.`;
|
|
4939
4953
|
}
|
|
4940
4954
|
async function resolveDoctorRuntime(input, directory) {
|
|
4941
4955
|
const explicit = input.runtime.trim();
|
|
@@ -4954,45 +4968,47 @@ async function discoverCredentialDirectory(homeDir, explicit) {
|
|
|
4954
4968
|
try {
|
|
4955
4969
|
entries = await promises.readdir(root);
|
|
4956
4970
|
} catch {
|
|
4957
|
-
return explicit ? { directory: explicit, others: [], parkedOnly: /* @__PURE__ */ new Set() } : { others: [], parkedOnly: /* @__PURE__ */ new Set() };
|
|
4971
|
+
return explicit ? { directory: explicit, others: [], parkedOnly: /* @__PURE__ */ new Set(), identityDirectories: [explicit] } : { others: [], parkedOnly: /* @__PURE__ */ new Set(), identityDirectories: [] };
|
|
4958
4972
|
}
|
|
4959
4973
|
const candidates = [];
|
|
4960
4974
|
const tombstonedOnly = [];
|
|
4961
4975
|
const parkedOnly = [];
|
|
4962
4976
|
for (const entry of entries) {
|
|
4963
4977
|
const directory = path.join(root, entry);
|
|
4978
|
+
if (await pathExists3(path.join(directory, TOMBSTONE_FILENAME))) {
|
|
4979
|
+
tombstonedOnly.push(directory);
|
|
4980
|
+
continue;
|
|
4981
|
+
}
|
|
4964
4982
|
try {
|
|
4965
4983
|
const s = await promises.stat(path.join(directory, "identity.json"));
|
|
4966
4984
|
candidates.push({ directory, mtimeMs: s.mtimeMs });
|
|
4967
4985
|
} catch {
|
|
4968
4986
|
try {
|
|
4969
|
-
await promises.stat(path.join(directory,
|
|
4970
|
-
|
|
4987
|
+
await promises.stat(path.join(directory, REKEY_PENDING_FILENAME));
|
|
4988
|
+
parkedOnly.push(directory);
|
|
4971
4989
|
} catch {
|
|
4972
|
-
try {
|
|
4973
|
-
await promises.stat(path.join(directory, REKEY_PENDING_FILENAME));
|
|
4974
|
-
parkedOnly.push(directory);
|
|
4975
|
-
} catch {
|
|
4976
|
-
}
|
|
4977
4990
|
}
|
|
4978
4991
|
}
|
|
4979
4992
|
}
|
|
4980
4993
|
const parkedOnlySet = new Set(parkedOnly);
|
|
4981
4994
|
candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
4995
|
+
const identityDirectories = candidates.map((c) => c.directory);
|
|
4982
4996
|
if (explicit) {
|
|
4983
4997
|
return {
|
|
4984
4998
|
directory: explicit,
|
|
4985
4999
|
others: [...candidates.map((c) => c.directory), ...tombstonedOnly, ...parkedOnly].filter((d) => d !== explicit),
|
|
4986
|
-
parkedOnly: parkedOnlySet
|
|
5000
|
+
parkedOnly: parkedOnlySet,
|
|
5001
|
+
identityDirectories: [explicit, ...identityDirectories.filter((d) => d !== explicit)]
|
|
4987
5002
|
};
|
|
4988
5003
|
}
|
|
4989
5004
|
if (candidates.length === 0 && tombstonedOnly.length === 0 && parkedOnly.length === 0) {
|
|
4990
|
-
return { others: [], parkedOnly: parkedOnlySet };
|
|
5005
|
+
return { others: [], parkedOnly: parkedOnlySet, identityDirectories };
|
|
4991
5006
|
}
|
|
4992
5007
|
return {
|
|
4993
5008
|
directory: candidates[0]?.directory,
|
|
4994
5009
|
others: [...candidates.slice(1).map((c) => c.directory), ...tombstonedOnly, ...parkedOnly],
|
|
4995
|
-
parkedOnly: parkedOnlySet
|
|
5010
|
+
parkedOnly: parkedOnlySet,
|
|
5011
|
+
identityDirectories
|
|
4996
5012
|
};
|
|
4997
5013
|
}
|
|
4998
5014
|
function agentIsWired(configText, names, slug, identity, sidecar, isPrimary, bareOwnerExists) {
|
|
@@ -5051,7 +5067,7 @@ function rekeyPendingVerdict(status, hostedDelegateAddress, runtime, slug) {
|
|
|
5051
5067
|
detail: `A re-key started ${started} is still open (expires ${status.expiresAt ?? "unknown"}). Paste this address into "Replace signing key" on the Haven agent page: ${address}. Parked at ${status.path}. ` + wedgeNote
|
|
5052
5068
|
};
|
|
5053
5069
|
}
|
|
5054
|
-
async function runtimeSpecOverrideCheck(directory, sidecar, env) {
|
|
5070
|
+
async function runtimeSpecOverrideCheck(directory, sidecar, env, repairDir) {
|
|
5055
5071
|
const facts = [];
|
|
5056
5072
|
const installed = sidecar?.runtime_spec_override;
|
|
5057
5073
|
if (installed) {
|
|
@@ -5080,7 +5096,7 @@ async function runtimeSpecOverrideCheck(directory, sidecar, env) {
|
|
|
5080
5096
|
label: "Runtime spec override",
|
|
5081
5097
|
level: "failed",
|
|
5082
5098
|
detail: `runtime spec overridden \u2014 not the pinned manifest (${exports.MCP_RUNTIME_MANIFEST.signerPackage}@${exports.MCP_RUNTIME_MANIFEST.signerVersion}, ${exports.MCP_RUNTIME_MANIFEST.sdkPackage}@${exports.MCP_RUNTIME_MANIFEST.sdkVersion}). ${facts.join(". ")}.`,
|
|
5083
|
-
repair: `Developer override (#2424). To return to the pinned manifest: unset ${variables}, then run ${RERUN} --doctor --repair --runtime <runtime
|
|
5099
|
+
repair: `Developer override (#2424). To return to the pinned manifest: unset ${variables}, then run ${RERUN} --doctor --repair --runtime <runtime>${credentialsDirFlagFor(repairDir)}. If the override is intentional, this finding is the record of it.`
|
|
5084
5100
|
});
|
|
5085
5101
|
}
|
|
5086
5102
|
async function readMcpSidecarOverride(directory) {
|
|
@@ -5139,7 +5155,7 @@ async function verdictsForAgent(entry, input, deps) {
|
|
|
5139
5155
|
label: "Signer runtime (preinstalled wrapper)",
|
|
5140
5156
|
level: "failed",
|
|
5141
5157
|
detail: "No signer-runtime.json sidecar \u2014 the pinned signer runtime was never prepared (or a pre-#1586 npx config).",
|
|
5142
|
-
repair: repairCommandFor(input.runtime)
|
|
5158
|
+
repair: repairCommandFor(input.runtime, "", input.repairDir)
|
|
5143
5159
|
});
|
|
5144
5160
|
} else if (sidecar.runtime_spec_override) {
|
|
5145
5161
|
const matches = await installedRuntimeMatchesVersions(sidecar.runtime_directory, sidecar.cli_path, {
|
|
@@ -5151,7 +5167,7 @@ async function verdictsForAgent(entry, input, deps) {
|
|
|
5151
5167
|
label: "Signer runtime (preinstalled wrapper)",
|
|
5152
5168
|
level: matches ? "ok" : "failed",
|
|
5153
5169
|
detail: matches ? `Installed ${sidecar.signer_package}@${sidecar.signer_version} at ${sidecar.runtime_directory} (override install \u2014 see runtime_spec_override)` : `Override runtime directory is stale or empty (${sidecar.runtime_directory}) \u2014 the CLI or package versions are missing.`,
|
|
5154
|
-
...matches ? {} : { repair: repairCommandFor(input.runtime, " with the same HAVEN_*_SPEC variables set.") }
|
|
5170
|
+
...matches ? {} : { repair: repairCommandFor(input.runtime, " with the same HAVEN_*_SPEC variables set.", input.repairDir) }
|
|
5155
5171
|
});
|
|
5156
5172
|
} else {
|
|
5157
5173
|
const intact = await installedRuntimeMatchesVersions(sidecar.runtime_directory, sidecar.cli_path, {
|
|
@@ -5165,10 +5181,10 @@ async function verdictsForAgent(entry, input, deps) {
|
|
|
5165
5181
|
label: "Signer runtime (preinstalled wrapper)",
|
|
5166
5182
|
level: ok ? "ok" : intact ? "advisory" : "failed",
|
|
5167
5183
|
detail: ok ? `Installed ${sidecar.signer_package}@${sidecar.signer_version} at ${sidecar.runtime_directory}` : intact ? `Installed version ${sidecar.signer_version} does not match the connector's pinned ${exports.MCP_RUNTIME_MANIFEST.signerVersion} \u2014 intact, but outdated.` : `Runtime directory is stale or empty (${sidecar.runtime_directory}) \u2014 the CLI or package versions are missing.`,
|
|
5168
|
-
...ok ? {} : { repair: repairCommandFor(input.runtime) }
|
|
5184
|
+
...ok ? {} : { repair: repairCommandFor(input.runtime, "", input.repairDir) }
|
|
5169
5185
|
});
|
|
5170
5186
|
}
|
|
5171
|
-
const overrideCheck = await runtimeSpecOverrideCheck(directory, sidecar, deps.env ?? process.env);
|
|
5187
|
+
const overrideCheck = await runtimeSpecOverrideCheck(directory, sidecar, deps.env ?? process.env, input.repairDir);
|
|
5172
5188
|
if (overrideCheck) checks.push(overrideCheck);
|
|
5173
5189
|
const hostedUrl = identity?.hosted_mcp_url ?? (identity?.api_url ? `${identity.api_url}/mcp` : void 0);
|
|
5174
5190
|
if (identity?.api_key && hostedUrl) {
|
|
@@ -5258,7 +5274,7 @@ async function verdictsForAgent(entry, input, deps) {
|
|
|
5258
5274
|
label: "Signer stdio handshake",
|
|
5259
5275
|
level: probe.status === "ok" ? "ok" : "failed",
|
|
5260
5276
|
detail: probe.status === "ok" ? `Signer started, listed ${probe.toolNames?.length ?? 0} tools${probe.serverInfo?.version ? ` (v${probe.serverInfo.version})` : ""}.${compatDetail}` : `Handshake failed: ${probe.status}.`,
|
|
5261
|
-
...probe.status === "ok" ? {} : { repair: repairCommandFor(input.runtime) }
|
|
5277
|
+
...probe.status === "ok" ? {} : { repair: repairCommandFor(input.runtime, "", input.repairDir) }
|
|
5262
5278
|
});
|
|
5263
5279
|
}
|
|
5264
5280
|
} else {
|
|
@@ -5267,7 +5283,7 @@ async function verdictsForAgent(entry, input, deps) {
|
|
|
5267
5283
|
label: "Signer stdio handshake",
|
|
5268
5284
|
level: "failed",
|
|
5269
5285
|
detail: "Skipped \u2014 no prepared signer runtime to probe.",
|
|
5270
|
-
repair: repairCommandFor(input.runtime)
|
|
5286
|
+
repair: repairCommandFor(input.runtime, "", input.repairDir)
|
|
5271
5287
|
});
|
|
5272
5288
|
}
|
|
5273
5289
|
return { checks, ...signerCapabilities ? { signerCapabilities } : {} };
|
|
@@ -5276,7 +5292,8 @@ async function runDoctor(input, deps = {}) {
|
|
|
5276
5292
|
const homeDir = deps.homeDir ?? os.homedir();
|
|
5277
5293
|
const checks = [];
|
|
5278
5294
|
let signerCapabilities;
|
|
5279
|
-
const { directory, others, parkedOnly } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
|
|
5295
|
+
const { directory, others, parkedOnly, identityDirectories } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
|
|
5296
|
+
const nameRepairDir = identityDirectories.length > 1 || Boolean(input.credentialsDir);
|
|
5280
5297
|
const resolution = await resolveDoctorRuntime(input, directory);
|
|
5281
5298
|
const runtime = resolution.runtime;
|
|
5282
5299
|
const input2 = { ...input, runtime };
|
|
@@ -5333,7 +5350,11 @@ async function runDoctor(input, deps = {}) {
|
|
|
5333
5350
|
...rekeyPending ? { rekeyPending } : {}
|
|
5334
5351
|
};
|
|
5335
5352
|
if (wired) {
|
|
5336
|
-
const result = await checksForAgent(
|
|
5353
|
+
const result = await checksForAgent(
|
|
5354
|
+
{ directory: dir, identity, sidecar },
|
|
5355
|
+
{ ...input2, ...nameRepairDir ? { repairDir: dir } : {} },
|
|
5356
|
+
deps
|
|
5357
|
+
);
|
|
5337
5358
|
entry.checks = result.checks;
|
|
5338
5359
|
capabilitiesByDirectory.set(dir, result.signerCapabilities);
|
|
5339
5360
|
} else if (rekeyPending) {
|
|
@@ -5362,7 +5383,7 @@ async function runDoctor(input, deps = {}) {
|
|
|
5362
5383
|
if (!primaryChecksById.has("credentials")) {
|
|
5363
5384
|
const result = await checksForAgent(
|
|
5364
5385
|
{ directory: primaryDirectory, identity: primaryIdentity, sidecar: primarySidecar },
|
|
5365
|
-
input2,
|
|
5386
|
+
{ ...input2, ...nameRepairDir ? { repairDir: primaryDirectory } : {} },
|
|
5366
5387
|
deps
|
|
5367
5388
|
);
|
|
5368
5389
|
signerCapabilities = result.signerCapabilities;
|
|
@@ -5403,7 +5424,7 @@ async function runDoctor(input, deps = {}) {
|
|
|
5403
5424
|
label: "Runtime MCP config",
|
|
5404
5425
|
level: "failed",
|
|
5405
5426
|
detail: `No runtime config at ${configPath}.`,
|
|
5406
|
-
repair: repairCommandFor(input2.runtime)
|
|
5427
|
+
repair: repairCommandFor(input2.runtime, "", nameRepairDir ? primaryDirectory : void 0)
|
|
5407
5428
|
});
|
|
5408
5429
|
} else {
|
|
5409
5430
|
const primaryIdentity = await readIdentity(primaryDirectory ?? "");
|
|
@@ -5417,7 +5438,7 @@ async function runDoctor(input, deps = {}) {
|
|
|
5417
5438
|
label: "Runtime MCP config",
|
|
5418
5439
|
level: ok ? "ok" : "failed",
|
|
5419
5440
|
detail: ok ? `Config at ${configPath} references the hosted server and the prepared signer wrapper.` : signerViaNpx ? `Config at ${configPath} still launches the signer via npx \u2014 the pre-#1586 shape that cannot start under a 120s startup timeout.` : `Config at ${configPath} is missing the Haven entries${primarySidecar && !wrapperReferenced ? " (or references a different signer wrapper)" : ""}.`,
|
|
5420
|
-
...ok ? {} : { repair: repairCommandFor(input2.runtime) }
|
|
5441
|
+
...ok ? {} : { repair: repairCommandFor(input2.runtime, "", nameRepairDir ? primaryDirectory : void 0) }
|
|
5421
5442
|
});
|
|
5422
5443
|
}
|
|
5423
5444
|
for (const id of ["hosted_mcp", "identity_match", "rekey_pending"]) {
|
|
@@ -5560,7 +5581,25 @@ async function runDoctor(input, deps = {}) {
|
|
|
5560
5581
|
async function runRepair(input, deps = {}) {
|
|
5561
5582
|
const homeDir = deps.homeDir ?? os.homedir();
|
|
5562
5583
|
const messages = [];
|
|
5563
|
-
const { directory, others } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
|
|
5584
|
+
const { directory, others, identityDirectories } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
|
|
5585
|
+
if (!input.credentialsDir && identityDirectories.length > 1) {
|
|
5586
|
+
const lines = await Promise.all(
|
|
5587
|
+
identityDirectories.map(async (dir) => {
|
|
5588
|
+
const identity2 = await readIdentity(dir);
|
|
5589
|
+
const who = identity2?.agent_id ? `agent ${identity2.agent_id}` : "agent (no agent_id recorded)";
|
|
5590
|
+
const retired = !identity2?.api_key || await pathExists3(path.join(dir, TOMBSTONE_FILENAME));
|
|
5591
|
+
return retired ? ` \u2022 ${who} (${dir}): retired \u2014 not a repair target` : ` \u2022 ${who}: ${repairCommandFor(input.runtime, "", dir).replace(/^Run: /, "")}`;
|
|
5592
|
+
})
|
|
5593
|
+
);
|
|
5594
|
+
return {
|
|
5595
|
+
ok: false,
|
|
5596
|
+
messages: [
|
|
5597
|
+
`${identityDirectories.length} agent credential directories exist on this machine, and a repair rewrites the runtime config from ONE of them. Name the agent to repair with --credentials-dir \u2014 nothing was changed:`,
|
|
5598
|
+
...lines,
|
|
5599
|
+
"Not sure which agent is which? Compare the agent ids against the Haven agent page, or run --doctor first."
|
|
5600
|
+
]
|
|
5601
|
+
};
|
|
5602
|
+
}
|
|
5564
5603
|
if (others.length > 0) {
|
|
5565
5604
|
messages.push(`Note: ${others.length} other agent credential dir(s) exist \u2014 run --doctor for their status.`);
|
|
5566
5605
|
}
|