@yfwu2020/dsh-selection-explain 0.12.0 → 0.13.1

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 (86) hide show
  1. package/README.md +18 -5
  2. package/lib/chat-context.js +6 -4
  3. package/lib/chat-context.js.map +1 -1
  4. package/lib/client.js +11810 -10118
  5. package/lib/client.js.map +4 -4
  6. package/lib/config-values.js +5 -1
  7. package/lib/config-values.js.map +1 -1
  8. package/lib/host/analyze-conclusion.js +115 -0
  9. package/lib/host/analyze-conclusion.js.map +1 -0
  10. package/lib/host/analyze-execution-types.js +2 -0
  11. package/lib/host/analyze-execution-types.js.map +1 -0
  12. package/lib/host/analyze-execution.js +166 -0
  13. package/lib/host/analyze-execution.js.map +1 -0
  14. package/lib/host/analyze-history.js +110 -0
  15. package/lib/host/analyze-history.js.map +1 -0
  16. package/lib/host/analyze-messages.js +95 -0
  17. package/lib/host/analyze-messages.js.map +1 -0
  18. package/lib/host/analyze-output.js +69 -0
  19. package/lib/host/analyze-output.js.map +1 -0
  20. package/lib/host/analyze-prompt.js +110 -0
  21. package/lib/host/analyze-prompt.js.map +1 -0
  22. package/lib/host/analyze-request.js +129 -0
  23. package/lib/host/analyze-request.js.map +1 -0
  24. package/lib/host/analyze-round.js +121 -0
  25. package/lib/host/analyze-round.js.map +1 -0
  26. package/lib/host/analyze-tool-execution.js +98 -0
  27. package/lib/host/analyze-tool-execution.js.map +1 -0
  28. package/lib/host/analyze-tools.js +79 -0
  29. package/lib/host/analyze-tools.js.map +1 -0
  30. package/lib/host/analyze-types.js +2 -0
  31. package/lib/host/analyze-types.js.map +1 -0
  32. package/lib/host/analyze.js +30 -926
  33. package/lib/host/analyze.js.map +1 -1
  34. package/lib/host/chat-context.js +8 -3
  35. package/lib/host/chat-context.js.map +1 -1
  36. package/lib/host/config.js +6 -0
  37. package/lib/host/config.js.map +1 -1
  38. package/lib/host/history-store.js +18 -6
  39. package/lib/host/history-store.js.map +1 -1
  40. package/lib/host/native-probe.js +66 -0
  41. package/lib/host/native-probe.js.map +1 -0
  42. package/lib/host/plugin.js +30 -0
  43. package/lib/host/plugin.js.map +1 -1
  44. package/lib/host/shot-capture.js +322 -0
  45. package/lib/host/shot-capture.js.map +1 -0
  46. package/lib/host/shot-geometry.js +183 -0
  47. package/lib/host/shot-geometry.js.map +1 -0
  48. package/lib/host/shot-platform.js +208 -0
  49. package/lib/host/shot-platform.js.map +1 -0
  50. package/lib/host/shot-service.js +127 -0
  51. package/lib/host/shot-service.js.map +1 -0
  52. package/lib/host/shot-types.js +5 -0
  53. package/lib/host/shot-types.js.map +1 -0
  54. package/lib/host/shot.js +136 -0
  55. package/lib/host/shot.js.map +1 -0
  56. package/lib/prompts.js +54 -1
  57. package/lib/prompts.js.map +1 -1
  58. package/lib/settings.js +13 -2
  59. package/lib/settings.js.map +1 -1
  60. package/lib/types/chat-context.d.ts +2 -2
  61. package/lib/types/config-values.d.ts +24 -0
  62. package/lib/types/host/analyze-conclusion.d.ts +3 -0
  63. package/lib/types/host/analyze-execution-types.d.ts +54 -0
  64. package/lib/types/host/analyze-execution.d.ts +3 -0
  65. package/lib/types/host/analyze-history.d.ts +41 -0
  66. package/lib/types/host/analyze-messages.d.ts +15 -0
  67. package/lib/types/host/analyze-output.d.ts +8 -0
  68. package/lib/types/host/analyze-prompt.d.ts +15 -0
  69. package/lib/types/host/analyze-request.d.ts +28 -0
  70. package/lib/types/host/analyze-round.d.ts +7 -0
  71. package/lib/types/host/analyze-tool-execution.d.ts +4 -0
  72. package/lib/types/host/analyze-tools.d.ts +15 -0
  73. package/lib/types/host/analyze-types.d.ts +13 -0
  74. package/lib/types/host/analyze.d.ts +4 -4
  75. package/lib/types/host/config.d.ts +12 -0
  76. package/lib/types/host/native-probe.d.ts +10 -0
  77. package/lib/types/host/shot-capture.d.ts +11 -0
  78. package/lib/types/host/shot-geometry.d.ts +37 -0
  79. package/lib/types/host/shot-platform.d.ts +11 -0
  80. package/lib/types/host/shot-service.d.ts +17 -0
  81. package/lib/types/host/shot-types.d.ts +105 -0
  82. package/lib/types/host/shot.d.ts +16 -0
  83. package/lib/types/host/types.d.ts +13 -0
  84. package/lib/types/prompts.d.ts +21 -0
  85. package/native/window-list.c +139 -0
  86. package/package.json +5 -2
@@ -0,0 +1,208 @@
1
+ /** macOS commands, native helper recovery, and private temporary files. */
2
+ import { spawn } from 'node:child_process';
3
+ import { access, chmod, mkdtemp, readFile, readdir, rm, stat } from 'node:fs/promises';
4
+ import { homedir, platform, tmpdir } from 'node:os';
5
+ import { dirname, join } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { resolveNativeProbe } from './native-probe.js';
8
+ import { parsePngSize, parseRectXattr } from './shot-geometry.js';
9
+ const unref = (timer) => timer.unref();
10
+ /** Every external command, including helper compilation, observes the service's abort signal. */
11
+ export function createCommandRunner(launch = spawn) {
12
+ return (command, args, signal, timeout = 10_000, env) => new Promise(resolve => {
13
+ const result = { stdout: '', stderr: '', code: null, signal: null, timedOut: false, failed: false };
14
+ if (signal.aborted) {
15
+ resolve({ ...result, failed: true });
16
+ return;
17
+ }
18
+ let child;
19
+ try {
20
+ child = launch(command, args, { stdio: ['ignore', 'pipe', 'pipe'], env });
21
+ }
22
+ catch {
23
+ resolve({ ...result, failed: true });
24
+ return;
25
+ }
26
+ let settled = false;
27
+ let escalation;
28
+ let fallback;
29
+ const finish = () => {
30
+ if (settled)
31
+ return;
32
+ settled = true;
33
+ clearTimeout(timer);
34
+ clearTimeout(escalation);
35
+ clearTimeout(fallback);
36
+ signal.removeEventListener('abort', abort);
37
+ resolve({ ...result });
38
+ };
39
+ const terminate = () => {
40
+ if (settled || escalation)
41
+ return;
42
+ result.failed = true;
43
+ try {
44
+ child.kill('SIGTERM');
45
+ }
46
+ catch { /* Already gone. */ }
47
+ if (settled)
48
+ return;
49
+ escalation = setTimeout(() => {
50
+ try {
51
+ child.kill('SIGKILL');
52
+ }
53
+ catch { /* Already gone. */ }
54
+ if (settled)
55
+ return;
56
+ fallback = setTimeout(finish, 250);
57
+ unref(fallback);
58
+ }, 250);
59
+ unref(escalation);
60
+ };
61
+ const abort = () => terminate();
62
+ const timer = setTimeout(() => { result.timedOut = true; terminate(); }, timeout);
63
+ unref(timer);
64
+ signal.addEventListener('abort', abort, { once: true });
65
+ child.stdout?.on('data', chunk => {
66
+ if (settled)
67
+ return;
68
+ if (result.stdout.length + String(chunk).length > 8 * 1024 * 1024)
69
+ terminate();
70
+ else
71
+ result.stdout += String(chunk);
72
+ });
73
+ child.stderr?.on('data', chunk => { if (!settled && result.stderr.length < 4000)
74
+ result.stderr += String(chunk).slice(0, 4000 - result.stderr.length); });
75
+ child.once('error', () => { result.failed = true; finish(); });
76
+ child.once('close', (code, exitSignal) => {
77
+ if (settled)
78
+ return;
79
+ result.code = code;
80
+ result.signal = exitSignal;
81
+ result.failed ||= code !== 0 || !!exitSignal;
82
+ finish();
83
+ });
84
+ // Also cover an abort delivered synchronously by an injected launcher.
85
+ if (signal.aborted)
86
+ abort();
87
+ });
88
+ }
89
+ export function shotSupported() { return platform() === 'darwin'; }
90
+ export function resolveWindowProbe() {
91
+ return join(dirname(fileURLToPath(import.meta.url)), '..', 'native', 'window-list');
92
+ }
93
+ export function createMacShotPlatform(execute = createCommandRunner()) {
94
+ const commands = new Set();
95
+ const run = (...args) => {
96
+ const pending = execute(...args);
97
+ commands.add(pending);
98
+ void pending.then(() => commands.delete(pending), () => commands.delete(pending));
99
+ return pending;
100
+ };
101
+ let windowProbePath = '';
102
+ let checked = false;
103
+ let probing;
104
+ const fileExists = (path) => stat(path).then(info => info.isFile() && info.size > 0).catch(() => false);
105
+ const probeAvailable = (signal) => {
106
+ if (signal.aborted || !shotSupported())
107
+ return Promise.resolve(false);
108
+ if (checked)
109
+ return Promise.resolve(!!windowProbePath);
110
+ if (probing)
111
+ return probing;
112
+ probing = resolveNativeProbe({
113
+ name: 'window-list',
114
+ packagedPath: resolveWindowProbe(),
115
+ sourcePath: fileURLToPath(new URL('../../native/window-list.c', import.meta.url)),
116
+ cacheDir: join(process.env.DSH_HOME ?? join(homedir(), '.dsh'), 'selection-explain'),
117
+ frameworks: ['CoreFoundation', 'CoreGraphics'],
118
+ compile: async (source, output, frameworks) => {
119
+ const flags = ['-O2', ...frameworks.flatMap(name => ['-framework', name]), source, '-o', output];
120
+ const xcode = '/Applications/Xcode.app/Contents/Developer';
121
+ const compiler = join(xcode, 'Toolchains/XcodeDefault.xctoolchain/usr/bin/clang');
122
+ try {
123
+ await access(compiler);
124
+ const env = { ...process.env, DEVELOPER_DIR: xcode };
125
+ const sdk = await run('xcrun', ['--sdk', 'macosx', '--show-sdk-path'], signal, 15000, env);
126
+ if (!sdk.failed && sdk.stdout.trim()) {
127
+ const built = await run(compiler, ['-isysroot', sdk.stdout.trim(), ...flags], signal, 60000, env);
128
+ if (!built.failed && await fileExists(output) && !signal.aborted)
129
+ return true;
130
+ }
131
+ }
132
+ catch { /* Xcode is optional. */ }
133
+ for (const [command, args] of [['clang', flags], ['xcrun', ['clang', ...flags]]]) {
134
+ if (signal.aborted)
135
+ return false;
136
+ if (!(await run(command, [...args], signal, 60000)).failed && await fileExists(output) && !signal.aborted)
137
+ return true;
138
+ }
139
+ return false;
140
+ },
141
+ }).then(path => {
142
+ if (signal.aborted)
143
+ return false;
144
+ windowProbePath = path;
145
+ checked = true;
146
+ return !!path;
147
+ }).finally(() => { probing = undefined; });
148
+ return probing;
149
+ };
150
+ const probeJson = async (args, signal) => {
151
+ if (!await probeAvailable(signal) || signal.aborted)
152
+ return [];
153
+ const result = await run(windowProbePath, args, signal);
154
+ if (result.failed || signal.aborted)
155
+ return [];
156
+ try {
157
+ const items = JSON.parse(result.stdout);
158
+ return Array.isArray(items) ? items : [];
159
+ }
160
+ catch {
161
+ return [];
162
+ }
163
+ };
164
+ const pngSize = async (path) => { try {
165
+ return parsePngSize(await readFile(path));
166
+ }
167
+ catch {
168
+ return null;
169
+ } };
170
+ return {
171
+ supported: shotSupported, probeAvailable, run, fileExists, readFile, pngSize,
172
+ async settle() {
173
+ await probing?.catch(() => { });
174
+ while (commands.size)
175
+ await Promise.allSettled([...commands]);
176
+ },
177
+ async createTempDir() {
178
+ const dir = await mkdtemp(join(tmpdir(), 'dsh-selection-explain-'));
179
+ await chmod(dir, 0o700);
180
+ return dir;
181
+ },
182
+ async remove(path, recursive = false) { await rm(path, { recursive, force: true }).catch(() => { }); },
183
+ async prune(dir, seq) {
184
+ const names = await readdir(dir).catch(() => []);
185
+ await Promise.all(names.filter(name => {
186
+ const match = /^(?:region|shot|display)-(\d+)\.png$/.exec(name);
187
+ return match && Number(match[1]) <= seq - 3;
188
+ }).map(name => rm(join(dir, name), { force: true }).catch(() => { })));
189
+ },
190
+ async rectFromXattr(path, signal) {
191
+ const result = await run('/usr/bin/xattr', ['-px', 'com.apple.metadata:kMDItemScreenCaptureGlobalRect', path], signal);
192
+ return result.failed || signal.aborted ? null : parseRectXattr(result.stdout);
193
+ },
194
+ async listWindows(signal) {
195
+ return (await probeJson([], signal)).filter((item) => {
196
+ const w = item;
197
+ return !!w && [w.id, w.x, w.y, w.w, w.h, w.layer].every(Number.isFinite) && w.w > 0 && w.h > 0 && typeof w.owner === 'string';
198
+ });
199
+ },
200
+ async listDisplays(signal) {
201
+ return (await probeJson(['--displays'], signal)).filter((item) => {
202
+ const d = item;
203
+ return !!d && [d.x, d.y, d.w, d.h].every(Number.isFinite) && Number(d.w) > 0 && Number(d.h) > 0;
204
+ }).map(d => ({ id: Number(d.id) || 0, x: Number(d.x), y: Number(d.y), w: Number(d.w), h: Number(d.h), index: Number(d.index) || 0, main: d.main === true || Number(d.main) === 1 }));
205
+ },
206
+ };
207
+ }
208
+ //# sourceMappingURL=shot-platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shot-platform.js","sourceRoot":"","sources":["../../src/host/shot-platform.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE1C,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AACtF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAIjE,MAAM,KAAK,GAAG,CAAC,KAAqB,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;AAEtD,iGAAiG;AACjG,MAAM,UAAU,mBAAmB,CAAC,SAAuB,KAAK;IAC9D,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;QAC7E,MAAM,MAAM,GAAkB,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;QAClH,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YAAC,OAAO,CAAC,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YAAC,OAAM;QAAC,CAAC;QACpE,IAAI,KAAmB,CAAA;QACvB,IAAI,CAAC;YAAC,KAAK,GAAG,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAA;QAAC,CAAC;QACjF,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YAAC,OAAM;QAAC,CAAC;QACtD,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,IAAI,UAAsC,CAAA;QAC1C,IAAI,QAAoC,CAAA;QACxC,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,IAAI,OAAO;gBAAE,OAAM;YACnB,OAAO,GAAG,IAAI,CAAA;YACd,YAAY,CAAC,KAAK,CAAC,CAAA;YACnB,YAAY,CAAC,UAAU,CAAC,CAAA;YACxB,YAAY,CAAC,QAAQ,CAAC,CAAA;YACtB,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;YAC1C,OAAO,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC,CAAA;QACxB,CAAC,CAAA;QACD,MAAM,SAAS,GAAG,GAAG,EAAE;YACrB,IAAI,OAAO,IAAI,UAAU;gBAAE,OAAM;YACjC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAA;YACpB,IAAI,CAAC;gBAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,CAAC;YAC3D,IAAI,OAAO;gBAAE,OAAM;YACnB,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC3B,IAAI,CAAC;oBAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,CAAC;gBAC3D,IAAI,OAAO;oBAAE,OAAM;gBACnB,QAAQ,GAAG,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;gBAClC,KAAK,CAAC,QAAQ,CAAC,CAAA;YACjB,CAAC,EAAE,GAAG,CAAC,CAAA;YACP,KAAK,CAAC,UAAU,CAAC,CAAA;QACnB,CAAC,CAAA;QACD,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,SAAS,EAAE,CAAA;QAC/B,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,SAAS,EAAE,CAAA,CAAC,CAAC,EAAE,OAAO,CAAC,CAAA;QAChF,KAAK,CAAC,KAAK,CAAC,CAAA;QACZ,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACvD,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE;YAC/B,IAAI,OAAO;gBAAE,OAAM;YACnB,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI;gBAAE,SAAS,EAAE,CAAA;;gBACzE,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,CAAC,CAAA;QACrC,CAAC,CAAC,CAAA;QACF,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI;YAAE,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA,CAAC,CAAC,CAAC,CAAA;QACxJ,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,MAAM,EAAE,CAAA,CAAC,CAAC,CAAC,CAAA;QAC7D,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE;YACvC,IAAI,OAAO;gBAAE,OAAM;YACnB,MAAM,CAAC,IAAI,GAAG,IAAI,CAAA;YAClB,MAAM,CAAC,MAAM,GAAG,UAAU,CAAA;YAC1B,MAAM,CAAC,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,UAAU,CAAA;YAC5C,MAAM,EAAE,CAAA;QACV,CAAC,CAAC,CAAA;QACF,uEAAuE;QACvE,IAAI,MAAM,CAAC,OAAO;YAAE,KAAK,EAAE,CAAA;IAC7B,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,KAAc,OAAO,QAAQ,EAAE,KAAK,QAAQ,CAAA,CAAC,CAAC;AAC3E,MAAM,UAAU,kBAAkB;IAChC,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAA;AACrF,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,UAAyB,mBAAmB,EAAE;IAClF,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA0B,CAAA;IAClD,MAAM,GAAG,GAAkB,CAAC,GAAG,IAAI,EAAE,EAAE;QACrC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;QAChC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QACrB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;QACjF,OAAO,OAAO,CAAA;IAChB,CAAC,CAAA;IACD,IAAI,eAAe,GAAG,EAAE,CAAA;IACxB,IAAI,OAAO,GAAG,KAAK,CAAA;IACnB,IAAI,OAAqC,CAAA;IACzC,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAA;IAC/G,MAAM,cAAc,GAAG,CAAC,MAAmB,EAAoB,EAAE;QAC/D,IAAI,MAAM,CAAC,OAAO,IAAI,CAAC,aAAa,EAAE;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QACrE,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAA;QACtD,IAAI,OAAO;YAAE,OAAO,OAAO,CAAA;QAC3B,OAAO,GAAG,kBAAkB,CAAC;YAC3B,IAAI,EAAE,aAAa;YACnB,YAAY,EAAE,kBAAkB,EAAE;YAClC,UAAU,EAAE,aAAa,CAAC,IAAI,GAAG,CAAC,4BAA4B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjF,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,mBAAmB,CAAC;YACpF,UAAU,EAAE,CAAC,gBAAgB,EAAE,cAAc,CAAC;YAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE;gBAC5C,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;gBAChG,MAAM,KAAK,GAAG,4CAA4C,CAAA;gBAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,EAAE,mDAAmD,CAAC,CAAA;gBACjF,IAAI,CAAC;oBACH,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAA;oBACtB,MAAM,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,CAAA;oBACpD,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,CAAA;oBAC1F,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;wBACrC,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,GAAG,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,CAAA;wBACjG,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,MAAM,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO;4BAAE,OAAO,IAAI,CAAA;oBAC/E,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC,CAAC,wBAAwB,CAAC,CAAC;gBACpC,KAAK,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,CAAC,CAAU,EAAE,CAAC;oBAC1F,IAAI,MAAM,CAAC,OAAO;wBAAE,OAAO,KAAK,CAAA;oBAChC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,IAAI,MAAM,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO;wBAAE,OAAO,IAAI,CAAA;gBACxH,CAAC;gBACD,OAAO,KAAK,CAAA;YACd,CAAC;SACF,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YACb,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAO,KAAK,CAAA;YAChC,eAAe,GAAG,IAAI,CAAA;YACtB,OAAO,GAAG,IAAI,CAAA;YACd,OAAO,CAAC,CAAC,IAAI,CAAA;QACf,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,SAAS,CAAA,CAAC,CAAC,CAAC,CAAA;QACzC,OAAO,OAAO,CAAA;IAChB,CAAC,CAAA;IACD,MAAM,SAAS,GAAG,KAAK,EAAE,IAAc,EAAE,MAAmB,EAAsB,EAAE;QAClF,IAAI,CAAC,MAAM,cAAc,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO;YAAE,OAAO,EAAE,CAAA;QAC9D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;QACvD,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO;YAAE,OAAO,EAAE,CAAA;QAC9C,IAAI,CAAC;YAAC,MAAM,KAAK,GAAY,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAAC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,OAAO,EAAE,CAAA;QAAC,CAAC;IACxH,CAAC,CAAA;IACD,MAAM,OAAO,GAAG,KAAK,EAAE,IAAY,EAAE,EAAE,GAAG,IAAI,CAAC;QAAC,OAAO,YAAY,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAA;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAA;IAAC,CAAC,CAAC,CAAC,CAAA;IACnH,OAAO;QACL,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO;QAC5E,KAAK,CAAC,MAAM;YACV,MAAM,OAAO,EAAE,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;YAC9B,OAAO,QAAQ,CAAC,IAAI;gBAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAA;QAC/D,CAAC;QACD,KAAK,CAAC,aAAa;YACjB,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,wBAAwB,CAAC,CAAC,CAAA;YACnE,MAAM,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;YACvB,OAAO,GAAG,CAAA;QACZ,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,GAAG,KAAK,IAAI,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA,CAAC,CAAC;QACpG,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG;YAClB,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;YAChD,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;gBACpC,MAAM,KAAK,GAAG,sCAAsC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBAC/D,OAAO,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,CAAA;YAC7C,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC,CAAA;QACvE,CAAC;QACD,KAAK,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM;YAC9B,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,gBAAgB,EAAE,CAAC,KAAK,EAAE,mDAAmD,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAA;YACtH,OAAO,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAC/E,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,MAAM;YACtB,OAAO,CAAC,MAAM,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAmB,EAAE;gBACpE,MAAM,CAAC,GAAG,IAAsB,CAAA;gBAChC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAA;YAC/H,CAAC,CAAC,CAAA;QACJ,CAAC;QACD,KAAK,CAAC,YAAY,CAAC,MAAM;YACvB,OAAO,CAAC,MAAM,SAAS,CAAC,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAgC,EAAE;gBAC7F,MAAM,CAAC,GAAG,IAAmC,CAAA;gBAC7C,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;YACjG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;QACtL,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,127 @@
1
+ /** Instance-owned task, event, and file lifetime; deliberately independent of HTTP. */
2
+ import { captureShot, ShotCaptureError } from './shot-capture.js';
3
+ export function createShotTasks(platform) {
4
+ const state = { available: false, reason: 'idle', capturing: false, total: 0, lastError: '' };
5
+ let pending = null;
6
+ let pendingError = null;
7
+ let lastShot = null;
8
+ let sequence = 0;
9
+ let stopped = false;
10
+ let tempDir;
11
+ let stopPromise;
12
+ const lifetime = new AbortController();
13
+ const waiters = new Set();
14
+ let active;
15
+ const notify = () => { const callbacks = [...waiters]; waiters.clear(); for (const callback of callbacks)
16
+ callback(); };
17
+ const ensureAvailable = () => {
18
+ state.available = !stopped && platform.supported();
19
+ state.reason = stopped ? 'stopped' : state.available ? 'ok' : 'not-macos';
20
+ return state.available;
21
+ };
22
+ const status = () => ({ ...state });
23
+ const clean = async (dir) => { await platform.remove(dir, true).catch(() => { }); };
24
+ async function start(options, rect) {
25
+ if (!ensureAvailable() || state.capturing)
26
+ return false;
27
+ state.capturing = true;
28
+ const task = { controller: new AbortController(), sequence: ++sequence };
29
+ active = task;
30
+ const current = () => !stopped && active === task && !task.controller.signal.aborted;
31
+ let dir;
32
+ try {
33
+ tempDir ??= platform.createTempDir();
34
+ dir = await tempDir;
35
+ if (!current()) {
36
+ await clean(dir);
37
+ return false;
38
+ }
39
+ }
40
+ catch (error) {
41
+ tempDir = undefined;
42
+ if (current()) {
43
+ state.lastError = `${rect ? 'start-failed' : 'tmp-failed'}: ${String(error?.message ?? error)}`;
44
+ state.capturing = false;
45
+ active = undefined;
46
+ }
47
+ return false;
48
+ }
49
+ const work = captureShot(platform, { ...options, seq: task.sequence }, dir, task.controller.signal, rect)
50
+ .then(async (result) => {
51
+ if (!current())
52
+ return false;
53
+ if (result) {
54
+ state.total++;
55
+ state.lastError = result.note;
56
+ pending = lastShot = result.shot;
57
+ pendingError = null;
58
+ state.capturing = false;
59
+ notify();
60
+ await platform.prune(dir, task.sequence).catch(() => { });
61
+ }
62
+ return !!result;
63
+ })
64
+ .catch((error) => {
65
+ if (!current())
66
+ return false;
67
+ const code = error instanceof ShotCaptureError ? error.code : rect ? 'start-failed' : 'finish-failed';
68
+ const message = error instanceof ShotCaptureError ? error.message : `截图已拍下,但处理失败:${String(error?.message ?? error)}`;
69
+ state.lastError = rect && !(error instanceof ShotCaptureError) ? `start-failed: ${String(error?.message ?? error)}` : code;
70
+ const finishError = error instanceof ShotCaptureError && !error.startup;
71
+ if (!rect || finishError) {
72
+ pendingError = { code, message, at: Date.now() };
73
+ state.capturing = false;
74
+ notify();
75
+ }
76
+ // The rect route historically reports startup success when finishing emits an error.
77
+ return !!rect && finishError;
78
+ })
79
+ .finally(async () => {
80
+ // Selection may finish before speculative window/display probes. Cancel
81
+ // those commands while this task still owns their cancellation signal.
82
+ task.controller.abort();
83
+ if (active === task) {
84
+ active = undefined;
85
+ state.capturing = false;
86
+ }
87
+ // An injected/uncooperative operation can resolve after disposal. Remove any late files too.
88
+ if (stopped)
89
+ await clean(dir);
90
+ });
91
+ if (rect)
92
+ return work;
93
+ void work;
94
+ return true;
95
+ }
96
+ function stop() {
97
+ if (stopPromise)
98
+ return stopPromise;
99
+ stopped = true;
100
+ lifetime.abort();
101
+ active?.controller.abort();
102
+ active = undefined;
103
+ state.capturing = false;
104
+ state.available = false;
105
+ state.reason = 'stopped';
106
+ pending = lastShot = null;
107
+ pendingError = null;
108
+ notify();
109
+ stopPromise = (async () => {
110
+ await platform.settle?.().catch(() => { });
111
+ if (tempDir)
112
+ await tempDir.then(clean).catch(() => { });
113
+ })();
114
+ return stopPromise;
115
+ }
116
+ return {
117
+ start, stop, status, ensureAvailable,
118
+ signal: lifetime.signal,
119
+ clearError() { pendingError = null; },
120
+ hasPending() { return !!pending || !!pendingError; },
121
+ takePending() { const result = { shot: pending, error: pendingError }; pending = null; pendingError = null; return result; },
122
+ findShot(id) { return pending?.id === id ? pending : lastShot?.id === id ? lastShot : null; },
123
+ lastShot() { return lastShot; },
124
+ subscribe(callback) { waiters.add(callback); return () => { waiters.delete(callback); }; },
125
+ };
126
+ }
127
+ //# sourceMappingURL=shot-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shot-service.js","sourceRoot":"","sources":["../../src/host/shot-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export const CAPTURE = '/usr/sbin/screencapture';
2
+ export const SELECT_TIMEOUT_MS = 90_000;
3
+ export const MAX_MARGIN_PT = 400;
4
+ export const DEFAULT_SHOT_CONFIG = { shotCardScope: 'window', shotPanelScope: 'selection', shotMargin: 48 };
5
+ //# sourceMappingURL=shot-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shot-types.js","sourceRoot":"","sources":["../../src/host/shot-types.ts"],"names":[],"mappings":"AAuDA,MAAM,CAAC,MAAM,OAAO,GAAG,yBAAyB,CAAA;AAChD,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAA;AACvC,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAA;AAChC,MAAM,CAAC,MAAM,mBAAmB,GAAe,EAAE,aAAa,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,EAAE,CAAA"}
@@ -0,0 +1,136 @@
1
+ import { readBody, sendJson } from './http.js';
2
+ import { createMacShotPlatform } from './shot-platform.js';
3
+ import { createShotTasks } from './shot-service.js';
4
+ import { resolveOptions } from './shot-geometry.js';
5
+ import { DEFAULT_SHOT_CONFIG } from './shot-types.js';
6
+ export { resolveWindowProbe, shotSupported } from './shot-platform.js';
7
+ export function createShotService(config = DEFAULT_SHOT_CONFIG, dependencies = {}) {
8
+ const platform = dependencies.platform ?? createMacShotPlatform();
9
+ const tasks = createShotTasks(platform);
10
+ let clientHello = null;
11
+ const clientReports = {};
12
+ async function handleShotStart(req, res) {
13
+ if (req.method !== 'POST') {
14
+ sendJson(res, 405, { ok: false, reason: 'method-not-allowed' });
15
+ return;
16
+ }
17
+ tasks.clearError();
18
+ const url = new URL(req.url || '/', 'http://localhost');
19
+ const mode = url.searchParams.get('mode') === 'panel' ? 'panel' : 'card';
20
+ await readBody(req, 64 * 1024).catch(() => '');
21
+ if (!tasks.ensureAvailable()) {
22
+ sendJson(res, 200, { ok: false, reason: tasks.status().reason });
23
+ return;
24
+ }
25
+ if (tasks.status().capturing) {
26
+ sendJson(res, 200, { ok: false, reason: 'busy' });
27
+ return;
28
+ }
29
+ const options = resolveOptions(config, mode, url);
30
+ const n = url.searchParams.get('rect')?.split(',').map(Number);
31
+ const rect = n?.length === 4 && n.every(Number.isFinite) && n[2] > 0 && n[3] > 0
32
+ ? { x: n[0], y: n[1], width: n[2], height: n[3] } : undefined;
33
+ const started = await tasks.start(options, rect);
34
+ sendJson(res, 200, {
35
+ ok: started, ...(rect ? { test: true } : {}), mode, scope: options.scope, margin: options.margin,
36
+ ...(started ? {} : { reason: tasks.status().lastError || 'start-failed' }),
37
+ });
38
+ }
39
+ function handleShotPending(req, res) {
40
+ const reply = () => sendJson(res, 200, { ok: true, ...tasks.takePending(), status: tasks.status() });
41
+ if (tasks.hasPending() || tasks.signal.aborted) {
42
+ reply();
43
+ return;
44
+ }
45
+ let finished = false;
46
+ let unsubscribe;
47
+ const clear = () => {
48
+ clearTimeout(timer);
49
+ unsubscribe?.();
50
+ req.removeListener('aborted', disconnect);
51
+ res.removeListener('close', disconnect);
52
+ };
53
+ const done = () => { if (finished)
54
+ return; finished = true; clear(); reply(); };
55
+ const disconnect = () => { if (finished)
56
+ return; finished = true; clear(); };
57
+ const timer = setTimeout(done, 25_000);
58
+ timer.unref();
59
+ req.once('aborted', disconnect);
60
+ // IncomingMessage.close also occurs for a normally completed GET body. Response.close
61
+ // identifies the socket/response lifetime without prematurely removing every long-poll.
62
+ res.once('close', disconnect);
63
+ unsubscribe = tasks.subscribe(done);
64
+ }
65
+ async function handleShotImage(req, res) {
66
+ const id = new URL(req.url || '/', 'http://localhost').searchParams.get('id') || '';
67
+ const shot = tasks.findShot(id);
68
+ if (!shot) {
69
+ res.statusCode = 404;
70
+ res.end('shot-not-found');
71
+ return;
72
+ }
73
+ if (!shot.ready) {
74
+ res.statusCode = 404;
75
+ res.end('not-ready');
76
+ return;
77
+ }
78
+ try {
79
+ const buf = await platform.readFile(shot.imagePath);
80
+ if (res.destroyed)
81
+ return;
82
+ if (tasks.signal.aborted) {
83
+ res.statusCode = 404;
84
+ res.end('shot-not-found');
85
+ return;
86
+ }
87
+ res.statusCode = 200;
88
+ res.setHeader('content-type', 'image/png');
89
+ res.setHeader('cache-control', 'no-store');
90
+ res.end(buf);
91
+ }
92
+ catch {
93
+ if (!res.destroyed) {
94
+ res.statusCode = tasks.signal.aborted ? 404 : 500;
95
+ res.end(tasks.signal.aborted ? 'shot-not-found' : 'read-failed');
96
+ }
97
+ }
98
+ }
99
+ async function handleShotHello(req, res) {
100
+ try {
101
+ const body = await readBody(req, Number.MAX_SAFE_INTEGER);
102
+ if (!tasks.signal.aborted) {
103
+ clientHello = JSON.parse(body || 'null');
104
+ const stage = clientHello?.stage;
105
+ if (stage)
106
+ clientReports[stage] = clientHello;
107
+ }
108
+ }
109
+ catch {
110
+ if (!tasks.signal.aborted)
111
+ clientHello = { parseError: true };
112
+ }
113
+ if (!res.destroyed)
114
+ sendJson(res, 200, { ok: true });
115
+ }
116
+ async function handleShotStatus(_req, res) {
117
+ tasks.ensureAvailable();
118
+ const probe = await platform.probeAvailable(tasks.signal);
119
+ const last = tasks.lastShot();
120
+ sendJson(res, 200, {
121
+ ok: true, status: tasks.status(), windowProbe: probe, clientHello, clientReports,
122
+ lastShot: last ? {
123
+ id: last.id, scope: last.scope, mode: last.mode, frame: last.frame, ready: last.ready,
124
+ ageMs: Date.now() - last.at, owner: last.owner, rect: last.rect, pad: last.pad,
125
+ scale: last.scale, selectionRect: last.selectionRect, windowRect: last.windowRect,
126
+ } : null,
127
+ });
128
+ }
129
+ return {
130
+ handleShotStart, handleShotPending, handleShotImage, handleShotHello, handleShotStatus,
131
+ status: tasks.status,
132
+ async stop() { clientHello = null; for (const stage of Object.keys(clientReports))
133
+ delete clientReports[stage]; await tasks.stop(); },
134
+ };
135
+ }
136
+ //# sourceMappingURL=shot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shot.js","sourceRoot":"","sources":["../../src/host/shot.ts"],"names":[],"mappings":"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"}
package/lib/prompts.js CHANGED
@@ -124,7 +124,60 @@ export const DETAIL_PROMPT = [
124
124
  '是后者就先说一句,再按它实际的意思解释。)',
125
125
  '选中文字是单个英文单词时,带上音标(IPA;英式美式不同就都写,拿不准就不写)。',
126
126
  ].join('\n');
127
- /** 追问模式(对话小窗)的系统提示词:不再要求两节结构,就是围绕这段文字聊天。 */
127
+ function shotFocusWording(scope, frame) {
128
+ if (!frame || scope === 'selection')
129
+ return '**这张图里的对象**';
130
+ return '**红框圈出的那个对象**';
131
+ }
132
+ function shotIntro(scope, frame) {
133
+ if (!frame || scope === 'selection') {
134
+ return '用户在 DSH 里按了截图快捷键,发来一张截图。这张图**就是**他框选的那块区域本身,同时提出一个问题。';
135
+ }
136
+ if (scope === 'margin') {
137
+ return '用户在 DSH 里按了截图快捷键,发来一张截图。这张图是选区加上周围留白:红色实线框内是选区,框外那一圈是他特意保留的周边内容。';
138
+ }
139
+ if (scope === 'screen') {
140
+ return '用户在 DSH 里按了截图快捷键,发来一张截图。这张图是他框选时所在的整块屏幕,红色实线框出了他关心的那一块。';
141
+ }
142
+ return '用户在 DSH 里按了截图快捷键,发来一张截图,并用红色实线框出了他关心的那一块,同时提出一个问题。';
143
+ }
144
+ function shotFocusRule(scope, frame) {
145
+ if (!frame || scope === 'selection') {
146
+ return '- 以**图里的内容**为依据回答;整张图就是他关心的地方。';
147
+ }
148
+ if (scope === 'margin' || scope === 'screen') {
149
+ return '- 以**图里的内容**为依据回答;红框内是问题的焦点,框外是它所在的位置(上下文)。';
150
+ }
151
+ return '- 以**图里的内容**为依据回答;红框标出的是问题的焦点,红框周围的界面是上下文。';
152
+ }
153
+ /** 按取景范围生成标题提示词(没有框时改用“这张图里的…”口径,不把注意力引到不存在的标记上)。 */
154
+ export function shotTitlePromptFor(scope, frame) {
155
+ return [
156
+ '现在只做一件事:给这次对话起个名字。',
157
+ '',
158
+ '要求:',
159
+ `- 概括${shotFocusWording(scope, frame)},不超过 20 个字(能短则短;是文字就取最关键的几个字,是图案就说清它是什么)。`,
160
+ '- 只输出这几个字本身:不要引号、不要标点、不要前缀、不要解释、不要调用任何工具。',
161
+ ].join('\n');
162
+ }
163
+ export const SHOT_TITLE_PROMPT = shotTitlePromptFor('window', true);
164
+ /** 按取景范围生成回答用的 system prompt。 */
165
+ export function shotPromptFor(scope, frame) {
166
+ const noFrame = !frame || scope === 'selection';
167
+ return [
168
+ shotIntro(scope, frame),
169
+ '',
170
+ '回答要求:',
171
+ shotFocusRule(scope, frame),
172
+ noFrame
173
+ ? '- 不要说"这段文字""这段代码"这种指代(用户没选中任何文字,只有一张图);需要提及时说"图里的…"。'
174
+ : '- 不要说"这段文字""这段代码"这种指代(用户没选中任何文字,只有一张图);需要提及时说"框里的…"。',
175
+ '- 不要输出翻译或"解读"的格式;直接回答问题本身。',
176
+ '- 看不清就说看不清(图可能被压缩过),不要猜内容。',
177
+ '- 需要外部信息时才用工具;能用图里内容回答的,不要联网。',
178
+ ].join('\n');
179
+ }
180
+ export const SHOT_PROMPT = shotPromptFor('window', true);
128
181
  export const CHAT_SYSTEM_PROMPT = [
129
182
  '你是 DeepSeek Harness 中「划词解读」小窗里的助手,正在辅助用户对一段【选中文字】及其上下文进行理解。',
130
183
  '用户问什么就答什么:可以问这段文字本身,也可以顺着它问到别的,不必把话题拉回选中文字。',
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,+BAA+B;AAC/B,iCAAiC;AACjC,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,mDAAmD;IACnD,oCAAoC;IACpC,4CAA4C;IAC5C,+CAA+C;IAC/C,mDAAmD;IACnD,EAAE;IACF,iDAAiD;CAClD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAEZ,mBAAmB;AACnB,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,WAAW;IACX,EAAE;IACF,uCAAuC;IACvC,+CAA+C;IAC/C,EAAE;IACF,sCAAsC;IACtC,gDAAgD;IAChD,sDAAsD;IACtD,wBAAwB;IACxB,4DAA4D;IAC5D,yDAAyD;IACzD,gEAAgE;IAChE,qDAAqD;IACrD,mCAAmC;IACnC,sDAAsD;IACtD,+CAA+C;IAC/C,0BAA0B;IAC1B,kDAAkD;IAClD,8BAA8B;IAC9B,4BAA4B;IAC5B,kEAAkE;IAClE,iEAAiE;IACjE,uDAAuD;IACvD,qCAAqC;IACrC,qFAAqF;IACrF,kCAAkC;IAClC,EAAE;IACF,gDAAgD;IAChD,wCAAwC;IACxC,2CAA2C;IAC3C,EAAE;IACF,+CAA+C;IAC/C,0BAA0B;IAC1B,wCAAwC;IACxC,qBAAqB;IACrB,kCAAkC;IAClC,wCAAwC;IACxC,EAAE;IACF,KAAK;IACL,gCAAgC;IAChC,oCAAoC;IACpC,uDAAuD;IACvD,iBAAiB;IACjB,8BAA8B;IAC9B,qDAAqD;IACrD,gBAAgB;CACjB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAEZ,6CAA6C;AAC7C,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,WAAW;IACX,EAAE;IACF,+CAA+C;IAC/C,EAAE;IACF,OAAO;IACP,kDAAkD;IAClD,8BAA8B;IAC9B,EAAE;IACF,KAAK;IACL,wBAAwB;IACxB,UAAU;IACV,cAAc;IACd,UAAU;IACV,KAAK;IACL,EAAE;IACF,OAAO;IACP,0DAA0D;IAC1D,2EAA2E;IAC3E,mCAAmC;IACnC,kDAAkD;IAClD,kCAAkC;IAClC,8BAA8B;IAC9B,yDAAyD;IACzD,EAAE;IACF,8BAA8B;IAC9B,2BAA2B;IAC3B,2CAA2C;IAC3C,iCAAiC;IACjC,+BAA+B;IAC/B,mCAAmC;IACnC,EAAE;IACF,KAAK;IACL,0CAA0C;IAC1C,0CAA0C;IAC1C,uBAAuB;IACvB,8BAA8B;CAC/B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAEZ,iCAAiC;AACjC,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,WAAW;IACX,EAAE;IACF,+CAA+C;IAC/C,EAAE;IACF,OAAO;IACP,2CAA2C;IAC3C,4CAA4C;IAC5C,uBAAuB;IACvB,0CAA0C;CAC3C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAEZ,4CAA4C;AAC5C,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,6DAA6D;IAC7D,6CAA6C;IAC7C,0CAA0C;IAC1C,sDAAsD;IACtD,oDAAoD;IACpD,gDAAgD;IAChD,oDAAoD;IACpD,4BAA4B;IAC5B,4DAA4D;IAC5D,mDAAmD;CACpD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA"}
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"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"}
package/lib/settings.js CHANGED
@@ -8,11 +8,11 @@ export const SETTINGS_GROUPS = [
8
8
  id: 'model', label: '模型',
9
9
  sections: [
10
10
  { "label": "模型", "keys": ["modelChoice"] },
11
- { "label": "对话记忆", "keys": ["chatContextTokens"], "note": "容量允许时保留完整对话;接近预算时压缩较早问答,原始历史和附件仍保留。图片计入预算,不按固定张数淘汰。" },
11
+ { "label": "对话记忆", "keys": ["chatContextTokens"], "note": "默认输入预算为20万 tokens。接近预算时优先压缩较早问答;最近一组也放不下时会一并整理为摘要,原始历史和附件仍保留。图片计入预算,不按固定张数淘汰。" },
12
12
  { "label": "思考强度", "keys": ["translationReasoningEffort", "reasoningEffort", "chatReasoningEffort"], "note": "关闭思考仍会生成回答。" },
13
13
  ],
14
14
  items: [
15
- { "key": "chatContextTokens", "kind": "number", "label": "对话输入预算", "hint": "按模型容量自动收紧,并为回答及工具结果预留空间;未知模型使用此预算。用量为估算。", "unit": "tokens", "min": CONFIG_LIMITS.chatContextTokens.min, "max": CONFIG_LIMITS.chatContextTokens.max, "step": 1024 },
15
+ { "key": "chatContextTokens", "kind": "number", "label": "对话输入预算", "hint": "按模型容量自动收紧,并为回答及工具结果预留空间;未知模型使用此预算。用量为估算。", "unit": "tokens", "min": CONFIG_LIMITS.chatContextTokens.min, "max": CONFIG_LIMITS.chatContextTokens.max, "step": 1 },
16
16
  { "key": "modelChoice", "kind": "model", "label": "模型", "hint": "", "keys": ["provider", "model"] },
17
17
  { "key": "translationReasoningEffort", "kind": "seg", "label": "首轮", "hint": "翻译 / 解读 / 注释", "options": ["off", "low", "high", "max"] },
18
18
  { "key": "reasoningEffort", "kind": "seg", "label": "次轮", "hint": "点击「展开详解」", "options": ["off", "low", "high", "max"] },
@@ -78,6 +78,17 @@ export const SETTINGS_GROUPS = [
78
78
  { "key": "voiceAutoSend", "kind": "switch", "label": "说完直接发送", "hint": "结束语音输入后,输入框里有字就直接发送(等于按发送键)。" },
79
79
  ],
80
80
  },
81
+ {
82
+ id: 'shot', label: '截图',
83
+ sections: [
84
+ { "label": "取景范围", "keys": ["shotCardScope", "shotPanelScope", "shotMargin"], "note": "两种模式的取景范围分开设置;留白两模式共用,仅「选区 + 留白」生效。" },
85
+ ],
86
+ items: [
87
+ { "key": "shotCardScope", "kind": "seg", "label": "小窗未打开时", "hint": "截图后弹语音卡时截取的范围。", "options": ["screen", "window", "margin", "selection"] },
88
+ { "key": "shotPanelScope", "kind": "seg", "label": "小窗已打开时", "hint": "截图直接进输入框附件时截取的范围。", "options": ["screen", "window", "margin", "selection"] },
89
+ { "key": "shotMargin", "kind": "number", "label": "选区留白", "hint": "向四周外扩的逻辑像素;仅「选区 + 留白」生效,外扩不会超出选区中心所在的那块屏。", "unit": "pt", "min": CONFIG_LIMITS.shotMargin.min, "max": CONFIG_LIMITS.shotMargin.max, "step": 8 },
90
+ ],
91
+ },
81
92
  {
82
93
  id: 'data', label: '数据',
83
94
  sections: [