codeagent-sync 0.1.0
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/codeagent-sync.js +144 -0
- package/package.json +48 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Installs the codeagent-sync program for this package's version at
|
|
3
|
+
// ~/.local/bin (the place the hooks of automatic sync start it from), then
|
|
4
|
+
// runs it with the given arguments. Runs as the postinstall script too; when
|
|
5
|
+
// a package manager skips that (pnpm does by default), the first run installs.
|
|
6
|
+
|
|
7
|
+
"use strict";
|
|
8
|
+
|
|
9
|
+
const crypto = require("crypto");
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
const http = require("http");
|
|
12
|
+
const https = require("https");
|
|
13
|
+
const os = require("os");
|
|
14
|
+
const path = require("path");
|
|
15
|
+
const { spawnSync } = require("child_process");
|
|
16
|
+
|
|
17
|
+
const REPO = "sametbrr/codeagent-sync";
|
|
18
|
+
const VERSION = require("./package.json").version;
|
|
19
|
+
const WINDOWS = process.platform === "win32";
|
|
20
|
+
|
|
21
|
+
function installDir() {
|
|
22
|
+
return process.env.CODEAGENT_SYNC_INSTALL_DIR || path.join(os.homedir(), ".local", "bin");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function programPath() {
|
|
26
|
+
return path.join(installDir(), WINDOWS ? "codeagent-sync.exe" : "codeagent-sync");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function assetName() {
|
|
30
|
+
const goos = { darwin: "darwin", linux: "linux", win32: "windows" }[process.platform];
|
|
31
|
+
const goarch = { x64: "amd64", arm64: "arm64" }[process.arch];
|
|
32
|
+
if (!goos || !goarch) {
|
|
33
|
+
throw new Error(`no codeagent-sync build for ${process.platform}/${process.arch}`);
|
|
34
|
+
}
|
|
35
|
+
return `codeagent-sync-${goos}-${goarch}${WINDOWS ? ".exe" : ""}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// installedVersion returns the version the program reports, "" when it is
|
|
39
|
+
// missing, or null when it cannot tell (a development build).
|
|
40
|
+
function installedVersion(program) {
|
|
41
|
+
if (!fs.existsSync(program)) return "";
|
|
42
|
+
const out = spawnSync(program, ["--version"], { encoding: "utf8" });
|
|
43
|
+
const m = /version v?(\d+\.\d+\.\d+)/.exec(out.stdout || "");
|
|
44
|
+
return m ? m[1] : null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function older(a, b) {
|
|
48
|
+
const pa = a.split(".").map(Number);
|
|
49
|
+
const pb = b.split(".").map(Number);
|
|
50
|
+
for (let i = 0; i < 3; i++) {
|
|
51
|
+
if (pa[i] !== pb[i]) return pa[i] < pb[i];
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function get(url, redirects = 5) {
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
(url.startsWith("http:") ? http : https)
|
|
59
|
+
.get(url, { headers: { "User-Agent": "codeagent-sync-npm" } }, (res) => {
|
|
60
|
+
if ([301, 302, 303, 307, 308].includes(res.statusCode) && res.headers.location && redirects > 0) {
|
|
61
|
+
res.resume();
|
|
62
|
+
resolve(get(res.headers.location, redirects - 1));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (res.statusCode !== 200) {
|
|
66
|
+
res.resume();
|
|
67
|
+
reject(new Error(`${url}: HTTP ${res.statusCode}`));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const chunks = [];
|
|
71
|
+
res.on("data", (c) => chunks.push(c));
|
|
72
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
73
|
+
})
|
|
74
|
+
.on("error", reject);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function install(program) {
|
|
79
|
+
const asset = assetName();
|
|
80
|
+
// CODEAGENT_SYNC_RELEASE_URL points at another copy of the release (tests, mirrors).
|
|
81
|
+
const base = process.env.CODEAGENT_SYNC_RELEASE_URL || `https://github.com/${REPO}/releases/download/v${VERSION}/`;
|
|
82
|
+
process.stderr.write(`Installing codeagent-sync v${VERSION} at ${program}\n`);
|
|
83
|
+
const [binary, sums] = await Promise.all([get(base + asset), get(base + "checksums.txt")]);
|
|
84
|
+
|
|
85
|
+
const want = sums
|
|
86
|
+
.toString("utf8")
|
|
87
|
+
.split("\n")
|
|
88
|
+
.map((l) => l.trim().split(/\s+/))
|
|
89
|
+
.find((f) => f.length === 2 && f[1].replace(/^\*/, "") === asset);
|
|
90
|
+
const got = crypto.createHash("sha256").update(binary).digest("hex");
|
|
91
|
+
if (!want || want[0].toLowerCase() !== got) {
|
|
92
|
+
throw new Error(`checksum mismatch for ${asset}; not installing it`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
fs.mkdirSync(path.dirname(program), { recursive: true });
|
|
96
|
+
const tmp = `${program}.new-${process.pid}`;
|
|
97
|
+
fs.writeFileSync(tmp, binary, { mode: 0o755 });
|
|
98
|
+
if (WINDOWS && fs.existsSync(program)) {
|
|
99
|
+
// A running .exe cannot be replaced, but it can be renamed.
|
|
100
|
+
const old = `${program}.old`;
|
|
101
|
+
try { fs.unlinkSync(old); } catch (_) {}
|
|
102
|
+
fs.renameSync(program, old);
|
|
103
|
+
}
|
|
104
|
+
fs.renameSync(tmp, program);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function ensure() {
|
|
108
|
+
const program = programPath();
|
|
109
|
+
const have = installedVersion(program);
|
|
110
|
+
// Keep a newer program (installed by `codeagent-sync update`) and a
|
|
111
|
+
// development build.
|
|
112
|
+
if (have === "" || (have !== null && older(have, VERSION))) {
|
|
113
|
+
await install(program);
|
|
114
|
+
}
|
|
115
|
+
return program;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function main() {
|
|
119
|
+
const args = process.argv.slice(2);
|
|
120
|
+
if (args[0] === "--codeagent-sync-install") {
|
|
121
|
+
try {
|
|
122
|
+
await ensure();
|
|
123
|
+
} catch (err) {
|
|
124
|
+
// Never fail the package install: the first run tries again.
|
|
125
|
+
process.stderr.write(`codeagent-sync: ${err.message}; it is installed on first use instead\n`);
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
let program;
|
|
130
|
+
try {
|
|
131
|
+
program = await ensure();
|
|
132
|
+
} catch (err) {
|
|
133
|
+
process.stderr.write(`codeagent-sync: ${err.message}\nDownload it from https://github.com/${REPO}/releases\n`);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
const res = spawnSync(program, args, { stdio: "inherit" });
|
|
137
|
+
if (res.error) {
|
|
138
|
+
process.stderr.write(`codeagent-sync: ${res.error.message}\n`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
process.exit(res.status === null ? 1 : res.status);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "codeagent-sync",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Keep Claude Code and Codex configuration (skills, settings, MCP servers, hooks) the same on every machine, end-to-end encrypted",
|
|
5
|
+
"bin": {
|
|
6
|
+
"codeagent-sync": "codeagent-sync.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"postinstall": "node codeagent-sync.js --codeagent-sync-install"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"codeagent-sync.js"
|
|
13
|
+
],
|
|
14
|
+
"keywords": [
|
|
15
|
+
"claude",
|
|
16
|
+
"claude-code",
|
|
17
|
+
"codex",
|
|
18
|
+
"sync",
|
|
19
|
+
"skills",
|
|
20
|
+
"mcp",
|
|
21
|
+
"cli",
|
|
22
|
+
"r2",
|
|
23
|
+
"s3",
|
|
24
|
+
"age"
|
|
25
|
+
],
|
|
26
|
+
"author": "Samet Birer",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/sametbrr/codeagent-sync.git"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/sametbrr/codeagent-sync#readme",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/sametbrr/codeagent-sync/issues"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18"
|
|
38
|
+
},
|
|
39
|
+
"os": [
|
|
40
|
+
"darwin",
|
|
41
|
+
"linux",
|
|
42
|
+
"win32"
|
|
43
|
+
],
|
|
44
|
+
"cpu": [
|
|
45
|
+
"x64",
|
|
46
|
+
"arm64"
|
|
47
|
+
]
|
|
48
|
+
}
|