@exodus/xqa 1.14.1 → 1.15.0
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 +84 -16
- package/dist/xqa.cjs +162 -237
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -14,12 +14,20 @@ The tool manages configuration, project initialization, session state tracking,
|
|
|
14
14
|
|
|
15
15
|
Initialize a new xqa project in the current directory.
|
|
16
16
|
|
|
17
|
-
Creates a `.xqa/` directory with
|
|
17
|
+
Creates a `.xqa/` directory with `app.md` and `explore.md` templates plus subdirectories for specs, designs, and suites. Installs bundled xqa skills.
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
20
|
xqa init
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
### update
|
|
24
|
+
|
|
25
|
+
Update installed xqa skills to the current CLI version.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
xqa update
|
|
29
|
+
```
|
|
30
|
+
|
|
23
31
|
### explore [prompt]
|
|
24
32
|
|
|
25
33
|
Run the explorer agent; omit prompt for a full breadth-first sweep.
|
|
@@ -31,44 +39,73 @@ xqa explore # breadth-first exploration
|
|
|
31
39
|
xqa explore "test the login flow" # focused exploration
|
|
32
40
|
xqa explore -v prompt,screen # verbose output for categories
|
|
33
41
|
xqa explore -v # verbose output for all categories
|
|
42
|
+
xqa explore -t 600 # override explorer timeout (seconds)
|
|
43
|
+
xqa explore --debug # log timing and event details to stderr
|
|
34
44
|
```
|
|
35
45
|
|
|
36
|
-
|
|
46
|
+
Flags:
|
|
47
|
+
|
|
48
|
+
- `-v, --verbose [categories]` — Log categories (prompt, tools, screen, memory). Default: all if flag is present without value.
|
|
49
|
+
- `-t, --timeout <seconds>` — Explorer timeout in seconds (overrides `QA_EXPLORE_TIMEOUT_SECONDS`).
|
|
50
|
+
- `--debug` — Log timing and event details to stderr.
|
|
37
51
|
|
|
38
52
|
### spec [spec-file]
|
|
39
53
|
|
|
40
54
|
Run the explorer agent against a spec file.
|
|
41
55
|
|
|
42
|
-
Loads a spec markdown file from `.xqa/specs/` (or an absolute path) and executes the agent against it.
|
|
56
|
+
Loads a spec markdown file from `.xqa/specs/` (or an absolute path) and executes the agent against it. Omit the argument to pick from available specs interactively.
|
|
43
57
|
|
|
44
58
|
```bash
|
|
45
59
|
xqa spec # interactive spec picker
|
|
46
|
-
xqa spec .xqa/specs/authentication.test.md
|
|
60
|
+
xqa spec .xqa/specs/authentication.test.md # explicit spec file
|
|
47
61
|
xqa spec -v tools,memory # verbose output
|
|
62
|
+
xqa spec --debug # debug logging
|
|
48
63
|
```
|
|
49
64
|
|
|
50
|
-
|
|
65
|
+
Flags:
|
|
66
|
+
|
|
67
|
+
- `-v, --verbose [categories]` — Same as explore.
|
|
68
|
+
- `--debug` — Log timing and event details to stderr.
|
|
51
69
|
|
|
52
70
|
Spec file format (YAML frontmatter + markdown):
|
|
53
71
|
|
|
54
72
|
```markdown
|
|
55
73
|
---
|
|
56
74
|
feature: 'Feature Name'
|
|
57
|
-
entry: 'Screen name or navigation path'
|
|
58
75
|
timeout: 300
|
|
59
76
|
---
|
|
60
77
|
|
|
61
78
|
# Spec content
|
|
62
79
|
```
|
|
63
80
|
|
|
81
|
+
Frontmatter fields: `feature` (required), `timeout` (optional, seconds).
|
|
82
|
+
|
|
83
|
+
### run
|
|
84
|
+
|
|
85
|
+
Run a test suite or a set of spec files in parallel across booted simulators.
|
|
86
|
+
|
|
87
|
+
Exactly one of `--suite` or `--spec` is required.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
xqa run --suite smoke # run .xqa/suites/smoke.suite.json
|
|
91
|
+
xqa run --spec 'specs/**/*.test.md' # run matching spec files
|
|
92
|
+
xqa run --suite smoke --debug # debug logging
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Flags:
|
|
96
|
+
|
|
97
|
+
- `--suite <name>` — Name of the suite (`<name>.suite.json`) under `.xqa/suites/`.
|
|
98
|
+
- `--spec <globs...>` — Glob patterns matching spec files, resolved from the xqa directory.
|
|
99
|
+
- `--debug` — Log timing and event details to stderr.
|
|
100
|
+
|
|
64
101
|
### review [findings-path]
|
|
65
102
|
|
|
66
103
|
Review findings and mark false positives.
|
|
67
104
|
|
|
68
|
-
Interactive session for triaging findings generated by explore or spec runs.
|
|
105
|
+
Interactive session for triaging findings generated by explore or spec runs. Mark findings as dismissed (with optional reason) or undo previous dismissals. Dismissals are written to `dismissals.json` next to the `.xqa` directory (override with `QA_DISMISSALS_PATH`). Defaults to the last findings path if omitted.
|
|
69
106
|
|
|
70
107
|
```bash
|
|
71
|
-
xqa review
|
|
108
|
+
xqa review # use last findings file
|
|
72
109
|
xqa review .xqa/output/findings-abc123.json # explicit path
|
|
73
110
|
```
|
|
74
111
|
|
|
@@ -93,26 +130,35 @@ xqa completion bash # generate bash completions
|
|
|
93
130
|
xqa completion zsh # generate zsh completions
|
|
94
131
|
```
|
|
95
132
|
|
|
96
|
-
### Suite
|
|
133
|
+
### Suite config
|
|
97
134
|
|
|
98
|
-
Suite
|
|
135
|
+
Suite files live at `.xqa/suites/<name>.suite.json` and declare the work items plus optional hooks.
|
|
99
136
|
|
|
100
137
|
```json
|
|
101
138
|
{
|
|
102
139
|
"specs": ["specs/send.test.md"],
|
|
140
|
+
"freestyle": [{ "prompt": "explore settings", "timeoutSeconds": 300 }],
|
|
103
141
|
"hooks": {
|
|
104
142
|
"beforeEach": {
|
|
105
143
|
"script": "qa/prepare-sim.mjs",
|
|
106
|
-
"env": { "APP_PROFILE": "funded" }
|
|
144
|
+
"env": { "APP_PROFILE": "funded" },
|
|
145
|
+
"timeoutSeconds": 120
|
|
107
146
|
}
|
|
108
147
|
}
|
|
109
148
|
}
|
|
110
149
|
```
|
|
111
150
|
|
|
151
|
+
Fields:
|
|
152
|
+
|
|
153
|
+
- `specs` (optional) — glob patterns resolved from the xqa directory.
|
|
154
|
+
- `freestyle` (optional) — either a positive integer (N empty entries) or an array of `{ prompt?, timeoutSeconds }` entries.
|
|
155
|
+
- At least one of `specs` or `freestyle` must resolve to a work item.
|
|
156
|
+
- `hooks.beforeEach` (optional) — runs before every work item on every simulator. Use for project-owned setup (wallet provisioning, cache warming, login seeding).
|
|
157
|
+
|
|
112
158
|
The hook script is invoked as a Node child process. It receives:
|
|
113
159
|
|
|
114
160
|
- Inherited `process.env`
|
|
115
|
-
- Suite-declared `env`
|
|
161
|
+
- Suite-declared `env` overlaid with reserved keys (reserved wins)
|
|
116
162
|
- Reserved xqa-owned keys: `XQA_SIM_UDID`, `XQA_ITEM_ID`, `XQA_ITEM_TYPE`, `XQA_ITEM_NAME`, `XQA_SUITE`, and (when item type is `spec`) `XQA_SPEC_PATH`
|
|
117
163
|
|
|
118
164
|
Suite-declared `env` cannot override reserved keys — the parser rejects such configs.
|
|
@@ -121,7 +167,7 @@ Contract:
|
|
|
121
167
|
|
|
122
168
|
- Exit 0 → proceed with item.
|
|
123
169
|
- Non-zero exit → item marked failed, `executeItem` skipped, counts toward simulator-unhealthy threshold.
|
|
124
|
-
- 120s timeout
|
|
170
|
+
- Default 120s timeout, overridable via `hooks.beforeEach.timeoutSeconds`.
|
|
125
171
|
- Honors the suite abort signal.
|
|
126
172
|
|
|
127
173
|
## Configuration
|
|
@@ -133,15 +179,17 @@ Configuration is loaded from environment variables and `.env.local`:
|
|
|
133
179
|
- `QA_RUN_ID` (optional) — Custom run identifier; defaults to auto-generated
|
|
134
180
|
- `QA_EXPLORE_TIMEOUT_SECONDS` (optional) — Exploration timeout in seconds
|
|
135
181
|
- `QA_BUILD_ENV` (optional) — Build environment: `dev` or `prod` (default: prod)
|
|
182
|
+
- `QA_DISMISSALS_PATH` (optional) — Override the dismissals file path used by `xqa review`
|
|
136
183
|
|
|
137
184
|
## Architecture
|
|
138
185
|
|
|
139
186
|
Key files and directories:
|
|
140
187
|
|
|
141
188
|
- `src/index.ts` — CLI entry point; wires commander commands and manages graceful shutdown via process locks
|
|
142
|
-
- `src/commands/` — Command implementations (init, explore, spec, review, analyse, completion)
|
|
143
|
-
- `src/
|
|
144
|
-
- `src/
|
|
189
|
+
- `src/commands/` — Command implementations (init, update, explore, spec, review, analyse, completion)
|
|
190
|
+
- `src/suite/` — Suite runner: config parsing, work-item building, worker pool, hooks
|
|
191
|
+
- `src/core/` — Pure functions: completion generation, verbose/timeout option parsing, last-path tracking
|
|
192
|
+
- `src/shell/` — I/O wrappers: app/explore context reading, debug logging, display factory, preflight, xqa directory discovery
|
|
145
193
|
- `src/config.ts`, `src/config-schema.ts` — Configuration loading and validation with Zod
|
|
146
194
|
- `src/review-session.ts` — Interactive finding review loop with dismissal tracking
|
|
147
195
|
- `src/spec-frontmatter.ts` — Spec markdown frontmatter parsing (YAML)
|
|
@@ -157,6 +205,8 @@ Core error discriminated unions:
|
|
|
157
205
|
- `XqaDirectoryError` — No .xqa directory found (XQA_NOT_INITIALIZED)
|
|
158
206
|
- `SpecFrontmatterError` — Malformed spec markdown (MISSING_FRONTMATTER, MISSING_FIELD, PARSE_ERROR)
|
|
159
207
|
- `LastPathError` — No findings path provided and no prior session (NO_ARG_AND_NO_STATE)
|
|
208
|
+
- `SuiteConfigError` — Suite config JSON malformed or schema-invalid (INVALID_SUITE_CONFIG)
|
|
209
|
+
- `HookError` — Suite hook failure (HOOK_SPAWN_FAILED, HOOK_EXIT_NONZERO, HOOK_TIMEOUT, HOOK_ABORTED)
|
|
160
210
|
|
|
161
211
|
## Development
|
|
162
212
|
|
|
@@ -231,20 +281,38 @@ src/
|
|
|
231
281
|
|
|
232
282
|
commands/
|
|
233
283
|
init-command.ts # Project initialization
|
|
284
|
+
update-command.ts # Skill updates
|
|
285
|
+
install-skills.ts # Bundled skill installer
|
|
234
286
|
explore-command.ts # Breadth-first exploration
|
|
235
287
|
spec-command.ts # Spec-based exploration
|
|
288
|
+
spec-resolver.ts # Spec file discovery and parsing
|
|
236
289
|
review-command.ts # Finding triage workflow
|
|
237
290
|
analyse-command.ts # Video analysis
|
|
238
291
|
completion-command.ts # Shell completion generation
|
|
292
|
+
item-events.ts # Start/complete/fail event emitters
|
|
239
293
|
|
|
240
294
|
core/
|
|
241
295
|
parse-verbose.ts # Verbose flag parsing
|
|
296
|
+
parse-timeout-seconds.ts # Timeout flag parsing
|
|
242
297
|
completion-generator.ts # Bash/zsh completion script generation
|
|
243
298
|
last-path.ts # Last findings path tracking
|
|
244
299
|
|
|
245
300
|
shell/
|
|
246
301
|
app-context.ts # Read app.md and explore.md
|
|
247
302
|
xqa-directory.ts # Locate .xqa directory
|
|
303
|
+
preflight.ts # Environment preflight checks
|
|
304
|
+
display-factory.ts # Solo and suite display factories
|
|
305
|
+
debug-logger.ts # Debug event logging
|
|
306
|
+
debug-agent-events.ts # Agent event debug formatter
|
|
307
|
+
debug-suite-events.ts # Suite event debug formatter
|
|
308
|
+
debug-logger-core.ts # Pure logging helpers
|
|
309
|
+
trigger-abort.ts # Abort signal plumbing
|
|
310
|
+
|
|
311
|
+
suite/
|
|
312
|
+
types.ts # Suite work-item and findings types
|
|
313
|
+
core/ # Pure: config parser, item builders, hook env, queue
|
|
314
|
+
shell/ # I/O: worker pool, hook runner, findings writer
|
|
315
|
+
commands/ # run-command, execute-item, suite-run-context
|
|
248
316
|
|
|
249
317
|
__tests__/
|
|
250
318
|
*.test.ts # Test files co-located with src/
|
package/dist/xqa.cjs
CHANGED
|
@@ -3733,7 +3733,7 @@ var require_index_cjs = __commonJS({
|
|
|
3733
3733
|
}
|
|
3734
3734
|
var fromPromise = ResultAsync14.fromPromise;
|
|
3735
3735
|
var fromSafePromise2 = ResultAsync14.fromSafePromise;
|
|
3736
|
-
var
|
|
3736
|
+
var fromAsyncThrowable9 = ResultAsync14.fromThrowable;
|
|
3737
3737
|
var combineResultList = (resultList) => {
|
|
3738
3738
|
let acc = ok21([]);
|
|
3739
3739
|
for (const result of resultList) {
|
|
@@ -3950,7 +3950,7 @@ var require_index_cjs = __commonJS({
|
|
|
3950
3950
|
exports2.ResultAsync = ResultAsync14;
|
|
3951
3951
|
exports2.err = err20;
|
|
3952
3952
|
exports2.errAsync = errAsync8;
|
|
3953
|
-
exports2.fromAsyncThrowable =
|
|
3953
|
+
exports2.fromAsyncThrowable = fromAsyncThrowable9;
|
|
3954
3954
|
exports2.fromPromise = fromPromise;
|
|
3955
3955
|
exports2.fromSafePromise = fromSafePromise2;
|
|
3956
3956
|
exports2.fromThrowable = fromThrowable15;
|
|
@@ -52351,27 +52351,13 @@ var DEFAULT_LONG_PRESS_DURATION_MS = 500;
|
|
|
52351
52351
|
var MS_PER_SECOND = 1e3;
|
|
52352
52352
|
var DEFAULT_SWIPE_DURATION_SECONDS = 0.1;
|
|
52353
52353
|
var ENTER_KEY_CODE = "0x28";
|
|
52354
|
-
var TAP_SCHEMA = { x: external_exports.number(), y: external_exports.number() };
|
|
52355
|
-
var LONG_PRESS_SCHEMA = { x: external_exports.number(), y: external_exports.number(), duration: external_exports.number().optional() };
|
|
52356
|
-
var SWIPE_SCHEMA = {
|
|
52357
|
-
x_start: external_exports.number(),
|
|
52358
|
-
y_start: external_exports.number(),
|
|
52359
|
-
x_end: external_exports.number(),
|
|
52360
|
-
y_end: external_exports.number(),
|
|
52361
|
-
duration: external_exports.number().positive().optional().describe(
|
|
52362
|
-
"Gesture duration in seconds. Smaller values (e.g. 0.1) produce a flick that scrolls lists and dismisses sheets; larger values (e.g. 0.5+) produce a slow drag suitable for reordering or panning. Defaults to 0.1s (flick)."
|
|
52363
|
-
)
|
|
52364
|
-
};
|
|
52365
|
-
var TYPE_TEXT_SCHEMA = { text: external_exports.string(), submit: external_exports.boolean().optional() };
|
|
52366
|
-
var PRESS_BUTTON_SCHEMA = {
|
|
52367
|
-
button: external_exports.enum(["HOME", "LOCK", "SIRI", "SIDE_BUTTON", "APPLE_PAY"])
|
|
52368
|
-
};
|
|
52369
52354
|
function errorResult2(error48) {
|
|
52370
52355
|
return {
|
|
52371
52356
|
content: [{ type: "text", text: `Error: ${String(error48.cause)}` }],
|
|
52372
52357
|
isError: true
|
|
52373
52358
|
};
|
|
52374
52359
|
}
|
|
52360
|
+
var TAP_SCHEMA = { x: external_exports.number(), y: external_exports.number() };
|
|
52375
52361
|
function buildTapArguments(resolvedUdid, input) {
|
|
52376
52362
|
return ["ui", "tap", "--udid", resolvedUdid, String(input.x), String(input.y)];
|
|
52377
52363
|
}
|
|
@@ -52406,6 +52392,23 @@ async function handleDoubleTap(input) {
|
|
|
52406
52392
|
]
|
|
52407
52393
|
};
|
|
52408
52394
|
}
|
|
52395
|
+
function createTapTool(udid = "booted") {
|
|
52396
|
+
return _x(
|
|
52397
|
+
"tap",
|
|
52398
|
+
"Tap on the screen at the given coordinates.",
|
|
52399
|
+
TAP_SCHEMA,
|
|
52400
|
+
async ({ x, y: y6 }) => handleTap({ udid, x, y: y6 })
|
|
52401
|
+
);
|
|
52402
|
+
}
|
|
52403
|
+
function createDoubleTapTool(udid = "booted") {
|
|
52404
|
+
return _x(
|
|
52405
|
+
"double_tap",
|
|
52406
|
+
"Double-tap on the screen at the given coordinates.",
|
|
52407
|
+
TAP_SCHEMA,
|
|
52408
|
+
async ({ x, y: y6 }) => handleDoubleTap({ udid, x, y: y6 })
|
|
52409
|
+
);
|
|
52410
|
+
}
|
|
52411
|
+
var LONG_PRESS_SCHEMA = { x: external_exports.number(), y: external_exports.number(), duration: external_exports.number().optional() };
|
|
52409
52412
|
function buildLongPressArguments(resolvedUdid, input) {
|
|
52410
52413
|
return [
|
|
52411
52414
|
"ui",
|
|
@@ -52434,8 +52437,39 @@ async function handleLongPress(input) {
|
|
|
52434
52437
|
]
|
|
52435
52438
|
};
|
|
52436
52439
|
}
|
|
52440
|
+
function createLongPressTool(udid = "booted") {
|
|
52441
|
+
return _x(
|
|
52442
|
+
"long_press",
|
|
52443
|
+
"Long-press on the screen at the given coordinates.",
|
|
52444
|
+
LONG_PRESS_SCHEMA,
|
|
52445
|
+
async ({ x, y: y6, duration: duration3 }) => handleLongPress({ udid, x, y: y6, duration: duration3 ?? DEFAULT_LONG_PRESS_DURATION_MS })
|
|
52446
|
+
);
|
|
52447
|
+
}
|
|
52448
|
+
var SWIPE_SCHEMA = {
|
|
52449
|
+
x_start: external_exports.number(),
|
|
52450
|
+
y_start: external_exports.number(),
|
|
52451
|
+
x_end: external_exports.number(),
|
|
52452
|
+
y_end: external_exports.number(),
|
|
52453
|
+
duration: external_exports.number().positive().optional().describe(
|
|
52454
|
+
"Seconds for the full gesture. Velocity = distance / duration \u2014 raise duration at fixed distance to slow the gesture and reduce momentum. Default 0.1s (flick) works for short lists; raise to 0.4\u20130.8 for controlled scrolling on long lists where the flick overshoots."
|
|
52455
|
+
),
|
|
52456
|
+
delta: external_exports.number().positive().optional().describe(
|
|
52457
|
+
"Pixel distance between interpolated touch points along the swipe path. Smaller values (e.g. 5) produce a denser event stream \u2014 smoother motion and more controllable stop-velocity, recommended when combining with a raised duration to tame long-list overshoot. Larger values produce coarser strokes. Omit to use idb defaults."
|
|
52458
|
+
)
|
|
52459
|
+
};
|
|
52460
|
+
function toSwipeInput(udid, params) {
|
|
52461
|
+
return {
|
|
52462
|
+
udid,
|
|
52463
|
+
xStart: params.x_start,
|
|
52464
|
+
yStart: params.y_start,
|
|
52465
|
+
xEnd: params.x_end,
|
|
52466
|
+
yEnd: params.y_end,
|
|
52467
|
+
duration: params.duration ?? DEFAULT_SWIPE_DURATION_SECONDS,
|
|
52468
|
+
delta: params.delta
|
|
52469
|
+
};
|
|
52470
|
+
}
|
|
52437
52471
|
function buildSwipeArguments(resolvedUdid, input) {
|
|
52438
|
-
|
|
52472
|
+
const arguments_ = [
|
|
52439
52473
|
"ui",
|
|
52440
52474
|
"swipe",
|
|
52441
52475
|
"--udid",
|
|
@@ -52447,6 +52481,10 @@ function buildSwipeArguments(resolvedUdid, input) {
|
|
|
52447
52481
|
"--duration",
|
|
52448
52482
|
String(input.duration)
|
|
52449
52483
|
];
|
|
52484
|
+
if (input.delta !== void 0) {
|
|
52485
|
+
arguments_.push("--delta", String(input.delta));
|
|
52486
|
+
}
|
|
52487
|
+
return arguments_;
|
|
52450
52488
|
}
|
|
52451
52489
|
async function handleSwipe(input) {
|
|
52452
52490
|
const result = await resolveUdid(input.udid).andThen(
|
|
@@ -52464,6 +52502,15 @@ async function handleSwipe(input) {
|
|
|
52464
52502
|
]
|
|
52465
52503
|
};
|
|
52466
52504
|
}
|
|
52505
|
+
function createSwipeTool(udid = "booted") {
|
|
52506
|
+
return _x(
|
|
52507
|
+
"swipe",
|
|
52508
|
+
"Swipe on the screen from one point to another. Default duration 0.1s produces a flick \u2014 use for scrolling lists, dismissing sheets, triggering paging. Use duration 0.5+ for slow drag (reorder, pan). For long lists where default flick overshoots: shorten swipe distance AND raise duration to 0.4\u20130.8 to lower velocity; optionally lower delta for denser touch events and a more controllable stop. Omitting duration uses the flick default.",
|
|
52509
|
+
SWIPE_SCHEMA,
|
|
52510
|
+
async (params) => handleSwipe(toSwipeInput(udid, params))
|
|
52511
|
+
);
|
|
52512
|
+
}
|
|
52513
|
+
var TYPE_TEXT_SCHEMA = { text: external_exports.string(), submit: external_exports.boolean().optional() };
|
|
52467
52514
|
async function handleTypeText({ udid, text, submit }) {
|
|
52468
52515
|
const result = await resolveUdid(udid).andThen((resolvedUdid) => {
|
|
52469
52516
|
const typeResult = runCommand("idb", ["ui", "text", "--udid", resolvedUdid, text]);
|
|
@@ -52479,6 +52526,17 @@ async function handleTypeText({ udid, text, submit }) {
|
|
|
52479
52526
|
}
|
|
52480
52527
|
return { content: [{ type: "text", text: `Typed: ${text}` }] };
|
|
52481
52528
|
}
|
|
52529
|
+
function createTypeTextTool(udid = "booted") {
|
|
52530
|
+
return _x(
|
|
52531
|
+
"type_text",
|
|
52532
|
+
"Type text into the currently focused element.",
|
|
52533
|
+
TYPE_TEXT_SCHEMA,
|
|
52534
|
+
async ({ text, submit }) => handleTypeText({ udid, text, submit: submit ?? false })
|
|
52535
|
+
);
|
|
52536
|
+
}
|
|
52537
|
+
var PRESS_BUTTON_SCHEMA = {
|
|
52538
|
+
button: external_exports.enum(["HOME", "LOCK", "SIRI", "SIDE_BUTTON", "APPLE_PAY"])
|
|
52539
|
+
};
|
|
52482
52540
|
async function handlePressButton(udid, button) {
|
|
52483
52541
|
const result = await resolveUdid(udid).andThen(
|
|
52484
52542
|
(resolvedUdid) => runCommand("idb", ["ui", "button", "--udid", resolvedUdid, button])
|
|
@@ -52488,56 +52546,6 @@ async function handlePressButton(udid, button) {
|
|
|
52488
52546
|
}
|
|
52489
52547
|
return { content: [{ type: "text", text: `Pressed button: ${button}` }] };
|
|
52490
52548
|
}
|
|
52491
|
-
function createTapTool(udid = "booted") {
|
|
52492
|
-
return _x(
|
|
52493
|
-
"tap",
|
|
52494
|
-
"Tap on the screen at the given coordinates.",
|
|
52495
|
-
TAP_SCHEMA,
|
|
52496
|
-
async ({ x, y: y6 }) => handleTap({ udid, x, y: y6 })
|
|
52497
|
-
);
|
|
52498
|
-
}
|
|
52499
|
-
function createDoubleTapTool(udid = "booted") {
|
|
52500
|
-
return _x(
|
|
52501
|
-
"double_tap",
|
|
52502
|
-
"Double-tap on the screen at the given coordinates.",
|
|
52503
|
-
TAP_SCHEMA,
|
|
52504
|
-
async ({ x, y: y6 }) => handleDoubleTap({ udid, x, y: y6 })
|
|
52505
|
-
);
|
|
52506
|
-
}
|
|
52507
|
-
function createLongPressTool(udid = "booted") {
|
|
52508
|
-
return _x(
|
|
52509
|
-
"long_press",
|
|
52510
|
-
"Long-press on the screen at the given coordinates.",
|
|
52511
|
-
LONG_PRESS_SCHEMA,
|
|
52512
|
-
async ({ x, y: y6, duration: duration3 }) => handleLongPress({ udid, x, y: y6, duration: duration3 ?? DEFAULT_LONG_PRESS_DURATION_MS })
|
|
52513
|
-
);
|
|
52514
|
-
}
|
|
52515
|
-
function toSwipeInput(udid, params) {
|
|
52516
|
-
return {
|
|
52517
|
-
udid,
|
|
52518
|
-
xStart: params.x_start,
|
|
52519
|
-
yStart: params.y_start,
|
|
52520
|
-
xEnd: params.x_end,
|
|
52521
|
-
yEnd: params.y_end,
|
|
52522
|
-
duration: params.duration ?? DEFAULT_SWIPE_DURATION_SECONDS
|
|
52523
|
-
};
|
|
52524
|
-
}
|
|
52525
|
-
function createSwipeTool(udid = "booted") {
|
|
52526
|
-
return _x(
|
|
52527
|
-
"swipe",
|
|
52528
|
-
"Swipe on the screen from one point to another. `duration` (seconds) controls gesture velocity: the default 0.1s produces a flick that scrolls lists, dismisses sheets and triggers paging; pass a larger value (e.g. 0.5+) for a slow drag suitable for reordering or panning. Omitting `duration` uses the flick default.",
|
|
52529
|
-
SWIPE_SCHEMA,
|
|
52530
|
-
async (params) => handleSwipe(toSwipeInput(udid, params))
|
|
52531
|
-
);
|
|
52532
|
-
}
|
|
52533
|
-
function createTypeTextTool(udid = "booted") {
|
|
52534
|
-
return _x(
|
|
52535
|
-
"type_text",
|
|
52536
|
-
"Type text into the currently focused element.",
|
|
52537
|
-
TYPE_TEXT_SCHEMA,
|
|
52538
|
-
async ({ text, submit }) => handleTypeText({ udid, text, submit: submit ?? false })
|
|
52539
|
-
);
|
|
52540
|
-
}
|
|
52541
52549
|
function createPressButtonTool(udid = "booted") {
|
|
52542
52550
|
return _x(
|
|
52543
52551
|
"press_button",
|
|
@@ -52855,8 +52863,6 @@ function resolveRunPaths({ outputDirectory, runId, date: date5 }) {
|
|
|
52855
52863
|
return (0, import_neverthrow8.ok)({
|
|
52856
52864
|
baseDir: baseDirectory,
|
|
52857
52865
|
videoPath: import_node_path2.default.join(baseDirectory, "recording.mp4"),
|
|
52858
|
-
videoPath2x: import_node_path2.default.join(baseDirectory, "recording_2x.mp4"),
|
|
52859
|
-
videoPath4x: import_node_path2.default.join(baseDirectory, "recording_4x.mp4"),
|
|
52860
52866
|
findingsPath: import_node_path2.default.join(baseDirectory, "findings.json"),
|
|
52861
52867
|
screenshotsDir: import_node_path2.default.join(baseDirectory, "screenshots")
|
|
52862
52868
|
});
|
|
@@ -56115,24 +56121,13 @@ function makePollTick(onEvent) {
|
|
|
56115
56121
|
});
|
|
56116
56122
|
};
|
|
56117
56123
|
}
|
|
56118
|
-
function resolveVideoPath(artifacts, variant) {
|
|
56119
|
-
if (variant === "2x") {
|
|
56120
|
-
return artifacts.videoPath2x;
|
|
56121
|
-
}
|
|
56122
|
-
if (variant === "4x") {
|
|
56123
|
-
return artifacts.videoPath4x;
|
|
56124
|
-
}
|
|
56125
|
-
return artifacts.videoPath;
|
|
56126
|
-
}
|
|
56127
56124
|
function buildRunContext(artifacts, config3) {
|
|
56128
|
-
const variant = config3?.videoVariant ?? "4x";
|
|
56129
56125
|
return {
|
|
56130
56126
|
onEvent: config3?.onEvent === void 0 ? void 0 : safeHandler(config3.onEvent),
|
|
56131
56127
|
apiKey: config3?.apiKey ?? "",
|
|
56132
56128
|
model: config3?.model ?? DEFAULT_MODEL,
|
|
56133
|
-
variant,
|
|
56134
56129
|
start: Date.now(),
|
|
56135
|
-
videoPath:
|
|
56130
|
+
videoPath: artifacts.videoPath,
|
|
56136
56131
|
prompt: buildAnalyserPrompt(artifacts.snapshots)
|
|
56137
56132
|
};
|
|
56138
56133
|
}
|
|
@@ -56162,11 +56157,11 @@ function runUploadPipeline(context) {
|
|
|
56162
56157
|
}
|
|
56163
56158
|
function runAnalyser(artifacts, config3) {
|
|
56164
56159
|
const context = buildRunContext(artifacts, config3);
|
|
56165
|
-
const { onEvent,
|
|
56160
|
+
const { onEvent, start, videoPath } = context;
|
|
56166
56161
|
onEvent?.({ type: "STAGE_START", agent: "analyser" });
|
|
56167
56162
|
if (!videoPath) {
|
|
56168
56163
|
emitStageEnd(onEvent, start);
|
|
56169
|
-
return (0, import_neverthrow10.errAsync)({ type: "VIDEO_PATH_MISSING"
|
|
56164
|
+
return (0, import_neverthrow10.errAsync)({ type: "VIDEO_PATH_MISSING" });
|
|
56170
56165
|
}
|
|
56171
56166
|
return runUploadPipeline(context);
|
|
56172
56167
|
}
|
|
@@ -62790,17 +62785,6 @@ function runQuery(prompt, config3) {
|
|
|
62790
62785
|
function collectAgentOutput(prompt, config3) {
|
|
62791
62786
|
return runQuery(prompt, config3).mapErr((cause) => ({ type: "QUERY_FAILED", cause }));
|
|
62792
62787
|
}
|
|
62793
|
-
async function runFfmpeg(arguments_) {
|
|
62794
|
-
const { promise: promise2, resolve, reject } = Promise.withResolvers();
|
|
62795
|
-
(0, import_node_child_process4.execFile)("ffmpeg", arguments_, (error48) => {
|
|
62796
|
-
if (error48) {
|
|
62797
|
-
reject(error48);
|
|
62798
|
-
} else {
|
|
62799
|
-
resolve(true);
|
|
62800
|
-
}
|
|
62801
|
-
});
|
|
62802
|
-
await promise2;
|
|
62803
|
-
}
|
|
62804
62788
|
function spawnRecorder(outputPath) {
|
|
62805
62789
|
const safeMkdirSync = (0, import_neverthrow20.fromThrowable)(import_node_fs.mkdirSync, (cause) => cause);
|
|
62806
62790
|
const safeSpawn2 = (0, import_neverthrow20.fromThrowable)(
|
|
@@ -62874,35 +62858,6 @@ function stopRecording(handle) {
|
|
|
62874
62858
|
(cause) => ({ type: "STOP_FAILED", cause })
|
|
62875
62859
|
);
|
|
62876
62860
|
}
|
|
62877
|
-
function speedUpVideo({
|
|
62878
|
-
inputPath,
|
|
62879
|
-
factor,
|
|
62880
|
-
outputPath
|
|
62881
|
-
}) {
|
|
62882
|
-
const pts = String(1 / factor);
|
|
62883
|
-
return (0, import_neverthrow20.fromAsyncThrowable)(
|
|
62884
|
-
runFfmpeg,
|
|
62885
|
-
(cause) => ({ type: "PROCESS_FAILED", cause })
|
|
62886
|
-
)(["-i", inputPath, "-filter:v", `setpts=${pts}*PTS`, "-an", "-y", outputPath]).map(
|
|
62887
|
-
() => outputPath
|
|
62888
|
-
);
|
|
62889
|
-
}
|
|
62890
|
-
var SPEED_2X = 2;
|
|
62891
|
-
var SPEED_4X = 4;
|
|
62892
|
-
function applySpeedUpVariants({
|
|
62893
|
-
result,
|
|
62894
|
-
params,
|
|
62895
|
-
toRecordingError
|
|
62896
|
-
}) {
|
|
62897
|
-
const { findings, snapshots } = result;
|
|
62898
|
-
const { videoPath, videoPath2x, videoPath4x, signal } = params;
|
|
62899
|
-
if (signal?.aborted) {
|
|
62900
|
-
return (0, import_neverthrow19.okAsync)({ findings, snapshots });
|
|
62901
|
-
}
|
|
62902
|
-
return speedUpVideo({ inputPath: videoPath, factor: SPEED_2X, outputPath: videoPath2x }).mapErr(toRecordingError).andThen(
|
|
62903
|
-
() => speedUpVideo({ inputPath: videoPath, factor: SPEED_4X, outputPath: videoPath4x }).mapErr(toRecordingError).map(() => ({ findings, snapshots }))
|
|
62904
|
-
);
|
|
62905
|
-
}
|
|
62906
62861
|
function runWithRecording(handle, collectOutput) {
|
|
62907
62862
|
const toRecordingError = (cause) => ({
|
|
62908
62863
|
type: "RECORDING_FAILED",
|
|
@@ -62931,7 +62886,7 @@ function startAndRun(params) {
|
|
|
62931
62886
|
}).mapErr((error48) => {
|
|
62932
62887
|
signal?.removeEventListener("abort", onAbort);
|
|
62933
62888
|
return error48;
|
|
62934
|
-
})
|
|
62889
|
+
});
|
|
62935
62890
|
});
|
|
62936
62891
|
}
|
|
62937
62892
|
var TOOL_SELECTION_SECTION = `## Tool Selection
|
|
@@ -63405,15 +63360,13 @@ function toArtifacts(result, runPaths) {
|
|
|
63405
63360
|
return {
|
|
63406
63361
|
findings: result.findings,
|
|
63407
63362
|
videoPath: runPaths.videoPath,
|
|
63408
|
-
videoPath2x: runPaths.videoPath2x,
|
|
63409
|
-
videoPath4x: runPaths.videoPath4x,
|
|
63410
63363
|
snapshots: result.snapshots
|
|
63411
63364
|
};
|
|
63412
63365
|
}
|
|
63413
63366
|
function emitStageEnd3(safeConfig, start) {
|
|
63414
63367
|
safeConfig.onEvent?.({ type: "STAGE_END", agent: "explorer", durationMs: Date.now() - start });
|
|
63415
63368
|
}
|
|
63416
|
-
var EMPTY_RUN_PATHS = { videoPath: ""
|
|
63369
|
+
var EMPTY_RUN_PATHS = { videoPath: "" };
|
|
63417
63370
|
function collectWithoutRecording({
|
|
63418
63371
|
safeConfig,
|
|
63419
63372
|
prompt,
|
|
@@ -63438,8 +63391,6 @@ function collectAndFinalize({
|
|
|
63438
63391
|
}
|
|
63439
63392
|
return startAndRun({
|
|
63440
63393
|
videoPath: runPaths.videoPath,
|
|
63441
|
-
videoPath2x: runPaths.videoPath2x,
|
|
63442
|
-
videoPath4x: runPaths.videoPath4x,
|
|
63443
63394
|
signal: safeConfig.signal,
|
|
63444
63395
|
collectOutput: () => collectAgentOutput(prompt, safeConfig)
|
|
63445
63396
|
}).map((result) => {
|
|
@@ -67433,7 +67384,6 @@ var ALLOWED_TOOLS = [...MOBILE_IOS_TOOLS];
|
|
|
67433
67384
|
var MS_PER_SECOND4 = 1e3;
|
|
67434
67385
|
var DEFAULT_ABORT_EXIT_CODE = 130;
|
|
67435
67386
|
var IDB_INSTALL_MESSAGE = "idb is not installed. Run:\n brew install idb-companion\n pipx install fb-idb\n";
|
|
67436
|
-
var FFMPEG_INSTALL_MESSAGE = "ffmpeg is not installed. Run:\n brew install ffmpeg\n";
|
|
67437
67387
|
|
|
67438
67388
|
// src/shell/debug-logger-core.ts
|
|
67439
67389
|
var ELAPSED_PAD = 8;
|
|
@@ -67715,7 +67665,7 @@ var JSON_INDENT = 2;
|
|
|
67715
67665
|
var ITEM_ID = "analyse";
|
|
67716
67666
|
var ITEM_NAME = "analyse";
|
|
67717
67667
|
function buildArtifacts(videoPath) {
|
|
67718
|
-
return { videoPath,
|
|
67668
|
+
return { videoPath, findings: [], snapshots: [] };
|
|
67719
67669
|
}
|
|
67720
67670
|
async function checkVideoPathExists(videoPath) {
|
|
67721
67671
|
const safeAccess = (0, import_neverthrow36.fromAsyncThrowable)(import_promises17.access, () => ({ type: "FILE_NOT_FOUND" }));
|
|
@@ -67762,7 +67712,6 @@ async function runAnalysisAndExit(input) {
|
|
|
67762
67712
|
const state = { identity, startedAt };
|
|
67763
67713
|
const result = await runAnalysis(input.artifacts, {
|
|
67764
67714
|
apiKey: input.apiKey,
|
|
67765
|
-
videoVariant: "1x",
|
|
67766
67715
|
onEvent
|
|
67767
67716
|
});
|
|
67768
67717
|
result.match(
|
|
@@ -67783,7 +67732,7 @@ function ensureApiKey(config3) {
|
|
|
67783
67732
|
}
|
|
67784
67733
|
return apiKey;
|
|
67785
67734
|
}
|
|
67786
|
-
async function
|
|
67735
|
+
async function resolveVideoPath(videoPath) {
|
|
67787
67736
|
if (videoPath === void 0) {
|
|
67788
67737
|
process.stderr.write('A video path is required. Pass the path printed by "xqa explore".\n');
|
|
67789
67738
|
process.exit(1);
|
|
@@ -67802,7 +67751,7 @@ async function runAnalyseCommand(input) {
|
|
|
67802
67751
|
if (apiKey === void 0) {
|
|
67803
67752
|
return;
|
|
67804
67753
|
}
|
|
67805
|
-
const videoPath = await
|
|
67754
|
+
const videoPath = await resolveVideoPath(input.videoPath);
|
|
67806
67755
|
if (videoPath === void 0) {
|
|
67807
67756
|
return;
|
|
67808
67757
|
}
|
|
@@ -68001,35 +67950,8 @@ function readExploreContext(xqaDirectory) {
|
|
|
68001
67950
|
return readContextFile(xqaDirectory, "explore.md");
|
|
68002
67951
|
}
|
|
68003
67952
|
|
|
68004
|
-
// src/shell/ffmpeg-check.ts
|
|
68005
|
-
var import_node_child_process5 = require("node:child_process");
|
|
68006
|
-
var import_neverthrow40 = __toESM(require_index_cjs(), 1);
|
|
68007
|
-
async function whichFfmpeg() {
|
|
68008
|
-
const { promise: promise2, resolve, reject } = Promise.withResolvers();
|
|
68009
|
-
(0, import_node_child_process5.execFile)("which", ["ffmpeg"], (error48, stdout) => {
|
|
68010
|
-
if (error48) {
|
|
68011
|
-
reject(error48);
|
|
68012
|
-
} else {
|
|
68013
|
-
resolve(stdout);
|
|
68014
|
-
}
|
|
68015
|
-
});
|
|
68016
|
-
return promise2;
|
|
68017
|
-
}
|
|
68018
|
-
var safeWhichFfmpeg = (0, import_neverthrow40.fromAsyncThrowable)(
|
|
68019
|
-
whichFfmpeg,
|
|
68020
|
-
() => ({ type: "FFMPEG_NOT_FOUND" })
|
|
68021
|
-
);
|
|
68022
|
-
function checkFfmpegAvailable() {
|
|
68023
|
-
return safeWhichFfmpeg();
|
|
68024
|
-
}
|
|
68025
|
-
|
|
68026
67953
|
// src/shell/preflight.ts
|
|
68027
67954
|
async function runPreflightChecks() {
|
|
68028
|
-
const ffmpegCheck = await checkFfmpegAvailable();
|
|
68029
|
-
if (ffmpegCheck.isErr()) {
|
|
68030
|
-
process.stderr.write(FFMPEG_INSTALL_MESSAGE);
|
|
68031
|
-
return 1;
|
|
68032
|
-
}
|
|
68033
67955
|
const idbCheck = await checkIdbAvailable();
|
|
68034
67956
|
if (idbCheck.isErr()) {
|
|
68035
67957
|
process.stderr.write(IDB_INSTALL_MESSAGE);
|
|
@@ -68217,7 +68139,7 @@ var import_node_fs6 = require("node:fs");
|
|
|
68217
68139
|
var import_node_path12 = __toESM(require("node:path"), 1);
|
|
68218
68140
|
|
|
68219
68141
|
// src/commands/install-skills.ts
|
|
68220
|
-
var
|
|
68142
|
+
var import_node_child_process5 = require("node:child_process");
|
|
68221
68143
|
var import_node_fs5 = require("node:fs");
|
|
68222
68144
|
var import_node_path11 = __toESM(require("node:path"), 1);
|
|
68223
68145
|
var import_node_url = require("node:url");
|
|
@@ -68228,7 +68150,7 @@ function resolveSkillsRoot() {
|
|
|
68228
68150
|
function installSkills() {
|
|
68229
68151
|
const skillsRoot = resolveSkillsRoot();
|
|
68230
68152
|
for (const skill of (0, import_node_fs5.readdirSync)(skillsRoot)) {
|
|
68231
|
-
(0,
|
|
68153
|
+
(0, import_node_child_process5.spawnSync)("npx", ["skills", "add", import_node_path11.default.join(skillsRoot, skill), "-y"], {
|
|
68232
68154
|
stdio: "inherit"
|
|
68233
68155
|
});
|
|
68234
68156
|
}
|
|
@@ -68306,7 +68228,7 @@ function runInitCommand() {
|
|
|
68306
68228
|
// src/commands/review-command.ts
|
|
68307
68229
|
var import_node_fs7 = require("node:fs");
|
|
68308
68230
|
var import_node_path14 = __toESM(require("node:path"), 1);
|
|
68309
|
-
var
|
|
68231
|
+
var import_neverthrow41 = __toESM(require_index_cjs(), 1);
|
|
68310
68232
|
|
|
68311
68233
|
// ../../node_modules/.pnpm/@inquirer+core@10.3.2_@types+node@22.19.15/node_modules/@inquirer/core/dist/esm/lib/key.js
|
|
68312
68234
|
var isUpKey = (key, keybindings = []) => (
|
|
@@ -70711,7 +70633,7 @@ var esm_default11 = createPrompt((config3, done) => {
|
|
|
70711
70633
|
});
|
|
70712
70634
|
|
|
70713
70635
|
// src/review-session.ts
|
|
70714
|
-
var
|
|
70636
|
+
var import_neverthrow40 = __toESM(require_index_cjs(), 1);
|
|
70715
70637
|
var CONFIDENCE_PERCENT = 100;
|
|
70716
70638
|
var FLOW_COL_WIDTH = 35;
|
|
70717
70639
|
var TRIGGER_COL_WIDTH = 16;
|
|
@@ -70864,15 +70786,15 @@ async function runInteractiveLoop(findings, existing) {
|
|
|
70864
70786
|
}
|
|
70865
70787
|
return { staged: state.staged, undoneKeys: state.undoneKeys };
|
|
70866
70788
|
}
|
|
70867
|
-
var safeRunInteractiveLoop = (0,
|
|
70789
|
+
var safeRunInteractiveLoop = (0, import_neverthrow40.fromAsyncThrowable)(
|
|
70868
70790
|
runInteractiveLoop,
|
|
70869
70791
|
(error48) => error48 instanceof Error && error48.name === "ExitPromptError" ? "exit-prompt" : "unexpected"
|
|
70870
70792
|
);
|
|
70871
70793
|
|
|
70872
70794
|
// src/commands/review-command.ts
|
|
70873
|
-
var safeReadFile3 = (0,
|
|
70874
|
-
var safeParseJson3 = (0,
|
|
70875
|
-
var safeWrite = (0,
|
|
70795
|
+
var safeReadFile3 = (0, import_neverthrow41.fromThrowable)((filePath) => (0, import_node_fs7.readFileSync)(filePath, "utf8"));
|
|
70796
|
+
var safeParseJson3 = (0, import_neverthrow41.fromThrowable)(JSON.parse);
|
|
70797
|
+
var safeWrite = (0, import_neverthrow41.fromThrowable)((filePath, content) => {
|
|
70876
70798
|
(0, import_node_fs7.writeFileSync)(filePath, content);
|
|
70877
70799
|
});
|
|
70878
70800
|
function readLastPath(xqaDirectory) {
|
|
@@ -70889,13 +70811,13 @@ function isPipelineOutput(data) {
|
|
|
70889
70811
|
function readFindings(filePath) {
|
|
70890
70812
|
const readResult = safeReadFile3(filePath);
|
|
70891
70813
|
if (readResult.isErr()) {
|
|
70892
|
-
return (0,
|
|
70814
|
+
return (0, import_neverthrow41.err)("not-found");
|
|
70893
70815
|
}
|
|
70894
70816
|
return safeParseJson3(readResult.value).mapErr(() => "invalid").andThen((data) => {
|
|
70895
70817
|
if (!isPipelineOutput(data)) {
|
|
70896
|
-
return (0,
|
|
70818
|
+
return (0, import_neverthrow41.err)("invalid");
|
|
70897
70819
|
}
|
|
70898
|
-
return (0,
|
|
70820
|
+
return (0, import_neverthrow41.ok)(data);
|
|
70899
70821
|
});
|
|
70900
70822
|
}
|
|
70901
70823
|
function loadExistingDismissals(filePath) {
|
|
@@ -70968,7 +70890,7 @@ function resolveAndReadFindings(findingsPath, xqaDirectory) {
|
|
|
70968
70890
|
"No findings path provided and no last path found. Run: xqa review <findings-path>\n"
|
|
70969
70891
|
);
|
|
70970
70892
|
process.exit(1);
|
|
70971
|
-
return (0,
|
|
70893
|
+
return (0, import_neverthrow41.err)();
|
|
70972
70894
|
}
|
|
70973
70895
|
const resolvedPath = resolvedPathResult.value;
|
|
70974
70896
|
const findingsResult = readFindings(resolvedPath);
|
|
@@ -70981,9 +70903,9 @@ function resolveAndReadFindings(findingsPath, xqaDirectory) {
|
|
|
70981
70903
|
`);
|
|
70982
70904
|
}
|
|
70983
70905
|
process.exit(1);
|
|
70984
|
-
return (0,
|
|
70906
|
+
return (0, import_neverthrow41.err)();
|
|
70985
70907
|
}
|
|
70986
|
-
return (0,
|
|
70908
|
+
return (0, import_neverthrow41.ok)({ resolvedPath, output: findingsResult.value });
|
|
70987
70909
|
}
|
|
70988
70910
|
async function runReviewLoop({
|
|
70989
70911
|
findings,
|
|
@@ -71057,40 +70979,40 @@ function stripExtensions(filename) {
|
|
|
71057
70979
|
// src/commands/spec-resolver.ts
|
|
71058
70980
|
var import_node_fs8 = require("node:fs");
|
|
71059
70981
|
var import_node_path16 = __toESM(require("node:path"), 1);
|
|
71060
|
-
var
|
|
70982
|
+
var import_neverthrow43 = __toESM(require_index_cjs(), 1);
|
|
71061
70983
|
|
|
71062
70984
|
// src/spec-frontmatter.ts
|
|
71063
|
-
var
|
|
70985
|
+
var import_neverthrow42 = __toESM(require_index_cjs(), 1);
|
|
71064
70986
|
var FRONTMATTER_OPEN_LEN = 4;
|
|
71065
70987
|
var FRONTMATTER_MARKER_LEN = 3;
|
|
71066
70988
|
function extractFrontmatterBlock(content) {
|
|
71067
70989
|
const normalized = content.replaceAll("\r\n", "\n");
|
|
71068
70990
|
if (!normalized.startsWith("---")) {
|
|
71069
|
-
return (0,
|
|
70991
|
+
return (0, import_neverthrow42.err)({ type: "MISSING_FRONTMATTER" });
|
|
71070
70992
|
}
|
|
71071
70993
|
const end = normalized.indexOf("\n---", FRONTMATTER_MARKER_LEN);
|
|
71072
70994
|
if (end === -1) {
|
|
71073
|
-
return (0,
|
|
70995
|
+
return (0, import_neverthrow42.err)({ type: "MISSING_FRONTMATTER" });
|
|
71074
70996
|
}
|
|
71075
|
-
return (0,
|
|
70997
|
+
return (0, import_neverthrow42.ok)(normalized.slice(FRONTMATTER_OPEN_LEN, end));
|
|
71076
70998
|
}
|
|
71077
70999
|
function parseTimeout(fields) {
|
|
71078
71000
|
const raw = fields.get("timeout");
|
|
71079
71001
|
if (raw === void 0) {
|
|
71080
|
-
return (0,
|
|
71002
|
+
return (0, import_neverthrow42.ok)(raw);
|
|
71081
71003
|
}
|
|
71082
71004
|
const parsed = Number(raw);
|
|
71083
71005
|
if (Number.isNaN(parsed) || parsed <= 0) {
|
|
71084
|
-
return (0,
|
|
71006
|
+
return (0, import_neverthrow42.err)({ type: "PARSE_ERROR", cause: `invalid timeout: ${raw}` });
|
|
71085
71007
|
}
|
|
71086
|
-
return (0,
|
|
71008
|
+
return (0, import_neverthrow42.ok)(parsed);
|
|
71087
71009
|
}
|
|
71088
71010
|
function parseSpecFrontmatter(content) {
|
|
71089
71011
|
return extractFrontmatterBlock(content).andThen((block) => {
|
|
71090
71012
|
const fields = parseYamlFields(block);
|
|
71091
71013
|
const feature = fields.get("feature");
|
|
71092
71014
|
if (feature === void 0) {
|
|
71093
|
-
return (0,
|
|
71015
|
+
return (0, import_neverthrow42.err)({ type: "MISSING_FIELD", field: "feature" });
|
|
71094
71016
|
}
|
|
71095
71017
|
return parseTimeout(fields).map((timeout) => ({ feature, timeout }));
|
|
71096
71018
|
});
|
|
@@ -71112,12 +71034,12 @@ function parseYamlFields(block) {
|
|
|
71112
71034
|
}
|
|
71113
71035
|
|
|
71114
71036
|
// src/commands/spec-resolver.ts
|
|
71115
|
-
var safeReadFile4 = (0,
|
|
71116
|
-
var safeReaddir = (0,
|
|
71037
|
+
var safeReadFile4 = (0, import_neverthrow43.fromThrowable)((filePath) => (0, import_node_fs8.readFileSync)(filePath, "utf8"));
|
|
71038
|
+
var safeReaddir = (0, import_neverthrow43.fromThrowable)(
|
|
71117
71039
|
(directory) => (0, import_node_fs8.readdirSync)(directory, { recursive: true, encoding: "utf8" })
|
|
71118
71040
|
);
|
|
71119
71041
|
var CANCEL = "xqa:cancel";
|
|
71120
|
-
var safeSelect =
|
|
71042
|
+
var safeSelect = import_neverthrow43.ResultAsync.fromThrowable(
|
|
71121
71043
|
esm_default11,
|
|
71122
71044
|
(error48) => error48 instanceof Error && error48.name === "ExitPromptError" ? "cancelled" : "failed"
|
|
71123
71045
|
);
|
|
@@ -71307,7 +71229,7 @@ function runUpdateCommand() {
|
|
|
71307
71229
|
var import_node_path18 = __toESM(require("node:path"), 1);
|
|
71308
71230
|
var import_node_url2 = require("node:url");
|
|
71309
71231
|
var import_dotenv = __toESM(require_main(), 1);
|
|
71310
|
-
var
|
|
71232
|
+
var import_neverthrow44 = __toESM(require_index_cjs(), 1);
|
|
71311
71233
|
|
|
71312
71234
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
71313
71235
|
var external_exports2 = {};
|
|
@@ -75368,10 +75290,10 @@ function loadConfig() {
|
|
|
75368
75290
|
const messages = result.error.issues.map(
|
|
75369
75291
|
(issue2) => ` - ${issue2.path.join(".")}: ${issue2.message}`
|
|
75370
75292
|
);
|
|
75371
|
-
return (0,
|
|
75293
|
+
return (0, import_neverthrow44.err)({ type: "INVALID_CONFIG", message: `Configuration error:
|
|
75372
75294
|
${messages.join("\n")}` });
|
|
75373
75295
|
}
|
|
75374
|
-
return (0,
|
|
75296
|
+
return (0, import_neverthrow44.ok)(result.data);
|
|
75375
75297
|
}
|
|
75376
75298
|
|
|
75377
75299
|
// src/core/parse-timeout-seconds.ts
|
|
@@ -75404,7 +75326,7 @@ function parseVerboseOption(value) {
|
|
|
75404
75326
|
|
|
75405
75327
|
// src/pid-lock.ts
|
|
75406
75328
|
var import_node_fs9 = require("node:fs");
|
|
75407
|
-
var
|
|
75329
|
+
var import_neverthrow45 = __toESM(require_index_cjs(), 1);
|
|
75408
75330
|
var PID_FILE = "/tmp/xqa.pid";
|
|
75409
75331
|
var SIGINT_EXIT_CODE = 130;
|
|
75410
75332
|
var SIGTERM_EXIT_CODE = 143;
|
|
@@ -75413,7 +75335,7 @@ var HARD_TIMEOUT_MS = 1e4;
|
|
|
75413
75335
|
var cleanup = () => {
|
|
75414
75336
|
(0, import_node_fs9.rmSync)(PID_FILE, { force: true });
|
|
75415
75337
|
};
|
|
75416
|
-
var checkProcessRunning = (0,
|
|
75338
|
+
var checkProcessRunning = (0, import_neverthrow45.fromThrowable)(
|
|
75417
75339
|
(pid) => {
|
|
75418
75340
|
process.kill(pid, 0);
|
|
75419
75341
|
return true;
|
|
@@ -75479,17 +75401,17 @@ function acquireLock() {
|
|
|
75479
75401
|
// src/shell/xqa-directory.ts
|
|
75480
75402
|
var import_node_fs10 = require("node:fs");
|
|
75481
75403
|
var import_node_path19 = __toESM(require("node:path"), 1);
|
|
75482
|
-
var
|
|
75404
|
+
var import_neverthrow46 = __toESM(require_index_cjs(), 1);
|
|
75483
75405
|
function findXqaDirectory(startDirectory) {
|
|
75484
75406
|
let current = startDirectory;
|
|
75485
75407
|
for (; ; ) {
|
|
75486
75408
|
const candidate = import_node_path19.default.join(current, ".xqa");
|
|
75487
75409
|
if ((0, import_node_fs10.existsSync)(candidate)) {
|
|
75488
|
-
return (0,
|
|
75410
|
+
return (0, import_neverthrow46.ok)(candidate);
|
|
75489
75411
|
}
|
|
75490
75412
|
const parent = import_node_path19.default.dirname(current);
|
|
75491
75413
|
if (parent === current) {
|
|
75492
|
-
return (0,
|
|
75414
|
+
return (0, import_neverthrow46.err)({ type: "XQA_NOT_INITIALIZED" });
|
|
75493
75415
|
}
|
|
75494
75416
|
current = parent;
|
|
75495
75417
|
}
|
|
@@ -75499,11 +75421,11 @@ function findXqaDirectory(startDirectory) {
|
|
|
75499
75421
|
var import_promises21 = __toESM(require("node:fs/promises"), 1);
|
|
75500
75422
|
var import_node_path23 = __toESM(require("node:path"), 1);
|
|
75501
75423
|
var import_fast_glob = __toESM(require_out4(), 1);
|
|
75502
|
-
var
|
|
75424
|
+
var import_neverthrow54 = __toESM(require_index_cjs(), 1);
|
|
75503
75425
|
|
|
75504
75426
|
// src/suite/core/suite-config-parser.ts
|
|
75427
|
+
var import_neverthrow47 = __toESM(require_index_cjs(), 1);
|
|
75505
75428
|
var import_neverthrow48 = __toESM(require_index_cjs(), 1);
|
|
75506
|
-
var import_neverthrow49 = __toESM(require_index_cjs(), 1);
|
|
75507
75429
|
var RESERVED_HOOK_ENV_KEYS = [
|
|
75508
75430
|
"XQA_SIM_UDID",
|
|
75509
75431
|
"XQA_ITEM_ID",
|
|
@@ -75555,7 +75477,7 @@ var suiteConfigSchema = external_exports2.object({
|
|
|
75555
75477
|
}).refine((data) => data.specs.length > 0 || data.freestyle.length > 0, {
|
|
75556
75478
|
message: "Suite must declare at least one spec or freestyle entry"
|
|
75557
75479
|
});
|
|
75558
|
-
var safeJsonParse4 = (0,
|
|
75480
|
+
var safeJsonParse4 = (0, import_neverthrow47.fromThrowable)(
|
|
75559
75481
|
JSON.parse,
|
|
75560
75482
|
(cause) => ({
|
|
75561
75483
|
type: "INVALID_SUITE_CONFIG",
|
|
@@ -75589,16 +75511,16 @@ function parseSuiteConfig(raw) {
|
|
|
75589
75511
|
return safeJsonParse4(raw).andThen((data) => {
|
|
75590
75512
|
const parsed = suiteConfigSchema.safeParse(data);
|
|
75591
75513
|
if (!parsed.success) {
|
|
75592
|
-
return (0,
|
|
75514
|
+
return (0, import_neverthrow48.err)({ type: "INVALID_SUITE_CONFIG", cause: parsed.error });
|
|
75593
75515
|
}
|
|
75594
75516
|
const hooks = normalizeHooks(parsed.data.hooks);
|
|
75595
75517
|
if (hooks === void 0) {
|
|
75596
|
-
return (0,
|
|
75518
|
+
return (0, import_neverthrow48.ok)({
|
|
75597
75519
|
specs: parsed.data.specs,
|
|
75598
75520
|
freestyle: parsed.data.freestyle
|
|
75599
75521
|
});
|
|
75600
75522
|
}
|
|
75601
|
-
return (0,
|
|
75523
|
+
return (0, import_neverthrow48.ok)({
|
|
75602
75524
|
specs: parsed.data.specs,
|
|
75603
75525
|
freestyle: parsed.data.freestyle,
|
|
75604
75526
|
hooks
|
|
@@ -75657,7 +75579,7 @@ function buildFreestyleItems(entries) {
|
|
|
75657
75579
|
// src/suite/shell/suite-findings-writer.ts
|
|
75658
75580
|
var import_promises19 = __toESM(require("node:fs/promises"), 1);
|
|
75659
75581
|
var import_node_path20 = __toESM(require("node:path"), 1);
|
|
75660
|
-
var
|
|
75582
|
+
var import_neverthrow49 = __toESM(require_index_cjs(), 1);
|
|
75661
75583
|
var INDENT_SPACES = 2;
|
|
75662
75584
|
function writeSuiteFindings(findings, options) {
|
|
75663
75585
|
const directory = import_node_path20.default.join(
|
|
@@ -75669,7 +75591,7 @@ function writeSuiteFindings(findings, options) {
|
|
|
75669
75591
|
);
|
|
75670
75592
|
const finalPath = import_node_path20.default.join(directory, "findings.json");
|
|
75671
75593
|
const temporaryPath = `${finalPath}.tmp`;
|
|
75672
|
-
const safeWriteAtomically =
|
|
75594
|
+
const safeWriteAtomically = import_neverthrow49.ResultAsync.fromThrowable(
|
|
75673
75595
|
async () => {
|
|
75674
75596
|
await import_promises19.default.mkdir(directory, { recursive: true });
|
|
75675
75597
|
await import_promises19.default.writeFile(temporaryPath, JSON.stringify(findings, void 0, INDENT_SPACES));
|
|
@@ -75682,7 +75604,7 @@ function writeSuiteFindings(findings, options) {
|
|
|
75682
75604
|
}
|
|
75683
75605
|
|
|
75684
75606
|
// src/suite/shell/worker-pool.ts
|
|
75685
|
-
var
|
|
75607
|
+
var import_neverthrow52 = __toESM(require_index_cjs(), 1);
|
|
75686
75608
|
|
|
75687
75609
|
// src/suite/core/priority-queue.ts
|
|
75688
75610
|
var PriorityQueue = class {
|
|
@@ -75866,7 +75788,7 @@ function recordHookFailure(input) {
|
|
|
75866
75788
|
}
|
|
75867
75789
|
|
|
75868
75790
|
// src/suite/shell/hook-invoker.ts
|
|
75869
|
-
var
|
|
75791
|
+
var import_neverthrow51 = __toESM(require_index_cjs(), 1);
|
|
75870
75792
|
|
|
75871
75793
|
// src/suite/core/hook-env-builder.ts
|
|
75872
75794
|
function buildReservedKeys(input) {
|
|
@@ -75892,8 +75814,8 @@ function buildHookEnv(input) {
|
|
|
75892
75814
|
}
|
|
75893
75815
|
|
|
75894
75816
|
// src/suite/shell/hook-runner.ts
|
|
75895
|
-
var
|
|
75896
|
-
var
|
|
75817
|
+
var import_node_child_process6 = require("node:child_process");
|
|
75818
|
+
var import_neverthrow50 = __toESM(require_index_cjs(), 1);
|
|
75897
75819
|
var noop2 = () => void 0;
|
|
75898
75820
|
function cleanup2(context) {
|
|
75899
75821
|
clearTimeout(context.timeoutHandle);
|
|
@@ -75922,22 +75844,22 @@ function collectStderr(child, stderrReference) {
|
|
|
75922
75844
|
function buildOnAbort(context) {
|
|
75923
75845
|
return () => {
|
|
75924
75846
|
context.child.kill("SIGTERM");
|
|
75925
|
-
settle(context, (0,
|
|
75847
|
+
settle(context, (0, import_neverthrow50.err)({ type: "HOOK_ABORTED" }));
|
|
75926
75848
|
};
|
|
75927
75849
|
}
|
|
75928
75850
|
function attachChildListeners(context) {
|
|
75929
75851
|
const { child, stderrReference } = context;
|
|
75930
75852
|
child.on("error", (cause) => {
|
|
75931
|
-
settle(context, (0,
|
|
75853
|
+
settle(context, (0, import_neverthrow50.err)({ type: "HOOK_SPAWN_FAILED", cause }));
|
|
75932
75854
|
});
|
|
75933
75855
|
child.on("exit", (code) => {
|
|
75934
75856
|
if (code === 0) {
|
|
75935
|
-
settle(context, (0,
|
|
75857
|
+
settle(context, (0, import_neverthrow50.ok)());
|
|
75936
75858
|
return;
|
|
75937
75859
|
}
|
|
75938
75860
|
settle(
|
|
75939
75861
|
context,
|
|
75940
|
-
(0,
|
|
75862
|
+
(0, import_neverthrow50.err)({
|
|
75941
75863
|
type: "HOOK_EXIT_NONZERO",
|
|
75942
75864
|
code: code ?? -1,
|
|
75943
75865
|
stderr: stderrReference.value
|
|
@@ -75949,14 +75871,14 @@ function attachTimeout(context, timeoutMs) {
|
|
|
75949
75871
|
clearTimeout(context.timeoutHandle);
|
|
75950
75872
|
context.timeoutHandle = setTimeout(() => {
|
|
75951
75873
|
context.child.kill("SIGTERM");
|
|
75952
|
-
settle(context, (0,
|
|
75874
|
+
settle(context, (0, import_neverthrow50.err)({ type: "HOOK_TIMEOUT", timeoutMs }));
|
|
75953
75875
|
}, timeoutMs);
|
|
75954
75876
|
}
|
|
75955
75877
|
function buildContext2(options) {
|
|
75956
75878
|
const { script, cwd, env: env2, baseEnv, nodeExecPath } = options;
|
|
75957
75879
|
const deferred = makeDeferred();
|
|
75958
75880
|
const stderrReference = { value: "" };
|
|
75959
|
-
const child = (0,
|
|
75881
|
+
const child = (0, import_node_child_process6.spawn)(nodeExecPath, [script], {
|
|
75960
75882
|
cwd,
|
|
75961
75883
|
env: { ...baseEnv, ...env2 },
|
|
75962
75884
|
stdio: ["ignore", "inherit", "pipe"]
|
|
@@ -75984,7 +75906,7 @@ async function spawnHook(options) {
|
|
|
75984
75906
|
attachChildListeners(context);
|
|
75985
75907
|
return context.deferred.promise;
|
|
75986
75908
|
}
|
|
75987
|
-
var safeSpawn =
|
|
75909
|
+
var safeSpawn = import_neverthrow50.ResultAsync.fromThrowable(
|
|
75988
75910
|
spawnHook,
|
|
75989
75911
|
(cause) => ({ type: "HOOK_SPAWN_FAILED", cause })
|
|
75990
75912
|
);
|
|
@@ -76012,7 +75934,7 @@ async function invokeHook(input) {
|
|
|
76012
75934
|
async function maybeInvokeHook(input) {
|
|
76013
75935
|
const { hook } = input;
|
|
76014
75936
|
if (hook === void 0) {
|
|
76015
|
-
return (0,
|
|
75937
|
+
return (0, import_neverthrow51.ok)();
|
|
76016
75938
|
}
|
|
76017
75939
|
return invokeHook({ ...input, hook });
|
|
76018
75940
|
}
|
|
@@ -76151,7 +76073,7 @@ function runWorkerPool(config3) {
|
|
|
76151
76073
|
const queue = setupQueue(config3);
|
|
76152
76074
|
const results = [];
|
|
76153
76075
|
const suiteStartMs = Date.now();
|
|
76154
|
-
const safeRun =
|
|
76076
|
+
const safeRun = import_neverthrow52.ResultAsync.fromThrowable(
|
|
76155
76077
|
async () => runAllWorkers({ config: config3, queue, results, suiteStartMs }),
|
|
76156
76078
|
(cause) => ({ type: "WORKER_POOL_FAILED", cause })
|
|
76157
76079
|
);
|
|
@@ -76180,29 +76102,31 @@ function resolveFreestyleTimeout(item, config3) {
|
|
|
76180
76102
|
}
|
|
76181
76103
|
return config3.QA_EXPLORE_TIMEOUT_SECONDS ?? DEFAULT_FREESTYLE_TIMEOUT_SECONDS2;
|
|
76182
76104
|
}
|
|
76105
|
+
function buildFreestyleExplorerConfig(input) {
|
|
76106
|
+
const { item, context, simulatorUdid } = input;
|
|
76107
|
+
const { config: config3, date: date5, appContext } = context;
|
|
76108
|
+
return {
|
|
76109
|
+
mode: "freestyle",
|
|
76110
|
+
date: date5,
|
|
76111
|
+
mcpServers: createDefaultMcpServers(simulatorUdid),
|
|
76112
|
+
allowedTools: ALLOWED_TOOLS,
|
|
76113
|
+
timeoutMs: resolveFreestyleTimeout(item, config3) * MS_PER_SECOND4,
|
|
76114
|
+
appContext: composeAppContext([buildDeviceInstruction(simulatorUdid), appContext, item.prompt]),
|
|
76115
|
+
buildEnv: config3.QA_BUILD_ENV,
|
|
76116
|
+
cwd: ensureWorkerCwd(simulatorUdid),
|
|
76117
|
+
record: true
|
|
76118
|
+
};
|
|
76119
|
+
}
|
|
76183
76120
|
function buildFreestylePipelineConfig(input) {
|
|
76184
76121
|
const { item, context, signal, simulatorUdid, onEvent } = input;
|
|
76185
|
-
const {
|
|
76122
|
+
const { xqaDirectory, runId } = context;
|
|
76186
76123
|
return {
|
|
76187
76124
|
outputDir: import_node_path21.default.join(xqaDirectory, "output", item.id),
|
|
76188
76125
|
runId,
|
|
76189
76126
|
simulatorUdid,
|
|
76190
76127
|
signal,
|
|
76191
76128
|
onEvent,
|
|
76192
|
-
explorer:
|
|
76193
|
-
mode: "freestyle",
|
|
76194
|
-
date: date5,
|
|
76195
|
-
mcpServers: createDefaultMcpServers(simulatorUdid),
|
|
76196
|
-
allowedTools: ALLOWED_TOOLS,
|
|
76197
|
-
timeoutMs: resolveFreestyleTimeout(item, config3) * MS_PER_SECOND4,
|
|
76198
|
-
appContext: composeAppContext([
|
|
76199
|
-
buildDeviceInstruction(simulatorUdid),
|
|
76200
|
-
appContext,
|
|
76201
|
-
item.prompt
|
|
76202
|
-
]),
|
|
76203
|
-
buildEnv: config3.QA_BUILD_ENV,
|
|
76204
|
-
cwd: ensureWorkerCwd(simulatorUdid)
|
|
76205
|
-
}
|
|
76129
|
+
explorer: buildFreestyleExplorerConfig(input)
|
|
76206
76130
|
};
|
|
76207
76131
|
}
|
|
76208
76132
|
function executeFreestyleItem(input) {
|
|
@@ -76224,7 +76148,8 @@ function buildSpecPipelineConfig(input) {
|
|
|
76224
76148
|
allowedTools: ALLOWED_TOOLS,
|
|
76225
76149
|
appContext: composeAppContext([buildDeviceInstruction(simulatorUdid), appContext]),
|
|
76226
76150
|
buildEnv: config3.QA_BUILD_ENV,
|
|
76227
|
-
cwd: ensureWorkerCwd(simulatorUdid)
|
|
76151
|
+
cwd: ensureWorkerCwd(simulatorUdid),
|
|
76152
|
+
record: true
|
|
76228
76153
|
}
|
|
76229
76154
|
};
|
|
76230
76155
|
}
|
|
@@ -76244,7 +76169,7 @@ function makeExecuteItem(context) {
|
|
|
76244
76169
|
// src/suite/commands/suite-run-context.ts
|
|
76245
76170
|
var import_promises20 = __toESM(require("node:fs/promises"), 1);
|
|
76246
76171
|
var import_node_path22 = __toESM(require("node:path"), 1);
|
|
76247
|
-
var
|
|
76172
|
+
var import_neverthrow53 = __toESM(require_index_cjs(), 1);
|
|
76248
76173
|
|
|
76249
76174
|
// src/suite/core/run-id.ts
|
|
76250
76175
|
var RUN_ID_PAD_LENGTH2 = 4;
|
|
@@ -76279,7 +76204,7 @@ function deriveSuiteId(input) {
|
|
|
76279
76204
|
|
|
76280
76205
|
// src/suite/commands/suite-run-context.ts
|
|
76281
76206
|
var ISO_DATE_LENGTH3 = 10;
|
|
76282
|
-
var safeReaddir2 =
|
|
76207
|
+
var safeReaddir2 = import_neverthrow53.ResultAsync.fromThrowable(
|
|
76283
76208
|
async (directoryPath) => {
|
|
76284
76209
|
const entries = await import_promises20.default.readdir(directoryPath, { withFileTypes: true });
|
|
76285
76210
|
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
@@ -76327,7 +76252,7 @@ async function buildSuiteRunContext(input) {
|
|
|
76327
76252
|
}
|
|
76328
76253
|
|
|
76329
76254
|
// src/suite/commands/run-command.ts
|
|
76330
|
-
var safeReadFile5 =
|
|
76255
|
+
var safeReadFile5 = import_neverthrow54.ResultAsync.fromThrowable(
|
|
76331
76256
|
async (filePath) => import_promises21.default.readFile(filePath, "utf8"),
|
|
76332
76257
|
() => "READ_FAILED"
|
|
76333
76258
|
);
|
|
@@ -76515,7 +76440,7 @@ function resolveXqaDirectory() {
|
|
|
76515
76440
|
return result.value;
|
|
76516
76441
|
}
|
|
76517
76442
|
var program2 = new Command();
|
|
76518
|
-
program2.name("xqa").description("AI-powered QA agent CLI").version(`${"1.
|
|
76443
|
+
program2.name("xqa").description("AI-powered QA agent CLI").version(`${"1.15.0"}${false ? ` (dev build +${"befb4fb"})` : ""}`);
|
|
76519
76444
|
program2.command("init").description("Initialize a new xqa project in the current directory").action(() => {
|
|
76520
76445
|
runInitCommand();
|
|
76521
76446
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/xqa",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22"
|
|
@@ -26,12 +26,12 @@
|
|
|
26
26
|
"typescript": "^5.8.3",
|
|
27
27
|
"vitest": "^3.2.1",
|
|
28
28
|
"zod": "^3.0.0",
|
|
29
|
-
"@qa-agents/explorer": "0.0.0",
|
|
30
|
-
"@qa-agents/mobile-ios": "0.0.0",
|
|
31
|
-
"@qa-agents/pipeline": "0.0.0",
|
|
32
29
|
"@qa-agents/display": "0.0.0",
|
|
33
30
|
"@qa-agents/eslint-config": "0.0.0",
|
|
31
|
+
"@qa-agents/explorer": "0.0.0",
|
|
32
|
+
"@qa-agents/mobile-ios": "0.0.0",
|
|
34
33
|
"@qa-agents/shared": "0.0.0",
|
|
34
|
+
"@qa-agents/pipeline": "0.0.0",
|
|
35
35
|
"@qa-agents/typescript-config": "0.0.0"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|