archal 0.9.19 → 0.10.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 +62 -154
- package/agents/openclaw/.archal.json +8 -0
- package/agents/openclaw/Dockerfile +97 -0
- package/agents/openclaw/README.md +113 -0
- package/agents/openclaw/drive.mjs +301 -0
- package/agents/openclaw/extract-openclaw-response-text.mjs +49 -0
- package/agents/openclaw/package.json +9 -0
- package/agents/openclaw/scenarios/acceptance/github-escalation-and-cleanup.md +43 -0
- package/agents/openclaw/scenarios/acceptance/jira-linear-release-triage.md +39 -0
- package/agents/openclaw/scenarios/acceptance/multi-clone-customer-risk-review.md +45 -0
- package/agents/openclaw/scenarios/acceptance/slack-incident-handoff.md +42 -0
- package/agents/openclaw/scenarios/acceptance/stripe-dunning-remediation.md +40 -0
- package/agents/openclaw/scenarios/close-stale-issues.md +35 -0
- package/agents/openclaw/scenarios/github-issue-triage-read-only.md +44 -0
- package/agents/openclaw/workspace/AGENTS.md +23 -0
- package/agents/openclaw/workspace/IDENTITY.md +8 -0
- package/agents/openclaw/workspace/SOUL.md +14 -0
- package/agents/openclaw/workspace/TOOLS.md +54 -0
- package/clone-assets/apify/tools.json +256 -22
- package/clone-assets/calcom/tools.json +2062 -0
- package/clone-assets/clickup/tools.json +3245 -0
- package/clone-assets/customerio/tools.json +2350 -0
- package/clone-assets/datadog/tools.json +734 -0
- package/clone-assets/github/tools.json +307 -27
- package/clone-assets/gitlab/tools.json +10688 -0
- package/clone-assets/google-workspace/tools.json +18 -6
- package/clone-assets/hubspot/tools.json +1604 -0
- package/clone-assets/jira/fidelity.json +1 -1
- package/clone-assets/jira/tools.json +266 -543
- package/clone-assets/linear/tools.json +278 -40
- package/clone-assets/ownerrez/tools.json +548 -0
- package/clone-assets/pricelabs/tools.json +343 -0
- package/clone-assets/sentry/tools.json +2821 -0
- package/clone-assets/slack/tools.json +1 -2
- package/clone-assets/stripe/tools.json +299 -46
- package/clone-assets/supabase/tools.json +437 -0
- package/clone-assets/unipile/tools.json +408 -0
- package/clone-assets/webflow/tools.json +2185 -0
- package/dist/autoloop-worker-types-CHaclqtD.d.cts +160 -0
- package/dist/cli.cjs +127896 -84811
- package/dist/{vitest/chunk-L36NXAU6.js → commands/autoloop-hosted-control-plane-client.cjs} +16344 -18845
- package/dist/commands/autoloop-hosted-control-plane-client.d.cts +133 -0
- package/dist/commands/autoloop-pr-verification.cjs +4312 -0
- package/dist/commands/autoloop-pr-verification.d.cts +19 -0
- package/dist/commands/autoloop-result-parser.cjs +27058 -0
- package/dist/commands/autoloop-result-parser.d.cts +196 -0
- package/dist/commands/autoloop-worker.cjs +38488 -0
- package/dist/commands/autoloop-worker.d.cts +102 -0
- package/dist/eval-shim.mjs +216 -0
- package/dist/index.cjs +15 -2
- package/dist/index.d.cts +3 -1
- package/dist/layer-1-ziaLpfLr.d.cts +62 -0
- package/dist/metafile-cjs.json +1 -0
- package/dist/reporter-entry.js +3 -0
- package/dist/sdk/index.cjs +51630 -0
- package/dist/sdk/index.d.cts +626 -0
- package/dist/vitest/chunk-2PDHTPZC.js +4867 -0
- package/dist/vitest/chunk-7L4BBB6M.js +2613 -0
- package/dist/vitest/index.cjs +6152 -75718
- package/dist/vitest/index.d.ts +22 -86
- package/dist/vitest/index.js +45 -414
- package/dist/vitest/runtime/hosted-session-reaper.cjs +682 -34399
- package/dist/vitest/runtime/hosted-session-reaper.js +1 -1
- package/dist/vitest/runtime/setup-files.js +2 -2
- package/manifest.json +9 -0
- package/package.json +20 -14
- package/skills/archal-agent/SKILL.md +86 -0
- package/skills/autoloop/SKILL.md +391 -0
- package/skills/autoloop/references/hosted-sources.md +94 -0
- package/skills/autoloop/references/trace-schema-mapping.md +104 -0
- package/skills/eval/SKILL.md +17 -15
- package/skills/free-account/SKILL.md +103 -0
- package/skills/install-agent/SKILL.md +202 -0
- package/skills/onboard/SKILL.md +29 -18
- package/skills/scenario/SKILL.md +36 -18
- package/skills/seed/SKILL.md +238 -0
- package/skills/vitest/SKILL.md +3 -2
- package/dist/harness.cjs +0 -62
- package/dist/harness.d.cts +0 -20
- package/dist/seed/dynamic-generator.cjs +0 -45687
- package/dist/seed/dynamic-generator.d.cts +0 -106
- package/dist/vitest/chunk-WZ7SA4CK.js +0 -47369
- package/skills/attach/SKILL.md +0 -402
package/dist/vitest/index.js
CHANGED
|
@@ -8,12 +8,10 @@ import {
|
|
|
8
8
|
readArchalVitestConfig,
|
|
9
9
|
resetArchalClones,
|
|
10
10
|
resolveRuntimeModule
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-2PDHTPZC.js";
|
|
12
12
|
import {
|
|
13
|
-
encodeConfig
|
|
14
|
-
|
|
15
|
-
getSessionIdFilePath
|
|
16
|
-
} from "./chunk-WZ7SA4CK.js";
|
|
13
|
+
encodeConfig
|
|
14
|
+
} from "./chunk-7L4BBB6M.js";
|
|
17
15
|
|
|
18
16
|
// src/index.ts
|
|
19
17
|
import { dirname, isAbsolute, resolve as resolve2 } from "path";
|
|
@@ -145,7 +143,7 @@ function installReporterLifecycleCheck(projectName) {
|
|
|
145
143
|
return;
|
|
146
144
|
}
|
|
147
145
|
process.stderr.write(
|
|
148
|
-
'\n\x1B[33m\x1B[1mArchal Vitest
|
|
146
|
+
'\n\x1B[33m\x1B[1mArchal Vitest reporter did not finish lifecycle diagnostics\n\x1B[0m\x1B[33mThe Archal reporter started, but Vitest did not call its final\nlifecycle step. Benchmark Results are submitted by the scenario runner.\n\nCommon causes:\n - A Vitest upgrade changed per-project reporter lifecycle timing.\n - A consumer config filtered out reporters (look for reporter overrides).\n - The ArchalReporter threw before reaching collectAndReport.\n\nWhat to check:\n 1. Grep stderr for "[archal/reporter]". If any line appears, the\n reporter did run and this warning is a false positive.\n 2. If no [archal/reporter] lines exist at all, the reporter was\n installed but never called. Verify your vitest config wires it\n at the level Vitest actually honors (workspace root vs project).\n'
|
|
149
147
|
);
|
|
150
148
|
process.stderr.write("\x1B[0m\n");
|
|
151
149
|
for (const survivor of survivors) {
|
|
@@ -193,7 +191,7 @@ function installWorkspaceReporterCheck(projectNames) {
|
|
|
193
191
|
return;
|
|
194
192
|
}
|
|
195
193
|
process.stderr.write(
|
|
196
|
-
"\n\x1B[33m\x1B[1mArchal Vitest did not
|
|
194
|
+
"\n\x1B[33m\x1B[1mArchal Vitest did not run workspace reporter diagnostics\n\x1B[0m\x1B[33marchalVitestWorkspace() configured hosted clone projects, but no\nroot Archal reporter was registered. Requests were routed to the\nhosted clones, but reporter lifecycle diagnostics were skipped.\n\nFix: add a root vitest.config.ts alongside vitest.workspace.ts:\n\n import { defineConfig } from 'vitest/config';\n import { archalVitestRootConfig } from 'archal/vitest';\n\n export default defineConfig(archalVitestRootConfig());\n\nIf you do not need workspace mode, use withArchal() instead.\n\x1B[0m\n"
|
|
197
195
|
);
|
|
198
196
|
for (const survivor of survivors) {
|
|
199
197
|
try {
|
|
@@ -254,7 +252,7 @@ function installImportSentinel() {
|
|
|
254
252
|
if (process.env[ARCHAL_VITEST_CONFIG_ENV2]) return;
|
|
255
253
|
warningWritten = true;
|
|
256
254
|
process.stderr.write(
|
|
257
|
-
"\n\x1B[33m\x1B[1mArchal Vitest was imported but not configured\n\x1B[0m\x1B[33mSomething in your test run imported `archal/vitest`, but no\nArchal Vitest helper was called (`withArchal`, `archalVitestProject`,\n`archalVitestRootConfig`).
|
|
255
|
+
"\n\x1B[33m\x1B[1mArchal Vitest was imported but not configured\n\x1B[0m\x1B[33mSomething in your test run imported `archal/vitest`, but no\nArchal Vitest helper was called (`withArchal`, `archalVitestProject`,\n`archalVitestRootConfig`). Benchmark Results are submitted by the scenario runner.\n\nFix: in your vitest.config.ts, wrap your test block with withArchal():\n\n import { defineConfig } from 'vitest/config';\n import { withArchal } from 'archal/vitest';\n export default defineConfig({\n test: withArchal({}, { services: {} }),\n });\n\nOr run `archal init` from your project root and we will wire it for you.\n\x1B[0m\n"
|
|
258
256
|
);
|
|
259
257
|
};
|
|
260
258
|
const sigintHandler = () => {
|
|
@@ -274,14 +272,8 @@ function installImportSentinel() {
|
|
|
274
272
|
}
|
|
275
273
|
|
|
276
274
|
// src/reporter.ts
|
|
277
|
-
import picomatch from "picomatch";
|
|
278
275
|
var LOG_PREFIX = "[archal/reporter]";
|
|
279
276
|
var STRICT_ENV = "ARCHAL_REPORTER_STRICT";
|
|
280
|
-
var REPORTER_TOKEN_ENV = "ARCHAL_REPORTER_TOKEN";
|
|
281
|
-
var REPORTER_AUTH_URL_ENV = "ARCHAL_REPORTER_AUTH_URL";
|
|
282
|
-
var REPORTER_AUTH_BASE_URL_ENV = "ARCHAL_REPORTER_AUTH_BASE_URL";
|
|
283
|
-
var REPORTER_BYPASS_SECRET_ENV = "ARCHAL_REPORTER_VERCEL_AUTOMATION_BYPASS_SECRET";
|
|
284
|
-
var REPORTER_WORKSPACE_ENV = "ARCHAL_REPORTER_WORKSPACE_ID";
|
|
285
277
|
var WORKSPACE_REPORTER_MARKER_ENV = "ARCHAL_VITEST_WORKSPACE_REPORTER_MARKER";
|
|
286
278
|
function isStrict() {
|
|
287
279
|
return process.env[STRICT_ENV] === "1";
|
|
@@ -294,112 +286,43 @@ function warn(message) {
|
|
|
294
286
|
}
|
|
295
287
|
process.stderr.write(line);
|
|
296
288
|
}
|
|
297
|
-
function
|
|
289
|
+
function diagnostic(message) {
|
|
298
290
|
process.stderr.write(`${LOG_PREFIX} ${message}
|
|
299
291
|
`);
|
|
300
292
|
}
|
|
301
|
-
async function resolveReporterToken() {
|
|
302
|
-
const explicitReporterToken = process.env[REPORTER_TOKEN_ENV]?.trim();
|
|
303
|
-
if (explicitReporterToken) {
|
|
304
|
-
return explicitReporterToken;
|
|
305
|
-
}
|
|
306
|
-
try {
|
|
307
|
-
const creds = getCredentials();
|
|
308
|
-
return creds?.token ?? process.env["ARCHAL_TOKEN"]?.trim();
|
|
309
|
-
} catch (authErr) {
|
|
310
|
-
warn(`auth resolution failed: ${authErr instanceof Error ? authErr.message : String(authErr)}`);
|
|
311
|
-
return void 0;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
function resolveReporterBaseUrl() {
|
|
315
|
-
return (process.env[REPORTER_AUTH_URL_ENV]?.trim() || process.env[REPORTER_AUTH_BASE_URL_ENV]?.trim() || process.env["ARCHAL_AUTH_URL"]?.trim() || process.env["ARCHAL_AUTH_BASE_URL"]?.trim() || "https://www.archal.ai").replace(/\/+$/, "");
|
|
316
|
-
}
|
|
317
|
-
function buildReporterHeaders(token) {
|
|
318
|
-
const headers = new Headers({
|
|
319
|
-
"content-type": "application/json",
|
|
320
|
-
authorization: `Bearer ${token}`
|
|
321
|
-
});
|
|
322
|
-
const bypassSecret = process.env[REPORTER_BYPASS_SECRET_ENV]?.trim();
|
|
323
|
-
if (bypassSecret) {
|
|
324
|
-
headers.set("x-vercel-protection-bypass", bypassSecret);
|
|
325
|
-
}
|
|
326
|
-
const workspaceId = resolveReporterWorkspaceId();
|
|
327
|
-
if (workspaceId) {
|
|
328
|
-
headers.set("x-archal-workspace", workspaceId);
|
|
329
|
-
}
|
|
330
|
-
return headers;
|
|
331
|
-
}
|
|
332
|
-
function resolveReporterWorkspaceId() {
|
|
333
|
-
const reporterWorkspaceId = process.env[REPORTER_WORKSPACE_ENV]?.trim();
|
|
334
|
-
if (reporterWorkspaceId) {
|
|
335
|
-
return reporterWorkspaceId;
|
|
336
|
-
}
|
|
337
|
-
const cliWorkspaceId = process.env["ARCHAL_WORKSPACE_ID"]?.trim();
|
|
338
|
-
return cliWorkspaceId || void 0;
|
|
339
|
-
}
|
|
340
|
-
function normalizePathForMatch(path) {
|
|
341
|
-
return path.replaceAll("\\", "/");
|
|
342
|
-
}
|
|
343
|
-
function buildIncludeMatcher(pattern) {
|
|
344
|
-
const normalizedPattern = normalizePathForMatch(pattern).replace(/^\.?\//, "");
|
|
345
|
-
const patterns = normalizedPattern.startsWith("**/") ? [normalizedPattern] : [normalizedPattern, `**/${normalizedPattern}`];
|
|
346
|
-
const matchers = patterns.map((candidate) => picomatch(candidate, { dot: true }));
|
|
347
|
-
return (filePath) => {
|
|
348
|
-
const normalizedFile = normalizePathForMatch(filePath);
|
|
349
|
-
return matchers.some((matcher) => matcher(normalizedFile));
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
293
|
function readEncodedConfigFromTarget(target) {
|
|
353
294
|
const env = target?.config?.env;
|
|
354
295
|
return env?.[ARCHAL_VITEST_CONFIG_ENV];
|
|
355
296
|
}
|
|
356
|
-
function readProjectIncludePatterns(project) {
|
|
357
|
-
const config = project?.config;
|
|
358
|
-
const include = Array.isArray(config?.include) ? config.include : Array.isArray(config?.test?.include) ? config.test.include : [];
|
|
359
|
-
return include.filter(
|
|
360
|
-
(pattern) => typeof pattern === "string" && pattern.length > 0
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
297
|
var ArchalReporter = class {
|
|
364
|
-
startTime = 0;
|
|
365
298
|
ctx = null;
|
|
366
299
|
lifecycleMarkerPath = "";
|
|
367
|
-
workspaceReporterMarkerPath = "";
|
|
368
|
-
projects = [];
|
|
369
300
|
/**
|
|
370
|
-
* In-flight
|
|
371
|
-
* `onFinished` and `onTestRunEnd` in parallel via `Promise.all
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
* so watch-mode reruns still upload.
|
|
301
|
+
* In-flight lifecycle promise for the current run. Vitest v3+ fires both
|
|
302
|
+
* `onFinished` and `onTestRunEnd` in parallel via `Promise.all`, so without
|
|
303
|
+
* this guard the reporter emits duplicate diagnostics. Reset at the start
|
|
304
|
+
* of each run (onTestRunStart / onWatcherRerun) so watch-mode reruns still
|
|
305
|
+
* report.
|
|
376
306
|
*/
|
|
377
|
-
|
|
307
|
+
activeLifecycle = null;
|
|
378
308
|
onInit(ctx) {
|
|
379
|
-
this.startTime = Date.now();
|
|
380
309
|
this.ctx = ctx;
|
|
381
|
-
|
|
382
|
-
const projectName = this.projects[0]?.config.projectName ?? "vitest";
|
|
310
|
+
const projectName = this.resolveProjectName();
|
|
383
311
|
this.lifecycleMarkerPath = installReporterLifecycleCheck(projectName);
|
|
384
|
-
|
|
385
|
-
markWorkspaceReporterInstalled(
|
|
386
|
-
this.workspaceReporterMarkerPath = "";
|
|
312
|
+
const workspaceReporterMarkerPath = process.env[WORKSPACE_REPORTER_MARKER_ENV] ?? "";
|
|
313
|
+
markWorkspaceReporterInstalled(workspaceReporterMarkerPath);
|
|
387
314
|
}
|
|
388
|
-
/**
|
|
389
|
-
* v3+ canonical start-of-run hook. Resetting the upload guard here lets
|
|
390
|
-
* watch-mode reruns upload their own results.
|
|
391
|
-
*/
|
|
392
315
|
onTestRunStart(_specifications) {
|
|
393
|
-
this.
|
|
394
|
-
this.startTime = Date.now();
|
|
316
|
+
this.activeLifecycle = null;
|
|
395
317
|
}
|
|
396
|
-
/**
|
|
397
|
-
* v2 watch-mode start-of-rerun hook. Same purpose as onTestRunStart for
|
|
398
|
-
* v3+. Vitest v2 has no onTestRunStart, so we reset here instead.
|
|
399
|
-
*/
|
|
400
318
|
onWatcherRerun(_files, _trigger) {
|
|
401
|
-
this.
|
|
402
|
-
|
|
319
|
+
this.activeLifecycle = null;
|
|
320
|
+
}
|
|
321
|
+
async onFinished(files, _errors) {
|
|
322
|
+
await this.runLifecycleOnce(files?.length ?? 0, "files");
|
|
323
|
+
}
|
|
324
|
+
async onTestRunEnd(testModules, _unhandledErrors, _reason) {
|
|
325
|
+
await this.runLifecycleOnce(testModules?.length ?? 0, "test modules");
|
|
403
326
|
}
|
|
404
327
|
decodeConfig(encoded) {
|
|
405
328
|
try {
|
|
@@ -408,320 +331,34 @@ var ArchalReporter = class {
|
|
|
408
331
|
return null;
|
|
409
332
|
}
|
|
410
333
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
config,
|
|
414
|
-
includePatterns,
|
|
415
|
-
includeMatchers: includePatterns.map(buildIncludeMatcher)
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
resolveProjects() {
|
|
419
|
-
const resolvedProjects = (this.ctx?.projects ?? []).map((project) => {
|
|
334
|
+
resolveProjectName() {
|
|
335
|
+
for (const project of this.ctx?.projects ?? []) {
|
|
420
336
|
const config = this.decodeConfig(readEncodedConfigFromTarget(project));
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
return resolvedProjects;
|
|
337
|
+
if (config?.projectName) {
|
|
338
|
+
return config.projectName;
|
|
339
|
+
}
|
|
425
340
|
}
|
|
426
341
|
const rootConfig = this.decodeConfig(
|
|
427
342
|
readEncodedConfigFromTarget(this.ctx) ?? process.env[ARCHAL_VITEST_CONFIG_ENV]
|
|
428
343
|
);
|
|
429
|
-
return rootConfig
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* vitest v2 calls onFinished with an array of File objects (from @vitest/runner).
|
|
433
|
-
* Each File extends Suite which has `tasks: Task[]`, and each Task has
|
|
434
|
-
* `.type` ('test' | 'suite'), `.name`, `.result?.state`, `.result?.duration`.
|
|
435
|
-
*/
|
|
436
|
-
async onFinished(files, _errors) {
|
|
437
|
-
await this.runUploadOnce(() => this.collectAndReport(files ?? [], "v2"));
|
|
344
|
+
return rootConfig?.projectName ?? "vitest";
|
|
438
345
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
* test modules, each with nested children.
|
|
443
|
-
*/
|
|
444
|
-
async onTestRunEnd(testModules) {
|
|
445
|
-
await this.runUploadOnce(() => this.collectAndReport(testModules ?? [], "v4"));
|
|
446
|
-
}
|
|
447
|
-
/**
|
|
448
|
-
* Dedupe upload work when Vitest fires multiple end-of-run hooks. In
|
|
449
|
-
* Vitest v3+ both `onFinished` and `onTestRunEnd` fire from the same
|
|
450
|
-
* `Promise.all` in TestRun.end(); the first hook to enter wins and the
|
|
451
|
-
* second awaits the same in-flight upload instead of POSTing a duplicate
|
|
452
|
-
* row to /api/test-results. The resolved promise is kept on the instance
|
|
453
|
-
* so any late hook still no-ops — the guard is only cleared at the start
|
|
454
|
-
* of a new run (onTestRunStart / onWatcherRerun).
|
|
455
|
-
*/
|
|
456
|
-
async runUploadOnce(work) {
|
|
457
|
-
if (!this.activeUpload) {
|
|
458
|
-
this.activeUpload = work();
|
|
346
|
+
async runLifecycleOnce(itemCount, itemLabel) {
|
|
347
|
+
if (!this.activeLifecycle) {
|
|
348
|
+
this.activeLifecycle = this.reportLifecycleComplete(itemCount, itemLabel);
|
|
459
349
|
}
|
|
460
|
-
await this.
|
|
350
|
+
await this.activeLifecycle;
|
|
461
351
|
}
|
|
462
|
-
async
|
|
352
|
+
async reportLifecycleComplete(itemCount, itemLabel) {
|
|
463
353
|
markReporterFired(this.lifecycleMarkerPath);
|
|
464
354
|
this.lifecycleMarkerPath = "";
|
|
465
|
-
if (
|
|
466
|
-
|
|
467
|
-
}
|
|
468
|
-
if (this.projects.length === 0) {
|
|
469
|
-
warn(
|
|
470
|
-
"Archal Vitest config was unreadable. Using the default project name. If this persists, make sure withArchal() or archalVitestProject() wraps your test config."
|
|
471
|
-
);
|
|
472
|
-
this.projects = [
|
|
473
|
-
this.buildProjectContext(
|
|
474
|
-
{
|
|
475
|
-
projectName: "vitest",
|
|
476
|
-
services: {}
|
|
477
|
-
},
|
|
478
|
-
[]
|
|
479
|
-
)
|
|
480
|
-
];
|
|
481
|
-
}
|
|
482
|
-
if (items.length === 0) {
|
|
483
|
-
warn(`No ${version === "v2" ? "files" : "test modules"} received. Nothing to upload.`);
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
486
|
-
const results = version === "v2" ? this.collectV2Files(items) : this.collectV4Modules(items);
|
|
487
|
-
if (results.length === 0) {
|
|
488
|
-
warn("Collected 0 tests after reading the test tree. Nothing to upload.");
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
const buckets = this.partitionResults(results);
|
|
492
|
-
if (buckets.length === 0) {
|
|
493
|
-
warn("collected tests but could not map them to an Archal Vitest project.");
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
try {
|
|
497
|
-
for (const bucket of buckets) {
|
|
498
|
-
await this.sendResults(bucket.project, bucket.results);
|
|
499
|
-
}
|
|
500
|
-
} catch (err) {
|
|
501
|
-
warn(`failed to upload test results: ${err instanceof Error ? err.message : String(err)}`);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
/**
|
|
505
|
-
* vitest v2: File extends Suite { tasks: Task[] }.
|
|
506
|
-
* Task.type is 'test' | 'suite' | 'custom'. Suite tasks recurse.
|
|
507
|
-
*/
|
|
508
|
-
collectV2Files(files) {
|
|
509
|
-
const results = [];
|
|
510
|
-
const collectTasks = (tasks, fileName) => {
|
|
511
|
-
for (const task of tasks) {
|
|
512
|
-
if (task.type === "test" || task.type === "custom") {
|
|
513
|
-
const state = task.result?.state;
|
|
514
|
-
results.push({
|
|
515
|
-
name: task.name ?? "unnamed",
|
|
516
|
-
file: fileName,
|
|
517
|
-
status: state === "pass" ? "passed" : state === "fail" ? "failed" : "skipped",
|
|
518
|
-
duration: task.result?.duration ?? 0,
|
|
519
|
-
error: state === "fail" && task.result?.errors?.[0] ? task.result.errors[0].message ?? String(task.result.errors[0]) : void 0
|
|
520
|
-
});
|
|
521
|
-
} else if (task.type === "suite" && Array.isArray(task.tasks)) {
|
|
522
|
-
collectTasks(task.tasks, fileName);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
};
|
|
526
|
-
for (const file of files) {
|
|
527
|
-
const f = file;
|
|
528
|
-
const fileName = f.filepath ?? f.name ?? "unknown";
|
|
529
|
-
if (Array.isArray(f.tasks)) {
|
|
530
|
-
collectTasks(f.tasks, fileName);
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
return results;
|
|
534
|
-
}
|
|
535
|
-
/**
|
|
536
|
-
* vitest v4: TestModule.children() → TestSuite[].children() → TestCase[]
|
|
537
|
-
* All accessors are methods (not properties) in v4.
|
|
538
|
-
*/
|
|
539
|
-
collectV4Modules(testModules) {
|
|
540
|
-
const results = [];
|
|
541
|
-
const collectTests = (node, fileName) => {
|
|
542
|
-
const children = typeof node.children === "function" ? node.children() : node.children ?? [];
|
|
543
|
-
for (const child of children) {
|
|
544
|
-
if (child.type === "test") {
|
|
545
|
-
const result = typeof child.result === "function" ? child.result() : child.result;
|
|
546
|
-
const diag = typeof child.diagnostic === "function" ? child.diagnostic() : child.diagnostic;
|
|
547
|
-
const state = result?.state;
|
|
548
|
-
results.push({
|
|
549
|
-
name: child.name ?? "unnamed",
|
|
550
|
-
file: fileName,
|
|
551
|
-
status: state === "passed" ? "passed" : state === "failed" ? "failed" : "skipped",
|
|
552
|
-
duration: diag?.duration ?? result?.duration ?? 0,
|
|
553
|
-
error: state === "failed" && result?.errors?.[0] ? result.errors[0].message ?? String(result.errors[0]) : void 0
|
|
554
|
-
});
|
|
555
|
-
} else {
|
|
556
|
-
collectTests(child, fileName);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
};
|
|
560
|
-
for (const mod of testModules) {
|
|
561
|
-
const m = mod;
|
|
562
|
-
const fileName = m.moduleId ?? m.id ?? "unknown";
|
|
563
|
-
collectTests(m, fileName);
|
|
564
|
-
}
|
|
565
|
-
return results;
|
|
566
|
-
}
|
|
567
|
-
resolveProjectForFile(fileName) {
|
|
568
|
-
const normalizedFile = normalizePathForMatch(fileName);
|
|
569
|
-
const onlyProject = this.projects[0] ?? null;
|
|
570
|
-
if (this.projects.length === 1) {
|
|
571
|
-
if (!onlyProject) {
|
|
572
|
-
return null;
|
|
573
|
-
}
|
|
574
|
-
if (onlyProject.includeMatchers.length === 0) {
|
|
575
|
-
return onlyProject;
|
|
576
|
-
}
|
|
577
|
-
return onlyProject.includeMatchers.some((matcher) => matcher(normalizedFile)) ? onlyProject : null;
|
|
578
|
-
}
|
|
579
|
-
let bestMatch = null;
|
|
580
|
-
let bestSpecificity = -1;
|
|
581
|
-
for (const project of this.projects) {
|
|
582
|
-
for (const [index, matcher] of project.includeMatchers.entries()) {
|
|
583
|
-
if (!matcher(normalizedFile)) {
|
|
584
|
-
continue;
|
|
585
|
-
}
|
|
586
|
-
const specificity = project.includePatterns[index]?.length ?? 0;
|
|
587
|
-
if (specificity > bestSpecificity) {
|
|
588
|
-
bestMatch = project;
|
|
589
|
-
bestSpecificity = specificity;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
return bestMatch;
|
|
594
|
-
}
|
|
595
|
-
partitionResults(results) {
|
|
596
|
-
const buckets = /* @__PURE__ */ new Map();
|
|
597
|
-
for (const result of results) {
|
|
598
|
-
const project = this.resolveProjectForFile(result.file);
|
|
599
|
-
if (!project) {
|
|
600
|
-
warn(
|
|
601
|
-
`Could not map ${result.file} to a Vitest project config. Skipping reporter upload for that file.`
|
|
602
|
-
);
|
|
603
|
-
continue;
|
|
604
|
-
}
|
|
605
|
-
const key = project.config.projectName;
|
|
606
|
-
const bucket = buckets.get(key);
|
|
607
|
-
if (bucket) {
|
|
608
|
-
bucket.results.push(result);
|
|
609
|
-
continue;
|
|
610
|
-
}
|
|
611
|
-
buckets.set(key, {
|
|
612
|
-
project,
|
|
613
|
-
results: [result]
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
return [...buckets.values()];
|
|
617
|
-
}
|
|
618
|
-
async sendResults(project, results) {
|
|
619
|
-
const summary = {
|
|
620
|
-
total: results.length,
|
|
621
|
-
passed: results.filter((result) => result.status === "passed").length,
|
|
622
|
-
failed: results.filter((result) => result.status === "failed").length,
|
|
623
|
-
skipped: results.filter((result) => result.status === "skipped").length,
|
|
624
|
-
duration: Date.now() - this.startTime
|
|
625
|
-
};
|
|
626
|
-
const token = await resolveReporterToken();
|
|
627
|
-
if (!token) {
|
|
628
|
-
warn(
|
|
629
|
-
`skipped upload (no auth token found). Set ${REPORTER_TOKEN_ENV} or ARCHAL_TOKEN, or run \`archal login\` to populate ~/.archal/credentials.json.`
|
|
630
|
-
);
|
|
355
|
+
if (itemCount === 0) {
|
|
356
|
+
warn(`No ${itemLabel} received. Nothing to upload.`);
|
|
631
357
|
return;
|
|
632
358
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
sessionId,
|
|
637
|
-
projectName: project.config.projectName,
|
|
638
|
-
services: Object.keys(project.config.services),
|
|
639
|
-
results,
|
|
640
|
-
summary,
|
|
641
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
642
|
-
};
|
|
643
|
-
const res = await fetch(`${baseUrl}/api/test-results`, {
|
|
644
|
-
method: "POST",
|
|
645
|
-
headers: buildReporterHeaders(token),
|
|
646
|
-
body: JSON.stringify(payload)
|
|
647
|
-
});
|
|
648
|
-
if (res.ok) {
|
|
649
|
-
info(`Uploaded ${summary.passed}/${summary.total} tests to Archal Tests.`);
|
|
650
|
-
if (sessionId) {
|
|
651
|
-
try {
|
|
652
|
-
const syncRes = await fetch(`${baseUrl}/api/sessions/sync`, {
|
|
653
|
-
method: "POST",
|
|
654
|
-
headers: buildReporterHeaders(token),
|
|
655
|
-
body: JSON.stringify({
|
|
656
|
-
sessionId,
|
|
657
|
-
twinIds: Object.keys(project.config.services),
|
|
658
|
-
status: summary.failed > 0 ? "failed" : "ended",
|
|
659
|
-
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
660
|
-
runtimeStartedAt: new Date(Date.now() - summary.duration).toISOString(),
|
|
661
|
-
source: "test",
|
|
662
|
-
evidence: {
|
|
663
|
-
reportSummary: {
|
|
664
|
-
scenarioTitle: project.config.projectName,
|
|
665
|
-
satisfactionScore: summary.total > 0 ? Math.round(summary.passed / summary.total * 100) : null,
|
|
666
|
-
runCount: 1
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
})
|
|
670
|
-
});
|
|
671
|
-
if (!syncRes.ok) {
|
|
672
|
-
const body = await syncRes.text().catch(() => "");
|
|
673
|
-
warn(`Session sync failed (${syncRes.status}): ${body.slice(0, 200)}`);
|
|
674
|
-
}
|
|
675
|
-
} catch (syncErr) {
|
|
676
|
-
warn(
|
|
677
|
-
`Session sync failed: ${syncErr instanceof Error ? syncErr.message : String(syncErr)}`
|
|
678
|
-
);
|
|
679
|
-
}
|
|
680
|
-
} else {
|
|
681
|
-
warn(
|
|
682
|
-
"Uploaded test results, but could not link them to a session. The run may not appear in Activity."
|
|
683
|
-
);
|
|
684
|
-
}
|
|
685
|
-
} else {
|
|
686
|
-
const body = await res.text().catch(() => "");
|
|
687
|
-
warn(`Upload failed (${res.status}): ${body.slice(0, 200)}`);
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
async readSessionId(sessionKey) {
|
|
691
|
-
const { readFileSync: readFileSync2, readdirSync, statSync } = await import("fs");
|
|
692
|
-
const { dirname: dirname2, join: join2 } = await import("path");
|
|
693
|
-
if (sessionKey?.trim()) {
|
|
694
|
-
try {
|
|
695
|
-
const keyed = readFileSync2(getSessionIdFilePath(sessionKey), "utf-8").trim();
|
|
696
|
-
if (keyed) return keyed;
|
|
697
|
-
} catch {
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
if (sessionKey?.trim()) {
|
|
701
|
-
return void 0;
|
|
702
|
-
}
|
|
703
|
-
try {
|
|
704
|
-
const sessionsDir = dirname2(getSessionIdFilePath("any"));
|
|
705
|
-
const entries = readdirSync(sessionsDir);
|
|
706
|
-
let newest;
|
|
707
|
-
for (const name of entries) {
|
|
708
|
-
if (!name.endsWith(".session-id")) continue;
|
|
709
|
-
const path = join2(sessionsDir, name);
|
|
710
|
-
try {
|
|
711
|
-
const st = statSync(path);
|
|
712
|
-
if (!newest || st.mtimeMs > newest.mtimeMs) {
|
|
713
|
-
newest = { path, mtimeMs: st.mtimeMs };
|
|
714
|
-
}
|
|
715
|
-
} catch {
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
if (newest) {
|
|
719
|
-
const content = readFileSync2(newest.path, "utf-8").trim();
|
|
720
|
-
if (content) return content;
|
|
721
|
-
}
|
|
722
|
-
} catch {
|
|
723
|
-
}
|
|
724
|
-
return void 0;
|
|
359
|
+
diagnostic(
|
|
360
|
+
"skipped legacy test-result upload. Benchmark Results ingestion requires a Benchmark Run payload and no longer writes /api/test-results."
|
|
361
|
+
);
|
|
725
362
|
}
|
|
726
363
|
};
|
|
727
364
|
|
|
@@ -735,16 +372,16 @@ function classifySeed(value) {
|
|
|
735
372
|
if (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("/")) {
|
|
736
373
|
const ext = extname(trimmed).toLowerCase();
|
|
737
374
|
if (ext === ".json") return { type: "file", path: trimmed, format: "json" };
|
|
738
|
-
if (ext === ".
|
|
375
|
+
if (ext === ".sql") return { type: "file", path: trimmed, format: "sql" };
|
|
739
376
|
throw new Error(
|
|
740
|
-
`Seed file must be .json or .
|
|
377
|
+
`Seed file must be .json or .sql, got "${ext || "(no extension)"}": ${trimmed}
|
|
741
378
|
.json \u2192 exact clone state loaded directly
|
|
742
|
-
.
|
|
379
|
+
.sql \u2192 SQL state loaded through the clone state boundary`
|
|
743
380
|
);
|
|
744
381
|
}
|
|
745
|
-
if (trimmed.endsWith(".json") || trimmed.endsWith(".
|
|
382
|
+
if (trimmed.endsWith(".json") || trimmed.endsWith(".sql")) {
|
|
746
383
|
const ext = extname(trimmed).toLowerCase();
|
|
747
|
-
const format = ext === ".json" ? "json" : "
|
|
384
|
+
const format = ext === ".json" ? "json" : "sql";
|
|
748
385
|
return { type: "file", path: trimmed, format };
|
|
749
386
|
}
|
|
750
387
|
return { type: "named", name: trimmed };
|
|
@@ -926,11 +563,6 @@ function archalVitestProject(options, testOptions = {}) {
|
|
|
926
563
|
const test = buildTestConfig(options, testOptions);
|
|
927
564
|
return defineProject({ test });
|
|
928
565
|
}
|
|
929
|
-
function archalVitestConfig(options, testOptions = {}) {
|
|
930
|
-
const built = buildTestConfig(options, testOptions);
|
|
931
|
-
built.reporters = ["default", new ArchalReporter()];
|
|
932
|
-
return built;
|
|
933
|
-
}
|
|
934
566
|
function archalVitestRootConfig() {
|
|
935
567
|
markHelperCalled();
|
|
936
568
|
return {
|
|
@@ -991,7 +623,6 @@ function archalVitestWorkspace(projects) {
|
|
|
991
623
|
}
|
|
992
624
|
export {
|
|
993
625
|
ArchalReporter,
|
|
994
|
-
archalVitestConfig,
|
|
995
626
|
archalVitestProject,
|
|
996
627
|
archalVitestRootConfig,
|
|
997
628
|
archalVitestWorkspace,
|