@alexkroman1/aai-cli 0.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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/dist/_build-p1HHkdon.mjs +132 -0
  3. package/dist/_discover-BzlCDVZ6.mjs +161 -0
  4. package/dist/_init-l_uoyFCN.mjs +82 -0
  5. package/dist/_link-BGXGFYWa.mjs +47 -0
  6. package/dist/_server-common-qLA1QU2C.mjs +36 -0
  7. package/dist/_ui-kJIua5L9.mjs +44 -0
  8. package/dist/cli.mjs +318 -0
  9. package/dist/deploy-KyNJaoP5.mjs +86 -0
  10. package/dist/dev-DBFvKyzk.mjs +39 -0
  11. package/dist/init-BWG5OrQa.mjs +65 -0
  12. package/dist/rag-BnCMnccf.mjs +173 -0
  13. package/dist/secret-CzeHIGzE.mjs +50 -0
  14. package/dist/start-C1qkhU4O.mjs +23 -0
  15. package/package.json +39 -0
  16. package/templates/_shared/.env.example +5 -0
  17. package/templates/_shared/CLAUDE.md +1051 -0
  18. package/templates/_shared/biome.json +32 -0
  19. package/templates/_shared/global.d.ts +1 -0
  20. package/templates/_shared/index.html +16 -0
  21. package/templates/_shared/package.json +23 -0
  22. package/templates/_shared/tsconfig.json +15 -0
  23. package/templates/code-interpreter/agent.ts +27 -0
  24. package/templates/code-interpreter/client.tsx +3 -0
  25. package/templates/css.d.ts +1 -0
  26. package/templates/dispatch-center/agent.ts +1227 -0
  27. package/templates/dispatch-center/client.tsx +505 -0
  28. package/templates/embedded-assets/agent.ts +48 -0
  29. package/templates/embedded-assets/client.tsx +3 -0
  30. package/templates/embedded-assets/knowledge.json +20 -0
  31. package/templates/health-assistant/agent.ts +160 -0
  32. package/templates/health-assistant/client.tsx +3 -0
  33. package/templates/infocom-adventure/agent.ts +164 -0
  34. package/templates/infocom-adventure/client.tsx +300 -0
  35. package/templates/math-buddy/agent.ts +21 -0
  36. package/templates/math-buddy/client.tsx +3 -0
  37. package/templates/memory-agent/agent.ts +20 -0
  38. package/templates/memory-agent/client.tsx +3 -0
  39. package/templates/night-owl/agent.ts +98 -0
  40. package/templates/night-owl/client.tsx +12 -0
  41. package/templates/personal-finance/agent.ts +26 -0
  42. package/templates/personal-finance/client.tsx +3 -0
  43. package/templates/pizza-ordering/agent.ts +218 -0
  44. package/templates/pizza-ordering/client.tsx +264 -0
  45. package/templates/simple/agent.ts +6 -0
  46. package/templates/simple/client.tsx +3 -0
  47. package/templates/smart-research/agent.ts +164 -0
  48. package/templates/smart-research/client.tsx +3 -0
  49. package/templates/solo-rpg/agent.ts +1244 -0
  50. package/templates/solo-rpg/client.tsx +698 -0
  51. package/templates/support/README.md +62 -0
  52. package/templates/support/agent.ts +19 -0
  53. package/templates/support/client.tsx +3 -0
  54. package/templates/travel-concierge/agent.ts +29 -0
  55. package/templates/travel-concierge/client.tsx +3 -0
  56. package/templates/tsconfig.json +1 -0
  57. package/templates/web-researcher/agent.ts +17 -0
  58. package/templates/web-researcher/client.tsx +3 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AAI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+ import { o as loadAgent } from "./_discover-BzlCDVZ6.mjs";
3
+ import { a as step, i as runCommand } from "./_ui-kJIua5L9.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 } 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) {
18
+ super(message);
19
+ this.name = "BundleError";
20
+ }
21
+ };
22
+ /** Read all files in a directory as a map of relative paths to contents. */
23
+ async function readDirFiles(dir) {
24
+ let entries;
25
+ try {
26
+ entries = await fs.readdir(dir, {
27
+ recursive: true,
28
+ withFileTypes: true
29
+ });
30
+ } catch (err) {
31
+ if (err instanceof Error && "code" in err && err.code === "ENOENT") return {};
32
+ throw err;
33
+ }
34
+ const files = {};
35
+ await Promise.all(entries.filter((e) => e.isFile()).map(async (e) => {
36
+ const full = path.join(e.parentPath, e.name);
37
+ files[path.relative(dir, full)] = await fs.readFile(full, "utf-8");
38
+ }));
39
+ return files;
40
+ }
41
+ /**
42
+ * Bundles an agent project into deployable artifacts using Vite.
43
+ *
44
+ * Writes all output to `.aai/` on disk:
45
+ * - `.aai/build/worker.js` — the platform worker bundle
46
+ * - `.aai/client/` — standard Vite multi-file output (index.html + assets/)
47
+ *
48
+ * Both `aai dev` and `aai deploy` use this function identically.
49
+ */
50
+ async function bundleAgent(agent, opts) {
51
+ const aaiDir = path.join(agent.dir, ".aai");
52
+ const buildDir = path.join(aaiDir, "build");
53
+ const clientDir = path.join(aaiDir, "client");
54
+ try {
55
+ await build({
56
+ configFile: false,
57
+ root: agent.dir,
58
+ logLevel: "warn",
59
+ build: {
60
+ rollupOptions: {
61
+ input: path.join(agent.dir, "agent.ts"),
62
+ output: {
63
+ format: "es",
64
+ entryFileNames: "worker.js"
65
+ }
66
+ },
67
+ outDir: buildDir,
68
+ emptyOutDir: true,
69
+ minify: true,
70
+ target: "es2022"
71
+ }
72
+ });
73
+ } catch (err) {
74
+ throw new BundleError(errorMessage(err));
75
+ }
76
+ if (!(opts?.skipClient || !agent.clientEntry)) try {
77
+ await build({
78
+ root: agent.dir,
79
+ base: "./",
80
+ logLevel: "warn",
81
+ plugins: [preact(), tailwindcss()],
82
+ resolve: { dedupe: ["preact", "@preact/signals"] },
83
+ build: {
84
+ outDir: clientDir,
85
+ emptyOutDir: true,
86
+ minify: true,
87
+ target: "es2022"
88
+ }
89
+ });
90
+ } catch (err) {
91
+ throw new BundleError(errorMessage(err));
92
+ }
93
+ const worker = await fs.readFile(path.join(buildDir, "worker.js"), "utf-8");
94
+ return {
95
+ worker,
96
+ clientFiles: await readDirFiles(clientDir),
97
+ clientDir,
98
+ workerBytes: Buffer.byteLength(worker)
99
+ };
100
+ }
101
+ //#endregion
102
+ //#region _build.ts
103
+ /**
104
+ * Discover the agent entry and bundle both worker and client.
105
+ *
106
+ * Shared by `aai build`, `aai dev`, and `aai deploy`.
107
+ */
108
+ async function buildAgentBundle(cwd, log) {
109
+ const agent = await loadAgent(cwd);
110
+ if (!agent) throw new Error("No agent found — run `aai init` first");
111
+ log(step("Bundle", agent.slug));
112
+ let bundle;
113
+ try {
114
+ bundle = await bundleAgent(agent);
115
+ } catch (err) {
116
+ if (err instanceof BundleError) throw new Error(`Bundle failed: ${err.message}`);
117
+ throw err;
118
+ }
119
+ const kb = (bundle.workerBytes / 1024).toFixed(1);
120
+ const clientCount = Object.keys(bundle.clientFiles).length;
121
+ log(`worker: ${kb} KB, client: ${clientCount} file(s)`);
122
+ return bundle;
123
+ }
124
+ /** Bundle the agent and report success. Used by `aai build`. */
125
+ async function runBuildCommand(cwd) {
126
+ await runCommand(async ({ log }) => {
127
+ await buildAgentBundle(cwd, log);
128
+ log(step("Build", "ok"));
129
+ });
130
+ }
131
+ //#endregion
132
+ export { buildAgentBundle, runBuildCommand };
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import fs$1 from "node:fs/promises";
6
+ import { humanId } from "human-id";
7
+ import { z } from "zod";
8
+ import * as p from "@clack/prompts";
9
+ //#region _prompts.ts
10
+ /**
11
+ * Prompt the user for a password (masked input).
12
+ * Returns the entered string.
13
+ */
14
+ async function askPassword(message) {
15
+ const value = await p.password({ message });
16
+ if (p.isCancel(value)) {
17
+ p.cancel("Cancelled.");
18
+ process.exit(0);
19
+ }
20
+ return value;
21
+ }
22
+ /**
23
+ * Prompt the user for text input with a default value.
24
+ * Returns the entered string, or the default if empty.
25
+ */
26
+ async function askText(message, defaultValue) {
27
+ const value = await p.text({
28
+ message,
29
+ placeholder: defaultValue,
30
+ defaultValue
31
+ });
32
+ if (p.isCancel(value)) {
33
+ p.cancel("Cancelled.");
34
+ process.exit(0);
35
+ }
36
+ return value || defaultValue;
37
+ }
38
+ //#endregion
39
+ //#region _discover.ts
40
+ const AuthConfigSchema = z.object({ assemblyai_api_key: z.string().optional() });
41
+ const ProjectConfigSchema = z.object({
42
+ slug: z.string(),
43
+ serverUrl: z.string()
44
+ });
45
+ /** Resolve the working directory from INIT_CWD or process.cwd(). */
46
+ function resolveCwd() {
47
+ return process.env.INIT_CWD || process.cwd();
48
+ }
49
+ /**
50
+ * Generates a human-readable slug using human-id.
51
+ */
52
+ function generateSlug() {
53
+ return humanId({
54
+ separator: "-",
55
+ capitalize: false
56
+ });
57
+ }
58
+ const CONFIG_DIR = path.join(process.env.HOME ?? process.env.USERPROFILE ?? ".", ".config", "aai");
59
+ const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
60
+ async function readAuthConfig() {
61
+ try {
62
+ return AuthConfigSchema.parse(JSON.parse(await fs$1.readFile(CONFIG_FILE, "utf-8")));
63
+ } catch {
64
+ return {};
65
+ }
66
+ }
67
+ async function writeAuthConfig(config) {
68
+ await fs$1.mkdir(CONFIG_DIR, { recursive: true });
69
+ await fs$1.writeFile(CONFIG_FILE, `${JSON.stringify(config, null, 2)}\n`);
70
+ if (process.platform !== "win32") await fs$1.chmod(CONFIG_FILE, 384);
71
+ }
72
+ /**
73
+ * Retrieves the AssemblyAI API key from `~/.config/aai/config.json`.
74
+ * If not found, interactively prompts the user and persists it.
75
+ */
76
+ async function getApiKey() {
77
+ if (process.env.ASSEMBLYAI_API_KEY) return process.env.ASSEMBLYAI_API_KEY;
78
+ const config = await readAuthConfig();
79
+ if (config.assemblyai_api_key) {
80
+ process.env.ASSEMBLYAI_API_KEY = config.assemblyai_api_key;
81
+ return config.assemblyai_api_key;
82
+ }
83
+ let key;
84
+ while (!key) key = await askPassword("ASSEMBLYAI_API_KEY");
85
+ config.assemblyai_api_key = key;
86
+ process.env.ASSEMBLYAI_API_KEY = key;
87
+ await writeAuthConfig(config);
88
+ return key;
89
+ }
90
+ /**
91
+ * Reads `.aai/project.json` from an agent directory.
92
+ * Returns null if the file doesn't exist.
93
+ */
94
+ async function readProjectConfig(agentDir) {
95
+ try {
96
+ return ProjectConfigSchema.parse(JSON.parse(await fs$1.readFile(path.join(agentDir, ".aai", "project.json"), "utf-8")));
97
+ } catch {
98
+ return null;
99
+ }
100
+ }
101
+ /**
102
+ * Writes `.aai/project.json` to an agent directory.
103
+ */
104
+ async function writeProjectConfig(agentDir, data) {
105
+ const aaiDir = path.join(agentDir, ".aai");
106
+ await fs$1.mkdir(aaiDir, { recursive: true });
107
+ await fs$1.writeFile(path.join(aaiDir, "project.json"), `${JSON.stringify(data, null, 2)}\n`);
108
+ }
109
+ /**
110
+ * Read project config (throws if missing), resolve API key and server URL.
111
+ * Shared by secret and rag commands.
112
+ */
113
+ async function getServerInfo(cwd, explicitServer, explicitApiKey) {
114
+ const config = await readProjectConfig(cwd);
115
+ if (!config) throw new Error("No .aai/project.json found — deploy first with `aai deploy`");
116
+ const apiKey = explicitApiKey ?? await getApiKey();
117
+ return {
118
+ serverUrl: resolveServerUrl(explicitServer, config.serverUrl),
119
+ slug: config.slug,
120
+ apiKey
121
+ };
122
+ }
123
+ /** Check if the CLI is running from the monorepo (dev mode). */
124
+ function isDevMode() {
125
+ const cliDir = path.dirname(fileURLToPath(import.meta.url));
126
+ const packagesDir = path.resolve(cliDir, "..");
127
+ const altPackagesDir = path.resolve(cliDir, "../..");
128
+ return existsSync(path.join(packagesDir, "aai", "package.json")) || existsSync(path.join(altPackagesDir, "aai", "package.json"));
129
+ }
130
+ /** Resolve the server URL from an explicit value, project config, or default. */
131
+ function resolveServerUrl(explicit, configUrl) {
132
+ return explicit || configUrl || (isDevMode() ? "http://localhost:8787" : "https://aai-agent.fly.dev");
133
+ }
134
+ async function fileExists(p) {
135
+ try {
136
+ await fs$1.access(p);
137
+ return true;
138
+ } catch {
139
+ return false;
140
+ }
141
+ }
142
+ /**
143
+ * Loads agent metadata from a directory by checking for `agent.ts` and
144
+ * resolving the client entry point.
145
+ *
146
+ * Env vars are NOT read from `.env` — they're managed on the server
147
+ * via `aai env add` (like `vercel env add`).
148
+ */
149
+ async function loadAgent(dir) {
150
+ if (!await fileExists(path.join(dir, "agent.ts"))) return null;
151
+ const slug = (await readProjectConfig(dir))?.slug ?? generateSlug();
152
+ const clientEntry = await fileExists(path.join(dir, "client.tsx")) ? path.join(dir, "client.tsx") : "";
153
+ return {
154
+ slug,
155
+ dir,
156
+ entryPoint: path.join(dir, "agent.ts"),
157
+ clientEntry
158
+ };
159
+ }
160
+ //#endregion
161
+ export { isDevMode as a, resolveCwd as c, askPassword as d, askText as f, getServerInfo as i, resolveServerUrl as l, generateSlug as n, loadAgent as o, getApiKey as r, readProjectConfig as s, fileExists as t, writeProjectConfig as u };
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+ import path from "node:path";
3
+ import fs from "node:fs/promises";
4
+ //#region _init.ts
5
+ async function listTemplates(dir) {
6
+ const templates = [];
7
+ const entries = await fs.readdir(dir, { withFileTypes: true });
8
+ for (const entry of entries) if (entry.isDirectory() && !entry.name.startsWith("_")) templates.push(entry.name);
9
+ return templates.sort();
10
+ }
11
+ /**
12
+ * Copy all files from `src` into `dest`, skipping files that already exist
13
+ * in `dest` so that template-specific files take precedence over shared ones.
14
+ */
15
+ async function copyDirNoOverwrite(src, dest) {
16
+ const entries = await fs.readdir(src, {
17
+ recursive: true,
18
+ withFileTypes: true
19
+ });
20
+ for (const entry of entries) {
21
+ if (!entry.isFile()) continue;
22
+ const rel = path.relative(src, path.join(entry.parentPath, entry.name));
23
+ const destPath = path.join(dest, rel);
24
+ await fs.mkdir(path.dirname(destPath), { recursive: true });
25
+ try {
26
+ await fs.copyFile(path.join(src, rel), destPath, fs.constants.COPYFILE_EXCL);
27
+ } catch (err) {
28
+ if (!(err instanceof Error && "code" in err && err.code === "EEXIST")) throw err;
29
+ }
30
+ }
31
+ }
32
+ async function runInit(opts) {
33
+ const { targetDir, template, templatesDir } = opts;
34
+ const available = await listTemplates(templatesDir);
35
+ if (!available.includes(template)) throw new Error(`unknown template '${template}' -- available: ${available.join(", ")}`);
36
+ await fs.cp(path.join(templatesDir, template), targetDir, {
37
+ recursive: true,
38
+ force: true
39
+ });
40
+ await copyDirNoOverwrite(path.join(templatesDir, "_shared"), targetDir);
41
+ try {
42
+ await fs.copyFile(path.join(targetDir, ".env.example"), path.join(targetDir, ".env"));
43
+ } catch {}
44
+ const readmePath = path.join(targetDir, "README.md");
45
+ const readme = `# ${path.basename(path.resolve(targetDir))}
46
+
47
+ A voice agent built with [aai](https://github.com/anthropics/aai).
48
+
49
+ ## Getting started
50
+
51
+ \`\`\`sh
52
+ npm install # Install dependencies
53
+ npm run dev # Run locally (opens browser)
54
+ npm run deploy # Deploy to production
55
+ \`\`\`
56
+
57
+ ## Environment variables
58
+
59
+ Secrets are managed on the server, not in local files:
60
+
61
+ \`\`\`sh
62
+ aai env add MY_KEY # Set a secret (prompts for value)
63
+ aai env ls # List secret names
64
+ aai env pull # Pull names into .env for reference
65
+ aai env rm MY_KEY # Remove a secret
66
+ \`\`\`
67
+
68
+ Access secrets in your agent via \`ctx.env.MY_KEY\`.
69
+
70
+ ## Learn more
71
+
72
+ See \`CLAUDE.md\` for the full agent API reference.
73
+ `;
74
+ try {
75
+ await fs.writeFile(readmePath, readme, { flag: "wx" });
76
+ } catch (err) {
77
+ if (err.code !== "EEXIST") throw err;
78
+ }
79
+ return targetDir;
80
+ }
81
+ //#endregion
82
+ export { runInit };
@@ -0,0 +1,47 @@
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 { execFileSync } from "node:child_process";
6
+ //#region _link.ts
7
+ const WORKSPACE_PKGS = ["aai", "aai-ui"];
8
+ function getPackagesDir() {
9
+ const cliDir = path.dirname(fileURLToPath(import.meta.url));
10
+ const parent = path.resolve(cliDir, "..");
11
+ return fs.existsSync(path.join(parent, "aai", "package.json")) ? parent : path.resolve(parent, "..");
12
+ }
13
+ function rewriteWorkspaceDeps(cwd, rewrite, verb) {
14
+ const packagesDir = getPackagesDir();
15
+ const pkgJsonPath = path.join(cwd, "package.json");
16
+ const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
17
+ const deps = pkgJson.dependencies ?? {};
18
+ const changed = [];
19
+ for (const pkgDir of WORKSPACE_PKGS) {
20
+ const localPath = path.join(packagesDir, pkgDir);
21
+ const name = JSON.parse(fs.readFileSync(path.join(localPath, "package.json"), "utf-8")).name;
22
+ if (!deps[name]) continue;
23
+ const newVersion = rewrite(deps[name], localPath);
24
+ if (newVersion !== null) {
25
+ deps[name] = newVersion;
26
+ changed.push(name);
27
+ }
28
+ }
29
+ if (changed.length === 0) {
30
+ console.log(`No packages to ${verb}.`);
31
+ return;
32
+ }
33
+ fs.writeFileSync(pkgJsonPath, `${JSON.stringify(pkgJson, null, 2)}\n`);
34
+ console.log(`${verb}: ${changed.join(", ")} → installing...`);
35
+ execFileSync("npm", ["install"], {
36
+ cwd,
37
+ stdio: "inherit"
38
+ });
39
+ }
40
+ function runLinkCommand(cwd) {
41
+ rewriteWorkspaceDeps(cwd, (_cur, localPath) => `file:${localPath}`, "Linked");
42
+ }
43
+ function runUnlinkCommand(cwd) {
44
+ rewriteWorkspaceDeps(cwd, (cur) => cur.startsWith("file:") ? "*" : null, "Unlinked");
45
+ }
46
+ //#endregion
47
+ export { runLinkCommand, runUnlinkCommand };
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import { r as getApiKey } from "./_discover-BzlCDVZ6.mjs";
3
+ import path from "node:path";
4
+ import fs from "node:fs/promises";
5
+ //#region _server-common.ts
6
+ /** Load an AgentDef by dynamically importing agent.ts via Node's native TS support. */
7
+ async function loadAgentDef(cwd) {
8
+ const agentDef = (await import(path.resolve(cwd, "agent.ts"))).default;
9
+ if (!agentDef || typeof agentDef !== "object" || !agentDef.name) throw new Error("agent.ts must export a default agent definition (from defineAgent())");
10
+ return agentDef;
11
+ }
12
+ /**
13
+ * Build an env record, ensuring ASSEMBLYAI_API_KEY is set.
14
+ *
15
+ * @param baseEnv - Override the base environment (defaults to process.env).
16
+ */
17
+ async function resolveServerEnv(baseEnv) {
18
+ const env = Object.fromEntries(Object.entries(baseEnv ?? process.env).filter((e) => e[1] !== void 0));
19
+ if (!env.ASSEMBLYAI_API_KEY) env.ASSEMBLYAI_API_KEY = await getApiKey();
20
+ return env;
21
+ }
22
+ /** Create and start an agent server with static file serving. */
23
+ async function bootServer(agentDef, clientDir, env, port) {
24
+ const clientHtml = await fs.readFile(path.join(clientDir, "index.html"), "utf-8");
25
+ const { createServer } = await import("@alexkroman1/aai/server");
26
+ const server = createServer({
27
+ agent: agentDef,
28
+ clientHtml,
29
+ clientDir,
30
+ env
31
+ });
32
+ await server.listen(port);
33
+ return server;
34
+ }
35
+ //#endregion
36
+ export { loadAgentDef as n, resolveServerEnv as r, bootServer as t };
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ import pc from "picocolors";
3
+ //#region _ui.ts
4
+ /** Interactive/info color wrapper. */
5
+ function interactive(s) {
6
+ return pc.cyan(s);
7
+ }
8
+ /** Colored step message: bold action label + message. */
9
+ function step(action, msg) {
10
+ return `${pc.bold(pc.yellow(action))} ${msg}`;
11
+ }
12
+ /** Informational step message: bold cyan action + message. */
13
+ function stepInfo(action, msg) {
14
+ return `${pc.bold(pc.cyan(action))} ${msg}`;
15
+ }
16
+ /** Dimmed info sub-line (indented). */
17
+ function info(msg) {
18
+ return pc.dim(` ${msg}`);
19
+ }
20
+ /** Detail sub-line (indented). */
21
+ function detail(msg) {
22
+ return ` ${msg}`;
23
+ }
24
+ /** Yellow warning message. */
25
+ function warn(msg) {
26
+ return `${pc.yellow("!")} ${msg}`;
27
+ }
28
+ /**
29
+ * Run an async command function, logging each step to stdout.
30
+ * Replaces the Ink `runWithInk` pattern.
31
+ */
32
+ async function runCommand(fn) {
33
+ const log = (msg) => console.log(msg);
34
+ const setStatus = (msg) => {
35
+ if (msg) process.stdout.write(`\r${pc.dim(msg)}`);
36
+ else process.stdout.write("\r\x1B[K");
37
+ };
38
+ await fn({
39
+ log,
40
+ setStatus
41
+ });
42
+ }
43
+ //#endregion
44
+ export { step as a, runCommand as i, info as n, stepInfo as o, interactive as r, warn as s, detail as t };