@agentuity/cli 0.0.94 → 0.0.96
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/AGENTS.md +54 -0
- package/dist/auth.d.ts +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +8 -5
- package/dist/auth.js.map +1 -1
- package/dist/banner.js +1 -1
- package/dist/banner.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +190 -27
- package/dist/cli.js.map +1 -1
- package/dist/cmd/auth/signup.js +1 -1
- package/dist/cmd/auth/signup.js.map +1 -1
- package/dist/cmd/build/ast.d.ts.map +1 -1
- package/dist/cmd/build/ast.js +7 -0
- package/dist/cmd/build/ast.js.map +1 -1
- package/dist/cmd/build/entry-generator.d.ts +20 -0
- package/dist/cmd/build/entry-generator.d.ts.map +1 -0
- package/dist/cmd/build/entry-generator.js +366 -0
- package/dist/cmd/build/entry-generator.js.map +1 -0
- package/dist/cmd/build/index.d.ts.map +1 -1
- package/dist/cmd/build/index.js +5 -23
- package/dist/cmd/build/index.js.map +1 -1
- package/dist/cmd/build/vite/agent-discovery.d.ts +33 -0
- package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -0
- package/dist/cmd/build/vite/agent-discovery.js +297 -0
- package/dist/cmd/build/vite/agent-discovery.js.map +1 -0
- package/dist/cmd/build/vite/browser-env-plugin.d.ts +9 -0
- package/dist/cmd/build/vite/browser-env-plugin.d.ts.map +1 -0
- package/dist/cmd/build/vite/browser-env-plugin.js +28 -0
- package/dist/cmd/build/vite/browser-env-plugin.js.map +1 -0
- package/dist/cmd/build/vite/bun-dev-server.d.ts +29 -0
- package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -0
- package/dist/cmd/build/vite/bun-dev-server.js +54 -0
- package/dist/cmd/build/vite/bun-dev-server.js.map +1 -0
- package/dist/cmd/build/vite/config-loader.d.ts +23 -0
- package/dist/cmd/build/vite/config-loader.d.ts.map +1 -0
- package/dist/cmd/build/vite/config-loader.js +50 -0
- package/dist/cmd/build/vite/config-loader.js.map +1 -0
- package/dist/cmd/build/vite/index.d.ts +26 -0
- package/dist/cmd/build/vite/index.d.ts.map +1 -0
- package/dist/cmd/build/vite/index.js +127 -0
- package/dist/cmd/build/vite/index.js.map +1 -0
- package/dist/cmd/build/vite/lifecycle-generator.d.ts +11 -0
- package/dist/cmd/build/vite/lifecycle-generator.d.ts.map +1 -0
- package/dist/cmd/build/vite/lifecycle-generator.js +35 -0
- package/dist/cmd/build/vite/lifecycle-generator.js.map +1 -0
- package/dist/cmd/build/vite/metadata-generator.d.ts +32 -0
- package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -0
- package/dist/cmd/build/vite/metadata-generator.js +489 -0
- package/dist/cmd/build/vite/metadata-generator.js.map +1 -0
- package/dist/cmd/build/vite/patch-plugin.d.ts +21 -0
- package/dist/cmd/build/vite/patch-plugin.d.ts.map +1 -0
- package/dist/cmd/build/vite/patch-plugin.js +70 -0
- package/dist/cmd/build/vite/patch-plugin.js.map +1 -0
- package/dist/cmd/build/vite/registry-generator.d.ts +19 -0
- package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -0
- package/dist/cmd/build/{route-registry.js → vite/registry-generator.js} +126 -48
- package/dist/cmd/build/vite/registry-generator.js.map +1 -0
- package/dist/cmd/build/vite/route-discovery.d.ts +58 -0
- package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -0
- package/dist/cmd/build/vite/route-discovery.js +125 -0
- package/dist/cmd/build/vite/route-discovery.js.map +1 -0
- package/dist/cmd/build/vite/server-bundler.d.ts +16 -0
- package/dist/cmd/build/vite/server-bundler.d.ts.map +1 -0
- package/dist/cmd/build/vite/server-bundler.js +194 -0
- package/dist/cmd/build/vite/server-bundler.js.map +1 -0
- package/dist/cmd/build/vite/vite-asset-server-config.d.ts +19 -0
- package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -0
- package/dist/cmd/build/vite/vite-asset-server-config.js +105 -0
- package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -0
- package/dist/cmd/build/vite/vite-asset-server.d.ts +23 -0
- package/dist/cmd/build/vite/vite-asset-server.d.ts.map +1 -0
- package/dist/cmd/build/vite/vite-asset-server.js +40 -0
- package/dist/cmd/build/vite/vite-asset-server.js.map +1 -0
- package/dist/cmd/build/vite/vite-builder.d.ts +44 -0
- package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -0
- package/dist/cmd/build/vite/vite-builder.js +232 -0
- package/dist/cmd/build/vite/vite-builder.js.map +1 -0
- package/dist/cmd/build/vite/workbench-generator.d.ts +10 -0
- package/dist/cmd/build/vite/workbench-generator.d.ts.map +1 -0
- package/dist/cmd/build/vite/workbench-generator.js +135 -0
- package/dist/cmd/build/vite/workbench-generator.js.map +1 -0
- package/dist/cmd/build/vite-bundler.d.ts +23 -0
- package/dist/cmd/build/vite-bundler.d.ts.map +1 -0
- package/dist/cmd/build/vite-bundler.js +79 -0
- package/dist/cmd/build/vite-bundler.js.map +1 -0
- package/dist/cmd/cloud/agent/get.d.ts.map +1 -1
- package/dist/cmd/cloud/agent/get.js +1 -0
- package/dist/cmd/cloud/agent/get.js.map +1 -1
- package/dist/cmd/cloud/agent/list.d.ts.map +1 -1
- package/dist/cmd/cloud/agent/list.js +1 -0
- package/dist/cmd/cloud/agent/list.js.map +1 -1
- package/dist/cmd/cloud/db/get.d.ts.map +1 -1
- package/dist/cmd/cloud/db/get.js +1 -0
- package/dist/cmd/cloud/db/get.js.map +1 -1
- package/dist/cmd/cloud/db/list.d.ts.map +1 -1
- package/dist/cmd/cloud/db/list.js +1 -0
- package/dist/cmd/cloud/db/list.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +152 -128
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/cloud/deployment/list.d.ts.map +1 -1
- package/dist/cmd/cloud/deployment/list.js +4 -0
- package/dist/cmd/cloud/deployment/list.js.map +1 -1
- package/dist/cmd/cloud/env/list.d.ts.map +1 -1
- package/dist/cmd/cloud/env/list.js +1 -0
- package/dist/cmd/cloud/env/list.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/get.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/get.js +1 -0
- package/dist/cmd/cloud/keyvalue/get.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/keys.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/keys.js +1 -0
- package/dist/cmd/cloud/keyvalue/keys.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/list-namespaces.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/list-namespaces.js +1 -0
- package/dist/cmd/cloud/keyvalue/list-namespaces.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/stats.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/stats.js +1 -0
- package/dist/cmd/cloud/keyvalue/stats.js.map +1 -1
- package/dist/cmd/cloud/secret/list.d.ts.map +1 -1
- package/dist/cmd/cloud/secret/list.js +1 -0
- package/dist/cmd/cloud/secret/list.js.map +1 -1
- package/dist/cmd/cloud/session/list.d.ts.map +1 -1
- package/dist/cmd/cloud/session/list.js +4 -0
- package/dist/cmd/cloud/session/list.js.map +1 -1
- package/dist/cmd/cloud/storage/get.d.ts.map +1 -1
- package/dist/cmd/cloud/storage/get.js +1 -0
- package/dist/cmd/cloud/storage/get.js.map +1 -1
- package/dist/cmd/cloud/storage/list.d.ts.map +1 -1
- package/dist/cmd/cloud/storage/list.js +3 -0
- package/dist/cmd/cloud/storage/list.js.map +1 -1
- package/dist/cmd/cloud/stream/get.d.ts.map +1 -1
- package/dist/cmd/cloud/stream/get.js +1 -0
- package/dist/cmd/cloud/stream/get.js.map +1 -1
- package/dist/cmd/cloud/stream/list.d.ts.map +1 -1
- package/dist/cmd/cloud/stream/list.js +1 -0
- package/dist/cmd/cloud/stream/list.js.map +1 -1
- package/dist/cmd/cloud/vector/get.d.ts.map +1 -1
- package/dist/cmd/cloud/vector/get.js +1 -0
- package/dist/cmd/cloud/vector/get.js.map +1 -1
- package/dist/cmd/cloud/vector/search.d.ts.map +1 -1
- package/dist/cmd/cloud/vector/search.js +1 -0
- package/dist/cmd/cloud/vector/search.js.map +1 -1
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +291 -751
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/dev/sync.d.ts +1 -1
- package/dist/cmd/dev/sync.d.ts.map +1 -1
- package/dist/cmd/dev/sync.js +3 -0
- package/dist/cmd/dev/sync.js.map +1 -1
- package/dist/cmd/profile/show.d.ts.map +1 -1
- package/dist/cmd/profile/show.js +3 -4
- package/dist/cmd/profile/show.js.map +1 -1
- package/dist/cmd/project/create.d.ts.map +1 -1
- package/dist/cmd/project/create.js +3 -4
- package/dist/cmd/project/create.js.map +1 -1
- package/dist/cmd/project/list.d.ts.map +1 -1
- package/dist/cmd/project/list.js +1 -0
- package/dist/cmd/project/list.js.map +1 -1
- package/dist/cmd/project/show.d.ts.map +1 -1
- package/dist/cmd/project/show.js +1 -0
- package/dist/cmd/project/show.js.map +1 -1
- package/dist/cmd/upgrade/index.d.ts.map +1 -1
- package/dist/cmd/upgrade/index.js +5 -3
- package/dist/cmd/upgrade/index.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +29 -11
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime-bootstrap.d.ts +3 -2
- package/dist/runtime-bootstrap.d.ts.map +1 -1
- package/dist/runtime-bootstrap.js +7 -2
- package/dist/runtime-bootstrap.js.map +1 -1
- package/dist/schemas/deploy.d.ts +1 -1
- package/dist/types.d.ts +40 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/bun-version-checker.d.ts +11 -0
- package/dist/utils/bun-version-checker.d.ts.map +1 -0
- package/dist/utils/bun-version-checker.js +56 -0
- package/dist/utils/bun-version-checker.js.map +1 -0
- package/dist/version-check.d.ts.map +1 -1
- package/dist/version-check.js +5 -2
- package/dist/version-check.js.map +1 -1
- package/package.json +10 -3
- package/src/auth.ts +9 -5
- package/src/banner.ts +1 -1
- package/src/cli.ts +228 -29
- package/src/cmd/auth/signup.ts +1 -1
- package/src/cmd/build/ast.ts +7 -0
- package/src/cmd/build/entry-generator.ts +404 -0
- package/src/cmd/build/index.ts +7 -28
- package/src/cmd/build/vite/agent-discovery.ts +467 -0
- package/src/cmd/build/vite/browser-env-plugin.ts +34 -0
- package/src/cmd/build/vite/bun-dev-server.ts +78 -0
- package/src/cmd/build/vite/config-loader.ts +70 -0
- package/src/cmd/build/vite/index.ts +166 -0
- package/src/cmd/build/vite/lifecycle-generator.ts +43 -0
- package/src/cmd/build/vite/metadata-generator.ts +602 -0
- package/src/cmd/build/vite/patch-plugin.ts +88 -0
- package/src/cmd/build/vite/registry-generator.ts +288 -0
- package/src/cmd/build/vite/route-discovery.ts +186 -0
- package/src/cmd/build/vite/server-bundler.ts +258 -0
- package/src/cmd/build/vite/vite-asset-server-config.ts +134 -0
- package/src/cmd/build/vite/vite-asset-server.ts +63 -0
- package/src/cmd/build/vite/vite-builder.ts +284 -0
- package/src/cmd/build/vite/workbench-generator.ts +152 -0
- package/src/cmd/build/vite-bundler.ts +110 -0
- package/src/cmd/cloud/agent/get.ts +2 -0
- package/src/cmd/cloud/agent/list.ts +1 -0
- package/src/cmd/cloud/db/get.ts +1 -0
- package/src/cmd/cloud/db/list.ts +1 -0
- package/src/cmd/cloud/deploy.ts +175 -144
- package/src/cmd/cloud/deployment/list.ts +4 -0
- package/src/cmd/cloud/env/list.ts +1 -0
- package/src/cmd/cloud/keyvalue/get.ts +1 -0
- package/src/cmd/cloud/keyvalue/keys.ts +1 -0
- package/src/cmd/cloud/keyvalue/list-namespaces.ts +1 -0
- package/src/cmd/cloud/keyvalue/stats.ts +2 -0
- package/src/cmd/cloud/secret/list.ts +1 -0
- package/src/cmd/cloud/session/list.ts +4 -0
- package/src/cmd/cloud/storage/get.ts +1 -0
- package/src/cmd/cloud/storage/list.ts +4 -0
- package/src/cmd/cloud/stream/get.ts +1 -0
- package/src/cmd/cloud/stream/list.ts +1 -0
- package/src/cmd/cloud/vector/get.ts +1 -0
- package/src/cmd/cloud/vector/search.ts +1 -0
- package/src/cmd/dev/index.ts +323 -914
- package/src/cmd/dev/sync.ts +5 -1
- package/src/cmd/profile/show.ts +3 -4
- package/src/cmd/project/create.ts +3 -4
- package/src/cmd/project/list.ts +1 -0
- package/src/cmd/project/show.ts +1 -0
- package/src/cmd/upgrade/index.ts +6 -3
- package/src/config.ts +31 -11
- package/src/index.ts +2 -0
- package/src/runtime-bootstrap.ts +8 -2
- package/src/types.ts +48 -1
- package/src/utils/bun-version-checker.ts +70 -0
- package/src/version-check.ts +6 -2
- package/dist/cmd/build/bundler.d.ts +0 -22
- package/dist/cmd/build/bundler.d.ts.map +0 -1
- package/dist/cmd/build/bundler.js +0 -766
- package/dist/cmd/build/bundler.js.map +0 -1
- package/dist/cmd/build/config-loader.d.ts +0 -16
- package/dist/cmd/build/config-loader.d.ts.map +0 -1
- package/dist/cmd/build/config-loader.js +0 -227
- package/dist/cmd/build/config-loader.js.map +0 -1
- package/dist/cmd/build/file.d.ts +0 -2
- package/dist/cmd/build/file.d.ts.map +0 -1
- package/dist/cmd/build/file.js +0 -10
- package/dist/cmd/build/file.js.map +0 -1
- package/dist/cmd/build/fix-duplicate-exports.d.ts +0 -2
- package/dist/cmd/build/fix-duplicate-exports.d.ts.map +0 -1
- package/dist/cmd/build/fix-duplicate-exports.js +0 -170
- package/dist/cmd/build/fix-duplicate-exports.js.map +0 -1
- package/dist/cmd/build/plugin.d.ts +0 -6
- package/dist/cmd/build/plugin.d.ts.map +0 -1
- package/dist/cmd/build/plugin.js +0 -645
- package/dist/cmd/build/plugin.js.map +0 -1
- package/dist/cmd/build/route-discovery.d.ts +0 -54
- package/dist/cmd/build/route-discovery.d.ts.map +0 -1
- package/dist/cmd/build/route-discovery.js +0 -148
- package/dist/cmd/build/route-discovery.js.map +0 -1
- package/dist/cmd/build/route-registry.d.ts +0 -38
- package/dist/cmd/build/route-registry.d.ts.map +0 -1
- package/dist/cmd/build/route-registry.js.map +0 -1
- package/src/cmd/build/bundler.ts +0 -927
- package/src/cmd/build/config-loader.ts +0 -268
- package/src/cmd/build/file.ts +0 -10
- package/src/cmd/build/fix-duplicate-exports.ts +0 -207
- package/src/cmd/build/plugin.ts +0 -782
- package/src/cmd/build/route-discovery.ts +0 -202
- package/src/cmd/build/route-registry.ts +0 -222
package/src/cmd/dev/index.ts
CHANGED
|
@@ -1,33 +1,52 @@
|
|
|
1
|
-
/** biome-ignore-all lint/style/useTemplate: its easier */
|
|
2
1
|
import { z } from 'zod';
|
|
3
2
|
import { resolve, join } from 'node:path';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
getDefaultConfigDir,
|
|
10
|
-
loadProjectSDKKey,
|
|
11
|
-
saveProjectDir,
|
|
12
|
-
saveConfig,
|
|
13
|
-
loadBuildMetadata,
|
|
14
|
-
} from '../../config';
|
|
15
|
-
import { type Config, createCommand } from '../../types';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { internalExit } from '@agentuity/runtime';
|
|
5
|
+
import { createCommand } from '../../types';
|
|
6
|
+
import { startBunDevServer } from '../build/vite/bun-dev-server';
|
|
16
7
|
import * as tui from '../../tui';
|
|
17
|
-
import {
|
|
8
|
+
import { getCommand } from '../../command-prefix';
|
|
18
9
|
import { generateEndpoint, type DevmodeResponse } from './api';
|
|
19
|
-
import { APIClient,
|
|
10
|
+
import { APIClient, getAPIBaseURL, getAppBaseURL, getGravityDevModeURL } from '../../api';
|
|
20
11
|
import { download } from './download';
|
|
21
12
|
import { createDevmodeSyncService } from './sync';
|
|
22
13
|
import { getDevmodeDeploymentId } from '../build/ast';
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
14
|
+
import { getDefaultConfigDir, saveConfig } from '../../config';
|
|
15
|
+
import type { Config } from '../../types';
|
|
16
|
+
|
|
17
|
+
const DEFAULT_PORT = 3500;
|
|
18
|
+
const MIN_PORT = 1024;
|
|
19
|
+
const MAX_PORT = 65535;
|
|
20
|
+
|
|
21
|
+
// Minimal interface for subprocess management
|
|
22
|
+
interface ProcessLike {
|
|
23
|
+
kill: (signal?: number | NodeJS.Signals) => void;
|
|
24
|
+
exitCode: number | null;
|
|
25
|
+
stdout?: AsyncIterable<Uint8Array>;
|
|
26
|
+
stderr?: AsyncIterable<Uint8Array>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface ServerLike {
|
|
30
|
+
close: () => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const getDefaultPort = (): number => {
|
|
34
|
+
const envPort = process.env.PORT;
|
|
35
|
+
if (!envPort) {
|
|
36
|
+
return DEFAULT_PORT;
|
|
37
|
+
}
|
|
38
|
+
const trimmed = envPort.trim();
|
|
39
|
+
if (!trimmed || !/^\d+$/.test(trimmed)) {
|
|
40
|
+
return DEFAULT_PORT;
|
|
41
|
+
}
|
|
42
|
+
const parsed = Number(trimmed);
|
|
43
|
+
if (!Number.isInteger(parsed) || parsed < MIN_PORT || parsed > MAX_PORT) {
|
|
44
|
+
return DEFAULT_PORT;
|
|
45
|
+
}
|
|
46
|
+
return parsed;
|
|
47
|
+
};
|
|
29
48
|
|
|
30
|
-
const shouldDisableInteractive= (interactive?: boolean) => {
|
|
49
|
+
const shouldDisableInteractive = (interactive?: boolean) => {
|
|
31
50
|
if (!interactive) {
|
|
32
51
|
return true;
|
|
33
52
|
}
|
|
@@ -56,32 +75,23 @@ export const command = createCommand({
|
|
|
56
75
|
.describe('Turn on or off the public url'),
|
|
57
76
|
port: z
|
|
58
77
|
.number()
|
|
59
|
-
.min(
|
|
60
|
-
.max(
|
|
61
|
-
.default(
|
|
62
|
-
.describe('The TCP port to start the dev
|
|
63
|
-
watch: z
|
|
64
|
-
.array(z.string())
|
|
65
|
-
.optional()
|
|
66
|
-
.describe(
|
|
67
|
-
'Additional paths to watch for changes (e.g., --watch ../packages/workbench/dist)'
|
|
68
|
-
),
|
|
78
|
+
.min(MIN_PORT)
|
|
79
|
+
.max(MAX_PORT)
|
|
80
|
+
.default(getDefaultPort())
|
|
81
|
+
.describe('The TCP port to start the dev server (also reads from PORT env)'),
|
|
69
82
|
}),
|
|
70
83
|
},
|
|
71
84
|
optional: { auth: 'Continue without an account (local only)', project: true },
|
|
72
85
|
|
|
73
86
|
async handler(ctx) {
|
|
74
|
-
const { opts, logger,
|
|
87
|
+
const { opts, logger, project, projectDir, auth } = ctx;
|
|
75
88
|
let { config } = ctx;
|
|
76
89
|
|
|
77
|
-
|
|
78
|
-
const useMockService = process.env.DEVMODE_SYNC_SERVICE_MOCK === 'true';
|
|
79
|
-
const apiClient = new APIClient(getAPIBaseURL(config), logger, config);
|
|
80
|
-
const syncService = createDevmodeSyncService({ logger, apiClient, mock: useMockService });
|
|
81
|
-
|
|
82
|
-
const rootDir = projectDir;
|
|
90
|
+
const rootDir = resolve(projectDir);
|
|
83
91
|
const appTs = join(rootDir, 'app.ts');
|
|
84
92
|
const srcDir = join(rootDir, 'src');
|
|
93
|
+
|
|
94
|
+
// Verify required files exist
|
|
85
95
|
const mustHaves = [join(rootDir, 'package.json'), appTs, srcDir];
|
|
86
96
|
const missing: string[] = [];
|
|
87
97
|
|
|
@@ -98,13 +108,17 @@ export const command = createCommand({
|
|
|
98
108
|
for (const filename of missing) {
|
|
99
109
|
tui.bullet(`Missing ${filename}`);
|
|
100
110
|
}
|
|
101
|
-
|
|
111
|
+
internalExit(1);
|
|
102
112
|
}
|
|
103
113
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
114
|
+
// Setup devmode and gravity (if using public URL)
|
|
115
|
+
const useMockService = process.env.DEVMODE_SYNC_SERVICE_MOCK === 'true';
|
|
116
|
+
const apiClient = auth ? new APIClient(getAPIBaseURL(config), logger, config) : null;
|
|
117
|
+
createDevmodeSyncService({
|
|
118
|
+
logger,
|
|
119
|
+
apiClient,
|
|
120
|
+
mock: useMockService,
|
|
121
|
+
});
|
|
108
122
|
|
|
109
123
|
let devmode: DevmodeResponse | undefined;
|
|
110
124
|
let gravityBin: string | undefined;
|
|
@@ -112,7 +126,7 @@ export const command = createCommand({
|
|
|
112
126
|
let appURL: string | undefined;
|
|
113
127
|
|
|
114
128
|
if (auth && project && opts.public) {
|
|
115
|
-
// Generate devmode endpoint
|
|
129
|
+
// Generate devmode endpoint for public URL
|
|
116
130
|
const endpoint = await tui.spinner({
|
|
117
131
|
message: 'Connecting to Gravity',
|
|
118
132
|
callback: () => {
|
|
@@ -120,6 +134,7 @@ export const command = createCommand({
|
|
|
120
134
|
},
|
|
121
135
|
clearOnSuccess: true,
|
|
122
136
|
});
|
|
137
|
+
|
|
123
138
|
const _config = { ...config } as Config;
|
|
124
139
|
_config.devmode = {
|
|
125
140
|
hostname: endpoint.hostname,
|
|
@@ -129,20 +144,12 @@ export const command = createCommand({
|
|
|
129
144
|
devmode = endpoint;
|
|
130
145
|
gravityURL = getGravityDevModeURL(project.region, config);
|
|
131
146
|
appURL = `${getAppBaseURL(config)}/r/${project.projectId}`;
|
|
132
|
-
logger.trace('gravity url: %s', gravityURL);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
logger.debug(
|
|
136
|
-
'Getting devmode deployment id for projectId: %s, endpointId: %s',
|
|
137
|
-
project?.projectId,
|
|
138
|
-
devmode?.id
|
|
139
|
-
);
|
|
140
|
-
const deploymentId = getDevmodeDeploymentId(project?.projectId ?? '', devmode?.id ?? '');
|
|
141
147
|
|
|
142
|
-
|
|
148
|
+
// Download gravity client
|
|
143
149
|
const configDir = getDefaultConfigDir();
|
|
144
150
|
const gravityDir = join(configDir, 'gravity');
|
|
145
151
|
let mustCheck = true;
|
|
152
|
+
|
|
146
153
|
if (
|
|
147
154
|
config?.gravity?.version &&
|
|
148
155
|
existsSync(join(gravityDir, config.gravity.version, 'gravity')) &&
|
|
@@ -153,6 +160,7 @@ export const command = createCommand({
|
|
|
153
160
|
gravityBin = join(gravityDir, config.gravity.version, 'gravity');
|
|
154
161
|
}
|
|
155
162
|
}
|
|
163
|
+
|
|
156
164
|
if (mustCheck) {
|
|
157
165
|
const res = await download(gravityDir);
|
|
158
166
|
gravityBin = res.filename;
|
|
@@ -166,12 +174,27 @@ export const command = createCommand({
|
|
|
166
174
|
}
|
|
167
175
|
}
|
|
168
176
|
|
|
169
|
-
|
|
177
|
+
// Get workbench info from config (new Vite approach)
|
|
178
|
+
const { loadAgentuityConfig, getWorkbenchConfig } = await import(
|
|
179
|
+
'../build/vite/config-loader'
|
|
180
|
+
);
|
|
181
|
+
const agentuityConfig = await loadAgentuityConfig(rootDir, ctx.logger);
|
|
182
|
+
const workbenchConfigData = getWorkbenchConfig(agentuityConfig, true); // dev mode
|
|
183
|
+
const workbench = {
|
|
184
|
+
hasWorkbench: workbenchConfigData.enabled,
|
|
185
|
+
config: workbenchConfigData.enabled
|
|
186
|
+
? { route: workbenchConfigData.route, headers: workbenchConfigData.headers }
|
|
187
|
+
: null,
|
|
188
|
+
};
|
|
170
189
|
|
|
171
|
-
const
|
|
172
|
-
interactive && !!(process.stdin.isTTY && process.stdout.isTTY && !process.env.CI);
|
|
190
|
+
const deploymentId = getDevmodeDeploymentId(project?.projectId ?? '', devmode?.id ?? '');
|
|
173
191
|
|
|
192
|
+
// Calculate URLs for banner
|
|
174
193
|
const padding = 12;
|
|
194
|
+
const workbenchUrl =
|
|
195
|
+
auth && project?.projectId
|
|
196
|
+
? `${getAppBaseURL(config)}/w/${project.projectId}`
|
|
197
|
+
: `http://127.0.0.1:${opts.port}${workbench.config?.route ?? '/workbench'}`;
|
|
175
198
|
|
|
176
199
|
const devmodebody =
|
|
177
200
|
tui.muted(tui.padRight('Local:', padding)) +
|
|
@@ -181,929 +204,315 @@ export const command = createCommand({
|
|
|
181
204
|
(devmode?.hostname ? tui.link(`https://${devmode.hostname}`) : tui.warn('Disabled')) +
|
|
182
205
|
'\n' +
|
|
183
206
|
tui.muted(tui.padRight('Workbench:', padding)) +
|
|
184
|
-
(workbench.hasWorkbench
|
|
185
|
-
? tui.link(`http://127.0.0.1:${opts.port}${workbench.config?.route ?? '/workbench'}`)
|
|
186
|
-
: tui.warn('Disabled')) +
|
|
207
|
+
(workbench.hasWorkbench ? tui.link(workbenchUrl) : tui.warn('Disabled')) +
|
|
187
208
|
'\n' +
|
|
188
209
|
tui.muted(tui.padRight('Dashboard:', padding)) +
|
|
189
210
|
(appURL ? tui.link(appURL) : tui.warn('Disabled')) +
|
|
190
211
|
'\n' +
|
|
191
|
-
(
|
|
212
|
+
(interactive
|
|
192
213
|
? '\n' + tui.muted('Press ') + tui.bold('h') + tui.muted(' for keyboard shortcuts')
|
|
193
214
|
: '');
|
|
194
215
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
centerTitle: false,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
showBanner();
|
|
205
|
-
|
|
206
|
-
// Load .env file(s) based on config profile (Bun no longer auto-loads .env files)
|
|
207
|
-
const isProduction = process.env.NODE_ENV === 'production' || config?.name !== 'local';
|
|
208
|
-
const envFiles = getEnvFilePaths(rootDir, {
|
|
209
|
-
configName: config?.name,
|
|
210
|
-
isProduction,
|
|
216
|
+
tui.banner('⨺ Agentuity DevMode', devmodebody, {
|
|
217
|
+
padding: 2,
|
|
218
|
+
topSpacer: false,
|
|
219
|
+
bottomSpacer: false,
|
|
220
|
+
centerTitle: false,
|
|
211
221
|
});
|
|
212
222
|
|
|
213
|
-
//
|
|
214
|
-
let
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const vars = await readEnvFile(envFilePath);
|
|
218
|
-
envVars = { ...envVars, ...vars };
|
|
219
|
-
logger.debug('Loaded environment variables from %s', envFilePath);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Start with process.env and merge in .env file vars
|
|
224
|
-
const env: Record<string, string | undefined> = { ...process.env, ...envVars };
|
|
225
|
-
env.AGENTUITY_SDK_DEV_MODE = 'true';
|
|
226
|
-
env.AGENTUITY_ENV = 'development';
|
|
227
|
-
env.NODE_ENV = 'development';
|
|
228
|
-
env.AGENTUITY_REGION = project?.region;
|
|
229
|
-
env.PORT = Number(opts.port).toFixed();
|
|
230
|
-
env.AGENTUITY_PORT = env.PORT;
|
|
231
|
-
const serviceUrls = getServiceUrls(project?.region);
|
|
232
|
-
if (options.logLevel !== undefined) env.AGENTUITY_LOG_LEVEL = options.logLevel;
|
|
233
|
-
// Pass through AGENTUITY_SDK_LOG_LEVEL for internal SDK logger
|
|
234
|
-
if (process.env.AGENTUITY_SDK_LOG_LEVEL) {
|
|
235
|
-
env.AGENTUITY_SDK_LOG_LEVEL = process.env.AGENTUITY_SDK_LOG_LEVEL;
|
|
236
|
-
}
|
|
237
|
-
env.AGENTUITY_FORCE_LOCAL_SERVICES = opts.local === true ? 'true' : 'false';
|
|
238
|
-
if (project) {
|
|
239
|
-
env.AGENTUITY_TRANSPORT_URL = serviceUrls.catalyst;
|
|
240
|
-
env.AGENTUITY_CATALYST_URL = serviceUrls.catalyst;
|
|
241
|
-
env.AGENTUITY_VECTOR_URL = serviceUrls.vector;
|
|
242
|
-
env.AGENTUITY_KEYVALUE_URL = serviceUrls.keyvalue;
|
|
243
|
-
env.AGENTUITY_STREAM_URL = serviceUrls.stream;
|
|
244
|
-
env.AGENTUITY_CLOUD_ORG_ID = project.orgId;
|
|
245
|
-
env.AGENTUITY_CLOUD_PROJECT_ID = project.projectId;
|
|
246
|
-
}
|
|
247
|
-
if (!process.stdout.isTTY) {
|
|
248
|
-
env.NO_COLOR = '1';
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const agentuityDir = resolve(rootDir, '.agentuity');
|
|
252
|
-
const appPath = resolve(agentuityDir, 'app.js');
|
|
253
|
-
|
|
254
|
-
// Load existing metadata file to use as previousMetadata for sync
|
|
255
|
-
// This prevents reinserting agents/evals that haven't changed
|
|
256
|
-
let previousMetadata: BuildMetadata | undefined;
|
|
257
|
-
try {
|
|
258
|
-
previousMetadata = await loadBuildMetadata(agentuityDir);
|
|
259
|
-
logger.debug(
|
|
260
|
-
'Loaded previous metadata with %d agent(s)',
|
|
261
|
-
previousMetadata.agents?.length ?? 0
|
|
262
|
-
);
|
|
263
|
-
} catch (_error) {
|
|
264
|
-
// File doesn't exist yet (first run), that's okay
|
|
265
|
-
logger.debug('No previous metadata file found, will treat all agents/evals as new');
|
|
266
|
-
previousMetadata = undefined;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Watch directories instead of files to survive atomic replacements (sed -i, cp)
|
|
270
|
-
const watches = [rootDir];
|
|
271
|
-
|
|
272
|
-
// Add additional watch paths from options
|
|
273
|
-
if (opts.watch) {
|
|
274
|
-
for (const watchPath of opts.watch) {
|
|
275
|
-
const resolvedPath = resolve(rootDir, watchPath);
|
|
276
|
-
if (existsSync(resolvedPath)) {
|
|
277
|
-
watches.push(resolvedPath);
|
|
278
|
-
logger.debug('Added additional watch path: %s', resolvedPath);
|
|
279
|
-
} else {
|
|
280
|
-
logger.warn('Watch path does not exist: %s', resolvedPath);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
const watchers: FSWatcher[] = [];
|
|
285
|
-
let failures = 0;
|
|
286
|
-
let running = false;
|
|
287
|
-
let pid = 0;
|
|
288
|
-
let failed = false;
|
|
289
|
-
let devServer: Bun.Subprocess | undefined;
|
|
290
|
-
let exitPromise: Promise<number> | undefined;
|
|
291
|
-
let restarting = false;
|
|
292
|
-
let shuttingDownForRestart = false;
|
|
293
|
-
let pendingRestart = false;
|
|
294
|
-
let building = false;
|
|
295
|
-
let buildCompletedAt = 0;
|
|
296
|
-
const BUILD_COOLDOWN_MS = 500; // Ignore file changes for 500ms after build completes
|
|
297
|
-
const templatedDirectories = new Map<string, number>(); // Track directories that just had templates created
|
|
298
|
-
let metadata: Partial<BuildMetadata> | undefined;
|
|
299
|
-
let showInitialReadyMessage = true;
|
|
300
|
-
let serverStartTime = 0;
|
|
301
|
-
let gravityClient: Bun.Subprocess | undefined;
|
|
302
|
-
let initialStartupComplete = false;
|
|
303
|
-
|
|
304
|
-
const sdkKey = await loadProjectSDKKey(logger, rootDir);
|
|
305
|
-
if (!sdkKey) {
|
|
306
|
-
tui.warning(`Couldn't find the AGENTUITY_SDK_KEY in ${rootDir} .env file`);
|
|
307
|
-
}
|
|
308
|
-
const gravityBinExists = gravityBin ? await Bun.file(gravityBin).exists() : true;
|
|
309
|
-
if (!gravityBinExists) {
|
|
310
|
-
logger.error(`Gravity binary not found at ${gravityBin}, skipping gravity client startup`);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
async function restartGravityClient() {
|
|
314
|
-
if (gravityClient) {
|
|
315
|
-
gravityClient.kill('SIGINT');
|
|
316
|
-
gravityClient.kill();
|
|
317
|
-
}
|
|
318
|
-
if (!devmode || !opts.public) {
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
try {
|
|
322
|
-
gravityClient = Bun.spawn(
|
|
323
|
-
[
|
|
324
|
-
gravityBin!,
|
|
325
|
-
'--endpoint-id',
|
|
326
|
-
devmode.id,
|
|
327
|
-
'--port',
|
|
328
|
-
env.PORT!,
|
|
329
|
-
'--url',
|
|
330
|
-
gravityURL!,
|
|
331
|
-
'--log-level',
|
|
332
|
-
process.env.AGENTUITY_GRAVITY_LOG_LEVEL ?? 'error',
|
|
333
|
-
],
|
|
334
|
-
{
|
|
335
|
-
cwd: rootDir,
|
|
336
|
-
stdout: 'inherit',
|
|
337
|
-
stderr: 'inherit',
|
|
338
|
-
stdin: 'ignore',
|
|
339
|
-
env: { ...env, AGENTUITY_SDK_KEY: sdkKey },
|
|
340
|
-
}
|
|
341
|
-
);
|
|
342
|
-
gravityClient.exited.then(() => {
|
|
343
|
-
logger.debug('gravity client exited');
|
|
344
|
-
});
|
|
345
|
-
} catch (err) {
|
|
346
|
-
logger.error(
|
|
347
|
-
'Failed to spawn gravity client: %s',
|
|
348
|
-
err instanceof Error ? err.message : String(err)
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// Track restart timestamps to detect restart loops
|
|
354
|
-
const restartTimestamps: number[] = [];
|
|
355
|
-
const MAX_RESTARTS = 10;
|
|
356
|
-
const TIME_WINDOW_MS = 10000; // 10 seconds
|
|
357
|
-
|
|
358
|
-
function checkRestartThrottle() {
|
|
359
|
-
const now = Date.now();
|
|
360
|
-
restartTimestamps.push(now);
|
|
361
|
-
|
|
362
|
-
// Remove timestamps older than the time window
|
|
363
|
-
while (restartTimestamps.length > 0 && now - restartTimestamps[0] > TIME_WINDOW_MS) {
|
|
364
|
-
restartTimestamps.shift();
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Check if we've exceeded the threshold
|
|
368
|
-
if (restartTimestamps.length >= MAX_RESTARTS) {
|
|
369
|
-
tui.error(`Detected ${MAX_RESTARTS} restarts in ${TIME_WINDOW_MS / 1000} seconds`);
|
|
370
|
-
tui.error(
|
|
371
|
-
'This usually indicates a file watcher loop (e.g., log files in the project root)'
|
|
372
|
-
);
|
|
373
|
-
tui.fatal('Too many rapid restarts, exiting to prevent infinite loop');
|
|
374
|
-
}
|
|
375
|
-
}
|
|
223
|
+
// Restart loop - allows server to restart on file changes
|
|
224
|
+
let shouldRestart = false;
|
|
225
|
+
let viteServer: ServerLike | null = null;
|
|
226
|
+
let gravityProcess: ProcessLike | null = null;
|
|
376
227
|
|
|
377
|
-
|
|
378
|
-
|
|
228
|
+
const restartServer = () => {
|
|
229
|
+
shouldRestart = true;
|
|
230
|
+
};
|
|
379
231
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
showedRestartMessage = false;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
232
|
+
const showWelcome = () => {
|
|
233
|
+
logger.info('DevMode ready 🚀');
|
|
234
|
+
};
|
|
386
235
|
|
|
387
|
-
function
|
|
388
|
-
|
|
389
|
-
failures++;
|
|
390
|
-
// Exit immediately on initial startup failure
|
|
391
|
-
if (!initialStartupComplete) {
|
|
392
|
-
tui.fatal(msg);
|
|
393
|
-
}
|
|
394
|
-
// During hot reload, show error but don't exit unless too many failures
|
|
395
|
-
if (failures >= 5) {
|
|
396
|
-
tui.error(msg);
|
|
397
|
-
tui.fatal('too many failures, exiting');
|
|
398
|
-
} else {
|
|
399
|
-
// Ensure we're on a new line before printing error
|
|
400
|
-
tui.error(msg);
|
|
401
|
-
// Track lines: 1 for "✗ Building..." + 1 for error message
|
|
402
|
-
lastErrorLineCount = 2;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
236
|
+
// Make restart function available globally for HMR plugin
|
|
237
|
+
(globalThis as Record<string, unknown>).__AGENTUITY_RESTART__ = restartServer;
|
|
405
238
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
for (let i = 0; i < lastErrorLineCount; i++) {
|
|
410
|
-
process.stdout.write('\x1b[1A\x1b[2K');
|
|
411
|
-
}
|
|
412
|
-
lastErrorLineCount = 0;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
239
|
+
// Setup signal handlers once before the loop
|
|
240
|
+
const cleanup = async () => {
|
|
241
|
+
tui.info('Shutting down...');
|
|
415
242
|
|
|
416
|
-
|
|
417
|
-
if (
|
|
418
|
-
|
|
419
|
-
return;
|
|
243
|
+
// Close Vite asset server first
|
|
244
|
+
if (viteServer) {
|
|
245
|
+
await viteServer.close();
|
|
420
246
|
}
|
|
421
247
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
running = false;
|
|
425
|
-
process.kill(pid, 'SIGINT');
|
|
426
|
-
try {
|
|
427
|
-
// Kill the process group (negative PID kills entire group)
|
|
428
|
-
process.kill(-pid, 'SIGTERM');
|
|
429
|
-
logger.trace('Sent SIGTERM to process group');
|
|
430
|
-
} catch {
|
|
431
|
-
// Fallback: kill the direct process
|
|
248
|
+
// Kill gravity client with SIGTERM first, then SIGKILL as fallback
|
|
249
|
+
if (gravityProcess) {
|
|
432
250
|
try {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
251
|
+
gravityProcess.kill('SIGTERM');
|
|
252
|
+
// Give it a moment to gracefully shutdown
|
|
253
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
254
|
+
if (gravityProcess.exitCode === null) {
|
|
255
|
+
gravityProcess.kill('SIGKILL');
|
|
436
256
|
}
|
|
437
|
-
} catch {
|
|
438
|
-
|
|
439
|
-
logger.trace('Process already dead');
|
|
257
|
+
} catch (err) {
|
|
258
|
+
logger.debug('Error killing gravity process: %s', err);
|
|
440
259
|
}
|
|
441
260
|
}
|
|
442
261
|
|
|
443
|
-
|
|
444
|
-
if (exitPromise) {
|
|
445
|
-
logger.trace('Waiting for dev server to exit...');
|
|
446
|
-
await exitPromise;
|
|
447
|
-
logger.trace('Dev server exited');
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
devServer = undefined;
|
|
451
|
-
exitPromise = undefined;
|
|
452
|
-
shuttingDownForRestart = false;
|
|
453
|
-
};
|
|
454
|
-
|
|
455
|
-
// Handle signals to ensure entire process tree is killed
|
|
456
|
-
const cleanup = (exitCode = 0) => {
|
|
457
|
-
logger.trace('cleanup() called with exitCode=%d', exitCode);
|
|
458
|
-
if (gravityClient) {
|
|
459
|
-
logger.debug('calling kill on gravity client with pid: %d', gravityClient.pid);
|
|
460
|
-
gravityClient.kill('SIGINT');
|
|
461
|
-
gravityClient = undefined;
|
|
462
|
-
}
|
|
463
|
-
if (pid && running) {
|
|
464
|
-
kill();
|
|
465
|
-
}
|
|
466
|
-
for (const watcher of watchers) {
|
|
467
|
-
watcher.close();
|
|
468
|
-
}
|
|
469
|
-
watchers.length = 0;
|
|
470
|
-
process.exit(exitCode);
|
|
262
|
+
internalExit(0);
|
|
471
263
|
};
|
|
472
264
|
|
|
473
265
|
process.on('SIGINT', cleanup);
|
|
474
266
|
process.on('SIGTERM', cleanup);
|
|
475
|
-
|
|
267
|
+
|
|
268
|
+
// Ensure gravity client is always killed on exit (even if cleanup is bypassed)
|
|
269
|
+
// Use SIGKILL for immediate termination since the process is already exiting
|
|
476
270
|
process.on('exit', () => {
|
|
477
|
-
|
|
478
|
-
if (gravityClient) {
|
|
271
|
+
if (gravityProcess && gravityProcess.exitCode === null) {
|
|
479
272
|
try {
|
|
480
|
-
|
|
481
|
-
} catch {
|
|
482
|
-
// Ignore errors
|
|
273
|
+
gravityProcess.kill('SIGKILL');
|
|
274
|
+
} catch (_err) {
|
|
275
|
+
// Ignore errors during exit cleanup
|
|
483
276
|
}
|
|
484
277
|
}
|
|
485
278
|
});
|
|
486
279
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
if (restarting) {
|
|
490
|
-
logger.trace('Restart already in progress, queuing another restart');
|
|
491
|
-
pendingRestart = true;
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
280
|
+
while (true) {
|
|
281
|
+
shouldRestart = false;
|
|
494
282
|
|
|
495
|
-
logger.trace('restart() called, restarting=%s, running=%s', restarting, running);
|
|
496
|
-
restarting = true;
|
|
497
|
-
pendingRestart = false;
|
|
498
|
-
failed = false;
|
|
499
283
|
try {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
clearRestartMessage();
|
|
526
|
-
|
|
527
|
-
try {
|
|
528
|
-
await tui.spinner({
|
|
529
|
-
message: 'Building...',
|
|
530
|
-
clearOnSuccess: true,
|
|
531
|
-
callback: async () => {
|
|
532
|
-
logger.trace('Bundle starting...');
|
|
533
|
-
building = true;
|
|
534
|
-
await bundle({
|
|
535
|
-
rootDir,
|
|
536
|
-
dev: true,
|
|
537
|
-
projectId: project?.projectId,
|
|
538
|
-
deploymentId,
|
|
539
|
-
port: opts.port,
|
|
540
|
-
region: project?.region ?? 'local',
|
|
541
|
-
logger,
|
|
542
|
-
workbench,
|
|
543
|
-
});
|
|
544
|
-
building = false;
|
|
545
|
-
buildCompletedAt = Date.now();
|
|
546
|
-
logger.trace('Bundle completed successfully');
|
|
547
|
-
logger.trace('tsc starting...');
|
|
548
|
-
const tscExitCode = await tui.runCommand({
|
|
549
|
-
command: 'tsc',
|
|
550
|
-
cmd: ['bunx', 'tsc', '--noEmit'],
|
|
551
|
-
cwd: rootDir,
|
|
552
|
-
clearOnSuccess: true,
|
|
553
|
-
truncate: false,
|
|
554
|
-
maxLinesOutput: 2,
|
|
555
|
-
maxLinesOnFailure: 15,
|
|
556
|
-
});
|
|
557
|
-
if (tscExitCode !== 0) {
|
|
558
|
-
logger.trace('tsc failed with exit code %d', tscExitCode);
|
|
559
|
-
failure('Type check failed');
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
562
|
-
logger.trace('tsc completed successfully');
|
|
563
|
-
await restartGravityClient();
|
|
564
|
-
},
|
|
565
|
-
});
|
|
566
|
-
} catch (error) {
|
|
567
|
-
building = false;
|
|
568
|
-
const e = error as Error;
|
|
569
|
-
if (e.constructor.name === 'AggregateError') {
|
|
570
|
-
const ex = e as AggregateError;
|
|
571
|
-
for (const err of ex.errors) {
|
|
572
|
-
if (err) {
|
|
573
|
-
failure(err);
|
|
574
|
-
return;
|
|
575
|
-
}
|
|
284
|
+
// Generate entry file for Vite before starting dev server
|
|
285
|
+
await tui.spinner({
|
|
286
|
+
message: 'Generating entry file',
|
|
287
|
+
callback: async () => {
|
|
288
|
+
const { generateEntryFile } = await import('../build/entry-generator');
|
|
289
|
+
await generateEntryFile({
|
|
290
|
+
rootDir,
|
|
291
|
+
projectId: project?.projectId ?? '',
|
|
292
|
+
deploymentId,
|
|
293
|
+
logger,
|
|
294
|
+
mode: 'dev',
|
|
295
|
+
});
|
|
296
|
+
},
|
|
297
|
+
clearOnSuccess: true,
|
|
298
|
+
});
|
|
299
|
+
} catch (error) {
|
|
300
|
+
tui.error(`Failed to generate entry file: ${error}`);
|
|
301
|
+
tui.warn('Waiting for file changes to retry...');
|
|
302
|
+
|
|
303
|
+
// Wait for next restart trigger
|
|
304
|
+
await new Promise<void>((resolve) => {
|
|
305
|
+
const checkRestart = setInterval(() => {
|
|
306
|
+
if (shouldRestart) {
|
|
307
|
+
clearInterval(checkRestart);
|
|
308
|
+
resolve();
|
|
576
309
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
logger.trace('Typecheck and build completed');
|
|
585
|
-
|
|
586
|
-
if (failed) {
|
|
587
|
-
logger.trace('Restart failed, returning early');
|
|
588
|
-
return;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
// Reset failure counter on successful build
|
|
592
|
-
failures = 0;
|
|
310
|
+
}, 100);
|
|
311
|
+
});
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
593
314
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
315
|
+
try {
|
|
316
|
+
// Start Bun dev server (with Vite asset server for HMR)
|
|
317
|
+
const result = await startBunDevServer({
|
|
318
|
+
rootDir,
|
|
319
|
+
port: opts.port,
|
|
320
|
+
projectId: project?.projectId,
|
|
321
|
+
orgId: project?.orgId,
|
|
322
|
+
deploymentId,
|
|
323
|
+
logger,
|
|
324
|
+
});
|
|
601
325
|
|
|
602
|
-
|
|
603
|
-
|
|
326
|
+
viteServer = result.viteAssetServer.server;
|
|
327
|
+
// Note: Bun server runs in-process, no separate app process needed
|
|
604
328
|
|
|
605
|
-
//
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
if (
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
'Agent: id=%s, name=%s, version=%s, evals=%d',
|
|
619
|
-
agent.id,
|
|
620
|
-
agent.name,
|
|
621
|
-
agent.version,
|
|
622
|
-
agent.evals?.length ?? 0
|
|
623
|
-
);
|
|
624
|
-
if (agent.evals) {
|
|
625
|
-
for (const evalItem of agent.evals) {
|
|
626
|
-
logger.debug(
|
|
627
|
-
' Eval: id=%s, name=%s, version=%s',
|
|
628
|
-
evalItem.id,
|
|
629
|
-
evalItem.name,
|
|
630
|
-
evalItem.version
|
|
631
|
-
);
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
}
|
|
329
|
+
// Wait for app.ts to finish loading (Vite is ready but app may still be initializing)
|
|
330
|
+
// Give it 2 seconds to ensure app initialization completes
|
|
331
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
332
|
+
} catch (error) {
|
|
333
|
+
tui.error(`Failed to start dev server: ${error}`);
|
|
334
|
+
tui.warn('Waiting for file changes to retry...');
|
|
335
|
+
|
|
336
|
+
// Wait for next restart trigger
|
|
337
|
+
await new Promise<void>((resolve) => {
|
|
338
|
+
const checkRestart = setInterval(() => {
|
|
339
|
+
if (shouldRestart) {
|
|
340
|
+
clearInterval(checkRestart);
|
|
341
|
+
resolve();
|
|
635
342
|
}
|
|
636
|
-
|
|
343
|
+
}, 100);
|
|
344
|
+
});
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
637
347
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
348
|
+
try {
|
|
349
|
+
// Start gravity client if we have devmode
|
|
350
|
+
if (gravityBin && gravityURL && devmode) {
|
|
351
|
+
logger.trace('Starting gravity client: %s', gravityBin);
|
|
352
|
+
gravityProcess = Bun.spawn(
|
|
353
|
+
[
|
|
354
|
+
gravityBin,
|
|
355
|
+
'--endpoint-id',
|
|
356
|
+
devmode.id,
|
|
357
|
+
'--port',
|
|
358
|
+
opts.port.toString(),
|
|
359
|
+
'--url',
|
|
360
|
+
gravityURL,
|
|
361
|
+
'--log-level',
|
|
362
|
+
process.env.AGENTUITY_GRAVITY_LOG_LEVEL ?? 'error',
|
|
363
|
+
],
|
|
364
|
+
{
|
|
365
|
+
cwd: rootDir,
|
|
366
|
+
stdout: 'pipe',
|
|
367
|
+
stderr: 'pipe',
|
|
368
|
+
detached: false, // Ensure gravity dies with parent process
|
|
650
369
|
}
|
|
651
|
-
// Don't fail the build, just log the error
|
|
652
|
-
}
|
|
653
|
-
} else {
|
|
654
|
-
logger.trace(
|
|
655
|
-
'Skipping sync - auth=%s, project=%s, devmode=%s, apiClient=%s',
|
|
656
|
-
!!auth,
|
|
657
|
-
!!project,
|
|
658
|
-
!!devmode,
|
|
659
|
-
!!apiClient
|
|
660
370
|
);
|
|
661
|
-
}
|
|
662
371
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
stderr: 'inherit',
|
|
671
|
-
stdin: process.stdin.isTTY ? 'ignore' : 'inherit', // Don't inherit stdin, we handle it ourselves
|
|
672
|
-
env,
|
|
673
|
-
});
|
|
674
|
-
|
|
675
|
-
logger.trace('Dev server process spawned, setting up state...');
|
|
676
|
-
running = true;
|
|
677
|
-
failed = false;
|
|
678
|
-
pid = devServer.pid;
|
|
679
|
-
exitPromise = devServer.exited;
|
|
680
|
-
serverStartTime = Date.now();
|
|
681
|
-
logger.trace('Dev server started (pid: %d)', pid);
|
|
682
|
-
|
|
683
|
-
if (showInitialReadyMessage) {
|
|
684
|
-
showInitialReadyMessage = false;
|
|
685
|
-
logger.info('DevMode ready 🚀');
|
|
686
|
-
logger.trace('Initial ready message logged');
|
|
687
|
-
// Mark initial startup complete immediately to prevent watcher restarts
|
|
688
|
-
initialStartupComplete = true;
|
|
689
|
-
logger.trace('Initial startup complete, file watcher restarts now enabled');
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
// Notify workbench clients that the server is alive and ready
|
|
693
|
-
// Use setTimeout to ensure server is fully ready before notifying
|
|
694
|
-
setTimeout(async () => {
|
|
695
|
-
await notifyWorkbenchClients({
|
|
696
|
-
port: opts.port,
|
|
697
|
-
message: 'alive',
|
|
698
|
-
});
|
|
699
|
-
}, 500);
|
|
700
|
-
|
|
701
|
-
logger.trace('Attaching exit handler to dev server process...');
|
|
702
|
-
// Attach non-blocking exit handler
|
|
703
|
-
exitPromise
|
|
704
|
-
.then((exitCode) => {
|
|
705
|
-
const runtime = Date.now() - serverStartTime;
|
|
706
|
-
logger.trace(
|
|
707
|
-
'Dev server exited with code %d (shuttingDownForRestart=%s, runtime=%dms)',
|
|
708
|
-
exitCode,
|
|
709
|
-
shuttingDownForRestart,
|
|
710
|
-
runtime
|
|
711
|
-
);
|
|
712
|
-
running = false;
|
|
713
|
-
devServer = undefined;
|
|
714
|
-
exitPromise = undefined;
|
|
715
|
-
// If server exited immediately after starting (< 2 seconds), treat as failure and restart
|
|
716
|
-
if (runtime < 2000 && !shuttingDownForRestart) {
|
|
717
|
-
logger.trace('Server exited too quickly, treating as failure and restarting');
|
|
718
|
-
failure('Server exited immediately after starting');
|
|
719
|
-
// Trigger a restart after a short delay
|
|
720
|
-
setTimeout(() => {
|
|
721
|
-
if (!running && !restarting) {
|
|
722
|
-
logger.trace('Triggering restart after quick exit');
|
|
723
|
-
restart();
|
|
372
|
+
// Log gravity output
|
|
373
|
+
(async () => {
|
|
374
|
+
try {
|
|
375
|
+
if (gravityProcess?.stdout) {
|
|
376
|
+
for await (const chunk of gravityProcess.stdout) {
|
|
377
|
+
const text = new TextDecoder().decode(chunk);
|
|
378
|
+
logger.debug('[gravity] %s', text.trim());
|
|
724
379
|
}
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
// Only exit the CLI if this is a clean exit AND not a restart AND server ran for a while
|
|
729
|
-
if (exitCode === 0 && !shuttingDownForRestart && runtime >= 2000) {
|
|
730
|
-
logger.trace('Clean exit, stopping CLI');
|
|
731
|
-
cleanup(exitCode);
|
|
732
|
-
}
|
|
733
|
-
// Non-zero exit codes are treated as restartable failures
|
|
734
|
-
// But if it's exit code 1 (common error exit), also exit the CLI
|
|
735
|
-
if (exitCode === 1 && !shuttingDownForRestart && runtime >= 2000) {
|
|
736
|
-
logger.trace('Server exited with error code 1, stopping CLI');
|
|
737
|
-
cleanup(exitCode);
|
|
380
|
+
}
|
|
381
|
+
} catch (err) {
|
|
382
|
+
logger.error('Error reading gravity stdout: %s', err);
|
|
738
383
|
}
|
|
739
|
-
})
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
exitPromise = undefined;
|
|
749
|
-
if (!shuttingDownForRestart) {
|
|
750
|
-
if (error instanceof Error) {
|
|
751
|
-
failure(`Dev server failed: ${error.message}`);
|
|
752
|
-
} else {
|
|
753
|
-
failure('Dev server failed');
|
|
384
|
+
})();
|
|
385
|
+
|
|
386
|
+
(async () => {
|
|
387
|
+
try {
|
|
388
|
+
if (gravityProcess?.stderr) {
|
|
389
|
+
for await (const chunk of gravityProcess.stderr) {
|
|
390
|
+
const text = new TextDecoder().decode(chunk);
|
|
391
|
+
logger.warn('[gravity] %s', text.trim());
|
|
392
|
+
}
|
|
754
393
|
}
|
|
394
|
+
} catch (err) {
|
|
395
|
+
logger.error('Error reading gravity stderr: %s', err);
|
|
755
396
|
}
|
|
756
|
-
});
|
|
757
|
-
} catch (error) {
|
|
758
|
-
logger.trace('Restart caught error: %s', error);
|
|
759
|
-
if (error instanceof Error) {
|
|
760
|
-
logger.trace('Error message: %s, stack: %s', error.message, error.stack);
|
|
761
|
-
failure(`Dev server failed: ${error.message}`);
|
|
762
|
-
} else {
|
|
763
|
-
logger.trace('Non-Error exception: %s', String(error));
|
|
764
|
-
failure('Dev server failed');
|
|
765
|
-
}
|
|
766
|
-
running = false;
|
|
767
|
-
devServer = undefined;
|
|
768
|
-
} finally {
|
|
769
|
-
logger.trace('Entering restart() finally block...');
|
|
770
|
-
const hadPendingRestart = pendingRestart;
|
|
771
|
-
restarting = false;
|
|
772
|
-
pendingRestart = false;
|
|
773
|
-
logger.trace(
|
|
774
|
-
'restart() completed, restarting=%s, hadPendingRestart=%s',
|
|
775
|
-
restarting,
|
|
776
|
-
hadPendingRestart
|
|
777
|
-
);
|
|
397
|
+
})();
|
|
778
398
|
|
|
779
|
-
|
|
780
|
-
if (hadPendingRestart) {
|
|
781
|
-
logger.trace('Triggering queued restart');
|
|
782
|
-
setImmediate(restart);
|
|
399
|
+
logger.debug('Gravity client started');
|
|
783
400
|
}
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
logger.trace('Starting initial build and server');
|
|
788
|
-
await restart();
|
|
789
|
-
logger.trace('Initial restart completed, setting up watchers');
|
|
790
|
-
logger.trace('initialStartupComplete is now: %s', initialStartupComplete);
|
|
791
401
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
process.stdin.setRawMode(true);
|
|
796
|
-
process.stdin.resume();
|
|
797
|
-
process.stdin.setEncoding('utf8');
|
|
402
|
+
// Sync service integration
|
|
403
|
+
// TODO: Integrate sync service with Vite's buildStart/buildEnd hooks
|
|
404
|
+
// The sync service will be called when metadata changes are detected
|
|
798
405
|
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
process.stdin.on('data', (data) => {
|
|
820
|
-
const key = data.toString();
|
|
406
|
+
// Handle keyboard shortcuts
|
|
407
|
+
if (interactive && process.stdin.isTTY && process.stdout.isTTY) {
|
|
408
|
+
process.stdin.setRawMode(true);
|
|
409
|
+
process.stdin.resume();
|
|
410
|
+
process.stdin.setEncoding('utf8');
|
|
411
|
+
|
|
412
|
+
const showHelp = () => {
|
|
413
|
+
console.log('\n' + tui.bold('Keyboard Shortcuts:'));
|
|
414
|
+
console.log(tui.muted(' h') + ' - show this help');
|
|
415
|
+
console.log(tui.muted(' c') + ' - clear console');
|
|
416
|
+
console.log(tui.muted(' q') + ' - quit\n');
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
process.stdin.on('data', (data) => {
|
|
420
|
+
const key = data.toString();
|
|
421
|
+
|
|
422
|
+
// Handle Ctrl+C
|
|
423
|
+
if (key === '\u0003') {
|
|
424
|
+
internalExit(0);
|
|
425
|
+
}
|
|
821
426
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
427
|
+
switch (key) {
|
|
428
|
+
case 'h':
|
|
429
|
+
showHelp();
|
|
430
|
+
break;
|
|
431
|
+
case 'c':
|
|
432
|
+
console.clear();
|
|
433
|
+
tui.banner('⨺ Agentuity DevMode', devmodebody, {
|
|
434
|
+
padding: 2,
|
|
435
|
+
topSpacer: false,
|
|
436
|
+
bottomSpacer: false,
|
|
437
|
+
centerTitle: false,
|
|
438
|
+
});
|
|
439
|
+
break;
|
|
440
|
+
case 'q':
|
|
441
|
+
internalExit(0);
|
|
442
|
+
break;
|
|
443
|
+
default:
|
|
444
|
+
console.log(data);
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
});
|
|
826
448
|
}
|
|
827
449
|
|
|
828
|
-
|
|
829
|
-
switch (key) {
|
|
830
|
-
case 'h':
|
|
831
|
-
showHelp();
|
|
832
|
-
break;
|
|
833
|
-
case 'c':
|
|
834
|
-
console.clear();
|
|
835
|
-
showBanner();
|
|
836
|
-
break;
|
|
837
|
-
case 'r':
|
|
838
|
-
tui.info('Manually restarting server...');
|
|
839
|
-
restart();
|
|
840
|
-
break;
|
|
841
|
-
case 'o':
|
|
842
|
-
showRoutes();
|
|
843
|
-
break;
|
|
844
|
-
case 'a':
|
|
845
|
-
showAgents();
|
|
846
|
-
break;
|
|
847
|
-
case 'q':
|
|
848
|
-
tui.info('Shutting down...');
|
|
849
|
-
cleanup();
|
|
850
|
-
break;
|
|
851
|
-
}
|
|
852
|
-
});
|
|
450
|
+
showWelcome();
|
|
853
451
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
process.stdin.on('data', (data) => {
|
|
863
|
-
const key = data.toString();
|
|
864
|
-
// Handle Ctrl+C
|
|
865
|
-
if (key === '\u0003') {
|
|
866
|
-
cleanup();
|
|
867
|
-
return;
|
|
868
|
-
}
|
|
452
|
+
// Wait for restart signal
|
|
453
|
+
await new Promise<void>((resolve) => {
|
|
454
|
+
const checkRestart = setInterval(() => {
|
|
455
|
+
if (shouldRestart) {
|
|
456
|
+
clearInterval(checkRestart);
|
|
457
|
+
resolve();
|
|
458
|
+
}
|
|
459
|
+
}, 100);
|
|
869
460
|
});
|
|
870
|
-
}
|
|
871
|
-
logger.trace('❌ Keyboard shortcuts disabled');
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
// Patterns to ignore (generated files that change during build)
|
|
875
|
-
const ignorePatterns = [
|
|
876
|
-
/\.generated\.(js|ts|d\.ts)$/,
|
|
877
|
-
/registry\.generated\.ts$/,
|
|
878
|
-
/types\.generated\.d\.ts$/,
|
|
879
|
-
/client\.generated\.js$/,
|
|
880
|
-
/\.tmp$/,
|
|
881
|
-
/\.tsbuildinfo$/,
|
|
882
|
-
/\.agentuity\//,
|
|
883
|
-
// Ignore temporary files created by sed (e.g., sedUprJj0)
|
|
884
|
-
/\/sed[A-Za-z0-9]+$/,
|
|
885
|
-
];
|
|
886
|
-
|
|
887
|
-
// Helper to check if a file is a temporary file created by sed
|
|
888
|
-
const isSedTempFile = (filePath: string): boolean => {
|
|
889
|
-
const basename = filePath.split('/').pop() || '';
|
|
890
|
-
return /^sed[A-Za-z0-9]+$/.test(basename);
|
|
891
|
-
};
|
|
892
|
-
|
|
893
|
-
logger.trace('Setting up file watchers for: %s', watches.join(', '));
|
|
894
|
-
for (const watchDir of watches) {
|
|
895
|
-
try {
|
|
896
|
-
logger.trace('Setting up watcher for %s', watchDir);
|
|
897
|
-
const watcher = watch(watchDir, { recursive: true }, (eventType, changedFile) => {
|
|
898
|
-
const absPath = changedFile ? resolve(watchDir, changedFile) : watchDir;
|
|
899
|
-
|
|
900
|
-
// Ignore file changes during initial startup to prevent spurious restarts
|
|
901
|
-
if (!initialStartupComplete) {
|
|
902
|
-
logger.trace(
|
|
903
|
-
'File change ignored (initial startup): %s (event: %s, file: %s)',
|
|
904
|
-
watchDir,
|
|
905
|
-
eventType,
|
|
906
|
-
changedFile
|
|
907
|
-
);
|
|
908
|
-
return;
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
// Ignore file changes during active build to prevent loops
|
|
912
|
-
if (building) {
|
|
913
|
-
logger.trace(
|
|
914
|
-
'File change ignored (build in progress): %s (event: %s, file: %s)',
|
|
915
|
-
watchDir,
|
|
916
|
-
eventType,
|
|
917
|
-
changedFile
|
|
918
|
-
);
|
|
919
|
-
return;
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
// Ignore file changes immediately after build completes (cooldown period)
|
|
923
|
-
// This prevents restarts from build output files that are written asynchronously
|
|
924
|
-
if (buildCompletedAt > 0 && Date.now() - buildCompletedAt < BUILD_COOLDOWN_MS) {
|
|
925
|
-
logger.trace(
|
|
926
|
-
'File change ignored (build cooldown): %s (event: %s, file: %s)',
|
|
927
|
-
watchDir,
|
|
928
|
-
eventType,
|
|
929
|
-
changedFile
|
|
930
|
-
);
|
|
931
|
-
return;
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
// Ignore node_modules folder
|
|
935
|
-
if (absPath.includes('node_modules')) {
|
|
936
|
-
logger.trace(
|
|
937
|
-
'File change ignored (node_modules): %s (event: %s, file: %s)',
|
|
938
|
-
watchDir,
|
|
939
|
-
eventType,
|
|
940
|
-
changedFile
|
|
941
|
-
);
|
|
942
|
-
return;
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
// Ignore .git folder
|
|
946
|
-
if (changedFile && (changedFile === '.git' || changedFile.startsWith('.git/'))) {
|
|
947
|
-
logger.trace(
|
|
948
|
-
'File change ignored (.git folder): %s (event: %s, file: %s)',
|
|
949
|
-
watchDir,
|
|
950
|
-
eventType,
|
|
951
|
-
changedFile
|
|
952
|
-
);
|
|
953
|
-
return;
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
// Ignore changes in .agentuity directory (build output)
|
|
957
|
-
// Check both relative path and normalized absolute path
|
|
958
|
-
const isInAgentuityDir =
|
|
959
|
-
(changedFile &&
|
|
960
|
-
(changedFile === '.agentuity' || changedFile.startsWith('.agentuity/'))) ||
|
|
961
|
-
resolve(absPath).startsWith(agentuityDir);
|
|
962
|
-
if (isInAgentuityDir) {
|
|
963
|
-
logger.trace(
|
|
964
|
-
'File change ignored (.agentuity dir): %s (event: %s, file: %s)',
|
|
965
|
-
watchDir,
|
|
966
|
-
eventType,
|
|
967
|
-
changedFile
|
|
968
|
-
);
|
|
969
|
-
return;
|
|
970
|
-
}
|
|
971
461
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
logger.trace(
|
|
975
|
-
'File change ignored (static assets dir): %s (event: %s, file: %s)',
|
|
976
|
-
watchDir,
|
|
977
|
-
eventType,
|
|
978
|
-
changedFile
|
|
979
|
-
);
|
|
980
|
-
return;
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
// Check for .tmp file renames that replace watched files (BEFORE ignoring)
|
|
984
|
-
// This handles cases like sed -i.tmp where agent.ts.tmp is renamed to agent.ts
|
|
985
|
-
if (eventType === 'rename' && changedFile && changedFile.endsWith('.tmp')) {
|
|
986
|
-
const targetFile = changedFile.slice(0, -4); // Remove .tmp suffix
|
|
987
|
-
const targetAbsPath = resolve(watchDir, targetFile);
|
|
988
|
-
|
|
989
|
-
// Only trigger restart for source files (ts, tsx, js, jsx, etc.)
|
|
990
|
-
const isSourceFile = /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(targetFile);
|
|
462
|
+
// Restart triggered - cleanup and loop
|
|
463
|
+
tui.info('Restarting server...');
|
|
991
464
|
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
(targetFile &&
|
|
997
|
-
(targetFile === '.agentuity' || targetFile.startsWith('.agentuity/'))) ||
|
|
998
|
-
resolve(targetAbsPath).startsWith(agentuityDir);
|
|
999
|
-
let isDirectory = false;
|
|
1000
|
-
if (targetExists) {
|
|
1001
|
-
try {
|
|
1002
|
-
isDirectory = statSync(targetAbsPath).isDirectory();
|
|
1003
|
-
} catch (err) {
|
|
1004
|
-
logger.trace('Failed to stat target file: %s', err);
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
465
|
+
// Close Vite asset server
|
|
466
|
+
if (viteServer) {
|
|
467
|
+
await viteServer.close();
|
|
468
|
+
}
|
|
1007
469
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
) {
|
|
1015
|
-
logger.trace(
|
|
1016
|
-
'File change detected (temp file rename): %s -> %s',
|
|
1017
|
-
absPath,
|
|
1018
|
-
targetAbsPath
|
|
1019
|
-
);
|
|
1020
|
-
restart();
|
|
1021
|
-
return;
|
|
470
|
+
if (gravityProcess) {
|
|
471
|
+
try {
|
|
472
|
+
gravityProcess.kill('SIGTERM');
|
|
473
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
474
|
+
if (gravityProcess.exitCode === null) {
|
|
475
|
+
gravityProcess.kill('SIGKILL');
|
|
1022
476
|
}
|
|
477
|
+
} catch (err) {
|
|
478
|
+
logger.debug('Error killing gravity process during restart: %s', err);
|
|
1023
479
|
}
|
|
480
|
+
}
|
|
1024
481
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
'File change ignored (sed temp file): %s (event: %s, file: %s)',
|
|
1031
|
-
watchDir,
|
|
1032
|
-
eventType,
|
|
1033
|
-
changedFile
|
|
1034
|
-
);
|
|
1035
|
-
return;
|
|
1036
|
-
}
|
|
1037
|
-
// Check other ignore patterns
|
|
1038
|
-
for (const pattern of ignorePatterns) {
|
|
1039
|
-
if (pattern.test(changedFile)) {
|
|
1040
|
-
logger.trace(
|
|
1041
|
-
'File change ignored (generated file): %s (event: %s, file: %s)',
|
|
1042
|
-
watchDir,
|
|
1043
|
-
eventType,
|
|
1044
|
-
changedFile
|
|
1045
|
-
);
|
|
1046
|
-
return;
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
482
|
+
// Brief pause before restart
|
|
483
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
484
|
+
} catch (error) {
|
|
485
|
+
tui.error(`Error during server operation: ${error}`);
|
|
486
|
+
tui.warn('Waiting for file changes to retry...');
|
|
1050
487
|
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
statSync(absPath).isDirectory() &&
|
|
1056
|
-
readdirSync(absPath).length === 0
|
|
1057
|
-
) {
|
|
1058
|
-
if (changedFile?.startsWith('src/agent/')) {
|
|
1059
|
-
logger.debug('agent directory created: %s', changedFile);
|
|
1060
|
-
createAgentTemplates(absPath);
|
|
1061
|
-
// Mark this directory as recently templated to avoid immediate rebuild
|
|
1062
|
-
templatedDirectories.set(absPath, Date.now());
|
|
1063
|
-
// Schedule cleanup of this marker after enough time for file events
|
|
1064
|
-
setTimeout(() => templatedDirectories.delete(absPath), 1000);
|
|
1065
|
-
// Don't restart - wait for the template files to trigger the rebuild
|
|
1066
|
-
return;
|
|
1067
|
-
} else if (changedFile?.startsWith('src/api/')) {
|
|
1068
|
-
logger.debug('api directory created: %s', changedFile);
|
|
1069
|
-
createAPITemplates(absPath);
|
|
1070
|
-
// Mark this directory as recently templated to avoid immediate rebuild
|
|
1071
|
-
templatedDirectories.set(absPath, Date.now());
|
|
1072
|
-
// Schedule cleanup of this marker after enough time for file events
|
|
1073
|
-
setTimeout(() => templatedDirectories.delete(absPath), 1000);
|
|
1074
|
-
// Don't restart - wait for the template files to trigger the rebuild
|
|
1075
|
-
return;
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
488
|
+
// Cleanup on error - close Vite asset server
|
|
489
|
+
if (viteServer) {
|
|
490
|
+
await viteServer.close();
|
|
491
|
+
}
|
|
1078
492
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
);
|
|
1086
|
-
return;
|
|
493
|
+
if (gravityProcess) {
|
|
494
|
+
try {
|
|
495
|
+
gravityProcess.kill('SIGTERM');
|
|
496
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
497
|
+
if (gravityProcess.exitCode === null) {
|
|
498
|
+
gravityProcess.kill('SIGKILL');
|
|
1087
499
|
}
|
|
500
|
+
} catch (err) {
|
|
501
|
+
logger.debug('Error killing gravity process on error: %s', err);
|
|
1088
502
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
503
|
+
}
|
|
504
|
+
if (viteServer) await viteServer.close();
|
|
505
|
+
|
|
506
|
+
// Wait for next restart trigger
|
|
507
|
+
await new Promise<void>((resolve) => {
|
|
508
|
+
const checkRestart = setInterval(() => {
|
|
509
|
+
if (shouldRestart) {
|
|
510
|
+
clearInterval(checkRestart);
|
|
511
|
+
resolve();
|
|
512
|
+
}
|
|
513
|
+
}, 100);
|
|
1097
514
|
});
|
|
1098
|
-
watchers.push(watcher);
|
|
1099
|
-
logger.trace('✓ Watcher added for %s', watchDir);
|
|
1100
|
-
} catch (error) {
|
|
1101
|
-
logger.error('Failed to setup watcher for %s: %s', watchDir, error);
|
|
1102
515
|
}
|
|
1103
516
|
}
|
|
1104
|
-
logger.debug('Dev server watching for changes');
|
|
1105
|
-
|
|
1106
|
-
// Keep the handler alive indefinitely
|
|
1107
|
-
await new Promise(() => {}).catch(() => cleanup());
|
|
1108
517
|
},
|
|
1109
518
|
});
|