@calo-design/cli 0.10.0 → 0.11.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.
@@ -160,15 +160,15 @@ async function cmdSave(args = []) {
160
160
  * user doing anything. Best-effort by contract — a checkpoint hiccup must never turn a
161
161
  * successful deploy into a failed command.
162
162
  */
163
- async function autoCheckpoint({ root, note, publishedUrl }) {
163
+ async function autoCheckpoint({ root, note, publishedUrl, artifact }) {
164
164
  try {
165
- await saveFlow({ root, note, publishedUrl, force: true, quiet: true });
165
+ await saveFlow({ root, note, publishedUrl, artifact, force: true, quiet: true });
166
166
  } catch (e) {
167
167
  elog(`${c.y("!")} deploy succeeded, but the source checkpoint didn't: ${e.message}`);
168
168
  }
169
169
  }
170
170
 
171
- async function saveFlow({ root, slugOverride, note = "", force = false, json = false, quiet = false, publishedUrl }) {
171
+ async function saveFlow({ root, slugOverride, note = "", force = false, json = false, quiet = false, publishedUrl, artifact }) {
172
172
  if (!fs.existsSync(path.join(root, "package.json"))) {
173
173
  throw new Error("no package.json here — run `calo-design save` from a prototype folder");
174
174
  }
@@ -212,6 +212,7 @@ async function saveFlow({ root, slugOverride, note = "", force = false, json = f
212
212
  ...(basedOn != null ? { "x-calo-based-on": String(basedOn) } : {}),
213
213
  ...(isFork && forkedFrom ? { "x-calo-forked-from": forkedFrom } : {}),
214
214
  ...(publishedUrl ? { "x-calo-published-url": publishedUrl } : {}),
215
+ ...(artifact ? { "x-calo-artifact": encodeURIComponent(JSON.stringify(artifact)) } : {}),
215
216
  ...(force ? { "x-calo-force": "1" } : {}),
216
217
  },
217
218
  body: tgz,
@@ -93,6 +93,15 @@ let easEnv = process.env;
93
93
  const easRun = (argv, opts = {}) => { const [b, ...p] = easPrefix(); return run(b, [...p, ...argv], { env: easEnv, ...opts }); };
94
94
  const easCapture = (argv, opts = {}) => { const [b, ...p] = easPrefix(); return capture(b, [...p, ...argv], { env: easEnv, ...opts }); };
95
95
 
96
+ // Pull the EAS update GROUP id (the immutable per-version ref checkpoints record) out
97
+ // of a `capture()` result from `eas update:list --json`. Regex, not JSON.parse: eas
98
+ // output shapes and banner noise vary by version, and this must stay best-effort.
99
+ function extractEasGroup(captured) {
100
+ if (!captured || captured.status !== 0 || typeof captured.out !== "string") return undefined;
101
+ const found = captured.out.match(/"group":\s*"([0-9a-fA-F-]{16,64})"/);
102
+ return found ? found[1] : undefined;
103
+ }
104
+
96
105
  function runtimeDir() {
97
106
  const home = process.env.DESIGNCHEF_HOME || path.join(os.homedir(), ".designchef");
98
107
  return path.join(home, "runtime");
@@ -839,12 +848,27 @@ async function cmdPush(args) {
839
848
  log(c.b("\n✨ Live in the Mirror.") + " Open Calo Mirror and tap Refresh — your prototype is at the top.");
840
849
 
841
850
  // Deploy-stamped versioning: every successful push leaves a source checkpoint, so
842
- // the thing on the phone and its exact source are permanently paired. Best-effort
843
- // never fails the push. (Lazy require: mirror-push is also loaded by art-push.)
851
+ // the thing on the phone and its exact source are permanently paired. The artifact
852
+ // records the EAS update GROUP id — the immutable per-version bundle so a future
853
+ // Mirror shell can load exactly this version (channel head keeps moving). Group id
854
+ // capture is best-effort (a quick update:list after the publish); so is the whole
855
+ // stamp — never fails the push. (Lazy require: mirror-push is also loaded by art-push.)
856
+ let easGroup;
857
+ try {
858
+ // easCapture → { status, out }. Tolerant extraction: eas --json shapes (and
859
+ // banner noise) vary by version, so regex the output for the first uuid-shaped
860
+ // `group` value instead of parsing. Known race, accepted as best-effort: this
861
+ // lists the branch head, so a concurrent push to the same slug in this window
862
+ // could stamp the other push's group — rare, and a stamp is advisory metadata.
863
+ easGroup = extractEasGroup(easCapture(["update:list", "--branch", slug, "--limit", "1", "--json", "--non-interactive"], { cwd: stage }));
864
+ } catch {
865
+ /* no artifact ref — the stamp still records source + deep link */
866
+ }
844
867
  await require("./checkpoints").autoCheckpoint({
845
868
  root: process.cwd(),
846
869
  note: flag(args, "note") || `pushed to the Mirror (${slug})`,
847
870
  publishedUrl: `designchef://open/${slug}`,
871
+ artifact: { kind: "mirror", channel: slug, runtimeVersion: RUNTIME_VERSION, ...(easGroup ? { easGroup } : {}) },
848
872
  });
849
873
 
850
874
  // Share target: the deep link the Mirror scanner (and iOS camera) opens.
@@ -865,4 +889,4 @@ async function cmdPush(args) {
865
889
  }
866
890
  }
867
891
 
868
- module.exports = { cmdPush, _s3Put: s3Put, _upsertRegistry: upsertRegistry, _publicBase: PUBLIC_BASE };
892
+ module.exports = { cmdPush, _s3Put: s3Put, _upsertRegistry: upsertRegistry, _publicBase: PUBLIC_BASE, _extractEasGroup: extractEasGroup };
package/bin/share.js CHANGED
@@ -168,10 +168,17 @@ async function cmdShare(args = []) {
168
168
  log(c.dim(` Re-run \`calo-design share\` to update “${slug}” in place. Share the link above.`));
169
169
  }
170
170
  if (r.version) log(c.dim(` this version: ${r.version}`));
171
- // Deploy-stamped versioning: every successful share leaves a source checkpoint
172
- // (with the live URL in its meta), so the deployed thing and its exact source are
173
- // permanently paired. Best-effort never fails the share.
174
- await autoCheckpoint({ root, note: flag(args, "note") || `shared to the web (${slug})`, publishedUrl: url });
171
+ // Deploy-stamped versioning: every successful share leaves a source checkpoint, so
172
+ // the deployed thing and its exact source are permanently paired. publishedUrl
173
+ // prefers the IMMUTABLE per-deploy URL (Cloudflare Pages keeps every deployment
174
+ // forever) so the version page opens exactly this version, not whatever the alias
175
+ // points at by then; the artifact keeps both. Best-effort — never fails the share.
176
+ await autoCheckpoint({
177
+ root,
178
+ note: flag(args, "note") || `shared to the web (${slug})`,
179
+ publishedUrl: r.version || url,
180
+ artifact: { kind: "web", aliasUrl: url, ...(r.version ? { deployUrl: r.version } : {}) },
181
+ });
175
182
  } finally {
176
183
  try { fs.rmSync(tgz); } catch {}
177
184
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calo-design/cli",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "One-line setup for Calo design tooling: logs in with your Calo email and installs the calo-design skill + design-system packages. No GitHub account needed.",
5
5
  "bin": {
6
6
  "calo-design": "bin/cli.js"