@lotics/cli 0.93.0 → 0.94.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli.js +282 -52
- package/dist/src/client.d.ts +20 -1
- package/dist/src/client.js +25 -2
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -29586,6 +29586,7 @@ var require_lib3 = __commonJS({
|
|
|
29586
29586
|
import dns from "node:dns";
|
|
29587
29587
|
import net2 from "node:net";
|
|
29588
29588
|
import fs9 from "node:fs";
|
|
29589
|
+
import os2 from "node:os";
|
|
29589
29590
|
import path7 from "node:path";
|
|
29590
29591
|
import readline from "node:readline";
|
|
29591
29592
|
|
|
@@ -30198,11 +30199,18 @@ var LoticsClient = class {
|
|
|
30198
30199
|
* `async function __workflow(): …` wrapper the server compiles inside, so the
|
|
30199
30200
|
* local typecheck mirrors the set-time verdict. Mirrors
|
|
30200
30201
|
* POST /v1/apps/{app_id}/workflows/{alias}/dts.
|
|
30202
|
+
*
|
|
30203
|
+
* `declaration` (the manifest's `{ inputs?, outputs? }`) is posted as the body
|
|
30204
|
+
* `{ declaration }` ONLY when the alias isn't `set` on the server yet — the
|
|
30205
|
+
* server then synthesizes the dts from the declared schemas instead of 400ing
|
|
30206
|
+
* "no workflow alias". A registered alias needs no declaration (the server's
|
|
30207
|
+
* own bound contract wins), so the field is omitted in that case.
|
|
30201
30208
|
*/
|
|
30202
|
-
async getAppWorkflowDts(app_id, alias) {
|
|
30209
|
+
async getAppWorkflowDts(app_id, alias, declaration) {
|
|
30203
30210
|
return this.request(
|
|
30204
30211
|
"POST",
|
|
30205
|
-
`/v1/apps/${encodeURIComponent(app_id)}/workflows/${encodeURIComponent(alias)}/dts
|
|
30212
|
+
`/v1/apps/${encodeURIComponent(app_id)}/workflows/${encodeURIComponent(alias)}/dts`,
|
|
30213
|
+
declaration ? { declaration } : void 0
|
|
30206
30214
|
);
|
|
30207
30215
|
}
|
|
30208
30216
|
/**
|
|
@@ -30224,6 +30232,25 @@ var LoticsClient = class {
|
|
|
30224
30232
|
if (!res.ok) await this.throwResponseError(res);
|
|
30225
30233
|
return res;
|
|
30226
30234
|
}
|
|
30235
|
+
/**
|
|
30236
|
+
* Continue a PARKED (`awaiting_input`) agent run with the user's answer to its
|
|
30237
|
+
* pending `ask_user_choice` — returns the RAW streamed continuation `Response`,
|
|
30238
|
+
* exactly like `appAgentRunStream`. Mirrors
|
|
30239
|
+
* POST /v1/apps/{app_id}/agent-runs/{run_id}/continue.
|
|
30240
|
+
*/
|
|
30241
|
+
async appAgentRunContinueStream(app_id, run_id, body, signal) {
|
|
30242
|
+
const res = await fetch(
|
|
30243
|
+
`${this.baseUrl}/v1/apps/${encodeURIComponent(app_id)}/agent-runs/${encodeURIComponent(run_id)}/continue`,
|
|
30244
|
+
{
|
|
30245
|
+
method: "POST",
|
|
30246
|
+
headers: { ...this.buildHeaders(), "Content-Type": "application/json" },
|
|
30247
|
+
body: JSON.stringify(body),
|
|
30248
|
+
signal
|
|
30249
|
+
}
|
|
30250
|
+
);
|
|
30251
|
+
if (!res.ok) await this.throwResponseError(res);
|
|
30252
|
+
return res;
|
|
30253
|
+
}
|
|
30227
30254
|
/**
|
|
30228
30255
|
* A session's app-agent run history, oldest-first (the run just started is the
|
|
30229
30256
|
* last, and its exact id is on the stream response's `x-app-agent-run-id`
|
|
@@ -31801,14 +31828,14 @@ function buildWrapperPage(args) {
|
|
|
31801
31828
|
// protocol expects (stream-chunk* -> stream-end). Mirrors the production
|
|
31802
31829
|
// host in frontend/features/app_ui/app_iframe_host.tsx.
|
|
31803
31830
|
const streamingRuns = {};
|
|
31804
|
-
async function handleAgentRun(id, payload) {
|
|
31831
|
+
async function handleAgentRun(id, op, payload) {
|
|
31805
31832
|
const controller = new AbortController();
|
|
31806
31833
|
streamingRuns[id] = controller;
|
|
31807
31834
|
try {
|
|
31808
31835
|
const res = await fetch("/_agent_run", {
|
|
31809
31836
|
method: "POST",
|
|
31810
31837
|
headers: { "content-type": "application/json" },
|
|
31811
|
-
body: JSON.stringify({ app_id: APP_ID, op:
|
|
31838
|
+
body: JSON.stringify({ app_id: APP_ID, op: op, payload: payload }),
|
|
31812
31839
|
signal: controller.signal,
|
|
31813
31840
|
});
|
|
31814
31841
|
if (!res.ok || !res.body) {
|
|
@@ -31816,6 +31843,9 @@ function buildWrapperPage(args) {
|
|
|
31816
31843
|
try { detail = JSON.parse(detail).message || detail; } catch (_) {}
|
|
31817
31844
|
throw new Error(detail || ("HTTP " + res.status));
|
|
31818
31845
|
}
|
|
31846
|
+
// Forward the run id (poll-recovery / cancel / continue key on it).
|
|
31847
|
+
const runId = res.headers.get("x-app-agent-run-id");
|
|
31848
|
+
if (runId) iframe.contentWindow.postMessage({ id: id, type: "run-id", runId: runId }, VITE_ORIGIN);
|
|
31819
31849
|
const reader = res.body.getReader();
|
|
31820
31850
|
const decoder = new TextDecoder();
|
|
31821
31851
|
for (;;) {
|
|
@@ -31849,7 +31879,7 @@ function buildWrapperPage(args) {
|
|
|
31849
31879
|
}
|
|
31850
31880
|
if (typeof msg.op !== "string") return;
|
|
31851
31881
|
// Streaming agent run \u2014 many messages back, not a single result.
|
|
31852
|
-
if (msg.op === "agentRun") { handleAgentRun(msg.id, msg.payload); return; }
|
|
31882
|
+
if (msg.op === "agentRun" || msg.op === "agentRunContinue") { handleAgentRun(msg.id, msg.op, msg.payload); return; }
|
|
31853
31883
|
const startedAt = performance.now();
|
|
31854
31884
|
try {
|
|
31855
31885
|
const data = msg.op === "upload"
|
|
@@ -32088,18 +32118,37 @@ async function startDevServer(args) {
|
|
|
32088
32118
|
});
|
|
32089
32119
|
try {
|
|
32090
32120
|
const body = await readJson(req);
|
|
32091
|
-
|
|
32092
|
-
if (
|
|
32093
|
-
|
|
32094
|
-
|
|
32095
|
-
|
|
32096
|
-
|
|
32097
|
-
|
|
32098
|
-
|
|
32099
|
-
|
|
32100
|
-
|
|
32121
|
+
let upstream;
|
|
32122
|
+
if (body.op === "agentRunContinue") {
|
|
32123
|
+
const p = body.payload ?? {};
|
|
32124
|
+
if (typeof p.run_id !== "string" || typeof p.tool_call_id !== "string" || typeof p.output !== "object" || p.output === null) {
|
|
32125
|
+
throw new Error("agentRunContinue payload must include `run_id`, `tool_call_id`, and `output`");
|
|
32126
|
+
}
|
|
32127
|
+
upstream = await args.client.appAgentRunContinueStream(
|
|
32128
|
+
body.app_id,
|
|
32129
|
+
p.run_id,
|
|
32130
|
+
{ tool_call_id: p.tool_call_id, output: p.output },
|
|
32131
|
+
ac.signal
|
|
32132
|
+
);
|
|
32133
|
+
} else {
|
|
32134
|
+
const p = body.payload ?? {};
|
|
32135
|
+
if (typeof p.alias !== "string" || typeof p.session_id !== "string") {
|
|
32136
|
+
throw new Error("agentRun payload must include `alias` and `session_id`");
|
|
32137
|
+
}
|
|
32138
|
+
upstream = await args.client.appAgentRunStream(
|
|
32139
|
+
body.app_id,
|
|
32140
|
+
p.alias,
|
|
32141
|
+
{ session_id: p.session_id, input: p.input ?? {} },
|
|
32142
|
+
ac.signal
|
|
32143
|
+
);
|
|
32144
|
+
}
|
|
32101
32145
|
if (!upstream.body) throw new Error("agent run returned no stream body");
|
|
32102
|
-
|
|
32146
|
+
const runId = upstream.headers.get("x-app-agent-run-id");
|
|
32147
|
+
res.writeHead(200, {
|
|
32148
|
+
"Content-Type": "text/event-stream",
|
|
32149
|
+
"Cache-Control": "no-cache, no-transform",
|
|
32150
|
+
...runId ? { "x-app-agent-run-id": runId } : {}
|
|
32151
|
+
});
|
|
32103
32152
|
const reader = upstream.body.getReader();
|
|
32104
32153
|
for (; ; ) {
|
|
32105
32154
|
const { value, done } = await reader.read();
|
|
@@ -32955,6 +33004,12 @@ var LOTICS_INCLUDE_GLOB = ".lotics/**/*";
|
|
|
32955
33004
|
var STALE_LOTICS_INCLUDES = /* @__PURE__ */ new Set([".lotics", "./.lotics", ".lotics/"]);
|
|
32956
33005
|
var FALLBACK_ENVELOPE_PREFIX = "async function __workflow(): Promise<__WorkflowReturn | void> {\n";
|
|
32957
33006
|
var FALLBACK_ENVELOPE_SUFFIX = "\n}";
|
|
33007
|
+
function toWorkflowDtsDeclaration(d) {
|
|
33008
|
+
return { inputs: d.inputs, outputs: d.outputs };
|
|
33009
|
+
}
|
|
33010
|
+
function isUnknownWorkflowAliasError(err2) {
|
|
33011
|
+
return err2 instanceof Error && /^400:/.test(err2.message) && /has no workflow alias/i.test(err2.message);
|
|
33012
|
+
}
|
|
32958
33013
|
function workflowFileHeader(alias) {
|
|
32959
33014
|
const refPath = path5.join("..", "..", WORKFLOW_GLOBALS_DIR, `${alias}.globals.d.ts`).split(path5.sep).join("/");
|
|
32960
33015
|
return `/// <reference path="${refPath}" />
|
|
@@ -33015,9 +33070,9 @@ function stripWorkflowHeader(content) {
|
|
|
33015
33070
|
}
|
|
33016
33071
|
return lines.slice(start + 1).join("\n").replace(/\s+$/, "");
|
|
33017
33072
|
}
|
|
33018
|
-
async function writeWorkflowFiles(client, projectDir, app_id,
|
|
33073
|
+
async function writeWorkflowFiles(client, projectDir, app_id, workflows) {
|
|
33019
33074
|
const written = [];
|
|
33020
|
-
for (const alias of
|
|
33075
|
+
for (const [alias, declaration] of Object.entries(workflows)) {
|
|
33021
33076
|
const res = await client.getAppWorkflow(app_id, alias);
|
|
33022
33077
|
const source = res.error || res.result === null || typeof res.result !== "object" ? null : res.result.source;
|
|
33023
33078
|
if (typeof source !== "string" || source.trim() === "") {
|
|
@@ -33026,15 +33081,26 @@ async function writeWorkflowFiles(client, projectDir, app_id, aliases) {
|
|
|
33026
33081
|
);
|
|
33027
33082
|
continue;
|
|
33028
33083
|
}
|
|
33029
|
-
const envelope = await fetchWorkflowGlobals(
|
|
33084
|
+
const envelope = await fetchWorkflowGlobals(
|
|
33085
|
+
client,
|
|
33086
|
+
projectDir,
|
|
33087
|
+
app_id,
|
|
33088
|
+
alias,
|
|
33089
|
+
toWorkflowDtsDeclaration(declaration)
|
|
33090
|
+
);
|
|
33030
33091
|
writeWorkflowFile(projectDir, alias, source, envelope);
|
|
33031
33092
|
written.push(alias);
|
|
33032
33093
|
}
|
|
33033
33094
|
return written;
|
|
33034
33095
|
}
|
|
33035
|
-
async function fetchWorkflowGlobals(client, projectDir, app_id, alias) {
|
|
33096
|
+
async function fetchWorkflowGlobals(client, projectDir, app_id, alias, declaration) {
|
|
33036
33097
|
try {
|
|
33037
|
-
const { dts, envelope_prefix, envelope_suffix } = await
|
|
33098
|
+
const { dts, envelope_prefix, envelope_suffix } = await fetchWorkflowDts(
|
|
33099
|
+
client,
|
|
33100
|
+
app_id,
|
|
33101
|
+
alias,
|
|
33102
|
+
declaration
|
|
33103
|
+
);
|
|
33038
33104
|
writeWorkflowGlobals(projectDir, alias, dts);
|
|
33039
33105
|
return { prefix: envelope_prefix, suffix: envelope_suffix };
|
|
33040
33106
|
} catch (err2) {
|
|
@@ -33044,6 +33110,16 @@ async function fetchWorkflowGlobals(client, projectDir, app_id, alias) {
|
|
|
33044
33110
|
return { prefix: FALLBACK_ENVELOPE_PREFIX, suffix: FALLBACK_ENVELOPE_SUFFIX };
|
|
33045
33111
|
}
|
|
33046
33112
|
}
|
|
33113
|
+
async function fetchWorkflowDts(client, app_id, alias, declaration) {
|
|
33114
|
+
try {
|
|
33115
|
+
return await client.getAppWorkflowDts(app_id, alias);
|
|
33116
|
+
} catch (err2) {
|
|
33117
|
+
if (declaration && isUnknownWorkflowAliasError(err2)) {
|
|
33118
|
+
return client.getAppWorkflowDts(app_id, alias, declaration);
|
|
33119
|
+
}
|
|
33120
|
+
throw err2;
|
|
33121
|
+
}
|
|
33122
|
+
}
|
|
33047
33123
|
function runTar(args, cwd) {
|
|
33048
33124
|
return new Promise((resolve2, reject2) => {
|
|
33049
33125
|
const proc = spawn2("tar", args, { cwd, stdio: ["ignore", "ignore", "pipe"] });
|
|
@@ -33090,10 +33166,37 @@ function readAppMeta(projectDir) {
|
|
|
33090
33166
|
capabilities: pkg2.lotics.capabilities
|
|
33091
33167
|
};
|
|
33092
33168
|
}
|
|
33169
|
+
var APP_META_KEYS = Object.keys({
|
|
33170
|
+
app_id: true,
|
|
33171
|
+
workspace_id: true,
|
|
33172
|
+
current_version_id: true,
|
|
33173
|
+
version_number: true,
|
|
33174
|
+
workflows: true,
|
|
33175
|
+
queries: true,
|
|
33176
|
+
agents: true,
|
|
33177
|
+
capabilities: true
|
|
33178
|
+
});
|
|
33093
33179
|
function writeAppMeta(projectDir, meta3) {
|
|
33094
33180
|
const pkgPath2 = path5.join(projectDir, "package.json");
|
|
33095
33181
|
const pkg2 = JSON.parse(fs4.readFileSync(pkgPath2, "utf-8"));
|
|
33096
|
-
pkg2.lotics
|
|
33182
|
+
const existing = pkg2.lotics && typeof pkg2.lotics === "object" ? pkg2.lotics : {};
|
|
33183
|
+
const preserved = {};
|
|
33184
|
+
for (const [key, value] of Object.entries(existing)) {
|
|
33185
|
+
if (!APP_META_KEYS.includes(key)) preserved[key] = value;
|
|
33186
|
+
}
|
|
33187
|
+
pkg2.lotics = { ...meta3, ...preserved };
|
|
33188
|
+
fs4.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
|
|
33189
|
+
}
|
|
33190
|
+
function writeWorkflowOutputs(projectDir, alias, outputs) {
|
|
33191
|
+
const pkgPath2 = path5.join(projectDir, "package.json");
|
|
33192
|
+
const pkg2 = JSON.parse(fs4.readFileSync(pkgPath2, "utf-8"));
|
|
33193
|
+
const declaration = pkg2.lotics?.workflows?.[alias];
|
|
33194
|
+
if (!declaration) {
|
|
33195
|
+
throw new Error(
|
|
33196
|
+
`package.json#lotics.workflows.${alias} disappeared before the derived-outputs write-back.`
|
|
33197
|
+
);
|
|
33198
|
+
}
|
|
33199
|
+
declaration.outputs = outputs;
|
|
33097
33200
|
fs4.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
|
|
33098
33201
|
}
|
|
33099
33202
|
function ensureAppTsconfig(projectDir) {
|
|
@@ -33228,19 +33331,29 @@ async function appCodegen(args) {
|
|
|
33228
33331
|
`\u26A0 Could not regenerate .lotics/app_fields.ts (${err2 instanceof Error ? err2.message : String(err2)}). Kept the existing file.`
|
|
33229
33332
|
);
|
|
33230
33333
|
}
|
|
33231
|
-
await refreshWorkflowGlobals(args.client, projectDir, meta3.app_id,
|
|
33334
|
+
await refreshWorkflowGlobals(args.client, projectDir, meta3.app_id, meta3.workflows ?? {});
|
|
33232
33335
|
}
|
|
33233
|
-
async function refreshWorkflowGlobals(client, projectDir, app_id,
|
|
33234
|
-
for (const alias of
|
|
33235
|
-
const
|
|
33236
|
-
|
|
33237
|
-
|
|
33238
|
-
|
|
33239
|
-
|
|
33240
|
-
|
|
33241
|
-
|
|
33336
|
+
async function refreshWorkflowGlobals(client, projectDir, app_id, workflows) {
|
|
33337
|
+
for (const [alias, declaration] of Object.entries(workflows)) {
|
|
33338
|
+
const refreshed = await refreshWorkflowTypes(
|
|
33339
|
+
client,
|
|
33340
|
+
projectDir,
|
|
33341
|
+
app_id,
|
|
33342
|
+
alias,
|
|
33343
|
+
toWorkflowDtsDeclaration(declaration)
|
|
33344
|
+
);
|
|
33345
|
+
if (refreshed) console.error(`Refreshed workflow types for ${alias}`);
|
|
33242
33346
|
}
|
|
33243
33347
|
}
|
|
33348
|
+
async function refreshWorkflowTypes(client, projectDir, app_id, alias, declaration) {
|
|
33349
|
+
const file2 = workflowFilePath(projectDir, alias);
|
|
33350
|
+
if (!fs4.existsSync(file2)) return false;
|
|
33351
|
+
const body = stripWorkflowHeader(fs4.readFileSync(file2, "utf-8"));
|
|
33352
|
+
if (body.trim() === "") return false;
|
|
33353
|
+
const envelope = await fetchWorkflowGlobals(client, projectDir, app_id, alias, declaration);
|
|
33354
|
+
writeWorkflowFile(projectDir, alias, body, envelope);
|
|
33355
|
+
return true;
|
|
33356
|
+
}
|
|
33244
33357
|
function stampPulledManifest(projectDir, args) {
|
|
33245
33358
|
writeAppMeta(projectDir, {
|
|
33246
33359
|
app_id: args.app_id,
|
|
@@ -33403,9 +33516,9 @@ async function appPull(client, args) {
|
|
|
33403
33516
|
queries: app.queries ?? {},
|
|
33404
33517
|
agents: app.agents ?? {}
|
|
33405
33518
|
});
|
|
33406
|
-
const
|
|
33407
|
-
if (
|
|
33408
|
-
const written = await writeWorkflowFiles(client, targetPath, app.id,
|
|
33519
|
+
const workflows = app.workflows ?? {};
|
|
33520
|
+
if (Object.keys(workflows).length > 0) {
|
|
33521
|
+
const written = await writeWorkflowFiles(client, targetPath, app.id, workflows);
|
|
33409
33522
|
if (written.length > 0) {
|
|
33410
33523
|
console.error(
|
|
33411
33524
|
`Wrote ${written.length} workflow ${written.length === 1 ? "body" : "bodies"} to ${WORKFLOWS_DIR}/ (${written.join(", ")})`
|
|
@@ -33807,8 +33920,19 @@ async function appWorkflowSet(client, args) {
|
|
|
33807
33920
|
const result = res.result ?? {};
|
|
33808
33921
|
const workflowId = typeof result.workflow_id === "string" ? result.workflow_id : "(unknown)";
|
|
33809
33922
|
console.error(`Set workflow "${args.alias}" \u2192 ${workflowId}`);
|
|
33810
|
-
|
|
33811
|
-
|
|
33923
|
+
const derivedOutputs = result.outputs !== null && typeof result.outputs === "object" && !Array.isArray(result.outputs) ? result.outputs : null;
|
|
33924
|
+
if (!declaration.outputs && derivedOutputs && Object.keys(derivedOutputs).length > 0) {
|
|
33925
|
+
writeWorkflowOutputs(projectDir, args.alias, derivedOutputs);
|
|
33926
|
+
console.error(
|
|
33927
|
+
` Wrote the derived result.data schema into package.json#lotics.workflows.${args.alias}.outputs.`
|
|
33928
|
+
);
|
|
33929
|
+
const refreshed = await refreshWorkflowTypes(client, projectDir, meta3.app_id, args.alias, {
|
|
33930
|
+
inputs: declaration.inputs,
|
|
33931
|
+
outputs: derivedOutputs
|
|
33932
|
+
});
|
|
33933
|
+
if (refreshed) console.error(` Refreshed workflow types for ${args.alias}.`);
|
|
33934
|
+
} else if (derivedOutputs) {
|
|
33935
|
+
console.error(` result.data schema: ${JSON.stringify(derivedOutputs)}`);
|
|
33812
33936
|
}
|
|
33813
33937
|
}
|
|
33814
33938
|
async function appQuerySet(client, args) {
|
|
@@ -33834,12 +33958,12 @@ async function appWorkflowPull(client) {
|
|
|
33834
33958
|
const projectDir = process.cwd();
|
|
33835
33959
|
const meta3 = readAppMeta(projectDir);
|
|
33836
33960
|
const app = await client.getApp(meta3.app_id);
|
|
33837
|
-
const
|
|
33838
|
-
if (
|
|
33961
|
+
const workflows = app.workflows ?? {};
|
|
33962
|
+
if (Object.keys(workflows).length === 0) {
|
|
33839
33963
|
console.error(`App ${meta3.app_id} has no bound workflows.`);
|
|
33840
33964
|
return;
|
|
33841
33965
|
}
|
|
33842
|
-
const written = await writeWorkflowFiles(client, projectDir, meta3.app_id,
|
|
33966
|
+
const written = await writeWorkflowFiles(client, projectDir, meta3.app_id, workflows);
|
|
33843
33967
|
console.error(
|
|
33844
33968
|
`Wrote ${written.length} workflow ${written.length === 1 ? "body" : "bodies"} to ${WORKFLOWS_DIR}/` + (written.length > 0 ? ` (${written.join(", ")})` : "")
|
|
33845
33969
|
);
|
|
@@ -47318,13 +47442,50 @@ function unmergeCellsCommand(workbook, sheetIndex, mergeRef) {
|
|
|
47318
47442
|
}
|
|
47319
47443
|
};
|
|
47320
47444
|
}
|
|
47445
|
+
function isInertCell(cell) {
|
|
47446
|
+
return (cell.value === null || cell.value === "") && cell.formula === void 0 && cell.styleIndex === 0 && (cell.originalXfIndex === void 0 || cell.originalXfIndex === 0) && (cell.numFmtCode === "General" || cell.numFmtCode === "") && cell.richText === void 0 && cell.error === void 0 && cell.content.type === "plain" && cell.content.text === "";
|
|
47447
|
+
}
|
|
47448
|
+
function clampInertCells(sheet) {
|
|
47449
|
+
let minRow = Infinity;
|
|
47450
|
+
let maxRow = 0;
|
|
47451
|
+
let minCol = Infinity;
|
|
47452
|
+
let maxCol = 0;
|
|
47453
|
+
let hasMeaningful = false;
|
|
47454
|
+
for (const [ref, cell] of sheet.cells) {
|
|
47455
|
+
if (isInertCell(cell)) continue;
|
|
47456
|
+
const rc = refToRowColSafe(ref);
|
|
47457
|
+
if (!rc) continue;
|
|
47458
|
+
hasMeaningful = true;
|
|
47459
|
+
if (rc.row < minRow) minRow = rc.row;
|
|
47460
|
+
if (rc.row > maxRow) maxRow = rc.row;
|
|
47461
|
+
if (rc.col < minCol) minCol = rc.col;
|
|
47462
|
+
if (rc.col > maxCol) maxCol = rc.col;
|
|
47463
|
+
}
|
|
47464
|
+
const pruned = /* @__PURE__ */ new Map();
|
|
47465
|
+
for (const [ref, cell] of sheet.cells) {
|
|
47466
|
+
if (!isInertCell(cell)) continue;
|
|
47467
|
+
if (!hasMeaningful) {
|
|
47468
|
+
pruned.set(ref, cell);
|
|
47469
|
+
continue;
|
|
47470
|
+
}
|
|
47471
|
+
const rc = refToRowColSafe(ref);
|
|
47472
|
+
if (!rc) continue;
|
|
47473
|
+
if (rc.row < minRow || rc.row > maxRow || rc.col < minCol || rc.col > maxCol) {
|
|
47474
|
+
pruned.set(ref, cell);
|
|
47475
|
+
}
|
|
47476
|
+
}
|
|
47477
|
+
for (const ref of pruned.keys()) sheet.cells.delete(ref);
|
|
47478
|
+
return pruned;
|
|
47479
|
+
}
|
|
47321
47480
|
function insertRowsCommand(workbook, sheetIndex, at, count) {
|
|
47322
47481
|
const sheet = workbook.sheets[sheetIndex];
|
|
47323
47482
|
let mergeSnapshot = [];
|
|
47483
|
+
let inertSnapshot = /* @__PURE__ */ new Map();
|
|
47324
47484
|
return {
|
|
47325
47485
|
description: `Insert ${count} row${count > 1 ? "s" : ""} at ${at}`,
|
|
47326
47486
|
execute() {
|
|
47327
47487
|
mergeSnapshot = [...sheet.mergedCells];
|
|
47488
|
+
inertSnapshot = clampInertCells(sheet);
|
|
47328
47489
|
const toMove = [];
|
|
47329
47490
|
for (const [ref] of sheet.cells) {
|
|
47330
47491
|
const rc = refToRowColSafe(ref);
|
|
@@ -47368,6 +47529,7 @@ function insertRowsCommand(workbook, sheetIndex, at, count) {
|
|
|
47368
47529
|
sheet.rowHeights.set(r - count, h);
|
|
47369
47530
|
}
|
|
47370
47531
|
for (let r = at; r < at + count; r++) sheet.rowHeights.delete(r);
|
|
47532
|
+
for (const [ref, cell] of inertSnapshot) sheet.cells.set(ref, cell);
|
|
47371
47533
|
workbook.emit({ type: "rows_deleted", sheet: sheetIndex, at, count });
|
|
47372
47534
|
}
|
|
47373
47535
|
};
|
|
@@ -47382,10 +47544,12 @@ function deleteRowsCommand(workbook, sheetIndex, at, count) {
|
|
|
47382
47544
|
}
|
|
47383
47545
|
}
|
|
47384
47546
|
let mergeSnapshot = [];
|
|
47547
|
+
let inertSnapshot = /* @__PURE__ */ new Map();
|
|
47385
47548
|
return {
|
|
47386
47549
|
description: `Delete ${count} row${count > 1 ? "s" : ""} at ${at}`,
|
|
47387
47550
|
execute() {
|
|
47388
47551
|
mergeSnapshot = [...sheet.mergedCells];
|
|
47552
|
+
inertSnapshot = clampInertCells(sheet);
|
|
47389
47553
|
for (const ref of snapshot.keys()) sheet.cells.delete(ref);
|
|
47390
47554
|
const toMove = [];
|
|
47391
47555
|
for (const [ref] of sheet.cells) {
|
|
@@ -47418,6 +47582,7 @@ function deleteRowsCommand(workbook, sheetIndex, at, count) {
|
|
|
47418
47582
|
for (const [ref, snap] of snapshot) {
|
|
47419
47583
|
sheet.set(ref, snap.value, sheet.styles?.get(snap.styleIndex), snap.numFmtCode, snap.formula);
|
|
47420
47584
|
}
|
|
47585
|
+
for (const [ref, cell] of inertSnapshot) sheet.cells.set(ref, cell);
|
|
47421
47586
|
workbook.emit({ type: "rows_inserted", sheet: sheetIndex, at, count });
|
|
47422
47587
|
}
|
|
47423
47588
|
};
|
|
@@ -47425,10 +47590,12 @@ function deleteRowsCommand(workbook, sheetIndex, at, count) {
|
|
|
47425
47590
|
function insertColsCommand(workbook, sheetIndex, at, count) {
|
|
47426
47591
|
const sheet = workbook.sheets[sheetIndex];
|
|
47427
47592
|
let mergeSnapshot = [];
|
|
47593
|
+
let inertSnapshot = /* @__PURE__ */ new Map();
|
|
47428
47594
|
return {
|
|
47429
47595
|
description: `Insert ${count} column${count > 1 ? "s" : ""} at ${at}`,
|
|
47430
47596
|
execute() {
|
|
47431
47597
|
mergeSnapshot = [...sheet.mergedCells];
|
|
47598
|
+
inertSnapshot = clampInertCells(sheet);
|
|
47432
47599
|
const toMove = [];
|
|
47433
47600
|
for (const [ref] of sheet.cells) {
|
|
47434
47601
|
const rc = refToRowColSafe(ref);
|
|
@@ -47472,6 +47639,7 @@ function insertColsCommand(workbook, sheetIndex, at, count) {
|
|
|
47472
47639
|
sheet.colWidths.set(c - count, w);
|
|
47473
47640
|
}
|
|
47474
47641
|
for (let c = at; c < at + count; c++) sheet.colWidths.delete(c);
|
|
47642
|
+
for (const [ref, cell] of inertSnapshot) sheet.cells.set(ref, cell);
|
|
47475
47643
|
workbook.emit({ type: "cols_deleted", sheet: sheetIndex, at, count });
|
|
47476
47644
|
}
|
|
47477
47645
|
};
|
|
@@ -47486,10 +47654,12 @@ function deleteColsCommand(workbook, sheetIndex, at, count) {
|
|
|
47486
47654
|
}
|
|
47487
47655
|
}
|
|
47488
47656
|
let mergeSnapshot = [];
|
|
47657
|
+
let inertSnapshot = /* @__PURE__ */ new Map();
|
|
47489
47658
|
return {
|
|
47490
47659
|
description: `Delete ${count} column${count > 1 ? "s" : ""} at ${at}`,
|
|
47491
47660
|
execute() {
|
|
47492
47661
|
mergeSnapshot = [...sheet.mergedCells];
|
|
47662
|
+
inertSnapshot = clampInertCells(sheet);
|
|
47493
47663
|
for (const ref of snapshot.keys()) sheet.cells.delete(ref);
|
|
47494
47664
|
const toMove = [];
|
|
47495
47665
|
for (const [ref] of sheet.cells) {
|
|
@@ -47522,6 +47692,7 @@ function deleteColsCommand(workbook, sheetIndex, at, count) {
|
|
|
47522
47692
|
for (const [ref, snap] of snapshot) {
|
|
47523
47693
|
sheet.set(ref, snap.value, sheet.styles?.get(snap.styleIndex), snap.numFmtCode, snap.formula);
|
|
47524
47694
|
}
|
|
47695
|
+
for (const [ref, cell] of inertSnapshot) sheet.cells.set(ref, cell);
|
|
47525
47696
|
workbook.emit({ type: "cols_inserted", sheet: sheetIndex, at, count });
|
|
47526
47697
|
}
|
|
47527
47698
|
};
|
|
@@ -47744,26 +47915,37 @@ function lazyEngine(workbook) {
|
|
|
47744
47915
|
}
|
|
47745
47916
|
};
|
|
47746
47917
|
}
|
|
47747
|
-
function readToJson(filePath) {
|
|
47918
|
+
function readToJson(filePath, filter2) {
|
|
47748
47919
|
const buffer = fs6.readFileSync(filePath);
|
|
47749
47920
|
const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
47750
47921
|
const parsed = parseExcelBuffer(arrayBuffer);
|
|
47922
|
+
let sheets = parsed.sheets;
|
|
47923
|
+
if (filter2?.sheet !== void 0) {
|
|
47924
|
+
const match = sheets.filter((s) => s.name === filter2.sheet);
|
|
47925
|
+
if (match.length === 0) {
|
|
47926
|
+
const names = parsed.sheets.map((s) => `"${s.name}"`).join(", ");
|
|
47927
|
+
fail(`Sheet not found: "${filter2.sheet}". Available: ${names}`);
|
|
47928
|
+
}
|
|
47929
|
+
sheets = match;
|
|
47930
|
+
}
|
|
47751
47931
|
return {
|
|
47752
47932
|
activeSheetIndex: parsed.activeSheetIndex,
|
|
47753
|
-
sheets:
|
|
47933
|
+
sheets: sheets.map((s) => ({
|
|
47754
47934
|
name: s.name,
|
|
47755
47935
|
totalRowCount: s.totalRowCount,
|
|
47756
47936
|
truncated: s.truncated,
|
|
47757
|
-
cells: cellsFromParsedRows(s.rows),
|
|
47937
|
+
cells: cellsFromParsedRows(s.rows, filter2?.range),
|
|
47758
47938
|
merges: s.mergedCells.map((m) => `${rowColToRef(m.startRow, m.startCol)}:${rowColToRef(m.endRow, m.endCol)}`),
|
|
47759
47939
|
freeze: s.freezePane ? { row: s.freezePane.frozenRows, col: s.freezePane.frozenCols } : void 0
|
|
47760
47940
|
}))
|
|
47761
47941
|
};
|
|
47762
47942
|
}
|
|
47763
|
-
function cellsFromParsedRows(rows) {
|
|
47943
|
+
function cellsFromParsedRows(rows, range) {
|
|
47764
47944
|
const out = {};
|
|
47765
47945
|
for (const row of rows) {
|
|
47946
|
+
if (range && (row.index < range.startRow || row.index > range.endRow)) continue;
|
|
47766
47947
|
for (const cell of row.cells) {
|
|
47948
|
+
if (range && (cell.column < range.startCol || cell.column > range.endCol)) continue;
|
|
47767
47949
|
const ref = rowColToRef(row.index, cell.column);
|
|
47768
47950
|
const value = cell.typedValue ?? cell.value;
|
|
47769
47951
|
const entry = { value };
|
|
@@ -47835,9 +48017,34 @@ function valueToInput(value) {
|
|
|
47835
48017
|
if (value === null || value === void 0) return "";
|
|
47836
48018
|
return String(value);
|
|
47837
48019
|
}
|
|
47838
|
-
function
|
|
47839
|
-
|
|
47840
|
-
|
|
48020
|
+
function readOptionFlag(rest, name) {
|
|
48021
|
+
const i2 = rest.indexOf(name);
|
|
48022
|
+
if (i2 < 0) return void 0;
|
|
48023
|
+
const val = rest[i2 + 1];
|
|
48024
|
+
if (val === void 0 || val.startsWith("--")) fail(`Missing value for ${name}`);
|
|
48025
|
+
return val;
|
|
48026
|
+
}
|
|
48027
|
+
function splitOptionalSheetRef(spec) {
|
|
48028
|
+
const idx = spec.indexOf("!");
|
|
48029
|
+
if (idx < 0) return { sheet: void 0, ref: spec };
|
|
48030
|
+
return { sheet: spec.slice(0, idx), ref: spec.slice(idx + 1) };
|
|
48031
|
+
}
|
|
48032
|
+
function xlsxRead(filePath, rest) {
|
|
48033
|
+
if (!filePath) fail("Usage: lotics xlsx read <file> [--sheet <name>] [--range <sheet>!<A1:G60>]");
|
|
48034
|
+
const rangeArg = readOptionFlag(rest, "--range");
|
|
48035
|
+
const sheetArg = readOptionFlag(rest, "--sheet");
|
|
48036
|
+
let filter2;
|
|
48037
|
+
if (rangeArg !== void 0) {
|
|
48038
|
+
const { sheet, ref } = splitOptionalSheetRef(rangeArg);
|
|
48039
|
+
const targetSheet = sheet ?? sheetArg;
|
|
48040
|
+
if (targetSheet === void 0) {
|
|
48041
|
+
fail("--range without a <sheet>! prefix requires --sheet <name>");
|
|
48042
|
+
}
|
|
48043
|
+
filter2 = { sheet: targetSheet, range: parseRange2(ref) };
|
|
48044
|
+
} else if (sheetArg !== void 0) {
|
|
48045
|
+
filter2 = { sheet: sheetArg };
|
|
48046
|
+
}
|
|
48047
|
+
const data = readToJson(filePath, filter2);
|
|
47841
48048
|
console.log(JSON.stringify(data, null, 2));
|
|
47842
48049
|
}
|
|
47843
48050
|
function xlsxWrite(filePath, json2) {
|
|
@@ -48074,7 +48281,10 @@ function printXlsxHelp() {
|
|
|
48074
48281
|
console.error(`Lotics xlsx commands \u2014 manipulate .xlsx files in place.
|
|
48075
48282
|
Uses Lotics' own xlsx engine; round-trips faithfully with the Lotics editor and templates.
|
|
48076
48283
|
|
|
48077
|
-
lotics xlsx read <file>
|
|
48284
|
+
lotics xlsx read <file> [--sheet <name>] [--range <sheet>!<A1:G60>]
|
|
48285
|
+
Dump file as JSON (sheets, cells, merges, freeze).
|
|
48286
|
+
--sheet limits to one sheet; --range to a cell window
|
|
48287
|
+
(its <sheet>! prefix is optional when --sheet is given)
|
|
48078
48288
|
lotics xlsx write <file> '<json>' Create .xlsx from {"sheets":[{"name","cells":{"A1":...}}]}
|
|
48079
48289
|
lotics xlsx set-cell <file> <sheet>!<ref> '<v>' Set cell value (prefix '=' for formula)
|
|
48080
48290
|
lotics xlsx clear-range <file> <sheet>!<range> Clear all cells in a range
|
|
@@ -48096,7 +48306,7 @@ Edit ops mutate the file atomically (temp file + rename).`);
|
|
|
48096
48306
|
async function runXlsxCommand(subcommand, toolArgs, restArgs) {
|
|
48097
48307
|
switch (subcommand) {
|
|
48098
48308
|
case "read":
|
|
48099
|
-
return xlsxRead(toolArgs);
|
|
48309
|
+
return xlsxRead(toolArgs, restArgs);
|
|
48100
48310
|
case "write":
|
|
48101
48311
|
return xlsxWrite(toolArgs, restArgs[0]);
|
|
48102
48312
|
case "set-cell":
|
|
@@ -68204,8 +68414,14 @@ async function cdpConnect(wsUrl) {
|
|
|
68204
68414
|
});
|
|
68205
68415
|
return { send, close: () => ws.close() };
|
|
68206
68416
|
}
|
|
68417
|
+
function isStoredFileId(target) {
|
|
68418
|
+
return /^fil_[A-Za-z0-9]+$/.test(target);
|
|
68419
|
+
}
|
|
68420
|
+
function defaultPreviewOutputPath(filename, cwd) {
|
|
68421
|
+
return join(cwd, basename(filename, extname(filename)) + ".png");
|
|
68422
|
+
}
|
|
68207
68423
|
async function runPreviewCommand(filePath, flags) {
|
|
68208
|
-
if (!filePath) fail2("Usage: lotics preview <file.docx|.xlsx> [
|
|
68424
|
+
if (!filePath) fail2("Usage: lotics preview <file.docx|.xlsx | fil_id> [-o <file.png>]");
|
|
68209
68425
|
const abs = resolve(filePath);
|
|
68210
68426
|
if (!existsSync(abs)) fail2(`File not found: ${abs}`);
|
|
68211
68427
|
const ext = extname(abs).toLowerCase();
|
|
@@ -68438,8 +68654,10 @@ COMMANDS
|
|
|
68438
68654
|
lotics file download <file_id> Download a file by ID (alias: lotics download)
|
|
68439
68655
|
lotics file download record <record_id> <field_key>
|
|
68440
68656
|
Download all files on a record file field
|
|
68441
|
-
lotics file preview <file> [-o png]
|
|
68442
|
-
|
|
68657
|
+
lotics file preview <file|fil_id> [-o png]
|
|
68658
|
+
Render a .docx/.xlsx to a PNG \u2014 a local path OR a
|
|
68659
|
+
stored file id (downloaded first). Frontend engines;
|
|
68660
|
+
needs a Chrome/Chromium on the machine
|
|
68443
68661
|
|
|
68444
68662
|
FLAGS
|
|
68445
68663
|
--json Full JSON output (default is human-readable text)
|
|
@@ -68799,6 +69017,18 @@ async function main() {
|
|
|
68799
69017
|
return;
|
|
68800
69018
|
}
|
|
68801
69019
|
if (command === "preview") {
|
|
69020
|
+
if (subcommand && isStoredFileId(subcommand)) {
|
|
69021
|
+
const { client: client2 } = requireClient(flags);
|
|
69022
|
+
const tmpDir = fs9.mkdtempSync(path7.join(os2.tmpdir(), "lotics-preview-"));
|
|
69023
|
+
try {
|
|
69024
|
+
const { path: localPath, filename } = await client2.downloadFileById(subcommand, tmpDir);
|
|
69025
|
+
const output = flags.output ?? defaultPreviewOutputPath(filename, process.cwd());
|
|
69026
|
+
await runPreviewCommand(localPath, { output });
|
|
69027
|
+
} finally {
|
|
69028
|
+
fs9.rmSync(tmpDir, { recursive: true, force: true });
|
|
69029
|
+
}
|
|
69030
|
+
return;
|
|
69031
|
+
}
|
|
68802
69032
|
await runPreviewCommand(subcommand, flags);
|
|
68803
69033
|
return;
|
|
68804
69034
|
}
|
package/dist/src/client.d.ts
CHANGED
|
@@ -883,8 +883,17 @@ export declare class LoticsClient {
|
|
|
883
883
|
* `async function __workflow(): …` wrapper the server compiles inside, so the
|
|
884
884
|
* local typecheck mirrors the set-time verdict. Mirrors
|
|
885
885
|
* POST /v1/apps/{app_id}/workflows/{alias}/dts.
|
|
886
|
+
*
|
|
887
|
+
* `declaration` (the manifest's `{ inputs?, outputs? }`) is posted as the body
|
|
888
|
+
* `{ declaration }` ONLY when the alias isn't `set` on the server yet — the
|
|
889
|
+
* server then synthesizes the dts from the declared schemas instead of 400ing
|
|
890
|
+
* "no workflow alias". A registered alias needs no declaration (the server's
|
|
891
|
+
* own bound contract wins), so the field is omitted in that case.
|
|
886
892
|
*/
|
|
887
|
-
getAppWorkflowDts(app_id: string, alias: string
|
|
893
|
+
getAppWorkflowDts(app_id: string, alias: string, declaration?: {
|
|
894
|
+
inputs?: Record<string, unknown>;
|
|
895
|
+
outputs?: Record<string, unknown>;
|
|
896
|
+
}): Promise<{
|
|
888
897
|
dts: string;
|
|
889
898
|
envelope_prefix: string;
|
|
890
899
|
envelope_suffix: string;
|
|
@@ -899,6 +908,16 @@ export declare class LoticsClient {
|
|
|
899
908
|
session_id: string;
|
|
900
909
|
input: Record<string, unknown>;
|
|
901
910
|
}, signal?: AbortSignal): Promise<Response>;
|
|
911
|
+
/**
|
|
912
|
+
* Continue a PARKED (`awaiting_input`) agent run with the user's answer to its
|
|
913
|
+
* pending `ask_user_choice` — returns the RAW streamed continuation `Response`,
|
|
914
|
+
* exactly like `appAgentRunStream`. Mirrors
|
|
915
|
+
* POST /v1/apps/{app_id}/agent-runs/{run_id}/continue.
|
|
916
|
+
*/
|
|
917
|
+
appAgentRunContinueStream(app_id: string, run_id: string, body: {
|
|
918
|
+
tool_call_id: string;
|
|
919
|
+
output: Record<string, unknown>;
|
|
920
|
+
}, signal?: AbortSignal): Promise<Response>;
|
|
902
921
|
/**
|
|
903
922
|
* A session's app-agent run history, oldest-first (the run just started is the
|
|
904
923
|
* last, and its exact id is on the stream response's `x-app-agent-run-id`
|
package/dist/src/client.js
CHANGED
|
@@ -608,9 +608,15 @@ export class LoticsClient {
|
|
|
608
608
|
* `async function __workflow(): …` wrapper the server compiles inside, so the
|
|
609
609
|
* local typecheck mirrors the set-time verdict. Mirrors
|
|
610
610
|
* POST /v1/apps/{app_id}/workflows/{alias}/dts.
|
|
611
|
+
*
|
|
612
|
+
* `declaration` (the manifest's `{ inputs?, outputs? }`) is posted as the body
|
|
613
|
+
* `{ declaration }` ONLY when the alias isn't `set` on the server yet — the
|
|
614
|
+
* server then synthesizes the dts from the declared schemas instead of 400ing
|
|
615
|
+
* "no workflow alias". A registered alias needs no declaration (the server's
|
|
616
|
+
* own bound contract wins), so the field is omitted in that case.
|
|
611
617
|
*/
|
|
612
|
-
async getAppWorkflowDts(app_id, alias) {
|
|
613
|
-
return this.request("POST", `/v1/apps/${encodeURIComponent(app_id)}/workflows/${encodeURIComponent(alias)}/dts
|
|
618
|
+
async getAppWorkflowDts(app_id, alias, declaration) {
|
|
619
|
+
return this.request("POST", `/v1/apps/${encodeURIComponent(app_id)}/workflows/${encodeURIComponent(alias)}/dts`, declaration ? { declaration } : undefined);
|
|
614
620
|
}
|
|
615
621
|
/**
|
|
616
622
|
* Open a streaming agent run and return the RAW streamed `Response` (the
|
|
@@ -629,6 +635,23 @@ export class LoticsClient {
|
|
|
629
635
|
await this.throwResponseError(res);
|
|
630
636
|
return res;
|
|
631
637
|
}
|
|
638
|
+
/**
|
|
639
|
+
* Continue a PARKED (`awaiting_input`) agent run with the user's answer to its
|
|
640
|
+
* pending `ask_user_choice` — returns the RAW streamed continuation `Response`,
|
|
641
|
+
* exactly like `appAgentRunStream`. Mirrors
|
|
642
|
+
* POST /v1/apps/{app_id}/agent-runs/{run_id}/continue.
|
|
643
|
+
*/
|
|
644
|
+
async appAgentRunContinueStream(app_id, run_id, body, signal) {
|
|
645
|
+
const res = await fetch(`${this.baseUrl}/v1/apps/${encodeURIComponent(app_id)}/agent-runs/${encodeURIComponent(run_id)}/continue`, {
|
|
646
|
+
method: "POST",
|
|
647
|
+
headers: { ...this.buildHeaders(), "Content-Type": "application/json" },
|
|
648
|
+
body: JSON.stringify(body),
|
|
649
|
+
signal,
|
|
650
|
+
});
|
|
651
|
+
if (!res.ok)
|
|
652
|
+
await this.throwResponseError(res);
|
|
653
|
+
return res;
|
|
654
|
+
}
|
|
632
655
|
/**
|
|
633
656
|
* A session's app-agent run history, oldest-first (the run just started is the
|
|
634
657
|
* last, and its exact id is on the stream response's `x-app-agent-run-id`
|