@agentuity/cli 0.0.97 → 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 +85 -64
- 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 +38 -16
- 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 +21 -2
- 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 +85 -65
- package/src/cmd/build/vite/bun-dev-server.ts +45 -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 +29 -2
- 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
|
`
|
|
@@ -290,9 +308,7 @@ if (existsSync(workbenchIndexPath)) {
|
|
|
290
308
|
// Server startup (same for dev and prod - Bun.serve with native WebSocket)
|
|
291
309
|
const serverStartup = `
|
|
292
310
|
// Start Bun server${isDev ? ' (dev mode with Vite asset proxy)' : ''}
|
|
293
|
-
if (typeof Bun !== 'undefined'
|
|
294
|
-
// Not the main module, skip server startup (this is being imported/analyzed)
|
|
295
|
-
} else if (typeof Bun !== 'undefined') {
|
|
311
|
+
if (typeof Bun !== 'undefined') {
|
|
296
312
|
// Enable process exit protection now that we're starting the server
|
|
297
313
|
enableProcessExitProtection();
|
|
298
314
|
|
|
@@ -313,16 +329,25 @@ if (typeof Bun !== 'undefined' && !import.meta.main) {
|
|
|
313
329
|
|
|
314
330
|
const code = `// Auto-generated by Agentuity for ${mode} mode
|
|
315
331
|
// DO NOT EDIT - This file is regenerated on every build
|
|
316
|
-
// NOTE: Bun auto-loads .env files from CWD before executing JavaScript
|
|
317
|
-
|
|
318
332
|
${imports.join('\n')}
|
|
319
333
|
|
|
320
|
-
|
|
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
|
+
}
|
|
321
341
|
|
|
322
342
|
// Step 1: Initialize telemetry and services
|
|
323
343
|
const serverUrl = \`http://127.0.0.1:\${process.env.PORT || '3500'}\`;
|
|
324
344
|
const otel = register({ processors: [], logLevel: (process.env.AGENTUITY_LOG_LEVEL || 'info') as LogLevel });
|
|
325
|
-
|
|
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);
|
|
326
351
|
|
|
327
352
|
// Make logger and tracer globally available for user's app.ts
|
|
328
353
|
setGlobalLogger(otel.logger);
|
|
@@ -352,10 +377,6 @@ app.use('/api/*', createAgentMiddleware(''));
|
|
|
352
377
|
// Step 4: Import user's app.ts (runs createApp, gets state/config)
|
|
353
378
|
await import('../app.ts');
|
|
354
379
|
|
|
355
|
-
// Get app state and config for use below
|
|
356
|
-
const appState = getAppState();
|
|
357
|
-
const appConfig = getAppConfig();
|
|
358
|
-
|
|
359
380
|
// Step 5: Initialize providers
|
|
360
381
|
const threadProvider = getThreadProvider();
|
|
361
382
|
const sessionProvider = getSessionProvider();
|
|
@@ -366,8 +387,8 @@ await sessionProvider.initialize(appState);
|
|
|
366
387
|
// Step 6: Mount routes (AFTER middleware is applied)
|
|
367
388
|
|
|
368
389
|
// System health/idle endpoints
|
|
369
|
-
const healthHandler = (c:
|
|
370
|
-
const idleHandler = (c:
|
|
390
|
+
const healthHandler = (c: Context) => c.text('OK');
|
|
391
|
+
const idleHandler = (c: Context) => {
|
|
371
392
|
// Check if server is idle (no pending requests/connections)
|
|
372
393
|
const server = (globalThis as any).__AGENTUITY_SERVER__;
|
|
373
394
|
if (!server) return c.text('NO', { status: 200 });
|
|
@@ -381,7 +402,6 @@ const idleHandler = (c: any) => {
|
|
|
381
402
|
return c.text('OK', { status: 200 });
|
|
382
403
|
};
|
|
383
404
|
|
|
384
|
-
// Mount on both /_agentuity/* and /* for backwards compatibility
|
|
385
405
|
app.get('/_agentuity/health', healthHandler);
|
|
386
406
|
app.get('/_health', healthHandler);
|
|
387
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,17 +41,8 @@ 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.info('🎨 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
|
-
logger.
|
|
45
|
+
logger.debug('📝 Generating entry file with asset proxy configuration...');
|
|
55
46
|
const { generateEntryFile } = await import('../entry-generator');
|
|
56
47
|
await generateEntryFile({
|
|
57
48
|
rootDir,
|
|
@@ -64,15 +55,51 @@ export async function startBunDevServer(options: BunDevServerOptions): Promise<B
|
|
|
64
55
|
});
|
|
65
56
|
|
|
66
57
|
// Step 3: Load the generated app - this will start Bun.serve() internally
|
|
67
|
-
logger.
|
|
58
|
+
logger.debug('📦 Loading generated app (Bun server will start)...');
|
|
68
59
|
const appPath = `${rootDir}/.agentuity/app.generated.ts`;
|
|
60
|
+
|
|
61
|
+
// Set PORT env var so the generated app uses the correct port
|
|
62
|
+
process.env.PORT = String(port);
|
|
63
|
+
|
|
69
64
|
await import(appPath);
|
|
70
65
|
|
|
71
|
-
|
|
66
|
+
// Wait for server to actually start listening
|
|
67
|
+
// The generated app sets (globalThis as any).__AGENTUITY_SERVER__ when server starts
|
|
68
|
+
const maxRetries = 50; // Increased retries for slower systems
|
|
69
|
+
const retryDelay = 100; // ms
|
|
70
|
+
let serverReady = false;
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
73
|
+
// Check if global server object exists
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
75
|
+
if ((globalThis as any).__AGENTUITY_SERVER__) {
|
|
76
|
+
// Server object exists, now verify it's actually listening by making a request
|
|
77
|
+
try {
|
|
78
|
+
await fetch(`http://127.0.0.1:${port}/`, {
|
|
79
|
+
method: 'HEAD',
|
|
80
|
+
signal: AbortSignal.timeout(1000),
|
|
81
|
+
});
|
|
82
|
+
// Any response (even 404) means server is listening
|
|
83
|
+
serverReady = true;
|
|
84
|
+
break;
|
|
85
|
+
} catch {
|
|
86
|
+
// Connection refused or timeout - server not ready yet
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Wait before next check
|
|
90
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!serverReady) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`Bun server failed to start on port ${port} after ${maxRetries * retryDelay}ms`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
logger.debug(`Bun dev server started on http://127.0.0.1:${port}`);
|
|
72
100
|
logger.debug(`Asset requests (/@vite/*, /src/web/*, etc.) proxied to Vite:${vitePort}`);
|
|
73
101
|
|
|
74
102
|
return {
|
|
75
|
-
viteAssetServer,
|
|
76
103
|
bunServerPort: port,
|
|
77
104
|
};
|
|
78
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,11 +85,20 @@ 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 =
|
|
91
100
|
!dev && deploymentId && !isLocalRegion
|
|
92
|
-
? `https://static.agentuity.com/${deploymentId}/`
|
|
101
|
+
? `https://static.agentuity.com/${deploymentId}/client/`
|
|
93
102
|
: undefined;
|
|
94
103
|
|
|
95
104
|
viteConfig = {
|
|
@@ -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: {
|