lecodes-cli 0.20.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/README.md +56 -57
  2. package/dist/index.js +11052 -7592
  3. package/package.json +14 -8
  4. package/runtime/materials/decal-relief.mat +12 -1
  5. package/runtime/materials/decal.mat +17 -3
  6. package/runtime/materials/lightmap.mat +117 -138
  7. package/runtime/materials/particles-quad.mat +6 -2
  8. package/runtime/materials/terrain-lightmap.mat +123 -0
  9. package/runtime/materials/terrain.mat +36 -55
  10. package/runtime/sdk-types.json +1 -1
  11. package/runtime/web/VERSION.json +5 -0
  12. package/runtime/web/assets/camera-5XOM8eHl.js +1 -0
  13. package/runtime/web/assets/creator-2d-D9WKYM6h.js +1 -0
  14. package/runtime/web/assets/creator-2d-ukvEmNEp.wasm +0 -0
  15. package/runtime/web/assets/creator-full-BBXzQlhq.js +1 -0
  16. package/runtime/web/assets/creator-full-D2dSDdDj.wasm +0 -0
  17. package/runtime/web/assets/creator-full.js-BYswGsqR.symbols +8124 -0
  18. package/runtime/web/assets/creator-ui-DIc92ion.wasm +0 -0
  19. package/runtime/web/assets/creator-ui-_1Z_AX2M.js +1 -0
  20. package/runtime/web/assets/geolocation-BjTKN9_P.js +1 -0
  21. package/runtime/web/assets/inspect-C7x78PNs.js +2 -0
  22. package/runtime/web/assets/map-CyBxOSV0.js +1 -0
  23. package/runtime/web/assets/map-XzoOFt0E.js +2 -0
  24. package/runtime/web/assets/mapImpl-bxvQ2h1Z.js +823 -0
  25. package/runtime/web/assets/maplibre-gl-worker-B7LXXUkR.js +8 -0
  26. package/runtime/web/assets/materials-uri4FvPE.bin +0 -0
  27. package/runtime/web/assets/materials-world-CQWt8TAm.bin +0 -0
  28. package/runtime/web/assets/qr-scanner-CK_cYoJR.js +1 -0
  29. package/runtime/web/assets/uberarchive-DLH-kBhM.bin +0 -0
  30. package/runtime/web/index.html +36 -0
  31. package/runtime/web/player.js +21 -0
  32. package/runtime/web-page/index.html +41 -0
  33. package/runtime/web-page/page.css +1 -0
  34. package/runtime/web-page/page.js +31 -0
  35. package/src/api.ts +1 -302
  36. package/src/cli/args.ts +37 -0
  37. package/src/cli/command.ts +113 -0
  38. package/src/cli/errors.ts +13 -0
  39. package/src/cli/help.ts +158 -0
  40. package/src/cli/index.ts +7 -0
  41. package/src/cli/output.ts +102 -0
  42. package/src/cli/run.ts +106 -0
  43. package/src/commands/{appAndroid.ts → app/android.ts} +454 -43
  44. package/src/commands/{appDesktop.ts → app/desktop.ts} +64 -33
  45. package/src/commands/{appDesktopMac.ts → app/desktopMac.ts} +34 -27
  46. package/src/commands/{appIcon.ts → app/icon.ts} +11 -8
  47. package/src/commands/{app.ts → app/index.ts} +400 -290
  48. package/src/commands/{appShared.ts → app/shared.ts} +43 -44
  49. package/src/commands/{appTemplatesAndroid.ts → app/templates/android.ts} +72 -39
  50. package/src/commands/{appTemplatesGradlew.ts → app/templates/gradlew.ts} +1 -1
  51. package/src/commands/{appTemplates.ts → app/templates/ios.ts} +2 -2
  52. package/src/commands/assets.ts +23 -21
  53. package/src/commands/clone.ts +48 -51
  54. package/src/commands/compile.ts +76 -89
  55. package/src/commands/create.ts +46 -47
  56. package/src/commands/design/comments.ts +361 -0
  57. package/src/commands/design/context.ts +101 -0
  58. package/src/commands/design/index.ts +391 -0
  59. package/src/commands/design/snapshot.ts +134 -0
  60. package/src/commands/{designTemplates.ts → design/templates.ts} +4 -3
  61. package/src/commands/desktop.ts +94 -82
  62. package/src/commands/dev.ts +260 -182
  63. package/src/commands/diff.ts +39 -51
  64. package/src/commands/index.ts +64 -0
  65. package/src/commands/{init.ts → init/index.ts} +195 -191
  66. package/src/commands/{projectTemplates.ts → init/templates.ts} +64 -21
  67. package/src/commands/install.ts +80 -67
  68. package/src/commands/lightmap.ts +543 -252
  69. package/src/commands/link.ts +87 -94
  70. package/src/commands/login.ts +25 -23
  71. package/src/commands/navmesh.ts +169 -139
  72. package/src/commands/plugin.ts +46 -0
  73. package/src/commands/pn.ts +201 -244
  74. package/src/commands/pull.ts +49 -56
  75. package/src/commands/push.ts +48 -54
  76. package/src/commands/render.ts +176 -111
  77. package/src/commands/scene.ts +63 -55
  78. package/src/commands/serve.ts +58 -0
  79. package/src/commands/{shaders.ts → shaders/index.ts} +61 -42
  80. package/src/commands/{shadersNew.ts → shaders/new.ts} +3 -3
  81. package/src/commands/shared.ts +79 -0
  82. package/src/commands/status.ts +22 -26
  83. package/src/commands/test.ts +94 -63
  84. package/src/commands/thumbs.ts +185 -175
  85. package/src/commands/update/index.ts +226 -0
  86. package/src/commands/{types.ts → update/types.ts} +31 -22
  87. package/src/compile/collect.ts +5 -5
  88. package/src/compile/collectLocal.ts +3 -3
  89. package/src/compile/designCompile.ts +4 -4
  90. package/src/compile/headlessBundle.ts +16 -12
  91. package/src/compile/nativeStack.ts +1 -1
  92. package/src/compile/projectCompile.ts +12 -4
  93. package/src/compile/sceneCompile.ts +14 -26
  94. package/src/compile/screenEntry.ts +123 -127
  95. package/src/compile/shaders.ts +3 -3
  96. package/src/{lecodes-3d-editor.d.ts → declarations/lecodes-3d-editor.d.ts} +4 -0
  97. package/src/{lecodes-assets.d.ts → declarations/lecodes-assets.d.ts} +1 -1
  98. package/src/{lecodes-renderer.d.ts → declarations/lecodes-headless.d.ts} +3 -3
  99. package/src/dev/androidDev.ts +1 -1
  100. package/src/dev/clientTemplates.ts +18 -14
  101. package/src/dev/devServer.ts +78 -41
  102. package/src/dev/iosDev.ts +180 -0
  103. package/src/dev/webRunner.ts +83 -15
  104. package/src/{cmgenTool.ts → hosts/cmgenTool.ts} +2 -2
  105. package/src/{desktopRenderer.ts → hosts/desktopRenderer.ts} +104 -32
  106. package/src/{desktopScript.ts → hosts/desktopScript.ts} +151 -36
  107. package/src/{distRoot.ts → hosts/distRoot.ts} +9 -5
  108. package/src/{matcTool.ts → hosts/matcTool.ts} +2 -2
  109. package/src/{peerInstall.ts → hosts/peerInstall.ts} +7 -7
  110. package/src/{peers.ts → hosts/peers.ts} +2 -2
  111. package/src/{releases.ts → hosts/releases.ts} +1 -1
  112. package/src/index.ts +35 -480
  113. package/src/platform/api.ts +302 -0
  114. package/src/{browserAuth.ts → platform/browserAuth.ts} +1 -1
  115. package/src/{config.ts → platform/config.ts} +1 -1
  116. package/src/{serverDiff.ts → platform/serverDiff.ts} +3 -3
  117. package/src/plugin/contract.ts +438 -0
  118. package/src/plugin/emitKotlin.ts +357 -0
  119. package/src/plugin/emitSdk.ts +259 -0
  120. package/src/plugin/emitSwift.ts +336 -0
  121. package/src/plugin/emitWeb.ts +383 -0
  122. package/src/plugin/generate.ts +157 -0
  123. package/src/plugin/lines.ts +28 -0
  124. package/src/plugin/lower.ts +231 -0
  125. package/src/plugin/manifest.ts +192 -0
  126. package/src/{projectEnv.ts → project/env.ts} +33 -2
  127. package/src/{ignore.ts → project/ignore.ts} +164 -163
  128. package/src/{localFiles.ts → project/localFiles.ts} +1 -1
  129. package/src/{manifest.ts → project/manifest.ts} +1 -1
  130. package/src/{project.ts → project/materialize.ts} +2 -2
  131. package/src/project/paths.ts +20 -0
  132. package/src/{textDiff.ts → project/textDiff.ts} +1 -1
  133. package/src/{types.ts → project/types.ts} +0 -0
  134. package/runtime/materials/lightmap-baked-lite.mat +0 -175
  135. package/runtime/materials/lightmap-baked.mat +0 -176
  136. package/runtime/scene-harness.json +0 -1
  137. package/runtime/web/assets/__vite-browser-external-BIHI7g3E.js +0 -1
  138. package/runtime/web/assets/basis-C64VHDVD.js +0 -1
  139. package/runtime/web/assets/basis_transcoder-VXdx5NbI.wasm +0 -0
  140. package/runtime/web/assets/createViewerLite-Ct_PZdof.js +0 -852
  141. package/runtime/web/assets/draco-BiISTFcR.js +0 -118
  142. package/runtime/web/assets/draco_decoder-DsQ12WqX.wasm +0 -0
  143. package/runtime/web/assets/index-BMt7AnC5.js +0 -2
  144. package/runtime/web/assets/mapViewImpl-B2JcES8l.js +0 -810
  145. package/runtime/web/assets/maplibre-gl-worker-CJfwIrte.js +0 -8
  146. package/runtime/web/assets/worker-Caf-yYEI.js +0 -2
  147. package/runtime/web/embed.html +0 -32
  148. package/runtime/web/embed.js +0 -171
  149. package/src/commands/design.ts +0 -846
  150. package/src/commands/update.ts +0 -211
  151. package/src/util.ts +0 -146
  152. /package/src/{lecodes-design.d.ts → declarations/lecodes-design.d.ts} +0 -0
  153. /package/src/{qrcode-terminal.d.ts → declarations/qrcode-terminal.d.ts} +0 -0
  154. /package/src/{designMeta.ts → project/designMeta.ts} +0 -0
@@ -0,0 +1,102 @@
1
+ import { createInterface } from "node:readline"
2
+
3
+ /*
4
+ * Everything a command prints goes through here. Two channels, one rule:
5
+ * stdout the command's RESULT — what a pipe or `-o` would capture (JSON, JS, a listing)
6
+ * stderr diagnostics: progress, notes, warnings — never in the way of piped output
7
+ * `log` / `success` / `warn` are stdout (for commands whose output IS the conversation);
8
+ * `note` / `warnErr` / `logErr` are the stderr twins for commands that write a result to stdout.
9
+ */
10
+
11
+ const useColor = process.stdout.isTTY && process.env.NO_COLOR === undefined
12
+ const paint = (code: string, s: string): string => useColor ? `\x1b[${code}m${s}\x1b[0m` : s
13
+
14
+ /** ANSI colours (no-ops when not a TTY or NO_COLOR is set). */
15
+ export const c = {
16
+ bold: (s: string) => paint("1", s),
17
+ dim: (s: string) => paint("2", s),
18
+ red: (s: string) => paint("31", s),
19
+ green: (s: string) => paint("32", s),
20
+ yellow: (s: string) => paint("33", s),
21
+ cyan: (s: string) => paint("36", s),
22
+ }
23
+
24
+ export const log = (msg = ""): void => { process.stdout.write(msg + "\n") }
25
+ export const info = (msg: string): void => log(msg)
26
+ export const success = (msg: string): void => log(c.green("✓ ") + msg)
27
+ export const warn = (msg: string): void => log(c.yellow("! ") + msg)
28
+
29
+ export const logErr = (msg = ""): void => { process.stderr.write(msg + "\n") }
30
+ export const note = (msg: string): void => logErr(c.dim(msg))
31
+ export const warnErr = (msg: string): void => logErr(c.yellow("! ") + msg)
32
+
33
+ const ANSI = /\x1b\[[0-9;]*m/g
34
+ /** Printed width of a string — colour codes don't count. */
35
+ export const printedWidth = (s: string): number => s.replace(ANSI, "").length
36
+
37
+ /** Rows as aligned columns (the last column unpadded so colours don't drag trailing spaces). */
38
+ export const table = (rows: string[][], indent = " "): void => {
39
+ if (rows.length === 0) return
40
+ const widths = rows[0].map((_, i) => Math.max(...rows.map(r => printedWidth(r[i] ?? ""))))
41
+ for (const row of rows) {
42
+ const cells = row.map((cell, i) => i === row.length - 1 ? cell : cell + " ".repeat(Math.max(0, widths[i] - printedWidth(cell))))
43
+ log(indent + cells.join(" "))
44
+ }
45
+ }
46
+
47
+ /** A compact "3h ago" — absolute timestamps make tables unreadable at a glance. */
48
+ export const relativeTime = (iso: string | null | undefined): string => {
49
+ if (!iso) return c.dim("never")
50
+ const ms = Date.now() - new Date(iso).getTime()
51
+ if (!Number.isFinite(ms)) return c.dim("—")
52
+ const mins = Math.round(ms / 60_000)
53
+ if (mins < 1) return "just now"
54
+ if (mins < 60) return `${mins}m ago`
55
+ const hours = Math.round(mins / 60)
56
+ if (hours < 24) return `${hours}h ago`
57
+ return `${Math.round(hours / 24)}d ago`
58
+ }
59
+
60
+ /** Keep a label short, dropping characters from the FRONT (paths matter most at the end). */
61
+ const ellipsize = (s: string, max: number): string => s.length <= max ? s : "…" + s.slice(s.length - max + 1)
62
+
63
+ /** A single, in-place-updating progress line (TTY only; a no-op when piped). */
64
+ export const progress = (done: number, total: number, label = ""): void => {
65
+ if (!process.stdout.isTTY) return
66
+ const pct = total > 0 ? Math.round((done / total) * 100) : 100
67
+ process.stdout.clearLine(0)
68
+ process.stdout.cursorTo(0)
69
+ process.stdout.write(` ${c.cyan(`${pct}`.padStart(3) + "%")} ${c.dim(`(${done}/${total})`)} ${c.dim(ellipsize(label, 48))}`)
70
+ }
71
+
72
+ /** Finish a progress line with a newline so later output starts cleanly. */
73
+ export const endProgress = (): void => { if (process.stdout.isTTY) process.stdout.write("\n") }
74
+
75
+ export const prompt = (question: string, fallback?: string): Promise<string> =>
76
+ new Promise(resolve => {
77
+ const rl = createInterface({ input: process.stdin, output: process.stdout })
78
+ const suffix = fallback ? c.dim(` (${fallback})`) : ""
79
+ rl.question(`${question}${suffix}: `, answer => {
80
+ rl.close()
81
+ resolve(answer.trim() || fallback || "")
82
+ })
83
+ })
84
+
85
+ /** Prompt without echoing the input (passwords, token paste). */
86
+ export const promptHidden = (question: string): Promise<string> =>
87
+ new Promise(resolve => {
88
+ const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: true })
89
+ rl.question(`${question}: `, answer => {
90
+ rl.close()
91
+ process.stdout.write("\n")
92
+ resolve(answer.trim())
93
+ })
94
+ // Mute everything written after the prompt line was emitted (the keystroke echoes).
95
+ ;(rl as unknown as { _writeToOutput: (s: string) => void })._writeToOutput = () => {}
96
+ })
97
+
98
+ export const confirm = async (question: string, fallback = false): Promise<boolean> => {
99
+ const answer = (await prompt(`${question} ${fallback ? "[Y/n]" : "[y/N]"}`)).toLowerCase()
100
+ if (!answer) return fallback
101
+ return answer === "y" || answer === "yes"
102
+ }
package/src/cli/run.ts ADDED
@@ -0,0 +1,106 @@
1
+ import { parseArgv } from "./args"
2
+ import { readFlags, unknownFlags, type Command, type Context } from "./command"
3
+ import { CliError, UsageError } from "./errors"
4
+ import { commandHelp, globalHelp, type Group } from "./help"
5
+ import { c, log, logErr, warnErr } from "./output"
6
+
7
+ /*
8
+ * The runner: argv → the command node → typed flags → run. Global switches are handled here for
9
+ * every node: --help / -h / `help <cmd…>`, --version / -v, --yes / -y (pre-approves first-use
10
+ * installs of optional peers, see hosts/peerInstall.ts).
11
+ */
12
+
13
+ export type Cli = {
14
+ version: string
15
+ groups: Group[]
16
+ /** Printed under the global help. */
17
+ footer?: string
18
+ /** `lecodes --version` — the caller prints whatever it wants there. */
19
+ printVersion: () => void
20
+ /** Runs before the command (the project's .env, for instance). */
21
+ before?: () => void
22
+ }
23
+
24
+ const all = (groups: Group[]): Command<any>[] => groups.flatMap(g => g.commands)
25
+
26
+ /** Levenshtein-closest command name for the "did you mean" line. */
27
+ const closest = (name: string, cmds: Command<any>[]): string | undefined => {
28
+ const dist = (a: string, b: string): number => {
29
+ const d = Array.from({ length: a.length + 1 }, (_, i) => [i, ...Array(b.length).fill(0)] as number[])
30
+ for (let j = 1; j <= b.length; j++) d[0][j] = j
31
+ for (let i = 1; i <= a.length; i++) for (let j = 1; j <= b.length; j++)
32
+ d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1))
33
+ return d[a.length][b.length]
34
+ }
35
+ const best = cmds.filter(x => !x.hidden).map(x => [x.name, dist(name, x.name)] as const).sort((p, q) => p[1] - q[1])[0]
36
+ return best && best[1] <= Math.max(2, Math.floor(name.length / 3)) ? best[0] : undefined
37
+ }
38
+
39
+ const find = (cmds: Command<any>[] | undefined, name: string): Command<any> | undefined =>
40
+ cmds?.find(x => x.name === name || x.aliases?.includes(name))
41
+
42
+ const HELP_WORDS = new Set(["--help", "-h"])
43
+
44
+ export const runCli = async (cli: Cli, argv: string[]): Promise<void> => {
45
+ cli.before?.()
46
+ if (argv.includes("--yes") || argv.includes("-y")) process.env.LECODES_YES = "1"
47
+
48
+ const top = all(cli.groups)
49
+ const help = () => globalHelp(cli.version, cli.groups, cli.footer)
50
+ let words = argv
51
+ // `lecodes help …` = `lecodes … --help`
52
+ if (words[0] === "help") words = [...words.slice(1), "--help"]
53
+ if (words.length === 0 || (words.length === 1 && HELP_WORDS.has(words[0]))) { log(help()); return }
54
+ if (words[0] === "--version" || words[0] === "-v" || words[0] === "version") { cli.printVersion(); return }
55
+
56
+ // walk the tree: every leading word that names a subcommand descends
57
+ const path: string[] = []
58
+ let node = find(top, words[0])
59
+ if (!node) {
60
+ const hint = closest(words[0], top)
61
+ logErr(c.red(`Unknown command: ${words[0]}`) + (hint ? ` — did you mean ${c.bold(hint)}?` : ""))
62
+ logErr(c.dim("Run `lecodes --help` for the list."))
63
+ process.exitCode = 1
64
+ return
65
+ }
66
+ path.push(node.name)
67
+ let rest = words.slice(1)
68
+ while (rest.length && node.commands) {
69
+ const sub = find(node.commands, rest[0])
70
+ if (!sub) break
71
+ node = sub
72
+ path.push(sub.name)
73
+ rest = rest.slice(1)
74
+ }
75
+
76
+ if (rest.some(w => HELP_WORDS.has(w)) && !node.passthrough) { log(commandHelp(path, node)); return }
77
+ if (!node.run) {
78
+ // a group called bare (or with a word that names no subcommand): its help is the answer
79
+ if (rest.length) logErr(c.red(`Unknown command: ${[...path, rest[0]].join(" ")}`))
80
+ log(commandHelp(path, node))
81
+ if (rest.length) process.exitCode = 1
82
+ return
83
+ }
84
+
85
+ const parsed = parseArgv(rest)
86
+ const spec = node.flags ?? {}
87
+ if (!node.passthrough) {
88
+ const unknown = unknownFlags(spec, parsed.flags)
89
+ if (unknown.length) warnErr(`${unknown.map(f => `--${f}`).join(", ")}: not an option of \`lecodes ${path.join(" ")}\` (see --help)`)
90
+ }
91
+ const ctx: Context<any> = { args: parsed.positional, flags: readFlags(spec, parsed.flags, path), raw: parsed.flags, argv: rest, path }
92
+ try {
93
+ await node.run(ctx)
94
+ } catch (e) {
95
+ if (e instanceof UsageError) { logErr(c.red("✗ ") + e.message); logErr(""); logErr(commandHelp(path, node)) }
96
+ else throw e
97
+ process.exitCode = 1
98
+ }
99
+ }
100
+
101
+ /** Top-level error reporting: a CliError is a message, anything else is unexpected. */
102
+ export const reportError = (e: unknown): void => {
103
+ if (e instanceof CliError) logErr(c.red("✗ ") + e.message)
104
+ else logErr(c.red("✗ Unexpected error: ") + (e instanceof Error ? (e.stack ?? e.message) : String(e)))
105
+ process.exitCode = 1
106
+ }