neoctl 0.1.18 → 0.1.20

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.
@@ -1,139 +1,139 @@
1
- #!/usr/bin/env node
2
- import fs from "node:fs/promises";
3
- import { existsSync } from "node:fs";
4
- import path from "node:path";
5
- import { execFile } from "node:child_process";
6
- import { fileURLToPath } from "node:url";
7
- import { promisify } from "node:util";
8
-
9
- const execFileAsync = promisify(execFile);
10
- const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
11
- const outRoot = path.join(projectRoot, "standalone");
12
- const workRoot = path.join(projectRoot, ".standalone-build");
13
- const bootstrapPath = path.join(workRoot, "bootstrap.cjs");
14
- const seaConfigPath = path.join(workRoot, "sea-config.json");
15
- const seaBlobPath = path.join(workRoot, "neo.blob");
16
- const packageJson = JSON.parse(await fs.readFile(path.join(projectRoot, "package.json"), "utf8"));
17
- const appName = "neo";
18
- const platformKey = `${process.platform}-${process.arch}`;
19
- const exeName = process.platform === "win32" ? `${appName}.exe` : appName;
20
- const outDir = path.join(outRoot, platformKey);
21
- const outExePath = path.join(outDir, exeName);
22
- const nodeBin = process.execPath;
23
- const postjectCli = path.join(projectRoot, "node_modules", "postject", "dist", "cli.js");
24
-
25
- await main();
26
-
27
- async function main() {
28
- await fs.rm(workRoot, { recursive: true, force: true });
29
- await fs.rm(outDir, { recursive: true, force: true });
30
- await fs.mkdir(workRoot, { recursive: true });
31
- await fs.mkdir(outDir, { recursive: true });
32
-
33
- await ensureRipgrep();
34
- await writeBootstrap();
35
- await writeSeaConfig();
36
- await runNode(["--experimental-sea-config", seaConfigPath]);
37
- await copyNodeBinary();
38
- await injectSeaBlob();
39
- await copyRuntimeFiles();
40
- await writeManifest();
41
-
42
- console.log(`[standalone] built ${path.relative(projectRoot, outExePath)}`);
43
- }
44
-
45
- async function ensureRipgrep() {
46
- const executable = process.platform === "win32" ? "rg.exe" : "rg";
47
- const rgPath = path.join(projectRoot, "vendor", "ripgrep", platformKey, executable);
48
- if (existsSync(rgPath)) return;
49
- await runNode([path.join(projectRoot, "scripts", "install-ripgrep.cjs")]);
50
- }
51
-
52
- async function writeBootstrap() {
53
- await fs.writeFile(bootstrapPath, `
54
- const path = require("node:path");
55
- const { pathToFileURL } = require("node:url");
56
-
57
- const exeDir = path.dirname(process.execPath);
58
- const entry = path.join(exeDir, "dist", "repl", "index.js");
59
- const userArgs = process.argv.slice(1);
60
- process.argv = [process.execPath, entry, ...userArgs];
61
- process.env.AGENT_VENDOR_DIR ||= exeDir;
62
-
63
- import(pathToFileURL(entry).href).catch((error) => {
64
- console.error(error && error.stack ? error.stack : String(error));
65
- process.exitCode = 1;
66
- });
67
- `, "utf8");
68
- }
69
-
70
- async function writeSeaConfig() {
71
- await fs.writeFile(
72
- seaConfigPath,
73
- `${JSON.stringify({
74
- main: bootstrapPath,
75
- output: seaBlobPath,
76
- disableExperimentalSEAWarning: true,
77
- useSnapshot: false,
78
- useCodeCache: false,
79
- }, null, 2)}\n`,
80
- "utf8",
81
- );
82
- }
83
-
84
- async function copyNodeBinary() {
85
- await fs.copyFile(nodeBin, outExePath);
86
- if (process.platform !== "win32") await fs.chmod(outExePath, 0o755);
87
- }
88
-
89
- async function injectSeaBlob() {
90
- if (!existsSync(postjectCli)) throw new Error(`postject not found at ${postjectCli}. Run npm install first.`);
91
- const args = [postjectCli, outExePath, "NODE_SEA_BLOB", seaBlobPath, "--overwrite", "--sentinel-fuse", "NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2"];
92
- if (process.platform === "darwin") args.push("--macho-segment-name", "NODE_SEA");
93
- await execFileAsync(nodeBin, args, { cwd: projectRoot, maxBuffer: 1024 * 1024 * 20 });
94
- }
95
-
96
- async function copyRuntimeFiles() {
97
- await fs.cp(path.join(projectRoot, "dist"), path.join(outDir, "dist"), { recursive: true });
98
- await fs.cp(path.join(projectRoot, "node_modules"), path.join(outDir, "node_modules"), {
99
- recursive: true,
100
- filter: (source) => !source.includes(`${path.sep}.cache${path.sep}`),
101
- });
102
-
103
- const packageForRuntime = {
104
- name: packageJson.name,
105
- version: packageJson.version,
106
- type: packageJson.type,
107
- dependencies: packageJson.dependencies,
108
- };
109
- await fs.writeFile(path.join(outDir, "package.json"), `${JSON.stringify(packageForRuntime, null, 2)}\n`, "utf8");
110
-
111
- const rgSource = path.join(projectRoot, "vendor", "ripgrep", platformKey);
112
- if (existsSync(rgSource)) {
113
- await fs.cp(rgSource, path.join(outDir, "vendor", "ripgrep", platformKey), { recursive: true });
114
- }
115
- }
116
-
117
- async function writeManifest() {
118
- await fs.writeFile(
119
- path.join(outDir, "manifest.json"),
120
- `${JSON.stringify({
121
- name: packageJson.name,
122
- command: appName,
123
- version: packageJson.version,
124
- node: process.version,
125
- platform: process.platform,
126
- arch: process.arch,
127
- platformKey,
128
- builtAt: new Date().toISOString(),
129
- executable: exeName,
130
- entry: "dist/repl/index.js",
131
- notes: "Portable standalone distribution built with Node.js SEA bootstrap. Node.js does not need to be installed on the target machine; keep the bundled files beside the executable.",
132
- }, null, 2)}\n`,
133
- "utf8",
134
- );
135
- }
136
-
137
- async function runNode(args) {
138
- await execFileAsync(nodeBin, args, { cwd: projectRoot, maxBuffer: 1024 * 1024 * 20 });
139
- }
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs/promises";
3
+ import { existsSync } from "node:fs";
4
+ import path from "node:path";
5
+ import { execFile } from "node:child_process";
6
+ import { fileURLToPath } from "node:url";
7
+ import { promisify } from "node:util";
8
+
9
+ const execFileAsync = promisify(execFile);
10
+ const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
11
+ const outRoot = path.join(projectRoot, "standalone");
12
+ const workRoot = path.join(projectRoot, ".standalone-build");
13
+ const bootstrapPath = path.join(workRoot, "bootstrap.cjs");
14
+ const seaConfigPath = path.join(workRoot, "sea-config.json");
15
+ const seaBlobPath = path.join(workRoot, "neo.blob");
16
+ const packageJson = JSON.parse(await fs.readFile(path.join(projectRoot, "package.json"), "utf8"));
17
+ const appName = "neo";
18
+ const platformKey = `${process.platform}-${process.arch}`;
19
+ const exeName = process.platform === "win32" ? `${appName}.exe` : appName;
20
+ const outDir = path.join(outRoot, platformKey);
21
+ const outExePath = path.join(outDir, exeName);
22
+ const nodeBin = process.execPath;
23
+ const postjectCli = path.join(projectRoot, "node_modules", "postject", "dist", "cli.js");
24
+
25
+ await main();
26
+
27
+ async function main() {
28
+ await fs.rm(workRoot, { recursive: true, force: true });
29
+ await fs.rm(outDir, { recursive: true, force: true });
30
+ await fs.mkdir(workRoot, { recursive: true });
31
+ await fs.mkdir(outDir, { recursive: true });
32
+
33
+ await ensureRipgrep();
34
+ await writeBootstrap();
35
+ await writeSeaConfig();
36
+ await runNode(["--experimental-sea-config", seaConfigPath]);
37
+ await copyNodeBinary();
38
+ await injectSeaBlob();
39
+ await copyRuntimeFiles();
40
+ await writeManifest();
41
+
42
+ console.log(`[standalone] built ${path.relative(projectRoot, outExePath)}`);
43
+ }
44
+
45
+ async function ensureRipgrep() {
46
+ const executable = process.platform === "win32" ? "rg.exe" : "rg";
47
+ const rgPath = path.join(projectRoot, "vendor", "ripgrep", platformKey, executable);
48
+ if (existsSync(rgPath)) return;
49
+ await runNode([path.join(projectRoot, "scripts", "install-ripgrep.cjs")]);
50
+ }
51
+
52
+ async function writeBootstrap() {
53
+ await fs.writeFile(bootstrapPath, `
54
+ const path = require("node:path");
55
+ const { pathToFileURL } = require("node:url");
56
+
57
+ const exeDir = path.dirname(process.execPath);
58
+ const entry = path.join(exeDir, "dist", "repl", "index.js");
59
+ const userArgs = process.argv.slice(1);
60
+ process.argv = [process.execPath, entry, ...userArgs];
61
+ process.env.AGENT_VENDOR_DIR ||= exeDir;
62
+
63
+ import(pathToFileURL(entry).href).catch((error) => {
64
+ console.error(error && error.stack ? error.stack : String(error));
65
+ process.exitCode = 1;
66
+ });
67
+ `, "utf8");
68
+ }
69
+
70
+ async function writeSeaConfig() {
71
+ await fs.writeFile(
72
+ seaConfigPath,
73
+ `${JSON.stringify({
74
+ main: bootstrapPath,
75
+ output: seaBlobPath,
76
+ disableExperimentalSEAWarning: true,
77
+ useSnapshot: false,
78
+ useCodeCache: false,
79
+ }, null, 2)}\n`,
80
+ "utf8",
81
+ );
82
+ }
83
+
84
+ async function copyNodeBinary() {
85
+ await fs.copyFile(nodeBin, outExePath);
86
+ if (process.platform !== "win32") await fs.chmod(outExePath, 0o755);
87
+ }
88
+
89
+ async function injectSeaBlob() {
90
+ if (!existsSync(postjectCli)) throw new Error(`postject not found at ${postjectCli}. Run npm install first.`);
91
+ const args = [postjectCli, outExePath, "NODE_SEA_BLOB", seaBlobPath, "--overwrite", "--sentinel-fuse", "NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2"];
92
+ if (process.platform === "darwin") args.push("--macho-segment-name", "NODE_SEA");
93
+ await execFileAsync(nodeBin, args, { cwd: projectRoot, maxBuffer: 1024 * 1024 * 20 });
94
+ }
95
+
96
+ async function copyRuntimeFiles() {
97
+ await fs.cp(path.join(projectRoot, "dist"), path.join(outDir, "dist"), { recursive: true });
98
+ await fs.cp(path.join(projectRoot, "node_modules"), path.join(outDir, "node_modules"), {
99
+ recursive: true,
100
+ filter: (source) => !source.includes(`${path.sep}.cache${path.sep}`),
101
+ });
102
+
103
+ const packageForRuntime = {
104
+ name: packageJson.name,
105
+ version: packageJson.version,
106
+ type: packageJson.type,
107
+ dependencies: packageJson.dependencies,
108
+ };
109
+ await fs.writeFile(path.join(outDir, "package.json"), `${JSON.stringify(packageForRuntime, null, 2)}\n`, "utf8");
110
+
111
+ const rgSource = path.join(projectRoot, "vendor", "ripgrep", platformKey);
112
+ if (existsSync(rgSource)) {
113
+ await fs.cp(rgSource, path.join(outDir, "vendor", "ripgrep", platformKey), { recursive: true });
114
+ }
115
+ }
116
+
117
+ async function writeManifest() {
118
+ await fs.writeFile(
119
+ path.join(outDir, "manifest.json"),
120
+ `${JSON.stringify({
121
+ name: packageJson.name,
122
+ command: appName,
123
+ version: packageJson.version,
124
+ node: process.version,
125
+ platform: process.platform,
126
+ arch: process.arch,
127
+ platformKey,
128
+ builtAt: new Date().toISOString(),
129
+ executable: exeName,
130
+ entry: "dist/repl/index.js",
131
+ notes: "Portable standalone distribution built with Node.js SEA bootstrap. Node.js does not need to be installed on the target machine; keep the bundled files beside the executable.",
132
+ }, null, 2)}\n`,
133
+ "utf8",
134
+ );
135
+ }
136
+
137
+ async function runNode(args) {
138
+ await execFileAsync(nodeBin, args, { cwd: projectRoot, maxBuffer: 1024 * 1024 * 20 });
139
+ }