pi-radar 0.7.0 → 0.10.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 +12 -0
- package/dist/extension/baseline.js +386 -10
- package/dist/extension/radar-children.js +344 -0
- package/dist/extension/radar-client.js +47 -4
- package/dist/extension/radar-launch.js +42 -112
- package/dist/extension/radar-runtime.js +1 -1
- package/dist/index.js +471 -341
- package/extension/baseline.ts +2 -0
- package/extension/generated-contracts.ts +15 -2
- package/extension/radar-children.ts +222 -0
- package/extension/radar-client.ts +70 -2
- package/extension/radar-launch.ts +91 -274
- package/extension/radar-runtime.ts +4 -1
- package/index.ts +1 -0
- package/package.json +8 -3
- package/schema/public/v1/observation-get-response.deserialize.schema.json +1 -13
- package/schema/public/v1/observation-get-response.serialize.schema.json +3 -17
- package/schema/public/v1/observation-snapshot.deserialize.schema.json +1 -13
- package/schema/public/v1/observation-snapshot.serialize.schema.json +3 -17
- package/schema/public/v1/spawn-list-page.deserialize.schema.json +7 -0
- package/schema/public/v1/spawn-list-page.serialize.schema.json +7 -0
package/extension/baseline.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { createRadarClient } from "./radar-client.ts";
|
|
4
|
+
import { createRadarChildren } from "./radar-children.ts";
|
|
4
5
|
import { createRadarHandoffPort } from "./radar-communication.ts";
|
|
5
6
|
import {
|
|
6
7
|
createRadarExecutionAdapter,
|
|
@@ -33,6 +34,7 @@ export default function registerPiRadar(pi: PiRadarHost): void {
|
|
|
33
34
|
const runtime = lazyRadarRuntime(async () => ({
|
|
34
35
|
publication: execution.publication,
|
|
35
36
|
communication: createRadarHandoffPort(client, execution.publication),
|
|
37
|
+
children: createRadarChildren(client, execution.publication),
|
|
36
38
|
execution,
|
|
37
39
|
}));
|
|
38
40
|
provideRadarRuntime(pi.events, runtime);
|
|
@@ -911,7 +911,6 @@ export interface ManagedSubject {
|
|
|
911
911
|
session: string | null;
|
|
912
912
|
state: State;
|
|
913
913
|
subject_id: string;
|
|
914
|
-
successor: string | null;
|
|
915
914
|
}
|
|
916
915
|
/**
|
|
917
916
|
* Exact spawn-edge child identity required by `spawn.close`.
|
|
@@ -1323,7 +1322,6 @@ export interface LifecycleObservation {
|
|
|
1323
1322
|
session: string | null;
|
|
1324
1323
|
state: State;
|
|
1325
1324
|
subject_id: string;
|
|
1326
|
-
successor: string | null;
|
|
1327
1325
|
}
|
|
1328
1326
|
/**
|
|
1329
1327
|
* A runtime pane: location plus optional display text.
|
|
@@ -1452,6 +1450,7 @@ export interface SpawnListPageSerializePublicSpawnTopology {
|
|
|
1452
1450
|
* nothing to verify.
|
|
1453
1451
|
*/
|
|
1454
1452
|
freshness?: SpawnListPageSerializeFreshness | null;
|
|
1453
|
+
granted_at?: string | null;
|
|
1455
1454
|
launched: SpawnListPageSerializeRequestOutcome;
|
|
1456
1455
|
location?: SpawnListPageSerializeRegistryLocation | null;
|
|
1457
1456
|
parent: string;
|
|
@@ -1461,6 +1460,7 @@ export interface SpawnListPageSerializePublicSpawnTopology {
|
|
|
1461
1460
|
request_id: string;
|
|
1462
1461
|
state: SpawnListPageSerializeSpawnState;
|
|
1463
1462
|
version: number;
|
|
1463
|
+
withdrawn?: SpawnListPageSerializeWithdrawn | null;
|
|
1464
1464
|
}
|
|
1465
1465
|
/**
|
|
1466
1466
|
* The child a token bound: the registering incarnation's own registry identity.
|
|
@@ -1476,6 +1476,11 @@ export interface SpawnListPageSerializeBoundChild {
|
|
|
1476
1476
|
*/
|
|
1477
1477
|
registration_id?: string | null;
|
|
1478
1478
|
source: string;
|
|
1479
|
+
/**
|
|
1480
|
+
* The durable agent the bound child attaches as. Absent for an edge without
|
|
1481
|
+
* a tracked launch and in records written before it was recorded.
|
|
1482
|
+
*/
|
|
1483
|
+
subject_id?: string | null;
|
|
1479
1484
|
}
|
|
1480
1485
|
/**
|
|
1481
1486
|
* Where an agent's incarnation lives on one backend, in that backend's own
|
|
@@ -1492,3 +1497,11 @@ export interface SpawnListPageSerializeRegistryLocation {
|
|
|
1492
1497
|
tab?: string | null;
|
|
1493
1498
|
workspace?: string | null;
|
|
1494
1499
|
}
|
|
1500
|
+
/**
|
|
1501
|
+
* Why and when a launch ended without ever presenting, so a reader never infers
|
|
1502
|
+
* failure from absence.
|
|
1503
|
+
*/
|
|
1504
|
+
export interface SpawnListPageSerializeWithdrawn {
|
|
1505
|
+
at: string;
|
|
1506
|
+
reason: string;
|
|
1507
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import type { RadarClient, RadarChannelSnapshot, RadarResult } from "./radar-client.ts";
|
|
3
|
+
import type { SpawnListPageSerializePublicSpawnTopology as ListedEdge } from "./generated-contracts.ts";
|
|
4
|
+
import { inspectRadarChild, type RadarInspection } from "./radar-inspection.ts";
|
|
5
|
+
import { observeRadarChild, waitForChild, launchFailure, type RadarChildObservation, type RadarLaunchOutcome } from "./radar-launch.ts";
|
|
6
|
+
import type { RadarPublication } from "./radar-publication.ts";
|
|
7
|
+
|
|
8
|
+
const PAGE = 100;
|
|
9
|
+
const MAX_PAGES = 20;
|
|
10
|
+
|
|
11
|
+
/** One child of the caller, named by the agent that survives its restarts. */
|
|
12
|
+
export type RadarChild = {
|
|
13
|
+
subject_id: string;
|
|
14
|
+
/** The launch that last bound this child. */
|
|
15
|
+
request_id: string;
|
|
16
|
+
pane_id?: string;
|
|
17
|
+
state: "unbound" | "bound" | "unresolved";
|
|
18
|
+
exited: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** The assignment a child is held to: published again when its process is replaced. */
|
|
22
|
+
export type RadarChildAssignment = {
|
|
23
|
+
update(snapshot: RadarChannelSnapshot): void;
|
|
24
|
+
retire(): void;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** The program a restarted child runs as. */
|
|
28
|
+
export type RadarChildProgram = {
|
|
29
|
+
executable: string;
|
|
30
|
+
argv: string[];
|
|
31
|
+
cwd?: string;
|
|
32
|
+
env?: Record<string, string>;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type RadarChildren = {
|
|
36
|
+
list(): Promise<RadarResult<RadarChild[]>>;
|
|
37
|
+
/**
|
|
38
|
+
* Replace this caller's live child with a new process in its own pane and
|
|
39
|
+
* session, and wait for the daemon to grant it or withdraw the launch.
|
|
40
|
+
*/
|
|
41
|
+
restart(
|
|
42
|
+
subject: string,
|
|
43
|
+
program: RadarChildProgram,
|
|
44
|
+
options?: { pollMs?: number; signal?: AbortSignal },
|
|
45
|
+
): Promise<RadarLaunchOutcome>;
|
|
46
|
+
assignment(subject: string, owner: string): RadarChildAssignment;
|
|
47
|
+
retire(subject: string): void;
|
|
48
|
+
observe(
|
|
49
|
+
subject: string,
|
|
50
|
+
session_id: string | undefined,
|
|
51
|
+
): Promise<RadarChildObservation>;
|
|
52
|
+
inspect(subject: string, session_id?: string): Promise<RadarInspection>;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
type Resolved = { child: RadarChild; registration_id: string };
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The caller's children, addressed by subject. A subject gains an edge per launch,
|
|
59
|
+
* so the newest bound edge is the current process; its registration is read per
|
|
60
|
+
* call and never remembered, because a remembered registration is the stale key
|
|
61
|
+
* this surface exists to hide. An edge bound without a subject is not addressable.
|
|
62
|
+
*/
|
|
63
|
+
export function createRadarChildren(
|
|
64
|
+
client: Pick<RadarClient, "spawnList" | "spawnGet" | "agentGet" | "output" | "processInfo" | "restart">,
|
|
65
|
+
publication: Pick<RadarPublication, "assignment" | "retireAssignment" | "subject">,
|
|
66
|
+
): RadarChildren {
|
|
67
|
+
const held = new Map<
|
|
68
|
+
string,
|
|
69
|
+
{ writer: RadarChildAssignment; published?: string; publish?: (target: Resolved) => void }
|
|
70
|
+
>();
|
|
71
|
+
|
|
72
|
+
async function resolveAll(): Promise<RadarResult<Map<string, Resolved & { at: number }>>> {
|
|
73
|
+
const newest = new Map<string, Resolved & { at: number }>();
|
|
74
|
+
let after: string | undefined;
|
|
75
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
76
|
+
const read = await client.spawnList({ ...(after !== undefined ? { after } : {}), limit: PAGE });
|
|
77
|
+
if (read.ok === false) return read;
|
|
78
|
+
for (const edge of read.value.spawns as ListedEdge[]) {
|
|
79
|
+
const bound = edge.bound;
|
|
80
|
+
if (!bound?.subject_id || !bound.registration_id || edge.granted_at == null || edge.withdrawn != null) continue;
|
|
81
|
+
const at = Date.parse(bound.bound_at);
|
|
82
|
+
if (Number.isNaN(at)) continue;
|
|
83
|
+
const current = newest.get(bound.subject_id);
|
|
84
|
+
if (current !== undefined && current.at >= at) continue;
|
|
85
|
+
newest.set(bound.subject_id, {
|
|
86
|
+
at,
|
|
87
|
+
registration_id: bound.registration_id,
|
|
88
|
+
child: {
|
|
89
|
+
subject_id: bound.subject_id,
|
|
90
|
+
request_id: edge.request_id,
|
|
91
|
+
...(edge.location?.pane ? { pane_id: edge.location.pane } : {}),
|
|
92
|
+
state: edge.state,
|
|
93
|
+
exited: edge.exit_observation != null && edge.exit_observation.state !== "unknown",
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (!read.value.next) return { ok: true, value: newest };
|
|
98
|
+
after = read.value.next;
|
|
99
|
+
}
|
|
100
|
+
return { ok: false, code: "protocol", message: "the caller's launches exceed the readable pages" };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function resolve(subject: string): Promise<Resolved | undefined> {
|
|
104
|
+
const all = await resolveAll();
|
|
105
|
+
return all.ok ? all.value.get(subject) : undefined;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function resolveAndFollow(subject: string): Promise<Resolved | undefined> {
|
|
109
|
+
const all = await resolveAll();
|
|
110
|
+
if (!all.ok) return undefined;
|
|
111
|
+
follow(all.value);
|
|
112
|
+
return all.value.get(subject);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* A child replaced under its subject has a new process with no assignment. Each
|
|
117
|
+
* read the caller already makes finds the new registration and moves the held
|
|
118
|
+
* assignment onto it, so no caller has to notice a replacement.
|
|
119
|
+
*/
|
|
120
|
+
function follow(all: Map<string, Resolved>): void {
|
|
121
|
+
for (const [subject, entry] of held) {
|
|
122
|
+
const target = all.get(subject);
|
|
123
|
+
if (target !== undefined && entry.published !== target.registration_id)
|
|
124
|
+
entry.publish?.(target);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
async list() {
|
|
130
|
+
const all = await resolveAll();
|
|
131
|
+
if (!all.ok) return all;
|
|
132
|
+
follow(all.value);
|
|
133
|
+
return { ok: true, value: [...all.value.values()].map((entry) => entry.child) };
|
|
134
|
+
},
|
|
135
|
+
async restart(subject, program, options) {
|
|
136
|
+
const parent = await publication.subject();
|
|
137
|
+
if (parent?.subject_id === undefined)
|
|
138
|
+
return { kind: "failed", message: "this process has no Radar registration to restart a child under" };
|
|
139
|
+
const request_id = randomUUID();
|
|
140
|
+
const requested = await client.restart({
|
|
141
|
+
request_id,
|
|
142
|
+
parent: parent.registration_id,
|
|
143
|
+
subject_id: subject,
|
|
144
|
+
...program,
|
|
145
|
+
});
|
|
146
|
+
if (requested.ok === false)
|
|
147
|
+
return launchFailure(requested, request_id);
|
|
148
|
+
const bound = await waitForChild(client, requested.value.launch_request_id, {
|
|
149
|
+
pollMs: options?.pollMs ?? 250,
|
|
150
|
+
...(options?.signal !== undefined ? { signal: options.signal } : {}),
|
|
151
|
+
});
|
|
152
|
+
if (bound.kind !== "bound") return bound;
|
|
153
|
+
await resolveAndFollow(subject);
|
|
154
|
+
return {
|
|
155
|
+
kind: "ready",
|
|
156
|
+
value: {
|
|
157
|
+
request_id: requested.value.launch_request_id,
|
|
158
|
+
subject_id: bound.subject_id,
|
|
159
|
+
pane_id: bound.pane_id,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
assignment(subject, owner) {
|
|
164
|
+
const existing = held.get(subject);
|
|
165
|
+
if (existing !== undefined) return existing.writer;
|
|
166
|
+
let latest: RadarChannelSnapshot | undefined;
|
|
167
|
+
const entry: {
|
|
168
|
+
writer: RadarChildAssignment;
|
|
169
|
+
published?: string;
|
|
170
|
+
publish?: (target: Resolved) => void;
|
|
171
|
+
} = { writer: undefined as unknown as RadarChildAssignment };
|
|
172
|
+
const send = (target: Resolved): void => {
|
|
173
|
+
if (latest === undefined) return;
|
|
174
|
+
if (entry.published !== undefined && entry.published !== target.registration_id)
|
|
175
|
+
publication.retireAssignment(entry.published);
|
|
176
|
+
entry.published = target.registration_id;
|
|
177
|
+
publication.assignment(target.registration_id, owner).update(latest);
|
|
178
|
+
};
|
|
179
|
+
entry.publish = send;
|
|
180
|
+
entry.writer = {
|
|
181
|
+
update(snapshot) {
|
|
182
|
+
latest = snapshot;
|
|
183
|
+
void resolve(subject).then((target) => {
|
|
184
|
+
if (target !== undefined) send(target);
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
retire() {
|
|
188
|
+
latest = undefined;
|
|
189
|
+
if (entry.published !== undefined) publication.retireAssignment(entry.published);
|
|
190
|
+
entry.published = undefined;
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
held.set(subject, entry);
|
|
194
|
+
return entry.writer;
|
|
195
|
+
},
|
|
196
|
+
retire(subject) {
|
|
197
|
+
held.get(subject)?.writer.retire();
|
|
198
|
+
held.delete(subject);
|
|
199
|
+
},
|
|
200
|
+
async observe(subject, session_id) {
|
|
201
|
+
const target = await resolveAndFollow(subject);
|
|
202
|
+
if (target === undefined)
|
|
203
|
+
return { kind: "unknown", diagnostic: "the daemon records no bound launch for this child" };
|
|
204
|
+
return observeRadarChild(client as RadarClient, {
|
|
205
|
+
request_id: target.child.request_id,
|
|
206
|
+
pane_id: target.child.pane_id,
|
|
207
|
+
registration_id: target.registration_id,
|
|
208
|
+
session_id,
|
|
209
|
+
});
|
|
210
|
+
},
|
|
211
|
+
async inspect(subject, session_id) {
|
|
212
|
+
const target = await resolveAndFollow(subject);
|
|
213
|
+
if (target?.child.pane_id === undefined)
|
|
214
|
+
throw new Error("Inspection target is not a bound child with a pane");
|
|
215
|
+
return inspectRadarChild(client as RadarClient, {
|
|
216
|
+
agentId: target.registration_id,
|
|
217
|
+
paneId: target.child.pane_id,
|
|
218
|
+
...(session_id !== undefined ? { session: session_id } : {}),
|
|
219
|
+
});
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
@@ -34,15 +34,18 @@ import type {
|
|
|
34
34
|
export const RADAR_PROTOCOL_VERSION = 2;
|
|
35
35
|
/**
|
|
36
36
|
* Revision 5 names attempts by claim_id and requires exact-claim controls.
|
|
37
|
+
* Revision 7 starts every managed launch through `radar run --launch` and lets a
|
|
38
|
+
* parent continue or restart its own child by subject.
|
|
37
39
|
* Earlier daemons expose different identity fields; require a coordinated update.
|
|
38
40
|
*/
|
|
39
|
-
export const RADAR_MINIMUM_REVISION =
|
|
41
|
+
export const RADAR_MINIMUM_REVISION = 8;
|
|
40
42
|
/** Public daemon contracts this client may preflight. */
|
|
41
43
|
export const RADAR_REGISTRY_CONTRACT = "registry.v1";
|
|
42
44
|
export const RADAR_REGISTRY_FACTS_CONTRACT = "registry.facts.v1";
|
|
43
45
|
export const RADAR_SPAWN_CONTRACT = "spawn.v1";
|
|
44
46
|
export const RADAR_SPAWN_LIST_CONTRACT = "spawn.list.v1";
|
|
45
47
|
export const RADAR_SPAWN_CLOSE_CONTRACT = "spawn.close.v1";
|
|
48
|
+
export const RADAR_LIFECYCLE_RESTART_CONTRACT = "lifecycle.restart.v1";
|
|
46
49
|
export const RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT = "spawn.launch_context.v1";
|
|
47
50
|
export const RADAR_EVENTS_CONTRACT = "events.v1";
|
|
48
51
|
export const RADAR_COMMUNICATION_CONTRACT = "communication.handoff.v1";
|
|
@@ -304,8 +307,11 @@ export type RadarSpawnEdge = {
|
|
|
304
307
|
created: RadarEffectOutcome;
|
|
305
308
|
launched: RadarEffectOutcome;
|
|
306
309
|
location?: RadarLocation;
|
|
307
|
-
bound?: { source: string; incarnation: string; registration_id?: string };
|
|
310
|
+
bound?: { source: string; incarnation: string; registration_id?: string; subject_id?: string };
|
|
308
311
|
exit_observation?: RadarSpawnExitObservation;
|
|
312
|
+
/** Set when the daemon withdrew the launch before its process presented. */
|
|
313
|
+
withdrawn?: { at: string; reason: string };
|
|
314
|
+
granted_at?: string;
|
|
309
315
|
};
|
|
310
316
|
|
|
311
317
|
/**
|
|
@@ -825,7 +831,11 @@ function spawnEdge(value: unknown): RadarResult<RadarSpawnEdge> {
|
|
|
825
831
|
const source = stringAt(bound, "source");
|
|
826
832
|
const incarnation = stringAt(bound, "incarnation");
|
|
827
833
|
const agentId = stringAt(bound, "registration_id");
|
|
834
|
+
const subjectId = stringAt(bound, "subject_id");
|
|
828
835
|
const exit = exitAt(recordAt(value, "exit_observation"));
|
|
836
|
+
const withdrawnRecord = recordAt(value, "withdrawn");
|
|
837
|
+
const withdrawnAt = stringAt(withdrawnRecord, "at");
|
|
838
|
+
const withdrawnReason = stringAt(withdrawnRecord, "reason");
|
|
829
839
|
return {
|
|
830
840
|
ok: true,
|
|
831
841
|
value: {
|
|
@@ -840,10 +850,15 @@ function spawnEdge(value: unknown): RadarResult<RadarSpawnEdge> {
|
|
|
840
850
|
source,
|
|
841
851
|
incarnation,
|
|
842
852
|
...(isUuid(agentId) ? { registration_id: agentId } : {}),
|
|
853
|
+
...(isUuid(subjectId) ? { subject_id: subjectId } : {}),
|
|
843
854
|
},
|
|
844
855
|
}
|
|
845
856
|
: {}),
|
|
846
857
|
...(exit !== undefined ? { exit_observation: exit } : {}),
|
|
858
|
+
...(stringAt(value, "granted_at") !== undefined ? { granted_at: stringAt(value, "granted_at") } : {}),
|
|
859
|
+
...(withdrawnAt !== undefined && withdrawnReason !== undefined
|
|
860
|
+
? { withdrawn: { at: withdrawnAt, reason: withdrawnReason } }
|
|
861
|
+
: {}),
|
|
847
862
|
},
|
|
848
863
|
};
|
|
849
864
|
}
|
|
@@ -959,9 +974,12 @@ function spawnRequestShape(request: {
|
|
|
959
974
|
cwd?: string;
|
|
960
975
|
env?: Record<string, string>;
|
|
961
976
|
managed?: RadarManagedLaunchMetadata;
|
|
977
|
+
continue_subject_id?: string;
|
|
962
978
|
}): string | undefined {
|
|
963
979
|
if (!isUuid(request.request_id))
|
|
964
980
|
return "spawn requires a canonical request UUID";
|
|
981
|
+
if (request.continue_subject_id !== undefined && !isUuid(request.continue_subject_id))
|
|
982
|
+
return "continue_subject_id must be a canonical UUID";
|
|
965
983
|
if (!isUuid(request.parent))
|
|
966
984
|
return "spawn requires the parent's canonical agent UUID";
|
|
967
985
|
if (!isUuid(request.parent_session_id))
|
|
@@ -1100,7 +1118,23 @@ export type RadarClient = {
|
|
|
1100
1118
|
cwd?: string;
|
|
1101
1119
|
env?: Record<string, string>;
|
|
1102
1120
|
managed?: RadarManagedLaunchMetadata;
|
|
1121
|
+
/** Start this released child agent again, in the session the daemon recorded for it. */
|
|
1122
|
+
continue_subject_id?: string;
|
|
1103
1123
|
}): Promise<RadarResult<RadarSpawnEdge>>;
|
|
1124
|
+
/**
|
|
1125
|
+
* Restart this caller's own live child under a new program. The daemon finds the
|
|
1126
|
+
* child's current process from the subject and replaces it in its own pane and
|
|
1127
|
+
* session; a program with a bare argument that may be a message is refused.
|
|
1128
|
+
*/
|
|
1129
|
+
restart(request: {
|
|
1130
|
+
request_id: string;
|
|
1131
|
+
parent: string;
|
|
1132
|
+
subject_id: string;
|
|
1133
|
+
executable: string;
|
|
1134
|
+
argv: string[];
|
|
1135
|
+
cwd?: string;
|
|
1136
|
+
env?: Record<string, string>;
|
|
1137
|
+
}): Promise<RadarResult<{ request_id: string; launch_request_id: string }>>;
|
|
1104
1138
|
/** List launches owned by this client's registered process. */
|
|
1105
1139
|
spawnList(request: Pick<SpawnListParamsDeserialize, "after" | "limit">): Promise<RadarResult<SpawnListPageSerialize>>;
|
|
1106
1140
|
spawnGet(request_id: string): Promise<RadarResult<RadarSpawnTopology>>;
|
|
@@ -1671,6 +1705,9 @@ export function createRadarClient(options: RadarClientOptions = {}): RadarClient
|
|
|
1671
1705
|
...(request.managed !== undefined
|
|
1672
1706
|
? { managed: request.managed }
|
|
1673
1707
|
: {}),
|
|
1708
|
+
...(request.continue_subject_id !== undefined
|
|
1709
|
+
? { continue_subject_id: request.continue_subject_id }
|
|
1710
|
+
: {}),
|
|
1674
1711
|
},
|
|
1675
1712
|
requester,
|
|
1676
1713
|
},
|
|
@@ -1706,6 +1743,37 @@ export function createRadarClient(options: RadarClientOptions = {}): RadarClient
|
|
|
1706
1743
|
},
|
|
1707
1744
|
);
|
|
1708
1745
|
},
|
|
1746
|
+
async restart(request) {
|
|
1747
|
+
if (!isUuid(request.request_id) || !isUuid(request.parent) || !isUuid(request.subject_id))
|
|
1748
|
+
return {
|
|
1749
|
+
ok: false,
|
|
1750
|
+
code: "bad_params",
|
|
1751
|
+
message: "restart requires canonical request, parent and subject UUIDs",
|
|
1752
|
+
};
|
|
1753
|
+
return withContract(RADAR_LIFECYCLE_RESTART_CONTRACT, async () => {
|
|
1754
|
+
const answer = await call(
|
|
1755
|
+
"lifecycle.restart",
|
|
1756
|
+
{
|
|
1757
|
+
request: {
|
|
1758
|
+
subject_id: request.subject_id,
|
|
1759
|
+
parent: request.parent,
|
|
1760
|
+
executable: request.executable,
|
|
1761
|
+
argv: request.argv,
|
|
1762
|
+
...(request.cwd !== undefined ? { cwd: request.cwd } : {}),
|
|
1763
|
+
...(request.env !== undefined ? { env: request.env } : {}),
|
|
1764
|
+
},
|
|
1765
|
+
requester,
|
|
1766
|
+
},
|
|
1767
|
+
request.request_id,
|
|
1768
|
+
);
|
|
1769
|
+
if (answer.ok === false) return answer;
|
|
1770
|
+
const settled = focusOutcome(recordAt(answer.value, "request"), request.request_id);
|
|
1771
|
+
if (settled.ok === false) return settled;
|
|
1772
|
+
const launch = recordAt(recordAt(answer.value, "request"), "launch_request_id");
|
|
1773
|
+
if (!isUuid(launch)) return protocol("lifecycle.restart reply names no launch");
|
|
1774
|
+
return { ok: true, value: { ...settled.value, launch_request_id: launch } };
|
|
1775
|
+
});
|
|
1776
|
+
},
|
|
1709
1777
|
async spawnList(request) {
|
|
1710
1778
|
const registrationId = processRegistrationId;
|
|
1711
1779
|
if (registrationId === undefined)
|