@centralu/win32-x64 0.1.0-beta.50 → 0.1.0-beta.52
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/Centralu/centralu.exe
CHANGED
|
Binary file
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"commit": "
|
|
2
|
+
"commit": "62e7369",
|
|
3
3
|
"protocolVersion": 1,
|
|
4
4
|
"hostProof": 1,
|
|
5
5
|
"tokenOnStdin": 1,
|
|
6
6
|
"verifiers": 1,
|
|
7
7
|
"keyPairing": 1,
|
|
8
|
-
"builtAt": "2026-11-
|
|
8
|
+
"builtAt": "2026-11-07T06:59:11.147Z",
|
|
9
9
|
"runtime": "system-node",
|
|
10
10
|
"platform": "win32",
|
|
11
11
|
"arch": "x64"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "centralu",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.52",
|
|
4
4
|
"description": "Run, watch and steer several Claude Code and Codex CLI sessions from one window",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "ijun17",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"win32"
|
|
39
39
|
],
|
|
40
40
|
"optionalDependencies": {
|
|
41
|
-
"@centralu/darwin-arm64": "0.1.0-beta.
|
|
42
|
-
"@centralu/linux-arm64": "0.1.0-beta.
|
|
43
|
-
"@centralu/linux-x64": "0.1.0-beta.
|
|
44
|
-
"@centralu/win32-x64": "0.1.0-beta.
|
|
41
|
+
"@centralu/darwin-arm64": "0.1.0-beta.52",
|
|
42
|
+
"@centralu/linux-arm64": "0.1.0-beta.52",
|
|
43
|
+
"@centralu/linux-x64": "0.1.0-beta.52",
|
|
44
|
+
"@centralu/win32-x64": "0.1.0-beta.52"
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -43050,7 +43050,7 @@ import { dirname as dirname27, join as join59 } from "node:path";
|
|
|
43050
43050
|
import { mkdirSync as mkdirSync21, readFileSync as readFileSync27, writeSync as writeSync4 } from "node:fs";
|
|
43051
43051
|
|
|
43052
43052
|
// packages/protocol/src/version.generated.ts
|
|
43053
|
-
var APP_VERSION = "0.1.0-beta.
|
|
43053
|
+
var APP_VERSION = "0.1.0-beta.52";
|
|
43054
43054
|
|
|
43055
43055
|
// packages/protocol/src/brand.ts
|
|
43056
43056
|
var APP_NAME = "Centralu";
|
|
@@ -108241,6 +108241,9 @@ function lowestPreset(...presets) {
|
|
|
108241
108241
|
function presetWithin(child, parent) {
|
|
108242
108242
|
return PRESET_RANK[child] <= PRESET_RANK[parent];
|
|
108243
108243
|
}
|
|
108244
|
+
function aboveCallerError(name, child, caller) {
|
|
108245
|
+
return presetWithin(child, caller) ? null : `"${name}" now has more permissions (${child}) than this session (${caller}), so this session cannot give it work. Tell the person.`;
|
|
108246
|
+
}
|
|
108244
108247
|
var PARENT_MAIL = 'To write to it before you finish, call mail with `to: "parent"`. ';
|
|
108245
108248
|
function canMailParent(child, senderId) {
|
|
108246
108249
|
return child.askedBy === senderId && !!child.projectId;
|
|
@@ -113921,7 +113924,7 @@ var HANDLERS = {
|
|
|
113921
113924
|
async recall({ query, limit }, { tools }) {
|
|
113922
113925
|
const r = await tools.recall(query, limit);
|
|
113923
113926
|
const unsearched = r.notSearched?.length ? `
|
|
113924
|
-
Not searched: ${r.notSearched.join("
|
|
113927
|
+
Not searched: ${r.notSearched.join(". ")}.` : "";
|
|
113925
113928
|
if (r.hits.length === 0) return { text: `Nothing was found for "${query}". Try a different word.${unsearched}` };
|
|
113926
113929
|
return {
|
|
113927
113930
|
text: r.hits.map(
|
|
@@ -114345,6 +114348,7 @@ var START_AGENT_MODEL_CHECK_MS = 3e3;
|
|
|
114345
114348
|
var START_AGENT_SETUP_MS = 2e5;
|
|
114346
114349
|
var START_AGENT_CALL_MS = 27e4;
|
|
114347
114350
|
var ASK_THE_PERSON = "Do not do this with your own subagents instead: tell the person what stopped it and let them decide.";
|
|
114351
|
+
var UNTRUSTED_START = `This session's project is not trusted, so it cannot start agents \u2014 the person can trust it from the project menu. ${ASK_THE_PERSON}`;
|
|
114348
114352
|
function agentStartPending(req, lowMemory) {
|
|
114349
114353
|
return { ok: true, state: "pending", sessionId: req.requestId, sessionName: "", tool: req.detail.tool, ...lowMemory ? { lowMemory } : {} };
|
|
114350
114354
|
}
|
|
@@ -114385,21 +114389,6 @@ var AgentStartGates = class {
|
|
|
114385
114389
|
* many. Counted by `agentCeilingError` with the running children, so starts at once cannot pass the ceiling together.
|
|
114386
114390
|
*/
|
|
114387
114391
|
agentStartReserved = /* @__PURE__ */ new Map();
|
|
114388
|
-
/**
|
|
114389
|
-
* What a child's start rests on in its caller, read once the person allowed it and again once its session exists,
|
|
114390
|
-
* right before its delegation is registered: the caller is still here, its project still registered and trusted
|
|
114391
|
-
* (or the caller is `auto`), and it is still above the agent depth (#867), which `auto` does not open.
|
|
114392
|
-
*/
|
|
114393
|
-
agentStartGateError(callerId, projectId) {
|
|
114394
|
-
const caller = this.host.meta.get(callerId);
|
|
114395
|
-
if (!caller) return "The session that asked is no longer here.";
|
|
114396
|
-
if (projectId === ORCHESTRATOR_PLACE && caller.kind === "orchestrator") return agentDepthRefusal(this.host, callerId, "start agents");
|
|
114397
|
-
const now = this.host.store.projects.listProjects().find((p) => p.id === projectId);
|
|
114398
|
-
if (!now) return "This session's project is no longer registered.";
|
|
114399
|
-
const auto = caller.permissionPreset === "auto";
|
|
114400
|
-
if (now.trusted !== true && !auto) return `This session's project is no longer trusted, so it cannot start agents. ${ASK_THE_PERSON}`;
|
|
114401
|
-
return agentDepthRefusal(this.host, callerId, "start agents");
|
|
114402
|
-
}
|
|
114403
114392
|
/**
|
|
114404
114393
|
* A place under the per-project ceiling, checked and taken in one synchronous step (`agentStartReserved`), for a start
|
|
114405
114394
|
* (`createAgentChild`) or a fork (`forkFor`) that waits on something before its delegation counts the child.
|
|
@@ -114439,6 +114428,16 @@ var AgentStartGates = class {
|
|
|
114439
114428
|
return `${running2} agents ${whose} are already working, the most at once. Wait for one (${wait}) or stop one, then try again.`;
|
|
114440
114429
|
}
|
|
114441
114430
|
};
|
|
114431
|
+
function agentStartGateError(host, callerId, projectId) {
|
|
114432
|
+
const caller = host.meta.get(callerId);
|
|
114433
|
+
if (!caller) return "The session that asked is no longer here.";
|
|
114434
|
+
if (projectId === ORCHESTRATOR_PLACE && caller.kind === "orchestrator") return agentDepthRefusal(host, callerId, "start agents");
|
|
114435
|
+
const now = host.store.projects.listProjects().find((p) => p.id === projectId);
|
|
114436
|
+
if (!now) return "This session's project is no longer registered.";
|
|
114437
|
+
const auto = caller.permissionPreset === "auto";
|
|
114438
|
+
if (now.trusted !== true && !auto) return `This session's project is no longer trusted, so it cannot start agents. ${ASK_THE_PERSON}`;
|
|
114439
|
+
return agentDepthRefusal(host, callerId, "start agents");
|
|
114440
|
+
}
|
|
114442
114441
|
|
|
114443
114442
|
// packages/agent-host/src/sessions/agent-consents.ts
|
|
114444
114443
|
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
@@ -114866,7 +114865,7 @@ var AgentStartFlow = class {
|
|
|
114866
114865
|
return done2({ ok: false, error: "A session the orchestrator starts works outside any project, so it has no worktree. Start it without one, or hand the work to a project's session with send_to_session." });
|
|
114867
114866
|
}
|
|
114868
114867
|
if (project2.trusted !== true && !this.host.autoBoundaryOpen(callerId, "start in an untrusted project")) {
|
|
114869
|
-
return done2({ ok: false, error:
|
|
114868
|
+
return done2({ ok: false, error: UNTRUSTED_START });
|
|
114870
114869
|
}
|
|
114871
114870
|
const waiting = [...this.host.children.agentStarts.values()].find(
|
|
114872
114871
|
(r5) => !r5.ended && r5.req.callerId === callerId && r5.req.task === opts.task && (!opts.tool || r5.req.tool === opts.tool)
|
|
@@ -114978,7 +114977,7 @@ var AgentStartFlow = class {
|
|
|
114978
114977
|
*/
|
|
114979
114978
|
async createAgentChild(callerId, project2, choice, progress, approvedPreset, requestId) {
|
|
114980
114979
|
const done2 = (r) => ({ done: r });
|
|
114981
|
-
const refused2 = this.
|
|
114980
|
+
const refused2 = agentStartGateError(this.host, callerId, project2.id);
|
|
114982
114981
|
if (refused2) return done2({ ok: false, error: refused2 });
|
|
114983
114982
|
const caller = this.host.meta.get(callerId);
|
|
114984
114983
|
if (approvedPreset && !presetWithin(caller.permissionPreset, approvedPreset)) {
|
|
@@ -115007,10 +115006,6 @@ var AgentStartFlow = class {
|
|
|
115007
115006
|
release();
|
|
115008
115007
|
}
|
|
115009
115008
|
}
|
|
115010
|
-
/** `AgentStartGates.agentStartGateError` (agent-start-gates.ts), kept on the flow for its callers */
|
|
115011
|
-
agentStartGateError(...a) {
|
|
115012
|
-
return this.gates.agentStartGateError(...a);
|
|
115013
|
-
}
|
|
115014
115009
|
/** `AgentStartGates.reservePlace` (agent-start-gates.ts), kept on the flow for its callers */
|
|
115015
115010
|
reservePlace(...a) {
|
|
115016
115011
|
return this.gates.reservePlace(...a);
|
|
@@ -115082,7 +115077,7 @@ var AgentStartFlow = class {
|
|
|
115082
115077
|
recordAgentDepth(this.host, child.id, callerId);
|
|
115083
115078
|
const callerNow = this.host.meta.get(callerId);
|
|
115084
115079
|
const withdrawn = requestId ? this.host.children.agentStarts.get(requestId)?.ended : void 0;
|
|
115085
|
-
const late = (withdrawn && "error" in withdrawn ? `The request was withdrawn while its session was being made. ${withdrawn.error}` : null) ?? this.
|
|
115080
|
+
const late = (withdrawn && "error" in withdrawn ? `The request was withdrawn while its session was being made. ${withdrawn.error}` : null) ?? agentStartGateError(this.host, callerId, project2.id) ?? (callerNow && !presetWithin(child.permissionPreset, callerNow.permissionPreset) ? `This session's permissions were lowered to ${callerNow.permissionPreset} while the session was being made.` : null);
|
|
115086
115081
|
const name = choice.name ?? (worktree ? void 0 : agentName(choice.task));
|
|
115087
115082
|
if (late) return done2(await this.abandonChild(child, late, name));
|
|
115088
115083
|
progress.stage = "created";
|
|
@@ -115757,7 +115752,7 @@ var AgentSessionActions = class {
|
|
|
115757
115752
|
const seat = caller?.projectId ? "project" : caller?.kind === "orchestrator" ? "orchestrator" : null;
|
|
115758
115753
|
const owns = seat === "orchestrator" ? child?.askedBy === callerId : startedHere || !deletingWithoutWorktree && this.host.autoBoundaryOpen(callerId, "session ownership");
|
|
115759
115754
|
if (!caller || !seat || !child || !owns || child.projectId !== caller.projectId) {
|
|
115760
|
-
return { ok: false, error: `${opts.sessionId} is not a session this session started. agent_session works only on sessions start_agent started here.${child ? "" : staleSessionHint(this.host, opts.sessionId, (s) => s.projectId === caller?.projectId && s.id !== callerId && (seat !== "orchestrator" || s.askedBy === callerId))}` };
|
|
115755
|
+
return { ok: false, error: `${opts.sessionId} is not a session this session started. agent_session works only on sessions start_agent started from here.${child ? "" : staleSessionHint(this.host, opts.sessionId, (s) => s.projectId === caller?.projectId && s.id !== callerId && (seat !== "orchestrator" || s.askedBy === callerId))}` };
|
|
115761
115756
|
}
|
|
115762
115757
|
if (opts.sessionId === callerId) return this.flow.agentAtRest(child);
|
|
115763
115758
|
if (opts.action === "answer") return this.host.answerRoutedCard(callerId, child.id, opts.card, opts.text ?? "");
|
|
@@ -115829,12 +115824,8 @@ var AgentSessionActions = class {
|
|
|
115829
115824
|
return { ok: true, state: "deleted", sessionId: child.id, sessionName: child.name, tool: child.tool, branch: r.branch, setAside: r.setAside, ...r.warning ? { warning: r.warning } : {}, ...note2 ? { note: note2 } : {} };
|
|
115830
115825
|
}
|
|
115831
115826
|
case "send": {
|
|
115832
|
-
|
|
115833
|
-
|
|
115834
|
-
ok: false,
|
|
115835
|
-
error: `"${child.name}" now has more permissions (${child.permissionPreset}) than this session (${caller.permissionPreset}), so this session cannot give it work. Tell the person.`
|
|
115836
|
-
};
|
|
115837
|
-
}
|
|
115827
|
+
const above = aboveCallerError(child.name, child.permissionPreset, caller.permissionPreset);
|
|
115828
|
+
if (above) return { ok: false, error: above };
|
|
115838
115829
|
const refused2 = this.host.refusesDelegation(callerId, { sessionId: child.id, projectId: child.projectId }, { tool: "agent_session", target: child.name || child.id, text: opts.text ?? "" });
|
|
115839
115830
|
if (refused2) return { ok: false, error: refused2 };
|
|
115840
115831
|
const from = { sessionId: callerId, name: labelOf(this.host.store.messages, caller) };
|
|
@@ -116182,9 +116173,11 @@ var RemoteAgents = class {
|
|
|
116182
116173
|
if (!caller?.projectId) {
|
|
116183
116174
|
return { ok: false, error: caller?.kind === "orchestrator" ? "The orchestrator starts a session on a linked machine with create_session; start_agent with a machine is for a project's session." : "This session belongs to no project, so it cannot start agents." };
|
|
116184
116175
|
}
|
|
116176
|
+
const project2 = this.host.store.projects.listProjects().find((p) => p.id === caller.projectId);
|
|
116177
|
+
if (!project2) return { ok: false, error: "This session's project is no longer registered." };
|
|
116178
|
+
if (project2.trusted !== true && !this.host.autoBoundaryOpen(callerId, "start in an untrusted project")) return { ok: false, error: UNTRUSTED_START };
|
|
116185
116179
|
const refused2 = this.gateError(callerId);
|
|
116186
116180
|
if (refused2) return { ok: false, error: refused2 };
|
|
116187
|
-
const project2 = this.host.store.projects.listProjects().find((p) => p.id === caller.projectId);
|
|
116188
116181
|
const full = this.places.agentCeilingError(callerId, project2.id);
|
|
116189
116182
|
if (full) return { ok: false, error: full };
|
|
116190
116183
|
const reader = this.host.linkedReader;
|
|
@@ -116377,11 +116370,8 @@ var RemoteAgents = class {
|
|
|
116377
116370
|
case "send": {
|
|
116378
116371
|
const text3 = opts.text?.trim();
|
|
116379
116372
|
if (!text3) return { ok: false, error: "Give the text to send." };
|
|
116380
|
-
const
|
|
116381
|
-
|
|
116382
|
-
if (!presetWithin(childPreset, own)) {
|
|
116383
|
-
return { ok: false, error: `"${rec.name}" now has more permissions (${childPreset}) than this session (${own}), so this session cannot give it work. Tell the person.` };
|
|
116384
|
-
}
|
|
116373
|
+
const above = aboveCallerError(rec.name, rec.preset ?? "normal", this.callerPreset(callerId));
|
|
116374
|
+
if (above) return { ok: false, error: above };
|
|
116385
116375
|
const now = await stateOf();
|
|
116386
116376
|
if (!now.ok) return now;
|
|
116387
116377
|
if (now.running) {
|
|
@@ -116413,16 +116403,8 @@ var RemoteAgents = class {
|
|
|
116413
116403
|
return this.sendAtRest(rec, callerId, text3, label, reader);
|
|
116414
116404
|
}
|
|
116415
116405
|
case "stop": {
|
|
116416
|
-
const
|
|
116417
|
-
if (
|
|
116418
|
-
const held3 = rec.queued;
|
|
116419
|
-
this.setQueued(rec.sessionId, []);
|
|
116420
|
-
const r = await reader.interrupt(rec.sessionId);
|
|
116421
|
-
if (!r.ok) {
|
|
116422
|
-
if (held3) this.setQueued(rec.sessionId, held3);
|
|
116423
|
-
if (owed?.orchestratorId === callerId && !this.host.children.linkedReports.has(rec.sessionId)) this.host.children.setLinkedReport(rec.sessionId, owed);
|
|
116424
|
-
return { ok: false, error: `Could not stop "${rec.name}" on ${rec.machine}: ${bare(r.error)}.` };
|
|
116425
|
-
}
|
|
116406
|
+
const r = await this.interruptFor(rec, callerId, reader);
|
|
116407
|
+
if (!r.ok) return { ok: false, error: `Could not stop "${rec.name}" on ${rec.machine}: ${bare(r.error)}.` };
|
|
116426
116408
|
return { ok: true, state: "stopped", sessionId: rec.sessionId, sessionName: rec.name, tool: rec.tool };
|
|
116427
116409
|
}
|
|
116428
116410
|
case "delete": {
|
|
@@ -116441,14 +116423,9 @@ var RemoteAgents = class {
|
|
|
116441
116423
|
const now = await stateOf();
|
|
116442
116424
|
if (!now.ok) return now;
|
|
116443
116425
|
if (now.running) {
|
|
116444
|
-
const
|
|
116445
|
-
if (
|
|
116446
|
-
|
|
116447
|
-
if (!stopped.ok) {
|
|
116448
|
-
if (owed?.orchestratorId === callerId && !this.host.children.linkedReports.has(rec.sessionId)) this.host.children.setLinkedReport(rec.sessionId, owed);
|
|
116449
|
-
return { ok: false, error: `Could not stop "${rec.name}" on ${rec.machine} before deleting it: ${bare(stopped.error)}.` };
|
|
116450
|
-
}
|
|
116451
|
-
}
|
|
116426
|
+
const stopped = await this.interruptFor(rec, callerId, reader);
|
|
116427
|
+
if (!stopped.ok) return { ok: false, error: `Could not stop "${rec.name}" on ${rec.machine} before deleting it: ${bare(stopped.error)}.` };
|
|
116428
|
+
} else this.setQueued(rec.sessionId, []);
|
|
116452
116429
|
const canClean = typeof reader.deleteAgentChild === "function";
|
|
116453
116430
|
const withWorktree = rec.worktree && canClean ? await reader.deleteAgentChild(rec.sessionId, rec.worktree).catch((e) => ({ ok: false, error: e instanceof Error ? e.message : String(e) })) : void 0;
|
|
116454
116431
|
if (withWorktree && !withWorktree.ok) return { ok: false, error: `Could not delete "${rec.name}" on ${rec.machine}: ${bare(withWorktree.error)}.` };
|
|
@@ -116713,6 +116690,22 @@ var RemoteAgents = class {
|
|
|
116713
116690
|
this.scheduleDrain(rec.sessionId);
|
|
116714
116691
|
return { ok: true, tool: rec.tool, state: "queued", sessionId: rec.sessionId, sessionName: rec.name, queueId: entry.id, position: (same ? held3.indexOf(same) : held3.length) + 1 };
|
|
116715
116692
|
}
|
|
116693
|
+
/**
|
|
116694
|
+
* Stops the child's turn there for a stop or a deletion by the one it would report to: nothing more is owed, and its
|
|
116695
|
+
* end must not send what was held for it. Both are taken down first, as the stopped turn's end can reach the hub before
|
|
116696
|
+
* the machine's answer does, and put back if the stop does not go
|
|
116697
|
+
*/
|
|
116698
|
+
async interruptFor(rec, callerId, reader) {
|
|
116699
|
+
const owed = this.host.children.linkedReports.get(rec.sessionId);
|
|
116700
|
+
if (owed?.orchestratorId === callerId) this.host.children.dropLinkedReport(rec.sessionId);
|
|
116701
|
+
const held3 = this.of(rec.sessionId)?.queued;
|
|
116702
|
+
this.setQueued(rec.sessionId, []);
|
|
116703
|
+
const r = await reader.interrupt(rec.sessionId);
|
|
116704
|
+
if (r.ok) return r;
|
|
116705
|
+
if (held3) this.setQueued(rec.sessionId, held3);
|
|
116706
|
+
if (owed?.orchestratorId === callerId && !this.host.children.linkedReports.has(rec.sessionId)) this.host.children.setLinkedReport(rec.sessionId, owed);
|
|
116707
|
+
return r;
|
|
116708
|
+
}
|
|
116716
116709
|
setQueued(sessionId, queued) {
|
|
116717
116710
|
if (!queued.length && !this.of(sessionId)?.queued) return;
|
|
116718
116711
|
this.save(this.records().map((r) => r.sessionId === sessionId ? { ...r, queued: queued.length ? queued : void 0 } : r));
|
|
@@ -117100,16 +117093,9 @@ Reason, by kind: ${errorKind(why2)} (its words are an error's, kept in this noti
|
|
|
117100
117093
|
for (const key of [...this.toldCards]) if (key.startsWith(`${sessionId}\0`)) this.toldCards.delete(key);
|
|
117101
117094
|
if (this.host.children.linkedReports.has(sessionId)) this.host.children.dropLinkedReport(sessionId);
|
|
117102
117095
|
}
|
|
117103
|
-
/** What start_agent and a send rest on in the caller, read again after a card
|
|
117096
|
+
/** What start_agent and a send rest on in the caller, read again after a card and before each send: a local start's (`agentStartGateError`) */
|
|
117104
117097
|
gateError(callerId) {
|
|
117105
|
-
|
|
117106
|
-
if (!caller) return "The session that asked is no longer here.";
|
|
117107
|
-
const project2 = this.host.store.projects.listProjects().find((p) => p.id === caller.projectId);
|
|
117108
|
-
if (!project2) return "This session's project is no longer registered.";
|
|
117109
|
-
if (project2.trusted !== true && !this.host.autoBoundaryOpen(callerId, "start in an untrusted project")) {
|
|
117110
|
-
return `This session's project is not trusted, so it cannot start agents \u2014 the person can trust it from the project menu. ${ASK_THE_PERSON}`;
|
|
117111
|
-
}
|
|
117112
|
-
return agentDepthRefusal(this.host, callerId, "start agents");
|
|
117098
|
+
return agentStartGateError(this.host, callerId, this.host.meta.get(callerId)?.projectId || null);
|
|
117113
117099
|
}
|
|
117114
117100
|
/** The caller's preset as it is now; `safe` for one gone, so nothing is decided on a preset no one has */
|
|
117115
117101
|
callerPreset(callerId) {
|
|
@@ -117124,7 +117110,7 @@ Reason, by kind: ${errorKind(why2)} (its words are an error's, kept in this noti
|
|
|
117124
117110
|
async autoThere(machine, reader) {
|
|
117125
117111
|
const r = await reader.hubAuto(machine.id);
|
|
117126
117112
|
if (r.ok) return r.allowed ? { auto: true } : { auto: false, withheld: "machine_off" };
|
|
117127
|
-
return { error: `Could not
|
|
117113
|
+
return { error: `Could not tell whether ${machine.name} takes auto from this computer: ${bare(r.error)}. Nothing was started there.` };
|
|
117128
117114
|
}
|
|
117129
117115
|
/** The machine's project that is a checkout of this project's repository: the same `origin`, read on both sides */
|
|
117130
117116
|
async checkoutOn(machine, project2, reader) {
|
|
@@ -122730,10 +122716,6 @@ var MailboxRecords = class {
|
|
|
122730
122716
|
markDelivered(recipient, ids, at2, turnSeq) {
|
|
122731
122717
|
this.update("delivered_at = ?, last_error = NULL, turn_seq = ?", [at2, turnSeq], recipient, ids);
|
|
122732
122718
|
}
|
|
122733
|
-
/** These delivered rows were delivered in the turn `turnSeq` (the delivery began it after it delivered them) */
|
|
122734
|
-
markTurn(recipient, ids, turnSeq) {
|
|
122735
|
-
this.update("turn_seq = ?", [turnSeq], recipient, ids, "delivered_at IS NOT NULL");
|
|
122736
|
-
}
|
|
122737
122719
|
/** Sets why mail waits for the person (`policy`, `loop`, `budget`, `failed`, `uncertain`), or clears it with null */
|
|
122738
122720
|
hold(recipient, ids, why2) {
|
|
122739
122721
|
this.update("held = ?", [why2], recipient, ids);
|
|
@@ -125182,11 +125164,6 @@ var MessageService = class {
|
|
|
125182
125164
|
});
|
|
125183
125165
|
return { needReply, unread: unread2, waiting, held: held3 };
|
|
125184
125166
|
}
|
|
125185
|
-
/** The messages to or from `address`, newest first, as the address reads them (the person's Inbox feed, M6's `mail.inbox`, §12.2). Marks nothing */
|
|
125186
|
-
feed(address, opts) {
|
|
125187
|
-
const found = this.records.feedOf(address, { ...opts.before === void 0 ? {} : { before: opts.before }, limit: opts.limit + 1 });
|
|
125188
|
-
return { items: found.slice(0, opts.limit).map((m) => this.item(m, address, null)), older: found.length > opts.limit };
|
|
125189
|
-
}
|
|
125190
125167
|
/**
|
|
125191
125168
|
* Whether mail that wakes `recipient` waits for it, due or not yet (the debounce makes a message wait a moment before it is due): a turn of its own is
|
|
125192
125169
|
* coming, which a note of the host's should follow. Reads only; mail that is held out or settled is not waiting
|
|
@@ -125270,10 +125247,6 @@ var MessageService = class {
|
|
|
125270
125247
|
asker: turnStarts && toOrchestrator ? picked : null
|
|
125271
125248
|
};
|
|
125272
125249
|
}
|
|
125273
|
-
/** The delivery began a turn for `recipient` (it called `MailTurns.begin`): the rows it delivered were delivered in that turn */
|
|
125274
|
-
turnStarted(recipient, messageIds, turnSeq) {
|
|
125275
|
-
this.run(() => this.records.markTurn(recipient, messageIds, turnSeq));
|
|
125276
|
-
}
|
|
125277
125250
|
/**
|
|
125278
125251
|
* What a host that died left half done (§7.6.5 of the old plan): a delivery whose attempt was written and whose acceptance was not. It is not sent
|
|
125279
125252
|
* again on its own, since an inline task the tool took just before the host died would start the same work twice: it is held `uncertain`, for the
|
|
@@ -125435,29 +125408,6 @@ var MessageService = class {
|
|
|
125435
125408
|
return rows3.length;
|
|
125436
125409
|
});
|
|
125437
125410
|
}
|
|
125438
|
-
/** One line for the person's guidance per project with mail held for its trust or delegations refused (§7.4) */
|
|
125439
|
-
heldByProject() {
|
|
125440
|
-
const byProject = /* @__PURE__ */ new Map();
|
|
125441
|
-
const entry = (projectId) => {
|
|
125442
|
-
const kept = byProject.get(projectId) ?? { count: 0, refused: 0, recipients: /* @__PURE__ */ new Set() };
|
|
125443
|
-
byProject.set(projectId, kept);
|
|
125444
|
-
return kept;
|
|
125445
|
-
};
|
|
125446
|
-
for (const { row, senderProject } of this.records.heldPolicy()) {
|
|
125447
|
-
if (senderProject === null) continue;
|
|
125448
|
-
const e = entry(senderProject);
|
|
125449
|
-
e.count++;
|
|
125450
|
-
e.recipients.add(row.recipient);
|
|
125451
|
-
}
|
|
125452
|
-
for (const { message } of this.records.refusedRecords()) {
|
|
125453
|
-
const e = message.facts.refused ? entry(message.facts.refused.projectId) : void 0;
|
|
125454
|
-
if (e) {
|
|
125455
|
-
e.refused++;
|
|
125456
|
-
e.recipients.add(message.facts.refused?.target ?? "");
|
|
125457
|
-
}
|
|
125458
|
-
}
|
|
125459
|
-
return [...byProject].map(([projectId, e]) => ({ projectId, count: e.count, refused: e.refused, recipients: [...e.recipients] })).sort((a, b2) => a.projectId.localeCompare(b2.projectId));
|
|
125460
|
-
}
|
|
125461
125411
|
// ── Parties that go, parties that take over (§7.7) ──
|
|
125462
125412
|
/** A session went to the trash or was deleted: its watched rows are `gone`, its own watched messages `withdrawn`, and each sender whose needsReply message it leaves is told once (D25) */
|
|
125463
125413
|
partyGone(address) {
|
|
@@ -125477,6 +125427,8 @@ var MessageService = class {
|
|
|
125477
125427
|
* `needsReply` message, the person's too) is released with the cause, and each sender is told once, in one notice. The rows stay open, so a reply that
|
|
125478
125428
|
* comes after the person continues the session still answers them. A question delivered in an earlier turn is not this stop's: it was read, and the
|
|
125479
125429
|
* session may have answered it in conversation. A task is not released here: the old report path tells its owner until requests decide reports.
|
|
125430
|
+
* A request to the orchestrator is (owner: its sender always learns it will not be answered, #1186): the turn's report tells the asker it was owed to,
|
|
125431
|
+
* which the caller passes as `held`, and every other asker, or one that report misses (an asker the same quit cut), gets the stop notice.
|
|
125480
125432
|
* `turnSeq` is the cut turn (none: nothing is released, as for a turn the mailbox never recorded)
|
|
125481
125433
|
*
|
|
125482
125434
|
* Who is woken (Q1): a sender that is at rest and waits on the stopped session, by a task it gave it that is still open or by a wait (`waitsOn`).
|
|
@@ -125499,10 +125451,7 @@ var MessageService = class {
|
|
|
125499
125451
|
if (turnSeq === null) continue;
|
|
125500
125452
|
const rows3 = this.records.watchedTo(address);
|
|
125501
125453
|
const gave = new Set(rows3.filter((r) => this.records.message(r.messageId)?.kind === "task").flatMap((r) => this.records.message(r.messageId)?.sender ?? []));
|
|
125502
|
-
for (const row of
|
|
125503
|
-
if (row.releasedAt !== null || row.deliveredAt === null || row.answerId !== null || row.turnSeq !== turnSeq) continue;
|
|
125504
|
-
const m = this.records.message(row.messageId);
|
|
125505
|
-
if (!m || m.kind === "task" || !m.needsReply || m.sender === address || this.isRequest(m, address)) continue;
|
|
125454
|
+
for (const { row, message: m } of this.cutQuestions(address, turnSeq)) {
|
|
125506
125455
|
if (!this.records.markReleased(row.messageId, address, now, why2)) continue;
|
|
125507
125456
|
released++;
|
|
125508
125457
|
if (gave.has(m.sender) || this.opts.waitsOn?.(m.sender, address)) waiting.add(m.sender);
|
|
@@ -125580,14 +125529,22 @@ var MessageService = class {
|
|
|
125580
125529
|
turnsLost(close) {
|
|
125581
125530
|
this.releaseAndTell((now, senders) => {
|
|
125582
125531
|
for (const { address, turnSeq } of close()) {
|
|
125583
|
-
for (const row of this.records.
|
|
125584
|
-
if (row.turnSeq !== turnSeq || row.settled !== null) continue;
|
|
125585
|
-
const m = this.records.message(row.messageId);
|
|
125586
|
-
if (m && m.kind !== "task" && this.records.markReleased(row.messageId, address, now, "lost")) senders.add(m.sender);
|
|
125587
|
-
}
|
|
125532
|
+
for (const { row, message } of this.cutQuestions(address, turnSeq)) if (this.records.markReleased(row.messageId, address, now, "lost")) senders.add(message.sender);
|
|
125588
125533
|
}
|
|
125589
125534
|
});
|
|
125590
125535
|
}
|
|
125536
|
+
/**
|
|
125537
|
+
* The questions `address` left unanswered in its turn `turnSeq`, the rows a stop (`sessionsStopped`) or a lost turn (`turnsLost`) releases: each
|
|
125538
|
+
* `needsReply` message delivered into that turn and not answered, settled or released, a request to the orchestrator included. Not a task (the old
|
|
125539
|
+
* report path tells its owner, #698), nor a message the session sent itself (a handoff to the very session that asked leaves such a row)
|
|
125540
|
+
*/
|
|
125541
|
+
cutQuestions(address, turnSeq) {
|
|
125542
|
+
return this.records.unansweredTo(address).flatMap((row) => {
|
|
125543
|
+
if (row.turnSeq !== turnSeq || row.settled !== null || row.releasedAt !== null) return [];
|
|
125544
|
+
const message = this.records.message(row.messageId);
|
|
125545
|
+
return message && message.kind !== "task" && message.sender !== address ? [{ row, message }] : [];
|
|
125546
|
+
});
|
|
125547
|
+
}
|
|
125591
125548
|
/** One write: `release` releases rows and adds each released question's sender to `senders`; then each sender is told once, in one notice (D25) */
|
|
125592
125549
|
releaseAndTell(release, quiet = () => false) {
|
|
125593
125550
|
this.run(() => {
|
|
@@ -156054,7 +156011,6 @@ function updateNodeSentence(o) {
|
|
|
156054
156011
|
import { execFileSync as execFileSync5, spawn as spawn6 } from "node:child_process";
|
|
156055
156012
|
import { createHash as createHash14 } from "node:crypto";
|
|
156056
156013
|
import { createConnection as createConnection2, createServer as createServer3 } from "node:net";
|
|
156057
|
-
import { win32 as win329 } from "node:path";
|
|
156058
156014
|
import { hostname as hostname3 } from "node:os";
|
|
156059
156015
|
|
|
156060
156016
|
// packages/agent-host/src/links/ssh-run.ts
|
|
@@ -156340,6 +156296,12 @@ function notInstalled(where2, spec) {
|
|
|
156340
156296
|
return new Error(`Centralu is not installed ${where2} (run \`${npmInstall(spec)}\` there, then \`${shimNames(spec).centralu} serve\` once)`);
|
|
156341
156297
|
}
|
|
156342
156298
|
var NOT_FOUND2 = "CENTRALU-NOT-FOUND";
|
|
156299
|
+
var STALE_LAUNCHER = "; if Node was upgraded there, run `centralu serve` once from an interactive shell on that machine";
|
|
156300
|
+
function noAnswer(err, r, remote) {
|
|
156301
|
+
const why2 = lastLine(r.stderr);
|
|
156302
|
+
if (r.code === 127 && !remote.command?.trim()) return `The Centralu there did not run: ${why2 || "exit 127"}${STALE_LAUNCHER}`;
|
|
156303
|
+
return `${err.message}${why2 ? `: ${why2}` : r.code ? ` (exit ${r.code})` : ""}`;
|
|
156304
|
+
}
|
|
156343
156305
|
function connectionCommand(spec, pair = null) {
|
|
156344
156306
|
return remoteCommand(spec, pair ? `serve --connection --pair ${pair}` : "serve --connection");
|
|
156345
156307
|
}
|
|
@@ -156468,13 +156430,12 @@ var SshTunnel = class {
|
|
|
156468
156430
|
const r = await this.run([...this.config(), "-T", "-o", "BatchMode=yes", "-o", "ConnectTimeout=15", "--", this.opts.target, detachCommand(remote, this.opts.hubName ?? hostname3())], void 0, this.opts.detachTimeoutMs ?? SSH_DETACH_TIMEOUT_MS);
|
|
156469
156431
|
this.ifClosed();
|
|
156470
156432
|
if (r.code === 255) throw new Error(`ssh could not reach ${this.opts.target}: ${lastLine(r.stderr) || "no answer"}`);
|
|
156471
|
-
if (r.stdout.includes(NOT_FOUND2)
|
|
156433
|
+
if (r.stdout.includes(NOT_FOUND2)) throw notInstalled(this.where, remote);
|
|
156472
156434
|
let answer;
|
|
156473
156435
|
try {
|
|
156474
156436
|
answer = parseDetachLine(r.stdout);
|
|
156475
156437
|
} catch (err) {
|
|
156476
|
-
|
|
156477
|
-
throw new Error(`${err.message}${why2 ? `: ${why2}` : r.code ? ` (exit ${r.code})` : ""}`);
|
|
156438
|
+
throw new Error(noAnswer(err, r, remote));
|
|
156478
156439
|
}
|
|
156479
156440
|
if (answer.ok) {
|
|
156480
156441
|
this.linkBound = false;
|
|
@@ -156597,14 +156558,14 @@ var SshTunnel = class {
|
|
|
156597
156558
|
return this.connectionLine();
|
|
156598
156559
|
}
|
|
156599
156560
|
if (r.code === 255) throw new Error(`ssh could not reach ${this.opts.target}: ${lastLine(r.stderr) || "no answer"}`);
|
|
156600
|
-
if (r.stdout.includes(NOT_FOUND2)
|
|
156561
|
+
if (r.stdout.includes(NOT_FOUND2)) throw notInstalled(this.where, remote);
|
|
156601
156562
|
try {
|
|
156602
156563
|
const line = parseConnectionLine(r.stdout, remote);
|
|
156603
156564
|
return pair && this.pairs && line.credential ? { ...line, pairedAs: oneLineText(pair, 80) } : line;
|
|
156604
156565
|
} catch (err) {
|
|
156605
156566
|
const why2 = lastLine(r.stderr);
|
|
156606
156567
|
if (r.code !== 0 && /cannot be read by this user \((EACCES|EPERM)\)/.test(why2)) throw new Error(why2.replace(/^centralu serve: /, ""));
|
|
156607
|
-
throw new Error(
|
|
156568
|
+
throw new Error(noAnswer(err, r, remote));
|
|
156608
156569
|
}
|
|
156609
156570
|
}
|
|
156610
156571
|
forward(localPort, remotePort, hosting = false) {
|
|
@@ -156746,8 +156707,7 @@ function ansiText(bytes2) {
|
|
|
156746
156707
|
ansiDecoder = null;
|
|
156747
156708
|
if (process.platform !== "win32") return null;
|
|
156748
156709
|
try {
|
|
156749
|
-
const
|
|
156750
|
-
const out = execFileSync5(win329.join(root3, "System32", "reg.exe"), ["query", "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage", "/v", "ACP"], { encoding: "utf8", timeout: 5e3, windowsHide: true });
|
|
156710
|
+
const out = execFileSync5(systemTool2(["reg.exe"]), ["query", "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage", "/v", "ACP"], { encoding: "utf8", timeout: 5e3, windowsHide: true });
|
|
156751
156711
|
const page = /\bACP\s+REG_SZ\s+(\d+)/.exec(out)?.[1];
|
|
156752
156712
|
if (page) ansiDecoder = new TextDecoder(CODE_PAGE_NAMES[page] ?? `windows-${page}`, { fatal: true });
|
|
156753
156713
|
} catch {
|
|
@@ -157401,6 +157361,17 @@ async function removeAutostart(c) {
|
|
|
157401
157361
|
}
|
|
157402
157362
|
}
|
|
157403
157363
|
|
|
157364
|
+
// packages/agent-host/src/links/hub-auto.ts
|
|
157365
|
+
async function askHubAuto(call2, allowed) {
|
|
157366
|
+
try {
|
|
157367
|
+
const r = asObj(await (allowed === void 0 ? call2("host.hubAuto", {}) : call2("host.setHubAuto", { allowed })));
|
|
157368
|
+
return r.allowed === true ? "on" : "off";
|
|
157369
|
+
} catch (err) {
|
|
157370
|
+
if (/^Unknown method/.test(errorMessage(err))) return "older";
|
|
157371
|
+
throw err;
|
|
157372
|
+
}
|
|
157373
|
+
}
|
|
157374
|
+
|
|
157404
157375
|
// packages/agent-host/src/links/linked-machine-connection.ts
|
|
157405
157376
|
import { randomBytes as randomBytes13 } from "node:crypto";
|
|
157406
157377
|
|
|
@@ -157722,6 +157693,11 @@ var RemoteClient = class {
|
|
|
157722
157693
|
attempt = 0;
|
|
157723
157694
|
reconnectTimer;
|
|
157724
157695
|
handshakeTimer;
|
|
157696
|
+
stableTimer;
|
|
157697
|
+
/** The hello of this socket is younger than `stableMs`: a drop now makes it short-lived */
|
|
157698
|
+
settling = false;
|
|
157699
|
+
/** The hellos in a row that dropped within `stableMs`; cleared by a hello that stayed up that long and by every `updateEndpoint` */
|
|
157700
|
+
shortLived = 0;
|
|
157725
157701
|
/** The refusal that came before the close, if one did: it names why the host closes */
|
|
157726
157702
|
refusal;
|
|
157727
157703
|
WS;
|
|
@@ -157808,7 +157784,9 @@ var RemoteClient = class {
|
|
|
157808
157784
|
}
|
|
157809
157785
|
/**
|
|
157810
157786
|
* Points the client at another address or token (the tunnel came back on another port, or the token rotated), and
|
|
157811
|
-
* the id of the credential the token is, when it was paired (#1405)
|
|
157787
|
+
* the id of the credential the token is, when it was paired (#1405). Every call, the same address or not, starts the
|
|
157788
|
+
* dials and the count of short-lived hellos over: the link has read the connection line again, which is the answer
|
|
157789
|
+
* the redials were a stand-in for
|
|
157812
157790
|
*/
|
|
157813
157791
|
updateEndpoint(url2, token2, credential) {
|
|
157814
157792
|
const sameHost = this.opts.url === url2;
|
|
@@ -157819,6 +157797,7 @@ var RemoteClient = class {
|
|
|
157819
157797
|
this.lastSeq = 0;
|
|
157820
157798
|
}
|
|
157821
157799
|
this.attempt = 0;
|
|
157800
|
+
this.shortLived = 0;
|
|
157822
157801
|
if (this.ws) {
|
|
157823
157802
|
const old = this.ws;
|
|
157824
157803
|
this.ws = null;
|
|
@@ -157887,6 +157866,14 @@ var RemoteClient = class {
|
|
|
157887
157866
|
this.cursorValid = true;
|
|
157888
157867
|
this.ready = true;
|
|
157889
157868
|
this.attempt = 0;
|
|
157869
|
+
if (this.opts.stableMs !== void 0) {
|
|
157870
|
+
this.settling = true;
|
|
157871
|
+
this.stableTimer = setTimeout(() => {
|
|
157872
|
+
this.settling = false;
|
|
157873
|
+
this.shortLived = 0;
|
|
157874
|
+
}, this.opts.stableMs);
|
|
157875
|
+
this.stableTimer.unref?.();
|
|
157876
|
+
}
|
|
157890
157877
|
this.on.hello?.({ resync, newLifetime: !sameLifetime, protocolVersion: frame.protocolVersion, ...frame.build ? { build: frame.build } : {}, ...typeof frame.elevated === "boolean" ? { elevated: frame.elevated } : {}, ...frame.logon ? { logon: frame.logon } : {}, ...frame.handover ? { handover: frame.handover } : {}, ...frame.updateContinued ? { updateContinued: frame.updateContinued } : {} });
|
|
157891
157878
|
return;
|
|
157892
157879
|
}
|
|
@@ -157958,11 +157945,13 @@ var RemoteClient = class {
|
|
|
157958
157945
|
this.ws = null;
|
|
157959
157946
|
const wasReady = this.ready;
|
|
157960
157947
|
this.ready = false;
|
|
157948
|
+
if (wasReady && this.settling) this.shortLived++;
|
|
157949
|
+
this.settling = false;
|
|
157961
157950
|
this.clearTimers();
|
|
157962
157951
|
this.failAll("The link dropped");
|
|
157963
157952
|
if (this.closed) return;
|
|
157964
157953
|
if (wasReady) this.on.down?.(reason, detail);
|
|
157965
|
-
if (this.opts.redials !== void 0 && this.attempt >= this.opts.redials) {
|
|
157954
|
+
if (this.opts.redials !== void 0 && (this.attempt >= this.opts.redials || this.shortLived >= this.opts.redials)) {
|
|
157966
157955
|
this.on.gaveUp?.(reason);
|
|
157967
157956
|
return;
|
|
157968
157957
|
}
|
|
@@ -157983,6 +157972,7 @@ var RemoteClient = class {
|
|
|
157983
157972
|
clearTimers() {
|
|
157984
157973
|
clearTimeout(this.reconnectTimer);
|
|
157985
157974
|
clearTimeout(this.handshakeTimer);
|
|
157975
|
+
clearTimeout(this.stableTimer);
|
|
157986
157976
|
this.reconnectTimer = void 0;
|
|
157987
157977
|
this.handshakeTimer = void 0;
|
|
157988
157978
|
}
|
|
@@ -158123,7 +158113,7 @@ async function follow(c, id3) {
|
|
|
158123
158113
|
const machine = c.machine ?? c.target;
|
|
158124
158114
|
throw new Error(`The update of ${machine} could not be followed to its end from this computer; how it ended is shown once ${machine} is reached again`);
|
|
158125
158115
|
}
|
|
158126
|
-
await
|
|
158116
|
+
await sleep(wait);
|
|
158127
158117
|
}
|
|
158128
158118
|
}
|
|
158129
158119
|
var restoredNote = (version2, machine) => `The store on ${machine} was put back as it was before the dev build first opened it, since Centralu ${version2} could not read it as the dev build left it; what was done on the dev build since is not in it`;
|
|
@@ -158321,9 +158311,8 @@ var LinkedMachineConnection = class {
|
|
|
158321
158311
|
client = null;
|
|
158322
158312
|
/** The client when it stopped redialling (`gaveUp`, #1143) and is waiting, idle, for the next opening */
|
|
158323
158313
|
gaveUp = null;
|
|
158324
|
-
/** Sessions of the remote the hub shows
|
|
158314
|
+
/** Sessions of the remote the hub shows, by the remote's id. Events of any other session are not passed on */
|
|
158325
158315
|
shown = /* @__PURE__ */ new Set();
|
|
158326
|
-
hidden = /* @__PURE__ */ new Set();
|
|
158327
158316
|
/** The remote's coordinators as it last gave them: not shown, but read by a reader (#1941, `unshown`) */
|
|
158328
158317
|
coordinators = /* @__PURE__ */ new Map();
|
|
158329
158318
|
retryTimer;
|
|
@@ -158649,13 +158638,6 @@ var LinkedMachineConnection = class {
|
|
|
158649
158638
|
isCurrent(c, line) {
|
|
158650
158639
|
return !!line.credential && c.id === line.credential && this.isPairedHere(c);
|
|
158651
158640
|
}
|
|
158652
|
-
/**
|
|
158653
|
-
* The credentials used on the machine under this hub's own label that this hub did not pair (#1405 review), and that
|
|
158654
|
-
* `listed` does not name already, as used and gone. The machine's host leaves out a credential whose label's next one
|
|
158655
|
-
* was paired the same way, taking it for its pairer again; under a hub's label only the hub can tell. Counted from the
|
|
158656
|
-
* first use there of one this hub paired in its present run, so the credentials of its own earlier runs, which it does
|
|
158657
|
-
* not remember, are not taken for another's
|
|
158658
|
-
*/
|
|
158659
158641
|
/**
|
|
158660
158642
|
* Marks, in a list the host answered with `nonce`, the entries whose `check` is of the secret this hub holds under
|
|
158661
158643
|
* their id (`own`). An entry without a check (a host from before it) or a credential this hub holds no secret of is not
|
|
@@ -158667,6 +158649,13 @@ var LinkedMachineConnection = class {
|
|
|
158667
158649
|
return c.check === void 0 || !verifier ? c : { ...c, own: c.check === credentialCheck(verifier, nonce) };
|
|
158668
158650
|
}) ?? null;
|
|
158669
158651
|
}
|
|
158652
|
+
/**
|
|
158653
|
+
* The credentials used on the machine under this hub's own label that this hub did not pair (#1405 review), and that
|
|
158654
|
+
* `listed` does not name already, as used and gone. The machine's host leaves out a credential whose label's next one
|
|
158655
|
+
* was paired the same way, taking it for its pairer again; under a hub's label only the hub can tell. Counted from the
|
|
158656
|
+
* first use there of one this hub paired in its present run, so the credentials of its own earlier runs, which it does
|
|
158657
|
+
* not remember, are not taken for another's
|
|
158658
|
+
*/
|
|
158670
158659
|
notPairedHere(line, listed2) {
|
|
158671
158660
|
if (!line.pairedAs) return [];
|
|
158672
158661
|
const first = this.usedUnderHubLabels.findIndex((u) => this.isPairedHere(u));
|
|
@@ -158854,7 +158843,6 @@ var LinkedMachineConnection = class {
|
|
|
158854
158843
|
observe(kind, list2) {
|
|
158855
158844
|
if (kind === "sessions") {
|
|
158856
158845
|
this.shown.clear();
|
|
158857
|
-
this.hidden.clear();
|
|
158858
158846
|
this.coordinators.clear();
|
|
158859
158847
|
for (const s of list2) this.classify(s);
|
|
158860
158848
|
this.deps.mirror.replace(this.id, "sessions", list2.filter((s) => !hiddenKind(s)));
|
|
@@ -159208,6 +159196,7 @@ var LinkedMachineConnection = class {
|
|
|
159208
159196
|
// A host that can prove it holds the token is asked to, before the token goes out (#838)
|
|
159209
159197
|
{
|
|
159210
159198
|
redials: LINK_REDIALS,
|
|
159199
|
+
stableMs: this.deps.stableMs ?? STABLE_MS,
|
|
159211
159200
|
...this.deps.client,
|
|
159212
159201
|
url: endpoint.url,
|
|
159213
159202
|
token: endpoint.token,
|
|
@@ -159351,8 +159340,7 @@ var LinkedMachineConnection = class {
|
|
|
159351
159340
|
}
|
|
159352
159341
|
classify(s) {
|
|
159353
159342
|
if (!isObj(s) || typeof s.id !== "string") return;
|
|
159354
|
-
if (hiddenKind(s)) this.
|
|
159355
|
-
else this.shown.add(s.id);
|
|
159343
|
+
if (!hiddenKind(s)) this.shown.add(s.id);
|
|
159356
159344
|
if (s.kind === "coordinator") this.coordinators.set(s.id, s);
|
|
159357
159345
|
}
|
|
159358
159346
|
transportDown(reason) {
|
|
@@ -159367,14 +159355,6 @@ var LinkedMachineConnection = class {
|
|
|
159367
159355
|
nextStep() {
|
|
159368
159356
|
return this.autoReconnect ? KEEPS_TRYING : WAITS_FOR_PERSON;
|
|
159369
159357
|
}
|
|
159370
|
-
/**
|
|
159371
|
-
* Asks the machine again after a delay. `backoff` (the default): the next step of the backoff, and nothing at all
|
|
159372
|
-
* while Auto reconnect is off (#1341), where only the person's Connect tries again (after the client's quick redials,
|
|
159373
|
-
* which an off machine keeps). `within`: part of an attempt that
|
|
159374
|
-
* is under way or of the person's own request (a refusal asked again at once, a Connect that met an opening), so it
|
|
159375
|
-
* goes ahead either way. `reads`: the versions check looking again for a change on either side; with the switch off it
|
|
159376
|
-
* reads the line and connects nothing (`readsOnly`)
|
|
159377
|
-
*/
|
|
159378
159358
|
/** The link is up, or was just: remembers when, and keeps it for the next start of this host */
|
|
159379
159359
|
seen() {
|
|
159380
159360
|
this.lastConnectedAt = Date.now();
|
|
@@ -159385,6 +159365,14 @@ var LinkedMachineConnection = class {
|
|
|
159385
159365
|
clearTimeout(this.retryTimer);
|
|
159386
159366
|
this.nextAttemptAt = null;
|
|
159387
159367
|
}
|
|
159368
|
+
/**
|
|
159369
|
+
* Asks the machine again after a delay. `backoff` (the default): the next step of the backoff, and nothing at all
|
|
159370
|
+
* while Auto reconnect is off (#1341), where only the person's Connect tries again (after the client's quick redials,
|
|
159371
|
+
* which an off machine keeps). `within`: part of an attempt that
|
|
159372
|
+
* is under way or of the person's own request (a refusal asked again at once, a Connect that met an opening), so it
|
|
159373
|
+
* goes ahead either way. `reads`: the versions check looking again for a change on either side; with the switch off it
|
|
159374
|
+
* reads the line and connects nothing (`readsOnly`)
|
|
159375
|
+
*/
|
|
159388
159376
|
retryLater(fixedMs, mode = "backoff") {
|
|
159389
159377
|
if (this.stopped || this.held) return;
|
|
159390
159378
|
if (mode === "backoff" && !this.autoReconnect && !this.inOperation()) return;
|
|
@@ -159806,13 +159794,10 @@ var LinkedMachine = class extends LinkedMachineConnection {
|
|
|
159806
159794
|
const client = this.client;
|
|
159807
159795
|
if (!client?.connected) return "unreachable";
|
|
159808
159796
|
try {
|
|
159809
|
-
|
|
159810
|
-
return r?.allowed === true ? "on" : "off";
|
|
159797
|
+
return await askHubAuto((method, params) => client.call(method, params), allowed);
|
|
159811
159798
|
} catch (err) {
|
|
159812
|
-
const message = err?.message ?? String(err);
|
|
159813
|
-
if (/^Unknown method/.test(message)) return "older";
|
|
159814
159799
|
if (!client.connected) return "unreachable";
|
|
159815
|
-
throw rpcError(`${this.name} did not answer for its "Allow auto from a linked computer": ${
|
|
159800
|
+
throw rpcError(`${this.name} did not answer for its "Allow auto from a linked computer": ${errorMessage(err)}`);
|
|
159816
159801
|
}
|
|
159817
159802
|
}
|
|
159818
159803
|
/**
|
|
@@ -160243,8 +160228,8 @@ function linkedSessions(machines, route = notRouted, hubName2, options = {}) {
|
|
|
160243
160228
|
await settle(m);
|
|
160244
160229
|
return m.reachable ? null : { ok: false, error: awaySentence(m, tail) };
|
|
160245
160230
|
};
|
|
160246
|
-
const attempt = async (m, run4) => {
|
|
160247
|
-
const down = await reach(m,
|
|
160231
|
+
const attempt = async (m, run4, tail = TAIL.tryAgain) => {
|
|
160232
|
+
const down = await reach(m, tail);
|
|
160248
160233
|
if (down) return down;
|
|
160249
160234
|
try {
|
|
160250
160235
|
return { ok: true, ...await run4() };
|
|
@@ -160257,13 +160242,13 @@ function linkedSessions(machines, route = notRouted, hubName2, options = {}) {
|
|
|
160257
160242
|
const m = find(id3);
|
|
160258
160243
|
return m ? [m, null] : [null, { ok: false, error: refusal2(id3) }];
|
|
160259
160244
|
};
|
|
160260
|
-
const via = async (id3, run4) => {
|
|
160245
|
+
const via = async (id3, run4, tail) => {
|
|
160261
160246
|
const [m, refused2] = found(id3);
|
|
160262
|
-
return m ? attempt(m, () => run4(m)) : refused2;
|
|
160247
|
+
return m ? attempt(m, () => run4(m), tail) : refused2;
|
|
160263
160248
|
};
|
|
160264
160249
|
return [found, via];
|
|
160265
160250
|
};
|
|
160266
|
-
const [
|
|
160251
|
+
const [, viaSession] = reaching(owner, (id3) => `Not a session of a linked machine: ${id3}`);
|
|
160267
160252
|
const [findProject2, viaProject] = reaching(owner, (id3) => `Not a project of a linked machine: ${id3}`);
|
|
160268
160253
|
const [findMachineRef, viaMachine] = reaching(machineById, (id3) => `No linked machine ${id3}`);
|
|
160269
160254
|
const listOf = async (m, method) => {
|
|
@@ -160459,8 +160444,9 @@ function linkedSessions(machines, route = notRouted, hubName2, options = {}) {
|
|
|
160459
160444
|
},
|
|
160460
160445
|
hubAuto(machineId) {
|
|
160461
160446
|
return viaMachine(machineId, async (m) => {
|
|
160462
|
-
const
|
|
160463
|
-
|
|
160447
|
+
const state = await askHubAuto((method, params) => m.call(method, params));
|
|
160448
|
+
if (state === "older") throw new Error(`${m.name} runs a Centralu from before "Allow auto from a linked computer"; update it there`);
|
|
160449
|
+
return { allowed: state === "on" };
|
|
160464
160450
|
});
|
|
160465
160451
|
},
|
|
160466
160452
|
origin(project2) {
|
|
@@ -160625,20 +160611,18 @@ function linkedSessions(machines, route = notRouted, hubName2, options = {}) {
|
|
|
160625
160611
|
const p = (m.lastKnown("projects") ?? []).find((x) => isObj(x) && x.id === s.projectId);
|
|
160626
160612
|
return { name: oneLine4(s.name), project: `${isObj(p) ? oneLine4(p.name) : "(none)"} on ${m.name}` };
|
|
160627
160613
|
},
|
|
160628
|
-
|
|
160629
|
-
|
|
160630
|
-
|
|
160631
|
-
|
|
160632
|
-
|
|
160633
|
-
|
|
160634
|
-
|
|
160635
|
-
|
|
160636
|
-
|
|
160637
|
-
|
|
160638
|
-
|
|
160639
|
-
|
|
160640
|
-
return { ok: false, error: cause(err) };
|
|
160641
|
-
}
|
|
160614
|
+
lastWords(sessionId, maxChars, opts) {
|
|
160615
|
+
return viaSession(
|
|
160616
|
+
sessionId,
|
|
160617
|
+
async (m) => {
|
|
160618
|
+
const answer = await m.call("host.lastAnswer", { sessionId: m.q.strip(sessionId, "The session"), maxChars: Math.min(maxChars, ANSWER_MAX_CHARS2), ...opts?.turn ? { turn: true } : {}, ...opts?.since !== void 0 ? { since: opts.since } : {} });
|
|
160619
|
+
const r = HostLastAnswerResult.safeParse(answer);
|
|
160620
|
+
if (!r.success) throw new Error(`${m.name} answered in a shape this Centralu does not read`);
|
|
160621
|
+
const text3 = r.data.text.length > maxChars || r.data.length > r.data.text.length ? `${r.data.text.slice(0, maxChars)}\u2026` : r.data.text;
|
|
160622
|
+
return { text: text3, length: r.data.length, seq: r.data.seq };
|
|
160623
|
+
},
|
|
160624
|
+
TAIL.none
|
|
160625
|
+
);
|
|
160642
160626
|
},
|
|
160643
160627
|
async list() {
|
|
160644
160628
|
const perMachine = await Promise.all(
|
|
@@ -160656,42 +160640,40 @@ function linkedSessions(machines, route = notRouted, hubName2, options = {}) {
|
|
|
160656
160640
|
machineOf(sessionId) {
|
|
160657
160641
|
return owner(sessionId)?.name ?? null;
|
|
160658
160642
|
},
|
|
160659
|
-
|
|
160660
|
-
const [m, refusal2] = findSession(sessionId);
|
|
160661
|
-
if (!m) return refusal2;
|
|
160662
|
-
const down = await reach(m, TAIL.readAgain);
|
|
160663
|
-
if (down) return down;
|
|
160643
|
+
read(sessionId, limit, opts) {
|
|
160664
160644
|
const toolMode = toolLinesOf(opts?.tools);
|
|
160665
|
-
|
|
160666
|
-
|
|
160667
|
-
|
|
160668
|
-
|
|
160669
|
-
|
|
160670
|
-
|
|
160671
|
-
|
|
160672
|
-
|
|
160673
|
-
|
|
160674
|
-
|
|
160675
|
-
|
|
160676
|
-
|
|
160677
|
-
|
|
160678
|
-
|
|
160679
|
-
|
|
160680
|
-
|
|
160681
|
-
|
|
160682
|
-
|
|
160683
|
-
|
|
160684
|
-
|
|
160685
|
-
|
|
160686
|
-
|
|
160687
|
-
|
|
160645
|
+
return viaSession(
|
|
160646
|
+
sessionId,
|
|
160647
|
+
async (m) => {
|
|
160648
|
+
const answer = await m.call("host.readSession", {
|
|
160649
|
+
sessionId: m.q.strip(sessionId, "The session"),
|
|
160650
|
+
...limit !== void 0 ? { limit } : {},
|
|
160651
|
+
...opts?.around !== void 0 ? { around: opts.around } : {},
|
|
160652
|
+
...opts?.tools !== void 0 ? { toolLines: toolMode } : {},
|
|
160653
|
+
...opts?.maxChars !== void 0 ? { maxChars: budgetOf(opts.maxChars) } : {},
|
|
160654
|
+
...opts?.before !== void 0 ? { before: opts.before } : {},
|
|
160655
|
+
...opts?.from !== void 0 ? { from: opts.from } : {},
|
|
160656
|
+
...opts?.offset !== void 0 ? { offset: opts.offset } : {}
|
|
160657
|
+
});
|
|
160658
|
+
const r = HostReadSessionResult.safeParse(answer);
|
|
160659
|
+
if (!r.success) throw new Error(`${m.name} answered in a shape this Centralu does not read`);
|
|
160660
|
+
const all = r.data.lines;
|
|
160661
|
+
const kept = toolMode === "none" ? all.filter((l) => !isToolLine(l)) : all;
|
|
160662
|
+
const spot = Math.min(Math.floor(Math.min(limit ?? 40, 200) / 2), all.length - 1);
|
|
160663
|
+
const around = opts?.around !== void 0 && opts.before === void 0 && opts.from === void 0;
|
|
160664
|
+
const center = !around ? null : Math.min(toolMode === "none" ? all.slice(0, spot).filter((l) => !isToolLine(l)).length : spot, kept.length - 1);
|
|
160665
|
+
const lines = fitMachineLines(kept, budgetOf(opts?.maxChars), center);
|
|
160666
|
+
return { state: oneLine4(r.data.state, 40), lines: lines.map(fencedLine) };
|
|
160667
|
+
},
|
|
160668
|
+
TAIL.readAgain
|
|
160669
|
+
);
|
|
160688
160670
|
},
|
|
160689
160671
|
async recall(query, limit) {
|
|
160690
160672
|
const notSearched = [];
|
|
160691
160673
|
const hits = await Promise.all(
|
|
160692
160674
|
[...machines()].map(async (m) => {
|
|
160693
160675
|
if (!m.reachable) {
|
|
160694
|
-
notSearched.push(`${m.name}
|
|
160676
|
+
notSearched.push(m.waitsForPerson ? `${m.name} ${NOT_CONNECTED_PHRASE}` : `${m.name} (${NOT_REACHABLE})`);
|
|
160695
160677
|
return [];
|
|
160696
160678
|
}
|
|
160697
160679
|
try {
|
|
@@ -161100,7 +161082,7 @@ var Router = class {
|
|
|
161100
161082
|
if (method === "apps.list" && isObj(params) && typeof params.machine === "string") {
|
|
161101
161083
|
return this.byMachine(method, params, { result: (r, q2) => Array.isArray(r) ? r.map((a) => q2.app(a)) : r }, ctx);
|
|
161102
161084
|
}
|
|
161103
|
-
return this.merge(method, params);
|
|
161085
|
+
return this.merge(method, params, ctx);
|
|
161104
161086
|
}
|
|
161105
161087
|
};
|
|
161106
161088
|
/** The linked machine an id names, or null for the hub's own (an unprefixed id, or a prefix no link has) */
|
|
@@ -161213,7 +161195,7 @@ var Router = class {
|
|
|
161213
161195
|
* mirror for it, marked `unreachable` and with `live` as last known, so the UI neither drops
|
|
161214
161196
|
* them nor tries to wake them (§5, "Recovery").
|
|
161215
161197
|
*/
|
|
161216
|
-
async merge(method, params) {
|
|
161198
|
+
async merge(method, params, ctx) {
|
|
161217
161199
|
const machines = [...this.opts.machines()];
|
|
161218
161200
|
switch (method) {
|
|
161219
161201
|
case "sessions.list":
|
|
@@ -161221,9 +161203,9 @@ var Router = class {
|
|
|
161221
161203
|
case "projects.list":
|
|
161222
161204
|
return this.mergeLists(machines, "projects", params);
|
|
161223
161205
|
case "projects.reorder":
|
|
161224
|
-
return this.reorderProjects(machines, params);
|
|
161206
|
+
return this.reorderProjects(machines, params, ctx);
|
|
161225
161207
|
case "approvals.deleteRule":
|
|
161226
|
-
return this.deleteRule(machines, params);
|
|
161208
|
+
return this.deleteRule(machines, params, ctx);
|
|
161227
161209
|
default:
|
|
161228
161210
|
return this.mergeSimple(method, machines, params);
|
|
161229
161211
|
}
|
|
@@ -161285,7 +161267,7 @@ var Router = class {
|
|
|
161285
161267
|
* out of it: a stale window cannot move what it never saw. The kept order forgets a project the hub no longer lists,
|
|
161286
161268
|
* unless it is a linked machine's, which may only be out of reach.
|
|
161287
161269
|
*/
|
|
161288
|
-
async reorderProjects(machines, params) {
|
|
161270
|
+
async reorderProjects(machines, params, ctx) {
|
|
161289
161271
|
const { orderedIds } = RpcMethods["projects.reorder"].params.parse(params);
|
|
161290
161272
|
const kept = this.opts.order?.read() ?? [];
|
|
161291
161273
|
const full = keepUnseen(orderedIds, kept);
|
|
@@ -161302,7 +161284,7 @@ var Router = class {
|
|
|
161302
161284
|
const ordered = perMachine.get(m);
|
|
161303
161285
|
if (!ordered) return this.listOf(m, "projects");
|
|
161304
161286
|
try {
|
|
161305
|
-
return m.q.projects(await this.forward(m, "projects.reorder", { orderedIds: ordered }));
|
|
161287
|
+
return m.q.projects(await this.forward(m, "projects.reorder", { orderedIds: ordered }, ctx));
|
|
161306
161288
|
} catch {
|
|
161307
161289
|
return this.present(m, "projects", m.lastKnown("projects") ?? [], true);
|
|
161308
161290
|
}
|
|
@@ -161315,13 +161297,13 @@ var Router = class {
|
|
|
161315
161297
|
}
|
|
161316
161298
|
return machines.length > 0 ? this.inPersonsOrder(all) : all;
|
|
161317
161299
|
}
|
|
161318
|
-
async deleteRule(machines, params) {
|
|
161300
|
+
async deleteRule(machines, params, ctx) {
|
|
161319
161301
|
const id3 = isObj(params) ? params.id : void 0;
|
|
161320
161302
|
const folded = typeof id3 === "number" ? decodeNumber(id3) : null;
|
|
161321
161303
|
if (!folded) return this.opts.local("approvals.deleteRule", params);
|
|
161322
161304
|
const m = machines.find((x) => x.q.slot === folded.slot);
|
|
161323
161305
|
if (!m) throw rpcError("That rule belongs to a machine that is no longer linked");
|
|
161324
|
-
return this.forward(m, "approvals.deleteRule", { id: folded.n });
|
|
161306
|
+
return this.forward(m, "approvals.deleteRule", { id: folded.n }, ctx);
|
|
161325
161307
|
}
|
|
161326
161308
|
/** The other merges: every reachable machine, each answer in the hub's terms, combined by kind */
|
|
161327
161309
|
async mergeSimple(method, machines, params) {
|
|
@@ -161892,7 +161874,7 @@ var handedToken = launch.tokenOnStdin ? await readHandedToken(process.stdin).cat
|
|
|
161892
161874
|
console.error(`[agent-host] the launcher did not hand over the token: ${e.message}`);
|
|
161893
161875
|
process.exit(1);
|
|
161894
161876
|
}) : launch.token;
|
|
161895
|
-
var BUILD = true ? "
|
|
161877
|
+
var BUILD = true ? "62e7369" : "dev";
|
|
161896
161878
|
var { values } = parseArgs({
|
|
161897
161879
|
options: {
|
|
161898
161880
|
port: { type: "string", default: "5175" },
|