@apilift/cli 1.4.33 → 1.4.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apilift-capture-server.js +76 -3
- package/dist/apilift-collector.js +159580 -158475
- package/dist/apilift-native-host.js +39 -4
- package/dist/apilift-register-native-host.js +2 -2
- package/dist/apilift.js +589 -329
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.codex-plugin/plugin.json +1 -1
- package/package.json +3 -3
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,
|
|
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,
|
|
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,
|
|
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 ?? (
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
20950
|
-
this.code = optimizeExpr(this.code, names2,
|
|
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,
|
|
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,
|
|
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,
|
|
21037
|
+
optimizeNames(names2, constants2) {
|
|
21038
21038
|
var _a3;
|
|
21039
|
-
this.else = (_a3 = this.else) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names2,
|
|
21040
|
-
if (!(super.optimizeNames(names2,
|
|
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,
|
|
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,
|
|
21066
|
-
if (!super.optimizeNames(names2,
|
|
21065
|
+
optimizeNames(names2, constants2) {
|
|
21066
|
+
if (!super.optimizeNames(names2, constants2))
|
|
21067
21067
|
return;
|
|
21068
|
-
this.iteration = optimizeExpr(this.iteration, names2,
|
|
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,
|
|
21105
|
-
if (!super.optimizeNames(names2,
|
|
21104
|
+
optimizeNames(names2, constants2) {
|
|
21105
|
+
if (!super.optimizeNames(names2, constants2))
|
|
21106
21106
|
return;
|
|
21107
|
-
this.iterable = optimizeExpr(this.iterable, names2,
|
|
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,
|
|
21149
|
+
optimizeNames(names2, constants2) {
|
|
21150
21150
|
var _a3, _b;
|
|
21151
|
-
super.optimizeNames(names2,
|
|
21152
|
-
(_a3 = this.catch) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names2,
|
|
21153
|
-
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names2,
|
|
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,
|
|
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 =
|
|
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 &&
|
|
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 =
|
|
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
|
|
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
|
|
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:
|
|
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((
|
|
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
|
|
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 =
|
|
28707
|
+
this.root = join2(dataRoot2, "usage");
|
|
28626
28708
|
mkdirSync(this.root, { recursive: true, mode: 448 });
|
|
28627
28709
|
chmodSync(this.root, 448);
|
|
28628
|
-
const file2 =
|
|
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 =
|
|
28653
|
-
|
|
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:${
|
|
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
|
|
28707
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync2, writeFileSync as
|
|
28708
|
-
import { join as
|
|
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(
|
|
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 =
|
|
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 =
|
|
28761
|
-
const dir =
|
|
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 =
|
|
28765
|
-
|
|
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
|
|
30308
|
-
import { chmodSync as chmodSync3, existsSync, mkdirSync as mkdirSync3, readFileSync as readFileSync2, unlinkSync, writeFileSync as
|
|
30309
|
-
import { dirname, join as
|
|
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" ?
|
|
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:
|
|
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
|
-
|
|
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) :
|
|
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(
|
|
30469
|
-
if (!
|
|
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 =
|
|
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(
|
|
30498
|
-
|
|
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(
|
|
31126
|
-
const child = spawn(process.execPath, [
|
|
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
|
|
31140
|
-
import { dirname as
|
|
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(
|
|
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(
|
|
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(
|
|
31257
|
+
function installedCaptureVersion(script2) {
|
|
31176
31258
|
try {
|
|
31177
|
-
const pkg = JSON.parse(readFileSync4(
|
|
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 =
|
|
31266
|
+
const locks = join5(options2.root, "locks");
|
|
31185
31267
|
mkdirSync4(locks, { recursive: true, mode: 448 });
|
|
31186
|
-
const updateLock = new DatabaseSync2(
|
|
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 =
|
|
31291
|
+
const locks = join5(root, "locks");
|
|
31210
31292
|
mkdirSync4(locks, { recursive: true, mode: 448 });
|
|
31211
|
-
const lockFile =
|
|
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(
|
|
31311
|
+
rmSync2(join5(root, "server.json"));
|
|
31230
31312
|
} catch {
|
|
31231
31313
|
}
|
|
31232
31314
|
}
|
|
31233
|
-
if (!
|
|
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((
|
|
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
|
|
31348
|
-
import { join as
|
|
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 =
|
|
31442
|
+
const id2 = randomUUID5(), now = Date.now(), ownerStartedAt = now - process.uptime() * 1e3;
|
|
31354
31443
|
const expiry = now + timeoutMs;
|
|
31355
|
-
db.prepare("
|
|
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(
|
|
31360
|
-
if (ready
|
|
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((
|
|
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) =>
|
|
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
|
|
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
|
|
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
|
|
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((
|
|
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
|
|
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
|
|
31617
|
+
if (env.APILIFT_HOME) return resolve3(env.APILIFT_HOME);
|
|
31516
31618
|
const home = homedir();
|
|
31517
|
-
if (platform() === "darwin") return
|
|
31518
|
-
if (platform() === "win32") return
|
|
31519
|
-
return
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
31850
|
-
import { readFileSync as readFileSync5, renameSync, unlinkSync as
|
|
31851
|
-
import { dirname as
|
|
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
|
-
|
|
32064
|
-
const pending =
|
|
32165
|
+
path2 = resolve4(path2);
|
|
32166
|
+
const pending = join9(dirname5(path2), `.apilift-output-${randomUUID7()}`);
|
|
32065
32167
|
try {
|
|
32066
|
-
|
|
32168
|
+
writeFileSync5(pending, content, { flag: "wx", mode: 384 });
|
|
32067
32169
|
renameSync(pending, path2);
|
|
32068
32170
|
} catch (error62) {
|
|
32069
32171
|
try {
|
|
32070
|
-
|
|
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
|
-
|
|
32119
|
-
if (!
|
|
32120
|
-
|
|
32121
|
-
|
|
32122
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
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 ?? ""}${
|
|
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 =
|
|
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
|
-
|
|
32332
|
-
|
|
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
|
-
|
|
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(
|
|
32347
|
-
output2 = result(`Saved the fetched result to ${
|
|
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
|
-
|
|
32352
|
-
|
|
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 ?? ""}${
|
|
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 =
|
|
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
|
|
32645
|
+
import { mkdtempSync, writeFileSync as writeFileSync6 } from "node:fs";
|
|
32491
32646
|
import { tmpdir } from "node:os";
|
|
32492
|
-
import {
|
|
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(
|
|
32802
|
-
const file2 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 ?
|
|
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
|
|
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
|
|
33577
|
-
import { join as
|
|
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 =
|
|
33581
|
-
|
|
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
|
-
|
|
33586
|
-
|
|
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(
|
|
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
|
|
33639
|
-
import { randomUUID as
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
33877
|
-
if (!
|
|
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
|
-
|
|
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}.${
|
|
34342
|
-
|
|
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(
|
|
34407
|
-
const resultPath =
|
|
34408
|
-
|
|
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((
|
|
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((
|
|
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((
|
|
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
|
-
|
|
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 =
|
|
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 :
|
|
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 =
|
|
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 =
|
|
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 =
|
|
34876
|
-
|
|
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
|
|
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
|
|
35146
|
+
import { join as join13 } from "node:path";
|
|
34935
35147
|
async function completedFile(directory, name, content) {
|
|
34936
|
-
const target =
|
|
34937
|
-
const pending =
|
|
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(
|
|
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
|
|
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(
|
|
35231
|
+
nodeBinPaths.push(resolve6(currentPath, "node_modules", ".bin"));
|
|
35020
35232
|
lastPath = currentPath;
|
|
35021
|
-
currentPath =
|
|
35233
|
+
currentPath = dirname7(currentPath);
|
|
35022
35234
|
} while (currentPath !== lastPath);
|
|
35023
|
-
nodeBinPaths.push(
|
|
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 =
|
|
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 (
|
|
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((
|
|
35284
|
-
this._resolveClose =
|
|
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((
|
|
35368
|
-
this._resolveClose =
|
|
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
|
|
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
|
|
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) => !
|
|
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) =>
|
|
35614
|
+
return files(source).every((path2) => existsSync5(join14(target, path2)));
|
|
35403
35615
|
}
|
|
35404
35616
|
function files(root, relative = "") {
|
|
35405
|
-
return readdirSync(
|
|
35406
|
-
const path2 =
|
|
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(
|
|
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
|
-
|
|
35417
|
-
|
|
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 =
|
|
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(
|
|
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 =
|
|
35463
|
-
const plugin = agent2 === "codex" ?
|
|
35464
|
-
const marker = readJson(
|
|
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 =
|
|
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 =
|
|
35478
|
-
writeJson(
|
|
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 =
|
|
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(
|
|
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 =
|
|
35488
|
-
const marketplace =
|
|
35489
|
-
const plugin = agent2 === "codex" ?
|
|
35490
|
-
const marker = readJson(
|
|
35491
|
-
if (
|
|
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
|
-
|
|
35709
|
+
mkdirSync10(dirname8(staging), { recursive: true });
|
|
35498
35710
|
cpSync(source, staging, { recursive: true });
|
|
35499
|
-
const manifestPath =
|
|
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(
|
|
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 (
|
|
35718
|
+
if (existsSync5(plugin)) renameSync3(plugin, backup);
|
|
35507
35719
|
try {
|
|
35508
35720
|
renameSync3(staging, plugin);
|
|
35509
35721
|
} catch (error62) {
|
|
35510
|
-
if (
|
|
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 =
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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 (!
|
|
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
|
|
36868
|
+
var constants2 = require_constants();
|
|
36609
36869
|
var SemVer = require_semver();
|
|
36610
36870
|
var truncate = (version2, truncation, options2) => {
|
|
36611
|
-
if (!
|
|
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
|
|
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:
|
|
37745
|
-
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
37876
|
-
const nativeRoot =
|
|
37877
|
-
const manifestRoot = env.APILIFT_NATIVE_MESSAGING_DIR ?
|
|
37878
|
-
const launcher =
|
|
37879
|
-
const manifest =
|
|
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 =
|
|
37884
|
-
if (await stat(profile).then((value) => value.isDirectory()).catch(() => false)) manifests.push(
|
|
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 =
|
|
38147
|
+
const receiptPath = join16(nativeRoot, "registration.json");
|
|
37888
38148
|
const registryBackup = options2.allowReplaceUnowned ? (view, value) => {
|
|
37889
|
-
const directory =
|
|
37890
|
-
|
|
37891
|
-
const path2 =
|
|
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
|
-
|
|
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(
|
|
37904
|
-
await copyFile(path2,
|
|
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 =
|
|
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(
|
|
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 &&
|
|
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
|
|
37951
|
-
import { mkdirSync as
|
|
37952
|
-
import { DatabaseSync as
|
|
37953
|
-
function globalPackage(
|
|
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
|
|
38218
|
+
return resolve9(path2);
|
|
37959
38219
|
}
|
|
37960
38220
|
};
|
|
37961
|
-
const normalized = canonical(
|
|
38221
|
+
const normalized = canonical(script2).toLowerCase();
|
|
37962
38222
|
const base2 = canonical(prefix).toLowerCase();
|
|
37963
|
-
return [
|
|
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" &&
|
|
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(
|
|
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(
|
|
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
|
|
38246
|
+
return resolve9(path2);
|
|
37987
38247
|
}
|
|
37988
38248
|
};
|
|
37989
|
-
const selected = (await pnpmInstallations(runner, env)).find((candidate2) => canonical(candidate2.entrypoint) === canonical(
|
|
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,
|
|
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 =
|
|
38010
|
-
|
|
38011
|
-
const db = new
|
|
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
|
|
38045
|
-
const installation = await installationManager(
|
|
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 =
|
|
38059
|
-
|
|
38060
|
-
|
|
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(
|
|
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:
|
|
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
|
|
38112
|
-
import { existsSync as
|
|
38113
|
-
import { dirname as
|
|
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
|
|
38129
|
-
const hostScript =
|
|
38130
|
-
if (!
|
|
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
|
|
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(
|
|
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=${
|
|
38144
|
-
|
|
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
|
|
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((
|
|
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((
|
|
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 =
|
|
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((
|
|
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 ? "
|
|
38461
|
-
`Chrome extension: ${readiness2.machine.extensionReady ? "
|
|
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"}.`
|