@agentuity/cli 0.0.70 → 0.0.72
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/cmd/auth/api.d.ts.map +1 -1
- package/dist/cmd/auth/api.js +1 -1
- package/dist/cmd/auth/api.js.map +1 -1
- package/dist/cmd/build/bundler.d.ts +4 -3
- package/dist/cmd/build/bundler.d.ts.map +1 -1
- package/dist/cmd/build/bundler.js +104 -10
- package/dist/cmd/build/bundler.js.map +1 -1
- package/dist/cmd/build/index.d.ts.map +1 -1
- package/dist/cmd/build/index.js +1 -0
- package/dist/cmd/build/index.js.map +1 -1
- package/dist/cmd/cloud/db/create.js +2 -2
- package/dist/cmd/cloud/db/create.js.map +1 -1
- package/dist/cmd/cloud/db/delete.js +2 -2
- package/dist/cmd/cloud/db/delete.js.map +1 -1
- package/dist/cmd/cloud/db/get.js +2 -2
- package/dist/cmd/cloud/db/get.js.map +1 -1
- package/dist/cmd/cloud/db/list.js +2 -2
- package/dist/cmd/cloud/db/list.js.map +1 -1
- package/dist/cmd/cloud/db/logs.js +2 -2
- package/dist/cmd/cloud/db/logs.js.map +1 -1
- package/dist/cmd/cloud/db/sql.js +2 -2
- package/dist/cmd/cloud/db/sql.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +164 -24
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/cloud/session/get.js +2 -2
- package/dist/cmd/cloud/session/get.js.map +1 -1
- package/dist/cmd/cloud/session/list.js +2 -2
- package/dist/cmd/cloud/session/list.js.map +1 -1
- package/dist/cmd/cloud/storage/create.js +2 -2
- package/dist/cmd/cloud/storage/create.js.map +1 -1
- package/dist/cmd/cloud/storage/delete.js +2 -2
- package/dist/cmd/cloud/storage/delete.js.map +1 -1
- package/dist/cmd/cloud/storage/download.js +2 -2
- package/dist/cmd/cloud/storage/download.js.map +1 -1
- package/dist/cmd/cloud/storage/get.js +2 -2
- package/dist/cmd/cloud/storage/get.js.map +1 -1
- package/dist/cmd/cloud/storage/list.js +2 -2
- package/dist/cmd/cloud/storage/list.js.map +1 -1
- package/dist/cmd/cloud/storage/upload.js +2 -2
- package/dist/cmd/cloud/storage/upload.js.map +1 -1
- package/dist/cmd/cloud/thread/delete.js +2 -2
- package/dist/cmd/cloud/thread/delete.js.map +1 -1
- package/dist/cmd/cloud/thread/get.js +2 -2
- package/dist/cmd/cloud/thread/get.js.map +1 -1
- package/dist/cmd/cloud/thread/list.js +2 -2
- package/dist/cmd/cloud/thread/list.js.map +1 -1
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +1 -0
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/config.d.ts +2 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -1
- package/dist/config.js.map +1 -1
- package/dist/env-util.d.ts.map +1 -1
- package/dist/env-util.js +0 -3
- package/dist/env-util.js.map +1 -1
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +5 -1
- package/dist/output.js.map +1 -1
- package/dist/tui.d.ts +27 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +140 -33
- package/dist/tui.js.map +1 -1
- package/package.json +3 -3
- package/src/cmd/auth/api.ts +1 -5
- package/src/cmd/build/bundler.ts +138 -13
- package/src/cmd/build/index.ts +1 -0
- package/src/cmd/cloud/db/create.ts +2 -2
- package/src/cmd/cloud/db/delete.ts +2 -2
- package/src/cmd/cloud/db/get.ts +2 -2
- package/src/cmd/cloud/db/list.ts +2 -2
- package/src/cmd/cloud/db/logs.ts +2 -2
- package/src/cmd/cloud/db/sql.ts +2 -2
- package/src/cmd/cloud/deploy.ts +188 -24
- package/src/cmd/cloud/session/get.ts +2 -2
- package/src/cmd/cloud/session/list.ts +2 -2
- package/src/cmd/cloud/storage/create.ts +2 -2
- package/src/cmd/cloud/storage/delete.ts +2 -2
- package/src/cmd/cloud/storage/download.ts +2 -2
- package/src/cmd/cloud/storage/get.ts +2 -2
- package/src/cmd/cloud/storage/list.ts +2 -2
- package/src/cmd/cloud/storage/upload.ts +2 -2
- package/src/cmd/cloud/thread/delete.ts +2 -2
- package/src/cmd/cloud/thread/get.ts +2 -2
- package/src/cmd/cloud/thread/list.ts +2 -2
- package/src/cmd/dev/index.ts +1 -0
- package/src/config.ts +9 -6
- package/src/env-util.ts +0 -3
- package/src/output.ts +7 -1
- package/src/tui.ts +192 -34
package/src/cmd/build/bundler.ts
CHANGED
|
@@ -1,21 +1,42 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $, semver } from 'bun';
|
|
2
2
|
import { join, relative, resolve, dirname, basename } from 'node:path';
|
|
3
|
-
import { cpSync, existsSync, mkdirSync, rmSync } from 'node:fs';
|
|
3
|
+
import { cpSync, existsSync, mkdirSync, rmSync, readdirSync } from 'node:fs';
|
|
4
4
|
import gitParseUrl from 'git-url-parse';
|
|
5
|
+
import { StructuredError } from '@agentuity/core';
|
|
6
|
+
import * as tui from '../../tui';
|
|
5
7
|
import AgentuityBundler, { getBuildMetadata } from './plugin';
|
|
6
8
|
import { getFilesRecursively } from './file';
|
|
7
9
|
import { getVersion } from '../../version';
|
|
8
10
|
import type { Project } from '../../types';
|
|
9
11
|
import { fixDuplicateExportsInDirectory } from './fix-duplicate-exports';
|
|
10
|
-
import {
|
|
11
|
-
import type { LogLevel } from '../../types';
|
|
12
|
+
import type { Logger } from '../../types';
|
|
12
13
|
import { generateWorkbenchMainTsx, generateWorkbenchIndexHtml } from './workbench-templates';
|
|
13
14
|
import { analyzeWorkbench } from './ast';
|
|
14
|
-
import {
|
|
15
|
-
|
|
15
|
+
import { type DeployOptions } from '../../schemas/deploy';
|
|
16
|
+
|
|
17
|
+
const minBunVersion = '>=1.3.3';
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
async function checkBunVersion() {
|
|
20
|
+
if (semver.satisfies(Bun.version, minBunVersion)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const message = `The current Bun installed is using version ${Bun.version}. This project requires Bun version ${minBunVersion} to build.`;
|
|
24
|
+
tui.warning(message);
|
|
25
|
+
if (process.stdin.isTTY) {
|
|
26
|
+
const ok = await tui.confirm('You would you to upgrade now?');
|
|
27
|
+
if (ok) {
|
|
28
|
+
await $`bun upgrade`.quiet();
|
|
29
|
+
const version = (await $`bun -v`.quiet().text()).trim();
|
|
30
|
+
tui.success(`Bun upgraded to ${version}`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
throw new InvalidBunVersion({
|
|
35
|
+
current: Bun.version,
|
|
36
|
+
required: minBunVersion,
|
|
37
|
+
message: `Please see https://bun.sh/ for information on how to download the latest version.`,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
19
40
|
|
|
20
41
|
export interface BundleOptions extends DeployOptions {
|
|
21
42
|
rootDir: string;
|
|
@@ -28,6 +49,7 @@ export interface BundleOptions extends DeployOptions {
|
|
|
28
49
|
port?: number;
|
|
29
50
|
outDir?: string;
|
|
30
51
|
region: string;
|
|
52
|
+
logger: Logger;
|
|
31
53
|
}
|
|
32
54
|
|
|
33
55
|
type BuildResult = Awaited<ReturnType<typeof Bun.build>>;
|
|
@@ -36,6 +58,10 @@ type BuildLogs = BuildResult['logs'];
|
|
|
36
58
|
const AppFileNotFoundError = StructuredError('AppFileNotFoundError');
|
|
37
59
|
const AgentsDirNotFoundError = StructuredError('AgentsDirNotFoundError');
|
|
38
60
|
const BuildFailedError = StructuredError('BuildFailedError')<{ logs?: BuildLogs }>();
|
|
61
|
+
const InvalidBunVersion = StructuredError('InvalidBunVersion')<{
|
|
62
|
+
current: string;
|
|
63
|
+
required: string;
|
|
64
|
+
}>();
|
|
39
65
|
|
|
40
66
|
const handleBuildFailure = (buildResult: BuildResult) => {
|
|
41
67
|
// Collect all build errors with full details
|
|
@@ -75,6 +101,7 @@ export async function bundle({
|
|
|
75
101
|
message,
|
|
76
102
|
env,
|
|
77
103
|
region,
|
|
104
|
+
logger,
|
|
78
105
|
}: BundleOptions) {
|
|
79
106
|
const appFile = join(rootDir, 'app.ts');
|
|
80
107
|
if (!existsSync(appFile)) {
|
|
@@ -82,6 +109,9 @@ export async function bundle({
|
|
|
82
109
|
message: `App file not found at expected location: ${appFile}`,
|
|
83
110
|
});
|
|
84
111
|
}
|
|
112
|
+
|
|
113
|
+
await checkBunVersion();
|
|
114
|
+
|
|
85
115
|
const outDir = customOutDir ?? join(rootDir, '.agentuity');
|
|
86
116
|
const srcDir = join(rootDir, 'src');
|
|
87
117
|
|
|
@@ -150,6 +180,98 @@ export async function bundle({
|
|
|
150
180
|
}
|
|
151
181
|
}
|
|
152
182
|
|
|
183
|
+
// Common externals for native modules (same as legacy CLI)
|
|
184
|
+
const commonExternals = ['bun', 'fsevents', 'chromium-bidi', 'sharp'];
|
|
185
|
+
|
|
186
|
+
// Allow projects to specify custom externals via package.json "externals" field
|
|
187
|
+
const customExternals: string[] = [];
|
|
188
|
+
if (pkgContents.externals && Array.isArray(pkgContents.externals)) {
|
|
189
|
+
customExternals.push(...pkgContents.externals.filter((e: unknown) => typeof e === 'string'));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const externalPatterns = [...commonExternals, ...customExternals];
|
|
193
|
+
|
|
194
|
+
// For production builds: install externals FIRST, then discover full dependency tree
|
|
195
|
+
// This prevents bundling dependencies that will be in node_modules anyway
|
|
196
|
+
let external = externalPatterns;
|
|
197
|
+
if (!dev) {
|
|
198
|
+
logger.debug('Installing externalized packages to discover full dependency tree...');
|
|
199
|
+
|
|
200
|
+
// Step 1: Collect packages matching external patterns
|
|
201
|
+
const externalInstalls: string[] = [];
|
|
202
|
+
for (const pattern of externalPatterns) {
|
|
203
|
+
if (pattern.endsWith('/*')) {
|
|
204
|
+
const prefix = pattern.slice(0, -2);
|
|
205
|
+
const nmDir = join(rootDir, 'node_modules', prefix);
|
|
206
|
+
if (existsSync(nmDir)) {
|
|
207
|
+
const entries = readdirSync(nmDir);
|
|
208
|
+
for (const entry of entries) {
|
|
209
|
+
const pkgName = `${prefix}/${entry}`;
|
|
210
|
+
if (existsSync(join(rootDir, 'node_modules', pkgName))) {
|
|
211
|
+
externalInstalls.push(pkgName);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
if (existsSync(join(rootDir, 'node_modules', pattern))) {
|
|
217
|
+
externalInstalls.push(pattern);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Step 2: Write minimal package.json and install externals
|
|
223
|
+
if (externalInstalls.length > 0) {
|
|
224
|
+
await Bun.write(
|
|
225
|
+
`${outDir}/package.json`,
|
|
226
|
+
JSON.stringify({ name: pkgContents.name, version: pkgContents.version }, null, 2)
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
logger.debug(
|
|
230
|
+
'Installing %d packages: %s',
|
|
231
|
+
externalInstalls.length,
|
|
232
|
+
externalInstalls.join(', ')
|
|
233
|
+
);
|
|
234
|
+
await $`bun install --no-save --ignore-scripts --target=bun-linux-x64 ${externalInstalls}`
|
|
235
|
+
.cwd(outDir)
|
|
236
|
+
.quiet();
|
|
237
|
+
|
|
238
|
+
// Step 3: Scan what actually got installed (includes transitive dependencies)
|
|
239
|
+
const installedNmDir = join(outDir, 'node_modules');
|
|
240
|
+
if (existsSync(installedNmDir)) {
|
|
241
|
+
const allInstalled: string[] = [];
|
|
242
|
+
|
|
243
|
+
// Recursively find all installed packages
|
|
244
|
+
const scanDir = (dir: string, prefix = '') => {
|
|
245
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
246
|
+
for (const entry of entries) {
|
|
247
|
+
if (entry.isDirectory()) {
|
|
248
|
+
const pkgName = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
249
|
+
|
|
250
|
+
// Check if this is a package (has package.json)
|
|
251
|
+
if (existsSync(join(dir, entry.name, 'package.json'))) {
|
|
252
|
+
allInstalled.push(pkgName);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Recurse into scoped packages (@org/package)
|
|
256
|
+
if (entry.name.startsWith('@')) {
|
|
257
|
+
scanDir(join(dir, entry.name), entry.name);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
scanDir(installedNmDir);
|
|
264
|
+
logger.debug(
|
|
265
|
+
'Discovered %d total packages (including dependencies)',
|
|
266
|
+
allInstalled.length
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
// Step 4: Use ALL installed packages as externals for bundling
|
|
270
|
+
external = allInstalled;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
153
275
|
await (async () => {
|
|
154
276
|
const config: Bun.BuildConfig = {
|
|
155
277
|
entrypoints: appEntrypoints,
|
|
@@ -168,6 +290,7 @@ export async function bundle({
|
|
|
168
290
|
drop: isProd ? ['debugger'] : undefined,
|
|
169
291
|
splitting: true,
|
|
170
292
|
conditions: [isProd ? 'production' : 'development', 'bun'],
|
|
293
|
+
external,
|
|
171
294
|
naming: {
|
|
172
295
|
entry: '[dir]/[name].[ext]',
|
|
173
296
|
chunk: 'chunk/[name]-[hash].[ext]',
|
|
@@ -328,7 +451,6 @@ export async function bundle({
|
|
|
328
451
|
// Create workbench config with proper defaults
|
|
329
452
|
const defaultConfig = { route: '/workbench', headers: {}, port: port || 3500 };
|
|
330
453
|
const config = { ...defaultConfig, ...analysis.config };
|
|
331
|
-
const logger = createLogger((process.env.AGENTUITY_LOG_LEVEL as LogLevel) || 'info');
|
|
332
454
|
try {
|
|
333
455
|
// Generate workbench files on the fly instead of using files from package
|
|
334
456
|
const tempWorkbenchDir = join(outDir, 'temp-workbench');
|
|
@@ -569,10 +691,13 @@ export async function bundle({
|
|
|
569
691
|
}
|
|
570
692
|
}
|
|
571
693
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
694
|
+
// Write minimal package.json for dev mode (production already wrote it above)
|
|
695
|
+
if (dev) {
|
|
696
|
+
await Bun.write(
|
|
697
|
+
`${outDir}/package.json`,
|
|
698
|
+
JSON.stringify({ name: pkgContents.name, version: pkgContents.version }, null, 2)
|
|
699
|
+
);
|
|
700
|
+
}
|
|
576
701
|
|
|
577
702
|
await Bun.write(
|
|
578
703
|
`${outDir}/agentuity.metadata.json`,
|
package/src/cmd/build/index.ts
CHANGED
|
@@ -31,7 +31,7 @@ export const createSubcommand = defineSubcommand({
|
|
|
31
31
|
},
|
|
32
32
|
|
|
33
33
|
async handler(ctx) {
|
|
34
|
-
const { logger, opts, orgId, region,
|
|
34
|
+
const { logger, opts, orgId, region, auth, options } = ctx;
|
|
35
35
|
|
|
36
36
|
// Handle dry-run mode
|
|
37
37
|
if (isDryRunMode(options)) {
|
|
@@ -49,7 +49,7 @@ export const createSubcommand = defineSubcommand({
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
const catalystClient = getCatalystAPIClient(
|
|
52
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
53
53
|
|
|
54
54
|
try {
|
|
55
55
|
const created = await tui.spinner({
|
|
@@ -35,9 +35,9 @@ export const deleteSubcommand = createSubcommand({
|
|
|
35
35
|
},
|
|
36
36
|
|
|
37
37
|
async handler(ctx) {
|
|
38
|
-
const { logger, args, opts,
|
|
38
|
+
const { logger, args, opts, orgId, region, auth, options } = ctx;
|
|
39
39
|
|
|
40
|
-
const catalystClient = getCatalystAPIClient(
|
|
40
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
41
41
|
|
|
42
42
|
let dbName = args.name;
|
|
43
43
|
|
package/src/cmd/cloud/db/get.ts
CHANGED
|
@@ -65,9 +65,9 @@ export const getSubcommand = createSubcommand({
|
|
|
65
65
|
},
|
|
66
66
|
|
|
67
67
|
async handler(ctx) {
|
|
68
|
-
const { logger, args, opts, options, orgId, region,
|
|
68
|
+
const { logger, args, opts, options, orgId, region, auth } = ctx;
|
|
69
69
|
|
|
70
|
-
const catalystClient = getCatalystAPIClient(
|
|
70
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
71
71
|
|
|
72
72
|
const resources = await tui.spinner({
|
|
73
73
|
message: `Fetching database ${args.name}`,
|
package/src/cmd/cloud/db/list.ts
CHANGED
|
@@ -46,9 +46,9 @@ export const listSubcommand = createSubcommand({
|
|
|
46
46
|
},
|
|
47
47
|
|
|
48
48
|
async handler(ctx) {
|
|
49
|
-
const { logger, opts, options, orgId, region,
|
|
49
|
+
const { logger, opts, options, orgId, region, auth } = ctx;
|
|
50
50
|
|
|
51
|
-
const catalystClient = getCatalystAPIClient(
|
|
51
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
52
52
|
|
|
53
53
|
const resources = await tui.spinner({
|
|
54
54
|
message: `Fetching databases for ${orgId} in ${region}`,
|
package/src/cmd/cloud/db/logs.ts
CHANGED
|
@@ -78,14 +78,14 @@ export const logsSubcommand = createSubcommand({
|
|
|
78
78
|
response: DbLogsResponseSchema,
|
|
79
79
|
},
|
|
80
80
|
async handler(ctx) {
|
|
81
|
-
const { args, options, orgId, region,
|
|
81
|
+
const { args, options, orgId, region, logger, auth } = ctx;
|
|
82
82
|
const showTimestamps = ctx.opts.timestamps ?? true;
|
|
83
83
|
const showSessionId = ctx.opts.showSessionId ?? false;
|
|
84
84
|
const showUsername = ctx.opts.showUsername ?? false;
|
|
85
85
|
const prettySQL = ctx.opts.pretty ?? false;
|
|
86
86
|
|
|
87
87
|
try {
|
|
88
|
-
const catalystClient = getCatalystAPIClient(
|
|
88
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
89
89
|
|
|
90
90
|
const logs = await dbLogs(catalystClient, {
|
|
91
91
|
database: args.database,
|
package/src/cmd/cloud/db/sql.ts
CHANGED
|
@@ -42,9 +42,9 @@ export const sqlSubcommand = createSubcommand({
|
|
|
42
42
|
},
|
|
43
43
|
|
|
44
44
|
async handler(ctx) {
|
|
45
|
-
const { logger, args, options, orgId, region,
|
|
45
|
+
const { logger, args, options, orgId, region, auth } = ctx;
|
|
46
46
|
|
|
47
|
-
const catalystClient = getCatalystAPIClient(
|
|
47
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
48
48
|
|
|
49
49
|
const result = await tui.spinner({
|
|
50
50
|
message: `Executing query on ${args.name}`,
|
package/src/cmd/cloud/deploy.ts
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
import { createPublicKey } from 'node:crypto';
|
|
4
|
-
import { createReadStream, createWriteStream } from 'node:fs';
|
|
4
|
+
import { createReadStream, createWriteStream, existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
5
5
|
import { tmpdir } from 'node:os';
|
|
6
6
|
import { createSubcommand } from '../../types';
|
|
7
7
|
import * as tui from '../../tui';
|
|
8
|
-
import { saveProjectDir } from '../../config';
|
|
8
|
+
import { saveProjectDir, getDefaultConfigDir } from '../../config';
|
|
9
9
|
import { runSteps, stepSuccess, stepSkipped, stepError, Step, ProgressCallback } from '../../steps';
|
|
10
10
|
import { bundle } from '../build/bundler';
|
|
11
|
-
import { loadBuildMetadata } from '../../config';
|
|
11
|
+
import { loadBuildMetadata, getStreamURL } from '../../config';
|
|
12
12
|
import {
|
|
13
13
|
projectEnvUpdate,
|
|
14
14
|
projectDeploymentCreate,
|
|
15
15
|
projectDeploymentUpdate,
|
|
16
16
|
projectDeploymentComplete,
|
|
17
|
+
projectDeploymentStatus,
|
|
17
18
|
type Deployment,
|
|
18
19
|
type BuildMetadata,
|
|
19
20
|
type DeploymentInstructions,
|
|
20
21
|
type DeploymentComplete,
|
|
22
|
+
type DeploymentStatusResult,
|
|
21
23
|
} from '@agentuity/server';
|
|
22
24
|
import {
|
|
23
25
|
findEnvFile,
|
|
@@ -30,11 +32,13 @@ import { encryptFIPSKEMDEMStream } from '../../crypto/box';
|
|
|
30
32
|
import { getCommand } from '../../command-prefix';
|
|
31
33
|
import { checkCustomDomainForDNS } from './domain';
|
|
32
34
|
import { DeployOptionsSchema } from '../../schemas/deploy';
|
|
35
|
+
import { ErrorCode } from '../../errors';
|
|
33
36
|
|
|
34
37
|
const DeployResponseSchema = z.object({
|
|
35
38
|
success: z.boolean().describe('Whether deployment succeeded'),
|
|
36
39
|
deploymentId: z.string().describe('Deployment ID'),
|
|
37
40
|
projectId: z.string().describe('Project ID'),
|
|
41
|
+
logs: z.array(z.string()).optional().describe('The deployment startup logs'),
|
|
38
42
|
urls: z
|
|
39
43
|
.object({
|
|
40
44
|
deployment: z.string().describe('Deployment-specific URL'),
|
|
@@ -77,12 +81,14 @@ export const deploySubcommand = createSubcommand({
|
|
|
77
81
|
},
|
|
78
82
|
|
|
79
83
|
async handler(ctx) {
|
|
80
|
-
const { project, apiClient, projectDir, config, options, opts } = ctx;
|
|
84
|
+
const { project, apiClient, projectDir, config, options, opts, logger } = ctx;
|
|
81
85
|
|
|
82
86
|
let deployment: Deployment | undefined;
|
|
83
87
|
let build: BuildMetadata | undefined;
|
|
84
88
|
let instructions: DeploymentInstructions | undefined;
|
|
85
89
|
let complete: DeploymentComplete | undefined;
|
|
90
|
+
let statusResult: DeploymentStatusResult | undefined;
|
|
91
|
+
const logs: string[] = [];
|
|
86
92
|
|
|
87
93
|
try {
|
|
88
94
|
await saveProjectDir(projectDir);
|
|
@@ -187,6 +193,7 @@ export const deploySubcommand = createSubcommand({
|
|
|
187
193
|
trigger: opts?.trigger,
|
|
188
194
|
tag: opts?.tag,
|
|
189
195
|
region: project.region,
|
|
196
|
+
logger: ctx.logger,
|
|
190
197
|
});
|
|
191
198
|
build = await loadBuildMetadata(join(projectDir, '.agentuity'));
|
|
192
199
|
instructions = await projectDeploymentUpdate(
|
|
@@ -236,7 +243,6 @@ export const deploySubcommand = createSubcommand({
|
|
|
236
243
|
if (relative.startsWith('.env')) return false;
|
|
237
244
|
if (relative.startsWith('.git/')) return false;
|
|
238
245
|
if (relative.startsWith('.ssh/')) return false;
|
|
239
|
-
if (relative.startsWith('node_modules/')) return false;
|
|
240
246
|
if (relative === '.DS_Store') return false;
|
|
241
247
|
return true;
|
|
242
248
|
},
|
|
@@ -366,9 +372,174 @@ export const deploySubcommand = createSubcommand({
|
|
|
366
372
|
].filter(Boolean) as Step[],
|
|
367
373
|
options.logLevel
|
|
368
374
|
);
|
|
369
|
-
tui.success('Your project was deployed!');
|
|
370
375
|
|
|
371
|
-
if (
|
|
376
|
+
if (!deployment) {
|
|
377
|
+
return {
|
|
378
|
+
success: false,
|
|
379
|
+
deploymentId: '',
|
|
380
|
+
projectId: project.projectId,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const streamId = complete?.streamId;
|
|
385
|
+
|
|
386
|
+
// Poll for deployment status with optional log streaming
|
|
387
|
+
const pollInterval = 500;
|
|
388
|
+
const maxAttempts = 600;
|
|
389
|
+
let attempts = 0;
|
|
390
|
+
|
|
391
|
+
if (streamId) {
|
|
392
|
+
// Use progress logger to stream logs while polling
|
|
393
|
+
const streamsUrl = getStreamURL(project.region, config);
|
|
394
|
+
|
|
395
|
+
await tui
|
|
396
|
+
.progress({
|
|
397
|
+
message: 'Deploying project...',
|
|
398
|
+
type: 'logger',
|
|
399
|
+
maxLines: 2,
|
|
400
|
+
clearOnSuccess: true,
|
|
401
|
+
callback: async (log) => {
|
|
402
|
+
// Start log streaming
|
|
403
|
+
const logStreamController = new AbortController();
|
|
404
|
+
const logStreamPromise = (async () => {
|
|
405
|
+
try {
|
|
406
|
+
logger.debug('fetching stream: %s/%s', streamsUrl, streamId);
|
|
407
|
+
const resp = await fetch(`${streamsUrl}/${streamId}`, {
|
|
408
|
+
signal: logStreamController.signal,
|
|
409
|
+
});
|
|
410
|
+
if (!resp.ok || !resp.body) {
|
|
411
|
+
ctx.logger.trace(
|
|
412
|
+
`Failed to connect to warmup log stream: ${resp.status}`
|
|
413
|
+
);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
const reader = resp.body.getReader();
|
|
417
|
+
const decoder = new TextDecoder();
|
|
418
|
+
let buffer = '';
|
|
419
|
+
while (true) {
|
|
420
|
+
const { done, value } = await reader.read();
|
|
421
|
+
if (done) break;
|
|
422
|
+
buffer += decoder.decode(value, { stream: true });
|
|
423
|
+
const lines = buffer.split('\n');
|
|
424
|
+
buffer = lines.pop() || ''; // Keep incomplete line in buffer
|
|
425
|
+
for (const line of lines) {
|
|
426
|
+
// Strip ISO 8601 timestamp prefix if present
|
|
427
|
+
const message = line.replace(
|
|
428
|
+
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z\s?/,
|
|
429
|
+
''
|
|
430
|
+
);
|
|
431
|
+
if (message) {
|
|
432
|
+
logs.push(message);
|
|
433
|
+
log(message);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
} catch (err) {
|
|
438
|
+
if (err instanceof Error && err.name === 'AbortError') {
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
ctx.logger.trace(`Warmup log stream error: ${err}`);
|
|
442
|
+
}
|
|
443
|
+
})();
|
|
444
|
+
|
|
445
|
+
// Poll for deployment status
|
|
446
|
+
while (attempts < maxAttempts) {
|
|
447
|
+
attempts++;
|
|
448
|
+
try {
|
|
449
|
+
statusResult = await projectDeploymentStatus(
|
|
450
|
+
apiClient,
|
|
451
|
+
deployment?.id ?? ''
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
logger.trace('status result: %s', statusResult);
|
|
455
|
+
|
|
456
|
+
if (statusResult.state === 'completed') {
|
|
457
|
+
logStreamController.abort();
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (statusResult.state === 'failed') {
|
|
462
|
+
throw new Error('Deployment failed');
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
await Bun.sleep(pollInterval);
|
|
466
|
+
} catch (err) {
|
|
467
|
+
logStreamController.abort();
|
|
468
|
+
throw err;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Wait for log stream to finish
|
|
473
|
+
await logStreamPromise;
|
|
474
|
+
|
|
475
|
+
if (attempts >= maxAttempts) {
|
|
476
|
+
throw new Error('Deployment timed out');
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
})
|
|
480
|
+
.then(() => {
|
|
481
|
+
tui.success('Your project was deployed!');
|
|
482
|
+
})
|
|
483
|
+
.catch((ex) => {
|
|
484
|
+
const exwithmessage = ex as { message: string };
|
|
485
|
+
const msg =
|
|
486
|
+
exwithmessage.message === 'Deployment failed' ? '' : exwithmessage.toString();
|
|
487
|
+
tui.error(`Your deployment failed to start${msg ? `: ${msg}` : ''}`);
|
|
488
|
+
if (logs.length) {
|
|
489
|
+
const logsDir = join(getDefaultConfigDir(), 'logs');
|
|
490
|
+
if (!existsSync(logsDir)) {
|
|
491
|
+
mkdirSync(logsDir, { recursive: true });
|
|
492
|
+
}
|
|
493
|
+
const errorFile = join(logsDir, `${deployment?.id ?? Date.now()}.txt`);
|
|
494
|
+
writeFileSync(errorFile, logs.join('\n'));
|
|
495
|
+
const count = Math.min(logs.length, 10);
|
|
496
|
+
const last = logs.length - count;
|
|
497
|
+
tui.newline();
|
|
498
|
+
tui.warning(`The last ${count} lines of the log:`);
|
|
499
|
+
let offset = last + 1; // we want to show the offset from inside the log starting at 1
|
|
500
|
+
const max = String(logs.length).length;
|
|
501
|
+
for (const _log of logs.slice(last)) {
|
|
502
|
+
console.log(tui.muted(`${offset.toFixed().padEnd(max)} | ${_log}`));
|
|
503
|
+
offset++;
|
|
504
|
+
}
|
|
505
|
+
tui.newline();
|
|
506
|
+
tui.fatal(`The logs were written to ${errorFile}`, ErrorCode.BUILD_FAILED);
|
|
507
|
+
}
|
|
508
|
+
tui.fatal('Deployment failed', ErrorCode.BUILD_FAILED);
|
|
509
|
+
});
|
|
510
|
+
} else {
|
|
511
|
+
// No stream ID - poll without log streaming
|
|
512
|
+
await tui.spinner({
|
|
513
|
+
message: 'Deploying project...',
|
|
514
|
+
type: 'simple',
|
|
515
|
+
clearOnSuccess: true,
|
|
516
|
+
callback: async () => {
|
|
517
|
+
while (attempts < maxAttempts) {
|
|
518
|
+
attempts++;
|
|
519
|
+
statusResult = await projectDeploymentStatus(apiClient, deployment?.id ?? '');
|
|
520
|
+
|
|
521
|
+
if (statusResult.state === 'completed') {
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (statusResult.state === 'failed') {
|
|
526
|
+
throw new Error('Deployment failed');
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
await Bun.sleep(pollInterval);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (attempts >= maxAttempts) {
|
|
533
|
+
throw new Error('Deployment timed out');
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
tui.success('Your project was deployed!');
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Show deployment URLs
|
|
542
|
+
if (complete?.publicUrls) {
|
|
372
543
|
tui.arrow(tui.bold(tui.padRight('Deployment ID:', 17)) + tui.link(deployment.id));
|
|
373
544
|
if (complete.publicUrls.custom?.length) {
|
|
374
545
|
for (const url of complete.publicUrls.custom) {
|
|
@@ -385,25 +556,18 @@ export const deploySubcommand = createSubcommand({
|
|
|
385
556
|
}
|
|
386
557
|
}
|
|
387
558
|
|
|
388
|
-
// Return deployment result (if available)
|
|
389
|
-
if (deployment && complete?.publicUrls) {
|
|
390
|
-
return {
|
|
391
|
-
success: true,
|
|
392
|
-
deploymentId: deployment.id,
|
|
393
|
-
projectId: project.projectId,
|
|
394
|
-
urls: {
|
|
395
|
-
deployment: complete.publicUrls.deployment,
|
|
396
|
-
latest: complete.publicUrls.latest,
|
|
397
|
-
custom: complete.publicUrls.custom,
|
|
398
|
-
},
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// Fallback response
|
|
403
559
|
return {
|
|
404
|
-
success:
|
|
405
|
-
deploymentId:
|
|
560
|
+
success: true,
|
|
561
|
+
deploymentId: deployment.id,
|
|
406
562
|
projectId: project.projectId,
|
|
563
|
+
logs,
|
|
564
|
+
urls: complete?.publicUrls
|
|
565
|
+
? {
|
|
566
|
+
deployment: complete.publicUrls.deployment,
|
|
567
|
+
latest: complete.publicUrls.latest,
|
|
568
|
+
custom: complete.publicUrls.custom,
|
|
569
|
+
}
|
|
570
|
+
: undefined,
|
|
407
571
|
};
|
|
408
572
|
} catch (ex) {
|
|
409
573
|
tui.fatal(`unexpected error trying to deploy project. ${ex}`);
|
|
@@ -125,8 +125,8 @@ export const getSubcommand = createSubcommand({
|
|
|
125
125
|
response: SessionGetResponseSchema,
|
|
126
126
|
},
|
|
127
127
|
async handler(ctx) {
|
|
128
|
-
const {
|
|
129
|
-
const catalystClient = getCatalystAPIClient(
|
|
128
|
+
const { logger, auth, args, options, region } = ctx;
|
|
129
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
130
130
|
|
|
131
131
|
try {
|
|
132
132
|
const enriched = await sessionGet(catalystClient, { id: args.session_id });
|
|
@@ -89,8 +89,8 @@ export const listSubcommand = createSubcommand({
|
|
|
89
89
|
response: SessionListResponseSchema,
|
|
90
90
|
},
|
|
91
91
|
async handler(ctx) {
|
|
92
|
-
const {
|
|
93
|
-
const catalystClient = getCatalystAPIClient(
|
|
92
|
+
const { logger, auth, project, opts, options, region } = ctx;
|
|
93
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
94
94
|
|
|
95
95
|
const projectId = opts.projectId || project?.projectId;
|
|
96
96
|
|
|
@@ -35,7 +35,7 @@ export const createSubcommand = defineSubcommand({
|
|
|
35
35
|
},
|
|
36
36
|
|
|
37
37
|
async handler(ctx) {
|
|
38
|
-
const { logger, orgId, region,
|
|
38
|
+
const { logger, orgId, region, auth, options } = ctx;
|
|
39
39
|
|
|
40
40
|
// Handle dry-run mode
|
|
41
41
|
if (isDryRunMode(options)) {
|
|
@@ -51,7 +51,7 @@ export const createSubcommand = defineSubcommand({
|
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
const catalystClient = getCatalystAPIClient(
|
|
54
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
55
55
|
|
|
56
56
|
const created = await tui.spinner({
|
|
57
57
|
message: `Creating storage in ${region}`,
|
|
@@ -49,9 +49,9 @@ export const deleteSubcommand = createSubcommand({
|
|
|
49
49
|
},
|
|
50
50
|
|
|
51
51
|
async handler(ctx) {
|
|
52
|
-
const { logger, args, opts,
|
|
52
|
+
const { logger, args, opts, orgId, region, auth, options } = ctx;
|
|
53
53
|
|
|
54
|
-
const catalystClient = getCatalystAPIClient(
|
|
54
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
55
55
|
|
|
56
56
|
const resources = await tui.spinner({
|
|
57
57
|
message: `Fetching storage for ${orgId} in ${region}`,
|
|
@@ -51,9 +51,9 @@ export const downloadSubcommand = createSubcommand({
|
|
|
51
51
|
},
|
|
52
52
|
|
|
53
53
|
async handler(ctx) {
|
|
54
|
-
const { logger, args, opts, options, orgId, region,
|
|
54
|
+
const { logger, args, opts, options, orgId, region, auth } = ctx;
|
|
55
55
|
|
|
56
|
-
const catalystClient = getCatalystAPIClient(
|
|
56
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
57
57
|
|
|
58
58
|
// Fetch bucket credentials
|
|
59
59
|
const resources = await tui.spinner({
|
|
@@ -51,9 +51,9 @@ export const getSubcommand = createSubcommand({
|
|
|
51
51
|
},
|
|
52
52
|
|
|
53
53
|
async handler(ctx) {
|
|
54
|
-
const { logger, args, opts, options, orgId, region,
|
|
54
|
+
const { logger, args, opts, options, orgId, region, auth } = ctx;
|
|
55
55
|
|
|
56
|
-
const catalystClient = getCatalystAPIClient(
|
|
56
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
57
57
|
|
|
58
58
|
const resources = await tui.spinner({
|
|
59
59
|
message: `Fetching storage bucket ${args.name}`,
|