@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.d.cts
CHANGED
|
@@ -743,7 +743,7 @@ type WiringCollisionResolution = {
|
|
|
743
743
|
action: 'abort';
|
|
744
744
|
};
|
|
745
745
|
|
|
746
|
-
declare const CONNECTOR_VERSION = "0.
|
|
746
|
+
declare const CONNECTOR_VERSION = "0.8.0-alpha.0";
|
|
747
747
|
interface ConnectOptions {
|
|
748
748
|
setupToken: string;
|
|
749
749
|
apiBaseUrl: string;
|
|
@@ -1194,11 +1194,11 @@ declare function isConnectError(err: unknown): err is ConnectError;
|
|
|
1194
1194
|
|
|
1195
1195
|
declare const MCP_RUNTIME_MANIFEST: {
|
|
1196
1196
|
readonly mcpPackage: "@haven_ai/mcp";
|
|
1197
|
-
readonly mcpVersion: "0.
|
|
1197
|
+
readonly mcpVersion: "0.8.0-alpha.0";
|
|
1198
1198
|
readonly sdkPackage: "@haven_ai/sdk";
|
|
1199
|
-
readonly sdkVersion: "0.
|
|
1199
|
+
readonly sdkVersion: "0.8.0-alpha.0";
|
|
1200
1200
|
readonly signerPackage: "@haven_ai/signer";
|
|
1201
|
-
readonly signerVersion: "0.
|
|
1201
|
+
readonly signerVersion: "0.8.0-alpha.0";
|
|
1202
1202
|
readonly minimumNodeVersion: "22.0.0";
|
|
1203
1203
|
readonly supportedClients: readonly ["codex-cli", "codex-desktop", "claude-code"];
|
|
1204
1204
|
readonly requiredTools: readonly string[];
|
package/dist/index.d.ts
CHANGED
|
@@ -743,7 +743,7 @@ type WiringCollisionResolution = {
|
|
|
743
743
|
action: 'abort';
|
|
744
744
|
};
|
|
745
745
|
|
|
746
|
-
declare const CONNECTOR_VERSION = "0.
|
|
746
|
+
declare const CONNECTOR_VERSION = "0.8.0-alpha.0";
|
|
747
747
|
interface ConnectOptions {
|
|
748
748
|
setupToken: string;
|
|
749
749
|
apiBaseUrl: string;
|
|
@@ -1194,11 +1194,11 @@ declare function isConnectError(err: unknown): err is ConnectError;
|
|
|
1194
1194
|
|
|
1195
1195
|
declare const MCP_RUNTIME_MANIFEST: {
|
|
1196
1196
|
readonly mcpPackage: "@haven_ai/mcp";
|
|
1197
|
-
readonly mcpVersion: "0.
|
|
1197
|
+
readonly mcpVersion: "0.8.0-alpha.0";
|
|
1198
1198
|
readonly sdkPackage: "@haven_ai/sdk";
|
|
1199
|
-
readonly sdkVersion: "0.
|
|
1199
|
+
readonly sdkVersion: "0.8.0-alpha.0";
|
|
1200
1200
|
readonly signerPackage: "@haven_ai/signer";
|
|
1201
|
-
readonly signerVersion: "0.
|
|
1201
|
+
readonly signerVersion: "0.8.0-alpha.0";
|
|
1202
1202
|
readonly minimumNodeVersion: "22.0.0";
|
|
1203
1203
|
readonly supportedClients: readonly ["codex-cli", "codex-desktop", "claude-code"];
|
|
1204
1204
|
readonly requiredTools: readonly string[];
|
package/dist/index.js
CHANGED
|
@@ -612,9 +612,9 @@ var init_runtime_manifest = __esm({
|
|
|
612
612
|
mcpPackage: "@haven_ai/mcp",
|
|
613
613
|
mcpVersion: MCP_VERSION,
|
|
614
614
|
sdkPackage: "@haven_ai/sdk",
|
|
615
|
-
sdkVersion: "0.
|
|
615
|
+
sdkVersion: "0.8.0-alpha.0",
|
|
616
616
|
signerPackage: "@haven_ai/signer",
|
|
617
|
-
signerVersion: "0.
|
|
617
|
+
signerVersion: "0.8.0-alpha.0",
|
|
618
618
|
// Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
|
|
619
619
|
// while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
|
|
620
620
|
// so the guard that was supposed to enforce the floor waved Node v23 through
|
|
@@ -4443,7 +4443,7 @@ var init_runtime = __esm({
|
|
|
4443
4443
|
init_tombstone();
|
|
4444
4444
|
init_local_mcp_runtime();
|
|
4445
4445
|
init_runtime_manifest();
|
|
4446
|
-
CONNECTOR_VERSION = "0.
|
|
4446
|
+
CONNECTOR_VERSION = "0.8.0-alpha.0";
|
|
4447
4447
|
CONNECTOR_CLIENT_IDENTITY = `@haven_ai/connect/${CONNECTOR_VERSION}`;
|
|
4448
4448
|
CONNECT_OUTCOME_SCHEMA_VERSION = 1;
|
|
4449
4449
|
failureOutcomesByError = /* @__PURE__ */ new WeakMap();
|
|
@@ -4926,9 +4926,23 @@ function rollUpLevel(checks) {
|
|
|
4926
4926
|
function runtimeFlagFor(runtime) {
|
|
4927
4927
|
return normalizeRuntimeName(runtime) ? ` --runtime ${runtime}` : "";
|
|
4928
4928
|
}
|
|
4929
|
-
function
|
|
4930
|
-
|
|
4931
|
-
|
|
4929
|
+
async function pathExists3(path) {
|
|
4930
|
+
try {
|
|
4931
|
+
await stat(path);
|
|
4932
|
+
return true;
|
|
4933
|
+
} catch {
|
|
4934
|
+
return false;
|
|
4935
|
+
}
|
|
4936
|
+
}
|
|
4937
|
+
function credentialsDirFlagFor(credentialsDir) {
|
|
4938
|
+
if (!credentialsDir) return "";
|
|
4939
|
+
const quoted = /^[\w@%+=:,./~-]+$/.test(credentialsDir) ? credentialsDir : `'${credentialsDir.replace(/'/g, `'\\''`)}'`;
|
|
4940
|
+
return ` --credentials-dir ${quoted}`;
|
|
4941
|
+
}
|
|
4942
|
+
function repairCommandFor(runtime, suffix = "", credentialsDir) {
|
|
4943
|
+
const dirFlag = credentialsDirFlagFor(credentialsDir);
|
|
4944
|
+
if (normalizeRuntimeName(runtime)) return `Run: ${RERUN} --doctor --repair --runtime ${runtime}${dirFlag}${suffix}`;
|
|
4945
|
+
return `Run: ${RERUN} --doctor --repair --runtime <runtime>${dirFlag}${suffix.replace(/\.$/, "")} \u2014 --repair needs the runtime named; one of: ${RUNTIME_FLAG_VALUE_LIST.join(", ")}.`;
|
|
4932
4946
|
}
|
|
4933
4947
|
async function resolveDoctorRuntime(input, directory) {
|
|
4934
4948
|
const explicit = input.runtime.trim();
|
|
@@ -4947,45 +4961,47 @@ async function discoverCredentialDirectory(homeDir, explicit) {
|
|
|
4947
4961
|
try {
|
|
4948
4962
|
entries = await readdir(root);
|
|
4949
4963
|
} catch {
|
|
4950
|
-
return explicit ? { directory: explicit, others: [], parkedOnly: /* @__PURE__ */ new Set() } : { others: [], parkedOnly: /* @__PURE__ */ new Set() };
|
|
4964
|
+
return explicit ? { directory: explicit, others: [], parkedOnly: /* @__PURE__ */ new Set(), identityDirectories: [explicit] } : { others: [], parkedOnly: /* @__PURE__ */ new Set(), identityDirectories: [] };
|
|
4951
4965
|
}
|
|
4952
4966
|
const candidates = [];
|
|
4953
4967
|
const tombstonedOnly = [];
|
|
4954
4968
|
const parkedOnly = [];
|
|
4955
4969
|
for (const entry of entries) {
|
|
4956
4970
|
const directory = join(root, entry);
|
|
4971
|
+
if (await pathExists3(join(directory, TOMBSTONE_FILENAME))) {
|
|
4972
|
+
tombstonedOnly.push(directory);
|
|
4973
|
+
continue;
|
|
4974
|
+
}
|
|
4957
4975
|
try {
|
|
4958
4976
|
const s = await stat(join(directory, "identity.json"));
|
|
4959
4977
|
candidates.push({ directory, mtimeMs: s.mtimeMs });
|
|
4960
4978
|
} catch {
|
|
4961
4979
|
try {
|
|
4962
|
-
await stat(join(directory,
|
|
4963
|
-
|
|
4980
|
+
await stat(join(directory, REKEY_PENDING_FILENAME));
|
|
4981
|
+
parkedOnly.push(directory);
|
|
4964
4982
|
} catch {
|
|
4965
|
-
try {
|
|
4966
|
-
await stat(join(directory, REKEY_PENDING_FILENAME));
|
|
4967
|
-
parkedOnly.push(directory);
|
|
4968
|
-
} catch {
|
|
4969
|
-
}
|
|
4970
4983
|
}
|
|
4971
4984
|
}
|
|
4972
4985
|
}
|
|
4973
4986
|
const parkedOnlySet = new Set(parkedOnly);
|
|
4974
4987
|
candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
4988
|
+
const identityDirectories = candidates.map((c) => c.directory);
|
|
4975
4989
|
if (explicit) {
|
|
4976
4990
|
return {
|
|
4977
4991
|
directory: explicit,
|
|
4978
4992
|
others: [...candidates.map((c) => c.directory), ...tombstonedOnly, ...parkedOnly].filter((d) => d !== explicit),
|
|
4979
|
-
parkedOnly: parkedOnlySet
|
|
4993
|
+
parkedOnly: parkedOnlySet,
|
|
4994
|
+
identityDirectories: [explicit, ...identityDirectories.filter((d) => d !== explicit)]
|
|
4980
4995
|
};
|
|
4981
4996
|
}
|
|
4982
4997
|
if (candidates.length === 0 && tombstonedOnly.length === 0 && parkedOnly.length === 0) {
|
|
4983
|
-
return { others: [], parkedOnly: parkedOnlySet };
|
|
4998
|
+
return { others: [], parkedOnly: parkedOnlySet, identityDirectories };
|
|
4984
4999
|
}
|
|
4985
5000
|
return {
|
|
4986
5001
|
directory: candidates[0]?.directory,
|
|
4987
5002
|
others: [...candidates.slice(1).map((c) => c.directory), ...tombstonedOnly, ...parkedOnly],
|
|
4988
|
-
parkedOnly: parkedOnlySet
|
|
5003
|
+
parkedOnly: parkedOnlySet,
|
|
5004
|
+
identityDirectories
|
|
4989
5005
|
};
|
|
4990
5006
|
}
|
|
4991
5007
|
function agentIsWired(configText, names, slug, identity, sidecar, isPrimary, bareOwnerExists) {
|
|
@@ -5044,7 +5060,7 @@ function rekeyPendingVerdict(status, hostedDelegateAddress, runtime, slug) {
|
|
|
5044
5060
|
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
|
|
5045
5061
|
};
|
|
5046
5062
|
}
|
|
5047
|
-
async function runtimeSpecOverrideCheck(directory, sidecar, env) {
|
|
5063
|
+
async function runtimeSpecOverrideCheck(directory, sidecar, env, repairDir) {
|
|
5048
5064
|
const facts = [];
|
|
5049
5065
|
const installed = sidecar?.runtime_spec_override;
|
|
5050
5066
|
if (installed) {
|
|
@@ -5073,7 +5089,7 @@ async function runtimeSpecOverrideCheck(directory, sidecar, env) {
|
|
|
5073
5089
|
label: "Runtime spec override",
|
|
5074
5090
|
level: "failed",
|
|
5075
5091
|
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(". ")}.`,
|
|
5076
|
-
repair: `Developer override (#2424). To return to the pinned manifest: unset ${variables}, then run ${RERUN} --doctor --repair --runtime <runtime
|
|
5092
|
+
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.`
|
|
5077
5093
|
});
|
|
5078
5094
|
}
|
|
5079
5095
|
async function readMcpSidecarOverride(directory) {
|
|
@@ -5132,7 +5148,7 @@ async function verdictsForAgent(entry, input, deps) {
|
|
|
5132
5148
|
label: "Signer runtime (preinstalled wrapper)",
|
|
5133
5149
|
level: "failed",
|
|
5134
5150
|
detail: "No signer-runtime.json sidecar \u2014 the pinned signer runtime was never prepared (or a pre-#1586 npx config).",
|
|
5135
|
-
repair: repairCommandFor(input.runtime)
|
|
5151
|
+
repair: repairCommandFor(input.runtime, "", input.repairDir)
|
|
5136
5152
|
});
|
|
5137
5153
|
} else if (sidecar.runtime_spec_override) {
|
|
5138
5154
|
const matches = await installedRuntimeMatchesVersions(sidecar.runtime_directory, sidecar.cli_path, {
|
|
@@ -5144,7 +5160,7 @@ async function verdictsForAgent(entry, input, deps) {
|
|
|
5144
5160
|
label: "Signer runtime (preinstalled wrapper)",
|
|
5145
5161
|
level: matches ? "ok" : "failed",
|
|
5146
5162
|
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.`,
|
|
5147
|
-
...matches ? {} : { repair: repairCommandFor(input.runtime, " with the same HAVEN_*_SPEC variables set.") }
|
|
5163
|
+
...matches ? {} : { repair: repairCommandFor(input.runtime, " with the same HAVEN_*_SPEC variables set.", input.repairDir) }
|
|
5148
5164
|
});
|
|
5149
5165
|
} else {
|
|
5150
5166
|
const intact = await installedRuntimeMatchesVersions(sidecar.runtime_directory, sidecar.cli_path, {
|
|
@@ -5158,10 +5174,10 @@ async function verdictsForAgent(entry, input, deps) {
|
|
|
5158
5174
|
label: "Signer runtime (preinstalled wrapper)",
|
|
5159
5175
|
level: ok ? "ok" : intact ? "advisory" : "failed",
|
|
5160
5176
|
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.`,
|
|
5161
|
-
...ok ? {} : { repair: repairCommandFor(input.runtime) }
|
|
5177
|
+
...ok ? {} : { repair: repairCommandFor(input.runtime, "", input.repairDir) }
|
|
5162
5178
|
});
|
|
5163
5179
|
}
|
|
5164
|
-
const overrideCheck = await runtimeSpecOverrideCheck(directory, sidecar, deps.env ?? process.env);
|
|
5180
|
+
const overrideCheck = await runtimeSpecOverrideCheck(directory, sidecar, deps.env ?? process.env, input.repairDir);
|
|
5165
5181
|
if (overrideCheck) checks.push(overrideCheck);
|
|
5166
5182
|
const hostedUrl = identity?.hosted_mcp_url ?? (identity?.api_url ? `${identity.api_url}/mcp` : void 0);
|
|
5167
5183
|
if (identity?.api_key && hostedUrl) {
|
|
@@ -5251,7 +5267,7 @@ async function verdictsForAgent(entry, input, deps) {
|
|
|
5251
5267
|
label: "Signer stdio handshake",
|
|
5252
5268
|
level: probe.status === "ok" ? "ok" : "failed",
|
|
5253
5269
|
detail: probe.status === "ok" ? `Signer started, listed ${probe.toolNames?.length ?? 0} tools${probe.serverInfo?.version ? ` (v${probe.serverInfo.version})` : ""}.${compatDetail}` : `Handshake failed: ${probe.status}.`,
|
|
5254
|
-
...probe.status === "ok" ? {} : { repair: repairCommandFor(input.runtime) }
|
|
5270
|
+
...probe.status === "ok" ? {} : { repair: repairCommandFor(input.runtime, "", input.repairDir) }
|
|
5255
5271
|
});
|
|
5256
5272
|
}
|
|
5257
5273
|
} else {
|
|
@@ -5260,7 +5276,7 @@ async function verdictsForAgent(entry, input, deps) {
|
|
|
5260
5276
|
label: "Signer stdio handshake",
|
|
5261
5277
|
level: "failed",
|
|
5262
5278
|
detail: "Skipped \u2014 no prepared signer runtime to probe.",
|
|
5263
|
-
repair: repairCommandFor(input.runtime)
|
|
5279
|
+
repair: repairCommandFor(input.runtime, "", input.repairDir)
|
|
5264
5280
|
});
|
|
5265
5281
|
}
|
|
5266
5282
|
return { checks, ...signerCapabilities ? { signerCapabilities } : {} };
|
|
@@ -5269,7 +5285,8 @@ async function runDoctor(input, deps = {}) {
|
|
|
5269
5285
|
const homeDir = deps.homeDir ?? homedir();
|
|
5270
5286
|
const checks = [];
|
|
5271
5287
|
let signerCapabilities;
|
|
5272
|
-
const { directory, others, parkedOnly } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
|
|
5288
|
+
const { directory, others, parkedOnly, identityDirectories } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
|
|
5289
|
+
const nameRepairDir = identityDirectories.length > 1 || Boolean(input.credentialsDir);
|
|
5273
5290
|
const resolution = await resolveDoctorRuntime(input, directory);
|
|
5274
5291
|
const runtime = resolution.runtime;
|
|
5275
5292
|
const input2 = { ...input, runtime };
|
|
@@ -5326,7 +5343,11 @@ async function runDoctor(input, deps = {}) {
|
|
|
5326
5343
|
...rekeyPending ? { rekeyPending } : {}
|
|
5327
5344
|
};
|
|
5328
5345
|
if (wired) {
|
|
5329
|
-
const result = await checksForAgent(
|
|
5346
|
+
const result = await checksForAgent(
|
|
5347
|
+
{ directory: dir, identity, sidecar },
|
|
5348
|
+
{ ...input2, ...nameRepairDir ? { repairDir: dir } : {} },
|
|
5349
|
+
deps
|
|
5350
|
+
);
|
|
5330
5351
|
entry.checks = result.checks;
|
|
5331
5352
|
capabilitiesByDirectory.set(dir, result.signerCapabilities);
|
|
5332
5353
|
} else if (rekeyPending) {
|
|
@@ -5355,7 +5376,7 @@ async function runDoctor(input, deps = {}) {
|
|
|
5355
5376
|
if (!primaryChecksById.has("credentials")) {
|
|
5356
5377
|
const result = await checksForAgent(
|
|
5357
5378
|
{ directory: primaryDirectory, identity: primaryIdentity, sidecar: primarySidecar },
|
|
5358
|
-
input2,
|
|
5379
|
+
{ ...input2, ...nameRepairDir ? { repairDir: primaryDirectory } : {} },
|
|
5359
5380
|
deps
|
|
5360
5381
|
);
|
|
5361
5382
|
signerCapabilities = result.signerCapabilities;
|
|
@@ -5396,7 +5417,7 @@ async function runDoctor(input, deps = {}) {
|
|
|
5396
5417
|
label: "Runtime MCP config",
|
|
5397
5418
|
level: "failed",
|
|
5398
5419
|
detail: `No runtime config at ${configPath}.`,
|
|
5399
|
-
repair: repairCommandFor(input2.runtime)
|
|
5420
|
+
repair: repairCommandFor(input2.runtime, "", nameRepairDir ? primaryDirectory : void 0)
|
|
5400
5421
|
});
|
|
5401
5422
|
} else {
|
|
5402
5423
|
const primaryIdentity = await readIdentity(primaryDirectory ?? "");
|
|
@@ -5410,7 +5431,7 @@ async function runDoctor(input, deps = {}) {
|
|
|
5410
5431
|
label: "Runtime MCP config",
|
|
5411
5432
|
level: ok ? "ok" : "failed",
|
|
5412
5433
|
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)" : ""}.`,
|
|
5413
|
-
...ok ? {} : { repair: repairCommandFor(input2.runtime) }
|
|
5434
|
+
...ok ? {} : { repair: repairCommandFor(input2.runtime, "", nameRepairDir ? primaryDirectory : void 0) }
|
|
5414
5435
|
});
|
|
5415
5436
|
}
|
|
5416
5437
|
for (const id of ["hosted_mcp", "identity_match", "rekey_pending"]) {
|
|
@@ -5553,7 +5574,25 @@ async function runDoctor(input, deps = {}) {
|
|
|
5553
5574
|
async function runRepair(input, deps = {}) {
|
|
5554
5575
|
const homeDir = deps.homeDir ?? homedir();
|
|
5555
5576
|
const messages = [];
|
|
5556
|
-
const { directory, others } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
|
|
5577
|
+
const { directory, others, identityDirectories } = await discoverCredentialDirectory(homeDir, input.credentialsDir);
|
|
5578
|
+
if (!input.credentialsDir && identityDirectories.length > 1) {
|
|
5579
|
+
const lines = await Promise.all(
|
|
5580
|
+
identityDirectories.map(async (dir) => {
|
|
5581
|
+
const identity2 = await readIdentity(dir);
|
|
5582
|
+
const who = identity2?.agent_id ? `agent ${identity2.agent_id}` : "agent (no agent_id recorded)";
|
|
5583
|
+
const retired = !identity2?.api_key || await pathExists3(join(dir, TOMBSTONE_FILENAME));
|
|
5584
|
+
return retired ? ` \u2022 ${who} (${dir}): retired \u2014 not a repair target` : ` \u2022 ${who}: ${repairCommandFor(input.runtime, "", dir).replace(/^Run: /, "")}`;
|
|
5585
|
+
})
|
|
5586
|
+
);
|
|
5587
|
+
return {
|
|
5588
|
+
ok: false,
|
|
5589
|
+
messages: [
|
|
5590
|
+
`${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:`,
|
|
5591
|
+
...lines,
|
|
5592
|
+
"Not sure which agent is which? Compare the agent ids against the Haven agent page, or run --doctor first."
|
|
5593
|
+
]
|
|
5594
|
+
};
|
|
5595
|
+
}
|
|
5557
5596
|
if (others.length > 0) {
|
|
5558
5597
|
messages.push(`Note: ${others.length} other agent credential dir(s) exist \u2014 run --doctor for their status.`);
|
|
5559
5598
|
}
|