@apilift/cli 1.5.15 → 1.6.1-dev.1793190001.48d047bc92bb2c8a02f67d83be4995c1e0cee871

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.
@@ -19917,7 +19917,7 @@ function windowsProcessStart(pid) {
19917
19917
 
19918
19918
  // src/capture-server/store.ts
19919
19919
  import { randomUUID, createHash } from "node:crypto";
19920
- import { mkdirSync, chmodSync, readFileSync as readFileSync2 } from "node:fs";
19920
+ import { constants, mkdirSync, chmodSync, readFileSync as readFileSync2, openSync, closeSync, readSync, writeSync, ftruncateSync, fsyncSync, statSync, rmSync } from "node:fs";
19921
19921
  import { join as join2 } from "node:path";
19922
19922
  import { execFileSync } from "node:child_process";
19923
19923
  import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
@@ -20003,6 +20003,7 @@ function inspectOwner(pid, expectedStartedAt, probe) {
20003
20003
  }
20004
20004
  var CaptureStore = class {
20005
20005
  constructor(root, clock = Date.now, alive = ownerProcessAlive) {
20006
+ this.root = root;
20006
20007
  this.clock = clock;
20007
20008
  this.alive = alive;
20008
20009
  mkdirSync(root, { recursive: true, mode: 448 });
@@ -20024,6 +20025,7 @@ var CaptureStore = class {
20024
20025
  CREATE TABLE IF NOT EXISTS bridge_status (id INTEGER PRIMARY KEY CHECK(id=1), version TEXT NOT NULL, updated_at REAL NOT NULL);
20025
20026
  CREATE TABLE IF NOT EXISTS blobs (scope_id TEXT NOT NULL REFERENCES scopes(id) ON DELETE CASCADE, digest TEXT NOT NULL, recording TEXT, job_id TEXT, bytes INTEGER NOT NULL, data BLOB NOT NULL, complete INTEGER NOT NULL, PRIMARY KEY(scope_id,digest));
20026
20027
  `);
20028
+ if (!this.db.prepare("PRAGMA table_info(blobs)").all().some((column) => column.name === "stored_bytes")) this.db.exec("ALTER TABLE blobs ADD COLUMN stored_bytes INTEGER");
20027
20029
  if (!hadCandidates) this.db.exec(`INSERT INTO capture_candidates (scope_id,seq,capture_id,entry_json) SELECT e.scope_id,g.first_seq,g.capture_id,e.entry_json FROM capture_entries e JOIN (SELECT scope_id,COALESCE(json_extract(entry_json,'$.captureId'),CAST(seq AS TEXT)) AS capture_id,MIN(seq) AS first_seq,MAX(seq) AS last_seq FROM capture_entries GROUP BY scope_id,capture_id) g ON e.scope_id=g.scope_id AND e.seq=g.last_seq`);
20028
20030
  const columns = this.db.prepare("PRAGMA table_info(scopes)").all();
20029
20031
  if (!columns.some((column) => column.name === "owner_session")) this.db.exec("ALTER TABLE scopes ADD COLUMN owner_session TEXT");
@@ -20074,7 +20076,7 @@ var CaptureStore = class {
20074
20076
  if (capture?.kind === "capture" && this.db.prepare("SELECT 1 FROM capture_candidates WHERE scope_id=? LIMIT 1").get(id2)) {
20075
20077
  this.db.prepare("UPDATE scopes SET window_json=NULL WHERE id=?").run(id2);
20076
20078
  this.retainScope(id2);
20077
- } else this.db.prepare("DELETE FROM scopes WHERE id=?").run(id2);
20079
+ } else this.closeScope(id2);
20078
20080
  this.emit();
20079
20081
  }
20080
20082
  row(id2, freshOwner = false) {
@@ -20136,8 +20138,15 @@ var CaptureStore = class {
20136
20138
  }
20137
20139
  closeScope(id2) {
20138
20140
  this.db.prepare("DELETE FROM scopes WHERE id=?").run(id2);
20141
+ rmSync(join2(this.root, "bodies", createHash("sha256").update(id2).digest("hex")), { recursive: true, force: true });
20139
20142
  this.emit();
20140
20143
  }
20144
+ bodyPath(scopeId, digest) {
20145
+ if (!/^[0-9a-f]{64}$/.test(digest)) throw new CaptureError("blob-invalid", "Body digest is invalid");
20146
+ const directory = join2(this.root, "bodies", createHash("sha256").update(scopeId).digest("hex"));
20147
+ mkdirSync(directory, { recursive: true, mode: 448 });
20148
+ return join2(directory, digest);
20149
+ }
20141
20150
  scopes() {
20142
20151
  this.prune();
20143
20152
  const rows = this.db.prepare("SELECT id,kind,page_origin,tab_selector,start_url,binding_mode,lease_id,expires_at,activated_at,ack_through,window_json,owner_session FROM scopes WHERE retained_at IS NULL ORDER BY activated_at").all();
@@ -20150,8 +20159,8 @@ var CaptureStore = class {
20150
20159
  blob(scopeId, digest) {
20151
20160
  const scope2 = this.row(scopeId);
20152
20161
  if (!/^[0-9a-f]{64}$/.test(digest)) throw new CaptureError("blob-invalid", "Selected body digest is invalid");
20153
- const held = this.db.prepare("SELECT data,complete FROM blobs WHERE scope_id=? AND digest=?").get(scopeId, digest);
20154
- if (held?.complete) return Buffer.from(held.data);
20162
+ const held = this.db.prepare("SELECT data,complete,stored_bytes FROM blobs WHERE scope_id=? AND digest=?").get(scopeId, digest);
20163
+ if (held?.complete) return held.stored_bytes === null ? Buffer.from(held.data) : readFileSync2(this.bodyPath(scopeId, digest));
20155
20164
  if (scope2.retained_at !== null && this.clock() > Number(scope2.retained_at) + 2e4) throw new CaptureError("blob-unavailable", "Recorded body delivery did not complete within its collection deadline");
20156
20165
  return null;
20157
20166
  }
@@ -20297,15 +20306,47 @@ var CaptureStore = class {
20297
20306
  if (Boolean(request2.payload.recording) === Boolean(request2.payload.jobId)) throw new CaptureError("blob-owner", "Body chunk must identify exactly one recording or job");
20298
20307
  const chunk = Buffer.from(request2.payload.dataBase64, "base64");
20299
20308
  if (createHash("sha256").update(chunk).digest("hex") !== request2.payload.chunkSha256) throw new CaptureError("blob-integrity", "Body chunk checksum does not match");
20300
- const held = this.db.prepare("SELECT data,bytes,complete FROM blobs WHERE scope_id=? AND digest=?").get(scopeId, request2.payload.sha256);
20301
- const previous = held ? Buffer.from(held.data) : Buffer.alloc(0);
20309
+ const held = this.db.prepare("SELECT bytes,complete,stored_bytes,length(data) AS legacy_bytes FROM blobs WHERE scope_id=? AND digest=?").get(scopeId, request2.payload.sha256);
20310
+ const offset = held ? held.stored_bytes ?? held.legacy_bytes : 0;
20311
+ if (held && held.bytes !== request2.payload.bytes) throw new CaptureError("blob-integrity", "Body length changed during transfer");
20302
20312
  if (held?.complete) return { offset: held.bytes, complete: true };
20303
- if (request2.payload.offset !== previous.length) throw new CaptureError("blob-offset", "Body chunk offset does not match persisted bytes");
20304
- if (request2.payload.bytes > 48 * 1024 * 1024 || previous.length + chunk.length > request2.payload.bytes) throw new CaptureError("blob-limit", "Body exceeds the capture limit");
20305
- const data = Buffer.concat([previous, chunk]), complete = data.length === request2.payload.bytes;
20306
- if (complete && createHash("sha256").update(data).digest("hex") !== request2.payload.sha256) throw new CaptureError("blob-integrity", "Body checksum does not match");
20307
- this.db.prepare("INSERT INTO blobs (scope_id,digest,recording,job_id,bytes,data,complete) VALUES (?,?,?,?,?,?,?) ON CONFLICT(scope_id,digest) DO UPDATE SET data=excluded.data,complete=excluded.complete").run(scopeId, request2.payload.sha256, request2.payload.recording ?? null, request2.payload.jobId ?? null, request2.payload.bytes, data, complete ? 1 : 0);
20308
- return { offset: data.length, complete };
20313
+ if (request2.payload.offset < offset) return { offset, complete: false };
20314
+ if (request2.payload.offset !== offset) throw new CaptureError("blob-offset", "Body chunk offset does not match persisted bytes");
20315
+ if (request2.payload.bytes > 48 * 1024 * 1024 || offset + chunk.length > request2.payload.bytes) throw new CaptureError("blob-limit", "Body exceeds the capture limit");
20316
+ const path = this.bodyPath(scopeId, request2.payload.sha256);
20317
+ const fd = openSync(path, constants.O_RDWR | constants.O_CREAT, 384);
20318
+ const next = offset + chunk.length, complete = next === request2.payload.bytes;
20319
+ const writeAt = (data, position) => {
20320
+ for (let written = 0; written < data.length; ) {
20321
+ const count = writeSync(fd, data, written, data.length - written, position + written);
20322
+ if (!count) throw new CaptureError("blob-write", "Body storage made no progress");
20323
+ written += count;
20324
+ }
20325
+ };
20326
+ try {
20327
+ if (held && held.stored_bytes === null) {
20328
+ const legacy = this.db.prepare("SELECT data FROM blobs WHERE scope_id=? AND digest=?").get(scopeId, request2.payload.sha256);
20329
+ ftruncateSync(fd, 0);
20330
+ writeAt(legacy.data, 0);
20331
+ } else if (statSync(path).size < offset) throw new CaptureError("blob-integrity", "Persisted body bytes are missing");
20332
+ ftruncateSync(fd, offset);
20333
+ writeAt(chunk, offset);
20334
+ fsyncSync(fd);
20335
+ if (complete) {
20336
+ const hash3 = createHash("sha256"), buffer = Buffer.allocUnsafe(64 * 1024);
20337
+ for (let position = 0; position < next; ) {
20338
+ const bytes = readSync(fd, buffer, 0, Math.min(buffer.length, next - position), position);
20339
+ if (!bytes) throw new CaptureError("blob-integrity", "Persisted body is incomplete");
20340
+ hash3.update(buffer.subarray(0, bytes));
20341
+ position += bytes;
20342
+ }
20343
+ if (hash3.digest("hex") !== request2.payload.sha256) throw new CaptureError("blob-integrity", "Body checksum does not match");
20344
+ }
20345
+ } finally {
20346
+ closeSync(fd);
20347
+ }
20348
+ this.db.prepare("INSERT INTO blobs (scope_id,digest,recording,job_id,bytes,data,complete,stored_bytes) VALUES (?,?,?,?,?,X'',?,?) ON CONFLICT(scope_id,digest) DO UPDATE SET data=X'',complete=excluded.complete,stored_bytes=excluded.stored_bytes").run(scopeId, request2.payload.sha256, request2.payload.recording ?? null, request2.payload.jobId ?? null, request2.payload.bytes, complete ? 1 : 0, next);
20349
+ return { offset: next, complete };
20309
20350
  }
20310
20351
  case "jobs.poll": {
20311
20352
  const job = this.db.prepare("SELECT id,envelope_json FROM jobs WHERE scope_id=? AND status='queued' ORDER BY created_at LIMIT 1").get(scopeId);
@@ -28273,8 +28273,8 @@ var init_contracts = __esm({
28273
28273
  setupStatus = external_exports.strictObject({ linkCode: external_exports.strictObject({ id: uuid5, agent: knownAgent.nullable(), status: external_exports.enum(["pending", "claimed", "expired"]), expiresAt: iso, installationId: uuid5.nullable() }).nullable(), installation: external_exports.strictObject({ id: uuid5, label: str, readiness, steps: data, agents: external_exports.array(knownAgent) }).nullable() });
28274
28274
  actionSummary = external_exports.strictObject({ name: str, title: str, summary: external_exports.string(), effect: external_exports.enum(["read", "create", "update", "delete"]).optional(), availability: external_exports.enum(["executable", "unavailable"]), policy: external_exports.enum(["approve", "deny", "disable"]), health: data, documentation: data.optional() });
28275
28275
  actionDocumentation = external_exports.strictObject({ name: str, title: str, description: external_exports.string(), effect: external_exports.enum(["read", "create", "update", "delete"]).optional(), availability: external_exports.enum(["executable", "unavailable"]), inputs: jsonSchema, output: jsonSchema.optional(), proof: external_exports.enum(["readback", "absence"]).optional(), unavailable: external_exports.strictObject({ reason: str, evidence: str }).optional(), transport: external_exports.strictObject({ name: str, mode: external_exports.enum(["direct", "browser"]), instructions: str, credentials: external_exports.array(external_exports.strictObject({ name: str, header: str })) }).optional(), version: external_exports.number().int().positive(), executableHash: hash2.optional(), policy: external_exports.enum(["approve", "deny", "disable"]), health: data });
28276
- bundle = external_exports.strictObject({ receipt: external_exports.strictObject({ id: uuid5, dispatchBefore: iso }).optional(), decision: external_exports.literal("allow"), app: external_exports.strictObject({ id: uuid5, slug: str, visibility: external_exports.enum(["public", "private"]) }), action: external_exports.strictObject({ name: str, effect: external_exports.enum(["read", "create", "update", "delete"]) }), closure: external_exports.strictObject({ hash: hash2, status: external_exports.enum(["pending", "validated"]), workspaceStatus: external_exports.literal("successful").nullable(), versions: external_exports.array(external_exports.strictObject({ kind: resourceKindSchema, name: str, version: external_exports.number().int().positive(), executableHash: hash2, definition })) }), selection: external_exports.strictObject({ channel: external_exports.enum(["latest", "validated"]), fallback: external_exports.boolean(), replaced: external_exports.strictObject({ closureHash: hash2, reason: str, until: iso.optional() }).optional() }), policy: external_exports.literal("approve") });
28277
- resourceRead = external_exports.strictObject({ ...kindName.shape, definition, version: external_exports.number().int().positive(), executableHash: hash2.optional(), verification: data.optional(), dependents: external_exports.array(kindName).optional() });
28276
+ bundle = external_exports.strictObject({ receipt: external_exports.strictObject({ id: uuid5, dispatchBefore: iso }).optional(), decision: external_exports.literal("allow"), app: external_exports.strictObject({ id: uuid5, slug: str, visibility: external_exports.enum(["public", "private"]) }), action: external_exports.strictObject({ name: str, effect: external_exports.enum(["read", "create", "update", "delete"]) }), closure: external_exports.strictObject({ hash: hash2, status: external_exports.enum(["pending", "validated"]), workspaceStatus: external_exports.literal("successful").nullable(), versions: external_exports.array(external_exports.strictObject({ kind: resourceKindSchema, name: str, version: external_exports.number().int().positive(), executableHash: hash2, versionId: uuid5.optional(), definition: definition.optional() })) }), selection: external_exports.strictObject({ channel: external_exports.enum(["latest", "validated"]), fallback: external_exports.boolean(), replaced: external_exports.strictObject({ closureHash: hash2, reason: str, until: iso.optional() }).optional() }), policy: external_exports.literal("approve") });
28277
+ resourceRead = external_exports.strictObject({ ...kindName.shape, definition, version: external_exports.number().int().positive(), executableHash: hash2.optional(), verification: data.optional(), dependents: external_exports.array(kindName).optional(), nextDependentsCursor: external_exports.string().nullable().optional() });
28278
28278
  resourceRequirements = external_exports.strictObject({ legacyCredentialContract: str.optional(), credentialContract: str.optional(), destinations: external_exports.array(str).optional(), name: str, title: str, mode: external_exports.enum(["direct", "browser"]), instructions: str, credentials: external_exports.array(external_exports.strictObject({ name: str, header: str, prefix: external_exports.string().optional() })) });
28279
28279
  safetyReview = external_exports.union([external_exports.strictObject({ access: external_exports.literal("locked") }), external_exports.strictObject({ access: external_exports.literal("included"), state: external_exports.enum(["missing", "checking", "ready", "unavailable"]), route: str, findings: data.optional(), checkedAt: iso.optional(), stale: external_exports.boolean().optional(), message: external_exports.string().optional() })]);
28280
28280
  billing = external_exports.strictObject({ workspaceId: uuid5, paddleCustomerId: str.optional(), entitlement, trial: external_exports.strictObject({ status: external_exports.enum(["active", "ended", "converted"]), startedAt: iso, endsAt: iso, completedAt: iso.optional() }).optional(), subscription: external_exports.strictObject({ id: str, status: str, plan: str, interval: str, currency: str, currentPeriod: external_exports.strictObject({ startsAt: iso, endsAt: iso }), nextBilledAt: iso.optional(), graceEndsAt: iso.optional(), scheduledChange: data.optional() }).optional(), usage: data.optional(), checkout: external_exports.strictObject({ status: external_exports.enum(["pending", "unknown"]), transactionId: str.optional() }).optional(), checkoutAvailable: external_exports.boolean(), paddle: external_exports.strictObject({ environment: external_exports.enum(["sandbox", "production"]), clientToken: str }) });
@@ -28298,12 +28298,12 @@ var init_contracts = __esm({
28298
28298
  releaseSession: apiContract("DELETE", "/api/v1/sessions/{id}", "installation", external_exports.strictObject({ id: str }), external_exports.strictObject({ appId: uuid5.optional(), purpose: external_exports.literal("execution").optional() }).refine((v2) => Boolean(v2.appId) === Boolean(v2.purpose), "appId and purpose must be supplied together"), noBody, data),
28299
28299
  listApps: apiContract("GET", "/api/v1/apps", "installation-or-cookie", noParams, external_exports.strictObject({ q: external_exports.string().max(200).optional(), domain: str.optional(), category: categorySchema.optional(), visibility: external_exports.enum(["public", "private", "all"]).optional(), ids: str.optional(), ...pageQuery }), noBody, page(appItem)),
28300
28300
  createApp: apiContract("POST", "/api/v1/apps", "installation", noParams, noQuery, external_exports.strictObject({ requestId: uuid5, slug: str, visibility: external_exports.enum(["public", "private"]).optional(), definition: appDefinitionSchema }), data, ["slug-taken"]),
28301
- getApp: apiContract("GET", "/api/v1/apps/{app}", "installation-or-cookie", path, noQuery, noBody, appItem.extend({ about: external_exports.string().optional(), safety: data.optional(), entities: external_exports.array(data), transports: external_exports.array(data) })),
28301
+ getApp: apiContract("GET", "/api/v1/apps/{app}", "installation-or-cookie", path, external_exports.strictObject({ entity: str.optional(), entityCursor: str.optional(), transportCursor: str.optional(), limit: pageQuery.limit }), noBody, appItem.extend({ about: external_exports.string().optional(), safety: data.optional(), entities: external_exports.array(data), transports: external_exports.array(data), nextEntityCursor: str.nullable().optional(), nextTransportCursor: str.nullable().optional() })),
28302
28302
  listActions: apiContract("GET", "/api/v1/apps/{app}/actions", "installation-or-cookie", path, external_exports.strictObject({ entity: str.optional(), q: external_exports.string().max(200).optional(), names: str.optional(), include: external_exports.literal("documentation").optional(), ...pageQuery }), noBody, page(actionSummary)),
28303
28303
  getAction: apiContract("GET", "/api/v1/apps/{app}/actions/{action}", "installation-or-cookie", actionPath, external_exports.strictObject({ include: external_exports.literal("schemas").optional() }), noBody, actionDocumentation),
28304
- getBundle: apiContract("GET", "/api/v1/apps/{app}/actions/{action}/bundle", "installation", actionPath, noQuery, noBody, bundle, ["session-limit", "action-withheld", "action-disabled", "action-denied", "action-unavailable"]),
28305
- listResources: apiContract("GET", "/api/v1/apps/{app}/resources", "installation", path, external_exports.strictObject({ kind: resourceKindSchema.optional(), names: str.optional() }), noBody, page(external_exports.strictObject({ ...kindName.shape, version: external_exports.number().int().positive(), executableHash: hash2.optional(), updatedAt: iso }))),
28306
- getResource: apiContract("GET", "/api/v1/apps/{app}/resources/{kind}/{name}", "installation-or-cookie", resourcePath, external_exports.strictObject({ view: external_exports.literal("requirements").optional() }), noBody, external_exports.union([resourceRead, resourceRequirements])),
28304
+ getBundle: apiContract("GET", "/api/v1/apps/{app}/actions/{action}/bundle", "installation", actionPath, external_exports.strictObject({ knownVersions: external_exports.string().max(120).optional() }), noBody, bundle, ["session-limit", "action-withheld", "action-disabled", "action-denied", "action-unavailable"]),
28305
+ listResources: apiContract("GET", "/api/v1/apps/{app}/resources", "installation", path, external_exports.strictObject({ kind: resourceKindSchema.optional(), names: str.optional(), ...pageQuery }), noBody, page(external_exports.strictObject({ ...kindName.shape, version: external_exports.number().int().positive(), executableHash: hash2.optional(), updatedAt: iso }))),
28306
+ getResource: apiContract("GET", "/api/v1/apps/{app}/resources/{kind}/{name}", "installation-or-cookie", resourcePath, external_exports.strictObject({ view: external_exports.literal("requirements").optional(), dependentsCursor: external_exports.string().optional() }), noBody, external_exports.union([resourceRead, resourceRequirements])),
28307
28307
  publishResource: apiContract("PUT", "/api/v1/apps/{app}/resources/{kind}/{name}", "installation", resourcePath, noQuery, external_exports.union([external_exports.strictObject({ requestId: uuid5, expectedVersion: external_exports.number().int().positive().nullable(), definition, verification: data.optional() }), external_exports.strictObject({ requestId: uuid5, expectedVersion: external_exports.number().int().positive(), remove: external_exports.literal(true) })]), external_exports.strictObject({ resource: external_exports.strictObject({ ...kindName.shape, version: external_exports.number().int().positive() }), executableHash: hash2.optional(), closureHash: hash2.optional(), distribution: external_exports.strictObject({ state: external_exports.enum(["available", "testing", "queued", "paused"]), reason: str.optional(), until: iso.optional() }).optional() }), ["definition-invalid", "documentation-incomplete", "reference-unresolved", "reference-cycle", "verification-incomplete", "verification-stale", "version-conflict"]),
28308
28308
  listChanges: apiContract("GET", "/api/v1/apps/{app}/changes", "installation-or-cookie", path, external_exports.strictObject({ resource: str.optional(), ...pageQuery }), noBody, page(external_exports.strictObject({ change: external_exports.number().int().positive(), kind: resourceKindSchema, name: str, version: external_exports.number().int().positive(), event: external_exports.enum(["published", "removed"]), distribution: external_exports.strictObject({ state: external_exports.enum(["available", "testing", "queued", "paused"]), reason: str.optional(), until: iso.optional() }).optional(), publishedAt: iso }))),
28309
28309
  safetyReview: apiContract("GET", "/api/v1/apps/{app}/safety-review", "cookie", path, noQuery, noBody, safetyReview),
@@ -28382,6 +28382,34 @@ var init_version = __esm({
28382
28382
  }
28383
28383
  });
28384
28384
 
28385
+ // src/execution-reports.ts
28386
+ async function flushExecutionReports(api2, state, budgetMs = 1e4) {
28387
+ const pending = state.pendingExecutionReports();
28388
+ if (!pending.length) return;
28389
+ const controller = new AbortController();
28390
+ let timer;
28391
+ try {
28392
+ const acknowledgment = await Promise.race([
28393
+ api2.call("postExecutions", { body: { reports: pending.map((item) => item.report) }, signal: controller.signal }),
28394
+ new Promise((_, reject) => {
28395
+ timer = setTimeout(() => {
28396
+ controller.abort();
28397
+ reject(new Error("Report delivery deferred"));
28398
+ }, budgetMs);
28399
+ })
28400
+ ]);
28401
+ state.acknowledgeExecutionReports(acknowledgment.results.map((item) => item.id));
28402
+ } catch {
28403
+ } finally {
28404
+ clearTimeout(timer);
28405
+ }
28406
+ }
28407
+ var init_execution_reports = __esm({
28408
+ "src/execution-reports.ts"() {
28409
+ "use strict";
28410
+ }
28411
+ });
28412
+
28385
28413
  // src/api.ts
28386
28414
  function retryDelay(response, attempt) {
28387
28415
  const value = response.headers.get("retry-after");
@@ -28400,7 +28428,7 @@ function createCliApi(baseUrl, fetcher = fetch, token, sessionId) {
28400
28428
  const transport = async (input2, init) => {
28401
28429
  const method = init?.method ?? "GET";
28402
28430
  const headers = new Headers(init?.headers);
28403
- headers.set("Apilift-Capabilities", "distributed-rollouts-v1,discovery-proof-v2,dynamic-bindings-v1,collection-proof-v3,web-responses-v1");
28431
+ headers.set("Apilift-Capabilities", "distributed-rollouts-v1,discovery-proof-v2,dynamic-bindings-v1,collection-proof-v3,web-responses-v1,bounded-catalog-v1");
28404
28432
  headers.set("Apilift-Client", `cli/${cliVersion}`);
28405
28433
  if (token) headers.set("Authorization", `Bearer ${token}`);
28406
28434
  if (sessionId) headers.set("Apilift-Session", sessionId);
@@ -28429,7 +28457,7 @@ function createCliApi(baseUrl, fetcher = fetch, token, sessionId) {
28429
28457
  }
28430
28458
  }
28431
28459
  };
28432
- return createApiClient(baseUrl, transport);
28460
+ return Object.assign(createApiClient(baseUrl, transport), { cacheNamespace: baseUrl });
28433
28461
  }
28434
28462
  var retryStatuses, ApiDeliveryUncertainError, ApiReadUnavailableError;
28435
28463
  var init_api3 = __esm({
@@ -28453,34 +28481,6 @@ var init_api3 = __esm({
28453
28481
  }
28454
28482
  });
28455
28483
 
28456
- // src/execution-reports.ts
28457
- async function flushExecutionReports(api2, state, budgetMs = 1e4) {
28458
- const pending = state.pendingExecutionReports();
28459
- if (!pending.length) return;
28460
- const controller = new AbortController();
28461
- let timer;
28462
- try {
28463
- const acknowledgment = await Promise.race([
28464
- api2.call("postExecutions", { body: { reports: pending.map((item) => item.report) }, signal: controller.signal }),
28465
- new Promise((_, reject) => {
28466
- timer = setTimeout(() => {
28467
- controller.abort();
28468
- reject(new Error("Report delivery deferred"));
28469
- }, budgetMs);
28470
- })
28471
- ]);
28472
- state.acknowledgeExecutionReports(acknowledgment.results.map((item) => item.id));
28473
- } catch {
28474
- } finally {
28475
- clearTimeout(timer);
28476
- }
28477
- }
28478
- var init_execution_reports = __esm({
28479
- "src/execution-reports.ts"() {
28480
- "use strict";
28481
- }
28482
- });
28483
-
28484
28484
  // src/usage/baselines.ts
28485
28485
  import { randomUUID as randomUUID4 } from "node:crypto";
28486
28486
  import { chmodSync as chmodSync4, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "node:fs";
@@ -28589,7 +28589,7 @@ var init_receipts = __esm({
28589
28589
  });
28590
28590
 
28591
28591
  // src/usage/transcript.ts
28592
- import { createHash as createHash2 } from "node:crypto";
28592
+ import { createHash as createHash4 } from "node:crypto";
28593
28593
  import { closeSync, openSync, readSync, readdirSync, statSync as statSync2 } from "node:fs";
28594
28594
  import { homedir as homedir2, platform as platform2 } from "node:os";
28595
28595
  import { basename, join as join5 } from "node:path";
@@ -28676,7 +28676,7 @@ function commandIdentity(command) {
28676
28676
  let argv = words.slice(index + 1);
28677
28677
  if (argv[0] === "--session") argv = argv.slice(2);
28678
28678
  if (!argv.length) return null;
28679
- return { key: argv.slice(0, 3).join(" "), argumentHash: createHash2("sha256").update(JSON.stringify(argv)).digest("hex") };
28679
+ return { key: argv.slice(0, 3).join(" "), argumentHash: createHash4("sha256").update(JSON.stringify(argv)).digest("hex") };
28680
28680
  }
28681
28681
  function commandKey(command) {
28682
28682
  return commandIdentity(command)?.key ?? null;
@@ -28776,7 +28776,7 @@ var init_transcript = __esm({
28776
28776
  provider = null;
28777
28777
  thread = null;
28778
28778
  lastAt = null;
28779
- hash = createHash2("sha256");
28779
+ hash = createHash4("sha256");
28780
28780
  head = Buffer.alloc(0);
28781
28781
  tail = Buffer.alloc(0);
28782
28782
  bytes(bytes) {
@@ -29009,8 +29009,8 @@ var init_transcript = __esm({
29009
29009
  }
29010
29010
  this.facts.records.sort((a, b2) => (a.startedAt ?? Infinity) - (b2.startedAt ?? Infinity));
29011
29011
  this.facts.sourceHash = this.hash.digest("hex");
29012
- this.facts.headHash = createHash2("sha256").update(this.head).digest("hex");
29013
- this.facts.tailHash = createHash2("sha256").update(this.tail).digest("hex");
29012
+ this.facts.headHash = createHash4("sha256").update(this.head).digest("hex");
29013
+ this.facts.tailHash = createHash4("sha256").update(this.tail).digest("hex");
29014
29014
  return this.facts;
29015
29015
  }
29016
29016
  };
@@ -163682,7 +163682,7 @@ var init_pricing = __esm({
163682
163682
  });
163683
163683
 
163684
163684
  // src/usage/measurement.ts
163685
- import { createHash as createHash3 } from "node:crypto";
163685
+ import { createHash as createHash5 } from "node:crypto";
163686
163686
  import { readFileSync as readFileSync3 } from "node:fs";
163687
163687
  function largestRemainder(total, weights) {
163688
163688
  const positive = weights.map((v2) => Math.max(0, Math.floor(v2))), sum = positive.reduce((a, b2) => a + b2, 0);
@@ -163748,7 +163748,7 @@ function matchesCommand(row, tool) {
163748
163748
  return false;
163749
163749
  }
163750
163750
  const expected = argv[0] === "--session" ? argv.slice(2) : argv;
163751
- return tool.category === "apilift" && tool.argumentHash === createHash3("sha256").update(JSON.stringify(expected)).digest("hex");
163751
+ return tool.category === "apilift" && tool.argumentHash === createHash5("sha256").update(JSON.stringify(expected)).digest("hex");
163752
163752
  }
163753
163753
  function invocationMatches(row, facts) {
163754
163754
  if (row.agent !== facts.runtime || row.session_id && !row.session_id.includes(":label:") && facts.sessionId && row.session_id !== `${facts.runtime}:${facts.sessionId}`) return [];
@@ -163968,7 +163968,7 @@ __export(collector_exports, {
163968
163968
  reconcilePending: () => reconcilePending
163969
163969
  });
163970
163970
  import { rmSync as rmSync2, statSync as statSync3 } from "node:fs";
163971
- import { createHash as createHash4 } from "node:crypto";
163971
+ import { createHash as createHash6 } from "node:crypto";
163972
163972
  function hasPendingUsage(ledger) {
163973
163973
  if (ledger.db.prepare("SELECT count(*) AS n FROM invocation WHERE output_path IS NOT NULL OR (execution_id IS NOT NULL AND status='pending')").get().n) return true;
163974
163974
  if (!ledger.db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='baseline_candidate'").get()) return false;
@@ -164062,9 +164062,9 @@ async function deliverPending(ledger, state, site, fetcher = fetch, now = Date.n
164062
164062
  if (!installation) return;
164063
164063
  const cutoff = new Date(now - 7 * 864e5).toISOString();
164064
164064
  ledger.db.prepare("UPDATE baseline_candidate SET status='rejected' WHERE status='pending' AND stopped_at IS NOT NULL AND stopped_at<?").run(cutoff);
164065
- const rows = ledger.pendingMeasurement(now);
164066
164065
  const baselines = new BaselineStore(ledger);
164067
- const baselineRows = baselines.rows().filter((row) => row.measurement_json && row.status === "pending" && row.next_attempt_at <= now).slice(0, Math.max(0, 100 - rows.length));
164066
+ const baselineRows = baselines.rows().filter((row) => row.measurement_json && row.status === "pending" && row.next_attempt_at <= now).slice(0, 100);
164067
+ const rows = ledger.pendingMeasurement(now, 100 - baselineRows.length);
164068
164068
  if (!rows.length && !baselineRows.length) return;
164069
164069
  const executions = rows.flatMap((row) => {
164070
164070
  try {
@@ -164158,6 +164158,10 @@ var init_collector = __esm({
164158
164158
  }
164159
164159
  let entry = rootScans.get(path2);
164160
164160
  if (!entry || entry.inode !== stat.ino || stat.size < entry.reader.offset || entry.facts && (entry.size !== stat.size || entry.mtime !== stat.mtimeMs)) {
164161
+ if (this.remaining <= 0) {
164162
+ this.deferred = true;
164163
+ return null;
164164
+ }
164161
164165
  entry = { reader: new TranscriptRead(path2, tokenCount), size: stat.size, mtime: stat.mtimeMs, inode: stat.ino, facts: null };
164162
164166
  rootScans.set(path2, entry);
164163
164167
  if (rootScans.size > 64) rootScans.delete(rootScans.keys().next().value);
@@ -164183,7 +164187,7 @@ var init_collector = __esm({
164183
164187
  entry.mtime = stat.mtimeMs;
164184
164188
  }
164185
164189
  const facts = entry.facts;
164186
- const pathHash = createHash4("sha256").update(path2).digest("hex");
164190
+ const pathHash = createHash6("sha256").update(path2).digest("hex");
164187
164191
  const previous = this.ledger.db.prepare("SELECT source_hash,head_hash,bytes,gap FROM source_observation WHERE path_hash=?").get(pathHash);
164188
164192
  if (previous) {
164189
164193
  const replaced = facts.bytes < previous.bytes || facts.bytes === previous.bytes && facts.sourceHash !== previous.source_hash || previous.head_hash !== null && previous.bytes >= 4096 && facts.headHash !== previous.head_hash;
@@ -164249,7 +164253,7 @@ init_ledger();
164249
164253
  import { fileURLToPath as fileURLToPath2 } from "node:url";
164250
164254
 
164251
164255
  // src/analytics.ts
164252
- import { randomUUID as randomUUID3 } from "node:crypto";
164256
+ import { createHash as createHash3, randomUUID as randomUUID3 } from "node:crypto";
164253
164257
 
164254
164258
  // ../../packages/core/dist/analytics/index.js
164255
164259
  var eventProperties = {
@@ -164347,7 +164351,87 @@ function scrubEventProperties(event, input2) {
164347
164351
 
164348
164352
  // src/analytics.ts
164349
164353
  init_api2();
164350
- init_api3();
164354
+
164355
+ // src/identity-cache.ts
164356
+ init_api2();
164357
+ init_version();
164358
+ import { createHash as createHash2 } from "node:crypto";
164359
+ var freshMs = 3e5;
164360
+ var retainedMs = 864e5;
164361
+ var IdentityCache = class {
164362
+ constructor(state, site, fetcher = fetch) {
164363
+ this.state = state;
164364
+ this.site = site;
164365
+ this.fetcher = fetcher;
164366
+ state.db.exec("CREATE TABLE IF NOT EXISTS registry_identity_cache (key TEXT PRIMARY KEY, value TEXT, fetched_at INTEGER NOT NULL DEFAULT 0, lease_until INTEGER NOT NULL DEFAULT 0, retry_at INTEGER NOT NULL DEFAULT 0, failures INTEGER NOT NULL DEFAULT 0, readiness_at INTEGER NOT NULL DEFAULT 0)");
164367
+ }
164368
+ key() {
164369
+ const installation = this.state.installation();
164370
+ return installation ? createHash2("sha256").update(JSON.stringify([this.site, installation])).digest("hex") : null;
164371
+ }
164372
+ fetchedAt() {
164373
+ const key = this.key();
164374
+ if (!key) return 0;
164375
+ return Number(this.state.db.prepare("SELECT fetched_at FROM registry_identity_cache WHERE key=?").get(key)?.fetched_at ?? 0);
164376
+ }
164377
+ peek(now = Date.now()) {
164378
+ const key = this.key();
164379
+ if (!key) return null;
164380
+ const row = this.state.db.prepare("SELECT value,fetched_at FROM registry_identity_cache WHERE key=?").get(key);
164381
+ if (!row?.value || now - row.fetched_at >= retainedMs) return null;
164382
+ try {
164383
+ return JSON.parse(row.value);
164384
+ } catch {
164385
+ return null;
164386
+ }
164387
+ }
164388
+ async read(now = Date.now(), active = () => true) {
164389
+ const key = this.key(), installation = this.state.installation();
164390
+ if (!key || !installation) return null;
164391
+ this.state.db.prepare("INSERT OR IGNORE INTO registry_identity_cache(key) VALUES (?)").run(key);
164392
+ const owned = this.state.db.prepare("UPDATE registry_identity_cache SET lease_until=? WHERE key=? AND lease_until<=? AND retry_at<=? AND fetched_at<=? RETURNING key").get(now + 1e4, key, now, now, now - freshMs);
164393
+ if (!owned) return this.peek(now);
164394
+ let timer;
164395
+ try {
164396
+ const signal = AbortSignal.timeout(2e3);
164397
+ const api2 = createApiClient(this.site, (input2, init) => {
164398
+ const headers = new Headers(init?.headers);
164399
+ headers.set("Authorization", `Bearer ${installation.token}`);
164400
+ headers.set("Apilift-Client", `cli/${cliVersion}`);
164401
+ return this.fetcher(input2, { ...init, headers, signal });
164402
+ });
164403
+ const me3 = await Promise.race([api2.call("me"), new Promise((_, reject) => {
164404
+ timer = setTimeout(() => reject(new Error("identity-timeout")), 2500);
164405
+ })]);
164406
+ if (!active() || this.key() !== key) return null;
164407
+ if (me3.installation.id !== installation.id || me3.account.id !== installation.accountId || me3.workspace.id !== installation.workspaceId) throw new Error("identity-mismatch");
164408
+ this.state.db.prepare("UPDATE registry_identity_cache SET value=?,fetched_at=?,lease_until=0,retry_at=0,failures=0 WHERE key=?").run(JSON.stringify(me3), now, key);
164409
+ this.state.db.prepare("DELETE FROM registry_identity_cache WHERE key<>?").run(key);
164410
+ return me3;
164411
+ } catch (error62) {
164412
+ if (!active() || this.key() !== key) return null;
164413
+ const code = error62.response?.error?.code;
164414
+ const revoked = error62.status === 401 || code === "installation-revoked" || code === "unauthenticated" || error62 instanceof Error && error62.message === "identity-mismatch";
164415
+ this.state.db.prepare("UPDATE registry_identity_cache SET value=CASE WHEN ? THEN NULL ELSE value END,fetched_at=CASE WHEN ? THEN 0 ELSE fetched_at END,lease_until=0,failures=min(failures+1,10),retry_at=?+min(300000,1000*(1 << min(failures+1,8))) WHERE key=?").run(Number(revoked), Number(revoked), now, key);
164416
+ if (revoked) throw error62;
164417
+ return this.peek(now);
164418
+ } finally {
164419
+ clearTimeout(timer);
164420
+ if (active()) this.state.db.prepare("UPDATE registry_identity_cache SET lease_until=0 WHERE key=? AND lease_until=?").run(key, now + 1e4);
164421
+ }
164422
+ }
164423
+ reserveReadiness(now = Date.now()) {
164424
+ const key = this.key();
164425
+ if (!key) return false;
164426
+ return Boolean(this.state.db.prepare("UPDATE registry_identity_cache SET readiness_at=? WHERE key=? AND readiness_at<=? RETURNING key").get(now, key, now - freshMs));
164427
+ }
164428
+ retryReadiness() {
164429
+ const key = this.key();
164430
+ if (key) this.state.db.prepare("UPDATE registry_identity_cache SET readiness_at=? WHERE key=?").run(Date.now(), key);
164431
+ }
164432
+ };
164433
+
164434
+ // src/analytics.ts
164351
164435
  var EVENT = /* @__PURE__ */ new Set(["cli_command_finished", "setup_stage_changed", "onboarding_failed", "discovery_started", "discovery_finished", "client_exception"]);
164352
164436
  var EU_HOST = "https://eu.i.posthog.com";
164353
164437
  var CliAnalytics = class {
@@ -164376,14 +164460,14 @@ var CliAnalytics = class {
164376
164460
  try {
164377
164461
  const identity = JSON.parse(row.json);
164378
164462
  const installation = this.state.installation();
164379
- return installation && identity.expiresAt > Date.now() && identity.accountId === installation.accountId && identity.workspaceId === installation.workspaceId && identity.installationId === installation.id ? identity : null;
164463
+ return installation && identity.installationKey === createHash3("sha256").update(JSON.stringify(installation)).digest("hex") && identity.expiresAt > Date.now() && identity.accountId === installation.accountId && identity.workspaceId === installation.workspaceId && identity.installationId === installation.id ? identity : null;
164380
164464
  } catch {
164381
164465
  return null;
164382
164466
  }
164383
164467
  }
164384
164468
  updateIdentity(me3, now = Date.now()) {
164385
164469
  const previous = this.identity();
164386
- const next = { accountId: me3.account.id, workspaceId: me3.workspace.id, installationId: me3.installation.id, ...me3.analytics, enabled: me3.analytics.enabled && me3.analytics.environment !== "disabled" && me3.analytics.host === EU_HOST && /^phc_[A-Za-z0-9]+$/u.test(me3.analytics.projectKey), expiresAt: now + 24 * 60 * 6e4 };
164470
+ const next = { installationKey: createHash3("sha256").update(JSON.stringify(this.state.installation())).digest("hex"), accountId: me3.account.id, workspaceId: me3.workspace.id, installationId: me3.installation.id, ...me3.analytics, enabled: me3.analytics.enabled && me3.analytics.environment !== "disabled" && me3.analytics.host === EU_HOST && /^phc_[A-Za-z0-9]+$/u.test(me3.analytics.projectKey), expiresAt: now + 24 * 60 * 6e4 };
164387
164471
  if (!next.enabled || previous && previous.accountId !== next.accountId) this.state.db.exec("DELETE FROM cli_analytics_queue");
164388
164472
  this.state.db.prepare("INSERT INTO cli_analytics_identity (id,json,refreshed_at) VALUES (1,?,?) ON CONFLICT(id) DO UPDATE SET json=excluded.json,refreshed_at=excluded.refreshed_at").run(JSON.stringify(next), now);
164389
164473
  this.state.db.prepare("DELETE FROM cli_analytics_queue WHERE account_id<>? OR occurred_at<?").run(next.accountId, new Date(now - 7 * 864e5).toISOString());
@@ -164391,10 +164475,11 @@ var CliAnalytics = class {
164391
164475
  async refresh(site, now = Date.now()) {
164392
164476
  const installation = this.state.installation();
164393
164477
  if (!installation) return "unavailable";
164394
- const last = this.state.db.prepare("SELECT refreshed_at FROM cli_analytics_identity WHERE id=1").get();
164395
- if (last && now - last.refreshed_at < 5 * 6e4 && this.identity()) return "ok";
164396
164478
  try {
164397
- this.updateIdentity(await createCliApi(site, this.fetcher, installation.token).call("me"), now);
164479
+ const cache = new IdentityCache(this.state, site, this.fetcher);
164480
+ const me3 = await cache.read(now);
164481
+ if (!me3) return "unavailable";
164482
+ this.updateIdentity(me3, cache.fetchedAt());
164398
164483
  return "ok";
164399
164484
  } catch (error62) {
164400
164485
  if (error62 instanceof ApiClientError && error62.status === 401 && error62.response?.error?.code === "unauthenticated") return "unauthorized";