@agentuity/cli 0.0.98 → 0.0.99
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/banner.d.ts.map +1 -1
- package/dist/banner.js +22 -6
- package/dist/banner.js.map +1 -1
- package/dist/cmd/build/entry-generator.d.ts.map +1 -1
- package/dist/cmd/build/entry-generator.js +84 -61
- package/dist/cmd/build/entry-generator.js.map +1 -1
- package/dist/cmd/build/vite/bun-dev-server.d.ts +3 -7
- package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
- package/dist/cmd/build/vite/bun-dev-server.js +5 -14
- package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
- package/dist/cmd/build/vite/server-bundler.d.ts.map +1 -1
- package/dist/cmd/build/vite/server-bundler.js +11 -1
- package/dist/cmd/build/vite/server-bundler.js.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server-config.js +17 -1
- package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server.js +1 -1
- package/dist/cmd/build/vite/vite-asset-server.js.map +1 -1
- package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +20 -1
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +32 -21
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/dev/file-watcher.d.ts +24 -0
- package/dist/cmd/dev/file-watcher.d.ts.map +1 -0
- package/dist/cmd/dev/file-watcher.js +183 -0
- package/dist/cmd/dev/file-watcher.js.map +1 -0
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +61 -23
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/setup/index.d.ts.map +1 -1
- package/dist/cmd/setup/index.js +3 -3
- package/dist/cmd/setup/index.js.map +1 -1
- package/dist/repl.js +2 -2
- package/dist/repl.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +8 -4
- package/src/banner.ts +25 -6
- package/src/cmd/build/entry-generator.ts +84 -62
- package/src/cmd/build/vite/bun-dev-server.ts +8 -18
- package/src/cmd/build/vite/server-bundler.ts +16 -1
- package/src/cmd/build/vite/vite-asset-server-config.ts +22 -1
- package/src/cmd/build/vite/vite-asset-server.ts +1 -1
- package/src/cmd/build/vite/vite-builder.ts +28 -1
- package/src/cmd/cloud/deploy.ts +41 -24
- package/src/cmd/dev/file-watcher.ts +234 -0
- package/src/cmd/dev/index.ts +69 -24
- package/src/cmd/setup/index.ts +7 -3
- package/src/repl.ts +2 -2
- package/src/types.ts +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentuity/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.99",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"author": "Agentuity employees and contributors",
|
|
6
6
|
"type": "module",
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
"./vite-plugin": {
|
|
18
18
|
"import": "./dist/cmd/build/vite/index.js",
|
|
19
19
|
"types": "./dist/cmd/build/vite/index.d.ts"
|
|
20
|
+
},
|
|
21
|
+
"./runtime-bootstrap": {
|
|
22
|
+
"import": "./dist/runtime-bootstrap.js",
|
|
23
|
+
"types": "./dist/runtime-bootstrap.d.ts"
|
|
20
24
|
}
|
|
21
25
|
},
|
|
22
26
|
"files": [
|
|
@@ -40,8 +44,8 @@
|
|
|
40
44
|
"prepublishOnly": "bun run clean && bun run build"
|
|
41
45
|
},
|
|
42
46
|
"dependencies": {
|
|
43
|
-
"@agentuity/core": "0.0.
|
|
44
|
-
"@agentuity/server": "0.0.
|
|
47
|
+
"@agentuity/core": "0.0.99",
|
|
48
|
+
"@agentuity/server": "0.0.99",
|
|
45
49
|
"@datasert/cronjs-parser": "^1.4.0",
|
|
46
50
|
"@terascope/fetch-github-release": "^2.2.1",
|
|
47
51
|
"@vitejs/plugin-react": "^5.1.2",
|
|
@@ -61,7 +65,7 @@
|
|
|
61
65
|
"zod": "^4.1.12"
|
|
62
66
|
},
|
|
63
67
|
"devDependencies": {
|
|
64
|
-
"@agentuity/test-utils": "0.0.
|
|
68
|
+
"@agentuity/test-utils": "0.0.99",
|
|
65
69
|
"@types/adm-zip": "^0.5.7",
|
|
66
70
|
"@types/bun": "latest",
|
|
67
71
|
"@types/tar-fs": "^2.0.4",
|
package/src/banner.ts
CHANGED
|
@@ -29,10 +29,31 @@ export function generateBanner(version?: string, compact?: true): string {
|
|
|
29
29
|
const versionLabel = ' Version: '; // Include leading space
|
|
30
30
|
const versionLink = LINKS
|
|
31
31
|
? link(getReleaseUrl(_version), _version, WHITE ?? undefined)
|
|
32
|
-
: _version;
|
|
32
|
+
: WHITE + _version + RESET;
|
|
33
33
|
const versionLinkWidth = getDisplayWidth(stripAnsi(versionLink));
|
|
34
34
|
const versionPadding = width - versionLabel.length - versionLinkWidth - 1;
|
|
35
35
|
|
|
36
|
+
const docsLabel = ' Docs: ';
|
|
37
|
+
const docsLink = LINKS
|
|
38
|
+
? link('https://preview.agentuity.dev', 'preview.agentuity.dev', WHITE!)
|
|
39
|
+
: WHITE + 'https://preview.agentuity.dev' + RESET;
|
|
40
|
+
const docsWidth = getDisplayWidth(stripAnsi(docsLink));
|
|
41
|
+
const docsPadding = width - docsLabel.length - docsWidth - 1;
|
|
42
|
+
|
|
43
|
+
const communityLabel = ' Community: ';
|
|
44
|
+
const communityLink = LINKS
|
|
45
|
+
? link('https://discord.gg/agentuity', 'discord.gg/agentuity', WHITE!)
|
|
46
|
+
: WHITE + 'https://discord.gg/agentuity' + RESET;
|
|
47
|
+
const communityWidth = getDisplayWidth(stripAnsi(communityLink));
|
|
48
|
+
const communityPadding = width - communityLabel.length - communityWidth - 1;
|
|
49
|
+
|
|
50
|
+
const dashboardLabel = ' Dashboard: ';
|
|
51
|
+
const dashboardLink = LINKS
|
|
52
|
+
? link('https://app-v1.agentuity.com', 'app-v1.agentuity.com', WHITE!)
|
|
53
|
+
: WHITE + 'https://app-v1.agentuity.com' + RESET;
|
|
54
|
+
const dashboardWidth = getDisplayWidth(stripAnsi(dashboardLink));
|
|
55
|
+
const dashboardPadding = width - dashboardLabel.length - dashboardWidth - 1;
|
|
56
|
+
|
|
36
57
|
const lines = [
|
|
37
58
|
CYAN + '╭────────────────────────────────────────────────────╮' + RESET,
|
|
38
59
|
CYAN + `│ ⨺ Agentuity ${WHITE}The full-stack platform for AI agents${CYAN} │` + RESET,
|
|
@@ -44,18 +65,16 @@ export function generateBanner(version?: string, compact?: true): string {
|
|
|
44
65
|
RESET,
|
|
45
66
|
compact
|
|
46
67
|
? undefined
|
|
47
|
-
: CYAN +
|
|
48
|
-
`│ Docs: ${link('https://preview.agentuity.dev', undefined, WHITE!)}${CYAN} │` +
|
|
49
|
-
RESET,
|
|
68
|
+
: CYAN + `│${docsLabel}${docsLink + ''.padEnd(docsPadding) + CYAN} │` + RESET,
|
|
50
69
|
compact
|
|
51
70
|
? undefined
|
|
52
71
|
: CYAN +
|
|
53
|
-
|
|
72
|
+
`│${communityLabel}${communityLink + ''.padEnd(communityPadding) + CYAN} │` +
|
|
54
73
|
RESET,
|
|
55
74
|
compact
|
|
56
75
|
? undefined
|
|
57
76
|
: CYAN +
|
|
58
|
-
|
|
77
|
+
`│${dashboardLabel}${dashboardLink + ''.padEnd(dashboardPadding) + CYAN} │` +
|
|
59
78
|
RESET,
|
|
60
79
|
CYAN + '╰────────────────────────────────────────────────────╯' + RESET,
|
|
61
80
|
].filter(Boolean) as string[];
|
|
@@ -76,23 +76,13 @@ export async function generateEntryFile(options: GenerateEntryOptions): Promise<
|
|
|
76
76
|
`import { `,
|
|
77
77
|
...runtimeImports,
|
|
78
78
|
`} from '@agentuity/runtime';`,
|
|
79
|
+
`import type { Context } from 'hono';`,
|
|
79
80
|
`import { websocket } from 'hono/bun';`, // Always use Bun WebSocket (dev and prod)
|
|
80
81
|
!isDev && hasWebFrontend ? `import { serveStatic } from 'hono/bun';` : '',
|
|
81
82
|
].filter(Boolean);
|
|
82
83
|
|
|
83
84
|
imports.push(`import { type LogLevel } from '@agentuity/core';`);
|
|
84
|
-
|
|
85
|
-
// HMR setup (dev only)
|
|
86
|
-
const hmrSetup = isDev
|
|
87
|
-
? `
|
|
88
|
-
// HMR restart handler
|
|
89
|
-
if (typeof (globalThis as any).__AGENTUITY_RESTART__ === 'undefined') {
|
|
90
|
-
(globalThis as any).__AGENTUITY_RESTART__ = () => {
|
|
91
|
-
console.log('[HMR] Restart triggered but handler not ready yet');
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
`
|
|
95
|
-
: '';
|
|
85
|
+
imports.push(`import { bootstrapRuntimeEnv } from '@agentuity/cli/runtime-bootstrap';`);
|
|
96
86
|
|
|
97
87
|
// Generate route mounting code for all discovered routes
|
|
98
88
|
const routeImportsAndMounts: string[] = [];
|
|
@@ -146,7 +136,7 @@ app.route('/', workbenchRouter);
|
|
|
146
136
|
// Asset proxy routes - Forward Vite-specific requests to asset server
|
|
147
137
|
const VITE_ASSET_PORT = ${vitePort};
|
|
148
138
|
|
|
149
|
-
const proxyToVite = async (c) => {
|
|
139
|
+
const proxyToVite = async (c: Context) => {
|
|
150
140
|
const viteUrl = \`http://127.0.0.1:\${VITE_ASSET_PORT}\${c.req.path}\`;
|
|
151
141
|
const controller = new AbortController();
|
|
152
142
|
const timeout = setTimeout(() => controller.abort(), 10000); // 10s timeout
|
|
@@ -206,54 +196,82 @@ app.get('/*.css', proxyToVite);
|
|
|
206
196
|
let webRoutes = '';
|
|
207
197
|
if (hasWebFrontend) {
|
|
208
198
|
if (isDev) {
|
|
209
|
-
const htmlPath = join(srcDir, 'web', 'index.html');
|
|
210
199
|
webRoutes = `
|
|
211
200
|
// Web routes (dev mode with Vite HMR via proxy)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
);
|
|
233
|
-
|
|
201
|
+
// Proxy HTML from Vite to let @vitejs/plugin-react handle React Fast Refresh preamble
|
|
202
|
+
const devHtmlHandler = async (c: Context) => {
|
|
203
|
+
const viteUrl = \`http://127.0.0.1:${vitePort}/src/web/index.html\`;
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
otel.logger.debug('[Proxy] GET /src/web/index.html -> Vite:%d', ${vitePort});
|
|
207
|
+
const res = await fetch(viteUrl, { signal: AbortSignal.timeout(10000) });
|
|
208
|
+
|
|
209
|
+
// Get HTML text and transform relative paths to absolute
|
|
210
|
+
const html = await res.text();
|
|
211
|
+
const transformedHtml = html
|
|
212
|
+
.replace(/src="\\.\\//g, 'src="/src/web/')
|
|
213
|
+
.replace(/href="\\.\\//g, 'href="/src/web/');
|
|
214
|
+
|
|
215
|
+
return new Response(transformedHtml, {
|
|
216
|
+
status: res.status,
|
|
217
|
+
headers: res.headers,
|
|
218
|
+
});
|
|
219
|
+
} catch (err) {
|
|
220
|
+
otel.logger.error('Failed to proxy HTML to Vite: %s', err instanceof Error ? err.message : String(err));
|
|
221
|
+
return c.text('Vite asset server error (HTML)', 500);
|
|
222
|
+
}
|
|
234
223
|
};
|
|
235
224
|
app.get('/', devHtmlHandler);
|
|
236
225
|
// 404 for unmatched API/system routes
|
|
237
|
-
app.all('/_agentuity/*', (c) => c.notFound());
|
|
238
|
-
app.all('/api/*', (c) => c.notFound());
|
|
239
|
-
${hasWorkbench ? '' : `app.all('/workbench/*', (c) => c.notFound());`}
|
|
240
|
-
// SPA fallback - serve index.html for
|
|
241
|
-
//
|
|
242
|
-
|
|
226
|
+
app.all('/_agentuity/*', (c: Context) => c.notFound());
|
|
227
|
+
app.all('/api/*', (c: Context) => c.notFound());
|
|
228
|
+
${hasWorkbench ? '' : `app.all('/workbench/*', (c: Context) => c.notFound());`}
|
|
229
|
+
// SPA fallback - serve index.html for client-side routing
|
|
230
|
+
// Asset requests (/*.js, /*.tsx, /*.css, etc.) are handled by Vite proxy routes if present,
|
|
231
|
+
// otherwise we check for file extensions to avoid returning HTML for missing assets
|
|
232
|
+
app.get('*', (c: Context) => {
|
|
233
|
+
const path = c.req.path;
|
|
234
|
+
// If path has a file extension and Vite proxy isn't handling it, return 404
|
|
235
|
+
// This prevents returning HTML for missing assets like /foo.js
|
|
236
|
+
if (${!vitePort} && /\\.[a-zA-Z0-9]+$/.test(path)) {
|
|
237
|
+
return c.notFound();
|
|
238
|
+
}
|
|
239
|
+
return devHtmlHandler(c);
|
|
240
|
+
});
|
|
243
241
|
`;
|
|
244
242
|
} else {
|
|
245
243
|
webRoutes = `
|
|
246
244
|
// Web routes (production - static files)
|
|
247
245
|
import { readFileSync } from 'node:fs';
|
|
248
246
|
const indexHtml = readFileSync(import.meta.dir + '/client/index.html', 'utf-8');
|
|
247
|
+
|
|
248
|
+
app.get('/', (c: Context) => c.html(indexHtml));
|
|
249
|
+
|
|
250
|
+
// Serve static assets from /assets/* (Vite bundled output)
|
|
249
251
|
app.use('/assets/*', serveStatic({ root: import.meta.dir + '/client' }));
|
|
250
|
-
|
|
251
|
-
//
|
|
252
|
-
app.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
app.
|
|
252
|
+
|
|
253
|
+
// Serve static public assets (favicon.ico, robots.txt, etc. from Vite's public folder)
|
|
254
|
+
app.use('/*', serveStatic({ root: import.meta.dir + '/client', rewriteRequestPath: (path) => path }));
|
|
255
|
+
|
|
256
|
+
// 404 for unmatched API/system routes (IMPORTANT: comes before SPA fallback)
|
|
257
|
+
app.all('/_agentuity/*', (c: Context) => c.notFound());
|
|
258
|
+
app.all('/api/*', (c: Context) => c.notFound());
|
|
259
|
+
${hasWorkbench ? '' : `app.all('/workbench/*', (c: Context) => c.notFound());`}
|
|
260
|
+
|
|
261
|
+
// SPA fallback with asset protection
|
|
262
|
+
// In production, we need to distinguish between:
|
|
263
|
+
// - SPA routes like /dashboard, /users/123 (should return HTML)
|
|
264
|
+
// - Missing assets like /foo.js, /bar.css (should return 404)
|
|
265
|
+
// We check for file extensions to detect asset requests
|
|
266
|
+
app.get('*', (c: Context) => {
|
|
267
|
+
const path = c.req.path;
|
|
268
|
+
// If path has a file extension, it's likely an asset request
|
|
269
|
+
// Return 404 instead of serving HTML
|
|
270
|
+
if (/\\.[a-zA-Z0-9]+$/.test(path)) {
|
|
271
|
+
return c.notFound();
|
|
272
|
+
}
|
|
273
|
+
return c.html(indexHtml);
|
|
274
|
+
});
|
|
257
275
|
`;
|
|
258
276
|
}
|
|
259
277
|
}
|
|
@@ -265,7 +283,7 @@ app.get('*', (c) => c.html(indexHtml));
|
|
|
265
283
|
? isDev
|
|
266
284
|
? `
|
|
267
285
|
// Workbench route (dev mode - let Vite serve source files with HMR)
|
|
268
|
-
app.get('${workbenchRoute}', async (c) => {
|
|
286
|
+
app.get('${workbenchRoute}', async (c: Context) => {
|
|
269
287
|
const html = await Bun.file('${workbenchSrcDir}/index.html').text();
|
|
270
288
|
// Rewrite script/css paths to use Vite's @fs protocol
|
|
271
289
|
const withVite = html
|
|
@@ -281,7 +299,7 @@ import { readFileSync, existsSync } from 'node:fs';
|
|
|
281
299
|
const workbenchIndexPath = import.meta.dir + '/workbench/index.html';
|
|
282
300
|
if (existsSync(workbenchIndexPath)) {
|
|
283
301
|
const workbenchIndex = readFileSync(workbenchIndexPath, 'utf-8');
|
|
284
|
-
app.get('${workbenchRoute}', (c) => c.html(workbenchIndex));
|
|
302
|
+
app.get('${workbenchRoute}', (c: Context) => c.html(workbenchIndex));
|
|
285
303
|
app.get('${workbenchRoute}/*', serveStatic({ root: import.meta.dir + '/workbench' }));
|
|
286
304
|
}
|
|
287
305
|
`
|
|
@@ -311,16 +329,25 @@ if (typeof Bun !== 'undefined') {
|
|
|
311
329
|
|
|
312
330
|
const code = `// Auto-generated by Agentuity for ${mode} mode
|
|
313
331
|
// DO NOT EDIT - This file is regenerated on every build
|
|
314
|
-
// NOTE: Bun auto-loads .env files from CWD before executing JavaScript
|
|
315
|
-
|
|
316
332
|
${imports.join('\n')}
|
|
317
333
|
|
|
318
|
-
|
|
334
|
+
// Step 0: Bootstrap runtime environment (load profile-specific .env files)
|
|
335
|
+
// Only in development - production env vars are injected by platform
|
|
336
|
+
// This must happen BEFORE any imports that depend on environment variables
|
|
337
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
338
|
+
// Pass project directory (parent of .agentuity/) so .env files are loaded correctly
|
|
339
|
+
await bootstrapRuntimeEnv({ projectDir: import.meta.dir + '/..' });
|
|
340
|
+
}
|
|
319
341
|
|
|
320
342
|
// Step 1: Initialize telemetry and services
|
|
321
343
|
const serverUrl = \`http://127.0.0.1:\${process.env.PORT || '3500'}\`;
|
|
322
344
|
const otel = register({ processors: [], logLevel: (process.env.AGENTUITY_LOG_LEVEL || 'info') as LogLevel });
|
|
323
|
-
|
|
345
|
+
|
|
346
|
+
// Get app state and config for use below
|
|
347
|
+
const appState = getAppState();
|
|
348
|
+
const appConfig = getAppConfig();
|
|
349
|
+
|
|
350
|
+
createServices(otel.logger, appConfig, serverUrl);
|
|
324
351
|
|
|
325
352
|
// Make logger and tracer globally available for user's app.ts
|
|
326
353
|
setGlobalLogger(otel.logger);
|
|
@@ -350,10 +377,6 @@ app.use('/api/*', createAgentMiddleware(''));
|
|
|
350
377
|
// Step 4: Import user's app.ts (runs createApp, gets state/config)
|
|
351
378
|
await import('../app.ts');
|
|
352
379
|
|
|
353
|
-
// Get app state and config for use below
|
|
354
|
-
const appState = getAppState();
|
|
355
|
-
const appConfig = getAppConfig();
|
|
356
|
-
|
|
357
380
|
// Step 5: Initialize providers
|
|
358
381
|
const threadProvider = getThreadProvider();
|
|
359
382
|
const sessionProvider = getSessionProvider();
|
|
@@ -364,8 +387,8 @@ await sessionProvider.initialize(appState);
|
|
|
364
387
|
// Step 6: Mount routes (AFTER middleware is applied)
|
|
365
388
|
|
|
366
389
|
// System health/idle endpoints
|
|
367
|
-
const healthHandler = (c:
|
|
368
|
-
const idleHandler = (c:
|
|
390
|
+
const healthHandler = (c: Context) => c.text('OK');
|
|
391
|
+
const idleHandler = (c: Context) => {
|
|
369
392
|
// Check if server is idle (no pending requests/connections)
|
|
370
393
|
const server = (globalThis as any).__AGENTUITY_SERVER__;
|
|
371
394
|
if (!server) return c.text('NO', { status: 200 });
|
|
@@ -379,7 +402,6 @@ const idleHandler = (c: any) => {
|
|
|
379
402
|
return c.text('OK', { status: 200 });
|
|
380
403
|
};
|
|
381
404
|
|
|
382
|
-
// Mount on both /_agentuity/* and /* for backwards compatibility
|
|
383
405
|
app.get('/_agentuity/health', healthHandler);
|
|
384
406
|
app.get('/_health', healthHandler);
|
|
385
407
|
app.get('/_agentuity/idle', idleHandler);
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { Logger } from '../../../types';
|
|
9
|
-
import { startViteAssetServer } from './vite-asset-server';
|
|
10
9
|
|
|
11
10
|
export interface BunDevServerOptions {
|
|
12
11
|
rootDir: string;
|
|
@@ -15,20 +14,21 @@ export interface BunDevServerOptions {
|
|
|
15
14
|
orgId?: string;
|
|
16
15
|
deploymentId?: string;
|
|
17
16
|
logger: Logger;
|
|
17
|
+
vitePort: number; // Port of already-running Vite asset server
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export interface BunDevServerResult {
|
|
21
|
-
viteAssetServer: { server: { close: () => void | Promise<void> }; port: number };
|
|
22
21
|
bunServerPort: number;
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
/**
|
|
26
|
-
* Start Bun dev server
|
|
25
|
+
* Start Bun dev server (Vite asset server must already be running)
|
|
26
|
+
* Generates entry file with proxy routes pointing to Vite
|
|
27
27
|
*/
|
|
28
28
|
export async function startBunDevServer(options: BunDevServerOptions): Promise<BunDevServerResult> {
|
|
29
|
-
const { rootDir, port = 3500, projectId = '', deploymentId = '', logger } = options;
|
|
29
|
+
const { rootDir, port = 3500, projectId = '', deploymentId = '', logger, vitePort } = options;
|
|
30
30
|
|
|
31
|
-
logger.debug('Starting Bun dev server
|
|
31
|
+
logger.debug('Starting Bun dev server (Vite already running on port %d)...', vitePort);
|
|
32
32
|
|
|
33
33
|
// Generate workbench source files if enabled (dev mode)
|
|
34
34
|
const { loadAgentuityConfig, getWorkbenchConfig } = await import('./config-loader');
|
|
@@ -41,15 +41,6 @@ export async function startBunDevServer(options: BunDevServerOptions): Promise<B
|
|
|
41
41
|
await generateWorkbenchFiles(rootDir, projectId, workbenchConfig, logger);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
// Step 1: Start Vite asset server FIRST and get its dynamic port
|
|
45
|
-
logger.debug('🎨 Starting Vite asset server for HMR...');
|
|
46
|
-
const viteAssetServer = await startViteAssetServer({
|
|
47
|
-
rootDir,
|
|
48
|
-
logger,
|
|
49
|
-
workbenchPath: workbenchConfig.enabled ? workbenchConfig.route : undefined,
|
|
50
|
-
});
|
|
51
|
-
const vitePort = viteAssetServer.port;
|
|
52
|
-
|
|
53
44
|
// Step 2: Generate entry file with Vite port for asset proxying
|
|
54
45
|
logger.debug('📝 Generating entry file with asset proxy configuration...');
|
|
55
46
|
const { generateEntryFile } = await import('../entry-generator');
|
|
@@ -66,10 +57,10 @@ export async function startBunDevServer(options: BunDevServerOptions): Promise<B
|
|
|
66
57
|
// Step 3: Load the generated app - this will start Bun.serve() internally
|
|
67
58
|
logger.debug('📦 Loading generated app (Bun server will start)...');
|
|
68
59
|
const appPath = `${rootDir}/.agentuity/app.generated.ts`;
|
|
69
|
-
|
|
60
|
+
|
|
70
61
|
// Set PORT env var so the generated app uses the correct port
|
|
71
62
|
process.env.PORT = String(port);
|
|
72
|
-
|
|
63
|
+
|
|
73
64
|
await import(appPath);
|
|
74
65
|
|
|
75
66
|
// Wait for server to actually start listening
|
|
@@ -105,11 +96,10 @@ export async function startBunDevServer(options: BunDevServerOptions): Promise<B
|
|
|
105
96
|
);
|
|
106
97
|
}
|
|
107
98
|
|
|
108
|
-
logger.
|
|
99
|
+
logger.debug(`Bun dev server started on http://127.0.0.1:${port}`);
|
|
109
100
|
logger.debug(`Asset requests (/@vite/*, /src/web/*, etc.) proxied to Vite:${vitePort}`);
|
|
110
101
|
|
|
111
102
|
return {
|
|
112
|
-
viteAssetServer,
|
|
113
103
|
bunServerPort: port,
|
|
114
104
|
};
|
|
115
105
|
}
|
|
@@ -35,18 +35,32 @@ export async function installExternalsAndBuild(options: ServerBundleOptions): Pr
|
|
|
35
35
|
// These are devDependencies that may exist in node_modules but aren't needed at runtime
|
|
36
36
|
const buildToolExternals = ['@babel/*', 'lightningcss', '@vitejs/*', 'vite', 'esbuild'];
|
|
37
37
|
|
|
38
|
-
// Load custom externals from agentuity.config.ts if it exists
|
|
38
|
+
// Load custom externals and define from agentuity.config.ts if it exists
|
|
39
39
|
const customExternals: string[] = [];
|
|
40
|
+
let userDefine: Record<string, string> = {};
|
|
40
41
|
const configPath = join(rootDir, 'agentuity.config.ts');
|
|
41
42
|
if (await Bun.file(configPath).exists()) {
|
|
42
43
|
try {
|
|
43
44
|
const config = await import(configPath);
|
|
44
45
|
const userConfig = config.default;
|
|
46
|
+
|
|
47
|
+
// Load custom externals (legacy build.external support)
|
|
45
48
|
if (userConfig?.build?.external && Array.isArray(userConfig.build.external)) {
|
|
46
49
|
customExternals.push(
|
|
47
50
|
...userConfig.build.external.filter((e: unknown) => typeof e === 'string')
|
|
48
51
|
);
|
|
49
52
|
}
|
|
53
|
+
|
|
54
|
+
// Load custom define values
|
|
55
|
+
if (userConfig?.define && typeof userConfig.define === 'object') {
|
|
56
|
+
userDefine = userConfig.define;
|
|
57
|
+
if (Object.keys(userDefine).length > 0) {
|
|
58
|
+
logger.debug(
|
|
59
|
+
'Loaded %d custom define(s) from agentuity.config.ts for server bundle',
|
|
60
|
+
Object.keys(userDefine).length
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
50
64
|
} catch (error) {
|
|
51
65
|
logger.info('Failed to load agentuity.config.ts for externals:', error);
|
|
52
66
|
}
|
|
@@ -197,6 +211,7 @@ export async function installExternalsAndBuild(options: ServerBundleOptions): Pr
|
|
|
197
211
|
minify: !dev,
|
|
198
212
|
sourcemap: (dev ? 'inline' : 'external') as 'inline' | 'external',
|
|
199
213
|
external,
|
|
214
|
+
define: userDefine, // Include custom define values from agentuity.config.ts
|
|
200
215
|
naming: {
|
|
201
216
|
entry: 'app.js', // Output as app.js (not app.generated.js)
|
|
202
217
|
},
|
|
@@ -24,6 +24,22 @@ export async function generateAssetServerConfig(
|
|
|
24
24
|
): Promise<InlineConfig> {
|
|
25
25
|
const { rootDir, logger, workbenchPath, port } = options;
|
|
26
26
|
|
|
27
|
+
// Load custom user config for define values and plugins
|
|
28
|
+
const { loadAgentuityConfig } = await import('./config-loader');
|
|
29
|
+
const userConfig = await loadAgentuityConfig(rootDir, logger);
|
|
30
|
+
const userDefine = userConfig?.define || {};
|
|
31
|
+
const userPlugins = userConfig?.plugins || [];
|
|
32
|
+
|
|
33
|
+
if (Object.keys(userDefine).length > 0) {
|
|
34
|
+
logger.debug(
|
|
35
|
+
'Loaded %d custom define(s) from agentuity.config.ts',
|
|
36
|
+
Object.keys(userDefine).length
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
if (userPlugins.length > 0) {
|
|
40
|
+
logger.debug('Loaded %d custom plugin(s) from agentuity.config.ts', userPlugins.length);
|
|
41
|
+
}
|
|
42
|
+
|
|
27
43
|
// Load path aliases from tsconfig.json if available
|
|
28
44
|
const tsconfigPath = join(rootDir, 'tsconfig.json');
|
|
29
45
|
let alias = {};
|
|
@@ -82,6 +98,9 @@ export async function generateAssetServerConfig(
|
|
|
82
98
|
|
|
83
99
|
// Define environment variables for browser
|
|
84
100
|
define: {
|
|
101
|
+
// Merge user-defined constants first
|
|
102
|
+
...userDefine,
|
|
103
|
+
// Then add default defines (these will override any user-defined protected keys)
|
|
85
104
|
...(workbenchPath
|
|
86
105
|
? { 'import.meta.env.AGENTUITY_PUBLIC_WORKBENCH_PATH': JSON.stringify(workbenchPath) }
|
|
87
106
|
: {}),
|
|
@@ -91,8 +110,10 @@ export async function generateAssetServerConfig(
|
|
|
91
110
|
'process.env.NODE_ENV': JSON.stringify('development'),
|
|
92
111
|
},
|
|
93
112
|
|
|
94
|
-
//
|
|
113
|
+
// Plugins: User plugins first (e.g., Tailwind), then React and browser env
|
|
95
114
|
plugins: [
|
|
115
|
+
// User-defined plugins from agentuity.config.ts (e.g., Tailwind CSS)
|
|
116
|
+
...userPlugins,
|
|
96
117
|
// React plugin for JSX/TSX transformation and Fast Refresh
|
|
97
118
|
(await import('@vitejs/plugin-react')).default(),
|
|
98
119
|
// Browser env plugin to map process.env to import.meta.env
|
|
@@ -55,7 +55,7 @@ export async function startViteAssetServer(
|
|
|
55
55
|
// Get the actual port Vite is using (may differ from preferred if port was taken)
|
|
56
56
|
const actualPort = server.config.server.port || preferredPort;
|
|
57
57
|
|
|
58
|
-
logger.
|
|
58
|
+
logger.debug(`✅ Vite asset server started on port ${actualPort}`);
|
|
59
59
|
if (actualPort !== preferredPort) {
|
|
60
60
|
logger.debug(`Port ${preferredPort} was taken, using ${actualPort} instead`);
|
|
61
61
|
}
|
|
@@ -54,7 +54,7 @@ export async function runViteBuild(options: ViteBuildOptions): Promise<void> {
|
|
|
54
54
|
logger,
|
|
55
55
|
});
|
|
56
56
|
} catch (error) {
|
|
57
|
-
logger.error('server-bundler import or execution failed:', error);
|
|
57
|
+
logger.error('server-bundler import or execution failed: %s', error);
|
|
58
58
|
throw error;
|
|
59
59
|
}
|
|
60
60
|
return;
|
|
@@ -85,6 +85,15 @@ export async function runViteBuild(options: ViteBuildOptions): Promise<void> {
|
|
|
85
85
|
logger.debug('Loaded %d custom plugin(s) from agentuity.config.ts', userPlugins.length);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
// Merge custom define values from user config
|
|
89
|
+
const userDefine = userConfig?.define || {};
|
|
90
|
+
if (Object.keys(userDefine).length > 0) {
|
|
91
|
+
logger.debug(
|
|
92
|
+
'Loaded %d custom define(s) from agentuity.config.ts',
|
|
93
|
+
Object.keys(userDefine).length
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
88
97
|
// Determine CDN base URL for production builds
|
|
89
98
|
const isLocalRegion = options.region === 'local';
|
|
90
99
|
const cdnBaseUrl =
|
|
@@ -99,6 +108,9 @@ export async function runViteBuild(options: ViteBuildOptions): Promise<void> {
|
|
|
99
108
|
publicDir: join(rootDir, 'src', 'web', 'public'),
|
|
100
109
|
base: cdnBaseUrl, // CDN URL for production assets
|
|
101
110
|
define: {
|
|
111
|
+
// Merge user-defined constants first
|
|
112
|
+
...userDefine,
|
|
113
|
+
// Then add default defines (these will override any user-defined protected keys)
|
|
102
114
|
// Set workbench path if enabled (use import.meta.env for client code)
|
|
103
115
|
'import.meta.env.AGENTUITY_PUBLIC_WORKBENCH_PATH': workbenchEnabled
|
|
104
116
|
? JSON.stringify(workbenchRoute)
|
|
@@ -121,11 +133,26 @@ export async function runViteBuild(options: ViteBuildOptions): Promise<void> {
|
|
|
121
133
|
// Ensure route ends with / for Vite base
|
|
122
134
|
const base = workbenchRoute.endsWith('/') ? workbenchRoute : `${workbenchRoute}/`;
|
|
123
135
|
|
|
136
|
+
// Load custom user config for define values (same as client mode)
|
|
137
|
+
const { loadAgentuityConfig } = await import('./config-loader');
|
|
138
|
+
const userConfig = await loadAgentuityConfig(rootDir, logger);
|
|
139
|
+
const userDefine = userConfig?.define || {};
|
|
140
|
+
if (Object.keys(userDefine).length > 0) {
|
|
141
|
+
logger.debug(
|
|
142
|
+
'Loaded %d custom define(s) from agentuity.config.ts for workbench',
|
|
143
|
+
Object.keys(userDefine).length
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
124
147
|
viteConfig = {
|
|
125
148
|
root: join(rootDir, '.agentuity/workbench-src'), // Use generated workbench source
|
|
126
149
|
base, // All workbench assets are under the configured route
|
|
127
150
|
plugins: [react(), patchPlugin({ logger, dev })],
|
|
128
151
|
envPrefix: ['VITE_', 'AGENTUITY_PUBLIC_', 'PUBLIC_'],
|
|
152
|
+
define: {
|
|
153
|
+
// Merge user-defined constants
|
|
154
|
+
...userDefine,
|
|
155
|
+
},
|
|
129
156
|
build: {
|
|
130
157
|
outDir: join(rootDir, '.agentuity/workbench'),
|
|
131
158
|
rollupOptions: {
|
package/src/cmd/cloud/deploy.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { createPublicKey } from 'node:crypto';
|
|
|
4
4
|
import { createReadStream, createWriteStream, existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
5
5
|
import { tmpdir } from 'node:os';
|
|
6
6
|
import { StructuredError } from '@agentuity/core';
|
|
7
|
+
import { isRunningFromExecutable } from '../upgrade';
|
|
7
8
|
import { createSubcommand } from '../../types';
|
|
8
9
|
import * as tui from '../../tui';
|
|
9
10
|
import { saveProjectDir, getDefaultConfigDir } from '../../config';
|
|
@@ -335,33 +336,49 @@ export const deploySubcommand = createSubcommand({
|
|
|
335
336
|
);
|
|
336
337
|
}
|
|
337
338
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
return stepError(
|
|
343
|
-
`server did not provide upload URL for asset "${asset.filename}"; upload aborted`
|
|
344
|
-
);
|
|
345
|
-
}
|
|
339
|
+
// Workaround for Bun crash in compiled executables (https://github.com/agentuity/sdk/issues/191)
|
|
340
|
+
// Use limited concurrency (2 at a time) for executables to avoid parallel fetch crash
|
|
341
|
+
const isExecutable = isRunningFromExecutable();
|
|
342
|
+
const concurrency = isExecutable ? 2 : build.assets.length;
|
|
346
343
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
fetch(assetUrl, {
|
|
351
|
-
method: 'PUT',
|
|
352
|
-
duplex: 'half',
|
|
353
|
-
headers: {
|
|
354
|
-
'Content-Type': asset.contentType,
|
|
355
|
-
},
|
|
356
|
-
body: file,
|
|
357
|
-
})
|
|
344
|
+
if (isExecutable) {
|
|
345
|
+
ctx.logger.trace(
|
|
346
|
+
`Running from executable - using limited concurrency (${concurrency} uploads at a time)`
|
|
358
347
|
);
|
|
359
348
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
for (
|
|
363
|
-
|
|
364
|
-
|
|
349
|
+
|
|
350
|
+
// Process assets in batches with limited concurrency
|
|
351
|
+
for (let i = 0; i < build.assets.length; i += concurrency) {
|
|
352
|
+
const batch = build.assets.slice(i, i + concurrency);
|
|
353
|
+
const promises: Promise<Response>[] = [];
|
|
354
|
+
|
|
355
|
+
for (const asset of batch) {
|
|
356
|
+
const assetUrl = instructions.assets[asset.filename];
|
|
357
|
+
if (!assetUrl) {
|
|
358
|
+
return stepError(
|
|
359
|
+
`server did not provide upload URL for asset "${asset.filename}"; upload aborted`
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Asset filename already includes the subdirectory (e.g., "client/assets/main-abc123.js")
|
|
364
|
+
const file = Bun.file(join(projectDir, '.agentuity', asset.filename));
|
|
365
|
+
promises.push(
|
|
366
|
+
fetch(assetUrl, {
|
|
367
|
+
method: 'PUT',
|
|
368
|
+
duplex: 'half',
|
|
369
|
+
headers: {
|
|
370
|
+
'Content-Type': asset.contentType,
|
|
371
|
+
},
|
|
372
|
+
body: file,
|
|
373
|
+
})
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const resps = await Promise.all(promises);
|
|
378
|
+
for (const r of resps) {
|
|
379
|
+
if (!r.ok) {
|
|
380
|
+
return stepError(`error uploading asset: ${await r.text()}`);
|
|
381
|
+
}
|
|
365
382
|
}
|
|
366
383
|
}
|
|
367
384
|
ctx.logger.trace('Asset uploads complete');
|