pi-radar 0.1.0 → 0.3.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 +14 -6
- package/dist/extension/baseline.js +2740 -0
- package/dist/extension/radar-client.js +97 -2
- package/dist/extension/radar-communication.js +39 -0
- package/dist/extension/radar-execution.js +148 -16
- package/dist/extension/radar-launch.js +5 -0
- package/dist/extension/radar-location.js +2 -2
- package/dist/extension/radar-publication.js +67 -15
- package/dist/extension/radar-runtime.js +42 -0
- package/dist/extension/radar-session-marker.js +82 -0
- package/dist/index.js +340 -21
- package/extension/baseline.ts +39 -0
- package/extension/generated-contracts.ts +85 -0
- package/extension/radar-client.ts +140 -1
- package/extension/radar-communication.ts +66 -0
- package/extension/radar-execution.ts +56 -3
- package/extension/radar-launch.ts +8 -1
- package/extension/radar-location.ts +2 -2
- package/extension/radar-publication.ts +73 -12
- package/extension/radar-runtime.ts +97 -0
- package/extension/radar-session-marker.ts +146 -0
- package/index.ts +2 -0
- package/package.json +24 -5
- package/schema/public/v1/handoff-inbox.deserialize.schema.json +18 -0
- package/schema/public/v1/handoff-inbox.serialize.schema.json +18 -0
- package/schema/public/v1/handoff-publish-params.deserialize.schema.json +33 -0
- package/schema/public/v1/handoff-publish-params.serialize.schema.json +33 -0
- package/schema/public/v1/handoff-receipt.deserialize.schema.json +66 -0
- package/schema/public/v1/handoff-receipt.serialize.schema.json +66 -0
- package/schema/public/v1/handoff.deserialize.schema.json +84 -0
- package/schema/public/v1/handoff.serialize.schema.json +84 -0
- package/schema/public/v1/launch-identity-marker.deserialize.schema.json +32 -0
- package/schema/public/v1/launch-identity-marker.serialize.schema.json +32 -0
- package/schema/public/v1/managed-launch-metadata.deserialize.schema.json +39 -0
- package/schema/public/v1/managed-launch-metadata.serialize.schema.json +39 -0
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ var __export = (target, all) => {
|
|
|
8
8
|
var radar_client_exports = {};
|
|
9
9
|
__export(radar_client_exports, {
|
|
10
10
|
RADAR_CALL_TIMEOUT_MS: () => RADAR_CALL_TIMEOUT_MS,
|
|
11
|
+
RADAR_COMMUNICATION_CONTRACT: () => RADAR_COMMUNICATION_CONTRACT,
|
|
11
12
|
RADAR_EVENTS_CONTRACT: () => RADAR_EVENTS_CONTRACT,
|
|
12
13
|
RADAR_EVENT_RETRY_MS: () => RADAR_EVENT_RETRY_MS,
|
|
13
14
|
RADAR_MAX_AWAITED: () => RADAR_MAX_AWAITED,
|
|
@@ -38,13 +39,14 @@ import { lstatSync } from "node:fs";
|
|
|
38
39
|
import { createConnection } from "node:net";
|
|
39
40
|
import { dirname, join } from "node:path";
|
|
40
41
|
var RADAR_PROTOCOL_VERSION = 2;
|
|
41
|
-
var RADAR_MINIMUM_REVISION =
|
|
42
|
+
var RADAR_MINIMUM_REVISION = 3;
|
|
42
43
|
var RADAR_REGISTRY_CONTRACT = "registry.v1";
|
|
43
44
|
var RADAR_REGISTRY_FACTS_CONTRACT = "registry.facts.v1";
|
|
44
45
|
var RADAR_SPAWN_CONTRACT = "spawn.v1";
|
|
45
46
|
var RADAR_SPAWN_CLOSE_CONTRACT = "spawn.close.v1";
|
|
46
47
|
var RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT = "spawn.launch_context.v1";
|
|
47
48
|
var RADAR_EVENTS_CONTRACT = "events.v1";
|
|
49
|
+
var RADAR_COMMUNICATION_CONTRACT = "communication.handoff.v1";
|
|
48
50
|
var RADAR_REQUESTER = "pi-radar";
|
|
49
51
|
var RADAR_MAX_LINE_BYTES = 1024 * 1024;
|
|
50
52
|
var RADAR_CALL_TIMEOUT_MS = 2e3;
|
|
@@ -152,6 +154,28 @@ function numberAt(value, field) {
|
|
|
152
154
|
const found = value[field];
|
|
153
155
|
return typeof found === "number" && Number.isFinite(found) ? found : void 0;
|
|
154
156
|
}
|
|
157
|
+
function handoffAt(value) {
|
|
158
|
+
const version = numberAt(value, "version");
|
|
159
|
+
const id = stringAt(value, "id");
|
|
160
|
+
const sender = recordAt(value, "sender");
|
|
161
|
+
const senderGeneration = numberAt(sender, "execution_generation");
|
|
162
|
+
const target = stringAt(value, "recipient_subject_id");
|
|
163
|
+
const summary = stringAt(value, "summary");
|
|
164
|
+
const content = stringAt(value, "content");
|
|
165
|
+
const published = numberAt(value, "published_at_unix_ms");
|
|
166
|
+
const correlation = typeof value === "object" && value !== null ? value.correlation_id : void 0;
|
|
167
|
+
if (version !== 1 || !isUuid(id) || !isUuid(stringAt(sender, "agent_id")) || !isUuid(stringAt(sender, "session_id")) || !isUuid(stringAt(sender, "subject_id")) || !isUuid(stringAt(sender, "execution_id")) || !Number.isSafeInteger(senderGeneration) || (senderGeneration ?? 0) < 1 || !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;
|
|
168
|
+
return value;
|
|
169
|
+
}
|
|
170
|
+
function handoffReceiptAt(value) {
|
|
171
|
+
const version = numberAt(value, "version");
|
|
172
|
+
const handoffId = stringAt(value, "handoff_id");
|
|
173
|
+
const recipient = recordAt(value, "recipient");
|
|
174
|
+
const generation = numberAt(recipient, "execution_generation");
|
|
175
|
+
const received = numberAt(value, "received_at_unix_ms");
|
|
176
|
+
if (version !== 1 || !isUuid(handoffId) || !isUuid(stringAt(recipient, "agent_id")) || !isUuid(stringAt(recipient, "session_id")) || !isUuid(stringAt(recipient, "subject_id")) || !isUuid(stringAt(recipient, "execution_id")) || !Number.isSafeInteger(generation) || (generation ?? 0) < 1 || received === void 0 || !Number.isSafeInteger(received) || received < 0) return void 0;
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
155
179
|
function writerBinding(value) {
|
|
156
180
|
const handle = stringAt(value, "handle");
|
|
157
181
|
const source = stringAt(value, "source");
|
|
@@ -258,6 +282,12 @@ function unresolvedOperation(requestId, detail) {
|
|
|
258
282
|
};
|
|
259
283
|
}
|
|
260
284
|
var RADAR_SPAWN_TOKEN_ENV = "PI_RADAR_SPAWN_TOKEN";
|
|
285
|
+
var RADAR_MANAGED_IDENTITY_ENV = /* @__PURE__ */ new Set([
|
|
286
|
+
"PI_RADAR_MANAGED_SUBJECT_ID",
|
|
287
|
+
"PI_RADAR_MANAGED_LAUNCH_ID",
|
|
288
|
+
"PI_RADAR_MANAGED_EXECUTION_GENERATION",
|
|
289
|
+
"PI_RADAR_MANAGED_PARENT_SUBJECT_ID"
|
|
290
|
+
]);
|
|
261
291
|
function unresolvedSpawn(requestId, detail) {
|
|
262
292
|
return {
|
|
263
293
|
ok: false,
|
|
@@ -402,9 +432,23 @@ function spawnRequestShape(request) {
|
|
|
402
432
|
for (const [name, value] of env) {
|
|
403
433
|
if (name === RADAR_SPAWN_TOKEN_ENV)
|
|
404
434
|
return `spawn env cannot set the reserved ${RADAR_SPAWN_TOKEN_ENV}`;
|
|
435
|
+
if (RADAR_MANAGED_IDENTITY_ENV.has(name))
|
|
436
|
+
return `spawn env cannot set Radar-issued identity variable ${name}`;
|
|
405
437
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || new RegExp("\\p{Cc}", "u").test(value))
|
|
406
438
|
return `spawn env contains an invalid name or value: ${name}`;
|
|
407
439
|
}
|
|
440
|
+
if (request.managed !== void 0) {
|
|
441
|
+
if (typeof request.managed !== "object" || request.managed === null)
|
|
442
|
+
return "managed launch metadata must be an object";
|
|
443
|
+
for (const [name, value] of Object.entries(request.managed)) {
|
|
444
|
+
if (!["owner", "run", "label", "role", "parent_subject_id"].includes(name))
|
|
445
|
+
return `unknown managed launch metadata field: ${name}`;
|
|
446
|
+
if (value != null && (typeof value !== "string" || value.trim() === "" || new TextEncoder().encode(value).length > 256 || new RegExp("\\p{Cc}", "u").test(value)))
|
|
447
|
+
return `managed launch ${name} must be non-empty, control-free, and at most 256 UTF-8 bytes`;
|
|
448
|
+
}
|
|
449
|
+
if (request.managed.parent_subject_id != null && !isUuid(request.managed.parent_subject_id))
|
|
450
|
+
return "managed parent_subject_id must be a canonical UUID";
|
|
451
|
+
}
|
|
408
452
|
return void 0;
|
|
409
453
|
}
|
|
410
454
|
function focusOutcome(record, requestId) {
|
|
@@ -815,7 +859,8 @@ function createRadarClient(options = {}) {
|
|
|
815
859
|
executable: request.executable,
|
|
816
860
|
argv: request.argv,
|
|
817
861
|
...request.cwd !== void 0 ? { cwd: request.cwd } : {},
|
|
818
|
-
...carriesContext && request.env !== void 0 ? { env: request.env } : {}
|
|
862
|
+
...carriesContext && request.env !== void 0 ? { env: request.env } : {},
|
|
863
|
+
...request.managed !== void 0 ? { managed: request.managed } : {}
|
|
819
864
|
},
|
|
820
865
|
requester
|
|
821
866
|
},
|
|
@@ -915,6 +960,56 @@ function createRadarClient(options = {}) {
|
|
|
915
960
|
return protocol("spawn.close reply carries no close outcome");
|
|
916
961
|
});
|
|
917
962
|
},
|
|
963
|
+
async communicationPublish(request) {
|
|
964
|
+
return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
|
|
965
|
+
const answer = await call("communication.publish", {
|
|
966
|
+
sender_agent_id: request.sender_agent_id,
|
|
967
|
+
recipient_subject_id: request.recipient_subject_id,
|
|
968
|
+
...request.correlation_id !== void 0 ? { correlation_id: request.correlation_id } : {},
|
|
969
|
+
summary: request.summary,
|
|
970
|
+
content: request.content
|
|
971
|
+
}, request.request_id);
|
|
972
|
+
if (answer.ok === false) return answer;
|
|
973
|
+
const handoff = handoffAt(recordAt(answer.value, "handoff"));
|
|
974
|
+
const created = recordAt(answer.value, "created");
|
|
975
|
+
if (handoff === void 0 || typeof created !== "boolean")
|
|
976
|
+
return protocol("communication.publish reply carries no handoff");
|
|
977
|
+
return { ok: true, value: { handoff, created } };
|
|
978
|
+
});
|
|
979
|
+
},
|
|
980
|
+
async communicationInbox(request) {
|
|
981
|
+
return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
|
|
982
|
+
const answer = await call("communication.inbox", request);
|
|
983
|
+
if (answer.ok === false) return answer;
|
|
984
|
+
const handoffIds = recordAt(answer.value, "handoff_ids");
|
|
985
|
+
if (!Array.isArray(handoffIds) || handoffIds.some((id) => !isUuid(id)))
|
|
986
|
+
return protocol("communication.inbox reply carries malformed handoff IDs");
|
|
987
|
+
return { ok: true, value: answer.value };
|
|
988
|
+
});
|
|
989
|
+
},
|
|
990
|
+
async communicationGet(request) {
|
|
991
|
+
return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
|
|
992
|
+
const answer = await call("communication.get", request);
|
|
993
|
+
if (answer.ok === false) return answer;
|
|
994
|
+
const handoff = handoffAt(recordAt(answer.value, "handoff"));
|
|
995
|
+
const receiptValue = recordAt(answer.value, "receipt");
|
|
996
|
+
const receipt = receiptValue === null ? null : handoffReceiptAt(receiptValue);
|
|
997
|
+
if (handoff === void 0 || receiptValue !== null && receipt === void 0)
|
|
998
|
+
return protocol("communication.get reply carries malformed content or receipt");
|
|
999
|
+
return { ok: true, value: { handoff, receipt: receipt ?? null } };
|
|
1000
|
+
});
|
|
1001
|
+
},
|
|
1002
|
+
async communicationAck(request) {
|
|
1003
|
+
return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
|
|
1004
|
+
const answer = await call("communication.ack", request);
|
|
1005
|
+
if (answer.ok === false) return answer;
|
|
1006
|
+
const receipt = handoffReceiptAt(recordAt(answer.value, "receipt"));
|
|
1007
|
+
const created = recordAt(answer.value, "created");
|
|
1008
|
+
if (receipt === void 0 || typeof created !== "boolean")
|
|
1009
|
+
return protocol("communication.ack reply carries no receipt");
|
|
1010
|
+
return { ok: true, value: { receipt, created } };
|
|
1011
|
+
});
|
|
1012
|
+
},
|
|
918
1013
|
async events(request) {
|
|
919
1014
|
if (!Number.isSafeInteger(request.after) || request.after < 0 || !Number.isSafeInteger(request.wait_ms) || request.wait_ms < 0 || request.wait_ms > RADAR_EVENT_WAIT_MS)
|
|
920
1015
|
return {
|
|
@@ -1135,6 +1230,47 @@ function boundedRadarSnapshot(snapshot) {
|
|
|
1135
1230
|
};
|
|
1136
1231
|
}
|
|
1137
1232
|
|
|
1233
|
+
// extension/radar-communication.ts
|
|
1234
|
+
var radar_communication_exports = {};
|
|
1235
|
+
__export(radar_communication_exports, {
|
|
1236
|
+
createRadarHandoffPort: () => createRadarHandoffPort
|
|
1237
|
+
});
|
|
1238
|
+
function createRadarHandoffPort(client, publication, parentSubjectId = () => process.env.PI_RADAR_MANAGED_PARENT_SUBJECT_ID) {
|
|
1239
|
+
async function agentId() {
|
|
1240
|
+
const subject = await publication.subject();
|
|
1241
|
+
return subject === void 0 ? { ok: false, code: "absent", message: "this process has no current Radar registration" } : { ok: true, value: subject.agent_id };
|
|
1242
|
+
}
|
|
1243
|
+
return {
|
|
1244
|
+
async publish(request) {
|
|
1245
|
+
const identity = await agentId();
|
|
1246
|
+
if (identity.ok === false) return identity;
|
|
1247
|
+
const parent = parentSubjectId();
|
|
1248
|
+
if (parent === void 0)
|
|
1249
|
+
return { ok: false, code: "absent", message: "this managed execution has no Radar parent subject" };
|
|
1250
|
+
return client.communicationPublish({
|
|
1251
|
+
...request,
|
|
1252
|
+
sender_agent_id: identity.value,
|
|
1253
|
+
recipient_subject_id: parent
|
|
1254
|
+
});
|
|
1255
|
+
},
|
|
1256
|
+
async inbox() {
|
|
1257
|
+
const identity = await agentId();
|
|
1258
|
+
if (identity.ok === false) return identity;
|
|
1259
|
+
return client.communicationInbox({ agent_id: identity.value });
|
|
1260
|
+
},
|
|
1261
|
+
async get(handoff_id) {
|
|
1262
|
+
const identity = await agentId();
|
|
1263
|
+
if (identity.ok === false) return identity;
|
|
1264
|
+
return client.communicationGet({ agent_id: identity.value, handoff_id });
|
|
1265
|
+
},
|
|
1266
|
+
async ack(handoff_id) {
|
|
1267
|
+
const identity = await agentId();
|
|
1268
|
+
if (identity.ok === false) return identity;
|
|
1269
|
+
return client.communicationAck({ agent_id: identity.value, handoff_id });
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1138
1274
|
// extension/radar-execution.ts
|
|
1139
1275
|
var radar_execution_exports = {};
|
|
1140
1276
|
__export(radar_execution_exports, {
|
|
@@ -1173,6 +1309,77 @@ import {
|
|
|
1173
1309
|
writeSync
|
|
1174
1310
|
} from "node:fs";
|
|
1175
1311
|
import { dirname as dirname2, join as join2, parse, resolve, sep } from "node:path";
|
|
1312
|
+
|
|
1313
|
+
// extension/radar-session-marker.ts
|
|
1314
|
+
var RADAR_LAUNCH_MARKER_TYPE = "pi-radar.launch-identity";
|
|
1315
|
+
var RADAR_LAUNCH_MARKER_VERSION = 1;
|
|
1316
|
+
var RADAR_MAX_EXECUTION_GENERATION = 4294967295;
|
|
1317
|
+
var RADAR_MANAGED_SUBJECT_ENV = "PI_RADAR_MANAGED_SUBJECT_ID";
|
|
1318
|
+
var RADAR_MANAGED_LAUNCH_ENV = "PI_RADAR_MANAGED_LAUNCH_ID";
|
|
1319
|
+
var RADAR_MANAGED_GENERATION_ENV = "PI_RADAR_MANAGED_EXECUTION_GENERATION";
|
|
1320
|
+
var UUID2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
|
1321
|
+
function decodeRadarLaunchMarker(value) {
|
|
1322
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
1323
|
+
const record = value;
|
|
1324
|
+
if (Object.keys(record).sort().join(",") !== "execution_generation,launch_id,subject_id,version") {
|
|
1325
|
+
return void 0;
|
|
1326
|
+
}
|
|
1327
|
+
if (record.version !== RADAR_LAUNCH_MARKER_VERSION || typeof record.subject_id !== "string" || !UUID2.test(record.subject_id) || typeof record.launch_id !== "string" || !UUID2.test(record.launch_id) || !Number.isSafeInteger(record.execution_generation) || record.execution_generation < 1 || record.execution_generation > RADAR_MAX_EXECUTION_GENERATION) {
|
|
1328
|
+
return void 0;
|
|
1329
|
+
}
|
|
1330
|
+
return {
|
|
1331
|
+
version: RADAR_LAUNCH_MARKER_VERSION,
|
|
1332
|
+
subject_id: record.subject_id,
|
|
1333
|
+
launch_id: record.launch_id,
|
|
1334
|
+
execution_generation: record.execution_generation
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
function appendRadarLaunchMarker(appendEntry, marker) {
|
|
1338
|
+
const validated = decodeRadarLaunchMarker(marker);
|
|
1339
|
+
if (!validated) throw new Error("invalid Radar launch identity marker");
|
|
1340
|
+
appendEntry(RADAR_LAUNCH_MARKER_TYPE, validated);
|
|
1341
|
+
}
|
|
1342
|
+
function latestRadarLaunchMarker(branch) {
|
|
1343
|
+
let latest;
|
|
1344
|
+
for (const entry of branch) {
|
|
1345
|
+
if (entry.type === "custom" && entry.customType === RADAR_LAUNCH_MARKER_TYPE) {
|
|
1346
|
+
latest = entry;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
if (!latest) return { status: "absent" };
|
|
1350
|
+
const marker = decodeRadarLaunchMarker(latest.data);
|
|
1351
|
+
return marker ? { status: "valid", marker } : { status: "invalid" };
|
|
1352
|
+
}
|
|
1353
|
+
function radarLaunchMarkerFromEnvironment(env = process.env) {
|
|
1354
|
+
const rawGeneration = env[RADAR_MANAGED_GENERATION_ENV];
|
|
1355
|
+
if (env[RADAR_MANAGED_SUBJECT_ENV] === void 0 || env[RADAR_MANAGED_LAUNCH_ENV] === void 0 || rawGeneration === void 0 || !/^[1-9][0-9]*$/.test(rawGeneration)) {
|
|
1356
|
+
return void 0;
|
|
1357
|
+
}
|
|
1358
|
+
return decodeRadarLaunchMarker({
|
|
1359
|
+
version: RADAR_LAUNCH_MARKER_VERSION,
|
|
1360
|
+
subject_id: env[RADAR_MANAGED_SUBJECT_ENV],
|
|
1361
|
+
launch_id: env[RADAR_MANAGED_LAUNCH_ENV],
|
|
1362
|
+
execution_generation: Number(rawGeneration)
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
function activeRadarLaunchMarker(host) {
|
|
1366
|
+
if (!host.sessionManager) return { status: "absent" };
|
|
1367
|
+
return latestRadarLaunchMarker(host.sessionManager.getBranch());
|
|
1368
|
+
}
|
|
1369
|
+
function persistRadarLaunchMarker(host, env = process.env) {
|
|
1370
|
+
const marker = radarLaunchMarkerFromEnvironment(env);
|
|
1371
|
+
if (!marker) return "absent";
|
|
1372
|
+
if (!host.appendEntry || !host.sessionManager) return "unsupported";
|
|
1373
|
+
const current = latestRadarLaunchMarker(host.sessionManager.getBranch());
|
|
1374
|
+
if (current.status === "valid" && sameMarker(current.marker, marker)) return "unchanged";
|
|
1375
|
+
appendRadarLaunchMarker(host.appendEntry, marker);
|
|
1376
|
+
return "written";
|
|
1377
|
+
}
|
|
1378
|
+
function sameMarker(a, b) {
|
|
1379
|
+
return a.version === b.version && a.subject_id === b.subject_id && a.launch_id === b.launch_id && a.execution_generation === b.execution_generation;
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
// extension/radar-publication.ts
|
|
1176
1383
|
var RADAR_PUBLICATION_VERSION = 1;
|
|
1177
1384
|
var RADAR_LEASE_MS = 3e4;
|
|
1178
1385
|
var RADAR_HEARTBEAT_MS = 15e3;
|
|
@@ -1483,15 +1690,21 @@ function pendingAt(value, keys = PENDING_KEYS, codec = executionCodec) {
|
|
|
1483
1690
|
return { sequence, snapshot, lease_ms: lease, observed_at: observed };
|
|
1484
1691
|
}
|
|
1485
1692
|
var CONTEXT_PENDING_KEYS = "lease_ms,observed_at,sequence,session";
|
|
1693
|
+
var CONTEXT_PENDING_MARKED_KEYS = "launch_identity,lease_ms,observed_at,sequence,session";
|
|
1486
1694
|
var CONTEXT_ACCEPTED_KEYS = "lease_ms,observed_at,sent_at,sequence,session";
|
|
1695
|
+
var CONTEXT_ACCEPTED_MARKED_KEYS = "launch_identity,lease_ms,observed_at,sent_at,sequence,session";
|
|
1487
1696
|
function sessionAt(value) {
|
|
1488
1697
|
if (typeof value !== "object" || value === null) return void 0;
|
|
1489
1698
|
const found = value.session;
|
|
1490
1699
|
if (found === null) return null;
|
|
1491
1700
|
return typeof found === "string" ? found : void 0;
|
|
1492
1701
|
}
|
|
1493
|
-
function
|
|
1494
|
-
|
|
1702
|
+
function sameLaunchIdentity(a, b) {
|
|
1703
|
+
return a === b || a !== void 0 && b !== void 0 && a.version === b.version && a.subject_id === b.subject_id && a.launch_id === b.launch_id && a.execution_generation === b.execution_generation;
|
|
1704
|
+
}
|
|
1705
|
+
function contextReportAt(value, keys, markedKeys) {
|
|
1706
|
+
const marked = exactKeys(value, markedKeys);
|
|
1707
|
+
if (!exactKeys(value, keys) && !marked) return void 0;
|
|
1495
1708
|
const sequence = numberAt2(value, "sequence");
|
|
1496
1709
|
const lease = numberAt2(value, "lease_ms");
|
|
1497
1710
|
const observed = textAt(value, "observed_at");
|
|
@@ -1499,7 +1712,15 @@ function contextReportAt(value, keys) {
|
|
|
1499
1712
|
if (sequence === void 0 || sequence < 1) return void 0;
|
|
1500
1713
|
if (lease === void 0 || observed === void 0 || session === void 0)
|
|
1501
1714
|
return void 0;
|
|
1502
|
-
|
|
1715
|
+
const marker = marked ? decodeRadarLaunchMarker(value.launch_identity) : void 0;
|
|
1716
|
+
if (marked && marker === void 0) return void 0;
|
|
1717
|
+
return {
|
|
1718
|
+
sequence,
|
|
1719
|
+
session,
|
|
1720
|
+
lease_ms: lease,
|
|
1721
|
+
observed_at: observed,
|
|
1722
|
+
...marker !== void 0 ? { launch_identity: marker } : {}
|
|
1723
|
+
};
|
|
1503
1724
|
}
|
|
1504
1725
|
function createChannelWriter(input) {
|
|
1505
1726
|
const { store, client, scheduler, recordName, channel, publisher } = input;
|
|
@@ -1738,6 +1959,7 @@ function createContextWriter(input) {
|
|
|
1738
1959
|
const { store, client, scheduler, recordName, publisher } = input;
|
|
1739
1960
|
let record = loadRecord();
|
|
1740
1961
|
let desired;
|
|
1962
|
+
let desiredMarker;
|
|
1741
1963
|
let running = false;
|
|
1742
1964
|
let again = false;
|
|
1743
1965
|
let recovering = false;
|
|
@@ -1763,9 +1985,17 @@ function createContextWriter(input) {
|
|
|
1763
1985
|
const generation = numberAt2(writer, "generation");
|
|
1764
1986
|
if (writer !== void 0 && (!exactKeys(writer, WRITER_KEYS) || !isUuid(handle) || generation === void 0))
|
|
1765
1987
|
return blank(agentId);
|
|
1766
|
-
const pending = contextReportAt(
|
|
1988
|
+
const pending = contextReportAt(
|
|
1989
|
+
recordAt2(value, "pending"),
|
|
1990
|
+
CONTEXT_PENDING_KEYS,
|
|
1991
|
+
CONTEXT_PENDING_MARKED_KEYS
|
|
1992
|
+
);
|
|
1767
1993
|
const acceptedValue = recordAt2(value, "accepted");
|
|
1768
|
-
const accepted = contextReportAt(
|
|
1994
|
+
const accepted = contextReportAt(
|
|
1995
|
+
acceptedValue,
|
|
1996
|
+
CONTEXT_ACCEPTED_KEYS,
|
|
1997
|
+
CONTEXT_ACCEPTED_MARKED_KEYS
|
|
1998
|
+
);
|
|
1769
1999
|
const sentAt = numberAt2(acceptedValue, "sent_at");
|
|
1770
2000
|
return {
|
|
1771
2001
|
version: RADAR_PUBLICATION_VERSION,
|
|
@@ -1825,7 +2055,10 @@ function createContextWriter(input) {
|
|
|
1825
2055
|
sequence: pending.sequence,
|
|
1826
2056
|
lease_ms: pending.lease_ms,
|
|
1827
2057
|
observed_at: pending.observed_at,
|
|
1828
|
-
context: {
|
|
2058
|
+
context: {
|
|
2059
|
+
session: pending.session,
|
|
2060
|
+
...pending.launch_identity !== void 0 ? { launch_identity: pending.launch_identity } : {}
|
|
2061
|
+
}
|
|
1829
2062
|
});
|
|
1830
2063
|
if (answer.ok === true) {
|
|
1831
2064
|
recovering = false;
|
|
@@ -1856,12 +2089,13 @@ function createContextWriter(input) {
|
|
|
1856
2089
|
return;
|
|
1857
2090
|
}
|
|
1858
2091
|
}
|
|
1859
|
-
async function stage(session) {
|
|
2092
|
+
async function stage(session, launchIdentity) {
|
|
1860
2093
|
const pending = {
|
|
1861
2094
|
sequence: record.sequence + 1,
|
|
1862
2095
|
session,
|
|
1863
2096
|
lease_ms: input.leaseMs,
|
|
1864
|
-
observed_at: new Date(scheduler.now()).toISOString()
|
|
2097
|
+
observed_at: new Date(scheduler.now()).toISOString(),
|
|
2098
|
+
...launchIdentity !== void 0 ? { launch_identity: launchIdentity } : {}
|
|
1865
2099
|
};
|
|
1866
2100
|
record.pending = pending;
|
|
1867
2101
|
save();
|
|
@@ -1876,11 +2110,11 @@ function createContextWriter(input) {
|
|
|
1876
2110
|
if (desired === void 0) return;
|
|
1877
2111
|
if (!await ensureSubject()) return;
|
|
1878
2112
|
if (fenced) return;
|
|
1879
|
-
if (record.writer === void 0 || record.accepted === void 0 || record.accepted.session !== desired) {
|
|
1880
|
-
await stage(desired);
|
|
2113
|
+
if (record.writer === void 0 || record.accepted === void 0 || record.accepted.session !== desired || !sameLaunchIdentity(record.accepted.launch_identity, desiredMarker)) {
|
|
2114
|
+
await stage(desired, desiredMarker);
|
|
1881
2115
|
return;
|
|
1882
2116
|
}
|
|
1883
|
-
if (heartbeatDue()) await stage(record.accepted.session);
|
|
2117
|
+
if (heartbeatDue()) await stage(record.accepted.session, desiredMarker);
|
|
1884
2118
|
}
|
|
1885
2119
|
async function drain() {
|
|
1886
2120
|
if (running || stopped || fenced) return;
|
|
@@ -1912,9 +2146,11 @@ function createContextWriter(input) {
|
|
|
1912
2146
|
void drain().catch(() => void 0);
|
|
1913
2147
|
}
|
|
1914
2148
|
return {
|
|
1915
|
-
update(session) {
|
|
1916
|
-
if (fenced || session === desired)
|
|
2149
|
+
update(session, launchIdentity) {
|
|
2150
|
+
if (fenced || session === desired && sameLaunchIdentity(desiredMarker, launchIdentity))
|
|
2151
|
+
return;
|
|
1917
2152
|
desired = session;
|
|
2153
|
+
desiredMarker = launchIdentity;
|
|
1918
2154
|
if (stopped) return;
|
|
1919
2155
|
kick();
|
|
1920
2156
|
},
|
|
@@ -2187,8 +2423,10 @@ function createRadarPublicationInner(options) {
|
|
|
2187
2423
|
const record = await ensureRegistration();
|
|
2188
2424
|
if (record === void 0) return void 0;
|
|
2189
2425
|
const location = record.registration.location;
|
|
2426
|
+
const subjectId = process.env.PI_RADAR_MANAGED_SUBJECT_ID;
|
|
2190
2427
|
return {
|
|
2191
2428
|
agent_id: agentId2,
|
|
2429
|
+
...subjectId !== void 0 ? { subject_id: subjectId } : {},
|
|
2192
2430
|
...location !== void 0 ? { location } : {}
|
|
2193
2431
|
};
|
|
2194
2432
|
},
|
|
@@ -2326,11 +2564,11 @@ function createRadarExecutionAdapter(pi, options) {
|
|
|
2326
2564
|
...lastOutcome !== void 0 ? { last_outcome: lastOutcome } : {}
|
|
2327
2565
|
};
|
|
2328
2566
|
}
|
|
2329
|
-
function reportSession(context) {
|
|
2567
|
+
function reportSession(context, launchIdentity) {
|
|
2330
2568
|
const session = context?.sessionManager?.getSessionId?.();
|
|
2331
2569
|
if (!isUuid(session)) return;
|
|
2332
2570
|
try {
|
|
2333
|
-
publication.context.update(session);
|
|
2571
|
+
publication.context.update(session, launchIdentity);
|
|
2334
2572
|
} catch {
|
|
2335
2573
|
}
|
|
2336
2574
|
}
|
|
@@ -2394,8 +2632,38 @@ function createRadarExecutionAdapter(pi, options) {
|
|
|
2394
2632
|
})
|
|
2395
2633
|
);
|
|
2396
2634
|
on("session_start", (_event, context) => {
|
|
2635
|
+
const sessionHost = {
|
|
2636
|
+
appendEntry: pi.appendEntry ? (customType, data) => pi.appendEntry?.(customType, data) : void 0,
|
|
2637
|
+
sessionManager: context?.sessionManager
|
|
2638
|
+
};
|
|
2639
|
+
let launchIdentity;
|
|
2640
|
+
try {
|
|
2641
|
+
const result = persistRadarLaunchMarker(
|
|
2642
|
+
sessionHost,
|
|
2643
|
+
options.markerEnvironment
|
|
2644
|
+
);
|
|
2645
|
+
if (result === "written" || result === "unchanged") {
|
|
2646
|
+
const expected = radarLaunchMarkerFromEnvironment(options.markerEnvironment);
|
|
2647
|
+
const activeMarker = activeRadarLaunchMarker(sessionHost);
|
|
2648
|
+
if (expected && activeMarker.status === "valid" && JSON.stringify(activeMarker.marker) === JSON.stringify(expected)) {
|
|
2649
|
+
launchIdentity = activeMarker.marker;
|
|
2650
|
+
} else {
|
|
2651
|
+
options.diagnostic?.("Radar launch marker was not visible on the active Pi session branch");
|
|
2652
|
+
}
|
|
2653
|
+
} else if (result === "unsupported") {
|
|
2654
|
+
options.diagnostic?.("Pi host does not support Radar launch identity session entries");
|
|
2655
|
+
} else if (options.markerEnvironment && [
|
|
2656
|
+
"PI_RADAR_MANAGED_SUBJECT_ID",
|
|
2657
|
+
"PI_RADAR_MANAGED_LAUNCH_ID",
|
|
2658
|
+
"PI_RADAR_MANAGED_EXECUTION_GENERATION"
|
|
2659
|
+
].some((key) => options.markerEnvironment?.[key] !== void 0)) {
|
|
2660
|
+
options.diagnostic?.("incomplete or invalid Radar launch identity environment");
|
|
2661
|
+
}
|
|
2662
|
+
} catch (error) {
|
|
2663
|
+
options.diagnostic?.(`could not persist Radar launch marker: ${String(error)}`);
|
|
2664
|
+
}
|
|
2397
2665
|
rootSession = true;
|
|
2398
|
-
reportSession(context);
|
|
2666
|
+
reportSession(context, launchIdentity);
|
|
2399
2667
|
publishFacts(context);
|
|
2400
2668
|
publication.reopen();
|
|
2401
2669
|
active = context?.isIdle?.() === false;
|
|
@@ -2582,7 +2850,8 @@ async function resolveRadarProcessLocation(client, env = process.env) {
|
|
|
2582
2850
|
const backend = ready.value.backend;
|
|
2583
2851
|
if (backend === void 0 || backend === "none")
|
|
2584
2852
|
return { state: "absent", reason: "the daemon serves no physical backend" };
|
|
2585
|
-
const
|
|
2853
|
+
const instance = ready.value.instance ?? void 0;
|
|
2854
|
+
const own = qualifiedOwnPane(env, backend, instance);
|
|
2586
2855
|
if ("absent" in own) return { state: "absent", reason: own.absent };
|
|
2587
2856
|
if ("pending" in own) return { state: "pending", reason: own.pending };
|
|
2588
2857
|
const paneId = own.pane;
|
|
@@ -2614,7 +2883,6 @@ async function resolveRadarProcessLocation(client, env = process.env) {
|
|
|
2614
2883
|
if ("ambiguous" in containment)
|
|
2615
2884
|
return { state: "pending", reason: containment.ambiguous };
|
|
2616
2885
|
const row = containment.pane;
|
|
2617
|
-
const instance = ready.value.instance;
|
|
2618
2886
|
return {
|
|
2619
2887
|
state: "resolved",
|
|
2620
2888
|
location: {
|
|
@@ -2760,6 +3028,10 @@ async function launchRadarWorker(options) {
|
|
|
2760
3028
|
return refusedLaunch(
|
|
2761
3029
|
"the launch has no exact parent registration to spawn under"
|
|
2762
3030
|
);
|
|
3031
|
+
if (!isUuid(launch.parent.subject_id))
|
|
3032
|
+
return refusedLaunch(
|
|
3033
|
+
"the launch has no managed Radar subject for its parent"
|
|
3034
|
+
);
|
|
2763
3035
|
if (pane === void 0 || pane === "")
|
|
2764
3036
|
return refusedLaunch(
|
|
2765
3037
|
"this process's Radar registration names no pane, so it cannot be a spawn parent"
|
|
@@ -2769,6 +3041,7 @@ async function launchRadarWorker(options) {
|
|
|
2769
3041
|
parent: launch.parent.agent_id,
|
|
2770
3042
|
parent_session_id: launch.parent_session_id,
|
|
2771
3043
|
executable: launch.executable,
|
|
3044
|
+
managed: { parent_subject_id: launch.parent.subject_id },
|
|
2772
3045
|
argv: launch.argv,
|
|
2773
3046
|
...launch.cwd !== "" ? { cwd: launch.cwd } : {},
|
|
2774
3047
|
...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
|
|
@@ -2895,11 +3168,57 @@ async function waitForChild(client, requestId, options) {
|
|
|
2895
3168
|
});
|
|
2896
3169
|
}
|
|
2897
3170
|
}
|
|
3171
|
+
|
|
3172
|
+
// extension/radar-runtime.ts
|
|
3173
|
+
var radar_runtime_exports = {};
|
|
3174
|
+
__export(radar_runtime_exports, {
|
|
3175
|
+
RADAR_RUNTIME_REQUEST: () => RADAR_RUNTIME_REQUEST,
|
|
3176
|
+
RADAR_RUNTIME_VERSION: () => RADAR_RUNTIME_VERSION,
|
|
3177
|
+
lazyRadarRuntime: () => lazyRadarRuntime,
|
|
3178
|
+
provideRadarRuntime: () => provideRadarRuntime,
|
|
3179
|
+
requestRadarRuntime: () => requestRadarRuntime
|
|
3180
|
+
});
|
|
3181
|
+
var RADAR_RUNTIME_REQUEST = "pi-radar/runtime-request";
|
|
3182
|
+
var RADAR_RUNTIME_VERSION = 2;
|
|
3183
|
+
function lazyRadarRuntime(start) {
|
|
3184
|
+
let pending;
|
|
3185
|
+
return {
|
|
3186
|
+
version: RADAR_RUNTIME_VERSION,
|
|
3187
|
+
start: () => pending ??= Promise.resolve().then(start)
|
|
3188
|
+
};
|
|
3189
|
+
}
|
|
3190
|
+
function provideRadarRuntime(events, handle) {
|
|
3191
|
+
return events.on(RADAR_RUNTIME_REQUEST, (value) => {
|
|
3192
|
+
if (!isRuntimeRequest(value)) return;
|
|
3193
|
+
value.provide(handle);
|
|
3194
|
+
});
|
|
3195
|
+
}
|
|
3196
|
+
function requestRadarRuntime(events) {
|
|
3197
|
+
const handles = [];
|
|
3198
|
+
events.emit(RADAR_RUNTIME_REQUEST, {
|
|
3199
|
+
version: RADAR_RUNTIME_VERSION,
|
|
3200
|
+
provide: (candidate) => {
|
|
3201
|
+
if (isRadarRuntimeHandle(candidate)) handles.push(candidate);
|
|
3202
|
+
}
|
|
3203
|
+
});
|
|
3204
|
+
if (handles.length === 1) return { ok: true, handle: handles[0] };
|
|
3205
|
+
return { ok: false, reason: handles.length === 0 ? "missing" : "duplicate" };
|
|
3206
|
+
}
|
|
3207
|
+
function isRuntimeRequest(value) {
|
|
3208
|
+
return typeof value === "object" && value !== null && value.version === RADAR_RUNTIME_VERSION && typeof value.provide === "function";
|
|
3209
|
+
}
|
|
3210
|
+
function isRadarRuntimeHandle(value) {
|
|
3211
|
+
if (typeof value !== "object" || value === null) return false;
|
|
3212
|
+
const handle = value;
|
|
3213
|
+
return handle.version === RADAR_RUNTIME_VERSION && typeof handle.start === "function";
|
|
3214
|
+
}
|
|
2898
3215
|
export {
|
|
2899
3216
|
radar_client_exports as client,
|
|
3217
|
+
radar_communication_exports as communication,
|
|
2900
3218
|
radar_execution_exports as execution,
|
|
2901
3219
|
radar_inspection_exports as inspection,
|
|
2902
3220
|
radar_launch_exports as launch,
|
|
2903
3221
|
radar_location_exports as location,
|
|
2904
|
-
radar_publication_exports as publication
|
|
3222
|
+
radar_publication_exports as publication,
|
|
3223
|
+
radar_runtime_exports as runtime
|
|
2905
3224
|
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { createRadarClient } from "./radar-client.ts";
|
|
4
|
+
import { createRadarHandoffPort } from "./radar-communication.ts";
|
|
5
|
+
import {
|
|
6
|
+
createRadarExecutionAdapter,
|
|
7
|
+
type RadarExecutionEventApi,
|
|
8
|
+
} from "./radar-execution.ts";
|
|
9
|
+
import { resolveRadarProcessLocation } from "./radar-location.ts";
|
|
10
|
+
import {
|
|
11
|
+
lazyRadarRuntime,
|
|
12
|
+
provideRadarRuntime,
|
|
13
|
+
type RadarExtensionEventBus,
|
|
14
|
+
} from "./radar-runtime.ts";
|
|
15
|
+
|
|
16
|
+
/** Minimal structural Pi API used by the baseline extension. */
|
|
17
|
+
type PiRadarHost = RadarExecutionEventApi & {
|
|
18
|
+
events: RadarExtensionEventBus;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Register pi-radar as the one baseline registrar for this Pi process.
|
|
23
|
+
* Herdsman integrations request the same session-scoped runtime handle over
|
|
24
|
+
* `pi.events`; they add orchestration data without creating another publisher.
|
|
25
|
+
*/
|
|
26
|
+
export default function registerPiRadar(pi: PiRadarHost): void {
|
|
27
|
+
const client = createRadarClient();
|
|
28
|
+
const execution = createRadarExecutionAdapter(pi, {
|
|
29
|
+
client,
|
|
30
|
+
dataRoot: join(homedir(), ".pi", "agent", "pi-radar"),
|
|
31
|
+
location: () => resolveRadarProcessLocation(client),
|
|
32
|
+
});
|
|
33
|
+
const runtime = lazyRadarRuntime(async () => ({
|
|
34
|
+
publication: execution.publication,
|
|
35
|
+
communication: createRadarHandoffPort(client, execution.publication),
|
|
36
|
+
execution,
|
|
37
|
+
}));
|
|
38
|
+
provideRadarRuntime(pi.events, runtime);
|
|
39
|
+
}
|