deskbuddy 0.2.0-beta.35 → 0.2.0-beta.36

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/dist/cli.js CHANGED
@@ -2890,13 +2890,40 @@ function singleCursor(lines3, q) {
2890
2890
  if (m) {
2891
2891
  const k = Number(m[1]) - 1;
2892
2892
  if (k >= 0 && k < n) return squash(m[2]).startsWith(squash(q.options[k].label).slice(0, 6)) ? k : -1;
2893
- if (k === n) return /^Typesomething/.test(squash(m[2])) ? n : -1;
2893
+ if (k === n) return /^(Typesomething)?$/.test(squash(m[2])) ? n : -1;
2894
2894
  return -1;
2895
2895
  }
2896
2896
  if (new RegExp(`^\\s*${CURSOR}\\s*(Next|Submit)\\s*$`).test(l)) return n + 1;
2897
2897
  }
2898
2898
  return -1;
2899
2899
  }
2900
+ function typeLine(lines3, n) {
2901
+ const start = questionStart(lines3);
2902
+ if (start < 0) return -1;
2903
+ for (let i = start; i < lines3.length; i++) {
2904
+ const m = lines3[i].match(/^\s*(?:[❯>›]\s*)?[☐☒]?\s*(\d+)\.(?:\s|$)/);
2905
+ if (m && Number(m[1]) === n + 1) return i;
2906
+ }
2907
+ return -1;
2908
+ }
2909
+ function typedText(lines3, n) {
2910
+ const ln = typeLine(lines3, n);
2911
+ if (ln < 0) return null;
2912
+ const parts = [lines3[ln].replace(/^\s*(?:[❯>›]\s*)?[☐☒]?\s*\d+\./, "")];
2913
+ for (let i = ln + 1; i < lines3.length; i++) {
2914
+ if (new RegExp(`^\\s*(?:${CURSOR}\\s*)?(Submit|Next)\\s*$`).test(lines3[i]) || /^\s*(?:[❯>›]\s*)?[☐☒]?\s*\d+\./.test(lines3[i])) break;
2915
+ parts.push(lines3[i]);
2916
+ }
2917
+ return squash(parts.join(""));
2918
+ }
2919
+ async function typeOther(io, q, text) {
2920
+ const n = q.options.length;
2921
+ const want = squash(text);
2922
+ if (!want) return "no_focus";
2923
+ if (!await waitUntil(io, (l) => onQuestion(l, q) && typedText(l, n) === "")) return "no_focus";
2924
+ io.write(text);
2925
+ return await waitUntil(io, (l) => typedText(l, n) === want) ? "typed" : "mismatch";
2926
+ }
2900
2927
  async function move(io, delta) {
2901
2928
  const key = delta > 0 ? KEY_DOWN : KEY_UP;
2902
2929
  for (let i = 0; i < Math.abs(delta); i++) {
@@ -2904,12 +2931,16 @@ async function move(io, delta) {
2904
2931
  await io.sleep(ARROW_GAP_MS);
2905
2932
  }
2906
2933
  }
2907
- async function answerQuestions(io, questions, selections) {
2934
+ async function answerQuestions(io, questions, selections, others) {
2908
2935
  if (questions.length === 0 || selections.length !== questions.length) return "not_waiting";
2936
+ if (others !== void 0 && others.length !== questions.length) return "not_waiting";
2909
2937
  for (let qi = 0; qi < questions.length; qi++) {
2910
2938
  const sel = selections[qi];
2911
2939
  const q = questions[qi];
2912
- if (sel.length === 0 || sel.some((i) => !Number.isInteger(i) || i < 0 || i >= q.options.length)) return "not_waiting";
2940
+ const n = q.options.length;
2941
+ const other = others?.[qi] ?? "";
2942
+ if (sel.length === 0 || sel.some((i) => !Number.isInteger(i) || i < 0 || i > n)) return "not_waiting";
2943
+ if (sel.includes(n) !== (squash(other) !== "")) return "not_waiting";
2913
2944
  if (q.multiSelect !== true && sel.length !== 1) return "not_waiting";
2914
2945
  }
2915
2946
  if (!await settled(io, (l) => onQuestion(l, questions[0]))) return "not_waiting";
@@ -2930,6 +2961,14 @@ async function answerQuestions(io, questions, selections) {
2930
2961
  for (const t of sel) {
2931
2962
  await move(io, t - cur);
2932
2963
  cur = t;
2964
+ if (t === n) {
2965
+ const ty = await typeOther(io, q, others?.[qi] ?? "");
2966
+ if (ty !== "no_focus") wrote = true;
2967
+ if (ty !== "typed") return fail();
2968
+ io.write(KEY_ENTER);
2969
+ if (!await waitUntil(io, (l) => checked(l, typeLine(l, n)))) return fail();
2970
+ continue;
2971
+ }
2933
2972
  const onTarget = await waitUntil(io, (l) => {
2934
2973
  const ln = optionLine(l, t + 1, q.options[t].label);
2935
2974
  return ln >= 0 && io.cursorLine?.() === ln;
@@ -2948,7 +2987,11 @@ async function answerQuestions(io, questions, selections) {
2948
2987
  const cur = singleCursor(io.screen(), q);
2949
2988
  if (cur < 0) return fail();
2950
2989
  await move(io, t - cur);
2951
- if (!await waitUntil(io, (l) => singleCursor(l, q) === t && optionLine(l, t + 1, q.options[t].label) >= 0)) return fail();
2990
+ if (t === n) {
2991
+ const ty = await typeOther(io, q, others?.[qi] ?? "");
2992
+ if (ty !== "no_focus") wrote = true;
2993
+ if (ty !== "typed") return fail();
2994
+ } else if (!await waitUntil(io, (l) => singleCursor(l, q) === t && optionLine(l, t + 1, q.options[t].label) >= 0)) return fail();
2952
2995
  io.write(KEY_ENTER);
2953
2996
  wrote = true;
2954
2997
  }
@@ -7806,7 +7849,14 @@ ${PRELUDE3}
7806
7849
  }
7807
7850
  }
7808
7851
  if (C && typeof C.currentId === 'string') return { mode: 'dom', locale: localeOf(), why };
7809
- return { mode: 'none', locale: localeOf(), why };
7852
+ // 没有 SDK 时列出页面上像接口的全局名(258 退回:WorkBuddy 5.1.7 没有 wb,日志里据此判断老版本把接口放在哪)
7853
+ let globals;
7854
+ if (why === 'no_wb') {
7855
+ try {
7856
+ globals = Object.getOwnPropertyNames(globalThis).filter((k) => /wb|work|buddy|genie|sdk|electron|bridge|ipc|conv|agent|app/i.test(k) && !/^on/.test(k) && k.length <= 40).sort().slice(0, 30);
7857
+ } catch (e) { globals = undefined; }
7858
+ }
7859
+ return { mode: 'none', locale: localeOf(), why, ...(globals ? { globals } : {}) };
7810
7860
  })()`;
7811
7861
  var IDLE_STATES = "['idle','completed','cancelled','canceled','done','failed','error','terminated','archived','deleted','']";
7812
7862
  var SNAPSHOT2 = String.raw`(async () => {
@@ -8249,7 +8299,10 @@ var WorkbuddyAppWatch = class {
8249
8299
  this.#nonSdkSince = first && kept && now - kept.at < NON_SDK_KEEP_MS ? kept.since : now;
8250
8300
  this.#nonSdkKept = null;
8251
8301
  }
8252
- if (first || prev !== this.#mode || prevWhy !== this.#modeWhy) this.#d.log(`WorkBuddy App \u6388\u6743\u6865\u6A21\u5F0F\uFF1A${this.#mode}${this.#modeWhy ? `\uFF08${modeWhyText(this.#modeWhy)}\uFF09` : ""}`);
8302
+ if (first || prev !== this.#mode || prevWhy !== this.#modeWhy) {
8303
+ const g = Array.isArray(r?.globals) ? r.globals.filter((x) => typeof x === "string").map((x) => x.slice(0, 40)).slice(0, 30) : null;
8304
+ this.#d.log(`WorkBuddy App \u6388\u6743\u6865\u6A21\u5F0F\uFF1A${this.#mode}${this.#modeWhy ? `\uFF08${modeWhyText(this.#modeWhy)}\uFF09` : ""}${g ? `\uFF1B\u9875\u9762\u5168\u5C40\u540D\uFF1A${g.join(",") || "\u65E0"}` : ""}`);
8305
+ }
8253
8306
  const cands = this.#client?.target?.candidates ?? 1;
8254
8307
  if (this.#mode !== "sdk" && this.#modeWhy === "no_wb" && cands > 1 && this.#nonSdkSince && now - this.#nonSdkSince >= NO_SDK_REPROBE_MS && now - this.#reprobeAt >= NO_SDK_REPROBE_MS) {
8255
8308
  this.#reprobeAt = now;
@@ -8518,7 +8571,7 @@ function modeWhyText(why) {
8518
8571
  const msg = rest.join(":");
8519
8572
  switch (k) {
8520
8573
  case "no_wb":
8521
- return "\u9875\u9762\u91CC\u6CA1\u6709 WorkBuddy SDK\uFF08globalThis.wb\uFF09";
8574
+ return "\u9875\u9762\u91CC\u6CA1\u6709 WorkBuddy SDK\uFF08globalThis.wb\uFF09\uFF0CWorkBuddy \u7248\u672C\u53EF\u80FD\u592A\u65E7\uFF0C\u5347\u7EA7\u5230\u6700\u65B0\u7248\u540E\u4EFB\u52A1\u624D\u80FD\u76F4\u63A5\u8FDB App";
8522
8575
  case "no_conversations":
8523
8576
  return "\u9875\u9762\u91CC\u6CA1\u6709\u4F1A\u8BDD\u63A5\u53E3\uFF08wb.conversations\uFF09";
8524
8577
  case "no_list_get":
@@ -10455,14 +10508,28 @@ function parseAnswer(m) {
10455
10508
  const questions = parseQuestions(a.questions);
10456
10509
  const sel = a.selections;
10457
10510
  if (!questions || sel.length !== questions.length) return null;
10511
+ let others;
10512
+ if (a.others !== void 0) {
10513
+ if (!Array.isArray(a.others) || a.others.length !== questions.length) return null;
10514
+ others = [];
10515
+ for (const o of a.others) {
10516
+ if (typeof o !== "string" || o.length > OTHER_MAX_CHARS || /[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u202a-\u202e\u2066-\u2069\ufeff]/.test(o)) return null;
10517
+ others.push(o);
10518
+ }
10519
+ }
10458
10520
  const selections = [];
10459
10521
  for (let i = 0; i < questions.length; i++) {
10460
10522
  const s2 = sel[i];
10461
- if (!Array.isArray(s2) || s2.length === 0 || !s2.every((x) => Number.isInteger(x) && x >= 0 && x < questions[i].options.length)) return null;
10523
+ const n = questions[i].options.length;
10524
+ const other = others?.[i] ?? "";
10525
+ if (!Array.isArray(s2) || s2.length === 0 || !s2.every((x) => Number.isInteger(x) && x >= 0 && x <= n)) return null;
10526
+ if (s2.includes(n) !== (other.trim() !== "")) return null;
10527
+ if (new Set(s2).size !== s2.length) return null;
10462
10528
  selections.push(s2);
10463
10529
  }
10464
- return { instructionId, sessionId, expireAt, answer: { kind: "question", questions, selections } };
10530
+ return { instructionId, sessionId, expireAt, answer: { kind: "question", questions, selections, ...others ? { others } : {} } };
10465
10531
  }
10532
+ var OTHER_MAX_CHARS = 500;
10466
10533
  function parseQuestions(v) {
10467
10534
  if (!Array.isArray(v)) return null;
10468
10535
  const qs = v;
@@ -10828,20 +10895,31 @@ function pendingCall(text, toolName) {
10828
10895
  function selectionsOf(input, lists) {
10829
10896
  const qs = Array.isArray(input.questions) ? input.questions : null;
10830
10897
  if (!qs || !Array.isArray(lists) || lists.length !== qs.length) return null;
10831
- const out = [];
10898
+ const selections = [];
10899
+ const others = [];
10832
10900
  for (let i = 0; i < qs.length; i++) {
10833
10901
  const opts = Array.isArray(qs[i]?.options) ? qs[i].options : [];
10834
10902
  const items = lists[i];
10835
10903
  if (!Array.isArray(items) || items.length === 0) return null;
10836
10904
  const idx = [];
10837
- for (const label of items) {
10905
+ let other = "";
10906
+ for (let j = 0; j < items.length; j++) {
10907
+ const label = items[j];
10838
10908
  const k = opts.findIndex((o) => o?.label === label);
10839
- if (k < 0) return null;
10840
- idx.push(k);
10909
+ if (k >= 0) {
10910
+ if (!idx.includes(k)) idx.push(k);
10911
+ continue;
10912
+ }
10913
+ if (j !== items.length - 1 || typeof label !== "string") return null;
10914
+ other = label.replace(/[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u202a-\u202e\u2066-\u2069\ufeff]+/g, " ").replace(/\s+/g, " ").trim();
10915
+ if (!other) return null;
10841
10916
  }
10842
- out.push(idx);
10917
+ if (other) idx.push(opts.length);
10918
+ if (qs[i]?.multiSelect !== true && idx.length !== 1) return null;
10919
+ selections.push(idx);
10920
+ others.push(other);
10843
10921
  }
10844
- return out;
10922
+ return { selections, others };
10845
10923
  }
10846
10924
  var WorkbuddyBridge = class {
10847
10925
  #d;
@@ -10979,10 +11057,16 @@ var WorkbuddyBridge = class {
10979
11057
  if (reply.decision !== "allow") return;
10980
11058
  const sel = selectionsOf(card.input, reply.answerLists);
10981
11059
  if (!sel) {
10982
- this.#d.log(`WorkBuddy\uFF1A\u4F1A\u8BDD ${card.sessionId.slice(0, 8)} \u7684\u8BBE\u5907\u7B54\u6848\u5BF9\u4E0D\u4E0A\u9009\u9879\uFF08\u542B\u81EA\u5DF1\u586B\u5199\u7684\u7B54\u6848\uFF09\uFF0C\u6846\u7559\u7ED9\u7535\u8111`);
11060
+ this.#d.log(`WorkBuddy\uFF1A\u4F1A\u8BDD ${card.sessionId.slice(0, 8)} \u7684\u8BBE\u5907\u7B54\u6848\u5BF9\u4E0D\u4E0A\u9009\u9879\uFF0C\u6846\u7559\u7ED9\u7535\u8111`);
10983
11061
  return;
10984
11062
  }
10985
- spec = { kind: "question", questions: card.input.questions, selections: sel };
11063
+ const withOther = sel.others.some((o) => o !== "");
11064
+ spec = {
11065
+ kind: "question",
11066
+ questions: card.input.questions,
11067
+ selections: sel.selections,
11068
+ ...withOther ? { others: sel.others } : {}
11069
+ };
10986
11070
  } else if (reply.decision === "allow" || reply.decision === "deny") {
10987
11071
  spec = { kind: "permission", toolName: card.toolName, fingerprint: card.fingerprint, decision: reply.decision };
10988
11072
  } else return;
@@ -19667,7 +19751,7 @@ async function applyAnswer(req, io, trust) {
19667
19751
  } else if (a.kind === "trust" && a.prompt !== "hooks" && trust?.widens) r = "not_waiting";
19668
19752
  else if (a.kind === "trust") r = await answerTrust(keys, trust?.tool ?? "", a.decision, trust?.forms ?? [], a.prompt ?? "trust");
19669
19753
  else if (a.kind === "permission") r = await answerPermission(keys, a.toolName, a.fingerprint, a.decision);
19670
- else r = await answerQuestions(keys, a.questions, a.selections);
19754
+ else r = await answerQuestions(keys, a.questions, a.selections, a.others);
19671
19755
  } catch {
19672
19756
  r = "partial";
19673
19757
  }
@@ -338,3 +338,27 @@
338
338
  2026-10-12T07:48:57.673Z [opencode] opencode 版本 读不出:带说明的拒绝不走新路由
339
339
  2026-10-12T07:48:57.673Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
340
340
  2026-10-12T07:49:00.208Z [opencode] 后台任务里的提问 que_r2 没法在设备上回答(后台任务不能提问,请在电脑上继续),已拒绝
341
+ 2026-10-12T08:43:33.911Z [opencode] 回填选择题失败:/question/{requestID}/reply status=404
342
+ 2026-10-12T08:43:38.975Z [opencode] opencode 版本 1.4.3:带说明的拒绝不走新路由
343
+ 2026-10-12T08:43:38.975Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
344
+ 2026-10-12T08:43:39.078Z [opencode] opencode 版本 读不出:带说明的拒绝不走新路由
345
+ 2026-10-12T08:43:39.078Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
346
+ 2026-10-12T08:43:41.400Z [opencode] 后台任务里的提问 que_r2 没法在设备上回答(后台任务不能提问,请在电脑上继续),已拒绝
347
+ 2026-10-12T08:45:36.004Z [opencode] 回填选择题失败:/question/{requestID}/reply status=404
348
+ 2026-10-12T08:45:40.986Z [opencode] opencode 版本 1.4.3:带说明的拒绝不走新路由
349
+ 2026-10-12T08:45:40.986Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
350
+ 2026-10-12T08:45:41.084Z [opencode] opencode 版本 读不出:带说明的拒绝不走新路由
351
+ 2026-10-12T08:45:41.084Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
352
+ 2026-10-12T08:45:43.292Z [opencode] 后台任务里的提问 que_r2 没法在设备上回答(后台任务不能提问,请在电脑上继续),已拒绝
353
+ 2026-10-12T08:51:55.878Z [opencode] 回填选择题失败:/question/{requestID}/reply status=404
354
+ 2026-10-12T08:52:01.359Z [opencode] opencode 版本 1.4.3:带说明的拒绝不走新路由
355
+ 2026-10-12T08:52:01.360Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
356
+ 2026-10-12T08:52:01.517Z [opencode] opencode 版本 读不出:带说明的拒绝不走新路由
357
+ 2026-10-12T08:52:01.517Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
358
+ 2026-10-12T08:52:04.661Z [opencode] 后台任务里的提问 que_r2 没法在设备上回答(后台任务不能提问,请在电脑上继续),已拒绝
359
+ 2026-10-12T09:01:07.164Z [opencode] 回填选择题失败:/question/{requestID}/reply status=404
360
+ 2026-10-12T09:01:12.193Z [opencode] opencode 版本 1.4.3:带说明的拒绝不走新路由
361
+ 2026-10-12T09:01:12.194Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
362
+ 2026-10-12T09:01:12.293Z [opencode] opencode 版本 读不出:带说明的拒绝不走新路由
363
+ 2026-10-12T09:01:12.293Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
364
+ 2026-10-12T09:01:14.613Z [opencode] 后台任务里的提问 que_r2 没法在设备上回答(后台任务不能提问,请在电脑上继续),已拒绝
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deskbuddy",
3
- "version": "0.2.0-beta.35",
3
+ "version": "0.2.0-beta.36",
4
4
  "description": "把 Claude Code、Codex 等 AI 编码智能体每个会话窗口的状态,实时同步到桌面硬件屏幕",
5
5
  "keywords": [
6
6
  "ai",