lecodes-cli 0.18.1 → 0.18.2
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 +1008 -488
- package/package.json +1 -1
- package/runtime/scene-harness.json +1 -1
- package/runtime/sdk/gl/Camera.ts +39 -0
- package/runtime/sdk/gl/CameraPlace.ts +52 -52
- package/runtime/sdk/gl/Scene.ts +13 -0
- package/runtime/sdk/gl/Trigger.ts +45 -45
- package/runtime/sdk/gl/scenarios.ts +291 -291
- package/runtime/sdk/gl/state.ts +6 -6
- package/runtime/sdk/runtime/device.ts +102 -1
- package/runtime/sdk/scene/defineScene.ts +1227 -1227
- package/runtime/sdk/scene/gizmos.ts +148 -148
- package/runtime/sdk-types.json +1 -1
- package/runtime/sdk/compile/aspectMacro.ts +0 -86
- package/runtime/sdk/compile/assetIconMacro.ts +0 -384
- package/runtime/sdk/compile/assetMacro.ts +0 -146
- package/runtime/sdk/compile/assetName.ts +0 -50
- package/runtime/sdk/compile/bundler.ts +0 -287
- package/runtime/sdk/compile/compileProject.ts +0 -129
- package/runtime/sdk/compile/detectEntry.ts +0 -128
- package/runtime/sdk/compile/fontMacro.ts +0 -459
- package/runtime/sdk/compile/fontRegistry.ts +0 -78
- package/runtime/sdk/compile/header.ts +0 -67
- package/runtime/sdk/compile/index.ts +0 -100
- package/runtime/sdk/compile/libraryImports.ts +0 -52
- package/runtime/sdk/compile/liteMaterial.ts +0 -247
- package/runtime/sdk/compile/sceneEditor.ts +0 -88
- package/runtime/sdk/compile/serverSplit.ts +0 -233
- package/runtime/sdk/compile/serverTypes.ts +0 -227
- package/runtime/sdk/compile/sfnt.ts +0 -98
- package/runtime/sdk/compile/shaderSchema.ts +0 -202
- package/runtime/sdk/compile/shaderTargets.ts +0 -81
- package/runtime/sdk/compile/sourcemap.ts +0 -25
package/dist/index.js
CHANGED
|
@@ -418,7 +418,7 @@ node_modules/
|
|
|
418
418
|
return "updated";
|
|
419
419
|
};
|
|
420
420
|
var init_ignore = __esm(() => {
|
|
421
|
-
BUILTIN_PATTERNS = ["node_modules/", ".git/", "_*", "*.log", ".DS_Store", ".artifacts/", "dist/"];
|
|
421
|
+
BUILTIN_PATTERNS = ["node_modules/", ".git/", "_*", "*.log", ".DS_Store", ".artifacts/", "dist/", ".env", ".env.*"];
|
|
422
422
|
});
|
|
423
423
|
|
|
424
424
|
// src/localFiles.ts
|
|
@@ -3174,7 +3174,7 @@ var require_source_map_support = __commonJS((exports, module) => {
|
|
|
3174
3174
|
|
|
3175
3175
|
// ../../node_modules/.bun/typescript@5.8.3/node_modules/typescript/lib/typescript.js
|
|
3176
3176
|
var require_typescript = __commonJS((exports, module) => {
|
|
3177
|
-
var __dirname = "
|
|
3177
|
+
var __dirname = "/Users/den/Desktop/Ingenuity/le-codes/node_modules/.bun/typescript@5.8.3/node_modules/typescript/lib", __filename = "/Users/den/Desktop/Ingenuity/le-codes/node_modules/.bun/typescript@5.8.3/node_modules/typescript/lib/typescript.js";
|
|
3178
3178
|
/*! *****************************************************************************
|
|
3179
3179
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3180
3180
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
@@ -173350,7 +173350,7 @@ var init_fonts = __esm(() => {
|
|
|
173350
173350
|
|
|
173351
173351
|
// src/compile/assetIcons.ts
|
|
173352
173352
|
import { createHash as createHash5 } from "node:crypto";
|
|
173353
|
-
import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as
|
|
173353
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "node:fs";
|
|
173354
173354
|
import { homedir as homedir3 } from "node:os";
|
|
173355
173355
|
import { dirname as dirname7, join as join14 } from "node:path";
|
|
173356
173356
|
var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) => createHash5("sha256").update(s).digest("hex").slice(0, 12), readText = async (registry, rel) => {
|
|
@@ -173363,13 +173363,13 @@ var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) => crea
|
|
|
173363
173363
|
}
|
|
173364
173364
|
}
|
|
173365
173365
|
try {
|
|
173366
|
-
return
|
|
173366
|
+
return readFileSync12(join14(registry, rel), "utf8");
|
|
173367
173367
|
} catch {
|
|
173368
173368
|
return null;
|
|
173369
173369
|
}
|
|
173370
173370
|
}, safeRead = (path) => {
|
|
173371
173371
|
try {
|
|
173372
|
-
return
|
|
173372
|
+
return readFileSync12(path, "utf8");
|
|
173373
173373
|
} catch {
|
|
173374
173374
|
return null;
|
|
173375
173375
|
}
|
|
@@ -173391,7 +173391,7 @@ var DEFAULT_REGISTRY, isHttp2 = (s) => /^https?:/i.test(s), sha122 = (s) => crea
|
|
|
173391
173391
|
return JSON.parse(raw);
|
|
173392
173392
|
}, readLock2 = (path) => {
|
|
173393
173393
|
try {
|
|
173394
|
-
const lock = JSON.parse(
|
|
173394
|
+
const lock = JSON.parse(readFileSync12(path, "utf8"));
|
|
173395
173395
|
if (lock && typeof lock === "object" && lock.icons && lock.packs)
|
|
173396
173396
|
return lock;
|
|
173397
173397
|
} catch {}
|
|
@@ -214120,65 +214120,65 @@ var init_esm30 = __esm(() => {
|
|
|
214120
214120
|
});
|
|
214121
214121
|
|
|
214122
214122
|
// src/types.ts
|
|
214123
|
-
import { dirname as dirname13, join as
|
|
214124
|
-
import { existsSync as
|
|
214123
|
+
import { dirname as dirname13, join as join22, relative as relative3, sep } from "node:path";
|
|
214124
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync15, readdirSync as readdirSync10, readFileSync as readFileSync18, rmSync as rmSync8, statSync as statSync8, writeFileSync as writeFileSync17 } from "node:fs";
|
|
214125
214125
|
var TSCONFIG, listBundleFiles = (dir, base = dir) => {
|
|
214126
|
-
if (!
|
|
214126
|
+
if (!existsSync21(dir))
|
|
214127
214127
|
return [];
|
|
214128
214128
|
const out = [];
|
|
214129
|
-
for (const name of
|
|
214130
|
-
const path2 =
|
|
214131
|
-
if (
|
|
214129
|
+
for (const name of readdirSync10(dir)) {
|
|
214130
|
+
const path2 = join22(dir, name);
|
|
214131
|
+
if (statSync8(path2).isDirectory())
|
|
214132
214132
|
out.push(...listBundleFiles(path2, base));
|
|
214133
214133
|
else
|
|
214134
214134
|
out.push(relative3(base, path2).split(sep).join("/"));
|
|
214135
214135
|
}
|
|
214136
214136
|
return out;
|
|
214137
214137
|
}, pruneEmptyDirs = (dir, root) => {
|
|
214138
|
-
if (!
|
|
214138
|
+
if (!existsSync21(dir) || !statSync8(dir).isDirectory())
|
|
214139
214139
|
return;
|
|
214140
|
-
for (const name of
|
|
214141
|
-
pruneEmptyDirs(
|
|
214142
|
-
if (dir !== root &&
|
|
214143
|
-
|
|
214140
|
+
for (const name of readdirSync10(dir))
|
|
214141
|
+
pruneEmptyDirs(join22(dir, name), root);
|
|
214142
|
+
if (dir !== root && readdirSync10(dir).length === 0)
|
|
214143
|
+
rmSync8(dir, { recursive: true });
|
|
214144
214144
|
}, writeTypeBundle = (root, bundle) => {
|
|
214145
|
-
const typesDir =
|
|
214145
|
+
const typesDir = join22(root, LECODES_DIR, "types");
|
|
214146
214146
|
const before = new Set(listBundleFiles(typesDir));
|
|
214147
214147
|
const stats = { added: 0, updated: 0, removed: 0, total: Object.keys(bundle).length };
|
|
214148
214148
|
for (const [path2, text] of Object.entries(bundle)) {
|
|
214149
|
-
const dest =
|
|
214149
|
+
const dest = join22(typesDir, path2);
|
|
214150
214150
|
if (!before.has(path2))
|
|
214151
214151
|
stats.added++;
|
|
214152
|
-
else if (
|
|
214152
|
+
else if (readFileSync18(dest, "utf8") !== text)
|
|
214153
214153
|
stats.updated++;
|
|
214154
214154
|
else
|
|
214155
214155
|
continue;
|
|
214156
|
-
|
|
214157
|
-
|
|
214156
|
+
mkdirSync15(dirname13(dest), { recursive: true });
|
|
214157
|
+
writeFileSync17(dest, text);
|
|
214158
214158
|
}
|
|
214159
214159
|
for (const path2 of before) {
|
|
214160
214160
|
if (path2 in bundle)
|
|
214161
214161
|
continue;
|
|
214162
|
-
|
|
214162
|
+
rmSync8(join22(typesDir, path2));
|
|
214163
214163
|
stats.removed++;
|
|
214164
214164
|
}
|
|
214165
214165
|
pruneEmptyDirs(typesDir, typesDir);
|
|
214166
214166
|
return stats;
|
|
214167
214167
|
}, bundleHash = (bundle) => sha256(Object.keys(bundle).sort().map((k) => `${k}\x00${bundle[k]}`).join("\x00")), readProjectTypeBundle = (root) => {
|
|
214168
|
-
const typesDir =
|
|
214168
|
+
const typesDir = join22(root, LECODES_DIR, "types");
|
|
214169
214169
|
const bundle = {};
|
|
214170
214170
|
for (const path2 of listBundleFiles(typesDir))
|
|
214171
|
-
bundle[path2] =
|
|
214171
|
+
bundle[path2] = readFileSync18(join22(typesDir, path2), "utf8");
|
|
214172
214172
|
return bundle;
|
|
214173
214173
|
}, materializeTypes = async (root, apiUrl) => writeTypeBundle(root, await getTypes(apiUrl)), readLocalTypeBundle = () => {
|
|
214174
214174
|
const candidates = [
|
|
214175
|
-
|
|
214176
|
-
|
|
214175
|
+
join22(distRoot, "runtime", "sdk-types.json"),
|
|
214176
|
+
join22(distRoot, "..", "sdk", "dist", "types.json")
|
|
214177
214177
|
];
|
|
214178
214178
|
for (const path2 of candidates) {
|
|
214179
|
-
if (
|
|
214179
|
+
if (existsSync21(path2)) {
|
|
214180
214180
|
try {
|
|
214181
|
-
return JSON.parse(
|
|
214181
|
+
return JSON.parse(readFileSync18(path2, "utf8"));
|
|
214182
214182
|
} catch {}
|
|
214183
214183
|
}
|
|
214184
214184
|
}
|
|
@@ -214190,7 +214190,7 @@ var TSCONFIG, listBundleFiles = (dir, base = dir) => {
|
|
|
214190
214190
|
writeTypeBundle(root, bundle);
|
|
214191
214191
|
return true;
|
|
214192
214192
|
}, writeTsconfig = (root) => {
|
|
214193
|
-
|
|
214193
|
+
writeFileSync17(join22(root, "tsconfig.json"), JSON.stringify(TSCONFIG, null, 2));
|
|
214194
214194
|
};
|
|
214195
214195
|
var init_types4 = __esm(() => {
|
|
214196
214196
|
init_api();
|
|
@@ -214216,14 +214216,14 @@ var init_types4 = __esm(() => {
|
|
|
214216
214216
|
});
|
|
214217
214217
|
|
|
214218
214218
|
// src/compile/designCompile.ts
|
|
214219
|
-
import { readFileSync as
|
|
214220
|
-
import { basename as
|
|
214219
|
+
import { readFileSync as readFileSync19 } from "node:fs";
|
|
214220
|
+
import { basename as basename9 } from "node:path";
|
|
214221
214221
|
var LOCAL_ASSET_SCHEME = "lecodesfile://", compileDesignScreen = async (opts) => {
|
|
214222
214222
|
const { root, manifest, designDir, designKey, screenId, state } = opts;
|
|
214223
214223
|
const entries = [];
|
|
214224
214224
|
const scan2 = scanLocal(root, { ignore: loadIgnore(root), tracked: new Set(Object.keys(manifest?.files ?? {})) });
|
|
214225
214225
|
for (const file of scan2.files) {
|
|
214226
|
-
const type = manifest?.files[file.path]?.type ?? classifyKind(
|
|
214226
|
+
const type = manifest?.files[file.path]?.type ?? classifyKind(basename9(file.path));
|
|
214227
214227
|
if (type === "resource") {
|
|
214228
214228
|
entries.push({
|
|
214229
214229
|
path: file.path,
|
|
@@ -214234,7 +214234,7 @@ var LOCAL_ASSET_SCHEME = "lecodesfile://", compileDesignScreen = async (opts) =>
|
|
|
214234
214234
|
} else if (type === "shader") {
|
|
214235
214235
|
entries.push({ path: file.path, type, shaders: [] });
|
|
214236
214236
|
} else {
|
|
214237
|
-
entries.push({ path: file.path, type, text:
|
|
214237
|
+
entries.push({ path: file.path, type, text: readFileSync19(file.absPath, "utf8") });
|
|
214238
214238
|
}
|
|
214239
214239
|
}
|
|
214240
214240
|
const invoke = state !== undefined ? `screen(${JSON.stringify(state)})` : "screen()";
|
|
@@ -214279,7 +214279,7 @@ var init_designCompile = __esm(() => {
|
|
|
214279
214279
|
|
|
214280
214280
|
// src/browserAuth.ts
|
|
214281
214281
|
import { createServer } from "node:http";
|
|
214282
|
-
import { spawn as
|
|
214282
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
214283
214283
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
214284
214284
|
import { hostname } from "node:os";
|
|
214285
214285
|
var SUCCESS_HTML = `<!doctype html><meta charset="utf-8"><title>lecodes</title>
|
|
@@ -214304,16 +214304,16 @@ var SUCCESS_HTML = `<!doctype html><meta charset="utf-8"><title>lecodes</title>
|
|
|
214304
214304
|
args = [url];
|
|
214305
214305
|
}
|
|
214306
214306
|
try {
|
|
214307
|
-
const child =
|
|
214307
|
+
const child = spawn5(cmd, args, { stdio: "ignore", detached: true });
|
|
214308
214308
|
child.on("error", () => {});
|
|
214309
214309
|
child.unref();
|
|
214310
214310
|
} catch {}
|
|
214311
214311
|
}, browserLogin = async (webBase, timeoutMs = 5 * 60 * 1000, open = openBrowser) => {
|
|
214312
214312
|
const state = randomBytes2(16).toString("hex");
|
|
214313
|
-
let
|
|
214313
|
+
let resolve11;
|
|
214314
214314
|
let reject;
|
|
214315
214315
|
const tokenPromise = new Promise((res, rej) => {
|
|
214316
|
-
|
|
214316
|
+
resolve11 = res;
|
|
214317
214317
|
reject = rej;
|
|
214318
214318
|
});
|
|
214319
214319
|
const server2 = createServer((req, res) => {
|
|
@@ -214331,7 +214331,7 @@ var SUCCESS_HTML = `<!doctype html><meta charset="utf-8"><title>lecodes</title>
|
|
|
214331
214331
|
}
|
|
214332
214332
|
res.writeHead(200, { "content-type": "text/html" });
|
|
214333
214333
|
res.end(SUCCESS_HTML);
|
|
214334
|
-
|
|
214334
|
+
resolve11(token);
|
|
214335
214335
|
});
|
|
214336
214336
|
await new Promise((res, rej) => {
|
|
214337
214337
|
server2.once("error", rej);
|
|
@@ -214831,11 +214831,11 @@ var init_designTemplates = __esm(() => {
|
|
|
214831
214831
|
});
|
|
214832
214832
|
|
|
214833
214833
|
// src/designMeta.ts
|
|
214834
|
-
import { readFileSync as
|
|
214835
|
-
import { join as
|
|
214834
|
+
import { readFileSync as readFileSync20 } from "node:fs";
|
|
214835
|
+
import { join as join24 } from "node:path";
|
|
214836
214836
|
var PHONE_SIZE, readMetaFrames = (designDir) => {
|
|
214837
214837
|
try {
|
|
214838
|
-
const meta = JSON.parse(
|
|
214838
|
+
const meta = JSON.parse(readFileSync20(join24(designDir, "meta.json"), "utf8"));
|
|
214839
214839
|
const d = meta?.defaultSize;
|
|
214840
214840
|
const valid = Array.isArray(d) && d.length === 2 && d.every((n2) => typeof n2 === "number" && Number.isFinite(n2) && n2 >= 64 && n2 <= 8192);
|
|
214841
214841
|
return {
|
|
@@ -214859,8 +214859,8 @@ __export(exports_design, {
|
|
|
214859
214859
|
flushDesignComments: () => flushDesignComments,
|
|
214860
214860
|
design: () => design
|
|
214861
214861
|
});
|
|
214862
|
-
import { existsSync as
|
|
214863
|
-
import { basename as
|
|
214862
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync18, readFileSync as readFileSync21, readdirSync as readdirSync13, writeFileSync as writeFileSync19, appendFileSync } from "node:fs";
|
|
214863
|
+
import { basename as basename10, join as join25 } from "node:path";
|
|
214864
214864
|
var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
214865
214865
|
let root;
|
|
214866
214866
|
let manifest;
|
|
@@ -214875,13 +214875,13 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214875
214875
|
return {
|
|
214876
214876
|
root,
|
|
214877
214877
|
manifest,
|
|
214878
|
-
name: manifest?.name ??
|
|
214878
|
+
name: manifest?.name ?? basename10(root),
|
|
214879
214879
|
dirName,
|
|
214880
|
-
designDir:
|
|
214880
|
+
designDir: join25(root, dirName),
|
|
214881
214881
|
designKey: "/" + dirName.replace(/\\/g, "/")
|
|
214882
214882
|
};
|
|
214883
214883
|
}, loadDesignServer = async (ctx, needs = []) => {
|
|
214884
|
-
if (!
|
|
214884
|
+
if (!existsSync24(ctx.designDir)) {
|
|
214885
214885
|
throw new CliError(`No ${ctx.dirName}/ folder here. Run "lecodes design init" to scaffold it.`);
|
|
214886
214886
|
}
|
|
214887
214887
|
let mod;
|
|
@@ -214913,17 +214913,17 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214913
214913
|
`).find((l2) => l2.trim())?.trim() ?? "";
|
|
214914
214914
|
return line.length > max ? line.slice(0, max - 1) + "…" : line;
|
|
214915
214915
|
}, listScreens = (designDir) => {
|
|
214916
|
-
const dir =
|
|
214917
|
-
if (!
|
|
214916
|
+
const dir = join25(designDir, "screens");
|
|
214917
|
+
if (!existsSync24(dir))
|
|
214918
214918
|
return [];
|
|
214919
|
-
return
|
|
214919
|
+
return readdirSync13(dir).filter((f3) => f3.endsWith(".ts") && !f3.startsWith(".")).map((f3) => f3.slice(0, -3)).sort();
|
|
214920
214920
|
}, writeMcpConfig = (ctx, port) => {
|
|
214921
|
-
const path2 =
|
|
214921
|
+
const path2 = join25(ctx.root, ".mcp.json");
|
|
214922
214922
|
const entry = { type: "http", url: `http://localhost:${port}/mcp` };
|
|
214923
214923
|
let config = {};
|
|
214924
|
-
if (
|
|
214924
|
+
if (existsSync24(path2)) {
|
|
214925
214925
|
try {
|
|
214926
|
-
config = JSON.parse(
|
|
214926
|
+
config = JSON.parse(readFileSync21(path2, "utf8"));
|
|
214927
214927
|
} catch {
|
|
214928
214928
|
warnErr(".mcp.json exists but isn't valid JSON — left it untouched. Add the lecodes-design server by hand.");
|
|
214929
214929
|
return;
|
|
@@ -214936,7 +214936,7 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214936
214936
|
return;
|
|
214937
214937
|
}
|
|
214938
214938
|
config.mcpServers["lecodes-design"] = entry;
|
|
214939
|
-
|
|
214939
|
+
writeFileSync19(path2, JSON.stringify(config, null, 2) + `
|
|
214940
214940
|
`);
|
|
214941
214941
|
log(`${c.green(existing ? "updated" : "created")} .mcp.json (lecodes-design MCP → ${entry.url})`);
|
|
214942
214942
|
}, init = async (ctx, args) => {
|
|
@@ -214945,26 +214945,26 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214945
214945
|
["spec.md", SPEC_MD],
|
|
214946
214946
|
["README.md", README_HINT],
|
|
214947
214947
|
["CLAUDE.md", CLAUDE_MD],
|
|
214948
|
-
[
|
|
214949
|
-
[
|
|
214950
|
-
[
|
|
214948
|
+
[join25("shared", "tokens.ts"), TOKENS_TS],
|
|
214949
|
+
[join25("shared", "tabs.ts"), TABS_TS],
|
|
214950
|
+
[join25("screens", "home.ts"), HOME_SCREEN_TS]
|
|
214951
214951
|
];
|
|
214952
|
-
|
|
214953
|
-
|
|
214952
|
+
mkdirSync18(join25(ctx.designDir, "screens"), { recursive: true });
|
|
214953
|
+
mkdirSync18(join25(ctx.designDir, "shared"), { recursive: true });
|
|
214954
214954
|
let created = 0;
|
|
214955
214955
|
for (const [rel, content] of files) {
|
|
214956
|
-
const abs =
|
|
214957
|
-
if (
|
|
214956
|
+
const abs = join25(ctx.designDir, rel);
|
|
214957
|
+
if (existsSync24(abs)) {
|
|
214958
214958
|
note(`kept ${ctx.dirName}/${rel.replace(/\\/g, "/")}`);
|
|
214959
214959
|
continue;
|
|
214960
214960
|
}
|
|
214961
|
-
|
|
214961
|
+
writeFileSync19(abs, content);
|
|
214962
214962
|
log(`${c.green("created")} ${ctx.dirName}/${rel.replace(/\\/g, "/")}`);
|
|
214963
214963
|
created++;
|
|
214964
214964
|
}
|
|
214965
|
-
const ignorePath =
|
|
214965
|
+
const ignorePath = join25(ctx.root, ".lecodesignore");
|
|
214966
214966
|
const ignoreLines = [`${ctx.dirName}/.snapshots/`, `${ctx.dirName}/comments/`];
|
|
214967
|
-
const current =
|
|
214967
|
+
const current = existsSync24(ignorePath) ? readFileSync21(ignorePath, "utf8") : "";
|
|
214968
214968
|
const present = new Set(current.split(/\r?\n/));
|
|
214969
214969
|
const missing = ignoreLines.filter((line) => !present.has(line));
|
|
214970
214970
|
if (missing.length) {
|
|
@@ -214979,8 +214979,8 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214979
214979
|
writeMcpConfig(ctx, Number(flagStr(args, "port")) || DEFAULT_PORT);
|
|
214980
214980
|
const wroteTypes = materializeTypesLocal(ctx.designDir);
|
|
214981
214981
|
writeTsconfig(ctx.designDir);
|
|
214982
|
-
|
|
214983
|
-
|
|
214982
|
+
mkdirSync18(join25(ctx.designDir, ".lecodes", "types"), { recursive: true });
|
|
214983
|
+
writeFileSync19(join25(ctx.designDir, ".lecodes", "types", "design-macros.d.ts"), DESIGN_MACROS_DTS);
|
|
214984
214984
|
log(`${c.green("created")} ${ctx.dirName}/tsconfig.json${wroteTypes ? ` + ${ctx.dirName}/.lecodes/types/` : ""} (IDE types)`);
|
|
214985
214985
|
if (!wroteTypes)
|
|
214986
214986
|
warnErr("Bundled SDK types not found — tsconfig written, but globals may show unresolved until the CLI is rebuilt with vendored types.");
|
|
@@ -215028,16 +215028,16 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
215028
215028
|
killer.unref?.();
|
|
215029
215029
|
const png2 = flagBool(args, "png") || flagStr(args, "png") !== undefined;
|
|
215030
215030
|
const frames = readMetaFrames(ctx.designDir);
|
|
215031
|
-
const outDir = flagStr(args, "out-dir") ??
|
|
215031
|
+
const outDir = flagStr(args, "out-dir") ?? join25(ctx.designDir, ".snapshots");
|
|
215032
215032
|
const single = targets.length === 1 && requested.length === 1 && !png2 && !allStates && !flagStr(args, "out-dir");
|
|
215033
215033
|
const onConsole = flagBool(args, "logs") ? (level, text) => logErr(`${c.dim(`[${level}]`)} ${text}`) : undefined;
|
|
215034
215034
|
if (!single)
|
|
215035
|
-
|
|
215035
|
+
mkdirSync18(outDir, { recursive: true });
|
|
215036
215036
|
for (const id of targets) {
|
|
215037
215037
|
const [width, height] = frames.screens[id]?.size ?? frames.defaultSize;
|
|
215038
215038
|
let plan = [{ state: undefined, bases: [id] }];
|
|
215039
215039
|
if (discover) {
|
|
215040
|
-
const { states, canonical } = discover(
|
|
215040
|
+
const { states, canonical } = discover(readFileSync21(join25(ctx.designDir, "screens", `${id}.ts`), "utf8"));
|
|
215041
215041
|
if (stateFlag !== undefined) {
|
|
215042
215042
|
if (!states.includes(stateFlag)) {
|
|
215043
215043
|
if (requested.length === 1)
|
|
@@ -215078,11 +215078,11 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
215078
215078
|
for (const w of pngResult.warnings)
|
|
215079
215079
|
warnErr(`${tag}: ${w}`);
|
|
215080
215080
|
for (const base of bases) {
|
|
215081
|
-
|
|
215082
|
-
log(`${c.green("✓")} ${tag} → ${
|
|
215081
|
+
writeFileSync19(join25(outDir, `${base}.json`), result.text);
|
|
215082
|
+
log(`${c.green("✓")} ${tag} → ${join25(outDir, `${base}.json`)}`);
|
|
215083
215083
|
if (pngResult) {
|
|
215084
|
-
|
|
215085
|
-
log(`${c.green("✓")} ${tag} → ${
|
|
215084
|
+
writeFileSync19(join25(outDir, `${base}.png`), pngResult.png);
|
|
215085
|
+
log(`${c.green("✓")} ${tag} → ${join25(outDir, `${base}.png`)}`);
|
|
215086
215086
|
}
|
|
215087
215087
|
}
|
|
215088
215088
|
}
|
|
@@ -215384,7 +215384,7 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
215384
215384
|
note("Read them with: lecodes design comments");
|
|
215385
215385
|
}, flushDesignComments = async (args) => {
|
|
215386
215386
|
const ctx = getContext(args);
|
|
215387
|
-
if (!
|
|
215387
|
+
if (!existsSync24(ctx.designDir))
|
|
215388
215388
|
return null;
|
|
215389
215389
|
const platform = optionalPlatform(ctx);
|
|
215390
215390
|
if (!platform)
|
|
@@ -216526,7 +216526,7 @@ var require_main = __commonJS((exports, module) => {
|
|
|
216526
216526
|
// package.json
|
|
216527
216527
|
var package_default = {
|
|
216528
216528
|
name: "lecodes-cli",
|
|
216529
|
-
version: "0.18.
|
|
216529
|
+
version: "0.18.2",
|
|
216530
216530
|
dependencies: {
|
|
216531
216531
|
"@letary/chisel": "^0.10.0",
|
|
216532
216532
|
jimp: "^1.6.1"
|
|
@@ -216593,10 +216593,10 @@ var package_default = {
|
|
|
216593
216593
|
// src/commands/app.ts
|
|
216594
216594
|
init_manifest();
|
|
216595
216595
|
init_config();
|
|
216596
|
-
import { execFileSync as
|
|
216597
|
-
import { cpSync as
|
|
216596
|
+
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
216597
|
+
import { cpSync as cpSync4, existsSync as existsSync20, mkdirSync as mkdirSync14, readFileSync as readFileSync17, readdirSync as readdirSync9, rmSync as rmSync7, statSync as statSync7, writeFileSync as writeFileSync16 } from "node:fs";
|
|
216598
216598
|
import { homedir as homedir7 } from "node:os";
|
|
216599
|
-
import { basename as
|
|
216599
|
+
import { basename as basename8, dirname as dirname12, join as join21, resolve as resolve8 } from "node:path";
|
|
216600
216600
|
|
|
216601
216601
|
// src/commands/compile.ts
|
|
216602
216602
|
init_api();
|
|
@@ -216721,7 +216721,7 @@ init_fonts();
|
|
|
216721
216721
|
init_compile();
|
|
216722
216722
|
import { execFile } from "node:child_process";
|
|
216723
216723
|
import { createHash as createHash4, randomBytes } from "node:crypto";
|
|
216724
|
-
import { copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as
|
|
216724
|
+
import { copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync11, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync7 } from "node:fs";
|
|
216725
216725
|
import { tmpdir } from "node:os";
|
|
216726
216726
|
import { basename as basename3, join as join13 } from "node:path";
|
|
216727
216727
|
|
|
@@ -216733,10 +216733,18 @@ import { join as join12, resolve } from "node:path";
|
|
|
216733
216733
|
// src/releases.ts
|
|
216734
216734
|
init_util();
|
|
216735
216735
|
import { createHash as createHash3 } from "node:crypto";
|
|
216736
|
-
import { existsSync as existsSync9, mkdirSync as mkdirSync5, mkdtempSync, renameSync, rmSync, writeFileSync as writeFileSync6 } from "node:fs";
|
|
216736
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync5, mkdtempSync, readFileSync as readFileSync10, renameSync, rmSync, writeFileSync as writeFileSync6 } from "node:fs";
|
|
216737
216737
|
import { join as join11 } from "node:path";
|
|
216738
216738
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
216739
216739
|
var MANIFEST_URL = "https://storage.yandexcloud.net/le-codes/releases/versions.json";
|
|
216740
|
+
var ARTIFACT_STAMP = ".lecodes-artifact.json";
|
|
216741
|
+
var cachedArtifactSha = (dir) => {
|
|
216742
|
+
try {
|
|
216743
|
+
return JSON.parse(readFileSync10(join11(dir, ARTIFACT_STAMP), "utf8")).sha256 ?? null;
|
|
216744
|
+
} catch {
|
|
216745
|
+
return null;
|
|
216746
|
+
}
|
|
216747
|
+
};
|
|
216740
216748
|
var semverNewer = (a, b) => {
|
|
216741
216749
|
const pa = a.split(/[.-]/).map(Number), pb = b.split(/[.-]/).map(Number);
|
|
216742
216750
|
for (let i = 0;i < 3; i++)
|
|
@@ -216778,15 +216786,20 @@ var downloadReleaseArchive = async (file, opts) => {
|
|
|
216778
216786
|
const archiveName = file.url.split("/").pop() ?? "archive";
|
|
216779
216787
|
const archive = join11(stage, archiveName);
|
|
216780
216788
|
writeFileSync6(archive, bytes);
|
|
216781
|
-
|
|
216782
|
-
|
|
216783
|
-
|
|
216784
|
-
|
|
216785
|
-
|
|
216789
|
+
if (opts.extract) {
|
|
216790
|
+
opts.extract(archive, stage);
|
|
216791
|
+
} else {
|
|
216792
|
+
const winTar = join11(process.env.SystemRoot ?? "C:\\Windows", "System32", "tar.exe");
|
|
216793
|
+
const tarExe = process.platform === "win32" && existsSync9(winTar) ? winTar : "tar";
|
|
216794
|
+
const tar = spawnSync2(tarExe, ["-xf", archiveName], { cwd: stage, stdio: ["ignore", "ignore", "pipe"] });
|
|
216795
|
+
if (tar.status !== 0)
|
|
216796
|
+
throw new CliError(`Couldn't extract the archive: ${tar.stderr?.toString() ?? "tar failed"}`);
|
|
216797
|
+
}
|
|
216786
216798
|
rmSync(archive);
|
|
216787
216799
|
const bad = opts.validate(stage);
|
|
216788
216800
|
if (bad)
|
|
216789
216801
|
throw new CliError(bad);
|
|
216802
|
+
writeFileSync6(join11(stage, ARTIFACT_STAMP), JSON.stringify({ sha256: file.sha256, url: file.url }, null, 2));
|
|
216790
216803
|
try {
|
|
216791
216804
|
renameSync(stage, opts.dest);
|
|
216792
216805
|
} catch {
|
|
@@ -216812,7 +216825,13 @@ var platformKey = () => {
|
|
|
216812
216825
|
return "windows-x64";
|
|
216813
216826
|
if (process.platform === "linux")
|
|
216814
216827
|
return "linux-x64";
|
|
216815
|
-
|
|
216828
|
+
if (process.platform === "darwin") {
|
|
216829
|
+
if (process.arch !== "arm64") {
|
|
216830
|
+
throw new CliError("matc isn't published for Intel macOS — set LECODES_MATC to a locally built matc " + "(creator-gl/out/cmake-release/filament/tools/matc), or push the project once so shaders " + "compile on the server.");
|
|
216831
|
+
}
|
|
216832
|
+
return "macos-arm64";
|
|
216833
|
+
}
|
|
216834
|
+
throw new CliError(`matc isn't published for ${process.platform}.`);
|
|
216816
216835
|
};
|
|
216817
216836
|
var cacheRoot = () => join12(homedir2(), ".lecodes", "matc");
|
|
216818
216837
|
var findMatc = (dir) => {
|
|
@@ -216930,7 +216949,7 @@ var cleanMatcOutput = (err, tempName, realName) => {
|
|
|
216930
216949
|
};
|
|
216931
216950
|
var NATIVE_PLATFORMS = ["opengl", "metal", "desktop"];
|
|
216932
216951
|
var ALL_NATIVE_PLATFORMS = ["opengl", "metal", "desktop", "desktop-vulkan"];
|
|
216933
|
-
var desktopShaderPlatform = (platform, renderer) => platform === "win32" ? renderer === "vulkan" ? "desktop-vulkan" : "desktop" : "opengl";
|
|
216952
|
+
var desktopShaderPlatform = (platform, renderer) => platform === "win32" ? renderer === "vulkan" ? "desktop-vulkan" : "desktop" : platform === "darwin" ? "metal" : "opengl";
|
|
216934
216953
|
var applyLocalShaders = async (entries, opts) => {
|
|
216935
216954
|
const shaderEntries = entries.filter((e) => e.type === "shader" && e.absPath && (opts.local || !e.shaders?.length));
|
|
216936
216955
|
if (shaderEntries.length === 0)
|
|
@@ -216948,7 +216967,7 @@ var applyLocalShaders = async (entries, opts) => {
|
|
|
216948
216967
|
mkdirSync6(cacheDir, { recursive: true });
|
|
216949
216968
|
const artifacts = [];
|
|
216950
216969
|
for (const entry of shaderEntries) {
|
|
216951
|
-
const text =
|
|
216970
|
+
const text = readFileSync11(entry.absPath, "utf8");
|
|
216952
216971
|
const hash = shaderHash(text, matcId);
|
|
216953
216972
|
const family = artifactFamily(text);
|
|
216954
216973
|
const artifact = (platform) => `${hash}_${platform}.filamat`;
|
|
@@ -217011,7 +217030,7 @@ init_manifest();
|
|
|
217011
217030
|
init_ignore();
|
|
217012
217031
|
init_util();
|
|
217013
217032
|
import { execFileSync } from "node:child_process";
|
|
217014
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as
|
|
217033
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "node:fs";
|
|
217015
217034
|
import { homedir as homedir4 } from "node:os";
|
|
217016
217035
|
import { dirname as dirname8, join as join15, resolve as resolve2 } from "node:path";
|
|
217017
217036
|
var FIRST_PARTY_PLUGINS = {
|
|
@@ -217029,8 +217048,13 @@ var resolveDesktopRenderer = (root, args) => {
|
|
|
217029
217048
|
const value = String(raw).toLowerCase();
|
|
217030
217049
|
if (value === "gl" || value === "opengl")
|
|
217031
217050
|
return "gl";
|
|
217032
|
-
if (value === "vulkan" || value === "vk")
|
|
217051
|
+
if (value === "vulkan" || value === "vk") {
|
|
217052
|
+
if (process.platform === "darwin") {
|
|
217053
|
+
note('desktop.renderer "vulkan" is Windows-only — the macOS host renders with Metal.');
|
|
217054
|
+
return "gl";
|
|
217055
|
+
}
|
|
217033
217056
|
return "vulkan";
|
|
217057
|
+
}
|
|
217034
217058
|
throw new CliError(`Unknown desktop renderer "${raw}". Use "gl" or "vulkan".`);
|
|
217035
217059
|
};
|
|
217036
217060
|
var desktopDefaultViewport = (root) => {
|
|
@@ -217046,7 +217070,7 @@ var readJson = (path) => {
|
|
|
217046
217070
|
if (!existsSync13(path))
|
|
217047
217071
|
return null;
|
|
217048
217072
|
try {
|
|
217049
|
-
return JSON.parse(
|
|
217073
|
+
return JSON.parse(readFileSync13(path, "utf8").replace(/^\uFEFF/, ""));
|
|
217050
217074
|
} catch {
|
|
217051
217075
|
throw new CliError(`Invalid JSON in ${path}`);
|
|
217052
217076
|
}
|
|
@@ -217077,7 +217101,7 @@ ${tail}`);
|
|
|
217077
217101
|
};
|
|
217078
217102
|
var ensureShellIgnored = (root, dir) => {
|
|
217079
217103
|
const ignorePath = join15(root, ".lecodesignore");
|
|
217080
|
-
const ignore = existsSync13(ignorePath) ?
|
|
217104
|
+
const ignore = existsSync13(ignorePath) ? readFileSync13(ignorePath, "utf8") : DEFAULT_IGNORE_FILE;
|
|
217081
217105
|
if (new RegExp(`^${dir}\\/$`, "m").test(ignore))
|
|
217082
217106
|
return;
|
|
217083
217107
|
const comment = ignore.includes("# Native app shells") ? "" : "# Native app shells — generated by `lecodes app init`, never pushed to LeCodes.\n";
|
|
@@ -218217,14 +218241,14 @@ buildNumber) — edit it there, then re-run \`lecodes app sync\`. Plugins are ma
|
|
|
218217
218241
|
init_manifest();
|
|
218218
218242
|
import { execFileSync as execFileSync2, spawn, spawnSync as spawnSync3 } from "node:child_process";
|
|
218219
218243
|
import { createHash as createHash6 } from "node:crypto";
|
|
218220
|
-
import { chmodSync, cpSync, existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as
|
|
218244
|
+
import { chmodSync, cpSync, existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as readFileSync15, readdirSync as readdirSync5, renameSync as renameSync2, rmSync as rmSync3, statSync as statSync3, writeFileSync as writeFileSync12 } from "node:fs";
|
|
218221
218245
|
import { homedir as homedir5 } from "node:os";
|
|
218222
218246
|
import { basename as basename5, dirname as dirname10, join as join17 } from "node:path";
|
|
218223
218247
|
init_util();
|
|
218224
218248
|
|
|
218225
218249
|
// src/commands/appIcon.ts
|
|
218226
218250
|
init_util();
|
|
218227
|
-
import { existsSync as existsSync15, mkdirSync as mkdirSync9, readFileSync as
|
|
218251
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "node:fs";
|
|
218228
218252
|
import { dirname as dirname9, join as join16, resolve as resolve4 } from "node:path";
|
|
218229
218253
|
var MASTER_PX = 1024;
|
|
218230
218254
|
var DENSITIES = [
|
|
@@ -218354,7 +218378,7 @@ var restampManifestIcon = (androidDir) => {
|
|
|
218354
218378
|
const path2 = join16(androidDir, "app", "src", "main", "AndroidManifest.xml");
|
|
218355
218379
|
if (!existsSync15(path2))
|
|
218356
218380
|
return;
|
|
218357
|
-
const content =
|
|
218381
|
+
const content = readFileSync14(path2, "utf8");
|
|
218358
218382
|
if (content.includes(`android:icon="@mipmap/ic_launcher"`))
|
|
218359
218383
|
return;
|
|
218360
218384
|
if (!/android:icon="[^"]*"/.test(content)) {
|
|
@@ -219274,7 +219298,7 @@ var shellSdkVersionAndroid = (androidDir) => {
|
|
|
219274
219298
|
const path2 = join17(androidDir, "lecodes-runtime", "build.gradle.kts");
|
|
219275
219299
|
if (!existsSync16(path2))
|
|
219276
219300
|
return null;
|
|
219277
|
-
const m = /io\.letary:lecodes-sdk(?:-(?:core|2d|3d))?:(\d+\.\d+\.\d+)@aar/.exec(
|
|
219301
|
+
const m = /io\.letary:lecodes-sdk(?:-(?:core|2d|3d))?:(\d+\.\d+\.\d+)@aar/.exec(readFileSync15(path2, "utf8"));
|
|
219278
219302
|
return m ? m[1] : null;
|
|
219279
219303
|
};
|
|
219280
219304
|
var GRADLE_WRAPPER_JAR_URL = "https://raw.githubusercontent.com/gradle/gradle/v8.13.0/gradle/wrapper/gradle-wrapper.jar";
|
|
@@ -219282,7 +219306,7 @@ var GRADLE_WRAPPER_JAR_SHA256 = "81a82aaea5abcc8ff68b3dfcb58b3c3c429378efd98e743
|
|
|
219282
219306
|
var ensureGradleWrapperJar = async () => {
|
|
219283
219307
|
const cached = join17(homedir5(), ".lecodes", "gradle-wrapper", GRADLE_VERSION, "gradle-wrapper.jar");
|
|
219284
219308
|
if (existsSync16(cached))
|
|
219285
|
-
return new Uint8Array(
|
|
219309
|
+
return new Uint8Array(readFileSync15(cached));
|
|
219286
219310
|
try {
|
|
219287
219311
|
const res = await fetch(GRADLE_WRAPPER_JAR_URL);
|
|
219288
219312
|
if (!res.ok)
|
|
@@ -219409,7 +219433,7 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
219409
219433
|
resources = prior.resources;
|
|
219410
219434
|
} else {
|
|
219411
219435
|
resources = await compileShell({ _: [], flags: { o: join17(assetsDir, "app.js") } }, { remotePatterns });
|
|
219412
|
-
bundleJs =
|
|
219436
|
+
bundleJs = readFileSync15(join17(assetsDir, "app.js"), "utf8");
|
|
219413
219437
|
}
|
|
219414
219438
|
const requestedVariant = config.android?.variant ?? "auto";
|
|
219415
219439
|
if (requestedVariant !== "auto" && !SDK_VARIANTS.includes(requestedVariant)) {
|
|
@@ -219546,7 +219570,7 @@ var restampAppBuildGradle = (androidDir, config, gradlePlugins) => {
|
|
|
219546
219570
|
warn("android/app/build.gradle.kts is missing — run `lecodes app init android` on a clean tree to regenerate the shell.");
|
|
219547
219571
|
return;
|
|
219548
219572
|
}
|
|
219549
|
-
let content =
|
|
219573
|
+
let content = readFileSync15(path2, "utf8");
|
|
219550
219574
|
const before = content;
|
|
219551
219575
|
const versionName = config.version ?? "1.0.0";
|
|
219552
219576
|
const versionCode = config.android?.versionCode ?? 1;
|
|
@@ -219719,13 +219743,13 @@ var doctorAndroidChecks = async (root, config, check) => {
|
|
|
219719
219743
|
};
|
|
219720
219744
|
|
|
219721
219745
|
// src/commands/appDesktop.ts
|
|
219722
|
-
import { execFileSync as
|
|
219723
|
-
import { cpSync as
|
|
219724
|
-
import { basename as
|
|
219746
|
+
import { execFileSync as execFileSync5, spawn as spawn4 } from "node:child_process";
|
|
219747
|
+
import { cpSync as cpSync3, existsSync as existsSync19, mkdirSync as mkdirSync13, readdirSync as readdirSync8, rmSync as rmSync6, statSync as statSync6, writeFileSync as writeFileSync15 } from "node:fs";
|
|
219748
|
+
import { basename as basename7, dirname as dirname11, join as join20, resolve as resolve7 } from "node:path";
|
|
219725
219749
|
|
|
219726
219750
|
// src/desktopRenderer.ts
|
|
219727
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
219728
|
-
import { copyFileSync as copyFileSync2, existsSync as existsSync17, mkdirSync as mkdirSync11, mkdtempSync as mkdtempSync2, readdirSync as readdirSync6, readFileSync as
|
|
219751
|
+
import { execFileSync as execFileSync3, spawn as spawn2, spawnSync as spawnSync4 } from "node:child_process";
|
|
219752
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync17, mkdirSync as mkdirSync11, mkdtempSync as mkdtempSync2, readdirSync as readdirSync6, readFileSync as readFileSync16, rmSync as rmSync4, statSync as statSync4, writeFileSync as writeFileSync13 } from "node:fs";
|
|
219729
219753
|
import { homedir as homedir6, tmpdir as tmpdir2 } from "node:os";
|
|
219730
219754
|
import { join as join18, resolve as resolve5 } from "node:path";
|
|
219731
219755
|
init_util();
|
|
@@ -219839,9 +219863,12 @@ var platformKey2 = (renderer = "gl") => {
|
|
|
219839
219863
|
}
|
|
219840
219864
|
if (process.platform === "linux")
|
|
219841
219865
|
return "ubuntu-linux-x64";
|
|
219842
|
-
|
|
219866
|
+
if (process.platform === "darwin")
|
|
219867
|
+
return process.arch === "arm64" ? "macos-arm64" : "macos-x64";
|
|
219868
|
+
throw new CliError(`The LeCodes Desktop host isn't available on ${process.platform}.`);
|
|
219843
219869
|
};
|
|
219844
|
-
var
|
|
219870
|
+
var isMac = () => process.platform === "darwin";
|
|
219871
|
+
var exeName = () => process.platform === "win32" ? "lecodes-desktop.exe" : isMac() ? "lecodes-app" : "lecodes-desktop";
|
|
219845
219872
|
var findExe = (dir) => {
|
|
219846
219873
|
const name = exeName();
|
|
219847
219874
|
for (const entry of readdirSync6(dir, { withFileTypes: true })) {
|
|
@@ -219867,13 +219894,33 @@ var installedDesktopVersions = () => {
|
|
|
219867
219894
|
};
|
|
219868
219895
|
var selectedDesktopVersion = () => {
|
|
219869
219896
|
try {
|
|
219870
|
-
const saved = JSON.parse(
|
|
219897
|
+
const saved = JSON.parse(readFileSync16(currentFile(), "utf8")).version;
|
|
219871
219898
|
if (saved && /^\d+\.\d+\.\d+/.test(saved) && semverNewer(DESKTOP_RENDERER_VERSION, saved) > 0)
|
|
219872
219899
|
return saved;
|
|
219873
219900
|
} catch {}
|
|
219874
219901
|
return DESKTOP_RENDERER_VERSION;
|
|
219875
219902
|
};
|
|
219876
219903
|
var fetchDesktopManifest = () => fetchReleaseProduct("desktop", "desktop");
|
|
219904
|
+
var pickMacVersion = (versions, wanted, key = "macos-arm64") => {
|
|
219905
|
+
if (versions.find((v) => v.version === wanted)?.files[key])
|
|
219906
|
+
return wanted;
|
|
219907
|
+
const eligible = versions.filter((v) => v.files[key] && semverNewer(DESKTOP_RENDERER_VERSION, v.version) >= 0).map((v) => v.version).sort(semverNewer);
|
|
219908
|
+
return eligible[0] ?? null;
|
|
219909
|
+
};
|
|
219910
|
+
var extractDmg = (archive, stage) => {
|
|
219911
|
+
const mount = join18(stage, "mnt");
|
|
219912
|
+
mkdirSync11(mount, { recursive: true });
|
|
219913
|
+
execFileSync3("hdiutil", ["attach", "-nobrowse", "-readonly", "-quiet", "-mountpoint", mount, archive]);
|
|
219914
|
+
try {
|
|
219915
|
+
const app = readdirSync6(mount).find((n2) => n2.endsWith(".app"));
|
|
219916
|
+
if (!app)
|
|
219917
|
+
throw new CliError("The downloaded DMG holds no .app bundle.");
|
|
219918
|
+
execFileSync3("cp", ["-R", join18(mount, app), join18(stage, app)]);
|
|
219919
|
+
} finally {
|
|
219920
|
+
spawnSync4("hdiutil", ["detach", "-quiet", "-force", mount]);
|
|
219921
|
+
rmSync4(mount, { recursive: true, force: true });
|
|
219922
|
+
}
|
|
219923
|
+
};
|
|
219877
219924
|
var downloadDesktop = async (version, dest, renderer = "gl") => {
|
|
219878
219925
|
const key = platformKey2(renderer);
|
|
219879
219926
|
const entry = (await fetchDesktopManifest()).versions.find((v) => v.version === version);
|
|
@@ -219886,13 +219933,55 @@ var downloadDesktop = async (version, dest, renderer = "gl") => {
|
|
|
219886
219933
|
throw new CliError(`LeCodes Desktop ${version} [${key}] isn't published yet — \`--desktop\` needs it for the ` + `headless render contract.
|
|
219887
219934
|
Set LECODES_DESKTOP_EXE to a locally built host, or update lecodes-cli.`);
|
|
219888
219935
|
}
|
|
219889
|
-
note(`Downloading
|
|
219936
|
+
note(`Downloading LeCodes Desktop ${version} [${key}] (${(file.size / 1024 / 1024).toFixed(1)} MB)…`);
|
|
219890
219937
|
await downloadReleaseArchive(file, {
|
|
219891
219938
|
cacheRoot: cacheRoot2(),
|
|
219892
219939
|
dest,
|
|
219893
|
-
validate: (dir) => findExe(dir) ? null : "The downloaded archive doesn't contain the desktop host binary."
|
|
219940
|
+
validate: (dir) => findExe(dir) ? null : "The downloaded archive doesn't contain the desktop host binary.",
|
|
219941
|
+
extract: file.url.toLowerCase().endsWith(".dmg") ? extractDmg : undefined
|
|
219894
219942
|
});
|
|
219895
219943
|
};
|
|
219944
|
+
var findMacHostExe = () => {
|
|
219945
|
+
const candidates = [
|
|
219946
|
+
...["/Applications", join18(homedir6(), "Applications")].flatMap((root) => ["LeCodes Desktop.app", "LeCodes.app"].map((app) => join18(root, app)))
|
|
219947
|
+
].map((app) => join18(app, "Contents", "MacOS", "lecodes-app"));
|
|
219948
|
+
return candidates.find((exe) => existsSync17(exe)) ?? null;
|
|
219949
|
+
};
|
|
219950
|
+
var resolveMacExe = async (selected) => {
|
|
219951
|
+
const cached = findExe(join18(cacheRoot2(), `desktop-${selected}`));
|
|
219952
|
+
if (cached)
|
|
219953
|
+
return cached;
|
|
219954
|
+
try {
|
|
219955
|
+
const { versions } = await fetchDesktopManifest();
|
|
219956
|
+
const version = pickMacVersion(versions, selected, platformKey2());
|
|
219957
|
+
if (!version) {
|
|
219958
|
+
throw new CliError(`No LeCodes Desktop release for ${platformKey2()} is published at or above ${DESKTOP_RENDERER_VERSION}.`);
|
|
219959
|
+
}
|
|
219960
|
+
const dir = join18(cacheRoot2(), `desktop-${version}`);
|
|
219961
|
+
let exe = findExe(dir);
|
|
219962
|
+
if (!exe) {
|
|
219963
|
+
await downloadDesktop(version, dir);
|
|
219964
|
+
exe = findExe(dir);
|
|
219965
|
+
if (!exe)
|
|
219966
|
+
throw new CliError(`Renderer cache at ${dir} is broken — delete the folder and retry.`);
|
|
219967
|
+
}
|
|
219968
|
+
if (version !== selected) {
|
|
219969
|
+
note(`LeCodes Desktop ${selected} has no macOS build — using ${version}.`);
|
|
219970
|
+
mkdirSync11(cacheRoot2(), { recursive: true });
|
|
219971
|
+
writeFileSync13(currentFile(), JSON.stringify({ version }, null, 2));
|
|
219972
|
+
}
|
|
219973
|
+
return exe;
|
|
219974
|
+
} catch (e2) {
|
|
219975
|
+
const installed = findMacHostExe();
|
|
219976
|
+
if (installed) {
|
|
219977
|
+
warnErr(`${e2 instanceof Error ? e2.message : String(e2)}
|
|
219978
|
+
Falling back to the installed host: ${installed}`);
|
|
219979
|
+
return installed;
|
|
219980
|
+
}
|
|
219981
|
+
throw new CliError(`${e2 instanceof Error ? e2.message : String(e2)}
|
|
219982
|
+
` + " No LeCodes Desktop.app is installed either — retry online (`lecodes desktop update`), install the DMG into\n" + " /Applications, or point LECODES_DESKTOP_EXE at a build (…/LeCodes Desktop.app/Contents/MacOS/lecodes-app).");
|
|
219983
|
+
}
|
|
219984
|
+
};
|
|
219896
219985
|
var resolveDesktopExe = async (renderer = "gl") => {
|
|
219897
219986
|
const override = process.env.LECODES_DESKTOP_EXE;
|
|
219898
219987
|
if (override) {
|
|
@@ -219903,6 +219992,8 @@ var resolveDesktopExe = async (renderer = "gl") => {
|
|
|
219903
219992
|
if (renderer === "vulkan")
|
|
219904
219993
|
platformKey2(renderer);
|
|
219905
219994
|
const version = selectedDesktopVersion();
|
|
219995
|
+
if (isMac())
|
|
219996
|
+
return resolveMacExe(version);
|
|
219906
219997
|
const dir = join18(cacheRoot2(), `desktop-${version}${renderer === "vulkan" ? "-vulkan" : ""}`);
|
|
219907
219998
|
let exe = existsSync17(dir) ? findExe(dir) : null;
|
|
219908
219999
|
if (!exe) {
|
|
@@ -219914,13 +220005,23 @@ var resolveDesktopExe = async (renderer = "gl") => {
|
|
|
219914
220005
|
return exe;
|
|
219915
220006
|
};
|
|
219916
220007
|
var updateDesktop = async () => {
|
|
219917
|
-
const
|
|
220008
|
+
const manifest = await fetchDesktopManifest();
|
|
220009
|
+
const latest = isMac() ? pickMacVersion(manifest.versions, manifest.latest, platformKey2()) : manifest.latest;
|
|
220010
|
+
if (!latest)
|
|
220011
|
+
throw new CliError(`No LeCodes Desktop release for ${platformKey2()} is published at or above ${DESKTOP_RENDERER_VERSION}.`);
|
|
219918
220012
|
if (semverNewer(latest, DESKTOP_RENDERER_VERSION) > 0) {
|
|
219919
220013
|
throw new CliError(`The newest published LeCodes Desktop is ${latest}, but this CLI needs ≥ ${DESKTOP_RENDERER_VERSION} ` + `for the render contract. Publish the desktop release first.`);
|
|
219920
220014
|
}
|
|
219921
220015
|
const before = selectedDesktopVersion();
|
|
219922
220016
|
const dir = join18(cacheRoot2(), `desktop-${latest}`);
|
|
219923
|
-
const
|
|
220017
|
+
const published = (await fetchDesktopManifest()).versions.find((v) => v.version === latest)?.files[platformKey2()];
|
|
220018
|
+
const cached = existsSync17(dir) && findExe(dir) !== null;
|
|
220019
|
+
const republished = cached && published !== undefined && cachedArtifactSha(dir) !== published.sha256;
|
|
220020
|
+
if (republished) {
|
|
220021
|
+
note(`LeCodes Desktop ${latest} was republished since this machine cached it — downloading the current build.`);
|
|
220022
|
+
rmSync4(dir, { recursive: true, force: true });
|
|
220023
|
+
}
|
|
220024
|
+
const hadIt = cached && !republished;
|
|
219924
220025
|
if (!hadIt)
|
|
219925
220026
|
await downloadDesktop(latest, dir);
|
|
219926
220027
|
mkdirSync11(cacheRoot2(), { recursive: true });
|
|
@@ -220019,6 +220120,9 @@ var runDesktopDev = async (url, opts = {}) => {
|
|
|
220019
220120
|
});
|
|
220020
220121
|
};
|
|
220021
220122
|
var runDesktopRender = async (js, resources, opts) => {
|
|
220123
|
+
if (isMac()) {
|
|
220124
|
+
throw new CliError("`lecodes render --desktop` isn't available on macOS yet — the macOS host has no headless mode " + "(CREATOR_HIDDEN / CREATOR_DUMP_PNG). `lecodes desktop run` and `lecodes desktop build` do work there.");
|
|
220125
|
+
}
|
|
220022
220126
|
const exe = await resolveDesktopExe(opts.renderer);
|
|
220023
220127
|
const runDir = stageBundle(js, resources);
|
|
220024
220128
|
try {
|
|
@@ -220087,15 +220191,333 @@ ${tail}` : "";
|
|
|
220087
220191
|
|
|
220088
220192
|
// src/commands/appDesktop.ts
|
|
220089
220193
|
init_util();
|
|
220194
|
+
|
|
220195
|
+
// src/commands/appDesktopMac.ts
|
|
220196
|
+
init_util();
|
|
220197
|
+
import { execFileSync as execFileSync4, spawn as spawn3, spawnSync as spawnSync5 } from "node:child_process";
|
|
220198
|
+
import { cpSync as cpSync2, existsSync as existsSync18, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, readdirSync as readdirSync7, rmSync as rmSync5, statSync as statSync5, writeFileSync as writeFileSync14 } from "node:fs";
|
|
220199
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
220200
|
+
import { basename as basename6, join as join19, resolve as resolve6 } from "node:path";
|
|
220090
220201
|
var mb = (bytes) => `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
220091
220202
|
var dirSize = (dir) => readdirSync7(dir, { withFileTypes: true }).reduce((sum, e2) => {
|
|
220092
220203
|
const p2 = join19(dir, e2.name);
|
|
220093
|
-
return sum + (e2.isDirectory() ? dirSize(p2) : statSync5(p2).size);
|
|
220204
|
+
return sum + (e2.isDirectory() ? dirSize(p2) : e2.isSymbolicLink() ? 0 : statSync5(p2).size);
|
|
220205
|
+
}, 0);
|
|
220206
|
+
var cleanBundleName = (name) => name.replace(/[/:]/g, "").trim() || "App";
|
|
220207
|
+
var macHostAppOf = (exe) => {
|
|
220208
|
+
const app = resolve6(exe, "..", "..", "..");
|
|
220209
|
+
if (!app.endsWith(".app") || !existsSync18(join19(app, "Contents", "Info.plist"))) {
|
|
220210
|
+
throw new CliError(`The macOS host must be an .app bundle (got ${exe}). Install LeCodes Desktop.app, or point ` + `LECODES_DESKTOP_EXE at <Build>.app/Contents/MacOS/lecodes-app — a bare SwiftPM binary has no ` + `resource bundles and can't be re-signed into an app.`);
|
|
220211
|
+
}
|
|
220212
|
+
return app;
|
|
220213
|
+
};
|
|
220214
|
+
var macHostProvidedNames = (exe) => {
|
|
220215
|
+
const names3 = new Set;
|
|
220216
|
+
const walk = (dir) => {
|
|
220217
|
+
for (const e2 of readdirSync7(dir, { withFileTypes: true })) {
|
|
220218
|
+
if (e2.isDirectory())
|
|
220219
|
+
walk(join19(dir, e2.name));
|
|
220220
|
+
else
|
|
220221
|
+
names3.add(e2.name);
|
|
220222
|
+
}
|
|
220223
|
+
};
|
|
220224
|
+
const res = join19(macHostAppOf(exe), "Contents", "Resources");
|
|
220225
|
+
if (existsSync18(res))
|
|
220226
|
+
walk(res);
|
|
220227
|
+
return names3;
|
|
220228
|
+
};
|
|
220229
|
+
var plistRead = (plist, key) => {
|
|
220230
|
+
const r2 = spawnSync5("/usr/libexec/PlistBuddy", ["-c", `Print :${key}`, plist], { encoding: "utf8" });
|
|
220231
|
+
return r2.status === 0 ? r2.stdout.trim() : null;
|
|
220232
|
+
};
|
|
220233
|
+
var plistSet = (plist, key, value) => {
|
|
220234
|
+
const quoted = `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
220235
|
+
const set = spawnSync5("/usr/libexec/PlistBuddy", ["-c", `Set :${key} ${quoted}`, plist], { stdio: "ignore" });
|
|
220236
|
+
if (set.status !== 0)
|
|
220237
|
+
execFileSync4("/usr/libexec/PlistBuddy", ["-c", `Add :${key} string ${quoted}`, plist], { stdio: "ignore" });
|
|
220238
|
+
};
|
|
220239
|
+
var macHostVersion = (exe) => {
|
|
220240
|
+
const plist = join19(macHostAppOf(exe), "Contents", "Info.plist");
|
|
220241
|
+
return plistRead(plist, "LeCodesHostVersion") ?? plistRead(plist, "CFBundleShortVersionString") ?? "unknown";
|
|
220242
|
+
};
|
|
220243
|
+
var macHostFeatures = (exe) => {
|
|
220244
|
+
const raw = plistRead(join19(macHostAppOf(exe), "Contents", "Info.plist"), "LeCodesHostFeatures") ?? "";
|
|
220245
|
+
return new Set(raw.split(`
|
|
220246
|
+
`).map((l2) => l2.trim()).filter((l2) => l2 && l2 !== "Array {" && l2 !== "}"));
|
|
220247
|
+
};
|
|
220248
|
+
var assertMacHostCanBundle = (exe) => {
|
|
220249
|
+
if (macHostFeatures(exe).has("project-bundle"))
|
|
220250
|
+
return;
|
|
220251
|
+
throw new CliError(`This LeCodes Desktop host (${macHostAppOf(exe)}) predates project bundles — an app built from it would ` + `open the host's home screen instead of the project.
|
|
220252
|
+
` + " Run `lecodes desktop update` (needs a published macOS host with project-bundle support), or point " + "LECODES_DESKTOP_EXE at a current build (…/LeCodes Desktop.app/Contents/MacOS/lecodes-app).");
|
|
220253
|
+
};
|
|
220254
|
+
var developerIdInKeychain = () => {
|
|
220255
|
+
const r2 = spawnSync5("security", ["find-identity", "-v", "-p", "codesigning"], { encoding: "utf8" });
|
|
220256
|
+
if (r2.status !== 0)
|
|
220257
|
+
return null;
|
|
220258
|
+
return /"(Developer ID Application: [^"]+)"/.exec(r2.stdout)?.[1] ?? null;
|
|
220259
|
+
};
|
|
220260
|
+
var resolveMacSigning = (cfg, overrides, keychainDeveloperId) => {
|
|
220261
|
+
let identity = overrides.sign ?? overrides.env.LECODES_MACOS_SIGN_ID ?? cfg?.signing?.identity;
|
|
220262
|
+
if (identity === undefined || identity === "" || identity === "auto")
|
|
220263
|
+
identity = keychainDeveloperId() ?? "-";
|
|
220264
|
+
if (identity === "adhoc" || identity === "ad-hoc")
|
|
220265
|
+
identity = "-";
|
|
220266
|
+
const notaryProfile = overrides.noNotarize ? null : overrides.notarize ?? overrides.env.LECODES_NOTARY_PROFILE ?? cfg?.notarize?.profile ?? null;
|
|
220267
|
+
if (notaryProfile && identity === "-") {
|
|
220268
|
+
throw new CliError(`Notarization needs a Developer ID signature — an ad-hoc-signed app can't be notarized.
|
|
220269
|
+
` + ' Set desktop.macos.signing.identity (or --sign) to a "Developer ID Application: …" certificate, ' + "or drop the notarize profile (--no-notarize).");
|
|
220270
|
+
}
|
|
220271
|
+
return { identity, notaryProfile: notaryProfile || null };
|
|
220272
|
+
};
|
|
220273
|
+
var FALLBACK_ENTITLEMENTS = `<?xml version="1.0" encoding="UTF-8"?>
|
|
220274
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
220275
|
+
<plist version="1.0">
|
|
220276
|
+
<dict>
|
|
220277
|
+
<key>com.apple.security.cs.allow-jit</key>
|
|
220278
|
+
<true/>
|
|
220279
|
+
</dict>
|
|
220280
|
+
</plist>
|
|
220281
|
+
`;
|
|
220282
|
+
var entitlementsFor = (hostApp) => {
|
|
220283
|
+
const r2 = spawnSync5("codesign", ["-d", "--entitlements", "-", "--xml", hostApp], { encoding: "utf8" });
|
|
220284
|
+
const xml = r2.status === 0 ? r2.stdout.trim() : "";
|
|
220285
|
+
if (xml.startsWith("<?xml") && xml.includes("com.apple.security.cs.allow-jit"))
|
|
220286
|
+
return xml + `
|
|
220287
|
+
`;
|
|
220288
|
+
return FALLBACK_ENTITLEMENTS;
|
|
220289
|
+
};
|
|
220290
|
+
var codesignRetrying = async (args) => {
|
|
220291
|
+
for (let attempt = 1;; attempt++) {
|
|
220292
|
+
const r2 = spawnSync5("codesign", args, { encoding: "utf8" });
|
|
220293
|
+
if (r2.status === 0)
|
|
220294
|
+
return;
|
|
220295
|
+
const out = (r2.stdout + `
|
|
220296
|
+
` + r2.stderr).trim();
|
|
220297
|
+
if (!/timestamp/i.test(out) || attempt >= 5)
|
|
220298
|
+
throw new CliError(`codesign failed:
|
|
220299
|
+
${out}`);
|
|
220300
|
+
const delay = attempt * 15;
|
|
220301
|
+
warn(`timestamp service unavailable (attempt ${attempt}/5) — retrying in ${delay}s…`);
|
|
220302
|
+
await new Promise((r3) => setTimeout(r3, delay * 1000));
|
|
220303
|
+
}
|
|
220304
|
+
};
|
|
220305
|
+
var signApp = async (app, signing, hostApp, tmp) => {
|
|
220306
|
+
if (signing.identity === "-") {
|
|
220307
|
+
note("Signing ad-hoc (runs on this Mac only — pass a Developer ID identity to distribute)…");
|
|
220308
|
+
await codesignRetrying(["--force", "--sign", "-", app]);
|
|
220309
|
+
return;
|
|
220310
|
+
}
|
|
220311
|
+
note(`Signing with "${signing.identity}" (Hardened Runtime + JIT entitlement)…`);
|
|
220312
|
+
const ent = join19(tmp, "entitlements.plist");
|
|
220313
|
+
writeFileSync14(ent, entitlementsFor(hostApp));
|
|
220314
|
+
await codesignRetrying(["--force", "--options", "runtime", "--timestamp", "--entitlements", ent, "--sign", signing.identity, app]);
|
|
220315
|
+
const verify = spawnSync5("codesign", ["--verify", "--strict", "--verbose=2", app], { encoding: "utf8" });
|
|
220316
|
+
if (verify.status !== 0)
|
|
220317
|
+
throw new CliError(`The signature doesn't verify:
|
|
220318
|
+
${(verify.stdout + verify.stderr).trim()}`);
|
|
220319
|
+
};
|
|
220320
|
+
var notarizeApp = (app, profile, tmp) => {
|
|
220321
|
+
const zip = join19(tmp, `${basename6(app, ".app")}.zip`);
|
|
220322
|
+
execFileSync4("ditto", ["-c", "-k", "--keepParent", app, zip]);
|
|
220323
|
+
note(`Notarizing with profile "${profile}" — this waits on Apple (usually 1–5 minutes)…`);
|
|
220324
|
+
const r2 = spawnSync5("xcrun", ["notarytool", "submit", zip, "--keychain-profile", profile, "--wait"], { encoding: "utf8" });
|
|
220325
|
+
const out = (r2.stdout + `
|
|
220326
|
+
` + r2.stderr).trim();
|
|
220327
|
+
const id = /id: ([0-9a-f-]{36})/.exec(out)?.[1];
|
|
220328
|
+
if (r2.status !== 0 || !/status: Accepted/.test(out)) {
|
|
220329
|
+
throw new CliError(`Notarization failed:
|
|
220330
|
+
${out}
|
|
220331
|
+
` + (id ? ` Details: xcrun notarytool log ${id} --keychain-profile ${profile}` : ""));
|
|
220332
|
+
}
|
|
220333
|
+
const staple = spawnSync5("xcrun", ["stapler", "staple", app], { encoding: "utf8" });
|
|
220334
|
+
if (staple.status !== 0)
|
|
220335
|
+
throw new CliError(`Stapling failed:
|
|
220336
|
+
${(staple.stdout + staple.stderr).trim()}`);
|
|
220337
|
+
success("Notarized + stapled.");
|
|
220338
|
+
};
|
|
220339
|
+
var gatekeeperVerdict = (app) => {
|
|
220340
|
+
const r2 = spawnSync5("spctl", ["-a", "-t", "exec", "-vv", app], { encoding: "utf8" });
|
|
220341
|
+
const line = (r2.stdout + r2.stderr).trim().split(`
|
|
220342
|
+
`).filter((l2) => l2.includes("source=") || l2.includes("rejected") || l2.includes("accepted")).join("; ");
|
|
220343
|
+
return { accepted: r2.status === 0, line: line || (r2.stdout + r2.stderr).trim() };
|
|
220344
|
+
};
|
|
220345
|
+
var writeIcns = async (root, config, dest, tmp) => {
|
|
220346
|
+
const master = await loadIconMaster(root, config);
|
|
220347
|
+
const { Jimp: Jimp2 } = await Promise.resolve().then(() => (init_esm30(), exports_esm2));
|
|
220348
|
+
const SIZE = 1024, INSET = 100, RADIUS = 185;
|
|
220349
|
+
const tileSize = SIZE - 2 * INSET;
|
|
220350
|
+
const tile = new Jimp2({ width: tileSize, height: tileSize, color: master.background });
|
|
220351
|
+
const art = master.image.clone();
|
|
220352
|
+
art.resize({ w: tileSize, h: tileSize });
|
|
220353
|
+
tile.composite(art, 0, 0);
|
|
220354
|
+
const { data } = tile.bitmap;
|
|
220355
|
+
for (let y2 = 0;y2 < tileSize; y2++) {
|
|
220356
|
+
for (let x2 = 0;x2 < tileSize; x2++) {
|
|
220357
|
+
const cx = x2 < RADIUS ? RADIUS : x2 >= tileSize - RADIUS ? tileSize - RADIUS - 1 : x2;
|
|
220358
|
+
const cy = y2 < RADIUS ? RADIUS : y2 >= tileSize - RADIUS ? tileSize - RADIUS - 1 : y2;
|
|
220359
|
+
const dx = x2 - cx, dy = y2 - cy;
|
|
220360
|
+
if (dx * dx + dy * dy > RADIUS * RADIUS)
|
|
220361
|
+
data[(y2 * tileSize + x2) * 4 + 3] = 0;
|
|
220362
|
+
}
|
|
220363
|
+
}
|
|
220364
|
+
const canvas = new Jimp2({ width: SIZE, height: SIZE, color: 0 });
|
|
220365
|
+
canvas.composite(tile, INSET, INSET);
|
|
220366
|
+
const iconset = join19(tmp, "AppIcon.iconset");
|
|
220367
|
+
mkdirSync12(iconset, { recursive: true });
|
|
220368
|
+
for (const pt of [16, 32, 128, 256, 512]) {
|
|
220369
|
+
for (const scale of [1, 2]) {
|
|
220370
|
+
const px = pt * scale;
|
|
220371
|
+
const frame = canvas.clone();
|
|
220372
|
+
frame.resize({ w: px, h: px });
|
|
220373
|
+
writeFileSync14(join19(iconset, `icon_${pt}x${pt}${scale === 2 ? "@2x" : ""}.png`), await frame.getBuffer("image/png"));
|
|
220374
|
+
}
|
|
220375
|
+
}
|
|
220376
|
+
execFileSync4("iconutil", ["-c", "icns", "-o", dest, iconset]);
|
|
220377
|
+
};
|
|
220378
|
+
var probeMacApp = async (app, out, args) => {
|
|
220379
|
+
const after = Number(flagStr(args, "after")) || 8;
|
|
220380
|
+
const shot = join19(out, `${basename6(app, ".app")}-probe.png`);
|
|
220381
|
+
rmSync5(shot, { force: true });
|
|
220382
|
+
note(`Probe: launching the built app for ${after + 2}s (the window will show)…`);
|
|
220383
|
+
await new Promise((done, fail) => {
|
|
220384
|
+
const child = spawn3(join19(app, "Contents", "MacOS", "lecodes-app"), [], {
|
|
220385
|
+
cwd: "/",
|
|
220386
|
+
env: { ...process.env, LECODES_SNAPSHOT: shot, LECODES_SNAPSHOT_AFTER: String(after), LECODES_EXIT_AFTER: String(after + 2) },
|
|
220387
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
220388
|
+
});
|
|
220389
|
+
const tail = [];
|
|
220390
|
+
const sink = (d) => {
|
|
220391
|
+
tail.push(String(d));
|
|
220392
|
+
while (tail.length > 60)
|
|
220393
|
+
tail.shift();
|
|
220394
|
+
};
|
|
220395
|
+
child.stdout.on("data", sink);
|
|
220396
|
+
child.stderr.on("data", sink);
|
|
220397
|
+
const killer = setTimeout(() => {
|
|
220398
|
+
child.kill();
|
|
220399
|
+
fail(new CliError("The probe timed out after 120s."));
|
|
220400
|
+
}, 120000);
|
|
220401
|
+
child.on("error", (e2) => {
|
|
220402
|
+
clearTimeout(killer);
|
|
220403
|
+
fail(new CliError(`Probe couldn't run: ${e2.message}`));
|
|
220404
|
+
});
|
|
220405
|
+
child.on("exit", (code) => {
|
|
220406
|
+
clearTimeout(killer);
|
|
220407
|
+
const detail = tail.length > 0 ? `
|
|
220408
|
+
${tail.join("").trimEnd()}` : "";
|
|
220409
|
+
if (code === 0 && existsSync18(shot)) {
|
|
220410
|
+
success(`Probe: ${shot}`);
|
|
220411
|
+
done();
|
|
220412
|
+
} else
|
|
220413
|
+
fail(new CliError(`Probe failed (exit ${code}).${detail}`));
|
|
220414
|
+
});
|
|
220415
|
+
});
|
|
220416
|
+
};
|
|
220417
|
+
var buildMacApp = async (input) => {
|
|
220418
|
+
const { root, config, name, slug, out, js, shipped, hostExe, args } = input;
|
|
220419
|
+
const hostApp = macHostAppOf(hostExe);
|
|
220420
|
+
assertMacHostCanBundle(hostExe);
|
|
220421
|
+
const mac = config.desktop?.macos ?? {};
|
|
220422
|
+
const appName = cleanBundleName(name);
|
|
220423
|
+
const app = join19(out, `${appName}.app`);
|
|
220424
|
+
const tmp = mkdtempSync3(join19(tmpdir3(), "lecodes-macapp-"));
|
|
220425
|
+
try {
|
|
220426
|
+
note(` host: LeCodes Desktop ${macHostVersion(hostExe)} (${hostApp})`);
|
|
220427
|
+
rmSync5(out, { recursive: true, force: true });
|
|
220428
|
+
mkdirSync12(out, { recursive: true });
|
|
220429
|
+
execFileSync4("cp", ["-R", hostApp, app]);
|
|
220430
|
+
execFileSync4("xattr", ["-cr", app]);
|
|
220431
|
+
rmSync5(join19(app, "Contents", "_CodeSignature"), { recursive: true, force: true });
|
|
220432
|
+
rmSync5(join19(app, "Contents", "CodeResources"), { force: true });
|
|
220433
|
+
const proj = join19(app, "Contents", "Resources", "project");
|
|
220434
|
+
mkdirSync12(join19(proj, "assets"), { recursive: true });
|
|
220435
|
+
writeFileSync14(join19(proj, "app.js"), js);
|
|
220436
|
+
let assetBytes = 0, assetCount = 0;
|
|
220437
|
+
for (const r2 of shipped) {
|
|
220438
|
+
if (!r2.absPath || !existsSync18(r2.absPath) || !statSync5(r2.absPath).isFile()) {
|
|
220439
|
+
warn(`asset "${r2.name}" (${r2.path}) has no local file — skipped`);
|
|
220440
|
+
continue;
|
|
220441
|
+
}
|
|
220442
|
+
cpSync2(r2.absPath, join19(proj, "assets", r2.name));
|
|
220443
|
+
assetBytes += statSync5(r2.absPath).size;
|
|
220444
|
+
assetCount++;
|
|
220445
|
+
}
|
|
220446
|
+
note(` assets: ${assetCount} files, ${mb(assetBytes)} → Contents/Resources/project/assets/`);
|
|
220447
|
+
writeJson(join19(proj, "app.json"), { name, ...config.desktop ? { desktop: { window: config.desktop.window } } : {} });
|
|
220448
|
+
const plist = join19(app, "Contents", "Info.plist");
|
|
220449
|
+
const bundleId = mac.bundleId ?? `codes.le.app.${slug.toLowerCase().replace(/[^a-z0-9.-]+/g, "-")}`;
|
|
220450
|
+
plistSet(plist, "CFBundleName", appName);
|
|
220451
|
+
plistSet(plist, "CFBundleDisplayName", appName);
|
|
220452
|
+
plistSet(plist, "CFBundleIdentifier", bundleId);
|
|
220453
|
+
plistSet(plist, "CFBundleShortVersionString", config.version ?? "1.0");
|
|
220454
|
+
plistSet(plist, "CFBundleVersion", String(mac.buildNumber ?? 1));
|
|
220455
|
+
if (mac.category)
|
|
220456
|
+
plistSet(plist, "LSApplicationCategoryType", mac.category);
|
|
220457
|
+
if (config.icon) {
|
|
220458
|
+
await writeIcns(root, config, join19(app, "Contents", "Resources", "AppIcon.icns"), tmp);
|
|
220459
|
+
plistSet(plist, "CFBundleIconFile", "AppIcon");
|
|
220460
|
+
note(" icon: AppIcon.icns rendered from " + config.icon);
|
|
220461
|
+
}
|
|
220462
|
+
note(` bundle: ${bundleId} ${config.version ?? "1.0"} (${mac.buildNumber ?? 1})`);
|
|
220463
|
+
const signing = resolveMacSigning(mac, {
|
|
220464
|
+
sign: flagStr(args, "sign"),
|
|
220465
|
+
notarize: flagStr(args, "notarize"),
|
|
220466
|
+
noNotarize: flagBool(args, "no-notarize"),
|
|
220467
|
+
env: process.env
|
|
220468
|
+
}, developerIdInKeychain);
|
|
220469
|
+
await signApp(app, signing, hostApp, tmp);
|
|
220470
|
+
if (signing.notaryProfile) {
|
|
220471
|
+
notarizeApp(app, signing.notaryProfile, tmp);
|
|
220472
|
+
} else if (signing.identity !== "-") {
|
|
220473
|
+
warn("Signed with Developer ID but NOT notarized — Gatekeeper refuses a downloaded copy on every other Mac.");
|
|
220474
|
+
warn(" Set desktop.macos.notarize.profile in app.json (a `xcrun notarytool store-credentials` profile) or pass --notarize <profile>.");
|
|
220475
|
+
}
|
|
220476
|
+
const verdict = gatekeeperVerdict(app);
|
|
220477
|
+
log(`${verdict.accepted ? c.green("gatekeeper") : c.yellow("gatekeeper")} ${verdict.line}`);
|
|
220478
|
+
if (signing.notaryProfile && !verdict.accepted) {
|
|
220479
|
+
throw new CliError("The app was notarized but Gatekeeper still rejects it — see the spctl line above.");
|
|
220480
|
+
}
|
|
220481
|
+
writeFileSync14(join19(out, "README.txt"), readmeText(appName, !!signing.notaryProfile, signing.identity !== "-"));
|
|
220482
|
+
success(`App: ${app} (${mb(dirSize(app))})`);
|
|
220483
|
+
if (flagBool(args, "probe"))
|
|
220484
|
+
await probeMacApp(app, out, args);
|
|
220485
|
+
if (flagBool(args, "zip")) {
|
|
220486
|
+
const zip = join19(out, `${appName}.zip`);
|
|
220487
|
+
rmSync5(zip, { force: true });
|
|
220488
|
+
execFileSync4("ditto", ["-c", "-k", "--keepParent", app, zip]);
|
|
220489
|
+
success(`Zip: ${zip} (${mb(statSync5(zip).size)})`);
|
|
220490
|
+
} else {
|
|
220491
|
+
note("Tip: --zip packs the .app for sending (ditto, structure-preserving).");
|
|
220492
|
+
}
|
|
220493
|
+
} finally {
|
|
220494
|
+
rmSync5(tmp, { recursive: true, force: true });
|
|
220495
|
+
}
|
|
220496
|
+
};
|
|
220497
|
+
var readmeText = (name, notarized, developerId) => `${name}
|
|
220498
|
+
${"=".repeat(Math.max(name.length, 4))}
|
|
220499
|
+
|
|
220500
|
+
A LeCodes desktop app for macOS (Apple Silicon). Nothing to install: unzip and double-click
|
|
220501
|
+
${name}.app — or drag it to Applications first.
|
|
220502
|
+
|
|
220503
|
+
${notarized ? `The app is signed and notarized by Apple: macOS asks once whether to open a downloaded app,
|
|
220504
|
+
then it runs.` : developerId ? `The app is signed but NOT notarized: macOS will refuse to open the downloaded copy. To run it
|
|
220505
|
+
anyway: System Settings › Privacy & Security › "Open Anyway" (after the first refused launch),
|
|
220506
|
+
or remove the quarantine flag in Terminal: xattr -dr com.apple.quarantine "` + name + '.app"' : `The app is not signed for distribution: macOS will refuse to open the downloaded copy. To run it
|
|
220507
|
+
anyway: System Settings › Privacy & Security › "Open Anyway" (after the first refused launch),
|
|
220508
|
+
or remove the quarantine flag in Terminal: xattr -dr com.apple.quarantine "` + name + '.app"'}
|
|
220509
|
+
`;
|
|
220510
|
+
|
|
220511
|
+
// src/commands/appDesktop.ts
|
|
220512
|
+
var mb2 = (bytes) => `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
220513
|
+
var dirSize2 = (dir) => readdirSync8(dir, { withFileTypes: true }).reduce((sum, e2) => {
|
|
220514
|
+
const p2 = join20(dir, e2.name);
|
|
220515
|
+
return sum + (e2.isDirectory() ? dirSize2(p2) : statSync6(p2).size);
|
|
220094
220516
|
}, 0);
|
|
220095
220517
|
var initDesktop = async (args) => {
|
|
220096
220518
|
const root = projectRoot();
|
|
220097
|
-
const existing = readJson(
|
|
220098
|
-
const name = flagStr(args, "name") ?? existing.name ??
|
|
220519
|
+
const existing = readJson(join20(root, "app.json")) ?? {};
|
|
220520
|
+
const name = flagStr(args, "name") ?? existing.name ?? basename7(root);
|
|
220099
220521
|
const window2 = {
|
|
220100
220522
|
width: 1280,
|
|
220101
220523
|
height: 800,
|
|
@@ -220105,7 +220527,7 @@ var initDesktop = async (args) => {
|
|
|
220105
220527
|
...flagBool(args, "fullscreen") ? { fullscreen: true } : {}
|
|
220106
220528
|
};
|
|
220107
220529
|
const config = { ...existing, name, desktop: { ...existing.desktop, window: window2 } };
|
|
220108
|
-
writeJson(
|
|
220530
|
+
writeJson(join20(root, "app.json"), config);
|
|
220109
220531
|
log(`${c.green("written")} app.json (desktop.window ${window2.width}×${window2.height}${window2.fullscreen ? ", fullscreen" : ""})`);
|
|
220110
220532
|
log(`Next: ${c.bold("lecodes dev --desktop")} to develop, ${c.bold("lecodes app build desktop")} to package.`);
|
|
220111
220533
|
};
|
|
@@ -220114,13 +220536,13 @@ var ASSETS_DIR = "assets";
|
|
|
220114
220536
|
var RUNTIME_DIR = "runtime";
|
|
220115
220537
|
var buildDesktop = async (args) => {
|
|
220116
220538
|
const root = projectRoot();
|
|
220117
|
-
const config = readJson(
|
|
220118
|
-
const name = (config.name ??
|
|
220539
|
+
const config = readJson(join20(root, "app.json")) ?? {};
|
|
220540
|
+
const name = (config.name ?? basename7(root)).trim() || "app";
|
|
220119
220541
|
const slug = flagStr(args, "slug") ?? cleanFileName(name);
|
|
220120
|
-
const out =
|
|
220121
|
-
if (
|
|
220122
|
-
const entries =
|
|
220123
|
-
const looksLikeBuild = entries.length === 0 || entries.includes("app.js");
|
|
220542
|
+
const out = resolve7(root, flagStr(args, "o", "out") ?? join20(root, "dist", slug));
|
|
220543
|
+
if (existsSync19(out)) {
|
|
220544
|
+
const entries = readdirSync8(out);
|
|
220545
|
+
const looksLikeBuild = entries.length === 0 || entries.includes("app.js") || entries.some((e2) => e2.endsWith(".app"));
|
|
220124
220546
|
if (!looksLikeBuild) {
|
|
220125
220547
|
throw new CliError(`${out} exists and doesn't look like a previous desktop build — pick another -o or clear it yourself.`);
|
|
220126
220548
|
}
|
|
@@ -220128,7 +220550,8 @@ var buildDesktop = async (args) => {
|
|
|
220128
220550
|
if (config.desktop?.entry && flagStr(args, "entry") === undefined)
|
|
220129
220551
|
args.flags.entry = config.desktop.entry;
|
|
220130
220552
|
const renderer = resolveDesktopRenderer(root, args);
|
|
220131
|
-
|
|
220553
|
+
const mac = process.platform === "darwin";
|
|
220554
|
+
note(`Building "${name}" for ${mac ? "macOS" : "desktop"}${renderer === "vulkan" ? " (Vulkan)" : ""}…`);
|
|
220132
220555
|
const { js, resources } = await buildBundle(args, { remotePatterns: config.resources?.remote ?? [] }, { localShaders: true, desktopRenderer: renderer });
|
|
220133
220556
|
assertRendererSupportsBundle(renderer, js);
|
|
220134
220557
|
const wanted = new Set([...js.matchAll(/fetchLocal\("([^"]+)"\)/g)].map((m) => m[1]));
|
|
@@ -220152,55 +220575,59 @@ var buildDesktop = async (args) => {
|
|
|
220152
220575
|
const exe = await resolveDesktopExe(renderer);
|
|
220153
220576
|
const hostDir = dirname11(exe);
|
|
220154
220577
|
const IMPLICIT_PRELOADS = new Set(["uberarchive", "capture"]);
|
|
220155
|
-
const hostProvided = new Set;
|
|
220156
|
-
|
|
220157
|
-
|
|
220158
|
-
|
|
220159
|
-
|
|
220578
|
+
const hostProvided = mac ? macHostProvidedNames(exe) : new Set;
|
|
220579
|
+
if (!mac) {
|
|
220580
|
+
try {
|
|
220581
|
+
for (const f3 of readdirSync8(join20(hostDir, "assets")))
|
|
220582
|
+
hostProvided.add(f3);
|
|
220583
|
+
} catch {}
|
|
220584
|
+
}
|
|
220160
220585
|
const shippedNames = new Set(shipped.map((r2) => r2.name));
|
|
220161
220586
|
const missing = [...wanted].filter((n2) => !shippedNames.has(n2) && !hostProvided.has(n2) && !IMPLICIT_PRELOADS.has(n2));
|
|
220162
220587
|
if (missing.length > 0) {
|
|
220163
220588
|
throw new CliError(`The bundle references assets with no local file: ${missing.join(", ")}`);
|
|
220164
220589
|
}
|
|
220590
|
+
if (mac)
|
|
220591
|
+
return buildMacApp({ root, config, name, slug, out, js, shipped, hostExe: exe, args });
|
|
220165
220592
|
const version = process.env.LECODES_DESKTOP_EXE ? "local build" : selectedDesktopVersion();
|
|
220166
|
-
|
|
220167
|
-
|
|
220168
|
-
for (const entry of
|
|
220593
|
+
rmSync6(out, { recursive: true, force: true });
|
|
220594
|
+
mkdirSync13(out, { recursive: true });
|
|
220595
|
+
for (const entry of readdirSync8(hostDir, { withFileTypes: true })) {
|
|
220169
220596
|
if (entry.isFile() && entry.name.toLowerCase().endsWith(".dll")) {
|
|
220170
|
-
|
|
220597
|
+
cpSync3(join20(hostDir, entry.name), join20(out, entry.name));
|
|
220171
220598
|
} else if (entry.isDirectory() && entry.name === "assets") {
|
|
220172
|
-
|
|
220599
|
+
cpSync3(join20(hostDir, entry.name), join20(out, RUNTIME_DIR), { recursive: true });
|
|
220173
220600
|
} else if (entry.isDirectory() && entry.name === "fonts") {
|
|
220174
|
-
|
|
220601
|
+
cpSync3(join20(hostDir, entry.name), join20(out, entry.name), { recursive: true });
|
|
220175
220602
|
}
|
|
220176
220603
|
}
|
|
220177
|
-
if (!
|
|
220604
|
+
if (!existsSync19(join20(out, RUNTIME_DIR, "initial.js"))) {
|
|
220178
220605
|
throw new CliError(`The host at ${hostDir} has no assets/initial.js next to it — a staged build can't boot. ` + `(LECODES_DESKTOP_EXE must point at an exe with its runtime assets/ beside it.)`);
|
|
220179
220606
|
}
|
|
220180
220607
|
const exeName2 = process.platform === "win32" ? `${cleanFileName(name)}.exe` : cleanFileName(name);
|
|
220181
|
-
|
|
220608
|
+
cpSync3(exe, join20(out, exeName2));
|
|
220182
220609
|
note(` host: LeCodes Desktop ${version}`);
|
|
220183
|
-
|
|
220610
|
+
writeFileSync15(join20(out, "app.js"), js);
|
|
220184
220611
|
let assetBytes = 0, assetCount = 0;
|
|
220185
220612
|
if (shipped.length > 0)
|
|
220186
|
-
|
|
220613
|
+
mkdirSync13(join20(out, ASSETS_DIR), { recursive: true });
|
|
220187
220614
|
for (const r2 of shipped) {
|
|
220188
|
-
if (!r2.absPath || !
|
|
220615
|
+
if (!r2.absPath || !existsSync19(r2.absPath) || !statSync6(r2.absPath).isFile()) {
|
|
220189
220616
|
warn(`asset "${r2.name}" (${r2.path}) has no local file — skipped`);
|
|
220190
220617
|
continue;
|
|
220191
220618
|
}
|
|
220192
|
-
|
|
220193
|
-
assetBytes +=
|
|
220619
|
+
cpSync3(r2.absPath, join20(out, ASSETS_DIR, r2.name));
|
|
220620
|
+
assetBytes += statSync6(r2.absPath).size;
|
|
220194
220621
|
assetCount++;
|
|
220195
220622
|
}
|
|
220196
|
-
note(` assets: ${assetCount} files, ${
|
|
220197
|
-
writeJson(
|
|
220623
|
+
note(` assets: ${assetCount} files, ${mb2(assetBytes)} → ${ASSETS_DIR}/`);
|
|
220624
|
+
writeJson(join20(out, "app.json"), { name, ...config.desktop ? { desktop: { window: config.desktop.window } } : {} });
|
|
220198
220625
|
if (config.icon) {
|
|
220199
|
-
await writeIco(root, config,
|
|
220626
|
+
await writeIco(root, config, join20(out, "app.ico"));
|
|
220200
220627
|
note(" app.ico rendered from " + config.icon);
|
|
220201
220628
|
}
|
|
220202
|
-
|
|
220203
|
-
success(`Portable build: ${out} (${
|
|
220629
|
+
writeFileSync15(join20(out, "README.txt"), "\uFEFF" + readmeText2(name, exeName2, version));
|
|
220630
|
+
success(`Portable build: ${out} (${mb2(dirSize2(out))})`);
|
|
220204
220631
|
if (flagBool(args, "probe"))
|
|
220205
220632
|
await probeBuild(out, exeName2, config, args);
|
|
220206
220633
|
if (flagBool(args, "zip"))
|
|
@@ -220232,15 +220659,15 @@ var writeIco = async (root, config, dest) => {
|
|
|
220232
220659
|
header.writeUInt32LE(offset, e2 + 12);
|
|
220233
220660
|
offset += pngs[i2].length;
|
|
220234
220661
|
});
|
|
220235
|
-
|
|
220662
|
+
writeFileSync15(dest, Buffer.concat([header, ...pngs]));
|
|
220236
220663
|
};
|
|
220237
220664
|
var probeBuild = async (out, exeName2, config, args) => {
|
|
220238
220665
|
const win = config.desktop?.window;
|
|
220239
220666
|
const frames = Number(flagStr(args, "frames")) || 90;
|
|
220240
|
-
const shot =
|
|
220667
|
+
const shot = join20(dirname11(out), `${basename7(out)}-probe.png`);
|
|
220241
220668
|
note(`Probe: hidden render of the staged folder (${frames} frames)…`);
|
|
220242
220669
|
await new Promise((done, fail) => {
|
|
220243
|
-
const child =
|
|
220670
|
+
const child = spawn4(join20(out, exeName2), [], {
|
|
220244
220671
|
cwd: out,
|
|
220245
220672
|
env: {
|
|
220246
220673
|
...process.env,
|
|
@@ -220269,7 +220696,7 @@ var probeBuild = async (out, exeName2, config, args) => {
|
|
|
220269
220696
|
});
|
|
220270
220697
|
child.on("exit", (code) => {
|
|
220271
220698
|
clearTimeout(killer);
|
|
220272
|
-
if (code === 0 &&
|
|
220699
|
+
if (code === 0 && existsSync19(shot)) {
|
|
220273
220700
|
success(`Probe: ${shot}`);
|
|
220274
220701
|
done();
|
|
220275
220702
|
} else {
|
|
@@ -220282,11 +220709,11 @@ ${tail.join("").trimEnd()}` : "";
|
|
|
220282
220709
|
};
|
|
220283
220710
|
var zipBuild = (out) => {
|
|
220284
220711
|
const zip = `${out}.zip`;
|
|
220285
|
-
|
|
220712
|
+
rmSync6(zip, { force: true });
|
|
220286
220713
|
note("Zipping…");
|
|
220287
220714
|
if (process.platform === "win32") {
|
|
220288
220715
|
const q = (s2) => s2.replaceAll("'", "''");
|
|
220289
|
-
|
|
220716
|
+
execFileSync5("powershell", ["-NoProfile", "-Command", [
|
|
220290
220717
|
"Add-Type -AssemblyName System.IO.Compression.FileSystem;",
|
|
220291
220718
|
`$src='${q(out)}'; $base=Split-Path $src -Parent;`,
|
|
220292
220719
|
`$zip=[System.IO.Compression.ZipFile]::Open('${q(zip)}','Create');`,
|
|
@@ -220296,11 +220723,11 @@ var zipBuild = (out) => {
|
|
|
220296
220723
|
"$zip.Dispose()"
|
|
220297
220724
|
].join(" ")], { stdio: ["ignore", "ignore", "pipe"] });
|
|
220298
220725
|
} else {
|
|
220299
|
-
|
|
220726
|
+
execFileSync5("zip", ["-rq", zip, basename7(out)], { cwd: dirname11(out) });
|
|
220300
220727
|
}
|
|
220301
|
-
success(`Zip: ${zip} (${
|
|
220728
|
+
success(`Zip: ${zip} (${mb2(statSync6(zip).size)})`);
|
|
220302
220729
|
};
|
|
220303
|
-
var
|
|
220730
|
+
var readmeText2 = (name, exeName2, version) => `${name}
|
|
220304
220731
|
${"=".repeat(Math.max(name.length, 4))}
|
|
220305
220732
|
|
|
220306
220733
|
A LeCodes desktop app. Nothing to install: unpack the WHOLE folder anywhere and run ${exeName2}.
|
|
@@ -220319,10 +220746,10 @@ var SDK_REPO = "letary/lecodes-ios-sdk";
|
|
|
220319
220746
|
var SDK_MIN_VERSION = "1.2.0";
|
|
220320
220747
|
var SDK_IDENTITY_VERSION = "1.6.0";
|
|
220321
220748
|
var shellSdkVersion = (iosDir) => {
|
|
220322
|
-
const path2 =
|
|
220323
|
-
if (!
|
|
220749
|
+
const path2 = join21(iosDir, "LeCodesRuntime", "Package.swift");
|
|
220750
|
+
if (!existsSync20(path2))
|
|
220324
220751
|
return null;
|
|
220325
|
-
const m = /lecodes-ios-sdk\.git",\s*(?:exact|from):\s*"([^"]+)"/.exec(
|
|
220752
|
+
const m = /lecodes-ios-sdk\.git",\s*(?:exact|from):\s*"([^"]+)"/.exec(readFileSync17(path2, "utf8"));
|
|
220326
220753
|
return m && VERSION_RE.test(m[1]) ? m[1] : null;
|
|
220327
220754
|
};
|
|
220328
220755
|
var IOS_SDK_OPTS = { repo: SDK_REPO, minVersion: SDK_MIN_VERSION, configKey: "ios.sdk" };
|
|
@@ -220336,11 +220763,11 @@ var appInit = async (args) => {
|
|
|
220336
220763
|
throw new CliError(platform ? `Unsupported platform "${platform}" — use ios, android or desktop.` : "Usage: lecodes app init <ios|android|desktop> [--name <display name>] [--bundle-id <id>] [--team <team id>] [--variant auto|core|2d|3d|full] [--app-id <application id>] [--sdk <version|latest>]");
|
|
220337
220764
|
}
|
|
220338
220765
|
const root = projectRoot();
|
|
220339
|
-
const iosDir =
|
|
220340
|
-
if (
|
|
220766
|
+
const iosDir = join21(root, "ios");
|
|
220767
|
+
if (existsSync20(iosDir)) {
|
|
220341
220768
|
throw new CliError("ios/ already exists — run `lecodes app sync` to update it.");
|
|
220342
220769
|
}
|
|
220343
|
-
const existing = readJson(
|
|
220770
|
+
const existing = readJson(join21(root, "app.json")) ?? {};
|
|
220344
220771
|
const variant = flagStr(args, "variant") ?? existing.ios?.variant ?? "auto";
|
|
220345
220772
|
if (variant !== "auto" && !SDK_VARIANTS.includes(variant)) {
|
|
220346
220773
|
throw new CliError(`--variant must be auto, ${SDK_VARIANTS.join(", ")} (got "${variant}")`);
|
|
@@ -220348,7 +220775,7 @@ var appInit = async (args) => {
|
|
|
220348
220775
|
const sdkFlag = flagStr(args, "sdk") ?? existing.ios?.sdk;
|
|
220349
220776
|
const sdkVersion = await resolveSdkVersion(process.env.LECODES_IOS_SDK ?? sdkFlag ?? "latest", null, IOS_SDK_OPTS);
|
|
220350
220777
|
const sdk = sdkFlag === "latest" ? "latest" : sdkVersion;
|
|
220351
|
-
const name = flagStr(args, "name") ?? existing.name ??
|
|
220778
|
+
const name = flagStr(args, "name") ?? existing.name ?? basename8(root);
|
|
220352
220779
|
const config = {
|
|
220353
220780
|
...existing,
|
|
220354
220781
|
name,
|
|
@@ -220362,7 +220789,7 @@ var appInit = async (args) => {
|
|
|
220362
220789
|
buildNumber: existing.ios?.buildNumber ?? 1
|
|
220363
220790
|
}
|
|
220364
220791
|
};
|
|
220365
|
-
writeJson(
|
|
220792
|
+
writeJson(join21(root, "app.json"), config);
|
|
220366
220793
|
log(`${c.green("written")} app.json`);
|
|
220367
220794
|
ensureShellIgnored(root, "ios");
|
|
220368
220795
|
const identity = {
|
|
@@ -220373,9 +220800,9 @@ var appInit = async (args) => {
|
|
|
220373
220800
|
buildNumber: config.ios.buildNumber
|
|
220374
220801
|
};
|
|
220375
220802
|
const write = (rel, content) => {
|
|
220376
|
-
const abs =
|
|
220377
|
-
|
|
220378
|
-
|
|
220803
|
+
const abs = join21(iosDir, rel);
|
|
220804
|
+
mkdirSync14(dirname12(abs), { recursive: true });
|
|
220805
|
+
writeFileSync16(abs, content);
|
|
220379
220806
|
log(`${c.green("created")} ios/${rel}`);
|
|
220380
220807
|
};
|
|
220381
220808
|
write("README.md", IOS_README);
|
|
@@ -220403,23 +220830,23 @@ var ensureSdkResourcesBundle = async (sdkVersion) => {
|
|
|
220403
220830
|
const override = process.env.LECODES_SDK_RESOURCES;
|
|
220404
220831
|
if (override) {
|
|
220405
220832
|
note(`Using local resources bundle ${override} (LECODES_SDK_RESOURCES)`);
|
|
220406
|
-
return
|
|
220833
|
+
return resolve8(override);
|
|
220407
220834
|
}
|
|
220408
|
-
const dir =
|
|
220409
|
-
const bundle =
|
|
220410
|
-
if (
|
|
220835
|
+
const dir = join21(homedir7(), ".lecodes", "sdk-resources", sdkVersion);
|
|
220836
|
+
const bundle = join21(dir, "LeCodesSDKResources.bundle");
|
|
220837
|
+
if (existsSync20(bundle))
|
|
220411
220838
|
return bundle;
|
|
220412
220839
|
const url = `https://github.com/${SDK_REPO}/releases/download/${sdkVersion}/LeCodesSDKResources.bundle.zip`;
|
|
220413
220840
|
note(`Downloading LeCodesSDKResources.bundle (SDK ${sdkVersion})…`);
|
|
220414
220841
|
const res = await fetch(url);
|
|
220415
220842
|
if (!res.ok)
|
|
220416
220843
|
throw new CliError(`Resources bundle download failed (HTTP ${res.status}) — ${url}`);
|
|
220417
|
-
|
|
220418
|
-
const zip =
|
|
220419
|
-
|
|
220420
|
-
|
|
220421
|
-
|
|
220422
|
-
if (!
|
|
220844
|
+
mkdirSync14(dir, { recursive: true });
|
|
220845
|
+
const zip = join21(dir, "LeCodesSDKResources.bundle.zip");
|
|
220846
|
+
writeFileSync16(zip, new Uint8Array(await res.arrayBuffer()));
|
|
220847
|
+
execFileSync6("ditto", ["-x", "-k", zip, dir]);
|
|
220848
|
+
rmSync7(zip);
|
|
220849
|
+
if (!existsSync20(bundle))
|
|
220423
220850
|
throw new CliError(`No LeCodesSDKResources.bundle inside the release asset ${url}`);
|
|
220424
220851
|
return bundle;
|
|
220425
220852
|
};
|
|
@@ -220444,13 +220871,13 @@ var resolveUpdateUrl = (update, root) => {
|
|
|
220444
220871
|
};
|
|
220445
220872
|
var appSync = async (args) => {
|
|
220446
220873
|
const root = projectRoot();
|
|
220447
|
-
const config = readJson(
|
|
220874
|
+
const config = readJson(join21(root, "app.json"));
|
|
220448
220875
|
const platform = args._[1];
|
|
220449
220876
|
if (platform !== undefined && platform !== "ios" && platform !== "android") {
|
|
220450
220877
|
throw new CliError(`Unknown platform "${platform}" — use ios or android.`);
|
|
220451
220878
|
}
|
|
220452
|
-
const hasIos = !!config?.ios &&
|
|
220453
|
-
const hasAndroid = !!config?.android &&
|
|
220879
|
+
const hasIos = !!config?.ios && existsSync20(join21(root, "ios"));
|
|
220880
|
+
const hasAndroid = !!config?.android && existsSync20(join21(root, "android"));
|
|
220454
220881
|
if (platform === "ios" && !hasIos)
|
|
220455
220882
|
throw new CliError("No iOS shell here — run `lecodes app init ios` first.");
|
|
220456
220883
|
if (platform === "android" && !hasAndroid)
|
|
@@ -220465,7 +220892,7 @@ var appSync = async (args) => {
|
|
|
220465
220892
|
await syncAndroid(root, config, iosArtifacts);
|
|
220466
220893
|
};
|
|
220467
220894
|
var syncIos = async (root, config) => {
|
|
220468
|
-
const iosDir =
|
|
220895
|
+
const iosDir = join21(root, "ios");
|
|
220469
220896
|
if (!config.ios)
|
|
220470
220897
|
throw new CliError("No iOS shell here — run `lecodes app init ios` first.");
|
|
220471
220898
|
const requestedVariant = config.ios.variant ?? "auto";
|
|
@@ -220475,14 +220902,14 @@ var syncIos = async (root, config) => {
|
|
|
220475
220902
|
const linkedSdk = shellSdkVersion(iosDir);
|
|
220476
220903
|
const sdkPin = process.env.LECODES_IOS_SDK ?? config.ios.sdk ?? linkedSdk ?? "latest";
|
|
220477
220904
|
const sdkVersion = await resolveSdkVersion(sdkPin, linkedSdk, IOS_SDK_OPTS);
|
|
220478
|
-
const lockPath =
|
|
220905
|
+
const lockPath = join21(root, "plugins.lock.json");
|
|
220479
220906
|
const oldLock = readJson(lockPath)?.plugins ?? {};
|
|
220480
220907
|
const pluginSpecs = Object.entries(config.plugins ?? {});
|
|
220481
220908
|
const runtimePlugins = [];
|
|
220482
220909
|
const infoValues = {};
|
|
220483
220910
|
const entitlementValues = {};
|
|
220484
220911
|
const newLock = {};
|
|
220485
|
-
const sourcesDir =
|
|
220912
|
+
const sourcesDir = join21(iosDir, "LeCodesRuntime", "Sources");
|
|
220486
220913
|
for (const [id, spec] of pluginSpecs) {
|
|
220487
220914
|
const plugin = resolvePlugin(id, spec, oldLock[id], "ios");
|
|
220488
220915
|
const ios = plugin.manifest.ios;
|
|
@@ -220490,12 +220917,12 @@ var syncIos = async (root, config) => {
|
|
|
220490
220917
|
const register = ios.register;
|
|
220491
220918
|
if (!module || !register)
|
|
220492
220919
|
throw new CliError(`Plugin "${id}": manifest ios.module / ios.register missing`);
|
|
220493
|
-
const src =
|
|
220494
|
-
if (!
|
|
220920
|
+
const src = join21(plugin.dir, ios.sources ?? "ios");
|
|
220921
|
+
if (!existsSync20(src))
|
|
220495
220922
|
throw new CliError(`Plugin "${id}": sources dir ${src} missing`);
|
|
220496
|
-
const dest =
|
|
220497
|
-
|
|
220498
|
-
|
|
220923
|
+
const dest = join21(sourcesDir, module);
|
|
220924
|
+
rmSync7(dest, { recursive: true, force: true });
|
|
220925
|
+
cpSync4(src, dest, { recursive: true });
|
|
220499
220926
|
if (ios.minSdk && VERSION_RE.test(ios.minSdk) && cmpVersion(sdkVersion, ios.minSdk) < 0) {
|
|
220500
220927
|
throw new CliError(`Plugin "${id}" needs lecodes-ios-sdk ${ios.minSdk} but the shell links ${sdkVersion} — run \`lecodes app update\` (or set app.json ios.sdk).`);
|
|
220501
220928
|
}
|
|
@@ -220510,9 +220937,9 @@ var syncIos = async (root, config) => {
|
|
|
220510
220937
|
log(`${c.green("plugin")} ${id}@${plugin.lock.version} (${plugin.lock.tag} → Sources/${module})`);
|
|
220511
220938
|
}
|
|
220512
220939
|
const keep = new Set(["LeCodesRuntime", ...runtimePlugins.map((p2) => p2.module)]);
|
|
220513
|
-
for (const entry of
|
|
220514
|
-
if (!keep.has(entry) &&
|
|
220515
|
-
|
|
220940
|
+
for (const entry of existsSync20(sourcesDir) ? readdirSync9(sourcesDir) : []) {
|
|
220941
|
+
if (!keep.has(entry) && statSync7(join21(sourcesDir, entry)).isDirectory()) {
|
|
220942
|
+
rmSync7(join21(sourcesDir, entry), { recursive: true });
|
|
220516
220943
|
log(`${c.yellow("removed")} ios/LeCodesRuntime/Sources/${entry} (plugin uninstalled)`);
|
|
220517
220944
|
}
|
|
220518
220945
|
}
|
|
@@ -220520,11 +220947,11 @@ var syncIos = async (root, config) => {
|
|
|
220520
220947
|
if (!Array.isArray(remotePatterns) || remotePatterns.some((p2) => typeof p2 !== "string")) {
|
|
220521
220948
|
throw new CliError('app.json "resources.remote" must be an array of glob strings (e.g. ["videos/**"]).');
|
|
220522
220949
|
}
|
|
220523
|
-
const resourcesDir =
|
|
220524
|
-
|
|
220525
|
-
|
|
220526
|
-
const resources = await compileShell({ _: [], flags: { o:
|
|
220527
|
-
const bundleJs =
|
|
220950
|
+
const resourcesDir = join21(iosDir, "App", "Resources");
|
|
220951
|
+
rmSync7(resourcesDir, { recursive: true, force: true });
|
|
220952
|
+
mkdirSync14(resourcesDir, { recursive: true });
|
|
220953
|
+
const resources = await compileShell({ _: [], flags: { o: join21(resourcesDir, "app.js") } }, { remotePatterns });
|
|
220954
|
+
const bundleJs = readFileSync17(join21(resourcesDir, "app.js"), "utf8");
|
|
220528
220955
|
const { variant, detected } = resolveVariant(requestedVariant, bundleJs);
|
|
220529
220956
|
const WHY = {
|
|
220530
220957
|
core: "UI only",
|
|
@@ -220538,8 +220965,8 @@ var syncIos = async (root, config) => {
|
|
|
220538
220965
|
const sdkProvided = new Set;
|
|
220539
220966
|
if (variant === "3d" || variant === "full") {
|
|
220540
220967
|
const sdkResources = await ensureSdkResourcesBundle(sdkVersion);
|
|
220541
|
-
|
|
220542
|
-
for (const f3 of
|
|
220968
|
+
cpSync4(sdkResources, join21(resourcesDir, "LeCodesSDKResources.bundle"), { recursive: true });
|
|
220969
|
+
for (const f3 of readdirSync9(sdkResources))
|
|
220543
220970
|
if (f3 !== "Info.plist")
|
|
220544
220971
|
sdkProvided.add(f3);
|
|
220545
220972
|
log(`${c.green("resources")} LeCodesSDKResources.bundle (filament) → ios/App/Resources/`);
|
|
@@ -220566,7 +220993,7 @@ var syncIos = async (root, config) => {
|
|
|
220566
220993
|
}
|
|
220567
220994
|
if (candidates.length > 1)
|
|
220568
220995
|
warn(`asset name collision: ${name} (${candidates.map((r2) => r2.path).join(", ")}) — embedding ${candidates[0].path}`);
|
|
220569
|
-
|
|
220996
|
+
cpSync4(candidates[0].absPath, join21(resourcesDir, name));
|
|
220570
220997
|
embedded++;
|
|
220571
220998
|
}
|
|
220572
220999
|
if (embedded > 0)
|
|
@@ -220586,14 +221013,14 @@ var syncIos = async (root, config) => {
|
|
|
220586
221013
|
}
|
|
220587
221014
|
if (shellUuid)
|
|
220588
221015
|
log(`${c.green("identity")} project ${shellUuid} baked into the shell (world identity)`);
|
|
220589
|
-
|
|
220590
|
-
|
|
220591
|
-
|
|
221016
|
+
writeFileSync16(join21(iosDir, "README.md"), IOS_README);
|
|
221017
|
+
writeFileSync16(join21(iosDir, "LeCodesRuntime", "Package.swift"), runtimePackageTemplate(sdkVersion, variant, runtimePlugins));
|
|
221018
|
+
writeFileSync16(join21(sourcesDir, "LeCodesRuntime", "LeCodesRuntime.swift"), runtimeSwiftTemplate(runtimePlugins, shellUuid));
|
|
220592
221019
|
const mergedKeys = {};
|
|
220593
221020
|
for (const [key, values] of Object.entries(infoValues))
|
|
220594
221021
|
mergedKeys[key] = [...new Set(values)].join(" ");
|
|
220595
221022
|
const updateUrl = resolveUpdateUrl(config.update, root);
|
|
220596
|
-
|
|
221023
|
+
writeFileSync16(join21(iosDir, "App", "Info.plist"), infoPlistTemplate(mergedKeys, updateUrl));
|
|
220597
221024
|
if (updateUrl)
|
|
220598
221025
|
log(`${c.green("update")} OTA source ${updateUrl} — checked natively at launch, new code applies next launch`);
|
|
220599
221026
|
else if (!config.update) {
|
|
@@ -220604,10 +221031,10 @@ var syncIos = async (root, config) => {
|
|
|
220604
221031
|
if (manifest)
|
|
220605
221032
|
note(`tip: add "update": true to app.json for over-the-air JS updates — ship with \`lecodes push --publish\`, no store release`);
|
|
220606
221033
|
}
|
|
220607
|
-
if (pluginSpecs.length > 0 ||
|
|
221034
|
+
if (pluginSpecs.length > 0 || existsSync20(lockPath))
|
|
220608
221035
|
writeJson(lockPath, { plugins: newLock });
|
|
220609
|
-
const pbxPath =
|
|
220610
|
-
let pbx =
|
|
221036
|
+
const pbxPath = join21(iosDir, "App.xcodeproj", "project.pbxproj");
|
|
221037
|
+
let pbx = readFileSync17(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};`);
|
|
220611
221038
|
const mergedEntitlements = {};
|
|
220612
221039
|
for (const [key, values] of Object.entries(entitlementValues)) {
|
|
220613
221040
|
if (values.every((v) => Array.isArray(v))) {
|
|
@@ -220628,7 +221055,7 @@ var syncIos = async (root, config) => {
|
|
|
220628
221055
|
}
|
|
220629
221056
|
const hasEntitlements = Object.keys(mergedEntitlements).length > 0;
|
|
220630
221057
|
if (pbx.includes("CODE_SIGN_ENTITLEMENTS") || hasEntitlements) {
|
|
220631
|
-
|
|
221058
|
+
writeFileSync16(join21(iosDir, "App", "App.entitlements"), entitlementsTemplate(mergedEntitlements));
|
|
220632
221059
|
if (hasEntitlements)
|
|
220633
221060
|
log(`${c.green("caps")} App/App.entitlements: ${Object.keys(mergedEntitlements).sort().join(", ")}`);
|
|
220634
221061
|
if (!pbx.includes("CODE_SIGN_ENTITLEMENTS")) {
|
|
@@ -220644,7 +221071,7 @@ $2Info.plist,`);
|
|
|
220644
221071
|
}
|
|
220645
221072
|
}
|
|
220646
221073
|
}
|
|
220647
|
-
|
|
221074
|
+
writeFileSync16(pbxPath, pbx);
|
|
220648
221075
|
if (config.icon) {
|
|
220649
221076
|
await writeIosAppIcon(iosDir, await loadIconMaster(root, config));
|
|
220650
221077
|
}
|
|
@@ -220656,7 +221083,7 @@ $2Info.plist,`);
|
|
|
220656
221083
|
var appPlugin = async (args) => {
|
|
220657
221084
|
const sub = args._[1];
|
|
220658
221085
|
const root = projectRoot();
|
|
220659
|
-
const configPath =
|
|
221086
|
+
const configPath = join21(root, "app.json");
|
|
220660
221087
|
const config = readJson(configPath);
|
|
220661
221088
|
if (!config)
|
|
220662
221089
|
throw new CliError("No app.json here — run `lecodes app init <ios|android>` first.");
|
|
@@ -220667,7 +221094,7 @@ var appPlugin = async (args) => {
|
|
|
220667
221094
|
log("No plugins installed.");
|
|
220668
221095
|
return;
|
|
220669
221096
|
}
|
|
220670
|
-
const lock = readJson(
|
|
221097
|
+
const lock = readJson(join21(root, "plugins.lock.json"))?.plugins ?? {};
|
|
220671
221098
|
for (const [id2, spec] of entries) {
|
|
220672
221099
|
const pin = lock[id2] ? c.dim(` ${lock[id2].repo} ${lock[id2].tag} (${lock[id2].commit.slice(0, 7)})`) : c.dim(" (not synced)");
|
|
220673
221100
|
log(`${c.bold(id2)} ${spec}${pin}`);
|
|
@@ -220720,14 +221147,14 @@ var exportOptionsPlist = (method, teamId, destination) => `<?xml version="1.0" e
|
|
|
220720
221147
|
`;
|
|
220721
221148
|
var requireShell = () => {
|
|
220722
221149
|
const root = projectRoot();
|
|
220723
|
-
const config = readJson(
|
|
220724
|
-
if (!config?.ios || !
|
|
221150
|
+
const config = readJson(join21(root, "app.json"));
|
|
221151
|
+
if (!config?.ios || !existsSync20(join21(root, "ios"))) {
|
|
220725
221152
|
throw new CliError("No iOS shell here — run `lecodes app init ios` first.");
|
|
220726
221153
|
}
|
|
220727
221154
|
return { root, config };
|
|
220728
221155
|
};
|
|
220729
221156
|
var updateSdkPin = async (root, config, args, checkOnly, platform) => {
|
|
220730
|
-
const [label, repo, minVersion, shellVersion] = platform === "ios" ? ["lecodes-ios-sdk", SDK_REPO, SDK_MIN_VERSION, () => shellSdkVersion(
|
|
221157
|
+
const [label, repo, minVersion, shellVersion] = platform === "ios" ? ["lecodes-ios-sdk", SDK_REPO, SDK_MIN_VERSION, () => shellSdkVersion(join21(root, "ios"))] : ["lecodes-android-sdk", ANDROID_SDK_REPO, ANDROID_SDK_MIN_VERSION, () => shellSdkVersionAndroid(join21(root, "android"))];
|
|
220731
221158
|
const section = config[platform];
|
|
220732
221159
|
const wanted = flagStr(args, "sdk");
|
|
220733
221160
|
let changed = false;
|
|
@@ -220758,19 +221185,19 @@ var updateSdkPin = async (root, config, args, checkOnly, platform) => {
|
|
|
220758
221185
|
}
|
|
220759
221186
|
if (!checkOnly && nextPin !== pin) {
|
|
220760
221187
|
section.sdk = nextPin;
|
|
220761
|
-
writeJson(
|
|
221188
|
+
writeJson(join21(root, "app.json"), config);
|
|
220762
221189
|
}
|
|
220763
221190
|
return changed;
|
|
220764
221191
|
};
|
|
220765
221192
|
var appUpdate = async (args) => {
|
|
220766
221193
|
const root = projectRoot();
|
|
220767
|
-
const config = readJson(
|
|
221194
|
+
const config = readJson(join21(root, "app.json"));
|
|
220768
221195
|
const platform = args._[1];
|
|
220769
221196
|
if (platform !== undefined && platform !== "ios" && platform !== "android") {
|
|
220770
221197
|
throw new CliError(`Unknown platform "${platform}" — use ios or android.`);
|
|
220771
221198
|
}
|
|
220772
|
-
const hasIos = !!config?.ios &&
|
|
220773
|
-
const hasAndroid = !!config?.android &&
|
|
221199
|
+
const hasIos = !!config?.ios && existsSync20(join21(root, "ios"));
|
|
221200
|
+
const hasAndroid = !!config?.android && existsSync20(join21(root, "android"));
|
|
220774
221201
|
if (platform === "ios" && !hasIos)
|
|
220775
221202
|
throw new CliError("No iOS shell here — run `lecodes app init ios` first.");
|
|
220776
221203
|
if (platform === "android" && !hasAndroid)
|
|
@@ -220788,7 +221215,7 @@ var appUpdate = async (args) => {
|
|
|
220788
221215
|
changed = await updateSdkPin(root, config, args, checkOnly, "ios") || changed;
|
|
220789
221216
|
if (doAndroid)
|
|
220790
221217
|
changed = await updateSdkPin(root, config, args, checkOnly, "android") || changed;
|
|
220791
|
-
const lockPath =
|
|
221218
|
+
const lockPath = join21(root, "plugins.lock.json");
|
|
220792
221219
|
const lock = readJson(lockPath)?.plugins ?? {};
|
|
220793
221220
|
const specs = Object.entries(config.plugins ?? {});
|
|
220794
221221
|
if (specs.length > 0 && !flagBool(args, "no-plugins")) {
|
|
@@ -220817,7 +221244,7 @@ var appUpdate = async (args) => {
|
|
|
220817
221244
|
log(`${c.green("plugin")} ${id} ${entry?.tag ?? "—"} → ${c.bold(tag)}`);
|
|
220818
221245
|
changed = true;
|
|
220819
221246
|
}
|
|
220820
|
-
if (!checkOnly &&
|
|
221247
|
+
if (!checkOnly && existsSync20(lockPath))
|
|
220821
221248
|
writeJson(lockPath, { plugins: kept });
|
|
220822
221249
|
}
|
|
220823
221250
|
log("");
|
|
@@ -220837,9 +221264,9 @@ var appUpdate = async (args) => {
|
|
|
220837
221264
|
};
|
|
220838
221265
|
var appDoctor = async (_args) => {
|
|
220839
221266
|
const root = projectRoot();
|
|
220840
|
-
const config = readJson(
|
|
220841
|
-
const hasIos = !!config?.ios &&
|
|
220842
|
-
const hasAndroid = !!config?.android &&
|
|
221267
|
+
const config = readJson(join21(root, "app.json"));
|
|
221268
|
+
const hasIos = !!config?.ios && existsSync20(join21(root, "ios"));
|
|
221269
|
+
const hasAndroid = !!config?.android && existsSync20(join21(root, "android"));
|
|
220843
221270
|
if (!hasIos && !hasAndroid) {
|
|
220844
221271
|
throw new CliError("No iOS shell here — run `lecodes app init ios` first.");
|
|
220845
221272
|
}
|
|
@@ -220850,14 +221277,14 @@ var appDoctor = async (_args) => {
|
|
|
220850
221277
|
failed++;
|
|
220851
221278
|
};
|
|
220852
221279
|
if (config?.icon) {
|
|
220853
|
-
check(
|
|
221280
|
+
check(existsSync20(join21(root, config.icon)), `Icon master ${config.icon}`, `app.json "icon" points at a missing file — sync will fail`);
|
|
220854
221281
|
}
|
|
220855
221282
|
if (hasIos)
|
|
220856
221283
|
await doctorIosChecks(root, config, check);
|
|
220857
221284
|
if (hasAndroid) {
|
|
220858
221285
|
await doctorAndroidChecks(root, config, check);
|
|
220859
221286
|
if (!hasIos) {
|
|
220860
|
-
const lock = readJson(
|
|
221287
|
+
const lock = readJson(join21(root, "plugins.lock.json"))?.plugins ?? {};
|
|
220861
221288
|
const wanted = Object.keys(config.plugins ?? {});
|
|
220862
221289
|
const inSync = wanted.every((id) => lock[id]) && Object.keys(lock).every((id) => wanted.includes(id));
|
|
220863
221290
|
check(wanted.length === 0 || inSync, "Plugins synced", "run `lecodes app sync`");
|
|
@@ -220876,22 +221303,22 @@ var doctorIosChecks = async (root, config, check) => {
|
|
|
220876
221303
|
} catch {}
|
|
220877
221304
|
check(xcodeVersion !== "", `Xcode CLI (${xcodeVersion || "not found"})`, "install Xcode + command line tools");
|
|
220878
221305
|
check(!!config.ios.teamId, "Development team set", "lecodes app init wrote app.json — fill ios.teamId", false);
|
|
220879
|
-
const iconDir =
|
|
220880
|
-
const hasIcon =
|
|
221306
|
+
const iconDir = join21(root, "ios", "App", "Assets.xcassets", "AppIcon.appiconset");
|
|
221307
|
+
const hasIcon = existsSync20(iconDir) && readdirSync9(iconDir).some((f3) => f3.endsWith(".png"));
|
|
220881
221308
|
check(hasIcon, "App icon present", "drop a 1024×1024 PNG into ios/App/Assets.xcassets/AppIcon.appiconset/ (App Store requires it)", false);
|
|
220882
|
-
const bundle =
|
|
220883
|
-
check(
|
|
221309
|
+
const bundle = join21(root, "ios", "App", "Resources", "app.js");
|
|
221310
|
+
check(existsSync20(bundle), "Bundled app.js", "run `lecodes app sync`");
|
|
220884
221311
|
const requestedVariant = config.ios.variant ?? "auto";
|
|
220885
221312
|
const validVariant = requestedVariant === "auto" || SDK_VARIANTS.includes(requestedVariant);
|
|
220886
221313
|
check(validVariant, `ios.variant "${requestedVariant}"`, `use "auto" or one of ${SDK_VARIANTS.join(", ")}`);
|
|
220887
|
-
if (
|
|
220888
|
-
const { variant } = resolveVariant(requestedVariant,
|
|
220889
|
-
const pkg =
|
|
221314
|
+
if (existsSync20(bundle) && validVariant) {
|
|
221315
|
+
const { variant } = resolveVariant(requestedVariant, readFileSync17(bundle, "utf8"));
|
|
221316
|
+
const pkg = readFileSync17(join21(root, "ios", "LeCodesRuntime", "Package.swift"), "utf8");
|
|
220890
221317
|
const needsResources = variant === "3d" || variant === "full";
|
|
220891
|
-
const hasResources =
|
|
221318
|
+
const hasResources = existsSync20(join21(root, "ios", "App", "Resources", "LeCodesSDKResources.bundle"));
|
|
220892
221319
|
check(pkg.includes(`"LeCodesSDK-${variant}"`) && (!needsResources || hasResources), `SDK variant ${variant}${needsResources ? " (+ filament resources)" : ""}`, "run `lecodes app sync`");
|
|
220893
221320
|
}
|
|
220894
|
-
const linked = shellSdkVersion(
|
|
221321
|
+
const linked = shellSdkVersion(join21(root, "ios"));
|
|
220895
221322
|
const pin = config.ios.sdk ?? "latest";
|
|
220896
221323
|
check(!!linked, `SDK ${linked ?? "version unreadable"}`, "run `lecodes app sync`");
|
|
220897
221324
|
if (linked && pin !== "latest") {
|
|
@@ -220903,27 +221330,27 @@ var doctorIosChecks = async (root, config, check) => {
|
|
|
220903
221330
|
check(cmpVersion(linked, latest) >= 0, `SDK is the latest release (${latest})`, "run `lecodes app update`", false);
|
|
220904
221331
|
} catch {}
|
|
220905
221332
|
}
|
|
220906
|
-
const lock = readJson(
|
|
221333
|
+
const lock = readJson(join21(root, "plugins.lock.json"))?.plugins ?? {};
|
|
220907
221334
|
const wanted = Object.keys(config.plugins ?? {});
|
|
220908
221335
|
const inSync = wanted.every((id) => lock[id]) && Object.keys(lock).every((id) => wanted.includes(id));
|
|
220909
221336
|
check(wanted.length === 0 || inSync, "Plugins synced", "run `lecodes app sync`");
|
|
220910
|
-
const pbxFile =
|
|
220911
|
-
if (
|
|
220912
|
-
check(
|
|
221337
|
+
const pbxFile = join21(root, "ios", "App.xcodeproj", "project.pbxproj");
|
|
221338
|
+
if (existsSync20(pbxFile) && readFileSync17(pbxFile, "utf8").includes("CODE_SIGN_ENTITLEMENTS")) {
|
|
221339
|
+
check(existsSync20(join21(root, "ios", "App", "App.entitlements")), "App/App.entitlements present", "run `lecodes app sync`");
|
|
220913
221340
|
}
|
|
220914
221341
|
if (wanted.includes("push")) {
|
|
220915
|
-
const runtimeSwift =
|
|
220916
|
-
const baked =
|
|
221342
|
+
const runtimeSwift = join21(root, "ios", "LeCodesRuntime", "Sources", "LeCodesRuntime", "LeCodesRuntime.swift");
|
|
221343
|
+
const baked = existsSync20(runtimeSwift) && readFileSync17(runtimeSwift, "utf8").includes("bootProjectUuid");
|
|
220917
221344
|
check(baked, "Push world identity baked", `without it Push.register() rejects "unavailable" — needs a pushed project (uuid), lecodes-ios-sdk ${SDK_IDENTITY_VERSION}+ and \`lecodes app sync\``, false);
|
|
220918
221345
|
}
|
|
220919
221346
|
if (config.update) {
|
|
220920
|
-
const plist =
|
|
221347
|
+
const plist = readFileSync17(join21(root, "ios", "App", "Info.plist"), "utf8");
|
|
220921
221348
|
check(plist.includes("<key>LeCodesUpdateURL</key>"), "OTA update URL in Info.plist", "run `lecodes app sync`", false);
|
|
220922
|
-
const appDir =
|
|
220923
|
-
const callsUpdater =
|
|
221349
|
+
const appDir = join21(root, "ios", "App");
|
|
221350
|
+
const callsUpdater = readdirSync9(appDir).some((f3) => f3.endsWith(".swift") && readFileSync17(join21(appDir, f3), "utf8").includes("LeCodesUpdater.activeBundle"));
|
|
220924
221351
|
check(callsUpdater, "App code launches via LeCodesUpdater", `no App/*.swift calls LeCodesUpdater.activeBundle() — downloaded updates would never run; see "Over-the-air updates" in ios/README.md for the ViewController snippet`, false);
|
|
220925
221352
|
}
|
|
220926
|
-
const asc = readJson(
|
|
221353
|
+
const asc = readJson(join21(homedir7(), ".lecodes", "asc.json"));
|
|
220927
221354
|
check(!!asc?.keyPath, "App Store Connect API key configured", "pass --key-path/--key-id/--issuer-id to `lecodes app upload` once to store it", false);
|
|
220928
221355
|
};
|
|
220929
221356
|
var ARCHIVE_PATH = "ios/build/App.xcarchive";
|
|
@@ -220959,8 +221386,8 @@ var appBuild = async (args) => {
|
|
|
220959
221386
|
success(`Archive: ios/build/App.xcarchive`);
|
|
220960
221387
|
if (flagBool(args, "no-export"))
|
|
220961
221388
|
return;
|
|
220962
|
-
const optionsPath =
|
|
220963
|
-
|
|
221389
|
+
const optionsPath = join21(root, "ios", "build", "ExportOptions.plist");
|
|
221390
|
+
writeFileSync16(optionsPath, exportOptionsPlist(method, config.ios.teamId ?? ""));
|
|
220964
221391
|
note(`Exporting (.ipa, method ${method})…`);
|
|
220965
221392
|
runTool(root, "xcodebuild", [
|
|
220966
221393
|
"-exportArchive",
|
|
@@ -220979,10 +221406,10 @@ var appUpload = async (args) => {
|
|
|
220979
221406
|
throw new CliError("Play upload isn't wired yet — upload android/app/build/outputs/bundle/release/*.aab in the Play Console (`lecodes app build android` produces it).");
|
|
220980
221407
|
}
|
|
220981
221408
|
const { root, config } = requireShell();
|
|
220982
|
-
if (!
|
|
221409
|
+
if (!existsSync20(join21(root, ARCHIVE_PATH))) {
|
|
220983
221410
|
throw new CliError("No archive at ios/build/App.xcarchive — run `lecodes app build ios` first.");
|
|
220984
221411
|
}
|
|
220985
|
-
const ascPath =
|
|
221412
|
+
const ascPath = join21(homedir7(), ".lecodes", "asc.json");
|
|
220986
221413
|
const stored = readJson(ascPath) ?? {};
|
|
220987
221414
|
const keyPath = flagStr(args, "key-path") ?? stored.keyPath;
|
|
220988
221415
|
const keyId = flagStr(args, "key-id") ?? stored.keyId;
|
|
@@ -220991,15 +221418,15 @@ var appUpload = async (args) => {
|
|
|
220991
221418
|
throw new CliError(`App Store Connect API key needed: --key-path <AuthKey.p8> --key-id <id> --issuer-id <id>
|
|
220992
221419
|
(create one at appstoreconnect.apple.com → Users and Access → Integrations; stored in ~/.lecodes/asc.json after first use)`);
|
|
220993
221420
|
}
|
|
220994
|
-
if (!
|
|
221421
|
+
if (!existsSync20(keyPath))
|
|
220995
221422
|
throw new CliError(`API key file not found: ${keyPath}`);
|
|
220996
221423
|
if (flagStr(args, "key-path")) {
|
|
220997
|
-
|
|
221424
|
+
mkdirSync14(dirname12(ascPath), { recursive: true });
|
|
220998
221425
|
writeJson(ascPath, { keyPath, keyId, issuerId });
|
|
220999
221426
|
note("Stored the key reference in ~/.lecodes/asc.json");
|
|
221000
221427
|
}
|
|
221001
|
-
const optionsPath =
|
|
221002
|
-
|
|
221428
|
+
const optionsPath = join21(root, "ios", "build", "UploadOptions.plist");
|
|
221429
|
+
writeFileSync16(optionsPath, exportOptionsPlist("app-store-connect", config.ios.teamId ?? "", "upload"));
|
|
221003
221430
|
note("Uploading to App Store Connect… (processing continues on Apple's side)");
|
|
221004
221431
|
runTool(root, "xcodebuild", [
|
|
221005
221432
|
"-exportArchive",
|
|
@@ -221010,14 +221437,14 @@ var appUpload = async (args) => {
|
|
|
221010
221437
|
"-exportPath",
|
|
221011
221438
|
"ios/build/upload",
|
|
221012
221439
|
"-authenticationKeyPath",
|
|
221013
|
-
|
|
221440
|
+
resolve8(keyPath),
|
|
221014
221441
|
"-authenticationKeyID",
|
|
221015
221442
|
keyId,
|
|
221016
221443
|
"-authenticationKeyIssuerID",
|
|
221017
221444
|
issuerId,
|
|
221018
221445
|
"-allowProvisioningUpdates"
|
|
221019
221446
|
]);
|
|
221020
|
-
const configPath =
|
|
221447
|
+
const configPath = join21(root, "app.json");
|
|
221021
221448
|
const fresh = readJson(configPath);
|
|
221022
221449
|
fresh.ios.buildNumber = (fresh.ios.buildNumber ?? 1) + 1;
|
|
221023
221450
|
writeJson(configPath, fresh);
|
|
@@ -221028,19 +221455,19 @@ var appOpen = async (args) => {
|
|
|
221028
221455
|
const root = projectRoot();
|
|
221029
221456
|
let platform = args._[1];
|
|
221030
221457
|
if (!platform) {
|
|
221031
|
-
const hasIos =
|
|
221032
|
-
const hasAndroid =
|
|
221458
|
+
const hasIos = existsSync20(join21(root, "ios"));
|
|
221459
|
+
const hasAndroid = existsSync20(join21(root, "android"));
|
|
221033
221460
|
if (hasIos && hasAndroid)
|
|
221034
221461
|
throw new CliError("Both shells exist — `lecodes app open ios` or `lecodes app open android`.");
|
|
221035
221462
|
platform = hasAndroid ? "android" : "ios";
|
|
221036
221463
|
}
|
|
221037
221464
|
if (platform === "ios") {
|
|
221038
|
-
const proj =
|
|
221039
|
-
if (!
|
|
221465
|
+
const proj = join21(root, "ios", "App.xcodeproj");
|
|
221466
|
+
if (!existsSync20(proj))
|
|
221040
221467
|
throw new CliError("No iOS shell here — run `lecodes app init ios` first.");
|
|
221041
221468
|
if (process.platform !== "darwin")
|
|
221042
221469
|
throw new CliError("Opening Xcode requires macOS.");
|
|
221043
|
-
|
|
221470
|
+
execFileSync6("open", [proj], { stdio: "ignore" });
|
|
221044
221471
|
success("Opened ios/App.xcodeproj in Xcode.");
|
|
221045
221472
|
return;
|
|
221046
221473
|
}
|
|
@@ -221073,8 +221500,8 @@ var app = async (args) => {
|
|
|
221073
221500
|
init_api();
|
|
221074
221501
|
init_config();
|
|
221075
221502
|
init_manifest();
|
|
221076
|
-
import { existsSync as
|
|
221077
|
-
import { resolve as
|
|
221503
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync16, readdirSync as readdirSync11 } from "node:fs";
|
|
221504
|
+
import { resolve as resolve9 } from "node:path";
|
|
221078
221505
|
init_types4();
|
|
221079
221506
|
init_ignore();
|
|
221080
221507
|
init_util();
|
|
@@ -221095,11 +221522,11 @@ var clone3 = async (args) => {
|
|
|
221095
221522
|
const uuid = parseUuid(args._[0]);
|
|
221096
221523
|
const project = await getProject(apiUrl, token, uuid);
|
|
221097
221524
|
const dir = args._[1] ?? slug(project.name);
|
|
221098
|
-
const root =
|
|
221099
|
-
if (
|
|
221525
|
+
const root = resolve9(dir);
|
|
221526
|
+
if (existsSync22(root) && readdirSync11(root).length > 0) {
|
|
221100
221527
|
throw new CliError(`Directory "${dir}" already exists and is not empty.`);
|
|
221101
221528
|
}
|
|
221102
|
-
|
|
221529
|
+
mkdirSync16(root, { recursive: true });
|
|
221103
221530
|
info(c.dim(`Fetching project ${c.bold(project.name)}…`));
|
|
221104
221531
|
const files = await materializeAssets(root, project, apiUrl, token, progress);
|
|
221105
221532
|
endProgress();
|
|
@@ -221122,8 +221549,8 @@ init_config();
|
|
|
221122
221549
|
init_manifest();
|
|
221123
221550
|
init_types4();
|
|
221124
221551
|
init_ignore();
|
|
221125
|
-
import { existsSync as
|
|
221126
|
-
import { join as
|
|
221552
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync17, readdirSync as readdirSync12, writeFileSync as writeFileSync18 } from "node:fs";
|
|
221553
|
+
import { join as join23, resolve as resolve10 } from "node:path";
|
|
221127
221554
|
|
|
221128
221555
|
// src/commands/projectTemplates.ts
|
|
221129
221556
|
var appClaudeMd = (name) => `# ${name} — LeCodes app
|
|
@@ -221263,21 +221690,21 @@ var create = async (args) => {
|
|
|
221263
221690
|
if (!name)
|
|
221264
221691
|
throw new CliError("Usage: lecodes create <name> [directory]");
|
|
221265
221692
|
const dir = args._[1] ?? slug2(name);
|
|
221266
|
-
const root =
|
|
221267
|
-
if (
|
|
221693
|
+
const root = resolve10(dir);
|
|
221694
|
+
if (existsSync23(root) && readdirSync12(root).length > 0) {
|
|
221268
221695
|
throw new CliError(`Directory "${dir}" already exists and is not empty.`);
|
|
221269
221696
|
}
|
|
221270
|
-
|
|
221697
|
+
mkdirSync17(root, { recursive: true });
|
|
221271
221698
|
info(c.dim(`Creating project ${c.bold(name)}…`));
|
|
221272
221699
|
const { uuid } = await createProject(apiUrl, token, name, flagStr(args, "team"));
|
|
221273
221700
|
info(c.dim("Fetching type definitions…"));
|
|
221274
221701
|
await materializeTypes(root, apiUrl);
|
|
221275
221702
|
writeTsconfig(root);
|
|
221276
221703
|
writeDefaultIgnore(root);
|
|
221277
|
-
|
|
221278
|
-
|
|
221279
|
-
|
|
221280
|
-
|
|
221704
|
+
writeFileSync18(join23(root, "main.ts"), STARTER);
|
|
221705
|
+
writeFileSync18(join23(root, "CLAUDE.md"), appClaudeMd(name));
|
|
221706
|
+
mkdirSync17(join23(root, ".claude"), { recursive: true });
|
|
221707
|
+
writeFileSync18(join23(root, ".claude", "settings.json"), CLAUDE_SETTINGS);
|
|
221281
221708
|
const commits = await getCommits(apiUrl, token, uuid);
|
|
221282
221709
|
writeManifest(root, { uuid, apiUrl, name, baseHeadId: commits.headId, files: {} });
|
|
221283
221710
|
success(`Created ${c.bold(name)} in ${c.bold(dir)}/`);
|
|
@@ -221344,6 +221771,10 @@ var status = async () => {
|
|
|
221344
221771
|
log(`downloaded ${installed.length > 0 ? installed.join(", ") : c.dim("none")} ${c.dim("(~/.lecodes/renderer)")}`);
|
|
221345
221772
|
if (process.env.LECODES_DESKTOP_EXE)
|
|
221346
221773
|
log(`override LECODES_DESKTOP_EXE=${process.env.LECODES_DESKTOP_EXE}`);
|
|
221774
|
+
if (process.platform === "darwin") {
|
|
221775
|
+
const installed2 = findMacHostExe();
|
|
221776
|
+
log(`installed ${installed2 ?? c.dim("none")} ${c.dim("(offline fallback only)")}`);
|
|
221777
|
+
}
|
|
221347
221778
|
try {
|
|
221348
221779
|
const { latest } = await fetchDesktopManifest();
|
|
221349
221780
|
const hint = semverNewer(selected, latest) > 0 ? c.yellow("(newer — run `lecodes desktop update`)") : latest === selected ? c.dim("(up to date)") : c.dim("(older than the CLI's minimum — this desktop release isn't published yet)");
|
|
@@ -221371,16 +221802,16 @@ init_design();
|
|
|
221371
221802
|
// src/commands/dev.ts
|
|
221372
221803
|
init_manifest();
|
|
221373
221804
|
init_ignore();
|
|
221374
|
-
import { existsSync as
|
|
221805
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync19, readFileSync as readFileSync22, watch, writeFileSync as writeFileSync20 } from "node:fs";
|
|
221375
221806
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
221376
221807
|
import { networkInterfaces } from "node:os";
|
|
221377
|
-
import { basename as
|
|
221808
|
+
import { basename as basename11, join as join27 } from "node:path";
|
|
221378
221809
|
|
|
221379
221810
|
// src/dev/devServer.ts
|
|
221380
221811
|
init_localFiles();
|
|
221381
221812
|
import { createServer as createServer2 } from "node:http";
|
|
221382
|
-
import { createReadStream, statSync as
|
|
221383
|
-
import { join as
|
|
221813
|
+
import { createReadStream, statSync as statSync9 } from "node:fs";
|
|
221814
|
+
import { join as join26 } from "node:path";
|
|
221384
221815
|
init_projectCompile();
|
|
221385
221816
|
init_fonts();
|
|
221386
221817
|
init_assetIcons();
|
|
@@ -221654,14 +222085,14 @@ var sendText = (res, status2, type, body) => {
|
|
|
221654
222085
|
res.end(body);
|
|
221655
222086
|
};
|
|
221656
222087
|
var sendJson = (res, status2, body) => sendText(res, status2, "application/json", JSON.stringify(body));
|
|
221657
|
-
var readBody = (req) => new Promise((
|
|
222088
|
+
var readBody = (req) => new Promise((resolve11) => {
|
|
221658
222089
|
let body = "";
|
|
221659
222090
|
req.on("data", (c4) => {
|
|
221660
222091
|
if (body.length < 256 * 1024)
|
|
221661
222092
|
body += c4.toString("utf8");
|
|
221662
222093
|
});
|
|
221663
|
-
req.on("end", () =>
|
|
221664
|
-
req.on("error", () =>
|
|
222094
|
+
req.on("end", () => resolve11(body));
|
|
222095
|
+
req.on("error", () => resolve11(body));
|
|
221665
222096
|
});
|
|
221666
222097
|
var startDevServer = async (options) => {
|
|
221667
222098
|
const { root, manifest, name, origin, token, entryOverride } = options;
|
|
@@ -221766,9 +222197,9 @@ var startDevServer = async (options) => {
|
|
|
221766
222197
|
const name2 = route.slice("/shaders/".length);
|
|
221767
222198
|
if (!isShaderArtifactName(name2))
|
|
221768
222199
|
return sendText(res, 400, "text/plain", "Bad path");
|
|
221769
|
-
const abs =
|
|
222200
|
+
const abs = join26(shadersCacheDir(root), name2);
|
|
221770
222201
|
try {
|
|
221771
|
-
if (!
|
|
222202
|
+
if (!statSync9(abs).isFile())
|
|
221772
222203
|
throw new Error("not a file");
|
|
221773
222204
|
} catch {
|
|
221774
222205
|
return sendText(res, 404, "text/plain", "Not found");
|
|
@@ -221785,7 +222216,7 @@ var startDevServer = async (options) => {
|
|
|
221785
222216
|
return sendText(res, 400, "text/plain", "Bad path");
|
|
221786
222217
|
}
|
|
221787
222218
|
const abs = resolveLocalPath(root, key);
|
|
221788
|
-
if (!abs || !
|
|
222219
|
+
if (!abs || !statSync9(abs).isFile())
|
|
221789
222220
|
return sendText(res, 404, "text/plain", "Not found");
|
|
221790
222221
|
res.writeHead(200, { "content-type": contentType(key), "cache-control": "no-store" });
|
|
221791
222222
|
createReadStream(abs).pipe(res);
|
|
@@ -221828,11 +222259,11 @@ var startDevServer = async (options) => {
|
|
|
221828
222259
|
if (conn)
|
|
221829
222260
|
clients.add(conn);
|
|
221830
222261
|
});
|
|
221831
|
-
await new Promise((
|
|
222262
|
+
await new Promise((resolve11, reject) => {
|
|
221832
222263
|
server2.once("error", reject);
|
|
221833
222264
|
server2.listen(options.port, options.bind ?? "0.0.0.0", () => {
|
|
221834
222265
|
server2.removeListener("error", reject);
|
|
221835
|
-
|
|
222266
|
+
resolve11();
|
|
221836
222267
|
});
|
|
221837
222268
|
});
|
|
221838
222269
|
const address = server2.address();
|
|
@@ -221880,24 +222311,24 @@ var lanCandidates = () => {
|
|
|
221880
222311
|
return out.sort((x2, y2) => x2.rank - y2.rank).map(({ address, iface }) => ({ address, iface }));
|
|
221881
222312
|
};
|
|
221882
222313
|
var loadDevToken = (root) => {
|
|
221883
|
-
const path2 =
|
|
222314
|
+
const path2 = join27(root, LECODES_DIR, "dev.json");
|
|
221884
222315
|
try {
|
|
221885
|
-
const token2 = JSON.parse(
|
|
222316
|
+
const token2 = JSON.parse(readFileSync22(path2, "utf8")).token;
|
|
221886
222317
|
if (token2 && /^[a-z0-9]{8,}$/.test(token2))
|
|
221887
222318
|
return token2;
|
|
221888
222319
|
} catch {}
|
|
221889
222320
|
const token = randomBytes3(6).toString("hex");
|
|
221890
|
-
|
|
221891
|
-
|
|
222321
|
+
mkdirSync19(join27(root, LECODES_DIR), { recursive: true });
|
|
222322
|
+
writeFileSync20(path2, JSON.stringify({ token }, null, 2) + `
|
|
221892
222323
|
`);
|
|
221893
222324
|
return token;
|
|
221894
222325
|
};
|
|
221895
222326
|
var printQr = async (url) => {
|
|
221896
222327
|
try {
|
|
221897
222328
|
const qrcode = (await Promise.resolve().then(() => __toESM(require_main(), 1))).default;
|
|
221898
|
-
await new Promise((
|
|
222329
|
+
await new Promise((resolve11) => qrcode.generate(url, { small: true }, (out) => {
|
|
221899
222330
|
log(out);
|
|
221900
|
-
|
|
222331
|
+
resolve11();
|
|
221901
222332
|
}));
|
|
221902
222333
|
} catch {
|
|
221903
222334
|
note("(couldn't render a QR code — open the URL below on the device)");
|
|
@@ -221913,7 +222344,7 @@ var dev = async (args) => {
|
|
|
221913
222344
|
root = process.cwd();
|
|
221914
222345
|
manifest = null;
|
|
221915
222346
|
}
|
|
221916
|
-
const name = manifest?.name ??
|
|
222347
|
+
const name = manifest?.name ?? basename11(root);
|
|
221917
222348
|
if (typeof globalThis.Bun !== "undefined") {
|
|
221918
222349
|
warnErr("Running under Bun: the websocket reload channel doesn't work here — run `lecodes dev` with Node (or use ?t=poll on the device URL).");
|
|
221919
222350
|
}
|
|
@@ -222005,7 +222436,7 @@ ${result.error}`);
|
|
|
222005
222436
|
if (alternatives.length > 0) {
|
|
222006
222437
|
note(`Device can't reach it? This machine also has ${alternatives.map((a2) => `${a2.address} (${a2.iface})`).join(", ")} — retry with --host <ip>.`);
|
|
222007
222438
|
}
|
|
222008
|
-
if (
|
|
222439
|
+
if (existsSync25(join27(root, LECODES_DIR, "manifest.json")))
|
|
222009
222440
|
note(`Token stored in ${LECODES_DIR}/dev.json (delete it to rotate the URL).`);
|
|
222010
222441
|
log("");
|
|
222011
222442
|
log(`Watching ${root} — save a file to hot-reload. Ctrl+C to stop.`);
|
|
@@ -222034,8 +222465,8 @@ init_localFiles();
|
|
|
222034
222465
|
|
|
222035
222466
|
// src/serverDiff.ts
|
|
222036
222467
|
init_localFiles();
|
|
222037
|
-
import { readFileSync as
|
|
222038
|
-
import { basename as
|
|
222468
|
+
import { readFileSync as readFileSync23 } from "node:fs";
|
|
222469
|
+
import { basename as basename12 } from "node:path";
|
|
222039
222470
|
var isTextAsset = (t2) => t2 === "text" || t2 === "shader" || t2 === "scene";
|
|
222040
222471
|
var computeServerDiff = (project, scan2) => {
|
|
222041
222472
|
const pathById = buildPaths(project.assets);
|
|
@@ -222052,8 +222483,8 @@ var computeServerDiff = (project, scan2) => {
|
|
|
222052
222483
|
const server2 = serverByPath.get(path2);
|
|
222053
222484
|
const local = localByPath.get(path2);
|
|
222054
222485
|
if (local && !server2) {
|
|
222055
|
-
const text = isTextKind(classifyKind(
|
|
222056
|
-
entries.push({ path: path2, status: "added", binary: !text, serverText: "", localText: text ?
|
|
222486
|
+
const text = isTextKind(classifyKind(basename12(path2)));
|
|
222487
|
+
entries.push({ path: path2, status: "added", binary: !text, serverText: "", localText: text ? readFileSync23(local.absPath, "utf8") : undefined });
|
|
222057
222488
|
continue;
|
|
222058
222489
|
}
|
|
222059
222490
|
if (server2 && !local) {
|
|
@@ -222064,12 +222495,12 @@ var computeServerDiff = (project, scan2) => {
|
|
|
222064
222495
|
if (server2 && local) {
|
|
222065
222496
|
if (isTextAsset(server2.type)) {
|
|
222066
222497
|
const serverText = server2.text ?? "";
|
|
222067
|
-
const localText =
|
|
222498
|
+
const localText = readFileSync23(local.absPath, "utf8");
|
|
222068
222499
|
if (serverText !== localText)
|
|
222069
222500
|
entries.push({ path: path2, status: "modified", binary: false, serverText, localText });
|
|
222070
222501
|
} else {
|
|
222071
222502
|
const serverSize = server2.file?.size;
|
|
222072
|
-
const localSize = Buffer.byteLength(
|
|
222503
|
+
const localSize = Buffer.byteLength(readFileSync23(local.absPath));
|
|
222073
222504
|
if (serverSize !== undefined && serverSize !== localSize) {
|
|
222074
222505
|
entries.push({ path: path2, status: "modified", binary: true });
|
|
222075
222506
|
}
|
|
@@ -222227,8 +222658,8 @@ var statusLabel = (e2) => e2.status === "added" ? c.green(" added ") : e2.st
|
|
|
222227
222658
|
init_manifest();
|
|
222228
222659
|
init_types4();
|
|
222229
222660
|
init_ignore();
|
|
222230
|
-
import { existsSync as
|
|
222231
|
-
import { basename as
|
|
222661
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync20, writeFileSync as writeFileSync21 } from "node:fs";
|
|
222662
|
+
import { basename as basename13, dirname as dirname14, join as join28, resolve as resolve11 } from "node:path";
|
|
222232
222663
|
init_util();
|
|
222233
222664
|
var STARTER2 = `// Welcome to LeCodes — the whole SDK is available as globals, no imports needed.
|
|
222234
222665
|
// Run it live on a device with \`lecodes dev\`, or bundle it with \`lecodes compile -o app.js\`.
|
|
@@ -222336,18 +222767,18 @@ Full command list: \`lecodes --help\`.
|
|
|
222336
222767
|
`;
|
|
222337
222768
|
var init2 = async (args) => {
|
|
222338
222769
|
const dirArg = (args._[0] ?? ".").replace(/[/\\]+$/, "") || ".";
|
|
222339
|
-
const root =
|
|
222340
|
-
const name =
|
|
222341
|
-
|
|
222770
|
+
const root = resolve11(dirArg);
|
|
222771
|
+
const name = basename13(root);
|
|
222772
|
+
mkdirSync20(root, { recursive: true });
|
|
222342
222773
|
let created = 0;
|
|
222343
222774
|
const write = (rel, content) => {
|
|
222344
|
-
const abs =
|
|
222345
|
-
if (
|
|
222775
|
+
const abs = join28(root, rel);
|
|
222776
|
+
if (existsSync26(abs)) {
|
|
222346
222777
|
note(`kept ${rel}`);
|
|
222347
222778
|
return;
|
|
222348
222779
|
}
|
|
222349
|
-
|
|
222350
|
-
|
|
222780
|
+
mkdirSync20(dirname14(abs), { recursive: true });
|
|
222781
|
+
writeFileSync21(abs, content);
|
|
222351
222782
|
log(`${c.green("created")} ${rel}`);
|
|
222352
222783
|
created++;
|
|
222353
222784
|
};
|
|
@@ -222394,8 +222825,8 @@ init_config();
|
|
|
222394
222825
|
init_ignore();
|
|
222395
222826
|
init_localFiles();
|
|
222396
222827
|
init_manifest();
|
|
222397
|
-
import { existsSync as
|
|
222398
|
-
import { basename as
|
|
222828
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
222829
|
+
import { basename as basename15, join as join29, resolve as resolve12 } from "node:path";
|
|
222399
222830
|
init_types4();
|
|
222400
222831
|
init_util();
|
|
222401
222832
|
|
|
@@ -222404,8 +222835,8 @@ init_api();
|
|
|
222404
222835
|
init_config();
|
|
222405
222836
|
init_manifest();
|
|
222406
222837
|
init_localFiles();
|
|
222407
|
-
import { basename as
|
|
222408
|
-
import { readFileSync as
|
|
222838
|
+
import { basename as basename14 } from "node:path";
|
|
222839
|
+
import { readFileSync as readFileSync24 } from "node:fs";
|
|
222409
222840
|
init_util();
|
|
222410
222841
|
var push = async (args) => {
|
|
222411
222842
|
const root = findProjectRoot(process.cwd());
|
|
@@ -222461,14 +222892,14 @@ var push = async (args) => {
|
|
|
222461
222892
|
let uploaded = 0;
|
|
222462
222893
|
for (const file of scan2.files) {
|
|
222463
222894
|
const source = sourceFor(file.path);
|
|
222464
|
-
const kind = source?.type ?? classifyKind(
|
|
222895
|
+
const kind = source?.type ?? classifyKind(basename14(file.path));
|
|
222465
222896
|
const entry = { path: file.path, type: kind };
|
|
222466
222897
|
if (source)
|
|
222467
222898
|
entry.assetId = source.id;
|
|
222468
222899
|
if (isTextKind(kind)) {
|
|
222469
|
-
entry.text =
|
|
222900
|
+
entry.text = readFileSync24(file.absPath, "utf8");
|
|
222470
222901
|
} else if (!source || source.sha !== file.sha) {
|
|
222471
|
-
const blob = await uploadBlob(apiUrl, token, manifest.uuid,
|
|
222902
|
+
const blob = await uploadBlob(apiUrl, token, manifest.uuid, basename14(file.path), readFileSync24(file.absPath));
|
|
222472
222903
|
entry.blobFileId = blob.id;
|
|
222473
222904
|
uploaded++;
|
|
222474
222905
|
}
|
|
@@ -222575,10 +223006,10 @@ var link = async (args) => {
|
|
|
222575
223006
|
const apiUrl = requireApiUrl(config);
|
|
222576
223007
|
const token = requireToken(config);
|
|
222577
223008
|
const dirArg = args._[0];
|
|
222578
|
-
const root = dirArg ?
|
|
222579
|
-
if (!
|
|
223009
|
+
const root = dirArg ? resolve12(dirArg) : process.cwd();
|
|
223010
|
+
if (!existsSync27(root))
|
|
222580
223011
|
throw new CliError(`No such directory: ${root}`);
|
|
222581
|
-
if (
|
|
223012
|
+
if (existsSync27(join29(root, LECODES_DIR, "manifest.json")) && !flagBool(args, "force")) {
|
|
222582
223013
|
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.");
|
|
222583
223014
|
}
|
|
222584
223015
|
let enclosing = null;
|
|
@@ -222605,7 +223036,7 @@ var link = async (args) => {
|
|
|
222605
223036
|
}
|
|
222606
223037
|
name = project.name;
|
|
222607
223038
|
} else {
|
|
222608
|
-
name = flagStr(args, "name") ??
|
|
223039
|
+
name = flagStr(args, "name") ?? basename15(root);
|
|
222609
223040
|
info(c.dim(`Creating project ${c.bold(name)}…`));
|
|
222610
223041
|
uuid = (await createProject(apiUrl, token, name, flagStr(args, "team"))).uuid;
|
|
222611
223042
|
files = {};
|
|
@@ -222642,10 +223073,10 @@ var link = async (args) => {
|
|
|
222642
223073
|
|
|
222643
223074
|
// src/commands/lightmap.ts
|
|
222644
223075
|
init_manifest();
|
|
222645
|
-
import { existsSync as
|
|
222646
|
-
import { spawnSync as
|
|
222647
|
-
import { tmpdir as
|
|
222648
|
-
import { join as
|
|
223076
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync21, mkdtempSync as mkdtempSync4, rmSync as rmSync9, statSync as statSync10, unlinkSync, writeFileSync as writeFileSync22 } from "node:fs";
|
|
223077
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
223078
|
+
import { tmpdir as tmpdir4 } from "node:os";
|
|
223079
|
+
import { join as join30, relative as relative4, resolve as resolve13 } from "node:path";
|
|
222649
223080
|
init_util();
|
|
222650
223081
|
var num = (args, key, fallback, min, max) => {
|
|
222651
223082
|
const raw = flagStr(args, key);
|
|
@@ -222658,20 +223089,20 @@ var num = (args, key, fallback, min, max) => {
|
|
|
222658
223089
|
};
|
|
222659
223090
|
var findToktx = () => {
|
|
222660
223091
|
const env = process.env.KTX_TOKTX;
|
|
222661
|
-
if (env &&
|
|
223092
|
+
if (env && existsSync28(env))
|
|
222662
223093
|
return env;
|
|
222663
|
-
for (const base of [process.env.LOCALAPPDATA &&
|
|
223094
|
+
for (const base of [process.env.LOCALAPPDATA && join30(process.env.LOCALAPPDATA, "Programs"), process.env.ProgramFiles]) {
|
|
222664
223095
|
if (!base)
|
|
222665
223096
|
continue;
|
|
222666
|
-
const guess =
|
|
222667
|
-
if (
|
|
223097
|
+
const guess = join30(base, "KTX-Software", "bin", "toktx.exe");
|
|
223098
|
+
if (existsSync28(guess))
|
|
222668
223099
|
return guess;
|
|
222669
223100
|
}
|
|
222670
223101
|
return "toktx";
|
|
222671
223102
|
};
|
|
222672
223103
|
var checkToktx = (toktx) => {
|
|
222673
223104
|
try {
|
|
222674
|
-
const p2 =
|
|
223105
|
+
const p2 = spawnSync6(toktx, ["--version"], { encoding: "utf8" });
|
|
222675
223106
|
if (p2.status === 0)
|
|
222676
223107
|
return (p2.stdout ?? "").toString().trim() || "toktx";
|
|
222677
223108
|
} catch {}
|
|
@@ -222687,7 +223118,7 @@ var requireToktx = () => {
|
|
|
222687
223118
|
};
|
|
222688
223119
|
var encodeKtx2 = (toktx, png2, ktx2) => {
|
|
222689
223120
|
const args = ["--t2", "--encode", "uastc", "--uastc_quality", "2", "--zcmp", "18", "--genmipmap", "--assign_oetf", "srgb", ktx2, png2];
|
|
222690
|
-
const p2 =
|
|
223121
|
+
const p2 = spawnSync6(toktx, args, { encoding: "utf8" });
|
|
222691
223122
|
if (p2.status !== 0) {
|
|
222692
223123
|
throw new CliError(`toktx failed (${p2.status ?? p2.error?.message ?? "?"}) while encoding ${ktx2}:
|
|
222693
223124
|
${(p2.stderr || p2.stdout || "").trim()}`);
|
|
@@ -222701,10 +223132,10 @@ var bake = async (args) => {
|
|
|
222701
223132
|
root = process.cwd();
|
|
222702
223133
|
}
|
|
222703
223134
|
const toktx = requireToktx();
|
|
222704
|
-
const outDir =
|
|
223135
|
+
const outDir = resolve13(root, flagStr(args, "out") ?? "assets/lightmap");
|
|
222705
223136
|
const entryFlag = flagStr(args, "entry");
|
|
222706
223137
|
const entryFile = entryFlag ?? "main.ts";
|
|
222707
|
-
if (!
|
|
223138
|
+
if (!existsSync28(resolve13(root, entryFile))) {
|
|
222708
223139
|
throw new CliError(`No ${entryFile} in ${root} — pass --entry <file> (the file that builds the scene and calls Lightmap.load).`);
|
|
222709
223140
|
}
|
|
222710
223141
|
const cfg = {
|
|
@@ -222727,7 +223158,7 @@ var bake = async (args) => {
|
|
|
222727
223158
|
const entryText = `globalThis.__lecodesLightmap = ${JSON.stringify(cfg)}
|
|
222728
223159
|
import ${JSON.stringify(importPath)}
|
|
222729
223160
|
`;
|
|
222730
|
-
|
|
223161
|
+
mkdirSync21(outDir, { recursive: true });
|
|
222731
223162
|
ensurePlaceholders(outDir);
|
|
222732
223163
|
const desktopRenderer = resolveDesktopRenderer(root, args);
|
|
222733
223164
|
const { js, resources } = await buildBundle(args, undefined, {
|
|
@@ -222736,8 +223167,8 @@ import ${JSON.stringify(importPath)}
|
|
|
222736
223167
|
extraEntries: [{ path: entryPath, type: "text", text: entryText }],
|
|
222737
223168
|
entryOverride: entryPath
|
|
222738
223169
|
});
|
|
222739
|
-
const scratch =
|
|
222740
|
-
const outPng =
|
|
223170
|
+
const scratch = mkdtempSync4(join30(tmpdir4(), "lecodes-lightmap-"));
|
|
223171
|
+
const outPng = join30(scratch, "frame.png");
|
|
222741
223172
|
note(`Baking ${cfg.size}² at ${(cfg.texel * 100).toFixed(1)} cm texels (${cfg.sunRays} sun + ${cfg.aoRays} AO rays) → ${relative4(root, outDir) || "."}…`);
|
|
222742
223173
|
let output = "";
|
|
222743
223174
|
try {
|
|
@@ -222759,7 +223190,7 @@ import ${JSON.stringify(importPath)}
|
|
|
222759
223190
|
throw e2;
|
|
222760
223191
|
output = msg;
|
|
222761
223192
|
} finally {
|
|
222762
|
-
|
|
223193
|
+
rmSync9(scratch, { recursive: true, force: true });
|
|
222763
223194
|
}
|
|
222764
223195
|
const lines = output.split(/\r?\n/).filter((l2) => l2.startsWith("[lightmap]"));
|
|
222765
223196
|
for (const l2 of lines) {
|
|
@@ -222769,17 +223200,17 @@ import ${JSON.stringify(importPath)}
|
|
|
222769
223200
|
logErr(text.startsWith("error") ? c.red(`✗ ${text}`) : c.dim(` ${text}`));
|
|
222770
223201
|
}
|
|
222771
223202
|
if (/\[lightmap\] done/.test(output)) {
|
|
222772
|
-
const png2 =
|
|
222773
|
-
const ktx2 =
|
|
222774
|
-
if (!
|
|
223203
|
+
const png2 = join30(outDir, "lightmap.png");
|
|
223204
|
+
const ktx2 = join30(outDir, "lightmap.ktx2");
|
|
223205
|
+
if (!existsSync28(png2))
|
|
222775
223206
|
throw new CliError(`The host reported a bake but ${png2} is missing.`);
|
|
222776
223207
|
const t0 = Date.now();
|
|
222777
223208
|
encodeKtx2(toktx, png2, ktx2);
|
|
222778
|
-
const pngKb = Math.round(
|
|
223209
|
+
const pngKb = Math.round(statSync10(png2).size / 1024), ktxKb = Math.round(statSync10(ktx2).size / 1024);
|
|
222779
223210
|
unlinkSync(png2);
|
|
222780
223211
|
logErr(c.dim(` encoded lightmap.ktx2 (UASTC + mips, ${ktxKb} KB from a ${pngKb} KB raster) in ${((Date.now() - t0) / 1000).toFixed(1)} s`));
|
|
222781
223212
|
logErr(c.green(`✓ baked → ${relative4(root, outDir) || "."}/lightmap.ktx2 + lightmap.bake`));
|
|
222782
|
-
logErr(c.dim(` apply it with Lightmap.load(scene, { data: asset('<rel>/${relative4(root,
|
|
223213
|
+
logErr(c.dim(` apply it with Lightmap.load(scene, { data: asset('<rel>/${relative4(root, join30(outDir, "lightmap.bake")).replace(/\\/g, "/")}'), texture: asset('<rel>/${relative4(root, ktx2).replace(/\\/g, "/")}') }) — asset() paths are relative to the calling file`));
|
|
222783
223214
|
return;
|
|
222784
223215
|
}
|
|
222785
223216
|
if (lines.some((l2) => /no bake module/.test(l2))) {
|
|
@@ -222792,13 +223223,13 @@ import ${JSON.stringify(importPath)}
|
|
|
222792
223223
|
};
|
|
222793
223224
|
var PLACEHOLDER_KTX2 = Buffer.from("q0tUWCAyMLsNChoKAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAAAAAaAAAACwAAACUAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAQAQAAAAAAABAAAAAAAAAAEAAAAAAAAAAsAAAAAAAAAAIAKACmAQIAAwMAABAAAAAAAAAAAAB/AAAAAAAAAAAA/////xEAAABLVFhvcmllbnRhdGlvbgByAAAAACcAAABLVFh3cml0ZXIAdG9rdHggdjQuNC4yIC8gbGlia3R4IHY0LjQuMgAAMwAAAEtUWHdyaXRlclNjUGFyYW1zAC0tZW5jb2RlIHVhc3RjIC0tdWFzdGNfcXVhbGl0eSAwAAD3////H3zvAQAAAAAAAAAA", "base64");
|
|
222794
223225
|
var ensurePlaceholders = (outDir) => {
|
|
222795
|
-
const bake2 =
|
|
222796
|
-
const ktx2 =
|
|
222797
|
-
if (!
|
|
222798
|
-
|
|
223226
|
+
const bake2 = join30(outDir, "lightmap.bake");
|
|
223227
|
+
const ktx2 = join30(outDir, "lightmap.ktx2");
|
|
223228
|
+
if (!existsSync28(bake2))
|
|
223229
|
+
writeFileSync22(bake2, JSON.stringify({ version: 1, placeholder: true, size: 1, texel: 0, instances: [] }) + `
|
|
222799
223230
|
`);
|
|
222800
|
-
if (!
|
|
222801
|
-
|
|
223231
|
+
if (!existsSync28(ktx2))
|
|
223232
|
+
writeFileSync22(ktx2, PLACEHOLDER_KTX2);
|
|
222802
223233
|
};
|
|
222803
223234
|
var lightmap = async (args) => {
|
|
222804
223235
|
const sub = args._[0];
|
|
@@ -222824,7 +223255,7 @@ var login = async (args) => {
|
|
|
222824
223255
|
|
|
222825
223256
|
// src/commands/shaders.ts
|
|
222826
223257
|
init_manifest();
|
|
222827
|
-
import { readFileSync as
|
|
223258
|
+
import { readFileSync as readFileSync25 } from "node:fs";
|
|
222828
223259
|
init_util();
|
|
222829
223260
|
init_compile();
|
|
222830
223261
|
var collectShaders = () => {
|
|
@@ -222856,7 +223287,7 @@ var status2 = async () => {
|
|
|
222856
223287
|
if (matcId === null)
|
|
222857
223288
|
state = c.dim("unknown (matc not downloaded)");
|
|
222858
223289
|
else {
|
|
222859
|
-
const text =
|
|
223290
|
+
const text = readFileSync25(shader.absPath, "utf8");
|
|
222860
223291
|
if (shaderIsCompiled(root, text, matcId))
|
|
222861
223292
|
state = "compiled";
|
|
222862
223293
|
else {
|
|
@@ -222865,7 +223296,7 @@ var status2 = async () => {
|
|
|
222865
223296
|
}
|
|
222866
223297
|
}
|
|
222867
223298
|
log(` ${shader.path.padEnd(width)} ${state}`);
|
|
222868
|
-
const schema = parseShaderSchema(
|
|
223299
|
+
const schema = parseShaderSchema(readFileSync25(shader.absPath, "utf8"));
|
|
222869
223300
|
for (const p2 of schema.params) {
|
|
222870
223301
|
const range = p2.min !== undefined ? ` [${p2.min}..${p2.max}]` : "";
|
|
222871
223302
|
const def = p2.default !== null && p2.default !== undefined ? ` = ${JSON.stringify(p2.default)}` : "";
|
|
@@ -222908,7 +223339,7 @@ var compile3 = async (args) => {
|
|
|
222908
223339
|
}
|
|
222909
223340
|
const matcExe = await resolveMatcExe();
|
|
222910
223341
|
const matcId = matcIdentity(matcExe);
|
|
222911
|
-
const wasCompiled = new Map(shaders.map((s2) => [s2.path, shaderIsCompiled(root,
|
|
223342
|
+
const wasCompiled = new Map(shaders.map((s2) => [s2.path, shaderIsCompiled(root, readFileSync25(s2.absPath, "utf8"), matcId)]));
|
|
222912
223343
|
const errors2 = [];
|
|
222913
223344
|
for (const shader of shaders) {
|
|
222914
223345
|
try {
|
|
@@ -222942,8 +223373,8 @@ var shaders = async (args) => {
|
|
|
222942
223373
|
};
|
|
222943
223374
|
|
|
222944
223375
|
// src/commands/render.ts
|
|
222945
|
-
import { mkdirSync as
|
|
222946
|
-
import { dirname as dirname15, resolve as
|
|
223376
|
+
import { mkdirSync as mkdirSync22, readFileSync as readFileSync27, writeFileSync as writeFileSync23 } from "node:fs";
|
|
223377
|
+
import { dirname as dirname15, resolve as resolve16 } from "node:path";
|
|
222947
223378
|
|
|
222948
223379
|
// src/compile/headlessBundle.ts
|
|
222949
223380
|
init_api();
|
|
@@ -223027,23 +223458,23 @@ var compileHeadlessBundle = async (args, opts = {}) => {
|
|
|
223027
223458
|
// src/compile/screenEntry.ts
|
|
223028
223459
|
init_designMeta();
|
|
223029
223460
|
init_util();
|
|
223030
|
-
import { existsSync as
|
|
223031
|
-
import { isAbsolute, join as
|
|
223461
|
+
import { existsSync as existsSync29, readFileSync as readFileSync26, statSync as statSync11 } from "node:fs";
|
|
223462
|
+
import { isAbsolute, join as join31, relative as relative5, resolve as resolve14 } from "node:path";
|
|
223032
223463
|
var SCREEN_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs"];
|
|
223033
223464
|
var SCREEN_ENTRY_PATH = "/.screen-entry.ts";
|
|
223034
223465
|
var DEFAULT_DESIGN_DIR = "design";
|
|
223035
223466
|
var screenArg = (args) => args._[0];
|
|
223036
223467
|
var hasSeparator = (file) => file.includes("/") || file.includes("\\");
|
|
223037
223468
|
var findScreenFile = (root, file, dirFlag) => {
|
|
223038
|
-
const candidates = [
|
|
223469
|
+
const candidates = [resolve14(process.cwd(), file)];
|
|
223039
223470
|
if (!hasSeparator(file)) {
|
|
223040
|
-
candidates.push(
|
|
223471
|
+
candidates.push(join31(root, (dirFlag ?? DEFAULT_DESIGN_DIR).replace(/[/\\]+$/, ""), "screens", file));
|
|
223041
223472
|
}
|
|
223042
223473
|
for (const base of candidates) {
|
|
223043
|
-
if (
|
|
223474
|
+
if (existsSync29(base) && statSync11(base).isFile())
|
|
223044
223475
|
return base;
|
|
223045
223476
|
for (const ext2 of SCREEN_EXTS)
|
|
223046
|
-
if (
|
|
223477
|
+
if (existsSync29(base + ext2))
|
|
223047
223478
|
return base + ext2;
|
|
223048
223479
|
}
|
|
223049
223480
|
throw new CliError(`No screen file at "${file}".` + (hasSeparator(file) ? "" : ` Pass a path, or a screen id that exists in ${dirFlag ?? DEFAULT_DESIGN_DIR}/screens/.`));
|
|
@@ -223052,8 +223483,8 @@ var designDirOf = (root, key) => {
|
|
|
223052
223483
|
const m = /^\/(.*)screens\/([^/]+)\.[^./]+$/.exec(key);
|
|
223053
223484
|
if (!m)
|
|
223054
223485
|
return null;
|
|
223055
|
-
const dir =
|
|
223056
|
-
return
|
|
223486
|
+
const dir = join31(root, m[1]);
|
|
223487
|
+
return existsSync29(join31(dir, "meta.json")) ? { dir, id: m[2] } : null;
|
|
223057
223488
|
};
|
|
223058
223489
|
var resolveScreenTarget = (root, file, opts = {}) => {
|
|
223059
223490
|
const abs = findScreenFile(root, file, opts.dir);
|
|
@@ -223062,7 +223493,7 @@ var resolveScreenTarget = (root, file, opts = {}) => {
|
|
|
223062
223493
|
throw new CliError(`"${file}" is outside the project (${root}) — a screen renders as part of its project, so its ` + `imports and asset() files resolve from the same tree.`);
|
|
223063
223494
|
}
|
|
223064
223495
|
const key = toKey(rel);
|
|
223065
|
-
if (!/^\s*export\s+default\b/m.test(
|
|
223496
|
+
if (!/^\s*export\s+default\b/m.test(readFileSync26(abs, "utf8"))) {
|
|
223066
223497
|
throw new CliError(`${key.slice(1)} has no default export. A screen file ends with ` + `\`export default UIScreen([...])\` — or \`export default (state) => UIScreen([...])\` for screen states.`);
|
|
223067
223498
|
}
|
|
223068
223499
|
const invoke = opts.state !== undefined ? `screen(${JSON.stringify(opts.state)})` : "screen()";
|
|
@@ -223101,7 +223532,7 @@ init_manifest();
|
|
|
223101
223532
|
|
|
223102
223533
|
// src/desktopScript.ts
|
|
223103
223534
|
init_util();
|
|
223104
|
-
import { resolve as
|
|
223535
|
+
import { resolve as resolve15 } from "node:path";
|
|
223105
223536
|
var isPoint = (v) => typeof v === "object" && v !== null && typeof v.x === "number" && typeof v.y === "number";
|
|
223106
223537
|
var FRAME_MS = 16;
|
|
223107
223538
|
var ACTIONS = ["tap", "type", "fill", "scroll", "wait", "frames", "drag", "hold", "key", "keyDown", "keyUp", "look", "mouseTo", "wheel", "stick", "gamepad", "camera", "view"];
|
|
@@ -223302,7 +223733,7 @@ var translateScenario = (rawSteps, opts) => {
|
|
|
223302
223733
|
if (s2.screenshot !== undefined) {
|
|
223303
223734
|
if (typeof s2.screenshot !== "string")
|
|
223304
223735
|
throw new CliError(`${at}: "screenshot" must be a path.`);
|
|
223305
|
-
plan.screenshot =
|
|
223736
|
+
plan.screenshot = resolve15(opts.outDir, s2.screenshot);
|
|
223306
223737
|
plan.lines.push(emit(`frames 2`));
|
|
223307
223738
|
plan.lines.push(emit(`shot ${plan.screenshot}`));
|
|
223308
223739
|
budgetMs += 3 * FRAME_MS;
|
|
@@ -223411,9 +223842,9 @@ var requireRenderer = async () => {
|
|
|
223411
223842
|
};
|
|
223412
223843
|
var fileScenarioIO = (dir) => ({
|
|
223413
223844
|
write: (relPath, data) => {
|
|
223414
|
-
const abs =
|
|
223415
|
-
|
|
223416
|
-
|
|
223845
|
+
const abs = resolve16(dir, relPath);
|
|
223846
|
+
mkdirSync22(dirname15(abs), { recursive: true });
|
|
223847
|
+
writeFileSync23(abs, data);
|
|
223417
223848
|
}
|
|
223418
223849
|
});
|
|
223419
223850
|
var resolveFixtures = (value, baseDir) => {
|
|
@@ -223421,9 +223852,9 @@ var resolveFixtures = (value, baseDir) => {
|
|
|
223421
223852
|
return;
|
|
223422
223853
|
let map = value;
|
|
223423
223854
|
if (typeof value === "string") {
|
|
223424
|
-
const abs =
|
|
223855
|
+
const abs = resolve16(baseDir, value);
|
|
223425
223856
|
try {
|
|
223426
|
-
map = JSON.parse(
|
|
223857
|
+
map = JSON.parse(readFileSync27(abs, "utf8"));
|
|
223427
223858
|
} catch (e2) {
|
|
223428
223859
|
throw new CliError(`Can't read fixtures "${value}": ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
223429
223860
|
}
|
|
@@ -223436,7 +223867,7 @@ var resolveFixtures = (value, baseDir) => {
|
|
|
223436
223867
|
var readScenarioFile = (path2) => {
|
|
223437
223868
|
let raw;
|
|
223438
223869
|
try {
|
|
223439
|
-
raw = JSON.parse(
|
|
223870
|
+
raw = JSON.parse(readFileSync27(resolve16(path2), "utf8"));
|
|
223440
223871
|
} catch (e2) {
|
|
223441
223872
|
throw new CliError(`Can't read scenario "${path2}": ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
223442
223873
|
}
|
|
@@ -223493,13 +223924,13 @@ var renderDesktop = async (args) => {
|
|
|
223493
223924
|
extraEntries: screen2 ? [screen2.entry] : undefined,
|
|
223494
223925
|
entryOverride: screen2?.entryPath
|
|
223495
223926
|
});
|
|
223496
|
-
|
|
223927
|
+
mkdirSync22(dirname15(resolve16(pngFlag)), { recursive: true });
|
|
223497
223928
|
if (scenario) {
|
|
223498
223929
|
const outDir = flagStr(args, "out-dir") ?? ".";
|
|
223499
223930
|
const plan = translateScenario(scenario.steps, { outDir, headroomFrames: frames });
|
|
223500
223931
|
for (const st of plan.steps)
|
|
223501
223932
|
if (st.screenshot)
|
|
223502
|
-
|
|
223933
|
+
mkdirSync22(dirname15(st.screenshot), { recursive: true });
|
|
223503
223934
|
const timeoutMs2 = Number(flagStr(args, "timeout")) || 60000 + plan.steps.length * 5000;
|
|
223504
223935
|
note(`Running ${plan.steps.length} step${plan.steps.length === 1 ? "" : "s"} (native)…`);
|
|
223505
223936
|
const { output } = await runDesktopRender(js, resources, {
|
|
@@ -223603,7 +224034,7 @@ var render = async (args) => {
|
|
|
223603
224034
|
const settleStr = flagStr(args, "settle") ?? (scenario?.settle !== undefined ? String(scenario.settle) : undefined);
|
|
223604
224035
|
const timeStr = flagStr(args, "time");
|
|
223605
224036
|
const onConsole = flagBool(args, "logs") ? (level, text2) => logErr(`${c.dim(`[${level}]`)} ${text2}`) : undefined;
|
|
223606
|
-
const envelopeFixtures = resolveFixtures(scenario?.fixtures, scriptFlag !== undefined ? dirname15(
|
|
224037
|
+
const envelopeFixtures = resolveFixtures(scenario?.fixtures, scriptFlag !== undefined ? dirname15(resolve16(scriptFlag)) : ".");
|
|
223607
224038
|
const flagFixtures = resolveFixtures(flagStr(args, "fixtures"), ".");
|
|
223608
224039
|
const fixtures = envelopeFixtures || flagFixtures ? { ...envelopeFixtures, ...flagFixtures } : undefined;
|
|
223609
224040
|
if (fixtures) {
|
|
@@ -223642,7 +224073,7 @@ var render = async (args) => {
|
|
|
223642
224073
|
${c.red(st.error ?? "failed")}`);
|
|
223643
224074
|
}
|
|
223644
224075
|
if (!result2.ok && result2.failureArtifacts.length > 0) {
|
|
223645
|
-
logErr(c.dim(` failing frame: ${result2.failureArtifacts.map((p2) =>
|
|
224076
|
+
logErr(c.dim(` failing frame: ${result2.failureArtifacts.map((p2) => resolve16(outDir, p2)).join(", ")}`));
|
|
223646
224077
|
}
|
|
223647
224078
|
logErr(result2.ok ? c.green(`✓ ${result2.steps.length}/${steps.length} steps passed`) : c.red(`✗ failed at step ${result2.steps.length} of ${steps.length}`));
|
|
223648
224079
|
return done((cb) => cb(), result2.ok ? 0 : 1);
|
|
@@ -223666,8 +224097,8 @@ var render = async (args) => {
|
|
|
223666
224097
|
if (isNullSink(pngFlag)) {
|
|
223667
224098
|
note(`Discarded PNG (${pngFlag})`);
|
|
223668
224099
|
} else {
|
|
223669
|
-
|
|
223670
|
-
|
|
224100
|
+
mkdirSync22(dirname15(resolve16(pngFlag)), { recursive: true });
|
|
224101
|
+
writeFileSync23(resolve16(pngFlag), png2);
|
|
223671
224102
|
note(`Wrote ${pngFlag} (${png2.length} bytes, ${outW}x${outH}${clip !== undefined ? ", clipped" : ""})`);
|
|
223672
224103
|
}
|
|
223673
224104
|
return done((cb) => cb());
|
|
@@ -223682,7 +224113,7 @@ var render = async (args) => {
|
|
|
223682
224113
|
return done((cb) => cb());
|
|
223683
224114
|
}
|
|
223684
224115
|
if (out) {
|
|
223685
|
-
|
|
224116
|
+
writeFileSync23(resolve16(out), result.text);
|
|
223686
224117
|
note(`Wrote ${out} (${result.text.length} bytes)`);
|
|
223687
224118
|
return done((cb) => cb());
|
|
223688
224119
|
}
|
|
@@ -223697,8 +224128,8 @@ init_api();
|
|
|
223697
224128
|
init_config();
|
|
223698
224129
|
init_manifest();
|
|
223699
224130
|
init_util();
|
|
223700
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
223701
|
-
import { join as
|
|
224131
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync30, readFileSync as readFileSync28, writeFileSync as writeFileSync24 } from "node:fs";
|
|
224132
|
+
import { join as join32 } from "node:path";
|
|
223702
224133
|
var ENV_VAR = "LECODES_PUSH_KEY";
|
|
223703
224134
|
var context = () => {
|
|
223704
224135
|
const root = findProjectRoot(process.cwd());
|
|
@@ -223842,11 +224273,11 @@ var pnKeysList = async (ctx) => {
|
|
|
223842
224273
|
};
|
|
223843
224274
|
var writeEnv = (root, key) => {
|
|
223844
224275
|
const touched = [];
|
|
223845
|
-
const envPath =
|
|
224276
|
+
const envPath = join32(root, ".env");
|
|
223846
224277
|
const line = `${ENV_VAR}=${key}`;
|
|
223847
|
-
const existing =
|
|
224278
|
+
const existing = existsSync30(envPath) ? readFileSync28(envPath, "utf8") : "";
|
|
223848
224279
|
if (new RegExp(`^${ENV_VAR}=`, "m").test(existing)) {
|
|
223849
|
-
|
|
224280
|
+
writeFileSync24(envPath, existing.replace(new RegExp(`^${ENV_VAR}=.*$`, "m"), line));
|
|
223850
224281
|
} else {
|
|
223851
224282
|
const separator = !existing || existing.endsWith(`
|
|
223852
224283
|
`) ? "" : `
|
|
@@ -223855,8 +224286,8 @@ var writeEnv = (root, key) => {
|
|
|
223855
224286
|
`);
|
|
223856
224287
|
}
|
|
223857
224288
|
touched.push(".env");
|
|
223858
|
-
const ignorePath =
|
|
223859
|
-
const ignore =
|
|
224289
|
+
const ignorePath = join32(root, ".gitignore");
|
|
224290
|
+
const ignore = existsSync30(ignorePath) ? readFileSync28(ignorePath, "utf8") : "";
|
|
223860
224291
|
if (!ignore.split(`
|
|
223861
224292
|
`).some((l2) => l2.trim() === ".env")) {
|
|
223862
224293
|
const separator = !ignore || ignore.endsWith(`
|
|
@@ -223955,8 +224386,8 @@ var pn = async (args) => {
|
|
|
223955
224386
|
init_api();
|
|
223956
224387
|
init_config();
|
|
223957
224388
|
init_manifest();
|
|
223958
|
-
import { existsSync as
|
|
223959
|
-
import { dirname as dirname16, join as
|
|
224389
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync23, rmSync as rmSync10, writeFileSync as writeFileSync25 } from "node:fs";
|
|
224390
|
+
import { dirname as dirname16, join as join33 } from "node:path";
|
|
223960
224391
|
init_types4();
|
|
223961
224392
|
init_localFiles();
|
|
223962
224393
|
init_util();
|
|
@@ -223979,9 +224410,9 @@ var pull = async (args) => {
|
|
|
223979
224410
|
const remotePaths = new Set([...buildPaths(project.assets).entries()].filter(([id]) => project.assets.find((a2) => a2.id === id)?.type !== "folder").map(([, path2]) => path2));
|
|
223980
224411
|
for (const path2 of Object.keys(manifest.files)) {
|
|
223981
224412
|
if (!remotePaths.has(path2)) {
|
|
223982
|
-
const abs =
|
|
223983
|
-
if (
|
|
223984
|
-
|
|
224413
|
+
const abs = join33(root, ...path2.replace(/^\//, "").split("/"));
|
|
224414
|
+
if (existsSync31(abs))
|
|
224415
|
+
rmSync10(abs);
|
|
223985
224416
|
}
|
|
223986
224417
|
}
|
|
223987
224418
|
const files = await materializeAssets(root, project, apiUrl, token, progress);
|
|
@@ -224018,18 +224449,18 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
|
|
|
224018
224449
|
const files = { ...manifest.files };
|
|
224019
224450
|
for (const asset of targets) {
|
|
224020
224451
|
const key = pathById.get(asset.id);
|
|
224021
|
-
const abs =
|
|
224022
|
-
|
|
224452
|
+
const abs = join33(root, ...key.replace(/^\//, "").split("/"));
|
|
224453
|
+
mkdirSync23(dirname16(abs), { recursive: true });
|
|
224023
224454
|
let sha;
|
|
224024
224455
|
if (asset.type === "file") {
|
|
224025
224456
|
if (!asset.file?.src)
|
|
224026
224457
|
continue;
|
|
224027
224458
|
const bytes = await downloadBlob(apiUrl, asset.file.src);
|
|
224028
|
-
|
|
224459
|
+
writeFileSync25(abs, bytes);
|
|
224029
224460
|
sha = sha256(bytes);
|
|
224030
224461
|
} else {
|
|
224031
224462
|
const text = asset.text ?? "";
|
|
224032
|
-
|
|
224463
|
+
writeFileSync25(abs, text);
|
|
224033
224464
|
sha = sha256(text);
|
|
224034
224465
|
}
|
|
224035
224466
|
files[key] = { id: asset.id, type: kindOf3(asset.type), sha };
|
|
@@ -224042,8 +224473,8 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
|
|
|
224042
224473
|
|
|
224043
224474
|
// src/commands/scene.ts
|
|
224044
224475
|
init_manifest();
|
|
224045
|
-
import { existsSync as
|
|
224046
|
-
import { basename as
|
|
224476
|
+
import { existsSync as existsSync33, readFileSync as readFileSync30, readdirSync as readdirSync14, statSync as statSync12, writeFileSync as writeFileSync27 } from "node:fs";
|
|
224477
|
+
import { basename as basename16, dirname as dirname17, join as join35, resolve as resolve17 } from "node:path";
|
|
224047
224478
|
|
|
224048
224479
|
// src/compile/sceneCompile.ts
|
|
224049
224480
|
init_localFiles();
|
|
@@ -224052,18 +224483,18 @@ init_distRoot();
|
|
|
224052
224483
|
init_util();
|
|
224053
224484
|
init_projectCompile();
|
|
224054
224485
|
import { createHash as createHash8 } from "node:crypto";
|
|
224055
|
-
import { existsSync as
|
|
224056
|
-
import { join as
|
|
224486
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync24, readFileSync as readFileSync29, writeFileSync as writeFileSync26 } from "node:fs";
|
|
224487
|
+
import { join as join34 } from "node:path";
|
|
224057
224488
|
var TEXT_EXT = /\.(ts|js|json|svg|txt|md)$/i;
|
|
224058
224489
|
var rel = (key) => key.replace(/^\//, "");
|
|
224059
224490
|
var resolveHarness = (loadSceneHarness2) => {
|
|
224060
|
-
const inRepo =
|
|
224491
|
+
const inRepo = join34(distRoot, "..", "projects", "src", "scene-editor");
|
|
224061
224492
|
try {
|
|
224062
|
-
return loadSceneHarness2(
|
|
224493
|
+
return loadSceneHarness2(existsSync32(inRepo) ? inRepo : undefined);
|
|
224063
224494
|
} catch {
|
|
224064
|
-
const vendored =
|
|
224065
|
-
if (
|
|
224066
|
-
return JSON.parse(
|
|
224495
|
+
const vendored = join34(distRoot, "runtime", "scene-harness.json");
|
|
224496
|
+
if (existsSync32(vendored))
|
|
224497
|
+
return JSON.parse(readFileSync29(vendored, "utf8"));
|
|
224067
224498
|
throw new CliError("scene-editor harness sources not found — reinstall the CLI or set LECODES_SCENE_HARNESS_DIR.");
|
|
224068
224499
|
}
|
|
224069
224500
|
};
|
|
@@ -224075,9 +224506,9 @@ var compileSceneEditor = async (opts) => {
|
|
|
224075
224506
|
for (const file of scan2.files) {
|
|
224076
224507
|
const path2 = "./" + rel(file.path);
|
|
224077
224508
|
if (TEXT_EXT.test(path2)) {
|
|
224078
|
-
entries.push({ path: path2, type: "text", text:
|
|
224509
|
+
entries.push({ path: path2, type: "text", text: readFileSync29(file.absPath, "utf8") });
|
|
224079
224510
|
} else if (/\.mat$/i.test(path2)) {
|
|
224080
|
-
entries.push({ path: path2, type: "shader", text:
|
|
224511
|
+
entries.push({ path: path2, type: "shader", text: readFileSync29(file.absPath, "utf8"), absPath: file.absPath });
|
|
224081
224512
|
} else {
|
|
224082
224513
|
entries.push({ path: path2, type: "resource", fileSrc: "/project/" + rel(file.path) });
|
|
224083
224514
|
}
|
|
@@ -224093,10 +224524,10 @@ var compileSceneEditor = async (opts) => {
|
|
|
224093
224524
|
continue;
|
|
224094
224525
|
const hash = createHash8("sha256").update(e2.text ?? "").digest("hex").slice(0, 16);
|
|
224095
224526
|
const dir = shadersCacheDir(root);
|
|
224096
|
-
|
|
224527
|
+
mkdirSync24(dir, { recursive: true });
|
|
224097
224528
|
const liteName = `${hash}_webgl-lite.filamat`;
|
|
224098
|
-
if (!
|
|
224099
|
-
|
|
224529
|
+
if (!existsSync32(join34(dir, liteName)))
|
|
224530
|
+
writeFileSync26(join34(dir, liteName), JSON.stringify(buildLiteMaterialArtifact2(e2.text ?? "")));
|
|
224100
224531
|
const src = `/project/.lecodes/shaders/${hash}_opengl.filamat`;
|
|
224101
224532
|
e2.shaders = ["opengl", "webgl", "webgl-lite"].map((platform) => ({ platform, src }));
|
|
224102
224533
|
}
|
|
@@ -224130,7 +224561,7 @@ var DEFAULT_PORT3 = 4488;
|
|
|
224130
224561
|
var findAppRoot = (start) => {
|
|
224131
224562
|
let dir = start;
|
|
224132
224563
|
while (true) {
|
|
224133
|
-
if (
|
|
224564
|
+
if (existsSync33(join35(dir, "app.json")))
|
|
224134
224565
|
return dir;
|
|
224135
224566
|
const parent = dirname17(dir);
|
|
224136
224567
|
if (parent === dir)
|
|
@@ -224149,10 +224580,10 @@ var getContext2 = (args) => {
|
|
|
224149
224580
|
manifest = null;
|
|
224150
224581
|
const pathArg = args._[0];
|
|
224151
224582
|
if (pathArg) {
|
|
224152
|
-
const abs =
|
|
224153
|
-
if (
|
|
224583
|
+
const abs = resolve17(process.cwd(), pathArg);
|
|
224584
|
+
if (existsSync33(abs) && statSync12(abs).isDirectory())
|
|
224154
224585
|
root = abs;
|
|
224155
|
-
else if (abs.endsWith(".scene.ts") &&
|
|
224586
|
+
else if (abs.endsWith(".scene.ts") && existsSync33(abs))
|
|
224156
224587
|
root = findAppRoot(dirname17(abs)) ?? dirname17(abs);
|
|
224157
224588
|
else
|
|
224158
224589
|
throw new CliError(`No such folder or .scene.ts: ${pathArg}`);
|
|
@@ -224160,15 +224591,15 @@ var getContext2 = (args) => {
|
|
|
224160
224591
|
root = findAppRoot(root) ?? root;
|
|
224161
224592
|
}
|
|
224162
224593
|
}
|
|
224163
|
-
return { root, manifest, name: manifest?.name ??
|
|
224594
|
+
return { root, manifest, name: manifest?.name ?? basename16(root) };
|
|
224164
224595
|
};
|
|
224165
224596
|
var writeMcpConfig2 = (root, port) => {
|
|
224166
|
-
const path2 =
|
|
224597
|
+
const path2 = join35(root, ".mcp.json");
|
|
224167
224598
|
const entry = { type: "http", url: `http://localhost:${port}/mcp` };
|
|
224168
224599
|
let config = {};
|
|
224169
|
-
if (
|
|
224600
|
+
if (existsSync33(path2)) {
|
|
224170
224601
|
try {
|
|
224171
|
-
config = JSON.parse(
|
|
224602
|
+
config = JSON.parse(readFileSync30(path2, "utf8"));
|
|
224172
224603
|
} catch {
|
|
224173
224604
|
warnErr(".mcp.json exists but isn't valid JSON — left it untouched. Add the lecodes-scene server by hand.");
|
|
224174
224605
|
return;
|
|
@@ -224181,17 +224612,17 @@ var writeMcpConfig2 = (root, port) => {
|
|
|
224181
224612
|
return;
|
|
224182
224613
|
}
|
|
224183
224614
|
config.mcpServers["lecodes-scene"] = entry;
|
|
224184
|
-
|
|
224615
|
+
writeFileSync27(path2, JSON.stringify(config, null, 2) + `
|
|
224185
224616
|
`);
|
|
224186
224617
|
note(`${existing ? "updated" : "created"} .mcp.json (lecodes-scene MCP → ${entry.url})`);
|
|
224187
224618
|
};
|
|
224188
224619
|
var countScenes = (dir) => {
|
|
224189
224620
|
let n2 = 0;
|
|
224190
|
-
for (const name of
|
|
224621
|
+
for (const name of readdirSync14(dir)) {
|
|
224191
224622
|
if (name === "node_modules" || name.startsWith("."))
|
|
224192
224623
|
continue;
|
|
224193
|
-
const full =
|
|
224194
|
-
if (
|
|
224624
|
+
const full = join35(dir, name);
|
|
224625
|
+
if (statSync12(full).isDirectory())
|
|
224195
224626
|
n2 += countScenes(full);
|
|
224196
224627
|
else if (name.endsWith(".scene.ts"))
|
|
224197
224628
|
n2++;
|
|
@@ -224236,9 +224667,9 @@ var scene = async (args) => {
|
|
|
224236
224667
|
init_util();
|
|
224237
224668
|
|
|
224238
224669
|
// src/cmgenTool.ts
|
|
224239
|
-
import { existsSync as
|
|
224670
|
+
import { existsSync as existsSync34, readdirSync as readdirSync15 } from "node:fs";
|
|
224240
224671
|
import { homedir as homedir8 } from "node:os";
|
|
224241
|
-
import { join as
|
|
224672
|
+
import { join as join36, resolve as resolve18 } from "node:path";
|
|
224242
224673
|
init_util();
|
|
224243
224674
|
var exeName2 = () => process.platform === "win32" ? "cmgen.exe" : "cmgen";
|
|
224244
224675
|
var platformKey3 = () => {
|
|
@@ -224248,10 +224679,10 @@ var platformKey3 = () => {
|
|
|
224248
224679
|
return "linux-x64";
|
|
224249
224680
|
throw new CliError(process.platform === "darwin" ? "cmgen isn't published for macOS yet — build filament's host tools locally and set LECODES_CMGEN." : `cmgen isn't published for ${process.platform}.`);
|
|
224250
224681
|
};
|
|
224251
|
-
var cacheRoot3 = () =>
|
|
224682
|
+
var cacheRoot3 = () => join36(homedir8(), ".lecodes", "cmgen");
|
|
224252
224683
|
var findCmgenIn = (dir) => {
|
|
224253
|
-
for (const entry of
|
|
224254
|
-
const p2 =
|
|
224684
|
+
for (const entry of readdirSync15(dir, { withFileTypes: true })) {
|
|
224685
|
+
const p2 = join36(dir, entry.name);
|
|
224255
224686
|
if (entry.isFile() && entry.name === exeName2())
|
|
224256
224687
|
return p2;
|
|
224257
224688
|
if (entry.isDirectory()) {
|
|
@@ -224264,7 +224695,7 @@ var findCmgenIn = (dir) => {
|
|
|
224264
224695
|
};
|
|
224265
224696
|
var installedCmgenVersions = () => {
|
|
224266
224697
|
try {
|
|
224267
|
-
return
|
|
224698
|
+
return readdirSync15(cacheRoot3(), { withFileTypes: true }).filter((e2) => e2.isDirectory() && e2.name.startsWith("cmgen-")).map((e2) => e2.name.slice("cmgen-".length)).filter((v) => findCmgenIn(join36(cacheRoot3(), `cmgen-${v}`)) !== null).sort(semverNewer);
|
|
224268
224699
|
} catch {
|
|
224269
224700
|
return [];
|
|
224270
224701
|
}
|
|
@@ -224286,19 +224717,19 @@ var downloadCmgen = async (version, versions, dest) => {
|
|
|
224286
224717
|
var resolveCmgenExeCached = () => {
|
|
224287
224718
|
const override = process.env.LECODES_CMGEN;
|
|
224288
224719
|
if (override) {
|
|
224289
|
-
if (!
|
|
224720
|
+
if (!existsSync34(override))
|
|
224290
224721
|
throw new CliError(`LECODES_CMGEN points at "${override}" which doesn't exist.`);
|
|
224291
|
-
return
|
|
224722
|
+
return resolve18(override);
|
|
224292
224723
|
}
|
|
224293
224724
|
const cached = installedCmgenVersions()[0];
|
|
224294
|
-
return cached ? findCmgenIn(
|
|
224725
|
+
return cached ? findCmgenIn(join36(cacheRoot3(), `cmgen-${cached}`)) : null;
|
|
224295
224726
|
};
|
|
224296
224727
|
var resolveCmgenExe = async () => {
|
|
224297
224728
|
const cached = resolveCmgenExeCached();
|
|
224298
224729
|
if (cached)
|
|
224299
224730
|
return cached;
|
|
224300
224731
|
const { latest, versions } = await fetchCmgenManifest();
|
|
224301
|
-
const dir =
|
|
224732
|
+
const dir = join36(cacheRoot3(), `cmgen-${latest}`);
|
|
224302
224733
|
await downloadCmgen(latest, versions, dir);
|
|
224303
224734
|
const exe = findCmgenIn(dir);
|
|
224304
224735
|
if (!exe)
|
|
@@ -224356,8 +224787,8 @@ var status3 = async (_args) => {
|
|
|
224356
224787
|
};
|
|
224357
224788
|
|
|
224358
224789
|
// src/commands/test.ts
|
|
224359
|
-
import { existsSync as
|
|
224360
|
-
import { dirname as dirname18, resolve as
|
|
224790
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync25, readFileSync as readFileSync31, readdirSync as readdirSync16 } from "node:fs";
|
|
224791
|
+
import { dirname as dirname18, resolve as resolve19 } from "node:path";
|
|
224361
224792
|
init_manifest();
|
|
224362
224793
|
init_util();
|
|
224363
224794
|
var deriveDesignFlows = (meta, entryScreen) => {
|
|
@@ -224420,7 +224851,7 @@ var slug3 = (s2) => s2.replace(/[^a-zA-Z0-9-_]+/g, "-").replace(/^-+|-+$/g, "").
|
|
|
224420
224851
|
var readFlowFile = (abs, displayName) => {
|
|
224421
224852
|
let raw;
|
|
224422
224853
|
try {
|
|
224423
|
-
raw = JSON.parse(
|
|
224854
|
+
raw = JSON.parse(readFileSync31(abs, "utf8"));
|
|
224424
224855
|
} catch (e2) {
|
|
224425
224856
|
throw new CliError(`Can't read "${displayName}": ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
224426
224857
|
}
|
|
@@ -224475,40 +224906,40 @@ var test = async (args) => {
|
|
|
224475
224906
|
if (!Number.isFinite(nativeFixedDt) || nativeFixedDt < 0 || nativeFixedDt > 1000)
|
|
224476
224907
|
throw new CliError(`--fixed-dt wants a number of ms (0 = wall clock, max 1000), got "${fixedDtFlag}".`);
|
|
224477
224908
|
const projectViewport = desktopDefaultViewport(root);
|
|
224478
|
-
const testsDir =
|
|
224479
|
-
const designMetaPath =
|
|
224909
|
+
const testsDir = resolve19(root, flagStr(args, "dir") ?? "tests");
|
|
224910
|
+
const designMetaPath = resolve19(root, flagStr(args, "design-dir") ?? "design", "meta.json");
|
|
224480
224911
|
const asJson = flagBool(args, "json");
|
|
224481
224912
|
const out = asJson ? logErr : log;
|
|
224482
224913
|
const cases = [];
|
|
224483
224914
|
if (args._.length > 0) {
|
|
224484
224915
|
for (const given of args._) {
|
|
224485
|
-
const candidates = [
|
|
224486
|
-
const abs = candidates.find((p2) =>
|
|
224916
|
+
const candidates = [resolve19(given), resolve19(testsDir, given), resolve19(testsDir, `${given}.flow.json`)];
|
|
224917
|
+
const abs = candidates.find((p2) => existsSync35(p2));
|
|
224487
224918
|
if (!abs)
|
|
224488
224919
|
throw new CliError(`No such test: "${given}" (looked in ${testsDir}).`);
|
|
224489
224920
|
cases.push(readFlowFile(abs, given));
|
|
224490
224921
|
}
|
|
224491
|
-
} else if (
|
|
224492
|
-
for (const f3 of
|
|
224493
|
-
cases.push(readFlowFile(
|
|
224922
|
+
} else if (existsSync35(testsDir)) {
|
|
224923
|
+
for (const f3 of readdirSync16(testsDir).filter((f4) => f4.endsWith(".flow.json")).sort()) {
|
|
224924
|
+
cases.push(readFlowFile(resolve19(testsDir, f3), f3));
|
|
224494
224925
|
}
|
|
224495
224926
|
}
|
|
224496
224927
|
const fixturesFlag = flagStr(args, "fixtures");
|
|
224497
|
-
const defaultPack =
|
|
224498
|
-
const flowFixtures = fixturesFlag !== undefined ? resolveFixtures(fixturesFlag, ".") :
|
|
224928
|
+
const defaultPack = resolve19(testsDir, "fixtures.json");
|
|
224929
|
+
const flowFixtures = fixturesFlag !== undefined ? resolveFixtures(fixturesFlag, ".") : existsSync35(defaultPack) ? resolveFixtures("fixtures.json", testsDir) : undefined;
|
|
224499
224930
|
const flowsOnly = flagBool(args, "flows");
|
|
224500
224931
|
const wantFlows = !flagBool(args, "no-flows") && args._.length === 0;
|
|
224501
224932
|
if (flowsOnly)
|
|
224502
224933
|
cases.length = 0;
|
|
224503
224934
|
let skipped = [];
|
|
224504
224935
|
let entryScreen = null;
|
|
224505
|
-
if (desktop2 && wantFlows &&
|
|
224936
|
+
if (desktop2 && wantFlows && existsSync35(designMetaPath)) {
|
|
224506
224937
|
note("Design-derived flows skipped with --desktop (they tap by name; run `lecodes test` headless for them).");
|
|
224507
224938
|
}
|
|
224508
|
-
if (!desktop2 && (wantFlows || flowsOnly) &&
|
|
224939
|
+
if (!desktop2 && (wantFlows || flowsOnly) && existsSync35(designMetaPath)) {
|
|
224509
224940
|
let meta;
|
|
224510
224941
|
try {
|
|
224511
|
-
meta = JSON.parse(
|
|
224942
|
+
meta = JSON.parse(readFileSync31(designMetaPath, "utf8"));
|
|
224512
224943
|
} catch (e2) {
|
|
224513
224944
|
throw new CliError(`Can't read ${designMetaPath}: ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
224514
224945
|
}
|
|
@@ -224540,16 +224971,16 @@ var test = async (args) => {
|
|
|
224540
224971
|
}, timeoutMs);
|
|
224541
224972
|
killer.unref?.();
|
|
224542
224973
|
const onConsole = flagBool(args, "logs") ? (level, text) => logErr(`${c.dim(`[${level}]`)} ${text}`) : undefined;
|
|
224543
|
-
const artifactsRoot =
|
|
224974
|
+
const artifactsRoot = resolve19(root, flagStr(args, "artifacts") ?? resolve19(testsDir, ".artifacts"));
|
|
224544
224975
|
const results = [];
|
|
224545
224976
|
const runNativeCase = async (kase) => {
|
|
224546
|
-
const caseDir =
|
|
224977
|
+
const caseDir = resolve19(artifactsRoot, slug3(kase.name));
|
|
224547
224978
|
const plan = translateScenario(kase.steps, { outDir: caseDir, headroomFrames: nativeFrames });
|
|
224548
224979
|
for (const st of plan.steps)
|
|
224549
224980
|
if (st.screenshot)
|
|
224550
|
-
|
|
224551
|
-
|
|
224552
|
-
const finalPng =
|
|
224981
|
+
mkdirSync25(dirname18(st.screenshot), { recursive: true });
|
|
224982
|
+
mkdirSync25(caseDir, { recursive: true });
|
|
224983
|
+
const finalPng = resolve19(caseDir, "final.png");
|
|
224553
224984
|
const { output } = await runDesktopRender(native.js, native.resources, {
|
|
224554
224985
|
renderer: desktopRenderer,
|
|
224555
224986
|
width: Number(flagStr(args, "width")) || kase.width || projectViewport?.width || 390,
|
|
@@ -224601,14 +225032,14 @@ var test = async (args) => {
|
|
|
224601
225032
|
onConsole
|
|
224602
225033
|
});
|
|
224603
225034
|
try {
|
|
224604
|
-
const io = fileScenarioIO(
|
|
225035
|
+
const io = fileScenarioIO(resolve19(artifactsRoot, slug3(kase.name)));
|
|
224605
225036
|
const outcome = await renderer.runScenario(session, steps, io);
|
|
224606
225037
|
result = {
|
|
224607
225038
|
name: kase.name,
|
|
224608
225039
|
kind: kase.kind,
|
|
224609
225040
|
ok: outcome.ok,
|
|
224610
225041
|
steps: outcome.steps.map((s2) => ({ desc: s2.desc, ok: s2.ok, error: s2.error })),
|
|
224611
|
-
artifacts: outcome.failureArtifacts.map((p2) =>
|
|
225042
|
+
artifacts: outcome.failureArtifacts.map((p2) => resolve19(artifactsRoot, slug3(kase.name), p2))
|
|
224612
225043
|
};
|
|
224613
225044
|
} finally {
|
|
224614
225045
|
session.dispose();
|
|
@@ -224676,11 +225107,11 @@ init_config();
|
|
|
224676
225107
|
init_manifest();
|
|
224677
225108
|
init_types4();
|
|
224678
225109
|
init_util();
|
|
224679
|
-
import { resolve as
|
|
225110
|
+
import { resolve as resolve20 } from "node:path";
|
|
224680
225111
|
var label = (bundle) => `${Object.keys(bundle).length} file(s)`;
|
|
224681
225112
|
var types4 = async (args) => {
|
|
224682
225113
|
const dirArg = args._[0];
|
|
224683
|
-
const root = dirArg ?
|
|
225114
|
+
const root = dirArg ? resolve20(dirArg) : (() => {
|
|
224684
225115
|
try {
|
|
224685
225116
|
return findProjectRoot(process.cwd());
|
|
224686
225117
|
} catch {
|
|
@@ -224739,23 +225170,23 @@ var types4 = async (args) => {
|
|
|
224739
225170
|
};
|
|
224740
225171
|
|
|
224741
225172
|
// src/index.ts
|
|
224742
|
-
import { existsSync as
|
|
225173
|
+
import { existsSync as existsSync38, readFileSync as readFileSync34, realpathSync as realpathSync2 } from "node:fs";
|
|
224743
225174
|
import { createRequire as createRequire4 } from "node:module";
|
|
224744
|
-
import { delimiter as delimiter2, dirname as
|
|
225175
|
+
import { delimiter as delimiter2, dirname as dirname21, join as join39 } from "node:path";
|
|
224745
225176
|
|
|
224746
225177
|
// src/commands/install.ts
|
|
224747
225178
|
init_util();
|
|
224748
|
-
import { execFileSync as
|
|
224749
|
-
import { appendFileSync as appendFileSync3, chmodSync as chmodSync2, cpSync as
|
|
225179
|
+
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
225180
|
+
import { appendFileSync as appendFileSync3, chmodSync as chmodSync2, cpSync as cpSync5, existsSync as existsSync36, mkdirSync as mkdirSync26, readFileSync as readFileSync32, realpathSync, rmSync as rmSync11, symlinkSync } from "node:fs";
|
|
224750
225181
|
import { homedir as homedir9 } from "node:os";
|
|
224751
|
-
import { basename as
|
|
225182
|
+
import { basename as basename17, delimiter, dirname as dirname19, join as join37, resolve as resolve21, sep as sep2 } from "node:path";
|
|
224752
225183
|
var isWin = process.platform === "win32";
|
|
224753
225184
|
var HOME = homedir9();
|
|
224754
225185
|
var safeRealpath = (p2) => {
|
|
224755
225186
|
try {
|
|
224756
225187
|
return realpathSync(p2);
|
|
224757
225188
|
} catch {
|
|
224758
|
-
return
|
|
225189
|
+
return resolve21(p2);
|
|
224759
225190
|
}
|
|
224760
225191
|
};
|
|
224761
225192
|
var onPath = (dir) => {
|
|
@@ -224772,24 +225203,24 @@ var onPath = (dir) => {
|
|
|
224772
225203
|
};
|
|
224773
225204
|
var install = async (args) => {
|
|
224774
225205
|
const bundle = dirname19(process.execPath);
|
|
224775
|
-
const exeName3 =
|
|
225206
|
+
const exeName3 = basename17(process.execPath);
|
|
224776
225207
|
const chiselName = isWin ? "chisel.exe" : "chisel";
|
|
224777
|
-
if (!
|
|
225208
|
+
if (!existsSync36(join37(bundle, "runtime")) || !existsSync36(join37(bundle, chiselName))) {
|
|
224778
225209
|
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.");
|
|
224779
225210
|
}
|
|
224780
|
-
const companions = [exeName3, chiselName, "runtime", "README.txt"].filter((f3) =>
|
|
225211
|
+
const companions = [exeName3, chiselName, "runtime", "README.txt"].filter((f3) => existsSync36(join37(bundle, f3)));
|
|
224781
225212
|
if (isWin)
|
|
224782
225213
|
return installWindows(args, bundle, companions);
|
|
224783
225214
|
return installUnix(args, bundle, companions);
|
|
224784
225215
|
};
|
|
224785
225216
|
var copyBundle = (bundle, target, companions) => {
|
|
224786
|
-
|
|
225217
|
+
mkdirSync26(target, { recursive: true });
|
|
224787
225218
|
if (safeRealpath(target) === safeRealpath(bundle))
|
|
224788
225219
|
return;
|
|
224789
225220
|
for (const f3 of companions) {
|
|
224790
|
-
const dest =
|
|
224791
|
-
|
|
224792
|
-
|
|
225221
|
+
const dest = join37(target, f3);
|
|
225222
|
+
rmSync11(dest, { recursive: true, force: true });
|
|
225223
|
+
cpSync5(join37(bundle, f3), dest, { recursive: true });
|
|
224793
225224
|
}
|
|
224794
225225
|
};
|
|
224795
225226
|
var permError = (e2, dir, system) => {
|
|
@@ -224801,41 +225232,41 @@ var permError = (e2, dir, system) => {
|
|
|
224801
225232
|
};
|
|
224802
225233
|
var installUnix = (args, bundle, companions) => {
|
|
224803
225234
|
const system = flagBool(args, "system");
|
|
224804
|
-
const libDir =
|
|
224805
|
-
const binDir =
|
|
224806
|
-
const link2 =
|
|
225235
|
+
const libDir = resolve21(flagStr(args, "dir") ?? (system ? "/usr/local/lib/lecodes-cli" : join37(HOME, ".local", "lib", "lecodes-cli")));
|
|
225236
|
+
const binDir = resolve21(flagStr(args, "bin") ?? (system ? "/usr/local/bin" : join37(HOME, ".local", "bin")));
|
|
225237
|
+
const link2 = join37(binDir, "lecodes");
|
|
224807
225238
|
try {
|
|
224808
225239
|
copyBundle(bundle, libDir, companions);
|
|
224809
|
-
chmodSync2(
|
|
224810
|
-
chmodSync2(
|
|
225240
|
+
chmodSync2(join37(libDir, "lecodes"), 493);
|
|
225241
|
+
chmodSync2(join37(libDir, "chisel"), 493);
|
|
224811
225242
|
} catch (e2) {
|
|
224812
225243
|
throw permError(e2, libDir, system);
|
|
224813
225244
|
}
|
|
224814
225245
|
try {
|
|
224815
|
-
|
|
224816
|
-
|
|
224817
|
-
symlinkSync(
|
|
225246
|
+
mkdirSync26(binDir, { recursive: true });
|
|
225247
|
+
rmSync11(link2, { force: true });
|
|
225248
|
+
symlinkSync(join37(libDir, "lecodes"), link2);
|
|
224818
225249
|
} catch (e2) {
|
|
224819
225250
|
throw permError(e2, binDir, system);
|
|
224820
225251
|
}
|
|
224821
225252
|
success(`Installed to ${c.bold(libDir)}`);
|
|
224822
|
-
log(` ${c.dim(link2 + " → " +
|
|
225253
|
+
log(` ${c.dim(link2 + " → " + join37(libDir, "lecodes"))}`);
|
|
224823
225254
|
finishPathUnix(args, binDir);
|
|
224824
225255
|
};
|
|
224825
225256
|
var shellRc = () => {
|
|
224826
|
-
const shell =
|
|
225257
|
+
const shell = basename17(process.env.SHELL ?? "");
|
|
224827
225258
|
if (shell === "zsh")
|
|
224828
|
-
return { file:
|
|
225259
|
+
return { file: join37(HOME, ".zshrc"), name: "~/.zshrc" };
|
|
224829
225260
|
if (shell === "bash")
|
|
224830
|
-
return { file:
|
|
224831
|
-
return { file:
|
|
225261
|
+
return { file: join37(HOME, ".bashrc"), name: "~/.bashrc" };
|
|
225262
|
+
return { file: join37(HOME, ".profile"), name: "~/.profile" };
|
|
224832
225263
|
};
|
|
224833
225264
|
var PATH_MARKER = "# Added by `lecodes install`";
|
|
224834
225265
|
var appendPathToShellRc = (binDir) => {
|
|
224835
225266
|
const { file, name } = shellRc();
|
|
224836
225267
|
const line = `export PATH="${binDir}:$PATH"`;
|
|
224837
225268
|
try {
|
|
224838
|
-
const existing =
|
|
225269
|
+
const existing = existsSync36(file) ? readFileSync32(file, "utf8") : "";
|
|
224839
225270
|
if (existing.includes(line))
|
|
224840
225271
|
return name;
|
|
224841
225272
|
appendFileSync3(file, `${existing && !existing.endsWith(`
|
|
@@ -224873,7 +225304,7 @@ var finishPathUnix = (args, binDir) => {
|
|
|
224873
225304
|
log(` • or install system-wide (no PATH edit): ${c.bold("sudo lecodes install --system")}`);
|
|
224874
225305
|
};
|
|
224875
225306
|
var installWindows = (args, bundle, companions) => {
|
|
224876
|
-
const target =
|
|
225307
|
+
const target = resolve21(flagStr(args, "dir") ?? join37(process.env.LOCALAPPDATA ?? join37(HOME, "AppData", "Local"), "Programs", "lecodes-cli"));
|
|
224877
225308
|
try {
|
|
224878
225309
|
copyBundle(bundle, target, companions);
|
|
224879
225310
|
} catch (e2) {
|
|
@@ -224901,7 +225332,7 @@ var installWindows = (args, bundle, companions) => {
|
|
|
224901
225332
|
var addToUserPathWindows = (dir) => {
|
|
224902
225333
|
const script = "$d = $env:LECODES_INSTALL_DIR;" + "$p = [Environment]::GetEnvironmentVariable('Path','User'); if (-not $p) { $p = '' };" + "$parts = @($p.Split(';') | Where-Object { $_ -ne '' });" + "if ($parts -notcontains $d) { [Environment]::SetEnvironmentVariable('Path', (@($parts + $d) -join ';'), 'User') }";
|
|
224903
225334
|
try {
|
|
224904
|
-
|
|
225335
|
+
execFileSync7("powershell", ["-NoProfile", "-NonInteractive", "-Command", script], { stdio: "ignore", env: { ...process.env, LECODES_INSTALL_DIR: dir } });
|
|
224905
225336
|
return true;
|
|
224906
225337
|
} catch {
|
|
224907
225338
|
return false;
|
|
@@ -224910,10 +225341,85 @@ var addToUserPathWindows = (dir) => {
|
|
|
224910
225341
|
|
|
224911
225342
|
// src/index.ts
|
|
224912
225343
|
init_util();
|
|
225344
|
+
|
|
225345
|
+
// src/projectEnv.ts
|
|
225346
|
+
import { existsSync as existsSync37, readFileSync as readFileSync33 } from "node:fs";
|
|
225347
|
+
import { homedir as homedir10 } from "node:os";
|
|
225348
|
+
import { dirname as dirname20, join as join38, resolve as resolve22 } from "node:path";
|
|
225349
|
+
var KEY_RE = /^(LECODES|CREATOR)_[A-Z0-9_]+$/;
|
|
225350
|
+
var parseDotEnv = (text) => {
|
|
225351
|
+
const out = {};
|
|
225352
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
225353
|
+
const line = raw.trim();
|
|
225354
|
+
if (!line || line.startsWith("#"))
|
|
225355
|
+
continue;
|
|
225356
|
+
const m = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(line);
|
|
225357
|
+
if (!m)
|
|
225358
|
+
continue;
|
|
225359
|
+
const key = m[1];
|
|
225360
|
+
let value = m[2];
|
|
225361
|
+
if (value.startsWith('"')) {
|
|
225362
|
+
const end = value.indexOf('"', 1);
|
|
225363
|
+
value = (end < 0 ? value.slice(1) : value.slice(1, end)).replace(/\\n/g, `
|
|
225364
|
+
`).replace(/\\t/g, "\t").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
225365
|
+
} else if (value.startsWith("'")) {
|
|
225366
|
+
const end = value.indexOf("'", 1);
|
|
225367
|
+
value = end < 0 ? value.slice(1) : value.slice(1, end);
|
|
225368
|
+
} else {
|
|
225369
|
+
const hash = value.search(/\s#/);
|
|
225370
|
+
value = (hash >= 0 ? value.slice(0, hash) : value).trim();
|
|
225371
|
+
}
|
|
225372
|
+
out[key] = value;
|
|
225373
|
+
}
|
|
225374
|
+
return out;
|
|
225375
|
+
};
|
|
225376
|
+
var applyDotEnv = (parsed, dir, env) => {
|
|
225377
|
+
const out = {};
|
|
225378
|
+
for (const [key, raw] of Object.entries(parsed)) {
|
|
225379
|
+
if (!KEY_RE.test(key) || env[key] !== undefined)
|
|
225380
|
+
continue;
|
|
225381
|
+
let value = raw;
|
|
225382
|
+
if (value.startsWith("./") || value.startsWith("../"))
|
|
225383
|
+
value = resolve22(dir, value);
|
|
225384
|
+
else if (value === "~" || value.startsWith("~/"))
|
|
225385
|
+
value = join38(homedir10(), value.slice(1));
|
|
225386
|
+
out[key] = value;
|
|
225387
|
+
}
|
|
225388
|
+
return out;
|
|
225389
|
+
};
|
|
225390
|
+
var findEnvRoot = (start) => {
|
|
225391
|
+
let dir = resolve22(start);
|
|
225392
|
+
while (true) {
|
|
225393
|
+
if (existsSync37(join38(dir, "app.json")) || existsSync37(join38(dir, ".lecodes")))
|
|
225394
|
+
return dir;
|
|
225395
|
+
const parent = dirname20(dir);
|
|
225396
|
+
if (parent === dir)
|
|
225397
|
+
return null;
|
|
225398
|
+
dir = parent;
|
|
225399
|
+
}
|
|
225400
|
+
};
|
|
225401
|
+
var loadProjectEnv = (cwd = process.cwd()) => {
|
|
225402
|
+
try {
|
|
225403
|
+
const root = findEnvRoot(cwd);
|
|
225404
|
+
if (!root)
|
|
225405
|
+
return [];
|
|
225406
|
+
const file = join38(root, ".env");
|
|
225407
|
+
if (!existsSync37(file))
|
|
225408
|
+
return [];
|
|
225409
|
+
const added = applyDotEnv(parseDotEnv(readFileSync33(file, "utf8")), root, process.env);
|
|
225410
|
+
for (const [k, v] of Object.entries(added))
|
|
225411
|
+
process.env[k] = v;
|
|
225412
|
+
return Object.keys(added);
|
|
225413
|
+
} catch {
|
|
225414
|
+
return [];
|
|
225415
|
+
}
|
|
225416
|
+
};
|
|
225417
|
+
|
|
225418
|
+
// src/index.ts
|
|
224913
225419
|
var STANDALONE = (() => {
|
|
224914
|
-
const dir =
|
|
225420
|
+
const dir = dirname21(process.execPath);
|
|
224915
225421
|
const chisel = process.platform === "win32" ? "chisel.exe" : "chisel";
|
|
224916
|
-
return
|
|
225422
|
+
return existsSync38(join39(dir, "runtime")) && existsSync38(join39(dir, chisel));
|
|
224917
225423
|
})();
|
|
224918
225424
|
var INSTALLED = STANDALONE && (() => {
|
|
224919
225425
|
const exe = process.platform === "win32" ? "lecodes.exe" : "lecodes";
|
|
@@ -224928,8 +225434,8 @@ var INSTALLED = STANDALONE && (() => {
|
|
|
224928
225434
|
if (!d)
|
|
224929
225435
|
return false;
|
|
224930
225436
|
try {
|
|
224931
|
-
const cand =
|
|
224932
|
-
return
|
|
225437
|
+
const cand = join39(d, exe);
|
|
225438
|
+
return existsSync38(cand) && realpathSync2(cand) === self2;
|
|
224933
225439
|
} catch {
|
|
224934
225440
|
return false;
|
|
224935
225441
|
}
|
|
@@ -225133,10 +225639,20 @@ ${c.bold("Commands:")}
|
|
|
225133
225639
|
it) + app.ico from app.json "icon". Duplicate asset
|
|
225134
225640
|
basenames are a build error (they silently alias in
|
|
225135
225641
|
the bundle). Also spelled 'lecodes app build desktop'.
|
|
225642
|
+
On macOS: a signed .app instead (the downloaded LeCodes
|
|
225643
|
+
Desktop.app + the project inside it); app.json
|
|
225644
|
+
desktop.macos = bundleId / buildNumber / category /
|
|
225645
|
+
signing.identity / notarize.profile (a notarytool
|
|
225646
|
+
keychain profile — notarized + stapled, so it opens
|
|
225647
|
+
on any Mac without "Open Anyway").
|
|
225136
225648
|
-o <dir> output folder (default dist/<name>)
|
|
225137
225649
|
--slug <name> folder/zip name override (Latin-only)
|
|
225138
225650
|
--renderer <gl|vulkan> host + shader variant
|
|
225139
225651
|
--probe headless-render the staged folder to PNG
|
|
225652
|
+
(macOS: a brief windowed run + snapshot)
|
|
225653
|
+
--zip also pack the result for sending
|
|
225654
|
+
--sign <identity|adhoc> macOS codesign identity
|
|
225655
|
+
--notarize <profile> / --no-notarize macOS
|
|
225140
225656
|
--zip <out>.zip, UTF-8/forward-slash entries
|
|
225141
225657
|
update move to the newest published release
|
|
225142
225658
|
(cached in ~/.lecodes/renderer)
|
|
@@ -225251,7 +225767,10 @@ ${c.bold("Commands:")}
|
|
|
225251
225767
|
pn keys [new|revoke] Push keys for YOUR server: list, mint (owner only, shown once
|
|
225252
225768
|
— 'new --env' banks it in .env), revoke <id>
|
|
225253
225769
|
|
|
225254
|
-
Config lives in ~/.lecodes/config.json (override with LECODES_API / LECODES_TOKEN)
|
|
225770
|
+
Config lives in ~/.lecodes/config.json (override with LECODES_API / LECODES_TOKEN).
|
|
225771
|
+
A .env next to app.json is read at startup for LECODES_* / CREATOR_* keys (the shell's own
|
|
225772
|
+
environment wins; ./ paths resolve against the project; the file is never pushed) — e.g.
|
|
225773
|
+
LECODES_DESKTOP_EXE, LECODES_MATC, LECODES_NOTARY_PROFILE, LECODES_MACOS_SIGN_ID, LECODES_TOKEN.`;
|
|
225255
225774
|
var commands = { init: init2, login, create, clone: clone3, link, status: status3, diff: diff2, pull, push, compile: compile2, preview: previewLegacy, render, test, types: types4, design, scene, dev, app, pn, desktop, shaders, assets, lightmap };
|
|
225256
225775
|
if (STANDALONE)
|
|
225257
225776
|
commands.install = install;
|
|
@@ -225265,15 +225784,15 @@ var OPTIONAL_PEERS = [
|
|
|
225265
225784
|
var peerVersion = (name, probe) => {
|
|
225266
225785
|
try {
|
|
225267
225786
|
const require2 = createRequire4(import.meta.url);
|
|
225268
|
-
let dir =
|
|
225787
|
+
let dir = dirname21(require2.resolve(probe));
|
|
225269
225788
|
for (let depth = 0;depth < 16; depth++) {
|
|
225270
|
-
const manifest =
|
|
225271
|
-
if (
|
|
225272
|
-
const parsed = JSON.parse(
|
|
225789
|
+
const manifest = join39(dir, "package.json");
|
|
225790
|
+
if (existsSync38(manifest)) {
|
|
225791
|
+
const parsed = JSON.parse(readFileSync34(manifest, "utf8"));
|
|
225273
225792
|
if (parsed.name === name)
|
|
225274
225793
|
return parsed.version ?? "unknown";
|
|
225275
225794
|
}
|
|
225276
|
-
const parent =
|
|
225795
|
+
const parent = dirname21(dir);
|
|
225277
225796
|
if (parent === dir)
|
|
225278
225797
|
break;
|
|
225279
225798
|
dir = parent;
|
|
@@ -225298,6 +225817,7 @@ var printVersion = () => {
|
|
|
225298
225817
|
var main = async () => {
|
|
225299
225818
|
const argv = process.argv.slice(2);
|
|
225300
225819
|
const command = argv[0];
|
|
225820
|
+
loadProjectEnv();
|
|
225301
225821
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
225302
225822
|
log(HELP);
|
|
225303
225823
|
return;
|