pi-radar 0.2.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 +10 -4
- package/dist/extension/baseline.js +333 -394
- package/dist/extension/radar-client.js +126 -11
- package/dist/extension/radar-communication.js +39 -0
- package/dist/extension/radar-execution.js +169 -382
- package/dist/extension/radar-launch.js +18 -13
- package/dist/extension/radar-publication.js +161 -298
- package/dist/extension/radar-runtime.js +1 -1
- package/dist/index.js +356 -407
- package/extension/baseline.ts +2 -0
- package/extension/generated-contracts.ts +1310 -10
- package/extension/radar-client.ts +191 -23
- package/extension/radar-communication.ts +66 -0
- package/extension/radar-execution.ts +11 -58
- package/extension/radar-launch.ts +29 -22
- package/extension/radar-publication.ts +211 -410
- package/extension/radar-runtime.ts +4 -1
- package/index.ts +1 -0
- package/package.json +9 -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/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
|
@@ -15,27 +15,6 @@ 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
|
-
|
|
39
18
|
// extension/radar-client.ts
|
|
40
19
|
var RADAR_MAX_LINE_BYTES = 1024 * 1024;
|
|
41
20
|
var wallClock = { setTimeout, clearTimeout, setInterval, clearInterval };
|
|
@@ -53,9 +32,9 @@ function bounded(text, bytes) {
|
|
|
53
32
|
end -= 1;
|
|
54
33
|
return text.slice(0, end);
|
|
55
34
|
}
|
|
56
|
-
var
|
|
35
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
57
36
|
function isUuid(value) {
|
|
58
|
-
return typeof value === "string" &&
|
|
37
|
+
return typeof value === "string" && UUID.test(value);
|
|
59
38
|
}
|
|
60
39
|
var RADAR_MAX_BACKGROUND_TASKS = 64;
|
|
61
40
|
var RADAR_MAX_AWAITED = 64;
|
|
@@ -133,6 +112,7 @@ function boundedRadarSnapshot(snapshot) {
|
|
|
133
112
|
}
|
|
134
113
|
|
|
135
114
|
// extension/radar-publication.ts
|
|
115
|
+
var DUPLICATE_SESSION_DIAGNOSTIC = "Radar rejected this session claim as a duplicate; reload or resume the session before retrying";
|
|
136
116
|
var RADAR_PUBLICATION_VERSION = 1;
|
|
137
117
|
var RADAR_LEASE_MS = 3e4;
|
|
138
118
|
var RADAR_HEARTBEAT_MS = 15e3;
|
|
@@ -157,7 +137,7 @@ function readProcessBirth() {
|
|
|
157
137
|
return void 0;
|
|
158
138
|
}
|
|
159
139
|
}
|
|
160
|
-
var RECORD_NAME = /^(?:subjects|channels|context|bindings)\/[a-z0-9-]{1,64}\.json$/;
|
|
140
|
+
var RECORD_NAME = /^(?:registrations|presence|subjects|channels|context|bindings)\/[a-z0-9-]{1,64}\.json$/;
|
|
161
141
|
var MAX_RECORD_BYTES = 64 * 1024;
|
|
162
142
|
var PRIVATE_MODE = 448;
|
|
163
143
|
var RECORD_MODE = 384;
|
|
@@ -442,39 +422,6 @@ function pendingAt(value, keys = PENDING_KEYS, codec = executionCodec) {
|
|
|
442
422
|
return void 0;
|
|
443
423
|
return { sequence, snapshot, lease_ms: lease, observed_at: observed };
|
|
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";
|
|
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";
|
|
449
|
-
function sessionAt(value) {
|
|
450
|
-
if (typeof value !== "object" || value === null) return void 0;
|
|
451
|
-
const found = value.session;
|
|
452
|
-
if (found === null) return null;
|
|
453
|
-
return typeof found === "string" ? found : void 0;
|
|
454
|
-
}
|
|
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;
|
|
461
|
-
const sequence = numberAt(value, "sequence");
|
|
462
|
-
const lease = numberAt(value, "lease_ms");
|
|
463
|
-
const observed = textAt(value, "observed_at");
|
|
464
|
-
const session = sessionAt(value);
|
|
465
|
-
if (sequence === void 0 || sequence < 1) return void 0;
|
|
466
|
-
if (lease === void 0 || observed === void 0 || session === void 0)
|
|
467
|
-
return void 0;
|
|
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
|
-
};
|
|
477
|
-
}
|
|
478
425
|
function createChannelWriter(input) {
|
|
479
426
|
const { store, client, scheduler, recordName, channel, publisher } = input;
|
|
480
427
|
const codec = input.codec ?? executionCodec;
|
|
@@ -490,7 +437,7 @@ function createChannelWriter(input) {
|
|
|
490
437
|
function blank(agentId) {
|
|
491
438
|
return {
|
|
492
439
|
version: RADAR_PUBLICATION_VERSION,
|
|
493
|
-
|
|
440
|
+
registration_id: agentId,
|
|
494
441
|
channel,
|
|
495
442
|
publisher,
|
|
496
443
|
sequence: 0
|
|
@@ -498,7 +445,7 @@ function createChannelWriter(input) {
|
|
|
498
445
|
}
|
|
499
446
|
function loadRecord() {
|
|
500
447
|
const value = store.read(recordName);
|
|
501
|
-
const agentId = textAt(value, "
|
|
448
|
+
const agentId = textAt(value, "registration_id");
|
|
502
449
|
const storedPublisher = recordAt(value, "publisher");
|
|
503
450
|
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)
|
|
504
451
|
return blank("");
|
|
@@ -513,7 +460,7 @@ function createChannelWriter(input) {
|
|
|
513
460
|
const sentAt = numberAt(acceptedValue, "sent_at");
|
|
514
461
|
return {
|
|
515
462
|
version: RADAR_PUBLICATION_VERSION,
|
|
516
|
-
|
|
463
|
+
registration_id: agentId,
|
|
517
464
|
channel,
|
|
518
465
|
publisher,
|
|
519
466
|
...handle !== void 0 && generation !== void 0 ? { writer: { handle, generation } } : {},
|
|
@@ -554,12 +501,12 @@ function createChannelWriter(input) {
|
|
|
554
501
|
if (record.writer !== void 0) return true;
|
|
555
502
|
const agentId = await input.subject(recovering);
|
|
556
503
|
if (agentId === void 0) return false;
|
|
557
|
-
if (record.
|
|
504
|
+
if (record.registration_id !== agentId) {
|
|
558
505
|
record = blank(agentId);
|
|
559
506
|
save();
|
|
560
507
|
}
|
|
561
508
|
const answer = await client.acquire({
|
|
562
|
-
|
|
509
|
+
registration_id: agentId,
|
|
563
510
|
channel,
|
|
564
511
|
publisher
|
|
565
512
|
});
|
|
@@ -589,7 +536,7 @@ function createChannelWriter(input) {
|
|
|
589
536
|
const writer = record.writer;
|
|
590
537
|
if (writer === void 0) return;
|
|
591
538
|
const answer = await client.publish({
|
|
592
|
-
|
|
539
|
+
registration_id: record.registration_id,
|
|
593
540
|
channel,
|
|
594
541
|
writer_handle: writer.handle,
|
|
595
542
|
sequence: pending.sequence,
|
|
@@ -683,9 +630,9 @@ function createChannelWriter(input) {
|
|
|
683
630
|
stopPump();
|
|
684
631
|
const writer = record.writer;
|
|
685
632
|
if (writer === void 0) return;
|
|
686
|
-
const agentId = record.
|
|
633
|
+
const agentId = record.registration_id;
|
|
687
634
|
try {
|
|
688
|
-
void client.retire({
|
|
635
|
+
void client.retire({ registration_id: agentId, channel, writer_handle: writer.handle }).then((answer) => {
|
|
689
636
|
if (answer.ok === true) {
|
|
690
637
|
try {
|
|
691
638
|
record.retired_at = new Date(scheduler.now()).toISOString();
|
|
@@ -708,214 +655,127 @@ function createChannelWriter(input) {
|
|
|
708
655
|
diagnostic: () => record.diagnostic
|
|
709
656
|
};
|
|
710
657
|
}
|
|
711
|
-
function
|
|
712
|
-
const { store, client, scheduler, recordName
|
|
713
|
-
let
|
|
714
|
-
let
|
|
715
|
-
let
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
function blank(agentId) {
|
|
723
|
-
return {
|
|
724
|
-
version: RADAR_PUBLICATION_VERSION,
|
|
725
|
-
agent_id: agentId,
|
|
726
|
-
publisher,
|
|
727
|
-
sequence: 0
|
|
728
|
-
};
|
|
729
|
-
}
|
|
730
|
-
function loadRecord() {
|
|
731
|
-
const value = store.read(recordName);
|
|
732
|
-
const agentId = textAt(value, "agent_id");
|
|
733
|
-
const storedPublisher = recordAt(value, "publisher");
|
|
734
|
-
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)
|
|
735
|
-
return blank("");
|
|
736
|
-
const writer = recordAt(value, "writer");
|
|
737
|
-
const handle = textAt(writer, "handle");
|
|
738
|
-
const generation = numberAt(writer, "generation");
|
|
739
|
-
if (writer !== void 0 && (!exactKeys(writer, WRITER_KEYS) || !isUuid(handle) || generation === void 0))
|
|
740
|
-
return blank(agentId);
|
|
741
|
-
const pending = contextReportAt(
|
|
742
|
-
recordAt(value, "pending"),
|
|
743
|
-
CONTEXT_PENDING_KEYS,
|
|
744
|
-
CONTEXT_PENDING_MARKED_KEYS
|
|
745
|
-
);
|
|
746
|
-
const acceptedValue = recordAt(value, "accepted");
|
|
747
|
-
const accepted = contextReportAt(
|
|
748
|
-
acceptedValue,
|
|
749
|
-
CONTEXT_ACCEPTED_KEYS,
|
|
750
|
-
CONTEXT_ACCEPTED_MARKED_KEYS
|
|
751
|
-
);
|
|
752
|
-
const sentAt = numberAt(acceptedValue, "sent_at");
|
|
753
|
-
return {
|
|
754
|
-
version: RADAR_PUBLICATION_VERSION,
|
|
755
|
-
agent_id: agentId,
|
|
756
|
-
publisher,
|
|
757
|
-
...handle !== void 0 && generation !== void 0 ? { writer: { handle, generation } } : {},
|
|
758
|
-
sequence: numberAt(value, "sequence") ?? 0,
|
|
759
|
-
...pending !== void 0 ? { pending } : {},
|
|
760
|
-
...accepted !== void 0 && sentAt !== void 0 ? { accepted: { ...accepted, sent_at: sentAt } } : {},
|
|
761
|
-
...textAt(value, "diagnostic") !== void 0 ? { diagnostic: textAt(value, "diagnostic") } : {}
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
function note(message) {
|
|
765
|
-
if (record.diagnostic !== void 0) return;
|
|
766
|
-
record.diagnostic = message.slice(0, 256);
|
|
767
|
-
save();
|
|
658
|
+
function createPresenceReporter(input) {
|
|
659
|
+
const { store, client, scheduler, recordName } = input;
|
|
660
|
+
let state;
|
|
661
|
+
let standing;
|
|
662
|
+
let diagnostic;
|
|
663
|
+
try {
|
|
664
|
+
const saved = store.read(recordName);
|
|
665
|
+
if (saved?.version === 2 && isUuid(saved.claim_id) && isUuid(saved.session) && Array.isArray(saved.releases))
|
|
666
|
+
state = saved;
|
|
667
|
+
} catch (error) {
|
|
668
|
+
diagnostic = `presence storage unavailable: ${String(error)}`;
|
|
768
669
|
}
|
|
670
|
+
let timer;
|
|
671
|
+
let stopped = false;
|
|
672
|
+
let running;
|
|
769
673
|
function save() {
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
function startPump() {
|
|
778
|
-
if (pump !== void 0 || stopped || fenced) return;
|
|
779
|
-
pump = scheduler.setInterval(() => kick(), input.heartbeatMs);
|
|
780
|
-
pump.unref?.();
|
|
781
|
-
}
|
|
782
|
-
function stopPump() {
|
|
783
|
-
if (pump === void 0) return;
|
|
784
|
-
scheduler.clearInterval(pump);
|
|
785
|
-
pump = void 0;
|
|
674
|
+
try {
|
|
675
|
+
if (state) store.write(recordName, state);
|
|
676
|
+
return true;
|
|
677
|
+
} catch (error) {
|
|
678
|
+
diagnostic = `presence storage unavailable: ${String(error)}`;
|
|
679
|
+
return false;
|
|
680
|
+
}
|
|
786
681
|
}
|
|
787
|
-
async function
|
|
788
|
-
if (
|
|
789
|
-
const
|
|
790
|
-
if (
|
|
791
|
-
|
|
792
|
-
|
|
682
|
+
async function releasePending() {
|
|
683
|
+
if (!state) return;
|
|
684
|
+
const registration = await input.registration();
|
|
685
|
+
if (!registration?.process) return;
|
|
686
|
+
for (const release of [...state.releases]) {
|
|
687
|
+
const answer = await client.releasePresence({ ...release, process: registration.process });
|
|
688
|
+
if (!answer.ok) return;
|
|
689
|
+
state.releases = state.releases.filter((r) => r.claim_id !== release.claim_id);
|
|
793
690
|
save();
|
|
794
691
|
}
|
|
795
|
-
return true;
|
|
796
692
|
}
|
|
797
|
-
function
|
|
798
|
-
|
|
693
|
+
function schedule() {
|
|
694
|
+
if (stopped || timer) return;
|
|
695
|
+
timer = scheduler.setTimeout(() => {
|
|
696
|
+
timer = void 0;
|
|
697
|
+
void ensure();
|
|
698
|
+
}, input.heartbeatMs);
|
|
699
|
+
timer.unref?.();
|
|
799
700
|
}
|
|
800
|
-
async function
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
701
|
+
async function ensure() {
|
|
702
|
+
if (running) return running;
|
|
703
|
+
if (stopped || !state) return void 0;
|
|
704
|
+
const claim = state;
|
|
705
|
+
if (!save()) return void 0;
|
|
706
|
+
running = (async () => {
|
|
707
|
+
await releasePending();
|
|
708
|
+
const registration = await input.registration();
|
|
709
|
+
if (!registration?.process || state !== claim || stopped) return void 0;
|
|
710
|
+
const answer = await client.presence({
|
|
711
|
+
...registration,
|
|
712
|
+
process: registration.process,
|
|
713
|
+
session_id: claim.session,
|
|
714
|
+
claim_id: claim.claim_id,
|
|
715
|
+
...claim.registration_id ? { registration_id: claim.registration_id } : {},
|
|
716
|
+
...claim.subject_id ? { subject_id: claim.subject_id } : {}
|
|
717
|
+
});
|
|
718
|
+
if (state !== claim) return void 0;
|
|
719
|
+
if (!answer.ok) {
|
|
720
|
+
if (!["absent", "timeout", "transport"].includes(answer.code)) diagnostic = answer.message;
|
|
721
|
+
return void 0;
|
|
814
722
|
}
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
handle: answer.value.writer.handle,
|
|
820
|
-
generation: answer.value.writer.generation
|
|
821
|
-
};
|
|
822
|
-
record.sequence = Math.max(record.sequence, answer.value.writer.sequence);
|
|
823
|
-
record.accepted = {
|
|
824
|
-
...pending,
|
|
825
|
-
sent_at: answer.value.warning === void 0 ? scheduler.now() : record.accepted?.sent_at ?? scheduler.now()
|
|
826
|
-
};
|
|
827
|
-
record.pending = void 0;
|
|
828
|
-
save();
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
if (answer.code === "refused" || answer.code === "bad_params" || // A daemon that speaks the registry but does not serve this record would
|
|
832
|
-
// refuse every retry the same way; one diagnostic beats a silent retry
|
|
833
|
-
// loop once per heartbeat.
|
|
834
|
-
answer.code === "unknown_method") {
|
|
835
|
-
fence(answer.message);
|
|
836
|
-
return;
|
|
837
|
-
}
|
|
838
|
-
if (answer.code === "not_found") {
|
|
839
|
-
record = blank("");
|
|
840
|
-
recovering = true;
|
|
723
|
+
claim.registration_id = answer.value.registration_id;
|
|
724
|
+
claim.subject_id = answer.value.standing === "granted" ? answer.value.subject_id ?? void 0 : void 0;
|
|
725
|
+
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" };
|
|
726
|
+
diagnostic = answer.value.standing === "duplicate" ? DUPLICATE_SESSION_DIAGNOSTIC : void 0;
|
|
841
727
|
save();
|
|
842
|
-
return;
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
async function stage(session, launchIdentity) {
|
|
846
|
-
const pending = {
|
|
847
|
-
sequence: record.sequence + 1,
|
|
848
|
-
session,
|
|
849
|
-
lease_ms: input.leaseMs,
|
|
850
|
-
observed_at: new Date(scheduler.now()).toISOString(),
|
|
851
|
-
...launchIdentity !== void 0 ? { launch_identity: launchIdentity } : {}
|
|
852
|
-
};
|
|
853
|
-
record.pending = pending;
|
|
854
|
-
save();
|
|
855
|
-
await send(pending);
|
|
856
|
-
}
|
|
857
|
-
async function drainOnce() {
|
|
858
|
-
if (fenced) return;
|
|
859
|
-
if (record.pending !== void 0) {
|
|
860
|
-
await send(record.pending);
|
|
861
|
-
return;
|
|
862
|
-
}
|
|
863
|
-
if (desired === void 0) return;
|
|
864
|
-
if (!await ensureSubject()) return;
|
|
865
|
-
if (fenced) return;
|
|
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);
|
|
868
|
-
return;
|
|
869
|
-
}
|
|
870
|
-
if (heartbeatDue()) await stage(record.accepted.session, desiredMarker);
|
|
871
|
-
}
|
|
872
|
-
async function drain() {
|
|
873
|
-
if (running || stopped || fenced) return;
|
|
874
|
-
running = true;
|
|
728
|
+
return claim.registration_id;
|
|
729
|
+
})();
|
|
875
730
|
try {
|
|
876
|
-
await
|
|
731
|
+
return await running;
|
|
877
732
|
} catch (error) {
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
try {
|
|
881
|
-
note(`context storage failure: ${String(error).slice(0, 180)}`);
|
|
882
|
-
} catch {
|
|
883
|
-
}
|
|
733
|
+
diagnostic = `presence unavailable: ${String(error)}`;
|
|
734
|
+
return void 0;
|
|
884
735
|
} finally {
|
|
885
|
-
running =
|
|
886
|
-
if (
|
|
887
|
-
|
|
888
|
-
kick();
|
|
889
|
-
}
|
|
736
|
+
running = void 0;
|
|
737
|
+
if (!stopped && state !== claim) void ensure();
|
|
738
|
+
else schedule();
|
|
890
739
|
}
|
|
891
740
|
}
|
|
892
|
-
function kick() {
|
|
893
|
-
if (stopped || fenced) return;
|
|
894
|
-
startPump();
|
|
895
|
-
if (running) {
|
|
896
|
-
again = true;
|
|
897
|
-
return;
|
|
898
|
-
}
|
|
899
|
-
void drain().catch(() => void 0);
|
|
900
|
-
}
|
|
901
741
|
return {
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
742
|
+
report(session) {
|
|
743
|
+
const releases = [...state?.releases ?? []];
|
|
744
|
+
if (state?.registration_id && !releases.some((release) => release.claim_id === state.claim_id)) {
|
|
745
|
+
releases.push({ registration_id: state.registration_id, claim_id: state.claim_id });
|
|
746
|
+
}
|
|
747
|
+
standing = void 0;
|
|
748
|
+
state = { version: 2, session, claim_id: randomUUID(), releases };
|
|
749
|
+
save();
|
|
750
|
+
stopped = false;
|
|
751
|
+
void ensure();
|
|
752
|
+
},
|
|
753
|
+
ensure,
|
|
754
|
+
async release() {
|
|
755
|
+
const releasing = state;
|
|
756
|
+
standing = void 0;
|
|
757
|
+
stopped = true;
|
|
758
|
+
if (timer) scheduler.clearTimeout(timer);
|
|
759
|
+
timer = void 0;
|
|
760
|
+
if (running) await running;
|
|
761
|
+
if (state === releasing) standing = void 0;
|
|
762
|
+
if (releasing?.registration_id && !releasing.releases.some((r) => r.claim_id === releasing.claim_id)) {
|
|
763
|
+
releasing.releases.push({ registration_id: releasing.registration_id, claim_id: releasing.claim_id });
|
|
764
|
+
if (!save()) return;
|
|
765
|
+
}
|
|
766
|
+
await releasePending();
|
|
909
767
|
},
|
|
910
768
|
stop() {
|
|
911
769
|
stopped = true;
|
|
912
|
-
|
|
770
|
+
if (timer) scheduler.clearTimeout(timer);
|
|
771
|
+
timer = void 0;
|
|
913
772
|
},
|
|
914
773
|
reopen() {
|
|
915
774
|
stopped = false;
|
|
916
|
-
|
|
775
|
+
void ensure();
|
|
917
776
|
},
|
|
918
|
-
|
|
777
|
+
management: () => standing,
|
|
778
|
+
diagnostic: () => diagnostic
|
|
919
779
|
};
|
|
920
780
|
}
|
|
921
781
|
var STORE_IDENTITY = /* @__PURE__ */ new WeakMap();
|
|
@@ -944,10 +804,13 @@ function unavailablePublication(error) {
|
|
|
944
804
|
};
|
|
945
805
|
return {
|
|
946
806
|
execution: writer,
|
|
947
|
-
|
|
948
|
-
|
|
807
|
+
presence: {
|
|
808
|
+
report: () => void 0,
|
|
809
|
+
ensure: async () => void 0,
|
|
810
|
+
release: async () => void 0,
|
|
949
811
|
stop: () => void 0,
|
|
950
812
|
reopen: () => void 0,
|
|
813
|
+
management: () => void 0,
|
|
951
814
|
diagnostic: () => diagnostic
|
|
952
815
|
},
|
|
953
816
|
facts: { ...writer, update: () => void 0 },
|
|
@@ -974,33 +837,29 @@ function createRadarPublicationInner(options) {
|
|
|
974
837
|
cached.reopen();
|
|
975
838
|
return cached;
|
|
976
839
|
}
|
|
977
|
-
const
|
|
840
|
+
const registrationName = `registrations/${createHash("sha256").update(`${options.registration.source}\0${incarnation}`).digest("hex").slice(0, 32)}.json`;
|
|
978
841
|
const channelRecord = (channel, target) => `channels/${createHash("sha256").update(`${channel}\0${incarnation}\0${target ?? ""}`).digest("hex").slice(0, 32)}.json`;
|
|
979
|
-
const
|
|
980
|
-
function
|
|
981
|
-
const value = store.read(
|
|
842
|
+
const presenceRecord = `presence/${createHash("sha256").update(`presence\0${incarnation}`).digest("hex").slice(0, 32)}.json`;
|
|
843
|
+
function readRegistration() {
|
|
844
|
+
const value = store.read(registrationName);
|
|
982
845
|
const registration = recordAt(value, "registration");
|
|
983
846
|
if (numberAt(value, "version") !== RADAR_PUBLICATION_VERSION || textAt(registration, "source") !== options.registration.source || textAt(registration, "incarnation") !== incarnation)
|
|
984
847
|
return void 0;
|
|
985
|
-
const storedAgentId = textAt(value, "
|
|
848
|
+
const storedAgentId = textAt(value, "registration_id");
|
|
986
849
|
return {
|
|
987
850
|
version: RADAR_PUBLICATION_VERSION,
|
|
988
851
|
registration,
|
|
989
|
-
...storedAgentId !== void 0 && isUuid(storedAgentId) ? {
|
|
852
|
+
...storedAgentId !== void 0 && isUuid(storedAgentId) ? { registration_id: storedAgentId } : {}
|
|
990
853
|
};
|
|
991
854
|
}
|
|
992
|
-
const stored =
|
|
993
|
-
let
|
|
855
|
+
const stored = readRegistration();
|
|
856
|
+
let processRecord = stored;
|
|
994
857
|
let registrationDiagnostic;
|
|
995
858
|
let registrationRecord;
|
|
996
859
|
async function ensureRegistration() {
|
|
997
|
-
if (
|
|
860
|
+
if (processRecord !== void 0) return processRecord;
|
|
998
861
|
registrationRecord ??= (async () => {
|
|
999
862
|
const resolution = options.location ? await options.location() : { state: "absent", reason: "this process publishes no location" };
|
|
1000
|
-
if (resolution.state === "pending") {
|
|
1001
|
-
registrationDiagnostic = `location bootstrap pending: ${resolution.reason}`.slice(0, 256);
|
|
1002
|
-
return void 0;
|
|
1003
|
-
}
|
|
1004
863
|
const built = {
|
|
1005
864
|
version: RADAR_PUBLICATION_VERSION,
|
|
1006
865
|
registration: {
|
|
@@ -1011,13 +870,13 @@ function createRadarPublicationInner(options) {
|
|
|
1011
870
|
}
|
|
1012
871
|
};
|
|
1013
872
|
try {
|
|
1014
|
-
store.write(
|
|
873
|
+
store.write(registrationName, built);
|
|
1015
874
|
} catch (error) {
|
|
1016
875
|
registrationDiagnostic ??= `private storage unavailable: ${String(error)}`.slice(0, 256);
|
|
1017
876
|
return void 0;
|
|
1018
877
|
}
|
|
1019
878
|
registrationDiagnostic = void 0;
|
|
1020
|
-
|
|
879
|
+
processRecord = built;
|
|
1021
880
|
return built;
|
|
1022
881
|
})();
|
|
1023
882
|
try {
|
|
@@ -1026,11 +885,10 @@ function createRadarPublicationInner(options) {
|
|
|
1026
885
|
registrationRecord = void 0;
|
|
1027
886
|
}
|
|
1028
887
|
}
|
|
1029
|
-
let agentId = stored?.
|
|
888
|
+
let agentId = stored?.registration_id;
|
|
1030
889
|
let subjectReady;
|
|
1031
890
|
async function ensureSubject(recheck) {
|
|
1032
|
-
if (recheck
|
|
1033
|
-
if (agentId !== void 0) return agentId;
|
|
891
|
+
if (!recheck && agentId !== void 0) return agentId;
|
|
1034
892
|
subjectReady ??= (async () => {
|
|
1035
893
|
const ready = await client.ping();
|
|
1036
894
|
if (ready.ok === false) {
|
|
@@ -1040,18 +898,11 @@ function createRadarPublicationInner(options) {
|
|
|
1040
898
|
}
|
|
1041
899
|
const record = await ensureRegistration();
|
|
1042
900
|
if (record === void 0) return void 0;
|
|
1043
|
-
const
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
if (answer.code !== "absent" && answer.code !== "timeout" && answer.code !== "transport")
|
|
1049
|
-
registrationDiagnostic ??= `subject registration refused: ${answer.message}`.slice(0, 256);
|
|
1050
|
-
return void 0;
|
|
1051
|
-
}
|
|
1052
|
-
agentId = answer.value.agent_id;
|
|
1053
|
-
subject = { ...record, agent_id: agentId };
|
|
1054
|
-
store.write(subjectName, subject);
|
|
901
|
+
const registered = await presence.ensure();
|
|
902
|
+
if (registered === void 0) return void 0;
|
|
903
|
+
agentId = registered;
|
|
904
|
+
processRecord = { ...record, registration_id: agentId };
|
|
905
|
+
store.write(registrationName, processRecord);
|
|
1055
906
|
if (options.binding !== void 0) writeBinding(options.binding);
|
|
1056
907
|
return agentId;
|
|
1057
908
|
})();
|
|
@@ -1065,7 +916,7 @@ function createRadarPublicationInner(options) {
|
|
|
1065
916
|
if (agentId === void 0) return;
|
|
1066
917
|
store.write(`bindings/${binding.key}.json`, {
|
|
1067
918
|
version: RADAR_PUBLICATION_VERSION,
|
|
1068
|
-
|
|
919
|
+
registration_id: agentId,
|
|
1069
920
|
incarnation,
|
|
1070
921
|
run: binding.run,
|
|
1071
922
|
owner: binding.owner,
|
|
@@ -1089,14 +940,22 @@ function createRadarPublicationInner(options) {
|
|
|
1089
940
|
leaseMs: options.leaseMs ?? RADAR_LEASE_MS,
|
|
1090
941
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
1091
942
|
});
|
|
1092
|
-
const
|
|
943
|
+
const presence = createPresenceReporter({
|
|
1093
944
|
store,
|
|
1094
945
|
client,
|
|
1095
946
|
scheduler,
|
|
1096
|
-
recordName:
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
947
|
+
recordName: presenceRecord,
|
|
948
|
+
registration: async () => {
|
|
949
|
+
const record = await ensureRegistration();
|
|
950
|
+
if (!record) return void 0;
|
|
951
|
+
const location = await options.location?.();
|
|
952
|
+
const { location: _initialLocation, ...identity } = record.registration;
|
|
953
|
+
return {
|
|
954
|
+
...identity,
|
|
955
|
+
...location?.state === "resolved" ? { location: location.location } : {},
|
|
956
|
+
...spawnToken ? { spawn_token: spawnToken } : {}
|
|
957
|
+
};
|
|
958
|
+
},
|
|
1100
959
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
1101
960
|
});
|
|
1102
961
|
const facts = createChannelWriter({
|
|
@@ -1114,7 +973,7 @@ function createRadarPublicationInner(options) {
|
|
|
1114
973
|
const assignmentWriters = /* @__PURE__ */ new Map();
|
|
1115
974
|
const publication = {
|
|
1116
975
|
execution,
|
|
1117
|
-
|
|
976
|
+
presence,
|
|
1118
977
|
facts,
|
|
1119
978
|
assignment(target, reportingOwner) {
|
|
1120
979
|
const existing = assignmentWriters.get(target);
|
|
@@ -1129,7 +988,7 @@ function createRadarPublicationInner(options) {
|
|
|
1129
988
|
publisher: publisher("herdsman-owner", reportingOwner),
|
|
1130
989
|
codec: assignmentCodec,
|
|
1131
990
|
// The owner publishes about a subject it did not register: the exact
|
|
1132
|
-
//
|
|
991
|
+
// registration_id from the child's sidecar is the whole correlation.
|
|
1133
992
|
subject: async () => target,
|
|
1134
993
|
leaseMs: options.leaseMs ?? RADAR_LEASE_MS,
|
|
1135
994
|
heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
|
|
@@ -1149,7 +1008,7 @@ function createRadarPublicationInner(options) {
|
|
|
1149
1008
|
registrationDiagnostic ??= `Radar binding unavailable: ${String(error).slice(0, 180)}`;
|
|
1150
1009
|
return void 0;
|
|
1151
1010
|
}
|
|
1152
|
-
const bound = textAt(value, "
|
|
1011
|
+
const bound = textAt(value, "registration_id");
|
|
1153
1012
|
const boundIncarnation = textAt(value, "incarnation");
|
|
1154
1013
|
const run = textAt(value, "run");
|
|
1155
1014
|
const owner = textAt(value, "owner");
|
|
@@ -1159,7 +1018,7 @@ function createRadarPublicationInner(options) {
|
|
|
1159
1018
|
return void 0;
|
|
1160
1019
|
return {
|
|
1161
1020
|
version: RADAR_PUBLICATION_VERSION,
|
|
1162
|
-
|
|
1021
|
+
registration_id: bound,
|
|
1163
1022
|
incarnation: boundIncarnation,
|
|
1164
1023
|
run,
|
|
1165
1024
|
owner,
|
|
@@ -1175,23 +1034,27 @@ function createRadarPublicationInner(options) {
|
|
|
1175
1034
|
if (agentId2 === void 0) return void 0;
|
|
1176
1035
|
const record = await ensureRegistration();
|
|
1177
1036
|
if (record === void 0) return void 0;
|
|
1178
|
-
const
|
|
1037
|
+
const currentLocation = await options.location?.();
|
|
1038
|
+
const location = currentLocation?.state === "resolved" ? currentLocation.location : void 0;
|
|
1039
|
+
const standing = presence.management();
|
|
1040
|
+
const subjectId = standing?.state === "managed" ? standing.subject_id : void 0;
|
|
1179
1041
|
return {
|
|
1180
|
-
|
|
1042
|
+
registration_id: agentId2,
|
|
1043
|
+
...subjectId !== void 0 ? { subject_id: subjectId } : {},
|
|
1181
1044
|
...location !== void 0 ? { location } : {}
|
|
1182
1045
|
};
|
|
1183
1046
|
},
|
|
1184
|
-
diagnostic: () => registrationDiagnostic ?? execution.diagnostic() ??
|
|
1047
|
+
diagnostic: () => registrationDiagnostic ?? execution.diagnostic() ?? presence.diagnostic() ?? facts.diagnostic(),
|
|
1185
1048
|
stop() {
|
|
1186
1049
|
for (const writer of assignmentWriters.values()) writer.stop();
|
|
1187
1050
|
execution.stop();
|
|
1188
|
-
|
|
1051
|
+
presence.stop();
|
|
1189
1052
|
facts.stop();
|
|
1190
1053
|
},
|
|
1191
1054
|
reopen() {
|
|
1192
1055
|
for (const writer of assignmentWriters.values()) writer.reopen();
|
|
1193
1056
|
execution.reopen();
|
|
1194
|
-
|
|
1057
|
+
presence.reopen();
|
|
1195
1058
|
facts.reopen();
|
|
1196
1059
|
}
|
|
1197
1060
|
};
|