pi-radar 0.1.0 → 0.2.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 +9 -5
- package/dist/extension/baseline.js +2626 -0
- package/dist/extension/radar-client.js +22 -2
- package/dist/extension/radar-execution.js +146 -16
- package/dist/extension/radar-location.js +2 -2
- package/dist/extension/radar-publication.js +65 -15
- package/dist/extension/radar-runtime.js +42 -0
- package/dist/extension/radar-session-marker.js +82 -0
- package/dist/index.js +216 -21
- package/extension/baseline.ts +37 -0
- package/extension/generated-contracts.ts +21 -0
- package/extension/radar-client.ts +35 -1
- package/extension/radar-execution.ts +56 -3
- package/extension/radar-location.ts +2 -2
- package/extension/radar-publication.ts +69 -12
- package/extension/radar-runtime.ts +94 -0
- package/extension/radar-session-marker.ts +146 -0
- package/index.ts +1 -0
- package/package.json +18 -4
- 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
|
@@ -4,7 +4,7 @@ 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 = 3;
|
|
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";
|
|
@@ -224,6 +224,11 @@ function unresolvedOperation(requestId, detail) {
|
|
|
224
224
|
};
|
|
225
225
|
}
|
|
226
226
|
var RADAR_SPAWN_TOKEN_ENV = "PI_RADAR_SPAWN_TOKEN";
|
|
227
|
+
var RADAR_MANAGED_IDENTITY_ENV = /* @__PURE__ */ new Set([
|
|
228
|
+
"PI_RADAR_MANAGED_SUBJECT_ID",
|
|
229
|
+
"PI_RADAR_MANAGED_LAUNCH_ID",
|
|
230
|
+
"PI_RADAR_MANAGED_EXECUTION_GENERATION"
|
|
231
|
+
]);
|
|
227
232
|
function unresolvedSpawn(requestId, detail) {
|
|
228
233
|
return {
|
|
229
234
|
ok: false,
|
|
@@ -368,9 +373,23 @@ function spawnRequestShape(request) {
|
|
|
368
373
|
for (const [name, value] of env) {
|
|
369
374
|
if (name === RADAR_SPAWN_TOKEN_ENV)
|
|
370
375
|
return `spawn env cannot set the reserved ${RADAR_SPAWN_TOKEN_ENV}`;
|
|
376
|
+
if (RADAR_MANAGED_IDENTITY_ENV.has(name))
|
|
377
|
+
return `spawn env cannot set Radar-issued identity variable ${name}`;
|
|
371
378
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || new RegExp("\\p{Cc}", "u").test(value))
|
|
372
379
|
return `spawn env contains an invalid name or value: ${name}`;
|
|
373
380
|
}
|
|
381
|
+
if (request.managed !== void 0) {
|
|
382
|
+
if (typeof request.managed !== "object" || request.managed === null)
|
|
383
|
+
return "managed launch metadata must be an object";
|
|
384
|
+
for (const [name, value] of Object.entries(request.managed)) {
|
|
385
|
+
if (!["owner", "run", "label", "role", "parent_subject_id"].includes(name))
|
|
386
|
+
return `unknown managed launch metadata field: ${name}`;
|
|
387
|
+
if (value != null && (typeof value !== "string" || value.trim() === "" || new TextEncoder().encode(value).length > 256 || new RegExp("\\p{Cc}", "u").test(value)))
|
|
388
|
+
return `managed launch ${name} must be non-empty, control-free, and at most 256 UTF-8 bytes`;
|
|
389
|
+
}
|
|
390
|
+
if (request.managed.parent_subject_id != null && !isUuid(request.managed.parent_subject_id))
|
|
391
|
+
return "managed parent_subject_id must be a canonical UUID";
|
|
392
|
+
}
|
|
374
393
|
return void 0;
|
|
375
394
|
}
|
|
376
395
|
function focusOutcome(record, requestId) {
|
|
@@ -781,7 +800,8 @@ function createRadarClient(options = {}) {
|
|
|
781
800
|
executable: request.executable,
|
|
782
801
|
argv: request.argv,
|
|
783
802
|
...request.cwd !== void 0 ? { cwd: request.cwd } : {},
|
|
784
|
-
...carriesContext && request.env !== void 0 ? { env: request.env } : {}
|
|
803
|
+
...carriesContext && request.env !== void 0 ? { env: request.env } : {},
|
|
804
|
+
...request.managed !== void 0 ? { managed: request.managed } : {}
|
|
785
805
|
},
|
|
786
806
|
requester
|
|
787
807
|
},
|
|
@@ -110,6 +110,77 @@ import {
|
|
|
110
110
|
writeSync
|
|
111
111
|
} from "node:fs";
|
|
112
112
|
import { dirname, join, parse, resolve, sep } from "node:path";
|
|
113
|
+
|
|
114
|
+
// extension/radar-session-marker.ts
|
|
115
|
+
var RADAR_LAUNCH_MARKER_TYPE = "pi-radar.launch-identity";
|
|
116
|
+
var RADAR_LAUNCH_MARKER_VERSION = 1;
|
|
117
|
+
var RADAR_MAX_EXECUTION_GENERATION = 4294967295;
|
|
118
|
+
var RADAR_MANAGED_SUBJECT_ENV = "PI_RADAR_MANAGED_SUBJECT_ID";
|
|
119
|
+
var RADAR_MANAGED_LAUNCH_ENV = "PI_RADAR_MANAGED_LAUNCH_ID";
|
|
120
|
+
var RADAR_MANAGED_GENERATION_ENV = "PI_RADAR_MANAGED_EXECUTION_GENERATION";
|
|
121
|
+
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;
|
|
122
|
+
function decodeRadarLaunchMarker(value) {
|
|
123
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
124
|
+
const record = value;
|
|
125
|
+
if (Object.keys(record).sort().join(",") !== "execution_generation,launch_id,subject_id,version") {
|
|
126
|
+
return void 0;
|
|
127
|
+
}
|
|
128
|
+
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) {
|
|
129
|
+
return void 0;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
version: RADAR_LAUNCH_MARKER_VERSION,
|
|
133
|
+
subject_id: record.subject_id,
|
|
134
|
+
launch_id: record.launch_id,
|
|
135
|
+
execution_generation: record.execution_generation
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function appendRadarLaunchMarker(appendEntry, marker) {
|
|
139
|
+
const validated = decodeRadarLaunchMarker(marker);
|
|
140
|
+
if (!validated) throw new Error("invalid Radar launch identity marker");
|
|
141
|
+
appendEntry(RADAR_LAUNCH_MARKER_TYPE, validated);
|
|
142
|
+
}
|
|
143
|
+
function latestRadarLaunchMarker(branch) {
|
|
144
|
+
let latest;
|
|
145
|
+
for (const entry of branch) {
|
|
146
|
+
if (entry.type === "custom" && entry.customType === RADAR_LAUNCH_MARKER_TYPE) {
|
|
147
|
+
latest = entry;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (!latest) return { status: "absent" };
|
|
151
|
+
const marker = decodeRadarLaunchMarker(latest.data);
|
|
152
|
+
return marker ? { status: "valid", marker } : { status: "invalid" };
|
|
153
|
+
}
|
|
154
|
+
function radarLaunchMarkerFromEnvironment(env = process.env) {
|
|
155
|
+
const rawGeneration = env[RADAR_MANAGED_GENERATION_ENV];
|
|
156
|
+
if (env[RADAR_MANAGED_SUBJECT_ENV] === void 0 || env[RADAR_MANAGED_LAUNCH_ENV] === void 0 || rawGeneration === void 0 || !/^[1-9][0-9]*$/.test(rawGeneration)) {
|
|
157
|
+
return void 0;
|
|
158
|
+
}
|
|
159
|
+
return decodeRadarLaunchMarker({
|
|
160
|
+
version: RADAR_LAUNCH_MARKER_VERSION,
|
|
161
|
+
subject_id: env[RADAR_MANAGED_SUBJECT_ENV],
|
|
162
|
+
launch_id: env[RADAR_MANAGED_LAUNCH_ENV],
|
|
163
|
+
execution_generation: Number(rawGeneration)
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
function activeRadarLaunchMarker(host) {
|
|
167
|
+
if (!host.sessionManager) return { status: "absent" };
|
|
168
|
+
return latestRadarLaunchMarker(host.sessionManager.getBranch());
|
|
169
|
+
}
|
|
170
|
+
function persistRadarLaunchMarker(host, env = process.env) {
|
|
171
|
+
const marker = radarLaunchMarkerFromEnvironment(env);
|
|
172
|
+
if (!marker) return "absent";
|
|
173
|
+
if (!host.appendEntry || !host.sessionManager) return "unsupported";
|
|
174
|
+
const current = latestRadarLaunchMarker(host.sessionManager.getBranch());
|
|
175
|
+
if (current.status === "valid" && sameMarker(current.marker, marker)) return "unchanged";
|
|
176
|
+
appendRadarLaunchMarker(host.appendEntry, marker);
|
|
177
|
+
return "written";
|
|
178
|
+
}
|
|
179
|
+
function sameMarker(a, b) {
|
|
180
|
+
return a.version === b.version && a.subject_id === b.subject_id && a.launch_id === b.launch_id && a.execution_generation === b.execution_generation;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// extension/radar-publication.ts
|
|
113
184
|
var RADAR_PUBLICATION_VERSION = 1;
|
|
114
185
|
var RADAR_LEASE_MS = 3e4;
|
|
115
186
|
var RADAR_HEARTBEAT_MS = 15e3;
|
|
@@ -417,15 +488,21 @@ function pendingAt(value, keys = PENDING_KEYS, codec = executionCodec) {
|
|
|
417
488
|
return { sequence, snapshot, lease_ms: lease, observed_at: observed };
|
|
418
489
|
}
|
|
419
490
|
var CONTEXT_PENDING_KEYS = "lease_ms,observed_at,sequence,session";
|
|
491
|
+
var CONTEXT_PENDING_MARKED_KEYS = "launch_identity,lease_ms,observed_at,sequence,session";
|
|
420
492
|
var CONTEXT_ACCEPTED_KEYS = "lease_ms,observed_at,sent_at,sequence,session";
|
|
493
|
+
var CONTEXT_ACCEPTED_MARKED_KEYS = "launch_identity,lease_ms,observed_at,sent_at,sequence,session";
|
|
421
494
|
function sessionAt(value) {
|
|
422
495
|
if (typeof value !== "object" || value === null) return void 0;
|
|
423
496
|
const found = value.session;
|
|
424
497
|
if (found === null) return null;
|
|
425
498
|
return typeof found === "string" ? found : void 0;
|
|
426
499
|
}
|
|
427
|
-
function
|
|
428
|
-
|
|
500
|
+
function sameLaunchIdentity(a, b) {
|
|
501
|
+
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;
|
|
502
|
+
}
|
|
503
|
+
function contextReportAt(value, keys, markedKeys) {
|
|
504
|
+
const marked = exactKeys(value, markedKeys);
|
|
505
|
+
if (!exactKeys(value, keys) && !marked) return void 0;
|
|
429
506
|
const sequence = numberAt(value, "sequence");
|
|
430
507
|
const lease = numberAt(value, "lease_ms");
|
|
431
508
|
const observed = textAt(value, "observed_at");
|
|
@@ -433,7 +510,15 @@ function contextReportAt(value, keys) {
|
|
|
433
510
|
if (sequence === void 0 || sequence < 1) return void 0;
|
|
434
511
|
if (lease === void 0 || observed === void 0 || session === void 0)
|
|
435
512
|
return void 0;
|
|
436
|
-
|
|
513
|
+
const marker = marked ? decodeRadarLaunchMarker(value.launch_identity) : void 0;
|
|
514
|
+
if (marked && marker === void 0) return void 0;
|
|
515
|
+
return {
|
|
516
|
+
sequence,
|
|
517
|
+
session,
|
|
518
|
+
lease_ms: lease,
|
|
519
|
+
observed_at: observed,
|
|
520
|
+
...marker !== void 0 ? { launch_identity: marker } : {}
|
|
521
|
+
};
|
|
437
522
|
}
|
|
438
523
|
function createChannelWriter(input) {
|
|
439
524
|
const { store, client, scheduler, recordName, channel, publisher } = input;
|
|
@@ -672,6 +757,7 @@ function createContextWriter(input) {
|
|
|
672
757
|
const { store, client, scheduler, recordName, publisher } = input;
|
|
673
758
|
let record = loadRecord();
|
|
674
759
|
let desired;
|
|
760
|
+
let desiredMarker;
|
|
675
761
|
let running = false;
|
|
676
762
|
let again = false;
|
|
677
763
|
let recovering = false;
|
|
@@ -697,9 +783,17 @@ function createContextWriter(input) {
|
|
|
697
783
|
const generation = numberAt(writer, "generation");
|
|
698
784
|
if (writer !== void 0 && (!exactKeys(writer, WRITER_KEYS) || !isUuid(handle) || generation === void 0))
|
|
699
785
|
return blank(agentId);
|
|
700
|
-
const pending = contextReportAt(
|
|
786
|
+
const pending = contextReportAt(
|
|
787
|
+
recordAt(value, "pending"),
|
|
788
|
+
CONTEXT_PENDING_KEYS,
|
|
789
|
+
CONTEXT_PENDING_MARKED_KEYS
|
|
790
|
+
);
|
|
701
791
|
const acceptedValue = recordAt(value, "accepted");
|
|
702
|
-
const accepted = contextReportAt(
|
|
792
|
+
const accepted = contextReportAt(
|
|
793
|
+
acceptedValue,
|
|
794
|
+
CONTEXT_ACCEPTED_KEYS,
|
|
795
|
+
CONTEXT_ACCEPTED_MARKED_KEYS
|
|
796
|
+
);
|
|
703
797
|
const sentAt = numberAt(acceptedValue, "sent_at");
|
|
704
798
|
return {
|
|
705
799
|
version: RADAR_PUBLICATION_VERSION,
|
|
@@ -759,7 +853,10 @@ function createContextWriter(input) {
|
|
|
759
853
|
sequence: pending.sequence,
|
|
760
854
|
lease_ms: pending.lease_ms,
|
|
761
855
|
observed_at: pending.observed_at,
|
|
762
|
-
context: {
|
|
856
|
+
context: {
|
|
857
|
+
session: pending.session,
|
|
858
|
+
...pending.launch_identity !== void 0 ? { launch_identity: pending.launch_identity } : {}
|
|
859
|
+
}
|
|
763
860
|
});
|
|
764
861
|
if (answer.ok === true) {
|
|
765
862
|
recovering = false;
|
|
@@ -790,12 +887,13 @@ function createContextWriter(input) {
|
|
|
790
887
|
return;
|
|
791
888
|
}
|
|
792
889
|
}
|
|
793
|
-
async function stage(session) {
|
|
890
|
+
async function stage(session, launchIdentity) {
|
|
794
891
|
const pending = {
|
|
795
892
|
sequence: record.sequence + 1,
|
|
796
893
|
session,
|
|
797
894
|
lease_ms: input.leaseMs,
|
|
798
|
-
observed_at: new Date(scheduler.now()).toISOString()
|
|
895
|
+
observed_at: new Date(scheduler.now()).toISOString(),
|
|
896
|
+
...launchIdentity !== void 0 ? { launch_identity: launchIdentity } : {}
|
|
799
897
|
};
|
|
800
898
|
record.pending = pending;
|
|
801
899
|
save();
|
|
@@ -810,11 +908,11 @@ function createContextWriter(input) {
|
|
|
810
908
|
if (desired === void 0) return;
|
|
811
909
|
if (!await ensureSubject()) return;
|
|
812
910
|
if (fenced) return;
|
|
813
|
-
if (record.writer === void 0 || record.accepted === void 0 || record.accepted.session !== desired) {
|
|
814
|
-
await stage(desired);
|
|
911
|
+
if (record.writer === void 0 || record.accepted === void 0 || record.accepted.session !== desired || !sameLaunchIdentity(record.accepted.launch_identity, desiredMarker)) {
|
|
912
|
+
await stage(desired, desiredMarker);
|
|
815
913
|
return;
|
|
816
914
|
}
|
|
817
|
-
if (heartbeatDue()) await stage(record.accepted.session);
|
|
915
|
+
if (heartbeatDue()) await stage(record.accepted.session, desiredMarker);
|
|
818
916
|
}
|
|
819
917
|
async function drain() {
|
|
820
918
|
if (running || stopped || fenced) return;
|
|
@@ -846,9 +944,11 @@ function createContextWriter(input) {
|
|
|
846
944
|
void drain().catch(() => void 0);
|
|
847
945
|
}
|
|
848
946
|
return {
|
|
849
|
-
update(session) {
|
|
850
|
-
if (fenced || session === desired)
|
|
947
|
+
update(session, launchIdentity) {
|
|
948
|
+
if (fenced || session === desired && sameLaunchIdentity(desiredMarker, launchIdentity))
|
|
949
|
+
return;
|
|
851
950
|
desired = session;
|
|
951
|
+
desiredMarker = launchIdentity;
|
|
852
952
|
if (stopped) return;
|
|
853
953
|
kick();
|
|
854
954
|
},
|
|
@@ -1260,11 +1360,11 @@ function createRadarExecutionAdapter(pi, options) {
|
|
|
1260
1360
|
...lastOutcome !== void 0 ? { last_outcome: lastOutcome } : {}
|
|
1261
1361
|
};
|
|
1262
1362
|
}
|
|
1263
|
-
function reportSession(context) {
|
|
1363
|
+
function reportSession(context, launchIdentity) {
|
|
1264
1364
|
const session = context?.sessionManager?.getSessionId?.();
|
|
1265
1365
|
if (!isUuid(session)) return;
|
|
1266
1366
|
try {
|
|
1267
|
-
publication.context.update(session);
|
|
1367
|
+
publication.context.update(session, launchIdentity);
|
|
1268
1368
|
} catch {
|
|
1269
1369
|
}
|
|
1270
1370
|
}
|
|
@@ -1328,8 +1428,38 @@ function createRadarExecutionAdapter(pi, options) {
|
|
|
1328
1428
|
})
|
|
1329
1429
|
);
|
|
1330
1430
|
on("session_start", (_event, context) => {
|
|
1431
|
+
const sessionHost = {
|
|
1432
|
+
appendEntry: pi.appendEntry ? (customType, data) => pi.appendEntry?.(customType, data) : void 0,
|
|
1433
|
+
sessionManager: context?.sessionManager
|
|
1434
|
+
};
|
|
1435
|
+
let launchIdentity;
|
|
1436
|
+
try {
|
|
1437
|
+
const result = persistRadarLaunchMarker(
|
|
1438
|
+
sessionHost,
|
|
1439
|
+
options.markerEnvironment
|
|
1440
|
+
);
|
|
1441
|
+
if (result === "written" || result === "unchanged") {
|
|
1442
|
+
const expected = radarLaunchMarkerFromEnvironment(options.markerEnvironment);
|
|
1443
|
+
const activeMarker = activeRadarLaunchMarker(sessionHost);
|
|
1444
|
+
if (expected && activeMarker.status === "valid" && JSON.stringify(activeMarker.marker) === JSON.stringify(expected)) {
|
|
1445
|
+
launchIdentity = activeMarker.marker;
|
|
1446
|
+
} else {
|
|
1447
|
+
options.diagnostic?.("Radar launch marker was not visible on the active Pi session branch");
|
|
1448
|
+
}
|
|
1449
|
+
} else if (result === "unsupported") {
|
|
1450
|
+
options.diagnostic?.("Pi host does not support Radar launch identity session entries");
|
|
1451
|
+
} else if (options.markerEnvironment && [
|
|
1452
|
+
"PI_RADAR_MANAGED_SUBJECT_ID",
|
|
1453
|
+
"PI_RADAR_MANAGED_LAUNCH_ID",
|
|
1454
|
+
"PI_RADAR_MANAGED_EXECUTION_GENERATION"
|
|
1455
|
+
].some((key) => options.markerEnvironment?.[key] !== void 0)) {
|
|
1456
|
+
options.diagnostic?.("incomplete or invalid Radar launch identity environment");
|
|
1457
|
+
}
|
|
1458
|
+
} catch (error) {
|
|
1459
|
+
options.diagnostic?.(`could not persist Radar launch marker: ${String(error)}`);
|
|
1460
|
+
}
|
|
1331
1461
|
rootSession = true;
|
|
1332
|
-
reportSession(context);
|
|
1462
|
+
reportSession(context, launchIdentity);
|
|
1333
1463
|
publishFacts(context);
|
|
1334
1464
|
publication.reopen();
|
|
1335
1465
|
active = context?.isIdle?.() === false;
|
|
@@ -63,7 +63,8 @@ async function resolveRadarProcessLocation(client, env = process.env) {
|
|
|
63
63
|
const backend = ready.value.backend;
|
|
64
64
|
if (backend === void 0 || backend === "none")
|
|
65
65
|
return { state: "absent", reason: "the daemon serves no physical backend" };
|
|
66
|
-
const
|
|
66
|
+
const instance = ready.value.instance ?? void 0;
|
|
67
|
+
const own = qualifiedOwnPane(env, backend, instance);
|
|
67
68
|
if ("absent" in own) return { state: "absent", reason: own.absent };
|
|
68
69
|
if ("pending" in own) return { state: "pending", reason: own.pending };
|
|
69
70
|
const paneId = own.pane;
|
|
@@ -95,7 +96,6 @@ async function resolveRadarProcessLocation(client, env = process.env) {
|
|
|
95
96
|
if ("ambiguous" in containment)
|
|
96
97
|
return { state: "pending", reason: containment.ambiguous };
|
|
97
98
|
const row = containment.pane;
|
|
98
|
-
const instance = ready.value.instance;
|
|
99
99
|
return {
|
|
100
100
|
state: "resolved",
|
|
101
101
|
location: {
|
|
@@ -15,6 +15,27 @@ import {
|
|
|
15
15
|
} from "node:fs";
|
|
16
16
|
import { dirname, join, parse, resolve, sep } from "node:path";
|
|
17
17
|
|
|
18
|
+
// extension/radar-session-marker.ts
|
|
19
|
+
var RADAR_LAUNCH_MARKER_VERSION = 1;
|
|
20
|
+
var RADAR_MAX_EXECUTION_GENERATION = 4294967295;
|
|
21
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
|
22
|
+
function decodeRadarLaunchMarker(value) {
|
|
23
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
24
|
+
const record = value;
|
|
25
|
+
if (Object.keys(record).sort().join(",") !== "execution_generation,launch_id,subject_id,version") {
|
|
26
|
+
return void 0;
|
|
27
|
+
}
|
|
28
|
+
if (record.version !== RADAR_LAUNCH_MARKER_VERSION || typeof record.subject_id !== "string" || !UUID.test(record.subject_id) || typeof record.launch_id !== "string" || !UUID.test(record.launch_id) || !Number.isSafeInteger(record.execution_generation) || record.execution_generation < 1 || record.execution_generation > RADAR_MAX_EXECUTION_GENERATION) {
|
|
29
|
+
return void 0;
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
version: RADAR_LAUNCH_MARKER_VERSION,
|
|
33
|
+
subject_id: record.subject_id,
|
|
34
|
+
launch_id: record.launch_id,
|
|
35
|
+
execution_generation: record.execution_generation
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
18
39
|
// extension/radar-client.ts
|
|
19
40
|
var RADAR_MAX_LINE_BYTES = 1024 * 1024;
|
|
20
41
|
var wallClock = { setTimeout, clearTimeout, setInterval, clearInterval };
|
|
@@ -32,9 +53,9 @@ function bounded(text, bytes) {
|
|
|
32
53
|
end -= 1;
|
|
33
54
|
return text.slice(0, end);
|
|
34
55
|
}
|
|
35
|
-
var
|
|
56
|
+
var UUID2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
36
57
|
function isUuid(value) {
|
|
37
|
-
return typeof value === "string" &&
|
|
58
|
+
return typeof value === "string" && UUID2.test(value);
|
|
38
59
|
}
|
|
39
60
|
var RADAR_MAX_BACKGROUND_TASKS = 64;
|
|
40
61
|
var RADAR_MAX_AWAITED = 64;
|
|
@@ -422,15 +443,21 @@ function pendingAt(value, keys = PENDING_KEYS, codec = executionCodec) {
|
|
|
422
443
|
return { sequence, snapshot, lease_ms: lease, observed_at: observed };
|
|
423
444
|
}
|
|
424
445
|
var CONTEXT_PENDING_KEYS = "lease_ms,observed_at,sequence,session";
|
|
446
|
+
var CONTEXT_PENDING_MARKED_KEYS = "launch_identity,lease_ms,observed_at,sequence,session";
|
|
425
447
|
var CONTEXT_ACCEPTED_KEYS = "lease_ms,observed_at,sent_at,sequence,session";
|
|
448
|
+
var CONTEXT_ACCEPTED_MARKED_KEYS = "launch_identity,lease_ms,observed_at,sent_at,sequence,session";
|
|
426
449
|
function sessionAt(value) {
|
|
427
450
|
if (typeof value !== "object" || value === null) return void 0;
|
|
428
451
|
const found = value.session;
|
|
429
452
|
if (found === null) return null;
|
|
430
453
|
return typeof found === "string" ? found : void 0;
|
|
431
454
|
}
|
|
432
|
-
function
|
|
433
|
-
|
|
455
|
+
function sameLaunchIdentity(a, b) {
|
|
456
|
+
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;
|
|
457
|
+
}
|
|
458
|
+
function contextReportAt(value, keys, markedKeys) {
|
|
459
|
+
const marked = exactKeys(value, markedKeys);
|
|
460
|
+
if (!exactKeys(value, keys) && !marked) return void 0;
|
|
434
461
|
const sequence = numberAt(value, "sequence");
|
|
435
462
|
const lease = numberAt(value, "lease_ms");
|
|
436
463
|
const observed = textAt(value, "observed_at");
|
|
@@ -438,7 +465,15 @@ function contextReportAt(value, keys) {
|
|
|
438
465
|
if (sequence === void 0 || sequence < 1) return void 0;
|
|
439
466
|
if (lease === void 0 || observed === void 0 || session === void 0)
|
|
440
467
|
return void 0;
|
|
441
|
-
|
|
468
|
+
const marker = marked ? decodeRadarLaunchMarker(value.launch_identity) : void 0;
|
|
469
|
+
if (marked && marker === void 0) return void 0;
|
|
470
|
+
return {
|
|
471
|
+
sequence,
|
|
472
|
+
session,
|
|
473
|
+
lease_ms: lease,
|
|
474
|
+
observed_at: observed,
|
|
475
|
+
...marker !== void 0 ? { launch_identity: marker } : {}
|
|
476
|
+
};
|
|
442
477
|
}
|
|
443
478
|
function createChannelWriter(input) {
|
|
444
479
|
const { store, client, scheduler, recordName, channel, publisher } = input;
|
|
@@ -677,6 +712,7 @@ function createContextWriter(input) {
|
|
|
677
712
|
const { store, client, scheduler, recordName, publisher } = input;
|
|
678
713
|
let record = loadRecord();
|
|
679
714
|
let desired;
|
|
715
|
+
let desiredMarker;
|
|
680
716
|
let running = false;
|
|
681
717
|
let again = false;
|
|
682
718
|
let recovering = false;
|
|
@@ -702,9 +738,17 @@ function createContextWriter(input) {
|
|
|
702
738
|
const generation = numberAt(writer, "generation");
|
|
703
739
|
if (writer !== void 0 && (!exactKeys(writer, WRITER_KEYS) || !isUuid(handle) || generation === void 0))
|
|
704
740
|
return blank(agentId);
|
|
705
|
-
const pending = contextReportAt(
|
|
741
|
+
const pending = contextReportAt(
|
|
742
|
+
recordAt(value, "pending"),
|
|
743
|
+
CONTEXT_PENDING_KEYS,
|
|
744
|
+
CONTEXT_PENDING_MARKED_KEYS
|
|
745
|
+
);
|
|
706
746
|
const acceptedValue = recordAt(value, "accepted");
|
|
707
|
-
const accepted = contextReportAt(
|
|
747
|
+
const accepted = contextReportAt(
|
|
748
|
+
acceptedValue,
|
|
749
|
+
CONTEXT_ACCEPTED_KEYS,
|
|
750
|
+
CONTEXT_ACCEPTED_MARKED_KEYS
|
|
751
|
+
);
|
|
708
752
|
const sentAt = numberAt(acceptedValue, "sent_at");
|
|
709
753
|
return {
|
|
710
754
|
version: RADAR_PUBLICATION_VERSION,
|
|
@@ -764,7 +808,10 @@ function createContextWriter(input) {
|
|
|
764
808
|
sequence: pending.sequence,
|
|
765
809
|
lease_ms: pending.lease_ms,
|
|
766
810
|
observed_at: pending.observed_at,
|
|
767
|
-
context: {
|
|
811
|
+
context: {
|
|
812
|
+
session: pending.session,
|
|
813
|
+
...pending.launch_identity !== void 0 ? { launch_identity: pending.launch_identity } : {}
|
|
814
|
+
}
|
|
768
815
|
});
|
|
769
816
|
if (answer.ok === true) {
|
|
770
817
|
recovering = false;
|
|
@@ -795,12 +842,13 @@ function createContextWriter(input) {
|
|
|
795
842
|
return;
|
|
796
843
|
}
|
|
797
844
|
}
|
|
798
|
-
async function stage(session) {
|
|
845
|
+
async function stage(session, launchIdentity) {
|
|
799
846
|
const pending = {
|
|
800
847
|
sequence: record.sequence + 1,
|
|
801
848
|
session,
|
|
802
849
|
lease_ms: input.leaseMs,
|
|
803
|
-
observed_at: new Date(scheduler.now()).toISOString()
|
|
850
|
+
observed_at: new Date(scheduler.now()).toISOString(),
|
|
851
|
+
...launchIdentity !== void 0 ? { launch_identity: launchIdentity } : {}
|
|
804
852
|
};
|
|
805
853
|
record.pending = pending;
|
|
806
854
|
save();
|
|
@@ -815,11 +863,11 @@ function createContextWriter(input) {
|
|
|
815
863
|
if (desired === void 0) return;
|
|
816
864
|
if (!await ensureSubject()) return;
|
|
817
865
|
if (fenced) return;
|
|
818
|
-
if (record.writer === void 0 || record.accepted === void 0 || record.accepted.session !== desired) {
|
|
819
|
-
await stage(desired);
|
|
866
|
+
if (record.writer === void 0 || record.accepted === void 0 || record.accepted.session !== desired || !sameLaunchIdentity(record.accepted.launch_identity, desiredMarker)) {
|
|
867
|
+
await stage(desired, desiredMarker);
|
|
820
868
|
return;
|
|
821
869
|
}
|
|
822
|
-
if (heartbeatDue()) await stage(record.accepted.session);
|
|
870
|
+
if (heartbeatDue()) await stage(record.accepted.session, desiredMarker);
|
|
823
871
|
}
|
|
824
872
|
async function drain() {
|
|
825
873
|
if (running || stopped || fenced) return;
|
|
@@ -851,9 +899,11 @@ function createContextWriter(input) {
|
|
|
851
899
|
void drain().catch(() => void 0);
|
|
852
900
|
}
|
|
853
901
|
return {
|
|
854
|
-
update(session) {
|
|
855
|
-
if (fenced || session === desired)
|
|
902
|
+
update(session, launchIdentity) {
|
|
903
|
+
if (fenced || session === desired && sameLaunchIdentity(desiredMarker, launchIdentity))
|
|
904
|
+
return;
|
|
856
905
|
desired = session;
|
|
906
|
+
desiredMarker = launchIdentity;
|
|
857
907
|
if (stopped) return;
|
|
858
908
|
kick();
|
|
859
909
|
},
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// extension/radar-runtime.ts
|
|
2
|
+
var RADAR_RUNTIME_REQUEST = "pi-radar/runtime-request";
|
|
3
|
+
var RADAR_RUNTIME_VERSION = 1;
|
|
4
|
+
function lazyRadarRuntime(start) {
|
|
5
|
+
let pending;
|
|
6
|
+
return {
|
|
7
|
+
version: RADAR_RUNTIME_VERSION,
|
|
8
|
+
start: () => pending ??= Promise.resolve().then(start)
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function provideRadarRuntime(events, handle) {
|
|
12
|
+
return events.on(RADAR_RUNTIME_REQUEST, (value) => {
|
|
13
|
+
if (!isRuntimeRequest(value)) return;
|
|
14
|
+
value.provide(handle);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function requestRadarRuntime(events) {
|
|
18
|
+
const handles = [];
|
|
19
|
+
events.emit(RADAR_RUNTIME_REQUEST, {
|
|
20
|
+
version: RADAR_RUNTIME_VERSION,
|
|
21
|
+
provide: (candidate) => {
|
|
22
|
+
if (isRadarRuntimeHandle(candidate)) handles.push(candidate);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
if (handles.length === 1) return { ok: true, handle: handles[0] };
|
|
26
|
+
return { ok: false, reason: handles.length === 0 ? "missing" : "duplicate" };
|
|
27
|
+
}
|
|
28
|
+
function isRuntimeRequest(value) {
|
|
29
|
+
return typeof value === "object" && value !== null && value.version === RADAR_RUNTIME_VERSION && typeof value.provide === "function";
|
|
30
|
+
}
|
|
31
|
+
function isRadarRuntimeHandle(value) {
|
|
32
|
+
if (typeof value !== "object" || value === null) return false;
|
|
33
|
+
const handle = value;
|
|
34
|
+
return handle.version === RADAR_RUNTIME_VERSION && typeof handle.start === "function";
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
RADAR_RUNTIME_REQUEST,
|
|
38
|
+
RADAR_RUNTIME_VERSION,
|
|
39
|
+
lazyRadarRuntime,
|
|
40
|
+
provideRadarRuntime,
|
|
41
|
+
requestRadarRuntime
|
|
42
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// extension/radar-session-marker.ts
|
|
2
|
+
var RADAR_LAUNCH_MARKER_TYPE = "pi-radar.launch-identity";
|
|
3
|
+
var RADAR_LAUNCH_MARKER_VERSION = 1;
|
|
4
|
+
var RADAR_MAX_EXECUTION_GENERATION = 4294967295;
|
|
5
|
+
var RADAR_MANAGED_SUBJECT_ENV = "PI_RADAR_MANAGED_SUBJECT_ID";
|
|
6
|
+
var RADAR_MANAGED_LAUNCH_ENV = "PI_RADAR_MANAGED_LAUNCH_ID";
|
|
7
|
+
var RADAR_MANAGED_GENERATION_ENV = "PI_RADAR_MANAGED_EXECUTION_GENERATION";
|
|
8
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
|
9
|
+
function decodeRadarLaunchMarker(value) {
|
|
10
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
11
|
+
const record = value;
|
|
12
|
+
if (Object.keys(record).sort().join(",") !== "execution_generation,launch_id,subject_id,version") {
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
if (record.version !== RADAR_LAUNCH_MARKER_VERSION || typeof record.subject_id !== "string" || !UUID.test(record.subject_id) || typeof record.launch_id !== "string" || !UUID.test(record.launch_id) || !Number.isSafeInteger(record.execution_generation) || record.execution_generation < 1 || record.execution_generation > RADAR_MAX_EXECUTION_GENERATION) {
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
version: RADAR_LAUNCH_MARKER_VERSION,
|
|
20
|
+
subject_id: record.subject_id,
|
|
21
|
+
launch_id: record.launch_id,
|
|
22
|
+
execution_generation: record.execution_generation
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function appendRadarLaunchMarker(appendEntry, marker) {
|
|
26
|
+
const validated = decodeRadarLaunchMarker(marker);
|
|
27
|
+
if (!validated) throw new Error("invalid Radar launch identity marker");
|
|
28
|
+
appendEntry(RADAR_LAUNCH_MARKER_TYPE, validated);
|
|
29
|
+
}
|
|
30
|
+
function latestRadarLaunchMarker(branch) {
|
|
31
|
+
let latest;
|
|
32
|
+
for (const entry of branch) {
|
|
33
|
+
if (entry.type === "custom" && entry.customType === RADAR_LAUNCH_MARKER_TYPE) {
|
|
34
|
+
latest = entry;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (!latest) return { status: "absent" };
|
|
38
|
+
const marker = decodeRadarLaunchMarker(latest.data);
|
|
39
|
+
return marker ? { status: "valid", marker } : { status: "invalid" };
|
|
40
|
+
}
|
|
41
|
+
function radarLaunchMarkerFromEnvironment(env = process.env) {
|
|
42
|
+
const rawGeneration = env[RADAR_MANAGED_GENERATION_ENV];
|
|
43
|
+
if (env[RADAR_MANAGED_SUBJECT_ENV] === void 0 || env[RADAR_MANAGED_LAUNCH_ENV] === void 0 || rawGeneration === void 0 || !/^[1-9][0-9]*$/.test(rawGeneration)) {
|
|
44
|
+
return void 0;
|
|
45
|
+
}
|
|
46
|
+
return decodeRadarLaunchMarker({
|
|
47
|
+
version: RADAR_LAUNCH_MARKER_VERSION,
|
|
48
|
+
subject_id: env[RADAR_MANAGED_SUBJECT_ENV],
|
|
49
|
+
launch_id: env[RADAR_MANAGED_LAUNCH_ENV],
|
|
50
|
+
execution_generation: Number(rawGeneration)
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function activeRadarLaunchMarker(host) {
|
|
54
|
+
if (!host.sessionManager) return { status: "absent" };
|
|
55
|
+
return latestRadarLaunchMarker(host.sessionManager.getBranch());
|
|
56
|
+
}
|
|
57
|
+
function persistRadarLaunchMarker(host, env = process.env) {
|
|
58
|
+
const marker = radarLaunchMarkerFromEnvironment(env);
|
|
59
|
+
if (!marker) return "absent";
|
|
60
|
+
if (!host.appendEntry || !host.sessionManager) return "unsupported";
|
|
61
|
+
const current = latestRadarLaunchMarker(host.sessionManager.getBranch());
|
|
62
|
+
if (current.status === "valid" && sameMarker(current.marker, marker)) return "unchanged";
|
|
63
|
+
appendRadarLaunchMarker(host.appendEntry, marker);
|
|
64
|
+
return "written";
|
|
65
|
+
}
|
|
66
|
+
function sameMarker(a, b) {
|
|
67
|
+
return a.version === b.version && a.subject_id === b.subject_id && a.launch_id === b.launch_id && a.execution_generation === b.execution_generation;
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
RADAR_LAUNCH_MARKER_TYPE,
|
|
71
|
+
RADAR_LAUNCH_MARKER_VERSION,
|
|
72
|
+
RADAR_MANAGED_GENERATION_ENV,
|
|
73
|
+
RADAR_MANAGED_LAUNCH_ENV,
|
|
74
|
+
RADAR_MANAGED_SUBJECT_ENV,
|
|
75
|
+
RADAR_MAX_EXECUTION_GENERATION,
|
|
76
|
+
activeRadarLaunchMarker,
|
|
77
|
+
appendRadarLaunchMarker,
|
|
78
|
+
decodeRadarLaunchMarker,
|
|
79
|
+
latestRadarLaunchMarker,
|
|
80
|
+
persistRadarLaunchMarker,
|
|
81
|
+
radarLaunchMarkerFromEnvironment
|
|
82
|
+
};
|