@agentuity/cli 2.0.5 → 2.0.6

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.
Files changed (35) hide show
  1. package/dist/cmd/build/patch/otel-llm.js +2 -2
  2. package/dist/cmd/build/patch/otel-llm.js.map +1 -1
  3. package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
  4. package/dist/cmd/build/vite/bun-dev-server.js +1 -0
  5. package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
  6. package/dist/cmd/build/vite/index.d.ts +0 -28
  7. package/dist/cmd/build/vite/index.d.ts.map +1 -1
  8. package/dist/cmd/build/vite/index.js +1 -104
  9. package/dist/cmd/build/vite/index.js.map +1 -1
  10. package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
  11. package/dist/cmd/build/vite/metadata-generator.js +8 -2
  12. package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
  13. package/dist/cmd/build/vite/vite-asset-server-config.d.ts +2 -0
  14. package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
  15. package/dist/cmd/build/vite/vite-asset-server-config.js +5 -1
  16. package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
  17. package/dist/cmd/build/vite/vite-asset-server.d.ts +2 -0
  18. package/dist/cmd/build/vite/vite-asset-server.d.ts.map +1 -1
  19. package/dist/cmd/build/vite/vite-asset-server.js +2 -1
  20. package/dist/cmd/build/vite/vite-asset-server.js.map +1 -1
  21. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  22. package/dist/cmd/build/vite/vite-builder.js +143 -2
  23. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  24. package/dist/cmd/dev/index.d.ts.map +1 -1
  25. package/dist/cmd/dev/index.js +1 -0
  26. package/dist/cmd/dev/index.js.map +1 -1
  27. package/package.json +6 -6
  28. package/src/cmd/build/patch/otel-llm.ts +2 -2
  29. package/src/cmd/build/vite/bun-dev-server.ts +1 -0
  30. package/src/cmd/build/vite/index.ts +1 -148
  31. package/src/cmd/build/vite/metadata-generator.ts +8 -2
  32. package/src/cmd/build/vite/vite-asset-server-config.ts +16 -1
  33. package/src/cmd/build/vite/vite-asset-server.ts +4 -0
  34. package/src/cmd/build/vite/vite-builder.ts +171 -9
  35. package/src/cmd/dev/index.ts +1 -0
@@ -18,6 +18,8 @@ export interface GenerateAssetServerConfigOptions {
18
18
  backendPort: number; // The port Bun backend is running on (internal)
19
19
  /** User-defined route mount paths from createApp({ router }) (e.g., ['/api', '/v1']) */
20
20
  routePaths?: string[];
21
+ /** Live tunnel hostname (e.g. "epXXX.agentuity-us.live.internal") to add to Vite's allowedHosts */
22
+ liveHostname?: string;
21
23
  }
22
24
 
23
25
  /**
@@ -165,7 +167,15 @@ function spaFallbackPlugin(rootDir: string, routePaths: string[], workbenchPath?
165
167
  export async function generateAssetServerConfig(
166
168
  options: GenerateAssetServerConfigOptions
167
169
  ): Promise<InlineConfig> {
168
- const { rootDir, logger, workbenchPath, port, backendPort, routePaths = ['/api'] } = options;
170
+ const {
171
+ rootDir,
172
+ logger,
173
+ workbenchPath,
174
+ port,
175
+ backendPort,
176
+ routePaths = ['/api'],
177
+ liveHostname,
178
+ } = options;
169
179
 
170
180
  // Load path aliases from tsconfig.json if available
171
181
  const tsconfigPath = join(rootDir, 'tsconfig.json');
@@ -219,6 +229,11 @@ export async function generateAssetServerConfig(
219
229
  strictPort: true, // Port is pre-verified as available by findAvailablePort()
220
230
  host: '127.0.0.1',
221
231
 
232
+ // When accessed via an Agentuity live tunnel, the Host header is the
233
+ // tunnel hostname (e.g. "epXXX.agentuity-us.live.internal"). Without
234
+ // adding it here, Vite 6+ rejects the request with "host not allowed".
235
+ ...(liveHostname ? { allowedHosts: [liveHostname] } : {}),
236
+
222
237
  // Proxy backend routes to Bun server (HTTP only).
223
238
  // WebSocket upgrades are handled by the front-door TCP proxy (ws-proxy.ts)
224
239
  // which routes them directly to the Bun backend, bypassing Vite entirely.
@@ -26,6 +26,8 @@ export interface StartViteAssetServerOptions {
26
26
  backendPort: number; // Port of the Bun backend server
27
27
  /** User-defined route mount paths from createApp({ router }) */
28
28
  routePaths?: string[];
29
+ /** Live tunnel hostname to add to Vite's allowedHosts */
30
+ liveHostname?: string;
29
31
  }
30
32
 
31
33
  /**
@@ -80,6 +82,7 @@ export async function startViteAssetServer(
80
82
  port: preferredPort = 3500,
81
83
  backendPort,
82
84
  routePaths,
85
+ liveHostname,
83
86
  } = options;
84
87
 
85
88
  logger.debug('Starting Vite dev server (primary, proxying backend on port %d)...', backendPort);
@@ -101,6 +104,7 @@ export async function startViteAssetServer(
101
104
  port: availablePort,
102
105
  backendPort,
103
106
  routePaths,
107
+ liveHostname,
104
108
  });
105
109
 
106
110
  // Dynamically import vite from the project's node_modules
@@ -5,11 +5,132 @@
5
5
  */
6
6
 
7
7
  import { join } from 'node:path';
8
- import { existsSync, rmSync } from 'node:fs';
8
+ import { existsSync, rmSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs';
9
+ import { createHash } from 'node:crypto';
10
+ import { createRequire } from 'node:module';
9
11
  import type { InlineConfig } from 'vite';
10
12
  import type { Logger, DeployOptions } from '../../../types';
11
13
  import type { BuildReportCollector } from '../../../build-report';
12
14
 
15
+ /**
16
+ * Read the pre-built beacon script from @agentuity/frontend package.
17
+ * Tries multiple resolution strategies for workspace/installed/symlink scenarios.
18
+ */
19
+ async function readBeaconScript(projectRoot: string): Promise<string> {
20
+ let frontendPath: string | null = null;
21
+
22
+ try {
23
+ frontendPath = await Bun.resolve('@agentuity/frontend', projectRoot);
24
+ } catch {
25
+ // Not found from project root
26
+ }
27
+
28
+ if (!frontendPath) {
29
+ try {
30
+ const thisDir = new URL('.', import.meta.url).pathname;
31
+ frontendPath = await Bun.resolve('@agentuity/frontend', thisDir);
32
+ } catch {
33
+ // Not found from CLI directory
34
+ }
35
+ }
36
+
37
+ if (!frontendPath) {
38
+ try {
39
+ const projectRequire = createRequire(join(projectRoot, 'package.json'));
40
+ frontendPath = projectRequire.resolve('@agentuity/frontend');
41
+ } catch {
42
+ // Not found via createRequire
43
+ }
44
+ }
45
+
46
+ if (!frontendPath) {
47
+ throw new Error(
48
+ 'Could not resolve @agentuity/frontend. Ensure the package is installed and built.'
49
+ );
50
+ }
51
+
52
+ const packageDir = join(frontendPath, '..');
53
+ const beaconPath = join(packageDir, 'beacon.js');
54
+
55
+ const beaconFile = Bun.file(beaconPath);
56
+ if (!(await beaconFile.exists())) {
57
+ throw new Error(
58
+ `Beacon script not found at ${beaconPath}. Run "bun run build" in @agentuity/frontend first.`
59
+ );
60
+ }
61
+
62
+ return beaconFile.text();
63
+ }
64
+
65
+ /**
66
+ * Post-build step: inject the analytics beacon into the built index.html.
67
+ *
68
+ * 1. Reads the beacon script from @agentuity/frontend
69
+ * 2. Writes it as a content-hashed asset file
70
+ * 3. Injects a <script data-agentuity-beacon> tag into the HTML
71
+ *
72
+ * This runs after `vite build` completes so it works regardless of the
73
+ * user's vite.config.ts — no Vite plugin required.
74
+ */
75
+ async function injectBeacon(rootDir: string, cdnBaseUrl: string, logger: Logger): Promise<void> {
76
+ const clientDir = join(rootDir, '.agentuity/client');
77
+ const indexHtmlPath = join(clientDir, 'index.html');
78
+
79
+ if (!existsSync(indexHtmlPath)) {
80
+ logger.debug('No index.html found, skipping beacon injection');
81
+ return;
82
+ }
83
+
84
+ let beaconCode: string;
85
+ try {
86
+ beaconCode = await readBeaconScript(rootDir);
87
+ } catch (error) {
88
+ logger.warn(
89
+ 'Failed to read beacon script, skipping injection: %s',
90
+ error instanceof Error ? error.message : String(error)
91
+ );
92
+ return;
93
+ }
94
+
95
+ // Write beacon as a content-hashed asset (matches Vite's naming convention)
96
+ const hash = createHash('sha256').update(beaconCode).digest('hex').slice(0, 8);
97
+ const beaconFileName = `agentuity-beacon-${hash}.js`;
98
+ const assetsDir = join(clientDir, 'assets');
99
+ mkdirSync(assetsDir, { recursive: true });
100
+ writeFileSync(join(assetsDir, beaconFileName), beaconCode);
101
+
102
+ // If a Vite manifest exists, add the beacon so the metadata generator
103
+ // includes it in the asset list. When no manifest exists, the directory
104
+ // scanner in metadata-generator.ts picks up assets/ directly.
105
+ const manifestPath = join(clientDir, '.vite', 'manifest.json');
106
+ if (existsSync(manifestPath)) {
107
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
108
+ manifest['agentuity-beacon'] = { file: `assets/${beaconFileName}` };
109
+ writeFileSync(manifestPath, JSON.stringify(manifest));
110
+ }
111
+
112
+ // Build the beacon URL using the CDN base
113
+ const normalizedBase = cdnBaseUrl.endsWith('/') ? cdnBaseUrl : `${cdnBaseUrl}/`;
114
+ const beaconUrl = `${normalizedBase}assets/${beaconFileName}`;
115
+
116
+ // Inject the script tag into index.html
117
+ // The script must be sync (no async/defer) to patch history API before router loads.
118
+ // The data-agentuity-beacon attribute is the marker the runtime looks for.
119
+ const beaconScript = `<script data-agentuity-beacon src="${beaconUrl}"></script>`;
120
+
121
+ let html = readFileSync(indexHtmlPath, 'utf-8');
122
+ if (html.includes('</head>')) {
123
+ html = html.replace('</head>', `${beaconScript}</head>`);
124
+ } else if (html.includes('<body')) {
125
+ html = html.replace(/<body([^>]*)>/, `<body$1>${beaconScript}`);
126
+ } else {
127
+ html = beaconScript + html;
128
+ }
129
+
130
+ writeFileSync(indexHtmlPath, html);
131
+ logger.debug('Injected analytics beacon: %s', beaconUrl);
132
+ }
133
+
13
134
  export interface ViteBuildOptions {
14
135
  rootDir: string;
15
136
  mode: 'client' | 'server' | 'workbench';
@@ -116,18 +237,43 @@ export default defineConfig({
116
237
  await Bun.write(viteConfigPath, fallbackConfig);
117
238
  }
118
239
 
240
+ // Construct CDN base URL for production builds so Vite prefixes all
241
+ // asset URLs (CSS, JS chunks) with the CDN origin instead of "/".
242
+ const cdnBaseUrl =
243
+ !dev && options.deploymentId
244
+ ? `https://${options.region === 'local' ? 'localstack-static-assets.t3.storageapi.dev' : 'cdn.agentuity.com'}/${options.deploymentId}/client/`
245
+ : undefined;
246
+
247
+ const args = [
248
+ 'bun',
249
+ 'x',
250
+ 'vite',
251
+ 'build',
252
+ '--mode',
253
+ buildMode,
254
+ '--outDir',
255
+ clientOutDir,
256
+ '--logLevel',
257
+ 'error',
258
+ '--clearScreen',
259
+ 'false',
260
+ ];
261
+ if (cdnBaseUrl) {
262
+ args.push('--base', cdnBaseUrl);
263
+ }
264
+
119
265
  logger.debug('Spawning vite build for client (subprocess mode)');
120
266
  logger.debug(' outDir: %s', clientOutDir);
121
267
  logger.debug(' mode: %s', buildMode);
268
+ if (cdnBaseUrl) {
269
+ logger.debug(' base (CDN): %s', cdnBaseUrl);
270
+ }
122
271
 
123
- const viteProcess = Bun.spawn(
124
- ['bun', 'x', 'vite', 'build', '--mode', buildMode, '--outDir', clientOutDir],
125
- {
126
- cwd: rootDir,
127
- stdout: 'inherit',
128
- stderr: 'inherit',
129
- }
130
- );
272
+ const viteProcess = Bun.spawn(args, {
273
+ cwd: rootDir,
274
+ stdout: 'inherit',
275
+ stderr: 'inherit',
276
+ });
131
277
 
132
278
  const exitCode = await viteProcess.exited;
133
279
 
@@ -264,6 +410,22 @@ export async function runAllBuilds(options: Omit<ViteBuildOptions, 'mode'>): Pro
264
410
  logger.debug('Moved index.html from src/web/ to client root');
265
411
  }
266
412
 
413
+ // Post-build: inject analytics beacon into the built HTML.
414
+ // Must run AFTER the index.html normalization above (Vite may
415
+ // output to src/web/index.html which gets moved to the client root).
416
+ const isLocalRegion = options.region === 'local';
417
+ const cdnDomain = isLocalRegion
418
+ ? 'localstack-static-assets.t3.storageapi.dev'
419
+ : 'cdn.agentuity.com';
420
+ const cdnBaseUrl =
421
+ !dev && options.deploymentId
422
+ ? `https://${cdnDomain}/${options.deploymentId}/client/`
423
+ : undefined;
424
+
425
+ if (cdnBaseUrl && analyticsEnabled) {
426
+ await injectBeacon(rootDir, cdnBaseUrl, logger);
427
+ }
428
+
267
429
  result.client.included = true;
268
430
  result.client.duration = Date.now() - started;
269
431
  endClientDiagnostic?.();
@@ -552,6 +552,7 @@ export const command = createCommand({
552
552
  port: viteInternalPort,
553
553
  backendPort: bunBackendPort,
554
554
  routePaths,
555
+ liveHostname: devmode?.hostname,
555
556
  });
556
557
  viteServer = viteResult.server;
557
558
  vitePort = viteResult.port;