lecodes-cli 0.19.0 → 0.19.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.
Files changed (254) hide show
  1. package/dist/index.js +730 -235
  2. package/package.json +14 -13
  3. package/runtime/scene-harness.json +1 -1
  4. package/runtime/sdk-types.json +1 -1
  5. package/src/api.ts +302 -0
  6. package/src/browserAuth.ts +87 -0
  7. package/src/cmgenTool.ts +104 -0
  8. package/src/commands/app.ts +891 -0
  9. package/src/commands/appAndroid.ts +603 -0
  10. package/src/commands/appDesktop.ts +326 -0
  11. package/src/commands/appDesktopMac.ts +470 -0
  12. package/src/commands/appIcon.ts +187 -0
  13. package/src/commands/appShared.ts +448 -0
  14. package/src/commands/appTemplates.ts +883 -0
  15. package/src/commands/appTemplatesAndroid.ts +599 -0
  16. package/src/commands/appTemplatesGradlew.ts +9 -0
  17. package/src/commands/assets.ts +28 -0
  18. package/src/commands/clone.ts +60 -0
  19. package/src/commands/compile.ts +253 -0
  20. package/src/commands/create.ts +59 -0
  21. package/src/commands/design.ts +863 -0
  22. package/src/commands/designTemplates.ts +11 -0
  23. package/src/commands/desktop.ts +122 -0
  24. package/src/commands/dev.ts +214 -0
  25. package/src/commands/diff.ts +59 -0
  26. package/src/commands/init.ts +191 -0
  27. package/src/commands/install.ts +198 -0
  28. package/src/commands/lightmap.ts +290 -0
  29. package/src/commands/link.ts +147 -0
  30. package/src/commands/login.ts +24 -0
  31. package/src/commands/navmesh.ts +225 -0
  32. package/src/commands/pn.ts +305 -0
  33. package/src/commands/projectTemplates.ts +129 -0
  34. package/src/commands/pull.ts +109 -0
  35. package/src/commands/push.ts +173 -0
  36. package/src/commands/render.ts +414 -0
  37. package/src/commands/scene.ts +148 -0
  38. package/src/commands/shaders.ts +157 -0
  39. package/src/commands/status.ts +29 -0
  40. package/src/commands/test.ts +370 -0
  41. package/src/commands/thumbs.ts +178 -0
  42. package/src/commands/types.ts +87 -0
  43. package/src/commands/update.ts +190 -0
  44. package/src/compile/assetIcons.ts +214 -0
  45. package/src/compile/collect.ts +46 -0
  46. package/src/compile/collectLocal.ts +37 -0
  47. package/src/compile/designCompile.ts +104 -0
  48. package/src/compile/fonts.ts +156 -0
  49. package/src/compile/headlessBundle.ts +129 -0
  50. package/src/compile/nativeStack.ts +21 -0
  51. package/src/compile/projectCompile.ts +109 -0
  52. package/src/compile/sceneCompile.ts +113 -0
  53. package/src/compile/screenEntry.ts +127 -0
  54. package/src/compile/shaders.ts +245 -0
  55. package/src/config.ts +42 -0
  56. package/src/designMeta.ts +35 -0
  57. package/src/desktopRenderer.ts +532 -0
  58. package/src/desktopScript.ts +276 -0
  59. package/src/dev/clientTemplates.ts +160 -0
  60. package/src/dev/devServer.ts +292 -0
  61. package/src/dev/wsServer.ts +144 -0
  62. package/src/distRoot.ts +20 -0
  63. package/src/ignore.ts +163 -0
  64. package/src/index.ts +491 -0
  65. package/src/lecodes-3d-editor.d.ts +41 -0
  66. package/src/lecodes-assets.d.ts +7 -0
  67. package/src/lecodes-design.d.ts +191 -0
  68. package/src/lecodes-renderer.d.ts +131 -0
  69. package/src/localFiles.ts +144 -0
  70. package/src/manifest.ts +46 -0
  71. package/src/matcTool.ts +137 -0
  72. package/src/peers.ts +40 -0
  73. package/src/project.ts +61 -0
  74. package/src/projectEnv.ts +94 -0
  75. package/src/qrcode-terminal.d.ts +9 -0
  76. package/src/releases.ts +125 -0
  77. package/src/serverDiff.ts +78 -0
  78. package/src/textDiff.ts +103 -0
  79. package/src/types.ts +0 -0
  80. package/src/util.ts +146 -0
  81. package/runtime/sdk/animate/animate.ts +0 -238
  82. package/runtime/sdk/animate/bezier.ts +0 -138
  83. package/runtime/sdk/animate/easings.ts +0 -126
  84. package/runtime/sdk/canvas/Canvas.ts +0 -305
  85. package/runtime/sdk/compile/aspectMacro.ts +0 -86
  86. package/runtime/sdk/compile/assetIconMacro.ts +0 -384
  87. package/runtime/sdk/compile/assetMacro.ts +0 -146
  88. package/runtime/sdk/compile/assetName.ts +0 -50
  89. package/runtime/sdk/compile/bundler.ts +0 -287
  90. package/runtime/sdk/compile/compileProject.ts +0 -129
  91. package/runtime/sdk/compile/detectEntry.ts +0 -128
  92. package/runtime/sdk/compile/fontMacro.ts +0 -459
  93. package/runtime/sdk/compile/fontRegistry.ts +0 -78
  94. package/runtime/sdk/compile/header.ts +0 -67
  95. package/runtime/sdk/compile/index.ts +0 -100
  96. package/runtime/sdk/compile/libraryImports.ts +0 -52
  97. package/runtime/sdk/compile/liteMaterial.ts +0 -247
  98. package/runtime/sdk/compile/sceneEditor.ts +0 -88
  99. package/runtime/sdk/compile/serverSplit.ts +0 -233
  100. package/runtime/sdk/compile/serverTypes.ts +0 -227
  101. package/runtime/sdk/compile/sfnt.ts +0 -98
  102. package/runtime/sdk/compile/shaderSchema.ts +0 -202
  103. package/runtime/sdk/compile/shaderTargets.ts +0 -81
  104. package/runtime/sdk/compile/sourcemap.ts +0 -25
  105. package/runtime/sdk/core/Aspect.ts +0 -568
  106. package/runtime/sdk/core/InspectorUI.ts +0 -212
  107. package/runtime/sdk/core/StateMachine.ts +0 -308
  108. package/runtime/sdk/core/color.ts +0 -66
  109. package/runtime/sdk/core/compWrite.ts +0 -42
  110. package/runtime/sdk/core/events.ts +0 -38
  111. package/runtime/sdk/core/fields.ts +0 -120
  112. package/runtime/sdk/core/registry.ts +0 -23
  113. package/runtime/sdk/core/signals.ts +0 -277
  114. package/runtime/sdk/core/time.ts +0 -84
  115. package/runtime/sdk/g2/Camera2D.ts +0 -40
  116. package/runtime/sdk/g2/CharacterController2D.ts +0 -276
  117. package/runtime/sdk/g2/Node2D.ts +0 -267
  118. package/runtime/sdk/g2/OneWay2D.ts +0 -66
  119. package/runtime/sdk/g2/Physics2D.ts +0 -346
  120. package/runtime/sdk/g2/Scene2D.ts +0 -209
  121. package/runtime/sdk/g2/Shape2D.ts +0 -259
  122. package/runtime/sdk/g2/Sprite.ts +0 -89
  123. package/runtime/sdk/g2/SpriteAnimation.ts +0 -171
  124. package/runtime/sdk/g2/SpriteSheet.ts +0 -166
  125. package/runtime/sdk/g2/Texture2D.ts +0 -47
  126. package/runtime/sdk/g2/Tilemap.ts +0 -41
  127. package/runtime/sdk/g2/Tileset.ts +0 -71
  128. package/runtime/sdk/g2/Trigger2D.ts +0 -77
  129. package/runtime/sdk/g2/autotile.ts +0 -433
  130. package/runtime/sdk/g2/cells.ts +0 -91
  131. package/runtime/sdk/g2/defineScene2d.ts +0 -381
  132. package/runtime/sdk/g2/groups2d.ts +0 -106
  133. package/runtime/sdk/g2/loop.ts +0 -50
  134. package/runtime/sdk/g2/scenarios2d.ts +0 -69
  135. package/runtime/sdk/g2/touch.ts +0 -83
  136. package/runtime/sdk/gl/Camera.ts +0 -160
  137. package/runtime/sdk/gl/CameraPlace.ts +0 -52
  138. package/runtime/sdk/gl/CharacterController.ts +0 -238
  139. package/runtime/sdk/gl/Gearbox.ts +0 -212
  140. package/runtime/sdk/gl/Geometry.ts +0 -279
  141. package/runtime/sdk/gl/IK.ts +0 -193
  142. package/runtime/sdk/gl/InstancedMesh.ts +0 -132
  143. package/runtime/sdk/gl/Light.ts +0 -107
  144. package/runtime/sdk/gl/Lightmap.ts +0 -256
  145. package/runtime/sdk/gl/Material.ts +0 -260
  146. package/runtime/sdk/gl/Mesh.ts +0 -83
  147. package/runtime/sdk/gl/Model.ts +0 -124
  148. package/runtime/sdk/gl/NavAgent.ts +0 -337
  149. package/runtime/sdk/gl/NavMesh.ts +0 -397
  150. package/runtime/sdk/gl/Node.ts +0 -350
  151. package/runtime/sdk/gl/Noise.ts +0 -30
  152. package/runtime/sdk/gl/Particles.ts +0 -676
  153. package/runtime/sdk/gl/Physics.ts +0 -222
  154. package/runtime/sdk/gl/Plane.ts +0 -53
  155. package/runtime/sdk/gl/Ragdoll.ts +0 -270
  156. package/runtime/sdk/gl/Ray.ts +0 -16
  157. package/runtime/sdk/gl/Scene.ts +0 -486
  158. package/runtime/sdk/gl/Shape.ts +0 -409
  159. package/runtime/sdk/gl/Terrain.ts +0 -1092
  160. package/runtime/sdk/gl/Texture.ts +0 -63
  161. package/runtime/sdk/gl/Trigger.ts +0 -45
  162. package/runtime/sdk/gl/Vehicle.ts +0 -473
  163. package/runtime/sdk/gl/Wheel.ts +0 -240
  164. package/runtime/sdk/gl/animation/AnimationClip.ts +0 -204
  165. package/runtime/sdk/gl/animation/Animator.ts +0 -120
  166. package/runtime/sdk/gl/animation/Layer.ts +0 -31
  167. package/runtime/sdk/gl/animation/Loop.ts +0 -30
  168. package/runtime/sdk/gl/animation/Playback.ts +0 -44
  169. package/runtime/sdk/gl/animation/core.ts +0 -336
  170. package/runtime/sdk/gl/controls.ts +0 -95
  171. package/runtime/sdk/gl/physicsEvents.ts +0 -20
  172. package/runtime/sdk/gl/scenarios.ts +0 -291
  173. package/runtime/sdk/gl/state.ts +0 -6
  174. package/runtime/sdk/gl/terrainMesh.ts +0 -219
  175. package/runtime/sdk/gl/touch.ts +0 -68
  176. package/runtime/sdk/inject.ts +0 -197
  177. package/runtime/sdk/math/Mathf.ts +0 -118
  178. package/runtime/sdk/math/mat4.ts +0 -278
  179. package/runtime/sdk/math/quat.ts +0 -232
  180. package/runtime/sdk/math/vec.ts +0 -255
  181. package/runtime/sdk/net/codec.ts +0 -119
  182. package/runtime/sdk/net/core.ts +0 -384
  183. package/runtime/sdk/net/index.ts +0 -181
  184. package/runtime/sdk/net/replication.ts +0 -622
  185. package/runtime/sdk/plugins/camera.ts +0 -81
  186. package/runtime/sdk/plugins/geolocation.ts +0 -123
  187. package/runtime/sdk/plugins/oauth.ts +0 -61
  188. package/runtime/sdk/plugins/permission.ts +0 -7
  189. package/runtime/sdk/plugins/push.ts +0 -132
  190. package/runtime/sdk/plugins/qr.ts +0 -73
  191. package/runtime/sdk/plugins/service.ts +0 -47
  192. package/runtime/sdk/runtime/app.ts +0 -101
  193. package/runtime/sdk/runtime/appEvents.ts +0 -54
  194. package/runtime/sdk/runtime/channel.ts +0 -61
  195. package/runtime/sdk/runtime/clipboard.ts +0 -20
  196. package/runtime/sdk/runtime/datetime.ts +0 -329
  197. package/runtime/sdk/runtime/device.ts +0 -293
  198. package/runtime/sdk/runtime/fetch.ts +0 -77
  199. package/runtime/sdk/runtime/files.ts +0 -23
  200. package/runtime/sdk/runtime/input.ts +0 -175
  201. package/runtime/sdk/runtime/media.ts +0 -111
  202. package/runtime/sdk/runtime/misc.ts +0 -16
  203. package/runtime/sdk/runtime/net.ts +0 -36
  204. package/runtime/sdk/runtime/rpc.ts +0 -218
  205. package/runtime/sdk/runtime/service.ts +0 -83
  206. package/runtime/sdk/runtime/share.ts +0 -9
  207. package/runtime/sdk/runtime/storage.ts +0 -13
  208. package/runtime/sdk/runtime/touch.ts +0 -76
  209. package/runtime/sdk/scene/defineScene.ts +0 -1353
  210. package/runtime/sdk/scene/editorPlugins.ts +0 -110
  211. package/runtime/sdk/scene/gizmos.ts +0 -148
  212. package/runtime/sdk/scene/grammar.ts +0 -120
  213. package/runtime/sdk/scene/material.ts +0 -188
  214. package/runtime/sdk/server/auth/appConfig.ts +0 -12
  215. package/runtime/sdk/server/auth/global.ts +0 -80
  216. package/runtime/sdk/server/auth/host.ts +0 -318
  217. package/runtime/sdk/server/auth/models.ts +0 -83
  218. package/runtime/sdk/server/auth/types.ts +0 -50
  219. package/runtime/sdk/server/channel.ts +0 -56
  220. package/runtime/sdk/server/context.ts +0 -36
  221. package/runtime/sdk/server/db/defineDb.ts +0 -237
  222. package/runtime/sdk/server/db/fields.ts +0 -132
  223. package/runtime/sdk/server/db/httpTransport.ts +0 -93
  224. package/runtime/sdk/server/db/index.ts +0 -7
  225. package/runtime/sdk/server/db/marci/query.ts +0 -412
  226. package/runtime/sdk/server/db/types.ts +0 -202
  227. package/runtime/sdk/server/errors.ts +0 -12
  228. package/runtime/sdk/server/host.ts +0 -74
  229. package/runtime/sdk/server/inject.ts +0 -13
  230. package/runtime/sdk/server/runtime.ts +0 -133
  231. package/runtime/sdk/server/validate.ts +0 -87
  232. package/runtime/sdk/ui/NativeView.ts +0 -142
  233. package/runtime/sdk/ui/UI.ts +0 -39
  234. package/runtime/sdk/ui/UIBottomSheet.ts +0 -139
  235. package/runtime/sdk/ui/UIButton.ts +0 -101
  236. package/runtime/sdk/ui/UIContainer.ts +0 -60
  237. package/runtime/sdk/ui/UIImage.ts +0 -83
  238. package/runtime/sdk/ui/UIInput.ts +0 -185
  239. package/runtime/sdk/ui/UIModal.ts +0 -139
  240. package/runtime/sdk/ui/UINode.ts +0 -830
  241. package/runtime/sdk/ui/UIPager.ts +0 -362
  242. package/runtime/sdk/ui/UIPopover.ts +0 -100
  243. package/runtime/sdk/ui/UIScreen.ts +0 -123
  244. package/runtime/sdk/ui/UIScrollable.ts +0 -87
  245. package/runtime/sdk/ui/UISpacer.ts +0 -14
  246. package/runtime/sdk/ui/UITabs.ts +0 -236
  247. package/runtime/sdk/ui/UIText.ts +0 -51
  248. package/runtime/sdk/ui/UIVideo.ts +0 -88
  249. package/runtime/sdk/ui/UIVirtualizedList.ts +0 -241
  250. package/runtime/sdk/ui/UIWidget.ts +0 -127
  251. package/runtime/sdk/ui/fonts.ts +0 -13
  252. package/runtime/sdk/ui/presentable.ts +0 -117
  253. package/runtime/sdk/ui/router.ts +0 -132
  254. package/runtime/sdk/ui/theme.ts +0 -84
package/src/index.ts ADDED
@@ -0,0 +1,491 @@
1
+ import pkg from "../package.json"
2
+ import { app } from "./commands/app"
3
+ import { clone } from "./commands/clone"
4
+ import { compile, previewLegacy } from "./commands/compile"
5
+ import { create } from "./commands/create"
6
+ import { desktop } from "./commands/desktop"
7
+ import { design } from "./commands/design"
8
+ import { dev } from "./commands/dev"
9
+ import { diff } from "./commands/diff"
10
+ import { init } from "./commands/init"
11
+ import { link } from "./commands/link"
12
+ import { lightmap } from "./commands/lightmap"
13
+ import { navmesh } from "./commands/navmesh"
14
+ import { login } from "./commands/login"
15
+ import { shaders } from "./commands/shaders"
16
+ import { render } from "./commands/render"
17
+ import { pn } from "./commands/pn"
18
+ import { pull } from "./commands/pull"
19
+ import { push } from "./commands/push"
20
+ import { scene } from "./commands/scene"
21
+ import { thumbs } from "./commands/thumbs"
22
+ import { assets } from "./commands/assets"
23
+ import { status } from "./commands/status"
24
+ import { test } from "./commands/test"
25
+ import { update } from "./commands/update"
26
+ import { existsSync, readFileSync, realpathSync } from "node:fs"
27
+ import { createRequire } from "node:module"
28
+ import { delimiter, dirname, join } from "node:path"
29
+ import { install } from "./commands/install"
30
+ import { CliError, c, log, parseArgs, type Args } from "./util"
31
+ import { installedDesktopVersions, selectedDesktopVersion } from "./desktopRenderer"
32
+ import { installedMatcVersions } from "./matcTool"
33
+ import { loadProjectEnv } from "./projectEnv"
34
+ import { OPTIONAL_PEERS, peerVersion } from "./peers"
35
+
36
+ /* `install` is a standalone-binary-only command: it installs the shipped bundle onto PATH, so it's only
37
+ * meaningful when that bundle actually sits next to the executable — the `chisel` bundler + `runtime/`
38
+ * that scripts/build-bin.ts lays down beside the compiled binary. For an npm install process.execPath
39
+ * is `node`, and for `bun run` it's `bun`; neither has those companions, so `install` is unavailable
40
+ * there (omitted from help and unregistered → `lecodes install` reports an unknown command). */
41
+ const STANDALONE = (() => {
42
+ const dir = dirname(process.execPath)
43
+ const chisel = process.platform === "win32" ? "chisel.exe" : "chisel"
44
+ return existsSync(join(dir, "runtime")) && existsSync(join(dir, chisel))
45
+ })()
46
+
47
+ /* Are we ALREADY the installed copy — i.e. is a `lecodes` on PATH resolving to this very executable?
48
+ * If so, re-installing is pointless, so we drop `install` from help (but keep it runnable for repair /
49
+ * install-over-the-top). Running the freshly-unpacked archive instead: no PATH entry points back to it
50
+ * → this is false → `install` is offered. */
51
+ const INSTALLED = STANDALONE && (() => {
52
+ const exe = process.platform === "win32" ? "lecodes.exe" : "lecodes"
53
+ const self = (() => { try { return realpathSync(process.execPath) } catch { return process.execPath } })()
54
+ return (process.env.PATH ?? "").split(delimiter).some(d => {
55
+ if (!d) return false
56
+ try { const cand = join(d, exe); return existsSync(cand) && realpathSync(cand) === self } catch { return false }
57
+ })
58
+ })()
59
+
60
+ const INSTALL_HELP = STANDALONE && !INSTALLED ? `
61
+ install Install this standalone build so 'lecodes' is on your PATH
62
+ (copies the bundle to a library folder + a symlink/PATH entry).
63
+ --modify-path also add it to PATH (edits your shell rc / user PATH)
64
+ --system install for all users (Linux/macOS; needs sudo)
65
+ --dir <path> override the install/library folder
66
+ --bin <path> override the symlink folder (Linux/macOS)` : ""
67
+
68
+ const HELP = `${c.bold("lecodes")} — clone, edit and push LeCodes projects from your machine.
69
+
70
+ ${c.bold("Usage:")} lecodes <command> [options]
71
+
72
+ ${c.bold("Commands:")}
73
+ init [dir] Scaffold a new LOCAL project (no login/server): a starter
74
+ main.ts, a CLAUDE.md + passing flow test (the headless
75
+ render/test loop), IDE types + tsconfig, a .lecodesignore.
76
+ Run it with 'lecodes dev' or 'lecodes compile'.
77
+ --desktop also write an app.json declaring the desktop
78
+ target (window size; 'dev --desktop',
79
+ 'desktop run' and headless render/test
80
+ pick it up)${INSTALL_HELP}
81
+ login Authorize in the browser and store an access token
82
+ --token <pat> store a token from the web UI instead
83
+ --api <url> API origin (default https://le.codes)
84
+ --web <url> app origin for the browser flow
85
+ create <name> [dir] Create a new project on the server and scaffold it locally
86
+ (types from the server + tsconfig + a starter main.ts)
87
+ --team <uuid> create it inside a team
88
+ clone <uuid|url> [dir] Download a project's files + types into a new folder
89
+ --no-types skip the .d.ts / tsconfig generation
90
+ link [dir] Connect an existing local folder (e.g. one made by 'lecodes
91
+ init') to the platform — the reverse of clone. Creates a
92
+ project on the server, writes .lecodes/manifest.json and
93
+ refreshes the types; nothing is uploaded until you push
94
+ --name <s> project name (default: folder name)
95
+ --team <uuid> create it inside a team
96
+ --project <uuid|url> adopt an EXISTING project instead —
97
+ prints what your next push would change
98
+ on its server tree, and confirms first
99
+ --push [-m <msg>] push right after linking (--publish to
100
+ deploy it live in the same step)
101
+ --force re-link a folder that's already linked
102
+ --no-types skip the .d.ts / tsconfig refresh
103
+ status Show local changes vs the last sync
104
+ diff [path…] Show a unified diff of local vs the current server state
105
+ --name-only list changed paths + status only
106
+ pull [path…] Overwrite local files with the project's current state
107
+ (with path… : pull only those files/folders, keep other edits)
108
+ --force discard uncommitted local changes
109
+ push Push local changes as one new checkpoint
110
+ -m, --message checkpoint message
111
+ --publish also deploy the project live (compile) with the push
112
+ --force push even if the server moved on (shows + confirms
113
+ which server files get overwritten)
114
+ -y, --yes skip the --force overwrite confirmation
115
+ dev Run the project on a device from this machine, with hot reload:
116
+ starts a LAN dev server + prints a QR code; scan it with the
117
+ LeCodes app. Saves recompile + hot-swap the running app; device
118
+ logs/errors stream back here. No login needed.
119
+ --port <n> server port (default 4499)
120
+ --host <ip> LAN address to advertise (default: auto-detect)
121
+ --entry <path> override the entrypoint
122
+ --no-qr don't render the QR code
123
+ --desktop also open the app in the native desktop host
124
+ (same hot reload; window from app.json's
125
+ desktop block; closing it keeps the server)
126
+ --fps (--desktop) draw the host's frame counter
127
+ compile Compile the local project to JS (prints to stdout)
128
+ (was 'lecodes preview' — the old name still works)
129
+ -o, --out <file> write to a file instead of stdout
130
+ --entry <path> override the entrypoint
131
+ --no-header omit the metadata header
132
+ --no-minify keep the bundle readable (no compress/mangle)
133
+ --no-sourcemap don't generate/inline the source map
134
+ --public-url <url> asset URL prefix (default: the API origin)
135
+ render [file] Render the local UI project to a semantic JSON description
136
+ (needs the optional 'lecodes-renderer' package; UI projects only)
137
+ [file] render ONE screen module instead of the app:
138
+ a file that default-exports a UIScreen (or a
139
+ (state) => UIScreen) — every 'lecodes design'
140
+ screen is written that way. A bare id
141
+ ("login") resolves in design/screens/, and a
142
+ design screen renders at its meta.json frame
143
+ --state <name> ([file]) the state to pass a
144
+ (state) => UIScreen screen
145
+ -o, --out <file> write JSON to a file instead of stdout
146
+ --png [file] rasterize a PNG (default screenshot.png;
147
+ needs @napi-rs/canvas)
148
+ --map [file] draw the 3D scene's annotated top-down map
149
+ (grid + XZ footprints + named markers) as
150
+ <file>.png + <file>.json world-space data
151
+ (default map.png; --label-all, --max-px N;
152
+ also a { "map": "f.png" } script step)
153
+ --script <file> drive the app through a step list (tap/type/
154
+ scroll/wait/expect/screenshot/json steps;
155
+ games: frames/drag/hold/key/keyDown/keyUp,
156
+ expect.log, screenshot clip)
157
+ --out-dir <dir> where --script step outputs land (default .)
158
+ --clip <name|x,y,w,h> (--png) crop to one node's rect or a
159
+ logical-px rect — a component/region render
160
+ --fixtures <file> canned fetch responses (URL pattern →
161
+ { status?, json?, text? }) — make
162
+ backend-gated screens reachable headless
163
+ --scale <n> PNG device-pixel scale (default 2)
164
+ --width/--height viewport size (default 390x844)
165
+ --entry <path> override the entrypoint
166
+ --settle <ms> wait for promises/timers/fetch before snapshot
167
+ --wait-network let data fetches hit the real network
168
+ --logs forward the app's console output (to stderr)
169
+ --desktop render through the NATIVE desktop host instead:
170
+ real Filament 3D + tgfx UI + 2D, composited.
171
+ PNG only (--png). Downloads the LeCodes
172
+ Desktop renderer on first use into
173
+ ~/.lecodes/renderer (Windows/Linux;
174
+ LECODES_DESKTOP_EXE overrides)
175
+ --frames <n> (--desktop) frames to run before the shot —
176
+ headroom for async loads (default 60)
177
+ --desktop --script <file> drive the scenario through the native
178
+ host: tap/drag/hold take { x, y } coordinates
179
+ (no names — the host has no UI tree; type/
180
+ scroll/clip unavailable), expect.log is
181
+ checked, text/node/screen/value are skipped;
182
+ per-step screenshots + final --png
183
+ --view <name> (--desktop) inspection camera auto-fitted to the
184
+ whole 3D scene: iso | top | front | side | back
185
+ [--fov N]; the app's camera is untouched
186
+ --camera x,y,z --look-at x,y,z [--fov N]
187
+ (--desktop) inspection camera at a fixed eye /
188
+ look-at point (world units, Y up). In a
189
+ --script run use { "view" } / { "camera" } steps
190
+ --fixed-dt <ms> (--desktop) virtual app clock, ms per frame —
191
+ deterministic game time (scripted runs
192
+ default to 16; plain renders to wall clock)
193
+ test [name…] Run headless flow tests: tests/*.flow.json scenarios, plus flows
194
+ derived from the design graph (design/meta.json activator edges
195
+ — checks the built app against the designed navigation; needs
196
+ named elements + screen roots). Failing steps dump the frame
197
+ (JSON + PNG) under tests/.artifacts/<flow>/.
198
+ Design flows read tests/fixtures.json automatically (canned
199
+ fetch responses) so backend-gated transitions run headless;
200
+ file scenarios declare their own via the "fixtures" envelope.
201
+ --flows run ONLY the design-derived flows
202
+ --no-flows run ONLY the tests/*.flow.json files
203
+ --fixtures <file> fixture pack for the design flows (overrides
204
+ the tests/fixtures.json default)
205
+ --json machine-readable report on stdout
206
+ --dir <folder> tests folder (default tests/)
207
+ --design-dir <dir> design folder (default design/)
208
+ --artifacts <dir> failure-artifact folder override
209
+ --desktop run the file scenarios through the NATIVE host
210
+ (coordinates only; expect.log checked,
211
+ text/node/screen/value skipped; design flows
212
+ stay headless). --frames / --fixed-dt as in
213
+ render --desktop
214
+ (+ the render timing flags: --width/--height, --settle,
215
+ --device, --safe-area, --wait-network, --logs, --entry)
216
+ update Move everything a project depends on to its newest published
217
+ state, in dependency order: the desktop host ('desktop
218
+ update'), matc ('shaders update'), the project's shader
219
+ compiles, the IDE types (.lecodes/types/ + tsconfig.json,
220
+ from the server — the former 'lecodes types'), the
221
+ ios/android shell pins ('app update'), then the GLOBAL npm
222
+ installs: every installed optional peer (lecodes-renderer,
223
+ lecodes-design, lecodes-3d-editor, lecodes-assets) and this
224
+ CLI that is behind npm's latest gets 'npm install -g'.
225
+ Pinned tools are skipped, not overridden: LECODES_DESKTOP_EXE
226
+ / LECODES_MATC mean a local build. Keeps going when a step
227
+ fails; the summary says which.
228
+ --local types from the SDK bundled in this CLI
229
+ --no-shells leave ios/android pins alone (no re-sync)
230
+ --check npm step: report what's behind, install nothing
231
+ --no-cli skip the npm step (offline)
232
+ design [serve] LeCodes Design — a live canvas of screens for AI-driven prototyping
233
+ (needs the optional 'lecodes-design' package; works inside a
234
+ cloned project or standalone in any folder)
235
+ --port <n> dev-server port (default 4477)
236
+ --dir <folder> design folder (default design/)
237
+ --no-open don't open the browser
238
+ design init Scaffold the design folder (screens/, shared/, meta.json, CLAUDE.md)
239
+ --desktop desktop-frame board (meta.json defaultSize
240
+ [1280, 800] instead of the phone frame)
241
+ design snapshot [screen…] Render design screens headless (needs 'lecodes-renderer')
242
+ one screen → JSON to stdout; else files in design/.snapshots/
243
+ --png also rasterize PNGs (needs @napi-rs/canvas)
244
+ --out-dir <dir> output folder override
245
+ --logs forward the screens' console output (to stderr)
246
+ design comments Review comments pinned to the board, grouped by screen (open first)
247
+ --all include resolved threads
248
+ add "<text>" [--screen <id>] [--state <s>] [--element <name>]
249
+ reply <number> "<text>" · resolve <number> [--reply "<text>"] [--reopen]
250
+ (--ai signs a write as Claude; sends with the next "lecodes push")
251
+ desktop [run|build|update] The desktop target — the host 'lecodes render --desktop' uses too:
252
+ no args shows the version status (selected / downloaded /
253
+ newest published)
254
+ run compile the current project and open it in a real
255
+ desktop window (size/min-size/title default from
256
+ app.json's desktop block; --width/--height/--title
257
+ override; --fullscreen/--windowed override the
258
+ app.json fullscreen; --scale <0.5..4|fit> overrides
259
+ the display scale — "fit" sizes a 1080p-designed app
260
+ to the monitor with the density raised to match;
261
+ --entry overrides the entrypoint; --fps draws the
262
+ host's frame counter in the corner — composited fps,
263
+ plus the 3D fps when they diverge; asset() resources
264
+ load from the working tree — --remote-assets or
265
+ --public-url <url> uses the server's copies instead);
266
+ --server [--port N] [--max-clients N] launches the
267
+ host as a multiplayer server, --connect ip:port as a
268
+ client — both only set Net.launch (the app decides)
269
+ build stage a portable app folder (default dist/<name>/):
270
+ the cached host renamed to <Name>.exe + app.js +
271
+ assets/ (the project's files) + runtime/ (the engine's)
272
+ + app.json (the host reads title/size/fullscreen from
273
+ it) + app.ico from app.json "icon". Duplicate asset
274
+ basenames are a build error (they silently alias in
275
+ the bundle). Also spelled 'lecodes app build desktop'.
276
+ On macOS: a signed .app instead (the downloaded LeCodes
277
+ Desktop.app + the project inside it); app.json
278
+ desktop.macos = bundleId / buildNumber / category /
279
+ signing.identity / notarize.profile (a notarytool
280
+ keychain profile — notarized + stapled, so it opens
281
+ on any Mac without "Open Anyway").
282
+ -o <dir> output folder (default dist/<name>)
283
+ --slug <name> folder/zip name override (Latin-only)
284
+ --renderer <gl|vulkan> host + shader variant
285
+ --probe headless-render the staged folder to PNG
286
+ (macOS: a brief windowed run + snapshot)
287
+ --zip also pack the result for sending
288
+ --dmg macOS: also build a drag-to-Applications
289
+ disk image (signed + notarized too)
290
+ --sign <identity|adhoc> macOS codesign identity
291
+ --notarize <profile> / --no-notarize macOS
292
+ --zip <out>.zip, UTF-8/forward-slash entries
293
+ update move to the newest published release
294
+ (cached in ~/.lecodes/renderer)
295
+ lightmap bake Bake static shadows + ambient occlusion into a lightmap atlas
296
+ (assets/lightmap/lightmap.{png,bake}) by running the app in
297
+ the desktop host: the statics are what the app registers
298
+ with Lightmap.add, the bake fires at its Lightmap.load call.
299
+ Props need UVs first: lecodes assets doctor … --lightmap-uv
300
+ --out <dir> where the atlas lands (assets/lightmap)
301
+ --size <px> atlas edge (4096) --texel <m> density (0.02)
302
+ --sun-rays/--ao-rays <n> quality (32 / 64)
303
+ --ao-distance <m> AO reach (2) --sun-angle <deg> (0.5)
304
+ --frames <n> load headroom (900) --logs host output
305
+ navmesh bake Build the level's navigation mesh (assets/nav/<scene>.navmesh)
306
+ for NavAgent / NavMesh: the desktop host dumps every STATIC
307
+ body's collision triangles at the scene's env.navmesh (or
308
+ NavMesh.load) point, Recast builds the mesh here.
309
+ --out <dir> where it lands (assets/nav)
310
+ --name <n> file name (the scene file with env.navmesh)
311
+ --radius/--height/--climb/--slope agent size (scene's)
312
+ --cell <m> (0.25) --cell-height <m> (0.1) --tile <cells> (48)
313
+ --obj also write <n>.navmesh.obj to inspect
314
+ --frames <n> load headroom (900) --logs host output
315
+ shaders [compile|update] The project's custom shaders (.mat) and their local compile:
316
+ no args lists the shaders + their compile state; compile
317
+ compiles them all into .lecodes/shaders (the same pass
318
+ 'lecodes dev' runs on the fly — via Filament's matc,
319
+ downloaded on first use to ~/.lecodes/matc); update
320
+ downloads the newest matc (run after an engine upgrade)
321
+ scene [path] LeCodes Scene Editor — visually edit a .scene.ts (tree, inspector,
322
+ gizmo) on local files (needs the optional 'lecodes-3d-editor' package;
323
+ works inside a cloned project or standalone in any folder)
324
+ --port <n> editor-server port (default 4488)
325
+ --no-open don't open the browser
326
+ --no-mcp don't register the MCP server in .mcp.json
327
+ thumbs [path…] Asset thumbnails for the scene editor, WITHOUT a browser: decodes
328
+ the project's images (KTX2 too, via KTX-Software's ktx tool) into the
329
+ editor's preview cache. Incremental — the cached name carries the
330
+ asset's content hash, so unchanged files are skipped. Models,
331
+ materials and prefabs are rendered by the editor, not here
332
+ --force redo even the up-to-date ones
333
+ --size <px> longest edge (default 256)
334
+ assets convert <model.fbx|.glb> [--clips a.fbx b.fbx …] [-o out.glb] [--drop-model-clips]
335
+ [--fix] [--ktx2|--ktx2-hq]
336
+ FBX → LeCodes-ready GLB: meters/Y-up, textures embedded, clips from
337
+ other FBX files merged by bone name (Mixamo: one FBX per animation);
338
+ --fix/--ktx2 run the doctor passes (needs 'lecodes-assets')
339
+ Unity .mat files next to the FBX are read for the real textures
340
+ (metallic/roughness/ORM/emission packed the glTF way); --no-unity
341
+ keeps the FBX references; --unity-index <unity-index.txt> resolves GUIDs
342
+ --list-nodes / --keep <node> … / --split [dir]: carve one variant out of a
343
+ multi-variant prop file, re-origined on the ground (or one GLB per node)
344
+ --merge-skins b.fbx …: modular character pieces onto ONE skeleton
345
+ assets unpack <pkg.unitypackage> [-o dir] [--only re …] [--list]
346
+ Stream a .unitypackage to a folder: writes unity-index.txt (GUID → path,
347
+ what convert resolves textures with) and the matching assets + .meta
348
+ assets scene <map.unity> [-o level.json] [--filter re] [--group g] [--region x0,z0,x1,z1]
349
+ Unity scene → placed prefabs / mesh objects with world TRS in LeCodes'
350
+ frame + a prefab table (FBX + --keep node per prefab)
351
+ assets probe <file.fbx|.glb> [--filmstrip [out.png]]
352
+ Skeleton, clips (+ root motion / first-frame-vs-rest per clip), meshes,
353
+ materials, textures, bind sanity, duplicate names; --filmstrip renders
354
+ a CPU-skinned PNG strip per clip (no GPU)
355
+ assets sky <env.exr|.hdr> [-o dir] [--size N] [--ibl-size N] [--ibl-only]
356
+ Equirectangular HDR → the two cubemaps a scene needs: sky.ktx (sharp,
357
+ for 'skybox: { texture }') and ibl.ktx (prefiltered + SH, lights the
358
+ scene) — they are NOT interchangeable. Reports where the sun is in
359
+ the environment so the scene's light can be aimed to match
360
+ assets terrain-pack <layer> … [-o dir] [--normals n0 n1 …] [--names a b …] [--size N] [--png]
361
+ A terrain's splat layers (≤ 4) → <name>.ktx2 per layer + the packed
362
+ normal maps normals01/normals23.ktx2 (what Terrain.create's
363
+ layers/normals read). A layer = an albedo file or a FOLDER holding
364
+ *Albedo* + *Normal* (Unity packs). Prints the Terrain.create fragment
365
+ assets doctor <in.glb|dir> [--fix] [--ktx2] [--dump]
366
+ Analyze / fix a GLB against engine limits (bone cap 256, texture
367
+ formats/size, KTX2 via toktx)
368
+ app init <ios|android|desktop> Generate a native shell in <project>/ios/ or <project>/android/
369
+ (offline-first: runs the bundled app.js; never pushed to LeCodes).
370
+ desktop: no shell needed — writes app.json's desktop block
371
+ (window size; --fullscreen for kiosks)
372
+ --name <s> display name (default: app.json / folder name)
373
+ --bundle-id <id> iOS bundle identifier
374
+ --app-id <id> Android applicationId (default com.example.<slug>)
375
+ --team <id> Apple development team id
376
+ --variant <v> iOS SDK variant: auto (default — sync picks the
377
+ smallest of core/2d/3d/full covering what
378
+ the bundle uses), or one of those to pin
379
+ --sdk <v> native SDK release (default: the newest one,
380
+ pinned in app.json ios.sdk / android.sdk);
381
+ 'latest' floats it — every sync re-resolves
382
+ app sync [ios|android] Make every existing shell match the project: compile the bundle
383
+ + embed its assets (ios/App/Resources/ resp. the android
384
+ assets/; offline by default, also for server projects;
385
+ app.json "resources": {"remote": [globs]} keeps heavy assets
386
+ streaming), pick the iOS SDK variant from the bundle's engine
387
+ use, vendor app.json plugins (pinned in plugins.lock.json),
388
+ regenerate the CLI-owned shell files. app.json "update": true
389
+ (or {"url": …}) enables native OTA JS updates at launch (iOS).
390
+ app.json "icon" (square ≥1024px image) renders the app icons
391
+ every sync ("iconBackground": adaptive/flatten backdrop)
392
+ app update [ios|android] Move the shell's pins forward — app.json ios.sdk / android.sdk
393
+ to the newest published SDK release, plugins.lock.json to each
394
+ plugin's newest tag — then re-sync
395
+ --sdk <v|latest> pin that release instead of the newest
396
+ --check report available updates, write nothing
397
+ --no-plugins only move the SDK pin
398
+ --no-sync update the pins without re-syncing
399
+ app plugin <list|add|remove> Manage native plugins in app.json (then run 'lecodes app sync')
400
+ add takes a known id (camera, qr-scanner) or github:owner/repo[@tag]
401
+ app open [ios|android] Open the native shell in its IDE (Xcode / Android Studio)
402
+ app doctor Preflight checks for building/uploading the shells
403
+ app build ios Sync, archive and export the .ipa (automatic signing)
404
+ --configuration <c> build configuration (default Release)
405
+ --method <m> export method (default app-store-connect)
406
+ --no-export stop after the archive
407
+ --no-sync skip the lecodes app sync step
408
+ app build android Sync, then gradlew assembleRelease + bundleRelease (signing via
409
+ android/keystore.properties or LECODES_ANDROID_* env vars)
410
+ --configuration <c> Release (default) or Debug
411
+ --no-aab skip the .aab (APK only)
412
+ --no-sync skip the lecodes app sync step
413
+ app build desktop Same as 'lecodes desktop build' (kept for symmetry with the
414
+ ios/android shells) — see the desktop group above.
415
+ app upload ios Upload the archive to App Store Connect (then bumps buildNumber)
416
+ --key-path <p8> --key-id <id> --issuer-id <id>
417
+ ASC API key; stored in ~/.lecodes/asc.json
418
+ pn send --title <s> Push notifications for this project ('lecodes pn' for the group).
419
+ Sends with your login — no push key needed to test.
420
+ --user <ids> target Push.register({ user }) ids (comma-sep)
421
+ --to <pa_…> target addresses (comma-separated)
422
+ --body/--url/--badge/--data payload fields
423
+ pn devices Which devices registered (the 'why didn't it arrive?' command)
424
+ --user <id> only that user's devices
425
+ pn keys [new|revoke] Push keys for YOUR server: list, mint (owner only, shown once
426
+ — 'new --env' banks it in .env), revoke <id>
427
+
428
+ Config lives in ~/.lecodes/config.json (override with LECODES_API / LECODES_TOKEN).
429
+ A .env next to app.json is read at startup for LECODES_* / CREATOR_* keys (the shell's own
430
+ environment wins; ./ paths resolve against the project; the file is never pushed) — e.g.
431
+ LECODES_DESKTOP_EXE, LECODES_MATC, LECODES_NOTARY_PROFILE, LECODES_MACOS_SIGN_ID, LECODES_TOKEN.`
432
+
433
+ // `preview` is the legacy name of `compile` — kept registered (and out of the help) for old scripts.
434
+ const commands: Record<string, (args: Args) => Promise<void>> = { init, login, create, clone, link, status, diff, pull, push, compile, preview: previewLegacy, render, test, design, scene, thumbs, dev, app, pn, desktop, shaders, assets, lightmap, navmesh, update }
435
+ if (STANDALONE) commands.install = install
436
+
437
+ /** Package version, inlined at build time (the standalone binary has no package.json on disk). */
438
+ const readVersion = (): string => pkg.version ?? "unknown"
439
+
440
+ /** `--version`: the CLI version, every separately-installed optional peer (or "not installed"),
441
+ * and the desktop render host `render --desktop` would use (no network — cache state only). */
442
+ const printVersion = () => {
443
+ const rows: [label: string, value: string][] = [["lecodes-cli", readVersion()]]
444
+ for (const [name, probe] of OPTIONAL_PEERS) {
445
+ const version = peerVersion(name, probe)
446
+ rows.push([name, version ?? c.dim("not installed")])
447
+ }
448
+ const desktopVersion = selectedDesktopVersion()
449
+ rows.push(["lecodes-desktop", installedDesktopVersions().includes(desktopVersion)
450
+ ? desktopVersion
451
+ : `${desktopVersion} ${c.dim("(not downloaded — fetched on first `render --desktop`)")}`])
452
+ const matcVersion = installedMatcVersions()[0]
453
+ rows.push(["matc", matcVersion ?? c.dim("not downloaded — fetched on the first local shader compile")])
454
+ const width = Math.max(...rows.map(([label]) => label.length))
455
+ for (const [label, value] of rows) log(`${label.padEnd(width)} ${value}`)
456
+ }
457
+
458
+ const main = async () => {
459
+ const argv = process.argv.slice(2)
460
+ const command = argv[0]
461
+ // The project's .env (LECODES_* / CREATOR_* next to app.json) — before any command reads the
462
+ // environment; the real environment keeps precedence. See projectEnv.ts.
463
+ loadProjectEnv()
464
+
465
+ if (!command || command === "help" || command === "--help" || command === "-h") {
466
+ log(HELP)
467
+ return
468
+ }
469
+
470
+ if (command === "--version" || command === "-v" || command === "version") {
471
+ printVersion()
472
+ return
473
+ }
474
+
475
+ const run = commands[command]
476
+ if (!run) {
477
+ log(c.red(`Unknown command: ${command}`))
478
+ log(HELP)
479
+ process.exitCode = 1
480
+ return
481
+ }
482
+
483
+ // `assets` owns its own flag grammar (multi-value --clips …) — hand it the raw argv too.
484
+ await run(Object.assign(parseArgs(argv.slice(1)), { _raw: argv.slice(1) }))
485
+ }
486
+
487
+ main().catch((e: unknown) => {
488
+ if (e instanceof CliError) log(c.red("✗ ") + e.message)
489
+ else log(c.red("✗ Unexpected error: ") + (e instanceof Error ? e.message : String(e)))
490
+ process.exitCode = 1
491
+ })
@@ -0,0 +1,41 @@
1
+ // Types for the optional `lecodes-3d-editor` package's server entry (lazy-loaded by the `scene`
2
+ // command). The real package is installed separately; this lets the dynamic import typecheck
3
+ // without it present. Keep in sync with lecodes-tools/packages/lecodes-3d-editor/src/server/index.ts (the CLI↔tool
4
+ // dist coupling: after a server-surface change, rebuild the editor's dist and update this file).
5
+ declare module "lecodes-3d-editor/server" {
6
+ export type CompiledScene = { js: string | null, error?: string }
7
+
8
+ export type SceneServerOptions = {
9
+ root: string
10
+ compile: (scenePath: string) => Promise<CompiledScene>
11
+ /** A shader's compiled artifact file for a platform ("webgl-lite" / "webgl") from the host's
12
+ * cache, null when absent (the server builds the lite JSON itself then). */
13
+ shaderArtifact?: (shaderText: string, platform: string) => string | null
14
+ port?: number
15
+ appDir?: string
16
+ log?: (message: string) => void
17
+ }
18
+
19
+ export type SceneServer = {
20
+ port: number
21
+ url: string
22
+ close: () => void
23
+ }
24
+
25
+ export function startSceneServer(options: SceneServerOptions): Promise<SceneServer>
26
+
27
+ /* The asset-preview cache (server/previewCache.ts) — `lecodes thumbs` writes into the same
28
+ * `.lecodes/preview/<asset>.<hash>.png` layout the editor reads. */
29
+
30
+ /** sha256 tag (10 hex) of an asset's current bytes; null when the file is gone. */
31
+ export function hashAsset(projectDir: string, assetPath: string): string | null
32
+ /** Whether the cached preview for THAT hash is already on disk (the incremental skip). */
33
+ export function previewIsCurrent(projectDir: string, assetPath: string, hash: string): boolean
34
+ /** Cache file name for an asset at a known hash. */
35
+ export function previewName(assetPath: string, hash: string): string
36
+ /** Store a preview PNG under the asset's current hash (older ones are dropped); returns the file
37
+ * name written, or null when the asset itself is missing. */
38
+ export function writePreviewFile(projectDir: string, assetPath: string, png: Uint8Array): string | null
39
+ /** Asset path → cache file name, for every preview on disk. */
40
+ export function listPreviews(projectDir: string): Record<string, string>
41
+ }
@@ -0,0 +1,7 @@
1
+ // Types for the optional `lecodes-assets` package's CLI entry (lazy-loaded by the `assets` command).
2
+ // The real package is installed separately; this lets the dynamic import typecheck without it
3
+ // present. Keep in sync with packages/lecodes-assets/src/cli.ts.
4
+ declare module "lecodes-assets/cli" {
5
+ /** Runs `convert | probe | doctor` with the given argv (sub-command first). Resolves to the exit code. */
6
+ export function runAssetsCli(argv: string[]): Promise<number>
7
+ }