cyberlegion 1.7.0 → 1.8.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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.plugin/pins.json +1 -1
- package/CHANGELOG.md +7 -0
- package/dist/cli.mjs +15 -9
- package/dist/index.d.mts +10 -5
- package/dist/index.mjs +11 -5
- package/package.json +2 -2
- package/plugin.json +1 -1
- package/skills/dispatch-governance/SKILL.md +1 -1
- package/skills/dispatch-governance/scripts/cyberlegion.mjs +3 -3
- package/skills/init-cyberlegion/scripts/cyberlegion.mjs +3 -3
- package/skills/legate/SKILL.md +1 -1
- package/skills/legate/scripts/cyberlegion.mjs +3 -3
- package/skills/manage-inbox/SKILL.md +1 -1
- package/skills/manage-inbox/scripts/cyberlegion.mjs +3 -3
- package/skills/relay-governance/SKILL.md +1 -1
- package/skills/relay-governance/scripts/cyberlegion.mjs +3 -3
- package/skills/subagent-backend-governance/SKILL.md +1 -1
- package/skills/subagent-backend-governance/scripts/cyberlegion.mjs +3 -3
package/.plugin/pins.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# cyberlegion
|
|
2
2
|
|
|
3
|
+
## 1.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 991f54c: `unit close` now closes a unit whose worktree is its repository's primary checkout instead of refusing it. It ends the session and deletes the record, pane pointer, brief and mailbox, and never removes, resets or cleans the checkout, so no `--force` is needed and uncommitted changes there are not refused. `--force` is still refused for such a unit, since it asks to discard changes in the checkout. This lets a live seat on a main checkout, or an exited record pointing at one, be removed for good.
|
|
8
|
+
- 999b959: Update runtime dependencies.
|
|
9
|
+
|
|
3
10
|
## 1.7.0
|
|
4
11
|
|
|
5
12
|
### Minor Changes
|
package/dist/cli.mjs
CHANGED
|
@@ -2963,7 +2963,7 @@ function useColor() {
|
|
|
2963
2963
|
}
|
|
2964
2964
|
new Command();
|
|
2965
2965
|
//#endregion
|
|
2966
|
-
//#region ../../node_modules/.pnpm/cyber-mux@0.
|
|
2966
|
+
//#region ../../node_modules/.pnpm/cyber-mux@0.12.0/node_modules/cyber-mux/dist/exec-Bmdcg_T7.mjs
|
|
2967
2967
|
const nodeExec$1 = (cmd, args) => {
|
|
2968
2968
|
try {
|
|
2969
2969
|
const out = execFileSync(cmd, args, {
|
|
@@ -2991,7 +2991,7 @@ function withReason(exec, message) {
|
|
|
2991
2991
|
return exec.lastError ? `${message} — ${exec.lastError}` : message;
|
|
2992
2992
|
}
|
|
2993
2993
|
//#endregion
|
|
2994
|
-
//#region ../../node_modules/.pnpm/cyber-mux@0.
|
|
2994
|
+
//#region ../../node_modules/.pnpm/cyber-mux@0.12.0/node_modules/cyber-mux/dist/backend-D3VSsev4.mjs
|
|
2995
2995
|
const KNOWN_MUX = [
|
|
2996
2996
|
"tmux",
|
|
2997
2997
|
"rmux",
|
|
@@ -10248,7 +10248,7 @@ async function exists(path) {
|
|
|
10248
10248
|
}
|
|
10249
10249
|
}
|
|
10250
10250
|
//#endregion
|
|
10251
|
-
//#region ../../node_modules/.pnpm/cyber-mux@0.
|
|
10251
|
+
//#region ../../node_modules/.pnpm/cyber-mux@0.12.0/node_modules/cyber-mux/dist/worktree.mjs
|
|
10252
10252
|
/**
|
|
10253
10253
|
* This module's own refusals and failures — plain cyber-mux prose, never a dependency's raw words.
|
|
10254
10254
|
* `reportWorktreeFailure` (`cli.ts`) forwards a `WorktreeGitError`'s message onto stdout verbatim
|
|
@@ -12612,8 +12612,7 @@ function deleteMailbox(store, id) {
|
|
|
12612
12612
|
//#endregion
|
|
12613
12613
|
//#region src/decommission.ts
|
|
12614
12614
|
/**
|
|
12615
|
-
* Tear a unit down and reap its registry record — the deterministic inverse of `spawn`. Refuses
|
|
12616
|
-
* the primary checkout (absolute — neither `--force` nor `--keep-worktree` overrides it) and a
|
|
12615
|
+
* Tear a unit down and reap its registry record — the deterministic inverse of `spawn`. Refuses a
|
|
12617
12616
|
* dirty worktree unless `--force`. Teardown always precedes reap: an already-gone worktree or pane
|
|
12618
12617
|
* is tolerated, but a genuine worktree-removal failure aborts and leaves the record intact so the
|
|
12619
12618
|
* operation is retryable.
|
|
@@ -12630,6 +12629,12 @@ function deleteMailbox(store, id) {
|
|
|
12630
12629
|
* discarded, so neither the dirty refusal nor `--force` applies — a dirty worktree is skipped by the
|
|
12631
12630
|
* next `acquire` rather than lost. Release precedes the pane teardown, so a release that throws leaves
|
|
12632
12631
|
* everything for a retry, as a failed removal does.
|
|
12632
|
+
*
|
|
12633
|
+
* A unit whose worktree is its repository's primary checkout — a seat registered on a main checkout —
|
|
12634
|
+
* is closed without touching the checkout: its session ends and its record, brief and mailbox are
|
|
12635
|
+
* reaped, but nothing is removed, reset or cleaned, so no dirty check applies and no `--force` is
|
|
12636
|
+
* needed. `--force` is refused there, since it asks to discard changes in the checkout. The checkout
|
|
12637
|
+
* is not reported as retained: it is no pool path a next unit could be spawned into.
|
|
12633
12638
|
*/
|
|
12634
12639
|
async function decommission(ctx, input) {
|
|
12635
12640
|
const rec = loadAgent(ctx.store, input.id);
|
|
@@ -12641,8 +12646,9 @@ async function decommission(ctx, input) {
|
|
|
12641
12646
|
const worktreeExists = worktreeRoot != null && existsSync(worktreeRoot);
|
|
12642
12647
|
const primaryRoot = worktreeExists ? await primaryRootOf(exec, worktreeRoot) : rec.worktree?.primaryRoot;
|
|
12643
12648
|
const held = rec.worktree?.lease;
|
|
12644
|
-
|
|
12645
|
-
|
|
12649
|
+
const onPrimary = worktreeRoot != null && primaryRoot != null && resolve(worktreeRoot) === resolve(primaryRoot);
|
|
12650
|
+
if (onPrimary && input.force) throw new Error(`refusing to close "${input.id}" with --force — its worktree is the primary checkout, which close never alters; close it without --force to end its session and reap its record`);
|
|
12651
|
+
const removesWorktree = worktreeExists && !input.keepWorktree && !held && !onPrimary;
|
|
12646
12652
|
if (removesWorktree && !input.force && isDirty(exec, worktreeRoot)) throw new Error(`unit "${input.id}" has uncommitted changes in its worktree — pass --force to discard them`);
|
|
12647
12653
|
const pane = rec.pane?.id ?? ctx.store.findPaneByAgentId(input.id);
|
|
12648
12654
|
if (worktreeRoot && !worktreeExists && primaryRoot && existsSync(primaryRoot)) exec("git", [
|
|
@@ -12671,7 +12677,7 @@ async function decommission(ctx, input) {
|
|
|
12671
12677
|
return {
|
|
12672
12678
|
agent: rec,
|
|
12673
12679
|
worktreeRoot,
|
|
12674
|
-
retainedWorktree: worktreeExists && (input.keepWorktree || held) ? worktreeRoot : void 0,
|
|
12680
|
+
retainedWorktree: worktreeExists && !onPrimary && (input.keepWorktree || held) ? worktreeRoot : void 0,
|
|
12675
12681
|
...lease ? { lease } : {},
|
|
12676
12682
|
pane
|
|
12677
12683
|
};
|
|
@@ -14785,7 +14791,7 @@ function defineSpawn(cmd) {
|
|
|
14785
14791
|
});
|
|
14786
14792
|
}
|
|
14787
14793
|
defineSpawn(unit.command("spawn"));
|
|
14788
|
-
syncsUnits(withGlobals(unit.command("close"))).description("release the unit's worktree lease (or remove a worktree it holds no lease on), tear down its session, and reap its state (the inverse of spawn)").argument("<id>", "unit id, handle, or worktree branch/CR ref").option("--force", "discard uncommitted changes in a worktree close removes (
|
|
14794
|
+
syncsUnits(withGlobals(unit.command("close"))).description("release the unit's worktree lease (or remove a worktree it holds no lease on), tear down its session, and reap its state (the inverse of spawn)").argument("<id>", "unit id, handle, or worktree branch/CR ref").option("--force", "discard uncommitted changes in a worktree close removes (refused for a unit on the primary checkout)").option("--keep-worktree", "leave the worktree on disk for reuse and reap everything else (skips the dirty check)").action(async (ref, opts) => {
|
|
14789
14795
|
const ctx = ctxOf(opts);
|
|
14790
14796
|
touch(ctx);
|
|
14791
14797
|
const agent = resolveAgent(ctx.store, ref);
|
package/dist/index.d.mts
CHANGED
|
@@ -332,12 +332,12 @@ export declare function realizeLaunch(def: AgentDef, opts?: RealizeLaunchOptions
|
|
|
332
332
|
//#region src/decommission.d.ts
|
|
333
333
|
interface DecommissionInput {
|
|
334
334
|
id: string;
|
|
335
|
-
/** Discard uncommitted changes in the worktree.
|
|
335
|
+
/** Discard uncommitted changes in the worktree. Refused for a unit on the primary checkout, which close never alters. */
|
|
336
336
|
force?: boolean;
|
|
337
337
|
/**
|
|
338
338
|
* Reap the unit but leave its worktree on disk, so a pool can detach it and hand it to the next
|
|
339
|
-
* unit — far cheaper than a fresh checkout per unit.
|
|
340
|
-
*
|
|
339
|
+
* unit — far cheaper than a fresh checkout per unit. A unit on the primary checkout is closed this
|
|
340
|
+
* way with or without the flag.
|
|
341
341
|
*/
|
|
342
342
|
keepWorktree?: boolean;
|
|
343
343
|
}
|
|
@@ -356,8 +356,7 @@ interface DecommissionResult {
|
|
|
356
356
|
pane?: string;
|
|
357
357
|
}
|
|
358
358
|
/**
|
|
359
|
-
* Tear a unit down and reap its registry record — the deterministic inverse of `spawn`. Refuses
|
|
360
|
-
* the primary checkout (absolute — neither `--force` nor `--keep-worktree` overrides it) and a
|
|
359
|
+
* Tear a unit down and reap its registry record — the deterministic inverse of `spawn`. Refuses a
|
|
361
360
|
* dirty worktree unless `--force`. Teardown always precedes reap: an already-gone worktree or pane
|
|
362
361
|
* is tolerated, but a genuine worktree-removal failure aborts and leaves the record intact so the
|
|
363
362
|
* operation is retryable.
|
|
@@ -374,6 +373,12 @@ interface DecommissionResult {
|
|
|
374
373
|
* discarded, so neither the dirty refusal nor `--force` applies — a dirty worktree is skipped by the
|
|
375
374
|
* next `acquire` rather than lost. Release precedes the pane teardown, so a release that throws leaves
|
|
376
375
|
* everything for a retry, as a failed removal does.
|
|
376
|
+
*
|
|
377
|
+
* A unit whose worktree is its repository's primary checkout — a seat registered on a main checkout —
|
|
378
|
+
* is closed without touching the checkout: its session ends and its record, brief and mailbox are
|
|
379
|
+
* reaped, but nothing is removed, reset or cleaned, so no dirty check applies and no `--force` is
|
|
380
|
+
* needed. `--force` is refused there, since it asks to discard changes in the checkout. The checkout
|
|
381
|
+
* is not reported as retained: it is no pool path a next unit could be spawned into.
|
|
377
382
|
*/
|
|
378
383
|
export declare function decommission(ctx: IdContext, input: DecommissionInput): Promise<DecommissionResult>;
|
|
379
384
|
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -1810,8 +1810,7 @@ function deleteMailbox(store, id) {
|
|
|
1810
1810
|
//#endregion
|
|
1811
1811
|
//#region src/decommission.ts
|
|
1812
1812
|
/**
|
|
1813
|
-
* Tear a unit down and reap its registry record — the deterministic inverse of `spawn`. Refuses
|
|
1814
|
-
* the primary checkout (absolute — neither `--force` nor `--keep-worktree` overrides it) and a
|
|
1813
|
+
* Tear a unit down and reap its registry record — the deterministic inverse of `spawn`. Refuses a
|
|
1815
1814
|
* dirty worktree unless `--force`. Teardown always precedes reap: an already-gone worktree or pane
|
|
1816
1815
|
* is tolerated, but a genuine worktree-removal failure aborts and leaves the record intact so the
|
|
1817
1816
|
* operation is retryable.
|
|
@@ -1828,6 +1827,12 @@ function deleteMailbox(store, id) {
|
|
|
1828
1827
|
* discarded, so neither the dirty refusal nor `--force` applies — a dirty worktree is skipped by the
|
|
1829
1828
|
* next `acquire` rather than lost. Release precedes the pane teardown, so a release that throws leaves
|
|
1830
1829
|
* everything for a retry, as a failed removal does.
|
|
1830
|
+
*
|
|
1831
|
+
* A unit whose worktree is its repository's primary checkout — a seat registered on a main checkout —
|
|
1832
|
+
* is closed without touching the checkout: its session ends and its record, brief and mailbox are
|
|
1833
|
+
* reaped, but nothing is removed, reset or cleaned, so no dirty check applies and no `--force` is
|
|
1834
|
+
* needed. `--force` is refused there, since it asks to discard changes in the checkout. The checkout
|
|
1835
|
+
* is not reported as retained: it is no pool path a next unit could be spawned into.
|
|
1831
1836
|
*/
|
|
1832
1837
|
async function decommission(ctx, input) {
|
|
1833
1838
|
const rec = loadAgent(ctx.store, input.id);
|
|
@@ -1839,8 +1844,9 @@ async function decommission(ctx, input) {
|
|
|
1839
1844
|
const worktreeExists = worktreeRoot != null && existsSync(worktreeRoot);
|
|
1840
1845
|
const primaryRoot = worktreeExists ? await primaryRootOf(exec, worktreeRoot) : rec.worktree?.primaryRoot;
|
|
1841
1846
|
const held = rec.worktree?.lease;
|
|
1842
|
-
|
|
1843
|
-
|
|
1847
|
+
const onPrimary = worktreeRoot != null && primaryRoot != null && resolve(worktreeRoot) === resolve(primaryRoot);
|
|
1848
|
+
if (onPrimary && input.force) throw new Error(`refusing to close "${input.id}" with --force — its worktree is the primary checkout, which close never alters; close it without --force to end its session and reap its record`);
|
|
1849
|
+
const removesWorktree = worktreeExists && !input.keepWorktree && !held && !onPrimary;
|
|
1844
1850
|
if (removesWorktree && !input.force && isDirty(exec, worktreeRoot)) throw new Error(`unit "${input.id}" has uncommitted changes in its worktree — pass --force to discard them`);
|
|
1845
1851
|
const pane = rec.pane?.id ?? ctx.store.findPaneByAgentId(input.id);
|
|
1846
1852
|
if (worktreeRoot && !worktreeExists && primaryRoot && existsSync(primaryRoot)) exec("git", [
|
|
@@ -1869,7 +1875,7 @@ async function decommission(ctx, input) {
|
|
|
1869
1875
|
return {
|
|
1870
1876
|
agent: rec,
|
|
1871
1877
|
worktreeRoot,
|
|
1872
|
-
retainedWorktree: worktreeExists && (input.keepWorktree || held) ? worktreeRoot : void 0,
|
|
1878
|
+
retainedWorktree: worktreeExists && !onPrimary && (input.keepWorktree || held) ? worktreeRoot : void 0,
|
|
1873
1879
|
...lease ? { lease } : {},
|
|
1874
1880
|
pane
|
|
1875
1881
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cyberlegion",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Harness-agnostic agent session spawning and messaging over the filesystem",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"cyber-agentkit": "^2.0.0",
|
|
45
|
-
"cyber-mux": "0.
|
|
45
|
+
"cyber-mux": "0.12.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"cynapse": "^0.1.0"
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "cyberlegion",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.8.0",
|
|
5
5
|
"description": "Agent session spawning, messaging, and dispatch — the Legion and its Legate.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "unional"
|
|
@@ -17,7 +17,7 @@ satisfies verdict schema `V`.
|
|
|
17
17
|
> **Running the CLI.** Every `node scripts/cyberlegion.mjs …` command below runs the `cyberlegion` CLI
|
|
18
18
|
> this plugin ships. The path is relative to this skill's own directory, not the working directory.
|
|
19
19
|
> If you cannot resolve it, run the published CLI of the version that shipped this skill instead,
|
|
20
|
-
> with the same arguments: `npx -y cyberlegion@1.
|
|
20
|
+
> with the same arguments: `npx -y cyberlegion@1.8.0`.
|
|
21
21
|
|
|
22
22
|
## 1. Resolve the agent definition
|
|
23
23
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Compiled by `universal-plugin plugin build` from src/skill-script.ts. Edit the source, not this file.
|
|
2
|
-
globalThis.__SKILL_SCRIPT__ = {"package":null,"bin":null,"version":"1.
|
|
2
|
+
globalThis.__SKILL_SCRIPT__ = {"package":null,"bin":null,"version":"1.8.0","entry":"skills/dispatch-governance/scripts/cyberlegion.mjs","commands":{}};
|
|
3
3
|
import e,{chmodSync as t,existsSync as n,mkdirSync as r,readFileSync as i,readdirSync as a,realpathSync as o,renameSync as s,rmSync as c,statSync as l,writeFileSync as u}from"node:fs";import d,{basename as f,dirname as p,isAbsolute as m,join as h,resolve as g,sep as _}from"node:path";import{EventEmitter as v}from"node:events";import y,{execFile as ee,execFileSync as b}from"node:child_process";import x from"node:process";import{stripVTControlCharacters as te}from"node:util";import{homedir as ne}from"node:os";import{createHash as re,randomBytes as ie,randomUUID as ae}from"node:crypto";import{lstat as oe,readFile as se,readdir as ce,readlink as le,realpath as ue,unlink as de,writeFile as fe}from"node:fs/promises";var pe=class extends Error{constructor(e,t,n){super(n),Error.captureStackTrace(this,this.constructor),this.name=this.constructor.name,this.code=t,this.exitCode=e,this.nestedError=void 0}},me=class extends pe{constructor(e){super(1,`commander.invalidArgument`,e),Error.captureStackTrace(this,this.constructor),this.name=this.constructor.name}},he=class{constructor(e,t){switch(this.description=t||``,this.variadic=!1,this.parseArg=void 0,this.defaultValue=void 0,this.defaultValueDescription=void 0,this.argChoices=void 0,e[0]){case`<`:this.required=!0,this._name=e.slice(1,-1);break;case`[`:this.required=!1,this._name=e.slice(1,-1);break;default:this.required=!0,this._name=e}this._name.endsWith(`...`)&&(this.variadic=!0,this._name=this._name.slice(0,-3))}name(){return this._name}_collectValue(e,t){return t===this.defaultValue||!Array.isArray(t)?[e]:(t.push(e),t)}default(e,t){return this.defaultValue=e,this.defaultValueDescription=t,this}argParser(e){return this.parseArg=e,this}choices(e){return this.argChoices=e.slice(),this.parseArg=(e,t)=>{if(!this.argChoices.includes(e))throw new me(`Allowed choices are ${this.argChoices.join(`, `)}.`);return this.variadic?this._collectValue(e,t):e},this}argRequired(){return this.required=!0,this}argOptional(){return this.required=!1,this}};function ge(e){let t=e.name()+(e.variadic===!0?`...`:``);return e.required?`<`+t+`>`:`[`+t+`]`}var _e=class{constructor(){this.helpWidth=void 0,this.minWidthToWrap=40,this.sortSubcommands=!1,this.sortOptions=!1,this.showGlobalOptions=!1}prepareContext(e){this.helpWidth=this.helpWidth??e.helpWidth??80}visibleCommands(e){let t=e.commands.filter(e=>!e._hidden),n=e._getHelpCommand();return n&&!n._hidden&&t.push(n),this.sortSubcommands&&t.sort((e,t)=>e.name().localeCompare(t.name())),t}compareOptions(e,t){let n=e=>e.short?e.short.replace(/^-/,``):e.long.replace(/^--/,``);return n(e).localeCompare(n(t))}visibleOptions(e){let t=e.options.filter(e=>!e.hidden),n=e._getHelpOption();if(n&&!n.hidden){let r=n.short&&e._findOption(n.short),i=n.long&&e._findOption(n.long);!r&&!i?t.push(n):n.long&&!i?t.push(e.createOption(n.long,n.description)):n.short&&!r&&t.push(e.createOption(n.short,n.description))}return this.sortOptions&&t.sort(this.compareOptions),t}visibleGlobalOptions(e){if(!this.showGlobalOptions)return[];let t=[];for(let n=e.parent;n;n=n.parent){let e=n.options.filter(e=>!e.hidden);t.push(...e)}return this.sortOptions&&t.sort(this.compareOptions),t}visibleArguments(e){return e._argsDescription&&e.registeredArguments.forEach(t=>{t.description=t.description||e._argsDescription[t.name()]||``}),e.registeredArguments.find(e=>e.description)?e.registeredArguments:[]}subcommandTerm(e){let t=e.registeredArguments.map(e=>ge(e)).join(` `);return e._name+(e._aliases[0]?`|`+e._aliases[0]:``)+(e.options.length?` [options]`:``)+(t?` `+t:``)}optionTerm(e){return e.flags}argumentTerm(e){return e.name()}longestSubcommandTermLength(e,t){return t.visibleCommands(e).reduce((e,n)=>Math.max(e,this.displayWidth(t.styleSubcommandTerm(t.subcommandTerm(n)))),0)}longestOptionTermLength(e,t){return t.visibleOptions(e).reduce((e,n)=>Math.max(e,this.displayWidth(t.styleOptionTerm(t.optionTerm(n)))),0)}longestGlobalOptionTermLength(e,t){return t.visibleGlobalOptions(e).reduce((e,n)=>Math.max(e,this.displayWidth(t.styleOptionTerm(t.optionTerm(n)))),0)}longestArgumentTermLength(e,t){return t.visibleArguments(e).reduce((e,n)=>Math.max(e,this.displayWidth(t.styleArgumentTerm(t.argumentTerm(n)))),0)}commandUsage(e){let t=e._name;e._aliases[0]&&(t=t+`|`+e._aliases[0]);let n=``;for(let t=e.parent;t;t=t.parent)n=t.name()+` `+n;return n+t+` `+e.usage()}commandDescription(e){return e.description()}subcommandDescription(e){return e.summary()||e.description()}optionDescription(e){let t=[];if(e.argChoices&&t.push(`choices: ${e.argChoices.map(e=>JSON.stringify(e)).join(`, `)}`),e.defaultValue!==void 0&&(e.required||e.optional||e.isBoolean()&&typeof e.defaultValue==`boolean`)&&t.push(`default: ${e.defaultValueDescription||JSON.stringify(e.defaultValue)}`),e.presetArg!==void 0&&e.optional&&t.push(`preset: ${JSON.stringify(e.presetArg)}`),e.envVar!==void 0&&t.push(`env: ${e.envVar}`),t.length>0){let n=`(${t.join(`, `)})`;return e.description?`${e.description} ${n}`:n}return e.description}argumentDescription(e){let t=[];if(e.argChoices&&t.push(`choices: ${e.argChoices.map(e=>JSON.stringify(e)).join(`, `)}`),e.defaultValue!==void 0&&t.push(`default: ${e.defaultValueDescription||JSON.stringify(e.defaultValue)}`),t.length>0){let n=`(${t.join(`, `)})`;return e.description?`${e.description} ${n}`:n}return e.description}formatItemList(e,t,n){return t.length===0?[]:[n.styleTitle(e),...t,``]}groupItems(e,t,n){let r=/* @__PURE__ */ new Map;return e.forEach(e=>{let t=n(e);r.has(t)||r.set(t,[])}),t.forEach(e=>{let t=n(e);r.has(t)||r.set(t,[]),r.get(t).push(e)}),r}formatHelp(e,t){let n=t.padWidth(e,t),r=t.helpWidth??80;function i(e,r){return t.formatItem(e,n,r,t)}let a=[`${t.styleTitle(`Usage:`)} ${t.styleUsage(t.commandUsage(e))}`,``],o=t.commandDescription(e);o.length>0&&(a=a.concat([t.boxWrap(t.styleCommandDescription(o),r),``]));let s=t.visibleArguments(e).map(e=>i(t.styleArgumentTerm(t.argumentTerm(e)),t.styleArgumentDescription(t.argumentDescription(e))));if(a=a.concat(this.formatItemList(`Arguments:`,s,t)),this.groupItems(e.options,t.visibleOptions(e),e=>e.helpGroupHeading??`Options:`).forEach((e,n)=>{let r=e.map(e=>i(t.styleOptionTerm(t.optionTerm(e)),t.styleOptionDescription(t.optionDescription(e))));a=a.concat(this.formatItemList(n,r,t))}),t.showGlobalOptions){let n=t.visibleGlobalOptions(e).map(e=>i(t.styleOptionTerm(t.optionTerm(e)),t.styleOptionDescription(t.optionDescription(e))));a=a.concat(this.formatItemList(`Global Options:`,n,t))}return this.groupItems(e.commands,t.visibleCommands(e),e=>e.helpGroup()||`Commands:`).forEach((e,n)=>{let r=e.map(e=>i(t.styleSubcommandTerm(t.subcommandTerm(e)),t.styleSubcommandDescription(t.subcommandDescription(e))));a=a.concat(this.formatItemList(n,r,t))}),a.join(`
|
|
4
4
|
`)}displayWidth(e){return te(e).length}styleTitle(e){return e}styleUsage(e){return e.split(` `).map(e=>e===`[options]`?this.styleOptionText(e):e===`[command]`?this.styleSubcommandText(e):e[0]===`[`||e[0]===`<`?this.styleArgumentText(e):this.styleCommandText(e)).join(` `)}styleCommandDescription(e){return this.styleDescriptionText(e)}styleOptionDescription(e){return this.styleDescriptionText(e)}styleSubcommandDescription(e){return this.styleDescriptionText(e)}styleArgumentDescription(e){return this.styleDescriptionText(e)}styleDescriptionText(e){return e}styleOptionTerm(e){return this.styleOptionText(e)}styleSubcommandTerm(e){return e.split(` `).map(e=>e===`[options]`?this.styleOptionText(e):e[0]===`[`||e[0]===`<`?this.styleArgumentText(e):this.styleSubcommandText(e)).join(` `)}styleArgumentTerm(e){return this.styleArgumentText(e)}styleOptionText(e){return e}styleArgumentText(e){return e}styleSubcommandText(e){return e}styleCommandText(e){return e}padWidth(e,t){return Math.max(t.longestOptionTermLength(e,t),t.longestGlobalOptionTermLength(e,t),t.longestSubcommandTermLength(e,t),t.longestArgumentTermLength(e,t))}preformatted(e){return/\n[^\S\r\n]/.test(e)}formatItem(e,t,n,r){let i=` `.repeat(2);if(!n)return i+e;let a=e.padEnd(t+e.length-r.displayWidth(e)),o=(this.helpWidth??80)-t-2-2,s;return s=o<this.minWidthToWrap||r.preformatted(n)?n:r.boxWrap(n,o).replace(/\n/g,`
|
|
5
5
|
`+` `.repeat(t+2)),i+a+` `.repeat(2)+s.replace(/\n/g,`\n${i}`)}boxWrap(e,t){if(t<this.minWidthToWrap)return e;let n=e.split(/\r\n|\n/),r=/[\s]*[^\s]+/g,i=[];return n.forEach(e=>{let n=e.match(r);if(n===null){i.push(``);return}let a=[n.shift()],o=this.displayWidth(a[0]);n.forEach(e=>{let n=this.displayWidth(e);if(o+n<=t){a.push(e),o+=n;return}i.push(a.join(``));let r=e.trimStart();a=[r],o=this.displayWidth(r)}),i.push(a.join(``))}),i.join(`
|
|
@@ -62,7 +62,7 @@ Expecting one of '${n.join(`', '`)}'`);let r=`${e}Help`;return this.on(r,e=>{let
|
|
|
62
62
|
`))if(t.trim()!==``)return t;return``}function $a(e){return e.toLowerCase().split(/[^a-z0-9]+/).filter(e=>e!==``)}function eo(e,t){if(e.length===0)return``;let n=e[0].slice(0,t);for(let r of e.slice(1)){let e=`${n}-${r}`;if(e.length>t)break;n=e}return n}const to={acquire:Ci,release:ri};function no(e){return async(t,n)=>e(t,[...n])}function ro(e){return`cyberlegion:${e}`}const io=[Fi],H={claude:`claude`,cursor:`cursor-agent`,codex:`codex`},ao={claude:`/clear`,codex:`/clear`,copilot:`/clear`,cursor:`/new-chat`},oo=/* @__PURE__ */ new Set([`gemini`]);function so(e){let t=ao[e];if(t)return t;throw oo.has(e)?Error(`"${e}" has no honest fresh-context command — its own "/clear" clears only the terminal screen, not the model context, so unit clear refuses to send a false-friend reset that would leave stale context behind`):Error(`"${e}" is not in the reset map (${Object.keys(ao).join(` | `)}) — unit clear refuses to guess a command`)}async function co(e,t){try{return await zr({from:g(t),exec:no(e)})}catch{throw Error(`--repo ${t} is not inside a git repository`)}}function lo(e,t){let n=g(t);try{return Mi((t,r)=>e(t,t===`git`?[`-C`,n,...r]:r))}catch{return}}function uo(e,t,n){if(n)return{ref:n};let r=(...n)=>e(`git`,[`-C`,t,...n]),i=e=>({ref:`HEAD`,fallback:e});if(r(`remote`,`get-url`,`origin`)===null)return i(`there is no origin remote`);if(r(`fetch`,`--quiet`,`origin`)===null)return i(`the fetch of origin failed`);let a=r(`symbolic-ref`,`--quiet`,`--short`,`refs/remotes/origin/HEAD`);return a?{ref:a}:i(`origin's default branch is unknown (origin/HEAD is not set)`)}async function fo(e,t){e.store.ensureMarker();let r=e.env??process.env,i=e.exec??P,a=V(r,i),o=N(r),s=t.harness;if(!s||!(s in H))throw Error(`spawn needs a --harness in the launch map (${Object.keys(H).join(` | `)})`);let c=jo(t);if(c==null)throw Error(`spawn needs a brief — pass --task <text>, --task - (stdin), or --brief-file <path>`);if(t.cwd&&(t.branch||t.worktreePath||t.repo||t.base))throw Error(`--cwd cannot combine with the worktree-creating flags --branch/--worktree-path/--repo/--base`);let l=Wi(),u=t.cwd!==void 0&&t.cwdOwnRepo===!0?lo(i,t.cwd):t.repo?await co(i,t.repo):await zr({exec:no(i)}),d=t.command??H[s],f=()=>vo(e,a.name,l,d),p=Ar(a,o),m,_,v,y,ee;if(t.cwd){if(!n(t.cwd))throw Error(`--cwd directory must already exist: ${t.cwd}`);m=g(t.cwd),u!==void 0&&Ni(m,u),_=null;let e=t.at??`tab`;ee=a.open(i,{cwd:m,launch:f(),at:e,from:p,..._o(e,t,c,l)})}else{if(u===void 0)throw Error(`a worktree spawn needs a primary checkout`);let e=t.branch??`cyberlegion/unit-${l}`,n=t.at??`workspace`;t.worktreePath&&Ni(g(t.worktreePath),u),v=uo(i,u,t.base);let r=_o(n,t,c,l),o=n===`workspace`?a.worktree:void 0,s,d=o?async t=>{let n=o.createInWorkspace(i,{primaryRoot:u,branch:e,path:t.path,base:t.base,launch:f(),...r});Ni(n.worktree.root,u),s=n.target}:xi(no(i)),b;if(t.worktreePath)await d({primaryRoot:u,path:t.worktreePath,base:v.ref,branch:e}),b=g(t.worktreePath),_={root:b,branch:e,primaryRoot:u};else{let t=ro(l),n=await to.acquire({primaryRoot:u,holder:t,branch:e,base:v.ref,exec:no(i),ignore:io,create:d});b=n.worktree,y=n.reused,_={root:b,branch:e,primaryRoot:u,lease:{leaseId:n.leaseId,holder:t}}}Ni(b,u),Ri(h(b,`.agents`,`cyberlegion`)),m=b,ee=s??(o?o.openInWorkspace(i,{primaryRoot:u,path:b,launch:f(),...r}).target:a.open(i,{cwd:m,launch:f(),at:n,from:p,...r}))}let b=new Date(e.now?.()??Date.now()).toISOString(),x=a.name,te={id:l,handle:t.handle??l.slice(0,6),harness:s,cwd:m,worktree:_,pane:x===`tmux`||x===`herdr`?{mux:x,id:ee.id}:null,status:`active`,createdAt:b,lastSeen:b,brief:M.briefFile(e.store.root,l),launch:d,...R(e)?{spawnedBy:R(e)}:{}};return I(e.store,te),e.store.putPaneIndex(ee.id,l),e.store.writeBrief(l,Ao(c,t.briefInstructions)),{agent:te,pane:ee.id,launch:d,...v?{base:v}:{},...y===void 0?{}:{reused:y}}}async function po(e,t,n={}){let r=await fo(e,t),i=await mo(e,r,n);return i.trustBlocked?{...r,...i}:{...r,...await ho(e,r,i,n)}}async function mo(e,t,n){let r=await go(e,t,n.trustOpts,n.trustFolder);return r.state===`needs-human`||r.state===`stuck`?{rung:!1,trust:r.state,trustBlocked:r.message}:{rung:!1,trust:r.state}}async function ho(e,t,n,r){let i=e.env??process.env,a=e.exec??P,o=t.agent.brief,s=await Va(()=>V(i,a),a,{target:{id:t.pane},briefPath:o??``,noWake:r.noWake||!o,harness:t.agent.harness},r.nudgeOpts);return{rung:s.rung,trust:n.trust,...s.warning?{warning:s.warning}:{}}}async function go(e,t,n,r=!1){let i=e.exec??P,a;try{a=V(e.env??process.env,i)}catch{return{state:`unsettled`}}return Ga(a,i,{id:t.pane},{harness:t.agent.harness,folder:t.agent.cwd,pane:t.pane,policy:t.agent.worktree||r?`accept`:`ask`},n)}function _o(e,t,n,r){return e===`workspace`?{label:Za({brief:n,handle:t.handle,id:r})}:{}}function vo(e,t,n,r,i={}){let a=e.self?wo(M.dataDir(e.store.root,n),e.self):void 0,o=a&&i.bindSelf?`${U(a)} unit rebind ${U(n)} --space ${U(e.store.root)} >/dev/null 2>&1; `:``,s=`${a?xo(a):``}${So(t)}`;return`${o}${s}${r}${i.fallback?` || ${s}${i.fallback}`:``}`}const yo={claude:e=>`--resume ${U(e)}`,codex:e=>`resume ${U(e)}`};function bo(e,t,n){let r=yo[e];if(r&&t.split(/\s+/)[0]===H[e])return`${t} ${r(n)}`}function xo(e){return`PATH=${U(p(e))}:"$PATH" CYBERLEGION_CLI=${U(e)} `}function So(e){return e===`tmux`?`CYBER_MUX=tmux CYBER_MUX_PANE=$TMUX_PANE `:e===`herdr`?`CYBER_MUX=herdr `:``}function Co(){let e=process.argv[1];return[process.execPath,...process.execArgv,...e?[o(e)]:[]]}function wo(e,n){let i=h(e,`bin`);r(i,{recursive:!0});let a=h(i,`cyberlegion`);return u(a,`#!/bin/sh\nexec ${n.map(U).join(` `)} "$@"\n`),t(a,493),a}function U(e){return`'${e.replaceAll(`'`,`'\\''`)}'`}function To(e,t){let n=typeof t==`string`?z(e.store,t):da(e.store,t),r=n.pane?.id??e.store.findPaneByAgentId(n.id);if(!r)throw Error(`unit "${typeof t==`string`?t:n.id}" has no known session pane`);return{agent:n,target:{id:r}}}function Eo(e,t){let{agent:n,target:r}=To(e,t),i=e.exec??P;return V(e.env??process.env,i).focus(i,r),{agent:n,pane:r.id}}async function Do(e,t,n={}){let{agent:r,target:i}=To(e,t),a=e.exec??P,o=n.message||`You have unread mail — check your inbox.`,s=V(e.env??process.env,a),c=await Da(s,a,i,()=>Aa(s,a,i,o,n.nudgeOpts),{...n.guardOpts,harness:r.harness,ownText:e=>Fa(e,o)});return{agent:r,pane:i.id,message:o,resubmits:c.resubmits}}function Oo(e,t,n={}){let{agent:r,target:i}=To(e,t),a=e.exec??P,o=V(e.env??process.env,a).read(a,i,{lines:n.lines}).text;return{agent:r,pane:i.id,output:o}}async function ko(e,t,n={}){let{agent:r,target:i}=To(e,t),a=i.id;if(!r.harness)throw Error(`unit "${t}" has no harness on record — cannot resolve its reset command`);let o=so(r.harness),s=e.env??process.env,c=e.exec??P,l=V(s,c);return await Da(l,c,i,async()=>l.submit(c,i,o),{...n.guardOpts,harness:r.harness,ownText:e=>Fa(e)}),{agent:r,pane:a,command:o}}function Ao(e,t){return t?`## Agent instructions\n\n${t}\n\n## Brief\n\n${e}`:e}function jo(e,t=()=>i(0,`utf8`)){return e.briefFile?i(e.briefFile,`utf8`):e.task===`-`?t():e.task!=null&&e.task!==``?e.task:null}function Mo(e){let t=/^---\r?\n([\s\S]*?)\r?\n---\s*(?:\r?\n)?([\s\S]*)$/.exec(e);if(!t)return{fm:{},instructions:e.trim()};let[,n,r]=t,i={},a=n.split(`
|
|
63
63
|
`),o=0;for(;o<a.length;){let e=a[o].replace(/\r$/,``);if(e.trim()===``||e.trim().startsWith(`#`)||e.length-e.trimStart().length>0){o++;continue}let t=e.indexOf(`:`);if(t===-1){o++;continue}let n=e.slice(0,t).trim(),r=e.slice(t+1).trim();if(o++,r===`>`||r===`>-`||r===`|`||r===`|-`){let e=[];for(;o<a.length;){let t=a[o].replace(/\r$/,``);if(t.trim()===``)o++;else{if(t.length-t.trimStart().length===0)break;e.push(t.trim()),o++}}r=e.join(r.startsWith(`|`)?`
|
|
64
64
|
`:` `)}i[n]=No(r)}return{fm:i,instructions:r.trim()}}function No(e){return e.length>=2&&(e.startsWith(`"`)&&e.endsWith(`"`)||e.startsWith(`'`)&&e.endsWith(`'`))?e.slice(1,-1):e}function Po(e){if(e!==void 0){if(e===`true`)return!0;if(e===`false`)return!1}}const Fo=Object.keys(H);function Io(e,t){if(e===void 0||Fo.includes(e))return e;throw Error(`agent def "${t}" has unknown harness "${e}" — expected one of ${Fo.join(`, `)}`)}function Lo(e,t,n){let{fm:r,instructions:i}=Mo(t);return{name:r.name??n,description:r.description,model:r.model,effort:r.effort,harness:Io(r.harness,e),warm:Po(r.warm),interactive:Po(r.interactive),instructions:i,path:e}}function Ro(e){return h(Ii(e),`.agents`,`agents`)}function zo(e){if(e.file){let t=g(e.file);if(!n(t))throw Error(`agent def file "${e.file}" does not exist`);return Lo(t,i(t,`utf8`),f(t).replace(/\.md$/,``))}if(!e.name)throw Error(`resolveAgentDef needs a --agent <name> or --agent-file <path>`);let t=[...e.searchRoots??[],Ro(e.cwd)];for(let r of t){let t=h(r,`${e.name}.md`);if(n(t))return Lo(t,i(t,`utf8`),e.name)}throw Error(`no agent definition named "${e.name}" found under .agents/agents/`)}function Bo(e={}){let t=[...e.searchRoots??[],Ro(e.cwd)],r=/* @__PURE__ */ new Set,o=[];for(let e of t)if(n(e))for(let t of a(e)){if(!t.endsWith(`.md`))continue;let n=h(e,t);r.has(n)||(r.add(n),o.push(Lo(n,i(n,`utf8`),t.replace(/\.md$/,``))))}return o}function Vo(e){return`'${e.replace(/'/g,`'\\''`)}'`}function Ho(e,t,n=()=>[]){if(!e||!t)return{model:e,effortApplied:!t};let r=n(),i=`-${t}`;if(e.endsWith(i)&&r.includes(e))return{model:e,effortApplied:!0};let a=`${e}${i}`;return r.includes(a)?{model:a,effortApplied:!0}:{model:e,effortApplied:!1}}function Uo(e,t,n){let r=t?[`--model`,Vo(t)]:[];return!n||e===`cursor`?r:e===`codex`?[...r,`-c`,Vo(`model_reasoning_effort="${n}"`)]:[...r,`--effort`,Vo(n)]}function Wo(e){return JSON.stringify(e).replace(/\x7f/g,`\\u007f`)}function Go(e,t){return!t||e===`cursor`?[]:e===`claude`?[`--append-system-prompt`,Vo(t)]:[`-c`,Vo(`developer_instructions=${Wo(t)}`)]}function Ko(e,t={}){let n=t.harness??e.harness??`claude`,r=t.effort??e.effort,i=t.model??e.model,a=n===`cursor`?Ho(i,r,t.listCursorModels):void 0,o=a?a.model:i,s=[H[n],...Uo(n,o,r),...Go(n,e.instructions)],c=n===`cursor`&&e.instructions?e.instructions:void 0,l=a&&!a.effortApplied?r:void 0;return{harness:n,command:s.join(` `),model:o,...l?{effortNotApplied:l}:{effort:r},...c?{briefInstructions:c}:{}}}function qo(e){let t={harness:e.harness,model:e.model,effort:e.effort,listCursorModels:e.listCursorModels};return!e.agent&&!e.agentFile?!e.harness||!e.model&&!e.effort?{harness:e.harness}:Ko({name:e.harness,instructions:``,path:``},t):Ko(zo({name:e.agent,file:e.agentFile,...e.cwd?{cwd:e.cwd}:{},...e.searchRoots?{searchRoots:e.searchRoots}:{}}),t)}function Jo(e,t){let{harness:n,command:r,model:i,effort:a,effortNotApplied:o,briefInstructions:s}=qo({agent:e.agent,agentFile:e.agentFile,harness:e.harness,model:e.model,effort:e.effort,listCursorModels:t});if(!n)throw Error(`unit spawn needs --harness, or --agent/--agent-file resolving one`);return{input:{harness:n,command:r,briefInstructions:s,task:e.task,briefFile:e.briefFile,handle:e.handle,branch:e.branch,worktreePath:e.worktreePath,cwd:e.cwd,repo:e.repo,base:e.base,at:e.at},noWake:e.wake===!1,launched:{model:i,effort:a,effortNotApplied:o}}}function Yo(e,t){return e===`json`?JSON.stringify(t,null,2):t.output}function Xo(e){return te(e).split(`
|
|
65
|
-
`).map(e=>e.trim().replace(/^[-*•]\s+/,``).split(/\s+/)[0]??``).filter(e=>e!==``)}function Zo(){try{return Xo(b(`cursor-agent`,[`models`],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`],timeout:15e3}))}catch{return[]}}function Qo(e,t){let n=la(e.store,t.to),r=F(e.store,t.fromId),i=e.now?.()??Date.now(),a={id:`${i}-${ie(3).toString(`hex`)}`,from:t.fromId,fromHandle:r?.handle??t.fromId,to:n,...t.subject?{subject:t.subject}:{},body:t.body,...t.thread?{thread:t.thread}:{},...t.replyTo?{replyTo:t.replyTo}:{},ts:i,sentAt:new Date(i).toISOString()};return e.store.putMessage(n,a),a}function $o(e,t,n=()=>i(0,`utf8`)){if(e!=null)return e;if(t)return t===`-`?n():i(t,`utf8`);throw Error(`provide --body <text> or --body-file <path|->`)}function es(e,t){let n=e.store.listInbox(t.meId),r=n.unread.map(e=>({...e,read:!1})),i=t.unread?[]:n.read.map(e=>({...e,read:!0})),a=[...r,...i];return t.from&&(a=a.filter(e=>e.from===t.from||e.fromHandle===t.from)),t.thread&&(a=a.filter(e=>e.thread===t.thread)),a.sort((e,t)=>e.id.localeCompare(t.id))}function ts(e,t,n){let r=e.store.listInbox(t);return[...r.unread,...r.read].find(e=>e.id===n)}function ns(e,t,n){return e.store.ackMessage(t,n)}function rs(e,t,n){let r=e.store.listInbox(t);if(r.unread.some(e=>e.id===n))return{msg:e.store.ackMessage(t,n),acked:!0};let i=r.read.find(e=>e.id===n);if(i)return{msg:i,acked:!1};throw Error(`"${n}" is not a message in this inbox`)}function is(e,t,n){e.store.removeMessage(t,n)}function as(e,t){e.removeMailbox(t)}async function os(e,t){let r=F(e.store,t.id);if(!r)throw Error(`no unit registered as agents/${t.id}.json — nothing to decommission`);if(r.kind===`service`)throw Error(`refusing to decommission "${t.id}" — it is a service endpoint, not a unit`);let i=e.exec??P,a=e.env??process.env,o=r.worktree?.root,s=o!=null&&n(o),c=s?await ss(i,o):r.worktree?.primaryRoot,l=r.worktree?.lease
|
|
65
|
+
`).map(e=>e.trim().replace(/^[-*•]\s+/,``).split(/\s+/)[0]??``).filter(e=>e!==``)}function Zo(){try{return Xo(b(`cursor-agent`,[`models`],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`],timeout:15e3}))}catch{return[]}}function Qo(e,t){let n=la(e.store,t.to),r=F(e.store,t.fromId),i=e.now?.()??Date.now(),a={id:`${i}-${ie(3).toString(`hex`)}`,from:t.fromId,fromHandle:r?.handle??t.fromId,to:n,...t.subject?{subject:t.subject}:{},body:t.body,...t.thread?{thread:t.thread}:{},...t.replyTo?{replyTo:t.replyTo}:{},ts:i,sentAt:new Date(i).toISOString()};return e.store.putMessage(n,a),a}function $o(e,t,n=()=>i(0,`utf8`)){if(e!=null)return e;if(t)return t===`-`?n():i(t,`utf8`);throw Error(`provide --body <text> or --body-file <path|->`)}function es(e,t){let n=e.store.listInbox(t.meId),r=n.unread.map(e=>({...e,read:!1})),i=t.unread?[]:n.read.map(e=>({...e,read:!0})),a=[...r,...i];return t.from&&(a=a.filter(e=>e.from===t.from||e.fromHandle===t.from)),t.thread&&(a=a.filter(e=>e.thread===t.thread)),a.sort((e,t)=>e.id.localeCompare(t.id))}function ts(e,t,n){let r=e.store.listInbox(t);return[...r.unread,...r.read].find(e=>e.id===n)}function ns(e,t,n){return e.store.ackMessage(t,n)}function rs(e,t,n){let r=e.store.listInbox(t);if(r.unread.some(e=>e.id===n))return{msg:e.store.ackMessage(t,n),acked:!0};let i=r.read.find(e=>e.id===n);if(i)return{msg:i,acked:!1};throw Error(`"${n}" is not a message in this inbox`)}function is(e,t,n){e.store.removeMessage(t,n)}function as(e,t){e.removeMailbox(t)}async function os(e,t){let r=F(e.store,t.id);if(!r)throw Error(`no unit registered as agents/${t.id}.json — nothing to decommission`);if(r.kind===`service`)throw Error(`refusing to decommission "${t.id}" — it is a service endpoint, not a unit`);let i=e.exec??P,a=e.env??process.env,o=r.worktree?.root,s=o!=null&&n(o),c=s?await ss(i,o):r.worktree?.primaryRoot,l=r.worktree?.lease,u=o!=null&&c!=null&&g(o)===g(c);if(u&&t.force)throw Error(`refusing to close "${t.id}" with --force — its worktree is the primary checkout, which close never alters; close it without --force to end its session and reap its record`);let d=s&&!t.keepWorktree&&!l&&!u;if(d&&!t.force&&cs(i,o))throw Error(`unit "${t.id}" has uncommitted changes in its worktree — pass --force to discard them`);let f=r.pane?.id??e.store.findPaneByAgentId(t.id);o&&!s&&c&&n(c)&&i(`git`,[`-C`,c,`worktree`,`prune`]);let p;if(l&&o&&(p=await to.release({worktree:o,...l},{exec:no(i)})),d)try{ji.remove(i,o,{primaryRoot:c})}catch(e){throw Error(`decommission "${t.id}" aborted — worktree removal failed, record left intact for retry: ${e instanceof Error?e.message:String(e)}`)}if(f)try{V(a,i).teardown(i,{id:f})}catch{}return e.store.removeAgent(t.id),f&&e.store.removePaneIndex(f),e.store.removeAgentData(t.id),as(e.store,t.id),{agent:r,worktreeRoot:o,retainedWorktree:s&&!u&&(t.keepWorktree||l)?o:void 0,...p?{lease:p}:{},pane:f}}async function ss(e,t){try{return await zr({from:t,exec:no(e)})}catch{throw Error(`cannot resolve the repository of worktree ${t} — git does not know it`)}}function cs(e,t){let n=e(`git`,[`-C`,t,`status`,`--porcelain`,`--untracked-files=all`]);return!!n&&n.split(`
|
|
66
66
|
`).some(e=>e&&e!==`?? .agents/cyberlegion/config.json`)}const ls=/^[0-9A-Za-z][0-9A-Za-z._+-]*$/;function us(e){if(!ls.test(e))throw Error(`invalid --pin "${e}" — expected a version or dist-tag token like 0.2.0 or latest (no spaces, ranges, or shell metacharacters)`)}const ds=(e,t)=>`if command -v cyberlegion >/dev/null 2>&1; then cyberlegion mail hook --event ${e}; else npx -y cyberlegion${t?`@${t}`:``} mail hook --event ${e}; fi`,fs=[/^(?:npx (?:-y )?cyberlegion(?:@\S+)?|cyberlegion) mail hook --event (\w+)$/,/^if command -v cyberlegion >\/dev\/null 2>&1; then cyberlegion mail hook --event (\w+); else npx -y cyberlegion(?:@\S+)? mail hook --event \1; fi$/];function ps(e){for(let t of fs){let n=e.match(t)?.[1];if(n)return n}}const ms={claude:{file:`.claude/settings.json`,shape:`claude`,events:{SessionStart:`SessionStart`},pluginHook:!0},cursor:{file:`.cursor/hooks.json`,shape:`cursor`,events:{SessionStart:`sessionStart`},pluginHook:!1},codex:{file:`.codex/hooks.json`,shape:`cursor`,events:{SessionStart:`SessionStart`},pluginHook:!0}},hs=[`PostToolUse`];function gs(e){if(!n(e))return{};try{return JSON.parse(i(e,`utf8`))}catch{return{}}}function _s(e,t){r(p(e),{recursive:!0}),u(e,`${JSON.stringify(t,null,2)}\n`)}function vs(e,t=process.cwd(),n){let r=ms[e];if(!r)throw Error(`unknown harness "${e}" (expected claude | cursor | codex)`);n!==void 0&&us(n);let i=h(t,r.file),a=gs(i),o=[],s=!1;for(let[t,c]of Object.entries(r.events)){let l;if(r.pluginHook){let e=r.shape===`claude`?Ss(a,c,t):Cs(a,c,t);l=e?`removed project hook`:`provided by plugin`,s||=e}else{let e=ds(t,n);l=r.shape===`claude`?ys(a,c,e):bs(a,c,e),s=!0}o.push({harness:e,event:t,vendorEvent:c,file:i,status:l})}if(r.pluginHook)for(let t of hs)(r.shape===`claude`?Ss(a,t,t):Cs(a,t,t))&&(s=!0,o.push({harness:e,event:t,vendorEvent:t,file:i,status:`removed project hook`}));return s&&_s(i,a),o}function ys(e,t,n){let r=e.hooks??={},i=r[t]??=[],a=ps(n);for(let e of i)for(let t of e.hooks??[]){if(t.command===n)return`already present`;if(a&&ps(t.command)===a)return t.command=n,`already present`}return i.push({hooks:[{type:`command`,command:n}]}),`registered`}function bs(e,t,n){e.version??=1;let r=e.hooks??={},i=r[t]??=[],a=ps(n);for(let e of i){if(e.command===n)return`already present`;if(a&&ps(e.command)===a)return e.command=n,`already present`}return i.push({command:n}),`registered`}const xs=(e,t)=>e!==void 0&&ps(e)===t;function Ss(e,t,n){let r=e.hooks,i=r?.[t];if(!r||!i)return!1;let a=!1,o=[];for(let e of i){let t=(e.hooks??[]).filter(e=>!xs(e.command,n));t.length!==(e.hooks??[]).length&&(a=!0),t.length>0&&o.push({...e,hooks:t})}return a?(o.length>0?r[t]=o:delete r[t],!0):!1}function Cs(e,t,n){let r=e.hooks,i=r?.[t];if(!r||!i)return!1;let a=i.filter(e=>!xs(e.command,n));return a.length!==i.length&&(a.length>0?r[t]=a:delete r[t],!0)}function ws(e){if(e==null)return``;let t=String(e);return/[,\n"]/.test(t)?`"${t.replace(/"/g,`""`)}"`:t}function W(e){return Object.entries(e).filter(([,e])=>e!=null).map(([e,t])=>`${e}: ${t}`).join(`
|
|
67
67
|
`)}function Ts(e,t,n,r){return[`${e}[${t.length}]{${n.map(e=>e.key).join(`,`)}}:`,...t.map(e=>n.map(t=>ws(t.get(e))).join(`,`)).map(e=>` ${e}`),r].join(`
|
|
68
68
|
`)}function G(e,t){console.log(e===`json`?JSON.stringify(t.json,null,2):t.toon)}function K(e){console.error(`→ ${e}`)}function q(e){console.error(JSON.stringify({error:e})),process.exit(1)}const Es=`cyberlegion:unit/`;function Ds(e){return`cyberlegion:hub/${re(`sha256`).update(e).digest(`hex`).slice(0,16)}`}function Os(e){return`${Es}${e}`}function ks(e){return e.kind===void 0||e.kind===`session`}function As(e,t){let n={registered:[],retired:[],renamed:[]},r=e.registerParticipant({key:Ds(t.root),kind:`service`,name:`cyberlegion`}).participant.id,i=/* @__PURE__ */ new Map;for(let t of e.participants({registeredBy:r}))t.key?.startsWith(Es)&&i.set(t.key.slice(17),t);let a=new Map(t.listAgents().filter(ks).map(e=>[e.id,e]));for(let t of a.values()){if(t.status===`exited`)continue;let a=i.get(t.id);(!a||a.status===`retired`)&&(a=e.registerParticipant({key:Os(t.id),kind:`agent`,name:t.handle,registeredBy:r}).participant,n.registered.push(t.id)),a.name!==t.handle&&(e.renameParticipant(a.id,t.handle,r),n.renamed.push(t.id))}for(let[t,o]of i){if(o.status!==`live`)continue;let i=a.get(t);i&&i.status!==`exited`||(e.retireParticipant(o.id,r),n.retired.push(t))}return n}function js(e,t){let n=e(`git`,[`-C`,t,`remote`,`get-url`,`origin`]);if(!n)return;let r=e(`gh`,[`repo`,`view`,n,`--json`,`id`,`--jq`,`.id`]);return r?{store:`gh`,nativeId:r}:void 0}async function Ms(e,t,n,r=process.env,i=Ns){let a;try{if(a=await Ps(r,i),!a)return;let o=t.subject;if(!o){if(o=js(n,t.root),!o)return;e.putProject({...t,subject:o})}let s=a.getChannelBySubject(o);return{subject:o,...s?{channel:{id:s.id,handle:s.handle}}:{}}}catch(e){console.error(`cynapse: project address skipped — ${e instanceof Error?e.message:String(e)}`);return}finally{a?.close()}}const Ns=async()=>{let{silenceSqliteWarning:e}=await import(`cynapse/sqlite-warning`);return e(),await import(`cynapse`)};async function Ps(e=process.env,t=Ns){let n;try{n=await t()}catch{return}return n.openStore({path:n.resolveDbPath(e)})}async function Fs(e,t=process.env,n=Ns){let r;try{if(r=await Ps(t,n),!r)return;let i=r;return e.withLock(`cynapse-participants`,()=>As(i,e))}catch(e){console.error(`cynapse: participant sync skipped — ${e instanceof Error?e.message:String(e)}`);return}finally{r?.close()}}const Is=`Bash(cyberlegion *)`,Ls=/^Bash(?:\((?:\*|cyberlegion(?: \*|:\*|\*))\))?$/;function Rs(e=process.env){return h(e.CLAUDE_CONFIG_DIR||h(ne(),`.claude`),`settings.json`)}function zs(e){if(!n(e))return{settings:{},allow:[]};let t;try{t=JSON.parse(i(e,`utf8`))}catch{throw Error(`cannot parse Claude Code settings ${e} — fix it by hand, then re-run`)}if(typeof t!=`object`||!t||Array.isArray(t))throw Error(`Claude Code settings ${e} is not a JSON object — fix it by hand, then re-run`);let{permissions:r}=t,a=r?.allow??[];if(r!==void 0&&(typeof r!=`object`||!r)||!Array.isArray(a))throw Error(`Claude Code settings ${e} has a malformed permissions.allow — fix it by hand, then re-run`);return{settings:t,allow:a}}const Bs=e=>e.some(e=>typeof e==`string`&&Ls.test(e));function Vs(e){try{return Bs(zs(e).allow)?`present`:`missing`}catch{return`unreadable`}}function Hs(e){let{settings:t,allow:n}=zs(e);return Bs(n)?`present`:(t.permissions={...t.permissions??{},allow:[...n,Is]},r(p(e),{recursive:!0}),u(e,`${JSON.stringify(t,null,2)}\n`),`added`)}function Us(e){return[`You stand in for the standing owner "${e}": you are its presence, spawned because mail was delivered to it and no unit was standing in.`,``,`Read its mail: cyberlegion mail inbox --owner ${e}, then cyberlegion mail read --owner ${e} <id>.`,`Ack what you have handled: cyberlegion mail ack --owner ${e} <id>.`].join(`
|
|
@@ -70,7 +70,7 @@ Expecting one of '${n.join(`', '`)}'`);let r=`${e}Help`;return this.on(r,e=>{let
|
|
|
70
70
|
`)}`)}let o=Ae(N(e.env??process.env));if(i&&!i.spawnedBy)try{let t=e.store.getMainPane();if(!t||o?.pane===t){let t=L(e.store).filter(e=>e.kind===`standing`);for(let n of t){let t=es({store:e.store},{meId:n.id,unread:!0});if(t.length===0)continue;let i=t.map(e=>`- **${e.fromHandle}**${e.subject?` — ${e.subject}`:``}: ${e.body} \`(${e.id})\``);r.push(`## Owner mail — ${n.handle} (${t.length})\n\n${i.join(`
|
|
71
71
|
`)}`)}}}catch{}return r.length===0?null:{hookSpecificOutput:{hookEventName:t,additionalContext:r.join(`
|
|
72
72
|
|
|
73
|
-
`)}}}var rc=class extends Error{code;constructor(e,t){super(t),this.code=e,this.name=`ServiceOwnershipError`}};const ic=/^[a-z0-9][a-z0-9_-]{0,62}$/;function ac(e){if(!ic.test(e))throw Error(`invalid service name "${e}" — use lowercase letters, digits, "-" or "_" (max 63)`)}function oc(e,t){return`svc-${e}-${t}`}const sc=e=>e.now?.()??Date.now(),cc=e=>new Date(e).toISOString();function lc(e,t){return`service-${e}-${t}`}function uc(e,t){return t.status===`exited`?!1:e.isLive?e.isLive(t):ha(e,t)}function dc(e,t,n){let r=oc(t.id,n),i=F(e.store,r);if(i)return i;e.store.ensureMarker();let a=cc(sc(e)),o={id:r,handle:`${n}@${t.name}`,kind:`service`,service:{project:t.id,name:n},cwd:t.root,pane:null,status:`active`,createdAt:a,lastSeen:a};return I(e.store,o),o}function fc(e,t,n,r){let i={project:t,endpoint:n,lease:r};if(r.state===`vacant`)return{...i,health:`vacant`,control:`none`};if(r.state===`reserved`){let t=sc(e)>Date.parse(r.reservation?.expiresAt??``);return{...i,health:t?`expired`:`starting`,control:`none`,...t?{note:`the reservation expired without a bound owner; the next acquire takes over`}:{}}}let a=r.holder?F(e.store,r.holder):void 0;if(!a)return{...i,health:`unhealthy`,control:`none`,note:`the owner has no unit record`};let o=a.pane||e.store.findPaneByAgentId(a.id)?`pane`:`none`,s=o===`none`?`the owner resolves, but its session control is not recoverable from here (no multiplexer pane — e.g. a native subagent only its parent can drive)`:void 0;return uc(e,a)?{...i,owner:a,health:`healthy`,control:o,...s?{note:s}:{}}:{...i,owner:a,health:`unhealthy`,control:o,note:`the owner session is gone`}}function pc(e,t,n){return{project:t,service:n,endpoint:oc(t,n),generation:0,state:`vacant`,updatedAt:cc(sc(e))}}function mc(e,t,n,r){ac(n);let i=$s(e,t),a=e.store.getServiceLease(i.id,n),o=F(e.store,oc(i.id,n));if(a&&o)return{project:i,endpoint:o,lease:a};if(!r)throw Error(`no service "${n}" in project ${i.name} (${i.id})`);return{project:i,endpoint:dc(e,i,n),lease:a??pc(e,i.id,n)}}function hc(e,t,n,r,i){ac(n);let a=$s(e,t);return e.store.withLock(lc(a.id,n),()=>i(mc(e,a.id,n,r)))}function gc(e,t){let n={...t,updatedAt:cc(sc(e))};return e.store.putServiceLease(n),n}function _c(e){return new rc(`stale`,e)}function vc(e,t,n){let{project:r,endpoint:i,lease:a}=mc(e,t,n,!1);return fc(e,r,i,a)}function yc(e,t,n,r={}){return hc(e,t,n,!0,({project:t,endpoint:i,lease:a})=>{let o=fc(e,t,i,a);if(r.force){if(r.force.generation!==a.generation)throw _c(`cannot force: generation ${r.force.generation} is not the current ${a.generation}`)}else if(o.health===`healthy`)return{outcome:`resolved`,lease:a,view:o};else if(o.health===`starting`)return{outcome:`starting`,lease:a,view:o};let s=sc(e),c=ie(8).toString(`hex`),l=gc(e,{project:t.id,service:n,endpoint:i.id,generation:a.generation+1,state:`reserved`,reservation:{token:c,...r.by?{by:r.by}:{},at:cc(s),expiresAt:cc(s+(r.ttlMs??3e5)),...r.force?{forced:!0}:{}},updatedAt:cc(s)});return{outcome:`reserved`,lease:l,view:fc(e,t,i,l),token:c}})}function bc(e,t){let n=F(e.store,t);if(!n)throw new rc(`unknown-unit`,`no unit "${t}"`);if(!uc(e,n))throw new rc(`unit-not-live`,`unit "${t}" has no live session`);return n}function xc(e,t,n,r){return hc(e,t,n,!1,({project:t,endpoint:n,lease:i})=>{if(i.state!==`reserved`||i.generation!==r.generation||i.reservation?.token!==r.token)throw _c(`reservation for generation ${r.generation} is no longer current (now ${i.state} at ${i.generation})`);return bc(e,r.unit),fc(e,t,n,gc(e,{project:i.project,service:i.service,endpoint:i.endpoint,generation:i.generation,state:`active`,holder:r.unit,updatedAt:i.updatedAt}))})}function Sc(e,t,n,r){return hc(e,t,n,!1,({project:t,endpoint:n,lease:i})=>{if(!(i.generation===r.generation&&(i.state===`reserved`&&r.token!==void 0&&i.reservation?.token===r.token||i.state===`active`&&r.unit!==void 0&&i.holder===r.unit)))throw _c(`nothing to release at generation ${r.generation} (now ${i.state} at ${i.generation})`);return fc(e,t,n,gc(e,{project:i.project,service:i.service,endpoint:i.endpoint,generation:i.generation,state:`vacant`,updatedAt:i.updatedAt}))})}function Cc(e,t,n,r){return hc(e,t,n,!1,({project:t,endpoint:n,lease:i})=>{if(i.state!==`active`||i.generation!==r.generation||i.holder!==r.from)throw _c(`"${r.from}" is not the owner at generation ${r.generation}`);return bc(e,r.to),fc(e,t,n,gc(e,{...i,generation:i.generation+1,holder:r.to}))})}function wc(e,t,n,r){let{project:i,endpoint:a,lease:o}=mc(e,t,n,!1);if(o.state!==`active`||o.holder!==r.unit||o.generation!==r.generation)throw _c(`"${r.unit}" at generation ${r.generation} is not the owner (now ${o.state}${o.holder?` by ${o.holder}`:``} at ${o.generation})`);return fc(e,i,a,o)}async function Tc(e,t,n,r){let i=yc(e,t,n,r);if(i.outcome!==`reserved`)return{outcome:i.outcome,view:i.view};let{generation:a,endpoint:o}=i.lease,s;try{s=await r.launch({generation:a,endpoint:o})}catch(r){try{Sc(e,t,n,{generation:a,token:i.token})}catch{}throw r}try{return{outcome:`started`,view:xc(e,t,n,{generation:a,token:i.token,unit:s.unit}),launched:s}}catch(e){throw e instanceof rc?new rc(e.code,`launched unit "${s.unit}" could not become the owner — ${e.message}; stop it`):e}}function Ec(e,t){if((t.home!==void 0||t.clearHome===!0||t.agent!==void 0)&&!(t.standing&&t.handle))throw Error(`a home belongs only to a named standing owner — pass --standing --handle <name>`);if(!t.standing)return;if(t.home!==void 0&&t.clearHome)throw Error(`--home and --clear-home contradict — pass one`);if(t.home===void 0&&(t.agent!==void 0||t.harness!==void 0))throw Error(`a launch needs --home: --agent and --harness name how to start a session in it`);if(t.clearHome)return null;if(t.home===void 0)return;let r=g(t.home);if(!n(r)||!l(r).isDirectory())throw Error(`the home must already exist: ${t.home}`);if(t.agent===void 0==(t.harness===void 0))throw Error(`a home needs exactly one of --agent or --harness`);let i;if(t.harness!==void 0){if(!(t.harness in H))throw Error(`unrecognized --harness "${t.harness}" (${Object.keys(H).join(` | `)})`);i={harness:t.harness}}else{let e=t.agent;try{zo({name:e,cwd:r})}catch{throw Error(`the agent definition "${e}" does not resolve from the home ${r}`)}i={agent:e}}let a=lo(e,r);if(a!==void 0&&o(a)===o(r))throw Error(`the home refuses the primary checkout ${r} — a spawned unit never works there`);return{dir:r,...i}}var Dc=class extends Error{file;constructor(e,t){super(`corrupt record at "${e}": ${t instanceof Error?t.message:String(t)}`,{cause:t}),this.file=e,this.name=`CorruptRecordError`}};function Oc(e){if(e===process.pid)return`alive`;try{return process.kill(e,0),`alive`}catch(e){return e.code===`ESRCH`?`dead`:`unknown`}}var kc=class extends Error{name;constructor(e){super(`timed out waiting for lock "${e}"`),this.name=e,this.name=`LockTimeoutError`}};function Ac(e){return h(e,`locks`)}function jc(e,t){return h(Ac(e),`${t}.lock`)}function Mc(e){try{return JSON.parse(i(h(e,`holder.json`),`utf8`))}catch{return}}function Nc(e){return Oc(e)!==`dead`}function Pc(e){e<=0||Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,e)}function Fc(e){let t=`${e}.stale-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;try{s(e,t)}catch(e){if(e.code===`ENOENT`)return!1;throw e}let n=Mc(t);if(n&&Nc(n.pid)){try{s(t,e)}catch{c(t,{recursive:!0,force:!0})}return!1}return c(t,{recursive:!0,force:!0}),!0}function Ic(e,t,n={}){let i=n.retryDelayMs??20,a=n.timeoutMs??5e3,o=jc(e,t);r(Ac(e),{recursive:!0});let s=Date.now()+a;for(;;)try{return r(o),u(h(o,`holder.json`),JSON.stringify({pid:process.pid,acquiredAt:Date.now()})),{release:()=>c(o,{recursive:!0,force:!0})}}catch(e){if(e.code!==`EEXIST`)throw e;let n=Mc(o);if(n&&!Nc(n.pid)&&Fc(o))continue;if(Date.now()>s)throw new kc(t);Pc(i)}}function Lc(e,t,n,r){let i=Ic(e,t,r);try{return n()}finally{i.release()}}async function Rc(e,t,n,r){let i=Ic(e,t,r);try{return await n()}finally{i.release()}}function zc(e){let t=i(e,`utf8`);try{return JSON.parse(t)}catch(t){throw new Dc(e,t)}}function Bc(e){return n(e)?a(e).filter(e=>e.endsWith(`.json`)).map(t=>zc(h(e,t))):[]}let Vc=0;function Hc(e,t){r(p(e),{recursive:!0});let n=`${e}.${process.pid}.${Vc++}.tmp`;u(n,t),s(n,e)}function Uc(e,t){Hc(e,`${JSON.stringify(t,null,2)}\n`)}function Wc(e,t){Hc(e,t)}function J(e){try{return e()}catch(e){if(e instanceof zi)return;throw e}}var Gc=class{root;constructor(e){this.root=e}ensureMarker(){Ri(this.root)}putMessage(e,t){Hc(M.messageFile(this.root,e,t.id),`${JSON.stringify(t,null,2)}\n`)}listInbox(e){let t=J(()=>M.inboxDir(this.root,e)),n=J(()=>M.inboxReadDir(this.root,e));return{unread:t?Bc(t):[],read:n?Bc(n):[]}}ackMessage(e,t){let i=J(()=>M.messageFile(this.root,e,t));if(!i||!n(i))throw Error(`"${t}" is not an unread message in this inbox`);let a=zc(i),o=M.messageReadFile(this.root,e,t);return r(p(o),{recursive:!0}),s(i,o),a}removeMessage(e,t){let r=J(()=>M.messageFile(this.root,e,t));if(r&&n(r)){c(r);return}let i=J(()=>M.messageReadFile(this.root,e,t));if(i&&n(i))c(i);else throw Error(`"${t}" is not a message in this inbox`)}removeMailbox(e){c(M.inboxDir(this.root,e),{recursive:!0,force:!0})}putAgent(e){Uc(M.agentFile(this.root,e.id),e)}getAgent(e){let t=J(()=>M.agentFile(this.root,e));if(t&&n(t))return zc(t)}listAgents(){let e=M.agentsDir(this.root);return n(e)?a(e).filter(e=>e.endsWith(`.json`)).map(t=>zc(h(e,t))).sort((e,t)=>e.createdAt.localeCompare(t.createdAt)):[]}removeAgent(e){c(M.agentFile(this.root,e),{force:!0})}removeAgentData(e){c(M.dataDir(this.root,e),{recursive:!0,force:!0})}putProject(e){Uc(M.projectFile(this.root,e.id),e)}getProject(e){let t=J(()=>M.projectFile(this.root,e));if(t&&n(t))return zc(t)}listProjects(){let e=M.projectsDir(this.root);return n(e)?a(e).filter(e=>e.endsWith(`.json`)).map(t=>zc(h(e,t))).sort((e,t)=>e.registeredAt.localeCompare(t.registeredAt)):[]}putServiceLease(e){Uc(M.serviceLeaseFile(this.root,e.project,e.service),e)}getServiceLease(e,t){let r=J(()=>M.serviceLeaseFile(this.root,e,t));if(r&&n(r))return zc(r)}putPaneIndex(e,t){Wc(M.paneFile(this.root,e),t)}resolvePaneId(e){let t=M.paneFile(this.root,e);return n(t)?i(t,`utf8`).trim():void 0}findPaneByAgentId(e){let t=M.panesDir(this.root);if(n(t)){for(let n of a(t))if(n.endsWith(`.id`)&&i(h(t,n),`utf8`).trim()===e)return n.slice(0,-3)}}removePaneIndex(e){c(M.paneFile(this.root,e),{force:!0})}writeBrief(e,t){Wc(M.briefFile(this.root,e),t)}readBrief(e){let t=J(()=>M.briefFile(this.root,e));return t&&n(t)?i(t,`utf8`):void 0}setMainPane(e){Lc(this.root,`main-pane`,()=>{let t=M.mainPaneFile(this.root);e?Wc(t,e):c(t,{force:!0})})}getMainPane(){let e=M.mainPaneFile(this.root);return n(e)?i(e,`utf8`).trim():void 0}withLock(e,t){return Lc(this.root,e,t)}withLockAsync(e,t){return Rc(this.root,e,t)}};function Kc(e,t){if(e.kind===`standing`||e.kind===`service`)throw Error(`"${t}" is a ${e.kind} record — it has no runtime to stop, restart, or rebind`)}function qc(e,t,n){if(R(e)===t.id)throw Error(`a unit cannot ${n} its own session — run unit ${n} ${t.handle} from another session`)}function Jc(e,t){return t.pane?.id??e.store.findPaneByAgentId(t.id)}function Yc(e,t){if(e.adapter)return e.adapter;if(t.pane?.mux===`tmux`)return D;if(t.pane?.mux===`herdr`)return T;try{return V(e.env??process.env,e.exec??P)}catch{return}}function Xc(e,t,n){let r={...t,status:`stopped`,pane:null};return I(e.store,r),n&&e.store.removePaneIndex(n),r}function Zc(e,t){let n=Jc(e,t);if(!n)return{agent:Xc(e,t,void 0),verified:!1};let r=e.exec??P,i=Yc(e,t);if(!i)return{agent:Xc(e,t,n),pane:n,verified:!1};try{i.teardown(r,{id:n})}catch{}let a=i.listPanes(r);if(a.some(e=>e.id===n))throw Error(`stop did not take effect — the backend still lists pane ${n} for unit "${t.handle}"; its record is unchanged`);return{agent:Xc(e,t,n),pane:n,verified:a.length>0}}function Qc(e,t){let n=z(e.store,t);return Kc(n,t),qc(e,n,`stop`),n.status===`stopped`?{agent:n,verified:!0,alreadyStopped:!0}:{...Zc(e,n),alreadyStopped:!1}}async function $c(e,t,r={}){let i=z(e.store,t);Kc(i,t),qc(e,i,`restart`);let a=i.harness;if(!a||!(a in H))throw Error(`unit "${i.handle}" has harness "${i.harness??``}", not in the launch map (${Object.keys(H).join(` | `)}) — cannot restart it`);if(!n(i.cwd))throw Error(`unit "${i.handle}" cannot restart — its cwd ${i.cwd} is gone; spawn a new unit instead`);let o=i,s;if(i.status!==`stopped`){let t=Zc(e,i);o=t.agent,s=t.pane}let c=e.env??process.env,l=e.exec??P,u=o.launch??H[a],d=o.conversation&&!r.fresh?bo(a,u,o.conversation):void 0,f=o.worktree?`workspace`:`tab`,p,m;try{m=e.adapter??V(c,l);let t=f===`workspace`?{label:Za({brief:e.store.readBrief(i.id)??``,handle:i.handle,id:i.id})}:{};p=m.open(l,{cwd:o.cwd,launch:d?vo(e,m.name,i.id,d,{bindSelf:!0,fallback:u}):vo(e,m.name,i.id,u,{bindSelf:!0}),at:f,from:Ar(m,N(c)),...t})}catch(e){throw Error(`restart could not open a session — unit "${i.handle}" is stopped, with its inbox, brief, and worktree kept; rerun unit restart ${i.handle}: ${e instanceof Error?e.message:String(e)}`)}let h=m.name,g={...o,status:`active`,pane:h===`tmux`||h===`herdr`?{mux:h,id:p.id}:null,lastSeen:new Date(e.now?.()??Date.now()).toISOString()};I(e.store,g),e.store.putPaneIndex(p.id,i.id);let _=g.brief,v=await Va(()=>m,l,{target:p,briefPath:_??``,noWake:r.noWake||!_,resumed:!!d},r.nudgeOpts);return{agent:g,...s?{previousPane:s}:{},pane:p.id,launch:u,resumed:!!d,rung:v.rung,...v.warning?{warning:v.warning}:{}}}function el(e,t){if(t.kind===`standing`||t.kind===`service`)return`none`;if(t.status===`stopped`)return`stopped`;if(t.status===`exited`)return`exited`;let n=Jc(e,t),r=n?Yc(e,t):void 0;if(!n||!r)return`unknown`;let i=r.listPanes(e.exec??P);return i.length===0?`unknown`:i.some(e=>e.id===n)?`live`:`gone`}function tl(e,t){let n=z(e.store,t);Kc(n,t);let r=Hi(e.env??process.env);if(!r)throw Error(`unit rebind needs to run inside a tmux or herdr pane — there is no pane to bind`);let i=Jc(e,n);if(n.pane?.mux===r.mux&&n.pane.id===r.pane&&e.store.resolvePaneId(r.pane)===n.id)return n;let a=e.store.resolvePaneId(r.pane),o=a&&a!==n.id?F(e.store,a):void 0;if(o&&o.status!==`stopped`&&o.status!==`exited`)throw Error(`this pane belongs to unit "${o.handle}" (${o.id}) — rebind never takes another unit's pane`);let s=el(e,n);if(i&&i!==r.pane&&(s===`live`||s===`unknown`))throw Error(`unit "${n.handle}" may still be running in pane ${i} — stop it first (unit stop ${n.handle})`);let c={...n,status:`active`,pane:{mux:r.mux,id:r.pane},lastSeen:new Date(e.now?.()??Date.now()).toISOString()};return I(e.store,c),i&&i!==r.pane&&e.store.removePaneIndex(i),e.store.putPaneIndex(r.pane,n.id),c}function nl(e){if(!e)return!1;try{return so(e),!0}catch{return!1}}function rl(e,t,r){if(r===`none`)return[];let i=[];return r===`live`&&(i.push(`focus`,`nudge`,`read`),nl(t.harness)&&i.push(`clear`)),Jc(e,t)!==void 0&&(r===`live`||r===`gone`||r===`unknown`)&&i.push(`stop`),t.harness&&t.harness in H&&n(t.cwd)&&i.push(`restart`),(r===`stopped`||r===`exited`||r===`gone`)&&i.push(`rebind`),i.push(`close`),i}function il(e,t){let n=z(e.store,t),r=el(e,n),i=Jc(e,n),a=n.pane?.mux??(i?Yc(e,n)?.name:void 0);return{id:n.id,handle:n.handle,...n.harness?{harness:n.harness}:{},status:n.status,liveness:r,pane:i&&a?{mux:a,id:i}:null,cwd:n.cwd,worktree:n.worktree??null,lastSeen:n.lastSeen,controls:rl(e,n,r)}}function al(e,t){let n;try{n=JSON.parse(t)?.session_id}catch{return}if(typeof n!=`string`||n===``)return;let r=R(e),i=r?F(e.store,r):void 0;i&&i.kind!==`standing`&&i.kind!==`service`&&i.conversation!==n&&I(e.store,{...i,conversation:n})}async function ol(e,t){let n=t.timeoutMs??6e5,r=(t.maxWaitS??240)*1e3,i=t.now??(()=>Date.now()),a=t.sleep??(e=>new Promise(t=>setTimeout(t,e))),o=i(),s=o+r;for(;;){let r=es(e,{meId:t.meId,unread:!0,thread:t.thread,from:t.from});if(r.length>0){let n=r[0];return ns(e,t.meId,n.id),{kind:`matched`,message:n}}let c=i()-o;if(n!==0&&c>=n)return{kind:`timed-out`};if(i()>=s)return{kind:`waiting`};await a(1e3)}}async function sl(e,t,n,r={}){let i=r.sleep??(e=>new Promise(t=>setTimeout(t,e))),a=/* @__PURE__ */ new Set;for(let n of es(e,{meId:t.meId,thread:t.thread,from:t.from}))a.add(n.id);let o=0;for(;;){let s=es(e,{meId:t.meId,thread:t.thread,from:t.from});for(let e of s)a.has(e.id)||(a.add(e.id),n(e));if(o++,r.maxIterations!=null&&o>=r.maxIterations)return;await i(1e3)}}const cl=globalThis.__SKILL_SCRIPT__?.version??JSON.parse(i(new URL(`../package.json`,import.meta.url),`utf8`)).version;function Y(e){return{store:new Gc(Li({space:e.space})),env:process.env,self:Co()}}function X(e){return e.format===`json`?`json`:`toon`}function ll(e){let t=R(e);return t||q("no identity in this session — run `cyberlegion unit register` first"),fa(e,t),t}function Z(e){return e.option(`--space <path>`,`isolate the hub root (overrides the global hub / $CYBERLEGION_ROOT)`).addOption(new ve(`--format <format>`,`output format`).choices([`toon`,`json`]).default(`toon`))}function ul(e){return e.hook(`postAction`,async(e,t)=>{let n=t.opts();(t.name()!==`who`||n.reconcile)&&await Fs(Y(n).store)})}const Q=new we;Q.name(`cyberlegion`).description(`Harness-agnostic agent session spawning and messaging over the filesystem`).version(cl).enablePositionalOptions();const $=Q.command(`unit`).description(`legion units — register, discover, spawn, and reap`);function dl(e,t,n){if(!t.handle){let n=L(e.store).filter(e=>e.kind===`standing`);G(X(t),{toon:Ts(`agents`,n,[{key:`id`,get:e=>e.id},{key:`handle`,get:e=>e.handle},{key:`harness`,get:e=>e.harness??`-`},{key:`status`,get:e=>e.status}],`${n.length} standing`),json:n});return}let r=L(e.store).find(e=>e.handle===t.handle&&e.kind!==`standing`&&e.status!==`exited`),i=Yi(e,{handle:t.handle,home:n});r&&console.error(`a live session already claims handle "${t.handle}"`),G(X(t),{toon:W({id:i.id,handle:i.handle,kind:i.kind,status:i.status,home:i.home?.dir??`none`,...i.home?{launch:i.home.agent?`agent ${i.home.agent}`:`harness ${i.home.harness}`}:{}}),json:i})}ul(Z($.command(`register`))).description(`register or refresh this session identity (or --standing: a session-independent owner inbox)`).option(`--handle <name>`,`human handle for this agent`).option(`--harness <h>`,`claude | cursor | codex (else auto-detected)`).option(`--standing`,`mint a standing, session-independent owner inbox (bare, with no --handle: list them)`).option(`--home <dir>`,`with --standing: the existing folder a presence is spawned in when mail arrives and none is live`).option(`--agent <name>`,`with --home: launch that agent definition, resolved from the home`).option(`--clear-home`,`with --standing: drop the owner's home`).action(e=>{let t=Y(e),n;try{n=Ec(t.exec??P,e)}catch(e){q(e instanceof Error?e.message:String(e))}if(e.standing){dl(t,e,n);return}let r=Ki(t,{handle:e.handle,harness:e.harness});G(X(e),{toon:W({id:r.id,handle:r.handle,harness:r.harness??`-`,status:r.status}),json:r})}),Z($.command(`claim`)).description(`bind the caller's unit as a standing owner's presence; --show reads it, --clear unbinds`).argument(`<handle>`,`standing owner handle`).option(`--clear`,`unbind the presence (a no-op when nothing is bound)`).option(`--show`,`print the bound presence instead of claiming`).action((e,t)=>{let n=Y(t);if(t.show){let r;try{r=ca(n.store,e)}catch(e){q(e instanceof Error?e.message:String(e))}G(X(t),{toon:W({presence:r?.id??`none`}),json:{presence:r?.id??null}});return}if(t.clear){try{oa(n,e)}catch(e){q(e instanceof Error?e.message:String(e))}G(X(t),{toon:W({presence:`none`}),json:{presence:null}});return}let r;try{r=aa(n,e)}catch(e){q(e instanceof Error?e.message:String(e))}G(X(t),{toon:W({owner:r.handle,presence:r.presence}),json:{owner:r.handle,presence:r.presence}})}),Z($.command(`whoami`)).description(`print this session own identity`).action(e=>{let t=Y(e),n=R(t);n||q("no identity in this session — run `cyberlegion unit register` first");let r=F(t.store,n);r||q(`registered self id "${n}" has no agent record`),G(X(e),{toon:W({id:r.id,handle:r.handle,harness:r.harness??`-`,status:r.status}),json:r})});function fl(e){let t=Y(e);B(t),e.reconcile&&ya(t,{adopt:!0});let n=L(t.store).filter(t=>e.all||t.status!==`exited`);G(X(e),{toon:Ts(`units`,n,[{key:`id`,get:e=>e.id},{key:`handle`,get:e=>e.handle},{key:`harness`,get:e=>e.harness??`-`},{key:`status`,get:e=>e.status},{key:`pane`,get:e=>e.pane?.id??`-`}],`${n.length} units`),json:n}),n.length===0&&K(`cyberlegion unit register to join`)}ul(Z($.command(`who`))).description(`list the addressable units`).option(`--all`,`include exited units`).option(`--reconcile`,`live-probe the current mux: cull dead-pane records and adopt unbound harness-bearing panes before listing`).action(fl),ul(Z($.command(`prune`))).description(`mark dead units exited and sweep`).action(e=>{let t=Y(e);B(t);let n=xa(t);G(X(e),{toon:Ts(`pruned`,n,[{key:`id`,get:e=>e.id},{key:`handle`,get:e=>e.handle}],`${n.length} pruned`),json:n})});const pl=`(harness default)`;function ml(e){return e.effortNotApplied?`${e.effortNotApplied} (not applied)`:e.effort??pl}function hl(e){let{effortNotApplied:t,model:n}=e.launched;t&&console.error(n?`${e.input.harness} carries effort only in a model id that cursor-agent models lists, and it lists no "${n}-${t}"; effort "${t}" not applied — launching ${n} at its default effort (run cursor-agent models to pick an id)`:`${e.input.harness} carries effort only as a parameter on a named model and none was set; effort "${t}" not applied — launching at the harness default model and effort (pass --model to apply it)`)}function gl(e){return Z(e).option(`--harness <h>`,`claude | cursor | codex (required unless --agent/--agent-file resolves one)`).option(`--agent <name>`,`resolve an agent def (.agents/agents/<name>.md) for harness/model/effort/instructions`).option(`--agent-file <path>`,`read an exact agent def file instead of resolving by name`).option(`--model <name>`,`model for this launch only (flag > agent def > harness default)`).option(`--effort <level>`,`effort for this launch only (flag > agent def > harness default)`).option(`--task <text>`,`brief text, or - for stdin`).option(`--brief-file <path>`,`read the brief from a file`).option(`--handle <name>`,`handle for the new peer`).option(`--branch <name>`,`branch for the new worktree (default cyberlegion/unit-<id>)`).option(`--worktree-path <path>`,`where to check out the new worktree`).option(`-C, --repo <path>`,`create the worktree from the git repository containing <path>, not the current directory's`).option(`--base <ref>`,`start the new worktree's branch from <ref> (default: fetch origin and use its default branch, else local HEAD)`).option(`--cwd <path>`,`spawn the session in an existing directory; create no worktree (mutually exclusive with --branch/--worktree-path/--repo/--base)`).addOption(new ve(`--at <placement>`,`where to open the new session (default: new-worktree → workspace, --cwd → tab)`).choices([`pane:right`,`pane:down`,`tab`,`workspace`])).option(`--no-wake`,`suppress the first-turn doorbell (spawn idle; the caller drives the first turn itself)`)}function _l(e){if(e.trustBlocked){if(console.error(e.trustBlocked),e.agent.brief){let t=Ma(e.agent.brief).replaceAll(`'`,`'\\''`);console.error(`then: cyberlegion unit nudge ${e.agent.handle} --message '${t}'`)}process.exitCode=1}}function vl(e){e.base?.fallback&&console.error(`base: fell back to local HEAD — ${e.base.fallback}`)}function yl(e){return ul(gl(e)).description(`launch a new peer session in its own git worktree (tmux or herdr)`).action(async e=>{let t=Y(e);B(t);let n;try{n=Jo(e,Zo)}catch(e){q(e instanceof Error?e.message:String(e))}hl(n);let r=await po(t,n.input,{noWake:n.noWake});r.warning&&console.error(`first-turn doorbell not confirmed (peer still spawned; nudge it manually): ${r.warning}`),_l(r),vl(r),G(X(e),{toon:W({spawned:r.agent.id,handle:r.agent.handle,harness:r.agent.harness,model:n.launched.model??pl,effort:ml(n.launched),worktree:r.agent.worktree?.root,reused:r.reused??`-`,base:r.base?.ref,pane:r.pane,rung:r.rung,trust:r.trust}),json:{agent:r.agent,pane:r.pane,launch:r.launch,base:r.base,...r.reused===void 0?{}:{reused:r.reused},model:n.launched.model??pl,effort:ml(n.launched),rung:r.rung,trust:r.trust}}),K(`cyberlegion unit read ${r.agent.id}`)})}yl($.command(`spawn`)),ul(Z($.command(`close`))).description(`release the unit's worktree lease (or remove a worktree it holds no lease on), tear down its session, and reap its state (the inverse of spawn)`).argument(`<id>`,`unit id, handle, or worktree branch/CR ref`).option(`--force`,`discard uncommitted changes in a worktree close removes (never overrides refusing the primary checkout)`).option(`--keep-worktree`,`leave the worktree on disk for reuse and reap everything else (skips the dirty check; never overrides refusing the primary checkout)`).action(async(e,t)=>{let n=Y(t);B(n);let r=z(n.store,e),i=await os(n,{id:r.id,force:t.force,keepWorktree:t.keepWorktree});G(X(t),{toon:W({closed:r.id,worktree:i.worktreeRoot??`-`,retained:i.retainedWorktree??`-`,lease:i.lease?i.lease.released?`released`:i.lease.reason:`-`,pane:i.pane??`-`}),json:i})}),Z($.command(`stop`)).description(`end a unit's session and keep the unit — its id, handle, inbox, brief, and worktree stay (unit close destroys them)`).argument(`<ref>`,`unit id, handle, or worktree branch/CR ref`).action((e,t)=>{let n=Y(t);B(n);let r=Qc(n,e);G(X(t),{toon:W({stopped:r.agent.id,pane:r.pane??`-`,verified:r.verified,already:r.alreadyStopped}),json:{stopped:r.agent.id,pane:r.pane??null,verified:r.verified,alreadyStopped:r.alreadyStopped}}),r.alreadyStopped||K(`cyberlegion unit restart ${r.agent.handle}`)}),Z($.command(`restart`)).description(`give a unit a new session and keep the unit — stops a running session, relaunches it resuming the harness's recorded conversation where it can, and otherwise rebriefs it`).argument(`<ref>`,`unit id, handle, or worktree branch/CR ref`).option(`--no-wake`,`do not ring the new session to read its brief (the caller briefs it by mail)`).option(`--fresh`,`start an empty session and rebrief it, even when the unit's conversation could be resumed`).action(async(e,t)=>{let n=Y(t);B(n);let r=await $c(n,e,{noWake:t.wake===!1,fresh:t.fresh===!0});G(X(t),{toon:W({restarted:r.agent.id,previous:r.previousPane??`-`,pane:r.pane,resumed:r.resumed,rung:r.rung}),json:r}),r.warning&&K(`warning: ${r.warning}`)}),Z($.command(`rebind`)).description(`bind a stopped unit to the calling pane — for a session started by hand that should be this unit`).argument(`<ref>`,`unit id, handle, or worktree branch/CR ref`).action((e,t)=>{let n=tl(Y(t),e);G(X(t),{toon:W({rebound:n.id,pane:n.pane?.id??`-`}),json:{rebound:n.id,pane:n.pane}})}),Z($.command(`show`)).description(`show a unit's runtime: where it is, whether it is live now, and the controls that work on it (read-only)`).argument(`<ref>`,`unit id, handle, or worktree branch/CR ref`).action((e,t)=>{let n=Y(t);B(n);let r=il(n,e);G(X(t),{toon:W({id:r.id,handle:r.handle,harness:r.harness??`-`,status:r.status,liveness:r.liveness,pane:r.pane?`${r.pane.mux}:${r.pane.id}`:`-`,cwd:r.cwd,worktree:r.worktree?.root??`-`,lastSeen:r.lastSeen,controls:r.controls.join(`,`)||`-`}),json:r})}),Z($.command(`focus`)).description(`move input focus to a peer's session, named by a ref or picked by its worktree's repo/branch`).argument(`[ref]`,`unit id, handle, or worktree branch/CR ref`).option(`--repo <name>`,`pick the one live unit in this repository (its primary checkout's folder name, else its cwd's)`).option(`--branch <name>`,`pick the one live unit whose worktree is on this branch`).action((e,t)=>{let n={repo:t.repo,branch:t.branch};e!==void 0&&(n.repo!==void 0||n.branch!==void 0)&&q(`a ref and --repo/--branch cannot be combined — pass one or the other`);let r=Y(t);B(r);let{agent:i,pane:a}=Eo(r,e??n),o=e??i.id;G(X(t),{toon:W({focused:o,pane:a}),json:{ref:o,pane:a}})}),Z($.command(`nudge`)).description(`deliver text as a turn in a peer's session — by default a doorbell telling them to check their mail; --message relays any text (e.g. a decision) instead`).argument(`<ref>`,`unit id, handle, or worktree branch/CR ref`).option(`--message <text>`,`text delivered as a turn in the peer session`,ja).action(async(e,t)=>{let n=Y(t);B(n);let{pane:r,message:i,resubmits:a}=await Do(n,e,{message:t.message});G(X(t),{toon:W({nudged:e,pane:r}),json:{ref:e,pane:r,message:i,resubmits:a}})}),Z($.command(`read`)).description(`scrape a peer's session screen`).argument(`<ref>`,`unit id, handle, or worktree branch/CR ref`).option(`--lines <n>`,`trailing lines to capture`,e=>Number.parseInt(e,10)).action((e,t)=>{let n=Y(t);B(n);let{pane:r,output:i}=Oo(n,e,{lines:t.lines});console.log(Yo(X(t),{ref:e,pane:r,output:i}))}),Z($.command(`clear`)).description(`reset a warm peer's context to cold by injecting its own harness fresh-context command — keeps the pane/session warm; tears down nothing`).argument(`<ref>`,`unit id, handle, or worktree branch/CR ref`).action(async(e,t)=>{let n=Y(t);B(n);let r;try{r=await ko(n,e)}catch(e){q(e instanceof Error?e.message:String(e))}G(X(t),{toon:W({cleared:e,pane:r.pane,command:r.command}),json:{cleared:e,pane:r.pane,command:r.command}})});const bl=Q.command(`project`).description(`register and resolve projects (one per git repository)`);function xl(e){return{id:e.id,name:e.name,root:e.root}}async function Sl(e,t,n){let r=await Ms(e.store,n,e.exec??P),i=r?{...n,subject:r.subject}:n;G(X(t),{toon:W({...xl(i),...r?{channel:r.channel?.handle??`none`}:{}}),json:r?{...i,...r.channel?{channel:r.channel}:{}}:i})}Z(bl.command(`register`)).description(`register (or refresh) the project containing a directory — any checkout of it`).option(`--dir <path>`,`a directory inside the project (default: the current directory)`).action(async e=>{let t=Y(e),n;try{n=Ys({store:t.store},{dir:e.dir})}catch(e){q(e instanceof Error?e.message:String(e))}await Sl(t,e,n)}),Z(bl.command(`list`)).description(`list the registered projects`).action(e=>{let t=Xs(Y(e).store);G(X(e),{toon:Ts(`projects`,t,[{key:`id`,get:e=>e.id},{key:`name`,get:e=>e.name},{key:`root`,get:e=>e.root}],`${t.length} projects`),json:t}),t.length===0&&K(`cyberlegion project register to add the current repository`)}),Z(bl.command(`show`)).description(`resolve a registered project by id, path, or unique name`).argument(`<ref>`,`project id, a path inside any of its checkouts, or its name`).action(async(e,t)=>{let n=Y(t),r;try{r=$s({store:n.store},e)}catch(e){q(e instanceof Error?e.message:String(e))}await Sl(n,t,r)});const Cl=Q.command(`service`).description(`project services — one authoritative owner each, fenced by generation`);function wl(e){return{project:e.project.id,service:e.lease.service,endpoint:e.endpoint.id,generation:e.lease.generation,state:e.lease.state,holder:e.lease.holder??null,health:e.health,control:e.control,...e.note?{note:e.note}:{}}}function Tl(e){return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,t??`-`]))}function El(e,t){G(X(e),{toon:W(Tl(t)),json:t})}function Dl(e){try{return e()}catch(e){q(e instanceof Error?e.message:String(e))}}const Ol=e=>Number.parseInt(e,10);function kl(e,t){return t===void 0?[`.`,e]:[e,t]}function Al(e,t){return t?Dl(()=>z(e.store,t)).id:ll(e)}Z(Cl.command(`resolve`)).description(`show a service's owner, generation, health, and whether its session is controllable`).argument(`<project-or-name>`,`project id, path, or unique name — omit to use the current directory`).argument(`[name]`,`service name`).action((e,t,n)=>{let[r,i]=kl(e,t),a=Y(n);El(n,wl(Dl(()=>vc(a,r,i))))}),Z(Cl.command(`acquire`)).description(`resolve the healthy owner, or reserve the right to start one (exactly one caller wins)`).argument(`<project-or-name>`,`project id, path, or unique name — omit to use the current directory`).argument(`[name]`,`service name`).option(`--ttl <ms>`,`how long the reservation holds before another caller may take over`,Ol).option(`--force-generation <n>`,`reserve even over a healthy owner — must name the current generation`,Ol).action((e,t,n)=>{let[r,i]=kl(e,t),a=Y(n),o=Dl(()=>yc(a,r,i,{by:R(a),ttlMs:n.ttl,...n.forceGeneration===void 0?{}:{force:{generation:n.forceGeneration}}}));El(n,{outcome:o.outcome,...wl(o.view),...o.outcome===`reserved`?{token:o.token,expiresAt:o.lease.reservation?.expiresAt}:{}}),o.outcome===`reserved`?K(`start the runtime, then: cyberlegion service bind ${r} ${i} --generation ${o.lease.generation} --token ${o.token}`):o.outcome===`starting`&&K(`another caller is starting it — re-run to resolve: cyberlegion service resolve ${r} ${i}`)}),Z(Cl.command(`bind`)).description(`complete a reservation: make a live unit (default: this session) the owner`).argument(`<project-or-name>`,`project id, path, or unique name — omit to use the current directory`).argument(`[name]`,`service name`).requiredOption(`--generation <n>`,`the reserved generation`,Ol).requiredOption(`--token <token>`,`the reservation token from acquire`).option(`--unit <ref>`,`the unit to bind (default: this session)`).action((e,t,n)=>{let[r,i]=kl(e,t),a=Y(n),o=Al(a,n.unit);El(n,wl(Dl(()=>xc(a,r,i,{generation:n.generation,token:n.token,unit:o}))))}),Z(Cl.command(`release`)).description(`abandon a reservation (--token) or step down as owner (default: this session)`).argument(`<project-or-name>`,`project id, path, or unique name — omit to use the current directory`).argument(`[name]`,`service name`).requiredOption(`--generation <n>`,`the current generation`,Ol).option(`--token <token>`,`release a reservation instead of an active ownership`).option(`--unit <ref>`,`the owning unit (default: this session)`).action((e,t,n)=>{let[r,i]=kl(e,t),a=Y(n),o=n.token?{generation:n.generation,token:n.token}:{generation:n.generation,unit:Al(a,n.unit)};El(n,wl(Dl(()=>Sc(a,r,i,o))))}),Z(Cl.command(`handoff`)).description(`transfer ownership to another live unit under a new generation`).argument(`<project-or-name>`,`project id, path, or unique name — omit to use the current directory`).argument(`[name]`,`service name`).requiredOption(`--generation <n>`,`the current generation`,Ol).requiredOption(`--to <ref>`,`the unit taking over`).option(`--from <ref>`,`the current owner (default: this session)`).action((e,t,n)=>{let[r,i]=kl(e,t),a=Y(n),o=Al(a,n.from),s=Dl(()=>z(a.store,n.to)).id;El(n,wl(Dl(()=>Cc(a,r,i,{generation:n.generation,from:o,to:s}))))}),Z(Cl.command(`verify`)).description(`the fencing check: exit 0 only when the unit (default: this session) owns the service at this generation`).argument(`<project-or-name>`,`project id, path, or unique name — omit to use the current directory`).argument(`[name]`,`service name`).requiredOption(`--generation <n>`,`the generation the caller believes it owns`,Ol).option(`--unit <ref>`,`the unit to check (default: this session)`).action((e,t,n)=>{let[r,i]=kl(e,t),a=Y(n),o=Al(a,n.unit);El(n,wl(Dl(()=>wc(a,r,i,{unit:o,generation:n.generation}))))}),ul(gl(Cl.command(`start`))).description(`resolve the healthy owner, or spawn one peer and bind it — concurrent starts launch once`).argument(`<project-or-name>`,`project id, path, or unique name — omit to use the current directory`).argument(`[name]`,`service name`).option(`--ttl <ms>`,`how long the reservation holds while the peer launches`,Ol).action(async(e,t,n)=>{let[r,i]=kl(e,t),a=Y(n);B(a);let o;try{o=Jo(n,Zo)}catch(e){q(e instanceof Error?e.message:String(e))}hl(o);let s,c;try{c=await Tc(a,r,i,{by:R(a),ttlMs:n.ttl,launch:async()=>{let e=await po(a,o.input,{noWake:o.noWake});return s=e.warning,_l(e),vl(e),{unit:e.agent.id,pane:e.pane}}})}catch(e){q(e instanceof Error?e.message:String(e))}s&&console.error(`first-turn doorbell not confirmed (peer still spawned; nudge it manually): ${s}`),El(n,{outcome:c.outcome,...wl(c.view)}),c.outcome===`starting`&&K(`another caller is starting it — re-run to resolve: cyberlegion service resolve ${r} ${i}`)});const jl=Q.command(`mail`).description(`durable inter-agent messaging`);function Ml(e){return Z(e).description(`send a message to a peer (by handle or id)`).requiredOption(`--to <peer>`,`recipient handle or id`).option(`--from <id>`,`sender id (else this session own identity)`).option(`--subject <s>`,`subject`).option(`--body <text>`,`message body`).option(`--body-file <path>`,`read body from a file, or - for stdin`).option(`--thread <id>`,`thread id`).option(`--reply-to <msg>`,`message id this replies to`).option(`--no-nudge`,`suppress the delivery doorbell (do not wake the recipient's pane)`).action(async e=>{let t=Y(e),n=e.from??ll(t),r=$o(e.body,e.bodyFile),i=Qo({store:t.store},{fromId:n,to:e.to,subject:e.subject,body:r,thread:e.thread,replyTo:e.replyTo}),a=await Ba(t.store,()=>V(t.env??process.env),P,{toId:i.to,fromId:n,noNudge:e.nudge===!1,spawnHome:e=>Ws(t,e)});a.warning&&console.error(`delivery doorbell not confirmed (message still delivered): ${a.warning}`),G(X(e),{toon:W({sent:i.id,to:e.to,subject:i.subject,rung:a.rung,...a.spawned?{spawned:a.spawned}:{}}),json:{...i,rung:a.rung,...a.spawned?{spawned:a.spawned}:{}}})})}Ml(jl.command(`send`));function Nl(e,t,n,r){let i=es({store:e.store},{meId:n,unread:t.unread,from:t.from,thread:t.thread}),a=i.filter(e=>!e.read).length;G(X(t),{toon:Ts(`messages`,i,[{key:`id`,get:e=>e.id},{key:`from`,get:e=>e.fromHandle},{key:`subject`,get:e=>e.subject??``},{key:`read`,get:e=>e.read}],`${i.length} messages (${a} unread)`),json:i});let o=i.find(e=>!e.read);o&&K(`${r} ${o.id}`)}function Pl(e){let t=Y(e);B(t),Nl(t,e,e.owner?ia(t.store,e.owner):ll(t),`cyberlegion mail read`)}Z(jl.command(`inbox`)).description(`list your mail`).option(`--unread`,`only un-acked mail`).option(`--from <id>`,`filter by sender`).option(`--thread <id>`,`filter to messages carrying this thread id`).option(`--owner <handle>`,`target a standing owner's mailbox instead of this session's own`).action(Pl),Z(jl.command(`read`)).description(`read a message; --ack consumes it in the same step, otherwise it only peeks`).argument(`<msg-id>`,`message id`).option(`--ack`,`acknowledge the message in the same step (idempotent — no error if already acked)`).option(`--owner <handle>`,`read a standing owner's mailbox instead of this session's own`).action((e,t)=>{let n=Y(t),r=t.owner?ia(n.store,t.owner):ll(n);if(t.ack){let{msg:i,acked:a}=rs({store:n.store},r,e);G(X(t),{toon:W({id:i.id,from:i.fromHandle,subject:i.subject,body:i.body,acked:a}),json:{...i,acked:a}});return}let i=ts({store:n.store},r,e);i||q(`"${e}" is not a message in this inbox`),G(X(t),{toon:W({id:i.id,from:i.fromHandle,subject:i.subject,body:i.body}),json:i}),K(`cyberlegion mail ack ${i.id}${t.owner?` --owner ${t.owner}`:``}`)}),Z(jl.command(`ack`)).description(`acknowledge a message (moves it out of the unread set)`).argument(`<msg-id>`,`message id`).option(`--owner <handle>`,`ack a standing owner's mailbox instead of this session's own`).action((e,t)=>{let n=Y(t),r=t.owner?ia(n.store,t.owner):ll(n),i=ns({store:n.store},r,e);G(X(t),{toon:W({acked:i.id,from:i.fromHandle,subject:i.subject}),json:i})}),Z(jl.command(`delete`)).description(`permanently remove a message from your inbox (unread or already-acked)`).argument(`<msg-id>`,`message id`).action((e,t)=>{let n=Y(t),r=ll(n);is({store:n.store},r,e),G(X(t),{toon:W({deleted:e}),json:{deleted:e}})}),Z(jl.command(`await`)).description(`block until a thread-correlated reply arrives, print it, and ack it`).requiredOption(`--thread <id>`,`thread id to wait on`).option(`--from <h>`,`only match a reply from this sender`).option(`--timeout <ms>`,`give up after this many ms with no match (0 = wait forever); exits non-zero on timeout`,e=>Number.parseInt(e,10),6e5).option(`--max-wait <s>`,`self-cap for one internal poll cycle, in seconds — returns the clean "waiting" sentinel at this cap so the caller can re-arm rather than blocking past a harness tool-timeout`,e=>Number.parseInt(e,10),240).addHelpText(`after`,`
|
|
73
|
+
`)}}}var rc=class extends Error{code;constructor(e,t){super(t),this.code=e,this.name=`ServiceOwnershipError`}};const ic=/^[a-z0-9][a-z0-9_-]{0,62}$/;function ac(e){if(!ic.test(e))throw Error(`invalid service name "${e}" — use lowercase letters, digits, "-" or "_" (max 63)`)}function oc(e,t){return`svc-${e}-${t}`}const sc=e=>e.now?.()??Date.now(),cc=e=>new Date(e).toISOString();function lc(e,t){return`service-${e}-${t}`}function uc(e,t){return t.status===`exited`?!1:e.isLive?e.isLive(t):ha(e,t)}function dc(e,t,n){let r=oc(t.id,n),i=F(e.store,r);if(i)return i;e.store.ensureMarker();let a=cc(sc(e)),o={id:r,handle:`${n}@${t.name}`,kind:`service`,service:{project:t.id,name:n},cwd:t.root,pane:null,status:`active`,createdAt:a,lastSeen:a};return I(e.store,o),o}function fc(e,t,n,r){let i={project:t,endpoint:n,lease:r};if(r.state===`vacant`)return{...i,health:`vacant`,control:`none`};if(r.state===`reserved`){let t=sc(e)>Date.parse(r.reservation?.expiresAt??``);return{...i,health:t?`expired`:`starting`,control:`none`,...t?{note:`the reservation expired without a bound owner; the next acquire takes over`}:{}}}let a=r.holder?F(e.store,r.holder):void 0;if(!a)return{...i,health:`unhealthy`,control:`none`,note:`the owner has no unit record`};let o=a.pane||e.store.findPaneByAgentId(a.id)?`pane`:`none`,s=o===`none`?`the owner resolves, but its session control is not recoverable from here (no multiplexer pane — e.g. a native subagent only its parent can drive)`:void 0;return uc(e,a)?{...i,owner:a,health:`healthy`,control:o,...s?{note:s}:{}}:{...i,owner:a,health:`unhealthy`,control:o,note:`the owner session is gone`}}function pc(e,t,n){return{project:t,service:n,endpoint:oc(t,n),generation:0,state:`vacant`,updatedAt:cc(sc(e))}}function mc(e,t,n,r){ac(n);let i=$s(e,t),a=e.store.getServiceLease(i.id,n),o=F(e.store,oc(i.id,n));if(a&&o)return{project:i,endpoint:o,lease:a};if(!r)throw Error(`no service "${n}" in project ${i.name} (${i.id})`);return{project:i,endpoint:dc(e,i,n),lease:a??pc(e,i.id,n)}}function hc(e,t,n,r,i){ac(n);let a=$s(e,t);return e.store.withLock(lc(a.id,n),()=>i(mc(e,a.id,n,r)))}function gc(e,t){let n={...t,updatedAt:cc(sc(e))};return e.store.putServiceLease(n),n}function _c(e){return new rc(`stale`,e)}function vc(e,t,n){let{project:r,endpoint:i,lease:a}=mc(e,t,n,!1);return fc(e,r,i,a)}function yc(e,t,n,r={}){return hc(e,t,n,!0,({project:t,endpoint:i,lease:a})=>{let o=fc(e,t,i,a);if(r.force){if(r.force.generation!==a.generation)throw _c(`cannot force: generation ${r.force.generation} is not the current ${a.generation}`)}else if(o.health===`healthy`)return{outcome:`resolved`,lease:a,view:o};else if(o.health===`starting`)return{outcome:`starting`,lease:a,view:o};let s=sc(e),c=ie(8).toString(`hex`),l=gc(e,{project:t.id,service:n,endpoint:i.id,generation:a.generation+1,state:`reserved`,reservation:{token:c,...r.by?{by:r.by}:{},at:cc(s),expiresAt:cc(s+(r.ttlMs??3e5)),...r.force?{forced:!0}:{}},updatedAt:cc(s)});return{outcome:`reserved`,lease:l,view:fc(e,t,i,l),token:c}})}function bc(e,t){let n=F(e.store,t);if(!n)throw new rc(`unknown-unit`,`no unit "${t}"`);if(!uc(e,n))throw new rc(`unit-not-live`,`unit "${t}" has no live session`);return n}function xc(e,t,n,r){return hc(e,t,n,!1,({project:t,endpoint:n,lease:i})=>{if(i.state!==`reserved`||i.generation!==r.generation||i.reservation?.token!==r.token)throw _c(`reservation for generation ${r.generation} is no longer current (now ${i.state} at ${i.generation})`);return bc(e,r.unit),fc(e,t,n,gc(e,{project:i.project,service:i.service,endpoint:i.endpoint,generation:i.generation,state:`active`,holder:r.unit,updatedAt:i.updatedAt}))})}function Sc(e,t,n,r){return hc(e,t,n,!1,({project:t,endpoint:n,lease:i})=>{if(!(i.generation===r.generation&&(i.state===`reserved`&&r.token!==void 0&&i.reservation?.token===r.token||i.state===`active`&&r.unit!==void 0&&i.holder===r.unit)))throw _c(`nothing to release at generation ${r.generation} (now ${i.state} at ${i.generation})`);return fc(e,t,n,gc(e,{project:i.project,service:i.service,endpoint:i.endpoint,generation:i.generation,state:`vacant`,updatedAt:i.updatedAt}))})}function Cc(e,t,n,r){return hc(e,t,n,!1,({project:t,endpoint:n,lease:i})=>{if(i.state!==`active`||i.generation!==r.generation||i.holder!==r.from)throw _c(`"${r.from}" is not the owner at generation ${r.generation}`);return bc(e,r.to),fc(e,t,n,gc(e,{...i,generation:i.generation+1,holder:r.to}))})}function wc(e,t,n,r){let{project:i,endpoint:a,lease:o}=mc(e,t,n,!1);if(o.state!==`active`||o.holder!==r.unit||o.generation!==r.generation)throw _c(`"${r.unit}" at generation ${r.generation} is not the owner (now ${o.state}${o.holder?` by ${o.holder}`:``} at ${o.generation})`);return fc(e,i,a,o)}async function Tc(e,t,n,r){let i=yc(e,t,n,r);if(i.outcome!==`reserved`)return{outcome:i.outcome,view:i.view};let{generation:a,endpoint:o}=i.lease,s;try{s=await r.launch({generation:a,endpoint:o})}catch(r){try{Sc(e,t,n,{generation:a,token:i.token})}catch{}throw r}try{return{outcome:`started`,view:xc(e,t,n,{generation:a,token:i.token,unit:s.unit}),launched:s}}catch(e){throw e instanceof rc?new rc(e.code,`launched unit "${s.unit}" could not become the owner — ${e.message}; stop it`):e}}function Ec(e,t){if((t.home!==void 0||t.clearHome===!0||t.agent!==void 0)&&!(t.standing&&t.handle))throw Error(`a home belongs only to a named standing owner — pass --standing --handle <name>`);if(!t.standing)return;if(t.home!==void 0&&t.clearHome)throw Error(`--home and --clear-home contradict — pass one`);if(t.home===void 0&&(t.agent!==void 0||t.harness!==void 0))throw Error(`a launch needs --home: --agent and --harness name how to start a session in it`);if(t.clearHome)return null;if(t.home===void 0)return;let r=g(t.home);if(!n(r)||!l(r).isDirectory())throw Error(`the home must already exist: ${t.home}`);if(t.agent===void 0==(t.harness===void 0))throw Error(`a home needs exactly one of --agent or --harness`);let i;if(t.harness!==void 0){if(!(t.harness in H))throw Error(`unrecognized --harness "${t.harness}" (${Object.keys(H).join(` | `)})`);i={harness:t.harness}}else{let e=t.agent;try{zo({name:e,cwd:r})}catch{throw Error(`the agent definition "${e}" does not resolve from the home ${r}`)}i={agent:e}}let a=lo(e,r);if(a!==void 0&&o(a)===o(r))throw Error(`the home refuses the primary checkout ${r} — a spawned unit never works there`);return{dir:r,...i}}var Dc=class extends Error{file;constructor(e,t){super(`corrupt record at "${e}": ${t instanceof Error?t.message:String(t)}`,{cause:t}),this.file=e,this.name=`CorruptRecordError`}};function Oc(e){if(e===process.pid)return`alive`;try{return process.kill(e,0),`alive`}catch(e){return e.code===`ESRCH`?`dead`:`unknown`}}var kc=class extends Error{name;constructor(e){super(`timed out waiting for lock "${e}"`),this.name=e,this.name=`LockTimeoutError`}};function Ac(e){return h(e,`locks`)}function jc(e,t){return h(Ac(e),`${t}.lock`)}function Mc(e){try{return JSON.parse(i(h(e,`holder.json`),`utf8`))}catch{return}}function Nc(e){return Oc(e)!==`dead`}function Pc(e){e<=0||Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,e)}function Fc(e){let t=`${e}.stale-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;try{s(e,t)}catch(e){if(e.code===`ENOENT`)return!1;throw e}let n=Mc(t);if(n&&Nc(n.pid)){try{s(t,e)}catch{c(t,{recursive:!0,force:!0})}return!1}return c(t,{recursive:!0,force:!0}),!0}function Ic(e,t,n={}){let i=n.retryDelayMs??20,a=n.timeoutMs??5e3,o=jc(e,t);r(Ac(e),{recursive:!0});let s=Date.now()+a;for(;;)try{return r(o),u(h(o,`holder.json`),JSON.stringify({pid:process.pid,acquiredAt:Date.now()})),{release:()=>c(o,{recursive:!0,force:!0})}}catch(e){if(e.code!==`EEXIST`)throw e;let n=Mc(o);if(n&&!Nc(n.pid)&&Fc(o))continue;if(Date.now()>s)throw new kc(t);Pc(i)}}function Lc(e,t,n,r){let i=Ic(e,t,r);try{return n()}finally{i.release()}}async function Rc(e,t,n,r){let i=Ic(e,t,r);try{return await n()}finally{i.release()}}function zc(e){let t=i(e,`utf8`);try{return JSON.parse(t)}catch(t){throw new Dc(e,t)}}function Bc(e){return n(e)?a(e).filter(e=>e.endsWith(`.json`)).map(t=>zc(h(e,t))):[]}let Vc=0;function Hc(e,t){r(p(e),{recursive:!0});let n=`${e}.${process.pid}.${Vc++}.tmp`;u(n,t),s(n,e)}function Uc(e,t){Hc(e,`${JSON.stringify(t,null,2)}\n`)}function Wc(e,t){Hc(e,t)}function J(e){try{return e()}catch(e){if(e instanceof zi)return;throw e}}var Gc=class{root;constructor(e){this.root=e}ensureMarker(){Ri(this.root)}putMessage(e,t){Hc(M.messageFile(this.root,e,t.id),`${JSON.stringify(t,null,2)}\n`)}listInbox(e){let t=J(()=>M.inboxDir(this.root,e)),n=J(()=>M.inboxReadDir(this.root,e));return{unread:t?Bc(t):[],read:n?Bc(n):[]}}ackMessage(e,t){let i=J(()=>M.messageFile(this.root,e,t));if(!i||!n(i))throw Error(`"${t}" is not an unread message in this inbox`);let a=zc(i),o=M.messageReadFile(this.root,e,t);return r(p(o),{recursive:!0}),s(i,o),a}removeMessage(e,t){let r=J(()=>M.messageFile(this.root,e,t));if(r&&n(r)){c(r);return}let i=J(()=>M.messageReadFile(this.root,e,t));if(i&&n(i))c(i);else throw Error(`"${t}" is not a message in this inbox`)}removeMailbox(e){c(M.inboxDir(this.root,e),{recursive:!0,force:!0})}putAgent(e){Uc(M.agentFile(this.root,e.id),e)}getAgent(e){let t=J(()=>M.agentFile(this.root,e));if(t&&n(t))return zc(t)}listAgents(){let e=M.agentsDir(this.root);return n(e)?a(e).filter(e=>e.endsWith(`.json`)).map(t=>zc(h(e,t))).sort((e,t)=>e.createdAt.localeCompare(t.createdAt)):[]}removeAgent(e){c(M.agentFile(this.root,e),{force:!0})}removeAgentData(e){c(M.dataDir(this.root,e),{recursive:!0,force:!0})}putProject(e){Uc(M.projectFile(this.root,e.id),e)}getProject(e){let t=J(()=>M.projectFile(this.root,e));if(t&&n(t))return zc(t)}listProjects(){let e=M.projectsDir(this.root);return n(e)?a(e).filter(e=>e.endsWith(`.json`)).map(t=>zc(h(e,t))).sort((e,t)=>e.registeredAt.localeCompare(t.registeredAt)):[]}putServiceLease(e){Uc(M.serviceLeaseFile(this.root,e.project,e.service),e)}getServiceLease(e,t){let r=J(()=>M.serviceLeaseFile(this.root,e,t));if(r&&n(r))return zc(r)}putPaneIndex(e,t){Wc(M.paneFile(this.root,e),t)}resolvePaneId(e){let t=M.paneFile(this.root,e);return n(t)?i(t,`utf8`).trim():void 0}findPaneByAgentId(e){let t=M.panesDir(this.root);if(n(t)){for(let n of a(t))if(n.endsWith(`.id`)&&i(h(t,n),`utf8`).trim()===e)return n.slice(0,-3)}}removePaneIndex(e){c(M.paneFile(this.root,e),{force:!0})}writeBrief(e,t){Wc(M.briefFile(this.root,e),t)}readBrief(e){let t=J(()=>M.briefFile(this.root,e));return t&&n(t)?i(t,`utf8`):void 0}setMainPane(e){Lc(this.root,`main-pane`,()=>{let t=M.mainPaneFile(this.root);e?Wc(t,e):c(t,{force:!0})})}getMainPane(){let e=M.mainPaneFile(this.root);return n(e)?i(e,`utf8`).trim():void 0}withLock(e,t){return Lc(this.root,e,t)}withLockAsync(e,t){return Rc(this.root,e,t)}};function Kc(e,t){if(e.kind===`standing`||e.kind===`service`)throw Error(`"${t}" is a ${e.kind} record — it has no runtime to stop, restart, or rebind`)}function qc(e,t,n){if(R(e)===t.id)throw Error(`a unit cannot ${n} its own session — run unit ${n} ${t.handle} from another session`)}function Jc(e,t){return t.pane?.id??e.store.findPaneByAgentId(t.id)}function Yc(e,t){if(e.adapter)return e.adapter;if(t.pane?.mux===`tmux`)return D;if(t.pane?.mux===`herdr`)return T;try{return V(e.env??process.env,e.exec??P)}catch{return}}function Xc(e,t,n){let r={...t,status:`stopped`,pane:null};return I(e.store,r),n&&e.store.removePaneIndex(n),r}function Zc(e,t){let n=Jc(e,t);if(!n)return{agent:Xc(e,t,void 0),verified:!1};let r=e.exec??P,i=Yc(e,t);if(!i)return{agent:Xc(e,t,n),pane:n,verified:!1};try{i.teardown(r,{id:n})}catch{}let a=i.listPanes(r);if(a.some(e=>e.id===n))throw Error(`stop did not take effect — the backend still lists pane ${n} for unit "${t.handle}"; its record is unchanged`);return{agent:Xc(e,t,n),pane:n,verified:a.length>0}}function Qc(e,t){let n=z(e.store,t);return Kc(n,t),qc(e,n,`stop`),n.status===`stopped`?{agent:n,verified:!0,alreadyStopped:!0}:{...Zc(e,n),alreadyStopped:!1}}async function $c(e,t,r={}){let i=z(e.store,t);Kc(i,t),qc(e,i,`restart`);let a=i.harness;if(!a||!(a in H))throw Error(`unit "${i.handle}" has harness "${i.harness??``}", not in the launch map (${Object.keys(H).join(` | `)}) — cannot restart it`);if(!n(i.cwd))throw Error(`unit "${i.handle}" cannot restart — its cwd ${i.cwd} is gone; spawn a new unit instead`);let o=i,s;if(i.status!==`stopped`){let t=Zc(e,i);o=t.agent,s=t.pane}let c=e.env??process.env,l=e.exec??P,u=o.launch??H[a],d=o.conversation&&!r.fresh?bo(a,u,o.conversation):void 0,f=o.worktree?`workspace`:`tab`,p,m;try{m=e.adapter??V(c,l);let t=f===`workspace`?{label:Za({brief:e.store.readBrief(i.id)??``,handle:i.handle,id:i.id})}:{};p=m.open(l,{cwd:o.cwd,launch:d?vo(e,m.name,i.id,d,{bindSelf:!0,fallback:u}):vo(e,m.name,i.id,u,{bindSelf:!0}),at:f,from:Ar(m,N(c)),...t})}catch(e){throw Error(`restart could not open a session — unit "${i.handle}" is stopped, with its inbox, brief, and worktree kept; rerun unit restart ${i.handle}: ${e instanceof Error?e.message:String(e)}`)}let h=m.name,g={...o,status:`active`,pane:h===`tmux`||h===`herdr`?{mux:h,id:p.id}:null,lastSeen:new Date(e.now?.()??Date.now()).toISOString()};I(e.store,g),e.store.putPaneIndex(p.id,i.id);let _=g.brief,v=await Va(()=>m,l,{target:p,briefPath:_??``,noWake:r.noWake||!_,resumed:!!d},r.nudgeOpts);return{agent:g,...s?{previousPane:s}:{},pane:p.id,launch:u,resumed:!!d,rung:v.rung,...v.warning?{warning:v.warning}:{}}}function el(e,t){if(t.kind===`standing`||t.kind===`service`)return`none`;if(t.status===`stopped`)return`stopped`;if(t.status===`exited`)return`exited`;let n=Jc(e,t),r=n?Yc(e,t):void 0;if(!n||!r)return`unknown`;let i=r.listPanes(e.exec??P);return i.length===0?`unknown`:i.some(e=>e.id===n)?`live`:`gone`}function tl(e,t){let n=z(e.store,t);Kc(n,t);let r=Hi(e.env??process.env);if(!r)throw Error(`unit rebind needs to run inside a tmux or herdr pane — there is no pane to bind`);let i=Jc(e,n);if(n.pane?.mux===r.mux&&n.pane.id===r.pane&&e.store.resolvePaneId(r.pane)===n.id)return n;let a=e.store.resolvePaneId(r.pane),o=a&&a!==n.id?F(e.store,a):void 0;if(o&&o.status!==`stopped`&&o.status!==`exited`)throw Error(`this pane belongs to unit "${o.handle}" (${o.id}) — rebind never takes another unit's pane`);let s=el(e,n);if(i&&i!==r.pane&&(s===`live`||s===`unknown`))throw Error(`unit "${n.handle}" may still be running in pane ${i} — stop it first (unit stop ${n.handle})`);let c={...n,status:`active`,pane:{mux:r.mux,id:r.pane},lastSeen:new Date(e.now?.()??Date.now()).toISOString()};return I(e.store,c),i&&i!==r.pane&&e.store.removePaneIndex(i),e.store.putPaneIndex(r.pane,n.id),c}function nl(e){if(!e)return!1;try{return so(e),!0}catch{return!1}}function rl(e,t,r){if(r===`none`)return[];let i=[];return r===`live`&&(i.push(`focus`,`nudge`,`read`),nl(t.harness)&&i.push(`clear`)),Jc(e,t)!==void 0&&(r===`live`||r===`gone`||r===`unknown`)&&i.push(`stop`),t.harness&&t.harness in H&&n(t.cwd)&&i.push(`restart`),(r===`stopped`||r===`exited`||r===`gone`)&&i.push(`rebind`),i.push(`close`),i}function il(e,t){let n=z(e.store,t),r=el(e,n),i=Jc(e,n),a=n.pane?.mux??(i?Yc(e,n)?.name:void 0);return{id:n.id,handle:n.handle,...n.harness?{harness:n.harness}:{},status:n.status,liveness:r,pane:i&&a?{mux:a,id:i}:null,cwd:n.cwd,worktree:n.worktree??null,lastSeen:n.lastSeen,controls:rl(e,n,r)}}function al(e,t){let n;try{n=JSON.parse(t)?.session_id}catch{return}if(typeof n!=`string`||n===``)return;let r=R(e),i=r?F(e.store,r):void 0;i&&i.kind!==`standing`&&i.kind!==`service`&&i.conversation!==n&&I(e.store,{...i,conversation:n})}async function ol(e,t){let n=t.timeoutMs??6e5,r=(t.maxWaitS??240)*1e3,i=t.now??(()=>Date.now()),a=t.sleep??(e=>new Promise(t=>setTimeout(t,e))),o=i(),s=o+r;for(;;){let r=es(e,{meId:t.meId,unread:!0,thread:t.thread,from:t.from});if(r.length>0){let n=r[0];return ns(e,t.meId,n.id),{kind:`matched`,message:n}}let c=i()-o;if(n!==0&&c>=n)return{kind:`timed-out`};if(i()>=s)return{kind:`waiting`};await a(1e3)}}async function sl(e,t,n,r={}){let i=r.sleep??(e=>new Promise(t=>setTimeout(t,e))),a=/* @__PURE__ */ new Set;for(let n of es(e,{meId:t.meId,thread:t.thread,from:t.from}))a.add(n.id);let o=0;for(;;){let s=es(e,{meId:t.meId,thread:t.thread,from:t.from});for(let e of s)a.has(e.id)||(a.add(e.id),n(e));if(o++,r.maxIterations!=null&&o>=r.maxIterations)return;await i(1e3)}}const cl=globalThis.__SKILL_SCRIPT__?.version??JSON.parse(i(new URL(`../package.json`,import.meta.url),`utf8`)).version;function Y(e){return{store:new Gc(Li({space:e.space})),env:process.env,self:Co()}}function X(e){return e.format===`json`?`json`:`toon`}function ll(e){let t=R(e);return t||q("no identity in this session — run `cyberlegion unit register` first"),fa(e,t),t}function Z(e){return e.option(`--space <path>`,`isolate the hub root (overrides the global hub / $CYBERLEGION_ROOT)`).addOption(new ve(`--format <format>`,`output format`).choices([`toon`,`json`]).default(`toon`))}function ul(e){return e.hook(`postAction`,async(e,t)=>{let n=t.opts();(t.name()!==`who`||n.reconcile)&&await Fs(Y(n).store)})}const Q=new we;Q.name(`cyberlegion`).description(`Harness-agnostic agent session spawning and messaging over the filesystem`).version(cl).enablePositionalOptions();const $=Q.command(`unit`).description(`legion units — register, discover, spawn, and reap`);function dl(e,t,n){if(!t.handle){let n=L(e.store).filter(e=>e.kind===`standing`);G(X(t),{toon:Ts(`agents`,n,[{key:`id`,get:e=>e.id},{key:`handle`,get:e=>e.handle},{key:`harness`,get:e=>e.harness??`-`},{key:`status`,get:e=>e.status}],`${n.length} standing`),json:n});return}let r=L(e.store).find(e=>e.handle===t.handle&&e.kind!==`standing`&&e.status!==`exited`),i=Yi(e,{handle:t.handle,home:n});r&&console.error(`a live session already claims handle "${t.handle}"`),G(X(t),{toon:W({id:i.id,handle:i.handle,kind:i.kind,status:i.status,home:i.home?.dir??`none`,...i.home?{launch:i.home.agent?`agent ${i.home.agent}`:`harness ${i.home.harness}`}:{}}),json:i})}ul(Z($.command(`register`))).description(`register or refresh this session identity (or --standing: a session-independent owner inbox)`).option(`--handle <name>`,`human handle for this agent`).option(`--harness <h>`,`claude | cursor | codex (else auto-detected)`).option(`--standing`,`mint a standing, session-independent owner inbox (bare, with no --handle: list them)`).option(`--home <dir>`,`with --standing: the existing folder a presence is spawned in when mail arrives and none is live`).option(`--agent <name>`,`with --home: launch that agent definition, resolved from the home`).option(`--clear-home`,`with --standing: drop the owner's home`).action(e=>{let t=Y(e),n;try{n=Ec(t.exec??P,e)}catch(e){q(e instanceof Error?e.message:String(e))}if(e.standing){dl(t,e,n);return}let r=Ki(t,{handle:e.handle,harness:e.harness});G(X(e),{toon:W({id:r.id,handle:r.handle,harness:r.harness??`-`,status:r.status}),json:r})}),Z($.command(`claim`)).description(`bind the caller's unit as a standing owner's presence; --show reads it, --clear unbinds`).argument(`<handle>`,`standing owner handle`).option(`--clear`,`unbind the presence (a no-op when nothing is bound)`).option(`--show`,`print the bound presence instead of claiming`).action((e,t)=>{let n=Y(t);if(t.show){let r;try{r=ca(n.store,e)}catch(e){q(e instanceof Error?e.message:String(e))}G(X(t),{toon:W({presence:r?.id??`none`}),json:{presence:r?.id??null}});return}if(t.clear){try{oa(n,e)}catch(e){q(e instanceof Error?e.message:String(e))}G(X(t),{toon:W({presence:`none`}),json:{presence:null}});return}let r;try{r=aa(n,e)}catch(e){q(e instanceof Error?e.message:String(e))}G(X(t),{toon:W({owner:r.handle,presence:r.presence}),json:{owner:r.handle,presence:r.presence}})}),Z($.command(`whoami`)).description(`print this session own identity`).action(e=>{let t=Y(e),n=R(t);n||q("no identity in this session — run `cyberlegion unit register` first");let r=F(t.store,n);r||q(`registered self id "${n}" has no agent record`),G(X(e),{toon:W({id:r.id,handle:r.handle,harness:r.harness??`-`,status:r.status}),json:r})});function fl(e){let t=Y(e);B(t),e.reconcile&&ya(t,{adopt:!0});let n=L(t.store).filter(t=>e.all||t.status!==`exited`);G(X(e),{toon:Ts(`units`,n,[{key:`id`,get:e=>e.id},{key:`handle`,get:e=>e.handle},{key:`harness`,get:e=>e.harness??`-`},{key:`status`,get:e=>e.status},{key:`pane`,get:e=>e.pane?.id??`-`}],`${n.length} units`),json:n}),n.length===0&&K(`cyberlegion unit register to join`)}ul(Z($.command(`who`))).description(`list the addressable units`).option(`--all`,`include exited units`).option(`--reconcile`,`live-probe the current mux: cull dead-pane records and adopt unbound harness-bearing panes before listing`).action(fl),ul(Z($.command(`prune`))).description(`mark dead units exited and sweep`).action(e=>{let t=Y(e);B(t);let n=xa(t);G(X(e),{toon:Ts(`pruned`,n,[{key:`id`,get:e=>e.id},{key:`handle`,get:e=>e.handle}],`${n.length} pruned`),json:n})});const pl=`(harness default)`;function ml(e){return e.effortNotApplied?`${e.effortNotApplied} (not applied)`:e.effort??pl}function hl(e){let{effortNotApplied:t,model:n}=e.launched;t&&console.error(n?`${e.input.harness} carries effort only in a model id that cursor-agent models lists, and it lists no "${n}-${t}"; effort "${t}" not applied — launching ${n} at its default effort (run cursor-agent models to pick an id)`:`${e.input.harness} carries effort only as a parameter on a named model and none was set; effort "${t}" not applied — launching at the harness default model and effort (pass --model to apply it)`)}function gl(e){return Z(e).option(`--harness <h>`,`claude | cursor | codex (required unless --agent/--agent-file resolves one)`).option(`--agent <name>`,`resolve an agent def (.agents/agents/<name>.md) for harness/model/effort/instructions`).option(`--agent-file <path>`,`read an exact agent def file instead of resolving by name`).option(`--model <name>`,`model for this launch only (flag > agent def > harness default)`).option(`--effort <level>`,`effort for this launch only (flag > agent def > harness default)`).option(`--task <text>`,`brief text, or - for stdin`).option(`--brief-file <path>`,`read the brief from a file`).option(`--handle <name>`,`handle for the new peer`).option(`--branch <name>`,`branch for the new worktree (default cyberlegion/unit-<id>)`).option(`--worktree-path <path>`,`where to check out the new worktree`).option(`-C, --repo <path>`,`create the worktree from the git repository containing <path>, not the current directory's`).option(`--base <ref>`,`start the new worktree's branch from <ref> (default: fetch origin and use its default branch, else local HEAD)`).option(`--cwd <path>`,`spawn the session in an existing directory; create no worktree (mutually exclusive with --branch/--worktree-path/--repo/--base)`).addOption(new ve(`--at <placement>`,`where to open the new session (default: new-worktree → workspace, --cwd → tab)`).choices([`pane:right`,`pane:down`,`tab`,`workspace`])).option(`--no-wake`,`suppress the first-turn doorbell (spawn idle; the caller drives the first turn itself)`)}function _l(e){if(e.trustBlocked){if(console.error(e.trustBlocked),e.agent.brief){let t=Ma(e.agent.brief).replaceAll(`'`,`'\\''`);console.error(`then: cyberlegion unit nudge ${e.agent.handle} --message '${t}'`)}process.exitCode=1}}function vl(e){e.base?.fallback&&console.error(`base: fell back to local HEAD — ${e.base.fallback}`)}function yl(e){return ul(gl(e)).description(`launch a new peer session in its own git worktree (tmux or herdr)`).action(async e=>{let t=Y(e);B(t);let n;try{n=Jo(e,Zo)}catch(e){q(e instanceof Error?e.message:String(e))}hl(n);let r=await po(t,n.input,{noWake:n.noWake});r.warning&&console.error(`first-turn doorbell not confirmed (peer still spawned; nudge it manually): ${r.warning}`),_l(r),vl(r),G(X(e),{toon:W({spawned:r.agent.id,handle:r.agent.handle,harness:r.agent.harness,model:n.launched.model??pl,effort:ml(n.launched),worktree:r.agent.worktree?.root,reused:r.reused??`-`,base:r.base?.ref,pane:r.pane,rung:r.rung,trust:r.trust}),json:{agent:r.agent,pane:r.pane,launch:r.launch,base:r.base,...r.reused===void 0?{}:{reused:r.reused},model:n.launched.model??pl,effort:ml(n.launched),rung:r.rung,trust:r.trust}}),K(`cyberlegion unit read ${r.agent.id}`)})}yl($.command(`spawn`)),ul(Z($.command(`close`))).description(`release the unit's worktree lease (or remove a worktree it holds no lease on), tear down its session, and reap its state (the inverse of spawn)`).argument(`<id>`,`unit id, handle, or worktree branch/CR ref`).option(`--force`,`discard uncommitted changes in a worktree close removes (refused for a unit on the primary checkout)`).option(`--keep-worktree`,`leave the worktree on disk for reuse and reap everything else (skips the dirty check)`).action(async(e,t)=>{let n=Y(t);B(n);let r=z(n.store,e),i=await os(n,{id:r.id,force:t.force,keepWorktree:t.keepWorktree});G(X(t),{toon:W({closed:r.id,worktree:i.worktreeRoot??`-`,retained:i.retainedWorktree??`-`,lease:i.lease?i.lease.released?`released`:i.lease.reason:`-`,pane:i.pane??`-`}),json:i})}),Z($.command(`stop`)).description(`end a unit's session and keep the unit — its id, handle, inbox, brief, and worktree stay (unit close destroys them)`).argument(`<ref>`,`unit id, handle, or worktree branch/CR ref`).action((e,t)=>{let n=Y(t);B(n);let r=Qc(n,e);G(X(t),{toon:W({stopped:r.agent.id,pane:r.pane??`-`,verified:r.verified,already:r.alreadyStopped}),json:{stopped:r.agent.id,pane:r.pane??null,verified:r.verified,alreadyStopped:r.alreadyStopped}}),r.alreadyStopped||K(`cyberlegion unit restart ${r.agent.handle}`)}),Z($.command(`restart`)).description(`give a unit a new session and keep the unit — stops a running session, relaunches it resuming the harness's recorded conversation where it can, and otherwise rebriefs it`).argument(`<ref>`,`unit id, handle, or worktree branch/CR ref`).option(`--no-wake`,`do not ring the new session to read its brief (the caller briefs it by mail)`).option(`--fresh`,`start an empty session and rebrief it, even when the unit's conversation could be resumed`).action(async(e,t)=>{let n=Y(t);B(n);let r=await $c(n,e,{noWake:t.wake===!1,fresh:t.fresh===!0});G(X(t),{toon:W({restarted:r.agent.id,previous:r.previousPane??`-`,pane:r.pane,resumed:r.resumed,rung:r.rung}),json:r}),r.warning&&K(`warning: ${r.warning}`)}),Z($.command(`rebind`)).description(`bind a stopped unit to the calling pane — for a session started by hand that should be this unit`).argument(`<ref>`,`unit id, handle, or worktree branch/CR ref`).action((e,t)=>{let n=tl(Y(t),e);G(X(t),{toon:W({rebound:n.id,pane:n.pane?.id??`-`}),json:{rebound:n.id,pane:n.pane}})}),Z($.command(`show`)).description(`show a unit's runtime: where it is, whether it is live now, and the controls that work on it (read-only)`).argument(`<ref>`,`unit id, handle, or worktree branch/CR ref`).action((e,t)=>{let n=Y(t);B(n);let r=il(n,e);G(X(t),{toon:W({id:r.id,handle:r.handle,harness:r.harness??`-`,status:r.status,liveness:r.liveness,pane:r.pane?`${r.pane.mux}:${r.pane.id}`:`-`,cwd:r.cwd,worktree:r.worktree?.root??`-`,lastSeen:r.lastSeen,controls:r.controls.join(`,`)||`-`}),json:r})}),Z($.command(`focus`)).description(`move input focus to a peer's session, named by a ref or picked by its worktree's repo/branch`).argument(`[ref]`,`unit id, handle, or worktree branch/CR ref`).option(`--repo <name>`,`pick the one live unit in this repository (its primary checkout's folder name, else its cwd's)`).option(`--branch <name>`,`pick the one live unit whose worktree is on this branch`).action((e,t)=>{let n={repo:t.repo,branch:t.branch};e!==void 0&&(n.repo!==void 0||n.branch!==void 0)&&q(`a ref and --repo/--branch cannot be combined — pass one or the other`);let r=Y(t);B(r);let{agent:i,pane:a}=Eo(r,e??n),o=e??i.id;G(X(t),{toon:W({focused:o,pane:a}),json:{ref:o,pane:a}})}),Z($.command(`nudge`)).description(`deliver text as a turn in a peer's session — by default a doorbell telling them to check their mail; --message relays any text (e.g. a decision) instead`).argument(`<ref>`,`unit id, handle, or worktree branch/CR ref`).option(`--message <text>`,`text delivered as a turn in the peer session`,ja).action(async(e,t)=>{let n=Y(t);B(n);let{pane:r,message:i,resubmits:a}=await Do(n,e,{message:t.message});G(X(t),{toon:W({nudged:e,pane:r}),json:{ref:e,pane:r,message:i,resubmits:a}})}),Z($.command(`read`)).description(`scrape a peer's session screen`).argument(`<ref>`,`unit id, handle, or worktree branch/CR ref`).option(`--lines <n>`,`trailing lines to capture`,e=>Number.parseInt(e,10)).action((e,t)=>{let n=Y(t);B(n);let{pane:r,output:i}=Oo(n,e,{lines:t.lines});console.log(Yo(X(t),{ref:e,pane:r,output:i}))}),Z($.command(`clear`)).description(`reset a warm peer's context to cold by injecting its own harness fresh-context command — keeps the pane/session warm; tears down nothing`).argument(`<ref>`,`unit id, handle, or worktree branch/CR ref`).action(async(e,t)=>{let n=Y(t);B(n);let r;try{r=await ko(n,e)}catch(e){q(e instanceof Error?e.message:String(e))}G(X(t),{toon:W({cleared:e,pane:r.pane,command:r.command}),json:{cleared:e,pane:r.pane,command:r.command}})});const bl=Q.command(`project`).description(`register and resolve projects (one per git repository)`);function xl(e){return{id:e.id,name:e.name,root:e.root}}async function Sl(e,t,n){let r=await Ms(e.store,n,e.exec??P),i=r?{...n,subject:r.subject}:n;G(X(t),{toon:W({...xl(i),...r?{channel:r.channel?.handle??`none`}:{}}),json:r?{...i,...r.channel?{channel:r.channel}:{}}:i})}Z(bl.command(`register`)).description(`register (or refresh) the project containing a directory — any checkout of it`).option(`--dir <path>`,`a directory inside the project (default: the current directory)`).action(async e=>{let t=Y(e),n;try{n=Ys({store:t.store},{dir:e.dir})}catch(e){q(e instanceof Error?e.message:String(e))}await Sl(t,e,n)}),Z(bl.command(`list`)).description(`list the registered projects`).action(e=>{let t=Xs(Y(e).store);G(X(e),{toon:Ts(`projects`,t,[{key:`id`,get:e=>e.id},{key:`name`,get:e=>e.name},{key:`root`,get:e=>e.root}],`${t.length} projects`),json:t}),t.length===0&&K(`cyberlegion project register to add the current repository`)}),Z(bl.command(`show`)).description(`resolve a registered project by id, path, or unique name`).argument(`<ref>`,`project id, a path inside any of its checkouts, or its name`).action(async(e,t)=>{let n=Y(t),r;try{r=$s({store:n.store},e)}catch(e){q(e instanceof Error?e.message:String(e))}await Sl(n,t,r)});const Cl=Q.command(`service`).description(`project services — one authoritative owner each, fenced by generation`);function wl(e){return{project:e.project.id,service:e.lease.service,endpoint:e.endpoint.id,generation:e.lease.generation,state:e.lease.state,holder:e.lease.holder??null,health:e.health,control:e.control,...e.note?{note:e.note}:{}}}function Tl(e){return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,t??`-`]))}function El(e,t){G(X(e),{toon:W(Tl(t)),json:t})}function Dl(e){try{return e()}catch(e){q(e instanceof Error?e.message:String(e))}}const Ol=e=>Number.parseInt(e,10);function kl(e,t){return t===void 0?[`.`,e]:[e,t]}function Al(e,t){return t?Dl(()=>z(e.store,t)).id:ll(e)}Z(Cl.command(`resolve`)).description(`show a service's owner, generation, health, and whether its session is controllable`).argument(`<project-or-name>`,`project id, path, or unique name — omit to use the current directory`).argument(`[name]`,`service name`).action((e,t,n)=>{let[r,i]=kl(e,t),a=Y(n);El(n,wl(Dl(()=>vc(a,r,i))))}),Z(Cl.command(`acquire`)).description(`resolve the healthy owner, or reserve the right to start one (exactly one caller wins)`).argument(`<project-or-name>`,`project id, path, or unique name — omit to use the current directory`).argument(`[name]`,`service name`).option(`--ttl <ms>`,`how long the reservation holds before another caller may take over`,Ol).option(`--force-generation <n>`,`reserve even over a healthy owner — must name the current generation`,Ol).action((e,t,n)=>{let[r,i]=kl(e,t),a=Y(n),o=Dl(()=>yc(a,r,i,{by:R(a),ttlMs:n.ttl,...n.forceGeneration===void 0?{}:{force:{generation:n.forceGeneration}}}));El(n,{outcome:o.outcome,...wl(o.view),...o.outcome===`reserved`?{token:o.token,expiresAt:o.lease.reservation?.expiresAt}:{}}),o.outcome===`reserved`?K(`start the runtime, then: cyberlegion service bind ${r} ${i} --generation ${o.lease.generation} --token ${o.token}`):o.outcome===`starting`&&K(`another caller is starting it — re-run to resolve: cyberlegion service resolve ${r} ${i}`)}),Z(Cl.command(`bind`)).description(`complete a reservation: make a live unit (default: this session) the owner`).argument(`<project-or-name>`,`project id, path, or unique name — omit to use the current directory`).argument(`[name]`,`service name`).requiredOption(`--generation <n>`,`the reserved generation`,Ol).requiredOption(`--token <token>`,`the reservation token from acquire`).option(`--unit <ref>`,`the unit to bind (default: this session)`).action((e,t,n)=>{let[r,i]=kl(e,t),a=Y(n),o=Al(a,n.unit);El(n,wl(Dl(()=>xc(a,r,i,{generation:n.generation,token:n.token,unit:o}))))}),Z(Cl.command(`release`)).description(`abandon a reservation (--token) or step down as owner (default: this session)`).argument(`<project-or-name>`,`project id, path, or unique name — omit to use the current directory`).argument(`[name]`,`service name`).requiredOption(`--generation <n>`,`the current generation`,Ol).option(`--token <token>`,`release a reservation instead of an active ownership`).option(`--unit <ref>`,`the owning unit (default: this session)`).action((e,t,n)=>{let[r,i]=kl(e,t),a=Y(n),o=n.token?{generation:n.generation,token:n.token}:{generation:n.generation,unit:Al(a,n.unit)};El(n,wl(Dl(()=>Sc(a,r,i,o))))}),Z(Cl.command(`handoff`)).description(`transfer ownership to another live unit under a new generation`).argument(`<project-or-name>`,`project id, path, or unique name — omit to use the current directory`).argument(`[name]`,`service name`).requiredOption(`--generation <n>`,`the current generation`,Ol).requiredOption(`--to <ref>`,`the unit taking over`).option(`--from <ref>`,`the current owner (default: this session)`).action((e,t,n)=>{let[r,i]=kl(e,t),a=Y(n),o=Al(a,n.from),s=Dl(()=>z(a.store,n.to)).id;El(n,wl(Dl(()=>Cc(a,r,i,{generation:n.generation,from:o,to:s}))))}),Z(Cl.command(`verify`)).description(`the fencing check: exit 0 only when the unit (default: this session) owns the service at this generation`).argument(`<project-or-name>`,`project id, path, or unique name — omit to use the current directory`).argument(`[name]`,`service name`).requiredOption(`--generation <n>`,`the generation the caller believes it owns`,Ol).option(`--unit <ref>`,`the unit to check (default: this session)`).action((e,t,n)=>{let[r,i]=kl(e,t),a=Y(n),o=Al(a,n.unit);El(n,wl(Dl(()=>wc(a,r,i,{unit:o,generation:n.generation}))))}),ul(gl(Cl.command(`start`))).description(`resolve the healthy owner, or spawn one peer and bind it — concurrent starts launch once`).argument(`<project-or-name>`,`project id, path, or unique name — omit to use the current directory`).argument(`[name]`,`service name`).option(`--ttl <ms>`,`how long the reservation holds while the peer launches`,Ol).action(async(e,t,n)=>{let[r,i]=kl(e,t),a=Y(n);B(a);let o;try{o=Jo(n,Zo)}catch(e){q(e instanceof Error?e.message:String(e))}hl(o);let s,c;try{c=await Tc(a,r,i,{by:R(a),ttlMs:n.ttl,launch:async()=>{let e=await po(a,o.input,{noWake:o.noWake});return s=e.warning,_l(e),vl(e),{unit:e.agent.id,pane:e.pane}}})}catch(e){q(e instanceof Error?e.message:String(e))}s&&console.error(`first-turn doorbell not confirmed (peer still spawned; nudge it manually): ${s}`),El(n,{outcome:c.outcome,...wl(c.view)}),c.outcome===`starting`&&K(`another caller is starting it — re-run to resolve: cyberlegion service resolve ${r} ${i}`)});const jl=Q.command(`mail`).description(`durable inter-agent messaging`);function Ml(e){return Z(e).description(`send a message to a peer (by handle or id)`).requiredOption(`--to <peer>`,`recipient handle or id`).option(`--from <id>`,`sender id (else this session own identity)`).option(`--subject <s>`,`subject`).option(`--body <text>`,`message body`).option(`--body-file <path>`,`read body from a file, or - for stdin`).option(`--thread <id>`,`thread id`).option(`--reply-to <msg>`,`message id this replies to`).option(`--no-nudge`,`suppress the delivery doorbell (do not wake the recipient's pane)`).action(async e=>{let t=Y(e),n=e.from??ll(t),r=$o(e.body,e.bodyFile),i=Qo({store:t.store},{fromId:n,to:e.to,subject:e.subject,body:r,thread:e.thread,replyTo:e.replyTo}),a=await Ba(t.store,()=>V(t.env??process.env),P,{toId:i.to,fromId:n,noNudge:e.nudge===!1,spawnHome:e=>Ws(t,e)});a.warning&&console.error(`delivery doorbell not confirmed (message still delivered): ${a.warning}`),G(X(e),{toon:W({sent:i.id,to:e.to,subject:i.subject,rung:a.rung,...a.spawned?{spawned:a.spawned}:{}}),json:{...i,rung:a.rung,...a.spawned?{spawned:a.spawned}:{}}})})}Ml(jl.command(`send`));function Nl(e,t,n,r){let i=es({store:e.store},{meId:n,unread:t.unread,from:t.from,thread:t.thread}),a=i.filter(e=>!e.read).length;G(X(t),{toon:Ts(`messages`,i,[{key:`id`,get:e=>e.id},{key:`from`,get:e=>e.fromHandle},{key:`subject`,get:e=>e.subject??``},{key:`read`,get:e=>e.read}],`${i.length} messages (${a} unread)`),json:i});let o=i.find(e=>!e.read);o&&K(`${r} ${o.id}`)}function Pl(e){let t=Y(e);B(t),Nl(t,e,e.owner?ia(t.store,e.owner):ll(t),`cyberlegion mail read`)}Z(jl.command(`inbox`)).description(`list your mail`).option(`--unread`,`only un-acked mail`).option(`--from <id>`,`filter by sender`).option(`--thread <id>`,`filter to messages carrying this thread id`).option(`--owner <handle>`,`target a standing owner's mailbox instead of this session's own`).action(Pl),Z(jl.command(`read`)).description(`read a message; --ack consumes it in the same step, otherwise it only peeks`).argument(`<msg-id>`,`message id`).option(`--ack`,`acknowledge the message in the same step (idempotent — no error if already acked)`).option(`--owner <handle>`,`read a standing owner's mailbox instead of this session's own`).action((e,t)=>{let n=Y(t),r=t.owner?ia(n.store,t.owner):ll(n);if(t.ack){let{msg:i,acked:a}=rs({store:n.store},r,e);G(X(t),{toon:W({id:i.id,from:i.fromHandle,subject:i.subject,body:i.body,acked:a}),json:{...i,acked:a}});return}let i=ts({store:n.store},r,e);i||q(`"${e}" is not a message in this inbox`),G(X(t),{toon:W({id:i.id,from:i.fromHandle,subject:i.subject,body:i.body}),json:i}),K(`cyberlegion mail ack ${i.id}${t.owner?` --owner ${t.owner}`:``}`)}),Z(jl.command(`ack`)).description(`acknowledge a message (moves it out of the unread set)`).argument(`<msg-id>`,`message id`).option(`--owner <handle>`,`ack a standing owner's mailbox instead of this session's own`).action((e,t)=>{let n=Y(t),r=t.owner?ia(n.store,t.owner):ll(n),i=ns({store:n.store},r,e);G(X(t),{toon:W({acked:i.id,from:i.fromHandle,subject:i.subject}),json:i})}),Z(jl.command(`delete`)).description(`permanently remove a message from your inbox (unread or already-acked)`).argument(`<msg-id>`,`message id`).action((e,t)=>{let n=Y(t),r=ll(n);is({store:n.store},r,e),G(X(t),{toon:W({deleted:e}),json:{deleted:e}})}),Z(jl.command(`await`)).description(`block until a thread-correlated reply arrives, print it, and ack it`).requiredOption(`--thread <id>`,`thread id to wait on`).option(`--from <h>`,`only match a reply from this sender`).option(`--timeout <ms>`,`give up after this many ms with no match (0 = wait forever); exits non-zero on timeout`,e=>Number.parseInt(e,10),6e5).option(`--max-wait <s>`,`self-cap for one internal poll cycle, in seconds — returns the clean "waiting" sentinel at this cap so the caller can re-arm rather than blocking past a harness tool-timeout`,e=>Number.parseInt(e,10),240).addHelpText(`after`,`
|
|
74
74
|
Three outcomes:
|
|
75
75
|
matched — exit 0; the message is printed on stdout and acked (moved out of the unread set).
|
|
76
76
|
waiting — exit 0; a stderr "waiting" line and nothing on stdout — the per-call --max-wait
|