pi-radar 0.3.0 → 0.4.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 +4 -2
- package/dist/extension/baseline.js +227 -402
- package/dist/extension/radar-client.js +53 -13
- package/dist/extension/radar-communication.js +5 -5
- package/dist/extension/radar-execution.js +168 -383
- package/dist/extension/radar-launch.js +13 -13
- package/dist/extension/radar-publication.js +160 -299
- package/dist/index.js +239 -414
- package/extension/generated-contracts.ts +1249 -13
- package/extension/radar-client.ts +92 -29
- package/extension/radar-communication.ts +5 -5
- package/extension/radar-execution.ts +11 -58
- package/extension/radar-launch.ts +21 -21
- package/extension/radar-publication.ts +209 -412
- package/package.json +3 -4
- package/schema/public/v1/handoff-publish-params.deserialize.schema.json +2 -2
- package/schema/public/v1/handoff-publish-params.serialize.schema.json +2 -2
- package/schema/public/v1/handoff-receipt.deserialize.schema.json +4 -4
- package/schema/public/v1/handoff-receipt.serialize.schema.json +4 -4
- package/schema/public/v1/handoff.deserialize.schema.json +4 -4
- package/schema/public/v1/handoff.serialize.schema.json +4 -4
- package/schema/public/v1/observation-get-params.deserialize.schema.json +46 -0
- package/schema/public/v1/observation-get-params.serialize.schema.json +52 -0
- package/schema/public/v1/observation-get-response.deserialize.schema.json +2731 -0
- package/schema/public/v1/observation-get-response.serialize.schema.json +2862 -0
- package/schema/public/v1/observation-snapshot.deserialize.schema.json +2616 -0
- package/schema/public/v1/observation-snapshot.serialize.schema.json +2741 -0
- package/schema/public/v1/presence-answer.deserialize.schema.json +40 -0
- package/schema/public/v1/presence-answer.serialize.schema.json +40 -0
- package/schema/public/v1/presence-release.deserialize.schema.json +51 -0
- package/schema/public/v1/presence-release.serialize.schema.json +52 -0
- package/schema/public/v1/presence-report.deserialize.schema.json +149 -0
- package/schema/public/v1/presence-report.serialize.schema.json +157 -0
- package/extension/radar-session-marker.ts +0 -146
- package/schema/public/v1/launch-identity-marker.deserialize.schema.json +0 -32
- package/schema/public/v1/launch-identity-marker.serialize.schema.json +0 -32
|
@@ -110,77 +110,7 @@ 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
|
+
var DUPLICATE_SESSION_DIAGNOSTIC = "Radar rejected this session claim as a duplicate; reload or resume the session before retrying";
|
|
184
114
|
var RADAR_PUBLICATION_VERSION = 1;
|
|
185
115
|
var RADAR_LEASE_MS = 3e4;
|
|
186
116
|
var RADAR_HEARTBEAT_MS = 15e3;
|
|
@@ -205,7 +135,7 @@ function readProcessBirth() {
|
|
|
205
135
|
return void 0;
|
|
206
136
|
}
|
|
207
137
|
}
|
|
208
|
-
var RECORD_NAME = /^(?:subjects|channels|context|bindings)\/[a-z0-9-]{1,64}\.json$/;
|
|
138
|
+
var RECORD_NAME = /^(?:registrations|presence|subjects|channels|context|bindings)\/[a-z0-9-]{1,64}\.json$/;
|
|
209
139
|
var MAX_RECORD_BYTES = 64 * 1024;
|
|
210
140
|
var PRIVATE_MODE = 448;
|
|
211
141
|
var RECORD_MODE = 384;
|
|
@@ -487,39 +417,6 @@ function pendingAt(value, keys = PENDING_KEYS, codec = executionCodec) {
|
|
|
487
417
|
return void 0;
|
|
488
418
|
return { sequence, snapshot, lease_ms: lease, observed_at: observed };
|
|
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";
|
|
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";
|
|
494
|
-
function sessionAt(value) {
|
|
495
|
-
if (typeof value !== "object" || value === null) return void 0;
|
|
496
|
-
const found = value.session;
|
|
497
|
-
if (found === null) return null;
|
|
498
|
-
return typeof found === "string" ? found : void 0;
|
|
499
|
-
}
|
|
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;
|
|
506
|
-
const sequence = numberAt(value, "sequence");
|
|
507
|
-
const lease = numberAt(value, "lease_ms");
|
|
508
|
-
const observed = textAt(value, "observed_at");
|
|
509
|
-
const session = sessionAt(value);
|
|
510
|
-
if (sequence === void 0 || sequence < 1) return void 0;
|
|
511
|
-
if (lease === void 0 || observed === void 0 || session === void 0)
|
|
512
|
-
return void 0;
|
|
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
|
-
};
|
|
522
|
-
}
|
|
523
420
|
function createChannelWriter(input) {
|
|
524
421
|
const { store, client, scheduler, recordName, channel, publisher } = input;
|
|
525
422
|
const codec = input.codec ?? executionCodec;
|
|
@@ -535,7 +432,7 @@ function createChannelWriter(input) {
|
|
|
535
432
|
function blank(agentId) {
|
|
536
433
|
return {
|
|
537
434
|
version: RADAR_PUBLICATION_VERSION,
|
|
538
|
-
|
|
435
|
+
registration_id: agentId,
|
|
539
436
|
channel,
|
|
540
437
|
publisher,
|
|
541
438
|
sequence: 0
|
|
@@ -543,7 +440,7 @@ function createChannelWriter(input) {
|
|
|
543
440
|
}
|
|
544
441
|
function loadRecord() {
|
|
545
442
|
const value = store.read(recordName);
|
|
546
|
-
const agentId = textAt(value, "
|
|
443
|
+
const agentId = textAt(value, "registration_id");
|
|
547
444
|
const storedPublisher = recordAt(value, "publisher");
|
|
548
445
|
if (numberAt(value, "version") !== RADAR_PUBLICATION_VERSION || agentId === void 0 || !isUuid(agentId) || textAt(value, "channel") !== channel || textAt(storedPublisher, "source") !== publisher.source || textAt(storedPublisher, "incarnation") !== publisher.incarnation || textAt(storedPublisher, "reporting_owner") !== publisher.reporting_owner)
|
|
549
446
|
return blank("");
|
|
@@ -558,7 +455,7 @@ function createChannelWriter(input) {
|
|
|
558
455
|
const sentAt = numberAt(acceptedValue, "sent_at");
|
|
559
456
|
return {
|
|
560
457
|
version: RADAR_PUBLICATION_VERSION,
|
|
561
|
-
|
|
458
|
+
registration_id: agentId,
|
|
562
459
|
channel,
|
|
563
460
|
publisher,
|
|
564
461
|
...handle !== void 0 && generation !== void 0 ? { writer: { handle, generation } } : {},
|
|
@@ -599,12 +496,12 @@ function createChannelWriter(input) {
|
|
|
599
496
|
if (record.writer !== void 0) return true;
|
|
600
497
|
const agentId = await input.subject(recovering);
|
|
601
498
|
if (agentId === void 0) return false;
|
|
602
|
-
if (record.
|
|
499
|
+
if (record.registration_id !== agentId) {
|
|
603
500
|
record = blank(agentId);
|
|
604
501
|
save();
|
|
605
502
|
}
|
|
606
503
|
const answer = await client.acquire({
|
|
607
|
-
|
|
504
|
+
registration_id: agentId,
|
|
608
505
|
channel,
|
|
609
506
|
publisher
|
|
610
507
|
});
|
|
@@ -634,7 +531,7 @@ function createChannelWriter(input) {
|
|
|
634
531
|
const writer = record.writer;
|
|
635
532
|
if (writer === void 0) return;
|
|
636
533
|
const answer = await client.publish({
|
|
637
|
-
|
|
534
|
+
registration_id: record.registration_id,
|
|
638
535
|
channel,
|
|
639
536
|
writer_handle: writer.handle,
|
|
640
537
|
sequence: pending.sequence,
|
|
@@ -728,9 +625,9 @@ function createChannelWriter(input) {
|
|
|
728
625
|
stopPump();
|
|
729
626
|
const writer = record.writer;
|
|
730
627
|
if (writer === void 0) return;
|
|
731
|
-
const agentId = record.
|
|
628
|
+
const agentId = record.registration_id;
|
|
732
629
|
try {
|
|
733
|
-
void client.retire({
|
|
630
|
+
void client.retire({ registration_id: agentId, channel, writer_handle: writer.handle }).then((answer) => {
|
|
734
631
|
if (answer.ok === true) {
|
|
735
632
|
try {
|
|
736
633
|
record.retired_at = new Date(scheduler.now()).toISOString();
|
|
@@ -753,214 +650,127 @@ function createChannelWriter(input) {
|
|
|
753
650
|
diagnostic: () => record.diagnostic
|
|
754
651
|
};
|
|
755
652
|
}
|
|
756
|
-
function
|
|
757
|
-
const { store, client, scheduler, recordName
|
|
758
|
-
let
|
|
759
|
-
let
|
|
760
|
-
let
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
function blank(agentId) {
|
|
768
|
-
return {
|
|
769
|
-
version: RADAR_PUBLICATION_VERSION,
|
|
770
|
-
agent_id: agentId,
|
|
771
|
-
publisher,
|
|
772
|
-
sequence: 0
|
|
773
|
-
};
|
|
774
|
-
}
|
|
775
|
-
function loadRecord() {
|
|
776
|
-
const value = store.read(recordName);
|
|
777
|
-
const agentId = textAt(value, "agent_id");
|
|
778
|
-
const storedPublisher = recordAt(value, "publisher");
|
|
779
|
-
if (numberAt(value, "version") !== RADAR_PUBLICATION_VERSION || agentId === void 0 || !isUuid(agentId) && agentId !== "" || textAt(storedPublisher, "source") !== publisher.source || textAt(storedPublisher, "incarnation") !== publisher.incarnation || textAt(storedPublisher, "reporting_owner") !== publisher.reporting_owner)
|
|
780
|
-
return blank("");
|
|
781
|
-
const writer = recordAt(value, "writer");
|
|
782
|
-
const handle = textAt(writer, "handle");
|
|
783
|
-
const generation = numberAt(writer, "generation");
|
|
784
|
-
if (writer !== void 0 && (!exactKeys(writer, WRITER_KEYS) || !isUuid(handle) || generation === void 0))
|
|
785
|
-
return blank(agentId);
|
|
786
|
-
const pending = contextReportAt(
|
|
787
|
-
recordAt(value, "pending"),
|
|
788
|
-
CONTEXT_PENDING_KEYS,
|
|
789
|
-
CONTEXT_PENDING_MARKED_KEYS
|
|
790
|
-
);
|
|
791
|
-
const acceptedValue = recordAt(value, "accepted");
|
|
792
|
-
const accepted = contextReportAt(
|
|
793
|
-
acceptedValue,
|
|
794
|
-
CONTEXT_ACCEPTED_KEYS,
|
|
795
|
-
CONTEXT_ACCEPTED_MARKED_KEYS
|
|
796
|
-
);
|
|
797
|
-
const sentAt = numberAt(acceptedValue, "sent_at");
|
|
798
|
-
return {
|
|
799
|
-
version: RADAR_PUBLICATION_VERSION,
|
|
800
|
-
agent_id: agentId,
|
|
801
|
-
publisher,
|
|
802
|
-
...handle !== void 0 && generation !== void 0 ? { writer: { handle, generation } } : {},
|
|
803
|
-
sequence: numberAt(value, "sequence") ?? 0,
|
|
804
|
-
...pending !== void 0 ? { pending } : {},
|
|
805
|
-
...accepted !== void 0 && sentAt !== void 0 ? { accepted: { ...accepted, sent_at: sentAt } } : {},
|
|
806
|
-
...textAt(value, "diagnostic") !== void 0 ? { diagnostic: textAt(value, "diagnostic") } : {}
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
function note(message) {
|
|
810
|
-
if (record.diagnostic !== void 0) return;
|
|
811
|
-
record.diagnostic = message.slice(0, 256);
|
|
812
|
-
save();
|
|
653
|
+
function createPresenceReporter(input) {
|
|
654
|
+
const { store, client, scheduler, recordName } = input;
|
|
655
|
+
let state;
|
|
656
|
+
let standing;
|
|
657
|
+
let diagnostic;
|
|
658
|
+
try {
|
|
659
|
+
const saved = store.read(recordName);
|
|
660
|
+
if (saved?.version === 2 && isUuid(saved.claim_id) && isUuid(saved.session) && Array.isArray(saved.releases))
|
|
661
|
+
state = saved;
|
|
662
|
+
} catch (error) {
|
|
663
|
+
diagnostic = `presence storage unavailable: ${String(error)}`;
|
|
813
664
|
}
|
|
665
|
+
let timer;
|
|
666
|
+
let stopped = false;
|
|
667
|
+
let running;
|
|
814
668
|
function save() {
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
function startPump() {
|
|
823
|
-
if (pump !== void 0 || stopped || fenced) return;
|
|
824
|
-
pump = scheduler.setInterval(() => kick(), input.heartbeatMs);
|
|
825
|
-
pump.unref?.();
|
|
826
|
-
}
|
|
827
|
-
function stopPump() {
|
|
828
|
-
if (pump === void 0) return;
|
|
829
|
-
scheduler.clearInterval(pump);
|
|
830
|
-
pump = void 0;
|
|
669
|
+
try {
|
|
670
|
+
if (state) store.write(recordName, state);
|
|
671
|
+
return true;
|
|
672
|
+
} catch (error) {
|
|
673
|
+
diagnostic = `presence storage unavailable: ${String(error)}`;
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
831
676
|
}
|
|
832
|
-
async function
|
|
833
|
-
if (
|
|
834
|
-
const
|
|
835
|
-
if (
|
|
836
|
-
|
|
837
|
-
|
|
677
|
+
async function releasePending() {
|
|
678
|
+
if (!state) return;
|
|
679
|
+
const registration = await input.registration();
|
|
680
|
+
if (!registration?.process) return;
|
|
681
|
+
for (const release of [...state.releases]) {
|
|
682
|
+
const answer = await client.releasePresence({ ...release, process: registration.process });
|
|
683
|
+
if (!answer.ok) return;
|
|
684
|
+
state.releases = state.releases.filter((r) => r.claim_id !== release.claim_id);
|
|
838
685
|
save();
|
|
839
686
|
}
|
|
840
|
-
return true;
|
|
841
687
|
}
|
|
842
|
-
function
|
|
843
|
-
|
|
688
|
+
function schedule() {
|
|
689
|
+
if (stopped || timer) return;
|
|
690
|
+
timer = scheduler.setTimeout(() => {
|
|
691
|
+
timer = void 0;
|
|
692
|
+
void ensure();
|
|
693
|
+
}, input.heartbeatMs);
|
|
694
|
+
timer.unref?.();
|
|
844
695
|
}
|
|
845
|
-
async function
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
696
|
+
async function ensure() {
|
|
697
|
+
if (running) return running;
|
|
698
|
+
if (stopped || !state) return void 0;
|
|
699
|
+
const claim = state;
|
|
700
|
+
if (!save()) return void 0;
|
|
701
|
+
running = (async () => {
|
|
702
|
+
await releasePending();
|
|
703
|
+
const registration = await input.registration();
|
|
704
|
+
if (!registration?.process || state !== claim || stopped) return void 0;
|
|
705
|
+
const answer = await client.presence({
|
|
706
|
+
...registration,
|
|
707
|
+
process: registration.process,
|
|
708
|
+
session_id: claim.session,
|
|
709
|
+
claim_id: claim.claim_id,
|
|
710
|
+
...claim.registration_id ? { registration_id: claim.registration_id } : {},
|
|
711
|
+
...claim.subject_id ? { subject_id: claim.subject_id } : {}
|
|
712
|
+
});
|
|
713
|
+
if (state !== claim) return void 0;
|
|
714
|
+
if (!answer.ok) {
|
|
715
|
+
if (!["absent", "timeout", "transport"].includes(answer.code)) diagnostic = answer.message;
|
|
716
|
+
return void 0;
|
|
859
717
|
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
handle: answer.value.writer.handle,
|
|
865
|
-
generation: answer.value.writer.generation
|
|
866
|
-
};
|
|
867
|
-
record.sequence = Math.max(record.sequence, answer.value.writer.sequence);
|
|
868
|
-
record.accepted = {
|
|
869
|
-
...pending,
|
|
870
|
-
sent_at: answer.value.warning === void 0 ? scheduler.now() : record.accepted?.sent_at ?? scheduler.now()
|
|
871
|
-
};
|
|
872
|
-
record.pending = void 0;
|
|
873
|
-
save();
|
|
874
|
-
return;
|
|
875
|
-
}
|
|
876
|
-
if (answer.code === "refused" || answer.code === "bad_params" || // A daemon that speaks the registry but does not serve this record would
|
|
877
|
-
// refuse every retry the same way; one diagnostic beats a silent retry
|
|
878
|
-
// loop once per heartbeat.
|
|
879
|
-
answer.code === "unknown_method") {
|
|
880
|
-
fence(answer.message);
|
|
881
|
-
return;
|
|
882
|
-
}
|
|
883
|
-
if (answer.code === "not_found") {
|
|
884
|
-
record = blank("");
|
|
885
|
-
recovering = true;
|
|
718
|
+
claim.registration_id = answer.value.registration_id;
|
|
719
|
+
claim.subject_id = answer.value.standing === "granted" ? answer.value.subject_id ?? void 0 : void 0;
|
|
720
|
+
standing = answer.value.standing === "granted" ? { state: "managed", subject_id: claim.subject_id } : answer.value.standing === "duplicate" ? { state: "duplicate" } : answer.value.standing === "pending" ? { state: "pending" } : { state: "unmanaged" };
|
|
721
|
+
diagnostic = answer.value.standing === "duplicate" ? DUPLICATE_SESSION_DIAGNOSTIC : void 0;
|
|
886
722
|
save();
|
|
887
|
-
return;
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
async function stage(session, launchIdentity) {
|
|
891
|
-
const pending = {
|
|
892
|
-
sequence: record.sequence + 1,
|
|
893
|
-
session,
|
|
894
|
-
lease_ms: input.leaseMs,
|
|
895
|
-
observed_at: new Date(scheduler.now()).toISOString(),
|
|
896
|
-
...launchIdentity !== void 0 ? { launch_identity: launchIdentity } : {}
|
|
897
|
-
};
|
|
898
|
-
record.pending = pending;
|
|
899
|
-
save();
|
|
900
|
-
await send(pending);
|
|
901
|
-
}
|
|
902
|
-
async function drainOnce() {
|
|
903
|
-
if (fenced) return;
|
|
904
|
-
if (record.pending !== void 0) {
|
|
905
|
-
await send(record.pending);
|
|
906
|
-
return;
|
|
907
|
-
}
|
|
908
|
-
if (desired === void 0) return;
|
|
909
|
-
if (!await ensureSubject()) return;
|
|
910
|
-
if (fenced) return;
|
|
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);
|
|
913
|
-
return;
|
|
914
|
-
}
|
|
915
|
-
if (heartbeatDue()) await stage(record.accepted.session, desiredMarker);
|
|
916
|
-
}
|
|
917
|
-
async function drain() {
|
|
918
|
-
if (running || stopped || fenced) return;
|
|
919
|
-
running = true;
|
|
723
|
+
return claim.registration_id;
|
|
724
|
+
})();
|
|
920
725
|
try {
|
|
921
|
-
await
|
|
726
|
+
return await running;
|
|
922
727
|
} catch (error) {
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
try {
|
|
926
|
-
note(`context storage failure: ${String(error).slice(0, 180)}`);
|
|
927
|
-
} catch {
|
|
928
|
-
}
|
|
728
|
+
diagnostic = `presence unavailable: ${String(error)}`;
|
|
729
|
+
return void 0;
|
|
929
730
|
} finally {
|
|
930
|
-
running =
|
|
931
|
-
if (
|
|
932
|
-
|
|
933
|
-
kick();
|
|
934
|
-
}
|
|
731
|
+
running = void 0;
|
|
732
|
+
if (!stopped && state !== claim) void ensure();
|
|
733
|
+
else schedule();
|
|
935
734
|
}
|
|
936
735
|
}
|
|
937
|
-
function kick() {
|
|
938
|
-
if (stopped || fenced) return;
|
|
939
|
-
startPump();
|
|
940
|
-
if (running) {
|
|
941
|
-
again = true;
|
|
942
|
-
return;
|
|
943
|
-
}
|
|
944
|
-
void drain().catch(() => void 0);
|
|
945
|
-
}
|
|
946
736
|
return {
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
737
|
+
report(session) {
|
|
738
|
+
const releases = [...state?.releases ?? []];
|
|
739
|
+
if (state?.registration_id && !releases.some((release) => release.claim_id === state.claim_id)) {
|
|
740
|
+
releases.push({ registration_id: state.registration_id, claim_id: state.claim_id });
|
|
741
|
+
}
|
|
742
|
+
standing = void 0;
|
|
743
|
+
state = { version: 2, session, claim_id: randomUUID(), releases };
|
|
744
|
+
save();
|
|
745
|
+
stopped = false;
|
|
746
|
+
void ensure();
|
|
747
|
+
},
|
|
748
|
+
ensure,
|
|
749
|
+
async release() {
|
|
750
|
+
const releasing = state;
|
|
751
|
+
standing = void 0;
|
|
752
|
+
stopped = true;
|
|
753
|
+
if (timer) scheduler.clearTimeout(timer);
|
|
754
|
+
timer = void 0;
|
|
755
|
+
if (running) await running;
|
|
756
|
+
if (state === releasing) standing = void 0;
|
|
757
|
+
if (releasing?.registration_id && !releasing.releases.some((r) => r.claim_id === releasing.claim_id)) {
|
|
758
|
+
releasing.releases.push({ registration_id: releasing.registration_id, claim_id: releasing.claim_id });
|
|
759
|
+
if (!save()) return;
|
|
760
|
+
}
|
|
761
|
+
await releasePending();
|
|
954
762
|
},
|
|
955
763
|
stop() {
|
|
956
764
|
stopped = true;
|
|
957
|
-
|
|
765
|
+
if (timer) scheduler.clearTimeout(timer);
|
|
766
|
+
timer = void 0;
|
|
958
767
|
},
|
|
959
768
|
reopen() {
|
|
960
769
|
stopped = false;
|
|
961
|
-
|
|
770
|
+
void ensure();
|
|
962
771
|
},
|
|
963
|
-
|
|
772
|
+
management: () => standing,
|
|
773
|
+
diagnostic: () => diagnostic
|
|
964
774
|
};
|
|
965
775
|
}
|
|
966
776
|
var STORE_IDENTITY = /* @__PURE__ */ new WeakMap();
|
|
@@ -989,10 +799,13 @@ function unavailablePublication(error) {
|
|
|
989
799
|
};
|
|
990
800
|
return {
|
|
991
801
|
execution: writer,
|
|
992
|
-
|
|
993
|
-
|
|
802
|
+
presence: {
|
|
803
|
+
report: () => void 0,
|
|
804
|
+
ensure: async () => void 0,
|
|
805
|
+
release: async () => void 0,
|
|
994
806
|
stop: () => void 0,
|
|
995
807
|
reopen: () => void 0,
|
|
808
|
+
management: () => void 0,
|
|
996
809
|
diagnostic: () => diagnostic
|
|
997
810
|
},
|
|
998
811
|
facts: { ...writer, update: () => void 0 },
|
|
@@ -1019,33 +832,29 @@ function createRadarPublicationInner(options) {
|
|
|
1019
832
|
cached.reopen();
|
|
1020
833
|
return cached;
|
|
1021
834
|
}
|
|
1022
|
-
const
|
|
835
|
+
const registrationName = `registrations/${createHash("sha256").update(`${options.registration.source}\0${incarnation}`).digest("hex").slice(0, 32)}.json`;
|
|
1023
836
|
const channelRecord = (channel, target) => `channels/${createHash("sha256").update(`${channel}\0${incarnation}\0${target ?? ""}`).digest("hex").slice(0, 32)}.json`;
|
|
1024
|
-
const
|
|
1025
|
-
function
|
|
1026
|
-
const value = store.read(
|
|
837
|
+
const presenceRecord = `presence/${createHash("sha256").update(`presence\0${incarnation}`).digest("hex").slice(0, 32)}.json`;
|
|
838
|
+
function readRegistration() {
|
|
839
|
+
const value = store.read(registrationName);
|
|
1027
840
|
const registration = recordAt(value, "registration");
|
|
1028
841
|
if (numberAt(value, "version") !== RADAR_PUBLICATION_VERSION || textAt(registration, "source") !== options.registration.source || textAt(registration, "incarnation") !== incarnation)
|
|
1029
842
|
return void 0;
|
|
1030
|
-
const storedAgentId = textAt(value, "
|
|
843
|
+
const storedAgentId = textAt(value, "registration_id");
|
|
1031
844
|
return {
|
|
1032
845
|
version: RADAR_PUBLICATION_VERSION,
|
|
1033
846
|
registration,
|
|
1034
|
-
...storedAgentId !== void 0 && isUuid(storedAgentId) ? {
|
|
847
|
+
...storedAgentId !== void 0 && isUuid(storedAgentId) ? { registration_id: storedAgentId } : {}
|
|
1035
848
|
};
|
|
1036
849
|
}
|
|
1037
|
-
const stored =
|
|
1038
|
-
let
|
|
850
|
+
const stored = readRegistration();
|
|
851
|
+
let processRecord = stored;
|
|
1039
852
|
let registrationDiagnostic;
|
|
1040
853
|
let registrationRecord;
|
|
1041
854
|
async function ensureRegistration() {
|
|
1042
|
-
if (
|
|
855
|
+
if (processRecord !== void 0) return processRecord;
|
|
1043
856
|
registrationRecord ??= (async () => {
|
|
1044
857
|
const resolution = options.location ? await options.location() : { state: "absent", reason: "this process publishes no location" };
|
|
1045
|
-
if (resolution.state === "pending") {
|
|
1046
|
-
registrationDiagnostic = `location bootstrap pending: ${resolution.reason}`.slice(0, 256);
|
|
1047
|
-
return void 0;
|
|
1048
|
-
}
|
|
1049
858
|
const built = {
|
|
1050
859
|
version: RADAR_PUBLICATION_VERSION,
|
|
1051
860
|
registration: {
|
|
@@ -1056,13 +865,13 @@ function createRadarPublicationInner(options) {
|
|
|
1056
865
|
}
|
|
1057
866
|
};
|
|
1058
867
|
try {
|
|
1059
|
-
store.write(
|
|
868
|
+
store.write(registrationName, built);
|
|
1060
869
|
} catch (error) {
|
|
1061
870
|
registrationDiagnostic ??= `private storage unavailable: ${String(error)}`.slice(0, 256);
|
|
1062
871
|
return void 0;
|
|
1063
872
|
}
|
|
1064
873
|
registrationDiagnostic = void 0;
|
|
1065
|
-
|
|
874
|
+
processRecord = built;
|
|
1066
875
|
return built;
|
|
1067
876
|
})();
|
|
1068
877
|
try {
|
|
@@ -1071,11 +880,10 @@ function createRadarPublicationInner(options) {
|
|
|
1071
880
|
registrationRecord = void 0;
|
|
1072
881
|
}
|
|
1073
882
|
}
|
|
1074
|
-
let agentId = stored?.
|
|
883
|
+
let agentId = stored?.registration_id;
|
|
1075
884
|
let subjectReady;
|
|
1076
885
|
async function ensureSubject(recheck) {
|
|
1077
|
-
if (recheck
|
|
1078
|
-
if (agentId !== void 0) return agentId;
|
|
886
|
+
if (!recheck && agentId !== void 0) return agentId;
|
|
1079
887
|
subjectReady ??= (async () => {
|
|
1080
888
|
const ready = await client.ping();
|
|
1081
889
|
if (ready.ok === false) {
|
|
@@ -1085,18 +893,11 @@ function createRadarPublicationInner(options) {
|
|
|
1085
893
|
}
|
|
1086
894
|
const record = await ensureRegistration();
|
|
1087
895
|
if (record === void 0) return void 0;
|
|
1088
|
-
const
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
if (answer.code !== "absent" && answer.code !== "timeout" && answer.code !== "transport")
|
|
1094
|
-
registrationDiagnostic ??= `subject registration refused: ${answer.message}`.slice(0, 256);
|
|
1095
|
-
return void 0;
|
|
1096
|
-
}
|
|
1097
|
-
agentId = answer.value.agent_id;
|
|
1098
|
-
subject = { ...record, agent_id: agentId };
|
|
1099
|
-
store.write(subjectName, subject);
|
|
896
|
+
const registered = await presence.ensure();
|
|
897
|
+
if (registered === void 0) return void 0;
|
|
898
|
+
agentId = registered;
|
|
899
|
+
processRecord = { ...record, registration_id: agentId };
|
|
900
|
+
store.write(registrationName, processRecord);
|
|
1100
901
|
if (options.binding !== void 0) writeBinding(options.binding);
|
|
1101
902
|
return agentId;
|
|
1102
903
|
})();
|
|
@@ -1110,7 +911,7 @@ function createRadarPublicationInner(options) {
|
|
|
1110
911
|
if (agentId === void 0) return;
|
|
1111
912
|
store.write(`bindings/${binding.key}.json`, {
|
|
1112
913
|
version: RADAR_PUBLICATION_VERSION,
|
|
1113
|
-
|
|
914
|
+
registration_id: agentId,
|
|
1114
915
|
incarnation,
|
|
1115
916
|
run: binding.run,
|
|
1116
917
|
owner: binding.owner,
|
|
@@ -1134,14 +935,22 @@ function createRadarPublicationInner(options) {
|
|
|
1134
935
|
leaseMs: options.leaseMs ?? RADAR_LEASE_MS,
|
|
1135
936
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
1136
937
|
});
|
|
1137
|
-
const
|
|
938
|
+
const presence = createPresenceReporter({
|
|
1138
939
|
store,
|
|
1139
940
|
client,
|
|
1140
941
|
scheduler,
|
|
1141
|
-
recordName:
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
942
|
+
recordName: presenceRecord,
|
|
943
|
+
registration: async () => {
|
|
944
|
+
const record = await ensureRegistration();
|
|
945
|
+
if (!record) return void 0;
|
|
946
|
+
const location = await options.location?.();
|
|
947
|
+
const { location: _initialLocation, ...identity } = record.registration;
|
|
948
|
+
return {
|
|
949
|
+
...identity,
|
|
950
|
+
...location?.state === "resolved" ? { location: location.location } : {},
|
|
951
|
+
...spawnToken ? { spawn_token: spawnToken } : {}
|
|
952
|
+
};
|
|
953
|
+
},
|
|
1145
954
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
1146
955
|
});
|
|
1147
956
|
const facts = createChannelWriter({
|
|
@@ -1159,7 +968,7 @@ function createRadarPublicationInner(options) {
|
|
|
1159
968
|
const assignmentWriters = /* @__PURE__ */ new Map();
|
|
1160
969
|
const publication = {
|
|
1161
970
|
execution,
|
|
1162
|
-
|
|
971
|
+
presence,
|
|
1163
972
|
facts,
|
|
1164
973
|
assignment(target, reportingOwner) {
|
|
1165
974
|
const existing = assignmentWriters.get(target);
|
|
@@ -1174,7 +983,7 @@ function createRadarPublicationInner(options) {
|
|
|
1174
983
|
publisher: publisher("herdsman-owner", reportingOwner),
|
|
1175
984
|
codec: assignmentCodec,
|
|
1176
985
|
// The owner publishes about a subject it did not register: the exact
|
|
1177
|
-
//
|
|
986
|
+
// registration_id from the child's sidecar is the whole correlation.
|
|
1178
987
|
subject: async () => target,
|
|
1179
988
|
leaseMs: options.leaseMs ?? RADAR_LEASE_MS,
|
|
1180
989
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
@@ -1194,7 +1003,7 @@ function createRadarPublicationInner(options) {
|
|
|
1194
1003
|
registrationDiagnostic ??= `Radar binding unavailable: ${String(error).slice(0, 180)}`;
|
|
1195
1004
|
return void 0;
|
|
1196
1005
|
}
|
|
1197
|
-
const bound = textAt(value, "
|
|
1006
|
+
const bound = textAt(value, "registration_id");
|
|
1198
1007
|
const boundIncarnation = textAt(value, "incarnation");
|
|
1199
1008
|
const run = textAt(value, "run");
|
|
1200
1009
|
const owner = textAt(value, "owner");
|
|
@@ -1204,7 +1013,7 @@ function createRadarPublicationInner(options) {
|
|
|
1204
1013
|
return void 0;
|
|
1205
1014
|
return {
|
|
1206
1015
|
version: RADAR_PUBLICATION_VERSION,
|
|
1207
|
-
|
|
1016
|
+
registration_id: bound,
|
|
1208
1017
|
incarnation: boundIncarnation,
|
|
1209
1018
|
run,
|
|
1210
1019
|
owner,
|
|
@@ -1220,25 +1029,27 @@ function createRadarPublicationInner(options) {
|
|
|
1220
1029
|
if (agentId2 === void 0) return void 0;
|
|
1221
1030
|
const record = await ensureRegistration();
|
|
1222
1031
|
if (record === void 0) return void 0;
|
|
1223
|
-
const
|
|
1224
|
-
const
|
|
1032
|
+
const currentLocation = await options.location?.();
|
|
1033
|
+
const location = currentLocation?.state === "resolved" ? currentLocation.location : void 0;
|
|
1034
|
+
const standing = presence.management();
|
|
1035
|
+
const subjectId = standing?.state === "managed" ? standing.subject_id : void 0;
|
|
1225
1036
|
return {
|
|
1226
|
-
|
|
1037
|
+
registration_id: agentId2,
|
|
1227
1038
|
...subjectId !== void 0 ? { subject_id: subjectId } : {},
|
|
1228
1039
|
...location !== void 0 ? { location } : {}
|
|
1229
1040
|
};
|
|
1230
1041
|
},
|
|
1231
|
-
diagnostic: () => registrationDiagnostic ?? execution.diagnostic() ??
|
|
1042
|
+
diagnostic: () => registrationDiagnostic ?? execution.diagnostic() ?? presence.diagnostic() ?? facts.diagnostic(),
|
|
1232
1043
|
stop() {
|
|
1233
1044
|
for (const writer of assignmentWriters.values()) writer.stop();
|
|
1234
1045
|
execution.stop();
|
|
1235
|
-
|
|
1046
|
+
presence.stop();
|
|
1236
1047
|
facts.stop();
|
|
1237
1048
|
},
|
|
1238
1049
|
reopen() {
|
|
1239
1050
|
for (const writer of assignmentWriters.values()) writer.reopen();
|
|
1240
1051
|
execution.reopen();
|
|
1241
|
-
|
|
1052
|
+
presence.reopen();
|
|
1242
1053
|
facts.reopen();
|
|
1243
1054
|
}
|
|
1244
1055
|
};
|
|
@@ -1276,10 +1087,13 @@ function unavailablePublication2() {
|
|
|
1276
1087
|
return {
|
|
1277
1088
|
execution: writer,
|
|
1278
1089
|
facts: { ...writer, update: () => void 0 },
|
|
1279
|
-
|
|
1280
|
-
|
|
1090
|
+
presence: {
|
|
1091
|
+
report: () => void 0,
|
|
1281
1092
|
stop: () => void 0,
|
|
1282
1093
|
reopen: () => void 0,
|
|
1094
|
+
management: () => void 0,
|
|
1095
|
+
ensure: async () => void 0,
|
|
1096
|
+
release: async () => void 0,
|
|
1283
1097
|
diagnostic: () => "Radar private storage is unavailable"
|
|
1284
1098
|
},
|
|
1285
1099
|
assignment: () => writer,
|
|
@@ -1362,11 +1176,11 @@ function createRadarExecutionAdapter(pi, options) {
|
|
|
1362
1176
|
...lastOutcome !== void 0 ? { last_outcome: lastOutcome } : {}
|
|
1363
1177
|
};
|
|
1364
1178
|
}
|
|
1365
|
-
function reportSession(context
|
|
1179
|
+
function reportSession(context) {
|
|
1366
1180
|
const session = context?.sessionManager?.getSessionId?.();
|
|
1367
1181
|
if (!isUuid(session)) return;
|
|
1368
1182
|
try {
|
|
1369
|
-
publication.
|
|
1183
|
+
publication.presence.report(session);
|
|
1370
1184
|
} catch {
|
|
1371
1185
|
}
|
|
1372
1186
|
}
|
|
@@ -1430,38 +1244,8 @@ function createRadarExecutionAdapter(pi, options) {
|
|
|
1430
1244
|
})
|
|
1431
1245
|
);
|
|
1432
1246
|
on("session_start", (_event, context) => {
|
|
1433
|
-
const sessionHost = {
|
|
1434
|
-
appendEntry: pi.appendEntry ? (customType, data) => pi.appendEntry?.(customType, data) : void 0,
|
|
1435
|
-
sessionManager: context?.sessionManager
|
|
1436
|
-
};
|
|
1437
|
-
let launchIdentity;
|
|
1438
|
-
try {
|
|
1439
|
-
const result = persistRadarLaunchMarker(
|
|
1440
|
-
sessionHost,
|
|
1441
|
-
options.markerEnvironment
|
|
1442
|
-
);
|
|
1443
|
-
if (result === "written" || result === "unchanged") {
|
|
1444
|
-
const expected = radarLaunchMarkerFromEnvironment(options.markerEnvironment);
|
|
1445
|
-
const activeMarker = activeRadarLaunchMarker(sessionHost);
|
|
1446
|
-
if (expected && activeMarker.status === "valid" && JSON.stringify(activeMarker.marker) === JSON.stringify(expected)) {
|
|
1447
|
-
launchIdentity = activeMarker.marker;
|
|
1448
|
-
} else {
|
|
1449
|
-
options.diagnostic?.("Radar launch marker was not visible on the active Pi session branch");
|
|
1450
|
-
}
|
|
1451
|
-
} else if (result === "unsupported") {
|
|
1452
|
-
options.diagnostic?.("Pi host does not support Radar launch identity session entries");
|
|
1453
|
-
} else if (options.markerEnvironment && [
|
|
1454
|
-
"PI_RADAR_MANAGED_SUBJECT_ID",
|
|
1455
|
-
"PI_RADAR_MANAGED_LAUNCH_ID",
|
|
1456
|
-
"PI_RADAR_MANAGED_EXECUTION_GENERATION"
|
|
1457
|
-
].some((key) => options.markerEnvironment?.[key] !== void 0)) {
|
|
1458
|
-
options.diagnostic?.("incomplete or invalid Radar launch identity environment");
|
|
1459
|
-
}
|
|
1460
|
-
} catch (error) {
|
|
1461
|
-
options.diagnostic?.(`could not persist Radar launch marker: ${String(error)}`);
|
|
1462
|
-
}
|
|
1463
1247
|
rootSession = true;
|
|
1464
|
-
reportSession(context
|
|
1248
|
+
reportSession(context);
|
|
1465
1249
|
publishFacts(context);
|
|
1466
1250
|
publication.reopen();
|
|
1467
1251
|
active = context?.isIdle?.() === false;
|
|
@@ -1492,8 +1276,9 @@ function createRadarExecutionAdapter(pi, options) {
|
|
|
1492
1276
|
}
|
|
1493
1277
|
emit();
|
|
1494
1278
|
});
|
|
1495
|
-
on("session_shutdown", () => {
|
|
1279
|
+
on("session_shutdown", async () => {
|
|
1496
1280
|
suspend();
|
|
1281
|
+
await publication.presence.release();
|
|
1497
1282
|
});
|
|
1498
1283
|
function suspend() {
|
|
1499
1284
|
if (stopped) return;
|