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