@lotics/cli 0.198.0 → 0.204.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/README.md +2 -2
- package/dist/probe_page.js +59 -7
- package/dist/src/cli.js +2956 -451
- package/dist/src/client.d.ts +29 -0
- package/dist/src/client.js +17 -0
- package/docs/building_an_app.md +23 -9
- package/docs/cli_reference.md +10 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -169,7 +169,7 @@ lotics org use acme # switch active org by name (or org id)
|
|
|
169
169
|
lotics org use "Acme Corp" # names are case-insensitive
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
-
The org **id** is the identity; the name is
|
|
172
|
+
The org **id** is the identity; the name is the label the key was saved under, and it is yours — renaming the org in the app never moves it, so the scripts scoping by it keep running. `lotics auth whoami` learns the new name and records it beside the label; both resolve, and `lotics org` prints `label (server: name)` while they differ. A name matching two saved orgs is refused rather than guessed; use the id.
|
|
173
173
|
|
|
174
174
|
### Working in parallel (worktrees)
|
|
175
175
|
|
|
@@ -195,7 +195,7 @@ Every command that resolves a workspace names its target before it acts — `lot
|
|
|
195
195
|
|
|
196
196
|
`LOTICS_WORKSPACE` (or `--workspace <id>` / `-w`) overrides the workspace at any level. For ephemeral or CI use, set `LOTICS_API_KEY` instead of saving anything.
|
|
197
197
|
|
|
198
|
-
**`LOTICS_ORG` is resolved once, before any command runs.** A value matching no saved credential refuses every verb with one sentence — a read, a write, and a check that needs no credential alike — and the refusal lists the orgs this machine does hold, so it is answerable without another command. It refuses even when a key arrives another way: a variable that scopes the command must not go unread while `LOTICS_API_KEY` sends the write somewhere else. When the variable resolves AND a key is supplied, the key decides the org and the command says so.
|
|
198
|
+
**`LOTICS_ORG` is resolved once, before any command runs.** A value matching no saved credential refuses every verb with one sentence — a read, a write, and a check that needs no credential alike — and the refusal lists the orgs this machine does hold, so it is answerable without another command. It refuses even when a key arrives another way: a variable that scopes the command must not go unread while `LOTICS_API_KEY` sends the write somewhere else. When the variable resolves AND a key is supplied, the key decides the org and the command says so. An org keeps the name it was saved under: a server-side rename never moves it, the new name resolves as well, and `lotics org` prints both when they differ.
|
|
199
199
|
|
|
200
200
|
### Diagnostics
|
|
201
201
|
|
package/dist/probe_page.js
CHANGED
|
@@ -98,6 +98,8 @@ var __loticsProbe = (() => {
|
|
|
98
98
|
}
|
|
99
99
|
function reportedValues() {
|
|
100
100
|
const numeric = /^[-+−]?\d[\d.,]*\s?(?:%|[A-Za-z₫đ$€]{1,4})?$/;
|
|
101
|
+
const counted = /(?:%|[A-Za-z₫đ$€]{1,4})$/;
|
|
102
|
+
const delta = /^[↑↓→▲▼]\s*[-+−]?\d[\d.,]*\s?%$/;
|
|
101
103
|
const dateLike = /\d[/:-]\d/;
|
|
102
104
|
const skipTag = /^(SCRIPT|STYLE|TITLE|META|LINK|NOSCRIPT|TEMPLATE)$/;
|
|
103
105
|
const notAValue = 'a, button, [role="tab"], [role="button"], input, th, [role="columnheader"], nav';
|
|
@@ -105,6 +107,7 @@ var __loticsProbe = (() => {
|
|
|
105
107
|
const nameReach = 360;
|
|
106
108
|
const values = [];
|
|
107
109
|
const labels = [];
|
|
110
|
+
let deltas = 0;
|
|
108
111
|
const walk = (node) => {
|
|
109
112
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
110
113
|
const text = (node.textContent || "").trim();
|
|
@@ -113,6 +116,10 @@ var __loticsProbe = (() => {
|
|
|
113
116
|
if (typeof parent.checkVisibility === "function" && !parent.checkVisibility()) return;
|
|
114
117
|
const rect = parent.getBoundingClientRect();
|
|
115
118
|
if (rect.width === 0 && rect.height === 0) return;
|
|
119
|
+
if (delta.test(text)) {
|
|
120
|
+
deltas += 1;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
116
123
|
const leaf = { text, left: Math.round(rect.left), right: Math.round(rect.right), top: Math.round(rect.top) };
|
|
117
124
|
if (!numeric.test(text) || dateLike.test(text)) {
|
|
118
125
|
labels.push(leaf);
|
|
@@ -138,6 +145,7 @@ var __loticsProbe = (() => {
|
|
|
138
145
|
}
|
|
139
146
|
const columns = [];
|
|
140
147
|
let bare = 0;
|
|
148
|
+
let quantities = 0;
|
|
141
149
|
for (const cluster of clusters) {
|
|
142
150
|
const items = [...cluster.items].sort((a, b) => a.top - b.top);
|
|
143
151
|
const ordinal = items.length >= 3 && items.every((item, index) => item.text === String(index + 1));
|
|
@@ -145,10 +153,13 @@ var __loticsProbe = (() => {
|
|
|
145
153
|
const top = items[0];
|
|
146
154
|
const above = labels.filter((label) => label.top < top.top && top.top - label.top <= nameReach && label.left < cluster.right && label.right > cluster.left).sort((a, b) => b.top - a.top)[0];
|
|
147
155
|
const beside = labels.filter((label) => Math.abs(label.top - top.top) <= 8 && label.right <= top.left).sort((a, b) => b.right - a.right)[0];
|
|
156
|
+
const scaled = items.filter((item) => counted.test(item.text)).length;
|
|
148
157
|
bare += items.length;
|
|
158
|
+
quantities += scaled;
|
|
149
159
|
columns.push({
|
|
150
160
|
slot: (above ?? beside)?.text.slice(0, 40) ?? "",
|
|
151
161
|
size: items.length,
|
|
162
|
+
quantities: scaled,
|
|
152
163
|
samples: items.slice(0, 4).map((item) => item.text)
|
|
153
164
|
});
|
|
154
165
|
}
|
|
@@ -157,13 +168,37 @@ var __loticsProbe = (() => {
|
|
|
157
168
|
const r = el.getBoundingClientRect();
|
|
158
169
|
return r.width > 0 && r.height > 0;
|
|
159
170
|
});
|
|
171
|
+
const reads = (el) => {
|
|
172
|
+
const role = el.getAttribute("role");
|
|
173
|
+
if (role !== "meter" && role !== "progressbar") return true;
|
|
174
|
+
const stated = (name) => {
|
|
175
|
+
const raw = el.getAttribute(name);
|
|
176
|
+
return raw === null || raw.trim() === "" ? Number.NaN : Number(raw);
|
|
177
|
+
};
|
|
178
|
+
const bound = (name, fallback) => {
|
|
179
|
+
const value = stated(name);
|
|
180
|
+
return Number.isFinite(value) ? value : fallback;
|
|
181
|
+
};
|
|
182
|
+
const now = stated("aria-valuenow");
|
|
183
|
+
if (!Number.isFinite(now)) return false;
|
|
184
|
+
const floor = bound("aria-valuemin", 0);
|
|
185
|
+
const top = bound("aria-valuemax", 100);
|
|
186
|
+
return top > floor && now < top;
|
|
187
|
+
};
|
|
160
188
|
const pictures = Array.from(document.querySelectorAll('[role="img"], svg, img')).filter((el) => {
|
|
161
189
|
if (el.getAttribute("aria-hidden") === "true") return false;
|
|
162
190
|
const r = el.getBoundingClientRect();
|
|
163
191
|
return r.width >= pictureMin && r.height >= pictureMin;
|
|
164
192
|
});
|
|
165
|
-
const encoded = drawn.length + pictures.length;
|
|
166
|
-
return {
|
|
193
|
+
const encoded = drawn.filter(reads).length + pictures.length + deltas;
|
|
194
|
+
return {
|
|
195
|
+
total: bare + encoded,
|
|
196
|
+
bare,
|
|
197
|
+
quantities,
|
|
198
|
+
encoded,
|
|
199
|
+
devices: drawn.length + pictures.length + deltas,
|
|
200
|
+
columns
|
|
201
|
+
};
|
|
167
202
|
}
|
|
168
203
|
function internalIds() {
|
|
169
204
|
const skipTag = /^(SCRIPT|STYLE|TITLE|META|LINK|NOSCRIPT|TEMPLATE)$/;
|
|
@@ -199,6 +234,8 @@ var __loticsProbe = (() => {
|
|
|
199
234
|
}
|
|
200
235
|
function truncatedLeaves() {
|
|
201
236
|
const skipTag = /^(SCRIPT|STYLE|TITLE|META|LINK|NOSCRIPT|TEMPLATE)$/;
|
|
237
|
+
const clippedAway = /^inset\(\s*50%/;
|
|
238
|
+
const breakable = /[\t\n\v\f\r \u200b\u3000]/;
|
|
202
239
|
const cut = [];
|
|
203
240
|
let total = 0;
|
|
204
241
|
for (const el of Array.from(document.querySelectorAll("*"))) {
|
|
@@ -206,8 +243,17 @@ var __loticsProbe = (() => {
|
|
|
206
243
|
const text = (el.textContent || "").trim();
|
|
207
244
|
if (!text) continue;
|
|
208
245
|
if (typeof el.checkVisibility === "function" && !el.checkVisibility()) continue;
|
|
246
|
+
const style = getComputedStyle(el);
|
|
247
|
+
const box = el.getBoundingClientRect();
|
|
248
|
+
if (box.width <= 1 && box.height <= 1) continue;
|
|
249
|
+
if (clippedAway.test(style.getPropertyValue("clip-path"))) continue;
|
|
209
250
|
total += 1;
|
|
210
|
-
if (
|
|
251
|
+
if (style.overflow === "visible") continue;
|
|
252
|
+
const states = (property) => {
|
|
253
|
+
const value = style.getPropertyValue(property);
|
|
254
|
+
return value !== "" && value !== "none";
|
|
255
|
+
};
|
|
256
|
+
if ((states("-webkit-line-clamp") || states("line-clamp")) && breakable.test(text)) continue;
|
|
211
257
|
if (el.scrollWidth > el.clientWidth + 1) {
|
|
212
258
|
cut.push({ text: text.slice(0, 40), axis: "width", has: el.clientWidth, needs: el.scrollWidth });
|
|
213
259
|
} else if (el.scrollHeight > el.clientHeight + 1) {
|
|
@@ -266,6 +312,7 @@ var __loticsProbe = (() => {
|
|
|
266
312
|
if (depth === 0 || el.children.length === 0) return null;
|
|
267
313
|
return leafOf(el.children[0], depth - 1);
|
|
268
314
|
};
|
|
315
|
+
const sameColumn = 8;
|
|
269
316
|
let total = 0;
|
|
270
317
|
const overDemoted = [];
|
|
271
318
|
for (const el of Array.from(document.querySelectorAll("div"))) {
|
|
@@ -279,6 +326,7 @@ var __loticsProbe = (() => {
|
|
|
279
326
|
const a = first.getBoundingClientRect();
|
|
280
327
|
const b = second.getBoundingClientRect();
|
|
281
328
|
if (b.top < a.bottom - 2) continue;
|
|
329
|
+
if (Math.abs(b.left - a.left) > sameColumn) continue;
|
|
282
330
|
if (a.height === 0 || b.height === 0) continue;
|
|
283
331
|
total += 1;
|
|
284
332
|
if (px(supporting) < px(subject) && weight(subject) > weight(supporting)) {
|
|
@@ -350,7 +398,7 @@ var __loticsProbe = (() => {
|
|
|
350
398
|
},
|
|
351
399
|
reported_values: {
|
|
352
400
|
section: "9",
|
|
353
|
-
fix: "Encode the column the screen is ABOUT \u2014 a mark, a meter, a chart or a picture \u2014 and leave the rest as text."
|
|
401
|
+
fix: "Encode the column the screen is ABOUT \u2014 a mark, a meter, a chart or a picture \u2014 and leave the rest as text; a meter whose maximum is the bound it alarms at reads the same on every row, so it encodes nothing."
|
|
354
402
|
},
|
|
355
403
|
internal_ids: {
|
|
356
404
|
section: "8",
|
|
@@ -362,7 +410,7 @@ var __loticsProbe = (() => {
|
|
|
362
410
|
},
|
|
363
411
|
truncation: {
|
|
364
412
|
section: "8g",
|
|
365
|
-
fix: "Shed a trailing column or
|
|
413
|
+
fix: "Shed a trailing column, or widen this one until the value fits \u2014 a date, a code or a figure cut inside itself reads as a different one."
|
|
366
414
|
},
|
|
367
415
|
banned_glyph: {
|
|
368
416
|
section: "10",
|
|
@@ -395,6 +443,7 @@ var __loticsProbe = (() => {
|
|
|
395
443
|
var MONEY_EDGES_ALLOWED = 2;
|
|
396
444
|
var MONEY_COLUMN_MIN = 3;
|
|
397
445
|
var BARE_VALUES_FLOOR = 8;
|
|
446
|
+
var QUANTITY_COLUMN_MIN = 2;
|
|
398
447
|
function run(options) {
|
|
399
448
|
const findings = [];
|
|
400
449
|
const money = moneyColumns();
|
|
@@ -409,14 +458,15 @@ var __loticsProbe = (() => {
|
|
|
409
458
|
});
|
|
410
459
|
}
|
|
411
460
|
const values = reportedValues();
|
|
412
|
-
|
|
461
|
+
const measurable = values.columns.some((column) => column.quantities >= QUANTITY_COLUMN_MIN) || values.devices > 0;
|
|
462
|
+
if (values.bare >= BARE_VALUES_FLOOR && values.encoded === 0 && measurable) {
|
|
413
463
|
findings.push({
|
|
414
464
|
probe: "reported_values",
|
|
415
465
|
...signature("reported_values"),
|
|
416
466
|
total: values.total,
|
|
417
467
|
count: values.bare,
|
|
418
468
|
detail: [
|
|
419
|
-
`${values.bare} values as bare text,
|
|
469
|
+
values.devices === 0 ? `${values.bare} values as bare text, nothing drawn as a mark, meter, chart or picture` : `${values.bare} values as bare text; ${values.devices} drawn, none of them reading a value`,
|
|
420
470
|
...values.columns.slice(0, 5).map((column) => ` ${column.slot === "" ? "(unnamed)" : column.slot}: ${column.size} \u2014 ${column.samples.join(", ")}`)
|
|
421
471
|
]
|
|
422
472
|
});
|
|
@@ -487,7 +537,9 @@ var __loticsProbe = (() => {
|
|
|
487
537
|
leaves: textLeafCount(),
|
|
488
538
|
money: money.total,
|
|
489
539
|
bare: values.bare,
|
|
540
|
+
quantities: values.quantities,
|
|
490
541
|
encoded: values.encoded,
|
|
542
|
+
devices: values.devices,
|
|
491
543
|
strips: strips.counted
|
|
492
544
|
}
|
|
493
545
|
};
|