@agentuity/cli 1.0.59 → 1.0.60
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/build/vite/static-render-worker.d.ts +4 -0
- package/dist/cmd/build/vite/static-render-worker.d.ts.map +1 -0
- package/dist/cmd/build/vite/static-render-worker.js +58 -0
- package/dist/cmd/build/vite/static-render-worker.js.map +1 -0
- package/dist/cmd/build/vite/vite-build-worker.d.ts +2 -0
- package/dist/cmd/build/vite/vite-build-worker.d.ts.map +1 -0
- package/dist/cmd/build/vite/vite-build-worker.js +50 -0
- package/dist/cmd/build/vite/vite-build-worker.js.map +1 -0
- package/dist/cmd/build/vite/vite-builder.d.ts +1 -0
- package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +261 -23
- 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 +41 -23
- 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/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/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 +6 -4
- package/dist/tui.js.map +1 -1
- 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 +8 -8
- package/src/cmd/build/vite/static-render-worker.ts +72 -0
- package/src/cmd/build/vite/vite-build-worker.ts +58 -0
- package/src/cmd/build/vite/vite-builder.ts +295 -23
- package/src/cmd/cloud/deploy-fork.ts +56 -22
- package/src/cmd/cloud/deploy.ts +68 -12
- package/src/cmd/project/show.ts +9 -0
- package/src/cmd/support/report.ts +21 -10
- package/src/steps.ts +38 -0
- package/src/tui.ts +6 -4
- package/src/utils/zip.ts +22 -10
package/dist/utils/zip.js
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { createWriteStream, lstatSync } from 'node:fs';
|
|
2
|
+
import { mkdir } from 'node:fs/promises';
|
|
3
|
+
import { dirname, relative } from 'node:path';
|
|
3
4
|
import { Glob } from 'bun';
|
|
4
|
-
import
|
|
5
|
+
import archiver from 'archiver';
|
|
5
6
|
import { toForwardSlash } from './normalize-path';
|
|
6
7
|
export async function zipDir(dir, outdir, options) {
|
|
7
|
-
|
|
8
|
+
await mkdir(dirname(outdir), { recursive: true });
|
|
9
|
+
const output = createWriteStream(outdir);
|
|
10
|
+
const zip = archiver('zip', {
|
|
11
|
+
zlib: { level: 9 },
|
|
12
|
+
});
|
|
13
|
+
const writeDone = new Promise((resolve, reject) => {
|
|
14
|
+
output.on('close', resolve);
|
|
15
|
+
output.on('error', reject);
|
|
16
|
+
zip.on('error', reject);
|
|
17
|
+
});
|
|
18
|
+
zip.pipe(output);
|
|
8
19
|
const files = await Array.fromAsync(new Glob('**/*').scan({ cwd: dir, absolute: true, dot: true, followSymlinks: false }));
|
|
9
20
|
const total = files.length;
|
|
10
21
|
let count = 0;
|
|
@@ -23,11 +34,8 @@ export async function zipDir(dir, outdir, options) {
|
|
|
23
34
|
// across machines and would cause EISDIR errors on extraction.
|
|
24
35
|
const stat = lstatSync(file);
|
|
25
36
|
if (!stat.isSymbolicLink() && !stat.isDirectory()) {
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
// with incorrect Unix permission bits, causing EACCES errors when extracted on Linux.
|
|
29
|
-
const data = readFileSync(file);
|
|
30
|
-
zip.addFile(rel, data, '', 0o644);
|
|
37
|
+
// Set explicit Unix permissions (0o644) for portability across OSes.
|
|
38
|
+
zip.file(file, { name: rel, mode: 0o644 });
|
|
31
39
|
}
|
|
32
40
|
}
|
|
33
41
|
catch (err) {
|
|
@@ -41,7 +49,8 @@ export async function zipDir(dir, outdir, options) {
|
|
|
41
49
|
await Bun.sleep(10); // give some time for the progress bar to render
|
|
42
50
|
}
|
|
43
51
|
}
|
|
44
|
-
await zip.
|
|
52
|
+
await zip.finalize();
|
|
53
|
+
await writeDone;
|
|
45
54
|
if (options?.progress) {
|
|
46
55
|
options.progress(100);
|
|
47
56
|
await Bun.sleep(100); // give some time for the progress bar to render
|
package/dist/utils/zip.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zip.js","sourceRoot":"","sources":["../../src/utils/zip.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"zip.js","sourceRoot":"","sources":["../../src/utils/zip.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAOlD,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,GAAW,EAAE,MAAc,EAAE,OAAiB;IAC1E,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,EAAE;QAC3B,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;KAClB,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACvD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC5B,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC3B,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEjB,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,SAAS,CAClC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CACrF,CAAC;IACF,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IAC3B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;QAChD,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;YACrB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;gBAChC,IAAI,GAAG,IAAI,CAAC;YACb,CAAC;QACF,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,IAAI,CAAC;gBACJ,mEAAmE;gBACnE,mEAAmE;gBACnE,+DAA+D;gBAC/D,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;gBAC7B,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;oBACnD,qEAAqE;oBACrE,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC5C,CAAC;YACF,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,KAAK,IAAI,GAAG,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YAChF,CAAC;QACF,CAAC;QACD,KAAK,EAAE,CAAC;QACR,IAAI,OAAO,EAAE,QAAQ,EAAE,CAAC;YACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;YACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC3B,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,gDAAgD;QACtE,CAAC;IACF,CAAC;IACD,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC;IACrB,MAAM,SAAS,CAAC;IAChB,IAAI,OAAO,EAAE,QAAQ,EAAE,CAAC;QACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACtB,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,gDAAgD;IACvE,CAAC;AACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentuity/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.60",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"author": "Agentuity employees and contributors",
|
|
6
6
|
"type": "module",
|
|
@@ -41,13 +41,13 @@
|
|
|
41
41
|
"prepublishOnly": "bun run clean && bun run build"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@agentuity/auth": "1.0.
|
|
45
|
-
"@agentuity/core": "1.0.
|
|
46
|
-
"@agentuity/server": "1.0.
|
|
44
|
+
"@agentuity/auth": "1.0.60",
|
|
45
|
+
"@agentuity/core": "1.0.60",
|
|
46
|
+
"@agentuity/server": "1.0.60",
|
|
47
47
|
"@datasert/cronjs-parser": "^1.4.0",
|
|
48
48
|
"@vitejs/plugin-react": "^5.1.2",
|
|
49
49
|
"acorn-loose": "^8.5.2",
|
|
50
|
-
"
|
|
50
|
+
"archiver": "^7.0.1",
|
|
51
51
|
"astring": "^1.9.0",
|
|
52
52
|
"cli-table3": "^0.6.5",
|
|
53
53
|
"commander": "^14.0.2",
|
|
@@ -59,11 +59,11 @@
|
|
|
59
59
|
"typescript": "^5.9.0",
|
|
60
60
|
"vite": "^7.2.7",
|
|
61
61
|
"zod": "^4.3.5",
|
|
62
|
-
"@agentuity/frontend": "1.0.
|
|
62
|
+
"@agentuity/frontend": "1.0.60"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@agentuity/test-utils": "1.0.
|
|
66
|
-
"@types/
|
|
65
|
+
"@agentuity/test-utils": "1.0.60",
|
|
66
|
+
"@types/archiver": "^6.0.3",
|
|
67
67
|
"@types/bun": "latest",
|
|
68
68
|
"@types/tar-fs": "^2.0.4",
|
|
69
69
|
"bun-plugin-tailwind": "^0.1.2",
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { format } from 'node:util';
|
|
2
|
+
import type { Logger } from '../../../types';
|
|
3
|
+
import { runStaticRender } from './static-renderer';
|
|
4
|
+
|
|
5
|
+
function createWorkerLogger(): Logger {
|
|
6
|
+
const write = (writer: (...args: unknown[]) => void, args: unknown[]) => {
|
|
7
|
+
writer(format(...args));
|
|
8
|
+
};
|
|
9
|
+
let loggerRef: Logger;
|
|
10
|
+
|
|
11
|
+
loggerRef = {
|
|
12
|
+
trace: (...args: unknown[]) => write(console.debug, args),
|
|
13
|
+
debug: (...args: unknown[]) => write(console.debug, args),
|
|
14
|
+
info: (...args: unknown[]) => write(console.log, args),
|
|
15
|
+
warn: (...args: unknown[]) => write(console.warn, args),
|
|
16
|
+
error: (...args: unknown[]) => write(console.error, args),
|
|
17
|
+
child: () => loggerRef,
|
|
18
|
+
fatal: (...args: unknown[]) => {
|
|
19
|
+
const message = format(...args);
|
|
20
|
+
console.error(message);
|
|
21
|
+
throw new Error(message);
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return loggerRef;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface StaticRenderWorkerOptions {
|
|
29
|
+
rootDir: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function main(): Promise<void> {
|
|
33
|
+
const optionsPath = process.argv[2];
|
|
34
|
+
if (!optionsPath) {
|
|
35
|
+
throw new Error('Missing worker options file path argument');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const optionsFile = Bun.file(optionsPath);
|
|
39
|
+
if (!(await optionsFile.exists())) {
|
|
40
|
+
throw new Error(`Worker options file does not exist: ${optionsPath}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const options = JSON.parse(await optionsFile.text()) as StaticRenderWorkerOptions;
|
|
44
|
+
const logger = createWorkerLogger();
|
|
45
|
+
|
|
46
|
+
// Load user plugins from agentuity.config.ts (can't serialize plugin functions)
|
|
47
|
+
const { loadAgentuityConfig } = await import('./config-loader');
|
|
48
|
+
const config = await loadAgentuityConfig(options.rootDir, logger);
|
|
49
|
+
const userPlugins = config?.plugins || [];
|
|
50
|
+
|
|
51
|
+
const result = await runStaticRender({
|
|
52
|
+
rootDir: options.rootDir,
|
|
53
|
+
logger,
|
|
54
|
+
userPlugins,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Write result to stdout for parent to parse
|
|
58
|
+
console.log(JSON.stringify(result));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
void main()
|
|
62
|
+
.then(() => {
|
|
63
|
+
process.exit(0);
|
|
64
|
+
})
|
|
65
|
+
.catch((error) => {
|
|
66
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
67
|
+
console.error(`[static-render-worker] ${message}`);
|
|
68
|
+
if (error instanceof Error && error.stack) {
|
|
69
|
+
console.error(error.stack);
|
|
70
|
+
}
|
|
71
|
+
process.exit(1);
|
|
72
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { format } from 'node:util';
|
|
2
|
+
import type { Logger } from '../../../types';
|
|
3
|
+
import { runViteBuild, type ViteBuildWorkerOptions } from './vite-builder';
|
|
4
|
+
|
|
5
|
+
function createWorkerLogger(): Logger {
|
|
6
|
+
const write = (writer: (...args: unknown[]) => void, args: unknown[]) => {
|
|
7
|
+
writer(format(...args));
|
|
8
|
+
};
|
|
9
|
+
let loggerRef: Logger;
|
|
10
|
+
|
|
11
|
+
loggerRef = {
|
|
12
|
+
trace: (...args: unknown[]) => write(console.debug, args),
|
|
13
|
+
debug: (...args: unknown[]) => write(console.debug, args),
|
|
14
|
+
info: (...args: unknown[]) => write(console.log, args),
|
|
15
|
+
warn: (...args: unknown[]) => write(console.warn, args),
|
|
16
|
+
error: (...args: unknown[]) => write(console.error, args),
|
|
17
|
+
child: () => loggerRef,
|
|
18
|
+
fatal: (...args: unknown[]) => {
|
|
19
|
+
const message = format(...args);
|
|
20
|
+
console.error(message);
|
|
21
|
+
throw new Error(message);
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return loggerRef;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function main(): Promise<void> {
|
|
29
|
+
const optionsPath = process.argv[2];
|
|
30
|
+
if (!optionsPath) {
|
|
31
|
+
throw new Error('Missing worker options file path argument');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const optionsFile = Bun.file(optionsPath);
|
|
35
|
+
if (!(await optionsFile.exists())) {
|
|
36
|
+
throw new Error(`Worker options file does not exist: ${optionsPath}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const options = JSON.parse(await optionsFile.text()) as ViteBuildWorkerOptions;
|
|
40
|
+
|
|
41
|
+
await runViteBuild({
|
|
42
|
+
...options,
|
|
43
|
+
logger: createWorkerLogger(),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
void main()
|
|
48
|
+
.then(() => {
|
|
49
|
+
process.exit(0);
|
|
50
|
+
})
|
|
51
|
+
.catch((error) => {
|
|
52
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
53
|
+
console.error(`[vite-build-worker] ${message}`);
|
|
54
|
+
if (error instanceof Error && error.stack) {
|
|
55
|
+
console.error(error.stack);
|
|
56
|
+
}
|
|
57
|
+
process.exit(1);
|
|
58
|
+
});
|
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
import { join } from 'node:path';
|
|
8
8
|
import { existsSync, renameSync, rmSync } from 'node:fs';
|
|
9
|
+
import { randomUUID } from 'node:crypto';
|
|
9
10
|
import { createRequire } from 'node:module';
|
|
11
|
+
import { tmpdir } from 'node:os';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
import { StructuredError } from '@agentuity/core';
|
|
10
14
|
import type { InlineConfig, Plugin } from 'vite';
|
|
11
15
|
import type { Logger, DeployOptions } from '../../../types';
|
|
12
16
|
import { browserEnvPlugin } from './browser-env-plugin';
|
|
@@ -15,6 +19,8 @@ import { beaconPlugin } from './beacon-plugin';
|
|
|
15
19
|
import { publicAssetPathPlugin } from './public-asset-path-plugin';
|
|
16
20
|
import type { BuildReportCollector } from '../../../build-report';
|
|
17
21
|
|
|
22
|
+
const BuildFailedError = StructuredError('BuildFailedError');
|
|
23
|
+
|
|
18
24
|
/**
|
|
19
25
|
* Vite plugin to flatten the output structure for index.html
|
|
20
26
|
*
|
|
@@ -70,6 +76,232 @@ export interface ViteBuildOptions {
|
|
|
70
76
|
profile?: string;
|
|
71
77
|
}
|
|
72
78
|
|
|
79
|
+
export type ViteBuildWorkerOptions = Omit<ViteBuildOptions, 'logger' | 'collector'>;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Drain a subprocess stream, forwarding each chunk to the callback
|
|
83
|
+
* without accumulating in memory. Only the last `tailBytes` of output
|
|
84
|
+
* are retained so we can include them in error messages on failure.
|
|
85
|
+
*/
|
|
86
|
+
async function drainSubprocessStream(
|
|
87
|
+
stream: ReadableStream<Uint8Array>,
|
|
88
|
+
onChunk: (chunk: string) => void,
|
|
89
|
+
tailBytes = 4096
|
|
90
|
+
): Promise<string> {
|
|
91
|
+
const reader = stream.getReader();
|
|
92
|
+
const decoder = new TextDecoder();
|
|
93
|
+
// Ring buffer that keeps only the trailing output for error context
|
|
94
|
+
let tail = '';
|
|
95
|
+
|
|
96
|
+
while (true) {
|
|
97
|
+
const { done, value } = await reader.read();
|
|
98
|
+
if (done) break;
|
|
99
|
+
const text = decoder.decode(value, { stream: true });
|
|
100
|
+
if (!text) continue;
|
|
101
|
+
onChunk(text);
|
|
102
|
+
tail = (tail + text).slice(-tailBytes);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const finalText = decoder.decode();
|
|
106
|
+
if (finalText) {
|
|
107
|
+
onChunk(finalText);
|
|
108
|
+
tail = (tail + finalText).slice(-tailBytes);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return tail;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Detect actually available memory for subprocess heap sizing.
|
|
116
|
+
* Reads cgroup v2/v1 max and current usage to compute free memory.
|
|
117
|
+
* Falls back to os.freemem() if cgroup files aren't available.
|
|
118
|
+
*/
|
|
119
|
+
async function detectAvailableMemory(
|
|
120
|
+
logger: Logger,
|
|
121
|
+
label: string
|
|
122
|
+
): Promise<Record<string, string>> {
|
|
123
|
+
let cgroupMaxBytes = 0;
|
|
124
|
+
let cgroupCurrentBytes = 0;
|
|
125
|
+
let availableBytes = 0;
|
|
126
|
+
try {
|
|
127
|
+
if (process.platform === 'linux') {
|
|
128
|
+
// Try cgroup v2
|
|
129
|
+
const cgroupMax = Bun.file('/sys/fs/cgroup/memory.max');
|
|
130
|
+
const cgroupCurrent = Bun.file('/sys/fs/cgroup/memory.current');
|
|
131
|
+
if ((await cgroupMax.exists()) && (await cgroupCurrent.exists())) {
|
|
132
|
+
const maxStr = (await cgroupMax.text()).trim();
|
|
133
|
+
const currentStr = (await cgroupCurrent.text()).trim();
|
|
134
|
+
// "max" means unlimited — use total system memory
|
|
135
|
+
if (maxStr === 'max') {
|
|
136
|
+
const { totalmem } = await import('node:os');
|
|
137
|
+
cgroupMaxBytes = totalmem();
|
|
138
|
+
} else if (/^\d+$/.test(maxStr)) {
|
|
139
|
+
cgroupMaxBytes = Number(maxStr);
|
|
140
|
+
}
|
|
141
|
+
if (/^\d+$/.test(currentStr)) {
|
|
142
|
+
cgroupCurrentBytes = Number(currentStr);
|
|
143
|
+
}
|
|
144
|
+
if (cgroupMaxBytes > 0) {
|
|
145
|
+
availableBytes =
|
|
146
|
+
cgroupCurrentBytes > 0 ? cgroupMaxBytes - cgroupCurrentBytes : cgroupMaxBytes;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Fallback to cgroup v1
|
|
150
|
+
if (!availableBytes) {
|
|
151
|
+
const v1Limit = Bun.file('/sys/fs/cgroup/memory/memory.limit_in_bytes');
|
|
152
|
+
const v1Usage = Bun.file('/sys/fs/cgroup/memory/memory.usage_in_bytes');
|
|
153
|
+
if ((await v1Limit.exists()) && (await v1Usage.exists())) {
|
|
154
|
+
const limitStr = (await v1Limit.text()).trim();
|
|
155
|
+
const usageStr = (await v1Usage.text()).trim();
|
|
156
|
+
if (/^\d+$/.test(limitStr) && /^\d+$/.test(usageStr)) {
|
|
157
|
+
cgroupMaxBytes = Number(limitStr);
|
|
158
|
+
cgroupCurrentBytes = Number(usageStr);
|
|
159
|
+
availableBytes = cgroupMaxBytes - cgroupCurrentBytes;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (!availableBytes) {
|
|
165
|
+
const { freemem } = await import('node:os');
|
|
166
|
+
availableBytes = freemem();
|
|
167
|
+
}
|
|
168
|
+
} catch {
|
|
169
|
+
// If detection fails, let JSC use its own defaults
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const jscEnv: Record<string, string> = {};
|
|
173
|
+
if (availableBytes > 0) {
|
|
174
|
+
const maxMb = Math.round(cgroupMaxBytes / 1024 / 1024);
|
|
175
|
+
const usedMb = Math.round(cgroupCurrentBytes / 1024 / 1024);
|
|
176
|
+
const availMb = Math.round(availableBytes / 1024 / 1024);
|
|
177
|
+
const maxHeap = Math.round(availableBytes * 0.8);
|
|
178
|
+
const maxHeapMb = Math.round(maxHeap / 1024 / 1024);
|
|
179
|
+
jscEnv.BUN_JSC_forceRAMSize = String(availableBytes);
|
|
180
|
+
jscEnv.BUN_JSC_gcMaxHeapSize = String(maxHeap);
|
|
181
|
+
logger.debug(
|
|
182
|
+
`[${label}] Memory: cgroup max=${maxMb} MiB, used=${usedMb} MiB, available=${availMb} MiB, gcMaxHeapSize=${maxHeapMb} MiB`
|
|
183
|
+
);
|
|
184
|
+
} else {
|
|
185
|
+
logger.debug(`[${label}] Could not detect available memory, using JSC defaults`);
|
|
186
|
+
}
|
|
187
|
+
return jscEnv;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function resolveWorkerScript(name: string): string {
|
|
191
|
+
const tsPath = fileURLToPath(new URL(`./${name}.ts`, import.meta.url));
|
|
192
|
+
const jsPath = fileURLToPath(new URL(`./${name}.js`, import.meta.url));
|
|
193
|
+
return existsSync(tsPath) ? tsPath : jsPath;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Spawn an isolated worker subprocess with JSC memory tuning.
|
|
198
|
+
* Returns the captured stdout tail (for parsing results) and throws on failure.
|
|
199
|
+
*/
|
|
200
|
+
async function spawnIsolatedWorker(opts: {
|
|
201
|
+
workerName: string;
|
|
202
|
+
label: string;
|
|
203
|
+
optionsJson: unknown;
|
|
204
|
+
cwd: string;
|
|
205
|
+
logger: Logger;
|
|
206
|
+
}): Promise<string> {
|
|
207
|
+
const { workerName, label, optionsJson, cwd, logger } = opts;
|
|
208
|
+
const workerScript = resolveWorkerScript(workerName);
|
|
209
|
+
const optionsPath = join(tmpdir(), `agentuity-${label}-${randomUUID()}.json`);
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
await Bun.write(optionsPath, JSON.stringify(optionsJson));
|
|
213
|
+
const jscEnv = await detectAvailableMemory(logger, label);
|
|
214
|
+
|
|
215
|
+
const proc = Bun.spawn({
|
|
216
|
+
cmd: [process.execPath, workerScript, optionsPath],
|
|
217
|
+
cwd,
|
|
218
|
+
env: { ...process.env, ...jscEnv },
|
|
219
|
+
stdin: 'ignore',
|
|
220
|
+
stdout: 'pipe',
|
|
221
|
+
stderr: 'pipe',
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const stdoutPromise =
|
|
225
|
+
proc.stdout && typeof proc.stdout !== 'number'
|
|
226
|
+
? drainSubprocessStream(proc.stdout, (chunk) => {
|
|
227
|
+
const text = chunk.trim();
|
|
228
|
+
if (text) logger.debug(`[${label}] ${text}`);
|
|
229
|
+
})
|
|
230
|
+
: Promise.resolve('');
|
|
231
|
+
|
|
232
|
+
const stderrPromise =
|
|
233
|
+
proc.stderr && typeof proc.stderr !== 'number'
|
|
234
|
+
? drainSubprocessStream(proc.stderr, (chunk) => {
|
|
235
|
+
const text = chunk.trim();
|
|
236
|
+
if (text) logger.error(`[${label}] ${text}`);
|
|
237
|
+
})
|
|
238
|
+
: Promise.resolve('');
|
|
239
|
+
|
|
240
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
241
|
+
stdoutPromise,
|
|
242
|
+
stderrPromise,
|
|
243
|
+
proc.exited,
|
|
244
|
+
]);
|
|
245
|
+
|
|
246
|
+
if (exitCode !== 0) {
|
|
247
|
+
const errorOutput = stderr.trim() || stdout.trim();
|
|
248
|
+
const suffix = errorOutput ? `: ${errorOutput}` : '';
|
|
249
|
+
throw new BuildFailedError({
|
|
250
|
+
message: `${label} failed with exit code ${exitCode}${suffix}`,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return stdout;
|
|
254
|
+
} catch (error) {
|
|
255
|
+
if (error instanceof Error && error.name === 'BuildFailedError') throw error;
|
|
256
|
+
throw new BuildFailedError({
|
|
257
|
+
message: `Failed to run ${label}: ${error instanceof Error ? error.message : String(error)}`,
|
|
258
|
+
});
|
|
259
|
+
} finally {
|
|
260
|
+
rmSync(optionsPath, { force: true });
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async function runViteBuildInSubprocess(
|
|
265
|
+
options: ViteBuildWorkerOptions,
|
|
266
|
+
logger: Logger
|
|
267
|
+
): Promise<void> {
|
|
268
|
+
await spawnIsolatedWorker({
|
|
269
|
+
workerName: 'vite-build-worker',
|
|
270
|
+
label: `vite-build-worker:${options.mode}`,
|
|
271
|
+
optionsJson: options,
|
|
272
|
+
cwd: options.rootDir,
|
|
273
|
+
logger,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async function runStaticRenderInSubprocess(
|
|
278
|
+
rootDir: string,
|
|
279
|
+
logger: Logger
|
|
280
|
+
): Promise<{ routes: number; duration: number }> {
|
|
281
|
+
const stdout = await spawnIsolatedWorker({
|
|
282
|
+
workerName: 'static-render-worker',
|
|
283
|
+
label: 'static-render-worker',
|
|
284
|
+
optionsJson: { rootDir },
|
|
285
|
+
cwd: rootDir,
|
|
286
|
+
logger,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// Worker writes JSON result as last line of stdout
|
|
290
|
+
try {
|
|
291
|
+
// Find the last JSON line in the tail
|
|
292
|
+
const lines = stdout.split('\n').filter((l) => l.trim());
|
|
293
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
294
|
+
const line = lines[i]!.trim();
|
|
295
|
+
if (line.startsWith('{')) {
|
|
296
|
+
return JSON.parse(line);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
} catch {
|
|
300
|
+
// Parse failure — return defaults
|
|
301
|
+
}
|
|
302
|
+
return { routes: 0, duration: 0 };
|
|
303
|
+
}
|
|
304
|
+
|
|
73
305
|
/**
|
|
74
306
|
* Run a Vite build for the specified mode
|
|
75
307
|
* Uses inline Vite config (customizable via agentuity.config.ts)
|
|
@@ -256,6 +488,9 @@ export async function runViteBuild(options: ViteBuildOptions): Promise<void> {
|
|
|
256
488
|
// Copy public files to output for CDN upload (production builds only)
|
|
257
489
|
// In dev mode, Vite serves them directly from src/web/public/
|
|
258
490
|
copyPublicDir: !dev,
|
|
491
|
+
// Skip compressed size reporting to save memory — we measure
|
|
492
|
+
// sizes during the asset upload phase instead.
|
|
493
|
+
reportCompressedSize: false,
|
|
259
494
|
},
|
|
260
495
|
logLevel: isViteDebug ? 'info' : 'warn',
|
|
261
496
|
};
|
|
@@ -322,6 +557,7 @@ interface BuildResult {
|
|
|
322
557
|
*/
|
|
323
558
|
export async function runAllBuilds(options: Omit<ViteBuildOptions, 'mode'>): Promise<BuildResult> {
|
|
324
559
|
const { rootDir, projectId = '', dev = false, logger, collector } = options;
|
|
560
|
+
const { logger: _logger, collector: _collector, ...workerBaseOptions } = options;
|
|
325
561
|
|
|
326
562
|
if (!dev) {
|
|
327
563
|
rmSync(join(rootDir, '.agentuity'), { force: true, recursive: true });
|
|
@@ -393,13 +629,26 @@ export async function runAllBuilds(options: Omit<ViteBuildOptions, 'mode'>): Pro
|
|
|
393
629
|
logger.debug('Building client assets...');
|
|
394
630
|
const endClientDiagnostic = collector?.startDiagnostic('client-build');
|
|
395
631
|
const started = Date.now();
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
632
|
+
if (dev) {
|
|
633
|
+
await runViteBuild({
|
|
634
|
+
...options,
|
|
635
|
+
mode: 'client',
|
|
636
|
+
workbenchEnabled: workbenchConfig.enabled,
|
|
637
|
+
workbenchRoute: workbenchConfig.route,
|
|
638
|
+
analyticsEnabled,
|
|
639
|
+
});
|
|
640
|
+
} else {
|
|
641
|
+
await runViteBuildInSubprocess(
|
|
642
|
+
{
|
|
643
|
+
...workerBaseOptions,
|
|
644
|
+
mode: 'client',
|
|
645
|
+
workbenchEnabled: workbenchConfig.enabled,
|
|
646
|
+
workbenchRoute: workbenchConfig.route,
|
|
647
|
+
analyticsEnabled,
|
|
648
|
+
},
|
|
649
|
+
logger
|
|
650
|
+
);
|
|
651
|
+
}
|
|
403
652
|
result.client.included = true;
|
|
404
653
|
result.client.duration = Date.now() - started;
|
|
405
654
|
endClientDiagnostic?.();
|
|
@@ -411,15 +660,22 @@ export async function runAllBuilds(options: Omit<ViteBuildOptions, 'mode'>): Pro
|
|
|
411
660
|
if (config?.render === 'static' && hasWebFrontend) {
|
|
412
661
|
logger.debug('Running static rendering (pre-rendering all routes)...');
|
|
413
662
|
const endStaticDiagnostic = collector?.startDiagnostic('static-render');
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
663
|
+
if (dev) {
|
|
664
|
+
const { runStaticRender } = await import('./static-renderer');
|
|
665
|
+
const staticResult = await runStaticRender({
|
|
666
|
+
rootDir,
|
|
667
|
+
logger,
|
|
668
|
+
userPlugins: config?.plugins || [],
|
|
669
|
+
});
|
|
670
|
+
result.static.included = true;
|
|
671
|
+
result.static.duration = staticResult.duration;
|
|
672
|
+
result.static.routes = staticResult.routes;
|
|
673
|
+
} else {
|
|
674
|
+
const staticResult = await runStaticRenderInSubprocess(rootDir, logger);
|
|
675
|
+
result.static.included = true;
|
|
676
|
+
result.static.duration = staticResult.duration;
|
|
677
|
+
result.static.routes = staticResult.routes;
|
|
678
|
+
}
|
|
423
679
|
endStaticDiagnostic?.();
|
|
424
680
|
}
|
|
425
681
|
|
|
@@ -428,12 +684,24 @@ export async function runAllBuilds(options: Omit<ViteBuildOptions, 'mode'>): Pro
|
|
|
428
684
|
logger.debug('Building workbench assets...');
|
|
429
685
|
const endWorkbenchDiagnostic = collector?.startDiagnostic('workbench-build');
|
|
430
686
|
const started = Date.now();
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
687
|
+
if (dev) {
|
|
688
|
+
await runViteBuild({
|
|
689
|
+
...options,
|
|
690
|
+
mode: 'workbench',
|
|
691
|
+
workbenchRoute: workbenchConfig.route,
|
|
692
|
+
workbenchEnabled: true,
|
|
693
|
+
});
|
|
694
|
+
} else {
|
|
695
|
+
await runViteBuildInSubprocess(
|
|
696
|
+
{
|
|
697
|
+
...workerBaseOptions,
|
|
698
|
+
mode: 'workbench',
|
|
699
|
+
workbenchRoute: workbenchConfig.route,
|
|
700
|
+
workbenchEnabled: true,
|
|
701
|
+
},
|
|
702
|
+
logger
|
|
703
|
+
);
|
|
704
|
+
}
|
|
437
705
|
result.workbench.included = true;
|
|
438
706
|
result.workbench.duration = Date.now() - started;
|
|
439
707
|
endWorkbenchDiagnostic?.();
|
|
@@ -443,7 +711,11 @@ export async function runAllBuilds(options: Omit<ViteBuildOptions, 'mode'>): Pro
|
|
|
443
711
|
logger.debug('Building server...');
|
|
444
712
|
const endServerDiagnostic = collector?.startDiagnostic('server-build');
|
|
445
713
|
const serverStarted = Date.now();
|
|
446
|
-
|
|
714
|
+
if (dev) {
|
|
715
|
+
await runViteBuild({ ...options, mode: 'server' });
|
|
716
|
+
} else {
|
|
717
|
+
await runViteBuildInSubprocess({ ...workerBaseOptions, mode: 'server' }, logger);
|
|
718
|
+
}
|
|
447
719
|
result.server.included = true;
|
|
448
720
|
result.server.duration = Date.now() - serverStarted;
|
|
449
721
|
endServerDiagnostic?.();
|