pi-radar 0.10.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 +6 -10
- package/dist/extension/baseline.js +147 -272
- package/dist/extension/radar-children.js +28 -242
- package/dist/extension/radar-client.js +92 -13
- package/dist/extension/radar-execution.js +26 -17
- package/dist/extension/radar-inspection.js +0 -45
- package/dist/extension/radar-launch.js +0 -92
- package/dist/extension/radar-publication.js +26 -17
- package/dist/extension/radar-runtime.js +1 -1
- package/dist/index.js +150 -295
- package/extension/generated-contracts.ts +352 -11
- package/extension/radar-children.ts +43 -184
- package/extension/radar-client.ts +114 -17
- package/extension/radar-inspection.ts +2 -77
- package/extension/radar-launch.ts +0 -177
- 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 -13
- package/schema/public/v1/observation-get-response.serialize.schema.json +5 -13
- package/schema/public/v1/observation-snapshot.deserialize.schema.json +5 -13
- package/schema/public/v1/observation-snapshot.serialize.schema.json +5 -13
- 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
|
@@ -8,11 +8,10 @@ import { lstatSync } from "node:fs";
|
|
|
8
8
|
import { createConnection } from "node:net";
|
|
9
9
|
import { dirname, join } from "node:path";
|
|
10
10
|
var RADAR_PROTOCOL_VERSION = 2;
|
|
11
|
-
var RADAR_MINIMUM_REVISION =
|
|
11
|
+
var RADAR_MINIMUM_REVISION = 9;
|
|
12
12
|
var RADAR_REGISTRY_CONTRACT = "registry.v1";
|
|
13
13
|
var RADAR_REGISTRY_FACTS_CONTRACT = "registry.facts.v1";
|
|
14
14
|
var RADAR_SPAWN_CONTRACT = "spawn.v1";
|
|
15
|
-
var RADAR_SPAWN_LIST_CONTRACT = "spawn.list.v1";
|
|
16
15
|
var RADAR_SPAWN_CLOSE_CONTRACT = "spawn.close.v1";
|
|
17
16
|
var RADAR_LIFECYCLE_RESTART_CONTRACT = "lifecycle.restart.v1";
|
|
18
17
|
var RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT = "spawn.launch_context.v1";
|
|
@@ -349,6 +348,14 @@ var PROCESS_VERDICTS = [
|
|
|
349
348
|
"mismatched",
|
|
350
349
|
"unavailable"
|
|
351
350
|
];
|
|
351
|
+
function subjectAt(value) {
|
|
352
|
+
const subjectId = recordAt(value, "subject_id");
|
|
353
|
+
const session = recordAt(value, "session_id");
|
|
354
|
+
const state = recordAt(value, "state");
|
|
355
|
+
if (!isUuid(subjectId) || session != null && !isUuid(session) || !["starting", "live", "detached", "unknown"].includes(String(state)))
|
|
356
|
+
return protocol("subject read carries no valid identity or attachment state");
|
|
357
|
+
return { ok: true, value };
|
|
358
|
+
}
|
|
352
359
|
function agentProcessAt(agent) {
|
|
353
360
|
const process2 = recordAt(agent, "process");
|
|
354
361
|
const claimed = recordAt(process2, "claimed");
|
|
@@ -683,7 +690,9 @@ function createRadarClient(options = {}) {
|
|
|
683
690
|
return withRegistry(async () => {
|
|
684
691
|
const answer = await call("agent.presence", { ...request });
|
|
685
692
|
if (!answer.ok) return answer;
|
|
686
|
-
|
|
693
|
+
const decoded = decodePresenceAnswer(answer.value);
|
|
694
|
+
if (decoded.ok) processRegistrationId = decoded.value.registration_id;
|
|
695
|
+
return decoded;
|
|
687
696
|
});
|
|
688
697
|
},
|
|
689
698
|
async releasePresence(request) {
|
|
@@ -718,9 +727,13 @@ function createRadarClient(options = {}) {
|
|
|
718
727
|
},
|
|
719
728
|
async acquire(request) {
|
|
720
729
|
return withRegistryChannel(request.channel, async () => {
|
|
721
|
-
const
|
|
722
|
-
|
|
723
|
-
|
|
730
|
+
const assignment = request.channel === "assignment";
|
|
731
|
+
if (assignment && processRegistrationId === void 0)
|
|
732
|
+
return { ok: false, code: "refused", message: "assignment requires a granted parent" };
|
|
733
|
+
if (assignment && request.subject_id === void 0)
|
|
734
|
+
return { ok: false, code: "bad_params", message: "an assignment names a child subject" };
|
|
735
|
+
const answer = await call(assignment ? "subject.assignment.acquire" : "agent.acquire", {
|
|
736
|
+
...assignment ? { registration_id: processRegistrationId, subject_id: request.subject_id } : { registration_id: request.registration_id, channel: request.channel },
|
|
724
737
|
publisher: request.publisher,
|
|
725
738
|
...request.replace !== void 0 ? { replace: request.replace } : {}
|
|
726
739
|
});
|
|
@@ -747,15 +760,19 @@ function createRadarClient(options = {}) {
|
|
|
747
760
|
}
|
|
748
761
|
}
|
|
749
762
|
return withRegistryChannel(request.channel, async () => {
|
|
750
|
-
const
|
|
751
|
-
registration_id: request.registration_id,
|
|
752
|
-
channel: request.channel,
|
|
763
|
+
const update = {
|
|
753
764
|
writer_handle: request.writer_handle,
|
|
754
765
|
sequence: request.sequence,
|
|
755
766
|
...request.lease_ms !== void 0 ? { lease_ms: request.lease_ms } : {},
|
|
756
767
|
...request.observed_at !== void 0 ? { observed_at: request.observed_at } : {},
|
|
757
768
|
snapshot
|
|
758
|
-
}
|
|
769
|
+
};
|
|
770
|
+
const assignment = request.channel === "assignment";
|
|
771
|
+
if (assignment && processRegistrationId === void 0)
|
|
772
|
+
return { ok: false, code: "refused", message: "assignment requires a granted parent" };
|
|
773
|
+
if (assignment && request.subject_id === void 0)
|
|
774
|
+
return { ok: false, code: "bad_params", message: "an assignment names a child subject" };
|
|
775
|
+
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 });
|
|
759
776
|
if (answer.ok === false) return answer;
|
|
760
777
|
const channel = answer.value.channel;
|
|
761
778
|
const writer = channel?.writer;
|
|
@@ -796,7 +813,15 @@ function createRadarClient(options = {}) {
|
|
|
796
813
|
},
|
|
797
814
|
async retire(request) {
|
|
798
815
|
return withRegistryChannel(request.channel, async () => {
|
|
799
|
-
const
|
|
816
|
+
const assignment = request.channel === "assignment";
|
|
817
|
+
if (assignment && processRegistrationId === void 0)
|
|
818
|
+
return { ok: false, code: "refused", message: "assignment requires a granted parent" };
|
|
819
|
+
if (assignment && request.subject_id === void 0)
|
|
820
|
+
return { ok: false, code: "bad_params", message: "an assignment names a child subject" };
|
|
821
|
+
const answer = await call(assignment ? "subject.assignment.retire" : "agent.retire", {
|
|
822
|
+
...assignment ? { registration_id: processRegistrationId, subject_id: request.subject_id } : { registration_id: request.registration_id, channel: request.channel },
|
|
823
|
+
writer_handle: request.writer_handle
|
|
824
|
+
});
|
|
800
825
|
return answer.ok === true ? { ok: true, value: void 0 } : answer;
|
|
801
826
|
});
|
|
802
827
|
},
|
|
@@ -878,11 +903,66 @@ function createRadarClient(options = {}) {
|
|
|
878
903
|
return { ok: true, value: { ...settled.value, launch_request_id: launch } };
|
|
879
904
|
});
|
|
880
905
|
},
|
|
906
|
+
async subjectChildren(request = {}) {
|
|
907
|
+
const registrationId = processRegistrationId;
|
|
908
|
+
if (registrationId === void 0)
|
|
909
|
+
return { ok: false, code: "refused", message: "this process has no Radar registration" };
|
|
910
|
+
return withRegistry(async () => {
|
|
911
|
+
const params = { ...request, registration_id: registrationId };
|
|
912
|
+
const answer = await call("subject.children", { ...params });
|
|
913
|
+
if (!answer.ok) return answer;
|
|
914
|
+
const subjects = recordAt(answer.value, "subjects");
|
|
915
|
+
const next = recordAt(answer.value, "next");
|
|
916
|
+
if (!Array.isArray(subjects) || next != null && !isUuid(next))
|
|
917
|
+
return protocol("subject.children reply carries no valid page");
|
|
918
|
+
for (const subject of subjects) {
|
|
919
|
+
const decoded = subjectAt(subject);
|
|
920
|
+
if (!decoded.ok) return decoded;
|
|
921
|
+
}
|
|
922
|
+
return { ok: true, value: answer.value };
|
|
923
|
+
});
|
|
924
|
+
},
|
|
925
|
+
async childGet(subject_id) {
|
|
926
|
+
const registrationId = processRegistrationId;
|
|
927
|
+
if (registrationId === void 0)
|
|
928
|
+
return { ok: false, code: "refused", message: "this process has no Radar registration" };
|
|
929
|
+
if (!isUuid(subject_id))
|
|
930
|
+
return { ok: false, code: "bad_params", message: "child requires a subject UUID" };
|
|
931
|
+
return withRegistry(async () => {
|
|
932
|
+
const params = { registration_id: registrationId, subject_id };
|
|
933
|
+
const answer = await call("subject.child.get", { ...params });
|
|
934
|
+
return answer.ok ? subjectAt(recordAt(answer.value, "subject")) : answer;
|
|
935
|
+
});
|
|
936
|
+
},
|
|
937
|
+
async childInspect(subject_id) {
|
|
938
|
+
const registration_id = processRegistrationId;
|
|
939
|
+
if (registration_id === void 0)
|
|
940
|
+
return { ok: false, code: "refused", message: "this process has no Radar registration" };
|
|
941
|
+
return withRegistry(async () => {
|
|
942
|
+
const answer = await call("subject.child.inspect", { registration_id, subject_id });
|
|
943
|
+
if (!answer.ok) return answer;
|
|
944
|
+
const subject = subjectAt(recordAt(answer.value, "subject"));
|
|
945
|
+
if (!subject.ok) return subject;
|
|
946
|
+
const output = recordAt(answer.value, "output");
|
|
947
|
+
const text = stringAt(output, "text");
|
|
948
|
+
if (text === void 0 || typeof recordAt(output, "truncated") !== "boolean")
|
|
949
|
+
return protocol("child inspection has no output evidence");
|
|
950
|
+
const foreground = recordAt(answer.value, "foreground");
|
|
951
|
+
const activity = subject.value.execution?.snapshot?.snapshot;
|
|
952
|
+
return { ok: true, value: {
|
|
953
|
+
capturedAt: Date.now(),
|
|
954
|
+
recentOutputTruncated: recordAt(output, "truncated"),
|
|
955
|
+
...text ? { recentOutput: text } : {},
|
|
956
|
+
...foreground ? { foreground } : {},
|
|
957
|
+
...activity && "activity" in activity ? { activity: activity.activity } : {}
|
|
958
|
+
} };
|
|
959
|
+
});
|
|
960
|
+
},
|
|
881
961
|
async spawnList(request) {
|
|
882
962
|
const registrationId = processRegistrationId;
|
|
883
963
|
if (registrationId === void 0)
|
|
884
964
|
return { ok: false, code: "refused", message: "spawn.list requires this client's process registration" };
|
|
885
|
-
return
|
|
965
|
+
return withRegistry(async () => {
|
|
886
966
|
const params = {
|
|
887
967
|
registration_id: registrationId,
|
|
888
968
|
...request.after !== void 0 ? { after: request.after } : {},
|
|
@@ -1248,125 +1328,8 @@ function boundedRadarSnapshot(snapshot) {
|
|
|
1248
1328
|
// extension/radar-children.ts
|
|
1249
1329
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1250
1330
|
|
|
1251
|
-
// extension/radar-inspection.ts
|
|
1252
|
-
var INSPECTION_LINES = 80;
|
|
1253
|
-
var INSPECTION_OUTPUT_BYTES = 16 * 1024;
|
|
1254
|
-
async function provedRead(client, target) {
|
|
1255
|
-
const read = await client.agentGet(target.agentId);
|
|
1256
|
-
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;
|
|
1257
|
-
}
|
|
1258
|
-
async function inspectRadarChild(client, target) {
|
|
1259
|
-
const before = await provedRead(client, target);
|
|
1260
|
-
if (before === void 0)
|
|
1261
|
-
throw new Error("Inspection target identity did not match");
|
|
1262
|
-
const output = await client.output({
|
|
1263
|
-
pane_id: target.paneId,
|
|
1264
|
-
source: "recent_unwrapped",
|
|
1265
|
-
lines: INSPECTION_LINES
|
|
1266
|
-
});
|
|
1267
|
-
if (output.ok === false)
|
|
1268
|
-
throw new Error(
|
|
1269
|
-
`Inspection output unavailable (${output.code}: ${output.message})`
|
|
1270
|
-
);
|
|
1271
|
-
const foreground = await client.processInfo({ pane_id: target.paneId });
|
|
1272
|
-
const after = await provedRead(client, target);
|
|
1273
|
-
if (after === void 0)
|
|
1274
|
-
throw new Error("Inspection target changed during capture");
|
|
1275
|
-
const lines = output.value.text.trim().split(/\r?\n/).slice(-INSPECTION_LINES);
|
|
1276
|
-
const bounded2 = utf8Tail(lines.join("\n"), INSPECTION_OUTPUT_BYTES);
|
|
1277
|
-
return Object.freeze({
|
|
1278
|
-
capturedAt: Date.now(),
|
|
1279
|
-
recentOutputTruncated: output.value.truncated || bounded2.truncated,
|
|
1280
|
-
...bounded2.text ? { recentOutput: bounded2.text } : {},
|
|
1281
|
-
...foreground.ok === true ? { foreground: foreground.value } : {},
|
|
1282
|
-
...after.activity !== void 0 ? { activity: after.activity } : {}
|
|
1283
|
-
});
|
|
1284
|
-
}
|
|
1285
|
-
function utf8Tail(value, maxBytes) {
|
|
1286
|
-
const bytes = Buffer.from(value);
|
|
1287
|
-
if (bytes.length <= maxBytes) return { text: value, truncated: false };
|
|
1288
|
-
let start = bytes.length - maxBytes;
|
|
1289
|
-
while (start < bytes.length && (bytes[start] & 192) === 128) start++;
|
|
1290
|
-
return { text: bytes.subarray(start).toString(), truncated: true };
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
1331
|
// extension/radar-launch.ts
|
|
1294
1332
|
import { setTimeout as delay } from "node:timers/promises";
|
|
1295
|
-
function unknownChild(reason) {
|
|
1296
|
-
return { kind: "unknown", diagnostic: trajectory(reason) };
|
|
1297
|
-
}
|
|
1298
|
-
function trajectory(reason) {
|
|
1299
|
-
return `the daemon's current record of this launch's child is ${reason}`;
|
|
1300
|
-
}
|
|
1301
|
-
async function observeRadarChild(client, provenance) {
|
|
1302
|
-
if (provenance.registration_id === void 0)
|
|
1303
|
-
return unknownChild("not bound to a child by this launch");
|
|
1304
|
-
if (provenance.pane_id === void 0)
|
|
1305
|
-
return unknownChild("without a pane this launch recorded for it");
|
|
1306
|
-
const read = await client.spawnGet(provenance.request_id);
|
|
1307
|
-
if (read.ok === false)
|
|
1308
|
-
return unknownChild(
|
|
1309
|
-
`not readable (${read.code}: ${read.message})`
|
|
1310
|
-
);
|
|
1311
|
-
const topology = read.value;
|
|
1312
|
-
const edge = topology.edge;
|
|
1313
|
-
if (edge.bound === void 0 || edge.bound.registration_id === void 0)
|
|
1314
|
-
return unknownChild("not bound to a child by the daemon");
|
|
1315
|
-
if (edge.bound.registration_id !== provenance.registration_id)
|
|
1316
|
-
return unknownChild(
|
|
1317
|
-
`bound to another subject (${edge.bound.registration_id}), not to ${provenance.registration_id}`
|
|
1318
|
-
);
|
|
1319
|
-
if (edge.location?.pane === void 0)
|
|
1320
|
-
return unknownChild("recorded without a pane");
|
|
1321
|
-
if (edge.location.pane !== provenance.pane_id)
|
|
1322
|
-
return unknownChild(
|
|
1323
|
-
`recorded on pane ${edge.location.pane}, not on ${provenance.pane_id}`
|
|
1324
|
-
);
|
|
1325
|
-
if (edge.exit_observation?.state !== "unknown")
|
|
1326
|
-
return unknownChild("recorded with an observed process exit");
|
|
1327
|
-
if (topology.freshness !== "fresh")
|
|
1328
|
-
return unknownChild(
|
|
1329
|
-
"on a pane the daemon's own inventory does not currently report"
|
|
1330
|
-
);
|
|
1331
|
-
if (topology.state !== "bound")
|
|
1332
|
-
return unknownChild(
|
|
1333
|
-
`not currently placed (state ${topology.state ?? "unreported"})`
|
|
1334
|
-
);
|
|
1335
|
-
const reply = await client.agentGet(edge.bound.registration_id);
|
|
1336
|
-
if (reply.ok === false)
|
|
1337
|
-
return unknownChild(
|
|
1338
|
-
`process verdict not readable (${reply.code}: ${reply.message})`
|
|
1339
|
-
);
|
|
1340
|
-
const { verification, location, contextSession } = reply.value;
|
|
1341
|
-
if (verification === "absent" || verification === "mismatched")
|
|
1342
|
-
return {
|
|
1343
|
-
kind: "gone",
|
|
1344
|
-
verification,
|
|
1345
|
-
diagnostic: trajectory(`the registered process is ${verification}`)
|
|
1346
|
-
};
|
|
1347
|
-
if (verification !== "verified")
|
|
1348
|
-
return unknownChild(`process verdict is ${verification}`);
|
|
1349
|
-
if (location?.pane !== provenance.pane_id)
|
|
1350
|
-
return unknownChild(
|
|
1351
|
-
`registered on ${location?.pane ?? "no pane"}, not on ${provenance.pane_id}`
|
|
1352
|
-
);
|
|
1353
|
-
if (provenance.session_id === void 0)
|
|
1354
|
-
return unknownChild("without a session this launch recorded for it");
|
|
1355
|
-
if (contextSession !== provenance.session_id)
|
|
1356
|
-
return unknownChild(
|
|
1357
|
-
`publishing session ${contextSession ?? "none"}, not ${provenance.session_id}`
|
|
1358
|
-
);
|
|
1359
|
-
return {
|
|
1360
|
-
kind: "live",
|
|
1361
|
-
evidence: {
|
|
1362
|
-
request_id: provenance.request_id,
|
|
1363
|
-
registration_id: edge.bound.registration_id,
|
|
1364
|
-
source: edge.bound.source,
|
|
1365
|
-
incarnation: edge.bound.incarnation,
|
|
1366
|
-
pane_id: edge.location.pane
|
|
1367
|
-
}
|
|
1368
|
-
};
|
|
1369
|
-
}
|
|
1370
1333
|
function refusedLaunch(message) {
|
|
1371
1334
|
return { kind: "failed", message };
|
|
1372
1335
|
}
|
|
@@ -1435,145 +1398,48 @@ async function waitForChild(client, requestId, options) {
|
|
|
1435
1398
|
}
|
|
1436
1399
|
|
|
1437
1400
|
// extension/radar-children.ts
|
|
1438
|
-
var PAGE = 100;
|
|
1439
|
-
var MAX_PAGES = 20;
|
|
1440
1401
|
function createRadarChildren(client, publication) {
|
|
1441
|
-
const held = /* @__PURE__ */ new Map();
|
|
1442
|
-
async function resolveAll() {
|
|
1443
|
-
const newest = /* @__PURE__ */ new Map();
|
|
1444
|
-
let after;
|
|
1445
|
-
for (let page = 0; page < MAX_PAGES; page++) {
|
|
1446
|
-
const read = await client.spawnList({ ...after !== void 0 ? { after } : {}, limit: PAGE });
|
|
1447
|
-
if (read.ok === false) return read;
|
|
1448
|
-
for (const edge of read.value.spawns) {
|
|
1449
|
-
const bound = edge.bound;
|
|
1450
|
-
if (!bound?.subject_id || !bound.registration_id || edge.granted_at == null || edge.withdrawn != null) continue;
|
|
1451
|
-
const at = Date.parse(bound.bound_at);
|
|
1452
|
-
if (Number.isNaN(at)) continue;
|
|
1453
|
-
const current = newest.get(bound.subject_id);
|
|
1454
|
-
if (current !== void 0 && current.at >= at) continue;
|
|
1455
|
-
newest.set(bound.subject_id, {
|
|
1456
|
-
at,
|
|
1457
|
-
registration_id: bound.registration_id,
|
|
1458
|
-
child: {
|
|
1459
|
-
subject_id: bound.subject_id,
|
|
1460
|
-
request_id: edge.request_id,
|
|
1461
|
-
...edge.location?.pane ? { pane_id: edge.location.pane } : {},
|
|
1462
|
-
state: edge.state,
|
|
1463
|
-
exited: edge.exit_observation != null && edge.exit_observation.state !== "unknown"
|
|
1464
|
-
}
|
|
1465
|
-
});
|
|
1466
|
-
}
|
|
1467
|
-
if (!read.value.next) return { ok: true, value: newest };
|
|
1468
|
-
after = read.value.next;
|
|
1469
|
-
}
|
|
1470
|
-
return { ok: false, code: "protocol", message: "the caller's launches exceed the readable pages" };
|
|
1471
|
-
}
|
|
1472
|
-
async function resolve2(subject) {
|
|
1473
|
-
const all = await resolveAll();
|
|
1474
|
-
return all.ok ? all.value.get(subject) : void 0;
|
|
1475
|
-
}
|
|
1476
|
-
async function resolveAndFollow(subject) {
|
|
1477
|
-
const all = await resolveAll();
|
|
1478
|
-
if (!all.ok) return void 0;
|
|
1479
|
-
follow(all.value);
|
|
1480
|
-
return all.value.get(subject);
|
|
1481
|
-
}
|
|
1482
|
-
function follow(all) {
|
|
1483
|
-
for (const [subject, entry] of held) {
|
|
1484
|
-
const target = all.get(subject);
|
|
1485
|
-
if (target !== void 0 && entry.published !== target.registration_id)
|
|
1486
|
-
entry.publish?.(target);
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1489
1402
|
return {
|
|
1490
|
-
async list() {
|
|
1491
|
-
const
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1403
|
+
async list(selection) {
|
|
1404
|
+
const children = [];
|
|
1405
|
+
let after;
|
|
1406
|
+
do {
|
|
1407
|
+
const page = await client.subjectChildren({ selection, after, limit: 100 });
|
|
1408
|
+
if (!page.ok) return page;
|
|
1409
|
+
children.push(...page.value.subjects);
|
|
1410
|
+
if (page.value.next === after)
|
|
1411
|
+
return { ok: false, code: "protocol", message: "child page did not advance" };
|
|
1412
|
+
after = page.value.next ?? void 0;
|
|
1413
|
+
} while (after !== void 0);
|
|
1414
|
+
return { ok: true, value: children };
|
|
1495
1415
|
},
|
|
1496
1416
|
async restart(subject, program, options) {
|
|
1497
1417
|
const parent = await publication.subject();
|
|
1498
1418
|
if (parent?.subject_id === void 0)
|
|
1499
|
-
return { kind: "failed", message: "this process has no
|
|
1419
|
+
return { kind: "failed", message: "this process has no granted subject" };
|
|
1500
1420
|
const request_id = randomUUID2();
|
|
1501
|
-
const requested = await client.restart({
|
|
1502
|
-
|
|
1503
|
-
parent: parent.registration_id,
|
|
1504
|
-
subject_id: subject,
|
|
1505
|
-
...program
|
|
1506
|
-
});
|
|
1507
|
-
if (requested.ok === false)
|
|
1508
|
-
return launchFailure(requested, request_id);
|
|
1421
|
+
const requested = await client.restart({ request_id, parent: parent.registration_id, subject_id: subject, ...program });
|
|
1422
|
+
if (!requested.ok) return launchFailure(requested, request_id);
|
|
1509
1423
|
const bound = await waitForChild(client, requested.value.launch_request_id, {
|
|
1510
1424
|
pollMs: options?.pollMs ?? 250,
|
|
1511
1425
|
...options?.signal !== void 0 ? { signal: options.signal } : {}
|
|
1512
1426
|
});
|
|
1513
1427
|
if (bound.kind !== "bound") return bound;
|
|
1514
|
-
|
|
1515
|
-
return {
|
|
1516
|
-
kind: "ready",
|
|
1517
|
-
value: {
|
|
1518
|
-
request_id: requested.value.launch_request_id,
|
|
1519
|
-
subject_id: bound.subject_id,
|
|
1520
|
-
pane_id: bound.pane_id
|
|
1521
|
-
}
|
|
1522
|
-
};
|
|
1523
|
-
},
|
|
1524
|
-
assignment(subject, owner) {
|
|
1525
|
-
const existing = held.get(subject);
|
|
1526
|
-
if (existing !== void 0) return existing.writer;
|
|
1527
|
-
let latest;
|
|
1528
|
-
const entry = { writer: void 0 };
|
|
1529
|
-
const send = (target) => {
|
|
1530
|
-
if (latest === void 0) return;
|
|
1531
|
-
if (entry.published !== void 0 && entry.published !== target.registration_id)
|
|
1532
|
-
publication.retireAssignment(entry.published);
|
|
1533
|
-
entry.published = target.registration_id;
|
|
1534
|
-
publication.assignment(target.registration_id, owner).update(latest);
|
|
1535
|
-
};
|
|
1536
|
-
entry.publish = send;
|
|
1537
|
-
entry.writer = {
|
|
1538
|
-
update(snapshot) {
|
|
1539
|
-
latest = snapshot;
|
|
1540
|
-
void resolve2(subject).then((target) => {
|
|
1541
|
-
if (target !== void 0) send(target);
|
|
1542
|
-
});
|
|
1543
|
-
},
|
|
1544
|
-
retire() {
|
|
1545
|
-
latest = void 0;
|
|
1546
|
-
if (entry.published !== void 0) publication.retireAssignment(entry.published);
|
|
1547
|
-
entry.published = void 0;
|
|
1548
|
-
}
|
|
1549
|
-
};
|
|
1550
|
-
held.set(subject, entry);
|
|
1551
|
-
return entry.writer;
|
|
1428
|
+
return { kind: "ready", value: { request_id: requested.value.launch_request_id, subject_id: bound.subject_id, pane_id: bound.pane_id } };
|
|
1552
1429
|
},
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1430
|
+
assignment: (subject, owner) => publication.assignment(subject, owner),
|
|
1431
|
+
retire: (subject) => publication.retireAssignment(subject),
|
|
1432
|
+
async observe(subject) {
|
|
1433
|
+
const read = await client.childGet(subject);
|
|
1434
|
+
if (!read.ok) return { kind: "unknown", diagnostic: `${read.code}: ${read.message}` };
|
|
1435
|
+
if (read.value.state === "live") return { kind: "live", subject: read.value };
|
|
1436
|
+
if (read.value.state === "detached") return { kind: "gone", subject: read.value };
|
|
1437
|
+
return { kind: "unknown", diagnostic: `child attachment is ${read.value.state}` };
|
|
1556
1438
|
},
|
|
1557
|
-
async
|
|
1558
|
-
const
|
|
1559
|
-
if (
|
|
1560
|
-
|
|
1561
|
-
return observeRadarChild(client, {
|
|
1562
|
-
request_id: target.child.request_id,
|
|
1563
|
-
pane_id: target.child.pane_id,
|
|
1564
|
-
registration_id: target.registration_id,
|
|
1565
|
-
session_id
|
|
1566
|
-
});
|
|
1567
|
-
},
|
|
1568
|
-
async inspect(subject, session_id) {
|
|
1569
|
-
const target = await resolveAndFollow(subject);
|
|
1570
|
-
if (target?.child.pane_id === void 0)
|
|
1571
|
-
throw new Error("Inspection target is not a bound child with a pane");
|
|
1572
|
-
return inspectRadarChild(client, {
|
|
1573
|
-
agentId: target.registration_id,
|
|
1574
|
-
paneId: target.child.pane_id,
|
|
1575
|
-
...session_id !== void 0 ? { session: session_id } : {}
|
|
1576
|
-
});
|
|
1439
|
+
async inspect(subject) {
|
|
1440
|
+
const read = await client.childInspect(subject);
|
|
1441
|
+
if (!read.ok) throw new Error(`Child inspection unavailable (${read.code}: ${read.message})`);
|
|
1442
|
+
return read.value;
|
|
1577
1443
|
}
|
|
1578
1444
|
};
|
|
1579
1445
|
}
|
|
@@ -1959,15 +1825,18 @@ function createChannelWriter(input) {
|
|
|
1959
1825
|
function blank(agentId) {
|
|
1960
1826
|
return {
|
|
1961
1827
|
version: RADAR_PUBLICATION_VERSION,
|
|
1962
|
-
|
|
1828
|
+
target_id: agentId,
|
|
1963
1829
|
channel,
|
|
1964
1830
|
publisher,
|
|
1965
1831
|
sequence: 0
|
|
1966
1832
|
};
|
|
1967
1833
|
}
|
|
1834
|
+
function addressed(target) {
|
|
1835
|
+
return channel === "assignment" ? { subject_id: target } : { registration_id: target };
|
|
1836
|
+
}
|
|
1968
1837
|
function loadRecord() {
|
|
1969
1838
|
const value = store.read(recordName);
|
|
1970
|
-
const agentId = textAt(value, "
|
|
1839
|
+
const agentId = textAt(value, "target_id");
|
|
1971
1840
|
const storedPublisher = recordAt2(value, "publisher");
|
|
1972
1841
|
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)
|
|
1973
1842
|
return blank("");
|
|
@@ -1982,7 +1851,7 @@ function createChannelWriter(input) {
|
|
|
1982
1851
|
const sentAt = numberAt2(acceptedValue, "sent_at");
|
|
1983
1852
|
return {
|
|
1984
1853
|
version: RADAR_PUBLICATION_VERSION,
|
|
1985
|
-
|
|
1854
|
+
target_id: agentId,
|
|
1986
1855
|
channel,
|
|
1987
1856
|
publisher,
|
|
1988
1857
|
...handle !== void 0 && generation !== void 0 ? { writer: { handle, generation } } : {},
|
|
@@ -2023,15 +1892,11 @@ function createChannelWriter(input) {
|
|
|
2023
1892
|
if (record.writer !== void 0) return true;
|
|
2024
1893
|
const agentId = await input.subject(recovering);
|
|
2025
1894
|
if (agentId === void 0) return false;
|
|
2026
|
-
if (record.
|
|
1895
|
+
if (record.target_id !== agentId) {
|
|
2027
1896
|
record = blank(agentId);
|
|
2028
1897
|
save();
|
|
2029
1898
|
}
|
|
2030
|
-
const answer = await client.acquire({
|
|
2031
|
-
registration_id: agentId,
|
|
2032
|
-
channel,
|
|
2033
|
-
publisher
|
|
2034
|
-
});
|
|
1899
|
+
const answer = await client.acquire({ ...addressed(agentId), channel, publisher });
|
|
2035
1900
|
if (answer.ok === false) {
|
|
2036
1901
|
if (answer.code === "not_found") {
|
|
2037
1902
|
recovering = true;
|
|
@@ -2058,7 +1923,7 @@ function createChannelWriter(input) {
|
|
|
2058
1923
|
const writer = record.writer;
|
|
2059
1924
|
if (writer === void 0) return;
|
|
2060
1925
|
const answer = await client.publish({
|
|
2061
|
-
|
|
1926
|
+
...addressed(record.target_id),
|
|
2062
1927
|
channel,
|
|
2063
1928
|
writer_handle: writer.handle,
|
|
2064
1929
|
sequence: pending.sequence,
|
|
@@ -2152,9 +2017,9 @@ function createChannelWriter(input) {
|
|
|
2152
2017
|
stopPump();
|
|
2153
2018
|
const writer = record.writer;
|
|
2154
2019
|
if (writer === void 0) return;
|
|
2155
|
-
const agentId = record.
|
|
2020
|
+
const agentId = record.target_id;
|
|
2156
2021
|
try {
|
|
2157
|
-
void client.retire({
|
|
2022
|
+
void client.retire({ ...addressed(agentId), channel, writer_handle: writer.handle }).then((answer) => {
|
|
2158
2023
|
if (answer.ok === true) {
|
|
2159
2024
|
try {
|
|
2160
2025
|
record.retired_at = new Date(scheduler.now()).toISOString();
|
|
@@ -2244,6 +2109,7 @@ function createPresenceReporter(input) {
|
|
|
2244
2109
|
}
|
|
2245
2110
|
claim.registration_id = answer.value.registration_id;
|
|
2246
2111
|
claim.subject_id = answer.value.standing === "granted" ? answer.value.subject_id ?? void 0 : void 0;
|
|
2112
|
+
if (claim.subject_id !== void 0) input.granted?.(claim.subject_id);
|
|
2247
2113
|
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" };
|
|
2248
2114
|
diagnostic = answer.value.standing === "duplicate" ? DUPLICATE_SESSION_DIAGNOSTIC : void 0;
|
|
2249
2115
|
save();
|
|
@@ -2425,7 +2291,7 @@ function createRadarPublicationInner(options) {
|
|
|
2425
2291
|
agentId = registered;
|
|
2426
2292
|
processRecord = { ...record, registration_id: agentId };
|
|
2427
2293
|
store.write(registrationName, processRecord);
|
|
2428
|
-
if (options.binding !== void 0) writeBinding(options.binding);
|
|
2294
|
+
if (options.binding !== void 0) writeBinding(options.binding, grantedSubject());
|
|
2429
2295
|
return agentId;
|
|
2430
2296
|
})();
|
|
2431
2297
|
try {
|
|
@@ -2434,11 +2300,16 @@ function createRadarPublicationInner(options) {
|
|
|
2434
2300
|
subjectReady = void 0;
|
|
2435
2301
|
}
|
|
2436
2302
|
}
|
|
2437
|
-
function
|
|
2303
|
+
function grantedSubject() {
|
|
2304
|
+
const standing = presence.management();
|
|
2305
|
+
return standing?.state === "managed" ? standing.subject_id : void 0;
|
|
2306
|
+
}
|
|
2307
|
+
function writeBinding(binding, subjectId) {
|
|
2438
2308
|
if (agentId === void 0) return;
|
|
2439
2309
|
store.write(`bindings/${binding.key}.json`, {
|
|
2440
2310
|
version: RADAR_PUBLICATION_VERSION,
|
|
2441
2311
|
registration_id: agentId,
|
|
2312
|
+
...subjectId !== void 0 ? { subject_id: subjectId } : {},
|
|
2442
2313
|
incarnation,
|
|
2443
2314
|
run: binding.run,
|
|
2444
2315
|
owner: binding.owner,
|
|
@@ -2478,6 +2349,9 @@ function createRadarPublicationInner(options) {
|
|
|
2478
2349
|
...launchRegistrationId ? { registration_id: launchRegistrationId } : {}
|
|
2479
2350
|
};
|
|
2480
2351
|
},
|
|
2352
|
+
granted: (subjectId) => {
|
|
2353
|
+
if (options.binding !== void 0) writeBinding(options.binding, subjectId);
|
|
2354
|
+
},
|
|
2481
2355
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
2482
2356
|
});
|
|
2483
2357
|
const facts = createChannelWriter({
|
|
@@ -2509,8 +2383,7 @@ function createRadarPublicationInner(options) {
|
|
|
2509
2383
|
channel: "assignment",
|
|
2510
2384
|
publisher: publisher("herdsman-owner", reportingOwner),
|
|
2511
2385
|
codec: assignmentCodec,
|
|
2512
|
-
//
|
|
2513
|
-
// registration_id from the child's sidecar is the whole correlation.
|
|
2386
|
+
// Radar owns this assignment across the child's process replacements.
|
|
2514
2387
|
subject: async () => target,
|
|
2515
2388
|
leaseMs: options.leaseMs ?? RADAR_LEASE_MS,
|
|
2516
2389
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
@@ -2531,16 +2404,18 @@ function createRadarPublicationInner(options) {
|
|
|
2531
2404
|
return void 0;
|
|
2532
2405
|
}
|
|
2533
2406
|
const bound = textAt(value, "registration_id");
|
|
2407
|
+
const subjectId = textAt(value, "subject_id");
|
|
2534
2408
|
const boundIncarnation = textAt(value, "incarnation");
|
|
2535
2409
|
const run = textAt(value, "run");
|
|
2536
2410
|
const owner = textAt(value, "owner");
|
|
2537
2411
|
const label = textAt(value, "label");
|
|
2538
2412
|
const observedAt = textAt(value, "observed_at");
|
|
2539
|
-
if (!isUuid(bound) || !isUuid(boundIncarnation) || run === void 0 || owner === void 0 || label === void 0 || observedAt === void 0)
|
|
2413
|
+
if (!isUuid(bound) || subjectId !== void 0 && !isUuid(subjectId) || !isUuid(boundIncarnation) || run === void 0 || owner === void 0 || label === void 0 || observedAt === void 0)
|
|
2540
2414
|
return void 0;
|
|
2541
2415
|
return {
|
|
2542
2416
|
version: RADAR_PUBLICATION_VERSION,
|
|
2543
2417
|
registration_id: bound,
|
|
2418
|
+
...subjectId !== void 0 ? { subject_id: subjectId } : {},
|
|
2544
2419
|
incarnation: boundIncarnation,
|
|
2545
2420
|
run,
|
|
2546
2421
|
owner,
|
|
@@ -2934,7 +2809,7 @@ async function resolveRadarProcessLocation(client, env = process.env) {
|
|
|
2934
2809
|
|
|
2935
2810
|
// extension/radar-runtime.ts
|
|
2936
2811
|
var RADAR_RUNTIME_REQUEST = "pi-radar/runtime-request";
|
|
2937
|
-
var RADAR_RUNTIME_VERSION =
|
|
2812
|
+
var RADAR_RUNTIME_VERSION = 5;
|
|
2938
2813
|
function lazyRadarRuntime(start) {
|
|
2939
2814
|
let pending;
|
|
2940
2815
|
return {
|