@zalify/cli 0.17.0 → 0.17.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.
Files changed (3) hide show
  1. package/README.md +106 -33
  2. package/dist/cli.js +191 -104
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # @zalify/cli
2
2
 
3
- Zalify CLI command-line interface for Zalify.
3
+ Zalify CLI author a brand, launch its shop, run its ads, keep the theme
4
+ current.
4
5
 
5
6
  ## Install
6
7
 
@@ -10,45 +11,119 @@ npm i -g zalify
10
11
  npm i -g @zalify/cli@latest
11
12
  ```
12
13
 
13
- ## Usage
14
+ Already installed? `zalify self-update`.
14
15
 
15
- ### Authentication
16
+ ## The command model
16
17
 
17
- ```bash
18
- zalify login # opens the browser: pick a workspace, authorize
19
- zalify login --app-url http://localhost:3000 # against a local app.zalify.com
20
- zalify whoami # show the authorized workspace
21
- zalify logout # delete local credentials
22
- ```
18
+ Four nouns, four objects:
23
19
 
24
- `login` runs a loopback flow (like `gh auth login`): the CLI listens on a
25
- random localhost port, the browser opens `app.zalify.com/cli/auth`, and on
26
- approval a **workspace-scoped API key** is handed back to the CLI and stored
27
- in `~/.zalify/config.json` (0600). Keys are ordinary Zalify Public API keys
28
- list and revoke them in **Settings Developer**.
20
+ | Noun | The object | What lives under it |
21
+ | --- | --- | --- |
22
+ | `brand` | the **data** | a brand folder: catalog, voice guide, image manifest |
23
+ | `shop` | the **shop** | Zalify-hosted at `<slug>.z1.shop`: repo + deployment + domain |
24
+ | `ads` | the **ad ops** | Autopilot brand instance: campaign intent repo (media plans, ads-as-code, policy) |
25
+ | `theme` | the **code** | Shopify-theme model: scaffold a copy, measure drift, upgrade |
29
26
 
30
- ### Assets
27
+ Everything acting on a brand folder lives under `brand`. Brand and theme
28
+ commands default their directory to the cwd; `shop refresh`/`open` resolve
29
+ their shop from the cwd (`.zalify/site.json` in a scaffold, `store.json` in
30
+ a brand folder); `ads` commands run arg-free from a checkout carrying
31
+ `.zalify/brand-repo.json`.
31
32
 
32
- Sync a directory's `images/*.png` with the workspace asset library
33
- (the same storage that backs Assets in the Zalify app):
33
+ ## The golden path (new brand, start to live)
34
34
 
35
35
  ```bash
36
- zalify assets push <dir> # upload new/changed files (sha256 dedup)
37
- zalify assets pull <dir> # download files listed in images/assets.json
36
+ zalify brand init stores/acme # 1. author the data (no login needed)
37
+ zalify login # 2. authorize; then: zalify workspace set <workspace>
38
+ zalify shop create acme # 3. live at acme.z1.shop in ~4 minutes
39
+ cd stores/acme && zalify brand push --prune
40
+ zalify brand images generate && zalify brand images attach
41
+ zalify theme status # 6. stay current; `theme upgrade` keeps your edits
38
42
  ```
39
43
 
40
- `push` writes `<dir>/images/assets.json` — a committed index mapping each
41
- file to its asset id, public URL, and checksum. `pull` restores the binaries
42
- on a fresh clone, so image masters stay out of git.
44
+ ## Authentication
43
45
 
44
- ## Commands
46
+ ```bash
47
+ zalify login # opens the browser: authorize once
48
+ zalify login --app-url http://localhost:3000 # against a local app.zalify.com
49
+ zalify whoami # workspace, plan, and key details
50
+ zalify logout # delete local credentials
51
+ ```
45
52
 
46
- - **login** `[--app-url <url>]` Browser-based authorization; stores a workspace API key locally.
47
- - **logout** Deletes `~/.zalify/config.json` (revoke the key itself in Settings → Developer).
48
- - **whoami** Shows the authorized workspace, app origin, and key prefix.
49
- - **assets push `<dir>`** – Uploads `<dir>/images/*.png` via presigned R2 PUTs; sha256 dedup; updates `assets.json`.
50
- - **assets pull `<dir>`** Downloads files listed in `<dir>/images/assets.json` that are missing locally.
51
- - **version** Prints `Zalify CLI version: x.x.x` and, if a newer version exists on npm, suggests running `npm i -g @zalify/cli@latest` to update.
53
+ `login` runs a loopback flow (like `gh auth login`): the CLI listens on a
54
+ random localhost port, the browser opens `app.zalify.com/cli/auth`, and on
55
+ approval a **user key covering all your workspaces** is handed back and
56
+ stored in `~/.zalify/config.json` (0600). Keys are ordinary Zalify Public
57
+ API keys list and revoke them in **Settings Developer**. One login
58
+ covers every workspace; `zalify workspace set` switches between them
59
+ locally, no re-login.
60
+
61
+ ## Command reference
62
+
63
+ ### Auth & workspace
64
+
65
+ | Command | What it does |
66
+ | --- | --- |
67
+ | `login [--app-url <url>]` | Browser-based authorization; stores a user key locally |
68
+ | `logout` | Deletes `~/.zalify/config.json` (revoke the key itself in Settings → Developer) |
69
+ | `whoami` | Verifies the stored key; shows workspace, plan, app origin, key prefix |
70
+ | `workspace` / `workspace list` | List authorized workspaces (`*` = active) |
71
+ | `workspace set <slug-or-id>` | Switch the active workspace (local, no re-login) |
72
+
73
+ ### `brand` — the data
74
+
75
+ | Command | What it does |
76
+ | --- | --- |
77
+ | `brand init [dir] [--name <name>] [--vertical <v>] [--currency <code>]` | Scaffold a brand folder: catalog, voice guide, image manifest, `AUTHORING.md` rules. No login needed |
78
+ | `brand validate [dir]` | Lint the brand folder against the authoring rules |
79
+ | `brand push [dir] [--prune] [--shop <slug>]` | Push `catalog.json` to the connected Shopify store — products, smart collections, publish; idempotent. `--prune` deletes store products (by catalog vendor) absent from the catalog; `--shop` narrows the post-push refresh to one shop |
80
+ | `brand images generate [dir]` | Generate the manifest's missing images on Zalify infrastructure (resumable) |
81
+ | `brand images attach [dir] [--shop <slug>]` | Attach generated images to their Shopify products per the manifest (skips already-attached) |
82
+ | `brand images push [dir]` | Upload image masters to the Zalify asset library — presigned R2 PUTs, sha256 dedup; writes `images/assets.json`, the committed index that keeps binaries out of git |
83
+ | `brand images pull [dir]` | Download masters listed in `images/assets.json` that are missing locally (fresh-clone restore) |
84
+
85
+ ### `shop` — the hosted shop
86
+
87
+ | Command | What it does |
88
+ | --- | --- |
89
+ | `shop create <dir> [-t <theme>] [--editor] [--to <version>] [--no-install] [--no-git]` | One-command launch: provision repo + deployment + domain + storefront token, scaffold locally, push, go live at `<slug>.z1.shop`. `--editor` wires in the Zalify canvas editor (z1) |
90
+ | `shop` / `shop list` | List the workspace's hosted shops |
91
+ | `shop refresh [slug-or-dir]` | Refresh the live shop's data cache (defaults to the shop linked to cwd) |
92
+ | `shop open [slug-or-dir]` | Open the live shop in the browser (defaults to the shop linked to cwd) |
93
+ | `shop domain-sync [slug-or-dir]` | Re-point the shop at the current root domain (the old domain 308-redirects) |
94
+
95
+ `site …` remains as a hidden alias for one release and prints a pointer to
96
+ its new home under `shop`.
97
+
98
+ ### `ads` — the ad ops
99
+
100
+ | Command | What it does |
101
+ | --- | --- |
102
+ | `ads create <dir>` | Provision an Autopilot brand-instance GitHub repo (POST `/api/repos`, GitHub App), scaffold the starter locally (`identity.yaml`, `brand.yaml`, `policy/`, `campaigns/`, `media-plans/`, `signals/`, `decisions/`), and push with a short-lived repo-scoped token. Writes `.zalify/brand-repo.json` so later commands run arg-free. The directory name becomes the slug — use the workspace's store slug so the service's digest writer can find the repo |
103
+ | `ads` / `ads list` | List the workspace's brand instances and their provisioning status |
104
+
105
+ The engine commands — `plan` / `apply` / `creative` / `report` — join this
106
+ group as `@zalify/ads-engine` merges in. Until then, drafting and applying
107
+ happen on the Structure canvas in app.zalify.com; the instance repo is the
108
+ durable intent layer that work lands in.
109
+
110
+ ### `theme` — the code
111
+
112
+ | Command | What it does |
113
+ | --- | --- |
114
+ | `theme create <dir> [-t <template>] [--editor] [--store-domain <domain>] [--storefront-token <token>] [--to <version>] [--no-install] [--no-git] [--tarball <path>]` | Scaffold only — no Zalify hosting (the self-host path; `shop create` is the fully-hosted launch). Templates: `liquid` \| `hydrogen` \| `nextjs`. Without `--store-domain` it runs on mock.shop demo data |
115
+ | `theme status [dir]` | Which theme files have you edited? Checksums vs `.zalify/theme.json` |
116
+ | `theme upgrade [dir] [--to <version>] [--dry-run] [--install] [--tarball <path>] [--base-tarball <path>]` | Move to a newer theme version: untouched files overwritten, edited files 3-way-merged, `theme/` + `.env` never touched. `--dry-run` classifies and reports without writing |
117
+
118
+ ### Maintenance
119
+
120
+ | Command | What it does |
121
+ | --- | --- |
122
+ | `version` | Print the CLI version and check npm for a newer one |
123
+ | `self-update` | Update the CLI to the latest version (auto-detects pnpm/bun/npm) |
124
+
125
+ Every invocation also runs a passive, non-blocking update check and prints
126
+ a one-line notice when a newer version exists.
52
127
 
53
128
  ## Development
54
129
 
@@ -76,8 +151,6 @@ Release flow:
76
151
 
77
152
  ```bash
78
153
  npm version minor --no-git-tag-version # or patch/major
79
- git commit -am "chore: release v0.x.0" && git push
154
+ # bump zalify/package.json in step and pin its @zalify/cli dependency
155
+ git commit -am "0.x.0" && git push
80
156
  ```
81
-
82
- `bun run release` (bump + local `npm publish`, needs `NODE_AUTH_TOKEN` in
83
- `.env`) remains as a manual fallback if CI is unavailable.
package/dist/cli.js CHANGED
@@ -11293,7 +11293,7 @@ function updateNotifier(options) {
11293
11293
 
11294
11294
  // src/cli.ts
11295
11295
  import { createRequire as createRequire2 } from "node:module";
11296
- import { dirname as dirname2, join as join9 } from "node:path";
11296
+ import { dirname as dirname2, join as join10 } from "node:path";
11297
11297
  import { fileURLToPath as fileURLToPath3 } from "node:url";
11298
11298
 
11299
11299
  // src/auth.ts
@@ -11762,37 +11762,122 @@ async function assetsPush(storeDir) {
11762
11762
 
11763
11763
  // src/self-update.ts
11764
11764
  import { spawnSync } from "node:child_process";
11765
- import { realpathSync } from "node:fs";
11766
- function detectManager() {
11767
- let binPath = process.argv[1] ?? "";
11765
+ import { existsSync as existsSync3, realpathSync } from "node:fs";
11766
+ import { delimiter, join as join3 } from "node:path";
11767
+ var PACKAGES = ["zalify", "@zalify/cli"];
11768
+ var real = (p) => {
11768
11769
  try {
11769
- binPath = realpathSync(binPath);
11770
- } catch {}
11770
+ return realpathSync(p);
11771
+ } catch {
11772
+ return p;
11773
+ }
11774
+ };
11775
+ function managerFor(binPath) {
11771
11776
  if (binPath.includes("/pnpm/") || binPath.includes("/.pnpm/")) {
11772
- return { cmd: "pnpm", args: ["add", "-g", "@zalify/cli@latest"] };
11777
+ return {
11778
+ cmd: "pnpm",
11779
+ update: (pkg) => ["add", "-g", `${pkg}@latest`],
11780
+ remove: `pnpm rm -g ${PACKAGES.join(" ")}`
11781
+ };
11773
11782
  }
11774
11783
  if (binPath.includes("/.bun/")) {
11775
- return { cmd: "bun", args: ["add", "-g", "@zalify/cli@latest"] };
11784
+ return {
11785
+ cmd: "bun",
11786
+ update: (pkg) => ["add", "-g", `${pkg}@latest`],
11787
+ remove: `bun remove -g ${PACKAGES.join(" ")}`
11788
+ };
11789
+ }
11790
+ return {
11791
+ cmd: "npm",
11792
+ update: (pkg) => ["install", "-g", `${pkg}@latest`],
11793
+ remove: `npm rm -g ${PACKAGES.join(" ")}`
11794
+ };
11795
+ }
11796
+ function packageFor(binPath) {
11797
+ return /\/node_modules\/zalify\//.test(binPath) || /\/zalify@\d/.test(binPath) ? "zalify" : "@zalify/cli";
11798
+ }
11799
+ function zalifysOnPath() {
11800
+ const hits = [];
11801
+ for (const dir2 of (process.env.PATH ?? "").split(delimiter)) {
11802
+ if (!dir2)
11803
+ continue;
11804
+ const candidate = join3(dir2, "zalify");
11805
+ if (existsSync3(candidate) && !hits.includes(candidate)) {
11806
+ hits.push(candidate);
11807
+ }
11776
11808
  }
11777
- return { cmd: "npm", args: ["install", "-g", "@zalify/cli@latest"] };
11809
+ return hits;
11810
+ }
11811
+ function versionOf(bin) {
11812
+ const res = spawnSync(bin, ["--version"], { encoding: "utf8" });
11813
+ if (res.error || res.status !== 0)
11814
+ return null;
11815
+ return res.stdout?.trim() || null;
11778
11816
  }
11779
- function selfUpdate(currentVersion) {
11780
- const { cmd, args } = detectManager();
11817
+ var HASH_HINT = "If `zalify -v` still shows the old version in this terminal, the shell\n" + "has cached the old location — run `hash -r` (zsh/bash) or open a new\n" + "terminal. A CLI cannot refresh its parent shell for you.";
11818
+ async function selfUpdate(currentVersion) {
11819
+ const running = real(process.argv[1] ?? "");
11820
+ const manager = managerFor(running);
11821
+ const pkg = packageFor(running);
11781
11822
  console.log(`Current version: ${currentVersion}`);
11782
- console.log(`Updating via: ${cmd} ${args.join(" ")}
11823
+ console.log(`Updating via: ${manager.cmd} ${manager.update(pkg).join(" ")}
11783
11824
  `);
11784
- const result = spawnSync(cmd, args, { stdio: "inherit" });
11825
+ const result = spawnSync(manager.cmd, manager.update(pkg), {
11826
+ stdio: "inherit"
11827
+ });
11785
11828
  if (result.error || result.status !== 0) {
11786
11829
  console.error(`
11787
11830
  Update failed${result.error ? `: ${result.error.message}` : ""}.` + `
11788
- Try manually: ${cmd} ${args.join(" ")}`);
11831
+ Try manually: ${manager.cmd} ${manager.update(pkg).join(" ")}`);
11789
11832
  process.exit(result.status ?? 1);
11790
11833
  }
11791
- const check = spawnSync("zalify", ["--version"], { encoding: "utf8" });
11792
- const updated = check.stdout?.trim();
11793
- console.log(updated && updated !== currentVersion ? `
11794
- Updated ${currentVersion} ${updated}` : `
11795
- ✓ Done${updated ? ` (version: ${updated})` : ""}`);
11834
+ let latest = null;
11835
+ try {
11836
+ const info = await updateNotifier({
11837
+ pkg: { name: "@zalify/cli", version: currentVersion }
11838
+ }).fetchInfo();
11839
+ latest = info?.latest ?? null;
11840
+ } catch {}
11841
+ const hits = zalifysOnPath();
11842
+ const first = hits[0];
11843
+ const firstVersion = first ? versionOf(first) : null;
11844
+ const mayBeHashed = hits.length > 1 || first && real(first) !== running;
11845
+ if (!first || !firstVersion) {
11846
+ console.log(`
11847
+ ✓ Done`);
11848
+ return;
11849
+ }
11850
+ const reachedLatest = latest ? firstVersion === latest : firstVersion !== currentVersion;
11851
+ if (reachedLatest) {
11852
+ console.log(firstVersion === currentVersion ? `
11853
+ ✓ Already the latest (${firstVersion})` : `
11854
+ ✓ Updated ${currentVersion} → ${firstVersion}`);
11855
+ if (mayBeHashed)
11856
+ console.log(`
11857
+ ${HASH_HINT}`);
11858
+ return;
11859
+ }
11860
+ if (latest) {
11861
+ console.log(`
11862
+ ✗ ${pkg} is now ${latest}, but your PATH still runs`);
11863
+ console.log(` ${first} (${firstVersion})`);
11864
+ if (hits.length > 1) {
11865
+ console.log(`
11866
+ Every zalify on your PATH:`);
11867
+ for (const hit of hits) {
11868
+ console.log(` ${hit} → ${versionOf(hit) ?? "unreadable"}`);
11869
+ }
11870
+ }
11871
+ console.log(`
11872
+ Remove the stale install (${managerFor(real(first)).remove}),`);
11873
+ console.log("then run `hash -r` or open a new terminal.");
11874
+ process.exit(1);
11875
+ }
11876
+ console.log(`
11877
+ ✓ Done (version: ${firstVersion})`);
11878
+ if (mayBeHashed)
11879
+ console.log(`
11880
+ ${HASH_HINT}`);
11796
11881
  }
11797
11882
 
11798
11883
  // src/workspace.ts
@@ -11867,8 +11952,8 @@ async function workspaceSet(slugOrId) {
11867
11952
  }
11868
11953
 
11869
11954
  // src/images.ts
11870
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, existsSync as existsSync3, rmSync as rmSync2 } from "node:fs";
11871
- import { join as join3, resolve as resolve2 } from "node:path";
11955
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, existsSync as existsSync4, rmSync as rmSync2 } from "node:fs";
11956
+ import { join as join4, resolve as resolve2 } from "node:path";
11872
11957
  var MAX_JOBS_PER_REQUEST = 40;
11873
11958
  var POLL_INTERVAL_MS = 5000;
11874
11959
  var POLL_TIMEOUT_MS = 30 * 60 * 1000;
@@ -11896,7 +11981,7 @@ async function saveProduced(imagesDir, indexPath, index, file2, asset) {
11896
11981
  return false;
11897
11982
  }
11898
11983
  const bytes = Buffer.from(await img.arrayBuffer());
11899
- writeFileSync4(join3(imagesDir, file2), bytes);
11984
+ writeFileSync4(join4(imagesDir, file2), bytes);
11900
11985
  index[file2] = {
11901
11986
  assetId: asset.id,
11902
11987
  url: asset.url,
@@ -12033,17 +12118,17 @@ async function streamingGenerate(auth, manifest, missing, imagesDir, indexPath,
12033
12118
  }
12034
12119
  async function imagesGenerate(storeDir) {
12035
12120
  const auth = requireActive();
12036
- const imagesDir = join3(resolve2(storeDir), "images");
12037
- const manifestPath = join3(imagesDir, "manifest.json");
12038
- if (!existsSync3(manifestPath)) {
12121
+ const imagesDir = join4(resolve2(storeDir), "images");
12122
+ const manifestPath = join4(imagesDir, "manifest.json");
12123
+ if (!existsSync4(manifestPath)) {
12039
12124
  throw new Error(`No images/manifest.json in ${storeDir}`);
12040
12125
  }
12041
12126
  const manifest = JSON.parse(readFileSync3(manifestPath, "utf8"));
12042
- const indexPath = join3(imagesDir, "assets.json");
12043
- const index = existsSync3(indexPath) ? JSON.parse(readFileSync3(indexPath, "utf8")) : {};
12044
- const jobStatePath = join3(imagesDir, ".imagegen-job.json");
12127
+ const indexPath = join4(imagesDir, "assets.json");
12128
+ const index = existsSync4(indexPath) ? JSON.parse(readFileSync3(indexPath, "utf8")) : {};
12129
+ const jobStatePath = join4(imagesDir, ".imagegen-job.json");
12045
12130
  const counters = { produced: 0, failed: 0 };
12046
- if (existsSync3(jobStatePath)) {
12131
+ if (existsSync4(jobStatePath)) {
12047
12132
  const state = JSON.parse(readFileSync3(jobStatePath, "utf8"));
12048
12133
  console.log(`Resuming job ${state.jobId}`);
12049
12134
  await pollJob(auth, state.jobId, state.files, imagesDir, indexPath, index, counters);
@@ -12051,7 +12136,7 @@ async function imagesGenerate(storeDir) {
12051
12136
  console.log(`Done: ${counters.produced} generated (already in the asset library), ${counters.failed} failed${counters.failed ? " — re-run to retry" : ""}.`);
12052
12137
  return;
12053
12138
  }
12054
- const missing = manifest.images.filter((e) => !existsSync3(join3(imagesDir, e.file)));
12139
+ const missing = manifest.images.filter((e) => !existsSync4(join4(imagesDir, e.file)));
12055
12140
  if (missing.length === 0) {
12056
12141
  console.log("All manifest images already exist locally.");
12057
12142
  return;
@@ -12075,20 +12160,20 @@ async function imagesGenerate(storeDir) {
12075
12160
  }
12076
12161
 
12077
12162
  // src/shopify.ts
12078
- import { readFileSync as readFileSync6, existsSync as existsSync6 } from "node:fs";
12079
- import { basename as basename3, join as join6, resolve as resolve5 } from "node:path";
12163
+ import { readFileSync as readFileSync6, existsSync as existsSync7 } from "node:fs";
12164
+ import { basename as basename3, join as join7, resolve as resolve5 } from "node:path";
12080
12165
 
12081
12166
  // src/shop.ts
12082
12167
  import { spawn as spawn3, spawnSync as spawnSync3 } from "node:child_process";
12083
- import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "node:fs";
12084
- import { basename as basename2, join as join5, resolve as resolve4 } from "node:path";
12168
+ import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "node:fs";
12169
+ import { basename as basename2, join as join6, resolve as resolve4 } from "node:path";
12085
12170
 
12086
12171
  // src/theme.ts
12087
12172
  import { spawnSync as spawnSync2 } from "node:child_process";
12088
12173
  import { randomBytes as randomBytes2 } from "node:crypto";
12089
12174
  import {
12090
12175
  cpSync,
12091
- existsSync as existsSync4,
12176
+ existsSync as existsSync5,
12092
12177
  mkdirSync as mkdirSync2,
12093
12178
  mkdtempSync,
12094
12179
  readdirSync as readdirSync2,
@@ -12100,7 +12185,7 @@ import {
12100
12185
  writeFileSync as writeFileSync5
12101
12186
  } from "node:fs";
12102
12187
  import { tmpdir } from "node:os";
12103
- import { basename, dirname, join as join4, resolve as resolve3 } from "node:path";
12188
+ import { basename, dirname, join as join5, resolve as resolve3 } from "node:path";
12104
12189
  var TEMPLATES_PKG = "@zalify/theme-templates";
12105
12190
  var REGISTRY = "https://registry.npmjs.org";
12106
12191
  var MANIFEST_PATH = ".zalify/theme.json";
@@ -12126,27 +12211,27 @@ async function downloadTarball(url) {
12126
12211
  const res = await fetch(url);
12127
12212
  if (!res.ok)
12128
12213
  throw new Error(`tarball download ${res.status}: ${url}`);
12129
- const file2 = join4(mkdtempSync(join4(tmpdir(), "zalify-theme-")), "pkg.tgz");
12214
+ const file2 = join5(mkdtempSync(join5(tmpdir(), "zalify-theme-")), "pkg.tgz");
12130
12215
  writeFileSync5(file2, Buffer.from(await res.arrayBuffer()));
12131
12216
  return file2;
12132
12217
  }
12133
12218
  function extractTarball(file2) {
12134
- const dir2 = mkdtempSync(join4(tmpdir(), "zalify-theme-x-"));
12219
+ const dir2 = mkdtempSync(join5(tmpdir(), "zalify-theme-x-"));
12135
12220
  const result = spawnSync2("tar", ["-xzf", resolve3(file2), "-C", dir2], {
12136
12221
  encoding: "utf8"
12137
12222
  });
12138
12223
  if (result.error || result.status !== 0) {
12139
12224
  throw new Error(`tar extract failed for ${file2}: ${result.stderr || result.error?.message}`);
12140
12225
  }
12141
- const root = join4(dir2, "package");
12142
- if (!existsSync4(root))
12226
+ const root = join5(dir2, "package");
12227
+ if (!existsSync5(root))
12143
12228
  throw new Error(`unexpected tarball layout in ${file2} (no package/)`);
12144
12229
  return root;
12145
12230
  }
12146
12231
  async function acquireTemplatePkg(localTarball, version) {
12147
12232
  if (localTarball) {
12148
12233
  const root = extractTarball(localTarball);
12149
- const v = JSON.parse(readFileSync4(join4(root, "package.json"), "utf8")).version;
12234
+ const v = JSON.parse(readFileSync4(join5(root, "package.json"), "utf8")).version;
12150
12235
  return { root, version: v };
12151
12236
  }
12152
12237
  const packument = await fetchPackument();
@@ -12157,8 +12242,8 @@ async function acquireTemplatePkg(localTarball, version) {
12157
12242
  return { root: extractTarball(await downloadTarball(entry.dist.tarball)), version: target };
12158
12243
  }
12159
12244
  function readTemplateManifest(pkgRoot, template) {
12160
- const path9 = join4(pkgRoot, "templates", template, MANIFEST_PATH);
12161
- if (!existsSync4(path9)) {
12245
+ const path9 = join5(pkgRoot, "templates", template, MANIFEST_PATH);
12246
+ if (!existsSync5(path9)) {
12162
12247
  throw new Error(`template "${template}" has no ${MANIFEST_PATH} — this ${TEMPLATES_PKG} version predates the upgrade contract`);
12163
12248
  }
12164
12249
  return JSON.parse(readFileSync4(path9, "utf8"));
@@ -12175,21 +12260,21 @@ function templateFilePath(templateDir, rel, variant) {
12175
12260
  const stashed = rel.replace(/(^|\/)\.gitignore$/, "$1gitignore");
12176
12261
  if (variant === "editor") {
12177
12262
  for (const candidate of [rel, stashed]) {
12178
- const overlay = join4(templateDir, "__editor__", candidate);
12179
- if (existsSync4(overlay))
12263
+ const overlay = join5(templateDir, "__editor__", candidate);
12264
+ if (existsSync5(overlay))
12180
12265
  return overlay;
12181
12266
  }
12182
12267
  }
12183
12268
  for (const candidate of [rel, stashed]) {
12184
- const path9 = join4(templateDir, candidate);
12185
- if (existsSync4(path9))
12269
+ const path9 = join5(templateDir, candidate);
12270
+ if (existsSync5(path9))
12186
12271
  return path9;
12187
12272
  }
12188
12273
  throw new Error(`file listed in manifest but missing from template: ${rel}`);
12189
12274
  }
12190
12275
  function readProjectManifest(dir2) {
12191
- const path9 = join4(dir2, MANIFEST_PATH);
12192
- if (!existsSync4(path9)) {
12276
+ const path9 = join5(dir2, MANIFEST_PATH);
12277
+ if (!existsSync5(path9)) {
12193
12278
  throw new Error(`no ${MANIFEST_PATH} in ${dir2} — not a scaffolded Zalify storefront.
12194
12279
  ` + ` Scaffold one with \`zalify theme create\`.`);
12195
12280
  }
@@ -12201,22 +12286,22 @@ function writeJson(path9, value) {
12201
12286
  }
12202
12287
  async function themeCreate(dir2, opts) {
12203
12288
  const targetDir = resolve3(dir2);
12204
- if (existsSync4(targetDir) && readdirSync2(targetDir).length > 0) {
12289
+ if (existsSync5(targetDir) && readdirSync2(targetDir).length > 0) {
12205
12290
  throw new Error(`${dir2} already exists and is not empty`);
12206
12291
  }
12207
12292
  const { root, version } = await acquireTemplatePkg(opts.tarball, opts.to);
12208
- const frameworks = JSON.parse(readFileSync4(join4(root, "templates.json"), "utf8"));
12293
+ const frameworks = JSON.parse(readFileSync4(join5(root, "templates.json"), "utf8"));
12209
12294
  const template = opts.template ?? "nextjs";
12210
12295
  const meta = frameworks.frameworks[template];
12211
12296
  if (!meta) {
12212
12297
  throw new Error(`unknown template "${template}" — expected one of: ${Object.keys(frameworks.frameworks).join(", ")}`);
12213
12298
  }
12214
- const templateDir = join4(root, "templates", template);
12299
+ const templateDir = join5(root, "templates", template);
12215
12300
  console.log(`Scaffolding ${meta.label} (${TEMPLATES_PKG}@${version}) into ${dir2}`);
12216
12301
  mkdirSync2(targetDir, { recursive: true });
12217
12302
  cpSync(templateDir, targetDir, { recursive: true });
12218
- const overlayDir = join4(targetDir, "__editor__");
12219
- const useEditor = Boolean(opts.editor) && existsSync4(overlayDir);
12303
+ const overlayDir = join5(targetDir, "__editor__");
12304
+ const useEditor = Boolean(opts.editor) && existsSync5(overlayDir);
12220
12305
  if (opts.editor && !useEditor) {
12221
12306
  console.log(` ! --editor has no effect for ${template} — no editor overlay in this template`);
12222
12307
  }
@@ -12225,14 +12310,14 @@ async function themeCreate(dir2, opts) {
12225
12310
  rmSync3(overlayDir, { recursive: true, force: true });
12226
12311
  (function restoreGitignores(d) {
12227
12312
  for (const entry of readdirSync2(d)) {
12228
- const full = join4(d, entry);
12313
+ const full = join5(d, entry);
12229
12314
  if (statSync(full).isDirectory())
12230
12315
  restoreGitignores(full);
12231
12316
  else if (entry === "gitignore")
12232
- renameSync(full, join4(d, ".gitignore"));
12317
+ renameSync(full, join5(d, ".gitignore"));
12233
12318
  }
12234
12319
  })(targetDir);
12235
- const pkgPath = join4(targetDir, "package.json");
12320
+ const pkgPath = join5(targetDir, "package.json");
12236
12321
  const pkg = JSON.parse(readFileSync4(pkgPath, "utf8"));
12237
12322
  pkg.name = basename(targetDir).toLowerCase().replaceAll(/[^a-z0-9-_.]+/g, "-").replaceAll(/^[-.]+|[-.]+$/g, "") || "zalify-storefront";
12238
12323
  writeJson(pkgPath, pkg);
@@ -12246,12 +12331,12 @@ async function themeCreate(dir2, opts) {
12246
12331
  if (meta.env.sessionSecret) {
12247
12332
  lines.push("# Session cookie signing key — unique to this project.", `${meta.env.sessionSecret}=${randomBytes2(32).toString("hex")}`);
12248
12333
  }
12249
- writeFileSync5(join4(targetDir, ".env"), lines.join(`
12334
+ writeFileSync5(join5(targetDir, ".env"), lines.join(`
12250
12335
  `) + `
12251
12336
  `);
12252
12337
  }
12253
12338
  const manifest = readProjectManifest(targetDir);
12254
- writeJson(join4(targetDir, MANIFEST_PATH), resolveVariant(manifest, useEditor ? "editor" : "default"));
12339
+ writeJson(join5(targetDir, MANIFEST_PATH), resolveVariant(manifest, useEditor ? "editor" : "default"));
12255
12340
  if (opts.git !== false) {
12256
12341
  const git = (args) => spawnSync2("git", args, { cwd: targetDir, stdio: "ignore" });
12257
12342
  if (git(["init", "-b", "main"]).status === 0) {
@@ -12291,8 +12376,8 @@ function themeStatus(dir2 = ".") {
12291
12376
  const edited = [];
12292
12377
  const missing = [];
12293
12378
  for (const [rel, expected] of Object.entries(manifest.files)) {
12294
- const full = join4(projectDir, rel);
12295
- if (!existsSync4(full))
12379
+ const full = join5(projectDir, rel);
12380
+ if (!existsSync5(full))
12296
12381
  missing.push(rel);
12297
12382
  else if (sha256(readFileSync4(full)) !== expected)
12298
12383
  edited.push(rel);
@@ -12326,8 +12411,8 @@ async function themeUpgrade(dir2 = ".", opts = {}) {
12326
12411
  }
12327
12412
  const oldManifest = resolveVariant(readTemplateManifest(base.root, local.template), variant);
12328
12413
  const newManifest = resolveVariant(readTemplateManifest(target.root, local.template), variant);
12329
- const oldDir = join4(base.root, "templates", local.template);
12330
- const newDir = join4(target.root, "templates", local.template);
12414
+ const oldDir = join5(base.root, "templates", local.template);
12415
+ const newDir = join5(target.root, "templates", local.template);
12331
12416
  console.log(`Upgrading ${local.template} theme ${local.version} → ${target.version}` + (opts.dryRun ? " (dry run)" : ""));
12332
12417
  const counts = {
12333
12418
  overwrite: 0,
@@ -12345,8 +12430,8 @@ async function themeUpgrade(dir2 = ".", opts = {}) {
12345
12430
  continue;
12346
12431
  const oldHash = oldManifest.files[rel];
12347
12432
  const newHash = newManifest.files[rel];
12348
- const localPath = join4(projectDir, rel);
12349
- const localExists = existsSync4(localPath);
12433
+ const localPath = join5(projectDir, rel);
12434
+ const localExists = existsSync5(localPath);
12350
12435
  const localBytes = localExists ? readFileSync4(localPath) : null;
12351
12436
  const localHash = localBytes ? sha256(localBytes) : null;
12352
12437
  const write = (bytes) => {
@@ -12385,9 +12470,9 @@ async function themeUpgrade(dir2 = ".", opts = {}) {
12385
12470
  console.log(` ~ would merge ${rel}`);
12386
12471
  continue;
12387
12472
  }
12388
- const scratch = mkdtempSync(join4(tmpdir(), "zalify-merge-"));
12389
- const basePath = join4(scratch, "base");
12390
- const newPath = join4(scratch, "new");
12473
+ const scratch = mkdtempSync(join5(tmpdir(), "zalify-merge-"));
12474
+ const basePath = join5(scratch, "base");
12475
+ const newPath = join5(scratch, "new");
12391
12476
  writeFileSync5(basePath, oldBytes);
12392
12477
  writeFileSync5(newPath, newBytes);
12393
12478
  const result = spawnSync2("git", ["merge-file", "-L", "current", "-L", `base (${base.version})`, "-L", `new (${target.version})`, localPath, basePath, newPath], { encoding: "utf8" });
@@ -12431,7 +12516,7 @@ async function themeUpgrade(dir2 = ".", opts = {}) {
12431
12516
  }
12432
12517
  const depsChanged = mergePackageJson(projectDir, oldDir, newDir, opts.dryRun ?? false);
12433
12518
  if (!opts.dryRun) {
12434
- writeJson(join4(projectDir, MANIFEST_PATH), { ...newManifest, version: target.version });
12519
+ writeJson(join5(projectDir, MANIFEST_PATH), { ...newManifest, version: target.version });
12435
12520
  }
12436
12521
  const summary = Object.entries(counts).filter(([, n]) => n > 0).map(([k, n]) => `${n} ${k.replace("-", " ")}`).join(", ");
12437
12522
  console.log(`
@@ -12452,7 +12537,7 @@ ${opts.dryRun ? "Would apply" : "Done"}: ${summary || "no file changes"}.`);
12452
12537
  }
12453
12538
  }
12454
12539
  function mergePackageJson(projectDir, oldDir, newDir, dryRun) {
12455
- const read = (p) => JSON.parse(readFileSync4(join4(p, "package.json"), "utf8"));
12540
+ const read = (p) => JSON.parse(readFileSync4(join5(p, "package.json"), "utf8"));
12456
12541
  const localPkg = read(projectDir);
12457
12542
  const oldPkg = read(oldDir);
12458
12543
  const newPkg = read(newDir);
@@ -12501,7 +12586,7 @@ function mergePackageJson(projectDir, oldDir, newDir, dryRun) {
12501
12586
  console.log(` ✓ package.json ${key} updated`);
12502
12587
  }
12503
12588
  if (changed && !dryRun) {
12504
- writeFileSync5(join4(projectDir, "package.json"), JSON.stringify(localPkg, null, 2) + `
12589
+ writeFileSync5(join5(projectDir, "package.json"), JSON.stringify(localPkg, null, 2) + `
12505
12590
  `);
12506
12591
  }
12507
12592
  return depsChanged;
@@ -12597,9 +12682,9 @@ async function shopCreate(dir2, opts) {
12597
12682
  install: opts.install,
12598
12683
  git: opts.git
12599
12684
  });
12600
- writeFileSync6(join5(resolve4(dir2), ".zalify", "site.json"), JSON.stringify({ id: siteId, slug: view.slug, domain: view.domain, repo: view.githubRepo }, null, 2) + `
12685
+ writeFileSync6(join6(resolve4(dir2), ".zalify", "site.json"), JSON.stringify({ id: siteId, slug: view.slug, domain: view.domain, repo: view.githubRepo }, null, 2) + `
12601
12686
  `);
12602
- writeFileSync6(join5(resolve4(dir2), "theme", "pixel.json"), JSON.stringify({ workspaceId: auth.workspaceId }, null, 2) + `
12687
+ writeFileSync6(join6(resolve4(dir2), "theme", "pixel.json"), JSON.stringify({ workspaceId: auth.workspaceId }, null, 2) + `
12603
12688
  `);
12604
12689
  if (opts.git !== false && view.githubRepo) {
12605
12690
  const { token, repo } = await request(auth, "POST", `/api/sites/${siteId}/push-token`, {});
@@ -12640,14 +12725,14 @@ Next steps:
12640
12725
  }
12641
12726
  async function resolveSite(auth, slugOrDir) {
12642
12727
  const fromDir = (dir2) => {
12643
- const link = join5(dir2, ".zalify", "site.json");
12644
- if (existsSync5(link)) {
12728
+ const link = join6(dir2, ".zalify", "site.json");
12729
+ if (existsSync6(link)) {
12645
12730
  const parsed = JSON.parse(readFileSync5(link, "utf8"));
12646
12731
  if (parsed.slug)
12647
12732
  return parsed.slug;
12648
12733
  }
12649
- const storeJson = join5(dir2, "store.json");
12650
- if (existsSync5(storeJson)) {
12734
+ const storeJson = join6(dir2, "store.json");
12735
+ if (existsSync6(storeJson)) {
12651
12736
  const parsed = JSON.parse(readFileSync5(storeJson, "utf8"));
12652
12737
  if (parsed.slug)
12653
12738
  return parsed.slug;
@@ -12655,7 +12740,7 @@ async function resolveSite(auth, slugOrDir) {
12655
12740
  return null;
12656
12741
  };
12657
12742
  let slug = null;
12658
- if (slugOrDir && existsSync5(resolve4(slugOrDir)))
12743
+ if (slugOrDir && existsSync6(resolve4(slugOrDir)))
12659
12744
  slug = fromDir(resolve4(slugOrDir));
12660
12745
  if (!slug && slugOrDir)
12661
12746
  slug = slugOrDir;
@@ -12716,8 +12801,8 @@ async function maybeRefreshShops(auth, brandDir, siteSlugOverride) {
12716
12801
  try {
12717
12802
  let slug = siteSlugOverride ?? null;
12718
12803
  if (!slug) {
12719
- const storeJson = join5(resolve4(brandDir), "store.json");
12720
- if (existsSync5(storeJson)) {
12804
+ const storeJson = join6(resolve4(brandDir), "store.json");
12805
+ if (existsSync6(storeJson)) {
12721
12806
  slug = JSON.parse(readFileSync5(storeJson, "utf8")).slug ?? null;
12722
12807
  }
12723
12808
  }
@@ -12772,7 +12857,7 @@ async function gql(auth, query, variables = {}) {
12772
12857
  return json.data;
12773
12858
  }
12774
12859
  function readJson(path9, label) {
12775
- if (!existsSync6(path9)) {
12860
+ if (!existsSync7(path9)) {
12776
12861
  throw new Error(`No ${label} at ${path9}`);
12777
12862
  }
12778
12863
  return JSON.parse(readFileSync6(path9, "utf8"));
@@ -12802,7 +12887,7 @@ async function findProductId(auth, handle) {
12802
12887
  async function shopifyImport(storeDir, options = {}) {
12803
12888
  const auth = requireActive();
12804
12889
  const dir2 = resolve5(storeDir);
12805
- const catalog = readJson(join6(dir2, "catalog.json"), "catalog.json");
12890
+ const catalog = readJson(join7(dir2, "catalog.json"), "catalog.json");
12806
12891
  const { locations } = await gql(auth, `{ locations(first: 1) { nodes { id name } } }`);
12807
12892
  const locationId = locations.nodes[0]?.id;
12808
12893
  console.log(`Importing ${catalog.products.length} product(s) into the Shopify store connected to ` + `workspace "${auth.workspaceName}" (location: ${locations.nodes[0]?.name ?? "none"})`);
@@ -13011,13 +13096,13 @@ async function shopifyImport(storeDir, options = {}) {
13011
13096
  async function shopifyUploadImages(storeDir, options = {}) {
13012
13097
  const auth = requireActive();
13013
13098
  const dir2 = resolve5(storeDir);
13014
- const manifest = readJson(join6(dir2, "images", "manifest.json"), "images/manifest.json");
13099
+ const manifest = readJson(join7(dir2, "images", "manifest.json"), "images/manifest.json");
13015
13100
  console.log(`Uploading images to the Shopify store connected to workspace "${auth.workspaceName}"`);
13016
13101
  for (const entry of manifest.images) {
13017
13102
  if (entry.type === "banner" || !entry.handle)
13018
13103
  continue;
13019
- const filePath = join6(dir2, "images", entry.file);
13020
- if (!existsSync6(filePath)) {
13104
+ const filePath = join7(dir2, "images", entry.file);
13105
+ if (!existsSync7(filePath)) {
13021
13106
  console.log(` - ${entry.handle}: ${entry.file} not generated yet, skipped`);
13022
13107
  continue;
13023
13108
  }
@@ -13092,7 +13177,7 @@ async function shopifyUploadImages(storeDir, options = {}) {
13092
13177
  // src/ads.ts
13093
13178
  import { spawnSync as spawnSync4 } from "node:child_process";
13094
13179
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync7 } from "node:fs";
13095
- import { basename as basename4, join as join7, resolve as resolve6 } from "node:path";
13180
+ import { basename as basename4, join as join8, resolve as resolve6 } from "node:path";
13096
13181
  function apiError3(status, json) {
13097
13182
  if (json.code === "SLUG_TAKEN") {
13098
13183
  return new Error(`${json.error}
@@ -13144,8 +13229,8 @@ async function pollUntilReady2(auth, id) {
13144
13229
  function scaffoldStarter(dir2, slug, workspaceId) {
13145
13230
  const root = resolve6(dir2);
13146
13231
  const write = (rel, content) => {
13147
- const abs = join7(root, rel);
13148
- mkdirSync3(join7(abs, ".."), { recursive: true });
13232
+ const abs = join8(root, rel);
13233
+ mkdirSync3(join8(abs, ".."), { recursive: true });
13149
13234
  writeFileSync7(abs, content);
13150
13235
  };
13151
13236
  write("identity.yaml", `# Brand instance identity — which workspace and which platform accounts
@@ -13202,8 +13287,8 @@ async function adsCreate(dir2) {
13202
13287
  const view = await pollUntilReady2(auth, created.repoId);
13203
13288
  console.log(` ✓ provisioned: ${view.githubRepo}`);
13204
13289
  scaffoldStarter(dir2, slug, auth.workspaceId);
13205
- mkdirSync3(join7(resolve6(dir2), ".zalify"), { recursive: true });
13206
- writeFileSync7(join7(resolve6(dir2), ".zalify", "brand-repo.json"), JSON.stringify({ id: created.repoId, slug: view.slug, repo: view.githubRepo }, null, 2) + `
13290
+ mkdirSync3(join8(resolve6(dir2), ".zalify"), { recursive: true });
13291
+ writeFileSync7(join8(resolve6(dir2), ".zalify", "brand-repo.json"), JSON.stringify({ id: created.repoId, slug: view.slug, repo: view.githubRepo }, null, 2) + `
13207
13292
  `);
13208
13293
  if (view.githubRepo) {
13209
13294
  const { token, repo } = await request2(auth, "POST", `/api/repos/${created.repoId}/push-token`, {});
@@ -13241,8 +13326,8 @@ async function adsList() {
13241
13326
  }
13242
13327
 
13243
13328
  // src/brand.ts
13244
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync as readdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync8 } from "node:fs";
13245
- import { basename as basename5, join as join8, resolve as resolve7 } from "node:path";
13329
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync8 } from "node:fs";
13330
+ import { basename as basename5, join as join9, resolve as resolve7 } from "node:path";
13246
13331
  var AUTHORING_MD = `# Brand authoring guide
13247
13332
 
13248
13333
  The rules for filling in this folder. An author — human or AI agent —
@@ -13545,17 +13630,17 @@ var MANIFEST_JSON = JSON.stringify({
13545
13630
  function brandInit(dir2 = ".", opts = {}) {
13546
13631
  const target = resolve7(dir2);
13547
13632
  const slug = basename5(target).toLowerCase().replaceAll(/[^a-z0-9-]+/g, "-").replaceAll(/^-+|-+$/g, "");
13548
- if (existsSync7(target) && readdirSync3(target).length > 0) {
13633
+ if (existsSync8(target) && readdirSync3(target).length > 0) {
13549
13634
  throw new Error(`${dir2} already exists and is not empty`);
13550
13635
  }
13551
13636
  const name = opts.name ?? slug.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
13552
13637
  const currency = (opts.currency ?? "USD").toUpperCase();
13553
- mkdirSync4(join8(target, "images"), { recursive: true });
13554
- writeFileSync8(join8(target, "AUTHORING.md"), AUTHORING_MD);
13555
- writeFileSync8(join8(target, "brand.md"), BRAND_MD(name));
13556
- writeFileSync8(join8(target, "store.json"), STORE_JSON(name, slug, opts.vertical ?? "<vertical>", currency));
13557
- writeFileSync8(join8(target, "catalog.json"), CATALOG_JSON(name, currency));
13558
- writeFileSync8(join8(target, "images", "manifest.json"), MANIFEST_JSON);
13638
+ mkdirSync4(join9(target, "images"), { recursive: true });
13639
+ writeFileSync8(join9(target, "AUTHORING.md"), AUTHORING_MD);
13640
+ writeFileSync8(join9(target, "brand.md"), BRAND_MD(name));
13641
+ writeFileSync8(join9(target, "store.json"), STORE_JSON(name, slug, opts.vertical ?? "<vertical>", currency));
13642
+ writeFileSync8(join9(target, "catalog.json"), CATALOG_JSON(name, currency));
13643
+ writeFileSync8(join9(target, "images", "manifest.json"), MANIFEST_JSON);
13559
13644
  console.log(`Scaffolded brand data in ${dir2}:`);
13560
13645
  for (const f of ["AUTHORING.md", "brand.md", "store.json", "catalog.json", "images/manifest.json"]) {
13561
13646
  console.log(` + ${f}`);
@@ -13570,8 +13655,8 @@ function brandValidate(dir2 = ".") {
13570
13655
  const problems = [];
13571
13656
  const warnings = [];
13572
13657
  const readJson2 = (rel) => {
13573
- const path9 = join8(target, rel);
13574
- if (!existsSync7(path9)) {
13658
+ const path9 = join9(target, rel);
13659
+ if (!existsSync8(path9)) {
13575
13660
  problems.push(`missing ${rel}`);
13576
13661
  return null;
13577
13662
  }
@@ -13582,7 +13667,7 @@ function brandValidate(dir2 = ".") {
13582
13667
  return null;
13583
13668
  }
13584
13669
  };
13585
- if (!existsSync7(join8(target, "brand.md")))
13670
+ if (!existsSync8(join9(target, "brand.md")))
13586
13671
  problems.push("missing brand.md");
13587
13672
  const store = readJson2("store.json");
13588
13673
  if (store && (!store.name || !store.slug))
@@ -13758,7 +13843,7 @@ function brandValidate(dir2 = ".") {
13758
13843
  // src/cli.ts
13759
13844
  var __dirname4 = dirname2(fileURLToPath3(import.meta.url));
13760
13845
  var require2 = createRequire2(import.meta.url);
13761
- var pkg = require2(join9(__dirname4, "..", "package.json"));
13846
+ var pkg = require2(join10(__dirname4, "..", "package.json"));
13762
13847
  try {
13763
13848
  updateNotifier({ pkg }).notify({
13764
13849
  isGlobal: true,
@@ -13847,7 +13932,9 @@ theme.command("status [dir]").description("Which theme files have you edited? (c
13847
13932
  theme.command("upgrade [dir]").description("Move to a newer theme version: untouched files overwritten, edited files 3-way-merged, theme/ + .env never touched").option("--to <version>", "target theme version (default: latest)").option("--dry-run", "classify and report without writing anything").option("--install", "run install when dependencies changed").option("--tarball <path>", "target version as a local .tgz (testing)").option("--base-tarball <path>", "base version as a local .tgz (testing)").action(async (dir2, options) => {
13848
13933
  await themeUpgrade(dir2, options);
13849
13934
  });
13850
- program2.command("self-update").description("Update the CLI to the latest version (auto-detects pnpm/bun/npm)").action(() => selfUpdate(pkg.version));
13935
+ program2.command("self-update").description("Update the CLI to the latest version (auto-detects pnpm/bun/npm)").action(async () => {
13936
+ await selfUpdate(pkg.version);
13937
+ });
13851
13938
  program2.command("version").description("Print Zalify CLI version and check for updates").action(async () => {
13852
13939
  console.log(`Zalify CLI version: ${pkg.version}`);
13853
13940
  const isNewer = (a, b) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zalify/cli",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "description": "Zalify CLI - command-line interface for Zalify",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",