ludus-cli 0.1.1 → 0.1.2
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/README.md +65 -0
- package/install.js +184 -184
- package/package.json +6 -6
- package/run.js +33 -33
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# ludus-cli
|
|
2
|
+
|
|
3
|
+
CLI tool that automates the end-to-end pipeline for deploying Unreal Engine 5 dedicated servers to AWS GameLift.
|
|
4
|
+
|
|
5
|
+
Ludus handles the entire workflow that would otherwise require dozens of manual steps across multiple tools: UE5 source builds, game server compilation, Docker containerization, ECR push, and GameLift fleet deployment.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g ludus-cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or run directly:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx ludus-cli --help
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## What it does
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
ludus run --verbose
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This single command orchestrates six stages:
|
|
26
|
+
|
|
27
|
+
1. **Prerequisite validation** — OS, engine source, game content, Docker, AWS CLI, disk space, RAM
|
|
28
|
+
2. **Engine build** — UE5 source compilation
|
|
29
|
+
3. **Game server build** — Dedicated server packaging via RunUAT
|
|
30
|
+
4. **Container build** — Dockerfile generation and Docker image build
|
|
31
|
+
5. **ECR push** — Docker image push to Amazon ECR
|
|
32
|
+
6. **GameLift deploy** — Container fleet creation with IAM roles and polling
|
|
33
|
+
|
|
34
|
+
## Deployment targets
|
|
35
|
+
|
|
36
|
+
| Target | Command | Docker required? | ARM64 (Graviton) |
|
|
37
|
+
|--------|---------|:---:|:---:|
|
|
38
|
+
| GameLift Containers | `ludus deploy fleet` | Yes | Yes |
|
|
39
|
+
| CloudFormation Stack | `ludus deploy stack` | Yes | Yes |
|
|
40
|
+
| GameLift Managed EC2 | `ludus deploy ec2` | No | Yes |
|
|
41
|
+
| GameLift Anywhere | `ludus deploy anywhere` | No | No |
|
|
42
|
+
| Binary export | `ludus deploy binary` | No | Yes |
|
|
43
|
+
|
|
44
|
+
## AI Agent Integration (MCP)
|
|
45
|
+
|
|
46
|
+
Ludus includes a built-in [Model Context Protocol](https://modelcontextprotocol.io/) server exposing 21 tools. Any MCP-compatible AI agent can orchestrate the full pipeline programmatically.
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"mcpServers": {
|
|
51
|
+
"ludus": {
|
|
52
|
+
"command": "npx",
|
|
53
|
+
"args": ["-y", "ludus-cli", "mcp"]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Documentation
|
|
60
|
+
|
|
61
|
+
Full documentation, configuration reference, and prerequisites: [github.com/jpvelasco/ludus](https://github.com/jpvelasco/ludus)
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
[MIT](https://github.com/jpvelasco/ludus/blob/main/LICENSE)
|
package/install.js
CHANGED
|
@@ -1,184 +1,184 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
const fs = require("fs");
|
|
5
|
-
const path = require("path");
|
|
6
|
-
const https = require("https");
|
|
7
|
-
const crypto = require("crypto");
|
|
8
|
-
const { spawnSync } = require("child_process");
|
|
9
|
-
|
|
10
|
-
const REPO = "jpvelasco/ludus";
|
|
11
|
-
const MAX_REDIRECTS = 5;
|
|
12
|
-
|
|
13
|
-
const PLATFORM_MAP = {
|
|
14
|
-
linux: "linux",
|
|
15
|
-
darwin: "darwin",
|
|
16
|
-
win32: "windows",
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const ARCH_MAP = {
|
|
20
|
-
x64: "amd64",
|
|
21
|
-
arm64: "arm64",
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
function getPackageVersion() {
|
|
25
|
-
const pkg = JSON.parse(
|
|
26
|
-
fs.readFileSync(path.join(__dirname, "package.json"), "utf8")
|
|
27
|
-
);
|
|
28
|
-
const version = pkg.version;
|
|
29
|
-
|
|
30
|
-
// Validate semver format to prevent URL injection
|
|
31
|
-
if (!/^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?(\+[a-zA-Z0-9.]+)?$/.test(version)) {
|
|
32
|
-
throw new Error(`Invalid version format: ${version}`);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return version;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function getExpectedChecksum(archiveName) {
|
|
39
|
-
const pkg = JSON.parse(
|
|
40
|
-
fs.readFileSync(path.join(__dirname, "package.json"), "utf8")
|
|
41
|
-
);
|
|
42
|
-
return pkg.binaryChecksums?.[archiveName] || null;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function getArchiveName(version, platform, arch) {
|
|
46
|
-
const os = PLATFORM_MAP[platform];
|
|
47
|
-
const cpu = ARCH_MAP[arch];
|
|
48
|
-
if (!os || !cpu) {
|
|
49
|
-
throw new Error(`Unsupported platform: ${platform}/${arch}`);
|
|
50
|
-
}
|
|
51
|
-
const ext = platform === "win32" ? "zip" : "tar.gz";
|
|
52
|
-
return `ludus_${version}_${os}_${cpu}.${ext}`;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function download(url, redirectCount = 0) {
|
|
56
|
-
return new Promise((resolve, reject) => {
|
|
57
|
-
if (redirectCount > MAX_REDIRECTS) {
|
|
58
|
-
return reject(new Error(`Too many redirects (max ${MAX_REDIRECTS})`));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
https
|
|
62
|
-
.get(url, (res) => {
|
|
63
|
-
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
64
|
-
return download(res.headers.location, redirectCount + 1).then(resolve, reject);
|
|
65
|
-
}
|
|
66
|
-
if (res.statusCode !== 200) {
|
|
67
|
-
return reject(new Error(`Download failed: HTTP ${res.statusCode} for ${url}`));
|
|
68
|
-
}
|
|
69
|
-
const chunks = [];
|
|
70
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
71
|
-
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
72
|
-
res.on("error", reject);
|
|
73
|
-
})
|
|
74
|
-
.on("error", reject);
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function verifyChecksum(buffer, archiveName) {
|
|
79
|
-
const expected = getExpectedChecksum(archiveName);
|
|
80
|
-
if (!expected) {
|
|
81
|
-
console.log("ludus-cli: no checksum available, skipping verification");
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const actual = crypto.createHash("sha256").update(buffer).digest("hex");
|
|
86
|
-
if (actual !== expected) {
|
|
87
|
-
throw new Error(
|
|
88
|
-
`Checksum mismatch for ${archiveName}\n` +
|
|
89
|
-
` Expected: ${expected}\n` +
|
|
90
|
-
` Actual: ${actual}`
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
console.log("ludus-cli: checksum verified (SHA-256)");
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Escape a string for use inside PowerShell single quotes.
|
|
97
|
-
// PowerShell single-quoted strings only need '' to represent a literal '.
|
|
98
|
-
function psEscape(s) {
|
|
99
|
-
return s.replace(/'/g, "''");
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function spawnOrFail(cmd, args, label) {
|
|
103
|
-
const result = spawnSync(cmd, args, { stdio: "pipe" });
|
|
104
|
-
if (result.error) {
|
|
105
|
-
throw new Error(`${label}: ${result.error.message}`);
|
|
106
|
-
}
|
|
107
|
-
if (result.status !== 0) {
|
|
108
|
-
const stderr = result.stderr ? result.stderr.toString().trim() : "";
|
|
109
|
-
throw new Error(`${label} exited with code ${result.status}${stderr ? ": " + stderr : ""}`);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function extract(buffer, archiveName, binDir) {
|
|
114
|
-
const tmpDir = path.join(__dirname, ".tmp-install");
|
|
115
|
-
fs.mkdirSync(tmpDir, { recursive: true });
|
|
116
|
-
|
|
117
|
-
const archivePath = path.join(tmpDir, archiveName);
|
|
118
|
-
fs.writeFileSync(archivePath, buffer);
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
if (archiveName.endsWith(".zip")) {
|
|
122
|
-
if (process.platform === "win32") {
|
|
123
|
-
spawnOrFail(
|
|
124
|
-
"powershell",
|
|
125
|
-
[
|
|
126
|
-
"-NoProfile",
|
|
127
|
-
"-Command",
|
|
128
|
-
`Expand-Archive -Force -Path '${psEscape(archivePath)}' -DestinationPath '${psEscape(tmpDir)}'`,
|
|
129
|
-
],
|
|
130
|
-
"Expand-Archive"
|
|
131
|
-
);
|
|
132
|
-
} else {
|
|
133
|
-
spawnOrFail("unzip", ["-o", archivePath, "-d", tmpDir], "unzip");
|
|
134
|
-
}
|
|
135
|
-
} else {
|
|
136
|
-
spawnOrFail("tar", ["-xzf", archivePath, "-C", tmpDir], "tar");
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Find the binary in the extracted files
|
|
140
|
-
const binaryName = process.platform === "win32" ? "ludus.exe" : "ludus";
|
|
141
|
-
const extractedBinary = path.join(tmpDir, binaryName);
|
|
142
|
-
|
|
143
|
-
if (!fs.existsSync(extractedBinary)) {
|
|
144
|
-
throw new Error(`Binary ${binaryName} not found in archive`);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
fs.mkdirSync(binDir, { recursive: true });
|
|
148
|
-
const destBinary = path.join(binDir, binaryName);
|
|
149
|
-
fs.copyFileSync(extractedBinary, destBinary);
|
|
150
|
-
|
|
151
|
-
if (process.platform !== "win32") {
|
|
152
|
-
fs.chmodSync(destBinary, 0o755);
|
|
153
|
-
}
|
|
154
|
-
} finally {
|
|
155
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async function main() {
|
|
160
|
-
const version = getPackageVersion();
|
|
161
|
-
if (version === "0.0.0") {
|
|
162
|
-
console.error("ludus-cli: skipping binary download for development version");
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const archiveName = getArchiveName(version, process.platform, process.arch);
|
|
167
|
-
const url = `https://github.com/${REPO}/releases/download/v${version}/${archiveName}`;
|
|
168
|
-
const binDir = path.join(__dirname, "bin");
|
|
169
|
-
|
|
170
|
-
console.log(`ludus-cli: downloading ${archiveName}...`);
|
|
171
|
-
const buffer = await download(url);
|
|
172
|
-
|
|
173
|
-
verifyChecksum(buffer, archiveName);
|
|
174
|
-
|
|
175
|
-
console.log("ludus-cli: extracting binary...");
|
|
176
|
-
extract(buffer, archiveName, binDir);
|
|
177
|
-
|
|
178
|
-
console.log("ludus-cli: installed successfully");
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
main().catch((err) => {
|
|
182
|
-
console.error(`ludus-cli: installation failed: ${err.message}`);
|
|
183
|
-
process.exit(1);
|
|
184
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const https = require("https");
|
|
7
|
+
const crypto = require("crypto");
|
|
8
|
+
const { spawnSync } = require("child_process");
|
|
9
|
+
|
|
10
|
+
const REPO = "jpvelasco/ludus";
|
|
11
|
+
const MAX_REDIRECTS = 5;
|
|
12
|
+
|
|
13
|
+
const PLATFORM_MAP = {
|
|
14
|
+
linux: "linux",
|
|
15
|
+
darwin: "darwin",
|
|
16
|
+
win32: "windows",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const ARCH_MAP = {
|
|
20
|
+
x64: "amd64",
|
|
21
|
+
arm64: "arm64",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function getPackageVersion() {
|
|
25
|
+
const pkg = JSON.parse(
|
|
26
|
+
fs.readFileSync(path.join(__dirname, "package.json"), "utf8")
|
|
27
|
+
);
|
|
28
|
+
const version = pkg.version;
|
|
29
|
+
|
|
30
|
+
// Validate semver format to prevent URL injection
|
|
31
|
+
if (!/^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?(\+[a-zA-Z0-9.]+)?$/.test(version)) {
|
|
32
|
+
throw new Error(`Invalid version format: ${version}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return version;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getExpectedChecksum(archiveName) {
|
|
39
|
+
const pkg = JSON.parse(
|
|
40
|
+
fs.readFileSync(path.join(__dirname, "package.json"), "utf8")
|
|
41
|
+
);
|
|
42
|
+
return pkg.binaryChecksums?.[archiveName] || null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getArchiveName(version, platform, arch) {
|
|
46
|
+
const os = PLATFORM_MAP[platform];
|
|
47
|
+
const cpu = ARCH_MAP[arch];
|
|
48
|
+
if (!os || !cpu) {
|
|
49
|
+
throw new Error(`Unsupported platform: ${platform}/${arch}`);
|
|
50
|
+
}
|
|
51
|
+
const ext = platform === "win32" ? "zip" : "tar.gz";
|
|
52
|
+
return `ludus_${version}_${os}_${cpu}.${ext}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function download(url, redirectCount = 0) {
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
if (redirectCount > MAX_REDIRECTS) {
|
|
58
|
+
return reject(new Error(`Too many redirects (max ${MAX_REDIRECTS})`));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
https
|
|
62
|
+
.get(url, (res) => {
|
|
63
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
64
|
+
return download(res.headers.location, redirectCount + 1).then(resolve, reject);
|
|
65
|
+
}
|
|
66
|
+
if (res.statusCode !== 200) {
|
|
67
|
+
return reject(new Error(`Download failed: HTTP ${res.statusCode} for ${url}`));
|
|
68
|
+
}
|
|
69
|
+
const chunks = [];
|
|
70
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
71
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
72
|
+
res.on("error", reject);
|
|
73
|
+
})
|
|
74
|
+
.on("error", reject);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function verifyChecksum(buffer, archiveName) {
|
|
79
|
+
const expected = getExpectedChecksum(archiveName);
|
|
80
|
+
if (!expected) {
|
|
81
|
+
console.log("ludus-cli: no checksum available, skipping verification");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const actual = crypto.createHash("sha256").update(buffer).digest("hex");
|
|
86
|
+
if (actual !== expected) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Checksum mismatch for ${archiveName}\n` +
|
|
89
|
+
` Expected: ${expected}\n` +
|
|
90
|
+
` Actual: ${actual}`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
console.log("ludus-cli: checksum verified (SHA-256)");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Escape a string for use inside PowerShell single quotes.
|
|
97
|
+
// PowerShell single-quoted strings only need '' to represent a literal '.
|
|
98
|
+
function psEscape(s) {
|
|
99
|
+
return s.replace(/'/g, "''");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function spawnOrFail(cmd, args, label) {
|
|
103
|
+
const result = spawnSync(cmd, args, { stdio: "pipe" });
|
|
104
|
+
if (result.error) {
|
|
105
|
+
throw new Error(`${label}: ${result.error.message}`);
|
|
106
|
+
}
|
|
107
|
+
if (result.status !== 0) {
|
|
108
|
+
const stderr = result.stderr ? result.stderr.toString().trim() : "";
|
|
109
|
+
throw new Error(`${label} exited with code ${result.status}${stderr ? ": " + stderr : ""}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function extract(buffer, archiveName, binDir) {
|
|
114
|
+
const tmpDir = path.join(__dirname, ".tmp-install");
|
|
115
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
116
|
+
|
|
117
|
+
const archivePath = path.join(tmpDir, archiveName);
|
|
118
|
+
fs.writeFileSync(archivePath, buffer);
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
if (archiveName.endsWith(".zip")) {
|
|
122
|
+
if (process.platform === "win32") {
|
|
123
|
+
spawnOrFail(
|
|
124
|
+
"powershell",
|
|
125
|
+
[
|
|
126
|
+
"-NoProfile",
|
|
127
|
+
"-Command",
|
|
128
|
+
`Expand-Archive -Force -Path '${psEscape(archivePath)}' -DestinationPath '${psEscape(tmpDir)}'`,
|
|
129
|
+
],
|
|
130
|
+
"Expand-Archive"
|
|
131
|
+
);
|
|
132
|
+
} else {
|
|
133
|
+
spawnOrFail("unzip", ["-o", archivePath, "-d", tmpDir], "unzip");
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
spawnOrFail("tar", ["-xzf", archivePath, "-C", tmpDir], "tar");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Find the binary in the extracted files
|
|
140
|
+
const binaryName = process.platform === "win32" ? "ludus.exe" : "ludus";
|
|
141
|
+
const extractedBinary = path.join(tmpDir, binaryName);
|
|
142
|
+
|
|
143
|
+
if (!fs.existsSync(extractedBinary)) {
|
|
144
|
+
throw new Error(`Binary ${binaryName} not found in archive`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
148
|
+
const destBinary = path.join(binDir, binaryName);
|
|
149
|
+
fs.copyFileSync(extractedBinary, destBinary);
|
|
150
|
+
|
|
151
|
+
if (process.platform !== "win32") {
|
|
152
|
+
fs.chmodSync(destBinary, 0o755);
|
|
153
|
+
}
|
|
154
|
+
} finally {
|
|
155
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function main() {
|
|
160
|
+
const version = getPackageVersion();
|
|
161
|
+
if (version === "0.0.0") {
|
|
162
|
+
console.error("ludus-cli: skipping binary download for development version");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const archiveName = getArchiveName(version, process.platform, process.arch);
|
|
167
|
+
const url = `https://github.com/${REPO}/releases/download/v${version}/${archiveName}`;
|
|
168
|
+
const binDir = path.join(__dirname, "bin");
|
|
169
|
+
|
|
170
|
+
console.log(`ludus-cli: downloading ${archiveName}...`);
|
|
171
|
+
const buffer = await download(url);
|
|
172
|
+
|
|
173
|
+
verifyChecksum(buffer, archiveName);
|
|
174
|
+
|
|
175
|
+
console.log("ludus-cli: extracting binary...");
|
|
176
|
+
extract(buffer, archiveName, binDir);
|
|
177
|
+
|
|
178
|
+
console.log("ludus-cli: installed successfully");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
main().catch((err) => {
|
|
182
|
+
console.error(`ludus-cli: installation failed: ${err.message}`);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ludus-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "UE5 dedicated server deployment CLI with MCP server",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
"bin/"
|
|
29
29
|
],
|
|
30
30
|
"binaryChecksums": {
|
|
31
|
-
"ludus_0.1.
|
|
32
|
-
"ludus_0.1.
|
|
33
|
-
"ludus_0.1.
|
|
34
|
-
"ludus_0.1.
|
|
35
|
-
"ludus_0.1.
|
|
31
|
+
"ludus_0.1.2_darwin_amd64.tar.gz": "e036c5a6468158f89c2852a2f4ab295290ee54d484da284b0cd0091ca3622e8c",
|
|
32
|
+
"ludus_0.1.2_darwin_arm64.tar.gz": "56c54a21f4b533f5cef9fc7bb034e416bb7d584e84c7098eacbce47c3596e9c5",
|
|
33
|
+
"ludus_0.1.2_linux_amd64.tar.gz": "df6bb92ff63ab0b563cb94471058820b3f0f2571db3581a449299d4571fad5de",
|
|
34
|
+
"ludus_0.1.2_linux_arm64.tar.gz": "7438a6734d30c8f03b7ba0767eeb31f6d359c808f484f7222ef0af86e9c9465f",
|
|
35
|
+
"ludus_0.1.2_windows_amd64.zip": "d8497c8216b8266e16877319df0b154e5b7ebfcf93a93a07843b3d8a4802d516"
|
|
36
36
|
}
|
|
37
37
|
}
|
package/run.js
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
const path = require("path");
|
|
5
|
-
const { spawn } = require("child_process");
|
|
6
|
-
|
|
7
|
-
const binaryName = process.platform === "win32" ? "ludus.exe" : "ludus";
|
|
8
|
-
const binaryPath = path.join(__dirname, "bin", binaryName);
|
|
9
|
-
|
|
10
|
-
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
11
|
-
stdio: "inherit",
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
// Forward signals so the Go binary shuts down cleanly
|
|
15
|
-
["SIGINT", "SIGTERM", "SIGHUP"].forEach((sig) => {
|
|
16
|
-
process.on(sig, () => child.kill(sig));
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
child.on("error", (err) => {
|
|
20
|
-
if (err.code === "ENOENT") {
|
|
21
|
-
console.error(
|
|
22
|
-
`ludus-cli: binary not found at ${binaryPath}\n` +
|
|
23
|
-
"Run 'npm rebuild ludus-cli' or reinstall the package."
|
|
24
|
-
);
|
|
25
|
-
} else {
|
|
26
|
-
console.error(`ludus-cli: failed to start: ${err.message}`);
|
|
27
|
-
}
|
|
28
|
-
process.exit(1);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
child.on("exit", (code, signal) => {
|
|
32
|
-
process.exit(signal ? 1 : code || 0);
|
|
33
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { spawn } = require("child_process");
|
|
6
|
+
|
|
7
|
+
const binaryName = process.platform === "win32" ? "ludus.exe" : "ludus";
|
|
8
|
+
const binaryPath = path.join(__dirname, "bin", binaryName);
|
|
9
|
+
|
|
10
|
+
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
11
|
+
stdio: "inherit",
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// Forward signals so the Go binary shuts down cleanly
|
|
15
|
+
["SIGINT", "SIGTERM", "SIGHUP"].forEach((sig) => {
|
|
16
|
+
process.on(sig, () => child.kill(sig));
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
child.on("error", (err) => {
|
|
20
|
+
if (err.code === "ENOENT") {
|
|
21
|
+
console.error(
|
|
22
|
+
`ludus-cli: binary not found at ${binaryPath}\n` +
|
|
23
|
+
"Run 'npm rebuild ludus-cli' or reinstall the package."
|
|
24
|
+
);
|
|
25
|
+
} else {
|
|
26
|
+
console.error(`ludus-cli: failed to start: ${err.message}`);
|
|
27
|
+
}
|
|
28
|
+
process.exit(1);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
child.on("exit", (code, signal) => {
|
|
32
|
+
process.exit(signal ? 1 : code || 0);
|
|
33
|
+
});
|