@remem-ai/remem 0.5.97
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 +27 -0
- package/bin/remem.js +34 -0
- package/package.json +48 -0
- package/scripts/install.js +171 -0
- package/scripts/platform.js +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# @remem-ai/remem
|
|
2
|
+
|
|
3
|
+
npm wrapper for the `remem` native binary.
|
|
4
|
+
|
|
5
|
+
`remem` provides persistent project memory for Claude Code and OpenAI Codex
|
|
6
|
+
coding-agent sessions. The npm package downloads the matching prebuilt binary
|
|
7
|
+
from the GitHub Release during installation.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @remem-ai/remem
|
|
13
|
+
remem install --target codex
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Supported platforms:
|
|
17
|
+
|
|
18
|
+
- macOS arm64 / x64
|
|
19
|
+
- Linux arm64 / x64
|
|
20
|
+
|
|
21
|
+
Environment variables:
|
|
22
|
+
|
|
23
|
+
- `REMEM_NPM_SKIP_DOWNLOAD=1`: skip binary download during npm install
|
|
24
|
+
- `REMEM_NPM_BINARY=/path/to/remem`: run an explicit existing binary
|
|
25
|
+
|
|
26
|
+
See the main project README for Claude Code and OpenAI Codex setup details:
|
|
27
|
+
https://github.com/majiayu000/remem
|
package/bin/remem.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const { spawnSync } = require("node:child_process");
|
|
6
|
+
const fs = require("node:fs");
|
|
7
|
+
const path = require("node:path");
|
|
8
|
+
|
|
9
|
+
const { platformKey } = require("../scripts/platform");
|
|
10
|
+
|
|
11
|
+
function binaryPath() {
|
|
12
|
+
if (process.env.REMEM_NPM_BINARY) {
|
|
13
|
+
return process.env.REMEM_NPM_BINARY;
|
|
14
|
+
}
|
|
15
|
+
return path.join(__dirname, "..", "vendor", platformKey(), "remem");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const bin = binaryPath();
|
|
19
|
+
if (!fs.existsSync(bin)) {
|
|
20
|
+
console.error(`remem binary not found at ${bin}`);
|
|
21
|
+
console.error("Run `npm rebuild @remem-ai/remem` or set REMEM_NPM_BINARY.");
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const result = spawnSync(bin, process.argv.slice(2), {
|
|
26
|
+
stdio: "inherit",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (result.error) {
|
|
30
|
+
console.error(result.error.message);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
process.exit(result.status === null ? 1 : result.status);
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@remem-ai/remem",
|
|
3
|
+
"version": "0.5.97",
|
|
4
|
+
"description": "Persistent memory for Claude Code and OpenAI Codex coding agents",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://majiayu000.github.io/remem/",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/majiayu000/remem.git",
|
|
10
|
+
"directory": "npm/remem"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"remem": "bin/remem.js"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"postinstall": "node scripts/install.js",
|
|
17
|
+
"test": "node bin/remem.js --version"
|
|
18
|
+
},
|
|
19
|
+
"os": [
|
|
20
|
+
"darwin",
|
|
21
|
+
"linux"
|
|
22
|
+
],
|
|
23
|
+
"cpu": [
|
|
24
|
+
"x64",
|
|
25
|
+
"arm64"
|
|
26
|
+
],
|
|
27
|
+
"files": [
|
|
28
|
+
"bin/",
|
|
29
|
+
"scripts/install.js",
|
|
30
|
+
"scripts/platform.js",
|
|
31
|
+
"README.md"
|
|
32
|
+
],
|
|
33
|
+
"keywords": [
|
|
34
|
+
"claude-code",
|
|
35
|
+
"openai-codex",
|
|
36
|
+
"codex",
|
|
37
|
+
"memory",
|
|
38
|
+
"agent-memory",
|
|
39
|
+
"coding-agents",
|
|
40
|
+
"mcp",
|
|
41
|
+
"mcp-server",
|
|
42
|
+
"rust-cli",
|
|
43
|
+
"ai"
|
|
44
|
+
],
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const { spawnSync } = require("node:child_process");
|
|
6
|
+
const crypto = require("node:crypto");
|
|
7
|
+
const fs = require("node:fs");
|
|
8
|
+
const https = require("node:https");
|
|
9
|
+
const os = require("node:os");
|
|
10
|
+
const path = require("node:path");
|
|
11
|
+
|
|
12
|
+
const { platformKey } = require("./platform");
|
|
13
|
+
|
|
14
|
+
const VERSION = require("../package.json").version;
|
|
15
|
+
const BASE_URL = `https://github.com/majiayu000/remem/releases/download/v${VERSION}`;
|
|
16
|
+
|
|
17
|
+
function download(url, dest, redirects = 0) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const request = https.get(url, (response) => {
|
|
20
|
+
if (
|
|
21
|
+
response.statusCode >= 300 &&
|
|
22
|
+
response.statusCode < 400 &&
|
|
23
|
+
response.headers.location
|
|
24
|
+
) {
|
|
25
|
+
response.resume();
|
|
26
|
+
if (redirects >= 5) {
|
|
27
|
+
reject(new Error(`Too many redirects while downloading ${url}`));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const next = new URL(response.headers.location, url).toString();
|
|
31
|
+
resolve(download(next, dest, redirects + 1));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (response.statusCode !== 200) {
|
|
36
|
+
response.resume();
|
|
37
|
+
reject(new Error(`Download failed with HTTP ${response.statusCode}: ${url}`));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const output = fs.createWriteStream(dest, { mode: 0o600 });
|
|
42
|
+
response.pipe(output);
|
|
43
|
+
output.on("finish", () => output.close(resolve));
|
|
44
|
+
output.on("error", reject);
|
|
45
|
+
});
|
|
46
|
+
request.on("error", reject);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function fetchJson(url, redirects = 0) {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
const request = https.get(url, (response) => {
|
|
53
|
+
if (
|
|
54
|
+
response.statusCode >= 300 &&
|
|
55
|
+
response.statusCode < 400 &&
|
|
56
|
+
response.headers.location
|
|
57
|
+
) {
|
|
58
|
+
response.resume();
|
|
59
|
+
if (redirects >= 5) {
|
|
60
|
+
reject(new Error(`Too many redirects while fetching ${url}`));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const next = new URL(response.headers.location, url).toString();
|
|
64
|
+
resolve(fetchJson(next, redirects + 1));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (response.statusCode !== 200) {
|
|
69
|
+
response.resume();
|
|
70
|
+
reject(new Error(`Manifest fetch failed with HTTP ${response.statusCode}: ${url}`));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let body = "";
|
|
75
|
+
response.setEncoding("utf8");
|
|
76
|
+
response.on("data", (chunk) => {
|
|
77
|
+
body += chunk;
|
|
78
|
+
});
|
|
79
|
+
response.on("end", () => {
|
|
80
|
+
try {
|
|
81
|
+
resolve(JSON.parse(body));
|
|
82
|
+
} catch (error) {
|
|
83
|
+
reject(new Error(`Invalid release manifest JSON from ${url}: ${error.message}`));
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
request.on("error", reject);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function sha256(file) {
|
|
92
|
+
const hash = crypto.createHash("sha256");
|
|
93
|
+
hash.update(fs.readFileSync(file));
|
|
94
|
+
return hash.digest("hex");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function resolveAsset(manifest, version, key) {
|
|
98
|
+
const release = manifest?.versions?.[version];
|
|
99
|
+
const asset = release?.assets?.[key];
|
|
100
|
+
if (!asset || typeof asset.file !== "string") {
|
|
101
|
+
throw new Error(`Release manifest for remem ${version} is missing asset ${key}`);
|
|
102
|
+
}
|
|
103
|
+
if (!/^[0-9a-f]{64}$/i.test(asset.sha256 || "")) {
|
|
104
|
+
throw new Error(`Release manifest asset ${key} is missing a valid sha256`);
|
|
105
|
+
}
|
|
106
|
+
const baseUrl = typeof release.base_url === "string" && release.base_url
|
|
107
|
+
? release.base_url.replace(/\/$/, "")
|
|
108
|
+
: BASE_URL;
|
|
109
|
+
return {
|
|
110
|
+
file: asset.file,
|
|
111
|
+
sha256: asset.sha256.toLowerCase(),
|
|
112
|
+
url: `${baseUrl}/${asset.file}`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function main() {
|
|
117
|
+
if (process.env.REMEM_NPM_SKIP_DOWNLOAD === "1") {
|
|
118
|
+
console.log("Skipping remem binary download because REMEM_NPM_SKIP_DOWNLOAD=1");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const key = platformKey();
|
|
123
|
+
const vendorDir = path.join(__dirname, "..", "vendor", key);
|
|
124
|
+
const binary = path.join(vendorDir, "remem");
|
|
125
|
+
if (process.argv.includes("--skip-existing") && fs.existsSync(binary)) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
fs.rmSync(vendorDir, { recursive: true, force: true });
|
|
130
|
+
fs.mkdirSync(vendorDir, { recursive: true, mode: 0o755 });
|
|
131
|
+
|
|
132
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "remem-npm-"));
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
const manifestUrl = `${BASE_URL}/remem-releases.json`;
|
|
136
|
+
const manifest = await fetchJson(manifestUrl);
|
|
137
|
+
const asset = resolveAsset(manifest, VERSION, key);
|
|
138
|
+
const archive = path.join(tmpDir, asset.file);
|
|
139
|
+
|
|
140
|
+
console.log(`Downloading remem ${VERSION} for ${key}`);
|
|
141
|
+
await download(asset.url, archive);
|
|
142
|
+
const actual = sha256(archive);
|
|
143
|
+
if (actual !== asset.sha256) {
|
|
144
|
+
throw new Error(`Checksum mismatch for ${asset.file}: expected ${asset.sha256}, got ${actual}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const tar = spawnSync("tar", ["-xzf", archive, "-C", vendorDir], {
|
|
148
|
+
stdio: "inherit",
|
|
149
|
+
});
|
|
150
|
+
if (tar.error) throw tar.error;
|
|
151
|
+
if (tar.status !== 0) throw new Error(`tar exited with status ${tar.status}`);
|
|
152
|
+
|
|
153
|
+
fs.chmodSync(binary, 0o755);
|
|
154
|
+
console.log(`Installed remem binary to ${binary}`);
|
|
155
|
+
} finally {
|
|
156
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (require.main === module) {
|
|
161
|
+
main().catch((error) => {
|
|
162
|
+
console.error(error.message);
|
|
163
|
+
process.exit(1);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
module.exports = {
|
|
168
|
+
BASE_URL,
|
|
169
|
+
VERSION,
|
|
170
|
+
resolveAsset,
|
|
171
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const os = require("node:os");
|
|
4
|
+
|
|
5
|
+
function platformKey() {
|
|
6
|
+
const platform = os.platform();
|
|
7
|
+
const arch = os.arch();
|
|
8
|
+
if (platform === "darwin" && arch === "arm64") return "darwin-arm64";
|
|
9
|
+
if (platform === "darwin" && arch === "x64") return "darwin-x64";
|
|
10
|
+
if (platform === "linux" && arch === "arm64") return "linux-arm64";
|
|
11
|
+
if (platform === "linux" && arch === "x64") return "linux-x64";
|
|
12
|
+
throw new Error(`Unsupported platform: ${platform}/${arch}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = { platformKey };
|