@alexkroman1/aai-cli 5.1.0 → 5.2.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 (119) hide show
  1. package/dist/{_agent-D39qatZJ.mjs → _agent-D8zBb3M5.mjs} +2 -3
  2. package/dist/_agent.d.ts +39 -0
  3. package/dist/{_api-client-a6cPMebU.mjs → _api-client-MenP4-O7.mjs} +3 -1
  4. package/dist/_api-client.d.ts +37 -0
  5. package/dist/{_bundler-DdmuZzeR.mjs → _bundler-DFS4xxqE.mjs} +3 -13
  6. package/dist/_bundler.d.ts +46 -0
  7. package/dist/{_config-Czjhvaax.mjs → _config-D3F9km8X.mjs} +3 -3
  8. package/dist/_config.d.ts +57 -0
  9. package/dist/_default-html.d.ts +12 -0
  10. package/dist/_deploy.d.ts +28 -0
  11. package/dist/{_dev-server-BCbEUhmv.mjs → _dev-server-BBUWo0sb.mjs} +12 -14
  12. package/dist/_dev-server.d.ts +74 -0
  13. package/dist/{_init-H-lw7czL.mjs → _init-DU-sXH6S.mjs} +47 -65
  14. package/dist/_init.d.ts +7 -0
  15. package/dist/_mock-api.d.ts +22 -0
  16. package/dist/_mock-registry.d.ts +17 -0
  17. package/dist/_output.d.ts +50 -0
  18. package/dist/{_server-common-B75oco06.mjs → _server-common-CnaP_Urf.mjs} +2 -1
  19. package/dist/_server-common.d.ts +17 -0
  20. package/dist/_slug-api-19R5kZ0U.mjs +27 -0
  21. package/dist/_slug-api.d.ts +15 -0
  22. package/dist/_templates.d.ts +21 -0
  23. package/dist/{_typecheck-gate-B1GcKZX-.mjs → _typecheck-gate-9IHWDnl1.mjs} +2 -2
  24. package/dist/_typecheck-gate.d.ts +7 -0
  25. package/dist/{_ui-DXZ9prrM.mjs → _ui-8kOEB-JH.mjs} +6 -3
  26. package/dist/_ui.d.ts +22 -0
  27. package/dist/{_utils-C502jKo8.mjs → _utils-Ch0J4s6a.mjs} +17 -1
  28. package/dist/_utils.d.ts +48 -0
  29. package/dist/_vite-env.d.ts +8 -0
  30. package/dist/build-Xq1xxoSg.mjs +38 -0
  31. package/dist/build.d.ts +16 -0
  32. package/dist/cli.d.ts +1 -0
  33. package/dist/cli.mjs +35 -52
  34. package/dist/{client-bundler-DO3GgO6p.mjs → client-bundler-BWDkUeEP.mjs} +5 -6
  35. package/dist/client-bundler.d.ts +21 -0
  36. package/dist/client-bundler.mjs +1 -1
  37. package/dist/{delete-K24hW1ab.mjs → delete-kSXFLxek.mjs} +4 -4
  38. package/dist/delete.d.ts +18 -0
  39. package/dist/{deploy-D-dnt9L8.mjs → deploy-Ci0X1gp2.mjs} +8 -8
  40. package/dist/deploy.d.ts +15 -0
  41. package/dist/{dev-C8o-p13B.mjs → dev-g7sGt8qX.mjs} +3 -3
  42. package/dist/dev.d.ts +13 -0
  43. package/dist/{init-B53rR3yD.mjs → init-BjeK8crW.mjs} +10 -17
  44. package/dist/init.d.ts +24 -0
  45. package/dist/scaffold/.env.example +11 -0
  46. package/dist/scaffold/CLAUDE.md +919 -0
  47. package/dist/scaffold/global.d.ts +1 -0
  48. package/dist/scaffold/package.json +32 -0
  49. package/dist/scaffold/pnpm-workspace.yaml +13 -0
  50. package/dist/scaffold/tsconfig.json +19 -0
  51. package/dist/scaffold/vite.config.ts +14 -0
  52. package/dist/scaffold/vitest.config.ts +24 -0
  53. package/dist/{secret-C4unwCEw.mjs → secret-DOva9OGk.mjs} +21 -20
  54. package/dist/secret.d.ts +25 -0
  55. package/dist/{storage-tZM1J8qj.mjs → storage-vM6HjDZZ.mjs} +6 -13
  56. package/dist/storage.d.ts +22 -0
  57. package/dist/templates/code-interpreter/agent.ts +11 -0
  58. package/dist/templates/code-interpreter/system-prompt.md +18 -0
  59. package/dist/templates/dispatch-center/agent.test.ts +153 -0
  60. package/dist/templates/dispatch-center/agent.ts +50 -0
  61. package/dist/templates/dispatch-center/client.tsx +378 -0
  62. package/dist/templates/dispatch-center/shared.ts +631 -0
  63. package/dist/templates/dispatch-center/system-prompt.md +39 -0
  64. package/dist/templates/dispatch-center/tools/incident_add_note.ts +27 -0
  65. package/dist/templates/dispatch-center/tools/incident_create.ts +79 -0
  66. package/dist/templates/dispatch-center/tools/incident_escalate.ts +88 -0
  67. package/dist/templates/dispatch-center/tools/incident_get.ts +36 -0
  68. package/dist/templates/dispatch-center/tools/incident_triage.ts +78 -0
  69. package/dist/templates/dispatch-center/tools/incident_update_status.ts +68 -0
  70. package/dist/templates/dispatch-center/tools/ops_dashboard.ts +52 -0
  71. package/dist/templates/dispatch-center/tools/ops_protocols.ts +27 -0
  72. package/dist/templates/dispatch-center/tools/ops_run_scenario.ts +149 -0
  73. package/dist/templates/dispatch-center/tools/resources_dispatch.ts +97 -0
  74. package/dist/templates/dispatch-center/tools/resources_get_available.ts +36 -0
  75. package/dist/templates/dispatch-center/tools/resources_update_status.ts +62 -0
  76. package/dist/templates/embedded-assets/agent.ts +55 -0
  77. package/dist/templates/embedded-assets/knowledge.json +20 -0
  78. package/dist/templates/health-assistant/agent.ts +174 -0
  79. package/dist/templates/health-assistant/system-prompt.md +17 -0
  80. package/dist/templates/infocom-adventure/agent.ts +119 -0
  81. package/dist/templates/infocom-adventure/client.tsx +279 -0
  82. package/dist/templates/infocom-adventure/shared.ts +38 -0
  83. package/dist/templates/infocom-adventure/system-prompt.md +43 -0
  84. package/dist/templates/math-buddy/agent.ts +16 -0
  85. package/dist/templates/math-buddy/system-prompt.md +12 -0
  86. package/dist/templates/night-owl/agent.ts +76 -0
  87. package/dist/templates/night-owl/client.tsx +130 -0
  88. package/dist/templates/night-owl/shared.ts +5 -0
  89. package/dist/templates/personal-finance/agent.ts +11 -0
  90. package/dist/templates/personal-finance/system-prompt.md +17 -0
  91. package/dist/templates/pipeline-simple/agent.test.ts +32 -0
  92. package/dist/templates/pipeline-simple/agent.ts +13 -0
  93. package/dist/templates/pizza-ordering/agent.test.ts +235 -0
  94. package/dist/templates/pizza-ordering/agent.ts +174 -0
  95. package/dist/templates/pizza-ordering/client.tsx +120 -0
  96. package/dist/templates/pizza-ordering/shared.ts +142 -0
  97. package/dist/templates/pizza-ordering/system-prompt.md +17 -0
  98. package/dist/templates/simple/agent.ts +5 -0
  99. package/dist/templates/solo-rpg/agent.test.ts +477 -0
  100. package/dist/templates/solo-rpg/agent.ts +37 -0
  101. package/dist/templates/solo-rpg/client.tsx +809 -0
  102. package/dist/templates/solo-rpg/shared.ts +747 -0
  103. package/dist/templates/solo-rpg/system-prompt.md +115 -0
  104. package/dist/templates/solo-rpg/tools/action_roll.ts +98 -0
  105. package/dist/templates/solo-rpg/tools/burn_momentum.ts +69 -0
  106. package/dist/templates/solo-rpg/tools/check_state.ts +11 -0
  107. package/dist/templates/solo-rpg/tools/load_game.ts +25 -0
  108. package/dist/templates/solo-rpg/tools/oracle.ts +248 -0
  109. package/dist/templates/solo-rpg/tools/save_game.ts +20 -0
  110. package/dist/templates/solo-rpg/tools/setup_character.ts +208 -0
  111. package/dist/templates/solo-rpg/tools/update_state.ts +170 -0
  112. package/dist/templates/web-researcher/agent.ts +11 -0
  113. package/dist/{test-CgWhus_Z.mjs → test-3Gq4pqH_.mjs} +5 -7
  114. package/dist/test.d.ts +39 -0
  115. package/dist/tsdown.config.d.ts +2 -0
  116. package/dist/typecheck.d.ts +26 -0
  117. package/dist/typecheck.mjs +6 -10
  118. package/dist/worker-bundler.d.ts +29 -0
  119. package/package.json +9 -6
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Structured output support for CLI commands.
3
+ *
4
+ * In JSON mode (non-TTY or --json), commands emit exactly one JSON line to
5
+ * stdout. In human mode (TTY, default), commands use @clack/prompts as before.
6
+ */
7
+ export type OutputMode = "json" | "human";
8
+ export type CommandResult<T> = {
9
+ ok: true;
10
+ data: T;
11
+ } | {
12
+ ok: false;
13
+ error: string;
14
+ code: string;
15
+ hint?: string;
16
+ };
17
+ /**
18
+ * Determine output mode from CLI flags and TTY state.
19
+ *
20
+ * Priority: --json flag > --no-json flag > TTY auto-detection.
21
+ */
22
+ export declare function getOutputMode(args: {
23
+ json?: boolean | undefined;
24
+ }, isTTY?: boolean): OutputMode;
25
+ /**
26
+ * Write a line to stdout, resolving only once it has been flushed.
27
+ *
28
+ * Resolves (never rejects) even when the write fails: the common failure is
29
+ * EPIPE — the consumer closed the pipe (`aai … --json | head -1`) — and there
30
+ * is nothing useful to do about a broken stdout except carry on and exit.
31
+ * Stream-level `'error'` events are handled by {@link installStdoutGuard}.
32
+ */
33
+ export declare function writeLine(line: string): Promise<void>;
34
+ /**
35
+ * Install an `'error'` listener on stdout so a broken pipe doesn't crash the
36
+ * CLI with an unhandled `'error'` event. EPIPE (consumer went away, e.g.
37
+ * `aai … --json | head -1`) exits quietly; anything else is reported on
38
+ * stderr and exits non-zero.
39
+ */
40
+ export declare function installStdoutGuard(stream?: NodeJS.WriteStream): void;
41
+ /** Create an ok result. */
42
+ export declare function ok<T>(data: T): CommandResult<T>;
43
+ /** Create an error result. */
44
+ export declare function fail<T>(code: string, error: string, hint?: string): CommandResult<T>;
45
+ /** Typed CLI error that carries a structured error code and optional hint. */
46
+ export declare class CliError extends Error {
47
+ readonly code: string;
48
+ readonly hint?: string | undefined;
49
+ constructor(code: string, message: string, hint?: string, options?: ErrorOptions);
50
+ }
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { r as errorCode } from "./_utils-Ch0J4s6a.mjs";
2
3
  import path from "node:path";
3
4
  import { parseEnv } from "node:util";
4
5
  import fs from "node:fs/promises";
@@ -26,7 +27,7 @@ async function resolveServerEnv(cwd, baseEnv) {
26
27
  try {
27
28
  content = await fs.readFile(path.join(cwd, ".env"), "utf-8");
28
29
  } catch (err) {
29
- if (err.code !== "ENOENT") throw err;
30
+ if (errorCode(err) !== "ENOENT") throw err;
30
31
  }
31
32
  if (content !== null) fileEntries = parseEnv(content);
32
33
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Build the `ctx.env` record that agent tools will see at runtime.
3
+ *
4
+ * Only variables explicitly declared in `.env` are included — matching
5
+ * the platform sandbox behavior where `ctx.env`
6
+ * contains only secrets set via `aai secret put`. This prevents agents
7
+ * from accidentally depending on shell-level vars (PATH, HOME, etc.) that
8
+ * won't exist in production.
9
+ *
10
+ * Values are resolved by merging the `.env` file with the current
11
+ * environment — existing shell exports take precedence over `.env`
12
+ * defaults, without mutating `process.env`.
13
+ *
14
+ * @param cwd - Project directory containing `.env` (optional).
15
+ * @param baseEnv - Override the environment to read values from (tests only).
16
+ */
17
+ export declare function resolveServerEnv(cwd?: string, baseEnv?: Record<string, string | undefined>): Promise<Record<string, string>>;
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ import { n as getServerInfo } from "./_agent-D8zBb3M5.mjs";
3
+ import { n as apiRequest, t as HINT_NOT_DEPLOYED } from "./_api-client-MenP4-O7.mjs";
4
+ //#region _slug-api.ts
5
+ /**
6
+ * Authenticated request against a deployed agent's slug-scoped resource
7
+ * (`${serverUrl}/${slug}${resourcePath}`) — the one shape every per-agent
8
+ * command (secret, storage) shares, including the standard "not deployed"
9
+ * 404 hint.
10
+ *
11
+ * Its own module (rather than living beside `getServerInfo` in `_agent.ts`)
12
+ * so tests can mock `_agent.ts`/`_api-client.ts` while this composition
13
+ * stays real — an intra-module call would bypass those mocks.
14
+ */
15
+ async function slugRequest(cwd, resourcePath, init, server) {
16
+ const { serverUrl, slug, apiKey } = await getServerInfo(cwd, server);
17
+ return {
18
+ data: await apiRequest(`${serverUrl}/${slug}${resourcePath}`, {
19
+ ...init,
20
+ apiKey,
21
+ hints: { 404: HINT_NOT_DEPLOYED }
22
+ }),
23
+ slug
24
+ };
25
+ }
26
+ //#endregion
27
+ export { slugRequest as t };
@@ -0,0 +1,15 @@
1
+ import { type ApiRequestOptions } from "./_api-client.ts";
2
+ /**
3
+ * Authenticated request against a deployed agent's slug-scoped resource
4
+ * (`${serverUrl}/${slug}${resourcePath}`) — the one shape every per-agent
5
+ * command (secret, storage) shares, including the standard "not deployed"
6
+ * 404 hint.
7
+ *
8
+ * Its own module (rather than living beside `getServerInfo` in `_agent.ts`)
9
+ * so tests can mock `_agent.ts`/`_api-client.ts` while this composition
10
+ * stays real — an intra-module call would bypass those mocks.
11
+ */
12
+ export declare function slugRequest<T = unknown>(cwd: string, resourcePath: string, init: Pick<ApiRequestOptions, "method" | "body" | "action">, server?: string): Promise<{
13
+ data: T;
14
+ slug: string;
15
+ }>;
@@ -0,0 +1,21 @@
1
+ export declare const REPO_URL = "https://github.com/alexkroman/agent";
2
+ /**
3
+ * Templates as shipped inside the published tarball, copied into `dist/` by
4
+ * `bundle-templates.mjs` at build time — so this resolves to `dist/` for a
5
+ * published CLI and to the (template-less) package root when running source
6
+ * in the monorepo, where the branch above wins.
7
+ *
8
+ * They used to be fetched at `init` time with giget from
9
+ * `github:alexkroman/agent/packages/aai-templates#main`. That required a
10
+ * network for every `init`, and pinned templates to `main` regardless of the
11
+ * CLI version the user had installed, so a template written against a newer
12
+ * SDK could land in a project resolving an older one. Bundling pins the two
13
+ * together by construction. It also puts the templates inside the studio's
14
+ * guest sandbox, which has the CLI in its baked toolchain but no way to fetch
15
+ * anything from GitHub.
16
+ */
17
+ export declare function bundledTemplatesDir(): string;
18
+ /**
19
+ * Copy a template into targetDir, merging scaffold files underneath.
20
+ */
21
+ export declare function downloadAndMergeTemplate(template: string, targetDir: string): Promise<void>;
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as log, o as CliError } from "./_ui-DXZ9prrM.mjs";
2
+ import { n as log, o as CliError } from "./_ui-8kOEB-JH.mjs";
3
3
  import { typecheckProject } from "./typecheck.mjs";
4
4
  //#region _typecheck-gate.ts
5
5
  /**
@@ -14,4 +14,4 @@ async function assertTypechecks(cwd) {
14
14
  if (!result.ok) throw new CliError("typecheck_failed", result.output, "Fix the type errors, or pass --skipTypecheck to build anyway");
15
15
  }
16
16
  //#endregion
17
- export { assertTypechecks };
17
+ export { assertTypechecks as t };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The build/deploy typecheck gate: run the project's own `tsc --noEmit`
3
+ * (see `typecheck.ts`) and turn a failure into a structured CliError. The
4
+ * bundlers strip types unchecked, so without this a type-broken agent
5
+ * ships and misbehaves at runtime instead of failing here.
6
+ */
7
+ export declare function assertTypechecks(cwd: string): Promise<void>;
@@ -83,10 +83,13 @@ const log = new Proxy(p.log, { get(target, prop, receiver) {
83
83
  function silenceOutput() {
84
84
  silenced = true;
85
85
  }
86
- /** Unwrap a clack prompt result, exiting cleanly if the user cancelled. */
87
- function unwrapCancel(result) {
86
+ /**
87
+ * Unwrap a clack prompt result, exiting cleanly if the user cancelled.
88
+ * `message` lets the caller name what was cancelled (e.g. "Setup cancelled").
89
+ */
90
+ function unwrapCancel(result, message = "Cancelled") {
88
91
  if (p.isCancel(result)) {
89
- p.cancel("Setup cancelled");
92
+ p.cancel(message);
90
93
  process.exit(0);
91
94
  }
92
95
  return result;
package/dist/_ui.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ import * as p from "@clack/prompts";
2
+ type Log = typeof p.log;
3
+ /** Log instance that delegates to clack (human mode) or no-ops (JSON mode). */
4
+ export declare const log: Log;
5
+ /** Replace all log methods with no-ops. Call once in JSON mode. */
6
+ export declare function silenceOutput(): void;
7
+ /**
8
+ * Unwrap a clack prompt result, exiting cleanly if the user cancelled.
9
+ * `message` lets the caller name what was cancelled (e.g. "Setup cancelled").
10
+ */
11
+ export declare function unwrapCancel<T>(result: T | symbol, message?: string): T;
12
+ /** Format a URL for display. */
13
+ export declare function fmtUrl(url: string): string;
14
+ /**
15
+ * Parse and validate a port string. Returns the numeric port or throws.
16
+ *
17
+ * Deliberately zod-free: this module loads on every CLI invocation
18
+ * (including `aai --help`), and keeping zod off that path is the same
19
+ * startup-cost invariant `_utils.ts` documents for its error helpers.
20
+ */
21
+ export declare function parsePort(raw: string): number;
22
+ export {};
@@ -1,8 +1,14 @@
1
1
  #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
2
3
  import path from "node:path";
3
4
  import fs from "node:fs/promises";
4
5
  import { errorDetail, errorMessage, errorMessage as errorMessage$1 } from "@alexkroman1/aai/utils";
5
6
  //#region _utils.ts
7
+ /**
8
+ * The file that marks a directory as an agent project — the single source
9
+ * for the entry filename every command checks or scaffolds.
10
+ */
11
+ const AGENT_ENTRY = "agent.ts";
6
12
  /** Resolve the working directory from INIT_CWD or process.cwd(). */
7
13
  function resolveCwd() {
8
14
  return process.env.INIT_CWD || process.cwd();
@@ -19,6 +25,16 @@ function isEexist(err) {
19
25
  function validateAgentExport(mod) {
20
26
  if (!mod?.name || typeof mod.name !== "string") throw new Error("agent.ts must export default agent({ name: ... })");
21
27
  }
28
+ /**
29
+ * Absolute path of `binName`'s script declared by the package.json at
30
+ * `pkgJsonPath`, or undefined when the package declares no such bin.
31
+ * Handles both `"bin": "script.js"` and `"bin": { name: "script.js" }`.
32
+ */
33
+ function binFromPackageJson(pkgJsonPath, binName) {
34
+ const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
35
+ const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.[binName];
36
+ return bin ? path.join(path.dirname(pkgJsonPath), bin) : void 0;
37
+ }
22
38
  async function fileExists(p) {
23
39
  try {
24
40
  await fs.access(p);
@@ -79,4 +95,4 @@ async function writeJson(filePath, data, opts = {}) {
79
95
  }
80
96
  }
81
97
  //#endregion
82
- export { isEexist as a, validateAgentExport as c, fileExists as i, writeJson as l, errorDetail as n, readJson as o, errorMessage$1 as r, resolveCwd as s, errorCode as t };
98
+ export { errorMessage$1 as a, readJson as c, writeJson as d, errorDetail as i, resolveCwd as l, binFromPackageJson as n, fileExists as o, errorCode as r, isEexist as s, AGENT_ENTRY as t, validateAgentExport as u };
@@ -0,0 +1,48 @@
1
+ export { errorDetail, errorMessage } from "@alexkroman1/aai/utils";
2
+ /**
3
+ * The file that marks a directory as an agent project — the single source
4
+ * for the entry filename every command checks or scaffolds.
5
+ */
6
+ export declare const AGENT_ENTRY = "agent.ts";
7
+ /** Resolve the working directory from INIT_CWD or process.cwd(). */
8
+ export declare function resolveCwd(): string;
9
+ /** The `code` of a Node errno-style error (`"ENOENT"`, `"EPIPE"`, …), or undefined. */
10
+ export declare function errorCode(err: unknown): string | undefined;
11
+ /** True when `err` is a filesystem EEXIST error (target already exists). */
12
+ export declare function isEexist(err: unknown): boolean;
13
+ /** Validate that a module's default export is a valid agent definition. Throws if invalid. */
14
+ export declare function validateAgentExport(mod: any): void;
15
+ /**
16
+ * Absolute path of `binName`'s script declared by the package.json at
17
+ * `pkgJsonPath`, or undefined when the package declares no such bin.
18
+ * Handles both `"bin": "script.js"` and `"bin": { name: "script.js" }`.
19
+ */
20
+ export declare function binFromPackageJson(pkgJsonPath: string, binName: string): string | undefined;
21
+ export declare function fileExists(p: string): Promise<boolean>;
22
+ /**
23
+ * Read and parse a JSON file. Returns null only when the file does not exist
24
+ * (ENOENT — the "optional file" case). A file that exists but cannot be read
25
+ * (EACCES, …) or parsed throws instead: treating a corrupted file as absent
26
+ * hides real problems — e.g. a corrupted `.aai/project.json` would silently
27
+ * deploy under a NEW slug, orphaning the live deployment.
28
+ */
29
+ export declare function readJson(filePath: string): Promise<unknown>;
30
+ /**
31
+ * Write `data` as pretty-printed JSON (+ trailing newline), creating parent dirs.
32
+ *
33
+ * Writes to a temp file in the same directory, then renames it into place.
34
+ * A plain `writeFile` can be observed (or left, on crash) half-written; a
35
+ * torn config.json fails `JSON.parse`, reads back as `{}`, and the next
36
+ * read-modify-write silently wipes fields like `approvedServers`. Rename on
37
+ * the same filesystem is atomic, so readers only ever see a complete file.
38
+ * Two concurrent CLI processes can still lose each other's *updates*
39
+ * (last rename wins) — acceptable for these small user-config files.
40
+ *
41
+ * `mode` restricts the file's permissions (the rename carries the temp
42
+ * file's mode to the destination, so an existing world-readable file is
43
+ * tightened on the next write). The parent directory is created 0o700 in
44
+ * that case so a fresh config dir never goes through a readable window.
45
+ */
46
+ export declare function writeJson(filePath: string, data: unknown, opts?: {
47
+ mode?: number;
48
+ }): Promise<void>;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `env` is injectable for tests ONLY, so specs can exercise the
3
+ * snapshot/refcount logic on a plain object instead of mutating (and
4
+ * repeatedly deleting) the real `process.env.NODE_ENV` mid-suite.
5
+ */
6
+ export declare function withPreservedNodeEnv<T>(fn: () => Promise<T>, env?: {
7
+ NODE_ENV?: string;
8
+ }): Promise<T>;
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ import { n as log, o as CliError, u as ok } from "./_ui-8kOEB-JH.mjs";
3
+ import { r as evalWorkerBundle, t as buildAgentBundle } from "./_bundler-DFS4xxqE.mjs";
4
+ import { t as assertTypechecks } from "./_typecheck-gate-9IHWDnl1.mjs";
5
+ import { classifyVitestError, runVitest } from "./test-3Gq4pqH_.mjs";
6
+ //#region build.ts
7
+ /**
8
+ * `aai build` — bundle the agent without deploying, behind the same gates
9
+ * deploy runs (tests, then typecheck), so the command previews the deploy
10
+ * artifact and its failures alike.
11
+ */
12
+ /**
13
+ * Map a {@link runVitest} failure to a CliError — distinguishing a real test
14
+ * failure (test_failed) from the runner not spawning (spawn_failed) instead
15
+ * of a generic command_failed.
16
+ */
17
+ function testGateError(err) {
18
+ const { code, message } = classifyVitestError(err);
19
+ return new CliError(code, message, "Re-run with --skipTests to build without tests", { cause: err });
20
+ }
21
+ async function executeBuild(opts) {
22
+ const { cwd } = opts;
23
+ if (!opts.skipTests) try {
24
+ runVitest(cwd);
25
+ } catch (err) {
26
+ throw testGateError(err);
27
+ }
28
+ if (!opts.skipTypecheck) await assertTypechecks(cwd);
29
+ const bundle = await buildAgentBundle(cwd, { minify: true });
30
+ const agentDef = await evalWorkerBundle(bundle.worker);
31
+ log.success("Build complete");
32
+ return ok({
33
+ name: agentDef.name,
34
+ workerBytes: bundle.worker.length
35
+ });
36
+ }
37
+ //#endregion
38
+ export { executeBuild };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `aai build` — bundle the agent without deploying, behind the same gates
3
+ * deploy runs (tests, then typecheck), so the command previews the deploy
4
+ * artifact and its failures alike.
5
+ */
6
+ import { type CommandResult } from "./_output.ts";
7
+ type BuildData = {
8
+ name: string;
9
+ workerBytes: number;
10
+ };
11
+ export declare function executeBuild(opts: {
12
+ cwd: string;
13
+ skipTests?: boolean | undefined;
14
+ skipTypecheck?: boolean | undefined;
15
+ }): Promise<CommandResult<BuildData>>;
16
+ export {};
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare const mainCommand: import("citty").CommandDef<import("citty").ArgsDef>;
package/dist/cli.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { c as getOutputMode, d as writeLine, i as silenceOutput, l as installStdoutGuard, n as log, o as CliError, s as fail } from "./_ui-DXZ9prrM.mjs";
3
- import { i as fileExists, r as errorMessage, s as resolveCwd } from "./_utils-C502jKo8.mjs";
2
+ import { c as getOutputMode, d as writeLine, i as silenceOutput, l as installStdoutGuard, n as log, o as CliError, s as fail } from "./_ui-8kOEB-JH.mjs";
3
+ import { a as errorMessage, l as resolveCwd, o as fileExists, t as AGENT_ENTRY } from "./_utils-Ch0J4s6a.mjs";
4
4
  import { existsSync, readFileSync } from "node:fs";
5
5
  import path from "node:path";
6
6
  import { fileURLToPath } from "node:url";
@@ -8,12 +8,6 @@ import { defineCommand, runMain } from "citty";
8
8
  //#region cli.ts
9
9
  /** Shared arg definitions for citty commands. */
10
10
  const sharedArgs = {
11
- port: {
12
- type: "string",
13
- alias: "p",
14
- description: "Port to listen on",
15
- default: "3000"
16
- },
17
11
  server: {
18
12
  type: "string",
19
13
  alias: "s",
@@ -48,7 +42,7 @@ const VERSION = readCliVersion(cliDir);
48
42
  async function setup(opts) {
49
43
  const cwd = resolveCwd();
50
44
  if (opts?.agent) {
51
- if (!await fileExists(path.join(cwd, "agent.ts"))) throw new Error("No agent.ts found in the current directory. Run `aai init` first.");
45
+ if (!await fileExists(path.join(cwd, "agent.ts"))) throw new Error(`No ${AGENT_ENTRY} found in the current directory. Run \`aai init\` first.`);
52
46
  }
53
47
  return cwd;
54
48
  }
@@ -56,7 +50,6 @@ async function setup(opts) {
56
50
  * Run a command body with standard output-mode resolution, error handling,
57
51
  * and result emission.
58
52
  *
59
- * - `setYes`: json mode sets `args.yes = true` (only meaningful for commands with a `yes` arg).
60
53
  * - API key acquisition is owned by `resolveDeployTarget`/`getServerInfo`
61
54
  * inside the commands that talk to the platform — after the server-trust
62
55
  * check, so an untrusted `serverUrl` is refused without prompting for a
@@ -66,12 +59,9 @@ async function setup(opts) {
66
59
  * human mode logs the message, JSON mode writes exactly one result line,
67
60
  * and both exit 1.
68
61
  */
69
- async function runCommand(args, fn, opts = {}) {
62
+ async function runCommand(args, fn) {
70
63
  const mode = getOutputMode(args);
71
- if (mode === "json") {
72
- silenceOutput();
73
- if (opts.setYes) args.yes = true;
74
- }
64
+ if (mode === "json") silenceOutput();
75
65
  let result;
76
66
  try {
77
67
  result = await fn(mode);
@@ -115,16 +105,16 @@ const init = defineCommand({
115
105
  },
116
106
  async run({ args }) {
117
107
  await runCommand(args, async (mode) => {
118
- const { executeInit } = await import("./init-B53rR3yD.mjs");
108
+ const { executeInit } = await import("./init-BjeK8crW.mjs");
119
109
  return executeInit({
120
110
  dir: args.dir,
121
111
  force: args.force,
122
112
  template: args.template,
123
- yes: args.yes,
113
+ yes: mode === "json" ? true : args.yes,
124
114
  skipDeploy: args.skipDeploy,
125
115
  server: args.server
126
116
  }, mode === "json" ? { silent: true } : void 0);
127
- }, { setYes: true });
117
+ });
128
118
  }
129
119
  });
130
120
  const dev = defineCommand({
@@ -133,13 +123,18 @@ const dev = defineCommand({
133
123
  description: "Start a local development server"
134
124
  },
135
125
  args: {
136
- port: sharedArgs.port,
126
+ port: {
127
+ type: "string",
128
+ alias: "p",
129
+ description: "Port to listen on",
130
+ default: "3000"
131
+ },
137
132
  json: sharedArgs.json
138
133
  },
139
134
  async run({ args }) {
140
135
  await runCommand(args, async () => {
141
136
  const cwd = await setup({ agent: true });
142
- const { executeDev } = await import("./dev-C8o-p13B.mjs");
137
+ const { executeDev } = await import("./dev-g7sGt8qX.mjs");
143
138
  return executeDev({
144
139
  cwd,
145
140
  port: args.port
@@ -156,7 +151,7 @@ const test = defineCommand({
156
151
  async run({ args }) {
157
152
  await runCommand(args, async () => {
158
153
  const cwd = await setup();
159
- const { executeTest } = await import("./test-CgWhus_Z.mjs");
154
+ const { executeTest } = await import("./test-3Gq4pqH_.mjs");
160
155
  return executeTest(cwd);
161
156
  });
162
157
  }
@@ -180,24 +175,12 @@ const build = defineCommand({
180
175
  async run({ args }) {
181
176
  await runCommand(args, async () => {
182
177
  const cwd = await setup({ agent: true });
183
- if (!args.skipTests) {
184
- const { classifyVitestError, runVitest } = await import("./test-CgWhus_Z.mjs");
185
- const toCliError = (err) => {
186
- const { code, message } = classifyVitestError(err);
187
- return new CliError(code, message, "Re-run with --skipTests to build without tests", { cause: err });
188
- };
189
- try {
190
- runVitest(cwd);
191
- } catch (err) {
192
- throw toCliError(err);
193
- }
194
- }
195
- if (!args.skipTypecheck) {
196
- const { assertTypechecks } = await import("./_typecheck-gate-B1GcKZX-.mjs");
197
- await assertTypechecks(cwd);
198
- }
199
- const { executeBuild } = await import("./_bundler-DdmuZzeR.mjs");
200
- return executeBuild(cwd);
178
+ const { executeBuild } = await import("./build-Xq1xxoSg.mjs");
179
+ return executeBuild({
180
+ cwd,
181
+ skipTests: args.skipTests,
182
+ skipTypecheck: args.skipTypecheck
183
+ });
201
184
  });
202
185
  }
203
186
  });
@@ -221,12 +204,12 @@ const deploy = defineCommand({
221
204
  async run({ args }) {
222
205
  await runCommand(args, async () => {
223
206
  const cwd = await setup({ agent: true });
224
- const { executeDeploy } = await import("./deploy-D-dnt9L8.mjs");
207
+ const { executeDeploy } = await import("./deploy-Ci0X1gp2.mjs");
225
208
  return executeDeploy({
226
209
  cwd,
227
- ...args.server ? { server: args.server } : {},
228
- ...args.allowMissingSecrets ? { allowMissingSecrets: true } : {},
229
- ...args.skipTypecheck ? { skipTypecheck: true } : {}
210
+ server: args.server,
211
+ allowMissingSecrets: args.allowMissingSecrets,
212
+ skipTypecheck: args.skipTypecheck
230
213
  });
231
214
  });
232
215
  }
@@ -243,10 +226,10 @@ const del = defineCommand({
243
226
  async run({ args }) {
244
227
  await runCommand(args, async () => {
245
228
  const cwd = await setup();
246
- const { executeDelete } = await import("./delete-K24hW1ab.mjs");
229
+ const { executeDelete } = await import("./delete-kSXFLxek.mjs");
247
230
  return executeDelete({
248
231
  cwd,
249
- ...args.server ? { server: args.server } : {}
232
+ server: args.server
250
233
  });
251
234
  });
252
235
  }
@@ -274,9 +257,9 @@ const secret = defineCommand({
274
257
  async run({ args }) {
275
258
  await runCommand(args, async (mode) => {
276
259
  const cwd = await setup();
277
- const { executeSecretPut, readStdin } = await import("./secret-C4unwCEw.mjs");
260
+ const { executeSecretPut, NO_INPUT, readStdin } = await import("./secret-DOva9OGk.mjs");
278
261
  const value = mode === "json" ? await readStdin() : void 0;
279
- if (mode === "json" && !value) throw new CliError("no_input", "No value provided", "Pipe secret value to stdin");
262
+ if (mode === "json" && !value) throw new CliError(...NO_INPUT);
280
263
  return executeSecretPut(cwd, args.name, value, args.server);
281
264
  });
282
265
  }
@@ -298,7 +281,7 @@ const secret = defineCommand({
298
281
  async run({ args }) {
299
282
  await runCommand(args, async () => {
300
283
  const cwd = await setup();
301
- const { executeSecretDelete } = await import("./secret-C4unwCEw.mjs");
284
+ const { executeSecretDelete } = await import("./secret-DOva9OGk.mjs");
302
285
  return executeSecretDelete(cwd, args.name, args.server);
303
286
  });
304
287
  }
@@ -315,7 +298,7 @@ const secret = defineCommand({
315
298
  async run({ args }) {
316
299
  await runCommand(args, async () => {
317
300
  const cwd = await setup();
318
- const { executeSecretList } = await import("./secret-C4unwCEw.mjs");
301
+ const { executeSecretList } = await import("./secret-DOva9OGk.mjs");
319
302
  return executeSecretList(cwd, args.server);
320
303
  });
321
304
  }
@@ -350,7 +333,7 @@ const storage = defineCommand({
350
333
  },
351
334
  async run({ args }) {
352
335
  await runCommand(args, async () => {
353
- const { executeStorageStatus } = await import("./storage-tZM1J8qj.mjs");
336
+ const { executeStorageStatus } = await import("./storage-vM6HjDZZ.mjs");
354
337
  return executeStorageStatus(resolveStorageCwd(args.dir), args.server);
355
338
  });
356
339
  }
@@ -367,7 +350,7 @@ const storage = defineCommand({
367
350
  },
368
351
  async run({ args }) {
369
352
  await runCommand(args, async () => {
370
- const { executeStorageEnable } = await import("./storage-tZM1J8qj.mjs");
353
+ const { executeStorageEnable } = await import("./storage-vM6HjDZZ.mjs");
371
354
  return executeStorageEnable(resolveStorageCwd(args.dir), args.server);
372
355
  });
373
356
  }
@@ -389,7 +372,7 @@ const storage = defineCommand({
389
372
  },
390
373
  async run({ args }) {
391
374
  await runCommand(args, async () => {
392
- const { executeStorageDisable } = await import("./storage-tZM1J8qj.mjs");
375
+ const { executeStorageDisable } = await import("./storage-vM6HjDZZ.mjs");
393
376
  return executeStorageDisable(resolveStorageCwd(args.dir), {
394
377
  server: args.server,
395
378
  force: args.force
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { i as fileExists, r as errorMessage } from "./_utils-C502jKo8.mjs";
2
+ import { a as errorMessage$1, o as fileExists } from "./_utils-Ch0J4s6a.mjs";
3
3
  import { t as withPreservedNodeEnv } from "./_vite-env-Dg_QlVv0.mjs";
4
4
  import { existsSync, unlinkSync, writeFileSync } from "node:fs";
5
5
  import path from "node:path";
6
6
  import fs from "node:fs/promises";
7
- import { isTextAssetPath } from "@alexkroman1/aai";
7
+ import { isTextAssetPath } from "@alexkroman1/aai/utils";
8
8
  import { build } from "vite";
9
9
  //#region _default-html.ts
10
10
  /**
@@ -65,7 +65,7 @@ function writeTempHtml(root) {
65
65
  writeFileSync(htmlPath, DEFAULT_HTML);
66
66
  } catch (err) {
67
67
  cleanup();
68
- throw new Error(`Failed to write a temporary index.html at ${htmlPath} for the client build — is the project directory writable? (${errorMessage(err)})`, { cause: err });
68
+ throw new Error(`Failed to write a temporary index.html at ${htmlPath} for the client build — is the project directory writable? (${errorMessage$1(err)})`, { cause: err });
69
69
  }
70
70
  return cleanup;
71
71
  }
@@ -88,8 +88,7 @@ const DEFAULT_OUT_DIR = ".aai/client";
88
88
  *
89
89
  * Deduping states the peer contract in terms the bundler enforces, and it is
90
90
  * what a React app wants anyway: two copies of React in one bundle break
91
- * hooks. Guarded by `client-bundler.test.ts` and, for the half that has to
92
- * hold in the image, `aai-server/dockerfile-packaging.test.ts`.
91
+ * hooks. Guarded by `client-bundler.test.ts`.
93
92
  */
94
93
  const DEDUPED_PEERS = ["react", "react-dom"];
95
94
  /**
@@ -131,7 +130,7 @@ async function readClientDir(clientDir) {
131
130
  withFileTypes: true
132
131
  });
133
132
  } catch (err) {
134
- throw new Error(`Client build produced no output at ${clientDir}: ${errorMessage(err)}`, { cause: err });
133
+ throw new Error(`Client build produced no output at ${clientDir}: ${errorMessage$1(err)}`, { cause: err });
135
134
  }
136
135
  await Promise.all(entries.filter((entry) => entry.isFile()).map(async (entry) => {
137
136
  const abs = path.join(entry.parentPath, entry.name);
@@ -0,0 +1,21 @@
1
+ import { type PluginOption } from "vite";
2
+ export type BuildClientOptions = {
3
+ /**
4
+ * Plugins to inject instead of relying on the project's `vite.config.ts`
5
+ * (React + Tailwind for the studio, whose workspace has neither).
6
+ */
7
+ plugins?: PluginOption[];
8
+ /**
9
+ * `false` ignores any `vite.config.ts` under `cwd`. The studio passes this:
10
+ * a Vite config is executable code, and workspace files are untrusted.
11
+ */
12
+ configFile?: false;
13
+ /** Build output directory, relative to `cwd`. */
14
+ outDir?: string;
15
+ };
16
+ /**
17
+ * Build the client SPA with Vite if `client.tsx` exists.
18
+ * Returns a map of relative file paths to string contents for deploy,
19
+ * or `{}` when the project has no `client.tsx`.
20
+ */
21
+ export declare function buildClient(cwd: string, opts?: BuildClientOptions): Promise<Record<string, string>>;
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { t as buildClient } from "./client-bundler-DO3GgO6p.mjs";
2
+ import { t as buildClient } from "./client-bundler-BWDkUeEP.mjs";
3
3
  export { buildClient };