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
|
@@ -1,237 +1,127 @@
|
|
|
1
|
-
// extension/radar-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
}
|
|
1
|
+
// extension/radar-children.ts
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
|
|
4
|
+
// extension/radar-launch.ts
|
|
5
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
42
6
|
|
|
43
7
|
// extension/radar-client.ts
|
|
44
8
|
var RADAR_MAX_LINE_BYTES = 1024 * 1024;
|
|
9
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
10
|
+
function isUuid(value) {
|
|
11
|
+
return typeof value === "string" && UUID.test(value);
|
|
12
|
+
}
|
|
45
13
|
|
|
46
14
|
// extension/radar-launch.ts
|
|
47
|
-
function
|
|
48
|
-
return { kind: "
|
|
15
|
+
function refusedLaunch(message) {
|
|
16
|
+
return { kind: "failed", message };
|
|
49
17
|
}
|
|
50
|
-
function
|
|
51
|
-
return `the daemon's current record of this launch's child is ${reason}`;
|
|
52
|
-
}
|
|
53
|
-
async function observeRadarChild(client, provenance) {
|
|
54
|
-
if (provenance.registration_id === void 0)
|
|
55
|
-
return unknownChild("not bound to a child by this launch");
|
|
56
|
-
if (provenance.pane_id === void 0)
|
|
57
|
-
return unknownChild("without a pane this launch recorded for it");
|
|
58
|
-
const read = await client.spawnGet(provenance.request_id);
|
|
59
|
-
if (read.ok === false)
|
|
60
|
-
return unknownChild(
|
|
61
|
-
`not readable (${read.code}: ${read.message})`
|
|
62
|
-
);
|
|
63
|
-
const topology = read.value;
|
|
64
|
-
const edge = topology.edge;
|
|
65
|
-
if (edge.bound === void 0 || edge.bound.registration_id === void 0)
|
|
66
|
-
return unknownChild("not bound to a child by the daemon");
|
|
67
|
-
if (edge.bound.registration_id !== provenance.registration_id)
|
|
68
|
-
return unknownChild(
|
|
69
|
-
`bound to another subject (${edge.bound.registration_id}), not to ${provenance.registration_id}`
|
|
70
|
-
);
|
|
71
|
-
if (edge.location?.pane === void 0)
|
|
72
|
-
return unknownChild("recorded without a pane");
|
|
73
|
-
if (edge.location.pane !== provenance.pane_id)
|
|
74
|
-
return unknownChild(
|
|
75
|
-
`recorded on pane ${edge.location.pane}, not on ${provenance.pane_id}`
|
|
76
|
-
);
|
|
77
|
-
if (edge.exit_observation?.state !== "unknown")
|
|
78
|
-
return unknownChild("recorded with an observed process exit");
|
|
79
|
-
if (topology.freshness !== "fresh")
|
|
80
|
-
return unknownChild(
|
|
81
|
-
"on a pane the daemon's own inventory does not currently report"
|
|
82
|
-
);
|
|
83
|
-
if (topology.state !== "bound")
|
|
84
|
-
return unknownChild(
|
|
85
|
-
`not currently placed (state ${topology.state ?? "unreported"})`
|
|
86
|
-
);
|
|
87
|
-
const reply = await client.agentGet(edge.bound.registration_id);
|
|
88
|
-
if (reply.ok === false)
|
|
89
|
-
return unknownChild(
|
|
90
|
-
`process verdict not readable (${reply.code}: ${reply.message})`
|
|
91
|
-
);
|
|
92
|
-
const { verification, location, contextSession } = reply.value;
|
|
93
|
-
if (verification === "absent" || verification === "mismatched")
|
|
94
|
-
return {
|
|
95
|
-
kind: "gone",
|
|
96
|
-
verification,
|
|
97
|
-
diagnostic: trajectory(`the registered process is ${verification}`)
|
|
98
|
-
};
|
|
99
|
-
if (verification !== "verified")
|
|
100
|
-
return unknownChild(`process verdict is ${verification}`);
|
|
101
|
-
if (location?.pane !== provenance.pane_id)
|
|
102
|
-
return unknownChild(
|
|
103
|
-
`registered on ${location?.pane ?? "no pane"}, not on ${provenance.pane_id}`
|
|
104
|
-
);
|
|
105
|
-
if (provenance.session_id === void 0)
|
|
106
|
-
return unknownChild("without a session this launch recorded for it");
|
|
107
|
-
if (contextSession !== provenance.session_id)
|
|
108
|
-
return unknownChild(
|
|
109
|
-
`publishing session ${contextSession ?? "none"}, not ${provenance.session_id}`
|
|
110
|
-
);
|
|
18
|
+
function unresolvedLaunch(requestId, message) {
|
|
111
19
|
return {
|
|
112
|
-
kind: "
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
registration_id: edge.bound.registration_id,
|
|
116
|
-
source: edge.bound.source,
|
|
117
|
-
incarnation: edge.bound.incarnation,
|
|
118
|
-
pane_id: edge.location.pane
|
|
119
|
-
}
|
|
20
|
+
kind: "unresolved",
|
|
21
|
+
request_id: requestId,
|
|
22
|
+
message: `spawn ${requestId} is unresolved (${message})`
|
|
120
23
|
};
|
|
121
24
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
});
|
|
25
|
+
var PRE_EFFECT_CODES = [
|
|
26
|
+
"absent",
|
|
27
|
+
"unsupported",
|
|
28
|
+
"bad_version",
|
|
29
|
+
"bad_request",
|
|
30
|
+
"bad_params",
|
|
31
|
+
"unknown_method",
|
|
32
|
+
"refused",
|
|
33
|
+
"not_found",
|
|
34
|
+
"busy",
|
|
35
|
+
"backend_unavailable"
|
|
36
|
+
];
|
|
37
|
+
function launchFailure(result, requestId) {
|
|
38
|
+
return PRE_EFFECT_CODES.includes(result.code) ? refusedLaunch(result.message) : unresolvedLaunch(requestId, result.message);
|
|
39
|
+
}
|
|
40
|
+
async function waitForChild(client, requestId, options) {
|
|
41
|
+
let last = "the daemon has not answered";
|
|
42
|
+
for (; ; ) {
|
|
43
|
+
if (options.signal?.aborted)
|
|
44
|
+
return unresolvedLaunch(requestId, `the launch was cancelled (${last})`);
|
|
45
|
+
const answer = await client.spawnGet(requestId);
|
|
46
|
+
if (answer.ok === true) {
|
|
47
|
+
const { edge } = answer.value;
|
|
48
|
+
if (edge.withdrawn !== void 0)
|
|
49
|
+
return refusedLaunch(`the daemon withdrew this launch: ${edge.withdrawn.reason}`);
|
|
50
|
+
if (edge.granted_at === void 0) {
|
|
51
|
+
await delay(options.pollMs);
|
|
52
|
+
continue;
|
|
152
53
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
54
|
+
const pane = edge.location?.pane;
|
|
55
|
+
if (pane === void 0 || pane === "")
|
|
56
|
+
return unresolvedLaunch(
|
|
57
|
+
requestId,
|
|
58
|
+
"the daemon's launch recorded no pane to verify the child against"
|
|
59
|
+
);
|
|
60
|
+
const agentId = edge.bound?.registration_id;
|
|
61
|
+
if (agentId !== void 0 && isUuid(agentId)) {
|
|
62
|
+
const subjectId = edge.bound?.subject_id;
|
|
63
|
+
if (subjectId === void 0 || !isUuid(subjectId))
|
|
64
|
+
return unresolvedLaunch(
|
|
65
|
+
requestId,
|
|
66
|
+
`the daemon bound the child ${agentId} without recording its subject`
|
|
67
|
+
);
|
|
68
|
+
return {
|
|
69
|
+
kind: "bound",
|
|
70
|
+
subject_id: subjectId,
|
|
71
|
+
pane_id: pane
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
last = "no child has registered with this launch's token yet";
|
|
75
|
+
} else {
|
|
76
|
+
return unresolvedLaunch(requestId, answer.message);
|
|
173
77
|
}
|
|
78
|
+
await delay(options.pollMs);
|
|
174
79
|
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// extension/radar-children.ts
|
|
83
|
+
function createRadarChildren(client, publication) {
|
|
175
84
|
return {
|
|
176
|
-
async list() {
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if (latest === void 0) return;
|
|
189
|
-
if (entry.published !== void 0 && entry.published !== target.registration_id)
|
|
190
|
-
publication.retireAssignment(entry.published);
|
|
191
|
-
entry.published = target.registration_id;
|
|
192
|
-
publication.assignment(target.registration_id, owner).update(latest);
|
|
193
|
-
};
|
|
194
|
-
entry.publish = send;
|
|
195
|
-
entry.writer = {
|
|
196
|
-
update(snapshot) {
|
|
197
|
-
latest = snapshot;
|
|
198
|
-
void resolve(subject).then((target) => {
|
|
199
|
-
if (target !== void 0) send(target);
|
|
200
|
-
});
|
|
201
|
-
},
|
|
202
|
-
retire() {
|
|
203
|
-
latest = void 0;
|
|
204
|
-
if (entry.published !== void 0) publication.retireAssignment(entry.published);
|
|
205
|
-
entry.published = void 0;
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
held.set(subject, entry);
|
|
209
|
-
return entry.writer;
|
|
85
|
+
async list(selection) {
|
|
86
|
+
const children = [];
|
|
87
|
+
let after;
|
|
88
|
+
do {
|
|
89
|
+
const page = await client.subjectChildren({ selection, after, limit: 100 });
|
|
90
|
+
if (!page.ok) return page;
|
|
91
|
+
children.push(...page.value.subjects);
|
|
92
|
+
if (page.value.next === after)
|
|
93
|
+
return { ok: false, code: "protocol", message: "child page did not advance" };
|
|
94
|
+
after = page.value.next ?? void 0;
|
|
95
|
+
} while (after !== void 0);
|
|
96
|
+
return { ok: true, value: children };
|
|
210
97
|
},
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const
|
|
217
|
-
if (
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
pane_id: target.child.pane_id,
|
|
222
|
-
registration_id: target.registration_id,
|
|
223
|
-
session_id
|
|
98
|
+
async restart(subject, program, options) {
|
|
99
|
+
const parent = await publication.subject();
|
|
100
|
+
if (parent?.subject_id === void 0)
|
|
101
|
+
return { kind: "failed", message: "this process has no granted subject" };
|
|
102
|
+
const request_id = randomUUID();
|
|
103
|
+
const requested = await client.restart({ request_id, parent: parent.registration_id, subject_id: subject, ...program });
|
|
104
|
+
if (!requested.ok) return launchFailure(requested, request_id);
|
|
105
|
+
const bound = await waitForChild(client, requested.value.launch_request_id, {
|
|
106
|
+
pollMs: options?.pollMs ?? 250,
|
|
107
|
+
...options?.signal !== void 0 ? { signal: options.signal } : {}
|
|
224
108
|
});
|
|
109
|
+
if (bound.kind !== "bound") return bound;
|
|
110
|
+
return { kind: "ready", value: { request_id: requested.value.launch_request_id, subject_id: bound.subject_id, pane_id: bound.pane_id } };
|
|
225
111
|
},
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
return
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
112
|
+
assignment: (subject, owner) => publication.assignment(subject, owner),
|
|
113
|
+
retire: (subject) => publication.retireAssignment(subject),
|
|
114
|
+
async observe(subject) {
|
|
115
|
+
const read = await client.childGet(subject);
|
|
116
|
+
if (!read.ok) return { kind: "unknown", diagnostic: `${read.code}: ${read.message}` };
|
|
117
|
+
if (read.value.state === "live") return { kind: "live", subject: read.value };
|
|
118
|
+
if (read.value.state === "detached") return { kind: "gone", subject: read.value };
|
|
119
|
+
return { kind: "unknown", diagnostic: `child attachment is ${read.value.state}` };
|
|
120
|
+
},
|
|
121
|
+
async inspect(subject) {
|
|
122
|
+
const read = await client.childInspect(subject);
|
|
123
|
+
if (!read.ok) throw new Error(`Child inspection unavailable (${read.code}: ${read.message})`);
|
|
124
|
+
return read.value;
|
|
235
125
|
}
|
|
236
126
|
};
|
|
237
127
|
}
|
|
@@ -4,11 +4,10 @@ import { lstatSync } from "node:fs";
|
|
|
4
4
|
import { createConnection } from "node:net";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
var RADAR_PROTOCOL_VERSION = 2;
|
|
7
|
-
var RADAR_MINIMUM_REVISION =
|
|
7
|
+
var RADAR_MINIMUM_REVISION = 9;
|
|
8
8
|
var RADAR_REGISTRY_CONTRACT = "registry.v1";
|
|
9
9
|
var RADAR_REGISTRY_FACTS_CONTRACT = "registry.facts.v1";
|
|
10
10
|
var RADAR_SPAWN_CONTRACT = "spawn.v1";
|
|
11
|
-
var RADAR_SPAWN_LIST_CONTRACT = "spawn.list.v1";
|
|
12
11
|
var RADAR_SPAWN_CLOSE_CONTRACT = "spawn.close.v1";
|
|
13
12
|
var RADAR_LIFECYCLE_RESTART_CONTRACT = "lifecycle.restart.v1";
|
|
14
13
|
var RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT = "spawn.launch_context.v1";
|
|
@@ -318,6 +317,9 @@ function spawnEdge(value) {
|
|
|
318
317
|
const agentId = stringAt(bound, "registration_id");
|
|
319
318
|
const subjectId = stringAt(bound, "subject_id");
|
|
320
319
|
const exit = exitAt(recordAt(value, "exit_observation"));
|
|
320
|
+
const withdrawnRecord = recordAt(value, "withdrawn");
|
|
321
|
+
const withdrawnAt = stringAt(withdrawnRecord, "at");
|
|
322
|
+
const withdrawnReason = stringAt(withdrawnRecord, "reason");
|
|
321
323
|
return {
|
|
322
324
|
ok: true,
|
|
323
325
|
value: {
|
|
@@ -334,7 +336,9 @@ function spawnEdge(value) {
|
|
|
334
336
|
...isUuid(subjectId) ? { subject_id: subjectId } : {}
|
|
335
337
|
}
|
|
336
338
|
} : {},
|
|
337
|
-
...exit !== void 0 ? { exit_observation: exit } : {}
|
|
339
|
+
...exit !== void 0 ? { exit_observation: exit } : {},
|
|
340
|
+
...stringAt(value, "granted_at") !== void 0 ? { granted_at: stringAt(value, "granted_at") } : {},
|
|
341
|
+
...withdrawnAt !== void 0 && withdrawnReason !== void 0 ? { withdrawn: { at: withdrawnAt, reason: withdrawnReason } } : {}
|
|
338
342
|
}
|
|
339
343
|
};
|
|
340
344
|
}
|
|
@@ -344,6 +348,14 @@ var PROCESS_VERDICTS = [
|
|
|
344
348
|
"mismatched",
|
|
345
349
|
"unavailable"
|
|
346
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
|
+
}
|
|
347
359
|
function agentProcessAt(agent) {
|
|
348
360
|
const process2 = recordAt(agent, "process");
|
|
349
361
|
const claimed = recordAt(process2, "claimed");
|
|
@@ -736,7 +748,9 @@ function createRadarClient(options = {}) {
|
|
|
736
748
|
return withRegistry(async () => {
|
|
737
749
|
const answer = await call("agent.presence", { ...request });
|
|
738
750
|
if (!answer.ok) return answer;
|
|
739
|
-
|
|
751
|
+
const decoded = decodePresenceAnswer(answer.value);
|
|
752
|
+
if (decoded.ok) processRegistrationId = decoded.value.registration_id;
|
|
753
|
+
return decoded;
|
|
740
754
|
});
|
|
741
755
|
},
|
|
742
756
|
async releasePresence(request) {
|
|
@@ -771,9 +785,13 @@ function createRadarClient(options = {}) {
|
|
|
771
785
|
},
|
|
772
786
|
async acquire(request) {
|
|
773
787
|
return withRegistryChannel(request.channel, async () => {
|
|
774
|
-
const
|
|
775
|
-
|
|
776
|
-
|
|
788
|
+
const assignment = request.channel === "assignment";
|
|
789
|
+
if (assignment && processRegistrationId === void 0)
|
|
790
|
+
return { ok: false, code: "refused", message: "assignment requires a granted parent" };
|
|
791
|
+
if (assignment && request.subject_id === void 0)
|
|
792
|
+
return { ok: false, code: "bad_params", message: "an assignment names a child subject" };
|
|
793
|
+
const answer = await call(assignment ? "subject.assignment.acquire" : "agent.acquire", {
|
|
794
|
+
...assignment ? { registration_id: processRegistrationId, subject_id: request.subject_id } : { registration_id: request.registration_id, channel: request.channel },
|
|
777
795
|
publisher: request.publisher,
|
|
778
796
|
...request.replace !== void 0 ? { replace: request.replace } : {}
|
|
779
797
|
});
|
|
@@ -800,15 +818,19 @@ function createRadarClient(options = {}) {
|
|
|
800
818
|
}
|
|
801
819
|
}
|
|
802
820
|
return withRegistryChannel(request.channel, async () => {
|
|
803
|
-
const
|
|
804
|
-
registration_id: request.registration_id,
|
|
805
|
-
channel: request.channel,
|
|
821
|
+
const update = {
|
|
806
822
|
writer_handle: request.writer_handle,
|
|
807
823
|
sequence: request.sequence,
|
|
808
824
|
...request.lease_ms !== void 0 ? { lease_ms: request.lease_ms } : {},
|
|
809
825
|
...request.observed_at !== void 0 ? { observed_at: request.observed_at } : {},
|
|
810
826
|
snapshot
|
|
811
|
-
}
|
|
827
|
+
};
|
|
828
|
+
const assignment = request.channel === "assignment";
|
|
829
|
+
if (assignment && processRegistrationId === void 0)
|
|
830
|
+
return { ok: false, code: "refused", message: "assignment requires a granted parent" };
|
|
831
|
+
if (assignment && request.subject_id === void 0)
|
|
832
|
+
return { ok: false, code: "bad_params", message: "an assignment names a child subject" };
|
|
833
|
+
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 });
|
|
812
834
|
if (answer.ok === false) return answer;
|
|
813
835
|
const channel = answer.value.channel;
|
|
814
836
|
const writer = channel?.writer;
|
|
@@ -849,7 +871,15 @@ function createRadarClient(options = {}) {
|
|
|
849
871
|
},
|
|
850
872
|
async retire(request) {
|
|
851
873
|
return withRegistryChannel(request.channel, async () => {
|
|
852
|
-
const
|
|
874
|
+
const assignment = request.channel === "assignment";
|
|
875
|
+
if (assignment && processRegistrationId === void 0)
|
|
876
|
+
return { ok: false, code: "refused", message: "assignment requires a granted parent" };
|
|
877
|
+
if (assignment && request.subject_id === void 0)
|
|
878
|
+
return { ok: false, code: "bad_params", message: "an assignment names a child subject" };
|
|
879
|
+
const answer = await call(assignment ? "subject.assignment.retire" : "agent.retire", {
|
|
880
|
+
...assignment ? { registration_id: processRegistrationId, subject_id: request.subject_id } : { registration_id: request.registration_id, channel: request.channel },
|
|
881
|
+
writer_handle: request.writer_handle
|
|
882
|
+
});
|
|
853
883
|
return answer.ok === true ? { ok: true, value: void 0 } : answer;
|
|
854
884
|
});
|
|
855
885
|
},
|
|
@@ -924,14 +954,73 @@ function createRadarClient(options = {}) {
|
|
|
924
954
|
request.request_id
|
|
925
955
|
);
|
|
926
956
|
if (answer.ok === false) return answer;
|
|
927
|
-
|
|
957
|
+
const settled = focusOutcome(recordAt(answer.value, "request"), request.request_id);
|
|
958
|
+
if (settled.ok === false) return settled;
|
|
959
|
+
const launch = recordAt(recordAt(answer.value, "request"), "launch_request_id");
|
|
960
|
+
if (!isUuid(launch)) return protocol("lifecycle.restart reply names no launch");
|
|
961
|
+
return { ok: true, value: { ...settled.value, launch_request_id: launch } };
|
|
962
|
+
});
|
|
963
|
+
},
|
|
964
|
+
async subjectChildren(request = {}) {
|
|
965
|
+
const registrationId = processRegistrationId;
|
|
966
|
+
if (registrationId === void 0)
|
|
967
|
+
return { ok: false, code: "refused", message: "this process has no Radar registration" };
|
|
968
|
+
return withRegistry(async () => {
|
|
969
|
+
const params = { ...request, registration_id: registrationId };
|
|
970
|
+
const answer = await call("subject.children", { ...params });
|
|
971
|
+
if (!answer.ok) return answer;
|
|
972
|
+
const subjects = recordAt(answer.value, "subjects");
|
|
973
|
+
const next = recordAt(answer.value, "next");
|
|
974
|
+
if (!Array.isArray(subjects) || next != null && !isUuid(next))
|
|
975
|
+
return protocol("subject.children reply carries no valid page");
|
|
976
|
+
for (const subject of subjects) {
|
|
977
|
+
const decoded = subjectAt(subject);
|
|
978
|
+
if (!decoded.ok) return decoded;
|
|
979
|
+
}
|
|
980
|
+
return { ok: true, value: answer.value };
|
|
981
|
+
});
|
|
982
|
+
},
|
|
983
|
+
async childGet(subject_id) {
|
|
984
|
+
const registrationId = processRegistrationId;
|
|
985
|
+
if (registrationId === void 0)
|
|
986
|
+
return { ok: false, code: "refused", message: "this process has no Radar registration" };
|
|
987
|
+
if (!isUuid(subject_id))
|
|
988
|
+
return { ok: false, code: "bad_params", message: "child requires a subject UUID" };
|
|
989
|
+
return withRegistry(async () => {
|
|
990
|
+
const params = { registration_id: registrationId, subject_id };
|
|
991
|
+
const answer = await call("subject.child.get", { ...params });
|
|
992
|
+
return answer.ok ? subjectAt(recordAt(answer.value, "subject")) : answer;
|
|
993
|
+
});
|
|
994
|
+
},
|
|
995
|
+
async childInspect(subject_id) {
|
|
996
|
+
const registration_id = processRegistrationId;
|
|
997
|
+
if (registration_id === void 0)
|
|
998
|
+
return { ok: false, code: "refused", message: "this process has no Radar registration" };
|
|
999
|
+
return withRegistry(async () => {
|
|
1000
|
+
const answer = await call("subject.child.inspect", { registration_id, subject_id });
|
|
1001
|
+
if (!answer.ok) return answer;
|
|
1002
|
+
const subject = subjectAt(recordAt(answer.value, "subject"));
|
|
1003
|
+
if (!subject.ok) return subject;
|
|
1004
|
+
const output = recordAt(answer.value, "output");
|
|
1005
|
+
const text = stringAt(output, "text");
|
|
1006
|
+
if (text === void 0 || typeof recordAt(output, "truncated") !== "boolean")
|
|
1007
|
+
return protocol("child inspection has no output evidence");
|
|
1008
|
+
const foreground = recordAt(answer.value, "foreground");
|
|
1009
|
+
const activity = subject.value.execution?.snapshot?.snapshot;
|
|
1010
|
+
return { ok: true, value: {
|
|
1011
|
+
capturedAt: Date.now(),
|
|
1012
|
+
recentOutputTruncated: recordAt(output, "truncated"),
|
|
1013
|
+
...text ? { recentOutput: text } : {},
|
|
1014
|
+
...foreground ? { foreground } : {},
|
|
1015
|
+
...activity && "activity" in activity ? { activity: activity.activity } : {}
|
|
1016
|
+
} };
|
|
928
1017
|
});
|
|
929
1018
|
},
|
|
930
1019
|
async spawnList(request) {
|
|
931
1020
|
const registrationId = processRegistrationId;
|
|
932
1021
|
if (registrationId === void 0)
|
|
933
1022
|
return { ok: false, code: "refused", message: "spawn.list requires this client's process registration" };
|
|
934
|
-
return
|
|
1023
|
+
return withRegistry(async () => {
|
|
935
1024
|
const params = {
|
|
936
1025
|
registration_id: registrationId,
|
|
937
1026
|
...request.after !== void 0 ? { after: request.after } : {},
|
|
@@ -1311,7 +1400,6 @@ export {
|
|
|
1311
1400
|
RADAR_SPAWN_CLOSE_CONTRACT,
|
|
1312
1401
|
RADAR_SPAWN_CONTRACT,
|
|
1313
1402
|
RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT,
|
|
1314
|
-
RADAR_SPAWN_LIST_CONTRACT,
|
|
1315
1403
|
boundedRadarAssignment,
|
|
1316
1404
|
boundedRadarFacts,
|
|
1317
1405
|
boundedRadarSnapshot,
|