@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.
Files changed (120) hide show
  1. package/LICENSE +21 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +307 -0
  4. package/README.zh.md +307 -0
  5. package/assets/computer-use-fixture.png +0 -0
  6. package/assets/cursor.png +0 -0
  7. package/cordis.patch.yml +13 -0
  8. package/docs/interaction-policy.i18n.yaml +4 -0
  9. package/docs/interaction-policy.md +140 -0
  10. package/docs/interaction-policy.zh.md +140 -0
  11. package/lib/approval-policy.js +12 -0
  12. package/lib/approval-policy.js.map +1 -0
  13. package/lib/artifacts.js +79 -0
  14. package/lib/artifacts.js.map +1 -0
  15. package/lib/backend.js +3 -0
  16. package/lib/backend.js.map +1 -0
  17. package/lib/client.js +349 -0
  18. package/lib/client.js.map +1 -0
  19. package/lib/config.js +120 -0
  20. package/lib/config.js.map +1 -0
  21. package/lib/confirmations.js +99 -0
  22. package/lib/confirmations.js.map +1 -0
  23. package/lib/diff.js +58 -0
  24. package/lib/diff.js.map +1 -0
  25. package/lib/errors.js +22 -0
  26. package/lib/errors.js.map +1 -0
  27. package/lib/exposure.js +231 -0
  28. package/lib/exposure.js.map +1 -0
  29. package/lib/index.js +56 -0
  30. package/lib/index.js.map +1 -0
  31. package/lib/leases.js +259 -0
  32. package/lib/leases.js.map +1 -0
  33. package/lib/providers/macos.js +148 -0
  34. package/lib/providers/macos.js.map +1 -0
  35. package/lib/providers/native-helper.js +356 -0
  36. package/lib/providers/native-helper.js.map +1 -0
  37. package/lib/service.js +480 -0
  38. package/lib/service.js.map +1 -0
  39. package/lib/skill.js +119 -0
  40. package/lib/skill.js.map +1 -0
  41. package/lib/target-resolver.js +180 -0
  42. package/lib/target-resolver.js.map +1 -0
  43. package/lib/tools.js +493 -0
  44. package/lib/tools.js.map +1 -0
  45. package/lib/types/approval-policy.d.ts +13 -0
  46. package/lib/types/approval-policy.d.ts.map +1 -0
  47. package/lib/types/artifacts.d.ts +10 -0
  48. package/lib/types/artifacts.d.ts.map +1 -0
  49. package/lib/types/backend.d.ts +94 -0
  50. package/lib/types/backend.d.ts.map +1 -0
  51. package/lib/types/client/index.d.ts +78 -0
  52. package/lib/types/client/index.d.ts.map +1 -0
  53. package/lib/types/config.d.ts +75 -0
  54. package/lib/types/config.d.ts.map +1 -0
  55. package/lib/types/confirmations.d.ts +23 -0
  56. package/lib/types/confirmations.d.ts.map +1 -0
  57. package/lib/types/diff.d.ts +5 -0
  58. package/lib/types/diff.d.ts.map +1 -0
  59. package/lib/types/errors.d.ts +15 -0
  60. package/lib/types/errors.d.ts.map +1 -0
  61. package/lib/types/exposure.d.ts +30 -0
  62. package/lib/types/exposure.d.ts.map +1 -0
  63. package/lib/types/index.d.ts +21 -0
  64. package/lib/types/index.d.ts.map +1 -0
  65. package/lib/types/leases.d.ts +80 -0
  66. package/lib/types/leases.d.ts.map +1 -0
  67. package/lib/types/providers/macos.d.ts +15 -0
  68. package/lib/types/providers/macos.d.ts.map +1 -0
  69. package/lib/types/providers/native-helper.d.ts +36 -0
  70. package/lib/types/providers/native-helper.d.ts.map +1 -0
  71. package/lib/types/service.d.ts +52 -0
  72. package/lib/types/service.d.ts.map +1 -0
  73. package/lib/types/skill.d.ts +9 -0
  74. package/lib/types/skill.d.ts.map +1 -0
  75. package/lib/types/target-resolver.d.ts +38 -0
  76. package/lib/types/target-resolver.d.ts.map +1 -0
  77. package/lib/types/tools.d.ts +6 -0
  78. package/lib/types/tools.d.ts.map +1 -0
  79. package/lib/types/types.d.ts +246 -0
  80. package/lib/types/types.d.ts.map +1 -0
  81. package/lib/types/web.d.ts +32 -0
  82. package/lib/types/web.d.ts.map +1 -0
  83. package/lib/types.js +9 -0
  84. package/lib/types.js.map +1 -0
  85. package/lib/web.js +163 -0
  86. package/lib/web.js.map +1 -0
  87. package/native/macos/Sources/Fixture/main.swift +366 -0
  88. package/native/macos/Sources/Helper/CursorImage.swift +11 -0
  89. package/native/macos/Sources/Helper/CursorOverlay.swift +488 -0
  90. package/native/macos/Sources/Helper/TargetedPointer.swift +314 -0
  91. package/native/macos/Sources/Helper/main.swift +1140 -0
  92. package/native/macos/Sources/Monitor/main.swift +139 -0
  93. package/native/macos/bin/dsh-computer-use-helper +0 -0
  94. package/native/macos/manifest.json +14 -0
  95. package/package.json +128 -0
  96. package/scripts/build-client.mjs +24 -0
  97. package/scripts/build-native.mjs +158 -0
  98. package/scripts/check-native.mjs +92 -0
  99. package/scripts/model-e2e.mjs +291 -0
  100. package/scripts/session-transcript.mjs +117 -0
  101. package/scripts/validate.mjs +256 -0
  102. package/src/approval-policy.ts +16 -0
  103. package/src/artifacts.ts +93 -0
  104. package/src/backend.ts +103 -0
  105. package/src/client/index.tsx +539 -0
  106. package/src/config.ts +196 -0
  107. package/src/confirmations.ts +128 -0
  108. package/src/diff.ts +52 -0
  109. package/src/errors.ts +39 -0
  110. package/src/exposure.ts +236 -0
  111. package/src/index.ts +66 -0
  112. package/src/leases.ts +337 -0
  113. package/src/providers/macos.ts +185 -0
  114. package/src/providers/native-helper.ts +393 -0
  115. package/src/service.ts +583 -0
  116. package/src/skill.ts +123 -0
  117. package/src/target-resolver.ts +237 -0
  118. package/src/tools.ts +528 -0
  119. package/src/types.ts +299 -0
  120. package/src/web.ts +180 -0
@@ -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
+ }