create-egregore 0.21.0-runtime-mvp.5 → 0.21.0-runtime-mvp.6

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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // egregore-launcher-version: 0.21.0-runtime-mvp.5
2
+ // egregore-launcher-version: 0.21.0-runtime-mvp.6
3
3
  //
4
4
  // egregore — launch and browse Egregore instances from your terminal.
5
5
  // Installed by create-egregore. Reads ~/.egregore/instances.json.
@@ -35,7 +35,7 @@ const crypto = require("node:crypto");
35
35
  const { spawnSync, spawn } = require("node:child_process");
36
36
  const readline = require("node:readline");
37
37
 
38
- const VERSION = "0.21.0-runtime-mvp.5";
38
+ const VERSION = "0.21.0-runtime-mvp.6";
39
39
  const REGISTRY = path.join(os.homedir(), ".egregore", "instances.json");
40
40
  const DEFAULT_FILE = path.join(os.homedir(), ".egregore", "default");
41
41
  const UPDATE_CACHE_FILE = path.join(os.homedir(), ".egregore", "update-check.json");
@@ -2564,6 +2564,54 @@ async function fetchRuntimeMvpCandidate() {
2564
2564
  }
2565
2565
  }
2566
2566
 
2567
+ // Fetch the published candidate tarball from the npm registry and verify its
2568
+ // integrity before staging. The first-timer path: `p prepare` with nothing
2569
+ // staged must not dead-end into a CLI instruction referencing a tarball the
2570
+ // user does not have — the registry has it, digest-verified.
2571
+ function fetchCandidateTarball(version, cb) {
2572
+ const metaUrl = new URL(`https://registry.npmjs.org/${PUBLIC_NPX_PACKAGE}/${version}`);
2573
+ requestJson({
2574
+ hostname: metaUrl.hostname,
2575
+ path: metaUrl.pathname,
2576
+ method: "GET",
2577
+ protocol: metaUrl.protocol,
2578
+ timeoutMs: 10000,
2579
+ headers: { Accept: "application/json" },
2580
+ }).then((meta) => {
2581
+ const dist = meta && meta.status === 200 && meta.data && meta.data.dist;
2582
+ if (!dist || !dist.tarball) return cb(new Error(`candidate ${version} not found on the registry`));
2583
+ const dest = path.join(os.tmpdir(), `egregore-candidate-${version}.tgz`);
2584
+ const fetchTarball = (url, redirects) => {
2585
+ https.get(url, { headers: { "User-Agent": "egregore-launcher" } }, (res) => {
2586
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location && redirects > 0) {
2587
+ res.resume();
2588
+ return fetchTarball(res.headers.location, redirects - 1);
2589
+ }
2590
+ if (res.statusCode !== 200) {
2591
+ res.resume();
2592
+ return cb(new Error(`tarball download failed (HTTP ${res.statusCode})`));
2593
+ }
2594
+ const hash = crypto.createHash("sha512");
2595
+ const out = fs.createWriteStream(dest);
2596
+ res.on("data", (d) => hash.update(d));
2597
+ res.pipe(out);
2598
+ out.on("finish", () => {
2599
+ out.close(() => {
2600
+ const digest = `sha512-${hash.digest("base64")}`;
2601
+ if (dist.integrity && digest !== dist.integrity) {
2602
+ fs.rmSync(dest, { force: true });
2603
+ return cb(new Error("candidate integrity mismatch — refusing to stage"));
2604
+ }
2605
+ cb(null, dest);
2606
+ });
2607
+ });
2608
+ out.on("error", (err) => cb(err));
2609
+ }).on("error", (err) => cb(err));
2610
+ };
2611
+ fetchTarball(dist.tarball, 3);
2612
+ }).catch((err) => cb(err));
2613
+ }
2614
+
2567
2615
  const UPGRADE_STATUS_LABEL = {
2568
2616
  none: "no update staged",
2569
2617
  pending: "staged, not started",
@@ -3784,8 +3832,36 @@ async function browser(args = {}) {
3784
3832
  && !["none", "cancelled", "rolled-back"].includes(status)
3785
3833
  && state.upgradeData.candidate_version;
3786
3834
  if (!staged) {
3787
- state.upgradeNotice = `${DIM}Stage a candidate package first: egregore settings ${state.upgradeInst.slug} runtime init <version> <candidate.tgz>${RESET}`;
3788
- render(); return;
3835
+ const candidate = state.upgradeCandidate;
3836
+ if (!candidate) {
3837
+ state.upgradeNotice = `${DIM}No published candidate found. Stage one manually: egregore settings ${state.upgradeInst.slug} runtime init --candidate <version> --tarball <candidate.tgz>${RESET}`;
3838
+ render(); return;
3839
+ }
3840
+ if (state.upgradeFetching) { render(); return; }
3841
+ state.upgradeFetching = true;
3842
+ const inst = state.upgradeInst;
3843
+ state.upgradeNotice = `${DIM}Downloading ${candidate} from npm…${RESET}`;
3844
+ render();
3845
+ fetchCandidateTarball(candidate, (err, tgz) => {
3846
+ state.upgradeFetching = false;
3847
+ if (!state.upgradeInst || state.upgradeInst !== inst) return;
3848
+ if (err) {
3849
+ state.upgradeNotice = `${YELLOW}${err.message}${RESET}`;
3850
+ render(); return;
3851
+ }
3852
+ const r = runUpgrade(inst, ["init", "--candidate", candidate, "--tarball", tgz]);
3853
+ if (r === null || r.status !== 0) {
3854
+ const detail = ((r && (r.stderr || r.stdout)) || "staging failed").trim().split("\n")[0];
3855
+ state.upgradeNotice = `${YELLOW}${detail}${RESET}`;
3856
+ render(); return;
3857
+ }
3858
+ refreshUpgrade();
3859
+ state.upgradeData = upgradeStatus(inst) || state.upgradeData;
3860
+ state.upgradeNotice = "";
3861
+ state.upgradeMode = "confirm-prepare";
3862
+ render();
3863
+ });
3864
+ return;
3789
3865
  }
3790
3866
  state.upgradeMode = "confirm-prepare"; render(); return;
3791
3867
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-egregore",
3
- "version": "0.21.0-runtime-mvp.5",
3
+ "version": "0.21.0-runtime-mvp.6",
4
4
  "description": "Set up Egregore for your team in one command — installs the egregore launcher and your choice of Claude Code, Codex, or Pi.",
5
5
  "license": "MIT",
6
6
  "bin": {