@lotics/cli 0.60.1 → 0.62.1

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
@@ -12243,12 +12243,12 @@ var require_tokens = __commonJS({
12243
12243
  });
12244
12244
  }
12245
12245
  exports2.assignCategoriesMapProp = assignCategoriesMapProp;
12246
- function singleAssignCategoriesToksMap(path7, nextNode) {
12247
- utils_1.forEach(path7, function(pathNode) {
12246
+ function singleAssignCategoriesToksMap(path8, nextNode) {
12247
+ utils_1.forEach(path8, function(pathNode) {
12248
12248
  nextNode.categoryMatchesMap[pathNode.tokenTypeIdx] = true;
12249
12249
  });
12250
12250
  utils_1.forEach(nextNode.CATEGORIES, function(nextCategory) {
12251
- var newPath = path7.concat(nextNode);
12251
+ var newPath = path8.concat(nextNode);
12252
12252
  if (!utils_1.contains(newPath, nextCategory)) {
12253
12253
  singleAssignCategoriesToksMap(newPath, nextCategory);
12254
12254
  }
@@ -14011,10 +14011,10 @@ var require_interpreter = __commonJS({
14011
14011
  /** @class */
14012
14012
  (function(_super) {
14013
14013
  __extends(AbstractNextPossibleTokensWalker2, _super);
14014
- function AbstractNextPossibleTokensWalker2(topProd, path7) {
14014
+ function AbstractNextPossibleTokensWalker2(topProd, path8) {
14015
14015
  var _this = _super.call(this) || this;
14016
14016
  _this.topProd = topProd;
14017
- _this.path = path7;
14017
+ _this.path = path8;
14018
14018
  _this.possibleTokTypes = [];
14019
14019
  _this.nextProductionName = "";
14020
14020
  _this.nextProductionOccurrence = 0;
@@ -14068,9 +14068,9 @@ var require_interpreter = __commonJS({
14068
14068
  /** @class */
14069
14069
  (function(_super) {
14070
14070
  __extends(NextAfterTokenWalker2, _super);
14071
- function NextAfterTokenWalker2(topProd, path7) {
14072
- var _this = _super.call(this, topProd, path7) || this;
14073
- _this.path = path7;
14071
+ function NextAfterTokenWalker2(topProd, path8) {
14072
+ var _this = _super.call(this, topProd, path8) || this;
14073
+ _this.path = path8;
14074
14074
  _this.nextTerminalName = "";
14075
14075
  _this.nextTerminalOccurrence = 0;
14076
14076
  _this.nextTerminalName = _this.path.lastTok.name;
@@ -14767,10 +14767,10 @@ var require_lookahead = __commonJS({
14767
14767
  }
14768
14768
  return result;
14769
14769
  }
14770
- function pathToHashKeys(path7) {
14770
+ function pathToHashKeys(path8) {
14771
14771
  var keys2 = [""];
14772
- for (var i2 = 0; i2 < path7.length; i2++) {
14773
- var tokType = path7[i2];
14772
+ for (var i2 = 0; i2 < path8.length; i2++) {
14773
+ var tokType = path8[i2];
14774
14774
  var longerKeys = [];
14775
14775
  for (var j = 0; j < keys2.length; j++) {
14776
14776
  var currShorterKey = keys2[j];
@@ -15082,9 +15082,9 @@ var require_checks = __commonJS({
15082
15082
  return errors;
15083
15083
  }
15084
15084
  exports2.validateRuleIsOverridden = validateRuleIsOverridden;
15085
- function validateNoLeftRecursion(topRule, currRule, errMsgProvider, path7) {
15086
- if (path7 === void 0) {
15087
- path7 = [];
15085
+ function validateNoLeftRecursion(topRule, currRule, errMsgProvider, path8) {
15086
+ if (path8 === void 0) {
15087
+ path8 = [];
15088
15088
  }
15089
15089
  var errors = [];
15090
15090
  var nextNonTerminals = getFirstNoneTerminal(currRule.definition);
@@ -15097,15 +15097,15 @@ var require_checks = __commonJS({
15097
15097
  errors.push({
15098
15098
  message: errMsgProvider.buildLeftRecursionError({
15099
15099
  topLevelRule: topRule,
15100
- leftRecursionPath: path7
15100
+ leftRecursionPath: path8
15101
15101
  }),
15102
15102
  type: parser_1.ParserDefinitionErrorType.LEFT_RECURSION,
15103
15103
  ruleName
15104
15104
  });
15105
15105
  }
15106
- var validNextSteps = utils.difference(nextNonTerminals, path7.concat([topRule]));
15106
+ var validNextSteps = utils.difference(nextNonTerminals, path8.concat([topRule]));
15107
15107
  var errorsFromNextSteps = utils.map(validNextSteps, function(currRefRule) {
15108
- var newPath = utils.cloneArr(path7);
15108
+ var newPath = utils.cloneArr(path8);
15109
15109
  newPath.push(currRefRule);
15110
15110
  return validateNoLeftRecursion(topRule, currRefRule, errMsgProvider, newPath);
15111
15111
  });
@@ -22714,8 +22714,8 @@ var require_utils4 = __commonJS({
22714
22714
  var result = transform[inputType][outputType](input);
22715
22715
  return result;
22716
22716
  };
22717
- exports2.resolve = function(path7) {
22718
- var parts = path7.split("/");
22717
+ exports2.resolve = function(path8) {
22718
+ var parts = path8.split("/");
22719
22719
  var result = [];
22720
22720
  for (var index = 0; index < parts.length; index++) {
22721
22721
  var part = parts[index];
@@ -28568,18 +28568,18 @@ var require_object = __commonJS({
28568
28568
  var object = new ZipObject(name, zipObjectContent, o);
28569
28569
  this.files[name] = object;
28570
28570
  };
28571
- var parentFolder = function(path7) {
28572
- if (path7.slice(-1) === "/") {
28573
- path7 = path7.substring(0, path7.length - 1);
28571
+ var parentFolder = function(path8) {
28572
+ if (path8.slice(-1) === "/") {
28573
+ path8 = path8.substring(0, path8.length - 1);
28574
28574
  }
28575
- var lastSlash = path7.lastIndexOf("/");
28576
- return lastSlash > 0 ? path7.substring(0, lastSlash) : "";
28575
+ var lastSlash = path8.lastIndexOf("/");
28576
+ return lastSlash > 0 ? path8.substring(0, lastSlash) : "";
28577
28577
  };
28578
- var forceTrailingSlash = function(path7) {
28579
- if (path7.slice(-1) !== "/") {
28580
- path7 += "/";
28578
+ var forceTrailingSlash = function(path8) {
28579
+ if (path8.slice(-1) !== "/") {
28580
+ path8 += "/";
28581
28581
  }
28582
- return path7;
28582
+ return path8;
28583
28583
  };
28584
28584
  var folderAdd = function(name, createFolders) {
28585
28585
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults2.createFolders;
@@ -29581,8 +29581,8 @@ var require_lib3 = __commonJS({
29581
29581
  // src/cli.ts
29582
29582
  import dns from "node:dns";
29583
29583
  import net2 from "node:net";
29584
- import fs7 from "node:fs";
29585
- import path6 from "node:path";
29584
+ import fs8 from "node:fs";
29585
+ import path7 from "node:path";
29586
29586
  import readline from "node:readline";
29587
29587
 
29588
29588
  // src/client.ts
@@ -29680,8 +29680,8 @@ var LoticsClient = class {
29680
29680
  }
29681
29681
  return headers;
29682
29682
  }
29683
- async request(method, path7, body) {
29684
- const url = `${this.baseUrl}${path7}`;
29683
+ async request(method, path8, body) {
29684
+ const url = `${this.baseUrl}${path8}`;
29685
29685
  const headers = this.buildHeaders();
29686
29686
  const init = { method, headers };
29687
29687
  if (body !== void 0) {
@@ -30342,8 +30342,8 @@ var pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
30342
30342
  var VERSION = pkg.version;
30343
30343
 
30344
30344
  // src/app_commands.ts
30345
- import fs3 from "node:fs";
30346
- import path4 from "node:path";
30345
+ import fs4 from "node:fs";
30346
+ import path5 from "node:path";
30347
30347
  import { spawn as spawn2 } from "node:child_process";
30348
30348
  import { tmpdir } from "node:os";
30349
30349
 
@@ -30403,7 +30403,9 @@ function buildStarterTemplate(args) {
30403
30403
  "@vitejs/plugin-react": "^4.3.0",
30404
30404
  jsdom: "^25.0.0",
30405
30405
  oxlint: "^0.13.0",
30406
- typescript: "^5.6.0",
30406
+ // Track the platform's TS major so `lotics app workflow check` (which runs
30407
+ // the app's OWN typescript) gives the same verdict the server does at set-time.
30408
+ typescript: "~6.0.3",
30407
30409
  vite: "^5.4.0",
30408
30410
  vitest: "^2.1.0"
30409
30411
  },
@@ -30447,45 +30449,17 @@ function buildStarterTemplate(args) {
30447
30449
  // base, which IS read. (GAP-38.)
30448
30450
  include: ["src", ".lotics/**/*"],
30449
30451
  // src/workflows/<alias>.ts bodies run on the server (workflow globals:
30450
- // trigger / runtime / tool calls). They ARE locally typecheckable
30451
- // each is wrapped in the server's `__workflow` envelope and references
30452
- // its per-alias `.lotics/workflows/<alias>.globals.d.ts` but under a
30453
- // DEDICATED config that mirrors the server's compile options (es2022
30454
- // lib, NO DOM). The main `npm run typecheck` excludes them so it stays
30455
- // green and DOM-lib-free for bodies; check a body with
30456
- // `tsc -p tsconfig.workflows.json`. The server is the single verifier
30457
- // on `lotics app workflow set`.
30458
- exclude: ["node_modules", "src/workflows"]
30459
- },
30460
- null,
30461
- 2
30462
- ) + "\n"
30463
- },
30464
- {
30465
- // Dedicated config for the editable workflow bodies (GAP-59). Mirrors the
30466
- // SERVER's set-time typecheck options (strict, lib es2022 with NO DOM,
30467
- // target ES2022, skipLibCheck) so a local `tsc -p tsconfig.workflows.json`
30468
- // gives the same verdict the server would on `lotics app workflow set`.
30469
- // Each body file is a module (the CLI writes a bookkeeping `export {};`),
30470
- // so the per-file `__workflow` wrapper doesn't collide across bodies.
30471
- path: "tsconfig.workflows.json",
30472
- content: JSON.stringify(
30473
- {
30474
- compilerOptions: {
30475
- target: "ES2022",
30476
- lib: ["ES2022"],
30477
- module: "ESNext",
30478
- moduleResolution: "Bundler",
30479
- strict: true,
30480
- skipLibCheck: true,
30481
- noEmit: true,
30482
- types: []
30483
- },
30484
- // The bodies + their per-alias ambient globals. `.lotics/workflows`
30485
- // uses the explicit `**/*` glob for the same dot-dir walk reason the
30486
- // main config does (TS's include walk skips bare dot-dirs).
30487
- include: ["src/workflows", ".lotics/workflows/**/*"],
30488
- exclude: ["node_modules"]
30452
+ // trigger / runtime / tool calls), and each pulls its own per-alias
30453
+ // ambient globals under `.lotics/workflows/`. BOTH are excluded from
30454
+ // the main typecheck: the bodies would apply the app's DOM lib (the
30455
+ // server doesn't), and the per-alias `.globals.d.ts` files each declare
30456
+ // their OWN ambient `trigger` loading several into one program
30457
+ // collides those declarations and reports false errors. Type-check the
30458
+ // bodies with `lotics app workflow check` instead it builds ONE
30459
+ // isolated program per alias (mirroring the server's set-time verify),
30460
+ // so the ambient `trigger` is unambiguous. The server stays the single
30461
+ // verifier on `lotics app workflow set`.
30462
+ exclude: ["node_modules", "src/workflows", ".lotics/workflows"]
30489
30463
  },
30490
30464
  null,
30491
30465
  2
@@ -30846,6 +30820,7 @@ Editable JS-subset bodies of this app's workflows live here, one file per alias:
30846
30820
  \`\`\`bash
30847
30821
  lotics app workflow pull # write/refresh every src/workflows/<alias>.ts from the server
30848
30822
  # edit src/workflows/<alias>.ts
30823
+ lotics app workflow check <alias> # typecheck the body locally (same verdict as \`set\`)
30849
30824
  lotics app workflow set <alias> # push it back through set_app_workflow (the server verifies)
30850
30825
  \`\`\`
30851
30826
 
@@ -30857,15 +30832,17 @@ A workflow body is a **JS-subset expression** that runs server-side using workfl
30857
30832
  globals (\`trigger\`, \`runtime\`, tool calls) and ends with \`return({ data })\`. Each
30858
30833
  pulled file wraps the body in the server's \`__workflow\` envelope and references its
30859
30834
  per-alias ambient globals at \`.lotics/workflows/<alias>.globals.d.ts\`, so it **is**
30860
- locally typecheckable \u2014 under the dedicated config that mirrors the server's compile
30861
- options:
30835
+ locally typecheckable:
30862
30836
 
30863
30837
  \`\`\`bash
30864
- tsc -p tsconfig.workflows.json # same verdict the server gives on \`set\`
30838
+ lotics app workflow check # check every body; [alias] to check one
30865
30839
  \`\`\`
30866
30840
 
30867
- The main \`npm run typecheck\` excludes \`src/workflows\` (it would apply the app's
30868
- DOM lib, which the server doesn't), so the bodies have their own config. The
30841
+ \`check\` builds ONE isolated program per alias (the app's own \`typescript\`, the
30842
+ server's compile options \u2014 strict, lib es2022 with no DOM), so each body's ambient
30843
+ \`trigger\` is unambiguous and the verdict mirrors set-time verify. The main
30844
+ \`npm run typecheck\` excludes \`src/workflows\` + \`.lotics/workflows\` (the bodies would
30845
+ apply the app's DOM lib, and the per-alias globals collide on \`trigger\`). The
30869
30846
  verification that matters still runs on the **server** when you \`set\` \u2014 the same
30870
30847
  guarantee as authoring via \`set_app_workflow\` directly. Edit only the body BETWEEN
30871
30848
  the wrapper lines; the wrapper, the \`/// <reference>\`, and the \`export {};\` marker
@@ -30905,6 +30882,9 @@ declare module "lucide-react-native/dist/esm/icons/*" {
30905
30882
  const classes: { [key: string]: string };
30906
30883
  export default classes;
30907
30884
  }
30885
+
30886
+ // Plain side-effect CSS imports (@lotics/ui ships .tsx with import "./x.css").
30887
+ declare module "*.css";
30908
30888
  `
30909
30889
  },
30910
30890
  {
@@ -32119,6 +32099,98 @@ export type AppOptions = typeof OPT;
32119
32099
  `;
32120
32100
  }
32121
32101
 
32102
+ // src/app_workflow_check.ts
32103
+ import fs3 from "node:fs";
32104
+ import path4 from "node:path";
32105
+ import { createRequire } from "node:module";
32106
+ function workflowCheckCompilerOptions(tsApi) {
32107
+ return {
32108
+ target: tsApi.ScriptTarget.ES2022,
32109
+ module: tsApi.ModuleKind.ESNext,
32110
+ lib: ["lib.es2022.d.ts"],
32111
+ strict: true,
32112
+ noImplicitAny: true,
32113
+ strictNullChecks: true,
32114
+ noImplicitReturns: false,
32115
+ noEmit: true,
32116
+ allowJs: false,
32117
+ isolatedModules: false,
32118
+ skipLibCheck: true,
32119
+ moduleResolution: tsApi.ModuleResolutionKind.NodeNext,
32120
+ types: [],
32121
+ declaration: false
32122
+ };
32123
+ }
32124
+ function bodyLineOffset(wrappedSource) {
32125
+ const lines = wrappedSource.split("\n");
32126
+ for (let i2 = 0; i2 < lines.length; i2++) {
32127
+ if (/^\s*async\s+function\s+__workflow\s*\(/.test(lines[i2])) return i2 + 1;
32128
+ }
32129
+ return 0;
32130
+ }
32131
+ async function loadProjectTypescript(projectDir) {
32132
+ const requireFromProject = createRequire(path4.join(projectDir, "package.json"));
32133
+ let resolved;
32134
+ try {
32135
+ resolved = requireFromProject.resolve("typescript");
32136
+ } catch {
32137
+ throw new Error(
32138
+ `Could not resolve 'typescript' from ${projectDir}. 'lotics app workflow check' type-checks bodies with the app's own compiler \u2014 run 'npm install' (the scaffold ships typescript as a devDependency).`
32139
+ );
32140
+ }
32141
+ const mod = await import(pathToImportUrl(resolved));
32142
+ return mod.default ?? mod;
32143
+ }
32144
+ function pathToImportUrl(p) {
32145
+ const resolved = path4.resolve(p);
32146
+ const prefixed = resolved.startsWith("/") ? resolved : `/${resolved}`;
32147
+ return `file://${prefixed.split(path4.sep).join("/")}`;
32148
+ }
32149
+ function checkOneWorkflowBody(tsApi, input) {
32150
+ const wrapped = fs3.readFileSync(input.bodyPath, "utf-8");
32151
+ const offset = bodyLineOffset(wrapped);
32152
+ const program = tsApi.createProgram({
32153
+ rootNames: [input.globalsPath, input.bodyPath],
32154
+ options: workflowCheckCompilerOptions(tsApi)
32155
+ });
32156
+ const bodyFile = program.getSourceFile(input.bodyPath);
32157
+ if (!bodyFile) {
32158
+ return [
32159
+ {
32160
+ line: 1,
32161
+ col: 1,
32162
+ code: "INTERNAL",
32163
+ message: `workflow check: body file was not loaded (${input.bodyPath})`
32164
+ }
32165
+ ];
32166
+ }
32167
+ const issues = [];
32168
+ for (const d of tsApi.getPreEmitDiagnostics(program)) {
32169
+ if (d.file !== bodyFile) continue;
32170
+ let line = 1;
32171
+ let col = 1;
32172
+ if (typeof d.start === "number") {
32173
+ const lc2 = bodyFile.getLineAndCharacterOfPosition(d.start);
32174
+ line = Math.max(1, lc2.line + 1 - offset);
32175
+ col = lc2.character + 1;
32176
+ }
32177
+ issues.push({
32178
+ line,
32179
+ col,
32180
+ code: `TS${d.code}`,
32181
+ message: tsApi.flattenDiagnosticMessageText(d.messageText, "\n")
32182
+ });
32183
+ }
32184
+ return issues;
32185
+ }
32186
+ function checkWorkflowBodies(tsApi, aliases) {
32187
+ return aliases.map(({ alias, input }) => ({
32188
+ alias,
32189
+ bodyPath: input.bodyPath,
32190
+ issues: checkOneWorkflowBody(tsApi, input)
32191
+ }));
32192
+ }
32193
+
32122
32194
  // src/app_commands.ts
32123
32195
  async function fetchLatestNpmVersion(packageName) {
32124
32196
  try {
@@ -32136,15 +32208,17 @@ async function fetchLatestNpmVersion(packageName) {
32136
32208
  return null;
32137
32209
  }
32138
32210
  }
32139
- var WORKFLOWS_DIR = path4.join("src", "workflows");
32140
- var WORKFLOW_GLOBALS_DIR = path4.join(".lotics", "workflows");
32211
+ var WORKFLOWS_DIR = path5.join("src", "workflows");
32212
+ var WORKFLOW_GLOBALS_DIR = path5.join(".lotics", "workflows");
32213
+ var WORKFLOW_TSCONFIG_EXCLUDES = ["src/workflows", ".lotics/workflows"];
32141
32214
  var FALLBACK_ENVELOPE_PREFIX = "async function __workflow(): Promise<__WorkflowReturn | void> {\n";
32142
32215
  var FALLBACK_ENVELOPE_SUFFIX = "\n}";
32143
32216
  function workflowFileHeader(alias) {
32144
- const refPath = path4.join("..", "..", WORKFLOW_GLOBALS_DIR, `${alias}.globals.d.ts`).split(path4.sep).join("/");
32217
+ const refPath = path5.join("..", "..", WORKFLOW_GLOBALS_DIR, `${alias}.globals.d.ts`).split(path5.sep).join("/");
32145
32218
  return `/// <reference path="${refPath}" />
32146
32219
  // Auto-pulled workflow body for "${alias}". Edit the BODY between the wrapper
32147
- // lines below, then push with:
32220
+ // lines below, then check + push with:
32221
+ // lotics app workflow check ${alias}
32148
32222
  // lotics app workflow set ${alias}
32149
32223
  // The push goes through set_app_workflow, where the SERVER verifies the body.
32150
32224
  // The __workflow wrapper + the reference above are CLI bookkeeping (stripped on
@@ -32154,25 +32228,25 @@ export {};
32154
32228
  `;
32155
32229
  }
32156
32230
  function workflowFilePath(projectDir, alias) {
32157
- return path4.join(projectDir, WORKFLOWS_DIR, `${alias}.ts`);
32231
+ return path5.join(projectDir, WORKFLOWS_DIR, `${alias}.ts`);
32158
32232
  }
32159
32233
  function workflowGlobalsPath(projectDir, alias) {
32160
- return path4.join(projectDir, WORKFLOW_GLOBALS_DIR, `${alias}.globals.d.ts`);
32234
+ return path5.join(projectDir, WORKFLOW_GLOBALS_DIR, `${alias}.globals.d.ts`);
32161
32235
  }
32162
32236
  function writeWorkflowGlobals(projectDir, alias, dts) {
32163
- const dir = path4.join(projectDir, WORKFLOW_GLOBALS_DIR);
32164
- fs3.mkdirSync(dir, { recursive: true });
32237
+ const dir = path5.join(projectDir, WORKFLOW_GLOBALS_DIR);
32238
+ fs4.mkdirSync(dir, { recursive: true });
32165
32239
  const file = workflowGlobalsPath(projectDir, alias);
32166
- fs3.writeFileSync(file, `${dts.replace(/\s+$/, "")}
32240
+ fs4.writeFileSync(file, `${dts.replace(/\s+$/, "")}
32167
32241
  `);
32168
32242
  return file;
32169
32243
  }
32170
32244
  function writeWorkflowFile(projectDir, alias, source, envelope = { prefix: FALLBACK_ENVELOPE_PREFIX, suffix: FALLBACK_ENVELOPE_SUFFIX }) {
32171
- const dir = path4.join(projectDir, WORKFLOWS_DIR);
32172
- fs3.mkdirSync(dir, { recursive: true });
32245
+ const dir = path5.join(projectDir, WORKFLOWS_DIR);
32246
+ fs4.mkdirSync(dir, { recursive: true });
32173
32247
  const file = workflowFilePath(projectDir, alias);
32174
32248
  const body = source.replace(/\s+$/, "");
32175
- fs3.writeFileSync(file, `${workflowFileHeader(alias)}
32249
+ fs4.writeFileSync(file, `${workflowFileHeader(alias)}
32176
32250
  ${envelope.prefix}${body}${envelope.suffix}
32177
32251
  `);
32178
32252
  return file;
@@ -32253,11 +32327,11 @@ function runNpm(args, cwd) {
32253
32327
  });
32254
32328
  }
32255
32329
  function readAppMeta(projectDir) {
32256
- const pkgPath2 = path4.join(projectDir, "package.json");
32257
- if (!fs3.existsSync(pkgPath2)) {
32330
+ const pkgPath2 = path5.join(projectDir, "package.json");
32331
+ if (!fs4.existsSync(pkgPath2)) {
32258
32332
  throw new Error(`No package.json found at ${projectDir}. Run 'lotics app create' first.`);
32259
32333
  }
32260
- const pkg2 = JSON.parse(fs3.readFileSync(pkgPath2, "utf-8"));
32334
+ const pkg2 = JSON.parse(fs4.readFileSync(pkgPath2, "utf-8"));
32261
32335
  if (!pkg2.lotics?.app_id || !pkg2.lotics.workspace_id) {
32262
32336
  throw new Error(
32263
32337
  `package.json is missing the 'lotics' field with app_id and workspace_id. Was this folder created with 'lotics app create'?`
@@ -32275,24 +32349,52 @@ function readAppMeta(projectDir) {
32275
32349
  };
32276
32350
  }
32277
32351
  function writeAppMeta(projectDir, meta) {
32278
- const pkgPath2 = path4.join(projectDir, "package.json");
32279
- const pkg2 = JSON.parse(fs3.readFileSync(pkgPath2, "utf-8"));
32352
+ const pkgPath2 = path5.join(projectDir, "package.json");
32353
+ const pkg2 = JSON.parse(fs4.readFileSync(pkgPath2, "utf-8"));
32280
32354
  pkg2.lotics = meta;
32281
- fs3.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
32355
+ fs4.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
32356
+ }
32357
+ function ensureWorkflowTsconfigExcludes(projectDir) {
32358
+ const tsconfigPath = path5.join(projectDir, "tsconfig.json");
32359
+ if (!fs4.existsSync(tsconfigPath)) {
32360
+ console.error(
32361
+ `\u26A0 No tsconfig.json at ${projectDir} \u2014 could not ensure ${WORKFLOW_TSCONFIG_EXCLUDES.join(", ")} are excluded. Add them to your tsconfig's "exclude" so npm run typecheck skips the workflow bodies.`
32362
+ );
32363
+ return;
32364
+ }
32365
+ let parsed;
32366
+ try {
32367
+ parsed = JSON.parse(fs4.readFileSync(tsconfigPath, "utf-8"));
32368
+ } catch (err2) {
32369
+ console.error(
32370
+ `\u26A0 Could not parse tsconfig.json (${err2 instanceof Error ? err2.message : String(err2)}) \u2014 add ${WORKFLOW_TSCONFIG_EXCLUDES.join(", ")} to its "exclude" manually so npm run typecheck skips the workflow bodies.`
32371
+ );
32372
+ return;
32373
+ }
32374
+ if (!parsed || typeof parsed !== "object") return;
32375
+ const current = Array.isArray(parsed.exclude) ? parsed.exclude : [];
32376
+ const currentStrings = current.filter((e) => typeof e === "string");
32377
+ const toAdd = WORKFLOW_TSCONFIG_EXCLUDES.filter((g) => !currentStrings.includes(g));
32378
+ if (toAdd.length === 0) return;
32379
+ parsed.exclude = [...currentStrings, ...toAdd];
32380
+ fs4.writeFileSync(tsconfigPath, JSON.stringify(parsed, null, 2) + "\n");
32381
+ console.error(
32382
+ `Patched tsconfig.json: added ${toAdd.join(", ")} to "exclude" so npm run typecheck skips the workflow bodies (check them with: lotics app workflow check).`
32383
+ );
32282
32384
  }
32283
32385
  function writeAppDts(projectDir, manifest) {
32284
- const dotLotics = path4.join(projectDir, ".lotics");
32285
- fs3.mkdirSync(dotLotics, { recursive: true });
32386
+ const dotLotics = path5.join(projectDir, ".lotics");
32387
+ fs4.mkdirSync(dotLotics, { recursive: true });
32286
32388
  const written = [
32287
- [path4.join(dotLotics, "app_workflows.d.ts"), generateAppWorkflowsDts(manifest.workflows)],
32288
- [path4.join(dotLotics, "app_queries.d.ts"), generateAppQueriesDts(manifest.queries)],
32289
- [path4.join(dotLotics, "app_agents.d.ts"), generateAppAgentsDts(manifest.agents)]
32389
+ [path5.join(dotLotics, "app_workflows.d.ts"), generateAppWorkflowsDts(manifest.workflows)],
32390
+ [path5.join(dotLotics, "app_queries.d.ts"), generateAppQueriesDts(manifest.queries)],
32391
+ [path5.join(dotLotics, "app_agents.d.ts"), generateAppAgentsDts(manifest.agents)]
32290
32392
  ];
32291
- for (const [file, content] of written) fs3.writeFileSync(file, content);
32393
+ for (const [file, content] of written) fs4.writeFileSync(file, content);
32292
32394
  return written.map(([file]) => file);
32293
32395
  }
32294
32396
  function readCodegenTablesAllowlist(projectDir) {
32295
- const pkg2 = JSON.parse(fs3.readFileSync(path4.join(projectDir, "package.json"), "utf-8"));
32397
+ const pkg2 = JSON.parse(fs4.readFileSync(path5.join(projectDir, "package.json"), "utf-8"));
32296
32398
  const tables = pkg2.lotics?.codegen?.tables;
32297
32399
  return Array.isArray(tables) ? tables.filter((t) => typeof t === "string") : [];
32298
32400
  }
@@ -32305,14 +32407,14 @@ function resolveCodegenTableIds(projectDir, queries) {
32305
32407
  return [...ids];
32306
32408
  }
32307
32409
  function writeAppFields(projectDir, tables) {
32308
- const dotLotics = path4.join(projectDir, ".lotics");
32309
- fs3.mkdirSync(dotLotics, { recursive: true });
32310
- const file = path4.join(dotLotics, "app_fields.ts");
32311
- fs3.writeFileSync(file, generateAppFields(tables));
32410
+ const dotLotics = path5.join(projectDir, ".lotics");
32411
+ fs4.mkdirSync(dotLotics, { recursive: true });
32412
+ const file = path5.join(dotLotics, "app_fields.ts");
32413
+ fs4.writeFileSync(file, generateAppFields(tables));
32312
32414
  return file;
32313
32415
  }
32314
32416
  async function appCodegen(args) {
32315
- const projectDir = path4.resolve(args.projectDir ?? process.cwd());
32417
+ const projectDir = path5.resolve(args.projectDir ?? process.cwd());
32316
32418
  const meta = readAppMeta(projectDir);
32317
32419
  const dtsPaths = writeAppDts(projectDir, {
32318
32420
  workflows: meta.workflows,
@@ -32337,16 +32439,17 @@ async function appCodegen(args) {
32337
32439
  );
32338
32440
  }
32339
32441
  await refreshWorkflowGlobals(args.client, projectDir, meta.app_id, Object.keys(meta.workflows ?? {}));
32442
+ if (Object.keys(meta.workflows ?? {}).length > 0) ensureWorkflowTsconfigExcludes(projectDir);
32340
32443
  }
32341
32444
  async function refreshWorkflowGlobals(client, projectDir, app_id, aliases) {
32342
32445
  for (const alias of aliases) {
32343
32446
  const file = workflowFilePath(projectDir, alias);
32344
- if (!fs3.existsSync(file)) continue;
32345
- const body = stripWorkflowHeader(fs3.readFileSync(file, "utf-8"));
32447
+ if (!fs4.existsSync(file)) continue;
32448
+ const body = stripWorkflowHeader(fs4.readFileSync(file, "utf-8"));
32346
32449
  if (body.trim() === "") continue;
32347
32450
  const envelope = await fetchWorkflowGlobals(client, projectDir, app_id, alias);
32348
32451
  writeWorkflowFile(projectDir, alias, body, envelope);
32349
- console.error(`Refreshed ${path4.relative(projectDir, file)} + its workflow types`);
32452
+ console.error(`Refreshed ${path5.relative(projectDir, file)} + its workflow types`);
32350
32453
  }
32351
32454
  }
32352
32455
  function stampPulledManifest(projectDir, args) {
@@ -32367,21 +32470,21 @@ async function downloadToFile(url, destPath) {
32367
32470
  throw new Error(`Failed to download ${url}: ${response.status}`);
32368
32471
  }
32369
32472
  const buffer = Buffer.from(await response.arrayBuffer());
32370
- fs3.writeFileSync(destPath, buffer);
32473
+ fs4.writeFileSync(destPath, buffer);
32371
32474
  }
32372
32475
  function appDirName(name) {
32373
32476
  const cleaned = name.replace(/[/\\]+/g, "-").replace(/[<>:"|?*]/g, "").replace(/\s+/g, " ").trim().replace(/^[.\s-]+|[.\s-]+$/g, "");
32374
32477
  return cleaned || "app";
32375
32478
  }
32376
32479
  async function appCreate(client, args) {
32377
- const targetPath = path4.resolve(args.targetPath ?? appDirName(args.name));
32378
- if (fs3.existsSync(targetPath)) {
32379
- const entries = fs3.readdirSync(targetPath);
32480
+ const targetPath = path5.resolve(args.targetPath ?? appDirName(args.name));
32481
+ if (fs4.existsSync(targetPath)) {
32482
+ const entries = fs4.readdirSync(targetPath);
32380
32483
  if (entries.length > 0) {
32381
32484
  throw new Error(`Target directory ${targetPath} is not empty.`);
32382
32485
  }
32383
32486
  }
32384
- fs3.mkdirSync(targetPath, { recursive: true });
32487
+ fs4.mkdirSync(targetPath, { recursive: true });
32385
32488
  const app = await client.createApp({ name: args.name });
32386
32489
  console.error(`Created app: ${app.name} (${app.id})`);
32387
32490
  const [uiLatest, sdkLatest] = await Promise.all([
@@ -32396,9 +32499,9 @@ async function appCreate(client, args) {
32396
32499
  sdk_version: sdkLatest ? `^${sdkLatest}` : void 0
32397
32500
  });
32398
32501
  for (const file of files) {
32399
- const fullPath = path4.join(targetPath, file.path);
32400
- fs3.mkdirSync(path4.dirname(fullPath), { recursive: true });
32401
- fs3.writeFileSync(fullPath, file.content);
32502
+ const fullPath = path5.join(targetPath, file.path);
32503
+ fs4.mkdirSync(path5.dirname(fullPath), { recursive: true });
32504
+ fs4.writeFileSync(fullPath, file.content);
32402
32505
  }
32403
32506
  console.error(`Scaffolded ${files.length} files into ${targetPath}`);
32404
32507
  writeAppDts(targetPath, {});
@@ -32411,7 +32514,7 @@ async function appCreate(client, args) {
32411
32514
  });
32412
32515
  console.error(`
32413
32516
  Ready. Next steps:`);
32414
- console.error(` cd ${path4.relative(process.cwd(), targetPath) || "."}`);
32517
+ console.error(` cd ${path5.relative(process.cwd(), targetPath) || "."}`);
32415
32518
  console.error(` # edit src/App.tsx, then:`);
32416
32519
  console.error(` lotics app deploy`);
32417
32520
  }
@@ -32426,6 +32529,21 @@ async function appRename(client, args) {
32426
32529
  if (res.error) throw new Error(res.error);
32427
32530
  console.error(`App renamed: "${args.name}" (${meta.app_id})`);
32428
32531
  }
32532
+ function defaultPullTarget(appId, appName) {
32533
+ const cwdPkgPath = path5.join(process.cwd(), "package.json");
32534
+ if (fs4.existsSync(cwdPkgPath)) {
32535
+ let pkg2 = null;
32536
+ try {
32537
+ pkg2 = JSON.parse(fs4.readFileSync(cwdPkgPath, "utf-8"));
32538
+ } catch (err2) {
32539
+ console.error(
32540
+ `\u26A0 Could not parse ${cwdPkgPath} (${err2 instanceof Error ? err2.message : String(err2)}) \u2014 pulling into a fresh ${appDirName(appName)}/ subdir.`
32541
+ );
32542
+ }
32543
+ if (pkg2?.lotics?.app_id === appId) return process.cwd();
32544
+ }
32545
+ return appDirName(appName);
32546
+ }
32429
32547
  async function appPull(client, args) {
32430
32548
  const app = await client.getApp(args.app_id);
32431
32549
  if (!app.current_version_id) {
@@ -32435,16 +32553,16 @@ async function appPull(client, args) {
32435
32553
  }
32436
32554
  const version = await client.getAppVersion(app.id, app.current_version_id);
32437
32555
  const sourceUrl = await client.getAppVersionSourceUrl(app.id, version.id);
32438
- const targetPath = path4.resolve(args.targetPath ?? appDirName(app.name));
32439
- fs3.mkdirSync(targetPath, { recursive: true });
32440
- const tmpFile = path4.join(tmpdir(), `lotics-app-${app.id}-${Date.now()}.tar.gz`);
32556
+ const targetPath = path5.resolve(args.targetPath ?? defaultPullTarget(app.id, app.name));
32557
+ fs4.mkdirSync(targetPath, { recursive: true });
32558
+ const tmpFile = path5.join(tmpdir(), `lotics-app-${app.id}-${Date.now()}.tar.gz`);
32441
32559
  console.error(`Downloading source archive...`);
32442
32560
  await downloadToFile(sourceUrl, tmpFile);
32443
32561
  try {
32444
32562
  console.error(`Extracting to ${targetPath}...`);
32445
32563
  await runTar(["-xzf", tmpFile, "-C", targetPath], targetPath);
32446
32564
  } finally {
32447
- if (fs3.existsSync(tmpFile)) fs3.unlinkSync(tmpFile);
32565
+ if (fs4.existsSync(tmpFile)) fs4.unlinkSync(tmpFile);
32448
32566
  }
32449
32567
  stampPulledManifest(targetPath, {
32450
32568
  app_id: app.id,
@@ -32463,12 +32581,13 @@ async function appPull(client, args) {
32463
32581
  `Wrote ${written.length} workflow ${written.length === 1 ? "body" : "bodies"} to ${WORKFLOWS_DIR}/ (${written.join(", ")})`
32464
32582
  );
32465
32583
  }
32584
+ ensureWorkflowTsconfigExcludes(targetPath);
32466
32585
  }
32467
32586
  console.error(`Installing npm dependencies...`);
32468
32587
  await runNpm(["install"], targetPath);
32469
32588
  console.error(`
32470
32589
  Ready. Next steps:`);
32471
- console.error(` cd ${path4.relative(process.cwd(), targetPath) || "."}`);
32590
+ console.error(` cd ${path5.relative(process.cwd(), targetPath) || "."}`);
32472
32591
  console.error(` # edit src/App.tsx`);
32473
32592
  console.error(` lotics app deploy`);
32474
32593
  }
@@ -32484,21 +32603,21 @@ function undeclaredCapabilities(sourceText, declared) {
32484
32603
  return used;
32485
32604
  }
32486
32605
  function readAppSourceText(projectDir) {
32487
- const srcDir = path4.join(projectDir, "src");
32488
- if (!fs3.existsSync(srcDir)) return "";
32606
+ const srcDir = path5.join(projectDir, "src");
32607
+ if (!fs4.existsSync(srcDir)) return "";
32489
32608
  const parts = [];
32490
32609
  const walk2 = (dir) => {
32491
- for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
32492
- const full = path4.join(dir, entry.name);
32610
+ for (const entry of fs4.readdirSync(dir, { withFileTypes: true })) {
32611
+ const full = path5.join(dir, entry.name);
32493
32612
  if (entry.isDirectory()) walk2(full);
32494
- else if (/\.(ts|tsx|js|jsx)$/.test(entry.name)) parts.push(fs3.readFileSync(full, "utf8"));
32613
+ else if (/\.(ts|tsx|js|jsx)$/.test(entry.name)) parts.push(fs4.readFileSync(full, "utf8"));
32495
32614
  }
32496
32615
  };
32497
32616
  walk2(srcDir);
32498
32617
  return parts.join("\n");
32499
32618
  }
32500
32619
  async function appDeploy(client, args) {
32501
- const projectDir = path4.resolve(args.projectDir ?? process.cwd());
32620
+ const projectDir = path5.resolve(args.projectDir ?? process.cwd());
32502
32621
  const meta = readAppMeta(projectDir);
32503
32622
  const undeclared = undeclaredCapabilities(readAppSourceText(projectDir), meta.capabilities);
32504
32623
  if (undeclared.length > 0) {
@@ -32512,14 +32631,14 @@ async function appDeploy(client, args) {
32512
32631
  writeAppDts(projectDir, { workflows: meta.workflows, queries: meta.queries, agents: meta.agents });
32513
32632
  console.error("Building...");
32514
32633
  await runNpm(["run", "build"], projectDir);
32515
- const distDir = path4.join(projectDir, "dist");
32516
- if (!fs3.existsSync(distDir)) {
32634
+ const distDir = path5.join(projectDir, "dist");
32635
+ if (!fs4.existsSync(distDir)) {
32517
32636
  throw new Error(
32518
32637
  `Build did not produce a dist/ directory in ${projectDir}. Check that 'npm run build' is configured correctly (Vite/etc. defaults to dist/).`
32519
32638
  );
32520
32639
  }
32521
- const tmpSource = path4.join(tmpdir(), `lotics-source-${Date.now()}.tar.gz`);
32522
- const tmpDist = path4.join(tmpdir(), `lotics-dist-${Date.now()}.tar.gz`);
32640
+ const tmpSource = path5.join(tmpdir(), `lotics-source-${Date.now()}.tar.gz`);
32641
+ const tmpDist = path5.join(tmpdir(), `lotics-dist-${Date.now()}.tar.gz`);
32523
32642
  try {
32524
32643
  console.error("Packaging source...");
32525
32644
  await runTar(
@@ -32541,8 +32660,8 @@ async function appDeploy(client, args) {
32541
32660
  try {
32542
32661
  const result = await client.deployAppVersion({
32543
32662
  app_id: meta.app_id,
32544
- source_archive: fs3.readFileSync(tmpSource),
32545
- dist_archive: fs3.readFileSync(tmpDist),
32663
+ source_archive: fs4.readFileSync(tmpSource),
32664
+ dist_archive: fs4.readFileSync(tmpDist),
32546
32665
  prev_version_id: meta.current_version_id,
32547
32666
  message: args.message,
32548
32667
  // Sync apps.queries from the manifest. Server validates each query
@@ -32576,8 +32695,8 @@ async function appDeploy(client, args) {
32576
32695
  throw err2;
32577
32696
  }
32578
32697
  } finally {
32579
- if (fs3.existsSync(tmpSource)) fs3.unlinkSync(tmpSource);
32580
- if (fs3.existsSync(tmpDist)) fs3.unlinkSync(tmpDist);
32698
+ if (fs4.existsSync(tmpSource)) fs4.unlinkSync(tmpSource);
32699
+ if (fs4.existsSync(tmpDist)) fs4.unlinkSync(tmpDist);
32581
32700
  }
32582
32701
  }
32583
32702
  async function warnIfUnbranded(client, appId) {
@@ -32598,7 +32717,7 @@ async function warnIfUnbranded(client, appId) {
32598
32717
  }
32599
32718
  }
32600
32719
  async function appDev(client, args) {
32601
- const projectDir = path4.resolve(args.projectDir ?? process.cwd());
32720
+ const projectDir = path5.resolve(args.projectDir ?? process.cwd());
32602
32721
  const meta = readAppMeta(projectDir);
32603
32722
  writeAppDts(projectDir, { workflows: meta.workflows, queries: meta.queries, agents: meta.agents });
32604
32723
  const app = await client.getApp(meta.app_id);
@@ -32737,15 +32856,15 @@ async function appWorkflowSet(client, args) {
32737
32856
  process.exit(1);
32738
32857
  }
32739
32858
  const file = workflowFilePath(projectDir, args.alias);
32740
- if (!fs3.existsSync(file)) {
32859
+ if (!fs4.existsSync(file)) {
32741
32860
  console.error(
32742
- `No workflow body at ${path4.relative(projectDir, file)}. Run 'lotics app pull ${meta.app_id}' to write src/workflows/${args.alias}.ts, then edit it.`
32861
+ `No workflow body at ${path5.relative(projectDir, file)}. Run 'lotics app pull ${meta.app_id}' to write src/workflows/${args.alias}.ts, then edit it.`
32743
32862
  );
32744
32863
  process.exit(1);
32745
32864
  }
32746
- const source = stripWorkflowHeader(fs3.readFileSync(file, "utf-8"));
32865
+ const source = stripWorkflowHeader(fs4.readFileSync(file, "utf-8"));
32747
32866
  if (source.trim() === "") {
32748
- console.error(`Workflow body ${path4.relative(projectDir, file)} is empty after stripping the header.`);
32867
+ console.error(`Workflow body ${path5.relative(projectDir, file)} is empty after stripping the header.`);
32749
32868
  process.exit(1);
32750
32869
  }
32751
32870
  const res = await client.setAppWorkflow(meta.app_id, args.alias, {
@@ -32777,22 +32896,97 @@ async function appWorkflowPull(client) {
32777
32896
  console.error(
32778
32897
  `Wrote ${written.length} workflow ${written.length === 1 ? "body" : "bodies"} to ${WORKFLOWS_DIR}/` + (written.length > 0 ? ` (${written.join(", ")})` : "")
32779
32898
  );
32899
+ ensureWorkflowTsconfigExcludes(projectDir);
32900
+ }
32901
+ async function appWorkflowCheck(args) {
32902
+ const projectDir = process.cwd();
32903
+ const meta = readAppMeta(projectDir);
32904
+ const bound = Object.keys(meta.workflows ?? {});
32905
+ let aliases;
32906
+ if (args.alias) {
32907
+ if (!bound.includes(args.alias)) {
32908
+ console.error(
32909
+ `No workflow "${args.alias}" in package.json#lotics.workflows. Bound aliases: ${bound.length > 0 ? bound.join(", ") : "(none)"}.`
32910
+ );
32911
+ process.exit(1);
32912
+ }
32913
+ aliases = [args.alias];
32914
+ } else {
32915
+ aliases = bound;
32916
+ }
32917
+ if (aliases.length === 0) {
32918
+ console.error(`App ${meta.app_id} has no bound workflows to check.`);
32919
+ return;
32920
+ }
32921
+ const toCheck = [];
32922
+ for (const alias of aliases) {
32923
+ const bodyPath = workflowFilePath(projectDir, alias);
32924
+ const globalsPath = workflowGlobalsPath(projectDir, alias);
32925
+ if (!fs4.existsSync(bodyPath)) {
32926
+ console.error(
32927
+ `\u26A0 Skipped "${alias}" \u2014 no body at ${path5.relative(projectDir, bodyPath)}. Run 'lotics app workflow pull' to write it.`
32928
+ );
32929
+ continue;
32930
+ }
32931
+ if (!fs4.existsSync(globalsPath)) {
32932
+ console.error(
32933
+ `Cannot check "${alias}" \u2014 missing types at ${path5.relative(projectDir, globalsPath)}. Run 'lotics app workflow pull' (or 'lotics app codegen') to fetch them.`
32934
+ );
32935
+ process.exit(1);
32936
+ }
32937
+ toCheck.push({ alias, input: { bodyPath, globalsPath } });
32938
+ }
32939
+ if (toCheck.length === 0) {
32940
+ console.error("No workflow bodies to check (every bound alias was skipped).");
32941
+ return;
32942
+ }
32943
+ const tsApi = await loadProjectTypescript(projectDir);
32944
+ const results = checkWorkflowBodies(tsApi, toCheck);
32945
+ printWorkflowCheckResults(projectDir, results);
32946
+ const failed = results.filter((r) => r.issues.length > 0);
32947
+ if (failed.length > 0) process.exit(1);
32948
+ }
32949
+ function printWorkflowCheckResults(projectDir, results) {
32950
+ let totalErrors = 0;
32951
+ for (const r of results) {
32952
+ const rel = path5.relative(projectDir, r.bodyPath);
32953
+ if (r.issues.length === 0) {
32954
+ console.error(`\u2713 ${r.alias} (${rel}) \u2014 no type errors`);
32955
+ continue;
32956
+ }
32957
+ totalErrors += r.issues.length;
32958
+ console.error(
32959
+ `\u2717 ${r.alias} (${rel}) \u2014 ${r.issues.length} error${r.issues.length === 1 ? "" : "s"}:`
32960
+ );
32961
+ for (const issue of r.issues) {
32962
+ const [first2, ...rest] = issue.message.split("\n");
32963
+ console.error(` ${rel}:${issue.line}:${issue.col} - ${issue.code}: ${first2}`);
32964
+ for (const line of rest) console.error(` ${line}`);
32965
+ }
32966
+ }
32967
+ const passed = results.length - results.filter((r) => r.issues.length > 0).length;
32968
+ console.error(
32969
+ totalErrors === 0 ? results.length === 1 ? `
32970
+ The workflow body type-checks clean.` : `
32971
+ All ${results.length} workflow bodies type-check clean.` : `
32972
+ ${totalErrors} error${totalErrors === 1 ? "" : "s"} across ${results.length - passed} of ${results.length} ${results.length === 1 ? "body" : "bodies"}.`
32973
+ );
32780
32974
  }
32781
32975
  function findUiSrcDir(start) {
32782
- let dir = path4.resolve(start);
32976
+ let dir = path5.resolve(start);
32783
32977
  for (; ; ) {
32784
- const candidate = path4.join(dir, "packages", "ui", "src");
32785
- if (fs3.existsSync(candidate) && fs3.statSync(candidate).isDirectory()) return candidate;
32786
- const parent = path4.dirname(dir);
32978
+ const candidate = path5.join(dir, "packages", "ui", "src");
32979
+ if (fs4.existsSync(candidate) && fs4.statSync(candidate).isDirectory()) return candidate;
32980
+ const parent = path5.dirname(dir);
32787
32981
  if (parent === dir) return null;
32788
32982
  dir = parent;
32789
32983
  }
32790
32984
  }
32791
32985
  var UI_ALIAS_FIND_SOURCE = String.raw`/^@lotics\/ui\/(.+)$/`;
32792
32986
  function appUiLink(args) {
32793
- const projectDir = path4.resolve(args.projectDir ?? process.cwd());
32794
- const viteConfigPath = path4.join(projectDir, "vite.config.ts");
32795
- if (!fs3.existsSync(viteConfigPath)) {
32987
+ const projectDir = path5.resolve(args.projectDir ?? process.cwd());
32988
+ const viteConfigPath = path5.join(projectDir, "vite.config.ts");
32989
+ if (!fs4.existsSync(viteConfigPath)) {
32796
32990
  throw new Error(
32797
32991
  `No vite.config.ts at ${projectDir}. Run inside a 'lotics app' project directory.`
32798
32992
  );
@@ -32803,13 +32997,13 @@ function appUiLink(args) {
32803
32997
  "Cannot find packages/ui/src by walking up from this directory \u2014 `lotics ui link` requires a monorepo checkout. External apps consume @lotics/ui from npm; bump the package version and widen the app's dependency range instead."
32804
32998
  );
32805
32999
  }
32806
- const hasComponent = fs3.existsSync(path4.join(uiSrc, `${args.component}.tsx`)) || fs3.existsSync(path4.join(uiSrc, `${args.component}.ts`)) || fs3.existsSync(path4.join(uiSrc, args.component));
33000
+ const hasComponent = fs4.existsSync(path5.join(uiSrc, `${args.component}.tsx`)) || fs4.existsSync(path5.join(uiSrc, `${args.component}.ts`)) || fs4.existsSync(path5.join(uiSrc, args.component));
32807
33001
  if (!hasComponent) {
32808
33002
  throw new Error(
32809
33003
  `No '@lotics/ui/${args.component}' under ${uiSrc} (expected ${args.component}.tsx/.ts). Check the component name.`
32810
33004
  );
32811
33005
  }
32812
- const source = fs3.readFileSync(viteConfigPath, "utf-8");
33006
+ const source = fs4.readFileSync(viteConfigPath, "utf-8");
32813
33007
  const aliasEntry = `{ find: ${UI_ALIAS_FIND_SOURCE}, replacement: ${JSON.stringify(`${uiSrc}/$1`)} },`;
32814
33008
  const alreadyLinked = source.includes(UI_ALIAS_FIND_SOURCE);
32815
33009
  if (args.remove) {
@@ -32818,7 +33012,7 @@ function appUiLink(args) {
32818
33012
  return;
32819
33013
  }
32820
33014
  const stripped = source.replace(new RegExp(`^\\s*\\{ find: ${escapeRegExp(UI_ALIAS_FIND_SOURCE)}.*$\\n?`, "m"), "");
32821
- fs3.writeFileSync(viteConfigPath, stripped);
33015
+ fs4.writeFileSync(viteConfigPath, stripped);
32822
33016
  console.error(`Removed the @lotics/ui dev-link alias from ${viteConfigPath}.`);
32823
33017
  console.error("Restart `lotics app dev` and rm -rf node_modules/.vite to clear cached modules.");
32824
33018
  return;
@@ -32836,7 +33030,7 @@ function appUiLink(args) {
32836
33030
  const insertAt = aliasMatch.index + aliasMatch[0].length;
32837
33031
  const updated = `${source.slice(0, insertAt)}
32838
33032
  ${aliasEntry}${source.slice(insertAt)}`;
32839
- fs3.writeFileSync(viteConfigPath, updated);
33033
+ fs4.writeFileSync(viteConfigPath, updated);
32840
33034
  console.error(`Dev-linked @lotics/ui \u2192 ${uiSrc} in ${viteConfigPath}.`);
32841
33035
  console.error("Restart `lotics app dev` and rm -rf node_modules/.vite to clear cached modules.");
32842
33036
  console.error("Finalize: PR the packages/ui change \u2192 publish \u2192 `lotics ui link <component> --remove` + bump the app's dep.");
@@ -32973,11 +33167,11 @@ async function ingestJsonArgs(opts) {
32973
33167
  }
32974
33168
 
32975
33169
  // src/xlsx.ts
32976
- import fs5 from "node:fs";
33170
+ import fs6 from "node:fs";
32977
33171
 
32978
33172
  // src/file_command_io.ts
32979
- import fs4 from "node:fs";
32980
- import path5 from "node:path";
33173
+ import fs5 from "node:fs";
33174
+ import path6 from "node:path";
32981
33175
  var CliError = class extends Error {
32982
33176
  constructor(message) {
32983
33177
  super(message);
@@ -32988,14 +33182,14 @@ function fail(message) {
32988
33182
  throw new CliError(message);
32989
33183
  }
32990
33184
  function writeFileAtomic(filePath, bytes) {
32991
- const dir = path5.dirname(path5.resolve(filePath));
32992
- const tmp = path5.join(dir, `.${path5.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
32993
- fs4.writeFileSync(tmp, bytes);
33185
+ const dir = path6.dirname(path6.resolve(filePath));
33186
+ const tmp = path6.join(dir, `.${path6.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
33187
+ fs5.writeFileSync(tmp, bytes);
32994
33188
  try {
32995
- fs4.renameSync(tmp, filePath);
33189
+ fs5.renameSync(tmp, filePath);
32996
33190
  } catch (error) {
32997
33191
  try {
32998
- fs4.unlinkSync(tmp);
33192
+ fs5.unlinkSync(tmp);
32999
33193
  } catch {
33000
33194
  }
33001
33195
  throw error;
@@ -35778,16 +35972,16 @@ var MatcherView = class {
35778
35972
  * @returns {string|undefined}
35779
35973
  */
35780
35974
  getCurrentTag() {
35781
- const path7 = this._matcher.path;
35782
- return path7.length > 0 ? path7[path7.length - 1].tag : void 0;
35975
+ const path8 = this._matcher.path;
35976
+ return path8.length > 0 ? path8[path8.length - 1].tag : void 0;
35783
35977
  }
35784
35978
  /**
35785
35979
  * Get current namespace.
35786
35980
  * @returns {string|undefined}
35787
35981
  */
35788
35982
  getCurrentNamespace() {
35789
- const path7 = this._matcher.path;
35790
- return path7.length > 0 ? path7[path7.length - 1].namespace : void 0;
35983
+ const path8 = this._matcher.path;
35984
+ return path8.length > 0 ? path8[path8.length - 1].namespace : void 0;
35791
35985
  }
35792
35986
  /**
35793
35987
  * Get current node's attribute value.
@@ -35795,9 +35989,9 @@ var MatcherView = class {
35795
35989
  * @returns {*}
35796
35990
  */
35797
35991
  getAttrValue(attrName) {
35798
- const path7 = this._matcher.path;
35799
- if (path7.length === 0) return void 0;
35800
- return path7[path7.length - 1].values?.[attrName];
35992
+ const path8 = this._matcher.path;
35993
+ if (path8.length === 0) return void 0;
35994
+ return path8[path8.length - 1].values?.[attrName];
35801
35995
  }
35802
35996
  /**
35803
35997
  * Check if current node has an attribute.
@@ -35805,9 +35999,9 @@ var MatcherView = class {
35805
35999
  * @returns {boolean}
35806
36000
  */
35807
36001
  hasAttr(attrName) {
35808
- const path7 = this._matcher.path;
35809
- if (path7.length === 0) return false;
35810
- const current = path7[path7.length - 1];
36002
+ const path8 = this._matcher.path;
36003
+ if (path8.length === 0) return false;
36004
+ const current = path8[path8.length - 1];
35811
36005
  return current.values !== void 0 && attrName in current.values;
35812
36006
  }
35813
36007
  /**
@@ -35815,18 +36009,18 @@ var MatcherView = class {
35815
36009
  * @returns {number}
35816
36010
  */
35817
36011
  getPosition() {
35818
- const path7 = this._matcher.path;
35819
- if (path7.length === 0) return -1;
35820
- return path7[path7.length - 1].position ?? 0;
36012
+ const path8 = this._matcher.path;
36013
+ if (path8.length === 0) return -1;
36014
+ return path8[path8.length - 1].position ?? 0;
35821
36015
  }
35822
36016
  /**
35823
36017
  * Get current node's repeat counter (occurrence count of this tag name).
35824
36018
  * @returns {number}
35825
36019
  */
35826
36020
  getCounter() {
35827
- const path7 = this._matcher.path;
35828
- if (path7.length === 0) return -1;
35829
- return path7[path7.length - 1].counter ?? 0;
36021
+ const path8 = this._matcher.path;
36022
+ if (path8.length === 0) return -1;
36023
+ return path8[path8.length - 1].counter ?? 0;
35830
36024
  }
35831
36025
  /**
35832
36026
  * Get current node's sibling index (alias for getPosition).
@@ -37681,8 +37875,8 @@ function isAttribute(name) {
37681
37875
  }
37682
37876
 
37683
37877
  // ../../node_modules/fflate/esm/index.mjs
37684
- import { createRequire } from "module";
37685
- var require2 = createRequire("/");
37878
+ import { createRequire as createRequire2 } from "module";
37879
+ var require2 = createRequire2("/");
37686
37880
  var Worker;
37687
37881
  try {
37688
37882
  Worker = require2("worker_threads").Worker;
@@ -41282,10 +41476,10 @@ function resolveRelativePath(basePath, relativePath) {
41282
41476
  }
41283
41477
  return resolved.join("/");
41284
41478
  }
41285
- function findZipEntry(zipEntries, path7) {
41286
- if (zipEntries[path7]) return zipEntries[path7];
41287
- if (zipEntries[`xl/${path7}`]) return zipEntries[`xl/${path7}`];
41288
- const noSlash = path7.replace(/^\//, "");
41479
+ function findZipEntry(zipEntries, path8) {
41480
+ if (zipEntries[path8]) return zipEntries[path8];
41481
+ if (zipEntries[`xl/${path8}`]) return zipEntries[`xl/${path8}`];
41482
+ const noSlash = path8.replace(/^\//, "");
41289
41483
  if (zipEntries[noSlash]) return zipEntries[noSlash];
41290
41484
  return void 0;
41291
41485
  }
@@ -42583,9 +42777,9 @@ function parseExcelFromZip(zip, options) {
42583
42777
  }
42584
42778
  return result;
42585
42779
  }
42586
- function findEntry(zip, path7) {
42587
- if (zip[path7]) return zip[path7];
42588
- const lower = path7.toLowerCase();
42780
+ function findEntry(zip, path8) {
42781
+ if (zip[path8]) return zip[path8];
42782
+ const lower = path8.toLowerCase();
42589
42783
  for (const key of Object.keys(zip)) {
42590
42784
  if (key.toLowerCase() === lower) return zip[key];
42591
42785
  }
@@ -43196,12 +43390,12 @@ function extractPivotRoundTripInfo(originalZip) {
43196
43390
  if (paths.length > 0) pivotTablesBySheetIndex.set(i2, paths);
43197
43391
  }
43198
43392
  const pivotXmlPaths = [];
43199
- for (const path7 of Object.keys(originalZip)) {
43200
- if (path7.startsWith("xl/pivotTables/") && path7.endsWith(".xml")) {
43201
- pivotXmlPaths.push(path7);
43393
+ for (const path8 of Object.keys(originalZip)) {
43394
+ if (path8.startsWith("xl/pivotTables/") && path8.endsWith(".xml")) {
43395
+ pivotXmlPaths.push(path8);
43202
43396
  }
43203
- if (path7.startsWith("xl/pivotCache/") && path7.endsWith(".xml")) {
43204
- pivotXmlPaths.push(path7);
43397
+ if (path8.startsWith("xl/pivotCache/") && path8.endsWith(".xml")) {
43398
+ pivotXmlPaths.push(path8);
43205
43399
  }
43206
43400
  }
43207
43401
  return { cachesByWorkbook, pivotTablesBySheetIndex, pivotXmlPaths };
@@ -43209,15 +43403,15 @@ function extractPivotRoundTripInfo(originalZip) {
43209
43403
  function decode(bytes) {
43210
43404
  return new TextDecoder().decode(bytes);
43211
43405
  }
43212
- function pivotContentTypeFor(path7) {
43213
- if (path7.startsWith("xl/pivotTables/") && path7.endsWith(".xml")) {
43214
- return `<Override PartName="/${path7}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"/>`;
43406
+ function pivotContentTypeFor(path8) {
43407
+ if (path8.startsWith("xl/pivotTables/") && path8.endsWith(".xml")) {
43408
+ return `<Override PartName="/${path8}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"/>`;
43215
43409
  }
43216
- if (path7.includes("/pivotCacheDefinition") && path7.endsWith(".xml")) {
43217
- return `<Override PartName="/${path7}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"/>`;
43410
+ if (path8.includes("/pivotCacheDefinition") && path8.endsWith(".xml")) {
43411
+ return `<Override PartName="/${path8}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"/>`;
43218
43412
  }
43219
- if (path7.includes("/pivotCacheRecords") && path7.endsWith(".xml")) {
43220
- return `<Override PartName="/${path7}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml"/>`;
43413
+ if (path8.includes("/pivotCacheRecords") && path8.endsWith(".xml")) {
43414
+ return `<Override PartName="/${path8}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml"/>`;
43221
43415
  }
43222
43416
  return void 0;
43223
43417
  }
@@ -43289,13 +43483,13 @@ function exportWorkbook(workbook, originalZip) {
43289
43483
  regeneratedPaths.add(`xl/worksheets/sheet${i2 + 1}.xml`);
43290
43484
  regeneratedPaths.add(`xl/worksheets/_rels/sheet${i2 + 1}.xml.rels`);
43291
43485
  }
43292
- for (const path7 of Object.keys(originalZip)) {
43293
- if (path7.startsWith("xl/drawings/") || path7.startsWith("xl/charts/") || path7.startsWith("xl/tables/") || path7.startsWith("xl/media/")) {
43294
- regeneratedPaths.add(path7);
43486
+ for (const path8 of Object.keys(originalZip)) {
43487
+ if (path8.startsWith("xl/drawings/") || path8.startsWith("xl/charts/") || path8.startsWith("xl/tables/") || path8.startsWith("xl/media/")) {
43488
+ regeneratedPaths.add(path8);
43295
43489
  }
43296
43490
  }
43297
- for (const [path7, data] of Object.entries(originalZip)) {
43298
- if (!regeneratedPaths.has(path7)) entries[path7] = data;
43491
+ for (const [path8, data] of Object.entries(originalZip)) {
43492
+ if (!regeneratedPaths.has(path8)) entries[path8] = data;
43299
43493
  }
43300
43494
  }
43301
43495
  const sharedStrings = buildSharedStrings(workbook);
@@ -43310,8 +43504,8 @@ function exportWorkbook(workbook, originalZip) {
43310
43504
  buildWorkbookRels(workbook, pivotInfo)
43311
43505
  );
43312
43506
  const extraContentTypes = [];
43313
- for (const path7 of pivotInfo.pivotXmlPaths) {
43314
- const ct = pivotContentTypeFor(path7);
43507
+ for (const path8 of pivotInfo.pivotXmlPaths) {
43508
+ const ct = pivotContentTypeFor(path8);
43315
43509
  if (ct) extraContentTypes.push(ct);
43316
43510
  }
43317
43511
  let globalChartIndex = 1;
@@ -45072,14 +45266,14 @@ var DependencyGraph = class {
45072
45266
  stack.push({ cell: p, path: [cell, p] });
45073
45267
  }
45074
45268
  while (stack.length > 0) {
45075
- const { cell: current, path: path7 } = stack.pop();
45076
- if (current === cell) return path7;
45269
+ const { cell: current, path: path8 } = stack.pop();
45270
+ if (current === cell) return path8;
45077
45271
  if (visited.has(current)) continue;
45078
45272
  visited.add(current);
45079
45273
  const nextPrec = this.precedents.get(current);
45080
45274
  if (!nextPrec) continue;
45081
45275
  for (const p of nextPrec) {
45082
- stack.push({ cell: p, path: [...path7, p] });
45276
+ stack.push({ cell: p, path: [...path8, p] });
45083
45277
  }
45084
45278
  }
45085
45279
  return null;
@@ -46070,8 +46264,8 @@ function adjustMergedCellsForColDelete(sheet, atCol, count) {
46070
46264
 
46071
46265
  // src/xlsx.ts
46072
46266
  function loadFile(filePath) {
46073
- if (!fs5.existsSync(filePath)) fail(`File not found: ${filePath}`);
46074
- const buffer = fs5.readFileSync(filePath);
46267
+ if (!fs6.existsSync(filePath)) fail(`File not found: ${filePath}`);
46268
+ const buffer = fs6.readFileSync(filePath);
46075
46269
  const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
46076
46270
  const originalZip = unzipXlsx(arrayBuffer);
46077
46271
  const parsed = parseExcelBuffer(arrayBuffer);
@@ -46121,7 +46315,7 @@ function lazyEngine(workbook) {
46121
46315
  };
46122
46316
  }
46123
46317
  function readToJson(filePath) {
46124
- const buffer = fs5.readFileSync(filePath);
46318
+ const buffer = fs6.readFileSync(filePath);
46125
46319
  const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
46126
46320
  const parsed = parseExcelBuffer(arrayBuffer);
46127
46321
  return {
@@ -46511,7 +46705,7 @@ async function runXlsxCommand(subcommand, toolArgs, restArgs) {
46511
46705
  }
46512
46706
 
46513
46707
  // src/docx.ts
46514
- import fs6 from "node:fs";
46708
+ import fs7 from "node:fs";
46515
46709
 
46516
46710
  // ../docx/src/parse/parser.ts
46517
46711
  var import_jszip = __toESM(require_lib3(), 1);
@@ -48282,11 +48476,11 @@ async function parseDocx(buffer) {
48282
48476
  const zip = await import_jszip.default.loadAsync(buffer);
48283
48477
  const parts = /* @__PURE__ */ new Map();
48284
48478
  const filePromises = [];
48285
- zip.forEach((path7, file) => {
48479
+ zip.forEach((path8, file) => {
48286
48480
  if (file.dir) return;
48287
48481
  filePromises.push(
48288
48482
  file.async("uint8array").then((bytes) => {
48289
- parts.set(path7, bytes);
48483
+ parts.set(path8, bytes);
48290
48484
  })
48291
48485
  );
48292
48486
  });
@@ -48515,9 +48709,9 @@ var DEFAULT_PARTS = [
48515
48709
  ["word/theme/theme1.xml", () => encoder.encode(THEME_XML)]
48516
48710
  ];
48517
48711
  function fillDefaultParts(parts) {
48518
- for (const [path7, makeBytes] of DEFAULT_PARTS) {
48519
- if (!parts.has(path7)) {
48520
- parts.set(path7, makeBytes());
48712
+ for (const [path8, makeBytes] of DEFAULT_PARTS) {
48713
+ if (!parts.has(path8)) {
48714
+ parts.set(path8, makeBytes());
48521
48715
  }
48522
48716
  }
48523
48717
  return parts;
@@ -48527,9 +48721,9 @@ function fillDefaultParts(parts) {
48527
48721
  async function serializeDocx(doc) {
48528
48722
  const zip = new import_jszip2.default();
48529
48723
  const parts = fillDefaultParts(new Map(doc.parts));
48530
- for (const [path7, bytes] of parts) {
48531
- if (path7 === "word/document.xml") continue;
48532
- zip.file(path7, bytes);
48724
+ for (const [path8, bytes] of parts) {
48725
+ if (path8 === "word/document.xml") continue;
48726
+ zip.file(path8, bytes);
48533
48727
  }
48534
48728
  const documentXml = serializeDocumentXml(doc);
48535
48729
  zip.file("word/document.xml", documentXml);
@@ -48615,8 +48809,8 @@ var DEFAULT_DOC_ATTRS = {
48615
48809
  "@_xmlns:w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
48616
48810
  };
48617
48811
  async function loadFile2(filePath) {
48618
- if (!fs6.existsSync(filePath)) fail(`File not found: ${filePath}`);
48619
- const bytes = fs6.readFileSync(filePath);
48812
+ if (!fs7.existsSync(filePath)) fail(`File not found: ${filePath}`);
48813
+ const bytes = fs7.readFileSync(filePath);
48620
48814
  return parseDocx(new Uint8Array(bytes));
48621
48815
  }
48622
48816
  async function writeDoc(filePath, doc) {
@@ -48973,6 +49167,8 @@ COMMANDS
48973
49167
  lotics app workflow set <alias> Push the edited src/workflows/<alias>.ts body
48974
49168
  through set_app_workflow (server verifies)
48975
49169
  lotics app workflow pull Rewrite src/workflows/*.ts from the server
49170
+ lotics app workflow check [alias] Typecheck src/workflows bodies locally (one
49171
+ isolated program per alias; the app's own tsc)
48976
49172
  lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address
48977
49173
  lotics app rename "<new name>" Rename the app's display name (launcher title)
48978
49174
  lotics app dev [path] Run the app locally with HMR (RPC forwarded to prod)
@@ -49080,8 +49276,8 @@ function prompt(question) {
49080
49276
  });
49081
49277
  });
49082
49278
  }
49083
- async function publicPost(path7, body) {
49084
- const response = await fetch(`${API_BASE_URL}${path7}`, {
49279
+ async function publicPost(path8, body) {
49280
+ const response = await fetch(`${API_BASE_URL}${path8}`, {
49085
49281
  method: "POST",
49086
49282
  headers: { "Content-Type": "application/json" },
49087
49283
  body: JSON.stringify(body)
@@ -49227,13 +49423,13 @@ async function resolveWorkspace(client, ctx) {
49227
49423
  function resolveUploadPaths(rawPaths) {
49228
49424
  const result = [];
49229
49425
  for (const p of rawPaths) {
49230
- const resolved = path6.resolve(p);
49231
- const stat = fs7.statSync(resolved);
49426
+ const resolved = path7.resolve(p);
49427
+ const stat = fs8.statSync(resolved);
49232
49428
  if (stat.isDirectory()) {
49233
- const entries = fs7.readdirSync(resolved, { withFileTypes: true });
49429
+ const entries = fs8.readdirSync(resolved, { withFileTypes: true });
49234
49430
  for (const entry of entries) {
49235
49431
  if (entry.isFile()) {
49236
- result.push(path6.join(resolved, entry.name));
49432
+ result.push(path7.join(resolved, entry.name));
49237
49433
  }
49238
49434
  }
49239
49435
  } else {
@@ -49352,6 +49548,10 @@ async function main() {
49352
49548
  console.error("Usage: lotics ui link <component> [--remove]");
49353
49549
  process.exit(1);
49354
49550
  }
49551
+ if (command === "app" && subcommand === "workflow" && toolArgs === "check") {
49552
+ await appWorkflowCheck({ alias: restArgs[0] });
49553
+ return;
49554
+ }
49355
49555
  if (command === "app" && subcommand === "codegen") {
49356
49556
  const projectDir = toolArgs;
49357
49557
  const ctx2 = resolveContext(flags);
@@ -49436,6 +49636,7 @@ async function main() {
49436
49636
  console.error(" lotics app workflow run <alias> '<json>' Execute a bound app workflow end-to-end");
49437
49637
  console.error(" lotics app workflow set <alias> Push the edited src/workflows/<alias>.ts body");
49438
49638
  console.error(" lotics app workflow pull Rewrite src/workflows/*.ts from the server");
49639
+ console.error(" lotics app workflow check [alias] Typecheck src/workflows bodies locally");
49439
49640
  console.error(" lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address");
49440
49641
  console.error(` lotics app rename "<new name>" Rename the app's display name (launcher title)`);
49441
49642
  console.error(" lotics app dev [path] Run the app locally with HMR + RPC forwarding");
@@ -49584,6 +49785,7 @@ Available workspaces:`);
49584
49785
  console.error(" lotics app workflow run <alias> '<json>' Execute a bound app workflow");
49585
49786
  console.error(" lotics app workflow set <alias> Push src/workflows/<alias>.ts");
49586
49787
  console.error(" lotics app workflow pull Rewrite src/workflows/*.ts from the server");
49788
+ console.error(" lotics app workflow check [alias] Typecheck src/workflows bodies locally");
49587
49789
  process.exit(1);
49588
49790
  };
49589
49791
  if (action === "run") {
@@ -49599,7 +49801,7 @@ Available workspaces:`);
49599
49801
  const ingested = await ingestJsonArgs({
49600
49802
  rawArg: restArgs[1],
49601
49803
  stdinIsTTY: process.stdin.isTTY ?? false,
49602
- readFile: (p) => fs7.readFileSync(p, "utf-8"),
49804
+ readFile: (p) => fs8.readFileSync(p, "utf-8"),
49603
49805
  readStdin
49604
49806
  });
49605
49807
  if (ingested.kind === "error") {
@@ -49731,7 +49933,7 @@ ${JSON.stringify(info.input_schema, null, 2)}`);
49731
49933
  const ingested = await ingestJsonArgs({
49732
49934
  rawArg: toolArgs,
49733
49935
  stdinIsTTY: process.stdin.isTTY ?? false,
49734
- readFile: (p) => fs7.readFileSync(p, "utf-8"),
49936
+ readFile: (p) => fs8.readFileSync(p, "utf-8"),
49735
49937
  readStdin
49736
49938
  });
49737
49939
  if (ingested.kind === "error") {