@haven_ai/connect 0.5.0-alpha.1 → 0.7.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 +12 -0
- package/dist/cli.cjs +64 -24
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +64 -24
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +64 -24
- 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 +64 -24
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -703,6 +703,18 @@ which.
|
|
|
703
703
|
runtime, rewrite the wrapper + sidecar, and re-write the runtime config from
|
|
704
704
|
the STORED credentials. It never touches keys and never needs a new token.
|
|
705
705
|
|
|
706
|
+
**Several agents on one machine (#3412).** A repair rewrites the runtime config
|
|
707
|
+
from ONE agent's stored credentials. With more than one agent credential
|
|
708
|
+
directory under `~/.haven/agents` — or when `--doctor` was itself given
|
|
709
|
+
`--credentials-dir` — every repair line the doctor prints names the agent it is
|
|
710
|
+
about with `--credentials-dir <dir>`, and a `--repair` without
|
|
711
|
+
`--credentials-dir` is refused — nothing is changed — with one exact command
|
|
712
|
+
per live agent (its agent id beside it); a retired directory is listed as
|
|
713
|
+
"retired — not a repair target" and never offered. Without that, "the newest directory" was a
|
|
714
|
+
guess, and it re-wired a different agent than the one the doctor described.
|
|
715
|
+
Every "update your signer" hint Haven emits names the flagless
|
|
716
|
+
`npx -y @haven_ai/connect@<channel> --doctor`, which then prints that line.
|
|
717
|
+
|
|
706
718
|
## Installing an unpublished signer / SDK / MCP build (`HAVEN_SIGNER_SPEC`, #2424)
|
|
707
719
|
|
|
708
720
|
Setup installs the connector's **pinned** siblings — `@haven_ai/signer@<pin>`
|
package/dist/cli.cjs
CHANGED
|
@@ -620,9 +620,9 @@ var init_runtime_manifest = __esm({
|
|
|
620
620
|
mcpPackage: "@haven_ai/mcp",
|
|
621
621
|
mcpVersion: mcp.MCP_VERSION,
|
|
622
622
|
sdkPackage: "@haven_ai/sdk",
|
|
623
|
-
sdkVersion: "0.
|
|
623
|
+
sdkVersion: "0.7.0-alpha.0",
|
|
624
624
|
signerPackage: "@haven_ai/signer",
|
|
625
|
-
signerVersion: "0.
|
|
625
|
+
signerVersion: "0.7.0-alpha.0",
|
|
626
626
|
// Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
|
|
627
627
|
// while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
|
|
628
628
|
// so the guard that was supposed to enforce the floor waved Node v23 through
|
|
@@ -4451,7 +4451,7 @@ var init_runtime = __esm({
|
|
|
4451
4451
|
init_tombstone();
|
|
4452
4452
|
init_local_mcp_runtime();
|
|
4453
4453
|
init_runtime_manifest();
|
|
4454
|
-
CONNECTOR_VERSION = "0.
|
|
4454
|
+
CONNECTOR_VERSION = "0.7.0-alpha.0";
|
|
4455
4455
|
CONNECTOR_CLIENT_IDENTITY = `@haven_ai/connect/${CONNECTOR_VERSION}`;
|
|
4456
4456
|
CONNECT_OUTCOME_SCHEMA_VERSION = 1;
|
|
4457
4457
|
failureOutcomesByError = /* @__PURE__ */ new WeakMap();
|
|
@@ -4934,9 +4934,23 @@ function rollUpLevel(checks) {
|
|
|
4934
4934
|
function runtimeFlagFor(runtime) {
|
|
4935
4935
|
return normalizeRuntimeName(runtime) ? ` --runtime ${runtime}` : "";
|
|
4936
4936
|
}
|
|
4937
|
-
function
|
|
4938
|
-
|
|
4939
|
-
|
|
4937
|
+
async function pathExists3(path) {
|
|
4938
|
+
try {
|
|
4939
|
+
await promises.stat(path);
|
|
4940
|
+
return true;
|
|
4941
|
+
} catch {
|
|
4942
|
+
return false;
|
|
4943
|
+
}
|
|
4944
|
+
}
|
|
4945
|
+
function credentialsDirFlagFor(credentialsDir) {
|
|
4946
|
+
if (!credentialsDir) return "";
|
|
4947
|
+
const quoted = /^[\w@%+=:,./~-]+$/.test(credentialsDir) ? credentialsDir : `'${credentialsDir.replace(/'/g, `'\\''`)}'`;
|
|
4948
|
+
return ` --credentials-dir ${quoted}`;
|
|
4949
|
+
}
|
|
4950
|
+
function repairCommandFor(runtime, suffix = "", credentialsDir) {
|
|
4951
|
+
const dirFlag = credentialsDirFlagFor(credentialsDir);
|
|
4952
|
+
if (normalizeRuntimeName(runtime)) return `Run: ${RERUN} --doctor --repair --runtime ${runtime}${dirFlag}${suffix}`;
|
|
4953
|
+
return `Run: ${RERUN} --doctor --repair --runtime <runtime>${dirFlag}${suffix.replace(/\.$/, "")} \u2014 --repair needs the runtime named; one of: ${RUNTIME_FLAG_VALUE_LIST.join(", ")}.`;
|
|
4940
4954
|
}
|
|
4941
4955
|
async function resolveDoctorRuntime(input, directory) {
|
|
4942
4956
|
const explicit = input.runtime.trim();
|
|
@@ -4955,7 +4969,7 @@ async function discoverCredentialDirectory(homeDir, explicit) {
|
|
|
4955
4969
|
try {
|
|
4956
4970
|
entries = await promises.readdir(root);
|
|
4957
4971
|
} catch {
|
|
4958
|
-
return explicit ? { directory: explicit, others: [], parkedOnly: /* @__PURE__ */ new Set() } : { others: [], parkedOnly: /* @__PURE__ */ new Set() };
|
|
4972
|
+
return explicit ? { directory: explicit, others: [], parkedOnly: /* @__PURE__ */ new Set(), identityDirectories: [explicit] } : { others: [], parkedOnly: /* @__PURE__ */ new Set(), identityDirectories: [] };
|
|
4959
4973
|
}
|
|
4960
4974
|
const candidates = [];
|
|
4961
4975
|
const tombstonedOnly = [];
|
|
@@ -4980,20 +4994,23 @@ async function discoverCredentialDirectory(homeDir, explicit) {
|
|
|
4980
4994
|
}
|
|
4981
4995
|
const parkedOnlySet = new Set(parkedOnly);
|
|
4982
4996
|
candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
4997
|
+
const identityDirectories = candidates.map((c) => c.directory);
|
|
4983
4998
|
if (explicit) {
|
|
4984
4999
|
return {
|
|
4985
5000
|
directory: explicit,
|
|
4986
5001
|
others: [...candidates.map((c) => c.directory), ...tombstonedOnly, ...parkedOnly].filter((d) => d !== explicit),
|
|
4987
|
-
parkedOnly: parkedOnlySet
|
|
5002
|
+
parkedOnly: parkedOnlySet,
|
|
5003
|
+
identityDirectories: [explicit, ...identityDirectories.filter((d) => d !== explicit)]
|
|
4988
5004
|
};
|
|
4989
5005
|
}
|
|
4990
5006
|
if (candidates.length === 0 && tombstonedOnly.length === 0 && parkedOnly.length === 0) {
|
|
4991
|
-
return { others: [], parkedOnly: parkedOnlySet };
|
|
5007
|
+
return { others: [], parkedOnly: parkedOnlySet, identityDirectories };
|
|
4992
5008
|
}
|
|
4993
5009
|
return {
|
|
4994
5010
|
directory: candidates[0]?.directory,
|
|
4995
5011
|
others: [...candidates.slice(1).map((c) => c.directory), ...tombstonedOnly, ...parkedOnly],
|
|
4996
|
-
parkedOnly: parkedOnlySet
|
|
5012
|
+
parkedOnly: parkedOnlySet,
|
|
5013
|
+
identityDirectories
|
|
4997
5014
|
};
|
|
4998
5015
|
}
|
|
4999
5016
|
function agentIsWired(configText, names, slug, identity, sidecar, isPrimary, bareOwnerExists) {
|
|
@@ -5052,7 +5069,7 @@ function rekeyPendingVerdict(status, hostedDelegateAddress, runtime, slug) {
|
|
|
5052
5069
|
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
|
|
5053
5070
|
};
|
|
5054
5071
|
}
|
|
5055
|
-
async function runtimeSpecOverrideCheck(directory, sidecar, env) {
|
|
5072
|
+
async function runtimeSpecOverrideCheck(directory, sidecar, env, repairDir) {
|
|
5056
5073
|
const facts = [];
|
|
5057
5074
|
const installed = sidecar?.runtime_spec_override;
|
|
5058
5075
|
if (installed) {
|
|
@@ -5081,7 +5098,7 @@ async function runtimeSpecOverrideCheck(directory, sidecar, env) {
|
|
|
5081
5098
|
label: "Runtime spec override",
|
|
5082
5099
|
level: "failed",
|
|
5083
5100
|
detail: `runtime spec overridden \u2014 not the pinned manifest (${MCP_RUNTIME_MANIFEST.signerPackage}@${MCP_RUNTIME_MANIFEST.signerVersion}, ${MCP_RUNTIME_MANIFEST.sdkPackage}@${MCP_RUNTIME_MANIFEST.sdkVersion}). ${facts.join(". ")}.`,
|
|
5084
|
-
repair: `Developer override (#2424). To return to the pinned manifest: unset ${variables}, then run ${RERUN} --doctor --repair --runtime <runtime
|
|
5101
|
+
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.`
|
|
5085
5102
|
});
|
|
5086
5103
|
}
|
|
5087
5104
|
async function readMcpSidecarOverride(directory) {
|
|
@@ -5140,7 +5157,7 @@ async function verdictsForAgent(entry, input, deps) {
|
|
|
5140
5157
|
label: "Signer runtime (preinstalled wrapper)",
|
|
5141
5158
|
level: "failed",
|
|
5142
5159
|
detail: "No signer-runtime.json sidecar \u2014 the pinned signer runtime was never prepared (or a pre-#1586 npx config).",
|
|
5143
|
-
repair: repairCommandFor(input.runtime)
|
|
5160
|
+
repair: repairCommandFor(input.runtime, "", input.repairDir)
|
|
5144
5161
|
});
|
|
5145
5162
|
} else if (sidecar.runtime_spec_override) {
|
|
5146
5163
|
const matches = await installedRuntimeMatchesVersions(sidecar.runtime_directory, sidecar.cli_path, {
|
|
@@ -5152,7 +5169,7 @@ async function verdictsForAgent(entry, input, deps) {
|
|
|
5152
5169
|
label: "Signer runtime (preinstalled wrapper)",
|
|
5153
5170
|
level: matches ? "ok" : "failed",
|
|
5154
5171
|
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.`,
|
|
5155
|
-
...matches ? {} : { repair: repairCommandFor(input.runtime, " with the same HAVEN_*_SPEC variables set.") }
|
|
5172
|
+
...matches ? {} : { repair: repairCommandFor(input.runtime, " with the same HAVEN_*_SPEC variables set.", input.repairDir) }
|
|
5156
5173
|
});
|
|
5157
5174
|
} else {
|
|
5158
5175
|
const intact = await installedRuntimeMatchesVersions(sidecar.runtime_directory, sidecar.cli_path, {
|
|
@@ -5166,10 +5183,10 @@ async function verdictsForAgent(entry, input, deps) {
|
|
|
5166
5183
|
label: "Signer runtime (preinstalled wrapper)",
|
|
5167
5184
|
level: ok ? "ok" : intact ? "advisory" : "failed",
|
|
5168
5185
|
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 ${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.`,
|
|
5169
|
-
...ok ? {} : { repair: repairCommandFor(input.runtime) }
|
|
5186
|
+
...ok ? {} : { repair: repairCommandFor(input.runtime, "", input.repairDir) }
|
|
5170
5187
|
});
|
|
5171
5188
|
}
|
|
5172
|
-
const overrideCheck = await runtimeSpecOverrideCheck(directory, sidecar, deps.env ?? process.env);
|
|
5189
|
+
const overrideCheck = await runtimeSpecOverrideCheck(directory, sidecar, deps.env ?? process.env, input.repairDir);
|
|
5173
5190
|
if (overrideCheck) checks.push(overrideCheck);
|
|
5174
5191
|
const hostedUrl = identity?.hosted_mcp_url ?? (identity?.api_url ? `${identity.api_url}/mcp` : void 0);
|
|
5175
5192
|
if (identity?.api_key && hostedUrl) {
|
|
@@ -5259,7 +5276,7 @@ async function verdictsForAgent(entry, input, deps) {
|
|
|
5259
5276
|
label: "Signer stdio handshake",
|
|
5260
5277
|
level: probe.status === "ok" ? "ok" : "failed",
|
|
5261
5278
|
detail: probe.status === "ok" ? `Signer started, listed ${probe.toolNames?.length ?? 0} tools${probe.serverInfo?.version ? ` (v${probe.serverInfo.version})` : ""}.${compatDetail}` : `Handshake failed: ${probe.status}.`,
|
|
5262
|
-
...probe.status === "ok" ? {} : { repair: repairCommandFor(input.runtime) }
|
|
5279
|
+
...probe.status === "ok" ? {} : { repair: repairCommandFor(input.runtime, "", input.repairDir) }
|
|
5263
5280
|
});
|
|
5264
5281
|
}
|
|
5265
5282
|
} else {
|
|
@@ -5268,7 +5285,7 @@ async function verdictsForAgent(entry, input, deps) {
|
|
|
5268
5285
|
label: "Signer stdio handshake",
|
|
5269
5286
|
level: "failed",
|
|
5270
5287
|
detail: "Skipped \u2014 no prepared signer runtime to probe.",
|
|
5271
|
-
repair: repairCommandFor(input.runtime)
|
|
5288
|
+
repair: repairCommandFor(input.runtime, "", input.repairDir)
|
|
5272
5289
|
});
|
|
5273
5290
|
}
|
|
5274
5291
|
return { checks, ...signerCapabilities ? { signerCapabilities } : {} };
|
|
@@ -5277,7 +5294,8 @@ async function runDoctor(input, deps = {}) {
|
|
|
5277
5294
|
const homeDir = deps.homeDir ?? os.homedir();
|
|
5278
5295
|
const checks = [];
|
|
5279
5296
|
let signerCapabilities;
|
|
5280
|
-
const { directory, others, parkedOnly } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
|
|
5297
|
+
const { directory, others, parkedOnly, identityDirectories } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
|
|
5298
|
+
const nameRepairDir = identityDirectories.length > 1 || Boolean(input.credentialsDir);
|
|
5281
5299
|
const resolution = await resolveDoctorRuntime(input, directory);
|
|
5282
5300
|
const runtime = resolution.runtime;
|
|
5283
5301
|
const input2 = { ...input, runtime };
|
|
@@ -5334,7 +5352,11 @@ async function runDoctor(input, deps = {}) {
|
|
|
5334
5352
|
...rekeyPending ? { rekeyPending } : {}
|
|
5335
5353
|
};
|
|
5336
5354
|
if (wired) {
|
|
5337
|
-
const result = await checksForAgent(
|
|
5355
|
+
const result = await checksForAgent(
|
|
5356
|
+
{ directory: dir, identity, sidecar },
|
|
5357
|
+
{ ...input2, ...nameRepairDir ? { repairDir: dir } : {} },
|
|
5358
|
+
deps
|
|
5359
|
+
);
|
|
5338
5360
|
entry.checks = result.checks;
|
|
5339
5361
|
capabilitiesByDirectory.set(dir, result.signerCapabilities);
|
|
5340
5362
|
} else if (rekeyPending) {
|
|
@@ -5363,7 +5385,7 @@ async function runDoctor(input, deps = {}) {
|
|
|
5363
5385
|
if (!primaryChecksById.has("credentials")) {
|
|
5364
5386
|
const result = await checksForAgent(
|
|
5365
5387
|
{ directory: primaryDirectory, identity: primaryIdentity, sidecar: primarySidecar },
|
|
5366
|
-
input2,
|
|
5388
|
+
{ ...input2, ...nameRepairDir ? { repairDir: primaryDirectory } : {} },
|
|
5367
5389
|
deps
|
|
5368
5390
|
);
|
|
5369
5391
|
signerCapabilities = result.signerCapabilities;
|
|
@@ -5404,7 +5426,7 @@ async function runDoctor(input, deps = {}) {
|
|
|
5404
5426
|
label: "Runtime MCP config",
|
|
5405
5427
|
level: "failed",
|
|
5406
5428
|
detail: `No runtime config at ${configPath}.`,
|
|
5407
|
-
repair: repairCommandFor(input2.runtime)
|
|
5429
|
+
repair: repairCommandFor(input2.runtime, "", nameRepairDir ? primaryDirectory : void 0)
|
|
5408
5430
|
});
|
|
5409
5431
|
} else {
|
|
5410
5432
|
const primaryIdentity = await readIdentity(primaryDirectory ?? "");
|
|
@@ -5418,7 +5440,7 @@ async function runDoctor(input, deps = {}) {
|
|
|
5418
5440
|
label: "Runtime MCP config",
|
|
5419
5441
|
level: ok ? "ok" : "failed",
|
|
5420
5442
|
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)" : ""}.`,
|
|
5421
|
-
...ok ? {} : { repair: repairCommandFor(input2.runtime) }
|
|
5443
|
+
...ok ? {} : { repair: repairCommandFor(input2.runtime, "", nameRepairDir ? primaryDirectory : void 0) }
|
|
5422
5444
|
});
|
|
5423
5445
|
}
|
|
5424
5446
|
for (const id of ["hosted_mcp", "identity_match", "rekey_pending"]) {
|
|
@@ -5561,7 +5583,25 @@ async function runDoctor(input, deps = {}) {
|
|
|
5561
5583
|
async function runRepair(input, deps = {}) {
|
|
5562
5584
|
const homeDir = deps.homeDir ?? os.homedir();
|
|
5563
5585
|
const messages = [];
|
|
5564
|
-
const { directory, others } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
|
|
5586
|
+
const { directory, others, identityDirectories } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
|
|
5587
|
+
if (!input.credentialsDir && identityDirectories.length > 1) {
|
|
5588
|
+
const lines = await Promise.all(
|
|
5589
|
+
identityDirectories.map(async (dir) => {
|
|
5590
|
+
const identity2 = await readIdentity(dir);
|
|
5591
|
+
const who = identity2?.agent_id ? `agent ${identity2.agent_id}` : "agent (no agent_id recorded)";
|
|
5592
|
+
const retired = !identity2?.api_key || await pathExists3(path.join(dir, TOMBSTONE_FILENAME));
|
|
5593
|
+
return retired ? ` \u2022 ${who} (${dir}): retired \u2014 not a repair target` : ` \u2022 ${who}: ${repairCommandFor(input.runtime, "", dir).replace(/^Run: /, "")}`;
|
|
5594
|
+
})
|
|
5595
|
+
);
|
|
5596
|
+
return {
|
|
5597
|
+
ok: false,
|
|
5598
|
+
messages: [
|
|
5599
|
+
`${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:`,
|
|
5600
|
+
...lines,
|
|
5601
|
+
"Not sure which agent is which? Compare the agent ids against the Haven agent page, or run --doctor first."
|
|
5602
|
+
]
|
|
5603
|
+
};
|
|
5604
|
+
}
|
|
5565
5605
|
if (others.length > 0) {
|
|
5566
5606
|
messages.push(`Note: ${others.length} other agent credential dir(s) exist \u2014 run --doctor for their status.`);
|
|
5567
5607
|
}
|