instar 1.3.1350 → 1.3.1352
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/index.html +62 -16
- package/dashboard/pairing.js +229 -109
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +5 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/core/ClaudeBinaryRetainer.d.ts +186 -0
- package/dist/core/ClaudeBinaryRetainer.d.ts.map +1 -0
- package/dist/core/ClaudeBinaryRetainer.js +585 -0
- package/dist/core/ClaudeBinaryRetainer.js.map +1 -0
- package/dist/core/Config.d.ts.map +1 -1
- package/dist/core/Config.js +5 -0
- package/dist/core/Config.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +53 -17
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SessionManager.d.ts +26 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +61 -5
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/WriteDomainRegistry.d.ts.map +1 -1
- package/dist/core/WriteDomainRegistry.js +26 -0
- package/dist/core/WriteDomainRegistry.js.map +1 -1
- package/dist/core/types.d.ts +14 -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/MessageQueue.d.ts.map +1 -1
- package/dist/lifeline/MessageQueue.js +5 -0
- package/dist/lifeline/MessageQueue.js.map +1 -1
- package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
- package/dist/lifeline/TelegramLifeline.js +8 -0
- package/dist/lifeline/TelegramLifeline.js.map +1 -1
- package/dist/messaging/TelegramAdapter.d.ts +13 -0
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +51 -0
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +9 -5
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +2 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +1 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +2 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +236 -0
- package/dist/server/routes.js.map +1 -1
- package/dist/threadline/AgentTrustManager.d.ts +36 -1
- package/dist/threadline/AgentTrustManager.d.ts.map +1 -1
- package/dist/threadline/AgentTrustManager.js +111 -0
- package/dist/threadline/AgentTrustManager.js.map +1 -1
- package/dist/threadline/InboundMessageGate.d.ts +10 -0
- package/dist/threadline/InboundMessageGate.d.ts.map +1 -1
- package/dist/threadline/InboundMessageGate.js +39 -1
- package/dist/threadline/InboundMessageGate.js.map +1 -1
- package/dist/threadline/PairingInvite.d.ts +92 -0
- package/dist/threadline/PairingInvite.d.ts.map +1 -0
- package/dist/threadline/PairingInvite.js +174 -0
- package/dist/threadline/PairingInvite.js.map +1 -0
- package/dist/threadline/PairingInviteFlow.d.ts +141 -0
- package/dist/threadline/PairingInviteFlow.d.ts.map +1 -0
- package/dist/threadline/PairingInviteFlow.js +345 -0
- package/dist/threadline/PairingInviteFlow.js.map +1 -0
- package/dist/threadline/PairingInviteStore.d.ts +92 -0
- package/dist/threadline/PairingInviteStore.d.ts.map +1 -0
- package/dist/threadline/PairingInviteStore.js +163 -0
- package/dist/threadline/PairingInviteStore.js.map +1 -0
- package/dist/threadline/PairingInviteTelegram.d.ts +76 -0
- package/dist/threadline/PairingInviteTelegram.d.ts.map +1 -0
- package/dist/threadline/PairingInviteTelegram.js +141 -0
- package/dist/threadline/PairingInviteTelegram.js.map +1 -0
- package/dist/threadline/ThreadlineBootstrap.d.ts +14 -0
- package/dist/threadline/ThreadlineBootstrap.d.ts.map +1 -1
- package/dist/threadline/ThreadlineBootstrap.js +51 -1
- package/dist/threadline/ThreadlineBootstrap.js.map +1 -1
- package/dist/threadline/ThreadlineMCPServer.d.ts +13 -0
- package/dist/threadline/ThreadlineMCPServer.d.ts.map +1 -1
- package/dist/threadline/ThreadlineMCPServer.js +22 -7
- package/dist/threadline/ThreadlineMCPServer.js.map +1 -1
- package/dist/threadline/client/ThreadlineClient.d.ts +11 -0
- package/dist/threadline/client/ThreadlineClient.d.ts.map +1 -1
- package/dist/threadline/client/ThreadlineClient.js +24 -1
- package/dist/threadline/client/ThreadlineClient.js.map +1 -1
- package/dist/threadline/mcp-http-client.d.ts +14 -0
- package/dist/threadline/mcp-http-client.d.ts.map +1 -1
- package/dist/threadline/mcp-http-client.js +29 -0
- package/dist/threadline/mcp-http-client.js.map +1 -1
- package/dist/threadline/mcp-stdio-entry.js +3 -1
- package/dist/threadline/mcp-stdio-entry.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 +9 -5
- package/upgrades/1.3.1351.md +28 -0
- package/upgrades/1.3.1352.md +41 -0
- package/upgrades/side-effects/retained-claude-executable.md +116 -0
- package/upgrades/side-effects/threadline-pairing-invite-link.md +184 -0
package/dashboard/index.html
CHANGED
|
@@ -1055,6 +1055,14 @@
|
|
|
1055
1055
|
.pair-word { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--text-dim); }
|
|
1056
1056
|
.pair-word input { flex: 1; min-width: 0; padding: 6px; font-family: ui-monospace, monospace; font-size: 15px; }
|
|
1057
1057
|
.pair-meta { font-size: 11px; color: var(--text-dim); margin-top: 8px; }
|
|
1058
|
+
.pair-waiting { border-color: #1f6feb; }
|
|
1059
|
+
#pairNotice:empty { display: none; }
|
|
1060
|
+
.pair-pin { width: 160px; padding: 8px; font-size: 16px; }
|
|
1061
|
+
.pair-nav-badge { margin-left: 8px; padding: 1px 8px; border-radius: 10px; background: #1f6feb; color: #fff; font-size: 12px; font-weight: 600; }
|
|
1062
|
+
/* On a phone the header has no room left: the badge floats on the menu button's corner and takes no width. */
|
|
1063
|
+
@media (max-width: 640px) { .pair-nav-badge-word { display: none; } #navToggle { position: relative; } .pair-nav-badge { position: absolute; top: -7px; right: -7px; margin: 0; padding: 1px 6px; } }
|
|
1064
|
+
.pair-alarm { border-color: #f85149; background: rgba(248, 81, 73, 0.08); }
|
|
1065
|
+
.pair-invite-text { width: 100%; box-sizing: border-box; font-family: ui-monospace, monospace; font-size: 13px; padding: 8px; }
|
|
1058
1066
|
@media (max-width: 640px) {
|
|
1059
1067
|
.pair-own-words { columns: 2; }
|
|
1060
1068
|
.pair-words { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
@@ -3023,6 +3031,10 @@
|
|
|
3023
3031
|
</div>
|
|
3024
3032
|
|
|
3025
3033
|
<!-- Main app -->
|
|
3034
|
+
<div id="dashVersionBar" style="display:none;position:fixed;top:0;left:0;right:0;z-index:10000;gap:10px;align-items:center;justify-content:center;padding:8px 12px;background:#1f6feb;color:#fff;font-size:14px">
|
|
3035
|
+
The agent was updated to <span data-new-version></span>. Reload to get the new dashboard.
|
|
3036
|
+
<button onclick="location.reload()" style="padding:4px 12px">Reload</button>
|
|
3037
|
+
</div>
|
|
3026
3038
|
<div class="app" id="app" style="display:none">
|
|
3027
3039
|
<header class="header">
|
|
3028
3040
|
<div class="header-left">
|
|
@@ -4173,6 +4185,30 @@
|
|
|
4173
4185
|
Threadline is your agent's secure channel to other AI agents. The Telegram bridge mirrors those conversations into per-thread Telegram topics so you can watch every exchange. Quiet by default — flip the master switch below to opt in.
|
|
4174
4186
|
</div>
|
|
4175
4187
|
|
|
4188
|
+
<!-- Pair with another agent — by invite (docs/specs/a2a-pairing-invite-link.md §8). First on the
|
|
4189
|
+
tab: decisions waiting for the operator sit at the top of this card. -->
|
|
4190
|
+
<div id="pairCard" class="mnd-card" style="border:1px solid var(--border);border-radius:8px;padding:16px">
|
|
4191
|
+
<strong>Pair with another agent</strong>
|
|
4192
|
+
<div class="mnd-hint">Pairing proves another agent is really who it says it is, before it can be sent anything secret.</div>
|
|
4193
|
+
<div id="pairNotice" class="mnd-notice"></div>
|
|
4194
|
+
<div id="pairPinRow" class="pair-row" style="display:none"><label for="pairPin" class="pair-step-title" style="margin:0">Your PIN, to approve (asked once):</label><input type="password" id="pairPin" class="mnd-pin pair-pin" inputmode="numeric" autocomplete="off" /></div>
|
|
4195
|
+
<div id="pairPending"></div>
|
|
4196
|
+
<div class="pair-step">
|
|
4197
|
+
<div class="pair-step-title">Invite an agent</div>
|
|
4198
|
+
<div class="pair-row"><label for="pairFor" class="mnd-dim">Who is it for?</label><select id="pairFor"><option value="">Someone new</option></select></div>
|
|
4199
|
+
<div class="pair-row"><select id="pairTtl"><option value="60">Valid for 1 hour</option><option value="1440">Valid for 24 hours</option></select>
|
|
4200
|
+
<button class="mnd-btn mnd-btn-primary" data-make-invite>Make an invite</button></div>
|
|
4201
|
+
<div id="pairInviteOut"></div>
|
|
4202
|
+
<div id="pairOpen"></div>
|
|
4203
|
+
</div>
|
|
4204
|
+
<div class="pair-step">
|
|
4205
|
+
<div class="pair-step-title">Got an invite? Paste it here</div>
|
|
4206
|
+
<textarea id="pairInviteIn" class="pair-invite-text" rows="3" placeholder="Paste the whole message you were sent" autocomplete="off" spellcheck="false"></textarea>
|
|
4207
|
+
<div class="pair-row"><button class="mnd-btn mnd-btn-primary" data-inspect-invite>Check it</button></div>
|
|
4208
|
+
</div>
|
|
4209
|
+
<div id="pairList"></div>
|
|
4210
|
+
</div>
|
|
4211
|
+
|
|
4176
4212
|
<!-- Bridge settings card -->
|
|
4177
4213
|
<div id="tlBridgeCard" style="border:1px solid var(--border);border-radius:8px;padding:16px;display:flex;flex-direction:column;gap:14px">
|
|
4178
4214
|
<div style="font-weight:600">Telegram bridge settings</div>
|
|
@@ -4202,7 +4238,7 @@
|
|
|
4202
4238
|
</span>
|
|
4203
4239
|
</label>
|
|
4204
4240
|
|
|
4205
|
-
<div style="display:grid;grid-template-columns:1fr
|
|
4241
|
+
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px">
|
|
4206
4242
|
<div>
|
|
4207
4243
|
<div style="font-weight:600;font-size:13px;margin-bottom:6px">Allow-list</div>
|
|
4208
4244
|
<div style="font-size:11px;color:var(--text-dim);margin-bottom:6px">Always auto-create topics for these remote agents (overrides "auto-create off").</div>
|
|
@@ -4226,21 +4262,10 @@
|
|
|
4226
4262
|
<div id="tlBridgeError" style="color:var(--err, #c44);font-size:12px;display:none"></div>
|
|
4227
4263
|
</div>
|
|
4228
4264
|
|
|
4229
|
-
<!-- Verified pairing — the 12-word check (docs/specs/secure-a2a-verified-pairing.md §3.6) -->
|
|
4230
|
-
<div id="pairCard" class="mnd-card" style="border:1px solid var(--border);border-radius:8px;padding:16px">
|
|
4231
|
-
<div style="display:flex;justify-content:space-between;align-items:center;gap:8px;flex-wrap:wrap">
|
|
4232
|
-
<strong>Verified pairing</strong>
|
|
4233
|
-
<button onclick="startPairingPanel()" class="mnd-btn mnd-btn-quiet" style="padding:6px 12px">Refresh</button>
|
|
4234
|
-
</div>
|
|
4235
|
-
<div class="mnd-hint">Confirm another agent is really who it says it is: compare 12 words with its operator before it can be sent credentials.</div>
|
|
4236
|
-
<div id="pairNotice" class="mnd-notice"></div>
|
|
4237
|
-
<div id="pairList"></div>
|
|
4238
|
-
</div>
|
|
4239
|
-
|
|
4240
4265
|
<!-- Conversation observability — threads list + message stream + search -->
|
|
4241
4266
|
<div style="border:1px solid var(--border);border-radius:8px;padding:0;display:flex;flex-direction:column;min-height:480px">
|
|
4242
4267
|
<!-- Toolbar -->
|
|
4243
|
-
<div style="display:flex;gap:8px;align-items:center;padding:12px 16px;border-bottom:1px solid var(--border)">
|
|
4268
|
+
<div style="display:flex;flex-wrap:wrap;gap:8px;align-items:center;padding:12px 16px;border-bottom:1px solid var(--border)">
|
|
4244
4269
|
<div style="font-weight:600">Conversations</div>
|
|
4245
4270
|
<div style="flex:1"></div>
|
|
4246
4271
|
<input type="search" id="tlObsSearchInput" placeholder="search messages…" style="padding:6px 8px;font-size:12px;width:220px" oninput="tlObsSearchDebounced(this.value)" />
|
|
@@ -4430,6 +4455,7 @@
|
|
|
4430
4455
|
startWaPolling();
|
|
4431
4456
|
startVitalSigns();
|
|
4432
4457
|
startPoolSessionsPolling();
|
|
4458
|
+
if (typeof startPairingPanel === 'function') startPairingPanel();
|
|
4433
4459
|
loadTopicProfileOptions();
|
|
4434
4460
|
} else {
|
|
4435
4461
|
showAuthError('Incorrect PIN');
|
|
@@ -4457,6 +4483,7 @@
|
|
|
4457
4483
|
startWaPolling();
|
|
4458
4484
|
startVitalSigns();
|
|
4459
4485
|
startPoolSessionsPolling();
|
|
4486
|
+
if (typeof startPairingPanel === 'function') startPairingPanel();
|
|
4460
4487
|
loadTopicProfileOptions();
|
|
4461
4488
|
}
|
|
4462
4489
|
})
|
|
@@ -8008,6 +8035,18 @@
|
|
|
8008
8035
|
const resp = await fetch('/health', { headers: { 'Authorization': 'Bearer ' + token } });
|
|
8009
8036
|
if (!resp.ok) return;
|
|
8010
8037
|
const h = await resp.json();
|
|
8038
|
+
// a2a-pairing-invite-link §8.4: an updated server means this page may be stale. Offer a
|
|
8039
|
+
// reload instead of silently showing an old dashboard.
|
|
8040
|
+
if (h.version) {
|
|
8041
|
+
if (!window.__dashServerVersion) window.__dashServerVersion = h.version;
|
|
8042
|
+
else if (h.version !== window.__dashServerVersion) {
|
|
8043
|
+
const bar = document.getElementById('dashVersionBar');
|
|
8044
|
+
if (bar && bar.style.display === 'none') {
|
|
8045
|
+
bar.querySelector('[data-new-version]').textContent = h.version;
|
|
8046
|
+
bar.style.display = 'flex';
|
|
8047
|
+
}
|
|
8048
|
+
}
|
|
8049
|
+
}
|
|
8011
8050
|
|
|
8012
8051
|
// Server status
|
|
8013
8052
|
const serverDot = document.getElementById('vitalServerDot');
|
|
@@ -10443,8 +10482,9 @@
|
|
|
10443
10482
|
} catch { host.innerHTML = '<div style="opacity:.7">Throughput data is temporarily unavailable.</div>'; }
|
|
10444
10483
|
}
|
|
10445
10484
|
|
|
10446
|
-
// ──
|
|
10447
|
-
//
|
|
10485
|
+
// ── Pairing by invite (Threadline tab; a2a-pairing-invite-link §8) ──
|
|
10486
|
+
// Started at login (not only on the tab) so the tab can show waiting decisions.
|
|
10487
|
+
// The PIN is held in the module's memory after one accepted use; never stored.
|
|
10448
10488
|
let __pairController = null;
|
|
10449
10489
|
let __pairModule = null;
|
|
10450
10490
|
async function startPairingPanel() {
|
|
@@ -10453,7 +10493,13 @@
|
|
|
10453
10493
|
catch (e) { console.error('[pairing] module load failed', e); return; }
|
|
10454
10494
|
}
|
|
10455
10495
|
if (!__pairController) {
|
|
10456
|
-
const
|
|
10496
|
+
const $ = (id) => document.getElementById(id);
|
|
10497
|
+
const els = {
|
|
10498
|
+
root: $('pairCard'), notice: $('pairNotice'), pending: $('pairPending'), open: $('pairOpen'), list: $('pairList'),
|
|
10499
|
+
pin: $('pairPin'), pinRow: $('pairPinRow'), ttl: $('pairTtl'), forPeer: $('pairFor'), inviteOut: $('pairInviteOut'), inviteIn: $('pairInviteIn'),
|
|
10500
|
+
tabButton: document.querySelector('.tab[data-tab="threadline"]'),
|
|
10501
|
+
navToggle: $('navToggle'),
|
|
10502
|
+
};
|
|
10457
10503
|
const fetchImpl = (url, opts = {}) => fetch(url, {
|
|
10458
10504
|
...opts,
|
|
10459
10505
|
headers: { 'Authorization': `Bearer ${token}`, ...(opts.headers || {}) },
|
package/dashboard/pairing.js
CHANGED
|
@@ -1,86 +1,116 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* pairing.js —
|
|
3
|
-
* (docs/specs/
|
|
2
|
+
* pairing.js — "Pair with another agent" on the dashboard's Threadline tab
|
|
3
|
+
* (docs/specs/a2a-pairing-invite-link.md §8; replaces the 12-word panel of
|
|
4
|
+
* secure-a2a-verified-pairing §3.6, which Justin rejected on 2026-10-13).
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* What the operator does: make an invite and send it however they like; or paste one
|
|
7
|
+
* they were sent and approve it; and, when someone accepts their invite, Finish (or say
|
|
8
|
+
* "Not them"). Nobody reads, compares or types words.
|
|
9
|
+
*
|
|
10
|
+
* The rules from Justin's test (spec §8):
|
|
11
|
+
* 1. Nothing the operator is looking at or typing into is re-rendered by a timer. Only the
|
|
12
|
+
* list sections (waiting decisions, open invites, paired agents) refresh, and only when
|
|
13
|
+
* their content actually changed; none of them holds an input.
|
|
14
|
+
* 2. The PIN is asked once per page: after one accepted use it is held in this module's
|
|
15
|
+
* memory (never storage) until the page is reloaded. A refused PIN is forgotten.
|
|
16
|
+
* 3. Decisions waiting for the operator come first, with a count on the Threadline tab AND on
|
|
17
|
+
* the header menu button (the tabs sit behind it, so a count only on the tab is invisible).
|
|
10
18
|
*
|
|
11
19
|
* Same shape as mandates.js: pure renderers + createController({ doc, els, fetchImpl }).
|
|
12
20
|
*/
|
|
13
21
|
|
|
14
|
-
const
|
|
15
|
-
const WORD_COUNT = 12;
|
|
22
|
+
const POLL_MS = 10_000;
|
|
16
23
|
|
|
17
24
|
function esc(s) {
|
|
18
25
|
return String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
const STATE_LABEL = {
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'verification
|
|
29
|
+
'mutual-verified': 'Paired',
|
|
30
|
+
'identity-verified': 'Paired (on another of my machines)',
|
|
31
|
+
'invite-accepted': 'Waiting for the other side to finish',
|
|
32
|
+
'pending-verification': 'Started with the old word check. Pair by invite instead.',
|
|
33
|
+
'verification-failed': 'Blocked (the old word check did not match)',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const claimed = (agent, owner) =>
|
|
37
|
+
`the agent that calls itself <strong>${esc(agent || 'unknown')}</strong>${owner ? `, which says it belongs to <strong>${esc(owner)}</strong>` : ''}`;
|
|
38
|
+
|
|
39
|
+
const cap = (h) => h.charAt(0).toUpperCase() + h.slice(1);
|
|
40
|
+
|
|
41
|
+
const timeLeft = (exp, now = Date.now()) => {
|
|
42
|
+
const ms = Date.parse(exp) - now;
|
|
43
|
+
if (!(ms > 0)) return 'expired';
|
|
44
|
+
const min = Math.round(ms / 60_000);
|
|
45
|
+
return min >= 90 ? `${Math.round(min / 60)} h left` : `${Math.max(1, min)} min left`;
|
|
26
46
|
};
|
|
27
47
|
|
|
28
|
-
/**
|
|
48
|
+
/** Invites waiting for this operator's Finish: used, with an acceptor, undecided, not leaked. */
|
|
49
|
+
export function finishPrompts(invites) {
|
|
50
|
+
return (invites || []).filter((i) => i.state === 'used' && i.acceptor && !i.decision && !i.secondAccept);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Leak alarms from the last 24 h. */
|
|
54
|
+
export function alarms(invites, now = Date.now()) {
|
|
55
|
+
return (invites || []).filter((i) => i.secondAccept && now - Date.parse(i.secondAccept.at) < 24 * 60 * 60_000);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** How many decisions wait for the operator (the tab badge). */
|
|
59
|
+
export function pendingCount(data) {
|
|
60
|
+
return finishPrompts(data?.invites).length + (data?.pendingAccepts || []).length;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function renderPending(data, now = Date.now()) {
|
|
64
|
+
const parts = [];
|
|
65
|
+
for (const a of alarms(data?.invites, now)) {
|
|
66
|
+
parts.push(`<div class="pair-card pair-alarm" data-alarm="${esc(a.iid)}">
|
|
67
|
+
<div><strong>Warning: your invite got out.</strong> Two agents used it, so I paused the first one: it cannot receive anything secret.
|
|
68
|
+
The second calls itself <strong>${esc(a.secondAccept.agent || 'unknown')}</strong>. Make a new invite and send it privately.</div></div>`);
|
|
69
|
+
}
|
|
70
|
+
for (const p of data?.pendingAccepts || []) {
|
|
71
|
+
parts.push(`<div class="pair-card pair-waiting" data-pending-accept="${esc(p.iid)}">
|
|
72
|
+
<div>Pair with ${claimed(p.claimedAgent, p.claimedOwner)}? Only approve if you know who sent you this invite.</div>
|
|
73
|
+
<div class="pair-meta">Check code (optional, to read to them): ${esc(p.checkCode)} · ${esc(timeLeft(p.exp, now))}</div>
|
|
74
|
+
<div class="pair-row"><button class="mnd-btn mnd-btn-primary" data-approve="${esc(p.iid)}">Approve</button>
|
|
75
|
+
<button class="mnd-btn mnd-btn-quiet" data-decline="${esc(p.iid)}">Decline</button></div></div>`);
|
|
76
|
+
}
|
|
77
|
+
for (const i of finishPrompts(data?.invites)) {
|
|
78
|
+
parts.push(`<div class="pair-card pair-waiting" data-finish-prompt="${esc(i.iid)}">
|
|
79
|
+
<div>${cap(claimed(i.acceptor.agent, i.acceptor.owner))} accepted your invite. Finish only if that is who you sent it to.</div>
|
|
80
|
+
<div class="pair-row"><button class="mnd-btn mnd-btn-primary" data-finish="${esc(i.iid)}">Finish</button>
|
|
81
|
+
<button class="mnd-btn mnd-btn-quiet mnd-btn-danger" data-not-them="${esc(i.iid)}">Not them</button></div></div>`);
|
|
82
|
+
}
|
|
83
|
+
return parts.join('');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function renderOpenInvites(invites, now = Date.now()) {
|
|
87
|
+
const open = (invites || []).filter((i) => i.state === 'open' && Date.parse(i.exp) > now);
|
|
88
|
+
if (open.length === 0) return '';
|
|
89
|
+
return '<div class="pair-step-title">Invites not used yet</div>' + open.map((i) => `<div class="pair-row" data-open-invite="${esc(i.iid)}">
|
|
90
|
+
<span>Invite ${esc(i.iid.slice(0, 6))} · ${esc(timeLeft(i.exp, now))}</span>
|
|
91
|
+
<button class="mnd-btn mnd-btn-quiet" data-revoke="${esc(i.iid)}">Cancel it</button></div>`).join('');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** One paired (or pairing) agent. No actions: invites replace the 12-word flow. */
|
|
29
95
|
export function renderPairing(p) {
|
|
30
96
|
const fp = esc(p.peerFp);
|
|
31
|
-
const
|
|
32
|
-
const state = p.state;
|
|
33
|
-
const label = esc(STATE_LABEL[state] || state || 'Unknown');
|
|
34
|
-
let actions = '';
|
|
35
|
-
if (state === 'pending-verification') {
|
|
36
|
-
const inputs = Array.from({ length: WORD_COUNT }, (_, i) =>
|
|
37
|
-
`<label class="pair-word"><span>${i + 1}</span><input type="text" autocomplete="off" autocapitalize="off" spellcheck="false" data-their-word="${fp}" data-idx="${i}" aria-label="Their word ${i + 1}" /></label>`).join('');
|
|
38
|
-
actions = `
|
|
39
|
-
<div class="pair-step">
|
|
40
|
-
<div class="pair-step-title">1. See the words on this side</div>
|
|
41
|
-
<div class="pair-row"><input type="password" class="mnd-pin" data-show-pin="${fp}" placeholder="Your PIN" autocomplete="off" />
|
|
42
|
-
<button class="mnd-btn mnd-btn-primary" data-show-words="${fp}">Show my words</button></div>
|
|
43
|
-
<ol class="pair-own-words" data-own-words="${fp}"></ol>
|
|
44
|
-
</div>
|
|
45
|
-
<div class="pair-step">
|
|
46
|
-
<div class="pair-step-title">2. Type the 12 words the OTHER agent's operator sees</div>
|
|
47
|
-
<div class="pair-words">${inputs}</div>
|
|
48
|
-
<div class="pair-row"><input type="password" class="mnd-pin" data-confirm-pin="${fp}" placeholder="Your PIN" autocomplete="off" />
|
|
49
|
-
<button class="mnd-btn mnd-btn-primary" data-confirm="${fp}">Confirm they match</button></div>
|
|
50
|
-
<div class="mnd-hint">Compare over a call or in person, not through the agents. Any wrong word blocks this pairing.</div>
|
|
51
|
-
<button class="mnd-btn mnd-btn-quiet mnd-btn-danger" data-deny="${fp}">They don't match</button>
|
|
52
|
-
</div>`;
|
|
53
|
-
} else if (state === 'verification-failed') {
|
|
54
|
-
actions = `
|
|
55
|
-
<div class="pair-step">
|
|
56
|
-
<div class="mnd-hint">Clear this only once you know why the words were different.</div>
|
|
57
|
-
<div class="pair-row"><input type="password" class="mnd-pin" data-clear-pin="${fp}" placeholder="Your PIN" autocomplete="off" />
|
|
58
|
-
<button class="mnd-btn mnd-btn-quiet" data-clear="${fp}">Clear and start again</button></div>
|
|
59
|
-
</div>`;
|
|
60
|
-
}
|
|
97
|
+
const label = esc(STATE_LABEL[p.state] || p.state || 'Unknown');
|
|
61
98
|
return `<div class="pair-card" data-pairing="${fp}">
|
|
62
|
-
<div class="pair-head"><strong>${
|
|
63
|
-
${actions}
|
|
99
|
+
<div class="pair-head"><strong>${esc(p.peerName || 'Unknown agent')}</strong> <span class="pair-state pair-state-${esc(p.state)}">${label}</span></div>
|
|
64
100
|
<div class="pair-meta">Agent ${fp.slice(0, 12)}…</div>
|
|
65
101
|
</div>`;
|
|
66
102
|
}
|
|
67
103
|
|
|
68
104
|
export function renderPairings(list) {
|
|
69
|
-
if (!Array.isArray(list) || list.length === 0)
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
return list.map(renderPairing).join('');
|
|
105
|
+
if (!Array.isArray(list) || list.length === 0) return '<div class="mnd-hint">No paired agents yet.</div>';
|
|
106
|
+
return '<div class="pair-step-title">Agents</div>' + list.map(renderPairing).join('');
|
|
73
107
|
}
|
|
74
108
|
|
|
75
|
-
|
|
76
|
-
export function collectTypedWords(root, fp) {
|
|
77
|
-
const inputs = Array.from(root.querySelectorAll(`[data-their-word="${fp}"]`));
|
|
78
|
-
inputs.sort((a, b) => Number(a.dataset.idx) - Number(b.dataset.idx));
|
|
79
|
-
return inputs.map((i) => String(i.value || '').trim().toLowerCase());
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function createController({ doc, els, fetchImpl }) {
|
|
109
|
+
export function createController({ doc, els, fetchImpl, now = () => Date.now() }) {
|
|
83
110
|
let timer = null;
|
|
111
|
+
let heldPin = ''; // §8.2: asked once per page, memory only
|
|
112
|
+
let data = { invites: [], pendingAccepts: [], pairings: [] };
|
|
113
|
+
const lastHtml = new Map(); // section → last rendered HTML (§8.1: re-render only on change)
|
|
84
114
|
|
|
85
115
|
async function fetchJson(url, opts) {
|
|
86
116
|
const res = await fetchImpl(url, opts);
|
|
@@ -88,6 +118,7 @@ export function createController({ doc, els, fetchImpl }) {
|
|
|
88
118
|
try { body = await res.json(); } catch { body = null; }
|
|
89
119
|
return { status: res.status, body };
|
|
90
120
|
}
|
|
121
|
+
const post = (url, payload, method = 'POST') => fetchJson(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload ?? {}) });
|
|
91
122
|
|
|
92
123
|
function note(msg, kind) {
|
|
93
124
|
if (!els.notice) return;
|
|
@@ -95,70 +126,158 @@ export function createController({ doc, els, fetchImpl }) {
|
|
|
95
126
|
els.notice.className = kind === 'error' ? 'mnd-notice mnd-notice-err' : kind === 'success' ? 'mnd-notice mnd-notice-ok' : 'mnd-notice';
|
|
96
127
|
}
|
|
97
128
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
129
|
+
function setSection(el, key, html) {
|
|
130
|
+
if (!el || lastHtml.get(key) === html) return;
|
|
131
|
+
lastHtml.set(key, html);
|
|
132
|
+
el.innerHTML = html;
|
|
102
133
|
}
|
|
103
134
|
|
|
104
|
-
|
|
105
|
-
|
|
135
|
+
// The PIN field sits right above the decisions and shows only while one waits and no PIN is
|
|
136
|
+
// held: a person meets it before the button that needs it, and never otherwise.
|
|
137
|
+
function showPinRow() {
|
|
138
|
+
if (els.pinRow) els.pinRow.style.display = !heldPin && pendingCount(data) > 0 ? '' : 'none';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function render() {
|
|
142
|
+
setSection(els.pending, 'pending', renderPending(data, now()));
|
|
143
|
+
setSection(els.open, 'open', renderOpenInvites(data.invites, now()));
|
|
144
|
+
setSection(els.list, 'list', renderPairings(data.pairings));
|
|
145
|
+
const n = pendingCount(data);
|
|
146
|
+
if (els.tabButton) {
|
|
147
|
+
const base = els.tabButton.dataset.baseLabel || (els.tabButton.dataset.baseLabel = els.tabButton.textContent);
|
|
148
|
+
els.tabButton.textContent = n > 0 ? `${base} (${n})` : base;
|
|
149
|
+
}
|
|
150
|
+
// The tabs sit behind the header menu button, so the count must also show THERE or nobody
|
|
151
|
+
// sees it (found walking the flow as a person, 2026-10-14).
|
|
152
|
+
if (els.navToggle) {
|
|
153
|
+
let badge = els.navToggle.querySelector('[data-pair-badge]');
|
|
154
|
+
if (!badge && n > 0) {
|
|
155
|
+
badge = doc.createElement('span');
|
|
156
|
+
badge.setAttribute('data-pair-badge', '');
|
|
157
|
+
badge.className = 'pair-nav-badge';
|
|
158
|
+
els.navToggle.appendChild(badge);
|
|
159
|
+
}
|
|
160
|
+
if (badge) {
|
|
161
|
+
// On a phone only the number shows (the word would push the header past the screen).
|
|
162
|
+
badge.innerHTML = n > 0 ? `${n}<span class="pair-nav-badge-word"> waiting</span>` : '';
|
|
163
|
+
badge.setAttribute('aria-label', n > 0 ? `${n} pairing decision${n === 1 ? '' : 's'} waiting` : '');
|
|
164
|
+
badge.style.display = n > 0 ? '' : 'none';
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
showPinRow();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function load() {
|
|
106
171
|
try {
|
|
107
|
-
const
|
|
108
|
-
if (status === 503
|
|
109
|
-
|
|
110
|
-
|
|
172
|
+
const [inv, pr] = await Promise.all([fetchJson('/threadline/pairing/invites'), fetchJson('/threadline/pairing')]);
|
|
173
|
+
if (inv.status === 503 || pr.status === 503) {
|
|
174
|
+
const off = /not enabled/.test(String(inv.body?.error ?? pr.body?.error ?? ''));
|
|
175
|
+
setSection(els.list, 'list', off
|
|
176
|
+
? '<div class="mnd-hint">Pairing is turned off on this agent.</div>'
|
|
177
|
+
: '<div class="mnd-hint">Pairing runs on the machine that holds this agent\'s Threadline identity. Open the dashboard on that machine.</div>');
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (inv.status !== 200) { note(`Could not load invites: ${inv.body?.error ?? `HTTP ${inv.status}`}`, 'error'); return; }
|
|
181
|
+
data = { invites: inv.body?.invites ?? [], pendingAccepts: inv.body?.pendingAccepts ?? [], pairings: pr.status === 200 ? (pr.body?.pairings ?? []) : data.pairings };
|
|
182
|
+
renderForChoices(inv.body?.knownPeers ?? []);
|
|
183
|
+
render();
|
|
111
184
|
} catch (e) {
|
|
112
185
|
note(`Could not reach the agent: ${e?.message ?? e}`, 'error');
|
|
113
186
|
}
|
|
114
187
|
}
|
|
115
188
|
|
|
116
|
-
|
|
117
|
-
|
|
189
|
+
/** The PIN for one action: the held one, or the field's (asked once). */
|
|
190
|
+
function pinForAction() {
|
|
191
|
+
if (heldPin) return heldPin;
|
|
192
|
+
const v = els.pin ? String(els.pin.value || '') : '';
|
|
193
|
+
return v;
|
|
194
|
+
}
|
|
195
|
+
/** After a PIN-gated call: keep an accepted PIN, forget a refused one. */
|
|
196
|
+
function settlePin(pin, status) {
|
|
197
|
+
if (status === 403 || status === 429) { heldPin = ''; if (els.pin) els.pin.value = ''; showPinRow(); return; }
|
|
198
|
+
if (status < 500 && status !== 401) { heldPin = pin; if (els.pin) els.pin.value = ''; showPinRow(); }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function pinned(url, okMsg) {
|
|
202
|
+
const pin = pinForAction();
|
|
203
|
+
if (!pin) { note('Enter your PIN first (you will only be asked once).', 'error'); if (els.pin) els.pin.focus?.(); return; }
|
|
204
|
+
const { status, body } = await post(url, { pin });
|
|
205
|
+
settlePin(pin, status);
|
|
206
|
+
if (status === 403) { note('That PIN was not accepted.', 'error'); return; }
|
|
207
|
+
if (status !== 200) { note(`That didn't work: ${body?.error ?? `HTTP ${status}`}`, 'error'); return; }
|
|
208
|
+
note(okMsg, 'success');
|
|
209
|
+
await load();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// "Who is it for?": a known agent binds the invite to it (spec §4.1: the default whenever the
|
|
213
|
+
// inviter starts from a peer it already knows). Rebuilt only when the list changes, and the
|
|
214
|
+
// operator's current choice is kept.
|
|
215
|
+
function renderForChoices(peers) {
|
|
216
|
+
if (!els.forPeer) return;
|
|
217
|
+
const key = JSON.stringify(peers.map((p) => [p.fp, p.name]));
|
|
218
|
+
if (lastHtml.get('for') === key) return;
|
|
219
|
+
const keep = els.forPeer.value;
|
|
220
|
+
lastHtml.set('for', key);
|
|
221
|
+
const opt = (fpValue, label) => { const o = doc.createElement('option'); o.value = fpValue; o.textContent = label; return o; };
|
|
222
|
+
els.forPeer.replaceChildren(opt('', 'Someone new'), ...peers.map((p) => opt(p.fp, `${p.name || 'Unknown agent'} (${p.fp.slice(0, 6)}…)`)));
|
|
223
|
+
if (peers.some((p) => p.fp === keep)) els.forPeer.value = keep;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function makeInvite() {
|
|
227
|
+
const ttlMinutes = els.ttl ? Number(els.ttl.value) || 60 : 60;
|
|
228
|
+
const bindFp = els.forPeer ? String(els.forPeer.value || '') : '';
|
|
229
|
+
const { status, body } = await post('/threadline/pairing/invites', bindFp ? { ttlMinutes, bindFp } : { ttlMinutes });
|
|
230
|
+
if (status !== 200 || !body?.message) { note(`Could not make an invite: ${body?.error ?? `HTTP ${status}`}`, 'error'); return; }
|
|
231
|
+
// Shown until the operator closes it; no timer touches this box.
|
|
232
|
+
els.inviteOut.innerHTML = `<div class="pair-step-title">Send this to the other person, privately, in any app. It works once.</div>
|
|
233
|
+
<textarea class="pair-invite-text" readonly rows="4" data-invite-text>${esc(body.message)}</textarea>
|
|
234
|
+
<div class="pair-row"><button class="mnd-btn mnd-btn-primary" data-copy-invite>Copy</button>
|
|
235
|
+
<button class="mnd-btn mnd-btn-quiet" data-close-invite>Done</button></div>
|
|
236
|
+
<div class="mnd-hint">Expires in ${esc(timeLeft(body.exp, now()))}. When they accept, you will see it at the top of this card.</div>`;
|
|
237
|
+
await load();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function copyInvite() {
|
|
241
|
+
const ta = els.inviteOut.querySelector('[data-invite-text]');
|
|
242
|
+
if (!ta) return;
|
|
243
|
+
try {
|
|
244
|
+
await doc.defaultView.navigator.clipboard.writeText(ta.value);
|
|
245
|
+
note('Copied. Paste it to the other person.', 'success');
|
|
246
|
+
} catch {
|
|
247
|
+
ta.select?.();
|
|
248
|
+
note('Select the text and copy it yourself (your browser blocked copying).', 'info');
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async function inspect() {
|
|
253
|
+
const text = els.inviteIn ? String(els.inviteIn.value || '') : '';
|
|
254
|
+
if (!text.includes('tlinv1:')) { note('Paste the whole invite you were sent.', 'error'); return; }
|
|
255
|
+
const { status, body } = await post('/threadline/pairing/invites/inspect', { invite: text });
|
|
256
|
+
if (status !== 200) { note(`I can't use that invite: ${body?.error ?? `HTTP ${status}`}`, 'error'); return; }
|
|
257
|
+
els.inviteIn.value = ''; // the secret leaves the page; the decision now waits at the top
|
|
258
|
+
note('Got it. Approve or decline it at the top of this card.', 'info');
|
|
259
|
+
await load();
|
|
118
260
|
}
|
|
119
261
|
|
|
120
262
|
async function onClick(ev) {
|
|
121
263
|
const t = ev.target;
|
|
122
264
|
if (!t || !t.dataset) return;
|
|
123
|
-
const
|
|
124
|
-
const take = (attr, fp) => { const el = root.querySelector(`[${attr}="${fp}"]`); const v = el ? el.value : ''; if (el) el.value = ''; return v; };
|
|
265
|
+
const d = t.dataset;
|
|
125
266
|
try {
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
note('These are the words on this side. Read them to the other operator and type theirs below.', 'info');
|
|
137
|
-
} else if (t.dataset.confirm) {
|
|
138
|
-
const fp = t.dataset.confirm;
|
|
139
|
-
const words = collectTypedWords(root, fp);
|
|
140
|
-
const pin = take('data-confirm-pin', fp);
|
|
141
|
-
if (words.some((w) => !w)) { note(`Type all ${WORD_COUNT} of the other side's words first.`, 'error'); return; }
|
|
142
|
-
if (!pin) { note('Enter your PIN to confirm.', 'error'); return; }
|
|
143
|
-
const { status, body } = await post(`/threadline/pairing/${fp}/verify`, { words, pin });
|
|
144
|
-
if (status !== 200) { note(`Not confirmed — the server refused: ${body?.error ?? `HTTP ${status}`}`, 'error'); return; }
|
|
145
|
-
note(body?.state === 'mutual-verified' ? 'Verified. Every word matched.' : 'The words did not match. This pairing is now blocked.', body?.state === 'mutual-verified' ? 'success' : 'error');
|
|
146
|
-
await load();
|
|
147
|
-
} else if (t.dataset.deny) {
|
|
148
|
-
const fp = t.dataset.deny;
|
|
149
|
-
const pin = take('data-confirm-pin', fp);
|
|
150
|
-
if (!pin) { note('Enter your PIN (in the field above) to record that the words did not match.', 'error'); return; }
|
|
151
|
-
const { status, body } = await post(`/threadline/pairing/${fp}/verify`, { match: false, pin });
|
|
152
|
-
if (status !== 200) { note(`Not recorded — the server refused: ${body?.error ?? `HTTP ${status}`}`, 'error'); return; }
|
|
153
|
-
note('Recorded: the words did not match. This pairing is blocked.', 'success');
|
|
267
|
+
if (d.makeInvite !== undefined) await makeInvite();
|
|
268
|
+
else if (d.copyInvite !== undefined) await copyInvite();
|
|
269
|
+
else if (d.closeInvite !== undefined) els.inviteOut.innerHTML = '';
|
|
270
|
+
else if (d.inspectInvite !== undefined) await inspect();
|
|
271
|
+
else if (d.approve) await pinned(`/threadline/pairing/invites/${d.approve}/approve`, 'Approved. Waiting for the other side to finish.');
|
|
272
|
+
else if (d.finish) await pinned(`/threadline/pairing/invites/${d.finish}/finish`, 'Finished. You are paired.');
|
|
273
|
+
else if (d.notThem) await pinned(`/threadline/pairing/invites/${d.notThem}/not-them`, 'Not paired. That invite is used up.');
|
|
274
|
+
else if (d.decline) {
|
|
275
|
+
const { status, body } = await post(`/threadline/pairing/invites/${d.decline}/decline`, {});
|
|
276
|
+
note(status === 200 ? 'Declined. Nothing was paired.' : `That didn't work: ${body?.error ?? `HTTP ${status}`}`, status === 200 ? 'success' : 'error');
|
|
154
277
|
await load();
|
|
155
|
-
} else if (
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
if (!pin) { note('Enter your PIN to clear this pairing.', 'error'); return; }
|
|
159
|
-
const { status, body } = await post(`/threadline/pairing/${fp}/start`, { clearFailed: true, pin });
|
|
160
|
-
if (status !== 200) { note(`Not cleared — the server refused: ${body?.error ?? `HTTP ${status}`}`, 'error'); return; }
|
|
161
|
-
note('Cleared. Compare the words again.', 'success');
|
|
278
|
+
} else if (d.revoke) {
|
|
279
|
+
const { status, body } = await post(`/threadline/pairing/invites/${d.revoke}`, {}, 'DELETE');
|
|
280
|
+
note(status === 200 ? 'Cancelled. That invite no longer works.' : `That didn't work: ${body?.error ?? `HTTP ${status}`}`, status === 200 ? 'success' : 'error');
|
|
162
281
|
await load();
|
|
163
282
|
}
|
|
164
283
|
} catch (e) {
|
|
@@ -168,12 +287,13 @@ export function createController({ doc, els, fetchImpl }) {
|
|
|
168
287
|
|
|
169
288
|
return {
|
|
170
289
|
start() {
|
|
171
|
-
if (els.
|
|
290
|
+
if (els.root && !els.root.__pairWired) { els.root.addEventListener('click', onClick); els.root.__pairWired = true; }
|
|
172
291
|
load();
|
|
173
|
-
if (!timer) timer = setInterval(
|
|
292
|
+
if (!timer) timer = setInterval(load, POLL_MS);
|
|
174
293
|
},
|
|
175
294
|
stop() { if (timer) { clearInterval(timer); timer = null; } },
|
|
176
295
|
load,
|
|
177
296
|
onClick,
|
|
297
|
+
hasPin: () => Boolean(heldPin),
|
|
178
298
|
};
|
|
179
299
|
}
|
|
@@ -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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"}
|