@everme/cli 0.1.0-beta.4 → 0.2.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/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # @everme/cli
2
2
 
3
- npm-distributed installer for [EverMe CLI](https://github.com/alwaysday1/everme). Detects your platform on install, downloads the matching pre-built binary from GitHub Releases, verifies its SHA-256 checksum, and exposes it as the `evercli` command.
3
+ npm-distributed installer for [EverMe CLI](https://github.com/EverMind-AI/EverMe-CLI). Detects your platform on install, downloads the matching pre-built binary from GitHub Releases, verifies its SHA-256 checksum, and exposes it as the `evercli` command.
4
4
 
5
5
  ```bash
6
- npm install -g @everme/cli@beta
6
+ npm install -g @everme/cli
7
7
  evercli --help
8
8
 
9
9
  # or, no install
10
- npx @everme/cli@beta --version
10
+ npx @everme/cli --version
11
11
  ```
12
12
 
13
13
  ## What this package does
@@ -15,7 +15,7 @@ npx @everme/cli@beta --version
15
15
  `@everme/cli` is a thin Node-side installer + runner; the actual CLI is a pre-compiled binary downloaded on first install. On `npm install`:
16
16
 
17
17
  1. Detect platform / arch (darwin/linux/windows × amd64/arm64)
18
- 2. Download the matching archive from `https://github.com/alwaysday1/everme/releases/download/v<version>/evercli_<os>_<arch>.{tar.gz|zip}`
18
+ 2. Download the matching archive from `https://github.com/EverMind-AI/EverMe-CLI/releases/download/v<version>/evercli_<os>_<arch>.{tar.gz|zip}`
19
19
  3. Verify SHA-256 against the `sha256sums.txt` shipped inside this npm package
20
20
  4. Extract and place the binary under the package's `bin/` directory
21
21
  5. The `evercli` shim (`scripts/run.js`) execs that binary with your args
@@ -26,7 +26,7 @@ If `postinstall` was skipped (some `npx` flows, restricted CI), the installer ru
26
26
 
27
27
  The installer tries download sources in this order:
28
28
 
29
- 1. `https://github.com/alwaysday1/everme/releases/...` (canonical)
29
+ 1. `https://github.com/EverMind-AI/EverMe-CLI/releases/...` (canonical)
30
30
  2. The npm registry's binary mirror path (`<registry>/-/binary/everme-cli/...`), if your `npm_config_registry` is non-default
31
31
  3. `https://registry.npmmirror.com/-/binary/everme-cli/...` (always tried as final fallback)
32
32
 
@@ -41,11 +41,6 @@ export EVERCLI_API_BASE_URL=https://memory.acme-internal.com
41
41
  evercli auth login
42
42
  ```
43
43
 
44
- ## Release channels
45
-
46
- - `@everme/cli@beta` — pre-release builds
47
- - `@everme/cli@latest` — stable releases
48
-
49
44
  ## License
50
45
 
51
46
  Apache-2.0.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@everme/cli",
3
- "version": "0.1.0-beta.4",
3
+ "version": "0.2.0",
4
4
  "description": "EverMe CLI — npm wrapper that downloads and runs the platform-native evercli binary on first use.",
5
5
  "license": "Apache-2.0",
6
6
  "bin": {
@@ -41,11 +41,11 @@
41
41
  "homepage": "https://everme.evermind.ai",
42
42
  "repository": {
43
43
  "type": "git",
44
- "url": "git+https://github.com/alwaysday1/everme.git",
44
+ "url": "git+https://github.com/EverMind-AI/EverMe-CLI.git",
45
45
  "directory": "plugins/cli"
46
46
  },
47
47
  "bugs": {
48
- "url": "https://github.com/alwaysday1/everme/issues"
48
+ "url": "https://github.com/EverMind-AI/EverMe-CLI/issues"
49
49
  },
50
50
  "publishConfig": {
51
51
  "access": "public",
@@ -25,12 +25,8 @@ const os = require("os");
25
25
  const crypto = require("crypto");
26
26
  const { execFileSync } = require("child_process");
27
27
 
28
- // Keep prerelease suffix (e.g. "0.1.0-beta.1") intact — GoReleaser tags
29
- // match package.json version verbatim, so stripping after `-` would build
30
- // TAG = cli/v0.1.0 for a 0.1.0-beta.1 publish and 404 against the real
31
- // cli/v0.1.0-beta.1 release.
32
28
  const VERSION = require("../package.json").version;
33
- const REPO = "alwaysday1/everme";
29
+ const REPO = "EverMind-AI/EverMe-CLI";
34
30
  const NAME = "evercli";
35
31
 
36
32
  // Allowlist gates the *initial* request URL. curl --location follows redirects
@@ -182,16 +178,24 @@ function extractZipWindows(archivePath, destDir) {
182
178
 
183
179
  /**
184
180
  * Look up the SHA256 checksum for the archive from sha256sums.txt that ships
185
- * inside this npm package. If the file is missing (e.g. local dev pack), warn
186
- * and skip — the goreleaser CI is expected to copy sha256sums.txt before
187
- * publishing.
181
+ * inside this npm package. sha256sums.txt is listed in package.json::files,
182
+ * so a real `npm install` will always include it. A missing file is either
183
+ * a tampered tarball or a broken publish — in both cases skipping the check
184
+ * defeats the integrity model, so we hard-fail instead of warning.
185
+ *
186
+ * The workspace dev-install path is already short-circuited above
187
+ * (`__dirname.includes("/node_modules/")`), so this branch is only ever
188
+ * reached for installs that MUST verify.
188
189
  */
189
190
  function getExpectedChecksum(archive, checksumsDir) {
190
191
  const dir = checksumsDir || path.join(__dirname, "..");
191
192
  const checksumsPath = path.join(dir, "sha256sums.txt");
192
193
  if (!fs.existsSync(checksumsPath)) {
193
- console.error("[WARN] sha256sums.txt not found, skipping checksum verification");
194
- return null;
194
+ throw new Error(
195
+ `[SECURITY] sha256sums.txt missing at ${checksumsPath}; refusing to install ` +
196
+ `an unverified binary. The file ships with the npm package — its absence ` +
197
+ `indicates tampering or a broken release.`,
198
+ );
195
199
  }
196
200
  const content = fs.readFileSync(checksumsPath, "utf8");
197
201
  for (const line of content.split("\n")) {
@@ -207,8 +211,34 @@ function getExpectedChecksum(archive, checksumsDir) {
207
211
  throw new Error(`Checksum entry not found for ${archive}`);
208
212
  }
209
213
 
214
+ /**
215
+ * Defends against zip-slip: an archive can ship `evercli` as a symlink to
216
+ * `/etc/passwd` (or any path outside tmpDir). copyFileSync follows symlinks,
217
+ * so without this check the extracted "binary" we ship to <pkg>/bin could
218
+ * be sourced from anywhere. lstat catches the symlink case (we reject non-
219
+ * regular files); realpath resolves what the path actually points at and we
220
+ * assert it stays within tmpDir.
221
+ */
222
+ function assertInsideDir(dir, candidate) {
223
+ const stat = fs.lstatSync(candidate);
224
+ if (!stat.isFile()) {
225
+ throw new Error(
226
+ `[SECURITY] Extracted entry is not a regular file (mode=${stat.mode.toString(8)}); ` +
227
+ `refusing to install. Path: ${candidate}`,
228
+ );
229
+ }
230
+ const realDir = fs.realpathSync(dir);
231
+ const realCandidate = fs.realpathSync(candidate);
232
+ const sep = path.sep;
233
+ if (realCandidate !== realDir && !realCandidate.startsWith(realDir + sep)) {
234
+ throw new Error(
235
+ `[SECURITY] Extracted path escapes archive root: ${realCandidate} not under ${realDir}`,
236
+ );
237
+ }
238
+ return realCandidate;
239
+ }
240
+
210
241
  function verifyChecksum(archivePath, expectedHash) {
211
- if (expectedHash === null) return;
212
242
  // Stream to keep RSS constant; archives can be ~10MB+.
213
243
  const hash = crypto.createHash("sha256");
214
244
  const fd = fs.openSync(archivePath, "r");
@@ -260,11 +290,27 @@ function install() {
260
290
  if (isWindows) {
261
291
  extractZipWindows(archivePath, tmpDir);
262
292
  } else {
263
- execFileSync("tar", ["-xzf", archivePath, "-C", tmpDir], { stdio: "ignore" });
293
+ // --no-same-owner / --no-same-permissions matter when postinstall
294
+ // runs as root (e.g. system-wide npm install) — without them, a
295
+ // hostile archive could set setuid/world-writable modes on the
296
+ // extracted binary. The flags are accepted by both GNU tar (Linux)
297
+ // and BSD tar (macOS). Modern tar already rejects ".." paths by
298
+ // default; the post-extraction realpath check below is the second
299
+ // line of defense against zip-slip via symlinks.
300
+ execFileSync(
301
+ "tar",
302
+ [
303
+ "-xzf", archivePath,
304
+ "-C", tmpDir,
305
+ "--no-same-owner",
306
+ "--no-same-permissions",
307
+ ],
308
+ { stdio: "ignore" },
309
+ );
264
310
  }
265
311
 
266
312
  const binaryName = NAME + (isWindows ? ".exe" : "");
267
- const extractedBinary = path.join(tmpDir, binaryName);
313
+ const extractedBinary = assertInsideDir(tmpDir, path.join(tmpDir, binaryName));
268
314
  fs.copyFileSync(extractedBinary, dest);
269
315
  fs.chmodSync(dest, 0o755);
270
316
  console.log(`${NAME} v${VERSION} installed successfully`);
@@ -331,5 +377,6 @@ module.exports = {
331
377
  getExpectedChecksum,
332
378
  verifyChecksum,
333
379
  assertAllowedHost,
380
+ assertInsideDir,
334
381
  resolveMirrorUrls,
335
382
  };
package/scripts/run.js CHANGED
@@ -22,6 +22,7 @@
22
22
 
23
23
  const { execFileSync } = require("child_process");
24
24
  const fs = require("fs");
25
+ const os = require("os");
25
26
  const path = require("path");
26
27
 
27
28
  const NAME = "evercli";
@@ -83,6 +84,17 @@ if (args[0] === "install") {
83
84
  try {
84
85
  execFileSync(bin, args, { stdio: "inherit" });
85
86
  } catch (e) {
86
- process.exit(e.status || 1);
87
+ // Preserve the child's exit semantics. execFileSync throws an Error
88
+ // whose `.status` is the exit code OR null when the child was killed
89
+ // by a signal (`.signal` is set instead). Mapping signal → 128+N
90
+ // matches POSIX shell convention so callers (CI, parent shells) can
91
+ // distinguish SIGINT (130) from a clean exit 1.
92
+ if (typeof e.status === "number") {
93
+ process.exit(e.status);
94
+ }
95
+ if (e.signal && os.constants.signals && os.constants.signals[e.signal]) {
96
+ process.exit(128 + os.constants.signals[e.signal]);
97
+ }
98
+ process.exit(1);
87
99
  }
88
100
  }
package/sha256sums.txt CHANGED
@@ -1,6 +1,6 @@
1
- a6ab8e5b6b7d1d2dd93eef809b6d2523e864736815af67ccd065bd8b0f60d75f evercli_darwin_amd64.tar.gz
2
- 108bdd23380d51f02968bb89c3a7ca6763f6104b2b439b7ae21fe388c57158f7 evercli_darwin_arm64.tar.gz
3
- fe6e0d27158378406359c3362a04cc34573e12a1613437f0637b84dbe9d928f2 evercli_linux_amd64.tar.gz
4
- 243ad1bf61c5f10ba19f053bbba0d92cd68545f5dd39846aa7618f6e2a2cf2ca evercli_linux_arm64.tar.gz
5
- fc321f0b50e891e97391d2565addbf52f21b4049471629630b57169cffe56421 evercli_windows_amd64.zip
6
- b76bcf4ba7f42669459e1d352f05faa36d08cc86ae5c54069892e61db149fec0 evercli_windows_arm64.zip
1
+ 7bce844f7be7d8ae4515a0584fb602b30cb4a26ec16f4acd8bbcf6a554603626 evercli_darwin_amd64.tar.gz
2
+ 3d5fdf715d8ea348297c257775793d5174206a0ddf0fdf99ef20ce1b51256b8d evercli_darwin_arm64.tar.gz
3
+ d1a09150783a2af6ec0faf06cd219ef6f788dc91ac948add73e7bd41c47d7cdf evercli_linux_amd64.tar.gz
4
+ 3f4ab86ebe5fc5bb81b6ad5caeb503a2b81eca1b89796272e3c8e443cd472f48 evercli_linux_arm64.tar.gz
5
+ 359a83fd8d08050633be64f183afe27531a647458fc55311af3387183e4e6cc8 evercli_windows_amd64.zip
6
+ 0e87459ddc517169111d944e6140a19e4a21bb094dbc2f9b9f1f51a99b80c56e evercli_windows_arm64.zip