@azure-id/orc 0.55.0 → 0.56.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/CHANGELOG.md +2139 -2017
- package/README-id.md +592 -521
- package/README.md +610 -485
- package/bin/cli.js +29236 -29050
- package/bin/webui/i18n/en/overview.json +90 -89
- package/bin/webui/i18n/id/overview.json +90 -89
- package/bin/webui/js/03-md.js +9 -3
- package/bin/webui/js/panels/overview.js +453 -447
- package/package.json +39 -39
- package/templates/skills/orc-fast/SKILL.md +187 -188
- package/templates/skills/orc-quick/SKILL.md +313 -306
|
@@ -1,447 +1,453 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/* panels/overview.js — orc ui client
|
|
3
|
-
FINDING_ROUTE (a caution routes to the panel that can CLEAR it), the
|
|
4
|
-
attention list, the stat tiles.
|
|
5
|
-
|
|
6
|
-
Loaded by app.html in the order its numeric prefix names. Classic
|
|
7
|
-
script, no import/export: an ES module import carries no query string,
|
|
8
|
-
and every static request here needs the per-launch session token. */
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/* ================================================================ OVERVIEW == */
|
|
12
|
-
|
|
13
|
-
/* WHERE A PROBLEM IS ACTUALLY FIXED (v0.43.6).
|
|
14
|
-
`orc doctor` reports every problem in one list, and the panel used to send
|
|
15
|
-
the whole list to Maintenance with a single "Open Maintenance" button. That
|
|
16
|
-
is right for the install-footprint findings — version skew, orphans, missing
|
|
17
|
-
files, unwired hooks — because `orc update` / `doctor --fix` really is where
|
|
18
|
-
those are repaired. It was WRONG for the ones whose fix lives on another
|
|
19
|
-
panel, and `diy-stale` was the one people hit: the flow is recompiled with
|
|
20
|
-
`orc diy compile`, which is a button on FLOW. Sending them to Maintenance
|
|
21
|
-
pointed at a page with no control for the thing it was complaining about.
|
|
22
|
-
|
|
23
|
-
So the routing is a table keyed on the finding id the CLI already emits.
|
|
24
|
-
`null` means there is nothing to press anywhere — a dangling trace pointer
|
|
25
|
-
clears itself on the next run, and offering a button for it would be a lie. */
|
|
26
|
-
const FINDING_ROUTE = {
|
|
27
|
-
"diy-stale": { panel: "flow", cta: "overview.item.diyStale.cta" },
|
|
28
|
-
"trace-pointer-dangling": { panel: null },
|
|
29
|
-
// v0.46.0. Correct by the rule, not by default: `export-stale` is cleared by
|
|
30
|
-
// `orc export`, which IS a CLI write Maintenance can run — so Maintenance is
|
|
31
|
-
// genuinely the panel that can clear it, not merely the fallback.
|
|
32
|
-
"export-stale": { panel: "maintenance", cta: "overview.item.exportStale.cta" },
|
|
33
|
-
"pact-broken": { panel: "pact", cta: "overview.item.pactBroken.cta" },
|
|
34
|
-
// v0.48.1. Same rule again: `orc doc audit` and `orc doc ship` are both on
|
|
35
|
-
// DOCS, so that is where a drifted document has to send you.
|
|
36
|
-
"doc-drifted": { panel: "docs", cta: "overview.item.docDrifted.cta" },
|
|
37
|
-
"boundary-refuse": { panel: "boundary", cta: "overview.item.boundaryRefuse.cta" },
|
|
38
|
-
// v0.49.1. Same rule a third time. `orc wiki sync` is a BUTTON on Knowledge
|
|
39
|
-
// and it is free, and `orc wiki plan` (which ranks the refresh) is the card
|
|
40
|
-
// directly above it — so Knowledge is genuinely the panel that can clear
|
|
41
|
-
// both, not merely the fallback.
|
|
42
|
-
"wiki-unregistered": { panel: "knowledge", cta: "overview.item.wikiUnregistered.cta" },
|
|
43
|
-
"wiki-debt": { panel: "knowledge", cta: "overview.item.wikiDebt.cta" },
|
|
44
|
-
// v0.50.0. `orc doctor` collapses the whole `orc extra` subsystem into ONE
|
|
45
|
-
// finding carrying the count and the ids — a doctor that recited eleven ids
|
|
46
|
-
// for a subsystem most repos never arm is a doctor people learn to scroll
|
|
47
|
-
// past. It routes to Extra because that is where every one of them is
|
|
48
|
-
// rendered beside the connection it is about, and where the connection test
|
|
49
|
-
// that clears most of them lives.
|
|
50
|
-
"extra-findings": { panel: "extra", cta: "overview.item.extraFindings.cta" },
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
"
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
vt.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
b
|
|
211
|
-
b.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
b
|
|
253
|
-
b.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
row.append(
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
row
|
|
433
|
-
row.
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
/* panels/overview.js — orc ui client
|
|
3
|
+
FINDING_ROUTE (a caution routes to the panel that can CLEAR it), the
|
|
4
|
+
attention list, the stat tiles.
|
|
5
|
+
|
|
6
|
+
Loaded by app.html in the order its numeric prefix names. Classic
|
|
7
|
+
script, no import/export: an ES module import carries no query string,
|
|
8
|
+
and every static request here needs the per-launch session token. */
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/* ================================================================ OVERVIEW == */
|
|
12
|
+
|
|
13
|
+
/* WHERE A PROBLEM IS ACTUALLY FIXED (v0.43.6).
|
|
14
|
+
`orc doctor` reports every problem in one list, and the panel used to send
|
|
15
|
+
the whole list to Maintenance with a single "Open Maintenance" button. That
|
|
16
|
+
is right for the install-footprint findings — version skew, orphans, missing
|
|
17
|
+
files, unwired hooks — because `orc update` / `doctor --fix` really is where
|
|
18
|
+
those are repaired. It was WRONG for the ones whose fix lives on another
|
|
19
|
+
panel, and `diy-stale` was the one people hit: the flow is recompiled with
|
|
20
|
+
`orc diy compile`, which is a button on FLOW. Sending them to Maintenance
|
|
21
|
+
pointed at a page with no control for the thing it was complaining about.
|
|
22
|
+
|
|
23
|
+
So the routing is a table keyed on the finding id the CLI already emits.
|
|
24
|
+
`null` means there is nothing to press anywhere — a dangling trace pointer
|
|
25
|
+
clears itself on the next run, and offering a button for it would be a lie. */
|
|
26
|
+
const FINDING_ROUTE = {
|
|
27
|
+
"diy-stale": { panel: "flow", cta: "overview.item.diyStale.cta" },
|
|
28
|
+
"trace-pointer-dangling": { panel: null },
|
|
29
|
+
// v0.46.0. Correct by the rule, not by default: `export-stale` is cleared by
|
|
30
|
+
// `orc export`, which IS a CLI write Maintenance can run — so Maintenance is
|
|
31
|
+
// genuinely the panel that can clear it, not merely the fallback.
|
|
32
|
+
"export-stale": { panel: "maintenance", cta: "overview.item.exportStale.cta" },
|
|
33
|
+
"pact-broken": { panel: "pact", cta: "overview.item.pactBroken.cta" },
|
|
34
|
+
// v0.48.1. Same rule again: `orc doc audit` and `orc doc ship` are both on
|
|
35
|
+
// DOCS, so that is where a drifted document has to send you.
|
|
36
|
+
"doc-drifted": { panel: "docs", cta: "overview.item.docDrifted.cta" },
|
|
37
|
+
"boundary-refuse": { panel: "boundary", cta: "overview.item.boundaryRefuse.cta" },
|
|
38
|
+
// v0.49.1. Same rule a third time. `orc wiki sync` is a BUTTON on Knowledge
|
|
39
|
+
// and it is free, and `orc wiki plan` (which ranks the refresh) is the card
|
|
40
|
+
// directly above it — so Knowledge is genuinely the panel that can clear
|
|
41
|
+
// both, not merely the fallback.
|
|
42
|
+
"wiki-unregistered": { panel: "knowledge", cta: "overview.item.wikiUnregistered.cta" },
|
|
43
|
+
"wiki-debt": { panel: "knowledge", cta: "overview.item.wikiDebt.cta" },
|
|
44
|
+
// v0.50.0. `orc doctor` collapses the whole `orc extra` subsystem into ONE
|
|
45
|
+
// finding carrying the count and the ids — a doctor that recited eleven ids
|
|
46
|
+
// for a subsystem most repos never arm is a doctor people learn to scroll
|
|
47
|
+
// past. It routes to Extra because that is where every one of them is
|
|
48
|
+
// rendered beside the connection it is about, and where the connection test
|
|
49
|
+
// that clears most of them lives.
|
|
50
|
+
"extra-findings": { panel: "extra", cta: "overview.item.extraFindings.cta" },
|
|
51
|
+
// v0.56.0. The rule again, and this time the finding is about the TOOL rather
|
|
52
|
+
// than the project: a legacy global `orc` package owns the `orc` command and
|
|
53
|
+
// blocks every install source. `orc upgrade` is the thing that clears it (it
|
|
54
|
+
// evicts the old package first), and `upgrade` is an action row on
|
|
55
|
+
// MAINTENANCE — so that is genuinely the panel that can clear it.
|
|
56
|
+
"legacy-global-package": { panel: "maintenance", cta: "overview.item.legacyGlobalPackage.cta" },
|
|
57
|
+
};
|
|
58
|
+
const DEFAULT_FINDING_ROUTE = { panel: "maintenance", cta: "overview.item.doctor.cta" };
|
|
59
|
+
const findingRoute = (id) => FINDING_ROUTE[id] || DEFAULT_FINDING_ROUTE;
|
|
60
|
+
|
|
61
|
+
PANELS.overview = function (host) {
|
|
62
|
+
head(host, t("overview.title"), t("overview.sub"));
|
|
63
|
+
section(
|
|
64
|
+
host,
|
|
65
|
+
() => read("/api/overview").then((r) => r.data),
|
|
66
|
+
(d) => {
|
|
67
|
+
const out = frag();
|
|
68
|
+
const doctor = d.doctor || {};
|
|
69
|
+
const w = d.wiki || {};
|
|
70
|
+
const p = d.patterns || {};
|
|
71
|
+
// Rows since v0.49.2: `{ slug, updated_ms, lane }`. Everything that only
|
|
72
|
+
// wants a count still reads `.length`.
|
|
73
|
+
const waiting = d.waiting || [];
|
|
74
|
+
const findings = doctor.findings || [];
|
|
75
|
+
|
|
76
|
+
/* --- the tiles ------------------------------------------------------ */
|
|
77
|
+
const stats = el("div", "grid grid-3");
|
|
78
|
+
stats.append(
|
|
79
|
+
statTile(
|
|
80
|
+
t("overview.tile.install"),
|
|
81
|
+
doctor.ok ? t("overview.tile.installHealthy") : tn(findings.length, "overview.tile.installIssues"),
|
|
82
|
+
doctor.installed_version
|
|
83
|
+
? t("overview.tile.installNote", { payload: doctor.installed_version, cli: doctor.package_version })
|
|
84
|
+
: "",
|
|
85
|
+
doctor.ok ? "ok" : "warn",
|
|
86
|
+
doctor.ok ? null : "maintenance"
|
|
87
|
+
)
|
|
88
|
+
);
|
|
89
|
+
stats.append(
|
|
90
|
+
statTile(
|
|
91
|
+
t("overview.tile.wiki"),
|
|
92
|
+
// TIER and STATE are CLI vocabulary (FRESH / AGING / STALE /
|
|
93
|
+
// unregistered) — the words the docs and every other lane use. They
|
|
94
|
+
// are shown as-is in both languages; only the sentence under them is
|
|
95
|
+
// translated.
|
|
96
|
+
w.state === "registered" ? w.tier || t("overview.tile.wikiUnknown") : String(w.state || "none").toUpperCase(),
|
|
97
|
+
w.state === "registered"
|
|
98
|
+
? t("overview.tile.wikiNote", { docs: w.docs, scan: w.last_scan || "?" })
|
|
99
|
+
: t("overview.tile.wikiNone"),
|
|
100
|
+
w.tier === "FRESH" ? "ok" : w.tier === "AGING" ? "warn" : w.state === "none" ? "" : "bad",
|
|
101
|
+
"knowledge"
|
|
102
|
+
)
|
|
103
|
+
);
|
|
104
|
+
stats.append(
|
|
105
|
+
statTile(
|
|
106
|
+
t("overview.tile.waiting"),
|
|
107
|
+
String(waiting.length),
|
|
108
|
+
t("overview.tile.waitingNote", { n: d.runs_total || 0 }),
|
|
109
|
+
waiting.length ? "warn" : "ok",
|
|
110
|
+
"runs"
|
|
111
|
+
)
|
|
112
|
+
);
|
|
113
|
+
const langs = (p.patterns || []).map((x) => x.lang);
|
|
114
|
+
stats.append(
|
|
115
|
+
statTile(
|
|
116
|
+
t("overview.tile.patterns"),
|
|
117
|
+
String(langs.length),
|
|
118
|
+
langs.length ? langs.join(", ") : t("overview.tile.patternsNone"),
|
|
119
|
+
langs.length ? "ok" : "",
|
|
120
|
+
"knowledge"
|
|
121
|
+
)
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
/* v0.46.0 — three chips repeating the CLI's OWN state words. A chip with
|
|
125
|
+
nothing to say still renders its good state: an absent chip and a
|
|
126
|
+
healthy one must never look the same. */
|
|
127
|
+
const pc = d.pact;
|
|
128
|
+
const bc = d.boundary;
|
|
129
|
+
const wd = d.wiki_debt;
|
|
130
|
+
stats.append(
|
|
131
|
+
statTile(
|
|
132
|
+
t("overview.tile.pact"),
|
|
133
|
+
pc && pc.ok
|
|
134
|
+
? (pc.counts.BROKEN ? pc.counts.BROKEN + " BROKEN" : pc.counts.DRIFTED ? pc.counts.DRIFTED + " DRIFTED" : pc.counts.HOLDING + " HOLDING")
|
|
135
|
+
: t("overview.tile.none"),
|
|
136
|
+
pc && pc.ok ? t("overview.tile.pactNote", { n: pc.entries, u: pc.counts.UNCHECKABLE }) : t("overview.tile.pactNone"),
|
|
137
|
+
pc && pc.ok ? (pc.counts.BROKEN ? "bad" : pc.counts.DRIFTED ? "warn" : "ok") : "",
|
|
138
|
+
pc && pc.ok ? "pact" : null
|
|
139
|
+
)
|
|
140
|
+
);
|
|
141
|
+
stats.append(
|
|
142
|
+
statTile(
|
|
143
|
+
t("overview.tile.boundary"),
|
|
144
|
+
bc && bc.cards && bc.cards.length
|
|
145
|
+
? (bc.counts.REFUSE ? bc.counts.REFUSE + " REFUSE" : bc.counts.ESCALATE ? bc.counts.ESCALATE + " ESCALATE" : bc.counts.EXECUTE + " EXECUTE")
|
|
146
|
+
: t("overview.tile.none"),
|
|
147
|
+
bc && bc.cards && bc.cards.length ? t("overview.tile.boundaryNote", { n: bc.cards.length, stale: bc.stale }) : t("overview.tile.boundaryNone"),
|
|
148
|
+
bc && bc.cards && bc.cards.length ? (bc.counts.REFUSE ? "bad" : bc.counts.ESCALATE ? "warn" : "ok") : "",
|
|
149
|
+
bc && bc.cards && bc.cards.length ? "boundary" : null
|
|
150
|
+
)
|
|
151
|
+
);
|
|
152
|
+
stats.append(
|
|
153
|
+
statTile(
|
|
154
|
+
t("overview.tile.debt"),
|
|
155
|
+
wd && wd.ok && wd.pending ? String(wd.pending) : "0",
|
|
156
|
+
wd && wd.ok && wd.pending
|
|
157
|
+
? t("overview.tile.debtNote", { tier: wd.tier, tok: kTokUi((wd.tokens || {}).input + (wd.tokens || {}).cache_write + (wd.tokens || {}).cache_read + (wd.tokens || {}).output) })
|
|
158
|
+
: t("overview.tile.debtNone"),
|
|
159
|
+
wd && wd.ok && wd.pending ? "warn" : "ok",
|
|
160
|
+
"knowledge"
|
|
161
|
+
)
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
// The version check crosses the network, so it must never hold up the
|
|
165
|
+
// tiles beside it: the tile renders in its pending state and fills itself
|
|
166
|
+
// in when the answer lands.
|
|
167
|
+
const vt = statTile(t("overview.tile.version"), t("common.checking"), t("overview.tile.versionChecking"), "");
|
|
168
|
+
vt.classList.add("stat-pending");
|
|
169
|
+
stats.append(vt);
|
|
170
|
+
versionInfo()
|
|
171
|
+
.then((v) => {
|
|
172
|
+
const s = versionState(v);
|
|
173
|
+
vt.classList.remove("stat-pending");
|
|
174
|
+
vt.replaceChildren();
|
|
175
|
+
vt.append(el("div", "stat-label", t("overview.tile.version")));
|
|
176
|
+
const val = el("div", "stat-value" + (s.kind ? " stat-" + s.kind : ""));
|
|
177
|
+
val.append(document.createTextNode(v && v.version ? "v" + v.version : "?"), chip(s.label, s.kind));
|
|
178
|
+
vt.append(val, el("div", "stat-note", s.note));
|
|
179
|
+
})
|
|
180
|
+
.catch(() => {
|
|
181
|
+
vt.classList.remove("stat-pending");
|
|
182
|
+
vt.replaceChildren(
|
|
183
|
+
el("div", "stat-label", t("overview.tile.version")),
|
|
184
|
+
el("div", "stat-value", "?"),
|
|
185
|
+
el("div", "stat-note", t("overview.tile.versionFailed"))
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
out.append(stats);
|
|
189
|
+
|
|
190
|
+
/* --- worth doing ----------------------------------------------------
|
|
191
|
+
One list, in severity order, of everything that wants a decision — and
|
|
192
|
+
every row carries the panel where the fix actually is. This is the
|
|
193
|
+
panel's answer to "what now", and it is the only place the wiki tier
|
|
194
|
+
turns into advice instead of a colour. */
|
|
195
|
+
out.append(attentionCard(d, findings));
|
|
196
|
+
|
|
197
|
+
/* --- waiting runs --------------------------------------------------- */
|
|
198
|
+
if (waiting.length) {
|
|
199
|
+
const c = card(t("overview.waiting.title"));
|
|
200
|
+
c.append(el("div", "note", t("overview.waiting.note")));
|
|
201
|
+
const list = el("div", "run-list");
|
|
202
|
+
for (const r of waiting) {
|
|
203
|
+
// The CARD CONTRACT (v0.49.2). `.run-card` is a four-column grid —
|
|
204
|
+
// caret, chip, mid, age — and this row used to append three children
|
|
205
|
+
// with no caret, so the chip landed in the 16px caret column and
|
|
206
|
+
// printed over an 88px-wide slug. One column short is not a rounding
|
|
207
|
+
// error; it is the whole card. This row navigates rather than expands,
|
|
208
|
+
// so it declares `.no-caret` and fills the three columns that variant
|
|
209
|
+
// has, in order.
|
|
210
|
+
const b = el("button", "run-card no-caret");
|
|
211
|
+
b.type = "button";
|
|
212
|
+
b.append(chip("waiting", "warn"));
|
|
213
|
+
const mid = el("div", "run-mid");
|
|
214
|
+
mid.append(el("div", "run-slug", r.slug));
|
|
215
|
+
mid.append(el("div", "run-where", r.lane ? r.lane + " · " + t("overview.waiting.where") : t("overview.waiting.where")));
|
|
216
|
+
b.append(mid, el("div", "run-age", r.updated_ms ? t("overview.waiting.since", { age: relAge(r.updated_ms) }) : ""));
|
|
217
|
+
b.addEventListener("click", () => {
|
|
218
|
+
location.hash = "#/runs?slug=" + encodeURIComponent(r.slug);
|
|
219
|
+
});
|
|
220
|
+
list.append(b);
|
|
221
|
+
// A CAUTION ROUTES TO THE PANEL THAT CAN CLEAR IT — and here the panel
|
|
222
|
+
// that can clear it is this one. A waiting run is what blocks the
|
|
223
|
+
// upgrade preview, so the way out sits beside the thing complaining.
|
|
224
|
+
const done = el("button", "btn btn-sm btn-ghost", t("runs.close.button"));
|
|
225
|
+
done.type = "button";
|
|
226
|
+
done.addEventListener("click", (ev) => {
|
|
227
|
+
ev.stopPropagation();
|
|
228
|
+
confirmRunClose(r.slug, () => route());
|
|
229
|
+
});
|
|
230
|
+
const act = el("div", "row-actions run-card-actions");
|
|
231
|
+
act.append(done);
|
|
232
|
+
list.append(act);
|
|
233
|
+
}
|
|
234
|
+
c.append(list);
|
|
235
|
+
out.append(c);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* --- v0.54.0: foreign dispatches that never reported back ------------
|
|
239
|
+
ONE LINE, and only when there is something to say. Money was spent and
|
|
240
|
+
work is half-done, and nothing will look at it again unless somebody is
|
|
241
|
+
told. It ROUTES to the panel that can clear it (Extra ▸ Recovery) — it
|
|
242
|
+
never resumes anything, because continuing a third party's unfinished
|
|
243
|
+
write without asking is the same class of act as routing off Claude
|
|
244
|
+
without saying so. */
|
|
245
|
+
const ej = d.extra_journal;
|
|
246
|
+
if (ej && ej.orphans) {
|
|
247
|
+
const c = card(t("overview.extraOrphan.title"));
|
|
248
|
+
c.append(el("div", "note", t("overview.extraOrphan.note")));
|
|
249
|
+
// `.no-caret` — this row NAVIGATES rather than expands, and every
|
|
250
|
+
// `.run-card` variant declares its own column count (v0.49.2). A card
|
|
251
|
+
// one column short is not a rounding error; it is the whole card.
|
|
252
|
+
const b = el("button", "run-card no-caret");
|
|
253
|
+
b.type = "button";
|
|
254
|
+
b.append(chip(tn(ej.orphans, "overview.extraOrphan.chip"), "bad"));
|
|
255
|
+
const mid = el("div", "run-mid");
|
|
256
|
+
mid.append(el("div", "run-slug", t("overview.extraOrphan.slug")));
|
|
257
|
+
mid.append(el("div", "run-where", t("overview.extraOrphan.where")));
|
|
258
|
+
b.append(mid, el("div", "run-age", ""));
|
|
259
|
+
b.addEventListener("click", () => {
|
|
260
|
+
location.hash = "#/extra";
|
|
261
|
+
});
|
|
262
|
+
c.append(b);
|
|
263
|
+
out.append(c);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/* --- the raw doctor list -------------------------------------------
|
|
267
|
+
Kept below the actionable card, because it is the EVIDENCE: the exact
|
|
268
|
+
message the CLI printed, unedited and untranslated, so what you read
|
|
269
|
+
here is what you would read in a terminal. */
|
|
270
|
+
if (!doctor.ok && findings.length) {
|
|
271
|
+
const c = card(t("overview.doctor.title"));
|
|
272
|
+
for (const f of findings) {
|
|
273
|
+
const row = el("div", "finding");
|
|
274
|
+
row.append(chip(f.fixable ? t("overview.doctor.fixable") : t("overview.doctor.manual"), f.fixable ? "info" : "warn"));
|
|
275
|
+
const detail = el("div");
|
|
276
|
+
detail.append(el("div", null, f.message));
|
|
277
|
+
const r = findingRoute(f.id);
|
|
278
|
+
if (r.panel) {
|
|
279
|
+
const go = el("button", "btn btn-ghost btn-sm", t(r.cta));
|
|
280
|
+
go.type = "button";
|
|
281
|
+
go.addEventListener("click", () => (location.hash = "#/" + r.panel));
|
|
282
|
+
detail.append(go);
|
|
283
|
+
} else {
|
|
284
|
+
detail.append(el("div", "note", t("overview.attention.nothingToDo")));
|
|
285
|
+
}
|
|
286
|
+
row.append(detail);
|
|
287
|
+
c.append(row);
|
|
288
|
+
}
|
|
289
|
+
out.append(c);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* --- where things are ----------------------------------------------- */
|
|
293
|
+
const paths = card(t("overview.paths.title"));
|
|
294
|
+
const where = d.where || {};
|
|
295
|
+
paths.append(
|
|
296
|
+
kvList(
|
|
297
|
+
[
|
|
298
|
+
[t("overview.paths.project"), where.project_root],
|
|
299
|
+
[t("overview.paths.config"), where.config],
|
|
300
|
+
[t("overview.paths.skills"), where.skills],
|
|
301
|
+
[t("overview.paths.runs"), where.run_dir],
|
|
302
|
+
[t("overview.paths.traces"), where.log_dir],
|
|
303
|
+
],
|
|
304
|
+
true
|
|
305
|
+
)
|
|
306
|
+
);
|
|
307
|
+
out.append(paths);
|
|
308
|
+
|
|
309
|
+
const know = card(t("overview.know.title"));
|
|
310
|
+
know.append(
|
|
311
|
+
kvList([
|
|
312
|
+
[t("overview.know.patterns"), langs.join(", ") || t("common.none")],
|
|
313
|
+
[t("overview.know.tags"), w.crosslink_tags === undefined ? "" : String(w.crosslink_tags)],
|
|
314
|
+
// `state` and `reason` are the CLI's — shown verbatim.
|
|
315
|
+
[t("overview.know.diy"), d.diy ? `${d.diy.state} — ${d.diy.reason}` : ""],
|
|
316
|
+
])
|
|
317
|
+
);
|
|
318
|
+
out.append(know);
|
|
319
|
+
|
|
320
|
+
return out;
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// The actionable list. Each entry is {kind, title, body, panel, cta} and knows
|
|
326
|
+
// where its fix lives — never a blanket "go to Maintenance".
|
|
327
|
+
function attentionCard(d, findings) {
|
|
328
|
+
const w = d.wiki || {};
|
|
329
|
+
const p = d.patterns || {};
|
|
330
|
+
const items = [];
|
|
331
|
+
|
|
332
|
+
// 1. Install findings, routed per id.
|
|
333
|
+
for (const f of findings) {
|
|
334
|
+
const r = findingRoute(f.id);
|
|
335
|
+
if (f.id === "diy-stale") {
|
|
336
|
+
items.push({
|
|
337
|
+
kind: "warn",
|
|
338
|
+
title: t("overview.item.diyStale.title"),
|
|
339
|
+
body: t("overview.item.diyStale.body"),
|
|
340
|
+
evidence: d.diy && d.diy.reason,
|
|
341
|
+
panel: "flow",
|
|
342
|
+
cta: t("overview.item.diyStale.cta"),
|
|
343
|
+
});
|
|
344
|
+
} else if (r.panel) {
|
|
345
|
+
items.push({
|
|
346
|
+
kind: f.fixable ? "warn" : "bad",
|
|
347
|
+
// A doctor finding is already a sentence written for a human — using it
|
|
348
|
+
// as the title beats paraphrasing it into something less exact.
|
|
349
|
+
title: f.message,
|
|
350
|
+
panel: r.panel,
|
|
351
|
+
cta: t(r.cta),
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// 2. The wiki. THIS is the recommendation the panel was missing: an AGING
|
|
357
|
+
// wiki is not an error, it is the moment a refresh is still cheap.
|
|
358
|
+
if (!w.state || w.state === "none") {
|
|
359
|
+
items.push({ kind: "info", title: t("overview.item.wikiNone.title"), body: t("overview.item.wikiNone.body"), panel: "knowledge", cta: t("overview.item.wikiNone.cta") });
|
|
360
|
+
} else if (w.state !== "registered") {
|
|
361
|
+
items.push({ kind: "warn", title: t("overview.item.wikiUnregistered.title"), body: t("overview.item.wikiUnregistered.body"), panel: "knowledge", cta: t("overview.item.wikiUnregistered.cta") });
|
|
362
|
+
} else if (w.tier === "AGING") {
|
|
363
|
+
items.push({
|
|
364
|
+
kind: "warn",
|
|
365
|
+
title: t("overview.item.wikiAging.title"),
|
|
366
|
+
body: t("overview.item.wikiAging.body"),
|
|
367
|
+
evidence: (w.reasons || [])[0],
|
|
368
|
+
panel: "knowledge",
|
|
369
|
+
cta: t("overview.item.wikiAging.cta"),
|
|
370
|
+
});
|
|
371
|
+
} else if (w.tier && w.tier !== "FRESH") {
|
|
372
|
+
items.push({
|
|
373
|
+
kind: "bad",
|
|
374
|
+
title: t("overview.item.wikiStale.title"),
|
|
375
|
+
body: t("overview.item.wikiStale.body"),
|
|
376
|
+
evidence: (w.reasons || [])[0],
|
|
377
|
+
panel: "knowledge",
|
|
378
|
+
cta: t("overview.item.wikiStale.cta"),
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// 3. No cached pattern — not a fault, but the cheapest quality win there is.
|
|
383
|
+
if (!(p.patterns || []).length)
|
|
384
|
+
items.push({ kind: "info", title: t("overview.item.patterns.title"), body: t("overview.item.patterns.body"), panel: "knowledge", cta: t("overview.item.patterns.cta") });
|
|
385
|
+
|
|
386
|
+
// 4. Runs left waiting.
|
|
387
|
+
if ((d.waiting || []).length)
|
|
388
|
+
items.push({
|
|
389
|
+
kind: "warn",
|
|
390
|
+
title: tn((d.waiting || []).length, "overview.item.waiting.title"),
|
|
391
|
+
body: t("overview.item.waiting.body"),
|
|
392
|
+
panel: "runs",
|
|
393
|
+
cta: t("overview.item.waiting.cta"),
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
const c = card(t("overview.attention.title"), chip(String(items.length), items.length ? "warn" : "ok"));
|
|
397
|
+
c.id = "attention-card";
|
|
398
|
+
|
|
399
|
+
if (!items.length) {
|
|
400
|
+
const ok = el("div", "all-clear");
|
|
401
|
+
ok.append(el("div", "all-clear-mark", "✓"));
|
|
402
|
+
const txt = el("div");
|
|
403
|
+
txt.append(el("div", "all-clear-title", t("overview.attention.allClear")));
|
|
404
|
+
txt.append(el("div", "note", t("overview.attention.allClearHint")));
|
|
405
|
+
ok.append(txt);
|
|
406
|
+
c.append(ok);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const list = el("div", "todo-list");
|
|
410
|
+
const order = { bad: 0, warn: 1, info: 2 };
|
|
411
|
+
items.sort((a, b) => (order[a.kind] || 3) - (order[b.kind] || 3));
|
|
412
|
+
for (const it of items) {
|
|
413
|
+
const row = el("button", "todo todo-" + it.kind);
|
|
414
|
+
row.type = "button";
|
|
415
|
+
row.append(el("span", "todo-mark"));
|
|
416
|
+
const mid = el("div", "todo-body");
|
|
417
|
+
mid.append(el("div", "todo-title", it.title));
|
|
418
|
+
if (it.body) mid.append(el("div", "todo-text", it.body));
|
|
419
|
+
// The CLI's own words for WHY, kept verbatim under our explanation.
|
|
420
|
+
if (it.evidence) mid.append(el("div", "todo-evidence", it.evidence));
|
|
421
|
+
row.append(mid, el("span", "todo-cta", it.cta));
|
|
422
|
+
row.addEventListener("click", () => (location.hash = "#/" + it.panel));
|
|
423
|
+
list.append(row);
|
|
424
|
+
}
|
|
425
|
+
if (items.length) c.append(list);
|
|
426
|
+
|
|
427
|
+
// The update offer is appended asynchronously so a slow network check never
|
|
428
|
+
// delays the list — it arrives as one more row when the answer does.
|
|
429
|
+
versionInfo()
|
|
430
|
+
.then((v) => {
|
|
431
|
+
if (!v || !v.update_available) return;
|
|
432
|
+
const row = el("button", "todo todo-info todo-late");
|
|
433
|
+
row.type = "button";
|
|
434
|
+
row.append(el("span", "todo-mark"));
|
|
435
|
+
const mid = el("div", "todo-body");
|
|
436
|
+
mid.append(el("div", "todo-title", t("overview.item.update.title", { latest: v.latest })));
|
|
437
|
+
mid.append(el("div", "todo-text", t("overview.item.update.body", { version: v.version })));
|
|
438
|
+
row.append(mid, el("span", "todo-cta", t("overview.item.update.cta")));
|
|
439
|
+
row.addEventListener("click", () => (location.hash = "#/maintenance"));
|
|
440
|
+
if (!list.isConnected) c.append(list);
|
|
441
|
+
list.append(row);
|
|
442
|
+
const count = c.querySelector(".card-head .chip");
|
|
443
|
+
if (count) {
|
|
444
|
+
count.textContent = String(list.children.length);
|
|
445
|
+
count.className = "chip chip-warn";
|
|
446
|
+
}
|
|
447
|
+
const clear = c.querySelector(".all-clear");
|
|
448
|
+
if (clear) clear.remove();
|
|
449
|
+
})
|
|
450
|
+
.catch(() => {});
|
|
451
|
+
|
|
452
|
+
return c;
|
|
453
|
+
}
|