@lotics/cli 0.70.0 → 0.71.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.
@@ -0,0 +1,14 @@
1
+ Bundled license information:
2
+
3
+ jszip/dist/jszip.min.js:
4
+ /*!
5
+
6
+ JSZip v3.10.1 - A JavaScript class for generating and reading zip files
7
+ <http://stuartk.com/jszip>
8
+
9
+ (c) 2009-2016 Stuart Knightley <stuart [at] stuartk.com>
10
+ Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown.
11
+
12
+ JSZip uses the library pako released under the MIT license :
13
+ https://github.com/nodeca/pako/blob/main/LICENSE
14
+ */
package/dist/src/cli.js CHANGED
@@ -20127,7 +20127,7 @@ var require_BufferList = __commonJS({
20127
20127
  this.head = this.tail = null;
20128
20128
  this.length = 0;
20129
20129
  };
20130
- BufferList.prototype.join = function join(s) {
20130
+ BufferList.prototype.join = function join2(s) {
20131
20131
  if (this.length === 0) return "";
20132
20132
  var p = this.head;
20133
20133
  var ret = "" + p.data;
@@ -22102,8 +22102,8 @@ var require_lib2 = __commonJS({
22102
22102
  return this;
22103
22103
  }
22104
22104
  var p = this.constructor;
22105
- return this.then(resolve2, reject3);
22106
- function resolve2(value) {
22105
+ return this.then(resolve3, reject3);
22106
+ function resolve3(value) {
22107
22107
  function yes() {
22108
22108
  return value;
22109
22109
  }
@@ -22256,8 +22256,8 @@ var require_lib2 = __commonJS({
22256
22256
  }
22257
22257
  return out;
22258
22258
  }
22259
- Promise2.resolve = resolve;
22260
- function resolve(value) {
22259
+ Promise2.resolve = resolve2;
22260
+ function resolve2(value) {
22261
22261
  if (value instanceof this) {
22262
22262
  return value;
22263
22263
  }
@@ -22788,10 +22788,10 @@ var require_utils4 = __commonJS({
22788
22788
  var promise = external.Promise.resolve(inputData).then(function(data) {
22789
22789
  var isBlob = support.blob && (data instanceof Blob || ["[object File]", "[object Blob]"].indexOf(Object.prototype.toString.call(data)) !== -1);
22790
22790
  if (isBlob && typeof FileReader !== "undefined") {
22791
- return new external.Promise(function(resolve, reject2) {
22791
+ return new external.Promise(function(resolve2, reject2) {
22792
22792
  var reader = new FileReader();
22793
22793
  reader.onload = function(e) {
22794
- resolve(e.target.result);
22794
+ resolve2(e.target.result);
22795
22795
  };
22796
22796
  reader.onerror = function(e) {
22797
22797
  reject2(e.target.error);
@@ -23346,7 +23346,7 @@ var require_StreamHelper = __commonJS({
23346
23346
  }
23347
23347
  }
23348
23348
  function accumulate(helper, updateCallback) {
23349
- return new external.Promise(function(resolve, reject2) {
23349
+ return new external.Promise(function(resolve2, reject2) {
23350
23350
  var dataArray = [];
23351
23351
  var chunkType = helper._internalType, resultType = helper._outputType, mimeType = helper._mimeType;
23352
23352
  helper.on("data", function(data, meta) {
@@ -23360,7 +23360,7 @@ var require_StreamHelper = __commonJS({
23360
23360
  }).on("end", function() {
23361
23361
  try {
23362
23362
  var result = transformZipOutput(resultType, concat2(chunkType, dataArray), mimeType);
23363
- resolve(result);
23363
+ resolve2(result);
23364
23364
  } catch (e) {
23365
23365
  reject2(e);
23366
23366
  }
@@ -29473,7 +29473,7 @@ var require_load = __commonJS({
29473
29473
  var Crc32Probe = require_Crc32Probe();
29474
29474
  var nodejsUtils = require_nodejsUtils();
29475
29475
  function checkEntryCRC32(zipEntry) {
29476
- return new external.Promise(function(resolve, reject2) {
29476
+ return new external.Promise(function(resolve2, reject2) {
29477
29477
  var worker = zipEntry.decompressed.getContentWorker().pipe(new Crc32Probe());
29478
29478
  worker.on("error", function(e) {
29479
29479
  reject2(e);
@@ -29481,7 +29481,7 @@ var require_load = __commonJS({
29481
29481
  if (worker.streamInfo.crc32 !== zipEntry.decompressed.crc32) {
29482
29482
  reject2(new Error("Corrupted zip : CRC32 mismatch"));
29483
29483
  } else {
29484
- resolve();
29484
+ resolve2();
29485
29485
  }
29486
29486
  }).resume();
29487
29487
  });
@@ -31553,8 +31553,8 @@ async function startDevServer(args) {
31553
31553
  );
31554
31554
  let stopped = false;
31555
31555
  let stoppingResolve = null;
31556
- const stoppingPromise = new Promise((resolve) => {
31557
- stoppingResolve = resolve;
31556
+ const stoppingPromise = new Promise((resolve2) => {
31557
+ stoppingResolve = resolve2;
31558
31558
  });
31559
31559
  viteChild.on("exit", (code, signal) => {
31560
31560
  if (!stopped) {
@@ -31655,11 +31655,11 @@ async function startDevServer(args) {
31655
31655
  res.writeHead(404, { "Content-Type": "text/plain" });
31656
31656
  res.end("Not Found");
31657
31657
  });
31658
- await new Promise((resolve, reject2) => {
31658
+ await new Promise((resolve2, reject2) => {
31659
31659
  server.once("error", reject2);
31660
31660
  server.listen(wrapperPort, () => {
31661
31661
  server.off("error", reject2);
31662
- resolve();
31662
+ resolve2();
31663
31663
  });
31664
31664
  });
31665
31665
  async function stopAll() {
@@ -31668,11 +31668,11 @@ async function startDevServer(args) {
31668
31668
  if (!viteChild.killed) {
31669
31669
  viteChild.kill("SIGINT");
31670
31670
  }
31671
- await new Promise((resolve) => server.close(() => resolve()));
31671
+ await new Promise((resolve2) => server.close(() => resolve2()));
31672
31672
  stoppingResolve?.();
31673
31673
  }
31674
- const ready = new Promise((resolve) => {
31675
- setTimeout(resolve, 800);
31674
+ const ready = new Promise((resolve2) => {
31675
+ setTimeout(resolve2, 800);
31676
31676
  });
31677
31677
  return {
31678
31678
  port: wrapperPort,
@@ -31688,22 +31688,22 @@ async function pickPort(preferred, ...avoid) {
31688
31688
  if (!avoid.includes(preferred) && await isPortFree(preferred)) {
31689
31689
  return preferred;
31690
31690
  }
31691
- return new Promise((resolve, reject2) => {
31691
+ return new Promise((resolve2, reject2) => {
31692
31692
  const srv = net.createServer();
31693
31693
  srv.once("error", reject2);
31694
31694
  srv.listen(0, () => {
31695
31695
  const addr = srv.address();
31696
31696
  const port = typeof addr === "object" && addr ? addr.port : 0;
31697
- srv.close(() => port ? resolve(port) : reject2(new Error("Failed to pick port")));
31697
+ srv.close(() => port ? resolve2(port) : reject2(new Error("Failed to pick port")));
31698
31698
  });
31699
31699
  });
31700
31700
  }
31701
31701
  function isPortFree(port) {
31702
- return new Promise((resolve) => {
31702
+ return new Promise((resolve2) => {
31703
31703
  const srv = net.createServer();
31704
- srv.once("error", () => resolve(false));
31704
+ srv.once("error", () => resolve2(false));
31705
31705
  srv.listen(port, () => {
31706
- srv.close(() => resolve(true));
31706
+ srv.close(() => resolve2(true));
31707
31707
  });
31708
31708
  });
31709
31709
  }
@@ -32337,7 +32337,7 @@ async function fetchWorkflowGlobals(client, projectDir, app_id, alias) {
32337
32337
  }
32338
32338
  }
32339
32339
  function runTar(args, cwd) {
32340
- return new Promise((resolve, reject2) => {
32340
+ return new Promise((resolve2, reject2) => {
32341
32341
  const proc = spawn2("tar", args, { cwd, stdio: ["ignore", "ignore", "pipe"] });
32342
32342
  let stderr = "";
32343
32343
  proc.stderr.on("data", (chunk) => {
@@ -32345,17 +32345,17 @@ function runTar(args, cwd) {
32345
32345
  });
32346
32346
  proc.on("error", reject2);
32347
32347
  proc.on("exit", (code) => {
32348
- if (code === 0) resolve();
32348
+ if (code === 0) resolve2();
32349
32349
  else reject2(new Error(`tar ${args.join(" ")} failed with exit ${code}: ${stderr}`));
32350
32350
  });
32351
32351
  });
32352
32352
  }
32353
32353
  function runNpm(args, cwd) {
32354
- return new Promise((resolve, reject2) => {
32354
+ return new Promise((resolve2, reject2) => {
32355
32355
  const proc = spawn2("npm", args, { cwd, stdio: "inherit", env: ipv4ChildEnv(process.env) });
32356
32356
  proc.on("error", reject2);
32357
32357
  proc.on("exit", (code) => {
32358
- if (code === 0) resolve();
32358
+ if (code === 0) resolve2();
32359
32359
  else reject2(new Error(`npm ${args.join(" ")} exited with code ${code}`));
32360
32360
  });
32361
32361
  });
@@ -32851,11 +32851,11 @@ async function appDev(client, args) {
32851
32851
  console.error(` Ctrl-C to stop.
32852
32852
  `);
32853
32853
  openBrowser(url);
32854
- await new Promise((resolve) => {
32854
+ await new Promise((resolve2) => {
32855
32855
  const onSig = () => {
32856
32856
  process.off("SIGINT", onSig);
32857
32857
  process.off("SIGTERM", onSig);
32858
- resolve();
32858
+ resolve2();
32859
32859
  };
32860
32860
  process.on("SIGINT", onSig);
32861
32861
  process.on("SIGTERM", onSig);
@@ -49655,6 +49655,198 @@ async function runDocxCommand(subcommand, toolArgs, restArgs) {
49655
49655
  }
49656
49656
  }
49657
49657
 
49658
+ // src/preview.ts
49659
+ import { spawn as spawn3 } from "node:child_process";
49660
+ import { createServer } from "node:http";
49661
+ import { readFileSync as readFileSync2, writeFileSync, existsSync, mkdtempSync, rmSync, readdirSync } from "node:fs";
49662
+ import { tmpdir as tmpdir2 } from "node:os";
49663
+ import { join, dirname, resolve, extname, basename } from "node:path";
49664
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
49665
+ import { setTimeout as sleep } from "node:timers/promises";
49666
+ var HERE = dirname(fileURLToPath2(import.meta.url));
49667
+ function fail2(msg) {
49668
+ console.error(msg);
49669
+ process.exit(1);
49670
+ }
49671
+ function findChrome() {
49672
+ const env = process.env.LOTICS_CHROME || process.env.CHROME_PATH;
49673
+ if (env && existsSync(env)) return env;
49674
+ const home = process.env.HOME || "";
49675
+ const pwDir = process.platform === "darwin" ? `${home}/Library/Caches/ms-playwright` : `${home}/.cache/ms-playwright`;
49676
+ if (existsSync(pwDir)) {
49677
+ const rel = process.platform === "darwin" ? ["chrome-mac/Chromium.app/Contents/MacOS/Chromium"] : ["chrome-linux/chrome", "chrome-linux/headless_shell"];
49678
+ const revs = readdirSync(pwDir).filter((n) => n.startsWith("chromium-") || n.startsWith("chromium_headless_shell-")).sort().reverse();
49679
+ for (const rev2 of revs) {
49680
+ for (const r of rel) {
49681
+ const bin = join(pwDir, rev2, r);
49682
+ if (existsSync(bin)) return bin;
49683
+ }
49684
+ }
49685
+ }
49686
+ const systemPaths = [
49687
+ "/usr/bin/google-chrome",
49688
+ "/usr/bin/google-chrome-stable",
49689
+ "/usr/bin/chromium",
49690
+ "/usr/bin/chromium-browser",
49691
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
49692
+ "/Applications/Chromium.app/Contents/MacOS/Chromium"
49693
+ ];
49694
+ return systemPaths.find((p) => existsSync(p)) ?? null;
49695
+ }
49696
+ async function cdpConnect(wsUrl) {
49697
+ const ws = new WebSocket(wsUrl);
49698
+ await new Promise((res, rej) => {
49699
+ ws.onopen = () => res();
49700
+ ws.onerror = () => rej(new Error("CDP websocket failed to open"));
49701
+ });
49702
+ let id = 0;
49703
+ const pending = /* @__PURE__ */ new Map();
49704
+ ws.onclose = () => {
49705
+ for (const done of pending.values()) done(Promise.reject(new Error("CDP connection closed (Chrome exited?)")));
49706
+ pending.clear();
49707
+ };
49708
+ ws.onmessage = (e) => {
49709
+ const m = JSON.parse(String(e.data));
49710
+ if (m.id != null && pending.has(m.id)) {
49711
+ const done = pending.get(m.id);
49712
+ pending.delete(m.id);
49713
+ done(m.error ? Promise.reject(new Error(m.error.message)) : m.result);
49714
+ }
49715
+ };
49716
+ const send = (method, params = {}) => new Promise((res) => {
49717
+ const i2 = ++id;
49718
+ pending.set(i2, (r) => res(r));
49719
+ ws.send(JSON.stringify({ id: i2, method, params }));
49720
+ });
49721
+ return { send, close: () => ws.close() };
49722
+ }
49723
+ async function runPreviewCommand(filePath, flags) {
49724
+ if (!filePath) fail2("Usage: lotics preview <file.docx|.xlsx> [--out <file.png>]");
49725
+ const abs = resolve(filePath);
49726
+ if (!existsSync(abs)) fail2(`File not found: ${abs}`);
49727
+ const ext = extname(abs).toLowerCase();
49728
+ const type = ext === ".docx" ? "docx" : ext === ".xlsx" || ext === ".xls" || ext === ".csv" ? "xlsx" : null;
49729
+ if (!type) fail2(`Unsupported file type "${ext}" \u2014 preview supports .docx and .xlsx/.csv. (PDFs open directly \u2014 no preview needed.)`);
49730
+ if (typeof WebSocket === "undefined") {
49731
+ fail2("lotics preview needs Node 22+ (it drives Chrome over CDP via the built-in WebSocket). Upgrade Node and retry.");
49732
+ }
49733
+ const bundlePath = join(HERE, "..", "render_page.js");
49734
+ if (!existsSync(bundlePath)) fail2(`Render bundle missing at ${bundlePath} \u2014 reinstall @lotics/cli (build step failed).`);
49735
+ const chrome = findChrome();
49736
+ if (!chrome) {
49737
+ fail2(
49738
+ "No Chrome/Chromium found. Set CHROME_PATH to a Chrome binary, or install one:\n npx playwright install chromium (then it's auto-detected)\n or install Google Chrome / Chromium via your package manager."
49739
+ );
49740
+ }
49741
+ const b64 = readFileSync2(abs).toString("base64");
49742
+ const bundle = readFileSync2(bundlePath, "utf8");
49743
+ const html = `<!doctype html><html><head><meta charset="utf-8"><style>body{margin:0;background:#fff;font-family:sans-serif}#root{padding:20px;max-width:1240px;margin:0 auto}</style></head><body><div id="root"></div><script>window.__LOTICS_RENDER=${JSON.stringify({ type, b64 })}</script><script src="/render_page.js"></script></body></html>`;
49744
+ const server = createServer((req, res) => {
49745
+ if (req.url === "/render_page.js") {
49746
+ res.writeHead(200, { "content-type": "application/javascript" });
49747
+ res.end(bundle);
49748
+ } else {
49749
+ res.writeHead(200, { "content-type": "text/html" });
49750
+ res.end(html);
49751
+ }
49752
+ });
49753
+ await new Promise((r) => server.listen(0, "127.0.0.1", () => r()));
49754
+ const httpPort = server.address().port;
49755
+ const udd = mkdtempSync(join(tmpdir2(), "lotics-render-"));
49756
+ const child = spawn3(chrome, [
49757
+ "--headless=new",
49758
+ "--disable-gpu",
49759
+ "--no-sandbox",
49760
+ "--hide-scrollbars",
49761
+ // Large multi-sheet renders exhaust the (often tiny) /dev/shm in containers/WSL2 and
49762
+ // crash the tab; back shared memory with /tmp instead.
49763
+ "--disable-dev-shm-usage",
49764
+ "--force-device-scale-factor=2",
49765
+ "--remote-debugging-port=0",
49766
+ `--user-data-dir=${udd}`,
49767
+ "about:blank"
49768
+ ], { stdio: "ignore" });
49769
+ const cleanup = () => {
49770
+ try {
49771
+ child.kill();
49772
+ } catch {
49773
+ }
49774
+ try {
49775
+ server.close();
49776
+ } catch {
49777
+ }
49778
+ try {
49779
+ rmSync(udd, { recursive: true, force: true });
49780
+ } catch {
49781
+ }
49782
+ };
49783
+ try {
49784
+ let cdpPort = 0;
49785
+ const portFile = join(udd, "DevToolsActivePort");
49786
+ for (let i2 = 0; i2 < 100 && !cdpPort; i2++) {
49787
+ if (existsSync(portFile)) {
49788
+ const p = parseInt(readFileSync2(portFile, "utf8").split("\n")[0], 10);
49789
+ if (p) cdpPort = p;
49790
+ }
49791
+ if (!cdpPort) await sleep(100);
49792
+ }
49793
+ if (!cdpPort) throw new Error("Chrome did not expose a debugging port (launch failed?).");
49794
+ let target;
49795
+ for (let i2 = 0; i2 < 60 && !target?.webSocketDebuggerUrl; i2++) {
49796
+ try {
49797
+ const list = await (await fetch(`http://127.0.0.1:${cdpPort}/json/list`)).json();
49798
+ target = list.find((t) => t.type === "page");
49799
+ } catch {
49800
+ }
49801
+ if (!target?.webSocketDebuggerUrl) await sleep(100);
49802
+ }
49803
+ if (!target?.webSocketDebuggerUrl) throw new Error("No Chrome page target available.");
49804
+ const cdp = await cdpConnect(target.webSocketDebuggerUrl);
49805
+ await cdp.send("Page.enable");
49806
+ await cdp.send("Runtime.enable");
49807
+ await cdp.send("Page.navigate", { url: `http://127.0.0.1:${httpPort}/` });
49808
+ let err2;
49809
+ let done = false;
49810
+ const warnings = [];
49811
+ for (let i2 = 0; i2 < 200; i2++) {
49812
+ const r = await cdp.send("Runtime.evaluate", {
49813
+ expression: "({done: !!window.__loticsDone, err: window.__loticsError || '', warnings: window.__loticsWarnings || []})",
49814
+ returnByValue: true
49815
+ });
49816
+ const v = r.result?.value;
49817
+ if (v?.done) {
49818
+ done = true;
49819
+ err2 = v.err || void 0;
49820
+ if (v.warnings?.length) warnings.push(...v.warnings);
49821
+ break;
49822
+ }
49823
+ await sleep(75);
49824
+ }
49825
+ if (!done) throw new Error("Render timed out (page never signaled completion).");
49826
+ if (err2) throw new Error(`Render engine error: ${err2}`);
49827
+ const metrics = await cdp.send("Page.getLayoutMetrics");
49828
+ const size = metrics.cssContentSize ?? metrics.contentSize ?? { width: 1240, height: 1600 };
49829
+ const MAX_CAPTURE = 15e3;
49830
+ const w = Math.ceil(size.width);
49831
+ const h = Math.ceil(size.height);
49832
+ if (w > MAX_CAPTURE || h > MAX_CAPTURE) {
49833
+ warnings.push(`content ${w}\xD7${h}px clipped to ${Math.min(w, MAX_CAPTURE)}\xD7${Math.min(h, MAX_CAPTURE)}px (exceeds the ${MAX_CAPTURE}px capture limit)`);
49834
+ }
49835
+ const shot = await cdp.send("Page.captureScreenshot", {
49836
+ format: "png",
49837
+ captureBeyondViewport: true,
49838
+ clip: { x: 0, y: 0, width: Math.min(w, MAX_CAPTURE), height: Math.min(h, MAX_CAPTURE), scale: 1 }
49839
+ });
49840
+ cdp.close();
49841
+ const outPath = flags.output ? resolve(flags.output) : join(dirname(abs), basename(abs, ext) + ".png");
49842
+ writeFileSync(outPath, Buffer.from(shot.data, "base64"));
49843
+ console.log(`Rendered ${basename(abs)} \u2192 ${outPath} (${Math.min(w, MAX_CAPTURE)}\xD7${Math.min(h, MAX_CAPTURE)}, via ${basename(chrome)})`);
49844
+ for (const warn of warnings) console.error(` \u26A0 ${warn}`);
49845
+ } finally {
49846
+ cleanup();
49847
+ }
49848
+ }
49849
+
49658
49850
  // src/cli.ts
49659
49851
  dns.setDefaultResultOrder("ipv4first");
49660
49852
  net2.setDefaultAutoSelectFamilyAttemptTimeout(2e3);
@@ -49706,10 +49898,6 @@ COMMANDS
49706
49898
  lotics run <tool> '<json>' Execute a tool
49707
49899
  lotics run <tool> @args.json Read JSON args from a file (large payloads)
49708
49900
  cat args.json | lotics run <tool> Read JSON args from stdin (large payloads)
49709
- lotics upload <file|dir...> Upload files (directories expand to their immediate files)
49710
- lotics download <file_id> Download a file by ID
49711
- lotics download record <record_id> <field_key>
49712
- Download all files on a record file field
49713
49901
  lotics app create <name> [path] Create a new custom-code app + scaffold locally
49714
49902
  lotics app pull <app_id> [path] Bootstrap full local env (source + npm install + types)
49715
49903
  lotics app deploy -m <message> Build + upload current dir as a new version
@@ -49747,6 +49935,12 @@ COMMANDS
49747
49935
  lotics docx <subcommand> ... Read/write/edit .docx files on your local filesystem
49748
49936
  (uses the bundled Lotics OOXML engine; preserves
49749
49937
  tables, images, and unknown markup verbatim)
49938
+ lotics file upload <file|dir...> Upload files (alias: lotics upload)
49939
+ lotics file download <file_id> Download a file by ID (alias: lotics download)
49940
+ lotics file download record <record_id> <field_key>
49941
+ Download all files on a record file field
49942
+ lotics file preview <file> [-o png] Render a .docx/.xlsx to a PNG (frontend engines;
49943
+ needs a Chrome/Chromium on the machine)
49750
49944
 
49751
49945
  FLAGS
49752
49946
  --json Full JSON output (default is human-readable text)
@@ -49824,10 +50018,10 @@ Resolution precedence (highest first):
49824
50018
  > global active profile. LOTICS_WORKSPACE / --workspace override the workspace.`);
49825
50019
  }
49826
50020
  function readStdin() {
49827
- return new Promise((resolve, reject2) => {
50021
+ return new Promise((resolve2, reject2) => {
49828
50022
  const chunks = [];
49829
50023
  process.stdin.on("data", (chunk) => chunks.push(chunk));
49830
- process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8").trim()));
50024
+ process.stdin.on("end", () => resolve2(Buffer.concat(chunks).toString("utf-8").trim()));
49831
50025
  process.stdin.on("error", reject2);
49832
50026
  });
49833
50027
  }
@@ -49836,10 +50030,10 @@ function prompt(question) {
49836
50030
  input: process.stdin,
49837
50031
  output: process.stderr
49838
50032
  });
49839
- return new Promise((resolve) => {
50033
+ return new Promise((resolve2) => {
49840
50034
  rl.question(question, (answer) => {
49841
50035
  rl.close();
49842
- resolve(answer.trim());
50036
+ resolve2(answer.trim());
49843
50037
  });
49844
50038
  });
49845
50039
  }
@@ -50006,7 +50200,18 @@ function resolveUploadPaths(rawPaths) {
50006
50200
  return result;
50007
50201
  }
50008
50202
  async function main() {
50009
- const { command, subcommand, toolArgs, restArgs, flags } = parseArgs(process.argv.slice(2));
50203
+ const parsed = parseArgs(process.argv.slice(2));
50204
+ const { restArgs, flags } = parsed;
50205
+ let { command, subcommand, toolArgs } = parsed;
50206
+ if (command === "file") {
50207
+ if (!subcommand) {
50208
+ printHelp();
50209
+ return;
50210
+ }
50211
+ command = subcommand;
50212
+ subcommand = toolArgs;
50213
+ toolArgs = restArgs.shift();
50214
+ }
50010
50215
  if (flags.help || !command && !flags.version) {
50011
50216
  printHelp();
50012
50217
  return;
@@ -50100,6 +50305,10 @@ async function main() {
50100
50305
  await runDocxCommand(subcommand, toolArgs, restArgs);
50101
50306
  return;
50102
50307
  }
50308
+ if (command === "preview") {
50309
+ await runPreviewCommand(subcommand, flags);
50310
+ return;
50311
+ }
50103
50312
  if (command === "ui") {
50104
50313
  if (subcommand === "link") {
50105
50314
  const component = toolArgs;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/cli",
3
- "version": "0.70.0",
3
+ "version": "0.71.0",
4
4
  "description": "Lotics SDK and CLI for AI agents",
5
5
  "type": "module",
6
6
  "bin": {