@alexkroman1/aai-cli 0.12.3 → 1.0.2

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.
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ import { n as ensureApiKey, r as readProjectConfig } from "./_config-Dv-T6uRj.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 };
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * Shared HTTP helpers for platform API calls (deploy, delete, secrets).
5
5
  */
6
- const HINT_INVALID_API_KEY = "Your API key may be invalid. Check ~/.config/aai/config.json or set ASSEMBLYAI_API_KEY.";
6
+ const HINT_INVALID_API_KEY = "Your API key may be invalid. Run `aai` to re-enter your AssemblyAI API key.";
7
7
  /**
8
8
  * Send an authenticated request to the platform API.
9
9
  *
@@ -31,5 +31,16 @@ async function apiRequest(url, init, fetchFn = globalThis.fetch.bind(globalThis)
31
31
  function apiError(action, status, body, hint) {
32
32
  return /* @__PURE__ */ new Error(`${action} failed (HTTP ${status}): ${body}${hint ? `\n ${hint}` : ""}`);
33
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
+ }
34
45
  //#endregion
35
- export { apiError as n, apiRequest as r, HINT_INVALID_API_KEY as t };
46
+ export { apiRequestOrThrow as t };
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+ import { a as ok } from "./_output-BKdAJaM5.mjs";
3
+ import { r as validateAgentExport, t as fileExists } from "./_utils-DZo3_J_v.mjs";
4
+ import path from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+ import fs from "node:fs/promises";
7
+ import { agentToolsToSchemas, toAgentConfig } from "@alexkroman1/aai/manifest";
8
+ import { build } from "vite";
9
+ //#region _bundler.ts
10
+ /** Shared Vite build base config for agent bundles. */
11
+ function agentViteBuildBase(entry) {
12
+ return {
13
+ logLevel: "silent",
14
+ build: {
15
+ lib: {
16
+ entry,
17
+ formats: ["es"]
18
+ },
19
+ target: "node20",
20
+ minify: false,
21
+ rollupOptions: { output: { entryFileNames: "[name].js" } }
22
+ }
23
+ };
24
+ }
25
+ /**
26
+ * Bundle an agent directory: build agent.ts into worker ESM + extract config.
27
+ *
28
+ * - agent.ts is the single entry point: `export default agent({...})`
29
+ * - A single Vite build produces the worker ESM (all deps bundled in).
30
+ * The AgentDef is extracted from that bundle via dynamic import, avoiding a
31
+ * second build pass.
32
+ */
33
+ async function buildAgentBundle(cwd) {
34
+ const { log } = await import("./_ui-r2t6_2eP.mjs").then((n) => n.t);
35
+ const [worker, clientFiles] = await Promise.all([buildWorker(cwd), buildClient(cwd)]);
36
+ const agentDef = await evalWorkerBundle(worker, cwd);
37
+ log.step(`Bundling ${agentDef.name}`);
38
+ const config = toAgentConfig(agentDef);
39
+ const toolSchemas = agentToolsToSchemas(agentDef.tools ?? {});
40
+ return {
41
+ worker,
42
+ clientFiles,
43
+ agentConfig: {
44
+ ...config,
45
+ toolSchemas
46
+ }
47
+ };
48
+ }
49
+ /**
50
+ * Write the worker ESM to a temp file and dynamic-import it, returning
51
+ * the AgentDef default export. All dependencies are bundled in, so the
52
+ * file can be evaluated from any directory.
53
+ */
54
+ async function evalWorkerBundle(code, cwd) {
55
+ const evalDir = path.join(cwd, ".aai", "eval");
56
+ await fs.mkdir(evalDir, { recursive: true });
57
+ const tmpPath = path.join(evalDir, `agent-${Date.now()}-${Math.random().toString(36).slice(2)}.mjs`);
58
+ try {
59
+ await fs.writeFile(tmpPath, code);
60
+ const mod = await import(pathToFileURL(tmpPath).href);
61
+ const agentDef = mod.default ?? mod;
62
+ validateAgentExport(agentDef);
63
+ return agentDef;
64
+ } finally {
65
+ await fs.rm(tmpPath).catch(() => {});
66
+ }
67
+ }
68
+ /**
69
+ * Bundle agent.ts into a single ESM string for the sandbox worker.
70
+ *
71
+ * Zod is bundled in — zod 4's `Function()` usage is wrapped in try/catch
72
+ * and gracefully degrades in restricted environments like Deno.
73
+ */
74
+ async function buildWorker(cwd) {
75
+ const base = agentViteBuildBase(path.join(cwd, "agent.ts"));
76
+ const result = await build({
77
+ ...base,
78
+ plugins: [{
79
+ name: "raw-md",
80
+ transform(code, id) {
81
+ if (id.endsWith(".md")) return `export default ${JSON.stringify(code)}`;
82
+ }
83
+ }],
84
+ build: {
85
+ ...base.build,
86
+ lib: {
87
+ ...base.build.lib,
88
+ fileName: "worker"
89
+ },
90
+ write: false,
91
+ rollupOptions: { output: { entryFileNames: "[name].js" } }
92
+ }
93
+ });
94
+ const output = Array.isArray(result) ? result[0] : result;
95
+ if (!output) throw new Error("Vite produced no output for agent.ts");
96
+ const chunk = output.output.find((o) => o.type === "chunk" && o.isEntry);
97
+ if (!chunk) throw new Error("Vite produced no entry chunk for agent.ts");
98
+ return chunk.code;
99
+ }
100
+ /**
101
+ * Build the client SPA using Vite if client.tsx exists.
102
+ * Returns a map of relative file paths to string contents for deploy.
103
+ */
104
+ async function buildClient(cwd) {
105
+ if (!await fileExists(path.join(cwd, "client.tsx"))) return {};
106
+ const clientDir = path.join(cwd, ".aai", "client");
107
+ await build({
108
+ root: cwd,
109
+ base: "./",
110
+ logLevel: "silent",
111
+ build: {
112
+ outDir: ".aai/client",
113
+ emptyOutDir: true
114
+ }
115
+ });
116
+ const files = {};
117
+ async function walk(dir, prefix) {
118
+ for (const entry of await fs.readdir(dir, { withFileTypes: true })) {
119
+ const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
120
+ if (entry.isDirectory()) await walk(path.join(dir, entry.name), rel);
121
+ else files[rel] = await fs.readFile(path.join(dir, entry.name), "utf-8");
122
+ }
123
+ }
124
+ await walk(clientDir, "");
125
+ return files;
126
+ }
127
+ async function executeBuild(cwd) {
128
+ const { log } = await import("./_ui-r2t6_2eP.mjs").then((n) => n.t);
129
+ const bundle = await buildAgentBundle(cwd);
130
+ log.success("Build complete");
131
+ return ok({
132
+ name: bundle.agentConfig.name,
133
+ workerBytes: bundle.worker.length
134
+ });
135
+ }
136
+ //#endregion
137
+ export { buildAgentBundle, executeBuild };
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+ import { t as __exportAll } from "./rolldown-runtime-DacLjcLf.mjs";
3
+ import path from "node:path";
4
+ import * as p from "@clack/prompts";
5
+ import fs from "node:fs/promises";
6
+ import { consola } from "consola";
7
+ import os from "node:os";
8
+ import { z } from "zod";
9
+ //#region _config.ts
10
+ var _config_exports = /* @__PURE__ */ __exportAll({
11
+ ensureApiKey: () => ensureApiKey,
12
+ getConfigDir: () => getConfigDir,
13
+ readGlobalConfig: () => readGlobalConfig,
14
+ readProjectConfig: () => readProjectConfig,
15
+ writeGlobalConfig: () => writeGlobalConfig,
16
+ writeProjectConfig: () => writeProjectConfig
17
+ });
18
+ const ProjectConfigSchema = z.object({
19
+ slug: z.string(),
20
+ serverUrl: z.string(),
21
+ sessionId: z.string().optional()
22
+ });
23
+ function getConfigDir() {
24
+ if (process.platform === "win32") return path.join(process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming"), "aai");
25
+ return path.join(process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config"), "aai");
26
+ }
27
+ async function readProjectConfig(agentDir) {
28
+ try {
29
+ return ProjectConfigSchema.parse(JSON.parse(await fs.readFile(path.join(agentDir, ".aai", "project.json"), "utf-8")));
30
+ } catch (error) {
31
+ consola.debug(`Failed to read project config from ${path.join(agentDir, ".aai", "project.json")}:`, error);
32
+ return null;
33
+ }
34
+ }
35
+ async function writeProjectConfig(agentDir, data) {
36
+ const aaiDir = path.join(agentDir, ".aai");
37
+ await fs.mkdir(aaiDir, { recursive: true });
38
+ await fs.writeFile(path.join(aaiDir, "project.json"), `${JSON.stringify(data, null, 2)}\n`);
39
+ }
40
+ async function readGlobalConfig(configDir) {
41
+ const dir = configDir ?? getConfigDir();
42
+ try {
43
+ return JSON.parse(await fs.readFile(path.join(dir, "config.json"), "utf-8"));
44
+ } catch {
45
+ return {};
46
+ }
47
+ }
48
+ async function writeGlobalConfig(configDir, data) {
49
+ await fs.mkdir(configDir, { recursive: true });
50
+ await fs.writeFile(path.join(configDir, "config.json"), `${JSON.stringify(data, null, 2)}\n`);
51
+ }
52
+ async function ensureApiKey(configDir) {
53
+ const dir = configDir ?? getConfigDir();
54
+ const config = await readGlobalConfig(dir);
55
+ if (config.apiKey) return config.apiKey;
56
+ const envKey = process.env.ASSEMBLYAI_API_KEY;
57
+ if (envKey) {
58
+ await writeGlobalConfig(dir, {
59
+ ...config,
60
+ apiKey: envKey
61
+ });
62
+ return envKey;
63
+ }
64
+ const result = await p.password({ message: "Enter your AssemblyAI API key" });
65
+ if (p.isCancel(result)) {
66
+ p.cancel("Setup cancelled");
67
+ process.exit(0);
68
+ }
69
+ const apiKey = result;
70
+ await writeGlobalConfig(dir, {
71
+ ...config,
72
+ apiKey
73
+ });
74
+ return apiKey;
75
+ }
76
+ //#endregion
77
+ export { writeProjectConfig as i, ensureApiKey as n, readProjectConfig as r, _config_exports as t };
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env node
2
+ import { r as log } from "./_ui-r2t6_2eP.mjs";
3
+ import { r as validateAgentExport } from "./_utils-DZo3_J_v.mjs";
4
+ import { n as ensureApiKey } from "./_config-Dv-T6uRj.mjs";
5
+ import { t as resolveServerEnv } from "./_server-common-Pdb-KUSK.mjs";
6
+ import { existsSync, watch } from "node:fs";
7
+ import path from "node:path";
8
+ import { pathToFileURL } from "node:url";
9
+ import { errorMessage } from "@alexkroman1/aai";
10
+ import pDebounce from "p-debounce";
11
+ //#region _dev-server.ts
12
+ /**
13
+ * Dev server for directory-based agents.
14
+ *
15
+ * Imports agent.ts directly for the full agent definition,
16
+ * builds a runtime, and starts an HTTP+WebSocket server. Watches for
17
+ * file changes and restarts automatically. Optionally runs Vite for
18
+ * client SPA HMR.
19
+ */
20
+ async function resolveAgentEnv(root) {
21
+ const env = await resolveServerEnv(root);
22
+ if (!env.ASSEMBLYAI_API_KEY) env.ASSEMBLYAI_API_KEY = await ensureApiKey();
23
+ return env;
24
+ }
25
+ /**
26
+ * Load agent definition from agent.ts directly.
27
+ * Uses cache-busting query param for hot reload support.
28
+ */
29
+ async function loadAgentDef(cwd) {
30
+ const agentDef = (await import(`${pathToFileURL(path.join(cwd, "agent.ts")).href}?t=${Date.now()}`)).default;
31
+ validateAgentExport(agentDef);
32
+ return agentDef;
33
+ }
34
+ /**
35
+ * Watch the agent directory for changes and call `onChange` when detected.
36
+ * Debounces to avoid rapid restarts.
37
+ */
38
+ function watchDirectory(dir, onChange) {
39
+ const watchers = [];
40
+ const debouncedChange = pDebounce((filename) => {
41
+ log.info("File change detected, restarting...");
42
+ onChange(filename);
43
+ }, 300);
44
+ function handleChange(filename) {
45
+ if (filename && (filename.startsWith(".aai") || filename.includes("node_modules"))) return;
46
+ debouncedChange(filename);
47
+ }
48
+ watchers.push(watch(dir, { persistent: false }, (_event, filename) => handleChange(filename)));
49
+ return watchers;
50
+ }
51
+ /**
52
+ * Start the dev server for a directory-based agent.
53
+ *
54
+ * Returns a cleanup function to shut down the server and watchers.
55
+ */
56
+ async function startDevServer(opts) {
57
+ const { cwd, port } = opts;
58
+ const { createRuntime, createServer } = await import("@alexkroman1/aai/runtime");
59
+ const hasClient = existsSync(path.join(cwd, "client.tsx"));
60
+ const backendPort = hasClient ? port + 1 : port;
61
+ const vitePort = port;
62
+ const agentDef = await loadAgentDef(cwd);
63
+ const env = await resolveAgentEnv(cwd);
64
+ const agentServer = createServer({
65
+ runtime: createRuntime({
66
+ agent: agentDef,
67
+ env
68
+ }),
69
+ name: agentDef.name
70
+ });
71
+ await agentServer.listen(backendPort);
72
+ let viteServer;
73
+ if (hasClient) {
74
+ const { createServer: createViteServer } = await import("vite");
75
+ const target = `http://localhost:${backendPort}`;
76
+ viteServer = await createViteServer({
77
+ root: cwd,
78
+ server: {
79
+ port: vitePort,
80
+ proxy: {
81
+ "/health": target,
82
+ "/websocket": {
83
+ target,
84
+ ws: true
85
+ }
86
+ }
87
+ }
88
+ });
89
+ await viteServer.listen();
90
+ }
91
+ let restarting = false;
92
+ let currentServer = agentServer;
93
+ let currentVite = viteServer;
94
+ let currentEnv = env;
95
+ const watchers = watchDirectory(cwd, () => {
96
+ if (restarting) return;
97
+ restarting = true;
98
+ restart().finally(() => {
99
+ restarting = false;
100
+ });
101
+ });
102
+ async function restart() {
103
+ try {
104
+ await currentServer.close();
105
+ } catch {}
106
+ try {
107
+ const newAgentDef = await loadAgentDef(cwd);
108
+ currentEnv = await resolveAgentEnv(cwd);
109
+ const newServer = createServer({
110
+ runtime: createRuntime({
111
+ agent: newAgentDef,
112
+ env: currentEnv
113
+ }),
114
+ name: newAgentDef.name
115
+ });
116
+ await newServer.listen(backendPort);
117
+ currentServer = newServer;
118
+ log.success("Restarted");
119
+ } catch (err) {
120
+ log.error(`Restart failed: ${errorMessage(err)}`);
121
+ }
122
+ }
123
+ return async () => {
124
+ for (const w of watchers) w.close();
125
+ if (currentVite) {
126
+ await currentVite.close();
127
+ currentVite = void 0;
128
+ }
129
+ await currentServer.close();
130
+ };
131
+ }
132
+ //#endregion
133
+ export { startDevServer };
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+ import { isDevMode } from "./_agent-CzbSa09n.mjs";
3
+ import { existsSync } from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import fs 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 (process.env.AAI_TEMPLATES_DIR) return process.env.AAI_TEMPLATES_DIR;
23
+ if (isDevMode()) return resolveLocalTemplatesDir();
24
+ const { dir } = await downloadTemplate(`${GIGET_SOURCE}#${GIGET_REF}`, { force: true });
25
+ return dir;
26
+ }
27
+ /**
28
+ * Download a template into targetDir, merging scaffold files underneath.
29
+ */
30
+ async function downloadAndMergeTemplate(template, targetDir) {
31
+ const root = await resolveTemplatesDir();
32
+ const templatesDir = path.join(root, "templates");
33
+ const names = (await fs.readdir(templatesDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
34
+ if (!names.includes(template)) throw new Error(`Unknown template "${template}". Available templates: ${names.join(", ")}`);
35
+ await fs.cp(path.join(templatesDir, template), targetDir, {
36
+ recursive: true,
37
+ force: true
38
+ });
39
+ const scaffoldDir = path.join(root, "scaffold");
40
+ if (existsSync(scaffoldDir)) {
41
+ const entries = await fs.readdir(scaffoldDir, {
42
+ recursive: true,
43
+ withFileTypes: true
44
+ });
45
+ for (const entry of entries) {
46
+ if (!entry.isFile()) continue;
47
+ const rel = path.relative(scaffoldDir, path.join(entry.parentPath, entry.name));
48
+ const destPath = path.join(targetDir, rel);
49
+ await fs.mkdir(path.dirname(destPath), { recursive: true });
50
+ try {
51
+ await fs.copyFile(path.join(scaffoldDir, rel), destPath, fs.constants.COPYFILE_EXCL);
52
+ } catch (err) {
53
+ if (!(err instanceof Error && "code" in err && err.code === "EEXIST")) throw err;
54
+ }
55
+ }
56
+ }
57
+ }
58
+ //#endregion
59
+ //#region _init.ts
60
+ function readmeContent(slug) {
61
+ return `# ${slug}
62
+
63
+ A voice agent built with [aai](https://github.com/anthropics/aai).
64
+
65
+ ## Getting started
66
+
67
+ \`\`\`sh
68
+ npm install # Install dependencies
69
+ aai dev # Run locally (opens browser)
70
+ aai deploy # Deploy to production
71
+ \`\`\`
72
+
73
+ ## Secrets
74
+
75
+ Access secrets in your agent via \`ctx.env.MY_KEY\`.
76
+
77
+ **Local development** — add secrets to \`.env\` (auto-loaded by \`aai dev\`):
78
+
79
+ \`\`\`sh
80
+ ALPHA_VANTAGE_KEY=sk-abc123
81
+ MY_API_KEY=secret-value
82
+ \`\`\`
83
+
84
+ **Production** — set secrets on the server:
85
+
86
+ \`\`\`sh
87
+ aai secret put MY_KEY # Set a secret (prompts for value)
88
+ aai secret list # List secret names
89
+ aai secret delete MY_KEY # Remove a secret
90
+ \`\`\`
91
+
92
+ `;
93
+ }
94
+ /**
95
+ * Map from npm package name to directory name under packages/.
96
+ * Used to rewrite published version ranges to link: paths in dev mode.
97
+ */
98
+ const WORKSPACE_PKG_DIRS = {
99
+ "@alexkroman1/aai": "aai",
100
+ "@alexkroman1/aai-cli": "aai-cli",
101
+ "@alexkroman1/aai-ui": "aai-ui",
102
+ "aai-server": "aai-server",
103
+ "aai-templates": "aai-templates"
104
+ };
105
+ /** Rewrite workspace deps to link: paths so pnpm links to local source. */
106
+ async function patchPackageJsonForWorkspace(targetDir) {
107
+ const pkgPath = path.join(targetDir, "package.json");
108
+ let raw;
109
+ try {
110
+ raw = await fs.readFile(pkgPath, "utf-8");
111
+ } catch {
112
+ return;
113
+ }
114
+ const pkgJson = JSON.parse(raw);
115
+ pkgJson.name = path.basename(targetDir);
116
+ delete pkgJson.packageManager;
117
+ const { getMonorepoRoot } = await import("./_agent-CzbSa09n.mjs");
118
+ const root = getMonorepoRoot();
119
+ if (!root) return;
120
+ const packagesDir = path.join(root, "packages");
121
+ for (const field of ["dependencies", "devDependencies"]) {
122
+ const deps = pkgJson[field];
123
+ if (!deps) continue;
124
+ for (const key of Object.keys(deps)) {
125
+ const dir = WORKSPACE_PKG_DIRS[key];
126
+ if (dir) deps[key] = `link:${path.relative(targetDir, path.join(packagesDir, dir))}`;
127
+ }
128
+ }
129
+ await fs.writeFile(pkgPath, `${JSON.stringify(pkgJson, null, 2)}\n`);
130
+ }
131
+ async function runInit(opts) {
132
+ const { targetDir } = opts;
133
+ await downloadAndMergeTemplate(opts.template ?? "simple", targetDir);
134
+ if (isDevMode()) {
135
+ await patchPackageJsonForWorkspace(targetDir);
136
+ try {
137
+ await fs.unlink(path.join(targetDir, ".npmrc"));
138
+ } catch {}
139
+ }
140
+ try {
141
+ await fs.copyFile(path.join(targetDir, ".env.example"), path.join(targetDir, ".env"));
142
+ } catch {}
143
+ const readmePath = path.join(targetDir, "README.md");
144
+ const slug = path.basename(path.resolve(targetDir));
145
+ try {
146
+ await fs.writeFile(readmePath, readmeContent(slug), { flag: "wx" });
147
+ } catch (err) {
148
+ if (!(err instanceof Error && "code" in err && err.code === "EEXIST")) throw err;
149
+ }
150
+ return targetDir;
151
+ }
152
+ //#endregion
153
+ export { runInit };
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+ import { t as __exportAll } from "./rolldown-runtime-DacLjcLf.mjs";
3
+ //#region _output.ts
4
+ var _output_exports = /* @__PURE__ */ __exportAll({
5
+ CliError: () => CliError,
6
+ fail: () => fail,
7
+ getOutputMode: () => getOutputMode,
8
+ ok: () => ok,
9
+ withOutput: () => withOutput
10
+ });
11
+ /**
12
+ * Determine output mode from CLI flags and TTY state.
13
+ *
14
+ * Priority: --json flag > --no-json flag > TTY auto-detection.
15
+ */
16
+ function getOutputMode(args, isTTY = Boolean(process.stdout.isTTY)) {
17
+ if (args.json === true) return "json";
18
+ if (args.json === false) return "human";
19
+ return isTTY ? "human" : "json";
20
+ }
21
+ /**
22
+ * Wrap a command function to handle output formatting.
23
+ *
24
+ * - `fn` does the work and returns a `CommandResult<T>`. It must not print.
25
+ * - `humanRender` formats the result for human-readable TTY output.
26
+ * - In JSON mode, writes exactly one JSON line to stdout.
27
+ */
28
+ async function withOutput(mode, fn, humanRender) {
29
+ const result = await fn();
30
+ if (mode === "json") {
31
+ process.stdout.write(`${JSON.stringify(result)}\n`);
32
+ if (!result.ok) process.exit(1);
33
+ } else {
34
+ humanRender(result);
35
+ if (!result.ok) process.exit(1);
36
+ }
37
+ }
38
+ /** Create an ok result. */
39
+ function ok(data) {
40
+ return {
41
+ ok: true,
42
+ data
43
+ };
44
+ }
45
+ /** Create an error result. */
46
+ function fail(code, error, hint) {
47
+ return hint ? {
48
+ ok: false,
49
+ error,
50
+ code,
51
+ hint
52
+ } : {
53
+ ok: false,
54
+ error,
55
+ code
56
+ };
57
+ }
58
+ /** Typed CLI error that carries a structured error code and optional hint. */
59
+ var CliError = class extends Error {
60
+ code;
61
+ hint;
62
+ constructor(code, message, hint) {
63
+ super(message);
64
+ this.name = "CliError";
65
+ this.code = code;
66
+ if (hint !== void 0) this.hint = hint;
67
+ }
68
+ };
69
+ //#endregion
70
+ export { ok as a, getOutputMode as i, _output_exports as n, fail as r, CliError as t };
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import path from "node:path";
3
+ import fs from "node:fs/promises";
4
+ import { parse } from "dotenv";
5
+ //#region _server-common.ts
6
+ /**
7
+ * Build the `ctx.env` record that agent tools will see at runtime.
8
+ *
9
+ * Only variables explicitly declared in `.env` are included — matching
10
+ * the platform sandbox behavior where `ctx.env`
11
+ * contains only secrets set via `aai secret put`. This prevents agents
12
+ * from accidentally depending on shell-level vars (PATH, HOME, etc.) that
13
+ * won't exist in production.
14
+ *
15
+ * Values are resolved by merging the `.env` file with the current
16
+ * environment — existing shell exports take precedence over `.env`
17
+ * defaults, without mutating `process.env`.
18
+ *
19
+ * @param cwd - Project directory containing `.env` (optional).
20
+ * @param baseEnv - Override the environment to read values from (tests only).
21
+ */
22
+ async function resolveServerEnv(cwd, baseEnv) {
23
+ let fileEntries = {};
24
+ if (cwd) try {
25
+ fileEntries = parse(await fs.readFile(path.join(cwd, ".env"), "utf-8"));
26
+ } catch {}
27
+ const source = baseEnv ?? process.env;
28
+ const env = {};
29
+ for (const [key, fileVal] of Object.entries(fileEntries)) {
30
+ const val = source[key] ?? fileVal;
31
+ if (val !== void 0) env[key] = val;
32
+ }
33
+ return env;
34
+ }
35
+ //#endregion
36
+ export { resolveServerEnv as t };