pi-radar 0.9.0 → 0.11.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 +8 -10
- package/dist/extension/baseline.js +343 -361
- package/dist/extension/radar-children.js +108 -218
- package/dist/extension/radar-client.js +103 -15
- package/dist/extension/radar-execution.js +26 -17
- package/dist/extension/radar-inspection.js +0 -45
- package/dist/extension/radar-launch.js +24 -185
- package/dist/extension/radar-publication.js +26 -17
- package/dist/extension/radar-runtime.js +1 -1
- package/dist/index.js +317 -488
- package/extension/generated-contracts.ts +362 -13
- package/extension/radar-children.ts +60 -155
- package/extension/radar-client.ts +130 -19
- package/extension/radar-inspection.ts +2 -77
- package/extension/radar-launch.ts +52 -391
- package/extension/radar-publication.ts +36 -53
- package/extension/radar-runtime.ts +1 -1
- package/package.json +1 -1
- package/schema/public/v1/observation-get-response.deserialize.schema.json +5 -25
- package/schema/public/v1/observation-get-response.serialize.schema.json +7 -29
- package/schema/public/v1/observation-snapshot.deserialize.schema.json +5 -25
- package/schema/public/v1/observation-snapshot.serialize.schema.json +7 -29
- package/schema/public/v1/spawn-list-page.deserialize.schema.json +40 -17
- package/schema/public/v1/spawn-list-page.serialize.schema.json +40 -17
- package/schema/public/v1/subject-assignment-acquire.deserialize.schema.json +78 -0
- package/schema/public/v1/subject-assignment-acquire.serialize.schema.json +79 -0
- package/schema/public/v1/subject-assignment-publish.deserialize.schema.json +310 -0
- package/schema/public/v1/subject-assignment-publish.serialize.schema.json +310 -0
- package/schema/public/v1/subject-assignment-retire.deserialize.schema.json +22 -0
- package/schema/public/v1/subject-assignment-retire.serialize.schema.json +22 -0
- package/schema/public/v1/subject-child-get-params.deserialize.schema.json +18 -0
- package/schema/public/v1/subject-child-get-params.serialize.schema.json +18 -0
- package/schema/public/v1/subject-children-params.deserialize.schema.json +51 -0
- package/schema/public/v1/subject-children-params.serialize.schema.json +54 -0
- package/schema/public/v1/subject-get-params.deserialize.schema.json +14 -0
- package/schema/public/v1/subject-get-params.serialize.schema.json +14 -0
- package/schema/public/v1/subject-get-result.deserialize.schema.json +566 -0
- package/schema/public/v1/subject-get-result.serialize.schema.json +573 -0
- package/schema/public/v1/subject-list-params.deserialize.schema.json +38 -0
- package/schema/public/v1/subject-list-params.serialize.schema.json +43 -0
- package/schema/public/v1/subject-list-result.deserialize.schema.json +575 -0
- package/schema/public/v1/subject-list-result.serialize.schema.json +583 -0
package/dist/index.js
CHANGED
|
@@ -9,61 +9,14 @@ var radar_children_exports = {};
|
|
|
9
9
|
__export(radar_children_exports, {
|
|
10
10
|
createRadarChildren: () => createRadarChildren
|
|
11
11
|
});
|
|
12
|
-
|
|
13
|
-
// extension/radar-inspection.ts
|
|
14
|
-
var radar_inspection_exports = {};
|
|
15
|
-
__export(radar_inspection_exports, {
|
|
16
|
-
inspectRadarChild: () => inspectRadarChild,
|
|
17
|
-
provedRead: () => provedRead
|
|
18
|
-
});
|
|
19
|
-
var INSPECTION_LINES = 80;
|
|
20
|
-
var INSPECTION_OUTPUT_BYTES = 16 * 1024;
|
|
21
|
-
async function provedRead(client, target) {
|
|
22
|
-
const read = await client.agentGet(target.agentId);
|
|
23
|
-
return read.ok === true && read.value.verification === "verified" && read.value.location?.pane === target.paneId && (target.session === void 0 || read.value.contextSession === target.session) ? read.value : void 0;
|
|
24
|
-
}
|
|
25
|
-
async function inspectRadarChild(client, target) {
|
|
26
|
-
const before = await provedRead(client, target);
|
|
27
|
-
if (before === void 0)
|
|
28
|
-
throw new Error("Inspection target identity did not match");
|
|
29
|
-
const output = await client.output({
|
|
30
|
-
pane_id: target.paneId,
|
|
31
|
-
source: "recent_unwrapped",
|
|
32
|
-
lines: INSPECTION_LINES
|
|
33
|
-
});
|
|
34
|
-
if (output.ok === false)
|
|
35
|
-
throw new Error(
|
|
36
|
-
`Inspection output unavailable (${output.code}: ${output.message})`
|
|
37
|
-
);
|
|
38
|
-
const foreground = await client.processInfo({ pane_id: target.paneId });
|
|
39
|
-
const after = await provedRead(client, target);
|
|
40
|
-
if (after === void 0)
|
|
41
|
-
throw new Error("Inspection target changed during capture");
|
|
42
|
-
const lines = output.value.text.trim().split(/\r?\n/).slice(-INSPECTION_LINES);
|
|
43
|
-
const bounded2 = utf8Tail(lines.join("\n"), INSPECTION_OUTPUT_BYTES);
|
|
44
|
-
return Object.freeze({
|
|
45
|
-
capturedAt: Date.now(),
|
|
46
|
-
recentOutputTruncated: output.value.truncated || bounded2.truncated,
|
|
47
|
-
...bounded2.text ? { recentOutput: bounded2.text } : {},
|
|
48
|
-
...foreground.ok === true ? { foreground: foreground.value } : {},
|
|
49
|
-
...after.activity !== void 0 ? { activity: after.activity } : {}
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
function utf8Tail(value, maxBytes) {
|
|
53
|
-
const bytes = Buffer.from(value);
|
|
54
|
-
if (bytes.length <= maxBytes) return { text: value, truncated: false };
|
|
55
|
-
let start = bytes.length - maxBytes;
|
|
56
|
-
while (start < bytes.length && (bytes[start] & 192) === 128) start++;
|
|
57
|
-
return { text: bytes.subarray(start).toString(), truncated: true };
|
|
58
|
-
}
|
|
12
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
59
13
|
|
|
60
14
|
// extension/radar-launch.ts
|
|
61
15
|
var radar_launch_exports = {};
|
|
62
16
|
__export(radar_launch_exports, {
|
|
17
|
+
launchFailure: () => launchFailure,
|
|
63
18
|
launchRadarWorker: () => launchRadarWorker,
|
|
64
|
-
|
|
65
|
-
readProcessGone: () => readProcessGone,
|
|
66
|
-
relaunchRefusalForChild: () => relaunchRefusalForChild
|
|
19
|
+
waitForChild: () => waitForChild
|
|
67
20
|
});
|
|
68
21
|
import { setTimeout as delay } from "node:timers/promises";
|
|
69
22
|
|
|
@@ -87,7 +40,6 @@ __export(radar_client_exports, {
|
|
|
87
40
|
RADAR_SPAWN_CLOSE_CONTRACT: () => RADAR_SPAWN_CLOSE_CONTRACT,
|
|
88
41
|
RADAR_SPAWN_CONTRACT: () => RADAR_SPAWN_CONTRACT,
|
|
89
42
|
RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT: () => RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT,
|
|
90
|
-
RADAR_SPAWN_LIST_CONTRACT: () => RADAR_SPAWN_LIST_CONTRACT,
|
|
91
43
|
boundedRadarAssignment: () => boundedRadarAssignment,
|
|
92
44
|
boundedRadarFacts: () => boundedRadarFacts,
|
|
93
45
|
boundedRadarSnapshot: () => boundedRadarSnapshot,
|
|
@@ -104,11 +56,10 @@ import { lstatSync } from "node:fs";
|
|
|
104
56
|
import { createConnection } from "node:net";
|
|
105
57
|
import { dirname, join } from "node:path";
|
|
106
58
|
var RADAR_PROTOCOL_VERSION = 2;
|
|
107
|
-
var RADAR_MINIMUM_REVISION =
|
|
59
|
+
var RADAR_MINIMUM_REVISION = 9;
|
|
108
60
|
var RADAR_REGISTRY_CONTRACT = "registry.v1";
|
|
109
61
|
var RADAR_REGISTRY_FACTS_CONTRACT = "registry.facts.v1";
|
|
110
62
|
var RADAR_SPAWN_CONTRACT = "spawn.v1";
|
|
111
|
-
var RADAR_SPAWN_LIST_CONTRACT = "spawn.list.v1";
|
|
112
63
|
var RADAR_SPAWN_CLOSE_CONTRACT = "spawn.close.v1";
|
|
113
64
|
var RADAR_LIFECYCLE_RESTART_CONTRACT = "lifecycle.restart.v1";
|
|
114
65
|
var RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT = "spawn.launch_context.v1";
|
|
@@ -418,6 +369,9 @@ function spawnEdge(value) {
|
|
|
418
369
|
const agentId = stringAt(bound, "registration_id");
|
|
419
370
|
const subjectId = stringAt(bound, "subject_id");
|
|
420
371
|
const exit = exitAt(recordAt(value, "exit_observation"));
|
|
372
|
+
const withdrawnRecord = recordAt(value, "withdrawn");
|
|
373
|
+
const withdrawnAt = stringAt(withdrawnRecord, "at");
|
|
374
|
+
const withdrawnReason = stringAt(withdrawnRecord, "reason");
|
|
421
375
|
return {
|
|
422
376
|
ok: true,
|
|
423
377
|
value: {
|
|
@@ -434,7 +388,9 @@ function spawnEdge(value) {
|
|
|
434
388
|
...isUuid(subjectId) ? { subject_id: subjectId } : {}
|
|
435
389
|
}
|
|
436
390
|
} : {},
|
|
437
|
-
...exit !== void 0 ? { exit_observation: exit } : {}
|
|
391
|
+
...exit !== void 0 ? { exit_observation: exit } : {},
|
|
392
|
+
...stringAt(value, "granted_at") !== void 0 ? { granted_at: stringAt(value, "granted_at") } : {},
|
|
393
|
+
...withdrawnAt !== void 0 && withdrawnReason !== void 0 ? { withdrawn: { at: withdrawnAt, reason: withdrawnReason } } : {}
|
|
438
394
|
}
|
|
439
395
|
};
|
|
440
396
|
}
|
|
@@ -444,6 +400,14 @@ var PROCESS_VERDICTS = [
|
|
|
444
400
|
"mismatched",
|
|
445
401
|
"unavailable"
|
|
446
402
|
];
|
|
403
|
+
function subjectAt(value) {
|
|
404
|
+
const subjectId = recordAt(value, "subject_id");
|
|
405
|
+
const session = recordAt(value, "session_id");
|
|
406
|
+
const state = recordAt(value, "state");
|
|
407
|
+
if (!isUuid(subjectId) || session != null && !isUuid(session) || !["starting", "live", "detached", "unknown"].includes(String(state)))
|
|
408
|
+
return protocol("subject read carries no valid identity or attachment state");
|
|
409
|
+
return { ok: true, value };
|
|
410
|
+
}
|
|
447
411
|
function agentProcessAt(agent) {
|
|
448
412
|
const process2 = recordAt(agent, "process");
|
|
449
413
|
const claimed = recordAt(process2, "claimed");
|
|
@@ -836,7 +800,9 @@ function createRadarClient(options = {}) {
|
|
|
836
800
|
return withRegistry(async () => {
|
|
837
801
|
const answer = await call("agent.presence", { ...request });
|
|
838
802
|
if (!answer.ok) return answer;
|
|
839
|
-
|
|
803
|
+
const decoded = decodePresenceAnswer(answer.value);
|
|
804
|
+
if (decoded.ok) processRegistrationId = decoded.value.registration_id;
|
|
805
|
+
return decoded;
|
|
840
806
|
});
|
|
841
807
|
},
|
|
842
808
|
async releasePresence(request) {
|
|
@@ -871,9 +837,13 @@ function createRadarClient(options = {}) {
|
|
|
871
837
|
},
|
|
872
838
|
async acquire(request) {
|
|
873
839
|
return withRegistryChannel(request.channel, async () => {
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
|
|
840
|
+
const assignment = request.channel === "assignment";
|
|
841
|
+
if (assignment && processRegistrationId === void 0)
|
|
842
|
+
return { ok: false, code: "refused", message: "assignment requires a granted parent" };
|
|
843
|
+
if (assignment && request.subject_id === void 0)
|
|
844
|
+
return { ok: false, code: "bad_params", message: "an assignment names a child subject" };
|
|
845
|
+
const answer = await call(assignment ? "subject.assignment.acquire" : "agent.acquire", {
|
|
846
|
+
...assignment ? { registration_id: processRegistrationId, subject_id: request.subject_id } : { registration_id: request.registration_id, channel: request.channel },
|
|
877
847
|
publisher: request.publisher,
|
|
878
848
|
...request.replace !== void 0 ? { replace: request.replace } : {}
|
|
879
849
|
});
|
|
@@ -900,15 +870,19 @@ function createRadarClient(options = {}) {
|
|
|
900
870
|
}
|
|
901
871
|
}
|
|
902
872
|
return withRegistryChannel(request.channel, async () => {
|
|
903
|
-
const
|
|
904
|
-
registration_id: request.registration_id,
|
|
905
|
-
channel: request.channel,
|
|
873
|
+
const update = {
|
|
906
874
|
writer_handle: request.writer_handle,
|
|
907
875
|
sequence: request.sequence,
|
|
908
876
|
...request.lease_ms !== void 0 ? { lease_ms: request.lease_ms } : {},
|
|
909
877
|
...request.observed_at !== void 0 ? { observed_at: request.observed_at } : {},
|
|
910
878
|
snapshot
|
|
911
|
-
}
|
|
879
|
+
};
|
|
880
|
+
const assignment = request.channel === "assignment";
|
|
881
|
+
if (assignment && processRegistrationId === void 0)
|
|
882
|
+
return { ok: false, code: "refused", message: "assignment requires a granted parent" };
|
|
883
|
+
if (assignment && request.subject_id === void 0)
|
|
884
|
+
return { ok: false, code: "bad_params", message: "an assignment names a child subject" };
|
|
885
|
+
const answer = await call(assignment ? "subject.assignment.publish" : "agent.publish", assignment ? { registration_id: processRegistrationId, subject_id: request.subject_id, update } : { registration_id: request.registration_id, channel: request.channel, ...update });
|
|
912
886
|
if (answer.ok === false) return answer;
|
|
913
887
|
const channel = answer.value.channel;
|
|
914
888
|
const writer = channel?.writer;
|
|
@@ -949,7 +923,15 @@ function createRadarClient(options = {}) {
|
|
|
949
923
|
},
|
|
950
924
|
async retire(request) {
|
|
951
925
|
return withRegistryChannel(request.channel, async () => {
|
|
952
|
-
const
|
|
926
|
+
const assignment = request.channel === "assignment";
|
|
927
|
+
if (assignment && processRegistrationId === void 0)
|
|
928
|
+
return { ok: false, code: "refused", message: "assignment requires a granted parent" };
|
|
929
|
+
if (assignment && request.subject_id === void 0)
|
|
930
|
+
return { ok: false, code: "bad_params", message: "an assignment names a child subject" };
|
|
931
|
+
const answer = await call(assignment ? "subject.assignment.retire" : "agent.retire", {
|
|
932
|
+
...assignment ? { registration_id: processRegistrationId, subject_id: request.subject_id } : { registration_id: request.registration_id, channel: request.channel },
|
|
933
|
+
writer_handle: request.writer_handle
|
|
934
|
+
});
|
|
953
935
|
return answer.ok === true ? { ok: true, value: void 0 } : answer;
|
|
954
936
|
});
|
|
955
937
|
},
|
|
@@ -1024,14 +1006,73 @@ function createRadarClient(options = {}) {
|
|
|
1024
1006
|
request.request_id
|
|
1025
1007
|
);
|
|
1026
1008
|
if (answer.ok === false) return answer;
|
|
1027
|
-
|
|
1009
|
+
const settled = focusOutcome(recordAt(answer.value, "request"), request.request_id);
|
|
1010
|
+
if (settled.ok === false) return settled;
|
|
1011
|
+
const launch = recordAt(recordAt(answer.value, "request"), "launch_request_id");
|
|
1012
|
+
if (!isUuid(launch)) return protocol("lifecycle.restart reply names no launch");
|
|
1013
|
+
return { ok: true, value: { ...settled.value, launch_request_id: launch } };
|
|
1014
|
+
});
|
|
1015
|
+
},
|
|
1016
|
+
async subjectChildren(request = {}) {
|
|
1017
|
+
const registrationId = processRegistrationId;
|
|
1018
|
+
if (registrationId === void 0)
|
|
1019
|
+
return { ok: false, code: "refused", message: "this process has no Radar registration" };
|
|
1020
|
+
return withRegistry(async () => {
|
|
1021
|
+
const params = { ...request, registration_id: registrationId };
|
|
1022
|
+
const answer = await call("subject.children", { ...params });
|
|
1023
|
+
if (!answer.ok) return answer;
|
|
1024
|
+
const subjects = recordAt(answer.value, "subjects");
|
|
1025
|
+
const next = recordAt(answer.value, "next");
|
|
1026
|
+
if (!Array.isArray(subjects) || next != null && !isUuid(next))
|
|
1027
|
+
return protocol("subject.children reply carries no valid page");
|
|
1028
|
+
for (const subject of subjects) {
|
|
1029
|
+
const decoded = subjectAt(subject);
|
|
1030
|
+
if (!decoded.ok) return decoded;
|
|
1031
|
+
}
|
|
1032
|
+
return { ok: true, value: answer.value };
|
|
1033
|
+
});
|
|
1034
|
+
},
|
|
1035
|
+
async childGet(subject_id) {
|
|
1036
|
+
const registrationId = processRegistrationId;
|
|
1037
|
+
if (registrationId === void 0)
|
|
1038
|
+
return { ok: false, code: "refused", message: "this process has no Radar registration" };
|
|
1039
|
+
if (!isUuid(subject_id))
|
|
1040
|
+
return { ok: false, code: "bad_params", message: "child requires a subject UUID" };
|
|
1041
|
+
return withRegistry(async () => {
|
|
1042
|
+
const params = { registration_id: registrationId, subject_id };
|
|
1043
|
+
const answer = await call("subject.child.get", { ...params });
|
|
1044
|
+
return answer.ok ? subjectAt(recordAt(answer.value, "subject")) : answer;
|
|
1045
|
+
});
|
|
1046
|
+
},
|
|
1047
|
+
async childInspect(subject_id) {
|
|
1048
|
+
const registration_id = processRegistrationId;
|
|
1049
|
+
if (registration_id === void 0)
|
|
1050
|
+
return { ok: false, code: "refused", message: "this process has no Radar registration" };
|
|
1051
|
+
return withRegistry(async () => {
|
|
1052
|
+
const answer = await call("subject.child.inspect", { registration_id, subject_id });
|
|
1053
|
+
if (!answer.ok) return answer;
|
|
1054
|
+
const subject = subjectAt(recordAt(answer.value, "subject"));
|
|
1055
|
+
if (!subject.ok) return subject;
|
|
1056
|
+
const output = recordAt(answer.value, "output");
|
|
1057
|
+
const text = stringAt(output, "text");
|
|
1058
|
+
if (text === void 0 || typeof recordAt(output, "truncated") !== "boolean")
|
|
1059
|
+
return protocol("child inspection has no output evidence");
|
|
1060
|
+
const foreground = recordAt(answer.value, "foreground");
|
|
1061
|
+
const activity = subject.value.execution?.snapshot?.snapshot;
|
|
1062
|
+
return { ok: true, value: {
|
|
1063
|
+
capturedAt: Date.now(),
|
|
1064
|
+
recentOutputTruncated: recordAt(output, "truncated"),
|
|
1065
|
+
...text ? { recentOutput: text } : {},
|
|
1066
|
+
...foreground ? { foreground } : {},
|
|
1067
|
+
...activity && "activity" in activity ? { activity: activity.activity } : {}
|
|
1068
|
+
} };
|
|
1028
1069
|
});
|
|
1029
1070
|
},
|
|
1030
1071
|
async spawnList(request) {
|
|
1031
1072
|
const registrationId = processRegistrationId;
|
|
1032
1073
|
if (registrationId === void 0)
|
|
1033
1074
|
return { ok: false, code: "refused", message: "spawn.list requires this client's process registration" };
|
|
1034
|
-
return
|
|
1075
|
+
return withRegistry(async () => {
|
|
1035
1076
|
const params = {
|
|
1036
1077
|
registration_id: registrationId,
|
|
1037
1078
|
...request.after !== void 0 ? { after: request.after } : {},
|
|
@@ -1394,215 +1435,9 @@ function boundedRadarSnapshot(snapshot) {
|
|
|
1394
1435
|
};
|
|
1395
1436
|
}
|
|
1396
1437
|
|
|
1397
|
-
// extension/radar-location.ts
|
|
1398
|
-
var radar_location_exports = {};
|
|
1399
|
-
__export(radar_location_exports, {
|
|
1400
|
-
radarLocalPane: () => radarLocalPane,
|
|
1401
|
-
radarPaneHandle: () => radarPaneHandle,
|
|
1402
|
-
resolveRadarProcessLocation: () => resolveRadarProcessLocation,
|
|
1403
|
-
tmuxServerName: () => tmuxServerName
|
|
1404
|
-
});
|
|
1405
|
-
import { basename } from "node:path";
|
|
1406
|
-
function radarLocalPane(env) {
|
|
1407
|
-
const pane = env.TMUX_PANE?.trim();
|
|
1408
|
-
return pane && /^%\d+$/.test(pane) ? pane : void 0;
|
|
1409
|
-
}
|
|
1410
|
-
function radarPaneHandle(qualified) {
|
|
1411
|
-
return qualified.slice(qualified.lastIndexOf(":") + 1);
|
|
1412
|
-
}
|
|
1413
|
-
function tmuxServerName(env) {
|
|
1414
|
-
const socket = env.TMUX?.trim().split(",")[0];
|
|
1415
|
-
return socket ? basename(socket) || void 0 : void 0;
|
|
1416
|
-
}
|
|
1417
|
-
function qualifiedOwnPane(env, backend, instance) {
|
|
1418
|
-
if (backend !== "tmux")
|
|
1419
|
-
return {
|
|
1420
|
-
absent: `the daemon serves the ${backend} backend, and Herdsman drives only tmux`
|
|
1421
|
-
};
|
|
1422
|
-
if (instance === void 0 || instance === "")
|
|
1423
|
-
return {
|
|
1424
|
-
pending: "the daemon did not name the tmux server it addresses"
|
|
1425
|
-
};
|
|
1426
|
-
const server = tmuxServerName(env);
|
|
1427
|
-
if (server === void 0)
|
|
1428
|
-
return {
|
|
1429
|
-
absent: "this process's own tmux client names no server, so it is not in the pane it would publish"
|
|
1430
|
-
};
|
|
1431
|
-
if (server !== instance)
|
|
1432
|
-
return {
|
|
1433
|
-
absent: `this process is attached to tmux server ${server}, not to ${instance}`
|
|
1434
|
-
};
|
|
1435
|
-
const pane = radarLocalPane(env);
|
|
1436
|
-
if (pane === void 0)
|
|
1437
|
-
return {
|
|
1438
|
-
absent: "this process's own environment names no tmux pane of that server"
|
|
1439
|
-
};
|
|
1440
|
-
return { pane: `tmux:${instance}:${pane}` };
|
|
1441
|
-
}
|
|
1442
|
-
function observedContainment(inventory, paneId) {
|
|
1443
|
-
const rows = inventory.panes.filter((pane) => pane.pane_id === paneId);
|
|
1444
|
-
if (rows.length === 0) return void 0;
|
|
1445
|
-
if (rows.length > 1) return { ambiguous: `topology reports pane ${paneId} twice` };
|
|
1446
|
-
const row = rows[0];
|
|
1447
|
-
const tab = inventory.tabs.find((candidate) => candidate.tab_id === row.tab_id);
|
|
1448
|
-
if (tab !== void 0 && tab.workspace_id !== row.workspace_id)
|
|
1449
|
-
return {
|
|
1450
|
-
ambiguous: `pane ${paneId} is reported in ${row.workspace_id}, but its tab belongs to ${tab.workspace_id}`
|
|
1451
|
-
};
|
|
1452
|
-
return { pane: row };
|
|
1453
|
-
}
|
|
1454
|
-
async function resolveRadarProcessLocation(client, env = process.env) {
|
|
1455
|
-
let ready;
|
|
1456
|
-
try {
|
|
1457
|
-
ready = await client.ping();
|
|
1458
|
-
} catch {
|
|
1459
|
-
return { state: "pending", reason: "the daemon did not answer this process" };
|
|
1460
|
-
}
|
|
1461
|
-
if (ready.ok === false)
|
|
1462
|
-
return {
|
|
1463
|
-
state: "pending",
|
|
1464
|
-
reason: `the daemon did not answer this process (${ready.code})`
|
|
1465
|
-
};
|
|
1466
|
-
const backend = ready.value.backend;
|
|
1467
|
-
if (backend === void 0 || backend === "none")
|
|
1468
|
-
return { state: "absent", reason: "the daemon serves no physical backend" };
|
|
1469
|
-
const instance = ready.value.instance ?? void 0;
|
|
1470
|
-
const own = qualifiedOwnPane(env, backend, instance);
|
|
1471
|
-
if ("absent" in own) return { state: "absent", reason: own.absent };
|
|
1472
|
-
if ("pending" in own) return { state: "pending", reason: own.pending };
|
|
1473
|
-
const paneId = own.pane;
|
|
1474
|
-
if (paneId === env.PI_HERDSMAN_PARENT_PANE_ID?.trim())
|
|
1475
|
-
return {
|
|
1476
|
-
state: "absent",
|
|
1477
|
-
reason: "this process is in the pane its launch was created beside, not in a pane of its own"
|
|
1478
|
-
};
|
|
1479
|
-
let inventory;
|
|
1480
|
-
try {
|
|
1481
|
-
inventory = await client.observe();
|
|
1482
|
-
} catch {
|
|
1483
|
-
return {
|
|
1484
|
-
state: "pending",
|
|
1485
|
-
reason: "the daemon's topology could not be read for this process's pane"
|
|
1486
|
-
};
|
|
1487
|
-
}
|
|
1488
|
-
if (inventory.ok === false)
|
|
1489
|
-
return {
|
|
1490
|
-
state: "pending",
|
|
1491
|
-
reason: `the daemon's topology could not be read (${inventory.code})`
|
|
1492
|
-
};
|
|
1493
|
-
const containment = observedContainment(inventory.value, paneId);
|
|
1494
|
-
if (containment === void 0)
|
|
1495
|
-
return {
|
|
1496
|
-
state: "pending",
|
|
1497
|
-
reason: `the daemon's topology does not report pane ${paneId} yet`
|
|
1498
|
-
};
|
|
1499
|
-
if ("ambiguous" in containment)
|
|
1500
|
-
return { state: "pending", reason: containment.ambiguous };
|
|
1501
|
-
const row = containment.pane;
|
|
1502
|
-
return {
|
|
1503
|
-
state: "resolved",
|
|
1504
|
-
location: {
|
|
1505
|
-
backend,
|
|
1506
|
-
...instance !== void 0 ? { instance } : {},
|
|
1507
|
-
workspace: row.workspace_id,
|
|
1508
|
-
tab: row.tab_id,
|
|
1509
|
-
pane: paneId
|
|
1510
|
-
}
|
|
1511
|
-
};
|
|
1512
|
-
}
|
|
1513
|
-
|
|
1514
1438
|
// extension/radar-launch.ts
|
|
1515
|
-
function unknownChild(reason) {
|
|
1516
|
-
return { kind: "unknown", diagnostic: trajectory(reason) };
|
|
1517
|
-
}
|
|
1518
|
-
function trajectory(reason) {
|
|
1519
|
-
return `the daemon's current record of this launch's child is ${reason}`;
|
|
1520
|
-
}
|
|
1521
|
-
async function observeRadarChild(client, provenance) {
|
|
1522
|
-
if (provenance.registration_id === void 0)
|
|
1523
|
-
return unknownChild("not bound to a child by this launch");
|
|
1524
|
-
if (provenance.pane_id === void 0)
|
|
1525
|
-
return unknownChild("without a pane this launch recorded for it");
|
|
1526
|
-
const read = await client.spawnGet(provenance.request_id);
|
|
1527
|
-
if (read.ok === false)
|
|
1528
|
-
return unknownChild(
|
|
1529
|
-
`not readable (${read.code}: ${read.message})`
|
|
1530
|
-
);
|
|
1531
|
-
const topology = read.value;
|
|
1532
|
-
const edge = topology.edge;
|
|
1533
|
-
if (edge.bound === void 0 || edge.bound.registration_id === void 0)
|
|
1534
|
-
return unknownChild("not bound to a child by the daemon");
|
|
1535
|
-
if (edge.bound.registration_id !== provenance.registration_id)
|
|
1536
|
-
return unknownChild(
|
|
1537
|
-
`bound to another subject (${edge.bound.registration_id}), not to ${provenance.registration_id}`
|
|
1538
|
-
);
|
|
1539
|
-
if (edge.location?.pane === void 0)
|
|
1540
|
-
return unknownChild("recorded without a pane");
|
|
1541
|
-
if (edge.location.pane !== provenance.pane_id)
|
|
1542
|
-
return unknownChild(
|
|
1543
|
-
`recorded on pane ${edge.location.pane}, not on ${provenance.pane_id}`
|
|
1544
|
-
);
|
|
1545
|
-
if (edge.exit_observation?.state !== "unknown")
|
|
1546
|
-
return unknownChild("recorded with an observed process exit");
|
|
1547
|
-
if (topology.freshness !== "fresh")
|
|
1548
|
-
return unknownChild(
|
|
1549
|
-
"on a pane the daemon's own inventory does not currently report"
|
|
1550
|
-
);
|
|
1551
|
-
if (topology.state !== "bound")
|
|
1552
|
-
return unknownChild(
|
|
1553
|
-
`not currently placed (state ${topology.state ?? "unreported"})`
|
|
1554
|
-
);
|
|
1555
|
-
const reply = await client.agentGet(edge.bound.registration_id);
|
|
1556
|
-
if (reply.ok === false)
|
|
1557
|
-
return unknownChild(
|
|
1558
|
-
`process verdict not readable (${reply.code}: ${reply.message})`
|
|
1559
|
-
);
|
|
1560
|
-
const { verification, location, contextSession } = reply.value;
|
|
1561
|
-
if (verification === "absent" || verification === "mismatched")
|
|
1562
|
-
return {
|
|
1563
|
-
kind: "gone",
|
|
1564
|
-
verification,
|
|
1565
|
-
diagnostic: trajectory(`the registered process is ${verification}`)
|
|
1566
|
-
};
|
|
1567
|
-
if (verification !== "verified")
|
|
1568
|
-
return unknownChild(`process verdict is ${verification}`);
|
|
1569
|
-
if (location?.pane !== provenance.pane_id)
|
|
1570
|
-
return unknownChild(
|
|
1571
|
-
`registered on ${location?.pane ?? "no pane"}, not on ${provenance.pane_id}`
|
|
1572
|
-
);
|
|
1573
|
-
if (provenance.session_id === void 0)
|
|
1574
|
-
return unknownChild("without a session this launch recorded for it");
|
|
1575
|
-
if (contextSession !== provenance.session_id)
|
|
1576
|
-
return unknownChild(
|
|
1577
|
-
`publishing session ${contextSession ?? "none"}, not ${provenance.session_id}`
|
|
1578
|
-
);
|
|
1579
|
-
return {
|
|
1580
|
-
kind: "live",
|
|
1581
|
-
evidence: {
|
|
1582
|
-
request_id: provenance.request_id,
|
|
1583
|
-
registration_id: edge.bound.registration_id,
|
|
1584
|
-
source: edge.bound.source,
|
|
1585
|
-
incarnation: edge.bound.incarnation,
|
|
1586
|
-
pane_id: edge.location.pane
|
|
1587
|
-
}
|
|
1588
|
-
};
|
|
1589
|
-
}
|
|
1590
|
-
async function readProcessGone(client, agentId) {
|
|
1591
|
-
const read = await client.agentGet(agentId);
|
|
1592
|
-
if (read.ok === false) return void 0;
|
|
1593
|
-
const { verification } = read.value;
|
|
1594
|
-
return verification === "absent" || verification === "mismatched" ? verification : void 0;
|
|
1595
|
-
}
|
|
1596
|
-
async function relaunchRefusalForChild(client, agentId) {
|
|
1597
|
-
const read = await client.agentGet(agentId);
|
|
1598
|
-
if (read.ok === false)
|
|
1599
|
-
return `the child's process cannot be read (${read.code}: ${read.message})`;
|
|
1600
|
-
const { verification } = read.value;
|
|
1601
|
-
if (verification === "absent" || verification === "mismatched") return void 0;
|
|
1602
|
-
return `the child's registered process is ${verification === "verified" ? "still running" : "not proved gone"}`;
|
|
1603
|
-
}
|
|
1604
1439
|
function refusedLaunch(message) {
|
|
1605
|
-
return { kind: "
|
|
1440
|
+
return { kind: "failed", message };
|
|
1606
1441
|
}
|
|
1607
1442
|
function unresolvedLaunch(requestId, message) {
|
|
1608
1443
|
return {
|
|
@@ -1611,14 +1446,6 @@ function unresolvedLaunch(requestId, message) {
|
|
|
1611
1446
|
message: `spawn ${requestId} is unresolved (${message})`
|
|
1612
1447
|
};
|
|
1613
1448
|
}
|
|
1614
|
-
function edgeStanding(edge) {
|
|
1615
|
-
if (edge.created === "refused" || edge.launched === "refused")
|
|
1616
|
-
return "refused";
|
|
1617
|
-
return edge.created === "completed" && edge.launched === "completed" ? "launched" : "unknown";
|
|
1618
|
-
}
|
|
1619
|
-
function edgeMessage(edge) {
|
|
1620
|
-
return `the daemon's spawn record is created ${edge.created} and launched ${edge.launched}`;
|
|
1621
|
-
}
|
|
1622
1449
|
var PRE_EFFECT_CODES = [
|
|
1623
1450
|
"absent",
|
|
1624
1451
|
"unsupported",
|
|
@@ -1631,14 +1458,12 @@ var PRE_EFFECT_CODES = [
|
|
|
1631
1458
|
"busy",
|
|
1632
1459
|
"backend_unavailable"
|
|
1633
1460
|
];
|
|
1634
|
-
function
|
|
1461
|
+
function launchFailure(result, requestId) {
|
|
1635
1462
|
return PRE_EFFECT_CODES.includes(result.code) ? refusedLaunch(result.message) : unresolvedLaunch(requestId, result.message);
|
|
1636
1463
|
}
|
|
1637
1464
|
async function launchRadarWorker(options) {
|
|
1638
1465
|
const { client, launch, signal } = options;
|
|
1639
1466
|
const pollMs = options.pollMs ?? 250;
|
|
1640
|
-
const deadline = Date.now() + options.timeoutMs;
|
|
1641
|
-
const remaining = () => Math.max(0, deadline - Date.now());
|
|
1642
1467
|
const parent = await options.publication.subject();
|
|
1643
1468
|
if (parent === void 0 || !isUuid(parent.registration_id))
|
|
1644
1469
|
return refusedLaunch(
|
|
@@ -1669,55 +1494,22 @@ async function launchRadarWorker(options) {
|
|
|
1669
1494
|
...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
|
|
1670
1495
|
});
|
|
1671
1496
|
if (spawned.ok === false) {
|
|
1672
|
-
const refusal =
|
|
1673
|
-
if (refusal.kind === "
|
|
1674
|
-
}
|
|
1675
|
-
if (spawned.ok === true) {
|
|
1676
|
-
const standing = edgeStanding(spawned.value);
|
|
1677
|
-
if (standing === "refused")
|
|
1678
|
-
return refusedLaunch(edgeMessage(spawned.value));
|
|
1679
|
-
if (standing === "unknown")
|
|
1680
|
-
return unresolvedLaunch(launch.request_id, edgeMessage(spawned.value));
|
|
1497
|
+
const refusal = launchFailure(spawned, launch.request_id);
|
|
1498
|
+
if (refusal.kind === "failed") return refusal;
|
|
1681
1499
|
}
|
|
1682
1500
|
const bound = await waitForChild(client, launch.request_id, {
|
|
1683
|
-
remaining,
|
|
1684
1501
|
pollMs,
|
|
1685
1502
|
...signal !== void 0 ? { signal } : {}
|
|
1686
1503
|
});
|
|
1687
1504
|
if (bound.kind !== "bound") return bound;
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
launch.
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
workspaceId: launch.identity.workspaceId,
|
|
1697
|
-
agentLabel: launch.identity.agentLabel,
|
|
1698
|
-
// The child writes the raw TMUX_PANE of the pane it runs in. Requiring
|
|
1699
|
-
// the pane the daemon created is what refuses a child that inherited
|
|
1700
|
-
// another process's coordinates.
|
|
1701
|
-
paneId: radarPaneHandle(bound.pane_id)
|
|
1702
|
-
}),
|
|
1703
|
-
{ timeoutMs: remaining(), ...signal !== void 0 ? { signal } : {} }
|
|
1704
|
-
);
|
|
1705
|
-
return {
|
|
1706
|
-
kind: "ready",
|
|
1707
|
-
value: {
|
|
1708
|
-
request_id: launch.request_id,
|
|
1709
|
-
subject_id: bound.subject_id,
|
|
1710
|
-
pane_id: bound.pane_id,
|
|
1711
|
-
state
|
|
1712
|
-
}
|
|
1713
|
-
};
|
|
1714
|
-
} catch (error) {
|
|
1715
|
-
const claimed = options.readers.readState(launch.mailbox)?.paneId;
|
|
1716
|
-
return unresolvedLaunch(
|
|
1717
|
-
launch.request_id,
|
|
1718
|
-
claimed !== void 0 && claimed !== radarPaneHandle(bound.pane_id) ? `the bound child ${agentId} claims the pane ${claimed}, not the pane ${bound.pane_id} this launch created for it` : `the bound child ${agentId} has not acknowledged this launch's startup (${String(error)})`
|
|
1719
|
-
);
|
|
1720
|
-
}
|
|
1505
|
+
return {
|
|
1506
|
+
kind: "ready",
|
|
1507
|
+
value: {
|
|
1508
|
+
request_id: launch.request_id,
|
|
1509
|
+
subject_id: bound.subject_id,
|
|
1510
|
+
pane_id: bound.pane_id
|
|
1511
|
+
}
|
|
1512
|
+
};
|
|
1721
1513
|
}
|
|
1722
1514
|
async function waitForChild(client, requestId, options) {
|
|
1723
1515
|
let last = "the daemon has not answered";
|
|
@@ -1726,37 +1518,21 @@ async function waitForChild(client, requestId, options) {
|
|
|
1726
1518
|
return unresolvedLaunch(requestId, `the launch was cancelled (${last})`);
|
|
1727
1519
|
const answer = await client.spawnGet(requestId);
|
|
1728
1520
|
if (answer.ok === true) {
|
|
1729
|
-
const { edge
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1521
|
+
const { edge } = answer.value;
|
|
1522
|
+
if (edge.withdrawn !== void 0)
|
|
1523
|
+
return refusedLaunch(`the daemon withdrew this launch: ${edge.withdrawn.reason}`);
|
|
1524
|
+
if (edge.granted_at === void 0) {
|
|
1525
|
+
await delay(options.pollMs);
|
|
1526
|
+
continue;
|
|
1527
|
+
}
|
|
1735
1528
|
const pane = edge.location?.pane;
|
|
1736
1529
|
if (pane === void 0 || pane === "")
|
|
1737
1530
|
return unresolvedLaunch(
|
|
1738
1531
|
requestId,
|
|
1739
1532
|
"the daemon's launch recorded no pane to verify the child against"
|
|
1740
1533
|
);
|
|
1741
|
-
if (freshness !== "fresh")
|
|
1742
|
-
return unresolvedLaunch(
|
|
1743
|
-
requestId,
|
|
1744
|
-
state === "unresolved" ? `the daemon's inventory does not report the pane ${pane} its own record names for this child` : `the daemon cannot confirm the pane ${pane} it recorded for this child`
|
|
1745
|
-
);
|
|
1746
|
-
if (edge.exit_observation?.state === "exited")
|
|
1747
|
-
return unresolvedLaunch(
|
|
1748
|
-
requestId,
|
|
1749
|
-
"the daemon observed the launched child's process exit"
|
|
1750
|
-
);
|
|
1751
1534
|
const agentId = edge.bound?.registration_id;
|
|
1752
1535
|
if (agentId !== void 0 && isUuid(agentId)) {
|
|
1753
|
-
const source = edge.bound?.source;
|
|
1754
|
-
const incarnation = edge.bound?.incarnation;
|
|
1755
|
-
if (source === void 0 || source === "" || incarnation === void 0 || incarnation === "")
|
|
1756
|
-
return unresolvedLaunch(
|
|
1757
|
-
requestId,
|
|
1758
|
-
`the daemon bound the child ${agentId} without recording the process it registered as`
|
|
1759
|
-
);
|
|
1760
1536
|
const subjectId = edge.bound?.subject_id;
|
|
1761
1537
|
if (subjectId === void 0 || !isUuid(subjectId))
|
|
1762
1538
|
return unresolvedLaunch(
|
|
@@ -1766,136 +1542,60 @@ async function waitForChild(client, requestId, options) {
|
|
|
1766
1542
|
return {
|
|
1767
1543
|
kind: "bound",
|
|
1768
1544
|
subject_id: subjectId,
|
|
1769
|
-
|
|
1770
|
-
pane_id: pane,
|
|
1771
|
-
source,
|
|
1772
|
-
incarnation
|
|
1545
|
+
pane_id: pane
|
|
1773
1546
|
};
|
|
1774
1547
|
}
|
|
1775
1548
|
last = "no child has registered with this launch's token yet";
|
|
1776
1549
|
} else {
|
|
1777
|
-
|
|
1550
|
+
return unresolvedLaunch(requestId, answer.message);
|
|
1778
1551
|
}
|
|
1779
|
-
|
|
1780
|
-
return unresolvedLaunch(requestId, last);
|
|
1781
|
-
await delay(options.pollMs, void 0, {
|
|
1782
|
-
...options.signal !== void 0 ? { signal: options.signal } : {}
|
|
1783
|
-
});
|
|
1552
|
+
await delay(options.pollMs);
|
|
1784
1553
|
}
|
|
1785
1554
|
}
|
|
1786
1555
|
|
|
1787
1556
|
// extension/radar-children.ts
|
|
1788
|
-
var PAGE = 100;
|
|
1789
|
-
var MAX_PAGES = 20;
|
|
1790
1557
|
function createRadarChildren(client, publication) {
|
|
1791
|
-
const held = /* @__PURE__ */ new Map();
|
|
1792
|
-
async function resolveAll() {
|
|
1793
|
-
const newest = /* @__PURE__ */ new Map();
|
|
1794
|
-
let after;
|
|
1795
|
-
for (let page = 0; page < MAX_PAGES; page++) {
|
|
1796
|
-
const read = await client.spawnList({ ...after !== void 0 ? { after } : {}, limit: PAGE });
|
|
1797
|
-
if (read.ok === false) return read;
|
|
1798
|
-
for (const edge of read.value.spawns) {
|
|
1799
|
-
const bound = edge.bound;
|
|
1800
|
-
if (!bound?.subject_id || !bound.registration_id || edge.launched !== "completed") continue;
|
|
1801
|
-
const at = Date.parse(bound.bound_at);
|
|
1802
|
-
if (Number.isNaN(at)) continue;
|
|
1803
|
-
const current = newest.get(bound.subject_id);
|
|
1804
|
-
if (current !== void 0 && current.at >= at) continue;
|
|
1805
|
-
newest.set(bound.subject_id, {
|
|
1806
|
-
at,
|
|
1807
|
-
registration_id: bound.registration_id,
|
|
1808
|
-
child: {
|
|
1809
|
-
subject_id: bound.subject_id,
|
|
1810
|
-
request_id: edge.request_id,
|
|
1811
|
-
...edge.location?.pane ? { pane_id: edge.location.pane } : {},
|
|
1812
|
-
state: edge.state,
|
|
1813
|
-
exited: edge.exit_observation != null && edge.exit_observation.state !== "unknown"
|
|
1814
|
-
}
|
|
1815
|
-
});
|
|
1816
|
-
}
|
|
1817
|
-
if (!read.value.next) return { ok: true, value: newest };
|
|
1818
|
-
after = read.value.next;
|
|
1819
|
-
}
|
|
1820
|
-
return { ok: false, code: "protocol", message: "the caller's launches exceed the readable pages" };
|
|
1821
|
-
}
|
|
1822
|
-
async function resolve2(subject) {
|
|
1823
|
-
const all = await resolveAll();
|
|
1824
|
-
return all.ok ? all.value.get(subject) : void 0;
|
|
1825
|
-
}
|
|
1826
|
-
async function resolveAndFollow(subject) {
|
|
1827
|
-
const all = await resolveAll();
|
|
1828
|
-
if (!all.ok) return void 0;
|
|
1829
|
-
follow(all.value);
|
|
1830
|
-
return all.value.get(subject);
|
|
1831
|
-
}
|
|
1832
|
-
function follow(all) {
|
|
1833
|
-
for (const [subject, entry] of held) {
|
|
1834
|
-
const target = all.get(subject);
|
|
1835
|
-
if (target !== void 0 && entry.published !== target.registration_id)
|
|
1836
|
-
entry.publish?.(target);
|
|
1837
|
-
}
|
|
1838
|
-
}
|
|
1839
1558
|
return {
|
|
1840
|
-
async list() {
|
|
1841
|
-
const
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1559
|
+
async list(selection) {
|
|
1560
|
+
const children = [];
|
|
1561
|
+
let after;
|
|
1562
|
+
do {
|
|
1563
|
+
const page = await client.subjectChildren({ selection, after, limit: 100 });
|
|
1564
|
+
if (!page.ok) return page;
|
|
1565
|
+
children.push(...page.value.subjects);
|
|
1566
|
+
if (page.value.next === after)
|
|
1567
|
+
return { ok: false, code: "protocol", message: "child page did not advance" };
|
|
1568
|
+
after = page.value.next ?? void 0;
|
|
1569
|
+
} while (after !== void 0);
|
|
1570
|
+
return { ok: true, value: children };
|
|
1845
1571
|
},
|
|
1846
|
-
|
|
1847
|
-
const
|
|
1848
|
-
if (
|
|
1849
|
-
|
|
1850
|
-
const
|
|
1851
|
-
const
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
publication.assignment(target.registration_id, owner).update(latest);
|
|
1857
|
-
};
|
|
1858
|
-
entry.publish = send;
|
|
1859
|
-
entry.writer = {
|
|
1860
|
-
update(snapshot) {
|
|
1861
|
-
latest = snapshot;
|
|
1862
|
-
void resolve2(subject).then((target) => {
|
|
1863
|
-
if (target !== void 0) send(target);
|
|
1864
|
-
});
|
|
1865
|
-
},
|
|
1866
|
-
retire() {
|
|
1867
|
-
latest = void 0;
|
|
1868
|
-
if (entry.published !== void 0) publication.retireAssignment(entry.published);
|
|
1869
|
-
entry.published = void 0;
|
|
1870
|
-
}
|
|
1871
|
-
};
|
|
1872
|
-
held.set(subject, entry);
|
|
1873
|
-
return entry.writer;
|
|
1874
|
-
},
|
|
1875
|
-
retire(subject) {
|
|
1876
|
-
held.get(subject)?.writer.retire();
|
|
1877
|
-
held.delete(subject);
|
|
1878
|
-
},
|
|
1879
|
-
async observe(subject, session_id) {
|
|
1880
|
-
const target = await resolveAndFollow(subject);
|
|
1881
|
-
if (target === void 0)
|
|
1882
|
-
return { kind: "unknown", diagnostic: "the daemon records no bound launch for this child" };
|
|
1883
|
-
return observeRadarChild(client, {
|
|
1884
|
-
request_id: target.child.request_id,
|
|
1885
|
-
pane_id: target.child.pane_id,
|
|
1886
|
-
registration_id: target.registration_id,
|
|
1887
|
-
session_id
|
|
1572
|
+
async restart(subject, program, options) {
|
|
1573
|
+
const parent = await publication.subject();
|
|
1574
|
+
if (parent?.subject_id === void 0)
|
|
1575
|
+
return { kind: "failed", message: "this process has no granted subject" };
|
|
1576
|
+
const request_id = randomUUID2();
|
|
1577
|
+
const requested = await client.restart({ request_id, parent: parent.registration_id, subject_id: subject, ...program });
|
|
1578
|
+
if (!requested.ok) return launchFailure(requested, request_id);
|
|
1579
|
+
const bound = await waitForChild(client, requested.value.launch_request_id, {
|
|
1580
|
+
pollMs: options?.pollMs ?? 250,
|
|
1581
|
+
...options?.signal !== void 0 ? { signal: options.signal } : {}
|
|
1888
1582
|
});
|
|
1583
|
+
if (bound.kind !== "bound") return bound;
|
|
1584
|
+
return { kind: "ready", value: { request_id: requested.value.launch_request_id, subject_id: bound.subject_id, pane_id: bound.pane_id } };
|
|
1889
1585
|
},
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
return
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1586
|
+
assignment: (subject, owner) => publication.assignment(subject, owner),
|
|
1587
|
+
retire: (subject) => publication.retireAssignment(subject),
|
|
1588
|
+
async observe(subject) {
|
|
1589
|
+
const read = await client.childGet(subject);
|
|
1590
|
+
if (!read.ok) return { kind: "unknown", diagnostic: `${read.code}: ${read.message}` };
|
|
1591
|
+
if (read.value.state === "live") return { kind: "live", subject: read.value };
|
|
1592
|
+
if (read.value.state === "detached") return { kind: "gone", subject: read.value };
|
|
1593
|
+
return { kind: "unknown", diagnostic: `child attachment is ${read.value.state}` };
|
|
1594
|
+
},
|
|
1595
|
+
async inspect(subject) {
|
|
1596
|
+
const read = await client.childInspect(subject);
|
|
1597
|
+
if (!read.ok) throw new Error(`Child inspection unavailable (${read.code}: ${read.message})`);
|
|
1598
|
+
return read.value;
|
|
1899
1599
|
}
|
|
1900
1600
|
};
|
|
1901
1601
|
}
|
|
@@ -1964,7 +1664,7 @@ __export(radar_publication_exports, {
|
|
|
1964
1664
|
radarProcessSlot: () => radarProcessSlot,
|
|
1965
1665
|
readProcessBirth: () => readProcessBirth
|
|
1966
1666
|
});
|
|
1967
|
-
import { createHash, randomUUID as
|
|
1667
|
+
import { createHash, randomUUID as randomUUID3 } from "node:crypto";
|
|
1968
1668
|
import {
|
|
1969
1669
|
closeSync,
|
|
1970
1670
|
constants,
|
|
@@ -2094,7 +1794,7 @@ function createPublicationStore(root) {
|
|
|
2094
1794
|
ensureDirectory(directory);
|
|
2095
1795
|
const temporary = join2(
|
|
2096
1796
|
directory,
|
|
2097
|
-
`.${name.slice(name.lastIndexOf("/") + 1)}.${
|
|
1797
|
+
`.${name.slice(name.lastIndexOf("/") + 1)}.${randomUUID3()}.tmp`
|
|
2098
1798
|
);
|
|
2099
1799
|
const bytes = Buffer.from(JSON.stringify(value), "utf8");
|
|
2100
1800
|
if (bytes.length > MAX_RECORD_BYTES)
|
|
@@ -2150,7 +1850,7 @@ function radarProcessSlot() {
|
|
|
2150
1850
|
return host[PROCESS_SLOT];
|
|
2151
1851
|
}
|
|
2152
1852
|
function radarProcessIncarnation(slot = radarProcessSlot()) {
|
|
2153
|
-
return slot.incarnation ??=
|
|
1853
|
+
return slot.incarnation ??= randomUUID3();
|
|
2154
1854
|
}
|
|
2155
1855
|
function radarBindingKey(run, owner, label) {
|
|
2156
1856
|
return createHash("sha256").update(`${run}\0${owner}\0${label}`).digest("hex").slice(0, 32);
|
|
@@ -2310,15 +2010,18 @@ function createChannelWriter(input) {
|
|
|
2310
2010
|
function blank(agentId) {
|
|
2311
2011
|
return {
|
|
2312
2012
|
version: RADAR_PUBLICATION_VERSION,
|
|
2313
|
-
|
|
2013
|
+
target_id: agentId,
|
|
2314
2014
|
channel,
|
|
2315
2015
|
publisher,
|
|
2316
2016
|
sequence: 0
|
|
2317
2017
|
};
|
|
2318
2018
|
}
|
|
2019
|
+
function addressed(target) {
|
|
2020
|
+
return channel === "assignment" ? { subject_id: target } : { registration_id: target };
|
|
2021
|
+
}
|
|
2319
2022
|
function loadRecord() {
|
|
2320
2023
|
const value = store.read(recordName);
|
|
2321
|
-
const agentId = textAt(value, "
|
|
2024
|
+
const agentId = textAt(value, "target_id");
|
|
2322
2025
|
const storedPublisher = recordAt2(value, "publisher");
|
|
2323
2026
|
if (numberAt2(value, "version") !== RADAR_PUBLICATION_VERSION || agentId === void 0 || !isUuid(agentId) || textAt(value, "channel") !== channel || textAt(storedPublisher, "source") !== publisher.source || textAt(storedPublisher, "incarnation") !== publisher.incarnation || textAt(storedPublisher, "reporting_owner") !== publisher.reporting_owner)
|
|
2324
2027
|
return blank("");
|
|
@@ -2333,7 +2036,7 @@ function createChannelWriter(input) {
|
|
|
2333
2036
|
const sentAt = numberAt2(acceptedValue, "sent_at");
|
|
2334
2037
|
return {
|
|
2335
2038
|
version: RADAR_PUBLICATION_VERSION,
|
|
2336
|
-
|
|
2039
|
+
target_id: agentId,
|
|
2337
2040
|
channel,
|
|
2338
2041
|
publisher,
|
|
2339
2042
|
...handle !== void 0 && generation !== void 0 ? { writer: { handle, generation } } : {},
|
|
@@ -2374,15 +2077,11 @@ function createChannelWriter(input) {
|
|
|
2374
2077
|
if (record.writer !== void 0) return true;
|
|
2375
2078
|
const agentId = await input.subject(recovering);
|
|
2376
2079
|
if (agentId === void 0) return false;
|
|
2377
|
-
if (record.
|
|
2080
|
+
if (record.target_id !== agentId) {
|
|
2378
2081
|
record = blank(agentId);
|
|
2379
2082
|
save();
|
|
2380
2083
|
}
|
|
2381
|
-
const answer = await client.acquire({
|
|
2382
|
-
registration_id: agentId,
|
|
2383
|
-
channel,
|
|
2384
|
-
publisher
|
|
2385
|
-
});
|
|
2084
|
+
const answer = await client.acquire({ ...addressed(agentId), channel, publisher });
|
|
2386
2085
|
if (answer.ok === false) {
|
|
2387
2086
|
if (answer.code === "not_found") {
|
|
2388
2087
|
recovering = true;
|
|
@@ -2409,7 +2108,7 @@ function createChannelWriter(input) {
|
|
|
2409
2108
|
const writer = record.writer;
|
|
2410
2109
|
if (writer === void 0) return;
|
|
2411
2110
|
const answer = await client.publish({
|
|
2412
|
-
|
|
2111
|
+
...addressed(record.target_id),
|
|
2413
2112
|
channel,
|
|
2414
2113
|
writer_handle: writer.handle,
|
|
2415
2114
|
sequence: pending.sequence,
|
|
@@ -2503,9 +2202,9 @@ function createChannelWriter(input) {
|
|
|
2503
2202
|
stopPump();
|
|
2504
2203
|
const writer = record.writer;
|
|
2505
2204
|
if (writer === void 0) return;
|
|
2506
|
-
const agentId = record.
|
|
2205
|
+
const agentId = record.target_id;
|
|
2507
2206
|
try {
|
|
2508
|
-
void client.retire({
|
|
2207
|
+
void client.retire({ ...addressed(agentId), channel, writer_handle: writer.handle }).then((answer) => {
|
|
2509
2208
|
if (answer.ok === true) {
|
|
2510
2209
|
try {
|
|
2511
2210
|
record.retired_at = new Date(scheduler.now()).toISOString();
|
|
@@ -2595,6 +2294,7 @@ function createPresenceReporter(input) {
|
|
|
2595
2294
|
}
|
|
2596
2295
|
claim.registration_id = answer.value.registration_id;
|
|
2597
2296
|
claim.subject_id = answer.value.standing === "granted" ? answer.value.subject_id ?? void 0 : void 0;
|
|
2297
|
+
if (claim.subject_id !== void 0) input.granted?.(claim.subject_id);
|
|
2598
2298
|
standing = answer.value.standing === "granted" ? { state: "managed", subject_id: claim.subject_id } : answer.value.standing === "duplicate" ? { state: "duplicate" } : answer.value.standing === "pending" ? { state: "pending" } : { state: "unmanaged" };
|
|
2599
2299
|
diagnostic = answer.value.standing === "duplicate" ? DUPLICATE_SESSION_DIAGNOSTIC : void 0;
|
|
2600
2300
|
save();
|
|
@@ -2618,7 +2318,7 @@ function createPresenceReporter(input) {
|
|
|
2618
2318
|
releases.push({ registration_id: state.registration_id, claim_id: state.claim_id });
|
|
2619
2319
|
}
|
|
2620
2320
|
standing = void 0;
|
|
2621
|
-
state = { version: 2, session, claim_id:
|
|
2321
|
+
state = { version: 2, session, claim_id: randomUUID3(), releases };
|
|
2622
2322
|
save();
|
|
2623
2323
|
stopped = false;
|
|
2624
2324
|
void ensure();
|
|
@@ -2655,7 +2355,7 @@ var STORE_IDENTITY = /* @__PURE__ */ new WeakMap();
|
|
|
2655
2355
|
function storeIdentity(store) {
|
|
2656
2356
|
const known = STORE_IDENTITY.get(store);
|
|
2657
2357
|
if (known !== void 0) return known;
|
|
2658
|
-
const created =
|
|
2358
|
+
const created = randomUUID3();
|
|
2659
2359
|
STORE_IDENTITY.set(store, created);
|
|
2660
2360
|
return created;
|
|
2661
2361
|
}
|
|
@@ -2776,7 +2476,7 @@ function createRadarPublicationInner(options) {
|
|
|
2776
2476
|
agentId = registered;
|
|
2777
2477
|
processRecord = { ...record, registration_id: agentId };
|
|
2778
2478
|
store.write(registrationName, processRecord);
|
|
2779
|
-
if (options.binding !== void 0) writeBinding(options.binding);
|
|
2479
|
+
if (options.binding !== void 0) writeBinding(options.binding, grantedSubject());
|
|
2780
2480
|
return agentId;
|
|
2781
2481
|
})();
|
|
2782
2482
|
try {
|
|
@@ -2785,11 +2485,16 @@ function createRadarPublicationInner(options) {
|
|
|
2785
2485
|
subjectReady = void 0;
|
|
2786
2486
|
}
|
|
2787
2487
|
}
|
|
2788
|
-
function
|
|
2488
|
+
function grantedSubject() {
|
|
2489
|
+
const standing = presence.management();
|
|
2490
|
+
return standing?.state === "managed" ? standing.subject_id : void 0;
|
|
2491
|
+
}
|
|
2492
|
+
function writeBinding(binding, subjectId) {
|
|
2789
2493
|
if (agentId === void 0) return;
|
|
2790
2494
|
store.write(`bindings/${binding.key}.json`, {
|
|
2791
2495
|
version: RADAR_PUBLICATION_VERSION,
|
|
2792
2496
|
registration_id: agentId,
|
|
2497
|
+
...subjectId !== void 0 ? { subject_id: subjectId } : {},
|
|
2793
2498
|
incarnation,
|
|
2794
2499
|
run: binding.run,
|
|
2795
2500
|
owner: binding.owner,
|
|
@@ -2829,6 +2534,9 @@ function createRadarPublicationInner(options) {
|
|
|
2829
2534
|
...launchRegistrationId ? { registration_id: launchRegistrationId } : {}
|
|
2830
2535
|
};
|
|
2831
2536
|
},
|
|
2537
|
+
granted: (subjectId) => {
|
|
2538
|
+
if (options.binding !== void 0) writeBinding(options.binding, subjectId);
|
|
2539
|
+
},
|
|
2832
2540
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
2833
2541
|
});
|
|
2834
2542
|
const facts = createChannelWriter({
|
|
@@ -2860,8 +2568,7 @@ function createRadarPublicationInner(options) {
|
|
|
2860
2568
|
channel: "assignment",
|
|
2861
2569
|
publisher: publisher("herdsman-owner", reportingOwner),
|
|
2862
2570
|
codec: assignmentCodec,
|
|
2863
|
-
//
|
|
2864
|
-
// registration_id from the child's sidecar is the whole correlation.
|
|
2571
|
+
// Radar owns this assignment across the child's process replacements.
|
|
2865
2572
|
subject: async () => target,
|
|
2866
2573
|
leaseMs: options.leaseMs ?? RADAR_LEASE_MS,
|
|
2867
2574
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
@@ -2882,16 +2589,18 @@ function createRadarPublicationInner(options) {
|
|
|
2882
2589
|
return void 0;
|
|
2883
2590
|
}
|
|
2884
2591
|
const bound = textAt(value, "registration_id");
|
|
2592
|
+
const subjectId = textAt(value, "subject_id");
|
|
2885
2593
|
const boundIncarnation = textAt(value, "incarnation");
|
|
2886
2594
|
const run = textAt(value, "run");
|
|
2887
2595
|
const owner = textAt(value, "owner");
|
|
2888
2596
|
const label = textAt(value, "label");
|
|
2889
2597
|
const observedAt = textAt(value, "observed_at");
|
|
2890
|
-
if (!isUuid(bound) || !isUuid(boundIncarnation) || run === void 0 || owner === void 0 || label === void 0 || observedAt === void 0)
|
|
2598
|
+
if (!isUuid(bound) || subjectId !== void 0 && !isUuid(subjectId) || !isUuid(boundIncarnation) || run === void 0 || owner === void 0 || label === void 0 || observedAt === void 0)
|
|
2891
2599
|
return void 0;
|
|
2892
2600
|
return {
|
|
2893
2601
|
version: RADAR_PUBLICATION_VERSION,
|
|
2894
2602
|
registration_id: bound,
|
|
2603
|
+
...subjectId !== void 0 ? { subject_id: subjectId } : {},
|
|
2895
2604
|
incarnation: boundIncarnation,
|
|
2896
2605
|
run,
|
|
2897
2606
|
owner,
|
|
@@ -3182,6 +2891,126 @@ function createRadarExecutionAdapter(pi, options) {
|
|
|
3182
2891
|
};
|
|
3183
2892
|
}
|
|
3184
2893
|
|
|
2894
|
+
// extension/radar-inspection.ts
|
|
2895
|
+
var radar_inspection_exports = {};
|
|
2896
|
+
|
|
2897
|
+
// extension/radar-location.ts
|
|
2898
|
+
var radar_location_exports = {};
|
|
2899
|
+
__export(radar_location_exports, {
|
|
2900
|
+
radarLocalPane: () => radarLocalPane,
|
|
2901
|
+
radarPaneHandle: () => radarPaneHandle,
|
|
2902
|
+
resolveRadarProcessLocation: () => resolveRadarProcessLocation,
|
|
2903
|
+
tmuxServerName: () => tmuxServerName
|
|
2904
|
+
});
|
|
2905
|
+
import { basename } from "node:path";
|
|
2906
|
+
function radarLocalPane(env) {
|
|
2907
|
+
const pane = env.TMUX_PANE?.trim();
|
|
2908
|
+
return pane && /^%\d+$/.test(pane) ? pane : void 0;
|
|
2909
|
+
}
|
|
2910
|
+
function radarPaneHandle(qualified) {
|
|
2911
|
+
return qualified.slice(qualified.lastIndexOf(":") + 1);
|
|
2912
|
+
}
|
|
2913
|
+
function tmuxServerName(env) {
|
|
2914
|
+
const socket = env.TMUX?.trim().split(",")[0];
|
|
2915
|
+
return socket ? basename(socket) || void 0 : void 0;
|
|
2916
|
+
}
|
|
2917
|
+
function qualifiedOwnPane(env, backend, instance) {
|
|
2918
|
+
if (backend !== "tmux")
|
|
2919
|
+
return {
|
|
2920
|
+
absent: `the daemon serves the ${backend} backend, and Herdsman drives only tmux`
|
|
2921
|
+
};
|
|
2922
|
+
if (instance === void 0 || instance === "")
|
|
2923
|
+
return {
|
|
2924
|
+
pending: "the daemon did not name the tmux server it addresses"
|
|
2925
|
+
};
|
|
2926
|
+
const server = tmuxServerName(env);
|
|
2927
|
+
if (server === void 0)
|
|
2928
|
+
return {
|
|
2929
|
+
absent: "this process's own tmux client names no server, so it is not in the pane it would publish"
|
|
2930
|
+
};
|
|
2931
|
+
if (server !== instance)
|
|
2932
|
+
return {
|
|
2933
|
+
absent: `this process is attached to tmux server ${server}, not to ${instance}`
|
|
2934
|
+
};
|
|
2935
|
+
const pane = radarLocalPane(env);
|
|
2936
|
+
if (pane === void 0)
|
|
2937
|
+
return {
|
|
2938
|
+
absent: "this process's own environment names no tmux pane of that server"
|
|
2939
|
+
};
|
|
2940
|
+
return { pane: `tmux:${instance}:${pane}` };
|
|
2941
|
+
}
|
|
2942
|
+
function observedContainment(inventory, paneId) {
|
|
2943
|
+
const rows = inventory.panes.filter((pane) => pane.pane_id === paneId);
|
|
2944
|
+
if (rows.length === 0) return void 0;
|
|
2945
|
+
if (rows.length > 1) return { ambiguous: `topology reports pane ${paneId} twice` };
|
|
2946
|
+
const row = rows[0];
|
|
2947
|
+
const tab = inventory.tabs.find((candidate) => candidate.tab_id === row.tab_id);
|
|
2948
|
+
if (tab !== void 0 && tab.workspace_id !== row.workspace_id)
|
|
2949
|
+
return {
|
|
2950
|
+
ambiguous: `pane ${paneId} is reported in ${row.workspace_id}, but its tab belongs to ${tab.workspace_id}`
|
|
2951
|
+
};
|
|
2952
|
+
return { pane: row };
|
|
2953
|
+
}
|
|
2954
|
+
async function resolveRadarProcessLocation(client, env = process.env) {
|
|
2955
|
+
let ready;
|
|
2956
|
+
try {
|
|
2957
|
+
ready = await client.ping();
|
|
2958
|
+
} catch {
|
|
2959
|
+
return { state: "pending", reason: "the daemon did not answer this process" };
|
|
2960
|
+
}
|
|
2961
|
+
if (ready.ok === false)
|
|
2962
|
+
return {
|
|
2963
|
+
state: "pending",
|
|
2964
|
+
reason: `the daemon did not answer this process (${ready.code})`
|
|
2965
|
+
};
|
|
2966
|
+
const backend = ready.value.backend;
|
|
2967
|
+
if (backend === void 0 || backend === "none")
|
|
2968
|
+
return { state: "absent", reason: "the daemon serves no physical backend" };
|
|
2969
|
+
const instance = ready.value.instance ?? void 0;
|
|
2970
|
+
const own = qualifiedOwnPane(env, backend, instance);
|
|
2971
|
+
if ("absent" in own) return { state: "absent", reason: own.absent };
|
|
2972
|
+
if ("pending" in own) return { state: "pending", reason: own.pending };
|
|
2973
|
+
const paneId = own.pane;
|
|
2974
|
+
if (paneId === env.PI_HERDSMAN_PARENT_PANE_ID?.trim())
|
|
2975
|
+
return {
|
|
2976
|
+
state: "absent",
|
|
2977
|
+
reason: "this process is in the pane its launch was created beside, not in a pane of its own"
|
|
2978
|
+
};
|
|
2979
|
+
let inventory;
|
|
2980
|
+
try {
|
|
2981
|
+
inventory = await client.observe();
|
|
2982
|
+
} catch {
|
|
2983
|
+
return {
|
|
2984
|
+
state: "pending",
|
|
2985
|
+
reason: "the daemon's topology could not be read for this process's pane"
|
|
2986
|
+
};
|
|
2987
|
+
}
|
|
2988
|
+
if (inventory.ok === false)
|
|
2989
|
+
return {
|
|
2990
|
+
state: "pending",
|
|
2991
|
+
reason: `the daemon's topology could not be read (${inventory.code})`
|
|
2992
|
+
};
|
|
2993
|
+
const containment = observedContainment(inventory.value, paneId);
|
|
2994
|
+
if (containment === void 0)
|
|
2995
|
+
return {
|
|
2996
|
+
state: "pending",
|
|
2997
|
+
reason: `the daemon's topology does not report pane ${paneId} yet`
|
|
2998
|
+
};
|
|
2999
|
+
if ("ambiguous" in containment)
|
|
3000
|
+
return { state: "pending", reason: containment.ambiguous };
|
|
3001
|
+
const row = containment.pane;
|
|
3002
|
+
return {
|
|
3003
|
+
state: "resolved",
|
|
3004
|
+
location: {
|
|
3005
|
+
backend,
|
|
3006
|
+
...instance !== void 0 ? { instance } : {},
|
|
3007
|
+
workspace: row.workspace_id,
|
|
3008
|
+
tab: row.tab_id,
|
|
3009
|
+
pane: paneId
|
|
3010
|
+
}
|
|
3011
|
+
};
|
|
3012
|
+
}
|
|
3013
|
+
|
|
3185
3014
|
// extension/radar-runtime.ts
|
|
3186
3015
|
var radar_runtime_exports = {};
|
|
3187
3016
|
__export(radar_runtime_exports, {
|
|
@@ -3192,7 +3021,7 @@ __export(radar_runtime_exports, {
|
|
|
3192
3021
|
requestRadarRuntime: () => requestRadarRuntime
|
|
3193
3022
|
});
|
|
3194
3023
|
var RADAR_RUNTIME_REQUEST = "pi-radar/runtime-request";
|
|
3195
|
-
var RADAR_RUNTIME_VERSION =
|
|
3024
|
+
var RADAR_RUNTIME_VERSION = 5;
|
|
3196
3025
|
function lazyRadarRuntime(start) {
|
|
3197
3026
|
let pending;
|
|
3198
3027
|
return {
|