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.
@@ -14,6 +14,7 @@ import {
14
14
  } from "node:fs";
15
15
  import { dirname, isAbsolute, join, parse, resolve, sep } from "node:path";
16
16
  import type { RadarLocationResolution } from "./radar-location.ts";
17
+ import { decodeRadarLaunchMarker } from "./radar-session-marker.ts";
17
18
  import {
18
19
  boundedRadarAssignment,
19
20
  boundedRadarFacts,
@@ -27,6 +28,7 @@ import {
27
28
  type RadarFactsSnapshot,
28
29
  type RadarClient,
29
30
  type RadarLocation,
31
+ type RadarLaunchIdentityMarker,
30
32
  type RadarPublisherIdentity,
31
33
  type RadarRegistration,
32
34
  type RadarScheduler,
@@ -323,6 +325,7 @@ type ContextReport = {
323
325
  session: string | null;
324
326
  lease_ms: number;
325
327
  observed_at: string;
328
+ launch_identity?: RadarLaunchIdentityMarker;
326
329
  };
327
330
 
328
331
  /**
@@ -560,7 +563,9 @@ function pendingAt(
560
563
  }
561
564
 
562
565
  const CONTEXT_PENDING_KEYS = "lease_ms,observed_at,sequence,session";
566
+ const CONTEXT_PENDING_MARKED_KEYS = "launch_identity,lease_ms,observed_at,sequence,session";
563
567
  const CONTEXT_ACCEPTED_KEYS = "lease_ms,observed_at,sent_at,sequence,session";
568
+ const CONTEXT_ACCEPTED_MARKED_KEYS = "launch_identity,lease_ms,observed_at,sent_at,sequence,session";
564
569
 
565
570
  /** The reported session, or `undefined` when the field is not one of the two. */
566
571
  function sessionAt(value: unknown): string | null | undefined {
@@ -570,11 +575,28 @@ function sessionAt(value: unknown): string | null | undefined {
570
575
  return typeof found === "string" ? found : undefined;
571
576
  }
572
577
 
578
+ function sameLaunchIdentity(
579
+ a: RadarLaunchIdentityMarker | undefined,
580
+ b: RadarLaunchIdentityMarker | undefined,
581
+ ): boolean {
582
+ return (
583
+ a === b ||
584
+ (a !== undefined &&
585
+ b !== undefined &&
586
+ a.version === b.version &&
587
+ a.subject_id === b.subject_id &&
588
+ a.launch_id === b.launch_id &&
589
+ a.execution_generation === b.execution_generation)
590
+ );
591
+ }
592
+
573
593
  function contextReportAt(
574
594
  value: unknown,
575
595
  keys: string,
596
+ markedKeys: string,
576
597
  ): ContextReport | undefined {
577
- if (!exactKeys(value, keys)) return undefined;
598
+ const marked = exactKeys(value, markedKeys);
599
+ if (!exactKeys(value, keys) && !marked) return undefined;
578
600
  const sequence = numberAt(value, "sequence");
579
601
  const lease = numberAt(value, "lease_ms");
580
602
  const observed = textAt(value, "observed_at");
@@ -582,7 +604,17 @@ function contextReportAt(
582
604
  if (sequence === undefined || sequence < 1) return undefined;
583
605
  if (lease === undefined || observed === undefined || session === undefined)
584
606
  return undefined;
585
- return { sequence, session, lease_ms: lease, observed_at: observed };
607
+ const marker = marked
608
+ ? decodeRadarLaunchMarker((value as Record<string, unknown>).launch_identity)
609
+ : undefined;
610
+ if (marked && marker === undefined) return undefined;
611
+ return {
612
+ sequence,
613
+ session,
614
+ lease_ms: lease,
615
+ observed_at: observed,
616
+ ...(marker !== undefined ? { launch_identity: marker } : {}),
617
+ };
586
618
  }
587
619
 
588
620
  export type RadarFactsWriter = Omit<RadarChannelWriter, "update"> & {
@@ -939,7 +971,7 @@ export type RadarContextWriter = {
939
971
  * session" the daemon serves; `undefined` never is, because a process that
940
972
  * does not know its session has nothing to assert.
941
973
  */
942
- update(session: string | null): void;
974
+ update(session: string | null, launchIdentity?: RadarLaunchIdentityMarker): void;
943
975
  /** Release this writer's timers. The accepted report stays until it goes stale. */
944
976
  stop(): void;
945
977
  /** Re-arm a stopped writer after an extension reload. */
@@ -977,6 +1009,7 @@ function createContextWriter(input: ContextWriterInput): RadarContextWriter {
977
1009
  const { store, client, scheduler, recordName, publisher } = input;
978
1010
  let record = loadRecord();
979
1011
  let desired: string | null | undefined;
1012
+ let desiredMarker: RadarLaunchIdentityMarker | undefined;
980
1013
  let running = false;
981
1014
  let again = false;
982
1015
  let recovering = false;
@@ -1016,9 +1049,17 @@ function createContextWriter(input: ContextWriterInput): RadarContextWriter {
1016
1049
  (!exactKeys(writer, WRITER_KEYS) || !isUuid(handle) || generation === undefined)
1017
1050
  )
1018
1051
  return blank(agentId);
1019
- const pending = contextReportAt(recordAt(value, "pending"), CONTEXT_PENDING_KEYS);
1052
+ const pending = contextReportAt(
1053
+ recordAt(value, "pending"),
1054
+ CONTEXT_PENDING_KEYS,
1055
+ CONTEXT_PENDING_MARKED_KEYS,
1056
+ );
1020
1057
  const acceptedValue = recordAt(value, "accepted");
1021
- const accepted = contextReportAt(acceptedValue, CONTEXT_ACCEPTED_KEYS);
1058
+ const accepted = contextReportAt(
1059
+ acceptedValue,
1060
+ CONTEXT_ACCEPTED_KEYS,
1061
+ CONTEXT_ACCEPTED_MARKED_KEYS,
1062
+ );
1022
1063
  const sentAt = numberAt(acceptedValue, "sent_at");
1023
1064
  return {
1024
1065
  version: RADAR_PUBLICATION_VERSION,
@@ -1100,7 +1141,12 @@ function createContextWriter(input: ContextWriterInput): RadarContextWriter {
1100
1141
  sequence: pending.sequence,
1101
1142
  lease_ms: pending.lease_ms,
1102
1143
  observed_at: pending.observed_at,
1103
- context: { session: pending.session },
1144
+ context: {
1145
+ session: pending.session,
1146
+ ...(pending.launch_identity !== undefined
1147
+ ? { launch_identity: pending.launch_identity }
1148
+ : {}),
1149
+ },
1104
1150
  });
1105
1151
  if (answer.ok === true) {
1106
1152
  recovering = false;
@@ -1144,12 +1190,16 @@ function createContextWriter(input: ContextWriterInput): RadarContextWriter {
1144
1190
  // silently advance.
1145
1191
  }
1146
1192
 
1147
- async function stage(session: string | null): Promise<void> {
1193
+ async function stage(
1194
+ session: string | null,
1195
+ launchIdentity?: RadarLaunchIdentityMarker,
1196
+ ): Promise<void> {
1148
1197
  const pending: ContextReport = {
1149
1198
  sequence: record.sequence + 1,
1150
1199
  session,
1151
1200
  lease_ms: input.leaseMs,
1152
1201
  observed_at: new Date(scheduler.now()).toISOString(),
1202
+ ...(launchIdentity !== undefined ? { launch_identity: launchIdentity } : {}),
1153
1203
  };
1154
1204
  // Persisted before it is sent, so a lost reply replays this exact request.
1155
1205
  record.pending = pending;
@@ -1170,12 +1220,13 @@ function createContextWriter(input: ContextWriterInput): RadarContextWriter {
1170
1220
  if (
1171
1221
  record.writer === undefined ||
1172
1222
  record.accepted === undefined ||
1173
- record.accepted.session !== desired
1223
+ record.accepted.session !== desired ||
1224
+ !sameLaunchIdentity(record.accepted.launch_identity, desiredMarker)
1174
1225
  ) {
1175
- await stage(desired);
1226
+ await stage(desired, desiredMarker);
1176
1227
  return;
1177
1228
  }
1178
- if (heartbeatDue()) await stage(record.accepted.session);
1229
+ if (heartbeatDue()) await stage(record.accepted.session, desiredMarker);
1179
1230
  }
1180
1231
 
1181
1232
  async function drain(): Promise<void> {
@@ -1211,12 +1262,18 @@ function createContextWriter(input: ContextWriterInput): RadarContextWriter {
1211
1262
  }
1212
1263
 
1213
1264
  return {
1214
- update(session) {
1215
- if (fenced || session === desired) return;
1265
+ update(session, launchIdentity) {
1266
+ if (
1267
+ fenced ||
1268
+ (session === desired &&
1269
+ sameLaunchIdentity(desiredMarker, launchIdentity))
1270
+ )
1271
+ return;
1216
1272
  // A session reported while this writer is stopped is remembered, not
1217
1273
  // published: only `reopen` restarts the timers, and it must see the
1218
1274
  // session the process is in now rather than the one it left.
1219
1275
  desired = session;
1276
+ desiredMarker = launchIdentity;
1220
1277
  if (stopped) return;
1221
1278
  kick();
1222
1279
  },
@@ -0,0 +1,94 @@
1
+ import type { RadarExecutionAdapter } from "./radar-execution.ts";
2
+ import type { RadarPublication } from "./radar-publication.ts";
3
+
4
+ /** The event channel through which Pi extensions obtain the baseline runtime. */
5
+ export const RADAR_RUNTIME_REQUEST = "pi-radar/runtime-request";
6
+ export const RADAR_RUNTIME_VERSION = 1 as const;
7
+
8
+ /** The sole baseline publisher's session-scoped runtime, shared with consumers. */
9
+ export type RadarRuntimeServices = {
10
+ /** The one baseline publication; Herdsman contributes through its channels. */
11
+ publication: RadarPublication;
12
+ execution: RadarExecutionAdapter;
13
+ };
14
+
15
+ /** Lazy so discovering the provider in an extension factory starts no resources. */
16
+ export type RadarRuntimeHandle = {
17
+ version: typeof RADAR_RUNTIME_VERSION;
18
+ /** Provider memoizes this promise, so every extension shares one publisher. */
19
+ start(): Promise<RadarRuntimeServices>;
20
+ };
21
+
22
+ /** Builds one lazy, memoized session runtime without starting factory resources. */
23
+ export function lazyRadarRuntime(
24
+ start: () => RadarRuntimeServices | Promise<RadarRuntimeServices>,
25
+ ): RadarRuntimeHandle {
26
+ let pending: Promise<RadarRuntimeServices> | undefined;
27
+ return {
28
+ version: RADAR_RUNTIME_VERSION,
29
+ start: () => (pending ??= Promise.resolve().then(start)),
30
+ };
31
+ }
32
+
33
+ /** Minimal structural view of Pi's shared extension EventBus. */
34
+ export type RadarExtensionEventBus = {
35
+ emit(channel: string, data: unknown): void;
36
+ on(channel: string, handler: (data: unknown) => void): () => void;
37
+ };
38
+
39
+ type RuntimeRequest = {
40
+ version: typeof RADAR_RUNTIME_VERSION;
41
+ provide(handle: unknown): void;
42
+ };
43
+
44
+ /**
45
+ * Makes this extension's handle discoverable to other extensions in this Pi
46
+ * runtime. Registering the listener is safe in a factory: it starts no resources.
47
+ */
48
+ export function provideRadarRuntime(
49
+ events: RadarExtensionEventBus,
50
+ handle: RadarRuntimeHandle,
51
+ ): () => void {
52
+ return events.on(RADAR_RUNTIME_REQUEST, (value) => {
53
+ if (!isRuntimeRequest(value)) return;
54
+ value.provide(handle);
55
+ });
56
+ }
57
+
58
+ /**
59
+ * Finds the one baseline runtime in the current Pi process. Pi's event bus emits
60
+ * synchronously; the reply callback therefore completes before this returns.
61
+ * Zero or multiple providers are explicit errors, not invitations to register a
62
+ * second baseline publisher.
63
+ */
64
+ export function requestRadarRuntime(
65
+ events: RadarExtensionEventBus,
66
+ ): { ok: true; handle: RadarRuntimeHandle } | { ok: false; reason: "missing" | "duplicate" } {
67
+ const handles: RadarRuntimeHandle[] = [];
68
+ events.emit(RADAR_RUNTIME_REQUEST, {
69
+ version: RADAR_RUNTIME_VERSION,
70
+ provide: (candidate: unknown) => {
71
+ if (isRadarRuntimeHandle(candidate)) handles.push(candidate);
72
+ },
73
+ } satisfies RuntimeRequest);
74
+ if (handles.length === 1) return { ok: true, handle: handles[0] };
75
+ return { ok: false, reason: handles.length === 0 ? "missing" : "duplicate" };
76
+ }
77
+
78
+ function isRuntimeRequest(value: unknown): value is RuntimeRequest {
79
+ return (
80
+ typeof value === "object" &&
81
+ value !== null &&
82
+ (value as RuntimeRequest).version === RADAR_RUNTIME_VERSION &&
83
+ typeof (value as RuntimeRequest).provide === "function"
84
+ );
85
+ }
86
+
87
+ function isRadarRuntimeHandle(value: unknown): value is RadarRuntimeHandle {
88
+ if (typeof value !== "object" || value === null) return false;
89
+ const handle = value as Partial<RadarRuntimeHandle>;
90
+ return (
91
+ handle.version === RADAR_RUNTIME_VERSION &&
92
+ typeof handle.start === "function"
93
+ );
94
+ }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Radar-issued identity carried as a non-context custom entry in the active Pi
3
+ * session branch. It is a recovery breadcrumb, not an authority to create or
4
+ * mutate a Radar subject.
5
+ */
6
+ import type { LaunchIdentityMarkerSerialize } from "./generated-contracts.ts";
7
+
8
+ export const RADAR_LAUNCH_MARKER_TYPE = "pi-radar.launch-identity";
9
+ export const RADAR_LAUNCH_MARKER_VERSION = 1 as const;
10
+ export const RADAR_MAX_EXECUTION_GENERATION = 0xffff_ffff;
11
+
12
+ export type RadarLaunchMarker = Omit<LaunchIdentityMarkerSerialize, "version"> & {
13
+ version: typeof RADAR_LAUNCH_MARKER_VERSION;
14
+ };
15
+
16
+ /** Structural subset of Pi's custom SessionEntry used by this codec. */
17
+ export type CustomSessionEntry = {
18
+ type: string;
19
+ customType?: string;
20
+ data?: unknown;
21
+ };
22
+
23
+ /** pi.appendEntry-compatible callback; no Pi runtime import is needed here. */
24
+ export type AppendSessionEntry = (customType: string, data?: unknown) => void;
25
+ export type PiSessionMarkerHost = {
26
+ appendEntry?: AppendSessionEntry;
27
+ sessionManager?: { getBranch(): readonly CustomSessionEntry[] };
28
+ };
29
+
30
+ export const RADAR_MANAGED_SUBJECT_ENV = "PI_RADAR_MANAGED_SUBJECT_ID";
31
+ export const RADAR_MANAGED_LAUNCH_ENV = "PI_RADAR_MANAGED_LAUNCH_ID";
32
+ export const RADAR_MANAGED_GENERATION_ENV = "PI_RADAR_MANAGED_EXECUTION_GENERATION";
33
+
34
+ const 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;
35
+
36
+ /** Validates the exact versioned marker shape before writing or accepting it. */
37
+ export function decodeRadarLaunchMarker(value: unknown): RadarLaunchMarker | undefined {
38
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
39
+ const record = value as Record<string, unknown>;
40
+ if (
41
+ Object.keys(record).sort().join(",") !==
42
+ "execution_generation,launch_id,subject_id,version"
43
+ ) {
44
+ return undefined;
45
+ }
46
+ if (
47
+ record.version !== RADAR_LAUNCH_MARKER_VERSION ||
48
+ typeof record.subject_id !== "string" ||
49
+ !UUID.test(record.subject_id) ||
50
+ typeof record.launch_id !== "string" ||
51
+ !UUID.test(record.launch_id) ||
52
+ !Number.isSafeInteger(record.execution_generation) ||
53
+ (record.execution_generation as number) < 1 ||
54
+ (record.execution_generation as number) > RADAR_MAX_EXECUTION_GENERATION
55
+ ) {
56
+ return undefined;
57
+ }
58
+ return {
59
+ version: RADAR_LAUNCH_MARKER_VERSION,
60
+ subject_id: record.subject_id,
61
+ launch_id: record.launch_id,
62
+ execution_generation: record.execution_generation as number,
63
+ };
64
+ }
65
+
66
+ /** Appends a valid Radar-issued marker as a Pi custom session entry. */
67
+ export function appendRadarLaunchMarker(
68
+ appendEntry: AppendSessionEntry,
69
+ marker: RadarLaunchMarker,
70
+ ): void {
71
+ const validated = decodeRadarLaunchMarker(marker);
72
+ if (!validated) throw new Error("invalid Radar launch identity marker");
73
+ appendEntry(RADAR_LAUNCH_MARKER_TYPE, validated);
74
+ }
75
+
76
+ /**
77
+ * Reads the newest marker on Pi's current branch. A malformed newest marker is
78
+ * returned as unresolved rather than silently falling back to an older launch.
79
+ */
80
+ export function latestRadarLaunchMarker(
81
+ branch: readonly CustomSessionEntry[],
82
+ ): { status: "absent" } | { status: "valid"; marker: RadarLaunchMarker } | { status: "invalid" } {
83
+ let latest: CustomSessionEntry | undefined;
84
+ for (const entry of branch) {
85
+ if (entry.type === "custom" && entry.customType === RADAR_LAUNCH_MARKER_TYPE) {
86
+ latest = entry;
87
+ }
88
+ }
89
+ if (!latest) return { status: "absent" };
90
+ const marker = decodeRadarLaunchMarker(latest.data);
91
+ return marker ? { status: "valid", marker } : { status: "invalid" };
92
+ }
93
+
94
+ /**
95
+ * Persist this daemon-injected process identity into the active Pi session branch.
96
+ * No marker is written when any variable is absent or malformed. Repeated session
97
+ * starts in the same generation do not append duplicate entries.
98
+ */
99
+ export function radarLaunchMarkerFromEnvironment(
100
+ env: Record<string, string | undefined> = process.env,
101
+ ): RadarLaunchMarker | undefined {
102
+ const rawGeneration = env[RADAR_MANAGED_GENERATION_ENV];
103
+ if (
104
+ env[RADAR_MANAGED_SUBJECT_ENV] === undefined ||
105
+ env[RADAR_MANAGED_LAUNCH_ENV] === undefined ||
106
+ rawGeneration === undefined ||
107
+ !/^[1-9][0-9]*$/.test(rawGeneration)
108
+ ) {
109
+ return undefined;
110
+ }
111
+ return decodeRadarLaunchMarker({
112
+ version: RADAR_LAUNCH_MARKER_VERSION,
113
+ subject_id: env[RADAR_MANAGED_SUBJECT_ENV],
114
+ launch_id: env[RADAR_MANAGED_LAUNCH_ENV],
115
+ execution_generation: Number(rawGeneration),
116
+ });
117
+ }
118
+
119
+ export function activeRadarLaunchMarker(
120
+ host: PiSessionMarkerHost,
121
+ ): ReturnType<typeof latestRadarLaunchMarker> {
122
+ if (!host.sessionManager) return { status: "absent" };
123
+ return latestRadarLaunchMarker(host.sessionManager.getBranch());
124
+ }
125
+
126
+ export function persistRadarLaunchMarker(
127
+ host: PiSessionMarkerHost,
128
+ env: Record<string, string | undefined> = process.env,
129
+ ): "absent" | "unsupported" | "unchanged" | "written" {
130
+ const marker = radarLaunchMarkerFromEnvironment(env);
131
+ if (!marker) return "absent";
132
+ if (!host.appendEntry || !host.sessionManager) return "unsupported";
133
+ const current = latestRadarLaunchMarker(host.sessionManager.getBranch());
134
+ if (current.status === "valid" && sameMarker(current.marker, marker)) return "unchanged";
135
+ appendRadarLaunchMarker(host.appendEntry, marker);
136
+ return "written";
137
+ }
138
+
139
+ function sameMarker(a: RadarLaunchMarker, b: RadarLaunchMarker): boolean {
140
+ return (
141
+ a.version === b.version &&
142
+ a.subject_id === b.subject_id &&
143
+ a.launch_id === b.launch_id &&
144
+ a.execution_generation === b.execution_generation
145
+ );
146
+ }
package/index.ts CHANGED
@@ -4,3 +4,4 @@ export * as inspection from "./extension/radar-inspection.ts";
4
4
  export * as launch from "./extension/radar-launch.ts";
5
5
  export * as location from "./extension/radar-location.ts";
6
6
  export * as publication from "./extension/radar-publication.ts";
7
+ export * as runtime from "./extension/radar-runtime.ts";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-radar",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "Radar daemon client, publication, execution, location, launch and inspection surface for Pi extensions.",
6
6
  "license": "Apache-2.0",
@@ -13,6 +13,9 @@
13
13
  "directory": "pi-radar"
14
14
  },
15
15
  "types": "./index.ts",
16
+ "pi": {
17
+ "extensions": ["./extension/baseline.ts"]
18
+ },
16
19
  "exports": {
17
20
  ".": {
18
21
  "types": "./index.ts",
@@ -30,6 +33,14 @@
30
33
  "types": "./extension/radar-publication.ts",
31
34
  "import": "./dist/extension/radar-publication.js"
32
35
  },
36
+ "./runtime": {
37
+ "types": "./extension/radar-runtime.ts",
38
+ "import": "./dist/extension/radar-runtime.js"
39
+ },
40
+ "./baseline": {
41
+ "types": "./extension/baseline.ts",
42
+ "import": "./dist/extension/baseline.js"
43
+ },
33
44
  "./launch": {
34
45
  "types": "./extension/radar-launch.ts",
35
46
  "import": "./dist/extension/radar-launch.js"
@@ -60,13 +71,16 @@
60
71
  "extension/radar-inspection.ts",
61
72
  "extension/radar-launch.ts",
62
73
  "extension/radar-location.ts",
63
- "extension/radar-publication.ts"
74
+ "extension/radar-publication.ts",
75
+ "extension/radar-session-marker.ts",
76
+ "extension/radar-runtime.ts",
77
+ "extension/baseline.ts"
64
78
  ],
65
79
  "scripts": {
66
- "build": "esbuild index.ts extension/generated-contracts.ts extension/radar-client.ts extension/radar-execution.ts extension/radar-inspection.ts extension/radar-launch.ts extension/radar-location.ts extension/radar-publication.ts --outbase=. --outdir=dist --bundle --platform=node --format=esm --target=node22",
80
+ "build": "esbuild index.ts extension/generated-contracts.ts extension/radar-client.ts extension/radar-execution.ts extension/radar-inspection.ts extension/radar-launch.ts extension/radar-location.ts extension/radar-publication.ts extension/radar-session-marker.ts extension/radar-runtime.ts extension/baseline.ts --outbase=. --outdir=dist --bundle --platform=node --format=esm --target=node22",
67
81
  "generate:contracts": "node tools/generate-contracts.mjs",
68
82
  "check:contracts": "node tools/generate-contracts.mjs --check",
69
- "typecheck:contracts": "tsc --noEmit --strict --skipLibCheck --target ES2022 --module NodeNext --moduleResolution NodeNext --allowImportingTsExtensions --types node extension/generated-contracts.ts extension/generated-contracts.typecheck.ts",
83
+ "typecheck:contracts": "tsc --noEmit --strict --skipLibCheck --target ES2022 --module NodeNext --moduleResolution NodeNext --allowImportingTsExtensions --types node index.ts extension/baseline.ts extension/generated-contracts.ts extension/generated-contracts.typecheck.ts extension/radar-client.ts extension/radar-execution.ts extension/radar-publication.ts extension/radar-session-marker.ts extension/radar-session-marker.test.ts extension/radar-runtime.ts extension/radar-runtime.test.ts",
70
84
  "prepack": "npm run build",
71
85
  "test": "npm run check:contracts && npm run typecheck:contracts && node --experimental-strip-types --test extension/*.test.ts"
72
86
  },
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "additionalProperties": false,
4
+ "description": "A non-secret Pi-session breadcrumb issued by Radar. This is correlation\nevidence only and cannot establish lifecycle authority.",
5
+ "properties": {
6
+ "execution_generation": {
7
+ "format": "uint32",
8
+ "maximum": 4294967295,
9
+ "minimum": 1,
10
+ "type": "integer"
11
+ },
12
+ "launch_id": {
13
+ "type": "string"
14
+ },
15
+ "subject_id": {
16
+ "type": "string"
17
+ },
18
+ "version": {
19
+ "format": "uint32",
20
+ "minimum": 0,
21
+ "type": "integer"
22
+ }
23
+ },
24
+ "required": [
25
+ "version",
26
+ "subject_id",
27
+ "launch_id",
28
+ "execution_generation"
29
+ ],
30
+ "title": "launch-identity-marker-deserialize",
31
+ "type": "object"
32
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "additionalProperties": false,
4
+ "description": "A non-secret Pi-session breadcrumb issued by Radar. This is correlation\nevidence only and cannot establish lifecycle authority.",
5
+ "properties": {
6
+ "execution_generation": {
7
+ "format": "uint32",
8
+ "maximum": 4294967295,
9
+ "minimum": 1,
10
+ "type": "integer"
11
+ },
12
+ "launch_id": {
13
+ "type": "string"
14
+ },
15
+ "subject_id": {
16
+ "type": "string"
17
+ },
18
+ "version": {
19
+ "format": "uint32",
20
+ "minimum": 0,
21
+ "type": "integer"
22
+ }
23
+ },
24
+ "required": [
25
+ "version",
26
+ "subject_id",
27
+ "launch_id",
28
+ "execution_generation"
29
+ ],
30
+ "title": "launch-identity-marker-serialize",
31
+ "type": "object"
32
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "additionalProperties": false,
4
+ "description": "Managed owner/parent metadata accepted with a spawn intent. Domain validation\nand parent-binding checks remain in the daemon handler.",
5
+ "properties": {
6
+ "label": {
7
+ "type": [
8
+ "string",
9
+ "null"
10
+ ]
11
+ },
12
+ "owner": {
13
+ "type": [
14
+ "string",
15
+ "null"
16
+ ]
17
+ },
18
+ "parent_subject_id": {
19
+ "type": [
20
+ "string",
21
+ "null"
22
+ ]
23
+ },
24
+ "role": {
25
+ "type": [
26
+ "string",
27
+ "null"
28
+ ]
29
+ },
30
+ "run": {
31
+ "type": [
32
+ "string",
33
+ "null"
34
+ ]
35
+ }
36
+ },
37
+ "title": "managed-launch-metadata-deserialize",
38
+ "type": "object"
39
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "additionalProperties": false,
4
+ "description": "Managed owner/parent metadata accepted with a spawn intent. Domain validation\nand parent-binding checks remain in the daemon handler.",
5
+ "properties": {
6
+ "label": {
7
+ "type": [
8
+ "string",
9
+ "null"
10
+ ]
11
+ },
12
+ "owner": {
13
+ "type": [
14
+ "string",
15
+ "null"
16
+ ]
17
+ },
18
+ "parent_subject_id": {
19
+ "type": [
20
+ "string",
21
+ "null"
22
+ ]
23
+ },
24
+ "role": {
25
+ "type": [
26
+ "string",
27
+ "null"
28
+ ]
29
+ },
30
+ "run": {
31
+ "type": [
32
+ "string",
33
+ "null"
34
+ ]
35
+ }
36
+ },
37
+ "title": "managed-launch-metadata-serialize",
38
+ "type": "object"
39
+ }