@everme/cli 0.1.0-beta.3 → 0.1.1

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.3",
3
+ "version": "0.1.1",
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": {
@@ -38,14 +38,14 @@
38
38
  "ai",
39
39
  "agent"
40
40
  ],
41
- "homepage": "https://everme.ai",
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",
@@ -6,8 +6,8 @@
6
6
  * postinstall hook for @everme/cli — downloads, verifies, and extracts the
7
7
  * platform-native evercli binary into <pkg>/bin.
8
8
  *
9
- * Conventions match this monorepo's existing goreleaser config and shell
10
- * installer (cli/.goreleaser.yml, cli/scripts/install.sh):
9
+ * Conventions match this monorepo's existing goreleaser config
10
+ * (cli/.goreleaser.yml):
11
11
  *
12
12
  * tag cli/vX.Y.Z
13
13
  * archive evercli_<os>_<arch>.tar.gz (Unix)
@@ -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
- ab0513a3c000d3dfa06f16ebd3289820ef52970d1262c585ffcc8d5b4e777fe0 evercli_darwin_amd64.tar.gz
2
- f41616c6d0c6fb8c2ab5271bb4cb3f8e757db82d8802f45e446d4bd64a653c32 evercli_darwin_arm64.tar.gz
3
- 7b3f8f2c48e197a21941f34e39d61c56bb394471c3ce50bc6718bd79361a22d9 evercli_linux_amd64.tar.gz
4
- 964b85a6cf4708eaa625178c9f52dcc04a17f53b6a7e0176a67b12762e293f88 evercli_linux_arm64.tar.gz
5
- 6608dd0a297868d91a5c91d1941bddeb39a11afa3f9d747a45495dce85527ab0 evercli_windows_amd64.zip
6
- de33a17dbba12f03a4bd729ab4976635b97d3c3632e56796e83515ebe54c229c evercli_windows_arm64.zip
1
+ ebe4459a6efeaeb7fab2d210208ff64b3d787338fad66690add284eff0442047 evercli_darwin_amd64.tar.gz
2
+ 285c4d77d69b4850a1b84156b4196a0800af8ddb16428558414c6ceb9f91dda6 evercli_darwin_arm64.tar.gz
3
+ 8ac26b56ed87875764c7cf84567e8123596565d33459a2278f95e00070db8251 evercli_linux_amd64.tar.gz
4
+ 8f2d75be861849f1c5bb8bbfb8437b3106a4ed48c2f40d268b4b21d5ea277cb7 evercli_linux_arm64.tar.gz
5
+ 6477aa4da68d15d8a2bba1bcdb6e39f3122215319cba4e44c60ac91e644fcbe2 evercli_windows_amd64.zip
6
+ dc17458b21b1e26958d9946506deeae40d8f4b4885beea926a95fd9487a26b58 evercli_windows_arm64.zip