@lotics/cli 0.69.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.
- package/dist/cli.js +27 -5
- package/dist/generate_app_workflows_dts.d.ts +3 -1
- package/dist/generate_app_workflows_dts.js +15 -4
- package/dist/generate_app_workflows_dts.test.js +26 -0
- package/dist/preview.d.ts +3 -0
- package/dist/preview.js +233 -0
- package/dist/render_page.js +59068 -0
- package/dist/render_page.js.LEGAL.txt +14 -0
- package/dist/src/cli.js +254 -42
- package/package.json +1 -1
|
@@ -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
|
|
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(
|
|
22106
|
-
function
|
|
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 =
|
|
22260
|
-
function
|
|
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(
|
|
22791
|
+
return new external.Promise(function(resolve2, reject2) {
|
|
22792
22792
|
var reader = new FileReader();
|
|
22793
22793
|
reader.onload = function(e) {
|
|
22794
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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((
|
|
31557
|
-
stoppingResolve =
|
|
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((
|
|
31658
|
+
await new Promise((resolve2, reject2) => {
|
|
31659
31659
|
server.once("error", reject2);
|
|
31660
31660
|
server.listen(wrapperPort, () => {
|
|
31661
31661
|
server.off("error", reject2);
|
|
31662
|
-
|
|
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((
|
|
31671
|
+
await new Promise((resolve2) => server.close(() => resolve2()));
|
|
31672
31672
|
stoppingResolve?.();
|
|
31673
31673
|
}
|
|
31674
|
-
const ready = new Promise((
|
|
31675
|
-
setTimeout(
|
|
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((
|
|
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 ?
|
|
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((
|
|
31702
|
+
return new Promise((resolve2) => {
|
|
31703
31703
|
const srv = net.createServer();
|
|
31704
|
-
srv.once("error", () =>
|
|
31704
|
+
srv.once("error", () => resolve2(false));
|
|
31705
31705
|
srv.listen(port, () => {
|
|
31706
|
-
srv.close(() =>
|
|
31706
|
+
srv.close(() => resolve2(true));
|
|
31707
31707
|
});
|
|
31708
31708
|
});
|
|
31709
31709
|
}
|
|
@@ -31759,7 +31759,7 @@ declare module "@lotics/app-sdk" {
|
|
|
31759
31759
|
const inputLines = [];
|
|
31760
31760
|
const resultLines = [];
|
|
31761
31761
|
for (const [alias, declaration] of entries) {
|
|
31762
|
-
const valueType = declaration.inputs ? inputsToType(declaration.inputs) : "Record<string, unknown>";
|
|
31762
|
+
const valueType = declaration.inputs ? inputsToType(declaration.inputs, { nullableOptional: true }) : "Record<string, unknown>";
|
|
31763
31763
|
const aliasKey = isValidIdentifier(alias) ? alias : JSON.stringify(alias);
|
|
31764
31764
|
inputLines.push(` ${aliasKey}: ${valueType};`);
|
|
31765
31765
|
if (declaration.outputs) {
|
|
@@ -31778,15 +31778,18 @@ ${inputLines.join("\n")}
|
|
|
31778
31778
|
}
|
|
31779
31779
|
`;
|
|
31780
31780
|
}
|
|
31781
|
-
function inputsToType(inputs) {
|
|
31781
|
+
function inputsToType(inputs, opts) {
|
|
31782
|
+
const nullableOptional = opts?.nullableOptional === true;
|
|
31782
31783
|
const fields = [];
|
|
31783
31784
|
for (const [key, decl] of Object.entries(inputs)) {
|
|
31784
31785
|
if (decl === null || typeof decl !== "object") continue;
|
|
31785
31786
|
const d = decl;
|
|
31786
31787
|
const tsType = inputDeclToTsType(d);
|
|
31787
|
-
const
|
|
31788
|
+
const isOptional = d.required === false;
|
|
31789
|
+
const fieldType = isOptional && nullableOptional ? `${tsType} | null` : tsType;
|
|
31790
|
+
const optional = isOptional ? "?" : "";
|
|
31788
31791
|
const fieldKey = isValidIdentifier(key) ? key : JSON.stringify(key);
|
|
31789
|
-
fields.push(` ${fieldKey}${optional}: ${
|
|
31792
|
+
fields.push(` ${fieldKey}${optional}: ${fieldType};`);
|
|
31790
31793
|
}
|
|
31791
31794
|
if (fields.length === 0) return "Record<string, never>";
|
|
31792
31795
|
return `{
|
|
@@ -32334,7 +32337,7 @@ async function fetchWorkflowGlobals(client, projectDir, app_id, alias) {
|
|
|
32334
32337
|
}
|
|
32335
32338
|
}
|
|
32336
32339
|
function runTar(args, cwd) {
|
|
32337
|
-
return new Promise((
|
|
32340
|
+
return new Promise((resolve2, reject2) => {
|
|
32338
32341
|
const proc = spawn2("tar", args, { cwd, stdio: ["ignore", "ignore", "pipe"] });
|
|
32339
32342
|
let stderr = "";
|
|
32340
32343
|
proc.stderr.on("data", (chunk) => {
|
|
@@ -32342,17 +32345,17 @@ function runTar(args, cwd) {
|
|
|
32342
32345
|
});
|
|
32343
32346
|
proc.on("error", reject2);
|
|
32344
32347
|
proc.on("exit", (code) => {
|
|
32345
|
-
if (code === 0)
|
|
32348
|
+
if (code === 0) resolve2();
|
|
32346
32349
|
else reject2(new Error(`tar ${args.join(" ")} failed with exit ${code}: ${stderr}`));
|
|
32347
32350
|
});
|
|
32348
32351
|
});
|
|
32349
32352
|
}
|
|
32350
32353
|
function runNpm(args, cwd) {
|
|
32351
|
-
return new Promise((
|
|
32354
|
+
return new Promise((resolve2, reject2) => {
|
|
32352
32355
|
const proc = spawn2("npm", args, { cwd, stdio: "inherit", env: ipv4ChildEnv(process.env) });
|
|
32353
32356
|
proc.on("error", reject2);
|
|
32354
32357
|
proc.on("exit", (code) => {
|
|
32355
|
-
if (code === 0)
|
|
32358
|
+
if (code === 0) resolve2();
|
|
32356
32359
|
else reject2(new Error(`npm ${args.join(" ")} exited with code ${code}`));
|
|
32357
32360
|
});
|
|
32358
32361
|
});
|
|
@@ -32848,11 +32851,11 @@ async function appDev(client, args) {
|
|
|
32848
32851
|
console.error(` Ctrl-C to stop.
|
|
32849
32852
|
`);
|
|
32850
32853
|
openBrowser(url);
|
|
32851
|
-
await new Promise((
|
|
32854
|
+
await new Promise((resolve2) => {
|
|
32852
32855
|
const onSig = () => {
|
|
32853
32856
|
process.off("SIGINT", onSig);
|
|
32854
32857
|
process.off("SIGTERM", onSig);
|
|
32855
|
-
|
|
32858
|
+
resolve2();
|
|
32856
32859
|
};
|
|
32857
32860
|
process.on("SIGINT", onSig);
|
|
32858
32861
|
process.on("SIGTERM", onSig);
|
|
@@ -49652,6 +49655,198 @@ async function runDocxCommand(subcommand, toolArgs, restArgs) {
|
|
|
49652
49655
|
}
|
|
49653
49656
|
}
|
|
49654
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
|
+
|
|
49655
49850
|
// src/cli.ts
|
|
49656
49851
|
dns.setDefaultResultOrder("ipv4first");
|
|
49657
49852
|
net2.setDefaultAutoSelectFamilyAttemptTimeout(2e3);
|
|
@@ -49703,10 +49898,6 @@ COMMANDS
|
|
|
49703
49898
|
lotics run <tool> '<json>' Execute a tool
|
|
49704
49899
|
lotics run <tool> @args.json Read JSON args from a file (large payloads)
|
|
49705
49900
|
cat args.json | lotics run <tool> Read JSON args from stdin (large payloads)
|
|
49706
|
-
lotics upload <file|dir...> Upload files (directories expand to their immediate files)
|
|
49707
|
-
lotics download <file_id> Download a file by ID
|
|
49708
|
-
lotics download record <record_id> <field_key>
|
|
49709
|
-
Download all files on a record file field
|
|
49710
49901
|
lotics app create <name> [path] Create a new custom-code app + scaffold locally
|
|
49711
49902
|
lotics app pull <app_id> [path] Bootstrap full local env (source + npm install + types)
|
|
49712
49903
|
lotics app deploy -m <message> Build + upload current dir as a new version
|
|
@@ -49744,6 +49935,12 @@ COMMANDS
|
|
|
49744
49935
|
lotics docx <subcommand> ... Read/write/edit .docx files on your local filesystem
|
|
49745
49936
|
(uses the bundled Lotics OOXML engine; preserves
|
|
49746
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)
|
|
49747
49944
|
|
|
49748
49945
|
FLAGS
|
|
49749
49946
|
--json Full JSON output (default is human-readable text)
|
|
@@ -49821,10 +50018,10 @@ Resolution precedence (highest first):
|
|
|
49821
50018
|
> global active profile. LOTICS_WORKSPACE / --workspace override the workspace.`);
|
|
49822
50019
|
}
|
|
49823
50020
|
function readStdin() {
|
|
49824
|
-
return new Promise((
|
|
50021
|
+
return new Promise((resolve2, reject2) => {
|
|
49825
50022
|
const chunks = [];
|
|
49826
50023
|
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
49827
|
-
process.stdin.on("end", () =>
|
|
50024
|
+
process.stdin.on("end", () => resolve2(Buffer.concat(chunks).toString("utf-8").trim()));
|
|
49828
50025
|
process.stdin.on("error", reject2);
|
|
49829
50026
|
});
|
|
49830
50027
|
}
|
|
@@ -49833,10 +50030,10 @@ function prompt(question) {
|
|
|
49833
50030
|
input: process.stdin,
|
|
49834
50031
|
output: process.stderr
|
|
49835
50032
|
});
|
|
49836
|
-
return new Promise((
|
|
50033
|
+
return new Promise((resolve2) => {
|
|
49837
50034
|
rl.question(question, (answer) => {
|
|
49838
50035
|
rl.close();
|
|
49839
|
-
|
|
50036
|
+
resolve2(answer.trim());
|
|
49840
50037
|
});
|
|
49841
50038
|
});
|
|
49842
50039
|
}
|
|
@@ -50003,7 +50200,18 @@ function resolveUploadPaths(rawPaths) {
|
|
|
50003
50200
|
return result;
|
|
50004
50201
|
}
|
|
50005
50202
|
async function main() {
|
|
50006
|
-
const
|
|
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
|
+
}
|
|
50007
50215
|
if (flags.help || !command && !flags.version) {
|
|
50008
50216
|
printHelp();
|
|
50009
50217
|
return;
|
|
@@ -50097,6 +50305,10 @@ async function main() {
|
|
|
50097
50305
|
await runDocxCommand(subcommand, toolArgs, restArgs);
|
|
50098
50306
|
return;
|
|
50099
50307
|
}
|
|
50308
|
+
if (command === "preview") {
|
|
50309
|
+
await runPreviewCommand(subcommand, flags);
|
|
50310
|
+
return;
|
|
50311
|
+
}
|
|
50100
50312
|
if (command === "ui") {
|
|
50101
50313
|
if (subcommand === "link") {
|
|
50102
50314
|
const component = toolArgs;
|