@anionex/dsh-computer-use 0.3.1 → 0.3.3
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/CHANGELOG.md +130 -0
- package/README.md +4 -1
- package/README.zh.md +4 -1
- package/lib/client.js +1 -1
- package/lib/client.js.map +1 -1
- package/lib/config.js +49 -20
- package/lib/config.js.map +1 -1
- package/lib/confirmations.js.map +1 -1
- package/lib/exposure.js +19 -6
- package/lib/exposure.js.map +1 -1
- package/lib/index.js +5 -1
- package/lib/index.js.map +1 -1
- package/lib/leases.js +4 -1
- package/lib/leases.js.map +1 -1
- package/lib/providers/macos.js +27 -11
- package/lib/providers/macos.js.map +1 -1
- package/lib/skill.js +13 -2
- package/lib/skill.js.map +1 -1
- package/lib/tools.js +1 -1
- package/lib/tools.js.map +1 -1
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/config.d.ts +15 -2
- package/lib/types/config.d.ts.map +1 -1
- package/lib/types/confirmations.d.ts +1 -1
- package/lib/types/confirmations.d.ts.map +1 -1
- package/lib/types/exposure.d.ts.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/leases.d.ts +2 -1
- package/lib/types/leases.d.ts.map +1 -1
- package/lib/types/providers/macos.d.ts +0 -1
- package/lib/types/providers/macos.d.ts.map +1 -1
- package/lib/types/skill.d.ts +7 -2
- package/lib/types/skill.d.ts.map +1 -1
- package/lib/types/tools.d.ts.map +1 -1
- package/lib/types/types.d.ts +3 -1
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/web.d.ts +2 -1
- package/lib/types/web.d.ts.map +1 -1
- package/lib/types.js.map +1 -1
- package/lib/web.js +34 -9
- package/lib/web.js.map +1 -1
- package/native/macos/Sources/Helper/CursorOverlay.swift +3 -0
- package/native/macos/Sources/Helper/main.swift +35 -19
- package/native/macos/bin/dsh-computer-use-helper +0 -0
- package/native/macos/manifest.json +3 -3
- package/package.json +100 -41
- package/scripts/build-client.mjs +0 -0
- package/scripts/build-native.mjs +0 -0
- package/scripts/model-e2e.mjs +7 -3
- package/scripts/session-transcript.mjs +4 -3
- package/scripts/validate.mjs +0 -0
- package/src/client/index.tsx +4 -2
- package/src/config.ts +53 -21
- package/src/confirmations.ts +1 -1
- package/src/exposure.ts +20 -6
- package/src/index.ts +5 -1
- package/src/leases.ts +6 -2
- package/src/providers/macos.ts +31 -11
- package/src/skill.ts +14 -2
- package/src/tools.ts +3 -2
- package/src/types.ts +2 -1
- package/src/web.ts +37 -7
package/lib/web.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Optional Web Settings and provider-health route. */
|
|
2
2
|
import { SettingsConflictError } from '@deepseek-ai/dsh-settings';
|
|
3
|
-
import { COMPUTER_USE_SETTINGS_NAMESPACE, } from "./config.js";
|
|
3
|
+
import { COMPUTER_USE_SETTINGS_NAMESPACE, resolveConfig, } from "./config.js";
|
|
4
4
|
/** Exact same-origin Settings endpoint. */
|
|
5
5
|
export const COMPUTER_USE_SETTINGS_ROUTE = '/_dsh/computer-use/settings';
|
|
6
6
|
function isRecord(value) {
|
|
@@ -25,13 +25,19 @@ function responseJson(res, status, body) {
|
|
|
25
25
|
function requestError(res, status, code, message) {
|
|
26
26
|
responseJson(res, status, { ok: false, error: { code, message } });
|
|
27
27
|
}
|
|
28
|
-
function sameOriginPost(req) {
|
|
28
|
+
function sameOriginPost(req, authorizeUnmarkedPost) {
|
|
29
29
|
const fetchSite = req.headers['sec-fetch-site'];
|
|
30
30
|
if (fetchSite === 'cross-site')
|
|
31
31
|
return false;
|
|
32
32
|
const origin = req.headers.origin;
|
|
33
|
-
if (origin === undefined)
|
|
34
|
-
|
|
33
|
+
if (origin === undefined) {
|
|
34
|
+
if (fetchSite === 'same-origin' || fetchSite === 'same-site' || fetchSite === 'none')
|
|
35
|
+
return true;
|
|
36
|
+
// Desktop strips browser origin metadata before forwarding, but injects a
|
|
37
|
+
// Host-issued signed cookie. Verify it through Connection's own fence;
|
|
38
|
+
// mere Cookie header presence is not authentication.
|
|
39
|
+
return fetchSite === undefined && authorizeUnmarkedPost?.(req) === true;
|
|
40
|
+
}
|
|
35
41
|
const host = req.headers.host;
|
|
36
42
|
if (host === undefined)
|
|
37
43
|
return false;
|
|
@@ -78,11 +84,28 @@ function parseRequest(value) {
|
|
|
78
84
|
function publicMessage(error) {
|
|
79
85
|
return error instanceof Error ? error.message.slice(0, 1000) : String(error).slice(0, 1000);
|
|
80
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Whether one thrown value is the Settings provider's optimistic-concurrency
|
|
89
|
+
* conflict. The provider — not this module — throws it, so a second copy of
|
|
90
|
+
* `SettingsConflictError` (plugin-local versus host-local install) makes
|
|
91
|
+
* `instanceof` alone unreliable. Both the 0.1.5 and the older class carry the
|
|
92
|
+
* stable machine code `"SETTINGS_CONFLICT"`, which is checked first-class;
|
|
93
|
+
* `instanceof` stays as an equivalent additional branch.
|
|
94
|
+
*/
|
|
95
|
+
function isSettingsConflict(error) {
|
|
96
|
+
if (error instanceof SettingsConflictError)
|
|
97
|
+
return true;
|
|
98
|
+
return typeof error === 'object'
|
|
99
|
+
&& error !== null
|
|
100
|
+
&& error.code === 'SETTINGS_CONFLICT';
|
|
101
|
+
}
|
|
81
102
|
/** Same-origin backend used by the optional client Settings section. */
|
|
82
103
|
export class ComputerUseWebBackend {
|
|
83
104
|
ctx;
|
|
84
|
-
|
|
105
|
+
authorizeUnmarkedPost;
|
|
106
|
+
constructor(ctx, authorizeUnmarkedPost) {
|
|
85
107
|
this.ctx = ctx;
|
|
108
|
+
this.authorizeUnmarkedPost = authorizeUnmarkedPost;
|
|
86
109
|
}
|
|
87
110
|
/** Current browser-safe Settings and health state. */
|
|
88
111
|
snapshot() {
|
|
@@ -116,7 +139,7 @@ export class ComputerUseWebBackend {
|
|
|
116
139
|
requestError(res, 405, 'method-not-allowed', 'Use GET or POST');
|
|
117
140
|
return;
|
|
118
141
|
}
|
|
119
|
-
if (!sameOriginPost(req)) {
|
|
142
|
+
if (!sameOriginPost(req, this.authorizeUnmarkedPost)) {
|
|
120
143
|
requestError(res, 403, 'origin-rejected', 'The request must originate from this DSH Web application');
|
|
121
144
|
return;
|
|
122
145
|
}
|
|
@@ -132,6 +155,7 @@ export class ComputerUseWebBackend {
|
|
|
132
155
|
if (parsed.action === 'save') {
|
|
133
156
|
if (!this.ctx.settings.writable)
|
|
134
157
|
throw new Error('settings provider is read-only');
|
|
158
|
+
resolveConfig(parsed.value);
|
|
135
159
|
await this.ctx.settings.replace(COMPUTER_USE_SETTINGS_NAMESPACE, parsed.value, parsed.expectedRevision);
|
|
136
160
|
}
|
|
137
161
|
else if (parsed.action === 'health') {
|
|
@@ -143,7 +167,7 @@ export class ComputerUseWebBackend {
|
|
|
143
167
|
responseJson(res, 200, { ok: true, value: this.snapshot() });
|
|
144
168
|
}
|
|
145
169
|
catch (error) {
|
|
146
|
-
const conflict = error
|
|
170
|
+
const conflict = isSettingsConflict(error);
|
|
147
171
|
this.ctx.logger.warn('dsh-computer-use Web action=%s failed: %s', parsed.action, publicMessage(error));
|
|
148
172
|
requestError(res, conflict ? 409 : 400, conflict ? 'settings-conflict' : 'action-failed', publicMessage(error));
|
|
149
173
|
}
|
|
@@ -151,8 +175,9 @@ export class ComputerUseWebBackend {
|
|
|
151
175
|
}
|
|
152
176
|
/** Attach the optional route when a Web host is present. */
|
|
153
177
|
export function installComputerUseWeb(ctx) {
|
|
154
|
-
|
|
155
|
-
|
|
178
|
+
ctx.inject(['webServer', 'connection'], (webCtx) => {
|
|
179
|
+
const connection = webCtx.connection;
|
|
180
|
+
const backend = new ComputerUseWebBackend(webCtx, (req) => typeof connection?.requestRejection === 'function' && connection.requestRejection(req) === undefined);
|
|
156
181
|
webCtx.effect(() => webCtx.webServer.register({
|
|
157
182
|
kind: 'exact',
|
|
158
183
|
path: COMPUTER_USE_SETTINGS_ROUTE,
|
package/lib/web.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.js","sourceRoot":"","sources":["../src/web.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAIvD,OAAO,EAAE,qBAAqB,EAA2B,MAAM,2BAA2B,CAAA;AAE1F,OAAO,EACL,+BAA+B,
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../src/web.ts"],"names":[],"mappings":"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"}
|
|
@@ -204,6 +204,9 @@ private final class CursorOverlayController: NSObject {
|
|
|
204
204
|
window.isOpaque = false
|
|
205
205
|
window.backgroundColor = .clear
|
|
206
206
|
window.hasShadow = false
|
|
207
|
+
// Target loss must remove the indicator immediately, without AppKit's
|
|
208
|
+
// panel fade leaving it visible over an unrelated foreground app.
|
|
209
|
+
window.animationBehavior = .none
|
|
207
210
|
window.ignoresMouseEvents = true
|
|
208
211
|
window.hidesOnDeactivate = false
|
|
209
212
|
window.becomesKeyOnlyIfNeeded = true
|
|
@@ -6,7 +6,7 @@ import Darwin
|
|
|
6
6
|
import Foundation
|
|
7
7
|
import ScreenCaptureKit
|
|
8
8
|
|
|
9
|
-
private let helperVersion = "0.3.
|
|
9
|
+
private let helperVersion = "0.3.3"
|
|
10
10
|
|
|
11
11
|
private struct HelperError: Error {
|
|
12
12
|
let code: String
|
|
@@ -501,22 +501,27 @@ private func captureWindow(_ snapshot: ObservationSnapshot, path: String, requir
|
|
|
501
501
|
return nil
|
|
502
502
|
}
|
|
503
503
|
let expectedId = (snapshot.windowJSON?["id"] as? NSNumber)?.uint32Value
|
|
504
|
-
let
|
|
504
|
+
let expectedFrame = (snapshot.windowJSON?["frame"] as? [String: Any]).flatMap { try? cgRect($0) }
|
|
505
505
|
let windows = content.windows.filter { $0.owningApplication?.processID == snapshot.app.processIdentifier }
|
|
506
506
|
let selected = windows.first { window in
|
|
507
|
-
if let expectedId
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
507
|
+
if let expectedId { return window.windowID == expectedId }
|
|
508
|
+
guard let expectedFrame else { return false }
|
|
509
|
+
let tolerance: CGFloat = 2
|
|
510
|
+
return abs(window.frame.minX - expectedFrame.minX) <= tolerance
|
|
511
|
+
&& abs(window.frame.minY - expectedFrame.minY) <= tolerance
|
|
512
|
+
&& abs(window.frame.width - expectedFrame.width) <= tolerance
|
|
513
|
+
&& abs(window.frame.height - expectedFrame.height) <= tolerance
|
|
514
|
+
}
|
|
511
515
|
guard let selected else {
|
|
512
|
-
if required { throw fail("COMPUTER_TARGET_UNAVAILABLE", "
|
|
516
|
+
if required { throw fail("COMPUTER_TARGET_UNAVAILABLE", "the observed window is not available for capture") }
|
|
513
517
|
return nil
|
|
514
518
|
}
|
|
519
|
+
let filter = SCContentFilter(desktopIndependentWindow: selected)
|
|
515
520
|
let configuration = SCStreamConfiguration()
|
|
516
|
-
|
|
517
|
-
configuration.
|
|
521
|
+
let pixelScale = CGFloat(filter.pointPixelScale)
|
|
522
|
+
configuration.width = max(1, Int((selected.frame.width * pixelScale).rounded()))
|
|
523
|
+
configuration.height = max(1, Int((selected.frame.height * pixelScale).rounded()))
|
|
518
524
|
configuration.showsCursor = false
|
|
519
|
-
let filter = SCContentFilter(desktopIndependentWindow: selected)
|
|
520
525
|
let image: CGImage
|
|
521
526
|
do {
|
|
522
527
|
image = try await SCScreenshotManager.captureImage(contentFilter: filter, configuration: configuration)
|
|
@@ -583,25 +588,36 @@ private func cgRect(_ frame: [String: Any]) throws -> CGRect {
|
|
|
583
588
|
|
|
584
589
|
private func windowAtPoint(app: NSRunningApplication, point: CGPoint) throws -> (windowNumber: Int64, frame: CGRect) {
|
|
585
590
|
guard let windows = CGWindowListCopyWindowInfo(
|
|
586
|
-
[.
|
|
591
|
+
[.optionAll, .excludeDesktopElements],
|
|
587
592
|
kCGNullWindowID
|
|
588
593
|
) as? [[String: Any]] else {
|
|
589
594
|
throw fail("COMPUTER_TARGET_UNAVAILABLE", "CoreGraphics window list is unavailable")
|
|
590
595
|
}
|
|
591
|
-
|
|
592
|
-
// first match is the topmost window of the selected app at the point.
|
|
593
|
-
guard let window = windows.first(where: { candidate in
|
|
596
|
+
let appWindows = windows.filter { candidate in
|
|
594
597
|
guard (candidate[kCGWindowOwnerPID as String] as? NSNumber)?.int32Value == app.processIdentifier,
|
|
595
|
-
(candidate[kCGWindowLayer as String] as? NSNumber)?.intValue == 0
|
|
596
|
-
let bounds = candidate[kCGWindowBounds as String] as? [String: Any],
|
|
597
|
-
let frame = CGRect(dictionaryRepresentation: bounds as CFDictionary),
|
|
598
|
-
frame.contains(point) else { return false }
|
|
598
|
+
(candidate[kCGWindowLayer as String] as? NSNumber)?.intValue == 0 else { return false }
|
|
599
599
|
return true
|
|
600
|
+
}
|
|
601
|
+
let containing = appWindows.filter { candidate in
|
|
602
|
+
guard let bounds = candidate[kCGWindowBounds as String] as? [String: Any],
|
|
603
|
+
let frame = CGRect(dictionaryRepresentation: bounds as CFDictionary) else { return false }
|
|
604
|
+
return frame.contains(point)
|
|
605
|
+
}
|
|
606
|
+
// optionAll can recover windows omitted by optionOnScreenOnly on some
|
|
607
|
+
// multi-display layouts. Never route pointer input to a hidden window.
|
|
608
|
+
guard let window = containing.first(where: {
|
|
609
|
+
($0[kCGWindowIsOnscreen as String] as? NSNumber)?.boolValue == true
|
|
600
610
|
}),
|
|
601
611
|
let windowNumber = (window[kCGWindowNumber as String] as? NSNumber)?.int64Value,
|
|
602
612
|
let bounds = window[kCGWindowBounds as String] as? [String: Any],
|
|
603
613
|
let frame = CGRect(dictionaryRepresentation: bounds as CFDictionary) else {
|
|
604
|
-
|
|
614
|
+
if !containing.isEmpty {
|
|
615
|
+
throw fail("COMPUTER_TARGET_UNAVAILABLE", "the selected app window contains the coordinate but CoreGraphics does not mark it on-screen")
|
|
616
|
+
}
|
|
617
|
+
if appWindows.isEmpty {
|
|
618
|
+
throw fail("COMPUTER_TARGET_UNAVAILABLE", "the selected app has no enumerable layer-zero window in CoreGraphics")
|
|
619
|
+
}
|
|
620
|
+
throw fail("COMPUTER_TARGET_UNAVAILABLE", "coordinate is outside the selected app's enumerable windows")
|
|
605
621
|
}
|
|
606
622
|
return (windowNumber, frame)
|
|
607
623
|
}
|
|
Binary file
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"helperVersion": "0.3.
|
|
4
|
-
"sourceSha256": "
|
|
3
|
+
"helperVersion": "0.3.3",
|
|
4
|
+
"sourceSha256": "b5de65a33e53689c5b490d8f6641a6577ba61a6f6f45798210b0df65bd0ced07",
|
|
5
5
|
"binary": {
|
|
6
6
|
"path": "bin/dsh-computer-use-helper",
|
|
7
|
-
"sha256": "
|
|
7
|
+
"sha256": "55325e0008d755bf2b1d408bcfd5cdb80842ec2a0fa5312da6961eb65d7d6dd3",
|
|
8
8
|
"architectures": [
|
|
9
9
|
"arm64",
|
|
10
10
|
"x86_64"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anionex/dsh-computer-use",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Accessibility-first macOS Computer Use capability for DeepSeek Harness with stale-observation protection, app leases, confirmations, screenshots, and Web diagnostics",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"url": "https://github.com/Anionex/dsh-computer-use/issues"
|
|
19
19
|
},
|
|
20
20
|
"type": "module",
|
|
21
|
+
"packageManager": "pnpm@11.20.0",
|
|
21
22
|
"engines": {
|
|
22
23
|
"node": "^22.19.0 || >=24.0.0"
|
|
23
24
|
},
|
|
@@ -55,44 +56,79 @@
|
|
|
55
56
|
"cordis.patch.yml",
|
|
56
57
|
"README.md",
|
|
57
58
|
"README.zh.md",
|
|
58
|
-
"LICENSE"
|
|
59
|
+
"LICENSE",
|
|
60
|
+
"CHANGELOG.md"
|
|
59
61
|
],
|
|
62
|
+
"scripts": {
|
|
63
|
+
"native:build": "node scripts/build-native.mjs",
|
|
64
|
+
"check:native": "node scripts/check-native.mjs",
|
|
65
|
+
"build": "pnpm run native:build && tsc -p tsconfig.json && tsc -p tsconfig.client.json && node scripts/build-client.mjs",
|
|
66
|
+
"prepack": "pnpm run build",
|
|
67
|
+
"test": "pnpm run build && vitest run tests",
|
|
68
|
+
"test:model": "node scripts/model-e2e.mjs",
|
|
69
|
+
"test:watch": "vitest tests",
|
|
70
|
+
"validate": "node scripts/validate.mjs --lane all",
|
|
71
|
+
"validate:local": "node scripts/validate.mjs --lane local",
|
|
72
|
+
"validate:profile": "node scripts/validate.mjs --lane profile",
|
|
73
|
+
"validate:model": "node scripts/model-e2e.mjs",
|
|
74
|
+
"validate:release": "pnpm run validate && pnpm run validate:model"
|
|
75
|
+
},
|
|
60
76
|
"dsh": {
|
|
61
77
|
"bundle": {
|
|
62
78
|
"patch": "./cordis.patch.yml"
|
|
63
79
|
},
|
|
64
80
|
"client": {
|
|
65
81
|
"inject": [
|
|
66
|
-
"@deepseek-ai/dsh-client-
|
|
67
|
-
"@deepseek-ai/dsh-client-ui-settings",
|
|
82
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
83
|
+
"@deepseek-ai/dsh-client-ui-settings-general",
|
|
68
84
|
"@deepseek-ai/dsh-client-locale",
|
|
69
|
-
"@deepseek-ai/dsh-api-remotes"
|
|
85
|
+
"@deepseek-ai/dsh-api-remotes",
|
|
86
|
+
"@deepseek-ai/dsh-client-connection"
|
|
70
87
|
],
|
|
71
88
|
"platform": "web"
|
|
89
|
+
},
|
|
90
|
+
"compatibility": {
|
|
91
|
+
"dsh": ">=0.1.0-rc.8 <0.2.0 || >=0.1.1-rc.1 <0.2.0 || >=0.1.2-alpha.3 <0.2.0 || >=0.1.5-rc.1 <0.2.0 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
92
|
+
"dshReleases": {
|
|
93
|
+
"0.1.0-rc.8": "compatible",
|
|
94
|
+
"0.1.1-rc.1": "compatible",
|
|
95
|
+
"0.1.1-rc.2": "compatible",
|
|
96
|
+
"0.1.2-alpha.3": "compatible",
|
|
97
|
+
"0.1.2-alpha.4": "compatible",
|
|
98
|
+
"0.1.2-alpha.5": "compatible",
|
|
99
|
+
"0.1.5-alpha.1": "compatible",
|
|
100
|
+
"0.1.5-alpha.2": "compatible",
|
|
101
|
+
"0.1.5-rc.1": "compatible",
|
|
102
|
+
"0.1.7-rc.2": "compatible",
|
|
103
|
+
"0.2.0-rc.2": "compatible"
|
|
104
|
+
},
|
|
105
|
+
"profiles": [
|
|
106
|
+
"web"
|
|
107
|
+
]
|
|
72
108
|
}
|
|
73
109
|
},
|
|
74
110
|
"peerDependencies": {
|
|
75
|
-
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
|
|
76
|
-
"@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.6",
|
|
77
|
-
"@deepseek-ai/dsh-brand": "^0.1.0-rc.6",
|
|
78
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6",
|
|
79
|
-
"@deepseek-ai/dsh-client-
|
|
80
|
-
"@deepseek-ai/dsh-client-ui-
|
|
81
|
-
"@deepseek-ai/dsh-client-ui-
|
|
82
|
-
"@deepseek-ai/dsh-
|
|
83
|
-
"@deepseek-ai/dsh-
|
|
84
|
-
"@deepseek-ai/dsh-
|
|
85
|
-
"@deepseek-ai/dsh-
|
|
86
|
-
"@deepseek-ai/dsh-
|
|
87
|
-
"@deepseek-ai/dsh-
|
|
88
|
-
"@deepseek-ai/dsh-
|
|
89
|
-
"@deepseek-ai/dsh-
|
|
90
|
-
"@deepseek-ai/dsh-
|
|
91
|
-
"@deepseek-ai/dsh-
|
|
92
|
-
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.6",
|
|
111
|
+
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
112
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
113
|
+
"@deepseek-ai/dsh-brand": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
114
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
115
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
116
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
117
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
118
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
119
|
+
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
120
|
+
"@deepseek-ai/dsh-session": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
121
|
+
"@deepseek-ai/dsh-settings": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
122
|
+
"@deepseek-ai/dsh-skill": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
123
|
+
"@deepseek-ai/dsh-storage-domain": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
124
|
+
"@deepseek-ai/dsh-subprocess": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
125
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
126
|
+
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
127
|
+
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
93
128
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
94
129
|
"react": "^18.2.0",
|
|
95
|
-
"@deepseek-ai/schemastery": "^3.18.1"
|
|
130
|
+
"@deepseek-ai/schemastery": "^3.18.1",
|
|
131
|
+
"@deepseek-ai/dsh-util-values": "^0.1.0-rc.6 || 0.2.0-rc.2"
|
|
96
132
|
},
|
|
97
133
|
"peerDependenciesMeta": {
|
|
98
134
|
"@deepseek-ai/dsh-host-webserver": {
|
|
@@ -104,23 +140,46 @@
|
|
|
104
140
|
"@types/react": "~18.3.1",
|
|
105
141
|
"@types/react-dom": "~18.3.0",
|
|
106
142
|
"typescript": "^6.0.3",
|
|
107
|
-
"vitest": "^4.1.10"
|
|
143
|
+
"vitest": "^4.1.10",
|
|
144
|
+
"@deepseek-ai/dsh-agent": "0.2.0-rc.2",
|
|
145
|
+
"@deepseek-ai/dsh-api-remotes": "0.2.0-rc.2",
|
|
146
|
+
"@deepseek-ai/dsh-brand": "0.2.0-rc.2",
|
|
147
|
+
"@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
|
|
148
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2",
|
|
149
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.2.0-rc.2",
|
|
150
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
|
|
151
|
+
"@deepseek-ai/dsh-host-webserver": "0.2.0-rc.2",
|
|
152
|
+
"@deepseek-ai/dsh-llm": "0.2.0-rc.2",
|
|
153
|
+
"@deepseek-ai/dsh-session": "0.2.0-rc.2",
|
|
154
|
+
"@deepseek-ai/dsh-settings": "0.2.0-rc.2",
|
|
155
|
+
"@deepseek-ai/dsh-skill": "0.2.0-rc.2",
|
|
156
|
+
"@deepseek-ai/dsh-storage-domain": "0.2.0-rc.2",
|
|
157
|
+
"@deepseek-ai/dsh-subprocess": "0.2.0-rc.2",
|
|
158
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.0-rc.2",
|
|
159
|
+
"@deepseek-ai/dsh-tools": "0.2.0-rc.2",
|
|
160
|
+
"@deepseek-ai/dsh-user-approval": "0.2.0-rc.2",
|
|
161
|
+
"@deepseek-ai/cordis": "4.0.4",
|
|
162
|
+
"@deepseek-ai/schemastery": "3.18.4",
|
|
163
|
+
"@deepseek-ai/dsh-util-values": "0.2.0-rc.2",
|
|
164
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
|
|
165
|
+
"@deepseek-ai/dsh-client-connection": "0.2.0-rc.2",
|
|
166
|
+
"@deepseek-ai/dsh-scope": "0.2.0-rc.2",
|
|
167
|
+
"@deepseek-ai/dsh-storage": "0.2.0-rc.2",
|
|
168
|
+
"@deepseek-ai/cosmokit": "1.8.5",
|
|
169
|
+
"@deepseek-ai/dsh-credentials": "0.2.0-rc.2",
|
|
170
|
+
"@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.2",
|
|
171
|
+
"@deepseek-ai/dsh-util-crypto": "0.2.0-rc.2",
|
|
172
|
+
"@deepseek-ai/dsh-timeout": "0.2.0-rc.2",
|
|
173
|
+
"@deepseek-ai/dsh-sandbox": "0.2.0-rc.2",
|
|
174
|
+
"@deepseek-ai/dsh-http-proxy": "0.2.0-rc.2",
|
|
175
|
+
"@deepseek-ai/cordis-plugin-loader": "1.0.5",
|
|
176
|
+
"@deepseek-ai/dsh-util-workspace-path": "0.2.0-rc.2",
|
|
177
|
+
"@deepseek-ai/dsh-client-store": "0.2.0-rc.2",
|
|
178
|
+
"@deepseek-ai/dsh-util-code-language": "0.2.0-rc.2",
|
|
179
|
+
"@deepseek-ai/dsh-deque": "0.2.0-rc.2"
|
|
108
180
|
},
|
|
109
181
|
"dependencies": {
|
|
110
|
-
"zod": "
|
|
182
|
+
"zod": "4.4.3"
|
|
111
183
|
},
|
|
112
|
-
"license": "MIT"
|
|
113
|
-
|
|
114
|
-
"native:build": "node scripts/build-native.mjs",
|
|
115
|
-
"check:native": "node scripts/check-native.mjs",
|
|
116
|
-
"build": "pnpm run native:build && tsc -p tsconfig.json && tsc -p tsconfig.client.json && node scripts/build-client.mjs",
|
|
117
|
-
"test": "pnpm run build && vitest run tests",
|
|
118
|
-
"test:model": "node scripts/model-e2e.mjs",
|
|
119
|
-
"test:watch": "vitest tests",
|
|
120
|
-
"validate": "node scripts/validate.mjs --lane all",
|
|
121
|
-
"validate:local": "node scripts/validate.mjs --lane local",
|
|
122
|
-
"validate:profile": "node scripts/validate.mjs --lane profile",
|
|
123
|
-
"validate:model": "node scripts/model-e2e.mjs",
|
|
124
|
-
"validate:release": "pnpm run validate && pnpm run validate:model"
|
|
125
|
-
}
|
|
126
|
-
}
|
|
184
|
+
"license": "MIT"
|
|
185
|
+
}
|
package/scripts/build-client.mjs
CHANGED
|
File without changes
|
package/scripts/build-native.mjs
CHANGED
|
File without changes
|
package/scripts/model-e2e.mjs
CHANGED
|
@@ -45,8 +45,8 @@ function externalCommand(command, args) {
|
|
|
45
45
|
// A long-running Node harness can leave DSH without its internal ESM loader.
|
|
46
46
|
// A login shell matches the real CLI entry and keeps profile package resolution isolated.
|
|
47
47
|
return command === 'dsh' && process.platform === 'darwin'
|
|
48
|
-
? { command: '/bin/bash', args: ['-lc', 'exec
|
|
49
|
-
: { command, args }
|
|
48
|
+
? { command: '/bin/bash', args: ['-lc', 'exec "$@"', 'dsh', process.env.DSH_TEST_CLI ?? 'dsh', ...args] }
|
|
49
|
+
: { command: command === 'dsh' ? process.env.DSH_TEST_CLI ?? command : command, args }
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
async function runCommand(name, command, args, options = {}) {
|
|
@@ -180,6 +180,8 @@ async function realModelWorkflow() {
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
const tarball = await findTarball(packing)
|
|
183
|
+
await mkdir(join(home, 'profiles', 'headless'), { recursive: true })
|
|
184
|
+
await writeFile(join(home, 'profiles', 'headless', 'pnpm-workspace.yaml'), 'nodeLinker: hoisted\nautoInstallPeers: false\n')
|
|
183
185
|
await runCommand(
|
|
184
186
|
'install dsh-computer-use tarball',
|
|
185
187
|
'dsh',
|
|
@@ -215,6 +217,8 @@ async function realModelWorkflow() {
|
|
|
215
217
|
].join('\n'))
|
|
216
218
|
|
|
217
219
|
const prompt = `/computer-use\n\nUse the dsh-computer-use capability to operate the running deterministic macOS fixture whose bundle id is ${BUNDLE_ID}. Load the Skill, list running applications, select the exact fixture process, observe it with a required screenshot and full Accessibility state, locate the element labelled "Targeted pointer probe", and click that current element using its observation id and element index with allowCoordinateFallback=true. Confirm from the fresh post-action observation that the status reads "Status: pointer click". Use only the focused computer-use Tools for UI observation and input; do not use shell, AppleScript, JXA, direct file edits, or coordinate guessing. Finish immediately after confirming the pointer-click state.`
|
|
220
|
+
const hostVersion = await runCommand('target DSH version', 'dsh', ['--version'])
|
|
221
|
+
const defaultBaseURL = hostVersion.stdout.includes('0.2.') ? 'https://api.deepseek.com/anthropic' : 'https://api.deepseek.com/v1'
|
|
218
222
|
const result = await runCommand(
|
|
219
223
|
'DeepSeek V4 computer-use fixture workflow',
|
|
220
224
|
'dsh',
|
|
@@ -226,7 +230,7 @@ async function realModelWorkflow() {
|
|
|
226
230
|
DSH_TELEMETRY_DISABLED: '1',
|
|
227
231
|
DSH_PERMISSION_MODE: 'workspace-write',
|
|
228
232
|
DEEPSEEK_API_KEY: requiredEnvironment('DEEPSEEK_API_KEY'),
|
|
229
|
-
DEEPSEEK_BASE_URL: process.env.DEEPSEEK_BASE_URL?.trim() ||
|
|
233
|
+
DEEPSEEK_BASE_URL: process.env.DEEPSEEK_BASE_URL?.trim() || defaultBaseURL,
|
|
230
234
|
},
|
|
231
235
|
timeoutMs: 600_000,
|
|
232
236
|
},
|
|
@@ -53,7 +53,7 @@ async function sessionLogs(directory) {
|
|
|
53
53
|
for (const entry of await readdir(directory, { withFileTypes: true }).catch(() => [])) {
|
|
54
54
|
const path = join(directory, entry.name)
|
|
55
55
|
if (entry.isDirectory()) result.push(...await sessionLogs(path))
|
|
56
|
-
else if (entry.isFile() && (
|
|
56
|
+
else if (entry.isFile() && /^session(?:\.v\d+)?\.jsonl(?:\.zstd)?$/u.test(entry.name)) result.push(path)
|
|
57
57
|
}
|
|
58
58
|
return result
|
|
59
59
|
}
|
|
@@ -71,8 +71,9 @@ function resultValue(event) {
|
|
|
71
71
|
const outer = event?.data?.message?.content
|
|
72
72
|
if (!Array.isArray(outer)) return undefined
|
|
73
73
|
for (const block of outer) {
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
// Session v4 stores tool-role text directly; earlier logs wrap it.
|
|
75
|
+
const contents = block?.type === 'tool-result' && Array.isArray(block.content) ? block.content : [block]
|
|
76
|
+
for (const content of contents) {
|
|
76
77
|
if (content?.type !== 'text' || typeof content.text !== 'string') continue
|
|
77
78
|
try { return JSON.parse(content.text) }
|
|
78
79
|
catch { return undefined }
|
package/scripts/validate.mjs
CHANGED
|
File without changes
|
package/src/client/index.tsx
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState, useSyncExternalStore, type ReactNode } from 'react'
|
|
4
4
|
import { Button, Input } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
5
|
-
import type { ClientContext } from '@deepseek-ai/
|
|
5
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis'
|
|
6
|
+
import type {} from '@deepseek-ai/dsh-client-ui-renderer/client'
|
|
7
|
+
import type {} from '@deepseek-ai/dsh-client-connection/client'
|
|
6
8
|
import type {} from '@deepseek-ai/dsh-api-remotes/client'
|
|
7
9
|
import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
|
|
8
10
|
import type {} from '@deepseek-ai/dsh-client-locale/client'
|
|
@@ -561,7 +563,7 @@ export function apply(ctx: ClientContext): void {
|
|
|
561
563
|
const controller = new ComputerUseSettingsController()
|
|
562
564
|
ctx.effect(() => {
|
|
563
565
|
const disposers = [
|
|
564
|
-
ctx.remote.$on('settings/document-updated', ns => { if (ns === NS) controller.refreshIfLoaded() }),
|
|
566
|
+
ctx.remote.$on('settings/document-updated', (ns: string) => { if (ns === NS) controller.refreshIfLoaded() }),
|
|
565
567
|
ctx.on('connection/reset', () => { controller.refreshIfLoaded() }),
|
|
566
568
|
]
|
|
567
569
|
return () => { for (const dispose of disposers) dispose() }
|
package/src/config.ts
CHANGED
|
@@ -2,11 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
import z from '@deepseek-ai/schemastery'
|
|
4
4
|
import type Schema from '@deepseek-ai/schemastery'
|
|
5
|
-
import
|
|
5
|
+
import * as SettingsModule from '@deepseek-ai/dsh-settings'
|
|
6
|
+
import type { SettingsNamespace } from '@deepseek-ai/dsh-settings'
|
|
6
7
|
import { ComputerUseError } from './errors.ts'
|
|
7
8
|
|
|
8
|
-
/**
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Settings document namespace owned by this package.
|
|
11
|
+
*
|
|
12
|
+
* DSH 0.1.5 removed the `settingsNamespace()` runtime brander: the namespace is
|
|
13
|
+
* a compile-time-only brand and the official convention is a plain string
|
|
14
|
+
* literal (0.1.5's own `ui-chat` client does `const CHAT_SETTINGS_NAMESPACE =
|
|
15
|
+
* "ui-chat"`). The literal below satisfies the 0.1.5 grammar
|
|
16
|
+
* `/^[a-z][a-z0-9-]*$/` and, because `SettingsNamespace` is a type-only export
|
|
17
|
+
* in every supported release, the cast is erased at build time — the value that
|
|
18
|
+
* reaches the host is exactly `'computer-use'`.
|
|
19
|
+
*/
|
|
20
|
+
export const COMPUTER_USE_SETTINGS_NAMESPACE: SettingsNamespace = 'computer-use' as SettingsNamespace
|
|
10
21
|
|
|
11
22
|
/** One persisted application grant. Wildcards are intentionally unsupported. */
|
|
12
23
|
export interface ComputerUseAppGrant {
|
|
@@ -50,23 +61,44 @@ export interface ComputerUseConfig {
|
|
|
50
61
|
grants?: ComputerUseAppGrant[]
|
|
51
62
|
}
|
|
52
63
|
|
|
64
|
+
/** DSH 0.1.7 derives editable Settings fields from volatile Config fields.
|
|
65
|
+
* Legacy Settings can coexist with a newer Schemastery installation. Only
|
|
66
|
+
* enable references when the host Settings service uses Config-derived forms.
|
|
67
|
+
*/
|
|
68
|
+
function live<T>(field: Schema<T>): Schema<T> {
|
|
69
|
+
const candidate = field as Schema<T> & { volatile?: () => Schema<T> }
|
|
70
|
+
return 'SettingsForms' in SettingsModule && typeof candidate.volatile === 'function'
|
|
71
|
+
? candidate.volatile() as unknown as Schema<T>
|
|
72
|
+
: field
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Read the newer Loader's stable references without changing the legacy shape. */
|
|
76
|
+
export function readComputerUseConfig(config: ComputerUseConfig): ComputerUseConfig {
|
|
77
|
+
return Object.fromEntries(Object.entries(config).map(([key, value]) => [
|
|
78
|
+
key,
|
|
79
|
+
value !== null && typeof value === 'object' && 'get' in value && typeof value.get === 'function'
|
|
80
|
+
? value.get()
|
|
81
|
+
: value,
|
|
82
|
+
])) as ComputerUseConfig
|
|
83
|
+
}
|
|
84
|
+
|
|
53
85
|
/** Configuration schema used by Cordis and the Settings provider. */
|
|
54
86
|
export const Config: Schema<ComputerUseConfig> = z.object({
|
|
55
|
-
observationTtlMs: z.number().default(0),
|
|
56
|
-
confirmationTtlMs: z.number().default(300000),
|
|
57
|
-
actionTimeoutMs: z.number().default(15000),
|
|
58
|
-
settleMs: z.number().default(250),
|
|
59
|
-
maxSettleMs: z.number().default(5000),
|
|
60
|
-
maxNodes: z.number().default(500),
|
|
61
|
-
maxDepth: z.number().default(14),
|
|
62
|
-
maxTextBytes: z.number().default(64000),
|
|
63
|
-
maxScreenshotBytes: z.number().default(33554432),
|
|
64
|
-
artifactRoot: z.string().default('.dsh-computer-use/artifacts'),
|
|
65
|
-
helper: z.object({
|
|
87
|
+
observationTtlMs: live(z.number().default(0)),
|
|
88
|
+
confirmationTtlMs: live(z.number().default(300000)),
|
|
89
|
+
actionTimeoutMs: live(z.number().default(15000)),
|
|
90
|
+
settleMs: live(z.number().default(250)),
|
|
91
|
+
maxSettleMs: live(z.number().default(5000)),
|
|
92
|
+
maxNodes: live(z.number().default(500)),
|
|
93
|
+
maxDepth: live(z.number().default(14)),
|
|
94
|
+
maxTextBytes: live(z.number().default(64000)),
|
|
95
|
+
maxScreenshotBytes: live(z.number().default(33554432)),
|
|
96
|
+
artifactRoot: live(z.string().default('.dsh-computer-use/artifacts')),
|
|
97
|
+
helper: live(z.object({
|
|
66
98
|
path: z.string(),
|
|
67
99
|
allowSourceBuild: z.boolean().default(false),
|
|
68
|
-
}),
|
|
69
|
-
interaction: z.object({
|
|
100
|
+
})),
|
|
101
|
+
interaction: live(z.object({
|
|
70
102
|
focusPolicy: z.union(['preserve', 'activate']).default('preserve'),
|
|
71
103
|
keyboardPolicy: z.union(['preserve', 'activate']).default('preserve'),
|
|
72
104
|
pointerInputPolicy: z.union(['deny', 'targeted']).default('targeted'),
|
|
@@ -76,14 +108,14 @@ export const Config: Schema<ComputerUseConfig> = z.object({
|
|
|
76
108
|
cursorAccelerationPxPerSecondSquared: z.number().default(6000),
|
|
77
109
|
cursorClickDelayMs: z.number().default(90),
|
|
78
110
|
cursorAutoHideMs: z.number().default(0),
|
|
79
|
-
}),
|
|
80
|
-
allowAllApps: z.boolean().default(false),
|
|
81
|
-
grants: z.array(z.object({
|
|
111
|
+
})),
|
|
112
|
+
allowAllApps: live(z.boolean().default(false)),
|
|
113
|
+
grants: live(z.array(z.object({
|
|
82
114
|
bundleId: z.string(),
|
|
83
115
|
read: z.boolean().default(false),
|
|
84
116
|
control: z.boolean().default(false),
|
|
85
|
-
})).default([]),
|
|
86
|
-
})
|
|
117
|
+
})).default([])),
|
|
118
|
+
}) as unknown as Schema<ComputerUseConfig>
|
|
87
119
|
|
|
88
120
|
/** Fully defaulted configuration consumed at runtime. */
|
|
89
121
|
export interface ResolvedComputerUseConfig {
|