@lotics/cli 0.165.0 → 0.171.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/src/cli.js CHANGED
@@ -26822,12 +26822,12 @@ var require_tokens = __commonJS({
26822
26822
  });
26823
26823
  }
26824
26824
  exports2.assignCategoriesMapProp = assignCategoriesMapProp;
26825
- function singleAssignCategoriesToksMap(path14, nextNode) {
26826
- utils_1.forEach(path14, function(pathNode) {
26825
+ function singleAssignCategoriesToksMap(path13, nextNode) {
26826
+ utils_1.forEach(path13, function(pathNode) {
26827
26827
  nextNode.categoryMatchesMap[pathNode.tokenTypeIdx] = true;
26828
26828
  });
26829
26829
  utils_1.forEach(nextNode.CATEGORIES, function(nextCategory) {
26830
- var newPath = path14.concat(nextNode);
26830
+ var newPath = path13.concat(nextNode);
26831
26831
  if (!utils_1.contains(newPath, nextCategory)) {
26832
26832
  singleAssignCategoriesToksMap(newPath, nextCategory);
26833
26833
  }
@@ -28590,10 +28590,10 @@ var require_interpreter = __commonJS({
28590
28590
  /** @class */
28591
28591
  (function(_super) {
28592
28592
  __extends(AbstractNextPossibleTokensWalker2, _super);
28593
- function AbstractNextPossibleTokensWalker2(topProd, path14) {
28593
+ function AbstractNextPossibleTokensWalker2(topProd, path13) {
28594
28594
  var _this = _super.call(this) || this;
28595
28595
  _this.topProd = topProd;
28596
- _this.path = path14;
28596
+ _this.path = path13;
28597
28597
  _this.possibleTokTypes = [];
28598
28598
  _this.nextProductionName = "";
28599
28599
  _this.nextProductionOccurrence = 0;
@@ -28647,9 +28647,9 @@ var require_interpreter = __commonJS({
28647
28647
  /** @class */
28648
28648
  (function(_super) {
28649
28649
  __extends(NextAfterTokenWalker2, _super);
28650
- function NextAfterTokenWalker2(topProd, path14) {
28651
- var _this = _super.call(this, topProd, path14) || this;
28652
- _this.path = path14;
28650
+ function NextAfterTokenWalker2(topProd, path13) {
28651
+ var _this = _super.call(this, topProd, path13) || this;
28652
+ _this.path = path13;
28653
28653
  _this.nextTerminalName = "";
28654
28654
  _this.nextTerminalOccurrence = 0;
28655
28655
  _this.nextTerminalName = _this.path.lastTok.name;
@@ -29346,10 +29346,10 @@ var require_lookahead = __commonJS({
29346
29346
  }
29347
29347
  return result;
29348
29348
  }
29349
- function pathToHashKeys(path14) {
29349
+ function pathToHashKeys(path13) {
29350
29350
  var keys3 = [""];
29351
- for (var i2 = 0; i2 < path14.length; i2++) {
29352
- var tokType = path14[i2];
29351
+ for (var i2 = 0; i2 < path13.length; i2++) {
29352
+ var tokType = path13[i2];
29353
29353
  var longerKeys = [];
29354
29354
  for (var j = 0; j < keys3.length; j++) {
29355
29355
  var currShorterKey = keys3[j];
@@ -29661,9 +29661,9 @@ var require_checks = __commonJS({
29661
29661
  return errors2;
29662
29662
  }
29663
29663
  exports2.validateRuleIsOverridden = validateRuleIsOverridden;
29664
- function validateNoLeftRecursion(topRule, currRule, errMsgProvider, path14) {
29665
- if (path14 === void 0) {
29666
- path14 = [];
29664
+ function validateNoLeftRecursion(topRule, currRule, errMsgProvider, path13) {
29665
+ if (path13 === void 0) {
29666
+ path13 = [];
29667
29667
  }
29668
29668
  var errors2 = [];
29669
29669
  var nextNonTerminals = getFirstNoneTerminal(currRule.definition);
@@ -29676,15 +29676,15 @@ var require_checks = __commonJS({
29676
29676
  errors2.push({
29677
29677
  message: errMsgProvider.buildLeftRecursionError({
29678
29678
  topLevelRule: topRule,
29679
- leftRecursionPath: path14
29679
+ leftRecursionPath: path13
29680
29680
  }),
29681
29681
  type: parser_1.ParserDefinitionErrorType.LEFT_RECURSION,
29682
29682
  ruleName
29683
29683
  });
29684
29684
  }
29685
- var validNextSteps = utils2.difference(nextNonTerminals, path14.concat([topRule]));
29685
+ var validNextSteps = utils2.difference(nextNonTerminals, path13.concat([topRule]));
29686
29686
  var errorsFromNextSteps = utils2.map(validNextSteps, function(currRefRule) {
29687
- var newPath = utils2.cloneArr(path14);
29687
+ var newPath = utils2.cloneArr(path13);
29688
29688
  newPath.push(currRefRule);
29689
29689
  return validateNoLeftRecursion(topRule, currRefRule, errMsgProvider, newPath);
29690
29690
  });
@@ -37293,8 +37293,8 @@ var require_utils4 = __commonJS({
37293
37293
  var result = transform2[inputType][outputType](input);
37294
37294
  return result;
37295
37295
  };
37296
- exports2.resolve = function(path14) {
37297
- var parts = path14.split("/");
37296
+ exports2.resolve = function(path13) {
37297
+ var parts = path13.split("/");
37298
37298
  var result = [];
37299
37299
  for (var index = 0; index < parts.length; index++) {
37300
37300
  var part = parts[index];
@@ -43147,18 +43147,18 @@ var require_object = __commonJS({
43147
43147
  var object2 = new ZipObject(name2, zipObjectContent, o);
43148
43148
  this.files[name2] = object2;
43149
43149
  };
43150
- var parentFolder = function(path14) {
43151
- if (path14.slice(-1) === "/") {
43152
- path14 = path14.substring(0, path14.length - 1);
43150
+ var parentFolder = function(path13) {
43151
+ if (path13.slice(-1) === "/") {
43152
+ path13 = path13.substring(0, path13.length - 1);
43153
43153
  }
43154
- var lastSlash = path14.lastIndexOf("/");
43155
- return lastSlash > 0 ? path14.substring(0, lastSlash) : "";
43154
+ var lastSlash = path13.lastIndexOf("/");
43155
+ return lastSlash > 0 ? path13.substring(0, lastSlash) : "";
43156
43156
  };
43157
- var forceTrailingSlash = function(path14) {
43158
- if (path14.slice(-1) !== "/") {
43159
- path14 += "/";
43157
+ var forceTrailingSlash = function(path13) {
43158
+ if (path13.slice(-1) !== "/") {
43159
+ path13 += "/";
43160
43160
  }
43161
- return path14;
43161
+ return path13;
43162
43162
  };
43163
43163
  var folderAdd = function(name2, createFolders) {
43164
43164
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults2.createFolders;
@@ -44162,7 +44162,7 @@ import dns from "node:dns";
44162
44162
  import net2 from "node:net";
44163
44163
  import fs14 from "node:fs";
44164
44164
  import os4 from "node:os";
44165
- import path13 from "node:path";
44165
+ import path12 from "node:path";
44166
44166
  import readline from "node:readline";
44167
44167
 
44168
44168
  // ../shared/src/transport_error.ts
@@ -44301,6 +44301,26 @@ function getMimeType(filename) {
44301
44301
  return MIME_MAP[ext] ?? "application/octet-stream";
44302
44302
  }
44303
44303
  var API_BASE_URL = process.env.LOTICS_API_URL ?? "https://api.lotics.ai";
44304
+ var WEB_APP_URL = "https://lotics.ai";
44305
+ async function fetchOfficialStarters() {
44306
+ let response;
44307
+ try {
44308
+ response = await fetch(`${API_BASE_URL}/v1/starters/official`, {
44309
+ signal: AbortSignal.timeout(SHELF_FETCH_TIMEOUT_MS)
44310
+ });
44311
+ } catch (error52) {
44312
+ throw new Error(
44313
+ `Could not reach Lotics to list the starters (${error52 instanceof Error ? error52.message : String(error52)}). Check your connection, or browse ${WEB_APP_URL}/docs/cli.`
44314
+ );
44315
+ }
44316
+ if (!response.ok) {
44317
+ throw new Error(
44318
+ `Lotics answered ${response.status} listing the starters. If this keeps happening, browse ${WEB_APP_URL}/docs/cli.`
44319
+ );
44320
+ }
44321
+ return await response.json();
44322
+ }
44323
+ var SHELF_FETCH_TIMEOUT_MS = 1e4;
44304
44324
  var LoticsClient = class {
44305
44325
  apiKey;
44306
44326
  workspaceId;
@@ -44358,8 +44378,8 @@ var LoticsClient = class {
44358
44378
  }
44359
44379
  return headers;
44360
44380
  }
44361
- async request(method, path14, body) {
44362
- const url2 = `${this.baseUrl}${path14}`;
44381
+ async request(method, path13, body) {
44382
+ const url2 = `${this.baseUrl}${path13}`;
44363
44383
  const headers = this.buildHeaders();
44364
44384
  const init = { method, headers };
44365
44385
  if (body !== void 0) {
@@ -45605,11 +45625,15 @@ function isNewerVersion(latest, current) {
45605
45625
  if (lMin !== cMin) return lMin > cMin;
45606
45626
  return lPat > cPat;
45607
45627
  }
45628
+ function updateCommand() {
45629
+ if (process.versions.bun === void 0) return "npm i -g @lotics/cli";
45630
+ return process.platform === "win32" ? "irm https://lotics.ai/install.ps1 | iex" : "curl -fsSL https://lotics.ai/install.sh | bash";
45631
+ }
45608
45632
  function printUpdateWarning(current, latest) {
45609
45633
  if (!isNewerVersion(latest, current)) return;
45610
45634
  console.error(`
45611
45635
  Update available: ${current} \u2192 ${latest}`);
45612
- console.error(`Run: npm i -g @lotics/cli
45636
+ console.error(`Run: ${updateCommand()}
45613
45637
  `);
45614
45638
  }
45615
45639
 
@@ -45672,17 +45696,12 @@ function resultSideEffects(result) {
45672
45696
  }
45673
45697
 
45674
45698
  // src/version.ts
45675
- import { readFileSync } from "node:fs";
45676
- import { fileURLToPath } from "node:url";
45677
- import path5 from "node:path";
45678
- var pkgPath = path5.resolve(path5.dirname(fileURLToPath(import.meta.url)), "../../package.json");
45679
- var pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
45680
- var VERSION = pkg.version;
45699
+ var VERSION = "0.171.0";
45681
45700
 
45682
45701
  // src/docs_command.ts
45683
45702
  import fs5 from "node:fs";
45684
- import path6 from "node:path";
45685
- import { fileURLToPath as fileURLToPath2 } from "node:url";
45703
+ import path5 from "node:path";
45704
+ import { fileURLToPath } from "node:url";
45686
45705
  var READING_ORDER = ["@lotics/app-sdk", "@lotics/ui", "@lotics/cli"];
45687
45706
  function firstHeading(file2) {
45688
45707
  let text;
@@ -45699,35 +45718,35 @@ function firstHeading(file2) {
45699
45718
  }
45700
45719
  function readVersion(pkgDir) {
45701
45720
  try {
45702
- const raw = fs5.readFileSync(path6.join(pkgDir, "package.json"), "utf-8");
45721
+ const raw = fs5.readFileSync(path5.join(pkgDir, "package.json"), "utf-8");
45703
45722
  return JSON.parse(raw).version ?? "?";
45704
45723
  } catch {
45705
45724
  return "?";
45706
45725
  }
45707
45726
  }
45708
45727
  function ownPackageDir() {
45709
- return path6.resolve(path6.dirname(fileURLToPath2(import.meta.url)), "../..");
45728
+ return path5.resolve(path5.dirname(fileURLToPath(import.meta.url)), "../..");
45710
45729
  }
45711
45730
  function referencePackageDirs(projectDir) {
45712
45731
  const found = /* @__PURE__ */ new Map();
45713
45732
  for (let dir = projectDir; ; ) {
45714
- const scope = path6.join(dir, "node_modules", "@lotics");
45733
+ const scope = path5.join(dir, "node_modules", "@lotics");
45715
45734
  if (fs5.existsSync(scope)) {
45716
45735
  for (const name2 of fs5.readdirSync(scope)) {
45717
- const pkgDir = path6.join(scope, name2);
45718
- const carries = fs5.existsSync(path6.join(pkgDir, "AGENTS.md")) || fs5.existsSync(path6.join(pkgDir, "docs"));
45736
+ const pkgDir = path5.join(scope, name2);
45737
+ const carries = fs5.existsSync(path5.join(pkgDir, "AGENTS.md")) || fs5.existsSync(path5.join(pkgDir, "docs"));
45719
45738
  if (carries && !found.has(`@lotics/${name2}`)) found.set(`@lotics/${name2}`, pkgDir);
45720
45739
  }
45721
45740
  }
45722
- const parent = path6.dirname(dir);
45741
+ const parent = path5.dirname(dir);
45723
45742
  if (parent === dir) break;
45724
45743
  dir = parent;
45725
45744
  }
45726
45745
  const own = ownPackageDir();
45727
- if (!found.has("@lotics/cli") && fs5.existsSync(path6.join(own, "AGENTS.md"))) {
45746
+ if (!found.has("@lotics/cli") && fs5.existsSync(path5.join(own, "AGENTS.md"))) {
45728
45747
  found.set("@lotics/cli", own);
45729
45748
  }
45730
- return [...found.entries()].map(([pkg2, dir]) => ({ pkg: pkg2, dir })).sort((a, b) => {
45749
+ return [...found.entries()].map(([pkg, dir]) => ({ pkg, dir })).sort((a, b) => {
45731
45750
  const ia = READING_ORDER.indexOf(a.pkg);
45732
45751
  const ib = READING_ORDER.indexOf(b.pkg);
45733
45752
  if (ia !== ib) return (ia < 0 ? READING_ORDER.length : ia) - (ib < 0 ? READING_ORDER.length : ib);
@@ -45736,26 +45755,26 @@ function referencePackageDirs(projectDir) {
45736
45755
  }
45737
45756
  function collectDocAreas(projectDir) {
45738
45757
  const found = [];
45739
- for (const { pkg: pkg2, dir } of referencePackageDirs(projectDir)) {
45758
+ for (const { pkg, dir } of referencePackageDirs(projectDir)) {
45740
45759
  const version2 = readVersion(dir);
45741
- const index = path6.join(dir, "AGENTS.md");
45760
+ const index = path5.join(dir, "AGENTS.md");
45742
45761
  if (fs5.existsSync(index)) {
45743
45762
  found.push({
45744
- area: pkg2.replace("@lotics/", ""),
45745
- pkg: pkg2,
45763
+ area: pkg.replace("@lotics/", ""),
45764
+ pkg,
45746
45765
  version: version2,
45747
45766
  absPath: index,
45748
45767
  title: firstHeading(index)
45749
45768
  });
45750
45769
  }
45751
- const docsDir = path6.join(dir, "docs");
45770
+ const docsDir = path5.join(dir, "docs");
45752
45771
  if (!fs5.existsSync(docsDir)) continue;
45753
45772
  for (const file2 of fs5.readdirSync(docsDir).sort()) {
45754
45773
  if (!file2.endsWith(".md")) continue;
45755
- const abs2 = path6.join(docsDir, file2);
45774
+ const abs2 = path5.join(docsDir, file2);
45756
45775
  found.push({
45757
45776
  area: file2.replace(/\.md$/, ""),
45758
- pkg: pkg2,
45777
+ pkg,
45759
45778
  version: version2,
45760
45779
  absPath: abs2,
45761
45780
  title: firstHeading(abs2)
@@ -45802,7 +45821,7 @@ ${a.pkg} ${a.version}`);
45802
45821
  return lines.join("\n");
45803
45822
  }
45804
45823
  function docsCommand(args) {
45805
- const projectDir = path6.resolve(args.projectDir ?? process.cwd());
45824
+ const projectDir = path5.resolve(args.projectDir ?? process.cwd());
45806
45825
  const areas = collectDocAreas(projectDir);
45807
45826
  if (args.area === void 0 || args.area === "") {
45808
45827
  console.log(renderDocIndex(areas));
@@ -45824,13 +45843,26 @@ function docsCommand(args) {
45824
45843
  process.exit(1);
45825
45844
  }
45826
45845
  const hit = hits[0];
45827
- console.error(`${hit.pkg} ${hit.version} \xB7 ${path6.relative(projectDir, hit.absPath)}
45846
+ console.error(`${hit.pkg} ${hit.version} \xB7 ${path5.relative(projectDir, hit.absPath)}
45828
45847
  `);
45829
45848
  process.stdout.write(fs5.readFileSync(hit.absPath, "utf-8"));
45830
45849
  }
45831
45850
 
45832
45851
  // src/commands.ts
45833
45852
  var COMMANDS = [
45853
+ {
45854
+ verbs: ["start"],
45855
+ help: [
45856
+ " lotics start <starter_id> [path] --email <you@co.com>",
45857
+ " First run, one command: create an account if this",
45858
+ " machine has none, then copy the starter into its",
45859
+ " workspace \u2014 schema, templates, knowledge docs,",
45860
+ " sample records, a deployed app \u2014 and print a",
45861
+ " one-time sign-in link. Drop --email to copy into",
45862
+ " the account you already have. --json prints one",
45863
+ " object and nothing else"
45864
+ ]
45865
+ },
45834
45866
  {
45835
45867
  verbs: ["org"],
45836
45868
  help: [
@@ -45859,7 +45891,9 @@ var COMMANDS = [
45859
45891
  {
45860
45892
  verbs: ["starter"],
45861
45893
  help: [
45862
- " lotics starter list Starters you can copy into this workspace",
45894
+ " lotics starter list Starters you can copy into this workspace. Works with",
45895
+ " NO account \u2014 it then lists the published shelf, so you",
45896
+ " can decide whether to copy one or build before signing up",
45863
45897
  " lotics starter show <starter_id> What one carries: tables, docs, templates, sample data",
45864
45898
  " lotics starter init <starter_id> [path]",
45865
45899
  " Copy it in \u2014 schema, templates, knowledge docs,",
@@ -46046,7 +46080,7 @@ function matchesKnownFlag(known, arg) {
46046
46080
 
46047
46081
  // src/report_command.ts
46048
46082
  import fs6 from "node:fs";
46049
- import path7 from "node:path";
46083
+ import path6 from "node:path";
46050
46084
  var LIMITS = { goal: 2e3, expected: 2e3, actual: 4e3, tried: 4e3, wanted: 2e3 };
46051
46085
  var REPORT_USAGE = `Usage: lotics report '<json>' lotics report @report.json \u2026 | lotics report -
46052
46086
 
@@ -46107,9 +46141,9 @@ function parseReport(raw) {
46107
46141
  }
46108
46142
  function appIdOf(projectDir) {
46109
46143
  try {
46110
- const raw = fs6.readFileSync(path7.join(projectDir, "package.json"), "utf-8");
46111
- const pkg2 = JSON.parse(raw);
46112
- const appId = pkg2.lotics?.app_id;
46144
+ const raw = fs6.readFileSync(path6.join(projectDir, "package.json"), "utf-8");
46145
+ const pkg = JSON.parse(raw);
46146
+ const appId = pkg.lotics?.app_id;
46113
46147
  return typeof appId === "string" && appId !== "" ? appId : void 0;
46114
46148
  } catch {
46115
46149
  return void 0;
@@ -46147,7 +46181,7 @@ async function reportCommand(client, options) {
46147
46181
 
46148
46182
  // src/app_commands.ts
46149
46183
  import fs8 from "node:fs";
46150
- import path10 from "node:path";
46184
+ import path9 from "node:path";
46151
46185
  import { spawn as spawn2 } from "node:child_process";
46152
46186
  import { createHash as createHash3 } from "node:crypto";
46153
46187
  import { tmpdir } from "node:os";
@@ -46914,7 +46948,7 @@ function escapeHtml(s) {
46914
46948
  import http from "node:http";
46915
46949
  import net from "node:net";
46916
46950
  import { existsSync } from "node:fs";
46917
- import path8 from "node:path";
46951
+ import path7 from "node:path";
46918
46952
  import { spawn } from "node:child_process";
46919
46953
 
46920
46954
  // src/child_env.ts
@@ -47498,6 +47532,9 @@ var STOP_DEADLINE_MS = 2e3;
47498
47532
  function serializeRpcResult(result) {
47499
47533
  return JSON.stringify(result ?? null);
47500
47534
  }
47535
+ function nodeForChildProcess() {
47536
+ return process.versions.bun === void 0 ? process.execPath : "node";
47537
+ }
47501
47538
  async function startDevServer(args) {
47502
47539
  const wrapperPort = await pickPort(args.port ?? DEFAULT_PORT);
47503
47540
  const vitePort = await pickPort(args.vitePort ?? DEFAULT_VITE_PORT, wrapperPort);
@@ -47509,14 +47546,14 @@ async function startDevServer(args) {
47509
47546
  vite_url: viteUrl,
47510
47547
  api_url: args.api_url
47511
47548
  });
47512
- const viteEntry = path8.join(args.projectDir, "node_modules", "vite", "bin", "vite.js");
47549
+ const viteEntry = path7.join(args.projectDir, "node_modules", "vite", "bin", "vite.js");
47513
47550
  if (!existsSync(viteEntry)) {
47514
47551
  throw new Error(
47515
47552
  `Vite is not installed in ${args.projectDir} \u2014 run \`npm install\` there first.`
47516
47553
  );
47517
47554
  }
47518
47555
  const viteChild = spawn(
47519
- process.execPath,
47556
+ nodeForChildProcess(),
47520
47557
  [viteEntry, "--port", String(vitePort), "--strictPort"],
47521
47558
  {
47522
47559
  cwd: args.projectDir,
@@ -47871,6 +47908,36 @@ function openBrowser(url2) {
47871
47908
  child.unref();
47872
47909
  }
47873
47910
 
47911
+ // src/output.ts
47912
+ var machineMode = false;
47913
+ var collectedWarnings = [];
47914
+ function setMachineOutput(on) {
47915
+ machineMode = on;
47916
+ }
47917
+ function isMachineOutput() {
47918
+ return machineMode;
47919
+ }
47920
+ function note(text) {
47921
+ if (machineMode) return;
47922
+ console.error(text);
47923
+ }
47924
+ function warn(text) {
47925
+ if (machineMode) {
47926
+ collectedWarnings.push(text);
47927
+ return;
47928
+ }
47929
+ console.error(text);
47930
+ }
47931
+ function emitJson(value2) {
47932
+ const payload = collectedWarnings.length > 0 ? { ...value2, warnings: [...collectedWarnings] } : value2;
47933
+ collectedWarnings.length = 0;
47934
+ console.log(JSON.stringify(payload, null, 2));
47935
+ }
47936
+ function flushPendingWarnings() {
47937
+ for (const text of collectedWarnings) console.error(text);
47938
+ collectedWarnings.length = 0;
47939
+ }
47940
+
47874
47941
  // ../shared/src/app_dts.ts
47875
47942
  var IDENTIFIER_REGEX = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
47876
47943
  function isValidIdentifier(name2) {
@@ -48437,7 +48504,7 @@ export type AppOptions = typeof OPT;
48437
48504
 
48438
48505
  // src/app_workflow_check.ts
48439
48506
  import fs7 from "node:fs";
48440
- import path9 from "node:path";
48507
+ import path8 from "node:path";
48441
48508
  import { createRequire } from "node:module";
48442
48509
 
48443
48510
  // ../shared/src/parse_workflow_js.ts
@@ -49209,10 +49276,10 @@ function mergeDefs(...defs) {
49209
49276
  function cloneDef(schema) {
49210
49277
  return mergeDefs(schema._zod.def);
49211
49278
  }
49212
- function getElementAtPath(obj, path14) {
49213
- if (!path14)
49279
+ function getElementAtPath(obj, path13) {
49280
+ if (!path13)
49214
49281
  return obj;
49215
- return path14.reduce((acc, key) => acc?.[key], obj);
49282
+ return path13.reduce((acc, key) => acc?.[key], obj);
49216
49283
  }
49217
49284
  function promiseAllObject(promisesObj) {
49218
49285
  const keys3 = Object.keys(promisesObj);
@@ -49621,11 +49688,11 @@ function explicitlyAborted(x2, startIndex = 0) {
49621
49688
  }
49622
49689
  return false;
49623
49690
  }
49624
- function prefixIssues(path14, issues) {
49691
+ function prefixIssues(path13, issues) {
49625
49692
  return issues.map((iss) => {
49626
49693
  var _a4;
49627
49694
  (_a4 = iss).path ?? (_a4.path = []);
49628
- iss.path.unshift(path14);
49695
+ iss.path.unshift(path13);
49629
49696
  return iss;
49630
49697
  });
49631
49698
  }
@@ -49772,16 +49839,16 @@ function flattenError(error52, mapper = (issue2) => issue2.message) {
49772
49839
  }
49773
49840
  function formatError(error52, mapper = (issue2) => issue2.message) {
49774
49841
  const fieldErrors = { _errors: [] };
49775
- const processError = (error53, path14 = []) => {
49842
+ const processError = (error53, path13 = []) => {
49776
49843
  for (const issue2 of error53.issues) {
49777
49844
  if (issue2.code === "invalid_union" && issue2.errors.length) {
49778
- issue2.errors.map((issues) => processError({ issues }, [...path14, ...issue2.path]));
49845
+ issue2.errors.map((issues) => processError({ issues }, [...path13, ...issue2.path]));
49779
49846
  } else if (issue2.code === "invalid_key") {
49780
- processError({ issues: issue2.issues }, [...path14, ...issue2.path]);
49847
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
49781
49848
  } else if (issue2.code === "invalid_element") {
49782
- processError({ issues: issue2.issues }, [...path14, ...issue2.path]);
49849
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
49783
49850
  } else {
49784
- const fullpath = [...path14, ...issue2.path];
49851
+ const fullpath = [...path13, ...issue2.path];
49785
49852
  if (fullpath.length === 0) {
49786
49853
  fieldErrors._errors.push(mapper(issue2));
49787
49854
  } else {
@@ -49808,17 +49875,17 @@ function formatError(error52, mapper = (issue2) => issue2.message) {
49808
49875
  }
49809
49876
  function treeifyError(error52, mapper = (issue2) => issue2.message) {
49810
49877
  const result = { errors: [] };
49811
- const processError = (error53, path14 = []) => {
49878
+ const processError = (error53, path13 = []) => {
49812
49879
  var _a4, _b2;
49813
49880
  for (const issue2 of error53.issues) {
49814
49881
  if (issue2.code === "invalid_union" && issue2.errors.length) {
49815
- issue2.errors.map((issues) => processError({ issues }, [...path14, ...issue2.path]));
49882
+ issue2.errors.map((issues) => processError({ issues }, [...path13, ...issue2.path]));
49816
49883
  } else if (issue2.code === "invalid_key") {
49817
- processError({ issues: issue2.issues }, [...path14, ...issue2.path]);
49884
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
49818
49885
  } else if (issue2.code === "invalid_element") {
49819
- processError({ issues: issue2.issues }, [...path14, ...issue2.path]);
49886
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
49820
49887
  } else {
49821
- const fullpath = [...path14, ...issue2.path];
49888
+ const fullpath = [...path13, ...issue2.path];
49822
49889
  if (fullpath.length === 0) {
49823
49890
  result.errors.push(mapper(issue2));
49824
49891
  continue;
@@ -49850,8 +49917,8 @@ function treeifyError(error52, mapper = (issue2) => issue2.message) {
49850
49917
  }
49851
49918
  function toDotPath(_path) {
49852
49919
  const segs = [];
49853
- const path14 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
49854
- for (const seg of path14) {
49920
+ const path13 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
49921
+ for (const seg of path13) {
49855
49922
  if (typeof seg === "number")
49856
49923
  segs.push(`[${seg}]`);
49857
49924
  else if (typeof seg === "symbol")
@@ -62543,13 +62610,13 @@ function resolveRef(ref2, ctx) {
62543
62610
  if (!ref2.startsWith("#")) {
62544
62611
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
62545
62612
  }
62546
- const path14 = ref2.slice(1).split("/").filter(Boolean);
62547
- if (path14.length === 0) {
62613
+ const path13 = ref2.slice(1).split("/").filter(Boolean);
62614
+ if (path13.length === 0) {
62548
62615
  return ctx.rootSchema;
62549
62616
  }
62550
62617
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
62551
- if (path14[0] === defsKey) {
62552
- const key = path14[1];
62618
+ if (path13[0] === defsKey) {
62619
+ const key = path13[1];
62553
62620
  if (!key || !ctx.defs[key]) {
62554
62621
  throw new Error(`Reference not found: ${ref2}`);
62555
62622
  }
@@ -67305,9 +67372,9 @@ __export(expression_object_exports, {
67305
67372
  pick: () => pick3,
67306
67373
  values: () => values2
67307
67374
  });
67308
- function getNestedValue(obj, path14) {
67375
+ function getNestedValue(obj, path13) {
67309
67376
  if (obj == null) return void 0;
67310
- const parts = path14.split(".");
67377
+ const parts = path13.split(".");
67311
67378
  let value2 = obj;
67312
67379
  for (const part of parts) {
67313
67380
  if (value2 == null) return void 0;
@@ -67346,11 +67413,11 @@ function entries(obj) {
67346
67413
  }
67347
67414
  return Object.entries(obj);
67348
67415
  }
67349
- function get(obj, path14, defaultValue) {
67350
- if (typeof path14 !== "string") {
67416
+ function get(obj, path13, defaultValue) {
67417
+ if (typeof path13 !== "string") {
67351
67418
  throw new Error("get: path must be a string");
67352
67419
  }
67353
- const value2 = getNestedValue(obj, path14);
67420
+ const value2 = getNestedValue(obj, path13);
67354
67421
  return value2 !== void 0 ? value2 : defaultValue;
67355
67422
  }
67356
67423
  function pick3(obj, fields) {
@@ -69246,17 +69313,17 @@ function readFromChanges(chain2) {
69246
69313
  if (first3.kind === "bracket-number" || first3.kind === "bracket-expr") {
69247
69314
  fail(first3.node, "changes is keyed by field name, not numeric index.");
69248
69315
  }
69249
- const path14 = [{ at: "key", key: first3.key }];
69316
+ const path13 = [{ at: "key", key: first3.key }];
69250
69317
  for (const seg of segments.slice(1)) {
69251
69318
  if (seg.kind === "bracket-number") {
69252
- path14.push({ at: "index", index: seg.index });
69319
+ path13.push({ at: "index", index: seg.index });
69253
69320
  } else if (seg.kind === "bracket-expr") {
69254
- path14.push({ at: "index", index: seg.expr });
69321
+ path13.push({ at: "index", index: seg.expr });
69255
69322
  } else {
69256
- path14.push({ at: "key", key: seg.key });
69323
+ path13.push({ at: "key", key: seg.key });
69257
69324
  }
69258
69325
  }
69259
- return { kind: "read", source: { from: "trigger_changes" }, path: path14 };
69326
+ return { kind: "read", source: { from: "trigger_changes" }, path: path13 };
69260
69327
  });
69261
69328
  }
69262
69329
  var TRIGGER_RECORD_VIEWS = /* @__PURE__ */ new Map([
@@ -69274,17 +69341,17 @@ function readFromTrigger(chain2) {
69274
69341
  return view === "trigger_changes" ? readFromChanges(lowered) : readFromRecord(view, lowered);
69275
69342
  }
69276
69343
  return wrapOptionalGuards(chain2, (segments) => {
69277
- const path14 = [];
69344
+ const path13 = [];
69278
69345
  for (const seg of segments) {
69279
69346
  if (seg.kind === "bracket-number") {
69280
- path14.push({ at: "index", index: seg.index });
69347
+ path13.push({ at: "index", index: seg.index });
69281
69348
  } else if (seg.kind === "bracket-expr") {
69282
- path14.push({ at: "index", index: seg.expr });
69349
+ path13.push({ at: "index", index: seg.expr });
69283
69350
  } else {
69284
- path14.push({ at: "key", key: seg.key });
69351
+ path13.push({ at: "key", key: seg.key });
69285
69352
  }
69286
69353
  }
69287
- return { kind: "read", source: { from: "trigger" }, path: path14 };
69354
+ return { kind: "read", source: { from: "trigger" }, path: path13 };
69288
69355
  });
69289
69356
  }
69290
69357
  function readFromRuntime(chain2) {
@@ -69306,17 +69373,17 @@ function readFromRuntime(chain2) {
69306
69373
  const parked = PARKED_RUNTIME_KEYS.get(key);
69307
69374
  if (parked) fail(first3.node, parked, parkedRuntimeKeyConstruct(key));
69308
69375
  const source = { from: "runtime", key };
69309
- const path14 = [];
69376
+ const path13 = [];
69310
69377
  for (const seg of segments.slice(1)) {
69311
69378
  if (seg.kind === "bracket-number") {
69312
- path14.push({ at: "index", index: seg.index });
69379
+ path13.push({ at: "index", index: seg.index });
69313
69380
  } else if (seg.kind === "bracket-expr") {
69314
- path14.push({ at: "index", index: seg.expr });
69381
+ path13.push({ at: "index", index: seg.expr });
69315
69382
  } else {
69316
- path14.push({ at: "key", key: seg.key });
69383
+ path13.push({ at: "key", key: seg.key });
69317
69384
  }
69318
69385
  }
69319
- return { kind: "read", source, path: path14 };
69386
+ return { kind: "read", source, path: path13 };
69320
69387
  });
69321
69388
  }
69322
69389
  function readFromForeachItem(name2, chain2) {
@@ -69355,17 +69422,17 @@ function readFromLexical(name2, chain2) {
69355
69422
  }
69356
69423
  function readFromStepOutput(stepId, chain2) {
69357
69424
  return wrapOptionalGuards(chain2, (segments) => {
69358
- const path14 = [];
69425
+ const path13 = [];
69359
69426
  for (const seg of segments) {
69360
69427
  if (seg.kind === "bracket-number") {
69361
- path14.push({ at: "index", index: seg.index });
69428
+ path13.push({ at: "index", index: seg.index });
69362
69429
  } else if (seg.kind === "bracket-expr") {
69363
- path14.push({ at: "index", index: seg.expr });
69430
+ path13.push({ at: "index", index: seg.expr });
69364
69431
  } else {
69365
- path14.push({ at: "key", key: seg.key });
69432
+ path13.push({ at: "key", key: seg.key });
69366
69433
  }
69367
69434
  }
69368
- return { kind: "read", source: { from: "step_output", step_id: stepId }, path: path14 };
69435
+ return { kind: "read", source: { from: "step_output", step_id: stepId }, path: path13 };
69369
69436
  });
69370
69437
  }
69371
69438
  function classifyRecordPath(segs) {
@@ -71038,7 +71105,7 @@ function subsetIssues(bodySource, offset) {
71038
71105
  ];
71039
71106
  }
71040
71107
  async function loadProjectTypescript(projectDir) {
71041
- const requireFromProject = createRequire(path9.join(projectDir, "package.json"));
71108
+ const requireFromProject = createRequire(path8.join(projectDir, "package.json"));
71042
71109
  let resolved;
71043
71110
  try {
71044
71111
  resolved = requireFromProject.resolve("typescript");
@@ -71051,9 +71118,9 @@ async function loadProjectTypescript(projectDir) {
71051
71118
  return mod2.default ?? mod2;
71052
71119
  }
71053
71120
  function pathToImportUrl(p) {
71054
- const resolved = path9.resolve(p);
71121
+ const resolved = path8.resolve(p);
71055
71122
  const prefixed = resolved.startsWith("/") ? resolved : `/${resolved}`;
71056
- return `file://${prefixed.split(path9.sep).join("/")}`;
71123
+ return `file://${prefixed.split(path8.sep).join("/")}`;
71057
71124
  }
71058
71125
  function checkOneWorkflowBody(tsApi, input) {
71059
71126
  const wrapped = fs7.readFileSync(input.bodyPath, "utf-8");
@@ -71103,7 +71170,7 @@ function checkOneWorkflowBody(tsApi, input) {
71103
71170
  }
71104
71171
  function createCheckedSourceHost(tsApi, options, bodyPath, bodyText) {
71105
71172
  const base = tsApi.createCompilerHost(options, false);
71106
- const key = (fileName) => base.getCanonicalFileName(path9.resolve(fileName).replace(/\\/g, "/"));
71173
+ const key = (fileName) => base.getCanonicalFileName(path8.resolve(fileName).replace(/\\/g, "/"));
71107
71174
  const bodyKey = key(bodyPath);
71108
71175
  return {
71109
71176
  ...base,
@@ -71229,10 +71296,10 @@ function syncedEntry(entry) {
71229
71296
  }
71230
71297
  function readSynced(projectDir) {
71231
71298
  try {
71232
- const pkg2 = JSON.parse(
71233
- fs8.readFileSync(path10.join(projectDir, "package.json"), "utf-8")
71299
+ const pkg = JSON.parse(
71300
+ fs8.readFileSync(path9.join(projectDir, "package.json"), "utf-8")
71234
71301
  );
71235
- const raw = pkg2.lotics?.synced ?? {};
71302
+ const raw = pkg.lotics?.synced ?? {};
71236
71303
  return {
71237
71304
  workflows: normalizeSyncedMap(raw.workflows),
71238
71305
  agents: normalizeSyncedMap(raw.agents),
@@ -71251,19 +71318,19 @@ function normalizeSyncedMap(map3) {
71251
71318
  );
71252
71319
  }
71253
71320
  function writeSynced(projectDir, kind, alias, entry) {
71254
- const pkgPath2 = path10.join(projectDir, "package.json");
71255
- const pkg2 = JSON.parse(fs8.readFileSync(pkgPath2, "utf-8"));
71256
- if (!pkg2.lotics) return;
71257
- pkg2.lotics.synced ??= {};
71258
- pkg2.lotics.synced[kind] = { ...pkg2.lotics.synced[kind], [alias]: entry };
71259
- fs8.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
71321
+ const pkgPath = path9.join(projectDir, "package.json");
71322
+ const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
71323
+ if (!pkg.lotics) return;
71324
+ pkg.lotics.synced ??= {};
71325
+ pkg.lotics.synced[kind] = { ...pkg.lotics.synced[kind], [alias]: entry };
71326
+ fs8.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
71260
71327
  }
71261
71328
  function readManifestEntry(projectDir, kind, alias) {
71262
- const pkg2 = readManifest(projectDir);
71263
- if (!pkg2.lotics) return { found: false };
71329
+ const pkg = readManifest(projectDir);
71330
+ if (!pkg.lotics) return { found: false };
71264
71331
  const entry = { found: true };
71265
71332
  let found = false;
71266
- const declarations = pkg2.lotics[kind];
71333
+ const declarations = pkg.lotics[kind];
71267
71334
  if (declarations && typeof declarations === "object" && !Array.isArray(declarations)) {
71268
71335
  const map3 = declarations;
71269
71336
  if (alias in map3) {
@@ -71271,7 +71338,7 @@ function readManifestEntry(projectDir, kind, alias) {
71271
71338
  found = true;
71272
71339
  }
71273
71340
  }
71274
- const baselines = pkg2.lotics.synced?.[kind];
71341
+ const baselines = pkg.lotics.synced?.[kind];
71275
71342
  if (baselines && alias in baselines) {
71276
71343
  entry.synced = baselines[alias];
71277
71344
  found = true;
@@ -71279,31 +71346,31 @@ function readManifestEntry(projectDir, kind, alias) {
71279
71346
  return found ? entry : { found: false };
71280
71347
  }
71281
71348
  function readManifest(projectDir) {
71282
- const pkgPath2 = path10.join(projectDir, "package.json");
71283
- return JSON.parse(fs8.readFileSync(pkgPath2, "utf-8"));
71349
+ const pkgPath = path9.join(projectDir, "package.json");
71350
+ return JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
71284
71351
  }
71285
71352
  function removeFromManifest(projectDir, kind, alias) {
71286
- const pkgPath2 = path10.join(projectDir, "package.json");
71287
- const pkg2 = readManifest(projectDir);
71288
- if (!pkg2.lotics) return;
71289
- const declarations = pkg2.lotics[kind];
71353
+ const pkgPath = path9.join(projectDir, "package.json");
71354
+ const pkg = readManifest(projectDir);
71355
+ if (!pkg.lotics) return;
71356
+ const declarations = pkg.lotics[kind];
71290
71357
  if (declarations && typeof declarations === "object" && !Array.isArray(declarations)) {
71291
71358
  delete declarations[alias];
71292
71359
  }
71293
- const baselines = pkg2.lotics.synced?.[kind];
71360
+ const baselines = pkg.lotics.synced?.[kind];
71294
71361
  if (baselines) delete baselines[alias];
71295
- fs8.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
71362
+ fs8.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
71296
71363
  }
71297
71364
  function parkPrunedDeclaration(projectDir, kind, alias, removed) {
71298
- const dir = path10.join(projectDir, ".lotics", "pruned", kind);
71365
+ const dir = path9.join(projectDir, ".lotics", "pruned", kind);
71299
71366
  fs8.mkdirSync(dir, { recursive: true });
71300
- const file2 = path10.join(dir, `${alias}.json`);
71367
+ const file2 = path9.join(dir, `${alias}.json`);
71301
71368
  fs8.writeFileSync(file2, JSON.stringify(removed, null, 2) + "\n");
71302
- return path10.relative(projectDir, file2);
71369
+ return path9.relative(projectDir, file2);
71303
71370
  }
71304
- var WORKFLOWS_DIR = path10.join("src", "workflows");
71305
- var WORKFLOW_GLOBALS_DIR = path10.join(".lotics", "workflows");
71306
- var AGENTS_DIR = path10.join("src", "agents");
71371
+ var WORKFLOWS_DIR = path9.join("src", "workflows");
71372
+ var WORKFLOW_GLOBALS_DIR = path9.join(".lotics", "workflows");
71373
+ var AGENTS_DIR = path9.join("src", "agents");
71307
71374
  var WORKFLOW_TSCONFIG_EXCLUDES = ["src/workflows", ".lotics/workflows"];
71308
71375
  var LOTICS_INCLUDE_GLOB = ".lotics/**/*";
71309
71376
  var STALE_LOTICS_INCLUDES = /* @__PURE__ */ new Set([".lotics", "./.lotics", ".lotics/"]);
@@ -71314,7 +71381,7 @@ function isMissingWorkflowBodyError(error52) {
71314
71381
  return /has no step source|cannot be rendered|unsupported step/i.test(error52);
71315
71382
  }
71316
71383
  function workflowFileHeader(alias) {
71317
- const refPath = path10.join("..", "..", WORKFLOW_GLOBALS_DIR, `${alias}.globals.d.ts`).split(path10.sep).join("/");
71384
+ const refPath = path9.join("..", "..", WORKFLOW_GLOBALS_DIR, `${alias}.globals.d.ts`).split(path9.sep).join("/");
71318
71385
  return (
71319
71386
  // The scaffold ships oxlint, whose `triple-slash-reference` rule flags the
71320
71387
  // line below — so every pulled body arrived with a warning its author could
@@ -71337,27 +71404,27 @@ export {};
71337
71404
  );
71338
71405
  }
71339
71406
  function workflowFilePath(projectDir, alias) {
71340
- return path10.join(projectDir, WORKFLOWS_DIR, `${alias}.ts`);
71407
+ return path9.join(projectDir, WORKFLOWS_DIR, `${alias}.ts`);
71341
71408
  }
71342
71409
  var GLOBALS_SUFFIX = ".globals.d.ts";
71343
71410
  function workflowGlobalsPath(projectDir, alias) {
71344
- return path10.join(projectDir, WORKFLOW_GLOBALS_DIR, `${alias}${GLOBALS_SUFFIX}`);
71411
+ return path9.join(projectDir, WORKFLOW_GLOBALS_DIR, `${alias}${GLOBALS_SUFFIX}`);
71345
71412
  }
71346
71413
  function pruneOrphanWorkflowGlobals(projectDir, declared) {
71347
- const dir = path10.join(projectDir, WORKFLOW_GLOBALS_DIR);
71414
+ const dir = path9.join(projectDir, WORKFLOW_GLOBALS_DIR);
71348
71415
  if (!fs8.existsSync(dir)) return [];
71349
71416
  const removed = [];
71350
71417
  for (const entry of fs8.readdirSync(dir)) {
71351
71418
  if (!entry.endsWith(GLOBALS_SUFFIX)) continue;
71352
71419
  const alias = entry.slice(0, -GLOBALS_SUFFIX.length);
71353
71420
  if (declared.has(alias)) continue;
71354
- fs8.unlinkSync(path10.join(dir, entry));
71421
+ fs8.unlinkSync(path9.join(dir, entry));
71355
71422
  removed.push(alias);
71356
71423
  }
71357
71424
  return removed.sort();
71358
71425
  }
71359
71426
  function orphanWorkflowBodies(projectDir, declared) {
71360
- const dir = path10.join(projectDir, WORKFLOWS_DIR);
71427
+ const dir = path9.join(projectDir, WORKFLOWS_DIR);
71361
71428
  if (!fs8.existsSync(dir)) return [];
71362
71429
  return fs8.readdirSync(dir).filter((e) => e.endsWith(".ts") && !e.endsWith(".d.ts") && !e.endsWith(".test.ts")).map((e) => e.slice(0, -".ts".length)).filter((alias) => !declared.has(alias)).sort();
71363
71430
  }
@@ -71395,7 +71462,7 @@ function staleWorkflowGlobals(projectDir, workflows) {
71395
71462
  return out;
71396
71463
  }
71397
71464
  function writeWorkflowGlobals(projectDir, alias, dts, declaration) {
71398
- const dir = path10.join(projectDir, WORKFLOW_GLOBALS_DIR);
71465
+ const dir = path9.join(projectDir, WORKFLOW_GLOBALS_DIR);
71399
71466
  fs8.mkdirSync(dir, { recursive: true });
71400
71467
  const file2 = workflowGlobalsPath(projectDir, alias);
71401
71468
  const stamp = `${GLOBALS_STAMP_PREFIX}${workflowDeclarationStamp(declaration)}`;
@@ -71408,7 +71475,7 @@ function normalizeWorkflowBody(source) {
71408
71475
  return source.replace(/\s+$/, "");
71409
71476
  }
71410
71477
  function writeWorkflowFile(projectDir, alias, source, envelope2 = { prefix: WORKFLOW_ENVELOPE_PREFIX, suffix: WORKFLOW_ENVELOPE_SUFFIX }) {
71411
- const dir = path10.join(projectDir, WORKFLOWS_DIR);
71478
+ const dir = path9.join(projectDir, WORKFLOWS_DIR);
71412
71479
  fs8.mkdirSync(dir, { recursive: true });
71413
71480
  const file2 = workflowFilePath(projectDir, alias);
71414
71481
  const body = normalizeWorkflowBody(source);
@@ -71418,14 +71485,14 @@ ${envelope2.prefix}${body}${envelope2.suffix}
71418
71485
  return file2;
71419
71486
  }
71420
71487
  function agentFilePath2(projectDir, alias) {
71421
- return path10.join(projectDir, ...agentFilePath(alias).split("/"));
71488
+ return path9.join(projectDir, ...agentFilePath(alias).split("/"));
71422
71489
  }
71423
71490
  var CLI_AGENT_NOTES = [
71424
71491
  "Pulled from the LIVE app row; edit here, then: lotics app agent set <alias>",
71425
71492
  "The typed fields (inputs/outputs/tool_names/model_tier) live in package.json#lotics.agents"
71426
71493
  ];
71427
71494
  function writeAgentFile(projectDir, alias, instructions) {
71428
- fs8.mkdirSync(path10.join(projectDir, AGENTS_DIR), { recursive: true });
71495
+ fs8.mkdirSync(path9.join(projectDir, AGENTS_DIR), { recursive: true });
71429
71496
  const file2 = agentFilePath2(projectDir, alias);
71430
71497
  fs8.writeFileSync(
71431
71498
  file2,
@@ -71451,15 +71518,15 @@ function writeAgentFiles(projectDir, agents, opts) {
71451
71518
  return written;
71452
71519
  }
71453
71520
  function parkLiveAgentProse(projectDir, alias, instructions) {
71454
- const dir = path10.join(projectDir, ".lotics", "agents");
71455
- const file2 = path10.join(dir, `${alias}.live.md`);
71521
+ const dir = path9.join(projectDir, ".lotics", "agents");
71522
+ const file2 = path9.join(dir, `${alias}.live.md`);
71456
71523
  try {
71457
71524
  fs8.mkdirSync(dir, { recursive: true });
71458
71525
  fs8.writeFileSync(file2, normalizeAgentInstructions(instructions) + "\n");
71459
- return path10.relative(projectDir, file2);
71526
+ return path9.relative(projectDir, file2);
71460
71527
  } catch (err2) {
71461
71528
  console.error(
71462
- `\u26A0 Could not write the live prose for ${alias} to ${path10.relative(projectDir, file2)}: ${err2 instanceof Error ? err2.message : String(err2)}`
71529
+ `\u26A0 Could not write the live prose for ${alias} to ${path9.relative(projectDir, file2)}: ${err2 instanceof Error ? err2.message : String(err2)}`
71463
71530
  );
71464
71531
  return null;
71465
71532
  }
@@ -71475,15 +71542,15 @@ function reportUnseenLive(kind, unseen, appId) {
71475
71542
  );
71476
71543
  }
71477
71544
  function parkLiveWorkflowBody(projectDir, alias, source) {
71478
- const dir = path10.join(projectDir, WORKFLOW_GLOBALS_DIR);
71479
- const file2 = path10.join(dir, `${alias}.live.ts`);
71545
+ const dir = path9.join(projectDir, WORKFLOW_GLOBALS_DIR);
71546
+ const file2 = path9.join(dir, `${alias}.live.ts`);
71480
71547
  try {
71481
71548
  fs8.mkdirSync(dir, { recursive: true });
71482
71549
  fs8.writeFileSync(file2, normalizeWorkflowBody(source) + "\n");
71483
- return path10.relative(projectDir, file2);
71550
+ return path9.relative(projectDir, file2);
71484
71551
  } catch (err2) {
71485
71552
  console.error(
71486
- `\u26A0 Could not write the live body for ${alias} to ${path10.relative(projectDir, file2)}: ${err2 instanceof Error ? err2.message : String(err2)}`
71553
+ `\u26A0 Could not write the live body for ${alias} to ${path9.relative(projectDir, file2)}: ${err2 instanceof Error ? err2.message : String(err2)}`
71487
71554
  );
71488
71555
  return null;
71489
71556
  }
@@ -71573,12 +71640,12 @@ async function writeWorkflowFiles(client, projectDir, app_id, appName, workflows
71573
71640
  return { written, kept, unseenLive };
71574
71641
  }
71575
71642
  function writeWorkflowDescription(projectDir, alias, description) {
71576
- const pkgPath2 = path10.join(projectDir, "package.json");
71577
- const pkg2 = JSON.parse(fs8.readFileSync(pkgPath2, "utf-8"));
71578
- const declaration = pkg2.lotics?.workflows?.[alias];
71643
+ const pkgPath = path9.join(projectDir, "package.json");
71644
+ const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
71645
+ const declaration = pkg.lotics?.workflows?.[alias];
71579
71646
  if (!declaration) return;
71580
71647
  declaration.description = description;
71581
- fs8.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
71648
+ fs8.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
71582
71649
  }
71583
71650
  async function fetchWorkflowGlobals(client, projectDir, app_id, alias, declaration) {
71584
71651
  try {
@@ -71617,62 +71684,101 @@ function runTar(args, cwd) {
71617
71684
  });
71618
71685
  }
71619
71686
  function runNpm(args, cwd) {
71687
+ const quiet = isMachineOutput();
71620
71688
  return new Promise((resolve2, reject2) => {
71621
- const proc = spawn2("npm", args, { cwd, stdio: "inherit", env: ipv4ChildEnv(process.env) });
71689
+ const proc = spawn2("npm", args, {
71690
+ cwd,
71691
+ // On Windows `npm` is `npm.cmd`, a batch file Node will not execute
71692
+ // without a shell — bare `spawn("npm")` dies at ENOENT, which took every
71693
+ // command that installs or builds with it. A shell is safe here because
71694
+ // the arguments are literals this file writes (`run build`, `ci
71695
+ // --ignore-scripts`); nothing a caller supplies reaches the command line.
71696
+ //
71697
+ // The alternative — run npm's own JS entry with `process.execPath`, as
71698
+ // `dev/server.ts` does for vite — works there because vite sits at a known
71699
+ // path inside the project. npm does not: nvm-windows, volta, corepack and
71700
+ // the installer each put it somewhere different, and letting the platform
71701
+ // resolve its own shim is the one approach that finds all of them.
71702
+ shell: process.platform === "win32",
71703
+ stdio: quiet ? ["ignore", "pipe", "pipe"] : "inherit",
71704
+ env: ipv4ChildEnv(process.env)
71705
+ });
71706
+ let captured = "";
71707
+ if (quiet) {
71708
+ const collect = (chunk) => {
71709
+ captured += chunk.toString();
71710
+ if (captured.length > NPM_LOG_TAIL_BYTES * 2) {
71711
+ captured = captured.slice(-NPM_LOG_TAIL_BYTES);
71712
+ }
71713
+ };
71714
+ proc.stdout?.on("data", collect);
71715
+ proc.stderr?.on("data", collect);
71716
+ }
71622
71717
  proc.on("error", reject2);
71623
71718
  proc.on("exit", (code) => {
71624
- if (code === 0) resolve2();
71625
- else reject2(new Error(`npm ${args.join(" ")} exited with code ${code}`));
71719
+ if (code === 0) {
71720
+ resolve2();
71721
+ return;
71722
+ }
71723
+ const tail = captured.slice(-NPM_LOG_TAIL_BYTES).trim();
71724
+ reject2(
71725
+ new Error(
71726
+ `npm ${args.join(" ")} exited with code ${code}` + (tail === "" ? "" : `
71727
+ --- npm output ---
71728
+ ${tail}`)
71729
+ )
71730
+ );
71626
71731
  });
71627
71732
  });
71628
71733
  }
71734
+ var NPM_LOG_TAIL_BYTES = 8e3;
71629
71735
  function npmInstallArgs(hasLockfile) {
71630
71736
  return [hasLockfile ? "ci" : "install", "--ignore-scripts"];
71631
71737
  }
71632
71738
  function runNpmInstall(cwd) {
71633
- const hasLockfile = fs8.existsSync(path10.join(cwd, "package-lock.json"));
71634
- console.error(
71739
+ const hasLockfile = fs8.existsSync(path9.join(cwd, "package-lock.json"));
71740
+ note(
71635
71741
  hasLockfile ? "Installing npm dependencies (npm ci \u2014 the lockfile's exact tree, no install scripts)..." : "Installing npm dependencies (no lockfile yet \u2014 resolving ranges, no install scripts)..."
71636
71742
  );
71637
71743
  return runNpm(npmInstallArgs(hasLockfile), cwd);
71638
71744
  }
71639
71745
  function readAppWorkspaceId(projectDir) {
71640
71746
  try {
71641
- const pkgPath2 = path10.join(projectDir, "package.json");
71642
- if (!fs8.existsSync(pkgPath2)) return null;
71643
- const pkg2 = JSON.parse(fs8.readFileSync(pkgPath2, "utf-8"));
71644
- return pkg2.lotics?.workspace_id ?? null;
71747
+ const pkgPath = path9.join(projectDir, "package.json");
71748
+ if (!fs8.existsSync(pkgPath)) return null;
71749
+ const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
71750
+ return pkg.lotics?.workspace_id ?? null;
71645
71751
  } catch {
71646
71752
  return null;
71647
71753
  }
71648
71754
  }
71649
71755
  function readAppMeta(projectDir) {
71650
- const pkgPath2 = path10.join(projectDir, "package.json");
71651
- if (!fs8.existsSync(pkgPath2)) {
71756
+ const pkgPath = path9.join(projectDir, "package.json");
71757
+ if (!fs8.existsSync(pkgPath)) {
71652
71758
  throw new Error(`No package.json found at ${projectDir}. Run 'lotics app create' first.`);
71653
71759
  }
71654
- const pkg2 = JSON.parse(fs8.readFileSync(pkgPath2, "utf-8"));
71655
- if (!pkg2.lotics?.app_id || !pkg2.lotics.workspace_id) {
71760
+ const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
71761
+ if (!pkg.lotics?.app_id || !pkg.lotics.workspace_id) {
71656
71762
  throw new Error(
71657
71763
  `package.json is missing the 'lotics' field with app_id and workspace_id. Was this folder created with 'lotics app create'?`
71658
71764
  );
71659
71765
  }
71660
- noteScope({ app_id: pkg2.lotics.app_id });
71766
+ noteScope({ app_id: pkg.lotics.app_id });
71661
71767
  return {
71662
- app_id: pkg2.lotics.app_id,
71663
- workspace_id: pkg2.lotics.workspace_id,
71664
- current_version_id: pkg2.lotics.current_version_id ?? null,
71665
- version_number: pkg2.lotics.version_number ?? null,
71666
- workflows: pkg2.lotics.workflows ?? {},
71667
- queries: pkg2.lotics.queries ?? {},
71668
- agents: pkg2.lotics.agents ?? {},
71669
- capabilities: pkg2.lotics.capabilities
71768
+ app_id: pkg.lotics.app_id,
71769
+ workspace_id: pkg.lotics.workspace_id,
71770
+ current_version_id: pkg.lotics.current_version_id ?? null,
71771
+ version_number: pkg.lotics.version_number ?? null,
71772
+ workflows: pkg.lotics.workflows ?? {},
71773
+ queries: pkg.lotics.queries ?? {},
71774
+ agents: pkg.lotics.agents ?? {},
71775
+ capabilities: pkg.lotics.capabilities
71670
71776
  };
71671
71777
  }
71672
71778
  function writeAppMeta(projectDir, meta3) {
71673
- const pkgPath2 = path10.join(projectDir, "package.json");
71674
- const pkg2 = JSON.parse(fs8.readFileSync(pkgPath2, "utf-8"));
71675
- const existing = pkg2.lotics && typeof pkg2.lotics === "object" ? pkg2.lotics : {};
71779
+ const pkgPath = path9.join(projectDir, "package.json");
71780
+ const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
71781
+ const existing = pkg.lotics && typeof pkg.lotics === "object" ? pkg.lotics : {};
71676
71782
  const supplied = new Set(
71677
71783
  Object.entries(meta3).filter(([, value2]) => value2 !== void 0).map(([key]) => key)
71678
71784
  );
@@ -71680,20 +71786,20 @@ function writeAppMeta(projectDir, meta3) {
71680
71786
  for (const [key, value2] of Object.entries(existing)) {
71681
71787
  if (!supplied.has(key)) preserved[key] = value2;
71682
71788
  }
71683
- pkg2.lotics = { ...meta3, ...preserved };
71684
- fs8.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
71789
+ pkg.lotics = { ...meta3, ...preserved };
71790
+ fs8.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
71685
71791
  }
71686
71792
  function writeWorkflowOutputs(projectDir, alias, outputs) {
71687
- const pkgPath2 = path10.join(projectDir, "package.json");
71688
- const pkg2 = JSON.parse(fs8.readFileSync(pkgPath2, "utf-8"));
71689
- const declaration = pkg2.lotics?.workflows?.[alias];
71793
+ const pkgPath = path9.join(projectDir, "package.json");
71794
+ const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
71795
+ const declaration = pkg.lotics?.workflows?.[alias];
71690
71796
  if (!declaration) {
71691
71797
  throw new Error(
71692
71798
  `package.json#lotics.workflows.${alias} disappeared before the derived-outputs write-back.`
71693
71799
  );
71694
71800
  }
71695
71801
  declaration.outputs = outputs;
71696
- fs8.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
71802
+ fs8.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
71697
71803
  }
71698
71804
  function workflowBodyDrift(projectDir, workflows, synced = {}) {
71699
71805
  const drifted = [];
@@ -71709,7 +71815,7 @@ function workflowBodyDrift(projectDir, workflows, synced = {}) {
71709
71815
  return drifted;
71710
71816
  }
71711
71817
  function ensureAppTsconfig(projectDir) {
71712
- const tsconfigPath = path10.join(projectDir, "tsconfig.json");
71818
+ const tsconfigPath = path9.join(projectDir, "tsconfig.json");
71713
71819
  if (!fs8.existsSync(tsconfigPath)) {
71714
71820
  console.error(
71715
71821
  `\u26A0 No tsconfig.json at ${projectDir} \u2014 could not ensure "include" has "${LOTICS_INCLUDE_GLOB}" (a bare .lotics loads none of the generated types) or "exclude" has ${WORKFLOW_TSCONFIG_EXCLUDES.join(", ")}.`
@@ -71761,15 +71867,15 @@ function writeDevLinkTsconfig(projectDir) {
71761
71867
  if (uiSrc) {
71762
71868
  paths["@lotics/ui/*"] = [`${uiSrc}/*`];
71763
71869
  for (const peer of readKitPeerNames(projectDir)) {
71764
- const typesDir = path10.join(projectDir, "node_modules", typesPackageFor(peer));
71765
- const runtimeDir = path10.join(projectDir, "node_modules", peer);
71870
+ const typesDir = path9.join(projectDir, "node_modules", typesPackageFor(peer));
71871
+ const runtimeDir = path9.join(projectDir, "node_modules", peer);
71766
71872
  const target = fs8.existsSync(typesDir) ? `../node_modules/${typesPackageFor(peer)}` : fs8.existsSync(runtimeDir) ? `../node_modules/${peer}` : null;
71767
71873
  if (!target) continue;
71768
71874
  paths[peer] = [target];
71769
71875
  paths[`${peer}/*`] = [`${target}/*`];
71770
71876
  }
71771
71877
  }
71772
- const file2 = path10.join(projectDir, ".lotics", "tsconfig.link.json");
71878
+ const file2 = path9.join(projectDir, ".lotics", "tsconfig.link.json");
71773
71879
  const header = uiSrc ? `// GENERATED by \`lotics app codegen\` \u2014 do not edit.
71774
71880
  // LOTICS_UI_SRC is set, so @lotics/ui resolves to your working copy for tsc,
71775
71881
  // vitest, eslint and your editor \u2014 the same copy Vite is bundling. The peer
@@ -71786,8 +71892,8 @@ function writeDevLinkTsconfig(projectDir) {
71786
71892
  }
71787
71893
  function writeKitTypeAugmentation(projectDir) {
71788
71894
  const uiSrc = process.env.LOTICS_UI_SRC;
71789
- const source = uiSrc ? path10.join(uiSrc, "react_native.d.ts") : path10.join(projectDir, "node_modules", "@lotics", "ui", "src", "react_native.d.ts");
71790
- const target = path10.join(projectDir, ".lotics", "react_native.d.ts");
71895
+ const source = uiSrc ? path9.join(uiSrc, "react_native.d.ts") : path9.join(projectDir, "node_modules", "@lotics", "ui", "src", "react_native.d.ts");
71896
+ const target = path9.join(projectDir, ".lotics", "react_native.d.ts");
71791
71897
  if (!fs8.existsSync(source)) {
71792
71898
  if (fs8.existsSync(target)) fs8.rmSync(target);
71793
71899
  return;
@@ -71804,7 +71910,7 @@ function typesPackageFor(peer) {
71804
71910
  return peer.startsWith("@") ? `@types/${peer.slice(1).replace("/", "__")}` : `@types/${peer}`;
71805
71911
  }
71806
71912
  function readKitPeerNames(projectDir) {
71807
- const kitPkg = path10.join(projectDir, "node_modules", "@lotics", "ui", "package.json");
71913
+ const kitPkg = path9.join(projectDir, "node_modules", "@lotics", "ui", "package.json");
71808
71914
  if (!fs8.existsSync(kitPkg)) return [];
71809
71915
  const parsed = JSON.parse(fs8.readFileSync(kitPkg, "utf-8"));
71810
71916
  const peers = parsed.peerDependencies;
@@ -71815,12 +71921,12 @@ function writeAppDtsKitLinks(projectDir) {
71815
71921
  writeKitTypeAugmentation(projectDir);
71816
71922
  }
71817
71923
  function writeAppDts(projectDir, manifest) {
71818
- const dotLotics = path10.join(projectDir, ".lotics");
71924
+ const dotLotics = path9.join(projectDir, ".lotics");
71819
71925
  fs8.mkdirSync(dotLotics, { recursive: true });
71820
71926
  const written = [
71821
- [path10.join(dotLotics, "app_workflows.d.ts"), generateAppWorkflowsDts(manifest.workflows)],
71822
- [path10.join(dotLotics, "app_queries.d.ts"), generateAppQueriesDts(manifest.queries)],
71823
- [path10.join(dotLotics, "app_agents.d.ts"), generateAppAgentsDts(manifest.agents)]
71927
+ [path9.join(dotLotics, "app_workflows.d.ts"), generateAppWorkflowsDts(manifest.workflows)],
71928
+ [path9.join(dotLotics, "app_queries.d.ts"), generateAppQueriesDts(manifest.queries)],
71929
+ [path9.join(dotLotics, "app_agents.d.ts"), generateAppAgentsDts(manifest.agents)]
71824
71930
  ];
71825
71931
  for (const [file2, content] of written) fs8.writeFileSync(file2, content);
71826
71932
  writeAppDtsKitLinks(projectDir);
@@ -71829,8 +71935,8 @@ function writeAppDts(projectDir, manifest) {
71829
71935
  return written.map(([file2]) => file2);
71830
71936
  }
71831
71937
  function readCodegenTablesAllowlist(projectDir) {
71832
- const pkg2 = JSON.parse(fs8.readFileSync(path10.join(projectDir, "package.json"), "utf-8"));
71833
- const tables = pkg2.lotics?.codegen?.tables;
71938
+ const pkg = JSON.parse(fs8.readFileSync(path9.join(projectDir, "package.json"), "utf-8"));
71939
+ const tables = pkg.lotics?.codegen?.tables;
71834
71940
  return Array.isArray(tables) ? tables.filter((t) => typeof t === "string") : [];
71835
71941
  }
71836
71942
  function resolveCodegenTableIds(projectDir, queries) {
@@ -71842,9 +71948,9 @@ function resolveCodegenTableIds(projectDir, queries) {
71842
71948
  return [...ids];
71843
71949
  }
71844
71950
  function writeAppFields(projectDir, tables) {
71845
- const dotLotics = path10.join(projectDir, ".lotics");
71951
+ const dotLotics = path9.join(projectDir, ".lotics");
71846
71952
  fs8.mkdirSync(dotLotics, { recursive: true });
71847
- const file2 = path10.join(dotLotics, "app_fields.ts");
71953
+ const file2 = path9.join(dotLotics, "app_fields.ts");
71848
71954
  fs8.writeFileSync(file2, generateAppFields(tables));
71849
71955
  return file2;
71850
71956
  }
@@ -71869,7 +71975,7 @@ async function writeGeneratedAppFields(client, projectDir, app, queries) {
71869
71975
  return;
71870
71976
  }
71871
71977
  const fieldsPath = writeAppFields(projectDir, tables);
71872
- console.error(
71978
+ note(
71873
71979
  `Wrote ${fieldsPath} (baked form \u2014 bespoke; ${tables.length} table${tables.length === 1 ? "" : "s"})`
71874
71980
  );
71875
71981
  }
@@ -71879,7 +71985,7 @@ async function writeGeneratedAppFields(client, projectDir, app, queries) {
71879
71985
  }
71880
71986
  function warnAppFieldsUnwritten(projectDir, err2) {
71881
71987
  const reason = err2 instanceof Error ? err2.message : String(err2);
71882
- if (fs8.existsSync(path10.join(projectDir, ".lotics", "app_fields.ts"))) {
71988
+ if (fs8.existsSync(path9.join(projectDir, ".lotics", "app_fields.ts"))) {
71883
71989
  console.error(`\u26A0 Could not regenerate .lotics/app_fields.ts (${reason}). Kept the existing file.`);
71884
71990
  return;
71885
71991
  }
@@ -71890,7 +71996,7 @@ function warnAppFieldsUnwritten(projectDir, err2) {
71890
71996
  function warnAboutDevLink(projectDir, command) {
71891
71997
  const uiSrc = process.env.LOTICS_UI_SRC;
71892
71998
  if (!uiSrc) return;
71893
- const viteConfigPath = path10.join(projectDir, "vite.config.ts");
71999
+ const viteConfigPath = path9.join(projectDir, "vite.config.ts");
71894
72000
  const config2 = fs8.existsSync(viteConfigPath) ? fs8.readFileSync(viteConfigPath, "utf-8") : "";
71895
72001
  const linked = config2.includes("loticsResolve") || config2.includes("LOTICS_UI_SRC");
71896
72002
  if (!linked) {
@@ -71909,9 +72015,9 @@ function warnAboutDevLink(projectDir, command) {
71909
72015
  }
71910
72016
  }
71911
72017
  var KIT_PACKAGES = ["@lotics/ui", "@lotics/app-sdk"];
71912
- function installedKitVersion(projectDir, pkg2) {
72018
+ function installedKitVersion(projectDir, pkg) {
71913
72019
  try {
71914
- const manifest = path10.join(projectDir, "node_modules", ...pkg2.split("/"), "package.json");
72020
+ const manifest = path9.join(projectDir, "node_modules", ...pkg.split("/"), "package.json");
71915
72021
  const parsed = JSON.parse(fs8.readFileSync(manifest, "utf-8"));
71916
72022
  const version2 = parsed.version;
71917
72023
  return typeof version2 === "string" ? version2 : null;
@@ -71919,12 +72025,12 @@ function installedKitVersion(projectDir, pkg2) {
71919
72025
  return null;
71920
72026
  }
71921
72027
  }
71922
- async function publishedKitVersion(pkg2) {
72028
+ async function publishedKitVersion(pkg) {
71923
72029
  const controller = new AbortController();
71924
72030
  const timeout = setTimeout(() => controller.abort(), 3e3);
71925
72031
  timeout.unref();
71926
72032
  try {
71927
- const response = await fetch(`https://registry.npmjs.org/${pkg2}/latest`, {
72033
+ const response = await fetch(`https://registry.npmjs.org/${pkg}/latest`, {
71928
72034
  signal: controller.signal
71929
72035
  });
71930
72036
  if (!response.ok) return null;
@@ -71938,13 +72044,13 @@ async function publishedKitVersion(pkg2) {
71938
72044
  }
71939
72045
  async function warnIfKitBehind(projectDir) {
71940
72046
  const checks = await Promise.all(
71941
- KIT_PACKAGES.map(async (pkg2) => {
71942
- const installed = installedKitVersion(projectDir, pkg2);
72047
+ KIT_PACKAGES.map(async (pkg) => {
72048
+ const installed = installedKitVersion(projectDir, pkg);
71943
72049
  if (!installed) return null;
71944
- const latest = await publishedKitVersion(pkg2);
72050
+ const latest = await publishedKitVersion(pkg);
71945
72051
  if (!latest || !isNewerVersion(latest, installed)) return null;
71946
72052
  const behindMajor = (Number.parseInt(latest, 10) || 0) > (Number.parseInt(installed, 10) || 0);
71947
- return { pkg: pkg2, installed, latest, behindMajor };
72053
+ return { pkg, installed, latest, behindMajor };
71948
72054
  })
71949
72055
  );
71950
72056
  const findings = checks.filter((c) => c !== null);
@@ -71959,7 +72065,7 @@ async function warnIfKitBehind(projectDir) {
71959
72065
 
71960
72066
  Breaking changes are listed newest-first in
71961
72067
  node_modules/@lotics/ui/MIGRATION.md.` : "";
71962
- console.error(
72068
+ warn(
71963
72069
  `
71964
72070
  \u26A0 This app builds against a kit a major release behind:
71965
72071
  ` + lines.join("\n") + migration + `
@@ -71975,12 +72081,12 @@ A newer kit is published \u2014 \`npm update ${findings.map((f) => f.pkg).join("
71975
72081
  );
71976
72082
  }
71977
72083
  function ensureAppVitestSetup(projectDir) {
71978
- const setupPath = path10.join(projectDir, VITEST_SETUP_FILENAME);
72084
+ const setupPath = path9.join(projectDir, VITEST_SETUP_FILENAME);
71979
72085
  if (!fs8.existsSync(setupPath)) {
71980
72086
  fs8.writeFileSync(setupPath, VITEST_SETUP_CONTENT);
71981
72087
  console.error(`Wrote ${VITEST_SETUP_FILENAME} (wired by vite.config.ts; empty until you fill it).`);
71982
72088
  }
71983
- const viteConfigPath = path10.join(projectDir, "vite.config.ts");
72089
+ const viteConfigPath = path9.join(projectDir, "vite.config.ts");
71984
72090
  if (!fs8.existsSync(viteConfigPath)) return;
71985
72091
  const viteConfig = fs8.readFileSync(viteConfigPath, "utf-8");
71986
72092
  if (/setupFiles\s*:/.test(viteConfig) && viteConfig.includes(VITEST_SETUP_FILENAME)) return;
@@ -71990,7 +72096,7 @@ function ensureAppVitestSetup(projectDir) {
71990
72096
  );
71991
72097
  }
71992
72098
  async function appCodegen(args) {
71993
- const projectDir = path10.resolve(args.projectDir ?? process.cwd());
72099
+ const projectDir = path9.resolve(args.projectDir ?? process.cwd());
71994
72100
  const meta3 = readAppMeta(projectDir);
71995
72101
  const dtsPaths = writeAppDts(projectDir, {
71996
72102
  workflows: meta3.workflows,
@@ -72004,7 +72110,7 @@ async function appCodegen(args) {
72004
72110
  }
72005
72111
  for (const alias of orphanWorkflowBodies(projectDir, declared)) {
72006
72112
  console.error(
72007
- `\u26A0 ${path10.join(WORKFLOWS_DIR, `${alias}.ts`)} is not declared in package.json#lotics.workflows \u2014 'workflow check' and 'workflow set' both skip it. Declare the alias or delete the file.`
72113
+ `\u26A0 ${path9.join(WORKFLOWS_DIR, `${alias}.ts`)} is not declared in package.json#lotics.workflows \u2014 'workflow check' and 'workflow set' both skip it. Declare the alias or delete the file.`
72008
72114
  );
72009
72115
  }
72010
72116
  if (!args.client) {
@@ -72063,15 +72169,15 @@ async function refreshWorkflowTypes(client, projectDir, app_id, alias, declarati
72063
72169
  return true;
72064
72170
  }
72065
72171
  function readPriorStamp(projectDir) {
72066
- const pkgPath2 = path10.join(projectDir, "package.json");
72067
- if (!fs8.existsSync(pkgPath2)) return null;
72068
- let pkg2;
72172
+ const pkgPath = path9.join(projectDir, "package.json");
72173
+ if (!fs8.existsSync(pkgPath)) return null;
72174
+ let pkg;
72069
72175
  try {
72070
- pkg2 = JSON.parse(fs8.readFileSync(pkgPath2, "utf-8"));
72176
+ pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
72071
72177
  } catch {
72072
72178
  return null;
72073
72179
  }
72074
- const lotics = pkg2.lotics && typeof pkg2.lotics === "object" ? pkg2.lotics : null;
72180
+ const lotics = pkg.lotics && typeof pkg.lotics === "object" ? pkg.lotics : null;
72075
72181
  if (!lotics) return null;
72076
72182
  const id = lotics.current_version_id;
72077
72183
  const number4 = lotics.version_number;
@@ -72132,8 +72238,8 @@ function appDirName(name2) {
72132
72238
  }
72133
72239
  async function materializeScaffold(targetPath, files) {
72134
72240
  for (const file2 of files) {
72135
- const fullPath = path10.join(targetPath, file2.path);
72136
- fs8.mkdirSync(path10.dirname(fullPath), { recursive: true });
72241
+ const fullPath = path9.join(targetPath, file2.path);
72242
+ fs8.mkdirSync(path9.dirname(fullPath), { recursive: true });
72137
72243
  fs8.writeFileSync(fullPath, file2.content);
72138
72244
  }
72139
72245
  console.error(`Scaffolded ${files.length} files into ${targetPath}`);
@@ -72142,7 +72248,7 @@ async function materializeScaffold(targetPath, files) {
72142
72248
  writeAppDtsKitLinks(targetPath);
72143
72249
  }
72144
72250
  async function appCreate(client, args) {
72145
- const targetPath = path10.resolve(args.targetPath ?? appDirName(args.name));
72251
+ const targetPath = path9.resolve(args.targetPath ?? appDirName(args.name));
72146
72252
  if (fs8.existsSync(targetPath)) {
72147
72253
  const entries2 = fs8.readdirSync(targetPath);
72148
72254
  if (entries2.length > 0) {
@@ -72171,7 +72277,7 @@ async function appCreate(client, args) {
72171
72277
  });
72172
72278
  console.error(`
72173
72279
  Ready. Next steps:`);
72174
- console.error(` cd ${path10.relative(process.cwd(), targetPath) || "."}`);
72280
+ console.error(` cd ${path9.relative(process.cwd(), targetPath) || "."}`);
72175
72281
  console.error(` lotics docs # the contracts, at the versions installed here`);
72176
72282
  console.error(` # edit src/App.tsx, then:`);
72177
72283
  console.error(` lotics app check # every pre-flight a deploy runs`);
@@ -72230,17 +72336,17 @@ async function appVersions(client, args) {
72230
72336
  }
72231
72337
  }
72232
72338
  function defaultPullTarget(appId, appName) {
72233
- const cwdPkgPath = path10.join(process.cwd(), "package.json");
72339
+ const cwdPkgPath = path9.join(process.cwd(), "package.json");
72234
72340
  if (fs8.existsSync(cwdPkgPath)) {
72235
- let pkg2 = null;
72341
+ let pkg = null;
72236
72342
  try {
72237
- pkg2 = JSON.parse(fs8.readFileSync(cwdPkgPath, "utf-8"));
72343
+ pkg = JSON.parse(fs8.readFileSync(cwdPkgPath, "utf-8"));
72238
72344
  } catch (err2) {
72239
72345
  console.error(
72240
72346
  `\u26A0 Could not parse ${cwdPkgPath} (${err2 instanceof Error ? err2.message : String(err2)}) \u2014 pulling into a fresh ${appDirName(appName)}/ subdir.`
72241
72347
  );
72242
72348
  }
72243
- if (pkg2?.lotics?.app_id === appId) return process.cwd();
72349
+ if (pkg?.lotics?.app_id === appId) return process.cwd();
72244
72350
  }
72245
72351
  return appDirName(appName);
72246
72352
  }
@@ -72280,7 +72386,7 @@ async function hydrateAppProject(client, targetPath, app, opts) {
72280
72386
  );
72281
72387
  opts.kept.push(...kept);
72282
72388
  if (written.length > 0) {
72283
- console.error(
72389
+ note(
72284
72390
  `Wrote ${written.length} workflow ${written.length === 1 ? "body" : "bodies"} to ${WORKFLOWS_DIR}/ (${written.join(", ")})`
72285
72391
  );
72286
72392
  }
@@ -72312,7 +72418,7 @@ async function hydrateAppProject(client, targetPath, app, opts) {
72312
72418
  unseenLiveProse.push({ alias, parked: parkLiveAgentProse(targetPath, alias, instructions) });
72313
72419
  }
72314
72420
  if (written.length > 0) {
72315
- console.error(
72421
+ note(
72316
72422
  `Wrote ${written.length} agent instruction ${written.length === 1 ? "file" : "files"} to ${AGENTS_DIR}/ (${written.join(", ")})`
72317
72423
  );
72318
72424
  }
@@ -72332,21 +72438,21 @@ async function appPull(client, args) {
72332
72438
  if (!versionId) throw new Error(`App ${app.id} has no version to pull.`);
72333
72439
  const version2 = await client.getAppVersion(app.id, versionId);
72334
72440
  const sourceUrl = await client.getAppVersionSourceUrl(app.id, version2.id);
72335
- const targetPath = path10.resolve(args.targetPath ?? defaultPullTarget(app.id, app.name));
72441
+ const targetPath = path9.resolve(args.targetPath ?? defaultPullTarget(app.id, app.name));
72336
72442
  fs8.mkdirSync(targetPath, { recursive: true });
72337
72443
  const prior = readPriorStamp(targetPath);
72338
- const tmpFile = path10.join(tmpdir(), `lotics-app-${app.id}-${Date.now()}.tar.gz`);
72444
+ const tmpFile = path9.join(tmpdir(), `lotics-app-${app.id}-${Date.now()}.tar.gz`);
72339
72445
  console.error(`Downloading source archive...`);
72340
72446
  await downloadToFile(sourceUrl, tmpFile);
72341
72447
  const keptLocal = [];
72342
72448
  const restoredFromArchive = [];
72343
- const stagingDir = fs8.mkdtempSync(path10.join(tmpdir(), `lotics-pull-${app.id}-`));
72449
+ const stagingDir = fs8.mkdtempSync(path9.join(tmpdir(), `lotics-pull-${app.id}-`));
72344
72450
  try {
72345
72451
  await runTar(["-xzf", tmpFile, "-C", stagingDir], stagingDir);
72346
72452
  console.error(`Extracting to ${targetPath}...`);
72347
72453
  const copied = copyTree(stagingDir, targetPath, {
72348
72454
  overwrite: args.force === true,
72349
- skip: [WORKFLOWS_DIR, AGENTS_DIR].map((d) => d.split(path10.sep).join("/"))
72455
+ skip: [WORKFLOWS_DIR, AGENTS_DIR].map((d) => d.split(path9.sep).join("/"))
72350
72456
  });
72351
72457
  keptLocal.push(...copied.kept);
72352
72458
  restoredFromArchive.push(...copied.created);
@@ -72375,20 +72481,20 @@ async function appPull(client, args) {
72375
72481
  });
72376
72482
  console.error(`
72377
72483
  Ready. Next steps:`);
72378
- console.error(` cd ${path10.relative(process.cwd(), targetPath) || "."}`);
72484
+ console.error(` cd ${path9.relative(process.cwd(), targetPath) || "."}`);
72379
72485
  console.error(` lotics docs # the contracts, at the versions installed here`);
72380
72486
  console.error(` # edit src/App.tsx, then:`);
72381
72487
  console.error(` lotics app deploy`);
72382
72488
  }
72383
72489
  function readAppSourceText(projectDir) {
72384
- const srcDir = path10.join(projectDir, "src");
72490
+ const srcDir = path9.join(projectDir, "src");
72385
72491
  if (!fs8.existsSync(srcDir)) return "";
72386
72492
  const parts = [];
72387
72493
  const walk2 = (dir) => {
72388
72494
  for (const entry of fs8.readdirSync(dir, { withFileTypes: true })) {
72389
- const full = path10.join(dir, entry.name);
72495
+ const full = path9.join(dir, entry.name);
72390
72496
  if (entry.isDirectory()) walk2(full);
72391
- else if (isAppSourcePath(path10.relative(projectDir, full))) {
72497
+ else if (isAppSourcePath(path9.relative(projectDir, full))) {
72392
72498
  parts.push(fs8.readFileSync(full, "utf8"));
72393
72499
  }
72394
72500
  }
@@ -72397,7 +72503,7 @@ function readAppSourceText(projectDir) {
72397
72503
  return parts.join("\n");
72398
72504
  }
72399
72505
  async function appDeploy(client, args) {
72400
- const projectDir = path10.resolve(args.projectDir ?? process.cwd());
72506
+ const projectDir = path9.resolve(args.projectDir ?? process.cwd());
72401
72507
  const meta3 = readAppMeta(projectDir);
72402
72508
  warnAboutDevLink(projectDir, "deploy");
72403
72509
  const liveApp = await client.getApp(meta3.app_id);
@@ -72426,23 +72532,23 @@ async function appDeploy(client, args) {
72426
72532
  meta3.queries ?? {}
72427
72533
  );
72428
72534
  } catch (err2) {
72429
- console.error(
72535
+ warn(
72430
72536
  `\u26A0 Could not regenerate .lotics/app_fields.ts (${err2.message}).
72431
72537
  The build will use whatever is on disk \u2014 run 'lotics app codegen' if this app is published.`
72432
72538
  );
72433
72539
  }
72434
- console.error("Building...");
72540
+ note("Building...");
72435
72541
  await runNpm(["run", "build"], projectDir);
72436
- const distDir = path10.join(projectDir, "dist");
72542
+ const distDir = path9.join(projectDir, "dist");
72437
72543
  if (!fs8.existsSync(distDir)) {
72438
72544
  throw new Error(
72439
72545
  `Build did not produce a dist/ directory in ${projectDir}. Check that 'npm run build' is configured correctly (Vite/etc. defaults to dist/).`
72440
72546
  );
72441
72547
  }
72442
- const tmpSource = path10.join(tmpdir(), `lotics-source-${Date.now()}.tar.gz`);
72443
- const tmpDist = path10.join(tmpdir(), `lotics-dist-${Date.now()}.tar.gz`);
72548
+ const tmpSource = path9.join(tmpdir(), `lotics-source-${Date.now()}.tar.gz`);
72549
+ const tmpDist = path9.join(tmpdir(), `lotics-dist-${Date.now()}.tar.gz`);
72444
72550
  try {
72445
- console.error("Packaging source...");
72551
+ note("Packaging source...");
72446
72552
  await runTar(
72447
72553
  [
72448
72554
  "-czf",
@@ -72456,10 +72562,10 @@ async function appDeploy(client, args) {
72456
72562
  ],
72457
72563
  projectDir
72458
72564
  );
72459
- console.error("Packaging dist...");
72565
+ note("Packaging dist...");
72460
72566
  await runTar(["-czf", tmpDist, "-C", distDir, "."], projectDir);
72461
72567
  const liveQueries = await readLiveQueries(client, meta3.app_id);
72462
- console.error("Uploading...");
72568
+ note("Uploading...");
72463
72569
  try {
72464
72570
  const result = await client.deployAppVersion({
72465
72571
  queries: liveQueries,
@@ -72498,23 +72604,23 @@ async function appDeploy(client, args) {
72498
72604
  current_version_id: result.version_id,
72499
72605
  version_number: result.version_number
72500
72606
  });
72501
- console.error(`Deployed v${result.version_number} (${result.version_id})`);
72502
- console.error(`Bundle size: ${(result.bundle_size_bytes / 1024).toFixed(1)} KB`);
72607
+ note(`Deployed v${result.version_number} (${result.version_id})`);
72608
+ note(`Bundle size: ${(result.bundle_size_bytes / 1024).toFixed(1)} KB`);
72503
72609
  try {
72504
72610
  const drifted = staleWorkflowGlobals(projectDir, meta3.workflows ?? {});
72505
72611
  for (const { alias, declaration } of drifted) {
72506
72612
  if (await refreshWorkflowTypes(client, projectDir, meta3.app_id, alias, declaration)) {
72507
- console.error(`Refreshed workflow types for ${alias} \u2014 this deploy moved its declaration.`);
72613
+ note(`Refreshed workflow types for ${alias} \u2014 this deploy moved its declaration.`);
72508
72614
  }
72509
72615
  }
72510
72616
  } catch (err2) {
72511
- console.error(`(types not refreshed: ${err2.message})`);
72617
+ warn(`(types not refreshed: ${err2.message})`);
72512
72618
  }
72513
72619
  let liveAfter;
72514
72620
  try {
72515
72621
  liveAfter = await client.getApp(meta3.app_id);
72516
72622
  } catch (err2) {
72517
- console.error(`(skipped post-deploy checks: ${err2.message})`);
72623
+ warn(`(skipped post-deploy checks: ${err2.message})`);
72518
72624
  }
72519
72625
  if (liveAfter) {
72520
72626
  warnIfUnbranded(liveAfter);
@@ -72555,7 +72661,7 @@ function warnIfUnbranded(app) {
72555
72661
  if (!app.icon) missing.push("icon");
72556
72662
  if (!app.theme?.color) missing.push("color");
72557
72663
  if (missing.length === 0) return;
72558
- console.error(
72664
+ warn(
72559
72665
  `
72560
72666
  \u26A0 This app has no ${missing.join(" or ")} set \u2014 it shows a generic tile in the launcher.
72561
72667
  Set it: lotics run update_app '{"app_id":"${app.id}","icon":"<lucide-name>","theme":{"color":"blue"}}'
@@ -72584,7 +72690,7 @@ async function readLiveQueries(client, appId) {
72584
72690
  }
72585
72691
  }
72586
72692
  async function appCheck(client, args = {}) {
72587
- const projectDir = path10.resolve(args.projectDir ?? process.cwd());
72693
+ const projectDir = path9.resolve(args.projectDir ?? process.cwd());
72588
72694
  const meta3 = readAppMeta(projectDir);
72589
72695
  const app = await client.getApp(meta3.app_id);
72590
72696
  assertProjectIsCurrent(meta3, app);
@@ -72859,10 +72965,10 @@ function copyTree(from, to, opts, relative = "") {
72859
72965
  const kept = [];
72860
72966
  const created = [];
72861
72967
  for (const entry of fs8.readdirSync(from, { withFileTypes: true })) {
72862
- const src = path10.join(from, entry.name);
72863
- const dest = path10.join(to, entry.name);
72864
- const rel = path10.join(relative, entry.name);
72865
- if (opts.skip?.includes(rel.split(path10.sep).join("/"))) continue;
72968
+ const src = path9.join(from, entry.name);
72969
+ const dest = path9.join(to, entry.name);
72970
+ const rel = path9.join(relative, entry.name);
72971
+ if (opts.skip?.includes(rel.split(path9.sep).join("/"))) continue;
72866
72972
  if (entry.isDirectory()) {
72867
72973
  fs8.mkdirSync(dest, { recursive: true });
72868
72974
  const child = copyTree(src, dest, opts, rel);
@@ -73035,9 +73141,9 @@ function warnIfProjectFootguns(projectDir, sourceText) {
73035
73141
  ].join("\n")
73036
73142
  );
73037
73143
  }
73038
- const configName = VITE_CONFIG_NAMES.find((name2) => fs8.existsSync(path10.join(projectDir, name2)));
73144
+ const configName = VITE_CONFIG_NAMES.find((name2) => fs8.existsSync(path9.join(projectDir, name2)));
73039
73145
  if (configName !== void 0) {
73040
- const config2 = fs8.readFileSync(path10.join(projectDir, configName), "utf-8");
73146
+ const config2 = fs8.readFileSync(path9.join(projectDir, configName), "utf-8");
73041
73147
  const defined = defineBlockKeys(config2);
73042
73148
  const missing = ["global", "__DEV__"].filter((name2) => !defined.has(name2));
73043
73149
  if (missing.length > 0) {
@@ -73133,7 +73239,7 @@ function warnIfUnboundAliases(app, called) {
73133
73239
  console.error(lines.join("\n"));
73134
73240
  }
73135
73241
  async function appDev(client, args) {
73136
- const projectDir = path10.resolve(args.projectDir ?? process.cwd());
73242
+ const projectDir = path9.resolve(args.projectDir ?? process.cwd());
73137
73243
  const meta3 = readAppMeta(projectDir);
73138
73244
  warnAboutDevLink(projectDir, "dev");
73139
73245
  writeAppDts(projectDir, { workflows: meta3.workflows, queries: meta3.queries, agents: meta3.agents });
@@ -73267,7 +73373,7 @@ async function appAgentSet(client, args) {
73267
73373
  }
73268
73374
  if (!hasDeclaration) {
73269
73375
  console.error(
73270
- `No instructions at ${path10.relative(projectDir, file2)}, and nothing declared in package.json#lotics.agents.${args.alias} to push instead.
73376
+ `No instructions at ${path9.relative(projectDir, file2)}, and nothing declared in package.json#lotics.agents.${args.alias} to push instead.
73271
73377
  Run 'lotics app pull ${meta3.app_id}' to write ${AGENTS_DIR}/${args.alias}.md, then edit it.`
73272
73378
  );
73273
73379
  process.exit(1);
@@ -73276,7 +73382,7 @@ async function appAgentSet(client, args) {
73276
73382
  const instructions = hasProse ? stripAgentHeader(fs8.readFileSync(file2, "utf-8")) : void 0;
73277
73383
  if (instructions === "") {
73278
73384
  console.error(
73279
- `${path10.relative(projectDir, file2)} is empty after stripping the header \u2014 refusing to push an empty prompt.`
73385
+ `${path9.relative(projectDir, file2)} is empty after stripping the header \u2014 refusing to push an empty prompt.`
73280
73386
  );
73281
73387
  process.exit(1);
73282
73388
  }
@@ -73343,7 +73449,7 @@ async function appWorkflowSet(client, args) {
73343
73449
  const file2 = workflowFilePath(projectDir, args.alias);
73344
73450
  if (!fs8.existsSync(file2)) {
73345
73451
  console.error(
73346
- `No workflow body at ${path10.relative(projectDir, file2)}.
73452
+ `No workflow body at ${path9.relative(projectDir, file2)}.
73347
73453
  If "${args.alias}" is already bound: lotics app pull ${meta3.app_id} (writes the body)
73348
73454
  If it is a NEW alias you declared: create that file yourself, then re-run this.`
73349
73455
  );
@@ -73352,7 +73458,7 @@ async function appWorkflowSet(client, args) {
73352
73458
  noteScope({ alias: args.alias });
73353
73459
  const source = stripWorkflowHeader(fs8.readFileSync(file2, "utf-8"));
73354
73460
  if (source.trim() === "") {
73355
- console.error(`Workflow body ${path10.relative(projectDir, file2)} is empty after stripping the header.`);
73461
+ console.error(`Workflow body ${path9.relative(projectDir, file2)} is empty after stripping the header.`);
73356
73462
  process.exit(1);
73357
73463
  }
73358
73464
  const res = await client.setAppWorkflow(meta3.app_id, args.alias, {
@@ -73491,13 +73597,13 @@ async function appWorkflowCheck(args) {
73491
73597
  const globalsPath = workflowGlobalsPath(projectDir, alias);
73492
73598
  if (!fs8.existsSync(bodyPath)) {
73493
73599
  console.error(
73494
- `\u26A0 Skipped "${alias}" \u2014 no body at ${path10.relative(projectDir, bodyPath)}. Run 'lotics app workflow pull' to write it.`
73600
+ `\u26A0 Skipped "${alias}" \u2014 no body at ${path9.relative(projectDir, bodyPath)}. Run 'lotics app workflow pull' to write it.`
73495
73601
  );
73496
73602
  continue;
73497
73603
  }
73498
73604
  if (!fs8.existsSync(globalsPath)) {
73499
73605
  console.error(
73500
- `Cannot check "${alias}" \u2014 missing types at ${path10.relative(projectDir, globalsPath)}. Run 'lotics app codegen' to fetch them (it refreshes types WITHOUT touching your body; a pull would overwrite it).`
73606
+ `Cannot check "${alias}" \u2014 missing types at ${path9.relative(projectDir, globalsPath)}. Run 'lotics app codegen' to fetch them (it refreshes types WITHOUT touching your body; a pull would overwrite it).`
73501
73607
  );
73502
73608
  process.exit(1);
73503
73609
  }
@@ -73533,7 +73639,7 @@ async function appWorkflowCheck(args) {
73533
73639
  function printWorkflowCheckResults(projectDir, results) {
73534
73640
  let totalErrors = 0;
73535
73641
  for (const r of results) {
73536
- const rel = path10.relative(projectDir, r.bodyPath);
73642
+ const rel = path9.relative(projectDir, r.bodyPath);
73537
73643
  if (r.issues.length === 0) {
73538
73644
  console.error(`\u2713 ${r.alias} (${rel}) \u2014 clean`);
73539
73645
  continue;
@@ -73585,6 +73691,7 @@ function parseArgs(argv) {
73585
73691
  remove: void 0,
73586
73692
  includeHidden: false,
73587
73693
  timezone: void 0,
73694
+ email: void 0,
73588
73695
  message: void 0,
73589
73696
  session: void 0,
73590
73697
  port: void 0,
@@ -73669,6 +73776,9 @@ function parseArgs(argv) {
73669
73776
  case "--name":
73670
73777
  flags.name = argv[++i2];
73671
73778
  break;
73779
+ case "--email":
73780
+ flags.email = argv[++i2];
73781
+ break;
73672
73782
  case "--description":
73673
73783
  flags.description = argv[++i2];
73674
73784
  break;
@@ -73779,8 +73889,53 @@ function parseArgs(argv) {
73779
73889
 
73780
73890
  // src/starter_commands.ts
73781
73891
  import fs9 from "node:fs";
73782
- import path11 from "node:path";
73892
+ import path10 from "node:path";
73783
73893
  import { tmpdir as tmpdir2 } from "node:os";
73894
+ function printStarterRows(rows) {
73895
+ const width = Math.max(...rows.map((row) => row.id.length));
73896
+ for (const row of rows) {
73897
+ const tags = row.tags ?? [];
73898
+ console.log(
73899
+ ` ${row.id.padEnd(width)} ${row.name}${tags.length > 0 ? ` [${tags.join(", ")}]` : ""}`
73900
+ );
73901
+ if (row.description) console.log(` ${" ".repeat(width)} ${row.description}`);
73902
+ }
73903
+ }
73904
+ function noteCopiedContent(templates, knowledgeDocs) {
73905
+ if (templates.length > 0) {
73906
+ note(
73907
+ `
73908
+ ${templates.length} document template${templates.length === 1 ? "" : "s"}: ${templates.join(", ")}
73909
+ These carry the publisher's wording and details, not yours \u2014 decide which should
73910
+ become your own documents. Editing them: lotics docs document_templates`
73911
+ );
73912
+ }
73913
+ if (knowledgeDocs.length > 0) {
73914
+ note(
73915
+ `
73916
+ ${knowledgeDocs.length} knowledge doc${knowledgeDocs.length === 1 ? "" : "s"}: ${knowledgeDocs.join(", ")}
73917
+ These describe how the publisher works, and the app's agents read them.
73918
+ Rewrite what differs: lotics docs knowledge_docs`
73919
+ );
73920
+ }
73921
+ }
73922
+ async function starterListPublic() {
73923
+ const starters = await fetchOfficialStarters();
73924
+ if (starters.length === 0) {
73925
+ console.error("No starters are published yet \u2014 build one with `lotics docs building_an_app`.");
73926
+ return;
73927
+ }
73928
+ console.error(`${starters.length} starter${starters.length === 1 ? "" : "s"} published by Lotics:
73929
+ `);
73930
+ printStarterRows(starters);
73931
+ console.error(
73932
+ `
73933
+ lotics start <starter_id> --email you@company.com Copy one \u2014 creates your account too
73934
+
73935
+ This machine has no Lotics account yet, so this is the published shelf.
73936
+ Signed in, the same command also lists what your own organization published.`
73937
+ );
73938
+ }
73784
73939
  async function starterList(client) {
73785
73940
  const starters = await client.listStarters();
73786
73941
  if (starters.length === 0) {
@@ -73789,19 +73944,19 @@ async function starterList(client) {
73789
73944
  );
73790
73945
  return;
73791
73946
  }
73792
- const width = Math.max(...starters.map((s) => s.id.length));
73793
73947
  console.error(`${starters.length} starter${starters.length === 1 ? "" : "s"}:
73794
73948
  `);
73795
- for (const s of starters) {
73796
- const tags = [
73797
- s.is_official ? "official" : null,
73798
- s.owned_by_caller === true ? "yours" : null
73799
- ].filter((t) => t !== null);
73800
- console.log(
73801
- ` ${s.id.padEnd(width)} ${s.name}${tags.length > 0 ? ` [${tags.join(", ")}]` : ""}`
73802
- );
73803
- if (s.description) console.log(` ${" ".repeat(width)} ${s.description}`);
73804
- }
73949
+ printStarterRows(
73950
+ starters.map((s) => ({
73951
+ id: s.id,
73952
+ name: s.name,
73953
+ description: s.description,
73954
+ tags: [
73955
+ s.is_official ? "official" : null,
73956
+ s.owned_by_caller === true ? "yours" : null
73957
+ ].filter((t) => t !== null)
73958
+ }))
73959
+ );
73805
73960
  console.error(`
73806
73961
  lotics starter init <starter_id> Copy one into this workspace`);
73807
73962
  }
@@ -73814,6 +73969,9 @@ async function starterShow(client, starter_id) {
73814
73969
  version ${starter.latest_version}
73815
73970
  trust ${starter.is_official ? "official \u2014 reviewed by Lotics" : starter.owned_by_caller === true ? "your organization's own" : "not copyable from this organization"}`
73816
73971
  );
73972
+ if (starter.icon !== void 0) {
73973
+ console.log(`icon ${starter.icon ?? "none \u2014 copies show a generic tile"}`);
73974
+ }
73817
73975
  if (starter.retired_at !== null) {
73818
73976
  console.log(`retired ${starter.retired_at} \u2014 can no longer be copied`);
73819
73977
  }
@@ -73821,7 +73979,7 @@ trust ${starter.is_official ? "official \u2014 reviewed by Lotics" : starter
73821
73979
  function reportKnowledgeWarnings(warnings) {
73822
73980
  const missing = warnings.missing_expected_docs;
73823
73981
  if (missing.length === 0) return;
73824
- console.error(
73982
+ warn(
73825
73983
  `
73826
73984
  \u26A0 ${missing.length} knowledge doc(s) this starter's agents expect are missing here:
73827
73985
  ` + missing.map((name2) => ` \u2022 ${name2}`).join("\n") + `
@@ -73831,40 +73989,55 @@ function reportKnowledgeWarnings(warnings) {
73831
73989
  }
73832
73990
  async function starterInit(client, args) {
73833
73991
  const starter = await client.getPackage(args.starter_id);
73834
- console.error(
73992
+ note(
73835
73993
  `Copying ${starter.name}${starter.is_official ? " (official)" : ""} into this workspace\u2026`
73836
73994
  );
73837
73995
  const result = await client.instantiateStarter(args.starter_id, {
73838
73996
  ...args.noSampleData === true ? { no_sample_data: true } : {},
73839
73997
  ...args.adopt === true ? { adopt: true } : {}
73840
73998
  });
73841
- const entities = Object.keys(result.binding.entities ?? {}).length;
73842
- const docs = Object.keys(result.binding.knowledge ?? {}).length;
73843
- const templates = Object.keys(result.binding.templates ?? {}).length;
73999
+ const tables = Object.keys(result.binding.entities ?? {}).sort();
74000
+ const knowledgeDocs = Object.keys(result.binding.knowledge ?? {}).sort();
74001
+ const templates = Object.keys(result.binding.templates ?? {}).sort();
73844
74002
  const records = Object.values(result.sample_record_ids).reduce((n, ids) => n + ids.length, 0);
73845
- console.error(
73846
- ` Created ${entities} table${entities === 1 ? "" : "s"}, ${templates} template${templates === 1 ? "" : "s"}, ${docs} knowledge doc${docs === 1 ? "" : "s"}, ${records} sample record${records === 1 ? "" : "s"}.`
74003
+ const created = {
74004
+ tables,
74005
+ templates,
74006
+ knowledge_docs: knowledgeDocs,
74007
+ sample_records: records
74008
+ };
74009
+ note(
74010
+ ` Created ${tables.length} table${tables.length === 1 ? "" : "s"}, ${templates.length} template${templates.length === 1 ? "" : "s"}, ${knowledgeDocs.length} knowledge doc${knowledgeDocs.length === 1 ? "" : "s"}, ${records} sample record${records === 1 ? "" : "s"}.`
73847
74011
  );
73848
74012
  reportKnowledgeWarnings(result.knowledge_warnings);
73849
74013
  if (result.app_id === null || result.bundle_url === null) {
73850
- console.error(`
73851
- Done. The documents and knowledge are yours to edit \u2014 nothing links back.`);
73852
- return;
74014
+ note(`
74015
+ Done \u2014 these are yours now, with no link back to the starter.`);
74016
+ noteCopiedContent(templates, knowledgeDocs);
74017
+ return {
74018
+ starter_id: args.starter_id,
74019
+ starter_name: starter.name,
74020
+ version: result.version,
74021
+ app_id: null,
74022
+ project_dir: null,
74023
+ created,
74024
+ signin_url: null
74025
+ };
73853
74026
  }
73854
- const targetPath = path11.resolve(args.targetPath ?? appDirName(starter.name));
74027
+ const targetPath = path10.resolve(args.targetPath ?? appDirName(starter.name));
73855
74028
  if (fs9.existsSync(targetPath) && fs9.readdirSync(targetPath).length > 0) {
73856
74029
  throw new Error(
73857
74030
  `Target directory ${targetPath} is not empty. The app (${result.app_id}) was created \u2014 pass an empty path and re-run, or finish it with "lotics app pull ${result.app_id}".`
73858
74031
  );
73859
74032
  }
73860
74033
  fs9.mkdirSync(targetPath, { recursive: true });
73861
- console.error(`Downloading source\u2026`);
73862
- const bundleFile = path11.join(tmpdir2(), `lotics-starter-${args.starter_id}-${process.pid}.tar.gz`);
73863
- const wrapperDir = fs9.mkdtempSync(path11.join(tmpdir2(), `lotics-starter-`));
74034
+ note(`Downloading source\u2026`);
74035
+ const bundleFile = path10.join(tmpdir2(), `lotics-starter-${args.starter_id}-${process.pid}.tar.gz`);
74036
+ const wrapperDir = fs9.mkdtempSync(path10.join(tmpdir2(), `lotics-starter-`));
73864
74037
  try {
73865
74038
  await downloadToFile(result.bundle_url, bundleFile);
73866
74039
  await runTar(["-xzf", bundleFile, "-C", wrapperDir], wrapperDir);
73867
- const sourceArchive = path11.join(wrapperDir, "source.tar.gz");
74040
+ const sourceArchive = path10.join(wrapperDir, "source.tar.gz");
73868
74041
  if (!fs9.existsSync(sourceArchive)) {
73869
74042
  throw new Error(
73870
74043
  `Starter bundle carried no source.tar.gz \u2014 the app (${result.app_id}) exists but has no code. Report this to the starter's publisher.`
@@ -73876,7 +74049,7 @@ Done. The documents and knowledge are yours to edit \u2014 nothing links back.`)
73876
74049
  fs9.rmSync(wrapperDir, { recursive: true, force: true });
73877
74050
  }
73878
74051
  for (const dir of ["knowledge", "fixtures"]) {
73879
- fs9.rmSync(path11.join(targetPath, dir), { recursive: true, force: true });
74052
+ fs9.rmSync(path10.join(targetPath, dir), { recursive: true, force: true });
73880
74053
  }
73881
74054
  const app = await client.getApp(result.app_id);
73882
74055
  const kept = [];
@@ -73886,15 +74059,14 @@ Done. The documents and knowledge are yours to edit \u2014 nothing links back.`)
73886
74059
  kept
73887
74060
  });
73888
74061
  if (starter.owned_by_caller !== true) {
73889
- console.error(
74062
+ warn(
73890
74063
  `
73891
74064
  Building ${starter.name} \u2014 this runs its build on your machine (its package scripts
73892
74065
  and vite config are the publisher's code, executed as you).${starter.is_official ? " Lotics reviewed this starter." : ""}`
73893
74066
  );
73894
74067
  } else {
73895
- console.error(`Building and deploying\u2026`);
74068
+ note(`Building and deploying\u2026`);
73896
74069
  }
73897
- await runNpm(["run", "build"], targetPath);
73898
74070
  await appDeploy(client, {
73899
74071
  projectDir: targetPath,
73900
74072
  message: `Copied from starter ${starter.name}`
@@ -73907,14 +74079,14 @@ Building ${starter.name} \u2014 this runs its build on your machine (its package
73907
74079
  });
73908
74080
  signInUrl = link.url ?? null;
73909
74081
  } catch (error52) {
73910
- console.error(
74082
+ warn(
73911
74083
  `
73912
74084
  (no sign-in link: ${error52 instanceof Error ? error52.message : String(error52)})
73913
74085
  The copy is complete either way \u2014 open it with "lotics auth web".`
73914
74086
  );
73915
74087
  signInUrl = null;
73916
74088
  }
73917
- console.error(
74089
+ note(
73918
74090
  `
73919
74091
  Done \u2014 ${starter.name} is live and yours.
73920
74092
  App: ${result.app_id}
@@ -73922,42 +74094,55 @@ Done \u2014 ${starter.name} is live and yours.
73922
74094
 
73923
74095
  Edit anything: the tables, the app source, the templates, the docs. There is no
73924
74096
  link back to the starter and nothing to upgrade \u2014 this is your app now.
73925
- Ship a change: cd ${path11.relative(process.cwd(), targetPath) || "."} && lotics app deploy -m "<what changed>"`
74097
+ Ship a change: cd ${path10.relative(process.cwd(), targetPath) || "."} && lotics app deploy -m "<what changed>"
74098
+
74099
+ How to change it: lotics docs building_an_app (then \`lotics docs\` for the rest \u2014
74100
+ the SDK and UI references are installed in this project, so they match its versions)`
73926
74101
  );
73927
74102
  if (records > 0) {
73928
- console.error(
74103
+ note(
73929
74104
  `
73930
74105
  ${records} sample record${records === 1 ? "" : "s"} came with it. They are ordinary records \u2014
73931
74106
  delete them whenever you like, or re-run with --no-sample-data next time.`
73932
74107
  );
73933
74108
  }
74109
+ noteCopiedContent(templates, knowledgeDocs);
73934
74110
  if (signInUrl !== null) {
73935
- console.error(
74111
+ note(
73936
74112
  `
73937
74113
  Open it \u2014 one-time sign-in link, expires in 15 minutes:
73938
74114
  ${signInUrl}`
73939
74115
  );
73940
74116
  }
74117
+ return {
74118
+ starter_id: args.starter_id,
74119
+ starter_name: starter.name,
74120
+ version: result.version,
74121
+ app_id: result.app_id,
74122
+ project_dir: targetPath,
74123
+ created,
74124
+ signin_url: signInUrl
74125
+ };
73941
74126
  }
73942
74127
  async function starterFixturesCapture(client, args) {
73943
- const projectDir = path11.resolve(args.projectDir ?? process.cwd());
74128
+ const projectDir = path10.resolve(args.projectDir ?? process.cwd());
73944
74129
  const meta3 = readAppMeta(projectDir);
73945
74130
  const result = await client.captureStarterFixtures(meta3.app_id, {
73946
74131
  entities: args.entities,
73947
74132
  ...args.limit !== void 0 ? { limit: args.limit } : {}
73948
74133
  });
73949
- const fixturesDir = path11.join(projectDir, "fixtures");
74134
+ const fixturesDir = path10.join(projectDir, "fixtures");
73950
74135
  fs9.mkdirSync(fixturesDir, { recursive: true });
73951
74136
  let totalRows = 0;
73952
74137
  for (const entry of result.captured) {
73953
- const file2 = path11.join(projectDir, entry.content_ref);
74138
+ const file2 = path10.join(projectDir, entry.content_ref);
73954
74139
  fs9.writeFileSync(file2, `${JSON.stringify(entry.file, null, 2)}
73955
74140
  `, "utf-8");
73956
74141
  totalRows += entry.file.rows.length;
73957
74142
  console.error(
73958
74143
  ` ${entry.content_ref} \u2014 ${entry.file.rows.length} row${entry.file.rows.length === 1 ? "" : "s"}`
73959
74144
  );
73960
- for (const note of entry.notes) console.error(` \u26A0 ${note}`);
74145
+ for (const line of entry.notes) console.error(` \u26A0 ${line}`);
73961
74146
  }
73962
74147
  if (result.captured.length === 0) {
73963
74148
  console.error(
@@ -74041,7 +74226,7 @@ import fs11 from "node:fs";
74041
74226
 
74042
74227
  // src/file_command_io.ts
74043
74228
  import fs10 from "node:fs";
74044
- import path12 from "node:path";
74229
+ import path11 from "node:path";
74045
74230
  var CliError = class extends Error {
74046
74231
  constructor(message2) {
74047
74232
  super(message2);
@@ -74064,8 +74249,8 @@ function rejectExtraArgs(rest2, arity, usage) {
74064
74249
  fail2(`${usage} takes ${arity} argument${arity === 1 ? "" : "s"} after <file>, got ${rest2.length}. Unused: ${rest2.slice(arity).join(" ")}`);
74065
74250
  }
74066
74251
  function writeFileAtomic(filePath, bytes) {
74067
- const dir = path12.dirname(path12.resolve(filePath));
74068
- const tmp = path12.join(dir, `.${path12.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
74252
+ const dir = path11.dirname(path11.resolve(filePath));
74253
+ const tmp = path11.join(dir, `.${path11.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
74069
74254
  fs10.writeFileSync(tmp, bytes);
74070
74255
  try {
74071
74256
  fs10.renameSync(tmp, filePath);
@@ -76868,16 +77053,16 @@ var MatcherView = class {
76868
77053
  * @returns {string|undefined}
76869
77054
  */
76870
77055
  getCurrentTag() {
76871
- const path14 = this._matcher.path;
76872
- return path14.length > 0 ? path14[path14.length - 1].tag : void 0;
77056
+ const path13 = this._matcher.path;
77057
+ return path13.length > 0 ? path13[path13.length - 1].tag : void 0;
76873
77058
  }
76874
77059
  /**
76875
77060
  * Get current namespace.
76876
77061
  * @returns {string|undefined}
76877
77062
  */
76878
77063
  getCurrentNamespace() {
76879
- const path14 = this._matcher.path;
76880
- return path14.length > 0 ? path14[path14.length - 1].namespace : void 0;
77064
+ const path13 = this._matcher.path;
77065
+ return path13.length > 0 ? path13[path13.length - 1].namespace : void 0;
76881
77066
  }
76882
77067
  /**
76883
77068
  * Get current node's attribute value.
@@ -76885,9 +77070,9 @@ var MatcherView = class {
76885
77070
  * @returns {*}
76886
77071
  */
76887
77072
  getAttrValue(attrName) {
76888
- const path14 = this._matcher.path;
76889
- if (path14.length === 0) return void 0;
76890
- return path14[path14.length - 1].values?.[attrName];
77073
+ const path13 = this._matcher.path;
77074
+ if (path13.length === 0) return void 0;
77075
+ return path13[path13.length - 1].values?.[attrName];
76891
77076
  }
76892
77077
  /**
76893
77078
  * Check if current node has an attribute.
@@ -76895,9 +77080,9 @@ var MatcherView = class {
76895
77080
  * @returns {boolean}
76896
77081
  */
76897
77082
  hasAttr(attrName) {
76898
- const path14 = this._matcher.path;
76899
- if (path14.length === 0) return false;
76900
- const current = path14[path14.length - 1];
77083
+ const path13 = this._matcher.path;
77084
+ if (path13.length === 0) return false;
77085
+ const current = path13[path13.length - 1];
76901
77086
  return current.values !== void 0 && attrName in current.values;
76902
77087
  }
76903
77088
  /**
@@ -76923,18 +77108,18 @@ var MatcherView = class {
76923
77108
  * @returns {number}
76924
77109
  */
76925
77110
  getPosition() {
76926
- const path14 = this._matcher.path;
76927
- if (path14.length === 0) return -1;
76928
- return path14[path14.length - 1].position ?? 0;
77111
+ const path13 = this._matcher.path;
77112
+ if (path13.length === 0) return -1;
77113
+ return path13[path13.length - 1].position ?? 0;
76929
77114
  }
76930
77115
  /**
76931
77116
  * Get current node's repeat counter (occurrence count of this tag name).
76932
77117
  * @returns {number}
76933
77118
  */
76934
77119
  getCounter() {
76935
- const path14 = this._matcher.path;
76936
- if (path14.length === 0) return -1;
76937
- return path14[path14.length - 1].counter ?? 0;
77120
+ const path13 = this._matcher.path;
77121
+ if (path13.length === 0) return -1;
77122
+ return path13[path13.length - 1].counter ?? 0;
76938
77123
  }
76939
77124
  /**
76940
77125
  * Get current node's sibling index (alias for getPosition).
@@ -82592,10 +82777,10 @@ function resolveRelativePath(basePath, relativePath) {
82592
82777
  }
82593
82778
  return resolved.join("/");
82594
82779
  }
82595
- function findZipEntry(zipEntries, path14) {
82596
- if (zipEntries[path14]) return zipEntries[path14];
82597
- if (zipEntries[`xl/${path14}`]) return zipEntries[`xl/${path14}`];
82598
- const noSlash = path14.replace(/^\//, "");
82780
+ function findZipEntry(zipEntries, path13) {
82781
+ if (zipEntries[path13]) return zipEntries[path13];
82782
+ if (zipEntries[`xl/${path13}`]) return zipEntries[`xl/${path13}`];
82783
+ const noSlash = path13.replace(/^\//, "");
82599
82784
  if (zipEntries[noSlash]) return zipEntries[noSlash];
82600
82785
  return void 0;
82601
82786
  }
@@ -84496,9 +84681,9 @@ function parseExcelFromZip(zip, options) {
84496
84681
  }
84497
84682
  return result;
84498
84683
  }
84499
- function findEntry(zip, path14) {
84500
- if (zip[path14]) return zip[path14];
84501
- const lower2 = path14.toLowerCase();
84684
+ function findEntry(zip, path13) {
84685
+ if (zip[path13]) return zip[path13];
84686
+ const lower2 = path13.toLowerCase();
84502
84687
  for (const key of Object.keys(zip)) {
84503
84688
  if (key.toLowerCase() === lower2) return zip[key];
84504
84689
  }
@@ -85135,12 +85320,12 @@ function extractPivotRoundTripInfo(originalZip) {
85135
85320
  if (paths.length > 0) pivotTablesBySheetIndex.set(i2, paths);
85136
85321
  }
85137
85322
  const pivotXmlPaths = [];
85138
- for (const path14 of Object.keys(originalZip)) {
85139
- if (path14.startsWith("xl/pivotTables/") && path14.endsWith(".xml")) {
85140
- pivotXmlPaths.push(path14);
85323
+ for (const path13 of Object.keys(originalZip)) {
85324
+ if (path13.startsWith("xl/pivotTables/") && path13.endsWith(".xml")) {
85325
+ pivotXmlPaths.push(path13);
85141
85326
  }
85142
- if (path14.startsWith("xl/pivotCache/") && path14.endsWith(".xml")) {
85143
- pivotXmlPaths.push(path14);
85327
+ if (path13.startsWith("xl/pivotCache/") && path13.endsWith(".xml")) {
85328
+ pivotXmlPaths.push(path13);
85144
85329
  }
85145
85330
  }
85146
85331
  return { cachesByWorkbook, pivotTablesBySheetIndex, pivotXmlPaths };
@@ -85148,15 +85333,15 @@ function extractPivotRoundTripInfo(originalZip) {
85148
85333
  function decode3(bytes) {
85149
85334
  return new TextDecoder().decode(bytes);
85150
85335
  }
85151
- function pivotContentTypeFor(path14) {
85152
- if (path14.startsWith("xl/pivotTables/") && path14.endsWith(".xml")) {
85153
- return `<Override PartName="/${path14}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"/>`;
85336
+ function pivotContentTypeFor(path13) {
85337
+ if (path13.startsWith("xl/pivotTables/") && path13.endsWith(".xml")) {
85338
+ return `<Override PartName="/${path13}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"/>`;
85154
85339
  }
85155
- if (path14.includes("/pivotCacheDefinition") && path14.endsWith(".xml")) {
85156
- return `<Override PartName="/${path14}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"/>`;
85340
+ if (path13.includes("/pivotCacheDefinition") && path13.endsWith(".xml")) {
85341
+ return `<Override PartName="/${path13}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"/>`;
85157
85342
  }
85158
- if (path14.includes("/pivotCacheRecords") && path14.endsWith(".xml")) {
85159
- return `<Override PartName="/${path14}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml"/>`;
85343
+ if (path13.includes("/pivotCacheRecords") && path13.endsWith(".xml")) {
85344
+ return `<Override PartName="/${path13}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml"/>`;
85160
85345
  }
85161
85346
  return void 0;
85162
85347
  }
@@ -85229,16 +85414,16 @@ function exportWorkbook(workbook, originalZip) {
85229
85414
  regeneratedPaths.add(`xl/worksheets/sheet${i2 + 1}.xml`);
85230
85415
  regeneratedPaths.add(`xl/worksheets/_rels/sheet${i2 + 1}.xml.rels`);
85231
85416
  }
85232
- for (const path14 of Object.keys(originalZip)) {
85233
- if (path14.startsWith("xl/drawings/") || path14.startsWith("xl/charts/") || path14.startsWith("xl/tables/") || path14.startsWith("xl/media/")) {
85234
- regeneratedPaths.add(path14);
85417
+ for (const path13 of Object.keys(originalZip)) {
85418
+ if (path13.startsWith("xl/drawings/") || path13.startsWith("xl/charts/") || path13.startsWith("xl/tables/") || path13.startsWith("xl/media/")) {
85419
+ regeneratedPaths.add(path13);
85235
85420
  }
85236
85421
  }
85237
- for (const [path14, data2] of Object.entries(originalZip)) {
85238
- if (regeneratedPaths.has(path14)) continue;
85239
- const ext = path14.slice(path14.lastIndexOf(".") + 1).toLowerCase();
85422
+ for (const [path13, data2] of Object.entries(originalZip)) {
85423
+ if (regeneratedPaths.has(path13)) continue;
85424
+ const ext = path13.slice(path13.lastIndexOf(".") + 1).toLowerCase();
85240
85425
  if (!DECLARED_EXTENSIONS.includes(ext)) continue;
85241
- entries2[path14] = data2;
85426
+ entries2[path13] = data2;
85242
85427
  }
85243
85428
  }
85244
85429
  const sharedStrings = buildSharedStrings(workbook);
@@ -85254,8 +85439,8 @@ function exportWorkbook(workbook, originalZip) {
85254
85439
  buildWorkbookRels(workbook, pivotInfo)
85255
85440
  );
85256
85441
  const extraContentTypes = [];
85257
- for (const path14 of pivotInfo.pivotXmlPaths) {
85258
- const ct = pivotContentTypeFor(path14);
85442
+ for (const path13 of pivotInfo.pivotXmlPaths) {
85443
+ const ct = pivotContentTypeFor(path13);
85259
85444
  if (ct) extraContentTypes.push(ct);
85260
85445
  }
85261
85446
  let globalChartIndex = 1;
@@ -94441,14 +94626,14 @@ var DependencyGraph = class {
94441
94626
  stack.push({ cell: p, path: [cell, p] });
94442
94627
  }
94443
94628
  while (stack.length > 0) {
94444
- const { cell: current, path: path14 } = stack.pop();
94445
- if (current === cell || originCoarse.has(current)) return path14;
94629
+ const { cell: current, path: path13 } = stack.pop();
94630
+ if (current === cell || originCoarse.has(current)) return path13;
94446
94631
  if (visited.has(current)) continue;
94447
94632
  visited.add(current);
94448
94633
  const nextPrec = this.precedents.get(current);
94449
94634
  if (!nextPrec) continue;
94450
94635
  for (const p of nextPrec) {
94451
- stack.push({ cell: p, path: [...path14, p] });
94636
+ stack.push({ cell: p, path: [...path13, p] });
94452
94637
  }
94453
94638
  }
94454
94639
  return null;
@@ -98510,15 +98695,15 @@ function finalizeParagraph(ctx, parts, startCp, markCp, isCellMark, numbering) {
98510
98695
  };
98511
98696
  }
98512
98697
  function readPictureAt(ctx, raw, cp) {
98513
- const note = (detail) => {
98698
+ const note2 = (detail) => {
98514
98699
  ctx.diagnostics.push({ kind: "image", location: { cp }, detail });
98515
98700
  return null;
98516
98701
  };
98517
- if (raw.picLocation === void 0) return note("Picture anchor carries no Data-stream location; dropped");
98518
- if (!ctx.data) return note("Picture anchor found but the document has no Data stream; dropped");
98702
+ if (raw.picLocation === void 0) return note2("Picture anchor carries no Data-stream location; dropped");
98703
+ if (!ctx.data) return note2("Picture anchor found but the document has no Data stream; dropped");
98519
98704
  const picture = readInlinePicture(ctx.data, raw.picLocation);
98520
98705
  if (!picture) {
98521
- return note(`Picture at Data+${raw.picLocation} is not a readable inline image; dropped`);
98706
+ return note2(`Picture at Data+${raw.picLocation} is not a readable inline image; dropped`);
98522
98707
  }
98523
98708
  return picture;
98524
98709
  }
@@ -101208,9 +101393,9 @@ function buildFootnotesXml(footnotes, ctx) {
101208
101393
  boilerplateNote("separator", "-1", "w:separator"),
101209
101394
  boilerplateNote("continuationSeparator", "0", "w:continuationSeparator")
101210
101395
  ];
101211
- for (const note of footnotes) {
101212
- const content = note.content.length > 0 ? note.content.map((b) => buildBlockElement(b, ctx)) : [{ "w:p": [] }];
101213
- notes.push({ "w:footnote": content, ":@": { "@_w:id": String(note.index) } });
101396
+ for (const note2 of footnotes) {
101397
+ const content = note2.content.length > 0 ? note2.content.map((b) => buildBlockElement(b, ctx)) : [{ "w:p": [] }];
101398
+ notes.push({ "w:footnote": content, ":@": { "@_w:id": String(note2.index) } });
101214
101399
  }
101215
101400
  const root = { "w:footnotes": notes, ":@": { "@_xmlns:w": W_NS3 } };
101216
101401
  return buildXml([root]);
@@ -101247,7 +101432,7 @@ function collectFonts(fonts, acc) {
101247
101432
  function walkModel(model) {
101248
101433
  const acc = { referencedStyleIstds: /* @__PURE__ */ new Set(), fontNames: /* @__PURE__ */ new Set() };
101249
101434
  walkBlocks(model.body, acc);
101250
- for (const note of model.footnotes) walkBlocks(note.content, acc);
101435
+ for (const note2 of model.footnotes) walkBlocks(note2.content, acc);
101251
101436
  for (const style of model.styles) {
101252
101437
  if (style.chp) collectFonts(style.chp.fonts, acc);
101253
101438
  }
@@ -101402,8 +101587,8 @@ async function docModelToDocx(model) {
101402
101587
  // ../ooxml/src/opc.ts
101403
101588
  var import_jszip = __toESM(require_lib4(), 1);
101404
101589
  function dropFolderEntries(zip) {
101405
- for (const [path14, entry] of Object.entries(zip.files)) {
101406
- if (entry.dir) delete zip.files[path14];
101590
+ for (const [path13, entry] of Object.entries(zip.files)) {
101591
+ if (entry.dir) delete zip.files[path13];
101407
101592
  }
101408
101593
  }
101409
101594
 
@@ -101658,11 +101843,11 @@ async function parseDocx(buffer) {
101658
101843
  const zip = await import_jszip3.default.loadAsync(buffer);
101659
101844
  const parts = /* @__PURE__ */ new Map();
101660
101845
  const filePromises = [];
101661
- zip.forEach((path14, file2) => {
101846
+ zip.forEach((path13, file2) => {
101662
101847
  if (file2.dir) return;
101663
101848
  filePromises.push(
101664
101849
  file2.async("uint8array").then((bytes) => {
101665
- parts.set(path14, bytes);
101850
+ parts.set(path13, bytes);
101666
101851
  })
101667
101852
  );
101668
101853
  });
@@ -101894,9 +102079,9 @@ var DEFAULT_PARTS = [
101894
102079
  ["word/theme/theme1.xml", () => encoder.encode(THEME_XML)]
101895
102080
  ];
101896
102081
  function fillDefaultParts(parts) {
101897
- for (const [path14, makeBytes] of DEFAULT_PARTS) {
101898
- if (!parts.has(path14)) {
101899
- parts.set(path14, makeBytes());
102082
+ for (const [path13, makeBytes] of DEFAULT_PARTS) {
102083
+ if (!parts.has(path13)) {
102084
+ parts.set(path13, makeBytes());
101900
102085
  }
101901
102086
  }
101902
102087
  return parts;
@@ -101906,9 +102091,9 @@ function fillDefaultParts(parts) {
101906
102091
  async function serializeDocx(doc) {
101907
102092
  const zip = new import_jszip4.default();
101908
102093
  const parts = fillDefaultParts(new Map(doc.parts));
101909
- for (const [path14, bytes] of parts) {
101910
- if (path14 === "word/document.xml") continue;
101911
- zip.file(path14, bytes);
102094
+ for (const [path13, bytes] of parts) {
102095
+ if (path13 === "word/document.xml") continue;
102096
+ zip.file(path13, bytes);
101912
102097
  }
101913
102098
  const documentXml = serializeDocumentXml(doc);
101914
102099
  zip.file("word/document.xml", documentXml);
@@ -102697,12 +102882,12 @@ async function runKnowledgeCommand(client, subcommand, toolArgs, flags, restArgs
102697
102882
  // src/preview.ts
102698
102883
  import { spawn as spawn3 } from "node:child_process";
102699
102884
  import { createServer } from "node:http";
102700
- import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdtempSync, rmSync, readdirSync } from "node:fs";
102885
+ import { readFileSync, writeFileSync, existsSync as existsSync2, mkdtempSync, rmSync, readdirSync } from "node:fs";
102701
102886
  import { tmpdir as tmpdir3 } from "node:os";
102702
102887
  import { join as join2, dirname, resolve, extname, basename } from "node:path";
102703
- import { fileURLToPath as fileURLToPath3 } from "node:url";
102888
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
102704
102889
  import { setTimeout as sleep } from "node:timers/promises";
102705
- var HERE = dirname(fileURLToPath3(import.meta.url));
102890
+ var HERE = dirname(fileURLToPath2(import.meta.url));
102706
102891
  function fail3(msg) {
102707
102892
  console.error(msg);
102708
102893
  process.exit(1);
@@ -102783,8 +102968,8 @@ async function runPreviewCommand(filePath, flags) {
102783
102968
  "No Chrome/Chromium found. Set CHROME_PATH to a Chrome binary, or install one:\n npx playwright install chromium (then it's auto-detected)\n or install Google Chrome / Chromium via your package manager."
102784
102969
  );
102785
102970
  }
102786
- const b64 = readFileSync2(abs2).toString("base64");
102787
- const bundle = readFileSync2(bundlePath, "utf8");
102971
+ const b64 = readFileSync(abs2).toString("base64");
102972
+ const bundle = readFileSync(bundlePath, "utf8");
102788
102973
  const html = `<!doctype html><html><head><meta charset="utf-8"><style>body{margin:0;background:#fff;font-family:sans-serif}#root{padding:20px;max-width:1240px;margin:0 auto}</style></head><body><div id="root"></div><script>window.__LOTICS_RENDER=${JSON.stringify({ type, b64 })}</script><script src="/render_page.js"></script></body></html>`;
102789
102974
  const server = createServer((req, res) => {
102790
102975
  if (req.url === "/render_page.js") {
@@ -102830,7 +103015,7 @@ async function runPreviewCommand(filePath, flags) {
102830
103015
  const portFile = join2(udd, "DevToolsActivePort");
102831
103016
  for (let i2 = 0; i2 < 100 && !cdpPort; i2++) {
102832
103017
  if (existsSync2(portFile)) {
102833
- const p = parseInt(readFileSync2(portFile, "utf8").split("\n")[0], 10);
103018
+ const p = parseInt(readFileSync(portFile, "utf8").split("\n")[0], 10);
102834
103019
  if (p) cdpPort = p;
102835
103020
  }
102836
103021
  if (!cdpPort) await sleep(100);
@@ -102886,7 +103071,7 @@ async function runPreviewCommand(filePath, flags) {
102886
103071
  const outPath = flags.output ? resolve(flags.output) : join2(dirname(abs2), basename(abs2, ext) + ".png");
102887
103072
  writeFileSync(outPath, Buffer.from(shot.data, "base64"));
102888
103073
  console.log(`Rendered ${basename(abs2)} \u2192 ${outPath} (${Math.min(w, MAX_CAPTURE)}\xD7${Math.min(h, MAX_CAPTURE)}, via ${basename(chrome)})`);
102889
- for (const warn of warnings) console.error(` \u26A0 ${warn}`);
103074
+ for (const warn2 of warnings) console.error(` \u26A0 ${warn2}`);
102890
103075
  } finally {
102891
103076
  cleanup();
102892
103077
  }
@@ -103037,8 +103222,8 @@ function prompt(question) {
103037
103222
  });
103038
103223
  });
103039
103224
  }
103040
- async function publicPost(path14, body) {
103041
- const response = await fetch(`${API_BASE_URL}${path14}`, {
103225
+ async function publicPost(path13, body) {
103226
+ const response = await fetch(`${API_BASE_URL}${path13}`, {
103042
103227
  method: "POST",
103043
103228
  headers: { "Content-Type": "application/json" },
103044
103229
  body: JSON.stringify(body)
@@ -103046,35 +103231,73 @@ async function publicPost(path14, body) {
103046
103231
  const data2 = await response.json();
103047
103232
  return { ok: response.ok, status: response.status, data: data2 };
103048
103233
  }
103049
- async function handleSignup(positionalEmail, flags) {
103050
- const email3 = positionalEmail ?? (process.stdin.isTTY ? await prompt("Email: ") : "");
103051
- if (!email3) {
103052
- console.error("Email is required. Usage: lotics auth signup <email>");
103053
- process.exit(1);
103054
- }
103055
- const name2 = flags.name ?? (process.stdin.isTTY ? await prompt("Name (enter to use email): ") : void 0);
103056
- const body = { email: email3 };
103057
- if (name2) body.name = name2;
103058
- if (flags.timezone) body.timezone = flags.timezone;
103234
+ async function signupAccount(args) {
103235
+ const body = { email: args.email };
103236
+ if (args.name) body.name = args.name;
103237
+ if (args.timezone) body.timezone = args.timezone;
103059
103238
  const { ok, status, data: data2 } = await publicPost("/v1/cli/signup", body);
103060
103239
  if (!ok) {
103061
103240
  if (status === 409) {
103062
- console.error("An account with this email already exists. Use: lotics auth api-key");
103063
- } else if (status === 429) {
103064
- console.error("Too many signup attempts. Try again later.");
103065
- } else {
103066
- console.error(`Signup failed: ${data2.message ?? JSON.stringify(data2)}`);
103241
+ throw new Error(
103242
+ `An account with ${args.email} already exists.
103243
+ If this machine is already signed in: lotics auth whoami
103244
+ Otherwise take an API key from Settings \u2192 API keys at ${WEB_APP_URL}, then:
103245
+ lotics auth api-key <key>`
103246
+ );
103067
103247
  }
103068
- process.exit(1);
103248
+ if (status === 429) {
103249
+ throw new Error("Too many signup attempts. Try again later.");
103250
+ }
103251
+ throw new Error(`Signup failed: ${data2.message ?? JSON.stringify(data2)}`);
103069
103252
  }
103070
103253
  const apiKey = data2.api_key;
103071
103254
  const orgId = data2.organization_id;
103072
103255
  const workspaceId = data2.workspace_id;
103073
- const orgName = name2 || email3.split("@")[0];
103256
+ const orgName = args.name || args.email.split("@")[0];
103074
103257
  upsertProfile(orgId, { api_key: apiKey, org_name: orgName, workspace_id: workspaceId });
103075
103258
  const existing = loadGlobalConfig() ?? {};
103076
- saveGlobalConfig({ ...existing, email: email3 });
103077
- setActiveOrg(orgId, flags.local ? "local" : "global");
103259
+ saveGlobalConfig({ ...existing, email: args.email });
103260
+ setActiveOrg(orgId, args.local ? "local" : "global");
103261
+ return { email: args.email, apiKey, orgId, orgName, workspaceId };
103262
+ }
103263
+ function alreadySetUp(email3) {
103264
+ const config2 = loadGlobalConfig();
103265
+ if (config2?.email !== email3) return false;
103266
+ return Object.keys(config2.profiles ?? {}).length > 0;
103267
+ }
103268
+ async function handleSignup(positionalEmail, flags) {
103269
+ const email3 = positionalEmail ?? (process.stdin.isTTY ? await prompt("Email: ") : "");
103270
+ if (!email3) {
103271
+ console.error("Email is required. Usage: lotics auth signup <email>");
103272
+ process.exit(1);
103273
+ }
103274
+ if (alreadySetUp(email3)) {
103275
+ console.error(
103276
+ `This machine is already signed in as ${email3} \u2014 no new account needed.
103277
+ Carry on: lotics starter list
103278
+ Which account: lotics auth whoami` + // `--local` asked for a pin, and returning early gives none. Saying
103279
+ // nothing would leave a flag silently ignored, which is worse than the
103280
+ // 409 this replaced.
103281
+ (flags.local === true ? `
103282
+ --local pins a directory to an org, which signup does not do for an
103283
+ account that exists \u2014 use: lotics org use <name|id> --local` : "")
103284
+ );
103285
+ return;
103286
+ }
103287
+ const name2 = flags.name ?? (process.stdin.isTTY ? await prompt("Name (enter to use email): ") : void 0);
103288
+ let account;
103289
+ try {
103290
+ account = await signupAccount({
103291
+ email: email3,
103292
+ ...name2 ? { name: name2 } : {},
103293
+ ...flags.timezone ? { timezone: flags.timezone } : {},
103294
+ ...flags.local === true ? { local: true } : {}
103295
+ });
103296
+ } catch (error52) {
103297
+ console.error(error52 instanceof Error ? error52.message : String(error52));
103298
+ process.exit(1);
103299
+ }
103300
+ const { orgId, orgName } = account;
103078
103301
  console.error(`Account created. You can now use the CLI.`);
103079
103302
  console.error(` Email: ${email3}`);
103080
103303
  console.error(` Org: ${orgName} (${orgId})`);
@@ -103206,13 +103429,13 @@ async function resolveWorkspace(client, ctx) {
103206
103429
  function resolveUploadPaths(rawPaths) {
103207
103430
  const result = [];
103208
103431
  for (const p of rawPaths) {
103209
- const resolved = path13.resolve(p);
103432
+ const resolved = path12.resolve(p);
103210
103433
  const stat = fs14.statSync(resolved);
103211
103434
  if (stat.isDirectory()) {
103212
103435
  const entries2 = fs14.readdirSync(resolved, { withFileTypes: true });
103213
103436
  for (const entry of entries2) {
103214
103437
  if (entry.isFile()) {
103215
- result.push(path13.join(resolved, entry.name));
103438
+ result.push(path12.join(resolved, entry.name));
103216
103439
  }
103217
103440
  }
103218
103441
  } else {
@@ -103261,8 +103484,65 @@ async function main() {
103261
103484
  console.log(VERSION);
103262
103485
  return;
103263
103486
  }
103487
+ if (command === "start") {
103488
+ const starterId = subcommand;
103489
+ if (!starterId) {
103490
+ console.error("Usage: lotics start <starter_id> [path] [--email <you@co.com>] [--json]");
103491
+ console.error(" Creates an account if this machine has none, then copies the starter");
103492
+ console.error(" into its workspace: schema, templates, knowledge docs, sample records");
103493
+ console.error(" and a deployed app. Ends with a one-time sign-in link.");
103494
+ console.error(" --json prints one object and nothing else.");
103495
+ process.exit(1);
103496
+ }
103497
+ if (flags.json) setMachineOutput(true);
103498
+ const existing = resolveContext(flags);
103499
+ if (existing === null) {
103500
+ const email3 = flags.email ?? (process.stdin.isTTY ? await prompt("Email: ") : "");
103501
+ if (!email3) {
103502
+ console.error(
103503
+ "This machine has no Lotics credential yet, so `start` needs an email to create one:\n lotics start " + starterId + " --email you@company.com"
103504
+ );
103505
+ process.exit(1);
103506
+ }
103507
+ try {
103508
+ const account = await signupAccount({
103509
+ email: email3,
103510
+ ...flags.name ? { name: flags.name } : {},
103511
+ ...flags.timezone ? { timezone: flags.timezone } : {},
103512
+ ...flags.local === true ? { local: true } : {}
103513
+ });
103514
+ note(`Account created for ${account.email} \u2014 org ${account.orgName} (${account.orgId}).`);
103515
+ } catch (error52) {
103516
+ console.error(error52 instanceof Error ? error52.message : String(error52));
103517
+ process.exit(1);
103518
+ }
103519
+ } else if (flags.email !== void 0) {
103520
+ console.error(
103521
+ `Already authenticated (${SOURCE_LABELS[existing.source]}), so --email would create a SECOND account and copy into it.
103522
+ Copy into the account you have: lotics start ${starterId}
103523
+ Or create another one first: lotics auth signup ${flags.email}`
103524
+ );
103525
+ process.exit(1);
103526
+ }
103527
+ const { client: client2, ctx: ctx2 } = requireClient(flags);
103528
+ await resolveWorkspace(client2, ctx2);
103529
+ const result = await starterInit(client2, {
103530
+ starter_id: starterId,
103531
+ ...toolArgs !== void 0 ? { targetPath: toolArgs } : {},
103532
+ ...flags.noSampleData === true ? { noSampleData: true } : {},
103533
+ ...flags.adopt === true ? { adopt: true } : {}
103534
+ });
103535
+ if (flags.json) {
103536
+ emitJson({ organization_id: ctx2.orgId, workspace_id: ctx2.workspaceId, ...result });
103537
+ }
103538
+ return;
103539
+ }
103264
103540
  if (command === "starter") {
103265
103541
  if (subcommand === "list") {
103542
+ if (resolveContext(flags) === null) {
103543
+ await starterListPublic();
103544
+ return;
103545
+ }
103266
103546
  const { client: client2, ctx: ctx2 } = requireClient(flags);
103267
103547
  await resolveWorkspace(client2, ctx2);
103268
103548
  await starterList(client2);
@@ -103286,14 +103566,18 @@ async function main() {
103286
103566
  console.error(" locally, so it needs node and a few minutes.");
103287
103567
  process.exit(1);
103288
103568
  }
103569
+ if (flags.json) setMachineOutput(true);
103289
103570
  const { client: client2, ctx: ctx2 } = requireClient(flags);
103290
103571
  await resolveWorkspace(client2, ctx2);
103291
- await starterInit(client2, {
103572
+ const result = await starterInit(client2, {
103292
103573
  starter_id: toolArgs,
103293
103574
  ...restArgs[0] !== void 0 ? { targetPath: restArgs[0] } : {},
103294
103575
  ...flags.noSampleData === true ? { noSampleData: true } : {},
103295
103576
  ...flags.adopt === true ? { adopt: true } : {}
103296
103577
  });
103578
+ if (flags.json) {
103579
+ emitJson({ organization_id: ctx2.orgId, workspace_id: ctx2.workspaceId, ...result });
103580
+ }
103297
103581
  return;
103298
103582
  }
103299
103583
  if (subcommand === "fixtures") {
@@ -103464,7 +103748,7 @@ async function main() {
103464
103748
  if (command === "preview") {
103465
103749
  if (subcommand && isStoredFileId(subcommand)) {
103466
103750
  const { client: client2 } = requireClient(flags);
103467
- const tmpDir = fs14.mkdtempSync(path13.join(os4.tmpdir(), "lotics-preview-"));
103751
+ const tmpDir = fs14.mkdtempSync(path12.join(os4.tmpdir(), "lotics-preview-"));
103468
103752
  try {
103469
103753
  const { path: localPath, filename } = await client2.downloadFileById(subcommand, tmpDir);
103470
103754
  const output = flags.output ?? defaultPreviewOutputPath(filename, process.cwd());
@@ -104067,6 +104351,7 @@ ${JSON.stringify(info.input_schema, null, 2)}`);
104067
104351
  }
104068
104352
  }
104069
104353
  main().then(() => checkForUpdate(VERSION)).catch((error52) => {
104354
+ flushPendingWarnings();
104070
104355
  console.error(describeCliError(error52));
104071
104356
  process.exit(1);
104072
104357
  });