lecodes-cli 0.6.1 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,14 +1,1111 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module";
3
+ var __create = Object.create;
4
+ var __getProtoOf = Object.getPrototypeOf;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ function __accessProp(key) {
9
+ return this[key];
10
+ }
11
+ var __toESMCache_node;
12
+ var __toESMCache_esm;
13
+ var __toESM = (mod, isNodeMode, target) => {
14
+ var canCache = mod != null && typeof mod === "object";
15
+ if (canCache) {
16
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
+ var cached = cache.get(mod);
18
+ if (cached)
19
+ return cached;
20
+ }
21
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
22
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
+ for (let key of __getOwnPropNames(mod))
24
+ if (!__hasOwnProp.call(to, key))
25
+ __defProp(to, key, {
26
+ get: __accessProp.bind(mod, key),
27
+ enumerable: true
28
+ });
29
+ if (canCache)
30
+ cache.set(mod, to);
31
+ return to;
32
+ };
33
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
3
34
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
35
 
5
- // src/index.ts
6
- import { readFileSync as readFileSync13 } from "node:fs";
7
- import { dirname as dirname8, join as join13 } from "node:path";
8
- import { fileURLToPath as fileURLToPath4 } from "node:url";
36
+ // ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMode.js
37
+ var require_QRMode = __commonJS((exports, module) => {
38
+ module.exports = {
39
+ MODE_NUMBER: 1 << 0,
40
+ MODE_ALPHA_NUM: 1 << 1,
41
+ MODE_8BIT_BYTE: 1 << 2,
42
+ MODE_KANJI: 1 << 3
43
+ };
44
+ });
45
+
46
+ // ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QR8bitByte.js
47
+ var require_QR8bitByte = __commonJS((exports, module) => {
48
+ var QRMode = require_QRMode();
49
+ function QR8bitByte(data) {
50
+ this.mode = QRMode.MODE_8BIT_BYTE;
51
+ this.data = data;
52
+ }
53
+ QR8bitByte.prototype = {
54
+ getLength: function() {
55
+ return this.data.length;
56
+ },
57
+ write: function(buffer) {
58
+ for (var i = 0;i < this.data.length; i++) {
59
+ buffer.put(this.data.charCodeAt(i), 8);
60
+ }
61
+ }
62
+ };
63
+ module.exports = QR8bitByte;
64
+ });
65
+
66
+ // ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMath.js
67
+ var require_QRMath = __commonJS((exports, module) => {
68
+ var QRMath = {
69
+ glog: function(n) {
70
+ if (n < 1) {
71
+ throw new Error("glog(" + n + ")");
72
+ }
73
+ return QRMath.LOG_TABLE[n];
74
+ },
75
+ gexp: function(n) {
76
+ while (n < 0) {
77
+ n += 255;
78
+ }
79
+ while (n >= 256) {
80
+ n -= 255;
81
+ }
82
+ return QRMath.EXP_TABLE[n];
83
+ },
84
+ EXP_TABLE: new Array(256),
85
+ LOG_TABLE: new Array(256)
86
+ };
87
+ for (i = 0;i < 8; i++) {
88
+ QRMath.EXP_TABLE[i] = 1 << i;
89
+ }
90
+ var i;
91
+ for (i = 8;i < 256; i++) {
92
+ QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
93
+ }
94
+ var i;
95
+ for (i = 0;i < 255; i++) {
96
+ QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
97
+ }
98
+ var i;
99
+ module.exports = QRMath;
100
+ });
101
+
102
+ // ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRPolynomial.js
103
+ var require_QRPolynomial = __commonJS((exports, module) => {
104
+ var QRMath = require_QRMath();
105
+ function QRPolynomial(num, shift) {
106
+ if (num.length === undefined) {
107
+ throw new Error(num.length + "/" + shift);
108
+ }
109
+ var offset = 0;
110
+ while (offset < num.length && num[offset] === 0) {
111
+ offset++;
112
+ }
113
+ this.num = new Array(num.length - offset + shift);
114
+ for (var i = 0;i < num.length - offset; i++) {
115
+ this.num[i] = num[i + offset];
116
+ }
117
+ }
118
+ QRPolynomial.prototype = {
119
+ get: function(index) {
120
+ return this.num[index];
121
+ },
122
+ getLength: function() {
123
+ return this.num.length;
124
+ },
125
+ multiply: function(e) {
126
+ var num = new Array(this.getLength() + e.getLength() - 1);
127
+ for (var i = 0;i < this.getLength(); i++) {
128
+ for (var j = 0;j < e.getLength(); j++) {
129
+ num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
130
+ }
131
+ }
132
+ return new QRPolynomial(num, 0);
133
+ },
134
+ mod: function(e) {
135
+ if (this.getLength() - e.getLength() < 0) {
136
+ return this;
137
+ }
138
+ var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0));
139
+ var num = new Array(this.getLength());
140
+ for (var i = 0;i < this.getLength(); i++) {
141
+ num[i] = this.get(i);
142
+ }
143
+ for (var x = 0;x < e.getLength(); x++) {
144
+ num[x] ^= QRMath.gexp(QRMath.glog(e.get(x)) + ratio);
145
+ }
146
+ return new QRPolynomial(num, 0).mod(e);
147
+ }
148
+ };
149
+ module.exports = QRPolynomial;
150
+ });
151
+
152
+ // ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMaskPattern.js
153
+ var require_QRMaskPattern = __commonJS((exports, module) => {
154
+ module.exports = {
155
+ PATTERN000: 0,
156
+ PATTERN001: 1,
157
+ PATTERN010: 2,
158
+ PATTERN011: 3,
159
+ PATTERN100: 4,
160
+ PATTERN101: 5,
161
+ PATTERN110: 6,
162
+ PATTERN111: 7
163
+ };
164
+ });
165
+
166
+ // ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRUtil.js
167
+ var require_QRUtil = __commonJS((exports, module) => {
168
+ var QRMode = require_QRMode();
169
+ var QRPolynomial = require_QRPolynomial();
170
+ var QRMath = require_QRMath();
171
+ var QRMaskPattern = require_QRMaskPattern();
172
+ var QRUtil = {
173
+ PATTERN_POSITION_TABLE: [
174
+ [],
175
+ [6, 18],
176
+ [6, 22],
177
+ [6, 26],
178
+ [6, 30],
179
+ [6, 34],
180
+ [6, 22, 38],
181
+ [6, 24, 42],
182
+ [6, 26, 46],
183
+ [6, 28, 50],
184
+ [6, 30, 54],
185
+ [6, 32, 58],
186
+ [6, 34, 62],
187
+ [6, 26, 46, 66],
188
+ [6, 26, 48, 70],
189
+ [6, 26, 50, 74],
190
+ [6, 30, 54, 78],
191
+ [6, 30, 56, 82],
192
+ [6, 30, 58, 86],
193
+ [6, 34, 62, 90],
194
+ [6, 28, 50, 72, 94],
195
+ [6, 26, 50, 74, 98],
196
+ [6, 30, 54, 78, 102],
197
+ [6, 28, 54, 80, 106],
198
+ [6, 32, 58, 84, 110],
199
+ [6, 30, 58, 86, 114],
200
+ [6, 34, 62, 90, 118],
201
+ [6, 26, 50, 74, 98, 122],
202
+ [6, 30, 54, 78, 102, 126],
203
+ [6, 26, 52, 78, 104, 130],
204
+ [6, 30, 56, 82, 108, 134],
205
+ [6, 34, 60, 86, 112, 138],
206
+ [6, 30, 58, 86, 114, 142],
207
+ [6, 34, 62, 90, 118, 146],
208
+ [6, 30, 54, 78, 102, 126, 150],
209
+ [6, 24, 50, 76, 102, 128, 154],
210
+ [6, 28, 54, 80, 106, 132, 158],
211
+ [6, 32, 58, 84, 110, 136, 162],
212
+ [6, 26, 54, 82, 110, 138, 166],
213
+ [6, 30, 58, 86, 114, 142, 170]
214
+ ],
215
+ G15: 1 << 10 | 1 << 8 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 1 | 1 << 0,
216
+ G18: 1 << 12 | 1 << 11 | 1 << 10 | 1 << 9 | 1 << 8 | 1 << 5 | 1 << 2 | 1 << 0,
217
+ G15_MASK: 1 << 14 | 1 << 12 | 1 << 10 | 1 << 4 | 1 << 1,
218
+ getBCHTypeInfo: function(data) {
219
+ var d = data << 10;
220
+ while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
221
+ d ^= QRUtil.G15 << QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15);
222
+ }
223
+ return (data << 10 | d) ^ QRUtil.G15_MASK;
224
+ },
225
+ getBCHTypeNumber: function(data) {
226
+ var d = data << 12;
227
+ while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
228
+ d ^= QRUtil.G18 << QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18);
229
+ }
230
+ return data << 12 | d;
231
+ },
232
+ getBCHDigit: function(data) {
233
+ var digit = 0;
234
+ while (data !== 0) {
235
+ digit++;
236
+ data >>>= 1;
237
+ }
238
+ return digit;
239
+ },
240
+ getPatternPosition: function(typeNumber) {
241
+ return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
242
+ },
243
+ getMask: function(maskPattern, i, j) {
244
+ switch (maskPattern) {
245
+ case QRMaskPattern.PATTERN000:
246
+ return (i + j) % 2 === 0;
247
+ case QRMaskPattern.PATTERN001:
248
+ return i % 2 === 0;
249
+ case QRMaskPattern.PATTERN010:
250
+ return j % 3 === 0;
251
+ case QRMaskPattern.PATTERN011:
252
+ return (i + j) % 3 === 0;
253
+ case QRMaskPattern.PATTERN100:
254
+ return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 === 0;
255
+ case QRMaskPattern.PATTERN101:
256
+ return i * j % 2 + i * j % 3 === 0;
257
+ case QRMaskPattern.PATTERN110:
258
+ return (i * j % 2 + i * j % 3) % 2 === 0;
259
+ case QRMaskPattern.PATTERN111:
260
+ return (i * j % 3 + (i + j) % 2) % 2 === 0;
261
+ default:
262
+ throw new Error("bad maskPattern:" + maskPattern);
263
+ }
264
+ },
265
+ getErrorCorrectPolynomial: function(errorCorrectLength) {
266
+ var a = new QRPolynomial([1], 0);
267
+ for (var i = 0;i < errorCorrectLength; i++) {
268
+ a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0));
269
+ }
270
+ return a;
271
+ },
272
+ getLengthInBits: function(mode, type) {
273
+ if (1 <= type && type < 10) {
274
+ switch (mode) {
275
+ case QRMode.MODE_NUMBER:
276
+ return 10;
277
+ case QRMode.MODE_ALPHA_NUM:
278
+ return 9;
279
+ case QRMode.MODE_8BIT_BYTE:
280
+ return 8;
281
+ case QRMode.MODE_KANJI:
282
+ return 8;
283
+ default:
284
+ throw new Error("mode:" + mode);
285
+ }
286
+ } else if (type < 27) {
287
+ switch (mode) {
288
+ case QRMode.MODE_NUMBER:
289
+ return 12;
290
+ case QRMode.MODE_ALPHA_NUM:
291
+ return 11;
292
+ case QRMode.MODE_8BIT_BYTE:
293
+ return 16;
294
+ case QRMode.MODE_KANJI:
295
+ return 10;
296
+ default:
297
+ throw new Error("mode:" + mode);
298
+ }
299
+ } else if (type < 41) {
300
+ switch (mode) {
301
+ case QRMode.MODE_NUMBER:
302
+ return 14;
303
+ case QRMode.MODE_ALPHA_NUM:
304
+ return 13;
305
+ case QRMode.MODE_8BIT_BYTE:
306
+ return 16;
307
+ case QRMode.MODE_KANJI:
308
+ return 12;
309
+ default:
310
+ throw new Error("mode:" + mode);
311
+ }
312
+ } else {
313
+ throw new Error("type:" + type);
314
+ }
315
+ },
316
+ getLostPoint: function(qrCode) {
317
+ var moduleCount = qrCode.getModuleCount();
318
+ var lostPoint = 0;
319
+ var row = 0;
320
+ var col = 0;
321
+ for (row = 0;row < moduleCount; row++) {
322
+ for (col = 0;col < moduleCount; col++) {
323
+ var sameCount = 0;
324
+ var dark = qrCode.isDark(row, col);
325
+ for (var r = -1;r <= 1; r++) {
326
+ if (row + r < 0 || moduleCount <= row + r) {
327
+ continue;
328
+ }
329
+ for (var c2 = -1;c2 <= 1; c2++) {
330
+ if (col + c2 < 0 || moduleCount <= col + c2) {
331
+ continue;
332
+ }
333
+ if (r === 0 && c2 === 0) {
334
+ continue;
335
+ }
336
+ if (dark === qrCode.isDark(row + r, col + c2)) {
337
+ sameCount++;
338
+ }
339
+ }
340
+ }
341
+ if (sameCount > 5) {
342
+ lostPoint += 3 + sameCount - 5;
343
+ }
344
+ }
345
+ }
346
+ for (row = 0;row < moduleCount - 1; row++) {
347
+ for (col = 0;col < moduleCount - 1; col++) {
348
+ var count = 0;
349
+ if (qrCode.isDark(row, col))
350
+ count++;
351
+ if (qrCode.isDark(row + 1, col))
352
+ count++;
353
+ if (qrCode.isDark(row, col + 1))
354
+ count++;
355
+ if (qrCode.isDark(row + 1, col + 1))
356
+ count++;
357
+ if (count === 0 || count === 4) {
358
+ lostPoint += 3;
359
+ }
360
+ }
361
+ }
362
+ for (row = 0;row < moduleCount; row++) {
363
+ for (col = 0;col < moduleCount - 6; col++) {
364
+ if (qrCode.isDark(row, col) && !qrCode.isDark(row, col + 1) && qrCode.isDark(row, col + 2) && qrCode.isDark(row, col + 3) && qrCode.isDark(row, col + 4) && !qrCode.isDark(row, col + 5) && qrCode.isDark(row, col + 6)) {
365
+ lostPoint += 40;
366
+ }
367
+ }
368
+ }
369
+ for (col = 0;col < moduleCount; col++) {
370
+ for (row = 0;row < moduleCount - 6; row++) {
371
+ if (qrCode.isDark(row, col) && !qrCode.isDark(row + 1, col) && qrCode.isDark(row + 2, col) && qrCode.isDark(row + 3, col) && qrCode.isDark(row + 4, col) && !qrCode.isDark(row + 5, col) && qrCode.isDark(row + 6, col)) {
372
+ lostPoint += 40;
373
+ }
374
+ }
375
+ }
376
+ var darkCount = 0;
377
+ for (col = 0;col < moduleCount; col++) {
378
+ for (row = 0;row < moduleCount; row++) {
379
+ if (qrCode.isDark(row, col)) {
380
+ darkCount++;
381
+ }
382
+ }
383
+ }
384
+ var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
385
+ lostPoint += ratio * 10;
386
+ return lostPoint;
387
+ }
388
+ };
389
+ module.exports = QRUtil;
390
+ });
391
+
392
+ // ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js
393
+ var require_QRErrorCorrectLevel = __commonJS((exports, module) => {
394
+ module.exports = {
395
+ L: 1,
396
+ M: 0,
397
+ Q: 3,
398
+ H: 2
399
+ };
400
+ });
401
+
402
+ // ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRRSBlock.js
403
+ var require_QRRSBlock = __commonJS((exports, module) => {
404
+ var QRErrorCorrectLevel = require_QRErrorCorrectLevel();
405
+ function QRRSBlock(totalCount, dataCount) {
406
+ this.totalCount = totalCount;
407
+ this.dataCount = dataCount;
408
+ }
409
+ QRRSBlock.RS_BLOCK_TABLE = [
410
+ [1, 26, 19],
411
+ [1, 26, 16],
412
+ [1, 26, 13],
413
+ [1, 26, 9],
414
+ [1, 44, 34],
415
+ [1, 44, 28],
416
+ [1, 44, 22],
417
+ [1, 44, 16],
418
+ [1, 70, 55],
419
+ [1, 70, 44],
420
+ [2, 35, 17],
421
+ [2, 35, 13],
422
+ [1, 100, 80],
423
+ [2, 50, 32],
424
+ [2, 50, 24],
425
+ [4, 25, 9],
426
+ [1, 134, 108],
427
+ [2, 67, 43],
428
+ [2, 33, 15, 2, 34, 16],
429
+ [2, 33, 11, 2, 34, 12],
430
+ [2, 86, 68],
431
+ [4, 43, 27],
432
+ [4, 43, 19],
433
+ [4, 43, 15],
434
+ [2, 98, 78],
435
+ [4, 49, 31],
436
+ [2, 32, 14, 4, 33, 15],
437
+ [4, 39, 13, 1, 40, 14],
438
+ [2, 121, 97],
439
+ [2, 60, 38, 2, 61, 39],
440
+ [4, 40, 18, 2, 41, 19],
441
+ [4, 40, 14, 2, 41, 15],
442
+ [2, 146, 116],
443
+ [3, 58, 36, 2, 59, 37],
444
+ [4, 36, 16, 4, 37, 17],
445
+ [4, 36, 12, 4, 37, 13],
446
+ [2, 86, 68, 2, 87, 69],
447
+ [4, 69, 43, 1, 70, 44],
448
+ [6, 43, 19, 2, 44, 20],
449
+ [6, 43, 15, 2, 44, 16],
450
+ [4, 101, 81],
451
+ [1, 80, 50, 4, 81, 51],
452
+ [4, 50, 22, 4, 51, 23],
453
+ [3, 36, 12, 8, 37, 13],
454
+ [2, 116, 92, 2, 117, 93],
455
+ [6, 58, 36, 2, 59, 37],
456
+ [4, 46, 20, 6, 47, 21],
457
+ [7, 42, 14, 4, 43, 15],
458
+ [4, 133, 107],
459
+ [8, 59, 37, 1, 60, 38],
460
+ [8, 44, 20, 4, 45, 21],
461
+ [12, 33, 11, 4, 34, 12],
462
+ [3, 145, 115, 1, 146, 116],
463
+ [4, 64, 40, 5, 65, 41],
464
+ [11, 36, 16, 5, 37, 17],
465
+ [11, 36, 12, 5, 37, 13],
466
+ [5, 109, 87, 1, 110, 88],
467
+ [5, 65, 41, 5, 66, 42],
468
+ [5, 54, 24, 7, 55, 25],
469
+ [11, 36, 12],
470
+ [5, 122, 98, 1, 123, 99],
471
+ [7, 73, 45, 3, 74, 46],
472
+ [15, 43, 19, 2, 44, 20],
473
+ [3, 45, 15, 13, 46, 16],
474
+ [1, 135, 107, 5, 136, 108],
475
+ [10, 74, 46, 1, 75, 47],
476
+ [1, 50, 22, 15, 51, 23],
477
+ [2, 42, 14, 17, 43, 15],
478
+ [5, 150, 120, 1, 151, 121],
479
+ [9, 69, 43, 4, 70, 44],
480
+ [17, 50, 22, 1, 51, 23],
481
+ [2, 42, 14, 19, 43, 15],
482
+ [3, 141, 113, 4, 142, 114],
483
+ [3, 70, 44, 11, 71, 45],
484
+ [17, 47, 21, 4, 48, 22],
485
+ [9, 39, 13, 16, 40, 14],
486
+ [3, 135, 107, 5, 136, 108],
487
+ [3, 67, 41, 13, 68, 42],
488
+ [15, 54, 24, 5, 55, 25],
489
+ [15, 43, 15, 10, 44, 16],
490
+ [4, 144, 116, 4, 145, 117],
491
+ [17, 68, 42],
492
+ [17, 50, 22, 6, 51, 23],
493
+ [19, 46, 16, 6, 47, 17],
494
+ [2, 139, 111, 7, 140, 112],
495
+ [17, 74, 46],
496
+ [7, 54, 24, 16, 55, 25],
497
+ [34, 37, 13],
498
+ [4, 151, 121, 5, 152, 122],
499
+ [4, 75, 47, 14, 76, 48],
500
+ [11, 54, 24, 14, 55, 25],
501
+ [16, 45, 15, 14, 46, 16],
502
+ [6, 147, 117, 4, 148, 118],
503
+ [6, 73, 45, 14, 74, 46],
504
+ [11, 54, 24, 16, 55, 25],
505
+ [30, 46, 16, 2, 47, 17],
506
+ [8, 132, 106, 4, 133, 107],
507
+ [8, 75, 47, 13, 76, 48],
508
+ [7, 54, 24, 22, 55, 25],
509
+ [22, 45, 15, 13, 46, 16],
510
+ [10, 142, 114, 2, 143, 115],
511
+ [19, 74, 46, 4, 75, 47],
512
+ [28, 50, 22, 6, 51, 23],
513
+ [33, 46, 16, 4, 47, 17],
514
+ [8, 152, 122, 4, 153, 123],
515
+ [22, 73, 45, 3, 74, 46],
516
+ [8, 53, 23, 26, 54, 24],
517
+ [12, 45, 15, 28, 46, 16],
518
+ [3, 147, 117, 10, 148, 118],
519
+ [3, 73, 45, 23, 74, 46],
520
+ [4, 54, 24, 31, 55, 25],
521
+ [11, 45, 15, 31, 46, 16],
522
+ [7, 146, 116, 7, 147, 117],
523
+ [21, 73, 45, 7, 74, 46],
524
+ [1, 53, 23, 37, 54, 24],
525
+ [19, 45, 15, 26, 46, 16],
526
+ [5, 145, 115, 10, 146, 116],
527
+ [19, 75, 47, 10, 76, 48],
528
+ [15, 54, 24, 25, 55, 25],
529
+ [23, 45, 15, 25, 46, 16],
530
+ [13, 145, 115, 3, 146, 116],
531
+ [2, 74, 46, 29, 75, 47],
532
+ [42, 54, 24, 1, 55, 25],
533
+ [23, 45, 15, 28, 46, 16],
534
+ [17, 145, 115],
535
+ [10, 74, 46, 23, 75, 47],
536
+ [10, 54, 24, 35, 55, 25],
537
+ [19, 45, 15, 35, 46, 16],
538
+ [17, 145, 115, 1, 146, 116],
539
+ [14, 74, 46, 21, 75, 47],
540
+ [29, 54, 24, 19, 55, 25],
541
+ [11, 45, 15, 46, 46, 16],
542
+ [13, 145, 115, 6, 146, 116],
543
+ [14, 74, 46, 23, 75, 47],
544
+ [44, 54, 24, 7, 55, 25],
545
+ [59, 46, 16, 1, 47, 17],
546
+ [12, 151, 121, 7, 152, 122],
547
+ [12, 75, 47, 26, 76, 48],
548
+ [39, 54, 24, 14, 55, 25],
549
+ [22, 45, 15, 41, 46, 16],
550
+ [6, 151, 121, 14, 152, 122],
551
+ [6, 75, 47, 34, 76, 48],
552
+ [46, 54, 24, 10, 55, 25],
553
+ [2, 45, 15, 64, 46, 16],
554
+ [17, 152, 122, 4, 153, 123],
555
+ [29, 74, 46, 14, 75, 47],
556
+ [49, 54, 24, 10, 55, 25],
557
+ [24, 45, 15, 46, 46, 16],
558
+ [4, 152, 122, 18, 153, 123],
559
+ [13, 74, 46, 32, 75, 47],
560
+ [48, 54, 24, 14, 55, 25],
561
+ [42, 45, 15, 32, 46, 16],
562
+ [20, 147, 117, 4, 148, 118],
563
+ [40, 75, 47, 7, 76, 48],
564
+ [43, 54, 24, 22, 55, 25],
565
+ [10, 45, 15, 67, 46, 16],
566
+ [19, 148, 118, 6, 149, 119],
567
+ [18, 75, 47, 31, 76, 48],
568
+ [34, 54, 24, 34, 55, 25],
569
+ [20, 45, 15, 61, 46, 16]
570
+ ];
571
+ QRRSBlock.getRSBlocks = function(typeNumber, errorCorrectLevel) {
572
+ var rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel);
573
+ if (rsBlock === undefined) {
574
+ throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + errorCorrectLevel);
575
+ }
576
+ var length = rsBlock.length / 3;
577
+ var list = [];
578
+ for (var i = 0;i < length; i++) {
579
+ var count = rsBlock[i * 3 + 0];
580
+ var totalCount = rsBlock[i * 3 + 1];
581
+ var dataCount = rsBlock[i * 3 + 2];
582
+ for (var j = 0;j < count; j++) {
583
+ list.push(new QRRSBlock(totalCount, dataCount));
584
+ }
585
+ }
586
+ return list;
587
+ };
588
+ QRRSBlock.getRsBlockTable = function(typeNumber, errorCorrectLevel) {
589
+ switch (errorCorrectLevel) {
590
+ case QRErrorCorrectLevel.L:
591
+ return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
592
+ case QRErrorCorrectLevel.M:
593
+ return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
594
+ case QRErrorCorrectLevel.Q:
595
+ return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
596
+ case QRErrorCorrectLevel.H:
597
+ return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
598
+ default:
599
+ return;
600
+ }
601
+ };
602
+ module.exports = QRRSBlock;
603
+ });
604
+
605
+ // ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRBitBuffer.js
606
+ var require_QRBitBuffer = __commonJS((exports, module) => {
607
+ function QRBitBuffer() {
608
+ this.buffer = [];
609
+ this.length = 0;
610
+ }
611
+ QRBitBuffer.prototype = {
612
+ get: function(index) {
613
+ var bufIndex = Math.floor(index / 8);
614
+ return (this.buffer[bufIndex] >>> 7 - index % 8 & 1) == 1;
615
+ },
616
+ put: function(num, length) {
617
+ for (var i = 0;i < length; i++) {
618
+ this.putBit((num >>> length - i - 1 & 1) == 1);
619
+ }
620
+ },
621
+ getLengthInBits: function() {
622
+ return this.length;
623
+ },
624
+ putBit: function(bit) {
625
+ var bufIndex = Math.floor(this.length / 8);
626
+ if (this.buffer.length <= bufIndex) {
627
+ this.buffer.push(0);
628
+ }
629
+ if (bit) {
630
+ this.buffer[bufIndex] |= 128 >>> this.length % 8;
631
+ }
632
+ this.length++;
633
+ }
634
+ };
635
+ module.exports = QRBitBuffer;
636
+ });
637
+
638
+ // ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/index.js
639
+ var require_QRCode = __commonJS((exports, module) => {
640
+ var QR8bitByte = require_QR8bitByte();
641
+ var QRUtil = require_QRUtil();
642
+ var QRPolynomial = require_QRPolynomial();
643
+ var QRRSBlock = require_QRRSBlock();
644
+ var QRBitBuffer = require_QRBitBuffer();
645
+ function QRCode(typeNumber, errorCorrectLevel) {
646
+ this.typeNumber = typeNumber;
647
+ this.errorCorrectLevel = errorCorrectLevel;
648
+ this.modules = null;
649
+ this.moduleCount = 0;
650
+ this.dataCache = null;
651
+ this.dataList = [];
652
+ }
653
+ QRCode.prototype = {
654
+ addData: function(data) {
655
+ var newData = new QR8bitByte(data);
656
+ this.dataList.push(newData);
657
+ this.dataCache = null;
658
+ },
659
+ isDark: function(row, col) {
660
+ if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) {
661
+ throw new Error(row + "," + col);
662
+ }
663
+ return this.modules[row][col];
664
+ },
665
+ getModuleCount: function() {
666
+ return this.moduleCount;
667
+ },
668
+ make: function() {
669
+ if (this.typeNumber < 1) {
670
+ var typeNumber = 1;
671
+ for (typeNumber = 1;typeNumber < 40; typeNumber++) {
672
+ var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, this.errorCorrectLevel);
673
+ var buffer = new QRBitBuffer;
674
+ var totalDataCount = 0;
675
+ for (var i = 0;i < rsBlocks.length; i++) {
676
+ totalDataCount += rsBlocks[i].dataCount;
677
+ }
678
+ for (var x = 0;x < this.dataList.length; x++) {
679
+ var data = this.dataList[x];
680
+ buffer.put(data.mode, 4);
681
+ buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber));
682
+ data.write(buffer);
683
+ }
684
+ if (buffer.getLengthInBits() <= totalDataCount * 8)
685
+ break;
686
+ }
687
+ this.typeNumber = typeNumber;
688
+ }
689
+ this.makeImpl(false, this.getBestMaskPattern());
690
+ },
691
+ makeImpl: function(test, maskPattern) {
692
+ this.moduleCount = this.typeNumber * 4 + 17;
693
+ this.modules = new Array(this.moduleCount);
694
+ for (var row = 0;row < this.moduleCount; row++) {
695
+ this.modules[row] = new Array(this.moduleCount);
696
+ for (var col = 0;col < this.moduleCount; col++) {
697
+ this.modules[row][col] = null;
698
+ }
699
+ }
700
+ this.setupPositionProbePattern(0, 0);
701
+ this.setupPositionProbePattern(this.moduleCount - 7, 0);
702
+ this.setupPositionProbePattern(0, this.moduleCount - 7);
703
+ this.setupPositionAdjustPattern();
704
+ this.setupTimingPattern();
705
+ this.setupTypeInfo(test, maskPattern);
706
+ if (this.typeNumber >= 7) {
707
+ this.setupTypeNumber(test);
708
+ }
709
+ if (this.dataCache === null) {
710
+ this.dataCache = QRCode.createData(this.typeNumber, this.errorCorrectLevel, this.dataList);
711
+ }
712
+ this.mapData(this.dataCache, maskPattern);
713
+ },
714
+ setupPositionProbePattern: function(row, col) {
715
+ for (var r = -1;r <= 7; r++) {
716
+ if (row + r <= -1 || this.moduleCount <= row + r)
717
+ continue;
718
+ for (var c2 = -1;c2 <= 7; c2++) {
719
+ if (col + c2 <= -1 || this.moduleCount <= col + c2)
720
+ continue;
721
+ if (0 <= r && r <= 6 && (c2 === 0 || c2 === 6) || 0 <= c2 && c2 <= 6 && (r === 0 || r === 6) || 2 <= r && r <= 4 && 2 <= c2 && c2 <= 4) {
722
+ this.modules[row + r][col + c2] = true;
723
+ } else {
724
+ this.modules[row + r][col + c2] = false;
725
+ }
726
+ }
727
+ }
728
+ },
729
+ getBestMaskPattern: function() {
730
+ var minLostPoint = 0;
731
+ var pattern = 0;
732
+ for (var i = 0;i < 8; i++) {
733
+ this.makeImpl(true, i);
734
+ var lostPoint = QRUtil.getLostPoint(this);
735
+ if (i === 0 || minLostPoint > lostPoint) {
736
+ minLostPoint = lostPoint;
737
+ pattern = i;
738
+ }
739
+ }
740
+ return pattern;
741
+ },
742
+ createMovieClip: function(target_mc, instance_name, depth) {
743
+ var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth);
744
+ var cs = 1;
745
+ this.make();
746
+ for (var row = 0;row < this.modules.length; row++) {
747
+ var y = row * cs;
748
+ for (var col = 0;col < this.modules[row].length; col++) {
749
+ var x = col * cs;
750
+ var dark = this.modules[row][col];
751
+ if (dark) {
752
+ qr_mc.beginFill(0, 100);
753
+ qr_mc.moveTo(x, y);
754
+ qr_mc.lineTo(x + cs, y);
755
+ qr_mc.lineTo(x + cs, y + cs);
756
+ qr_mc.lineTo(x, y + cs);
757
+ qr_mc.endFill();
758
+ }
759
+ }
760
+ }
761
+ return qr_mc;
762
+ },
763
+ setupTimingPattern: function() {
764
+ for (var r = 8;r < this.moduleCount - 8; r++) {
765
+ if (this.modules[r][6] !== null) {
766
+ continue;
767
+ }
768
+ this.modules[r][6] = r % 2 === 0;
769
+ }
770
+ for (var c2 = 8;c2 < this.moduleCount - 8; c2++) {
771
+ if (this.modules[6][c2] !== null) {
772
+ continue;
773
+ }
774
+ this.modules[6][c2] = c2 % 2 === 0;
775
+ }
776
+ },
777
+ setupPositionAdjustPattern: function() {
778
+ var pos = QRUtil.getPatternPosition(this.typeNumber);
779
+ for (var i = 0;i < pos.length; i++) {
780
+ for (var j = 0;j < pos.length; j++) {
781
+ var row = pos[i];
782
+ var col = pos[j];
783
+ if (this.modules[row][col] !== null) {
784
+ continue;
785
+ }
786
+ for (var r = -2;r <= 2; r++) {
787
+ for (var c2 = -2;c2 <= 2; c2++) {
788
+ if (Math.abs(r) === 2 || Math.abs(c2) === 2 || r === 0 && c2 === 0) {
789
+ this.modules[row + r][col + c2] = true;
790
+ } else {
791
+ this.modules[row + r][col + c2] = false;
792
+ }
793
+ }
794
+ }
795
+ }
796
+ }
797
+ },
798
+ setupTypeNumber: function(test) {
799
+ var bits = QRUtil.getBCHTypeNumber(this.typeNumber);
800
+ var mod;
801
+ for (var i = 0;i < 18; i++) {
802
+ mod = !test && (bits >> i & 1) === 1;
803
+ this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;
804
+ }
805
+ for (var x = 0;x < 18; x++) {
806
+ mod = !test && (bits >> x & 1) === 1;
807
+ this.modules[x % 3 + this.moduleCount - 8 - 3][Math.floor(x / 3)] = mod;
808
+ }
809
+ },
810
+ setupTypeInfo: function(test, maskPattern) {
811
+ var data = this.errorCorrectLevel << 3 | maskPattern;
812
+ var bits = QRUtil.getBCHTypeInfo(data);
813
+ var mod;
814
+ for (var v = 0;v < 15; v++) {
815
+ mod = !test && (bits >> v & 1) === 1;
816
+ if (v < 6) {
817
+ this.modules[v][8] = mod;
818
+ } else if (v < 8) {
819
+ this.modules[v + 1][8] = mod;
820
+ } else {
821
+ this.modules[this.moduleCount - 15 + v][8] = mod;
822
+ }
823
+ }
824
+ for (var h = 0;h < 15; h++) {
825
+ mod = !test && (bits >> h & 1) === 1;
826
+ if (h < 8) {
827
+ this.modules[8][this.moduleCount - h - 1] = mod;
828
+ } else if (h < 9) {
829
+ this.modules[8][15 - h - 1 + 1] = mod;
830
+ } else {
831
+ this.modules[8][15 - h - 1] = mod;
832
+ }
833
+ }
834
+ this.modules[this.moduleCount - 8][8] = !test;
835
+ },
836
+ mapData: function(data, maskPattern) {
837
+ var inc = -1;
838
+ var row = this.moduleCount - 1;
839
+ var bitIndex = 7;
840
+ var byteIndex = 0;
841
+ for (var col = this.moduleCount - 1;col > 0; col -= 2) {
842
+ if (col === 6)
843
+ col--;
844
+ while (true) {
845
+ for (var c2 = 0;c2 < 2; c2++) {
846
+ if (this.modules[row][col - c2] === null) {
847
+ var dark = false;
848
+ if (byteIndex < data.length) {
849
+ dark = (data[byteIndex] >>> bitIndex & 1) === 1;
850
+ }
851
+ var mask = QRUtil.getMask(maskPattern, row, col - c2);
852
+ if (mask) {
853
+ dark = !dark;
854
+ }
855
+ this.modules[row][col - c2] = dark;
856
+ bitIndex--;
857
+ if (bitIndex === -1) {
858
+ byteIndex++;
859
+ bitIndex = 7;
860
+ }
861
+ }
862
+ }
863
+ row += inc;
864
+ if (row < 0 || this.moduleCount <= row) {
865
+ row -= inc;
866
+ inc = -inc;
867
+ break;
868
+ }
869
+ }
870
+ }
871
+ }
872
+ };
873
+ QRCode.PAD0 = 236;
874
+ QRCode.PAD1 = 17;
875
+ QRCode.createData = function(typeNumber, errorCorrectLevel, dataList) {
876
+ var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);
877
+ var buffer = new QRBitBuffer;
878
+ for (var i = 0;i < dataList.length; i++) {
879
+ var data = dataList[i];
880
+ buffer.put(data.mode, 4);
881
+ buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber));
882
+ data.write(buffer);
883
+ }
884
+ var totalDataCount = 0;
885
+ for (var x = 0;x < rsBlocks.length; x++) {
886
+ totalDataCount += rsBlocks[x].dataCount;
887
+ }
888
+ if (buffer.getLengthInBits() > totalDataCount * 8) {
889
+ throw new Error("code length overflow. (" + buffer.getLengthInBits() + ">" + totalDataCount * 8 + ")");
890
+ }
891
+ if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
892
+ buffer.put(0, 4);
893
+ }
894
+ while (buffer.getLengthInBits() % 8 !== 0) {
895
+ buffer.putBit(false);
896
+ }
897
+ while (true) {
898
+ if (buffer.getLengthInBits() >= totalDataCount * 8) {
899
+ break;
900
+ }
901
+ buffer.put(QRCode.PAD0, 8);
902
+ if (buffer.getLengthInBits() >= totalDataCount * 8) {
903
+ break;
904
+ }
905
+ buffer.put(QRCode.PAD1, 8);
906
+ }
907
+ return QRCode.createBytes(buffer, rsBlocks);
908
+ };
909
+ QRCode.createBytes = function(buffer, rsBlocks) {
910
+ var offset = 0;
911
+ var maxDcCount = 0;
912
+ var maxEcCount = 0;
913
+ var dcdata = new Array(rsBlocks.length);
914
+ var ecdata = new Array(rsBlocks.length);
915
+ for (var r = 0;r < rsBlocks.length; r++) {
916
+ var dcCount = rsBlocks[r].dataCount;
917
+ var ecCount = rsBlocks[r].totalCount - dcCount;
918
+ maxDcCount = Math.max(maxDcCount, dcCount);
919
+ maxEcCount = Math.max(maxEcCount, ecCount);
920
+ dcdata[r] = new Array(dcCount);
921
+ for (var i = 0;i < dcdata[r].length; i++) {
922
+ dcdata[r][i] = 255 & buffer.buffer[i + offset];
923
+ }
924
+ offset += dcCount;
925
+ var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
926
+ var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);
927
+ var modPoly = rawPoly.mod(rsPoly);
928
+ ecdata[r] = new Array(rsPoly.getLength() - 1);
929
+ for (var x = 0;x < ecdata[r].length; x++) {
930
+ var modIndex = x + modPoly.getLength() - ecdata[r].length;
931
+ ecdata[r][x] = modIndex >= 0 ? modPoly.get(modIndex) : 0;
932
+ }
933
+ }
934
+ var totalCodeCount = 0;
935
+ for (var y = 0;y < rsBlocks.length; y++) {
936
+ totalCodeCount += rsBlocks[y].totalCount;
937
+ }
938
+ var data = new Array(totalCodeCount);
939
+ var index = 0;
940
+ for (var z = 0;z < maxDcCount; z++) {
941
+ for (var s = 0;s < rsBlocks.length; s++) {
942
+ if (z < dcdata[s].length) {
943
+ data[index++] = dcdata[s][z];
944
+ }
945
+ }
946
+ }
947
+ for (var xx = 0;xx < maxEcCount; xx++) {
948
+ for (var t = 0;t < rsBlocks.length; t++) {
949
+ if (xx < ecdata[t].length) {
950
+ data[index++] = ecdata[t][xx];
951
+ }
952
+ }
953
+ }
954
+ return data;
955
+ };
956
+ module.exports = QRCode;
957
+ });
958
+
959
+ // ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/lib/main.js
960
+ var require_main = __commonJS((exports, module) => {
961
+ var QRCode = require_QRCode();
962
+ var QRErrorCorrectLevel = require_QRErrorCorrectLevel();
963
+ var black = "\x1B[40m \x1B[0m";
964
+ var white = "\x1B[47m \x1B[0m";
965
+ var toCell = function(isBlack) {
966
+ return isBlack ? black : white;
967
+ };
968
+ var repeat = function(color) {
969
+ return {
970
+ times: function(count) {
971
+ return new Array(count).join(color);
972
+ }
973
+ };
974
+ };
975
+ var fill = function(length, value) {
976
+ var arr = new Array(length);
977
+ for (var i = 0;i < length; i++) {
978
+ arr[i] = value;
979
+ }
980
+ return arr;
981
+ };
982
+ module.exports = {
983
+ error: QRErrorCorrectLevel.L,
984
+ generate: function(input, opts, cb) {
985
+ if (typeof opts === "function") {
986
+ cb = opts;
987
+ opts = {};
988
+ }
989
+ var qrcode = new QRCode(-1, this.error);
990
+ qrcode.addData(input);
991
+ qrcode.make();
992
+ var output = "";
993
+ if (opts && opts.small) {
994
+ var BLACK = true, WHITE = false;
995
+ var moduleCount = qrcode.getModuleCount();
996
+ var moduleData = qrcode.modules.slice();
997
+ var oddRow = moduleCount % 2 === 1;
998
+ if (oddRow) {
999
+ moduleData.push(fill(moduleCount, WHITE));
1000
+ }
1001
+ var platte = {
1002
+ WHITE_ALL: "█",
1003
+ WHITE_BLACK: "▀",
1004
+ BLACK_WHITE: "▄",
1005
+ BLACK_ALL: " "
1006
+ };
1007
+ var borderTop = repeat(platte.BLACK_WHITE).times(moduleCount + 3);
1008
+ var borderBottom = repeat(platte.WHITE_BLACK).times(moduleCount + 3);
1009
+ output += borderTop + `
1010
+ `;
1011
+ for (var row = 0;row < moduleCount; row += 2) {
1012
+ output += platte.WHITE_ALL;
1013
+ for (var col = 0;col < moduleCount; col++) {
1014
+ if (moduleData[row][col] === WHITE && moduleData[row + 1][col] === WHITE) {
1015
+ output += platte.WHITE_ALL;
1016
+ } else if (moduleData[row][col] === WHITE && moduleData[row + 1][col] === BLACK) {
1017
+ output += platte.WHITE_BLACK;
1018
+ } else if (moduleData[row][col] === BLACK && moduleData[row + 1][col] === WHITE) {
1019
+ output += platte.BLACK_WHITE;
1020
+ } else {
1021
+ output += platte.BLACK_ALL;
1022
+ }
1023
+ }
1024
+ output += platte.WHITE_ALL + `
1025
+ `;
1026
+ }
1027
+ if (!oddRow) {
1028
+ output += borderBottom;
1029
+ }
1030
+ } else {
1031
+ var border = repeat(white).times(qrcode.getModuleCount() + 3);
1032
+ output += border + `
1033
+ `;
1034
+ qrcode.modules.forEach(function(row2) {
1035
+ output += white;
1036
+ output += row2.map(toCell).join("");
1037
+ output += white + `
1038
+ `;
1039
+ });
1040
+ output += border;
1041
+ }
1042
+ if (cb)
1043
+ cb(output);
1044
+ else
1045
+ console.log(output);
1046
+ },
1047
+ setErrorLevel: function(error) {
1048
+ this.error = QRErrorCorrectLevel[error] || this.error;
1049
+ }
1050
+ };
1051
+ });
1052
+ // package.json
1053
+ var package_default = {
1054
+ name: "lecodes-cli",
1055
+ version: "0.6.3",
1056
+ description: "Clone, edit and push LeCodes projects from your machine.",
1057
+ type: "module",
1058
+ license: "MIT",
1059
+ keywords: [
1060
+ "lecodes",
1061
+ "cli"
1062
+ ],
1063
+ bin: {
1064
+ lecodes: "dist/index.js"
1065
+ },
1066
+ files: [
1067
+ "dist",
1068
+ "runtime",
1069
+ "README.md"
1070
+ ],
1071
+ engines: {
1072
+ node: ">=18"
1073
+ },
1074
+ scripts: {
1075
+ icon: "bun scripts/make-icon.ts",
1076
+ "vendor-runtime": "bun scripts/vendor-runtime.ts",
1077
+ "sync-peers": "bun scripts/sync-peer-versions.ts",
1078
+ build: 'bun scripts/vendor-runtime.ts && bun build src/index.ts --target=node --outfile dist/index.js --external @letary/chisel --external lecodes-renderer --external lecodes-design --banner="#!/usr/bin/env node"',
1079
+ "build:bin": "bun scripts/build-bin.ts",
1080
+ typecheck: "tsc --noEmit",
1081
+ dev: "bun run src/index.ts",
1082
+ prepublishOnly: "bun run sync-peers && bun run build"
1083
+ },
1084
+ dependencies: {
1085
+ "@letary/chisel": "^0.2.0"
1086
+ },
1087
+ peerDependencies: {
1088
+ "lecodes-design": "^0.3.1",
1089
+ "lecodes-renderer": "^0.5.0"
1090
+ },
1091
+ peerDependenciesMeta: {
1092
+ "lecodes-design": {
1093
+ optional: true
1094
+ },
1095
+ "lecodes-renderer": {
1096
+ optional: true
1097
+ }
1098
+ },
1099
+ devDependencies: {
1100
+ "lecodes-design": "workspace:*",
1101
+ "lecodes-renderer": "^0.4.1",
1102
+ "qrcode-terminal": "^0.12.0",
1103
+ sdk: "workspace:*"
1104
+ }
1105
+ };
9
1106
 
10
1107
  // src/commands/clone.ts
11
- import { existsSync as existsSync4, mkdirSync as mkdirSync5, readdirSync } from "node:fs";
1108
+ import { existsSync as existsSync5, mkdirSync as mkdirSync5, readdirSync } from "node:fs";
12
1109
  import { resolve } from "node:path";
13
1110
 
14
1111
  // src/util.ts
@@ -285,9 +1382,25 @@ var materializeAssets = async (root, project, apiUrl, token, onProgress) => {
285
1382
  };
286
1383
 
287
1384
  // src/types.ts
1385
+ import { dirname as dirname4, join as join5 } from "node:path";
1386
+ import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
1387
+
1388
+ // src/distRoot.ts
1389
+ import { existsSync as existsSync2 } from "node:fs";
288
1390
  import { dirname as dirname3, join as join4 } from "node:path";
289
1391
  import { fileURLToPath } from "node:url";
290
- import { existsSync as existsSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
1392
+ var distRoot = (() => {
1393
+ const candidates = [
1394
+ join4(dirname3(fileURLToPath(import.meta.url)), ".."),
1395
+ dirname3(process.execPath)
1396
+ ];
1397
+ for (const c2 of candidates)
1398
+ if (existsSync2(join4(c2, "runtime")))
1399
+ return c2;
1400
+ return candidates[0];
1401
+ })();
1402
+
1403
+ // src/types.ts
291
1404
  var TSCONFIG = {
292
1405
  compilerOptions: {
293
1406
  target: "ES2020",
@@ -305,10 +1418,10 @@ var TSCONFIG = {
305
1418
  exclude: ["node_modules"]
306
1419
  };
307
1420
  var writeTypeBundle = (root, bundle) => {
308
- const typesDir = join4(root, LECODES_DIR, "types");
1421
+ const typesDir = join5(root, LECODES_DIR, "types");
309
1422
  for (const [path, text] of Object.entries(bundle)) {
310
- const dest = join4(typesDir, path);
311
- mkdirSync4(dirname3(dest), { recursive: true });
1423
+ const dest = join5(typesDir, path);
1424
+ mkdirSync4(dirname4(dest), { recursive: true });
312
1425
  writeFileSync4(dest, text);
313
1426
  }
314
1427
  };
@@ -316,13 +1429,12 @@ var materializeTypes = async (root, apiUrl) => {
316
1429
  writeTypeBundle(root, await getTypes(apiUrl));
317
1430
  };
318
1431
  var readLocalTypeBundle = () => {
319
- const here = dirname3(fileURLToPath(import.meta.url));
320
1432
  const candidates = [
321
- join4(here, "..", "runtime", "sdk-types.json"),
322
- join4(here, "..", "..", "sdk", "dist", "types.json")
1433
+ join5(distRoot, "runtime", "sdk-types.json"),
1434
+ join5(distRoot, "..", "sdk", "dist", "types.json")
323
1435
  ];
324
1436
  for (const path of candidates) {
325
- if (existsSync2(path)) {
1437
+ if (existsSync3(path)) {
326
1438
  try {
327
1439
  return JSON.parse(readFileSync3(path, "utf8"));
328
1440
  } catch {}
@@ -338,12 +1450,12 @@ var materializeTypesLocal = (root) => {
338
1450
  return true;
339
1451
  };
340
1452
  var writeTsconfig = (root) => {
341
- writeFileSync4(join4(root, "tsconfig.json"), JSON.stringify(TSCONFIG, null, 2));
1453
+ writeFileSync4(join5(root, "tsconfig.json"), JSON.stringify(TSCONFIG, null, 2));
342
1454
  };
343
1455
 
344
1456
  // src/ignore.ts
345
- import { existsSync as existsSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "node:fs";
346
- import { join as join5 } from "node:path";
1457
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "node:fs";
1458
+ import { join as join6 } from "node:path";
347
1459
  var BUILTIN_PATTERNS = ["node_modules/", ".git/", "_*", "*.log", ".DS_Store"];
348
1460
  var DEFAULT_IGNORE_FILE = `# Files matched here are NOT pushed to LeCodes.
349
1461
  # Only untracked files are affected — assets already in the project stay tracked
@@ -419,8 +1531,8 @@ var buildMatcher = (rules) => (key, isDir) => {
419
1531
  };
420
1532
  var loadIgnore = (root) => {
421
1533
  const lines = [...BUILTIN_PATTERNS];
422
- const file = join5(root, ".lecodesignore");
423
- if (existsSync3(file)) {
1534
+ const file = join6(root, ".lecodesignore");
1535
+ if (existsSync4(file)) {
424
1536
  try {
425
1537
  lines.push(...readFileSync4(file, "utf8").split(/\r?\n/));
426
1538
  } catch {}
@@ -429,8 +1541,8 @@ var loadIgnore = (root) => {
429
1541
  return buildMatcher(rules);
430
1542
  };
431
1543
  var writeDefaultIgnore = (root) => {
432
- const file = join5(root, ".lecodesignore");
433
- if (!existsSync3(file))
1544
+ const file = join6(root, ".lecodesignore");
1545
+ if (!existsSync4(file))
434
1546
  writeFileSync5(file, DEFAULT_IGNORE_FILE);
435
1547
  };
436
1548
 
@@ -453,7 +1565,7 @@ var clone = async (args) => {
453
1565
  const project = await getProject(apiUrl, token, uuid);
454
1566
  const dir = args._[1] ?? slug(project.name);
455
1567
  const root = resolve(dir);
456
- if (existsSync4(root) && readdirSync(root).length > 0) {
1568
+ if (existsSync5(root) && readdirSync(root).length > 0) {
457
1569
  throw new CliError(`Directory "${dir}" already exists and is not empty.`);
458
1570
  }
459
1571
  mkdirSync5(root, { recursive: true });
@@ -473,8 +1585,8 @@ var clone = async (args) => {
473
1585
  };
474
1586
 
475
1587
  // src/commands/create.ts
476
- import { existsSync as existsSync5, mkdirSync as mkdirSync6, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "node:fs";
477
- import { join as join6, resolve as resolve2 } from "node:path";
1588
+ import { existsSync as existsSync6, mkdirSync as mkdirSync6, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "node:fs";
1589
+ import { join as join7, resolve as resolve2 } from "node:path";
478
1590
  var slug2 = (name) => name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
479
1591
  var STARTER = `// Welcome to le.codes — no imports needed, the whole SDK is available as globals.
480
1592
  const scene = new Scene2D({ background: '#10131a' })
@@ -493,7 +1605,7 @@ var create = async (args) => {
493
1605
  throw new CliError("Usage: lecodes create <name> [directory]");
494
1606
  const dir = args._[1] ?? slug2(name);
495
1607
  const root = resolve2(dir);
496
- if (existsSync5(root) && readdirSync2(root).length > 0) {
1608
+ if (existsSync6(root) && readdirSync2(root).length > 0) {
497
1609
  throw new CliError(`Directory "${dir}" already exists and is not empty.`);
498
1610
  }
499
1611
  mkdirSync6(root, { recursive: true });
@@ -503,7 +1615,7 @@ var create = async (args) => {
503
1615
  await materializeTypes(root, apiUrl);
504
1616
  writeTsconfig(root);
505
1617
  writeDefaultIgnore(root);
506
- writeFileSync6(join6(root, "main.ts"), STARTER);
1618
+ writeFileSync6(join7(root, "main.ts"), STARTER);
507
1619
  const commits = await getCommits(apiUrl, token, uuid);
508
1620
  writeManifest(root, { uuid, apiUrl, name, baseHeadId: commits.headId, files: {} });
509
1621
  success(`Created ${c.bold(name)} in ${c.bold(dir)}/`);
@@ -511,8 +1623,8 @@ var create = async (args) => {
511
1623
  };
512
1624
 
513
1625
  // src/commands/design.ts
514
- import { existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync9, readdirSync as readdirSync5, writeFileSync as writeFileSync8, appendFileSync } from "node:fs";
515
- import { basename as basename2, join as join11 } from "node:path";
1626
+ import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync9, readdirSync as readdirSync5, writeFileSync as writeFileSync8, appendFileSync } from "node:fs";
1627
+ import { basename as basename2, join as join12 } from "node:path";
516
1628
 
517
1629
  // src/compile/designCompile.ts
518
1630
  import { readFileSync as readFileSync8 } from "node:fs";
@@ -520,7 +1632,7 @@ import { basename } from "node:path";
520
1632
 
521
1633
  // src/localFiles.ts
522
1634
  import { readdirSync as readdirSync3, readFileSync as readFileSync5 } from "node:fs";
523
- import { join as join7, relative } from "node:path";
1635
+ import { join as join8, relative } from "node:path";
524
1636
  var SKIP_DIRS = new Set([".lecodes", ".git", "node_modules"]);
525
1637
  var SKIP_FILES = new Set(["tsconfig.json", "jsconfig.json", ".gitignore", ".lecodesignore", ".DS_Store"]);
526
1638
  var TEXT_EXTS = new Set([
@@ -574,7 +1686,7 @@ var scanLocal = (root, opts = {}) => {
574
1686
  };
575
1687
  const walk = (dir, parentIgnored) => {
576
1688
  for (const entry of readdirSync3(dir, { withFileTypes: true })) {
577
- const abs = join7(dir, entry.name);
1689
+ const abs = join8(dir, entry.name);
578
1690
  const key = toKey(relative(root, abs));
579
1691
  if (entry.isDirectory()) {
580
1692
  if (SKIP_DIRS.has(entry.name))
@@ -628,15 +1740,14 @@ var diffLocal = (manifest, scan) => {
628
1740
  var hasChanges = (diff) => diff.added.length + diff.modified.length + diff.deleted.length > 0;
629
1741
 
630
1742
  // src/compile/projectCompile.ts
631
- import { existsSync as existsSync7 } from "node:fs";
1743
+ import { existsSync as existsSync8 } from "node:fs";
632
1744
  import { createRequire as createRequire3 } from "node:module";
633
- import { dirname as dirname5, join as join9 } from "node:path";
634
- import { fileURLToPath as fileURLToPath3 } from "node:url";
1745
+ import { dirname as dirname6, join as join10 } from "node:path";
635
1746
  // ../sdk/src/compile/bundler.ts
636
- import { existsSync as existsSync6, readdirSync as readdirSync4, readFileSync as readFileSync6, statSync } from "node:fs";
1747
+ import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync6, statSync } from "node:fs";
637
1748
  import { spawnSync } from "node:child_process";
638
1749
  import { createRequire as createRequire2 } from "node:module";
639
- import { dirname as dirname4, join as join8, relative as relative2 } from "node:path";
1750
+ import { dirname as dirname5, join as join9, relative as relative2 } from "node:path";
640
1751
  import { fileURLToPath as fileURLToPath2 } from "node:url";
641
1752
 
642
1753
  // ../sdk/src/compile/assetMacro.ts
@@ -697,11 +1808,11 @@ var classBodyDeclaresAspect = (src, from) => {
697
1808
  // ../sdk/src/compile/bundler.ts
698
1809
  var resolveInject = () => {
699
1810
  const override = process.env.LECODES_SDK_INJECT;
700
- if (override && existsSync6(override))
1811
+ if (override && existsSync7(override))
701
1812
  return override;
702
- const here = dirname4(fileURLToPath2(import.meta.url));
703
- const src = join8(here, "..", "inject.ts");
704
- if (existsSync6(src))
1813
+ const here = dirname5(fileURLToPath2(import.meta.url));
1814
+ const src = join9(here, "..", "inject.ts");
1815
+ if (existsSync7(src))
705
1816
  return src;
706
1817
  throw new Error("sdk: could not locate the inject source (LECODES_SDK_INJECT / src/inject.ts)");
707
1818
  };
@@ -724,7 +1835,7 @@ var resolveSdkSrc = () => {
724
1835
  if (!inject.endsWith("inject.ts")) {
725
1836
  throw new Error(`chisel needs the SDK source tree (src/inject.ts); got ${inject}. Set LECODES_SDK_INJECT to the source.`);
726
1837
  }
727
- return dirname4(inject);
1838
+ return dirname5(inject);
728
1839
  };
729
1840
  var isSdkSource = (path) => /\.[tj]s$/.test(path) && !path.endsWith(".d.ts") && !path.includes("/compile/") && !path.includes("/tests/") && !path.includes("/__tests__/");
730
1841
  var sdkFilesCache = new Map;
@@ -735,7 +1846,7 @@ var collectSdkFiles = (sdkSrc) => {
735
1846
  const out = {};
736
1847
  const walk = (dir) => {
737
1848
  for (const name of readdirSync4(dir)) {
738
- const p = join8(dir, name);
1849
+ const p = join9(dir, name);
739
1850
  if (statSync(p).isDirectory())
740
1851
  walk(p);
741
1852
  else if (isSdkSource(p))
@@ -749,13 +1860,13 @@ var collectSdkFiles = (sdkSrc) => {
749
1860
  var resolveChiselBin = () => {
750
1861
  const override = process.env.LECODES_CHISEL_BIN;
751
1862
  if (override) {
752
- if (existsSync6(override))
1863
+ if (existsSync7(override))
753
1864
  return override;
754
1865
  throw new Error(`LECODES_CHISEL_BIN points at a missing file: ${override}`);
755
1866
  }
756
1867
  try {
757
1868
  const bin = createRequire2(import.meta.url)("@letary/chisel").binaryPath();
758
- if (bin && existsSync6(bin))
1869
+ if (bin && existsSync7(bin))
759
1870
  return bin;
760
1871
  } catch {}
761
1872
  throw new Error("chisel binary not found. Install @letary/chisel, or set LECODES_CHISEL_BIN.");
@@ -915,18 +2026,19 @@ ${inlineSourceMapComment(map)}` : code;
915
2026
  ${inlineSourceMapComment(offsetSourceMap(map, headerLines))}`;
916
2027
  };
917
2028
  // src/compile/projectCompile.ts
918
- var here = dirname5(fileURLToPath3(import.meta.url));
919
- var vendoredInject = join9(here, "..", "runtime", "sdk", "inject.ts");
920
- if (existsSync7(vendoredInject))
2029
+ var vendoredInject = join10(distRoot, "runtime", "sdk", "inject.ts");
2030
+ if (existsSync8(vendoredInject))
921
2031
  process.env.LECODES_SDK_INJECT = vendoredInject;
922
2032
  var resolveChiselBin2 = () => {
923
2033
  if (process.env.LECODES_CHISEL_BIN)
924
2034
  return process.env.LECODES_CHISEL_BIN;
925
2035
  try {
926
- return createRequire3(import.meta.url)("@letary/chisel").binaryPath() ?? null;
927
- } catch {
928
- return null;
929
- }
2036
+ const fromPkg = createRequire3(import.meta.url)("@letary/chisel").binaryPath();
2037
+ if (fromPkg)
2038
+ return fromPkg;
2039
+ } catch {}
2040
+ const local = join10(dirname6(process.execPath), process.platform === "win32" ? "chisel.exe" : "chisel");
2041
+ return existsSync8(local) ? local : null;
930
2042
  };
931
2043
  var chiselBin = resolveChiselBin2();
932
2044
  if (chiselBin)
@@ -943,8 +2055,8 @@ var compileProject2 = (opts) => compileProject({
943
2055
 
944
2056
  // src/compile/assetIcons.ts
945
2057
  import { createHash as createHash2 } from "node:crypto";
946
- import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "node:fs";
947
- import { dirname as dirname6, join as join10 } from "node:path";
2058
+ import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "node:fs";
2059
+ import { dirname as dirname7, join as join11 } from "node:path";
948
2060
  var DEFAULT_REGISTRY = process.env.LECODES_ICON_REGISTRY?.trim() || "https://icon-registry.jt3.ru/v1";
949
2061
  var ICON_ID = /^([a-z0-9]+(?:-[a-z0-9]+)*):([a-z0-9]+(?:-[a-z0-9]+)*)$/;
950
2062
  var isIdentStart = (c2) => c2 >= "A" && c2 <= "Z" || c2 >= "a" && c2 <= "z" || c2 === "_" || c2 === "$";
@@ -1196,7 +2308,7 @@ var readText = async (registry, rel) => {
1196
2308
  return res.ok ? await res.text() : null;
1197
2309
  }
1198
2310
  try {
1199
- return readFileSync7(join10(registry, rel), "utf8");
2311
+ return readFileSync7(join11(registry, rel), "utf8");
1200
2312
  } catch {
1201
2313
  return null;
1202
2314
  }
@@ -1240,7 +2352,7 @@ var renderNotices = (lock) => {
1240
2352
  `);
1241
2353
  };
1242
2354
  var writeIfChanged = (path, text) => {
1243
- const current = existsSync8(path) ? safeRead(path) : null;
2355
+ const current = existsSync9(path) ? safeRead(path) : null;
1244
2356
  if (current === text)
1245
2357
  return false;
1246
2358
  writeFileSync7(path, text);
@@ -1255,13 +2367,13 @@ var safeRead = (path) => {
1255
2367
  };
1256
2368
  var vendorIcons = async (designDir, ids, opts = {}) => {
1257
2369
  const registry = (opts.registry ?? DEFAULT_REGISTRY).replace(/\/+$/, "");
1258
- const lockPath = join10(designDir, "icons.lock.json");
2370
+ const lockPath = join11(designDir, "icons.lock.json");
1259
2371
  const lock = readLock(lockPath) ?? { registry, icons: {}, packs: {} };
1260
2372
  const svgById = new Map;
1261
2373
  const missing = [];
1262
2374
  for (const id of ids) {
1263
2375
  const li = lock.icons[id];
1264
- const cached = li && safeRead(join10(designDir, li.path));
2376
+ const cached = li && safeRead(join11(designDir, li.path));
1265
2377
  if (cached)
1266
2378
  svgById.set(id, cached);
1267
2379
  else
@@ -1282,8 +2394,8 @@ var vendorIcons = async (designDir, ids, opts = {}) => {
1282
2394
  if (svg === null)
1283
2395
  throw new Error(`assetIcon: icon "${id}" not found in the registry. Check the name at ${registryHome(registry)}.`);
1284
2396
  const rel = `assets/icons/${pack}/${name}.svg`;
1285
- const abs = join10(designDir, rel);
1286
- mkdirSync7(dirname6(abs), { recursive: true });
2397
+ const abs = join11(designDir, rel);
2398
+ mkdirSync7(dirname7(abs), { recursive: true });
1287
2399
  writeFileSync7(abs, svg);
1288
2400
  lock.icons[id] = { pack, name, hash: sha12(svg), spdx: lic.spdx, path: rel };
1289
2401
  lock.packs[pack] = { spdx: lic.spdx, title: lic.title, url: lic.url, attribution: lic.attribution, modifiedNotice: lic.modifiedNotice };
@@ -1293,7 +2405,7 @@ var vendorIcons = async (designDir, ids, opts = {}) => {
1293
2405
  writeIfChanged(lockPath, serializeLock(lock));
1294
2406
  }
1295
2407
  if (Object.keys(lock.icons).length)
1296
- writeIfChanged(join10(designDir, "ICON-NOTICES.md"), renderNotices(lock));
2408
+ writeIfChanged(join11(designDir, "ICON-NOTICES.md"), renderNotices(lock));
1297
2409
  return svgById;
1298
2410
  };
1299
2411
  var applyAssetIcons = async (entries, designDir, opts = {}) => {
@@ -1737,29 +2849,29 @@ var getContext = (args) => {
1737
2849
  manifest,
1738
2850
  name: manifest?.name ?? basename2(root),
1739
2851
  dirName,
1740
- designDir: join11(root, dirName),
2852
+ designDir: join12(root, dirName),
1741
2853
  designKey: "/" + dirName.replace(/\\/g, "/")
1742
2854
  };
1743
2855
  };
1744
2856
  var listScreens = (designDir) => {
1745
- const dir = join11(designDir, "screens");
1746
- if (!existsSync9(dir))
2857
+ const dir = join12(designDir, "screens");
2858
+ if (!existsSync10(dir))
1747
2859
  return [];
1748
2860
  return readdirSync5(dir).filter((f) => f.endsWith(".ts") && !f.startsWith(".")).map((f) => f.slice(0, -3)).sort();
1749
2861
  };
1750
2862
  var readMetaSizes = (designDir) => {
1751
2863
  try {
1752
- const meta = JSON.parse(readFileSync9(join11(designDir, "meta.json"), "utf8"));
2864
+ const meta = JSON.parse(readFileSync9(join12(designDir, "meta.json"), "utf8"));
1753
2865
  return meta?.screens ?? {};
1754
2866
  } catch {
1755
2867
  return {};
1756
2868
  }
1757
2869
  };
1758
2870
  var writeMcpConfig = (ctx, port) => {
1759
- const path = join11(ctx.root, ".mcp.json");
2871
+ const path = join12(ctx.root, ".mcp.json");
1760
2872
  const entry = { type: "http", url: `http://localhost:${port}/mcp` };
1761
2873
  let config = {};
1762
- if (existsSync9(path)) {
2874
+ if (existsSync10(path)) {
1763
2875
  try {
1764
2876
  config = JSON.parse(readFileSync9(path, "utf8"));
1765
2877
  } catch {
@@ -1784,15 +2896,15 @@ var init = async (ctx, args) => {
1784
2896
  ["spec.md", SPEC_MD],
1785
2897
  ["README.md", README_HINT],
1786
2898
  ["CLAUDE.md", CLAUDE_MD],
1787
- [join11("shared", "tokens.ts"), TOKENS_TS],
1788
- [join11("screens", "home.ts"), HOME_SCREEN_TS]
2899
+ [join12("shared", "tokens.ts"), TOKENS_TS],
2900
+ [join12("screens", "home.ts"), HOME_SCREEN_TS]
1789
2901
  ];
1790
- mkdirSync8(join11(ctx.designDir, "screens"), { recursive: true });
1791
- mkdirSync8(join11(ctx.designDir, "shared"), { recursive: true });
2902
+ mkdirSync8(join12(ctx.designDir, "screens"), { recursive: true });
2903
+ mkdirSync8(join12(ctx.designDir, "shared"), { recursive: true });
1792
2904
  let created = 0;
1793
2905
  for (const [rel, content] of files) {
1794
- const abs = join11(ctx.designDir, rel);
1795
- if (existsSync9(abs)) {
2906
+ const abs = join12(ctx.designDir, rel);
2907
+ if (existsSync10(abs)) {
1796
2908
  note(`kept ${ctx.dirName}/${rel.replace(/\\/g, "/")}`);
1797
2909
  continue;
1798
2910
  }
@@ -1800,9 +2912,9 @@ var init = async (ctx, args) => {
1800
2912
  log(`${c.green("created")} ${ctx.dirName}/${rel.replace(/\\/g, "/")}`);
1801
2913
  created++;
1802
2914
  }
1803
- const ignorePath = join11(ctx.root, ".lecodesignore");
2915
+ const ignorePath = join12(ctx.root, ".lecodesignore");
1804
2916
  const ignoreLine = `${ctx.dirName}/.snapshots/`;
1805
- const current = existsSync9(ignorePath) ? readFileSync9(ignorePath, "utf8") : "";
2917
+ const current = existsSync10(ignorePath) ? readFileSync9(ignorePath, "utf8") : "";
1806
2918
  if (!current.split(/\r?\n/).includes(ignoreLine)) {
1807
2919
  appendFileSync(ignorePath, (current && !current.endsWith(`
1808
2920
  `) ? `
@@ -1813,8 +2925,8 @@ var init = async (ctx, args) => {
1813
2925
  writeMcpConfig(ctx, Number(flagStr(args, "port")) || DEFAULT_PORT);
1814
2926
  const wroteTypes = materializeTypesLocal(ctx.designDir);
1815
2927
  writeTsconfig(ctx.designDir);
1816
- mkdirSync8(join11(ctx.designDir, ".lecodes", "types"), { recursive: true });
1817
- writeFileSync8(join11(ctx.designDir, ".lecodes", "types", "design-macros.d.ts"), DESIGN_MACROS_DTS);
2928
+ mkdirSync8(join12(ctx.designDir, ".lecodes", "types"), { recursive: true });
2929
+ writeFileSync8(join12(ctx.designDir, ".lecodes", "types", "design-macros.d.ts"), DESIGN_MACROS_DTS);
1818
2930
  log(`${c.green("created")} ${ctx.dirName}/tsconfig.json${wroteTypes ? ` + ${ctx.dirName}/.lecodes/types/` : ""} (IDE types)`);
1819
2931
  if (!wroteTypes)
1820
2932
  warnErr("Bundled SDK types not found — tsconfig written, but globals may show unresolved until the CLI is rebuilt with vendored types.");
@@ -1863,7 +2975,7 @@ var snapshot = async (ctx, args) => {
1863
2975
  killer.unref?.();
1864
2976
  const png = flagBool(args, "png") || flagStr(args, "png") !== undefined;
1865
2977
  const sizes = readMetaSizes(ctx.designDir);
1866
- const outDir = flagStr(args, "out-dir") ?? join11(ctx.designDir, ".snapshots");
2978
+ const outDir = flagStr(args, "out-dir") ?? join12(ctx.designDir, ".snapshots");
1867
2979
  const single = targets.length === 1 && requested.length === 1 && !png && !allStates && !flagStr(args, "out-dir");
1868
2980
  const onConsole = flagBool(args, "logs") ? (level, text) => logErr(`${c.dim(`[${level}]`)} ${text}`) : undefined;
1869
2981
  if (!single)
@@ -1872,7 +2984,7 @@ var snapshot = async (ctx, args) => {
1872
2984
  const [width, height] = sizes[id]?.size ?? [390, 844];
1873
2985
  let plan = [{ state: undefined, bases: [id] }];
1874
2986
  if (discover) {
1875
- const { states, canonical } = discover(readFileSync9(join11(ctx.designDir, "screens", `${id}.ts`), "utf8"));
2987
+ const { states, canonical } = discover(readFileSync9(join12(ctx.designDir, "screens", `${id}.ts`), "utf8"));
1876
2988
  if (stateFlag !== undefined) {
1877
2989
  if (!states.includes(stateFlag)) {
1878
2990
  if (requested.length === 1)
@@ -1913,18 +3025,18 @@ var snapshot = async (ctx, args) => {
1913
3025
  for (const w of pngResult.warnings)
1914
3026
  warnErr(`${tag}: ${w}`);
1915
3027
  for (const base of bases) {
1916
- writeFileSync8(join11(outDir, `${base}.json`), result.text);
1917
- log(`${c.green("✓")} ${tag} → ${join11(outDir, `${base}.json`)}`);
3028
+ writeFileSync8(join12(outDir, `${base}.json`), result.text);
3029
+ log(`${c.green("✓")} ${tag} → ${join12(outDir, `${base}.json`)}`);
1918
3030
  if (pngResult) {
1919
- writeFileSync8(join11(outDir, `${base}.png`), pngResult.png);
1920
- log(`${c.green("✓")} ${tag} → ${join11(outDir, `${base}.png`)}`);
3031
+ writeFileSync8(join12(outDir, `${base}.png`), pngResult.png);
3032
+ log(`${c.green("✓")} ${tag} → ${join12(outDir, `${base}.png`)}`);
1921
3033
  }
1922
3034
  }
1923
3035
  }
1924
3036
  }
1925
3037
  };
1926
3038
  var serve = async (ctx, args) => {
1927
- if (!existsSync9(ctx.designDir)) {
3039
+ if (!existsSync10(ctx.designDir)) {
1928
3040
  throw new CliError(`No ${ctx.dirName}/ folder here. Run "lecodes design init" to scaffold it.`);
1929
3041
  }
1930
3042
  let designServer;
@@ -1997,7 +3109,7 @@ var serve = async (ctx, args) => {
1997
3109
  openBrowser(server.url);
1998
3110
  };
1999
3111
  var arrange = async (ctx, args) => {
2000
- if (!existsSync9(ctx.designDir)) {
3112
+ if (!existsSync10(ctx.designDir)) {
2001
3113
  throw new CliError(`No ${ctx.dirName}/ folder here. Run "lecodes design init" to scaffold it.`);
2002
3114
  }
2003
3115
  let designServer;
@@ -2033,7 +3145,7 @@ var share = async (ctx, args) => {
2033
3145
  note("Turn it off with: lecodes design share --off");
2034
3146
  };
2035
3147
  var check = async (ctx, args) => {
2036
- if (!existsSync9(ctx.designDir)) {
3148
+ if (!existsSync10(ctx.designDir)) {
2037
3149
  throw new CliError(`No ${ctx.dirName}/ folder here. Run "lecodes design init" to scaffold it.`);
2038
3150
  }
2039
3151
  let designServer;
@@ -2097,9 +3209,605 @@ var design = async (args) => {
2097
3209
  throw new CliError(`Unknown design subcommand "${sub}" (serve | init | snapshot | arrange | check | share).`);
2098
3210
  };
2099
3211
 
2100
- // src/serverDiff.ts
3212
+ // src/commands/dev.ts
3213
+ import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync11, watch, writeFileSync as writeFileSync9 } from "node:fs";
3214
+ import { randomBytes as randomBytes2 } from "node:crypto";
3215
+ import { networkInterfaces } from "node:os";
3216
+ import { basename as basename4, join as join14 } from "node:path";
3217
+
3218
+ // src/dev/devServer.ts
3219
+ import { createServer as createServer2 } from "node:http";
3220
+ import { createReadStream, existsSync as existsSync11, statSync as statSync2 } from "node:fs";
3221
+ import { join as join13 } from "node:path";
3222
+
3223
+ // src/compile/collectLocal.ts
2101
3224
  import { readFileSync as readFileSync10 } from "node:fs";
2102
3225
  import { basename as basename3 } from "node:path";
3226
+ var collectLocalEntries = (root, manifest, assetBase) => {
3227
+ const entries = [];
3228
+ const warnings = [];
3229
+ const scan = scanLocal(root, { ignore: loadIgnore(root), tracked: new Set(Object.keys(manifest?.files ?? {})) });
3230
+ for (const file of scan.files) {
3231
+ const type = manifest?.files[file.path]?.type ?? classifyKind(basename3(file.path));
3232
+ if (type === "resource") {
3233
+ entries.push({ path: file.path, type, fileSrc: assetBase + encodeURI(file.path), absPath: file.absPath });
3234
+ } else if (type === "shader") {
3235
+ warnings.push(`${file.path}: shaders compile on the server — push the project once for shaders to work in dev mode.`);
3236
+ entries.push({ path: file.path, type, shaders: [] });
3237
+ } else {
3238
+ entries.push({ path: file.path, type, text: readFileSync10(file.absPath, "utf8") });
3239
+ }
3240
+ }
3241
+ return { entries, warnings };
3242
+ };
3243
+
3244
+ // src/dev/clientTemplates.ts
3245
+ var DEV_SCREEN_TS = `const title = "LeCodes Dev"
3246
+ const project = __DEV_NAME__
3247
+
3248
+ UIScreen([
3249
+ UIText(title).style({ fontSize: 30, fontWeight: 700, color: "#ffffff" }),
3250
+ UIText(project).style({ fontSize: 16, color: "#9fb0d0", mt: 8 }),
3251
+ UIText("Connected — loading the project…").style({ fontSize: 14, color: "#6b7a99", mt: 20 }),
3252
+ ]).style({ bgColor: "#11131a", justifyContent: "center", alignItems: "center", p: 40 }).open()
3253
+ `;
3254
+ var DEV_PRELUDE_JS = `
3255
+ ;(function () {
3256
+ var ORIGIN = "__DEV_ORIGIN__"
3257
+ var BASE = "__DEV_BASE__"
3258
+ var TRANSPORT = "__DEV_TRANSPORT__"
3259
+ var V = __DEV_V__
3260
+ var wsId = null
3261
+ var busy = false
3262
+ var queued = null
3263
+
3264
+ function post(obj) {
3265
+ var text = JSON.stringify(obj)
3266
+ if (TRANSPORT === "ws") {
3267
+ if (wsId !== null) { try { _creatorUtils.websocketSend(wsId, text) } catch (e) {} }
3268
+ return
3269
+ }
3270
+ try {
3271
+ _creatorUtils.fetch(ORIGIN + BASE + "/log", { method: "POST", body: text, headers: { "content-type": "application/json" } },
3272
+ function (id) { try { _creatorUtils.disposeFetch(id) } catch (e) {} }, function () {})
3273
+ } catch (e) {}
3274
+ }
3275
+
3276
+ // Mirror console output to the dev terminal ('silent' guards recursion through wrapped calls).
3277
+ var silent = false
3278
+ ;["log", "warn", "error"].forEach(function (level) {
3279
+ var orig = console[level]
3280
+ console[level] = function () {
3281
+ var parts = []
3282
+ for (var i = 0; i < arguments.length; i++) {
3283
+ var a = arguments[i]
3284
+ if (typeof a === "string") { parts.push(a); continue }
3285
+ try { parts.push(JSON.stringify(a)) } catch (e) { parts.push(String(a)) }
3286
+ }
3287
+ try { orig.apply(console, arguments) } catch (e) {}
3288
+ if (!silent) { silent = true; try { post({ type: "log", level: level, text: parts.join(" ") }) } finally { silent = false } }
3289
+ }
3290
+ })
3291
+
3292
+ function report(err) { post({ type: "error", text: String(err) }) }
3293
+
3294
+ function load(v) {
3295
+ if (busy) { queued = v; return }
3296
+ busy = true
3297
+ _creatorUtils.fetch(ORIGIN + BASE + "/bundle.js?v=" + v + "&t=" + TRANSPORT, {}, function (id, status) {
3298
+ if (status >= 400) { busy = false; report("bundle fetch: HTTP " + status); return }
3299
+ // Same engine bringup the app launcher does for a fresh launch (see desktop initial.ts).
3300
+ var head = ""
3301
+ try { head = _creatorUtils.fetchSlice(id, 0, 300) } catch (e) {}
3302
+ var preloads = head.match(/\\/\\/ preload: (.+)/)
3303
+ if (head.indexOf("// gl:") < 0) {
3304
+ try { _creator.createEngine(preloads ? preloads[1].split(";") : []) } catch (e) { report("createEngine: " + String(e)) }
3305
+ }
3306
+ _creatorUtils.run(id, function (err) {
3307
+ // Eval failed → native rollback kept THIS world (and its connection) alive. Keep serving.
3308
+ busy = false
3309
+ report(err)
3310
+ if (queued !== null) { var q = queued; queued = null; load(q) }
3311
+ })
3312
+ // On success this world is torn down next tick — the new bundle carries its own prelude.
3313
+ }, function (err) { busy = false; report("bundle fetch failed: " + String(err)) })
3314
+ }
3315
+
3316
+ function onServer(data) {
3317
+ var m = null
3318
+ try { m = JSON.parse(data) } catch (e) { return }
3319
+ if (m && m.type === "reload" && typeof m.v === "number" && m.v !== V) load(m.v)
3320
+ }
3321
+
3322
+ if (TRANSPORT === "ws") {
3323
+ var url = ORIGIN.replace("http", "ws") + BASE + "/ws"
3324
+ var open = function () {
3325
+ try {
3326
+ wsId = _creatorUtils.websocketOpen(url, function (channel, data) {
3327
+ if (channel === "open") {
3328
+ try { _creatorUtils.websocketSend(wsId, JSON.stringify({ type: "hello", v: V })) } catch (e) {}
3329
+ } else if (channel === "message") {
3330
+ onServer(data)
3331
+ } else if (channel === "close" || channel === "error") {
3332
+ wsId = null
3333
+ setTimeout(open, 2000)
3334
+ }
3335
+ })
3336
+ } catch (e) { setTimeout(open, 2000) }
3337
+ }
3338
+ open()
3339
+ } else {
3340
+ var poll = function () {
3341
+ _creatorUtils.fetch(ORIGIN + BASE + "/version", {}, function (id, status) {
3342
+ var v = null
3343
+ try { v = _creatorUtils.fetchToJson(id).v } catch (e) {}
3344
+ try { _creatorUtils.disposeFetch(id) } catch (e) {}
3345
+ setTimeout(poll, 700)
3346
+ if (status < 400 && typeof v === "number" && v !== V && !busy) load(v)
3347
+ }, function () { setTimeout(poll, 1500) })
3348
+ }
3349
+ poll()
3350
+ }
3351
+ })();
3352
+ `;
3353
+ var renderPrelude = (p) => DEV_PRELUDE_JS.split("__DEV_ORIGIN__").join(p.origin).split("__DEV_BASE__").join(p.base).split("__DEV_TRANSPORT__").join(p.transport).split("__DEV_V__").join(String(p.v));
3354
+ var renderDevScreen = (name) => DEV_SCREEN_TS.split("__DEV_NAME__").join(JSON.stringify(name));
3355
+
3356
+ // src/dev/wsServer.ts
3357
+ import { createHash as createHash3 } from "node:crypto";
3358
+ var WS_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
3359
+ var acceptKey = (key) => createHash3("sha1").update(key + WS_GUID).digest("base64");
3360
+ var encodeTextFrame = (text) => {
3361
+ const payload = Buffer.from(text, "utf8");
3362
+ const len = payload.length;
3363
+ let header;
3364
+ if (len < 126) {
3365
+ header = Buffer.from([129, len]);
3366
+ } else if (len < 65536) {
3367
+ header = Buffer.alloc(4);
3368
+ header[0] = 129;
3369
+ header[1] = 126;
3370
+ header.writeUInt16BE(len, 2);
3371
+ } else {
3372
+ header = Buffer.alloc(10);
3373
+ header[0] = 129;
3374
+ header[1] = 127;
3375
+ header.writeBigUInt64BE(BigInt(len), 2);
3376
+ }
3377
+ return Buffer.concat([header, payload]);
3378
+ };
3379
+ var encodeControlFrame = (opcode, payload = Buffer.alloc(0)) => Buffer.concat([Buffer.from([128 | opcode, Math.min(payload.length, 125)]), payload.subarray(0, 125)]);
3380
+ var OPCODE = { CONTINUATION: 0, TEXT: 1, BINARY: 2, CLOSE: 8, PING: 9, PONG: 10 };
3381
+ var createFrameDecoder = () => {
3382
+ let buf = Buffer.alloc(0);
3383
+ return (chunk) => {
3384
+ buf = buf.length === 0 ? chunk : Buffer.concat([buf, chunk]);
3385
+ const frames = [];
3386
+ while (buf.length >= 2) {
3387
+ const opcode = buf[0] & 15;
3388
+ const masked = (buf[1] & 128) !== 0;
3389
+ let len = buf[1] & 127;
3390
+ let off = 2;
3391
+ if (len === 126) {
3392
+ if (buf.length < 4)
3393
+ break;
3394
+ len = buf.readUInt16BE(2);
3395
+ off = 4;
3396
+ } else if (len === 127) {
3397
+ if (buf.length < 10)
3398
+ break;
3399
+ len = Number(buf.readBigUInt64BE(2));
3400
+ off = 10;
3401
+ }
3402
+ const maskLen = masked ? 4 : 0;
3403
+ if (buf.length < off + maskLen + len)
3404
+ break;
3405
+ let payload = buf.subarray(off + maskLen, off + maskLen + len);
3406
+ if (masked) {
3407
+ const mask = buf.subarray(off, off + 4);
3408
+ const unmasked = Buffer.alloc(len);
3409
+ for (let i = 0;i < len; i++)
3410
+ unmasked[i] = payload[i] ^ mask[i & 3];
3411
+ payload = unmasked;
3412
+ }
3413
+ buf = buf.subarray(off + maskLen + len);
3414
+ frames.push({ opcode, payload });
3415
+ }
3416
+ return frames;
3417
+ };
3418
+ };
3419
+ var upgradeWebSocket = (req, socket, handlers) => {
3420
+ const key = req.headers["sec-websocket-key"];
3421
+ if (typeof key !== "string" || (req.headers.upgrade ?? "").toLowerCase() !== "websocket") {
3422
+ socket.destroy();
3423
+ return null;
3424
+ }
3425
+ socket.write(`HTTP/1.1 101 Switching Protocols\r
3426
+ ` + `Upgrade: websocket\r
3427
+ ` + `Connection: Upgrade\r
3428
+ ` + `Sec-WebSocket-Accept: ${acceptKey(key)}\r
3429
+ \r
3430
+ `);
3431
+ let open = true;
3432
+ const finish = () => {
3433
+ if (!open)
3434
+ return;
3435
+ open = false;
3436
+ handlers.onClose();
3437
+ };
3438
+ const decode = createFrameDecoder();
3439
+ socket.on("data", (chunk) => {
3440
+ for (const frame of decode(chunk)) {
3441
+ if (frame.opcode === OPCODE.TEXT) {
3442
+ handlers.onMessage(frame.payload.toString("utf8"));
3443
+ } else if (frame.opcode === OPCODE.PING) {
3444
+ if (open)
3445
+ socket.write(encodeControlFrame(OPCODE.PONG, frame.payload));
3446
+ } else if (frame.opcode === OPCODE.CLOSE) {
3447
+ if (open)
3448
+ socket.write(encodeControlFrame(OPCODE.CLOSE));
3449
+ socket.end();
3450
+ finish();
3451
+ }
3452
+ }
3453
+ });
3454
+ socket.on("close", finish);
3455
+ socket.on("error", () => socket.destroy());
3456
+ return {
3457
+ send: (text) => {
3458
+ if (open)
3459
+ socket.write(encodeTextFrame(text));
3460
+ },
3461
+ close: () => {
3462
+ if (!open)
3463
+ return;
3464
+ socket.write(encodeControlFrame(OPCODE.CLOSE));
3465
+ socket.end();
3466
+ finish();
3467
+ }
3468
+ };
3469
+ };
3470
+
3471
+ // src/dev/devServer.ts
3472
+ var MIME = {
3473
+ png: "image/png",
3474
+ jpg: "image/jpeg",
3475
+ jpeg: "image/jpeg",
3476
+ webp: "image/webp",
3477
+ gif: "image/gif",
3478
+ svg: "image/svg+xml",
3479
+ ktx: "application/octet-stream",
3480
+ ktx2: "application/octet-stream",
3481
+ glb: "model/gltf-binary",
3482
+ bin: "application/octet-stream",
3483
+ mp3: "audio/mpeg",
3484
+ wav: "audio/wav",
3485
+ ogg: "audio/ogg",
3486
+ m4a: "audio/mp4",
3487
+ mp4: "video/mp4",
3488
+ webm: "video/webm",
3489
+ ttf: "font/ttf",
3490
+ otf: "font/otf",
3491
+ woff: "font/woff",
3492
+ woff2: "font/woff2",
3493
+ json: "application/json",
3494
+ txt: "text/plain",
3495
+ js: "text/javascript"
3496
+ };
3497
+ var contentType = (path) => {
3498
+ const i = path.lastIndexOf(".");
3499
+ return i >= 0 && MIME[path.slice(i + 1).toLowerCase()] || "application/octet-stream";
3500
+ };
3501
+ var sendText = (res, status, type, body) => {
3502
+ res.writeHead(status, { "content-type": type, "cache-control": "no-store" });
3503
+ res.end(body);
3504
+ };
3505
+ var sendJson = (res, status, body) => sendText(res, status, "application/json", JSON.stringify(body));
3506
+ var readBody = (req) => new Promise((resolve3) => {
3507
+ let body = "";
3508
+ req.on("data", (c2) => {
3509
+ if (body.length < 256 * 1024)
3510
+ body += c2.toString("utf8");
3511
+ });
3512
+ req.on("end", () => resolve3(body));
3513
+ req.on("error", () => resolve3(body));
3514
+ });
3515
+ var startDevServer = async (options) => {
3516
+ const { root, manifest, name, origin, token, entryOverride } = options;
3517
+ const log2 = options.log ?? (() => {});
3518
+ const base = `/t/${token}`;
3519
+ let v = 0;
3520
+ let bundleJs = null;
3521
+ let compileChain = Promise.resolve();
3522
+ const stripVolatile = (js) => js.replace(/^\/\/ last-updated: .*$/m, "");
3523
+ const compileOnce = async () => {
3524
+ try {
3525
+ const { entries, warnings } = collectLocalEntries(root, manifest, `${base}/assets`);
3526
+ const js = await compileProject2({ entries, name, publicUrl: origin, entryOverride, header: true });
3527
+ const changed = bundleJs === null || stripVolatile(js) !== stripVolatile(bundleJs);
3528
+ if (changed) {
3529
+ bundleJs = js;
3530
+ v++;
3531
+ broadcast(JSON.stringify({ type: "reload", v }));
3532
+ }
3533
+ return { ok: true, changed, v, warnings };
3534
+ } catch (e) {
3535
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
3536
+ }
3537
+ };
3538
+ const recompile = () => {
3539
+ const run = compileChain.then(compileOnce);
3540
+ compileChain = run;
3541
+ return run;
3542
+ };
3543
+ const devJs = await compileProject2({
3544
+ entries: [{ path: "/.dev-entry.ts", type: "text", text: renderDevScreen(name) }],
3545
+ name: "LeCodes Dev",
3546
+ publicUrl: origin,
3547
+ entryOverride: "/.dev-entry.ts",
3548
+ header: true
3549
+ });
3550
+ const clients = new Set;
3551
+ const broadcast = (text) => {
3552
+ for (const client of clients)
3553
+ client.send(text);
3554
+ };
3555
+ const printDeviceMessage = (raw) => {
3556
+ let m = {};
3557
+ try {
3558
+ m = JSON.parse(raw);
3559
+ } catch {
3560
+ return;
3561
+ }
3562
+ if (m.type === "log")
3563
+ log2(`[device:${m.level ?? "log"}] ${m.text ?? ""}`);
3564
+ else if (m.type === "error")
3565
+ log2(`[device:error] ${m.text ?? ""}`);
3566
+ };
3567
+ const transportOf = (url) => url.searchParams.get("t") === "poll" ? "poll" : "ws";
3568
+ const handle = async (req, res) => {
3569
+ const url = new URL(req.url ?? "/", origin);
3570
+ const path = url.pathname;
3571
+ if (!path.startsWith(base + "/"))
3572
+ return sendText(res, 404, "text/plain", "Not found");
3573
+ const route = path.slice(base.length);
3574
+ if (route === "/dev.js") {
3575
+ return sendText(res, 200, "text/javascript", devJs + `
3576
+ ` + renderPrelude({ origin, base, transport: transportOf(url), v: -1 }));
3577
+ }
3578
+ if (route === "/bundle.js") {
3579
+ if (bundleJs === null)
3580
+ return sendText(res, 503, "text/plain", "No successful compile yet — check the terminal.");
3581
+ return sendText(res, 200, "text/javascript", bundleJs + `
3582
+ ` + renderPrelude({ origin, base, transport: transportOf(url), v }));
3583
+ }
3584
+ if (route === "/version") {
3585
+ return sendJson(res, 200, { v });
3586
+ }
3587
+ if (route === "/log" && req.method === "POST") {
3588
+ printDeviceMessage(await readBody(req));
3589
+ return sendJson(res, 200, { ok: true });
3590
+ }
3591
+ if (route.startsWith("/assets/")) {
3592
+ let key;
3593
+ try {
3594
+ key = toKey(decodeURIComponent(route.slice("/assets".length)));
3595
+ } catch {
3596
+ return sendText(res, 400, "text/plain", "Bad path");
3597
+ }
3598
+ const abs = join13(root, key.slice(1));
3599
+ if (!existsSync11(abs) || !statSync2(abs).isFile())
3600
+ return sendText(res, 404, "text/plain", "Not found");
3601
+ res.writeHead(200, { "content-type": contentType(key), "cache-control": "no-store" });
3602
+ createReadStream(abs).pipe(res);
3603
+ return;
3604
+ }
3605
+ return sendText(res, 404, "text/plain", "Not found");
3606
+ };
3607
+ const server = createServer2((req, res) => {
3608
+ handle(req, res).catch((e) => {
3609
+ log2(`request failed: ${e instanceof Error ? e.message : String(e)}`);
3610
+ if (!res.headersSent)
3611
+ sendText(res, 500, "text/plain", "Internal error");
3612
+ });
3613
+ });
3614
+ server.on("upgrade", (req, socket) => {
3615
+ const url = new URL(req.url ?? "/", origin);
3616
+ if (url.pathname !== base + "/ws") {
3617
+ socket.destroy();
3618
+ return;
3619
+ }
3620
+ const conn = upgradeWebSocket(req, socket, {
3621
+ onMessage: (text) => {
3622
+ let m = {};
3623
+ try {
3624
+ m = JSON.parse(text);
3625
+ } catch {}
3626
+ if (m.type === "hello") {
3627
+ if (typeof m.v === "number" && m.v !== v && bundleJs !== null) {
3628
+ conn?.send(JSON.stringify({ type: "reload", v }));
3629
+ }
3630
+ } else {
3631
+ printDeviceMessage(text);
3632
+ }
3633
+ },
3634
+ onClose: () => {
3635
+ if (conn)
3636
+ clients.delete(conn);
3637
+ }
3638
+ });
3639
+ if (conn)
3640
+ clients.add(conn);
3641
+ });
3642
+ await new Promise((resolve3, reject) => {
3643
+ server.once("error", reject);
3644
+ server.listen(options.port, options.bind ?? "0.0.0.0", () => {
3645
+ server.removeListener("error", reject);
3646
+ resolve3();
3647
+ });
3648
+ });
3649
+ const address = server.address();
3650
+ const boundPort = typeof address === "object" && address !== null ? address.port : options.port;
3651
+ return {
3652
+ url: `${origin}${base}/dev.js`,
3653
+ base,
3654
+ port: boundPort,
3655
+ v: () => v,
3656
+ clientCount: () => clients.size,
3657
+ recompile,
3658
+ close: () => {
3659
+ for (const client of clients)
3660
+ client.close();
3661
+ server.close();
3662
+ }
3663
+ };
3664
+ };
3665
+
3666
+ // src/commands/dev.ts
3667
+ var DEFAULT_PORT2 = 4499;
3668
+ var DEBOUNCE_MS = 120;
3669
+ var WATCH_SKIP_DIRS = new Set([".lecodes", ".git", "node_modules"]);
3670
+ var WATCH_SKIP_FILES = new Set(["tsconfig.json", "jsconfig.json", ".gitignore", ".lecodesignore", ".DS_Store"]);
3671
+ var pickLanAddress = () => {
3672
+ const nets = networkInterfaces();
3673
+ for (const name of Object.keys(nets)) {
3674
+ for (const net of nets[name] ?? []) {
3675
+ if (net.family === "IPv4" && !net.internal)
3676
+ return net.address;
3677
+ }
3678
+ }
3679
+ return null;
3680
+ };
3681
+ var loadDevToken = (root) => {
3682
+ const path = join14(root, LECODES_DIR, "dev.json");
3683
+ try {
3684
+ const token2 = JSON.parse(readFileSync11(path, "utf8")).token;
3685
+ if (token2 && /^[a-z0-9]{8,}$/.test(token2))
3686
+ return token2;
3687
+ } catch {}
3688
+ const token = randomBytes2(6).toString("hex");
3689
+ mkdirSync9(join14(root, LECODES_DIR), { recursive: true });
3690
+ writeFileSync9(path, JSON.stringify({ token }, null, 2) + `
3691
+ `);
3692
+ return token;
3693
+ };
3694
+ var printQr = async (url) => {
3695
+ try {
3696
+ const qrcode = (await Promise.resolve().then(() => __toESM(require_main(), 1))).default;
3697
+ await new Promise((resolve3) => qrcode.generate(url, { small: true }, (out) => {
3698
+ log(out);
3699
+ resolve3();
3700
+ }));
3701
+ } catch {
3702
+ note("(couldn't render a QR code — open the URL below on the device)");
3703
+ }
3704
+ };
3705
+ var dev = async (args) => {
3706
+ let root;
3707
+ let manifest;
3708
+ try {
3709
+ root = findProjectRoot(process.cwd());
3710
+ manifest = readManifest(root);
3711
+ } catch {
3712
+ root = process.cwd();
3713
+ manifest = null;
3714
+ }
3715
+ const name = manifest?.name ?? basename4(root);
3716
+ if (typeof globalThis.Bun !== "undefined") {
3717
+ 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).");
3718
+ }
3719
+ const port = Number(flagStr(args, "port")) || DEFAULT_PORT2;
3720
+ const host = flagStr(args, "host") ?? pickLanAddress();
3721
+ if (!host)
3722
+ throw new CliError("No LAN address found — pass one with --host <ip>.");
3723
+ const origin = `http://${host}:${port}`;
3724
+ const token = loadDevToken(root);
3725
+ const server = await startDevServer({
3726
+ root,
3727
+ manifest,
3728
+ name,
3729
+ origin,
3730
+ port,
3731
+ token,
3732
+ entryOverride: flagStr(args, "entry"),
3733
+ log: note
3734
+ }).catch((e) => {
3735
+ const msg = e instanceof Error ? e.message : String(e);
3736
+ throw new CliError(msg.includes("EADDRINUSE") ? `Port ${port} is already in use — pass --port <n>.` : msg);
3737
+ });
3738
+ note("Compiling…");
3739
+ const first = await server.recompile();
3740
+ if (first.ok) {
3741
+ for (const w of first.warnings)
3742
+ warnErr(w);
3743
+ } else {
3744
+ warnErr(`Compile failed — fix it and save; devices connect once it compiles:
3745
+ ${first.error}`);
3746
+ }
3747
+ const ignore = loadIgnore(root);
3748
+ const shouldRecompile = (file) => {
3749
+ if (file === null)
3750
+ return true;
3751
+ const parts = file.replace(/\\/g, "/").split("/");
3752
+ if (parts.some((p) => WATCH_SKIP_DIRS.has(p)))
3753
+ return false;
3754
+ if (WATCH_SKIP_FILES.has(parts[parts.length - 1]))
3755
+ return false;
3756
+ try {
3757
+ const key = toKey(file);
3758
+ if (ignore(key, false))
3759
+ return false;
3760
+ } catch {
3761
+ return false;
3762
+ }
3763
+ return true;
3764
+ };
3765
+ const debug = process.env.LECODES_DEV_DEBUG === "1";
3766
+ let timer = null;
3767
+ const onChange = (file) => {
3768
+ if (debug)
3769
+ note(`[watch] event: ${file ?? "(null)"} → ${shouldRecompile(file) ? "recompile" : "skip"}`);
3770
+ if (!shouldRecompile(file))
3771
+ return;
3772
+ if (timer)
3773
+ clearTimeout(timer);
3774
+ timer = setTimeout(async () => {
3775
+ timer = null;
3776
+ const result = await server.recompile();
3777
+ if (!result.ok) {
3778
+ warnErr(`Compile failed (devices keep the last good version):
3779
+ ${result.error}`);
3780
+ } else if (result.changed) {
3781
+ const devices = server.clientCount();
3782
+ note(`✓ v${result.v} → ${devices} device${devices === 1 ? "" : "s"}`);
3783
+ for (const w of result.warnings)
3784
+ warnErr(w);
3785
+ }
3786
+ }, DEBOUNCE_MS);
3787
+ };
3788
+ let watcher = null;
3789
+ try {
3790
+ watcher = watch(root, { recursive: true }, (_type, file) => onChange(file));
3791
+ } catch {
3792
+ warnErr("fs.watch(recursive) unavailable on this platform — hot reload disabled (restart to pick up changes).");
3793
+ }
3794
+ watcher?.on("error", (e) => warnErr(`Watcher error — hot reload may have stopped (restart lecodes dev): ${e.message}`));
3795
+ log("");
3796
+ log(`${c.bold("LeCodes Dev")} — ${name}`);
3797
+ log("");
3798
+ if (!flagBool(args, "no-qr"))
3799
+ await printQr(server.url);
3800
+ log(`Scan the QR with the LeCodes app, or open: ${c.bold(server.url)}`);
3801
+ note(`Older desktop builds (no websocket bridge): ${server.url}?t=poll`);
3802
+ if (existsSync12(join14(root, LECODES_DIR, "manifest.json")))
3803
+ note(`Token stored in ${LECODES_DIR}/dev.json (delete it to rotate the URL).`);
3804
+ log("");
3805
+ log(`Watching ${root} — save a file to hot-reload. Ctrl+C to stop.`);
3806
+ };
3807
+
3808
+ // src/serverDiff.ts
3809
+ import { readFileSync as readFileSync12 } from "node:fs";
3810
+ import { basename as basename5 } from "node:path";
2103
3811
  var isTextAsset = (t) => t === "text" || t === "shader" || t === "scene";
2104
3812
  var computeServerDiff = (project, scan) => {
2105
3813
  const pathById = buildPaths(project.assets);
@@ -2116,8 +3824,8 @@ var computeServerDiff = (project, scan) => {
2116
3824
  const server = serverByPath.get(path);
2117
3825
  const local = localByPath.get(path);
2118
3826
  if (local && !server) {
2119
- const text = isTextKind(classifyKind(basename3(path)));
2120
- entries.push({ path, status: "added", binary: !text, serverText: "", localText: text ? readFileSync10(local.absPath, "utf8") : undefined });
3827
+ const text = isTextKind(classifyKind(basename5(path)));
3828
+ entries.push({ path, status: "added", binary: !text, serverText: "", localText: text ? readFileSync12(local.absPath, "utf8") : undefined });
2121
3829
  continue;
2122
3830
  }
2123
3831
  if (server && !local) {
@@ -2128,12 +3836,12 @@ var computeServerDiff = (project, scan) => {
2128
3836
  if (server && local) {
2129
3837
  if (isTextAsset(server.type)) {
2130
3838
  const serverText = server.text ?? "";
2131
- const localText = readFileSync10(local.absPath, "utf8");
3839
+ const localText = readFileSync12(local.absPath, "utf8");
2132
3840
  if (serverText !== localText)
2133
3841
  entries.push({ path, status: "modified", binary: false, serverText, localText });
2134
3842
  } else {
2135
3843
  const serverSize = server.file?.size;
2136
- const localSize = Buffer.byteLength(readFileSync10(local.absPath));
3844
+ const localSize = Buffer.byteLength(readFileSync12(local.absPath));
2137
3845
  if (serverSize !== undefined && serverSize !== localSize) {
2138
3846
  entries.push({ path, status: "modified", binary: true });
2139
3847
  }
@@ -2285,6 +3993,76 @@ var diff = async (args) => {
2285
3993
  };
2286
3994
  var statusLabel = (e) => e.status === "added" ? c.green(" added ") : e.status === "removed" ? c.red(" removed ") : c.yellow(" modified ");
2287
3995
 
3996
+ // src/commands/init.ts
3997
+ import { existsSync as existsSync13, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "node:fs";
3998
+ import { basename as basename6, dirname as dirname8, join as join15, resolve as resolve3 } from "node:path";
3999
+ var STARTER2 = `// Welcome to LeCodes — the whole SDK is available as globals, no imports needed.
4000
+ // Run it live on a device with \`lecodes dev\`, or bundle it with \`lecodes preview -o app.js\`.
4001
+
4002
+ let count = 0
4003
+
4004
+ const counter = UIText({ color: "#9fb0d0", fontSize: 22, marginTop: 18 }, "Taps: 0")
4005
+
4006
+ const button = UIButton(
4007
+ {
4008
+ backgroundColor: "#4f7cff",
4009
+ borderRadius: 14,
4010
+ paddingVertical: 16,
4011
+ paddingHorizontal: 40,
4012
+ marginTop: 24,
4013
+ alignItems: "center",
4014
+ justifyContent: "center",
4015
+ },
4016
+ [UIText({ color: "#ffffff", fontSize: 22 }, "Tap me")],
4017
+ ).onClick(() => {
4018
+ count++
4019
+ counter.text = "Taps: " + count
4020
+ })
4021
+
4022
+ const screen = UIScreen(
4023
+ { backgroundColor: "#1e2230", alignItems: "center", justifyContent: "center", padding: 40 },
4024
+ [
4025
+ UIColumn(
4026
+ { backgroundColor: "#2b3550", borderRadius: 24, padding: 40, alignItems: "center" },
4027
+ [UIText({ color: "#ffffff", fontSize: 32 }, "Hello, LeCodes"), button, counter],
4028
+ ),
4029
+ ],
4030
+ )
4031
+
4032
+ screen.open()
4033
+ `;
4034
+ var init2 = async (args) => {
4035
+ const dirArg = (args._[0] ?? ".").replace(/[/\\]+$/, "") || ".";
4036
+ const root = resolve3(dirArg);
4037
+ const name = basename6(root);
4038
+ mkdirSync10(root, { recursive: true });
4039
+ let created = 0;
4040
+ const write = (rel, content) => {
4041
+ const abs = join15(root, rel);
4042
+ if (existsSync13(abs)) {
4043
+ note(`kept ${rel}`);
4044
+ return;
4045
+ }
4046
+ mkdirSync10(dirname8(abs), { recursive: true });
4047
+ writeFileSync10(abs, content);
4048
+ log(`${c.green("created")} ${rel}`);
4049
+ created++;
4050
+ };
4051
+ write("main.ts", STARTER2);
4052
+ write(".lecodesignore", DEFAULT_IGNORE_FILE);
4053
+ const wroteTypes = materializeTypesLocal(root);
4054
+ writeTsconfig(root);
4055
+ log(`${c.green("written")} tsconfig.json${wroteTypes ? ` + ${LECODES_DIR}/types/` : ""} (IDE types)`);
4056
+ if (!wroteTypes) {
4057
+ warnErr("Bundled SDK types not found — tsconfig written, but globals may show unresolved until the CLI is rebuilt with vendored types.");
4058
+ }
4059
+ log("");
4060
+ success(created ? `Local project ${c.bold(name)} ready${dirArg === "." ? "" : ` in ${c.bold(dirArg)}/`}.` : `Project ${c.bold(name)} already initialized.`);
4061
+ const cd = dirArg === "." ? "" : `cd ${dirArg} && `;
4062
+ log(` ${c.bold(cd + "lecodes dev")} run it on a device with hot reload`);
4063
+ log(` ${c.bold(cd + "lecodes preview -o app.js")} bundle it for LeCodes Desktop`);
4064
+ };
4065
+
2288
4066
  // src/commands/login.ts
2289
4067
  var login = async (args) => {
2290
4068
  const config = loadConfig();
@@ -2297,12 +4075,12 @@ var login = async (args) => {
2297
4075
  };
2298
4076
 
2299
4077
  // src/commands/preview.ts
2300
- import { writeFileSync as writeFileSync9 } from "node:fs";
2301
- import { resolve as resolve3 } from "node:path";
4078
+ import { writeFileSync as writeFileSync11 } from "node:fs";
4079
+ import { basename as basename8, resolve as resolve4 } from "node:path";
2302
4080
 
2303
4081
  // src/compile/collect.ts
2304
- import { readFileSync as readFileSync11 } from "node:fs";
2305
- import { basename as basename4 } from "node:path";
4082
+ import { readFileSync as readFileSync13 } from "node:fs";
4083
+ import { basename as basename7 } from "node:path";
2306
4084
  var collectEntries = (root, manifest, project) => {
2307
4085
  const pathById = buildPaths(project.assets);
2308
4086
  const serverByPath = new Map;
@@ -2314,7 +4092,7 @@ var collectEntries = (root, manifest, project) => {
2314
4092
  const entries = [];
2315
4093
  const warnings = [];
2316
4094
  for (const file of scanLocal(root).files) {
2317
- const type = manifest.files[file.path]?.type ?? classifyKind(basename4(file.path));
4095
+ const type = manifest.files[file.path]?.type ?? classifyKind(basename7(file.path));
2318
4096
  if (type === "resource") {
2319
4097
  const src = serverByPath.get(file.path)?.fileSrc;
2320
4098
  if (!src)
@@ -2326,7 +4104,7 @@ var collectEntries = (root, manifest, project) => {
2326
4104
  warnings.push(`No compiled shader for ${file.path} — push it to compile the shader.`);
2327
4105
  entries.push({ path: file.path, type, shaders });
2328
4106
  } else {
2329
- entries.push({ path: file.path, type, text: readFileSync11(file.absPath, "utf8") });
4107
+ entries.push({ path: file.path, type, text: readFileSync13(file.absPath, "utf8") });
2330
4108
  }
2331
4109
  }
2332
4110
  return { entries, warnings };
@@ -2334,21 +4112,44 @@ var collectEntries = (root, manifest, project) => {
2334
4112
 
2335
4113
  // src/commands/preview.ts
2336
4114
  var preview = async (args) => {
2337
- const root = findProjectRoot(process.cwd());
2338
- const manifest = readManifest(root);
2339
- const config = loadConfig();
2340
- const apiUrl = requireApiUrl(config);
2341
- const token = requireToken(config);
2342
- note("Resolving project assets…");
2343
- const project = await getProject(apiUrl, token, manifest.uuid);
2344
- const { entries, warnings } = collectEntries(root, manifest, project);
4115
+ let root;
4116
+ let manifest;
4117
+ try {
4118
+ root = findProjectRoot(process.cwd());
4119
+ manifest = readManifest(root);
4120
+ } catch {
4121
+ root = process.cwd();
4122
+ manifest = null;
4123
+ }
4124
+ let entries;
4125
+ let warnings;
4126
+ let name;
4127
+ let publicUrl;
4128
+ let localAssets = false;
4129
+ if (manifest) {
4130
+ const config = loadConfig();
4131
+ const apiUrl = requireApiUrl(config);
4132
+ const token = requireToken(config);
4133
+ note("Resolving project assets…");
4134
+ const project = await getProject(apiUrl, token, manifest.uuid);
4135
+ ({ entries, warnings } = collectEntries(root, manifest, project));
4136
+ name = manifest.name;
4137
+ publicUrl = flagStr(args, "public-url") ?? apiUrl;
4138
+ } else {
4139
+ const publicUrlFlag = flagStr(args, "public-url");
4140
+ localAssets = publicUrlFlag === undefined;
4141
+ publicUrl = publicUrlFlag ?? "";
4142
+ ({ entries, warnings } = collectLocalEntries(root, null, publicUrl));
4143
+ name = basename8(root);
4144
+ }
2345
4145
  note("Compiling…");
2346
4146
  const js = await compileProject2({
2347
4147
  entries,
2348
- name: manifest.name,
2349
- publicUrl: flagStr(args, "public-url") ?? apiUrl,
4148
+ name,
4149
+ publicUrl,
2350
4150
  entryOverride: flagStr(args, "entry"),
2351
- header: !flagBool(args, "no-header")
4151
+ header: !flagBool(args, "no-header"),
4152
+ localAssets
2352
4153
  });
2353
4154
  for (const w of warnings)
2354
4155
  warnErr(w);
@@ -2356,7 +4157,7 @@ var preview = async (args) => {
2356
4157
  if (out && isNullSink(out)) {
2357
4158
  note(`Discarded output (${out})`);
2358
4159
  } else if (out) {
2359
- writeFileSync9(resolve3(out), js);
4160
+ writeFileSync11(resolve4(out), js);
2360
4161
  note(`Wrote ${out} (${js.length} bytes)`);
2361
4162
  } else {
2362
4163
  process.stdout.write(js);
@@ -2368,8 +4169,8 @@ var preview = async (args) => {
2368
4169
  };
2369
4170
 
2370
4171
  // src/commands/render.ts
2371
- import { writeFileSync as writeFileSync10 } from "node:fs";
2372
- import { resolve as resolve4 } from "node:path";
4172
+ import { writeFileSync as writeFileSync12 } from "node:fs";
4173
+ import { resolve as resolve5 } from "node:path";
2373
4174
  var LOCAL_ASSET_SCHEME2 = "lecodesfile://";
2374
4175
  var DEVICE_SAFE_AREA = {
2375
4176
  none: [0, 0, 0, 0],
@@ -2465,7 +4266,7 @@ var render = async (args) => {
2465
4266
  if (isNullSink(pngFlag)) {
2466
4267
  note(`Discarded PNG (${pngFlag})`);
2467
4268
  } else {
2468
- writeFileSync10(resolve4(pngFlag), png);
4269
+ writeFileSync12(resolve5(pngFlag), png);
2469
4270
  note(`Wrote ${pngFlag} (${png.length} bytes, ${width}x${height})`);
2470
4271
  }
2471
4272
  return done((cb) => cb());
@@ -2480,7 +4281,7 @@ var render = async (args) => {
2480
4281
  return done((cb) => cb());
2481
4282
  }
2482
4283
  if (out) {
2483
- writeFileSync10(resolve4(out), result.text);
4284
+ writeFileSync12(resolve5(out), result.text);
2484
4285
  note(`Wrote ${out} (${result.text.length} bytes)`);
2485
4286
  return done((cb) => cb());
2486
4287
  }
@@ -2491,8 +4292,8 @@ var render = async (args) => {
2491
4292
  };
2492
4293
 
2493
4294
  // src/commands/pull.ts
2494
- import { existsSync as existsSync10, mkdirSync as mkdirSync9, rmSync, writeFileSync as writeFileSync11 } from "node:fs";
2495
- import { dirname as dirname7, join as join12 } from "node:path";
4295
+ import { existsSync as existsSync14, mkdirSync as mkdirSync11, rmSync, writeFileSync as writeFileSync13 } from "node:fs";
4296
+ import { dirname as dirname9, join as join16 } from "node:path";
2496
4297
  var pull = async (args) => {
2497
4298
  const root = findProjectRoot(process.cwd());
2498
4299
  const manifest = readManifest(root);
@@ -2512,8 +4313,8 @@ var pull = async (args) => {
2512
4313
  const remotePaths = new Set([...buildPaths(project.assets).entries()].filter(([id]) => project.assets.find((a) => a.id === id)?.type !== "folder").map(([, path]) => path));
2513
4314
  for (const path of Object.keys(manifest.files)) {
2514
4315
  if (!remotePaths.has(path)) {
2515
- const abs = join12(root, ...path.replace(/^\//, "").split("/"));
2516
- if (existsSync10(abs))
4316
+ const abs = join16(root, ...path.replace(/^\//, "").split("/"));
4317
+ if (existsSync14(abs))
2517
4318
  rmSync(abs);
2518
4319
  }
2519
4320
  }
@@ -2551,18 +4352,18 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
2551
4352
  const files = { ...manifest.files };
2552
4353
  for (const asset of targets) {
2553
4354
  const key = pathById.get(asset.id);
2554
- const abs = join12(root, ...key.replace(/^\//, "").split("/"));
2555
- mkdirSync9(dirname7(abs), { recursive: true });
4355
+ const abs = join16(root, ...key.replace(/^\//, "").split("/"));
4356
+ mkdirSync11(dirname9(abs), { recursive: true });
2556
4357
  let sha;
2557
4358
  if (asset.type === "file") {
2558
4359
  if (!asset.file?.src)
2559
4360
  continue;
2560
4361
  const bytes = await downloadBlob(apiUrl, asset.file.src);
2561
- writeFileSync11(abs, bytes);
4362
+ writeFileSync13(abs, bytes);
2562
4363
  sha = sha256(bytes);
2563
4364
  } else {
2564
4365
  const text = asset.text ?? "";
2565
- writeFileSync11(abs, text);
4366
+ writeFileSync13(abs, text);
2566
4367
  sha = sha256(text);
2567
4368
  }
2568
4369
  files[key] = { id: asset.id, type: kindOf2(asset.type), sha };
@@ -2574,8 +4375,8 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
2574
4375
  };
2575
4376
 
2576
4377
  // src/commands/push.ts
2577
- import { basename as basename5 } from "node:path";
2578
- import { readFileSync as readFileSync12 } from "node:fs";
4378
+ import { basename as basename9 } from "node:path";
4379
+ import { readFileSync as readFileSync14 } from "node:fs";
2579
4380
  var push = async (args) => {
2580
4381
  const root = findProjectRoot(process.cwd());
2581
4382
  const manifest = readManifest(root);
@@ -2629,14 +4430,14 @@ var push = async (args) => {
2629
4430
  let uploaded = 0;
2630
4431
  for (const file of scan.files) {
2631
4432
  const source = sourceFor(file.path);
2632
- const kind = source?.type ?? classifyKind(basename5(file.path));
4433
+ const kind = source?.type ?? classifyKind(basename9(file.path));
2633
4434
  const entry = { path: file.path, type: kind };
2634
4435
  if (source)
2635
4436
  entry.assetId = source.id;
2636
4437
  if (isTextKind(kind)) {
2637
- entry.text = readFileSync12(file.absPath, "utf8");
4438
+ entry.text = readFileSync14(file.absPath, "utf8");
2638
4439
  } else if (!source || source.sha !== file.sha) {
2639
- const blob = await uploadBlob(apiUrl, token, manifest.uuid, basename5(file.path), readFileSync12(file.absPath));
4440
+ const blob = await uploadBlob(apiUrl, token, manifest.uuid, basename9(file.path), readFileSync14(file.absPath));
2640
4441
  entry.blobFileId = blob.id;
2641
4442
  uploaded++;
2642
4443
  }
@@ -2723,6 +4524,9 @@ var HELP = `${c.bold("lecodes")} — clone, edit and push LeCodes projects from
2723
4524
  ${c.bold("Usage:")} lecodes <command> [options]
2724
4525
 
2725
4526
  ${c.bold("Commands:")}
4527
+ init [dir] Scaffold a new LOCAL project (no login/server): a starter
4528
+ main.ts, IDE types + tsconfig, and a .lecodesignore.
4529
+ Run it with 'lecodes dev' or 'lecodes preview'.
2726
4530
  login Authorize in the browser and store an access token
2727
4531
  --token <pat> store a token from the web UI instead
2728
4532
  --api <url> API origin (default https://le.codes)
@@ -2744,6 +4548,14 @@ ${c.bold("Commands:")}
2744
4548
  --force push even if the server moved on (shows + confirms
2745
4549
  which server files get overwritten)
2746
4550
  -y, --yes skip the --force overwrite confirmation
4551
+ dev Run the project on a device from this machine, with hot reload:
4552
+ starts a LAN dev server + prints a QR code; scan it with the
4553
+ LeCodes app. Saves recompile + hot-swap the running app; device
4554
+ logs/errors stream back here. No login needed.
4555
+ --port <n> server port (default 4499)
4556
+ --host <ip> LAN address to advertise (default: auto-detect)
4557
+ --entry <path> override the entrypoint
4558
+ --no-qr don't render the QR code
2747
4559
  preview Compile the local project to JS (prints to stdout)
2748
4560
  -o, --out <file> write to a file instead of stdout
2749
4561
  --entry <path> override the entrypoint
@@ -2774,15 +4586,8 @@ ${c.bold("Commands:")}
2774
4586
  --logs forward the screens' console output (to stderr)
2775
4587
 
2776
4588
  Config lives in ~/.lecodes/config.json (override with LECODES_API / LECODES_TOKEN).`;
2777
- var commands = { login, create, clone, status, diff, pull, push, preview, render, design };
2778
- var readVersion = () => {
2779
- try {
2780
- const here2 = dirname8(fileURLToPath4(import.meta.url));
2781
- return JSON.parse(readFileSync13(join13(here2, "..", "package.json"), "utf8")).version ?? "unknown";
2782
- } catch {
2783
- return "unknown";
2784
- }
2785
- };
4589
+ var commands = { init: init2, login, create, clone, status, diff, pull, push, preview, render, design, dev };
4590
+ var readVersion = () => package_default.version ?? "unknown";
2786
4591
  var main = async () => {
2787
4592
  const argv = process.argv.slice(2);
2788
4593
  const command = argv[0];