@alexkroman1/aai-cli 8.2.0 → 9.0.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/dist/_dev-env.d.ts +15 -2
- package/dist/{_dev-server-Dt3TCjhj.mjs → _dev-server-BAR8Ya43.mjs} +182 -55
- package/dist/_dev-server.d.ts +5 -30
- package/dist/_dev-typecheck.d.ts +38 -0
- package/dist/_dev-vite-config.d.ts +74 -0
- package/dist/_e2e-test-utils.d.ts +33 -1
- package/dist/_init-BNotRgV1.mjs +163 -0
- package/dist/_resource-commands.d.ts +0 -1
- package/dist/{_slug-api-z-USxPtL.mjs → _slug-api--ZBmIGH-.mjs} +1 -1
- package/dist/{_studio-DR5yzZ1T.mjs → _studio-CKrsixd0.mjs} +2 -1
- package/dist/_studio.d.ts +3 -1
- package/dist/{build-BBxf8vom.mjs → build-DvDdSwAS.mjs} +1 -1
- package/dist/cli.mjs +26 -97
- package/dist/{delete-Dl_LSfvI.mjs → delete-8pjH8n8n.mjs} +3 -2
- package/dist/{deploy-DRC8jWr8.mjs → deploy-CjU3e-3P.mjs} +4 -4
- package/dist/{dev-QuuOOUtv.mjs → dev-dFzdvwa7.mjs} +3 -2
- package/dist/dev.d.ts +2 -0
- package/dist/{eval-DfQ8cm5p.mjs → eval-DwNwdqmS.mjs} +1 -1
- package/dist/{init-CUo87gcI.mjs → init-DK7GpIF9.mjs} +7 -6
- package/dist/{logs-CQ2T46dr.mjs → logs-D2Yhqe2w.mjs} +2 -2
- package/dist/scaffold/.env.example +7 -4
- package/dist/scaffold/CLAUDE.md +152 -97
- package/dist/scaffold/global.d.ts +12 -0
- package/dist/scaffold/package.json +4 -4
- package/dist/scaffold/server.mjs +49 -2
- package/dist/scaffold/tsconfig.json +1 -0
- package/dist/scaffold/vitest.config.ts +24 -0
- package/dist/{secret-Ct34kdgU.mjs → secret-CwgDLpSy.mjs} +2 -2
- package/dist/{studio-CzTzPqe1.mjs → studio-D3-HWIfk.mjs} +7 -5
- package/dist/templates/briefing-desk/agent.test.ts +3 -15
- package/dist/templates/briefing-desk/shared.ts +3 -3
- package/dist/templates/call-audit/agent.eval.test.ts +8 -1
- package/dist/templates/call-audit/agent.test.ts +0 -1
- package/dist/templates/call-audit/agent.ts +4 -3
- package/dist/templates/call-audit/client.tsx +4 -7
- package/dist/templates/call-audit/workflows/audit.ts +10 -1
- package/dist/templates/code-interpreter/agent.eval.test.ts +14 -43
- package/dist/templates/dispatch-center/agent.eval.test.ts +16 -26
- package/dist/templates/embedded-assets/agent.eval.test.ts +6 -16
- package/dist/templates/embedded-assets/agent.test.ts +3 -16
- package/dist/templates/health-assistant/agent.eval.test.ts +5 -15
- package/dist/templates/health-assistant/agent.test.ts +3 -14
- package/dist/templates/infocom-adventure/agent.eval.test.ts +6 -16
- package/dist/templates/infocom-adventure/agent.test.ts +3 -17
- package/dist/templates/link-digest/agent.test.ts +0 -14
- package/dist/templates/link-digest/agent.ts +9 -5
- package/dist/templates/link-digest/client.tsx +8 -6
- package/dist/templates/math-buddy/agent.eval.test.ts +11 -40
- package/dist/templates/night-owl/agent.eval.test.ts +15 -44
- package/dist/templates/night-owl/agent.test.ts +2 -15
- package/dist/templates/personal-finance/agent.eval.test.ts +10 -39
- package/dist/templates/pipeline-simple/agent.test.ts +73 -0
- package/dist/templates/pizza-ordering/agent.eval.test.ts +2 -17
- package/dist/templates/pizza-ordering/agent.test.ts +2 -17
- package/dist/templates/plan-and-execute/agent.eval.test.ts +2 -12
- package/dist/templates/plan-and-execute/agent.test.ts +3 -23
- package/dist/templates/plan-and-execute/shared.ts +1 -1
- package/dist/templates/podcast-digest/agent.test.ts +0 -1
- package/dist/templates/podcast-digest/client.tsx +3 -6
- package/dist/templates/podcast-digest/workflows/digest.ts +16 -8
- package/dist/templates/recap-workflow/agent.eval.test.ts +7 -14
- package/dist/templates/recap-workflow/agent.test.ts +3 -18
- package/dist/templates/recap-workflow/agent.ts +7 -5
- package/dist/templates/redline/agent.test.ts +0 -13
- package/dist/templates/redline/agent.ts +3 -2
- package/dist/templates/redline/client.tsx +9 -4
- package/dist/templates/research-workflow/agent.eval.test.ts +7 -14
- package/dist/templates/research-workflow/agent.test.ts +3 -15
- package/dist/templates/research-workflow/agent.ts +7 -5
- package/dist/templates/research-workflow/workflows/research.ts +8 -2
- package/dist/templates/retail/agent.eval.test.ts +10 -25
- package/dist/templates/retail/registry.test.ts +16 -13
- package/dist/templates/solo-rpg/agent.eval.test.ts +5 -15
- package/dist/templates/solo-rpg/agent.test.ts +17 -124
- package/dist/templates/solo-rpg/shared.ts +13 -61
- package/dist/templates/spoken-summary/agent.test.ts +0 -1
- package/dist/templates/spoken-summary/agent.ts +5 -4
- package/dist/templates/spoken-summary/client.tsx +4 -7
- package/dist/templates/support-line/agent.eval.test.ts +2 -12
- package/dist/templates/support-line/agent.test.ts +3 -22
- package/dist/templates/transcription-workflow/agent.ts +4 -4
- package/dist/templates/transcription-workflow/client.tsx +5 -5
- package/dist/templates/transcription-workflow/workflows/transcribe.ts +10 -1
- package/dist/templates/travel-concierge/agent.eval.test.ts +5 -15
- package/dist/templates/travel-concierge/agent.test.ts +3 -23
- package/dist/templates/web-researcher/agent.eval.test.ts +2 -5
- package/dist/test-CvwgeVSQ.mjs +181 -0
- package/dist/test.d.ts +33 -2
- package/package.json +4 -4
- package/dist/_init-BhEfQ6Yi.mjs +0 -93
- package/dist/storage-i_79ZwAR.mjs +0 -66
- package/dist/storage.d.ts +0 -22
- package/dist/templates/solo-rpg/tools/load_game.ts +0 -37
- package/dist/templates/solo-rpg/tools/save_game.ts +0 -32
- package/dist/test-BvyT6sAc.mjs +0 -104
package/dist/_init-BhEfQ6Yi.mjs
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { c as isEexist, l as readJson, p as writeJson } from "./_utils-B8QmtFhK.mjs";
|
|
3
|
-
import { r as isDevMode, t as getMonorepoRoot } from "./_agent-BTwZJu4P.mjs";
|
|
4
|
-
import { REPO_URL, downloadAndMergeTemplate } from "./_templates-4WcKOjS5.mjs";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import fs from "node:fs/promises";
|
|
7
|
-
//#region _init.ts
|
|
8
|
-
function readmeContent(slug) {
|
|
9
|
-
return `# ${slug}
|
|
10
|
-
|
|
11
|
-
A voice agent built with [aai](${REPO_URL}).
|
|
12
|
-
|
|
13
|
-
## Getting started
|
|
14
|
-
|
|
15
|
-
\`\`\`sh
|
|
16
|
-
npm install # Install dependencies
|
|
17
|
-
aai dev # Run locally (opens browser)
|
|
18
|
-
aai publish # Publish to production (and sync to the studio)
|
|
19
|
-
\`\`\`
|
|
20
|
-
|
|
21
|
-
## Secrets
|
|
22
|
-
|
|
23
|
-
Access secrets in your agent via \`ctx.env.MY_KEY\`.
|
|
24
|
-
|
|
25
|
-
**Local development** — add secrets to \`.env\` (auto-loaded by \`aai dev\`):
|
|
26
|
-
|
|
27
|
-
\`\`\`sh
|
|
28
|
-
ALPHA_VANTAGE_KEY=sk-abc123
|
|
29
|
-
MY_API_KEY=secret-value
|
|
30
|
-
\`\`\`
|
|
31
|
-
|
|
32
|
-
**Production** — set secrets on the server:
|
|
33
|
-
|
|
34
|
-
\`\`\`sh
|
|
35
|
-
aai secret put MY_KEY # Set a secret (prompts for value)
|
|
36
|
-
aai secret list # List secret names
|
|
37
|
-
aai secret delete MY_KEY # Remove a secret
|
|
38
|
-
\`\`\`
|
|
39
|
-
|
|
40
|
-
`;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Map from npm package name to directory name under packages/.
|
|
44
|
-
* Used to rewrite published version ranges to link: paths in dev mode.
|
|
45
|
-
*/
|
|
46
|
-
const WORKSPACE_PKG_DIRS = {
|
|
47
|
-
"@alexkroman1/aai": "aai",
|
|
48
|
-
"@alexkroman1/aai-cli": "aai-cli",
|
|
49
|
-
"@alexkroman1/aai-runtime": "aai-runtime",
|
|
50
|
-
"@alexkroman1/aai-ui": "aai-ui"
|
|
51
|
-
};
|
|
52
|
-
/** Rewrite workspace deps to link: paths so pnpm links to local source. */
|
|
53
|
-
async function patchPackageJsonForWorkspace(targetDir) {
|
|
54
|
-
const pkgPath = path.join(targetDir, "package.json");
|
|
55
|
-
const pkgJson = await readJson(pkgPath);
|
|
56
|
-
if (!pkgJson) return;
|
|
57
|
-
pkgJson.name = path.basename(targetDir);
|
|
58
|
-
delete pkgJson.packageManager;
|
|
59
|
-
const root = getMonorepoRoot();
|
|
60
|
-
if (!root) return;
|
|
61
|
-
const packagesDir = path.join(root, "packages");
|
|
62
|
-
for (const field of ["dependencies", "devDependencies"]) {
|
|
63
|
-
const deps = pkgJson[field];
|
|
64
|
-
if (!deps) continue;
|
|
65
|
-
for (const key of Object.keys(deps)) {
|
|
66
|
-
const dir = WORKSPACE_PKG_DIRS[key];
|
|
67
|
-
if (dir) deps[key] = `link:${path.relative(targetDir, path.join(packagesDir, dir))}`;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
await writeJson(pkgPath, pkgJson);
|
|
71
|
-
}
|
|
72
|
-
async function runInit(opts) {
|
|
73
|
-
const { targetDir, template } = opts;
|
|
74
|
-
await downloadAndMergeTemplate(template, targetDir);
|
|
75
|
-
if (isDevMode()) {
|
|
76
|
-
await patchPackageJsonForWorkspace(targetDir);
|
|
77
|
-
try {
|
|
78
|
-
await fs.unlink(path.join(targetDir, ".npmrc"));
|
|
79
|
-
} catch {}
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
await fs.copyFile(path.join(targetDir, ".env.example"), path.join(targetDir, ".env"), fs.constants.COPYFILE_EXCL);
|
|
83
|
-
} catch {}
|
|
84
|
-
const readmePath = path.join(targetDir, "README.md");
|
|
85
|
-
const slug = path.basename(path.resolve(targetDir));
|
|
86
|
-
try {
|
|
87
|
-
await fs.writeFile(readmePath, readmeContent(slug), { flag: "wx" });
|
|
88
|
-
} catch (err) {
|
|
89
|
-
if (!isEexist(err)) throw err;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
//#endregion
|
|
93
|
-
export { runInit };
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { a as ok, n as fail } from "./_output-CKkmWs7i.mjs";
|
|
3
|
-
import { n as log } from "./_ui-u7T4YooX.mjs";
|
|
4
|
-
import { f as checkedResponse } from "./_studio-DR5yzZ1T.mjs";
|
|
5
|
-
import { n as slugRequest } from "./_slug-api-z-USxPtL.mjs";
|
|
6
|
-
import { isRecord } from "@alexkroman1/aai/utils";
|
|
7
|
-
import * as p from "@clack/prompts";
|
|
8
|
-
//#region storage.ts
|
|
9
|
-
/**
|
|
10
|
-
* The storage route's answer, CHECKED — every caller here reads `enabled` and
|
|
11
|
-
* a body without it reported "Storage is disabled for <slug>" (and returned
|
|
12
|
-
* `enabled: undefined` to a script) for a server that never said so. See
|
|
13
|
-
* `checkedResponse`.
|
|
14
|
-
*/
|
|
15
|
-
async function storageRequest(cwd, init, server) {
|
|
16
|
-
const { data, slug } = await slugRequest(cwd, "/storage", {
|
|
17
|
-
...init,
|
|
18
|
-
action: "storage"
|
|
19
|
-
}, server);
|
|
20
|
-
return {
|
|
21
|
-
enabled: checkedResponse(data, (value) => isRecord(value) && typeof value.enabled === "boolean", `the storage route for ${slug}`).enabled,
|
|
22
|
-
slug
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
async function executeStorageStatus(cwd, server) {
|
|
26
|
-
const { enabled, slug } = await storageRequest(cwd, void 0, server);
|
|
27
|
-
if (enabled) log.info(`Storage is enabled for ${slug}`);
|
|
28
|
-
else log.info(`Storage is disabled for ${slug}. Use \`aai storage enable\` to turn it on.`);
|
|
29
|
-
return ok({
|
|
30
|
-
slug,
|
|
31
|
-
enabled
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
async function executeStorageEnable(cwd, server) {
|
|
35
|
-
const { enabled, slug } = await storageRequest(cwd, { method: "POST" }, server);
|
|
36
|
-
log.success(`Storage enabled for ${slug}`);
|
|
37
|
-
log.info("Tool code can now use ctx.db.query(sql, params).");
|
|
38
|
-
return ok({
|
|
39
|
-
slug,
|
|
40
|
-
enabled
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Disable storage — destructive: the server DROPS the app's database schema
|
|
45
|
-
* and all its data. On a TTY this requires interactive confirmation; without
|
|
46
|
-
* a TTY it refuses unless `--force` is passed, so a script can never drop
|
|
47
|
-
* data by accident.
|
|
48
|
-
*/
|
|
49
|
-
async function executeStorageDisable(cwd, opts = {}) {
|
|
50
|
-
if (!opts.force) {
|
|
51
|
-
if (!(opts.isTTY ?? Boolean(process.stdin.isTTY && process.stdout.isTTY))) return fail("confirmation_required", "Disabling storage drops the app's database schema and ALL its data.", "Re-run with --force to disable storage without confirmation.");
|
|
52
|
-
const confirmed = await p.confirm({ message: "Disable storage? This DROPS the app's database schema and all its data." });
|
|
53
|
-
if (p.isCancel(confirmed) || confirmed !== true) {
|
|
54
|
-
log.info("Cancelled. Storage was left unchanged.");
|
|
55
|
-
return fail("cancelled", "Disable cancelled");
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
const { enabled, slug } = await storageRequest(cwd, { method: "DELETE" }, opts.server);
|
|
59
|
-
log.success(`Storage disabled for ${slug} — database schema and data dropped`);
|
|
60
|
-
return ok({
|
|
61
|
-
slug,
|
|
62
|
-
enabled
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
//#endregion
|
|
66
|
-
export { executeStorageDisable, executeStorageEnable, executeStorageStatus };
|
package/dist/storage.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { type CommandResult } from "./_output.ts";
|
|
2
|
-
type StorageStatusData = {
|
|
3
|
-
slug: string;
|
|
4
|
-
enabled: boolean;
|
|
5
|
-
};
|
|
6
|
-
export declare function executeStorageStatus(cwd: string, server: string | undefined): Promise<CommandResult<StorageStatusData>>;
|
|
7
|
-
export declare function executeStorageEnable(cwd: string, server: string | undefined): Promise<CommandResult<StorageStatusData>>;
|
|
8
|
-
export type StorageDisableOpts = {
|
|
9
|
-
server?: string | undefined;
|
|
10
|
-
/** Skip the confirmation prompt (required in non-interactive runs). */
|
|
11
|
-
force?: boolean | undefined;
|
|
12
|
-
/** TTY override for testing. Defaults to real stdin+stdout TTY state. */
|
|
13
|
-
isTTY?: boolean | undefined;
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* Disable storage — destructive: the server DROPS the app's database schema
|
|
17
|
-
* and all its data. On a TTY this requires interactive confirmation; without
|
|
18
|
-
* a TTY it refuses unless `--force` is passed, so a script can never drop
|
|
19
|
-
* data by accident.
|
|
20
|
-
*/
|
|
21
|
-
export declare function executeStorageDisable(cwd: string, opts?: StorageDisableOpts): Promise<CommandResult<StorageStatusData>>;
|
|
22
|
-
export {};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { tool } from "@alexkroman1/aai";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import type { GameState } from "../shared.ts";
|
|
4
|
-
import { gameSlot, loadState, saveSlotKey, saveSlotParam, storyFlow } from "../shared.ts";
|
|
5
|
-
|
|
6
|
-
// Requires storage — `aai storage enable` (or DATABASE_URL in .env under
|
|
7
|
-
// `aai dev`); the rest of the game works without it.
|
|
8
|
-
export default tool({
|
|
9
|
-
description: "Load a previously saved game.",
|
|
10
|
-
inputSchema: z.object({ slot: saveSlotParam }),
|
|
11
|
-
async execute(args, ctx) {
|
|
12
|
-
const saved = await loadState<GameState>(ctx, saveSlotKey(args.slot));
|
|
13
|
-
if (!saved) return { error: "No save found." };
|
|
14
|
-
gameSlot.set(ctx, saved);
|
|
15
|
-
|
|
16
|
-
// The flow has to be restored alongside the campaign, or a loaded game
|
|
17
|
-
// would still be sitting in `awaitingSetup` with every roll tool refusing.
|
|
18
|
-
// A save is written from a settled scene, so it resumes at `awaitingRoll`
|
|
19
|
-
// rather than with a standing roll — `lastRoll` may be set, but a burn
|
|
20
|
-
// window that survived a save and a reload is not one a player is still in.
|
|
21
|
-
storyFlow.reset(ctx);
|
|
22
|
-
let at = storyFlow.send(ctx, { type: "SETUP" });
|
|
23
|
-
if (saved.gameOver) at = storyFlow.send(ctx, { type: "DOWNED" });
|
|
24
|
-
|
|
25
|
-
return {
|
|
26
|
-
loaded: true,
|
|
27
|
-
// The position spread verbatim — the same keys every gated tool reports.
|
|
28
|
-
...at,
|
|
29
|
-
playerName: saved.playerName,
|
|
30
|
-
characterConcept: saved.characterConcept,
|
|
31
|
-
settingGenre: saved.settingGenre,
|
|
32
|
-
sceneCount: saved.sceneCount,
|
|
33
|
-
currentLocation: saved.currentLocation,
|
|
34
|
-
initialized: saved.initialized,
|
|
35
|
-
};
|
|
36
|
-
},
|
|
37
|
-
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { gameSlot, saveSlotKey, saveSlotParam, saveState, storyFlow } from "../shared.ts";
|
|
3
|
-
|
|
4
|
-
// Requires storage — `aai storage enable` (or DATABASE_URL in .env under
|
|
5
|
-
// `aai dev`); the rest of the game works without it.
|
|
6
|
-
//
|
|
7
|
-
/**
|
|
8
|
-
* Gated on `playing` or `gameOver` — i.e. anything but `awaitingSetup`.
|
|
9
|
-
*
|
|
10
|
-
* Saving before a character exists writes an empty campaign under a slot name,
|
|
11
|
-
* and `load_game` would then cheerfully restore it over a real game. That is a
|
|
12
|
-
* position, so it is a `when`.
|
|
13
|
-
*
|
|
14
|
-
* The body AWAITS, which a flow tool allows: only `slot.updateTool` must be
|
|
15
|
-
* synchronous, because its draft is stored when it returns. What a save needs is
|
|
16
|
-
* the frozen value, and `gameSlot.get` is what hands it over.
|
|
17
|
-
*/
|
|
18
|
-
export default storyFlow.tool({
|
|
19
|
-
description: "Save current game to persistent storage.",
|
|
20
|
-
inputSchema: z.object({ slot: saveSlotParam }),
|
|
21
|
-
when: ["playing", "gameOver"],
|
|
22
|
-
async execute(args, ctx) {
|
|
23
|
-
const game = gameSlot.get(ctx);
|
|
24
|
-
await saveState(ctx, saveSlotKey(args.slot), game);
|
|
25
|
-
return {
|
|
26
|
-
saved: true,
|
|
27
|
-
slot: args.slot ?? "autosave",
|
|
28
|
-
name: game.playerName,
|
|
29
|
-
scene: game.sceneCount,
|
|
30
|
-
};
|
|
31
|
-
},
|
|
32
|
-
});
|
package/dist/test-BvyT6sAc.mjs
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { a as ok, n as fail } from "./_output-CKkmWs7i.mjs";
|
|
3
|
-
import { n as log } from "./_ui-u7T4YooX.mjs";
|
|
4
|
-
import { i as errorCode, o as errorMessage$1, r as binFromPackageJson } from "./_utils-B8QmtFhK.mjs";
|
|
5
|
-
import { createRequire } from "node:module";
|
|
6
|
-
import { existsSync } from "node:fs";
|
|
7
|
-
import path from "node:path";
|
|
8
|
-
import { omitUndefined } from "@alexkroman1/aai/utils";
|
|
9
|
-
import { execaSync } from "execa";
|
|
10
|
-
//#region test.ts
|
|
11
|
-
/**
|
|
12
|
-
* `aai test` — run agent tests via vitest.
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* Resolve the agent project's own vitest binary so tests run without the
|
|
16
|
-
* npx resolution overhead (and its potential network fetch of vitest).
|
|
17
|
-
*
|
|
18
|
-
* Resolves `vitest/package.json` from the agent directory, derives the bin
|
|
19
|
-
* script, and runs it with the current Node executable. Falls back to
|
|
20
|
-
* `npx vitest` only when no local install is resolvable.
|
|
21
|
-
*/
|
|
22
|
-
function resolveVitestCommand(cwd, resolve = createRequire(path.join(cwd, "package.json")).resolve) {
|
|
23
|
-
try {
|
|
24
|
-
const bin = binFromPackageJson(resolve("vitest/package.json"), "vitest");
|
|
25
|
-
if (bin) return {
|
|
26
|
-
cmd: process.execPath,
|
|
27
|
-
args: [bin]
|
|
28
|
-
};
|
|
29
|
-
} catch {}
|
|
30
|
-
return {
|
|
31
|
-
cmd: "npx",
|
|
32
|
-
args: ["vitest"]
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
/** The files `aai test` runs, in preference order. */
|
|
36
|
-
const TEST_FILES = ["agent.test.ts", "agent.test.js"];
|
|
37
|
-
/**
|
|
38
|
-
* Run vitest over one of `candidates` in the given project directory.
|
|
39
|
-
*
|
|
40
|
-
* Returns `true` if it ran, `false` if none of the candidate files exists.
|
|
41
|
-
* Throws on failure.
|
|
42
|
-
*
|
|
43
|
-
* A vitest FILTER, not an include glob: the argument is matched as a substring
|
|
44
|
-
* against the paths vitest's own include globs already found, which is why the
|
|
45
|
-
* candidates are named `*.test.ts` — `agent.test.ts` cannot match
|
|
46
|
-
* `agent.eval.test.ts` and vice versa, so the two commands stay disjoint
|
|
47
|
-
* without either one having to exclude the other's file.
|
|
48
|
-
*/
|
|
49
|
-
function runVitest(cwd, opts = { candidates: TEST_FILES }) {
|
|
50
|
-
const testFile = opts.candidates.find((name) => existsSync(path.join(cwd, name)));
|
|
51
|
-
if (!testFile) return false;
|
|
52
|
-
const { cmd, args } = resolveVitestCommand(cwd);
|
|
53
|
-
execaSync(cmd, [
|
|
54
|
-
...args,
|
|
55
|
-
"run",
|
|
56
|
-
"--root",
|
|
57
|
-
".",
|
|
58
|
-
...opts.extraArgs ?? [],
|
|
59
|
-
testFile
|
|
60
|
-
], {
|
|
61
|
-
cwd,
|
|
62
|
-
stdio: "inherit",
|
|
63
|
-
...omitUndefined({ env: opts.env ? {
|
|
64
|
-
...process.env,
|
|
65
|
-
...opts.env
|
|
66
|
-
} : void 0 })
|
|
67
|
-
});
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Classify a {@link runVitest} failure. execaSync throws an ENOENT-coded
|
|
72
|
-
* error when the binary itself couldn't be spawned (infrastructure problem)
|
|
73
|
-
* and an exit-code error when vitest ran and the tests failed.
|
|
74
|
-
*/
|
|
75
|
-
function classifyVitestError(err, label = "Tests") {
|
|
76
|
-
if (errorCode(err) === "ENOENT") return {
|
|
77
|
-
code: "spawn_failed",
|
|
78
|
-
message: `Could not launch the test runner: ${errorMessage$1(err)} — is the binary on your PATH?`
|
|
79
|
-
};
|
|
80
|
-
return {
|
|
81
|
-
code: "test_failed",
|
|
82
|
-
message: `${label} failed: ${errorMessage$1(err)}`
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
/** Execute agent tests and return structured result. */
|
|
86
|
-
async function executeTest(cwd) {
|
|
87
|
-
log.step("Running agent tests");
|
|
88
|
-
try {
|
|
89
|
-
if (!runVitest(cwd)) {
|
|
90
|
-
log.info("No test file found. Create agent.test.ts to add tests.");
|
|
91
|
-
return ok({
|
|
92
|
-
passed: true,
|
|
93
|
-
skipped: true
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
log.success("Tests passed");
|
|
97
|
-
return ok({ passed: true });
|
|
98
|
-
} catch (err) {
|
|
99
|
-
const { code, message } = classifyVitestError(err);
|
|
100
|
-
return fail(code, message);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
//#endregion
|
|
104
|
-
export { classifyVitestError, executeTest, runVitest };
|