@agentuity/cli 2.0.11 → 3.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cmd/ai/prompt/index.d.ts.map +1 -1
- package/dist/cmd/ai/prompt/index.js +2 -7
- package/dist/cmd/ai/prompt/index.js.map +1 -1
- package/dist/cmd/build/adapters/generic.d.ts +29 -0
- package/dist/cmd/build/adapters/generic.d.ts.map +1 -0
- package/dist/cmd/build/adapters/generic.js +190 -0
- package/dist/cmd/build/adapters/generic.js.map +1 -0
- package/dist/cmd/build/adapters/index.d.ts +15 -0
- package/dist/cmd/build/adapters/index.d.ts.map +1 -0
- package/dist/cmd/build/adapters/index.js +24 -0
- package/dist/cmd/build/adapters/index.js.map +1 -0
- package/dist/cmd/build/adapters/nextjs.d.ts +11 -0
- package/dist/cmd/build/adapters/nextjs.d.ts.map +1 -0
- package/dist/cmd/build/adapters/nextjs.js +118 -0
- package/dist/cmd/build/adapters/nextjs.js.map +1 -0
- package/dist/cmd/build/adapters/static-server.d.ts +23 -0
- package/dist/cmd/build/adapters/static-server.d.ts.map +1 -0
- package/dist/cmd/build/adapters/static-server.js +137 -0
- package/dist/cmd/build/adapters/static-server.js.map +1 -0
- package/dist/cmd/build/adapters/types.d.ts +71 -0
- package/dist/cmd/build/adapters/types.d.ts.map +1 -0
- package/dist/cmd/build/adapters/types.js +8 -0
- package/dist/cmd/build/adapters/types.js.map +1 -0
- package/dist/cmd/build/detect/engine.d.ts +24 -0
- package/dist/cmd/build/detect/engine.d.ts.map +1 -0
- package/dist/cmd/build/detect/engine.js +91 -0
- package/dist/cmd/build/detect/engine.js.map +1 -0
- package/dist/cmd/build/detect/frameworks.d.ts +66 -0
- package/dist/cmd/build/detect/frameworks.d.ts.map +1 -0
- package/dist/cmd/build/detect/frameworks.js +278 -0
- package/dist/cmd/build/detect/frameworks.js.map +1 -0
- package/dist/cmd/build/detect/generic.d.ts +13 -0
- package/dist/cmd/build/detect/generic.d.ts.map +1 -0
- package/dist/cmd/build/detect/generic.js +62 -0
- package/dist/cmd/build/detect/generic.js.map +1 -0
- package/dist/cmd/build/detect/index.d.ts +27 -0
- package/dist/cmd/build/detect/index.d.ts.map +1 -0
- package/dist/cmd/build/detect/index.js +76 -0
- package/dist/cmd/build/detect/index.js.map +1 -0
- package/dist/cmd/build/detect/types.d.ts +91 -0
- package/dist/cmd/build/detect/types.d.ts.map +1 -0
- package/dist/cmd/build/detect/types.js +9 -0
- package/dist/cmd/build/detect/types.js.map +1 -0
- package/dist/cmd/build/detect/util.d.ts +41 -0
- package/dist/cmd/build/detect/util.d.ts.map +1 -0
- package/dist/cmd/build/detect/util.js +101 -0
- package/dist/cmd/build/detect/util.js.map +1 -0
- package/dist/cmd/build/index.d.ts.map +1 -1
- package/dist/cmd/build/index.js +49 -22
- package/dist/cmd/build/index.js.map +1 -1
- package/dist/cmd/build/package/index.d.ts +30 -0
- package/dist/cmd/build/package/index.d.ts.map +1 -0
- package/dist/cmd/build/package/index.js +39 -0
- package/dist/cmd/build/package/index.js.map +1 -0
- package/dist/cmd/build/package/launch.d.ts +56 -0
- package/dist/cmd/build/package/launch.d.ts.map +1 -0
- package/dist/cmd/build/package/launch.js +56 -0
- package/dist/cmd/build/package/launch.js.map +1 -0
- package/dist/cmd/build/typecheck.d.ts.map +1 -1
- package/dist/cmd/build/typecheck.js +8 -0
- package/dist/cmd/build/typecheck.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +63 -13
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/coder/create.js +7 -7
- package/dist/cmd/coder/create.js.map +1 -1
- package/dist/cmd/coder/update.js +8 -8
- package/dist/cmd/coder/update.js.map +1 -1
- package/dist/cmd/coder/workspace/create.js +9 -9
- package/dist/cmd/coder/workspace/create.js.map +1 -1
- package/dist/cmd/dev/index.d.ts +9 -0
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +127 -923
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/project/auth/shared.d.ts.map +1 -1
- package/dist/cmd/project/auth/shared.js +14 -38
- package/dist/cmd/project/auth/shared.js.map +1 -1
- package/dist/cmd/project/create.d.ts.map +1 -1
- package/dist/cmd/project/create.js +12 -19
- package/dist/cmd/project/create.js.map +1 -1
- package/dist/cmd/project/frameworks-ai-examples.d.ts +15 -0
- package/dist/cmd/project/frameworks-ai-examples.d.ts.map +1 -0
- package/dist/cmd/project/frameworks-ai-examples.js +160 -0
- package/dist/cmd/project/frameworks-ai-examples.js.map +1 -0
- package/dist/cmd/project/frameworks-landing-pages.d.ts +17 -0
- package/dist/cmd/project/frameworks-landing-pages.d.ts.map +1 -0
- package/dist/cmd/project/frameworks-landing-pages.js +242 -0
- package/dist/cmd/project/frameworks-landing-pages.js.map +1 -0
- package/dist/cmd/project/frameworks.d.ts +58 -0
- package/dist/cmd/project/frameworks.d.ts.map +1 -0
- package/dist/cmd/project/frameworks.js +152 -0
- package/dist/cmd/project/frameworks.js.map +1 -0
- package/dist/cmd/project/reconcile.d.ts.map +1 -1
- package/dist/cmd/project/reconcile.js +10 -23
- package/dist/cmd/project/reconcile.js.map +1 -1
- package/dist/cmd/project/remote-import.js +1 -1
- package/dist/cmd/project/scaffold.d.ts +58 -0
- package/dist/cmd/project/scaffold.d.ts.map +1 -0
- package/dist/cmd/project/scaffold.js +223 -0
- package/dist/cmd/project/scaffold.js.map +1 -0
- package/dist/cmd/project/template-flow.d.ts +8 -4
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.js +93 -144
- package/dist/cmd/project/template-flow.js.map +1 -1
- package/dist/deploy-metadata.d.ts +49 -0
- package/dist/deploy-metadata.d.ts.map +1 -0
- package/dist/deploy-metadata.js +183 -0
- package/dist/deploy-metadata.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +24 -30
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +13 -8
- package/src/cmd/ai/prompt/index.ts +2 -7
- package/src/cmd/build/adapters/generic.ts +239 -0
- package/src/cmd/build/adapters/index.ts +30 -0
- package/src/cmd/build/adapters/nextjs.ts +146 -0
- package/src/cmd/build/adapters/static-server.ts +143 -0
- package/src/cmd/build/adapters/types.ts +93 -0
- package/src/cmd/build/detect/engine.ts +111 -0
- package/src/cmd/build/detect/frameworks.ts +335 -0
- package/src/cmd/build/detect/generic.ts +71 -0
- package/src/cmd/build/detect/index.ts +122 -0
- package/src/cmd/build/detect/types.ts +109 -0
- package/src/cmd/build/detect/util.ts +104 -0
- package/src/cmd/build/index.ts +64 -24
- package/src/cmd/build/package/index.ts +66 -0
- package/src/cmd/build/package/launch.ts +104 -0
- package/src/cmd/build/typecheck.ts +9 -0
- package/src/cmd/cloud/deploy.ts +86 -13
- package/src/cmd/coder/create.ts +8 -8
- package/src/cmd/coder/update.ts +7 -7
- package/src/cmd/coder/workspace/create.ts +10 -10
- package/src/cmd/dev/index.ts +155 -1059
- package/src/cmd/project/auth/shared.ts +14 -39
- package/src/cmd/project/create.ts +14 -19
- package/src/cmd/project/frameworks-ai-examples.ts +166 -0
- package/src/cmd/project/frameworks-landing-pages.ts +267 -0
- package/src/cmd/project/frameworks.ts +223 -0
- package/src/cmd/project/reconcile.ts +10 -22
- package/src/cmd/project/remote-import.ts +1 -1
- package/src/cmd/project/scaffold.ts +300 -0
- package/src/cmd/project/template-flow.ts +102 -156
- package/src/deploy-metadata.ts +253 -0
- package/src/index.ts +0 -2
- package/src/types.ts +0 -31
- package/dist/agents-docs.d.ts +0 -23
- package/dist/agents-docs.d.ts.map +0 -1
- package/dist/agents-docs.js +0 -56
- package/dist/agents-docs.js.map +0 -1
- package/dist/cmd/ai/prompt/agent.d.ts +0 -10
- package/dist/cmd/ai/prompt/agent.d.ts.map +0 -1
- package/dist/cmd/ai/prompt/agent.js +0 -28
- package/dist/cmd/ai/prompt/agent.js.map +0 -1
- package/dist/cmd/build/app-router-detector.d.ts +0 -39
- package/dist/cmd/build/app-router-detector.d.ts.map +0 -1
- package/dist/cmd/build/app-router-detector.js +0 -229
- package/dist/cmd/build/app-router-detector.js.map +0 -1
- package/dist/cmd/build/format-schema.d.ts +0 -6
- package/dist/cmd/build/format-schema.d.ts.map +0 -1
- package/dist/cmd/build/format-schema.js +0 -60
- package/dist/cmd/build/format-schema.js.map +0 -1
- package/dist/cmd/build/vite/agent-discovery.d.ts +0 -37
- package/dist/cmd/build/vite/agent-discovery.d.ts.map +0 -1
- package/dist/cmd/build/vite/agent-discovery.js +0 -263
- package/dist/cmd/build/vite/agent-discovery.js.map +0 -1
- package/dist/cmd/build/vite/beacon-plugin.d.ts +0 -19
- package/dist/cmd/build/vite/beacon-plugin.d.ts.map +0 -1
- package/dist/cmd/build/vite/beacon-plugin.js +0 -137
- package/dist/cmd/build/vite/beacon-plugin.js.map +0 -1
- package/dist/cmd/build/vite/browser-env-plugin.d.ts +0 -9
- package/dist/cmd/build/vite/browser-env-plugin.d.ts.map +0 -1
- package/dist/cmd/build/vite/browser-env-plugin.js +0 -28
- package/dist/cmd/build/vite/browser-env-plugin.js.map +0 -1
- package/dist/cmd/build/vite/bun-dev-server.d.ts +0 -67
- package/dist/cmd/build/vite/bun-dev-server.d.ts.map +0 -1
- package/dist/cmd/build/vite/bun-dev-server.js +0 -340
- package/dist/cmd/build/vite/bun-dev-server.js.map +0 -1
- package/dist/cmd/build/vite/bundle-files.d.ts +0 -12
- package/dist/cmd/build/vite/bundle-files.d.ts.map +0 -1
- package/dist/cmd/build/vite/bundle-files.js +0 -107
- package/dist/cmd/build/vite/bundle-files.js.map +0 -1
- package/dist/cmd/build/vite/config-loader.d.ts +0 -29
- package/dist/cmd/build/vite/config-loader.d.ts.map +0 -1
- package/dist/cmd/build/vite/config-loader.js +0 -57
- package/dist/cmd/build/vite/config-loader.js.map +0 -1
- package/dist/cmd/build/vite/db-rewrite.d.ts +0 -50
- package/dist/cmd/build/vite/db-rewrite.d.ts.map +0 -1
- package/dist/cmd/build/vite/db-rewrite.js +0 -169
- package/dist/cmd/build/vite/db-rewrite.js.map +0 -1
- package/dist/cmd/build/vite/docs-generator.d.ts +0 -13
- package/dist/cmd/build/vite/docs-generator.d.ts.map +0 -1
- package/dist/cmd/build/vite/docs-generator.js +0 -97
- package/dist/cmd/build/vite/docs-generator.js.map +0 -1
- package/dist/cmd/build/vite/env-types-generator.d.ts +0 -26
- package/dist/cmd/build/vite/env-types-generator.d.ts.map +0 -1
- package/dist/cmd/build/vite/env-types-generator.js +0 -110
- package/dist/cmd/build/vite/env-types-generator.js.map +0 -1
- package/dist/cmd/build/vite/index.d.ts +0 -3
- package/dist/cmd/build/vite/index.d.ts.map +0 -1
- package/dist/cmd/build/vite/index.js +0 -4
- package/dist/cmd/build/vite/index.js.map +0 -1
- package/dist/cmd/build/vite/lifecycle-generator.d.ts +0 -19
- package/dist/cmd/build/vite/lifecycle-generator.d.ts.map +0 -1
- package/dist/cmd/build/vite/lifecycle-generator.js +0 -328
- package/dist/cmd/build/vite/lifecycle-generator.js.map +0 -1
- package/dist/cmd/build/vite/metadata-generator.d.ts +0 -36
- package/dist/cmd/build/vite/metadata-generator.d.ts.map +0 -1
- package/dist/cmd/build/vite/metadata-generator.js +0 -575
- package/dist/cmd/build/vite/metadata-generator.js.map +0 -1
- package/dist/cmd/build/vite/prompt-generator.d.ts +0 -23
- package/dist/cmd/build/vite/prompt-generator.d.ts.map +0 -1
- package/dist/cmd/build/vite/prompt-generator.js +0 -123
- package/dist/cmd/build/vite/prompt-generator.js.map +0 -1
- package/dist/cmd/build/vite/public-asset-path-plugin.d.ts +0 -45
- package/dist/cmd/build/vite/public-asset-path-plugin.d.ts.map +0 -1
- package/dist/cmd/build/vite/public-asset-path-plugin.js +0 -166
- package/dist/cmd/build/vite/public-asset-path-plugin.js.map +0 -1
- package/dist/cmd/build/vite/route-discovery.d.ts +0 -64
- package/dist/cmd/build/vite/route-discovery.d.ts.map +0 -1
- package/dist/cmd/build/vite/route-discovery.js +0 -187
- package/dist/cmd/build/vite/route-discovery.js.map +0 -1
- package/dist/cmd/build/vite/server-bundler.d.ts +0 -20
- package/dist/cmd/build/vite/server-bundler.d.ts.map +0 -1
- package/dist/cmd/build/vite/server-bundler.js +0 -388
- package/dist/cmd/build/vite/server-bundler.js.map +0 -1
- package/dist/cmd/build/vite/static-renderer.d.ts +0 -26
- package/dist/cmd/build/vite/static-renderer.d.ts.map +0 -1
- package/dist/cmd/build/vite/static-renderer.js +0 -188
- package/dist/cmd/build/vite/static-renderer.js.map +0 -1
- package/dist/cmd/build/vite/tailwind-source-plugin.d.ts +0 -15
- package/dist/cmd/build/vite/tailwind-source-plugin.d.ts.map +0 -1
- package/dist/cmd/build/vite/tailwind-source-plugin.js +0 -61
- package/dist/cmd/build/vite/tailwind-source-plugin.js.map +0 -1
- package/dist/cmd/build/vite/vite-asset-server-config.d.ts +0 -24
- package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +0 -1
- package/dist/cmd/build/vite/vite-asset-server-config.js +0 -285
- package/dist/cmd/build/vite/vite-asset-server-config.js.map +0 -1
- package/dist/cmd/build/vite/vite-asset-server.d.ts +0 -30
- package/dist/cmd/build/vite/vite-asset-server.d.ts.map +0 -1
- package/dist/cmd/build/vite/vite-asset-server.js +0 -109
- package/dist/cmd/build/vite/vite-asset-server.js.map +0 -1
- package/dist/cmd/build/vite/vite-builder.d.ts +0 -59
- package/dist/cmd/build/vite/vite-builder.d.ts.map +0 -1
- package/dist/cmd/build/vite/vite-builder.js +0 -401
- package/dist/cmd/build/vite/vite-builder.js.map +0 -1
- package/dist/cmd/build/vite/workbench-generator.d.ts +0 -10
- package/dist/cmd/build/vite/workbench-generator.d.ts.map +0 -1
- package/dist/cmd/build/vite/workbench-generator.js +0 -135
- package/dist/cmd/build/vite/workbench-generator.js.map +0 -1
- package/dist/cmd/build/vite/ws-proxy.d.ts +0 -53
- package/dist/cmd/build/vite/ws-proxy.d.ts.map +0 -1
- package/dist/cmd/build/vite/ws-proxy.js +0 -95
- package/dist/cmd/build/vite/ws-proxy.js.map +0 -1
- package/dist/cmd/build/vite-bundler.d.ts +0 -29
- package/dist/cmd/build/vite-bundler.d.ts.map +0 -1
- package/dist/cmd/build/vite-bundler.js +0 -93
- package/dist/cmd/build/vite-bundler.js.map +0 -1
- package/dist/cmd/dev/agents.d.ts +0 -2
- package/dist/cmd/dev/agents.d.ts.map +0 -1
- package/dist/cmd/dev/agents.js +0 -123
- package/dist/cmd/dev/agents.js.map +0 -1
- package/dist/cmd/dev/api.d.ts +0 -20
- package/dist/cmd/dev/api.d.ts.map +0 -1
- package/dist/cmd/dev/api.js +0 -42
- package/dist/cmd/dev/api.js.map +0 -1
- package/dist/cmd/dev/dev-lock.d.ts +0 -62
- package/dist/cmd/dev/dev-lock.d.ts.map +0 -1
- package/dist/cmd/dev/dev-lock.js +0 -250
- package/dist/cmd/dev/dev-lock.js.map +0 -1
- package/dist/cmd/dev/download.d.ts +0 -11
- package/dist/cmd/dev/download.d.ts.map +0 -1
- package/dist/cmd/dev/download.js +0 -94
- package/dist/cmd/dev/download.js.map +0 -1
- package/dist/cmd/dev/file-watcher.d.ts +0 -24
- package/dist/cmd/dev/file-watcher.d.ts.map +0 -1
- package/dist/cmd/dev/file-watcher.js +0 -349
- package/dist/cmd/dev/file-watcher.js.map +0 -1
- package/dist/cmd/dev/process-manager.d.ts +0 -104
- package/dist/cmd/dev/process-manager.d.ts.map +0 -1
- package/dist/cmd/dev/process-manager.js +0 -204
- package/dist/cmd/dev/process-manager.js.map +0 -1
- package/dist/cmd/dev/sync.d.ts +0 -12
- package/dist/cmd/dev/sync.d.ts.map +0 -1
- package/dist/cmd/dev/sync.js +0 -227
- package/dist/cmd/dev/sync.js.map +0 -1
- package/dist/cmd/dev/templates.d.ts +0 -3
- package/dist/cmd/dev/templates.d.ts.map +0 -1
- package/dist/cmd/dev/templates.js +0 -58
- package/dist/cmd/dev/templates.js.map +0 -1
- package/dist/cmd/project/download.d.ts +0 -35
- package/dist/cmd/project/download.d.ts.map +0 -1
- package/dist/cmd/project/download.js +0 -403
- package/dist/cmd/project/download.js.map +0 -1
- package/dist/cmd/project/templates.d.ts +0 -9
- package/dist/cmd/project/templates.d.ts.map +0 -1
- package/dist/cmd/project/templates.js +0 -34
- package/dist/cmd/project/templates.js.map +0 -1
- package/src/agents-docs.ts +0 -86
- package/src/cmd/ai/prompt/agent.md +0 -305
- package/src/cmd/ai/prompt/agent.ts +0 -31
- package/src/cmd/build/app-router-detector.ts +0 -320
- package/src/cmd/build/format-schema.ts +0 -66
- package/src/cmd/build/vite/agent-discovery.ts +0 -380
- package/src/cmd/build/vite/beacon-plugin.ts +0 -164
- package/src/cmd/build/vite/browser-env-plugin.ts +0 -34
- package/src/cmd/build/vite/bun-dev-server.ts +0 -458
- package/src/cmd/build/vite/bundle-files.ts +0 -135
- package/src/cmd/build/vite/config-loader.ts +0 -76
- package/src/cmd/build/vite/db-rewrite.ts +0 -189
- package/src/cmd/build/vite/docs-generator.ts +0 -103
- package/src/cmd/build/vite/env-types-generator.ts +0 -145
- package/src/cmd/build/vite/index.ts +0 -3
- package/src/cmd/build/vite/lifecycle-generator.ts +0 -381
- package/src/cmd/build/vite/metadata-generator.ts +0 -713
- package/src/cmd/build/vite/prompt-generator.ts +0 -169
- package/src/cmd/build/vite/public-asset-path-plugin.ts +0 -209
- package/src/cmd/build/vite/route-discovery.ts +0 -271
- package/src/cmd/build/vite/server-bundler.ts +0 -481
- package/src/cmd/build/vite/static-renderer.ts +0 -239
- package/src/cmd/build/vite/tailwind-source-plugin.ts +0 -73
- package/src/cmd/build/vite/vite-asset-server-config.ts +0 -349
- package/src/cmd/build/vite/vite-asset-server.ts +0 -154
- package/src/cmd/build/vite/vite-builder.ts +0 -503
- package/src/cmd/build/vite/workbench-generator.ts +0 -152
- package/src/cmd/build/vite/ws-proxy.ts +0 -126
- package/src/cmd/build/vite-bundler.ts +0 -137
- package/src/cmd/dev/agents.ts +0 -140
- package/src/cmd/dev/api.ts +0 -65
- package/src/cmd/dev/dev-lock.ts +0 -332
- package/src/cmd/dev/download.ts +0 -117
- package/src/cmd/dev/file-watcher.ts +0 -423
- package/src/cmd/dev/process-manager.ts +0 -261
- package/src/cmd/dev/sync.ts +0 -411
- package/src/cmd/dev/templates.ts +0 -66
- package/src/cmd/project/download.ts +0 -505
- package/src/cmd/project/templates.ts +0 -56
package/src/cmd/dev/index.ts
CHANGED
|
@@ -1,1116 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev command — runs the project's own dev script.
|
|
3
|
+
*
|
|
4
|
+
* Detects the package manager (bun/npm/pnpm/yarn) from the project,
|
|
5
|
+
* then runs `<pm> run dev`. Before spawning, injects Agentuity AI
|
|
6
|
+
* Gateway environment variables so LLM SDK calls (OpenAI, Anthropic,
|
|
7
|
+
* Groq) are automatically routed through the gateway when the user
|
|
8
|
+
* has an AGENTUITY_SDK_KEY configured.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { resolve } from 'node:path';
|
|
1
12
|
import { z } from 'zod';
|
|
2
|
-
import { resolve, join } from 'node:path';
|
|
3
|
-
import { existsSync } from 'node:fs';
|
|
4
|
-
import { getServiceUrls } from '@agentuity/server';
|
|
5
13
|
import { createCommand } from '../../types';
|
|
6
|
-
import { startBunDevServer } from '../build/vite/bun-dev-server';
|
|
7
|
-
import { startViteAssetServer } from '../build/vite/vite-asset-server';
|
|
8
14
|
import * as tui from '../../tui';
|
|
9
15
|
import { getCommand } from '../../command-prefix';
|
|
10
|
-
import { generateEndpoint, type DevmodeResponse } from './api';
|
|
11
|
-
import { APIClient, getAPIBaseURL, getAppBaseURL, getGravityDevModeURL } from '../../api';
|
|
12
|
-
import { download } from './download';
|
|
13
|
-
import { createDevmodeSyncService } from './sync';
|
|
14
|
-
import { getDevmodeDeploymentId } from '../build/ids';
|
|
15
|
-
import { getDefaultConfigDir, saveConfig, loadProjectSDKKey, getAuth } from '../../config';
|
|
16
|
-
import type { Config } from '../../types';
|
|
17
|
-
import { typecheck } from '../build/typecheck';
|
|
18
|
-
import { validateGravityRequiresUpgrade } from '../../runtime';
|
|
19
|
-
import { isTTY, hasLoggedInBefore } from '../../auth';
|
|
20
|
-
|
|
21
|
-
import { prepareDevLock, releaseLockSync } from './dev-lock';
|
|
22
|
-
import { checkAndUpgradeDependencies } from '../../utils/dependency-checker';
|
|
23
|
-
import { initProcessManager } from './process-manager';
|
|
24
|
-
import { detectVersionMismatch, formatVersionMismatchWarning } from '../../utils/version-mismatch';
|
|
25
|
-
|
|
26
16
|
import { ErrorCode } from '../../errors';
|
|
17
|
+
import { loadProjectSDKKey, getAuth, loadConfig } from '../../config';
|
|
18
|
+
import { detectFrameworkWithPackageJson } from '../build/detect';
|
|
19
|
+
import { detectPackageManager, getRunCommand } from '../build/detect/util';
|
|
27
20
|
|
|
28
|
-
const DEFAULT_PORT =
|
|
29
|
-
const MIN_PORT = 1024;
|
|
30
|
-
const MAX_PORT = 65535;
|
|
31
|
-
|
|
32
|
-
// Minimal interface for subprocess management
|
|
33
|
-
interface ProcessLike {
|
|
34
|
-
kill: (signal?: number | NodeJS.Signals) => void;
|
|
35
|
-
exitCode: number | null;
|
|
36
|
-
pid?: number;
|
|
37
|
-
stdout?: AsyncIterable<Uint8Array>;
|
|
38
|
-
stderr?: AsyncIterable<Uint8Array>;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
interface ServerLike {
|
|
42
|
-
close: () => void | Promise<void>;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Kill any lingering gravity processes from previous dev sessions.
|
|
47
|
-
* This is a defensive measure to clean up orphaned processes.
|
|
48
|
-
*/
|
|
49
|
-
async function killLingeringGravityProcesses(logger: {
|
|
50
|
-
debug: (msg: string, ...args: unknown[]) => void;
|
|
51
|
-
}): Promise<void> {
|
|
52
|
-
// Only attempt on Unix-like systems (macOS, Linux)
|
|
53
|
-
if (process.platform === 'win32') {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
// Use pkill to kill gravity processes owned by current user
|
|
59
|
-
// The -f flag matches against full command line
|
|
60
|
-
// We specifically match the gravity binary name to avoid killing unrelated processes
|
|
61
|
-
const result = Bun.spawnSync(['pkill', '-f', 'gravity.*--endpoint-id'], {
|
|
62
|
-
stdout: 'ignore',
|
|
63
|
-
stderr: 'ignore',
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// Exit code 0 = processes killed, 1 = no matching processes, other = error
|
|
67
|
-
if (result.exitCode === 0) {
|
|
68
|
-
logger.debug('Killed lingering gravity processes from previous session');
|
|
69
|
-
// Brief pause to let processes fully terminate
|
|
70
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
71
|
-
} else if (result.exitCode === 1) {
|
|
72
|
-
logger.debug('no lingering gravity processes found');
|
|
73
|
-
}
|
|
74
|
-
} catch {
|
|
75
|
-
// pkill not available or failed - not critical, continue
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Kill the Bun backend subprocess if one is running.
|
|
81
|
-
*/
|
|
82
|
-
function killBunSubprocess(logger: { debug: (msg: string, ...args: unknown[]) => void }): void {
|
|
83
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
84
|
-
const globalAny = globalThis as any;
|
|
85
|
-
const bunSubprocess = globalAny.__AGENTUITY_BUN_SUBPROCESS__ as ProcessLike | undefined;
|
|
86
|
-
if (!bunSubprocess) return;
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
bunSubprocess.kill('SIGTERM');
|
|
90
|
-
logger.debug('Bun subprocess killed');
|
|
91
|
-
} catch (err) {
|
|
92
|
-
logger.debug('Error killing Bun subprocess: %s', err);
|
|
93
|
-
}
|
|
94
|
-
globalAny.__AGENTUITY_BUN_SUBPROCESS__ = undefined;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const getDefaultPort = (): number => {
|
|
98
|
-
const envPort = process.env.PORT;
|
|
99
|
-
if (!envPort) {
|
|
100
|
-
return DEFAULT_PORT;
|
|
101
|
-
}
|
|
102
|
-
const trimmed = envPort.trim();
|
|
103
|
-
if (!trimmed || !/^\d+$/.test(trimmed)) {
|
|
104
|
-
return DEFAULT_PORT;
|
|
105
|
-
}
|
|
106
|
-
const parsed = Number(trimmed);
|
|
107
|
-
if (!Number.isInteger(parsed) || parsed < MIN_PORT || parsed > MAX_PORT) {
|
|
108
|
-
return DEFAULT_PORT;
|
|
109
|
-
}
|
|
110
|
-
return parsed;
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const shouldDisableInteractive = (interactive?: boolean) => {
|
|
114
|
-
if (!interactive) {
|
|
115
|
-
return true;
|
|
116
|
-
}
|
|
117
|
-
return process.env.TERM_PROGRAM === 'vscode';
|
|
118
|
-
};
|
|
21
|
+
const DEFAULT_PORT = 3000;
|
|
119
22
|
|
|
120
23
|
export const command = createCommand({
|
|
121
24
|
name: 'dev',
|
|
122
|
-
description: '
|
|
123
|
-
tags: ['mutating', 'slow'
|
|
25
|
+
description: 'Run the project development server',
|
|
26
|
+
tags: ['mutating', 'slow'],
|
|
124
27
|
idempotent: true,
|
|
28
|
+
optional: { project: true },
|
|
125
29
|
examples: [
|
|
126
30
|
{ command: getCommand('dev'), description: 'Start development server' },
|
|
127
31
|
{ command: getCommand('dev --port 8080'), description: 'Specify custom port' },
|
|
128
|
-
{ command: getCommand('dev --local'), description: 'Run in local mode' },
|
|
129
|
-
{ command: getCommand('dev --no-public'), description: 'Disable public URL' },
|
|
130
32
|
],
|
|
131
33
|
schema: {
|
|
132
34
|
options: z.object({
|
|
133
|
-
local: z.boolean().optional().describe('Turn on local services (instead of cloud)'),
|
|
134
|
-
interactive: z.boolean().default(true).optional().describe('Turn on interactive mode'),
|
|
135
|
-
public: z
|
|
136
|
-
.boolean()
|
|
137
|
-
.optional()
|
|
138
|
-
.default(!process.env.CI)
|
|
139
|
-
.describe('Turn on or off the public url'),
|
|
140
35
|
port: z
|
|
141
36
|
.number()
|
|
142
|
-
.min(
|
|
143
|
-
.max(
|
|
144
|
-
.default(getDefaultPort())
|
|
145
|
-
.describe('The TCP port to start the dev server (also reads from PORT env)'),
|
|
146
|
-
inspect: z.boolean().optional().describe('Enable bun debugger on available port'),
|
|
147
|
-
inspectWait: z
|
|
148
|
-
.boolean()
|
|
37
|
+
.min(1024)
|
|
38
|
+
.max(65535)
|
|
149
39
|
.optional()
|
|
150
|
-
.describe('
|
|
151
|
-
|
|
152
|
-
.
|
|
40
|
+
.describe('Port to pass to the dev server via PORT env var'),
|
|
41
|
+
script: z
|
|
42
|
+
.string()
|
|
153
43
|
.optional()
|
|
154
|
-
.describe('
|
|
155
|
-
|
|
156
|
-
noTypecheck: z
|
|
157
|
-
.boolean()
|
|
158
|
-
.optional()
|
|
159
|
-
.describe('Skip TypeScript type checking on startup and restarts'),
|
|
160
|
-
|
|
161
|
-
resume: z.string().optional().describe('Resume a paused Hub session by ID'),
|
|
44
|
+
.describe('Custom script name to run instead of "dev" (e.g., "dev:web")'),
|
|
162
45
|
}),
|
|
163
46
|
},
|
|
164
|
-
optional: { project: true },
|
|
165
47
|
|
|
166
48
|
async handler(ctx) {
|
|
167
|
-
const { opts,
|
|
168
|
-
let { config, project } = ctx;
|
|
169
|
-
|
|
170
|
-
// Get auth state - we handle auth ourselves based on project state
|
|
171
|
-
let auth = await getAuth();
|
|
172
|
-
|
|
49
|
+
const { opts, projectDir, logger } = ctx;
|
|
173
50
|
const rootDir = resolve(projectDir);
|
|
174
|
-
const appTs = join(rootDir, 'app.ts');
|
|
175
|
-
const srcDir = join(rootDir, 'src');
|
|
176
51
|
|
|
177
|
-
//
|
|
178
|
-
const
|
|
179
|
-
const missing: string[] = [];
|
|
52
|
+
// Read package.json
|
|
53
|
+
const { packageJson } = await detectFrameworkWithPackageJson(rootDir);
|
|
180
54
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (!originalExit) {
|
|
187
|
-
originalExit = process.exit.bind(process);
|
|
55
|
+
if (!packageJson) {
|
|
56
|
+
tui.fatal(
|
|
57
|
+
'No package.json found. Ensure you are in a JS/TS project directory.',
|
|
58
|
+
ErrorCode.CONFIG_INVALID
|
|
59
|
+
);
|
|
188
60
|
}
|
|
189
61
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
missing.push(filename);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
62
|
+
// Determine which script to run
|
|
63
|
+
const scriptName = opts.script ?? 'dev';
|
|
195
64
|
|
|
196
|
-
if (
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
65
|
+
if (!packageJson.scripts?.[scriptName]) {
|
|
66
|
+
const available = packageJson.scripts
|
|
67
|
+
? Object.keys(packageJson.scripts).join(', ')
|
|
68
|
+
: 'none';
|
|
69
|
+
tui.fatal(
|
|
70
|
+
`No "${scriptName}" script found in package.json. Available scripts: ${available}`,
|
|
71
|
+
ErrorCode.CONFIG_INVALID
|
|
72
|
+
);
|
|
202
73
|
}
|
|
203
74
|
|
|
204
|
-
//
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const isValidAuth = auth && auth.expires > new Date();
|
|
208
|
-
if (!isValidAuth) {
|
|
209
|
-
if (isTTY()) {
|
|
210
|
-
const hasProfile = await hasLoggedInBefore();
|
|
211
|
-
const message = hasProfile
|
|
212
|
-
? 'Your session has expired or you are not logged in.'
|
|
213
|
-
: 'This project is registered with Agentuity Cloud but you are not logged in.';
|
|
75
|
+
// Detect package manager
|
|
76
|
+
const pm = await detectPackageManager(rootDir);
|
|
77
|
+
const runCmd = getRunCommand(pm);
|
|
214
78
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const shouldLogin = await tui.confirm(
|
|
219
|
-
hasProfile
|
|
220
|
-
? 'Would you like to login now?'
|
|
221
|
-
: 'Would you like to login or create an account?',
|
|
222
|
-
true
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
if (shouldLogin) {
|
|
226
|
-
tui.newline();
|
|
227
|
-
|
|
228
|
-
// Run login flow inline
|
|
229
|
-
const { loginCommand } = await import('../auth/login');
|
|
230
|
-
|
|
231
|
-
// Ensure apiClient is available for login handler
|
|
232
|
-
const loginCtx = ctx as unknown as Record<string, unknown>;
|
|
233
|
-
if (!loginCtx.apiClient) {
|
|
234
|
-
loginCtx.apiClient = new APIClient(getAPIBaseURL(config), logger, config);
|
|
235
|
-
}
|
|
79
|
+
// Build the command
|
|
80
|
+
const cmd = runCmd.split(' ');
|
|
81
|
+
cmd.push(scriptName);
|
|
236
82
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
83
|
+
// Build environment
|
|
84
|
+
const env: Record<string, string> = { ...process.env } as Record<string, string>;
|
|
85
|
+
const port = opts.port ?? DEFAULT_PORT;
|
|
86
|
+
env.PORT = String(port);
|
|
242
87
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// Non-TTY: fatal error with instruction
|
|
259
|
-
logger.fatal(
|
|
260
|
-
`Authentication required for this project.\n` +
|
|
261
|
-
`Run "${getCommand('auth login')}" to login to Agentuity`,
|
|
262
|
-
ErrorCode.AUTH_REQUIRED
|
|
88
|
+
// Resolve SDK key: env → .env files → auth profile
|
|
89
|
+
if (!env.AGENTUITY_SDK_KEY) {
|
|
90
|
+
const sdkKey = await loadProjectSDKKey(logger, rootDir);
|
|
91
|
+
if (sdkKey) {
|
|
92
|
+
env.AGENTUITY_SDK_KEY = sdkKey;
|
|
93
|
+
} else {
|
|
94
|
+
// No project-level SDK key — fall back to CLI auth key
|
|
95
|
+
const auth = await getAuth();
|
|
96
|
+
if (auth?.apiKey) {
|
|
97
|
+
env.AGENTUITY_SDK_KEY = auth.apiKey;
|
|
98
|
+
tui.warning(
|
|
99
|
+
'No linked Agentuity project found. Using your auth key for AI Gateway.'
|
|
100
|
+
);
|
|
101
|
+
tui.arrow(
|
|
102
|
+
`Link this project: ${tui.colorInfo(tui.bold('agentuity project import'))}`
|
|
263
103
|
);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// After auth is established, verify project access
|
|
268
|
-
if (auth && config) {
|
|
269
|
-
const { reconcileProject } = await import('../project/reconcile');
|
|
270
|
-
const apiClient = new APIClient(getAPIBaseURL(config), logger, auth.apiKey, config);
|
|
271
|
-
|
|
272
|
-
const result = await reconcileProject({
|
|
273
|
-
dir: rootDir,
|
|
274
|
-
auth,
|
|
275
|
-
apiClient,
|
|
276
|
-
config,
|
|
277
|
-
logger,
|
|
278
|
-
interactive: isTTY(),
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
if (result.status === 'error') {
|
|
282
|
-
tui.fatal(result.message!, ErrorCode.PROJECT_NOT_FOUND);
|
|
283
|
-
} else if (result.status === 'imported' && result.project) {
|
|
284
|
-
// Project was re-imported to user's org
|
|
285
|
-
project = result.project;
|
|
286
|
-
tui.newline();
|
|
287
|
-
} else if (result.status === 'skipped') {
|
|
288
|
-
// User declined import - can't continue with cloud features
|
|
289
|
-
tui.warning('Continuing in local-only mode.');
|
|
290
|
-
project = undefined;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
} else {
|
|
294
|
-
// No agentuity.json - check if this is a valid project that needs importing
|
|
295
|
-
if (auth && config) {
|
|
296
|
-
const { reconcileProject } = await import('../project/reconcile');
|
|
297
|
-
const apiClient = new APIClient(getAPIBaseURL(config), logger, auth.apiKey, config);
|
|
298
|
-
|
|
299
|
-
const result = await reconcileProject({
|
|
300
|
-
dir: rootDir,
|
|
301
|
-
auth,
|
|
302
|
-
apiClient,
|
|
303
|
-
config,
|
|
304
|
-
logger,
|
|
305
|
-
interactive: isTTY(),
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
if (result.status === 'error') {
|
|
309
|
-
// Not a valid project - show local-only warning
|
|
310
|
-
tui.showLocalOnlyWarning();
|
|
311
|
-
} else if (result.status === 'imported' && result.project) {
|
|
312
|
-
// Project was imported - reload project config
|
|
313
|
-
project = result.project;
|
|
314
104
|
tui.newline();
|
|
315
|
-
} else if (result.status === 'skipped') {
|
|
316
|
-
// User declined import - continue in local-only mode
|
|
317
|
-
tui.showLocalOnlyWarning();
|
|
318
105
|
}
|
|
319
|
-
} else {
|
|
320
|
-
// Not authenticated - local-only mode
|
|
321
|
-
tui.showLocalOnlyWarning();
|
|
322
106
|
}
|
|
323
107
|
}
|
|
324
108
|
|
|
325
|
-
//
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
// Kill any lingering gravity processes from previous dev sessions
|
|
330
|
-
// This is a fallback for cases where the lockfile was corrupted
|
|
331
|
-
await killLingeringGravityProcesses(logger);
|
|
332
|
-
|
|
333
|
-
// Check and upgrade @agentuity/* dependencies if needed
|
|
334
|
-
const upgradeResult = await checkAndUpgradeDependencies(rootDir, logger);
|
|
335
|
-
if (upgradeResult.failed.length > 0) {
|
|
336
|
-
devLock.release();
|
|
337
|
-
tui.fatal(
|
|
338
|
-
`Failed to upgrade dependencies: ${upgradeResult.failed.join(', ')}`,
|
|
339
|
-
ErrorCode.BUILD_FAILED
|
|
340
|
-
);
|
|
109
|
+
// Load profile config to get transport URL for gateway routing
|
|
110
|
+
const config = await loadConfig();
|
|
111
|
+
if (config?.overrides?.transport_url && !env.AGENTUITY_TRANSPORT_URL) {
|
|
112
|
+
env.AGENTUITY_TRANSPORT_URL = config.overrides.transport_url;
|
|
341
113
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
const versionMismatch = detectVersionMismatch(rootDir, logger);
|
|
345
|
-
if (versionMismatch.hasV1Packages || versionMismatch.hasMajorMismatches) {
|
|
346
|
-
tui.newline();
|
|
347
|
-
tui.warning(formatVersionMismatchWarning(versionMismatch));
|
|
348
|
-
tui.newline();
|
|
114
|
+
if (config?.overrides?.catalyst_url && !env.AGENTUITY_CATALYST_URL) {
|
|
115
|
+
env.AGENTUITY_CATALYST_URL = config.overrides.catalyst_url;
|
|
349
116
|
}
|
|
350
117
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
logger,
|
|
361
|
-
apiClient,
|
|
362
|
-
mock: useMockService,
|
|
363
|
-
})
|
|
364
|
-
: null;
|
|
365
|
-
|
|
366
|
-
// Track previous metadata for sync diffing
|
|
367
|
-
let previousMetadata:
|
|
368
|
-
| Awaited<
|
|
369
|
-
ReturnType<typeof import('../build/vite/metadata-generator').generateMetadata>
|
|
370
|
-
>
|
|
371
|
-
| undefined;
|
|
372
|
-
|
|
373
|
-
let devmode: DevmodeResponse | undefined;
|
|
374
|
-
let gravityBin: string | undefined;
|
|
375
|
-
let gravityURL: string | undefined;
|
|
376
|
-
let appURL: string | undefined;
|
|
377
|
-
let savedPrivateKey: string | undefined = config?.devmode?.privateKey
|
|
378
|
-
? Buffer.from(config.devmode.privateKey, 'base64').toString('utf-8')
|
|
379
|
-
: undefined;
|
|
380
|
-
|
|
381
|
-
if (auth && project && opts.public) {
|
|
382
|
-
// Generate devmode endpoint for public URL
|
|
383
|
-
const endpoint = await tui.spinner({
|
|
384
|
-
message: 'Connecting to Gravity',
|
|
385
|
-
callback: () => {
|
|
386
|
-
return generateEndpoint(
|
|
387
|
-
apiClient!,
|
|
388
|
-
project.projectId,
|
|
389
|
-
config?.devmode?.hostname,
|
|
390
|
-
savedPrivateKey
|
|
391
|
-
);
|
|
392
|
-
},
|
|
393
|
-
clearOnSuccess: true,
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
if (endpoint.privateKey) {
|
|
397
|
-
savedPrivateKey = endpoint.privateKey;
|
|
398
|
-
}
|
|
399
|
-
const _config = { ...config } as Config;
|
|
400
|
-
_config.devmode = {
|
|
401
|
-
hostname: endpoint.hostname,
|
|
402
|
-
privateKey: savedPrivateKey
|
|
403
|
-
? Buffer.from(savedPrivateKey).toString('base64')
|
|
404
|
-
: undefined,
|
|
405
|
-
};
|
|
406
|
-
await saveConfig(_config);
|
|
407
|
-
config = _config;
|
|
408
|
-
devmode = endpoint;
|
|
409
|
-
gravityURL = getGravityDevModeURL(project.region, config);
|
|
410
|
-
appURL = `${getAppBaseURL(config)}/r/${project.projectId}`;
|
|
411
|
-
|
|
412
|
-
// Download gravity client
|
|
413
|
-
const configDir = getDefaultConfigDir();
|
|
414
|
-
const gravityDir = join(configDir, 'gravity');
|
|
415
|
-
let mustCheck = true;
|
|
416
|
-
|
|
417
|
-
if (
|
|
418
|
-
config?.gravity?.version &&
|
|
419
|
-
existsSync(join(gravityDir, config.gravity.version, 'gravity')) &&
|
|
420
|
-
config?.gravity?.checked &&
|
|
421
|
-
!validateGravityRequiresUpgrade(config.gravity.version)
|
|
422
|
-
) {
|
|
423
|
-
if (Date.now() - config.gravity.checked < 3.6e6) {
|
|
424
|
-
mustCheck = false;
|
|
425
|
-
gravityBin = join(gravityDir, config.gravity.version, 'gravity');
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
if (mustCheck) {
|
|
430
|
-
const res = await download(gravityDir);
|
|
431
|
-
gravityBin = res.filename;
|
|
432
|
-
const _config = { ...config } as Config;
|
|
433
|
-
_config.gravity = {
|
|
434
|
-
checked: Date.now(),
|
|
435
|
-
version: res.version,
|
|
436
|
-
};
|
|
437
|
-
await saveConfig(_config);
|
|
438
|
-
config = _config;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// Get workbench info from createApp() in app.ts (v2 approach)
|
|
443
|
-
const { getWorkbenchConfig, loadRuntimeConfig } = await import(
|
|
444
|
-
'../build/vite/config-loader'
|
|
118
|
+
// Inject AI Gateway env vars so LLM SDKs route through Agentuity
|
|
119
|
+
const gatewayInjected = injectGatewayEnv(env, logger);
|
|
120
|
+
if (gatewayInjected) {
|
|
121
|
+
tui.info('AI Gateway: routing LLM requests through Agentuity');
|
|
122
|
+
} else if (!env.OPENAI_API_KEY && !env.ANTHROPIC_API_KEY) {
|
|
123
|
+
tui.warning(
|
|
124
|
+
'No AI API keys found. Run ' +
|
|
125
|
+
tui.bold('agentuity auth login') +
|
|
126
|
+
' to enable AI Gateway routing.'
|
|
445
127
|
);
|
|
446
|
-
|
|
447
|
-
const workbenchConfigData = getWorkbenchConfig(true, runtimeConfig); // dev mode
|
|
448
|
-
const workbench = {
|
|
449
|
-
hasWorkbench: workbenchConfigData.enabled,
|
|
450
|
-
config: workbenchConfigData.enabled
|
|
451
|
-
? { route: workbenchConfigData.route, headers: workbenchConfigData.headers }
|
|
452
|
-
: null,
|
|
453
|
-
};
|
|
454
|
-
|
|
455
|
-
const deploymentId = getDevmodeDeploymentId(project?.projectId ?? '', devmode?.id ?? '');
|
|
456
|
-
|
|
457
|
-
// Calculate URLs for banner
|
|
458
|
-
const padding = 12;
|
|
459
|
-
const workbenchUrl =
|
|
460
|
-
auth && project?.projectId
|
|
461
|
-
? `${getAppBaseURL(config)}/w/${project.projectId}`
|
|
462
|
-
: `http://127.0.0.1:${opts.port}${workbench.config?.route ?? '/workbench'}`;
|
|
463
|
-
|
|
464
|
-
const devmodebody =
|
|
465
|
-
tui.muted(tui.padRight('Local:', padding)) +
|
|
466
|
-
tui.link(`http://127.0.0.1:${opts.port}`) +
|
|
467
|
-
'\n' +
|
|
468
|
-
tui.muted(tui.padRight('Public:', padding)) +
|
|
469
|
-
(devmode?.hostname ? tui.link(`https://${devmode.hostname}`) : tui.warn('Disabled')) +
|
|
470
|
-
'\n' +
|
|
471
|
-
tui.muted(tui.padRight('Workbench:', padding)) +
|
|
472
|
-
(workbench.hasWorkbench ? tui.link(workbenchUrl) : tui.warn('Disabled')) +
|
|
473
|
-
'\n' +
|
|
474
|
-
tui.muted(tui.padRight('Dashboard:', padding)) +
|
|
475
|
-
(appURL ? tui.link(appURL) : tui.warn('Disabled')) +
|
|
476
|
-
'\n' +
|
|
477
|
-
(interactive
|
|
478
|
-
? '\n' + tui.muted('Press ') + tui.bold('h') + tui.muted(' for keyboard shortcuts')
|
|
479
|
-
: '');
|
|
480
|
-
|
|
481
|
-
tui.banner('⨺ Agentuity DevMode', devmodebody, {
|
|
482
|
-
padding: 2,
|
|
483
|
-
topSpacer: false,
|
|
484
|
-
bottomSpacer: false,
|
|
485
|
-
centerTitle: false,
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
// Detect user route mount paths for Vite proxy configuration
|
|
489
|
-
// This is a quick AST scan of app.ts — runs before Vite starts
|
|
490
|
-
let routePaths: string[] = ['/api']; // Default fallback
|
|
491
|
-
try {
|
|
492
|
-
const { detectExplicitRouter } = await import('../build/app-router-detector');
|
|
493
|
-
const detection = await detectExplicitRouter(rootDir, logger);
|
|
494
|
-
if (detection.detected && detection.mounts.length > 0) {
|
|
495
|
-
routePaths = detection.mounts.map((m) => m.path);
|
|
496
|
-
logger.debug('Detected route mount paths: %s', routePaths.join(', '));
|
|
497
|
-
}
|
|
498
|
-
} catch (err) {
|
|
499
|
-
logger.debug('Route detection failed, using default /api: %s', err);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
// Pick internal ports (neither is user-facing — the front-door proxy is)
|
|
503
|
-
const bunBackendPort = opts.port + 1;
|
|
504
|
-
const viteInternalPort = opts.port + 2;
|
|
505
|
-
|
|
506
|
-
// No-bundle dev mode guard: ensure stale bundled app artifact cannot be executed.
|
|
507
|
-
// We keep other .agentuity artifacts (metadata/workbench files) intact.
|
|
508
|
-
try {
|
|
509
|
-
const staleBundlePath = join(rootDir, '.agentuity', 'app.js');
|
|
510
|
-
if (existsSync(staleBundlePath)) {
|
|
511
|
-
await Bun.file(staleBundlePath).delete();
|
|
512
|
-
logger.debug('Removed stale dev bundle artifact: %s', staleBundlePath);
|
|
513
|
-
}
|
|
514
|
-
} catch (err) {
|
|
515
|
-
logger.debug('Failed to remove stale dev bundle artifact: %s', err);
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
// Debug trace: locate unexpected legacy credential warnings.
|
|
519
|
-
// Enable with AGENTUITY_TRACE_CREDENTIAL_WARNINGS=true.
|
|
520
|
-
if (process.env.AGENTUITY_TRACE_CREDENTIAL_WARNINGS === 'true') {
|
|
521
|
-
const originalConsoleError = console.error.bind(console);
|
|
522
|
-
console.error = (...args: unknown[]) => {
|
|
523
|
-
try {
|
|
524
|
-
const first = typeof args[0] === 'string' ? args[0] : '';
|
|
525
|
-
if (first.includes('No credentials found for this AI provider')) {
|
|
526
|
-
const stack = new Error('Credential warning trace').stack;
|
|
527
|
-
originalConsoleError('[TRACE] Credential warning origin stack:');
|
|
528
|
-
if (stack) originalConsoleError(stack);
|
|
529
|
-
}
|
|
530
|
-
} catch {
|
|
531
|
-
// ignore tracing errors
|
|
532
|
-
}
|
|
533
|
-
originalConsoleError(...args);
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
// Start Vite dev server on an internal port.
|
|
538
|
-
// The user-facing port is handled by the front-door TCP proxy (ws-proxy)
|
|
539
|
-
// which routes WS upgrades to Bun and everything else to Vite.
|
|
540
|
-
let viteServer: ServerLike | null = null;
|
|
541
|
-
let vitePort: number;
|
|
542
|
-
|
|
543
|
-
// Initialize process manager to track all servers/processes
|
|
544
|
-
const procManager = initProcessManager(logger);
|
|
545
|
-
|
|
546
|
-
try {
|
|
547
|
-
logger.debug('Starting Vite dev server (internal port %d)...', viteInternalPort);
|
|
548
|
-
const viteResult = await startViteAssetServer({
|
|
549
|
-
rootDir,
|
|
550
|
-
logger,
|
|
551
|
-
workbenchPath: workbench.config?.route,
|
|
552
|
-
port: viteInternalPort,
|
|
553
|
-
backendPort: bunBackendPort,
|
|
554
|
-
routePaths,
|
|
555
|
-
liveHostname: devmode?.hostname,
|
|
556
|
-
});
|
|
557
|
-
viteServer = viteResult.server;
|
|
558
|
-
vitePort = viteResult.port;
|
|
559
|
-
|
|
560
|
-
// Register Vite server with process manager
|
|
561
|
-
procManager.registerServer({
|
|
562
|
-
id: 'vite',
|
|
563
|
-
server: viteServer,
|
|
564
|
-
description: 'Vite dev server (frontend assets)',
|
|
565
|
-
port: vitePort,
|
|
566
|
-
});
|
|
567
|
-
|
|
568
|
-
// Update dev lock with actual Vite port
|
|
569
|
-
await devLock.updatePorts({ vite: vitePort });
|
|
570
|
-
|
|
571
|
-
logger.debug(
|
|
572
|
-
`Vite dev server running on port ${vitePort} (internal, proxying backend on port ${bunBackendPort})`
|
|
573
|
-
);
|
|
574
|
-
} catch (error) {
|
|
575
|
-
tui.error(`Failed to start Vite dev server: ${error}`);
|
|
576
|
-
await procManager.cleanup('vite startup failure');
|
|
577
|
-
await devLock.release();
|
|
578
|
-
originalExit(1);
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
// Start the front-door TCP proxy on the user-facing port.
|
|
583
|
-
// Routes WebSocket upgrades (for /api/*, /_agentuity/*) directly to Bun
|
|
584
|
-
// and everything else (HTTP, HMR WebSocket) to Vite.
|
|
585
|
-
// This works around Bun's broken node:http upgrade socket implementation.
|
|
586
|
-
let frontDoorServer: import('node:net').Server | null = null;
|
|
587
|
-
try {
|
|
588
|
-
const { startWsProxy } = await import('../build/vite/ws-proxy');
|
|
589
|
-
frontDoorServer = await startWsProxy({
|
|
590
|
-
port: opts.port,
|
|
591
|
-
vitePort,
|
|
592
|
-
backendPort: bunBackendPort,
|
|
593
|
-
routePaths,
|
|
594
|
-
logger,
|
|
595
|
-
});
|
|
596
|
-
|
|
597
|
-
// Register front-door proxy with process manager
|
|
598
|
-
procManager.registerServer({
|
|
599
|
-
id: 'front-door-proxy',
|
|
600
|
-
server: {
|
|
601
|
-
close: () => {
|
|
602
|
-
frontDoorServer?.close();
|
|
603
|
-
},
|
|
604
|
-
},
|
|
605
|
-
description: 'Front-door TCP proxy (WS routing)',
|
|
606
|
-
port: opts.port,
|
|
607
|
-
});
|
|
608
|
-
|
|
609
|
-
logger.debug(
|
|
610
|
-
`Front-door proxy on port ${opts.port} (Vite:${vitePort}, Bun:${bunBackendPort})`
|
|
611
|
-
);
|
|
612
|
-
} catch (error) {
|
|
613
|
-
tui.error(`Failed to start front-door proxy: ${error}`);
|
|
614
|
-
await procManager.cleanup('front-door proxy startup failure');
|
|
615
|
-
await devLock.release();
|
|
616
|
-
originalExit(1);
|
|
617
|
-
return;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
// --- State for long-running processes ---
|
|
621
|
-
let gravityProcess: ProcessLike | null = null;
|
|
622
|
-
let gravityHeartbeatInterval: ReturnType<typeof setInterval> | null = null;
|
|
623
|
-
let stdinListenerRegistered = false;
|
|
624
|
-
let stdinDataHandler: ((data: Buffer | string) => void) | null = null;
|
|
625
|
-
let shutdownRequested = false;
|
|
626
|
-
|
|
627
|
-
/**
|
|
628
|
-
* Centralized cleanup function for all resources.
|
|
629
|
-
* Uses the process manager for tracked servers/processes.
|
|
630
|
-
*/
|
|
631
|
-
const cleanup = async (exitAfter = false, exitCode = 0, silent = false) => {
|
|
632
|
-
if (shutdownRequested) return;
|
|
633
|
-
shutdownRequested = true;
|
|
634
|
-
|
|
635
|
-
if (!silent) {
|
|
636
|
-
tui.info('Shutting down...');
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
// Stop gravity heartbeat interval first
|
|
640
|
-
if (gravityHeartbeatInterval) {
|
|
641
|
-
clearInterval(gravityHeartbeatInterval);
|
|
642
|
-
gravityHeartbeatInterval = null;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
// Use process manager for tracked cleanup
|
|
646
|
-
await procManager.cleanup('shutdown');
|
|
647
|
-
|
|
648
|
-
// Additional cleanup for non-tracked resources
|
|
649
|
-
await devLock.release();
|
|
650
|
-
await killLingeringGravityProcesses(logger);
|
|
651
|
-
|
|
652
|
-
if (exitAfter) {
|
|
653
|
-
if (stdinListenerRegistered && process.stdin.isTTY) {
|
|
654
|
-
try {
|
|
655
|
-
if (stdinDataHandler) {
|
|
656
|
-
process.stdin.removeListener('data', stdinDataHandler);
|
|
657
|
-
stdinDataHandler = null;
|
|
658
|
-
}
|
|
659
|
-
process.stdin.setRawMode(false);
|
|
660
|
-
process.stdin.pause();
|
|
661
|
-
process.stdin.unref();
|
|
662
|
-
} catch {
|
|
663
|
-
// Ignore
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
originalExit(exitCode);
|
|
667
|
-
}
|
|
668
|
-
};
|
|
669
|
-
|
|
670
|
-
// Signal handlers
|
|
671
|
-
let exitingFromSignal = false;
|
|
672
|
-
const safeExit = (code: number, reason?: string) => {
|
|
673
|
-
if (exitingFromSignal) return;
|
|
674
|
-
exitingFromSignal = true;
|
|
675
|
-
if (reason) logger.debug('DevMode terminating (%d): %s', code, reason);
|
|
676
|
-
shutdownRequested = true;
|
|
677
|
-
cleanup(true, code).catch(() => originalExit(1));
|
|
678
|
-
};
|
|
679
|
-
|
|
680
|
-
process.on('SIGINT', () => safeExit(0, 'SIGINT'));
|
|
681
|
-
process.on('SIGTERM', () => safeExit(0, 'SIGTERM'));
|
|
682
|
-
process.on('SIGHUP', () => safeExit(0, 'SIGHUP'));
|
|
683
|
-
process.on('uncaughtException', (err) => {
|
|
684
|
-
tui.error(
|
|
685
|
-
`Uncaught exception: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}`
|
|
686
|
-
);
|
|
687
|
-
void safeExit(1, 'uncaughtException');
|
|
688
|
-
});
|
|
689
|
-
process.on('unhandledRejection', (reason) => {
|
|
690
|
-
logger.warn(
|
|
691
|
-
'Unhandled promise rejection: %s',
|
|
692
|
-
reason instanceof Error ? (reason.stack ?? reason.message) : String(reason)
|
|
693
|
-
);
|
|
694
|
-
});
|
|
695
|
-
process.on('exit', () => {
|
|
696
|
-
if (gravityProcess?.exitCode === null) {
|
|
697
|
-
try {
|
|
698
|
-
gravityProcess.kill('SIGKILL');
|
|
699
|
-
} catch {
|
|
700
|
-
// Ignore
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
if (viteServer) {
|
|
704
|
-
try {
|
|
705
|
-
viteServer.close();
|
|
706
|
-
} catch {
|
|
707
|
-
// Ignore
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
killBunSubprocess(logger);
|
|
711
|
-
releaseLockSync(rootDir);
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
// ================================================================
|
|
715
|
-
// Step 0b: Early environment setup
|
|
716
|
-
// ================================================================
|
|
717
|
-
// Load SDK key and set gateway env vars BEFORE agent discovery.
|
|
718
|
-
// Agent discovery imports eval files, which may import LLM SDKs at
|
|
719
|
-
// module scope. Those SDKs check for API keys (e.g. GROQ_API_KEY)
|
|
720
|
-
// at import time, so the gateway env patching must happen first.
|
|
721
|
-
|
|
722
|
-
if (!process.env.AGENTUITY_SDK_KEY) {
|
|
723
|
-
const sdkKey = await loadProjectSDKKey(logger, rootDir);
|
|
724
|
-
if (sdkKey) {
|
|
725
|
-
process.env.AGENTUITY_SDK_KEY = sdkKey;
|
|
726
|
-
} else if (project) {
|
|
727
|
-
tui.warning(
|
|
728
|
-
'AGENTUITY_SDK_KEY not found in .env file. Numerous features will be unavailable.'
|
|
729
|
-
);
|
|
730
|
-
tui.bullet(
|
|
731
|
-
`Run "${getCommand('cloud env pull')}" to sync your SDK key, or add AGENTUITY_SDK_KEY to your .env file.`
|
|
732
|
-
);
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
process.env.NODE_ENV = 'development';
|
|
737
|
-
process.env.AGENTUITY_ENV = 'development';
|
|
738
|
-
|
|
739
|
-
if (project) {
|
|
740
|
-
const earlyServiceUrls = getServiceUrls(project.region);
|
|
741
|
-
if (!process.env.AGENTUITY_TRANSPORT_URL) {
|
|
742
|
-
process.env.AGENTUITY_TRANSPORT_URL = earlyServiceUrls.catalyst;
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
// Apply gateway env patching so LLM SDK API keys are set before
|
|
747
|
-
// agent discovery imports eval files that may reference them.
|
|
748
|
-
{
|
|
749
|
-
const sdkKey = process.env.AGENTUITY_SDK_KEY;
|
|
750
|
-
const gatewayUrl =
|
|
751
|
-
process.env.AGENTUITY_AIGATEWAY_URL ||
|
|
752
|
-
process.env.AGENTUITY_TRANSPORT_URL ||
|
|
753
|
-
(sdkKey ? 'https://catalyst.agentuity.cloud' : '');
|
|
754
|
-
|
|
755
|
-
const gatewayConfigs = [
|
|
756
|
-
{
|
|
757
|
-
apiKeyEnv: 'ANTHROPIC_API_KEY',
|
|
758
|
-
baseUrlEnv: 'ANTHROPIC_BASE_URL',
|
|
759
|
-
provider: 'anthropic',
|
|
760
|
-
},
|
|
761
|
-
{ apiKeyEnv: 'GROQ_API_KEY', baseUrlEnv: 'GROQ_BASE_URL', provider: 'groq' },
|
|
762
|
-
{ apiKeyEnv: 'OPENAI_API_KEY', baseUrlEnv: 'OPENAI_BASE_URL', provider: 'openai' },
|
|
763
|
-
];
|
|
764
|
-
|
|
765
|
-
for (const cfg of gatewayConfigs) {
|
|
766
|
-
const currentKey = process.env[cfg.apiKeyEnv];
|
|
767
|
-
if (currentKey && currentKey !== sdkKey) continue;
|
|
768
|
-
if (gatewayUrl && sdkKey) {
|
|
769
|
-
process.env[cfg.apiKeyEnv] = sdkKey;
|
|
770
|
-
process.env[cfg.baseUrlEnv] = `${gatewayUrl}/gateway/${cfg.provider}`;
|
|
771
|
-
logger.debug('Enabled Agentuity AI Gateway for %s', cfg.provider);
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
// ================================================================
|
|
777
|
-
// Step 1: Prepare dev server (once)
|
|
778
|
-
// ================================================================
|
|
779
|
-
|
|
780
|
-
await tui.spinner({
|
|
781
|
-
message: 'Preparing dev server',
|
|
782
|
-
callback: async () => {
|
|
783
|
-
// Typecheck (skip with --no-typecheck)
|
|
784
|
-
if (!opts.noTypecheck) {
|
|
785
|
-
const typeResult = await typecheck(rootDir);
|
|
786
|
-
if (!typeResult.success) {
|
|
787
|
-
// Non-fatal in dev: log errors and continue
|
|
788
|
-
console.log('');
|
|
789
|
-
console.log(typeResult.output);
|
|
790
|
-
console.log('');
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
// Generate workbench files if enabled
|
|
795
|
-
if (workbenchConfigData.enabled) {
|
|
796
|
-
const { generateWorkbenchFiles } = await import(
|
|
797
|
-
'../build/vite/workbench-generator'
|
|
798
|
-
);
|
|
799
|
-
await generateWorkbenchFiles(
|
|
800
|
-
rootDir,
|
|
801
|
-
project?.projectId ?? '',
|
|
802
|
-
workbenchConfigData,
|
|
803
|
-
logger
|
|
804
|
-
);
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
// Discover agents and routes in parallel
|
|
808
|
-
const srcDir = join(rootDir, 'src');
|
|
809
|
-
const { discoverAgents } = await import('../build/vite/agent-discovery');
|
|
810
|
-
const { discoverRoutes } = await import('../build/vite/route-discovery');
|
|
811
|
-
|
|
812
|
-
const [agentMetadata, { routes }] = await Promise.all([
|
|
813
|
-
discoverAgents(srcDir, project?.projectId ?? '', deploymentId, logger),
|
|
814
|
-
discoverRoutes(srcDir, project?.projectId ?? '', deploymentId, logger),
|
|
815
|
-
]);
|
|
816
|
-
|
|
817
|
-
// Generate metadata file
|
|
818
|
-
const { generateMetadata, writeMetadataFile } = await import(
|
|
819
|
-
'../build/vite/metadata-generator'
|
|
820
|
-
);
|
|
821
|
-
|
|
822
|
-
const promises: Promise<void>[] = [];
|
|
823
|
-
|
|
824
|
-
// Generate prompt files (non-blocking)
|
|
825
|
-
promises.push(
|
|
826
|
-
import('../build/vite/prompt-generator')
|
|
827
|
-
.then(({ generatePromptFiles }) => generatePromptFiles(srcDir, logger))
|
|
828
|
-
.catch((err) =>
|
|
829
|
-
logger.warn('Failed to generate prompt files: %s', err.message)
|
|
830
|
-
)
|
|
831
|
-
);
|
|
832
|
-
|
|
833
|
-
const metadata = await generateMetadata({
|
|
834
|
-
rootDir,
|
|
835
|
-
projectId: project?.projectId ?? '',
|
|
836
|
-
orgId: project?.orgId ?? '',
|
|
837
|
-
deploymentId,
|
|
838
|
-
agents: agentMetadata,
|
|
839
|
-
routes,
|
|
840
|
-
dev: true,
|
|
841
|
-
logger,
|
|
842
|
-
});
|
|
843
|
-
|
|
844
|
-
writeMetadataFile(rootDir, metadata, true, logger);
|
|
845
|
-
|
|
846
|
-
// Sync metadata with backend
|
|
847
|
-
if (syncService && project?.projectId) {
|
|
848
|
-
promises.push(
|
|
849
|
-
syncService.sync(metadata, previousMetadata, project.projectId, deploymentId)
|
|
850
|
-
);
|
|
851
|
-
previousMetadata = metadata;
|
|
852
|
-
}
|
|
853
|
-
await Promise.all(promises);
|
|
854
|
-
},
|
|
855
|
-
clearOnSuccess: true,
|
|
856
|
-
});
|
|
857
|
-
|
|
858
|
-
// ================================================================
|
|
859
|
-
// Step 2: Set remaining environment variables
|
|
860
|
-
// ================================================================
|
|
861
|
-
// Note: AGENTUITY_SDK_KEY, NODE_ENV, AGENTUITY_ENV, and
|
|
862
|
-
// AGENTUITY_TRANSPORT_URL are already set in Step 0b (before
|
|
863
|
-
// agent discovery) to support gateway env patching.
|
|
864
|
-
|
|
865
|
-
process.env.AGENTUITY_SDK_DEV_MODE = 'true';
|
|
866
|
-
process.env.AGENTUITY_RUNTIME = 'yes';
|
|
867
|
-
process.env.AGENTUITY_PROJECT_DIR = rootDir;
|
|
868
|
-
if (project?.region) {
|
|
869
|
-
process.env.AGENTUITY_REGION = project.region;
|
|
870
|
-
}
|
|
871
|
-
process.env.PORT = String(bunBackendPort);
|
|
872
|
-
process.env.AGENTUITY_PORT = String(bunBackendPort);
|
|
873
|
-
process.env.AGENTUITY_BASE_URL =
|
|
874
|
-
process.env.AGENTUITY_BASE_URL || `http://localhost:${vitePort}`;
|
|
875
|
-
process.env.AGENTUITY_NO_BUNDLE = 'true';
|
|
876
|
-
|
|
877
|
-
if (opts.resume) {
|
|
878
|
-
process.env.AGENTUITY_CODER_RESUME_SESSION = opts.resume;
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
if (project) {
|
|
882
|
-
const serviceUrls = getServiceUrls(project.region);
|
|
883
|
-
process.env.AGENTUITY_TRANSPORT_URL = serviceUrls.catalyst;
|
|
884
|
-
process.env.AGENTUITY_CATALYST_URL = serviceUrls.catalyst;
|
|
885
|
-
process.env.AGENTUITY_VECTOR_URL = serviceUrls.vector;
|
|
886
|
-
process.env.AGENTUITY_KEYVALUE_URL = serviceUrls.keyvalue;
|
|
887
|
-
process.env.AGENTUITY_SANDBOX_URL = serviceUrls.sandbox;
|
|
888
|
-
process.env.AGENTUITY_STREAM_URL = serviceUrls.stream;
|
|
889
|
-
process.env.AGENTUITY_CLOUD_ORG_ID = project.orgId;
|
|
890
|
-
process.env.AGENTUITY_CLOUD_PROJECT_ID = project.projectId;
|
|
891
|
-
process.env.AGENTUITY_CLOUD_DEPLOYMENT_ID = deploymentId;
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
if (devmode?.hostname) {
|
|
895
|
-
process.env.AGENTUITY_DEVMODE_URL = `https://${devmode.hostname}`;
|
|
896
|
-
} else {
|
|
897
|
-
process.env.AGENTUITY_DEVMODE_URL = `http://localhost:${vitePort}`;
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
// ================================================================
|
|
901
|
-
// Step 3: Start Bun backend with --hot (handles its own HMR)
|
|
902
|
-
// ================================================================
|
|
903
|
-
|
|
904
|
-
try {
|
|
905
|
-
await startBunDevServer({
|
|
906
|
-
rootDir,
|
|
907
|
-
port: bunBackendPort,
|
|
908
|
-
logger,
|
|
909
|
-
vitePort,
|
|
910
|
-
inspect: opts.inspect,
|
|
911
|
-
inspectWait: opts.inspectWait,
|
|
912
|
-
inspectBrk: opts.inspectBrk,
|
|
913
|
-
});
|
|
914
|
-
|
|
915
|
-
// Register Bun subprocess with process manager
|
|
916
|
-
// The subprocess is stored in globalThis.__AGENTUITY_BUN_SUBPROCESS__
|
|
917
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
918
|
-
const bunSubprocess = (globalThis as any).__AGENTUITY_BUN_SUBPROCESS__ as ProcessLike;
|
|
919
|
-
if (bunSubprocess) {
|
|
920
|
-
procManager.registerProcess({
|
|
921
|
-
id: 'bun-backend',
|
|
922
|
-
process: bunSubprocess,
|
|
923
|
-
description: 'Bun backend server (--hot)',
|
|
924
|
-
port: bunBackendPort,
|
|
925
|
-
critical: true,
|
|
926
|
-
});
|
|
927
|
-
}
|
|
928
|
-
} catch (error) {
|
|
929
|
-
tui.error(`Failed to start Bun backend server: ${error}`);
|
|
930
|
-
await cleanup(true, 1, true);
|
|
931
|
-
return;
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
// ================================================================
|
|
935
|
-
// Step 4: Start gravity tunnel (if public URL enabled)
|
|
936
|
-
// ================================================================
|
|
937
|
-
|
|
938
|
-
if (gravityBin && gravityURL && devmode && project) {
|
|
939
|
-
const privateKeyPEM = devmode.privateKey ?? savedPrivateKey;
|
|
940
|
-
if (!privateKeyPEM) {
|
|
941
|
-
tui.error(
|
|
942
|
-
'No private key available for gravity connection. Please re-run to generate a new key.'
|
|
943
|
-
);
|
|
944
|
-
await cleanup(true, 1, true);
|
|
945
|
-
return;
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
try {
|
|
949
|
-
gravityProcess = Bun.spawn(
|
|
950
|
-
[
|
|
951
|
-
gravityBin,
|
|
952
|
-
'--endpoint-id',
|
|
953
|
-
devmode.id,
|
|
954
|
-
'--port',
|
|
955
|
-
vitePort.toString(),
|
|
956
|
-
'--url',
|
|
957
|
-
gravityURL,
|
|
958
|
-
'--log-level',
|
|
959
|
-
process.env.AGENTUITY_GRAVITY_LOG_LEVEL ?? 'error',
|
|
960
|
-
'--org-id',
|
|
961
|
-
project.orgId,
|
|
962
|
-
'--project-id',
|
|
963
|
-
project.projectId,
|
|
964
|
-
'--private-key',
|
|
965
|
-
Buffer.from(privateKeyPEM).toString('base64'),
|
|
966
|
-
'--health-check',
|
|
967
|
-
],
|
|
968
|
-
{
|
|
969
|
-
cwd: rootDir,
|
|
970
|
-
stdout: 'pipe',
|
|
971
|
-
stderr: 'pipe',
|
|
972
|
-
detached: false,
|
|
973
|
-
}
|
|
974
|
-
);
|
|
128
|
+
}
|
|
975
129
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
130
|
+
// Run the dev command, inheriting stdio for full interactivity
|
|
131
|
+
const proc = Bun.spawn(cmd, {
|
|
132
|
+
cwd: rootDir,
|
|
133
|
+
env,
|
|
134
|
+
stdin: 'inherit',
|
|
135
|
+
stdout: 'inherit',
|
|
136
|
+
stderr: 'inherit',
|
|
137
|
+
});
|
|
983
138
|
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
});
|
|
991
|
-
}
|
|
139
|
+
// Forward signals
|
|
140
|
+
const signalHandler = (signal: NodeJS.Signals) => {
|
|
141
|
+
proc.kill(signal === 'SIGINT' ? 'SIGINT' : 'SIGTERM');
|
|
142
|
+
};
|
|
143
|
+
process.on('SIGINT', signalHandler);
|
|
144
|
+
process.on('SIGTERM', signalHandler);
|
|
992
145
|
|
|
993
|
-
|
|
994
|
-
(async () => {
|
|
995
|
-
try {
|
|
996
|
-
if (gravityProcess?.stdout) {
|
|
997
|
-
for await (const chunk of gravityProcess.stdout) {
|
|
998
|
-
const text = new TextDecoder().decode(chunk);
|
|
999
|
-
const trimmed = text.trim();
|
|
146
|
+
const exitCode = await proc.exited;
|
|
1000
147
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
const heartbeatPort = parseInt(match[1], 10);
|
|
1004
|
-
logger.debug('Gravity heartbeat port: %d', heartbeatPort);
|
|
148
|
+
process.off('SIGINT', signalHandler);
|
|
149
|
+
process.off('SIGTERM', signalHandler);
|
|
1005
150
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
method: 'POST',
|
|
1011
|
-
signal: AbortSignal.timeout(2000),
|
|
1012
|
-
});
|
|
1013
|
-
} catch {
|
|
1014
|
-
// Ignore heartbeat failures
|
|
1015
|
-
}
|
|
1016
|
-
};
|
|
1017
|
-
sendHeartbeat();
|
|
1018
|
-
gravityHeartbeatInterval = setInterval(sendHeartbeat, 5000);
|
|
1019
|
-
}
|
|
1020
|
-
} else if (trimmed) {
|
|
1021
|
-
logger.debug('[gravity] %s', trimmed);
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
} catch (err) {
|
|
1026
|
-
logger.error('Error reading gravity stdout: %s', err);
|
|
1027
|
-
}
|
|
1028
|
-
})();
|
|
151
|
+
if (exitCode !== 0 && exitCode !== 130) {
|
|
152
|
+
// 130 = SIGINT (Ctrl+C), which is normal
|
|
153
|
+
logger.debug('Dev server exited with code %d', exitCode);
|
|
154
|
+
}
|
|
1029
155
|
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
for await (const chunk of gravityProcess.stderr) {
|
|
1034
|
-
logger.warn('[gravity] %s', new TextDecoder().decode(chunk).trim());
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
} catch (err) {
|
|
1038
|
-
logger.error('Error reading gravity stderr: %s', err);
|
|
1039
|
-
}
|
|
1040
|
-
})();
|
|
1041
|
-
} catch (error) {
|
|
1042
|
-
tui.error(`Failed to start gravity tunnel: ${error}`);
|
|
1043
|
-
await cleanup(true, 1, true);
|
|
1044
|
-
return;
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
156
|
+
process.exit(exitCode ?? 0);
|
|
157
|
+
},
|
|
158
|
+
});
|
|
1047
159
|
|
|
1048
|
-
|
|
1049
|
-
// Step 5: Keyboard shortcuts + wait for shutdown
|
|
1050
|
-
// ================================================================
|
|
160
|
+
// ─── AI Gateway Env Injection ─────────────────────────────────────────────────
|
|
1051
161
|
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
162
|
+
interface GatewayProvider {
|
|
163
|
+
apiKeyEnv: string;
|
|
164
|
+
baseUrlEnv: string;
|
|
165
|
+
provider: string;
|
|
166
|
+
}
|
|
1057
167
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
};
|
|
168
|
+
const GATEWAY_PROVIDERS: GatewayProvider[] = [
|
|
169
|
+
{ apiKeyEnv: 'OPENAI_API_KEY', baseUrlEnv: 'OPENAI_BASE_URL', provider: 'openai' },
|
|
170
|
+
{ apiKeyEnv: 'ANTHROPIC_API_KEY', baseUrlEnv: 'ANTHROPIC_BASE_URL', provider: 'anthropic' },
|
|
171
|
+
{ apiKeyEnv: 'GROQ_API_KEY', baseUrlEnv: 'GROQ_BASE_URL', provider: 'groq' },
|
|
172
|
+
];
|
|
1064
173
|
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
}
|
|
174
|
+
/**
|
|
175
|
+
* Inject AI Gateway environment variables into the child process env.
|
|
176
|
+
*
|
|
177
|
+
* For each LLM provider, if the user hasn't set their own API key
|
|
178
|
+
* (or it matches the SDK key), we redirect to the Agentuity gateway.
|
|
179
|
+
* This lets `openai`, `@anthropic-ai/sdk`, and `groq-sdk` work
|
|
180
|
+
* out of the box without separate provider API keys.
|
|
181
|
+
*/
|
|
182
|
+
function injectGatewayEnv(
|
|
183
|
+
env: Record<string, string>,
|
|
184
|
+
logger: { debug: (...args: unknown[]) => void }
|
|
185
|
+
): boolean {
|
|
186
|
+
const sdkKey = env.AGENTUITY_SDK_KEY;
|
|
187
|
+
if (!sdkKey) return false;
|
|
188
|
+
|
|
189
|
+
let injected = false;
|
|
190
|
+
|
|
191
|
+
const gatewayUrl =
|
|
192
|
+
env.AGENTUITY_AIGATEWAY_URL ||
|
|
193
|
+
env.AGENTUITY_TRANSPORT_URL ||
|
|
194
|
+
env.AGENTUITY_CATALYST_URL ||
|
|
195
|
+
'https://catalyst-usc.agentuity.cloud';
|
|
196
|
+
|
|
197
|
+
for (const { apiKeyEnv, baseUrlEnv, provider } of GATEWAY_PROVIDERS) {
|
|
198
|
+
const currentKey = env[apiKeyEnv];
|
|
199
|
+
|
|
200
|
+
// If the user provided their own key (different from SDK key), leave it alone
|
|
201
|
+
if (currentKey && currentKey !== sdkKey) {
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
1096
204
|
|
|
1097
|
-
|
|
205
|
+
env[apiKeyEnv] = sdkKey;
|
|
206
|
+
env[baseUrlEnv] = `${gatewayUrl}/gateway/${provider}`;
|
|
207
|
+
logger.debug('AI Gateway: routing %s through %s', provider, env[baseUrlEnv]);
|
|
208
|
+
injected = true;
|
|
209
|
+
}
|
|
1098
210
|
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
await new Promise<void>((resolve) => {
|
|
1102
|
-
const check = setInterval(() => {
|
|
1103
|
-
if (shutdownRequested) {
|
|
1104
|
-
clearInterval(check);
|
|
1105
|
-
resolve();
|
|
1106
|
-
}
|
|
1107
|
-
}, 200);
|
|
1108
|
-
});
|
|
1109
|
-
} finally {
|
|
1110
|
-
/* brute force clean up */
|
|
1111
|
-
await devLock.release();
|
|
1112
|
-
await killLingeringGravityProcesses(logger);
|
|
1113
|
-
releaseLockSync(rootDir);
|
|
1114
|
-
}
|
|
1115
|
-
},
|
|
1116
|
-
});
|
|
211
|
+
return injected;
|
|
212
|
+
}
|