ai_co_discuss 0.3.28 → 0.3.30

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/web/app.js CHANGED
@@ -35,6 +35,10 @@ const LOCAL_CONNECTION_KEY = "discuss-local-connection";
35
35
  const LOCAL_CONNECTION_TIMEOUT_MS = 15000;
36
36
  // A relayed launch waits this long for the paired computer before the request is kept as a pending item.
37
37
  const RELAY_WAIT_MS = 45000;
38
+ // A relayed read that goes unanswered or finds the computer briefly away is asked again after these pauses; reads change nothing.
39
+ const RELAY_READ_RETRY_MS = [1000, 3000];
40
+ // After the connection drops, the page reconnects by itself after these pauses, the last one repeating until it is back.
41
+ const RECONNECT_DELAYS_MS = [2000, 4000, 8000, 15000, 30000];
38
42
  const RELAY_POLL_MS = 1500;
39
43
  const MODEL_CATALOG_TIMEOUT_MS = 35000;
40
44
  // Without the local service, local operations go through the paired computer, encrypted so the website only sees ciphertext.
@@ -368,10 +372,24 @@ function management_error_message(room_id, error) {
368
372
  return error.message;
369
373
  }
370
374
 
371
- /** Show connection state without claiming that any AI is online. */
375
+ let reconnect_timer = null, reconnect_attempt = 0;
376
+ /** Reconnect without waiting for the user, backing off while the site or the computer stays away. */
377
+ function schedule_reconnect() {
378
+ if (reconnect_timer !== null || document.hidden) return;
379
+ const delay = RECONNECT_DELAYS_MS[Math.min(reconnect_attempt, RECONNECT_DELAYS_MS.length - 1)];
380
+ reconnect_attempt += 1;
381
+ reconnect_timer = setTimeout(() => {
382
+ reconnect_timer = null;
383
+ if (!state.connected && !document.hidden) reconnect_live_updates({ automatic: true });
384
+ }, delay);
385
+ }
386
+
387
+ /** Show connection state without claiming that any AI is online; a lost connection is retried automatically. */
372
388
  function set_connection(connected) {
373
389
  state.connected = connected;
374
390
  state.reconnecting = false;
391
+ if (connected) { clearTimeout(reconnect_timer); reconnect_timer = null; reconnect_attempt = 0; }
392
+ else schedule_reconnect();
375
393
  element("connection-dot").className = `status-dot ${connected ? "connected" : "failed"}`;
376
394
  element("connection-status").textContent = connected ? "讨论网站已连接" : "网站连接中断,可手动刷新";
377
395
  publish_view();
@@ -2021,7 +2039,16 @@ async function relay_command(action, payload) {
2021
2039
 
2022
2040
  /** Read or write one room route on the paired computer's local website, as its owner, and answer as request_json would. */
2023
2041
  async function relay_room_request(path, payload) {
2024
- const answer = await relay_exchange("room-api", { method: payload === undefined ? "GET" : "POST", path, body: payload });
2042
+ let answer;
2043
+ for (let attempt = 0; ; attempt += 1) {
2044
+ try { answer = await relay_exchange("room-api", { method: payload === undefined ? "GET" : "POST", path, body: payload }); break; }
2045
+ catch (error) {
2046
+ // Only reads are asked again: a write whose outcome is unconfirmed must not be sent twice.
2047
+ const transient = error.unconfirmed || error.offline || error instanceof TypeError;
2048
+ if (payload !== undefined || !transient || attempt >= RELAY_READ_RETRY_MS.length) throw error;
2049
+ await new Promise((resolve) => setTimeout(resolve, RELAY_READ_RETRY_MS[attempt]));
2050
+ }
2051
+ }
2025
2052
  if (answer.status >= 400) throw Object.assign(new Error(answer.body?.error || "请求失败,请重试"), { preflight: answer.status < 500, status: answer.status });
2026
2053
  return answer;
2027
2054
  }
@@ -2168,7 +2195,13 @@ function relay_event_source() {
2168
2195
  let frame;
2169
2196
  try { frame = JSON.parse(event.data); } catch { return; }
2170
2197
  if (frame.type === "change") emit("change", JSON.stringify({ rooms: frame.rooms }));
2171
- if (frame.type === "presence") { state.remote_online = Boolean(frame.online); publish_view(); }
2198
+ if (frame.type === "presence") {
2199
+ // The computer coming back (after a restart, say) is read at once rather than at the next poll.
2200
+ const back = frame.online && state.remote_online === false;
2201
+ state.remote_online = Boolean(frame.online);
2202
+ publish_view();
2203
+ if (back) emit("ready");
2204
+ }
2172
2205
  });
2173
2206
  socket.addEventListener("close", () => {
2174
2207
  clearInterval(ping);
@@ -3738,10 +3771,10 @@ function start_live_updates({ refresh = true } = {}) {
3738
3771
  if (refresh) queue_live_refresh();
3739
3772
  }
3740
3773
 
3741
- /** Reopen the update stream and read the site now; used by the footer retry and when the stream was closed. */
3742
- async function reconnect_live_updates() {
3774
+ /** Reopen the update stream and read the site now; used by the footer retry and, quietly, by automatic reconnects. */
3775
+ async function reconnect_live_updates({ automatic = false } = {}) {
3743
3776
  if (document.hidden) return;
3744
- state.reconnecting = true;
3777
+ state.reconnecting = !automatic;
3745
3778
  publish_view();
3746
3779
  live_updates.source?.close();
3747
3780
  live_updates.source = null;
@@ -3810,6 +3843,8 @@ async function initialize() {
3810
3843
  else { start_live_updates(); load_admin_state(); }
3811
3844
  });
3812
3845
  window.addEventListener("pagehide", stop_live_updates);
3846
+ // The network coming back (a phone leaving a lift, a laptop waking) reconnects at once.
3847
+ window.addEventListener("online", () => { if (!state.connected) reconnect_live_updates({ automatic: true }); });
3813
3848
  window.addEventListener("pageshow", start_live_updates);
3814
3849
  }
3815
3850