@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.js CHANGED
@@ -4529,7 +4529,7 @@ function isRef(value) {
4529
4529
  function cloneIssues(issues) {
4530
4530
  return issues.map((iss) => iss.path ? { ...iss, path: iss.path.slice() } : { ...iss });
4531
4531
  }
4532
- function isRecursive(inst, stack, resolve6) {
4532
+ function isRecursive(inst, stack, resolve10) {
4533
4533
  const cached2 = recursive.get(inst);
4534
4534
  if (cached2 !== void 0)
4535
4535
  return cached2 ? PROVEN : NONE;
@@ -4539,7 +4539,7 @@ function isRecursive(inst, stack, resolve6) {
4539
4539
  let result3 = NONE;
4540
4540
  const check2 = (child) => {
4541
4541
  if (result3 !== PROVEN && child?._zod) {
4542
- const answer = isRecursive(child, stack, resolve6);
4542
+ const answer = isRecursive(child, stack, resolve10);
4543
4543
  if (answer > result3)
4544
4544
  result3 = answer;
4545
4545
  }
@@ -4550,7 +4550,7 @@ function isRecursive(inst, stack, resolve6) {
4550
4550
  const desc = Object.getOwnPropertyDescriptor(sh, key);
4551
4551
  if (spread && !desc.enumerable)
4552
4552
  continue;
4553
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve6) : NONE;
4553
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve10) : NONE;
4554
4554
  if (child > answer)
4555
4555
  answer = child;
4556
4556
  }
@@ -4614,7 +4614,7 @@ function isRecursive(inst, stack, resolve6) {
4614
4614
  break;
4615
4615
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
4616
4616
  case "lazy": {
4617
- const inner = def._cachedInner ?? (resolve6 ? inst._zod.innerType : void 0);
4617
+ const inner = def._cachedInner ?? (resolve10 ? inst._zod.innerType : void 0);
4618
4618
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
4619
4619
  break;
4620
4620
  }
@@ -20861,11 +20861,11 @@ var require_codegen = __commonJS({
20861
20861
  const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`;
20862
20862
  return `${varKind} ${this.name}${rhs};` + _n;
20863
20863
  }
20864
- optimizeNames(names2, constants) {
20864
+ optimizeNames(names2, constants2) {
20865
20865
  if (!names2[this.name.str])
20866
20866
  return;
20867
20867
  if (this.rhs)
20868
- this.rhs = optimizeExpr(this.rhs, names2, constants);
20868
+ this.rhs = optimizeExpr(this.rhs, names2, constants2);
20869
20869
  return this;
20870
20870
  }
20871
20871
  get names() {
@@ -20882,10 +20882,10 @@ var require_codegen = __commonJS({
20882
20882
  render({ _n }) {
20883
20883
  return `${this.lhs} = ${this.rhs};` + _n;
20884
20884
  }
20885
- optimizeNames(names2, constants) {
20885
+ optimizeNames(names2, constants2) {
20886
20886
  if (this.lhs instanceof code_1.Name && !names2[this.lhs.str] && !this.sideEffects)
20887
20887
  return;
20888
- this.rhs = optimizeExpr(this.rhs, names2, constants);
20888
+ this.rhs = optimizeExpr(this.rhs, names2, constants2);
20889
20889
  return this;
20890
20890
  }
20891
20891
  get names() {
@@ -20946,8 +20946,8 @@ var require_codegen = __commonJS({
20946
20946
  optimizeNodes() {
20947
20947
  return `${this.code}` ? this : void 0;
20948
20948
  }
20949
- optimizeNames(names2, constants) {
20950
- this.code = optimizeExpr(this.code, names2, constants);
20949
+ optimizeNames(names2, constants2) {
20950
+ this.code = optimizeExpr(this.code, names2, constants2);
20951
20951
  return this;
20952
20952
  }
20953
20953
  get names() {
@@ -20976,12 +20976,12 @@ var require_codegen = __commonJS({
20976
20976
  }
20977
20977
  return nodes.length > 0 ? this : void 0;
20978
20978
  }
20979
- optimizeNames(names2, constants) {
20979
+ optimizeNames(names2, constants2) {
20980
20980
  const { nodes } = this;
20981
20981
  let i = nodes.length;
20982
20982
  while (i--) {
20983
20983
  const n = nodes[i];
20984
- if (n.optimizeNames(names2, constants))
20984
+ if (n.optimizeNames(names2, constants2))
20985
20985
  continue;
20986
20986
  subtractNames(names2, n.names);
20987
20987
  nodes.splice(i, 1);
@@ -21034,12 +21034,12 @@ var require_codegen = __commonJS({
21034
21034
  return void 0;
21035
21035
  return this;
21036
21036
  }
21037
- optimizeNames(names2, constants) {
21037
+ optimizeNames(names2, constants2) {
21038
21038
  var _a3;
21039
- this.else = (_a3 = this.else) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names2, constants);
21040
- if (!(super.optimizeNames(names2, constants) || this.else))
21039
+ this.else = (_a3 = this.else) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names2, constants2);
21040
+ if (!(super.optimizeNames(names2, constants2) || this.else))
21041
21041
  return;
21042
- this.condition = optimizeExpr(this.condition, names2, constants);
21042
+ this.condition = optimizeExpr(this.condition, names2, constants2);
21043
21043
  return this;
21044
21044
  }
21045
21045
  get names() {
@@ -21062,10 +21062,10 @@ var require_codegen = __commonJS({
21062
21062
  render(opts) {
21063
21063
  return `for(${this.iteration})` + super.render(opts);
21064
21064
  }
21065
- optimizeNames(names2, constants) {
21066
- if (!super.optimizeNames(names2, constants))
21065
+ optimizeNames(names2, constants2) {
21066
+ if (!super.optimizeNames(names2, constants2))
21067
21067
  return;
21068
- this.iteration = optimizeExpr(this.iteration, names2, constants);
21068
+ this.iteration = optimizeExpr(this.iteration, names2, constants2);
21069
21069
  return this;
21070
21070
  }
21071
21071
  get names() {
@@ -21101,10 +21101,10 @@ var require_codegen = __commonJS({
21101
21101
  render(opts) {
21102
21102
  return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
21103
21103
  }
21104
- optimizeNames(names2, constants) {
21105
- if (!super.optimizeNames(names2, constants))
21104
+ optimizeNames(names2, constants2) {
21105
+ if (!super.optimizeNames(names2, constants2))
21106
21106
  return;
21107
- this.iterable = optimizeExpr(this.iterable, names2, constants);
21107
+ this.iterable = optimizeExpr(this.iterable, names2, constants2);
21108
21108
  return this;
21109
21109
  }
21110
21110
  get names() {
@@ -21146,11 +21146,11 @@ var require_codegen = __commonJS({
21146
21146
  (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNodes();
21147
21147
  return this;
21148
21148
  }
21149
- optimizeNames(names2, constants) {
21149
+ optimizeNames(names2, constants2) {
21150
21150
  var _a3, _b;
21151
- super.optimizeNames(names2, constants);
21152
- (_a3 = this.catch) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names2, constants);
21153
- (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names2, constants);
21151
+ super.optimizeNames(names2, constants2);
21152
+ (_a3 = this.catch) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names2, constants2);
21153
+ (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names2, constants2);
21154
21154
  return this;
21155
21155
  }
21156
21156
  get names() {
@@ -21451,7 +21451,7 @@ var require_codegen = __commonJS({
21451
21451
  function addExprNames(names2, from) {
21452
21452
  return from instanceof code_1._CodeOrName ? addNames(names2, from.names) : names2;
21453
21453
  }
21454
- function optimizeExpr(expr, names2, constants) {
21454
+ function optimizeExpr(expr, names2, constants2) {
21455
21455
  if (expr instanceof code_1.Name)
21456
21456
  return replaceName(expr);
21457
21457
  if (!canOptimize(expr))
@@ -21466,14 +21466,14 @@ var require_codegen = __commonJS({
21466
21466
  return items;
21467
21467
  }, []));
21468
21468
  function replaceName(n) {
21469
- const c = constants[n.str];
21469
+ const c = constants2[n.str];
21470
21470
  if (c === void 0 || names2[n.str] !== 1)
21471
21471
  return n;
21472
21472
  delete names2[n.str];
21473
21473
  return c;
21474
21474
  }
21475
21475
  function canOptimize(e) {
21476
- return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names2[c.str] === 1 && constants[c.str] !== void 0);
21476
+ return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names2[c.str] === 1 && constants2[c.str] !== void 0);
21477
21477
  }
21478
21478
  }
21479
21479
  function subtractNames(names2, from) {
@@ -23435,7 +23435,7 @@ var require_compile = __commonJS({
23435
23435
  const schOrFunc = root.refs[ref];
23436
23436
  if (schOrFunc)
23437
23437
  return schOrFunc;
23438
- let _sch = resolve6.call(this, root, ref);
23438
+ let _sch = resolve10.call(this, root, ref);
23439
23439
  if (_sch === void 0) {
23440
23440
  const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
23441
23441
  const { schemaId } = this.opts;
@@ -23462,7 +23462,7 @@ var require_compile = __commonJS({
23462
23462
  function sameSchemaEnv(s1, s2) {
23463
23463
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
23464
23464
  }
23465
- function resolve6(root, ref) {
23465
+ function resolve10(root, ref) {
23466
23466
  let sch;
23467
23467
  while (typeof (sch = this.refs[ref]) == "string")
23468
23468
  ref = sch;
@@ -24093,7 +24093,7 @@ var require_fast_uri = __commonJS({
24093
24093
  }
24094
24094
  return uri;
24095
24095
  }
24096
- function resolve6(baseURI, relativeURI, options2) {
24096
+ function resolve10(baseURI, relativeURI, options2) {
24097
24097
  const schemelessOptions = options2 ? Object.assign({ scheme: "null" }, options2) : { scheme: "null" };
24098
24098
  const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
24099
24099
  const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
@@ -24377,7 +24377,7 @@ var require_fast_uri = __commonJS({
24377
24377
  var fastUri = {
24378
24378
  SCHEMES,
24379
24379
  normalize: normalize2,
24380
- resolve: resolve6,
24380
+ resolve: resolve10,
24381
24381
  resolveComponent,
24382
24382
  equal,
24383
24383
  serialize,
@@ -28418,7 +28418,7 @@ function retryDelay(response, attempt) {
28418
28418
  return 250 * 2 ** attempt;
28419
28419
  }
28420
28420
  function sleep(ms) {
28421
- return new Promise((resolve6) => setTimeout(resolve6, ms));
28421
+ return new Promise((resolve10) => setTimeout(resolve10, ms));
28422
28422
  }
28423
28423
  function createCliApi(baseUrl, fetcher = fetch, token, sessionId) {
28424
28424
  const transport = async (input2, init) => {
@@ -28600,10 +28600,92 @@ var init_registry = __esm({
28600
28600
  }
28601
28601
  });
28602
28602
 
28603
+ // src/timings.ts
28604
+ import { appendFileSync } from "node:fs";
28605
+ import { resolve } from "node:path";
28606
+ function executionTimings() {
28607
+ return { brokerMs: 0, bridgeMs: 0, registryMs: 0, bindingMs: 0, replayMs: 0, appMs: 0, proofMs: 0, reportingMs: 0 };
28608
+ }
28609
+ async function timed(timings, stage, work) {
28610
+ const started = performance.now();
28611
+ try {
28612
+ return await work();
28613
+ } finally {
28614
+ if (timings) timings[stage] += performance.now() - started;
28615
+ }
28616
+ }
28617
+ function saveTimings(timings, commandMs) {
28618
+ if (!process.env.APILIFT_TIMING_FILE) return;
28619
+ try {
28620
+ appendFileSync(resolve(process.env.APILIFT_TIMING_FILE), JSON.stringify({ ...Object.fromEntries(Object.entries(timings).map(([key, value]) => [key, Math.round(value * 100) / 100])), commandMs: Math.round(commandMs * 100) / 100 }) + "\n", { mode: 384 });
28621
+ } catch {
28622
+ }
28623
+ }
28624
+ var init_timings = __esm({
28625
+ "src/timings.ts"() {
28626
+ "use strict";
28627
+ }
28628
+ });
28629
+
28630
+ // src/files.ts
28631
+ import { randomUUID } from "node:crypto";
28632
+ import { accessSync, constants, existsSync, statSync, unlinkSync, writeFileSync } from "node:fs";
28633
+ import { dirname, join, resolve as resolve2 } from "node:path";
28634
+ function outputDestination(file2) {
28635
+ const target = resolve2(file2);
28636
+ if (existsSync(target)) {
28637
+ if (!statSync(target).isFile()) throw new Error("Output destination must be a regular file");
28638
+ accessSync(target, constants.W_OK);
28639
+ }
28640
+ const probe = join(dirname(target), `.apilift-output-check-${randomUUID()}`);
28641
+ try {
28642
+ writeFileSync(probe, "", { flag: "wx", mode: 384 });
28643
+ } finally {
28644
+ try {
28645
+ unlinkSync(probe);
28646
+ } catch {
28647
+ }
28648
+ }
28649
+ return target;
28650
+ }
28651
+ var init_files = __esm({
28652
+ "src/files.ts"() {
28653
+ "use strict";
28654
+ }
28655
+ });
28656
+
28657
+ // src/execution-reports.ts
28658
+ async function flushExecutionReports(api, state, budgetMs = 1e4) {
28659
+ const pending = state.pendingExecutionReports();
28660
+ if (!pending.length) return;
28661
+ const controller = new AbortController();
28662
+ let timer;
28663
+ try {
28664
+ const acknowledgment = await Promise.race([
28665
+ api.call("postExecutions", { body: { reports: pending.map((item) => item.report) }, signal: controller.signal }),
28666
+ new Promise((_, reject) => {
28667
+ timer = setTimeout(() => {
28668
+ controller.abort();
28669
+ reject(new Error("Report delivery deferred"));
28670
+ }, budgetMs);
28671
+ })
28672
+ ]);
28673
+ state.acknowledgeExecutionReports(acknowledgment.results.map((item) => item.id));
28674
+ } catch {
28675
+ } finally {
28676
+ clearTimeout(timer);
28677
+ }
28678
+ }
28679
+ var init_execution_reports = __esm({
28680
+ "src/execution-reports.ts"() {
28681
+ "use strict";
28682
+ }
28683
+ });
28684
+
28603
28685
  // src/usage/ledger.ts
28604
- import { createHmac, randomBytes, randomUUID } from "node:crypto";
28605
- import { chmodSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
28606
- import { join } from "node:path";
28686
+ import { createHmac, randomBytes, randomUUID as randomUUID2 } from "node:crypto";
28687
+ import { chmodSync, mkdirSync, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
28688
+ import { join as join2 } from "node:path";
28607
28689
  import { DatabaseSync } from "node:sqlite";
28608
28690
  function purposeFor(key) {
28609
28691
  return key.startsWith("discover") || key === "appsNew" || key === "appsUpdate" ? "discovery" : key.startsWith("setup") ? "setup" : "unknown";
@@ -28612,7 +28694,7 @@ function countedCommand(key) {
28612
28694
  return !["root", "version", "helpCommands", "appsSearch", "actionsList", "actionsSearch", "actionsShow", "transportsShow", "appsHistory", "contextShow"].includes(key);
28613
28695
  }
28614
28696
  function newInvocationId() {
28615
- return randomUUID();
28697
+ return randomUUID2();
28616
28698
  }
28617
28699
  var UsageLedger;
28618
28700
  var init_ledger = __esm({
@@ -28622,10 +28704,10 @@ var init_ledger = __esm({
28622
28704
  db;
28623
28705
  root;
28624
28706
  constructor(dataRoot2) {
28625
- this.root = join(dataRoot2, "usage");
28707
+ this.root = join2(dataRoot2, "usage");
28626
28708
  mkdirSync(this.root, { recursive: true, mode: 448 });
28627
28709
  chmodSync(this.root, 448);
28628
- const file2 = join(this.root, "ledger.sqlite");
28710
+ const file2 = join2(this.root, "ledger.sqlite");
28629
28711
  this.db = new DatabaseSync(file2);
28630
28712
  chmodSync(file2, 384);
28631
28713
  this.db.exec("PRAGMA busy_timeout=5000; PRAGMA journal_mode=WAL");
@@ -28649,8 +28731,8 @@ var init_ledger = __esm({
28649
28731
  return createHmac("sha256", this.secret()).update(JSON.stringify([runtime, nativeId])).digest("hex");
28650
28732
  }
28651
28733
  save(value) {
28652
- const path2 = join(this.root, `pending-${value.id}.json`);
28653
- writeFileSync(path2, JSON.stringify({ argv: value.argv, stdout: value.stdout, stderr: value.stderr }), { mode: 384, flag: "wx" });
28734
+ const path2 = join2(this.root, `pending-${value.id}.json`);
28735
+ writeFileSync2(path2, JSON.stringify({ argv: value.argv, stdout: value.stdout, stderr: value.stderr }), { mode: 384, flag: "wx" });
28654
28736
  this.db.prepare("INSERT INTO invocation (id,command_key,session_id,agent,started_at,finished_at,duration_ms,argv_json,output_path,purpose,execution_id) VALUES (?,?,?,?,?,?,?,?,?,?,?)").run(value.id, value.commandKey, value.session.id, value.session.agent, value.startedAt, value.finishedAt, value.durationMs, JSON.stringify(value.argv), path2, purposeFor(value.commandKey), value.executionId ?? null);
28655
28737
  }
28656
28738
  attachExecution(invocationId, executionId) {
@@ -28681,7 +28763,7 @@ var init_ledger = __esm({
28681
28763
  this.db.prepare("INSERT INTO usage_meta (key,value) VALUES ('account_id',?) ON CONFLICT(key) DO UPDATE SET value=excluded.value").run(accountId);
28682
28764
  }
28683
28765
  clearForRevocation() {
28684
- this.clearForAccountChange(`revoked:${randomUUID()}`);
28766
+ this.clearForAccountChange(`revoked:${randomUUID2()}`);
28685
28767
  this.db.prepare("DELETE FROM usage_meta WHERE key='account_id'").run();
28686
28768
  }
28687
28769
  prune() {
@@ -28703,9 +28785,9 @@ var init_ledger = __esm({
28703
28785
  });
28704
28786
 
28705
28787
  // src/usage/baselines.ts
28706
- import { randomUUID as randomUUID2 } from "node:crypto";
28707
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
28708
- import { join as join2 } from "node:path";
28788
+ import { randomUUID as randomUUID3 } from "node:crypto";
28789
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "node:fs";
28790
+ import { join as join3 } from "node:path";
28709
28791
  function withBaselines(root, work) {
28710
28792
  try {
28711
28793
  const ledger = new UsageLedger(root);
@@ -28739,10 +28821,10 @@ var init_baselines = __esm({
28739
28821
  finishPreparation(sessionId, appSlug, appId, method, at = /* @__PURE__ */ new Date()) {
28740
28822
  const row = this.ledger.db.prepare("SELECT started_at FROM baseline_preparation_start WHERE session_id=? AND app_slug=?").get(sessionId, appSlug);
28741
28823
  if (!row || this.ledger.db.prepare("SELECT 1 FROM baseline_candidate WHERE draft_id=?").get(`preparation:${sessionId}:${appId}:${method}`)) return;
28742
- this.ledger.db.prepare("INSERT INTO baseline_candidate (id,draft_id,app_id,session_id,method,started_at,stopped_at) VALUES (?,?,?,?,?,?,?)").run(randomUUID2(), `preparation:${sessionId}:${appId}:${method}`, appId, sessionId, method, row.started_at, at.toISOString());
28824
+ this.ledger.db.prepare("INSERT INTO baseline_candidate (id,draft_id,app_id,session_id,method,started_at,stopped_at) VALUES (?,?,?,?,?,?,?)").run(randomUUID3(), `preparation:${sessionId}:${appId}:${method}`, appId, sessionId, method, row.started_at, at.toISOString());
28743
28825
  }
28744
28826
  startCapture(draftId, appId, sessionId, at = /* @__PURE__ */ new Date()) {
28745
- const id2 = randomUUID2();
28827
+ const id2 = randomUUID3();
28746
28828
  this.ledger.db.prepare("UPDATE baseline_candidate SET observation_finished_at=?,observation_reason='next-capture' WHERE session_id=? AND observation_version=1 AND observation_finished_at IS NULL").run(at.toISOString(), sessionId);
28747
28829
  this.ledger.db.prepare("INSERT INTO baseline_candidate (id,draft_id,app_id,session_id,method,started_at,observation_version) VALUES (?,?,?,?,?,?,1)").run(id2, draftId, appId, sessionId, "browser", at.toISOString());
28748
28830
  return id2;
@@ -28757,12 +28839,12 @@ var init_baselines = __esm({
28757
28839
  this.ledger.db.prepare("UPDATE baseline_candidate SET observation_finished_at=?,observation_reason=CASE WHEN stopped_at IS NULL THEN 'cancelled' ELSE 'released' END WHERE draft_id=? AND method='browser' AND observation_version=1 AND observation_finished_at IS NULL").run(at.toISOString(), draftId);
28758
28840
  }
28759
28841
  recordDirect(input2) {
28760
- const id2 = randomUUID2(), at = input2.at ?? /* @__PURE__ */ new Date();
28761
- const dir = join2(this.ledger.root, "pending-baselines");
28842
+ const id2 = randomUUID3(), at = input2.at ?? /* @__PURE__ */ new Date();
28843
+ const dir = join3(this.ledger.root, "pending-baselines");
28762
28844
  mkdirSync2(dir, { recursive: true, mode: 448 });
28763
28845
  chmodSync2(dir, 448);
28764
- const path2 = join2(dir, `${id2}.json`);
28765
- writeFileSync2(path2, JSON.stringify({ request: input2.request, response: input2.response }), { mode: 384, flag: "wx" });
28846
+ const path2 = join3(dir, `${id2}.json`);
28847
+ writeFileSync3(path2, JSON.stringify({ request: input2.request, response: input2.response }), { mode: 384, flag: "wx" });
28766
28848
  this.ledger.db.prepare("INSERT INTO baseline_candidate (id,draft_id,app_id,session_id,method,started_at,stopped_at,duration_ms,payload_path,coverage,time_coverage) VALUES (?,?,?,?,?,?,?,?,?,'final','final')").run(id2, input2.draftId, input2.appId, input2.sessionId, "direct", at.toISOString(), at.toISOString(), Math.max(0, Math.round(input2.durationMs)), path2);
28767
28849
  if (input2.invocationId) this.ledger.db.prepare("UPDATE baseline_candidate SET invocation_id=?,http_started_at=?,http_finished_at=? WHERE id=?").run(input2.invocationId, input2.startedAt ?? null, input2.finishedAt ?? null, id2);
28768
28850
  return id2;
@@ -30304,13 +30386,13 @@ var init_envelope = __esm({
30304
30386
  });
30305
30387
 
30306
30388
  // src/discovery/drafts.ts
30307
- import { randomUUID as randomUUID3 } from "node:crypto";
30308
- import { chmodSync as chmodSync3, existsSync, mkdirSync as mkdirSync3, readFileSync as readFileSync2, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
30309
- import { dirname, join as join3 } from "node:path";
30389
+ import { randomUUID as randomUUID4 } from "node:crypto";
30390
+ import { chmodSync as chmodSync3, existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
30391
+ import { dirname as dirname2, join as join4 } from "node:path";
30310
30392
  function pathFor(root, appSlug, kind, name) {
30311
30393
  if (!slugPattern.test(appSlug)) throw new Error("App slug is invalid");
30312
30394
  if (kind === "action" ? !actionPattern.test(name) : kind !== "app" && !resourcePattern.test(name)) throw new Error("Resource name is invalid");
30313
- return kind === "app" ? join3(root, "drafts", diskName(appSlug), "app.jsonc") : join3(root, "drafts", diskName(appSlug), paths[kind], `${diskName(name)}.jsonc`);
30395
+ return kind === "app" ? join4(root, "drafts", diskName(appSlug), "app.jsonc") : join4(root, "drafts", diskName(appSlug), paths[kind], `${diskName(name)}.jsonc`);
30314
30396
  }
30315
30397
  function record2(row) {
30316
30398
  return { id: String(row.id), appSlug: String(row.app_slug), appId: row.app_id === null ? null : String(row.app_id), kind: String(row.kind), name: String(row.name), ownerSessionId: row.owner_session_id === null ? null : String(row.owner_session_id), filePath: String(row.file_path), intent: row.intent === "remove" ? "remove" : "edit", baseVersion: row.base_version === null ? null : Number(row.base_version) };
@@ -30387,7 +30469,7 @@ var init_drafts = __esm({
30387
30469
  const serialized = JSON.stringify(body);
30388
30470
  const existing = this.publication(draftId);
30389
30471
  if (existing) return existing;
30390
- const intent = { requestId: randomUUID3(), requestBody: serialized };
30472
+ const intent = { requestId: randomUUID4(), requestBody: serialized };
30391
30473
  this.state.db.prepare("INSERT INTO discovery_publication (draft_id,request_id,request_body) VALUES (?,?,?)").run(draftId, intent.requestId, serialized);
30392
30474
  return intent;
30393
30475
  }
@@ -30422,7 +30504,7 @@ var init_drafts = __esm({
30422
30504
  this.state.db.prepare("DELETE FROM discovery_draft WHERE id=? AND owner_session_id=?").run(draft.id, sessionId);
30423
30505
  this.state.db.exec("COMMIT");
30424
30506
  try {
30425
- unlinkSync(draft.filePath);
30507
+ unlinkSync2(draft.filePath);
30426
30508
  } catch {
30427
30509
  }
30428
30510
  return draft;
@@ -30461,12 +30543,12 @@ var init_drafts = __esm({
30461
30543
  const existing = this.state.db.prepare("SELECT * FROM discovery_draft WHERE app_slug=? AND kind=? AND name=?").get(input2.appSlug, input2.kind, name);
30462
30544
  if (existing?.owner_session_id && existing.owner_session_id !== input2.sessionId) throw new DraftConflict("owned-draft", `${input2.kind} ${name} is owned by another agent session; its file was preserved`);
30463
30545
  if (existing) filePath = String(existing.file_path);
30464
- const id2 = existing ? String(existing.id) : randomUUID3();
30546
+ const id2 = existing ? String(existing.id) : randomUUID4();
30465
30547
  if (!existing) this.state.db.prepare("INSERT INTO discovery_draft (id,app_slug,app_id,kind,name,owner_session_id,file_path,base_version) VALUES (?,?,?,?,?,?,?,?)").run(id2, input2.appSlug, input2.appId ?? null, input2.kind, name, input2.sessionId, filePath, input2.baseVersion ?? null);
30466
30548
  else this.state.db.prepare("UPDATE discovery_draft SET owner_session_id=? WHERE id=?").run(input2.sessionId, id2);
30467
30549
  this.state.db.prepare("INSERT INTO selected_discovery_draft (session_id,draft_id) VALUES (?,?) ON CONFLICT(session_id) DO UPDATE SET draft_id=excluded.draft_id").run(input2.sessionId, id2);
30468
- mkdirSync3(dirname(filePath), { recursive: true, mode: 448 });
30469
- if (!existsSync(filePath) && existing?.intent !== "remove") writeFileSync3(filePath, draftTemplate(input2.kind), { flag: "wx", mode: 384 });
30550
+ mkdirSync3(dirname2(filePath), { recursive: true, mode: 448 });
30551
+ if (!existsSync2(filePath) && existing?.intent !== "remove") writeFileSync4(filePath, draftTemplate(input2.kind), { flag: "wx", mode: 384 });
30470
30552
  chmodSync3(filePath, 384);
30471
30553
  const result3 = this.selected(input2.sessionId);
30472
30554
  this.state.db.exec("COMMIT");
@@ -30491,11 +30573,11 @@ var init_drafts = __esm({
30491
30573
  this.state.db.exec("COMMIT");
30492
30574
  return this.selected(input2.sessionId);
30493
30575
  }
30494
- const id2 = randomUUID3();
30576
+ const id2 = randomUUID4();
30495
30577
  this.state.db.prepare("INSERT INTO discovery_draft (id,app_slug,app_id,kind,name,owner_session_id,file_path,base_version,intent) VALUES (?,?,?,?,?,?,?,?,'remove')").run(id2, input2.appSlug, input2.appId, input2.kind, input2.name, input2.sessionId, filePath, input2.baseVersion);
30496
30578
  this.state.db.prepare("INSERT INTO selected_discovery_draft (session_id,draft_id) VALUES (?,?)").run(input2.sessionId, id2);
30497
- mkdirSync3(dirname(filePath), { recursive: true, mode: 448 });
30498
- writeFileSync3(filePath, `// Publish to remove this resource. No records inside the app are changed.
30579
+ mkdirSync3(dirname2(filePath), { recursive: true, mode: 448 });
30580
+ writeFileSync4(filePath, `// Publish to remove this resource. No records inside the app are changed.
30499
30581
  ${JSON.stringify({ remove: true, expectedVersion: input2.baseVersion }, null, 2)}
30500
30582
  `, { flag: "wx", mode: 384 });
30501
30583
  this.state.db.exec("COMMIT");
@@ -31122,8 +31204,8 @@ import { spawn } from "node:child_process";
31122
31204
  function backgroundOptions(env) {
31123
31205
  return { detached: true, windowsHide: true, stdio: "ignore", env };
31124
31206
  }
31125
- function startBackground(script, env, onError) {
31126
- const child = spawn(process.execPath, [script], backgroundOptions(env));
31207
+ function startBackground(script2, env, onError) {
31208
+ const child = spawn(process.execPath, [script2], backgroundOptions(env));
31127
31209
  child.once("error", onError);
31128
31210
  child.unref();
31129
31211
  return child;
@@ -31136,8 +31218,8 @@ var init_process = __esm({
31136
31218
 
31137
31219
  // src/capture-server/client.ts
31138
31220
  import { createHash as createHash2 } from "node:crypto";
31139
- import { chmodSync as chmodSync4, existsSync as existsSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync as rmSync2 } from "node:fs";
31140
- import { dirname as dirname2, join as join4 } from "node:path";
31221
+ import { chmodSync as chmodSync4, existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync as rmSync2 } from "node:fs";
31222
+ import { dirname as dirname3, join as join5 } from "node:path";
31141
31223
  import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
31142
31224
  function knownLegacyBroker(version2) {
31143
31225
  const match = /^1\.4\.(\d+)$/.exec(version2);
@@ -31157,7 +31239,7 @@ function reusableBroker(client, health, requestedVersion) {
31157
31239
  }
31158
31240
  function receiptAt(root) {
31159
31241
  try {
31160
- const raw = JSON.parse(readFileSync4(join4(root, "server.json"), "utf8"));
31242
+ const raw = JSON.parse(readFileSync4(join5(root, "server.json"), "utf8"));
31161
31243
  if (!Number.isInteger(raw.port) || raw.port <= 0 || typeof raw.token !== "string" || typeof raw.version !== "string") return null;
31162
31244
  return raw;
31163
31245
  } catch {
@@ -31166,24 +31248,24 @@ function receiptAt(root) {
31166
31248
  }
31167
31249
  function nativeRestartVersion(root) {
31168
31250
  try {
31169
- const version2 = JSON.parse(readFileSync4(join4(root, "native-host", "restart-required.json"), "utf8")).version;
31251
+ const version2 = JSON.parse(readFileSync4(join5(root, "native-host", "restart-required.json"), "utf8")).version;
31170
31252
  return typeof version2 === "string" && /^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/u.test(version2) ? version2 : null;
31171
31253
  } catch {
31172
31254
  return null;
31173
31255
  }
31174
31256
  }
31175
- function installedCaptureVersion(script) {
31257
+ function installedCaptureVersion(script2) {
31176
31258
  try {
31177
- const pkg = JSON.parse(readFileSync4(join4(dirname2(script), "..", "package.json"), "utf8"));
31259
+ const pkg = JSON.parse(readFileSync4(join5(dirname3(script2), "..", "package.json"), "utf8"));
31178
31260
  return ["apilift", "@apilift/cli"].includes(String(pkg.name)) && typeof pkg.version === "string" && /^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/u.test(pkg.version) ? pkg.version : null;
31179
31261
  } catch {
31180
31262
  return null;
31181
31263
  }
31182
31264
  }
31183
31265
  async function ensureCaptureServer(options2) {
31184
- const locks = join4(options2.root, "locks");
31266
+ const locks = join5(options2.root, "locks");
31185
31267
  mkdirSync4(locks, { recursive: true, mode: 448 });
31186
- const updateLock = new DatabaseSync2(join4(locks, "self-update.sqlite"));
31268
+ const updateLock = new DatabaseSync2(join5(locks, "self-update.sqlite"));
31187
31269
  let acquired = false;
31188
31270
  try {
31189
31271
  updateLock.exec("PRAGMA busy_timeout=10000; BEGIN EXCLUSIVE");
@@ -31206,9 +31288,9 @@ async function ensureCaptureServerUnderUpdateLock(options2) {
31206
31288
  } catch {
31207
31289
  }
31208
31290
  }
31209
- const locks = join4(root, "locks");
31291
+ const locks = join5(root, "locks");
31210
31292
  mkdirSync4(locks, { recursive: true, mode: 448 });
31211
- const lockFile = join4(locks, "capture-server.sqlite");
31293
+ const lockFile = join5(locks, "capture-server.sqlite");
31212
31294
  const lock2 = new DatabaseSync2(lockFile);
31213
31295
  chmodSync4(lockFile, 384);
31214
31296
  lock2.exec("PRAGMA busy_timeout=10000; CREATE TABLE IF NOT EXISTS owner (name TEXT PRIMARY KEY)");
@@ -31226,11 +31308,11 @@ async function ensureCaptureServerUnderUpdateLock(options2) {
31226
31308
  if (error62.code === "capture-server-busy") throw error62;
31227
31309
  }
31228
31310
  try {
31229
- rmSync2(join4(root, "server.json"));
31311
+ rmSync2(join5(root, "server.json"));
31230
31312
  } catch {
31231
31313
  }
31232
31314
  }
31233
- if (!existsSync2(options2.script)) throw new Error(`Capture server entry point is missing: ${options2.script}`);
31315
+ if (!existsSync3(options2.script)) throw new Error(`Capture server entry point is missing: ${options2.script}`);
31234
31316
  let startupError;
31235
31317
  const child = startBackground(options2.script, { ...process.env, ...options2.env, APILIFT_HOME: root }, (error62) => {
31236
31318
  startupError = error62;
@@ -31246,7 +31328,7 @@ async function ensureCaptureServerUnderUpdateLock(options2) {
31246
31328
  } catch {
31247
31329
  }
31248
31330
  }
31249
- await new Promise((resolve6) => setTimeout(resolve6, 50));
31331
+ await new Promise((resolve10) => setTimeout(resolve10, 50));
31250
31332
  }
31251
31333
  throw new Error("Capture server did not become ready within 10 seconds");
31252
31334
  } finally {
@@ -31284,12 +31366,19 @@ var init_client2 = __esm({
31284
31366
  scopes() {
31285
31367
  return this.call("GET", "/scopes");
31286
31368
  }
31369
+ async bridgeDemand() {
31370
+ try {
31371
+ await this.call("POST", "/bridge/demand", {});
31372
+ } catch (error62) {
31373
+ if (error62.code !== "not-found") throw error62;
31374
+ }
31375
+ }
31287
31376
  bridgeReady(version2, capabilities = []) {
31288
31377
  return this.call("POST", "/bridge/ready", { version: version2, capabilities }).then(() => {
31289
31378
  });
31290
31379
  }
31291
- candidates(id2) {
31292
- return this.call("GET", `/scope/candidates?id=${encodeURIComponent(id2)}`);
31380
+ candidates(id2, waitMs = 0) {
31381
+ return this.call("GET", `/scope/candidates?id=${encodeURIComponent(id2)}&waitMs=${waitMs}`);
31293
31382
  }
31294
31383
  entries(id2, after = 0, limit = 100) {
31295
31384
  return this.call("GET", `/scope/entries?id=${encodeURIComponent(id2)}&after=${after}&limit=${limit}`);
@@ -31322,8 +31411,8 @@ var init_client2 = __esm({
31322
31411
  enqueueJob(scopeId, envelope) {
31323
31412
  return this.call("POST", "/job/enqueue", { scopeId, envelope });
31324
31413
  }
31325
- jobResult(id2) {
31326
- return this.call("GET", `/job/result?id=${encodeURIComponent(id2)}`);
31414
+ jobResult(id2, waitMs = 0) {
31415
+ return this.call("GET", `/job/result?id=${encodeURIComponent(id2)}&waitMs=${waitMs}`);
31327
31416
  }
31328
31417
  acknowledgeJob(id2) {
31329
31418
  return this.call("POST", "/job/ack", { id: id2 }).then(() => {
@@ -31344,25 +31433,37 @@ var init_client2 = __esm({
31344
31433
  });
31345
31434
 
31346
31435
  // src/capture-server/demand.ts
31347
- import { randomUUID as randomUUID4 } from "node:crypto";
31348
- import { join as join5 } from "node:path";
31436
+ import { randomUUID as randomUUID5 } from "node:crypto";
31437
+ import { join as join6 } from "node:path";
31349
31438
  import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
31350
- async function waitForBridge(root, version2, timeoutMs = 9e4) {
31439
+ async function waitForBridge(root, version2, timeoutMs = 9e4, wake, compatibleBrokerVersion) {
31351
31440
  const db = open2(root);
31352
31441
  db.exec("PRAGMA busy_timeout=1000");
31353
- const id2 = randomUUID4(), now = Date.now(), ownerStartedAt = now - process.uptime() * 1e3;
31442
+ const id2 = randomUUID5(), now = Date.now(), ownerStartedAt = now - process.uptime() * 1e3;
31354
31443
  const expiry = now + timeoutMs;
31355
- db.prepare("INSERT INTO bridge_demands(id,owner_pid,owner_started_at,expires_at) VALUES(?,?,?,?)").run(id2, process.pid, ownerStartedAt, expiry);
31444
+ const sequenced = db.prepare("PRAGMA table_info(bridge_status)").all().some((column) => column.name === "sequence");
31445
+ let priorSequence = -1;
31446
+ db.exec("BEGIN IMMEDIATE");
31447
+ try {
31448
+ if (sequenced) priorSequence = Number(db.prepare("SELECT sequence FROM bridge_status WHERE id=1").get()?.sequence ?? -1);
31449
+ db.prepare("INSERT INTO bridge_demands(id,owner_pid,owner_started_at,expires_at) VALUES(?,?,?,?)").run(id2, process.pid, ownerStartedAt, expiry);
31450
+ db.exec("COMMIT");
31451
+ } catch (error62) {
31452
+ db.exec("ROLLBACK");
31453
+ db.close();
31454
+ throw error62;
31455
+ }
31356
31456
  let announced = false;
31357
31457
  try {
31458
+ await wake?.();
31358
31459
  while (Date.now() < expiry) {
31359
- const ready = db.prepare("SELECT version,updated_at FROM bridge_status WHERE id=1").get();
31360
- if (ready?.version === version2 && ready.updated_at >= now && Date.now() - ready.updated_at < 15e3) return;
31460
+ const ready = db.prepare(`SELECT version,updated_at${sequenced ? ",sequence" : ""} FROM bridge_status WHERE id=1`).get();
31461
+ if (ready && (ready.version === version2 || ready.version === compatibleBrokerVersion) && ready.updated_at >= now && (!sequenced || Number(ready.sequence) > priorSequence) && Date.now() - ready.updated_at < 15e3) return;
31361
31462
  if (!announced && Date.now() - now >= 5e3) {
31362
31463
  process.stderr.write("Waiting for the Apilift browser connection.\n");
31363
31464
  announced = true;
31364
31465
  }
31365
- await new Promise((resolve6) => setTimeout(resolve6, 250));
31466
+ await new Promise((resolve10) => setTimeout(resolve10, 20));
31366
31467
  }
31367
31468
  throw Object.assign(new Error("The Apilift browser did not respond within 90 seconds. Verify that Chrome is running with the extension enabled, then retry this command. No app action was sent."), { code: "extension-unavailable" });
31368
31469
  } finally {
@@ -31377,14 +31478,14 @@ var databasePath, open2;
31377
31478
  var init_demand = __esm({
31378
31479
  "src/capture-server/demand.ts"() {
31379
31480
  "use strict";
31380
- databasePath = (root) => join5(root, "capture.sqlite");
31481
+ databasePath = (root) => join6(root, "capture.sqlite");
31381
31482
  open2 = (root, readOnly = false) => new DatabaseSync3(databasePath(root), { readOnly });
31382
31483
  }
31383
31484
  });
31384
31485
 
31385
31486
  // src/commands/browser.ts
31386
31487
  import { realpathSync } from "node:fs";
31387
- import { dirname as dirname3, join as join6 } from "node:path";
31488
+ import { dirname as dirname4, join as join7 } from "node:path";
31388
31489
  async function checkTabOwnership(client, origin, tab, app2, prefix) {
31389
31490
  const state = await client.scopes();
31390
31491
  const active2 = state.scopes.filter((scope2) => scope2.pageOrigin === origin && scope2.kind !== "inspect");
@@ -31417,13 +31518,13 @@ async function browserContext(api, appId) {
31417
31518
  return null;
31418
31519
  }
31419
31520
  function captureServerScript(entry2 = process.argv[1]) {
31420
- return join6(dirname3(realpathSync(entry2)), "apilift-capture-server.js");
31521
+ return join7(dirname4(realpathSync(entry2)), "apilift-capture-server.js");
31421
31522
  }
31422
- async function captureClient(root, requireBridge = true) {
31523
+ async function captureClient(root, requireBridge = true, timings) {
31423
31524
  const startedAt = Date.now();
31424
- const script = process.env.APILIFT_CAPTURE_SERVER_SCRIPT ?? captureServerScript();
31425
- const client = await ensureCaptureServer({ root, script, version: cliVersion });
31426
- if (requireBridge && !client.legacyBridge) await waitForBridge(root, cliVersion, Math.max(0, 9e4 - (Date.now() - startedAt)));
31525
+ const script2 = process.env.APILIFT_CAPTURE_SERVER_SCRIPT ?? captureServerScript();
31526
+ const client = await timed(timings, "brokerMs", () => ensureCaptureServer({ root, script: script2, version: cliVersion }));
31527
+ if (requireBridge && !client.legacyBridge) await timed(timings, "bridgeMs", () => waitForBridge(root, cliVersion, Math.max(0, 9e4 - (Date.now() - startedAt)), () => client.bridgeDemand(), client.receipt.version));
31427
31528
  return client;
31428
31529
  }
31429
31530
  async function inspectTabs(client, appId, contextId, pageOrigin) {
@@ -31433,7 +31534,7 @@ async function inspectTabs(client, appId, contextId, pageOrigin) {
31433
31534
  do {
31434
31535
  const candidates = (await client.candidates(scope2.id)).candidates;
31435
31536
  if (candidates.length) return candidates;
31436
- await new Promise((resolve6) => setTimeout(resolve6, 250));
31537
+ await new Promise((resolve10) => setTimeout(resolve10, 250));
31437
31538
  } while (Date.now() < deadline);
31438
31539
  return [];
31439
31540
  } finally {
@@ -31490,6 +31591,7 @@ var ok, fail, command, BrowserControlError;
31490
31591
  var init_browser = __esm({
31491
31592
  "src/commands/browser.ts"() {
31492
31593
  "use strict";
31594
+ init_timings();
31493
31595
  init_version();
31494
31596
  init_client2();
31495
31597
  init_demand();
@@ -31509,14 +31611,14 @@ var init_browser = __esm({
31509
31611
  // src/state.ts
31510
31612
  import { chmodSync as chmodSync5, mkdirSync as mkdirSync5 } from "node:fs";
31511
31613
  import { homedir, platform } from "node:os";
31512
- import { isAbsolute, join as join7, resolve } from "node:path";
31614
+ import { isAbsolute, join as join8, resolve as resolve3 } from "node:path";
31513
31615
  import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
31514
31616
  function dataRoot(env) {
31515
- if (env.APILIFT_HOME) return resolve(env.APILIFT_HOME);
31617
+ if (env.APILIFT_HOME) return resolve3(env.APILIFT_HOME);
31516
31618
  const home = homedir();
31517
- if (platform() === "darwin") return join7(home, "Library", "Application Support", "apilift");
31518
- if (platform() === "win32") return join7(env.LOCALAPPDATA || join7(home, "AppData", "Local"), "apilift");
31519
- return join7(env.XDG_DATA_HOME && isAbsolute(env.XDG_DATA_HOME) ? env.XDG_DATA_HOME : join7(home, ".local", "share"), "apilift");
31619
+ if (platform() === "darwin") return join8(home, "Library", "Application Support", "apilift");
31620
+ if (platform() === "win32") return join8(env.LOCALAPPDATA || join8(home, "AppData", "Local"), "apilift");
31621
+ return join8(env.XDG_DATA_HOME && isAbsolute(env.XDG_DATA_HOME) ? env.XDG_DATA_HOME : join8(home, ".local", "share"), "apilift");
31520
31622
  }
31521
31623
  var LocalState;
31522
31624
  var init_state = __esm({
@@ -31528,7 +31630,7 @@ var init_state = __esm({
31528
31630
  if (!isAbsolute(root)) throw new Error("State root must be absolute");
31529
31631
  mkdirSync5(root, { recursive: true, mode: 448 });
31530
31632
  chmodSync5(root, 448);
31531
- const file2 = join7(root, "state.sqlite");
31633
+ const file2 = join8(root, "state.sqlite");
31532
31634
  this.db = new DatabaseSync4(file2);
31533
31635
  chmodSync5(file2, 384);
31534
31636
  this.db.exec("PRAGMA busy_timeout=5000; PRAGMA journal_mode=WAL");
@@ -31614,7 +31716,7 @@ var init_state = __esm({
31614
31716
  });
31615
31717
 
31616
31718
  // src/native-host/credentials.ts
31617
- import { createCipheriv, createDecipheriv, randomBytes as randomBytes2, randomUUID as randomUUID5 } from "node:crypto";
31719
+ import { createCipheriv, createDecipheriv, randomBytes as randomBytes2, randomUUID as randomUUID6 } from "node:crypto";
31618
31720
  var CredentialError, sameNames, sameRequirements, CredentialStore;
31619
31721
  var init_credentials = __esm({
31620
31722
  "src/native-host/credentials.ts"() {
@@ -31690,7 +31792,7 @@ var init_credentials = __esm({
31690
31792
  if (!name || name.length > 80) throw new CredentialError("credential-label-invalid", "Credential label must be 1\u201380 characters");
31691
31793
  const names2 = binding2.requirements.map((item) => item.name);
31692
31794
  if (!sameNames(Object.keys(binding2.fields), names2) || Object.values(binding2.fields).some((value) => !value)) throw new CredentialError("credential-fields-invalid", "Credential fields do not match the current transport requirements");
31693
- const id2 = randomUUID5(), key = randomBytes2(32), iv = randomBytes2(12), cipher = createCipheriv("aes-256-gcm", key, iv);
31795
+ const id2 = randomUUID6(), key = randomBytes2(32), iv = randomBytes2(12), cipher = createCipheriv("aes-256-gcm", key, iv);
31694
31796
  const ciphertext = Buffer.concat([cipher.update(JSON.stringify(binding2.fields), "utf8"), cipher.final()]), tag = cipher.getAuthTag();
31695
31797
  await this.vault.put(id2, key.toString("base64"));
31696
31798
  try {
@@ -31846,9 +31948,9 @@ var init_vault = __esm({
31846
31948
  });
31847
31949
 
31848
31950
  // src/commands/run.ts
31849
- import { randomUUID as randomUUID6 } from "node:crypto";
31850
- import { readFileSync as readFileSync5, renameSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
31851
- import { dirname as dirname4, isAbsolute as isAbsolute2, join as join8 } from "node:path";
31951
+ import { randomUUID as randomUUID7 } from "node:crypto";
31952
+ import { readFileSync as readFileSync5, renameSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6 } from "node:fs";
31953
+ import { dirname as dirname5, resolve as resolve4, join as join9 } from "node:path";
31852
31954
  function pointer2(value, path2) {
31853
31955
  if (path2 === "") return value;
31854
31956
  if (!path2.startsWith("/") || /~(?![01])/u.test(path2)) throw new Error(`Invalid JSON Pointer ${path2}`);
@@ -32060,14 +32162,14 @@ function selectPointers(full, paths2) {
32060
32162
  return selection;
32061
32163
  }
32062
32164
  function writeOutput(path2, content) {
32063
- if (!isAbsolute2(path2)) throw new Error("--output requires an absolute path");
32064
- const pending = join8(dirname4(path2), `.apilift-output-${randomUUID6()}`);
32165
+ path2 = resolve4(path2);
32166
+ const pending = join9(dirname5(path2), `.apilift-output-${randomUUID7()}`);
32065
32167
  try {
32066
- writeFileSync4(pending, content, { flag: "wx", mode: 384 });
32168
+ writeFileSync5(pending, content, { flag: "wx", mode: 384 });
32067
32169
  renameSync(pending, path2);
32068
32170
  } catch (error62) {
32069
32171
  try {
32070
- unlinkSync2(pending);
32172
+ unlinkSync3(pending);
32071
32173
  } catch {
32072
32174
  }
32073
32175
  throw error62;
@@ -32101,25 +32203,32 @@ async function send(fetcher, prepared, allowNonJsonProbe = false) {
32101
32203
  return { response, body };
32102
32204
  }
32103
32205
  async function sendBrowser(root, appId, contextName, pageOrigin, effect2, transportName, transport, prepared, dispatched, credentialId, credentialDraftId, allowNonJsonProbe = false, dispatchBefore, selection = {}) {
32104
- const client = await captureClient(root);
32105
- const inspect = await client.openScope({ kind: "inspect", appId, contextId: contextName, pageOrigin, ownerPid: process.pid, ownerStartedAt: Date.now() - process.uptime() * 1e3 });
32106
- let candidates = [];
32107
- try {
32108
- const deadline = Date.now() + (client.legacyBridge ? 9e4 : 1e4);
32109
- do {
32110
- candidates = (await client.candidates(inspect.id)).candidates;
32111
- if (candidates.length) break;
32112
- await new Promise((resolve6) => setTimeout(resolve6, 200));
32113
- } while (Date.now() < deadline);
32114
- } finally {
32115
- await client.closeScope(inspect.id);
32116
- }
32206
+ const client = selection.client ?? await captureClient(root, true, selection.timings);
32117
32207
  const app2 = selection.appSlug ?? appId, prefix = selection.prefix ?? "apilift";
32118
- const tab = selection.tab ? candidates.find((candidate2) => candidate2.selector === selection.tab) : candidates.length === 1 ? candidates[0] : void 0;
32119
- if (!tab) throw new BrowserControlError(selection.tab ? `Configured capture.tab is not present. Inspect ${prefix} browser tabs ${app2} and select a reported tab in the current draft` : candidates.length ? `Several matching Chrome tabs were reported. Use ${prefix} browser tabs ${app2} and set capture.tab in the current draft for both capture and execution; leave other sessions' tabs open` : "No matching Chrome tab was reported within 10 seconds", "tab-unselected");
32120
- await checkTabOwnership(client, pageOrigin, tab.selector, app2, prefix);
32121
- const scope2 = await client.openScope({ kind: "execute", appId, contextId: contextName, pageOrigin, tabSelector: tab.selector, ownerPid: process.pid, ownerStartedAt: Date.now() - process.uptime() * 1e3, ownerSession: selection.ownerSession });
32122
- const id2 = randomUUID6();
32208
+ let scope2 = selection.execution?.scope;
32209
+ if (!scope2) {
32210
+ const bindingStarted = performance.now();
32211
+ const inspect = await client.openScope({ kind: "inspect", appId, contextId: contextName, pageOrigin, ownerPid: process.pid, ownerStartedAt: Date.now() - process.uptime() * 1e3 });
32212
+ let candidates = [];
32213
+ try {
32214
+ const deadline = Date.now() + (client.legacyBridge ? 9e4 : 1e4);
32215
+ do {
32216
+ candidates = (await client.candidates(inspect.id, client.legacyBridge ? 0 : 1e3)).candidates;
32217
+ if (candidates.length) break;
32218
+ await new Promise((resolve10) => setTimeout(resolve10, client.legacyBridge ? 200 : 10));
32219
+ } while (Date.now() < deadline);
32220
+ } finally {
32221
+ await client.closeScope(inspect.id);
32222
+ }
32223
+ const tab = selection.tab ? candidates.find((candidate2) => candidate2.selector === selection.tab) : candidates.length === 1 ? candidates[0] : void 0;
32224
+ if (!tab) throw new BrowserControlError(selection.tab ? `Configured capture.tab is not present. Inspect ${prefix} browser tabs ${app2} and select a reported tab in the current draft` : candidates.length ? `Several matching Chrome tabs were reported. Use ${prefix} browser tabs ${app2} and set capture.tab in the current draft for both capture and execution; leave other sessions' tabs open` : "No matching Chrome tab was reported within 10 seconds", "tab-unselected");
32225
+ await checkTabOwnership(client, pageOrigin, tab.selector, app2, prefix);
32226
+ scope2 = await client.openScope({ kind: "execute", appId, contextId: contextName, pageOrigin, tabSelector: tab.selector, ownerPid: process.pid, ownerStartedAt: Date.now() - process.uptime() * 1e3, ownerSession: selection.ownerSession });
32227
+ if (selection.execution) selection.execution.scope = scope2;
32228
+ if (selection.timings) selection.timings.bindingMs += performance.now() - bindingStarted;
32229
+ }
32230
+ const replayStarted = performance.now();
32231
+ const id2 = randomUUID7();
32123
32232
  try {
32124
32233
  const headers = Object.fromEntries(new Headers(prepared.init.headers).entries());
32125
32234
  const authRecipeDefinition = transport.inject?.length ? { inject: transport.inject } : void 0;
@@ -32129,7 +32238,7 @@ async function sendBrowser(root, appId, contextName, pageOrigin, effect2, transp
32129
32238
  await client.enqueueJob(scope2.id, envelope);
32130
32239
  const deadline = Date.now() + 45e3;
32131
32240
  do {
32132
- const held = await client.jobResult(id2);
32241
+ const held = await client.jobResult(id2, client.legacyBridge ? 0 : 1e3);
32133
32242
  if (held.status === "delivered") {
32134
32243
  await client.acknowledgeJob(id2);
32135
32244
  const value = held.result;
@@ -32145,16 +32254,18 @@ async function sendBrowser(root, appId, contextName, pageOrigin, effect2, transp
32145
32254
  throw new Error(value.transportError);
32146
32255
  }
32147
32256
  if (!Number.isInteger(value.status) || value.bodyState && value.bodyState !== "complete") throw new Error("Chrome did not return complete response evidence");
32257
+ if (selection.timings && Number.isFinite(value.ms) && value.ms >= 0) selection.timings.appMs += value.ms;
32148
32258
  const body = [204, 205, 304].includes(value.status) ? null : value.body ?? null;
32149
32259
  const response = new Response(body, { status: value.status });
32150
32260
  return { response, body: !response.ok ? null : decodedBody(value.body ?? "", response.status, allowNonJsonProbe) };
32151
32261
  }
32152
32262
  if (held.status === "uncertain") throw new Error("Chrome execution outcome is uncertain");
32153
- await new Promise((resolve6) => setTimeout(resolve6, 200));
32263
+ await new Promise((resolve10) => setTimeout(resolve10, client.legacyBridge ? 200 : 10));
32154
32264
  } while (Date.now() < deadline);
32155
32265
  throw new Error("Chrome did not return a result within 45 seconds");
32156
32266
  } finally {
32157
- await client.closeScope(scope2.id);
32267
+ if (selection.timings) selection.timings.replayMs += performance.now() - replayStarted;
32268
+ if (!selection.execution) await client.closeScope(scope2.id);
32158
32269
  }
32159
32270
  }
32160
32271
  function proofPassed(proof, body, primary, inputs, status) {
@@ -32171,8 +32282,22 @@ async function handleRun(invocation, api, state, session, fetcher, root, stdin =
32171
32282
  const prefix = (suffix) => `apilift${invocation.sessionLabel ? ` --session ${invocation.sessionLabel}` : ""} ${suffix}`;
32172
32283
  if (!session) return fail2(`No stable agent identity is available. No app request was sent. Use apilift --session <worker-label> run ${appName} ${actionName} and keep the same label.
32173
32284
  `);
32285
+ const commandStarted = performance.now(), timings = executionTimings();
32174
32286
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
32175
- const documentation = await api.call("getAction", { params: { app: appName, action: actionName } });
32287
+ let outputPath, recoveryPath;
32288
+ try {
32289
+ for (const path2 of invocation.repeats.select ?? []) if (path2 !== "" && (!path2.startsWith("/") || /~(?![01])/u.test(path2))) throw new Error(`Invalid JSON Pointer ${path2}`);
32290
+ if (invocation.options.output) outputPath = outputDestination(invocation.options.output);
32291
+ if (outputPath || invocation.repeats.select?.length) {
32292
+ const directory = join9(root, "results");
32293
+ mkdirSync6(directory, { recursive: true, mode: 448 });
32294
+ recoveryPath = outputDestination(join9(directory, `${randomUUID7()}.json`));
32295
+ }
32296
+ } catch (error62) {
32297
+ return fail2(`invalid-output \u2014 ${error62 instanceof Error ? error62.message : "Output destination is unusable"}. No app request was sent.
32298
+ `);
32299
+ }
32300
+ const documentation = await timed(timings, "registryMs", () => api.call("getAction", { params: { app: appName, action: actionName } }));
32176
32301
  if (documentation.availability !== "executable") return fail2(`action-unavailable \u2014 ${actionName} cannot run. ${documentation.unavailable?.reason ?? "Inspect its documentation."}
32177
32302
  `, 3);
32178
32303
  let explicit;
@@ -32198,15 +32323,17 @@ ${lines.length ? lines.join("\n") : " (none)"}
32198
32323
  No app request was sent.
32199
32324
  `);
32200
32325
  }
32326
+ const browserExecution = {};
32327
+ let liveBrowser;
32201
32328
  if (documentation.transport?.mode === "browser") {
32202
32329
  try {
32203
- await captureClient(root);
32330
+ liveBrowser = await captureClient(root, true, timings);
32204
32331
  } catch (error62) {
32205
32332
  return fail2(`extension-unavailable \u2014 ${error62 instanceof Error ? error62.message : "Chrome did not respond"}. No app request was sent.
32206
32333
  `, 5);
32207
32334
  }
32208
32335
  }
32209
- const bundle2 = await api.call("getBundle", { params: { app: appName, action: actionName } });
32336
+ const bundle2 = await timed(timings, "registryMs", () => api.call("getBundle", { params: { app: appName, action: actionName } }));
32210
32337
  const actionVersion = bundle2.closure.versions.find((item) => item.kind === "action" && item.name === actionName);
32211
32338
  const action = actionVersion?.definition;
32212
32339
  const transportVersion = bundle2.closure.versions.find((item) => item.kind === "transport" && item.name === (action && "transport" in action ? action.transport : ""));
@@ -32221,6 +32348,14 @@ No app request was sent.
32221
32348
  const page2 = bundle2.closure.versions.find((item) => item.kind === "browser-context" && item.name === transport.browserContext);
32222
32349
  if (!page2) return fail2("browser-page-missing \u2014 The selected browser page is unavailable. No app request was sent.\n", 5);
32223
32350
  browserPage = page2.definition;
32351
+ if (!liveBrowser) {
32352
+ try {
32353
+ liveBrowser = await captureClient(root, true, timings);
32354
+ } catch (error62) {
32355
+ return fail2(`extension-unavailable \u2014 ${error62 instanceof Error ? error62.message : "Chrome did not respond"}. No app request was sent.
32356
+ `, 5);
32357
+ }
32358
+ }
32224
32359
  }
32225
32360
  try {
32226
32361
  inputs = effectiveInputs(action.inputs ?? { type: "object", properties: {}, additionalProperties: false }, explicit, state.useSession(session.id, bundle2.app.id)?.context ?? {}, invocation.flags.includes("no-context"));
@@ -32229,7 +32364,7 @@ No app request was sent.
32229
32364
  Inspect current action documentation: ${prefix(`actions show ${appName} ${actionName}`)}
32230
32365
  `);
32231
32366
  }
32232
- const generated = Object.fromEntries(Object.entries(action.generated ?? {}).map(([name, spec]) => [name, `${spec.prefix ?? ""}${randomUUID6()}`]));
32367
+ const generated = Object.fromEntries(Object.entries(action.generated ?? {}).map(([name, spec]) => [name, `${spec.prefix ?? ""}${randomUUID7()}`]));
32233
32368
  let prepared;
32234
32369
  try {
32235
32370
  prepared = prepareRequest(action.request, transport, inputs.values, generated);
@@ -32246,7 +32381,7 @@ Inspect current action documentation: ${prefix(`actions show ${appName} ${action
32246
32381
  if (transport.mode === "browser") {
32247
32382
  const prior = dispatchedAt;
32248
32383
  try {
32249
- return await sendBrowser(root, bundle2.app.id, transport.browserContext, browserPage.pageOrigin, effect2, action.transport, transport, request2, markDispatched, selectedCredential, void 0, false, bundle2.receipt?.dispatchBefore, { tab: state.browserTab(session.id, bundle2.app.id), appSlug: bundle2.app.slug, prefix: prefix("").trim(), ownerSession: session.id });
32384
+ return await sendBrowser(root, bundle2.app.id, transport.browserContext, browserPage.pageOrigin, effect2, action.transport, transport, request2, markDispatched, selectedCredential, void 0, false, bundle2.receipt?.dispatchBefore, { tab: state.browserTab(session.id, bundle2.app.id), appSlug: bundle2.app.slug, prefix: prefix("").trim(), ownerSession: session.id, client: liveBrowser, timings, execution: browserExecution });
32250
32385
  } catch (error62) {
32251
32386
  if (error62 instanceof BrowserControlError && !error62.dispatched) {
32252
32387
  if (prior) throw new BrowserControlError("The mutation was already dispatched; its browser readback could not run. Inspect app state instead of rerunning the mutation", error62.code, true);
@@ -32270,9 +32405,9 @@ Inspect current action documentation: ${prefix(`actions show ${appName} ${action
32270
32405
  }
32271
32406
  }
32272
32407
  markDispatched();
32273
- return send(fetcher, request2);
32408
+ return timed(timings, "appMs", () => send(fetcher, request2));
32274
32409
  };
32275
- const reportId = randomUUID6();
32410
+ const reportId = randomUUID7();
32276
32411
  let outcome = "network-error";
32277
32412
  let diagnostic;
32278
32413
  let output2;
@@ -32286,7 +32421,7 @@ Inspect current action documentation: ${prefix(`actions show ${appName} ${action
32286
32421
  const value = projected(primary.body, action, generated);
32287
32422
  if (action.effect !== "read" && action.proof) {
32288
32423
  const preparedProof = prepareRequest(action.proof.request, transport, inputs.values, generated, value);
32289
- const proof = await sendRequest(preparedProof, "read");
32424
+ const proof = await timed(timings, "proofMs", () => sendRequest(preparedProof, "read"));
32290
32425
  let passed = false;
32291
32426
  try {
32292
32427
  passed = proofPassed(action.proof, proof.body, value, inputs.values, proof.response.status);
@@ -32324,36 +32459,47 @@ Inspect saved entries: ${prefix(`credentials list ${appName} ${action.transport}
32324
32459
  `, 5, !!dispatchedAt);
32325
32460
  }
32326
32461
  }
32462
+ if (browserExecution.scope) await liveBrowser?.closeScope(browserExecution.scope.id).catch(() => {
32463
+ });
32327
32464
  const report = { id: reportId, connectionFingerprint: await hashJson({ transport: transportVersion.executableHash, credential: selectedCredential ?? null, browser: browserPage?.pageOrigin ?? null }), ...bundle2.receipt ? { receiptId: bundle2.receipt.id } : {}, ...outcome === "success" && action.effect !== "read" ? { proof: "passed" } : {}, appId: bundle2.app.id, actionName, actionVersion: actionVersion.version, closureHash: bundle2.closure.hash, purpose: "run", clientFamily: "cli", agent: session.agent, outcome, ...diagnostic ? { diagnostic } : {}, startedAt, ...dispatchedAt ? { dispatchedAt } : {}, finishedAt: (/* @__PURE__ */ new Date()).toISOString() };
32328
- state.queueExecutionReport(reportId, report);
32329
- onReport?.(reportId);
32330
32465
  try {
32331
- const pending = state.pendingExecutionReports();
32332
- if (pending.length) {
32333
- const acknowledgment = await api.call("postExecutions", { body: { reports: pending.map((item) => item.report) } });
32334
- state.acknowledgeExecutionReports(acknowledgment.results.map((item) => item.id));
32335
- }
32466
+ state.queueExecutionReport(reportId, report);
32467
+ onReport?.(reportId);
32336
32468
  } catch {
32337
32469
  }
32470
+ await timed(timings, "reportingMs", () => flushExecutionReports(api, state, 100));
32338
32471
  if (output2.exitCode === 0 && (invocation.repeats.select?.length || invocation.options.output)) {
32472
+ const completeResult = output2.stdout;
32339
32473
  try {
32340
- const full = JSON.parse(output2.stdout);
32474
+ writeOutput(recoveryPath, completeResult);
32475
+ const full = JSON.parse(completeResult);
32341
32476
  const selected = invocation.repeats.select?.length ? selectPointers(full, invocation.repeats.select) : full;
32342
32477
  if (typeof selected === "object" && selected !== null && !Array.isArray(selected) && typeof full === "object" && full !== null && "nextCursor" in full && !("nextCursor" in selected)) selected.nextCursor = full.nextCursor;
32343
32478
  const serialized = `${JSON.stringify(selected, null, 2)}
32344
32479
  `;
32345
32480
  if (invocation.options.output) {
32346
- writeOutput(invocation.options.output, serialized);
32347
- output2 = result(`Saved the fetched result to ${invocation.options.output}. It contains only this invocation's page.
32481
+ writeOutput(outputPath, serialized);
32482
+ output2 = result(`Saved the fetched result to ${outputPath}.${typeof full === "object" && full !== null && "nextCursor" in full ? " It contains only this invocation's page." : ""}
32348
32483
  `);
32349
32484
  } else output2 = result(serialized, true);
32485
+ try {
32486
+ unlinkSync3(recoveryPath);
32487
+ } catch {
32488
+ }
32350
32489
  } catch (error62) {
32351
- output2 = fail2(`output-failed \u2014 The app request completed, but the result could not be saved or selected: ${error62 instanceof Error ? error62.message : "unknown error"}. Do not repeat a mutation to recover output.
32352
- `, 12, true);
32490
+ let saved = false;
32491
+ try {
32492
+ saved = readFileSync5(recoveryPath, "utf8") === completeResult;
32493
+ } catch {
32494
+ }
32495
+ output2 = { ...fail2(`output-failed \u2014 The app request completed, but the result could not be saved or selected: ${error62 instanceof Error ? error62.message : "unknown error"}. ${saved ? `Complete result: ${recoveryPath}. Read that local JSON file to recover the returned identifiers` : "The complete result is on stdout"}. Do not repeat the app mutation.
32496
+ `, 12, true), ...saved ? {} : { stdout: completeResult, stdoutJson: true } };
32353
32497
  }
32354
32498
  }
32355
32499
  if (bundle2.selection.fallback && !["prefer-verified", "pinned-selection"].includes(bundle2.selection.replaced?.reason ?? "")) output2.stderr += `Using a previously working revision (${bundle2.selection.replaced?.reason ?? "compatible-fallback"}). Selection recovers automatically when an eligible revision is available.${bundle2.selection.replaced?.until ? ` Protective pause ends ${bundle2.selection.replaced.until}.` : ""} Inspect distribution with ${prefix(`apps history ${appName}`)}.
32356
32500
  `;
32501
+ output2.dispatched = Boolean(dispatchedAt);
32502
+ saveTimings(timings, performance.now() - commandStarted);
32357
32503
  return output2;
32358
32504
  }
32359
32505
  async function executeDraftVerification(input2) {
@@ -32365,7 +32511,7 @@ async function executeDraftVerification(input2) {
32365
32511
  const schema = input2.kind === "action" ? action.inputs ?? { type: "object", properties: {}, additionalProperties: false } : probe.inputs ?? { type: "object", properties: {}, additionalProperties: false };
32366
32512
  let values;
32367
32513
  let prepared;
32368
- const generated = input2.kind === "action" && action && "generated" in action ? Object.fromEntries(Object.entries(action.generated ?? {}).map(([name, spec]) => [name, `${spec.prefix ?? ""}${randomUUID6()}`])) : {};
32514
+ const generated = input2.kind === "action" && action && "generated" in action ? Object.fromEntries(Object.entries(action.generated ?? {}).map(([name, spec]) => [name, `${spec.prefix ?? ""}${randomUUID7()}`])) : {};
32369
32515
  try {
32370
32516
  values = effectiveInputs(schema, input2.sampleInputs, {}, true).values;
32371
32517
  const request2 = input2.kind === "action" ? action.request : probe.request;
@@ -32388,6 +32534,8 @@ async function executeDraftVerification(input2) {
32388
32534
  let dispatched = false;
32389
32535
  let dispatchStarted = 0;
32390
32536
  let dispatchAt = null;
32537
+ const browserExecution = {};
32538
+ let liveBrowser;
32391
32539
  const sendConfigured = async (request2, effect2) => {
32392
32540
  const allowNonJsonProbe = input2.kind === "transport" && probe?.response.schema === void 0 && probe?.response.path === void 0;
32393
32541
  let credentialDraftId;
@@ -32399,7 +32547,7 @@ async function executeDraftVerification(input2) {
32399
32547
  const installation = input2.state.installation();
32400
32548
  const store = new CredentialStore(input2.root, new KeyringVault());
32401
32549
  try {
32402
- credentialDraftId = randomUUID6();
32550
+ credentialDraftId = randomUUID7();
32403
32551
  store.registerDraft({ workspaceId: installation.workspaceId, appId: input2.appId, appTitle: input2.appSlug, transportName: input2.name, requirements: transport.credentials ?? [], credentialContract: credentialContractKey(transport), destinations: transport.allowedOrigins, draftId: credentialDraftId, expiresAt: new Date(Date.now() + 30 * 6e4).toISOString(), source: { draftId: selected.id, sessionId: input2.session.id, fingerprint: current.fingerprint } });
32404
32552
  } finally {
32405
32553
  store.close();
@@ -32408,10 +32556,11 @@ async function executeDraftVerification(input2) {
32408
32556
  if (transport.mode === "browser") {
32409
32557
  const prior = dispatched;
32410
32558
  try {
32559
+ liveBrowser ??= await captureClient(input2.root);
32411
32560
  return await sendBrowser(input2.root, input2.appId, transport.browserContext, browserPage.pageOrigin, effect2, input2.kind === "action" && action && "transport" in action ? action.transport : input2.name, transport, request2, () => {
32412
32561
  if (!dispatched) input2.onDispatch?.();
32413
32562
  dispatched = true;
32414
- }, credential2, credentialDraftId, allowNonJsonProbe, void 0, { tab: input2.browserTab || input2.state.browserTab(input2.session.id, input2.appId), appSlug: input2.appSlug, prefix: input2.commandPrefix, ownerSession: input2.session.id });
32563
+ }, credential2, credentialDraftId, allowNonJsonProbe, void 0, { tab: input2.browserTab || input2.state.browserTab(input2.session.id, input2.appId), appSlug: input2.appSlug, prefix: input2.commandPrefix, ownerSession: input2.session.id, client: liveBrowser, execution: browserExecution });
32415
32564
  } catch (error62) {
32416
32565
  if (error62 instanceof BrowserControlError && !error62.dispatched) {
32417
32566
  if (prior) throw new BrowserControlError("The mutation was already dispatched; its browser readback could not run. Inspect app state instead of rerunning the mutation", error62.code, true);
@@ -32459,6 +32608,9 @@ async function executeDraftVerification(input2) {
32459
32608
  return { ok: true, output: value, proof: "not-applicable", dispatched: true };
32460
32609
  } catch (error62) {
32461
32610
  return { ok: false, message: `${(error62 instanceof Error ? error62.message : "The test did not return").replace(/[.\s]+$/u, "")}. ${dispatched ? input2.kind === "transport" || action && "effect" in action && action.effect === "read" ? "The read did not return complete evidence" : "The request may have reached the app; inspect its state before another mutation" : "No app request was sent"}`, exitCode: dispatched || error62 instanceof BrowserControlError || ["extension-unavailable", "capture-server-unavailable", "native-host-upgrade-required"].includes(error62?.code ?? "") ? 5 : 2, dispatched };
32611
+ } finally {
32612
+ if (browserExecution.scope) await liveBrowser?.closeScope(browserExecution.scope.id).catch(() => {
32613
+ });
32462
32614
  }
32463
32615
  }
32464
32616
  var import__3, import_ajv_formats, ajv3, result, fail2, ResponseContractFailure, omit2;
@@ -32467,6 +32619,9 @@ var init_run = __esm({
32467
32619
  "use strict";
32468
32620
  init_definitions();
32469
32621
  init_json();
32622
+ init_timings();
32623
+ init_files();
32624
+ init_execution_reports();
32470
32625
  init_drafts();
32471
32626
  init_credential_draft();
32472
32627
  import__3 = __toESM(require__(), 1);
@@ -32487,9 +32642,9 @@ var init_run = __esm({
32487
32642
  });
32488
32643
 
32489
32644
  // src/commands/catalog.ts
32490
- import { mkdtempSync, writeFileSync as writeFileSync5 } from "node:fs";
32645
+ import { mkdtempSync, writeFileSync as writeFileSync6 } from "node:fs";
32491
32646
  import { tmpdir } from "node:os";
32492
- import { isAbsolute as isAbsolute3, join as join9 } from "node:path";
32647
+ import { resolve as resolve5, join as join10 } from "node:path";
32493
32648
  function numberOption(value, fallback, max) {
32494
32649
  if (!value) return fallback;
32495
32650
  const number4 = Number(value);
@@ -32798,8 +32953,8 @@ Or address it explicitly: ${prefix("context set <name> <value> --app <app>")}
32798
32953
  `);
32799
32954
  if (search && !second?.trim()) return fail3("Enter an action name or title to search.\n");
32800
32955
  if (!search && invocation.flags.includes("all")) {
32801
- const directory = mkdtempSync(join9(tmpdir(), "apilift-action-index-"));
32802
- const file2 = join9(directory, "actions.txt");
32956
+ const directory = mkdtempSync(join10(tmpdir(), "apilift-action-index-"));
32957
+ const file2 = join10(directory, "actions.txt");
32803
32958
  const rows = [];
32804
32959
  let cursor;
32805
32960
  let complete = false;
@@ -32815,7 +32970,7 @@ Or address it explicitly: ${prefix("context set <name> <value> --app <app>")}
32815
32970
  complete = true;
32816
32971
  } catch {
32817
32972
  }
32818
- writeFileSync5(file2, `${rows.join("\n")}${rows.length ? "\n" : ""}`, { mode: 384 });
32973
+ writeFileSync6(file2, `${rows.join("\n")}${rows.length ? "\n" : ""}`, { mode: 384 });
32819
32974
  return complete ? ok2(`Saved the complete accessible action index (${rows.length} actions) to ${file2}.
32820
32975
  Search this file with ordinary file tools. No app request was sent.
32821
32976
  `) : { stdout: "", stderr: `Action index collection is incomplete. ${rows.length} actions were saved to ${file2}.
@@ -32887,9 +33042,8 @@ If the interface needs an operation: ${prefix(`apps update ${appName}`)}
32887
33042
  if (invocation.flags.includes("schema")) return ok2(`${JSON.stringify(docs.map((doc) => ({ name: doc.name, inputs: doc.inputs, output: doc.output ?? null })), null, 2)}
32888
33043
  `, true);
32889
33044
  if (invocation.options["input-template"]) {
32890
- const path2 = invocation.options["input-template"];
32891
- if (!isAbsolute3(path2)) return fail3("--input-template needs an absolute file path.\n");
32892
- writeFileSync5(path2, inputTemplate(docs[0].inputs), { flag: "wx", mode: 384 });
33045
+ const path2 = resolve5(invocation.options["input-template"]);
33046
+ writeFileSync6(path2, inputTemplate(docs[0].inputs), { flag: "wx", mode: 384 });
32893
33047
  return ok2(`Wrote an editable input template to ${path2}. Replace examples with real values before running:
32894
33048
  ${prefix(`run ${appName} ${names2[0]} --input @${path2} --check`)}
32895
33049
  `);
@@ -32922,7 +33076,7 @@ If the app has gained this operation, investigate: ${prefix(`apps update ${appNa
32922
33076
  ${prefix(`run ${appName} ${doc.name}${requiredFlags.replace(` --${contextName.replaceAll("_", "-")} <chosen-value>`, "")}`)}
32923
33077
 
32924
33078
  ` : ""}Other ways to supply inputs and handle results:
32925
- ${inputExample(properties)} --input @/absolute/input.json reads JSON/JSONC; --input - reads JSON from stdin.
33079
+ ${inputExample(properties)} --input @./input.json reads JSON/JSONC; --input - reads JSON from stdin.
32926
33080
  Do not supply the same input in both JSON and flags. Booleans take true/false;
32927
33081
  null is a value only when the displayed schema allows it, not a request to omit.
32928
33082
  --check prints effective inputs and their sources without sending an app request.
@@ -32945,10 +33099,10 @@ ${outputLines(doc.output)}
32945
33099
  ${requiredFlags ? "Run after choosing real required values:" : "Run:"}
32946
33100
  ${prefix(`run ${appName} ${doc.name}${requiredFlags}`)}
32947
33101
 
32948
- ${inputGuidance} --output /absolute/result.json saves the fetched result${Object.hasOwn(outputProperties, "nextCursor") ? ", not unfetched pages" : ""}.
33102
+ ${inputGuidance} --output ./result.json saves the fetched result${Object.hasOwn(outputProperties, "nextCursor") ? ", not unfetched pages" : ""}.
32949
33103
  ${selection}
32950
33104
  ${hasInputs ? `Generate an editable input file, including types and field instructions:
32951
- ${prefix(`actions show ${appName} ${doc.name} --input-template /absolute/input.jsonc`)}
33105
+ ${prefix(`actions show ${appName} ${doc.name} --input-template ./input.jsonc`)}
32952
33106
  ` : ""}Read the machine-readable input and output contracts:
32953
33107
  ${prefix(`actions show ${appName} ${doc.name} --schema`)}
32954
33108
 
@@ -32974,7 +33128,7 @@ var init_catalog = __esm({
32974
33128
  });
32975
33129
 
32976
33130
  // src/commands/credentials.ts
32977
- import { randomUUID as randomUUID7 } from "node:crypto";
33131
+ import { randomUUID as randomUUID8 } from "node:crypto";
32978
33132
  async function app(api, ref) {
32979
33133
  return api.call("getApp", { params: { app: ref } });
32980
33134
  }
@@ -33095,7 +33249,7 @@ List current entries: ${command3(invocation, `credentials list ${selectedApp.slu
33095
33249
  ${current.instructions}
33096
33250
  `);
33097
33251
  const expiresAt = new Date(Date.now() + 30 * 6e4).toISOString();
33098
- const draftId = ownedDraft && draftContract ? randomUUID7() : void 0;
33252
+ const draftId = ownedDraft && draftContract ? randomUUID8() : void 0;
33099
33253
  if (draftId && ownedDraft && draftContract) store.registerDraft({ ...current, draftId, expiresAt, source: { draftId: ownedDraft.id, sessionId: session.id, fingerprint: draftContract.fingerprint } });
33100
33254
  const link = { ...draftId ? { draftId } : {}, appId: selectedApp.id, appTitle: selectedApp.title, transportName, instructions: current.instructions, requirements: current.requirements, sessionId: session.id, expiresAt };
33101
33255
  const url3 = `${siteUrl2}/dashboard/credentials/setup#${Buffer.from(canonicalJson(link)).toString("base64url")}`;
@@ -33294,6 +33448,59 @@ var init_capture_derivation = __esm({
33294
33448
  }
33295
33449
  });
33296
33450
 
33451
+ // src/capture-server/windows-owner.ts
33452
+ import { Worker } from "node:worker_threads";
33453
+ function windowsProcessStart(pid) {
33454
+ if (!worker) {
33455
+ memory = new SharedArrayBuffer(24);
33456
+ worker = new Worker(workerSource, { eval: true, workerData: { memory, script } });
33457
+ worker.on("error", () => {
33458
+ });
33459
+ worker.unref();
33460
+ }
33461
+ const state = new Int32Array(memory, 0, 4), value = new Float64Array(memory, 16, 1);
33462
+ const id2 = ++sequence;
33463
+ Atomics.store(state, 1, id2);
33464
+ Atomics.store(state, 0, 1);
33465
+ worker.postMessage({ id: id2, pid });
33466
+ Atomics.wait(state, 0, 1, 4e3);
33467
+ if (Atomics.load(state, 0) === 2 && value[0] > 0) return value[0];
33468
+ if (Atomics.load(state, 0) !== 2) {
33469
+ void worker.terminate();
33470
+ worker = void 0;
33471
+ }
33472
+ return null;
33473
+ }
33474
+ var script, workerSource, worker, memory, sequence;
33475
+ var init_windows_owner = __esm({
33476
+ "src/capture-server/windows-owner.ts"() {
33477
+ "use strict";
33478
+ 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() }`;
33479
+ workerSource = `
33480
+ const {workerData}=require('node:worker_threads');
33481
+ const {spawn}=require('node:child_process');
33482
+ const state=new Int32Array(workerData.memory,0,4), value=new Float64Array(workerData.memory,16,1);
33483
+ const child=spawn('powershell.exe',['-NoLogo','-NoProfile','-NonInteractive','-Command',workerData.script],{windowsHide:true,stdio:['pipe','pipe','ignore']});
33484
+ const fail=()=>{Atomics.store(state,0,3);Atomics.notify(state,0);};
33485
+ child.on('error',fail);child.on('exit',fail);
33486
+ let text='';
33487
+ child.stdout.on('data',chunk=>{
33488
+ text+=chunk.toString();
33489
+ let end;
33490
+ while((end=text.indexOf('\\n'))>=0){
33491
+ const [id,stamp]=text.slice(0,end).trim().split('|').map(Number);text=text.slice(end+1);
33492
+ if(id!==Atomics.load(state,1)||Atomics.load(state,0)!==1)continue;
33493
+ value[0]=Number.isFinite(stamp)?stamp:0;Atomics.store(state,0,2);Atomics.notify(state,0);
33494
+ }
33495
+ if(text.length>1024){child.kill();fail();}
33496
+ });
33497
+ const {parentPort}=require('node:worker_threads');
33498
+ parentPort.on('message',({id,pid})=>{try{child.stdin.write(id+'|'+pid+'\\n');}catch{fail();}});
33499
+ `;
33500
+ sequence = 0;
33501
+ }
33502
+ });
33503
+
33297
33504
  // ../../packages/protocol/dist/native.js
33298
33505
  function validateNativeFrameLength(byteLength, direction) {
33299
33506
  return Number.isInteger(byteLength) && byteLength >= 0 && byteLength <= (direction === "request" ? 512 * 1024 : 1024 * 1024);
@@ -33494,7 +33701,7 @@ var init_dist = __esm({
33494
33701
  });
33495
33702
 
33496
33703
  // src/capture-server/store.ts
33497
- import { mkdirSync as mkdirSync6, chmodSync as chmodSync6, readFileSync as readFileSync6 } from "node:fs";
33704
+ import { mkdirSync as mkdirSync7, chmodSync as chmodSync6, readFileSync as readFileSync6 } from "node:fs";
33498
33705
  import { execFileSync } from "node:child_process";
33499
33706
  import { DatabaseSync as DatabaseSync5 } from "node:sqlite";
33500
33707
  function createOwnerVerifier(probe = {}) {
@@ -33539,6 +33746,10 @@ function inspectOwner(pid, expectedStartedAt, probe) {
33539
33746
  return false;
33540
33747
  }
33541
33748
  }
33749
+ if (windows && !probe.inspect) {
33750
+ const observed = windowsProcessStart(pid);
33751
+ return observed !== null && Math.abs(observed - expectedStartedAt) <= 5e3;
33752
+ }
33542
33753
  for (let attempt = 0; attempt < (windows ? 2 : 1); attempt++) {
33543
33754
  if (!alive(pid)) return false;
33544
33755
  const remaining = windows ? 8e3 - (now() - started) : 2e3;
@@ -33557,6 +33768,7 @@ var pidAlive, inspectProcess, linuxClockTicks, defaultOwnerVerifier;
33557
33768
  var init_store = __esm({
33558
33769
  "src/capture-server/store.ts"() {
33559
33770
  "use strict";
33771
+ init_windows_owner();
33560
33772
  init_dist();
33561
33773
  pidAlive = (candidate2) => {
33562
33774
  try {
@@ -33573,17 +33785,17 @@ var init_store = __esm({
33573
33785
 
33574
33786
  // src/discovery/lock.ts
33575
33787
  import { createHash as createHash3 } from "node:crypto";
33576
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync7, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "node:fs";
33577
- import { join as join10 } from "node:path";
33788
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync7, rmSync as rmSync3, writeFileSync as writeFileSync7 } from "node:fs";
33789
+ import { join as join11 } from "node:path";
33578
33790
  function lockDiscovery(root, sessionId) {
33579
33791
  const name = createHash3("sha256").update(sessionId).digest("hex");
33580
- const directory = join10(root, "locks", `discovery-${name}`);
33581
- mkdirSync7(join10(root, "locks"), { recursive: true, mode: 448 });
33792
+ const directory = join11(root, "locks", `discovery-${name}`);
33793
+ mkdirSync8(join11(root, "locks"), { recursive: true, mode: 448 });
33582
33794
  const owner = { pid: process.pid, startedAt: Date.now() - process.uptime() * 1e3 };
33583
33795
  for (let attempt = 0; attempt < 2; attempt++) {
33584
33796
  try {
33585
- mkdirSync7(directory, { mode: 448 });
33586
- writeFileSync6(join10(directory, "owner.json"), JSON.stringify(owner), { flag: "wx", mode: 384 });
33797
+ mkdirSync8(directory, { mode: 448 });
33798
+ writeFileSync7(join11(directory, "owner.json"), JSON.stringify(owner), { flag: "wx", mode: 384 });
33587
33799
  return () => {
33588
33800
  rmSync3(directory, { recursive: true, force: true });
33589
33801
  };
@@ -33591,7 +33803,7 @@ function lockDiscovery(root, sessionId) {
33591
33803
  if (error62.code !== "EEXIST") throw error62;
33592
33804
  let held = null;
33593
33805
  try {
33594
- held = JSON.parse(readFileSync7(join10(directory, "owner.json"), "utf8"));
33806
+ held = JSON.parse(readFileSync7(join11(directory, "owner.json"), "utf8"));
33595
33807
  } catch {
33596
33808
  }
33597
33809
  if (!held || ownerProcessAlive(held.pid, held.startedAt)) throw new Error("discovery-command-busy \u2014 This agent already has a discovery command running; wait for it to finish, then retry");
@@ -33635,10 +33847,10 @@ var init_session = __esm({
33635
33847
  });
33636
33848
 
33637
33849
  // src/commands/discover.ts
33638
- import { existsSync as existsSync3, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync2, readFileSync as readFileSync8, renameSync as renameSync2, writeFileSync as writeFileSync7 } from "node:fs";
33639
- import { randomUUID as randomUUID8 } from "node:crypto";
33850
+ import { existsSync as existsSync4, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync2, readFileSync as readFileSync8, renameSync as renameSync2, writeFileSync as writeFileSync8 } from "node:fs";
33851
+ import { randomUUID as randomUUID9 } from "node:crypto";
33640
33852
  import { tmpdir as tmpdir2 } from "node:os";
33641
- import { dirname as dirname5, join as join11 } from "node:path";
33853
+ import { dirname as dirname6, join as join12 } from "node:path";
33642
33854
  async function currentResource(api, appId, kind, name) {
33643
33855
  try {
33644
33856
  const resource = await api.call("getResource", { params: { app: appId, kind: backendKind(kind), name } });
@@ -33842,7 +34054,7 @@ File: ${draft.filePath}
33842
34054
  const definition2 = resource.definition;
33843
34055
  const dependencies = kind === "action" && !("unavailable" in definition2) ? { transport: String(definition2.transport ?? "") } : kind === "transport" && definition2.browserContext ? { browserPage: String(definition2.browserContext) } : void 0;
33844
34056
  const editable = { definition: definition2, ...dependencies ? { dependencies } : {}, ...kind === "action" && !("unavailable" in definition2) || kind === "transport" ? { verification: { inputs: {}, review: "pending", notes: "" } } : {} };
33845
- writeFileSync7(draft.filePath, `${JSON.stringify(editable, null, 2)}
34057
+ writeFileSync8(draft.filePath, `${JSON.stringify(editable, null, 2)}
33846
34058
  `, { mode: 384 });
33847
34059
  }
33848
34060
  void root;
@@ -33858,7 +34070,7 @@ function researchTables(state) {
33858
34070
  }
33859
34071
  function researchPath(root, appSlug, scope2) {
33860
34072
  if (!/^(app|[a-z][a-z0-9_-]{0,79}\.[a-z][a-z0-9_-]{0,79})$/.test(scope2)) throw new Error("Research scope must be app or entity.verb, for example ticket.get");
33861
- return join11(root, "drafts", appSlug, "api", `${scope2}.jsonc`);
34073
+ return join12(root, "drafts", appSlug, "api", `${scope2}.jsonc`);
33862
34074
  }
33863
34075
  function handleResearch(invocation, state, store, session, root) {
33864
34076
  const current = requireDiscovery(store, session, invocation);
@@ -33873,8 +34085,8 @@ function handleResearch(invocation, state, store, session, root) {
33873
34085
  return fail5(`${error62 instanceof Error ? error62.message : "Invalid research scope"}.
33874
34086
  `);
33875
34087
  }
33876
- mkdirSync8(dirname5(path2), { recursive: true, mode: 448 });
33877
- if (!existsSync3(path2)) writeFileSync7(path2, `// Record a focused check of official operation and access documentation.
34088
+ mkdirSync9(dirname6(path2), { recursive: true, mode: 448 });
34089
+ if (!existsSync4(path2)) writeFileSync8(path2, `// Record a focused check of official operation and access documentation.
33878
34090
  ${JSON.stringify({ scope: scope2, conclusion: "pending", findings: "", sources: [{ kind: "operation", url: "", finding: "" }, { kind: "access", url: "", finding: "" }] }, null, 2)}
33879
34091
  `, { flag: "wx", mode: 384 });
33880
34092
  state.db.prepare("INSERT INTO discovery_research (app_slug,scope,file_path) VALUES (?,?,?) ON CONFLICT(app_slug,scope) DO NOTHING").run(current.appSlug, scope2, path2);
@@ -34331,15 +34543,15 @@ async function rebaseDraft(invocation, api, store, session) {
34331
34543
  if (current.version === draft.baseVersion) return ok4(`The published ${draft.kind} ${draft.name} is still at version ${current.version}; no rebase is needed.
34332
34544
  `);
34333
34545
  const comparisonPath = `${draft.filePath}.current.json`;
34334
- writeFileSync7(comparisonPath, `${JSON.stringify({ version: current.version, definition: current.definition }, null, 2)}
34546
+ writeFileSync8(comparisonPath, `${JSON.stringify({ version: current.version, definition: current.definition }, null, 2)}
34335
34547
  `, { mode: 384 });
34336
34548
  if (draft.intent === "remove") {
34337
34549
  const checked = await checkDraft(store, session.id, api);
34338
34550
  if (checked.issues.length) return checkResponse(checked, invocation);
34339
34551
  const source = readFileSync8(draft.filePath, "utf8");
34340
34552
  const updated = applyEdits(source, modify(source, ["expectedVersion"], current.version, { formattingOptions: { insertSpaces: true, tabSize: 2 } }));
34341
- const temporary = `${draft.filePath}.${randomUUID8()}.tmp`;
34342
- writeFileSync7(temporary, updated, { flag: "wx", mode: 384 });
34553
+ const temporary = `${draft.filePath}.${randomUUID9()}.tmp`;
34554
+ writeFileSync8(temporary, updated, { flag: "wx", mode: 384 });
34343
34555
  renameSync2(temporary, draft.filePath);
34344
34556
  store.setBaseVersion(draft.id, current.version);
34345
34557
  return ok4(`The published ${label(draft.kind)} ${draft.name} changed. The removal now targets version ${current.version}.
@@ -34403,9 +34615,9 @@ File: ${draft.filePath}
34403
34615
  }
34404
34616
  withBaselines(root, (baselines) => baselines.finishPreparation(session.id, draft.appSlug, draft.appId, transport.mode));
34405
34617
  if (write) store.saveAttempt(draft.id, identity2.fingerprint, "success");
34406
- const dir = mkdtempSync2(join11(tmpdir2(), "apilift-discover-"));
34407
- const resultPath = join11(dir, "verification.json");
34408
- writeFileSync7(resultPath, `${JSON.stringify(executed.output, null, 2)}
34618
+ const dir = mkdtempSync2(join12(tmpdir2(), "apilift-discover-"));
34619
+ const resultPath = join12(dir, "verification.json");
34620
+ writeFileSync8(resultPath, `${JSON.stringify(executed.output, null, 2)}
34409
34621
  `, { flag: "wx", mode: 384 });
34410
34622
  const prior = store.evidence(draft.id);
34411
34623
  const probes = draft.kind === "transport" ? [...((prior?.fingerprint === identity2.fingerprint ? prior.data.probes : []) ?? []).filter((item) => item.origin !== envelope.verification?.probeOrigin), { origin: envelope.verification.probeOrigin, verifiedAt: (/* @__PURE__ */ new Date()).toISOString() }] : void 0;
@@ -34459,7 +34671,7 @@ async function awaitCaptureJob(client, id2) {
34459
34671
  do {
34460
34672
  const job = await client.jobResult(id2);
34461
34673
  if (job.status === "delivered" || job.status === "uncertain") return job;
34462
- await new Promise((resolve6) => setTimeout(resolve6, 200));
34674
+ await new Promise((resolve10) => setTimeout(resolve10, 200));
34463
34675
  } while (Date.now() < deadline);
34464
34676
  return { status: "pending" };
34465
34677
  }
@@ -34531,7 +34743,7 @@ async function refreshCandidate(item, scopeId, root, deadline, waitForResponse)
34531
34743
  }
34532
34744
  if (latest) item = latest;
34533
34745
  if (!waitForResponse || !item.entry.pending) return item;
34534
- await new Promise((resolve6) => setTimeout(resolve6, 100));
34746
+ await new Promise((resolve10) => setTimeout(resolve10, 100));
34535
34747
  } while (Date.now() < deadline);
34536
34748
  return item;
34537
34749
  }
@@ -34555,7 +34767,7 @@ async function selectedBody(item, part, scopeId, root, deadline = Date.now() + 5
34555
34767
  if (bytes.length !== artifact.bytes) throw new Error("Selected body length does not match its artifact");
34556
34768
  return bytes.toString("utf8");
34557
34769
  }
34558
- await new Promise((resolve6) => setTimeout(resolve6, 100));
34770
+ await new Promise((resolve10) => setTimeout(resolve10, 100));
34559
34771
  } while (Date.now() < deadline);
34560
34772
  return null;
34561
34773
  }
@@ -34636,7 +34848,7 @@ function selectCapture(invocation, draft, source, item) {
34636
34848
  }
34637
34849
  }
34638
34850
  updated = applyEdits(updated, modify(updated, ["verification", "review"], "pending", { formattingOptions: { insertSpaces: true, tabSize: 2 } }));
34639
- writeFileSync7(draft.filePath, updated, { mode: 384 });
34851
+ writeFileSync8(draft.filePath, updated, { mode: 384 });
34640
34852
  const candidates = authFieldPointers(entry2);
34641
34853
  const authHint = candidates.length ? `Authentication field candidates (names only): ${candidates.join(", ")}. Probe bounded local/session storage without guessing a field or location: ${command4(invocation, `discover capture auth c${item.seq}`)}. Optionally narrow capture.authField and capture.authScope in the draft after reviewing the value-free result. A locator is only a hypothesis until a safe read verifies the selected connection.
34642
34854
  ` : browserCookiePresent(entry2) ? "Browser-managed cookie was present; browser replay sends cookies automatically. No source locator is needed for that cookie. Verify the selected connection with a safe read.\n" : "";
@@ -34712,7 +34924,7 @@ ${detail}
34712
34924
  const scope2 = await client.openScope({ kind: "capture", appId: draft.appId, contextId: transport.browserContext, pageOrigin: context.pageOrigin, tabSelector: tab.selector, ownerPid: client.receipt.pid, ownerStartedAt: Date.parse(client.receipt.startedAt), ownerSession: session.id });
34713
34925
  const previousEvidence = row?.evidence_scope_id ?? row?.scope_id;
34714
34926
  if (previousEvidence) await client.closeScope(previousEvidence);
34715
- const id2 = randomUUID8();
34927
+ const id2 = randomUUID9();
34716
34928
  state.db.prepare("INSERT INTO discovery_capture (draft_id,scope_id,job_id,phase,entries_json) VALUES (?,?,?,?,NULL) ON CONFLICT(draft_id) DO UPDATE SET scope_id=excluded.scope_id,job_id=excluded.job_id,phase=excluded.phase,entries_json=NULL,evidence_scope_id=excluded.scope_id").run(draft.id, scope2.id, id2, "starting");
34717
34929
  state.db.prepare("UPDATE discovery_capture SET evidence_scope_id=? WHERE draft_id=?").run(scope2.id, draft.id);
34718
34930
  await client.enqueueJob(scope2.id, { job: { id: id2, kind: "begin", label: draft.name, entity: nameParts[0], operation: nameParts[1], effect: effect2, inputs: envelope.verification.inputs } });
@@ -34763,7 +34975,7 @@ Review: ${command4(invocation, "discover status")}
34763
34975
  `, 12);
34764
34976
  }
34765
34977
  client = await captureClient(root);
34766
- let stopId = row.phase === "stopping" && row.job_id ? row.job_id : randomUUID8();
34978
+ let stopId = row.phase === "stopping" && row.job_id ? row.job_id : randomUUID9();
34767
34979
  if (row.phase !== "stopping") {
34768
34980
  state.db.prepare("UPDATE discovery_capture SET phase='stopping',job_id=? WHERE draft_id=?").run(stopId, draft.id);
34769
34981
  await client.enqueueJob(row.scope_id, { job: { id: stopId, kind: "stop" } });
@@ -34771,7 +34983,7 @@ Review: ${command4(invocation, "discover status")}
34771
34983
  const previous = await client.jobResult(stopId);
34772
34984
  if (previous.status === "delivered" && previous.result?.boundaryConfirmed !== true) {
34773
34985
  const failedId = stopId;
34774
- stopId = randomUUID8();
34986
+ stopId = randomUUID9();
34775
34987
  state.db.prepare("UPDATE discovery_capture SET job_id=? WHERE draft_id=?").run(stopId, draft.id);
34776
34988
  await client.enqueueJob(row.scope_id, { job: { id: stopId, kind: "stop" } });
34777
34989
  await client.acknowledgeJob(failedId);
@@ -34866,14 +35078,14 @@ Inspect a returned candidate: ${command4(invocation, `discover capture show c${p
34866
35078
  const [location, keyName] = capture?.authScope?.split(":", 2) ?? [];
34867
35079
  if (location && !["localStorage", "sessionStorage", "global", "cookie", "meta"].includes(location)) return fail5(`capture.authScope must be localStorage, sessionStorage, global, cookie or meta, optionally followed by :<key>. Edit ${draft.filePath}.
34868
35080
  `);
34869
- const client = await captureClient(root), id2 = randomUUID8();
35081
+ const client = await captureClient(root), id2 = randomUUID9();
34870
35082
  try {
34871
35083
  await client.enqueueJob(row.scope_id, { job: { id: id2, kind: "auth-probe", captureId: String(item.entry.captureId), ...capture?.authField ? { field: capture.authField } : {}, ...location ? { scope: { in: location, ...keyName ? { key: keyName } : {} } } : {} } });
34872
35084
  const result3 = await awaitCaptureJob(client, id2);
34873
35085
  if (result3.status !== "delivered") return fail5(`Auth source probe is ${result3.status}; no source was confirmed. Retry only this probe while evidence is live.
34874
35086
  `, 12);
34875
- const path2 = join11(mkdtempSync2(join11(tmpdir2(), "apilift-auth-source-")), "source.json");
34876
- writeFileSync7(path2, `${JSON.stringify(result3.result, null, 2)}
35087
+ const path2 = join12(mkdtempSync2(join12(tmpdir2(), "apilift-auth-source-")), "source.json");
35088
+ writeFileSync8(path2, `${JSON.stringify(result3.result, null, 2)}
34877
35089
  `, { mode: 384 });
34878
35090
  await client.acknowledgeJob(id2);
34879
35091
  const connection = parsed.envelope?.dependencies?.transport;
@@ -34928,13 +35140,13 @@ var init_discover = __esm({
34928
35140
  });
34929
35141
 
34930
35142
  // src/output.ts
34931
- import { randomUUID as randomUUID9 } from "node:crypto";
35143
+ import { randomUUID as randomUUID10 } from "node:crypto";
34932
35144
  import { chmod, mkdtemp, open as open3, rename } from "node:fs/promises";
34933
35145
  import { tmpdir as tmpdir3 } from "node:os";
34934
- import { join as join12 } from "node:path";
35146
+ import { join as join13 } from "node:path";
34935
35147
  async function completedFile(directory, name, content) {
34936
- const target = join12(directory, name);
34937
- const pending = join12(directory, `.${name}-${randomUUID9()}`);
35148
+ const target = join13(directory, name);
35149
+ const pending = join13(directory, `.${name}-${randomUUID10()}`);
34938
35150
  const handle2 = await open3(pending, "wx", 384);
34939
35151
  try {
34940
35152
  await handle2.writeFile(content, "utf8");
@@ -34952,7 +35164,7 @@ async function finalizeOutput(output2, sink, directory = tmpdir3()) {
34952
35164
  const size = () => Buffer.byteLength(stdout) + Buffer.byteLength(stderr);
34953
35165
  try {
34954
35166
  if (size() > displayBudget) {
34955
- const root = await mkdtemp(join12(directory, "apilift-output-"));
35167
+ const root = await mkdtemp(join13(directory, "apilift-output-"));
34956
35168
  await chmod(root, 448);
34957
35169
  const streams = ["stdout", "stderr"];
34958
35170
  streams.sort((left, right) => Buffer.byteLength(output2[right]) - Buffer.byteLength(output2[left]));
@@ -34992,12 +35204,12 @@ var init_output = __esm({
34992
35204
  // ../../node_modules/.pnpm/tinyexec@1.3.1/node_modules/tinyexec/dist/main.mjs
34993
35205
  import { spawn as spawn2, spawnSync } from "node:child_process";
34994
35206
  import { cwd } from "node:process";
34995
- import { basename, delimiter, dirname as dirname6, normalize, resolve as resolve2 } from "node:path";
35207
+ import { basename, delimiter, dirname as dirname7, normalize, resolve as resolve6 } from "node:path";
34996
35208
  import { fileURLToPath } from "node:url";
34997
35209
  import { pipeline } from "node:stream/promises";
34998
35210
  import { PassThrough } from "node:stream";
34999
35211
  import readline from "node:readline";
35000
- import { closeSync, openSync, readSync, statSync } from "node:fs";
35212
+ import { closeSync, openSync, readSync, statSync as statSync2 } from "node:fs";
35001
35213
  function getPathFromEnv(env) {
35002
35214
  for (const key in env) {
35003
35215
  if (!Object.prototype.hasOwnProperty.call(env, key) || !isPathLikePattern.test(key)) continue;
@@ -35016,11 +35228,11 @@ function addNodeBinToPath(cwd2, path2) {
35016
35228
  let currentPath = typeof cwd2 === "string" ? cwd2 : fileURLToPath(cwd2, { windows: false });
35017
35229
  let lastPath;
35018
35230
  do {
35019
- nodeBinPaths.push(resolve2(currentPath, "node_modules", ".bin"));
35231
+ nodeBinPaths.push(resolve6(currentPath, "node_modules", ".bin"));
35020
35232
  lastPath = currentPath;
35021
- currentPath = dirname6(currentPath);
35233
+ currentPath = dirname7(currentPath);
35022
35234
  } while (currentPath !== lastPath);
35023
- nodeBinPaths.push(dirname6(process.execPath));
35235
+ nodeBinPaths.push(dirname7(process.execPath));
35024
35236
  const newPath = nodeBinPaths.concat(parts).join(delimiter);
35025
35237
  return {
35026
35238
  key: path2.key,
@@ -35110,11 +35322,11 @@ function resolveCommand(command6, options2) {
35110
35322
  if (command6.includes(".") && pathExt[0] !== "") pathExt = ["", ...pathExt];
35111
35323
  for (const extensions of [pathExt, noPathExt]) for (const path2 of pathEnv) {
35112
35324
  const unquoted = path2.startsWith('"') && path2.endsWith('"') && path2.length > 1 ? path2.slice(1, -1) : path2;
35113
- const dest = resolve2(cwd$3, unquoted, command6);
35325
+ const dest = resolve6(cwd$3, unquoted, command6);
35114
35326
  for (const ext of extensions) {
35115
35327
  const destWithExt = dest + ext;
35116
35328
  try {
35117
- if (statSync(destWithExt).isFile()) return destWithExt;
35329
+ if (statSync2(destWithExt).isFile()) return destWithExt;
35118
35330
  } catch {
35119
35331
  }
35120
35332
  }
@@ -35280,8 +35492,8 @@ var init_main2 = __esm({
35280
35492
  };
35281
35493
  this._command = command6;
35282
35494
  this._args = args ?? [];
35283
- this._processClosed = new Promise((resolve6) => {
35284
- this._resolveClose = resolve6;
35495
+ this._processClosed = new Promise((resolve10) => {
35496
+ this._resolveClose = resolve10;
35285
35497
  });
35286
35498
  }
35287
35499
  kill(signal) {
@@ -35364,8 +35576,8 @@ var init_main2 = __esm({
35364
35576
  }
35365
35577
  _resetState() {
35366
35578
  this._aborted = false;
35367
- this._processClosed = new Promise((resolve6) => {
35368
- this._resolveClose = resolve6;
35579
+ this._processClosed = new Promise((resolve10) => {
35580
+ this._resolveClose = resolve10;
35369
35581
  });
35370
35582
  this._thrownError = void 0;
35371
35583
  }
@@ -35391,30 +35603,30 @@ var init_main2 = __esm({
35391
35603
 
35392
35604
  // src/plugin.ts
35393
35605
  import { createHash as createHash5 } from "node:crypto";
35394
- import { cpSync, existsSync as existsSync4, mkdirSync as mkdirSync9, readFileSync as readFileSync9, readdirSync, renameSync as renameSync3, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "node:fs";
35606
+ import { cpSync, existsSync as existsSync5, mkdirSync as mkdirSync10, readFileSync as readFileSync9, readdirSync, renameSync as renameSync3, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "node:fs";
35395
35607
  import { homedir as homedir2 } from "node:os";
35396
- import { dirname as dirname7, join as join13, resolve as resolve3 } from "node:path";
35608
+ import { dirname as dirname8, join as join14, resolve as resolve7 } from "node:path";
35397
35609
  import { fileURLToPath as fileURLToPath2 } from "node:url";
35398
35610
  function missingSkill(root) {
35399
- return skillNames.find((name) => !existsSync4(join13(root, "skills", name, "SKILL.md")));
35611
+ return skillNames.find((name) => !existsSync5(join14(root, "skills", name, "SKILL.md")));
35400
35612
  }
35401
35613
  function completeCopy(source, target) {
35402
- return files(source).every((path2) => existsSync4(join13(target, path2)));
35614
+ return files(source).every((path2) => existsSync5(join14(target, path2)));
35403
35615
  }
35404
35616
  function files(root, relative = "") {
35405
- return readdirSync(join13(root, relative), { withFileTypes: true }).flatMap((entry2) => {
35406
- const path2 = join13(relative, entry2.name);
35617
+ return readdirSync(join14(root, relative), { withFileTypes: true }).flatMap((entry2) => {
35618
+ const path2 = join14(relative, entry2.name);
35407
35619
  return entry2.isDirectory() ? files(root, path2) : [path2];
35408
35620
  }).sort();
35409
35621
  }
35410
35622
  function digest(root) {
35411
35623
  const hash4 = createHash5("sha256");
35412
- for (const path2 of files(root)) hash4.update(path2).update("\0").update(readFileSync9(join13(root, path2))).update("\0");
35624
+ for (const path2 of files(root)) hash4.update(path2).update("\0").update(readFileSync9(join14(root, path2))).update("\0");
35413
35625
  return hash4.digest("hex");
35414
35626
  }
35415
35627
  function writeJson(path2, value) {
35416
- mkdirSync9(dirname7(path2), { recursive: true });
35417
- writeFileSync8(path2, `${JSON.stringify(value, null, 2)}
35628
+ mkdirSync10(dirname8(path2), { recursive: true });
35629
+ writeFileSync9(path2, `${JSON.stringify(value, null, 2)}
35418
35630
  `, { mode: 384 });
35419
35631
  }
35420
35632
  function readJson(path2) {
@@ -35452,16 +35664,16 @@ function active(agent2, expected) {
35452
35664
  }
35453
35665
  function inspectAgentPlugin(agent2, env, sourceOverride) {
35454
35666
  if (env.APILIFT_DEV_PLUGIN_ROOT) return { ready: false, version: "development", detail: "Development plugin activation has not been verified" };
35455
- const source = resolve3(sourceOverride || join13(dirname7(fileURLToPath2(import.meta.url)), "plugin"));
35667
+ const source = resolve7(sourceOverride || join14(dirname8(fileURLToPath2(import.meta.url)), "plugin"));
35456
35668
  const missing = missingSkill(source);
35457
35669
  if (missing) return { ready: false, version: "unknown", detail: `CLI package is missing its ${missing} skill` };
35458
- const version2 = readJson(join13(source, `.${agent2}-plugin`, "plugin.json"))?.version;
35670
+ const version2 = readJson(join14(source, `.${agent2}-plugin`, "plugin.json"))?.version;
35459
35671
  if (typeof version2 !== "string" || !/^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/.test(version2)) throw new Error("Plugin package version is invalid");
35460
35672
  const bundleDigest = digest(source);
35461
35673
  const expected = agent2 === "codex" ? `${version2}+codex.${bundleDigest.slice(0, 12)}` : version2;
35462
- const home = resolve3(env.APILIFT_AGENT_HOME || homedir2());
35463
- const plugin = agent2 === "codex" ? join13(home, "plugins", "apilift") : join13(home, ".agents", "plugins", "plugins", "apilift");
35464
- const marker = readJson(join13(plugin, markerName));
35674
+ const home = resolve7(env.APILIFT_AGENT_HOME || homedir2());
35675
+ const plugin = agent2 === "codex" ? join14(home, "plugins", "apilift") : join14(home, ".agents", "plugins", "plugins", "apilift");
35676
+ const marker = readJson(join14(plugin, markerName));
35465
35677
  const staged = marker?.package === "apilift" && marker.bundleDigest === bundleDigest && marker.version === version2 && completeCopy(source, plugin);
35466
35678
  if (!staged) return { ready: false, version: expected, detail: "Apilift plugin files are missing or stale" };
35467
35679
  if (env.APILIFT_AGENT_HOME || env.APILIFT_AGENT_NO_ACTIVATE === "1") return { ready: false, version: expected, detail: "Apilift skills are staged; activation is unverified" };
@@ -35470,54 +35682,54 @@ function inspectAgentPlugin(agent2, env, sourceOverride) {
35470
35682
  }
35471
35683
  function ensureAgentPlugin(agent2, env, sourceOverride) {
35472
35684
  if (env.APILIFT_DEV_PLUGIN_ROOT) {
35473
- const source2 = resolve3(env.APILIFT_DEV_PLUGIN_ROOT);
35685
+ const source2 = resolve7(env.APILIFT_DEV_PLUGIN_ROOT);
35474
35686
  const missing2 = missingSkill(source2);
35475
35687
  if (missing2) throw new Error(`Development plugin ${missing2} skill is missing`);
35476
35688
  const bundleDigest2 = digest(source2);
35477
- const root = resolve3(env.APILIFT_HOME || join13(homedir2(), ".local", "share", "apilift"));
35478
- writeJson(join13(root, "agent-plugins", `${agent2}.json`), { root: source2, digest: bundleDigest2 });
35689
+ const root = resolve7(env.APILIFT_HOME || join14(homedir2(), ".local", "share", "apilift"));
35690
+ writeJson(join14(root, "agent-plugins", `${agent2}.json`), { root: source2, digest: bundleDigest2 });
35479
35691
  return { ready: false, version: `dev+${bundleDigest2.slice(0, 12)}`, detail: "Development plugin source recorded; activation was not verified" };
35480
35692
  }
35481
- const source = resolve3(sourceOverride || join13(dirname7(fileURLToPath2(import.meta.url)), "plugin"));
35693
+ const source = resolve7(sourceOverride || join14(dirname8(fileURLToPath2(import.meta.url)), "plugin"));
35482
35694
  const missing = missingSkill(source);
35483
35695
  if (missing) throw new Error(`CLI package is missing its ${missing} skill`);
35484
- const version2 = readJson(join13(source, `.${agent2}-plugin`, "plugin.json"))?.version;
35696
+ const version2 = readJson(join14(source, `.${agent2}-plugin`, "plugin.json"))?.version;
35485
35697
  if (typeof version2 !== "string" || !/^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/.test(version2)) throw new Error("Plugin package version is invalid");
35486
35698
  const bundleDigest = digest(source);
35487
- const home = resolve3(env.APILIFT_AGENT_HOME || homedir2());
35488
- const marketplace = join13(home, ".agents", "plugins");
35489
- const plugin = agent2 === "codex" ? join13(home, "plugins", "apilift") : join13(marketplace, "plugins", "apilift");
35490
- const marker = readJson(join13(plugin, markerName));
35491
- if (existsSync4(plugin) && marker?.package !== "apilift") throw new Error(`Refusing to replace unmanaged plugin ${plugin}`);
35699
+ const home = resolve7(env.APILIFT_AGENT_HOME || homedir2());
35700
+ const marketplace = join14(home, ".agents", "plugins");
35701
+ const plugin = agent2 === "codex" ? join14(home, "plugins", "apilift") : join14(marketplace, "plugins", "apilift");
35702
+ const marker = readJson(join14(plugin, markerName));
35703
+ if (existsSync5(plugin) && marker?.package !== "apilift") throw new Error(`Refusing to replace unmanaged plugin ${plugin}`);
35492
35704
  const expected = agent2 === "codex" ? `${version2}+codex.${bundleDigest.slice(0, 12)}` : version2;
35493
35705
  const current = marker?.bundleDigest === bundleDigest && marker.version === version2 && completeCopy(source, plugin);
35494
35706
  if (!current) {
35495
35707
  const staging = `${plugin}.stage-${process.pid}`;
35496
35708
  rmSync4(staging, { recursive: true, force: true });
35497
- mkdirSync9(dirname7(staging), { recursive: true });
35709
+ mkdirSync10(dirname8(staging), { recursive: true });
35498
35710
  cpSync(source, staging, { recursive: true });
35499
- const manifestPath = join13(staging, `.${agent2}-plugin`, "plugin.json");
35711
+ const manifestPath = join14(staging, `.${agent2}-plugin`, "plugin.json");
35500
35712
  const manifest = readJson(manifestPath);
35501
35713
  if (!manifest) throw new Error("Plugin manifest is invalid");
35502
35714
  manifest.version = expected;
35503
35715
  writeJson(manifestPath, manifest);
35504
- writeJson(join13(staging, markerName), { schema: "apilift.plugin-install/v1", package: "apilift", version: version2, bundleDigest });
35716
+ writeJson(join14(staging, markerName), { schema: "apilift.plugin-install/v1", package: "apilift", version: version2, bundleDigest });
35505
35717
  const backup = `${plugin}.backup-${process.pid}`;
35506
- if (existsSync4(plugin)) renameSync3(plugin, backup);
35718
+ if (existsSync5(plugin)) renameSync3(plugin, backup);
35507
35719
  try {
35508
35720
  renameSync3(staging, plugin);
35509
35721
  } catch (error62) {
35510
- if (existsSync4(backup)) renameSync3(backup, plugin);
35722
+ if (existsSync5(backup)) renameSync3(backup, plugin);
35511
35723
  throw error62;
35512
35724
  }
35513
35725
  rmSync4(backup, { recursive: true, force: true });
35514
35726
  }
35515
- const marketplacePath = join13(marketplace, "marketplace.json");
35727
+ const marketplacePath = join14(marketplace, "marketplace.json");
35516
35728
  const document = readJson(marketplacePath) ?? { name: "personal", interface: { displayName: "Personal" }, plugins: [] };
35517
35729
  if (document.name !== "personal" || !Array.isArray(document.plugins)) throw new Error("Unsupported personal plugin marketplace");
35518
35730
  document.plugins = [...document.plugins.filter((item) => typeof item === "object" && item !== null && item.name !== "apilift"), { name: "apilift", source: { source: "local", path: "./plugins/apilift" }, policy: { installation: "AVAILABLE", authentication: "ON_INSTALL" }, category: "Developer Tools" }];
35519
35731
  writeJson(marketplacePath, document);
35520
- writeJson(join13(marketplace, ".claude-plugin", "marketplace.json"), { "$schema": "https://anthropic.com/claude-code/marketplace.schema.json", name: "personal", description: "Personal agent plugins", owner: { name: "Local" }, plugins: [{ name: "apilift", description: "Use logged-in apps and discover missing actions with Apilift", source: "./plugins/apilift", category: "development" }] });
35732
+ writeJson(join14(marketplace, ".claude-plugin", "marketplace.json"), { "$schema": "https://anthropic.com/claude-code/marketplace.schema.json", name: "personal", description: "Personal agent plugins", owner: { name: "Local" }, plugins: [{ name: "apilift", description: "Use logged-in apps and discover missing actions with Apilift", source: "./plugins/apilift", category: "development" }] });
35521
35733
  if (env.APILIFT_AGENT_HOME || env.APILIFT_AGENT_NO_ACTIVATE === "1") return { ready: false, version: expected, detail: `Plugin staged at ${plugin}; activation is disabled` };
35522
35734
  const initial = active(agent2, expected);
35523
35735
  if (!initial.known) return { ready: false, version: expected, detail: initial.detail, checkFailed: true };
@@ -35642,7 +35854,7 @@ var init_analytics = __esm({
35642
35854
  });
35643
35855
 
35644
35856
  // src/analytics.ts
35645
- import { randomUUID as randomUUID10 } from "node:crypto";
35857
+ import { randomUUID as randomUUID11 } from "node:crypto";
35646
35858
  var EVENT, EU_HOST, CliAnalytics;
35647
35859
  var init_analytics2 = __esm({
35648
35860
  "src/analytics.ts"() {
@@ -35699,6 +35911,7 @@ var init_analytics2 = __esm({
35699
35911
  this.updateIdentity(await createCliApi(site, this.fetcher, installation.token).call("me"), now);
35700
35912
  return "ok";
35701
35913
  } catch (error62) {
35914
+ if (error62 instanceof ApiClientError && error62.status === 401 && error62.response?.error?.code === "unauthenticated") return "unauthorized";
35702
35915
  if (error62 instanceof ApiClientError && error62.response?.error?.code === "installation-revoked") {
35703
35916
  this.state.db.exec("DELETE FROM cli_analytics_queue; DELETE FROM cli_analytics_identity");
35704
35917
  return "revoked";
@@ -35712,7 +35925,7 @@ var init_analytics2 = __esm({
35712
35925
  if (!installation) return;
35713
35926
  const identity2 = this.identity();
35714
35927
  if (identity2 && !identity2.enabled) return;
35715
- this.state.db.prepare("INSERT INTO cli_analytics_queue (id,account_id,event,properties_json,occurred_at) VALUES (?,?,?,?,?)").run(randomUUID10(), installation.accountId, event, JSON.stringify(scrubEventProperties(event, values)), (/* @__PURE__ */ new Date()).toISOString());
35928
+ this.state.db.prepare("INSERT INTO cli_analytics_queue (id,account_id,event,properties_json,occurred_at) VALUES (?,?,?,?,?)").run(randomUUID11(), installation.accountId, event, JSON.stringify(scrubEventProperties(event, values)), (/* @__PURE__ */ new Date()).toISOString());
35716
35929
  this.state.db.exec("DELETE FROM cli_analytics_queue WHERE id IN (SELECT id FROM cli_analytics_queue ORDER BY occurred_at DESC LIMIT -1 OFFSET 1000)");
35717
35930
  }
35718
35931
  async flush() {
@@ -35735,13 +35948,58 @@ var init_analytics2 = __esm({
35735
35948
  }
35736
35949
  });
35737
35950
 
35951
+ // src/usage/ownership.ts
35952
+ import { mkdirSync as mkdirSync11, chmodSync as chmodSync7, existsSync as existsSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync10, renameSync as renameSync4, unlinkSync as unlinkSync4 } from "node:fs";
35953
+ import { join as join15 } from "node:path";
35954
+ import { DatabaseSync as DatabaseSync6 } from "node:sqlite";
35955
+ function readOwner(root) {
35956
+ try {
35957
+ const value = JSON.parse(readFileSync10(ownerFile(root), "utf8"));
35958
+ return typeof value.id === "string" && Number.isInteger(value.pid) && value.pid > 0 && Number.isFinite(value.expiresAt) ? value : null;
35959
+ } catch {
35960
+ return null;
35961
+ }
35962
+ }
35963
+ function collectorActive(root) {
35964
+ const owner = readOwner(root), now = Date.now();
35965
+ if (owner && owner.expiresAt > now && owner.expiresAt <= now + ownerLifetimeMs) {
35966
+ try {
35967
+ process.kill(owner.pid, 0);
35968
+ return true;
35969
+ } catch {
35970
+ }
35971
+ }
35972
+ const file2 = join15(root, "usage", "collector-lock.sqlite");
35973
+ if (!existsSync6(file2)) return false;
35974
+ try {
35975
+ const db = new DatabaseSync6(file2, { readOnly: true });
35976
+ try {
35977
+ db.exec("PRAGMA busy_timeout=0");
35978
+ db.prepare("PRAGMA schema_version").get();
35979
+ return false;
35980
+ } finally {
35981
+ db.close();
35982
+ }
35983
+ } catch {
35984
+ return true;
35985
+ }
35986
+ }
35987
+ var ownerLifetimeMs, ownerFile;
35988
+ var init_ownership = __esm({
35989
+ "src/usage/ownership.ts"() {
35990
+ "use strict";
35991
+ ownerLifetimeMs = 6e4;
35992
+ ownerFile = (root) => join15(root, "usage", "collector-owner.json");
35993
+ }
35994
+ });
35995
+
35738
35996
  // src/usage/process.ts
35739
- import { existsSync as existsSync5 } from "node:fs";
35997
+ import { existsSync as existsSync7 } from "node:fs";
35740
35998
  import { fileURLToPath as fileURLToPath3 } from "node:url";
35741
35999
  function startCollector(env) {
35742
36000
  if (process.env.VITEST || env.VITEST || env.APILIFT_COLLECTOR_DISABLED === "1") return;
35743
36001
  const entry2 = fileURLToPath3(new URL("./apilift-collector.js", import.meta.url));
35744
- if (!existsSync5(entry2)) return;
36002
+ if (!existsSync7(entry2) || collectorActive(dataRoot(env))) return;
35745
36003
  try {
35746
36004
  startBackground(entry2, env, () => {
35747
36005
  });
@@ -35751,6 +36009,8 @@ function startCollector(env) {
35751
36009
  var init_process2 = __esm({
35752
36010
  "src/usage/process.ts"() {
35753
36011
  "use strict";
36012
+ init_ownership();
36013
+ init_state();
35754
36014
  init_process();
35755
36015
  }
35756
36016
  });
@@ -36605,10 +36865,10 @@ var require_truncate = __commonJS({
36605
36865
  "../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/truncate.js"(exports, module) {
36606
36866
  "use strict";
36607
36867
  var parse5 = require_parse();
36608
- var constants = require_constants();
36868
+ var constants2 = require_constants();
36609
36869
  var SemVer = require_semver();
36610
36870
  var truncate = (version2, truncation, options2) => {
36611
- if (!constants.RELEASE_TYPES.includes(truncation)) {
36871
+ if (!constants2.RELEASE_TYPES.includes(truncation)) {
36612
36872
  return null;
36613
36873
  }
36614
36874
  const clonedVersion = cloneInputVersion(version2, options2);
@@ -37657,7 +37917,7 @@ var require_semver2 = __commonJS({
37657
37917
  "../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/index.js"(exports, module) {
37658
37918
  "use strict";
37659
37919
  var internalRe = require_re();
37660
- var constants = require_constants();
37920
+ var constants2 = require_constants();
37661
37921
  var SemVer = require_semver();
37662
37922
  var identifiers = require_identifiers();
37663
37923
  var parse5 = require_parse();
@@ -37741,8 +38001,8 @@ var require_semver2 = __commonJS({
37741
38001
  re: internalRe.re,
37742
38002
  src: internalRe.src,
37743
38003
  tokens: internalRe.t,
37744
- SEMVER_SPEC_VERSION: constants.SEMVER_SPEC_VERSION,
37745
- RELEASE_TYPES: constants.RELEASE_TYPES,
38004
+ SEMVER_SPEC_VERSION: constants2.SEMVER_SPEC_VERSION,
38005
+ RELEASE_TYPES: constants2.RELEASE_TYPES,
37746
38006
  compareIdentifiers: identifiers.compareIdentifiers,
37747
38007
  rcompareIdentifiers: identifiers.rcompareIdentifiers
37748
38008
  };
@@ -37790,11 +38050,11 @@ var init_compat2 = __esm({
37790
38050
  });
37791
38051
 
37792
38052
  // src/native-host/registration.ts
37793
- import { createHash as createHash6, randomUUID as randomUUID11 } from "node:crypto";
38053
+ import { createHash as createHash6, randomUUID as randomUUID12 } from "node:crypto";
37794
38054
  import { execFileSync as execFileSync2 } from "node:child_process";
37795
- import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "node:fs";
38055
+ import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync11 } from "node:fs";
37796
38056
  import { homedir as homedir3, platform as platform2 } from "node:os";
37797
- import { dirname as dirname8, isAbsolute as isAbsolute4, join as join14, resolve as resolve4 } from "node:path";
38057
+ import { dirname as dirname9, isAbsolute as isAbsolute2, join as join16, resolve as resolve8 } from "node:path";
37798
38058
  import { chmod as chmod2, mkdir, readFile, rename as rename2, writeFile, copyFile, rm, stat } from "node:fs/promises";
37799
38059
  function batchLiteral(value) {
37800
38060
  if (/["\r\n\0]/u.test(value)) throw new Error("Native host launcher values contain unsupported control characters");
@@ -37815,7 +38075,7 @@ ${Object.entries(environment).map(([key, value]) => `${key}=${posixQuote(value)}
37815
38075
  }
37816
38076
  function readWindowsHostValues(hostName, run2 = execFileSync2) {
37817
38077
  if (!/^[a-z0-9_]+(?:\.[a-z0-9_]+)*$/u.test(hostName)) throw new Error("Invalid native host name");
37818
- const script = `$ErrorActionPreference='Stop'; $results=@(); foreach($view in @('32','64')) {
38078
+ const script2 = `$ErrorActionPreference='Stop'; $results=@(); foreach($view in @('32','64')) {
37819
38079
  $base=[Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::CurrentUser,[Microsoft.Win32.RegistryView]('Registry'+$view));
37820
38080
  try {
37821
38081
  $key=$base.OpenSubKey('Software\\Google\\Chrome\\NativeMessagingHosts\\${hostName}');
@@ -37828,7 +38088,7 @@ function readWindowsHostValues(hostName, run2 = execFileSync2) {
37828
38088
  } finally { $base.Dispose(); }
37829
38089
  }; ConvertTo-Json -InputObject @($results) -Compress`;
37830
38090
  try {
37831
- const output2 = run2("powershell.exe", ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(script, "utf16le").toString("base64")], { encoding: "utf8", timeout: 1e4, windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
38091
+ const output2 = run2("powershell.exe", ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(script2, "utf16le").toString("base64")], { encoding: "utf8", timeout: 1e4, windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
37832
38092
  const values = JSON.parse(String(output2));
37833
38093
  if (!Array.isArray(values) || values.length !== 2) throw new Error("Invalid registry snapshot");
37834
38094
  return ["32", "64"].map((view) => {
@@ -37858,39 +38118,39 @@ function registerWindowsHost(hostName, manifest, owned, run2 = execFileSync2, wr
37858
38118
  if (write) for (const view of /* @__PURE__ */ new Set(["32", ...present])) run2("reg.exe", ["add", key, "/ve", "/t", "REG_SZ", "/d", manifest, "/f", `/reg:${view}`], options2);
37859
38119
  }
37860
38120
  function manifestDirectory(env = process.env, system = { platform: platform2(), home: homedir3() }) {
37861
- if (env.APILIFT_NATIVE_MESSAGING_DIR) return resolve4(env.APILIFT_NATIVE_MESSAGING_DIR);
38121
+ if (env.APILIFT_NATIVE_MESSAGING_DIR) return resolve8(env.APILIFT_NATIVE_MESSAGING_DIR);
37862
38122
  const home = system.home;
37863
- if (system.platform === "darwin") return join14(home, "Library", "Application Support", "Google", "Chrome", "NativeMessagingHosts");
38123
+ if (system.platform === "darwin") return join16(home, "Library", "Application Support", "Google", "Chrome", "NativeMessagingHosts");
37864
38124
  if (system.platform === "win32") throw new Error("Windows native-host registration needs a data-root manifest and HKCU registry entry");
37865
- return join14(absoluteEnv(env.CHROME_CONFIG_HOME) || absoluteEnv(env.XDG_CONFIG_HOME) || join14(home, ".config"), "google-chrome", "NativeMessagingHosts");
38125
+ return join16(absoluteEnv(env.CHROME_CONFIG_HOME) || absoluteEnv(env.XDG_CONFIG_HOME) || join16(home, ".config"), "google-chrome", "NativeMessagingHosts");
37866
38126
  }
37867
38127
  async function registerNativeHost(options2) {
37868
38128
  const env = options2.env ?? process.env;
37869
38129
  const hostName = options2.hostName ?? "com.apilift.browser";
37870
38130
  if (!hostNames.has(hostName)) throw new Error("Unknown Apilift native host name");
37871
38131
  if (!validId.test(options2.extensionId)) throw new Error("Extension id must be 32 a\u2013p characters");
37872
- if (![options2.root, options2.hostScript, options2.nodePath ?? process.execPath].every(isAbsolute4)) throw new Error("Native host paths must be absolute");
38132
+ if (![options2.root, options2.hostScript, options2.nodePath ?? process.execPath].every(isAbsolute2)) throw new Error("Native host paths must be absolute");
37873
38133
  const siteUrl2 = options2.siteUrl ? new URL(options2.siteUrl) : null;
37874
38134
  if (siteUrl2 && (!["http:", "https:"].includes(siteUrl2.protocol) || siteUrl2.origin !== options2.siteUrl)) throw new Error("Native host site URL must be an http(s) origin");
37875
- const root = resolve4(options2.root), hostScript = resolve4(options2.hostScript), nodePath = resolve4(options2.nodePath ?? process.execPath);
37876
- const nativeRoot = join14(root, "native-host");
37877
- const manifestRoot = env.APILIFT_NATIVE_MESSAGING_DIR ? resolve4(env.APILIFT_NATIVE_MESSAGING_DIR) : platform2() === "win32" ? nativeRoot : manifestDirectory(env);
37878
- const launcher = join14(nativeRoot, platform2() === "win32" ? "launcher.cmd" : "launcher.sh");
37879
- const manifest = join14(manifestRoot, `${hostName}.json`);
38135
+ const root = resolve8(options2.root), hostScript = resolve8(options2.hostScript), nodePath = resolve8(options2.nodePath ?? process.execPath);
38136
+ const nativeRoot = join16(root, "native-host");
38137
+ const manifestRoot = env.APILIFT_NATIVE_MESSAGING_DIR ? resolve8(env.APILIFT_NATIVE_MESSAGING_DIR) : platform2() === "win32" ? nativeRoot : manifestDirectory(env);
38138
+ const launcher = join16(nativeRoot, platform2() === "win32" ? "launcher.cmd" : "launcher.sh");
38139
+ const manifest = join16(manifestRoot, `${hostName}.json`);
37880
38140
  const manifests = [manifest];
37881
38141
  if (platform2() === "linux" && !env.APILIFT_NATIVE_MESSAGING_DIR) {
37882
38142
  for (const browser of ["google-chrome-beta", "google-chrome-unstable", "chromium"]) {
37883
- const profile = join14(dirname8(dirname8(manifestRoot)), browser);
37884
- if (await stat(profile).then((value) => value.isDirectory()).catch(() => false)) manifests.push(join14(profile, "NativeMessagingHosts", `${hostName}.json`));
38143
+ const profile = join16(dirname9(dirname9(manifestRoot)), browser);
38144
+ if (await stat(profile).then((value) => value.isDirectory()).catch(() => false)) manifests.push(join16(profile, "NativeMessagingHosts", `${hostName}.json`));
37885
38145
  }
37886
38146
  }
37887
- const receiptPath = join14(nativeRoot, "registration.json");
38147
+ const receiptPath = join16(nativeRoot, "registration.json");
37888
38148
  const registryBackup = options2.allowReplaceUnowned ? (view, value) => {
37889
- const directory = join14(nativeRoot, "backup");
37890
- mkdirSync10(directory, { recursive: true, mode: 448 });
37891
- const path2 = join14(directory, `${hostName}-registry-${view}-${fingerprint(value)}.json`);
38149
+ const directory = join16(nativeRoot, "backup");
38150
+ mkdirSync12(directory, { recursive: true, mode: 448 });
38151
+ const path2 = join16(directory, `${hostName}-registry-${view}-${fingerprint(value)}.json`);
37892
38152
  try {
37893
- writeFileSync9(path2, JSON.stringify({ hostName, view, query: value }), { flag: "wx", mode: 384 });
38153
+ writeFileSync11(path2, JSON.stringify({ hostName, view, query: value }), { flag: "wx", mode: 384 });
37894
38154
  } catch (error62) {
37895
38155
  if (error62.code !== "EEXIST") throw error62;
37896
38156
  }
@@ -37900,8 +38160,8 @@ async function registerNativeHost(options2) {
37900
38160
  const bytes = await readFile(path2).catch(() => null);
37901
38161
  if (!bytes || fingerprint(bytes) === previous[path2]) return;
37902
38162
  if (!options2.allowReplaceUnowned) throw new Error(`Native host registration is not owned by Apilift: ${path2}`);
37903
- await mkdir(join14(nativeRoot, "backup"), { recursive: true, mode: 448 });
37904
- await copyFile(path2, join14(nativeRoot, "backup", `${fingerprint(bytes)}-${Date.now()}`));
38163
+ await mkdir(join16(nativeRoot, "backup"), { recursive: true, mode: 448 });
38164
+ await copyFile(path2, join16(nativeRoot, "backup", `${fingerprint(bytes)}-${Date.now()}`));
37905
38165
  };
37906
38166
  await guard(launcher);
37907
38167
  for (const path2 of manifests) await guard(path2);
@@ -37914,7 +38174,7 @@ async function registerNativeHost(options2) {
37914
38174
  const launcherText = nativeLauncher({ root, hostName, nodePath, hostScript, manifestRoot, site: siteUrl2?.origin }, platform2() === "win32");
37915
38175
  const manifestText = JSON.stringify({ name: hostName, description: "Apilift browser bridge", path: launcher, type: "stdio", allowed_origins: [`chrome-extension://${options2.extensionId}/`] }, null, 2) + "\n";
37916
38176
  const atomicWrite = async (path2, bytes, mode) => {
37917
- const temp = join14(dirname8(path2), `.${hostName}-${randomUUID11()}.tmp`);
38177
+ const temp = join16(dirname9(path2), `.${hostName}-${randomUUID12()}.tmp`);
37918
38178
  try {
37919
38179
  await writeFile(temp, bytes, { mode, flag: "wx" });
37920
38180
  await chmod2(temp, mode);
@@ -37925,7 +38185,7 @@ async function registerNativeHost(options2) {
37925
38185
  };
37926
38186
  await atomicWrite(launcher, launcherText, platform2() === "win32" ? 384 : 448);
37927
38187
  for (const path2 of manifests) {
37928
- await mkdir(dirname8(path2), { recursive: true, mode: 448 });
38188
+ await mkdir(dirname9(path2), { recursive: true, mode: 448 });
37929
38189
  await atomicWrite(path2, manifestText, 384);
37930
38190
  }
37931
38191
  const receipt = { ...previous, [launcher]: fingerprint(launcherText), ...Object.fromEntries(manifests.map((path2) => [path2, fingerprint(manifestText)])) };
@@ -37941,26 +38201,26 @@ var init_registration = __esm({
37941
38201
  hostNames = /* @__PURE__ */ new Set(["com.apilift.browser", "com.apilift.browser.dev"]);
37942
38202
  validId = /^[a-p]{32}$/;
37943
38203
  posixQuote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
37944
- absoluteEnv = (value) => value && isAbsolute4(value) ? value : void 0;
38204
+ absoluteEnv = (value) => value && isAbsolute2(value) ? value : void 0;
37945
38205
  }
37946
38206
  });
37947
38207
 
37948
38208
  // src/update.ts
37949
38209
  import { fileURLToPath as fileURLToPath4 } from "node:url";
37950
- import { dirname as dirname9, join as join15, resolve as resolve5, sep, isAbsolute as isAbsolute5 } from "node:path";
37951
- import { mkdirSync as mkdirSync11, realpathSync as realpathSync2, writeFileSync as writeFileSync10, readFileSync as readFileSync10 } from "node:fs";
37952
- import { DatabaseSync as DatabaseSync6 } from "node:sqlite";
37953
- function globalPackage(script, prefix) {
38210
+ import { dirname as dirname10, join as join17, resolve as resolve9, sep, isAbsolute as isAbsolute3 } from "node:path";
38211
+ import { mkdirSync as mkdirSync13, realpathSync as realpathSync2, writeFileSync as writeFileSync12, readFileSync as readFileSync11 } from "node:fs";
38212
+ import { DatabaseSync as DatabaseSync7 } from "node:sqlite";
38213
+ function globalPackage(script2, prefix) {
37954
38214
  const canonical = (path2) => {
37955
38215
  try {
37956
38216
  return realpathSync2(path2);
37957
38217
  } catch {
37958
- return resolve5(path2);
38218
+ return resolve9(path2);
37959
38219
  }
37960
38220
  };
37961
- const normalized = canonical(script).toLowerCase();
38221
+ const normalized = canonical(script2).toLowerCase();
37962
38222
  const base2 = canonical(prefix).toLowerCase();
37963
- return [join15(base2, "lib", "node_modules", "@apilift", "cli"), join15(base2, "node_modules", "@apilift", "cli")].some((path2) => normalized.startsWith(`${path2}${sep}`));
38223
+ return [join17(base2, "lib", "node_modules", "@apilift", "cli"), join17(base2, "node_modules", "@apilift", "cli")].some((path2) => normalized.startsWith(`${path2}${sep}`));
37964
38224
  }
37965
38225
  async function pnpmInstallations(runner, env) {
37966
38226
  const listed = await runner("pnpm", ["list", "-g", "@apilift/cli", "--depth", "0", "--json"], 1e4, env).catch(() => null);
@@ -37970,34 +38230,34 @@ async function pnpmInstallations(runner, env) {
37970
38230
  if (!Array.isArray(groups)) return [];
37971
38231
  return groups.flatMap((group) => {
37972
38232
  const pkg = group?.dependencies?.["@apilift/cli"];
37973
- return pkg && typeof pkg.path === "string" && isAbsolute5(pkg.path) && typeof pkg.version === "string" ? [{ entrypoint: join15(pkg.path, "dist", "apilift.js"), version: pkg.version }] : [];
38233
+ return pkg && typeof pkg.path === "string" && isAbsolute3(pkg.path) && typeof pkg.version === "string" ? [{ entrypoint: join17(pkg.path, "dist", "apilift.js"), version: pkg.version }] : [];
37974
38234
  });
37975
38235
  } catch {
37976
38236
  return [];
37977
38237
  }
37978
38238
  }
37979
- async function installationManager(script, runner, env) {
38239
+ async function installationManager(script2, runner, env) {
37980
38240
  const prefix = await runner("npm", ["prefix", "-g"], 1e4, env).catch(() => null);
37981
- if (prefix?.exitCode === 0 && globalPackage(script, prefix.stdout.trim())) return { manager: "npm", entrypoint: script };
38241
+ if (prefix?.exitCode === 0 && globalPackage(script2, prefix.stdout.trim())) return { manager: "npm", entrypoint: script2 };
37982
38242
  const canonical = (path2) => {
37983
38243
  try {
37984
38244
  return realpathSync2(path2);
37985
38245
  } catch {
37986
- return resolve5(path2);
38246
+ return resolve9(path2);
37987
38247
  }
37988
38248
  };
37989
- const selected = (await pnpmInstallations(runner, env)).find((candidate2) => canonical(candidate2.entrypoint) === canonical(script));
38249
+ const selected = (await pnpmInstallations(runner, env)).find((candidate2) => canonical(candidate2.entrypoint) === canonical(script2));
37990
38250
  return selected ? { manager: "pnpm", entrypoint: selected.entrypoint } : null;
37991
38251
  }
37992
38252
  async function maybeAutoUpdate(options2) {
37993
38253
  if (options2.env.APILIFT_AUTO_UPDATE === "0" || options2.env.CI || options2.env.APILIFT_DEV_PLUGIN_ROOT || options2.env.APILIFT_UPDATE_REEXEC === "1") return null;
37994
- const current = options2.current || CURRENT, runner = options2.runner || run, script = options2.script || fileURLToPath4(import.meta.url);
38254
+ const current = options2.current || CURRENT, runner = options2.runner || run, script2 = options2.script || fileURLToPath4(import.meta.url);
37995
38255
  if (!shouldCheckUpdate(options2.state, current)) return null;
37996
- const installation = await installationManager(script, runner, options2.env);
37997
- if (!installation) return null;
37998
38256
  markUpdateChecked(options2.state, current);
38257
+ const installation = await installationManager(script2, runner, options2.env);
38258
+ if (!installation) return null;
37999
38259
  let updatedEntry = installation.entrypoint;
38000
- const update = await updateCli({ ...options2, current, runner, script, manual: false }, (entrypoint) => {
38260
+ const update = await updateCli({ ...options2, current, runner, script: script2, manual: false }, (entrypoint) => {
38001
38261
  updatedEntry = entrypoint;
38002
38262
  });
38003
38263
  if (update.stderr.startsWith("update-incomplete")) return update;
@@ -38006,9 +38266,9 @@ async function maybeAutoUpdate(options2) {
38006
38266
  return { stdout: result3.stdout, stderr: result3.stderr, exitCode: result3.exitCode ?? 1, dispatched: false };
38007
38267
  }
38008
38268
  function lock(root) {
38009
- const dir = join15(root, "locks");
38010
- mkdirSync11(dir, { recursive: true, mode: 448 });
38011
- const db = new DatabaseSync6(join15(dir, "self-update.sqlite"));
38269
+ const dir = join17(root, "locks");
38270
+ mkdirSync13(dir, { recursive: true, mode: 448 });
38271
+ const db = new DatabaseSync7(join17(dir, "self-update.sqlite"));
38012
38272
  db.exec("PRAGMA busy_timeout=0");
38013
38273
  try {
38014
38274
  db.exec("BEGIN EXCLUSIVE");
@@ -38041,8 +38301,8 @@ async function updateCli(options2, onInstalled) {
38041
38301
  if (compatible2(current, latest)) return message("The installed CLI and its required local components are up to date.\nNo installation changes were made. Continue the app work you were working on.\n");
38042
38302
  if (!options2.manual && (options2.env.APILIFT_AUTO_UPDATE === "0" || options2.env.CI || options2.env.APILIFT_DEV_PLUGIN_ROOT || options2.env.npm_execpath?.includes("npx"))) return message("", 5, `client-upgrade-required \u2014 Install Apilift ${latest} through the supported package installation, then retry.
38043
38303
  `);
38044
- const script = options2.script || fileURLToPath4(import.meta.url);
38045
- const installation = await installationManager(script, runner, options2.env);
38304
+ const script2 = options2.script || fileURLToPath4(import.meta.url);
38305
+ const installation = await installationManager(script2, runner, options2.env);
38046
38306
  if (!installation) return message("", 5, `client-upgrade-required \u2014 This CLI is not a global npm or pnpm installation. Install Apilift ${latest} through your installation method, then retry.
38047
38307
  `);
38048
38308
  const { manager } = installation;
@@ -38055,19 +38315,19 @@ async function updateCli(options2, onInstalled) {
38055
38315
  `);
38056
38316
  options2.state.db.exec("CREATE TABLE IF NOT EXISTS cli_update_state (id INTEGER PRIMARY KEY CHECK(id=1), version TEXT NOT NULL, updated_at TEXT NOT NULL)");
38057
38317
  options2.state.db.prepare("INSERT INTO cli_update_state (id,version,updated_at) VALUES (1,?,?) ON CONFLICT(id) DO UPDATE SET version=excluded.version,updated_at=excluded.updated_at").run(latest, (/* @__PURE__ */ new Date()).toISOString());
38058
- const nativeRoot = join15(options2.root, "native-host");
38059
- mkdirSync11(nativeRoot, { recursive: true, mode: 448 });
38060
- writeFileSync10(join15(nativeRoot, "restart-required.json"), `${JSON.stringify({ version: latest })}
38318
+ const nativeRoot = join17(options2.root, "native-host");
38319
+ mkdirSync13(nativeRoot, { recursive: true, mode: 448 });
38320
+ writeFileSync12(join17(nativeRoot, "restart-required.json"), `${JSON.stringify({ version: latest })}
38061
38321
  `, { mode: 384 });
38062
38322
  try {
38063
38323
  if (manager === "pnpm") {
38064
38324
  const candidates = (await pnpmInstallations(runner, options2.env)).filter((candidate2) => candidate2.version === latest);
38065
38325
  if (candidates.length !== 1) throw new Error("Updated pnpm installation is ambiguous or unavailable");
38066
38326
  entrypoint = candidates[0].entrypoint;
38067
- const pkg = JSON.parse(readFileSync10(join15(dirname9(entrypoint), "..", "package.json"), "utf8"));
38327
+ const pkg = JSON.parse(readFileSync11(join17(dirname10(entrypoint), "..", "package.json"), "utf8"));
38068
38328
  if (pkg.name !== "@apilift/cli" || pkg.version !== latest) throw new Error("Updated pnpm package could not be verified");
38069
38329
  }
38070
- await registerNativeHost({ root: options2.root, hostScript: join15(dirname9(entrypoint), "apilift-native-host.js"), extensionId: options2.env.APILIFT_DEV_EXTENSION_ID || STORE_EXTENSION_ID, hostName: options2.env.APILIFT_NATIVE_HOST_NAME, siteUrl: options2.site, env: options2.env });
38330
+ await registerNativeHost({ root: options2.root, hostScript: join17(dirname10(entrypoint), "apilift-native-host.js"), extensionId: options2.env.APILIFT_DEV_EXTENSION_ID || STORE_EXTENSION_ID, hostName: options2.env.APILIFT_NATIVE_HOST_NAME, siteUrl: options2.site, env: options2.env });
38071
38331
  for (const agent2 of linkedAgents) {
38072
38332
  if (!ensureAgentPlugin(agent2, options2.env).ready) throw new Error("Updated agent plugin activation is unverified");
38073
38333
  }
@@ -38108,9 +38368,9 @@ var init_update = __esm({
38108
38368
  });
38109
38369
 
38110
38370
  // src/setup-check.ts
38111
- import { createHash as createHash7, randomUUID as randomUUID12 } from "node:crypto";
38112
- import { existsSync as existsSync6, readFileSync as readFileSync11, realpathSync as realpathSync3, writeFileSync as writeFileSync11 } from "node:fs";
38113
- import { dirname as dirname10, join as join16 } from "node:path";
38371
+ import { createHash as createHash7, randomUUID as randomUUID13 } from "node:crypto";
38372
+ import { existsSync as existsSync8, readFileSync as readFileSync12, realpathSync as realpathSync3, writeFileSync as writeFileSync13 } from "node:fs";
38373
+ import { dirname as dirname11, join as join18 } from "node:path";
38114
38374
  async function setupCheckBody(client, scopeId, value) {
38115
38375
  if (typeof value.body === "string") return Buffer.byteLength(value.body, "utf8") <= maxCheckBodyBytes ? value.body : null;
38116
38376
  if (value.body != null) return null;
@@ -38125,30 +38385,30 @@ async function setupCheckBody(client, scopeId, value) {
38125
38385
  }
38126
38386
  }
38127
38387
  async function registerSetupHost(root, site, env) {
38128
- const script = realpathSync3(process.argv[1]);
38129
- const hostScript = join16(dirname10(script), "apilift-native-host.js");
38130
- if (!existsSync6(hostScript)) throw new Error("The native host binary is missing beside the Apilift CLI");
38388
+ const script2 = realpathSync3(process.argv[1]);
38389
+ const hostScript = join18(dirname11(script2), "apilift-native-host.js");
38390
+ if (!existsSync8(hostScript)) throw new Error("The native host binary is missing beside the Apilift CLI");
38131
38391
  await registerNativeHost({ root, hostScript, extensionId: env.APILIFT_DEV_EXTENSION_ID || storeExtensionId, hostName: env.APILIFT_NATIVE_HOST_NAME, siteUrl: site, env });
38132
38392
  }
38133
38393
  function challengeFile(root, agent2) {
38134
- return join16(root, `setup-check-${agent2}.json`);
38394
+ return join18(root, `setup-check-${agent2}.json`);
38135
38395
  }
38136
38396
  function setupChallenge(root, agent2, site) {
38137
38397
  const file2 = challengeFile(root, agent2);
38138
38398
  try {
38139
- const saved = JSON.parse(readFileSync11(file2, "utf8"));
38399
+ const saved = JSON.parse(readFileSync12(file2, "utf8"));
38140
38400
  if (new URL(saved.url).origin === site && Number.isFinite(saved.createdAt) && Date.now() - saved.createdAt < 10 * 6e4) return saved;
38141
38401
  } catch {
38142
38402
  }
38143
- const challenge = { url: `${site}/setup-check?challenge=${randomUUID12()}`, createdAt: Date.now() };
38144
- writeFileSync11(file2, JSON.stringify(challenge), { mode: 384 });
38403
+ const challenge = { url: `${site}/setup-check?challenge=${randomUUID13()}`, createdAt: Date.now() };
38404
+ writeFileSync13(file2, JSON.stringify(challenge), { mode: 384 });
38145
38405
  return challenge;
38146
38406
  }
38147
38407
  async function setupBridge(root) {
38148
38408
  const startedAt = Date.now();
38149
- const script = join16(dirname10(realpathSync3(process.argv[1])), "apilift-capture-server.js");
38150
- const client = await ensureCaptureServer({ root, script, version: cliVersion });
38151
- if (!client.legacyBridge) await waitForBridge(root, cliVersion, Math.max(0, 9e4 - (Date.now() - startedAt)));
38409
+ const script2 = join18(dirname11(realpathSync3(process.argv[1])), "apilift-capture-server.js");
38410
+ const client = await ensureCaptureServer({ root, script: script2, version: cliVersion });
38411
+ if (!client.legacyBridge) await waitForBridge(root, cliVersion, Math.max(0, 9e4 - (Date.now() - startedAt)), () => client.bridgeDemand(), client.receipt.version);
38152
38412
  return client;
38153
38413
  }
38154
38414
  async function setupReadiness(api, waitForReport, timeoutMs = 1e4) {
@@ -38156,7 +38416,7 @@ async function setupReadiness(api, waitForReport, timeoutMs = 1e4) {
38156
38416
  const signal = AbortSignal.timeout(timeoutMs);
38157
38417
  let readiness2 = await api.call("updateReadiness", { body: {}, ...waitForReport ? { signal } : {} });
38158
38418
  while (waitForReport && (!readiness2.machine.nativeHostReady || !readiness2.machine.extensionReady) && Date.now() < deadline) {
38159
- await new Promise((resolve6) => setTimeout(resolve6, Math.min(250, Math.max(0, deadline - Date.now()))));
38419
+ await new Promise((resolve10) => setTimeout(resolve10, Math.min(250, Math.max(0, deadline - Date.now()))));
38160
38420
  if (signal.aborted || Date.now() >= deadline) break;
38161
38421
  try {
38162
38422
  readiness2 = await api.call("updateReadiness", { body: {}, signal });
@@ -38179,14 +38439,14 @@ async function verifySetupCheck(root, challenge) {
38179
38439
  do {
38180
38440
  candidates = (await client.candidates(inspect.id)).candidates;
38181
38441
  if (candidates.length) break;
38182
- await new Promise((resolve6) => setTimeout(resolve6, 200));
38442
+ await new Promise((resolve10) => setTimeout(resolve10, 200));
38183
38443
  } while (Date.now() < deadline);
38184
38444
  } finally {
38185
38445
  await client.closeScope(inspect.id);
38186
38446
  }
38187
38447
  if (candidates.length !== 1) return { ok: false, reason: candidates.length ? "More than one exact check tab matched" : "The extension did not report the exact check tab within 10 seconds" };
38188
38448
  const execute = await client.openScope({ kind: "execute", tabSelector: candidates[0].selector, ...scopeInput });
38189
- const id2 = randomUUID12();
38449
+ const id2 = randomUUID13();
38190
38450
  try {
38191
38451
  await client.enqueueJob(execute.id, { job: { id: id2, kind: "replay", effect: "read", method: "GET", url: challenge.url, headers: {}, pageOrigin, allowedRequestOrigin: pageOrigin, timeoutMs: 2e4 } });
38192
38452
  const deadline = Date.now() + 3e4;
@@ -38205,7 +38465,7 @@ async function verifySetupCheck(root, challenge) {
38205
38465
  return body.includes("Apilift can reach this tab") ? { ok: true } : { ok: false, reason: "The safe check response did not contain the expected page marker" };
38206
38466
  }
38207
38467
  if (held.status === "uncertain") return { ok: false, reason: "Chrome reported an uncertain check outcome" };
38208
- await new Promise((resolve6) => setTimeout(resolve6, 200));
38468
+ await new Promise((resolve10) => setTimeout(resolve10, 200));
38209
38469
  } while (Date.now() < deadline);
38210
38470
  return { ok: false, reason: `Chrome did not return the safe check result within 30 seconds (job ${lastStatus})` };
38211
38471
  } finally {
@@ -38457,8 +38717,8 @@ Fix the local Chrome registration and retry:
38457
38717
  const agentState = agent2 ? readiness2.agents[agent2] : void 0;
38458
38718
  const plugin = agent2 ? inspectAgentPlugin(agent2, env) : null;
38459
38719
  const checks = [
38460
- `Native host: ${readiness2.machine.nativeHostReady ? "recorded ready" : "not verified"}.`,
38461
- `Chrome extension: ${readiness2.machine.extensionReady ? "recorded ready" : "not verified"}.`,
38720
+ `Native host: ${readiness2.machine.nativeHostReady ? "previously connected; current availability unverified" : "not verified"}.`,
38721
+ `Chrome extension: ${readiness2.machine.extensionReady ? "previously connected; current installation and availability unverified" : "not verified"}.`,
38462
38722
  `Agent setup skill: ${agentState?.skillsReady && plugin?.ready ? "active" : plugin?.detail ?? "stable agent identity needed"}.`,
38463
38723
  `Agent browser control: ${agentState?.browserReady ? "recorded ready" : "not verified"}.`,
38464
38724
  `Stored setup verification: ${agentState?.verified && plugin?.ready ? "verified" : "incomplete"}.`