curat 0.0.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/bin/cli.js +130 -0
- package/bin/platform.js +64 -0
- package/bin/postinstall.js +73 -0
- package/dist/macos-arm64/curat.zip +0 -0
- package/package.json +30 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { execSync, spawn } = require("child_process");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const https = require("https");
|
|
8
|
+
const http = require("http");
|
|
9
|
+
const { getPlatformDir, getBinaryName, R2_PUBLIC_URL } = require("./platform");
|
|
10
|
+
|
|
11
|
+
const platformDir = getPlatformDir();
|
|
12
|
+
const extractDir = path.join(__dirname, "..", "dist", platformDir);
|
|
13
|
+
|
|
14
|
+
fs.mkdirSync(extractDir, { recursive: true });
|
|
15
|
+
|
|
16
|
+
function download(url) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
const client = url.startsWith("https") ? https : http;
|
|
19
|
+
client
|
|
20
|
+
.get(url, (res) => {
|
|
21
|
+
if (
|
|
22
|
+
res.statusCode >= 300 &&
|
|
23
|
+
res.statusCode < 400 &&
|
|
24
|
+
res.headers.location
|
|
25
|
+
) {
|
|
26
|
+
return download(res.headers.location).then(resolve, reject);
|
|
27
|
+
}
|
|
28
|
+
if (res.statusCode !== 200) {
|
|
29
|
+
reject(new Error(`HTTP ${res.statusCode} for ${url}`));
|
|
30
|
+
res.resume();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
resolve(res);
|
|
34
|
+
})
|
|
35
|
+
.on("error", reject);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function ensureBinaryZip(baseName) {
|
|
40
|
+
const zipPath = path.join(extractDir, `${baseName}.zip`);
|
|
41
|
+
if (fs.existsSync(zipPath)) return;
|
|
42
|
+
|
|
43
|
+
const version = require("../package.json").version;
|
|
44
|
+
const url = `${R2_PUBLIC_URL}/releases/v${version}/${platformDir}/${baseName}.zip`;
|
|
45
|
+
console.log("Binary not found locally. Downloading from R2...");
|
|
46
|
+
|
|
47
|
+
const res = await download(url);
|
|
48
|
+
const file = fs.createWriteStream(zipPath);
|
|
49
|
+
await new Promise((resolve, reject) => {
|
|
50
|
+
res.pipe(file);
|
|
51
|
+
file.on("finish", () => file.close(resolve));
|
|
52
|
+
file.on("error", (err) => {
|
|
53
|
+
fs.unlink(zipPath, () => {});
|
|
54
|
+
reject(err);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
console.log("Download complete.");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function extractAndRun(baseName, launch) {
|
|
61
|
+
const binName = getBinaryName(baseName);
|
|
62
|
+
const binPath = path.join(extractDir, binName);
|
|
63
|
+
const zipPath = path.join(extractDir, `${baseName}.zip`);
|
|
64
|
+
|
|
65
|
+
if (fs.existsSync(binPath)) {
|
|
66
|
+
try {
|
|
67
|
+
fs.unlinkSync(binPath);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
if (process.env.CURAT_DEBUG) {
|
|
70
|
+
console.warn(
|
|
71
|
+
`Warning: Could not delete existing binary: ${err.message}`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!fs.existsSync(zipPath)) {
|
|
78
|
+
console.error(`${baseName}.zip not found at: ${zipPath}`);
|
|
79
|
+
console.error(`Current platform: ${platformDir}`);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const unzipCmd =
|
|
84
|
+
process.platform === "win32"
|
|
85
|
+
? `powershell -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${extractDir}' -Force"`
|
|
86
|
+
: `unzip -qq -o "${zipPath}" -d "${extractDir}"`;
|
|
87
|
+
try {
|
|
88
|
+
execSync(unzipCmd, { stdio: "inherit" });
|
|
89
|
+
} catch (err) {
|
|
90
|
+
console.error("Extraction failed:", err.message);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!fs.existsSync(binPath)) {
|
|
95
|
+
console.error(`Extracted binary not found at: ${binPath}`);
|
|
96
|
+
console.error(
|
|
97
|
+
"This usually indicates a corrupt download. Please reinstall.",
|
|
98
|
+
);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (process.platform !== "win32") {
|
|
103
|
+
try {
|
|
104
|
+
fs.chmodSync(binPath, 0o755);
|
|
105
|
+
} catch {}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return launch(binPath);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const args = process.argv.slice(2);
|
|
112
|
+
|
|
113
|
+
ensureBinaryZip("curat")
|
|
114
|
+
.then(() => {
|
|
115
|
+
extractAndRun("curat", (bin) => {
|
|
116
|
+
const proc = spawn(bin, args, { stdio: "inherit" });
|
|
117
|
+
proc.on("exit", (c) => process.exit(c || 0));
|
|
118
|
+
proc.on("error", (e) => {
|
|
119
|
+
console.error("CLI error:", e.message);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
});
|
|
122
|
+
process.on("SIGINT", () => proc.kill("SIGINT"));
|
|
123
|
+
process.on("SIGTERM", () => proc.kill("SIGTERM"));
|
|
124
|
+
});
|
|
125
|
+
})
|
|
126
|
+
.catch((err) => {
|
|
127
|
+
console.error(`Failed to obtain curat binary: ${err.message}`);
|
|
128
|
+
console.error("Please check your network connection and try again.");
|
|
129
|
+
process.exit(1);
|
|
130
|
+
});
|
package/bin/platform.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { execSync } = require("child_process");
|
|
5
|
+
|
|
6
|
+
// Public bucket URL for binary distribution (not a secret)
|
|
7
|
+
const R2_PUBLIC_URL = "https://pub-89e7bd64996d4da48e9371727aa0a14c.r2.dev";
|
|
8
|
+
|
|
9
|
+
function getEffectiveArch() {
|
|
10
|
+
const platform = process.platform;
|
|
11
|
+
const nodeArch = process.arch;
|
|
12
|
+
|
|
13
|
+
if (platform === "darwin") {
|
|
14
|
+
if (nodeArch === "arm64") return "arm64";
|
|
15
|
+
try {
|
|
16
|
+
const translated = execSync("sysctl -in sysctl.proc_translated", {
|
|
17
|
+
encoding: "utf8",
|
|
18
|
+
}).trim();
|
|
19
|
+
if (translated === "1") return "arm64";
|
|
20
|
+
} catch {
|
|
21
|
+
// sysctl key not present -> assume true Intel
|
|
22
|
+
}
|
|
23
|
+
return "x64";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (/arm/i.test(nodeArch)) return "arm64";
|
|
27
|
+
|
|
28
|
+
if (platform === "win32") {
|
|
29
|
+
const pa = process.env.PROCESSOR_ARCHITECTURE || "";
|
|
30
|
+
const paw = process.env.PROCESSOR_ARCHITEW6432 || "";
|
|
31
|
+
if (/arm/i.test(pa) || /arm/i.test(paw)) return "arm64";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return "x64";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const PLATFORM_MAP = {
|
|
38
|
+
"linux-x64": "linux-x64",
|
|
39
|
+
"linux-arm64": "linux-arm64",
|
|
40
|
+
"win32-x64": "windows-x64",
|
|
41
|
+
"win32-arm64": "windows-arm64",
|
|
42
|
+
"darwin-x64": "macos-x64",
|
|
43
|
+
"darwin-arm64": "macos-arm64",
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function getPlatformDir() {
|
|
47
|
+
const key = `${process.platform}-${getEffectiveArch()}`;
|
|
48
|
+
const dir = PLATFORM_MAP[key];
|
|
49
|
+
if (!dir) {
|
|
50
|
+
console.error(`Unsupported platform: ${key}`);
|
|
51
|
+
console.error("Supported platforms:");
|
|
52
|
+
console.error(" - Linux x64 / ARM64");
|
|
53
|
+
console.error(" - Windows x64 / ARM64");
|
|
54
|
+
console.error(" - macOS x64 (Intel) / ARM64 (Apple Silicon)");
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
return dir;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getBinaryName(base) {
|
|
61
|
+
return process.platform === "win32" ? `${base}.exe` : base;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = { getEffectiveArch, getPlatformDir, getBinaryName, R2_PUBLIC_URL };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const https = require("https");
|
|
5
|
+
const http = require("http");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const { getPlatformDir, R2_PUBLIC_URL } = require("./platform");
|
|
9
|
+
|
|
10
|
+
const version = require("../package.json").version;
|
|
11
|
+
const platformDir = getPlatformDir();
|
|
12
|
+
const distDir = path.join(__dirname, "..", "dist", platformDir);
|
|
13
|
+
const zipPath = path.join(distDir, "curat.zip");
|
|
14
|
+
|
|
15
|
+
function download(url) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const client = url.startsWith("https") ? https : http;
|
|
18
|
+
client
|
|
19
|
+
.get(url, (res) => {
|
|
20
|
+
if (
|
|
21
|
+
res.statusCode >= 300 &&
|
|
22
|
+
res.statusCode < 400 &&
|
|
23
|
+
res.headers.location
|
|
24
|
+
) {
|
|
25
|
+
return download(res.headers.location).then(resolve, reject);
|
|
26
|
+
}
|
|
27
|
+
if (res.statusCode !== 200) {
|
|
28
|
+
reject(new Error(`HTTP ${res.statusCode} for ${url}`));
|
|
29
|
+
res.resume();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
resolve(res);
|
|
33
|
+
})
|
|
34
|
+
.on("error", reject);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function main() {
|
|
39
|
+
if (fs.existsSync(zipPath)) {
|
|
40
|
+
console.log(
|
|
41
|
+
`curat: binary already present at ${zipPath}, skipping download`,
|
|
42
|
+
);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const url = `${R2_PUBLIC_URL}/releases/v${version}/${platformDir}/curat.zip`;
|
|
47
|
+
console.log(`curat: downloading binary for ${platformDir}...`);
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
fs.mkdirSync(distDir, { recursive: true });
|
|
51
|
+
const res = await download(url);
|
|
52
|
+
const file = fs.createWriteStream(zipPath);
|
|
53
|
+
|
|
54
|
+
await new Promise((resolve, reject) => {
|
|
55
|
+
res.pipe(file);
|
|
56
|
+
file.on("finish", () => file.close(resolve));
|
|
57
|
+
file.on("error", (err) => {
|
|
58
|
+
fs.unlink(zipPath, () => {});
|
|
59
|
+
reject(err);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
console.log("curat: binary downloaded successfully");
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.warn(`curat: postinstall download failed: ${err.message}`);
|
|
66
|
+
console.warn("curat: binary will be downloaded on first run");
|
|
67
|
+
try {
|
|
68
|
+
fs.unlinkSync(zipPath);
|
|
69
|
+
} catch {}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
main();
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "curat",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.0.2",
|
|
5
|
+
"description": "Curate codebases into AI-friendly context files",
|
|
6
|
+
"bin": {
|
|
7
|
+
"curat": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"ai",
|
|
11
|
+
"llm",
|
|
12
|
+
"context",
|
|
13
|
+
"codebase",
|
|
14
|
+
"curate",
|
|
15
|
+
"cli"
|
|
16
|
+
],
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"scripts": {
|
|
20
|
+
"postinstall": "node bin/postinstall.js"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {},
|
|
23
|
+
"files": [
|
|
24
|
+
"bin",
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18"
|
|
29
|
+
}
|
|
30
|
+
}
|