deskbuddy 0.2.0-beta.4 → 0.2.0-beta.5

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.
Files changed (2) hide show
  1. package/dist/cli.js +84 -12
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -3873,9 +3873,17 @@ function tooLongToInject(text) {
3873
3873
  function prepare(text) {
3874
3874
  return " " + oneLine(text);
3875
3875
  }
3876
+ var TRUST_RE = /trust (the files in )?this folder|Yes, I trust this folder/i;
3877
+ var PLACEHOLDER_RE = /^Try ".*"$/;
3878
+ function isTerminalReport(s) {
3879
+ if (!s) return true;
3880
+ const rest = s.replace(/\x1b\[[IO]/g, "").replace(/\x1b\[\d+;\d+R/g, "").replace(/\x1b\[[?>][\d;]*\$?[a-zA-Z]/g, "").replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "").replace(/\x1bP[^\x1b]*\x1b\\/g, "");
3881
+ return rest === "";
3882
+ }
3876
3883
  var PERMISSION_RE = /Do you want to (proceed|make|create|run|allow)|[❯>]\s*1\. Yes/;
3877
- function inspectLines(lines2) {
3884
+ function inspectLines(lines2, undimmed) {
3878
3885
  const text = lines2.join("\n");
3886
+ const trust = TRUST_RE.test(text);
3879
3887
  const permission = PERMISSION_RE.test(text);
3880
3888
  const rules = [];
3881
3889
  lines2.forEach((l, i) => {
@@ -3885,9 +3893,13 @@ function inspectLines(lines2) {
3885
3893
  if (rules.length >= 2) {
3886
3894
  const a = rules[rules.length - 2];
3887
3895
  const b = rules[rules.length - 1];
3888
- input = lines2.slice(a + 1, b).join("\n").replace(/^\s*[❯>]\s?/, "").trimEnd();
3896
+ const src = undimmed && undimmed.length === lines2.length ? undimmed : lines2;
3897
+ input = src.slice(a + 1, b).map((l) => l.trimEnd()).join("\n").replace(/^\s*[❯>]\s?/, "").trimEnd();
3889
3898
  }
3890
- return { permission, input };
3899
+ const v = { permission, input };
3900
+ if (trust) v.trust = true;
3901
+ if (input !== null && PLACEHOLDER_RE.test(input.trim())) v.placeholder = true;
3902
+ return v;
3891
3903
  }
3892
3904
  function boxMatches(view, body) {
3893
3905
  if (view.input === null || view.permission) return false;
@@ -4590,6 +4602,8 @@ var PING_FRAME_SERVICE = "ping";
4590
4602
  var PONG_FRAME = JSON.stringify({ service: "pong", version: "1.0" });
4591
4603
  var CODEX_REMOTE_REFRESH_MS = 30 * 1e3;
4592
4604
  var CLAIM_WAIT_MS = 15 * 1e3;
4605
+ var FIRST_LINE_WAIT_MS = 10 * 60 * 1e3;
4606
+ var FIRST_LINE_CAP_MS = 18 * 60 * 1e3;
4593
4607
  var WINDOW_READY_MS = 30 * 1e3;
4594
4608
  var CODEX_WATCH_MS = 5 * 1e3;
4595
4609
  function defaultCodexAppServers() {
@@ -4973,6 +4987,7 @@ var Daemon = class {
4973
4987
  return;
4974
4988
  }
4975
4989
  this.#seen.save();
4990
+ cmd.receivedAt = this.#deps.now();
4976
4991
  this.#route(cmd);
4977
4992
  }
4978
4993
  /**
@@ -5113,9 +5128,9 @@ var Daemon = class {
5113
5128
  * 返回时:窗口已收下首句 / 已按失败回报 / 降级的后台那次已跑完
5114
5129
  */
5115
5130
  async #openWindowFor(cmd, sid, cwd) {
5116
- const headless = (why) => {
5131
+ const headless = (why, c = cmd) => {
5117
5132
  log(`\u6307\u4EE4 ${cmd.instructionId} ${why}\uFF0C\u6539\u540E\u53F0\u6267\u884C`);
5118
- return this.#headless(cmd, sid, cwd);
5133
+ return this.#headless(c, sid, cwd);
5119
5134
  };
5120
5135
  let gui = false;
5121
5136
  try {
@@ -5130,6 +5145,7 @@ var Daemon = class {
5130
5145
  } catch (e) {
5131
5146
  return headless(`\u5199\u4EA4\u63A5\u5355\u5931\u8D25\uFF1A${e.message}`);
5132
5147
  }
5148
+ const openedAt = this.#deps.now();
5133
5149
  try {
5134
5150
  await this.#deps.openWindow(runId);
5135
5151
  } catch (e) {
@@ -5145,7 +5161,9 @@ var Daemon = class {
5145
5161
  removeCommandFile(deskbuddyHome(), runId);
5146
5162
  if (launchPending(runId) && claimLaunch(runId)) return headless(`${Math.round(this.#deps.claimWaitMs / 1e3)} \u79D2\u5185\u7A97\u53E3\u6CA1\u8D77\u6765`);
5147
5163
  log(`\u6307\u4EE4 ${cmd.instructionId} \u7A97\u53E3\u5DF2\u8D77\u6765\uFF08${runId.slice(0, 8)}\uFF09\uFF0C\u7B49\u5B83\u5C31\u7EEA\u540E\u5199\u5165\u9996\u53E5`);
5148
- const req2 = { instructionId: cmd.instructionId, sessionId: "", text: cmd.text, expireAt: cmd.expireAt };
5164
+ const firstLineExpire = Math.min(Math.max(cmd.expireAt, openedAt + FIRST_LINE_WAIT_MS), (cmd.receivedAt ?? openedAt) + FIRST_LINE_CAP_MS);
5165
+ const wcmd = cmd.expireAt > 0 ? { ...cmd, expireAt: Math.max(cmd.expireAt, firstLineExpire) } : cmd;
5166
+ const req2 = { instructionId: cmd.instructionId, sessionId: "", text: cmd.text, expireAt: wcmd.expireAt };
5149
5167
  const fb = { p: null, final: false };
5150
5168
  const readyDeadline = this.#deps.now() + this.#deps.windowReadyMs;
5151
5169
  while (this.#deps.now() < readyDeadline) {
@@ -5167,7 +5185,7 @@ var Daemon = class {
5167
5185
  if (r === "handled") {
5168
5186
  log(`\u6307\u4EE4 ${cmd.instructionId} \u9996\u53E5\u5DF2\u4EA4\u7ED9\u65B0\u7A97\u53E3 len=${cmd.text.length}`);
5169
5187
  if (sid) {
5170
- const left = cmd.expireAt > 0 ? Math.max(cmd.expireAt - this.#deps.now(), 0) : 10 * 60 * 1e3;
5188
+ const left = wcmd.expireAt > 0 ? Math.max(wcmd.expireAt - this.#deps.now(), 0) : FIRST_LINE_WAIT_MS;
5171
5189
  const cap = this.#deps.now() + left + this.#deps.windowReadyMs;
5172
5190
  while (!fb.final && this.#deps.now() < cap) {
5173
5191
  let found = null;
@@ -6300,6 +6318,31 @@ function screenLines(term) {
6300
6318
  }
6301
6319
  return out;
6302
6320
  }
6321
+ function screenLinesUndimmed(term) {
6322
+ const b = term.buffer.active;
6323
+ const out = [];
6324
+ for (let i = b.viewportY; i < b.viewportY + term.rows; i++) {
6325
+ const l = b.getLine(i);
6326
+ if (!l) {
6327
+ out.push("");
6328
+ continue;
6329
+ }
6330
+ if (typeof l.getCell !== "function") {
6331
+ out.push(l.translateToString(true));
6332
+ continue;
6333
+ }
6334
+ let s = "";
6335
+ for (let x = 0; x < term.cols; x++) {
6336
+ const c = l.getCell(x);
6337
+ if (!c) break;
6338
+ const w = c.getWidth();
6339
+ if (w === 0) continue;
6340
+ s += c.isDim() ? " ".repeat(w) : c.getChars() || " ";
6341
+ }
6342
+ out.push(s.trimEnd());
6343
+ }
6344
+ return out;
6345
+ }
6303
6346
 
6304
6347
  // src/commands/enable.ts
6305
6348
  function stdout(line) {
@@ -6676,6 +6719,8 @@ async function runSetup(opts = {}) {
6676
6719
  bound.remoteCwd = remoteCwd;
6677
6720
  saveConfig(bound);
6678
6721
  out(` \u5DF2\u8BB0\u4E0B\uFF1A${remoteCwd}`);
6722
+ out(" \u63D0\u793A\uFF1AClaude Code \u7B2C\u4E00\u6B21\u5728\u65B0\u76EE\u5F55\u91CC\u542F\u52A8\u4F1A\u95EE\u300C\u662F\u5426\u4FE1\u4EFB\u6B64\u6587\u4EF6\u5939\u300D\uFF0CDeskBuddy \u4E0D\u4F1A\u66FF\u4F60\u56DE\u7B54\u3002");
6723
+ out(" \u5EFA\u8BAE\u5148\u5728\u8FD9\u4E2A\u76EE\u5F55\u91CC\u624B\u52A8\u8FD0\u884C\u4E00\u6B21 claude \u5E76\u9009\u62E9\u4FE1\u4EFB\uFF0C\u5426\u5219\u65B0\u5EFA\u4EFB\u52A1\u8981\u7B49\u4F60\u5728\u7535\u8111\u4E0A\u70B9\u4E86\u624D\u4F1A\u5F00\u59CB\u3002");
6679
6724
  out("");
6680
6725
  out("\u2705 \u6388\u6743\u6210\u529F\u3002\u63A5\u4E0B\u6765\u8FD0\u884C\uFF1Adeskbuddy enable claude_code,codex");
6681
6726
  return 0;
@@ -7079,6 +7124,13 @@ var Injector = class {
7079
7124
  #lastKey = 0;
7080
7125
  #busy = false;
7081
7126
  #closed = false;
7127
+ #lastWhy = "";
7128
+ /** 用户在这个窗口里按过键(终端报告不算) */
7129
+ #userKeyed = false;
7130
+ /** 本窗口已经写进过一条 */
7131
+ #injectedOnce = false;
7132
+ /** 出现过信任框:之后窗口关闭多半是用户选了 No / 不想在这个目录里跑,不能交回后台 */
7133
+ #sawTrust = false;
7082
7134
  /** instructionId → 那一条(去重:daemon 等 ack 超时重送时,不能写进窗口两遍) */
7083
7135
  #seen = /* @__PURE__ */ new Map();
7084
7136
  constructor(deps) {
@@ -7088,8 +7140,11 @@ var Injector = class {
7088
7140
  noteOutput() {
7089
7141
  this.#lastOut = this.#d.now();
7090
7142
  }
7091
- noteKey() {
7143
+ /** 终端输入(不传 = 按一次真按键算)。只有终端自己回的报告(焦点进出、光标位置等)不算按键 */
7144
+ noteKey(input) {
7145
+ if (input !== void 0 && isTerminalReport(input)) return;
7092
7146
  this.#lastKey = this.#d.now();
7147
+ this.#userKeyed = true;
7093
7148
  }
7094
7149
  get pending() {
7095
7150
  return this.#queue.length;
@@ -7100,6 +7155,7 @@ var Injector = class {
7100
7155
  if (i >= 0) this.#queue.splice(i, 1);
7101
7156
  if (job.done) return;
7102
7157
  job.done = true;
7158
+ this.#d.log?.(`\u7ED3\u5C40 ${job.instructionId} ${msg.status}${msg.reason ? "/" + msg.reason : ""}`);
7103
7159
  job.finalMsg = { instructionId: job.instructionId, ...msg };
7104
7160
  job.reply(job.finalMsg);
7105
7161
  }
@@ -7160,6 +7216,7 @@ var Injector = class {
7160
7216
  return;
7161
7217
  }
7162
7218
  this.#remember(job);
7219
+ this.#d.log?.(`\u6536\u5230 ${job.instructionId} len=${job.text.length} head=${JSON.stringify(job.text.slice(0, 12))}`);
7163
7220
  job.phase = "queued";
7164
7221
  job.reply({ instructionId: job.instructionId, status: "ack" });
7165
7222
  this.#queue.push(job);
@@ -7176,10 +7233,15 @@ var Injector = class {
7176
7233
  const why = [];
7177
7234
  const v = this.#d.view();
7178
7235
  if (!v) return ["no-screen"];
7236
+ const last = this.#d.lastEvent();
7237
+ if (v.trust) {
7238
+ why.push("trust-dialog");
7239
+ this.#sawTrust = true;
7240
+ }
7179
7241
  if (v.permission) why.push("permission");
7242
+ const pristine = this.#d.startupIdle === true && !this.#userKeyed && !this.#injectedOnce && !last;
7180
7243
  if (v.input === null) why.push("no-input-box");
7181
- else if (v.input !== "") why.push("user-typing");
7182
- const last = this.#d.lastEvent();
7244
+ else if (v.input !== "" && !v.placeholder && !pristine) why.push("user-typing");
7183
7245
  if (!last) {
7184
7246
  if (!this.#d.startupIdle) why.push("hook:none");
7185
7247
  } else if (!IDLE_EVENTS.has(last.ev)) why.push("hook:" + last.ev);
@@ -7215,9 +7277,16 @@ var Injector = class {
7215
7277
  this.#d.log?.(`\u7A7A\u95F2\u5224\u65AD\u51FA\u9519\uFF1A${e.message}`);
7216
7278
  }
7217
7279
  if (why.length > 0) {
7280
+ const key = why.join(",");
7281
+ if (key !== this.#lastWhy) {
7282
+ this.#lastWhy = key;
7283
+ const v = this.#d.view();
7284
+ this.#d.log?.(`\u6392\u961F\u4E2D\uFF1A${key} input=${v?.input === null ? "null" : JSON.stringify((v?.input ?? "").slice(0, 12)) + "/" + (v?.input ?? "").length} last=${this.#d.lastEvent()?.ev ?? "none"}`);
7285
+ }
7218
7286
  this.#markQueued(job);
7219
7287
  return;
7220
7288
  }
7289
+ this.#lastWhy = "";
7221
7290
  this.#busy = true;
7222
7291
  try {
7223
7292
  await this.#inject(job);
@@ -7230,6 +7299,8 @@ var Injector = class {
7230
7299
  }
7231
7300
  async #inject(job) {
7232
7301
  const body = prepare(job.text);
7302
+ this.#injectedOnce = true;
7303
+ this.#d.log?.(`\u5199\u5165 ${job.instructionId} len=${body.length - 1}`);
7233
7304
  let enterAt = 0;
7234
7305
  this.#d.holdKeys(true);
7235
7306
  try {
@@ -7276,6 +7347,7 @@ var Injector = class {
7276
7347
  for (const job of [...this.#queue]) {
7277
7348
  if (job.phase === "entered") this.#final(job, { status: "started", mode: "window", reason: "" });
7278
7349
  else if (job.phase === "pasted") this.#final(job, { status: "failed", mode: "window", reason: "window_failed" });
7350
+ else if (this.#sawTrust) this.#final(job, { status: "rejected", mode: "window", reason: "window_failed" });
7279
7351
  else this.#final(job, { status: "not_here" });
7280
7352
  }
7281
7353
  }
@@ -7951,7 +8023,7 @@ function runAttached(tool, p, term0, windowId, deps, launched = false) {
7951
8023
  };
7952
8024
  const injector = new Injector({
7953
8025
  now: () => Date.now(),
7954
- view: () => term ? inspectLines(screenLines(term)) : null,
8026
+ view: () => term ? inspectLines(screenLines(term), screenLinesUndimmed(term)) : null,
7955
8027
  lastEvent: () => readLastEvent(windowId),
7956
8028
  write: toPty,
7957
8029
  sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
@@ -7982,8 +8054,8 @@ function runAttached(tool, p, term0, windowId, deps, launched = false) {
7982
8054
  injector.noteOutput();
7983
8055
  });
7984
8056
  const onStdin = (b) => {
7985
- injector.noteKey();
7986
8057
  const s = typeof b === "string" ? b : decoder.write(b);
8058
+ injector.noteKey(s);
7987
8059
  if (!s) return;
7988
8060
  if (held) held.push(s);
7989
8061
  else toPty(s);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deskbuddy",
3
- "version": "0.2.0-beta.4",
3
+ "version": "0.2.0-beta.5",
4
4
  "description": "把 Claude Code、Codex 等 AI 编码智能体每个会话窗口的状态,实时同步到桌面硬件屏幕",
5
5
  "keywords": [
6
6
  "ai",