@lotics/cli 0.93.0 → 0.94.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli.js +332 -53
- package/dist/src/client.d.ts +42 -1
- package/dist/src/client.js +40 -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`
|
|
@@ -30236,6 +30263,27 @@ var LoticsClient = class {
|
|
|
30236
30263
|
`/v1/apps/${encodeURIComponent(app_id)}/agent-runs?session_id=${encodeURIComponent(session_id)}`
|
|
30237
30264
|
);
|
|
30238
30265
|
}
|
|
30266
|
+
/**
|
|
30267
|
+
* A single run by id — the poll read a client follows after its stream drops
|
|
30268
|
+
* (a parked `awaiting_input` row carries `pending_interactive` so the question
|
|
30269
|
+
* survives reconnection). Mirrors GET /v1/apps/{app_id}/agent-runs/{run_id}.
|
|
30270
|
+
*/
|
|
30271
|
+
async getAgentRun(app_id, run_id) {
|
|
30272
|
+
return this.request(
|
|
30273
|
+
"GET",
|
|
30274
|
+
`/v1/apps/${encodeURIComponent(app_id)}/agent-runs/${encodeURIComponent(run_id)}`
|
|
30275
|
+
);
|
|
30276
|
+
}
|
|
30277
|
+
/**
|
|
30278
|
+
* Request cancellation of an in-flight (or parked) run. Mirrors
|
|
30279
|
+
* POST /v1/apps/{app_id}/agent-runs/{run_id}/cancel.
|
|
30280
|
+
*/
|
|
30281
|
+
async cancelAgentRun(app_id, run_id) {
|
|
30282
|
+
return this.request(
|
|
30283
|
+
"POST",
|
|
30284
|
+
`/v1/apps/${encodeURIComponent(app_id)}/agent-runs/${encodeURIComponent(run_id)}/cancel`
|
|
30285
|
+
);
|
|
30286
|
+
}
|
|
30239
30287
|
/**
|
|
30240
30288
|
* Mint a presigned URL for uploading a file into an app. Mirrors
|
|
30241
30289
|
* POST /v1/apps/{app_id}/files/upload-url.
|
|
@@ -30921,7 +30969,11 @@ export default defineConfig({
|
|
|
30921
30969
|
},
|
|
30922
30970
|
build: {
|
|
30923
30971
|
outDir: "dist",
|
|
30924
|
-
|
|
30972
|
+
// No source maps in the production build: they ship the original source
|
|
30973
|
+
// (comments, logic) as a static asset, and a password-gated app is served
|
|
30974
|
+
// openly at the asset layer \u2014 the map would expose the very source the gate
|
|
30975
|
+
// exists to protect. Source maps aren't needed to run the app.
|
|
30976
|
+
sourcemap: false,
|
|
30925
30977
|
// Top-level await (package projects' generated .lotics/app_fields.ts
|
|
30926
30978
|
// resolves the installation binding at module load) needs es2022 \u2014 Vite's
|
|
30927
30979
|
// default 'modules' baseline is es2020 and esbuild hard-fails TLA there.
|
|
@@ -31459,6 +31511,9 @@ var SUPPORTED_OPS = /* @__PURE__ */ new Set([
|
|
|
31459
31511
|
"binding",
|
|
31460
31512
|
"upload_url",
|
|
31461
31513
|
"upload_complete",
|
|
31514
|
+
"agentRuns",
|
|
31515
|
+
"agentRun.get",
|
|
31516
|
+
"agentRun.cancel",
|
|
31462
31517
|
"comments.list",
|
|
31463
31518
|
"comments.create",
|
|
31464
31519
|
"comments.update",
|
|
@@ -31543,6 +31598,27 @@ async function dispatchRpc(client, body, opts) {
|
|
|
31543
31598
|
// Comment ops mirror the production iframe-host's `handleCommentRpc` return
|
|
31544
31599
|
// shapes so the SDK hooks behave identically in dev. App authority + the
|
|
31545
31600
|
// `comments` capability + tenant floor are enforced server-side.
|
|
31601
|
+
case "agentRuns": {
|
|
31602
|
+
const p = body.payload;
|
|
31603
|
+
if (!p || typeof p.session_id !== "string") {
|
|
31604
|
+
throw new Error("agentRuns payload must include session_id");
|
|
31605
|
+
}
|
|
31606
|
+
return client.listAgentRuns(body.app_id, p.session_id);
|
|
31607
|
+
}
|
|
31608
|
+
case "agentRun.get": {
|
|
31609
|
+
const p = body.payload;
|
|
31610
|
+
if (!p || typeof p.run_id !== "string") {
|
|
31611
|
+
throw new Error("agentRun.get payload must include run_id");
|
|
31612
|
+
}
|
|
31613
|
+
return client.getAgentRun(body.app_id, p.run_id);
|
|
31614
|
+
}
|
|
31615
|
+
case "agentRun.cancel": {
|
|
31616
|
+
const p = body.payload;
|
|
31617
|
+
if (!p || typeof p.run_id !== "string") {
|
|
31618
|
+
throw new Error("agentRun.cancel payload must include run_id");
|
|
31619
|
+
}
|
|
31620
|
+
return client.cancelAgentRun(body.app_id, p.run_id);
|
|
31621
|
+
}
|
|
31546
31622
|
case "comments.list": {
|
|
31547
31623
|
const p = body.payload;
|
|
31548
31624
|
if (!p || typeof p.record_id !== "string") {
|
|
@@ -31801,14 +31877,14 @@ function buildWrapperPage(args) {
|
|
|
31801
31877
|
// protocol expects (stream-chunk* -> stream-end). Mirrors the production
|
|
31802
31878
|
// host in frontend/features/app_ui/app_iframe_host.tsx.
|
|
31803
31879
|
const streamingRuns = {};
|
|
31804
|
-
async function handleAgentRun(id, payload) {
|
|
31880
|
+
async function handleAgentRun(id, op, payload) {
|
|
31805
31881
|
const controller = new AbortController();
|
|
31806
31882
|
streamingRuns[id] = controller;
|
|
31807
31883
|
try {
|
|
31808
31884
|
const res = await fetch("/_agent_run", {
|
|
31809
31885
|
method: "POST",
|
|
31810
31886
|
headers: { "content-type": "application/json" },
|
|
31811
|
-
body: JSON.stringify({ app_id: APP_ID, op:
|
|
31887
|
+
body: JSON.stringify({ app_id: APP_ID, op: op, payload: payload }),
|
|
31812
31888
|
signal: controller.signal,
|
|
31813
31889
|
});
|
|
31814
31890
|
if (!res.ok || !res.body) {
|
|
@@ -31816,6 +31892,9 @@ function buildWrapperPage(args) {
|
|
|
31816
31892
|
try { detail = JSON.parse(detail).message || detail; } catch (_) {}
|
|
31817
31893
|
throw new Error(detail || ("HTTP " + res.status));
|
|
31818
31894
|
}
|
|
31895
|
+
// Forward the run id (poll-recovery / cancel / continue key on it).
|
|
31896
|
+
const runId = res.headers.get("x-app-agent-run-id");
|
|
31897
|
+
if (runId) iframe.contentWindow.postMessage({ id: id, type: "run-id", runId: runId }, VITE_ORIGIN);
|
|
31819
31898
|
const reader = res.body.getReader();
|
|
31820
31899
|
const decoder = new TextDecoder();
|
|
31821
31900
|
for (;;) {
|
|
@@ -31849,7 +31928,7 @@ function buildWrapperPage(args) {
|
|
|
31849
31928
|
}
|
|
31850
31929
|
if (typeof msg.op !== "string") return;
|
|
31851
31930
|
// Streaming agent run \u2014 many messages back, not a single result.
|
|
31852
|
-
if (msg.op === "agentRun") { handleAgentRun(msg.id, msg.payload); return; }
|
|
31931
|
+
if (msg.op === "agentRun" || msg.op === "agentRunContinue") { handleAgentRun(msg.id, msg.op, msg.payload); return; }
|
|
31853
31932
|
const startedAt = performance.now();
|
|
31854
31933
|
try {
|
|
31855
31934
|
const data = msg.op === "upload"
|
|
@@ -32088,18 +32167,37 @@ async function startDevServer(args) {
|
|
|
32088
32167
|
});
|
|
32089
32168
|
try {
|
|
32090
32169
|
const body = await readJson(req);
|
|
32091
|
-
|
|
32092
|
-
if (
|
|
32093
|
-
|
|
32094
|
-
|
|
32095
|
-
|
|
32096
|
-
|
|
32097
|
-
|
|
32098
|
-
|
|
32099
|
-
|
|
32100
|
-
|
|
32170
|
+
let upstream;
|
|
32171
|
+
if (body.op === "agentRunContinue") {
|
|
32172
|
+
const p = body.payload ?? {};
|
|
32173
|
+
if (typeof p.run_id !== "string" || typeof p.tool_call_id !== "string" || typeof p.output !== "object" || p.output === null) {
|
|
32174
|
+
throw new Error("agentRunContinue payload must include `run_id`, `tool_call_id`, and `output`");
|
|
32175
|
+
}
|
|
32176
|
+
upstream = await args.client.appAgentRunContinueStream(
|
|
32177
|
+
body.app_id,
|
|
32178
|
+
p.run_id,
|
|
32179
|
+
{ tool_call_id: p.tool_call_id, output: p.output },
|
|
32180
|
+
ac.signal
|
|
32181
|
+
);
|
|
32182
|
+
} else {
|
|
32183
|
+
const p = body.payload ?? {};
|
|
32184
|
+
if (typeof p.alias !== "string" || typeof p.session_id !== "string") {
|
|
32185
|
+
throw new Error("agentRun payload must include `alias` and `session_id`");
|
|
32186
|
+
}
|
|
32187
|
+
upstream = await args.client.appAgentRunStream(
|
|
32188
|
+
body.app_id,
|
|
32189
|
+
p.alias,
|
|
32190
|
+
{ session_id: p.session_id, input: p.input ?? {} },
|
|
32191
|
+
ac.signal
|
|
32192
|
+
);
|
|
32193
|
+
}
|
|
32101
32194
|
if (!upstream.body) throw new Error("agent run returned no stream body");
|
|
32102
|
-
|
|
32195
|
+
const runId = upstream.headers.get("x-app-agent-run-id");
|
|
32196
|
+
res.writeHead(200, {
|
|
32197
|
+
"Content-Type": "text/event-stream",
|
|
32198
|
+
"Cache-Control": "no-cache, no-transform",
|
|
32199
|
+
...runId ? { "x-app-agent-run-id": runId } : {}
|
|
32200
|
+
});
|
|
32103
32201
|
const reader = upstream.body.getReader();
|
|
32104
32202
|
for (; ; ) {
|
|
32105
32203
|
const { value, done } = await reader.read();
|
|
@@ -32955,6 +33053,12 @@ var LOTICS_INCLUDE_GLOB = ".lotics/**/*";
|
|
|
32955
33053
|
var STALE_LOTICS_INCLUDES = /* @__PURE__ */ new Set([".lotics", "./.lotics", ".lotics/"]);
|
|
32956
33054
|
var FALLBACK_ENVELOPE_PREFIX = "async function __workflow(): Promise<__WorkflowReturn | void> {\n";
|
|
32957
33055
|
var FALLBACK_ENVELOPE_SUFFIX = "\n}";
|
|
33056
|
+
function toWorkflowDtsDeclaration(d) {
|
|
33057
|
+
return { inputs: d.inputs, outputs: d.outputs };
|
|
33058
|
+
}
|
|
33059
|
+
function isUnknownWorkflowAliasError(err2) {
|
|
33060
|
+
return err2 instanceof Error && /^400:/.test(err2.message) && /has no workflow alias/i.test(err2.message);
|
|
33061
|
+
}
|
|
32958
33062
|
function workflowFileHeader(alias) {
|
|
32959
33063
|
const refPath = path5.join("..", "..", WORKFLOW_GLOBALS_DIR, `${alias}.globals.d.ts`).split(path5.sep).join("/");
|
|
32960
33064
|
return `/// <reference path="${refPath}" />
|
|
@@ -33015,9 +33119,9 @@ function stripWorkflowHeader(content) {
|
|
|
33015
33119
|
}
|
|
33016
33120
|
return lines.slice(start + 1).join("\n").replace(/\s+$/, "");
|
|
33017
33121
|
}
|
|
33018
|
-
async function writeWorkflowFiles(client, projectDir, app_id,
|
|
33122
|
+
async function writeWorkflowFiles(client, projectDir, app_id, workflows) {
|
|
33019
33123
|
const written = [];
|
|
33020
|
-
for (const alias of
|
|
33124
|
+
for (const [alias, declaration] of Object.entries(workflows)) {
|
|
33021
33125
|
const res = await client.getAppWorkflow(app_id, alias);
|
|
33022
33126
|
const source = res.error || res.result === null || typeof res.result !== "object" ? null : res.result.source;
|
|
33023
33127
|
if (typeof source !== "string" || source.trim() === "") {
|
|
@@ -33026,15 +33130,26 @@ async function writeWorkflowFiles(client, projectDir, app_id, aliases) {
|
|
|
33026
33130
|
);
|
|
33027
33131
|
continue;
|
|
33028
33132
|
}
|
|
33029
|
-
const envelope = await fetchWorkflowGlobals(
|
|
33133
|
+
const envelope = await fetchWorkflowGlobals(
|
|
33134
|
+
client,
|
|
33135
|
+
projectDir,
|
|
33136
|
+
app_id,
|
|
33137
|
+
alias,
|
|
33138
|
+
toWorkflowDtsDeclaration(declaration)
|
|
33139
|
+
);
|
|
33030
33140
|
writeWorkflowFile(projectDir, alias, source, envelope);
|
|
33031
33141
|
written.push(alias);
|
|
33032
33142
|
}
|
|
33033
33143
|
return written;
|
|
33034
33144
|
}
|
|
33035
|
-
async function fetchWorkflowGlobals(client, projectDir, app_id, alias) {
|
|
33145
|
+
async function fetchWorkflowGlobals(client, projectDir, app_id, alias, declaration) {
|
|
33036
33146
|
try {
|
|
33037
|
-
const { dts, envelope_prefix, envelope_suffix } = await
|
|
33147
|
+
const { dts, envelope_prefix, envelope_suffix } = await fetchWorkflowDts(
|
|
33148
|
+
client,
|
|
33149
|
+
app_id,
|
|
33150
|
+
alias,
|
|
33151
|
+
declaration
|
|
33152
|
+
);
|
|
33038
33153
|
writeWorkflowGlobals(projectDir, alias, dts);
|
|
33039
33154
|
return { prefix: envelope_prefix, suffix: envelope_suffix };
|
|
33040
33155
|
} catch (err2) {
|
|
@@ -33044,6 +33159,16 @@ async function fetchWorkflowGlobals(client, projectDir, app_id, alias) {
|
|
|
33044
33159
|
return { prefix: FALLBACK_ENVELOPE_PREFIX, suffix: FALLBACK_ENVELOPE_SUFFIX };
|
|
33045
33160
|
}
|
|
33046
33161
|
}
|
|
33162
|
+
async function fetchWorkflowDts(client, app_id, alias, declaration) {
|
|
33163
|
+
try {
|
|
33164
|
+
return await client.getAppWorkflowDts(app_id, alias);
|
|
33165
|
+
} catch (err2) {
|
|
33166
|
+
if (declaration && isUnknownWorkflowAliasError(err2)) {
|
|
33167
|
+
return client.getAppWorkflowDts(app_id, alias, declaration);
|
|
33168
|
+
}
|
|
33169
|
+
throw err2;
|
|
33170
|
+
}
|
|
33171
|
+
}
|
|
33047
33172
|
function runTar(args, cwd) {
|
|
33048
33173
|
return new Promise((resolve2, reject2) => {
|
|
33049
33174
|
const proc = spawn2("tar", args, { cwd, stdio: ["ignore", "ignore", "pipe"] });
|
|
@@ -33090,10 +33215,37 @@ function readAppMeta(projectDir) {
|
|
|
33090
33215
|
capabilities: pkg2.lotics.capabilities
|
|
33091
33216
|
};
|
|
33092
33217
|
}
|
|
33218
|
+
var APP_META_KEYS = Object.keys({
|
|
33219
|
+
app_id: true,
|
|
33220
|
+
workspace_id: true,
|
|
33221
|
+
current_version_id: true,
|
|
33222
|
+
version_number: true,
|
|
33223
|
+
workflows: true,
|
|
33224
|
+
queries: true,
|
|
33225
|
+
agents: true,
|
|
33226
|
+
capabilities: true
|
|
33227
|
+
});
|
|
33093
33228
|
function writeAppMeta(projectDir, meta3) {
|
|
33094
33229
|
const pkgPath2 = path5.join(projectDir, "package.json");
|
|
33095
33230
|
const pkg2 = JSON.parse(fs4.readFileSync(pkgPath2, "utf-8"));
|
|
33096
|
-
pkg2.lotics
|
|
33231
|
+
const existing = pkg2.lotics && typeof pkg2.lotics === "object" ? pkg2.lotics : {};
|
|
33232
|
+
const preserved = {};
|
|
33233
|
+
for (const [key, value] of Object.entries(existing)) {
|
|
33234
|
+
if (!APP_META_KEYS.includes(key)) preserved[key] = value;
|
|
33235
|
+
}
|
|
33236
|
+
pkg2.lotics = { ...meta3, ...preserved };
|
|
33237
|
+
fs4.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
|
|
33238
|
+
}
|
|
33239
|
+
function writeWorkflowOutputs(projectDir, alias, outputs) {
|
|
33240
|
+
const pkgPath2 = path5.join(projectDir, "package.json");
|
|
33241
|
+
const pkg2 = JSON.parse(fs4.readFileSync(pkgPath2, "utf-8"));
|
|
33242
|
+
const declaration = pkg2.lotics?.workflows?.[alias];
|
|
33243
|
+
if (!declaration) {
|
|
33244
|
+
throw new Error(
|
|
33245
|
+
`package.json#lotics.workflows.${alias} disappeared before the derived-outputs write-back.`
|
|
33246
|
+
);
|
|
33247
|
+
}
|
|
33248
|
+
declaration.outputs = outputs;
|
|
33097
33249
|
fs4.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
|
|
33098
33250
|
}
|
|
33099
33251
|
function ensureAppTsconfig(projectDir) {
|
|
@@ -33228,19 +33380,29 @@ async function appCodegen(args) {
|
|
|
33228
33380
|
`\u26A0 Could not regenerate .lotics/app_fields.ts (${err2 instanceof Error ? err2.message : String(err2)}). Kept the existing file.`
|
|
33229
33381
|
);
|
|
33230
33382
|
}
|
|
33231
|
-
await refreshWorkflowGlobals(args.client, projectDir, meta3.app_id,
|
|
33383
|
+
await refreshWorkflowGlobals(args.client, projectDir, meta3.app_id, meta3.workflows ?? {});
|
|
33232
33384
|
}
|
|
33233
|
-
async function refreshWorkflowGlobals(client, projectDir, app_id,
|
|
33234
|
-
for (const alias of
|
|
33235
|
-
const
|
|
33236
|
-
|
|
33237
|
-
|
|
33238
|
-
|
|
33239
|
-
|
|
33240
|
-
|
|
33241
|
-
|
|
33385
|
+
async function refreshWorkflowGlobals(client, projectDir, app_id, workflows) {
|
|
33386
|
+
for (const [alias, declaration] of Object.entries(workflows)) {
|
|
33387
|
+
const refreshed = await refreshWorkflowTypes(
|
|
33388
|
+
client,
|
|
33389
|
+
projectDir,
|
|
33390
|
+
app_id,
|
|
33391
|
+
alias,
|
|
33392
|
+
toWorkflowDtsDeclaration(declaration)
|
|
33393
|
+
);
|
|
33394
|
+
if (refreshed) console.error(`Refreshed workflow types for ${alias}`);
|
|
33242
33395
|
}
|
|
33243
33396
|
}
|
|
33397
|
+
async function refreshWorkflowTypes(client, projectDir, app_id, alias, declaration) {
|
|
33398
|
+
const file2 = workflowFilePath(projectDir, alias);
|
|
33399
|
+
if (!fs4.existsSync(file2)) return false;
|
|
33400
|
+
const body = stripWorkflowHeader(fs4.readFileSync(file2, "utf-8"));
|
|
33401
|
+
if (body.trim() === "") return false;
|
|
33402
|
+
const envelope = await fetchWorkflowGlobals(client, projectDir, app_id, alias, declaration);
|
|
33403
|
+
writeWorkflowFile(projectDir, alias, body, envelope);
|
|
33404
|
+
return true;
|
|
33405
|
+
}
|
|
33244
33406
|
function stampPulledManifest(projectDir, args) {
|
|
33245
33407
|
writeAppMeta(projectDir, {
|
|
33246
33408
|
app_id: args.app_id,
|
|
@@ -33403,9 +33565,9 @@ async function appPull(client, args) {
|
|
|
33403
33565
|
queries: app.queries ?? {},
|
|
33404
33566
|
agents: app.agents ?? {}
|
|
33405
33567
|
});
|
|
33406
|
-
const
|
|
33407
|
-
if (
|
|
33408
|
-
const written = await writeWorkflowFiles(client, targetPath, app.id,
|
|
33568
|
+
const workflows = app.workflows ?? {};
|
|
33569
|
+
if (Object.keys(workflows).length > 0) {
|
|
33570
|
+
const written = await writeWorkflowFiles(client, targetPath, app.id, workflows);
|
|
33409
33571
|
if (written.length > 0) {
|
|
33410
33572
|
console.error(
|
|
33411
33573
|
`Wrote ${written.length} workflow ${written.length === 1 ? "body" : "bodies"} to ${WORKFLOWS_DIR}/ (${written.join(", ")})`
|
|
@@ -33807,8 +33969,19 @@ async function appWorkflowSet(client, args) {
|
|
|
33807
33969
|
const result = res.result ?? {};
|
|
33808
33970
|
const workflowId = typeof result.workflow_id === "string" ? result.workflow_id : "(unknown)";
|
|
33809
33971
|
console.error(`Set workflow "${args.alias}" \u2192 ${workflowId}`);
|
|
33810
|
-
|
|
33811
|
-
|
|
33972
|
+
const derivedOutputs = result.outputs !== null && typeof result.outputs === "object" && !Array.isArray(result.outputs) ? result.outputs : null;
|
|
33973
|
+
if (!declaration.outputs && derivedOutputs && Object.keys(derivedOutputs).length > 0) {
|
|
33974
|
+
writeWorkflowOutputs(projectDir, args.alias, derivedOutputs);
|
|
33975
|
+
console.error(
|
|
33976
|
+
` Wrote the derived result.data schema into package.json#lotics.workflows.${args.alias}.outputs.`
|
|
33977
|
+
);
|
|
33978
|
+
const refreshed = await refreshWorkflowTypes(client, projectDir, meta3.app_id, args.alias, {
|
|
33979
|
+
inputs: declaration.inputs,
|
|
33980
|
+
outputs: derivedOutputs
|
|
33981
|
+
});
|
|
33982
|
+
if (refreshed) console.error(` Refreshed workflow types for ${args.alias}.`);
|
|
33983
|
+
} else if (derivedOutputs) {
|
|
33984
|
+
console.error(` result.data schema: ${JSON.stringify(derivedOutputs)}`);
|
|
33812
33985
|
}
|
|
33813
33986
|
}
|
|
33814
33987
|
async function appQuerySet(client, args) {
|
|
@@ -33834,12 +34007,12 @@ async function appWorkflowPull(client) {
|
|
|
33834
34007
|
const projectDir = process.cwd();
|
|
33835
34008
|
const meta3 = readAppMeta(projectDir);
|
|
33836
34009
|
const app = await client.getApp(meta3.app_id);
|
|
33837
|
-
const
|
|
33838
|
-
if (
|
|
34010
|
+
const workflows = app.workflows ?? {};
|
|
34011
|
+
if (Object.keys(workflows).length === 0) {
|
|
33839
34012
|
console.error(`App ${meta3.app_id} has no bound workflows.`);
|
|
33840
34013
|
return;
|
|
33841
34014
|
}
|
|
33842
|
-
const written = await writeWorkflowFiles(client, projectDir, meta3.app_id,
|
|
34015
|
+
const written = await writeWorkflowFiles(client, projectDir, meta3.app_id, workflows);
|
|
33843
34016
|
console.error(
|
|
33844
34017
|
`Wrote ${written.length} workflow ${written.length === 1 ? "body" : "bodies"} to ${WORKFLOWS_DIR}/` + (written.length > 0 ? ` (${written.join(", ")})` : "")
|
|
33845
34018
|
);
|
|
@@ -47318,13 +47491,50 @@ function unmergeCellsCommand(workbook, sheetIndex, mergeRef) {
|
|
|
47318
47491
|
}
|
|
47319
47492
|
};
|
|
47320
47493
|
}
|
|
47494
|
+
function isInertCell(cell) {
|
|
47495
|
+
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 === "";
|
|
47496
|
+
}
|
|
47497
|
+
function clampInertCells(sheet) {
|
|
47498
|
+
let minRow = Infinity;
|
|
47499
|
+
let maxRow = 0;
|
|
47500
|
+
let minCol = Infinity;
|
|
47501
|
+
let maxCol = 0;
|
|
47502
|
+
let hasMeaningful = false;
|
|
47503
|
+
for (const [ref, cell] of sheet.cells) {
|
|
47504
|
+
if (isInertCell(cell)) continue;
|
|
47505
|
+
const rc = refToRowColSafe(ref);
|
|
47506
|
+
if (!rc) continue;
|
|
47507
|
+
hasMeaningful = true;
|
|
47508
|
+
if (rc.row < minRow) minRow = rc.row;
|
|
47509
|
+
if (rc.row > maxRow) maxRow = rc.row;
|
|
47510
|
+
if (rc.col < minCol) minCol = rc.col;
|
|
47511
|
+
if (rc.col > maxCol) maxCol = rc.col;
|
|
47512
|
+
}
|
|
47513
|
+
const pruned = /* @__PURE__ */ new Map();
|
|
47514
|
+
for (const [ref, cell] of sheet.cells) {
|
|
47515
|
+
if (!isInertCell(cell)) continue;
|
|
47516
|
+
if (!hasMeaningful) {
|
|
47517
|
+
pruned.set(ref, cell);
|
|
47518
|
+
continue;
|
|
47519
|
+
}
|
|
47520
|
+
const rc = refToRowColSafe(ref);
|
|
47521
|
+
if (!rc) continue;
|
|
47522
|
+
if (rc.row < minRow || rc.row > maxRow || rc.col < minCol || rc.col > maxCol) {
|
|
47523
|
+
pruned.set(ref, cell);
|
|
47524
|
+
}
|
|
47525
|
+
}
|
|
47526
|
+
for (const ref of pruned.keys()) sheet.cells.delete(ref);
|
|
47527
|
+
return pruned;
|
|
47528
|
+
}
|
|
47321
47529
|
function insertRowsCommand(workbook, sheetIndex, at, count) {
|
|
47322
47530
|
const sheet = workbook.sheets[sheetIndex];
|
|
47323
47531
|
let mergeSnapshot = [];
|
|
47532
|
+
let inertSnapshot = /* @__PURE__ */ new Map();
|
|
47324
47533
|
return {
|
|
47325
47534
|
description: `Insert ${count} row${count > 1 ? "s" : ""} at ${at}`,
|
|
47326
47535
|
execute() {
|
|
47327
47536
|
mergeSnapshot = [...sheet.mergedCells];
|
|
47537
|
+
inertSnapshot = clampInertCells(sheet);
|
|
47328
47538
|
const toMove = [];
|
|
47329
47539
|
for (const [ref] of sheet.cells) {
|
|
47330
47540
|
const rc = refToRowColSafe(ref);
|
|
@@ -47368,6 +47578,7 @@ function insertRowsCommand(workbook, sheetIndex, at, count) {
|
|
|
47368
47578
|
sheet.rowHeights.set(r - count, h);
|
|
47369
47579
|
}
|
|
47370
47580
|
for (let r = at; r < at + count; r++) sheet.rowHeights.delete(r);
|
|
47581
|
+
for (const [ref, cell] of inertSnapshot) sheet.cells.set(ref, cell);
|
|
47371
47582
|
workbook.emit({ type: "rows_deleted", sheet: sheetIndex, at, count });
|
|
47372
47583
|
}
|
|
47373
47584
|
};
|
|
@@ -47382,10 +47593,12 @@ function deleteRowsCommand(workbook, sheetIndex, at, count) {
|
|
|
47382
47593
|
}
|
|
47383
47594
|
}
|
|
47384
47595
|
let mergeSnapshot = [];
|
|
47596
|
+
let inertSnapshot = /* @__PURE__ */ new Map();
|
|
47385
47597
|
return {
|
|
47386
47598
|
description: `Delete ${count} row${count > 1 ? "s" : ""} at ${at}`,
|
|
47387
47599
|
execute() {
|
|
47388
47600
|
mergeSnapshot = [...sheet.mergedCells];
|
|
47601
|
+
inertSnapshot = clampInertCells(sheet);
|
|
47389
47602
|
for (const ref of snapshot.keys()) sheet.cells.delete(ref);
|
|
47390
47603
|
const toMove = [];
|
|
47391
47604
|
for (const [ref] of sheet.cells) {
|
|
@@ -47418,6 +47631,7 @@ function deleteRowsCommand(workbook, sheetIndex, at, count) {
|
|
|
47418
47631
|
for (const [ref, snap] of snapshot) {
|
|
47419
47632
|
sheet.set(ref, snap.value, sheet.styles?.get(snap.styleIndex), snap.numFmtCode, snap.formula);
|
|
47420
47633
|
}
|
|
47634
|
+
for (const [ref, cell] of inertSnapshot) sheet.cells.set(ref, cell);
|
|
47421
47635
|
workbook.emit({ type: "rows_inserted", sheet: sheetIndex, at, count });
|
|
47422
47636
|
}
|
|
47423
47637
|
};
|
|
@@ -47425,10 +47639,12 @@ function deleteRowsCommand(workbook, sheetIndex, at, count) {
|
|
|
47425
47639
|
function insertColsCommand(workbook, sheetIndex, at, count) {
|
|
47426
47640
|
const sheet = workbook.sheets[sheetIndex];
|
|
47427
47641
|
let mergeSnapshot = [];
|
|
47642
|
+
let inertSnapshot = /* @__PURE__ */ new Map();
|
|
47428
47643
|
return {
|
|
47429
47644
|
description: `Insert ${count} column${count > 1 ? "s" : ""} at ${at}`,
|
|
47430
47645
|
execute() {
|
|
47431
47646
|
mergeSnapshot = [...sheet.mergedCells];
|
|
47647
|
+
inertSnapshot = clampInertCells(sheet);
|
|
47432
47648
|
const toMove = [];
|
|
47433
47649
|
for (const [ref] of sheet.cells) {
|
|
47434
47650
|
const rc = refToRowColSafe(ref);
|
|
@@ -47472,6 +47688,7 @@ function insertColsCommand(workbook, sheetIndex, at, count) {
|
|
|
47472
47688
|
sheet.colWidths.set(c - count, w);
|
|
47473
47689
|
}
|
|
47474
47690
|
for (let c = at; c < at + count; c++) sheet.colWidths.delete(c);
|
|
47691
|
+
for (const [ref, cell] of inertSnapshot) sheet.cells.set(ref, cell);
|
|
47475
47692
|
workbook.emit({ type: "cols_deleted", sheet: sheetIndex, at, count });
|
|
47476
47693
|
}
|
|
47477
47694
|
};
|
|
@@ -47486,10 +47703,12 @@ function deleteColsCommand(workbook, sheetIndex, at, count) {
|
|
|
47486
47703
|
}
|
|
47487
47704
|
}
|
|
47488
47705
|
let mergeSnapshot = [];
|
|
47706
|
+
let inertSnapshot = /* @__PURE__ */ new Map();
|
|
47489
47707
|
return {
|
|
47490
47708
|
description: `Delete ${count} column${count > 1 ? "s" : ""} at ${at}`,
|
|
47491
47709
|
execute() {
|
|
47492
47710
|
mergeSnapshot = [...sheet.mergedCells];
|
|
47711
|
+
inertSnapshot = clampInertCells(sheet);
|
|
47493
47712
|
for (const ref of snapshot.keys()) sheet.cells.delete(ref);
|
|
47494
47713
|
const toMove = [];
|
|
47495
47714
|
for (const [ref] of sheet.cells) {
|
|
@@ -47522,6 +47741,7 @@ function deleteColsCommand(workbook, sheetIndex, at, count) {
|
|
|
47522
47741
|
for (const [ref, snap] of snapshot) {
|
|
47523
47742
|
sheet.set(ref, snap.value, sheet.styles?.get(snap.styleIndex), snap.numFmtCode, snap.formula);
|
|
47524
47743
|
}
|
|
47744
|
+
for (const [ref, cell] of inertSnapshot) sheet.cells.set(ref, cell);
|
|
47525
47745
|
workbook.emit({ type: "cols_inserted", sheet: sheetIndex, at, count });
|
|
47526
47746
|
}
|
|
47527
47747
|
};
|
|
@@ -47744,26 +47964,37 @@ function lazyEngine(workbook) {
|
|
|
47744
47964
|
}
|
|
47745
47965
|
};
|
|
47746
47966
|
}
|
|
47747
|
-
function readToJson(filePath) {
|
|
47967
|
+
function readToJson(filePath, filter2) {
|
|
47748
47968
|
const buffer = fs6.readFileSync(filePath);
|
|
47749
47969
|
const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
47750
47970
|
const parsed = parseExcelBuffer(arrayBuffer);
|
|
47971
|
+
let sheets = parsed.sheets;
|
|
47972
|
+
if (filter2?.sheet !== void 0) {
|
|
47973
|
+
const match = sheets.filter((s) => s.name === filter2.sheet);
|
|
47974
|
+
if (match.length === 0) {
|
|
47975
|
+
const names = parsed.sheets.map((s) => `"${s.name}"`).join(", ");
|
|
47976
|
+
fail(`Sheet not found: "${filter2.sheet}". Available: ${names}`);
|
|
47977
|
+
}
|
|
47978
|
+
sheets = match;
|
|
47979
|
+
}
|
|
47751
47980
|
return {
|
|
47752
47981
|
activeSheetIndex: parsed.activeSheetIndex,
|
|
47753
|
-
sheets:
|
|
47982
|
+
sheets: sheets.map((s) => ({
|
|
47754
47983
|
name: s.name,
|
|
47755
47984
|
totalRowCount: s.totalRowCount,
|
|
47756
47985
|
truncated: s.truncated,
|
|
47757
|
-
cells: cellsFromParsedRows(s.rows),
|
|
47986
|
+
cells: cellsFromParsedRows(s.rows, filter2?.range),
|
|
47758
47987
|
merges: s.mergedCells.map((m) => `${rowColToRef(m.startRow, m.startCol)}:${rowColToRef(m.endRow, m.endCol)}`),
|
|
47759
47988
|
freeze: s.freezePane ? { row: s.freezePane.frozenRows, col: s.freezePane.frozenCols } : void 0
|
|
47760
47989
|
}))
|
|
47761
47990
|
};
|
|
47762
47991
|
}
|
|
47763
|
-
function cellsFromParsedRows(rows) {
|
|
47992
|
+
function cellsFromParsedRows(rows, range) {
|
|
47764
47993
|
const out = {};
|
|
47765
47994
|
for (const row of rows) {
|
|
47995
|
+
if (range && (row.index < range.startRow || row.index > range.endRow)) continue;
|
|
47766
47996
|
for (const cell of row.cells) {
|
|
47997
|
+
if (range && (cell.column < range.startCol || cell.column > range.endCol)) continue;
|
|
47767
47998
|
const ref = rowColToRef(row.index, cell.column);
|
|
47768
47999
|
const value = cell.typedValue ?? cell.value;
|
|
47769
48000
|
const entry = { value };
|
|
@@ -47835,9 +48066,34 @@ function valueToInput(value) {
|
|
|
47835
48066
|
if (value === null || value === void 0) return "";
|
|
47836
48067
|
return String(value);
|
|
47837
48068
|
}
|
|
47838
|
-
function
|
|
47839
|
-
|
|
47840
|
-
|
|
48069
|
+
function readOptionFlag(rest, name) {
|
|
48070
|
+
const i2 = rest.indexOf(name);
|
|
48071
|
+
if (i2 < 0) return void 0;
|
|
48072
|
+
const val = rest[i2 + 1];
|
|
48073
|
+
if (val === void 0 || val.startsWith("--")) fail(`Missing value for ${name}`);
|
|
48074
|
+
return val;
|
|
48075
|
+
}
|
|
48076
|
+
function splitOptionalSheetRef(spec) {
|
|
48077
|
+
const idx = spec.indexOf("!");
|
|
48078
|
+
if (idx < 0) return { sheet: void 0, ref: spec };
|
|
48079
|
+
return { sheet: spec.slice(0, idx), ref: spec.slice(idx + 1) };
|
|
48080
|
+
}
|
|
48081
|
+
function xlsxRead(filePath, rest) {
|
|
48082
|
+
if (!filePath) fail("Usage: lotics xlsx read <file> [--sheet <name>] [--range <sheet>!<A1:G60>]");
|
|
48083
|
+
const rangeArg = readOptionFlag(rest, "--range");
|
|
48084
|
+
const sheetArg = readOptionFlag(rest, "--sheet");
|
|
48085
|
+
let filter2;
|
|
48086
|
+
if (rangeArg !== void 0) {
|
|
48087
|
+
const { sheet, ref } = splitOptionalSheetRef(rangeArg);
|
|
48088
|
+
const targetSheet = sheet ?? sheetArg;
|
|
48089
|
+
if (targetSheet === void 0) {
|
|
48090
|
+
fail("--range without a <sheet>! prefix requires --sheet <name>");
|
|
48091
|
+
}
|
|
48092
|
+
filter2 = { sheet: targetSheet, range: parseRange2(ref) };
|
|
48093
|
+
} else if (sheetArg !== void 0) {
|
|
48094
|
+
filter2 = { sheet: sheetArg };
|
|
48095
|
+
}
|
|
48096
|
+
const data = readToJson(filePath, filter2);
|
|
47841
48097
|
console.log(JSON.stringify(data, null, 2));
|
|
47842
48098
|
}
|
|
47843
48099
|
function xlsxWrite(filePath, json2) {
|
|
@@ -48074,7 +48330,10 @@ function printXlsxHelp() {
|
|
|
48074
48330
|
console.error(`Lotics xlsx commands \u2014 manipulate .xlsx files in place.
|
|
48075
48331
|
Uses Lotics' own xlsx engine; round-trips faithfully with the Lotics editor and templates.
|
|
48076
48332
|
|
|
48077
|
-
lotics xlsx read <file>
|
|
48333
|
+
lotics xlsx read <file> [--sheet <name>] [--range <sheet>!<A1:G60>]
|
|
48334
|
+
Dump file as JSON (sheets, cells, merges, freeze).
|
|
48335
|
+
--sheet limits to one sheet; --range to a cell window
|
|
48336
|
+
(its <sheet>! prefix is optional when --sheet is given)
|
|
48078
48337
|
lotics xlsx write <file> '<json>' Create .xlsx from {"sheets":[{"name","cells":{"A1":...}}]}
|
|
48079
48338
|
lotics xlsx set-cell <file> <sheet>!<ref> '<v>' Set cell value (prefix '=' for formula)
|
|
48080
48339
|
lotics xlsx clear-range <file> <sheet>!<range> Clear all cells in a range
|
|
@@ -48096,7 +48355,7 @@ Edit ops mutate the file atomically (temp file + rename).`);
|
|
|
48096
48355
|
async function runXlsxCommand(subcommand, toolArgs, restArgs) {
|
|
48097
48356
|
switch (subcommand) {
|
|
48098
48357
|
case "read":
|
|
48099
|
-
return xlsxRead(toolArgs);
|
|
48358
|
+
return xlsxRead(toolArgs, restArgs);
|
|
48100
48359
|
case "write":
|
|
48101
48360
|
return xlsxWrite(toolArgs, restArgs[0]);
|
|
48102
48361
|
case "set-cell":
|
|
@@ -68204,8 +68463,14 @@ async function cdpConnect(wsUrl) {
|
|
|
68204
68463
|
});
|
|
68205
68464
|
return { send, close: () => ws.close() };
|
|
68206
68465
|
}
|
|
68466
|
+
function isStoredFileId(target) {
|
|
68467
|
+
return /^fil_[A-Za-z0-9]+$/.test(target);
|
|
68468
|
+
}
|
|
68469
|
+
function defaultPreviewOutputPath(filename, cwd) {
|
|
68470
|
+
return join(cwd, basename(filename, extname(filename)) + ".png");
|
|
68471
|
+
}
|
|
68207
68472
|
async function runPreviewCommand(filePath, flags) {
|
|
68208
|
-
if (!filePath) fail2("Usage: lotics preview <file.docx|.xlsx> [
|
|
68473
|
+
if (!filePath) fail2("Usage: lotics preview <file.docx|.xlsx | fil_id> [-o <file.png>]");
|
|
68209
68474
|
const abs = resolve(filePath);
|
|
68210
68475
|
if (!existsSync(abs)) fail2(`File not found: ${abs}`);
|
|
68211
68476
|
const ext = extname(abs).toLowerCase();
|
|
@@ -68438,8 +68703,10 @@ COMMANDS
|
|
|
68438
68703
|
lotics file download <file_id> Download a file by ID (alias: lotics download)
|
|
68439
68704
|
lotics file download record <record_id> <field_key>
|
|
68440
68705
|
Download all files on a record file field
|
|
68441
|
-
lotics file preview <file> [-o png]
|
|
68442
|
-
|
|
68706
|
+
lotics file preview <file|fil_id> [-o png]
|
|
68707
|
+
Render a .docx/.xlsx to a PNG \u2014 a local path OR a
|
|
68708
|
+
stored file id (downloaded first). Frontend engines;
|
|
68709
|
+
needs a Chrome/Chromium on the machine
|
|
68443
68710
|
|
|
68444
68711
|
FLAGS
|
|
68445
68712
|
--json Full JSON output (default is human-readable text)
|
|
@@ -68799,6 +69066,18 @@ async function main() {
|
|
|
68799
69066
|
return;
|
|
68800
69067
|
}
|
|
68801
69068
|
if (command === "preview") {
|
|
69069
|
+
if (subcommand && isStoredFileId(subcommand)) {
|
|
69070
|
+
const { client: client2 } = requireClient(flags);
|
|
69071
|
+
const tmpDir = fs9.mkdtempSync(path7.join(os2.tmpdir(), "lotics-preview-"));
|
|
69072
|
+
try {
|
|
69073
|
+
const { path: localPath, filename } = await client2.downloadFileById(subcommand, tmpDir);
|
|
69074
|
+
const output = flags.output ?? defaultPreviewOutputPath(filename, process.cwd());
|
|
69075
|
+
await runPreviewCommand(localPath, { output });
|
|
69076
|
+
} finally {
|
|
69077
|
+
fs9.rmSync(tmpDir, { recursive: true, force: true });
|
|
69078
|
+
}
|
|
69079
|
+
return;
|
|
69080
|
+
}
|
|
68802
69081
|
await runPreviewCommand(subcommand, flags);
|
|
68803
69082
|
return;
|
|
68804
69083
|
}
|
package/dist/src/client.d.ts
CHANGED
|
@@ -74,6 +74,13 @@ export interface AppAgentRunSummary {
|
|
|
74
74
|
triggered_by_member_id: string | null;
|
|
75
75
|
started_at: string;
|
|
76
76
|
completed_at: string | null;
|
|
77
|
+
/** Single-run GET only, while `status` is "awaiting_input": the pending ask
|
|
78
|
+
* derived from the transcript — what /continue answers. */
|
|
79
|
+
pending_interactive?: {
|
|
80
|
+
tool_call_id: string;
|
|
81
|
+
tool_name: string;
|
|
82
|
+
input: Record<string, unknown>;
|
|
83
|
+
};
|
|
77
84
|
}
|
|
78
85
|
export interface ToolInfo {
|
|
79
86
|
name: string;
|
|
@@ -883,8 +890,17 @@ export declare class LoticsClient {
|
|
|
883
890
|
* `async function __workflow(): …` wrapper the server compiles inside, so the
|
|
884
891
|
* local typecheck mirrors the set-time verdict. Mirrors
|
|
885
892
|
* POST /v1/apps/{app_id}/workflows/{alias}/dts.
|
|
893
|
+
*
|
|
894
|
+
* `declaration` (the manifest's `{ inputs?, outputs? }`) is posted as the body
|
|
895
|
+
* `{ declaration }` ONLY when the alias isn't `set` on the server yet — the
|
|
896
|
+
* server then synthesizes the dts from the declared schemas instead of 400ing
|
|
897
|
+
* "no workflow alias". A registered alias needs no declaration (the server's
|
|
898
|
+
* own bound contract wins), so the field is omitted in that case.
|
|
886
899
|
*/
|
|
887
|
-
getAppWorkflowDts(app_id: string, alias: string
|
|
900
|
+
getAppWorkflowDts(app_id: string, alias: string, declaration?: {
|
|
901
|
+
inputs?: Record<string, unknown>;
|
|
902
|
+
outputs?: Record<string, unknown>;
|
|
903
|
+
}): Promise<{
|
|
888
904
|
dts: string;
|
|
889
905
|
envelope_prefix: string;
|
|
890
906
|
envelope_suffix: string;
|
|
@@ -899,6 +915,16 @@ export declare class LoticsClient {
|
|
|
899
915
|
session_id: string;
|
|
900
916
|
input: Record<string, unknown>;
|
|
901
917
|
}, signal?: AbortSignal): Promise<Response>;
|
|
918
|
+
/**
|
|
919
|
+
* Continue a PARKED (`awaiting_input`) agent run with the user's answer to its
|
|
920
|
+
* pending `ask_user_choice` — returns the RAW streamed continuation `Response`,
|
|
921
|
+
* exactly like `appAgentRunStream`. Mirrors
|
|
922
|
+
* POST /v1/apps/{app_id}/agent-runs/{run_id}/continue.
|
|
923
|
+
*/
|
|
924
|
+
appAgentRunContinueStream(app_id: string, run_id: string, body: {
|
|
925
|
+
tool_call_id: string;
|
|
926
|
+
output: Record<string, unknown>;
|
|
927
|
+
}, signal?: AbortSignal): Promise<Response>;
|
|
902
928
|
/**
|
|
903
929
|
* A session's app-agent run history, oldest-first (the run just started is the
|
|
904
930
|
* last, and its exact id is on the stream response's `x-app-agent-run-id`
|
|
@@ -908,6 +934,21 @@ export declare class LoticsClient {
|
|
|
908
934
|
listAgentRuns(app_id: string, session_id: string): Promise<{
|
|
909
935
|
runs: AppAgentRunSummary[];
|
|
910
936
|
}>;
|
|
937
|
+
/**
|
|
938
|
+
* A single run by id — the poll read a client follows after its stream drops
|
|
939
|
+
* (a parked `awaiting_input` row carries `pending_interactive` so the question
|
|
940
|
+
* survives reconnection). Mirrors GET /v1/apps/{app_id}/agent-runs/{run_id}.
|
|
941
|
+
*/
|
|
942
|
+
getAgentRun(app_id: string, run_id: string): Promise<{
|
|
943
|
+
run: AppAgentRunSummary;
|
|
944
|
+
}>;
|
|
945
|
+
/**
|
|
946
|
+
* Request cancellation of an in-flight (or parked) run. Mirrors
|
|
947
|
+
* POST /v1/apps/{app_id}/agent-runs/{run_id}/cancel.
|
|
948
|
+
*/
|
|
949
|
+
cancelAgentRun(app_id: string, run_id: string): Promise<{
|
|
950
|
+
ok: true;
|
|
951
|
+
}>;
|
|
911
952
|
/**
|
|
912
953
|
* Mint a presigned URL for uploading a file into an app. Mirrors
|
|
913
954
|
* POST /v1/apps/{app_id}/files/upload-url.
|
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`
|
|
@@ -638,6 +661,21 @@ export class LoticsClient {
|
|
|
638
661
|
async listAgentRuns(app_id, session_id) {
|
|
639
662
|
return this.request("GET", `/v1/apps/${encodeURIComponent(app_id)}/agent-runs?session_id=${encodeURIComponent(session_id)}`);
|
|
640
663
|
}
|
|
664
|
+
/**
|
|
665
|
+
* A single run by id — the poll read a client follows after its stream drops
|
|
666
|
+
* (a parked `awaiting_input` row carries `pending_interactive` so the question
|
|
667
|
+
* survives reconnection). Mirrors GET /v1/apps/{app_id}/agent-runs/{run_id}.
|
|
668
|
+
*/
|
|
669
|
+
async getAgentRun(app_id, run_id) {
|
|
670
|
+
return this.request("GET", `/v1/apps/${encodeURIComponent(app_id)}/agent-runs/${encodeURIComponent(run_id)}`);
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Request cancellation of an in-flight (or parked) run. Mirrors
|
|
674
|
+
* POST /v1/apps/{app_id}/agent-runs/{run_id}/cancel.
|
|
675
|
+
*/
|
|
676
|
+
async cancelAgentRun(app_id, run_id) {
|
|
677
|
+
return this.request("POST", `/v1/apps/${encodeURIComponent(app_id)}/agent-runs/${encodeURIComponent(run_id)}/cancel`);
|
|
678
|
+
}
|
|
641
679
|
/**
|
|
642
680
|
* Mint a presigned URL for uploading a file into an app. Mirrors
|
|
643
681
|
* POST /v1/apps/{app_id}/files/upload-url.
|