@lotics/cli 0.184.0 → 0.186.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 +228 -31
- package/dist/src/client.d.ts +1 -0
- package/dist/src/client.js +1032 -891
- package/docs/cli_reference.md +2 -2
- package/package.json +1 -1
- package/dist/src/invocation.js +0 -142
package/dist/src/cli.js
CHANGED
|
@@ -44182,6 +44182,66 @@ function transportErrorMessage(status, parsed) {
|
|
|
44182
44182
|
return status < 500 || authored ? jsonMessage : gatewayErrorMessage(status);
|
|
44183
44183
|
}
|
|
44184
44184
|
|
|
44185
|
+
// ../shared/src/multipart_parts.ts
|
|
44186
|
+
var DEFAULT_MAX_RETRIES = 5;
|
|
44187
|
+
var MAX_RETRY_DELAY_MS = 15e3;
|
|
44188
|
+
var TIMEOUT_BASE_MS = 3e4;
|
|
44189
|
+
var TIMEOUT_PER_MB_MS = 15e3;
|
|
44190
|
+
function partTimeoutMs(partSizeBytes) {
|
|
44191
|
+
return TIMEOUT_BASE_MS + Math.ceil(partSizeBytes / (1024 * 1024)) * TIMEOUT_PER_MB_MS;
|
|
44192
|
+
}
|
|
44193
|
+
var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
44194
|
+
var PermanentPartError = class extends Error {
|
|
44195
|
+
};
|
|
44196
|
+
function isRetryableStatus(status) {
|
|
44197
|
+
return status >= 500 || status === 408 || status === 429;
|
|
44198
|
+
}
|
|
44199
|
+
function partErrorFor(partNumber, status) {
|
|
44200
|
+
const message2 = `part ${partNumber} rejected by storage (${status})`;
|
|
44201
|
+
return isRetryableStatus(status) ? new Error(message2) : new PermanentPartError(message2);
|
|
44202
|
+
}
|
|
44203
|
+
async function uploadParts(options) {
|
|
44204
|
+
const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
44205
|
+
const concurrency = Math.max(1, options.concurrency ?? 1);
|
|
44206
|
+
const done = [...options.alreadyUploaded ?? []];
|
|
44207
|
+
const finished = new Set(done.map((part) => part.part_number));
|
|
44208
|
+
const queue = options.parts.filter((part) => !finished.has(part.part_number));
|
|
44209
|
+
const uploadOne = async (part) => {
|
|
44210
|
+
const bytes = options.partBytes(part);
|
|
44211
|
+
for (let attempt = 1; ; attempt += 1) {
|
|
44212
|
+
options.signal?.throwIfAborted();
|
|
44213
|
+
try {
|
|
44214
|
+
const timeout = AbortSignal.timeout(partTimeoutMs(bytes));
|
|
44215
|
+
const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
|
|
44216
|
+
const etag = await options.putPart(part, await options.readPart(part), signal);
|
|
44217
|
+
if (!etag) {
|
|
44218
|
+
throw new PermanentPartError(`part ${part.part_number} stored without an ETag`);
|
|
44219
|
+
}
|
|
44220
|
+
return { part_number: part.part_number, etag };
|
|
44221
|
+
} catch (error52) {
|
|
44222
|
+
if (options.signal?.aborted) throw error52;
|
|
44223
|
+
if (error52 instanceof PermanentPartError) throw error52;
|
|
44224
|
+
if (attempt >= maxRetries) {
|
|
44225
|
+
options.onExhausted?.({ partNumber: part.part_number, attempts: attempt, error: error52 });
|
|
44226
|
+
throw error52;
|
|
44227
|
+
}
|
|
44228
|
+
options.onRetry?.({ partNumber: part.part_number, attempt, error: error52 });
|
|
44229
|
+
const base = 1e3 * 2 ** (attempt - 1);
|
|
44230
|
+
await sleep(Math.min(base + Math.random() * base * 0.5, MAX_RETRY_DELAY_MS));
|
|
44231
|
+
}
|
|
44232
|
+
}
|
|
44233
|
+
};
|
|
44234
|
+
const workers = Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
|
|
44235
|
+
for (let next = queue.shift(); next !== void 0; next = queue.shift()) {
|
|
44236
|
+
const part = await uploadOne(next);
|
|
44237
|
+
done.push(part);
|
|
44238
|
+
options.onPartUploaded?.(part);
|
|
44239
|
+
}
|
|
44240
|
+
});
|
|
44241
|
+
await Promise.all(workers);
|
|
44242
|
+
return done.sort((a, b) => a.part_number - b.part_number);
|
|
44243
|
+
}
|
|
44244
|
+
|
|
44185
44245
|
// src/client.ts
|
|
44186
44246
|
import crypto from "node:crypto";
|
|
44187
44247
|
import fs2 from "node:fs";
|
|
@@ -44301,6 +44361,7 @@ function getMimeType(filename) {
|
|
|
44301
44361
|
const ext = path2.extname(filename).toLowerCase();
|
|
44302
44362
|
return MIME_MAP[ext] ?? "application/octet-stream";
|
|
44303
44363
|
}
|
|
44364
|
+
var MULTIPART_THRESHOLD_BYTES = 8 * 1024 * 1024;
|
|
44304
44365
|
var API_BASE_URL = process.env.LOTICS_API_URL ?? "https://api.lotics.ai";
|
|
44305
44366
|
var WEB_APP_URL = "https://lotics.ai";
|
|
44306
44367
|
async function fetchOfficialStarters() {
|
|
@@ -45077,15 +45138,118 @@ var LoticsClient = class {
|
|
|
45077
45138
|
);
|
|
45078
45139
|
}
|
|
45079
45140
|
async uploadFiles(filePaths, options) {
|
|
45080
|
-
const
|
|
45141
|
+
const small = [];
|
|
45142
|
+
const large = [];
|
|
45081
45143
|
for (let i2 = 0; i2 < filePaths.length; i2++) {
|
|
45082
45144
|
const absolutePath = path2.resolve(filePaths[i2]);
|
|
45083
|
-
|
|
45084
|
-
|
|
45085
|
-
|
|
45145
|
+
const filename = options?.filenames?.[i2] ?? path2.basename(absolutePath);
|
|
45146
|
+
const { size: size2 } = await fs2.promises.stat(absolutePath);
|
|
45147
|
+
if (size2 >= MULTIPART_THRESHOLD_BYTES) {
|
|
45148
|
+
large.push({ absolutePath, filename, size: size2 });
|
|
45149
|
+
} else {
|
|
45150
|
+
small.push({ bytes: await fs2.promises.readFile(absolutePath), filename });
|
|
45151
|
+
}
|
|
45152
|
+
}
|
|
45153
|
+
const files = [];
|
|
45154
|
+
const errors2 = [];
|
|
45155
|
+
if (small.length > 0) {
|
|
45156
|
+
const result = await this.uploadFileBytes(small);
|
|
45157
|
+
files.push(...result.files);
|
|
45158
|
+
errors2.push(...result.errors);
|
|
45159
|
+
}
|
|
45160
|
+
for (const item of large) {
|
|
45161
|
+
try {
|
|
45162
|
+
files.push(await this.uploadLargeFile(item));
|
|
45163
|
+
} catch (error52) {
|
|
45164
|
+
errors2.push({
|
|
45165
|
+
filename: item.filename,
|
|
45166
|
+
error: error52 instanceof Error ? error52.message : String(error52)
|
|
45167
|
+
});
|
|
45168
|
+
}
|
|
45169
|
+
}
|
|
45170
|
+
return { files, errors: errors2 };
|
|
45171
|
+
}
|
|
45172
|
+
/**
|
|
45173
|
+
* Store one file by handing its bytes straight to object storage.
|
|
45174
|
+
*
|
|
45175
|
+
* The API mints presigned part URLs and registers the row; the bytes go
|
|
45176
|
+
* directly from disk to the bucket and never enter the API process. That is
|
|
45177
|
+
* what makes the ceiling "what a workspace may store" rather than "what one
|
|
45178
|
+
* request may cost the server" — the reason `POST /v1/files` and the ticketed
|
|
45179
|
+
* upload are both capped far lower, and the reason this exists at all.
|
|
45180
|
+
*
|
|
45181
|
+
* Parts are read one at a time rather than streamed as a request body: a part
|
|
45182
|
+
* is bounded (the server picks 5–10 MiB), a `fetch` body needs a length the
|
|
45183
|
+
* bucket will check anyway, and reading a slice keeps peak memory at one part
|
|
45184
|
+
* instead of one file.
|
|
45185
|
+
*
|
|
45186
|
+
* The PUT loop itself — retry, backoff, part timeout, ordering — is
|
|
45187
|
+
* `@lotics/shared/multipart_parts`, shared with the browser client so the
|
|
45188
|
+
* hardening cannot exist on one side only.
|
|
45189
|
+
*/
|
|
45190
|
+
async uploadLargeFile(input) {
|
|
45191
|
+
const mimeType = getMimeType(input.filename);
|
|
45192
|
+
const init = await this.request("POST", "/v1/files/multipart/init", {
|
|
45193
|
+
filename: input.filename,
|
|
45194
|
+
mime_type: mimeType,
|
|
45195
|
+
file_size: input.size
|
|
45196
|
+
});
|
|
45197
|
+
const partLength = (part) => Math.min(init.part_size, input.size - (part.part_number - 1) * init.part_size);
|
|
45198
|
+
const handle = await fs2.promises.open(input.absolutePath, "r");
|
|
45199
|
+
let uploaded;
|
|
45200
|
+
try {
|
|
45201
|
+
uploaded = await uploadParts({
|
|
45202
|
+
parts: init.parts,
|
|
45203
|
+
partBytes: partLength,
|
|
45204
|
+
// Sequential. Peak memory is then one part rather than one file, which
|
|
45205
|
+
// is the property that lets this run against a 2 GiB upload at all;
|
|
45206
|
+
// raising it trades that for wall-clock, and no upload has asked yet.
|
|
45207
|
+
concurrency: 1,
|
|
45208
|
+
putPart: async (part, body, signal) => {
|
|
45209
|
+
const response = await fetch(part.url, { method: "PUT", body, signal });
|
|
45210
|
+
if (!response.ok) throw partErrorFor(part.part_number, response.status);
|
|
45211
|
+
return response.headers.get("etag") ?? "";
|
|
45212
|
+
},
|
|
45213
|
+
readPart: async (part) => {
|
|
45214
|
+
const offset = (part.part_number - 1) * init.part_size;
|
|
45215
|
+
const length2 = partLength(part);
|
|
45216
|
+
const buffer = Buffer.alloc(length2);
|
|
45217
|
+
let filled = 0;
|
|
45218
|
+
while (filled < length2) {
|
|
45219
|
+
const { bytesRead } = await handle.read(buffer, filled, length2 - filled, offset + filled);
|
|
45220
|
+
if (bytesRead === 0) {
|
|
45221
|
+
throw new Error(
|
|
45222
|
+
`${input.filename} ended after ${offset + filled} bytes, short of the ${input.size} it reported \u2014 it changed while uploading`
|
|
45223
|
+
);
|
|
45224
|
+
}
|
|
45225
|
+
filled += bytesRead;
|
|
45226
|
+
}
|
|
45227
|
+
return buffer;
|
|
45228
|
+
}
|
|
45086
45229
|
});
|
|
45230
|
+
} catch (error52) {
|
|
45231
|
+
await this.request("POST", "/v1/files/multipart/abort", {
|
|
45232
|
+
upload_id: init.upload_id,
|
|
45233
|
+
file_storage_key: init.file_storage_key
|
|
45234
|
+
}).catch(() => {
|
|
45235
|
+
});
|
|
45236
|
+
throw error52;
|
|
45237
|
+
} finally {
|
|
45238
|
+
await handle.close();
|
|
45239
|
+
}
|
|
45240
|
+
const result = await this.request("POST", "/v1/files/multipart/complete", {
|
|
45241
|
+
file_id: init.file_id,
|
|
45242
|
+
upload_id: init.upload_id,
|
|
45243
|
+
file_storage_key: init.file_storage_key,
|
|
45244
|
+
filename: input.filename,
|
|
45245
|
+
mime_type: mimeType,
|
|
45246
|
+
parts: uploaded
|
|
45247
|
+
});
|
|
45248
|
+
const stored = result.files[0];
|
|
45249
|
+
if (!stored) {
|
|
45250
|
+
throw new Error(result.errors[0]?.error ?? "upload completed but stored no file");
|
|
45087
45251
|
}
|
|
45088
|
-
return
|
|
45252
|
+
return stored;
|
|
45089
45253
|
}
|
|
45090
45254
|
/**
|
|
45091
45255
|
* Store files from bytes the caller already holds — the path for a caller that
|
|
@@ -45711,7 +45875,7 @@ function resultSideEffects(result) {
|
|
|
45711
45875
|
}
|
|
45712
45876
|
|
|
45713
45877
|
// src/version.ts
|
|
45714
|
-
var VERSION = "0.
|
|
45878
|
+
var VERSION = "0.186.0";
|
|
45715
45879
|
|
|
45716
45880
|
// src/timezone.ts
|
|
45717
45881
|
function machineTimezone() {
|
|
@@ -45998,9 +46162,11 @@ var COMMANDS = [
|
|
|
45998
46162
|
" shipping: agent schemas vs the live app, workflow",
|
|
45999
46163
|
" declarations and BODIES vs what is live, aliases",
|
|
46000
46164
|
" the code calls but nothing bound, undeclared",
|
|
46001
|
-
" capabilities, query drift. Plus
|
|
46002
|
-
" deploy runs:
|
|
46003
|
-
"
|
|
46165
|
+
" capabilities, query drift. Plus two rules no",
|
|
46166
|
+
" deploy runs: every body typechecked against",
|
|
46167
|
+
" the pulled types, and a concrete id or an",
|
|
46168
|
+
" id-shaped placeholder in source a starter",
|
|
46169
|
+
" would ship.",
|
|
46004
46170
|
" Exits 1 on any of it, so CI can gate on it",
|
|
46005
46171
|
" lotics app workflow set <alias> Push the edited src/workflows/<alias>.ts body",
|
|
46006
46172
|
" through set_app_workflow (server verifies)",
|
|
@@ -71205,11 +71371,8 @@ function subsetIssues(bodySource, offset) {
|
|
|
71205
71371
|
];
|
|
71206
71372
|
}
|
|
71207
71373
|
async function loadProjectTypescript(projectDir) {
|
|
71208
|
-
const
|
|
71209
|
-
|
|
71210
|
-
try {
|
|
71211
|
-
resolved = requireFromProject.resolve("typescript");
|
|
71212
|
-
} catch {
|
|
71374
|
+
const resolved = resolveProjectTypescript(projectDir);
|
|
71375
|
+
if (resolved === null) {
|
|
71213
71376
|
throw new Error(
|
|
71214
71377
|
`Could not resolve 'typescript' from ${projectDir}. 'lotics app workflow check' type-checks bodies with the app's own compiler \u2014 run 'npm install' (the scaffold ships typescript as a devDependency).`
|
|
71215
71378
|
);
|
|
@@ -71217,6 +71380,14 @@ async function loadProjectTypescript(projectDir) {
|
|
|
71217
71380
|
const mod2 = await import(pathToImportUrl(resolved));
|
|
71218
71381
|
return mod2.default ?? mod2;
|
|
71219
71382
|
}
|
|
71383
|
+
function resolveProjectTypescript(projectDir) {
|
|
71384
|
+
const requireFromProject = createRequire(path8.join(projectDir, "package.json"));
|
|
71385
|
+
try {
|
|
71386
|
+
return requireFromProject.resolve("typescript");
|
|
71387
|
+
} catch {
|
|
71388
|
+
return null;
|
|
71389
|
+
}
|
|
71390
|
+
}
|
|
71220
71391
|
function pathToImportUrl(p) {
|
|
71221
71392
|
const resolved = path8.resolve(p);
|
|
71222
71393
|
const prefixed = resolved.startsWith("/") ? resolved : `/${resolved}`;
|
|
@@ -73079,13 +73250,35 @@ async function appCheck(client, args = {}) {
|
|
|
73079
73250
|
warnIfUndescribed(app);
|
|
73080
73251
|
const unportable = scanProjectForIds(projectDir);
|
|
73081
73252
|
reportPortabilityIds(unportable);
|
|
73082
|
-
|
|
73253
|
+
const bodiesFailing = await countFailingWorkflowBodies(client, projectDir, meta3);
|
|
73254
|
+
if (!nothingPending(pending) || unportable.length > 0 || bodiesFailing > 0) {
|
|
73083
73255
|
process.exit(1);
|
|
73084
73256
|
}
|
|
73085
73257
|
console.error(
|
|
73086
|
-
"Checked the app's bindings, capabilities, agent schemas and id portability \u2014 nothing blocking."
|
|
73258
|
+
"Checked the app's bindings, capabilities, agent schemas, workflow bodies and id portability \u2014 nothing blocking."
|
|
73087
73259
|
);
|
|
73088
73260
|
}
|
|
73261
|
+
async function countFailingWorkflowBodies(client, projectDir, meta3) {
|
|
73262
|
+
const bound = Object.keys(meta3.workflows ?? {});
|
|
73263
|
+
const typed = bound.filter((alias) => fs8.existsSync(workflowGlobalsPath(projectDir, alias)));
|
|
73264
|
+
const untyped = bound.filter((alias) => !typed.includes(alias));
|
|
73265
|
+
if (untyped.length > 0) {
|
|
73266
|
+
console.error(
|
|
73267
|
+
`\u26A0 Not type-checked, no local types for: ${untyped.join(", ")}. Run 'lotics app codegen' to write .lotics/workflows/<alias>.globals.d.ts.`
|
|
73268
|
+
);
|
|
73269
|
+
}
|
|
73270
|
+
const toCheck = await collectWorkflowBodyChecks(projectDir, meta3, typed, client);
|
|
73271
|
+
if (toCheck.length === 0) return 0;
|
|
73272
|
+
if (resolveProjectTypescript(projectDir) === null) {
|
|
73273
|
+
console.error(
|
|
73274
|
+
`\u26A0 ${toCheck.length} workflow bod${toCheck.length === 1 ? "y" : "ies"} not type-checked \u2014 no 'typescript' in this project's node_modules. Run 'npm install', then 'lotics app workflow check'.`
|
|
73275
|
+
);
|
|
73276
|
+
return 0;
|
|
73277
|
+
}
|
|
73278
|
+
const results = checkWorkflowBodies(await loadProjectTypescript(projectDir), toCheck);
|
|
73279
|
+
printWorkflowCheckResults(projectDir, results);
|
|
73280
|
+
return results.filter((r) => r.issues.length > 0).length;
|
|
73281
|
+
}
|
|
73089
73282
|
async function pushPendingBindings(client, projectDir, pending) {
|
|
73090
73283
|
const plan = [
|
|
73091
73284
|
...pending.queries.map((alias) => `query ${alias}`),
|
|
@@ -73974,6 +74167,18 @@ async function appWorkflowCheck(args) {
|
|
|
73974
74167
|
console.error(`App ${meta3.app_id} has no bound workflows to check.`);
|
|
73975
74168
|
return;
|
|
73976
74169
|
}
|
|
74170
|
+
const toCheck = await collectWorkflowBodyChecks(projectDir, meta3, aliases, args.client);
|
|
74171
|
+
if (toCheck.length === 0) {
|
|
74172
|
+
console.error("No workflow bodies to check (every bound alias was skipped).");
|
|
74173
|
+
return;
|
|
74174
|
+
}
|
|
74175
|
+
const tsApi = await loadProjectTypescript(projectDir);
|
|
74176
|
+
const results = checkWorkflowBodies(tsApi, toCheck);
|
|
74177
|
+
printWorkflowCheckResults(projectDir, results);
|
|
74178
|
+
const failed = results.filter((r) => r.issues.length > 0);
|
|
74179
|
+
if (failed.length > 0) process.exit(1);
|
|
74180
|
+
}
|
|
74181
|
+
async function collectWorkflowBodyChecks(projectDir, meta3, aliases, client) {
|
|
73977
74182
|
const toCheck = [];
|
|
73978
74183
|
for (const alias of aliases) {
|
|
73979
74184
|
const bodyPath = workflowFilePath(projectDir, alias);
|
|
@@ -73994,14 +74199,14 @@ async function appWorkflowCheck(args) {
|
|
|
73994
74199
|
}
|
|
73995
74200
|
for (const { alias, declaration } of staleWorkflowGlobals(projectDir, meta3.workflows ?? {})) {
|
|
73996
74201
|
if (!toCheck.some((c) => c.alias === alias)) continue;
|
|
73997
|
-
if (!
|
|
74202
|
+
if (!client) {
|
|
73998
74203
|
console.error(
|
|
73999
74204
|
`\u26A0 "${alias}" \u2014 package.json declares inputs/outputs the local types were not built from, and they cannot be refreshed here (no credentials). Checked against the older types, so a newly declared input will read as a type error that set_app_workflow will accept.`
|
|
74000
74205
|
);
|
|
74001
74206
|
continue;
|
|
74002
74207
|
}
|
|
74003
74208
|
try {
|
|
74004
|
-
await refreshWorkflowTypes(
|
|
74209
|
+
await refreshWorkflowTypes(client, projectDir, meta3.app_id, alias, declaration);
|
|
74005
74210
|
console.error(`Refreshed workflow types for ${alias} \u2014 package.json had moved on.`);
|
|
74006
74211
|
} catch (err2) {
|
|
74007
74212
|
console.error(
|
|
@@ -74009,15 +74214,7 @@ async function appWorkflowCheck(args) {
|
|
|
74009
74214
|
);
|
|
74010
74215
|
}
|
|
74011
74216
|
}
|
|
74012
|
-
|
|
74013
|
-
console.error("No workflow bodies to check (every bound alias was skipped).");
|
|
74014
|
-
return;
|
|
74015
|
-
}
|
|
74016
|
-
const tsApi = await loadProjectTypescript(projectDir);
|
|
74017
|
-
const results = checkWorkflowBodies(tsApi, toCheck);
|
|
74018
|
-
printWorkflowCheckResults(projectDir, results);
|
|
74019
|
-
const failed = results.filter((r) => r.issues.length > 0);
|
|
74020
|
-
if (failed.length > 0) process.exit(1);
|
|
74217
|
+
return toCheck;
|
|
74021
74218
|
}
|
|
74022
74219
|
function printWorkflowCheckResults(projectDir, results) {
|
|
74023
74220
|
let totalErrors = 0;
|
|
@@ -103207,7 +103404,7 @@ import { readFileSync, writeFileSync, existsSync as existsSync2, mkdtempSync, rm
|
|
|
103207
103404
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
103208
103405
|
import { join as join2, dirname, resolve, extname, basename } from "node:path";
|
|
103209
103406
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
103210
|
-
import { setTimeout as
|
|
103407
|
+
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
103211
103408
|
var HERE = dirname(fileURLToPath2(import.meta.url));
|
|
103212
103409
|
function fail3(msg) {
|
|
103213
103410
|
console.error(msg);
|
|
@@ -103339,7 +103536,7 @@ async function runPreviewCommand(filePath, flags) {
|
|
|
103339
103536
|
const p = parseInt(readFileSync(portFile, "utf8").split("\n")[0], 10);
|
|
103340
103537
|
if (p) cdpPort = p;
|
|
103341
103538
|
}
|
|
103342
|
-
if (!cdpPort) await
|
|
103539
|
+
if (!cdpPort) await sleep2(100);
|
|
103343
103540
|
}
|
|
103344
103541
|
if (!cdpPort) throw new Error("Chrome did not expose a debugging port (launch failed?).");
|
|
103345
103542
|
let target;
|
|
@@ -103349,7 +103546,7 @@ async function runPreviewCommand(filePath, flags) {
|
|
|
103349
103546
|
target = list2.find((t) => t.type === "page");
|
|
103350
103547
|
} catch {
|
|
103351
103548
|
}
|
|
103352
|
-
if (!target?.webSocketDebuggerUrl) await
|
|
103549
|
+
if (!target?.webSocketDebuggerUrl) await sleep2(100);
|
|
103353
103550
|
}
|
|
103354
103551
|
if (!target?.webSocketDebuggerUrl) throw new Error("No Chrome page target available.");
|
|
103355
103552
|
const cdp = await cdpConnect(target.webSocketDebuggerUrl);
|
|
@@ -103371,7 +103568,7 @@ async function runPreviewCommand(filePath, flags) {
|
|
|
103371
103568
|
if (v.warnings?.length) warnings.push(...v.warnings);
|
|
103372
103569
|
break;
|
|
103373
103570
|
}
|
|
103374
|
-
await
|
|
103571
|
+
await sleep2(75);
|
|
103375
103572
|
}
|
|
103376
103573
|
if (!done) throw new Error("Render timed out (page never signaled completion).");
|
|
103377
103574
|
if (err2) throw new Error(`Render engine error: ${err2}`);
|
package/dist/src/client.d.ts
CHANGED
|
@@ -968,6 +968,7 @@ export declare class LoticsClient {
|
|
|
968
968
|
uploadFiles(filePaths: string[], options?: {
|
|
969
969
|
filenames?: string[];
|
|
970
970
|
}): Promise<FileUploadResult>;
|
|
971
|
+
private uploadLargeFile;
|
|
971
972
|
/**
|
|
972
973
|
* Store files from bytes the caller already holds — the path for a caller that
|
|
973
974
|
* never had them on disk (an email attachment decoded in memory, a generated
|