agent-qa 0.0.1 → 0.1.2
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/LICENSE.md +105 -0
- package/NOTICE.md +11 -0
- package/dist/cli.cjs +5605 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +5 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +5600 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +349 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +40 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +300 -0
- package/dist/index.js.map +1 -0
- package/package.json +79 -8
- package/skills/agent-qa-authoring/SKILL.md +49 -0
- package/skills/agent-qa-authoring/agents/openai.yaml +15 -0
- package/skills/agent-qa-authoring/references/agent-qa-contracts.json +31 -0
- package/skills/agent-qa-debug-fix/SKILL.md +31 -0
- package/skills/agent-qa-debug-fix/agents/openai.yaml +15 -0
- package/skills/agent-qa-result-triage/SKILL.md +41 -0
- package/skills/agent-qa-result-triage/agents/openai.yaml +15 -0
- package/skills/agent-qa-result-triage/references/triage-categories.md +16 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
23
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
24
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
25
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
26
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
27
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
28
|
+
mod
|
|
29
|
+
));
|
|
30
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
31
|
+
|
|
32
|
+
// src/index.ts
|
|
33
|
+
var index_exports = {};
|
|
34
|
+
__export(index_exports, {
|
|
35
|
+
VERSION: () => VERSION,
|
|
36
|
+
deepMerge: () => deepMerge,
|
|
37
|
+
formatConfigDebug: () => formatConfigDebug,
|
|
38
|
+
listTargets: () => listTargets,
|
|
39
|
+
loadConfigFile: () => loadConfigFile,
|
|
40
|
+
loadEnvOverrides: () => loadEnvOverrides,
|
|
41
|
+
loadRequiredConfigFile: () => loadRequiredConfigFile,
|
|
42
|
+
mergeConfigs: () => mergeConfigs,
|
|
43
|
+
mergeUseBlocks: () => mergeUseBlocks,
|
|
44
|
+
mergeWithTestConfig: () => mergeWithTestConfig,
|
|
45
|
+
purgeAll: () => purgeAll,
|
|
46
|
+
purgeTest: () => purgeTest,
|
|
47
|
+
resolveConfig: () => resolveConfig,
|
|
48
|
+
resolveTarget: () => resolveTarget
|
|
49
|
+
});
|
|
50
|
+
module.exports = __toCommonJS(index_exports);
|
|
51
|
+
var import_agent_qa_core3 = require("@vostride/agent-qa-core");
|
|
52
|
+
__reExport(index_exports, require("@vostride/agent-qa-core"), module.exports);
|
|
53
|
+
|
|
54
|
+
// src/config.ts
|
|
55
|
+
var import_promises = require("fs/promises");
|
|
56
|
+
var import_node_path = require("path");
|
|
57
|
+
var import_yaml = require("yaml");
|
|
58
|
+
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
59
|
+
var import_agent_qa_core = require("@vostride/agent-qa-core");
|
|
60
|
+
var ENV_MAPPING = {
|
|
61
|
+
AGENT_QA_DASHBOARD_PORT: "services.dashboard.port",
|
|
62
|
+
AGENT_QA_MCP_PORT: "services.mcp.port",
|
|
63
|
+
AGENT_QA_CACHE_DIR: "services.cache.dir",
|
|
64
|
+
AGENT_QA_CACHE_TTL: "services.cache.ttl",
|
|
65
|
+
AGENT_QA_LOG_LEVEL: "services.logging.level",
|
|
66
|
+
AGENT_QA_HEADLESS: "use.browser.headless"
|
|
67
|
+
};
|
|
68
|
+
function setNestedValue(obj, path, value) {
|
|
69
|
+
const parts = path.split(".");
|
|
70
|
+
let current = obj;
|
|
71
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
72
|
+
const key = parts[i];
|
|
73
|
+
if (!(key in current) || typeof current[key] !== "object" || current[key] === null) {
|
|
74
|
+
current[key] = {};
|
|
75
|
+
}
|
|
76
|
+
current = current[key];
|
|
77
|
+
}
|
|
78
|
+
current[parts[parts.length - 1]] = value;
|
|
79
|
+
}
|
|
80
|
+
function parseEnvValue(key, value) {
|
|
81
|
+
if (key === "AGENT_QA_DASHBOARD_PORT" || key === "AGENT_QA_MCP_PORT") {
|
|
82
|
+
const parsed = Number.parseInt(value, 10);
|
|
83
|
+
if (Number.isInteger(parsed)) return parsed;
|
|
84
|
+
}
|
|
85
|
+
if (key === "AGENT_QA_HEADLESS") {
|
|
86
|
+
const normalized = value.trim().toLowerCase();
|
|
87
|
+
if (normalized === "true") return true;
|
|
88
|
+
if (normalized === "false") return false;
|
|
89
|
+
}
|
|
90
|
+
return value;
|
|
91
|
+
}
|
|
92
|
+
async function loadConfigFile(configPath) {
|
|
93
|
+
try {
|
|
94
|
+
const content = await (0, import_promises.readFile)(configPath, "utf-8");
|
|
95
|
+
return (0, import_yaml.parse)(content) ?? {};
|
|
96
|
+
} catch (err) {
|
|
97
|
+
if (err.code === "ENOENT") return {};
|
|
98
|
+
throw err;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function loadRequiredConfigFile(configPath) {
|
|
102
|
+
try {
|
|
103
|
+
const content = await (0, import_promises.readFile)(configPath, "utf-8");
|
|
104
|
+
return (0, import_yaml.parse)(content) ?? {};
|
|
105
|
+
} catch (err) {
|
|
106
|
+
if (err.code === "ENOENT") {
|
|
107
|
+
throw new Error(`Config file not found: ${configPath}`);
|
|
108
|
+
}
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function loadEnvOverrides() {
|
|
113
|
+
const result = {};
|
|
114
|
+
for (const [envKey, configPath] of Object.entries(ENV_MAPPING)) {
|
|
115
|
+
const value = process.env[envKey];
|
|
116
|
+
if (value !== void 0) {
|
|
117
|
+
setNestedValue(result, configPath, parseEnvValue(envKey, value));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
function isPlainObject(val) {
|
|
123
|
+
return typeof val === "object" && val !== null && !Array.isArray(val);
|
|
124
|
+
}
|
|
125
|
+
function mergeConfigs(file, env, flags) {
|
|
126
|
+
return deepMerge(deepMerge(file ?? {}, env), flags);
|
|
127
|
+
}
|
|
128
|
+
function deepMerge(base, override) {
|
|
129
|
+
if (!isPlainObject(base) || !isPlainObject(override)) return override;
|
|
130
|
+
const result = { ...base };
|
|
131
|
+
for (const key of Object.keys(override)) {
|
|
132
|
+
if (key in result && isPlainObject(result[key]) && isPlainObject(override[key])) {
|
|
133
|
+
result[key] = deepMerge(result[key], override[key]);
|
|
134
|
+
} else {
|
|
135
|
+
result[key] = override[key];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
function mergeWithTestConfig(globalConfig, testConfig, flagOverrides) {
|
|
141
|
+
return deepMerge(deepMerge(globalConfig ?? {}, testConfig ?? {}), flagOverrides);
|
|
142
|
+
}
|
|
143
|
+
function normalizeConfigForValidation(config) {
|
|
144
|
+
if (!isPlainObject(config)) return config;
|
|
145
|
+
const normalized = deepMerge({}, config);
|
|
146
|
+
const use = normalized.use;
|
|
147
|
+
if (isPlainObject(use)) {
|
|
148
|
+
delete use.headless;
|
|
149
|
+
}
|
|
150
|
+
return normalized;
|
|
151
|
+
}
|
|
152
|
+
function mergeUseBlocks(globalUse, suiteUse, testUse, cliFlags) {
|
|
153
|
+
return deepMerge(
|
|
154
|
+
deepMerge(
|
|
155
|
+
deepMerge(globalUse ?? {}, suiteUse ?? {}),
|
|
156
|
+
testUse ?? {}
|
|
157
|
+
),
|
|
158
|
+
cliFlags
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
function flattenObject(obj, prefix = "") {
|
|
162
|
+
const result = {};
|
|
163
|
+
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
|
|
164
|
+
if (prefix) result[prefix] = obj;
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
168
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
169
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
170
|
+
Object.assign(result, flattenObject(value, path));
|
|
171
|
+
} else {
|
|
172
|
+
result[path] = value;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
function formatConfigDebug(globalConfig, testConfig, flagOverrides) {
|
|
178
|
+
const layers = [
|
|
179
|
+
{ flat: flattenObject(globalConfig), label: "global config" },
|
|
180
|
+
{ flat: flattenObject(testConfig), label: "test yaml" },
|
|
181
|
+
{ flat: flattenObject(flagOverrides), label: "cli flag" }
|
|
182
|
+
];
|
|
183
|
+
const merged = mergeWithTestConfig(globalConfig, testConfig, flagOverrides);
|
|
184
|
+
const mergedFlat = flattenObject(merged);
|
|
185
|
+
const colorize = {
|
|
186
|
+
"global config": import_picocolors.default.dim,
|
|
187
|
+
"test yaml": import_picocolors.default.cyan,
|
|
188
|
+
"cli flag": import_picocolors.default.green
|
|
189
|
+
};
|
|
190
|
+
const rows = [];
|
|
191
|
+
for (const key of Object.keys(mergedFlat).sort()) {
|
|
192
|
+
let source = "global config";
|
|
193
|
+
for (const layer of layers) {
|
|
194
|
+
if (key in layer.flat) {
|
|
195
|
+
source = layer.label;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const valStr = typeof mergedFlat[key] === "string" ? mergedFlat[key] : JSON.stringify(mergedFlat[key]);
|
|
199
|
+
rows.push({ key, value: valStr, source });
|
|
200
|
+
}
|
|
201
|
+
if (rows.length === 0) return import_picocolors.default.dim(" (no config values)");
|
|
202
|
+
const maxKey = Math.max(...rows.map((r) => r.key.length), 3);
|
|
203
|
+
const maxVal = Math.max(...rows.map((r) => r.value.length), 5);
|
|
204
|
+
const header = ` ${"Key".padEnd(maxKey)} ${"Value".padEnd(maxVal)} Source`;
|
|
205
|
+
const sep = ` ${"\u2500".repeat(maxKey)} ${"\u2500".repeat(maxVal)} ${"\u2500".repeat(13)}`;
|
|
206
|
+
const lines = rows.map((r) => {
|
|
207
|
+
const color = colorize[r.source];
|
|
208
|
+
return ` ${r.key.padEnd(maxKey)} ${r.value.padEnd(maxVal)} ${color(r.source)}`;
|
|
209
|
+
});
|
|
210
|
+
return [header, sep, ...lines].join("\n");
|
|
211
|
+
}
|
|
212
|
+
async function resolveConfig(options) {
|
|
213
|
+
const configPath = options.configPath ?? "agent-qa.config.yaml";
|
|
214
|
+
const fileConfig = await loadRequiredConfigFile(configPath);
|
|
215
|
+
const envOverrides = loadEnvOverrides();
|
|
216
|
+
const merged = mergeConfigs(fileConfig, envOverrides, options.flags ?? {});
|
|
217
|
+
const normalized = normalizeConfigForValidation(merged);
|
|
218
|
+
const result = import_agent_qa_core.AgentQaConfigSchema.safeParse(normalized);
|
|
219
|
+
if (!result.success) {
|
|
220
|
+
const messages = result.error.issues.map((issue) => {
|
|
221
|
+
const path = issue.path.map(String).join(".");
|
|
222
|
+
return ` ${path}: ${issue.message}`;
|
|
223
|
+
});
|
|
224
|
+
const legacyDashboardEnabled = typeof merged === "object" && merged !== null && !Array.isArray(merged) && typeof merged.services === "object" && merged.services !== null && !Array.isArray(merged.services) && typeof merged.services.dashboard === "object" && merged.services.dashboard !== null && !Array.isArray(merged.services.dashboard) && "enabled" in merged.services.dashboard;
|
|
225
|
+
const dashboardHint = legacyDashboardEnabled ? `
|
|
226
|
+
|
|
227
|
+
Hint: services.dashboard.enabled was removed. Dashboard behavior is enabled by the presence of the services.dashboard block itself. Remove the enabled key and keep fields like port, dbPath, or artifactsDir.` : "";
|
|
228
|
+
throw new Error(
|
|
229
|
+
`Config validation failed:
|
|
230
|
+
${messages.join("\n")}
|
|
231
|
+
|
|
232
|
+
Run 'agent-qa init' to generate a complete config file.` + dashboardHint
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
if (options.loadAuthPlugins !== false) {
|
|
236
|
+
await (0, import_agent_qa_core.loadLLMAuthPlugins)(result.data.plugins?.auth, {
|
|
237
|
+
baseDir: (0, import_node_path.dirname)((0, import_node_path.resolve)(configPath))
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
return result.data;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/targets.ts
|
|
244
|
+
function resolveTarget(config, targetName) {
|
|
245
|
+
const targets = config.registry?.targets;
|
|
246
|
+
if (!targets || !(targetName in targets)) {
|
|
247
|
+
throw new Error(`Target "${targetName}" not found in config. Available targets: ${targets ? Object.keys(targets).join(", ") : "none"}`);
|
|
248
|
+
}
|
|
249
|
+
const target = targets[targetName];
|
|
250
|
+
return {
|
|
251
|
+
name: targetName,
|
|
252
|
+
product: target.product ?? targetName,
|
|
253
|
+
platform: target.platform,
|
|
254
|
+
url: target.url,
|
|
255
|
+
bundleId: target.bundleId,
|
|
256
|
+
appPackage: target.appPackage,
|
|
257
|
+
appActivity: target.appActivity,
|
|
258
|
+
device: target.device
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function listTargets(config) {
|
|
262
|
+
const targets = config.registry?.targets;
|
|
263
|
+
if (!targets) return [];
|
|
264
|
+
return Object.entries(targets).map(([name, entry]) => {
|
|
265
|
+
const typedEntry = entry;
|
|
266
|
+
return {
|
|
267
|
+
name,
|
|
268
|
+
platform: typedEntry.platform
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// src/commands/cache.ts
|
|
274
|
+
var import_commander = require("commander");
|
|
275
|
+
var import_picocolors2 = __toESM(require("picocolors"), 1);
|
|
276
|
+
var import_promises2 = require("fs/promises");
|
|
277
|
+
var import_yaml2 = require("yaml");
|
|
278
|
+
var import_node_path2 = require("path");
|
|
279
|
+
var import_node_readline = require("readline");
|
|
280
|
+
var import_agent_qa_core2 = require("@vostride/agent-qa-core");
|
|
281
|
+
async function purgeTest(testPath, cacheDir, configContent = "") {
|
|
282
|
+
const content = await (0, import_promises2.readFile)(testPath, "utf-8");
|
|
283
|
+
const doc = (0, import_yaml2.parse)(content);
|
|
284
|
+
const platform = doc.platform ?? "web";
|
|
285
|
+
const steps = (doc.steps ?? []).map(
|
|
286
|
+
(s) => typeof s === "string" ? s : s.step
|
|
287
|
+
);
|
|
288
|
+
const resolvedCacheDir = (0, import_node_path2.resolve)(cacheDir);
|
|
289
|
+
let purged = 0;
|
|
290
|
+
for (let i = 0; i < steps.length; i++) {
|
|
291
|
+
const stepHash = (0, import_agent_qa_core2.hashStepInstruction)(steps[i], platform, configContent, content, i);
|
|
292
|
+
const dirPath = (0, import_node_path2.join)(resolvedCacheDir, stepHash);
|
|
293
|
+
try {
|
|
294
|
+
await (0, import_promises2.stat)(dirPath);
|
|
295
|
+
await (0, import_promises2.rm)(dirPath, { recursive: true, force: true });
|
|
296
|
+
purged++;
|
|
297
|
+
} catch {
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return purged;
|
|
301
|
+
}
|
|
302
|
+
async function purgeAll(cacheDir, force) {
|
|
303
|
+
const resolvedDir = (0, import_node_path2.resolve)(cacheDir);
|
|
304
|
+
let entries;
|
|
305
|
+
try {
|
|
306
|
+
entries = await (0, import_promises2.readdir)(resolvedDir);
|
|
307
|
+
} catch (err) {
|
|
308
|
+
if (err.code === "ENOENT") return 0;
|
|
309
|
+
throw err;
|
|
310
|
+
}
|
|
311
|
+
if (entries.length === 0) return 0;
|
|
312
|
+
if (!force) {
|
|
313
|
+
const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
314
|
+
const answer = await new Promise((res) => {
|
|
315
|
+
rl.question(`About to delete ${entries.length} cached plans. Continue? (y/N) `, res);
|
|
316
|
+
});
|
|
317
|
+
rl.close();
|
|
318
|
+
if (answer.toLowerCase() !== "y") {
|
|
319
|
+
console.log("Aborted.");
|
|
320
|
+
return 0;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
for (const entry of entries) {
|
|
324
|
+
await (0, import_promises2.rm)((0, import_node_path2.join)(resolvedDir, entry), { recursive: true, force: true });
|
|
325
|
+
}
|
|
326
|
+
return entries.length;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// src/index.ts
|
|
330
|
+
var VERSION = (0, import_agent_qa_core3.getAgentQaVersion)();
|
|
331
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
332
|
+
0 && (module.exports = {
|
|
333
|
+
VERSION,
|
|
334
|
+
deepMerge,
|
|
335
|
+
formatConfigDebug,
|
|
336
|
+
listTargets,
|
|
337
|
+
loadConfigFile,
|
|
338
|
+
loadEnvOverrides,
|
|
339
|
+
loadRequiredConfigFile,
|
|
340
|
+
mergeConfigs,
|
|
341
|
+
mergeUseBlocks,
|
|
342
|
+
mergeWithTestConfig,
|
|
343
|
+
purgeAll,
|
|
344
|
+
purgeTest,
|
|
345
|
+
resolveConfig,
|
|
346
|
+
resolveTarget,
|
|
347
|
+
...require("@vostride/agent-qa-core")
|
|
348
|
+
});
|
|
349
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/targets.ts","../src/commands/cache.ts"],"sourcesContent":["import { getAgentQaVersion } from '@vostride/agent-qa-core'\n\nexport * from '@vostride/agent-qa-core'\nexport const VERSION = getAgentQaVersion()\nexport * from './config.js'\nexport * from './targets.js'\nexport { purgeTest, purgeAll } from './commands/cache.js'\n","import { readFile } from 'node:fs/promises'\nimport { dirname, resolve as resolvePath } from 'node:path'\nimport { parse as parseYaml } from 'yaml'\nimport pc from 'picocolors'\nimport { AgentQaConfigSchema, loadLLMAuthPlugins } from '@vostride/agent-qa-core'\nimport type { AgentQaConfig } from '@vostride/agent-qa-core'\n\nconst ENV_MAPPING: Record<string, string> = {\n AGENT_QA_DASHBOARD_PORT: 'services.dashboard.port',\n AGENT_QA_MCP_PORT: 'services.mcp.port',\n AGENT_QA_CACHE_DIR: 'services.cache.dir',\n AGENT_QA_CACHE_TTL: 'services.cache.ttl',\n AGENT_QA_LOG_LEVEL: 'services.logging.level',\n AGENT_QA_HEADLESS: 'use.browser.headless',\n}\n\nfunction setNestedValue(obj: Record<string, unknown>, path: string, value: unknown): void {\n const parts = path.split('.')\n let current: Record<string, unknown> = obj\n for (let i = 0; i < parts.length - 1; i++) {\n const key = parts[i]\n if (!(key in current) || typeof current[key] !== 'object' || current[key] === null) {\n current[key] = {}\n }\n current = current[key] as Record<string, unknown>\n }\n current[parts[parts.length - 1]] = value\n}\n\nfunction parseEnvValue(key: string, value: string): unknown {\n if (key === 'AGENT_QA_DASHBOARD_PORT' || key === 'AGENT_QA_MCP_PORT') {\n const parsed = Number.parseInt(value, 10)\n if (Number.isInteger(parsed)) return parsed\n }\n if (key === 'AGENT_QA_HEADLESS') {\n const normalized = value.trim().toLowerCase()\n if (normalized === 'true') return true\n if (normalized === 'false') return false\n }\n return value\n}\n\nexport async function loadConfigFile(configPath: string): Promise<unknown> {\n try {\n const content = await readFile(configPath, 'utf-8')\n return parseYaml(content) ?? {}\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return {}\n throw err\n }\n}\n\nexport async function loadRequiredConfigFile(configPath: string): Promise<unknown> {\n try {\n const content = await readFile(configPath, 'utf-8')\n return parseYaml(content) ?? {}\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new Error(`Config file not found: ${configPath}`)\n }\n throw err\n }\n}\n\nexport function loadEnvOverrides(): Record<string, unknown> {\n const result: Record<string, unknown> = {}\n for (const [envKey, configPath] of Object.entries(ENV_MAPPING)) {\n const value = process.env[envKey]\n if (value !== undefined) {\n setNestedValue(result, configPath, parseEnvValue(envKey, value))\n }\n }\n return result\n}\n\nfunction isPlainObject(val: unknown): val is Record<string, unknown> {\n return typeof val === 'object' && val !== null && !Array.isArray(val)\n}\n\nexport function mergeConfigs(\n file: unknown,\n env: Record<string, unknown>,\n flags: Record<string, unknown>,\n): unknown {\n return deepMerge(deepMerge(file ?? {}, env), flags)\n}\n\nexport function deepMerge(base: unknown, override: unknown): unknown {\n if (!isPlainObject(base) || !isPlainObject(override)) return override\n const result: Record<string, unknown> = { ...base }\n for (const key of Object.keys(override)) {\n if (key in result && isPlainObject(result[key]) && isPlainObject(override[key])) {\n result[key] = deepMerge(result[key], override[key])\n } else {\n result[key] = override[key]\n }\n }\n return result\n}\n\nexport function mergeWithTestConfig(\n globalConfig: unknown,\n testConfig: unknown,\n flagOverrides: Record<string, unknown>,\n): unknown {\n return deepMerge(deepMerge(globalConfig ?? {}, testConfig ?? {}), flagOverrides)\n}\n\nfunction normalizeConfigForValidation(config: unknown): unknown {\n if (!isPlainObject(config)) return config\n const normalized = deepMerge({}, config) as Record<string, unknown>\n const use = normalized.use\n if (isPlainObject(use)) {\n delete use.headless\n }\n return normalized\n}\n\nexport function mergeUseBlocks(\n globalUse: Record<string, unknown> | undefined,\n suiteUse: Record<string, unknown> | undefined,\n testUse: Record<string, unknown> | undefined,\n cliFlags: Record<string, unknown>,\n): Record<string, unknown> {\n return deepMerge(\n deepMerge(\n deepMerge(globalUse ?? {}, suiteUse ?? {}),\n testUse ?? {},\n ),\n cliFlags,\n ) as Record<string, unknown>\n}\n\nfunction flattenObject(obj: unknown, prefix = ''): Record<string, unknown> {\n const result: Record<string, unknown> = {}\n if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {\n if (prefix) result[prefix] = obj\n return result\n }\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n const path = prefix ? `${prefix}.${key}` : key\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n Object.assign(result, flattenObject(value, path))\n } else {\n result[path] = value\n }\n }\n return result\n}\n\ntype ConfigSource = 'global config' | 'test yaml' | 'cli flag'\n\nexport function formatConfigDebug(\n globalConfig: unknown,\n testConfig: unknown,\n flagOverrides: Record<string, unknown>,\n): string {\n const layers: { flat: Record<string, unknown>; label: ConfigSource }[] = [\n { flat: flattenObject(globalConfig), label: 'global config' },\n { flat: flattenObject(testConfig), label: 'test yaml' },\n { flat: flattenObject(flagOverrides), label: 'cli flag' },\n ]\n\n const merged = mergeWithTestConfig(globalConfig, testConfig, flagOverrides)\n const mergedFlat = flattenObject(merged)\n\n const colorize: Record<ConfigSource, (s: string) => string> = {\n 'global config': pc.dim,\n 'test yaml': pc.cyan,\n 'cli flag': pc.green,\n }\n\n const rows: { key: string; value: string; source: ConfigSource }[] = []\n for (const key of Object.keys(mergedFlat).sort()) {\n let source: ConfigSource = 'global config'\n // Check layers in reverse priority order — last match wins\n for (const layer of layers) {\n if (key in layer.flat) {\n source = layer.label\n }\n }\n const valStr = typeof mergedFlat[key] === 'string' ? mergedFlat[key] : JSON.stringify(mergedFlat[key])\n rows.push({ key, value: valStr, source })\n }\n\n if (rows.length === 0) return pc.dim(' (no config values)')\n\n const maxKey = Math.max(...rows.map(r => r.key.length), 3)\n const maxVal = Math.max(...rows.map(r => r.value.length), 5)\n\n const header = ` ${'Key'.padEnd(maxKey)} ${'Value'.padEnd(maxVal)} Source`\n const sep = ` ${'─'.repeat(maxKey)} ${'─'.repeat(maxVal)} ${'─'.repeat(13)}`\n const lines = rows.map(r => {\n const color = colorize[r.source]\n return ` ${r.key.padEnd(maxKey)} ${r.value.padEnd(maxVal)} ${color(r.source)}`\n })\n\n return [header, sep, ...lines].join('\\n')\n}\n\nexport async function resolveConfig(options: {\n configPath?: string\n flags?: Record<string, unknown>\n loadAuthPlugins?: boolean\n}): Promise<AgentQaConfig> {\n const configPath = options.configPath ?? 'agent-qa.config.yaml'\n const fileConfig = await loadRequiredConfigFile(configPath)\n const envOverrides = loadEnvOverrides()\n const merged = mergeConfigs(fileConfig, envOverrides, options.flags ?? {})\n const normalized = normalizeConfigForValidation(merged)\n\n const result = AgentQaConfigSchema.safeParse(normalized)\n if (!result.success) {\n const messages = result.error.issues.map((issue: { path: PropertyKey[]; message: string }) => {\n const path = issue.path.map(String).join('.')\n return ` ${path}: ${issue.message}`\n })\n const legacyDashboardEnabled =\n typeof merged === 'object'\n && merged !== null\n && !Array.isArray(merged)\n && typeof (merged as Record<string, unknown>).services === 'object'\n && (merged as Record<string, unknown>).services !== null\n && !Array.isArray((merged as Record<string, unknown>).services)\n && typeof ((merged as Record<string, unknown>).services as Record<string, unknown>).dashboard === 'object'\n && ((merged as Record<string, unknown>).services as Record<string, unknown>).dashboard !== null\n && !Array.isArray(((merged as Record<string, unknown>).services as Record<string, unknown>).dashboard)\n && 'enabled' in (((merged as Record<string, unknown>).services as Record<string, unknown>).dashboard as Record<string, unknown>)\n\n const dashboardHint = legacyDashboardEnabled\n ? `\\n\\nHint: services.dashboard.enabled was removed. Dashboard behavior is enabled by the presence of the services.dashboard block itself. Remove the enabled key and keep fields like port, dbPath, or artifactsDir.`\n : ''\n throw new Error(\n `Config validation failed:\\n${messages.join('\\n')}\\n\\n` +\n `Run 'agent-qa init' to generate a complete config file.` +\n dashboardHint\n )\n }\n if (options.loadAuthPlugins !== false) {\n await loadLLMAuthPlugins(result.data.plugins?.auth, {\n baseDir: dirname(resolvePath(configPath)),\n })\n }\n return result.data\n}\n","import type { AgentQaConfig } from '@vostride/agent-qa-core'\n\nexport interface ResolvedTarget {\n name: string\n product: string\n platform: 'web' | 'android' | 'ios'\n url?: string\n bundleId?: string\n appPackage?: string\n appActivity?: string\n device?: string\n}\n\nexport interface TargetSummary {\n name: string\n platform: 'web' | 'android' | 'ios'\n}\n\ninterface TargetEntry {\n product?: string\n platform: 'web' | 'android' | 'ios'\n bundleId?: string\n appPackage?: string\n appActivity?: string\n url?: string\n device?: string\n}\n\nexport function resolveTarget(\n config: AgentQaConfig,\n targetName: string,\n): ResolvedTarget {\n const targets = (config as any).registry?.targets\n if (!targets || !(targetName in targets)) {\n throw new Error(`Target \"${targetName}\" not found in config. Available targets: ${targets ? Object.keys(targets).join(', ') : 'none'}`)\n }\n\n const target = targets[targetName] as TargetEntry\n\n return {\n name: targetName,\n product: target.product ?? targetName,\n platform: target.platform,\n url: target.url,\n bundleId: target.bundleId,\n appPackage: target.appPackage,\n appActivity: target.appActivity,\n device: target.device,\n }\n}\n\nexport function listTargets(config: AgentQaConfig): TargetSummary[] {\n const targets = (config as any).registry?.targets\n if (!targets) return []\n\n return Object.entries(targets).map(([name, entry]) => {\n const typedEntry = entry as TargetEntry\n return {\n name,\n platform: typedEntry.platform,\n }\n })\n}\n","import { Command } from 'commander'\nimport pc from 'picocolors'\nimport { readFile, readdir, rm, stat } from 'node:fs/promises'\nimport { parse } from 'yaml'\nimport { join, resolve, basename } from 'node:path'\nimport { createInterface } from 'node:readline'\nimport { DEFAULT_AGENT_QA_CACHE_DIR, hashStepInstruction } from '@vostride/agent-qa-core'\nimport { resolveConfig } from '../config.js'\n\nconst CACHE_DIR_FALLBACK = DEFAULT_AGENT_QA_CACHE_DIR || '.agent-qa/cache'\n\nexport async function purgeTest(testPath: string, cacheDir: string, configContent = ''): Promise<number> {\n const content = await readFile(testPath, 'utf-8')\n const doc = parse(content)\n const platform: string = doc.platform ?? 'web'\n const steps: string[] = (doc.steps ?? []).map((s: unknown) =>\n typeof s === 'string' ? s : (s as Record<string, string>).step,\n )\n\n const resolvedCacheDir = resolve(cacheDir)\n let purged = 0\n\n for (let i = 0; i < steps.length; i++) {\n const stepHash = hashStepInstruction(steps[i], platform, configContent, content, i)\n const dirPath = join(resolvedCacheDir, stepHash)\n try {\n await stat(dirPath)\n await rm(dirPath, { recursive: true, force: true })\n purged++\n } catch {\n // directory doesn't exist — skip\n }\n }\n\n return purged\n}\n\nexport async function purgeAll(cacheDir: string, force: boolean): Promise<number> {\n const resolvedDir = resolve(cacheDir)\n let entries: string[]\n try {\n entries = await readdir(resolvedDir)\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return 0\n throw err\n }\n\n if (entries.length === 0) return 0\n\n if (!force) {\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n const answer = await new Promise<string>((res) => {\n rl.question(`About to delete ${entries.length} cached plans. Continue? (y/N) `, res)\n })\n rl.close()\n if (answer.toLowerCase() !== 'y') {\n console.log('Aborted.')\n return 0\n }\n }\n\n for (const entry of entries) {\n await rm(join(resolvedDir, entry), { recursive: true, force: true })\n }\n\n return entries.length\n}\n\nexport function createCacheCommand(): Command {\n const cmd = new Command('cache').description('Manage action cache')\n\n cmd\n .command('purge')\n .description('Clear cached action plans')\n .option('--test <path>', 'purge cache for a specific test file')\n .option('--all', 'purge all cached plans')\n .option('--force', 'skip confirmation prompt')\n .action(async (opts) => {\n if (!opts.test && !opts.all) {\n console.error(pc.red('Specify --test <path> or --all'))\n process.exitCode = 1\n return\n }\n\n const parentOpts = cmd.parent?.opts() ?? {}\n const config = await resolveConfig({\n configPath: parentOpts.config as string | undefined,\n })\n const cacheDir = config.services?.cache?.dir ?? CACHE_DIR_FALLBACK\n\n // Read raw config file content for cache key scoping\n const configPath = (parentOpts.config as string | undefined) ?? 'agent-qa.config.yaml'\n let configContent = ''\n try {\n configContent = await readFile(configPath, 'utf-8')\n } catch { /* config file may not exist */ }\n\n if (opts.test) {\n const count = await purgeTest(opts.test, cacheDir, configContent)\n if (count === 0) {\n console.log(pc.dim('No cached plans found'))\n } else {\n console.log(pc.green(`Purged ${count} cached plans for ${basename(opts.test)}`))\n }\n } else if (opts.all) {\n const count = await purgeAll(cacheDir, !!opts.force)\n if (count === 0) {\n console.log(pc.dim('No cached plans found'))\n } else {\n console.log(pc.green(`Purged ${count} cached plans (all)`))\n }\n }\n })\n\n return cmd\n}\n"],"mappings":"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","names":["import_agent_qa_core","parseYaml","pc","resolvePath","import_picocolors","import_promises","import_yaml","import_node_path","import_agent_qa_core"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { AgentQaConfig } from '@vostride/agent-qa-core';
|
|
2
|
+
export * from '@vostride/agent-qa-core';
|
|
3
|
+
|
|
4
|
+
declare function loadConfigFile(configPath: string): Promise<unknown>;
|
|
5
|
+
declare function loadRequiredConfigFile(configPath: string): Promise<unknown>;
|
|
6
|
+
declare function loadEnvOverrides(): Record<string, unknown>;
|
|
7
|
+
declare function mergeConfigs(file: unknown, env: Record<string, unknown>, flags: Record<string, unknown>): unknown;
|
|
8
|
+
declare function deepMerge(base: unknown, override: unknown): unknown;
|
|
9
|
+
declare function mergeWithTestConfig(globalConfig: unknown, testConfig: unknown, flagOverrides: Record<string, unknown>): unknown;
|
|
10
|
+
declare function mergeUseBlocks(globalUse: Record<string, unknown> | undefined, suiteUse: Record<string, unknown> | undefined, testUse: Record<string, unknown> | undefined, cliFlags: Record<string, unknown>): Record<string, unknown>;
|
|
11
|
+
declare function formatConfigDebug(globalConfig: unknown, testConfig: unknown, flagOverrides: Record<string, unknown>): string;
|
|
12
|
+
declare function resolveConfig(options: {
|
|
13
|
+
configPath?: string;
|
|
14
|
+
flags?: Record<string, unknown>;
|
|
15
|
+
loadAuthPlugins?: boolean;
|
|
16
|
+
}): Promise<AgentQaConfig>;
|
|
17
|
+
|
|
18
|
+
interface ResolvedTarget {
|
|
19
|
+
name: string;
|
|
20
|
+
product: string;
|
|
21
|
+
platform: 'web' | 'android' | 'ios';
|
|
22
|
+
url?: string;
|
|
23
|
+
bundleId?: string;
|
|
24
|
+
appPackage?: string;
|
|
25
|
+
appActivity?: string;
|
|
26
|
+
device?: string;
|
|
27
|
+
}
|
|
28
|
+
interface TargetSummary {
|
|
29
|
+
name: string;
|
|
30
|
+
platform: 'web' | 'android' | 'ios';
|
|
31
|
+
}
|
|
32
|
+
declare function resolveTarget(config: AgentQaConfig, targetName: string): ResolvedTarget;
|
|
33
|
+
declare function listTargets(config: AgentQaConfig): TargetSummary[];
|
|
34
|
+
|
|
35
|
+
declare function purgeTest(testPath: string, cacheDir: string, configContent?: string): Promise<number>;
|
|
36
|
+
declare function purgeAll(cacheDir: string, force: boolean): Promise<number>;
|
|
37
|
+
|
|
38
|
+
declare const VERSION: string;
|
|
39
|
+
|
|
40
|
+
export { type ResolvedTarget, type TargetSummary, VERSION, deepMerge, formatConfigDebug, listTargets, loadConfigFile, loadEnvOverrides, loadRequiredConfigFile, mergeConfigs, mergeUseBlocks, mergeWithTestConfig, purgeAll, purgeTest, resolveConfig, resolveTarget };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { AgentQaConfig } from '@vostride/agent-qa-core';
|
|
2
|
+
export * from '@vostride/agent-qa-core';
|
|
3
|
+
|
|
4
|
+
declare function loadConfigFile(configPath: string): Promise<unknown>;
|
|
5
|
+
declare function loadRequiredConfigFile(configPath: string): Promise<unknown>;
|
|
6
|
+
declare function loadEnvOverrides(): Record<string, unknown>;
|
|
7
|
+
declare function mergeConfigs(file: unknown, env: Record<string, unknown>, flags: Record<string, unknown>): unknown;
|
|
8
|
+
declare function deepMerge(base: unknown, override: unknown): unknown;
|
|
9
|
+
declare function mergeWithTestConfig(globalConfig: unknown, testConfig: unknown, flagOverrides: Record<string, unknown>): unknown;
|
|
10
|
+
declare function mergeUseBlocks(globalUse: Record<string, unknown> | undefined, suiteUse: Record<string, unknown> | undefined, testUse: Record<string, unknown> | undefined, cliFlags: Record<string, unknown>): Record<string, unknown>;
|
|
11
|
+
declare function formatConfigDebug(globalConfig: unknown, testConfig: unknown, flagOverrides: Record<string, unknown>): string;
|
|
12
|
+
declare function resolveConfig(options: {
|
|
13
|
+
configPath?: string;
|
|
14
|
+
flags?: Record<string, unknown>;
|
|
15
|
+
loadAuthPlugins?: boolean;
|
|
16
|
+
}): Promise<AgentQaConfig>;
|
|
17
|
+
|
|
18
|
+
interface ResolvedTarget {
|
|
19
|
+
name: string;
|
|
20
|
+
product: string;
|
|
21
|
+
platform: 'web' | 'android' | 'ios';
|
|
22
|
+
url?: string;
|
|
23
|
+
bundleId?: string;
|
|
24
|
+
appPackage?: string;
|
|
25
|
+
appActivity?: string;
|
|
26
|
+
device?: string;
|
|
27
|
+
}
|
|
28
|
+
interface TargetSummary {
|
|
29
|
+
name: string;
|
|
30
|
+
platform: 'web' | 'android' | 'ios';
|
|
31
|
+
}
|
|
32
|
+
declare function resolveTarget(config: AgentQaConfig, targetName: string): ResolvedTarget;
|
|
33
|
+
declare function listTargets(config: AgentQaConfig): TargetSummary[];
|
|
34
|
+
|
|
35
|
+
declare function purgeTest(testPath: string, cacheDir: string, configContent?: string): Promise<number>;
|
|
36
|
+
declare function purgeAll(cacheDir: string, force: boolean): Promise<number>;
|
|
37
|
+
|
|
38
|
+
declare const VERSION: string;
|
|
39
|
+
|
|
40
|
+
export { type ResolvedTarget, type TargetSummary, VERSION, deepMerge, formatConfigDebug, listTargets, loadConfigFile, loadEnvOverrides, loadRequiredConfigFile, mergeConfigs, mergeUseBlocks, mergeWithTestConfig, purgeAll, purgeTest, resolveConfig, resolveTarget };
|