@floh-solutions/pharos-cli 0.31.1 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -6
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +31 -9
- package/dist/cli.js.map +1 -1
- package/dist/commands/delegate.d.ts +77 -12
- package/dist/commands/delegate.d.ts.map +1 -1
- package/dist/commands/delegate.js +392 -24
- package/dist/commands/delegate.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +81 -2
- package/dist/commands/doctor.js.map +1 -1
- package/dist/delegate/argus.d.ts +327 -0
- package/dist/delegate/argus.d.ts.map +1 -0
- package/dist/delegate/argus.js +611 -0
- package/dist/delegate/argus.js.map +1 -0
- package/package.json +3 -3
- package/skill/SKILL.md +16 -5
- package/vscode/pharos-bridge.json +1 -1
- package/vscode/pharos-bridge.vsix +0 -0
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
import { connect } from "node:net";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* The Argus board socket — the route to a coding agent running in Navarch.
|
|
6
|
+
*
|
|
7
|
+
* ## Why there is a fourth route at all
|
|
8
|
+
*
|
|
9
|
+
* Terminal takes an Apple Event and VS Code takes a URI, because in both the
|
|
10
|
+
* thing that owns the terminal is the application. Navarch owns neither: a
|
|
11
|
+
* worker's PTY belongs to `argusd`, a daemon that outlives the app, and the
|
|
12
|
+
* app is what watches a screen settle before typing into it. So the delegation
|
|
13
|
+
* goes over `argusd`'s own socket as one verb — `delegate` — and the two
|
|
14
|
+
* halves split where the knowledge is: the daemon validates and does the find,
|
|
15
|
+
* the app starts a worker if there is none, waits for a screen that can take
|
|
16
|
+
* text, types, and reports back.
|
|
17
|
+
*
|
|
18
|
+
* The contract is `docs/delegation-plan.md` in the argus repo (board #1378).
|
|
19
|
+
* Everything below was measured against an isolated `argusd` built from argus
|
|
20
|
+
* master `2aaefd3` on 2026-09-11; the fixtures in `test/argus.test.ts` are that
|
|
21
|
+
* daemon's replies verbatim.
|
|
22
|
+
*
|
|
23
|
+
* ## Spoken directly, not through `argus-board`
|
|
24
|
+
*
|
|
25
|
+
* `~/.config/argus/bin/argus-board` frames exactly this — one newline-
|
|
26
|
+
* terminated JSON object in, one line back — and shelling out to it would have
|
|
27
|
+
* been half the code. It is wrong here for one measured reason: **that script
|
|
28
|
+
* stamps the CALLER'S identity onto every request**, from the environment.
|
|
29
|
+
* `_agent` from `$ARGUS_AGENT`, `_worker` from `$ARGUS_WORKER`, plus `_sub`,
|
|
30
|
+
* `_persona` and `_mission`. A `pharos delegate` run inside an Argus worker —
|
|
31
|
+
* which is precisely where Pharos.app's Ask Agent runs it — would therefore
|
|
32
|
+
* raise an approval card naming that worker, and file the ledger entry and the
|
|
33
|
+
* outcome note under it. The card is the whole point of the dial: it has to
|
|
34
|
+
* read *Pharos wants to hand work to a claude worker in …*, which is a
|
|
35
|
+
* question a person can answer.
|
|
36
|
+
*
|
|
37
|
+
* Speaking the socket means exactly the keys below travel, and nothing this
|
|
38
|
+
* process happens to have been started with. It also lets a park hold for the
|
|
39
|
+
* daemon's full wait (the script's own deadline is `wait + 5`, tuned for the
|
|
40
|
+
* board's reads) and keeps a transport failure distinguishable from a refusal,
|
|
41
|
+
* which a script that prints one line for both cannot be.
|
|
42
|
+
*
|
|
43
|
+
* ## `ARGUS_BOARD_SOCK` is honoured
|
|
44
|
+
*
|
|
45
|
+
* The same variable `argus-board` reads. It is how an isolated daemon is
|
|
46
|
+
* reached, and a session pointed at one should not have this verb quietly
|
|
47
|
+
* talking to the live fleet instead.
|
|
48
|
+
*/
|
|
49
|
+
/** Where `argusd` binds its board socket, unless the environment says otherwise. */
|
|
50
|
+
export function argusSocketPath(env = process.env) {
|
|
51
|
+
const override = (env["ARGUS_BOARD_SOCK"] ?? "").trim();
|
|
52
|
+
if (override !== "")
|
|
53
|
+
return override;
|
|
54
|
+
return join(env["HOME"] ?? homedir(), ".config", "argus", "argus-board.sock");
|
|
55
|
+
}
|
|
56
|
+
/** The one-line limit the daemon holds `text` to (`BoardStore.sendTextLimit`). */
|
|
57
|
+
export const ARGUS_TEXT_LIMIT = 4000;
|
|
58
|
+
/**
|
|
59
|
+
* How long to ask the daemon to park, in seconds.
|
|
60
|
+
*
|
|
61
|
+
* **60, always, and never the default 10.** Two measured reasons. A delegation
|
|
62
|
+
* onto an empty folder has to start a worker and wait out a cold agent's first
|
|
63
|
+
* screen — `FleetControl.delegateSpawnWindow` is 45s — so a ten-second answer
|
|
64
|
+
* is `pending` with the real outcome landing half a minute later in a reply
|
|
65
|
+
* nobody is reading. And on the default autonomy dial the first hand-off on a
|
|
66
|
+
* machine raises an approval card, which answers `pending` the instant it goes
|
|
67
|
+
* up whatever the wait is; the wait is what catches the person who clicks
|
|
68
|
+
* *Allow* straight away, and ten seconds does not.
|
|
69
|
+
*
|
|
70
|
+
* 60 is also the daemon's ceiling (`maxFleetWait`), so this is "as long as it
|
|
71
|
+
* will hold", not a number picked here.
|
|
72
|
+
*/
|
|
73
|
+
export const ARGUS_WAIT_SECONDS = 60;
|
|
74
|
+
/** What the request is stamped with. The card names this; an anonymous one reads "A program outside the fleet". */
|
|
75
|
+
export const ARGUS_AGENT = "Pharos";
|
|
76
|
+
/**
|
|
77
|
+
* Decode one line. Anything that is not a JSON object with a boolean-ish `ok`
|
|
78
|
+
* is a transport fault rather than an answer, and is reported as one.
|
|
79
|
+
*/
|
|
80
|
+
export function parseArgusReply(line) {
|
|
81
|
+
let parsed;
|
|
82
|
+
try {
|
|
83
|
+
parsed = JSON.parse(line);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
89
|
+
return null;
|
|
90
|
+
const row = parsed;
|
|
91
|
+
if (row["ok"] === true) {
|
|
92
|
+
const data = row["data"];
|
|
93
|
+
return { ok: true, data: isRecord(data) ? data : {} };
|
|
94
|
+
}
|
|
95
|
+
if (row["ok"] === false) {
|
|
96
|
+
return {
|
|
97
|
+
ok: false,
|
|
98
|
+
reason: typeof row["reason"] === "string" ? row["reason"] : null,
|
|
99
|
+
error: typeof row["error"] === "string" ? row["error"] : "",
|
|
100
|
+
data: isRecord(row["data"]) ? row["data"] : null,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The socket was there and the conversation still failed — a park that outlived
|
|
107
|
+
* its own deadline, a daemon that closed mid-answer, a line that is not JSON.
|
|
108
|
+
*
|
|
109
|
+
* Separate from a refusal on purpose: a refusal is a fact about this machine
|
|
110
|
+
* with a fix on it, and this is the CLI's ordinary failure.
|
|
111
|
+
*/
|
|
112
|
+
export class ArgusTransportError extends Error {
|
|
113
|
+
code;
|
|
114
|
+
constructor(message, code) {
|
|
115
|
+
super(message);
|
|
116
|
+
this.name = "ArgusTransportError";
|
|
117
|
+
this.code = code;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** Nothing is listening: the socket file is absent, or the connect was refused. */
|
|
121
|
+
export class ArgusUnreachableError extends Error {
|
|
122
|
+
code;
|
|
123
|
+
socketPath;
|
|
124
|
+
constructor(message, code, socketPath) {
|
|
125
|
+
super(message);
|
|
126
|
+
this.name = "ArgusUnreachableError";
|
|
127
|
+
this.code = code;
|
|
128
|
+
this.socketPath = socketPath;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* A connect that was REFUSED is retried; one that answered is not.
|
|
133
|
+
*
|
|
134
|
+
* `ECONNREFUSED` on a unix socket does not mean "nothing is listening". A
|
|
135
|
+
* socket whose accept backlog is full refuses with the same errno, and argus
|
|
136
|
+
* measured 48 of 64 concurrent connects refused by a daemon that was running
|
|
137
|
+
* and answered normally seconds later (`argus-board`, #742). So a refusal is
|
|
138
|
+
* worth one more try — and it is the one failure that is safe to repeat for a
|
|
139
|
+
* WRITE, because a connect that was never accepted sent no bytes and there is
|
|
140
|
+
* nothing to duplicate.
|
|
141
|
+
*/
|
|
142
|
+
const CONNECT_ATTEMPTS = 3;
|
|
143
|
+
const CONNECT_BACKOFF_MS = 120;
|
|
144
|
+
export function systemArgusDeps(env = process.env) {
|
|
145
|
+
const socketPath = argusSocketPath(env);
|
|
146
|
+
return {
|
|
147
|
+
ask: async (request, timeoutMs) => {
|
|
148
|
+
let last = null;
|
|
149
|
+
for (let attempt = 0; attempt < CONNECT_ATTEMPTS; attempt += 1) {
|
|
150
|
+
try {
|
|
151
|
+
return await askOnce(socketPath, request, timeoutMs);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (!(error instanceof ArgusUnreachableError) || error.code !== "ECONNREFUSED")
|
|
155
|
+
throw error;
|
|
156
|
+
last = error;
|
|
157
|
+
await pause(CONNECT_BACKOFF_MS * (attempt + 1));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
throw last ?? new ArgusUnreachableError("the Argus socket refused every attempt", "ECONNREFUSED", socketPath);
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function pause(ms) {
|
|
165
|
+
return new Promise((resolve) => {
|
|
166
|
+
setTimeout(resolve, ms).unref?.();
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
function askOnce(socketPath, request, timeoutMs) {
|
|
170
|
+
return new Promise((resolve, reject) => {
|
|
171
|
+
const socket = connect({ path: socketPath });
|
|
172
|
+
let buffer = "";
|
|
173
|
+
let settled = false;
|
|
174
|
+
const done = (error, line) => {
|
|
175
|
+
if (settled)
|
|
176
|
+
return;
|
|
177
|
+
settled = true;
|
|
178
|
+
socket.destroy();
|
|
179
|
+
if (error !== null)
|
|
180
|
+
reject(error);
|
|
181
|
+
else
|
|
182
|
+
resolve(line);
|
|
183
|
+
};
|
|
184
|
+
// An IDLE timeout, which is the right one: the daemon holds the reply for
|
|
185
|
+
// the whole park and sends nothing at all in the meantime, so any data is
|
|
186
|
+
// the answer and no data for this long is a park that outlived its own
|
|
187
|
+
// deadline.
|
|
188
|
+
socket.setTimeout(timeoutMs, () => {
|
|
189
|
+
done(new ArgusTransportError(`argusd did not answer within ${Math.round(timeoutMs / 1000)}s`, "ETIMEDOUT"));
|
|
190
|
+
});
|
|
191
|
+
socket.on("connect", () => {
|
|
192
|
+
socket.write(`${JSON.stringify(request)}\n`);
|
|
193
|
+
});
|
|
194
|
+
socket.on("data", (chunk) => {
|
|
195
|
+
buffer += chunk.toString("utf8");
|
|
196
|
+
const newline = buffer.indexOf("\n");
|
|
197
|
+
if (newline >= 0)
|
|
198
|
+
done(null, buffer.slice(0, newline));
|
|
199
|
+
});
|
|
200
|
+
socket.on("error", (error) => {
|
|
201
|
+
const code = error.code ?? "";
|
|
202
|
+
if (code === "ENOENT" || code === "ECONNREFUSED") {
|
|
203
|
+
done(new ArgusUnreachableError(error.message, code, socketPath));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
done(new ArgusTransportError(error.message, code === "" ? "EIO" : code));
|
|
207
|
+
});
|
|
208
|
+
socket.on("close", () => {
|
|
209
|
+
// Closed without a newline. A line that arrived whole but unterminated is
|
|
210
|
+
// still an answer; nothing at all is a daemon that hung up mid-reply.
|
|
211
|
+
if (buffer.trim() !== "")
|
|
212
|
+
done(null, buffer.trim());
|
|
213
|
+
else
|
|
214
|
+
done(new ArgusTransportError("argusd closed the connection without answering", "EPIPE"));
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The refusal ids this route adds to `delegate`'s closed set.
|
|
220
|
+
*
|
|
221
|
+
* Every one names a DIFFERENT fix, which is the whole reason they are told
|
|
222
|
+
* apart — the app renders a fact and the fix beside it, and two states that
|
|
223
|
+
* share a word send somebody to the wrong Settings pane. `unsupported` and
|
|
224
|
+
* `folder-missing` are not here because the verb already has them and they
|
|
225
|
+
* mean the same thing on this route.
|
|
226
|
+
*/
|
|
227
|
+
export const ARGUS_REASONS = [
|
|
228
|
+
/** No `argusd`. Nothing is listening on the socket — opening Navarch starts one. */
|
|
229
|
+
"argusd-not-running",
|
|
230
|
+
/** `argusd` is running and predates the verb. The fix is a reload, which ends every live session. */
|
|
231
|
+
"argusd-outdated",
|
|
232
|
+
/** `argusd` is up, Navarch is not. Only the app can start or type into a worker. */
|
|
233
|
+
"navarch-not-running",
|
|
234
|
+
/** The gate: this request did not carry operator authority. */
|
|
235
|
+
"not-an-operator",
|
|
236
|
+
/** Twelve operator writes a minute, and this one was the thirteenth. */
|
|
237
|
+
"rate-limited",
|
|
238
|
+
/** A person said no at the approval card, or the dial is set to Never. */
|
|
239
|
+
"declined",
|
|
240
|
+
/** Navarch tried and nothing was delivered — a trust dialog, a blank screen, a worker that exited. */
|
|
241
|
+
"delivery-failed",
|
|
242
|
+
/** `--status` for a delegation this core does not hold: another core's, or one that aged out. */
|
|
243
|
+
"request-unknown",
|
|
244
|
+
/** The daemon refused for a reason this pharos has no id for. Its own reason and prose travel in the detail. */
|
|
245
|
+
"argus-refused",
|
|
246
|
+
];
|
|
247
|
+
/**
|
|
248
|
+
* Generic over what it is paired with, because three different reads return
|
|
249
|
+
* "the thing, or the refusal that stopped it": a delegation, a fleet, a list.
|
|
250
|
+
*/
|
|
251
|
+
export function isFailure(value) {
|
|
252
|
+
return "reason" in value && "detail" in value && "argus" in value;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* The delegation inside a reply, or the refusal it is instead.
|
|
256
|
+
*
|
|
257
|
+
* One function for all three answers — `delegate`, the end of its park and
|
|
258
|
+
* `delegate_status` — because the daemon builds all three from one builder and
|
|
259
|
+
* a caller that parsed them separately would be inventing a difference.
|
|
260
|
+
*/
|
|
261
|
+
export function readDelegation(reply) {
|
|
262
|
+
if (reply.ok)
|
|
263
|
+
return readRow(reply.data);
|
|
264
|
+
const failure = mapRefusal(reply);
|
|
265
|
+
// `declined` and `failed` are reported WITH the row, so the app can still say
|
|
266
|
+
// which worker and which folder it was about.
|
|
267
|
+
if (reply.data !== null && typeof reply.data["action"] === "string") {
|
|
268
|
+
failure.delegation = readRow(reply.data);
|
|
269
|
+
}
|
|
270
|
+
return failure;
|
|
271
|
+
}
|
|
272
|
+
function readRow(data) {
|
|
273
|
+
const action = data["action"];
|
|
274
|
+
return {
|
|
275
|
+
action: action === "sent" || action === "launched" ? action : "pending",
|
|
276
|
+
request: text(data["request"]),
|
|
277
|
+
worker: optional(data["worker"]),
|
|
278
|
+
name: optional(data["name"]),
|
|
279
|
+
created: data["created"] === true,
|
|
280
|
+
kind: text(data["kind"]),
|
|
281
|
+
directory: text(data["directory"]),
|
|
282
|
+
host: text(data["host"]),
|
|
283
|
+
todo: typeof data["todo"] === "number" ? data["todo"] : null,
|
|
284
|
+
detail: text(data["detail"]),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* A refusal, as an id this CLI's caller already renders.
|
|
289
|
+
*
|
|
290
|
+
* ## The rule that matters: **no `reason` key is an OLD DAEMON**
|
|
291
|
+
*
|
|
292
|
+
* `reason` arrived with `delegate` itself, so an `argusd` too old to serve the
|
|
293
|
+
* verb is also too old to stamp one. That is not a corner — it is the first
|
|
294
|
+
* thing this route meets on any machine whose daemon has not been reloaded,
|
|
295
|
+
* which on the machine this was written on was every machine the day it
|
|
296
|
+
* landed. And an old daemon says three different things, two of which accuse
|
|
297
|
+
* this CLI of a bug it does not have (measured against argus `2fa7bf0`, the
|
|
298
|
+
* commit before the verb):
|
|
299
|
+
*
|
|
300
|
+
* | what is attached | what comes back, with no `reason` |
|
|
301
|
+
* |---|---|
|
|
302
|
+
* | old daemon, old app or none | `unknown op 'delegate' — see argus-board help …` |
|
|
303
|
+
* | old daemon, **new Navarch publishing `delegate`** | `delegate requires a "todo" (id) — the work it serves` |
|
|
304
|
+
* | …the same, unstamped | `'delegate' is an Argus Agent action — this session was not started as one. …` |
|
|
305
|
+
*
|
|
306
|
+
* The middle row is the trap: an old daemon forwards any verb the app claims
|
|
307
|
+
* it can run, and that forwarder demands the `todo` this verb exists to
|
|
308
|
+
* remove. A caller that believed the prose would go and add one. So the test
|
|
309
|
+
* is the MISSING KEY and never the wording — the wording only chooses how
|
|
310
|
+
* sharply the detail can put it. A current daemon stamps a reason on every
|
|
311
|
+
* refusal, and argus asserts that in `Tests/delegate_test.py` precisely so this
|
|
312
|
+
* inference stays true.
|
|
313
|
+
*/
|
|
314
|
+
export function mapRefusal(reply) {
|
|
315
|
+
const argus = { reason: reply.reason, error: reply.error };
|
|
316
|
+
const said = reply.error.trim();
|
|
317
|
+
const because = said === "" ? "" : ` argusd said: ${said}`;
|
|
318
|
+
if (reply.reason === null) {
|
|
319
|
+
const shape = /^unknown op '/.test(said)
|
|
320
|
+
? "It does not know the verb at all"
|
|
321
|
+
: said.includes('requires a "todo"')
|
|
322
|
+
? "It is forwarding the verb to Navarch, which is newer than it is, and its forwarder still "
|
|
323
|
+
+ "demands the board todo this verb exists to do without — the message blames the request "
|
|
324
|
+
+ "and the request is fine"
|
|
325
|
+
: "It refused without the stable reason every current refusal carries";
|
|
326
|
+
return {
|
|
327
|
+
reason: "argusd-outdated",
|
|
328
|
+
detail: "The argusd running on this machine predates the delegate verb. "
|
|
329
|
+
+ `${shape}. Reload it — deliberately, because a reload ends every live worker session — `
|
|
330
|
+
+ `and Navarch will come back with it.${because}`,
|
|
331
|
+
argus,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
switch (reply.reason) {
|
|
335
|
+
case "unknown-op":
|
|
336
|
+
return {
|
|
337
|
+
reason: "argusd-outdated",
|
|
338
|
+
detail: "The argusd running on this machine does not serve the delegate verb. Reload it — "
|
|
339
|
+
+ "deliberately, because a reload ends every live worker session."
|
|
340
|
+
+ because,
|
|
341
|
+
argus,
|
|
342
|
+
};
|
|
343
|
+
case "no-core":
|
|
344
|
+
return {
|
|
345
|
+
reason: "navarch-not-running",
|
|
346
|
+
detail: "argusd is running but Navarch is not, and only the app can start a worker or type into "
|
|
347
|
+
+ "one. Open Navarch and try again."
|
|
348
|
+
+ because,
|
|
349
|
+
argus,
|
|
350
|
+
};
|
|
351
|
+
case "unsupported":
|
|
352
|
+
return {
|
|
353
|
+
reason: "unsupported",
|
|
354
|
+
detail: "The Navarch running on this machine is an older build that does not serve delegations. "
|
|
355
|
+
+ "Update it — `./scripts/make-app.sh --install` in the argus repo — and try again."
|
|
356
|
+
+ because,
|
|
357
|
+
argus,
|
|
358
|
+
};
|
|
359
|
+
case "not-an-operator":
|
|
360
|
+
return {
|
|
361
|
+
reason: "not-an-operator",
|
|
362
|
+
detail: "argusd refused the delegation for want of operator authority, which pharos stamps on "
|
|
363
|
+
+ "every request it sends. That means the daemon did not read the stamp — it is almost "
|
|
364
|
+
+ "always one that predates the verb."
|
|
365
|
+
+ because,
|
|
366
|
+
argus,
|
|
367
|
+
};
|
|
368
|
+
case "rate-limited":
|
|
369
|
+
return {
|
|
370
|
+
reason: "rate-limited",
|
|
371
|
+
detail: "argusd caps operator writes at twelve a minute and this delegation was over it. Wait a "
|
|
372
|
+
+ "minute and send it again; nothing was delivered."
|
|
373
|
+
+ because,
|
|
374
|
+
argus,
|
|
375
|
+
};
|
|
376
|
+
case "declined":
|
|
377
|
+
return {
|
|
378
|
+
reason: "declined",
|
|
379
|
+
detail: said === "" ? "The delegation was declined in Navarch. Nothing was delivered." : said,
|
|
380
|
+
argus,
|
|
381
|
+
};
|
|
382
|
+
case "failed":
|
|
383
|
+
return {
|
|
384
|
+
reason: "delivery-failed",
|
|
385
|
+
detail: said === "" ? "Navarch could not deliver the prompt. Nothing was typed." : said,
|
|
386
|
+
argus,
|
|
387
|
+
};
|
|
388
|
+
case "bad-directory":
|
|
389
|
+
return {
|
|
390
|
+
reason: "folder-missing",
|
|
391
|
+
detail: "argusd does not see that folder on this machine. The linked folder for this project has "
|
|
392
|
+
+ "moved or was never here — pick it again in Settings ▸ Agents."
|
|
393
|
+
+ because,
|
|
394
|
+
argus,
|
|
395
|
+
};
|
|
396
|
+
case "unknown-request":
|
|
397
|
+
return {
|
|
398
|
+
reason: "request-unknown",
|
|
399
|
+
detail: "argusd holds no delegation with that id — it was made on another core, or it has aged "
|
|
400
|
+
+ "out (delegations are kept for an hour). A status read cannot recover one; delegate "
|
|
401
|
+
+ "again."
|
|
402
|
+
+ because,
|
|
403
|
+
argus,
|
|
404
|
+
};
|
|
405
|
+
default:
|
|
406
|
+
return {
|
|
407
|
+
reason: "argus-refused",
|
|
408
|
+
detail: `argusd refused the delegation (${reply.reason}). This pharos has no fix for that reason, `
|
|
409
|
+
+ `so here is what it said verbatim: ${said === "" ? "(nothing)" : said}`,
|
|
410
|
+
argus,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* The request, built in one place.
|
|
416
|
+
*
|
|
417
|
+
* `host`, `name`, `stage` and `project` are deliberately never sent. `host`
|
|
418
|
+
* would name a remote machine and this CLI's `--host` means the application, so
|
|
419
|
+
* spelling one here would be a collision waiting to pick the wrong worker;
|
|
420
|
+
* `name` names a NEW worker, which Navarch derives better than a caller can;
|
|
421
|
+
* `project` and `stage` belong to a board this delegation does not have.
|
|
422
|
+
*/
|
|
423
|
+
export function delegateRequest(job) {
|
|
424
|
+
return {
|
|
425
|
+
op: "delegate",
|
|
426
|
+
_fleet: "1",
|
|
427
|
+
_agent: ARGUS_AGENT,
|
|
428
|
+
directory: job.directory,
|
|
429
|
+
kind: job.kind,
|
|
430
|
+
text: job.text,
|
|
431
|
+
wait: job.wait ?? ARGUS_WAIT_SECONDS,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
export function delegateStatusRequest(request, wait = 0) {
|
|
435
|
+
return { op: "delegate_status", _fleet: "1", _agent: ARGUS_AGENT, request, wait };
|
|
436
|
+
}
|
|
437
|
+
export function fleetStatusRequest() {
|
|
438
|
+
// A read, and the daemon never gates one — but it is stamped like every other
|
|
439
|
+
// request from here so a refusal cannot be about the stamp.
|
|
440
|
+
return { op: "fleet_status", _fleet: "1", _agent: ARGUS_AGENT };
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* How long to wait on the socket for a park of `wait` seconds.
|
|
444
|
+
*
|
|
445
|
+
* The daemon has to notice its own deadline and write a reply after it, so the
|
|
446
|
+
* read has to outlast the park or every timed-out delegation reads as a broken
|
|
447
|
+
* socket. Ten seconds of margin, the same shape `argus-board` uses (it allows
|
|
448
|
+
* five, for reads that are not parked on a person).
|
|
449
|
+
*/
|
|
450
|
+
export function readTimeoutMs(waitSeconds) {
|
|
451
|
+
return (waitSeconds + 10) * 1000;
|
|
452
|
+
}
|
|
453
|
+
export async function askArgus(deps, request, timeoutMs) {
|
|
454
|
+
const line = await deps.ask(request, timeoutMs);
|
|
455
|
+
const reply = parseArgusReply(line);
|
|
456
|
+
if (reply === null) {
|
|
457
|
+
throw new ArgusTransportError(`argusd answered with something that is not a reply: ${line.slice(0, 200)}`, "EBADMSG");
|
|
458
|
+
}
|
|
459
|
+
return reply;
|
|
460
|
+
}
|
|
461
|
+
/** The refusal for a socket nothing is listening on. */
|
|
462
|
+
export function unreachable(error) {
|
|
463
|
+
return {
|
|
464
|
+
reason: "argusd-not-running",
|
|
465
|
+
detail: `Nothing is listening on the Argus socket at ${error.socketPath} (${error.code}). argusd is `
|
|
466
|
+
+ "what holds a Navarch worker's terminal, and opening Navarch starts it — so open Navarch "
|
|
467
|
+
+ "and try again. If it IS open, the socket has moved: ARGUS_BOARD_SOCK overrides where this "
|
|
468
|
+
+ "looks.",
|
|
469
|
+
argus: { reason: null, error: `${error.code}: ${error.message}` },
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
function isRecord(value) {
|
|
473
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
474
|
+
}
|
|
475
|
+
function text(value) {
|
|
476
|
+
return typeof value === "string" ? value : "";
|
|
477
|
+
}
|
|
478
|
+
/** Empty is not absent, and on this wire the difference is load-bearing. */
|
|
479
|
+
function optional(value) {
|
|
480
|
+
return typeof value === "string" && value !== "" ? value : null;
|
|
481
|
+
}
|
|
482
|
+
export function parseFleet(reply) {
|
|
483
|
+
const workers = reply.data["workers"];
|
|
484
|
+
if (!Array.isArray(workers))
|
|
485
|
+
return [];
|
|
486
|
+
const rows = [];
|
|
487
|
+
for (const entry of workers) {
|
|
488
|
+
if (!isRecord(entry))
|
|
489
|
+
continue;
|
|
490
|
+
const id = text(entry["id"]);
|
|
491
|
+
if (id === "")
|
|
492
|
+
continue;
|
|
493
|
+
rows.push({
|
|
494
|
+
id,
|
|
495
|
+
name: text(entry["name"]),
|
|
496
|
+
kind: text(entry["kind"]),
|
|
497
|
+
status: text(entry["status"]),
|
|
498
|
+
stage: text(entry["stage"]),
|
|
499
|
+
directory: text(entry["directory"]),
|
|
500
|
+
host: text(entry["host"]),
|
|
501
|
+
todo: typeof entry["todo"] === "number" ? entry["todo"] : null,
|
|
502
|
+
fleet: entry["fleet"] === true,
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
return rows;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Is this worker a candidate for a delegation onto `directory`?
|
|
509
|
+
*
|
|
510
|
+
* `BoardStore.delegateTarget`, rule for rule. Two of the five exclusions are
|
|
511
|
+
* the interesting ones, and both look like perfectly good targets from here:
|
|
512
|
+
*
|
|
513
|
+
* - **a mission worker** runs in a shared base checkout and would match, and
|
|
514
|
+
* typing an unrelated work item into an agent mid-branch derails the todo it
|
|
515
|
+
* is holding;
|
|
516
|
+
* - **an Argus Agent** is a claude worker with no mission sitting in a base
|
|
517
|
+
* checkout — which is exactly the folder a person links in Pharos. A
|
|
518
|
+
* delegation landing there hands the work item to the thing that *drives* the
|
|
519
|
+
* fleet, in the session whose next turn can start missions and type into
|
|
520
|
+
* every other worker.
|
|
521
|
+
*
|
|
522
|
+
* **Mission-hood is weaker here than it is inside the daemon**, and the
|
|
523
|
+
* difference is worth knowing. The daemon tests `missionTodo` on the worker
|
|
524
|
+
* itself; `fleet_status` only writes `todo` onto the row when the board still
|
|
525
|
+
* holds that todo, so a mission whose todo was deleted reads as free here and
|
|
526
|
+
* is excluded there. The direction of the error is the safe one — this can
|
|
527
|
+
* only list or predict a worker the daemon would decline to use, never hide
|
|
528
|
+
* one it would pick — but it is why a prediction is all this is, and why the
|
|
529
|
+
* send itself never names a worker: the daemon does the find that counts.
|
|
530
|
+
*/
|
|
531
|
+
export function eligible(worker, kind, directory) {
|
|
532
|
+
return (worker.status !== "exited"
|
|
533
|
+
&& worker.kind === kind
|
|
534
|
+
&& worker.todo === null
|
|
535
|
+
&& !worker.fleet
|
|
536
|
+
// Local only. This CLI has no way to name a remote machine — `--host` here
|
|
537
|
+
// means the application — so a worker on one is never a candidate.
|
|
538
|
+
&& worker.host.trim() === ""
|
|
539
|
+
&& sameFolder(worker.directory, directory));
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Two spellings of one folder.
|
|
543
|
+
*
|
|
544
|
+
* The daemon compares `standardizingPath` on both sides, which expands `~`,
|
|
545
|
+
* drops `.`/`..` and a trailing slash and resolves the symlinked prefixes
|
|
546
|
+
* macOS ships (`/tmp` → `/private/tmp`) — but NOT an arbitrary symlink
|
|
547
|
+
* somebody made. That is why the caller canonicalises the folder with
|
|
548
|
+
* `realpath` before it gets here, the same way the Terminal route does; this
|
|
549
|
+
* is the rest of the daemon's normalisation, and it is case-insensitive
|
|
550
|
+
* because `caseInsensitiveCompare` is what the daemon uses.
|
|
551
|
+
*/
|
|
552
|
+
export function sameFolder(a, b) {
|
|
553
|
+
return standardize(a).toLowerCase() === standardize(b).toLowerCase();
|
|
554
|
+
}
|
|
555
|
+
function standardize(path) {
|
|
556
|
+
let value = path.trim();
|
|
557
|
+
if (value === "")
|
|
558
|
+
return "";
|
|
559
|
+
// `/tmp`, `/var` and `/etc` are symlinks into `/private` on macOS, and
|
|
560
|
+
// `standardizingPath` resolves exactly those three.
|
|
561
|
+
for (const prefix of ["/tmp", "/var", "/etc"]) {
|
|
562
|
+
if (value === prefix || value.startsWith(`${prefix}/`)) {
|
|
563
|
+
value = `/private${value}`;
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
while (value.length > 1 && value.endsWith("/"))
|
|
568
|
+
value = value.slice(0, -1);
|
|
569
|
+
return value;
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* The candidates for a delegation onto `directory`, in the order the daemon
|
|
573
|
+
* would pick them. `[0]` is the worker a plain delegate takes.
|
|
574
|
+
*
|
|
575
|
+
* **The tie-break is the id and never the status**, because two calls a second
|
|
576
|
+
* apart must pick the same pane. A worker that has not painted yet is worst —
|
|
577
|
+
* there is no box to type into — and everything else is equal, so `starting`
|
|
578
|
+
* sinks and the rest sort on a uuid that never changes.
|
|
579
|
+
*/
|
|
580
|
+
export function rankWorkers(workers, kind, directory) {
|
|
581
|
+
return workers
|
|
582
|
+
.filter((worker) => eligible(worker, kind, directory))
|
|
583
|
+
.sort((a, b) => {
|
|
584
|
+
const starting = Number(a.status === "starting") - Number(b.status === "starting");
|
|
585
|
+
if (starting !== 0)
|
|
586
|
+
return starting;
|
|
587
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
// MARK: - Does this daemon serve the verb at all?
|
|
591
|
+
/**
|
|
592
|
+
* The read that answers "has this argusd been reloaded since `delegate`
|
|
593
|
+
* landed?" without delegating anything.
|
|
594
|
+
*
|
|
595
|
+
* `delegate_status` is a pure read of the daemon's own table — not gated, not
|
|
596
|
+
* rate-limited, and about an id it minted — so asking it about one that cannot
|
|
597
|
+
* exist costs nothing and changes nothing. A current daemon answers
|
|
598
|
+
* `unknown-request`; one that predates the verb answers `unknown op
|
|
599
|
+
* 'delegate_status'` with no `reason` at all, which is the same tell the send
|
|
600
|
+
* path uses.
|
|
601
|
+
*
|
|
602
|
+
* It earns its round trip on `--list` and `--dry-run`, which otherwise read
|
|
603
|
+
* `fleet_status` — a verb every generation of the daemon serves — and would
|
|
604
|
+
* cheerfully list workers that a send is about to refuse to deliver to.
|
|
605
|
+
*/
|
|
606
|
+
export const PROBE_REQUEST = "00000000-0000-0000-0000-000000000000";
|
|
607
|
+
/** True when the reply says this daemon knows the delegate family. */
|
|
608
|
+
export function servesDelegate(reply) {
|
|
609
|
+
return reply.ok || reply.reason !== null;
|
|
610
|
+
}
|
|
611
|
+
//# sourceMappingURL=argus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"argus.js","sourceRoot":"","sources":["../../src/delegate/argus.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@floh-solutions/pharos-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"description": "Azure DevOps from a headless shell, for an agent: the whole context of a task in one call, and the wiki/comment verbs Microsoft's MCP server does not ship.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "FLOH Solutions",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"node": ">=22"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
+
"@floh-solutions/gh-core": "0.4.0",
|
|
28
29
|
"@floh-solutions/ado-core": "0.11.0",
|
|
29
|
-
"@floh-solutions/plan-to-board": "0.2.0"
|
|
30
|
-
"@floh-solutions/gh-core": "0.4.0"
|
|
30
|
+
"@floh-solutions/plan-to-board": "0.2.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^22.10.2",
|