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
|
@@ -438,15 +438,18 @@ function createChannelWriter(input) {
|
|
|
438
438
|
function blank(agentId) {
|
|
439
439
|
return {
|
|
440
440
|
version: RADAR_PUBLICATION_VERSION,
|
|
441
|
-
|
|
441
|
+
target_id: agentId,
|
|
442
442
|
channel,
|
|
443
443
|
publisher,
|
|
444
444
|
sequence: 0
|
|
445
445
|
};
|
|
446
446
|
}
|
|
447
|
+
function addressed(target) {
|
|
448
|
+
return channel === "assignment" ? { subject_id: target } : { registration_id: target };
|
|
449
|
+
}
|
|
447
450
|
function loadRecord() {
|
|
448
451
|
const value = store.read(recordName);
|
|
449
|
-
const agentId = textAt(value, "
|
|
452
|
+
const agentId = textAt(value, "target_id");
|
|
450
453
|
const storedPublisher = recordAt(value, "publisher");
|
|
451
454
|
if (numberAt(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)
|
|
452
455
|
return blank("");
|
|
@@ -461,7 +464,7 @@ function createChannelWriter(input) {
|
|
|
461
464
|
const sentAt = numberAt(acceptedValue, "sent_at");
|
|
462
465
|
return {
|
|
463
466
|
version: RADAR_PUBLICATION_VERSION,
|
|
464
|
-
|
|
467
|
+
target_id: agentId,
|
|
465
468
|
channel,
|
|
466
469
|
publisher,
|
|
467
470
|
...handle !== void 0 && generation !== void 0 ? { writer: { handle, generation } } : {},
|
|
@@ -502,15 +505,11 @@ function createChannelWriter(input) {
|
|
|
502
505
|
if (record.writer !== void 0) return true;
|
|
503
506
|
const agentId = await input.subject(recovering);
|
|
504
507
|
if (agentId === void 0) return false;
|
|
505
|
-
if (record.
|
|
508
|
+
if (record.target_id !== agentId) {
|
|
506
509
|
record = blank(agentId);
|
|
507
510
|
save();
|
|
508
511
|
}
|
|
509
|
-
const answer = await client.acquire({
|
|
510
|
-
registration_id: agentId,
|
|
511
|
-
channel,
|
|
512
|
-
publisher
|
|
513
|
-
});
|
|
512
|
+
const answer = await client.acquire({ ...addressed(agentId), channel, publisher });
|
|
514
513
|
if (answer.ok === false) {
|
|
515
514
|
if (answer.code === "not_found") {
|
|
516
515
|
recovering = true;
|
|
@@ -537,7 +536,7 @@ function createChannelWriter(input) {
|
|
|
537
536
|
const writer = record.writer;
|
|
538
537
|
if (writer === void 0) return;
|
|
539
538
|
const answer = await client.publish({
|
|
540
|
-
|
|
539
|
+
...addressed(record.target_id),
|
|
541
540
|
channel,
|
|
542
541
|
writer_handle: writer.handle,
|
|
543
542
|
sequence: pending.sequence,
|
|
@@ -631,9 +630,9 @@ function createChannelWriter(input) {
|
|
|
631
630
|
stopPump();
|
|
632
631
|
const writer = record.writer;
|
|
633
632
|
if (writer === void 0) return;
|
|
634
|
-
const agentId = record.
|
|
633
|
+
const agentId = record.target_id;
|
|
635
634
|
try {
|
|
636
|
-
void client.retire({
|
|
635
|
+
void client.retire({ ...addressed(agentId), channel, writer_handle: writer.handle }).then((answer) => {
|
|
637
636
|
if (answer.ok === true) {
|
|
638
637
|
try {
|
|
639
638
|
record.retired_at = new Date(scheduler.now()).toISOString();
|
|
@@ -723,6 +722,7 @@ function createPresenceReporter(input) {
|
|
|
723
722
|
}
|
|
724
723
|
claim.registration_id = answer.value.registration_id;
|
|
725
724
|
claim.subject_id = answer.value.standing === "granted" ? answer.value.subject_id ?? void 0 : void 0;
|
|
725
|
+
if (claim.subject_id !== void 0) input.granted?.(claim.subject_id);
|
|
726
726
|
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" };
|
|
727
727
|
diagnostic = answer.value.standing === "duplicate" ? DUPLICATE_SESSION_DIAGNOSTIC : void 0;
|
|
728
728
|
save();
|
|
@@ -904,7 +904,7 @@ function createRadarPublicationInner(options) {
|
|
|
904
904
|
agentId = registered;
|
|
905
905
|
processRecord = { ...record, registration_id: agentId };
|
|
906
906
|
store.write(registrationName, processRecord);
|
|
907
|
-
if (options.binding !== void 0) writeBinding(options.binding);
|
|
907
|
+
if (options.binding !== void 0) writeBinding(options.binding, grantedSubject());
|
|
908
908
|
return agentId;
|
|
909
909
|
})();
|
|
910
910
|
try {
|
|
@@ -913,11 +913,16 @@ function createRadarPublicationInner(options) {
|
|
|
913
913
|
subjectReady = void 0;
|
|
914
914
|
}
|
|
915
915
|
}
|
|
916
|
-
function
|
|
916
|
+
function grantedSubject() {
|
|
917
|
+
const standing = presence.management();
|
|
918
|
+
return standing?.state === "managed" ? standing.subject_id : void 0;
|
|
919
|
+
}
|
|
920
|
+
function writeBinding(binding, subjectId) {
|
|
917
921
|
if (agentId === void 0) return;
|
|
918
922
|
store.write(`bindings/${binding.key}.json`, {
|
|
919
923
|
version: RADAR_PUBLICATION_VERSION,
|
|
920
924
|
registration_id: agentId,
|
|
925
|
+
...subjectId !== void 0 ? { subject_id: subjectId } : {},
|
|
921
926
|
incarnation,
|
|
922
927
|
run: binding.run,
|
|
923
928
|
owner: binding.owner,
|
|
@@ -957,6 +962,9 @@ function createRadarPublicationInner(options) {
|
|
|
957
962
|
...launchRegistrationId ? { registration_id: launchRegistrationId } : {}
|
|
958
963
|
};
|
|
959
964
|
},
|
|
965
|
+
granted: (subjectId) => {
|
|
966
|
+
if (options.binding !== void 0) writeBinding(options.binding, subjectId);
|
|
967
|
+
},
|
|
960
968
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
961
969
|
});
|
|
962
970
|
const facts = createChannelWriter({
|
|
@@ -988,8 +996,7 @@ function createRadarPublicationInner(options) {
|
|
|
988
996
|
channel: "assignment",
|
|
989
997
|
publisher: publisher("herdsman-owner", reportingOwner),
|
|
990
998
|
codec: assignmentCodec,
|
|
991
|
-
//
|
|
992
|
-
// registration_id from the child's sidecar is the whole correlation.
|
|
999
|
+
// Radar owns this assignment across the child's process replacements.
|
|
993
1000
|
subject: async () => target,
|
|
994
1001
|
leaseMs: options.leaseMs ?? RADAR_LEASE_MS,
|
|
995
1002
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
@@ -1010,16 +1017,18 @@ function createRadarPublicationInner(options) {
|
|
|
1010
1017
|
return void 0;
|
|
1011
1018
|
}
|
|
1012
1019
|
const bound = textAt(value, "registration_id");
|
|
1020
|
+
const subjectId = textAt(value, "subject_id");
|
|
1013
1021
|
const boundIncarnation = textAt(value, "incarnation");
|
|
1014
1022
|
const run = textAt(value, "run");
|
|
1015
1023
|
const owner = textAt(value, "owner");
|
|
1016
1024
|
const label = textAt(value, "label");
|
|
1017
1025
|
const observedAt = textAt(value, "observed_at");
|
|
1018
|
-
if (!isUuid(bound) || !isUuid(boundIncarnation) || run === void 0 || owner === void 0 || label === void 0 || observedAt === void 0)
|
|
1026
|
+
if (!isUuid(bound) || subjectId !== void 0 && !isUuid(subjectId) || !isUuid(boundIncarnation) || run === void 0 || owner === void 0 || label === void 0 || observedAt === void 0)
|
|
1019
1027
|
return void 0;
|
|
1020
1028
|
return {
|
|
1021
1029
|
version: RADAR_PUBLICATION_VERSION,
|
|
1022
1030
|
registration_id: bound,
|
|
1031
|
+
...subjectId !== void 0 ? { subject_id: subjectId } : {},
|
|
1023
1032
|
incarnation: boundIncarnation,
|
|
1024
1033
|
run,
|
|
1025
1034
|
owner,
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
// extension/radar-inspection.ts
|
|
2
|
-
var INSPECTION_LINES = 80;
|
|
3
|
-
var INSPECTION_OUTPUT_BYTES = 16 * 1024;
|
|
4
|
-
async function provedRead(client, target) {
|
|
5
|
-
const read = await client.agentGet(target.agentId);
|
|
6
|
-
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;
|
|
7
|
-
}
|
|
8
|
-
async function inspectRadarChild(client, target) {
|
|
9
|
-
const before = await provedRead(client, target);
|
|
10
|
-
if (before === void 0)
|
|
11
|
-
throw new Error("Inspection target identity did not match");
|
|
12
|
-
const output = await client.output({
|
|
13
|
-
pane_id: target.paneId,
|
|
14
|
-
source: "recent_unwrapped",
|
|
15
|
-
lines: INSPECTION_LINES
|
|
16
|
-
});
|
|
17
|
-
if (output.ok === false)
|
|
18
|
-
throw new Error(
|
|
19
|
-
`Inspection output unavailable (${output.code}: ${output.message})`
|
|
20
|
-
);
|
|
21
|
-
const foreground = await client.processInfo({ pane_id: target.paneId });
|
|
22
|
-
const after = await provedRead(client, target);
|
|
23
|
-
if (after === void 0)
|
|
24
|
-
throw new Error("Inspection target changed during capture");
|
|
25
|
-
const lines = output.value.text.trim().split(/\r?\n/).slice(-INSPECTION_LINES);
|
|
26
|
-
const bounded = utf8Tail(lines.join("\n"), INSPECTION_OUTPUT_BYTES);
|
|
27
|
-
return Object.freeze({
|
|
28
|
-
capturedAt: Date.now(),
|
|
29
|
-
recentOutputTruncated: output.value.truncated || bounded.truncated,
|
|
30
|
-
...bounded.text ? { recentOutput: bounded.text } : {},
|
|
31
|
-
...foreground.ok === true ? { foreground: foreground.value } : {},
|
|
32
|
-
...after.activity !== void 0 ? { activity: after.activity } : {}
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
function utf8Tail(value, maxBytes) {
|
|
36
|
-
const bytes = Buffer.from(value);
|
|
37
|
-
if (bytes.length <= maxBytes) return { text: value, truncated: false };
|
|
38
|
-
let start = bytes.length - maxBytes;
|
|
39
|
-
while (start < bytes.length && (bytes[start] & 192) === 128) start++;
|
|
40
|
-
return { text: bytes.subarray(start).toString(), truncated: true };
|
|
41
|
-
}
|
|
42
|
-
export {
|
|
43
|
-
inspectRadarChild,
|
|
44
|
-
provedRead
|
|
45
|
-
};
|
|
@@ -8,103 +8,9 @@ function isUuid(value) {
|
|
|
8
8
|
return typeof value === "string" && UUID.test(value);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
// extension/radar-location.ts
|
|
12
|
-
function radarPaneHandle(qualified) {
|
|
13
|
-
return qualified.slice(qualified.lastIndexOf(":") + 1);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
11
|
// extension/radar-launch.ts
|
|
17
|
-
function unknownChild(reason) {
|
|
18
|
-
return { kind: "unknown", diagnostic: trajectory(reason) };
|
|
19
|
-
}
|
|
20
|
-
function trajectory(reason) {
|
|
21
|
-
return `the daemon's current record of this launch's child is ${reason}`;
|
|
22
|
-
}
|
|
23
|
-
async function observeRadarChild(client, provenance) {
|
|
24
|
-
if (provenance.registration_id === void 0)
|
|
25
|
-
return unknownChild("not bound to a child by this launch");
|
|
26
|
-
if (provenance.pane_id === void 0)
|
|
27
|
-
return unknownChild("without a pane this launch recorded for it");
|
|
28
|
-
const read = await client.spawnGet(provenance.request_id);
|
|
29
|
-
if (read.ok === false)
|
|
30
|
-
return unknownChild(
|
|
31
|
-
`not readable (${read.code}: ${read.message})`
|
|
32
|
-
);
|
|
33
|
-
const topology = read.value;
|
|
34
|
-
const edge = topology.edge;
|
|
35
|
-
if (edge.bound === void 0 || edge.bound.registration_id === void 0)
|
|
36
|
-
return unknownChild("not bound to a child by the daemon");
|
|
37
|
-
if (edge.bound.registration_id !== provenance.registration_id)
|
|
38
|
-
return unknownChild(
|
|
39
|
-
`bound to another subject (${edge.bound.registration_id}), not to ${provenance.registration_id}`
|
|
40
|
-
);
|
|
41
|
-
if (edge.location?.pane === void 0)
|
|
42
|
-
return unknownChild("recorded without a pane");
|
|
43
|
-
if (edge.location.pane !== provenance.pane_id)
|
|
44
|
-
return unknownChild(
|
|
45
|
-
`recorded on pane ${edge.location.pane}, not on ${provenance.pane_id}`
|
|
46
|
-
);
|
|
47
|
-
if (edge.exit_observation?.state !== "unknown")
|
|
48
|
-
return unknownChild("recorded with an observed process exit");
|
|
49
|
-
if (topology.freshness !== "fresh")
|
|
50
|
-
return unknownChild(
|
|
51
|
-
"on a pane the daemon's own inventory does not currently report"
|
|
52
|
-
);
|
|
53
|
-
if (topology.state !== "bound")
|
|
54
|
-
return unknownChild(
|
|
55
|
-
`not currently placed (state ${topology.state ?? "unreported"})`
|
|
56
|
-
);
|
|
57
|
-
const reply = await client.agentGet(edge.bound.registration_id);
|
|
58
|
-
if (reply.ok === false)
|
|
59
|
-
return unknownChild(
|
|
60
|
-
`process verdict not readable (${reply.code}: ${reply.message})`
|
|
61
|
-
);
|
|
62
|
-
const { verification, location, contextSession } = reply.value;
|
|
63
|
-
if (verification === "absent" || verification === "mismatched")
|
|
64
|
-
return {
|
|
65
|
-
kind: "gone",
|
|
66
|
-
verification,
|
|
67
|
-
diagnostic: trajectory(`the registered process is ${verification}`)
|
|
68
|
-
};
|
|
69
|
-
if (verification !== "verified")
|
|
70
|
-
return unknownChild(`process verdict is ${verification}`);
|
|
71
|
-
if (location?.pane !== provenance.pane_id)
|
|
72
|
-
return unknownChild(
|
|
73
|
-
`registered on ${location?.pane ?? "no pane"}, not on ${provenance.pane_id}`
|
|
74
|
-
);
|
|
75
|
-
if (provenance.session_id === void 0)
|
|
76
|
-
return unknownChild("without a session this launch recorded for it");
|
|
77
|
-
if (contextSession !== provenance.session_id)
|
|
78
|
-
return unknownChild(
|
|
79
|
-
`publishing session ${contextSession ?? "none"}, not ${provenance.session_id}`
|
|
80
|
-
);
|
|
81
|
-
return {
|
|
82
|
-
kind: "live",
|
|
83
|
-
evidence: {
|
|
84
|
-
request_id: provenance.request_id,
|
|
85
|
-
registration_id: edge.bound.registration_id,
|
|
86
|
-
source: edge.bound.source,
|
|
87
|
-
incarnation: edge.bound.incarnation,
|
|
88
|
-
pane_id: edge.location.pane
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
async function readProcessGone(client, agentId) {
|
|
93
|
-
const read = await client.agentGet(agentId);
|
|
94
|
-
if (read.ok === false) return void 0;
|
|
95
|
-
const { verification } = read.value;
|
|
96
|
-
return verification === "absent" || verification === "mismatched" ? verification : void 0;
|
|
97
|
-
}
|
|
98
|
-
async function relaunchRefusalForChild(client, agentId) {
|
|
99
|
-
const read = await client.agentGet(agentId);
|
|
100
|
-
if (read.ok === false)
|
|
101
|
-
return `the child's process cannot be read (${read.code}: ${read.message})`;
|
|
102
|
-
const { verification } = read.value;
|
|
103
|
-
if (verification === "absent" || verification === "mismatched") return void 0;
|
|
104
|
-
return `the child's registered process is ${verification === "verified" ? "still running" : "not proved gone"}`;
|
|
105
|
-
}
|
|
106
12
|
function refusedLaunch(message) {
|
|
107
|
-
return { kind: "
|
|
13
|
+
return { kind: "failed", message };
|
|
108
14
|
}
|
|
109
15
|
function unresolvedLaunch(requestId, message) {
|
|
110
16
|
return {
|
|
@@ -113,14 +19,6 @@ function unresolvedLaunch(requestId, message) {
|
|
|
113
19
|
message: `spawn ${requestId} is unresolved (${message})`
|
|
114
20
|
};
|
|
115
21
|
}
|
|
116
|
-
function edgeStanding(edge) {
|
|
117
|
-
if (edge.created === "refused" || edge.launched === "refused")
|
|
118
|
-
return "refused";
|
|
119
|
-
return edge.created === "completed" && edge.launched === "completed" ? "launched" : "unknown";
|
|
120
|
-
}
|
|
121
|
-
function edgeMessage(edge) {
|
|
122
|
-
return `the daemon's spawn record is created ${edge.created} and launched ${edge.launched}`;
|
|
123
|
-
}
|
|
124
22
|
var PRE_EFFECT_CODES = [
|
|
125
23
|
"absent",
|
|
126
24
|
"unsupported",
|
|
@@ -133,14 +31,12 @@ var PRE_EFFECT_CODES = [
|
|
|
133
31
|
"busy",
|
|
134
32
|
"backend_unavailable"
|
|
135
33
|
];
|
|
136
|
-
function
|
|
34
|
+
function launchFailure(result, requestId) {
|
|
137
35
|
return PRE_EFFECT_CODES.includes(result.code) ? refusedLaunch(result.message) : unresolvedLaunch(requestId, result.message);
|
|
138
36
|
}
|
|
139
37
|
async function launchRadarWorker(options) {
|
|
140
38
|
const { client, launch, signal } = options;
|
|
141
39
|
const pollMs = options.pollMs ?? 250;
|
|
142
|
-
const deadline = Date.now() + options.timeoutMs;
|
|
143
|
-
const remaining = () => Math.max(0, deadline - Date.now());
|
|
144
40
|
const parent = await options.publication.subject();
|
|
145
41
|
if (parent === void 0 || !isUuid(parent.registration_id))
|
|
146
42
|
return refusedLaunch(
|
|
@@ -171,55 +67,22 @@ async function launchRadarWorker(options) {
|
|
|
171
67
|
...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
|
|
172
68
|
});
|
|
173
69
|
if (spawned.ok === false) {
|
|
174
|
-
const refusal =
|
|
175
|
-
if (refusal.kind === "
|
|
176
|
-
}
|
|
177
|
-
if (spawned.ok === true) {
|
|
178
|
-
const standing = edgeStanding(spawned.value);
|
|
179
|
-
if (standing === "refused")
|
|
180
|
-
return refusedLaunch(edgeMessage(spawned.value));
|
|
181
|
-
if (standing === "unknown")
|
|
182
|
-
return unresolvedLaunch(launch.request_id, edgeMessage(spawned.value));
|
|
70
|
+
const refusal = launchFailure(spawned, launch.request_id);
|
|
71
|
+
if (refusal.kind === "failed") return refusal;
|
|
183
72
|
}
|
|
184
73
|
const bound = await waitForChild(client, launch.request_id, {
|
|
185
|
-
remaining,
|
|
186
74
|
pollMs,
|
|
187
75
|
...signal !== void 0 ? { signal } : {}
|
|
188
76
|
});
|
|
189
77
|
if (bound.kind !== "bound") return bound;
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
launch.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
workspaceId: launch.identity.workspaceId,
|
|
199
|
-
agentLabel: launch.identity.agentLabel,
|
|
200
|
-
// The child writes the raw TMUX_PANE of the pane it runs in. Requiring
|
|
201
|
-
// the pane the daemon created is what refuses a child that inherited
|
|
202
|
-
// another process's coordinates.
|
|
203
|
-
paneId: radarPaneHandle(bound.pane_id)
|
|
204
|
-
}),
|
|
205
|
-
{ timeoutMs: remaining(), ...signal !== void 0 ? { signal } : {} }
|
|
206
|
-
);
|
|
207
|
-
return {
|
|
208
|
-
kind: "ready",
|
|
209
|
-
value: {
|
|
210
|
-
request_id: launch.request_id,
|
|
211
|
-
subject_id: bound.subject_id,
|
|
212
|
-
pane_id: bound.pane_id,
|
|
213
|
-
state
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
} catch (error) {
|
|
217
|
-
const claimed = options.readers.readState(launch.mailbox)?.paneId;
|
|
218
|
-
return unresolvedLaunch(
|
|
219
|
-
launch.request_id,
|
|
220
|
-
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)})`
|
|
221
|
-
);
|
|
222
|
-
}
|
|
78
|
+
return {
|
|
79
|
+
kind: "ready",
|
|
80
|
+
value: {
|
|
81
|
+
request_id: launch.request_id,
|
|
82
|
+
subject_id: bound.subject_id,
|
|
83
|
+
pane_id: bound.pane_id
|
|
84
|
+
}
|
|
85
|
+
};
|
|
223
86
|
}
|
|
224
87
|
async function waitForChild(client, requestId, options) {
|
|
225
88
|
let last = "the daemon has not answered";
|
|
@@ -228,37 +91,21 @@ async function waitForChild(client, requestId, options) {
|
|
|
228
91
|
return unresolvedLaunch(requestId, `the launch was cancelled (${last})`);
|
|
229
92
|
const answer = await client.spawnGet(requestId);
|
|
230
93
|
if (answer.ok === true) {
|
|
231
|
-
const { edge
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
94
|
+
const { edge } = answer.value;
|
|
95
|
+
if (edge.withdrawn !== void 0)
|
|
96
|
+
return refusedLaunch(`the daemon withdrew this launch: ${edge.withdrawn.reason}`);
|
|
97
|
+
if (edge.granted_at === void 0) {
|
|
98
|
+
await delay(options.pollMs);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
237
101
|
const pane = edge.location?.pane;
|
|
238
102
|
if (pane === void 0 || pane === "")
|
|
239
103
|
return unresolvedLaunch(
|
|
240
104
|
requestId,
|
|
241
105
|
"the daemon's launch recorded no pane to verify the child against"
|
|
242
106
|
);
|
|
243
|
-
if (freshness !== "fresh")
|
|
244
|
-
return unresolvedLaunch(
|
|
245
|
-
requestId,
|
|
246
|
-
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`
|
|
247
|
-
);
|
|
248
|
-
if (edge.exit_observation?.state === "exited")
|
|
249
|
-
return unresolvedLaunch(
|
|
250
|
-
requestId,
|
|
251
|
-
"the daemon observed the launched child's process exit"
|
|
252
|
-
);
|
|
253
107
|
const agentId = edge.bound?.registration_id;
|
|
254
108
|
if (agentId !== void 0 && isUuid(agentId)) {
|
|
255
|
-
const source = edge.bound?.source;
|
|
256
|
-
const incarnation = edge.bound?.incarnation;
|
|
257
|
-
if (source === void 0 || source === "" || incarnation === void 0 || incarnation === "")
|
|
258
|
-
return unresolvedLaunch(
|
|
259
|
-
requestId,
|
|
260
|
-
`the daemon bound the child ${agentId} without recording the process it registered as`
|
|
261
|
-
);
|
|
262
109
|
const subjectId = edge.bound?.subject_id;
|
|
263
110
|
if (subjectId === void 0 || !isUuid(subjectId))
|
|
264
111
|
return unresolvedLaunch(
|
|
@@ -268,26 +115,18 @@ async function waitForChild(client, requestId, options) {
|
|
|
268
115
|
return {
|
|
269
116
|
kind: "bound",
|
|
270
117
|
subject_id: subjectId,
|
|
271
|
-
|
|
272
|
-
pane_id: pane,
|
|
273
|
-
source,
|
|
274
|
-
incarnation
|
|
118
|
+
pane_id: pane
|
|
275
119
|
};
|
|
276
120
|
}
|
|
277
121
|
last = "no child has registered with this launch's token yet";
|
|
278
122
|
} else {
|
|
279
|
-
|
|
123
|
+
return unresolvedLaunch(requestId, answer.message);
|
|
280
124
|
}
|
|
281
|
-
|
|
282
|
-
return unresolvedLaunch(requestId, last);
|
|
283
|
-
await delay(options.pollMs, void 0, {
|
|
284
|
-
...options.signal !== void 0 ? { signal: options.signal } : {}
|
|
285
|
-
});
|
|
125
|
+
await delay(options.pollMs);
|
|
286
126
|
}
|
|
287
127
|
}
|
|
288
128
|
export {
|
|
129
|
+
launchFailure,
|
|
289
130
|
launchRadarWorker,
|
|
290
|
-
|
|
291
|
-
readProcessGone,
|
|
292
|
-
relaunchRefusalForChild
|
|
131
|
+
waitForChild
|
|
293
132
|
};
|
|
@@ -443,15 +443,18 @@ function createChannelWriter(input) {
|
|
|
443
443
|
function blank(agentId) {
|
|
444
444
|
return {
|
|
445
445
|
version: RADAR_PUBLICATION_VERSION,
|
|
446
|
-
|
|
446
|
+
target_id: agentId,
|
|
447
447
|
channel,
|
|
448
448
|
publisher,
|
|
449
449
|
sequence: 0
|
|
450
450
|
};
|
|
451
451
|
}
|
|
452
|
+
function addressed(target) {
|
|
453
|
+
return channel === "assignment" ? { subject_id: target } : { registration_id: target };
|
|
454
|
+
}
|
|
452
455
|
function loadRecord() {
|
|
453
456
|
const value = store.read(recordName);
|
|
454
|
-
const agentId = textAt(value, "
|
|
457
|
+
const agentId = textAt(value, "target_id");
|
|
455
458
|
const storedPublisher = recordAt(value, "publisher");
|
|
456
459
|
if (numberAt(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)
|
|
457
460
|
return blank("");
|
|
@@ -466,7 +469,7 @@ function createChannelWriter(input) {
|
|
|
466
469
|
const sentAt = numberAt(acceptedValue, "sent_at");
|
|
467
470
|
return {
|
|
468
471
|
version: RADAR_PUBLICATION_VERSION,
|
|
469
|
-
|
|
472
|
+
target_id: agentId,
|
|
470
473
|
channel,
|
|
471
474
|
publisher,
|
|
472
475
|
...handle !== void 0 && generation !== void 0 ? { writer: { handle, generation } } : {},
|
|
@@ -507,15 +510,11 @@ function createChannelWriter(input) {
|
|
|
507
510
|
if (record.writer !== void 0) return true;
|
|
508
511
|
const agentId = await input.subject(recovering);
|
|
509
512
|
if (agentId === void 0) return false;
|
|
510
|
-
if (record.
|
|
513
|
+
if (record.target_id !== agentId) {
|
|
511
514
|
record = blank(agentId);
|
|
512
515
|
save();
|
|
513
516
|
}
|
|
514
|
-
const answer = await client.acquire({
|
|
515
|
-
registration_id: agentId,
|
|
516
|
-
channel,
|
|
517
|
-
publisher
|
|
518
|
-
});
|
|
517
|
+
const answer = await client.acquire({ ...addressed(agentId), channel, publisher });
|
|
519
518
|
if (answer.ok === false) {
|
|
520
519
|
if (answer.code === "not_found") {
|
|
521
520
|
recovering = true;
|
|
@@ -542,7 +541,7 @@ function createChannelWriter(input) {
|
|
|
542
541
|
const writer = record.writer;
|
|
543
542
|
if (writer === void 0) return;
|
|
544
543
|
const answer = await client.publish({
|
|
545
|
-
|
|
544
|
+
...addressed(record.target_id),
|
|
546
545
|
channel,
|
|
547
546
|
writer_handle: writer.handle,
|
|
548
547
|
sequence: pending.sequence,
|
|
@@ -636,9 +635,9 @@ function createChannelWriter(input) {
|
|
|
636
635
|
stopPump();
|
|
637
636
|
const writer = record.writer;
|
|
638
637
|
if (writer === void 0) return;
|
|
639
|
-
const agentId = record.
|
|
638
|
+
const agentId = record.target_id;
|
|
640
639
|
try {
|
|
641
|
-
void client.retire({
|
|
640
|
+
void client.retire({ ...addressed(agentId), channel, writer_handle: writer.handle }).then((answer) => {
|
|
642
641
|
if (answer.ok === true) {
|
|
643
642
|
try {
|
|
644
643
|
record.retired_at = new Date(scheduler.now()).toISOString();
|
|
@@ -728,6 +727,7 @@ function createPresenceReporter(input) {
|
|
|
728
727
|
}
|
|
729
728
|
claim.registration_id = answer.value.registration_id;
|
|
730
729
|
claim.subject_id = answer.value.standing === "granted" ? answer.value.subject_id ?? void 0 : void 0;
|
|
730
|
+
if (claim.subject_id !== void 0) input.granted?.(claim.subject_id);
|
|
731
731
|
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" };
|
|
732
732
|
diagnostic = answer.value.standing === "duplicate" ? DUPLICATE_SESSION_DIAGNOSTIC : void 0;
|
|
733
733
|
save();
|
|
@@ -909,7 +909,7 @@ function createRadarPublicationInner(options) {
|
|
|
909
909
|
agentId = registered;
|
|
910
910
|
processRecord = { ...record, registration_id: agentId };
|
|
911
911
|
store.write(registrationName, processRecord);
|
|
912
|
-
if (options.binding !== void 0) writeBinding(options.binding);
|
|
912
|
+
if (options.binding !== void 0) writeBinding(options.binding, grantedSubject());
|
|
913
913
|
return agentId;
|
|
914
914
|
})();
|
|
915
915
|
try {
|
|
@@ -918,11 +918,16 @@ function createRadarPublicationInner(options) {
|
|
|
918
918
|
subjectReady = void 0;
|
|
919
919
|
}
|
|
920
920
|
}
|
|
921
|
-
function
|
|
921
|
+
function grantedSubject() {
|
|
922
|
+
const standing = presence.management();
|
|
923
|
+
return standing?.state === "managed" ? standing.subject_id : void 0;
|
|
924
|
+
}
|
|
925
|
+
function writeBinding(binding, subjectId) {
|
|
922
926
|
if (agentId === void 0) return;
|
|
923
927
|
store.write(`bindings/${binding.key}.json`, {
|
|
924
928
|
version: RADAR_PUBLICATION_VERSION,
|
|
925
929
|
registration_id: agentId,
|
|
930
|
+
...subjectId !== void 0 ? { subject_id: subjectId } : {},
|
|
926
931
|
incarnation,
|
|
927
932
|
run: binding.run,
|
|
928
933
|
owner: binding.owner,
|
|
@@ -962,6 +967,9 @@ function createRadarPublicationInner(options) {
|
|
|
962
967
|
...launchRegistrationId ? { registration_id: launchRegistrationId } : {}
|
|
963
968
|
};
|
|
964
969
|
},
|
|
970
|
+
granted: (subjectId) => {
|
|
971
|
+
if (options.binding !== void 0) writeBinding(options.binding, subjectId);
|
|
972
|
+
},
|
|
965
973
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
966
974
|
});
|
|
967
975
|
const facts = createChannelWriter({
|
|
@@ -993,8 +1001,7 @@ function createRadarPublicationInner(options) {
|
|
|
993
1001
|
channel: "assignment",
|
|
994
1002
|
publisher: publisher("herdsman-owner", reportingOwner),
|
|
995
1003
|
codec: assignmentCodec,
|
|
996
|
-
//
|
|
997
|
-
// registration_id from the child's sidecar is the whole correlation.
|
|
1004
|
+
// Radar owns this assignment across the child's process replacements.
|
|
998
1005
|
subject: async () => target,
|
|
999
1006
|
leaseMs: options.leaseMs ?? RADAR_LEASE_MS,
|
|
1000
1007
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
@@ -1015,16 +1022,18 @@ function createRadarPublicationInner(options) {
|
|
|
1015
1022
|
return void 0;
|
|
1016
1023
|
}
|
|
1017
1024
|
const bound = textAt(value, "registration_id");
|
|
1025
|
+
const subjectId = textAt(value, "subject_id");
|
|
1018
1026
|
const boundIncarnation = textAt(value, "incarnation");
|
|
1019
1027
|
const run = textAt(value, "run");
|
|
1020
1028
|
const owner = textAt(value, "owner");
|
|
1021
1029
|
const label = textAt(value, "label");
|
|
1022
1030
|
const observedAt = textAt(value, "observed_at");
|
|
1023
|
-
if (!isUuid(bound) || !isUuid(boundIncarnation) || run === void 0 || owner === void 0 || label === void 0 || observedAt === void 0)
|
|
1031
|
+
if (!isUuid(bound) || subjectId !== void 0 && !isUuid(subjectId) || !isUuid(boundIncarnation) || run === void 0 || owner === void 0 || label === void 0 || observedAt === void 0)
|
|
1024
1032
|
return void 0;
|
|
1025
1033
|
return {
|
|
1026
1034
|
version: RADAR_PUBLICATION_VERSION,
|
|
1027
1035
|
registration_id: bound,
|
|
1036
|
+
...subjectId !== void 0 ? { subject_id: subjectId } : {},
|
|
1028
1037
|
incarnation: boundIncarnation,
|
|
1029
1038
|
run,
|
|
1030
1039
|
owner,
|