@airprompter/agent-core 0.1.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.
Files changed (129) hide show
  1. package/README.md +28 -0
  2. package/dist/cjs/bundle/apbundle.js +98 -0
  3. package/dist/cjs/bundle/apbundle.js.map +1 -0
  4. package/dist/cjs/bundle/hpke.js +120 -0
  5. package/dist/cjs/bundle/hpke.js.map +1 -0
  6. package/dist/cjs/checks/index.js +450 -0
  7. package/dist/cjs/checks/index.js.map +1 -0
  8. package/dist/cjs/control/client.js +85 -0
  9. package/dist/cjs/control/client.js.map +1 -0
  10. package/dist/cjs/golden/index.js +138 -0
  11. package/dist/cjs/golden/index.js.map +1 -0
  12. package/dist/cjs/index.js +125 -0
  13. package/dist/cjs/index.js.map +1 -0
  14. package/dist/cjs/judge/index.js +134 -0
  15. package/dist/cjs/judge/index.js.map +1 -0
  16. package/dist/cjs/package.json +1 -0
  17. package/dist/cjs/ports/node.js +30 -0
  18. package/dist/cjs/ports/node.js.map +1 -0
  19. package/dist/cjs/protocol/assignment.js +158 -0
  20. package/dist/cjs/protocol/assignment.js.map +1 -0
  21. package/dist/cjs/protocol/canonicalJson.js +72 -0
  22. package/dist/cjs/protocol/canonicalJson.js.map +1 -0
  23. package/dist/cjs/protocol/errors.js +22 -0
  24. package/dist/cjs/protocol/errors.js.map +1 -0
  25. package/dist/cjs/protocol/ports.js +19 -0
  26. package/dist/cjs/protocol/ports.js.map +1 -0
  27. package/dist/cjs/protocol/trust.js +225 -0
  28. package/dist/cjs/protocol/trust.js.map +1 -0
  29. package/dist/cjs/protocol/types.js +15 -0
  30. package/dist/cjs/protocol/types.js.map +1 -0
  31. package/dist/cjs/protocol/version.js +8 -0
  32. package/dist/cjs/protocol/version.js.map +1 -0
  33. package/dist/cjs/release/bundleRelease.js +61 -0
  34. package/dist/cjs/release/bundleRelease.js.map +1 -0
  35. package/dist/cjs/release/reader.js +12 -0
  36. package/dist/cjs/release/reader.js.map +1 -0
  37. package/dist/cjs/render/runRef.js +33 -0
  38. package/dist/cjs/render/runRef.js.map +1 -0
  39. package/dist/cjs/render/template.js +71 -0
  40. package/dist/cjs/render/template.js.map +1 -0
  41. package/dist/cjs/telemetry/feedback.js +81 -0
  42. package/dist/cjs/telemetry/feedback.js.map +1 -0
  43. package/dist/cjs/telemetry/rows.js +28 -0
  44. package/dist/cjs/telemetry/rows.js.map +1 -0
  45. package/dist/cjs/telemetry/uploadSink.js +13 -0
  46. package/dist/cjs/telemetry/uploadSink.js.map +1 -0
  47. package/dist/cjs/testing/controlPlane.js +381 -0
  48. package/dist/cjs/testing/controlPlane.js.map +1 -0
  49. package/dist/cjs/testing/index.js +45 -0
  50. package/dist/cjs/testing/index.js.map +1 -0
  51. package/dist/cjs/testing/memoryFs.js +236 -0
  52. package/dist/cjs/testing/memoryFs.js.map +1 -0
  53. package/dist/esm/.tsbuildinfo +1 -0
  54. package/dist/esm/bundle/apbundle.d.ts +33 -0
  55. package/dist/esm/bundle/apbundle.js +89 -0
  56. package/dist/esm/bundle/apbundle.js.map +1 -0
  57. package/dist/esm/bundle/hpke.d.ts +48 -0
  58. package/dist/esm/bundle/hpke.js +107 -0
  59. package/dist/esm/bundle/hpke.js.map +1 -0
  60. package/dist/esm/checks/index.d.ts +85 -0
  61. package/dist/esm/checks/index.js +436 -0
  62. package/dist/esm/checks/index.js.map +1 -0
  63. package/dist/esm/control/client.d.ts +78 -0
  64. package/dist/esm/control/client.js +81 -0
  65. package/dist/esm/control/client.js.map +1 -0
  66. package/dist/esm/golden/index.d.ts +97 -0
  67. package/dist/esm/golden/index.js +130 -0
  68. package/dist/esm/golden/index.js.map +1 -0
  69. package/dist/esm/index.d.ts +41 -0
  70. package/dist/esm/index.js +31 -0
  71. package/dist/esm/index.js.map +1 -0
  72. package/dist/esm/judge/index.d.ts +51 -0
  73. package/dist/esm/judge/index.js +127 -0
  74. package/dist/esm/judge/index.js.map +1 -0
  75. package/dist/esm/ports/node.d.ts +8 -0
  76. package/dist/esm/ports/node.js +27 -0
  77. package/dist/esm/ports/node.js.map +1 -0
  78. package/dist/esm/protocol/assignment.d.ts +49 -0
  79. package/dist/esm/protocol/assignment.js +143 -0
  80. package/dist/esm/protocol/assignment.js.map +1 -0
  81. package/dist/esm/protocol/canonicalJson.d.ts +10 -0
  82. package/dist/esm/protocol/canonicalJson.js +65 -0
  83. package/dist/esm/protocol/canonicalJson.js.map +1 -0
  84. package/dist/esm/protocol/errors.d.ts +15 -0
  85. package/dist/esm/protocol/errors.js +19 -0
  86. package/dist/esm/protocol/errors.js.map +1 -0
  87. package/dist/esm/protocol/ports.d.ts +65 -0
  88. package/dist/esm/protocol/ports.js +16 -0
  89. package/dist/esm/protocol/ports.js.map +1 -0
  90. package/dist/esm/protocol/trust.d.ts +60 -0
  91. package/dist/esm/protocol/trust.js +211 -0
  92. package/dist/esm/protocol/trust.js.map +1 -0
  93. package/dist/esm/protocol/types.d.ts +235 -0
  94. package/dist/esm/protocol/types.js +12 -0
  95. package/dist/esm/protocol/types.js.map +1 -0
  96. package/dist/esm/protocol/version.d.ts +4 -0
  97. package/dist/esm/protocol/version.js +5 -0
  98. package/dist/esm/protocol/version.js.map +1 -0
  99. package/dist/esm/release/bundleRelease.d.ts +45 -0
  100. package/dist/esm/release/bundleRelease.js +57 -0
  101. package/dist/esm/release/bundleRelease.js.map +1 -0
  102. package/dist/esm/release/reader.d.ts +30 -0
  103. package/dist/esm/release/reader.js +11 -0
  104. package/dist/esm/release/reader.js.map +1 -0
  105. package/dist/esm/render/runRef.d.ts +17 -0
  106. package/dist/esm/render/runRef.js +29 -0
  107. package/dist/esm/render/runRef.js.map +1 -0
  108. package/dist/esm/render/template.d.ts +38 -0
  109. package/dist/esm/render/template.js +65 -0
  110. package/dist/esm/render/template.js.map +1 -0
  111. package/dist/esm/telemetry/feedback.d.ts +21 -0
  112. package/dist/esm/telemetry/feedback.js +77 -0
  113. package/dist/esm/telemetry/feedback.js.map +1 -0
  114. package/dist/esm/telemetry/rows.d.ts +85 -0
  115. package/dist/esm/telemetry/rows.js +22 -0
  116. package/dist/esm/telemetry/rows.js.map +1 -0
  117. package/dist/esm/telemetry/uploadSink.d.ts +52 -0
  118. package/dist/esm/telemetry/uploadSink.js +12 -0
  119. package/dist/esm/telemetry/uploadSink.js.map +1 -0
  120. package/dist/esm/testing/controlPlane.d.ts +116 -0
  121. package/dist/esm/testing/controlPlane.js +341 -0
  122. package/dist/esm/testing/controlPlane.js.map +1 -0
  123. package/dist/esm/testing/index.d.ts +25 -0
  124. package/dist/esm/testing/index.js +35 -0
  125. package/dist/esm/testing/index.js.map +1 -0
  126. package/dist/esm/testing/memoryFs.d.ts +64 -0
  127. package/dist/esm/testing/memoryFs.js +232 -0
  128. package/dist/esm/testing/memoryFs.js.map +1 -0
  129. package/package.json +45 -0
@@ -0,0 +1,341 @@
1
+ /**
2
+ * A fake control plane for SDK tests: an offline root ceremony, a signing
3
+ * key, sealed manifests per generation, payloads by hash, an edge pointer —
4
+ * served through a `fetch` the SDK takes as an option. Signs exactly as the
5
+ * hosted service does (canonical payload bytes, ES256, P1363).
6
+ */
7
+ import { generateKeyPairSync } from "node:crypto";
8
+ import { canonicalBytes, sha256Prefixed } from "../protocol/canonicalJson.js";
9
+ import { keyThumbprint, publicJwkOf, releaseDigest, signBytes } from "../protocol/trust.js";
10
+ import { PROTOCOL_VERSION } from "../protocol/version.js";
11
+ /** The protocol version this checkout of the repository declares; manifests the fake signs carry it. */
12
+ export const PROTOCOL = PROTOCOL_VERSION;
13
+ export function newKey() {
14
+ const { privateKey } = generateKeyPairSync("ec", { namedCurve: "P-256" });
15
+ const jwk = privateKey.export({ format: "jwk" });
16
+ return { kty: "EC", crv: "P-256", x: jwk.x, y: jwk.y, d: jwk.d };
17
+ }
18
+ export function rootDocument(input) {
19
+ const keys = {};
20
+ const rootId = keyThumbprint(input.rootKey);
21
+ keys[rootId] = { keyType: "ecdsa-p256", scheme: "ES256", publicKey: publicJwkOf(input.rootKey) };
22
+ const targetIds = input.signingKeys.map((key) => {
23
+ const id = keyThumbprint(key);
24
+ keys[id] = { keyType: "ecdsa-p256", scheme: "ES256", publicKey: publicJwkOf(key) };
25
+ return id;
26
+ });
27
+ const signed = {
28
+ type: "root",
29
+ protocol: PROTOCOL,
30
+ purpose: input.purpose ?? "platform",
31
+ environment: input.environment,
32
+ version: input.version ?? 1,
33
+ expires: input.expires ?? "2027-12-11T00:00:00Z",
34
+ keys,
35
+ roles: { root: { keyIds: [rootId], threshold: 1 }, targets: { keyIds: targetIds, threshold: 1 } },
36
+ };
37
+ return { signed, signatures: [{ keyId: rootId, alg: "ES256", sig: signBytes(canonicalBytes(signed), input.rootKey) }] };
38
+ }
39
+ export class FakeControlPlane {
40
+ scope;
41
+ apiKey;
42
+ rootKey;
43
+ signingKey;
44
+ root;
45
+ payloads = new Map();
46
+ requests = [];
47
+ current = null;
48
+ generation = 0;
49
+ edgeEtag = 0;
50
+ requireCountersign = false;
51
+ /**
52
+ * Fresh keys by default (a test). `airprompter dev` (S12) hands in the keys it persisted, so the root a customer
53
+ * pinned yesterday still verifies today's generations.
54
+ */
55
+ constructor(scope, apiKey = "apa_live_testkey", keys = {}) {
56
+ this.scope = scope;
57
+ this.apiKey = apiKey;
58
+ this.rootKey = keys.rootKey ?? newKey();
59
+ this.signingKey = keys.signingKey ?? newKey();
60
+ this.root = rootDocument({ rootKey: this.rootKey, signingKeys: [this.signingKey], environment: scope.target });
61
+ }
62
+ slot(spec) {
63
+ const bytes = Buffer.from(spec.text, "utf8");
64
+ const hash = sha256Prefixed(bytes);
65
+ this.payloads.set(hash, bytes);
66
+ const slot = {
67
+ tag: spec.tag,
68
+ kind: spec.steps ? "workflow" : "prompt",
69
+ artifactId: `art_${spec.tag}`,
70
+ versionId: spec.versionId ?? `ver_${spec.tag}_1`,
71
+ versionOrdinal: 1,
72
+ contentHash: hash,
73
+ byteLength: bytes.length,
74
+ model: spec.model ?? "claude-sonnet-5",
75
+ variables: spec.variables ?? [],
76
+ };
77
+ if (spec.steps) {
78
+ slot.steps = spec.steps.map((step, index) => {
79
+ const stepBytes = Buffer.from(step.text, "utf8");
80
+ const stepHash = sha256Prefixed(stepBytes);
81
+ this.payloads.set(stepHash, stepBytes);
82
+ return { stepId: `${spec.tag}#${index + 1}`, ordinal: index + 1, promptArtifactId: `art_${spec.tag}_${index + 1}`, promptVersionId: step.versionId ?? `ver_${spec.tag}_step${index + 1}`, contentHash: stepHash, byteLength: stepBytes.length };
83
+ });
84
+ }
85
+ return slot;
86
+ }
87
+ /** Seal and promote: generation + 1, signed with the signing key. */
88
+ /** T9: every heartbeat body the fake accepted, and what it answers (a test may change the cadence). */
89
+ heartbeats = [];
90
+ heartbeatIntervalSeconds = 300;
91
+ heartbeatRefusal = null;
92
+ /**
93
+ * T26: the grant issuer and a fake S3 behind it. With `grantBaseUrl` set, every accepted heartbeat answers an
94
+ * `uploadGrant` for the body's instance prefix (or `retryAfterSeconds` while `grantHold` is set); the POST endpoint at
95
+ * `<grantBaseUrl>/s3/agent-telemetry` checks the policy the way the bucket would and keeps the objects by key.
96
+ */
97
+ grantBaseUrl = null;
98
+ /** S3: a party between the fleet and the edge pins the pointer at this generation; the origin moves on regardless. */
99
+ pinnedPointer = null;
100
+ /** S3: whether the heartbeat answer names the origin's generation (an older service does not). */
101
+ heartbeatLatestGeneration = true;
102
+ grantTtlMs = 15 * 60 * 1000;
103
+ grantHold = null;
104
+ uploadIntervalSeconds = 300;
105
+ grants = [];
106
+ objects = new Map();
107
+ uploads = [];
108
+ /** A test may fail the next N POSTs (a 500) to exercise backoff. */
109
+ failNextUploads = 0;
110
+ /** The issuer's and the bucket's clock (grant expiry, policy expiry); a test drives it beside the uploader's. */
111
+ now = () => Date.now();
112
+ grantSeq = 0;
113
+ issueGrant(instanceId, now) {
114
+ const grantId = `grant_${String(++this.grantSeq).padStart(4, "0")}_${instanceId.slice(0, 8)}`.replace(/[^A-Za-z0-9_-]/g, "_").padEnd(16, "0");
115
+ const keyPrefix = `org/${this.scope.organizationId}/agent/${this.scope.agentId}/${this.scope.target}/${instanceId}/`;
116
+ const expiresAt = new Date(now + this.grantTtlMs).toISOString();
117
+ const policy = Buffer.from(JSON.stringify({ expiration: expiresAt, conditions: [["starts-with", "$key", keyPrefix], ["content-length-range", 0, 1048576], { "Content-Type": "application/x-ndjson" }, { "x-amz-meta-grant-id": grantId }] })).toString("base64");
118
+ this.grants.push({ grantId, instanceId, keyPrefix, expiresAt });
119
+ return {
120
+ grantId,
121
+ url: `${this.grantBaseUrl}/s3/agent-telemetry`,
122
+ fields: { policy, "x-amz-algorithm": "AWS4-HMAC-SHA256", "x-amz-credential": "AKIAFAKE/20260912/eu-west-1/s3/aws4_request", "x-amz-date": "20260912T000000Z", "x-amz-signature": "fake", "x-amz-server-side-encryption": "aws:kms", "x-amz-server-side-encryption-aws-kms-key-id": "arn:aws:kms:eu-west-1:000000000000:key/fake", "x-amz-meta-grant-id": grantId },
123
+ keyPrefix,
124
+ expiresAt,
125
+ maxObjectBytes: 1048576,
126
+ contentType: "application/x-ndjson",
127
+ };
128
+ }
129
+ /** The bucket's side of a presigned POST: the policy's conditions, the expiry, the size cap; nothing else is looked at. */
130
+ acceptUpload(init, now) {
131
+ const contentType = init?.headers?.["content-type"] ?? "";
132
+ const boundary = /boundary=(.+)$/.exec(contentType)?.[1];
133
+ if (!boundary || !init?.body)
134
+ return { status: 400, body: "<Error><Code>MalformedPOSTRequest</Code></Error>" };
135
+ const raw = typeof init.body === "string" ? Buffer.from(init.body, "utf8") : Buffer.from(init.body);
136
+ const fields = new Map();
137
+ const marker = Buffer.from(`--${boundary}`);
138
+ let offset = raw.indexOf(marker);
139
+ while (offset !== -1) {
140
+ const next = raw.indexOf(marker, offset + marker.length);
141
+ if (next === -1)
142
+ break;
143
+ const part = raw.subarray(offset + marker.length + 2, next - 2); // skip CRLF after the marker; drop CRLF before the next
144
+ const headerEnd = part.indexOf("\r\n\r\n");
145
+ const headers = part.subarray(0, headerEnd).toString("utf8");
146
+ const name = /name="([^"]+)"/.exec(headers)?.[1];
147
+ if (name)
148
+ fields.set(name, part.subarray(headerEnd + 4));
149
+ offset = next;
150
+ }
151
+ if (this.failNextUploads > 0) {
152
+ this.failNextUploads -= 1;
153
+ return { status: 500, body: "<Error><Code>InternalError</Code></Error>" };
154
+ }
155
+ const policyText = fields.get("policy")?.toString("utf8");
156
+ const policy = policyText ? JSON.parse(Buffer.from(policyText, "base64").toString("utf8")) : null;
157
+ if (!policy)
158
+ return { status: 403, body: "<Error><Code>AccessDenied</Code><Message>Invalid according to Policy: Policy missing</Message></Error>" };
159
+ if (Date.parse(policy.expiration) <= now)
160
+ return { status: 403, body: "<Error><Code>AccessDenied</Code><Message>Invalid according to Policy: Policy expired.</Message></Error>" };
161
+ const key = fields.get("key")?.toString("utf8") ?? "";
162
+ const grantId = fields.get("x-amz-meta-grant-id")?.toString("utf8");
163
+ const grant = this.grants.find((g) => g.grantId === grantId);
164
+ if (!grant || !key.startsWith(grant.keyPrefix))
165
+ return { status: 403, body: "<Error><Code>AccessDenied</Code><Message>Invalid according to Policy: Policy Condition failed: [\"starts-with\", \"$key\", ...]</Message></Error>" };
166
+ if (fields.get("Content-Type")?.toString("utf8") !== "application/x-ndjson")
167
+ return { status: 403, body: "<Error><Code>AccessDenied</Code><Message>Invalid according to Policy: Content-Type</Message></Error>" };
168
+ if (fields.get("x-amz-server-side-encryption")?.toString("utf8") !== "aws:kms")
169
+ return { status: 403, body: "<Error><Code>AccessDenied</Code><Message>Invalid according to Policy: SSE</Message></Error>" };
170
+ const file = fields.get("file");
171
+ if (!file)
172
+ return { status: 400, body: "<Error><Code>InvalidArgument</Code><Message>POST requires exactly one file upload per request.</Message></Error>" };
173
+ if (file.length > 1048576)
174
+ return { status: 400, body: "<Error><Code>EntityTooLarge</Code></Error>" };
175
+ this.objects.set(key, Buffer.from(file));
176
+ this.uploads.push(key);
177
+ return { status: 204, body: "" };
178
+ }
179
+ promote(slots, options = {}) {
180
+ const sorted = [...slots].sort((a, b) => (a.tag < b.tag ? -1 : 1));
181
+ this.generation = options.generation ?? this.generation + 1;
182
+ const payload = {
183
+ protocol: PROTOCOL,
184
+ ...this.scope,
185
+ generation: this.generation,
186
+ releaseDigest: releaseDigest(sorted),
187
+ issuedAt: new Date().toISOString(),
188
+ leaseSeconds: options.leaseSeconds ?? 3600,
189
+ onLeaseExpiry: options.onLeaseExpiry ?? "degrade",
190
+ applyPolicy: options.applyPolicy ?? "auto",
191
+ ...(options.unlockWindow ? { unlockWindow: options.unlockWindow } : {}),
192
+ requireCountersign: this.requireCountersign,
193
+ slots: sorted,
194
+ ...(options.experiment ? { experiment: options.experiment } : {}),
195
+ directives: options.directives ?? [],
196
+ };
197
+ const signer = options.signWith ?? this.signingKey;
198
+ const manifest = { payload, signatures: [{ keyId: keyThumbprint(signer), alg: "ES256", sig: signBytes(canonicalBytes(payload), signer) }], countersignatures: [] };
199
+ const bytes = Buffer.from(JSON.stringify(manifest), "utf8");
200
+ this.current = { manifest, bytes, etag: sha256Prefixed(bytes) };
201
+ this.edgeEtag += 1;
202
+ return manifest;
203
+ }
204
+ get manifest() {
205
+ return this.current?.manifest ?? null;
206
+ }
207
+ /** The `fetch` the SDK sees. */
208
+ fetch() {
209
+ const respond = (status, body = "", headers = {}) => ({
210
+ status,
211
+ headers: { get: (name) => headers[name.toLowerCase()] ?? null },
212
+ /** Every header, for a test that bridges this fake onto a real HTTP listener. */
213
+ headerEntries: Object.entries(headers),
214
+ arrayBuffer: async () => {
215
+ // A Buffer may be a view into a shared pool: copy exactly its bytes, never the pool.
216
+ const bytes = typeof body === "string" ? Buffer.from(body, "utf8") : body;
217
+ return new Uint8Array(bytes).buffer;
218
+ },
219
+ text: async () => (typeof body === "string" ? body : body.toString("utf8")),
220
+ });
221
+ return async (url, init) => {
222
+ this.requests.push(url);
223
+ const auth = init?.headers?.authorization ?? init?.headers?.Authorization;
224
+ const parsed = new URL(url);
225
+ if (parsed.pathname.endsWith("/generation.json")) {
226
+ if (!this.current)
227
+ return respond(404);
228
+ if (this.pinnedPointer) {
229
+ // The pinned pointer: the same stale answer, the same ETag, forever.
230
+ const pinnedEtag = `"edge-pinned-${this.pinnedPointer.generation}"`;
231
+ if (init?.headers?.["if-none-match"] === pinnedEtag)
232
+ return respond(304);
233
+ return respond(200, JSON.stringify({ generation: this.pinnedPointer.generation, releaseDigest: this.current.manifest.payload.releaseDigest, leaseSeconds: this.current.manifest.payload.leaseSeconds }), { etag: pinnedEtag });
234
+ }
235
+ const etag = `"edge-${this.edgeEtag}"`;
236
+ if (init?.headers?.["if-none-match"] === etag)
237
+ return respond(304);
238
+ return respond(200, JSON.stringify({ generation: this.current.manifest.payload.generation, releaseDigest: this.current.manifest.payload.releaseDigest, leaseSeconds: this.current.manifest.payload.leaseSeconds }), { etag });
239
+ }
240
+ if (parsed.pathname.endsWith("/root.json"))
241
+ return respond(200, JSON.stringify(this.root));
242
+ if (parsed.pathname === "/s3/agent-telemetry") {
243
+ const reply = this.acceptUpload(init, this.now());
244
+ return respond(reply.status, reply.body, { "content-type": "application/xml" });
245
+ }
246
+ if (auth !== `Bearer ${this.apiKey}`)
247
+ return respond(401, JSON.stringify({ error: "Unauthorized" }));
248
+ // T23: the hosted catalogue — the manifest's tags, variables, step ids and experiment, no payloads.
249
+ const slotsMatch = /^\/v1\/agents\/([^/]+)\/targets\/([^/]+)\/slots$/.exec(parsed.pathname);
250
+ if (slotsMatch) {
251
+ if (slotsMatch[1] !== this.scope.agentId || slotsMatch[2] !== this.scope.target)
252
+ return respond(403, JSON.stringify({ error: "Forbidden", code: "forbidden", detail: slotsMatch[1] !== this.scope.agentId ? "agent_mismatch" : "target_mismatch" }));
253
+ if (!this.current)
254
+ return respond(404, JSON.stringify({ error: "nothing is promoted to this environment", code: "nothing_promoted" }));
255
+ const payload = this.current.manifest.payload;
256
+ return respond(200, JSON.stringify({ agentId: payload.agentId, target: payload.target, generation: payload.generation, releaseDigest: payload.releaseDigest, slots: payload.slots.map((pin) => ({ tag: pin.tag, kind: pin.kind, model: pin.model, variables: pin.variables, steps: pin.steps ? pin.steps.map((s) => ({ stepId: s.stepId })) : null })), experiment: payload.experiment ? { salt: payload.experiment.salt, subjectKey: payload.experiment.subjectKey, arms: payload.experiment.arms.map((a) => a.arm) } : null }), { "x-agent-generation": String(payload.generation) });
257
+ }
258
+ // T9: the heartbeat — the protocol schema's required keys, content-free; answers the cadence and the expiry.
259
+ const heartbeatMatch = /^\/v1\/agents\/([^/]+)\/targets\/([^/]+)\/heartbeat$/.exec(parsed.pathname);
260
+ if (heartbeatMatch) {
261
+ if (heartbeatMatch[1] !== this.scope.agentId || heartbeatMatch[2] !== this.scope.target)
262
+ return respond(403, JSON.stringify({ error: "x", details: { code: heartbeatMatch[1] !== this.scope.agentId ? "agent_mismatch" : "target_mismatch" } }));
263
+ if (this.heartbeatRefusal)
264
+ return respond(this.heartbeatRefusal.status, JSON.stringify({ error: "x", details: { code: this.heartbeatRefusal.code } }));
265
+ const body = JSON.parse(typeof init?.body === "string" ? init.body : init?.body ? Buffer.from(init.body).toString("utf8") : "{}");
266
+ for (const key of ["protocol", "instanceId", "sdk", "syncMode", "generation", "applyState", "storageProtection", "catalog", "lease", "spool"]) {
267
+ if (!(key in body))
268
+ return respond(400, JSON.stringify({ error: `heartbeat: missing ${key}` }));
269
+ }
270
+ for (const key of Object.keys(body)) {
271
+ if (!["protocol", "instanceId", "instanceClass", "sdk", "host", "syncMode", "heartbeatIntervalSeconds", "generation", "activeReleaseDigest", "stagedReleaseDigest", "applyState", "refusal", "signingKeyId", "storageProtection", "catalog", "lease", "localRollback", "spool", "unlockRequestsSeen", "disabled", "applyPolicy"].includes(key))
272
+ return respond(400, JSON.stringify({ error: `heartbeat: unknown ${key}` }));
273
+ }
274
+ this.heartbeats.push(body);
275
+ const answer = { pollSeconds: 30, uploadIntervalSeconds: this.uploadIntervalSeconds, heartbeatIntervalSeconds: this.heartbeatIntervalSeconds, expiresAt: new Date(Date.now() + this.heartbeatIntervalSeconds * 3000).toISOString() };
276
+ // S3: the authenticated answer names the origin's generation; a runtime whose pointer says less goes to the manifest.
277
+ if (this.heartbeatLatestGeneration)
278
+ answer.latestGeneration = this.current?.manifest.payload.generation ?? 0;
279
+ if (this.grantBaseUrl) {
280
+ if (this.grantHold)
281
+ answer.retryAfterSeconds = this.grantHold.retryAfterSeconds;
282
+ else
283
+ answer.uploadGrant = this.issueGrant(String(body.instanceId), this.now());
284
+ }
285
+ return respond(200, JSON.stringify(answer), { "content-type": "application/json" });
286
+ }
287
+ const manifestMatch = /^\/v1\/agents\/([^/]+)\/targets\/([^/]+)\/manifest$/.exec(parsed.pathname);
288
+ if (manifestMatch) {
289
+ if (manifestMatch[1] !== this.scope.agentId)
290
+ return respond(403, JSON.stringify({ error: "x", details: { code: "agent_mismatch" } }));
291
+ if (manifestMatch[2] !== this.scope.target)
292
+ return respond(403, JSON.stringify({ error: "x", details: { code: "target_mismatch" } }));
293
+ if (!this.current)
294
+ return respond(404, JSON.stringify({ error: "Not found" }));
295
+ if (init?.headers?.["if-none-match"] === this.current.etag)
296
+ return respond(304, "", { etag: this.current.etag });
297
+ return respond(200, this.current.bytes, { etag: this.current.etag, "x-agent-generation": String(this.current.manifest.payload.generation), "content-type": "application/json" });
298
+ }
299
+ const payloadMatch = /^\/v1\/agents\/([^/]+)\/payloads\/(sha256:[0-9a-f]{64})$/.exec(parsed.pathname);
300
+ if (payloadMatch) {
301
+ const bytes = this.payloads.get(payloadMatch[2]);
302
+ return bytes ? respond(200, bytes) : respond(404, JSON.stringify({ error: "Not found" }));
303
+ }
304
+ return respond(404, JSON.stringify({ error: "Not found" }));
305
+ };
306
+ }
307
+ }
308
+ /** The fake behind a real HTTP listener, for daemons and CLIs that run as their own process — and `airprompter dev` (S12). */
309
+ export async function serveOverHttp(plane, listen = {}) {
310
+ const { createServer } = await import("node:http");
311
+ const fetchImpl = plane.fetch();
312
+ const server = createServer((request, response) => {
313
+ void (async () => {
314
+ const headers = {};
315
+ for (const [name, value] of Object.entries(request.headers))
316
+ if (typeof value === "string")
317
+ headers[name.toLowerCase()] = value;
318
+ const url = `http://${request.headers.host ?? "127.0.0.1"}${request.url ?? "/"}`;
319
+ const chunks = [];
320
+ for await (const chunk of request)
321
+ chunks.push(Buffer.from(chunk));
322
+ const raw = Buffer.concat(chunks);
323
+ const body = raw.length === 0 ? undefined : headers["content-type"]?.startsWith("multipart/") ? raw : raw.toString("utf8");
324
+ const result = (await fetchImpl(url, { method: request.method ?? "GET", headers, ...(body !== undefined ? { body } : {}) }));
325
+ const answer = Buffer.from(await result.arrayBuffer());
326
+ response.writeHead(result.status, Object.fromEntries(result.headerEntries ?? []));
327
+ response.end(answer);
328
+ })();
329
+ });
330
+ const host = listen.host ?? "127.0.0.1";
331
+ await new Promise((resolve, reject) => {
332
+ server.once("error", reject);
333
+ server.listen(listen.port ?? 0, host, () => {
334
+ server.off("error", reject);
335
+ resolve();
336
+ });
337
+ });
338
+ const address = server.address();
339
+ return { baseUrl: `http://${host.includes(":") ? `[${host}]` : host}:${address.port}`, close: () => new Promise((resolve) => server.close(() => resolve())) };
340
+ }
341
+ //# sourceMappingURL=controlPlane.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controlPlane.js","sourceRoot":"","sources":["../../../src/testing/controlPlane.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAG5F,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,wGAAwG;AACxG,MAAM,CAAC,MAAM,QAAQ,GAAG,gBAAgB,CAAC;AAEzC,MAAM,UAAU,MAAM;IACpB,MAAM,EAAE,UAAU,EAAE,GAAG,mBAAmB,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;IAC1E,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACjD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,CAAC,CAAE,EAAE,CAAC,EAAE,GAAG,CAAC,CAAE,EAAE,CAAC,EAAE,GAAG,CAAC,CAAE,EAAE,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAgK;IAC3L,MAAM,IAAI,GAA+B,EAAE,CAAC;IAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC5C,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;IACjG,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QAC9C,MAAM,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;QACnF,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;IACH,MAAM,MAAM,GAAuB;QACjC,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,UAAU;QACpC,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,CAAC;QAC3B,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,sBAAsB;QAChD,IAAI;QACJ,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE;KAClG,CAAC;IACF,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;AAC1H,CAAC;AAWD,MAAM,OAAO,gBAAgB;IAgBhB;IACA;IAhBF,OAAO,CAAiB;IACxB,UAAU,CAAiB;IAC3B,IAAI,CAAe;IACnB,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IACrC,QAAQ,GAAa,EAAE,CAAC;IACzB,OAAO,GAA+D,IAAI,CAAC;IACnF,UAAU,GAAG,CAAC,CAAC;IACf,QAAQ,GAAG,CAAC,CAAC;IACb,kBAAkB,GAAG,KAAK,CAAC;IAE3B;;;OAGG;IACH,YACW,KAAkE,EAClE,SAAS,kBAAkB,EACpC,OAAkE,EAAE;QAF3D,UAAK,GAAL,KAAK,CAA6D;QAClE,WAAM,GAAN,MAAM,CAAqB;QAGpC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,MAAM,EAAE,CAAC;QACxC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,MAAM,EAAE,CAAC;QAC9C,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IACjH,CAAC;IAED,IAAI,CAAC,IAAc;QACjB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAiB;YACzB,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ;YACxC,UAAU,EAAE,OAAO,IAAI,CAAC,GAAG,EAAE;YAC7B,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,OAAO,IAAI,CAAC,GAAG,IAAI;YAChD,cAAc,EAAE,CAAC;YACjB,WAAW,EAAE,IAAI;YACjB,UAAU,EAAE,KAAK,CAAC,MAAM;YACxB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,iBAAiB;YACtC,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,EAAE;SAChC,CAAC;QACF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;gBAC1C,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBACjD,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;gBAC3C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;gBACvC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,KAAK,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,GAAG,CAAC,EAAE,gBAAgB,EAAE,OAAO,IAAI,CAAC,GAAG,IAAI,KAAK,GAAG,CAAC,EAAE,EAAE,eAAe,EAAE,IAAI,CAAC,SAAS,IAAI,OAAO,IAAI,CAAC,GAAG,QAAQ,KAAK,GAAG,CAAC,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;YAClP,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,qEAAqE;IACrE,uGAAuG;IAC9F,UAAU,GAAmC,EAAE,CAAC;IACzD,wBAAwB,GAAG,GAAG,CAAC;IAC/B,gBAAgB,GAA4C,IAAI,CAAC;IACjE;;;;OAIG;IACH,YAAY,GAAkB,IAAI,CAAC;IACnC,sHAAsH;IACtH,aAAa,GAAkC,IAAI,CAAC;IACpD,kGAAkG;IAClG,yBAAyB,GAAG,IAAI,CAAC;IACjC,UAAU,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC5B,SAAS,GAAyC,IAAI,CAAC;IACvD,qBAAqB,GAAG,GAAG,CAAC;IACnB,MAAM,GAAyF,EAAE,CAAC;IAClG,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IACpC,OAAO,GAAa,EAAE,CAAC;IAChC,oEAAoE;IACpE,eAAe,GAAG,CAAC,CAAC;IACpB,iHAAiH;IACjH,GAAG,GAAiB,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,QAAQ,GAAG,CAAC,CAAC;IAEb,UAAU,CAAC,UAAkB,EAAE,GAAW;QAChD,MAAM,OAAO,GAAG,SAAS,MAAM,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAC9I,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,UAAU,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,UAAU,GAAG,CAAC;QACrH,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;QAChE,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC,sBAAsB,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,EAAE,cAAc,EAAE,sBAAsB,EAAE,EAAE,EAAE,qBAAqB,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACjQ,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC;QAChE,OAAO;YACL,OAAO;YACP,GAAG,EAAE,GAAG,IAAI,CAAC,YAAY,qBAAqB;YAC9C,MAAM,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,6CAA6C,EAAE,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,EAAE,8BAA8B,EAAE,SAAS,EAAE,6CAA6C,EAAE,6CAA6C,EAAE,qBAAqB,EAAE,OAAO,EAAE;YAClW,SAAS;YACT,SAAS;YACT,cAAc,EAAE,OAAO;YACvB,WAAW,EAAE,sBAAsB;SACpC,CAAC;IACJ,CAAC;IAED,2HAA2H;IACnH,YAAY,CAAC,IAAkF,EAAE,GAAW;QAClH,MAAM,WAAW,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QAC1D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,EAAE,IAAI;YAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,kDAAkD,EAAE,CAAC;QAC/G,MAAM,GAAG,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpG,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC;QAC5C,IAAI,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACjC,OAAO,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;YACzD,IAAI,IAAI,KAAK,CAAC,CAAC;gBAAE,MAAM;YACvB,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,wDAAwD;YACzH,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC7D,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACjD,IAAI,IAAI;gBAAE,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC;YACzD,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;QACD,IAAI,IAAI,CAAC,eAAe,GAAG,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,eAAe,IAAI,CAAC,CAAC;YAC1B,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,2CAA2C,EAAE,CAAC;QAC5E,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAmD,CAAC,CAAC,CAAC,IAAI,CAAC;QACrJ,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,wGAAwG,EAAE,CAAC;QACpJ,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,GAAG;YAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,yGAAyG,EAAE,CAAC;QAClL,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACtD,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,qBAAqB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;QAC7D,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC;YAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,mJAAmJ,EAAE,CAAC;QAClO,IAAI,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,sBAAsB;YAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,sGAAsG,EAAE,CAAC;QAClN,IAAI,MAAM,CAAC,GAAG,CAAC,8BAA8B,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,SAAS;YAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,6FAA6F,EAAE,CAAC;QAC5M,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,kHAAkH,EAAE,CAAC;QAC5J,IAAI,IAAI,CAAC,MAAM,GAAG,OAAO;YAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,4CAA4C,EAAE,CAAC;QACtG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACzC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IACnC,CAAC;IAED,OAAO,CAAC,KAAqB,EAAE,UAAgM,EAAE;QAC/N,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnE,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QAC5D,MAAM,OAAO,GAAoB;YAC/B,QAAQ,EAAE,QAAQ;YAClB,GAAG,IAAI,CAAC,KAAK;YACb,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC;YACpC,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAClC,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,IAAI;YAC1C,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,SAAS;YACjD,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,MAAM;YAC1C,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvE,kBAAkB,EAAE,IAAI,CAAC,kBAAkB;YAC3C,KAAK,EAAE,MAAM;YACb,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACjE,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,EAAE;SACrC,CAAC;QACF,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC;QACnD,MAAM,QAAQ,GAAa,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,iBAAiB,EAAE,EAAE,EAAE,CAAC;QAC7K,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;QAC5D,IAAI,CAAC,OAAO,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;QACnB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,OAAO,EAAE,QAAQ,IAAI,IAAI,CAAC;IACxC,CAAC;IAED,gCAAgC;IAChC,KAAK;QACH,MAAM,OAAO,GAAG,CAAC,MAAc,EAAE,OAAwB,EAAE,EAAE,UAAkC,EAAE,EAAE,EAAE,CAAC,CAAC;YACrG,MAAM;YACN,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,IAAI,EAAE;YACvE,iFAAiF;YACjF,aAAa,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;YACtC,WAAW,EAAE,KAAK,IAAI,EAAE;gBACtB,qFAAqF;gBACrF,MAAM,KAAK,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gBAC1E,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,MAAqB,CAAC;YACrD,CAAC;YACD,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;SAC5E,CAAC,CAAC;QACH,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YACzB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACxB,MAAM,IAAI,GAAG,IAAI,EAAE,OAAO,EAAE,aAAa,IAAI,IAAI,EAAE,OAAO,EAAE,aAAa,CAAC;YAC1E,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBACjD,IAAI,CAAC,IAAI,CAAC,OAAO;oBAAE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;gBACvC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;oBACvB,qEAAqE;oBACrE,MAAM,UAAU,GAAG,gBAAgB,IAAI,CAAC,aAAa,CAAC,UAAU,GAAG,CAAC;oBACpE,IAAI,IAAI,EAAE,OAAO,EAAE,CAAC,eAAe,CAAC,KAAK,UAAU;wBAAE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;oBACzE,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,aAAa,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,aAAa,EAAE,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;gBACjO,CAAC;gBACD,MAAM,IAAI,GAAG,SAAS,IAAI,CAAC,QAAQ,GAAG,CAAC;gBACvC,IAAI,IAAI,EAAE,OAAO,EAAE,CAAC,eAAe,CAAC,KAAK,IAAI;oBAAE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;gBACnE,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,aAAa,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,aAAa,EAAE,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YAChO,CAAC;YACD,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC;gBAAE,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAC3F,IAAI,MAAM,CAAC,QAAQ,KAAK,qBAAqB,EAAE,CAAC;gBAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;gBAClD,OAAO,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,cAAc,EAAE,iBAAiB,EAAE,CAAC,CAAC;YAClF,CAAC;YACD,IAAI,IAAI,KAAK,UAAU,IAAI,CAAC,MAAM,EAAE;gBAAE,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;YACrG,oGAAoG;YACpG,MAAM,UAAU,GAAG,kDAAkD,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC5F,IAAI,UAAU,EAAE,CAAC;gBACf,IAAI,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM;oBAAE,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC;gBACrP,IAAI,CAAC,IAAI,CAAC,OAAO;oBAAE,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,yCAAyC,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC;gBACvI,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAC9C,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,oBAAoB,EAAE,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YAC1jB,CAAC;YACD,6GAA6G;YAC7G,MAAM,cAAc,GAAG,sDAAsD,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACpG,IAAI,cAAc,EAAE,CAAC;gBACnB,IAAI,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM;oBAAE,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC,CAAC;gBACjP,IAAI,IAAI,CAAC,gBAAgB;oBAAE,OAAO,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;gBACvJ,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAA4B,CAAC;gBAC7J,KAAK,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC;oBAC9I,IAAI,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC;wBAAE,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,sBAAsB,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;gBAClG,CAAC;gBACD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBACpC,IAAI,CAAC,CAAC,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,0BAA0B,EAAE,YAAY,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,mBAAmB,EAAE,SAAS,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;wBAAE,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,sBAAsB,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;gBAC9Z,CAAC;gBACD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC3B,MAAM,MAAM,GAA4B,EAAE,WAAW,EAAE,EAAE,EAAE,qBAAqB,EAAE,IAAI,CAAC,qBAAqB,EAAE,wBAAwB,EAAE,IAAI,CAAC,wBAAwB,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,wBAAwB,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;gBAC9P,sHAAsH;gBACtH,IAAI,IAAI,CAAC,yBAAyB;oBAAE,MAAM,CAAC,gBAAgB,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC;gBAC7G,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;oBACtB,IAAI,IAAI,CAAC,SAAS;wBAAE,MAAM,CAAC,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC;;wBAC3E,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;gBACjF,CAAC;gBACD,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;YACtF,CAAC;YACD,MAAM,aAAa,GAAG,qDAAqD,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAClG,IAAI,aAAa,EAAE,CAAC;gBAClB,IAAI,aAAa,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO;oBAAE,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,CAAC,CAAC,CAAC;gBACtI,IAAI,aAAa,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM;oBAAE,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC,CAAC;gBACtI,IAAI,CAAC,IAAI,CAAC,OAAO;oBAAE,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;gBAC/E,IAAI,IAAI,EAAE,OAAO,EAAE,CAAC,eAAe,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI;oBAAE,OAAO,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;gBACjH,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;YACnL,CAAC;YACD,MAAM,YAAY,GAAG,0DAA0D,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACtG,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAE,CAAC,CAAC;gBAClD,OAAO,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;YAC5F,CAAC;YACD,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;QAC9D,CAAC,CAAC;IACJ,CAAC;CACF;AAED,8HAA8H;AAC9H,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAuB,EAAE,SAA2C,EAAE;IACxG,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;IAChC,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;QAChD,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,MAAM,OAAO,GAA2B,EAAE,CAAC;YAC3C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;gBAAE,IAAI,OAAO,KAAK,KAAK,QAAQ;oBAAE,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC;YAChI,MAAM,GAAG,GAAG,UAAU,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,WAAW,GAAG,OAAO,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;YACjF,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO;gBAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YACnE,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAClC,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC3H,MAAM,MAAM,GAAG,CAAC,MAAM,SAAS,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAiF,CAAC;YAC7M,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;YACvD,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,CAAC;YAClF,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC,CAAC,EAAE,CAAC;IACP,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,WAAW,CAAC;IACxC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE;YACzC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC5B,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAsB,CAAC;IACrD,OAAO,EAAE,OAAO,EAAE,UAAU,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC;AAChK,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The testing kit (S2, AIR-1970): what our own vectors run over and what a
3
+ * customer's CI can import to prove the same things about their host —
4
+ * a filesystem that fills, fails and loses files (`MemoryFs`), a clock
5
+ * that advances and skews (`FakeClock`), and the fake registry the SDK
6
+ * tests use (`FakeControlPlane`: signed manifests, refusals, lease and a
7
+ * 15-minute upload grant that expires). Nothing here is ever loaded by the
8
+ * runtime.
9
+ */
10
+ import type { ClockPort } from "../protocol/ports.js";
11
+ export { MemoryFs } from "./memoryFs.js";
12
+ export { FakeControlPlane, PROTOCOL, newKey, rootDocument, serveOverHttp, type SlotSpec } from "./controlPlane.js";
13
+ /** A clock a test moves by hand: `advance(ms)`, or `skew(ms)` to model a host whose clock is off. */
14
+ export declare class FakeClock implements ClockPort {
15
+ private currentMs;
16
+ private offsetMs;
17
+ constructor(currentMs?: number);
18
+ nowMs(): number;
19
+ advance(ms: number): void;
20
+ set(ms: number): void;
21
+ /** Two hosts with the same `currentMs` and different skews disagree by the difference — the ramp walk's vector. */
22
+ skew(ms: number): void;
23
+ /** The `now` callback the facade and uploader accept. */
24
+ readonly now: () => number;
25
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The testing kit (S2, AIR-1970): what our own vectors run over and what a
3
+ * customer's CI can import to prove the same things about their host —
4
+ * a filesystem that fills, fails and loses files (`MemoryFs`), a clock
5
+ * that advances and skews (`FakeClock`), and the fake registry the SDK
6
+ * tests use (`FakeControlPlane`: signed manifests, refusals, lease and a
7
+ * 15-minute upload grant that expires). Nothing here is ever loaded by the
8
+ * runtime.
9
+ */
10
+ export { MemoryFs } from "./memoryFs.js";
11
+ export { FakeControlPlane, PROTOCOL, newKey, rootDocument, serveOverHttp } from "./controlPlane.js";
12
+ /** A clock a test moves by hand: `advance(ms)`, or `skew(ms)` to model a host whose clock is off. */
13
+ export class FakeClock {
14
+ currentMs;
15
+ offsetMs = 0;
16
+ constructor(currentMs = Date.UTC(2026, 8, 13, 12, 0, 0)) {
17
+ this.currentMs = currentMs;
18
+ }
19
+ nowMs() {
20
+ return this.currentMs + this.offsetMs;
21
+ }
22
+ advance(ms) {
23
+ this.currentMs += ms;
24
+ }
25
+ set(ms) {
26
+ this.currentMs = ms;
27
+ }
28
+ /** Two hosts with the same `currentMs` and different skews disagree by the difference — the ramp walk's vector. */
29
+ skew(ms) {
30
+ this.offsetMs = ms;
31
+ }
32
+ /** The `now` callback the facade and uploader accept. */
33
+ now = () => this.nowMs();
34
+ }
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAiB,MAAM,mBAAmB,CAAC;AAEnH,qGAAqG;AACrG,MAAM,OAAO,SAAS;IAEA;IADZ,QAAQ,GAAG,CAAC,CAAC;IACrB,YAAoB,YAAoB,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QAAnD,cAAS,GAAT,SAAS,CAA0C;IAAG,CAAC;IAC3E,KAAK;QACH,OAAO,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC;IACxC,CAAC;IACD,OAAO,CAAC,EAAU;QAChB,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC;IACvB,CAAC;IACD,GAAG,CAAC,EAAU;QACZ,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;IACtB,CAAC;IACD,mHAAmH;IACnH,IAAI,CAAC,EAAU;QACb,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;IACrB,CAAC;IACD,yDAAyD;IAChD,GAAG,GAAG,GAAW,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;CAC3C"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * `MemoryFs` (S2, AIR-1970): a filesystem that fills, fails and loses files
3
+ * on demand — the fake our own rule requires ("a forgiving fake hides every
4
+ * budget bug"). Same contract as the Node port, so the spool, the store and
5
+ * the uploader run over it unchanged.
6
+ *
7
+ * - `capacityBytes`: writes past it throw `ENOSPC`, like a full disk.
8
+ * - `failNext(op, code, times?)`: the next call(s) to `op` throw that code.
9
+ * - `vanish(path)`: the file disappears out from under a listing (the
10
+ * sibling-process race the budget sweep must survive).
11
+ * - `bytesUsed()` and `tree()`: what a customer's CI asserts against.
12
+ *
13
+ * Handles are numbers; a closed or unknown handle throws `EBADF`.
14
+ */
15
+ import type { FsFailure, FsOpenFlags, FsPort } from "../protocol/ports.js";
16
+ type Op = keyof FsPort;
17
+ export declare class MemoryFs implements FsPort {
18
+ capacityBytes: number;
19
+ private readonly files;
20
+ private readonly dirs;
21
+ private readonly handles;
22
+ private nextFd;
23
+ private readonly pending;
24
+ /** Every operation in order — a test asserts what the spool did, not only what it left. */
25
+ readonly journal: Array<{
26
+ op: Op;
27
+ path: string;
28
+ }>;
29
+ clockMs: number;
30
+ constructor(capacityBytes?: number);
31
+ /** The next `times` calls to `op` fail with `code`. */
32
+ failNext(op: Op, code: FsFailure["code"], times?: number): void;
33
+ /** The file is gone without a rename or unlink — a sibling process took it. */
34
+ vanish(path: string): void;
35
+ bytesUsed(): number;
36
+ /** Every file path, sorted, with its size. */
37
+ tree(): Array<{
38
+ path: string;
39
+ bytes: number;
40
+ }>;
41
+ private gate;
42
+ private ensureCapacity;
43
+ mkdirp(path: string, _mode?: number): void;
44
+ exists(path: string): boolean;
45
+ list(dir: string): string[];
46
+ listRecursive(dir: string): string[];
47
+ stat(path: string): {
48
+ size: number;
49
+ mtimeMs: number;
50
+ };
51
+ open(path: string, flags: FsOpenFlags, mode?: number): number;
52
+ write(fd: number, bytes: Uint8Array): void;
53
+ fsync(fd: number): void;
54
+ close(fd: number): void;
55
+ rename(from: string, to: string): void;
56
+ unlink(path: string): void;
57
+ readFile(path: string): Uint8Array;
58
+ writeFile(path: string, bytes: Uint8Array, mode?: number): void;
59
+ rm(path: string, options?: {
60
+ recursive?: boolean;
61
+ force?: boolean;
62
+ }): void;
63
+ }
64
+ export {};