@apilift/cli 1.5.15 → 1.6.0-dev.1793187013.3a1b810900605353fc6fcbb6e4cc2020deb7ca40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apilift-capture-server.js +53 -12
- package/dist/apilift-collector.js +139 -54
- package/dist/apilift-native-host.js +9540 -9394
- package/dist/apilift.js +1995 -828
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.codex-plugin/plugin.json +1 -1
- package/dist/plugin/skills/discover/SKILL.md +9 -3
- package/dist/plugin/skills/discover/references/operation-contract.md +19 -3
- package/package.json +3 -3
|
@@ -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.
|
|
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
|
|
20301
|
-
const
|
|
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
|
|
20304
|
-
if (request2.payload.
|
|
20305
|
-
|
|
20306
|
-
|
|
20307
|
-
|
|
20308
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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:
|
|
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 =
|
|
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 =
|
|
29013
|
-
this.facts.tailHash =
|
|
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
|
|
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 ===
|
|
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
|
|
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,
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
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";
|