@haven_ai/connect 0.0.0-dev.202610071649.8f72409 → 0.0.0-dev.202610071831.a279c0e
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 +43 -18
- package/dist/cli.cjs +109 -26
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +109 -26
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +109 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -4
- package/dist/index.d.ts +29 -4
- package/dist/index.js +109 -26
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -385,8 +385,8 @@ and the API key travels beside it in a header.
|
|
|
385
385
|
|
|
386
386
|
`superseded_agent_ids` lists the other agent directories on this machine. A
|
|
387
387
|
re-run mints a NEW agent, and without `--replace` retires nothing, so those
|
|
388
|
-
older agents still hold live API and signing keys —
|
|
389
|
-
agent page if you meant to replace them. Empty on a clean first run; an empty
|
|
388
|
+
older agents still hold live API and signing keys — use **Remove agent…** on the
|
|
389
|
+
Haven agent page for each if you meant to replace them. Empty on a clean first run; an empty
|
|
390
390
|
list here is not a guarantee, since a scan that cannot read the credential root
|
|
391
391
|
also yields one rather than failing a completed setup.
|
|
392
392
|
|
|
@@ -499,12 +499,24 @@ already-configured machine behaves as follows (characterized in
|
|
|
499
499
|
consumed setup when Connect resolves it (or, in a rare concurrent-run race,
|
|
500
500
|
at registration). The key pair minted for the attempt exists only in memory
|
|
501
501
|
and is discarded. Start a fresh connection from the Haven dashboard instead.
|
|
502
|
-
- **Running a fresh setup on a configured machine
|
|
503
|
-
credentials into its own directory under `~/.haven/agents/<
|
|
502
|
+
- **Running a fresh setup on a configured machine writes the new agent's
|
|
503
|
+
credentials into its own directory under `~/.haven/agents/<slug-or-id>/`,
|
|
504
504
|
alongside the previous agent's directory, which stays byte-identical.
|
|
505
|
-
Nothing is rotated, revoked, or deleted locally
|
|
505
|
+
Nothing is rotated, revoked, or deleted locally.**
|
|
506
|
+
- **A default setup names its pair from the agent's display name
|
|
507
|
+
([#3737](https://github.com/d-hinders/Haven-AI/issues/3737)) and never
|
|
508
|
+
collides.** Without `--name`, `--bare` or `--replace`, the connector derives
|
|
509
|
+
the slug itself (`proposeServerSlug`: slugified display name, `agent`
|
|
510
|
+
fallback, numeric suffix when taken) and wires `haven-<slug>` /
|
|
511
|
+
`haven-signer-<slug>` — de-collided against **every** directory under the
|
|
512
|
+
credential root (retired and key-removed ones included, so a slug freed by
|
|
513
|
+
`--unwire` is never reused), and marked `-dev` on a non-production backend
|
|
514
|
+
(Base Sepolia or any chain that is not Base mainnet 8453; an explicit
|
|
515
|
+
`--name` is used verbatim and never altered). Nothing is displaced, so
|
|
516
|
+
nothing is asked. `--bare` opts back into the unnamed pair.
|
|
506
517
|
- **A bare re-run over a live previous agent is a decision, not a default
|
|
507
|
-
([#2551](https://github.com/d-hinders/Haven-AI/issues/2551)).**
|
|
518
|
+
([#2551](https://github.com/d-hinders/Haven-AI/issues/2551)).** Only a run
|
|
519
|
+
that targets the bare pair — `--bare`, or `--replace` — can collide; before
|
|
508
520
|
minting a key or registering, Connect scans the credential root for a
|
|
509
521
|
bare-pair directory that still holds a usable key — the same reading
|
|
510
522
|
`--doctor` classifies as `wired` or `superseded`; `retired`, `orphaned`,
|
|
@@ -521,7 +533,9 @@ already-configured machine behaves as follows (characterized in
|
|
|
521
533
|
flag itself. The setup prompt then permits one re-run with the flag the
|
|
522
534
|
user picks (#3689), on top of `--json` and a `--runtime` retry.
|
|
523
535
|
- **`--replace`** is the unattended answer "yes, replace". `--name <slug>`
|
|
524
|
-
installs alongside.
|
|
536
|
+
installs alongside. `--bare` is the human opt-in to the unnamed pair and
|
|
537
|
+
refuses with `--name`. Passing `--replace --name` is a usage error — they
|
|
538
|
+
contradict.
|
|
525
539
|
- **Replacing re-points the bare pair, then retires the previous directory
|
|
526
540
|
locally.** Connect owns the `haven` and `haven-signer` entries (and the
|
|
527
541
|
managed Codex/Hermes equivalents) and re-points them at the new agent's
|
|
@@ -539,8 +553,9 @@ already-configured machine behaves as follows (characterized in
|
|
|
539
553
|
[Running several agents in one runtime](#running-several-agents-in-one-runtime).
|
|
540
554
|
- **The previous agent is not revoked by a re-run — with or without
|
|
541
555
|
`--replace`.** Local retirement is local: its authority remains whatever
|
|
542
|
-
its on-chain rules and the Haven agent page say.
|
|
543
|
-
|
|
556
|
+
its on-chain rules and the Haven agent page say. Use **Remove agent…** on
|
|
557
|
+
the Haven agent page for agents you no longer use (or the superseded-agents
|
|
558
|
+
revoke on the setup screen). Connect never calls revoke — that
|
|
544
559
|
route is owner-authenticated, and an agent credential revoking a sibling
|
|
545
560
|
agent would be an agent editing its own authority.
|
|
546
561
|
- **A re-run never overwrites an existing credential file.** A write that would
|
|
@@ -567,15 +582,17 @@ As above, `<channel>` is a placeholder like the rest of this line: take the
|
|
|
567
582
|
package from the setup response's `connector_package`
|
|
568
583
|
and add `--name` to the command the dashboard gave you.
|
|
569
584
|
|
|
570
|
-
| |
|
|
571
|
-
|
|
572
|
-
| MCP entries | `haven
|
|
573
|
-
| Credentials | `~/.haven/agents/<
|
|
585
|
+
| | Default (no `--name`) | `--name research` | `--bare` |
|
|
586
|
+
|---|---|---|---|
|
|
587
|
+
| MCP entries | `haven-<slug>`, `haven-signer-<slug>` (slug from the agent's display name, `-dev`-marked on a non-production backend) | `haven-research`, `haven-signer-research` | `haven`, `haven-signer` |
|
|
588
|
+
| Credentials | `~/.haven/agents/<slug>/` | `~/.haven/agents/research/` | `~/.haven/agents/<agent-id>/` |
|
|
574
589
|
|
|
575
590
|
A writer only ever touches the pair it owns, so adding a named agent cannot
|
|
576
|
-
disturb the bare pair or another named one.
|
|
577
|
-
to how the connector behaved before named pairs existed
|
|
578
|
-
wired needs changing
|
|
591
|
+
disturb the bare pair or another named one. **`--bare` wires the bare pair
|
|
592
|
+
byte-identically to how the connector behaved before named pairs existed**, so
|
|
593
|
+
nothing already wired needs changing — it is the one flag (besides
|
|
594
|
+
`--replace`) that reaches the unnamed pair, and it cannot be combined with
|
|
595
|
+
`--name`.
|
|
579
596
|
|
|
580
597
|
The slug is **1–32 lowercase letters, digits and single hyphens**, validated
|
|
581
598
|
before anything is written, and **immutable once wired** — it is the server name
|
|
@@ -615,6 +632,14 @@ npx -y @haven_ai/connect@<channel> --rekey-finish --api-key sk_agent_... \
|
|
|
615
632
|
--runtime claude-code [--name research]
|
|
616
633
|
```
|
|
617
634
|
|
|
635
|
+
**No `--name` needed when exactly one live agent is on the machine.** Since
|
|
636
|
+
[#3737](https://github.com/d-hinders/Haven-AI/issues/3737), `--rekey` without
|
|
637
|
+
`--name` selects the sole agent directory that still holds a stored API key —
|
|
638
|
+
tombstoned and key-removed directories left by `--unwire` do not count — and
|
|
639
|
+
the printed phase-two command carries that agent's exact `--name` when it has
|
|
640
|
+
one. With **several** live agents it refuses and lists them, so you must pass
|
|
641
|
+
`--name <slug>`; a bare agent needs no `--name`, exactly as before.
|
|
642
|
+
|
|
618
643
|
**Phase one prints the exact phase-two command — prefer it over the line above.**
|
|
619
644
|
Since [#2423](https://github.com/d-hinders/Haven-AI/issues/2423) the connector
|
|
620
645
|
builds that command from the npm dist-tag **it** was published under, so a build
|
|
@@ -689,8 +714,8 @@ terminal and refuses everywhere else, so a `superseded` directory now means
|
|
|
689
714
|
someone chose it — a `--replace` whose install failed, an older connector, or
|
|
690
715
|
a directory this scan could not classify. A superseded directory
|
|
691
716
|
whose key is still live is a FAILING check naming the agent id, with the
|
|
692
|
-
repair spelled out:
|
|
693
|
-
directory. An already-revoked one reports as informational; an unreachable
|
|
717
|
+
repair spelled out: use **Remove agent…** on the Haven agent page, then remove
|
|
718
|
+
the directory. An already-revoked one reports as informational; an unreachable
|
|
694
719
|
probe is a note, never a verdict. Connect never revokes or deletes
|
|
695
720
|
credentials itself — it reports, you decide. The setup completion output
|
|
696
721
|
names superseded agents the moment they are created, for the same reason.
|
package/dist/cli.cjs
CHANGED
|
@@ -325,6 +325,19 @@ async function readStoredCredentials(serverName, agentIdOrSlug, baseDir) {
|
|
|
325
325
|
`No Haven credentials at ${directory}. Nothing to re-key \u2014 connect this agent first, or pass the --name you wired it under.`
|
|
326
326
|
);
|
|
327
327
|
}
|
|
328
|
+
let discoveredSlug;
|
|
329
|
+
if (!key) {
|
|
330
|
+
const binding = await readJsonFile(path.join(directory, "mcp-server-binding.json"));
|
|
331
|
+
const hostedName = binding && typeof binding === "object" ? asString(binding.server_name) : void 0;
|
|
332
|
+
if (hostedName && hostedName.startsWith("haven-")) {
|
|
333
|
+
const slug = hostedName.slice("haven-".length);
|
|
334
|
+
try {
|
|
335
|
+
assertValidServerSlug(slug);
|
|
336
|
+
discoveredSlug = slug;
|
|
337
|
+
} catch {
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
328
341
|
const agent = await readJsonFile(path.join(directory, "agent.json")) ?? {};
|
|
329
342
|
const signer = await readJsonFile(path.join(directory, "signer.json")) ?? {};
|
|
330
343
|
const agentId = asString(identity.agent_id);
|
|
@@ -340,6 +353,7 @@ async function readStoredCredentials(serverName, agentIdOrSlug, baseDir) {
|
|
|
340
353
|
directory,
|
|
341
354
|
agentId,
|
|
342
355
|
apiKey,
|
|
356
|
+
...discoveredSlug ? { discoveredSlug } : {},
|
|
343
357
|
apiUrl,
|
|
344
358
|
hostedMcpUrl,
|
|
345
359
|
delegateAddress: asString(agent.delegate_address) ?? asString(signer.delegate_address),
|
|
@@ -368,7 +382,9 @@ async function discoverSoleAgentDirectory(baseDir) {
|
|
|
368
382
|
const candidates = [];
|
|
369
383
|
for (const entry of entries) {
|
|
370
384
|
const directory = path.join(root, entry);
|
|
371
|
-
|
|
385
|
+
const identity = await readJsonFile(path.join(directory, "identity.json"));
|
|
386
|
+
if (!identity) continue;
|
|
387
|
+
if (!asString(identity.api_key)) continue;
|
|
372
388
|
if (await readJsonFile(path.join(directory, "TOMBSTONE.json"))) continue;
|
|
373
389
|
candidates.push(directory);
|
|
374
390
|
}
|
|
@@ -598,6 +614,7 @@ async function listMcpServerBindings(baseDir, excludeDirectory) {
|
|
|
598
614
|
var REKEY_PENDING_FILENAME, REKEY_PENDING_TTL_MS, CONNECT_OUTCOME_FILENAME, MCP_SERVER_BINDING_FILENAME;
|
|
599
615
|
var init_storage = __esm({
|
|
600
616
|
"src/storage.ts"() {
|
|
617
|
+
init_server_names();
|
|
601
618
|
REKEY_PENDING_FILENAME = "rekey-pending.json";
|
|
602
619
|
REKEY_PENDING_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
603
620
|
CONNECT_OUTCOME_FILENAME = "last-connect-outcome.json";
|
|
@@ -620,9 +637,9 @@ var init_runtime_manifest = __esm({
|
|
|
620
637
|
mcpPackage: "@haven_ai/mcp",
|
|
621
638
|
mcpVersion: mcp.MCP_VERSION,
|
|
622
639
|
sdkPackage: "@haven_ai/sdk",
|
|
623
|
-
sdkVersion: "0.0.0-dev.
|
|
640
|
+
sdkVersion: "0.0.0-dev.202610071831.a279c0e",
|
|
624
641
|
signerPackage: "@haven_ai/signer",
|
|
625
|
-
signerVersion: "0.0.0-dev.
|
|
642
|
+
signerVersion: "0.0.0-dev.202610071831.a279c0e",
|
|
626
643
|
// Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
|
|
627
644
|
// while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
|
|
628
645
|
// so the guard that was supposed to enforce the floor waved Node v23 through
|
|
@@ -1035,7 +1052,9 @@ async function writeHermesConfig(input, deps) {
|
|
|
1035
1052
|
restartRequired: true,
|
|
1036
1053
|
messages: [
|
|
1037
1054
|
`Updated Haven MCP entries in ${target}; stored the hosted MCP identity in ${envTarget}.`,
|
|
1038
|
-
|
|
1055
|
+
// #3737: name the pair actually written — a bare run prints exactly the
|
|
1056
|
+
// historical sentence; a named run names its own pair.
|
|
1057
|
+
`Restart Hermes (start a new session; gateway users: /restart), then verify with \`hermes mcp list\`, \`hermes mcp test ${names.hosted}\`, and \`hermes mcp test ${names.signer}\`.`,
|
|
1039
1058
|
"If several long-lived Hermes processes are running (a gateway plus TUI workers), restart EVERY one: each loads its MCP wiring at startup, so a process started before this setup keeps using its old snapshot.",
|
|
1040
1059
|
"If no mcp_* tools appear after restart, ensure the MCP SDK is installed in Hermes: pip install mcp"
|
|
1041
1060
|
]
|
|
@@ -3392,17 +3411,45 @@ async function detectWiringCollision(input) {
|
|
|
3392
3411
|
suggestedServerName: proposeServerSlug(input.agentName, taken)
|
|
3393
3412
|
};
|
|
3394
3413
|
}
|
|
3395
|
-
function proposeServerSlug(agentName, taken) {
|
|
3396
|
-
let base = agentName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-").slice(0,
|
|
3414
|
+
function proposeServerSlug(agentName, taken, maxBaseLength = 32) {
|
|
3415
|
+
let base = agentName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-").slice(0, maxBaseLength).replace(/-+$/g, "");
|
|
3397
3416
|
if (!slugIsValid(base)) base = "agent";
|
|
3398
3417
|
if (!taken.has(base)) return base;
|
|
3399
3418
|
for (let n = 2; n < 1e3; n += 1) {
|
|
3400
3419
|
const suffix = `-${n}`;
|
|
3401
|
-
const candidate = `${base.slice(0,
|
|
3420
|
+
const candidate = `${base.slice(0, maxBaseLength - suffix.length).replace(/-+$/g, "")}${suffix}`;
|
|
3402
3421
|
if (slugIsValid(candidate) && !taken.has(candidate)) return candidate;
|
|
3403
3422
|
}
|
|
3404
3423
|
throw new Error(`Could not propose an unused server name for ${JSON.stringify(agentName)}.`);
|
|
3405
3424
|
}
|
|
3425
|
+
function deriveServerSlug(agentName, taken, markNonProduction) {
|
|
3426
|
+
const mark = "-dev";
|
|
3427
|
+
if (!markNonProduction) return proposeServerSlug(agentName, taken);
|
|
3428
|
+
const base = proposeServerSlug(agentName, taken, 32 - mark.length);
|
|
3429
|
+
if (base.endsWith(mark)) return base;
|
|
3430
|
+
const marked = `${base}${mark}`;
|
|
3431
|
+
if (!taken.has(marked)) return marked;
|
|
3432
|
+
return proposeServerSlug(marked, taken);
|
|
3433
|
+
}
|
|
3434
|
+
async function listTakenDirectoryNames(credentialsDir) {
|
|
3435
|
+
const root = defaultCredentialRoot(credentialsDir);
|
|
3436
|
+
let entries = [];
|
|
3437
|
+
try {
|
|
3438
|
+
entries = await promises.readdir(root);
|
|
3439
|
+
} catch {
|
|
3440
|
+
return /* @__PURE__ */ new Set();
|
|
3441
|
+
}
|
|
3442
|
+
const taken = /* @__PURE__ */ new Set();
|
|
3443
|
+
for (const entry of entries) {
|
|
3444
|
+
try {
|
|
3445
|
+
if (!(await promises.stat(path.join(root, entry))).isDirectory()) continue;
|
|
3446
|
+
} catch {
|
|
3447
|
+
continue;
|
|
3448
|
+
}
|
|
3449
|
+
taken.add(entry);
|
|
3450
|
+
}
|
|
3451
|
+
return taken;
|
|
3452
|
+
}
|
|
3406
3453
|
function slugIsValid(slug) {
|
|
3407
3454
|
try {
|
|
3408
3455
|
assertValidServerSlug(slug);
|
|
@@ -3418,7 +3465,7 @@ async function promptWiringCollisionResolution(collision, agentName, io) {
|
|
|
3418
3465
|
io.write(`Setting up "${agentName}" on the bare haven / haven-signer pair would replace that wiring.
|
|
3419
3466
|
`);
|
|
3420
3467
|
io.write(" r) replace \u2014 re-point haven / haven-signer at the new agent and retire the previous directory locally\n");
|
|
3421
|
-
io.write(" (tombstoned, local key files removed; you still
|
|
3468
|
+
io.write(" (tombstoned, local key files removed; you still use Remove agent\u2026 on the Haven agent page)\n");
|
|
3422
3469
|
io.write(` a) alongside \u2014 install as a named agent (suggested: ${collision.suggestedServerName}) with its own
|
|
3423
3470
|
`);
|
|
3424
3471
|
io.write(` haven-<name> / haven-signer-<name> pair, leaving the current wiring untouched
|
|
@@ -3802,7 +3849,16 @@ async function executeConnect(options, deps, trace) {
|
|
|
3802
3849
|
log("Checked local credential storage \u2014 all clear.");
|
|
3803
3850
|
let serverName = options.serverName;
|
|
3804
3851
|
let replacing;
|
|
3805
|
-
if (!serverName) {
|
|
3852
|
+
if (!serverName && options.deriveServerName) {
|
|
3853
|
+
const taken = await listTakenDirectoryNames(options.credentialsDir);
|
|
3854
|
+
const nonProduction = setup.haven_wallet.chain_id !== PRODUCTION_CHAIN_ID;
|
|
3855
|
+
serverName = deriveServerSlug(setup.agent.name, taken, nonProduction);
|
|
3856
|
+
await assertServerSlugAvailable(serverName, options.credentialsDir);
|
|
3857
|
+
const derivedNames = serverNamesFor(serverName);
|
|
3858
|
+
log(
|
|
3859
|
+
`Naming this agent's MCP pair from its display name: ${derivedNames.hosted} / ${derivedNames.signer}. Nothing was displaced; to wire the bare haven / haven-signer pair instead, re-run with --bare, or choose the name yourself with --name <slug>.`
|
|
3860
|
+
);
|
|
3861
|
+
} else if (!serverName) {
|
|
3806
3862
|
const collision = await detectWiringCollision({
|
|
3807
3863
|
credentialsDir: options.credentialsDir,
|
|
3808
3864
|
agentName: setup.agent.name
|
|
@@ -4028,7 +4084,7 @@ async function executeConnect(options, deps, trace) {
|
|
|
4028
4084
|
if (supersededAgentsRetiredLocally === true) {
|
|
4029
4085
|
log("");
|
|
4030
4086
|
log(
|
|
4031
|
-
`Replaced: previous agent(s) ${supersededIds(replacing)} are retired on this machine but NOT revoked \u2014
|
|
4087
|
+
`Replaced: previous agent(s) ${supersededIds(replacing)} are retired on this machine but NOT revoked \u2014 use Remove agent\u2026 on the Haven agent page for each (it ends their live budgets), then restart EVERY long-lived host (gateways, TUI workers, editors): each holds the MCP wiring snapshot from its own start time, and the tombstone speaks only when a stale host next probes the old path.`
|
|
4032
4088
|
);
|
|
4033
4089
|
} else if (supersededAgentIds.length > 0) {
|
|
4034
4090
|
log("");
|
|
@@ -4036,7 +4092,7 @@ async function executeConnect(options, deps, trace) {
|
|
|
4036
4092
|
`Heads-up: this setup created a NEW agent. Your previous agent(s) \u2014 ${supersededAgentIds.join(", ")} \u2014 still exist with their own keys, and any host that was already running keeps acting as them.`
|
|
4037
4093
|
);
|
|
4038
4094
|
log(
|
|
4039
|
-
`If you meant to replace them:
|
|
4095
|
+
`If you meant to replace them: use Remove agent\u2026 on the Haven agent page for each (it ends their live budgets), then restart EVERY long-lived host (gateways, TUI workers, editors) \u2014 each holds the MCP wiring snapshot from its own start time, so after repeated setups each can be stuck on a DIFFERENT old agent. Then remove their directories under ~/.haven/agents (or ${RERUN_HINT} --tombstone <dir> to leave a diagnostic in their place). Run ${RERUN_HINT} --doctor to check whether their keys are still live.`
|
|
4040
4096
|
);
|
|
4041
4097
|
}
|
|
4042
4098
|
} catch {
|
|
@@ -4077,6 +4133,9 @@ async function executeConnect(options, deps, trace) {
|
|
|
4077
4133
|
runtimeInstall,
|
|
4078
4134
|
delegateAddress: registration.delegate_address,
|
|
4079
4135
|
hostedMcpUrl: registration.hosted_mcp_url,
|
|
4136
|
+
// #3737: the resolved name — derived, explicit, or the bare pair — rides
|
|
4137
|
+
// the machine-readable outcome alongside the prose completion text.
|
|
4138
|
+
serverName,
|
|
4080
4139
|
supersededAgentIds,
|
|
4081
4140
|
supersededAgentsRetiredLocally,
|
|
4082
4141
|
...replacing ? { retiredAgentIds } : {},
|
|
@@ -4151,6 +4210,9 @@ function completionOutcome(input) {
|
|
|
4151
4210
|
// #3122: always emitted on a completed run (empty list included), for the
|
|
4152
4211
|
// same reason as superseded_agent_ids above.
|
|
4153
4212
|
existing_agents_before_write: input.existingAgentsBeforeWrite ?? [],
|
|
4213
|
+
// #3737: the resolved hosted server name — always present on a completed
|
|
4214
|
+
// run, bare pair included, so a caller never has to parse the prose.
|
|
4215
|
+
server_name: serverNamesFor(input.serverName).hosted,
|
|
4154
4216
|
...input.serverNameReboundFrom ? { server_name_rebound_from: input.serverNameReboundFrom } : {},
|
|
4155
4217
|
...input.setupChallengeExpiresAt ? { setup_challenge_expires_at: input.setupChallengeExpiresAt } : {},
|
|
4156
4218
|
...runtimeInstall.errorCode ? { error: { code: runtimeInstall.errorCode, next_action: nextAction2 } } : {}
|
|
@@ -4201,7 +4263,7 @@ async function resolveWiringCollision(collision, agentName, options, deps) {
|
|
|
4201
4263
|
}
|
|
4202
4264
|
throw new ConnectError(
|
|
4203
4265
|
"wiring_collision",
|
|
4204
|
-
`This machine is already wired to a Haven agent with a live key (${supersededIds(collision)}), and setting up "${agentName}" on the bare haven / haven-signer pair would replace that wiring. Nothing was written and the Haven setup token is still unused. If you are an AI agent running this command: do NOT add a flag yourself \u2014 relay this to your user and stop. Your user decides: REPLACE the existing wiring (re-run the same command with --replace added, which re-points the pair at the new agent and retires the previous directory locally \u2014 they still
|
|
4266
|
+
`This machine is already wired to a Haven agent with a live key (${supersededIds(collision)}), and setting up "${agentName}" on the bare haven / haven-signer pair would replace that wiring. Nothing was written and the Haven setup token is still unused. If you are an AI agent running this command: do NOT add a flag yourself \u2014 relay this to your user and stop. Your user decides: REPLACE the existing wiring (re-run the same command with --replace added, which re-points the pair at the new agent and retires the previous directory locally \u2014 they still use Remove agent\u2026 on the Haven agent page to end the old agent\u2019s budgets), or install ALONGSIDE it (re-run with --name <slug> added, e.g. --name ${collision.suggestedServerName}, which gives the new agent its own haven-<slug> / haven-signer-<slug> pair). Re-run only with the flag your user chooses.`,
|
|
4205
4267
|
"relay_wiring_collision_to_user",
|
|
4206
4268
|
{ supersededAgentIds: collision.superseded.map((e) => e.agentId), suggestedServerName: collision.suggestedServerName }
|
|
4207
4269
|
);
|
|
@@ -4482,7 +4544,7 @@ async function listOtherAgentIds(baseDir, currentDirectory) {
|
|
|
4482
4544
|
function printNextSteps(result, log, approval, approvalUrl) {
|
|
4483
4545
|
for (const line of completionHandoffLines(result, approval, approvalUrl)) log(line);
|
|
4484
4546
|
}
|
|
4485
|
-
var CONNECTOR_VERSION, CONNECTOR_CLIENT_IDENTITY, CONNECT_OUTCOME_SCHEMA_VERSION, failureOutcomesByError, RERUN_HINT;
|
|
4547
|
+
var CONNECTOR_VERSION, PRODUCTION_CHAIN_ID, CONNECTOR_CLIENT_IDENTITY, CONNECT_OUTCOME_SCHEMA_VERSION, failureOutcomesByError, RERUN_HINT;
|
|
4486
4548
|
var init_runtime = __esm({
|
|
4487
4549
|
"src/runtime.ts"() {
|
|
4488
4550
|
init_api();
|
|
@@ -4499,7 +4561,8 @@ var init_runtime = __esm({
|
|
|
4499
4561
|
init_tombstone();
|
|
4500
4562
|
init_local_mcp_runtime();
|
|
4501
4563
|
init_runtime_manifest();
|
|
4502
|
-
CONNECTOR_VERSION = "0.0.0-dev.
|
|
4564
|
+
CONNECTOR_VERSION = "0.0.0-dev.202610071831.a279c0e";
|
|
4565
|
+
PRODUCTION_CHAIN_ID = 8453;
|
|
4503
4566
|
CONNECTOR_CLIENT_IDENTITY = `@haven_ai/connect/${CONNECTOR_VERSION}`;
|
|
4504
4567
|
CONNECT_OUTCOME_SCHEMA_VERSION = 1;
|
|
4505
4568
|
failureOutcomesByError = /* @__PURE__ */ new WeakMap();
|
|
@@ -4709,9 +4772,10 @@ async function startRekey(options, deps = {}) {
|
|
|
4709
4772
|
started_at: startedAt,
|
|
4710
4773
|
expires_at: expiresAt
|
|
4711
4774
|
});
|
|
4775
|
+
const finishName = options.serverName ?? stored.discoveredSlug;
|
|
4712
4776
|
const finishCommand = [
|
|
4713
4777
|
sdk.connectorRerunCommand("--rekey-finish"),
|
|
4714
|
-
|
|
4778
|
+
finishName ? `--name ${finishName}` : void 0,
|
|
4715
4779
|
"--api-key <the key the dashboard showed you>",
|
|
4716
4780
|
options.runtime ? `--runtime ${options.runtime}` : "--runtime <your runtime>"
|
|
4717
4781
|
].filter(Boolean).join(" ");
|
|
@@ -5543,9 +5607,9 @@ async function runDoctor(input, deps = {}) {
|
|
|
5543
5607
|
level: supersededLevel,
|
|
5544
5608
|
detail: supersededLevel === "failed" ? `${otherEntries.length} other credential dir(s) found \u2014 ${parts.join("; ")}. A host started before your latest setup keeps authenticating (and spending) as the old agent.` : supersededLevel === "advisory" ? `${otherEntries.length} other credential dir(s) found \u2014 ${parts.join("; ")}. Runtime '${input2.runtime}' has no config file the connector can read, so which of these agents are wired cannot be verified from this machine: a live key here may be an agent you use deliberately, or one a host started before your latest setup is still spending as.` : `${otherEntries.length} other credential dir(s) found \u2014 ${parts.join("; ")}.`,
|
|
5545
5609
|
...supersededLevel === "failed" ? {
|
|
5546
|
-
repair: `
|
|
5610
|
+
repair: `Use Remove agent\u2026 on the Haven agent page for ${supersededLive.join(", ")} (it ends each one\u2019s live budgets), then remove the old director(y/ies) under ~/.haven/agents. Connect never revokes or deletes for you.`
|
|
5547
5611
|
} : supersededLevel === "advisory" ? {
|
|
5548
|
-
repair: `Check ${supersededLive.join(", ")} on the Haven agent page:
|
|
5612
|
+
repair: `Check ${supersededLive.join(", ")} on the Haven agent page: use Remove agent\u2026 on the ones you no longer use, then remove their director(y/ies) under ~/.haven/agents. Connect never revokes or deletes for you.`
|
|
5549
5613
|
} : {}
|
|
5550
5614
|
});
|
|
5551
5615
|
}
|
|
@@ -5770,6 +5834,7 @@ function parseArgs(argv, env = process.env) {
|
|
|
5770
5834
|
let dryRun = false;
|
|
5771
5835
|
let unwireDir;
|
|
5772
5836
|
let replace = false;
|
|
5837
|
+
let bare = false;
|
|
5773
5838
|
for (let i = 0; i < argv.length; i += 1) {
|
|
5774
5839
|
const arg = argv[i];
|
|
5775
5840
|
if (arg === "--help" || arg === "-h") {
|
|
@@ -5817,6 +5882,8 @@ function parseArgs(argv, env = process.env) {
|
|
|
5817
5882
|
options.credentialsDir = requireValue(argv, ++i, arg);
|
|
5818
5883
|
} else if (arg === "--replace") {
|
|
5819
5884
|
replace = true;
|
|
5885
|
+
} else if (arg === "--bare") {
|
|
5886
|
+
bare = true;
|
|
5820
5887
|
} else if (arg === "--name") {
|
|
5821
5888
|
options.serverName = requireValue(argv, ++i, arg);
|
|
5822
5889
|
assertValidServerSlug(options.serverName);
|
|
@@ -5842,16 +5909,23 @@ function parseArgs(argv, env = process.env) {
|
|
|
5842
5909
|
if (help) {
|
|
5843
5910
|
return { options, help, json, doctor, repair, tombstone, rekey };
|
|
5844
5911
|
}
|
|
5845
|
-
if (replace) {
|
|
5912
|
+
if (replace || bare) {
|
|
5846
5913
|
if (rekeyPhase || tombstoneDir || unwire || doctor || repair || pruneSignerRuntimes2) {
|
|
5847
|
-
throw new Error("--replace belongs to a --setup run: it says what to do when the bare haven / haven-signer pair is already wired to another agent
|
|
5914
|
+
throw new Error(`${replace ? "--replace" : "--bare"} belongs to a --setup run: it says what to do when the bare haven / haven-signer pair is already wired to another agent.`);
|
|
5848
5915
|
}
|
|
5849
|
-
if (options.serverName) {
|
|
5916
|
+
if (bare && options.serverName) {
|
|
5850
5917
|
throw new Error(
|
|
5851
|
-
"--
|
|
5918
|
+
"--bare and --name contradict each other: --bare wires the bare haven / haven-signer pair, --name installs a named haven-<slug> pair. Pass one of them."
|
|
5852
5919
|
);
|
|
5853
5920
|
}
|
|
5854
|
-
|
|
5921
|
+
if (replace) {
|
|
5922
|
+
if (options.serverName) {
|
|
5923
|
+
throw new Error(
|
|
5924
|
+
"--replace and --name contradict each other: --replace overwrites the bare haven / haven-signer wiring, --name installs alongside it under its own pair. Pass one of them."
|
|
5925
|
+
);
|
|
5926
|
+
}
|
|
5927
|
+
options.replaceExistingWiring = true;
|
|
5928
|
+
}
|
|
5855
5929
|
}
|
|
5856
5930
|
if (destroyKeyMaterial && !unwire) {
|
|
5857
5931
|
throw new Error("--destroy-key-material only applies to --unwire.");
|
|
@@ -5925,6 +5999,9 @@ function parseArgs(argv, env = process.env) {
|
|
|
5925
5999
|
if (!options.apiBaseUrl) {
|
|
5926
6000
|
throw new Error("Missing --api <Haven API URL>.");
|
|
5927
6001
|
}
|
|
6002
|
+
if (!options.serverName && !bare && !replace) {
|
|
6003
|
+
options.deriveServerName = true;
|
|
6004
|
+
}
|
|
5928
6005
|
options.apiBaseUrl = options.apiBaseUrl.replace(/\/+$/, "");
|
|
5929
6006
|
return { options, help, json, doctor, repair, tombstone, rekey };
|
|
5930
6007
|
}
|
|
@@ -5952,13 +6029,19 @@ function helpText() {
|
|
|
5952
6029
|
" --replace When this machine is already wired to a different Haven agent on the bare",
|
|
5953
6030
|
" haven / haven-signer pair, re-point that pair at the new agent and retire the",
|
|
5954
6031
|
" previous agent directory locally (tombstoned, local key files removed).",
|
|
5955
|
-
" Nothing is revoked \u2014
|
|
5956
|
-
"
|
|
5957
|
-
" and an interactive terminal is asked
|
|
6032
|
+
" Nothing is revoked \u2014 use Remove agent\u2026 on the Haven agent page. Implies",
|
|
6033
|
+
" the bare pair. Without it (or --bare) a non-interactive run REFUSES such a",
|
|
6034
|
+
" collision (wiring_collision) and an interactive terminal is asked.",
|
|
6035
|
+
" --bare Wire the bare haven / haven-signer pair. Without --name a setup now names",
|
|
6036
|
+
" its pair from the agent's display name (haven-<slug> /",
|
|
6037
|
+
" haven-signer-<slug>, de-collided against this machine, ending -dev on a",
|
|
6038
|
+
" non-production backend), so adding an agent never has to displace anyone;",
|
|
6039
|
+
" --bare is the explicit opt-in to the unnamed pair. --name overrides it.",
|
|
5958
6040
|
" --name <slug> Wiring slug for a NAMED agent: writes haven-<slug> / haven-signer-<slug>",
|
|
5959
6041
|
" MCP entries and stores credentials at ~/.haven/agents/<slug>/, so several",
|
|
5960
6042
|
" agents can run side by side in one runtime. 1-32 lowercase letters, digits,",
|
|
5961
|
-
" single hyphens; immutable once wired.
|
|
6043
|
+
" single hyphens; immutable once wired. Without it the pair is named from the",
|
|
6044
|
+
" agent's display name; --bare wires the bare haven / haven-signer pair.",
|
|
5962
6045
|
" --ack-local-tools Write the one-time local Haven tools acknowledgement during setup.",
|
|
5963
6046
|
" --ack-signer Backward-compatible alias for --ack-local-tools.",
|
|
5964
6047
|
" --local Advanced: install the fully-local Haven MCP (no hosted dependency).",
|