lecodes-cli 0.10.1 → 0.10.3

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/dist/index.js CHANGED
@@ -47,6 +47,41 @@ var __export = (target, all) => {
47
47
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
48
48
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
49
49
 
50
+ // ../sdk/src/compile/assetName.ts
51
+ var inRanges = (cp, ranges) => ranges.some(([from, to]) => cp >= from && cp <= to), INVISIBLE, ODD_SPACE, ILLEGAL = '/\\:*?"<>|', sanitizeAssetName = (name) => {
52
+ let out = "";
53
+ for (const ch of name.normalize("NFC")) {
54
+ const cp = ch.codePointAt(0);
55
+ if (inRanges(cp, INVISIBLE))
56
+ continue;
57
+ out += inRanges(cp, ODD_SPACE) ? " " : ILLEGAL.includes(ch) ? "_" : ch;
58
+ }
59
+ return out.replace(/ {2,}/g, " ").replace(/^ +/, "").replace(/[ .]+$/, "");
60
+ }, isCanonicalAssetName = (name) => sanitizeAssetName(name) === name;
61
+ var init_assetName = __esm(() => {
62
+ INVISIBLE = [
63
+ [0, 31],
64
+ [127, 159],
65
+ [173, 173],
66
+ [1564, 1564],
67
+ [6158, 6158],
68
+ [8203, 8207],
69
+ [8232, 8233],
70
+ [8234, 8238],
71
+ [8288, 8292],
72
+ [8294, 8303],
73
+ [65279, 65279]
74
+ ];
75
+ ODD_SPACE = [
76
+ [160, 160],
77
+ [5760, 5760],
78
+ [8192, 8202],
79
+ [8239, 8239],
80
+ [8287, 8287],
81
+ [12288, 12288]
82
+ ];
83
+ });
84
+
50
85
  // ../sdk/src/compile/assetMacro.ts
51
86
  var ASSET_RE, hasAssetMacro = (src) => src.includes("asset("), desugarAssetMacros = (src, filePath) => {
52
87
  const byPath = new Map;
@@ -129,18 +164,18 @@ var init_libraryImports = __esm(() => {
129
164
  });
130
165
 
131
166
  // ../sdk/src/compile/bundler.ts
132
- import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync7, statSync } from "node:fs";
167
+ import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync7, statSync } from "node:fs";
133
168
  import { spawnSync } from "node:child_process";
134
169
  import { createRequire as createRequire2 } from "node:module";
135
170
  import { dirname as dirname4, join as join7, relative as relative2 } from "node:path";
136
171
  import { fileURLToPath as fileURLToPath2 } from "node:url";
137
172
  var resolveInject = () => {
138
173
  const override = process.env.LECODES_SDK_INJECT;
139
- if (override && existsSync4(override))
174
+ if (override && existsSync5(override))
140
175
  return override;
141
176
  const here = dirname4(fileURLToPath2(import.meta.url));
142
177
  const src = join7(here, "..", "inject.ts");
143
- if (existsSync4(src))
178
+ if (existsSync5(src))
144
179
  return src;
145
180
  throw new Error("sdk: could not locate the inject source (LECODES_SDK_INJECT / src/inject.ts)");
146
181
  }, normalize = (path) => {
@@ -180,13 +215,13 @@ var resolveInject = () => {
180
215
  }, resolveChiselBin = () => {
181
216
  const override = process.env.LECODES_CHISEL_BIN;
182
217
  if (override) {
183
- if (existsSync4(override))
218
+ if (existsSync5(override))
184
219
  return override;
185
220
  throw new Error(`LECODES_CHISEL_BIN points at a missing file: ${override}`);
186
221
  }
187
222
  try {
188
223
  const bin = createRequire2(import.meta.url)("@letary/chisel").binaryPath();
189
- if (bin && existsSync4(bin))
224
+ if (bin && existsSync5(bin))
190
225
  return bin;
191
226
  } catch {}
192
227
  throw new Error("chisel binary not found. Install @letary/chisel, or set LECODES_CHISEL_BIN.");
@@ -462,11 +497,11 @@ var init_compileProject = __esm(() => {
462
497
  });
463
498
 
464
499
  // ../sdk/src/compile/sceneEditor.ts
465
- import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync8 } from "node:fs";
500
+ import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync8 } from "node:fs";
466
501
  import { join as join8 } from "node:path";
467
502
  var SCENE_HARNESS_DIR = "./.scene-editor", SCENE_ENTRY_PATH = "./.scene-entry.ts", loadSceneHarness = (fallbackDir) => {
468
503
  const override = process.env.LECODES_SCENE_HARNESS_DIR;
469
- const dir = override && existsSync5(override) ? override : fallbackDir && existsSync5(fallbackDir) ? fallbackDir : null;
504
+ const dir = override && existsSync6(override) ? override : fallbackDir && existsSync6(fallbackDir) ? fallbackDir : null;
470
505
  if (!dir)
471
506
  throw new Error("scene-editor harness sources not found (set LECODES_SCENE_HARNESS_DIR)");
472
507
  const out = {};
@@ -1043,9 +1078,11 @@ __export(exports_compile, {
1043
1078
  scanModulesFallback: () => scanModulesFallback,
1044
1079
  scanModulesChisel: () => scanModulesChisel,
1045
1080
  scanFontCalls: () => scanFontCalls,
1081
+ sanitizeAssetName: () => sanitizeAssetName,
1046
1082
  parseSfnt: () => parseSfnt,
1047
1083
  offsetSourceMap: () => offsetSourceMap,
1048
1084
  loadSceneHarness: () => loadSceneHarness,
1085
+ isCanonicalAssetName: () => isCanonicalAssetName,
1049
1086
  inlineSourceMapComment: () => inlineSourceMapComment,
1050
1087
  fontRegistryBase: () => fontRegistryBase,
1051
1088
  faceFileName: () => faceFileName,
@@ -1066,6 +1103,7 @@ __export(exports_compile, {
1066
1103
  var init_compile = __esm(() => {
1067
1104
  init_bundler();
1068
1105
  init_detectEntry();
1106
+ init_assetName();
1069
1107
  init_compileProject();
1070
1108
  init_sceneEditor();
1071
1109
  init_fontMacro();
@@ -2091,7 +2129,7 @@ var require_main = __commonJS((exports, module) => {
2091
2129
  // package.json
2092
2130
  var package_default = {
2093
2131
  name: "lecodes-cli",
2094
- version: "0.10.1",
2132
+ version: "0.10.3",
2095
2133
  dependencies: {
2096
2134
  "@letary/chisel": "^0.6.0"
2097
2135
  },
@@ -2150,7 +2188,7 @@ var package_default = {
2150
2188
 
2151
2189
  // src/commands/app.ts
2152
2190
  import { execFileSync } from "node:child_process";
2153
- import { cpSync, existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync10, readdirSync as readdirSync4, rmSync, statSync as statSync2, writeFileSync as writeFileSync7 } from "node:fs";
2191
+ import { cpSync, existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync10, readdirSync as readdirSync4, rmSync, statSync as statSync2, writeFileSync as writeFileSync7 } from "node:fs";
2154
2192
  import { homedir as homedir2 } from "node:os";
2155
2193
  import { basename as basename4, dirname as dirname7, join as join11, resolve as resolve2 } from "node:path";
2156
2194
 
@@ -2159,6 +2197,7 @@ import { dirname, join } from "node:path";
2159
2197
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
2160
2198
 
2161
2199
  // src/util.ts
2200
+ init_assetName();
2162
2201
  import { createHash } from "node:crypto";
2163
2202
  import { createInterface } from "node:readline";
2164
2203
  var useColor = process.stdout.isTTY && process.env.NO_COLOR === undefined;
@@ -2204,7 +2243,10 @@ var toKey = (path) => {
2204
2243
  const parts = path.replace(/\\/g, "/").split("/").map((p) => p.trim()).filter((p) => p && p !== ".");
2205
2244
  if (parts.some((p) => p === ".."))
2206
2245
  throw new CliError(`Unsafe path: ${path}`);
2207
- return "/" + parts.join("/");
2246
+ const names = parts.map((p) => sanitizeAssetName(p));
2247
+ if (names.some((name) => !name))
2248
+ throw new CliError(`Wrong path: ${path}`);
2249
+ return "/" + names.join("/");
2208
2250
  };
2209
2251
  var prompt = (question, fallback) => new Promise((resolve) => {
2210
2252
  const rl = createInterface({ input: process.stdin, output: process.stdout });
@@ -2493,7 +2535,8 @@ import { readFileSync as readFileSync5 } from "node:fs";
2493
2535
  import { basename } from "node:path";
2494
2536
 
2495
2537
  // src/localFiles.ts
2496
- import { readdirSync, readFileSync as readFileSync4 } from "node:fs";
2538
+ init_assetName();
2539
+ import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync4 } from "node:fs";
2497
2540
  import { join as join4, relative } from "node:path";
2498
2541
  var SKIP_DIRS = new Set([".lecodes", ".git", "node_modules"]);
2499
2542
  var SKIP_FILES = new Set(["tsconfig.json", "jsconfig.json", ".gitignore", ".lecodesignore", ".DS_Store"]);
@@ -2575,6 +2618,23 @@ var scanLocal = (root, opts = {}) => {
2575
2618
  walk(root, false);
2576
2619
  return { files, dirs, ignored };
2577
2620
  };
2621
+ var resolveLocalPath = (root, key) => {
2622
+ const direct = join4(root, key.slice(1));
2623
+ if (existsSync3(direct))
2624
+ return direct;
2625
+ let dir = root;
2626
+ try {
2627
+ for (const name of key.slice(1).split("/")) {
2628
+ const match = readdirSync(dir).find((entry) => entry === name || sanitizeAssetName(entry) === name);
2629
+ if (!match)
2630
+ return null;
2631
+ dir = join4(dir, match);
2632
+ }
2633
+ } catch {
2634
+ return null;
2635
+ }
2636
+ return dir;
2637
+ };
2578
2638
  var scanProject = (root, manifest) => scanLocal(root, { ignore: loadIgnore(root), tracked: new Set(Object.keys(manifest.files)) });
2579
2639
  var diffLocal = (manifest, scan) => {
2580
2640
  const added = [];
@@ -2698,12 +2758,12 @@ var collectLocalEntries = (root, manifest, assetBase) => {
2698
2758
  };
2699
2759
 
2700
2760
  // src/compile/projectCompile.ts
2701
- import { existsSync as existsSync6 } from "node:fs";
2761
+ import { existsSync as existsSync7 } from "node:fs";
2702
2762
  import { createRequire as createRequire3 } from "node:module";
2703
2763
  import { dirname as dirname5, join as join9 } from "node:path";
2704
2764
 
2705
2765
  // src/distRoot.ts
2706
- import { existsSync as existsSync3 } from "node:fs";
2766
+ import { existsSync as existsSync4 } from "node:fs";
2707
2767
  import { dirname as dirname3, join as join6 } from "node:path";
2708
2768
  import { fileURLToPath } from "node:url";
2709
2769
  var distRoot = (() => {
@@ -2712,7 +2772,7 @@ var distRoot = (() => {
2712
2772
  dirname3(process.execPath)
2713
2773
  ];
2714
2774
  for (const c2 of candidates)
2715
- if (existsSync3(join6(c2, "runtime")))
2775
+ if (existsSync4(join6(c2, "runtime")))
2716
2776
  return c2;
2717
2777
  return candidates[0];
2718
2778
  })();
@@ -2720,7 +2780,7 @@ var distRoot = (() => {
2720
2780
  // src/compile/projectCompile.ts
2721
2781
  init_compile();
2722
2782
  var vendoredInject = join9(distRoot, "runtime", "sdk", "inject.ts");
2723
- if (existsSync6(vendoredInject))
2783
+ if (existsSync7(vendoredInject))
2724
2784
  process.env.LECODES_SDK_INJECT = vendoredInject;
2725
2785
  var resolveChiselBin2 = () => {
2726
2786
  if (process.env.LECODES_CHISEL_BIN)
@@ -2731,7 +2791,7 @@ var resolveChiselBin2 = () => {
2731
2791
  return fromPkg;
2732
2792
  } catch {}
2733
2793
  const local = join9(dirname5(process.execPath), process.platform === "win32" ? "chisel.exe" : "chisel");
2734
- return existsSync6(local) ? local : null;
2794
+ return existsSync7(local) ? local : null;
2735
2795
  };
2736
2796
  var chiselBin = resolveChiselBin2();
2737
2797
  if (chiselBin)
@@ -2759,7 +2819,7 @@ var compileProject2 = (opts) => compileProject({
2759
2819
  // src/compile/fonts.ts
2760
2820
  init_compile();
2761
2821
  import { createHash as createHash2 } from "node:crypto";
2762
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "node:fs";
2822
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "node:fs";
2763
2823
  import { dirname as dirname6, join as join10 } from "node:path";
2764
2824
  var applyProjectFonts = async (entries, opts) => {
2765
2825
  const registry = (opts.registry ?? fontRegistryBase()).replace(/\/+$/, "");
@@ -2781,7 +2841,7 @@ var applyProjectFonts = async (entries, opts) => {
2781
2841
  const rel = `assets/fonts/${face.fileName}`;
2782
2842
  const abs = join10(vendorDir, rel);
2783
2843
  const entryPath = `${opts.vendorPrefix ?? ""}/${rel}`;
2784
- if (!existsSync7(abs))
2844
+ if (!existsSync8(abs))
2785
2845
  await downloadFace(registry, vendorDir, face, rel, abs);
2786
2846
  if (!byPath().has(entryPath.replace(/^\//, ""))) {
2787
2847
  entries.push(opts.makeResourceEntry?.(entryPath, abs) ?? { path: entryPath, type: "resource", absPath: abs });
@@ -3733,7 +3793,7 @@ var FIRST_PARTY_PLUGINS = {
3733
3793
  "qr-scanner": "letary/lecodes-plugins"
3734
3794
  };
3735
3795
  var readJson = (path) => {
3736
- if (!existsSync8(path))
3796
+ if (!existsSync9(path))
3737
3797
  return null;
3738
3798
  try {
3739
3799
  return JSON.parse(readFileSync10(path, "utf8"));
@@ -3773,7 +3833,7 @@ var fetchLatestSdkVersion = async () => {
3773
3833
  };
3774
3834
  var shellSdkVersion = (iosDir) => {
3775
3835
  const path = join11(iosDir, "LeCodesRuntime", "Package.swift");
3776
- if (!existsSync8(path))
3836
+ if (!existsSync9(path))
3777
3837
  return null;
3778
3838
  const m = /lecodes-ios-sdk\.git",\s*(?:exact|from):\s*"([^"]+)"/.exec(readFileSync10(path, "utf8"));
3779
3839
  return m && VERSION_RE.test(m[1]) ? m[1] : null;
@@ -3804,7 +3864,7 @@ var ensureRepo = (repo, wantTag, lock) => {
3804
3864
  }
3805
3865
  const cache = join11(homedir2(), ".lecodes", "plugins", repo.replace("/", "__"));
3806
3866
  const url = `https://github.com/${repo}.git`;
3807
- if (!existsSync8(join11(cache, ".git"))) {
3867
+ if (!existsSync9(join11(cache, ".git"))) {
3808
3868
  mkdirSync5(dirname7(cache), { recursive: true });
3809
3869
  note(`Cloning ${url}…`);
3810
3870
  git(dirname7(cache), "clone", "--quiet", url, cache);
@@ -3877,7 +3937,7 @@ var appInit = async (args) => {
3877
3937
  }
3878
3938
  const root = projectRoot();
3879
3939
  const iosDir = join11(root, "ios");
3880
- if (existsSync8(iosDir)) {
3940
+ if (existsSync9(iosDir)) {
3881
3941
  throw new CliError("ios/ already exists — run `lecodes app sync` to update it.");
3882
3942
  }
3883
3943
  const existing = readJson(join11(root, "app.json")) ?? {};
@@ -3905,7 +3965,7 @@ var appInit = async (args) => {
3905
3965
  writeJson(join11(root, "app.json"), config);
3906
3966
  log(`${c.green("written")} app.json`);
3907
3967
  const ignorePath = join11(root, ".lecodesignore");
3908
- const ignore = existsSync8(ignorePath) ? readFileSync10(ignorePath, "utf8") : DEFAULT_IGNORE_FILE;
3968
+ const ignore = existsSync9(ignorePath) ? readFileSync10(ignorePath, "utf8") : DEFAULT_IGNORE_FILE;
3909
3969
  if (!/^ios\/$/m.test(ignore)) {
3910
3970
  writeFileSync7(ignorePath, ignore.replace(/\n*$/, `
3911
3971
  `) + `
@@ -3982,7 +4042,7 @@ var ensureSdkResourcesBundle = async (sdkVersion) => {
3982
4042
  }
3983
4043
  const dir = join11(homedir2(), ".lecodes", "sdk-resources", sdkVersion);
3984
4044
  const bundle = join11(dir, "LeCodesSDKResources.bundle");
3985
- if (existsSync8(bundle))
4045
+ if (existsSync9(bundle))
3986
4046
  return bundle;
3987
4047
  const url = `https://github.com/${SDK_REPO}/releases/download/${sdkVersion}/LeCodesSDKResources.bundle.zip`;
3988
4048
  note(`Downloading LeCodesSDKResources.bundle (SDK ${sdkVersion})…`);
@@ -3994,7 +4054,7 @@ var ensureSdkResourcesBundle = async (sdkVersion) => {
3994
4054
  writeFileSync7(zip, new Uint8Array(await res.arrayBuffer()));
3995
4055
  execFileSync("ditto", ["-x", "-k", zip, dir]);
3996
4056
  rmSync(zip);
3997
- if (!existsSync8(bundle))
4057
+ if (!existsSync9(bundle))
3998
4058
  throw new CliError(`No LeCodesSDKResources.bundle inside the release asset ${url}`);
3999
4059
  return bundle;
4000
4060
  };
@@ -4021,7 +4081,7 @@ var appSync = async (_args) => {
4021
4081
  const root = projectRoot();
4022
4082
  const config = readJson(join11(root, "app.json"));
4023
4083
  const iosDir = join11(root, "ios");
4024
- if (!config?.ios || !existsSync8(iosDir)) {
4084
+ if (!config?.ios || !existsSync9(iosDir)) {
4025
4085
  throw new CliError("No iOS shell here — run `lecodes app init ios` first.");
4026
4086
  }
4027
4087
  const requestedVariant = config.ios.variant ?? "auto";
@@ -4046,7 +4106,7 @@ var appSync = async (_args) => {
4046
4106
  if (!module || !register)
4047
4107
  throw new CliError(`Plugin "${id}": manifest ios.module / ios.register missing`);
4048
4108
  const src = join11(plugin.dir, ios.sources ?? "ios");
4049
- if (!existsSync8(src))
4109
+ if (!existsSync9(src))
4050
4110
  throw new CliError(`Plugin "${id}": sources dir ${src} missing`);
4051
4111
  const dest = join11(sourcesDir, module);
4052
4112
  rmSync(dest, { recursive: true, force: true });
@@ -4062,7 +4122,7 @@ var appSync = async (_args) => {
4062
4122
  log(`${c.green("plugin")} ${id}@${plugin.lock.version} (${plugin.lock.tag} → Sources/${module})`);
4063
4123
  }
4064
4124
  const keep = new Set(["LeCodesRuntime", ...runtimePlugins.map((p) => p.module)]);
4065
- for (const entry of existsSync8(sourcesDir) ? readdirSync4(sourcesDir) : []) {
4125
+ for (const entry of existsSync9(sourcesDir) ? readdirSync4(sourcesDir) : []) {
4066
4126
  if (!keep.has(entry) && statSync2(join11(sourcesDir, entry)).isDirectory()) {
4067
4127
  rmSync(join11(sourcesDir, entry), { recursive: true });
4068
4128
  log(`${c.yellow("removed")} ios/LeCodesRuntime/Sources/${entry} (plugin uninstalled)`);
@@ -4136,7 +4196,7 @@ var appSync = async (_args) => {
4136
4196
  writeFileSync7(join11(iosDir, "App", "Info.plist"), infoPlistTemplate(mergedKeys, updateUrl));
4137
4197
  if (updateUrl)
4138
4198
  log(`${c.green("update")} OTA source ${updateUrl} — checked natively at launch, new code applies next launch`);
4139
- if (pluginSpecs.length > 0 || existsSync8(lockPath))
4199
+ if (pluginSpecs.length > 0 || existsSync9(lockPath))
4140
4200
  writeJson(lockPath, { plugins: newLock });
4141
4201
  const pbxPath = join11(iosDir, "App.xcodeproj", "project.pbxproj");
4142
4202
  const pbx = readFileSync10(pbxPath, "utf8").replace(/MARKETING_VERSION = [^;]+;/g, `MARKETING_VERSION = ${config.version ?? "1.0.0"};`).replace(/CURRENT_PROJECT_VERSION = [^;]+;/g, `CURRENT_PROJECT_VERSION = ${config.ios.buildNumber ?? 1};`);
@@ -4226,7 +4286,7 @@ var exportOptionsPlist = (method, teamId, destination) => `<?xml version="1.0" e
4226
4286
  var requireShell = () => {
4227
4287
  const root = projectRoot();
4228
4288
  const config = readJson(join11(root, "app.json"));
4229
- if (!config?.ios || !existsSync8(join11(root, "ios"))) {
4289
+ if (!config?.ios || !existsSync9(join11(root, "ios"))) {
4230
4290
  throw new CliError("No iOS shell here — run `lecodes app init ios` first.");
4231
4291
  }
4232
4292
  return { root, config };
@@ -4302,7 +4362,7 @@ var appUpdate = async (args) => {
4302
4362
  log(`${c.green("plugin")} ${id} ${entry?.tag ?? "—"} → ${c.bold(tag)}`);
4303
4363
  changed = true;
4304
4364
  }
4305
- if (!checkOnly && existsSync8(lockPath))
4365
+ if (!checkOnly && existsSync9(lockPath))
4306
4366
  writeJson(lockPath, { plugins: kept });
4307
4367
  }
4308
4368
  log("");
@@ -4336,18 +4396,18 @@ var appDoctor = async (_args) => {
4336
4396
  check(xcodeVersion !== "", `Xcode CLI (${xcodeVersion || "not found"})`, "install Xcode + command line tools");
4337
4397
  check(!!config.ios.teamId, "Development team set", "lecodes app init wrote app.json — fill ios.teamId", false);
4338
4398
  const iconDir = join11(root, "ios", "App", "Assets.xcassets", "AppIcon.appiconset");
4339
- const hasIcon = existsSync8(iconDir) && readdirSync4(iconDir).some((f2) => f2.endsWith(".png"));
4399
+ const hasIcon = existsSync9(iconDir) && readdirSync4(iconDir).some((f2) => f2.endsWith(".png"));
4340
4400
  check(hasIcon, "App icon present", "drop a 1024×1024 PNG into ios/App/Assets.xcassets/AppIcon.appiconset/ (App Store requires it)", false);
4341
4401
  const bundle = join11(root, "ios", "App", "Resources", "app.js");
4342
- check(existsSync8(bundle), "Bundled app.js", "run `lecodes app sync`");
4402
+ check(existsSync9(bundle), "Bundled app.js", "run `lecodes app sync`");
4343
4403
  const requestedVariant = config.ios.variant ?? "auto";
4344
4404
  const validVariant = requestedVariant === "auto" || SDK_VARIANTS.includes(requestedVariant);
4345
4405
  check(validVariant, `ios.variant "${requestedVariant}"`, `use "auto" or one of ${SDK_VARIANTS.join(", ")}`);
4346
- if (existsSync8(bundle) && validVariant) {
4406
+ if (existsSync9(bundle) && validVariant) {
4347
4407
  const { variant } = resolveVariant(requestedVariant, readFileSync10(bundle, "utf8"));
4348
4408
  const pkg = readFileSync10(join11(root, "ios", "LeCodesRuntime", "Package.swift"), "utf8");
4349
4409
  const needsResources = variant === "3d" || variant === "full";
4350
- const hasResources = existsSync8(join11(root, "ios", "App", "Resources", "LeCodesSDKResources.bundle"));
4410
+ const hasResources = existsSync9(join11(root, "ios", "App", "Resources", "LeCodesSDKResources.bundle"));
4351
4411
  check(pkg.includes(`"LeCodesSDK-${variant}"`) && (!needsResources || hasResources), `SDK variant ${variant}${needsResources ? " (+ filament resources)" : ""}`, "run `lecodes app sync`");
4352
4412
  }
4353
4413
  const linked = shellSdkVersion(join11(root, "ios"));
@@ -4419,7 +4479,7 @@ var appBuild = async (args) => {
4419
4479
  };
4420
4480
  var appUpload = async (args) => {
4421
4481
  const { root, config } = requireShell();
4422
- if (!existsSync8(join11(root, ARCHIVE_PATH))) {
4482
+ if (!existsSync9(join11(root, ARCHIVE_PATH))) {
4423
4483
  throw new CliError("No archive at ios/build/App.xcarchive — run `lecodes app build ios` first.");
4424
4484
  }
4425
4485
  const ascPath = join11(homedir2(), ".lecodes", "asc.json");
@@ -4431,7 +4491,7 @@ var appUpload = async (args) => {
4431
4491
  throw new CliError(`App Store Connect API key needed: --key-path <AuthKey.p8> --key-id <id> --issuer-id <id>
4432
4492
  (create one at appstoreconnect.apple.com → Users and Access → Integrations; stored in ~/.lecodes/asc.json after first use)`);
4433
4493
  }
4434
- if (!existsSync8(keyPath))
4494
+ if (!existsSync9(keyPath))
4435
4495
  throw new CliError(`API key file not found: ${keyPath}`);
4436
4496
  if (flagStr(args, "key-path")) {
4437
4497
  mkdirSync5(dirname7(ascPath), { recursive: true });
@@ -4469,7 +4529,7 @@ var appOpen = async (args) => {
4469
4529
  const platform = args._[1] ?? "ios";
4470
4530
  if (platform === "ios") {
4471
4531
  const proj = join11(root, "ios", "App.xcodeproj");
4472
- if (!existsSync8(proj))
4532
+ if (!existsSync9(proj))
4473
4533
  throw new CliError("No iOS shell here — run `lecodes app init ios` first.");
4474
4534
  if (process.platform !== "darwin")
4475
4535
  throw new CliError("Opening Xcode requires macOS.");
@@ -4504,12 +4564,12 @@ var app = async (args) => {
4504
4564
  };
4505
4565
 
4506
4566
  // src/commands/clone.ts
4507
- import { existsSync as existsSync10, mkdirSync as mkdirSync7, readdirSync as readdirSync6 } from "node:fs";
4567
+ import { existsSync as existsSync11, mkdirSync as mkdirSync7, readdirSync as readdirSync6 } from "node:fs";
4508
4568
  import { resolve as resolve3 } from "node:path";
4509
4569
 
4510
4570
  // src/types.ts
4511
4571
  import { dirname as dirname8, join as join12, relative as relative3, sep } from "node:path";
4512
- import { existsSync as existsSync9, mkdirSync as mkdirSync6, readdirSync as readdirSync5, readFileSync as readFileSync11, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync8 } from "node:fs";
4572
+ import { existsSync as existsSync10, mkdirSync as mkdirSync6, readdirSync as readdirSync5, readFileSync as readFileSync11, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync8 } from "node:fs";
4513
4573
  var TSCONFIG = {
4514
4574
  compilerOptions: {
4515
4575
  target: "ES2020",
@@ -4527,7 +4587,7 @@ var TSCONFIG = {
4527
4587
  exclude: ["node_modules"]
4528
4588
  };
4529
4589
  var listBundleFiles = (dir, base = dir) => {
4530
- if (!existsSync9(dir))
4590
+ if (!existsSync10(dir))
4531
4591
  return [];
4532
4592
  const out = [];
4533
4593
  for (const name of readdirSync5(dir)) {
@@ -4540,7 +4600,7 @@ var listBundleFiles = (dir, base = dir) => {
4540
4600
  return out;
4541
4601
  };
4542
4602
  var pruneEmptyDirs = (dir, root) => {
4543
- if (!existsSync9(dir) || !statSync3(dir).isDirectory())
4603
+ if (!existsSync10(dir) || !statSync3(dir).isDirectory())
4544
4604
  return;
4545
4605
  for (const name of readdirSync5(dir))
4546
4606
  pruneEmptyDirs(join12(dir, name), root);
@@ -4586,7 +4646,7 @@ var readLocalTypeBundle = () => {
4586
4646
  join12(distRoot, "..", "sdk", "dist", "types.json")
4587
4647
  ];
4588
4648
  for (const path of candidates) {
4589
- if (existsSync9(path)) {
4649
+ if (existsSync10(path)) {
4590
4650
  try {
4591
4651
  return JSON.parse(readFileSync11(path, "utf8"));
4592
4652
  } catch {}
@@ -4624,7 +4684,7 @@ var clone = async (args) => {
4624
4684
  const project = await getProject(apiUrl, token, uuid);
4625
4685
  const dir = args._[1] ?? slug(project.name);
4626
4686
  const root = resolve3(dir);
4627
- if (existsSync10(root) && readdirSync6(root).length > 0) {
4687
+ if (existsSync11(root) && readdirSync6(root).length > 0) {
4628
4688
  throw new CliError(`Directory "${dir}" already exists and is not empty.`);
4629
4689
  }
4630
4690
  mkdirSync7(root, { recursive: true });
@@ -4644,7 +4704,7 @@ var clone = async (args) => {
4644
4704
  };
4645
4705
 
4646
4706
  // src/commands/create.ts
4647
- import { existsSync as existsSync11, mkdirSync as mkdirSync8, readdirSync as readdirSync7, writeFileSync as writeFileSync9 } from "node:fs";
4707
+ import { existsSync as existsSync12, mkdirSync as mkdirSync8, readdirSync as readdirSync7, writeFileSync as writeFileSync9 } from "node:fs";
4648
4708
  import { join as join13, resolve as resolve4 } from "node:path";
4649
4709
 
4650
4710
  // src/commands/projectTemplates.ts
@@ -4731,7 +4791,7 @@ var create = async (args) => {
4731
4791
  throw new CliError("Usage: lecodes create <name> [directory]");
4732
4792
  const dir = args._[1] ?? slug2(name);
4733
4793
  const root = resolve4(dir);
4734
- if (existsSync11(root) && readdirSync7(root).length > 0) {
4794
+ if (existsSync12(root) && readdirSync7(root).length > 0) {
4735
4795
  throw new CliError(`Directory "${dir}" already exists and is not empty.`);
4736
4796
  }
4737
4797
  mkdirSync8(root, { recursive: true });
@@ -4750,7 +4810,7 @@ var create = async (args) => {
4750
4810
  };
4751
4811
 
4752
4812
  // src/commands/design.ts
4753
- import { existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync14, readdirSync as readdirSync8, writeFileSync as writeFileSync11, appendFileSync } from "node:fs";
4813
+ import { existsSync as existsSync14, mkdirSync as mkdirSync10, readFileSync as readFileSync14, readdirSync as readdirSync8, writeFileSync as writeFileSync11, appendFileSync } from "node:fs";
4754
4814
  import { basename as basename6, join as join15 } from "node:path";
4755
4815
 
4756
4816
  // src/compile/designCompile.ts
@@ -4759,7 +4819,7 @@ import { basename as basename5 } from "node:path";
4759
4819
 
4760
4820
  // src/compile/assetIcons.ts
4761
4821
  import { createHash as createHash3 } from "node:crypto";
4762
- import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "node:fs";
4822
+ import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "node:fs";
4763
4823
  import { dirname as dirname9, join as join14 } from "node:path";
4764
4824
  var DEFAULT_REGISTRY = process.env.LECODES_ICON_REGISTRY?.trim() || "https://icon-registry.jt3.ru/v1";
4765
4825
  var ICON_ID = /^([a-z0-9]+(?:-[a-z0-9]+)*):([a-z0-9]+(?:-[a-z0-9]+)*)$/;
@@ -5056,7 +5116,7 @@ var renderNotices2 = (lock) => {
5056
5116
  `);
5057
5117
  };
5058
5118
  var writeIfChanged2 = (path, text) => {
5059
- const current = existsSync12(path) ? safeRead(path) : null;
5119
+ const current = existsSync13(path) ? safeRead(path) : null;
5060
5120
  if (current === text)
5061
5121
  return false;
5062
5122
  writeFileSync10(path, text);
@@ -5735,7 +5795,7 @@ var getContext = (args) => {
5735
5795
  };
5736
5796
  var listScreens = (designDir) => {
5737
5797
  const dir = join15(designDir, "screens");
5738
- if (!existsSync13(dir))
5798
+ if (!existsSync14(dir))
5739
5799
  return [];
5740
5800
  return readdirSync8(dir).filter((f2) => f2.endsWith(".ts") && !f2.startsWith(".")).map((f2) => f2.slice(0, -3)).sort();
5741
5801
  };
@@ -5751,7 +5811,7 @@ var writeMcpConfig = (ctx, port) => {
5751
5811
  const path = join15(ctx.root, ".mcp.json");
5752
5812
  const entry = { type: "http", url: `http://localhost:${port}/mcp` };
5753
5813
  let config = {};
5754
- if (existsSync13(path)) {
5814
+ if (existsSync14(path)) {
5755
5815
  try {
5756
5816
  config = JSON.parse(readFileSync14(path, "utf8"));
5757
5817
  } catch {
@@ -5785,7 +5845,7 @@ var init = async (ctx, args) => {
5785
5845
  let created = 0;
5786
5846
  for (const [rel, content] of files) {
5787
5847
  const abs = join15(ctx.designDir, rel);
5788
- if (existsSync13(abs)) {
5848
+ if (existsSync14(abs)) {
5789
5849
  note(`kept ${ctx.dirName}/${rel.replace(/\\/g, "/")}`);
5790
5850
  continue;
5791
5851
  }
@@ -5795,7 +5855,7 @@ var init = async (ctx, args) => {
5795
5855
  }
5796
5856
  const ignorePath = join15(ctx.root, ".lecodesignore");
5797
5857
  const ignoreLine = `${ctx.dirName}/.snapshots/`;
5798
- const current = existsSync13(ignorePath) ? readFileSync14(ignorePath, "utf8") : "";
5858
+ const current = existsSync14(ignorePath) ? readFileSync14(ignorePath, "utf8") : "";
5799
5859
  if (!current.split(/\r?\n/).includes(ignoreLine)) {
5800
5860
  appendFileSync(ignorePath, (current && !current.endsWith(`
5801
5861
  `) ? `
@@ -5917,7 +5977,7 @@ var snapshot = async (ctx, args) => {
5917
5977
  }
5918
5978
  };
5919
5979
  var serve = async (ctx, args) => {
5920
- if (!existsSync13(ctx.designDir)) {
5980
+ if (!existsSync14(ctx.designDir)) {
5921
5981
  throw new CliError(`No ${ctx.dirName}/ folder here. Run "lecodes design init" to scaffold it.`);
5922
5982
  }
5923
5983
  let designServer;
@@ -5990,7 +6050,7 @@ var serve = async (ctx, args) => {
5990
6050
  openBrowser(server.url);
5991
6051
  };
5992
6052
  var arrange = async (ctx, args) => {
5993
- if (!existsSync13(ctx.designDir)) {
6053
+ if (!existsSync14(ctx.designDir)) {
5994
6054
  throw new CliError(`No ${ctx.dirName}/ folder here. Run "lecodes design init" to scaffold it.`);
5995
6055
  }
5996
6056
  let designServer;
@@ -6026,7 +6086,7 @@ var share = async (ctx, args) => {
6026
6086
  note("Turn it off with: lecodes design share --off");
6027
6087
  };
6028
6088
  var check = async (ctx, args) => {
6029
- if (!existsSync13(ctx.designDir)) {
6089
+ if (!existsSync14(ctx.designDir)) {
6030
6090
  throw new CliError(`No ${ctx.dirName}/ folder here. Run "lecodes design init" to scaffold it.`);
6031
6091
  }
6032
6092
  let designServer;
@@ -6102,12 +6162,11 @@ var design = async (args) => {
6102
6162
  import { existsSync as existsSync15, mkdirSync as mkdirSync11, readFileSync as readFileSync15, watch, writeFileSync as writeFileSync12 } from "node:fs";
6103
6163
  import { randomBytes as randomBytes2 } from "node:crypto";
6104
6164
  import { networkInterfaces } from "node:os";
6105
- import { basename as basename7, join as join17 } from "node:path";
6165
+ import { basename as basename7, join as join16 } from "node:path";
6106
6166
 
6107
6167
  // src/dev/devServer.ts
6108
6168
  import { createServer as createServer2 } from "node:http";
6109
- import { createReadStream, existsSync as existsSync14, statSync as statSync4 } from "node:fs";
6110
- import { join as join16 } from "node:path";
6169
+ import { createReadStream, statSync as statSync4 } from "node:fs";
6111
6170
 
6112
6171
  // src/dev/clientTemplates.ts
6113
6172
  var DEV_SCREEN_TS = `const title = "LeCodes Dev"
@@ -6476,8 +6535,8 @@ var startDevServer = async (options) => {
6476
6535
  } catch {
6477
6536
  return sendText(res, 400, "text/plain", "Bad path");
6478
6537
  }
6479
- const abs = join16(root, key.slice(1));
6480
- if (!existsSync14(abs) || !statSync4(abs).isFile())
6538
+ const abs = resolveLocalPath(root, key);
6539
+ if (!abs || !statSync4(abs).isFile())
6481
6540
  return sendText(res, 404, "text/plain", "Not found");
6482
6541
  res.writeHead(200, { "content-type": contentType(key), "cache-control": "no-store" });
6483
6542
  createReadStream(abs).pipe(res);
@@ -6560,14 +6619,14 @@ var pickLanAddress = () => {
6560
6619
  return null;
6561
6620
  };
6562
6621
  var loadDevToken = (root) => {
6563
- const path = join17(root, LECODES_DIR, "dev.json");
6622
+ const path = join16(root, LECODES_DIR, "dev.json");
6564
6623
  try {
6565
6624
  const token2 = JSON.parse(readFileSync15(path, "utf8")).token;
6566
6625
  if (token2 && /^[a-z0-9]{8,}$/.test(token2))
6567
6626
  return token2;
6568
6627
  } catch {}
6569
6628
  const token = randomBytes2(6).toString("hex");
6570
- mkdirSync11(join17(root, LECODES_DIR), { recursive: true });
6629
+ mkdirSync11(join16(root, LECODES_DIR), { recursive: true });
6571
6630
  writeFileSync12(path, JSON.stringify({ token }, null, 2) + `
6572
6631
  `);
6573
6632
  return token;
@@ -6680,7 +6739,7 @@ ${result.error}`);
6680
6739
  await printQr(server.url);
6681
6740
  log(`Scan the QR with the LeCodes app, or open: ${c.bold(server.url)}`);
6682
6741
  note(`Older desktop builds (no websocket bridge): ${server.url}?t=poll`);
6683
- if (existsSync15(join17(root, LECODES_DIR, "manifest.json")))
6742
+ if (existsSync15(join16(root, LECODES_DIR, "manifest.json")))
6684
6743
  note(`Token stored in ${LECODES_DIR}/dev.json (delete it to rotate the URL).`);
6685
6744
  log("");
6686
6745
  log(`Watching ${root} — save a file to hot-reload. Ctrl+C to stop.`);
@@ -6876,7 +6935,7 @@ var statusLabel = (e) => e.status === "added" ? c.green(" added ") : e.statu
6876
6935
 
6877
6936
  // src/commands/init.ts
6878
6937
  import { existsSync as existsSync16, mkdirSync as mkdirSync12, writeFileSync as writeFileSync13 } from "node:fs";
6879
- import { basename as basename9, dirname as dirname10, join as join18, resolve as resolve5 } from "node:path";
6938
+ import { basename as basename9, dirname as dirname10, join as join17, resolve as resolve5 } from "node:path";
6880
6939
  var STARTER2 = `// Welcome to LeCodes — the whole SDK is available as globals, no imports needed.
6881
6940
  // Run it live on a device with \`lecodes dev\`, or bundle it with \`lecodes compile -o app.js\`.
6882
6941
 
@@ -6978,7 +7037,7 @@ var init2 = async (args) => {
6978
7037
  mkdirSync12(root, { recursive: true });
6979
7038
  let created = 0;
6980
7039
  const write = (rel, content) => {
6981
- const abs = join18(root, rel);
7040
+ const abs = join17(root, rel);
6982
7041
  if (existsSync16(abs)) {
6983
7042
  note(`kept ${rel}`);
6984
7043
  return;
@@ -7009,7 +7068,7 @@ var init2 = async (args) => {
7009
7068
 
7010
7069
  // src/commands/link.ts
7011
7070
  import { existsSync as existsSync17 } from "node:fs";
7012
- import { basename as basename11, join as join19, resolve as resolve6 } from "node:path";
7071
+ import { basename as basename11, join as join18, resolve as resolve6 } from "node:path";
7013
7072
 
7014
7073
  // src/commands/push.ts
7015
7074
  import { basename as basename10 } from "node:path";
@@ -7172,7 +7231,7 @@ var link = async (args) => {
7172
7231
  const root = dirArg ? resolve6(dirArg) : process.cwd();
7173
7232
  if (!existsSync17(root))
7174
7233
  throw new CliError(`No such directory: ${root}`);
7175
- if (existsSync17(join19(root, LECODES_DIR, "manifest.json")) && !flagBool(args, "force")) {
7234
+ if (existsSync17(join18(root, LECODES_DIR, "manifest.json")) && !flagBool(args, "force")) {
7176
7235
  throw new CliError("This folder is already linked (.lecodes/manifest.json exists) — use `lecodes push`, or `lecodes link --force` to re-link it to another project.");
7177
7236
  }
7178
7237
  let enclosing = null;
@@ -7489,7 +7548,7 @@ var render = async (args) => {
7489
7548
 
7490
7549
  // src/commands/pull.ts
7491
7550
  import { existsSync as existsSync18, mkdirSync as mkdirSync14, rmSync as rmSync3, writeFileSync as writeFileSync15 } from "node:fs";
7492
- import { dirname as dirname12, join as join20 } from "node:path";
7551
+ import { dirname as dirname12, join as join19 } from "node:path";
7493
7552
  var pull = async (args) => {
7494
7553
  const root = findProjectRoot(process.cwd());
7495
7554
  const manifest = readManifest(root);
@@ -7509,7 +7568,7 @@ var pull = async (args) => {
7509
7568
  const remotePaths = new Set([...buildPaths(project.assets).entries()].filter(([id]) => project.assets.find((a) => a.id === id)?.type !== "folder").map(([, path]) => path));
7510
7569
  for (const path of Object.keys(manifest.files)) {
7511
7570
  if (!remotePaths.has(path)) {
7512
- const abs = join20(root, ...path.replace(/^\//, "").split("/"));
7571
+ const abs = join19(root, ...path.replace(/^\//, "").split("/"));
7513
7572
  if (existsSync18(abs))
7514
7573
  rmSync3(abs);
7515
7574
  }
@@ -7548,7 +7607,7 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
7548
7607
  const files = { ...manifest.files };
7549
7608
  for (const asset of targets) {
7550
7609
  const key = pathById.get(asset.id);
7551
- const abs = join20(root, ...key.replace(/^\//, "").split("/"));
7610
+ const abs = join19(root, ...key.replace(/^\//, "").split("/"));
7552
7611
  mkdirSync14(dirname12(abs), { recursive: true });
7553
7612
  let sha;
7554
7613
  if (asset.type === "file") {
@@ -7572,19 +7631,19 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
7572
7631
 
7573
7632
  // src/commands/scene.ts
7574
7633
  import { existsSync as existsSync20, readFileSync as readFileSync20, readdirSync as readdirSync9, statSync as statSync5, writeFileSync as writeFileSync16 } from "node:fs";
7575
- import { basename as basename12, dirname as dirname13, join as join22, resolve as resolve8 } from "node:path";
7634
+ import { basename as basename12, dirname as dirname13, join as join21, resolve as resolve8 } from "node:path";
7576
7635
 
7577
7636
  // src/compile/sceneCompile.ts
7578
7637
  import { existsSync as existsSync19, readFileSync as readFileSync19 } from "node:fs";
7579
- import { join as join21 } from "node:path";
7638
+ import { join as join20 } from "node:path";
7580
7639
  var TEXT_EXT = /\.(ts|js|json|svg|txt|md)$/i;
7581
7640
  var rel = (key) => key.replace(/^\//, "");
7582
7641
  var resolveHarness = (loadSceneHarness2) => {
7583
- const inRepo = join21(distRoot, "..", "projects", "src", "scene-editor");
7642
+ const inRepo = join20(distRoot, "..", "projects", "src", "scene-editor");
7584
7643
  try {
7585
7644
  return loadSceneHarness2(existsSync19(inRepo) ? inRepo : undefined);
7586
7645
  } catch {
7587
- const vendored = join21(distRoot, "runtime", "scene-harness.json");
7646
+ const vendored = join20(distRoot, "runtime", "scene-harness.json");
7588
7647
  if (existsSync19(vendored))
7589
7648
  return JSON.parse(readFileSync19(vendored, "utf8"));
7590
7649
  throw new CliError("scene-editor harness sources not found — reinstall the CLI or set LECODES_SCENE_HARNESS_DIR.");
@@ -7650,7 +7709,7 @@ var getContext2 = (args) => {
7650
7709
  return { root, manifest, name: manifest?.name ?? basename12(root) };
7651
7710
  };
7652
7711
  var writeMcpConfig2 = (root, port) => {
7653
- const path = join22(root, ".mcp.json");
7712
+ const path = join21(root, ".mcp.json");
7654
7713
  const entry = { type: "http", url: `http://localhost:${port}/mcp` };
7655
7714
  let config = {};
7656
7715
  if (existsSync20(path)) {
@@ -7677,7 +7736,7 @@ var countScenes = (dir) => {
7677
7736
  for (const name of readdirSync9(dir)) {
7678
7737
  if (name === "node_modules" || name.startsWith("."))
7679
7738
  continue;
7680
- const full = join22(dir, name);
7739
+ const full = join21(dir, name);
7681
7740
  if (statSync5(full).isDirectory())
7682
7741
  n += countScenes(full);
7683
7742
  else if (name.endsWith(".scene.ts"))
@@ -8044,13 +8103,13 @@ var types = async (args) => {
8044
8103
  // src/index.ts
8045
8104
  import { existsSync as existsSync23, readFileSync as readFileSync23, realpathSync as realpathSync2 } from "node:fs";
8046
8105
  import { createRequire as createRequire4 } from "node:module";
8047
- import { delimiter as delimiter2, dirname as dirname16, join as join24 } from "node:path";
8106
+ import { delimiter as delimiter2, dirname as dirname16, join as join23 } from "node:path";
8048
8107
 
8049
8108
  // src/commands/install.ts
8050
8109
  import { execFileSync as execFileSync2 } from "node:child_process";
8051
8110
  import { appendFileSync as appendFileSync2, chmodSync, cpSync as cpSync2, existsSync as existsSync22, mkdirSync as mkdirSync15, readFileSync as readFileSync22, realpathSync, rmSync as rmSync4, symlinkSync } from "node:fs";
8052
8111
  import { homedir as homedir3 } from "node:os";
8053
- import { basename as basename13, delimiter, dirname as dirname15, join as join23, resolve as resolve11, sep as sep2 } from "node:path";
8112
+ import { basename as basename13, delimiter, dirname as dirname15, join as join22, resolve as resolve11, sep as sep2 } from "node:path";
8054
8113
  var isWin = process.platform === "win32";
8055
8114
  var HOME = homedir3();
8056
8115
  var safeRealpath = (p) => {
@@ -8076,10 +8135,10 @@ var install = async (args) => {
8076
8135
  const bundle = dirname15(process.execPath);
8077
8136
  const exeName = basename13(process.execPath);
8078
8137
  const chiselName = isWin ? "chisel.exe" : "chisel";
8079
- if (!existsSync22(join23(bundle, "runtime")) || !existsSync22(join23(bundle, chiselName))) {
8138
+ if (!existsSync22(join22(bundle, "runtime")) || !existsSync22(join22(bundle, chiselName))) {
8080
8139
  throw new CliError("`lecodes install` only works from the packaged release build (the archive containing lecodes + " + "chisel + runtime/). You appear to be running from source or an npm install — install globally " + "with your package manager there instead.");
8081
8140
  }
8082
- const companions = [exeName, chiselName, "runtime", "README.txt"].filter((f2) => existsSync22(join23(bundle, f2)));
8141
+ const companions = [exeName, chiselName, "runtime", "README.txt"].filter((f2) => existsSync22(join22(bundle, f2)));
8083
8142
  if (isWin)
8084
8143
  return installWindows(args, bundle, companions);
8085
8144
  return installUnix(args, bundle, companions);
@@ -8089,9 +8148,9 @@ var copyBundle = (bundle, target, companions) => {
8089
8148
  if (safeRealpath(target) === safeRealpath(bundle))
8090
8149
  return;
8091
8150
  for (const f2 of companions) {
8092
- const dest = join23(target, f2);
8151
+ const dest = join22(target, f2);
8093
8152
  rmSync4(dest, { recursive: true, force: true });
8094
- cpSync2(join23(bundle, f2), dest, { recursive: true });
8153
+ cpSync2(join22(bundle, f2), dest, { recursive: true });
8095
8154
  }
8096
8155
  };
8097
8156
  var permError = (e, dir, system) => {
@@ -8103,34 +8162,34 @@ var permError = (e, dir, system) => {
8103
8162
  };
8104
8163
  var installUnix = (args, bundle, companions) => {
8105
8164
  const system = flagBool(args, "system");
8106
- const libDir = resolve11(flagStr(args, "dir") ?? (system ? "/usr/local/lib/lecodes-cli" : join23(HOME, ".local", "lib", "lecodes-cli")));
8107
- const binDir = resolve11(flagStr(args, "bin") ?? (system ? "/usr/local/bin" : join23(HOME, ".local", "bin")));
8108
- const link2 = join23(binDir, "lecodes");
8165
+ const libDir = resolve11(flagStr(args, "dir") ?? (system ? "/usr/local/lib/lecodes-cli" : join22(HOME, ".local", "lib", "lecodes-cli")));
8166
+ const binDir = resolve11(flagStr(args, "bin") ?? (system ? "/usr/local/bin" : join22(HOME, ".local", "bin")));
8167
+ const link2 = join22(binDir, "lecodes");
8109
8168
  try {
8110
8169
  copyBundle(bundle, libDir, companions);
8111
- chmodSync(join23(libDir, "lecodes"), 493);
8112
- chmodSync(join23(libDir, "chisel"), 493);
8170
+ chmodSync(join22(libDir, "lecodes"), 493);
8171
+ chmodSync(join22(libDir, "chisel"), 493);
8113
8172
  } catch (e) {
8114
8173
  throw permError(e, libDir, system);
8115
8174
  }
8116
8175
  try {
8117
8176
  mkdirSync15(binDir, { recursive: true });
8118
8177
  rmSync4(link2, { force: true });
8119
- symlinkSync(join23(libDir, "lecodes"), link2);
8178
+ symlinkSync(join22(libDir, "lecodes"), link2);
8120
8179
  } catch (e) {
8121
8180
  throw permError(e, binDir, system);
8122
8181
  }
8123
8182
  success(`Installed to ${c.bold(libDir)}`);
8124
- log(` ${c.dim(link2 + " → " + join23(libDir, "lecodes"))}`);
8183
+ log(` ${c.dim(link2 + " → " + join22(libDir, "lecodes"))}`);
8125
8184
  finishPathUnix(args, binDir);
8126
8185
  };
8127
8186
  var shellRc = () => {
8128
8187
  const shell = basename13(process.env.SHELL ?? "");
8129
8188
  if (shell === "zsh")
8130
- return { file: join23(HOME, ".zshrc"), name: "~/.zshrc" };
8189
+ return { file: join22(HOME, ".zshrc"), name: "~/.zshrc" };
8131
8190
  if (shell === "bash")
8132
- return { file: join23(HOME, ".bashrc"), name: "~/.bashrc" };
8133
- return { file: join23(HOME, ".profile"), name: "~/.profile" };
8191
+ return { file: join22(HOME, ".bashrc"), name: "~/.bashrc" };
8192
+ return { file: join22(HOME, ".profile"), name: "~/.profile" };
8134
8193
  };
8135
8194
  var PATH_MARKER = "# Added by `lecodes install`";
8136
8195
  var appendPathToShellRc = (binDir) => {
@@ -8175,7 +8234,7 @@ var finishPathUnix = (args, binDir) => {
8175
8234
  log(` • or install system-wide (no PATH edit): ${c.bold("sudo lecodes install --system")}`);
8176
8235
  };
8177
8236
  var installWindows = (args, bundle, companions) => {
8178
- const target = resolve11(flagStr(args, "dir") ?? join23(process.env.LOCALAPPDATA ?? join23(HOME, "AppData", "Local"), "Programs", "lecodes-cli"));
8237
+ const target = resolve11(flagStr(args, "dir") ?? join22(process.env.LOCALAPPDATA ?? join22(HOME, "AppData", "Local"), "Programs", "lecodes-cli"));
8179
8238
  try {
8180
8239
  copyBundle(bundle, target, companions);
8181
8240
  } catch (e) {
@@ -8214,7 +8273,7 @@ var addToUserPathWindows = (dir) => {
8214
8273
  var STANDALONE = (() => {
8215
8274
  const dir = dirname16(process.execPath);
8216
8275
  const chisel = process.platform === "win32" ? "chisel.exe" : "chisel";
8217
- return existsSync23(join24(dir, "runtime")) && existsSync23(join24(dir, chisel));
8276
+ return existsSync23(join23(dir, "runtime")) && existsSync23(join23(dir, chisel));
8218
8277
  })();
8219
8278
  var INSTALLED = STANDALONE && (() => {
8220
8279
  const exe = process.platform === "win32" ? "lecodes.exe" : "lecodes";
@@ -8229,7 +8288,7 @@ var INSTALLED = STANDALONE && (() => {
8229
8288
  if (!d)
8230
8289
  return false;
8231
8290
  try {
8232
- const cand = join24(d, exe);
8291
+ const cand = join23(d, exe);
8233
8292
  return existsSync23(cand) && realpathSync2(cand) === self;
8234
8293
  } catch {
8235
8294
  return false;
@@ -8416,7 +8475,7 @@ var peerVersion = (name, probe) => {
8416
8475
  const require2 = createRequire4(import.meta.url);
8417
8476
  let dir = dirname16(require2.resolve(probe));
8418
8477
  for (let depth = 0;depth < 16; depth++) {
8419
- const manifest = join24(dir, "package.json");
8478
+ const manifest = join23(dir, "package.json");
8420
8479
  if (existsSync23(manifest)) {
8421
8480
  const parsed = JSON.parse(readFileSync23(manifest, "utf8"));
8422
8481
  if (parsed.name === name)