instar 1.3.1587 → 1.3.1588
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/promises-model.js +16 -0
- package/dashboard/console/promises.js +14 -2
- package/dashboard/desktop-setup.js +164 -0
- package/dashboard/desktop.css +6 -0
- package/dashboard/desktop.html +5 -0
- package/dashboard/desktop.js +40 -4
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +12 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/core/DesktopSetupGuide.d.ts +72 -0
- package/dist/core/DesktopSetupGuide.d.ts.map +1 -0
- package/dist/core/DesktopSetupGuide.js +186 -0
- package/dist/core/DesktopSetupGuide.js.map +1 -0
- package/dist/core/DesktopSetupTarget.d.ts +67 -0
- package/dist/core/DesktopSetupTarget.d.ts.map +1 -0
- package/dist/core/DesktopSetupTarget.js +227 -0
- package/dist/core/DesktopSetupTarget.js.map +1 -0
- package/dist/core/MacDesktopHost.d.ts +2 -0
- package/dist/core/MacDesktopHost.d.ts.map +1 -1
- package/dist/core/MacDesktopHost.js +2 -0
- package/dist/core/MacDesktopHost.js.map +1 -1
- package/dist/core/MobileDeviceKeys.d.ts +30 -0
- package/dist/core/MobileDeviceKeys.d.ts.map +1 -1
- package/dist/core/MobileDeviceKeys.js +60 -0
- package/dist/core/MobileDeviceKeys.js.map +1 -1
- package/dist/core/MobilePush.d.ts +81 -0
- package/dist/core/MobilePush.d.ts.map +1 -0
- package/dist/core/MobilePush.js +386 -0
- package/dist/core/MobilePush.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts +5 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +20 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/WriteDomainRegistry.d.ts.map +1 -1
- package/dist/core/WriteDomainRegistry.js +4 -0
- package/dist/core/WriteDomainRegistry.js.map +1 -1
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +6 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/types.d.ts +11 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.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 +1 -0
- package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
- package/dist/lifeline/TelegramLifeline.js +7 -1
- package/dist/lifeline/TelegramLifeline.js.map +1 -1
- package/dist/lifeline/lifelineLock.d.ts +27 -4
- package/dist/lifeline/lifelineLock.d.ts.map +1 -1
- package/dist/lifeline/lifelineLock.js +198 -24
- package/dist/lifeline/lifelineLock.js.map +1 -1
- package/dist/messaging/TelegramAdapter.d.ts +7 -0
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +11 -2
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/native/instar-desktop-host +0 -0
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +3 -2
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +1 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +107 -5
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +4 -3
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/desktopSetupRoutes.d.ts +34 -0
- package/dist/server/desktopSetupRoutes.d.ts.map +1 -0
- package/dist/server/desktopSetupRoutes.js +234 -0
- package/dist/server/desktopSetupRoutes.js.map +1 -0
- package/dist/server/machineRoutes.d.ts +2 -0
- package/dist/server/machineRoutes.d.ts.map +1 -1
- package/dist/server/machineRoutes.js +17 -0
- package/dist/server/machineRoutes.js.map +1 -1
- package/dist/server/mobilePairingRoutes.d.ts +9 -0
- package/dist/server/mobilePairingRoutes.d.ts.map +1 -1
- package/dist/server/mobilePairingRoutes.js +190 -0
- package/dist/server/mobilePairingRoutes.js.map +1 -1
- package/dist/server/routes.d.ts +3 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +126 -2
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +66 -66
- 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/src/scaffold/templates.ts +3 -2
- package/upgrades/1.3.1588.md +61 -0
- package/upgrades/side-effects/console-promise-approve-decline.eli16.md +13 -0
- package/upgrades/side-effects/console-promise-approve-decline.md +36 -0
- package/upgrades/side-effects/desktop-guided-setup.md +117 -0
- package/upgrades/side-effects/lifeline-lock-heartbeat.md +150 -0
- package/upgrades/side-effects/mobile-push.md +109 -0
|
@@ -174,8 +174,24 @@ export function updateQuestionOf(c, now = Date.now()) {
|
|
|
174
174
|
: `What is the current status of “${subject}”? Please tell me what is done, what remains, and when I should expect the next update.`;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
/** A promise that waits only on the operator's yes or no: the page offers Approve and Decline. */
|
|
178
|
+
export function canDecide(c) {
|
|
179
|
+
return Boolean(c) && c.status === 'pending' && c.blockedOn === 'user-authorization';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** The words after an Approve / Decline answer, from the server's reply. */
|
|
183
|
+
export function decideResultWords(decision, res) {
|
|
184
|
+
if (res && res.ok) {
|
|
185
|
+
const posted = res.data && res.data.postedInConversation === false ? ' I could not post it in the conversation, so mention it there if it matters.' : '';
|
|
186
|
+
return { ok: true, text: (decision === 'approve' ? 'Approved. I will go ahead.' : 'Declined. The promise is closed.') + posted };
|
|
187
|
+
}
|
|
188
|
+
const why = (res && res.reason) || 'the server could not be reached';
|
|
189
|
+
return { ok: false, text: `That did not go through: ${String(why).replace(/\.$/, '').replace(/^The /, 'the ')}.` };
|
|
190
|
+
}
|
|
191
|
+
|
|
177
192
|
/** A concrete next step without assigning the agent's work to the operator. */
|
|
178
193
|
export function nextStepOf(c, now = Date.now()) {
|
|
194
|
+
if (canDecide(c)) return 'Your approval is needed. Approve or decline it here, or reply in the conversation to talk it through first.';
|
|
179
195
|
if (waitsOnOperator(c)) return 'Your answer or approval is needed. Open the conversation to respond.';
|
|
180
196
|
if (isMissed(c, now) || needsPromiseReview(c, now)) return 'This follow-up is mine: I need to verify the current outcome. No action is needed from you.';
|
|
181
197
|
return 'The next step is mine. No action is needed from you.';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Promises page (docs/specs/console-features/work.md, "Commitments → Promises").
|
|
2
2
|
// Read with Home's rules; actions use the bearer token only (no PIN on these routes).
|
|
3
3
|
|
|
4
|
-
import { buildPromisesPage, storyOf, backstoryOf, updateQuestionOf, nextStepOf, actionsFor } from './promises-model.js';
|
|
4
|
+
import { buildPromisesPage, storyOf, backstoryOf, updateQuestionOf, nextStepOf, actionsFor, canDecide, decideResultWords } from './promises-model.js';
|
|
5
5
|
import { isInternalFollowUp } from './home-model.js';
|
|
6
6
|
import { promiseActionLinks } from './promise-actions.js';
|
|
7
7
|
import { el, setKids, pageHead, section, sheet, closeSheet, sheetIsOpen, story, actionButton, note, cantTell } from './ui.js';
|
|
@@ -131,7 +131,7 @@ function openPromise(r, { post, chatId, names, refresh }) {
|
|
|
131
131
|
el('p', { class: 'promise-backstory-note', text: 'Copy this question and paste it in the conversation:' }),
|
|
132
132
|
el('textarea', { class: 'promise-question', readonly: true }, question)) : null;
|
|
133
133
|
if (fallback) blocks.push(fallback);
|
|
134
|
-
row.append(el('a', { class: `${actionLinks.length || !waitsOnYou ? 'btn-quiet' : 'btn-primary'} btn-inline`, href: link,
|
|
134
|
+
row.append(el('a', { class: `${actionLinks.length || !waitsOnYou || canDecide(c) ? 'btn-quiet' : 'btn-primary'} btn-inline`, href: link,
|
|
135
135
|
target: '_blank', rel: 'noopener', text: waitsOnYou ? 'Reply in the conversation' : internal ? 'View source conversation' : 'Optional: copy a question and open chat',
|
|
136
136
|
onclick: question ? () => {
|
|
137
137
|
// Topic links open the right chat; Telegram does not document a draft
|
|
@@ -150,6 +150,18 @@ function openPromise(r, { post, chatId, names, refresh }) {
|
|
|
150
150
|
if (x.ok) return { ok: true, message: path === 'deliver' ? 'Marked as done.' : path === 'withdraw' ? 'Dropped.' : 'Reminders resumed.' };
|
|
151
151
|
return { ok: false, message: x.reason };
|
|
152
152
|
};
|
|
153
|
+
if (canDecide(c)) {
|
|
154
|
+
const decide = async (decision, note) => {
|
|
155
|
+
const x = await post(`/commitments/${id}/decide${q}`, { decision, ...(note ? { note } : {}) });
|
|
156
|
+
const w = decideResultWords(decision, x);
|
|
157
|
+
return { ok: w.ok, message: w.text };
|
|
158
|
+
};
|
|
159
|
+
const after = () => setTimeout(() => { closeSheet(); refresh(); }, 1200);
|
|
160
|
+
const decideRow = el('div', { class: 'sheet-actions promise-decide' },
|
|
161
|
+
actionButton({ label: 'Approve', kind: 'primary', confirm: 'Approve this? The session goes ahead with it.', run: () => decide('approve'), done: after }),
|
|
162
|
+
actionButton({ label: 'Decline', kind: 'quiet', confirm: 'Decline this? The promise closes and the session is told not to do it.', run: () => decide('decline'), done: after }));
|
|
163
|
+
blocks.push(decideRow);
|
|
164
|
+
}
|
|
153
165
|
if (acts.includes('deliver')) row.append(actionButton({ label: waitsOnYou ? 'I handled this' : 'Mark as done', kind: link ? 'quiet' : 'primary',
|
|
154
166
|
confirm: waitsOnYou ? 'Close this reminder? This does not send a reply or perform the requested approval.' : 'Mark this promise as done?',
|
|
155
167
|
run: () => result('deliver', {}), done: () => setTimeout(() => { closeSheet(); refresh(); }, 700) }));
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// Guided Remote Desktop setup. Everything shown here comes from the server, so
|
|
2
|
+
// the dashboard and the app say the same thing. It never decides anything.
|
|
3
|
+
const POLL_MS = 4_000;
|
|
4
|
+
const MAX_POLL_MS = 60_000;
|
|
5
|
+
const POLL_FOR_MS = 10 * 60_000;
|
|
6
|
+
|
|
7
|
+
export function createDesktopSetup({ api, $, onReady, onPinLost }) {
|
|
8
|
+
const rows = new Map();
|
|
9
|
+
let activeId = '';
|
|
10
|
+
let timer = 0;
|
|
11
|
+
let delay = POLL_MS;
|
|
12
|
+
let startedAt = 0;
|
|
13
|
+
|
|
14
|
+
function stopPolling() {
|
|
15
|
+
if (timer) clearTimeout(timer);
|
|
16
|
+
timer = 0;
|
|
17
|
+
activeId = '';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function schedule() {
|
|
21
|
+
if (timer) clearTimeout(timer);
|
|
22
|
+
timer = 0;
|
|
23
|
+
if (!activeId || Date.now() - startedAt > POLL_FOR_MS) { stopPolling(); return; }
|
|
24
|
+
timer = setTimeout(poll, delay);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function poll() {
|
|
28
|
+
timer = 0;
|
|
29
|
+
const id = activeId;
|
|
30
|
+
if (!id) return;
|
|
31
|
+
try {
|
|
32
|
+
const row = await api(`/desktop/setup/${encodeURIComponent(id)}`);
|
|
33
|
+
delay = POLL_MS;
|
|
34
|
+
const wasReady = rows.get(id)?.ready;
|
|
35
|
+
show(row);
|
|
36
|
+
if (row.ready && !wasReady) {
|
|
37
|
+
stopPolling();
|
|
38
|
+
await onReady(row);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (row.ready) { stopPolling(); return; }
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (lostPin(error)) return;
|
|
44
|
+
delay = Math.min(delay * 2, MAX_POLL_MS);
|
|
45
|
+
}
|
|
46
|
+
schedule();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function lostPin(error) {
|
|
50
|
+
// This machine restarting ends the PIN session, which is expected here.
|
|
51
|
+
if (!/dashboard operator session/i.test(error?.message || '')) return false;
|
|
52
|
+
stopPolling();
|
|
53
|
+
onPinLost();
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function follow(id) {
|
|
58
|
+
activeId = id;
|
|
59
|
+
delay = POLL_MS;
|
|
60
|
+
startedAt = Date.now();
|
|
61
|
+
schedule();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function act(row, action, note) {
|
|
65
|
+
if (action.id === 'check') { note.textContent = 'Checking…'; }
|
|
66
|
+
try {
|
|
67
|
+
if (action.id === 'check') {
|
|
68
|
+
show(await api(`/desktop/setup/${encodeURIComponent(row.machineId)}`));
|
|
69
|
+
rows.get(row.machineId)?.ready ? await onReady(rows.get(row.machineId)) : follow(row.machineId);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const reply = await api(`/desktop/setup/${encodeURIComponent(row.machineId)}`, 'POST',
|
|
73
|
+
{ action: action.id, ...(action.pane ? { pane: action.pane } : {}) });
|
|
74
|
+
show(reply.step);
|
|
75
|
+
follow(row.machineId);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (lostPin(error)) return;
|
|
78
|
+
if (error.step) show(error.step);
|
|
79
|
+
const current = rowElement(row.machineId)?.querySelector('.setup-note');
|
|
80
|
+
if (current) current.textContent = error.message || 'That did not work. Try again.';
|
|
81
|
+
follow(row.machineId);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function rowElement(id) {
|
|
86
|
+
return [...$('setupRows').children].find(item => item.dataset.machineId === id);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function show(row) {
|
|
90
|
+
rows.set(row.machineId, row);
|
|
91
|
+
const item = document.createElement('li');
|
|
92
|
+
item.dataset.machineId = row.machineId;
|
|
93
|
+
const title = document.createElement('strong');
|
|
94
|
+
title.textContent = row.title;
|
|
95
|
+
const detail = document.createElement('p');
|
|
96
|
+
detail.textContent = row.detail;
|
|
97
|
+
const actions = document.createElement('div');
|
|
98
|
+
actions.className = 'setup-actions';
|
|
99
|
+
const note = document.createElement('p');
|
|
100
|
+
note.className = 'setup-note';
|
|
101
|
+
note.setAttribute('role', 'status');
|
|
102
|
+
for (const action of row.actions) {
|
|
103
|
+
const button = document.createElement('button');
|
|
104
|
+
button.type = 'button';
|
|
105
|
+
button.textContent = action.label;
|
|
106
|
+
if (action.id === 'turn-off') button.className = 'setup-quiet';
|
|
107
|
+
button.addEventListener('click', async () => {
|
|
108
|
+
if (action.id === 'turn-off'
|
|
109
|
+
&& !confirm(`Turn off Remote Desktop on ${row.label}? Its Instar server restarts.`)) return;
|
|
110
|
+
button.disabled = true;
|
|
111
|
+
try { await act(row, action, note); } finally { button.disabled = false; }
|
|
112
|
+
});
|
|
113
|
+
actions.append(button);
|
|
114
|
+
}
|
|
115
|
+
item.append(title, detail, actions);
|
|
116
|
+
if (row.note) {
|
|
117
|
+
const extra = document.createElement('p');
|
|
118
|
+
extra.textContent = row.note;
|
|
119
|
+
item.append(extra);
|
|
120
|
+
}
|
|
121
|
+
if (row.fallback) {
|
|
122
|
+
const more = document.createElement('details');
|
|
123
|
+
const summary = document.createElement('summary');
|
|
124
|
+
summary.textContent = 'Do it by hand instead';
|
|
125
|
+
const text = document.createElement('p');
|
|
126
|
+
text.textContent = row.fallback;
|
|
127
|
+
more.append(summary, text);
|
|
128
|
+
item.append(more);
|
|
129
|
+
}
|
|
130
|
+
item.append(note);
|
|
131
|
+
const existing = rowElement(row.machineId);
|
|
132
|
+
if (existing) existing.replaceWith(item);
|
|
133
|
+
else $('setupRows').append(item);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function refresh() {
|
|
137
|
+
let list;
|
|
138
|
+
try { list = await api('/desktop/setup'); }
|
|
139
|
+
catch (error) {
|
|
140
|
+
if (lostPin(error)) return false;
|
|
141
|
+
// An older server, or one with no machine identity yet.
|
|
142
|
+
$('setup').hidden = true;
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
stopPolling();
|
|
146
|
+
$('setupRows').replaceChildren();
|
|
147
|
+
rows.clear();
|
|
148
|
+
if (list.frontElsewhere) {
|
|
149
|
+
$('setupLead').textContent = `Remote Desktop runs from ${list.frontLabel}. Open this page on ${list.frontLabel} to set up other machines.`;
|
|
150
|
+
for (const row of list.machines || []) show(row);
|
|
151
|
+
$('setup').hidden = false;
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
const machines = list.machines || [];
|
|
155
|
+
$('setupLead').textContent = 'This page rechecks by itself after each step. A machine is ready when the check here says so.';
|
|
156
|
+
for (const row of machines) show(row);
|
|
157
|
+
$('setup').hidden = machines.length === 0;
|
|
158
|
+
// These rows carry the same reasons with the steps to fix them.
|
|
159
|
+
if (machines.length) $('machineNotes')?.replaceChildren();
|
|
160
|
+
return machines.length > 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return { refresh, stop: stopPolling };
|
|
164
|
+
}
|
package/dashboard/desktop.css
CHANGED
|
@@ -34,3 +34,9 @@ body.desktop-live #localCursor{display:block}
|
|
|
34
34
|
.machine-notes{margin:.8rem 0 0;padding-left:1.1rem;font-size:.95rem;line-height:1.4}
|
|
35
35
|
.machine-notes li{margin:.35rem 0}
|
|
36
36
|
.machine-notes:empty{display:none}
|
|
37
|
+
.setup-rows { list-style: none; margin: 0; padding: 0; }
|
|
38
|
+
.setup-rows li { border-top: 1px solid currentColor; padding: 12px 0; }
|
|
39
|
+
.setup-rows p { margin: 6px 0; overflow-wrap: anywhere; }
|
|
40
|
+
.setup-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
|
|
41
|
+
.setup-note:empty { display: none; }
|
|
42
|
+
.setup-quiet { background: none; border: none; color: inherit; opacity: 0.7; text-decoration: underline; }
|
package/dashboard/desktop.html
CHANGED
|
@@ -20,6 +20,11 @@
|
|
|
20
20
|
<button id="requestButton">Open selected machine</button>
|
|
21
21
|
<ul id="machineNotes" class="machine-notes" aria-label="Machines that are not ready"></ul>
|
|
22
22
|
</section>
|
|
23
|
+
<section id="setup" hidden>
|
|
24
|
+
<h2>Machines</h2>
|
|
25
|
+
<p id="setupLead"></p>
|
|
26
|
+
<ul id="setupRows" class="setup-rows" aria-label="Each machine and its next step"></ul>
|
|
27
|
+
</section>
|
|
23
28
|
<section id="control" hidden>
|
|
24
29
|
<div class="desktop-bar"><strong id="machineName"></strong><span id="liveStatus" role="status"></span><button id="stopButton" class="danger">Stop</button></div>
|
|
25
30
|
<div id="surface" aria-label="Remote desktop touch surface">
|
package/dashboard/desktop.js
CHANGED
|
@@ -600,12 +600,15 @@ async function api(path, method = 'GET', body, signal) {
|
|
|
600
600
|
if (!response.ok) {
|
|
601
601
|
const error = new Error(result.error || `Server answered ${response.status}`);
|
|
602
602
|
error.httpStatus = response.status;
|
|
603
|
+
if (result.step) error.step = result.step;
|
|
603
604
|
throw error;
|
|
604
605
|
}
|
|
605
606
|
return result;
|
|
606
607
|
}
|
|
607
608
|
|
|
608
609
|
function requirePinAgain() {
|
|
610
|
+
desktopSetup?.stop();
|
|
611
|
+
if ($('setup')) $('setup').hidden = true;
|
|
609
612
|
token = '';
|
|
610
613
|
operatorSession = '';
|
|
611
614
|
$('retryButton').hidden = true;
|
|
@@ -614,7 +617,40 @@ function requirePinAgain() {
|
|
|
614
617
|
status('Your dashboard session ended. Enter your PIN again.');
|
|
615
618
|
}
|
|
616
619
|
|
|
617
|
-
|
|
620
|
+
let desktopSetup = null;
|
|
621
|
+
async function refreshSetup() {
|
|
622
|
+
try {
|
|
623
|
+
if (!desktopSetup) {
|
|
624
|
+
const { createDesktopSetup } = await import('./desktop-setup.js');
|
|
625
|
+
desktopSetup = createDesktopSetup({
|
|
626
|
+
api, $,
|
|
627
|
+
onPinLost: () => requirePinAgain(),
|
|
628
|
+
onReady: async row => {
|
|
629
|
+
status(`${row.label} is ready.`);
|
|
630
|
+
await loadTargetsAndMaybeOpen({ open: false });
|
|
631
|
+
},
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
await desktopSetup.refresh();
|
|
635
|
+
} catch {
|
|
636
|
+
// @silent-fallback-ok — setup is an aid; the picker above it still works.
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// Setup exists even while this machine's own desktop is off, which is exactly
|
|
641
|
+
// when /desktop/targets answers 503.
|
|
642
|
+
async function loadDesktopPage(options) {
|
|
643
|
+
try { await loadTargetsAndMaybeOpen(options); }
|
|
644
|
+
catch (error) {
|
|
645
|
+
if (error.httpStatus !== 503) throw error;
|
|
646
|
+
$('unlock').hidden = true;
|
|
647
|
+
$('request').hidden = true;
|
|
648
|
+
status('Remote Desktop is off here. Set it up below.');
|
|
649
|
+
}
|
|
650
|
+
await refreshSetup();
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
async function loadTargetsAndMaybeOpen({ open = true } = {}) {
|
|
618
654
|
const targets = await api('/desktop/targets');
|
|
619
655
|
const machines = targets.machines || [];
|
|
620
656
|
$('machine').replaceChildren();
|
|
@@ -664,7 +700,7 @@ async function loadTargetsAndMaybeOpen() {
|
|
|
664
700
|
return;
|
|
665
701
|
}
|
|
666
702
|
$('retryButton').hidden = true;
|
|
667
|
-
if (hasPreferredMachine || (machines.length === 1 && ready.length === 1)) {
|
|
703
|
+
if (open && (hasPreferredMachine || (machines.length === 1 && ready.length === 1))) {
|
|
668
704
|
if (!hasPreferredMachine) $('machine').value = ready[0].value;
|
|
669
705
|
status(`Opening ${$('machine').selectedOptions[0].textContent}…`);
|
|
670
706
|
await requestDesktop();
|
|
@@ -689,7 +725,7 @@ $('unlockButton').addEventListener('click', async () => {
|
|
|
689
725
|
token = result.token;
|
|
690
726
|
operatorSession = result.operatorSessionToken;
|
|
691
727
|
$('pin').value = '';
|
|
692
|
-
await
|
|
728
|
+
await loadDesktopPage();
|
|
693
729
|
} catch (error) {
|
|
694
730
|
if (/dashboard operator session|dashboard PIN session/i.test(error.message || '')) requirePinAgain();
|
|
695
731
|
else status(error.message || 'Could not unlock.');
|
|
@@ -699,7 +735,7 @@ $('unlockButton').addEventListener('click', async () => {
|
|
|
699
735
|
|
|
700
736
|
$('retryButton').addEventListener('click', async () => {
|
|
701
737
|
$('retryButton').disabled = true;
|
|
702
|
-
try { await
|
|
738
|
+
try { await loadDesktopPage(); }
|
|
703
739
|
catch (error) {
|
|
704
740
|
if (/dashboard operator session|dashboard PIN session/i.test(error.message || '')) requirePinAgain();
|
|
705
741
|
else status(error.message || 'Could not check this machine.');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/server.js
CHANGED
|
@@ -3873,6 +3873,9 @@ export async function startServer(options) {
|
|
|
3873
3873
|
// i.e. this machine is trivially the owner — a strict single-process limiter.
|
|
3874
3874
|
// The pool block later calls configureBounds AGAIN to add the resolver.
|
|
3875
3875
|
notificationBatcher.configureBounds({ stateDir: config.stateDir });
|
|
3876
|
+
// Mobile push hub-notice hook (docs/specs/mobile-push.md §2.3); reads the live adapter.
|
|
3877
|
+
let _hubNoticeAdapter = null;
|
|
3878
|
+
const _hubNoticeHook = (t) => _hubNoticeAdapter?.onHubPost?.(t);
|
|
3876
3879
|
// State reference — set once StateManager is created, used by notify()
|
|
3877
3880
|
let _notifyState = null;
|
|
3878
3881
|
/**
|
|
@@ -3895,6 +3898,13 @@ export async function startServer(options) {
|
|
|
3895
3898
|
return;
|
|
3896
3899
|
}
|
|
3897
3900
|
}
|
|
3901
|
+
// Mobile push (docs/specs/mobile-push.md §2.3): a hub notice the filter admitted.
|
|
3902
|
+
if ((!topicId || topicId === hubTopicId) && hubTopicId) {
|
|
3903
|
+
try {
|
|
3904
|
+
_hubNoticeHook?.({ kind: 'notice', dedupeKey: `notice:${category}`, priority: tier === 'IMMEDIATE' ? 'URGENT' : undefined });
|
|
3905
|
+
}
|
|
3906
|
+
catch { /* @silent-fallback-ok — push is best-effort and never affects the notice */ }
|
|
3907
|
+
}
|
|
3898
3908
|
// Telegram: via notification batcher
|
|
3899
3909
|
const resolvedTopicId = topicId ?? hubTopicId;
|
|
3900
3910
|
if (resolvedTopicId) {
|
|
@@ -7804,6 +7814,7 @@ export async function startServer(options) {
|
|
|
7804
7814
|
systemTopics: systemTopicsHooks(state),
|
|
7805
7815
|
}, config.stateDir);
|
|
7806
7816
|
armHolderForwardWait(telegram);
|
|
7817
|
+
_hubNoticeAdapter = telegram;
|
|
7807
7818
|
console.log(pc.green(` Telegram send-only mode (${isStandbyTelegram ? 'standby' : lifelineOwnsPolling ? 'lifeline owns polling (lease detected)' : 'lifeline owns polling'})`));
|
|
7808
7819
|
// Send-only still owns the attention store: run its hourly lifecycle
|
|
7809
7820
|
// pass here too (start() is never called on this path). Idempotent.
|
|
@@ -7910,6 +7921,7 @@ export async function startServer(options) {
|
|
|
7910
7921
|
systemTopics: systemTopicsHooks(state),
|
|
7911
7922
|
}, config.stateDir);
|
|
7912
7923
|
armHolderForwardWait(telegram);
|
|
7924
|
+
_hubNoticeAdapter = telegram;
|
|
7913
7925
|
telegram.intelligence = sharedIntelligence ?? null;
|
|
7914
7926
|
await telegram.start();
|
|
7915
7927
|
console.log(pc.green(` Telegram connected (stall alerts: ${sharedIntelligence ? 'LLM-gated' : 'timer-only'})`));
|