@vostride/agent-qa-core 0.1.9 → 0.1.10
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 +179 -146
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +132 -102
- 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;
|
|
@@ -8853,14 +8880,14 @@ async function runSuite(suite, testEntries, config) {
|
|
|
8853
8880
|
|
|
8854
8881
|
// src/validation/validate.ts
|
|
8855
8882
|
import { readFile as readFile10 } from "fs/promises";
|
|
8856
|
-
import { existsSync as
|
|
8883
|
+
import { existsSync as existsSync6 } from "fs";
|
|
8857
8884
|
import path4 from "path";
|
|
8858
8885
|
import { parse as parseYaml4, LineCounter as LineCounter2, parseDocument } from "yaml";
|
|
8859
8886
|
import { glob as glob2 } from "glob";
|
|
8860
8887
|
|
|
8861
8888
|
// src/hooks/parser.ts
|
|
8862
8889
|
import { readFile as readFile9 } from "fs/promises";
|
|
8863
|
-
import { dirname as
|
|
8890
|
+
import { dirname as dirname9, resolve as resolve5 } from "path";
|
|
8864
8891
|
import { parse as parseYaml3 } from "yaml";
|
|
8865
8892
|
async function parseHooksFile(filePath) {
|
|
8866
8893
|
let raw;
|
|
@@ -8882,12 +8909,12 @@ async function parseHooksFile(filePath) {
|
|
|
8882
8909
|
);
|
|
8883
8910
|
return { hooks: [], errors };
|
|
8884
8911
|
}
|
|
8885
|
-
const dir =
|
|
8912
|
+
const dir = dirname9(filePath);
|
|
8886
8913
|
const hooks = result.data.hooks.map((h) => ({
|
|
8887
8914
|
...h,
|
|
8888
|
-
file:
|
|
8889
|
-
deps: h.deps.map((d) =>
|
|
8890
|
-
packageFile: h.packageFile ?
|
|
8915
|
+
file: resolve5(dir, h.file),
|
|
8916
|
+
deps: h.deps.map((d) => resolve5(dir, d)),
|
|
8917
|
+
packageFile: h.packageFile ? resolve5(dir, h.packageFile) : void 0
|
|
8891
8918
|
}));
|
|
8892
8919
|
return { hooks, errors: [] };
|
|
8893
8920
|
}
|
|
@@ -8902,7 +8929,7 @@ function isPathInsideDir(childPath, parentDir) {
|
|
|
8902
8929
|
}
|
|
8903
8930
|
|
|
8904
8931
|
// src/workspace/workspace-paths.ts
|
|
8905
|
-
import { existsSync as
|
|
8932
|
+
import { existsSync as existsSync5 } from "fs";
|
|
8906
8933
|
import path3 from "path";
|
|
8907
8934
|
import { glob } from "glob";
|
|
8908
8935
|
function toForwardSlash(value) {
|
|
@@ -9015,7 +9042,7 @@ function resolveWorkspacePaths(input) {
|
|
|
9015
9042
|
["workspace.envFile", resolved.envFile],
|
|
9016
9043
|
["workspace.secretsFile", resolved.secretsFile]
|
|
9017
9044
|
]) {
|
|
9018
|
-
if (!
|
|
9045
|
+
if (!existsSync5(file.absolutePath)) {
|
|
9019
9046
|
throw new Error(`Configured workspace file not found: ${key} -> ${file.absolutePath}`);
|
|
9020
9047
|
}
|
|
9021
9048
|
}
|
|
@@ -9046,7 +9073,7 @@ async function resolveWorkspaceFileTarget(input) {
|
|
|
9046
9073
|
})) {
|
|
9047
9074
|
throw new Error(`Workspace ${input.kind} file is not matched by configured workspace patterns: ${workspaceRelativePath}`);
|
|
9048
9075
|
}
|
|
9049
|
-
if (input.requireExisting && !
|
|
9076
|
+
if (input.requireExisting && !existsSync5(absolutePath)) {
|
|
9050
9077
|
throw new Error(`Workspace ${input.kind} file not found: ${workspaceRelativePath}`);
|
|
9051
9078
|
}
|
|
9052
9079
|
return {
|
|
@@ -9168,7 +9195,7 @@ function validateSuiteFile(filePath, content, basedir) {
|
|
|
9168
9195
|
continue;
|
|
9169
9196
|
}
|
|
9170
9197
|
const resolved = path4.resolve(basedir, testPath);
|
|
9171
|
-
if (!
|
|
9198
|
+
if (!existsSync6(resolved)) {
|
|
9172
9199
|
let line = 1;
|
|
9173
9200
|
let column = 1;
|
|
9174
9201
|
const seqNode = testsNode;
|
|
@@ -9203,7 +9230,7 @@ function validateFilename(filePath) {
|
|
|
9203
9230
|
}
|
|
9204
9231
|
async function validateHooksFile(hooksFilePath, options = {}) {
|
|
9205
9232
|
const diagnostics = [];
|
|
9206
|
-
if (!
|
|
9233
|
+
if (!existsSync6(hooksFilePath)) {
|
|
9207
9234
|
if (options.required) {
|
|
9208
9235
|
return [buildDiagnostic(hooksFilePath, `Configured hooks file not found: ${hooksFilePath}`)];
|
|
9209
9236
|
}
|
|
@@ -9223,7 +9250,7 @@ async function validateHooksFile(hooksFilePath, options = {}) {
|
|
|
9223
9250
|
return diagnostics;
|
|
9224
9251
|
}
|
|
9225
9252
|
for (const hook of hooks) {
|
|
9226
|
-
if (!
|
|
9253
|
+
if (!existsSync6(hook.file)) {
|
|
9227
9254
|
diagnostics.push({
|
|
9228
9255
|
file: hooksFilePath,
|
|
9229
9256
|
line: 1,
|
|
@@ -9233,7 +9260,7 @@ async function validateHooksFile(hooksFilePath, options = {}) {
|
|
|
9233
9260
|
});
|
|
9234
9261
|
}
|
|
9235
9262
|
for (const dep of hook.deps) {
|
|
9236
|
-
if (!
|
|
9263
|
+
if (!existsSync6(dep)) {
|
|
9237
9264
|
diagnostics.push({
|
|
9238
9265
|
file: hooksFilePath,
|
|
9239
9266
|
line: 1,
|
|
@@ -9243,7 +9270,7 @@ async function validateHooksFile(hooksFilePath, options = {}) {
|
|
|
9243
9270
|
});
|
|
9244
9271
|
}
|
|
9245
9272
|
}
|
|
9246
|
-
if (hook.packageFile && !
|
|
9273
|
+
if (hook.packageFile && !existsSync6(hook.packageFile)) {
|
|
9247
9274
|
diagnostics.push({
|
|
9248
9275
|
file: hooksFilePath,
|
|
9249
9276
|
line: 1,
|
|
@@ -9358,7 +9385,7 @@ function validateRequiredWorkspaceFiles(workspace) {
|
|
|
9358
9385
|
["workspace.envFile", workspace.envFile],
|
|
9359
9386
|
["workspace.secretsFile", workspace.secretsFile]
|
|
9360
9387
|
]) {
|
|
9361
|
-
if (!
|
|
9388
|
+
if (!existsSync6(file.absolutePath)) {
|
|
9362
9389
|
diagnostics.push(buildDiagnostic(file.absolutePath, `Configured workspace file not found: ${key} -> ${file.absolutePath}`));
|
|
9363
9390
|
}
|
|
9364
9391
|
}
|
|
@@ -9419,7 +9446,7 @@ async function validateProject(options = {}) {
|
|
|
9419
9446
|
if (d.severity === "error") result.errorCount++;
|
|
9420
9447
|
else result.warningCount++;
|
|
9421
9448
|
}
|
|
9422
|
-
if (hooksFilePath && hooksDiags.length === 0 &&
|
|
9449
|
+
if (hooksFilePath && hooksDiags.length === 0 && existsSync6(hooksFilePath)) {
|
|
9423
9450
|
const { hooks } = await parseHooksFile(hooksFilePath);
|
|
9424
9451
|
const hookIds = new Set(hooks.map((h) => h.id));
|
|
9425
9452
|
for (const filePath of filePaths) {
|
|
@@ -9525,7 +9552,7 @@ function warnIfOutOfBounds(coords, viewport, actionType) {
|
|
|
9525
9552
|
|
|
9526
9553
|
// src/hooks/migrate-hook-refs.ts
|
|
9527
9554
|
import { mkdir as mkdir6, readFile as readFile11, rm as rm2, writeFile as writeFile6 } from "fs/promises";
|
|
9528
|
-
import { dirname as
|
|
9555
|
+
import { dirname as dirname10, relative, resolve as resolve6 } from "path";
|
|
9529
9556
|
import { parse as parseYaml5, stringify as stringifyYaml2 } from "yaml";
|
|
9530
9557
|
import { z as z20 } from "zod";
|
|
9531
9558
|
import { generateHookId, isCanonicalHookId as isCanonicalHookId5 } from "@vostride/agent-qa-ids";
|
|
@@ -9631,7 +9658,7 @@ function rewriteSteps(owner, filePath, hookNameToId, hookIds, errors) {
|
|
|
9631
9658
|
return changed;
|
|
9632
9659
|
}
|
|
9633
9660
|
async function resolveWorkspaceMigrationContext(workspaceDir, configPath) {
|
|
9634
|
-
const resolvedConfigPath = configPath ?
|
|
9661
|
+
const resolvedConfigPath = configPath ? resolve6(configPath) : resolve6(workspaceDir, "agent-qa.config.yaml");
|
|
9635
9662
|
const rawConfig = await readFile11(resolvedConfigPath, "utf-8");
|
|
9636
9663
|
const parsedConfig = parseYaml5(rawConfig);
|
|
9637
9664
|
const configResult = AgentQaConfigSchema.safeParse(parsedConfig);
|
|
@@ -9658,7 +9685,7 @@ async function resolveWorkspaceMigrationContext(workspaceDir, configPath) {
|
|
|
9658
9685
|
};
|
|
9659
9686
|
}
|
|
9660
9687
|
async function auditWorkspaceHookReferenceMigration(options) {
|
|
9661
|
-
const workspaceDir =
|
|
9688
|
+
const workspaceDir = resolve6(options.workspaceDir);
|
|
9662
9689
|
const errors = [];
|
|
9663
9690
|
let context;
|
|
9664
9691
|
try {
|
|
@@ -9726,7 +9753,7 @@ async function auditWorkspaceHookReferenceMigration(options) {
|
|
|
9726
9753
|
});
|
|
9727
9754
|
}
|
|
9728
9755
|
for (const relativePath of context.targetFiles) {
|
|
9729
|
-
const absolutePath =
|
|
9756
|
+
const absolutePath = resolve6(context.configDir, relativePath);
|
|
9730
9757
|
let parsedFile;
|
|
9731
9758
|
try {
|
|
9732
9759
|
parsedFile = parseYaml5(await readFile11(absolutePath, "utf-8"));
|
|
@@ -9767,14 +9794,14 @@ async function applyWorkspaceHookReferenceMigration(options) {
|
|
|
9767
9794
|
if (!audit.ok) {
|
|
9768
9795
|
throw new HookReferenceMigrationError(audit.errors);
|
|
9769
9796
|
}
|
|
9770
|
-
const workspaceDir = options.configPath ?
|
|
9797
|
+
const workspaceDir = options.configPath ? dirname10(resolve6(options.configPath)) : resolve6(options.workspaceDir);
|
|
9771
9798
|
for (const rewrite of audit.rewriteFiles) {
|
|
9772
|
-
const outputPath =
|
|
9773
|
-
await mkdir6(
|
|
9799
|
+
const outputPath = resolve6(workspaceDir, rewrite.path);
|
|
9800
|
+
await mkdir6(dirname10(outputPath), { recursive: true });
|
|
9774
9801
|
await writeFile6(outputPath, rewrite.content, "utf-8");
|
|
9775
9802
|
}
|
|
9776
9803
|
for (const cleanupTarget of audit.cleanupTargets) {
|
|
9777
|
-
await rm2(
|
|
9804
|
+
await rm2(resolve6(workspaceDir, cleanupTarget), { recursive: true, force: true });
|
|
9778
9805
|
}
|
|
9779
9806
|
return audit;
|
|
9780
9807
|
}
|
|
@@ -9845,12 +9872,12 @@ var RUN_ARTIFACT_SCHEMA_VERSION = 1;
|
|
|
9845
9872
|
// src/analytics/identity.ts
|
|
9846
9873
|
import { chmod, mkdir as mkdir8, readFile as readFile15, writeFile as writeFile8 } from "fs/promises";
|
|
9847
9874
|
import { homedir as homedir2 } from "os";
|
|
9848
|
-
import { dirname as
|
|
9875
|
+
import { dirname as dirname13, join as join10 } from "path";
|
|
9849
9876
|
import { idAgent } from "id-agent";
|
|
9850
9877
|
var LOCAL_ANALYTICS_ID_PATTERN = /^u_([a-z]+-){9}[a-z]+$/;
|
|
9851
9878
|
function getAnalyticsIdentityPath(env = process.env, homeDir = homedir2()) {
|
|
9852
|
-
if (env.XDG_DATA_HOME) return
|
|
9853
|
-
return
|
|
9879
|
+
if (env.XDG_DATA_HOME) return join10(env.XDG_DATA_HOME, "agent-qa", "analytics.json");
|
|
9880
|
+
return join10(homeDir, ".agent-qa", "analytics.json");
|
|
9854
9881
|
}
|
|
9855
9882
|
function generateAnalyticsUserId() {
|
|
9856
9883
|
return idAgent({ prefix: "u", words: 10 });
|
|
@@ -9882,7 +9909,7 @@ async function readAnalyticsIdentity(path5) {
|
|
|
9882
9909
|
}
|
|
9883
9910
|
async function writeAnalyticsIdentity(distinctId, path5, isInternal = false) {
|
|
9884
9911
|
const identityPath = path5 ?? getAnalyticsIdentityPath();
|
|
9885
|
-
await mkdir8(
|
|
9912
|
+
await mkdir8(dirname13(identityPath), { recursive: true });
|
|
9886
9913
|
await writeFile8(identityPath, JSON.stringify({
|
|
9887
9914
|
distinctId,
|
|
9888
9915
|
is_internal: isInternal
|
|
@@ -10992,6 +11019,7 @@ export {
|
|
|
10992
11019
|
findSecretTemplates,
|
|
10993
11020
|
findUnresolvedTemplates,
|
|
10994
11021
|
flushAnalytics,
|
|
11022
|
+
formatAppiumInstallGuidance,
|
|
10995
11023
|
formatDiagnostics,
|
|
10996
11024
|
formatDiagnosticsPlain,
|
|
10997
11025
|
formatLogEntry,
|
|
@@ -11026,6 +11054,7 @@ export {
|
|
|
11026
11054
|
listObservations,
|
|
11027
11055
|
listProviders,
|
|
11028
11056
|
loadLLMAuthPlugins,
|
|
11057
|
+
localAppiumBinaryNames,
|
|
11029
11058
|
mapMobileCapabilities,
|
|
11030
11059
|
mapWebCapabilities,
|
|
11031
11060
|
mergeRunAttributes,
|
|
@@ -11052,6 +11081,7 @@ export {
|
|
|
11052
11081
|
resolveAnalyticsAgentProduct,
|
|
11053
11082
|
resolveAnalyticsIdentity,
|
|
11054
11083
|
resolveAnalyticsStandardProperties,
|
|
11084
|
+
resolveAppiumExecutable,
|
|
11055
11085
|
resolveBetterSqliteRebuildCommand,
|
|
11056
11086
|
resolveFarmCredentials,
|
|
11057
11087
|
resolveFileReferences,
|