clay-server 2.27.0-beta.8 → 2.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/lib/daemon-projects.js +164 -0
- package/lib/daemon.js +13 -126
- package/lib/mates-identity.js +132 -0
- package/lib/mates-knowledge.js +113 -0
- package/lib/mates-prompts.js +398 -0
- package/lib/mates.js +40 -599
- package/lib/project-connection.js +2 -0
- package/lib/project-debate.js +19 -12
- package/lib/project-http.js +4 -2
- package/lib/project-loop.js +110 -48
- package/lib/project-mate-interaction.js +4 -0
- package/lib/project-notifications.js +210 -0
- package/lib/project-sessions.js +5 -2
- package/lib/project-user-message.js +2 -1
- package/lib/project.js +26 -2
- package/lib/public/app.js +1193 -8521
- package/lib/public/css/command-palette.css +14 -0
- package/lib/public/css/loop.css +301 -0
- package/lib/public/css/notifications-center.css +190 -0
- package/lib/public/css/rewind.css +6 -0
- package/lib/public/index.html +89 -35
- package/lib/public/modules/app-connection.js +160 -0
- package/lib/public/modules/app-cursors.js +473 -0
- package/lib/public/modules/app-debate-ui.js +389 -0
- package/lib/public/modules/app-dm.js +627 -0
- package/lib/public/modules/app-favicon.js +212 -0
- package/lib/public/modules/app-header.js +229 -0
- package/lib/public/modules/app-home-hub.js +600 -0
- package/lib/public/modules/app-loop-ui.js +589 -0
- package/lib/public/modules/app-loop-wizard.js +439 -0
- package/lib/public/modules/app-messages.js +1560 -0
- package/lib/public/modules/app-misc.js +299 -0
- package/lib/public/modules/app-notifications.js +372 -0
- package/lib/public/modules/app-panels.js +888 -0
- package/lib/public/modules/app-projects.js +798 -0
- package/lib/public/modules/app-rate-limit.js +451 -0
- package/lib/public/modules/app-rendering.js +597 -0
- package/lib/public/modules/app-skills-install.js +234 -0
- package/lib/public/modules/command-palette.js +27 -4
- package/lib/public/modules/input.js +31 -20
- package/lib/public/modules/scheduler-config.js +1532 -0
- package/lib/public/modules/scheduler-history.js +79 -0
- package/lib/public/modules/scheduler.js +33 -1554
- package/lib/public/modules/session-search.js +13 -1
- package/lib/public/modules/sidebar-mates.js +812 -0
- package/lib/public/modules/sidebar-mobile.js +1269 -0
- package/lib/public/modules/sidebar-projects.js +1449 -0
- package/lib/public/modules/sidebar-sessions.js +986 -0
- package/lib/public/modules/sidebar.js +232 -4591
- package/lib/public/modules/store.js +27 -0
- package/lib/public/modules/ws-ref.js +7 -0
- package/lib/public/style.css +1 -0
- package/lib/sdk-bridge.js +96 -717
- package/lib/sdk-message-processor.js +587 -0
- package/lib/sdk-message-queue.js +42 -0
- package/lib/sdk-skill-discovery.js +131 -0
- package/lib/server-admin.js +712 -0
- package/lib/server-auth.js +737 -0
- package/lib/server-dm.js +221 -0
- package/lib/server-mates.js +281 -0
- package/lib/server-palette.js +110 -0
- package/lib/server-settings.js +479 -0
- package/lib/server-skills.js +280 -0
- package/lib/server.js +246 -2755
- package/lib/sessions.js +11 -4
- package/lib/users-auth.js +146 -0
- package/lib/users-permissions.js +118 -0
- package/lib/users-preferences.js +210 -0
- package/lib/users.js +48 -398
- package/lib/ws-schema.js +498 -0
- package/package.json +1 -1
package/lib/public/index.html
CHANGED
|
@@ -88,6 +88,7 @@
|
|
|
88
88
|
<!-- Pill badges -->
|
|
89
89
|
<div id="skip-perms-pill" class="top-bar-pill pill-error hidden"><i data-lucide="shield-off"></i> <span>Skip perms</span></div>
|
|
90
90
|
<div id="client-count" class="top-bar-pill pill-accent hidden"><i data-lucide="users"></i> <span id="client-count-text"></span></div>
|
|
91
|
+
<button id="notif-center-btn" title="Notifications"><i data-lucide="bell"></i><span id="notif-center-badge" class="notif-badge hidden">0</span></button>
|
|
91
92
|
<button id="server-settings-btn" title="Server settings"><i data-lucide="settings"></i></button>
|
|
92
93
|
</div>
|
|
93
94
|
</div>
|
|
@@ -199,7 +200,7 @@
|
|
|
199
200
|
<div class="session-list-header">
|
|
200
201
|
<span>Sessions</span>
|
|
201
202
|
<div class="session-list-header-actions">
|
|
202
|
-
<button id="new-ralph-btn" type="button" class="new-ralph-pill" data-tip="New
|
|
203
|
+
<button id="new-ralph-btn" type="button" class="new-ralph-pill" data-tip="New Loop"><i data-lucide="repeat"></i> Loop</button>
|
|
203
204
|
<button id="new-session-btn" type="button" data-tip="New session"><i data-lucide="plus"></i></button>
|
|
204
205
|
<button id="resume-session-btn" type="button" data-tip="Import CLI session"><i data-lucide="import"></i></button>
|
|
205
206
|
<button id="search-session-btn" type="button" data-tip="Search sessions"><i data-lucide="search"></i></button>
|
|
@@ -1547,59 +1548,106 @@
|
|
|
1547
1548
|
<div class="ralph-wizard-backdrop"></div>
|
|
1548
1549
|
<div class="ralph-wizard-card">
|
|
1549
1550
|
<div class="ralph-wizard-header">
|
|
1550
|
-
<span>New
|
|
1551
|
+
<span>New Loop</span>
|
|
1551
1552
|
<button id="ralph-wizard-close">×</button>
|
|
1552
1553
|
</div>
|
|
1553
1554
|
<div class="ralph-wizard-progress">
|
|
1554
1555
|
<span class="ralph-dot active" data-step="1"></span>
|
|
1555
1556
|
<span class="ralph-dot" data-step="2"></span>
|
|
1557
|
+
<span class="ralph-dot" data-step="3"></span>
|
|
1556
1558
|
</div>
|
|
1557
1559
|
<div class="ralph-wizard-steps">
|
|
1558
1560
|
<div class="ralph-step active" data-step="1">
|
|
1559
|
-
<h3>
|
|
1560
|
-
<div
|
|
1561
|
-
|
|
1562
|
-
<div class="ralph-
|
|
1563
|
-
<div class="ralph-
|
|
1564
|
-
<
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1561
|
+
<h3>Choose loop mode</h3>
|
|
1562
|
+
<div id="ralph-install-status" class="hidden"></div>
|
|
1563
|
+
<div class="ralph-mode-preview" id="ralph-mode-preview">
|
|
1564
|
+
<div class="ralph-mode-preview-judge">
|
|
1565
|
+
<div class="ralph-intro-diagram">
|
|
1566
|
+
<div class="ralph-dia-row">
|
|
1567
|
+
<span class="ralph-dia-node">Coding agent</span>
|
|
1568
|
+
<span class="ralph-dia-fwd">→</span>
|
|
1569
|
+
<span class="ralph-dia-node">Judge reviews</span>
|
|
1570
|
+
<span class="ralph-dia-fwd">→</span>
|
|
1571
|
+
<span class="ralph-dia-done">Done</span>
|
|
1572
|
+
</div>
|
|
1573
|
+
<div class="ralph-dia-loop">
|
|
1574
|
+
<svg class="ralph-dia-svg" viewBox="0 0 200 28" preserveAspectRatio="none">
|
|
1575
|
+
<path d="M170 2 C190 2 196 14 190 24 L10 24 C4 24 -2 14 10 2" fill="none" stroke="currentColor" stroke-width="1.2" stroke-dasharray="4 3"/>
|
|
1576
|
+
<polygon points="14,-1 6,5 14,5" fill="currentColor" transform="translate(0,0)"/>
|
|
1577
|
+
</svg>
|
|
1578
|
+
<span class="ralph-dia-loop-label">Fail — retry</span>
|
|
1579
|
+
</div>
|
|
1569
1580
|
</div>
|
|
1570
|
-
<
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1581
|
+
<p class="ralph-mode-preview-text">Each iteration is reviewed by a separate judge session that decides pass or fail. The coding agent does not see the judge's result. On fail, a fresh session retries from scratch. The loop ends on pass or when the max iteration count is reached. Best suited for tasks with clear success criteria, like making tests pass or matching a spec.</p>
|
|
1582
|
+
</div>
|
|
1583
|
+
<div class="ralph-mode-preview-simple" style="display:none">
|
|
1584
|
+
<div class="ralph-intro-diagram">
|
|
1585
|
+
<div class="ralph-dia-row">
|
|
1586
|
+
<span class="ralph-dia-node">Coding agent</span>
|
|
1587
|
+
<span class="ralph-dia-fwd">→</span>
|
|
1588
|
+
<span class="ralph-dia-node">Repeat N times</span>
|
|
1589
|
+
<span class="ralph-dia-fwd">→</span>
|
|
1590
|
+
<span class="ralph-dia-done">Done</span>
|
|
1591
|
+
</div>
|
|
1576
1592
|
</div>
|
|
1593
|
+
<p class="ralph-mode-preview-text">The original Ralph Loop. The prompt runs a set number of times with no review step in between. Each iteration starts a fresh session. There is no judge and no pass/fail. Useful when the task itself is the goal, not a specific outcome. Suited for bulk refactoring, repeated migrations, or running the same operation across multiple targets.</p>
|
|
1577
1594
|
</div>
|
|
1578
|
-
|
|
1579
|
-
|
|
1595
|
+
</div>
|
|
1596
|
+
<div class="ralph-mode-choice">
|
|
1597
|
+
<button class="ralph-mode-card active" data-loop-mode="judge">
|
|
1598
|
+
<span class="ralph-mode-card-icon"><i data-lucide="scale"></i></span>
|
|
1599
|
+
<span class="ralph-mode-card-title">Clay Loop</span>
|
|
1600
|
+
<span class="ralph-mode-card-sub">Loop + Judge</span>
|
|
1601
|
+
</button>
|
|
1602
|
+
<button class="ralph-mode-card" data-loop-mode="simple">
|
|
1603
|
+
<span class="ralph-mode-card-icon"><i data-lucide="repeat"></i></span>
|
|
1604
|
+
<span class="ralph-mode-card-title">Ralph Loop</span>
|
|
1605
|
+
<span class="ralph-mode-card-sub">Loop only</span>
|
|
1606
|
+
</button>
|
|
1580
1607
|
</div>
|
|
1581
1608
|
</div>
|
|
1582
1609
|
<div class="ralph-step" data-step="2">
|
|
1583
|
-
<h3>
|
|
1584
|
-
<
|
|
1585
|
-
|
|
1586
|
-
<
|
|
1610
|
+
<h3>Who writes the instructions?</h3>
|
|
1611
|
+
<p class="ralph-hint">Choose who provides each file.</p>
|
|
1612
|
+
<div class="ralph-authorship-rows">
|
|
1613
|
+
<div class="ralph-authorship-row">
|
|
1614
|
+
<div class="ralph-authorship-info">
|
|
1615
|
+
<span class="ralph-authorship-label">PROMPT</span>
|
|
1616
|
+
<span class="ralph-authorship-desc">The instructions the coding agent follows each iteration.</span>
|
|
1617
|
+
</div>
|
|
1618
|
+
<div class="config-segmented ralph-authorship-toggle" data-file="prompt">
|
|
1619
|
+
<button class="config-segment-btn" data-author="me">Me</button>
|
|
1620
|
+
<button class="config-segment-btn active" data-author="clay">Clay</button>
|
|
1621
|
+
</div>
|
|
1622
|
+
</div>
|
|
1623
|
+
<div class="ralph-authorship-row" id="ralph-judge-authorship-row">
|
|
1624
|
+
<div class="ralph-authorship-info">
|
|
1625
|
+
<span class="ralph-authorship-label">JUDGE</span>
|
|
1626
|
+
<span class="ralph-authorship-desc">The criteria used to evaluate each iteration as pass or fail.</span>
|
|
1627
|
+
</div>
|
|
1628
|
+
<div class="config-segmented ralph-authorship-toggle" data-file="judge">
|
|
1629
|
+
<button class="config-segment-btn" data-author="me">Me</button>
|
|
1630
|
+
<button class="config-segment-btn active" data-author="clay">Clay</button>
|
|
1631
|
+
</div>
|
|
1632
|
+
</div>
|
|
1587
1633
|
</div>
|
|
1588
|
-
|
|
1589
|
-
|
|
1634
|
+
</div>
|
|
1635
|
+
<div class="ralph-step" data-step="3">
|
|
1636
|
+
<h3>Provide details</h3>
|
|
1637
|
+
<div id="ralph-input-task-section" class="hidden">
|
|
1590
1638
|
<label class="ralph-field-label" for="ralph-task">Task description <span class="ralph-field-req">*</span></label>
|
|
1639
|
+
<p class="ralph-hint">Write a rough idea. Clay will refine it into detailed instructions.</p>
|
|
1591
1640
|
<textarea id="ralph-task" rows="5" placeholder="e.g. Add dark mode toggle to the settings page"></textarea>
|
|
1592
1641
|
</div>
|
|
1593
|
-
<div
|
|
1594
|
-
<p class="ralph-hint">Paste your PROMPT.md content. JUDGE.md is optional; if omitted, Clay will generate it for you.</p>
|
|
1642
|
+
<div id="ralph-input-prompt-section" class="hidden">
|
|
1595
1643
|
<label class="ralph-field-label" for="ralph-prompt-input">PROMPT.md <span class="ralph-field-req">*</span></label>
|
|
1596
1644
|
<textarea id="ralph-prompt-input" rows="5" placeholder="Paste your PROMPT.md content here"></textarea>
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
<
|
|
1645
|
+
</div>
|
|
1646
|
+
<div id="ralph-input-judge-section" class="hidden">
|
|
1647
|
+
<label class="ralph-field-label" for="ralph-judge-input" style="margin-top: 8px;">JUDGE.md <span class="ralph-field-req">*</span></label>
|
|
1648
|
+
<textarea id="ralph-judge-input" rows="4" placeholder="Paste your JUDGE.md content here"></textarea>
|
|
1600
1649
|
</div>
|
|
1601
1650
|
</div>
|
|
1602
|
-
<input type="hidden" id="ralph-max-iterations" value="25">
|
|
1603
1651
|
</div>
|
|
1604
1652
|
<div class="ralph-wizard-footer">
|
|
1605
1653
|
<button id="ralph-wizard-back" class="ralph-btn secondary">Back</button>
|
|
@@ -1611,14 +1659,13 @@
|
|
|
1611
1659
|
</div>
|
|
1612
1660
|
</div>
|
|
1613
1661
|
|
|
1614
|
-
<!-- Ralph Preview Modal -->
|
|
1662
|
+
<!-- Ralph Preview Modal (also serves as the execution modal) -->
|
|
1615
1663
|
<div id="ralph-preview-modal" class="hidden">
|
|
1616
1664
|
<div class="confirm-backdrop"></div>
|
|
1617
1665
|
<div class="ralph-preview-dialog">
|
|
1618
1666
|
<div class="ralph-preview-header">
|
|
1619
|
-
<span class="ralph-preview-name" id="ralph-preview-name">
|
|
1667
|
+
<span class="ralph-preview-name" id="ralph-preview-name">Loop</span>
|
|
1620
1668
|
<div class="ralph-preview-actions">
|
|
1621
|
-
<button class="ralph-preview-action-btn primary" id="ralph-preview-run" title="Start loop"><i data-lucide="play"></i> Run now</button>
|
|
1622
1669
|
<button class="ralph-preview-action-icon" id="ralph-preview-delete" title="Cancel and discard"><i data-lucide="trash-2"></i></button>
|
|
1623
1670
|
</div>
|
|
1624
1671
|
</div>
|
|
@@ -1627,6 +1674,13 @@
|
|
|
1627
1674
|
<button class="ralph-tab" data-tab="judge">JUDGE.md</button>
|
|
1628
1675
|
</div>
|
|
1629
1676
|
<div id="ralph-preview-body" class="ralph-preview-body"></div>
|
|
1677
|
+
<div class="ralph-preview-footer" id="ralph-preview-footer">
|
|
1678
|
+
<button class="ralph-run-combo" id="ralph-preview-run">
|
|
1679
|
+
<span class="ralph-run-combo-label" id="ralph-run-combo-label">Run</span>
|
|
1680
|
+
<span class="ralph-run-combo-sep"></span>
|
|
1681
|
+
<input type="number" id="ralph-preview-iterations" class="ralph-run-combo-input" value="20" min="1" max="100" onclick="event.stopPropagation()">
|
|
1682
|
+
</button>
|
|
1683
|
+
</div>
|
|
1630
1684
|
</div>
|
|
1631
1685
|
</div>
|
|
1632
1686
|
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// app-connection.js - WebSocket connection, reconnect, status
|
|
2
|
+
// Extracted from app.js (PR-22)
|
|
3
|
+
|
|
4
|
+
var _ctx = null;
|
|
5
|
+
var wasConnected = false;
|
|
6
|
+
var reconnectTimer = null;
|
|
7
|
+
var reconnectDelay = 1000;
|
|
8
|
+
var connectTimeoutId = null;
|
|
9
|
+
var disconnectNotifTimer = null;
|
|
10
|
+
var disconnectNotifShown = false;
|
|
11
|
+
|
|
12
|
+
export function initConnection(ctx) {
|
|
13
|
+
_ctx = ctx;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function setStatus(status) {
|
|
17
|
+
var dot = _ctx.getStatusDot();
|
|
18
|
+
if (dot) dot.className = "icon-strip-status";
|
|
19
|
+
if (status === "connected") {
|
|
20
|
+
if (dot) dot.classList.add("connected");
|
|
21
|
+
_ctx.setConnected(true);
|
|
22
|
+
_ctx.setProcessing(false);
|
|
23
|
+
_ctx.sendBtn.disabled = false;
|
|
24
|
+
_ctx.setSendBtnMode("send");
|
|
25
|
+
_ctx.connectOverlay.classList.add("hidden");
|
|
26
|
+
// Hide update banner on reconnect; server will re-send update_available if still needed
|
|
27
|
+
var updPill = document.getElementById("update-pill-wrap");
|
|
28
|
+
if (updPill) updPill.classList.add("hidden");
|
|
29
|
+
_ctx.stopVerbCycle();
|
|
30
|
+
} else if (status === "processing") {
|
|
31
|
+
if (dot) { dot.classList.add("connected"); dot.classList.add("processing"); }
|
|
32
|
+
_ctx.setProcessing(true);
|
|
33
|
+
_ctx.setSendBtnMode(_ctx.hasSendableContent() ? "send" : "stop");
|
|
34
|
+
} else {
|
|
35
|
+
_ctx.setConnected(false);
|
|
36
|
+
_ctx.sendBtn.disabled = true;
|
|
37
|
+
_ctx.connectOverlay.classList.remove("hidden");
|
|
38
|
+
_ctx.startVerbCycle();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function connect() {
|
|
43
|
+
var ws = _ctx.getWs();
|
|
44
|
+
if (ws) { ws.onclose = null; ws.close(); }
|
|
45
|
+
if (connectTimeoutId) { clearTimeout(connectTimeoutId); connectTimeoutId = null; }
|
|
46
|
+
|
|
47
|
+
var protocol = location.protocol === "https:" ? "wss:" : "ws:";
|
|
48
|
+
var newWs = new WebSocket(protocol + "//" + location.host + _ctx.getWsPath());
|
|
49
|
+
_ctx.setWs(newWs);
|
|
50
|
+
|
|
51
|
+
// If not connected within 3s, force retry
|
|
52
|
+
connectTimeoutId = setTimeout(function () {
|
|
53
|
+
if (!_ctx.isConnected()) {
|
|
54
|
+
newWs.onclose = null;
|
|
55
|
+
newWs.onerror = null;
|
|
56
|
+
newWs.close();
|
|
57
|
+
connect();
|
|
58
|
+
}
|
|
59
|
+
}, 3000);
|
|
60
|
+
|
|
61
|
+
newWs.onopen = function () {
|
|
62
|
+
if (connectTimeoutId) { clearTimeout(connectTimeoutId); connectTimeoutId = null; }
|
|
63
|
+
// Cancel pending "connection lost" notification if reconnected quickly
|
|
64
|
+
if (disconnectNotifTimer) {
|
|
65
|
+
clearTimeout(disconnectNotifTimer);
|
|
66
|
+
disconnectNotifTimer = null;
|
|
67
|
+
}
|
|
68
|
+
// Only show "restored" notification if "lost" was actually shown
|
|
69
|
+
var isMobileDevice = /Mobi|Android|iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
|
70
|
+
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
|
|
71
|
+
if (wasConnected && disconnectNotifShown && !isMobileDevice && _ctx.isNotifAlertEnabled() && !document.hasFocus() && "serviceWorker" in navigator && Notification.permission === "granted") {
|
|
72
|
+
navigator.serviceWorker.ready.then(function (reg) {
|
|
73
|
+
return reg.showNotification("Clay", {
|
|
74
|
+
body: "Server connection restored",
|
|
75
|
+
tag: "claude-disconnect",
|
|
76
|
+
});
|
|
77
|
+
}).catch(function () {});
|
|
78
|
+
}
|
|
79
|
+
disconnectNotifShown = false;
|
|
80
|
+
wasConnected = true;
|
|
81
|
+
setStatus("connected");
|
|
82
|
+
reconnectDelay = 1000;
|
|
83
|
+
if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
|
|
84
|
+
|
|
85
|
+
// Wrap ws.send to blink LED on outgoing traffic
|
|
86
|
+
var currentWs = _ctx.getWs();
|
|
87
|
+
var _origSend = currentWs.send.bind(currentWs);
|
|
88
|
+
currentWs.send = function (data) {
|
|
89
|
+
_ctx.blinkIO();
|
|
90
|
+
return _origSend(data);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
_ctx.onConnected();
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
newWs.onclose = function (e) {
|
|
97
|
+
if (connectTimeoutId) { clearTimeout(connectTimeoutId); connectTimeoutId = null; }
|
|
98
|
+
_ctx.closeDmUserPicker();
|
|
99
|
+
setStatus("disconnected");
|
|
100
|
+
_ctx.setProcessing(false);
|
|
101
|
+
_ctx.setActivity(null);
|
|
102
|
+
// Delay "connection lost" notification by 5s to suppress brief disconnects
|
|
103
|
+
if (!disconnectNotifTimer) {
|
|
104
|
+
disconnectNotifTimer = setTimeout(function () {
|
|
105
|
+
disconnectNotifTimer = null;
|
|
106
|
+
disconnectNotifShown = true;
|
|
107
|
+
var isMobileDevice = /Mobi|Android|iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
|
108
|
+
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
|
|
109
|
+
if (!isMobileDevice && _ctx.isNotifAlertEnabled() && !document.hasFocus() && "serviceWorker" in navigator && Notification.permission === "granted") {
|
|
110
|
+
navigator.serviceWorker.ready.then(function (reg) {
|
|
111
|
+
return reg.showNotification("Clay", {
|
|
112
|
+
body: "Server connection lost",
|
|
113
|
+
tag: "claude-disconnect",
|
|
114
|
+
});
|
|
115
|
+
}).catch(function () {});
|
|
116
|
+
}
|
|
117
|
+
}, 5000);
|
|
118
|
+
}
|
|
119
|
+
scheduleReconnect();
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
newWs.onerror = function () {};
|
|
123
|
+
|
|
124
|
+
newWs.onmessage = function (event) {
|
|
125
|
+
// Backup: if we're receiving messages, we're connected
|
|
126
|
+
if (!_ctx.isConnected()) {
|
|
127
|
+
setStatus("connected");
|
|
128
|
+
reconnectDelay = 1000;
|
|
129
|
+
if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
_ctx.blinkIO();
|
|
133
|
+
var msg;
|
|
134
|
+
try { msg = JSON.parse(event.data); } catch (e) { return; }
|
|
135
|
+
_ctx.processMessage(msg);
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function cancelReconnect() {
|
|
140
|
+
if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function scheduleReconnect() {
|
|
144
|
+
if (reconnectTimer) return;
|
|
145
|
+
reconnectTimer = setTimeout(function () {
|
|
146
|
+
reconnectTimer = null;
|
|
147
|
+
// Check if auth is still valid before reconnecting
|
|
148
|
+
fetch("/info").then(function (res) {
|
|
149
|
+
if (res.status === 401) {
|
|
150
|
+
location.reload();
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
connect();
|
|
154
|
+
}).catch(function () {
|
|
155
|
+
// Server still down, try connecting anyway
|
|
156
|
+
connect();
|
|
157
|
+
});
|
|
158
|
+
}, reconnectDelay);
|
|
159
|
+
reconnectDelay = Math.min(reconnectDelay * 1.5, 10000);
|
|
160
|
+
}
|