@lotics/cli 0.142.0 → 0.143.2
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/render_page.js +215 -63
- package/dist/src/cli.js +248 -75
- package/dist/src/client.d.ts +4 -0
- package/docs/building_an_app.md +18 -4
- package/docs/cli_reference.md +2 -2
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -16769,7 +16769,7 @@ var require_operators = __commonJS({
|
|
|
16769
16769
|
}
|
|
16770
16770
|
};
|
|
16771
16771
|
var type2Number = { "boolean": 3, "string": 2, "number": 1 };
|
|
16772
|
-
var
|
|
16772
|
+
var Infix2 = {
|
|
16773
16773
|
compareOp: (value1, infix, value2, isArray1, isArray22) => {
|
|
16774
16774
|
if (value1 == null) value1 = 0;
|
|
16775
16775
|
if (value2 == null) value2 = 0;
|
|
@@ -16860,7 +16860,7 @@ var require_operators = __commonJS({
|
|
|
16860
16860
|
module2.exports = {
|
|
16861
16861
|
Prefix,
|
|
16862
16862
|
Postfix,
|
|
16863
|
-
Infix,
|
|
16863
|
+
Infix: Infix2,
|
|
16864
16864
|
Operators: {
|
|
16865
16865
|
compareOp: ["<", ">", "=", "<>", "<=", ">="],
|
|
16866
16866
|
concatOp: ["&"],
|
|
@@ -16875,7 +16875,7 @@ var require_math = __commonJS({
|
|
|
16875
16875
|
"../../node_modules/fast-formula-parser/formulas/functions/math.js"(exports2, module2) {
|
|
16876
16876
|
var FormulaError = require_error();
|
|
16877
16877
|
var { FormulaHelpers, Types, Factorials, Criteria } = require_helpers();
|
|
16878
|
-
var { Infix } = require_operators();
|
|
16878
|
+
var { Infix: Infix2 } = require_operators();
|
|
16879
16879
|
var H = FormulaHelpers;
|
|
16880
16880
|
var f = [];
|
|
16881
16881
|
var fd2 = [];
|
|
@@ -17398,7 +17398,7 @@ var require_math = __commonJS({
|
|
|
17398
17398
|
if (criteria.match === criteria.value.test(value2)) {
|
|
17399
17399
|
sum2 += valueToAdd;
|
|
17400
17400
|
}
|
|
17401
|
-
} else if (
|
|
17401
|
+
} else if (Infix2.compareOp(value2, criteria.op, criteria.value, Array.isArray(value2), isCriteriaArray)) {
|
|
17402
17402
|
sum2 += valueToAdd;
|
|
17403
17403
|
}
|
|
17404
17404
|
});
|
|
@@ -23678,7 +23678,7 @@ var require_statistical = __commonJS({
|
|
|
23678
23678
|
"../../node_modules/fast-formula-parser/formulas/functions/statistical.js"(exports2, module2) {
|
|
23679
23679
|
var FormulaError = require_error();
|
|
23680
23680
|
var { FormulaHelpers, Types, Criteria, Address } = require_helpers();
|
|
23681
|
-
var { Infix } = require_operators();
|
|
23681
|
+
var { Infix: Infix2 } = require_operators();
|
|
23682
23682
|
var H = FormulaHelpers;
|
|
23683
23683
|
var { DistributionFunctions } = require_distribution();
|
|
23684
23684
|
var StatisticalFunctions = {
|
|
@@ -23740,7 +23740,7 @@ var require_statistical = __commonJS({
|
|
|
23740
23740
|
sum2 += valueToAdd;
|
|
23741
23741
|
cnt++;
|
|
23742
23742
|
}
|
|
23743
|
-
} else if (
|
|
23743
|
+
} else if (Infix2.compareOp(value2, criteria.op, criteria.value, Array.isArray(value2), isCriteriaArray)) {
|
|
23744
23744
|
sum2 += valueToAdd;
|
|
23745
23745
|
cnt++;
|
|
23746
23746
|
}
|
|
@@ -23779,7 +23779,7 @@ var require_statistical = __commonJS({
|
|
|
23779
23779
|
if (criteria.op === "wc") {
|
|
23780
23780
|
if (criteria.match === criteria.value.test(value2))
|
|
23781
23781
|
cnt++;
|
|
23782
|
-
} else if (
|
|
23782
|
+
} else if (Infix2.compareOp(value2, criteria.op, criteria.value, Array.isArray(value2), isCriteriaArray)) {
|
|
23783
23783
|
cnt++;
|
|
23784
23784
|
}
|
|
23785
23785
|
});
|
|
@@ -33332,7 +33332,7 @@ var require_utils2 = __commonJS({
|
|
|
33332
33332
|
"../../node_modules/fast-formula-parser/grammar/utils.js"(exports2, module2) {
|
|
33333
33333
|
var FormulaError = require_error();
|
|
33334
33334
|
var { Address } = require_helpers();
|
|
33335
|
-
var { Prefix, Postfix, Infix, Operators } = require_operators();
|
|
33335
|
+
var { Prefix, Postfix, Infix: Infix2, Operators } = require_operators();
|
|
33336
33336
|
var Collection = require_collection();
|
|
33337
33337
|
var MAX_ROW2 = 1048576;
|
|
33338
33338
|
var MAX_COLUMN2 = 16384;
|
|
@@ -33456,11 +33456,11 @@ var require_utils2 = __commonJS({
|
|
|
33456
33456
|
if (this.isFormulaError(val2))
|
|
33457
33457
|
return val2;
|
|
33458
33458
|
if (Operators.compareOp.includes(infix))
|
|
33459
|
-
return
|
|
33459
|
+
return Infix2.compareOp(val1, infix, val2, isArray1, isArray22);
|
|
33460
33460
|
else if (Operators.concatOp.includes(infix))
|
|
33461
|
-
return
|
|
33461
|
+
return Infix2.concatOp(val1, infix, val2, isArray1, isArray22);
|
|
33462
33462
|
else if (Operators.mathOp.includes(infix))
|
|
33463
|
-
return
|
|
33463
|
+
return Infix2.mathOp(val1, infix, val2, isArray1, isArray22);
|
|
33464
33464
|
else
|
|
33465
33465
|
throw new Error(`Unrecognized infix: ${infix}`);
|
|
33466
33466
|
}
|
|
@@ -34012,7 +34012,7 @@ var require_utils3 = __commonJS({
|
|
|
34012
34012
|
"../../node_modules/fast-formula-parser/grammar/dependency/utils.js"(exports2, module2) {
|
|
34013
34013
|
var FormulaError = require_error();
|
|
34014
34014
|
var { FormulaHelpers, Types, Address } = require_helpers();
|
|
34015
|
-
var { Prefix, Postfix, Infix, Operators } = require_operators();
|
|
34015
|
+
var { Prefix, Postfix, Infix: Infix2, Operators } = require_operators();
|
|
34016
34016
|
var Collection = require_collection();
|
|
34017
34017
|
var MAX_ROW2 = 1048576;
|
|
34018
34018
|
var MAX_COLUMN2 = 16384;
|
|
@@ -72146,7 +72146,7 @@ function writeAgentFiles(projectDir, agents, opts) {
|
|
|
72146
72146
|
console.error(`\u26A0 Skipped src/agents/${alias}.md \u2014 the live declaration carries no instructions.`);
|
|
72147
72147
|
continue;
|
|
72148
72148
|
}
|
|
72149
|
-
if (!opts.overwrite && localPromptUnpushed(projectDir, alias)) {
|
|
72149
|
+
if (!opts.overwrite && localPromptUnpushed(projectDir, alias, instructions)) {
|
|
72150
72150
|
opts.kept.push(`src/agents/${alias}.md`);
|
|
72151
72151
|
continue;
|
|
72152
72152
|
}
|
|
@@ -72155,22 +72155,26 @@ function writeAgentFiles(projectDir, agents, opts) {
|
|
|
72155
72155
|
}
|
|
72156
72156
|
return written;
|
|
72157
72157
|
}
|
|
72158
|
-
function localPromptUnpushed(projectDir, alias) {
|
|
72158
|
+
function localPromptUnpushed(projectDir, alias, live) {
|
|
72159
72159
|
const file2 = agentFilePath2(projectDir, alias);
|
|
72160
72160
|
if (!fs8.existsSync(file2)) return false;
|
|
72161
|
+
const onDisk = fs8.readFileSync(file2, "utf-8");
|
|
72161
72162
|
const baseline = readSynced(projectDir).agents[alias]?.content;
|
|
72162
|
-
if (baseline === void 0) return
|
|
72163
|
-
return contentSha(stripAgentHeader(
|
|
72163
|
+
if (baseline === void 0) return stripAgentHeader(onDisk).trim() !== live.trim();
|
|
72164
|
+
return contentSha(stripAgentHeader(onDisk)) !== baseline;
|
|
72164
72165
|
}
|
|
72165
72166
|
function generatedWorkflowDescription(appName, alias) {
|
|
72166
72167
|
return `Workflow invoked by app "${appName}" via useWorkflow("${alias}").`;
|
|
72167
72168
|
}
|
|
72168
|
-
function localBodyUnpushed(projectDir, alias) {
|
|
72169
|
+
function localBodyUnpushed(projectDir, alias, live) {
|
|
72169
72170
|
const file2 = workflowFilePath(projectDir, alias);
|
|
72170
72171
|
if (!fs8.existsSync(file2)) return false;
|
|
72172
|
+
const onDisk = fs8.readFileSync(file2, "utf-8");
|
|
72171
72173
|
const baseline = readSynced(projectDir).workflows[alias]?.content;
|
|
72172
|
-
if (baseline === void 0)
|
|
72173
|
-
|
|
72174
|
+
if (baseline === void 0) {
|
|
72175
|
+
return stripWorkflowHeader(onDisk).trim() !== normalizeWorkflowBody(live).trim();
|
|
72176
|
+
}
|
|
72177
|
+
return contentSha(stripWorkflowHeader(onDisk)) !== baseline;
|
|
72174
72178
|
}
|
|
72175
72179
|
async function writeWorkflowFiles(client, projectDir, app_id, appName, workflows, opts) {
|
|
72176
72180
|
const written = [];
|
|
@@ -72197,7 +72201,7 @@ async function writeWorkflowFiles(client, projectDir, app_id, appName, workflows
|
|
|
72197
72201
|
alias,
|
|
72198
72202
|
toWorkflowDtsDeclaration(declaration)
|
|
72199
72203
|
);
|
|
72200
|
-
if (!opts.overwrite && localBodyUnpushed(projectDir, alias)) {
|
|
72204
|
+
if (!opts.overwrite && localBodyUnpushed(projectDir, alias, source)) {
|
|
72201
72205
|
opts.kept.push(`src/workflows/${alias}.ts`);
|
|
72202
72206
|
continue;
|
|
72203
72207
|
}
|
|
@@ -72816,7 +72820,12 @@ async function appPull(client, args) {
|
|
|
72816
72820
|
try {
|
|
72817
72821
|
await runTar(["-xzf", tmpFile, "-C", stagingDir], stagingDir);
|
|
72818
72822
|
console.error(`Extracting to ${targetPath}...`);
|
|
72819
|
-
keptLocal.push(
|
|
72823
|
+
keptLocal.push(
|
|
72824
|
+
...copyTree(stagingDir, targetPath, {
|
|
72825
|
+
overwrite: args.force === true,
|
|
72826
|
+
skip: [WORKFLOWS_DIR, AGENTS_DIR].map((d) => d.split(path9.sep).join("/"))
|
|
72827
|
+
})
|
|
72828
|
+
);
|
|
72820
72829
|
} finally {
|
|
72821
72830
|
if (fs8.existsSync(tmpFile)) fs8.unlinkSync(tmpFile);
|
|
72822
72831
|
fs8.rmSync(stagingDir, { recursive: true, force: true });
|
|
@@ -73009,6 +73018,7 @@ async function appDeploy(client, args) {
|
|
|
73009
73018
|
}
|
|
73010
73019
|
if (liveAfter) {
|
|
73011
73020
|
warnIfUnbranded(liveAfter);
|
|
73021
|
+
warnIfUndescribed(liveAfter);
|
|
73012
73022
|
warnIfUnboundAliases(liveAfter, called);
|
|
73013
73023
|
warnIfInertAgentTools(liveAfter);
|
|
73014
73024
|
}
|
|
@@ -73047,6 +73057,15 @@ function warnIfUnbranded(app) {
|
|
|
73047
73057
|
Find an icon: lotics run search_app_icons '{"query":"<word>"}'`
|
|
73048
73058
|
);
|
|
73049
73059
|
}
|
|
73060
|
+
function warnIfUndescribed(app) {
|
|
73061
|
+
if (app.description && app.description.trim() !== "") return;
|
|
73062
|
+
console.error(
|
|
73063
|
+
`
|
|
73064
|
+
\u26A0 This app has no description \u2014 the capability catalog its chat agent reads every turn
|
|
73065
|
+
lists the aliases and says nothing about what to do with them.
|
|
73066
|
+
Set it: lotics run update_app '{"app_id":"${app.id}","description":"<what the app is for, and the standing job it does>"}'`
|
|
73067
|
+
);
|
|
73068
|
+
}
|
|
73050
73069
|
async function readLiveQueries(client, appId) {
|
|
73051
73070
|
try {
|
|
73052
73071
|
const app = await client.getApp(appId);
|
|
@@ -73154,6 +73173,7 @@ async function appCheck(client, args = {}) {
|
|
|
73154
73173
|
warnIfInertAgentTools(app);
|
|
73155
73174
|
warnIfProjectFootguns(projectDir, sourceText);
|
|
73156
73175
|
warnIfUnbranded(app);
|
|
73176
|
+
warnIfUndescribed(app);
|
|
73157
73177
|
if (!nothingPending(pending)) {
|
|
73158
73178
|
process.exit(1);
|
|
73159
73179
|
}
|
|
@@ -73348,6 +73368,7 @@ function copyTree(from, to, opts, relative = "") {
|
|
|
73348
73368
|
const src = path9.join(from, entry.name);
|
|
73349
73369
|
const dest = path9.join(to, entry.name);
|
|
73350
73370
|
const rel = path9.join(relative, entry.name);
|
|
73371
|
+
if (opts.skip?.includes(rel.split(path9.sep).join("/"))) continue;
|
|
73351
73372
|
if (entry.isDirectory()) {
|
|
73352
73373
|
fs8.mkdirSync(dest, { recursive: true });
|
|
73353
73374
|
kept.push(...copyTree(src, dest, opts, rel));
|
|
@@ -84764,14 +84785,14 @@ function readSourceData(workbook, cache) {
|
|
|
84764
84785
|
if (!range2) return void 0;
|
|
84765
84786
|
const header = [];
|
|
84766
84787
|
for (let col = range2.startCol; col <= range2.endCol; col++) {
|
|
84767
|
-
const cell = sheet.
|
|
84788
|
+
const cell = sheet.getCellAt(range2.startRow, col);
|
|
84768
84789
|
header.push(formatHeader(cell?.value));
|
|
84769
84790
|
}
|
|
84770
84791
|
const records = [];
|
|
84771
84792
|
for (let row = range2.startRow + 1; row <= range2.endRow; row++) {
|
|
84772
84793
|
const rec = [];
|
|
84773
84794
|
for (let col = range2.startCol; col <= range2.endCol; col++) {
|
|
84774
|
-
const cell = sheet.
|
|
84795
|
+
const cell = sheet.getCellAt(row, col);
|
|
84775
84796
|
rec.push(coerceValue(cell?.value));
|
|
84776
84797
|
}
|
|
84777
84798
|
records.push(rec);
|
|
@@ -86315,23 +86336,70 @@ var CellMap = class {
|
|
|
86315
86336
|
get size() {
|
|
86316
86337
|
return this.store.size;
|
|
86317
86338
|
}
|
|
86339
|
+
/**
|
|
86340
|
+
* The populated extent of this map, memoized until a mutation invalidates it.
|
|
86341
|
+
*
|
|
86342
|
+
* It lives HERE rather than on `SheetModel` because it is a pure function of
|
|
86343
|
+
* the cell set: a cache held next to the data cannot outlive it, so replacing
|
|
86344
|
+
* a sheet's map can never serve the previous map's extent. Every mutator
|
|
86345
|
+
* below drops it, including the packed pair — those bypass `set`/`delete`
|
|
86346
|
+
* entirely and are what the row/column commands use.
|
|
86347
|
+
*
|
|
86348
|
+
* Walking PACKED keys is the other half. `keys()` unpacks each key into an A1
|
|
86349
|
+
* string, which the caller then reparsed with a regex, so the old form packed,
|
|
86350
|
+
* unpacked and reparsed the whole sheet on every call — and it is called once
|
|
86351
|
+
* per oversized range during a recompute.
|
|
86352
|
+
*/
|
|
86353
|
+
usedRange() {
|
|
86354
|
+
if (!this.usedRangeCache) {
|
|
86355
|
+
let minRow2 = Infinity;
|
|
86356
|
+
let maxRow2 = 0;
|
|
86357
|
+
let minCol2 = Infinity;
|
|
86358
|
+
let maxCol2 = 0;
|
|
86359
|
+
for (const packed of this.store.keys()) {
|
|
86360
|
+
const { row, col } = unpackRowCol(packed);
|
|
86361
|
+
if (row < minRow2) minRow2 = row;
|
|
86362
|
+
if (row > maxRow2) maxRow2 = row;
|
|
86363
|
+
if (col < minCol2) minCol2 = col;
|
|
86364
|
+
if (col > maxCol2) maxCol2 = col;
|
|
86365
|
+
}
|
|
86366
|
+
this.usedRangeCache = maxRow2 === 0 ? { minRow: 1, maxRow: 1, minCol: 1, maxCol: 1 } : { minRow: minRow2, maxRow: maxRow2, minCol: minCol2, maxCol: maxCol2 };
|
|
86367
|
+
}
|
|
86368
|
+
const { minRow, maxRow, minCol, maxCol } = this.usedRangeCache;
|
|
86369
|
+
return { minRow, maxRow, minCol, maxCol };
|
|
86370
|
+
}
|
|
86318
86371
|
get(ref2) {
|
|
86319
86372
|
return this.store.get(packRef(ref2));
|
|
86320
86373
|
}
|
|
86374
|
+
/**
|
|
86375
|
+
* Read by coordinate, skipping the A1 round trip.
|
|
86376
|
+
*
|
|
86377
|
+
* `get(ref)` costs a number→string→regex→number conversion per call, which
|
|
86378
|
+
* is invisible until a caller reads a range cell by cell: on one real
|
|
86379
|
+
* workbook `refToRowCol` alone was 39% of a full recompute. A caller already
|
|
86380
|
+
* holding (row, col) — the range resolver, any grid walk — should use this.
|
|
86381
|
+
*/
|
|
86382
|
+
getAt(row, col) {
|
|
86383
|
+
return this.store.get(packRowCol(row, col));
|
|
86384
|
+
}
|
|
86321
86385
|
set(ref2, cell) {
|
|
86322
86386
|
const key = packRef(ref2);
|
|
86323
86387
|
if (key < 0) return this;
|
|
86324
86388
|
this.store.set(key, cell);
|
|
86389
|
+
this.usedRangeCache = void 0;
|
|
86325
86390
|
return this;
|
|
86326
86391
|
}
|
|
86327
86392
|
has(ref2) {
|
|
86328
86393
|
return this.store.has(packRef(ref2));
|
|
86329
86394
|
}
|
|
86330
86395
|
delete(ref2) {
|
|
86331
|
-
|
|
86396
|
+
const deleted = this.store.delete(packRef(ref2));
|
|
86397
|
+
if (deleted) this.usedRangeCache = void 0;
|
|
86398
|
+
return deleted;
|
|
86332
86399
|
}
|
|
86333
86400
|
clear() {
|
|
86334
86401
|
this.store.clear();
|
|
86402
|
+
this.usedRangeCache = void 0;
|
|
86335
86403
|
}
|
|
86336
86404
|
*keys() {
|
|
86337
86405
|
for (const k of this.store.keys()) yield unpackRef(k);
|
|
@@ -86359,10 +86427,13 @@ var CellMap = class {
|
|
|
86359
86427
|
/** Direct packed-key set, for the same hot loops. */
|
|
86360
86428
|
setPacked(packed, cell) {
|
|
86361
86429
|
this.store.set(packed, cell);
|
|
86430
|
+
this.usedRangeCache = void 0;
|
|
86362
86431
|
}
|
|
86363
86432
|
/** Direct packed-key delete. */
|
|
86364
86433
|
deletePacked(packed) {
|
|
86365
|
-
|
|
86434
|
+
const deleted = this.store.delete(packed);
|
|
86435
|
+
if (deleted) this.usedRangeCache = void 0;
|
|
86436
|
+
return deleted;
|
|
86366
86437
|
}
|
|
86367
86438
|
};
|
|
86368
86439
|
var StyleRegistry = class {
|
|
@@ -86545,6 +86616,10 @@ var SheetModel = class {
|
|
|
86545
86616
|
getCell(ref2) {
|
|
86546
86617
|
return this.cells.get(ref2);
|
|
86547
86618
|
}
|
|
86619
|
+
/** Read by coordinate, skipping the A1 round trip — see `CellMap.getAt`. */
|
|
86620
|
+
getCellAt(row, col) {
|
|
86621
|
+
return this.cells.getAt(row, col);
|
|
86622
|
+
}
|
|
86548
86623
|
/**
|
|
86549
86624
|
* Set cell value with optional inline style. Interns style via the
|
|
86550
86625
|
* StyleRegistry when available. When `style` is supplied and matches the
|
|
@@ -86639,22 +86714,12 @@ var SheetModel = class {
|
|
|
86639
86714
|
this.set(ref2, label, style);
|
|
86640
86715
|
});
|
|
86641
86716
|
}
|
|
86642
|
-
/**
|
|
86717
|
+
/**
|
|
86718
|
+
* The populated extent of the sheet. Derived from — and cached by — the cell
|
|
86719
|
+
* map, so it cannot outlive the data it describes.
|
|
86720
|
+
*/
|
|
86643
86721
|
getUsedRange() {
|
|
86644
|
-
|
|
86645
|
-
let maxRow = 0;
|
|
86646
|
-
let minCol = Infinity;
|
|
86647
|
-
let maxCol = 0;
|
|
86648
|
-
for (const ref2 of this.cells.keys()) {
|
|
86649
|
-
const rc = refToRowCol(ref2);
|
|
86650
|
-
if (!rc) continue;
|
|
86651
|
-
if (rc.row < minRow) minRow = rc.row;
|
|
86652
|
-
if (rc.row > maxRow) maxRow = rc.row;
|
|
86653
|
-
if (rc.col < minCol) minCol = rc.col;
|
|
86654
|
-
if (rc.col > maxCol) maxCol = rc.col;
|
|
86655
|
-
}
|
|
86656
|
-
if (maxRow === 0) return { minRow: 1, maxRow: 1, minCol: 1, maxCol: 1 };
|
|
86657
|
-
return { minRow, maxRow, minCol, maxCol };
|
|
86722
|
+
return this.cells.usedRange();
|
|
86658
86723
|
}
|
|
86659
86724
|
};
|
|
86660
86725
|
var WorkbookModel = class {
|
|
@@ -86901,7 +86966,7 @@ function applyAutoFilterHiddenRows(sheet) {
|
|
|
86901
86966
|
let visible = true;
|
|
86902
86967
|
for (const filterCol of activeFilters) {
|
|
86903
86968
|
const col = startCol + filterCol.colIndex;
|
|
86904
|
-
const cell = sheet.
|
|
86969
|
+
const cell = sheet.getCellAt(r, col);
|
|
86905
86970
|
const cellText = cell?.displayValue ?? "";
|
|
86906
86971
|
if (filterCol.filterValues && filterCol.filterValues.length > 0) {
|
|
86907
86972
|
if (!filterCol.filterValues.includes(cellText)) {
|
|
@@ -94131,6 +94196,28 @@ function parseCellKey(key) {
|
|
|
94131
94196
|
const bang = key.indexOf("!");
|
|
94132
94197
|
return { sheet: key.slice(0, bang), ref: key.slice(bang + 1) };
|
|
94133
94198
|
}
|
|
94199
|
+
var COARSE_COL_SUFFIX = ":*";
|
|
94200
|
+
var COARSE_ROW_PREFIX = "*:";
|
|
94201
|
+
function columnKey(sheet, column) {
|
|
94202
|
+
return `${sheet}!${column}${COARSE_COL_SUFFIX}`;
|
|
94203
|
+
}
|
|
94204
|
+
function rowKey(sheet, row) {
|
|
94205
|
+
return `${sheet}!${COARSE_ROW_PREFIX}${row}`;
|
|
94206
|
+
}
|
|
94207
|
+
function isCoarseKey(key) {
|
|
94208
|
+
return key.endsWith(COARSE_COL_SUFFIX) || key.includes(`!${COARSE_ROW_PREFIX}`);
|
|
94209
|
+
}
|
|
94210
|
+
var A1 = /^([A-Z]+)(\d+)$/;
|
|
94211
|
+
function coarseKeysFor(cellKeyStr) {
|
|
94212
|
+
const bang = cellKeyStr.indexOf("!");
|
|
94213
|
+
if (bang < 0) return [];
|
|
94214
|
+
const sheet = cellKeyStr.slice(0, bang);
|
|
94215
|
+
const ref2 = cellKeyStr.slice(bang + 1);
|
|
94216
|
+
const m = A1.exec(ref2);
|
|
94217
|
+
if (!m) return [];
|
|
94218
|
+
return [columnKey(sheet, m[1]), rowKey(sheet, Number(m[2]))];
|
|
94219
|
+
}
|
|
94220
|
+
var EMPTY = /* @__PURE__ */ new Set();
|
|
94134
94221
|
var DependencyGraph = class {
|
|
94135
94222
|
constructor() {
|
|
94136
94223
|
// Forward edges: cell → cells it references (precedents)
|
|
@@ -94165,19 +94252,35 @@ var DependencyGraph = class {
|
|
|
94165
94252
|
}
|
|
94166
94253
|
/** Get all cells that directly depend on the given cell. */
|
|
94167
94254
|
getDirectDependents(cell) {
|
|
94168
|
-
return Array.from(this.
|
|
94255
|
+
return Array.from(this.dependentsOf(cell));
|
|
94256
|
+
}
|
|
94257
|
+
/**
|
|
94258
|
+
* Everything that references this cell — directly, or through the column or
|
|
94259
|
+
* row node that contains it. The single place `dependents` is read, so a
|
|
94260
|
+
* whole-column reference cannot be forgotten by one traversal and honoured by
|
|
94261
|
+
* another.
|
|
94262
|
+
*/
|
|
94263
|
+
dependentsOf(cell) {
|
|
94264
|
+
const direct = this.dependents.get(cell);
|
|
94265
|
+
if (isCoarseKey(cell)) return direct ?? EMPTY;
|
|
94266
|
+
const coarse = coarseKeysFor(cell);
|
|
94267
|
+
let widened;
|
|
94268
|
+
for (const key of coarse) {
|
|
94269
|
+
const via = this.dependents.get(key);
|
|
94270
|
+
if (!via || via.size === 0) continue;
|
|
94271
|
+
widened ??= new Set(direct);
|
|
94272
|
+
for (const d of via) widened.add(d);
|
|
94273
|
+
}
|
|
94274
|
+
return widened ?? direct ?? EMPTY;
|
|
94169
94275
|
}
|
|
94170
94276
|
/** Get all cells that need recalculation when the given cells change, in topological order. */
|
|
94171
94277
|
getRecalcOrder(changed) {
|
|
94172
|
-
const changedSet = new Set(changed);
|
|
94173
94278
|
const affected = /* @__PURE__ */ new Set();
|
|
94174
94279
|
const queue = [...changed];
|
|
94175
94280
|
let head = 0;
|
|
94176
94281
|
while (head < queue.length) {
|
|
94177
94282
|
const cell = queue[head++];
|
|
94178
|
-
const
|
|
94179
|
-
if (!deps) continue;
|
|
94180
|
-
for (const dep of deps) {
|
|
94283
|
+
for (const dep of this.dependentsOf(cell)) {
|
|
94181
94284
|
if (!affected.has(dep)) {
|
|
94182
94285
|
affected.add(dep);
|
|
94183
94286
|
queue.push(dep);
|
|
@@ -94186,15 +94289,11 @@ var DependencyGraph = class {
|
|
|
94186
94289
|
}
|
|
94187
94290
|
if (affected.size === 0) return [];
|
|
94188
94291
|
const inDegree = /* @__PURE__ */ new Map();
|
|
94292
|
+
for (const cell of affected) inDegree.set(cell, 0);
|
|
94189
94293
|
for (const cell of affected) {
|
|
94190
|
-
|
|
94191
|
-
|
|
94192
|
-
if (prec) {
|
|
94193
|
-
for (const p of prec) {
|
|
94194
|
-
if (affected.has(p)) count++;
|
|
94195
|
-
}
|
|
94294
|
+
for (const dep of this.dependentsOf(cell)) {
|
|
94295
|
+
if (affected.has(dep)) inDegree.set(dep, (inDegree.get(dep) ?? 0) + 1);
|
|
94196
94296
|
}
|
|
94197
|
-
inDegree.set(cell, count);
|
|
94198
94297
|
}
|
|
94199
94298
|
const result = [];
|
|
94200
94299
|
const ready = [];
|
|
@@ -94205,9 +94304,7 @@ var DependencyGraph = class {
|
|
|
94205
94304
|
while (readyHead < ready.length) {
|
|
94206
94305
|
const cell = ready[readyHead++];
|
|
94207
94306
|
result.push(cell);
|
|
94208
|
-
const
|
|
94209
|
-
if (!deps) continue;
|
|
94210
|
-
for (const dep of deps) {
|
|
94307
|
+
for (const dep of this.dependentsOf(cell)) {
|
|
94211
94308
|
if (!affected.has(dep)) continue;
|
|
94212
94309
|
const newDeg = (inDegree.get(dep) ?? 1) - 1;
|
|
94213
94310
|
inDegree.set(dep, newDeg);
|
|
@@ -94228,12 +94325,13 @@ var DependencyGraph = class {
|
|
|
94228
94325
|
const stack = new Array();
|
|
94229
94326
|
const prec = this.precedents.get(cell);
|
|
94230
94327
|
if (!prec) return null;
|
|
94328
|
+
const originCoarse = new Set(coarseKeysFor(cell));
|
|
94231
94329
|
for (const p of prec) {
|
|
94232
94330
|
stack.push({ cell: p, path: [cell, p] });
|
|
94233
94331
|
}
|
|
94234
94332
|
while (stack.length > 0) {
|
|
94235
94333
|
const { cell: current, path: path12 } = stack.pop();
|
|
94236
|
-
if (current === cell) return path12;
|
|
94334
|
+
if (current === cell || originCoarse.has(current)) return path12;
|
|
94237
94335
|
if (visited.has(current)) continue;
|
|
94238
94336
|
visited.add(current);
|
|
94239
94337
|
const nextPrec = this.precedents.get(current);
|
|
@@ -94346,6 +94444,20 @@ function parseTableRange(ref2) {
|
|
|
94346
94444
|
};
|
|
94347
94445
|
}
|
|
94348
94446
|
|
|
94447
|
+
// ../xlsx/src/blank_comparison.ts
|
|
94448
|
+
var import_operators = __toESM(require_operators(), 1);
|
|
94449
|
+
var patched = false;
|
|
94450
|
+
function applyExcelBlankSemantics() {
|
|
94451
|
+
if (patched) return;
|
|
94452
|
+
patched = true;
|
|
94453
|
+
const original = import_operators.Infix.compareOp;
|
|
94454
|
+
import_operators.Infix.compareOp = (value1, infix, value2, isArray1, isArray22) => {
|
|
94455
|
+
if (value1 == null && typeof value2 === "string") value1 = "";
|
|
94456
|
+
if (value2 == null && typeof value1 === "string") value2 = "";
|
|
94457
|
+
return original(value1, infix, value2, isArray1, isArray22);
|
|
94458
|
+
};
|
|
94459
|
+
}
|
|
94460
|
+
|
|
94349
94461
|
// ../xlsx/src/formula_engine.ts
|
|
94350
94462
|
var unwrapArg = (a) => a !== null && typeof a === "object" && "value" in a ? a.value : a;
|
|
94351
94463
|
function adaptFormulajs(fn) {
|
|
@@ -94388,9 +94500,24 @@ function formulajsBridgeWithContext() {
|
|
|
94388
94500
|
bridgeCtxCache = withCtx;
|
|
94389
94501
|
return withCtx;
|
|
94390
94502
|
}
|
|
94503
|
+
var MAX_EXPANDED_CELLS = 1e4;
|
|
94391
94504
|
var FormulaEngine = class _FormulaEngine {
|
|
94392
94505
|
constructor(workbook) {
|
|
94393
94506
|
this.graph = new DependencyGraph();
|
|
94507
|
+
/**
|
|
94508
|
+
* Formula cells (by key) whose text the parser could not read AT ALL, found
|
|
94509
|
+
* while building the graph from a loaded file.
|
|
94510
|
+
*
|
|
94511
|
+
* They are held apart from every other failure because the honest response
|
|
94512
|
+
* differs: a formula that evaluates TO an error has been understood, so
|
|
94513
|
+
* writing `#DIV/0!` reports a real answer, while one we cannot parse has not
|
|
94514
|
+
* been — and overwriting the value its author computed would replace a
|
|
94515
|
+
* correct number with a guess. These keep their cached value and are named in
|
|
94516
|
+
* the report instead. The engine's own rule, one level down: degrade what you
|
|
94517
|
+
* cannot draw, refuse what you cannot read.
|
|
94518
|
+
*/
|
|
94519
|
+
this.unreadableFormulas = /* @__PURE__ */ new Set();
|
|
94520
|
+
applyExcelBlankSemantics();
|
|
94394
94521
|
this.workbook = workbook;
|
|
94395
94522
|
this.parser = new import_fast_formula_parser.default({
|
|
94396
94523
|
onCell: (ref2) => this.resolveCell(ref2),
|
|
@@ -94411,11 +94538,13 @@ var FormulaEngine = class _FormulaEngine {
|
|
|
94411
94538
|
/** Build dependency graph from all formula cells in the workbook. */
|
|
94412
94539
|
buildGraph() {
|
|
94413
94540
|
this.graph.clear();
|
|
94541
|
+
this.unreadableFormulas.clear();
|
|
94414
94542
|
for (const sheet of this.workbook.sheets) {
|
|
94415
94543
|
for (const [ref2, cell] of sheet.cells) {
|
|
94416
94544
|
if (!cell.formula) continue;
|
|
94417
94545
|
const key = cellKey({ sheet: sheet.name, ref: ref2 });
|
|
94418
|
-
const deps = this.
|
|
94546
|
+
const { deps, readable } = this.analyzeFormula(cell.formula, sheet.name);
|
|
94547
|
+
if (!readable) this.unreadableFormulas.add(key);
|
|
94419
94548
|
this.graph.setDependencies(key, deps);
|
|
94420
94549
|
}
|
|
94421
94550
|
}
|
|
@@ -94471,8 +94600,13 @@ var FormulaEngine = class _FormulaEngine {
|
|
|
94471
94600
|
onCellChanged(sheetName, ref2, formula) {
|
|
94472
94601
|
const key = cellKey({ sheet: sheetName, ref: ref2 });
|
|
94473
94602
|
if (formula) {
|
|
94474
|
-
const deps = this.
|
|
94603
|
+
const { deps, readable } = this.analyzeFormula(formula, sheetName);
|
|
94475
94604
|
this.graph.setDependencies(key, deps);
|
|
94605
|
+
this.unreadableFormulas.delete(key);
|
|
94606
|
+
if (!readable) {
|
|
94607
|
+
this.setCellError(key, "#ERROR!");
|
|
94608
|
+
return;
|
|
94609
|
+
}
|
|
94476
94610
|
const cycle = this.graph.detectCycle(key);
|
|
94477
94611
|
if (cycle) {
|
|
94478
94612
|
this.setCellError(key, "#CIRC!");
|
|
@@ -94481,6 +94615,7 @@ var FormulaEngine = class _FormulaEngine {
|
|
|
94481
94615
|
this.evaluateCell(key);
|
|
94482
94616
|
} else {
|
|
94483
94617
|
this.graph.removeCell(key);
|
|
94618
|
+
this.unreadableFormulas.delete(key);
|
|
94484
94619
|
}
|
|
94485
94620
|
const order = this.graph.getRecalcOrder([key]);
|
|
94486
94621
|
for (const depKey of order) {
|
|
@@ -94497,6 +94632,7 @@ var FormulaEngine = class _FormulaEngine {
|
|
|
94497
94632
|
// ===========================================================================
|
|
94498
94633
|
/** Evaluate a single formula cell and update its value. */
|
|
94499
94634
|
evaluateCell(key) {
|
|
94635
|
+
if (this.unreadableFormulas.has(key)) return;
|
|
94500
94636
|
const { sheet: sheetName, ref: ref2 } = parseCellKey(key);
|
|
94501
94637
|
const sheetIndex = this.workbook.sheets.findIndex((s) => s.name === sheetName);
|
|
94502
94638
|
if (sheetIndex < 0) return;
|
|
@@ -94545,26 +94681,60 @@ var FormulaEngine = class _FormulaEngine {
|
|
|
94545
94681
|
// ===========================================================================
|
|
94546
94682
|
/** Extract cell/range references from a formula string. Returns empty array for unparseable formulas. */
|
|
94547
94683
|
extractDependencies(formula, currentSheet) {
|
|
94548
|
-
|
|
94549
|
-
|
|
94550
|
-
|
|
94684
|
+
return this.analyzeFormula(formula, currentSheet).deps;
|
|
94685
|
+
}
|
|
94686
|
+
/**
|
|
94687
|
+
* Dependencies plus whether the parser could read the formula at all.
|
|
94688
|
+
*
|
|
94689
|
+
* `fast-formula-parser` reports a formula it cannot LEX by throwing, the same
|
|
94690
|
+
* channel it uses for `#VALUE!`, so the lexer failure of a single cell used to
|
|
94691
|
+
* escape `buildGraph` and abort the recompute for the entire workbook —
|
|
94692
|
+
* observed on a real workbook whose `VLOOKUP` pointed at another file
|
|
94693
|
+
* (`'[1]DATA'!B:C`, an external-workbook reference the lexer has no rule for).
|
|
94694
|
+
* Anything the grammar does not cover lands here the same way, so this catches
|
|
94695
|
+
* the class rather than that one syntax.
|
|
94696
|
+
*/
|
|
94697
|
+
analyzeFormula(formula, currentSheet) {
|
|
94698
|
+
let deps;
|
|
94699
|
+
try {
|
|
94700
|
+
const sheet = this.workbook.sheets.find((s) => s.name === currentSheet);
|
|
94701
|
+
const expanded = sheet ? expandStructuredRefs(formula, sheet.tables, 1) : formula;
|
|
94702
|
+
deps = this.depParser.parse(expanded, { sheet: currentSheet, row: 1, col: 1 });
|
|
94703
|
+
} catch {
|
|
94704
|
+
return { deps: [], readable: false };
|
|
94705
|
+
}
|
|
94551
94706
|
const refs = [];
|
|
94552
94707
|
for (const dep of deps) {
|
|
94553
94708
|
const depSheet = dep.sheet ?? currentSheet;
|
|
94554
94709
|
if (dep.from && dep.to) {
|
|
94555
|
-
const
|
|
94556
|
-
|
|
94557
|
-
|
|
94558
|
-
|
|
94710
|
+
const rows = dep.to.row - dep.from.row + 1;
|
|
94711
|
+
const cols = dep.to.col - dep.from.col + 1;
|
|
94712
|
+
if (rows * cols > MAX_EXPANDED_CELLS) {
|
|
94713
|
+
if (cols <= rows) {
|
|
94714
|
+
for (let c = dep.from.col; c <= dep.to.col; c++) {
|
|
94715
|
+
refs.push(columnKey(depSheet, colNumToLetters(c)));
|
|
94716
|
+
}
|
|
94717
|
+
} else {
|
|
94718
|
+
for (let r = dep.from.row; r <= dep.to.row; r++) {
|
|
94719
|
+
refs.push(rowKey(depSheet, r));
|
|
94720
|
+
}
|
|
94721
|
+
}
|
|
94722
|
+
continue;
|
|
94723
|
+
}
|
|
94724
|
+
for (let r = dep.from.row; r <= dep.to.row; r++) {
|
|
94725
|
+
for (let c = dep.from.col; c <= dep.to.col; c++) {
|
|
94559
94726
|
refs.push(cellKey({ sheet: depSheet, ref: rowColToRef(r, c) }));
|
|
94560
|
-
count++;
|
|
94561
94727
|
}
|
|
94562
94728
|
}
|
|
94563
94729
|
} else if (dep.row != null && dep.col != null) {
|
|
94564
94730
|
refs.push(cellKey({ sheet: depSheet, ref: rowColToRef(dep.row, dep.col) }));
|
|
94565
94731
|
}
|
|
94566
94732
|
}
|
|
94567
|
-
return refs;
|
|
94733
|
+
return { deps: refs, readable: true };
|
|
94734
|
+
}
|
|
94735
|
+
/** Formula cells the parser could not read, by key. Read by the recompute report. */
|
|
94736
|
+
unreadable() {
|
|
94737
|
+
return this.unreadableFormulas;
|
|
94568
94738
|
}
|
|
94569
94739
|
// ===========================================================================
|
|
94570
94740
|
// Cell value resolution callbacks
|
|
@@ -94601,8 +94771,7 @@ var FormulaEngine = class _FormulaEngine {
|
|
|
94601
94771
|
for (let r = ref2.from.row; r <= lastRow; r++) {
|
|
94602
94772
|
const row = [];
|
|
94603
94773
|
for (let c = ref2.from.col; c <= lastCol; c++) {
|
|
94604
|
-
|
|
94605
|
-
row.push(cell?.value ?? null);
|
|
94774
|
+
row.push(sheet.getCellAt(r, c)?.value ?? null);
|
|
94606
94775
|
}
|
|
94607
94776
|
result.push(row);
|
|
94608
94777
|
}
|
|
@@ -94686,15 +94855,19 @@ function recomputeAll(workbook) {
|
|
|
94686
94855
|
const engine = new FormulaEngine(workbook);
|
|
94687
94856
|
engine.buildGraph();
|
|
94688
94857
|
engine.evaluateAll();
|
|
94689
|
-
return collectRecomputeReport(workbook);
|
|
94858
|
+
return collectRecomputeReport(workbook, engine.unreadable());
|
|
94690
94859
|
}
|
|
94691
|
-
function collectRecomputeReport(workbook) {
|
|
94860
|
+
function collectRecomputeReport(workbook, unreadable) {
|
|
94692
94861
|
let total = 0;
|
|
94693
94862
|
const unevaluated = [];
|
|
94694
94863
|
for (const sheet of workbook.sheets) {
|
|
94695
94864
|
for (const [ref2, cell] of sheet.cells) {
|
|
94696
94865
|
if (!cell.formula) continue;
|
|
94697
94866
|
total++;
|
|
94867
|
+
if (unreadable.has(cellKey({ sheet: sheet.name, ref: ref2 }))) {
|
|
94868
|
+
unevaluated.push({ sheet: sheet.name, ref: ref2, formula: cell.formula, error: "#UNREADABLE!" });
|
|
94869
|
+
continue;
|
|
94870
|
+
}
|
|
94698
94871
|
if (cell.value === null) {
|
|
94699
94872
|
unevaluated.push({ sheet: sheet.name, ref: ref2, formula: cell.formula, error: cell.error ?? "#ERROR!" });
|
|
94700
94873
|
}
|
|
@@ -95632,9 +95805,9 @@ function applySheetCells(workbook, sheetIndex, def) {
|
|
|
95632
95805
|
}
|
|
95633
95806
|
}
|
|
95634
95807
|
if (def.rowHeights) {
|
|
95635
|
-
for (const [
|
|
95636
|
-
const row = Number(
|
|
95637
|
-
if (!Number.isInteger(row) || row < 1) fail2(`Invalid row in sheet "${def.name}".rowHeights: ${
|
|
95808
|
+
for (const [rowKey2, height] of Object.entries(def.rowHeights)) {
|
|
95809
|
+
const row = Number(rowKey2);
|
|
95810
|
+
if (!Number.isInteger(row) || row < 1) fail2(`Invalid row in sheet "${def.name}".rowHeights: ${rowKey2}`);
|
|
95638
95811
|
sheet.rowHeights.set(row, height);
|
|
95639
95812
|
}
|
|
95640
95813
|
}
|
package/dist/src/client.d.ts
CHANGED
|
@@ -224,6 +224,10 @@ export declare class LoticsClient {
|
|
|
224
224
|
getApp(app_id: string): Promise<{
|
|
225
225
|
id: string;
|
|
226
226
|
name: string;
|
|
227
|
+
/** What the app is for. Heads the capability catalog the member's chat agent
|
|
228
|
+
* reads every turn the app is open, so it is where a standing process is
|
|
229
|
+
* stated. Null when unset. */
|
|
230
|
+
description?: string | null;
|
|
227
231
|
workspace_id: string;
|
|
228
232
|
current_version_id: string | null;
|
|
229
233
|
/** Launcher icon — a Lucide name, or an image ref. Null when unset. */
|