deskbuddy 0.2.0-beta.47 → 0.2.0-beta.48
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/{chunk-JPCZVBK6.js → chunk-XWSJEGGS.js} +35 -6
- package/dist/cli.js +200 -41
- package/dist/{enable-OICEMNUF.js → enable-W4R6MJGE.js} +1 -1
- package/dist/hook.cjs +18 -2
- package/hook/hook-debug.log +30 -0
- package/hook/hook.cjs +18 -2
- package/package.json +1 -1
|
@@ -8,6 +8,8 @@ import { randomUUID } from "crypto";
|
|
|
8
8
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
9
9
|
import { homedir, hostname } from "os";
|
|
10
10
|
import { dirname, join, resolve } from "path";
|
|
11
|
+
var DEFAULT_FOCUS_IDLE_SECONDS = 5;
|
|
12
|
+
var MAX_FOCUS_IDLE_SECONDS = 600;
|
|
11
13
|
function defaultRemoteCwd(home = homedir()) {
|
|
12
14
|
return process.env.DESKBUDDY_DEFAULT_REMOTE_CWD || join(home, "DeskBuddy");
|
|
13
15
|
}
|
|
@@ -206,7 +208,8 @@ function loadConfig() {
|
|
|
206
208
|
chatgptCdpPort: portOrUndefined(parsed.chatgptCdpPort),
|
|
207
209
|
focusSessionOnRemote: typeof parsed.focusSessionOnRemote === "boolean" ? parsed.focusSessionOnRemote : void 0,
|
|
208
210
|
focusSessionOnRemoteWindows: typeof parsed.focusSessionOnRemoteWindows === "boolean" ? parsed.focusSessionOnRemoteWindows : void 0,
|
|
209
|
-
focusSessionInApp: typeof parsed.focusSessionInApp === "boolean" ? parsed.focusSessionInApp : void 0
|
|
211
|
+
focusSessionInApp: typeof parsed.focusSessionInApp === "boolean" ? parsed.focusSessionInApp : void 0,
|
|
212
|
+
focusIdleSeconds: typeof parsed.focusIdleSeconds === "number" && Number.isInteger(parsed.focusIdleSeconds) && parsed.focusIdleSeconds >= 0 && parsed.focusIdleSeconds <= MAX_FOCUS_IDLE_SECONDS ? parsed.focusIdleSeconds : void 0
|
|
210
213
|
};
|
|
211
214
|
} catch {
|
|
212
215
|
return fallback;
|
|
@@ -1714,10 +1717,19 @@ var CODEX_EVENTS = [
|
|
|
1714
1717
|
"SessionEnd",
|
|
1715
1718
|
"PermissionRequest"
|
|
1716
1719
|
];
|
|
1717
|
-
var CODEX_TRUST_NOTICE = "\u26A0\uFE0F \u4E0B\u6B21\u542F\u52A8 Codex \u65F6\u4F1A\u8981\u6C42\u4F60\u786E\u8BA4\u4FE1\u4EFB\u8FD9\u4E2A hook\uFF0C\u8BF7\u9009\u62E9\u540C\u610F\u2014\u2014\u4E0D\u786E\u8BA4\u7684\u8BDD Codex \u4E0D\u4F1A\u6267\u884C\u5B83\u3002\n DeskBuddy \u5347\u7EA7\uFF08\u542B\u81EA\u52A8\u5347\u7EA7\uFF09\u65B0\u589E hook \u6761\u76EE\u65F6\uFF0CCodex \
|
|
1720
|
+
var CODEX_TRUST_NOTICE = "\u26A0\uFE0F \u4E0B\u6B21\u542F\u52A8 Codex \u65F6\u4F1A\u8981\u6C42\u4F60\u786E\u8BA4\u4FE1\u4EFB\u8FD9\u4E2A hook\uFF0C\u8BF7\u9009\u62E9\u540C\u610F\u2014\u2014\u4E0D\u786E\u8BA4\u7684\u8BDD Codex \u4E0D\u4F1A\u6267\u884C\u5B83\u3002\n \u6BCF\u6761 hook \u90FD\u5E26\u300CDeskBuddy\uFF1A\u2026\u300D\u540D\u5B57\uFF0C\u5BA1\u67E5\u6846\u91CC\u80FD\u770B\u5230\u5B83\u662F\u505A\u4EC0\u4E48\u7684\u3002\n DeskBuddy \u5347\u7EA7\uFF08\u542B\u81EA\u52A8\u5347\u7EA7\uFF09\u65B0\u589E\u6216\u6539\u52A8 hook \u6761\u76EE\u65F6\uFF0CCodex \u4F1A\u5C31\u8FD9\u4E9B\u6761\u76EE\u518D\u95EE\u4E00\u6B21\uFF0C\u540C\u6837\u9009\u540C\u610F\uFF1B\u786E\u8BA4\u524D\u8FD9\u4E9B\u6761\u76EE\u4E0D\u6267\u884C\u3002\n Codex \u7ECF `--remote` \u8FDE\u5E38\u9A7B app-server \u4F7F\u7528\u65F6\uFF0C\u7A97\u53E3\u91CC\u7684\u6388\u6743\u6846\u4E5F\u80FD\u5728\u8BBE\u5907\u4E0A\u786E\u8BA4\uFF08\u5B88\u62A4\u8FDB\u7A0B\u5728\u8DD1\u65F6\u751F\u6548\uFF09";
|
|
1718
1721
|
function codexConfigPath() {
|
|
1719
1722
|
return join7(codexHome(), "config.toml");
|
|
1720
1723
|
}
|
|
1724
|
+
var CODEX_HOOK_LABELS = {
|
|
1725
|
+
UserPromptSubmit: "DeskBuddy\uFF1A\u628A\u4F60\u53D1\u7684\u6D88\u606F\u540C\u6B65\u5230\u684C\u9762\u8BBE\u5907",
|
|
1726
|
+
PreToolUse: "DeskBuddy\uFF1A\u628A\u6B63\u5728\u6267\u884C\u7684\u64CD\u4F5C\u540C\u6B65\u5230\u684C\u9762\u8BBE\u5907",
|
|
1727
|
+
PostToolUse: "DeskBuddy\uFF1A\u628A\u64CD\u4F5C\u7ED3\u679C\u540C\u6B65\u5230\u684C\u9762\u8BBE\u5907",
|
|
1728
|
+
Stop: "DeskBuddy\uFF1A\u4EFB\u52A1\u5B8C\u6210\u65F6\u901A\u77E5\u684C\u9762\u8BBE\u5907",
|
|
1729
|
+
SessionEnd: "DeskBuddy\uFF1A\u4F1A\u8BDD\u7ED3\u675F\u65F6\u901A\u77E5\u684C\u9762\u8BBE\u5907",
|
|
1730
|
+
PermissionRequest: "DeskBuddy\uFF1A\u628A\u6388\u6743\u8BF7\u6C42\u540C\u6B65\u5230\u684C\u9762\u8BBE\u5907"
|
|
1731
|
+
};
|
|
1732
|
+
var CODEX_APPENDED_LABEL = "DeskBuddy\uFF1A\u8BFB\u53D6\u4F60\u5728\u684C\u9762\u8BBE\u5907\u4E0A\u8FFD\u52A0\u7684\u6307\u4EE4";
|
|
1721
1733
|
var CODEX_APPENDED_TIMEOUT_S = 3;
|
|
1722
1734
|
function renderBlock(hookPath, events, tool, platform) {
|
|
1723
1735
|
const script = platform === "win32" ? winPsPath(hookPath) : hookPath;
|
|
@@ -1725,9 +1737,10 @@ function renderBlock(hookPath, events, tool, platform) {
|
|
|
1725
1737
|
const appended = JSON.stringify(`node ${script} ${tool} --appended`);
|
|
1726
1738
|
const lines = [CODEX_MARKER_BEGIN];
|
|
1727
1739
|
for (const event of events) {
|
|
1728
|
-
|
|
1740
|
+
const label2 = JSON.stringify(CODEX_HOOK_LABELS[event] ?? `DeskBuddy\uFF1A${event} \u65F6\u540C\u6B65\u5230\u684C\u9762\u8BBE\u5907`);
|
|
1741
|
+
lines.push(`[[hooks.${event}]]`, 'matcher = ""', "hooks = [", ` { type = "command", command = ${command}, async = true, timeoutSec = 5, statusMessage = ${label2} }`);
|
|
1729
1742
|
if (event === "UserPromptSubmit") {
|
|
1730
|
-
lines.push(` , { type = "command", command = ${appended}, async = false, timeout = ${CODEX_APPENDED_TIMEOUT_S} }`);
|
|
1743
|
+
lines.push(` , { type = "command", command = ${appended}, async = false, timeout = ${CODEX_APPENDED_TIMEOUT_S}, statusMessage = ${JSON.stringify(CODEX_APPENDED_LABEL)} }`);
|
|
1731
1744
|
}
|
|
1732
1745
|
lines.push("]", "");
|
|
1733
1746
|
}
|
|
@@ -7225,6 +7238,16 @@ var TYPE_TEXT = "text";
|
|
|
7225
7238
|
var TYPE_SUMMARY = "summary";
|
|
7226
7239
|
var MAX_TEXT_LENGTH = 2e3;
|
|
7227
7240
|
var MAX_ID_LENGTH = 64;
|
|
7241
|
+
function parseFocusOverride(d) {
|
|
7242
|
+
const o = {};
|
|
7243
|
+
const e = typeof d.focusEnabled === "string" ? d.focusEnabled.trim() : d.focusEnabled;
|
|
7244
|
+
if (e === true || e === "true") o.enabled = true;
|
|
7245
|
+
else if (e === false || e === "false") o.enabled = false;
|
|
7246
|
+
const raw = d.focusIdleSec;
|
|
7247
|
+
const sec = typeof raw === "number" ? raw : typeof raw === "string" && /^\d{1,3}$/.test(raw.trim()) ? Number(raw.trim()) : NaN;
|
|
7248
|
+
if (Number.isInteger(sec) && sec >= 0 && sec <= 600) o.idleMs = sec * 1e3;
|
|
7249
|
+
return o.enabled === void 0 && o.idleMs === void 0 ? void 0 : o;
|
|
7250
|
+
}
|
|
7228
7251
|
function str2(v) {
|
|
7229
7252
|
return typeof v === "string" ? v : "";
|
|
7230
7253
|
}
|
|
@@ -7261,6 +7284,10 @@ function parseCommand(frame) {
|
|
|
7261
7284
|
const dirRef = !sessionId && /^[0-9a-f]{16}$/.test(rawDir) ? rawDir : "";
|
|
7262
7285
|
const cmd = { instructionId, type, sessionId, tool, text, expireAt: parseMillis(d.expireAt), dirRef };
|
|
7263
7286
|
if (type === TYPE_SUMMARY) cmd.kind = "summary";
|
|
7287
|
+
else {
|
|
7288
|
+
const focus = parseFocusOverride(d);
|
|
7289
|
+
if (focus) cmd.focus = focus;
|
|
7290
|
+
}
|
|
7264
7291
|
return cmd;
|
|
7265
7292
|
}
|
|
7266
7293
|
var TYPE_APPROVAL = "approval";
|
|
@@ -11563,7 +11590,7 @@ var defaultLoginOpenDeps = () => ({
|
|
|
11563
11590
|
});
|
|
11564
11591
|
async function installCodebuddyHooks(log) {
|
|
11565
11592
|
try {
|
|
11566
|
-
const { installTools: installTools2 } = await import("./enable-
|
|
11593
|
+
const { installTools: installTools2 } = await import("./enable-W4R6MJGE.js");
|
|
11567
11594
|
installTools2(["workbuddy"], () => {
|
|
11568
11595
|
}, null, { auto: true });
|
|
11569
11596
|
} catch (e) {
|
|
@@ -12077,7 +12104,7 @@ function defaultOfferDeps(log) {
|
|
|
12077
12104
|
}),
|
|
12078
12105
|
openLogin: () => defaultOpenLogin(platform),
|
|
12079
12106
|
installHooks: async () => {
|
|
12080
|
-
const { installTools: installTools2 } = await import("./enable-
|
|
12107
|
+
const { installTools: installTools2 } = await import("./enable-W4R6MJGE.js");
|
|
12081
12108
|
installTools2(["workbuddy"], (l) => log(`[WorkBuddy \u72EC\u7ACB\u7248] ${l}`), null, { auto: true });
|
|
12082
12109
|
},
|
|
12083
12110
|
log: (l) => log(`[WorkBuddy \u72EC\u7ACB\u7248] ${l}`),
|
|
@@ -12317,6 +12344,8 @@ async function runEnable(tools, opts = {}) {
|
|
|
12317
12344
|
}
|
|
12318
12345
|
|
|
12319
12346
|
export {
|
|
12347
|
+
DEFAULT_FOCUS_IDLE_SECONDS,
|
|
12348
|
+
MAX_FOCUS_IDLE_SECONDS,
|
|
12320
12349
|
defaultRemoteCwd,
|
|
12321
12350
|
ensureDefaultRemoteCwd,
|
|
12322
12351
|
remoteCwdReady,
|
package/dist/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
COZE_TOOL,
|
|
19
19
|
CdpKeeper,
|
|
20
20
|
DEFAULT_CDP_PORT,
|
|
21
|
+
DEFAULT_FOCUS_IDLE_SECONDS,
|
|
21
22
|
DM_DEFAULT_CDP_PORT,
|
|
22
23
|
DUMATE_BRAND,
|
|
23
24
|
DumateKeeper,
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
LAUNCH_TOOLS,
|
|
28
29
|
LIMITED_NOTE,
|
|
29
30
|
LOCATE_EVERY_MS,
|
|
31
|
+
MAX_FOCUS_IDLE_SECONDS,
|
|
30
32
|
MAX_TEXT_LENGTH,
|
|
31
33
|
NOT_REGISTERED_RE_SRC,
|
|
32
34
|
PROBE,
|
|
@@ -279,22 +281,25 @@ import {
|
|
|
279
281
|
writeState,
|
|
280
282
|
writeWakeMark,
|
|
281
283
|
writeWakeSecret
|
|
282
|
-
} from "./chunk-
|
|
284
|
+
} from "./chunk-XWSJEGGS.js";
|
|
283
285
|
|
|
284
286
|
// src/commands/config.ts
|
|
285
287
|
var BOOL_KEYS = {
|
|
286
288
|
focusSessionOnRemote: {
|
|
287
|
-
def:
|
|
288
|
-
|
|
289
|
+
def: true,
|
|
290
|
+
sticky: true,
|
|
291
|
+
on: "\u5DF2\u6253\u5F00\uFF1A\u8BBE\u5907\u56DE\u590D / \u65B0\u5EFA\u4EFB\u52A1\u540E\uFF0C\u628A WorkBuddy\u3001ChatGPT\uFF08Codex\uFF09\u3001\u5343\u95EE\u529E\u516C\u5207\u5230\u8BE5\u4F1A\u8BDD\u5E76\u8C03\u5230\u524D\u53F0\uFF08\u4F60\u5728\u7A7A\u95F2\u95E8\u69DB\u5185\u7528\u8FC7\u952E\u9F20\u65F6\u4E0D\u5207\uFF09",
|
|
289
292
|
off: "\u5DF2\u5173\u95ED\uFF1A\u8BBE\u5907\u56DE\u590D / \u65B0\u5EFA\u4EFB\u52A1\u540E\u4E0D\u518D\u628A App \u8C03\u5230\u524D\u53F0"
|
|
290
293
|
},
|
|
291
294
|
focusSessionOnRemoteWindows: {
|
|
292
|
-
def:
|
|
293
|
-
|
|
295
|
+
def: null,
|
|
296
|
+
sticky: true,
|
|
297
|
+
on: "\u5DF2\u6253\u5F00\uFF1AWindows \u4E0A\u7528\u6DF1\u94FE\u5207\u4F1A\u8BDD\uFF08App \u6CA1\u6CE8\u518C\u534F\u8BAE\u7684\u7167\u6837\u4E0D\u5207\uFF09",
|
|
294
298
|
off: "\u5DF2\u5173\u95ED\uFF1AWindows \u4E0A\u4E0D\u7528\u6DF1\u94FE\u5207\u4F1A\u8BDD"
|
|
295
299
|
},
|
|
296
300
|
focusSessionInApp: {
|
|
297
301
|
def: true,
|
|
302
|
+
sticky: false,
|
|
298
303
|
on: "\u5DF2\u6253\u5F00\uFF1A\u8BBE\u5907\u56DE\u590D opencode \u4F1A\u8BDD\u540E\uFF0C\u7EC8\u7AEF\u754C\u9762\u91CC\u5207\u5230\u8BE5\u4F1A\u8BDD\uFF08\u4E0D\u62A2\u7126\u70B9\uFF09",
|
|
299
304
|
off: "\u5DF2\u5173\u95ED\uFF1A\u8BBE\u5907\u56DE\u590D opencode \u4F1A\u8BDD\u540E\u4E0D\u5207\u6362\u754C\u9762\u91CC\u7684\u4F1A\u8BDD"
|
|
300
305
|
}
|
|
@@ -304,7 +309,12 @@ var USAGE = [
|
|
|
304
309
|
"\u7528\u6CD5\uFF1Adeskbuddy config set transcript on|off \uFF08\u4F1A\u8BDD\u5BF9\u8BDD\u540C\u6B65\u7684\u672C\u5730\u5F00\u5173\uFF0C\u9ED8\u8BA4 on\uFF09",
|
|
305
310
|
" deskbuddy config get transcript",
|
|
306
311
|
` deskbuddy config set <${Object.keys(BOOL_KEYS).join("|")}> true|false`,
|
|
307
|
-
" deskbuddy config get <\u540C\u4E0A>"
|
|
312
|
+
" deskbuddy config get <\u540C\u4E0A>",
|
|
313
|
+
" deskbuddy config set focusSessionOnRemoteWindows auto \uFF08Windows \u4E0D\u5355\u72EC\u8BBE\uFF0C\u8DDF\u968F focusSessionOnRemote\uFF0C\u9ED8\u8BA4\u5C31\u662F\u8FD9\u6837\uFF09",
|
|
314
|
+
` deskbuddy config set focusIdleSeconds <0\uFF5E${MAX_FOCUS_IDLE_SECONDS}> \uFF08\u5207\u4F1A\u8BDD\u7684\u952E\u9F20\u7A7A\u95F2\u95E8\u69DB\u79D2\u6570\uFF0C\u9ED8\u8BA4 ${DEFAULT_FOCUS_IDLE_SECONDS}\uFF1B0 = \u4E0D\u770B\u952E\u9F20\uFF09`,
|
|
315
|
+
" deskbuddy config set focusIdleSeconds default \uFF08\u5220\u6389\u672C\u5730\u95E8\u69DB\uFF0C\u6062\u590D\u9ED8\u8BA4\uFF09",
|
|
316
|
+
" deskbuddy config get focusIdleSeconds",
|
|
317
|
+
" \uFF08App \u5BF9\u8FD9\u53F0\u7535\u8111 / \u8BBE\u5907\u8BBE\u8FC7\u5207\u4F1A\u8BDD\u5F00\u5173\u6216\u95E8\u69DB\u65F6\uFF0C\u4EE5 App \u4E3A\u51C6\uFF09"
|
|
308
318
|
].join("\n");
|
|
309
319
|
function runConfig(args, out = (s2) => process.stdout.write(s2 + "\n")) {
|
|
310
320
|
const [verb, key, value] = args;
|
|
@@ -313,6 +323,7 @@ function runConfig(args, out = (s2) => process.stdout.write(s2 + "\n")) {
|
|
|
313
323
|
return 1;
|
|
314
324
|
}
|
|
315
325
|
if (key === "transcript") return transcript(verb, value, out);
|
|
326
|
+
if (key === "focusIdleSeconds") return idleSeconds(verb, value, out);
|
|
316
327
|
if (!isBoolKey(key)) {
|
|
317
328
|
out(USAGE);
|
|
318
329
|
return 1;
|
|
@@ -320,19 +331,46 @@ function runConfig(args, out = (s2) => process.stdout.write(s2 + "\n")) {
|
|
|
320
331
|
const spec = BOOL_KEYS[key];
|
|
321
332
|
const cfg = loadConfig();
|
|
322
333
|
if (verb === "get") {
|
|
323
|
-
out(String(cfg[key] ?? spec.def));
|
|
334
|
+
out(String(cfg[key] ?? spec.def ?? "auto"));
|
|
335
|
+
return 0;
|
|
336
|
+
}
|
|
337
|
+
if (spec.def === null && value === "auto") {
|
|
338
|
+
saveConfig({ ...cfg, [key]: void 0 });
|
|
339
|
+
out("\u5DF2\u6539\u4E3A\u8DDF\u968F focusSessionOnRemote\uFF08\u5B88\u62A4\u8FDB\u7A0B\u4E0B\u4E00\u6761\u6307\u4EE4\u751F\u6548\uFF09");
|
|
324
340
|
return 0;
|
|
325
341
|
}
|
|
326
342
|
if (value !== "true" && value !== "false") {
|
|
327
|
-
out("\u53D6\u503C\u53EA\u80FD\u662F true \u6216 false");
|
|
343
|
+
out(spec.def === null ? "\u53D6\u503C\u53EA\u80FD\u662F true\u3001false \u6216 auto" : "\u53D6\u503C\u53EA\u80FD\u662F true \u6216 false");
|
|
328
344
|
return 1;
|
|
329
345
|
}
|
|
330
346
|
const v = value === "true";
|
|
331
|
-
const next = { ...cfg, [key]: v === spec.def ? void 0 : v };
|
|
347
|
+
const next = { ...cfg, [key]: !spec.sticky && v === spec.def ? void 0 : v };
|
|
332
348
|
saveConfig(next);
|
|
333
349
|
out(`${v ? spec.on : spec.off}\uFF08\u5B88\u62A4\u8FDB\u7A0B\u4E0B\u4E00\u6761\u6307\u4EE4\u751F\u6548\uFF09`);
|
|
334
350
|
return 0;
|
|
335
351
|
}
|
|
352
|
+
function idleSeconds(verb, value, out) {
|
|
353
|
+
const cfg = loadConfig();
|
|
354
|
+
if (verb === "get") {
|
|
355
|
+
out(String(cfg.focusIdleSeconds ?? DEFAULT_FOCUS_IDLE_SECONDS));
|
|
356
|
+
return 0;
|
|
357
|
+
}
|
|
358
|
+
if (value === "default") {
|
|
359
|
+
saveConfig({ ...cfg, focusIdleSeconds: void 0 });
|
|
360
|
+
out(`\u5DF2\u6062\u590D\u9ED8\u8BA4 ${DEFAULT_FOCUS_IDLE_SECONDS} \u79D2\uFF08App \u8BBE\u8FC7\u95E8\u69DB\u65F6\u4EE5 App \u4E3A\u51C6\uFF1B\u5B88\u62A4\u8FDB\u7A0B\u4E0B\u4E00\u6761\u6307\u4EE4\u751F\u6548\uFF09`);
|
|
361
|
+
return 0;
|
|
362
|
+
}
|
|
363
|
+
const n = value !== void 0 && /^\d{1,3}$/.test(value) ? Number(value) : NaN;
|
|
364
|
+
if (!Number.isInteger(n) || n < 0 || n > MAX_FOCUS_IDLE_SECONDS) {
|
|
365
|
+
out(`\u53D6\u503C\u53EA\u80FD\u662F 0\uFF5E${MAX_FOCUS_IDLE_SECONDS} \u7684\u6574\u6570\uFF08\u79D2\uFF09\uFF0C\u6216 default \u6062\u590D\u9ED8\u8BA4`);
|
|
366
|
+
return 1;
|
|
367
|
+
}
|
|
368
|
+
saveConfig({ ...cfg, focusIdleSeconds: n });
|
|
369
|
+
out(
|
|
370
|
+
`${n === 0 ? "\u5DF2\u8BBE\u4E3A 0\uFF1A\u8BBE\u5907\u56DE\u590D / \u65B0\u5EFA\u4EFB\u52A1\u540E\u4E0D\u770B\u952E\u9F20\uFF0C\u6536\u5230\u5C31\u5207\u5230\u8BE5\u4F1A\u8BDD" : `\u5DF2\u8BBE\u4E3A ${n} \u79D2\uFF1A\u4F60 ${n} \u79D2\u5185\u7528\u8FC7\u952E\u9F20\u65F6\u4E0D\u5207\u4F1A\u8BDD`}\uFF08App \u8BBE\u8FC7\u95E8\u69DB\u65F6\u4EE5 App \u4E3A\u51C6\uFF1B\u5B88\u62A4\u8FDB\u7A0B\u4E0B\u4E00\u6761\u6307\u4EE4\u751F\u6548\uFF09`
|
|
371
|
+
);
|
|
372
|
+
return 0;
|
|
373
|
+
}
|
|
336
374
|
function transcript(verb, value, out) {
|
|
337
375
|
const cfg = loadConfig();
|
|
338
376
|
if (verb === "get") {
|
|
@@ -2280,10 +2318,18 @@ var TitleRefresh = class {
|
|
|
2280
2318
|
import { basename as basename2 } from "path";
|
|
2281
2319
|
var KEEPALIVE_MS = 2 * 60 * 1e3;
|
|
2282
2320
|
var MAX_SESSIONS = 20;
|
|
2283
|
-
var CAPS = ["cmd_result", "window_inject", "resume_ended", "reply_idle_window", "turns", "summary", ...SUMMARY_CAPS, ...TRANSCRIPT_CAPS];
|
|
2321
|
+
var CAPS = ["cmd_result", "window_inject", "resume_ended", "reply_idle_window", "turns", "summary", "focus_cfg", ...SUMMARY_CAPS, ...TRANSCRIPT_CAPS];
|
|
2284
2322
|
function capsFor(cfg) {
|
|
2285
2323
|
return cfg.transcript === false ? CAPS.filter((c) => !TRANSCRIPT_CAPS.includes(c)) : [...CAPS];
|
|
2286
2324
|
}
|
|
2325
|
+
function focusLocalFor(cfg) {
|
|
2326
|
+
const o = {};
|
|
2327
|
+
if (typeof cfg.focusSessionOnRemote === "boolean") o.enabled = cfg.focusSessionOnRemote;
|
|
2328
|
+
const n = cfg.focusIdleSeconds;
|
|
2329
|
+
if (typeof n === "number" && Number.isInteger(n) && n >= 0 && n <= 600) o.idleSeconds = n;
|
|
2330
|
+
if (typeof cfg.focusSessionOnRemoteWindows === "boolean") o.windowsEnabled = cfg.focusSessionOnRemoteWindows;
|
|
2331
|
+
return o;
|
|
2332
|
+
}
|
|
2287
2333
|
function turnsOf(s2) {
|
|
2288
2334
|
return Number.isInteger(s2.turns) && s2.turns >= 0 ? s2.turns : void 0;
|
|
2289
2335
|
}
|
|
@@ -2305,7 +2351,7 @@ function buildSnapshot(states, tool, cfg, nowMs, remote) {
|
|
|
2305
2351
|
if (turns !== void 0) o.turns = turns;
|
|
2306
2352
|
return o;
|
|
2307
2353
|
});
|
|
2308
|
-
return { clientId: cfg.clientId, hostName: cfg.hostName, tool, snapshotAt: nowMs, sessions, caps: capsFor(cfg) };
|
|
2354
|
+
return { clientId: cfg.clientId, hostName: cfg.hostName, tool, snapshotAt: nowMs, sessions, caps: capsFor(cfg), focusLocal: focusLocalFor(cfg) };
|
|
2309
2355
|
}
|
|
2310
2356
|
|
|
2311
2357
|
// src/core/dirs.ts
|
|
@@ -5982,7 +6028,7 @@ function sleep(ms) {
|
|
|
5982
6028
|
// src/daemon/focusSession.ts
|
|
5983
6029
|
import { execFile as execFile3 } from "child_process";
|
|
5984
6030
|
import { open as open2 } from "fs/promises";
|
|
5985
|
-
var
|
|
6031
|
+
var DEFAULT_FOCUS_IDLE_MS = DEFAULT_FOCUS_IDLE_SECONDS * 1e3;
|
|
5986
6032
|
var FOCUS_ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
5987
6033
|
var okId = (id) => typeof id === "string" && FOCUS_ID_RE.test(id);
|
|
5988
6034
|
function workbuddyChatUrl(sessionId) {
|
|
@@ -6006,8 +6052,19 @@ function focusScheme(t) {
|
|
|
6006
6052
|
return t.kind === "workbuddy" ? "workbuddy" : t.kind === "codex" ? "codex" : "qwenwork-cn";
|
|
6007
6053
|
}
|
|
6008
6054
|
var TOOL_NAME = { workbuddy: "WorkBuddy", codex: "ChatGPT\uFF08Codex\uFF09", qwenwork: "\u5343\u95EE\u529E\u516C" };
|
|
6009
|
-
function focusSettings(cfg) {
|
|
6010
|
-
|
|
6055
|
+
function focusSettings(cfg, override) {
|
|
6056
|
+
const app = override?.enabled;
|
|
6057
|
+
const onRemote = app ?? cfg.focusSessionOnRemote !== false;
|
|
6058
|
+
const local = cfg.focusIdleSeconds;
|
|
6059
|
+
const localMs = Number.isInteger(local) && local >= 0 && local <= MAX_FOCUS_IDLE_SECONDS ? local * 1e3 : void 0;
|
|
6060
|
+
const ov = override?.idleMs;
|
|
6061
|
+
const appMs = Number.isInteger(ov) && ov >= 0 && ov <= MAX_FOCUS_IDLE_SECONDS * 1e3 ? ov : void 0;
|
|
6062
|
+
return {
|
|
6063
|
+
onRemote,
|
|
6064
|
+
onRemoteWindows: app ?? (typeof cfg.focusSessionOnRemoteWindows === "boolean" ? cfg.focusSessionOnRemoteWindows : onRemote),
|
|
6065
|
+
inApp: app ?? cfg.focusSessionInApp !== false,
|
|
6066
|
+
idleMinMs: appMs ?? localMs ?? DEFAULT_FOCUS_IDLE_MS
|
|
6067
|
+
};
|
|
6011
6068
|
}
|
|
6012
6069
|
var APP_ORIGINATOR_RE = /"originator"\s*:\s*"(Codex Desktop|codex_work_desktop)"/;
|
|
6013
6070
|
function isChatgptAppRolloutHead(head) {
|
|
@@ -6070,6 +6127,52 @@ function defaultProtocolRegistered(scheme) {
|
|
|
6070
6127
|
if (skipSystem() || !/^[a-z][a-z0-9+.-]{0,31}$/.test(scheme)) return Promise.resolve(false);
|
|
6071
6128
|
return execOk("reg.exe", ["query", `HKCR\\${scheme}`, "/v", "URL Protocol"], 5e3);
|
|
6072
6129
|
}
|
|
6130
|
+
var WIN_PROCESS_NAMES = {
|
|
6131
|
+
workbuddy: ["WorkBuddy"],
|
|
6132
|
+
qwenwork: ["QwenWorkCN"],
|
|
6133
|
+
codex: ["ChatGPT (Beta)", "ChatGPT"]
|
|
6134
|
+
};
|
|
6135
|
+
var WIN_RAISE_DELAY_MS = 1500;
|
|
6136
|
+
var WIN_RAISE_TIMEOUT_MS = 8e3;
|
|
6137
|
+
function tasklistHas(stdout6, names) {
|
|
6138
|
+
const want = new Set(names.map((n) => n.toLowerCase()));
|
|
6139
|
+
for (const line of stdout6.split(/\r?\n/)) {
|
|
6140
|
+
const m = /^"([^"]+)"/.exec(line.trim());
|
|
6141
|
+
if (m && want.has(m[1].replace(/\.exe$/i, "").toLowerCase())) return true;
|
|
6142
|
+
}
|
|
6143
|
+
return false;
|
|
6144
|
+
}
|
|
6145
|
+
async function defaultAppRunning(names, exec = defaultExec) {
|
|
6146
|
+
if (skipSystem()) return null;
|
|
6147
|
+
const r = await exec("tasklist", ["/FO", "CSV", "/NH"], 1e4);
|
|
6148
|
+
return r.code === 0 ? tasklistHas(r.stdout, names) : null;
|
|
6149
|
+
}
|
|
6150
|
+
var PROC_NAME_RE = /^[A-Za-z0-9 ()._-]{1,64}$/;
|
|
6151
|
+
function winRaiseScript(names) {
|
|
6152
|
+
if (names.length === 0 || !names.every((n) => PROC_NAME_RE.test(n))) return null;
|
|
6153
|
+
const list = names.map((n) => `'${n}'`).join(",");
|
|
6154
|
+
return String.raw`Add-Type @"
|
|
6155
|
+
using System; using System.Runtime.InteropServices;
|
|
6156
|
+
public class DbFocusWin {
|
|
6157
|
+
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr h);
|
|
6158
|
+
[DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr h, int n);
|
|
6159
|
+
[DllImport("user32.dll")] public static extern bool IsIconic(IntPtr h);
|
|
6160
|
+
[DllImport("user32.dll")] public static extern void keybd_event(byte vk, byte scan, uint flags, UIntPtr extra);
|
|
6161
|
+
}
|
|
6162
|
+
"@
|
|
6163
|
+
$p = Get-Process -Name ${list} -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowHandle -ne [IntPtr]::Zero -and $_.MainWindowTitle } | Select-Object -First 1
|
|
6164
|
+
if (-not $p) { Write-Output 'nowin'; exit 0 }
|
|
6165
|
+
$h = $p.MainWindowHandle
|
|
6166
|
+
[DbFocusWin]::keybd_event(0x12, 0, 0, [UIntPtr]::Zero); [DbFocusWin]::keybd_event(0x12, 0, 2, [UIntPtr]::Zero)
|
|
6167
|
+
if ([DbFocusWin]::IsIconic($h)) { [void][DbFocusWin]::ShowWindow($h, 9) }
|
|
6168
|
+
Write-Output ("fg={0}" -f [DbFocusWin]::SetForegroundWindow($h))`;
|
|
6169
|
+
}
|
|
6170
|
+
async function defaultRaiseWindow(names, exec = defaultExec) {
|
|
6171
|
+
const script = winRaiseScript(names);
|
|
6172
|
+
if (skipSystem() || !script) return false;
|
|
6173
|
+
const r = await exec("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], WIN_RAISE_TIMEOUT_MS);
|
|
6174
|
+
return r.code === 0 && /fg=True/i.test(r.stdout);
|
|
6175
|
+
}
|
|
6073
6176
|
var idleWhy = (idle) => idle === null ? "\u8BFB\u4E0D\u5230\u952E\u9F20\u7A7A\u95F2\u65F6\u957F" : `\u7528\u6237\u6B63\u5728\u7528\u7535\u8111\uFF08${Math.round(idle / 1e3)} \u79D2\u524D\u78B0\u8FC7\u952E\u9F20\uFF09`;
|
|
6074
6177
|
var DONE_MAX = 200;
|
|
6075
6178
|
var IDLE_REFRESH_MAC_MS = 5 * 1e3;
|
|
@@ -6081,6 +6184,11 @@ var SessionFocuser = class {
|
|
|
6081
6184
|
/** 后台取到的键鼠空闲(opencode 切会话只读它,投递永不等取空闲,评审 73f95f0) */
|
|
6082
6185
|
#idleCache = null;
|
|
6083
6186
|
#lastInAppUse = Number.NEGATIVE_INFINITY;
|
|
6187
|
+
/** 最近一次 opencode 投递带的 App 设置:后台刷新据它判开关 / 门槛(App 开了而本地关着时也要刷) */
|
|
6188
|
+
#lastInAppOverride;
|
|
6189
|
+
/** Windows 调前台:是否有一轮在等 / 在跑;等着处理的最新目标 */
|
|
6190
|
+
#raiseBusy = false;
|
|
6191
|
+
#raiseNext = null;
|
|
6084
6192
|
#idleTimer = null;
|
|
6085
6193
|
#refreshing = false;
|
|
6086
6194
|
constructor(d) {
|
|
@@ -6091,12 +6199,13 @@ var SessionFocuser = class {
|
|
|
6091
6199
|
return this.#platform() === "win32" ? IDLE_REFRESH_WIN_MS : IDLE_REFRESH_MAC_MS;
|
|
6092
6200
|
}
|
|
6093
6201
|
/**
|
|
6094
|
-
* 后台刷新一次空闲缓存:只在
|
|
6095
|
-
*
|
|
6202
|
+
* 后台刷新一次空闲缓存:只在 App 内切换开着、门槛不是 0、且最近 10 分钟内有过 opencode 投递时取(开关 / 门槛按最近一次投递带的 App 设置);
|
|
6203
|
+
* 同一时刻只取一次。不活跃了就停掉定时器(下次投递再起)
|
|
6096
6204
|
*/
|
|
6097
6205
|
async refreshIdle() {
|
|
6098
6206
|
if (this.#refreshing) return;
|
|
6099
|
-
|
|
6207
|
+
const s2 = focusSettings(this.#cfg(), this.#lastInAppOverride);
|
|
6208
|
+
if (!s2.inApp || s2.idleMinMs === 0 || this.#now() - this.#lastInAppUse > IDLE_ACTIVE_WINDOW_MS) {
|
|
6100
6209
|
this.#stopIdleTimer();
|
|
6101
6210
|
return;
|
|
6102
6211
|
}
|
|
@@ -6122,16 +6231,22 @@ var SessionFocuser = class {
|
|
|
6122
6231
|
return c.ms;
|
|
6123
6232
|
}
|
|
6124
6233
|
/** opencode 这类 App 内切换(不激活)开着吗 */
|
|
6125
|
-
inAppEnabled() {
|
|
6126
|
-
return focusSettings(this.#cfg()).inApp;
|
|
6234
|
+
inAppEnabled(override) {
|
|
6235
|
+
return focusSettings(this.#cfg(), override).inApp;
|
|
6127
6236
|
}
|
|
6128
6237
|
/**
|
|
6129
|
-
* opencode 这类 App 内切换现在能不能做:开关开着,且用户没在用电脑(键鼠空闲 ≥
|
|
6238
|
+
* opencode 这类 App 内切换现在能不能做:开关开着,且用户没在用电脑(键鼠空闲 ≥ 门槛;读不到也不切)。
|
|
6239
|
+
* 门槛 0 → 不看键鼠直接切(不要求有缓存、不起取空闲定时器)。
|
|
6130
6240
|
* 🔴 同步、只读后台缓存,绝不在投递路径上取空闲(Windows 要起 PowerShell)。缓存没有 / 过期 → 这次不切;
|
|
6131
6241
|
* 同时记下「最近有 opencode 投递」并确保后台刷新在跑(第一次投递时还没有缓存,从下一条起生效)。跳过时记一行
|
|
6132
6242
|
*/
|
|
6133
|
-
inAppReady(instructionId, name = "opencode") {
|
|
6134
|
-
|
|
6243
|
+
inAppReady(instructionId, override, name = "opencode") {
|
|
6244
|
+
const s2 = focusSettings(this.#cfg(), override);
|
|
6245
|
+
this.#lastInAppOverride = override ?? void 0;
|
|
6246
|
+
if (!s2.inApp || s2.idleMinMs === 0) {
|
|
6247
|
+
this.#stopIdleTimer();
|
|
6248
|
+
return s2.inApp;
|
|
6249
|
+
}
|
|
6135
6250
|
this.#lastInAppUse = this.#now();
|
|
6136
6251
|
if (!this.#idleTimer && this.#d.timers !== false) {
|
|
6137
6252
|
void this.refreshIdle().catch(() => {
|
|
@@ -6141,29 +6256,28 @@ var SessionFocuser = class {
|
|
|
6141
6256
|
this.#idleTimer.unref?.();
|
|
6142
6257
|
}
|
|
6143
6258
|
const idle = this.#cachedIdle();
|
|
6144
|
-
if (idle === null || idle <
|
|
6259
|
+
if (idle === null || idle < s2.idleMinMs) {
|
|
6145
6260
|
this.#d.log(`\u6307\u4EE4 ${instructionId} \u4E0D\u5728 ${name} \u754C\u9762\u91CC\u5207\u4F1A\u8BDD\uFF1A${idleWhy(idle)}`);
|
|
6146
6261
|
return false;
|
|
6147
6262
|
}
|
|
6148
6263
|
return true;
|
|
6149
6264
|
}
|
|
6150
6265
|
/** 深链切会话在这台电脑上会生效吗(开关 + 平台)。调用方据此决定要不要先做查找类的准备工作 */
|
|
6151
|
-
remoteEnabled() {
|
|
6152
|
-
const s2 = focusSettings(this.#cfg());
|
|
6153
|
-
if (!s2.onRemote) return false;
|
|
6266
|
+
remoteEnabled(override) {
|
|
6267
|
+
const s2 = focusSettings(this.#cfg(), override);
|
|
6154
6268
|
const p = this.#platform();
|
|
6155
|
-
return p === "darwin" || p === "win32" && s2.onRemoteWindows;
|
|
6269
|
+
return p === "darwin" && s2.onRemote || p === "win32" && s2.onRemoteWindows;
|
|
6156
6270
|
}
|
|
6157
6271
|
/**
|
|
6158
6272
|
* 设备回复 / 新建任务交出去之后调:开关、平台、id、用户在不在打字都过了才打开深链。永不抛。
|
|
6159
6273
|
* instructionId 只用来去重和记日志;不记会话内容
|
|
6160
6274
|
*/
|
|
6161
|
-
async focus(instructionId, t) {
|
|
6275
|
+
async focus(instructionId, t, override) {
|
|
6162
6276
|
if (this.#done.has(instructionId)) return "duplicate";
|
|
6163
|
-
const s2 = focusSettings(this.#cfg());
|
|
6164
|
-
if (!s2.onRemote) return "disabled";
|
|
6277
|
+
const s2 = focusSettings(this.#cfg(), override);
|
|
6165
6278
|
const p = this.#platform();
|
|
6166
|
-
if (p !== "darwin" &&
|
|
6279
|
+
if (p !== "darwin" && p !== "win32") return "platform";
|
|
6280
|
+
if (!(p === "win32" ? s2.onRemoteWindows : s2.onRemote)) return "disabled";
|
|
6167
6281
|
const url = focusUrl(t);
|
|
6168
6282
|
const name = TOOL_NAME[t.kind];
|
|
6169
6283
|
if (!url) {
|
|
@@ -6182,11 +6296,25 @@ var SessionFocuser = class {
|
|
|
6182
6296
|
this.#d.log(`\u6307\u4EE4 ${instructionId} \u5207\u5230 ${name} \u4F1A\u8BDD\u5931\u8D25\uFF1A\u672C\u673A\u6CA1\u6CE8\u518C ${focusScheme(t)}:// \u534F\u8BAE`);
|
|
6183
6297
|
return "protocol_missing";
|
|
6184
6298
|
}
|
|
6299
|
+
if (t.kind === "codex") {
|
|
6300
|
+
let running = null;
|
|
6301
|
+
try {
|
|
6302
|
+
running = await (this.#d.appRunning ?? ((n) => defaultAppRunning(n)))(WIN_PROCESS_NAMES.codex);
|
|
6303
|
+
} catch {
|
|
6304
|
+
running = null;
|
|
6305
|
+
}
|
|
6306
|
+
if (running === false) {
|
|
6307
|
+
this.#d.log(`\u6307\u4EE4 ${instructionId} \u4E0D\u5207\u5230 ${name} \u4F1A\u8BDD\uFF1AChatGPT App \u6CA1\u5F00\uFF0C\u4E0D\u5207`);
|
|
6308
|
+
return "app_not_running";
|
|
6309
|
+
}
|
|
6310
|
+
}
|
|
6185
6311
|
}
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6312
|
+
if (s2.idleMinMs > 0) {
|
|
6313
|
+
const idle = await this.#idle();
|
|
6314
|
+
if (idle === null || idle < s2.idleMinMs) {
|
|
6315
|
+
this.#d.log(`\u6307\u4EE4 ${instructionId} \u4E0D\u5207\u5230 ${name} \u4F1A\u8BDD\uFF1A${idleWhy(idle)}`);
|
|
6316
|
+
return "user_active";
|
|
6317
|
+
}
|
|
6190
6318
|
}
|
|
6191
6319
|
let ok = false;
|
|
6192
6320
|
try {
|
|
@@ -6195,8 +6323,36 @@ var SessionFocuser = class {
|
|
|
6195
6323
|
ok = false;
|
|
6196
6324
|
}
|
|
6197
6325
|
this.#d.log(ok ? `\u6307\u4EE4 ${instructionId} \u5DF2\u5207\u5230 ${name} \u4F1A\u8BDD\uFF08\u6DF1\u94FE\uFF09` : `\u6307\u4EE4 ${instructionId} \u5207\u5230 ${name} \u4F1A\u8BDD\u5931\u8D25\uFF1A\u6DF1\u94FE\u6CA1\u6253\u5F00`);
|
|
6326
|
+
if (ok && p === "win32") await this.#raise(instructionId, t, name);
|
|
6198
6327
|
return ok ? "opened" : "failed";
|
|
6199
6328
|
}
|
|
6329
|
+
/**
|
|
6330
|
+
* Windows:深链打开后约 1.5 秒把 App 窗口调到前台(前台锁下深链本身不一定能把窗口带上来)。失败只记日志,永不抛。
|
|
6331
|
+
* 🔴 同一时刻只跑一个:已有在等 / 在跑的,只记下最新目标(等待中被后来的覆盖),跑完再处理,不并发起多个 PowerShell(评审)
|
|
6332
|
+
*/
|
|
6333
|
+
async #raise(instructionId, t, name) {
|
|
6334
|
+
this.#raiseNext = { instructionId, t, name };
|
|
6335
|
+
if (this.#raiseBusy) return;
|
|
6336
|
+
this.#raiseBusy = true;
|
|
6337
|
+
try {
|
|
6338
|
+
while (this.#raiseNext) {
|
|
6339
|
+
let ok = false;
|
|
6340
|
+
let job = null;
|
|
6341
|
+
try {
|
|
6342
|
+
await (this.#d.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms))))(WIN_RAISE_DELAY_MS);
|
|
6343
|
+
job = this.#raiseNext;
|
|
6344
|
+
this.#raiseNext = null;
|
|
6345
|
+
if (!job) break;
|
|
6346
|
+
ok = await (this.#d.raiseWindow ?? ((n) => defaultRaiseWindow(n)))(WIN_PROCESS_NAMES[job.t.kind]);
|
|
6347
|
+
} catch {
|
|
6348
|
+
ok = false;
|
|
6349
|
+
}
|
|
6350
|
+
if (job && !ok) this.#d.log(`\u6307\u4EE4 ${job.instructionId} \u5DF2\u6253\u5F00 ${job.name} \u6DF1\u94FE\uFF0C\u4F46\u6CA1\u80FD\u628A\u7A97\u53E3\u8C03\u5230\u524D\u53F0`);
|
|
6351
|
+
}
|
|
6352
|
+
} finally {
|
|
6353
|
+
this.#raiseBusy = false;
|
|
6354
|
+
}
|
|
6355
|
+
}
|
|
6200
6356
|
/** 键鼠空闲毫秒(Windows 起一次 PowerShell);取不到 null。永不抛 */
|
|
6201
6357
|
async #idle() {
|
|
6202
6358
|
const p = this.#platform();
|
|
@@ -18985,17 +19141,17 @@ var Daemon = class {
|
|
|
18985
19141
|
#focusAfter(cmd, target) {
|
|
18986
19142
|
if (isSummary(cmd)) return;
|
|
18987
19143
|
try {
|
|
18988
|
-
if (!this.#deps.focuser.remoteEnabled()) return;
|
|
19144
|
+
if (!this.#deps.focuser.remoteEnabled(cmd.focus)) return;
|
|
18989
19145
|
} catch {
|
|
18990
19146
|
return;
|
|
18991
19147
|
}
|
|
18992
|
-
void Promise.resolve().then(target).then((t) => t ? this.#deps.focuser.focus(cmd.instructionId, t) : null).catch(() => {
|
|
19148
|
+
void Promise.resolve().then(target).then((t) => t ? this.#deps.focuser.focus(cmd.instructionId, t, cmd.focus) : null).catch(() => {
|
|
18993
19149
|
});
|
|
18994
19150
|
}
|
|
18995
|
-
/** opencode 这类 App
|
|
19151
|
+
/** opencode 这类 App 内切会话(不激活):开关开着且用户没在用电脑(开关 / 门槛先看指令帧带的 App 设置) */
|
|
18996
19152
|
#focusInApp(cmd) {
|
|
18997
19153
|
try {
|
|
18998
|
-
return this.#deps.focuser.inAppReady(cmd.instructionId);
|
|
19154
|
+
return this.#deps.focuser.inAppReady(cmd.instructionId, cmd.focus);
|
|
18999
19155
|
} catch {
|
|
19000
19156
|
return false;
|
|
19001
19157
|
}
|
|
@@ -22768,8 +22924,11 @@ var USAGE2 = `deskbuddy \u2014\u2014 \u628A AI \u7F16\u7801\u667A\u80FD\u4F53\u6
|
|
|
22768
22924
|
deskbuddy logout [--yes] \u79FB\u9664 hook \u5E76\u6E05\u9664\u672C\u673A\u6570\u636E
|
|
22769
22925
|
deskbuddy config set transcript on|off \u4F1A\u8BDD\u5BF9\u8BDD\u540C\u6B65\u7684\u672C\u5730\u5F00\u5173\uFF08\u9ED8\u8BA4 on\uFF1Boff \u540E\u4E0D\u4E0A\u4F20\u4EFB\u4F55\u5BF9\u8BDD\u6B63\u6587\uFF09
|
|
22770
22926
|
deskbuddy config set <\u5F00\u5173> true|false \u8BBE\u5907\u56DE\u590D / \u65B0\u5EFA\u4EFB\u52A1\u540E\u5207\u5230\u8BE5\u4F1A\u8BDD\uFF1AfocusSessionOnRemote\uFF08\u6DF1\u94FE\u628A
|
|
22771
|
-
App \u8C03\u5230\u524D\u53F0\uFF0C\u9ED8\u8BA4\
|
|
22772
|
-
focusSessionOnRemoteWindows\uFF08Windows \
|
|
22927
|
+
App \u8C03\u5230\u524D\u53F0\uFF0C\u9ED8\u8BA4\u5F00\uFF09\u3001focusSessionInApp\uFF08opencode \u5185\u5207\u6362\uFF0C\u9ED8\u8BA4\u5F00\uFF09\u3001
|
|
22928
|
+
focusSessionOnRemoteWindows\uFF08Windows \u5355\u72EC\u8BBE\uFF0C\u4E0D\u8BBE = \u8DDF\u968F
|
|
22929
|
+
focusSessionOnRemote\uFF1Bauto \u6062\u590D\u8DDF\u968F\uFF09
|
|
22930
|
+
deskbuddy config set focusIdleSeconds <0\uFF5E600|default> \u5207\u4F1A\u8BDD\u524D\u7684\u952E\u9F20\u7A7A\u95F2\u95E8\u69DB\u79D2\u6570\uFF08\u9ED8\u8BA4 5\uFF1B0 = \u4E0D\u770B\u952E\u9F20\uFF09
|
|
22931
|
+
App \u5BF9\u8FD9\u53F0\u7535\u8111 / \u8BBE\u5907\u8BBE\u8FC7\u5F00\u5173\u6216\u95E8\u69DB\u65F6\uFF0C\u4EE5 App \u4E3A\u51C6
|
|
22773
22932
|
deskbuddy workbuddy-setup [--yes] \u5B89\u88C5 WorkBuddy \u7684\u72EC\u7ACB\u7248\u547D\u4EE4\u884C\uFF08CodeBuddy Code\uFF09\uFF0C\u88C5\u4E86\u624D\u80FD\u5728
|
|
22774
22933
|
\u8BBE\u5907\u4E0A\u65B0\u5EFA / \u56DE\u590D WorkBuddy \u4EFB\u52A1\uFF08--yes \u4E0D\u5F39\u6846\u76F4\u63A5\u88C5\uFF09
|
|
22775
22934
|
deskbuddy wrap <tool> [\u53C2\u6570\u2026] \uFF08\u57AB\u7247\u5185\u90E8\u7528\uFF09\u7ECF DeskBuddy \u6253\u5F00\u667A\u80FD\u4F53\u7A97\u53E3\uFF0C
|
package/dist/hook.cjs
CHANGED
|
@@ -31,11 +31,22 @@ const SUMMARY_TOOLS = ['claude_code', 'codex', 'qoder', 'qoder_cn', 'opencode',
|
|
|
31
31
|
// transcript:<tool> 与 src/core/transcriptTools.ts 的 TRANSCRIPT_TOOLS 同一份(有对齐用例)
|
|
32
32
|
const TRANSCRIPT_TOOLS = ['claude_code', 'codex', 'qoder', 'qoder_cn', 'workbuddy', 'qwenwork', 'opencode', 'dumate', 'deepseek_harness']
|
|
33
33
|
const TRANSCRIPT_CAPS = ['transcript'].concat(TRANSCRIPT_TOOLS.map((t) => 'transcript:' + t))
|
|
34
|
-
const CAPS = ['cmd_result', 'window_inject', 'resume_ended', 'reply_idle_window', 'turns', 'summary'].concat(SUMMARY_TOOLS.map((t) => 'summary:' + t), TRANSCRIPT_CAPS)
|
|
34
|
+
const CAPS = ['cmd_result', 'window_inject', 'resume_ended', 'reply_idle_window', 'turns', 'summary', 'focus_cfg'].concat(SUMMARY_TOOLS.map((t) => 'summary:' + t), TRANSCRIPT_CAPS)
|
|
35
35
|
/** 本地硬开关 transcript=false 时不声明 transcript caps(与 src/core/snapshot.ts 的 capsFor 一致) */
|
|
36
36
|
function capsFor(cfg) {
|
|
37
37
|
return cfg && cfg.transcript === false ? CAPS.filter((c) => TRANSCRIPT_CAPS.indexOf(c) === -1) : CAPS.slice()
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
/** 本机显式写过的切会话设置,没写的项不带(spec 2026-10-13 focus-remote-config §2.4;与 src/core/snapshot.ts 的 focusLocalFor 一致) */
|
|
41
|
+
function focusLocalFor(cfg) {
|
|
42
|
+
const o = {}
|
|
43
|
+
if (!cfg) return o
|
|
44
|
+
if (typeof cfg.focusSessionOnRemote === 'boolean') o.enabled = cfg.focusSessionOnRemote
|
|
45
|
+
const n = cfg.focusIdleSeconds
|
|
46
|
+
if (typeof n === 'number' && Number.isInteger(n) && n >= 0 && n <= 600) o.idleSeconds = n
|
|
47
|
+
if (typeof cfg.focusSessionOnRemoteWindows === 'boolean') o.windowsEnabled = cfg.focusSessionOnRemoteWindows
|
|
48
|
+
return o
|
|
49
|
+
}
|
|
39
50
|
/** 状态文件里保留的最近事件条数,与 src/core/state.ts 的 RECENT_MAX 一致 */
|
|
40
51
|
const RECENT_MAX = 8
|
|
41
52
|
const REPORT_TIMEOUT_MS = 2000
|
|
@@ -285,6 +296,10 @@ function loadConfig() {
|
|
|
285
296
|
apiBase: process.env.DESKBUDDY_API_BASE || parsed.apiBase || DEFAULT_API_BASE,
|
|
286
297
|
// 本地硬开关(spec 2026-10-11 transcript-sync §5):只有显式 false 才关
|
|
287
298
|
transcript: parsed.transcript === false ? false : undefined,
|
|
299
|
+
// 快照带的 focusLocal 用(与 src/core/config.ts 同样只认合法值)
|
|
300
|
+
focusSessionOnRemote: typeof parsed.focusSessionOnRemote === 'boolean' ? parsed.focusSessionOnRemote : undefined,
|
|
301
|
+
focusSessionOnRemoteWindows: typeof parsed.focusSessionOnRemoteWindows === 'boolean' ? parsed.focusSessionOnRemoteWindows : undefined,
|
|
302
|
+
focusIdleSeconds: typeof parsed.focusIdleSeconds === 'number' ? parsed.focusIdleSeconds : undefined,
|
|
288
303
|
}
|
|
289
304
|
} catch (e) {
|
|
290
305
|
return fallback
|
|
@@ -1219,7 +1234,7 @@ function buildSnapshot(states, tool, cfg, nowMs, remote) {
|
|
|
1219
1234
|
if (turnsOf(s) !== undefined) o.turns = s.turns
|
|
1220
1235
|
return o
|
|
1221
1236
|
})
|
|
1222
|
-
return { clientId: cfg.clientId, hostName: cfg.hostName, tool: tool, snapshotAt: nowMs, sessions: sessions, caps: capsFor(cfg) }
|
|
1237
|
+
return { clientId: cfg.clientId, hostName: cfg.hostName, tool: tool, snapshotAt: nowMs, sessions: sessions, caps: capsFor(cfg), focusLocal: focusLocalFor(cfg) }
|
|
1223
1238
|
}
|
|
1224
1239
|
|
|
1225
1240
|
// ---------- 上报 ----------
|
|
@@ -3013,6 +3028,7 @@ module.exports = {
|
|
|
3013
3028
|
parseWinProcJson,
|
|
3014
3029
|
CAPS,
|
|
3015
3030
|
capsFor,
|
|
3031
|
+
focusLocalFor,
|
|
3016
3032
|
turnEvents,
|
|
3017
3033
|
turnsOf,
|
|
3018
3034
|
setTurns,
|
package/hook/hook-debug.log
CHANGED
|
@@ -548,3 +548,33 @@
|
|
|
548
548
|
2026-10-13T10:34:18.075Z [opencode] opencode 版本 读不出:带说明的拒绝不走新路由
|
|
549
549
|
2026-10-13T10:34:18.075Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
|
|
550
550
|
2026-10-13T10:34:22.944Z [opencode] 后台任务里的提问 que_r2 没法在设备上回答(后台任务不能提问,请在电脑上继续),已拒绝
|
|
551
|
+
2026-10-13T12:11:57.052Z [opencode] 回填选择题失败:/question/{requestID}/reply status=404
|
|
552
|
+
2026-10-13T12:12:02.307Z [opencode] opencode 版本 1.4.3:带说明的拒绝不走新路由
|
|
553
|
+
2026-10-13T12:12:02.307Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
|
|
554
|
+
2026-10-13T12:12:02.419Z [opencode] opencode 版本 读不出:带说明的拒绝不走新路由
|
|
555
|
+
2026-10-13T12:12:02.419Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
|
|
556
|
+
2026-10-13T12:12:06.051Z [opencode] 后台任务里的提问 que_r2 没法在设备上回答(后台任务不能提问,请在电脑上继续),已拒绝
|
|
557
|
+
2026-10-13T12:46:22.157Z [opencode] 回填选择题失败:/question/{requestID}/reply status=404
|
|
558
|
+
2026-10-13T12:46:27.749Z [opencode] opencode 版本 1.4.3:带说明的拒绝不走新路由
|
|
559
|
+
2026-10-13T12:46:27.749Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
|
|
560
|
+
2026-10-13T12:46:27.968Z [opencode] opencode 版本 读不出:带说明的拒绝不走新路由
|
|
561
|
+
2026-10-13T12:46:27.968Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
|
|
562
|
+
2026-10-13T12:46:40.616Z [opencode] 后台任务里的提问 que_r2 没法在设备上回答(后台任务不能提问,请在电脑上继续),已拒绝
|
|
563
|
+
2026-10-13T12:55:40.939Z [opencode] 回填选择题失败:/question/{requestID}/reply status=404
|
|
564
|
+
2026-10-13T12:55:47.599Z [opencode] opencode 版本 1.4.3:带说明的拒绝不走新路由
|
|
565
|
+
2026-10-13T12:55:47.599Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
|
|
566
|
+
2026-10-13T12:55:47.850Z [opencode] opencode 版本 读不出:带说明的拒绝不走新路由
|
|
567
|
+
2026-10-13T12:55:47.850Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
|
|
568
|
+
2026-10-13T12:55:58.486Z [opencode] 后台任务里的提问 que_r2 没法在设备上回答(后台任务不能提问,请在电脑上继续),已拒绝
|
|
569
|
+
2026-10-13T13:13:26.197Z [opencode] 回填选择题失败:/question/{requestID}/reply status=404
|
|
570
|
+
2026-10-13T13:13:33.512Z [opencode] opencode 版本 1.4.3:带说明的拒绝不走新路由
|
|
571
|
+
2026-10-13T13:13:33.512Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
|
|
572
|
+
2026-10-13T13:13:34.084Z [opencode] opencode 版本 读不出:带说明的拒绝不走新路由
|
|
573
|
+
2026-10-13T13:13:34.085Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
|
|
574
|
+
2026-10-13T13:13:44.931Z [opencode] 后台任务里的提问 que_r2 没法在设备上回答(后台任务不能提问,请在电脑上继续),已拒绝
|
|
575
|
+
2026-10-13T13:17:21.262Z [opencode] 回填选择题失败:/question/{requestID}/reply status=404
|
|
576
|
+
2026-10-13T13:17:26.871Z [opencode] opencode 版本 1.4.3:带说明的拒绝不走新路由
|
|
577
|
+
2026-10-13T13:17:26.871Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
|
|
578
|
+
2026-10-13T13:17:27.063Z [opencode] opencode 版本 读不出:带说明的拒绝不走新路由
|
|
579
|
+
2026-10-13T13:17:27.063Z [opencode] opencode 版本不确定支持带说明的拒绝,设备上的反馈没有带给模型
|
|
580
|
+
2026-10-13T13:17:30.209Z [opencode] 后台任务里的提问 que_r2 没法在设备上回答(后台任务不能提问,请在电脑上继续),已拒绝
|
package/hook/hook.cjs
CHANGED
|
@@ -31,11 +31,22 @@ const SUMMARY_TOOLS = ['claude_code', 'codex', 'qoder', 'qoder_cn', 'opencode',
|
|
|
31
31
|
// transcript:<tool> 与 src/core/transcriptTools.ts 的 TRANSCRIPT_TOOLS 同一份(有对齐用例)
|
|
32
32
|
const TRANSCRIPT_TOOLS = ['claude_code', 'codex', 'qoder', 'qoder_cn', 'workbuddy', 'qwenwork', 'opencode', 'dumate', 'deepseek_harness']
|
|
33
33
|
const TRANSCRIPT_CAPS = ['transcript'].concat(TRANSCRIPT_TOOLS.map((t) => 'transcript:' + t))
|
|
34
|
-
const CAPS = ['cmd_result', 'window_inject', 'resume_ended', 'reply_idle_window', 'turns', 'summary'].concat(SUMMARY_TOOLS.map((t) => 'summary:' + t), TRANSCRIPT_CAPS)
|
|
34
|
+
const CAPS = ['cmd_result', 'window_inject', 'resume_ended', 'reply_idle_window', 'turns', 'summary', 'focus_cfg'].concat(SUMMARY_TOOLS.map((t) => 'summary:' + t), TRANSCRIPT_CAPS)
|
|
35
35
|
/** 本地硬开关 transcript=false 时不声明 transcript caps(与 src/core/snapshot.ts 的 capsFor 一致) */
|
|
36
36
|
function capsFor(cfg) {
|
|
37
37
|
return cfg && cfg.transcript === false ? CAPS.filter((c) => TRANSCRIPT_CAPS.indexOf(c) === -1) : CAPS.slice()
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
/** 本机显式写过的切会话设置,没写的项不带(spec 2026-10-13 focus-remote-config §2.4;与 src/core/snapshot.ts 的 focusLocalFor 一致) */
|
|
41
|
+
function focusLocalFor(cfg) {
|
|
42
|
+
const o = {}
|
|
43
|
+
if (!cfg) return o
|
|
44
|
+
if (typeof cfg.focusSessionOnRemote === 'boolean') o.enabled = cfg.focusSessionOnRemote
|
|
45
|
+
const n = cfg.focusIdleSeconds
|
|
46
|
+
if (typeof n === 'number' && Number.isInteger(n) && n >= 0 && n <= 600) o.idleSeconds = n
|
|
47
|
+
if (typeof cfg.focusSessionOnRemoteWindows === 'boolean') o.windowsEnabled = cfg.focusSessionOnRemoteWindows
|
|
48
|
+
return o
|
|
49
|
+
}
|
|
39
50
|
/** 状态文件里保留的最近事件条数,与 src/core/state.ts 的 RECENT_MAX 一致 */
|
|
40
51
|
const RECENT_MAX = 8
|
|
41
52
|
const REPORT_TIMEOUT_MS = 2000
|
|
@@ -285,6 +296,10 @@ function loadConfig() {
|
|
|
285
296
|
apiBase: process.env.DESKBUDDY_API_BASE || parsed.apiBase || DEFAULT_API_BASE,
|
|
286
297
|
// 本地硬开关(spec 2026-10-11 transcript-sync §5):只有显式 false 才关
|
|
287
298
|
transcript: parsed.transcript === false ? false : undefined,
|
|
299
|
+
// 快照带的 focusLocal 用(与 src/core/config.ts 同样只认合法值)
|
|
300
|
+
focusSessionOnRemote: typeof parsed.focusSessionOnRemote === 'boolean' ? parsed.focusSessionOnRemote : undefined,
|
|
301
|
+
focusSessionOnRemoteWindows: typeof parsed.focusSessionOnRemoteWindows === 'boolean' ? parsed.focusSessionOnRemoteWindows : undefined,
|
|
302
|
+
focusIdleSeconds: typeof parsed.focusIdleSeconds === 'number' ? parsed.focusIdleSeconds : undefined,
|
|
288
303
|
}
|
|
289
304
|
} catch (e) {
|
|
290
305
|
return fallback
|
|
@@ -1219,7 +1234,7 @@ function buildSnapshot(states, tool, cfg, nowMs, remote) {
|
|
|
1219
1234
|
if (turnsOf(s) !== undefined) o.turns = s.turns
|
|
1220
1235
|
return o
|
|
1221
1236
|
})
|
|
1222
|
-
return { clientId: cfg.clientId, hostName: cfg.hostName, tool: tool, snapshotAt: nowMs, sessions: sessions, caps: capsFor(cfg) }
|
|
1237
|
+
return { clientId: cfg.clientId, hostName: cfg.hostName, tool: tool, snapshotAt: nowMs, sessions: sessions, caps: capsFor(cfg), focusLocal: focusLocalFor(cfg) }
|
|
1223
1238
|
}
|
|
1224
1239
|
|
|
1225
1240
|
// ---------- 上报 ----------
|
|
@@ -3013,6 +3028,7 @@ module.exports = {
|
|
|
3013
3028
|
parseWinProcJson,
|
|
3014
3029
|
CAPS,
|
|
3015
3030
|
capsFor,
|
|
3031
|
+
focusLocalFor,
|
|
3016
3032
|
turnEvents,
|
|
3017
3033
|
turnsOf,
|
|
3018
3034
|
setTurns,
|