@alexkroman1/aai-cli 1.8.2 → 1.9.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/_agent-cHGzbDVG.mjs +108 -0
- package/dist/_api-client-yJD7xGfd.mjs +47 -0
- package/dist/_bundler-BRQFvx00.mjs +62 -0
- package/dist/_config-zV70t71V.mjs +125 -0
- package/dist/_dev-server-CBrIeDoI.mjs +214 -0
- package/dist/{_init-Bh5etNEh.mjs → _init-y7K-dzni.mjs} +17 -43
- package/dist/{_output-vvoR6N1x.mjs → _output-fy2bXRNb.mjs} +12 -19
- package/dist/{_ui-nOp7hFVy.mjs → _ui-YqQ6Fi8K.mjs} +14 -20
- package/dist/_utils-BeU10C7O.mjs +55 -0
- package/dist/_vite-env-CmXG4ZAu.mjs +31 -0
- package/dist/cli.mjs +41 -40
- package/dist/client-bundler-D4VKqCxw.mjs +125 -0
- package/dist/client-bundler.mjs +3 -0
- package/dist/{delete-BEC2ZXbk.mjs → delete-DE35s3d5.mjs} +7 -8
- package/dist/{deploy-D8uMWSDH.mjs → deploy-C0LittrE.mjs} +20 -18
- package/dist/{dev-D2EwGHYi.mjs → dev-BvjnXK2x.mjs} +10 -3
- package/dist/{init-GWTdzrrm.mjs → init-kYsRaf7e.mjs} +29 -75
- package/dist/{secret-DlSN4FdF.mjs → secret-D-90QtDQ.mjs} +14 -16
- package/dist/{test-BLYrV5ap.mjs → test-C6jwCnFu.mjs} +30 -5
- package/dist/types.mjs +2 -0
- package/dist/worker-bundler.mjs +68 -0
- package/package.json +25 -14
- package/dist/_agent-De8f1JdZ.mjs +0 -38
- package/dist/_api-client-Cf9Lv2Rw.mjs +0 -46
- package/dist/_bundler-CMMSjh9o.mjs +0 -143
- package/dist/_config-B0FXR5GQ.mjs +0 -77
- package/dist/_default-html-BasMCgr9.mjs +0 -62
- package/dist/_dev-server-DMFyqc9v.mjs +0 -143
- package/dist/_utils-DZo3_J_v.mjs +0 -23
- package/dist/rolldown-runtime-uZa2dNXj.mjs +0 -15
- /package/dist/{_server-common-BVkNI-o4.mjs → _server-common-QpOTRZgi.mjs} +0 -0
|
@@ -1,72 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { n as resolveCwd,
|
|
5
|
-
import {
|
|
2
|
+
import { i as ok } from "./_output-fy2bXRNb.mjs";
|
|
3
|
+
import { a as unwrapCancel, n as log } from "./_ui-YqQ6Fi8K.mjs";
|
|
4
|
+
import { a as readJson, n as errorMessage, o as resolveCwd, r as fileExists } from "./_utils-BeU10C7O.mjs";
|
|
5
|
+
import { i as isDevMode, n as getMonorepoRoot } from "./_agent-cHGzbDVG.mjs";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import * as p from "@clack/prompts";
|
|
8
8
|
import { colorize } from "consola/utils";
|
|
9
|
-
import
|
|
10
|
-
import { execFile } from "node:child_process";
|
|
11
|
-
import { promisify } from "node:util";
|
|
9
|
+
import { execa } from "execa";
|
|
12
10
|
//#region init.ts
|
|
13
|
-
/**
|
|
14
|
-
* Format an install error so the user sees what actually went wrong.
|
|
15
|
-
* pnpm writes failures to stdout (not stderr), and Node's execFile error
|
|
16
|
-
* message is only "Command failed: ..." — so we append both streams.
|
|
17
|
-
*/
|
|
18
|
-
function formatInstallError(err) {
|
|
19
|
-
if (!(err instanceof Error)) return String(err);
|
|
20
|
-
const parts = [err.message];
|
|
21
|
-
const stderr = err.stderr?.trim();
|
|
22
|
-
const stdout = err.stdout?.trim();
|
|
23
|
-
if (stderr) parts.push(stderr);
|
|
24
|
-
if (stdout) parts.push(stdout);
|
|
25
|
-
return parts.join("\n");
|
|
26
|
-
}
|
|
27
|
-
const execFileAsync = promisify(execFile);
|
|
28
11
|
const DEFAULT_PROJECT_NAME = "my-voice-agent";
|
|
29
12
|
/** Prompt for project name or return default when --yes is set. */
|
|
30
13
|
async function promptProjectName(yes) {
|
|
31
14
|
if (yes) return DEFAULT_PROJECT_NAME;
|
|
32
|
-
|
|
15
|
+
return unwrapCancel(await p.text({
|
|
33
16
|
message: "What is your project named?",
|
|
34
17
|
placeholder: DEFAULT_PROJECT_NAME,
|
|
35
18
|
defaultValue: DEFAULT_PROJECT_NAME
|
|
36
|
-
});
|
|
37
|
-
if (p.isCancel(result)) {
|
|
38
|
-
p.cancel("Setup cancelled");
|
|
39
|
-
process.exit(0);
|
|
40
|
-
}
|
|
41
|
-
return result || DEFAULT_PROJECT_NAME;
|
|
19
|
+
})) || DEFAULT_PROJECT_NAME;
|
|
42
20
|
}
|
|
43
21
|
/** Enable corepack so pnpm is available (scaffold declares packageManager: pnpm). */
|
|
44
22
|
async function ensurePnpm() {
|
|
45
|
-
|
|
46
|
-
await execFileAsync("corepack", ["enable"]);
|
|
47
|
-
} catch {}
|
|
23
|
+
await execa("corepack", ["enable"], { reject: false });
|
|
48
24
|
}
|
|
49
25
|
/** Check if the project has any dependencies to install. */
|
|
50
26
|
async function hasDeps(cwd) {
|
|
51
27
|
if (await fileExists(path.join(cwd, "node_modules"))) return false;
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
pkgJson = JSON.parse(await fs.readFile(path.join(cwd, "package.json"), "utf-8"));
|
|
55
|
-
} catch {
|
|
56
|
-
pkgJson = {};
|
|
57
|
-
}
|
|
28
|
+
const pkgJson = await readJson(path.join(cwd, "package.json")) ?? {};
|
|
58
29
|
const deps = Object.keys(pkgJson.dependencies ?? {});
|
|
59
30
|
const devDeps = Object.keys(pkgJson.devDependencies ?? {});
|
|
60
31
|
return deps.length > 0 || devDeps.length > 0;
|
|
61
32
|
}
|
|
62
33
|
/** Check whether the safe-chain binary is on PATH. */
|
|
63
34
|
async function hasSafeChain() {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return true;
|
|
67
|
-
} catch {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
35
|
+
const { failed } = await execa("safe-chain", ["--version"], { reject: false });
|
|
36
|
+
return !failed;
|
|
70
37
|
}
|
|
71
38
|
/** Build the command + args for running pnpm, routing through safe-chain when available. */
|
|
72
39
|
async function resolvePnpmCommand(checkSafeChain = hasSafeChain) {
|
|
@@ -83,30 +50,22 @@ async function resolvePnpmCommand(checkSafeChain = hasSafeChain) {
|
|
|
83
50
|
async function runPnpmInstall(cwd) {
|
|
84
51
|
const { cmd, args } = await resolvePnpmCommand();
|
|
85
52
|
const pnpmArgs = isDevMode() ? ["install"] : ["install", "--ignore-workspace"];
|
|
86
|
-
await
|
|
53
|
+
await execa(cmd, [...args, ...pnpmArgs], { cwd });
|
|
87
54
|
}
|
|
88
55
|
/** Install deps with pnpm. Returns true on success (or no deps to install). */
|
|
89
56
|
async function installDeps(cwd, silent) {
|
|
90
57
|
if (!await hasDeps(cwd)) return true;
|
|
91
58
|
await ensurePnpm();
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return true;
|
|
95
|
-
} catch (err) {
|
|
96
|
-
log$1.warn(`pnpm install failed: ${formatInstallError(err)}`);
|
|
97
|
-
log$1.warn("Run `corepack enable && pnpm install` manually in the project directory.");
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
const s = p.spinner();
|
|
101
|
-
s.start("Installing dependencies with pnpm");
|
|
59
|
+
const s = silent ? void 0 : p.spinner();
|
|
60
|
+
s?.start("Installing dependencies with pnpm");
|
|
102
61
|
try {
|
|
103
62
|
await runPnpmInstall(cwd);
|
|
104
|
-
s
|
|
63
|
+
s?.stop("Dependencies installed");
|
|
105
64
|
return true;
|
|
106
65
|
} catch (err) {
|
|
107
|
-
s
|
|
108
|
-
log
|
|
109
|
-
log
|
|
66
|
+
s?.stop("Dependency install failed");
|
|
67
|
+
log.warn(`pnpm install failed: ${errorMessage(err)}`);
|
|
68
|
+
log.warn("Run `corepack enable && pnpm install` manually in the project directory.");
|
|
110
69
|
return false;
|
|
111
70
|
}
|
|
112
71
|
}
|
|
@@ -114,17 +73,12 @@ async function installDeps(cwd, silent) {
|
|
|
114
73
|
function resolveTargetDir(dir) {
|
|
115
74
|
return path.resolve(resolveCwd(), dir);
|
|
116
75
|
}
|
|
117
|
-
/** Resolve the deploy server — in dev mode, default to localhost. */
|
|
118
|
-
function resolveDeployServer(explicit, monorepoRoot) {
|
|
119
|
-
return explicit ?? (monorepoRoot ? "http://localhost:8080" : void 0);
|
|
120
|
-
}
|
|
121
76
|
/** Run deploy after init and return deploy metadata if successful. */
|
|
122
|
-
async function tryDeploy(cwd, server
|
|
123
|
-
const
|
|
124
|
-
const { executeDeploy } = await import("./deploy-D8uMWSDH.mjs");
|
|
77
|
+
async function tryDeploy(cwd, server) {
|
|
78
|
+
const { executeDeploy } = await import("./deploy-C0LittrE.mjs");
|
|
125
79
|
const result = await executeDeploy({
|
|
126
80
|
cwd,
|
|
127
|
-
...
|
|
81
|
+
...server ? { server } : {}
|
|
128
82
|
});
|
|
129
83
|
return result.ok ? {
|
|
130
84
|
slug: result.data.slug,
|
|
@@ -133,7 +87,7 @@ async function tryDeploy(cwd, server, monorepoRoot) {
|
|
|
133
87
|
}
|
|
134
88
|
/** Scaffold the project, optionally showing a spinner. */
|
|
135
89
|
async function scaffoldProject(dir, cwd, template, silent) {
|
|
136
|
-
const { runInit } = await import("./_init-
|
|
90
|
+
const { runInit } = await import("./_init-y7K-dzni.mjs");
|
|
137
91
|
if (silent) {
|
|
138
92
|
await runInit({
|
|
139
93
|
targetDir: cwd,
|
|
@@ -151,12 +105,12 @@ async function scaffoldProject(dir, cwd, template, silent) {
|
|
|
151
105
|
}
|
|
152
106
|
/** Print post-init instructions. */
|
|
153
107
|
function printPostInitInfo(cwd, monorepoRoot) {
|
|
154
|
-
log
|
|
155
|
-
if (monorepoRoot) log
|
|
156
|
-
log
|
|
108
|
+
log.success(`Created ${cwd}`);
|
|
109
|
+
if (monorepoRoot) log.info("Dev mode: project linked to workspace packages");
|
|
110
|
+
log.info(`Next: cd ${cwd} && aai dev`);
|
|
157
111
|
}
|
|
158
112
|
async function executeInit(opts, extra) {
|
|
159
|
-
const suppressUi = extra?.
|
|
113
|
+
const suppressUi = extra?.silent;
|
|
160
114
|
if (!suppressUi) p.intro(colorize("cyanBright", "Create a new voice agent"));
|
|
161
115
|
const dir = opts.dir ?? await promptProjectName(opts.yes);
|
|
162
116
|
const monorepoRoot = getMonorepoRoot();
|
|
@@ -168,9 +122,9 @@ async function executeInit(opts, extra) {
|
|
|
168
122
|
let deployed = false;
|
|
169
123
|
let slug;
|
|
170
124
|
let url;
|
|
171
|
-
if (!installed) log
|
|
172
|
-
else if (!
|
|
173
|
-
const deployInfo = await tryDeploy(cwd, opts.server
|
|
125
|
+
if (!installed) log.warn("Skipping deploy because dependencies were not installed.");
|
|
126
|
+
else if (!opts.skipDeploy) {
|
|
127
|
+
const deployInfo = await tryDeploy(cwd, opts.server);
|
|
174
128
|
if (deployInfo) {
|
|
175
129
|
deployed = true;
|
|
176
130
|
slug = deployInfo.slug;
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { getServerInfo } from "./_agent-
|
|
5
|
-
import { t as
|
|
2
|
+
import { i as ok, n as fail } from "./_output-fy2bXRNb.mjs";
|
|
3
|
+
import { n as log } from "./_ui-YqQ6Fi8K.mjs";
|
|
4
|
+
import { r as getServerInfo } from "./_agent-cHGzbDVG.mjs";
|
|
5
|
+
import { t as apiRequest } from "./_api-client-yJD7xGfd.mjs";
|
|
6
6
|
import * as p from "@clack/prompts";
|
|
7
|
+
import { text } from "node:stream/consumers";
|
|
7
8
|
//#region secret.ts
|
|
8
9
|
async function secretRequest(cwd, pathSuffix, init, server) {
|
|
9
10
|
const { serverUrl, slug, apiKey } = await getServerInfo(cwd, server);
|
|
10
11
|
return {
|
|
11
|
-
|
|
12
|
+
data: await apiRequest(`${serverUrl}/${slug}/secret${pathSuffix}`, {
|
|
12
13
|
...init,
|
|
13
14
|
apiKey,
|
|
14
15
|
action: "secret"
|
|
@@ -18,9 +19,7 @@ async function secretRequest(cwd, pathSuffix, init, server) {
|
|
|
18
19
|
}
|
|
19
20
|
/** Read secret value from stdin (for non-TTY / piped input). */
|
|
20
21
|
async function readStdin() {
|
|
21
|
-
|
|
22
|
-
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
23
|
-
return Buffer.concat(chunks).toString("utf-8").trim();
|
|
22
|
+
return (await text(process.stdin)).trim();
|
|
24
23
|
}
|
|
25
24
|
/**
|
|
26
25
|
* Execute secret put. If `value` is provided, use it directly (non-TTY path).
|
|
@@ -36,23 +35,22 @@ async function executeSecretPut(cwd, name, value, server) {
|
|
|
36
35
|
}
|
|
37
36
|
const { slug } = await secretRequest(cwd, "", {
|
|
38
37
|
method: "PUT",
|
|
39
|
-
body:
|
|
38
|
+
body: { [name]: secretValue }
|
|
40
39
|
}, server);
|
|
41
|
-
log
|
|
40
|
+
log.success(`Set ${name} for ${slug}`);
|
|
42
41
|
return ok({ name });
|
|
43
42
|
}
|
|
44
43
|
async function executeSecretDelete(cwd, name, server) {
|
|
45
44
|
const { slug } = await secretRequest(cwd, `/${name}`, { method: "DELETE" }, server);
|
|
46
|
-
log
|
|
45
|
+
log.success(`Deleted ${name} from ${slug}`);
|
|
47
46
|
return ok({ name });
|
|
48
47
|
}
|
|
49
48
|
async function executeSecretList(cwd, server) {
|
|
50
|
-
const {
|
|
51
|
-
|
|
52
|
-
if (vars.length === 0) log$1.info("No secrets set. Use `aai secret put <name>` to add one.");
|
|
49
|
+
const { data: { vars } } = await secretRequest(cwd, "", void 0, server);
|
|
50
|
+
if (vars.length === 0) log.info("No secrets set. Use `aai secret put <name>` to add one.");
|
|
53
51
|
else {
|
|
54
|
-
log
|
|
55
|
-
for (const v of vars) log
|
|
52
|
+
log.message(`${vars.length} secret${vars.length === 1 ? "" : "s"}:`);
|
|
53
|
+
for (const v of vars) log.message(` ${v}`);
|
|
56
54
|
}
|
|
57
55
|
return ok({ secrets: vars });
|
|
58
56
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { i as ok, n as fail } from "./_output-fy2bXRNb.mjs";
|
|
3
|
+
import { n as log } from "./_ui-YqQ6Fi8K.mjs";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
6
|
import path from "node:path";
|
|
6
7
|
import { execFileSync } from "node:child_process";
|
|
7
8
|
//#region test.ts
|
|
@@ -9,6 +10,29 @@ import { execFileSync } from "node:child_process";
|
|
|
9
10
|
* `aai test` — run agent tests via vitest.
|
|
10
11
|
*/
|
|
11
12
|
/**
|
|
13
|
+
* Resolve the agent project's own vitest binary so tests run without the
|
|
14
|
+
* npx resolution overhead (and its potential network fetch of vitest).
|
|
15
|
+
*
|
|
16
|
+
* Resolves `vitest/package.json` from the agent directory, derives the bin
|
|
17
|
+
* script, and runs it with the current Node executable. Falls back to
|
|
18
|
+
* `npx vitest` only when no local install is resolvable.
|
|
19
|
+
*/
|
|
20
|
+
function resolveVitestCommand(cwd, resolve = createRequire(path.join(cwd, "package.json")).resolve) {
|
|
21
|
+
try {
|
|
22
|
+
const pkgPath = resolve("vitest/package.json");
|
|
23
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
24
|
+
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.vitest;
|
|
25
|
+
if (bin) return {
|
|
26
|
+
cmd: process.execPath,
|
|
27
|
+
args: [path.join(path.dirname(pkgPath), bin)]
|
|
28
|
+
};
|
|
29
|
+
} catch {}
|
|
30
|
+
return {
|
|
31
|
+
cmd: "npx",
|
|
32
|
+
args: ["vitest"]
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
12
36
|
* Run vitest in the given project directory.
|
|
13
37
|
*
|
|
14
38
|
* Returns `true` if tests passed, `false` if no test files exist.
|
|
@@ -19,8 +43,9 @@ function runVitest(cwd) {
|
|
|
19
43
|
if (existsSync(path.join(cwd, "agent.test.ts"))) testFile = "agent.test.ts";
|
|
20
44
|
else if (existsSync(path.join(cwd, "agent.test.js"))) testFile = "agent.test.js";
|
|
21
45
|
if (!testFile) return false;
|
|
22
|
-
|
|
23
|
-
|
|
46
|
+
const { cmd, args } = resolveVitestCommand(cwd);
|
|
47
|
+
execFileSync(cmd, [
|
|
48
|
+
...args,
|
|
24
49
|
"run",
|
|
25
50
|
"--root",
|
|
26
51
|
".",
|
package/dist/types.mjs
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { t as withPreservedNodeEnv } from "./_vite-env-CmXG4ZAu.mjs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { build } from "vite";
|
|
5
|
+
//#region worker-bundler.ts
|
|
6
|
+
/**
|
|
7
|
+
* Worker bundling — the one implementation of "turn an `agent.ts` into the
|
|
8
|
+
* ESM the guest sandbox loads".
|
|
9
|
+
*
|
|
10
|
+
* Public (no `_` prefix) for the same reason as `client-bundler.ts`: the
|
|
11
|
+
* platform's browser studio builds its workspaces through this function, so a
|
|
12
|
+
* worker published from the browser comes out of the same Vite/Rollup pass as
|
|
13
|
+
* one from `aai deploy`.
|
|
14
|
+
*
|
|
15
|
+
* What the studio supplies via options, because a workspace is not a project:
|
|
16
|
+
*
|
|
17
|
+
* - **`root` + `entry`.** The studio builds a generated entry that re-exports
|
|
18
|
+
* the agent *and* its extracted config, so the guest can report the config
|
|
19
|
+
* back without the host ever evaluating agent code.
|
|
20
|
+
* - **`configFile: false`.** Workspace files are untrusted and a Vite config
|
|
21
|
+
* is executable host code.
|
|
22
|
+
* - **`plugins`.** The studio adds its import allowlist; without it, Vite
|
|
23
|
+
* would happily resolve any package in the server's `node_modules`.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Transform `.md` imports into raw string exports so templates that do
|
|
27
|
+
* `import systemPrompt from "./system-prompt.md"` bundle correctly.
|
|
28
|
+
*/
|
|
29
|
+
const rawMdPlugin = {
|
|
30
|
+
name: "raw-md",
|
|
31
|
+
transform(code, id) {
|
|
32
|
+
if (id.endsWith(".md")) return `export default ${JSON.stringify(code)}`;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Bundle agent.ts into a single ESM string for the sandbox worker.
|
|
37
|
+
*
|
|
38
|
+
* Zod is bundled in — zod 4's `Function()` usage is wrapped in try/catch
|
|
39
|
+
* and gracefully degrades in restricted environments like Deno.
|
|
40
|
+
*/
|
|
41
|
+
async function buildWorker(cwd, opts = {}) {
|
|
42
|
+
const entry = opts.entry ?? "agent.ts";
|
|
43
|
+
const agentEntry = path.isAbsolute(entry) ? entry : path.join(cwd, entry);
|
|
44
|
+
const result = await withPreservedNodeEnv(() => build({
|
|
45
|
+
root: cwd,
|
|
46
|
+
logLevel: "silent",
|
|
47
|
+
...opts.configFile === false && { configFile: false },
|
|
48
|
+
plugins: [rawMdPlugin, ...opts.plugins ?? []],
|
|
49
|
+
build: {
|
|
50
|
+
lib: {
|
|
51
|
+
entry: agentEntry,
|
|
52
|
+
formats: ["es"],
|
|
53
|
+
fileName: "worker"
|
|
54
|
+
},
|
|
55
|
+
target: "node20",
|
|
56
|
+
minify: opts.minify ? "esbuild" : false,
|
|
57
|
+
write: false,
|
|
58
|
+
rollupOptions: { output: { entryFileNames: "[name].js" } }
|
|
59
|
+
}
|
|
60
|
+
}));
|
|
61
|
+
const output = Array.isArray(result) ? result[0] : result;
|
|
62
|
+
if (!output) throw new Error("Vite produced no output for agent.ts");
|
|
63
|
+
const chunk = output.output.find((o) => o.type === "chunk" && o.isEntry);
|
|
64
|
+
if (!chunk) throw new Error("Vite produced no entry chunk for agent.ts");
|
|
65
|
+
return chunk.code;
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
export { buildWorker };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexkroman1/aai-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"aai": "dist/cli.mjs"
|
|
@@ -9,33 +9,44 @@
|
|
|
9
9
|
"./types": {
|
|
10
10
|
"@dev/source": "./types.ts",
|
|
11
11
|
"import": "./dist/types.mjs"
|
|
12
|
+
},
|
|
13
|
+
"./client-bundler": {
|
|
14
|
+
"@dev/source": "./client-bundler.ts",
|
|
15
|
+
"import": "./dist/client-bundler.mjs"
|
|
16
|
+
},
|
|
17
|
+
"./worker-bundler": {
|
|
18
|
+
"@dev/source": "./worker-bundler.ts",
|
|
19
|
+
"import": "./dist/worker-bundler.mjs"
|
|
12
20
|
}
|
|
13
21
|
},
|
|
14
22
|
"files": [
|
|
15
23
|
"dist"
|
|
16
24
|
],
|
|
17
25
|
"dependencies": {
|
|
18
|
-
"@clack/prompts": "^1.
|
|
26
|
+
"@clack/prompts": "^1.7.0",
|
|
27
|
+
"chokidar": "^5.0.0",
|
|
19
28
|
"citty": "^0.2.2",
|
|
20
29
|
"consola": "^3.4.2",
|
|
21
30
|
"dotenv": "^17.4.2",
|
|
22
|
-
"
|
|
31
|
+
"execa": "^9.6.1",
|
|
32
|
+
"get-port": "^7.2.0",
|
|
33
|
+
"giget": "^3.3.0",
|
|
34
|
+
"ofetch": "^1.5.1",
|
|
23
35
|
"p-debounce": "^5.1.0",
|
|
24
|
-
"vite": "^8.
|
|
25
|
-
"zod": "^4.3
|
|
26
|
-
"@alexkroman1/aai": "1.
|
|
27
|
-
"@alexkroman1/aai-ui": "1.
|
|
36
|
+
"vite": "^8.1.5",
|
|
37
|
+
"zod": "^4.4.3",
|
|
38
|
+
"@alexkroman1/aai": "1.9.0",
|
|
39
|
+
"@alexkroman1/aai-ui": "1.9.0"
|
|
28
40
|
},
|
|
29
41
|
"devDependencies": {
|
|
30
|
-
"
|
|
31
|
-
"playwright": "^1.59.1",
|
|
42
|
+
"playwright": "^1.61.1",
|
|
32
43
|
"tree-kill": "^1.2.2",
|
|
33
|
-
"tsdown": "^0.
|
|
34
|
-
"verdaccio": "^6.
|
|
35
|
-
"vitest": "^4.1.
|
|
44
|
+
"tsdown": "^0.22.13",
|
|
45
|
+
"verdaccio": "^6.8.0",
|
|
46
|
+
"vitest": "^4.1.10"
|
|
36
47
|
},
|
|
37
48
|
"peerDependencies": {
|
|
38
|
-
"vitest": "^4.1.
|
|
49
|
+
"vitest": "^4.1.10"
|
|
39
50
|
},
|
|
40
51
|
"peerDependenciesMeta": {
|
|
41
52
|
"vitest": {
|
|
@@ -56,7 +67,7 @@
|
|
|
56
67
|
"build": "tsdown",
|
|
57
68
|
"typecheck": "tsc --noEmit",
|
|
58
69
|
"lint": "biome check .",
|
|
59
|
-
"test:e2e": "VITEST_PROFILE=e2e VITEST_INCLUDE=e2e
|
|
70
|
+
"test:e2e": "VITEST_PROFILE=e2e VITEST_INCLUDE=e2e*.test.ts vitest run -c ../../vitest.slow.config.ts",
|
|
60
71
|
"check:e2e": "pnpm run test:e2e"
|
|
61
72
|
}
|
|
62
73
|
}
|
package/dist/_agent-De8f1JdZ.mjs
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { n as ensureApiKey, r as readProjectConfig } from "./_config-B0FXR5GQ.mjs";
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
const DEFAULT_DEV_SERVER = "http://localhost:8080";
|
|
7
|
-
let _cachedMonorepoRoot;
|
|
8
|
-
function getMonorepoRoot() {
|
|
9
|
-
if (_cachedMonorepoRoot !== void 0) return _cachedMonorepoRoot;
|
|
10
|
-
const cliDir = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
-
const root1 = path.resolve(cliDir, "../..");
|
|
12
|
-
const root2 = path.resolve(cliDir, "../../..");
|
|
13
|
-
if (existsSync(path.join(root1, "pnpm-workspace.yaml"))) _cachedMonorepoRoot = root1;
|
|
14
|
-
else if (existsSync(path.join(root2, "pnpm-workspace.yaml"))) _cachedMonorepoRoot = root2;
|
|
15
|
-
else _cachedMonorepoRoot = null;
|
|
16
|
-
return _cachedMonorepoRoot;
|
|
17
|
-
}
|
|
18
|
-
function isDevMode() {
|
|
19
|
-
if (process.env.AAI_NO_DEV === "1") return false;
|
|
20
|
-
return getMonorepoRoot() !== null;
|
|
21
|
-
}
|
|
22
|
-
function resolveServerUrl(explicit, configUrl) {
|
|
23
|
-
if (explicit) return explicit;
|
|
24
|
-
if (isDevMode()) return DEFAULT_DEV_SERVER;
|
|
25
|
-
return configUrl ?? "https://aai-agent.fly.dev";
|
|
26
|
-
}
|
|
27
|
-
async function getServerInfo(cwd, explicitServer, explicitApiKey) {
|
|
28
|
-
const config = await readProjectConfig(cwd);
|
|
29
|
-
if (!config) throw new Error("No .aai/project.json found — run `aai deploy` first");
|
|
30
|
-
const apiKey = explicitApiKey ?? await ensureApiKey();
|
|
31
|
-
return {
|
|
32
|
-
serverUrl: resolveServerUrl(explicitServer, config.serverUrl),
|
|
33
|
-
slug: config.slug,
|
|
34
|
-
apiKey
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
//#endregion
|
|
38
|
-
export { DEFAULT_DEV_SERVER, getMonorepoRoot, getServerInfo, isDevMode, resolveServerUrl };
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
//#region _api-client.ts
|
|
3
|
-
/**
|
|
4
|
-
* Shared HTTP helpers for platform API calls (deploy, delete, secrets).
|
|
5
|
-
*/
|
|
6
|
-
const HINT_INVALID_API_KEY = "Your API key may be invalid. Run `aai` to re-enter your AssemblyAI API key.";
|
|
7
|
-
/**
|
|
8
|
-
* Send an authenticated request to the platform API.
|
|
9
|
-
*
|
|
10
|
-
* Adds the `Authorization` header and, on network failure, throws with a
|
|
11
|
-
* contextual hint (localhost → "is the dev server running?", remote →
|
|
12
|
-
* "check your network connection").
|
|
13
|
-
*/
|
|
14
|
-
async function apiRequest(url, init, fetchFn = globalThis.fetch.bind(globalThis)) {
|
|
15
|
-
const { apiKey, action, ...rest } = init;
|
|
16
|
-
const headers = {
|
|
17
|
-
Authorization: `Bearer ${apiKey}`,
|
|
18
|
-
...rest.body ? { "Content-Type": "application/json" } : {},
|
|
19
|
-
...rest.headers
|
|
20
|
-
};
|
|
21
|
-
try {
|
|
22
|
-
return await fetchFn(url, {
|
|
23
|
-
...rest,
|
|
24
|
-
headers
|
|
25
|
-
});
|
|
26
|
-
} catch (err) {
|
|
27
|
-
throw new Error(`${action} failed: could not reach ${url}\n Check your network connection and verify the server URL is correct.`, { cause: err });
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
/** Format a non-ok API response into a descriptive error. */
|
|
31
|
-
function apiError(action, status, body, hint) {
|
|
32
|
-
return /* @__PURE__ */ new Error(`${action} failed (HTTP ${status}): ${body}${hint ? `\n ${hint}` : ""}`);
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Like `apiRequest`, but throws on non-ok responses with status-specific hints.
|
|
36
|
-
* The 401 hint is always included. Pass additional hints via `opts.hints`.
|
|
37
|
-
*/
|
|
38
|
-
async function apiRequestOrThrow(url, init, opts) {
|
|
39
|
-
const resp = await apiRequest(url, init, opts?.fetch);
|
|
40
|
-
if (resp.ok) return resp;
|
|
41
|
-
const text = await resp.text();
|
|
42
|
-
const hint = resp.status === 401 ? HINT_INVALID_API_KEY : opts?.hints?.[resp.status];
|
|
43
|
-
throw apiError(init.action, resp.status, text, hint);
|
|
44
|
-
}
|
|
45
|
-
//#endregion
|
|
46
|
-
export { apiRequestOrThrow as t };
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { a as ok } from "./_output-vvoR6N1x.mjs";
|
|
3
|
-
import { r as validateAgentExport, t as fileExists } from "./_utils-DZo3_J_v.mjs";
|
|
4
|
-
import { n as writeTempHtml } from "./_default-html-BasMCgr9.mjs";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import { pathToFileURL } from "node:url";
|
|
7
|
-
import fs from "node:fs/promises";
|
|
8
|
-
import { agentToolsToSchemas, toAgentConfig } from "@alexkroman1/aai/manifest";
|
|
9
|
-
import { build } from "vite";
|
|
10
|
-
//#region _bundler.ts
|
|
11
|
-
/** Shared Vite build base config for agent bundles. */
|
|
12
|
-
function agentViteBuildBase(entry) {
|
|
13
|
-
return {
|
|
14
|
-
logLevel: "silent",
|
|
15
|
-
build: {
|
|
16
|
-
lib: {
|
|
17
|
-
entry,
|
|
18
|
-
formats: ["es"]
|
|
19
|
-
},
|
|
20
|
-
target: "node20",
|
|
21
|
-
minify: false,
|
|
22
|
-
rollupOptions: { output: { entryFileNames: "[name].js" } }
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Bundle an agent directory: build agent.ts into worker ESM + extract config.
|
|
28
|
-
*
|
|
29
|
-
* - agent.ts is the single entry point: `export default agent({...})`
|
|
30
|
-
* - A single Vite build produces the worker ESM (all deps bundled in).
|
|
31
|
-
* The AgentDef is extracted from that bundle via dynamic import, avoiding a
|
|
32
|
-
* second build pass.
|
|
33
|
-
*/
|
|
34
|
-
async function buildAgentBundle(cwd) {
|
|
35
|
-
const { log } = await import("./_ui-nOp7hFVy.mjs").then((n) => n.t);
|
|
36
|
-
const [worker, clientFiles] = await Promise.all([buildWorker(cwd), buildClient(cwd)]);
|
|
37
|
-
const agentDef = await evalWorkerBundle(worker, cwd);
|
|
38
|
-
log.step(`Bundling ${agentDef.name}`);
|
|
39
|
-
const config = toAgentConfig(agentDef);
|
|
40
|
-
const toolSchemas = agentToolsToSchemas(agentDef.tools ?? {});
|
|
41
|
-
return {
|
|
42
|
-
worker,
|
|
43
|
-
clientFiles,
|
|
44
|
-
agentConfig: {
|
|
45
|
-
...config,
|
|
46
|
-
toolSchemas
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Write the worker ESM to a temp file and dynamic-import it, returning
|
|
52
|
-
* the AgentDef default export. All dependencies are bundled in, so the
|
|
53
|
-
* file can be evaluated from any directory.
|
|
54
|
-
*/
|
|
55
|
-
async function evalWorkerBundle(code, cwd) {
|
|
56
|
-
const evalDir = path.join(cwd, ".aai", "eval");
|
|
57
|
-
await fs.mkdir(evalDir, { recursive: true });
|
|
58
|
-
const tmpPath = path.join(evalDir, `agent-${Date.now()}-${Math.random().toString(36).slice(2)}.mjs`);
|
|
59
|
-
try {
|
|
60
|
-
await fs.writeFile(tmpPath, code);
|
|
61
|
-
const mod = await import(pathToFileURL(tmpPath).href);
|
|
62
|
-
const agentDef = mod.default ?? mod;
|
|
63
|
-
validateAgentExport(agentDef);
|
|
64
|
-
return agentDef;
|
|
65
|
-
} finally {
|
|
66
|
-
await fs.rm(tmpPath).catch(() => {});
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Bundle agent.ts into a single ESM string for the sandbox worker.
|
|
71
|
-
*
|
|
72
|
-
* Zod is bundled in — zod 4's `Function()` usage is wrapped in try/catch
|
|
73
|
-
* and gracefully degrades in restricted environments like Deno.
|
|
74
|
-
*/
|
|
75
|
-
async function buildWorker(cwd) {
|
|
76
|
-
const base = agentViteBuildBase(path.join(cwd, "agent.ts"));
|
|
77
|
-
const result = await build({
|
|
78
|
-
...base,
|
|
79
|
-
plugins: [{
|
|
80
|
-
name: "raw-md",
|
|
81
|
-
transform(code, id) {
|
|
82
|
-
if (id.endsWith(".md")) return `export default ${JSON.stringify(code)}`;
|
|
83
|
-
}
|
|
84
|
-
}],
|
|
85
|
-
build: {
|
|
86
|
-
...base.build,
|
|
87
|
-
lib: {
|
|
88
|
-
...base.build.lib,
|
|
89
|
-
fileName: "worker"
|
|
90
|
-
},
|
|
91
|
-
write: false,
|
|
92
|
-
rollupOptions: { output: { entryFileNames: "[name].js" } }
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
const output = Array.isArray(result) ? result[0] : result;
|
|
96
|
-
if (!output) throw new Error("Vite produced no output for agent.ts");
|
|
97
|
-
const chunk = output.output.find((o) => o.type === "chunk" && o.isEntry);
|
|
98
|
-
if (!chunk) throw new Error("Vite produced no entry chunk for agent.ts");
|
|
99
|
-
return chunk.code;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Build the client SPA using Vite if client.tsx exists.
|
|
103
|
-
* Returns a map of relative file paths to string contents for deploy.
|
|
104
|
-
*/
|
|
105
|
-
async function buildClient(cwd) {
|
|
106
|
-
if (!await fileExists(path.join(cwd, "client.tsx"))) return {};
|
|
107
|
-
const clientDir = path.join(cwd, ".aai", "client");
|
|
108
|
-
const cleanupHtml = writeTempHtml(cwd);
|
|
109
|
-
try {
|
|
110
|
-
await build({
|
|
111
|
-
root: cwd,
|
|
112
|
-
base: "./",
|
|
113
|
-
logLevel: "silent",
|
|
114
|
-
build: {
|
|
115
|
-
outDir: ".aai/client",
|
|
116
|
-
emptyOutDir: true
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
} finally {
|
|
120
|
-
cleanupHtml();
|
|
121
|
-
}
|
|
122
|
-
const files = {};
|
|
123
|
-
async function walk(dir, prefix) {
|
|
124
|
-
for (const entry of await fs.readdir(dir, { withFileTypes: true })) {
|
|
125
|
-
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
126
|
-
if (entry.isDirectory()) await walk(path.join(dir, entry.name), rel);
|
|
127
|
-
else files[rel] = await fs.readFile(path.join(dir, entry.name), "utf-8");
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
await walk(clientDir, "");
|
|
131
|
-
return files;
|
|
132
|
-
}
|
|
133
|
-
async function executeBuild(cwd) {
|
|
134
|
-
const { log } = await import("./_ui-nOp7hFVy.mjs").then((n) => n.t);
|
|
135
|
-
const bundle = await buildAgentBundle(cwd);
|
|
136
|
-
log.success("Build complete");
|
|
137
|
-
return ok({
|
|
138
|
-
name: bundle.agentConfig.name,
|
|
139
|
-
workerBytes: bundle.worker.length
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
//#endregion
|
|
143
|
-
export { buildAgentBundle, executeBuild };
|