@exodus/xqa 1.14.2 → 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 +0 -1
- package/dist/xqa.cjs +88 -171
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -301,7 +301,6 @@ src/
|
|
|
301
301
|
app-context.ts # Read app.md and explore.md
|
|
302
302
|
xqa-directory.ts # Locate .xqa directory
|
|
303
303
|
preflight.ts # Environment preflight checks
|
|
304
|
-
ffmpeg-check.ts # ffmpeg availability probe
|
|
305
304
|
display-factory.ts # Solo and suite display factories
|
|
306
305
|
debug-logger.ts # Debug event logging
|
|
307
306
|
debug-agent-events.ts # Agent event debug formatter
|
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;
|
|
@@ -52863,8 +52863,6 @@ function resolveRunPaths({ outputDirectory, runId, date: date5 }) {
|
|
|
52863
52863
|
return (0, import_neverthrow8.ok)({
|
|
52864
52864
|
baseDir: baseDirectory,
|
|
52865
52865
|
videoPath: import_node_path2.default.join(baseDirectory, "recording.mp4"),
|
|
52866
|
-
videoPath2x: import_node_path2.default.join(baseDirectory, "recording_2x.mp4"),
|
|
52867
|
-
videoPath4x: import_node_path2.default.join(baseDirectory, "recording_4x.mp4"),
|
|
52868
52866
|
findingsPath: import_node_path2.default.join(baseDirectory, "findings.json"),
|
|
52869
52867
|
screenshotsDir: import_node_path2.default.join(baseDirectory, "screenshots")
|
|
52870
52868
|
});
|
|
@@ -56123,24 +56121,13 @@ function makePollTick(onEvent) {
|
|
|
56123
56121
|
});
|
|
56124
56122
|
};
|
|
56125
56123
|
}
|
|
56126
|
-
function resolveVideoPath(artifacts, variant) {
|
|
56127
|
-
if (variant === "2x") {
|
|
56128
|
-
return artifacts.videoPath2x;
|
|
56129
|
-
}
|
|
56130
|
-
if (variant === "4x") {
|
|
56131
|
-
return artifacts.videoPath4x;
|
|
56132
|
-
}
|
|
56133
|
-
return artifacts.videoPath;
|
|
56134
|
-
}
|
|
56135
56124
|
function buildRunContext(artifacts, config3) {
|
|
56136
|
-
const variant = config3?.videoVariant ?? "4x";
|
|
56137
56125
|
return {
|
|
56138
56126
|
onEvent: config3?.onEvent === void 0 ? void 0 : safeHandler(config3.onEvent),
|
|
56139
56127
|
apiKey: config3?.apiKey ?? "",
|
|
56140
56128
|
model: config3?.model ?? DEFAULT_MODEL,
|
|
56141
|
-
variant,
|
|
56142
56129
|
start: Date.now(),
|
|
56143
|
-
videoPath:
|
|
56130
|
+
videoPath: artifacts.videoPath,
|
|
56144
56131
|
prompt: buildAnalyserPrompt(artifacts.snapshots)
|
|
56145
56132
|
};
|
|
56146
56133
|
}
|
|
@@ -56170,11 +56157,11 @@ function runUploadPipeline(context) {
|
|
|
56170
56157
|
}
|
|
56171
56158
|
function runAnalyser(artifacts, config3) {
|
|
56172
56159
|
const context = buildRunContext(artifacts, config3);
|
|
56173
|
-
const { onEvent,
|
|
56160
|
+
const { onEvent, start, videoPath } = context;
|
|
56174
56161
|
onEvent?.({ type: "STAGE_START", agent: "analyser" });
|
|
56175
56162
|
if (!videoPath) {
|
|
56176
56163
|
emitStageEnd(onEvent, start);
|
|
56177
|
-
return (0, import_neverthrow10.errAsync)({ type: "VIDEO_PATH_MISSING"
|
|
56164
|
+
return (0, import_neverthrow10.errAsync)({ type: "VIDEO_PATH_MISSING" });
|
|
56178
56165
|
}
|
|
56179
56166
|
return runUploadPipeline(context);
|
|
56180
56167
|
}
|
|
@@ -62798,17 +62785,6 @@ function runQuery(prompt, config3) {
|
|
|
62798
62785
|
function collectAgentOutput(prompt, config3) {
|
|
62799
62786
|
return runQuery(prompt, config3).mapErr((cause) => ({ type: "QUERY_FAILED", cause }));
|
|
62800
62787
|
}
|
|
62801
|
-
async function runFfmpeg(arguments_) {
|
|
62802
|
-
const { promise: promise2, resolve, reject } = Promise.withResolvers();
|
|
62803
|
-
(0, import_node_child_process4.execFile)("ffmpeg", arguments_, (error48) => {
|
|
62804
|
-
if (error48) {
|
|
62805
|
-
reject(error48);
|
|
62806
|
-
} else {
|
|
62807
|
-
resolve(true);
|
|
62808
|
-
}
|
|
62809
|
-
});
|
|
62810
|
-
await promise2;
|
|
62811
|
-
}
|
|
62812
62788
|
function spawnRecorder(outputPath) {
|
|
62813
62789
|
const safeMkdirSync = (0, import_neverthrow20.fromThrowable)(import_node_fs.mkdirSync, (cause) => cause);
|
|
62814
62790
|
const safeSpawn2 = (0, import_neverthrow20.fromThrowable)(
|
|
@@ -62882,35 +62858,6 @@ function stopRecording(handle) {
|
|
|
62882
62858
|
(cause) => ({ type: "STOP_FAILED", cause })
|
|
62883
62859
|
);
|
|
62884
62860
|
}
|
|
62885
|
-
function speedUpVideo({
|
|
62886
|
-
inputPath,
|
|
62887
|
-
factor,
|
|
62888
|
-
outputPath
|
|
62889
|
-
}) {
|
|
62890
|
-
const pts = String(1 / factor);
|
|
62891
|
-
return (0, import_neverthrow20.fromAsyncThrowable)(
|
|
62892
|
-
runFfmpeg,
|
|
62893
|
-
(cause) => ({ type: "PROCESS_FAILED", cause })
|
|
62894
|
-
)(["-i", inputPath, "-filter:v", `setpts=${pts}*PTS`, "-an", "-y", outputPath]).map(
|
|
62895
|
-
() => outputPath
|
|
62896
|
-
);
|
|
62897
|
-
}
|
|
62898
|
-
var SPEED_2X = 2;
|
|
62899
|
-
var SPEED_4X = 4;
|
|
62900
|
-
function applySpeedUpVariants({
|
|
62901
|
-
result,
|
|
62902
|
-
params,
|
|
62903
|
-
toRecordingError
|
|
62904
|
-
}) {
|
|
62905
|
-
const { findings, snapshots } = result;
|
|
62906
|
-
const { videoPath, videoPath2x, videoPath4x, signal } = params;
|
|
62907
|
-
if (signal?.aborted) {
|
|
62908
|
-
return (0, import_neverthrow19.okAsync)({ findings, snapshots });
|
|
62909
|
-
}
|
|
62910
|
-
return speedUpVideo({ inputPath: videoPath, factor: SPEED_2X, outputPath: videoPath2x }).mapErr(toRecordingError).andThen(
|
|
62911
|
-
() => speedUpVideo({ inputPath: videoPath, factor: SPEED_4X, outputPath: videoPath4x }).mapErr(toRecordingError).map(() => ({ findings, snapshots }))
|
|
62912
|
-
);
|
|
62913
|
-
}
|
|
62914
62861
|
function runWithRecording(handle, collectOutput) {
|
|
62915
62862
|
const toRecordingError = (cause) => ({
|
|
62916
62863
|
type: "RECORDING_FAILED",
|
|
@@ -62939,7 +62886,7 @@ function startAndRun(params) {
|
|
|
62939
62886
|
}).mapErr((error48) => {
|
|
62940
62887
|
signal?.removeEventListener("abort", onAbort);
|
|
62941
62888
|
return error48;
|
|
62942
|
-
})
|
|
62889
|
+
});
|
|
62943
62890
|
});
|
|
62944
62891
|
}
|
|
62945
62892
|
var TOOL_SELECTION_SECTION = `## Tool Selection
|
|
@@ -63413,15 +63360,13 @@ function toArtifacts(result, runPaths) {
|
|
|
63413
63360
|
return {
|
|
63414
63361
|
findings: result.findings,
|
|
63415
63362
|
videoPath: runPaths.videoPath,
|
|
63416
|
-
videoPath2x: runPaths.videoPath2x,
|
|
63417
|
-
videoPath4x: runPaths.videoPath4x,
|
|
63418
63363
|
snapshots: result.snapshots
|
|
63419
63364
|
};
|
|
63420
63365
|
}
|
|
63421
63366
|
function emitStageEnd3(safeConfig, start) {
|
|
63422
63367
|
safeConfig.onEvent?.({ type: "STAGE_END", agent: "explorer", durationMs: Date.now() - start });
|
|
63423
63368
|
}
|
|
63424
|
-
var EMPTY_RUN_PATHS = { videoPath: ""
|
|
63369
|
+
var EMPTY_RUN_PATHS = { videoPath: "" };
|
|
63425
63370
|
function collectWithoutRecording({
|
|
63426
63371
|
safeConfig,
|
|
63427
63372
|
prompt,
|
|
@@ -63446,8 +63391,6 @@ function collectAndFinalize({
|
|
|
63446
63391
|
}
|
|
63447
63392
|
return startAndRun({
|
|
63448
63393
|
videoPath: runPaths.videoPath,
|
|
63449
|
-
videoPath2x: runPaths.videoPath2x,
|
|
63450
|
-
videoPath4x: runPaths.videoPath4x,
|
|
63451
63394
|
signal: safeConfig.signal,
|
|
63452
63395
|
collectOutput: () => collectAgentOutput(prompt, safeConfig)
|
|
63453
63396
|
}).map((result) => {
|
|
@@ -67441,7 +67384,6 @@ var ALLOWED_TOOLS = [...MOBILE_IOS_TOOLS];
|
|
|
67441
67384
|
var MS_PER_SECOND4 = 1e3;
|
|
67442
67385
|
var DEFAULT_ABORT_EXIT_CODE = 130;
|
|
67443
67386
|
var IDB_INSTALL_MESSAGE = "idb is not installed. Run:\n brew install idb-companion\n pipx install fb-idb\n";
|
|
67444
|
-
var FFMPEG_INSTALL_MESSAGE = "ffmpeg is not installed. Run:\n brew install ffmpeg\n";
|
|
67445
67387
|
|
|
67446
67388
|
// src/shell/debug-logger-core.ts
|
|
67447
67389
|
var ELAPSED_PAD = 8;
|
|
@@ -67723,7 +67665,7 @@ var JSON_INDENT = 2;
|
|
|
67723
67665
|
var ITEM_ID = "analyse";
|
|
67724
67666
|
var ITEM_NAME = "analyse";
|
|
67725
67667
|
function buildArtifacts(videoPath) {
|
|
67726
|
-
return { videoPath,
|
|
67668
|
+
return { videoPath, findings: [], snapshots: [] };
|
|
67727
67669
|
}
|
|
67728
67670
|
async function checkVideoPathExists(videoPath) {
|
|
67729
67671
|
const safeAccess = (0, import_neverthrow36.fromAsyncThrowable)(import_promises17.access, () => ({ type: "FILE_NOT_FOUND" }));
|
|
@@ -67770,7 +67712,6 @@ async function runAnalysisAndExit(input) {
|
|
|
67770
67712
|
const state = { identity, startedAt };
|
|
67771
67713
|
const result = await runAnalysis(input.artifacts, {
|
|
67772
67714
|
apiKey: input.apiKey,
|
|
67773
|
-
videoVariant: "1x",
|
|
67774
67715
|
onEvent
|
|
67775
67716
|
});
|
|
67776
67717
|
result.match(
|
|
@@ -67791,7 +67732,7 @@ function ensureApiKey(config3) {
|
|
|
67791
67732
|
}
|
|
67792
67733
|
return apiKey;
|
|
67793
67734
|
}
|
|
67794
|
-
async function
|
|
67735
|
+
async function resolveVideoPath(videoPath) {
|
|
67795
67736
|
if (videoPath === void 0) {
|
|
67796
67737
|
process.stderr.write('A video path is required. Pass the path printed by "xqa explore".\n');
|
|
67797
67738
|
process.exit(1);
|
|
@@ -67810,7 +67751,7 @@ async function runAnalyseCommand(input) {
|
|
|
67810
67751
|
if (apiKey === void 0) {
|
|
67811
67752
|
return;
|
|
67812
67753
|
}
|
|
67813
|
-
const videoPath = await
|
|
67754
|
+
const videoPath = await resolveVideoPath(input.videoPath);
|
|
67814
67755
|
if (videoPath === void 0) {
|
|
67815
67756
|
return;
|
|
67816
67757
|
}
|
|
@@ -68009,35 +67950,8 @@ function readExploreContext(xqaDirectory) {
|
|
|
68009
67950
|
return readContextFile(xqaDirectory, "explore.md");
|
|
68010
67951
|
}
|
|
68011
67952
|
|
|
68012
|
-
// src/shell/ffmpeg-check.ts
|
|
68013
|
-
var import_node_child_process5 = require("node:child_process");
|
|
68014
|
-
var import_neverthrow40 = __toESM(require_index_cjs(), 1);
|
|
68015
|
-
async function whichFfmpeg() {
|
|
68016
|
-
const { promise: promise2, resolve, reject } = Promise.withResolvers();
|
|
68017
|
-
(0, import_node_child_process5.execFile)("which", ["ffmpeg"], (error48, stdout) => {
|
|
68018
|
-
if (error48) {
|
|
68019
|
-
reject(error48);
|
|
68020
|
-
} else {
|
|
68021
|
-
resolve(stdout);
|
|
68022
|
-
}
|
|
68023
|
-
});
|
|
68024
|
-
return promise2;
|
|
68025
|
-
}
|
|
68026
|
-
var safeWhichFfmpeg = (0, import_neverthrow40.fromAsyncThrowable)(
|
|
68027
|
-
whichFfmpeg,
|
|
68028
|
-
() => ({ type: "FFMPEG_NOT_FOUND" })
|
|
68029
|
-
);
|
|
68030
|
-
function checkFfmpegAvailable() {
|
|
68031
|
-
return safeWhichFfmpeg();
|
|
68032
|
-
}
|
|
68033
|
-
|
|
68034
67953
|
// src/shell/preflight.ts
|
|
68035
67954
|
async function runPreflightChecks() {
|
|
68036
|
-
const ffmpegCheck = await checkFfmpegAvailable();
|
|
68037
|
-
if (ffmpegCheck.isErr()) {
|
|
68038
|
-
process.stderr.write(FFMPEG_INSTALL_MESSAGE);
|
|
68039
|
-
return 1;
|
|
68040
|
-
}
|
|
68041
67955
|
const idbCheck = await checkIdbAvailable();
|
|
68042
67956
|
if (idbCheck.isErr()) {
|
|
68043
67957
|
process.stderr.write(IDB_INSTALL_MESSAGE);
|
|
@@ -68225,7 +68139,7 @@ var import_node_fs6 = require("node:fs");
|
|
|
68225
68139
|
var import_node_path12 = __toESM(require("node:path"), 1);
|
|
68226
68140
|
|
|
68227
68141
|
// src/commands/install-skills.ts
|
|
68228
|
-
var
|
|
68142
|
+
var import_node_child_process5 = require("node:child_process");
|
|
68229
68143
|
var import_node_fs5 = require("node:fs");
|
|
68230
68144
|
var import_node_path11 = __toESM(require("node:path"), 1);
|
|
68231
68145
|
var import_node_url = require("node:url");
|
|
@@ -68236,7 +68150,7 @@ function resolveSkillsRoot() {
|
|
|
68236
68150
|
function installSkills() {
|
|
68237
68151
|
const skillsRoot = resolveSkillsRoot();
|
|
68238
68152
|
for (const skill of (0, import_node_fs5.readdirSync)(skillsRoot)) {
|
|
68239
|
-
(0,
|
|
68153
|
+
(0, import_node_child_process5.spawnSync)("npx", ["skills", "add", import_node_path11.default.join(skillsRoot, skill), "-y"], {
|
|
68240
68154
|
stdio: "inherit"
|
|
68241
68155
|
});
|
|
68242
68156
|
}
|
|
@@ -68314,7 +68228,7 @@ function runInitCommand() {
|
|
|
68314
68228
|
// src/commands/review-command.ts
|
|
68315
68229
|
var import_node_fs7 = require("node:fs");
|
|
68316
68230
|
var import_node_path14 = __toESM(require("node:path"), 1);
|
|
68317
|
-
var
|
|
68231
|
+
var import_neverthrow41 = __toESM(require_index_cjs(), 1);
|
|
68318
68232
|
|
|
68319
68233
|
// ../../node_modules/.pnpm/@inquirer+core@10.3.2_@types+node@22.19.15/node_modules/@inquirer/core/dist/esm/lib/key.js
|
|
68320
68234
|
var isUpKey = (key, keybindings = []) => (
|
|
@@ -70719,7 +70633,7 @@ var esm_default11 = createPrompt((config3, done) => {
|
|
|
70719
70633
|
});
|
|
70720
70634
|
|
|
70721
70635
|
// src/review-session.ts
|
|
70722
|
-
var
|
|
70636
|
+
var import_neverthrow40 = __toESM(require_index_cjs(), 1);
|
|
70723
70637
|
var CONFIDENCE_PERCENT = 100;
|
|
70724
70638
|
var FLOW_COL_WIDTH = 35;
|
|
70725
70639
|
var TRIGGER_COL_WIDTH = 16;
|
|
@@ -70872,15 +70786,15 @@ async function runInteractiveLoop(findings, existing) {
|
|
|
70872
70786
|
}
|
|
70873
70787
|
return { staged: state.staged, undoneKeys: state.undoneKeys };
|
|
70874
70788
|
}
|
|
70875
|
-
var safeRunInteractiveLoop = (0,
|
|
70789
|
+
var safeRunInteractiveLoop = (0, import_neverthrow40.fromAsyncThrowable)(
|
|
70876
70790
|
runInteractiveLoop,
|
|
70877
70791
|
(error48) => error48 instanceof Error && error48.name === "ExitPromptError" ? "exit-prompt" : "unexpected"
|
|
70878
70792
|
);
|
|
70879
70793
|
|
|
70880
70794
|
// src/commands/review-command.ts
|
|
70881
|
-
var safeReadFile3 = (0,
|
|
70882
|
-
var safeParseJson3 = (0,
|
|
70883
|
-
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) => {
|
|
70884
70798
|
(0, import_node_fs7.writeFileSync)(filePath, content);
|
|
70885
70799
|
});
|
|
70886
70800
|
function readLastPath(xqaDirectory) {
|
|
@@ -70897,13 +70811,13 @@ function isPipelineOutput(data) {
|
|
|
70897
70811
|
function readFindings(filePath) {
|
|
70898
70812
|
const readResult = safeReadFile3(filePath);
|
|
70899
70813
|
if (readResult.isErr()) {
|
|
70900
|
-
return (0,
|
|
70814
|
+
return (0, import_neverthrow41.err)("not-found");
|
|
70901
70815
|
}
|
|
70902
70816
|
return safeParseJson3(readResult.value).mapErr(() => "invalid").andThen((data) => {
|
|
70903
70817
|
if (!isPipelineOutput(data)) {
|
|
70904
|
-
return (0,
|
|
70818
|
+
return (0, import_neverthrow41.err)("invalid");
|
|
70905
70819
|
}
|
|
70906
|
-
return (0,
|
|
70820
|
+
return (0, import_neverthrow41.ok)(data);
|
|
70907
70821
|
});
|
|
70908
70822
|
}
|
|
70909
70823
|
function loadExistingDismissals(filePath) {
|
|
@@ -70976,7 +70890,7 @@ function resolveAndReadFindings(findingsPath, xqaDirectory) {
|
|
|
70976
70890
|
"No findings path provided and no last path found. Run: xqa review <findings-path>\n"
|
|
70977
70891
|
);
|
|
70978
70892
|
process.exit(1);
|
|
70979
|
-
return (0,
|
|
70893
|
+
return (0, import_neverthrow41.err)();
|
|
70980
70894
|
}
|
|
70981
70895
|
const resolvedPath = resolvedPathResult.value;
|
|
70982
70896
|
const findingsResult = readFindings(resolvedPath);
|
|
@@ -70989,9 +70903,9 @@ function resolveAndReadFindings(findingsPath, xqaDirectory) {
|
|
|
70989
70903
|
`);
|
|
70990
70904
|
}
|
|
70991
70905
|
process.exit(1);
|
|
70992
|
-
return (0,
|
|
70906
|
+
return (0, import_neverthrow41.err)();
|
|
70993
70907
|
}
|
|
70994
|
-
return (0,
|
|
70908
|
+
return (0, import_neverthrow41.ok)({ resolvedPath, output: findingsResult.value });
|
|
70995
70909
|
}
|
|
70996
70910
|
async function runReviewLoop({
|
|
70997
70911
|
findings,
|
|
@@ -71065,40 +70979,40 @@ function stripExtensions(filename) {
|
|
|
71065
70979
|
// src/commands/spec-resolver.ts
|
|
71066
70980
|
var import_node_fs8 = require("node:fs");
|
|
71067
70981
|
var import_node_path16 = __toESM(require("node:path"), 1);
|
|
71068
|
-
var
|
|
70982
|
+
var import_neverthrow43 = __toESM(require_index_cjs(), 1);
|
|
71069
70983
|
|
|
71070
70984
|
// src/spec-frontmatter.ts
|
|
71071
|
-
var
|
|
70985
|
+
var import_neverthrow42 = __toESM(require_index_cjs(), 1);
|
|
71072
70986
|
var FRONTMATTER_OPEN_LEN = 4;
|
|
71073
70987
|
var FRONTMATTER_MARKER_LEN = 3;
|
|
71074
70988
|
function extractFrontmatterBlock(content) {
|
|
71075
70989
|
const normalized = content.replaceAll("\r\n", "\n");
|
|
71076
70990
|
if (!normalized.startsWith("---")) {
|
|
71077
|
-
return (0,
|
|
70991
|
+
return (0, import_neverthrow42.err)({ type: "MISSING_FRONTMATTER" });
|
|
71078
70992
|
}
|
|
71079
70993
|
const end = normalized.indexOf("\n---", FRONTMATTER_MARKER_LEN);
|
|
71080
70994
|
if (end === -1) {
|
|
71081
|
-
return (0,
|
|
70995
|
+
return (0, import_neverthrow42.err)({ type: "MISSING_FRONTMATTER" });
|
|
71082
70996
|
}
|
|
71083
|
-
return (0,
|
|
70997
|
+
return (0, import_neverthrow42.ok)(normalized.slice(FRONTMATTER_OPEN_LEN, end));
|
|
71084
70998
|
}
|
|
71085
70999
|
function parseTimeout(fields) {
|
|
71086
71000
|
const raw = fields.get("timeout");
|
|
71087
71001
|
if (raw === void 0) {
|
|
71088
|
-
return (0,
|
|
71002
|
+
return (0, import_neverthrow42.ok)(raw);
|
|
71089
71003
|
}
|
|
71090
71004
|
const parsed = Number(raw);
|
|
71091
71005
|
if (Number.isNaN(parsed) || parsed <= 0) {
|
|
71092
|
-
return (0,
|
|
71006
|
+
return (0, import_neverthrow42.err)({ type: "PARSE_ERROR", cause: `invalid timeout: ${raw}` });
|
|
71093
71007
|
}
|
|
71094
|
-
return (0,
|
|
71008
|
+
return (0, import_neverthrow42.ok)(parsed);
|
|
71095
71009
|
}
|
|
71096
71010
|
function parseSpecFrontmatter(content) {
|
|
71097
71011
|
return extractFrontmatterBlock(content).andThen((block) => {
|
|
71098
71012
|
const fields = parseYamlFields(block);
|
|
71099
71013
|
const feature = fields.get("feature");
|
|
71100
71014
|
if (feature === void 0) {
|
|
71101
|
-
return (0,
|
|
71015
|
+
return (0, import_neverthrow42.err)({ type: "MISSING_FIELD", field: "feature" });
|
|
71102
71016
|
}
|
|
71103
71017
|
return parseTimeout(fields).map((timeout) => ({ feature, timeout }));
|
|
71104
71018
|
});
|
|
@@ -71120,12 +71034,12 @@ function parseYamlFields(block) {
|
|
|
71120
71034
|
}
|
|
71121
71035
|
|
|
71122
71036
|
// src/commands/spec-resolver.ts
|
|
71123
|
-
var safeReadFile4 = (0,
|
|
71124
|
-
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)(
|
|
71125
71039
|
(directory) => (0, import_node_fs8.readdirSync)(directory, { recursive: true, encoding: "utf8" })
|
|
71126
71040
|
);
|
|
71127
71041
|
var CANCEL = "xqa:cancel";
|
|
71128
|
-
var safeSelect =
|
|
71042
|
+
var safeSelect = import_neverthrow43.ResultAsync.fromThrowable(
|
|
71129
71043
|
esm_default11,
|
|
71130
71044
|
(error48) => error48 instanceof Error && error48.name === "ExitPromptError" ? "cancelled" : "failed"
|
|
71131
71045
|
);
|
|
@@ -71315,7 +71229,7 @@ function runUpdateCommand() {
|
|
|
71315
71229
|
var import_node_path18 = __toESM(require("node:path"), 1);
|
|
71316
71230
|
var import_node_url2 = require("node:url");
|
|
71317
71231
|
var import_dotenv = __toESM(require_main(), 1);
|
|
71318
|
-
var
|
|
71232
|
+
var import_neverthrow44 = __toESM(require_index_cjs(), 1);
|
|
71319
71233
|
|
|
71320
71234
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
71321
71235
|
var external_exports2 = {};
|
|
@@ -75376,10 +75290,10 @@ function loadConfig() {
|
|
|
75376
75290
|
const messages = result.error.issues.map(
|
|
75377
75291
|
(issue2) => ` - ${issue2.path.join(".")}: ${issue2.message}`
|
|
75378
75292
|
);
|
|
75379
|
-
return (0,
|
|
75293
|
+
return (0, import_neverthrow44.err)({ type: "INVALID_CONFIG", message: `Configuration error:
|
|
75380
75294
|
${messages.join("\n")}` });
|
|
75381
75295
|
}
|
|
75382
|
-
return (0,
|
|
75296
|
+
return (0, import_neverthrow44.ok)(result.data);
|
|
75383
75297
|
}
|
|
75384
75298
|
|
|
75385
75299
|
// src/core/parse-timeout-seconds.ts
|
|
@@ -75412,7 +75326,7 @@ function parseVerboseOption(value) {
|
|
|
75412
75326
|
|
|
75413
75327
|
// src/pid-lock.ts
|
|
75414
75328
|
var import_node_fs9 = require("node:fs");
|
|
75415
|
-
var
|
|
75329
|
+
var import_neverthrow45 = __toESM(require_index_cjs(), 1);
|
|
75416
75330
|
var PID_FILE = "/tmp/xqa.pid";
|
|
75417
75331
|
var SIGINT_EXIT_CODE = 130;
|
|
75418
75332
|
var SIGTERM_EXIT_CODE = 143;
|
|
@@ -75421,7 +75335,7 @@ var HARD_TIMEOUT_MS = 1e4;
|
|
|
75421
75335
|
var cleanup = () => {
|
|
75422
75336
|
(0, import_node_fs9.rmSync)(PID_FILE, { force: true });
|
|
75423
75337
|
};
|
|
75424
|
-
var checkProcessRunning = (0,
|
|
75338
|
+
var checkProcessRunning = (0, import_neverthrow45.fromThrowable)(
|
|
75425
75339
|
(pid) => {
|
|
75426
75340
|
process.kill(pid, 0);
|
|
75427
75341
|
return true;
|
|
@@ -75487,17 +75401,17 @@ function acquireLock() {
|
|
|
75487
75401
|
// src/shell/xqa-directory.ts
|
|
75488
75402
|
var import_node_fs10 = require("node:fs");
|
|
75489
75403
|
var import_node_path19 = __toESM(require("node:path"), 1);
|
|
75490
|
-
var
|
|
75404
|
+
var import_neverthrow46 = __toESM(require_index_cjs(), 1);
|
|
75491
75405
|
function findXqaDirectory(startDirectory) {
|
|
75492
75406
|
let current = startDirectory;
|
|
75493
75407
|
for (; ; ) {
|
|
75494
75408
|
const candidate = import_node_path19.default.join(current, ".xqa");
|
|
75495
75409
|
if ((0, import_node_fs10.existsSync)(candidate)) {
|
|
75496
|
-
return (0,
|
|
75410
|
+
return (0, import_neverthrow46.ok)(candidate);
|
|
75497
75411
|
}
|
|
75498
75412
|
const parent = import_node_path19.default.dirname(current);
|
|
75499
75413
|
if (parent === current) {
|
|
75500
|
-
return (0,
|
|
75414
|
+
return (0, import_neverthrow46.err)({ type: "XQA_NOT_INITIALIZED" });
|
|
75501
75415
|
}
|
|
75502
75416
|
current = parent;
|
|
75503
75417
|
}
|
|
@@ -75507,11 +75421,11 @@ function findXqaDirectory(startDirectory) {
|
|
|
75507
75421
|
var import_promises21 = __toESM(require("node:fs/promises"), 1);
|
|
75508
75422
|
var import_node_path23 = __toESM(require("node:path"), 1);
|
|
75509
75423
|
var import_fast_glob = __toESM(require_out4(), 1);
|
|
75510
|
-
var
|
|
75424
|
+
var import_neverthrow54 = __toESM(require_index_cjs(), 1);
|
|
75511
75425
|
|
|
75512
75426
|
// src/suite/core/suite-config-parser.ts
|
|
75427
|
+
var import_neverthrow47 = __toESM(require_index_cjs(), 1);
|
|
75513
75428
|
var import_neverthrow48 = __toESM(require_index_cjs(), 1);
|
|
75514
|
-
var import_neverthrow49 = __toESM(require_index_cjs(), 1);
|
|
75515
75429
|
var RESERVED_HOOK_ENV_KEYS = [
|
|
75516
75430
|
"XQA_SIM_UDID",
|
|
75517
75431
|
"XQA_ITEM_ID",
|
|
@@ -75563,7 +75477,7 @@ var suiteConfigSchema = external_exports2.object({
|
|
|
75563
75477
|
}).refine((data) => data.specs.length > 0 || data.freestyle.length > 0, {
|
|
75564
75478
|
message: "Suite must declare at least one spec or freestyle entry"
|
|
75565
75479
|
});
|
|
75566
|
-
var safeJsonParse4 = (0,
|
|
75480
|
+
var safeJsonParse4 = (0, import_neverthrow47.fromThrowable)(
|
|
75567
75481
|
JSON.parse,
|
|
75568
75482
|
(cause) => ({
|
|
75569
75483
|
type: "INVALID_SUITE_CONFIG",
|
|
@@ -75597,16 +75511,16 @@ function parseSuiteConfig(raw) {
|
|
|
75597
75511
|
return safeJsonParse4(raw).andThen((data) => {
|
|
75598
75512
|
const parsed = suiteConfigSchema.safeParse(data);
|
|
75599
75513
|
if (!parsed.success) {
|
|
75600
|
-
return (0,
|
|
75514
|
+
return (0, import_neverthrow48.err)({ type: "INVALID_SUITE_CONFIG", cause: parsed.error });
|
|
75601
75515
|
}
|
|
75602
75516
|
const hooks = normalizeHooks(parsed.data.hooks);
|
|
75603
75517
|
if (hooks === void 0) {
|
|
75604
|
-
return (0,
|
|
75518
|
+
return (0, import_neverthrow48.ok)({
|
|
75605
75519
|
specs: parsed.data.specs,
|
|
75606
75520
|
freestyle: parsed.data.freestyle
|
|
75607
75521
|
});
|
|
75608
75522
|
}
|
|
75609
|
-
return (0,
|
|
75523
|
+
return (0, import_neverthrow48.ok)({
|
|
75610
75524
|
specs: parsed.data.specs,
|
|
75611
75525
|
freestyle: parsed.data.freestyle,
|
|
75612
75526
|
hooks
|
|
@@ -75665,7 +75579,7 @@ function buildFreestyleItems(entries) {
|
|
|
75665
75579
|
// src/suite/shell/suite-findings-writer.ts
|
|
75666
75580
|
var import_promises19 = __toESM(require("node:fs/promises"), 1);
|
|
75667
75581
|
var import_node_path20 = __toESM(require("node:path"), 1);
|
|
75668
|
-
var
|
|
75582
|
+
var import_neverthrow49 = __toESM(require_index_cjs(), 1);
|
|
75669
75583
|
var INDENT_SPACES = 2;
|
|
75670
75584
|
function writeSuiteFindings(findings, options) {
|
|
75671
75585
|
const directory = import_node_path20.default.join(
|
|
@@ -75677,7 +75591,7 @@ function writeSuiteFindings(findings, options) {
|
|
|
75677
75591
|
);
|
|
75678
75592
|
const finalPath = import_node_path20.default.join(directory, "findings.json");
|
|
75679
75593
|
const temporaryPath = `${finalPath}.tmp`;
|
|
75680
|
-
const safeWriteAtomically =
|
|
75594
|
+
const safeWriteAtomically = import_neverthrow49.ResultAsync.fromThrowable(
|
|
75681
75595
|
async () => {
|
|
75682
75596
|
await import_promises19.default.mkdir(directory, { recursive: true });
|
|
75683
75597
|
await import_promises19.default.writeFile(temporaryPath, JSON.stringify(findings, void 0, INDENT_SPACES));
|
|
@@ -75690,7 +75604,7 @@ function writeSuiteFindings(findings, options) {
|
|
|
75690
75604
|
}
|
|
75691
75605
|
|
|
75692
75606
|
// src/suite/shell/worker-pool.ts
|
|
75693
|
-
var
|
|
75607
|
+
var import_neverthrow52 = __toESM(require_index_cjs(), 1);
|
|
75694
75608
|
|
|
75695
75609
|
// src/suite/core/priority-queue.ts
|
|
75696
75610
|
var PriorityQueue = class {
|
|
@@ -75874,7 +75788,7 @@ function recordHookFailure(input) {
|
|
|
75874
75788
|
}
|
|
75875
75789
|
|
|
75876
75790
|
// src/suite/shell/hook-invoker.ts
|
|
75877
|
-
var
|
|
75791
|
+
var import_neverthrow51 = __toESM(require_index_cjs(), 1);
|
|
75878
75792
|
|
|
75879
75793
|
// src/suite/core/hook-env-builder.ts
|
|
75880
75794
|
function buildReservedKeys(input) {
|
|
@@ -75900,8 +75814,8 @@ function buildHookEnv(input) {
|
|
|
75900
75814
|
}
|
|
75901
75815
|
|
|
75902
75816
|
// src/suite/shell/hook-runner.ts
|
|
75903
|
-
var
|
|
75904
|
-
var
|
|
75817
|
+
var import_node_child_process6 = require("node:child_process");
|
|
75818
|
+
var import_neverthrow50 = __toESM(require_index_cjs(), 1);
|
|
75905
75819
|
var noop2 = () => void 0;
|
|
75906
75820
|
function cleanup2(context) {
|
|
75907
75821
|
clearTimeout(context.timeoutHandle);
|
|
@@ -75930,22 +75844,22 @@ function collectStderr(child, stderrReference) {
|
|
|
75930
75844
|
function buildOnAbort(context) {
|
|
75931
75845
|
return () => {
|
|
75932
75846
|
context.child.kill("SIGTERM");
|
|
75933
|
-
settle(context, (0,
|
|
75847
|
+
settle(context, (0, import_neverthrow50.err)({ type: "HOOK_ABORTED" }));
|
|
75934
75848
|
};
|
|
75935
75849
|
}
|
|
75936
75850
|
function attachChildListeners(context) {
|
|
75937
75851
|
const { child, stderrReference } = context;
|
|
75938
75852
|
child.on("error", (cause) => {
|
|
75939
|
-
settle(context, (0,
|
|
75853
|
+
settle(context, (0, import_neverthrow50.err)({ type: "HOOK_SPAWN_FAILED", cause }));
|
|
75940
75854
|
});
|
|
75941
75855
|
child.on("exit", (code) => {
|
|
75942
75856
|
if (code === 0) {
|
|
75943
|
-
settle(context, (0,
|
|
75857
|
+
settle(context, (0, import_neverthrow50.ok)());
|
|
75944
75858
|
return;
|
|
75945
75859
|
}
|
|
75946
75860
|
settle(
|
|
75947
75861
|
context,
|
|
75948
|
-
(0,
|
|
75862
|
+
(0, import_neverthrow50.err)({
|
|
75949
75863
|
type: "HOOK_EXIT_NONZERO",
|
|
75950
75864
|
code: code ?? -1,
|
|
75951
75865
|
stderr: stderrReference.value
|
|
@@ -75957,14 +75871,14 @@ function attachTimeout(context, timeoutMs) {
|
|
|
75957
75871
|
clearTimeout(context.timeoutHandle);
|
|
75958
75872
|
context.timeoutHandle = setTimeout(() => {
|
|
75959
75873
|
context.child.kill("SIGTERM");
|
|
75960
|
-
settle(context, (0,
|
|
75874
|
+
settle(context, (0, import_neverthrow50.err)({ type: "HOOK_TIMEOUT", timeoutMs }));
|
|
75961
75875
|
}, timeoutMs);
|
|
75962
75876
|
}
|
|
75963
75877
|
function buildContext2(options) {
|
|
75964
75878
|
const { script, cwd, env: env2, baseEnv, nodeExecPath } = options;
|
|
75965
75879
|
const deferred = makeDeferred();
|
|
75966
75880
|
const stderrReference = { value: "" };
|
|
75967
|
-
const child = (0,
|
|
75881
|
+
const child = (0, import_node_child_process6.spawn)(nodeExecPath, [script], {
|
|
75968
75882
|
cwd,
|
|
75969
75883
|
env: { ...baseEnv, ...env2 },
|
|
75970
75884
|
stdio: ["ignore", "inherit", "pipe"]
|
|
@@ -75992,7 +75906,7 @@ async function spawnHook(options) {
|
|
|
75992
75906
|
attachChildListeners(context);
|
|
75993
75907
|
return context.deferred.promise;
|
|
75994
75908
|
}
|
|
75995
|
-
var safeSpawn =
|
|
75909
|
+
var safeSpawn = import_neverthrow50.ResultAsync.fromThrowable(
|
|
75996
75910
|
spawnHook,
|
|
75997
75911
|
(cause) => ({ type: "HOOK_SPAWN_FAILED", cause })
|
|
75998
75912
|
);
|
|
@@ -76020,7 +75934,7 @@ async function invokeHook(input) {
|
|
|
76020
75934
|
async function maybeInvokeHook(input) {
|
|
76021
75935
|
const { hook } = input;
|
|
76022
75936
|
if (hook === void 0) {
|
|
76023
|
-
return (0,
|
|
75937
|
+
return (0, import_neverthrow51.ok)();
|
|
76024
75938
|
}
|
|
76025
75939
|
return invokeHook({ ...input, hook });
|
|
76026
75940
|
}
|
|
@@ -76159,7 +76073,7 @@ function runWorkerPool(config3) {
|
|
|
76159
76073
|
const queue = setupQueue(config3);
|
|
76160
76074
|
const results = [];
|
|
76161
76075
|
const suiteStartMs = Date.now();
|
|
76162
|
-
const safeRun =
|
|
76076
|
+
const safeRun = import_neverthrow52.ResultAsync.fromThrowable(
|
|
76163
76077
|
async () => runAllWorkers({ config: config3, queue, results, suiteStartMs }),
|
|
76164
76078
|
(cause) => ({ type: "WORKER_POOL_FAILED", cause })
|
|
76165
76079
|
);
|
|
@@ -76188,29 +76102,31 @@ function resolveFreestyleTimeout(item, config3) {
|
|
|
76188
76102
|
}
|
|
76189
76103
|
return config3.QA_EXPLORE_TIMEOUT_SECONDS ?? DEFAULT_FREESTYLE_TIMEOUT_SECONDS2;
|
|
76190
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
|
+
}
|
|
76191
76120
|
function buildFreestylePipelineConfig(input) {
|
|
76192
76121
|
const { item, context, signal, simulatorUdid, onEvent } = input;
|
|
76193
|
-
const {
|
|
76122
|
+
const { xqaDirectory, runId } = context;
|
|
76194
76123
|
return {
|
|
76195
76124
|
outputDir: import_node_path21.default.join(xqaDirectory, "output", item.id),
|
|
76196
76125
|
runId,
|
|
76197
76126
|
simulatorUdid,
|
|
76198
76127
|
signal,
|
|
76199
76128
|
onEvent,
|
|
76200
|
-
explorer:
|
|
76201
|
-
mode: "freestyle",
|
|
76202
|
-
date: date5,
|
|
76203
|
-
mcpServers: createDefaultMcpServers(simulatorUdid),
|
|
76204
|
-
allowedTools: ALLOWED_TOOLS,
|
|
76205
|
-
timeoutMs: resolveFreestyleTimeout(item, config3) * MS_PER_SECOND4,
|
|
76206
|
-
appContext: composeAppContext([
|
|
76207
|
-
buildDeviceInstruction(simulatorUdid),
|
|
76208
|
-
appContext,
|
|
76209
|
-
item.prompt
|
|
76210
|
-
]),
|
|
76211
|
-
buildEnv: config3.QA_BUILD_ENV,
|
|
76212
|
-
cwd: ensureWorkerCwd(simulatorUdid)
|
|
76213
|
-
}
|
|
76129
|
+
explorer: buildFreestyleExplorerConfig(input)
|
|
76214
76130
|
};
|
|
76215
76131
|
}
|
|
76216
76132
|
function executeFreestyleItem(input) {
|
|
@@ -76232,7 +76148,8 @@ function buildSpecPipelineConfig(input) {
|
|
|
76232
76148
|
allowedTools: ALLOWED_TOOLS,
|
|
76233
76149
|
appContext: composeAppContext([buildDeviceInstruction(simulatorUdid), appContext]),
|
|
76234
76150
|
buildEnv: config3.QA_BUILD_ENV,
|
|
76235
|
-
cwd: ensureWorkerCwd(simulatorUdid)
|
|
76151
|
+
cwd: ensureWorkerCwd(simulatorUdid),
|
|
76152
|
+
record: true
|
|
76236
76153
|
}
|
|
76237
76154
|
};
|
|
76238
76155
|
}
|
|
@@ -76252,7 +76169,7 @@ function makeExecuteItem(context) {
|
|
|
76252
76169
|
// src/suite/commands/suite-run-context.ts
|
|
76253
76170
|
var import_promises20 = __toESM(require("node:fs/promises"), 1);
|
|
76254
76171
|
var import_node_path22 = __toESM(require("node:path"), 1);
|
|
76255
|
-
var
|
|
76172
|
+
var import_neverthrow53 = __toESM(require_index_cjs(), 1);
|
|
76256
76173
|
|
|
76257
76174
|
// src/suite/core/run-id.ts
|
|
76258
76175
|
var RUN_ID_PAD_LENGTH2 = 4;
|
|
@@ -76287,7 +76204,7 @@ function deriveSuiteId(input) {
|
|
|
76287
76204
|
|
|
76288
76205
|
// src/suite/commands/suite-run-context.ts
|
|
76289
76206
|
var ISO_DATE_LENGTH3 = 10;
|
|
76290
|
-
var safeReaddir2 =
|
|
76207
|
+
var safeReaddir2 = import_neverthrow53.ResultAsync.fromThrowable(
|
|
76291
76208
|
async (directoryPath) => {
|
|
76292
76209
|
const entries = await import_promises20.default.readdir(directoryPath, { withFileTypes: true });
|
|
76293
76210
|
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
@@ -76335,7 +76252,7 @@ async function buildSuiteRunContext(input) {
|
|
|
76335
76252
|
}
|
|
76336
76253
|
|
|
76337
76254
|
// src/suite/commands/run-command.ts
|
|
76338
|
-
var safeReadFile5 =
|
|
76255
|
+
var safeReadFile5 = import_neverthrow54.ResultAsync.fromThrowable(
|
|
76339
76256
|
async (filePath) => import_promises21.default.readFile(filePath, "utf8"),
|
|
76340
76257
|
() => "READ_FAILED"
|
|
76341
76258
|
);
|
|
@@ -76523,7 +76440,7 @@ function resolveXqaDirectory() {
|
|
|
76523
76440
|
return result.value;
|
|
76524
76441
|
}
|
|
76525
76442
|
var program2 = new Command();
|
|
76526
|
-
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"})` : ""}`);
|
|
76527
76444
|
program2.command("init").description("Initialize a new xqa project in the current directory").action(() => {
|
|
76528
76445
|
runInitCommand();
|
|
76529
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"
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"@qa-agents/eslint-config": "0.0.0",
|
|
31
31
|
"@qa-agents/explorer": "0.0.0",
|
|
32
32
|
"@qa-agents/mobile-ios": "0.0.0",
|
|
33
|
-
"@qa-agents/pipeline": "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": {
|