@alexkroman1/aai-cli 0.10.2 → 0.10.4
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/_api-client-Br1a_Dsp.mjs +36 -0
- package/dist/_bundler-BM-vnJUp.mjs +117 -0
- package/dist/{_discover-D7HCLa_N.mjs → _discover-DiRl7b_K.mjs} +51 -28
- package/dist/_init-CVkKf8t_.mjs +56 -0
- package/dist/_templates-s5MYfWS9.mjs +63 -0
- package/dist/_ui-Cu-v_Bzz.mjs +13 -0
- package/dist/cli.mjs +49 -191
- package/dist/delete-CSPF9vdh.mjs +34 -0
- package/dist/deploy-C2s4C_ff.mjs +92 -0
- package/dist/dev-CBKWhwaQ.mjs +20 -0
- package/dist/{generate-D7zJtqoC.mjs → generate-DQt7C4zz.mjs} +33 -38
- package/dist/init-BAYELcXE.mjs +112 -0
- package/dist/secret-CRLbxN5P.mjs +44 -0
- package/dist/{test-9fFn9DCf.mjs → test-CuMTKw38.mjs} +8 -10
- package/package.json +8 -13
- package/dist/_build-aW_zyPpr.mjs +0 -176
- package/dist/_init-CweK2mJJ.mjs +0 -108
- package/dist/_link-D6mmDQ0B.mjs +0 -49
- package/dist/_server-common-B6EXxxB2.mjs +0 -105
- package/dist/_ui-C9IvR7Fh.mjs +0 -50
- package/dist/delete-DxECFYOp.mjs +0 -40
- package/dist/deploy-DB0PeNtc.mjs +0 -90
- package/dist/dev-DCtHG1pz.mjs +0 -60
- package/dist/doctor-bWuArZi6.mjs +0 -215
- package/dist/init-COkINbH_.mjs +0 -61
- package/dist/rag-BNfrEr4A.mjs +0 -178
- package/dist/secret-Zzv7fJ_3.mjs +0 -50
- package/dist/start-DQypQkOq.mjs +0 -23
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { l as resolveCwd, n as ensureApiKeyInEnv, r as fileExists } from "./_discover-DiRl7b_K.mjs";
|
|
3
|
+
import { n as listTemplates } from "./_templates-s5MYfWS9.mjs";
|
|
4
|
+
import { consola } from "./_ui-Cu-v_Bzz.mjs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import fs from "node:fs/promises";
|
|
7
|
+
import { errorMessage } from "@alexkroman1/aai/utils";
|
|
8
|
+
import * as p from "@clack/prompts";
|
|
9
|
+
import { colorize } from "consola/utils";
|
|
10
|
+
import { execFile } from "node:child_process";
|
|
11
|
+
import { promisify } from "node:util";
|
|
12
|
+
//#region init.ts
|
|
13
|
+
const execFileAsync = promisify(execFile);
|
|
14
|
+
const DEFAULT_PROJECT_NAME = "my-voice-agent";
|
|
15
|
+
const DEFAULT_TEMPLATE = "simple";
|
|
16
|
+
/** Detect the package manager from the environment. */
|
|
17
|
+
function detectPackageManager() {
|
|
18
|
+
const ua = process.env.npm_config_user_agent ?? "";
|
|
19
|
+
if (ua.startsWith("pnpm")) return "pnpm";
|
|
20
|
+
if (ua.startsWith("yarn")) return "yarn";
|
|
21
|
+
if (ua.startsWith("bun")) return "bun";
|
|
22
|
+
return "npm";
|
|
23
|
+
}
|
|
24
|
+
/** Prompt for project name or return default when --yes is set. */
|
|
25
|
+
async function promptProjectName(yes) {
|
|
26
|
+
if (yes) return DEFAULT_PROJECT_NAME;
|
|
27
|
+
const result = await p.text({
|
|
28
|
+
message: "What is your project named?",
|
|
29
|
+
placeholder: DEFAULT_PROJECT_NAME,
|
|
30
|
+
defaultValue: DEFAULT_PROJECT_NAME
|
|
31
|
+
});
|
|
32
|
+
if (p.isCancel(result)) {
|
|
33
|
+
p.cancel("Setup cancelled.");
|
|
34
|
+
process.exit(0);
|
|
35
|
+
}
|
|
36
|
+
return result || DEFAULT_PROJECT_NAME;
|
|
37
|
+
}
|
|
38
|
+
/** Prompt for template selection or return default when --yes is set. */
|
|
39
|
+
async function promptTemplate(yes) {
|
|
40
|
+
if (yes) return DEFAULT_TEMPLATE;
|
|
41
|
+
const templates = await listTemplates();
|
|
42
|
+
const result = await p.select({
|
|
43
|
+
message: "Which template would you like to use?",
|
|
44
|
+
options: templates.map((name) => ({
|
|
45
|
+
value: name,
|
|
46
|
+
label: name
|
|
47
|
+
})),
|
|
48
|
+
initialValue: DEFAULT_TEMPLATE
|
|
49
|
+
});
|
|
50
|
+
if (p.isCancel(result)) {
|
|
51
|
+
p.cancel("Setup cancelled.");
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
/** Install deps via the detected package manager. */
|
|
57
|
+
async function installDeps(cwd, pm) {
|
|
58
|
+
if (await fileExists(path.join(cwd, "node_modules"))) return;
|
|
59
|
+
let pkgJson;
|
|
60
|
+
try {
|
|
61
|
+
pkgJson = JSON.parse(await fs.readFile(path.join(cwd, "package.json"), "utf-8"));
|
|
62
|
+
} catch {
|
|
63
|
+
pkgJson = {};
|
|
64
|
+
}
|
|
65
|
+
const deps = Object.keys(pkgJson.dependencies ?? {});
|
|
66
|
+
const devDeps = Object.keys(pkgJson.devDependencies ?? {});
|
|
67
|
+
if (deps.length === 0 && devDeps.length === 0) return;
|
|
68
|
+
const s = p.spinner();
|
|
69
|
+
s.start(`Installing dependencies with ${pm}`);
|
|
70
|
+
try {
|
|
71
|
+
await execFileAsync(pm, ["install"], { cwd });
|
|
72
|
+
s.stop("Dependencies installed");
|
|
73
|
+
} catch (err) {
|
|
74
|
+
const msg = errorMessage(err);
|
|
75
|
+
s.stop("Dependency install failed");
|
|
76
|
+
consola.warn(`${pm} install failed: ${msg}`);
|
|
77
|
+
consola.warn(`Run \`${pm} install\` manually in the project directory.`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** Format the run command for the detected package manager. */
|
|
81
|
+
function devCommand(pm) {
|
|
82
|
+
return pm === "npm" ? `${pm} run dev` : `${pm} dev`;
|
|
83
|
+
}
|
|
84
|
+
async function runInitCommand(opts, extra) {
|
|
85
|
+
const pm = detectPackageManager();
|
|
86
|
+
if (!extra?.quiet) p.intro(colorize("blueBright", "Create a new voice agent"));
|
|
87
|
+
if (!opts.skipApi) await ensureApiKeyInEnv();
|
|
88
|
+
const dir = opts.dir ?? await promptProjectName(opts.yes);
|
|
89
|
+
const cwd = path.resolve(resolveCwd(), dir);
|
|
90
|
+
if (!opts.force && await fileExists(path.join(cwd, "agent.ts"))) throw new Error(`agent.ts already exists in this directory. Use ${colorize("blueBright", "--force")} to overwrite.`);
|
|
91
|
+
const template = opts.template ?? await promptTemplate(opts.yes);
|
|
92
|
+
const s = p.spinner();
|
|
93
|
+
s.start(`Creating ${dir} from ${template} template`);
|
|
94
|
+
const { runInit } = await import("./_init-CVkKf8t_.mjs");
|
|
95
|
+
await runInit({
|
|
96
|
+
targetDir: cwd,
|
|
97
|
+
template
|
|
98
|
+
});
|
|
99
|
+
s.stop("Project created");
|
|
100
|
+
await installDeps(cwd, pm);
|
|
101
|
+
if (!(opts.skipDeploy || extra?.quiet)) {
|
|
102
|
+
const { runDeployCommand } = await import("./deploy-C2s4C_ff.mjs");
|
|
103
|
+
await runDeployCommand({ cwd });
|
|
104
|
+
}
|
|
105
|
+
if (!extra?.quiet) {
|
|
106
|
+
p.note(`cd ${dir}\n${devCommand(pm)}`, "Next steps");
|
|
107
|
+
p.outro("Happy building!");
|
|
108
|
+
}
|
|
109
|
+
return cwd;
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
export { runInitCommand };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { f as askPassword, o as getServerInfo } from "./_discover-DiRl7b_K.mjs";
|
|
3
|
+
import { consola } from "./_ui-Cu-v_Bzz.mjs";
|
|
4
|
+
//#region secret.ts
|
|
5
|
+
async function apiFetch(cwd, pathSuffix, init) {
|
|
6
|
+
const { serverUrl, slug, apiKey } = await getServerInfo(cwd);
|
|
7
|
+
const resp = await fetch(`${serverUrl}/${slug}/secret${pathSuffix}`, {
|
|
8
|
+
...init,
|
|
9
|
+
headers: {
|
|
10
|
+
Authorization: `Bearer ${apiKey}`,
|
|
11
|
+
...init?.headers
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
if (!resp.ok) {
|
|
15
|
+
const text = await resp.text();
|
|
16
|
+
throw new Error(`Secret operation failed: ${text}`);
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
resp,
|
|
20
|
+
slug
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
async function runSecretPut(cwd, name) {
|
|
24
|
+
const value = await askPassword(`Enter value for ${name}`);
|
|
25
|
+
if (!value) throw new Error("No value provided");
|
|
26
|
+
const { slug } = await apiFetch(cwd, "", {
|
|
27
|
+
method: "PUT",
|
|
28
|
+
headers: { "Content-Type": "application/json" },
|
|
29
|
+
body: JSON.stringify({ [name]: value })
|
|
30
|
+
});
|
|
31
|
+
consola.success(`Set ${name} for ${slug}`);
|
|
32
|
+
}
|
|
33
|
+
async function runSecretDelete(cwd, name) {
|
|
34
|
+
const { slug } = await apiFetch(cwd, `/${name}`, { method: "DELETE" });
|
|
35
|
+
consola.success(`Deleted ${name} from ${slug}`);
|
|
36
|
+
}
|
|
37
|
+
async function runSecretList(cwd) {
|
|
38
|
+
const { resp } = await apiFetch(cwd, "");
|
|
39
|
+
const { vars } = await resp.json();
|
|
40
|
+
if (vars.length === 0) consola.info("Secrets: none set");
|
|
41
|
+
else for (const name of vars) consola.log(` ${name}`);
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { runSecretDelete, runSecretList, runSecretPut };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { consola } from "./_ui-Cu-v_Bzz.mjs";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { execSync } from "node:child_process";
|
|
@@ -15,7 +15,7 @@ import { execSync } from "node:child_process";
|
|
|
15
15
|
*/
|
|
16
16
|
function runVitest(cwd) {
|
|
17
17
|
if (!(existsSync(path.join(cwd, "agent.test.ts")) || existsSync(path.join(cwd, "agent.test.js")))) return false;
|
|
18
|
-
execSync(
|
|
18
|
+
execSync(`npx vitest run --root . ${existsSync(path.join(cwd, "agent.test.ts")) ? "agent.test.ts" : "agent.test.js"}`, {
|
|
19
19
|
cwd,
|
|
20
20
|
stdio: "inherit",
|
|
21
21
|
env: {
|
|
@@ -27,14 +27,12 @@ function runVitest(cwd) {
|
|
|
27
27
|
}
|
|
28
28
|
/** Run agent tests. Used by `aai test`. */
|
|
29
29
|
async function runTestCommand(cwd) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
log(step("Test", "ok"));
|
|
37
|
-
});
|
|
30
|
+
consola.start("Running agent tests");
|
|
31
|
+
if (!runVitest(cwd)) {
|
|
32
|
+
consola.info("No test files found (agent.test.ts). Skipping.");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
consola.success("Tests passed");
|
|
38
36
|
}
|
|
39
37
|
//#endregion
|
|
40
38
|
export { runTestCommand, runVitest };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexkroman1/aai-cli",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"aai": "dist/cli.mjs"
|
|
@@ -10,24 +10,20 @@
|
|
|
10
10
|
],
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@ai-sdk/openai-compatible": "^2.0.37",
|
|
13
|
-
"@
|
|
14
|
-
"consola": "^3.4.2",
|
|
15
|
-
"giget": "^2.0.0",
|
|
16
|
-
"@hono/node-server": "^1.19.11",
|
|
17
|
-
"@preact/preset-vite": "^2.10.5",
|
|
18
|
-
"@tailwindcss/vite": "^4.2.2",
|
|
13
|
+
"@clack/prompts": "^1.1.0",
|
|
19
14
|
"ai": "^6.0.140",
|
|
20
15
|
"citty": "^0.2.1",
|
|
21
|
-
"
|
|
16
|
+
"consola": "^3.4.2",
|
|
17
|
+
"giget": "^2.0.0",
|
|
22
18
|
"human-id": "^4.1.3",
|
|
23
|
-
"p-limit": "^7.3.0",
|
|
24
19
|
"vite": "^8.0.3",
|
|
25
20
|
"zod": "^4.3.6",
|
|
26
|
-
"@alexkroman1/aai": "0.10.
|
|
21
|
+
"@alexkroman1/aai": "0.10.4"
|
|
27
22
|
},
|
|
28
23
|
"devDependencies": {
|
|
29
24
|
"playwright": "^1.58.2",
|
|
30
|
-
"tsdown": "^0.21.5"
|
|
25
|
+
"tsdown": "^0.21.5",
|
|
26
|
+
"vitest": "^4.1.1"
|
|
31
27
|
},
|
|
32
28
|
"engines": {
|
|
33
29
|
"node": ">=22.6"
|
|
@@ -41,8 +37,7 @@
|
|
|
41
37
|
"build": "tsdown",
|
|
42
38
|
"typecheck": "tsc --noEmit",
|
|
43
39
|
"lint": "biome check .",
|
|
44
|
-
"test:integration": "vitest run pack-build.test.ts -c vitest.slow.config.ts",
|
|
45
40
|
"test:e2e": "vitest run e2e.test.ts -c vitest.slow.config.ts",
|
|
46
|
-
"check:e2e": "
|
|
41
|
+
"check:e2e": "pnpm run test:e2e"
|
|
47
42
|
}
|
|
48
43
|
}
|
package/dist/_build-aW_zyPpr.mjs
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { s as loadAgent } from "./_discover-D7HCLa_N.mjs";
|
|
3
|
-
import { a as runCommand, o as step } from "./_ui-C9IvR7Fh.mjs";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import fs from "node:fs/promises";
|
|
6
|
-
import { errorMessage } from "@alexkroman1/aai/utils";
|
|
7
|
-
import preact from "@preact/preset-vite";
|
|
8
|
-
import tailwindcss from "@tailwindcss/vite";
|
|
9
|
-
import { build, createServer } from "vite";
|
|
10
|
-
//#region _bundler.ts
|
|
11
|
-
/**
|
|
12
|
-
* Error thrown when bundling fails.
|
|
13
|
-
*
|
|
14
|
-
* @param message Human-readable error message (typically formatted build output).
|
|
15
|
-
*/
|
|
16
|
-
var BundleError = class extends Error {
|
|
17
|
-
constructor(message, options) {
|
|
18
|
-
super(message, options);
|
|
19
|
-
this.name = "BundleError";
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
/** File extensions that are safe to read as UTF-8 text. */
|
|
23
|
-
const TEXT_EXTENSIONS = new Set([
|
|
24
|
-
".html",
|
|
25
|
-
".htm",
|
|
26
|
-
".css",
|
|
27
|
-
".js",
|
|
28
|
-
".mjs",
|
|
29
|
-
".cjs",
|
|
30
|
-
".ts",
|
|
31
|
-
".mts",
|
|
32
|
-
".json",
|
|
33
|
-
".map",
|
|
34
|
-
".svg",
|
|
35
|
-
".xml",
|
|
36
|
-
".txt",
|
|
37
|
-
".md"
|
|
38
|
-
]);
|
|
39
|
-
/** Read all files in a directory as a map of relative paths to contents. */
|
|
40
|
-
async function readDirFiles(dir) {
|
|
41
|
-
let entries;
|
|
42
|
-
try {
|
|
43
|
-
entries = await fs.readdir(dir, {
|
|
44
|
-
recursive: true,
|
|
45
|
-
withFileTypes: true
|
|
46
|
-
});
|
|
47
|
-
} catch (err) {
|
|
48
|
-
if (err instanceof Error && "code" in err && err.code === "ENOENT") return {};
|
|
49
|
-
throw err;
|
|
50
|
-
}
|
|
51
|
-
const files = {};
|
|
52
|
-
await Promise.all(entries.filter((e) => e.isFile()).map(async (e) => {
|
|
53
|
-
const full = path.join(e.parentPath, e.name);
|
|
54
|
-
const ext = path.extname(e.name).toLowerCase();
|
|
55
|
-
const rel = path.relative(dir, full);
|
|
56
|
-
if (TEXT_EXTENSIONS.has(ext)) files[rel] = await fs.readFile(full, "utf-8");
|
|
57
|
-
else files[rel] = `base64:${(await fs.readFile(full)).toString("base64")}`;
|
|
58
|
-
}));
|
|
59
|
-
return files;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Bundles an agent project into deployable artifacts using Vite.
|
|
63
|
-
*
|
|
64
|
-
* Writes all output to `.aai/` on disk:
|
|
65
|
-
* - `.aai/build/worker.js` — the platform worker bundle
|
|
66
|
-
* - `.aai/client/` — standard Vite multi-file output (index.html + assets/)
|
|
67
|
-
*
|
|
68
|
-
* Both `aai dev` and `aai deploy` use this function identically.
|
|
69
|
-
*/
|
|
70
|
-
async function bundleAgent(agent, opts) {
|
|
71
|
-
const aaiDir = path.join(agent.dir, ".aai");
|
|
72
|
-
const buildDir = path.join(aaiDir, "build");
|
|
73
|
-
const clientDir = path.join(aaiDir, "client");
|
|
74
|
-
try {
|
|
75
|
-
await build({
|
|
76
|
-
configFile: false,
|
|
77
|
-
root: agent.dir,
|
|
78
|
-
logLevel: "warn",
|
|
79
|
-
build: {
|
|
80
|
-
lib: {
|
|
81
|
-
entry: path.join(agent.dir, "agent.ts"),
|
|
82
|
-
formats: ["es"],
|
|
83
|
-
fileName: () => "worker.js"
|
|
84
|
-
},
|
|
85
|
-
outDir: buildDir,
|
|
86
|
-
emptyOutDir: true,
|
|
87
|
-
minify: true,
|
|
88
|
-
target: "es2022"
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
} catch (err) {
|
|
92
|
-
throw new BundleError(errorMessage(err), { cause: err });
|
|
93
|
-
}
|
|
94
|
-
if (!(opts?.skipClient ?? !agent.clientEntry)) try {
|
|
95
|
-
await build({
|
|
96
|
-
root: agent.dir,
|
|
97
|
-
base: "./",
|
|
98
|
-
logLevel: "warn",
|
|
99
|
-
plugins: [preact(), tailwindcss()],
|
|
100
|
-
resolve: { dedupe: ["preact", "@preact/signals"] },
|
|
101
|
-
build: {
|
|
102
|
-
outDir: clientDir,
|
|
103
|
-
emptyOutDir: true,
|
|
104
|
-
minify: true,
|
|
105
|
-
target: "es2022"
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
} catch (err) {
|
|
109
|
-
throw new BundleError(errorMessage(err), { cause: err });
|
|
110
|
-
}
|
|
111
|
-
const worker = await fs.readFile(path.join(buildDir, "worker.js"), "utf-8");
|
|
112
|
-
return {
|
|
113
|
-
worker,
|
|
114
|
-
clientFiles: await readDirFiles(clientDir),
|
|
115
|
-
clientDir,
|
|
116
|
-
workerBytes: Buffer.byteLength(worker)
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Create a Vite dev server for client HMR during development.
|
|
121
|
-
*
|
|
122
|
-
* The dev server serves client files with hot module replacement enabled and
|
|
123
|
-
* proxies backend requests (`/health`, `/websocket`) to the agent server.
|
|
124
|
-
*/
|
|
125
|
-
async function createClientDevServer(agentDir, backendPort, port) {
|
|
126
|
-
const target = `http://localhost:${backendPort}`;
|
|
127
|
-
return await createServer({
|
|
128
|
-
configFile: false,
|
|
129
|
-
root: agentDir,
|
|
130
|
-
plugins: [preact(), tailwindcss()],
|
|
131
|
-
resolve: { dedupe: ["preact", "@preact/signals"] },
|
|
132
|
-
server: {
|
|
133
|
-
port,
|
|
134
|
-
strictPort: true,
|
|
135
|
-
proxy: {
|
|
136
|
-
"/health": target,
|
|
137
|
-
"/websocket": {
|
|
138
|
-
target,
|
|
139
|
-
ws: true
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
//#endregion
|
|
146
|
-
//#region _build.ts
|
|
147
|
-
/**
|
|
148
|
-
* Discover the agent entry and bundle both worker and client.
|
|
149
|
-
*
|
|
150
|
-
* Shared by `aai build`, `aai dev`, and `aai deploy`.
|
|
151
|
-
*/
|
|
152
|
-
async function buildAgentBundle(cwd, log) {
|
|
153
|
-
const agent = await loadAgent(cwd);
|
|
154
|
-
if (!agent) throw new Error("No agent found — run `aai init` first");
|
|
155
|
-
log(step("Bundle", agent.slug));
|
|
156
|
-
let bundle;
|
|
157
|
-
try {
|
|
158
|
-
bundle = await bundleAgent(agent);
|
|
159
|
-
} catch (err) {
|
|
160
|
-
if (err instanceof BundleError) throw new Error(`Bundle failed: ${err.message}`, { cause: err });
|
|
161
|
-
throw err;
|
|
162
|
-
}
|
|
163
|
-
const kb = (bundle.workerBytes / 1024).toFixed(1);
|
|
164
|
-
const clientCount = Object.keys(bundle.clientFiles).length;
|
|
165
|
-
log(`worker: ${kb} KB, client: ${clientCount} file(s)`);
|
|
166
|
-
return bundle;
|
|
167
|
-
}
|
|
168
|
-
/** Bundle the agent and report success. Used by `aai build`. */
|
|
169
|
-
async function runBuildCommand(cwd) {
|
|
170
|
-
await runCommand(async ({ log }) => {
|
|
171
|
-
await buildAgentBundle(cwd, log);
|
|
172
|
-
log(step("Build", "ok"));
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
//#endregion
|
|
176
|
-
export { buildAgentBundle, runBuildCommand, createClientDevServer as t };
|
package/dist/_init-CweK2mJJ.mjs
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { o as isDevMode } from "./_discover-D7HCLa_N.mjs";
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
import fs$1 from "node:fs/promises";
|
|
7
|
-
import { downloadTemplate } from "giget";
|
|
8
|
-
//#region _templates.ts
|
|
9
|
-
const GIGET_SOURCE = "github:alexkroman/agent/packages/aai-templates";
|
|
10
|
-
const GIGET_REF = process.env.AAI_TEMPLATES_REF ?? "main";
|
|
11
|
-
/** Resolve the local aai-templates package directory (dev mode only). */
|
|
12
|
-
function resolveLocalTemplatesDir() {
|
|
13
|
-
const cliDir = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
-
const fromSrc = path.resolve(cliDir, "../aai-templates");
|
|
15
|
-
const fromDist = path.resolve(cliDir, "../../aai-templates");
|
|
16
|
-
if (existsSync(fromSrc)) return fromSrc;
|
|
17
|
-
if (existsSync(fromDist)) return fromDist;
|
|
18
|
-
throw new Error("Cannot find local aai-templates package");
|
|
19
|
-
}
|
|
20
|
-
/** Resolve the templates directory — local in dev, giget download in prod. */
|
|
21
|
-
async function resolveTemplatesDir() {
|
|
22
|
-
if (isDevMode()) return resolveLocalTemplatesDir();
|
|
23
|
-
const { dir } = await downloadTemplate(`${GIGET_SOURCE}#${GIGET_REF}`, { force: true });
|
|
24
|
-
return dir;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Download a template into targetDir, merging _shared files underneath.
|
|
28
|
-
*/
|
|
29
|
-
async function downloadAndMergeTemplate(template, targetDir) {
|
|
30
|
-
const templatesDir = await resolveTemplatesDir();
|
|
31
|
-
const names = (await fs$1.readdir(templatesDir, { withFileTypes: true })).filter((e) => e.isDirectory() && !e.name.startsWith("_") && e.name !== "node_modules").map((e) => e.name);
|
|
32
|
-
if (!names.includes(template)) throw new Error(`unknown template '${template}' -- available: ${names.join(", ")}`);
|
|
33
|
-
await fs$1.cp(path.join(templatesDir, template), targetDir, {
|
|
34
|
-
recursive: true,
|
|
35
|
-
force: true
|
|
36
|
-
});
|
|
37
|
-
const sharedDir = path.join(templatesDir, "_shared");
|
|
38
|
-
if (existsSync(sharedDir)) {
|
|
39
|
-
const entries = await fs$1.readdir(sharedDir, {
|
|
40
|
-
recursive: true,
|
|
41
|
-
withFileTypes: true
|
|
42
|
-
});
|
|
43
|
-
for (const entry of entries) {
|
|
44
|
-
if (!entry.isFile()) continue;
|
|
45
|
-
const rel = path.relative(sharedDir, path.join(entry.parentPath, entry.name));
|
|
46
|
-
const destPath = path.join(targetDir, rel);
|
|
47
|
-
await fs$1.mkdir(path.dirname(destPath), { recursive: true });
|
|
48
|
-
try {
|
|
49
|
-
await fs$1.copyFile(path.join(sharedDir, rel), destPath, fs$1.constants.COPYFILE_EXCL);
|
|
50
|
-
} catch (err) {
|
|
51
|
-
if (!(err instanceof Error && "code" in err && err.code === "EEXIST")) throw err;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
//#endregion
|
|
57
|
-
//#region _init.ts
|
|
58
|
-
async function runInit(opts) {
|
|
59
|
-
const { targetDir, template } = opts;
|
|
60
|
-
await downloadAndMergeTemplate(template, targetDir);
|
|
61
|
-
try {
|
|
62
|
-
await fs$1.copyFile(path.join(targetDir, ".env.example"), path.join(targetDir, ".env"));
|
|
63
|
-
} catch {}
|
|
64
|
-
const readmePath = path.join(targetDir, "README.md");
|
|
65
|
-
const readme = `# ${path.basename(path.resolve(targetDir))}
|
|
66
|
-
|
|
67
|
-
A voice agent built with [aai](https://github.com/anthropics/aai).
|
|
68
|
-
|
|
69
|
-
## Getting started
|
|
70
|
-
|
|
71
|
-
\`\`\`sh
|
|
72
|
-
npm install # Install dependencies
|
|
73
|
-
npm run dev # Run locally (opens browser)
|
|
74
|
-
npm run deploy # Deploy to production
|
|
75
|
-
\`\`\`
|
|
76
|
-
|
|
77
|
-
## Secrets
|
|
78
|
-
|
|
79
|
-
Access secrets in your agent via \`ctx.env.MY_KEY\`.
|
|
80
|
-
|
|
81
|
-
**Local development** — add secrets to \`.env\` (auto-loaded by \`aai dev\`):
|
|
82
|
-
|
|
83
|
-
\`\`\`sh
|
|
84
|
-
ALPHA_VANTAGE_KEY=sk-abc123
|
|
85
|
-
MY_API_KEY=secret-value
|
|
86
|
-
\`\`\`
|
|
87
|
-
|
|
88
|
-
**Production** — set secrets on the server:
|
|
89
|
-
|
|
90
|
-
\`\`\`sh
|
|
91
|
-
aai secret put MY_KEY # Set a secret (prompts for value)
|
|
92
|
-
aai secret list # List secret names
|
|
93
|
-
aai secret delete MY_KEY # Remove a secret
|
|
94
|
-
\`\`\`
|
|
95
|
-
|
|
96
|
-
## Learn more
|
|
97
|
-
|
|
98
|
-
See \`CLAUDE.md\` for the full agent API reference.
|
|
99
|
-
`;
|
|
100
|
-
try {
|
|
101
|
-
await fs$1.writeFile(readmePath, readme, { flag: "wx" });
|
|
102
|
-
} catch (err) {
|
|
103
|
-
if (!(err instanceof Error && "code" in err && err.code === "EEXIST")) throw err;
|
|
104
|
-
}
|
|
105
|
-
return targetDir;
|
|
106
|
-
}
|
|
107
|
-
//#endregion
|
|
108
|
-
export { runInit };
|
package/dist/_link-D6mmDQ0B.mjs
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { consola } from "consola";
|
|
6
|
-
import { z } from "zod";
|
|
7
|
-
import { execFileSync } from "node:child_process";
|
|
8
|
-
//#region _link.ts
|
|
9
|
-
const WORKSPACE_PKGS = ["aai", "aai-ui"];
|
|
10
|
-
function getPackagesDir() {
|
|
11
|
-
const cliDir = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
-
const parent = path.resolve(cliDir, "..");
|
|
13
|
-
return fs.existsSync(path.join(parent, "aai", "package.json")) ? parent : path.resolve(parent, "..");
|
|
14
|
-
}
|
|
15
|
-
function rewriteWorkspaceDeps(cwd, rewrite, verb) {
|
|
16
|
-
const packagesDir = getPackagesDir();
|
|
17
|
-
const pkgJsonPath = path.join(cwd, "package.json");
|
|
18
|
-
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
19
|
-
const deps = pkgJson.dependencies ?? {};
|
|
20
|
-
const changed = [];
|
|
21
|
-
for (const pkgDir of WORKSPACE_PKGS) {
|
|
22
|
-
const localPath = path.join(packagesDir, pkgDir);
|
|
23
|
-
const name = z.object({ name: z.string() }).parse(JSON.parse(fs.readFileSync(path.join(localPath, "package.json"), "utf-8"))).name;
|
|
24
|
-
if (!deps[name]) continue;
|
|
25
|
-
const newVersion = rewrite(deps[name], localPath);
|
|
26
|
-
if (newVersion !== null) {
|
|
27
|
-
deps[name] = newVersion;
|
|
28
|
-
changed.push(name);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
if (changed.length === 0) {
|
|
32
|
-
consola.info(`No packages to ${verb}.`);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
fs.writeFileSync(pkgJsonPath, `${JSON.stringify(pkgJson, null, 2)}\n`);
|
|
36
|
-
consola.start(`${verb}: ${changed.join(", ")} → installing...`);
|
|
37
|
-
execFileSync("npm", ["install"], {
|
|
38
|
-
cwd,
|
|
39
|
-
stdio: "inherit"
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
function runLinkCommand(cwd) {
|
|
43
|
-
rewriteWorkspaceDeps(cwd, (_cur, localPath) => `file:${localPath}`, "Linked");
|
|
44
|
-
}
|
|
45
|
-
function runUnlinkCommand(cwd) {
|
|
46
|
-
rewriteWorkspaceDeps(cwd, (cur) => cur.startsWith("file:") ? "*" : null, "Unlinked");
|
|
47
|
-
}
|
|
48
|
-
//#endregion
|
|
49
|
-
export { runLinkCommand, runUnlinkCommand };
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { i as getApiKey } from "./_discover-D7HCLa_N.mjs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import fs from "node:fs/promises";
|
|
5
|
-
//#region _server-common.ts
|
|
6
|
-
/**
|
|
7
|
-
* Return the variable names declared in a `.env` file.
|
|
8
|
-
*
|
|
9
|
-
* Only used to determine *which* keys the developer intended as agent
|
|
10
|
-
* secrets — actual values are resolved from `process.env` (so shell
|
|
11
|
-
* overrides still win).
|
|
12
|
-
*/
|
|
13
|
-
function envFileKeys(content) {
|
|
14
|
-
const keys = [];
|
|
15
|
-
for (const raw of content.split("\n")) {
|
|
16
|
-
const line = raw.trim();
|
|
17
|
-
if (!line || line.startsWith("#")) continue;
|
|
18
|
-
const eq = line.indexOf("=");
|
|
19
|
-
if (eq === -1) continue;
|
|
20
|
-
const key = line.slice(0, eq).trim();
|
|
21
|
-
if (key) keys.push(key);
|
|
22
|
-
}
|
|
23
|
-
return keys;
|
|
24
|
-
}
|
|
25
|
-
/** Load an AgentDef by dynamically importing agent.ts via Node's native TS support. */
|
|
26
|
-
async function loadAgentDef(cwd) {
|
|
27
|
-
const agentDef = (await import(path.resolve(cwd, "agent.ts"))).default;
|
|
28
|
-
if (!agentDef || typeof agentDef !== "object" || !agentDef.name) throw new Error("agent.ts must export a default agent definition (from defineAgent())");
|
|
29
|
-
const missing = [];
|
|
30
|
-
if (typeof agentDef.name !== "string") missing.push("name (string)");
|
|
31
|
-
if (typeof agentDef.instructions !== "string") missing.push("instructions (string)");
|
|
32
|
-
if (typeof agentDef.greeting !== "string") missing.push("greeting (string)");
|
|
33
|
-
if (typeof agentDef.maxSteps !== "number" && typeof agentDef.maxSteps !== "function") missing.push("maxSteps (number or function)");
|
|
34
|
-
if (!agentDef.tools || typeof agentDef.tools !== "object" || Array.isArray(agentDef.tools)) missing.push("tools (object)");
|
|
35
|
-
if (missing.length > 0) throw new Error(`Invalid agent definition: missing or invalid fields: ${missing.join(", ")}. Use defineAgent() to create a valid agent definition.`);
|
|
36
|
-
return agentDef;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Build the `ctx.env` record that agent tools will see at runtime.
|
|
40
|
-
*
|
|
41
|
-
* Only variables explicitly declared in `.env` (plus `ASSEMBLYAI_API_KEY`)
|
|
42
|
-
* are included — matching the platform sandbox behavior where `ctx.env`
|
|
43
|
-
* contains only secrets set via `aai secret put`. This prevents agents
|
|
44
|
-
* from accidentally depending on shell-level vars (PATH, HOME, etc.) that
|
|
45
|
-
* won't exist in production.
|
|
46
|
-
*
|
|
47
|
-
* Values are resolved from `process.env` after loading `.env` via Node's
|
|
48
|
-
* built-in `process.loadEnvFile()`, so shell exports override `.env`.
|
|
49
|
-
*
|
|
50
|
-
* @param cwd - Project directory containing `.env` (optional).
|
|
51
|
-
* @param baseEnv - Override the environment to read values from (tests only).
|
|
52
|
-
*/
|
|
53
|
-
async function resolveServerEnv(cwd, baseEnv) {
|
|
54
|
-
let declaredKeys = [];
|
|
55
|
-
if (cwd) {
|
|
56
|
-
const envPath = path.join(cwd, ".env");
|
|
57
|
-
try {
|
|
58
|
-
declaredKeys = envFileKeys(await fs.readFile(envPath, "utf-8"));
|
|
59
|
-
process.loadEnvFile(envPath);
|
|
60
|
-
} catch {}
|
|
61
|
-
}
|
|
62
|
-
const source = baseEnv ?? process.env;
|
|
63
|
-
const env = {};
|
|
64
|
-
for (const key of declaredKeys) {
|
|
65
|
-
const val = source[key];
|
|
66
|
-
if (val !== void 0) env[key] = val;
|
|
67
|
-
}
|
|
68
|
-
if (!env.ASSEMBLYAI_API_KEY) env.ASSEMBLYAI_API_KEY = source.ASSEMBLYAI_API_KEY ?? await getApiKey();
|
|
69
|
-
return env;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Create and start an agent server with static file serving.
|
|
73
|
-
*
|
|
74
|
-
* NOTE: This dynamically imports `@alexkroman1/aai/server` which has peer
|
|
75
|
-
* dependencies on `hono` and `@hono/node-server`. Those packages are listed
|
|
76
|
-
* as direct dependencies of aai-cli (in package.json) solely to satisfy
|
|
77
|
-
* those peer deps — they are not imported directly by aai-cli code.
|
|
78
|
-
*/
|
|
79
|
-
async function bootServer(agentDef, clientDir, env, port) {
|
|
80
|
-
const { createServer } = await import("@alexkroman1/aai/server");
|
|
81
|
-
const server = createServer({
|
|
82
|
-
agent: agentDef,
|
|
83
|
-
clientDir,
|
|
84
|
-
env
|
|
85
|
-
});
|
|
86
|
-
await server.listen(port);
|
|
87
|
-
return server;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Boot the agent server without client serving.
|
|
91
|
-
*
|
|
92
|
-
* Used in dev mode where Vite handles client files with HMR,
|
|
93
|
-
* and only the backend (health, WebSocket) runs on this server.
|
|
94
|
-
*/
|
|
95
|
-
async function bootBackendServer(agentDef, env, port) {
|
|
96
|
-
const { createServer } = await import("@alexkroman1/aai/server");
|
|
97
|
-
const server = createServer({
|
|
98
|
-
agent: agentDef,
|
|
99
|
-
env
|
|
100
|
-
});
|
|
101
|
-
await server.listen(port);
|
|
102
|
-
return server;
|
|
103
|
-
}
|
|
104
|
-
//#endregion
|
|
105
|
-
export { resolveServerEnv as a, loadAgentDef as i, bootServer as n, envFileKeys as r, bootBackendServer as t };
|