@alexkroman1/aai-cli 1.9.2 → 1.10.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-cHGzbDVG.mjs → _agent-Ba5Ykp05.mjs} +22 -26
- package/dist/{_api-client-yJD7xGfd.mjs → _api-client-a6cPMebU.mjs} +2 -2
- package/dist/_bundler-CKz9Q15i.mjs +96 -0
- package/dist/{_config-zV70t71V.mjs → _config-BMjZt5hG.mjs} +58 -43
- package/dist/_dev-server-DZl-xNwH.mjs +411 -0
- package/dist/{_init-y7K-dzni.mjs → _init-BznoJC91.mjs} +57 -25
- package/dist/{_server-common-QpOTRZgi.mjs → _server-common-Bv-4Rskq.mjs} +2 -2
- package/dist/_ui-CKEIHAtB.mjs +111 -0
- package/dist/_utils-ECl2je7-.mjs +97 -0
- package/dist/_vite-env-DNb9R8xq.mjs +48 -0
- package/dist/cli.mjs +75 -66
- package/dist/{client-bundler-Bki_Rned.mjs → client-bundler-VdcgFcos.mjs} +22 -24
- package/dist/client-bundler.mjs +1 -1
- package/dist/{delete-DE35s3d5.mjs → delete-xZ8PzM_4.mjs} +4 -7
- package/dist/{deploy-DOy6wOCd.mjs → deploy-Ds1spTPC.mjs} +23 -13
- package/dist/{dev-DGzDKzGZ.mjs → dev-CtE0d-Gs.mjs} +19 -12
- package/dist/{init-BDhs6-jB.mjs → init-CFwZf8q1.mjs} +22 -17
- package/dist/{secret-D-90QtDQ.mjs → secret-B4LRmUhF.mjs} +5 -7
- package/dist/{test-C6jwCnFu.mjs → test-DXPY9Bqq.mjs} +24 -11
- package/dist/worker-bundler.mjs +1 -1
- package/package.json +7 -11
- package/dist/_bundler-CpLVjRzc.mjs +0 -62
- package/dist/_dev-server-8pf2tqvC.mjs +0 -214
- package/dist/_output-fy2bXRNb.mjs +0 -63
- package/dist/_ui-YqQ6Fi8K.mjs +0 -34
- package/dist/_utils-BeU10C7O.mjs +0 -55
- package/dist/_vite-env-CmXG4ZAu.mjs +0 -31
- package/dist/types.mjs +0 -2
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
//#region _utils.ts
|
|
5
|
+
/** Resolve the working directory from INIT_CWD or process.cwd(). */
|
|
6
|
+
function resolveCwd() {
|
|
7
|
+
return process.env.INIT_CWD || process.cwd();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Extract a message from an unknown error. Local copy of `errorMessage` from
|
|
11
|
+
* `@alexkroman1/aai` — importing the root barrel pulls zod into every CLI
|
|
12
|
+
* invocation (including `aai --help`), so the one-liner lives here instead.
|
|
13
|
+
*/
|
|
14
|
+
function errorMessage(err) {
|
|
15
|
+
return err instanceof Error ? err.message : String(err);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Extract a stack (falling back to the message) from an unknown error. Local
|
|
19
|
+
* copy of `errorDetail` from `@alexkroman1/aai` for the same reason as
|
|
20
|
+
* {@link errorMessage} above.
|
|
21
|
+
*/
|
|
22
|
+
function errorDetail(err) {
|
|
23
|
+
return err instanceof Error ? err.stack ?? err.message : String(err);
|
|
24
|
+
}
|
|
25
|
+
/** The `code` of a Node errno-style error (`"ENOENT"`, `"EPIPE"`, …), or undefined. */
|
|
26
|
+
function errorCode(err) {
|
|
27
|
+
return err instanceof Error && "code" in err && typeof err.code === "string" ? err.code : void 0;
|
|
28
|
+
}
|
|
29
|
+
/** True when `err` is a filesystem EEXIST error (target already exists). */
|
|
30
|
+
function isEexist(err) {
|
|
31
|
+
return errorCode(err) === "EEXIST";
|
|
32
|
+
}
|
|
33
|
+
/** Validate that a module's default export is a valid agent definition. Throws if invalid. */
|
|
34
|
+
function validateAgentExport(mod) {
|
|
35
|
+
if (!mod?.name || typeof mod.name !== "string") throw new Error("agent.ts must export default agent({ name: ... })");
|
|
36
|
+
}
|
|
37
|
+
async function fileExists(p) {
|
|
38
|
+
try {
|
|
39
|
+
await fs.access(p);
|
|
40
|
+
return true;
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Read and parse a JSON file. Returns null only when the file does not exist
|
|
47
|
+
* (ENOENT — the "optional file" case). A file that exists but cannot be read
|
|
48
|
+
* (EACCES, …) or parsed throws instead: treating a corrupted file as absent
|
|
49
|
+
* hides real problems — e.g. a corrupted `.aai/project.json` would silently
|
|
50
|
+
* deploy under a NEW slug, orphaning the live deployment.
|
|
51
|
+
*/
|
|
52
|
+
async function readJson(filePath) {
|
|
53
|
+
let raw;
|
|
54
|
+
try {
|
|
55
|
+
raw = await fs.readFile(filePath, "utf-8");
|
|
56
|
+
} catch (err) {
|
|
57
|
+
if (errorCode(err) === "ENOENT") return null;
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
return JSON.parse(raw);
|
|
62
|
+
} catch (err) {
|
|
63
|
+
throw new Error(`Invalid JSON in ${filePath}: ${errorMessage(err)}`, { cause: err });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Write `data` as pretty-printed JSON (+ trailing newline), creating parent dirs.
|
|
68
|
+
*
|
|
69
|
+
* Writes to a temp file in the same directory, then renames it into place.
|
|
70
|
+
* A plain `writeFile` can be observed (or left, on crash) half-written; a
|
|
71
|
+
* torn config.json fails `JSON.parse`, reads back as `{}`, and the next
|
|
72
|
+
* read-modify-write silently wipes fields like `approvedServers`. Rename on
|
|
73
|
+
* the same filesystem is atomic, so readers only ever see a complete file.
|
|
74
|
+
* Two concurrent CLI processes can still lose each other's *updates*
|
|
75
|
+
* (last rename wins) — acceptable for these small user-config files.
|
|
76
|
+
*
|
|
77
|
+
* `mode` restricts the file's permissions (the rename carries the temp
|
|
78
|
+
* file's mode to the destination, so an existing world-readable file is
|
|
79
|
+
* tightened on the next write). The parent directory is created 0o700 in
|
|
80
|
+
* that case so a fresh config dir never goes through a readable window.
|
|
81
|
+
*/
|
|
82
|
+
async function writeJson(filePath, data, opts = {}) {
|
|
83
|
+
await fs.mkdir(path.dirname(filePath), {
|
|
84
|
+
recursive: true,
|
|
85
|
+
...opts.mode !== void 0 ? { mode: 448 } : {}
|
|
86
|
+
});
|
|
87
|
+
const tmpPath = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
88
|
+
await fs.writeFile(tmpPath, `${JSON.stringify(data, null, 2)}\n`, { ...opts.mode !== void 0 ? { mode: opts.mode } : {} });
|
|
89
|
+
try {
|
|
90
|
+
await fs.rename(tmpPath, filePath);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
await fs.rm(tmpPath, { force: true }).catch(() => void 0);
|
|
93
|
+
throw err;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
export { isEexist as a, validateAgentExport as c, fileExists as i, writeJson as l, errorDetail as n, readJson as o, errorMessage as r, resolveCwd as s, errorCode as t };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
//#region _vite-env.ts
|
|
3
|
+
/**
|
|
4
|
+
* Run a Vite build without letting it mutate the calling process's env.
|
|
5
|
+
*
|
|
6
|
+
* Vite's `build()` sets `process.env.NODE_ENV = "production"` when NODE_ENV is
|
|
7
|
+
* unset — a global, permanent side effect on whatever process invoked it. That
|
|
8
|
+
* is fine for a one-shot `aai build`, but both long-lived callers are broken by
|
|
9
|
+
* it:
|
|
10
|
+
*
|
|
11
|
+
* - `aai dev` rebuilds on every file change, so the first rebuild would flip
|
|
12
|
+
* the dev server into production mode.
|
|
13
|
+
* - The platform studio builds inside the server process, where flipping
|
|
14
|
+
* NODE_ENV makes the sandbox demand gVisor ("gVisor (runsc) is required in
|
|
15
|
+
* production but not found on PATH") and refuse every subsequent deploy on a
|
|
16
|
+
* dev machine.
|
|
17
|
+
*
|
|
18
|
+
* Snapshot and restore rather than pinning a value: callers that legitimately
|
|
19
|
+
* run with NODE_ENV=production must keep it.
|
|
20
|
+
*
|
|
21
|
+
* The snapshot is refcounted, not per-call: both bundle paths run the worker
|
|
22
|
+
* and client builds concurrently (`Promise.all`), and independent snapshots
|
|
23
|
+
* interleave — the second entrant would snapshot the "production" the first
|
|
24
|
+
* build's Vite just set, and "restore" it after the first exiter deleted it,
|
|
25
|
+
* flipping the process permanently anyway. So the first entrant snapshots,
|
|
26
|
+
* later entrants just join, and only the last exiter restores. This keeps the
|
|
27
|
+
* builds parallel (a mutex serializing them would cost real deploy time).
|
|
28
|
+
*/
|
|
29
|
+
let activeBuilds = 0;
|
|
30
|
+
let savedNodeEnv;
|
|
31
|
+
/**
|
|
32
|
+
* `env` is injectable for tests ONLY, so specs can exercise the
|
|
33
|
+
* snapshot/refcount logic on a plain object instead of mutating (and
|
|
34
|
+
* repeatedly deleting) the real `process.env.NODE_ENV` mid-suite.
|
|
35
|
+
*/
|
|
36
|
+
async function withPreservedNodeEnv(fn, env = process.env) {
|
|
37
|
+
if (activeBuilds === 0) savedNodeEnv = env.NODE_ENV;
|
|
38
|
+
activeBuilds++;
|
|
39
|
+
try {
|
|
40
|
+
return await fn();
|
|
41
|
+
} finally {
|
|
42
|
+
activeBuilds--;
|
|
43
|
+
if (activeBuilds === 0) if (savedNodeEnv === void 0) delete env.NODE_ENV;
|
|
44
|
+
else env.NODE_ENV = savedNodeEnv;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { withPreservedNodeEnv as t };
|
package/dist/cli.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { i as
|
|
4
|
-
import { n as errorMessage, o as resolveCwd, r as fileExists } from "./_utils-BeU10C7O.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-CKEIHAtB.mjs";
|
|
3
|
+
import { i as fileExists, r as errorMessage, s as resolveCwd } from "./_utils-ECl2je7-.mjs";
|
|
5
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
6
5
|
import path from "node:path";
|
|
7
6
|
import { fileURLToPath } from "node:url";
|
|
@@ -31,14 +30,20 @@ const sharedArgs = {
|
|
|
31
30
|
}
|
|
32
31
|
};
|
|
33
32
|
const cliDir = path.dirname(fileURLToPath(import.meta.url));
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Read this CLI's own version from its package.json (source layout keeps it
|
|
35
|
+
* next to cli.ts; dist layout one level up). A missing or corrupt file must
|
|
36
|
+
* not brick every command over a cosmetic string — warn and fall back.
|
|
37
|
+
*/
|
|
38
|
+
function readCliVersion(dir) {
|
|
39
|
+
for (const candidate of [path.join(dir, "package.json"), path.join(dir, "..", "package.json")]) try {
|
|
40
|
+
const parsed = JSON.parse(readFileSync(candidate, "utf-8"));
|
|
41
|
+
if (typeof parsed.version === "string") return parsed.version;
|
|
42
|
+
} catch {}
|
|
43
|
+
process.stderr.write("warning: could not read aai's package.json — reporting version unknown\n");
|
|
44
|
+
return "unknown";
|
|
40
45
|
}
|
|
41
|
-
const VERSION =
|
|
46
|
+
const VERSION = readCliVersion(cliDir);
|
|
42
47
|
/** Shared command setup: resolve cwd, optionally require agent.ts. */
|
|
43
48
|
async function setup(opts) {
|
|
44
49
|
const cwd = resolveCwd();
|
|
@@ -47,28 +52,19 @@ async function setup(opts) {
|
|
|
47
52
|
}
|
|
48
53
|
return cwd;
|
|
49
54
|
}
|
|
50
|
-
/** Catch command errors and display a clean message instead of a raw stack trace. */
|
|
51
|
-
async function handleErrors(mode, fn) {
|
|
52
|
-
try {
|
|
53
|
-
await fn();
|
|
54
|
-
} catch (err) {
|
|
55
|
-
const code = err instanceof CliError ? err.code : "command_failed";
|
|
56
|
-
const hint = err instanceof CliError ? err.hint : void 0;
|
|
57
|
-
if (mode === "json") {
|
|
58
|
-
const result = fail(code, errorMessage(err), hint);
|
|
59
|
-
await writeLine(`${JSON.stringify(result)}\n`);
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
|
62
|
-
log.error(errorMessage(err));
|
|
63
|
-
process.exit(1);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
55
|
/**
|
|
67
|
-
* Run a command body with standard
|
|
56
|
+
* Run a command body with standard output-mode resolution, error handling,
|
|
57
|
+
* and result emission.
|
|
68
58
|
*
|
|
69
59
|
* - `setYes`: json mode sets `args.yes = true` (only meaningful for commands with a `yes` arg).
|
|
70
|
-
* -
|
|
71
|
-
*
|
|
60
|
+
* - API key acquisition is owned by `resolveDeployTarget`/`getServerInfo`
|
|
61
|
+
* inside the commands that talk to the platform — after the server-trust
|
|
62
|
+
* check, so an untrusted `serverUrl` is refused without prompting for a
|
|
63
|
+
* key, and commands with no platform traffic never prompt at all.
|
|
64
|
+
*
|
|
65
|
+
* A thrown error and a returned `fail(...)` converge here on one emitter:
|
|
66
|
+
* human mode logs the message, JSON mode writes exactly one result line,
|
|
67
|
+
* and both exit 1.
|
|
72
68
|
*/
|
|
73
69
|
async function runCommand(args, fn, opts = {}) {
|
|
74
70
|
const mode = getOutputMode(args);
|
|
@@ -76,13 +72,17 @@ async function runCommand(args, fn, opts = {}) {
|
|
|
76
72
|
silenceOutput();
|
|
77
73
|
if (opts.setYes) args.yes = true;
|
|
78
74
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
75
|
+
let result;
|
|
76
|
+
try {
|
|
77
|
+
result = await fn(mode);
|
|
78
|
+
} catch (err) {
|
|
79
|
+
const code = err instanceof CliError ? err.code : "command_failed";
|
|
80
|
+
const hint = err instanceof CliError ? err.hint : void 0;
|
|
81
|
+
if (mode === "human") log.error(errorMessage(err));
|
|
82
|
+
result = fail(code, errorMessage(err), hint);
|
|
83
|
+
}
|
|
84
|
+
if (mode === "json") await writeLine(`${JSON.stringify(result)}\n`);
|
|
85
|
+
if (!result.ok) process.exit(1);
|
|
86
86
|
}
|
|
87
87
|
const init = defineCommand({
|
|
88
88
|
meta: {
|
|
@@ -110,7 +110,7 @@ const init = defineCommand({
|
|
|
110
110
|
json: sharedArgs.json,
|
|
111
111
|
skipApi: {
|
|
112
112
|
type: "boolean",
|
|
113
|
-
description: "
|
|
113
|
+
description: "Deprecated no-op: the API key is now only requested when deploying"
|
|
114
114
|
},
|
|
115
115
|
skipDeploy: {
|
|
116
116
|
type: "boolean",
|
|
@@ -119,7 +119,7 @@ const init = defineCommand({
|
|
|
119
119
|
},
|
|
120
120
|
async run({ args }) {
|
|
121
121
|
await runCommand(args, async (mode) => {
|
|
122
|
-
const { executeInit } = await import("./init-
|
|
122
|
+
const { executeInit } = await import("./init-CFwZf8q1.mjs");
|
|
123
123
|
return executeInit({
|
|
124
124
|
dir: args.dir,
|
|
125
125
|
force: args.force,
|
|
@@ -128,10 +128,7 @@ const init = defineCommand({
|
|
|
128
128
|
skipDeploy: args.skipDeploy,
|
|
129
129
|
server: args.server
|
|
130
130
|
}, mode === "json" ? { silent: true } : void 0);
|
|
131
|
-
}, {
|
|
132
|
-
setYes: true,
|
|
133
|
-
apiKey: !args.skipApi
|
|
134
|
-
});
|
|
131
|
+
}, { setYes: true });
|
|
135
132
|
}
|
|
136
133
|
});
|
|
137
134
|
const dev = defineCommand({
|
|
@@ -146,7 +143,7 @@ const dev = defineCommand({
|
|
|
146
143
|
async run({ args }) {
|
|
147
144
|
await runCommand(args, async () => {
|
|
148
145
|
const cwd = await setup({ agent: true });
|
|
149
|
-
const { executeDev } = await import("./dev-
|
|
146
|
+
const { executeDev } = await import("./dev-CtE0d-Gs.mjs");
|
|
150
147
|
return executeDev({
|
|
151
148
|
cwd,
|
|
152
149
|
port: args.port
|
|
@@ -163,9 +160,9 @@ const test = defineCommand({
|
|
|
163
160
|
async run({ args }) {
|
|
164
161
|
await runCommand(args, async () => {
|
|
165
162
|
const cwd = await setup();
|
|
166
|
-
const { executeTest } = await import("./test-
|
|
163
|
+
const { executeTest } = await import("./test-DXPY9Bqq.mjs");
|
|
167
164
|
return executeTest(cwd);
|
|
168
|
-
}
|
|
165
|
+
});
|
|
169
166
|
}
|
|
170
167
|
});
|
|
171
168
|
const build = defineCommand({
|
|
@@ -184,12 +181,20 @@ const build = defineCommand({
|
|
|
184
181
|
await runCommand(args, async () => {
|
|
185
182
|
const cwd = await setup({ agent: true });
|
|
186
183
|
if (!args.skipTests) {
|
|
187
|
-
const { runVitest } = await import("./test-
|
|
188
|
-
|
|
184
|
+
const { classifyVitestError, runVitest } = await import("./test-DXPY9Bqq.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
|
+
}
|
|
189
194
|
}
|
|
190
|
-
const { executeBuild } = await import("./_bundler-
|
|
195
|
+
const { executeBuild } = await import("./_bundler-CKz9Q15i.mjs");
|
|
191
196
|
return executeBuild(cwd);
|
|
192
|
-
}
|
|
197
|
+
});
|
|
193
198
|
}
|
|
194
199
|
});
|
|
195
200
|
const deploy = defineCommand({
|
|
@@ -204,7 +209,7 @@ const deploy = defineCommand({
|
|
|
204
209
|
async run({ args }) {
|
|
205
210
|
await runCommand(args, async () => {
|
|
206
211
|
const cwd = await setup({ agent: true });
|
|
207
|
-
const { executeDeploy } = await import("./deploy-
|
|
212
|
+
const { executeDeploy } = await import("./deploy-Ds1spTPC.mjs");
|
|
208
213
|
return executeDeploy({
|
|
209
214
|
cwd,
|
|
210
215
|
...args.server ? { server: args.server } : {}
|
|
@@ -224,7 +229,7 @@ const del = defineCommand({
|
|
|
224
229
|
async run({ args }) {
|
|
225
230
|
await runCommand(args, async () => {
|
|
226
231
|
const cwd = await setup();
|
|
227
|
-
const { executeDelete } = await import("./delete-
|
|
232
|
+
const { executeDelete } = await import("./delete-xZ8PzM_4.mjs");
|
|
228
233
|
return executeDelete({
|
|
229
234
|
cwd,
|
|
230
235
|
...args.server ? { server: args.server } : {}
|
|
@@ -255,7 +260,7 @@ const secret = defineCommand({
|
|
|
255
260
|
async run({ args }) {
|
|
256
261
|
await runCommand(args, async (mode) => {
|
|
257
262
|
const cwd = await setup();
|
|
258
|
-
const { executeSecretPut, readStdin } = await import("./secret-
|
|
263
|
+
const { executeSecretPut, readStdin } = await import("./secret-B4LRmUhF.mjs");
|
|
259
264
|
const value = mode === "json" ? await readStdin() : void 0;
|
|
260
265
|
if (mode === "json" && !value) throw new CliError("no_input", "No value provided", "Pipe secret value to stdin");
|
|
261
266
|
return executeSecretPut(cwd, args.name, value, args.server);
|
|
@@ -279,7 +284,7 @@ const secret = defineCommand({
|
|
|
279
284
|
async run({ args }) {
|
|
280
285
|
await runCommand(args, async () => {
|
|
281
286
|
const cwd = await setup();
|
|
282
|
-
const { executeSecretDelete } = await import("./secret-
|
|
287
|
+
const { executeSecretDelete } = await import("./secret-B4LRmUhF.mjs");
|
|
283
288
|
return executeSecretDelete(cwd, args.name, args.server);
|
|
284
289
|
});
|
|
285
290
|
}
|
|
@@ -296,7 +301,7 @@ const secret = defineCommand({
|
|
|
296
301
|
async run({ args }) {
|
|
297
302
|
await runCommand(args, async () => {
|
|
298
303
|
const cwd = await setup();
|
|
299
|
-
const { executeSecretList } = await import("./secret-
|
|
304
|
+
const { executeSecretList } = await import("./secret-B4LRmUhF.mjs");
|
|
300
305
|
return executeSecretList(cwd, args.server);
|
|
301
306
|
});
|
|
302
307
|
}
|
|
@@ -320,18 +325,22 @@ const mainCommand = defineCommand({
|
|
|
320
325
|
}
|
|
321
326
|
});
|
|
322
327
|
if (process.env.VITEST !== "true") {
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
"
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
328
|
+
installStdoutGuard();
|
|
329
|
+
const runDefault = async () => {
|
|
330
|
+
if (process.argv.length > 2) return;
|
|
331
|
+
if (!existsSync(path.join(resolveCwd(), "agent.ts"))) {
|
|
332
|
+
process.argv.splice(2, 0, "init");
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
336
|
+
if (await (await import("@clack/prompts")).confirm({ message: "Deploy this agent to production?" }) !== true) {
|
|
337
|
+
log.info("Cancelled. Run `aai --help` to see all commands.");
|
|
338
|
+
process.exit(0);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
process.argv.splice(2, 0, "deploy");
|
|
342
|
+
};
|
|
343
|
+
runDefault().then(() => runMain(mainCommand)).catch((err) => {
|
|
335
344
|
log.error(errorMessage(err));
|
|
336
345
|
process.exitCode = 1;
|
|
337
346
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { t as withPreservedNodeEnv } from "./_vite-env-
|
|
2
|
+
import { i as fileExists, r as errorMessage } from "./_utils-ECl2je7-.mjs";
|
|
3
|
+
import { t as withPreservedNodeEnv } from "./_vite-env-DNb9R8xq.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";
|
|
@@ -41,7 +41,7 @@ function fallbackHtmlPlugin(root) {
|
|
|
41
41
|
server.transformIndexHtml("/", DEFAULT_HTML, req.originalUrl).then((html) => {
|
|
42
42
|
res.setHeader("Content-Type", "text/html");
|
|
43
43
|
res.end(html);
|
|
44
|
-
}
|
|
44
|
+
}).catch(next);
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
next();
|
|
@@ -56,29 +56,21 @@ function fallbackHtmlPlugin(root) {
|
|
|
56
56
|
function writeTempHtml(root) {
|
|
57
57
|
const htmlPath = path.join(root, "index.html");
|
|
58
58
|
if (existsSync(htmlPath)) return () => {};
|
|
59
|
-
|
|
60
|
-
return () => {
|
|
59
|
+
const cleanup = () => {
|
|
61
60
|
try {
|
|
62
61
|
unlinkSync(htmlPath);
|
|
63
62
|
} catch {}
|
|
64
63
|
};
|
|
64
|
+
try {
|
|
65
|
+
writeFileSync(htmlPath, DEFAULT_HTML);
|
|
66
|
+
} catch (err) {
|
|
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 });
|
|
69
|
+
}
|
|
70
|
+
return cleanup;
|
|
65
71
|
}
|
|
66
72
|
//#endregion
|
|
67
73
|
//#region client-bundler.ts
|
|
68
|
-
/**
|
|
69
|
-
* Client SPA bundling — the one implementation of "turn a `client.tsx` into
|
|
70
|
-
* deployable `clientFiles`".
|
|
71
|
-
*
|
|
72
|
-
* Public (no `_` prefix) because the platform's browser studio reuses it: a
|
|
73
|
-
* studio workspace is materialized to a directory and built through this same
|
|
74
|
-
* function, so a UI published from the browser is byte-identical to one
|
|
75
|
-
* deployed with `aai deploy`. Keep it that way — a second client bundler is
|
|
76
|
-
* how the two paths would silently drift.
|
|
77
|
-
*
|
|
78
|
-
* The studio differs from a CLI project in two ways, hence the options:
|
|
79
|
-
* it has no `vite.config.ts` to supply React/Tailwind plugins, and its files
|
|
80
|
-
* are untrusted so any config the workspace *does* contain must be ignored.
|
|
81
|
-
*/
|
|
82
74
|
const DEFAULT_OUT_DIR = ".aai/client";
|
|
83
75
|
/**
|
|
84
76
|
* Packages resolved from the build root rather than from whichever
|
|
@@ -109,8 +101,9 @@ async function buildClient(cwd, opts = {}) {
|
|
|
109
101
|
if (!await fileExists(path.join(cwd, "client.tsx"))) return {};
|
|
110
102
|
const outDir = opts.outDir ?? DEFAULT_OUT_DIR;
|
|
111
103
|
const clientDir = path.join(cwd, outDir);
|
|
112
|
-
|
|
104
|
+
let cleanupHtml = () => {};
|
|
113
105
|
try {
|
|
106
|
+
cleanupHtml = writeTempHtml(cwd);
|
|
114
107
|
await withPreservedNodeEnv(() => build({
|
|
115
108
|
root: cwd,
|
|
116
109
|
base: "./",
|
|
@@ -131,10 +124,15 @@ async function buildClient(cwd, opts = {}) {
|
|
|
131
124
|
/** Read a built client directory into an in-memory deploy payload. */
|
|
132
125
|
async function readClientDir(clientDir) {
|
|
133
126
|
const files = {};
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
127
|
+
let entries;
|
|
128
|
+
try {
|
|
129
|
+
entries = await fs.readdir(clientDir, {
|
|
130
|
+
recursive: true,
|
|
131
|
+
withFileTypes: true
|
|
132
|
+
});
|
|
133
|
+
} catch (err) {
|
|
134
|
+
throw new Error(`Client build produced no output at ${clientDir}: ${errorMessage(err)}`, { cause: err });
|
|
135
|
+
}
|
|
138
136
|
await Promise.all(entries.filter((entry) => entry.isFile()).map(async (entry) => {
|
|
139
137
|
const abs = path.join(entry.parentPath, entry.name);
|
|
140
138
|
const rel = path.relative(clientDir, abs).split(path.sep).join("/");
|
package/dist/client-bundler.mjs
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
//#region _delete.ts
|
|
2
|
+
import { n as log, u as ok } from "./_ui-CKEIHAtB.mjs";
|
|
3
|
+
import { n as getServerInfo } from "./_agent-Ba5Ykp05.mjs";
|
|
4
|
+
import { t as apiRequest } from "./_api-client-a6cPMebU.mjs";
|
|
5
|
+
//#region delete.ts
|
|
7
6
|
async function runDelete(opts) {
|
|
8
7
|
await apiRequest(`${opts.url}/${opts.slug}`, {
|
|
9
8
|
method: "DELETE",
|
|
@@ -13,8 +12,6 @@ async function runDelete(opts) {
|
|
|
13
12
|
...opts.fetch ? { fetch: opts.fetch } : {}
|
|
14
13
|
});
|
|
15
14
|
}
|
|
16
|
-
//#endregion
|
|
17
|
-
//#region delete.ts
|
|
18
15
|
/** Execute delete and return structured result. */
|
|
19
16
|
async function executeDelete(opts) {
|
|
20
17
|
const { cwd } = opts;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { t as apiRequest } from "./_api-client-
|
|
2
|
+
import { n as log, t as fmtUrl, u as ok } from "./_ui-CKEIHAtB.mjs";
|
|
3
|
+
import { r as errorMessage } from "./_utils-ECl2je7-.mjs";
|
|
4
|
+
import { buildAgentBundle } from "./_bundler-CKz9Q15i.mjs";
|
|
5
|
+
import { o as writeProjectConfig } from "./_config-BMjZt5hG.mjs";
|
|
6
|
+
import { t as resolveServerEnv } from "./_server-common-Bv-4Rskq.mjs";
|
|
7
|
+
import { i as resolveDeployTarget } from "./_agent-Ba5Ykp05.mjs";
|
|
8
|
+
import { t as apiRequest } from "./_api-client-a6cPMebU.mjs";
|
|
9
9
|
import { gzipSync } from "node:zlib";
|
|
10
10
|
//#region _deploy.ts
|
|
11
11
|
async function runDeploy(opts) {
|
|
@@ -26,6 +26,8 @@ async function runDeploy(opts) {
|
|
|
26
26
|
apiKey: opts.apiKey,
|
|
27
27
|
action: "deploy",
|
|
28
28
|
hints: { 413: "Your bundle is too large. Try reducing dependencies or splitting your agent." },
|
|
29
|
+
...opts.slug ? {} : { retry: 0 },
|
|
30
|
+
...opts.retryDelay !== void 0 ? { retryDelay: opts.retryDelay } : {},
|
|
29
31
|
...opts.fetch ? { fetch: opts.fetch } : {}
|
|
30
32
|
})).slug };
|
|
31
33
|
}
|
|
@@ -42,17 +44,25 @@ async function executeDeploy(opts) {
|
|
|
42
44
|
url: serverUrl,
|
|
43
45
|
bundle,
|
|
44
46
|
env: {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
ASSEMBLYAI_API_KEY: apiKey,
|
|
48
|
+
...env
|
|
47
49
|
},
|
|
48
50
|
...slug ? { slug } : {},
|
|
49
51
|
apiKey
|
|
50
52
|
});
|
|
51
|
-
await writeProjectConfig(cwd, {
|
|
52
|
-
slug: deployed.slug,
|
|
53
|
-
serverUrl
|
|
54
|
-
});
|
|
55
53
|
const agentUrl = `${serverUrl}/${deployed.slug}`;
|
|
54
|
+
try {
|
|
55
|
+
await writeProjectConfig(cwd, {
|
|
56
|
+
slug: deployed.slug,
|
|
57
|
+
serverUrl
|
|
58
|
+
});
|
|
59
|
+
} catch (err) {
|
|
60
|
+
log.warn(`Deployed as ${deployed.slug}, but couldn't save .aai/project.json: ${errorMessage(err)}\n Write it manually so future deploys reuse this slug:
|
|
61
|
+
${JSON.stringify({
|
|
62
|
+
slug: deployed.slug,
|
|
63
|
+
serverUrl
|
|
64
|
+
})}`);
|
|
65
|
+
}
|
|
56
66
|
log.success(`Deployed ${fmtUrl(agentUrl)}`);
|
|
57
67
|
return ok({
|
|
58
68
|
slug: deployed.slug,
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
4
|
-
import { t as errorDetail } from "./_utils-BeU10C7O.mjs";
|
|
2
|
+
import { n as log, r as parsePort, t as fmtUrl, u as ok } from "./_ui-CKEIHAtB.mjs";
|
|
3
|
+
import { n as errorDetail } from "./_utils-ECl2je7-.mjs";
|
|
5
4
|
import path from "node:path";
|
|
6
|
-
import
|
|
5
|
+
import pc from "picocolors";
|
|
7
6
|
//#region dev.ts
|
|
8
7
|
/**
|
|
9
8
|
* Start the dev server and return the result.
|
|
@@ -12,19 +11,27 @@ import { colorize } from "consola/utils";
|
|
|
12
11
|
async function executeDev(opts) {
|
|
13
12
|
const port = parsePort(opts.port);
|
|
14
13
|
const agentName = path.basename(path.resolve(opts.cwd));
|
|
15
|
-
const { startDevServer } = await import("./_dev-server-
|
|
16
|
-
|
|
14
|
+
const { startDevServer } = await import("./_dev-server-DZl-xNwH.mjs");
|
|
15
|
+
let cleanup;
|
|
16
|
+
let shuttingDown = false;
|
|
17
|
+
const onSignal = () => {
|
|
18
|
+
if (shuttingDown) return;
|
|
19
|
+
shuttingDown = true;
|
|
20
|
+
if (!cleanup) process.exit(130);
|
|
21
|
+
cleanup().then(() => process.exit(0), (err) => {
|
|
22
|
+
log.error(`Shutdown failed: ${errorDetail(err)}`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
process.on("SIGINT", onSignal);
|
|
27
|
+
process.on("SIGTERM", onSignal);
|
|
28
|
+
cleanup = await startDevServer({
|
|
17
29
|
cwd: opts.cwd,
|
|
18
30
|
port
|
|
19
31
|
});
|
|
20
32
|
const url = `http://localhost:${port}`;
|
|
21
|
-
log.success(`${
|
|
33
|
+
log.success(`${pc.bold(agentName)} running at ${fmtUrl(url)}`);
|
|
22
34
|
log.info("Press Ctrl-C to stop");
|
|
23
|
-
const onSignal = () => {
|
|
24
|
-
cleanup().finally(() => process.exit(0));
|
|
25
|
-
};
|
|
26
|
-
process.on("SIGINT", onSignal);
|
|
27
|
-
process.on("SIGTERM", onSignal);
|
|
28
35
|
process.on("unhandledRejection", (err) => {
|
|
29
36
|
log.error(`Unhandled rejection: ${errorDetail(err)}`);
|
|
30
37
|
});
|