@apilift/cli 1.4.33 → 1.4.35
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 +76 -3
- package/dist/apilift-collector.js +159580 -158475
- package/dist/apilift-native-host.js +39 -4
- package/dist/apilift-register-native-host.js +2 -2
- package/dist/apilift.js +589 -329
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.codex-plugin/plugin.json +1 -1
- package/package.json +3 -3
|
@@ -19866,6 +19866,55 @@ var extensionPageMessageSchema = external_exports.discriminatedUnion("operation"
|
|
|
19866
19866
|
}).strict()
|
|
19867
19867
|
]);
|
|
19868
19868
|
|
|
19869
|
+
// src/capture-server/windows-owner.ts
|
|
19870
|
+
import { Worker } from "node:worker_threads";
|
|
19871
|
+
var script = `$ErrorActionPreference='Stop'; while ($null -ne ($line=[Console]::ReadLine())) { $parts=$line.Split('|'); try { $p=[System.Diagnostics.Process]::GetProcessById([int]$parts[1]); $stamp=([DateTimeOffset]$p.StartTime.ToUniversalTime()).ToUnixTimeMilliseconds(); $p.Dispose() } catch { $stamp=0 }; [Console]::WriteLine($parts[0]+'|'+$stamp); [Console]::Out.Flush() }`;
|
|
19872
|
+
var workerSource = `
|
|
19873
|
+
const {workerData}=require('node:worker_threads');
|
|
19874
|
+
const {spawn}=require('node:child_process');
|
|
19875
|
+
const state=new Int32Array(workerData.memory,0,4), value=new Float64Array(workerData.memory,16,1);
|
|
19876
|
+
const child=spawn('powershell.exe',['-NoLogo','-NoProfile','-NonInteractive','-Command',workerData.script],{windowsHide:true,stdio:['pipe','pipe','ignore']});
|
|
19877
|
+
const fail=()=>{Atomics.store(state,0,3);Atomics.notify(state,0);};
|
|
19878
|
+
child.on('error',fail);child.on('exit',fail);
|
|
19879
|
+
let text='';
|
|
19880
|
+
child.stdout.on('data',chunk=>{
|
|
19881
|
+
text+=chunk.toString();
|
|
19882
|
+
let end;
|
|
19883
|
+
while((end=text.indexOf('\\n'))>=0){
|
|
19884
|
+
const [id,stamp]=text.slice(0,end).trim().split('|').map(Number);text=text.slice(end+1);
|
|
19885
|
+
if(id!==Atomics.load(state,1)||Atomics.load(state,0)!==1)continue;
|
|
19886
|
+
value[0]=Number.isFinite(stamp)?stamp:0;Atomics.store(state,0,2);Atomics.notify(state,0);
|
|
19887
|
+
}
|
|
19888
|
+
if(text.length>1024){child.kill();fail();}
|
|
19889
|
+
});
|
|
19890
|
+
const {parentPort}=require('node:worker_threads');
|
|
19891
|
+
parentPort.on('message',({id,pid})=>{try{child.stdin.write(id+'|'+pid+'\\n');}catch{fail();}});
|
|
19892
|
+
`;
|
|
19893
|
+
var worker;
|
|
19894
|
+
var memory;
|
|
19895
|
+
var sequence = 0;
|
|
19896
|
+
function windowsProcessStart(pid) {
|
|
19897
|
+
if (!worker) {
|
|
19898
|
+
memory = new SharedArrayBuffer(24);
|
|
19899
|
+
worker = new Worker(workerSource, { eval: true, workerData: { memory, script } });
|
|
19900
|
+
worker.on("error", () => {
|
|
19901
|
+
});
|
|
19902
|
+
worker.unref();
|
|
19903
|
+
}
|
|
19904
|
+
const state = new Int32Array(memory, 0, 4), value = new Float64Array(memory, 16, 1);
|
|
19905
|
+
const id2 = ++sequence;
|
|
19906
|
+
Atomics.store(state, 1, id2);
|
|
19907
|
+
Atomics.store(state, 0, 1);
|
|
19908
|
+
worker.postMessage({ id: id2, pid });
|
|
19909
|
+
Atomics.wait(state, 0, 1, 4e3);
|
|
19910
|
+
if (Atomics.load(state, 0) === 2 && value[0] > 0) return value[0];
|
|
19911
|
+
if (Atomics.load(state, 0) !== 2) {
|
|
19912
|
+
void worker.terminate();
|
|
19913
|
+
worker = void 0;
|
|
19914
|
+
}
|
|
19915
|
+
return null;
|
|
19916
|
+
}
|
|
19917
|
+
|
|
19869
19918
|
// src/capture-server/store.ts
|
|
19870
19919
|
import { randomUUID, createHash } from "node:crypto";
|
|
19871
19920
|
import { mkdirSync, chmodSync, readFileSync as readFileSync2 } from "node:fs";
|
|
@@ -19934,6 +19983,10 @@ function inspectOwner(pid, expectedStartedAt, probe) {
|
|
|
19934
19983
|
return false;
|
|
19935
19984
|
}
|
|
19936
19985
|
}
|
|
19986
|
+
if (windows && !probe.inspect) {
|
|
19987
|
+
const observed = windowsProcessStart(pid);
|
|
19988
|
+
return observed !== null && Math.abs(observed - expectedStartedAt) <= 5e3;
|
|
19989
|
+
}
|
|
19937
19990
|
for (let attempt = 0; attempt < (windows ? 2 : 1); attempt++) {
|
|
19938
19991
|
if (!alive(pid)) return false;
|
|
19939
19992
|
const remaining = windows ? 8e3 - (now() - started) : 2e3;
|
|
@@ -19974,6 +20027,7 @@ var CaptureStore = class {
|
|
|
19974
20027
|
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`);
|
|
19975
20028
|
const columns = this.db.prepare("PRAGMA table_info(scopes)").all();
|
|
19976
20029
|
if (!columns.some((column) => column.name === "owner_session")) this.db.exec("ALTER TABLE scopes ADD COLUMN owner_session TEXT");
|
|
20030
|
+
if (!this.db.prepare("PRAGMA table_info(bridge_status)").all().some((column) => column.name === "sequence")) this.db.exec("ALTER TABLE bridge_status ADD COLUMN sequence INTEGER NOT NULL DEFAULT 0");
|
|
19977
20031
|
if (!this.db.prepare("PRAGMA table_info(bridge_status)").all().some((column) => column.name === "capabilities_json")) this.db.exec("ALTER TABLE bridge_status ADD COLUMN capabilities_json TEXT NOT NULL DEFAULT '[]'");
|
|
19978
20032
|
if (!columns.some((column) => column.name === "start_url")) this.db.exec("ALTER TABLE scopes ADD COLUMN start_url TEXT");
|
|
19979
20033
|
if (!columns.some((column) => column.name === "binding_mode")) this.db.exec("ALTER TABLE scopes ADD COLUMN binding_mode TEXT");
|
|
@@ -20136,8 +20190,11 @@ var CaptureStore = class {
|
|
|
20136
20190
|
this.prune();
|
|
20137
20191
|
return Boolean(this.db.prepare("SELECT 1 FROM scopes WHERE retained_at IS NULL LIMIT 1").get() || this.db.prepare("SELECT 1 FROM jobs j JOIN scopes s ON s.id=j.scope_id WHERE s.retained_at IS NULL LIMIT 1").get() || this.db.prepare("SELECT 1 FROM scopes s WHERE s.retained_at>? AND (EXISTS (SELECT 1 FROM capture_candidates c WHERE c.scope_id=s.id AND json_extract(c.entry_json,'$.pending')=1) OR EXISTS (SELECT 1 FROM capture_body_refs r LEFT JOIN blobs b ON b.scope_id=r.scope_id AND b.digest=r.digest WHERE r.scope_id=s.id AND COALESCE(b.complete,0)=0)) LIMIT 1").get(this.clock() - 2e4));
|
|
20138
20192
|
}
|
|
20193
|
+
wakeBridge() {
|
|
20194
|
+
this.emit();
|
|
20195
|
+
}
|
|
20139
20196
|
bridgeReady(version2, capabilities = []) {
|
|
20140
|
-
this.db.prepare("INSERT INTO bridge_status(id,version,updated_at,capabilities_json) VALUES(1,?,?,?) ON CONFLICT(id) DO UPDATE SET version=excluded.version,updated_at=excluded.updated_at,capabilities_json=excluded.capabilities_json").run(version2, this.clock(), json3(capabilities));
|
|
20197
|
+
this.db.prepare("INSERT INTO bridge_status(id,version,updated_at,capabilities_json) VALUES(1,?,?,?) ON CONFLICT(id) DO UPDATE SET version=excluded.version,updated_at=excluded.updated_at,capabilities_json=excluded.capabilities_json,sequence=bridge_status.sequence+1").run(version2, this.clock(), json3(capabilities));
|
|
20141
20198
|
}
|
|
20142
20199
|
bridgeCapabilities() {
|
|
20143
20200
|
const row = this.db.prepare("SELECT capabilities_json FROM bridge_status WHERE id=1 AND updated_at>?").get(this.clock() - 6e4);
|
|
@@ -20312,6 +20369,15 @@ async function startCaptureServer(options) {
|
|
|
20312
20369
|
const store = new CaptureStore(root, options.clock, options.alive);
|
|
20313
20370
|
const token = randomBytes(32).toString("base64url");
|
|
20314
20371
|
let lastActiveAt = Date.now();
|
|
20372
|
+
async function untilChanged(read, ready, waitMs) {
|
|
20373
|
+
const deadline = Date.now() + Math.max(0, Math.min(25e3, Number.isFinite(waitMs) ? waitMs : 0));
|
|
20374
|
+
for (; ; ) {
|
|
20375
|
+
const revision = (await store.changes(-1, 0)).revision;
|
|
20376
|
+
const value = read();
|
|
20377
|
+
if (ready(value) || Date.now() >= deadline) return value;
|
|
20378
|
+
await store.changes(revision, deadline - Date.now());
|
|
20379
|
+
}
|
|
20380
|
+
}
|
|
20315
20381
|
const server = createServer(async (request2, response) => {
|
|
20316
20382
|
try {
|
|
20317
20383
|
if (!equalToken(request2.headers.authorization?.replace(/^Bearer /, "") ?? "", token)) {
|
|
@@ -20333,6 +20399,12 @@ async function startCaptureServer(options) {
|
|
|
20333
20399
|
send(response, 200, { scopes: store.scopes(), extensionCapabilities: store.bridgeCapabilities() });
|
|
20334
20400
|
return;
|
|
20335
20401
|
}
|
|
20402
|
+
if (path === "/bridge/demand" && request2.method === "POST") {
|
|
20403
|
+
await readBody(request2);
|
|
20404
|
+
store.wakeBridge();
|
|
20405
|
+
send(response, 200, { ok: true });
|
|
20406
|
+
return;
|
|
20407
|
+
}
|
|
20336
20408
|
if (path === "/bridge/ready" && request2.method === "POST") {
|
|
20337
20409
|
const body2 = object2(await readBody(request2));
|
|
20338
20410
|
if (typeof body2.version !== "string" || body2.capabilities !== void 0 && (!Array.isArray(body2.capabilities) || body2.capabilities.some((value) => typeof value !== "string"))) throw new CaptureError("invalid-bridge-version", "Native host version and supported capabilities are required");
|
|
@@ -20341,7 +20413,8 @@ async function startCaptureServer(options) {
|
|
|
20341
20413
|
return;
|
|
20342
20414
|
}
|
|
20343
20415
|
if (path === "/scope/candidates" && request2.method === "GET") {
|
|
20344
|
-
|
|
20416
|
+
const candidates = await untilChanged(() => store.candidates(String(url3.searchParams.get("id") ?? "")), (value) => value.length > 0, Number(url3.searchParams.get("waitMs") ?? 0));
|
|
20417
|
+
send(response, 200, { candidates });
|
|
20345
20418
|
return;
|
|
20346
20419
|
}
|
|
20347
20420
|
if (path === "/scope/entry" && request2.method === "GET") {
|
|
@@ -20363,7 +20436,7 @@ async function startCaptureServer(options) {
|
|
|
20363
20436
|
return;
|
|
20364
20437
|
}
|
|
20365
20438
|
if (path === "/job/result" && request2.method === "GET") {
|
|
20366
|
-
const result = store.jobResult(String(url3.searchParams.get("id") ?? ""));
|
|
20439
|
+
const result = await untilChanged(() => store.jobResult(String(url3.searchParams.get("id") ?? "")), (value) => !value || ["delivered", "uncertain"].includes(value.status), Number(url3.searchParams.get("waitMs") ?? 0));
|
|
20367
20440
|
send(response, result ? 200 : 404, result ?? { error: { code: "job-missing", message: "Job not found" } });
|
|
20368
20441
|
return;
|
|
20369
20442
|
}
|