@anionex/dsh-computer-use 0.1.0
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/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +307 -0
- package/README.zh.md +307 -0
- package/assets/computer-use-fixture.png +0 -0
- package/assets/cursor.png +0 -0
- package/cordis.patch.yml +13 -0
- package/docs/interaction-policy.i18n.yaml +4 -0
- package/docs/interaction-policy.md +140 -0
- package/docs/interaction-policy.zh.md +140 -0
- package/lib/approval-policy.js +12 -0
- package/lib/approval-policy.js.map +1 -0
- package/lib/artifacts.js +79 -0
- package/lib/artifacts.js.map +1 -0
- package/lib/backend.js +3 -0
- package/lib/backend.js.map +1 -0
- package/lib/client.js +349 -0
- package/lib/client.js.map +1 -0
- package/lib/config.js +120 -0
- package/lib/config.js.map +1 -0
- package/lib/confirmations.js +99 -0
- package/lib/confirmations.js.map +1 -0
- package/lib/diff.js +58 -0
- package/lib/diff.js.map +1 -0
- package/lib/errors.js +22 -0
- package/lib/errors.js.map +1 -0
- package/lib/exposure.js +231 -0
- package/lib/exposure.js.map +1 -0
- package/lib/index.js +56 -0
- package/lib/index.js.map +1 -0
- package/lib/leases.js +259 -0
- package/lib/leases.js.map +1 -0
- package/lib/providers/macos.js +148 -0
- package/lib/providers/macos.js.map +1 -0
- package/lib/providers/native-helper.js +356 -0
- package/lib/providers/native-helper.js.map +1 -0
- package/lib/service.js +480 -0
- package/lib/service.js.map +1 -0
- package/lib/skill.js +119 -0
- package/lib/skill.js.map +1 -0
- package/lib/target-resolver.js +180 -0
- package/lib/target-resolver.js.map +1 -0
- package/lib/tools.js +493 -0
- package/lib/tools.js.map +1 -0
- package/lib/types/approval-policy.d.ts +13 -0
- package/lib/types/approval-policy.d.ts.map +1 -0
- package/lib/types/artifacts.d.ts +10 -0
- package/lib/types/artifacts.d.ts.map +1 -0
- package/lib/types/backend.d.ts +94 -0
- package/lib/types/backend.d.ts.map +1 -0
- package/lib/types/client/index.d.ts +78 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/config.d.ts +75 -0
- package/lib/types/config.d.ts.map +1 -0
- package/lib/types/confirmations.d.ts +23 -0
- package/lib/types/confirmations.d.ts.map +1 -0
- package/lib/types/diff.d.ts +5 -0
- package/lib/types/diff.d.ts.map +1 -0
- package/lib/types/errors.d.ts +15 -0
- package/lib/types/errors.d.ts.map +1 -0
- package/lib/types/exposure.d.ts +30 -0
- package/lib/types/exposure.d.ts.map +1 -0
- package/lib/types/index.d.ts +21 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/leases.d.ts +80 -0
- package/lib/types/leases.d.ts.map +1 -0
- package/lib/types/providers/macos.d.ts +15 -0
- package/lib/types/providers/macos.d.ts.map +1 -0
- package/lib/types/providers/native-helper.d.ts +36 -0
- package/lib/types/providers/native-helper.d.ts.map +1 -0
- package/lib/types/service.d.ts +52 -0
- package/lib/types/service.d.ts.map +1 -0
- package/lib/types/skill.d.ts +9 -0
- package/lib/types/skill.d.ts.map +1 -0
- package/lib/types/target-resolver.d.ts +38 -0
- package/lib/types/target-resolver.d.ts.map +1 -0
- package/lib/types/tools.d.ts +6 -0
- package/lib/types/tools.d.ts.map +1 -0
- package/lib/types/types.d.ts +246 -0
- package/lib/types/types.d.ts.map +1 -0
- package/lib/types/web.d.ts +32 -0
- package/lib/types/web.d.ts.map +1 -0
- package/lib/types.js +9 -0
- package/lib/types.js.map +1 -0
- package/lib/web.js +163 -0
- package/lib/web.js.map +1 -0
- package/native/macos/Sources/Fixture/main.swift +366 -0
- package/native/macos/Sources/Helper/CursorImage.swift +11 -0
- package/native/macos/Sources/Helper/CursorOverlay.swift +488 -0
- package/native/macos/Sources/Helper/TargetedPointer.swift +314 -0
- package/native/macos/Sources/Helper/main.swift +1140 -0
- package/native/macos/Sources/Monitor/main.swift +139 -0
- package/native/macos/bin/dsh-computer-use-helper +0 -0
- package/native/macos/manifest.json +14 -0
- package/package.json +128 -0
- package/scripts/build-client.mjs +24 -0
- package/scripts/build-native.mjs +158 -0
- package/scripts/check-native.mjs +92 -0
- package/scripts/model-e2e.mjs +291 -0
- package/scripts/session-transcript.mjs +117 -0
- package/scripts/validate.mjs +256 -0
- package/src/approval-policy.ts +16 -0
- package/src/artifacts.ts +93 -0
- package/src/backend.ts +103 -0
- package/src/client/index.tsx +539 -0
- package/src/config.ts +196 -0
- package/src/confirmations.ts +128 -0
- package/src/diff.ts +52 -0
- package/src/errors.ts +39 -0
- package/src/exposure.ts +236 -0
- package/src/index.ts +66 -0
- package/src/leases.ts +337 -0
- package/src/providers/macos.ts +185 -0
- package/src/providers/native-helper.ts +393 -0
- package/src/service.ts +583 -0
- package/src/skill.ts +123 -0
- package/src/target-resolver.ts +237 -0
- package/src/tools.ts +528 -0
- package/src/types.ts +299 -0
- package/src/web.ts +180 -0
package/src/artifacts.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/** Workspace-fenced screenshot artifact allocation and validation. */
|
|
2
|
+
|
|
3
|
+
import { randomUUID } from 'node:crypto'
|
|
4
|
+
import { lstat, mkdir, realpath, stat } from 'node:fs/promises'
|
|
5
|
+
import { basename, isAbsolute, relative, resolve, sep } from 'node:path'
|
|
6
|
+
import type { SessionId } from '@deepseek-ai/dsh-session'
|
|
7
|
+
import { ComputerUseError } from './errors.ts'
|
|
8
|
+
import type { ComputerArtifact } from './types.ts'
|
|
9
|
+
|
|
10
|
+
/** Screenshot description that hands visual analysis to the sibling Vision Toolkit. */
|
|
11
|
+
export const COMPUTER_SCREENSHOT_DESCRIPTION = [
|
|
12
|
+
'Current macOS application window observation.',
|
|
13
|
+
'For OCR, visual grounding, or pixel inspection, load the vision-tools Skill and pass this exact path to vision_glance, vision_ground, vision_detect, or vision_crop;',
|
|
14
|
+
'do not recreate OCR with bash, tesseract, or an ad hoc script.',
|
|
15
|
+
].join(' ')
|
|
16
|
+
|
|
17
|
+
function isWithin(root: string, candidate: string): boolean {
|
|
18
|
+
const rel = relative(root, candidate)
|
|
19
|
+
return rel === '' || (!rel.startsWith(`..${sep}`) && rel !== '..' && !isAbsolute(rel))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function rejectSymlinkComponents(root: string, target: string): Promise<void> {
|
|
23
|
+
const rel = relative(root, target)
|
|
24
|
+
let current = root
|
|
25
|
+
for (const part of rel.split(sep).filter(Boolean)) {
|
|
26
|
+
current = resolve(current, part)
|
|
27
|
+
try {
|
|
28
|
+
const info = await lstat(current)
|
|
29
|
+
if (info.isSymbolicLink()) {
|
|
30
|
+
throw new ComputerUseError('COMPUTER_PROVIDER_FAILURE', `artifact path component must not be a symbolic link: ${part}`)
|
|
31
|
+
}
|
|
32
|
+
} catch (error) {
|
|
33
|
+
if (error instanceof ComputerUseError) throw error
|
|
34
|
+
const code = (error as NodeJS.ErrnoException).code
|
|
35
|
+
if (code !== 'ENOENT') throw error
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Allocate a unique managed PNG path inside the current Session workspace. */
|
|
41
|
+
export async function allocateScreenshotPath(
|
|
42
|
+
workspace: string,
|
|
43
|
+
artifactRoot: string,
|
|
44
|
+
sessionId: SessionId,
|
|
45
|
+
): Promise<string> {
|
|
46
|
+
const realWorkspace = await realpath(workspace)
|
|
47
|
+
const directory = resolve(realWorkspace, artifactRoot, String(sessionId))
|
|
48
|
+
if (!isWithin(realWorkspace, directory)) {
|
|
49
|
+
throw new ComputerUseError('COMPUTER_PROVIDER_FAILURE', 'artifactRoot escapes the Session workspace')
|
|
50
|
+
}
|
|
51
|
+
await rejectSymlinkComponents(realWorkspace, directory)
|
|
52
|
+
await mkdir(directory, { recursive: true, mode: 0o700 })
|
|
53
|
+
const realDirectory = await realpath(directory)
|
|
54
|
+
if (!isWithin(realWorkspace, realDirectory)) {
|
|
55
|
+
throw new ComputerUseError('COMPUTER_PROVIDER_FAILURE', 'artifact directory escaped the Session workspace')
|
|
56
|
+
}
|
|
57
|
+
return resolve(realDirectory, `observation-${randomUUID()}.png`)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Validate a committed screenshot and return its stable model/client descriptor. */
|
|
61
|
+
export async function describeScreenshot(
|
|
62
|
+
path: string,
|
|
63
|
+
width: number,
|
|
64
|
+
height: number,
|
|
65
|
+
maxBytes: number,
|
|
66
|
+
sourceTool: ComputerArtifact['sourceTool'],
|
|
67
|
+
): Promise<ComputerArtifact> {
|
|
68
|
+
const link = await lstat(path).catch((error: unknown) => {
|
|
69
|
+
throw new ComputerUseError('COMPUTER_PROVIDER_FAILURE', 'the screenshot artifact was not created', { cause: error })
|
|
70
|
+
})
|
|
71
|
+
if (link.isSymbolicLink() || !link.isFile()) {
|
|
72
|
+
throw new ComputerUseError('COMPUTER_PROVIDER_FAILURE', 'the screenshot artifact must be a regular non-symbolic-link file')
|
|
73
|
+
}
|
|
74
|
+
const info = await stat(path)
|
|
75
|
+
if (info.size > maxBytes) {
|
|
76
|
+
throw new ComputerUseError('COMPUTER_PROVIDER_FAILURE', `screenshot exceeds maxScreenshotBytes (${info.size} > ${maxBytes})`)
|
|
77
|
+
}
|
|
78
|
+
if (!Number.isInteger(width) || width <= 0 || !Number.isInteger(height) || height <= 0) {
|
|
79
|
+
throw new ComputerUseError('COMPUTER_PROVIDER_FAILURE', 'provider returned invalid screenshot dimensions')
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
path,
|
|
83
|
+
filename: basename(path),
|
|
84
|
+
mimeType: 'image/png',
|
|
85
|
+
kind: 'image',
|
|
86
|
+
description: COMPUTER_SCREENSHOT_DESCRIPTION,
|
|
87
|
+
sourceTool,
|
|
88
|
+
previewIntent: 'image',
|
|
89
|
+
bytes: info.size,
|
|
90
|
+
width,
|
|
91
|
+
height,
|
|
92
|
+
}
|
|
93
|
+
}
|
package/src/backend.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/** Provider-facing backend protocol kept below the public Computer Use Service. */
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
ComputerActionRequest,
|
|
5
|
+
ComputerAppIdentity,
|
|
6
|
+
ComputerAppSelector,
|
|
7
|
+
ComputerAppSummary,
|
|
8
|
+
ComputerElement,
|
|
9
|
+
ComputerPermissionState,
|
|
10
|
+
ComputerRect,
|
|
11
|
+
ComputerScreenshotMode,
|
|
12
|
+
} from './types.ts'
|
|
13
|
+
import type { ResolvedComputerUseConfig } from './config.ts'
|
|
14
|
+
|
|
15
|
+
/** Internal provider evidence never exposed to the model or persisted in Session logs. */
|
|
16
|
+
export interface BackendElement extends Omit<ComputerElement, 'targetHandle'> {
|
|
17
|
+
locator: number[]
|
|
18
|
+
nativeIdentifier?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Raw full-state observation returned by a provider before Service diff projection. */
|
|
22
|
+
export interface BackendObservation {
|
|
23
|
+
app: ComputerAppIdentity
|
|
24
|
+
stateHash: string
|
|
25
|
+
frontmost: boolean
|
|
26
|
+
window?: {
|
|
27
|
+
title?: string
|
|
28
|
+
frame: ComputerRect
|
|
29
|
+
id?: number
|
|
30
|
+
}
|
|
31
|
+
treeText: string
|
|
32
|
+
truncated: boolean
|
|
33
|
+
elements: BackendElement[]
|
|
34
|
+
screenshot?: {
|
|
35
|
+
path: string
|
|
36
|
+
width: number
|
|
37
|
+
height: number
|
|
38
|
+
}
|
|
39
|
+
permissions: {
|
|
40
|
+
accessibility: ComputerPermissionState
|
|
41
|
+
screenRecording: ComputerPermissionState
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Provider limits resolved by the configuration owner before a call. */
|
|
46
|
+
export interface BackendObserveOptions {
|
|
47
|
+
screenshot: ComputerScreenshotMode
|
|
48
|
+
screenshotPath?: string
|
|
49
|
+
maxNodes: number
|
|
50
|
+
maxDepth: number
|
|
51
|
+
maxTextBytes: number
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Action bound to fresh provider state and one exact internally resolved target. */
|
|
55
|
+
export interface BackendActionRequest {
|
|
56
|
+
action: Exclude<ComputerActionRequest, { kind: 'wait' }>
|
|
57
|
+
app: ComputerAppIdentity
|
|
58
|
+
expectedStateHash: string
|
|
59
|
+
interaction: ResolvedComputerUseConfig['interaction']
|
|
60
|
+
element?: BackendElement
|
|
61
|
+
window?: BackendObservation['window']
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Provider action outcome before the Service obtains the mandatory post-action observation. */
|
|
65
|
+
export interface BackendActionResult {
|
|
66
|
+
channel: 'accessibility' | 'coordinates' | 'keyboard'
|
|
67
|
+
activation: 'not-requested' | 'already-frontmost' | 'activated'
|
|
68
|
+
pointerInput: boolean
|
|
69
|
+
pointerRouting: 'none' | 'target-process'
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** One model-selected point or gesture for the non-interactive Agent cursor overlay. */
|
|
73
|
+
export interface BackendCursorAction {
|
|
74
|
+
kind: 'click' | 'scroll' | 'drag'
|
|
75
|
+
from?: { x: number; y: number }
|
|
76
|
+
to: { x: number; y: number }
|
|
77
|
+
/** Exact target identity used to prevent the overlay from lingering over another window. */
|
|
78
|
+
targetPid: number
|
|
79
|
+
targetWindowNumber: number
|
|
80
|
+
targetWindowFrame: ComputerRect
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Health facts obtained without changing permissions. */
|
|
84
|
+
export interface BackendHealth {
|
|
85
|
+
helperVersion: string
|
|
86
|
+
helperSha256: string
|
|
87
|
+
accessibility: ComputerPermissionState
|
|
88
|
+
screenRecording: ComputerPermissionState
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Platform backend used by the provider-independent Service implementation. */
|
|
92
|
+
export interface ComputerUseBackend {
|
|
93
|
+
readonly name: 'macos-ax'
|
|
94
|
+
readonly helperPath: string
|
|
95
|
+
resolveApp(selector: ComputerAppSelector, signal: AbortSignal): Promise<ComputerAppIdentity>
|
|
96
|
+
listApps(signal: AbortSignal): Promise<ComputerAppSummary[]>
|
|
97
|
+
observe(app: ComputerAppIdentity, options: BackendObserveOptions, signal: AbortSignal): Promise<BackendObservation>
|
|
98
|
+
act(request: BackendActionRequest, signal: AbortSignal): Promise<BackendActionResult>
|
|
99
|
+
visualizeCursor(action: BackendCursorAction, phase: 'before' | 'after', signal: AbortSignal): Promise<void>
|
|
100
|
+
dispose(): Promise<void>
|
|
101
|
+
health(signal: AbortSignal): Promise<BackendHealth>
|
|
102
|
+
openSettings(kind: 'accessibility' | 'screen-recording', signal: AbortSignal): Promise<void>
|
|
103
|
+
}
|