instar 1.3.1611 → 1.3.1612
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/dashboard/console/console.css +3 -1
- package/dashboard/console/console.js +4 -3
- package/dashboard/console/home-model.js +4 -1
- package/dashboard/console/messages-model.js +2 -1
- package/dashboard/console/messages.js +1 -1
- package/dashboard/console/pace-model.js +5 -0
- package/dashboard/console/pace.js +3 -1
- package/dashboard/console/plans-model.js +17 -1
- package/dashboard/console/promises.js +2 -1
- package/dashboard/console/secrets-model.js +11 -1
- package/dist/commands/server.js +1 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +26 -2
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/data/standards-guard-index.json +1 -1
- package/dist/data/standards-guard-index.meta.json +2 -2
- package/dist/data/standards-registry.meta.json +1 -1
- package/dist/lifeline/TelegramLifeline.d.ts +2 -0
- package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
- package/dist/lifeline/TelegramLifeline.js +28 -0
- package/dist/lifeline/TelegramLifeline.js.map +1 -1
- package/dist/localVerify/localVerifyWiring.d.ts +11 -0
- package/dist/localVerify/localVerifyWiring.d.ts.map +1 -1
- package/dist/localVerify/localVerifyWiring.js +9 -0
- package/dist/localVerify/localVerifyWiring.js.map +1 -1
- package/dist/messaging/TelegramAdapter.d.ts +8 -0
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +39 -23
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/monitoring/GreenPrAutoMerger.d.ts.map +1 -1
- package/dist/monitoring/GreenPrAutoMerger.js +13 -0
- package/dist/monitoring/GreenPrAutoMerger.js.map +1 -1
- package/dist/monitoring/greenPrAutomergeWiring.d.ts.map +1 -1
- package/dist/monitoring/greenPrAutomergeWiring.js +3 -0
- package/dist/monitoring/greenPrAutomergeWiring.js.map +1 -1
- package/dist/native/instar-desktop-host +0 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +26 -3
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +64 -64
- package/src/data/standards-guard-index.json +1 -1
- package/src/data/standards-guard-index.meta.json +2 -2
- package/src/data/standards-registry.meta.json +1 -1
- package/upgrades/1.3.1612.md +72 -0
- package/upgrades/side-effects/blocker-text-is-the-message.eli16.md +5 -0
- package/upgrades/side-effects/blocker-text-is-the-message.md +38 -0
- package/upgrades/side-effects/console-needs-row-tidy.eli16.md +13 -0
- package/upgrades/side-effects/console-needs-row-tidy.md +24 -0
- package/upgrades/side-effects/console-secret-drop-names.eli16.md +5 -0
- package/upgrades/side-effects/console-secret-drop-names.md +34 -0
- package/upgrades/side-effects/green-pr-eligible-needs-passing-record.eli16.md +7 -0
- package/upgrades/side-effects/green-pr-eligible-needs-passing-record.md +39 -0
- package/upgrades/side-effects/green-pr-poke-unknown-mergeable.eli16.md +5 -0
- package/upgrades/side-effects/green-pr-poke-unknown-mergeable.md +41 -0
- package/upgrades/side-effects/pool-rows-use-local-machine-name.eli16.md +5 -0
- package/upgrades/side-effects/pool-rows-use-local-machine-name.md +34 -0
- package/upgrades/side-effects/topic-names-through-lifeline.eli16.md +9 -0
- package/upgrades/side-effects/topic-names-through-lifeline.md +39 -0
|
@@ -1161,8 +1161,10 @@ a.machine-line-link:hover { border-color: var(--accent); color: var(--text); }
|
|
|
1161
1161
|
/* Queue pace card (docs/specs/queue-pace.md) */
|
|
1162
1162
|
.pace-card { grid-column: 1 / -1; margin-top: var(--s-4, 16px); }
|
|
1163
1163
|
.pace-lines { list-style: none; margin: var(--s-2, 8px) 0 0; padding: 0; display: grid; gap: 8px; }
|
|
1164
|
-
.pace-line { display:
|
|
1164
|
+
.pace-line { display: grid; grid-template-columns: auto 1fr; align-items: baseline; column-gap: 8px; font-size: 14px; color: var(--text); }
|
|
1165
1165
|
.pace-line .dot { position: relative; top: -1px; }
|
|
1166
|
+
.pace-line .pace-text { min-width: 0; }
|
|
1167
|
+
.pace-line .pace-prs { grid-column: 2; }
|
|
1166
1168
|
.pace-line[data-tone="muted"] .pace-text { color: var(--text-muted); }
|
|
1167
1169
|
.pace-prs { display: inline-flex; gap: 6px; flex-wrap: wrap; font-size: 13px; }
|
|
1168
1170
|
.mb-pace { margin-top: 4px; }
|
|
@@ -393,6 +393,8 @@ function paintCard(def, model, repaint = false) {
|
|
|
393
393
|
foot.append(b);
|
|
394
394
|
} else foot.append(el('span', { class: 'card-age', text: extra > 0 ? `and ${extra} more` : '' }));
|
|
395
395
|
if (def.href) foot.append(el('a', { href: def.href, text: 'Open', 'aria-label': `Open ${def.title} in the dashboard` }));
|
|
396
|
+
// An empty footer would leave a blank band (and its divider) under the rows.
|
|
397
|
+
foot.hidden = !foot.textContent.trim() && !foot.querySelector('a, button');
|
|
396
398
|
return model;
|
|
397
399
|
}
|
|
398
400
|
|
|
@@ -565,7 +567,6 @@ function renderGroup(main, g) {
|
|
|
565
567
|
el('section', { class: 'group-head' },
|
|
566
568
|
el('div', { class: 'eyebrow', text: g.label }),
|
|
567
569
|
el('h1', { text: g.question }),
|
|
568
|
-
el('p', { text: 'Each of these opens in the dashboard, where everything works exactly as before.' }),
|
|
569
570
|
),
|
|
570
571
|
el('div', { class: 'tiles' }, g.tabs.map((t) => tile(t))),
|
|
571
572
|
...groupExtras(g, { get }), // the Development tile (Work, maintainer mode only) and the developer-tools switch (Settings)
|
|
@@ -585,8 +586,8 @@ function renderGroup(main, g) {
|
|
|
585
586
|
|
|
586
587
|
function footNote() {
|
|
587
588
|
return el('p', { class: 'foot-note' },
|
|
588
|
-
document.createTextNode('
|
|
589
|
-
el('a', { href: '/dashboard', text: 'legacy dashboard' }),
|
|
589
|
+
document.createTextNode('The older dashboard is still available: '),
|
|
590
|
+
el('a', { href: '/dashboard', text: 'open the legacy dashboard' }),
|
|
590
591
|
document.createTextNode('.'),
|
|
591
592
|
);
|
|
592
593
|
}
|
|
@@ -166,7 +166,10 @@ export function buildNeeds(res, now = Date.now(), names = {}) {
|
|
|
166
166
|
return {
|
|
167
167
|
key: groupKey(top), stale, last: g.last, prio: PRIORITY[top.priority] ?? 9,
|
|
168
168
|
main: top.title || top.summary || 'Untitled item',
|
|
169
|
-
|
|
169
|
+
// The machine only when the title does not already start with it; the
|
|
170
|
+
// priority word is the queue's bookkeeping, not something to act on.
|
|
171
|
+
sub: [g.items.length > 1 ? `${g.items.length} copies` : null,
|
|
172
|
+
[...g.machines].filter((m) => !String(top.title || top.summary || '').startsWith(`${m}:`)).join(', ') || null].filter(Boolean).join(' · '),
|
|
170
173
|
end: (ago(g.last, now) || '').replace(' ago', '') ? `${ago(g.last, now)}` : '',
|
|
171
174
|
tone: stale ? 'unknown' : (top.priority === 'URGENT' ? 'bad' : 'attn'),
|
|
172
175
|
detail: top.description || top.body || '',
|
|
@@ -82,7 +82,8 @@ export function buildMessagesPage(statusRes, healthRes, now = Date.now(), topicN
|
|
|
82
82
|
const automatic = held.filter(h => h.producerKind === 'server-automation').length;
|
|
83
83
|
headline = heldHeadline(held.length, replies, automatic, held.length - replies - automatic);
|
|
84
84
|
} else if (expired) headline = `${expired} ${expired === 1 ? 'message' : 'messages'} expired before delivery.`;
|
|
85
|
-
|
|
85
|
+
// Held replies are a fault, never a feature to report on: with none, the page is just Messages.
|
|
86
|
+
else headline = 'Messages to you';
|
|
86
87
|
const sub = [
|
|
87
88
|
held.length ? 'I need to restore delivery. This is not a task for you.' : null,
|
|
88
89
|
individual.length && individual[individual.length - 1].sinceTs ? `The oldest has waited ${ago(individual[individual.length - 1].sinceTs, now).replace(' ago', '')}.` : null,
|
|
@@ -43,7 +43,7 @@ export async function renderMessages(main, ctx) {
|
|
|
43
43
|
el('span', { class: 'need-sub', text: 'Who wrote each message is private to you. One unlock lasts about 15 minutes in this page.' }),
|
|
44
44
|
el('button', { class: 'btn-primary btn-inline', type: 'button', text: 'Unlock', onclick: async () => { const a = await askPin({ title: 'Message records', explain: 'Unlock message records. Your PIN is not kept; the unlock lives in this page until it expires.', adapter: 'session' }); if (a) refresh(); } })) : null;
|
|
45
45
|
setKids(main, el('div', { class: 'msg-page' },
|
|
46
|
-
pageHead({ eyebrow: 'People · Messages to you', headline: hf ? heldHeadline(hf.count, hf.sessionReplies, hf.automaticMessages, hf.otherMessages) : 'Messages to you', sub: hf ? 'I’m handling delivery. Unlock to see the individual records.' : '', tone: hf && hf.count ? 'attn' : 'muted' }),
|
|
46
|
+
pageHead({ eyebrow: 'People · Messages to you', headline: hf && hf.count ? heldHeadline(hf.count, hf.sessionReplies, hf.automaticMessages, hf.otherMessages) : 'Messages to you', sub: hf && hf.count ? 'I’m handling delivery. Unlock to see the individual records.' : '', tone: hf && hf.count ? 'attn' : 'muted' }),
|
|
47
47
|
unlock || cantTell(page.reason), legacyLink()));
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
@@ -60,6 +60,11 @@ export function paceLine(q, showMachine = false) {
|
|
|
60
60
|
if (person && q.verdict === 'falling-behind') verdict = 'waiting on you';
|
|
61
61
|
if (q.verdict === 'catching-up' && typeof q.daysToClear === 'number' && !person) verdict += `, about ${fmtRate(q.daysToClear)} days to clear`;
|
|
62
62
|
const inR = fmtRate(q.inflowPerDay), outR = fmtRate(q.outflowPerDay);
|
|
63
|
+
// Nothing in, nothing done, but items still open: that is a stalled pile, not "steady".
|
|
64
|
+
const open = Number(q.open);
|
|
65
|
+
if (!Number(q.inflowPerDay) && !Number(q.outflowPerDay) && open > 0) {
|
|
66
|
+
return `${label}${where}: nothing came in or got done this week; ${open} still open`;
|
|
67
|
+
}
|
|
63
68
|
const rates = inR !== null && outR !== null ? `${inR} a day in, ${outR} done — ` : '';
|
|
64
69
|
let line = `${label}${where}: ${rates}${verdict}`;
|
|
65
70
|
if (q.verdict === 'falling-behind') {
|
|
@@ -17,7 +17,9 @@ export function paceCard(get) {
|
|
|
17
17
|
const machines = new Set(queues.map((q) => q.machine));
|
|
18
18
|
const anyBehind = queues.some((q) => toneOf(q) === 'attn');
|
|
19
19
|
host.dataset.state = anyBehind ? 'attn' : 'ok';
|
|
20
|
-
|
|
20
|
+
// A queue with nothing coming in, nothing done and nothing open says nothing.
|
|
21
|
+
const idle = (q) => !Number(q.inflowPerDay) && !Number(q.outflowPerDay) && !Number(q.open);
|
|
22
|
+
const lines = queues.filter((q) => !idle(q)).map((q) => {
|
|
21
23
|
const tone = toneOf(q);
|
|
22
24
|
const li = el('li', { class: 'pace-line', 'data-tone': tone },
|
|
23
25
|
el('span', { class: `dot ${DOT[tone] || ''}`.trim() }),
|
|
@@ -162,9 +162,25 @@ function recheckText(e, now) {
|
|
|
162
162
|
return { text: `rechecking ${new Date(t).toLocaleDateString([], { weekday: 'short', month: 'short', day: 'numeric' })}`, overdue: false };
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
// The deferral detector recorded whole reply commands (`cat <<'EOF' | <reply script> N`
|
|
166
|
+
// + the message + `EOF`), signed links included (2026-11-02). Show the message's first line,
|
|
167
|
+
// with credential-looking query values masked; a command with no message text is named, not
|
|
168
|
+
// printed.
|
|
169
|
+
const SECRET_QUERY = /([?&](?:sig|signature|token|key|auth|access_token)[^=&\s]*=)[^&\s]+/gi;
|
|
170
|
+
const SHELL_START = /^\s*(?:cd|cat|curl|echo|node|bash|sh|gh|git|python3?|npx)\b/;
|
|
171
|
+
export function blockerText(raw) {
|
|
172
|
+
const s = String(raw || '');
|
|
173
|
+
const doc = /<<-?\s*['"]?(\w+)['"]?[^\n]*\n([\s\S]*?)(?:\n[ \t]*\1[ \t]*(?:\n|$)|$)/.exec(s);
|
|
174
|
+
let body = doc ? doc[2] : s;
|
|
175
|
+
if (!doc && SHELL_START.test(body)) return 'Flagged in a command I ran (no message text)';
|
|
176
|
+
body = body.replace(SECRET_QUERY, '$1…');
|
|
177
|
+
const line = body.split('\n').map((l) => l.trim()).find(Boolean) || '';
|
|
178
|
+
return line.length > 220 ? `${line.slice(0, 219)}…` : line;
|
|
179
|
+
}
|
|
180
|
+
|
|
165
181
|
/** A blocker as a row: { key, main, sub, end, tone, kind, record }. */
|
|
166
182
|
export function blockerRow(e, now = Date.now()) {
|
|
167
|
-
const main = e.detectedText || e.origin || 'A blocker';
|
|
183
|
+
const main = blockerText(e.detectedText) || e.origin || 'A blocker';
|
|
168
184
|
let sub; let tone = null; let kind;
|
|
169
185
|
if (e.state === 'true-blocker') { const r = recheckText(e, now); sub = `Truly stuck for now · ${r.text}`; tone = 'bad'; kind = 'stuck'; }
|
|
170
186
|
else if (e.state === 'access-requested') { sub = 'Waiting on you for access'; tone = 'attn'; kind = 'needs-you'; }
|
|
@@ -56,7 +56,8 @@ export async function renderPromises(main, ctx) {
|
|
|
56
56
|
}),
|
|
57
57
|
page.coverage ? note(page.coverage) : null,
|
|
58
58
|
el('div', { class: 'page-sections' },
|
|
59
|
-
|
|
59
|
+
// When nothing waits on the operator the headline already says so; an empty card would repeat it.
|
|
60
|
+
s.waiting.length ? section({ id: 'waiting', title: 'Waiting on you', rows: s.waiting.map((r) => ({ ...r, tone: 'attn' })), empty: 'Nothing is waiting on you.', onRow: open }) : null,
|
|
60
61
|
section({ id: 'carrying', title: 'I’m handling', rows: s.carrying, collapsed: true, onRow: open }),
|
|
61
62
|
section({ id: 'handled', title: 'Older, still open', rows: s.handled.map((r) => ({ ...r, tone: 'unknown' })), collapsed: true, onRow: open }),
|
|
62
63
|
section({ id: 'closed', title: 'Closed this week', rows: s.closed, collapsed: true, onRow: open }),
|
|
@@ -124,8 +124,18 @@ export function buildRequests(res, opts = {}) {
|
|
|
124
124
|
* (names only); falls back to the secret-sync key list when that is switched off. Lengths
|
|
125
125
|
* and values are never read into the model.
|
|
126
126
|
*/
|
|
127
|
+
// A saved Secret Drop is stored under `secretDrops.<64-hex token>` (often with
|
|
128
|
+
// "(+N nested)"): a token, not a name. Show them as one counted line instead.
|
|
129
|
+
const DROP_KEY = /^secretDrops\.[0-9a-f]{16,}\b/;
|
|
130
|
+
function groupDrops(names) {
|
|
131
|
+
const drops = names.filter((n) => DROP_KEY.test(n)).length;
|
|
132
|
+
if (!drops) return names;
|
|
133
|
+
const rest = names.filter((n) => !DROP_KEY.test(n));
|
|
134
|
+
return [...rest, drops === 1 ? 'Saved Secret Drop submission (1)' : `Saved Secret Drop submissions (${drops})`].sort((a, b) => a.localeCompare(b));
|
|
135
|
+
}
|
|
136
|
+
|
|
127
137
|
export function buildKeys(ctxRes, syncRes) {
|
|
128
|
-
const clean = (arr) => [...new Set(arr.filter((n) => typeof n === 'string' && n.trim()).map((n) => n.trim()))].sort((a, b) => a.localeCompare(b));
|
|
138
|
+
const clean = (arr) => groupDrops([...new Set(arr.filter((n) => typeof n === 'string' && n.trim()).map((n) => n.trim()))]).sort((a, b) => a.localeCompare(b));
|
|
129
139
|
if (ctxRes && ctxRes.ok && ctxRes.data && Array.isArray(ctxRes.data.names)) {
|
|
130
140
|
const vaultState = ctxRes.data.vaultState;
|
|
131
141
|
if (vaultState === 'decrypt-failed') return { ok: false, decryptFailed: true, names: [], reason: 'Can’t open the saved keys on this machine.' };
|
package/dist/commands/server.js
CHANGED
|
@@ -22302,7 +22302,7 @@ export async function startServer(options) {
|
|
|
22302
22302
|
greenPrAutoMerger?.attachLocalVerify({
|
|
22303
22303
|
effectiveFloor: () => (rec ? rec.effectiveFloor() : null),
|
|
22304
22304
|
mainHealth: () => svc.mainHealth().state,
|
|
22305
|
-
hasRecord: (pr, head, base) =>
|
|
22305
|
+
hasRecord: (pr, head, base) => wiring.hasPassingRecord(svc, pr, head, base),
|
|
22306
22306
|
requestVerify: (pr) => svc.request(pr, { explicit: false }),
|
|
22307
22307
|
// Dry-run posting also suppresses the watcher's actions→local disarm write.
|
|
22308
22308
|
postStatuses: () => liveConfig.get('monitoring.localVerify.postStatuses', false) === true,
|