dsh-multi-router 0.5.5 → 0.5.7

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/lib/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createAccountClient
4
- } from "./chunk-EGLF4CWD.js";
4
+ } from "./chunk-U2MU2M5C.js";
5
5
  import {
6
6
  importAccount
7
7
  } from "./chunk-OFFVP6YN.js";
package/lib/client.js CHANGED
@@ -35,33 +35,40 @@ __export(client_exports, {
35
35
  name: () => name,
36
36
  performanceSummary: () => performanceSummary,
37
37
  readManagementResponse: () => readManagementResponse,
38
+ requestPluginReload: () => requestPluginReload,
38
39
  usageDateBounds: () => usageDateBounds
39
40
  });
40
41
  module.exports = __toCommonJS(client_exports);
41
42
  var import_react = require("react");
42
43
 
43
44
  // package.json
44
- var version = "0.5.5";
45
+ var version = "0.5.7";
45
46
 
46
47
  // src/client.jsx
47
48
  var import_jsx_runtime = require("react/jsx-runtime");
48
49
  var name = "dsh-multi-router-client";
49
50
  var inject = ["slots"];
50
51
  var ENDPOINT = "/.well-known/dsh-multi-router";
52
+ var reloadFromHost;
53
+ async function requestPluginReload(remote) {
54
+ if (!remote?.pluginManager?.setBundleEnabled) throw Error("\u5F53\u524D DSH \u672A\u63D0\u4F9B\u91CD\u65B0\u52A0\u8F7D\u63A5\u53E3\uFF0C\u8BF7\u5347\u7EA7 DSH \u6216\u5728\u63D2\u4EF6\u9875\u5173\u95ED\u540E\u91CD\u65B0\u542F\u7528\u672C\u63D2\u4EF6\u3002");
55
+ const answer = await remote.pluginManager.setBundleEnabled("dsh-multi-router", true);
56
+ const result = answer?.ok === true ? answer.value : answer;
57
+ if (answer?.ok === false || result?.application !== "applied") throw Error("DSH \u672A\u80FD\u5373\u65F6\u91CD\u8F7D\uFF0C\u8BF7\u68C0\u67E5\u7EC4\u4EF6\u9519\u8BEF\uFF1B\u4E0D\u652F\u6301\u70ED\u52A0\u8F7D\u7684\u65E7\u7248 DSH \u4ECD\u9700\u91CD\u542F\u3002");
58
+ }
51
59
  var backendNeedsRestart = (data) => !!data && data.pluginVersion !== version;
52
- function RestartGuidance({ data, retry }) {
60
+ function RestartGuidance({ data, retry, reload, busy }) {
53
61
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box stack", role: "alert", children: [
54
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u9700\u8981\u91CD\u65B0\u542F\u52A8 DeepSeek Harness" }),
55
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: data ? `\u9762\u677F\u7248\u672C ${version}\uFF0C\u540E\u53F0\u7248\u672C ${data.pluginVersion || "\u65E7\u7248"}\u3002\u66F4\u65B0\u540E\u7684\u540E\u53F0\u5C1A\u672A\u91CD\u65B0\u52A0\u8F7D\u3002` : "\u63D2\u4EF6\u540E\u53F0\u5C1A\u672A\u542F\u52A8\u3002\u82E5\u521A\u5B89\u88C5\u6216\u66F4\u65B0\u63D2\u4EF6\uFF0C\u8BF7\u5B8C\u6210\u4E0B\u9762\u7684\u91CD\u542F\u6B65\u9AA4\u3002" }),
56
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
57
- "macOS\uFF1A\u9876\u90E8\u83DC\u5355\u300CDeepSeek Harness \u2192 \u9000\u51FA DeepSeek Harness\u300D\uFF08\u2318Q\uFF09\uFF0C\u518D\u91CD\u65B0\u6253\u5F00\u5E94\u7528\u3002",
58
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}),
59
- "Windows\uFF1A\u9000\u51FA\u6258\u76D8\u4E2D\u7684 DeepSeek Harness\uFF0C\u518D\u91CD\u65B0\u6253\u5F00\u3002",
60
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}),
61
- "\u7F51\u9875\u7248\uFF1A\u91CD\u542F\u8FD0\u884C DSH \u7684\u670D\u52A1\u8FDB\u7A0B\u3002"
62
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u63D2\u4EF6\u6B63\u5728\u8FDE\u63A5\u6216\u540C\u6B65\u7248\u672C" }),
63
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: data ? `\u9762\u677F v${version}\uFF0C\u8FD0\u884C\u4E2D\u7684\u540E\u53F0 v${data.pluginVersion || "\u65E7\u7248"}\u3002\u6B63\u5728\u7B49\u5F85 DSH \u540C\u6B65\u65B0\u7248\u672C\u3002` : "\u6682\u65F6\u65E0\u6CD5\u8FDE\u63A5\u63D2\u4EF6\u540E\u53F0\u3002\u5B89\u88C5\u6216\u66F4\u65B0\u540E\u4F1A\u81EA\u52A8\u91CD\u8BD5\uFF0C\u4E5F\u53EF\u4EE5\u53EA\u91CD\u65B0\u52A0\u8F7D\u672C\u63D2\u4EF6\u3002" }),
64
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
65
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: reload, children: "\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6" }),
66
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: retry, children: "\u91CD\u65B0\u8FDE\u63A5" })
62
67
  ] }),
63
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u5173\u95ED\u7A97\u53E3\u6216\u5237\u65B0\u9762\u677F\u4E0D\u4F1A\u91CD\u65B0\u52A0\u8F7D\u540E\u53F0\u3002\u91CD\u542F\u540E\u4F1A\u4FDD\u7559\u8D26\u53F7\u3001\u8DEF\u7531\u548C\u7528\u91CF\uFF1B\u5982\u4ECD\u63D0\u793A\u5F02\u5E38\uFF0C\u8BF7\u67E5\u770B\u4E0B\u65B9\u7EC4\u4EF6\u7684\u542F\u7528\u9519\u8BEF\u3002" }),
64
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: retry, children: "\u6211\u5DF2\u91CD\u542F\uFF0C\u91CD\u65B0\u68C0\u67E5" }) })
68
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { children: [
69
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: "\u4ECD\u65E0\u6CD5\u8FDE\u63A5\uFF1F" }),
70
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u5148\u68C0\u67E5\u4E0B\u65B9\u7EC4\u4EF6\u7684\u542F\u7528\u9519\u8BEF\u3002\u65E7\u7248 DSH \u82E5\u4E0D\u652F\u6301\u8FD0\u884C\u4E2D\u91CD\u8F7D\uFF0C\u53EF\u5347\u7EA7 DSH\uFF0C\u6216\u901A\u8FC7\u300C\u9000\u51FA DeepSeek Harness\u300D\u5B8C\u5168\u9000\u51FA\u540E\u91CD\u65B0\u6253\u5F00\u3002\u5173\u95ED\u7A97\u53E3\u4E0D\u7B49\u4E8E\u9000\u51FA\uFF0C\u8D26\u53F7\u3001\u8DEF\u7531\u4E0E\u7528\u91CF\u4F1A\u4FDD\u7559\u3002" })
71
+ ] })
65
72
  ] });
66
73
  }
67
74
  async function readManagementResponse(res) {
@@ -69,7 +76,7 @@ async function readManagementResponse(res) {
69
76
  try {
70
77
  next = JSON.parse(await res.text());
71
78
  } catch {
72
- throw Error(`\u63D2\u4EF6\u540E\u53F0\u672A\u5C31\u7EEA\uFF08HTTP ${res.status}\uFF09\u3002\u8BF7\u68C0\u67E5\u63D2\u4EF6\u7EC4\u4EF6\u7684\u542F\u7528\u9519\u8BEF\uFF0C\u66F4\u65B0\u63D2\u4EF6\u540E\u5B8C\u5168\u9000\u51FA\u5E76\u91CD\u542F DSH\u3002`);
79
+ throw Error(`\u63D2\u4EF6\u540E\u53F0\u672A\u5C31\u7EEA\uFF08HTTP ${res.status}\uFF09\u3002\u6B63\u5728\u91CD\u65B0\u8FDE\u63A5\uFF1B\u82E5\u6301\u7EED\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6\u5E76\u68C0\u67E5\u7EC4\u4EF6\u9519\u8BEF\u3002`);
73
80
  }
74
81
  if (!next || typeof next !== "object" || Array.isArray(next)) throw Error("\u63D2\u4EF6\u540E\u53F0\u8FD4\u56DE\u4E86\u65E0\u6548\u6570\u636E\uFF0C\u8BF7\u5237\u65B0\u9762\u677F\u6216\u91CD\u542F DSH\u3002");
75
82
  if (!res.ok) throw Error(next.error || `\u64CD\u4F5C\u5931\u8D25\uFF08HTTP ${res.status}\uFF09`);
@@ -109,6 +116,7 @@ var styles = `
109
116
  @media(prefers-reduced-motion:reduce){.mr *{transition:none!important}}
110
117
  `;
111
118
  function apply(ctx) {
119
+ reloadFromHost = () => requestPluginReload(ctx.get?.("remote"));
112
120
  ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({ name: "plugins.bundle.config", key: "dsh-multi-router" }, RouterPage));
113
121
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({ name: "shell.overlay", key: "dsh-multi-router-updates" }, UpdateReminder));
114
122
  }
@@ -235,33 +243,43 @@ function RouterPage({ view = "page" }) {
235
243
  const [accountProvider, setAccountProvider] = (0, import_react.useState)(""), [provider, setProvider] = (0, import_react.useState)("workbuddy"), [busy, setBusy] = (0, import_react.useState)(false), [live, setLive] = (0, import_react.useState)(true);
236
244
  const [error, setError] = (0, import_react.useState)(""), [notice, setNotice] = (0, import_react.useState)(""), [draft, setDraft] = (0, import_react.useState)(null), [accountModal, setAccountModal] = (0, import_react.useState)(null), [presetModal, setPresetModal] = (0, import_react.useState)(false), [search, setSearch] = (0, import_react.useState)("");
237
245
  const polling = (0, import_react.useRef)(false);
238
- async function load() {
239
- const next = await readManagementResponse(await fetch(ENDPOINT, { cache: "no-store" }));
246
+ async function load(signal = AbortSignal.timeout(8e3)) {
247
+ const next = await readManagementResponse(await fetch(ENDPOINT, { cache: "no-store", signal }));
240
248
  setData(next);
241
249
  setError("");
242
250
  }
243
251
  (0, import_react.useEffect)(() => {
244
- const accountProviderSelect2 = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "filter", children: [
245
- "\u4F9B\u5E94\u5546 / \u533A\u57DF",
246
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { value: accountProvider, onChange: (e) => setAccountProvider(e.target.value), children: [
247
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "\u5168\u90E8\u4F9B\u5E94\u5546" }),
248
- data?.providers.map((p2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: p2.id, children: p2.name }, p2.id))
249
- ] })
250
- ] });
251
252
  if (view === "summary") return;
252
- void load().catch((e) => setError(e.message));
253
- if (!live) return;
254
- const timer = setInterval(() => {
253
+ let stopped = false, retry, attempts = 0;
254
+ const abort = new AbortController();
255
+ async function connect() {
256
+ try {
257
+ await load(AbortSignal.any([abort.signal, AbortSignal.timeout(4e3)]));
258
+ } catch (e) {
259
+ if (stopped) return;
260
+ if (++attempts < 8) retry = setTimeout(connect, 750);
261
+ else setError(e.message);
262
+ }
263
+ }
264
+ void connect();
265
+ const timer = live && setInterval(() => {
255
266
  if (document.hidden || polling.current) return;
256
267
  polling.current = true;
257
- void load().catch((e) => setError(e.message)).finally(() => {
268
+ void load(abort.signal).catch((e) => {
269
+ if (!stopped) setError(e.message);
270
+ }).finally(() => {
258
271
  polling.current = false;
259
272
  });
260
273
  }, 15e3);
261
- return () => clearInterval(timer);
274
+ return () => {
275
+ stopped = true;
276
+ abort.abort();
277
+ clearTimeout(retry);
278
+ clearInterval(timer);
279
+ };
262
280
  }, [view, live]);
263
281
  (0, import_react.useEffect)(() => {
264
- if (data?.pluginUpdates?.job?.state !== "running" && !data?.checkins?.running) return;
282
+ if (data?.startup?.phase !== "discovering" && !["waiting", "reloading"].includes(data?.runtime?.phase) && data?.pluginUpdates?.job?.state !== "running" && !data?.checkins?.running) return;
265
283
  const timer = setInterval(() => {
266
284
  if (polling.current) return;
267
285
  polling.current = true;
@@ -270,7 +288,20 @@ function RouterPage({ view = "page" }) {
270
288
  });
271
289
  }, 2e3);
272
290
  return () => clearInterval(timer);
273
- }, [data?.pluginUpdates?.job?.state, data?.checkins?.running]);
291
+ }, [data?.startup?.phase, data?.runtime?.phase, data?.pluginUpdates?.job?.state, data?.checkins?.running]);
292
+ async function reload() {
293
+ setBusy(true);
294
+ setError("");
295
+ try {
296
+ if (!reloadFromHost) throw Error("\u8BF7\u5728 DSH \u63D2\u4EF6\u9875\u91CD\u65B0\u542F\u7528\u672C\u63D2\u4EF6\u3002");
297
+ await reloadFromHost();
298
+ await load();
299
+ } catch (e) {
300
+ setError(e.message);
301
+ } finally {
302
+ setBusy(false);
303
+ }
304
+ }
274
305
  async function act(action) {
275
306
  if (busy || backendNeedsRestart(data)) return false;
276
307
  setBusy(true);
@@ -279,9 +310,9 @@ function RouterPage({ view = "page" }) {
279
310
  try {
280
311
  const res = await fetch(ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json", "X-Multi-Router-Key": data.key }, body: JSON.stringify({ revision: data.revision, ...action }) });
281
312
  const next = await readManagementResponse(res);
282
- const { exported, ...state } = next;
313
+ const { exported, workbuddyLogin, loginResult, ...state } = next;
283
314
  setData(state);
284
- setNotice(action.action === "checkins.settings" ? "\u7B7E\u5230\u8BA1\u5212\u5DF2\u4FDD\u5B58\uFF1B\u4ECA\u65E5\u5DF2\u5904\u7406\u8BB0\u5F55\u4FDD\u6301\u4E0D\u53D8\u3002" : action.action.startsWith("checkins.") || action.action.startsWith("plugins.") || action.action === "setup.dismiss" ? "" : action.action === "accounts.discover" || action.action === "monitor.refresh" ? "\u5DF2\u91CD\u65B0\u68C0\u6D4B\u672C\u673A\u767B\u5F55\u5E76\u5237\u65B0\u7528\u91CF\uFF1B\u68C0\u6D4B\u7ED3\u679C\u89C1\u4E0B\u65B9\u3002" : action.action.includes("refresh") ? "\u5DF2\u5237\u65B0\u3002\u65E0\u6CD5\u83B7\u53D6\u7684\u8D26\u53F7\u4F1A\u4FDD\u7559\u4E0A\u6B21\u6570\u636E\u5E76\u6807\u8BB0\u3002" : action.action === "config.import" ? "\u914D\u7F6E\u5DF2\u5BFC\u5165\u5E76\u751F\u6548\uFF0C\u539F\u914D\u7F6E\u5DF2\u5907\u4EFD\uFF1B\u6A21\u578B\u548C\u7528\u91CF\u6B63\u5728\u540E\u53F0\u66F4\u65B0\u3002" : action.action.startsWith("config.") || action.action === "account.export" ? "" : "\u5DF2\u4FDD\u5B58\u5E76\u751F\u6548\u3002");
315
+ setNotice(action.action === "workbuddy.sms" ? "" : action.action === "workbuddy.login" ? loginResult?.warning || "\u767B\u5F55\u51ED\u8BC1\u5DF2\u4FDD\u5B58\uFF0C\u81EA\u52A8\u7B7E\u5230\u5F00\u5173\u4FDD\u6301\u539F\u8BBE\u7F6E\u3002" : action.action === "checkins.verify" ? "\u767B\u5F55\u9A8C\u8BC1\u901A\u8FC7\uFF0C\u65E7\u7684\u4EE4\u724C\u5931\u6548\u63D0\u793A\u5DF2\u89E3\u9664\uFF1B\u672A\u63D0\u4EA4\u7B7E\u5230\u3002" : action.action === "checkins.settings" ? "\u7B7E\u5230\u8BA1\u5212\u5DF2\u4FDD\u5B58\uFF1B\u4ECA\u65E5\u5DF2\u5904\u7406\u8BB0\u5F55\u4FDD\u6301\u4E0D\u53D8\u3002" : action.action.startsWith("checkins.") || action.action.startsWith("plugins.") || action.action === "setup.dismiss" ? "" : action.action === "accounts.discover" || action.action === "monitor.refresh" ? "\u5DF2\u91CD\u65B0\u68C0\u6D4B\u672C\u673A\u767B\u5F55\u5E76\u5237\u65B0\u7528\u91CF\uFF1B\u68C0\u6D4B\u7ED3\u679C\u89C1\u4E0B\u65B9\u3002" : action.action.includes("refresh") ? "\u5DF2\u5237\u65B0\u3002\u65E0\u6CD5\u83B7\u53D6\u7684\u8D26\u53F7\u4F1A\u4FDD\u7559\u4E0A\u6B21\u6570\u636E\u5E76\u6807\u8BB0\u3002" : action.action === "config.import" ? "\u914D\u7F6E\u5DF2\u5BFC\u5165\u5E76\u751F\u6548\uFF0C\u539F\u914D\u7F6E\u5DF2\u5907\u4EFD\uFF1B\u6A21\u578B\u548C\u7528\u91CF\u6B63\u5728\u540E\u53F0\u66F4\u65B0\u3002" : action.action.startsWith("config.") || action.action === "account.export" ? "" : "\u5DF2\u4FDD\u5B58\u5E76\u751F\u6548\u3002");
285
316
  return next;
286
317
  } catch (e) {
287
318
  setError(e.message);
@@ -325,9 +356,13 @@ function RouterPage({ view = "page" }) {
325
356
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "muted", children: "\u7BA1\u7406\u4E09\u5BB6\u8D26\u53F7\u3001\u6A21\u578B\u4E0E\u7528\u91CF" })
326
357
  ] }),
327
358
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
359
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "badge", children: [
360
+ "v",
361
+ data?.pluginVersion ?? version
362
+ ] }),
328
363
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "status", children: [
329
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { className: busy ? "warn" : "good", "aria-hidden": "true" }),
330
- busy ? "\u6B63\u5728\u5904\u7406\u2026" : "\u672C\u673A\u8FD0\u884C"
364
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { className: busy || !data ? "warn" : "good", "aria-hidden": "true" }),
365
+ busy ? "\u6B63\u5728\u5904\u7406\u2026" : !data ? "\u6B63\u5728\u8FDE\u63A5\u2026" : "\u672C\u673A\u8FD0\u884C"
331
366
  ] }),
332
367
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => {
333
368
  void load().catch((e) => setError(e.message));
@@ -340,10 +375,30 @@ function RouterPage({ view = "page" }) {
340
375
  setNotice("");
341
376
  }, children: label }, key)) }),
342
377
  !draft && !accountModal && !presetModal && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Feedback, { error, notice }),
343
- (backendNeedsRestart(data) || !data && error) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RestartGuidance, { data, retry: () => {
378
+ (backendNeedsRestart(data) || !data && error) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RestartGuidance, { data, busy, reload: () => {
379
+ void reload();
380
+ }, retry: () => {
344
381
  void load().catch((e) => setError(e.message));
345
382
  } }),
346
- !data ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", children: error ? "\u63D2\u4EF6\u540E\u53F0\u6682\u4E0D\u53EF\u7528\u3002\u4FEE\u590D\u542F\u7528\u9519\u8BEF\u540E\u70B9\u51FB\u300C\u5237\u65B0\u9762\u677F\u300D\u3002" : "\u6B63\u5728\u8BFB\u53D6\u8D26\u53F7\u4E0E\u7528\u91CF\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("fieldset", { className: "stack", style: { border: 0, padding: 0, margin: 0, minWidth: 0 }, disabled: backendNeedsRestart(data), "aria-busy": busy, children: [
383
+ !data ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box stack", role: "status", "aria-live": "polite", children: [
384
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: error ? "\u7B49\u5F85\u540E\u53F0\u8FDE\u63A5" : "\u6B63\u5728\u8FDE\u63A5\u672C\u673A\u63D2\u4EF6\u2026" }),
385
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u9875\u9762\u4F1A\u81EA\u52A8\u91CD\u65B0\u8FDE\u63A5\u3002\u8D26\u53F7\u53D1\u73B0\u3001\u7528\u91CF\u548C\u6A21\u578B\u76EE\u5F55\u5C06\u5728\u540E\u53F0\u66F4\u65B0\u3002" }),
386
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("progress", { "aria-label": "\u6B63\u5728\u8FDE\u63A5\u63D2\u4EF6\u540E\u53F0" })
387
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("fieldset", { className: "stack", style: { border: 0, padding: 0, margin: 0, minWidth: 0 }, disabled: backendNeedsRestart(data), "aria-busy": busy, children: [
388
+ data.startup?.phase === "discovering" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "steps", role: "status", children: "\u6B63\u5728\u540E\u53F0\u83B7\u53D6\u672C\u673A\u8D26\u53F7\uFF0C\u5DF2\u6709\u8D26\u53F7\u548C\u8DEF\u7531\u73B0\u5728\u5373\u53EF\u4F7F\u7528\u3002" }),
389
+ data.runtime?.phase && data.runtime.phase !== "current" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "steps", children: [
390
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
391
+ data.runtime.message,
392
+ "\uFF08\u8FD0\u884C v",
393
+ data.runtime.runningVersion,
394
+ " / \u5DF2\u5B89\u88C5 v",
395
+ data.runtime.installedVersion,
396
+ "\uFF09"
397
+ ] }),
398
+ ["failed", "restart-required"].includes(data.runtime.phase) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => {
399
+ void reload();
400
+ }, children: "\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6" })
401
+ ] }),
347
402
  ["overview", "accounts"].includes(tab) && data.setup?.dismissed !== true && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InitialSetup, { data, busy, act, go: (key) => setTab(key) }),
348
403
  tab === "overview" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Overview, { data, live, setLive, busy, act, go: (key) => setTab(key) }),
349
404
  tab === "plugins" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PluginUpdatesPanel, { data: data.pluginUpdates, busy, act }),
@@ -783,7 +838,9 @@ function CheckinPanel({ data, busy, act }) {
783
838
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "muted", children: [r.last.activity.streakDays !== void 0 && `\u8FDE\u7B7E ${fmt(r.last.activity.streakDays)} \u5929`, r.last.activity.totalCredits !== void 0 && `\u6D3B\u52A8\u7D2F\u8BA1 ${fmt(r.last.activity.totalCredits)} \u79EF\u5206`, r.last.activity.nextStreakDay !== void 0 && `\u4E0B\u4E00\u8FDE\u7B7E\u6863\u4F4D ${fmt(r.last.activity.nextStreakDay)} \u5929`, r.last.activity.endsAt !== void 0 && `\u6D3B\u52A8\u622A\u6B62 ${when(r.last.activity.endsAt)}`].filter(Boolean).join(" \xB7 ") })
784
839
  ] })
785
840
  ] }),
786
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "cell-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: blocked || !ready(r), onClick: () => {
841
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "cell-actions", children: p.id === "workbuddy" && r.last?.status === "auth-invalid" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: blocked, onClick: () => {
842
+ void act({ action: "checkins.verify", id: a.id });
843
+ }, children: "\u91CD\u65B0\u9A8C\u8BC1\u767B\u5F55" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: blocked || !ready(r), onClick: () => {
787
844
  void act({ action: "checkins.run", ids: [a.id] });
788
845
  }, children: r.last?.status === "auth-invalid" ? "\u8BF7\u66F4\u65B0\u51ED\u8BC1" : last?.retryReady ? "\u8865\u67E5\u7B7E\u5230" : last?.retryAt ? "\u7B49\u5F85\u665A\u95F4\u8865\u67E5" : last ? "\u4ECA\u65E5\u5DF2\u5904\u7406" : "\u7ACB\u5373\u7B7E\u5230" }) })
789
846
  ] }, a.id);
@@ -1324,6 +1381,13 @@ function CredentialForm({ modal, provider, data, busy, act, close, error, notice
1324
1381
  const replacing = modal.mode === "credentials";
1325
1382
  const [selected, setSelected] = (0, import_react.useState)(modal.account?.provider || provider), [mode, setMode] = (0, import_react.useState)("client"), [localError, setLocalError] = (0, import_react.useState)(""), [json, setJson] = (0, import_react.useState)("");
1326
1383
  const [edition, setEdition] = (0, import_react.useState)(modal.account?.login?.edition || "auto");
1384
+ const [phone, setPhone] = (0, import_react.useState)(""), [smsCode, setSmsCode] = (0, import_react.useState)(""), [sms, setSms] = (0, import_react.useState)(null), [verify, setVerify] = (0, import_react.useState)(true), [clock, setClock] = (0, import_react.useState)(Date.now());
1385
+ (0, import_react.useEffect)(() => {
1386
+ if (mode !== "sms") return;
1387
+ const timer = setInterval(() => setClock(Date.now()), 1e3);
1388
+ return () => clearInterval(timer);
1389
+ }, [mode]);
1390
+ const resend = Math.max(0, Math.ceil(((sms?.resendAt || 0) - clock) / 1e3));
1327
1391
  const isTrae = selected.startsWith("trae"), isQoder = selected.startsWith("qoder"), global = selected === "trae-global" || selected === "workbuddy-ai" || selected === "qoder-global";
1328
1392
  const field = (label, name2, type = "text", required = true, value) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1329
1393
  label,
@@ -1345,6 +1409,16 @@ function CredentialForm({ modal, provider, data, busy, act, close, error, notice
1345
1409
  e.preventDefault();
1346
1410
  setLocalError("");
1347
1411
  const f = Object.fromEntries(new FormData(e.currentTarget));
1412
+ if (mode === "sms") {
1413
+ const result = await act({ action: "workbuddy.login", revision: data.revision, challenge: sms?.challenge, code: smsCode, verify, id: replacing ? modal.account.id : void 0, name: f.name || void 0 });
1414
+ if (result) {
1415
+ setPhone("");
1416
+ setSmsCode("");
1417
+ setSms(null);
1418
+ close(selected);
1419
+ }
1420
+ return;
1421
+ }
1348
1422
  let document2, identityDocument;
1349
1423
  try {
1350
1424
  if (mode === "json") {
@@ -1392,8 +1466,12 @@ function CredentialForm({ modal, provider, data, busy, act, close, error, notice
1392
1466
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { disabled: busy || replacing, value: selected, onChange: (e) => {
1393
1467
  setSelected(e.target.value);
1394
1468
  setEdition("auto");
1469
+ setMode("client");
1395
1470
  setJson("");
1396
1471
  setLocalError("");
1472
+ setSms(null);
1473
+ setSmsCode("");
1474
+ setPhone("");
1397
1475
  }, children: data.providers.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: p.id, children: p.name }, p.id)) })
1398
1476
  ] }),
1399
1477
  field("\u8D26\u53F7\u540D\u79F0\uFF08\u53EF\u9009\uFF09", "name", "text", false, modal.account?.name),
@@ -1411,6 +1489,7 @@ function CredentialForm({ modal, provider, data, busy, act, close, error, notice
1411
1489
  setLocalError("");
1412
1490
  }, children: [
1413
1491
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "client", children: "\u4E00\u952E\u8BFB\u53D6\u672C\u673A\u767B\u5F55" }),
1492
+ selected === "workbuddy" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "sms", children: "\u77ED\u4FE1\u767B\u5F55\u5E76\u4FDD\u5B58\u51ED\u8BC1" }),
1414
1493
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", { value: "manual", children: [
1415
1494
  "\u624B\u52A8\u586B\u5199",
1416
1495
  isQoder ? " PAT" : "\u51ED\u8BC1"
@@ -1418,7 +1497,38 @@ function CredentialForm({ modal, provider, data, busy, act, close, error, notice
1418
1497
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "json", children: "\u7C98\u8D34 / \u9009\u62E9\u51ED\u8BC1 JSON" })
1419
1498
  ] })
1420
1499
  ] }),
1421
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "steps stack", children: [
1500
+ mode === "sms" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "steps stack", children: [
1501
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "WorkBuddy \u624B\u673A\u53F7\u767B\u5F55" }),
1502
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u4EC5\u7528\u4E8E\u5DF2\u6709\u4E2D\u56FD\u7248\u8D26\u53F7\u3002\u624B\u673A\u53F7\u548C\u9A8C\u8BC1\u7801\u63D0\u4EA4\u81F3 WorkBuddy \u5B98\u65B9\uFF0C\u51ED\u8BC1\u4EC5\u4FDD\u5B58\u5728\u672C\u673A\uFF0C\u65E0\u9700\u5B89\u88C5 Python\u3002" }),
1503
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1504
+ "\u624B\u673A\u53F7",
1505
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "tel", inputMode: "tel", autoComplete: "tel", required: true, pattern: "1[3-9][0-9]{9}", maxLength: "11", value: phone, disabled: busy || resend > 0, onChange: (e) => {
1506
+ setPhone(e.target.value);
1507
+ setSms(null);
1508
+ setSmsCode("");
1509
+ } })
1510
+ ] }),
1511
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
1512
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: busy || resend > 0 || !/^1[3-9]\d{9}$/.test(phone), onClick: async () => {
1513
+ const next = await act({ action: "workbuddy.sms", phone });
1514
+ if (next) {
1515
+ setSms(next.workbuddyLogin);
1516
+ setClock(Date.now());
1517
+ setSmsCode("");
1518
+ }
1519
+ }, children: resend ? `${resend} \u79D2\u540E\u53EF\u91CD\u53D1` : sms ? "\u91CD\u65B0\u53D1\u9001\u9A8C\u8BC1\u7801" : "\u53D1\u9001\u9A8C\u8BC1\u7801" }),
1520
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "notice", role: "status", children: sms ? clock >= sms.expiresAt ? "\u9A8C\u8BC1\u7801\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u83B7\u53D6" : "\u9A8C\u8BC1\u7801\u5DF2\u53D1\u9001\uFF0C\u8BF7\u67E5\u770B\u77ED\u4FE1" : "\u70B9\u51FB\u53D1\u9001\u624D\u4F1A\u8BF7\u6C42\u77ED\u4FE1\uFF0C\u4E0D\u81EA\u52A8\u91CD\u8BD5" })
1521
+ ] }),
1522
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1523
+ "\u77ED\u4FE1\u9A8C\u8BC1\u7801",
1524
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "text", inputMode: "numeric", autoComplete: "one-time-code", required: true, pattern: "[0-9]{4,8}", maxLength: "8", value: smsCode, disabled: busy || !sms, onChange: (e) => setSmsCode(e.target.value) })
1525
+ ] }),
1526
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check", children: [
1527
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: verify, disabled: busy, onChange: (e) => setVerify(e.target.checked) }),
1528
+ "\u767B\u5F55\u540E\u9A8C\u8BC1\u5237\u65B0\u4EE4\u724C\uFF08\u63A8\u8350\uFF09"
1529
+ ] }),
1530
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u540C\u4E00\u8D26\u53F7\u66F4\u65B0\u51ED\u8BC1\uFF0C\u65B0\u8D26\u53F7\u72EC\u7ACB\u4FDD\u5B58\uFF1B\u4E0D\u4F1A\u5F00\u542F\u81EA\u52A8\u7B7E\u5230\u3002\u9047\u5E73\u53F0\u9A8C\u8BC1\u6216\u9650\u5236\uFF0C\u8BF7\u6539\u7528\u672C\u673A\u5BA2\u6237\u7AEF\u8BFB\u53D6\u3002" })
1531
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "steps stack", children: [
1422
1532
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u83B7\u53D6\u51ED\u8BC1\u5F15\u5BFC" }),
1423
1533
  isQoder ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "\u5728\u5BF9\u5E94\u56FD\u5185 / \u56FD\u9645\u7AD9\u7684\u8D26\u53F7\u8BBE\u7F6E\u4E2D\u521B\u5EFA PAT\uFF0C\u7136\u540E\u9009\u62E9\u300C\u624B\u52A8\u586B\u5199 PAT\u300D\u7C98\u8D34\u3002\u5DF2\u6709\u65E7\u8FDE\u63A5\u5668\u767B\u5F55\u65F6\uFF0C\u53EF\u4E00\u952E\u8BFB\u53D6\u672C\u673A\u4FDD\u5B58\u7684 PAT\u3002\u7F51\u9875\u767B\u5F55 Cookie \u4E0D\u4F5C\u4E3A PAT \u4F7F\u7528\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
1424
1534
  "1. \u5728\u6240\u9009\u533A\u57DF\u7684\u5B98\u65B9\u5BA2\u6237\u7AEF\u767B\u5F55\u8D26\u53F7\u3002",
@@ -1481,14 +1591,14 @@ function CredentialForm({ modal, provider, data, busy, act, close, error, notice
1481
1591
  ] })
1482
1592
  ] })
1483
1593
  ] }, `${selected}:${mode}`),
1484
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
1594
+ mode !== "sms" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
1485
1595
  "\u51ED\u8BC1\u4EC5\u4FDD\u5B58\u5230\u672C\u673A\u63D2\u4EF6\u8D26\u53F7\u6C60\u3002\u4E0D\u4F1A\u5728\u8D26\u53F7\u5217\u8868\u663E\u793A Token\uFF1B\u6BCF\u4E2A\u8D26\u53F7\u72EC\u7ACB\u5237\u65B0\u3002",
1486
1596
  replacing ? "\u4FDD\u5B58\u540E\u542F\u7528\u6B64\u8D26\u53F7\uFF0C\u539F\u51ED\u8BC1\u526F\u672C\u4FDD\u7559\u3002" : "\u91CD\u590D\u8BFB\u53D6\u540C\u4E00\u8D26\u53F7\u4F1A\u63D0\u793A\u3002"
1487
1597
  ] })
1488
1598
  ] }),
1489
1599
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "modal-foot bar", children: [
1490
1600
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: busy, onClick: close, children: "\u53D6\u6D88" }),
1491
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "primary", type: "submit", disabled: busy, children: busy ? "\u6B63\u5728\u5904\u7406\u2026" : mode === "client" ? "\u4E00\u952E\u83B7\u53D6\u5E76\u4FDD\u5B58\u767B\u5F55" : "\u4FDD\u5B58\u51ED\u8BC1\u5E76\u83B7\u53D6\u6A21\u578B" })
1601
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "primary", type: "submit", disabled: busy || mode === "sms" && (!sms || clock >= sms.expiresAt), children: busy ? "\u6B63\u5728\u5904\u7406\u2026" : mode === "sms" ? "\u767B\u5F55\u5E76\u4FDD\u5B58\u5230\u8D26\u53F7\u6C60" : mode === "client" ? "\u4E00\u952E\u83B7\u53D6\u5E76\u4FDD\u5B58\u767B\u5F55" : "\u4FDD\u5B58\u51ED\u8BC1\u5E76\u83B7\u53D6\u6A21\u578B" })
1492
1602
  ] })
1493
1603
  ] });
1494
1604
  }
@@ -1872,15 +1982,16 @@ function RouteForm({ draft, setDraft, data, act, busy, error, notice }) {
1872
1982
  }
1873
1983
  function InitialSetup({ data, busy, act, go }) {
1874
1984
  const discovery = data.localDiscovery ?? [];
1985
+ const discovering = data.startup?.phase === "discovering";
1875
1986
  const saved = discovery.reduce((n, s) => n + (s.savedCount ?? 0), 0);
1876
1987
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box stack", "aria-label": "\u9996\u6B21\u8D26\u53F7\u8BBE\u7F6E", children: [
1877
1988
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
1878
1989
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1879
1990
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", { children: [
1880
1991
  "\u672C\u673A\u8D26\u53F7\u81EA\u52A8\u83B7\u53D6",
1881
- discovery.length ? "\u5DF2\u5B8C\u6210" : "\u5F85\u91CD\u8BD5"
1992
+ discovering ? "\u8FDB\u884C\u4E2D" : discovery.length ? "\u5DF2\u5B8C\u6210" : "\u5F85\u91CD\u8BD5"
1882
1993
  ] }),
1883
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: saved ? `\u5DF2\u4FDD\u5B58 ${saved} \u4E2A WorkBuddy / Trae \u672C\u673A\u767B\u5F55\uFF0C\u65E0\u9700\u518D\u6B21\u624B\u52A8\u6DFB\u52A0\u3002` : "\u6682\u672A\u4FDD\u5B58\u672C\u673A\u767B\u5F55\u3002\u5148\u5728\u5B98\u65B9\u5BA2\u6237\u7AEF\u767B\u5F55\uFF0C\u518D\u70B9\u51FB\u300C\u4E00\u952E\u83B7\u53D6\u5E76\u4FDD\u5B58\u300D\u3002" })
1994
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: discovering ? "\u6B63\u5728\u540E\u53F0\u8BFB\u53D6 WorkBuddy / Trae \u672C\u673A\u767B\u5F55\uFF0C\u65E0\u9700\u624B\u52A8\u91CD\u590D\u83B7\u53D6\uFF0C\u5DF2\u6709\u8D26\u53F7\u53EF\u4EE5\u7EE7\u7EED\u4F7F\u7528\u3002" : saved ? `\u5DF2\u4FDD\u5B58 ${saved} \u4E2A WorkBuddy / Trae \u672C\u673A\u767B\u5F55\uFF0C\u65E0\u9700\u518D\u6B21\u624B\u52A8\u6DFB\u52A0\u3002` : "\u6682\u672A\u4FDD\u5B58\u672C\u673A\u767B\u5F55\u3002\u5148\u5728\u5B98\u65B9\u5BA2\u6237\u7AEF\u767B\u5F55\uFF0C\u518D\u70B9\u51FB\u300C\u4E00\u952E\u83B7\u53D6\u5E76\u4FDD\u5B58\u300D\u3002" })
1884
1995
  ] }),
1885
1996
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => {
1886
1997
  void act({ action: "setup.dismiss" });
@@ -1891,7 +2002,7 @@ function InitialSetup({ data, busy, act, go }) {
1891
2002
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: (s.savedCount ?? 0) > 0 ? "good" : "notice", children: s.savedCount ? `\u5DF2\u4FDD\u5B58 ${s.savedCount} \u4E2A\u767B\u5F55` : s.state === "found" ? "\u68C0\u6D4B\u5230\u767B\u5F55\uFF1B\u5DF2\u5FFD\u7565\u6216\u5C1A\u672A\u4FDD\u5B58\uFF0C\u8BF7\u91CD\u65B0\u83B7\u53D6" : s.state === "unreadable" ? "\u767B\u5F55\u6587\u4EF6\u65E0\u6CD5\u8BFB\u53D6\uFF0C\u8BF7\u5728\u5BA2\u6237\u7AEF\u91CD\u65B0\u767B\u5F55\u540E\u91CD\u8BD5" : "\u672A\u68C0\u6D4B\u5230\u767B\u5F55\uFF0C\u8BF7\u5148\u767B\u5F55\u5B98\u65B9\u5BA2\u6237\u7AEF" })
1892
2003
  ] }, s.provider)) }),
1893
2004
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
1894
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "primary", disabled: busy, onClick: () => {
2005
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "primary", disabled: busy || discovering, onClick: () => {
1895
2006
  void act({ action: "accounts.discover" });
1896
2007
  }, children: "\u4E00\u952E\u83B7\u53D6\u5E76\u4FDD\u5B58 WorkBuddy / Trae" }),
1897
2008
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => go("accounts"), children: "\u67E5\u770B\u8D26\u53F7 / \u624B\u52A8\u6DFB\u52A0" }),