lecodes-cli 0.1.5 → 0.1.6

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
@@ -4,8 +4,8 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
4
 
5
5
  // src/index.ts
6
6
  import { readFileSync as readFileSync8 } from "node:fs";
7
- import { dirname as dirname5, join as join9 } from "node:path";
8
- import { fileURLToPath as fileURLToPath2 } from "node:url";
7
+ import { dirname as dirname7, join as join11 } from "node:path";
8
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
9
9
 
10
10
  // src/commands/clone.ts
11
11
  import { existsSync as existsSync3, mkdirSync as mkdirSync5, readdirSync } from "node:fs";
@@ -160,6 +160,7 @@ var apiRequest = async (apiUrl, path, opts = {}) => {
160
160
  };
161
161
  var getAccount = (apiUrl, token) => apiRequest(apiUrl, "/account", { token });
162
162
  var getProject = (apiUrl, token, uuid) => apiRequest(apiUrl, `/projects/${uuid}`, { token });
163
+ var createProject = (apiUrl, token, name, teamUuid) => apiRequest(apiUrl, "/projects", { token, body: { name, teamUuid } });
163
164
  var getCommits = (apiUrl, token, uuid) => apiRequest(apiUrl, `/projects/${uuid}/commits`, { token });
164
165
  var getTypes = (apiUrl) => apiRequest(apiUrl, "/types");
165
166
  var uploadBlob = (apiUrl, token, uuid, name, bytes) => {
@@ -282,38 +283,8 @@ var materializeAssets = async (root, project, apiUrl, token, onProgress) => {
282
283
  };
283
284
 
284
285
  // src/types.ts
285
- import { join as join4 } from "node:path";
286
+ import { dirname as dirname3, join as join4 } from "node:path";
286
287
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "node:fs";
287
- var wrapGlobal = (text) => {
288
- const marker = text.indexOf("declare global");
289
- if (marker < 0)
290
- return text;
291
- const importLines = [];
292
- const bodyLines = [];
293
- for (const line of text.split(`
294
- `)) {
295
- if (/^\s*import\s.*\sfrom\s/.test(line))
296
- importLines.push(line);
297
- else
298
- bodyLines.push(line);
299
- }
300
- const rest = bodyLines.join(`
301
- `);
302
- const open = rest.indexOf("{", rest.indexOf("declare global"));
303
- const close = rest.lastIndexOf("}");
304
- const preamble = rest.slice(0, rest.indexOf("declare global"));
305
- const inner = rest.slice(open + 1, close);
306
- const stripDeclare = (s) => s.replace(/(^|\n)([ \t]*)declare\s+/g, "$1$2");
307
- const merged = stripDeclare(preamble) + `
308
- ` + stripDeclare(inner);
309
- return `${importLines.join(`
310
- `)}
311
-
312
- declare global {
313
- ${merged}
314
- }
315
- `;
316
- };
317
288
  var TSCONFIG = {
318
289
  compilerOptions: {
319
290
  target: "ES2020",
@@ -333,10 +304,10 @@ var TSCONFIG = {
333
304
  var materializeTypes = async (root, apiUrl) => {
334
305
  const bundle = await getTypes(apiUrl);
335
306
  const typesDir = join4(root, LECODES_DIR, "types");
336
- mkdirSync4(typesDir, { recursive: true });
337
- for (const [name, text] of Object.entries(bundle)) {
338
- const content = name === "global" ? wrapGlobal(text) : text;
339
- writeFileSync4(join4(typesDir, `${name}.d.ts`), content);
307
+ for (const [path, text] of Object.entries(bundle)) {
308
+ const dest = join4(typesDir, path);
309
+ mkdirSync4(dirname3(dest), { recursive: true });
310
+ writeFileSync4(dest, text);
340
311
  }
341
312
  };
342
313
  var writeTsconfig = (root) => {
@@ -471,9 +442,47 @@ var clone = async (args) => {
471
442
  info(c.dim(` cd ${dir} — edit, then \`lecodes push -m "your message"\``));
472
443
  };
473
444
 
445
+ // src/commands/create.ts
446
+ import { existsSync as existsSync4, mkdirSync as mkdirSync6, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "node:fs";
447
+ import { join as join6, resolve as resolve2 } from "node:path";
448
+ var slug2 = (name) => name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
449
+ var STARTER = `// Welcome to le.codes — no imports needed, the whole SDK is available as globals.
450
+ const scene = new Scene2D({ background: '#10131a' })
451
+ scene.open()
452
+
453
+ setLoop((dt) => {
454
+ // your per-frame logic — dt is seconds since the previous frame
455
+ })
456
+ `;
457
+ var create = async (args) => {
458
+ const config = loadConfig();
459
+ const apiUrl = requireApiUrl(config);
460
+ const token = requireToken(config);
461
+ const name = args._[0];
462
+ if (!name)
463
+ throw new CliError("Usage: lecodes create <name> [directory]");
464
+ const dir = args._[1] ?? slug2(name);
465
+ const root = resolve2(dir);
466
+ if (existsSync4(root) && readdirSync2(root).length > 0) {
467
+ throw new CliError(`Directory "${dir}" already exists and is not empty.`);
468
+ }
469
+ mkdirSync6(root, { recursive: true });
470
+ info(c.dim(`Creating project ${c.bold(name)}…`));
471
+ const { uuid } = await createProject(apiUrl, token, name, flagStr(args, "team"));
472
+ info(c.dim("Fetching type definitions…"));
473
+ await materializeTypes(root, apiUrl);
474
+ writeTsconfig(root);
475
+ writeDefaultIgnore(root);
476
+ writeFileSync6(join6(root, "main.ts"), STARTER);
477
+ const commits = await getCommits(apiUrl, token, uuid);
478
+ writeManifest(root, { uuid, apiUrl, name, baseHeadId: commits.headId, files: {} });
479
+ success(`Created ${c.bold(name)} in ${c.bold(dir)}/`);
480
+ info(c.dim(` cd ${dir} — edit main.ts, then \`lecodes push -m "first commit"\` to save it`));
481
+ };
482
+
474
483
  // src/localFiles.ts
475
- import { readdirSync as readdirSync2, readFileSync as readFileSync4 } from "node:fs";
476
- import { join as join6, relative } from "node:path";
484
+ import { readdirSync as readdirSync3, readFileSync as readFileSync4 } from "node:fs";
485
+ import { join as join7, relative } from "node:path";
477
486
  var SKIP_DIRS = new Set([".lecodes", ".git", "node_modules"]);
478
487
  var SKIP_FILES = new Set(["tsconfig.json", "jsconfig.json", ".gitignore", ".lecodesignore", ".DS_Store"]);
479
488
  var TEXT_EXTS = new Set([
@@ -526,8 +535,8 @@ var scanLocal = (root, opts = {}) => {
526
535
  return false;
527
536
  };
528
537
  const walk = (dir, parentIgnored) => {
529
- for (const entry of readdirSync2(dir, { withFileTypes: true })) {
530
- const abs = join6(dir, entry.name);
538
+ for (const entry of readdirSync3(dir, { withFileTypes: true })) {
539
+ const abs = join7(dir, entry.name);
531
540
  const key = toKey(relative(root, abs));
532
541
  if (entry.isDirectory()) {
533
542
  if (SKIP_DIRS.has(entry.name))
@@ -804,10 +813,10 @@ var openBrowser = (url) => {
804
813
  };
805
814
  var browserLogin = async (webBase, timeoutMs = 5 * 60 * 1000, open = openBrowser) => {
806
815
  const state = randomBytes(16).toString("hex");
807
- let resolve2;
816
+ let resolve3;
808
817
  let reject;
809
818
  const tokenPromise = new Promise((res, rej) => {
810
- resolve2 = res;
819
+ resolve3 = res;
811
820
  reject = rej;
812
821
  });
813
822
  const server = createServer((req, res) => {
@@ -825,7 +834,7 @@ var browserLogin = async (webBase, timeoutMs = 5 * 60 * 1000, open = openBrowser
825
834
  }
826
835
  res.writeHead(200, { "content-type": "text/html" });
827
836
  res.end(SUCCESS_HTML);
828
- resolve2(token);
837
+ resolve3(token);
829
838
  });
830
839
  await new Promise((res, rej) => {
831
840
  server.once("error", rej);
@@ -858,8 +867,8 @@ var login = async (args) => {
858
867
  };
859
868
 
860
869
  // src/commands/preview.ts
861
- import { writeFileSync as writeFileSync6 } from "node:fs";
862
- import { resolve as resolve2 } from "node:path";
870
+ import { writeFileSync as writeFileSync7 } from "node:fs";
871
+ import { resolve as resolve3 } from "node:path";
863
872
 
864
873
  // src/compile/collect.ts
865
874
  import { readFileSync as readFileSync6 } from "node:fs";
@@ -893,13 +902,17 @@ var collectEntries = (root, manifest, project) => {
893
902
  return { entries, warnings };
894
903
  };
895
904
 
896
- // src/compile/bundler.ts
905
+ // src/compile/projectCompile.ts
906
+ import { existsSync as existsSync6 } from "node:fs";
907
+ import { dirname as dirname5, join as join9 } from "node:path";
908
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
909
+ // ../sdk/src/compile/bundler.ts
897
910
  import * as esbuild from "esbuild";
898
- import { existsSync as existsSync4 } from "node:fs";
899
- import { dirname as dirname3, join as join7 } from "node:path";
911
+ import { existsSync as existsSync5 } from "node:fs";
912
+ import { dirname as dirname4, join as join8 } from "node:path";
900
913
  import { fileURLToPath } from "node:url";
901
914
 
902
- // src/compile/assetMacro.ts
915
+ // ../sdk/src/compile/assetMacro.ts
903
916
  var ASSET_RE = /\basset\(\s*(["'])((?:[^"'\\]|\\.)*)\1\s*\)/g;
904
917
  var hasAssetMacro = (src) => src.includes("asset(");
905
918
  var desugarAssetMacros = (src, filePath) => {
@@ -924,52 +937,21 @@ var desugarAssetMacros = (src, filePath) => {
924
937
  ` + out;
925
938
  };
926
939
 
927
- // src/compile/bundler.ts
928
- var GL_MATRIX_HEADER = `
929
- import * as glMatrix from "gl-matrix/common";
930
- import * as mat2 from "gl-matrix/mat2";
931
- import * as mat2d from "gl-matrix/mat2d";
932
- import * as mat3 from "gl-matrix/mat3";
933
- import * as mat4 from "gl-matrix/mat4";
934
- import * as quat from "gl-matrix/quat";
935
- import * as quat2 from "gl-matrix/quat2";
936
- import * as vec2 from "gl-matrix/vec2";
937
- import * as vec3 from "gl-matrix/vec3";
938
- import * as vec4 from "gl-matrix/vec4";
939
- `;
940
- var resolveRuntime = () => {
941
- const here = dirname3(fileURLToPath(import.meta.url));
942
- const vendored = join7(here, "..", "runtime");
943
- if (existsSync4(join7(vendored, "worker", "creator.js"))) {
944
- const w = join7(vendored, "worker");
945
- return {
946
- alias: {
947
- creator: join7(w, "creator.js"),
948
- ui: join7(w, "ui.js"),
949
- animate: join7(w, "animate.js"),
950
- utils: join7(w, "utils.js"),
951
- "gl-matrix": join7(vendored, "gl-matrix")
952
- },
953
- inject: [join7(w, "globals.js")]
954
- };
955
- }
956
- const repoRoot = join7(here, "..", "..", "..", "..");
957
- const ws = join7(repoRoot, "packages", "worker", "src");
958
- if (existsSync4(join7(ws, "index.ts"))) {
959
- return {
960
- alias: {
961
- creator: join7(ws, "index.ts"),
962
- ui: join7(ws, "ui.ts"),
963
- animate: join7(ws, "animate.ts"),
964
- utils: join7(ws, "utils.ts"),
965
- "gl-matrix": join7(repoRoot, "packages", "backend", "src", "assets", "dependencies", "gl-matrix")
966
- },
967
- inject: [join7(ws, "globals.ts")]
968
- };
969
- }
970
- throw new CliError("Could not locate the compile runtime — try reinstalling lecodes-cli.");
971
- };
972
- function normalize(path) {
940
+ // ../sdk/src/compile/bundler.ts
941
+ var resolveInject = () => {
942
+ const override = process.env.LECODES_SDK_INJECT;
943
+ if (override && existsSync5(override))
944
+ return override;
945
+ const here = dirname4(fileURLToPath(import.meta.url));
946
+ const src = join8(here, "..", "inject.ts");
947
+ if (existsSync5(src))
948
+ return src;
949
+ const dist = join8(here, "..", "..", "dist", "inject.js");
950
+ if (existsSync5(dist))
951
+ return dist;
952
+ throw new Error("sdk: could not locate the inject entry (LECODES_SDK_INJECT / src/inject.ts / dist/inject.js)");
953
+ };
954
+ var normalize = (path) => {
973
955
  const parts = [];
974
956
  for (const part of path.split("/")) {
975
957
  if (!part || part === ".")
@@ -980,20 +962,20 @@ function normalize(path) {
980
962
  parts.push(part);
981
963
  }
982
964
  return "/" + parts.join("/");
983
- }
984
- function dirOf(path) {
965
+ };
966
+ var dirOf = (path) => {
985
967
  const i = path.lastIndexOf("/");
986
968
  return i <= 0 ? "/" : path.slice(0, i);
987
- }
969
+ };
988
970
  var hasExtension = (path) => /\.[a-zA-Z0-9]+$/.test(path);
989
- function resolveBase(importer, specifier) {
971
+ var resolveBase = (importer, specifier) => {
990
972
  if (specifier.startsWith("/"))
991
973
  return normalize(specifier);
992
974
  if (specifier.startsWith("./") || specifier.startsWith("../"))
993
975
  return normalize(dirOf(importer) + "/" + specifier);
994
- throw new Error(`Bare import is not allowed here: ${specifier}`);
995
- }
996
- function resolveWithExtensions(files, importer, specifier) {
976
+ throw new Error(`Bare imports are not allowed — the whole SDK is available as globals (no imports): ${specifier}`);
977
+ };
978
+ var resolveWithExtensions = (files, importer, specifier) => {
997
979
  const base = resolveBase(importer, specifier);
998
980
  if (hasExtension(base)) {
999
981
  if (files.has(base))
@@ -1005,31 +987,26 @@ function resolveWithExtensions(files, importer, specifier) {
1005
987
  return candidate;
1006
988
  }
1007
989
  throw new Error(`Module not found: ${specifier} from ${importer}`);
1008
- }
990
+ };
1009
991
  var getLoader = (path) => path.endsWith(".ts") ? "ts" : "js";
1010
- async function buildInMemory(entrypoint, inputFiles, format = "esm") {
1011
- const runtime = resolveRuntime();
1012
- const files = new Map(Object.entries(inputFiles).map(([k, v]) => [normalize(k), v]));
992
+ var bundleProject = async (entrypoint, files, format = "esm") => {
993
+ const fileMap = new Map(Object.entries(files).map(([k, v]) => [normalize(k), v]));
1013
994
  const entry = normalize(entrypoint);
1014
- if (!files.has(entry))
1015
- throw new CliError(`Entrypoint not found: ${entry}`);
995
+ if (!fileMap.has(entry))
996
+ throw new Error(`Entrypoint not found: ${entry}`);
1016
997
  const plugin = {
1017
998
  name: "memfs",
1018
999
  setup(build2) {
1019
1000
  build2.onResolve({ filter: /^entry$/ }, () => ({ path: entry, namespace: "mem" }));
1020
1001
  build2.onResolve({ filter: /.*/, namespace: "mem" }, (args) => {
1021
- const isRelative = args.path.startsWith("./") || args.path.startsWith("../");
1022
- const isAbsolute = args.path.startsWith("/");
1023
- if (!isRelative && !isAbsolute)
1024
- return null;
1025
1002
  try {
1026
- return { path: resolveWithExtensions(files, args.importer || entry, args.path), namespace: "mem" };
1003
+ return { path: resolveWithExtensions(fileMap, args.importer || entry, args.path), namespace: "mem" };
1027
1004
  } catch (error) {
1028
1005
  return { errors: [{ text: error instanceof Error ? error.message : String(error) }] };
1029
1006
  }
1030
1007
  });
1031
1008
  build2.onLoad({ filter: /.*/, namespace: "mem" }, (args) => {
1032
- const contents = files.get(args.path);
1009
+ const contents = fileMap.get(args.path);
1033
1010
  if (contents == null)
1034
1011
  return { errors: [{ text: `Module not found: ${args.path}` }] };
1035
1012
  const finalContents = /\.[tj]sx?$/.test(args.path) && hasAssetMacro(contents) ? desugarAssetMacros(contents, args.path) : contents;
@@ -1039,9 +1016,8 @@ async function buildInMemory(entrypoint, inputFiles, format = "esm") {
1039
1016
  };
1040
1017
  const result = await esbuild.build({
1041
1018
  stdin: { contents: `import "entry";`, loader: "js", sourcefile: "bootstrap.js" },
1042
- alias: runtime.alias,
1019
+ inject: [resolveInject()],
1043
1020
  define: { RAD2DEG: "57.29577951308232", DEG2RAD: "0.017453292519943295" },
1044
- inject: runtime.inject,
1045
1021
  bundle: true,
1046
1022
  write: false,
1047
1023
  format,
@@ -1051,13 +1027,16 @@ async function buildInMemory(entrypoint, inputFiles, format = "esm") {
1051
1027
  });
1052
1028
  const output = result.outputFiles?.[0];
1053
1029
  if (!output)
1054
- throw new CliError("Compilation produced no output");
1030
+ throw new Error("Compilation produced no output");
1055
1031
  return output.text;
1056
- }
1032
+ };
1057
1033
 
1058
- // src/compile/projectCompile.ts
1059
- var buildHeader = (name, code) => {
1034
+ // ../sdk/src/compile/header.ts
1035
+ var buildHeader = (name, code, opts = {}) => {
1060
1036
  let header = `// name: ${name.slice(0, 20)}
1037
+ `;
1038
+ if (opts.icon)
1039
+ header += `// icon: ${opts.icon}
1061
1040
  `;
1062
1041
  header += `// last-updated: ${new Date().toISOString()}
1063
1042
  `;
@@ -1077,6 +1056,9 @@ var buildHeader = (name, code) => {
1077
1056
  `;
1078
1057
  else if (code.includes("_creator.createEngine"))
1079
1058
  header += `// gl: defer
1059
+ `;
1060
+ if (code.includes("_creator2d."))
1061
+ header += `// 2d: enable
1080
1062
  `;
1081
1063
  if (code.includes("_creator.createARController"))
1082
1064
  header += "// ar: enable";
@@ -1084,6 +1066,8 @@ var buildHeader = (name, code) => {
1084
1066
  `;
1085
1067
  return header;
1086
1068
  };
1069
+
1070
+ // ../sdk/src/compile/compileProject.ts
1087
1071
  var detectEntry = (entries) => {
1088
1072
  let entry = "";
1089
1073
  let max = -1;
@@ -1101,7 +1085,7 @@ var detectEntry = (entries) => {
1101
1085
  var compileProject = async (opts) => {
1102
1086
  const entry = opts.entryOverride ?? detectEntry(opts.entries);
1103
1087
  if (!entry) {
1104
- throw new CliError("Couldn't find an entrypoint (a top-level .ts/.js file with no `export`). Pass --entry <path>.");
1088
+ throw new Error("Couldn't find an entrypoint (a top-level .ts/.js file with no `export`). Pass entryOverride.");
1105
1089
  }
1106
1090
  const files = {};
1107
1091
  for (const e of opts.entries) {
@@ -1119,12 +1103,24 @@ var compileProject = async (opts) => {
1119
1103
  } else if (e.path.toLowerCase().endsWith(".svg")) {
1120
1104
  files[e.path] = `export default SvgSource(\`${e.text ?? ""}\`)`;
1121
1105
  } else {
1122
- files[e.path] = GL_MATRIX_HEADER + (e.text ?? "");
1106
+ files[e.path] = e.text ?? "";
1123
1107
  }
1124
1108
  }
1125
- const code = await buildInMemory(entry, files, opts.format ?? "esm");
1126
- return opts.header === false ? code : buildHeader(opts.name, code) + code;
1109
+ const code = await bundleProject(entry, files, opts.format ?? "esm");
1110
+ return opts.header === false ? code : buildHeader(opts.name, code, { icon: opts.icon }) + code;
1127
1111
  };
1112
+ // src/compile/projectCompile.ts
1113
+ var vendoredInject = join9(dirname5(fileURLToPath2(import.meta.url)), "..", "runtime", "inject.js");
1114
+ if (existsSync6(vendoredInject))
1115
+ process.env.LECODES_SDK_INJECT = vendoredInject;
1116
+ var compileProject2 = (opts) => compileProject({
1117
+ entries: opts.entries.map((e) => ({ path: e.path, type: e.type, text: e.text, fileSrc: e.fileSrc, shaders: e.shaders })),
1118
+ name: opts.name,
1119
+ publicUrl: opts.publicUrl,
1120
+ entryOverride: opts.entryOverride,
1121
+ header: opts.header,
1122
+ format: opts.format
1123
+ });
1128
1124
 
1129
1125
  // src/commands/preview.ts
1130
1126
  var preview = async (args) => {
@@ -1137,7 +1133,7 @@ var preview = async (args) => {
1137
1133
  const project = await getProject(apiUrl, token, manifest.uuid);
1138
1134
  const { entries, warnings } = collectEntries(root, manifest, project);
1139
1135
  note("Compiling…");
1140
- const js = await compileProject({
1136
+ const js = await compileProject2({
1141
1137
  entries,
1142
1138
  name: manifest.name,
1143
1139
  publicUrl: flagStr(args, "public-url") ?? apiUrl,
@@ -1150,7 +1146,7 @@ var preview = async (args) => {
1150
1146
  if (out && isNullSink(out)) {
1151
1147
  note(`Discarded output (${out})`);
1152
1148
  } else if (out) {
1153
- writeFileSync6(resolve2(out), js);
1149
+ writeFileSync7(resolve3(out), js);
1154
1150
  note(`Wrote ${out} (${js.length} bytes)`);
1155
1151
  } else {
1156
1152
  process.stdout.write(js);
@@ -1162,8 +1158,8 @@ var preview = async (args) => {
1162
1158
  };
1163
1159
 
1164
1160
  // src/commands/render.ts
1165
- import { writeFileSync as writeFileSync7 } from "node:fs";
1166
- import { resolve as resolve3 } from "node:path";
1161
+ import { writeFileSync as writeFileSync8 } from "node:fs";
1162
+ import { resolve as resolve4 } from "node:path";
1167
1163
  var DEVICE_SAFE_AREA = {
1168
1164
  none: [0, 0, 0, 0],
1169
1165
  iphone: [47, 0, 34, 0],
@@ -1202,7 +1198,7 @@ var render = async (args) => {
1202
1198
  const project = await getProject(apiUrl, token, manifest.uuid);
1203
1199
  const { entries, warnings } = collectEntries(root, manifest, project);
1204
1200
  note("Compiling…");
1205
- const js = await compileProject({
1201
+ const js = await compileProject2({
1206
1202
  entries,
1207
1203
  name: manifest.name,
1208
1204
  publicUrl: flagStr(args, "public-url") ?? apiUrl,
@@ -1250,7 +1246,7 @@ var render = async (args) => {
1250
1246
  if (isNullSink(pngFlag)) {
1251
1247
  note(`Discarded PNG (${pngFlag})`);
1252
1248
  } else {
1253
- writeFileSync7(resolve3(pngFlag), png);
1249
+ writeFileSync8(resolve4(pngFlag), png);
1254
1250
  note(`Wrote ${pngFlag} (${png.length} bytes, ${width}x${height})`);
1255
1251
  }
1256
1252
  return done((cb) => cb());
@@ -1265,7 +1261,7 @@ var render = async (args) => {
1265
1261
  return done((cb) => cb());
1266
1262
  }
1267
1263
  if (out) {
1268
- writeFileSync7(resolve3(out), result.text);
1264
+ writeFileSync8(resolve4(out), result.text);
1269
1265
  note(`Wrote ${out} (${result.text.length} bytes)`);
1270
1266
  return done((cb) => cb());
1271
1267
  }
@@ -1276,8 +1272,8 @@ var render = async (args) => {
1276
1272
  };
1277
1273
 
1278
1274
  // src/commands/pull.ts
1279
- import { existsSync as existsSync5, mkdirSync as mkdirSync6, rmSync, writeFileSync as writeFileSync8 } from "node:fs";
1280
- import { dirname as dirname4, join as join8 } from "node:path";
1275
+ import { existsSync as existsSync7, mkdirSync as mkdirSync7, rmSync, writeFileSync as writeFileSync9 } from "node:fs";
1276
+ import { dirname as dirname6, join as join10 } from "node:path";
1281
1277
  var pull = async (args) => {
1282
1278
  const root = findProjectRoot(process.cwd());
1283
1279
  const manifest = readManifest(root);
@@ -1297,8 +1293,8 @@ var pull = async (args) => {
1297
1293
  const remotePaths = new Set([...buildPaths(project.assets).entries()].filter(([id]) => project.assets.find((a) => a.id === id)?.type !== "folder").map(([, path]) => path));
1298
1294
  for (const path of Object.keys(manifest.files)) {
1299
1295
  if (!remotePaths.has(path)) {
1300
- const abs = join8(root, ...path.replace(/^\//, "").split("/"));
1301
- if (existsSync5(abs))
1296
+ const abs = join10(root, ...path.replace(/^\//, "").split("/"));
1297
+ if (existsSync7(abs))
1302
1298
  rmSync(abs);
1303
1299
  }
1304
1300
  }
@@ -1336,18 +1332,18 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
1336
1332
  const files = { ...manifest.files };
1337
1333
  for (const asset of targets) {
1338
1334
  const key = pathById.get(asset.id);
1339
- const abs = join8(root, ...key.replace(/^\//, "").split("/"));
1340
- mkdirSync6(dirname4(abs), { recursive: true });
1335
+ const abs = join10(root, ...key.replace(/^\//, "").split("/"));
1336
+ mkdirSync7(dirname6(abs), { recursive: true });
1341
1337
  let sha;
1342
1338
  if (asset.type === "file") {
1343
1339
  if (!asset.file?.src)
1344
1340
  continue;
1345
1341
  const bytes = await downloadBlob(apiUrl, asset.file.src);
1346
- writeFileSync8(abs, bytes);
1342
+ writeFileSync9(abs, bytes);
1347
1343
  sha = sha256(bytes);
1348
1344
  } else {
1349
1345
  const text = asset.text ?? "";
1350
- writeFileSync8(abs, text);
1346
+ writeFileSync9(abs, text);
1351
1347
  sha = sha256(text);
1352
1348
  }
1353
1349
  files[key] = { id: asset.id, type: kindOf2(asset.type), sha };
@@ -1493,6 +1489,9 @@ ${c.bold("Commands:")}
1493
1489
  --token <pat> store a token from the web UI instead
1494
1490
  --api <url> API origin (default https://le.codes)
1495
1491
  --web <url> app origin for the browser flow
1492
+ create <name> [dir] Create a new project on the server and scaffold it locally
1493
+ (types from the server + tsconfig + a starter main.ts)
1494
+ --team <uuid> create it inside a team
1496
1495
  clone <uuid|url> [dir] Download a project's files + types into a new folder
1497
1496
  --no-types skip the .d.ts / tsconfig generation
1498
1497
  status Show local changes vs the last sync
@@ -1525,11 +1524,11 @@ ${c.bold("Commands:")}
1525
1524
  --logs forward the app's console output (to stderr)
1526
1525
 
1527
1526
  Config lives in ~/.lecodes/config.json (override with LECODES_API / LECODES_TOKEN).`;
1528
- var commands = { login, clone, status, diff, pull, push, preview, render };
1527
+ var commands = { login, create, clone, status, diff, pull, push, preview, render };
1529
1528
  var readVersion = () => {
1530
1529
  try {
1531
- const here = dirname5(fileURLToPath2(import.meta.url));
1532
- return JSON.parse(readFileSync8(join9(here, "..", "package.json"), "utf8")).version ?? "unknown";
1530
+ const here = dirname7(fileURLToPath3(import.meta.url));
1531
+ return JSON.parse(readFileSync8(join11(here, "..", "package.json"), "utf8")).version ?? "unknown";
1533
1532
  } catch {
1534
1533
  return "unknown";
1535
1534
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lecodes-cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Clone, edit and push LeCodes projects from your machine.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -38,6 +38,7 @@
38
38
  }
39
39
  },
40
40
  "devDependencies": {
41
- "lecodes-renderer": "0.1.1"
41
+ "lecodes-renderer": "workspace:*",
42
+ "sdk": "workspace:*"
42
43
  }
43
44
  }