@vostride/agent-qa-core 0.2.2 → 0.2.4
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/README.md +78 -0
- package/dist/index.cjs +400 -700
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -423
- package/dist/index.d.ts +20 -423
- package/dist/index.js +362 -655
- package/dist/index.js.map +1 -1
- package/dist/run-text-output.cjs +86 -0
- package/dist/run-text-output.cjs.map +1 -0
- package/dist/run-text-output.d.cts +13 -0
- package/dist/run-text-output.d.ts +13 -0
- package/dist/run-text-output.js +55 -0
- package/dist/run-text-output.js.map +1 -0
- package/package.json +10 -6
package/dist/index.cjs
CHANGED
|
@@ -43,7 +43,6 @@ __export(index_exports, {
|
|
|
43
43
|
AUTH_STATE_SCHEMA_VERSION: () => AUTH_STATE_SCHEMA_VERSION,
|
|
44
44
|
AUTH_STATE_SLUG_PATTERN: () => AUTH_STATE_SLUG_PATTERN,
|
|
45
45
|
AccessibilityConfigSchema: () => AccessibilityConfigSchema,
|
|
46
|
-
ActionPlanSchema: () => ActionPlanSchema,
|
|
47
46
|
AdapterLogDataSchema: () => AdapterLogDataSchema,
|
|
48
47
|
AgentLogDataSchema: () => AgentLogDataSchema,
|
|
49
48
|
AgentQaConfigSchema: () => AgentQaConfigSchema,
|
|
@@ -100,7 +99,6 @@ __export(index_exports, {
|
|
|
100
99
|
PlannerConfigSchema: () => PlannerConfigSchema,
|
|
101
100
|
PlannerLogDataSchema: () => PlannerLogDataSchema,
|
|
102
101
|
PluginsSchema: () => PluginsSchema,
|
|
103
|
-
ProviderConfigSchema: () => ProviderConfigSchema,
|
|
104
102
|
ProviderPluginDeclarationSchema: () => ProviderPluginDeclarationSchema,
|
|
105
103
|
RUNTIME_IMAGE_MAP: () => import_agent_qa_runner6.RUNTIME_IMAGE_MAP,
|
|
106
104
|
RUN_ATTRIBUTE_KEY_MAX_LENGTH: () => RUN_ATTRIBUTE_KEY_MAX_LENGTH,
|
|
@@ -118,7 +116,6 @@ __export(index_exports, {
|
|
|
118
116
|
TestMetaSchema: () => TestMetaSchema,
|
|
119
117
|
TestStepSchema: () => TestStepSchema,
|
|
120
118
|
TimeoutConfigSchema: () => TimeoutConfigSchema,
|
|
121
|
-
TransportSchema: () => TransportSchema,
|
|
122
119
|
UseSchema: () => UseSchema,
|
|
123
120
|
VALID_FILENAME_RE: () => VALID_FILENAME_RE,
|
|
124
121
|
VERSION_CHECK_CACHE_TTL_MS: () => VERSION_CHECK_CACHE_TTL_MS,
|
|
@@ -126,7 +123,6 @@ __export(index_exports, {
|
|
|
126
123
|
WEBVIEW_SCROLLERS_SCRIPT: () => WEBVIEW_SCROLLERS_SCRIPT,
|
|
127
124
|
WorkspaceSchema: () => WorkspaceSchema,
|
|
128
125
|
applyPatchSetToTestDoc: () => applyPatchSetToTestDoc,
|
|
129
|
-
browserstackProvider: () => browserstackProvider,
|
|
130
126
|
buildAuthStateHookEnv: () => import_agent_qa_runner4.buildAuthStateHookEnv,
|
|
131
127
|
buildInternalRunAttributes: () => buildInternalRunAttributes,
|
|
132
128
|
buildRefEntries: () => buildRefEntries,
|
|
@@ -139,7 +135,6 @@ __export(index_exports, {
|
|
|
139
135
|
createAuthFetch: () => createAuthFetch,
|
|
140
136
|
deriveMobileScroll: () => deriveMobileScroll,
|
|
141
137
|
detectGlobOverlap: () => detectGlobOverlap,
|
|
142
|
-
detectPlaywrightVersion: () => detectPlaywrightVersion,
|
|
143
138
|
discoverWorkspaceFiles: () => discoverWorkspaceFiles,
|
|
144
139
|
estimateTokens: () => estimateTokens,
|
|
145
140
|
formatAppiumInstallGuidance: () => formatAppiumInstallGuidance,
|
|
@@ -158,21 +153,21 @@ __export(index_exports, {
|
|
|
158
153
|
getAuthPath: () => getAuthPath,
|
|
159
154
|
getCredential: () => getCredential,
|
|
160
155
|
getLLMAuthProviderPlugin: () => getLLMAuthProviderPlugin,
|
|
161
|
-
|
|
156
|
+
guardJsonText: () => guardJsonText,
|
|
157
|
+
guardRunText: () => guardRunText,
|
|
158
|
+
hookRunnerImage: () => import_agent_qa_runner6.hookRunnerImage,
|
|
162
159
|
isCanonicalRunId: () => isCanonicalRunId,
|
|
160
|
+
isGitHubActions: () => isGitHubActions,
|
|
163
161
|
isLoopbackHost: () => isLoopbackHost,
|
|
164
162
|
isPathInsideDir: () => isPathInsideDir,
|
|
165
163
|
isReservedAuthStateHookEnvKey: () => import_agent_qa_runner4.isReservedAuthStateHookEnvKey,
|
|
166
164
|
isWorkspacePathMatch: () => isWorkspacePathMatch,
|
|
167
165
|
listAuthStateMetadata: () => listAuthStateMetadata,
|
|
168
166
|
listLLMAuthProviderPlugins: () => listLLMAuthProviderPlugins,
|
|
169
|
-
listProviders: () => listProviders,
|
|
170
167
|
loadLLMAuthPlugins: () => loadLLMAuthPlugins,
|
|
171
168
|
loadProviderPlugins: () => loadProviderPlugins,
|
|
172
169
|
localAppiumBinaryNames: () => localAppiumBinaryNames,
|
|
173
170
|
locateRunsDbPath: () => locateRunsDbPath,
|
|
174
|
-
mapMobileCapabilities: () => mapMobileCapabilities,
|
|
175
|
-
mapWebCapabilities: () => mapWebCapabilities,
|
|
176
171
|
mergeRunAttributes: () => mergeRunAttributes,
|
|
177
172
|
normalizeAuthStateUse: () => normalizeAuthStateUse,
|
|
178
173
|
normalizeRole: () => normalizeRole,
|
|
@@ -187,21 +182,21 @@ __export(index_exports, {
|
|
|
187
182
|
parseTestFileWithTokens: () => parseTestFileWithTokens,
|
|
188
183
|
parseWebViewScrollers: () => parseWebViewScrollers,
|
|
189
184
|
patchTestForSuite: () => patchTestForSuite,
|
|
185
|
+
printRunText: () => printRunText,
|
|
190
186
|
readAuth: () => readAuth,
|
|
191
187
|
readAuthStateMetadata: () => readAuthStateMetadata,
|
|
192
|
-
|
|
188
|
+
refuseRetiredLocalBindings: () => refuseRetiredLocalBindings,
|
|
193
189
|
registerLLMAuthProviderPlugin: () => registerLLMAuthProviderPlugin,
|
|
194
190
|
registerLLMAuthProviderPlugins: () => registerLLMAuthProviderPlugins,
|
|
195
|
-
registerProvider: () => registerProvider,
|
|
196
191
|
removeAuth: () => removeAuth,
|
|
197
192
|
removeAuthStateFiles: () => removeAuthStateFiles,
|
|
198
193
|
removeAuthStateTarget: () => removeAuthStateTarget,
|
|
199
194
|
renderPatchedTestDoc: () => renderPatchedTestDoc,
|
|
195
|
+
reserveStdoutForData: () => reserveStdoutForData,
|
|
200
196
|
resolveAppiumExecutable: () => resolveAppiumExecutable,
|
|
201
197
|
resolveAuthStateForRun: () => resolveAuthStateForRun,
|
|
202
198
|
resolveAuthStatePaths: () => resolveAuthStatePaths,
|
|
203
199
|
resolveAuthStateRoot: () => resolveAuthStateRoot,
|
|
204
|
-
resolveFarmCredentials: () => resolveFarmCredentials,
|
|
205
200
|
resolveFileReferences: () => resolveFileReferences,
|
|
206
201
|
resolveKnowledgeRoot: () => resolveKnowledgeRoot,
|
|
207
202
|
resolveLLMAuth: () => resolveLLMAuth,
|
|
@@ -217,7 +212,6 @@ __export(index_exports, {
|
|
|
217
212
|
validateProject: () => validateProject,
|
|
218
213
|
validateSuiteFile: () => validateSuiteFile,
|
|
219
214
|
validateTestFile: () => validateTestFile,
|
|
220
|
-
validateTrustedRunAttributes: () => validateTrustedRunAttributes,
|
|
221
215
|
validateUserRunAttributes: () => validateUserRunAttributes,
|
|
222
216
|
warnIfOutOfBounds: () => warnIfOutOfBounds,
|
|
223
217
|
webViewSwipeSignal: () => webViewSwipeSignal,
|
|
@@ -352,7 +346,9 @@ var NamedLLMConfigSchema = ModelConfigBaseSchema.extend({
|
|
|
352
346
|
var TimeoutConfigSchema = import_zod2.z.object({
|
|
353
347
|
step: DurationString,
|
|
354
348
|
test: DurationString,
|
|
355
|
-
navigation: DurationString
|
|
349
|
+
navigation: DurationString.describe(
|
|
350
|
+
"How long a web page load or URL wait can take. Default 2m. Android and iOS runs do not use it."
|
|
351
|
+
)
|
|
356
352
|
}).strict();
|
|
357
353
|
var BrowserConfigSchema = import_zod2.z.object({
|
|
358
354
|
name: import_zod2.z.enum(["chromium", "firefox", "webkit"]),
|
|
@@ -362,7 +358,6 @@ var BrowserConfigSchema = import_zod2.z.object({
|
|
|
362
358
|
height: import_zod2.z.number()
|
|
363
359
|
}).strict().optional()
|
|
364
360
|
}).strict();
|
|
365
|
-
var TransportSchema = import_zod2.z.enum(["local", "browserstack"]);
|
|
366
361
|
var PlannerConfigSchema = import_zod2.z.object({
|
|
367
362
|
maxActions: import_zod2.z.number().int().positive().optional(),
|
|
368
363
|
maxPlanningTurns: import_zod2.z.number().int().positive().optional(),
|
|
@@ -386,69 +381,28 @@ var DashboardConfigSchema = import_zod3.z.object({
|
|
|
386
381
|
port: import_zod3.z.number().optional(),
|
|
387
382
|
dbPath: import_zod3.z.string().optional(),
|
|
388
383
|
artifactsDir: import_zod3.z.string().optional(),
|
|
389
|
-
//
|
|
390
|
-
// is a card with no `url` at all rather than a link to a route nothing serves. The card
|
|
391
|
-
// producer reads these through the masked config reader and renders
|
|
392
|
-
// `ENTITY_URL_ROUTE` (@vostride/agent-qa-schema) against them.
|
|
393
|
-
//
|
|
394
|
-
// They live on the EXISTING `services.dashboard` block rather than on a new top-level
|
|
395
|
-
// `dashboard` key: this project already has one dashboard configuration home, and a second
|
|
396
|
-
// one would be two places an operator could reasonably write the same fact.
|
|
397
|
-
//
|
|
398
|
-
// 2026-09-01 (Phase 408 plan 10, WR-04 — T-408-16): REFINED TO AN ABSOLUTE http(s) URL, because
|
|
399
|
-
// this value stops being a display string in M5. It composes the tokenless live link
|
|
400
|
-
// (`live-link.ts`'s `liveRunLinkOf`, which only strips trailing slashes), and that link is
|
|
401
|
-
// assigned to an `href` INSIDE the served App document — which runs with `allow-scripts` at an
|
|
402
|
-
// opaque origin — as well as appended verbatim to every carrier tool result's model-readable
|
|
403
|
-
// text. A `javascript:` base therefore composed a script-executing anchor. The trust boundary is
|
|
404
|
-
// the operator, so this is not remote-triggerable; the finding is that a URL-shaped value reached
|
|
405
|
-
// an `href` with no scheme check at any hop, and the guard costs one refinement.
|
|
406
|
-
//
|
|
407
|
-
// AT THE PARSE BOUNDARY, so there is exactly one place that decides. The defensive drop at the
|
|
408
|
-
// anchor itself is the SECOND half of the same finding and lives in `app-wrapper.ts`.
|
|
409
|
-
//
|
|
410
|
-
// STILL `.optional()`: an absent base is a real and honest posture — a standalone `agent-qa mcp`
|
|
411
|
-
// with no dashboard — and the carrier result names that absence rather than fabricating a URL.
|
|
412
|
-
// An EMPTY STRING is now REFUSED, which is stricter than before: `""` is not an absence an
|
|
413
|
-
// operator wrote by leaving the key out, it is a value that composes a relative fragment reading
|
|
414
|
-
// as a link and resolving against nothing.
|
|
384
|
+
// Only an absolute http(s) URL: the base composes an `href` inside the served App document.
|
|
415
385
|
baseUrl: import_zod3.z.string().refine(
|
|
416
386
|
isAbsoluteHttpUrl,
|
|
417
387
|
{ message: "services.dashboard.baseUrl must be an absolute http(s) URL" }
|
|
418
388
|
).optional(),
|
|
419
389
|
workspace: import_zod3.z.string().optional(),
|
|
420
|
-
//
|
|
421
|
-
//
|
|
422
|
-
//
|
|
423
|
-
//
|
|
424
|
-
// which REFUSES any non-loopback value — unconditionally since the phase-402 verification round
|
|
425
|
-
// (a declared run-service credential authenticates the three run-service paths and nothing else,
|
|
426
|
-
// so it was never evidence that the rest of this server's surface was authed).
|
|
427
|
-
//
|
|
428
|
-
// DELIBERATELY NOT `LoopbackHostSchema` even so. That schema refuses at PARSE time, which would
|
|
429
|
-
// make the runtime refusal unreachable and would report the problem as a config-shape error
|
|
430
|
-
// rather than as the security refusal it is — and it would also refuse a loopback SPELLING this
|
|
431
|
-
// field exists to allow (`localhost`, `::1`, `127.0.0.2` is refused on purpose).
|
|
390
|
+
// The run service and the other local doors are tokenless, so this block declares no token key.
|
|
391
|
+
// The interface the dashboard binds. A plain string, because the pre-listen bind guard refuses
|
|
392
|
+
// any non-loopback value at runtime, as the security refusal it is rather than a config-shape
|
|
393
|
+
// error, and a loopback spelling other than the default (`localhost`, `::1`) stays configurable.
|
|
432
394
|
host: import_zod3.z.string().trim().min(1).optional()
|
|
433
395
|
}).strict();
|
|
434
396
|
var LOOPBACK_HOSTS = Object.freeze(["localhost", "127.0.0.1", "::1"]);
|
|
435
397
|
function isLoopbackHost(host) {
|
|
436
398
|
return LOOPBACK_HOSTS.includes(host.trim());
|
|
437
399
|
}
|
|
438
|
-
var LoopbackHostSchema = import_zod3.z.string().trim().min(1).refine(
|
|
439
|
-
isLoopbackHost,
|
|
440
|
-
{ message: "MCP host must be localhost, 127.0.0.1, or ::1 in local-only mode" }
|
|
441
|
-
);
|
|
442
400
|
var McpConfigSchema = import_zod3.z.object({
|
|
443
401
|
enabled: import_zod3.z.boolean().optional(),
|
|
444
|
-
|
|
445
|
-
host: LoopbackHostSchema.optional(),
|
|
446
|
-
port: import_zod3.z.number().int().min(1).max(65535).optional(),
|
|
447
|
-
path: import_zod3.z.string().trim().min(1).regex(/^\//, "MCP path must start with /").optional()
|
|
402
|
+
port: import_zod3.z.number().int().min(1).max(65535).optional()
|
|
448
403
|
}).strict();
|
|
449
404
|
var CacheConfigSchema = import_zod3.z.object({
|
|
450
|
-
dir: import_zod3.z.string()
|
|
451
|
-
ttl: DurationString
|
|
405
|
+
dir: import_zod3.z.string()
|
|
452
406
|
}).strict();
|
|
453
407
|
var AuthStateConfigSchema = import_zod3.z.object({
|
|
454
408
|
dir: import_zod3.z.string().trim().min(1)
|
|
@@ -463,7 +417,7 @@ var RecordingConfigSchema = import_zod3.z.object({
|
|
|
463
417
|
var AccessibilityConfigSchema = import_zod3.z.object({
|
|
464
418
|
enabled: import_zod3.z.boolean(),
|
|
465
419
|
standard: import_zod3.z.enum(["wcag2a", "wcag2aa", "wcag2aaa"]).optional(),
|
|
466
|
-
runAfter: import_zod3.z.enum(["every-step", "
|
|
420
|
+
runAfter: import_zod3.z.enum(["every-step", "test-end"]).optional(),
|
|
467
421
|
failOnViolation: import_zod3.z.boolean().optional(),
|
|
468
422
|
disableRules: import_zod3.z.array(import_zod3.z.string()).optional(),
|
|
469
423
|
exclude: import_zod3.z.array(import_zod3.z.string()).optional()
|
|
@@ -490,14 +444,7 @@ var isRepoRelative = (dir) => !/^[/\\]|^[A-Za-z]:[\\/]/.test(dir) && !/^\.\.(?:[
|
|
|
490
444
|
var KnowledgeConfigSchema = import_zod4.z.object({
|
|
491
445
|
dir: import_zod4.z.string().trim().min(1).refine(isRepoRelative, {
|
|
492
446
|
message: "knowledge.dir must be repo-relative \u2014 the committed knowledge root must live inside the repo for PR review and the committed .gitignore split"
|
|
493
|
-
}).default("agent-qa-knowledge")
|
|
494
|
-
// D-08: closed enum — deliberately no 'full' mode; full-res artifacts ride the
|
|
495
|
-
// artifact store/git-LFS side channel per knowledge-store.md §5.
|
|
496
|
-
commitArtifacts: import_zod4.z.enum(["none", "thumbnails"]).default("none"),
|
|
497
|
-
// Absent = no cap: the engine's derived budget serves everything it ranks useful; when set
|
|
498
|
-
// it is a hard ceiling min(cap, derived), clamped above min_brief — efficacy over token
|
|
499
|
-
// saving (D-09, binds Phase 380 brief budgeting).
|
|
500
|
-
contextBudgetTokens: import_zod4.z.number().int().positive().optional()
|
|
447
|
+
}).default("agent-qa-knowledge")
|
|
501
448
|
}).strict();
|
|
502
449
|
|
|
503
450
|
// src/schema/retired-keys.ts
|
|
@@ -518,6 +465,39 @@ var RETIRED_MEMORY_KEY_MESSAGES = {
|
|
|
518
465
|
circuitBreakerBaselineSize: "services.memory.circuitBreakerBaselineSize was removed \u2014 no circuit breaker exists",
|
|
519
466
|
circuitBreakerThreshold: "services.memory.circuitBreakerThreshold was removed \u2014 no circuit breaker exists"
|
|
520
467
|
};
|
|
468
|
+
var RETIRED_EVAL_KEY_MESSAGES = {
|
|
469
|
+
gate1: "eval.gate1 was removed: memory is no longer screened by replay before promotion. Delete this block.",
|
|
470
|
+
gate2: "eval.gate2 was removed: completed runs no longer replay served memory. Delete this block.",
|
|
471
|
+
adversarial: "eval.adversarial was removed: runs no longer stage adversarial probes. Delete this block.",
|
|
472
|
+
seeding: "eval.seeding was removed: runs no longer seed an eval corpus. Delete this block.",
|
|
473
|
+
judge: "eval.judge was removed: pass --judge <name> to agent-qa eval replay. Delete this key.",
|
|
474
|
+
judges: "eval.judges was removed: repeat --judge <name> on agent-qa eval replay. Delete this key.",
|
|
475
|
+
judgeAllowSameFamily: "eval.judgeAllowSameFamily was removed: pass --judge-allow-same-family to agent-qa eval replay. Delete this key.",
|
|
476
|
+
k: "eval.k was removed: pass --k <n> to agent-qa eval replay. Delete this key.",
|
|
477
|
+
seed: "eval.seed was removed: pass --seed <n> to agent-qa eval replay. Delete this key."
|
|
478
|
+
};
|
|
479
|
+
var RETIRED_STEP_KEY_MESSAGES = {
|
|
480
|
+
retries: "steps[].retries was removed: a test retries as a whole; set meta.retries. Delete this line.",
|
|
481
|
+
screenshot: "steps[].screenshot was removed: screenshots are set per run; pass --screenshot-mode every-step to agent-qa run. Delete this line."
|
|
482
|
+
};
|
|
483
|
+
var RETIRED_EVAL_BLOCK_MESSAGE = "eval was removed: agent-qa eval replay takes its settings as flags. Delete this block.";
|
|
484
|
+
var RETIRED_ALLOW_PRODUCTION_MESSAGE = "allowProduction was removed: production targets run like any other target. Delete this line.";
|
|
485
|
+
var RETIRED_REGISTRY_PROVIDERS_MESSAGE = "registry.providers was removed: BrowserStack is no longer supported; runs use local Playwright and Appium. Delete this block.";
|
|
486
|
+
var RETIRED_DEVICE_TRANSPORT_MESSAGE = "registry.devices.<name>.transport was removed: devices run on local Appium. Delete this line.";
|
|
487
|
+
var RETIRED_APP_BROWSERSTACK_MESSAGE = "registry.targets.<name>.app.browserstack was removed: BrowserStack is no longer supported; app.path is the app file. Delete this line.";
|
|
488
|
+
var RETIRED_CACHE_TTL_MESSAGE = "services.cache.ttl was removed: cached plans do not expire by age. Delete this line.";
|
|
489
|
+
var RETIRED_MCP_KEY_MESSAGES = {
|
|
490
|
+
host: "services.mcp.host was removed: agent-qa mcp always listens on 127.0.0.1. Delete this line.",
|
|
491
|
+
path: "services.mcp.path was removed: the MCP server answers on any path. Delete this line.",
|
|
492
|
+
transport: "services.mcp.transport was removed: agent-qa mcp uses stdio, or HTTP with --http. Delete this line."
|
|
493
|
+
};
|
|
494
|
+
var RETIRED_RUN_AFTER_NAVIGATION_MESSAGE = "services.accessibility.runAfter: navigation was removed: it never ran a check. Use test-end, or delete the line for every-step.";
|
|
495
|
+
var RETIRED_KNOWLEDGE_KEY_MESSAGES = {
|
|
496
|
+
contextBudgetTokens: "knowledge.contextBudgetTokens was removed: agent-qa sizes each run's knowledge itself. Delete this line.",
|
|
497
|
+
commitArtifacts: "knowledge.commitArtifacts was removed: knowledge bundles never commit images. Delete this line."
|
|
498
|
+
};
|
|
499
|
+
var RETIRED_LOCAL_PROVIDERS_MESSAGE = "providers was removed: BrowserStack is no longer supported. Delete this block.";
|
|
500
|
+
var RETIRED_LOCAL_APP_BROWSERSTACK_MESSAGE = "apps.<name>.browserstack was removed: BrowserStack is no longer supported; apps.<name>.path is the app file. Delete this line.";
|
|
521
501
|
var RETIRED_MAX_SUB_ACTIONS_MESSAGE = "use.planner.maxSubActions was removed in v2 \u2014 it was a refilling iteration count; set use.planner.maxActions (default 30) and use.planner.maxPlanningTurns (default 30)";
|
|
522
502
|
var RETIRED_PLANNER_KEY = "maxSubActions";
|
|
523
503
|
function isPlainObject(value) {
|
|
@@ -531,7 +511,7 @@ var retiredKeyScan = import_zod5.z.any().superRefine((value, ctx) => {
|
|
|
531
511
|
if (isPlainObject(value.services) && value.services.memory !== void 0) {
|
|
532
512
|
ctx.addIssue({
|
|
533
513
|
code: import_zod5.z.ZodIssueCode.custom,
|
|
534
|
-
params: { retired: true },
|
|
514
|
+
params: { retired: true, hardCut: true },
|
|
535
515
|
path: ["services", "memory"],
|
|
536
516
|
message: "services.memory was removed in v2 \u2014 the memory config surface collapsed into the top-level knowledge block"
|
|
537
517
|
});
|
|
@@ -542,7 +522,7 @@ var retiredKeyScan = import_zod5.z.any().superRefine((value, ctx) => {
|
|
|
542
522
|
if (message) {
|
|
543
523
|
ctx.addIssue({
|
|
544
524
|
code: import_zod5.z.ZodIssueCode.custom,
|
|
545
|
-
params: { retired: true },
|
|
525
|
+
params: { retired: true, hardCut: true },
|
|
546
526
|
path: ["services", "memory", key],
|
|
547
527
|
message
|
|
548
528
|
});
|
|
@@ -553,30 +533,164 @@ var retiredKeyScan = import_zod5.z.any().superRefine((value, ctx) => {
|
|
|
553
533
|
if (isPlainObject(value.workspace) && value.workspace.agentRules !== void 0) {
|
|
554
534
|
ctx.addIssue({
|
|
555
535
|
code: import_zod5.z.ZodIssueCode.custom,
|
|
556
|
-
params: { retired: true },
|
|
536
|
+
params: { retired: true, hardCut: true },
|
|
557
537
|
path: ["workspace", "agentRules"],
|
|
558
538
|
message: "workspace.agentRules was removed in v2 \u2014 rules dissolve into agent-qa-knowledge/policies/<scope>.md files"
|
|
559
539
|
});
|
|
560
540
|
}
|
|
561
|
-
if (
|
|
541
|
+
if (Object.hasOwn(value, "eval")) {
|
|
542
|
+
let named = 0;
|
|
543
|
+
if (isPlainObject(value.eval)) {
|
|
544
|
+
for (const key of Object.keys(value.eval)) {
|
|
545
|
+
if (Object.hasOwn(RETIRED_EVAL_KEY_MESSAGES, key)) {
|
|
546
|
+
named++;
|
|
547
|
+
ctx.addIssue({
|
|
548
|
+
code: import_zod5.z.ZodIssueCode.custom,
|
|
549
|
+
params: { retired: true },
|
|
550
|
+
path: ["eval", key],
|
|
551
|
+
message: RETIRED_EVAL_KEY_MESSAGES[key]
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
if (named === 0) {
|
|
557
|
+
ctx.addIssue({
|
|
558
|
+
code: import_zod5.z.ZodIssueCode.custom,
|
|
559
|
+
params: { retired: true },
|
|
560
|
+
path: ["eval"],
|
|
561
|
+
message: RETIRED_EVAL_BLOCK_MESSAGE
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (Object.hasOwn(value, "allowProduction")) {
|
|
566
|
+
ctx.addIssue({
|
|
567
|
+
code: import_zod5.z.ZodIssueCode.custom,
|
|
568
|
+
params: { retired: true },
|
|
569
|
+
path: ["allowProduction"],
|
|
570
|
+
message: RETIRED_ALLOW_PRODUCTION_MESSAGE
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
if (isPlainObject(value.registry) && Object.hasOwn(value.registry, "providers")) {
|
|
574
|
+
ctx.addIssue({
|
|
575
|
+
code: import_zod5.z.ZodIssueCode.custom,
|
|
576
|
+
params: { retired: true },
|
|
577
|
+
path: ["registry", "providers"],
|
|
578
|
+
message: RETIRED_REGISTRY_PROVIDERS_MESSAGE
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
if (isPlainObject(value.registry) && isPlainObject(value.registry.devices)) {
|
|
582
|
+
for (const [name, device] of Object.entries(value.registry.devices)) {
|
|
583
|
+
if (isPlainObject(device) && Object.hasOwn(device, "transport")) {
|
|
584
|
+
ctx.addIssue({
|
|
585
|
+
code: import_zod5.z.ZodIssueCode.custom,
|
|
586
|
+
params: { retired: true },
|
|
587
|
+
path: ["registry", "devices", name, "transport"],
|
|
588
|
+
message: RETIRED_DEVICE_TRANSPORT_MESSAGE
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
if (isPlainObject(value.registry) && isPlainObject(value.registry.targets)) {
|
|
594
|
+
for (const [name, target] of Object.entries(value.registry.targets)) {
|
|
595
|
+
if (isPlainObject(target) && isPlainObject(target.app) && Object.hasOwn(target.app, "browserstack")) {
|
|
596
|
+
ctx.addIssue({
|
|
597
|
+
code: import_zod5.z.ZodIssueCode.custom,
|
|
598
|
+
params: { retired: true },
|
|
599
|
+
path: ["registry", "targets", name, "app", "browserstack"],
|
|
600
|
+
message: RETIRED_APP_BROWSERSTACK_MESSAGE
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
if (isPlainObject(value.services) && isPlainObject(value.services.cache) && Object.hasOwn(value.services.cache, "ttl")) {
|
|
562
606
|
ctx.addIssue({
|
|
563
607
|
code: import_zod5.z.ZodIssueCode.custom,
|
|
564
608
|
params: { retired: true },
|
|
609
|
+
path: ["services", "cache", "ttl"],
|
|
610
|
+
message: RETIRED_CACHE_TTL_MESSAGE
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
if (isPlainObject(value.services) && isPlainObject(value.services.mcp)) {
|
|
614
|
+
for (const key of Object.keys(value.services.mcp)) {
|
|
615
|
+
if (Object.hasOwn(RETIRED_MCP_KEY_MESSAGES, key)) {
|
|
616
|
+
ctx.addIssue({
|
|
617
|
+
code: import_zod5.z.ZodIssueCode.custom,
|
|
618
|
+
params: { retired: true },
|
|
619
|
+
path: ["services", "mcp", key],
|
|
620
|
+
message: RETIRED_MCP_KEY_MESSAGES[key]
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (isPlainObject(value.services) && isPlainObject(value.services.accessibility) && value.services.accessibility.runAfter === "navigation") {
|
|
626
|
+
ctx.addIssue({
|
|
627
|
+
code: import_zod5.z.ZodIssueCode.custom,
|
|
628
|
+
params: { retired: true },
|
|
629
|
+
path: ["services", "accessibility", "runAfter"],
|
|
630
|
+
message: RETIRED_RUN_AFTER_NAVIGATION_MESSAGE
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
if (isPlainObject(value.knowledge)) {
|
|
634
|
+
for (const key of Object.keys(value.knowledge)) {
|
|
635
|
+
if (Object.hasOwn(RETIRED_KNOWLEDGE_KEY_MESSAGES, key)) {
|
|
636
|
+
ctx.addIssue({
|
|
637
|
+
code: import_zod5.z.ZodIssueCode.custom,
|
|
638
|
+
params: { retired: true },
|
|
639
|
+
path: ["knowledge", key],
|
|
640
|
+
message: RETIRED_KNOWLEDGE_KEY_MESSAGES[key]
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
if (hasRetiredPlannerKey(value.use)) {
|
|
646
|
+
ctx.addIssue({
|
|
647
|
+
code: import_zod5.z.ZodIssueCode.custom,
|
|
648
|
+
params: { retired: true, hardCut: true },
|
|
565
649
|
path: ["use", "planner", RETIRED_PLANNER_KEY],
|
|
566
650
|
message: RETIRED_MAX_SUB_ACTIONS_MESSAGE
|
|
567
651
|
});
|
|
568
652
|
}
|
|
569
653
|
});
|
|
654
|
+
function refuseRetiredLocalBindings(raw, filePath) {
|
|
655
|
+
if (!isPlainObject(raw)) return;
|
|
656
|
+
const issues = [];
|
|
657
|
+
if (Object.hasOwn(raw, "providers")) {
|
|
658
|
+
issues.push({ path: "providers", message: RETIRED_LOCAL_PROVIDERS_MESSAGE });
|
|
659
|
+
}
|
|
660
|
+
if (isPlainObject(raw.apps)) {
|
|
661
|
+
for (const [name, app] of Object.entries(raw.apps)) {
|
|
662
|
+
if (isPlainObject(app) && Object.hasOwn(app, "browserstack")) {
|
|
663
|
+
issues.push({ path: `apps.${name}.browserstack`, message: RETIRED_LOCAL_APP_BROWSERSTACK_MESSAGE });
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
if (issues.length === 0) return;
|
|
668
|
+
throw new Error(
|
|
669
|
+
[`${filePath}:`, ...issues.map((issue) => ` ${issue.path}: ${issue.message}`)].join("\n")
|
|
670
|
+
);
|
|
671
|
+
}
|
|
570
672
|
var retiredUseKeyScan = import_zod5.z.any().superRefine((value, ctx) => {
|
|
571
673
|
if (hasRetiredPlannerKey(value)) {
|
|
572
674
|
ctx.addIssue({
|
|
573
675
|
code: import_zod5.z.ZodIssueCode.custom,
|
|
574
|
-
params: { retired: true },
|
|
676
|
+
params: { retired: true, hardCut: true },
|
|
575
677
|
path: ["planner", RETIRED_PLANNER_KEY],
|
|
576
678
|
message: RETIRED_MAX_SUB_ACTIONS_MESSAGE
|
|
577
679
|
});
|
|
578
680
|
}
|
|
579
681
|
});
|
|
682
|
+
var retiredStepKeyScan = import_zod5.z.any().superRefine((value, ctx) => {
|
|
683
|
+
if (!isPlainObject(value)) return;
|
|
684
|
+
for (const [key, message] of Object.entries(RETIRED_STEP_KEY_MESSAGES)) {
|
|
685
|
+
if (!Object.hasOwn(value, key)) continue;
|
|
686
|
+
ctx.addIssue({
|
|
687
|
+
code: import_zod5.z.ZodIssueCode.custom,
|
|
688
|
+
params: { retired: true },
|
|
689
|
+
path: [key],
|
|
690
|
+
message
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
});
|
|
580
694
|
|
|
581
695
|
// src/schema/registry-schema.ts
|
|
582
696
|
var import_zod7 = require("zod");
|
|
@@ -606,8 +720,7 @@ function isAbsoluteAppPath(value) {
|
|
|
606
720
|
return value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
607
721
|
}
|
|
608
722
|
var AppInstallSchema = import_zod7.z.object({
|
|
609
|
-
path: import_zod7.z.string().refine((v) => !isAbsoluteAppPath(v), { message: "app.path must be relative" }).optional()
|
|
610
|
-
browserstack: import_zod7.z.string().optional()
|
|
723
|
+
path: import_zod7.z.string().refine((v) => !isAbsoluteAppPath(v), { message: "app.path must be relative" }).optional()
|
|
611
724
|
}).strict();
|
|
612
725
|
var TargetSchema = import_zod7.z.object({
|
|
613
726
|
product: import_zod7.z.string().refine(
|
|
@@ -620,15 +733,8 @@ var TargetSchema = import_zod7.z.object({
|
|
|
620
733
|
appActivity: import_zod7.z.string().optional(),
|
|
621
734
|
app: AppInstallSchema.optional(),
|
|
622
735
|
url: import_zod7.z.string().optional(),
|
|
623
|
-
//
|
|
624
|
-
|
|
625
|
-
// (D-18) undeclared means non-production, decided where the production refusal reads it (the
|
|
626
|
-
// run gate that decided "shared" is deleted). A default here would make silence and an honest
|
|
627
|
-
// declaration indistinguishable at every later reader, and
|
|
628
|
-
// `.strict()` above is what turns a typoed key into a refusal rather than a silent undeclared.
|
|
629
|
-
// 432 D-18: `production` is the third declared class; only top-level `allowProduction: true`
|
|
630
|
-
// opts a run into it.
|
|
631
|
-
environment: import_zod7.z.enum(["local", "shared", "production"]).optional()
|
|
736
|
+
// A label only: agent-qa runs every target the same way, whatever it declares.
|
|
737
|
+
environment: import_zod7.z.enum(["local", "shared", "production"]).optional().describe("A label for this target's environment. agent-qa runs every target the same way.")
|
|
632
738
|
}).strict().superRefine((val, ctx) => {
|
|
633
739
|
if (val.platform === "web" && !val.url) {
|
|
634
740
|
ctx.addIssue({ code: import_zod7.z.ZodIssueCode.custom, message: "Web targets must have a url" });
|
|
@@ -636,30 +742,25 @@ var TargetSchema = import_zod7.z.object({
|
|
|
636
742
|
});
|
|
637
743
|
var DeviceProfileSchema = import_zod7.z.object({
|
|
638
744
|
platform: import_zod7.z.enum(["android", "ios"]),
|
|
639
|
-
transport: TransportSchema,
|
|
640
745
|
match: import_zod7.z.record(import_zod7.z.string(), import_zod7.z.unknown()).optional().default({})
|
|
641
746
|
}).strict().superRefine((val, ctx) => {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
});
|
|
653
|
-
}
|
|
747
|
+
const androidOnly = ["avd", "serial", "appPackage", "appActivity", "automationName", "browserName", "platformVersion"];
|
|
748
|
+
const iosOnly = ["udid", "bundleId", "automationName", "platformVersion"];
|
|
749
|
+
const allowed = val.platform === "android" ? androidOnly : iosOnly;
|
|
750
|
+
for (const key of Object.keys(val.match)) {
|
|
751
|
+
if (!allowed.includes(key)) {
|
|
752
|
+
ctx.addIssue({
|
|
753
|
+
code: import_zod7.z.ZodIssueCode.custom,
|
|
754
|
+
path: ["match", key],
|
|
755
|
+
message: `Match field "${key}" is not valid for an ${val.platform} device. Allowed: ${allowed.join(", ")}`
|
|
756
|
+
});
|
|
654
757
|
}
|
|
655
758
|
}
|
|
656
759
|
});
|
|
657
|
-
var ProviderConfigSchema = import_zod7.z.record(import_zod7.z.string(), import_zod7.z.unknown());
|
|
658
760
|
var RegistrySchema = import_zod7.z.object({
|
|
659
761
|
llms: import_zod7.z.array(NamedLLMConfigSchema).optional().default([]),
|
|
660
762
|
targets: import_zod7.z.record(TargetNameSchema, TargetSchema).optional(),
|
|
661
|
-
devices: import_zod7.z.record(import_zod7.z.string(), DeviceProfileSchema).optional()
|
|
662
|
-
providers: import_zod7.z.record(import_zod7.z.string(), ProviderConfigSchema).optional()
|
|
763
|
+
devices: import_zod7.z.record(import_zod7.z.string(), DeviceProfileSchema).optional()
|
|
663
764
|
}).strict();
|
|
664
765
|
|
|
665
766
|
// src/schema/use-schema.ts
|
|
@@ -703,28 +804,11 @@ var UseSchema = import_zod8.z.object({
|
|
|
703
804
|
logCapture: LogCaptureConfigSchema.optional(),
|
|
704
805
|
cache: import_zod8.z.boolean().optional(),
|
|
705
806
|
llm: import_zod8.z.string().optional(),
|
|
706
|
-
|
|
707
|
-
* Names the app-under-test build for the DCAY-01 buildTag-to-ordinal mapping.
|
|
708
|
-
* Precedence: CLI --build-tag > AGENT_QA_BUILD_TAG > this config key.
|
|
709
|
-
*/
|
|
807
|
+
// Precedence: CLI `--build-tag`, then `AGENT_QA_BUILD_TAG`, then this key.
|
|
710
808
|
buildTag: import_zod8.z.string().trim().min(1).optional(),
|
|
711
|
-
|
|
712
|
-
* 402-03 (D-12): run the members of one run CONCURRENTLY. Absent or false is one member at a
|
|
713
|
-
* time — today's behaviour, byte for byte.
|
|
714
|
-
*/
|
|
809
|
+
// Runs one run's members concurrently; absent or false runs one member at a time.
|
|
715
810
|
parallel: import_zod8.z.boolean().optional(),
|
|
716
|
-
|
|
717
|
-
* 402-03 (D-12): the ceiling on concurrent members when `parallel` is true. Absent resolves to
|
|
718
|
-
* `DEFAULT_PARALLEL_WORKERS` (3). Effective concurrency is `min(memberCount, workers)`, decided
|
|
719
|
-
* in exactly one place — `resolveEffectiveWorkers` in `@vostride/agent-qa-schema`.
|
|
720
|
-
*
|
|
721
|
-
* A POSITIVE INTEGER, refused otherwise: an unbounded or nonsensical count here is the only
|
|
722
|
-
* reachable input to a concurrency denial-of-service (T-402-12), and it is a config typo rather
|
|
723
|
-
* than a caller's field — `RunsStartInput` names neither of these two.
|
|
724
|
-
*
|
|
725
|
-
* NOT `.default(...)`: a defaulted key changes the PARSED config type and every hand-built
|
|
726
|
-
* config fixture would then need it. The default is the resolver's, not the schema's.
|
|
727
|
-
*/
|
|
811
|
+
// Not defaulted, so the parsed type is unchanged; `resolveEffectiveWorkers` owns the default.
|
|
728
812
|
workers: import_zod8.z.number().int().positive().optional()
|
|
729
813
|
}).strict();
|
|
730
814
|
var UseOverrideSchema = import_zod8.z.object({
|
|
@@ -739,7 +823,9 @@ var UseOverrideSchema = import_zod8.z.object({
|
|
|
739
823
|
timeout: import_zod8.z.object({
|
|
740
824
|
step: DurationString.optional(),
|
|
741
825
|
test: DurationString.optional(),
|
|
742
|
-
navigation: DurationString.optional()
|
|
826
|
+
navigation: DurationString.optional().describe(
|
|
827
|
+
"How long a web page load or URL wait can take. Default 2m. Android and iOS runs do not use it."
|
|
828
|
+
)
|
|
743
829
|
}).strict().optional(),
|
|
744
830
|
planner: import_zod8.z.object({
|
|
745
831
|
maxActions: import_zod8.z.number().int().positive().optional(),
|
|
@@ -755,7 +841,7 @@ var UseOverrideSchema = import_zod8.z.object({
|
|
|
755
841
|
mobile: MobileUseOverrideSchema.optional(),
|
|
756
842
|
llm: import_zod8.z.string().optional(),
|
|
757
843
|
parallel: import_zod8.z.boolean().optional(),
|
|
758
|
-
|
|
844
|
+
// The override half of `use.workers`, shaped like `UseSchema`'s.
|
|
759
845
|
workers: import_zod8.z.number().int().positive().optional(),
|
|
760
846
|
device: import_zod8.z.string().optional()
|
|
761
847
|
}).strict();
|
|
@@ -799,82 +885,15 @@ var PluginsSchema = import_zod10.z.object({
|
|
|
799
885
|
var AgentQaConfigSchemaInner = import_zod10.z.object({
|
|
800
886
|
workspace: WorkspaceSchema,
|
|
801
887
|
services: ServicesSchema.prefault({}).optional(),
|
|
802
|
-
//
|
|
803
|
-
// configs always materialize knowledge defaults (zod 4 .default() short-circuits without
|
|
804
|
-
// parsing — .prefault() runs the inner defaults) — matches the providers `.default([])`
|
|
805
|
-
// precedent in spirit.
|
|
888
|
+
// `.prefault` runs the inner defaults; zod 4's `.default()` short-circuits without parsing.
|
|
806
889
|
knowledge: KnowledgeConfigSchema.prefault({}),
|
|
807
890
|
registry: RegistrySchema.optional(),
|
|
808
891
|
use: UseSchema.optional(),
|
|
809
892
|
plugins: PluginsSchema.optional(),
|
|
810
|
-
analytics: AnalyticsSchema.optional()
|
|
811
|
-
//
|
|
812
|
-
//
|
|
813
|
-
allowProduction: import_zod10.z.boolean().optional(),
|
|
814
|
-
// 2026-10-01 (432-22, D-15/D-18/D-22): the `safety` section (`safety.trustMode`) is deleted with
|
|
815
|
-
// trust mode. `allowProduction` above is the only production opt-in, and a file that still
|
|
816
|
-
// declares `safety` gets the normal strict unrecognized-key error.
|
|
817
|
-
// D-03 (Phase 372): eval substrate params. `judge` references a NAMED registry.llms entry;
|
|
818
|
-
// strict so unknown eval subkeys are rejected.
|
|
819
|
-
eval: import_zod10.z.object({
|
|
820
|
-
judge: import_zod10.z.string().min(1).refine((name) => name.trim().length > 0).optional(),
|
|
821
|
-
judges: import_zod10.z.array(import_zod10.z.string().min(1).refine((name) => name.trim().length > 0)).max(3).optional(),
|
|
822
|
-
// Owner override of D-10 (2026-07-12): explicit default-off opt-in that lets a
|
|
823
|
-
// KNOWN-same-family judge run; the eligibility law discloses the override on every
|
|
824
|
-
// scored entry (familyOverride) and the report projection renders one honesty line.
|
|
825
|
-
// Unknown families stay forbidden regardless.
|
|
826
|
-
judgeAllowSameFamily: import_zod10.z.boolean().optional(),
|
|
827
|
-
k: import_zod10.z.number().int().positive().optional(),
|
|
828
|
-
seed: import_zod10.z.number().int().optional(),
|
|
829
|
-
// 2026-09-07, 417 D-19: Gate-1 screen cap; recorded default lives in the CLI.
|
|
830
|
-
// 2026-09-11, 422 D-14: `maxPerJob` bounds how many Gate-1 CALLS one post-run proposal job
|
|
831
|
-
// may make; recorded default lives beside the handler (PROPOSE_DEFAULT_MAX_GATE1_PER_JOB).
|
|
832
|
-
// A SECOND axis beside `maxScreens`, not a refinement of it — that one bounds how much a
|
|
833
|
-
// single screening looks at, this one bounds how many screenings a run's tail buys — and an
|
|
834
|
-
// operator can legitimately want a generous first and a tight second. Positive-integer only,
|
|
835
|
-
// for the sharper version of the usual reason: a bound that silently fell back to the
|
|
836
|
-
// recorded default because the operator typed `0` would buy replay work they refused. There
|
|
837
|
-
// is deliberately NO CLI flag: the bound is charged durably per job and read back by a
|
|
838
|
-
// resumed attempt, so a per-invocation flag would be a bound that could change between the
|
|
839
|
-
// attempt that spent it and the attempt that has to honour what was spent.
|
|
840
|
-
gate1: import_zod10.z.object({
|
|
841
|
-
maxScreens: import_zod10.z.number().int().positive().optional(),
|
|
842
|
-
maxPerJob: import_zod10.z.number().int().positive().optional()
|
|
843
|
-
}).strict().optional(),
|
|
844
|
-
// 2026-09-07, 418 D-04/D-13/D-14: Gate-2 ablation cap, canary horizon (build ordinals)
|
|
845
|
-
// and per-scope lower-trust cap; recorded defaults live in the CLI (GATE2_DEFAULT_*).
|
|
846
|
-
gate2: import_zod10.z.object({
|
|
847
|
-
maxAblations: import_zod10.z.number().int().positive().optional(),
|
|
848
|
-
canaryHorizonBuilds: import_zod10.z.number().int().positive().optional(),
|
|
849
|
-
maxLowerTrustPerScope: import_zod10.z.number().int().positive().optional()
|
|
850
|
-
}).strict().optional(),
|
|
851
|
-
// 2026-09-11, 422 D-04: how many NOVEL seeds one adversarial refresh may derive from
|
|
852
|
-
// before the rest are counted and deferred; recorded default lives in eval core
|
|
853
|
-
// (ADVERSARIAL_DEFAULT_MAX_SEEDS_PER_REFRESH). Strict, and positive-integer only: a cap
|
|
854
|
-
// that silently fell back to the default because the operator typed `0` is not a cap.
|
|
855
|
-
// 2026-09-11, 422 D-04/D-10: `maxProbeCalls` bounds PROVIDER CALLS one refresh may make to
|
|
856
|
-
// author probes; recorded default lives in eval-local
|
|
857
|
-
// (ADVERSARIAL_DEFAULT_MAX_PROBE_CALLS). It is a SECOND budget beside the seed cap on
|
|
858
|
-
// purpose — deterministic seeds cost CPU and authored probes cost money no later run can
|
|
859
|
-
// recover — and it is held to the same positive-integer strictness for the sharper version
|
|
860
|
-
// of the same reason: a cap that silently fell back to the default because the operator
|
|
861
|
-
// typed `0` would buy calls they refused.
|
|
862
|
-
adversarial: import_zod10.z.object({
|
|
863
|
-
maxSeedsPerRefresh: import_zod10.z.number().int().positive().optional(),
|
|
864
|
-
maxProbeCalls: import_zod10.z.number().int().positive().optional()
|
|
865
|
-
}).strict().optional(),
|
|
866
|
-
seeding: import_zod10.z.object({
|
|
867
|
-
silverPerTestCap: import_zod10.z.number().int().positive().optional()
|
|
868
|
-
}).strict().optional()
|
|
869
|
-
}).strict().optional()
|
|
893
|
+
analytics: AnalyticsSchema.optional()
|
|
894
|
+
// There is no `safety` section: a file that still declares one gets the normal strict
|
|
895
|
+
// unrecognized-key error.
|
|
870
896
|
}).strict().superRefine((val, ctx) => {
|
|
871
|
-
if (val.eval?.judge !== void 0 && val.eval.judges !== void 0) {
|
|
872
|
-
ctx.addIssue({
|
|
873
|
-
code: import_zod10.z.ZodIssueCode.custom,
|
|
874
|
-
path: ["eval"],
|
|
875
|
-
message: "Set either eval.judge or eval.judges, never both; remove one declaration."
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
897
|
const llms = val.registry?.llms ?? [];
|
|
879
898
|
const names = llms.map((c) => c.name);
|
|
880
899
|
const dupes = names.filter((n, i) => names.indexOf(n) !== i);
|
|
@@ -899,13 +918,6 @@ var AgentQaConfigSchemaInner = import_zod10.z.object({
|
|
|
899
918
|
message: `use.llm "${val.use.llm}" does not match any name in registry.llms`
|
|
900
919
|
});
|
|
901
920
|
}
|
|
902
|
-
if (!MobileAppStateSchema.safeParse(val.use?.mobile?.appState).success) {
|
|
903
|
-
ctx.addIssue({
|
|
904
|
-
code: import_zod10.z.ZodIssueCode.custom,
|
|
905
|
-
path: ["use", "mobile", "appState"],
|
|
906
|
-
message: "use.mobile.appState is required and must be one of: preserve | reset"
|
|
907
|
-
});
|
|
908
|
-
}
|
|
909
921
|
});
|
|
910
922
|
var AgentQaConfigSchema = retiredKeyScan.pipe(AgentQaConfigSchemaInner);
|
|
911
923
|
|
|
@@ -928,8 +940,6 @@ var TestStepSchema = import_zod11.z.union([
|
|
|
928
940
|
import_zod11.z.object({
|
|
929
941
|
step: import_zod11.z.string(),
|
|
930
942
|
timeout: OptionalDuration,
|
|
931
|
-
retries: import_zod11.z.number().optional(),
|
|
932
|
-
screenshot: import_zod11.z.boolean().optional(),
|
|
933
943
|
capture: CaptureConfigSchema.optional()
|
|
934
944
|
}).strict()
|
|
935
945
|
]);
|
|
@@ -952,19 +962,9 @@ var TestDefinitionSchema = import_zod11.z.object({
|
|
|
952
962
|
meta: TestMetaSchema.optional(),
|
|
953
963
|
setup: import_zod11.z.array(HookIdSchema).optional(),
|
|
954
964
|
teardown: import_zod11.z.array(HookIdSchema).optional(),
|
|
955
|
-
steps: import_zod11.z.array(TestStepSchema).min(1)
|
|
965
|
+
steps: import_zod11.z.array(retiredStepKeyScan.pipe(TestStepSchema)).min(1)
|
|
956
966
|
}).strict();
|
|
957
967
|
|
|
958
|
-
// src/schema/action-schema.ts
|
|
959
|
-
var import_zod12 = require("zod");
|
|
960
|
-
var ActionPlanSchema = import_zod12.z.object({
|
|
961
|
-
reasoning: import_zod12.z.string().describe("Brief explanation of why this action was chosen"),
|
|
962
|
-
action: import_zod12.z.object({ type: import_zod12.z.string() }).passthrough(),
|
|
963
|
-
confidence: import_zod12.z.number().describe("Confidence level from 0 to 1"),
|
|
964
|
-
stepComplete: import_zod12.z.boolean().default(false).describe("Whether this action completes the step goal. Set true when the step objective is fully accomplished, false when more actions are needed."),
|
|
965
|
-
stepFailed: import_zod12.z.boolean().optional().default(false).describe("Set true when the step goal cannot be achieved from the current screen state. When true, no action is executed and the step fails with the reasoning as the error message.")
|
|
966
|
-
});
|
|
967
|
-
|
|
968
968
|
// src/schema/resolve-knowledge-root.ts
|
|
969
969
|
var import_node_path = __toESM(require("path"), 1);
|
|
970
970
|
function resolveKnowledgeRoot(knowledge, configDir) {
|
|
@@ -972,66 +972,66 @@ function resolveKnowledgeRoot(knowledge, configDir) {
|
|
|
972
972
|
}
|
|
973
973
|
|
|
974
974
|
// src/schema/suite-schema.ts
|
|
975
|
-
var
|
|
975
|
+
var import_zod12 = require("zod");
|
|
976
976
|
var import_agent_qa_ids2 = require("@vostride/agent-qa-ids");
|
|
977
|
-
var SuiteTestEntrySchema =
|
|
978
|
-
test:
|
|
979
|
-
id:
|
|
977
|
+
var SuiteTestEntrySchema = import_zod12.z.object({
|
|
978
|
+
test: import_zod12.z.string(),
|
|
979
|
+
id: import_zod12.z.string().refine((value) => (0, import_agent_qa_ids2.isCanonicalTestId)(value), {
|
|
980
980
|
message: "Test ID must be t_ followed by 10 id-agent words"
|
|
981
981
|
})
|
|
982
982
|
}).strict();
|
|
983
|
-
var HookIdSchema2 =
|
|
983
|
+
var HookIdSchema2 = import_zod12.z.string().refine((value) => (0, import_agent_qa_ids2.isCanonicalHookId)(value), {
|
|
984
984
|
message: "Hook ID must be h_ followed by 10 id-agent words"
|
|
985
985
|
});
|
|
986
|
-
var SuiteDefinitionSchema =
|
|
987
|
-
"suite-id":
|
|
986
|
+
var SuiteDefinitionSchema = import_zod12.z.object({
|
|
987
|
+
"suite-id": import_zod12.z.string().refine((value) => (0, import_agent_qa_ids2.isCanonicalSuiteId)(value), {
|
|
988
988
|
message: "Suite ID must be s_ followed by 10 id-agent words"
|
|
989
989
|
}).optional(),
|
|
990
|
-
name:
|
|
991
|
-
target:
|
|
992
|
-
context:
|
|
993
|
-
setup:
|
|
994
|
-
teardown:
|
|
995
|
-
tests:
|
|
990
|
+
name: import_zod12.z.string(),
|
|
991
|
+
target: import_zod12.z.string(),
|
|
992
|
+
context: import_zod12.z.string().optional(),
|
|
993
|
+
setup: import_zod12.z.array(HookIdSchema2).optional(),
|
|
994
|
+
teardown: import_zod12.z.array(HookIdSchema2).optional(),
|
|
995
|
+
tests: import_zod12.z.array(SuiteTestEntrySchema).min(1),
|
|
996
996
|
use: retiredUseKeyScan.pipe(UseOverrideSchema).optional()
|
|
997
997
|
}).strict();
|
|
998
998
|
|
|
999
999
|
// src/hooks/schema.ts
|
|
1000
|
-
var
|
|
1000
|
+
var import_zod13 = require("zod");
|
|
1001
1001
|
var import_ms3 = __toESM(require("ms"), 1);
|
|
1002
1002
|
var import_agent_qa_ids3 = require("@vostride/agent-qa-ids");
|
|
1003
|
-
var HookDurationString =
|
|
1003
|
+
var HookDurationString = import_zod13.z.string().refine((v) => (0, import_ms3.default)(v) !== void 0, {
|
|
1004
1004
|
message: 'Invalid duration format (e.g., "30s", "2m", "1h")'
|
|
1005
1005
|
}).transform((v) => (0, import_ms3.default)(v));
|
|
1006
1006
|
var HOOK_ID_MESSAGE = "Hook ID must be h_ followed by 10 id-agent words";
|
|
1007
|
-
var HookDefinitionSchema =
|
|
1008
|
-
id:
|
|
1007
|
+
var HookDefinitionSchema = import_zod13.z.object({
|
|
1008
|
+
id: import_zod13.z.string().refine((value) => (0, import_agent_qa_ids3.isCanonicalHookId)(value), {
|
|
1009
1009
|
message: HOOK_ID_MESSAGE
|
|
1010
1010
|
}),
|
|
1011
|
-
name:
|
|
1012
|
-
runtime:
|
|
1013
|
-
file:
|
|
1014
|
-
deps:
|
|
1015
|
-
packageFile:
|
|
1011
|
+
name: import_zod13.z.string().min(1),
|
|
1012
|
+
runtime: import_zod13.z.enum(["node", "bun", "python", "bash"]),
|
|
1013
|
+
file: import_zod13.z.string().min(1),
|
|
1014
|
+
deps: import_zod13.z.array(import_zod13.z.string()).optional().default([]),
|
|
1015
|
+
packageFile: import_zod13.z.string().optional(),
|
|
1016
1016
|
timeout: HookDurationString,
|
|
1017
|
-
network:
|
|
1017
|
+
network: import_zod13.z.boolean().optional().default(true)
|
|
1018
1018
|
});
|
|
1019
|
-
var HooksFileSchema =
|
|
1020
|
-
hooks:
|
|
1019
|
+
var HooksFileSchema = import_zod13.z.object({
|
|
1020
|
+
hooks: import_zod13.z.array(HookDefinitionSchema)
|
|
1021
1021
|
}).superRefine((val, ctx) => {
|
|
1022
1022
|
const ids = /* @__PURE__ */ new Set();
|
|
1023
1023
|
const names = /* @__PURE__ */ new Set();
|
|
1024
1024
|
for (let i = 0; i < val.hooks.length; i++) {
|
|
1025
1025
|
if (ids.has(val.hooks[i].id)) {
|
|
1026
1026
|
ctx.addIssue({
|
|
1027
|
-
code:
|
|
1027
|
+
code: import_zod13.z.ZodIssueCode.custom,
|
|
1028
1028
|
path: ["hooks", i, "id"],
|
|
1029
1029
|
message: `Duplicate hook id: "${val.hooks[i].id}"`
|
|
1030
1030
|
});
|
|
1031
1031
|
}
|
|
1032
1032
|
if (names.has(val.hooks[i].name)) {
|
|
1033
1033
|
ctx.addIssue({
|
|
1034
|
-
code:
|
|
1034
|
+
code: import_zod13.z.ZodIssueCode.custom,
|
|
1035
1035
|
path: ["hooks", i, "name"],
|
|
1036
1036
|
message: `Duplicate hook name: "${val.hooks[i].name}"`
|
|
1037
1037
|
});
|
|
@@ -1386,7 +1386,7 @@ var PatchOpUnsupportedTargetError = class extends Error {
|
|
|
1386
1386
|
var PatchedDocumentInvalidError = class extends Error {
|
|
1387
1387
|
constructor(phase, detail) {
|
|
1388
1388
|
super(
|
|
1389
|
-
`cst-patch: the ${phase} document is not a valid test: ${detail}; a patched document is schema-validated before any caller may write it
|
|
1389
|
+
`cst-patch: the ${phase} document is not a valid test: ${detail}; a patched document is schema-validated before any caller may write it`
|
|
1390
1390
|
);
|
|
1391
1391
|
this.phase = phase;
|
|
1392
1392
|
this.detail = detail;
|
|
@@ -1482,7 +1482,7 @@ function resolveSeqSlot(doc, pointer, segments, op) {
|
|
|
1482
1482
|
if (segments.length < 2) {
|
|
1483
1483
|
throw new PatchOpUnsupportedTargetError(
|
|
1484
1484
|
pointer,
|
|
1485
|
-
`${op} operates on a sequence item, and that path names a top-level key; adding or removing a key is a document rewrite
|
|
1485
|
+
`${op} operates on a sequence item, and that path names a top-level key; adding or removing a key is a document rewrite, not a patch`
|
|
1486
1486
|
);
|
|
1487
1487
|
}
|
|
1488
1488
|
const parent = resolveNode(doc, segments.slice(0, -1));
|
|
@@ -2151,9 +2151,6 @@ var MobileSetupError = class extends Error {
|
|
|
2151
2151
|
function isMobilePlatform(value) {
|
|
2152
2152
|
return value === "android" || value === "ios";
|
|
2153
2153
|
}
|
|
2154
|
-
function isMobileTransport(value) {
|
|
2155
|
-
return value === "local" || value === "browserstack";
|
|
2156
|
-
}
|
|
2157
2154
|
function asOptionalString(value) {
|
|
2158
2155
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
2159
2156
|
}
|
|
@@ -2225,11 +2222,6 @@ function resolveAppInstall(target, input, platform, deviceName, sourceTrace) {
|
|
|
2225
2222
|
const pathValue = localPath ?? targetPath;
|
|
2226
2223
|
const pathSource = localPath ? `agent-qa.local.yaml apps.${input.targetName}.path` : targetPath ? `agent-qa.config.yaml registry.targets.${input.targetName}.app.path` : void 0;
|
|
2227
2224
|
const pathBaseDir = localPath ? baseDirFor(input.localConfigFilePath ?? input.localBindings?.filePath) : targetPath ? baseDirFor(input.configFilePath) : void 0;
|
|
2228
|
-
const localBrowserStack = asOptionalString(localApp?.browserstack);
|
|
2229
|
-
const targetBrowserStack = asOptionalString(target.app?.browserstack);
|
|
2230
|
-
const browserstackValue = localBrowserStack ?? targetBrowserStack;
|
|
2231
|
-
const browserstackSource = localBrowserStack ? `agent-qa.local.yaml apps.${input.targetName}.browserstack` : targetBrowserStack ? `agent-qa.config.yaml registry.targets.${input.targetName}.app.browserstack` : void 0;
|
|
2232
|
-
const browserstackBaseDir = localBrowserStack ? baseDirFor(input.localConfigFilePath ?? input.localBindings?.filePath) : targetBrowserStack ? baseDirFor(input.configFilePath) : void 0;
|
|
2233
2225
|
const install = { sourceTrace: installSourceTrace };
|
|
2234
2226
|
if (pathValue) {
|
|
2235
2227
|
if (isAbsoluteAppInstallValue(pathValue)) {
|
|
@@ -2255,23 +2247,7 @@ function resolveAppInstall(target, input, platform, deviceName, sourceTrace) {
|
|
|
2255
2247
|
sourceTrace.push(`app.path=${pathSource}`);
|
|
2256
2248
|
}
|
|
2257
2249
|
}
|
|
2258
|
-
|
|
2259
|
-
if (isAbsoluteAppInstallValue(browserstackValue)) {
|
|
2260
|
-
throw appInstallError(`Configured BrowserStack app value must be relative or a BrowserStack reference: ${browserstackValue}`, input, {
|
|
2261
|
-
platform,
|
|
2262
|
-
deviceName,
|
|
2263
|
-
appId: browserstackValue,
|
|
2264
|
-
sourceTrace: [...sourceTrace, `app.browserstack=${browserstackSource}`]
|
|
2265
|
-
});
|
|
2266
|
-
}
|
|
2267
|
-
install.browserstack = browserstackValue;
|
|
2268
|
-
install.browserstackBaseDir = browserstackBaseDir;
|
|
2269
|
-
if (browserstackSource) {
|
|
2270
|
-
installSourceTrace["app.browserstack"] = browserstackSource;
|
|
2271
|
-
sourceTrace.push(`app.browserstack=${browserstackSource}`);
|
|
2272
|
-
}
|
|
2273
|
-
}
|
|
2274
|
-
return install.path || install.browserstack ? install : void 0;
|
|
2250
|
+
return install.path ? install : void 0;
|
|
2275
2251
|
}
|
|
2276
2252
|
function resolveMobileRunConfig(input) {
|
|
2277
2253
|
const targets = input.config.registry?.targets;
|
|
@@ -2325,7 +2301,6 @@ function resolveMobileRunConfig(input) {
|
|
|
2325
2301
|
{ platform, deviceName }
|
|
2326
2302
|
);
|
|
2327
2303
|
}
|
|
2328
|
-
const transport = isMobileTransport(deviceProfile.transport) ? deviceProfile.transport : "local";
|
|
2329
2304
|
const localMatch = input.localBindings?.devices?.[deviceName] ?? null;
|
|
2330
2305
|
const mergedMatch = { ...deviceProfile.match ?? {}, ...localMatch ?? {} };
|
|
2331
2306
|
const appSourceTrace = {};
|
|
@@ -2367,11 +2342,9 @@ function resolveMobileRunConfig(input) {
|
|
|
2367
2342
|
platform,
|
|
2368
2343
|
targetName: input.targetName,
|
|
2369
2344
|
deviceName,
|
|
2370
|
-
transport,
|
|
2371
2345
|
device: {
|
|
2372
2346
|
name: deviceName,
|
|
2373
2347
|
platform,
|
|
2374
|
-
transport,
|
|
2375
2348
|
match: mergedMatch
|
|
2376
2349
|
},
|
|
2377
2350
|
app,
|
|
@@ -3321,7 +3294,7 @@ function clearLoadedProviderPluginModules() {
|
|
|
3321
3294
|
}
|
|
3322
3295
|
|
|
3323
3296
|
// src/logging/types.ts
|
|
3324
|
-
var
|
|
3297
|
+
var import_zod14 = require("zod");
|
|
3325
3298
|
var LOG_LEVEL_PRIORITY = {
|
|
3326
3299
|
silent: 0,
|
|
3327
3300
|
error: 1,
|
|
@@ -3329,46 +3302,45 @@ var LOG_LEVEL_PRIORITY = {
|
|
|
3329
3302
|
info: 3,
|
|
3330
3303
|
debug: 4
|
|
3331
3304
|
};
|
|
3332
|
-
var AgentLogDataSchema =
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
elementCount: import_zod15.z.number().optional()
|
|
3305
|
+
var AgentLogDataSchema = import_zod14.z.object({
|
|
3306
|
+
hasScreenshot: import_zod14.z.boolean().optional(),
|
|
3307
|
+
duration: import_zod14.z.number().optional(),
|
|
3308
|
+
elementCount: import_zod14.z.number().optional()
|
|
3337
3309
|
});
|
|
3338
|
-
var AdapterLogDataSchema =
|
|
3339
|
-
actionType:
|
|
3340
|
-
selector:
|
|
3341
|
-
coordinates:
|
|
3342
|
-
elementProperties:
|
|
3343
|
-
duration:
|
|
3310
|
+
var AdapterLogDataSchema = import_zod14.z.object({
|
|
3311
|
+
actionType: import_zod14.z.string().optional(),
|
|
3312
|
+
selector: import_zod14.z.string().optional(),
|
|
3313
|
+
coordinates: import_zod14.z.object({ x: import_zod14.z.number(), y: import_zod14.z.number() }).optional(),
|
|
3314
|
+
elementProperties: import_zod14.z.record(import_zod14.z.string(), import_zod14.z.unknown()).optional(),
|
|
3315
|
+
duration: import_zod14.z.number().optional()
|
|
3344
3316
|
});
|
|
3345
|
-
var CacheLogDataSchema =
|
|
3346
|
-
operation:
|
|
3347
|
-
stepHash:
|
|
3348
|
-
screenHash:
|
|
3349
|
-
hit:
|
|
3350
|
-
age:
|
|
3351
|
-
entriesDeleted:
|
|
3317
|
+
var CacheLogDataSchema = import_zod14.z.object({
|
|
3318
|
+
operation: import_zod14.z.enum(["get", "set", "invalidate", "bypass", "batch-purge"]),
|
|
3319
|
+
stepHash: import_zod14.z.string().optional(),
|
|
3320
|
+
screenHash: import_zod14.z.string().optional(),
|
|
3321
|
+
hit: import_zod14.z.boolean().optional(),
|
|
3322
|
+
age: import_zod14.z.number().optional(),
|
|
3323
|
+
entriesDeleted: import_zod14.z.number().optional()
|
|
3352
3324
|
});
|
|
3353
|
-
var PlannerLogDataSchema =
|
|
3354
|
-
model:
|
|
3355
|
-
promptTokens:
|
|
3356
|
-
completionTokens:
|
|
3357
|
-
latencyMs:
|
|
3358
|
-
confidence:
|
|
3359
|
-
actionType:
|
|
3325
|
+
var PlannerLogDataSchema = import_zod14.z.object({
|
|
3326
|
+
model: import_zod14.z.string().optional(),
|
|
3327
|
+
promptTokens: import_zod14.z.number().optional(),
|
|
3328
|
+
completionTokens: import_zod14.z.number().optional(),
|
|
3329
|
+
latencyMs: import_zod14.z.number().optional(),
|
|
3330
|
+
confidence: import_zod14.z.number().optional(),
|
|
3331
|
+
actionType: import_zod14.z.string().optional()
|
|
3360
3332
|
});
|
|
3361
|
-
var HookLogDataSchema =
|
|
3362
|
-
hookName:
|
|
3363
|
-
phase:
|
|
3364
|
-
duration:
|
|
3365
|
-
success:
|
|
3333
|
+
var HookLogDataSchema = import_zod14.z.object({
|
|
3334
|
+
hookName: import_zod14.z.string().optional(),
|
|
3335
|
+
phase: import_zod14.z.enum(["setup", "teardown"]).optional(),
|
|
3336
|
+
duration: import_zod14.z.number().optional(),
|
|
3337
|
+
success: import_zod14.z.boolean().optional()
|
|
3366
3338
|
});
|
|
3367
|
-
var RunnerLogDataSchema =
|
|
3368
|
-
stepIndex:
|
|
3369
|
-
totalSteps:
|
|
3370
|
-
testName:
|
|
3371
|
-
variablesCaptured:
|
|
3339
|
+
var RunnerLogDataSchema = import_zod14.z.object({
|
|
3340
|
+
stepIndex: import_zod14.z.number().optional(),
|
|
3341
|
+
totalSteps: import_zod14.z.number().optional(),
|
|
3342
|
+
testName: import_zod14.z.string().optional(),
|
|
3343
|
+
variablesCaptured: import_zod14.z.number().optional()
|
|
3372
3344
|
});
|
|
3373
3345
|
var LogSourceDataSchemas = {
|
|
3374
3346
|
agent: AgentLogDataSchema,
|
|
@@ -3380,7 +3352,7 @@ var LogSourceDataSchemas = {
|
|
|
3380
3352
|
};
|
|
3381
3353
|
|
|
3382
3354
|
// src/logging/log-manager.ts
|
|
3383
|
-
var
|
|
3355
|
+
var import_node_crypto5 = require("crypto");
|
|
3384
3356
|
|
|
3385
3357
|
// src/logging/cli-formatter.ts
|
|
3386
3358
|
var import_picocolors2 = __toESM(require("picocolors"), 1);
|
|
@@ -3399,12 +3371,60 @@ function formatLogEntry(entry) {
|
|
|
3399
3371
|
return `${time} ${level} ${source} ${entry.message}${dataSuffix}`;
|
|
3400
3372
|
}
|
|
3401
3373
|
|
|
3374
|
+
// src/logging/run-text-output.ts
|
|
3375
|
+
var import_node_crypto4 = require("crypto");
|
|
3376
|
+
var STDOUT_RESERVATIONS = /* @__PURE__ */ Symbol.for("agent-qa.run-text-output.stdout-reservations");
|
|
3377
|
+
function stdoutReservations() {
|
|
3378
|
+
const holder = globalThis;
|
|
3379
|
+
holder[STDOUT_RESERVATIONS] ??= { count: 0 };
|
|
3380
|
+
return holder[STDOUT_RESERVATIONS];
|
|
3381
|
+
}
|
|
3382
|
+
function isGitHubActions(env = process.env) {
|
|
3383
|
+
const value = env.GITHUB_ACTIONS;
|
|
3384
|
+
return value !== void 0 && value !== "" && value.toLowerCase() !== "false" && value !== "0";
|
|
3385
|
+
}
|
|
3386
|
+
function guardRunText(text, env = process.env) {
|
|
3387
|
+
if (!isGitHubActions(env)) return text;
|
|
3388
|
+
const token = (0, import_node_crypto4.randomBytes)(16).toString("hex");
|
|
3389
|
+
const block = text.endsWith("\n") ? text : `${text}
|
|
3390
|
+
`;
|
|
3391
|
+
return `::stop-commands::${token}
|
|
3392
|
+
${block}::${token}::
|
|
3393
|
+
`;
|
|
3394
|
+
}
|
|
3395
|
+
function guardJsonText(serialized, env = process.env) {
|
|
3396
|
+
return isGitHubActions(env) ? serialized.replaceAll("#", "\\u0023") : serialized;
|
|
3397
|
+
}
|
|
3398
|
+
function reserveStdoutForData() {
|
|
3399
|
+
const reservations = stdoutReservations();
|
|
3400
|
+
reservations.count += 1;
|
|
3401
|
+
let released = false;
|
|
3402
|
+
return () => {
|
|
3403
|
+
if (released) return;
|
|
3404
|
+
released = true;
|
|
3405
|
+
reservations.count -= 1;
|
|
3406
|
+
};
|
|
3407
|
+
}
|
|
3408
|
+
function printRunText(text, print = (line) => console.log(line), options = {}) {
|
|
3409
|
+
const env = options.env ?? process.env;
|
|
3410
|
+
if (!isGitHubActions(env)) {
|
|
3411
|
+
print(text);
|
|
3412
|
+
return;
|
|
3413
|
+
}
|
|
3414
|
+
const block = guardRunText(text, env);
|
|
3415
|
+
if (options.stdoutIsData ?? stdoutReservations().count > 0) {
|
|
3416
|
+
process.stderr.write(block);
|
|
3417
|
+
return;
|
|
3418
|
+
}
|
|
3419
|
+
process.stdout.write(block);
|
|
3420
|
+
}
|
|
3421
|
+
|
|
3402
3422
|
// src/logging/log-manager.ts
|
|
3403
3423
|
var import_agent_qa_runner5 = require("@vostride/agent-qa-runner");
|
|
3404
3424
|
var MemberLogScopeUnavailableError = class extends Error {
|
|
3405
3425
|
constructor() {
|
|
3406
3426
|
super(
|
|
3407
|
-
"LogManager.runInMemberScope requires a memberScope store. Construct the manager with `memberScope: new AsyncLocalStorage()` \u2014 without it, concurrent members would silently share one run id
|
|
3427
|
+
"LogManager.runInMemberScope requires a memberScope store. Construct the manager with `memberScope: new AsyncLocalStorage()` \u2014 without it, concurrent members would silently share one run id."
|
|
3408
3428
|
);
|
|
3409
3429
|
this.name = "MemberLogScopeUnavailableError";
|
|
3410
3430
|
}
|
|
@@ -3419,6 +3439,7 @@ var LogManager = class {
|
|
|
3419
3439
|
ndjson;
|
|
3420
3440
|
redactor;
|
|
3421
3441
|
memberScope;
|
|
3442
|
+
env;
|
|
3422
3443
|
constructor(opts) {
|
|
3423
3444
|
this.runId = opts.runId ?? "";
|
|
3424
3445
|
this.runIdSet = !!opts.runId;
|
|
@@ -3427,21 +3448,12 @@ var LogManager = class {
|
|
|
3427
3448
|
this.ndjson = opts.ndjson ?? false;
|
|
3428
3449
|
this.redactor = opts.redactor;
|
|
3429
3450
|
this.memberScope = opts.memberScope;
|
|
3451
|
+
this.env = opts.env;
|
|
3430
3452
|
}
|
|
3431
3453
|
setRedactor(redactor) {
|
|
3432
3454
|
this.redactor = redactor;
|
|
3433
3455
|
}
|
|
3434
|
-
|
|
3435
|
-
* Run `fn` with its own run-id binding, so concurrent members cannot overwrite each other.
|
|
3436
|
-
*
|
|
3437
|
-
* ONE `LogManager` SERVES EVERY MEMBER of a run — `commands/run.ts` builds it once, outside the
|
|
3438
|
-
* per-member closure — and since 402-02/402-03 the direct path runs members concurrently. Without
|
|
3439
|
-
* a scope, `setRunId` is a single mutable field plus a whole-buffer backfill, so the LAST member
|
|
3440
|
-
* to mint an id claims every line in the buffer, its siblings' included (measured, not inferred).
|
|
3441
|
-
*
|
|
3442
|
-
* Outside a scope nothing changes: `setRunId` keeps its original whole-buffer behaviour, which is
|
|
3443
|
-
* what the sequential path and the run-level `onRunCreated` stamp rely on.
|
|
3444
|
-
*/
|
|
3456
|
+
// One manager serves every concurrent member; outside a scope `setRunId` backfills the buffer.
|
|
3445
3457
|
runInMemberScope(fn) {
|
|
3446
3458
|
if (!this.memberScope) throw new MemberLogScopeUnavailableError();
|
|
3447
3459
|
return this.memberScope.run({ runId: this.runId, pending: [] }, fn);
|
|
@@ -3449,7 +3461,7 @@ var LogManager = class {
|
|
|
3449
3461
|
log(level, source, message, data) {
|
|
3450
3462
|
const scope = this.memberScope?.getStore();
|
|
3451
3463
|
const entry = {
|
|
3452
|
-
id: (0,
|
|
3464
|
+
id: (0, import_node_crypto5.randomUUID)(),
|
|
3453
3465
|
stepId: this.currentStepId,
|
|
3454
3466
|
runId: scope ? scope.runId : this.runId,
|
|
3455
3467
|
level,
|
|
@@ -3461,7 +3473,14 @@ var LogManager = class {
|
|
|
3461
3473
|
this.buffer.push(entry);
|
|
3462
3474
|
if (scope && !entry.runId) scope.pending.push(entry);
|
|
3463
3475
|
if (this.shouldDisplay(level)) {
|
|
3464
|
-
|
|
3476
|
+
printRunText(
|
|
3477
|
+
formatLogEntry(entry),
|
|
3478
|
+
(line) => {
|
|
3479
|
+
process.stderr.write(`${line}
|
|
3480
|
+
`);
|
|
3481
|
+
},
|
|
3482
|
+
{ env: this.env }
|
|
3483
|
+
);
|
|
3465
3484
|
}
|
|
3466
3485
|
if (this.ndjson) {
|
|
3467
3486
|
this.emitNdjson(entry);
|
|
@@ -3482,7 +3501,7 @@ var LogManager = class {
|
|
|
3482
3501
|
*/
|
|
3483
3502
|
emitNdjson(entry) {
|
|
3484
3503
|
process.stdout.write(
|
|
3485
|
-
`AGENT_QA_EVENT:${JSON.stringify({ type: "log", ...entry })}
|
|
3504
|
+
`AGENT_QA_EVENT:${guardJsonText(JSON.stringify({ type: "log", ...entry }), this.env)}
|
|
3486
3505
|
`
|
|
3487
3506
|
);
|
|
3488
3507
|
}
|
|
@@ -3530,264 +3549,6 @@ var LogManager = class {
|
|
|
3530
3549
|
}
|
|
3531
3550
|
};
|
|
3532
3551
|
|
|
3533
|
-
// src/farm/credentials.ts
|
|
3534
|
-
var ENV_MAP = {
|
|
3535
|
-
browserstack: {
|
|
3536
|
-
username: "BROWSERSTACK_USERNAME",
|
|
3537
|
-
accessKey: "BROWSERSTACK_ACCESS_KEY"
|
|
3538
|
-
}
|
|
3539
|
-
};
|
|
3540
|
-
var DISPLAY_NAMES = {
|
|
3541
|
-
browserstack: "BrowserStack"
|
|
3542
|
-
};
|
|
3543
|
-
function resolveFarmCredentials(provider, providerConfig) {
|
|
3544
|
-
const envKeys = ENV_MAP[provider];
|
|
3545
|
-
if (!envKeys) {
|
|
3546
|
-
throw new Error(`Unknown farm provider: ${provider}`);
|
|
3547
|
-
}
|
|
3548
|
-
const envUsername = process.env[envKeys.username];
|
|
3549
|
-
const envAccessKey = process.env[envKeys.accessKey];
|
|
3550
|
-
if (envUsername && envAccessKey) {
|
|
3551
|
-
return { username: envUsername, accessKey: envAccessKey };
|
|
3552
|
-
}
|
|
3553
|
-
if (providerConfig?.username && providerConfig?.accessKey) {
|
|
3554
|
-
return { username: providerConfig.username, accessKey: providerConfig.accessKey };
|
|
3555
|
-
}
|
|
3556
|
-
const displayName = DISPLAY_NAMES[provider] ?? provider;
|
|
3557
|
-
throw new Error(
|
|
3558
|
-
`${displayName} credentials not found. Set ${envKeys.username} and ${envKeys.accessKey} env vars, or add provider credentials to agent-qa.local.yaml.`
|
|
3559
|
-
);
|
|
3560
|
-
}
|
|
3561
|
-
|
|
3562
|
-
// src/farm/capability-mapper.ts
|
|
3563
|
-
var import_node_module2 = require("module");
|
|
3564
|
-
var import_meta2 = {};
|
|
3565
|
-
var BROWSER_MAP = {
|
|
3566
|
-
chromium: "playwright-chromium",
|
|
3567
|
-
firefox: "playwright-firefox",
|
|
3568
|
-
webkit: "playwright-webkit"
|
|
3569
|
-
};
|
|
3570
|
-
function detectPlaywrightVersion() {
|
|
3571
|
-
try {
|
|
3572
|
-
const require4 = (0, import_node_module2.createRequire)(typeof __filename === "string" ? __filename : import_meta2.url);
|
|
3573
|
-
const pkg = require4("playwright-core/package.json");
|
|
3574
|
-
return pkg.version;
|
|
3575
|
-
} catch {
|
|
3576
|
-
return "1.59.1";
|
|
3577
|
-
}
|
|
3578
|
-
}
|
|
3579
|
-
function mapWebCapabilities(browser, credentials, opts) {
|
|
3580
|
-
if (!browser.viewport) {
|
|
3581
|
-
throw new Error("Farm mode requires browser.viewport to be set (e.g. { width: 1280, height: 720 })");
|
|
3582
|
-
}
|
|
3583
|
-
const bstackBrowser = BROWSER_MAP[browser.name];
|
|
3584
|
-
if (!bstackBrowser) {
|
|
3585
|
-
throw new Error(`Unsupported browser for BrowserStack: "${browser.name}". Supported: ${Object.keys(BROWSER_MAP).join(", ")}`);
|
|
3586
|
-
}
|
|
3587
|
-
return {
|
|
3588
|
-
browser: bstackBrowser,
|
|
3589
|
-
os: "os x",
|
|
3590
|
-
os_version: "sonoma",
|
|
3591
|
-
resolution: "1920x1080",
|
|
3592
|
-
"browserstack.username": credentials.username,
|
|
3593
|
-
"browserstack.accessKey": credentials.accessKey,
|
|
3594
|
-
"client.playwrightVersion": opts?.playwrightVersion ?? detectPlaywrightVersion(),
|
|
3595
|
-
project: "agent-qa",
|
|
3596
|
-
build: "agent-qa-" + Date.now(),
|
|
3597
|
-
name: opts?.testName
|
|
3598
|
-
};
|
|
3599
|
-
}
|
|
3600
|
-
function mapMobileCapabilities(match, platform, credentials, opts) {
|
|
3601
|
-
if (!opts?.testTimeout) {
|
|
3602
|
-
throw new Error("Farm mode requires timeout.test to be set in config");
|
|
3603
|
-
}
|
|
3604
|
-
return {
|
|
3605
|
-
"bstack:options": {
|
|
3606
|
-
userName: credentials.username,
|
|
3607
|
-
accessKey: credentials.accessKey,
|
|
3608
|
-
deviceName: match.deviceName,
|
|
3609
|
-
osVersion: match.platformVersion,
|
|
3610
|
-
projectName: "agent-qa",
|
|
3611
|
-
buildName: "agent-qa-" + Date.now(),
|
|
3612
|
-
sessionName: opts.testName,
|
|
3613
|
-
idleTimeout: Math.ceil(opts.testTimeout / 1e3)
|
|
3614
|
-
}
|
|
3615
|
-
};
|
|
3616
|
-
}
|
|
3617
|
-
|
|
3618
|
-
// src/farm/browserstack.ts
|
|
3619
|
-
var import_node_fs5 = require("fs");
|
|
3620
|
-
var import_node_path10 = require("path");
|
|
3621
|
-
function isAbsoluteAppValue(value) {
|
|
3622
|
-
return value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
3623
|
-
}
|
|
3624
|
-
function isExplicitRelativePath(value) {
|
|
3625
|
-
return value.startsWith("./") || value.startsWith("../");
|
|
3626
|
-
}
|
|
3627
|
-
async function resolveBrowserStackApp(config) {
|
|
3628
|
-
if (!config.app) return void 0;
|
|
3629
|
-
if (isAbsoluteAppValue(config.app)) {
|
|
3630
|
-
throw new MobileSetupError({
|
|
3631
|
-
category: "app-install",
|
|
3632
|
-
message: `BrowserStack app value must be a BrowserStack reference or relative upload path: ${config.app}`,
|
|
3633
|
-
platform: config.platform,
|
|
3634
|
-
appId: config.app,
|
|
3635
|
-
sourceTrace: config.appSourceTrace
|
|
3636
|
-
});
|
|
3637
|
-
}
|
|
3638
|
-
const appBaseDir = config.appBaseDir ?? process.cwd();
|
|
3639
|
-
const resolvedPath = (0, import_node_path10.resolve)(appBaseDir, config.app);
|
|
3640
|
-
const mustExist = isExplicitRelativePath(config.app);
|
|
3641
|
-
if (!(0, import_node_fs5.existsSync)(resolvedPath)) {
|
|
3642
|
-
if (mustExist) {
|
|
3643
|
-
throw new MobileSetupError({
|
|
3644
|
-
category: "app-install",
|
|
3645
|
-
message: `Configured BrowserStack app upload path not found: ${resolvedPath}`,
|
|
3646
|
-
platform: config.platform,
|
|
3647
|
-
appId: config.app,
|
|
3648
|
-
sourceTrace: config.appSourceTrace
|
|
3649
|
-
});
|
|
3650
|
-
}
|
|
3651
|
-
return config.app;
|
|
3652
|
-
}
|
|
3653
|
-
const auth = Buffer.from(`${config.credentials.username}:${config.credentials.accessKey}`).toString("base64");
|
|
3654
|
-
const form = new FormData();
|
|
3655
|
-
form.append("file", new Blob([(0, import_node_fs5.readFileSync)(resolvedPath)]), (0, import_node_path10.basename)(resolvedPath));
|
|
3656
|
-
let res;
|
|
3657
|
-
try {
|
|
3658
|
-
res = await fetch("https://api-cloud.browserstack.com/app-automate/upload", {
|
|
3659
|
-
method: "POST",
|
|
3660
|
-
headers: { Authorization: `Basic ${auth}` },
|
|
3661
|
-
body: form
|
|
3662
|
-
});
|
|
3663
|
-
} catch (err) {
|
|
3664
|
-
throw new MobileSetupError({
|
|
3665
|
-
category: "app-install",
|
|
3666
|
-
message: `Failed to upload BrowserStack app: ${err instanceof Error ? err.message : String(err)}`,
|
|
3667
|
-
platform: config.platform,
|
|
3668
|
-
appId: config.app,
|
|
3669
|
-
sourceTrace: config.appSourceTrace,
|
|
3670
|
-
cause: err
|
|
3671
|
-
});
|
|
3672
|
-
}
|
|
3673
|
-
if (!res.ok) {
|
|
3674
|
-
throw new MobileSetupError({
|
|
3675
|
-
category: "app-install",
|
|
3676
|
-
message: `Failed to upload BrowserStack app: HTTP ${res.status}`,
|
|
3677
|
-
platform: config.platform,
|
|
3678
|
-
appId: config.app,
|
|
3679
|
-
sourceTrace: config.appSourceTrace
|
|
3680
|
-
});
|
|
3681
|
-
}
|
|
3682
|
-
const body = await res.json();
|
|
3683
|
-
if (!body.app_url) {
|
|
3684
|
-
throw new MobileSetupError({
|
|
3685
|
-
category: "app-install",
|
|
3686
|
-
message: "Failed to upload BrowserStack app: response missing app_url",
|
|
3687
|
-
platform: config.platform,
|
|
3688
|
-
appId: config.app,
|
|
3689
|
-
sourceTrace: config.appSourceTrace
|
|
3690
|
-
});
|
|
3691
|
-
}
|
|
3692
|
-
return body.app_url;
|
|
3693
|
-
}
|
|
3694
|
-
var browserstackProvider = {
|
|
3695
|
-
name: "BrowserStack",
|
|
3696
|
-
slug: "browserstack",
|
|
3697
|
-
async resolveWebEndpoint(config) {
|
|
3698
|
-
const caps = mapWebCapabilities(config.browser, config.credentials, {
|
|
3699
|
-
testName: config.testName,
|
|
3700
|
-
playwrightVersion: config.playwrightVersion
|
|
3701
|
-
});
|
|
3702
|
-
const wsEndpoint = `wss://cdp.browserstack.com/playwright?caps=${encodeURIComponent(JSON.stringify(caps))}`;
|
|
3703
|
-
return { wsEndpoint, capabilities: caps };
|
|
3704
|
-
},
|
|
3705
|
-
async resolveMobileCapabilities(config) {
|
|
3706
|
-
const bstackCaps = mapMobileCapabilities(config.match, config.platform, config.credentials, {
|
|
3707
|
-
testName: config.testName,
|
|
3708
|
-
testTimeout: config.testTimeout
|
|
3709
|
-
});
|
|
3710
|
-
const automationName = config.match.automationName ?? (config.platform === "android" ? "UiAutomator2" : "XCUITest");
|
|
3711
|
-
const platformName = config.platform === "android" ? "Android" : "iOS";
|
|
3712
|
-
const capabilities = {
|
|
3713
|
-
platformName,
|
|
3714
|
-
"appium:automationName": automationName
|
|
3715
|
-
};
|
|
3716
|
-
for (const [key, value] of Object.entries(config.match)) {
|
|
3717
|
-
if (key !== "automationName" && key !== "noReset") {
|
|
3718
|
-
capabilities[`appium:${key}`] = value;
|
|
3719
|
-
}
|
|
3720
|
-
}
|
|
3721
|
-
Object.assign(capabilities, bstackCaps);
|
|
3722
|
-
const browserName = typeof config.match.browserName === "string" ? config.match.browserName.trim() : "";
|
|
3723
|
-
const appStateNoReset = config.appState === "preserve" ? true : config.appState === "reset" ? false : void 0;
|
|
3724
|
-
if (!browserName && appStateNoReset !== void 0) {
|
|
3725
|
-
capabilities["appium:noReset"] = appStateNoReset;
|
|
3726
|
-
}
|
|
3727
|
-
const resolvedAppValue = await resolveBrowserStackApp(config);
|
|
3728
|
-
if (resolvedAppValue) {
|
|
3729
|
-
capabilities["appium:app"] = resolvedAppValue;
|
|
3730
|
-
}
|
|
3731
|
-
return {
|
|
3732
|
-
hostname: "hub.browserstack.com",
|
|
3733
|
-
port: 443,
|
|
3734
|
-
path: "/wd/hub",
|
|
3735
|
-
capabilities
|
|
3736
|
-
};
|
|
3737
|
-
},
|
|
3738
|
-
resolveCredentials(providerConfig) {
|
|
3739
|
-
return resolveFarmCredentials("browserstack", providerConfig);
|
|
3740
|
-
},
|
|
3741
|
-
async validateDevice(deviceName, platform) {
|
|
3742
|
-
let credentials;
|
|
3743
|
-
try {
|
|
3744
|
-
credentials = resolveFarmCredentials("browserstack");
|
|
3745
|
-
} catch {
|
|
3746
|
-
return { valid: false, suggestions: ["Could not validate \u2014 credentials not configured"] };
|
|
3747
|
-
}
|
|
3748
|
-
const auth = Buffer.from(`${credentials.username}:${credentials.accessKey}`).toString("base64");
|
|
3749
|
-
try {
|
|
3750
|
-
const res = await fetch("https://api-cloud.browserstack.com/app-automate/devices.json", {
|
|
3751
|
-
headers: { Authorization: `Basic ${auth}` }
|
|
3752
|
-
});
|
|
3753
|
-
if (!res.ok) {
|
|
3754
|
-
return { valid: false, suggestions: ["Could not validate \u2014 API returned " + res.status] };
|
|
3755
|
-
}
|
|
3756
|
-
const devices = await res.json();
|
|
3757
|
-
const targetOs = platform === "android" ? "android" : "ios";
|
|
3758
|
-
const match = devices.find(
|
|
3759
|
-
(d) => d.device.toLowerCase() === deviceName.toLowerCase() && d.os.toLowerCase() === targetOs
|
|
3760
|
-
);
|
|
3761
|
-
if (match) return { valid: true };
|
|
3762
|
-
const similar = devices.filter((d) => d.os.toLowerCase() === targetOs).filter((d) => d.device.toLowerCase().includes(deviceName.toLowerCase().split(" ")[0])).map((d) => d.device).slice(0, 5);
|
|
3763
|
-
return { valid: false, suggestions: similar.length > 0 ? similar : void 0 };
|
|
3764
|
-
} catch {
|
|
3765
|
-
return { valid: false, suggestions: ["Could not validate \u2014 network error"] };
|
|
3766
|
-
}
|
|
3767
|
-
},
|
|
3768
|
-
getSessionUrl(sessionId, type) {
|
|
3769
|
-
if (type === "web") {
|
|
3770
|
-
return `https://automate.browserstack.com/sessions/${sessionId}`;
|
|
3771
|
-
}
|
|
3772
|
-
return `https://app-automate.browserstack.com/sessions/${sessionId}`;
|
|
3773
|
-
}
|
|
3774
|
-
};
|
|
3775
|
-
|
|
3776
|
-
// src/farm/registry.ts
|
|
3777
|
-
var providers = /* @__PURE__ */ new Map();
|
|
3778
|
-
function registerProvider(provider) {
|
|
3779
|
-
providers.set(provider.slug, provider);
|
|
3780
|
-
}
|
|
3781
|
-
function getProvider(slug) {
|
|
3782
|
-
return providers.get(slug);
|
|
3783
|
-
}
|
|
3784
|
-
function listProviders() {
|
|
3785
|
-
return Array.from(providers.values());
|
|
3786
|
-
}
|
|
3787
|
-
function registerAllProviders() {
|
|
3788
|
-
registerProvider(browserstackProvider);
|
|
3789
|
-
}
|
|
3790
|
-
|
|
3791
3552
|
// src/suite/member.ts
|
|
3792
3553
|
function isPlainObject2(val) {
|
|
3793
3554
|
return typeof val === "object" && val !== null && !Array.isArray(val);
|
|
@@ -3823,14 +3584,14 @@ function patchTestForSuite(test, suite, _index) {
|
|
|
3823
3584
|
|
|
3824
3585
|
// src/validation/validate.ts
|
|
3825
3586
|
var import_promises8 = require("fs/promises");
|
|
3826
|
-
var
|
|
3827
|
-
var
|
|
3587
|
+
var import_node_fs6 = require("fs");
|
|
3588
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
3828
3589
|
var import_yaml5 = require("yaml");
|
|
3829
3590
|
var import_glob2 = require("glob");
|
|
3830
3591
|
|
|
3831
3592
|
// src/hooks/parser.ts
|
|
3832
3593
|
var import_promises7 = require("fs/promises");
|
|
3833
|
-
var
|
|
3594
|
+
var import_node_path10 = require("path");
|
|
3834
3595
|
var import_yaml4 = require("yaml");
|
|
3835
3596
|
async function parseHooksFile(filePath) {
|
|
3836
3597
|
let raw;
|
|
@@ -3852,31 +3613,31 @@ async function parseHooksFile(filePath) {
|
|
|
3852
3613
|
);
|
|
3853
3614
|
return { hooks: [], errors };
|
|
3854
3615
|
}
|
|
3855
|
-
const dir = (0,
|
|
3616
|
+
const dir = (0, import_node_path10.dirname)(filePath);
|
|
3856
3617
|
const hooks = result.data.hooks.map((h) => ({
|
|
3857
3618
|
...h,
|
|
3858
|
-
file: (0,
|
|
3859
|
-
deps: h.deps.map((d) => (0,
|
|
3860
|
-
packageFile: h.packageFile ? (0,
|
|
3619
|
+
file: (0, import_node_path10.resolve)(dir, h.file),
|
|
3620
|
+
deps: h.deps.map((d) => (0, import_node_path10.resolve)(dir, d)),
|
|
3621
|
+
packageFile: h.packageFile ? (0, import_node_path10.resolve)(dir, h.packageFile) : void 0
|
|
3861
3622
|
}));
|
|
3862
3623
|
return { hooks, errors: [] };
|
|
3863
3624
|
}
|
|
3864
3625
|
|
|
3865
3626
|
// src/utils/path-validation.ts
|
|
3866
|
-
var
|
|
3627
|
+
var import_node_path11 = __toESM(require("path"), 1);
|
|
3867
3628
|
function isPathInsideDir(childPath, parentDir) {
|
|
3868
3629
|
if (!childPath) return false;
|
|
3869
|
-
const resolved =
|
|
3870
|
-
const relative2 =
|
|
3871
|
-
return !!relative2 && !relative2.startsWith("..") && !
|
|
3630
|
+
const resolved = import_node_path11.default.resolve(parentDir, childPath);
|
|
3631
|
+
const relative2 = import_node_path11.default.relative(parentDir, resolved);
|
|
3632
|
+
return !!relative2 && !relative2.startsWith("..") && !import_node_path11.default.isAbsolute(relative2);
|
|
3872
3633
|
}
|
|
3873
3634
|
|
|
3874
3635
|
// src/workspace/workspace-paths.ts
|
|
3875
|
-
var
|
|
3876
|
-
var
|
|
3636
|
+
var import_node_fs5 = require("fs");
|
|
3637
|
+
var import_node_path12 = __toESM(require("path"), 1);
|
|
3877
3638
|
var import_glob = require("glob");
|
|
3878
3639
|
function toForwardSlash(value) {
|
|
3879
|
-
return value.split(
|
|
3640
|
+
return value.split(import_node_path12.default.sep).join("/");
|
|
3880
3641
|
}
|
|
3881
3642
|
function trimLeadingCurrentDir(value) {
|
|
3882
3643
|
return toForwardSlash(value).replace(/^\.\//, "");
|
|
@@ -3895,22 +3656,22 @@ function assertNonEmptyString(value, key) {
|
|
|
3895
3656
|
}
|
|
3896
3657
|
function resolveWorkspaceScalar(configDir, key, configuredPath) {
|
|
3897
3658
|
const value = assertNonEmptyString(configuredPath, key);
|
|
3898
|
-
const absolutePath =
|
|
3659
|
+
const absolutePath = import_node_path12.default.isAbsolute(value) ? import_node_path12.default.resolve(value) : import_node_path12.default.resolve(configDir, value);
|
|
3899
3660
|
return {
|
|
3900
3661
|
configuredPath: value,
|
|
3901
3662
|
absolutePath,
|
|
3902
|
-
workspaceRelativePath: trimLeadingCurrentDir(
|
|
3663
|
+
workspaceRelativePath: trimLeadingCurrentDir(import_node_path12.default.relative(configDir, absolutePath))
|
|
3903
3664
|
};
|
|
3904
3665
|
}
|
|
3905
3666
|
function assertInsideWorkspace(configDir, absolutePath, filePath) {
|
|
3906
|
-
const relativePath =
|
|
3907
|
-
if (!relativePath || relativePath.startsWith("..") ||
|
|
3667
|
+
const relativePath = import_node_path12.default.relative(configDir, absolutePath);
|
|
3668
|
+
if (!relativePath || relativePath.startsWith("..") || import_node_path12.default.isAbsolute(relativePath)) {
|
|
3908
3669
|
throw new Error(`Workspace file path escapes config directory: ${filePath}`);
|
|
3909
3670
|
}
|
|
3910
3671
|
return trimLeadingCurrentDir(relativePath);
|
|
3911
3672
|
}
|
|
3912
3673
|
function workspaceRelativeIdentityPath(configDir, filePath) {
|
|
3913
|
-
const absolutePath =
|
|
3674
|
+
const absolutePath = import_node_path12.default.isAbsolute(filePath) ? import_node_path12.default.resolve(filePath) : import_node_path12.default.resolve(configDir, filePath);
|
|
3914
3675
|
return assertInsideWorkspace(configDir, absolutePath, filePath);
|
|
3915
3676
|
}
|
|
3916
3677
|
function escapeRegexChar(char) {
|
|
@@ -3965,8 +3726,8 @@ function isIgnored(workspace, workspaceRelativePath) {
|
|
|
3965
3726
|
return workspace.testPathIgnore.length > 0 && matchesAnyPattern(workspaceRelativePath, workspace.testPathIgnore);
|
|
3966
3727
|
}
|
|
3967
3728
|
function resolveWorkspacePaths(input) {
|
|
3968
|
-
const configPath =
|
|
3969
|
-
const configDir =
|
|
3729
|
+
const configPath = import_node_path12.default.resolve(input.configPath);
|
|
3730
|
+
const configDir = import_node_path12.default.dirname(configPath);
|
|
3970
3731
|
const workspace = input.config.workspace;
|
|
3971
3732
|
if (!workspace) {
|
|
3972
3733
|
throw new Error("Missing required workspace config key: workspace");
|
|
@@ -3987,7 +3748,7 @@ function resolveWorkspacePaths(input) {
|
|
|
3987
3748
|
["workspace.envFile", resolved.envFile],
|
|
3988
3749
|
["workspace.secretsFile", resolved.secretsFile]
|
|
3989
3750
|
]) {
|
|
3990
|
-
if (!(0,
|
|
3751
|
+
if (!(0, import_node_fs5.existsSync)(file.absolutePath)) {
|
|
3991
3752
|
throw new Error(`Configured workspace file not found: ${key} -> ${file.absolutePath}`);
|
|
3992
3753
|
}
|
|
3993
3754
|
}
|
|
@@ -4002,14 +3763,14 @@ async function discoverWorkspaceFiles(input) {
|
|
|
4002
3763
|
absolute: true,
|
|
4003
3764
|
nodir: true
|
|
4004
3765
|
});
|
|
4005
|
-
return matches.map((match) =>
|
|
3766
|
+
return matches.map((match) => import_node_path12.default.resolve(match)).map((absolutePath) => ({
|
|
4006
3767
|
kind: input.kind,
|
|
4007
3768
|
absolutePath,
|
|
4008
|
-
workspaceRelativePath: trimLeadingCurrentDir(
|
|
3769
|
+
workspaceRelativePath: trimLeadingCurrentDir(import_node_path12.default.relative(input.workspace.configDir, absolutePath))
|
|
4009
3770
|
})).sort((a, b) => a.workspaceRelativePath.localeCompare(b.workspaceRelativePath));
|
|
4010
3771
|
}
|
|
4011
3772
|
async function resolveWorkspaceFileTarget(input) {
|
|
4012
|
-
const absolutePath =
|
|
3773
|
+
const absolutePath = import_node_path12.default.isAbsolute(input.filePath) ? import_node_path12.default.resolve(input.filePath) : import_node_path12.default.resolve(input.workspace.configDir, input.filePath);
|
|
4013
3774
|
const workspaceRelativePath = assertInsideWorkspace(input.workspace.configDir, absolutePath, input.filePath);
|
|
4014
3775
|
if (!isWorkspacePathMatch({
|
|
4015
3776
|
workspace: input.workspace,
|
|
@@ -4018,7 +3779,7 @@ async function resolveWorkspaceFileTarget(input) {
|
|
|
4018
3779
|
})) {
|
|
4019
3780
|
throw new Error(`Workspace ${input.kind} file is not matched by configured workspace patterns: ${workspaceRelativePath}`);
|
|
4020
3781
|
}
|
|
4021
|
-
if (input.requireExisting && !(0,
|
|
3782
|
+
if (input.requireExisting && !(0, import_node_fs5.existsSync)(absolutePath)) {
|
|
4022
3783
|
throw new Error(`Workspace ${input.kind} file not found: ${workspaceRelativePath}`);
|
|
4023
3784
|
}
|
|
4024
3785
|
return {
|
|
@@ -4139,8 +3900,8 @@ function validateSuiteFile(filePath, content, basedir) {
|
|
|
4139
3900
|
});
|
|
4140
3901
|
continue;
|
|
4141
3902
|
}
|
|
4142
|
-
const resolved =
|
|
4143
|
-
if (!(0,
|
|
3903
|
+
const resolved = import_node_path13.default.resolve(basedir, testPath);
|
|
3904
|
+
if (!(0, import_node_fs6.existsSync)(resolved)) {
|
|
4144
3905
|
let line = 1;
|
|
4145
3906
|
let column = 1;
|
|
4146
3907
|
const seqNode = testsNode;
|
|
@@ -4161,13 +3922,13 @@ function validateSuiteFile(filePath, content, basedir) {
|
|
|
4161
3922
|
return diagnostics;
|
|
4162
3923
|
}
|
|
4163
3924
|
function validateFilename(filePath) {
|
|
4164
|
-
const
|
|
4165
|
-
if (!VALID_FILENAME_RE.test(
|
|
3925
|
+
const basename2 = import_node_path13.default.basename(filePath);
|
|
3926
|
+
if (!VALID_FILENAME_RE.test(basename2)) {
|
|
4166
3927
|
return [{
|
|
4167
3928
|
file: filePath,
|
|
4168
3929
|
line: 1,
|
|
4169
3930
|
column: 1,
|
|
4170
|
-
message: `Invalid filename "${
|
|
3931
|
+
message: `Invalid filename "${basename2}" \u2014 expected pattern: [a-zA-Z0-9._-]+.(suite.)?(yaml|yml)`,
|
|
4171
3932
|
severity: "warning"
|
|
4172
3933
|
}];
|
|
4173
3934
|
}
|
|
@@ -4175,7 +3936,7 @@ function validateFilename(filePath) {
|
|
|
4175
3936
|
}
|
|
4176
3937
|
async function validateHooksFile(hooksFilePath, options = {}) {
|
|
4177
3938
|
const diagnostics = [];
|
|
4178
|
-
if (!(0,
|
|
3939
|
+
if (!(0, import_node_fs6.existsSync)(hooksFilePath)) {
|
|
4179
3940
|
if (options.required) {
|
|
4180
3941
|
return [buildDiagnostic(hooksFilePath, `Configured hooks file not found: ${hooksFilePath}`)];
|
|
4181
3942
|
}
|
|
@@ -4195,7 +3956,7 @@ async function validateHooksFile(hooksFilePath, options = {}) {
|
|
|
4195
3956
|
return diagnostics;
|
|
4196
3957
|
}
|
|
4197
3958
|
for (const hook of hooks) {
|
|
4198
|
-
if (!(0,
|
|
3959
|
+
if (!(0, import_node_fs6.existsSync)(hook.file)) {
|
|
4199
3960
|
diagnostics.push({
|
|
4200
3961
|
file: hooksFilePath,
|
|
4201
3962
|
line: 1,
|
|
@@ -4205,7 +3966,7 @@ async function validateHooksFile(hooksFilePath, options = {}) {
|
|
|
4205
3966
|
});
|
|
4206
3967
|
}
|
|
4207
3968
|
for (const dep of hook.deps) {
|
|
4208
|
-
if (!(0,
|
|
3969
|
+
if (!(0, import_node_fs6.existsSync)(dep)) {
|
|
4209
3970
|
diagnostics.push({
|
|
4210
3971
|
file: hooksFilePath,
|
|
4211
3972
|
line: 1,
|
|
@@ -4215,7 +3976,7 @@ async function validateHooksFile(hooksFilePath, options = {}) {
|
|
|
4215
3976
|
});
|
|
4216
3977
|
}
|
|
4217
3978
|
}
|
|
4218
|
-
if (hook.packageFile && !(0,
|
|
3979
|
+
if (hook.packageFile && !(0, import_node_fs6.existsSync)(hook.packageFile)) {
|
|
4219
3980
|
diagnostics.push({
|
|
4220
3981
|
file: hooksFilePath,
|
|
4221
3982
|
line: 1,
|
|
@@ -4329,7 +4090,7 @@ function validateRequiredWorkspaceFiles(workspace) {
|
|
|
4329
4090
|
["workspace.envFile", workspace.envFile],
|
|
4330
4091
|
["workspace.secretsFile", workspace.secretsFile]
|
|
4331
4092
|
]) {
|
|
4332
|
-
if (!(0,
|
|
4093
|
+
if (!(0, import_node_fs6.existsSync)(file.absolutePath)) {
|
|
4333
4094
|
diagnostics.push(buildDiagnostic(file.absolutePath, `Configured workspace file not found: ${key} -> ${file.absolutePath}`));
|
|
4334
4095
|
}
|
|
4335
4096
|
}
|
|
@@ -4347,7 +4108,7 @@ async function validateProject(options = {}) {
|
|
|
4347
4108
|
}
|
|
4348
4109
|
filePaths = resolvedFiles;
|
|
4349
4110
|
} else if (options.files && options.files.length > 0) {
|
|
4350
|
-
filePaths = options.files.map((f) =>
|
|
4111
|
+
filePaths = options.files.map((f) => import_node_path13.default.resolve(f));
|
|
4351
4112
|
} else if (options.workspace) {
|
|
4352
4113
|
const [testFiles, suiteFiles] = await Promise.all([
|
|
4353
4114
|
discoverWorkspaceFiles({ workspace: options.workspace, kind: "test" }),
|
|
@@ -4390,11 +4151,11 @@ async function validateProject(options = {}) {
|
|
|
4390
4151
|
if (d.severity === "error") result.errorCount++;
|
|
4391
4152
|
else result.warningCount++;
|
|
4392
4153
|
}
|
|
4393
|
-
if (hooksFilePath && hooksDiags.length === 0 && (0,
|
|
4154
|
+
if (hooksFilePath && hooksDiags.length === 0 && (0, import_node_fs6.existsSync)(hooksFilePath)) {
|
|
4394
4155
|
const { hooks } = await parseHooksFile(hooksFilePath);
|
|
4395
4156
|
const hookIds = new Set(hooks.map((h) => h.id));
|
|
4396
4157
|
for (const filePath of filePaths) {
|
|
4397
|
-
const workspaceRelativePath = options.workspace ?
|
|
4158
|
+
const workspaceRelativePath = options.workspace ? import_node_path13.default.relative(options.workspace.configDir, filePath).split(import_node_path13.default.sep).join("/") : filePath;
|
|
4398
4159
|
const isWorkspaceFile = options.workspace ? isWorkspacePathMatch({ workspace: options.workspace, kind: isSuiteFile(filePath) ? "suite" : "test", workspaceRelativePath }) : true;
|
|
4399
4160
|
if (!isWorkspaceFile) continue;
|
|
4400
4161
|
try {
|
|
@@ -4519,8 +4280,6 @@ var ATTR_RUNNER = "agent-qa.runner";
|
|
|
4519
4280
|
var ATTR_GATE_RUN = "agent-qa.gate-run";
|
|
4520
4281
|
var RUN_ATTRIBUTE_KEY_MAX_LENGTH = 256;
|
|
4521
4282
|
var RUN_ATTRIBUTE_VALUE_MAX_LENGTH = 4096;
|
|
4522
|
-
var RUN_ATTRIBUTE_TRIGGERS = ["cli", "dashboard", "api", "mcp"];
|
|
4523
|
-
var RUN_ATTRIBUTE_RUNNERS = ["local", "browserstack"];
|
|
4524
4283
|
function isPlainRecord(value) {
|
|
4525
4284
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4526
4285
|
}
|
|
@@ -4535,43 +4294,6 @@ function assertValidUserAttributeKey(key, sourceLabel) {
|
|
|
4535
4294
|
throw new Error(`${sourceLabel}: Attribute key "${key}" exceeds ${RUN_ATTRIBUTE_KEY_MAX_LENGTH} characters`);
|
|
4536
4295
|
}
|
|
4537
4296
|
}
|
|
4538
|
-
function assertValidAttributeKeyShape(key, sourceLabel) {
|
|
4539
|
-
if (key.length === 0) {
|
|
4540
|
-
throw new Error(`${sourceLabel}: Attribute key must be non-empty`);
|
|
4541
|
-
}
|
|
4542
|
-
if (key.length > RUN_ATTRIBUTE_KEY_MAX_LENGTH) {
|
|
4543
|
-
throw new Error(`${sourceLabel}: Attribute key "${key}" exceeds ${RUN_ATTRIBUTE_KEY_MAX_LENGTH} characters`);
|
|
4544
|
-
}
|
|
4545
|
-
}
|
|
4546
|
-
function assertValidAttributeValue(key, value, sourceLabel) {
|
|
4547
|
-
if (typeof value !== "string") {
|
|
4548
|
-
throw new Error(`${sourceLabel}: Attribute value for "${key}" must be a string`);
|
|
4549
|
-
}
|
|
4550
|
-
if (value.length > RUN_ATTRIBUTE_VALUE_MAX_LENGTH) {
|
|
4551
|
-
throw new Error(`${sourceLabel}: Attribute value for "${key}" exceeds ${RUN_ATTRIBUTE_VALUE_MAX_LENGTH} characters`);
|
|
4552
|
-
}
|
|
4553
|
-
}
|
|
4554
|
-
function assertValidInternalAttribute(key, value, sourceLabel) {
|
|
4555
|
-
if (key === ATTR_TRIGGER) {
|
|
4556
|
-
if (!RUN_ATTRIBUTE_TRIGGERS.includes(value)) {
|
|
4557
|
-
throw new Error(`${sourceLabel}: Attribute value for "${key}" must be one of: ${RUN_ATTRIBUTE_TRIGGERS.join(", ")}`);
|
|
4558
|
-
}
|
|
4559
|
-
return;
|
|
4560
|
-
}
|
|
4561
|
-
if (key === ATTR_RUNNER) {
|
|
4562
|
-
if (!RUN_ATTRIBUTE_RUNNERS.includes(value)) {
|
|
4563
|
-
throw new Error(`${sourceLabel}: Attribute value for "${key}" must be one of: ${RUN_ATTRIBUTE_RUNNERS.join(", ")}`);
|
|
4564
|
-
}
|
|
4565
|
-
return;
|
|
4566
|
-
}
|
|
4567
|
-
if (key === ATTR_GATE_RUN) {
|
|
4568
|
-
if (value !== "true") {
|
|
4569
|
-
throw new Error(`${sourceLabel}: Attribute value for "${key}" must be "true"`);
|
|
4570
|
-
}
|
|
4571
|
-
return;
|
|
4572
|
-
}
|
|
4573
|
-
throw new Error(`${sourceLabel}: Attribute key "${key}" uses the reserved prefix "${INTERNAL_ATTRIBUTE_PREFIX}"`);
|
|
4574
|
-
}
|
|
4575
4297
|
function validateUserRunAttributes(input, sourceLabel = "run attributes") {
|
|
4576
4298
|
if (input === void 0 || input === null) return {};
|
|
4577
4299
|
if (!isPlainRecord(input)) {
|
|
@@ -4590,22 +4312,6 @@ function validateUserRunAttributes(input, sourceLabel = "run attributes") {
|
|
|
4590
4312
|
}
|
|
4591
4313
|
return attributes;
|
|
4592
4314
|
}
|
|
4593
|
-
function validateTrustedRunAttributes(input, sourceLabel = "run attributes") {
|
|
4594
|
-
if (input === void 0 || input === null) return {};
|
|
4595
|
-
if (!isPlainRecord(input)) {
|
|
4596
|
-
throw new Error(`${sourceLabel}: attributes must be a plain object`);
|
|
4597
|
-
}
|
|
4598
|
-
const attributes = /* @__PURE__ */ Object.create(null);
|
|
4599
|
-
for (const [key, value] of Object.entries(input)) {
|
|
4600
|
-
assertValidAttributeKeyShape(key, sourceLabel);
|
|
4601
|
-
assertValidAttributeValue(key, value, sourceLabel);
|
|
4602
|
-
if (key.startsWith(INTERNAL_ATTRIBUTE_PREFIX)) {
|
|
4603
|
-
assertValidInternalAttribute(key, value, sourceLabel);
|
|
4604
|
-
}
|
|
4605
|
-
attributes[key] = value;
|
|
4606
|
-
}
|
|
4607
|
-
return attributes;
|
|
4608
|
-
}
|
|
4609
4315
|
function parseRunAttrFlags(flags) {
|
|
4610
4316
|
const rawAttributes = /* @__PURE__ */ Object.create(null);
|
|
4611
4317
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -4665,9 +4371,9 @@ function formatRunAttributesBlock(attributes) {
|
|
|
4665
4371
|
}
|
|
4666
4372
|
|
|
4667
4373
|
// src/version.ts
|
|
4668
|
-
var
|
|
4669
|
-
var
|
|
4670
|
-
var require2 = (0,
|
|
4374
|
+
var import_node_module2 = require("module");
|
|
4375
|
+
var import_meta2 = {};
|
|
4376
|
+
var require2 = (0, import_node_module2.createRequire)(typeof __filename === "string" ? __filename : import_meta2.url);
|
|
4671
4377
|
function readPackageVersion() {
|
|
4672
4378
|
try {
|
|
4673
4379
|
const manifest = require2("../package.json");
|
|
@@ -4685,17 +4391,17 @@ function getAgentQaVersion() {
|
|
|
4685
4391
|
|
|
4686
4392
|
// src/version-check/index.ts
|
|
4687
4393
|
var import_promises9 = require("fs/promises");
|
|
4688
|
-
var
|
|
4394
|
+
var import_node_module3 = require("module");
|
|
4689
4395
|
var import_node_os2 = require("os");
|
|
4690
|
-
var
|
|
4691
|
-
var
|
|
4692
|
-
var require3 = (0,
|
|
4396
|
+
var import_node_path14 = require("path");
|
|
4397
|
+
var import_meta3 = {};
|
|
4398
|
+
var require3 = (0, import_node_module3.createRequire)(typeof __filename === "string" ? __filename : import_meta3.url);
|
|
4693
4399
|
var AGENT_QA_NPM_REGISTRY_URL = "https://registry.npmjs.org/agent-qa";
|
|
4694
4400
|
var VERSION_CHECK_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4695
4401
|
var VERSION_CHECK_TIMEOUT_MS = 5e3;
|
|
4696
4402
|
var semverApi;
|
|
4697
4403
|
function getAgentQaVersionCheckCachePath(homeDir) {
|
|
4698
|
-
return (0,
|
|
4404
|
+
return (0, import_node_path14.join)(homeDir ?? (0, import_node_os2.homedir)(), ".agent-qa", "version-check.json");
|
|
4699
4405
|
}
|
|
4700
4406
|
async function getAgentQaUpdateStatus(options = {}) {
|
|
4701
4407
|
const installedVersion = getInstalledVersion();
|
|
@@ -4829,7 +4535,7 @@ async function readVersionCheckCache(cachePath) {
|
|
|
4829
4535
|
}
|
|
4830
4536
|
async function writeVersionCheckCache(cachePath, cache) {
|
|
4831
4537
|
try {
|
|
4832
|
-
await (0, import_promises9.mkdir)((0,
|
|
4538
|
+
await (0, import_promises9.mkdir)((0, import_node_path14.dirname)(cachePath), { recursive: true });
|
|
4833
4539
|
await (0, import_promises9.writeFile)(cachePath, JSON.stringify(cache, null, 2), {
|
|
4834
4540
|
encoding: "utf-8",
|
|
4835
4541
|
mode: 384
|
|
@@ -4875,7 +4581,6 @@ function isRecord2(value) {
|
|
|
4875
4581
|
AUTH_STATE_SCHEMA_VERSION,
|
|
4876
4582
|
AUTH_STATE_SLUG_PATTERN,
|
|
4877
4583
|
AccessibilityConfigSchema,
|
|
4878
|
-
ActionPlanSchema,
|
|
4879
4584
|
AdapterLogDataSchema,
|
|
4880
4585
|
AgentLogDataSchema,
|
|
4881
4586
|
AgentQaConfigSchema,
|
|
@@ -4932,7 +4637,6 @@ function isRecord2(value) {
|
|
|
4932
4637
|
PlannerConfigSchema,
|
|
4933
4638
|
PlannerLogDataSchema,
|
|
4934
4639
|
PluginsSchema,
|
|
4935
|
-
ProviderConfigSchema,
|
|
4936
4640
|
ProviderPluginDeclarationSchema,
|
|
4937
4641
|
RUNTIME_IMAGE_MAP,
|
|
4938
4642
|
RUN_ATTRIBUTE_KEY_MAX_LENGTH,
|
|
@@ -4950,7 +4654,6 @@ function isRecord2(value) {
|
|
|
4950
4654
|
TestMetaSchema,
|
|
4951
4655
|
TestStepSchema,
|
|
4952
4656
|
TimeoutConfigSchema,
|
|
4953
|
-
TransportSchema,
|
|
4954
4657
|
UseSchema,
|
|
4955
4658
|
VALID_FILENAME_RE,
|
|
4956
4659
|
VERSION_CHECK_CACHE_TTL_MS,
|
|
@@ -4958,7 +4661,6 @@ function isRecord2(value) {
|
|
|
4958
4661
|
WEBVIEW_SCROLLERS_SCRIPT,
|
|
4959
4662
|
WorkspaceSchema,
|
|
4960
4663
|
applyPatchSetToTestDoc,
|
|
4961
|
-
browserstackProvider,
|
|
4962
4664
|
buildAuthStateHookEnv,
|
|
4963
4665
|
buildInternalRunAttributes,
|
|
4964
4666
|
buildRefEntries,
|
|
@@ -4971,7 +4673,6 @@ function isRecord2(value) {
|
|
|
4971
4673
|
createAuthFetch,
|
|
4972
4674
|
deriveMobileScroll,
|
|
4973
4675
|
detectGlobOverlap,
|
|
4974
|
-
detectPlaywrightVersion,
|
|
4975
4676
|
discoverWorkspaceFiles,
|
|
4976
4677
|
estimateTokens,
|
|
4977
4678
|
formatAppiumInstallGuidance,
|
|
@@ -4990,21 +4691,21 @@ function isRecord2(value) {
|
|
|
4990
4691
|
getAuthPath,
|
|
4991
4692
|
getCredential,
|
|
4992
4693
|
getLLMAuthProviderPlugin,
|
|
4993
|
-
|
|
4694
|
+
guardJsonText,
|
|
4695
|
+
guardRunText,
|
|
4696
|
+
hookRunnerImage,
|
|
4994
4697
|
isCanonicalRunId,
|
|
4698
|
+
isGitHubActions,
|
|
4995
4699
|
isLoopbackHost,
|
|
4996
4700
|
isPathInsideDir,
|
|
4997
4701
|
isReservedAuthStateHookEnvKey,
|
|
4998
4702
|
isWorkspacePathMatch,
|
|
4999
4703
|
listAuthStateMetadata,
|
|
5000
4704
|
listLLMAuthProviderPlugins,
|
|
5001
|
-
listProviders,
|
|
5002
4705
|
loadLLMAuthPlugins,
|
|
5003
4706
|
loadProviderPlugins,
|
|
5004
4707
|
localAppiumBinaryNames,
|
|
5005
4708
|
locateRunsDbPath,
|
|
5006
|
-
mapMobileCapabilities,
|
|
5007
|
-
mapWebCapabilities,
|
|
5008
4709
|
mergeRunAttributes,
|
|
5009
4710
|
normalizeAuthStateUse,
|
|
5010
4711
|
normalizeRole,
|
|
@@ -5019,21 +4720,21 @@ function isRecord2(value) {
|
|
|
5019
4720
|
parseTestFileWithTokens,
|
|
5020
4721
|
parseWebViewScrollers,
|
|
5021
4722
|
patchTestForSuite,
|
|
4723
|
+
printRunText,
|
|
5022
4724
|
readAuth,
|
|
5023
4725
|
readAuthStateMetadata,
|
|
5024
|
-
|
|
4726
|
+
refuseRetiredLocalBindings,
|
|
5025
4727
|
registerLLMAuthProviderPlugin,
|
|
5026
4728
|
registerLLMAuthProviderPlugins,
|
|
5027
|
-
registerProvider,
|
|
5028
4729
|
removeAuth,
|
|
5029
4730
|
removeAuthStateFiles,
|
|
5030
4731
|
removeAuthStateTarget,
|
|
5031
4732
|
renderPatchedTestDoc,
|
|
4733
|
+
reserveStdoutForData,
|
|
5032
4734
|
resolveAppiumExecutable,
|
|
5033
4735
|
resolveAuthStateForRun,
|
|
5034
4736
|
resolveAuthStatePaths,
|
|
5035
4737
|
resolveAuthStateRoot,
|
|
5036
|
-
resolveFarmCredentials,
|
|
5037
4738
|
resolveFileReferences,
|
|
5038
4739
|
resolveKnowledgeRoot,
|
|
5039
4740
|
resolveLLMAuth,
|
|
@@ -5049,7 +4750,6 @@ function isRecord2(value) {
|
|
|
5049
4750
|
validateProject,
|
|
5050
4751
|
validateSuiteFile,
|
|
5051
4752
|
validateTestFile,
|
|
5052
|
-
validateTrustedRunAttributes,
|
|
5053
4753
|
validateUserRunAttributes,
|
|
5054
4754
|
warnIfOutOfBounds,
|
|
5055
4755
|
webViewSwipeSignal,
|