@agentuity/cli 2.0.0-beta.1 → 2.0.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/bin/cli.ts +3 -1
- package/dist/cmd/build/ci.d.ts +1 -1
- package/dist/cmd/build/ci.d.ts.map +1 -1
- package/dist/cmd/build/ci.js +70 -63
- package/dist/cmd/build/ci.js.map +1 -1
- package/dist/cmd/build/index.d.ts.map +1 -1
- package/dist/cmd/build/index.js +0 -3
- package/dist/cmd/build/index.js.map +1 -1
- package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/agent-discovery.js +26 -2
- package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
- package/dist/cmd/build/vite/route-discovery.d.ts +5 -0
- package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/route-discovery.js +13 -11
- package/dist/cmd/build/vite/route-discovery.js.map +1 -1
- package/dist/cmd/build/vite/static-renderer.d.ts +3 -2
- package/dist/cmd/build/vite/static-renderer.d.ts.map +1 -1
- package/dist/cmd/build/vite/static-renderer.js +28 -58
- package/dist/cmd/build/vite/static-renderer.js.map +1 -1
- package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +33 -0
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/cloud/deploy-fork.d.ts +10 -0
- package/dist/cmd/cloud/deploy-fork.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy-fork.js +71 -32
- package/dist/cmd/cloud/deploy-fork.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +53 -11
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/cloud/sandbox/create.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/create.js +5 -0
- package/dist/cmd/cloud/sandbox/create.js.map +1 -1
- package/dist/cmd/cloud/sandbox/exec.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/exec.js +76 -66
- package/dist/cmd/cloud/sandbox/exec.js.map +1 -1
- package/dist/cmd/cloud/sandbox/job/index.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/job/index.js +12 -1
- package/dist/cmd/cloud/sandbox/job/index.js.map +1 -1
- package/dist/cmd/cloud/sandbox/job/logs.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/job/logs.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/job/logs.js +124 -0
- package/dist/cmd/cloud/sandbox/job/logs.js.map +1 -0
- package/dist/cmd/cloud/sandbox/run.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/run.js +14 -2
- package/dist/cmd/cloud/sandbox/run.js.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/build.js +2 -2
- package/dist/cmd/cloud/sandbox/snapshot/build.js.map +1 -1
- package/dist/cmd/coder/hub-url.d.ts.map +1 -1
- package/dist/cmd/coder/hub-url.js +3 -1
- package/dist/cmd/coder/hub-url.js.map +1 -1
- package/dist/cmd/coder/start.js +6 -6
- package/dist/cmd/coder/start.js.map +1 -1
- package/dist/cmd/coder/tui-init.d.ts +2 -2
- package/dist/cmd/coder/tui-init.js +2 -2
- package/dist/cmd/coder/tui-init.js.map +1 -1
- package/dist/cmd/project/show.d.ts.map +1 -1
- package/dist/cmd/project/show.js +9 -0
- package/dist/cmd/project/show.js.map +1 -1
- package/dist/cmd/support/report.d.ts.map +1 -1
- package/dist/cmd/support/report.js +19 -10
- package/dist/cmd/support/report.js.map +1 -1
- package/dist/errors.d.ts +24 -10
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +42 -12
- package/dist/errors.js.map +1 -1
- package/dist/schema-generator.d.ts.map +1 -1
- package/dist/schema-generator.js +2 -12
- package/dist/schema-generator.js.map +1 -1
- package/dist/steps.d.ts.map +1 -1
- package/dist/steps.js +38 -0
- package/dist/steps.js.map +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +25 -9
- package/dist/tui.js.map +1 -1
- package/dist/utils/stream-capture.d.ts +9 -0
- package/dist/utils/stream-capture.d.ts.map +1 -0
- package/dist/utils/stream-capture.js +34 -0
- package/dist/utils/stream-capture.js.map +1 -0
- package/dist/utils/stream-url.d.ts +23 -0
- package/dist/utils/stream-url.d.ts.map +1 -0
- package/dist/utils/stream-url.js +153 -0
- package/dist/utils/stream-url.js.map +1 -0
- package/dist/utils/zip.d.ts.map +1 -1
- package/dist/utils/zip.js +19 -10
- package/dist/utils/zip.js.map +1 -1
- package/package.json +9 -7
- package/src/cmd/build/ci.ts +82 -80
- package/src/cmd/build/index.ts +0 -4
- package/src/cmd/build/vite/agent-discovery.ts +30 -5
- package/src/cmd/build/vite/route-discovery.ts +25 -12
- package/src/cmd/build/vite/static-renderer.ts +33 -64
- package/src/cmd/build/vite/vite-builder.ts +36 -0
- package/src/cmd/cloud/deploy-fork.ts +90 -33
- package/src/cmd/cloud/deploy.ts +68 -12
- package/src/cmd/cloud/sandbox/create.ts +7 -0
- package/src/cmd/cloud/sandbox/exec.ts +102 -90
- package/src/cmd/cloud/sandbox/job/index.ts +12 -1
- package/src/cmd/cloud/sandbox/job/logs.ts +139 -0
- package/src/cmd/cloud/sandbox/run.ts +16 -2
- package/src/cmd/cloud/sandbox/snapshot/build.ts +2 -2
- package/src/cmd/coder/hub-url.ts +3 -1
- package/src/cmd/coder/start.ts +6 -6
- package/src/cmd/coder/tui-init.ts +4 -4
- package/src/cmd/project/show.ts +9 -0
- package/src/cmd/support/report.ts +21 -10
- package/src/errors.ts +44 -12
- package/src/schema-generator.ts +2 -12
- package/src/steps.ts +38 -0
- package/src/tui.ts +24 -9
- package/src/utils/stream-capture.ts +39 -0
- package/src/utils/stream-url.ts +226 -0
- package/src/utils/zip.ts +22 -10
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Static Renderer
|
|
3
3
|
*
|
|
4
|
-
* When `
|
|
5
|
-
* 1. Runs a Vite SSR build to
|
|
4
|
+
* When `src/web/entry-server.tsx` exists, this module:
|
|
5
|
+
* 1. Runs a Vite SSR build (as a subprocess to avoid in-process Bun/Vite issues)
|
|
6
6
|
* 2. Imports the built entry-server.js
|
|
7
7
|
* 3. Discovers routes to pre-render:
|
|
8
8
|
* - If `routeTree` is exported: auto-discovers all non-parameterized routes
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { join } from 'node:path';
|
|
17
|
-
import { createRequire } from 'node:module';
|
|
18
17
|
import { mkdirSync, writeFileSync, readFileSync, existsSync, rmSync } from 'node:fs';
|
|
19
18
|
import type { Logger } from '../../../types';
|
|
20
19
|
|
|
@@ -74,6 +73,7 @@ function extractRoutePaths(node: RouteTreeNode): string[] {
|
|
|
74
73
|
export interface StaticRenderOptions {
|
|
75
74
|
rootDir: string;
|
|
76
75
|
logger: Logger;
|
|
76
|
+
dev?: boolean;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
export interface StaticRenderResult {
|
|
@@ -82,7 +82,7 @@ export interface StaticRenderResult {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
export async function runStaticRender(options: StaticRenderOptions): Promise<StaticRenderResult> {
|
|
85
|
-
const { rootDir, logger } = options;
|
|
85
|
+
const { rootDir, logger, dev = false } = options;
|
|
86
86
|
const started = Date.now();
|
|
87
87
|
|
|
88
88
|
const clientDir = join(rootDir, '.agentuity/client');
|
|
@@ -106,71 +106,40 @@ export async function runStaticRender(options: StaticRenderOptions): Promise<Sta
|
|
|
106
106
|
);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
109
|
+
// Step 1: Vite SSR build (subprocess)
|
|
110
|
+
// Run as a subprocess to avoid in-process Bun/Vite module resolution issues
|
|
111
|
+
// that cause @mdx-js/rollup and other plugins to fail during SSR builds.
|
|
112
|
+
// This matches the approach used for client builds in vite-builder.ts.
|
|
113
|
+
logger.debug('Running Vite SSR build for static rendering (subprocess)...');
|
|
114
|
+
|
|
115
|
+
const buildMode = dev ? 'development' : 'production';
|
|
116
|
+
|
|
117
|
+
const viteProcess = Bun.spawn(
|
|
118
|
+
[
|
|
119
|
+
'bun',
|
|
120
|
+
'x',
|
|
121
|
+
'vite',
|
|
122
|
+
'build',
|
|
123
|
+
'--ssr',
|
|
124
|
+
entryServerPath,
|
|
125
|
+
'--outDir',
|
|
126
|
+
ssrOutDir,
|
|
127
|
+
'--mode',
|
|
128
|
+
buildMode,
|
|
129
|
+
],
|
|
130
|
+
{
|
|
131
|
+
cwd: rootDir,
|
|
132
|
+
stdout: 'inherit',
|
|
133
|
+
stderr: 'inherit',
|
|
116
134
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// Step 1: Vite SSR build
|
|
120
|
-
// This resolves import.meta.glob, MDX imports, and other Vite-specific APIs
|
|
121
|
-
logger.debug('Running Vite SSR build for static rendering...');
|
|
122
|
-
|
|
123
|
-
const projectRequire = createRequire(join(rootDir, 'package.json'));
|
|
124
|
-
let vitePath = 'vite';
|
|
125
|
-
try {
|
|
126
|
-
vitePath = projectRequire.resolve('vite');
|
|
127
|
-
} catch {
|
|
128
|
-
// Use CLI's bundled version
|
|
129
|
-
}
|
|
135
|
+
);
|
|
130
136
|
|
|
131
|
-
const
|
|
137
|
+
const exitCode = await viteProcess.exited;
|
|
132
138
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const viteConfigPath = join(rootDir, 'vite.config.ts');
|
|
136
|
-
|
|
137
|
-
if (await Bun.file(viteConfigPath).exists()) {
|
|
138
|
-
try {
|
|
139
|
-
const loaded = await loadConfigFromFile(
|
|
140
|
-
{ command: 'build', mode: 'production' },
|
|
141
|
-
viteConfigPath
|
|
142
|
-
);
|
|
143
|
-
if (loaded?.config) {
|
|
144
|
-
userConfig = loaded.config as import('vite').InlineConfig;
|
|
145
|
-
logger.debug('Loaded vite.config.ts for SSR build');
|
|
146
|
-
}
|
|
147
|
-
} catch (error) {
|
|
148
|
-
logger.warn('Failed to load vite.config.ts: %s', error);
|
|
149
|
-
}
|
|
139
|
+
if (exitCode !== 0) {
|
|
140
|
+
throw new Error(`Vite SSR build exited with code ${exitCode}`);
|
|
150
141
|
}
|
|
151
142
|
|
|
152
|
-
// Merge user config with SSR build settings
|
|
153
|
-
const ssrConfig = mergeConfig(userConfig, {
|
|
154
|
-
root: rootDir,
|
|
155
|
-
build: {
|
|
156
|
-
ssr: entryServerPath,
|
|
157
|
-
outDir: ssrOutDir,
|
|
158
|
-
rollupOptions: {
|
|
159
|
-
output: {
|
|
160
|
-
format: 'esm',
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
-
},
|
|
164
|
-
ssr: {
|
|
165
|
-
// Bundle all dependencies for SSR — we need import.meta.glob, MDX, etc.
|
|
166
|
-
// resolved at build time. Node built-ins are still externalized.
|
|
167
|
-
noExternal: true,
|
|
168
|
-
},
|
|
169
|
-
logLevel: isViteDebug ? 'info' : 'warn',
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
await viteBuild(ssrConfig);
|
|
173
|
-
|
|
174
143
|
// Steps 2–4: wrapped in try-finally so SSR artifacts are always cleaned up,
|
|
175
144
|
// even if an exception is thrown during module import, validation, or rendering.
|
|
176
145
|
let routeCount = 0;
|
|
@@ -95,6 +95,27 @@ export async function runViteBuild(options: ViteBuildOptions): Promise<void> {
|
|
|
95
95
|
const buildMode = dev ? 'development' : 'production';
|
|
96
96
|
const clientOutDir = join(rootDir, '.agentuity/client');
|
|
97
97
|
|
|
98
|
+
// Ensure vite.config.ts exists (fallback for projects created before v2 template update)
|
|
99
|
+
const viteConfigPath = join(rootDir, 'vite.config.ts');
|
|
100
|
+
if (!existsSync(viteConfigPath)) {
|
|
101
|
+
logger.debug('Generating fallback vite.config.ts');
|
|
102
|
+
const fallbackConfig = `import react from '@vitejs/plugin-react';
|
|
103
|
+
import { defineConfig } from 'vite';
|
|
104
|
+
import { join } from 'node:path';
|
|
105
|
+
|
|
106
|
+
export default defineConfig({
|
|
107
|
+
plugins: [react()],
|
|
108
|
+
root: '.',
|
|
109
|
+
build: {
|
|
110
|
+
rollupOptions: {
|
|
111
|
+
input: join(__dirname, 'src/web/index.html'),
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
`;
|
|
116
|
+
await Bun.write(viteConfigPath, fallbackConfig);
|
|
117
|
+
}
|
|
118
|
+
|
|
98
119
|
logger.debug('Spawning vite build for client (subprocess mode)');
|
|
99
120
|
logger.debug(' outDir: %s', clientOutDir);
|
|
100
121
|
logger.debug(' mode: %s', buildMode);
|
|
@@ -229,6 +250,20 @@ export async function runAllBuilds(options: Omit<ViteBuildOptions, 'mode'>): Pro
|
|
|
229
250
|
workbenchRoute: workbenchConfig.route,
|
|
230
251
|
analyticsEnabled,
|
|
231
252
|
});
|
|
253
|
+
|
|
254
|
+
// Normalize index.html location: vite may output to src/web/index.html
|
|
255
|
+
// depending on the project's vite.config.ts configuration
|
|
256
|
+
const clientDir = join(rootDir, '.agentuity/client');
|
|
257
|
+
const nestedIndexHtml = join(clientDir, 'src/web/index.html');
|
|
258
|
+
const rootIndexHtml = join(clientDir, 'index.html');
|
|
259
|
+
if (existsSync(nestedIndexHtml) && !existsSync(rootIndexHtml)) {
|
|
260
|
+
const { renameSync, mkdirSync: mkdirSyncFs } = await import('node:fs');
|
|
261
|
+
// Ensure target directory exists
|
|
262
|
+
mkdirSyncFs(clientDir, { recursive: true });
|
|
263
|
+
renameSync(nestedIndexHtml, rootIndexHtml);
|
|
264
|
+
logger.debug('Moved index.html from src/web/ to client root');
|
|
265
|
+
}
|
|
266
|
+
|
|
232
267
|
result.client.included = true;
|
|
233
268
|
result.client.duration = Date.now() - started;
|
|
234
269
|
endClientDiagnostic?.();
|
|
@@ -245,6 +280,7 @@ export async function runAllBuilds(options: Omit<ViteBuildOptions, 'mode'>): Pro
|
|
|
245
280
|
const staticResult = await runStaticRender({
|
|
246
281
|
rootDir,
|
|
247
282
|
logger,
|
|
283
|
+
dev,
|
|
248
284
|
});
|
|
249
285
|
result.static.included = true;
|
|
250
286
|
result.static.duration = staticResult.duration;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { spawn, type Subprocess } from 'bun';
|
|
14
14
|
import { tmpdir } from 'node:os';
|
|
15
15
|
import { join } from 'node:path';
|
|
16
|
-
import { existsSync, readFileSync, unlinkSync } from 'node:fs';
|
|
16
|
+
import { appendFileSync, createWriteStream, existsSync, readFileSync, unlinkSync } from 'node:fs';
|
|
17
17
|
import type { APIClient } from '../../api';
|
|
18
18
|
import { getUserAgent } from '../../api';
|
|
19
19
|
import { isUnicode } from '../../tui/symbols';
|
|
@@ -34,15 +34,27 @@ export interface ForkDeployResult {
|
|
|
34
34
|
success: boolean;
|
|
35
35
|
exitCode: number;
|
|
36
36
|
diagnostics?: ClientDiagnostics;
|
|
37
|
+
/** Deploy result passed back from child process via temp file */
|
|
38
|
+
deployResult?: {
|
|
39
|
+
urls?: {
|
|
40
|
+
deployment: string;
|
|
41
|
+
latest: string;
|
|
42
|
+
custom?: string[];
|
|
43
|
+
dashboard: string;
|
|
44
|
+
};
|
|
45
|
+
logs?: string[];
|
|
46
|
+
};
|
|
37
47
|
}
|
|
38
48
|
|
|
39
49
|
/**
|
|
40
|
-
* Stream data to a Pulse stream URL
|
|
50
|
+
* Stream data to a Pulse stream URL.
|
|
51
|
+
* Accepts a string, Blob/BunFile, or ReadableStream as the body to avoid
|
|
52
|
+
* loading large outputs into memory.
|
|
41
53
|
*/
|
|
42
54
|
async function streamToPulse(
|
|
43
55
|
streamURL: string,
|
|
44
56
|
sdkKey: string,
|
|
45
|
-
data: string
|
|
57
|
+
data: string | Blob | ReadableStream<Uint8Array>,
|
|
46
58
|
logger: Logger
|
|
47
59
|
): Promise<void> {
|
|
48
60
|
try {
|
|
@@ -74,7 +86,9 @@ export async function runForkedDeploy(options: ForkDeployOptions): Promise<ForkD
|
|
|
74
86
|
const buildLogsStreamURL = deployment.buildLogsStreamURL;
|
|
75
87
|
const reportFile = join(tmpdir(), `agentuity-deploy-${deploymentId}.json`);
|
|
76
88
|
const cleanLogsFile = join(tmpdir(), `agentuity-deploy-${deploymentId}-logs.txt`);
|
|
77
|
-
|
|
89
|
+
const rawLogsFile = join(tmpdir(), `agentuity-deploy-${deploymentId}-raw.txt`);
|
|
90
|
+
const deployResultFile = join(tmpdir(), `agentuity-deploy-${deploymentId}-result.json`);
|
|
91
|
+
const rawLogsWriter = createWriteStream(rawLogsFile);
|
|
78
92
|
let proc: Subprocess | null = null;
|
|
79
93
|
let cancelled = false;
|
|
80
94
|
|
|
@@ -149,13 +163,7 @@ export async function runForkedDeploy(options: ForkDeployOptions): Promise<ForkD
|
|
|
149
163
|
process.on('SIGTERM', sigtermHandler);
|
|
150
164
|
|
|
151
165
|
try {
|
|
152
|
-
const childArgs = [
|
|
153
|
-
'agentuity',
|
|
154
|
-
'deploy',
|
|
155
|
-
'--child-mode',
|
|
156
|
-
`--report-file=${reportFile}`,
|
|
157
|
-
...args,
|
|
158
|
-
];
|
|
166
|
+
const childArgs = ['deploy', '--child-mode', `--report-file=${reportFile}`, ...args];
|
|
159
167
|
|
|
160
168
|
// Pass the deployment info via environment variable (same format as CI builds)
|
|
161
169
|
const deploymentEnvValue = JSON.stringify({
|
|
@@ -164,14 +172,19 @@ export async function runForkedDeploy(options: ForkDeployOptions): Promise<ForkD
|
|
|
164
172
|
publicKey: deployment.publicKey,
|
|
165
173
|
});
|
|
166
174
|
|
|
167
|
-
|
|
175
|
+
// Re-exec the same entry point that the parent is running so that
|
|
176
|
+
// local/dev builds test the current code instead of a stale global
|
|
177
|
+
// install. process.execPath is the bun binary; Bun.main is the
|
|
178
|
+
// script entry (e.g. bin/cli.ts or the compiled binary).
|
|
179
|
+
const cmd = [process.execPath, Bun.main, ...childArgs];
|
|
180
|
+
logger.debug('Spawning child deploy process: %s', cmd.join(' '));
|
|
168
181
|
|
|
169
182
|
// Get terminal dimensions to pass to child
|
|
170
183
|
const columns = process.stdout.columns || 80;
|
|
171
184
|
const rows = process.stdout.rows || 24;
|
|
172
185
|
|
|
173
186
|
proc = spawn({
|
|
174
|
-
cmd
|
|
187
|
+
cmd,
|
|
175
188
|
cwd: projectDir,
|
|
176
189
|
env: {
|
|
177
190
|
...process.env,
|
|
@@ -187,6 +200,8 @@ export async function runForkedDeploy(options: ForkDeployOptions): Promise<ForkD
|
|
|
187
200
|
LINES: String(rows),
|
|
188
201
|
// Enable clean log collection for Pulse streaming
|
|
189
202
|
AGENTUITY_CLEAN_LOGS_FILE: cleanLogsFile,
|
|
203
|
+
// Pass result file path for child to write deploy URLs/logs back
|
|
204
|
+
AGENTUITY_DEPLOY_RESULT_FILE: deployResultFile,
|
|
190
205
|
},
|
|
191
206
|
stdin: 'inherit',
|
|
192
207
|
stdout: 'pipe',
|
|
@@ -195,7 +210,6 @@ export async function runForkedDeploy(options: ForkDeployOptions): Promise<ForkD
|
|
|
195
210
|
|
|
196
211
|
const handleOutput = async (stream: ReadableStream<Uint8Array>, isStderr: boolean) => {
|
|
197
212
|
const reader = stream.getReader();
|
|
198
|
-
const decoder = new TextDecoder();
|
|
199
213
|
const target = isStderr ? process.stderr : process.stdout;
|
|
200
214
|
|
|
201
215
|
try {
|
|
@@ -203,8 +217,9 @@ export async function runForkedDeploy(options: ForkDeployOptions): Promise<ForkD
|
|
|
203
217
|
const { done, value } = await reader.read();
|
|
204
218
|
if (done) break;
|
|
205
219
|
|
|
206
|
-
|
|
207
|
-
|
|
220
|
+
// Stream raw bytes to disk instead of accumulating in memory.
|
|
221
|
+
// This prevents OOM / ERR_STRING_TOO_LONG crashes on large builds.
|
|
222
|
+
rawLogsWriter.write(value);
|
|
208
223
|
target.write(value);
|
|
209
224
|
}
|
|
210
225
|
} catch (err) {
|
|
@@ -223,6 +238,11 @@ export async function runForkedDeploy(options: ForkDeployOptions): Promise<ForkD
|
|
|
223
238
|
|
|
224
239
|
await Promise.all([stdoutPromise, stderrPromise]);
|
|
225
240
|
|
|
241
|
+
// Close the raw logs writer so the file is fully flushed before reading
|
|
242
|
+
await new Promise<void>((resolve) => {
|
|
243
|
+
rawLogsWriter.end(resolve);
|
|
244
|
+
});
|
|
245
|
+
|
|
226
246
|
const exitCode = await proc.exited;
|
|
227
247
|
logger.debug('Child process exited with code: %d', exitCode);
|
|
228
248
|
|
|
@@ -238,23 +258,36 @@ export async function runForkedDeploy(options: ForkDeployOptions): Promise<ForkD
|
|
|
238
258
|
}
|
|
239
259
|
}
|
|
240
260
|
|
|
261
|
+
// Read deploy result (URLs, logs) from child process
|
|
262
|
+
let deployResult: ForkDeployResult['deployResult'] | undefined;
|
|
263
|
+
if (existsSync(deployResultFile)) {
|
|
264
|
+
try {
|
|
265
|
+
const resultContent = readFileSync(deployResultFile, 'utf-8');
|
|
266
|
+
deployResult = JSON.parse(resultContent);
|
|
267
|
+
unlinkSync(deployResultFile);
|
|
268
|
+
} catch (err) {
|
|
269
|
+
logger.debug('Failed to read deploy result file: %s', err);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
241
273
|
// Stream clean logs to Pulse (prefer clean logs over raw output)
|
|
242
274
|
if (buildLogsStreamURL) {
|
|
243
|
-
let
|
|
275
|
+
let streamedCleanLogs = false;
|
|
244
276
|
if (existsSync(cleanLogsFile)) {
|
|
245
277
|
try {
|
|
246
|
-
|
|
278
|
+
const cleanLogs = Bun.file(cleanLogsFile);
|
|
279
|
+
if (cleanLogs.size > 0) {
|
|
280
|
+
await streamToPulse(buildLogsStreamURL, sdkKey, cleanLogs, logger);
|
|
281
|
+
streamedCleanLogs = true;
|
|
282
|
+
}
|
|
247
283
|
unlinkSync(cleanLogsFile);
|
|
248
284
|
} catch (err) {
|
|
249
|
-
logger.debug('Failed to
|
|
285
|
+
logger.debug('Failed to stream clean logs file: %s', err);
|
|
250
286
|
}
|
|
251
287
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
if (logsContent) {
|
|
257
|
-
await streamToPulse(buildLogsStreamURL, sdkKey, logsContent, logger);
|
|
288
|
+
if (!streamedCleanLogs && existsSync(rawLogsFile)) {
|
|
289
|
+
// Stream raw logs file directly to Pulse without loading into memory
|
|
290
|
+
await streamToPulse(buildLogsStreamURL, sdkKey, Bun.file(rawLogsFile), logger);
|
|
258
291
|
}
|
|
259
292
|
}
|
|
260
293
|
|
|
@@ -292,26 +325,50 @@ export async function runForkedDeploy(options: ForkDeployOptions): Promise<ForkD
|
|
|
292
325
|
return { success: false, exitCode, diagnostics };
|
|
293
326
|
}
|
|
294
327
|
|
|
295
|
-
return { success: true, exitCode, diagnostics };
|
|
328
|
+
return { success: true, exitCode, diagnostics, deployResult };
|
|
296
329
|
} catch (err) {
|
|
297
330
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
298
331
|
logger.error('Fork deploy error: %s', errorMessage);
|
|
299
332
|
|
|
300
333
|
if (buildLogsStreamURL) {
|
|
301
|
-
let
|
|
334
|
+
let streamedCleanLogs = false;
|
|
302
335
|
if (existsSync(cleanLogsFile)) {
|
|
303
336
|
try {
|
|
304
|
-
|
|
337
|
+
const cleanLogs = Bun.file(cleanLogsFile);
|
|
338
|
+
if (cleanLogs.size > 0) {
|
|
339
|
+
await streamToPulse(buildLogsStreamURL, sdkKey, cleanLogs, logger);
|
|
340
|
+
streamedCleanLogs = true;
|
|
341
|
+
}
|
|
305
342
|
unlinkSync(cleanLogsFile);
|
|
306
343
|
} catch {
|
|
307
344
|
// ignore
|
|
308
345
|
}
|
|
309
346
|
}
|
|
310
|
-
if (
|
|
311
|
-
|
|
347
|
+
if (streamedCleanLogs) {
|
|
348
|
+
await streamToPulse(
|
|
349
|
+
buildLogsStreamURL,
|
|
350
|
+
sdkKey,
|
|
351
|
+
`\n\n--- FORK ERROR ---\n${errorMessage}\n`,
|
|
352
|
+
logger
|
|
353
|
+
);
|
|
354
|
+
} else {
|
|
355
|
+
// Append error to raw logs file and stream it without loading into memory
|
|
356
|
+
try {
|
|
357
|
+
appendFileSync(rawLogsFile, `\n\n--- FORK ERROR ---\n${errorMessage}\n`);
|
|
358
|
+
} catch {
|
|
359
|
+
// ignore — file may not exist if child never produced output
|
|
360
|
+
}
|
|
361
|
+
if (existsSync(rawLogsFile)) {
|
|
362
|
+
await streamToPulse(buildLogsStreamURL, sdkKey, Bun.file(rawLogsFile), logger);
|
|
363
|
+
} else {
|
|
364
|
+
await streamToPulse(
|
|
365
|
+
buildLogsStreamURL,
|
|
366
|
+
sdkKey,
|
|
367
|
+
`--- FORK ERROR ---\n${errorMessage}\n`,
|
|
368
|
+
logger
|
|
369
|
+
);
|
|
370
|
+
}
|
|
312
371
|
}
|
|
313
|
-
logsContent += `\n\n--- FORK ERROR ---\n${errorMessage}\n`;
|
|
314
|
-
await streamToPulse(buildLogsStreamURL, sdkKey, logsContent, logger);
|
|
315
372
|
}
|
|
316
373
|
|
|
317
374
|
try {
|
|
@@ -360,7 +417,7 @@ export async function runForkedDeploy(options: ForkDeployOptions): Promise<ForkD
|
|
|
360
417
|
process.off('SIGTERM', sigtermHandler);
|
|
361
418
|
|
|
362
419
|
// Clean up temp files
|
|
363
|
-
for (const file of [reportFile, cleanLogsFile]) {
|
|
420
|
+
for (const file of [reportFile, cleanLogsFile, rawLogsFile, deployResultFile]) {
|
|
364
421
|
if (existsSync(file)) {
|
|
365
422
|
try {
|
|
366
423
|
unlinkSync(file);
|
package/src/cmd/cloud/deploy.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { createPublicKey } from 'node:crypto';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
createReadStream,
|
|
4
|
+
createWriteStream,
|
|
5
|
+
existsSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
unlinkSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
} from 'node:fs';
|
|
3
10
|
import { tmpdir } from 'node:os';
|
|
4
11
|
import { join, resolve } from 'node:path';
|
|
12
|
+
import { pipeline } from 'node:stream/promises';
|
|
13
|
+
import { createGzip } from 'node:zlib';
|
|
5
14
|
import { StructuredError } from '@agentuity/core';
|
|
6
15
|
import {
|
|
7
16
|
type BuildMetadata,
|
|
@@ -403,6 +412,8 @@ export const deploySubcommand = createSubcommand({
|
|
|
403
412
|
success: true,
|
|
404
413
|
deploymentId: initialDeployment.id,
|
|
405
414
|
projectId: project.projectId,
|
|
415
|
+
logs: result.deployResult?.logs,
|
|
416
|
+
urls: result.deployResult?.urls,
|
|
406
417
|
};
|
|
407
418
|
}
|
|
408
419
|
let useExistingDeployment = false;
|
|
@@ -821,9 +832,10 @@ export const deploySubcommand = createSubcommand({
|
|
|
821
832
|
endCodeUploadDiagnostic();
|
|
822
833
|
|
|
823
834
|
progress(70);
|
|
824
|
-
ctx.logger.trace('
|
|
825
|
-
//
|
|
826
|
-
|
|
835
|
+
ctx.logger.trace('Cancelling upload response body');
|
|
836
|
+
// No response payload is needed for successful uploads.
|
|
837
|
+
// Cancel to release resources without buffering into memory.
|
|
838
|
+
await resp.body?.cancel();
|
|
827
839
|
ctx.logger.trace('Deleting encrypted zip');
|
|
828
840
|
await zipfile.delete();
|
|
829
841
|
} finally {
|
|
@@ -874,27 +886,48 @@ export const deploySubcommand = createSubcommand({
|
|
|
874
886
|
|
|
875
887
|
bytes += asset.size;
|
|
876
888
|
|
|
877
|
-
let body:
|
|
889
|
+
let body: Blob;
|
|
890
|
+
let gzTempPath: string | undefined;
|
|
878
891
|
if (asset.contentEncoding === 'gzip') {
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
892
|
+
// Gzip to a temp file so Bun.file() can provide
|
|
893
|
+
// Content-Length to S3 (streaming bodies use chunked
|
|
894
|
+
// transfer encoding which S3 rejects).
|
|
895
|
+
gzTempPath = join(
|
|
896
|
+
tmpdir(),
|
|
897
|
+
`agentuity-asset-${deployment.id}-${Date.now()}-${asset.filename.replace(/\//g, '_')}.gz`
|
|
898
|
+
);
|
|
899
|
+
await pipeline(
|
|
900
|
+
createReadStream(filePath),
|
|
901
|
+
createGzip(),
|
|
902
|
+
createWriteStream(gzTempPath)
|
|
903
|
+
);
|
|
882
904
|
headers['Content-Encoding'] = 'gzip';
|
|
883
|
-
body =
|
|
905
|
+
body = Bun.file(gzTempPath);
|
|
906
|
+
const compressedSize = body.size;
|
|
884
907
|
ctx.logger.trace(
|
|
885
|
-
`
|
|
908
|
+
`Gzip compressed ${asset.filename} (${asset.size} -> ${compressedSize} bytes)`
|
|
886
909
|
);
|
|
887
910
|
} else {
|
|
888
911
|
body = Bun.file(filePath);
|
|
889
912
|
}
|
|
890
913
|
|
|
914
|
+
const assetGzTempPath = gzTempPath;
|
|
891
915
|
promises.push(
|
|
892
916
|
fetch(assetUrl, {
|
|
893
917
|
method: 'PUT',
|
|
894
|
-
duplex: 'half',
|
|
895
918
|
headers,
|
|
896
919
|
body,
|
|
897
920
|
signal: stepCtx.signal,
|
|
921
|
+
}).then((response) => {
|
|
922
|
+
// Clean up temp gzip file after upload completes
|
|
923
|
+
if (assetGzTempPath) {
|
|
924
|
+
try {
|
|
925
|
+
unlinkSync(assetGzTempPath);
|
|
926
|
+
} catch {
|
|
927
|
+
// ignore — file may already be cleaned up
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
return response;
|
|
898
931
|
})
|
|
899
932
|
);
|
|
900
933
|
}
|
|
@@ -1194,7 +1227,7 @@ export const deploySubcommand = createSubcommand({
|
|
|
1194
1227
|
}
|
|
1195
1228
|
|
|
1196
1229
|
// Show deployment URLs
|
|
1197
|
-
if (complete?.publicUrls) {
|
|
1230
|
+
if (complete?.publicUrls && !options.json) {
|
|
1198
1231
|
const lines: string[] = [];
|
|
1199
1232
|
if (complete.publicUrls.custom?.length) {
|
|
1200
1233
|
for (const url of complete.publicUrls.custom) {
|
|
@@ -1237,6 +1270,29 @@ export const deploySubcommand = createSubcommand({
|
|
|
1237
1270
|
}
|
|
1238
1271
|
clearGlobalCollector();
|
|
1239
1272
|
|
|
1273
|
+
// Write deploy result to file for fork parent to consume
|
|
1274
|
+
const deployResultFile = process.env.AGENTUITY_DEPLOY_RESULT_FILE;
|
|
1275
|
+
if (deployResultFile) {
|
|
1276
|
+
try {
|
|
1277
|
+
const resultData = {
|
|
1278
|
+
urls: complete?.publicUrls
|
|
1279
|
+
? {
|
|
1280
|
+
deployment:
|
|
1281
|
+
complete.publicUrls.vanityDeployment ??
|
|
1282
|
+
complete.publicUrls.deployment,
|
|
1283
|
+
latest: complete.publicUrls.vanityProject ?? complete.publicUrls.latest,
|
|
1284
|
+
custom: complete.publicUrls.custom,
|
|
1285
|
+
dashboard,
|
|
1286
|
+
}
|
|
1287
|
+
: undefined,
|
|
1288
|
+
logs,
|
|
1289
|
+
};
|
|
1290
|
+
writeFileSync(deployResultFile, JSON.stringify(resultData));
|
|
1291
|
+
} catch {
|
|
1292
|
+
// Non-fatal: result file is optional
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1240
1296
|
return {
|
|
1241
1297
|
success: true,
|
|
1242
1298
|
deploymentId: deployment.id,
|
|
@@ -83,6 +83,12 @@ export const createSubcommand = createCommand({
|
|
|
83
83
|
.optional()
|
|
84
84
|
.describe('Apt packages to install (can be specified multiple times)'),
|
|
85
85
|
metadata: z.string().optional().describe('JSON object of user-defined metadata'),
|
|
86
|
+
scope: z
|
|
87
|
+
.array(z.string())
|
|
88
|
+
.optional()
|
|
89
|
+
.describe(
|
|
90
|
+
'Permission scopes for service access (e.g., kv:read, aigateway, services:write)'
|
|
91
|
+
),
|
|
86
92
|
port: z
|
|
87
93
|
.number()
|
|
88
94
|
.int()
|
|
@@ -195,6 +201,7 @@ export const createSubcommand = createCommand({
|
|
|
195
201
|
snapshot: opts.snapshot,
|
|
196
202
|
dependencies: opts.dependency,
|
|
197
203
|
metadata,
|
|
204
|
+
scopes: opts.scope,
|
|
198
205
|
},
|
|
199
206
|
orgId,
|
|
200
207
|
});
|