@lotics/cli 0.64.0 → 0.65.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.
@@ -87,16 +87,23 @@ export declare function writeWorkflowFile(projectDir: string, alias: string, sou
87
87
  */
88
88
  export declare function stripWorkflowHeader(content: string): string;
89
89
  /**
90
- * Add the workflow-body globs to the main `tsconfig.json`'s `exclude` if missing,
91
- * preserving every other exclude. The starter ships these excludes already, but
92
- * an app SCAFFOLDED before this CLI release (or one with a hand-written tsconfig)
93
- * doesn't and pulling bodies into it would otherwise break its
94
- * `npm run typecheck`: the bodies pull the app's DOM lib (the server doesn't) and
95
- * the per-alias ambient globals collide on `trigger`. Idempotent a second pull
96
- * is a no-op. Warns exactly what it added. A missing/unparseable tsconfig is a
97
- * non-fatal warn (the pull itself still succeeds); the author fixes the config.
90
+ * Heal a pre-existing app's `tsconfig.json` so its generated types load and its
91
+ * `npm run typecheck` stays honest. Idempotent, run on every pull / codegen /
92
+ * deploy. Two things the current starter ships but an app SCAFFOLDED before those
93
+ * releases (or with a hand-written tsconfig) can lack:
94
+ *
95
+ * 1. `include`: a bare `.lotics` is rewritten to the recursive `LOTICS_INCLUDE_GLOB`.
96
+ * A bare dot-dir is skipped by TypeScript's include-glob walk, so the codegen'd
97
+ * `.d.ts` never enter the program every `useQuery`/`useWorkflow`/`useAgentRun`
98
+ * param and result silently falls back to `unknown`.
99
+ * 2. `exclude`: the workflow-body globs — the bodies use the app's DOM lib (the
100
+ * server doesn't) and the per-alias ambient globals collide on `trigger`,
101
+ * poisoning typecheck. Checked separately by `lotics app workflow check`.
102
+ *
103
+ * A missing / unparseable tsconfig is a non-fatal warn (the caller still succeeds);
104
+ * the author fixes the config.
98
105
  */
99
- export declare function ensureWorkflowTsconfigExcludes(projectDir: string): void;
106
+ export declare function ensureAppTsconfig(projectDir: string): void;
100
107
  /**
101
108
  * `lotics app codegen [path]` — regenerate every `.lotics/` artifact from the
102
109
  * manifest + workspace schema, WITHOUT a deploy. The `.d.ts` companions are
@@ -64,6 +64,17 @@ const WORKFLOW_GLOBALS_DIR = path.join(".lotics", "workflows");
64
64
  * with `/` separators — tsconfig globs are POSIX even on Windows.
65
65
  */
66
66
  const WORKFLOW_TSCONFIG_EXCLUDES = ["src/workflows", ".lotics/workflows"];
67
+ /**
68
+ * The include glob that actually loads the generated `.lotics/*.d.ts` companions.
69
+ * A BARE `.lotics` entry loads NONE of them: TypeScript's include-glob walk skips
70
+ * dot-directories, so `useQuery` / `useWorkflow` / `useAgentRun` fall back to their
71
+ * untyped string overloads and every param / input / result silently becomes
72
+ * `unknown`. The current starter emits the glob (`starter_template.ts`); apps
73
+ * scaffolded before that fix shipped the bare form and need healing.
74
+ */
75
+ const LOTICS_INCLUDE_GLOB = ".lotics/**/*";
76
+ /** The bare `.lotics` forms an older starter emitted — all skipped by the glob walk. */
77
+ const STALE_LOTICS_INCLUDES = new Set([".lotics", "./.lotics", ".lotics/"]);
67
78
  /**
68
79
  * The `async function __workflow(...)` wrapper a workflow body sits inside —
69
80
  * the SAME envelope the server compiles the body within at `set_app_workflow`
@@ -304,20 +315,27 @@ function writeAppMeta(projectDir, meta) {
304
315
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
305
316
  }
306
317
  /**
307
- * Add the workflow-body globs to the main `tsconfig.json`'s `exclude` if missing,
308
- * preserving every other exclude. The starter ships these excludes already, but
309
- * an app SCAFFOLDED before this CLI release (or one with a hand-written tsconfig)
310
- * doesn't and pulling bodies into it would otherwise break its
311
- * `npm run typecheck`: the bodies pull the app's DOM lib (the server doesn't) and
312
- * the per-alias ambient globals collide on `trigger`. Idempotent a second pull
313
- * is a no-op. Warns exactly what it added. A missing/unparseable tsconfig is a
314
- * non-fatal warn (the pull itself still succeeds); the author fixes the config.
318
+ * Heal a pre-existing app's `tsconfig.json` so its generated types load and its
319
+ * `npm run typecheck` stays honest. Idempotent, run on every pull / codegen /
320
+ * deploy. Two things the current starter ships but an app SCAFFOLDED before those
321
+ * releases (or with a hand-written tsconfig) can lack:
322
+ *
323
+ * 1. `include`: a bare `.lotics` is rewritten to the recursive `LOTICS_INCLUDE_GLOB`.
324
+ * A bare dot-dir is skipped by TypeScript's include-glob walk, so the codegen'd
325
+ * `.d.ts` never enter the program every `useQuery`/`useWorkflow`/`useAgentRun`
326
+ * param and result silently falls back to `unknown`.
327
+ * 2. `exclude`: the workflow-body globs — the bodies use the app's DOM lib (the
328
+ * server doesn't) and the per-alias ambient globals collide on `trigger`,
329
+ * poisoning typecheck. Checked separately by `lotics app workflow check`.
330
+ *
331
+ * A missing / unparseable tsconfig is a non-fatal warn (the caller still succeeds);
332
+ * the author fixes the config.
315
333
  */
316
- export function ensureWorkflowTsconfigExcludes(projectDir) {
334
+ export function ensureAppTsconfig(projectDir) {
317
335
  const tsconfigPath = path.join(projectDir, "tsconfig.json");
318
336
  if (!fs.existsSync(tsconfigPath)) {
319
- console.error(`⚠ No tsconfig.json at ${projectDir} — could not ensure ${WORKFLOW_TSCONFIG_EXCLUDES.join(", ")} are excluded. ` +
320
- `Add them to your tsconfig's "exclude" so npm run typecheck skips the workflow bodies.`);
337
+ console.error(`⚠ No tsconfig.json at ${projectDir} — could not ensure "include" has "${LOTICS_INCLUDE_GLOB}" ` +
338
+ `(a bare .lotics loads none of the generated types) or "exclude" has ${WORKFLOW_TSCONFIG_EXCLUDES.join(", ")}.`);
321
339
  return;
322
340
  }
323
341
  let parsed;
@@ -326,30 +344,47 @@ export function ensureWorkflowTsconfigExcludes(projectDir) {
326
344
  }
327
345
  catch (err) {
328
346
  console.error(`⚠ Could not parse tsconfig.json (${err instanceof Error ? err.message : String(err)}) — ` +
329
- `add ${WORKFLOW_TSCONFIG_EXCLUDES.join(", ")} to its "exclude" manually so npm run typecheck skips the workflow bodies.`);
347
+ `ensure "include" has "${LOTICS_INCLUDE_GLOB}" and "exclude" has ${WORKFLOW_TSCONFIG_EXCLUDES.join(", ")} manually.`);
330
348
  return;
331
349
  }
332
350
  if (!parsed || typeof parsed !== "object")
333
351
  return;
334
- // `exclude` is a TOP-LEVEL tsconfig field — tsc ignores `compilerOptions.exclude`,
335
- // so the workflow globs must land at the top level or `npm run typecheck` still
336
- // loads the bodies + their colliding per-alias globals.
337
- const current = Array.isArray(parsed.exclude) ? parsed.exclude : [];
338
- const currentStrings = current.filter((e) => typeof e === "string");
339
- const toAdd = WORKFLOW_TSCONFIG_EXCLUDES.filter((g) => !currentStrings.includes(g));
340
- if (toAdd.length === 0)
352
+ const changes = [];
353
+ // 1. Heal a stale bare `.lotics` include so the generated `.d.ts` actually load
354
+ // (a bare dot-dir is invisible to TypeScript's include-glob walk).
355
+ if (Array.isArray(parsed.include)) {
356
+ const inc = parsed.include.filter((e) => typeof e === "string");
357
+ if (inc.some((e) => STALE_LOTICS_INCLUDES.has(e))) {
358
+ const healed = inc.map((e) => (STALE_LOTICS_INCLUDES.has(e) ? LOTICS_INCLUDE_GLOB : e));
359
+ parsed.include = healed.filter((e, i) => healed.indexOf(e) === i); // dedupe if the glob was already present
360
+ changes.push(`rewrote a bare ".lotics" to "${LOTICS_INCLUDE_GLOB}" in "include" (a dot-dir loads zero generated types)`);
361
+ }
362
+ }
363
+ // 2. Ensure the workflow-body excludes. `exclude` is a TOP-LEVEL tsconfig field —
364
+ // tsc ignores `compilerOptions.exclude`, so they must land at the top level.
365
+ const currentEx = Array.isArray(parsed.exclude)
366
+ ? parsed.exclude.filter((e) => typeof e === "string")
367
+ : [];
368
+ const toAdd = WORKFLOW_TSCONFIG_EXCLUDES.filter((g) => !currentEx.includes(g));
369
+ if (toAdd.length > 0) {
370
+ parsed.exclude = [...currentEx, ...toAdd];
371
+ changes.push(`added ${toAdd.join(", ")} to "exclude"`);
372
+ }
373
+ if (changes.length === 0)
341
374
  return;
342
- parsed.exclude = [...currentStrings, ...toAdd];
343
375
  fs.writeFileSync(tsconfigPath, JSON.stringify(parsed, null, 2) + "\n");
344
- console.error(`Patched tsconfig.json: added ${toAdd.join(", ")} to "exclude" so npm run typecheck skips the workflow bodies ` +
345
- `(check them with: lotics app workflow check).`);
376
+ console.error(`Patched tsconfig.json: ${changes.join("; ")}.`);
346
377
  }
347
378
  /**
348
- * Write `.lotics/app_workflows.d.ts` + `.lotics/app_queries.d.ts` from the
349
- * manifest's `workflows` / `queries` maps. Called from `app create / pull /
350
- * dev / deploy` so the augmented `AppWorkflows` / `AppQueries` types stay in
351
- * sync with the manifest. The starter tsconfig.json includes `.lotics/` so
352
- * tsc + Vite pick the files up automatically.
379
+ * Write the three `.lotics/app_{workflows,queries,agents}.d.ts` companions from
380
+ * the manifest's maps, then heal the app's tsconfig so they actually load. Called
381
+ * from `app create / pull / dev / deploy / codegen`, so the augmented `AppWorkflows`
382
+ * / `AppQueries` / `AppAgents` types stay in sync with the manifest.
383
+ *
384
+ * The heal is at the write boundary on purpose: a `.d.ts` written but not loaded is
385
+ * useless (a bare `.lotics` include is skipped by TypeScript's include-glob walk and
386
+ * loads zero of them), so `ensureAppTsconfig` couples "wrote the types" with "the
387
+ * program can see them" — no caller can do one without the other.
353
388
  */
354
389
  function writeAppDts(projectDir, manifest) {
355
390
  const dotLotics = path.join(projectDir, ".lotics");
@@ -361,6 +396,7 @@ function writeAppDts(projectDir, manifest) {
361
396
  ];
362
397
  for (const [file, content] of written)
363
398
  fs.writeFileSync(file, content);
399
+ ensureAppTsconfig(projectDir);
364
400
  return written.map(([file]) => file);
365
401
  }
366
402
  /**
@@ -450,10 +486,6 @@ export async function appCodegen(args) {
450
486
  // it, so the local typecheck tracks the current workspace schema. Aliases
451
487
  // never pulled (no body file yet) are skipped — codegen isn't a pull.
452
488
  await refreshWorkflowGlobals(args.client, projectDir, meta.app_id, Object.keys(meta.workflows ?? {}));
453
- // Keep the main tsconfig excluding the workflow-body globs (idempotent) so
454
- // npm run typecheck never loads the bodies or their colliding per-alias globals.
455
- if (Object.keys(meta.workflows ?? {}).length > 0)
456
- ensureWorkflowTsconfigExcludes(projectDir);
457
489
  }
458
490
  /**
459
491
  * For each bound alias that already has a local body file, fetch its current
@@ -745,9 +777,6 @@ export async function appPull(client, args) {
745
777
  if (written.length > 0) {
746
778
  console.error(`Wrote ${written.length} workflow ${written.length === 1 ? "body" : "bodies"} to ${WORKFLOWS_DIR}/ (${written.join(", ")})`);
747
779
  }
748
- // A pre-existing app's tsconfig may predate the workflow-body excludes; pulling
749
- // bodies into it would break its npm run typecheck. Patch it idempotently.
750
- ensureWorkflowTsconfigExcludes(targetPath);
751
780
  }
752
781
  console.error(`Installing npm dependencies...`);
753
782
  await runNpm(["install"], targetPath);
@@ -1187,9 +1216,10 @@ export async function appWorkflowPull(client) {
1187
1216
  const written = await writeWorkflowFiles(client, projectDir, meta.app_id, aliases);
1188
1217
  console.error(`Wrote ${written.length} workflow ${written.length === 1 ? "body" : "bodies"} to ${WORKFLOWS_DIR}/` +
1189
1218
  (written.length > 0 ? ` (${written.join(", ")})` : ""));
1190
- // A pre-existing app's tsconfig may predate the workflow-body excludes; pulling
1191
- // bodies into it would break its npm run typecheck. Patch it idempotently.
1192
- ensureWorkflowTsconfigExcludes(projectDir);
1219
+ // This command writes workflow BODIES, not the `.d.ts` so it doesn't reach
1220
+ // `writeAppDts`'s heal. Heal here so the bodies land excluded and a stale
1221
+ // `.lotics` include doesn't leave the app's other generated types dead.
1222
+ ensureAppTsconfig(projectDir);
1193
1223
  }
1194
1224
  /**
1195
1225
  * `lotics app workflow check [alias]` — local TypeScript type check of the
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { tmpdir } from "node:os";
5
- import { stampPulledManifest, undeclaredCapabilities, appDirName, defaultPullTarget, ensureWorkflowTsconfigExcludes, appCodegen, appUiLink, appVersions, appWorkflowSet, appWorkflowPull, appExecuteWorkflow, writeWorkflowFile, writeWorkflowGlobals, stripWorkflowHeader, FALLBACK_ENVELOPE_PREFIX, FALLBACK_ENVELOPE_SUFFIX, } from "./app_commands.js";
5
+ import { stampPulledManifest, undeclaredCapabilities, appDirName, defaultPullTarget, ensureAppTsconfig, appCodegen, appUiLink, appVersions, appWorkflowSet, appWorkflowPull, appExecuteWorkflow, writeWorkflowFile, writeWorkflowGlobals, stripWorkflowHeader, FALLBACK_ENVELOPE_PREFIX, FALLBACK_ENVELOPE_SUFFIX, } from "./app_commands.js";
6
6
  /**
7
7
  * `appPull` reads workflows from the live App row (server response), NOT from
8
8
  * the manifest embedded in the extracted source archive. The frozen archive
@@ -680,7 +680,7 @@ describe("defaultPullTarget", () => {
680
680
  expect(defaultPullTarget("app_X", "Sales Tracker")).toBe("Sales Tracker");
681
681
  });
682
682
  });
683
- describe("ensureWorkflowTsconfigExcludes", () => {
683
+ describe("ensureAppTsconfig", () => {
684
684
  let dir;
685
685
  beforeEach(() => {
686
686
  dir = fs.mkdtempSync(path.join(tmpdir(), "lotics-tsconfig-"));
@@ -692,37 +692,60 @@ describe("ensureWorkflowTsconfigExcludes", () => {
692
692
  const read = () => JSON.parse(fs.readFileSync(tsconfigPath(), "utf-8"));
693
693
  it("adds both workflow globs to the TOP-LEVEL exclude", () => {
694
694
  fs.writeFileSync(tsconfigPath(), JSON.stringify({ compilerOptions: {}, exclude: ["node_modules"] }));
695
- ensureWorkflowTsconfigExcludes(dir);
695
+ ensureAppTsconfig(dir);
696
696
  expect(read().exclude).toEqual(["node_modules", "src/workflows", ".lotics/workflows"]);
697
697
  });
698
698
  it("writes TOP-LEVEL even when a compilerOptions.exclude exists (tsc ignores the nested key)", () => {
699
699
  fs.writeFileSync(tsconfigPath(), JSON.stringify({ compilerOptions: { exclude: ["x"] } }));
700
- ensureWorkflowTsconfigExcludes(dir);
700
+ ensureAppTsconfig(dir);
701
701
  const cfg = read();
702
702
  expect(cfg.exclude).toEqual(["src/workflows", ".lotics/workflows"]);
703
703
  expect(cfg.compilerOptions.exclude).toEqual(["x"]); // left untouched (and tsc-ignored)
704
704
  });
705
705
  it("preserves pre-existing top-level excludes", () => {
706
706
  fs.writeFileSync(tsconfigPath(), JSON.stringify({ exclude: ["node_modules", "dist"] }));
707
- ensureWorkflowTsconfigExcludes(dir);
707
+ ensureAppTsconfig(dir);
708
708
  expect(read().exclude).toEqual(["node_modules", "dist", "src/workflows", ".lotics/workflows"]);
709
709
  });
710
710
  it("is idempotent — a second call writes nothing new", () => {
711
711
  fs.writeFileSync(tsconfigPath(), JSON.stringify({ exclude: ["node_modules"] }));
712
- ensureWorkflowTsconfigExcludes(dir);
712
+ ensureAppTsconfig(dir);
713
713
  const afterFirst = fs.readFileSync(tsconfigPath(), "utf-8");
714
- ensureWorkflowTsconfigExcludes(dir);
714
+ ensureAppTsconfig(dir);
715
715
  expect(fs.readFileSync(tsconfigPath(), "utf-8")).toBe(afterFirst);
716
716
  });
717
717
  it("warns + does not throw or create a file when there is no tsconfig", () => {
718
- expect(() => ensureWorkflowTsconfigExcludes(dir)).not.toThrow();
718
+ expect(() => ensureAppTsconfig(dir)).not.toThrow();
719
719
  expect(fs.existsSync(tsconfigPath())).toBe(false);
720
720
  });
721
721
  it("warns + does not throw on an unparseable tsconfig (left as-is)", () => {
722
722
  fs.writeFileSync(tsconfigPath(), "{ not json,, }");
723
- expect(() => ensureWorkflowTsconfigExcludes(dir)).not.toThrow();
723
+ expect(() => ensureAppTsconfig(dir)).not.toThrow();
724
724
  expect(fs.readFileSync(tsconfigPath(), "utf-8")).toBe("{ not json,, }");
725
725
  });
726
+ it("rewrites a bare .lotics include to the glob (a dot-dir loads zero generated types)", () => {
727
+ fs.writeFileSync(tsconfigPath(), JSON.stringify({ include: ["src", ".lotics"], exclude: ["node_modules"] }));
728
+ ensureAppTsconfig(dir);
729
+ expect(read().include).toEqual(["src", ".lotics/**/*"]);
730
+ });
731
+ it("leaves an already-correct .lotics/**/* include untouched (idempotent)", () => {
732
+ fs.writeFileSync(tsconfigPath(), JSON.stringify({ include: ["src", ".lotics/**/*"], exclude: ["node_modules", "src/workflows", ".lotics/workflows"] }));
733
+ const before = fs.readFileSync(tsconfigPath(), "utf-8");
734
+ ensureAppTsconfig(dir);
735
+ expect(fs.readFileSync(tsconfigPath(), "utf-8")).toBe(before);
736
+ });
737
+ it("dedupes when both a bare .lotics and the glob are present", () => {
738
+ fs.writeFileSync(tsconfigPath(), JSON.stringify({ include: [".lotics", ".lotics/**/*", "src"] }));
739
+ ensureAppTsconfig(dir);
740
+ expect(read().include).toEqual([".lotics/**/*", "src"]);
741
+ });
742
+ it("heals the include AND adds the excludes in a single write", () => {
743
+ fs.writeFileSync(tsconfigPath(), JSON.stringify({ include: ["src", ".lotics"], exclude: ["node_modules"] }));
744
+ ensureAppTsconfig(dir);
745
+ const cfg = read();
746
+ expect(cfg.include).toEqual(["src", ".lotics/**/*"]);
747
+ expect(cfg.exclude).toEqual(["node_modules", "src/workflows", ".lotics/workflows"]);
748
+ });
726
749
  });
727
750
  describe("appVersions", () => {
728
751
  let logLines;
@@ -122,7 +122,7 @@ function inputDeclToTsType(decl) {
122
122
  case "date_range":
123
123
  return "{ start: string; end: string }";
124
124
  case "file":
125
- return "string";
125
+ return decl.multi === true ? "ReadonlyArray<string>" : "string";
126
126
  case "object": {
127
127
  const fields = decl.fields !== null && typeof decl.fields === "object"
128
128
  ? decl.fields
@@ -70,4 +70,14 @@ describe("generateAppWorkflowsDts", () => {
70
70
  });
71
71
  expect(dts).toContain('owner: string; tags: ReadonlyArray<"a" | "b">');
72
72
  });
73
+ it("types a multi file input as ReadonlyArray<string>, single file as string", () => {
74
+ const dts = generateAppWorkflowsDts({
75
+ attach: {
76
+ workflow_id: "wfl_f",
77
+ inputs: { one: { type: "file" }, many: { type: "file", multi: true } },
78
+ },
79
+ });
80
+ expect(dts).toContain("one: string");
81
+ expect(dts).toContain("many: ReadonlyArray<string>");
82
+ });
73
83
  });
package/dist/src/cli.js CHANGED
@@ -3387,7 +3387,7 @@ var require_jstat = __commonJS({
3387
3387
  }
3388
3388
  })(exports2, function() {
3389
3389
  var jStat = (function(Math2, undefined2) {
3390
- var concat = Array.prototype.concat;
3390
+ var concat2 = Array.prototype.concat;
3391
3391
  var slice = Array.prototype.slice;
3392
3392
  var toString = Object.prototype.toString;
3393
3393
  function calcRdx(n, m) {
@@ -3407,7 +3407,7 @@ var require_jstat = __commonJS({
3407
3407
  return typeof num === "number" ? num - num === 0 : false;
3408
3408
  }
3409
3409
  function toVector(arr) {
3410
- return concat.apply([], arr);
3410
+ return concat2.apply([], arr);
3411
3411
  }
3412
3412
  function jStat2() {
3413
3413
  return new jStat2._init(arguments);
@@ -20136,7 +20136,7 @@ var require_BufferList = __commonJS({
20136
20136
  }
20137
20137
  return ret;
20138
20138
  };
20139
- BufferList.prototype.concat = function concat(n) {
20139
+ BufferList.prototype.concat = function concat2(n) {
20140
20140
  if (this.length === 0) return Buffer2.alloc(0);
20141
20141
  var ret = Buffer2.allocUnsafe(n >>> 0);
20142
20142
  var p = this.head;
@@ -23322,7 +23322,7 @@ var require_StreamHelper = __commonJS({
23322
23322
  return utils.transformTo(type, content);
23323
23323
  }
23324
23324
  }
23325
- function concat(type, dataArray) {
23325
+ function concat2(type, dataArray) {
23326
23326
  var i2, index = 0, res = null, totalLength = 0;
23327
23327
  for (i2 = 0; i2 < dataArray.length; i2++) {
23328
23328
  totalLength += dataArray[i2].length;
@@ -23359,7 +23359,7 @@ var require_StreamHelper = __commonJS({
23359
23359
  reject2(err2);
23360
23360
  }).on("end", function() {
23361
23361
  try {
23362
- var result = transformZipOutput(resultType, concat(chunkType, dataArray), mimeType);
23362
+ var result = transformZipOutput(resultType, concat2(chunkType, dataArray), mimeType);
23363
23363
  resolve(result);
23364
23364
  } catch (e) {
23365
23365
  reject2(e);
@@ -31803,7 +31803,7 @@ function inputDeclToTsType(decl) {
31803
31803
  case "date_range":
31804
31804
  return "{ start: string; end: string }";
31805
31805
  case "file":
31806
- return "string";
31806
+ return decl.multi === true ? "ReadonlyArray<string>" : "string";
31807
31807
  case "object": {
31808
31808
  const fields = decl.fields !== null && typeof decl.fields === "object" ? decl.fields : {};
31809
31809
  return inputsToType(fields);
@@ -32219,6 +32219,8 @@ async function fetchLatestNpmVersion(packageName) {
32219
32219
  var WORKFLOWS_DIR = path5.join("src", "workflows");
32220
32220
  var WORKFLOW_GLOBALS_DIR = path5.join(".lotics", "workflows");
32221
32221
  var WORKFLOW_TSCONFIG_EXCLUDES = ["src/workflows", ".lotics/workflows"];
32222
+ var LOTICS_INCLUDE_GLOB = ".lotics/**/*";
32223
+ var STALE_LOTICS_INCLUDES = /* @__PURE__ */ new Set([".lotics", "./.lotics", ".lotics/"]);
32222
32224
  var FALLBACK_ENVELOPE_PREFIX = "async function __workflow(): Promise<__WorkflowReturn | void> {\n";
32223
32225
  var FALLBACK_ENVELOPE_SUFFIX = "\n}";
32224
32226
  function workflowFileHeader(alias) {
@@ -32362,11 +32364,11 @@ function writeAppMeta(projectDir, meta) {
32362
32364
  pkg2.lotics = meta;
32363
32365
  fs4.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
32364
32366
  }
32365
- function ensureWorkflowTsconfigExcludes(projectDir) {
32367
+ function ensureAppTsconfig(projectDir) {
32366
32368
  const tsconfigPath = path5.join(projectDir, "tsconfig.json");
32367
32369
  if (!fs4.existsSync(tsconfigPath)) {
32368
32370
  console.error(
32369
- `\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.`
32371
+ `\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(", ")}.`
32370
32372
  );
32371
32373
  return;
32372
32374
  }
@@ -32375,20 +32377,29 @@ function ensureWorkflowTsconfigExcludes(projectDir) {
32375
32377
  parsed = JSON.parse(fs4.readFileSync(tsconfigPath, "utf-8"));
32376
32378
  } catch (err2) {
32377
32379
  console.error(
32378
- `\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.`
32380
+ `\u26A0 Could not parse tsconfig.json (${err2 instanceof Error ? err2.message : String(err2)}) \u2014 ensure "include" has "${LOTICS_INCLUDE_GLOB}" and "exclude" has ${WORKFLOW_TSCONFIG_EXCLUDES.join(", ")} manually.`
32379
32381
  );
32380
32382
  return;
32381
32383
  }
32382
32384
  if (!parsed || typeof parsed !== "object") return;
32383
- const current = Array.isArray(parsed.exclude) ? parsed.exclude : [];
32384
- const currentStrings = current.filter((e) => typeof e === "string");
32385
- const toAdd = WORKFLOW_TSCONFIG_EXCLUDES.filter((g) => !currentStrings.includes(g));
32386
- if (toAdd.length === 0) return;
32387
- parsed.exclude = [...currentStrings, ...toAdd];
32385
+ const changes = [];
32386
+ if (Array.isArray(parsed.include)) {
32387
+ const inc = parsed.include.filter((e) => typeof e === "string");
32388
+ if (inc.some((e) => STALE_LOTICS_INCLUDES.has(e))) {
32389
+ const healed = inc.map((e) => STALE_LOTICS_INCLUDES.has(e) ? LOTICS_INCLUDE_GLOB : e);
32390
+ parsed.include = healed.filter((e, i2) => healed.indexOf(e) === i2);
32391
+ changes.push(`rewrote a bare ".lotics" to "${LOTICS_INCLUDE_GLOB}" in "include" (a dot-dir loads zero generated types)`);
32392
+ }
32393
+ }
32394
+ const currentEx = Array.isArray(parsed.exclude) ? parsed.exclude.filter((e) => typeof e === "string") : [];
32395
+ const toAdd = WORKFLOW_TSCONFIG_EXCLUDES.filter((g) => !currentEx.includes(g));
32396
+ if (toAdd.length > 0) {
32397
+ parsed.exclude = [...currentEx, ...toAdd];
32398
+ changes.push(`added ${toAdd.join(", ")} to "exclude"`);
32399
+ }
32400
+ if (changes.length === 0) return;
32388
32401
  fs4.writeFileSync(tsconfigPath, JSON.stringify(parsed, null, 2) + "\n");
32389
- console.error(
32390
- `Patched tsconfig.json: added ${toAdd.join(", ")} to "exclude" so npm run typecheck skips the workflow bodies (check them with: lotics app workflow check).`
32391
- );
32402
+ console.error(`Patched tsconfig.json: ${changes.join("; ")}.`);
32392
32403
  }
32393
32404
  function writeAppDts(projectDir, manifest) {
32394
32405
  const dotLotics = path5.join(projectDir, ".lotics");
@@ -32399,6 +32410,7 @@ function writeAppDts(projectDir, manifest) {
32399
32410
  [path5.join(dotLotics, "app_agents.d.ts"), generateAppAgentsDts(manifest.agents)]
32400
32411
  ];
32401
32412
  for (const [file, content] of written) fs4.writeFileSync(file, content);
32413
+ ensureAppTsconfig(projectDir);
32402
32414
  return written.map(([file]) => file);
32403
32415
  }
32404
32416
  function readCodegenTablesAllowlist(projectDir) {
@@ -32447,7 +32459,6 @@ async function appCodegen(args) {
32447
32459
  );
32448
32460
  }
32449
32461
  await refreshWorkflowGlobals(args.client, projectDir, meta.app_id, Object.keys(meta.workflows ?? {}));
32450
- if (Object.keys(meta.workflows ?? {}).length > 0) ensureWorkflowTsconfigExcludes(projectDir);
32451
32462
  }
32452
32463
  async function refreshWorkflowGlobals(client, projectDir, app_id, aliases) {
32453
32464
  for (const alias of aliases) {
@@ -32630,7 +32641,6 @@ async function appPull(client, args) {
32630
32641
  `Wrote ${written.length} workflow ${written.length === 1 ? "body" : "bodies"} to ${WORKFLOWS_DIR}/ (${written.join(", ")})`
32631
32642
  );
32632
32643
  }
32633
- ensureWorkflowTsconfigExcludes(targetPath);
32634
32644
  }
32635
32645
  console.error(`Installing npm dependencies...`);
32636
32646
  await runNpm(["install"], targetPath);
@@ -32945,7 +32955,7 @@ async function appWorkflowPull(client) {
32945
32955
  console.error(
32946
32956
  `Wrote ${written.length} workflow ${written.length === 1 ? "body" : "bodies"} to ${WORKFLOWS_DIR}/` + (written.length > 0 ? ` (${written.join(", ")})` : "")
32947
32957
  );
32948
- ensureWorkflowTsconfigExcludes(projectDir);
32958
+ ensureAppTsconfig(projectDir);
32949
32959
  }
32950
32960
  async function appWorkflowCheck(args) {
32951
32961
  const projectDir = process.cwd();
@@ -42644,8 +42654,426 @@ function ensureVmlArray(val) {
42644
42654
  return Array.isArray(val) ? val : [val];
42645
42655
  }
42646
42656
 
42657
+ // ../xlsx/src/biff8_reader.ts
42658
+ var OLE2_MAGIC2 = [208, 207, 17, 224, 161, 177, 26, 225];
42659
+ var ENDOFCHAIN = 4294967294;
42660
+ var FREESECT = 4294967295;
42661
+ var DEFAULT_MAX_ROWS = 1e4;
42662
+ function isOle2(bytes) {
42663
+ return bytes.length >= 8 && OLE2_MAGIC2.every((b, i2) => bytes[i2] === b);
42664
+ }
42665
+ function readCfbStreams(bytes) {
42666
+ const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
42667
+ const u162 = (o) => dv.getUint16(o, true);
42668
+ const u32 = (o) => dv.getUint32(o, true);
42669
+ const sectorSize = 1 << u162(30);
42670
+ const miniSectorSize = 1 << u162(32);
42671
+ const firstDirSector = u32(48);
42672
+ const miniCutoff = u32(56);
42673
+ const firstMiniFatSector = u32(60);
42674
+ const firstDifatSector = u32(68);
42675
+ if (sectorSize < 512 || sectorSize > 1 << 20) {
42676
+ throw new Error("Unsupported .xls: invalid OLE2 sector size");
42677
+ }
42678
+ const sectorOffset = (n) => sectorSize * (n + 1);
42679
+ const difat = [];
42680
+ for (let i2 = 0; i2 < 109; i2++) {
42681
+ const v = u32(76 + i2 * 4);
42682
+ if (v !== FREESECT) difat.push(v);
42683
+ }
42684
+ let ds = firstDifatSector;
42685
+ const entriesPerSector = sectorSize / 4;
42686
+ for (let guard = 0; ds !== ENDOFCHAIN && ds !== FREESECT && guard < 1 << 20; guard++) {
42687
+ const base = sectorOffset(ds);
42688
+ for (let i2 = 0; i2 < entriesPerSector - 1; i2++) {
42689
+ const v = u32(base + i2 * 4);
42690
+ if (v !== FREESECT) difat.push(v);
42691
+ }
42692
+ ds = u32(base + (entriesPerSector - 1) * 4);
42693
+ }
42694
+ const fat = [];
42695
+ for (const fatSector of difat) {
42696
+ const base = sectorOffset(fatSector);
42697
+ for (let i2 = 0; i2 < entriesPerSector; i2++) fat.push(u32(base + i2 * 4));
42698
+ }
42699
+ const readChain = (src, start, size, ss, offOf, chain) => {
42700
+ const maxSectors = Math.floor(src.length / ss) + 2;
42701
+ const parts = [];
42702
+ let s = start;
42703
+ for (let guard = 0; s !== ENDOFCHAIN && s !== FREESECT && s < chain.length && guard < maxSectors; guard++) {
42704
+ const o = offOf(s);
42705
+ parts.push(src.subarray(o, o + ss));
42706
+ s = chain[s];
42707
+ }
42708
+ const merged = concat(parts);
42709
+ return size != null ? merged.subarray(0, size) : merged;
42710
+ };
42711
+ const readBig = (start, size) => readChain(bytes, start, size, sectorSize, sectorOffset, fat);
42712
+ const dirBytes = readBig(firstDirSector, null);
42713
+ const ddv = new DataView(dirBytes.buffer, dirBytes.byteOffset, dirBytes.byteLength);
42714
+ const dir = [];
42715
+ for (let off = 0; off + 128 <= dirBytes.length; off += 128) {
42716
+ const nameLen = ddv.getUint16(off + 64, true);
42717
+ const type = ddv.getUint8(off + 66);
42718
+ if (nameLen < 2 || type === 0) continue;
42719
+ const name = utf16le(dirBytes.subarray(off, off + nameLen - 2));
42720
+ dir.push({ name, type, start: ddv.getUint32(off + 116, true), size: Number(ddv.getBigUint64(off + 120, true)) });
42721
+ }
42722
+ const root = dir.find((e) => e.type === 5);
42723
+ const miniStream = root ? readBig(root.start, root.size) : new Uint8Array(0);
42724
+ const miniFatBytes = readBig(firstMiniFatSector, null);
42725
+ const miniFat = [];
42726
+ for (let i2 = 0; i2 + 4 <= miniFatBytes.length; i2 += 4) miniFat.push(miniFatBytes[i2] | miniFatBytes[i2 + 1] << 8 | miniFatBytes[i2 + 2] << 16 | miniFatBytes[i2 + 3] << 24);
42727
+ const readMini = (start, size) => readChain(miniStream, start, size, miniSectorSize, (n) => n * miniSectorSize, miniFat);
42728
+ const readStream = (e) => e.size >= miniCutoff ? readBig(e.start, e.size) : readMini(e.start, e.size);
42729
+ return dir.filter((e) => e.type === 2).map((e) => ({ name: e.name, bytes: readStream(e) }));
42730
+ }
42731
+ var REC = {
42732
+ FORMULA: 6,
42733
+ EOF: 10,
42734
+ CONTINUE: 60,
42735
+ DATEMODE: 34,
42736
+ FILEPASS: 47,
42737
+ BLANK: 513,
42738
+ NUMBER: 515,
42739
+ LABEL: 516,
42740
+ STRING: 519,
42741
+ BOOLERR: 517,
42742
+ BOUNDSHEET: 133,
42743
+ FORMAT: 1054,
42744
+ XF: 224,
42745
+ RK: 638,
42746
+ MULRK: 189,
42747
+ MULBLANK: 190,
42748
+ LABELSST: 253,
42749
+ SST: 252,
42750
+ BOF: 2057
42751
+ };
42752
+ function* iterRecords(stream, from, to) {
42753
+ const dv = new DataView(stream.buffer, stream.byteOffset, stream.byteLength);
42754
+ let p = from;
42755
+ while (p + 4 <= to) {
42756
+ const type = dv.getUint16(p, true);
42757
+ const len = dv.getUint16(p + 2, true);
42758
+ const bodyStart = p + 4;
42759
+ if (bodyStart + len > stream.length) break;
42760
+ yield { type, body: stream.subarray(bodyStart, bodyStart + len), offset: p };
42761
+ p = bodyStart + len;
42762
+ }
42763
+ }
42764
+ function parseGlobals(stream) {
42765
+ const sstChunks = [];
42766
+ const formats = /* @__PURE__ */ new Map();
42767
+ const xfFormatIds = [];
42768
+ const boundsheets = [];
42769
+ let date1904 = false;
42770
+ let sawSst = false;
42771
+ let sstUnique = 0;
42772
+ let end = stream.length;
42773
+ let started = false;
42774
+ const dv = new DataView(stream.buffer, stream.byteOffset, stream.byteLength);
42775
+ for (const r of iterRecords(stream, 0, stream.length)) {
42776
+ if (r.type === REC.BOF) {
42777
+ if (started) {
42778
+ end = r.offset;
42779
+ break;
42780
+ }
42781
+ started = true;
42782
+ continue;
42783
+ }
42784
+ if (r.type === REC.EOF) {
42785
+ end = r.offset;
42786
+ break;
42787
+ }
42788
+ }
42789
+ for (const r of iterRecords(stream, 0, end)) {
42790
+ switch (r.type) {
42791
+ case REC.FILEPASS:
42792
+ throw new PasswordProtectedError();
42793
+ case REC.DATEMODE:
42794
+ date1904 = readU16(r.body, 0) === 1;
42795
+ break;
42796
+ case REC.SST:
42797
+ sawSst = true;
42798
+ sstUnique = readU32(r.body, 4);
42799
+ sstChunks.push(r.body.subarray(8));
42800
+ break;
42801
+ case REC.CONTINUE:
42802
+ if (sawSst) sstChunks.push(r.body);
42803
+ break;
42804
+ case REC.FORMAT: {
42805
+ const id = readU16(r.body, 0);
42806
+ formats.set(id, readUnicodeShort(r.body, 2, 2));
42807
+ break;
42808
+ }
42809
+ case REC.XF: {
42810
+ xfFormatIds.push(readU16(r.body, 2));
42811
+ break;
42812
+ }
42813
+ case REC.BOUNDSHEET: {
42814
+ const pos = dv.getUint32(r.offset + 4, true);
42815
+ boundsheets.push({ name: readUnicodeShort(r.body, 6, 1), pos });
42816
+ break;
42817
+ }
42818
+ default:
42819
+ break;
42820
+ }
42821
+ }
42822
+ const sst = parseSst(sstChunks, sstUnique);
42823
+ const xfFormats = xfFormatIds.map((id) => formats.get(id) ?? builtinFormat(id));
42824
+ return { sst, date1904, xfFormats, boundsheets };
42825
+ }
42826
+ function parseSst(chunks, count) {
42827
+ const out = [];
42828
+ if (chunks.length === 0) return out;
42829
+ let ci = 0;
42830
+ let off = 0;
42831
+ const atEnd = () => ci >= chunks.length;
42832
+ const cur = () => chunks[ci];
42833
+ const advanceChunk = () => {
42834
+ ci++;
42835
+ off = 0;
42836
+ };
42837
+ const rawU8 = () => {
42838
+ while (!atEnd() && off >= cur().length) advanceChunk();
42839
+ if (atEnd()) return 0;
42840
+ return cur()[off++];
42841
+ };
42842
+ const rawU16 = () => rawU8() | rawU8() << 8;
42843
+ const rawU32 = () => rawU8() | rawU8() << 8 | rawU8() << 16 | rawU8() << 24;
42844
+ const skip = (n) => {
42845
+ for (let i2 = 0; i2 < n; i2++) rawU8();
42846
+ };
42847
+ for (let s = 0; s < count && !atEnd(); s++) {
42848
+ const cch = rawU16();
42849
+ const grbit = rawU8();
42850
+ let high = (grbit & 1) !== 0;
42851
+ const rich = (grbit & 8) !== 0 ? rawU16() : 0;
42852
+ const ext = (grbit & 4) !== 0 ? rawU32() : 0;
42853
+ let str = "";
42854
+ let read = 0;
42855
+ while (read < cch) {
42856
+ if (off >= cur().length) {
42857
+ advanceChunk();
42858
+ if (atEnd()) break;
42859
+ high = (cur()[off++] & 1) !== 0;
42860
+ }
42861
+ if (high) {
42862
+ const code = cur()[off] | cur()[off + 1] << 8;
42863
+ off += 2;
42864
+ str += String.fromCharCode(code);
42865
+ } else {
42866
+ str += String.fromCharCode(cur()[off++]);
42867
+ }
42868
+ read++;
42869
+ }
42870
+ skip(rich * 4 + ext);
42871
+ out.push(str);
42872
+ }
42873
+ return out;
42874
+ }
42875
+ function parseSheet2(stream, start, name, g, maxRows) {
42876
+ const sheet = emptySheet(name);
42877
+ const grid = /* @__PURE__ */ new Map();
42878
+ let maxRow = -1;
42879
+ let truncated = false;
42880
+ const put = (row, col, cell) => {
42881
+ if (row >= maxRows) {
42882
+ truncated = true;
42883
+ return;
42884
+ }
42885
+ let r = grid.get(row);
42886
+ if (!r) {
42887
+ r = /* @__PURE__ */ new Map();
42888
+ grid.set(row, r);
42889
+ }
42890
+ r.set(col, cell);
42891
+ if (row > maxRow) maxRow = row;
42892
+ };
42893
+ let end = stream.length;
42894
+ let seenBof = false;
42895
+ for (const r of iterRecords(stream, start, stream.length)) {
42896
+ if (r.type === REC.BOF) {
42897
+ if (seenBof) {
42898
+ end = r.offset;
42899
+ break;
42900
+ }
42901
+ seenBof = true;
42902
+ continue;
42903
+ }
42904
+ if (r.type === REC.EOF) {
42905
+ end = r.offset;
42906
+ break;
42907
+ }
42908
+ }
42909
+ for (const r of iterRecords(stream, start, end)) {
42910
+ switch (r.type) {
42911
+ case REC.LABELSST: {
42912
+ const row = readU16(r.body, 0), col = readU16(r.body, 2), isst = readU32(r.body, 6);
42913
+ put(row, col, textCell(col, g.sst[isst] ?? ""));
42914
+ break;
42915
+ }
42916
+ case REC.LABEL: {
42917
+ const row = readU16(r.body, 0), col = readU16(r.body, 2);
42918
+ put(row, col, textCell(col, readUnicodeShort(r.body, 6, 2)));
42919
+ break;
42920
+ }
42921
+ case REC.NUMBER: {
42922
+ const row = readU16(r.body, 0), col = readU16(r.body, 2), xf = readU16(r.body, 4);
42923
+ put(row, col, numberCell(col, readF64(r.body, 6), g.xfFormats[xf]));
42924
+ break;
42925
+ }
42926
+ case REC.RK: {
42927
+ const row = readU16(r.body, 0), col = readU16(r.body, 2), xf = readU16(r.body, 4);
42928
+ put(row, col, numberCell(col, decodeRk(readU32(r.body, 6)), g.xfFormats[xf]));
42929
+ break;
42930
+ }
42931
+ case REC.MULRK: {
42932
+ const row = readU16(r.body, 0), first2 = readU16(r.body, 2);
42933
+ const n = (r.body.length - 6) / 6;
42934
+ for (let i2 = 0; i2 < n; i2++) {
42935
+ const xf = readU16(r.body, 4 + i2 * 6);
42936
+ const rk = readU32(r.body, 6 + i2 * 6);
42937
+ put(row, first2 + i2, numberCell(first2 + i2, decodeRk(rk), g.xfFormats[xf]));
42938
+ }
42939
+ break;
42940
+ }
42941
+ case REC.FORMULA: {
42942
+ const row = readU16(r.body, 0), col = readU16(r.body, 2), xf = readU16(r.body, 4);
42943
+ const cell = formulaCell(col, r.body, g.xfFormats[xf]);
42944
+ if (cell) put(row, col, cell);
42945
+ break;
42946
+ }
42947
+ // BLANK / MULBLANK / BOOLERR carry no data value we surface — skipped.
42948
+ default:
42949
+ break;
42950
+ }
42951
+ }
42952
+ const rows = [];
42953
+ for (let ri = 0; ri <= maxRow; ri++) {
42954
+ const r = grid.get(ri);
42955
+ if (!r) continue;
42956
+ const cells = [...r.values()].sort((a, b) => a.column - b.column);
42957
+ rows.push({ index: ri, height: sheet.defaultRowHeight, cells, hidden: false });
42958
+ }
42959
+ sheet.rows = rows;
42960
+ sheet.totalRowCount = maxRow + 1;
42961
+ sheet.truncated = truncated;
42962
+ return sheet;
42963
+ }
42964
+ function textCell(column, text) {
42965
+ return { column, value: text, typedValue: text, content: { type: "plain", text }, style: {} };
42966
+ }
42967
+ function numberCell(column, num, numFmt) {
42968
+ const value = Number.isFinite(num) ? String(num) : "";
42969
+ return {
42970
+ column,
42971
+ value,
42972
+ rawValue: num,
42973
+ typedValue: num,
42974
+ numFmtCode: numFmt && numFmt !== "General" ? numFmt : void 0,
42975
+ content: { type: "plain", text: value },
42976
+ style: {}
42977
+ };
42978
+ }
42979
+ function formulaCell(column, body, numFmt) {
42980
+ if (readU16(body, 12) === 65535) {
42981
+ const kind = body[6];
42982
+ if (kind === 1) {
42983
+ const b = body[8] !== 0;
42984
+ return { column, value: b ? "TRUE" : "FALSE", typedValue: b, content: { type: "plain", text: b ? "TRUE" : "FALSE" }, style: {} };
42985
+ }
42986
+ return null;
42987
+ }
42988
+ return numberCell(column, readF64(body, 6), numFmt);
42989
+ }
42990
+ function decodeRk(rk) {
42991
+ const div100 = (rk & 1) !== 0;
42992
+ const isInt = (rk & 2) !== 0;
42993
+ let n;
42994
+ if (isInt) {
42995
+ n = rk >> 2;
42996
+ } else {
42997
+ const buf = new ArrayBuffer(8);
42998
+ new DataView(buf).setUint32(4, rk & 4294967292, true);
42999
+ n = new DataView(buf).getFloat64(0, true);
43000
+ }
43001
+ return div100 ? n / 100 : n;
43002
+ }
43003
+ function readU16(b, o) {
43004
+ return b[o] | b[o + 1] << 8;
43005
+ }
43006
+ function readU32(b, o) {
43007
+ return (b[o] | b[o + 1] << 8 | b[o + 2] << 16 | b[o + 3] << 24) >>> 0;
43008
+ }
43009
+ function readF64(b, o) {
43010
+ return new DataView(b.buffer, b.byteOffset + o, 8).getFloat64(0, true);
43011
+ }
43012
+ function readUnicodeShort(b, offset, lenBytes) {
43013
+ const cch = lenBytes === 1 ? b[offset] : readU16(b, offset);
43014
+ const grbit = b[offset + lenBytes];
43015
+ const high = (grbit & 1) !== 0;
43016
+ let p = offset + lenBytes + 1;
43017
+ let s = "";
43018
+ for (let i2 = 0; i2 < cch; i2++) {
43019
+ if (high) {
43020
+ s += String.fromCharCode(b[p] | b[p + 1] << 8);
43021
+ p += 2;
43022
+ } else {
43023
+ s += String.fromCharCode(b[p]);
43024
+ p += 1;
43025
+ }
43026
+ }
43027
+ return s;
43028
+ }
43029
+ function utf16le(b) {
43030
+ let s = "";
43031
+ for (let i2 = 0; i2 + 1 < b.length; i2 += 2) s += String.fromCharCode(b[i2] | b[i2 + 1] << 8);
43032
+ return s;
43033
+ }
43034
+ function concat(parts) {
43035
+ let total = 0;
43036
+ for (const p of parts) total += p.length;
43037
+ const out = new Uint8Array(total);
43038
+ let o = 0;
43039
+ for (const p of parts) {
43040
+ out.set(p, o);
43041
+ o += p.length;
43042
+ }
43043
+ return out;
43044
+ }
43045
+ function builtinFormat(id) {
43046
+ const DATES = {
43047
+ 14: "m/d/yyyy",
43048
+ 15: "d-mmm-yy",
43049
+ 16: "d-mmm",
43050
+ 17: "mmm-yy",
43051
+ 18: "h:mm AM/PM",
43052
+ 19: "h:mm:ss AM/PM",
43053
+ 20: "h:mm",
43054
+ 21: "h:mm:ss",
43055
+ 22: "m/d/yyyy h:mm",
43056
+ 45: "mm:ss",
43057
+ 46: "[h]:mm:ss",
43058
+ 47: "mm:ss.0"
43059
+ };
43060
+ return DATES[id] ?? "General";
43061
+ }
43062
+ function parseBiff8(bytes, options) {
43063
+ const streams = readCfbStreams(bytes);
43064
+ if (streams.some((s) => /^EncryptedPackage$/i.test(s.name))) throw new PasswordProtectedError();
43065
+ const wb = streams.find((s) => /^workbook$/i.test(s.name)) ?? streams.find((s) => /^book$/i.test(s.name));
43066
+ if (!wb) throw new Error("Not a valid .xls: no Workbook stream in the OLE2 container");
43067
+ const g = parseGlobals(wb.bytes);
43068
+ const maxRows = options?.maxRowsPerSheet ?? DEFAULT_MAX_ROWS;
43069
+ const sheets = g.boundsheets.length > 0 ? g.boundsheets.map((bs) => parseSheet2(wb.bytes, bs.pos, bs.name, g, maxRows)) : [emptySheet("Sheet1")];
43070
+ return { sheets, activeSheetIndex: 0 };
43071
+ }
43072
+
42647
43073
  // ../xlsx/src/excel_parser.ts
42648
43074
  function parseExcelBuffer(arrayBuffer, options) {
43075
+ const bytes = new Uint8Array(arrayBuffer);
43076
+ if (isOle2(bytes)) return parseBiff8(bytes, options);
42649
43077
  const zip = unzipXlsx(arrayBuffer);
42650
43078
  return parseExcelFromZip(zip, options);
42651
43079
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/cli",
3
- "version": "0.64.0",
3
+ "version": "0.65.0",
4
4
  "description": "Lotics SDK and CLI for AI agents",
5
5
  "type": "module",
6
6
  "bin": {