pi-radar 0.5.1 → 0.7.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 +76 -2
- package/dist/extension/baseline.js +45 -13
- package/dist/extension/radar-client.js +37 -10
- package/dist/extension/radar-execution.js +10 -4
- package/dist/extension/radar-publication.js +9 -3
- package/dist/index.js +47 -14
- package/extension/generated-contracts.ts +115 -8
- package/extension/radar-client.ts +42 -15
- package/extension/radar-execution.ts +5 -5
- package/extension/radar-publication.ts +11 -4
- package/package.json +1 -1
- package/schema/public/v1/handoff-receipt.deserialize.schema.json +2 -9
- package/schema/public/v1/handoff-receipt.serialize.schema.json +2 -9
- package/schema/public/v1/handoff.deserialize.schema.json +2 -9
- package/schema/public/v1/handoff.serialize.schema.json +2 -9
- package/schema/public/v1/observation-get-response.deserialize.schema.json +18 -6
- package/schema/public/v1/observation-get-response.serialize.schema.json +19 -7
- package/schema/public/v1/observation-snapshot.deserialize.schema.json +18 -6
- package/schema/public/v1/observation-snapshot.serialize.schema.json +19 -7
- package/schema/public/v1/presence-answer.deserialize.schema.json +1 -0
- package/schema/public/v1/presence-answer.serialize.schema.json +1 -0
- package/schema/public/v1/presence-report.deserialize.schema.json +0 -7
- package/schema/public/v1/presence-report.serialize.schema.json +0 -8
- package/schema/public/v1/spawn-list-page.deserialize.schema.json +291 -0
- package/schema/public/v1/spawn-list-page.serialize.schema.json +296 -0
- package/schema/public/v1/spawn-list-params.deserialize.schema.json +33 -0
- package/schema/public/v1/spawn-list-params.serialize.schema.json +38 -0
package/README.md
CHANGED
|
@@ -15,11 +15,17 @@ Focused subpaths are also exported: `pi-radar/client`, `pi-radar/communication`,
|
|
|
15
15
|
|
|
16
16
|
Installing `pi-radar` directly as a Pi package loads `extension/baseline.ts`, which creates one baseline publisher and exposes its lazy runtime handle through Pi's event bus. Herdsman must request that handle rather than starting another `RadarExecutionAdapter`. Do not load the baseline extension with an older Herdsman integration that still creates its own publisher; first migrate Herdsman to the runtime handle.
|
|
17
17
|
|
|
18
|
-
The baseline reports session presence through one flat request.
|
|
18
|
+
The baseline reports session presence through one flat request. The first managed `session_start` uses its pre-minted launch claim; subsequent session loads create fresh claims. Heartbeat/reconnect retries reuse the same claim. `publication.presence.management()` reports `managed`, `duplicate`, `pending` or `unmanaged`; `session_shutdown` awaits release of the exact claim. A duplicate remains rejected until a fresh Pi session load. `subject()` returns Radar's current subject only after a grant; no session marker is written to the Pi transcript.
|
|
19
19
|
|
|
20
20
|
## Daemon contract
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
**Managed execution:** a launch Radar admits arrives with `PI_RADAR_REGISTRATION_ID` in its environment. The baseline captures it, removes it from this process's environment before any child starts, and reports it as `registration_id`, so a managed launch binds the registration Radar already allocated instead of registering itself. A session claim is still fresh per load, and a reload or `/resume` takes a new one under the same registration; nothing about the process identity is presented or reused as a claim. `launch.admit` is the method `radar run` uses and is not part of the client surface. `agent.list` entries and observation agents carry `scoped`: `true` for a process Radar launched into a scope, `false` for an observed process it did not.
|
|
23
|
+
|
|
24
|
+
**0.7.0 upgrade:** update Radar (0.3.0) and pi-radar together; the floor is wire revision 6. A managed launch now carries `PI_RADAR_REGISTRATION_ID` instead of a claim id, and pi-radar always presents its own fresh claim. Nothing in your code changes unless you read the launch environment directly.
|
|
25
|
+
|
|
26
|
+
**0.6.0 upgrade:** update Radar and pi-radar together. Lifecycle controls require both `subject_id` and `claim_id`; handoff identities and spawn edges use `claim_id`, with no execution generation. Do not pass an agent's subject id where an attempt is required. Existing private launch records remain readable.
|
|
27
|
+
|
|
28
|
+
pi-radar 0.7.0 speaks protocol 2 and requires wire revision 6. Revision 6 gives a managed launch its process identity up front: Radar mints the `registration_id`, passes it as `PI_RADAR_REGISTRATION_ID`, and holds a `reserved` claim that grants nothing until the first verified report replaces it with the session's own claim, so a 0.6.0 client, which spends the spawn token as its first claim, cannot bind. Revision 4 added Pi presence (`agent.presence`/`agent.release`), which is how a session acquires its subject. Revision 5 names the agent `subject_id` and one attempt `claim_id`, drops the execution-generation ordinal, and requires lifecycle controls to name the claim they were issued against, so a revision-4 client would read those fields with the old meanings. Handoff methods additionally require the independently negotiated `communication.handoff.v1` contract; its timestamps remain exactly representable as JSON numbers. Clients that control managed executions must not target the legacy per-execution ID semantics. It reads `revision` and the public `contracts` list from `daemon.ping`, preserves unfamiliar contract names, and preflights only contracts needed for a multi-step or versioned operation. Backend `mux.*` primitives are private to the daemon; direct mux methods are dispatched and return the daemon's backend refusal when unavailable.
|
|
23
29
|
|
|
24
30
|
The client uses `registry.v1` for registry operations and additionally requires `registry.facts.v1` for the facts channel and assignment extensions. Managed children require `spawn.v1`, child-pane removal requires `spawn.close.v1`, event reads require `events.v1`, and handoff operations require `communication.handoff.v1`. The expected wire examples are maintained in this repository's `docs/control-plane.fixture.jsonl` and `docs/agent-registration.fixture.jsonl` and are exercised by the package tests.
|
|
25
31
|
|
|
@@ -41,6 +47,74 @@ Lifecycle execution generations are bounded to `u32` and remain exactly represen
|
|
|
41
47
|
|
|
42
48
|
Run `npm pack` to build the bundled runtime entrypoints before creating the tarball.
|
|
43
49
|
|
|
50
|
+
## Testing against this SDK
|
|
51
|
+
|
|
52
|
+
Fake the port, not the daemon. Every consumer-facing class here depends on a
|
|
53
|
+
narrow `Pick` of the client, so a test double implements a handful of methods and
|
|
54
|
+
no socket, framing, IDs, error envelope or durable state:
|
|
55
|
+
|
|
56
|
+
| Port | Methods | Where |
|
|
57
|
+
| --- | --- | --- |
|
|
58
|
+
| `RadarPublicationClient` | `ping`, `presence`, `releasePresence`, `acquire`, `publish`, `retire` | `pi-radar/publication` |
|
|
59
|
+
| `RadarHandoffPort` | `publish`, `inbox`, `get`, `ack` | `pi-radar/communication` |
|
|
60
|
+
| anything else | `Pick<RadarClient, ...>` of the reads you call | `pi-radar/client` |
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import type { RadarPublicationClient } from "pi-radar/publication";
|
|
64
|
+
import { createRadarPublication } from "pi-radar/publication";
|
|
65
|
+
|
|
66
|
+
const client: RadarPublicationClient = {
|
|
67
|
+
ping: async () => ({
|
|
68
|
+
ok: true,
|
|
69
|
+
value: {
|
|
70
|
+
protocol: 2, backend: "tmux", revision: 4, instance: null,
|
|
71
|
+
contracts: ["registry.v1"], tasks: { available: false, error: null },
|
|
72
|
+
},
|
|
73
|
+
}),
|
|
74
|
+
presence: async (report) => ({
|
|
75
|
+
ok: true,
|
|
76
|
+
value: {
|
|
77
|
+
registration_id: REGISTRATION,
|
|
78
|
+
claim_id: report.claim_id,
|
|
79
|
+
standing: "granted",
|
|
80
|
+
subject_id: SUBJECT,
|
|
81
|
+
},
|
|
82
|
+
}),
|
|
83
|
+
releasePresence: async (release) => ({
|
|
84
|
+
ok: true,
|
|
85
|
+
value: {
|
|
86
|
+
registration_id: release.registration_id,
|
|
87
|
+
claim_id: release.claim_id,
|
|
88
|
+
standing: "released",
|
|
89
|
+
},
|
|
90
|
+
}),
|
|
91
|
+
acquire: async () => ({
|
|
92
|
+
ok: true,
|
|
93
|
+
value: {
|
|
94
|
+
handle: "writer", source: "consumer", incarnation: INCARNATION,
|
|
95
|
+
generation: 1, sequence: 0,
|
|
96
|
+
},
|
|
97
|
+
}),
|
|
98
|
+
publish: async ({ sequence }) => ({ ok: true, value: { sequence } }),
|
|
99
|
+
retire: async () => ({ ok: true, value: undefined }),
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const publication = createRadarPublication({
|
|
103
|
+
dataRoot, client, registration: { source: "consumer", label: "lead" },
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
A socket-level fake daemon is a second implementation of Radar's protocol
|
|
108
|
+
maintained by someone who does not own it. It drifts silently: it keeps passing
|
|
109
|
+
while the real daemon refuses the same call, which is worse than having no fake
|
|
110
|
+
at all. That is not hypothetical — a consumer's fake still answered the retired
|
|
111
|
+
registration methods after the daemon had moved to presence, and its suite stayed
|
|
112
|
+
green through the whole contract change.
|
|
113
|
+
|
|
114
|
+
Wire-level proof belongs to one integration test against the real daemon (a
|
|
115
|
+
temporary socket path and state root are enough; `tests/agent_publisher.py` in
|
|
116
|
+
the Radar repository shows the shape), not to a fake every consumer re-implements.
|
|
117
|
+
|
|
44
118
|
## Releasing
|
|
45
119
|
|
|
46
120
|
Publishing is one tag push and it runs in CI; no npm token is stored anywhere.
|
|
@@ -8,10 +8,11 @@ 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 = 6;
|
|
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";
|
|
15
16
|
var RADAR_SPAWN_CLOSE_CONTRACT = "spawn.close.v1";
|
|
16
17
|
var RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT = "spawn.launch_context.v1";
|
|
17
18
|
var RADAR_EVENTS_CONTRACT = "events.v1";
|
|
@@ -118,7 +119,7 @@ function decodePresenceAnswer(value) {
|
|
|
118
119
|
const claimId = recordAt(value, "claim_id");
|
|
119
120
|
const standing = recordAt(value, "standing");
|
|
120
121
|
const subjectId = recordAt(value, "subject_id");
|
|
121
|
-
if (!isUuid(registrationId) || !isUuid(claimId) || !["pending", "granted", "duplicate", "released"].includes(String(standing)) || subjectId !== void 0 && !isUuid(subjectId) || standing === "granted" && !isUuid(subjectId))
|
|
122
|
+
if (!isUuid(registrationId) || !isUuid(claimId) || !["reserved", "pending", "granted", "duplicate", "released"].includes(String(standing)) || subjectId !== void 0 && !isUuid(subjectId) || standing === "granted" && !isUuid(subjectId))
|
|
122
123
|
return protocol("presence reply carries no valid claim standing");
|
|
123
124
|
return { ok: true, value };
|
|
124
125
|
}
|
|
@@ -146,22 +147,20 @@ function handoffAt(value) {
|
|
|
146
147
|
const version = numberAt(value, "version");
|
|
147
148
|
const id = stringAt(value, "id");
|
|
148
149
|
const sender = recordAt(value, "sender");
|
|
149
|
-
const senderGeneration = numberAt(sender, "execution_generation");
|
|
150
150
|
const target = stringAt(value, "recipient_subject_id");
|
|
151
151
|
const summary = stringAt(value, "summary");
|
|
152
152
|
const content = stringAt(value, "content");
|
|
153
153
|
const published = numberAt(value, "published_at_unix_ms");
|
|
154
154
|
const correlation = typeof value === "object" && value !== null ? value.correlation_id : void 0;
|
|
155
|
-
if (version !== 1 || !isUuid(id) || !isUuid(stringAt(sender, "registration_id")) || !isUuid(stringAt(sender, "session_id")) || !isUuid(stringAt(sender, "subject_id")) || !isUuid(stringAt(sender, "
|
|
155
|
+
if (version !== 1 || !isUuid(id) || !isUuid(stringAt(sender, "registration_id")) || !isUuid(stringAt(sender, "session_id")) || !isUuid(stringAt(sender, "subject_id")) || !isUuid(stringAt(sender, "claim_id")) || !isUuid(target) || summary === void 0 || content === void 0 || published === void 0 || !Number.isSafeInteger(published) || published < 0 || correlation !== void 0 && correlation !== null && (typeof correlation !== "string" || correlation.length === 0 || Buffer.byteLength(correlation, "utf8") > 256 || new RegExp("\\p{Cc}", "u").test(correlation))) return void 0;
|
|
156
156
|
return value;
|
|
157
157
|
}
|
|
158
158
|
function handoffReceiptAt(value) {
|
|
159
159
|
const version = numberAt(value, "version");
|
|
160
160
|
const handoffId = stringAt(value, "handoff_id");
|
|
161
161
|
const recipient = recordAt(value, "recipient");
|
|
162
|
-
const generation = numberAt(recipient, "execution_generation");
|
|
163
162
|
const received = numberAt(value, "received_at_unix_ms");
|
|
164
|
-
if (version !== 1 || !isUuid(handoffId) || !isUuid(stringAt(recipient, "registration_id")) || !isUuid(stringAt(recipient, "session_id")) || !isUuid(stringAt(recipient, "subject_id")) || !isUuid(stringAt(recipient, "
|
|
163
|
+
if (version !== 1 || !isUuid(handoffId) || !isUuid(stringAt(recipient, "registration_id")) || !isUuid(stringAt(recipient, "session_id")) || !isUuid(stringAt(recipient, "subject_id")) || !isUuid(stringAt(recipient, "claim_id")) || received === void 0 || !Number.isSafeInteger(received) || received < 0) return void 0;
|
|
165
164
|
return value;
|
|
166
165
|
}
|
|
167
166
|
function writerBinding(value) {
|
|
@@ -265,7 +264,7 @@ function unresolvedOperation(requestId, detail) {
|
|
|
265
264
|
message: `focus ${requestId} is unresolved (${detail})`
|
|
266
265
|
};
|
|
267
266
|
}
|
|
268
|
-
var
|
|
267
|
+
var RADAR_REGISTRATION_ENV = "PI_RADAR_REGISTRATION_ID";
|
|
269
268
|
var RADAR_MANAGED_IDENTITY_ENV = /* @__PURE__ */ new Set([
|
|
270
269
|
"PI_RADAR_MANAGED_SUBJECT_ID",
|
|
271
270
|
"PI_RADAR_MANAGED_LAUNCH_ID",
|
|
@@ -414,8 +413,8 @@ function spawnRequestShape(request) {
|
|
|
414
413
|
const env = Object.entries(request.env ?? {});
|
|
415
414
|
if (env.length > 256) return "spawn env names more than 256 entries";
|
|
416
415
|
for (const [name, value] of env) {
|
|
417
|
-
if (name ===
|
|
418
|
-
return `spawn env cannot set the reserved ${
|
|
416
|
+
if (name === RADAR_REGISTRATION_ENV)
|
|
417
|
+
return `spawn env cannot set the reserved ${RADAR_REGISTRATION_ENV}`;
|
|
419
418
|
if (RADAR_MANAGED_IDENTITY_ENV.has(name))
|
|
420
419
|
return `spawn env cannot set Radar-issued identity variable ${name}`;
|
|
421
420
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || new RegExp("\\p{Cc}", "u").test(value))
|
|
@@ -621,6 +620,7 @@ function createRadarClient(options = {}) {
|
|
|
621
620
|
};
|
|
622
621
|
return answer;
|
|
623
622
|
};
|
|
623
|
+
let processRegistrationId;
|
|
624
624
|
let handshake;
|
|
625
625
|
const negotiated = async () => {
|
|
626
626
|
handshake ??= probe();
|
|
@@ -702,6 +702,7 @@ function createRadarClient(options = {}) {
|
|
|
702
702
|
const record = answer.value.registration;
|
|
703
703
|
const agentId = stringAt(record, "registration_id");
|
|
704
704
|
if (!isUuid(agentId)) return protocol("registration reply carries no registration_id");
|
|
705
|
+
processRegistrationId = agentId;
|
|
705
706
|
return { ok: true, value: { registration_id: agentId } };
|
|
706
707
|
});
|
|
707
708
|
},
|
|
@@ -835,6 +836,31 @@ function createRadarClient(options = {}) {
|
|
|
835
836
|
}
|
|
836
837
|
);
|
|
837
838
|
},
|
|
839
|
+
async spawnList(request) {
|
|
840
|
+
const registrationId = processRegistrationId;
|
|
841
|
+
if (registrationId === void 0)
|
|
842
|
+
return { ok: false, code: "refused", message: "spawn.list requires this client's process registration" };
|
|
843
|
+
return withContract(RADAR_SPAWN_LIST_CONTRACT, async () => {
|
|
844
|
+
const params = {
|
|
845
|
+
registration_id: registrationId,
|
|
846
|
+
...request.after !== void 0 ? { after: request.after } : {},
|
|
847
|
+
...request.limit !== void 0 ? { limit: request.limit } : {}
|
|
848
|
+
};
|
|
849
|
+
const answer = await call("spawn.list", { ...params });
|
|
850
|
+
if (answer.ok === false) return answer;
|
|
851
|
+
const spawns = recordAt(answer.value, "spawns");
|
|
852
|
+
const next = recordAt(answer.value, "next");
|
|
853
|
+
if (!Array.isArray(spawns) || next != null && !isUuid(next))
|
|
854
|
+
return protocol("spawn.list reply carries no valid page");
|
|
855
|
+
for (const spawn of spawns) {
|
|
856
|
+
const decoded = spawnTopology(spawn);
|
|
857
|
+
if (decoded.ok === false) return decoded;
|
|
858
|
+
if (typeof recordAt(spawn, "version") !== "number" || typeof recordAt(spawn, "recorded_at") !== "string" || !["unbound", "bound", "unresolved"].includes(String(recordAt(spawn, "state"))))
|
|
859
|
+
return protocol("spawn.list reply carries an incomplete topology");
|
|
860
|
+
}
|
|
861
|
+
return { ok: true, value: answer.value };
|
|
862
|
+
});
|
|
863
|
+
},
|
|
838
864
|
async spawnGet(requestId) {
|
|
839
865
|
if (!isUuid(requestId))
|
|
840
866
|
return {
|
|
@@ -1392,7 +1418,13 @@ function createRadarProcessSlot() {
|
|
|
1392
1418
|
}
|
|
1393
1419
|
function radarProcessSlot() {
|
|
1394
1420
|
const host = globalThis;
|
|
1395
|
-
|
|
1421
|
+
if (!host[PROCESS_SLOT]) {
|
|
1422
|
+
const slot = createRadarProcessSlot();
|
|
1423
|
+
slot.launchRegistrationId = process.env.PI_RADAR_REGISTRATION_ID;
|
|
1424
|
+
delete process.env.PI_RADAR_REGISTRATION_ID;
|
|
1425
|
+
host[PROCESS_SLOT] = slot;
|
|
1426
|
+
}
|
|
1427
|
+
return host[PROCESS_SLOT];
|
|
1396
1428
|
}
|
|
1397
1429
|
function radarProcessIncarnation(slot = radarProcessSlot()) {
|
|
1398
1430
|
return slot.incarnation ??= randomUUID2();
|
|
@@ -1943,8 +1975,8 @@ function createRadarPublicationInner(options) {
|
|
|
1943
1975
|
const store = options.store ?? createPublicationStore(dataRoot);
|
|
1944
1976
|
const scheduler = options.scheduler ?? systemScheduler;
|
|
1945
1977
|
const client = options.client;
|
|
1946
|
-
const spawnToken = options.spawnToken;
|
|
1947
1978
|
const slot = options.slot ?? radarProcessSlot();
|
|
1979
|
+
const launchRegistrationId = options.launchRegistrationId ?? slot.launchRegistrationId;
|
|
1948
1980
|
const incarnation = options.incarnation ?? radarProcessIncarnation(slot);
|
|
1949
1981
|
const cacheKey = `${options.store === void 0 ? dataRoot : storeIdentity(options.store)}\0${incarnation}`;
|
|
1950
1982
|
const cached = slot.publications.get(cacheKey);
|
|
@@ -2068,7 +2100,7 @@ function createRadarPublicationInner(options) {
|
|
|
2068
2100
|
return {
|
|
2069
2101
|
...identity,
|
|
2070
2102
|
...location?.state === "resolved" ? { location: location.location } : {},
|
|
2071
|
-
...
|
|
2103
|
+
...launchRegistrationId ? { registration_id: launchRegistrationId } : {}
|
|
2072
2104
|
};
|
|
2073
2105
|
},
|
|
2074
2106
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
@@ -2255,7 +2287,7 @@ function createRadarExecutionAdapter(pi, options) {
|
|
|
2255
2287
|
},
|
|
2256
2288
|
...options.location !== void 0 ? { location: options.location } : {},
|
|
2257
2289
|
...managed !== void 0 ? { binding: managed } : {},
|
|
2258
|
-
...options.
|
|
2290
|
+
...options.launchRegistrationId !== void 0 ? { launchRegistrationId: options.launchRegistrationId } : {}
|
|
2259
2291
|
});
|
|
2260
2292
|
} catch {
|
|
2261
2293
|
publication = unavailablePublication2();
|
|
@@ -4,10 +4,11 @@ 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 = 6;
|
|
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";
|
|
11
12
|
var RADAR_SPAWN_CLOSE_CONTRACT = "spawn.close.v1";
|
|
12
13
|
var RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT = "spawn.launch_context.v1";
|
|
13
14
|
var RADAR_EVENTS_CONTRACT = "events.v1";
|
|
@@ -114,7 +115,7 @@ function decodePresenceAnswer(value) {
|
|
|
114
115
|
const claimId = recordAt(value, "claim_id");
|
|
115
116
|
const standing = recordAt(value, "standing");
|
|
116
117
|
const subjectId = recordAt(value, "subject_id");
|
|
117
|
-
if (!isUuid(registrationId) || !isUuid(claimId) || !["pending", "granted", "duplicate", "released"].includes(String(standing)) || subjectId !== void 0 && !isUuid(subjectId) || standing === "granted" && !isUuid(subjectId))
|
|
118
|
+
if (!isUuid(registrationId) || !isUuid(claimId) || !["reserved", "pending", "granted", "duplicate", "released"].includes(String(standing)) || subjectId !== void 0 && !isUuid(subjectId) || standing === "granted" && !isUuid(subjectId))
|
|
118
119
|
return protocol("presence reply carries no valid claim standing");
|
|
119
120
|
return { ok: true, value };
|
|
120
121
|
}
|
|
@@ -142,22 +143,20 @@ function handoffAt(value) {
|
|
|
142
143
|
const version = numberAt(value, "version");
|
|
143
144
|
const id = stringAt(value, "id");
|
|
144
145
|
const sender = recordAt(value, "sender");
|
|
145
|
-
const senderGeneration = numberAt(sender, "execution_generation");
|
|
146
146
|
const target = stringAt(value, "recipient_subject_id");
|
|
147
147
|
const summary = stringAt(value, "summary");
|
|
148
148
|
const content = stringAt(value, "content");
|
|
149
149
|
const published = numberAt(value, "published_at_unix_ms");
|
|
150
150
|
const correlation = typeof value === "object" && value !== null ? value.correlation_id : void 0;
|
|
151
|
-
if (version !== 1 || !isUuid(id) || !isUuid(stringAt(sender, "registration_id")) || !isUuid(stringAt(sender, "session_id")) || !isUuid(stringAt(sender, "subject_id")) || !isUuid(stringAt(sender, "
|
|
151
|
+
if (version !== 1 || !isUuid(id) || !isUuid(stringAt(sender, "registration_id")) || !isUuid(stringAt(sender, "session_id")) || !isUuid(stringAt(sender, "subject_id")) || !isUuid(stringAt(sender, "claim_id")) || !isUuid(target) || summary === void 0 || content === void 0 || published === void 0 || !Number.isSafeInteger(published) || published < 0 || correlation !== void 0 && correlation !== null && (typeof correlation !== "string" || correlation.length === 0 || Buffer.byteLength(correlation, "utf8") > 256 || new RegExp("\\p{Cc}", "u").test(correlation))) return void 0;
|
|
152
152
|
return value;
|
|
153
153
|
}
|
|
154
154
|
function handoffReceiptAt(value) {
|
|
155
155
|
const version = numberAt(value, "version");
|
|
156
156
|
const handoffId = stringAt(value, "handoff_id");
|
|
157
157
|
const recipient = recordAt(value, "recipient");
|
|
158
|
-
const generation = numberAt(recipient, "execution_generation");
|
|
159
158
|
const received = numberAt(value, "received_at_unix_ms");
|
|
160
|
-
if (version !== 1 || !isUuid(handoffId) || !isUuid(stringAt(recipient, "registration_id")) || !isUuid(stringAt(recipient, "session_id")) || !isUuid(stringAt(recipient, "subject_id")) || !isUuid(stringAt(recipient, "
|
|
159
|
+
if (version !== 1 || !isUuid(handoffId) || !isUuid(stringAt(recipient, "registration_id")) || !isUuid(stringAt(recipient, "session_id")) || !isUuid(stringAt(recipient, "subject_id")) || !isUuid(stringAt(recipient, "claim_id")) || received === void 0 || !Number.isSafeInteger(received) || received < 0) return void 0;
|
|
161
160
|
return value;
|
|
162
161
|
}
|
|
163
162
|
function writerBinding(value) {
|
|
@@ -265,7 +264,7 @@ function unresolvedOperation(requestId, detail) {
|
|
|
265
264
|
message: `focus ${requestId} is unresolved (${detail})`
|
|
266
265
|
};
|
|
267
266
|
}
|
|
268
|
-
var
|
|
267
|
+
var RADAR_REGISTRATION_ENV = "PI_RADAR_REGISTRATION_ID";
|
|
269
268
|
var RADAR_MANAGED_IDENTITY_ENV = /* @__PURE__ */ new Set([
|
|
270
269
|
"PI_RADAR_MANAGED_SUBJECT_ID",
|
|
271
270
|
"PI_RADAR_MANAGED_LAUNCH_ID",
|
|
@@ -414,8 +413,8 @@ function spawnRequestShape(request) {
|
|
|
414
413
|
const env = Object.entries(request.env ?? {});
|
|
415
414
|
if (env.length > 256) return "spawn env names more than 256 entries";
|
|
416
415
|
for (const [name, value] of env) {
|
|
417
|
-
if (name ===
|
|
418
|
-
return `spawn env cannot set the reserved ${
|
|
416
|
+
if (name === RADAR_REGISTRATION_ENV)
|
|
417
|
+
return `spawn env cannot set the reserved ${RADAR_REGISTRATION_ENV}`;
|
|
419
418
|
if (RADAR_MANAGED_IDENTITY_ENV.has(name))
|
|
420
419
|
return `spawn env cannot set Radar-issued identity variable ${name}`;
|
|
421
420
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || new RegExp("\\p{Cc}", "u").test(value))
|
|
@@ -679,6 +678,7 @@ function createRadarClient(options = {}) {
|
|
|
679
678
|
};
|
|
680
679
|
return answer;
|
|
681
680
|
};
|
|
681
|
+
let processRegistrationId;
|
|
682
682
|
let handshake;
|
|
683
683
|
const negotiated = async () => {
|
|
684
684
|
handshake ??= probe();
|
|
@@ -760,6 +760,7 @@ function createRadarClient(options = {}) {
|
|
|
760
760
|
const record = answer.value.registration;
|
|
761
761
|
const agentId = stringAt(record, "registration_id");
|
|
762
762
|
if (!isUuid(agentId)) return protocol("registration reply carries no registration_id");
|
|
763
|
+
processRegistrationId = agentId;
|
|
763
764
|
return { ok: true, value: { registration_id: agentId } };
|
|
764
765
|
});
|
|
765
766
|
},
|
|
@@ -893,6 +894,31 @@ function createRadarClient(options = {}) {
|
|
|
893
894
|
}
|
|
894
895
|
);
|
|
895
896
|
},
|
|
897
|
+
async spawnList(request) {
|
|
898
|
+
const registrationId = processRegistrationId;
|
|
899
|
+
if (registrationId === void 0)
|
|
900
|
+
return { ok: false, code: "refused", message: "spawn.list requires this client's process registration" };
|
|
901
|
+
return withContract(RADAR_SPAWN_LIST_CONTRACT, async () => {
|
|
902
|
+
const params = {
|
|
903
|
+
registration_id: registrationId,
|
|
904
|
+
...request.after !== void 0 ? { after: request.after } : {},
|
|
905
|
+
...request.limit !== void 0 ? { limit: request.limit } : {}
|
|
906
|
+
};
|
|
907
|
+
const answer = await call("spawn.list", { ...params });
|
|
908
|
+
if (answer.ok === false) return answer;
|
|
909
|
+
const spawns = recordAt(answer.value, "spawns");
|
|
910
|
+
const next = recordAt(answer.value, "next");
|
|
911
|
+
if (!Array.isArray(spawns) || next != null && !isUuid(next))
|
|
912
|
+
return protocol("spawn.list reply carries no valid page");
|
|
913
|
+
for (const spawn of spawns) {
|
|
914
|
+
const decoded = spawnTopology(spawn);
|
|
915
|
+
if (decoded.ok === false) return decoded;
|
|
916
|
+
if (typeof recordAt(spawn, "version") !== "number" || typeof recordAt(spawn, "recorded_at") !== "string" || !["unbound", "bound", "unresolved"].includes(String(recordAt(spawn, "state"))))
|
|
917
|
+
return protocol("spawn.list reply carries an incomplete topology");
|
|
918
|
+
}
|
|
919
|
+
return { ok: true, value: answer.value };
|
|
920
|
+
});
|
|
921
|
+
},
|
|
896
922
|
async spawnGet(requestId) {
|
|
897
923
|
if (!isUuid(requestId))
|
|
898
924
|
return {
|
|
@@ -1244,13 +1270,14 @@ export {
|
|
|
1244
1270
|
RADAR_MAX_LINE_BYTES,
|
|
1245
1271
|
RADAR_MINIMUM_REVISION,
|
|
1246
1272
|
RADAR_PROTOCOL_VERSION,
|
|
1273
|
+
RADAR_REGISTRATION_ENV,
|
|
1247
1274
|
RADAR_REGISTRY_CONTRACT,
|
|
1248
1275
|
RADAR_REGISTRY_FACTS_CONTRACT,
|
|
1249
1276
|
RADAR_REQUESTER,
|
|
1250
1277
|
RADAR_SPAWN_CLOSE_CONTRACT,
|
|
1251
1278
|
RADAR_SPAWN_CONTRACT,
|
|
1252
1279
|
RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT,
|
|
1253
|
-
|
|
1280
|
+
RADAR_SPAWN_LIST_CONTRACT,
|
|
1254
1281
|
boundedRadarAssignment,
|
|
1255
1282
|
boundedRadarFacts,
|
|
1256
1283
|
boundedRadarSnapshot,
|
|
@@ -272,7 +272,13 @@ function createRadarProcessSlot() {
|
|
|
272
272
|
}
|
|
273
273
|
function radarProcessSlot() {
|
|
274
274
|
const host = globalThis;
|
|
275
|
-
|
|
275
|
+
if (!host[PROCESS_SLOT]) {
|
|
276
|
+
const slot = createRadarProcessSlot();
|
|
277
|
+
slot.launchRegistrationId = process.env.PI_RADAR_REGISTRATION_ID;
|
|
278
|
+
delete process.env.PI_RADAR_REGISTRATION_ID;
|
|
279
|
+
host[PROCESS_SLOT] = slot;
|
|
280
|
+
}
|
|
281
|
+
return host[PROCESS_SLOT];
|
|
276
282
|
}
|
|
277
283
|
function radarProcessIncarnation(slot = radarProcessSlot()) {
|
|
278
284
|
return slot.incarnation ??= randomUUID();
|
|
@@ -823,8 +829,8 @@ function createRadarPublicationInner(options) {
|
|
|
823
829
|
const store = options.store ?? createPublicationStore(dataRoot);
|
|
824
830
|
const scheduler = options.scheduler ?? systemScheduler;
|
|
825
831
|
const client = options.client;
|
|
826
|
-
const spawnToken = options.spawnToken;
|
|
827
832
|
const slot = options.slot ?? radarProcessSlot();
|
|
833
|
+
const launchRegistrationId = options.launchRegistrationId ?? slot.launchRegistrationId;
|
|
828
834
|
const incarnation = options.incarnation ?? radarProcessIncarnation(slot);
|
|
829
835
|
const cacheKey = `${options.store === void 0 ? dataRoot : storeIdentity(options.store)}\0${incarnation}`;
|
|
830
836
|
const cached = slot.publications.get(cacheKey);
|
|
@@ -948,7 +954,7 @@ function createRadarPublicationInner(options) {
|
|
|
948
954
|
return {
|
|
949
955
|
...identity,
|
|
950
956
|
...location?.state === "resolved" ? { location: location.location } : {},
|
|
951
|
-
...
|
|
957
|
+
...launchRegistrationId ? { registration_id: launchRegistrationId } : {}
|
|
952
958
|
};
|
|
953
959
|
},
|
|
954
960
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
@@ -1141,7 +1147,7 @@ function createRadarExecutionAdapter(pi, options) {
|
|
|
1141
1147
|
},
|
|
1142
1148
|
...options.location !== void 0 ? { location: options.location } : {},
|
|
1143
1149
|
...managed !== void 0 ? { binding: managed } : {},
|
|
1144
|
-
...options.
|
|
1150
|
+
...options.launchRegistrationId !== void 0 ? { launchRegistrationId: options.launchRegistrationId } : {}
|
|
1145
1151
|
});
|
|
1146
1152
|
} catch {
|
|
1147
1153
|
publication = unavailablePublication2();
|
|
@@ -274,7 +274,13 @@ function createRadarProcessSlot() {
|
|
|
274
274
|
}
|
|
275
275
|
function radarProcessSlot() {
|
|
276
276
|
const host = globalThis;
|
|
277
|
-
|
|
277
|
+
if (!host[PROCESS_SLOT]) {
|
|
278
|
+
const slot = createRadarProcessSlot();
|
|
279
|
+
slot.launchRegistrationId = process.env.PI_RADAR_REGISTRATION_ID;
|
|
280
|
+
delete process.env.PI_RADAR_REGISTRATION_ID;
|
|
281
|
+
host[PROCESS_SLOT] = slot;
|
|
282
|
+
}
|
|
283
|
+
return host[PROCESS_SLOT];
|
|
278
284
|
}
|
|
279
285
|
function radarProcessIncarnation(slot = radarProcessSlot()) {
|
|
280
286
|
return slot.incarnation ??= randomUUID();
|
|
@@ -828,8 +834,8 @@ function createRadarPublicationInner(options) {
|
|
|
828
834
|
const store = options.store ?? createPublicationStore(dataRoot);
|
|
829
835
|
const scheduler = options.scheduler ?? systemScheduler;
|
|
830
836
|
const client = options.client;
|
|
831
|
-
const spawnToken = options.spawnToken;
|
|
832
837
|
const slot = options.slot ?? radarProcessSlot();
|
|
838
|
+
const launchRegistrationId = options.launchRegistrationId ?? slot.launchRegistrationId;
|
|
833
839
|
const incarnation = options.incarnation ?? radarProcessIncarnation(slot);
|
|
834
840
|
const cacheKey = `${options.store === void 0 ? dataRoot : storeIdentity(options.store)}\0${incarnation}`;
|
|
835
841
|
const cached = slot.publications.get(cacheKey);
|
|
@@ -953,7 +959,7 @@ function createRadarPublicationInner(options) {
|
|
|
953
959
|
return {
|
|
954
960
|
...identity,
|
|
955
961
|
...location?.state === "resolved" ? { location: location.location } : {},
|
|
956
|
-
...
|
|
962
|
+
...launchRegistrationId ? { registration_id: launchRegistrationId } : {}
|
|
957
963
|
};
|
|
958
964
|
},
|
|
959
965
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
package/dist/index.js
CHANGED
|
@@ -16,13 +16,14 @@ __export(radar_client_exports, {
|
|
|
16
16
|
RADAR_MAX_LINE_BYTES: () => RADAR_MAX_LINE_BYTES,
|
|
17
17
|
RADAR_MINIMUM_REVISION: () => RADAR_MINIMUM_REVISION,
|
|
18
18
|
RADAR_PROTOCOL_VERSION: () => RADAR_PROTOCOL_VERSION,
|
|
19
|
+
RADAR_REGISTRATION_ENV: () => RADAR_REGISTRATION_ENV,
|
|
19
20
|
RADAR_REGISTRY_CONTRACT: () => RADAR_REGISTRY_CONTRACT,
|
|
20
21
|
RADAR_REGISTRY_FACTS_CONTRACT: () => RADAR_REGISTRY_FACTS_CONTRACT,
|
|
21
22
|
RADAR_REQUESTER: () => RADAR_REQUESTER,
|
|
22
23
|
RADAR_SPAWN_CLOSE_CONTRACT: () => RADAR_SPAWN_CLOSE_CONTRACT,
|
|
23
24
|
RADAR_SPAWN_CONTRACT: () => RADAR_SPAWN_CONTRACT,
|
|
24
25
|
RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT: () => RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT,
|
|
25
|
-
|
|
26
|
+
RADAR_SPAWN_LIST_CONTRACT: () => RADAR_SPAWN_LIST_CONTRACT,
|
|
26
27
|
boundedRadarAssignment: () => boundedRadarAssignment,
|
|
27
28
|
boundedRadarFacts: () => boundedRadarFacts,
|
|
28
29
|
boundedRadarSnapshot: () => boundedRadarSnapshot,
|
|
@@ -39,10 +40,11 @@ import { lstatSync } from "node:fs";
|
|
|
39
40
|
import { createConnection } from "node:net";
|
|
40
41
|
import { dirname, join } from "node:path";
|
|
41
42
|
var RADAR_PROTOCOL_VERSION = 2;
|
|
42
|
-
var RADAR_MINIMUM_REVISION =
|
|
43
|
+
var RADAR_MINIMUM_REVISION = 6;
|
|
43
44
|
var RADAR_REGISTRY_CONTRACT = "registry.v1";
|
|
44
45
|
var RADAR_REGISTRY_FACTS_CONTRACT = "registry.facts.v1";
|
|
45
46
|
var RADAR_SPAWN_CONTRACT = "spawn.v1";
|
|
47
|
+
var RADAR_SPAWN_LIST_CONTRACT = "spawn.list.v1";
|
|
46
48
|
var RADAR_SPAWN_CLOSE_CONTRACT = "spawn.close.v1";
|
|
47
49
|
var RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT = "spawn.launch_context.v1";
|
|
48
50
|
var RADAR_EVENTS_CONTRACT = "events.v1";
|
|
@@ -149,7 +151,7 @@ function decodePresenceAnswer(value) {
|
|
|
149
151
|
const claimId = recordAt(value, "claim_id");
|
|
150
152
|
const standing = recordAt(value, "standing");
|
|
151
153
|
const subjectId = recordAt(value, "subject_id");
|
|
152
|
-
if (!isUuid(registrationId) || !isUuid(claimId) || !["pending", "granted", "duplicate", "released"].includes(String(standing)) || subjectId !== void 0 && !isUuid(subjectId) || standing === "granted" && !isUuid(subjectId))
|
|
154
|
+
if (!isUuid(registrationId) || !isUuid(claimId) || !["reserved", "pending", "granted", "duplicate", "released"].includes(String(standing)) || subjectId !== void 0 && !isUuid(subjectId) || standing === "granted" && !isUuid(subjectId))
|
|
153
155
|
return protocol("presence reply carries no valid claim standing");
|
|
154
156
|
return { ok: true, value };
|
|
155
157
|
}
|
|
@@ -177,22 +179,20 @@ function handoffAt(value) {
|
|
|
177
179
|
const version = numberAt(value, "version");
|
|
178
180
|
const id = stringAt(value, "id");
|
|
179
181
|
const sender = recordAt(value, "sender");
|
|
180
|
-
const senderGeneration = numberAt(sender, "execution_generation");
|
|
181
182
|
const target = stringAt(value, "recipient_subject_id");
|
|
182
183
|
const summary = stringAt(value, "summary");
|
|
183
184
|
const content = stringAt(value, "content");
|
|
184
185
|
const published = numberAt(value, "published_at_unix_ms");
|
|
185
186
|
const correlation = typeof value === "object" && value !== null ? value.correlation_id : void 0;
|
|
186
|
-
if (version !== 1 || !isUuid(id) || !isUuid(stringAt(sender, "registration_id")) || !isUuid(stringAt(sender, "session_id")) || !isUuid(stringAt(sender, "subject_id")) || !isUuid(stringAt(sender, "
|
|
187
|
+
if (version !== 1 || !isUuid(id) || !isUuid(stringAt(sender, "registration_id")) || !isUuid(stringAt(sender, "session_id")) || !isUuid(stringAt(sender, "subject_id")) || !isUuid(stringAt(sender, "claim_id")) || !isUuid(target) || summary === void 0 || content === void 0 || published === void 0 || !Number.isSafeInteger(published) || published < 0 || correlation !== void 0 && correlation !== null && (typeof correlation !== "string" || correlation.length === 0 || Buffer.byteLength(correlation, "utf8") > 256 || new RegExp("\\p{Cc}", "u").test(correlation))) return void 0;
|
|
187
188
|
return value;
|
|
188
189
|
}
|
|
189
190
|
function handoffReceiptAt(value) {
|
|
190
191
|
const version = numberAt(value, "version");
|
|
191
192
|
const handoffId = stringAt(value, "handoff_id");
|
|
192
193
|
const recipient = recordAt(value, "recipient");
|
|
193
|
-
const generation = numberAt(recipient, "execution_generation");
|
|
194
194
|
const received = numberAt(value, "received_at_unix_ms");
|
|
195
|
-
if (version !== 1 || !isUuid(handoffId) || !isUuid(stringAt(recipient, "registration_id")) || !isUuid(stringAt(recipient, "session_id")) || !isUuid(stringAt(recipient, "subject_id")) || !isUuid(stringAt(recipient, "
|
|
195
|
+
if (version !== 1 || !isUuid(handoffId) || !isUuid(stringAt(recipient, "registration_id")) || !isUuid(stringAt(recipient, "session_id")) || !isUuid(stringAt(recipient, "subject_id")) || !isUuid(stringAt(recipient, "claim_id")) || received === void 0 || !Number.isSafeInteger(received) || received < 0) return void 0;
|
|
196
196
|
return value;
|
|
197
197
|
}
|
|
198
198
|
function writerBinding(value) {
|
|
@@ -300,7 +300,7 @@ function unresolvedOperation(requestId, detail) {
|
|
|
300
300
|
message: `focus ${requestId} is unresolved (${detail})`
|
|
301
301
|
};
|
|
302
302
|
}
|
|
303
|
-
var
|
|
303
|
+
var RADAR_REGISTRATION_ENV = "PI_RADAR_REGISTRATION_ID";
|
|
304
304
|
var RADAR_MANAGED_IDENTITY_ENV = /* @__PURE__ */ new Set([
|
|
305
305
|
"PI_RADAR_MANAGED_SUBJECT_ID",
|
|
306
306
|
"PI_RADAR_MANAGED_LAUNCH_ID",
|
|
@@ -449,8 +449,8 @@ function spawnRequestShape(request) {
|
|
|
449
449
|
const env = Object.entries(request.env ?? {});
|
|
450
450
|
if (env.length > 256) return "spawn env names more than 256 entries";
|
|
451
451
|
for (const [name, value] of env) {
|
|
452
|
-
if (name ===
|
|
453
|
-
return `spawn env cannot set the reserved ${
|
|
452
|
+
if (name === RADAR_REGISTRATION_ENV)
|
|
453
|
+
return `spawn env cannot set the reserved ${RADAR_REGISTRATION_ENV}`;
|
|
454
454
|
if (RADAR_MANAGED_IDENTITY_ENV.has(name))
|
|
455
455
|
return `spawn env cannot set Radar-issued identity variable ${name}`;
|
|
456
456
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || new RegExp("\\p{Cc}", "u").test(value))
|
|
@@ -714,6 +714,7 @@ function createRadarClient(options = {}) {
|
|
|
714
714
|
};
|
|
715
715
|
return answer;
|
|
716
716
|
};
|
|
717
|
+
let processRegistrationId;
|
|
717
718
|
let handshake;
|
|
718
719
|
const negotiated = async () => {
|
|
719
720
|
handshake ??= probe();
|
|
@@ -795,6 +796,7 @@ function createRadarClient(options = {}) {
|
|
|
795
796
|
const record = answer.value.registration;
|
|
796
797
|
const agentId = stringAt(record, "registration_id");
|
|
797
798
|
if (!isUuid(agentId)) return protocol("registration reply carries no registration_id");
|
|
799
|
+
processRegistrationId = agentId;
|
|
798
800
|
return { ok: true, value: { registration_id: agentId } };
|
|
799
801
|
});
|
|
800
802
|
},
|
|
@@ -928,6 +930,31 @@ function createRadarClient(options = {}) {
|
|
|
928
930
|
}
|
|
929
931
|
);
|
|
930
932
|
},
|
|
933
|
+
async spawnList(request) {
|
|
934
|
+
const registrationId = processRegistrationId;
|
|
935
|
+
if (registrationId === void 0)
|
|
936
|
+
return { ok: false, code: "refused", message: "spawn.list requires this client's process registration" };
|
|
937
|
+
return withContract(RADAR_SPAWN_LIST_CONTRACT, async () => {
|
|
938
|
+
const params = {
|
|
939
|
+
registration_id: registrationId,
|
|
940
|
+
...request.after !== void 0 ? { after: request.after } : {},
|
|
941
|
+
...request.limit !== void 0 ? { limit: request.limit } : {}
|
|
942
|
+
};
|
|
943
|
+
const answer = await call("spawn.list", { ...params });
|
|
944
|
+
if (answer.ok === false) return answer;
|
|
945
|
+
const spawns = recordAt(answer.value, "spawns");
|
|
946
|
+
const next = recordAt(answer.value, "next");
|
|
947
|
+
if (!Array.isArray(spawns) || next != null && !isUuid(next))
|
|
948
|
+
return protocol("spawn.list reply carries no valid page");
|
|
949
|
+
for (const spawn of spawns) {
|
|
950
|
+
const decoded = spawnTopology(spawn);
|
|
951
|
+
if (decoded.ok === false) return decoded;
|
|
952
|
+
if (typeof recordAt(spawn, "version") !== "number" || typeof recordAt(spawn, "recorded_at") !== "string" || !["unbound", "bound", "unresolved"].includes(String(recordAt(spawn, "state"))))
|
|
953
|
+
return protocol("spawn.list reply carries an incomplete topology");
|
|
954
|
+
}
|
|
955
|
+
return { ok: true, value: answer.value };
|
|
956
|
+
});
|
|
957
|
+
},
|
|
931
958
|
async spawnGet(requestId) {
|
|
932
959
|
if (!isUuid(requestId))
|
|
933
960
|
return {
|
|
@@ -1511,7 +1538,13 @@ function createRadarProcessSlot() {
|
|
|
1511
1538
|
}
|
|
1512
1539
|
function radarProcessSlot() {
|
|
1513
1540
|
const host = globalThis;
|
|
1514
|
-
|
|
1541
|
+
if (!host[PROCESS_SLOT]) {
|
|
1542
|
+
const slot = createRadarProcessSlot();
|
|
1543
|
+
slot.launchRegistrationId = process.env.PI_RADAR_REGISTRATION_ID;
|
|
1544
|
+
delete process.env.PI_RADAR_REGISTRATION_ID;
|
|
1545
|
+
host[PROCESS_SLOT] = slot;
|
|
1546
|
+
}
|
|
1547
|
+
return host[PROCESS_SLOT];
|
|
1515
1548
|
}
|
|
1516
1549
|
function radarProcessIncarnation(slot = radarProcessSlot()) {
|
|
1517
1550
|
return slot.incarnation ??= randomUUID2();
|
|
@@ -2065,8 +2098,8 @@ function createRadarPublicationInner(options) {
|
|
|
2065
2098
|
const store = options.store ?? createPublicationStore(dataRoot);
|
|
2066
2099
|
const scheduler = options.scheduler ?? systemScheduler;
|
|
2067
2100
|
const client = options.client;
|
|
2068
|
-
const spawnToken = options.spawnToken;
|
|
2069
2101
|
const slot = options.slot ?? radarProcessSlot();
|
|
2102
|
+
const launchRegistrationId = options.launchRegistrationId ?? slot.launchRegistrationId;
|
|
2070
2103
|
const incarnation = options.incarnation ?? radarProcessIncarnation(slot);
|
|
2071
2104
|
const cacheKey = `${options.store === void 0 ? dataRoot : storeIdentity(options.store)}\0${incarnation}`;
|
|
2072
2105
|
const cached = slot.publications.get(cacheKey);
|
|
@@ -2190,7 +2223,7 @@ function createRadarPublicationInner(options) {
|
|
|
2190
2223
|
return {
|
|
2191
2224
|
...identity,
|
|
2192
2225
|
...location?.state === "resolved" ? { location: location.location } : {},
|
|
2193
|
-
...
|
|
2226
|
+
...launchRegistrationId ? { registration_id: launchRegistrationId } : {}
|
|
2194
2227
|
};
|
|
2195
2228
|
},
|
|
2196
2229
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
@@ -2383,7 +2416,7 @@ function createRadarExecutionAdapter(pi, options) {
|
|
|
2383
2416
|
},
|
|
2384
2417
|
...options.location !== void 0 ? { location: options.location } : {},
|
|
2385
2418
|
...managed !== void 0 ? { binding: managed } : {},
|
|
2386
|
-
...options.
|
|
2419
|
+
...options.launchRegistrationId !== void 0 ? { launchRegistrationId: options.launchRegistrationId } : {}
|
|
2387
2420
|
});
|
|
2388
2421
|
} catch {
|
|
2389
2422
|
publication = unavailablePublication2();
|