@vostride/agent-qa-core 0.1.9 → 0.1.11
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/index.cjs +182 -147
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +135 -103
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -67,20 +67,20 @@ var init_schema = __esm({
|
|
|
67
67
|
// src/memory/observation-io.ts
|
|
68
68
|
import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
|
|
69
69
|
import { mkdir as mkdir5, writeFile as writeFile5, readdir as readdir2 } from "fs/promises";
|
|
70
|
-
import { join as
|
|
70
|
+
import { join as join5, resolve as resolve4, sep } from "path";
|
|
71
71
|
function toObservationFrontmatter(data) {
|
|
72
72
|
const { content, ...frontmatter } = data;
|
|
73
73
|
return frontmatter;
|
|
74
74
|
}
|
|
75
75
|
async function ensureMemoryDirs(root) {
|
|
76
76
|
await Promise.all(
|
|
77
|
-
MEMORY_TIERS.map((tier) => mkdir5(
|
|
77
|
+
MEMORY_TIERS.map((tier) => mkdir5(join5(root, tier), { recursive: true }))
|
|
78
78
|
);
|
|
79
79
|
}
|
|
80
80
|
async function writeObservation(root, tier, scope, data) {
|
|
81
|
-
const dir =
|
|
82
|
-
const resolved =
|
|
83
|
-
const boundary =
|
|
81
|
+
const dir = join5(root, tier, scope);
|
|
82
|
+
const resolved = resolve4(dir);
|
|
83
|
+
const boundary = resolve4(root);
|
|
84
84
|
if (!resolved.startsWith(boundary + sep)) {
|
|
85
85
|
throw new Error(`Scope "${scope}" escapes memory root`);
|
|
86
86
|
}
|
|
@@ -92,7 +92,7 @@ async function writeObservation(root, tier, scope, data) {
|
|
|
92
92
|
${stringifyYaml({ ...frontmatter }, { lineWidth: 0 })}---
|
|
93
93
|
${validated.content}
|
|
94
94
|
`;
|
|
95
|
-
const filePath =
|
|
95
|
+
const filePath = join5(dir, `${data.id}.md`);
|
|
96
96
|
await writeFile5(filePath, content, "utf-8");
|
|
97
97
|
return filePath;
|
|
98
98
|
}
|
|
@@ -198,11 +198,11 @@ var init_security_scanner = __esm({
|
|
|
198
198
|
|
|
199
199
|
// src/sqlite.ts
|
|
200
200
|
import { execFileSync } from "child_process";
|
|
201
|
-
import { existsSync as
|
|
201
|
+
import { existsSync as existsSync7, mkdirSync, rmSync, writeFileSync } from "fs";
|
|
202
202
|
import { createHash as createHash3 } from "crypto";
|
|
203
203
|
import { createRequire as createRequire3 } from "module";
|
|
204
204
|
import { tmpdir as tmpdir2 } from "os";
|
|
205
|
-
import { dirname as
|
|
205
|
+
import { dirname as dirname11, join as join7 } from "path";
|
|
206
206
|
function getBetterSqlite3ErrorMessage(error) {
|
|
207
207
|
return error instanceof Error ? error.message : String(error);
|
|
208
208
|
}
|
|
@@ -222,14 +222,14 @@ function isBetterSqlite3NodeGypSymlinkConflict(error) {
|
|
|
222
222
|
return message.includes("node_gyp_bins/python3") && message.includes("EEXIST");
|
|
223
223
|
}
|
|
224
224
|
function resolveCurrentRuntimeNpmCliPath() {
|
|
225
|
-
const nodeInstallRoot =
|
|
226
|
-
const npmCliPath =
|
|
227
|
-
return
|
|
225
|
+
const nodeInstallRoot = dirname11(dirname11(process.execPath));
|
|
226
|
+
const npmCliPath = join7(nodeInstallRoot, "lib", "node_modules", "npm", "bin", "npm-cli.js");
|
|
227
|
+
return existsSync7(npmCliPath) ? npmCliPath : null;
|
|
228
228
|
}
|
|
229
229
|
function resolveCurrentRuntimeNodeDir() {
|
|
230
|
-
const nodeInstallRoot =
|
|
231
|
-
const nodeHeaderPath =
|
|
232
|
-
return
|
|
230
|
+
const nodeInstallRoot = dirname11(dirname11(process.execPath));
|
|
231
|
+
const nodeHeaderPath = join7(nodeInstallRoot, "include", "node", "node.h");
|
|
232
|
+
return existsSync7(nodeHeaderPath) ? nodeInstallRoot : null;
|
|
233
233
|
}
|
|
234
234
|
function resolveBetterSqliteRebuildCommand() {
|
|
235
235
|
const npmCliPath = resolveCurrentRuntimeNpmCliPath();
|
|
@@ -250,26 +250,26 @@ function sleepSync(milliseconds) {
|
|
|
250
250
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
|
|
251
251
|
}
|
|
252
252
|
function waitForBetterSqlite3BuildOutput(installRoot, timeoutMs = 3e4) {
|
|
253
|
-
const bindingPath =
|
|
253
|
+
const bindingPath = join7(installRoot, "build", "Release", "better_sqlite3.node");
|
|
254
254
|
const deadline = Date.now() + timeoutMs;
|
|
255
255
|
while (Date.now() < deadline) {
|
|
256
|
-
if (
|
|
256
|
+
if (existsSync7(bindingPath)) {
|
|
257
257
|
return true;
|
|
258
258
|
}
|
|
259
259
|
sleepSync(200);
|
|
260
260
|
}
|
|
261
|
-
return
|
|
261
|
+
return existsSync7(bindingPath);
|
|
262
262
|
}
|
|
263
263
|
function acquireBetterSqlite3RebuildLock(installRoot) {
|
|
264
|
-
const lockRoot =
|
|
264
|
+
const lockRoot = join7(tmpdir2(), "agent-qa-better-sqlite3-rebuild-locks");
|
|
265
265
|
const lockKey = createHash3("sha256").update(`${installRoot}\0${process.version}`).digest("hex");
|
|
266
|
-
const lockDir =
|
|
266
|
+
const lockDir = join7(lockRoot, lockKey);
|
|
267
267
|
const deadline = Date.now() + 12e4;
|
|
268
268
|
mkdirSync(lockRoot, { recursive: true });
|
|
269
269
|
while (true) {
|
|
270
270
|
try {
|
|
271
271
|
mkdirSync(lockDir);
|
|
272
|
-
writeFileSync(
|
|
272
|
+
writeFileSync(join7(lockDir, "owner"), `${process.pid}
|
|
273
273
|
${(/* @__PURE__ */ new Date()).toISOString()}
|
|
274
274
|
${installRoot}
|
|
275
275
|
`);
|
|
@@ -298,13 +298,13 @@ function rebuildBetterSqlite3ForCurrentRuntime(originalError) {
|
|
|
298
298
|
}
|
|
299
299
|
rebuildAttempted = true;
|
|
300
300
|
const packageJsonPath = require2.resolve("better-sqlite3/package.json");
|
|
301
|
-
const installRoot =
|
|
301
|
+
const installRoot = dirname11(packageJsonPath);
|
|
302
302
|
const rebuildCommand = resolveBetterSqliteRebuildCommand();
|
|
303
|
-
const nodeGypDevDir =
|
|
304
|
-
const npmCacheDir =
|
|
303
|
+
const nodeGypDevDir = join7(tmpdir2(), "agent-qa-node-gyp", process.version.replace(/^v/, ""));
|
|
304
|
+
const npmCacheDir = join7(tmpdir2(), "agent-qa-npm-cache");
|
|
305
305
|
const nodeDir = resolveCurrentRuntimeNodeDir();
|
|
306
|
-
const nodeGypBinsPath =
|
|
307
|
-
const buildRoot =
|
|
306
|
+
const nodeGypBinsPath = join7(installRoot, "build", "node_gyp_bins");
|
|
307
|
+
const buildRoot = join7(installRoot, "build");
|
|
308
308
|
const releaseLock = acquireBetterSqlite3RebuildLock(installRoot);
|
|
309
309
|
try {
|
|
310
310
|
clearBetterSqlite3Cache();
|
|
@@ -387,7 +387,7 @@ var init_sqlite = __esm({
|
|
|
387
387
|
|
|
388
388
|
// src/memory/memory-index.ts
|
|
389
389
|
import { readFile as readFile12 } from "fs/promises";
|
|
390
|
-
import { join as
|
|
390
|
+
import { join as join8 } from "path";
|
|
391
391
|
function validatePathComponent(value) {
|
|
392
392
|
return !/\.\./.test(value) && !/[/\\]/.test(value) && !/\0/.test(value);
|
|
393
393
|
}
|
|
@@ -409,13 +409,13 @@ async function buildMemoryIndex(params) {
|
|
|
409
409
|
const insert = db.prepare("INSERT INTO observations(title, content, id, trust) VALUES (?, ?, ?, ?)");
|
|
410
410
|
const dirs = [];
|
|
411
411
|
if (validatePathComponent(params.product)) {
|
|
412
|
-
dirs.push({ path:
|
|
412
|
+
dirs.push({ path: join8(params.memoryRoot, "products", params.product), isSuite: false });
|
|
413
413
|
}
|
|
414
414
|
if (params.suiteId && validatePathComponent(params.suiteId)) {
|
|
415
|
-
dirs.push({ path:
|
|
415
|
+
dirs.push({ path: join8(params.memoryRoot, "suites", params.suiteId), isSuite: true });
|
|
416
416
|
}
|
|
417
417
|
if (validatePathComponent(params.testId)) {
|
|
418
|
-
dirs.push({ path:
|
|
418
|
+
dirs.push({ path: join8(params.memoryRoot, "tests", params.testId), isSuite: false });
|
|
419
419
|
}
|
|
420
420
|
if (dirs.length === 0) {
|
|
421
421
|
console.warn("[memory-index] All path components failed validation", {
|
|
@@ -434,7 +434,7 @@ async function buildMemoryIndex(params) {
|
|
|
434
434
|
for (const { path: dirPath, isSuite } of dirs) {
|
|
435
435
|
const filenames = await listObservations(dirPath);
|
|
436
436
|
for (const filename of filenames) {
|
|
437
|
-
const filePath =
|
|
437
|
+
const filePath = join8(dirPath, filename);
|
|
438
438
|
let raw;
|
|
439
439
|
try {
|
|
440
440
|
raw = await readFile12(filePath, "utf-8");
|
|
@@ -485,7 +485,7 @@ var init_xml_escape = __esm({
|
|
|
485
485
|
|
|
486
486
|
// src/memory/lock-manager.ts
|
|
487
487
|
import { writeFile as writeFile7, readFile as readFile13, unlink as unlink3, mkdir as mkdir7 } from "fs/promises";
|
|
488
|
-
import { dirname as
|
|
488
|
+
import { dirname as dirname12 } from "path";
|
|
489
489
|
var LockManager;
|
|
490
490
|
var init_lock_manager = __esm({
|
|
491
491
|
"src/memory/lock-manager.ts"() {
|
|
@@ -498,7 +498,7 @@ var init_lock_manager = __esm({
|
|
|
498
498
|
this.timeout = timeout;
|
|
499
499
|
}
|
|
500
500
|
async acquire() {
|
|
501
|
-
await mkdir7(
|
|
501
|
+
await mkdir7(dirname12(this.lockPath), { recursive: true });
|
|
502
502
|
const deadline = Date.now() + this.timeout;
|
|
503
503
|
while (Date.now() < deadline) {
|
|
504
504
|
try {
|
|
@@ -630,7 +630,7 @@ var local_provider_exports = {};
|
|
|
630
630
|
__export(local_provider_exports, {
|
|
631
631
|
LocalMemoryProvider: () => LocalMemoryProvider
|
|
632
632
|
});
|
|
633
|
-
import { join as
|
|
633
|
+
import { join as join9, resolve as resolve7, sep as sep2 } from "path";
|
|
634
634
|
import { readdirSync, readFileSync as readFileSync3 } from "fs";
|
|
635
635
|
import { unlink as unlink4 } from "fs/promises";
|
|
636
636
|
var LocalMemoryProvider;
|
|
@@ -657,7 +657,7 @@ var init_local_provider = __esm({
|
|
|
657
657
|
this.minTrust = opts?.minTrust ?? 0.3;
|
|
658
658
|
this.maxInjections = opts?.maxInjections ?? 3;
|
|
659
659
|
this.lockManager = new LockManager(
|
|
660
|
-
|
|
660
|
+
join9(this.memoryRoot, ".curator.lock"),
|
|
661
661
|
opts?.curatorLockTimeout ?? 12e4
|
|
662
662
|
);
|
|
663
663
|
}
|
|
@@ -727,9 +727,9 @@ ${lines.join("\n")}
|
|
|
727
727
|
return writeObservation(this.memoryRoot, tier, scope, data);
|
|
728
728
|
}
|
|
729
729
|
async deleteObservation(tier, scope, id) {
|
|
730
|
-
const filePath =
|
|
731
|
-
const resolved =
|
|
732
|
-
if (!resolved.startsWith(
|
|
730
|
+
const filePath = join9(this.memoryRoot, tier, scope, `${id}.md`);
|
|
731
|
+
const resolved = resolve7(filePath);
|
|
732
|
+
if (!resolved.startsWith(resolve7(this.memoryRoot) + sep2)) {
|
|
733
733
|
throw new Error("Path escapes memory root");
|
|
734
734
|
}
|
|
735
735
|
try {
|
|
@@ -746,7 +746,7 @@ ${lines.join("\n")}
|
|
|
746
746
|
const results = [];
|
|
747
747
|
const tiers = ["products", "suites", "tests"];
|
|
748
748
|
for (const tier of tiers) {
|
|
749
|
-
const tierDir =
|
|
749
|
+
const tierDir = join9(this.memoryRoot, tier);
|
|
750
750
|
let scopes;
|
|
751
751
|
try {
|
|
752
752
|
scopes = readdirSync(tierDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -754,7 +754,7 @@ ${lines.join("\n")}
|
|
|
754
754
|
continue;
|
|
755
755
|
}
|
|
756
756
|
for (const scope of scopes) {
|
|
757
|
-
const scopeDir =
|
|
757
|
+
const scopeDir = join9(tierDir, scope);
|
|
758
758
|
let files;
|
|
759
759
|
try {
|
|
760
760
|
files = readdirSync(scopeDir).filter((f) => f.startsWith("obs_") && f.endsWith(".md"));
|
|
@@ -764,7 +764,7 @@ ${lines.join("\n")}
|
|
|
764
764
|
for (const file of files) {
|
|
765
765
|
let raw;
|
|
766
766
|
try {
|
|
767
|
-
raw = readFileSync3(
|
|
767
|
+
raw = readFileSync3(join9(scopeDir, file), "utf-8");
|
|
768
768
|
} catch {
|
|
769
769
|
continue;
|
|
770
770
|
}
|
|
@@ -799,7 +799,7 @@ ${lines.join("\n")}
|
|
|
799
799
|
const tiers = ["products", "suites", "tests"];
|
|
800
800
|
const toInsert = [];
|
|
801
801
|
for (const tier of tiers) {
|
|
802
|
-
const tierDir =
|
|
802
|
+
const tierDir = join9(this.memoryRoot, tier);
|
|
803
803
|
let scopes;
|
|
804
804
|
try {
|
|
805
805
|
scopes = readdirSync(tierDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -807,7 +807,7 @@ ${lines.join("\n")}
|
|
|
807
807
|
continue;
|
|
808
808
|
}
|
|
809
809
|
for (const scope of scopes) {
|
|
810
|
-
const scopeDir =
|
|
810
|
+
const scopeDir = join9(tierDir, scope);
|
|
811
811
|
let files;
|
|
812
812
|
try {
|
|
813
813
|
files = readdirSync(scopeDir).filter((f) => f.startsWith("obs_") && f.endsWith(".md"));
|
|
@@ -817,7 +817,7 @@ ${lines.join("\n")}
|
|
|
817
817
|
for (const file of files) {
|
|
818
818
|
let raw;
|
|
819
819
|
try {
|
|
820
|
-
raw = readFileSync3(
|
|
820
|
+
raw = readFileSync3(join9(scopeDir, file), "utf-8");
|
|
821
821
|
} catch {
|
|
822
822
|
continue;
|
|
823
823
|
}
|
|
@@ -4674,9 +4674,9 @@ function getHookCommand(runtime, entryFile) {
|
|
|
4674
4674
|
}
|
|
4675
4675
|
}
|
|
4676
4676
|
async function checkDockerAvailable(dockerBin = DEFAULT_DOCKER_BIN) {
|
|
4677
|
-
return new Promise((
|
|
4677
|
+
return new Promise((resolve8) => {
|
|
4678
4678
|
execFile(dockerBin, ["info", "--format", "{{.ID}}"], { timeout: 5e3 }, (err) => {
|
|
4679
|
-
|
|
4679
|
+
resolve8(!err);
|
|
4680
4680
|
});
|
|
4681
4681
|
});
|
|
4682
4682
|
}
|
|
@@ -4789,22 +4789,22 @@ function filterSecretVariables(variables, secretStore) {
|
|
|
4789
4789
|
);
|
|
4790
4790
|
}
|
|
4791
4791
|
function imageExists(dockerBin, image) {
|
|
4792
|
-
return new Promise((
|
|
4792
|
+
return new Promise((resolve8) => {
|
|
4793
4793
|
execFile(dockerBin, ["image", "inspect", image], { timeout: 1e4 }, (err) => {
|
|
4794
|
-
|
|
4794
|
+
resolve8(!err);
|
|
4795
4795
|
});
|
|
4796
4796
|
});
|
|
4797
4797
|
}
|
|
4798
4798
|
function pullImage(dockerBin, image) {
|
|
4799
|
-
return new Promise((
|
|
4799
|
+
return new Promise((resolve8, reject) => {
|
|
4800
4800
|
execFile(dockerBin, ["pull", image], { timeout: 12e4 }, (err) => {
|
|
4801
4801
|
if (err) reject(new Error(`Failed to pull image ${image}: ${err.message}`));
|
|
4802
|
-
else
|
|
4802
|
+
else resolve8();
|
|
4803
4803
|
});
|
|
4804
4804
|
});
|
|
4805
4805
|
}
|
|
4806
4806
|
function execDocker(dockerBin, args, timeout) {
|
|
4807
|
-
return new Promise((
|
|
4807
|
+
return new Promise((resolve8, reject) => {
|
|
4808
4808
|
let stdout = "";
|
|
4809
4809
|
let stderr = "";
|
|
4810
4810
|
let resolved = false;
|
|
@@ -4814,11 +4814,11 @@ function execDocker(dockerBin, args, timeout) {
|
|
|
4814
4814
|
stdout = out ?? "";
|
|
4815
4815
|
stderr = errOut ?? "";
|
|
4816
4816
|
if (err && "killed" in err && err.killed) {
|
|
4817
|
-
|
|
4817
|
+
resolve8({ stdout, stderr: `Hook timed out after ${timeout}ms`, exitCode: 124 });
|
|
4818
4818
|
return;
|
|
4819
4819
|
}
|
|
4820
4820
|
const exitCode = err?.code ?? 0;
|
|
4821
|
-
|
|
4821
|
+
resolve8({ stdout, stderr, exitCode: typeof exitCode === "number" ? exitCode : 1 });
|
|
4822
4822
|
});
|
|
4823
4823
|
});
|
|
4824
4824
|
}
|
|
@@ -6894,14 +6894,41 @@ function resolveMobileRunConfig(input) {
|
|
|
6894
6894
|
};
|
|
6895
6895
|
}
|
|
6896
6896
|
|
|
6897
|
+
// src/mobile/appium-executable.ts
|
|
6898
|
+
import { existsSync as existsSync2 } from "fs";
|
|
6899
|
+
import { dirname as dirname6, join as join3, parse, resolve as resolve2 } from "path";
|
|
6900
|
+
function localAppiumBinaryNames(platform = process.platform) {
|
|
6901
|
+
return platform === "win32" ? ["appium.cmd", "appium.exe", "appium"] : ["appium"];
|
|
6902
|
+
}
|
|
6903
|
+
function resolveAppiumExecutable(opts = {}) {
|
|
6904
|
+
const exists = opts.exists ?? existsSync2;
|
|
6905
|
+
const names = localAppiumBinaryNames(opts.platform ?? process.platform);
|
|
6906
|
+
let current = resolve2(opts.cwd ?? process.cwd());
|
|
6907
|
+
const root = parse(current).root;
|
|
6908
|
+
while (true) {
|
|
6909
|
+
for (const name of names) {
|
|
6910
|
+
const candidate = join3(current, "node_modules", ".bin", name);
|
|
6911
|
+
if (exists(candidate)) {
|
|
6912
|
+
return { command: candidate, source: "local" };
|
|
6913
|
+
}
|
|
6914
|
+
}
|
|
6915
|
+
if (current === root) break;
|
|
6916
|
+
current = dirname6(current);
|
|
6917
|
+
}
|
|
6918
|
+
return { command: "appium", source: "path" };
|
|
6919
|
+
}
|
|
6920
|
+
function formatAppiumInstallGuidance() {
|
|
6921
|
+
return "Install Appium locally with `npm install -D appium` (or `pnpm add -D appium` / `yarn add -D appium`). If you prefer a machine-wide setup, use `npm install -g appium`.";
|
|
6922
|
+
}
|
|
6923
|
+
|
|
6897
6924
|
// src/auth/store.ts
|
|
6898
6925
|
import { readFile as readFile6, writeFile as writeFile4, mkdir as mkdir4, unlink as unlink2 } from "fs/promises";
|
|
6899
|
-
import { dirname as
|
|
6926
|
+
import { dirname as dirname7, join as join4 } from "path";
|
|
6900
6927
|
import { homedir } from "os";
|
|
6901
6928
|
function getAuthPath() {
|
|
6902
6929
|
const xdg = process.env.XDG_DATA_HOME;
|
|
6903
|
-
if (xdg) return
|
|
6904
|
-
return
|
|
6930
|
+
if (xdg) return join4(xdg, "agent-qa", "auth.json");
|
|
6931
|
+
return join4(homedir(), ".agent-qa", "auth.json");
|
|
6905
6932
|
}
|
|
6906
6933
|
async function readAuth(path5) {
|
|
6907
6934
|
const authPath = path5 ?? getAuthPath();
|
|
@@ -6916,7 +6943,7 @@ async function writeAuth(provider, credential, path5) {
|
|
|
6916
6943
|
const authPath = path5 ?? getAuthPath();
|
|
6917
6944
|
const store = await readAuth(authPath);
|
|
6918
6945
|
store[provider] = credential;
|
|
6919
|
-
await mkdir4(
|
|
6946
|
+
await mkdir4(dirname7(authPath), { recursive: true });
|
|
6920
6947
|
await writeFile4(authPath, JSON.stringify(store, null, 2), { mode: 384 });
|
|
6921
6948
|
}
|
|
6922
6949
|
async function removeAuth(provider, path5) {
|
|
@@ -7094,16 +7121,16 @@ async function resolveLLMAuth(configName, llmConfig, authPath) {
|
|
|
7094
7121
|
|
|
7095
7122
|
// src/auth/plugin-loader.ts
|
|
7096
7123
|
import { createRequire } from "module";
|
|
7097
|
-
import { existsSync as
|
|
7098
|
-
import { dirname as
|
|
7124
|
+
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
7125
|
+
import { dirname as dirname8, isAbsolute as isAbsolute2, resolve as resolve3 } from "path";
|
|
7099
7126
|
import { pathToFileURL } from "url";
|
|
7100
7127
|
var loadedDeclarations = /* @__PURE__ */ new Set();
|
|
7101
7128
|
function findPackageJson(packageName, baseDir) {
|
|
7102
|
-
let current =
|
|
7129
|
+
let current = resolve3(baseDir);
|
|
7103
7130
|
while (true) {
|
|
7104
|
-
const candidate =
|
|
7105
|
-
if (
|
|
7106
|
-
const parent =
|
|
7131
|
+
const candidate = resolve3(current, "node_modules", ...packageName.split("/"), "package.json");
|
|
7132
|
+
if (existsSync3(candidate)) return candidate;
|
|
7133
|
+
const parent = dirname8(current);
|
|
7107
7134
|
if (parent === current) return null;
|
|
7108
7135
|
current = parent;
|
|
7109
7136
|
}
|
|
@@ -7131,7 +7158,7 @@ function resolvePackageImportFromBase(packageName, baseDir) {
|
|
|
7131
7158
|
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
7132
7159
|
const target = exportTargetFromPackageJson(pkg);
|
|
7133
7160
|
if (!target || !target.startsWith(".")) return null;
|
|
7134
|
-
return pathToFileURL(
|
|
7161
|
+
return pathToFileURL(resolve3(dirname8(packageJsonPath), target)).href;
|
|
7135
7162
|
}
|
|
7136
7163
|
function packageDeclarationValue(declaration) {
|
|
7137
7164
|
return "package" in declaration && typeof declaration.package === "string" ? declaration.package.trim() : "";
|
|
@@ -7142,24 +7169,24 @@ function pathDeclarationValue(declaration) {
|
|
|
7142
7169
|
function declarationKey(declaration, baseDir) {
|
|
7143
7170
|
const packageName = packageDeclarationValue(declaration);
|
|
7144
7171
|
if (packageName) return `package:${packageName}`;
|
|
7145
|
-
return `path:${
|
|
7172
|
+
return `path:${resolve3(baseDir, pathDeclarationValue(declaration))}`;
|
|
7146
7173
|
}
|
|
7147
7174
|
function resolveModuleSpecifier(declaration, baseDir) {
|
|
7148
7175
|
const packageName = packageDeclarationValue(declaration);
|
|
7149
7176
|
if (packageName) {
|
|
7150
7177
|
const packageImport = resolvePackageImportFromBase(packageName, baseDir);
|
|
7151
7178
|
if (packageImport) return packageImport;
|
|
7152
|
-
const parentUrl = pathToFileURL(
|
|
7179
|
+
const parentUrl = pathToFileURL(resolve3(baseDir, "agent-qa.config.yaml")).href;
|
|
7153
7180
|
try {
|
|
7154
7181
|
const resolveImport = import.meta.resolve;
|
|
7155
7182
|
return resolveImport(packageName, parentUrl);
|
|
7156
7183
|
} catch {
|
|
7157
|
-
const require3 = createRequire(
|
|
7184
|
+
const require3 = createRequire(resolve3(baseDir, "agent-qa.config.yaml"));
|
|
7158
7185
|
return pathToFileURL(require3.resolve(packageName)).href;
|
|
7159
7186
|
}
|
|
7160
7187
|
}
|
|
7161
7188
|
const pluginPath = pathDeclarationValue(declaration);
|
|
7162
|
-
const absolutePath = isAbsolute2(pluginPath) ? pluginPath :
|
|
7189
|
+
const absolutePath = isAbsolute2(pluginPath) ? pluginPath : resolve3(baseDir, pluginPath);
|
|
7163
7190
|
return pathToFileURL(absolutePath).href;
|
|
7164
7191
|
}
|
|
7165
7192
|
async function resolvePluginBundle(module, source) {
|
|
@@ -7548,7 +7575,7 @@ function mapMobileCapabilities(match, platform, credentials, opts) {
|
|
|
7548
7575
|
}
|
|
7549
7576
|
|
|
7550
7577
|
// src/farm/browserstack.ts
|
|
7551
|
-
import { existsSync as
|
|
7578
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
7552
7579
|
import { basename as basename2, resolve as resolvePath3 } from "path";
|
|
7553
7580
|
function isAbsoluteAppValue(value) {
|
|
7554
7581
|
return value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
@@ -7570,7 +7597,7 @@ async function resolveBrowserStackApp(config) {
|
|
|
7570
7597
|
const appBaseDir = config.appBaseDir ?? process.cwd();
|
|
7571
7598
|
const resolvedPath = resolvePath3(appBaseDir, config.app);
|
|
7572
7599
|
const mustExist = isExplicitRelativePath(config.app);
|
|
7573
|
-
if (!
|
|
7600
|
+
if (!existsSync4(resolvedPath)) {
|
|
7574
7601
|
if (mustExist) {
|
|
7575
7602
|
throw new MobileSetupError({
|
|
7576
7603
|
category: "app-install",
|
|
@@ -7762,7 +7789,7 @@ async function runHooks(hooks, options = {}) {
|
|
|
7762
7789
|
init_observation_io();
|
|
7763
7790
|
import { generateText as generateText5, Output as Output4 } from "ai";
|
|
7764
7791
|
import { z as z19 } from "zod";
|
|
7765
|
-
import { join as
|
|
7792
|
+
import { join as join6 } from "path";
|
|
7766
7793
|
import { access as access2, readFile as readFile7, readdir as readdir3 } from "fs/promises";
|
|
7767
7794
|
|
|
7768
7795
|
// src/memory/observation-id.ts
|
|
@@ -7910,7 +7937,7 @@ function collectKnownObservationIds(injectedObservationIds, dedupMatches) {
|
|
|
7910
7937
|
async function findObservationFile(memoryRoot, id) {
|
|
7911
7938
|
const tiers = ["products", "suites", "tests"];
|
|
7912
7939
|
for (const tier of tiers) {
|
|
7913
|
-
const tierDir =
|
|
7940
|
+
const tierDir = join6(memoryRoot, tier);
|
|
7914
7941
|
let scopes;
|
|
7915
7942
|
try {
|
|
7916
7943
|
scopes = await readdir3(tierDir);
|
|
@@ -7918,7 +7945,7 @@ async function findObservationFile(memoryRoot, id) {
|
|
|
7918
7945
|
continue;
|
|
7919
7946
|
}
|
|
7920
7947
|
for (const scope of scopes) {
|
|
7921
|
-
const filePath =
|
|
7948
|
+
const filePath = join6(tierDir, scope, `${id}.md`);
|
|
7922
7949
|
try {
|
|
7923
7950
|
await access2(filePath);
|
|
7924
7951
|
return { tier, scope, filePath };
|
|
@@ -8147,11 +8174,11 @@ ${userMessage}`;
|
|
|
8147
8174
|
}
|
|
8148
8175
|
if (ctx.suiteId && ctx.suiteContext) {
|
|
8149
8176
|
try {
|
|
8150
|
-
const suiteDir =
|
|
8177
|
+
const suiteDir = join6(ctx.memoryRoot, "suites", ctx.suiteId);
|
|
8151
8178
|
const files = await listObservations(suiteDir);
|
|
8152
8179
|
for (const file of files) {
|
|
8153
8180
|
try {
|
|
8154
|
-
const filePath =
|
|
8181
|
+
const filePath = join6(suiteDir, file);
|
|
8155
8182
|
const raw = await readFile7(filePath, "utf-8");
|
|
8156
8183
|
const { data } = parseObservation(raw, file);
|
|
8157
8184
|
if (!data || !("suite_snapshot" in data)) continue;
|
|
@@ -8615,6 +8642,7 @@ async function runSuite(suite, testEntries, config) {
|
|
|
8615
8642
|
inlineHookSandboxOptions: config.sandboxOptions,
|
|
8616
8643
|
secretStore: config.secretStore,
|
|
8617
8644
|
secretRedactor: config.secretRedactor,
|
|
8645
|
+
accessibility: config.accessibility,
|
|
8618
8646
|
logCapture: config.logCapture,
|
|
8619
8647
|
screenshotSize: config.screenshotSize,
|
|
8620
8648
|
effectiveResolution: config.effectiveResolution,
|
|
@@ -8682,7 +8710,8 @@ async function runSuite(suite, testEntries, config) {
|
|
|
8682
8710
|
timeouts: config.timeouts,
|
|
8683
8711
|
logger: config.logger,
|
|
8684
8712
|
secretStore: config.secretStore,
|
|
8685
|
-
secretRedactor: config.secretRedactor
|
|
8713
|
+
secretRedactor: config.secretRedactor,
|
|
8714
|
+
accessibility: config.accessibility
|
|
8686
8715
|
}, filePath);
|
|
8687
8716
|
if (config.circuitBreaker) {
|
|
8688
8717
|
config.circuitBreaker.record({ withMemory: false, passed: ablationResult.status === "passed" });
|
|
@@ -8853,14 +8882,14 @@ async function runSuite(suite, testEntries, config) {
|
|
|
8853
8882
|
|
|
8854
8883
|
// src/validation/validate.ts
|
|
8855
8884
|
import { readFile as readFile10 } from "fs/promises";
|
|
8856
|
-
import { existsSync as
|
|
8885
|
+
import { existsSync as existsSync6 } from "fs";
|
|
8857
8886
|
import path4 from "path";
|
|
8858
8887
|
import { parse as parseYaml4, LineCounter as LineCounter2, parseDocument } from "yaml";
|
|
8859
8888
|
import { glob as glob2 } from "glob";
|
|
8860
8889
|
|
|
8861
8890
|
// src/hooks/parser.ts
|
|
8862
8891
|
import { readFile as readFile9 } from "fs/promises";
|
|
8863
|
-
import { dirname as
|
|
8892
|
+
import { dirname as dirname9, resolve as resolve5 } from "path";
|
|
8864
8893
|
import { parse as parseYaml3 } from "yaml";
|
|
8865
8894
|
async function parseHooksFile(filePath) {
|
|
8866
8895
|
let raw;
|
|
@@ -8882,12 +8911,12 @@ async function parseHooksFile(filePath) {
|
|
|
8882
8911
|
);
|
|
8883
8912
|
return { hooks: [], errors };
|
|
8884
8913
|
}
|
|
8885
|
-
const dir =
|
|
8914
|
+
const dir = dirname9(filePath);
|
|
8886
8915
|
const hooks = result.data.hooks.map((h) => ({
|
|
8887
8916
|
...h,
|
|
8888
|
-
file:
|
|
8889
|
-
deps: h.deps.map((d) =>
|
|
8890
|
-
packageFile: h.packageFile ?
|
|
8917
|
+
file: resolve5(dir, h.file),
|
|
8918
|
+
deps: h.deps.map((d) => resolve5(dir, d)),
|
|
8919
|
+
packageFile: h.packageFile ? resolve5(dir, h.packageFile) : void 0
|
|
8891
8920
|
}));
|
|
8892
8921
|
return { hooks, errors: [] };
|
|
8893
8922
|
}
|
|
@@ -8902,7 +8931,7 @@ function isPathInsideDir(childPath, parentDir) {
|
|
|
8902
8931
|
}
|
|
8903
8932
|
|
|
8904
8933
|
// src/workspace/workspace-paths.ts
|
|
8905
|
-
import { existsSync as
|
|
8934
|
+
import { existsSync as existsSync5 } from "fs";
|
|
8906
8935
|
import path3 from "path";
|
|
8907
8936
|
import { glob } from "glob";
|
|
8908
8937
|
function toForwardSlash(value) {
|
|
@@ -9015,7 +9044,7 @@ function resolveWorkspacePaths(input) {
|
|
|
9015
9044
|
["workspace.envFile", resolved.envFile],
|
|
9016
9045
|
["workspace.secretsFile", resolved.secretsFile]
|
|
9017
9046
|
]) {
|
|
9018
|
-
if (!
|
|
9047
|
+
if (!existsSync5(file.absolutePath)) {
|
|
9019
9048
|
throw new Error(`Configured workspace file not found: ${key} -> ${file.absolutePath}`);
|
|
9020
9049
|
}
|
|
9021
9050
|
}
|
|
@@ -9046,7 +9075,7 @@ async function resolveWorkspaceFileTarget(input) {
|
|
|
9046
9075
|
})) {
|
|
9047
9076
|
throw new Error(`Workspace ${input.kind} file is not matched by configured workspace patterns: ${workspaceRelativePath}`);
|
|
9048
9077
|
}
|
|
9049
|
-
if (input.requireExisting && !
|
|
9078
|
+
if (input.requireExisting && !existsSync5(absolutePath)) {
|
|
9050
9079
|
throw new Error(`Workspace ${input.kind} file not found: ${workspaceRelativePath}`);
|
|
9051
9080
|
}
|
|
9052
9081
|
return {
|
|
@@ -9168,7 +9197,7 @@ function validateSuiteFile(filePath, content, basedir) {
|
|
|
9168
9197
|
continue;
|
|
9169
9198
|
}
|
|
9170
9199
|
const resolved = path4.resolve(basedir, testPath);
|
|
9171
|
-
if (!
|
|
9200
|
+
if (!existsSync6(resolved)) {
|
|
9172
9201
|
let line = 1;
|
|
9173
9202
|
let column = 1;
|
|
9174
9203
|
const seqNode = testsNode;
|
|
@@ -9203,7 +9232,7 @@ function validateFilename(filePath) {
|
|
|
9203
9232
|
}
|
|
9204
9233
|
async function validateHooksFile(hooksFilePath, options = {}) {
|
|
9205
9234
|
const diagnostics = [];
|
|
9206
|
-
if (!
|
|
9235
|
+
if (!existsSync6(hooksFilePath)) {
|
|
9207
9236
|
if (options.required) {
|
|
9208
9237
|
return [buildDiagnostic(hooksFilePath, `Configured hooks file not found: ${hooksFilePath}`)];
|
|
9209
9238
|
}
|
|
@@ -9223,7 +9252,7 @@ async function validateHooksFile(hooksFilePath, options = {}) {
|
|
|
9223
9252
|
return diagnostics;
|
|
9224
9253
|
}
|
|
9225
9254
|
for (const hook of hooks) {
|
|
9226
|
-
if (!
|
|
9255
|
+
if (!existsSync6(hook.file)) {
|
|
9227
9256
|
diagnostics.push({
|
|
9228
9257
|
file: hooksFilePath,
|
|
9229
9258
|
line: 1,
|
|
@@ -9233,7 +9262,7 @@ async function validateHooksFile(hooksFilePath, options = {}) {
|
|
|
9233
9262
|
});
|
|
9234
9263
|
}
|
|
9235
9264
|
for (const dep of hook.deps) {
|
|
9236
|
-
if (!
|
|
9265
|
+
if (!existsSync6(dep)) {
|
|
9237
9266
|
diagnostics.push({
|
|
9238
9267
|
file: hooksFilePath,
|
|
9239
9268
|
line: 1,
|
|
@@ -9243,7 +9272,7 @@ async function validateHooksFile(hooksFilePath, options = {}) {
|
|
|
9243
9272
|
});
|
|
9244
9273
|
}
|
|
9245
9274
|
}
|
|
9246
|
-
if (hook.packageFile && !
|
|
9275
|
+
if (hook.packageFile && !existsSync6(hook.packageFile)) {
|
|
9247
9276
|
diagnostics.push({
|
|
9248
9277
|
file: hooksFilePath,
|
|
9249
9278
|
line: 1,
|
|
@@ -9358,7 +9387,7 @@ function validateRequiredWorkspaceFiles(workspace) {
|
|
|
9358
9387
|
["workspace.envFile", workspace.envFile],
|
|
9359
9388
|
["workspace.secretsFile", workspace.secretsFile]
|
|
9360
9389
|
]) {
|
|
9361
|
-
if (!
|
|
9390
|
+
if (!existsSync6(file.absolutePath)) {
|
|
9362
9391
|
diagnostics.push(buildDiagnostic(file.absolutePath, `Configured workspace file not found: ${key} -> ${file.absolutePath}`));
|
|
9363
9392
|
}
|
|
9364
9393
|
}
|
|
@@ -9419,7 +9448,7 @@ async function validateProject(options = {}) {
|
|
|
9419
9448
|
if (d.severity === "error") result.errorCount++;
|
|
9420
9449
|
else result.warningCount++;
|
|
9421
9450
|
}
|
|
9422
|
-
if (hooksFilePath && hooksDiags.length === 0 &&
|
|
9451
|
+
if (hooksFilePath && hooksDiags.length === 0 && existsSync6(hooksFilePath)) {
|
|
9423
9452
|
const { hooks } = await parseHooksFile(hooksFilePath);
|
|
9424
9453
|
const hookIds = new Set(hooks.map((h) => h.id));
|
|
9425
9454
|
for (const filePath of filePaths) {
|
|
@@ -9525,7 +9554,7 @@ function warnIfOutOfBounds(coords, viewport, actionType) {
|
|
|
9525
9554
|
|
|
9526
9555
|
// src/hooks/migrate-hook-refs.ts
|
|
9527
9556
|
import { mkdir as mkdir6, readFile as readFile11, rm as rm2, writeFile as writeFile6 } from "fs/promises";
|
|
9528
|
-
import { dirname as
|
|
9557
|
+
import { dirname as dirname10, relative, resolve as resolve6 } from "path";
|
|
9529
9558
|
import { parse as parseYaml5, stringify as stringifyYaml2 } from "yaml";
|
|
9530
9559
|
import { z as z20 } from "zod";
|
|
9531
9560
|
import { generateHookId, isCanonicalHookId as isCanonicalHookId5 } from "@vostride/agent-qa-ids";
|
|
@@ -9631,7 +9660,7 @@ function rewriteSteps(owner, filePath, hookNameToId, hookIds, errors) {
|
|
|
9631
9660
|
return changed;
|
|
9632
9661
|
}
|
|
9633
9662
|
async function resolveWorkspaceMigrationContext(workspaceDir, configPath) {
|
|
9634
|
-
const resolvedConfigPath = configPath ?
|
|
9663
|
+
const resolvedConfigPath = configPath ? resolve6(configPath) : resolve6(workspaceDir, "agent-qa.config.yaml");
|
|
9635
9664
|
const rawConfig = await readFile11(resolvedConfigPath, "utf-8");
|
|
9636
9665
|
const parsedConfig = parseYaml5(rawConfig);
|
|
9637
9666
|
const configResult = AgentQaConfigSchema.safeParse(parsedConfig);
|
|
@@ -9658,7 +9687,7 @@ async function resolveWorkspaceMigrationContext(workspaceDir, configPath) {
|
|
|
9658
9687
|
};
|
|
9659
9688
|
}
|
|
9660
9689
|
async function auditWorkspaceHookReferenceMigration(options) {
|
|
9661
|
-
const workspaceDir =
|
|
9690
|
+
const workspaceDir = resolve6(options.workspaceDir);
|
|
9662
9691
|
const errors = [];
|
|
9663
9692
|
let context;
|
|
9664
9693
|
try {
|
|
@@ -9726,7 +9755,7 @@ async function auditWorkspaceHookReferenceMigration(options) {
|
|
|
9726
9755
|
});
|
|
9727
9756
|
}
|
|
9728
9757
|
for (const relativePath of context.targetFiles) {
|
|
9729
|
-
const absolutePath =
|
|
9758
|
+
const absolutePath = resolve6(context.configDir, relativePath);
|
|
9730
9759
|
let parsedFile;
|
|
9731
9760
|
try {
|
|
9732
9761
|
parsedFile = parseYaml5(await readFile11(absolutePath, "utf-8"));
|
|
@@ -9767,14 +9796,14 @@ async function applyWorkspaceHookReferenceMigration(options) {
|
|
|
9767
9796
|
if (!audit.ok) {
|
|
9768
9797
|
throw new HookReferenceMigrationError(audit.errors);
|
|
9769
9798
|
}
|
|
9770
|
-
const workspaceDir = options.configPath ?
|
|
9799
|
+
const workspaceDir = options.configPath ? dirname10(resolve6(options.configPath)) : resolve6(options.workspaceDir);
|
|
9771
9800
|
for (const rewrite of audit.rewriteFiles) {
|
|
9772
|
-
const outputPath =
|
|
9773
|
-
await mkdir6(
|
|
9801
|
+
const outputPath = resolve6(workspaceDir, rewrite.path);
|
|
9802
|
+
await mkdir6(dirname10(outputPath), { recursive: true });
|
|
9774
9803
|
await writeFile6(outputPath, rewrite.content, "utf-8");
|
|
9775
9804
|
}
|
|
9776
9805
|
for (const cleanupTarget of audit.cleanupTargets) {
|
|
9777
|
-
await rm2(
|
|
9806
|
+
await rm2(resolve6(workspaceDir, cleanupTarget), { recursive: true, force: true });
|
|
9778
9807
|
}
|
|
9779
9808
|
return audit;
|
|
9780
9809
|
}
|
|
@@ -9845,12 +9874,12 @@ var RUN_ARTIFACT_SCHEMA_VERSION = 1;
|
|
|
9845
9874
|
// src/analytics/identity.ts
|
|
9846
9875
|
import { chmod, mkdir as mkdir8, readFile as readFile15, writeFile as writeFile8 } from "fs/promises";
|
|
9847
9876
|
import { homedir as homedir2 } from "os";
|
|
9848
|
-
import { dirname as
|
|
9877
|
+
import { dirname as dirname13, join as join10 } from "path";
|
|
9849
9878
|
import { idAgent } from "id-agent";
|
|
9850
9879
|
var LOCAL_ANALYTICS_ID_PATTERN = /^u_([a-z]+-){9}[a-z]+$/;
|
|
9851
9880
|
function getAnalyticsIdentityPath(env = process.env, homeDir = homedir2()) {
|
|
9852
|
-
if (env.XDG_DATA_HOME) return
|
|
9853
|
-
return
|
|
9881
|
+
if (env.XDG_DATA_HOME) return join10(env.XDG_DATA_HOME, "agent-qa", "analytics.json");
|
|
9882
|
+
return join10(homeDir, ".agent-qa", "analytics.json");
|
|
9854
9883
|
}
|
|
9855
9884
|
function generateAnalyticsUserId() {
|
|
9856
9885
|
return idAgent({ prefix: "u", words: 10 });
|
|
@@ -9882,7 +9911,7 @@ async function readAnalyticsIdentity(path5) {
|
|
|
9882
9911
|
}
|
|
9883
9912
|
async function writeAnalyticsIdentity(distinctId, path5, isInternal = false) {
|
|
9884
9913
|
const identityPath = path5 ?? getAnalyticsIdentityPath();
|
|
9885
|
-
await mkdir8(
|
|
9914
|
+
await mkdir8(dirname13(identityPath), { recursive: true });
|
|
9886
9915
|
await writeFile8(identityPath, JSON.stringify({
|
|
9887
9916
|
distinctId,
|
|
9888
9917
|
is_internal: isInternal
|
|
@@ -10992,6 +11021,7 @@ export {
|
|
|
10992
11021
|
findSecretTemplates,
|
|
10993
11022
|
findUnresolvedTemplates,
|
|
10994
11023
|
flushAnalytics,
|
|
11024
|
+
formatAppiumInstallGuidance,
|
|
10995
11025
|
formatDiagnostics,
|
|
10996
11026
|
formatDiagnosticsPlain,
|
|
10997
11027
|
formatLogEntry,
|
|
@@ -11026,6 +11056,7 @@ export {
|
|
|
11026
11056
|
listObservations,
|
|
11027
11057
|
listProviders,
|
|
11028
11058
|
loadLLMAuthPlugins,
|
|
11059
|
+
localAppiumBinaryNames,
|
|
11029
11060
|
mapMobileCapabilities,
|
|
11030
11061
|
mapWebCapabilities,
|
|
11031
11062
|
mergeRunAttributes,
|
|
@@ -11052,6 +11083,7 @@ export {
|
|
|
11052
11083
|
resolveAnalyticsAgentProduct,
|
|
11053
11084
|
resolveAnalyticsIdentity,
|
|
11054
11085
|
resolveAnalyticsStandardProperties,
|
|
11086
|
+
resolveAppiumExecutable,
|
|
11055
11087
|
resolveBetterSqliteRebuildCommand,
|
|
11056
11088
|
resolveFarmCredentials,
|
|
11057
11089
|
resolveFileReferences,
|