@alexkroman1/aai-cli 5.1.1 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_agent.d.ts +39 -0
- package/dist/_api-client.d.ts +37 -0
- package/dist/_bundler.d.ts +46 -0
- package/dist/_config.d.ts +57 -0
- package/dist/_default-html.d.ts +12 -0
- package/dist/_deploy.d.ts +28 -0
- package/dist/_dev-server.d.ts +74 -0
- package/dist/{_init-B-LEVTHx.mjs → _init-DU-sXH6S.mjs} +44 -65
- package/dist/_init.d.ts +7 -0
- package/dist/_mock-api.d.ts +22 -0
- package/dist/_mock-registry.d.ts +17 -0
- package/dist/_output.d.ts +50 -0
- package/dist/_server-common.d.ts +17 -0
- package/dist/_slug-api.d.ts +15 -0
- package/dist/_templates.d.ts +21 -0
- package/dist/_typecheck-gate.d.ts +7 -0
- package/dist/_ui.d.ts +22 -0
- package/dist/_utils.d.ts +48 -0
- package/dist/_vite-env.d.ts +8 -0
- package/dist/build.d.ts +16 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.mjs +1 -1
- package/dist/client-bundler.d.ts +21 -0
- package/dist/delete.d.ts +18 -0
- package/dist/deploy.d.ts +15 -0
- package/dist/dev.d.ts +13 -0
- package/dist/{init-tc3Rbgrf.mjs → init-BjeK8crW.mjs} +1 -1
- package/dist/init.d.ts +24 -0
- package/dist/scaffold/.env.example +11 -0
- package/dist/scaffold/CLAUDE.md +938 -0
- package/dist/scaffold/global.d.ts +1 -0
- package/dist/scaffold/package.json +32 -0
- package/dist/scaffold/pnpm-workspace.yaml +13 -0
- package/dist/scaffold/tsconfig.json +19 -0
- package/dist/scaffold/vite.config.ts +14 -0
- package/dist/scaffold/vitest.config.ts +24 -0
- package/dist/secret.d.ts +25 -0
- package/dist/storage.d.ts +22 -0
- package/dist/templates/code-interpreter/agent.ts +11 -0
- package/dist/templates/code-interpreter/system-prompt.md +18 -0
- package/dist/templates/dispatch-center/agent.test.ts +153 -0
- package/dist/templates/dispatch-center/agent.ts +50 -0
- package/dist/templates/dispatch-center/client.tsx +378 -0
- package/dist/templates/dispatch-center/shared.ts +631 -0
- package/dist/templates/dispatch-center/system-prompt.md +39 -0
- package/dist/templates/dispatch-center/tools/incident_add_note.ts +27 -0
- package/dist/templates/dispatch-center/tools/incident_create.ts +79 -0
- package/dist/templates/dispatch-center/tools/incident_escalate.ts +88 -0
- package/dist/templates/dispatch-center/tools/incident_get.ts +36 -0
- package/dist/templates/dispatch-center/tools/incident_triage.ts +78 -0
- package/dist/templates/dispatch-center/tools/incident_update_status.ts +68 -0
- package/dist/templates/dispatch-center/tools/ops_dashboard.ts +52 -0
- package/dist/templates/dispatch-center/tools/ops_protocols.ts +27 -0
- package/dist/templates/dispatch-center/tools/ops_run_scenario.ts +149 -0
- package/dist/templates/dispatch-center/tools/resources_dispatch.ts +97 -0
- package/dist/templates/dispatch-center/tools/resources_get_available.ts +36 -0
- package/dist/templates/dispatch-center/tools/resources_update_status.ts +62 -0
- package/dist/templates/embedded-assets/agent.ts +55 -0
- package/dist/templates/embedded-assets/knowledge.json +20 -0
- package/dist/templates/health-assistant/agent.ts +174 -0
- package/dist/templates/health-assistant/system-prompt.md +17 -0
- package/dist/templates/infocom-adventure/agent.ts +119 -0
- package/dist/templates/infocom-adventure/client.tsx +279 -0
- package/dist/templates/infocom-adventure/shared.ts +38 -0
- package/dist/templates/infocom-adventure/system-prompt.md +43 -0
- package/dist/templates/math-buddy/agent.ts +16 -0
- package/dist/templates/math-buddy/system-prompt.md +12 -0
- package/dist/templates/night-owl/agent.ts +76 -0
- package/dist/templates/night-owl/client.tsx +130 -0
- package/dist/templates/night-owl/shared.ts +5 -0
- package/dist/templates/personal-finance/agent.ts +11 -0
- package/dist/templates/personal-finance/system-prompt.md +17 -0
- package/dist/templates/pipeline-simple/agent.test.ts +32 -0
- package/dist/templates/pipeline-simple/agent.ts +13 -0
- package/dist/templates/pizza-ordering/agent.test.ts +235 -0
- package/dist/templates/pizza-ordering/agent.ts +174 -0
- package/dist/templates/pizza-ordering/client.tsx +120 -0
- package/dist/templates/pizza-ordering/shared.ts +142 -0
- package/dist/templates/pizza-ordering/system-prompt.md +17 -0
- package/dist/templates/simple/agent.ts +5 -0
- package/dist/templates/solo-rpg/agent.test.ts +477 -0
- package/dist/templates/solo-rpg/agent.ts +37 -0
- package/dist/templates/solo-rpg/client.tsx +809 -0
- package/dist/templates/solo-rpg/shared.ts +747 -0
- package/dist/templates/solo-rpg/system-prompt.md +115 -0
- package/dist/templates/solo-rpg/tools/action_roll.ts +98 -0
- package/dist/templates/solo-rpg/tools/burn_momentum.ts +69 -0
- package/dist/templates/solo-rpg/tools/check_state.ts +11 -0
- package/dist/templates/solo-rpg/tools/load_game.ts +25 -0
- package/dist/templates/solo-rpg/tools/oracle.ts +248 -0
- package/dist/templates/solo-rpg/tools/save_game.ts +20 -0
- package/dist/templates/solo-rpg/tools/setup_character.ts +208 -0
- package/dist/templates/solo-rpg/tools/update_state.ts +170 -0
- package/dist/templates/web-researcher/agent.ts +11 -0
- package/dist/test.d.ts +39 -0
- package/dist/tsdown.config.d.ts +2 -0
- package/dist/typecheck.d.ts +26 -0
- package/dist/worker-bundler.d.ts +29 -0
- package/package.json +9 -6
package/dist/_agent.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export declare const DEFAULT_SERVER = "https://alexkroman--aai-server-web-server.modal.run";
|
|
2
|
+
export declare const DEFAULT_DEV_SERVER = "http://localhost:8080";
|
|
3
|
+
export declare function getMonorepoRoot(): string | null;
|
|
4
|
+
export declare function isDevMode(): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Resolve which platform server to talk to.
|
|
7
|
+
*
|
|
8
|
+
* Precedence: an explicit `--server` flag, then dev mode, then the project
|
|
9
|
+
* config, then the shipped default.
|
|
10
|
+
*
|
|
11
|
+
* `configUrl` comes from `.aai/project.json` — a file in the working tree, so
|
|
12
|
+
* a cloned repo controls it. Because callers pair this URL with the user's API
|
|
13
|
+
* key (and, for `aai secret`, with secret values), a config-supplied origin is
|
|
14
|
+
* only honored when it is implicitly trusted or previously approved by the
|
|
15
|
+
* user via `--server`. Otherwise a repo could redirect a credentialed request
|
|
16
|
+
* to a host of its choosing simply by shipping a `project.json`, and
|
|
17
|
+
* `aai deploy` would hand over the developer's key on first run.
|
|
18
|
+
*
|
|
19
|
+
* @param approvedOrigins - Origins from the user-owned global config.
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveServerUrl(explicit?: string, configUrl?: string, approvedOrigins?: readonly string[]): string;
|
|
22
|
+
/**
|
|
23
|
+
* Resolve everything needed to talk to the platform: project config (null if
|
|
24
|
+
* the project has never been deployed), server URL, and API key.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveDeployTarget(cwd: string, explicitServer?: string): Promise<{
|
|
27
|
+
config: {
|
|
28
|
+
slug: string;
|
|
29
|
+
serverUrl: string;
|
|
30
|
+
} | null;
|
|
31
|
+
serverUrl: string;
|
|
32
|
+
apiKey: string;
|
|
33
|
+
}>;
|
|
34
|
+
/** Like resolveDeployTarget, but requires an existing deployment (project config). */
|
|
35
|
+
export declare function getServerInfo(cwd: string, explicitServer?: string): Promise<{
|
|
36
|
+
serverUrl: string;
|
|
37
|
+
slug: string;
|
|
38
|
+
apiKey: string;
|
|
39
|
+
}>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export declare const HINT_INVALID_API_KEY = "Your API key may be invalid. Run `aai` to re-enter your AssemblyAI API key.";
|
|
2
|
+
/** 404 hint for requests scoped to a deployed agent's slug. */
|
|
3
|
+
export declare const HINT_NOT_DEPLOYED = "The agent may not be deployed. Check `.aai/project.json` for the correct slug.";
|
|
4
|
+
export type ApiRequestOptions = {
|
|
5
|
+
apiKey: string;
|
|
6
|
+
/** Verb used in error messages, e.g. "deploy". */
|
|
7
|
+
action: string;
|
|
8
|
+
method?: "GET" | "POST" | "PUT" | "DELETE";
|
|
9
|
+
/**
|
|
10
|
+
* Request body. Plain objects are JSON-serialized by ofetch (with
|
|
11
|
+
* Content-Type set); binary bodies (e.g. a pre-gzipped Buffer) pass
|
|
12
|
+
* through untouched — set Content-Type/Content-Encoding via `headers`.
|
|
13
|
+
*/
|
|
14
|
+
body?: unknown;
|
|
15
|
+
/** Extra request headers, merged with the built-in Authorization header. */
|
|
16
|
+
headers?: Record<string, string>;
|
|
17
|
+
/** Extra error hints keyed by HTTP status. The 401 hint is built in. */
|
|
18
|
+
hints?: Record<number, string>;
|
|
19
|
+
/**
|
|
20
|
+
* Transient-failure retry count (default 2). Pass 0 for requests that are
|
|
21
|
+
* not idempotent server-side — a retry of a request that succeeded but lost
|
|
22
|
+
* its response would perform the action twice (e.g. a first deploy with no
|
|
23
|
+
* slug creates a fresh agent per attempt).
|
|
24
|
+
*/
|
|
25
|
+
retry?: number;
|
|
26
|
+
/** Delay between retries in ms (default 300). Tests pass 0 so retry-path
|
|
27
|
+
* assertions don't sleep real wall-clock time. */
|
|
28
|
+
retryDelay?: number;
|
|
29
|
+
/** Optional fetch implementation for testing. Defaults to globalThis.fetch. */
|
|
30
|
+
fetch?: typeof globalThis.fetch;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Send an authenticated request to the platform API and return the parsed
|
|
34
|
+
* JSON response. Throws a descriptive error with status-specific hints on
|
|
35
|
+
* failure (the 401 hint is always included; pass more via `hints`).
|
|
36
|
+
*/
|
|
37
|
+
export declare function apiRequest<T = unknown>(url: string, opts: ApiRequestOptions): Promise<T>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { AgentDef } from "@alexkroman1/aai";
|
|
2
|
+
import { type BuildWorkerOptions } from "./worker-bundler.ts";
|
|
3
|
+
/** Output from the bundler: worker ESM + client files. */
|
|
4
|
+
export type DirectoryBundleOutput = {
|
|
5
|
+
/** ESM bundle of agent.ts (tool execute functions + hook handlers). */
|
|
6
|
+
worker: string;
|
|
7
|
+
/** Static client files from Vite build. Empty if no client.tsx. */
|
|
8
|
+
clientFiles: Record<string, string>;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Bundle an agent directory: build agent.ts into worker ESM + client files.
|
|
12
|
+
*
|
|
13
|
+
* agent.ts is the single entry point: `export default agent({...})`. The
|
|
14
|
+
* worker self-describes (it exports `__aaiConfig` — see `worker-bundler.ts`),
|
|
15
|
+
* so nothing here evaluates the bundle: the server extracts the config inside
|
|
16
|
+
* a guest sandbox at deploy time.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildAgentBundle(cwd: string, opts?: BuildWorkerOptions): Promise<DirectoryBundleOutput>;
|
|
19
|
+
/**
|
|
20
|
+
* Import the worker ESM from a uniquely named temp file and return the
|
|
21
|
+
* AgentDef default export. A real `file:` URL, not a `data:` URL: deploy
|
|
22
|
+
* bundles ship the SDK runtime, whose CJS interop calls
|
|
23
|
+
* `createRequire(import.meta.url)` — which rejects anything that isn't a
|
|
24
|
+
* file URL or absolute path. (The guest harness imports bundles the same
|
|
25
|
+
* way, for the same reason.) The file is removed after import; the module
|
|
26
|
+
* lives on in memory.
|
|
27
|
+
*
|
|
28
|
+
* Each call imports a unique URL, and Node's ESM registry never evicts — so
|
|
29
|
+
* every call retains one bundle for the process lifetime. That is fine for
|
|
30
|
+
* one-shot commands (`aai build`); long-lived callers must go through
|
|
31
|
+
* `createWorkerEvaluator` to at least dedupe identical builds. Evaluating in
|
|
32
|
+
* a discardable context is not an option: tool `execute` functions from the
|
|
33
|
+
* returned AgentDef are called in-process by the dev runtime, which rules
|
|
34
|
+
* out worker threads, and `node:vm` ESM evaluation is still flagged
|
|
35
|
+
* experimental.
|
|
36
|
+
*/
|
|
37
|
+
export declare function evalWorkerBundle(code: string): Promise<AgentDef>;
|
|
38
|
+
/**
|
|
39
|
+
* Memoizing wrapper around `evalWorkerBundle` for long-lived callers (the
|
|
40
|
+
* dev server): byte-identical worker code returns the previously evaluated
|
|
41
|
+
* AgentDef without touching the ESM registry. No-op saves and formatter
|
|
42
|
+
* churn are the common watcher events, so this caps the registry leak (see
|
|
43
|
+
* `evalWorkerBundle`) to genuinely-new bundles — the residual one-module-per-
|
|
44
|
+
* distinct-build leak is accepted for the reasons documented there.
|
|
45
|
+
*/
|
|
46
|
+
export declare function createWorkerEvaluator(): (code: string) => Promise<AgentDef>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* `.aai/project.json` lives in the working tree, so everything in it is
|
|
4
|
+
* untrusted input — a cloned repo can supply any value.
|
|
5
|
+
*
|
|
6
|
+
* `serverUrl` is deliberately NOT validated here. A failed field makes
|
|
7
|
+
* `readProjectConfig` return null for the whole file, which discards the
|
|
8
|
+
* `slug` too — and a deploy with no slug generates a fresh one, silently
|
|
9
|
+
* creating a duplicate agent and overwriting the config. The URL is instead
|
|
10
|
+
* validated where it is used, by `resolveServerUrl`, which rejects anything
|
|
11
|
+
* that isn't an approved http(s) origin.
|
|
12
|
+
*/
|
|
13
|
+
declare const ProjectConfigSchema: z.ZodObject<{
|
|
14
|
+
slug: z.ZodString;
|
|
15
|
+
serverUrl: z.ZodString;
|
|
16
|
+
}, z.core.$strip>;
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the global config directory (the platform-conventional env-paths
|
|
19
|
+
* location).
|
|
20
|
+
*
|
|
21
|
+
* `AAI_CONFIG_DIR` overrides everything — it exists so tests (and unusual
|
|
22
|
+
* setups) can redirect ALL global-config reads and writes away from the
|
|
23
|
+
* user's real config. The test suite's `approveServer` calls used to
|
|
24
|
+
* permanently pollute `~/.config/aai/config.json` with approved origins.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getConfigDir(): string;
|
|
27
|
+
export type ProjectConfig = z.infer<typeof ProjectConfigSchema>;
|
|
28
|
+
export declare function readProjectConfig(agentDir: string): Promise<ProjectConfig | null>;
|
|
29
|
+
export declare function writeProjectConfig(agentDir: string, data: ProjectConfig): Promise<void>;
|
|
30
|
+
export type GlobalConfig = {
|
|
31
|
+
apiKey?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Origins the user has explicitly pointed the CLI at with `--server`.
|
|
34
|
+
*
|
|
35
|
+
* Lives in the user-owned global config, never in the repo: it is what makes
|
|
36
|
+
* a `serverUrl` from `.aai/project.json` trustworthy enough to receive an
|
|
37
|
+
* API key. See `resolveServerUrl`.
|
|
38
|
+
*/
|
|
39
|
+
approvedServers?: string[];
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Origin of `url`, or `null` when it is not an absolute http(s) URL.
|
|
43
|
+
*
|
|
44
|
+
* Non-HTTP schemes are rejected rather than returned: `new URL()` yields the
|
|
45
|
+
* opaque origin `"null"` for them, which would otherwise flow on as if it
|
|
46
|
+
* were a real origin.
|
|
47
|
+
*/
|
|
48
|
+
export declare function serverOrigin(url: string): string | null;
|
|
49
|
+
/**
|
|
50
|
+
* Record `url`'s origin as user-approved, so later commands in this project
|
|
51
|
+
* may send credentials there without re-passing `--server`.
|
|
52
|
+
*/
|
|
53
|
+
export declare function approveServer(url: string, configDir?: string): Promise<void>;
|
|
54
|
+
export declare function readGlobalConfig(configDir?: string): Promise<GlobalConfig>;
|
|
55
|
+
export declare function writeGlobalConfig(configDir: string, data: GlobalConfig): Promise<void>;
|
|
56
|
+
export declare function ensureApiKey(configDir?: string): Promise<string>;
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
export declare const DEFAULT_HTML = "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, viewport-fit=cover\" />\n <title>aai</title>\n <link rel=\"icon\" href=\"./favicon.ico\" />\n <style>html, body { background: #FBF8F2; margin: 0; }</style>\n </head>\n <body>\n <main id=\"app\"></main>\n <script type=\"module\" src=\"./client.tsx\"></script>\n </body>\n</html>";
|
|
3
|
+
/**
|
|
4
|
+
* Vite plugin that serves a fallback index.html in dev mode when one doesn't
|
|
5
|
+
* exist on disk. No-op if index.html exists (user override).
|
|
6
|
+
*/
|
|
7
|
+
export declare function fallbackHtmlPlugin(root: string): Plugin;
|
|
8
|
+
/**
|
|
9
|
+
* Write a temporary index.html for Vite build (HTML must be on disk for build).
|
|
10
|
+
* Returns a cleanup function to remove it. No-op if index.html already exists.
|
|
11
|
+
*/
|
|
12
|
+
export declare function writeTempHtml(root: string): () => void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { DirectoryBundleOutput } from "./_bundler.ts";
|
|
2
|
+
export type DeployOpts = {
|
|
3
|
+
url: string;
|
|
4
|
+
bundle: DirectoryBundleOutput;
|
|
5
|
+
/** Env var values from .env to send to the server. */
|
|
6
|
+
env: Record<string, string>;
|
|
7
|
+
/** Existing slug for redeployment. Omit for first deploy — server generates one. */
|
|
8
|
+
slug?: string;
|
|
9
|
+
apiKey: string;
|
|
10
|
+
/**
|
|
11
|
+
* Ask the server to WARN (in `warnings`) instead of rejecting when the
|
|
12
|
+
* agent's providers are missing credentials — `aai deploy
|
|
13
|
+
* --allow-missing-secrets`, for setting them post-deploy with
|
|
14
|
+
* `aai secret put` (the studio's publish flow relies on this: its Secrets
|
|
15
|
+
* panel needs a deployed slug to attach secrets to).
|
|
16
|
+
*/
|
|
17
|
+
allowMissingSecrets?: boolean;
|
|
18
|
+
/** Retry delay override for tests (0 = no real sleeps on retry paths). */
|
|
19
|
+
retryDelay?: number;
|
|
20
|
+
/** Optional fetch implementation for testing. Defaults to globalThis.fetch. */
|
|
21
|
+
fetch?: typeof globalThis.fetch;
|
|
22
|
+
};
|
|
23
|
+
export type DeployResult = {
|
|
24
|
+
slug: string;
|
|
25
|
+
/** Server-side deploy warnings (e.g. the missing-credential preflight). */
|
|
26
|
+
warnings?: string[];
|
|
27
|
+
};
|
|
28
|
+
export declare function runDeploy(opts: DeployOpts): Promise<DeployResult>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev server for directory-based agents.
|
|
3
|
+
*
|
|
4
|
+
* Imports agent.ts directly for the full agent definition,
|
|
5
|
+
* builds a runtime, and starts an HTTP+WebSocket server. Watches for
|
|
6
|
+
* file changes and restarts automatically. Optionally runs Vite for
|
|
7
|
+
* client SPA HMR.
|
|
8
|
+
*/
|
|
9
|
+
import type { AgentDef } from "@alexkroman1/aai";
|
|
10
|
+
import { type FSWatcher } from "chokidar";
|
|
11
|
+
/**
|
|
12
|
+
* Warnings about the agent's credentials, computed against the `.env`-derived
|
|
13
|
+
* env and the shell. Pure so it is directly testable; `resolveAgentEnv` logs
|
|
14
|
+
* each entry. Three cases, in increasing subtlety:
|
|
15
|
+
*
|
|
16
|
+
* - a provider key found nowhere → the first session will fail auth;
|
|
17
|
+
* - a provider key found only in the shell → works here (the
|
|
18
|
+
* `withHostCredentialFallback` ergonomic) but is invisible to `aai deploy`,
|
|
19
|
+
* which uploads `.env` — the classic "works locally, dead on deploy";
|
|
20
|
+
* - a declared `requiredEnv` key absent from `.env` → `ctx.env` won't contain
|
|
21
|
+
* it at all: custom keys never fall back to the shell, so a shell export
|
|
22
|
+
* can't mask one that would be missing both here and after deploy.
|
|
23
|
+
*/
|
|
24
|
+
export declare function agentEnvWarnings(agentDef: Pick<AgentDef, "stt" | "llm" | "tts" | "s2s" | "requiredEnv">, env: Record<string, string>, shellEnv?: Record<string, string | undefined>): string[];
|
|
25
|
+
/**
|
|
26
|
+
* Load the agent definition by bundling agent.ts (and all its local imports)
|
|
27
|
+
* into a single ESM file, then importing that. A raw `import(agent.ts?t=...)`
|
|
28
|
+
* only cache-busts agent.ts itself — transitive imports (./tools.ts, etc.)
|
|
29
|
+
* stay in Node's ESM registry, so edits to them are ignored on reload.
|
|
30
|
+
* Bundling picks them up.
|
|
31
|
+
*
|
|
32
|
+
* The bundle comes from the same Vite pass deploy runs (`buildWorker`), so
|
|
33
|
+
* dev and deploy can't drift; a warm rebuild is well under 100ms. Compile
|
|
34
|
+
* errors in the agent's code propagate — the restart loop reports them and
|
|
35
|
+
* keeps the old server. Evaluation goes through the memoizing evaluator so
|
|
36
|
+
* a no-op save doesn't leak another module into the ESM registry.
|
|
37
|
+
*/
|
|
38
|
+
export declare function loadAgentDef(cwd: string, evaluate: (code: string) => Promise<AgentDef>): Promise<AgentDef>;
|
|
39
|
+
/**
|
|
40
|
+
* True for paths that should never trigger a restart: anything inside
|
|
41
|
+
* `node_modules/` and any dot-entry (`.git/`, `.aai/`, `.DS_Store`, …).
|
|
42
|
+
* `.git/` especially matters — commits and status checks churn the index
|
|
43
|
+
* and would otherwise cause spurious full backend restarts.
|
|
44
|
+
*
|
|
45
|
+
* Exception: `.env` / `.env.*` files stay watched — env edits should
|
|
46
|
+
* restart the server with the new values.
|
|
47
|
+
*/
|
|
48
|
+
export declare function isIgnoredPath(dir: string, filePath: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Watch the agent directory for changes and call `onChange` when detected.
|
|
51
|
+
* Debounces to avoid rapid restarts. Uses chokidar for reliable recursive
|
|
52
|
+
* watching across platforms (raw `fs.watch` misses events on Linux).
|
|
53
|
+
*/
|
|
54
|
+
export declare function watchDirectory(dir: string, onChange: () => void): FSWatcher;
|
|
55
|
+
export type DevServerOptions = {
|
|
56
|
+
cwd: string;
|
|
57
|
+
port: number;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Vite dev-server config for the client SPA. Extracted so the proxy wiring
|
|
61
|
+
* is unit-testable: `/websocket` MUST proxy with `ws: true` or `aai dev`
|
|
62
|
+
* with a `client.tsx` serves a page whose WebSocket never connects.
|
|
63
|
+
*
|
|
64
|
+
* `strictPort` because the reported URL is `http://localhost:<port>` —
|
|
65
|
+
* without it, Vite silently binds port+N when the port is busy and the
|
|
66
|
+
* printed/JSON-returned URL points at whatever else was listening.
|
|
67
|
+
*/
|
|
68
|
+
export declare function viteDevConfig(cwd: string, vitePort: number, backendPort: number): import("vite").InlineConfig;
|
|
69
|
+
/**
|
|
70
|
+
* Start the dev server for a directory-based agent.
|
|
71
|
+
*
|
|
72
|
+
* Returns a cleanup function to shut down the server and watchers.
|
|
73
|
+
*/
|
|
74
|
+
export declare function startDevServer(opts: DevServerOptions): Promise<() => Promise<void>>;
|
|
@@ -4,78 +4,57 @@ import { r as isDevMode, t as getMonorepoRoot } from "./_agent-D8zBb3M5.mjs";
|
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import fs from "node:fs/promises";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
7
|
+
const REPO_URL = `https://github.com/alexkroman/agent`;
|
|
8
|
+
/**
|
|
9
|
+
* Templates as shipped inside the published tarball, copied into `dist/` by
|
|
10
|
+
* `bundle-templates.mjs` at build time — so this resolves to `dist/` for a
|
|
11
|
+
* published CLI and to the (template-less) package root when running source
|
|
12
|
+
* in the monorepo, where the branch above wins.
|
|
13
|
+
*
|
|
14
|
+
* They used to be fetched at `init` time with giget from
|
|
15
|
+
* `github:alexkroman/agent/packages/aai-templates#main`. That required a
|
|
16
|
+
* network for every `init`, and pinned templates to `main` regardless of the
|
|
17
|
+
* CLI version the user had installed, so a template written against a newer
|
|
18
|
+
* SDK could land in a project resolving an older one. Bundling pins the two
|
|
19
|
+
* together by construction. It also puts the templates inside the studio's
|
|
20
|
+
* guest sandbox, which has the CLI in its baked toolchain but no way to fetch
|
|
21
|
+
* anything from GitHub.
|
|
22
|
+
*/
|
|
23
|
+
function bundledTemplatesDir() {
|
|
24
|
+
return import.meta.dirname;
|
|
25
|
+
}
|
|
26
|
+
/** Resolve the templates root — env override, then monorepo, then bundled. */
|
|
27
|
+
function resolveTemplatesDir() {
|
|
28
|
+
const override = process.env.AAI_TEMPLATES_DIR;
|
|
29
|
+
if (override) return override;
|
|
23
30
|
const monorepoRoot = getMonorepoRoot();
|
|
24
|
-
if (monorepoRoot) return
|
|
25
|
-
|
|
26
|
-
cleanup: noCleanup
|
|
27
|
-
};
|
|
28
|
-
if (!VALID_REF_RE.test(GIGET_REF)) throw new Error(`Invalid AAI_TEMPLATES_REF: ${JSON.stringify(GIGET_REF)} is not a git ref.`);
|
|
29
|
-
const extractDir = await fs.mkdtemp(path.join(os.tmpdir(), "aai-templates-"));
|
|
30
|
-
const cleanup = async () => {
|
|
31
|
-
await fs.rm(extractDir, {
|
|
32
|
-
recursive: true,
|
|
33
|
-
force: true
|
|
34
|
-
}).catch(() => void 0);
|
|
35
|
-
};
|
|
36
|
-
try {
|
|
37
|
-
const { dir } = await downloadTemplate(`${GIGET_SOURCE}#${GIGET_REF}`, {
|
|
38
|
-
dir: extractDir,
|
|
39
|
-
force: true,
|
|
40
|
-
forceClean: true
|
|
41
|
-
});
|
|
42
|
-
return {
|
|
43
|
-
root: dir,
|
|
44
|
-
cleanup
|
|
45
|
-
};
|
|
46
|
-
} catch (err) {
|
|
47
|
-
await cleanup();
|
|
48
|
-
throw new Error(`Failed to download templates from ${GIGET_SOURCE}#${GIGET_REF}: ${errorMessage(err)}`, { cause: err });
|
|
49
|
-
}
|
|
31
|
+
if (monorepoRoot) return path.join(monorepoRoot, "packages", "aai-templates");
|
|
32
|
+
return bundledTemplatesDir();
|
|
50
33
|
}
|
|
51
34
|
/**
|
|
52
|
-
*
|
|
35
|
+
* Copy a template into targetDir, merging scaffold files underneath.
|
|
53
36
|
*/
|
|
54
37
|
async function downloadAndMergeTemplate(template, targetDir) {
|
|
55
|
-
const
|
|
38
|
+
const root = resolveTemplatesDir();
|
|
39
|
+
const templatesDir = path.join(root, "templates");
|
|
40
|
+
let available;
|
|
56
41
|
try {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
available = await fs.readdir(templatesDir, { withFileTypes: true });
|
|
61
|
-
} catch (err) {
|
|
62
|
-
throw new Error(`Templates directory is missing or unreadable at ${templatesDir} (corrupt or incomplete template download?): ${errorMessage(err)}`, { cause: err });
|
|
63
|
-
}
|
|
64
|
-
const names = available.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
65
|
-
if (!names.includes(template)) throw new Error(`Unknown template "${template}". Available templates: ${names.join(", ")}`);
|
|
66
|
-
await fs.cp(path.join(templatesDir, template), targetDir, {
|
|
67
|
-
recursive: true,
|
|
68
|
-
force: true
|
|
69
|
-
});
|
|
70
|
-
const scaffoldDir = path.join(root, "scaffold");
|
|
71
|
-
if (existsSync(scaffoldDir)) await fs.cp(scaffoldDir, targetDir, {
|
|
72
|
-
recursive: true,
|
|
73
|
-
force: false,
|
|
74
|
-
errorOnExist: false
|
|
75
|
-
});
|
|
76
|
-
} finally {
|
|
77
|
-
await cleanup();
|
|
42
|
+
available = await fs.readdir(templatesDir, { withFileTypes: true });
|
|
43
|
+
} catch (err) {
|
|
44
|
+
throw new Error(`Templates directory is missing or unreadable at ${templatesDir} (incomplete @alexkroman1/aai-cli install?): ${errorMessage(err)}`, { cause: err });
|
|
78
45
|
}
|
|
46
|
+
const names = available.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
47
|
+
if (!names.includes(template)) throw new Error(`Unknown template "${template}". Available templates: ${names.join(", ")}`);
|
|
48
|
+
await fs.cp(path.join(templatesDir, template), targetDir, {
|
|
49
|
+
recursive: true,
|
|
50
|
+
force: true
|
|
51
|
+
});
|
|
52
|
+
const scaffoldDir = path.join(root, "scaffold");
|
|
53
|
+
if (existsSync(scaffoldDir)) await fs.cp(scaffoldDir, targetDir, {
|
|
54
|
+
recursive: true,
|
|
55
|
+
force: false,
|
|
56
|
+
errorOnExist: false
|
|
57
|
+
});
|
|
79
58
|
}
|
|
80
59
|
//#endregion
|
|
81
60
|
//#region _init.ts
|
package/dist/_init.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type InitOptions = {
|
|
2
|
+
targetDir: string;
|
|
3
|
+
template: string;
|
|
4
|
+
};
|
|
5
|
+
/** Rewrite workspace deps to link: paths so pnpm links to local source. */
|
|
6
|
+
export declare function patchPackageJsonForWorkspace(targetDir: string): Promise<void>;
|
|
7
|
+
export declare function runInit(opts: InitOptions): Promise<void>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface RecordedRequest {
|
|
2
|
+
method: string;
|
|
3
|
+
path: string;
|
|
4
|
+
headers: Record<string, string | string[] | undefined>;
|
|
5
|
+
/** Decoded body text (inflated first when sent with Content-Encoding: gzip). */
|
|
6
|
+
body: string;
|
|
7
|
+
}
|
|
8
|
+
export interface MockApi {
|
|
9
|
+
/** Base URL of the mock server (http://localhost:<port>) */
|
|
10
|
+
url: string;
|
|
11
|
+
/** All recorded requests */
|
|
12
|
+
requests: RecordedRequest[];
|
|
13
|
+
/** Secrets currently stored */
|
|
14
|
+
secrets: Record<string, string>;
|
|
15
|
+
/** Override response for a specific method+path pattern */
|
|
16
|
+
override(method: string, pathPattern: string, status: number, body?: string): void;
|
|
17
|
+
/** Clear recorded requests */
|
|
18
|
+
clear(): void;
|
|
19
|
+
/** Stop the server */
|
|
20
|
+
stop(): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export declare function startMockApi(): Promise<MockApi>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface MockRegistry {
|
|
2
|
+
/** Local registry URL (http://localhost:<port>) */
|
|
3
|
+
registryUrl: string;
|
|
4
|
+
/** The unique version string used for published workspace packages */
|
|
5
|
+
testVersion: string;
|
|
6
|
+
/** Environment variables to set for child processes using this registry */
|
|
7
|
+
env: Record<string, string>;
|
|
8
|
+
/** Stop the registry and clean up */
|
|
9
|
+
stop: () => Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Start a mock npm registry, build and publish workspace packages to it.
|
|
13
|
+
*
|
|
14
|
+
* @param packagesDir - Path to the `packages/` directory in the monorepo
|
|
15
|
+
* @param packageNames - Directory names under `packages/` to publish (e.g. ["aai", "aai-ui", "aai-cli"])
|
|
16
|
+
*/
|
|
17
|
+
export declare function startMockRegistry(packagesDir: string, packageNames: string[]): Promise<MockRegistry>;
|
|
@@ -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
|
+
}
|
|
@@ -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,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>;
|
|
@@ -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>;
|
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 {};
|