@anionex/dsh-computer-use 0.2.1 → 0.3.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 (50) hide show
  1. package/README.md +13 -7
  2. package/README.zh.md +13 -7
  3. package/cordis.patch.yml +3 -0
  4. package/docs/interaction-policy.md +6 -4
  5. package/docs/interaction-policy.zh.md +7 -5
  6. package/lib/client.js +31 -11
  7. package/lib/client.js.map +1 -1
  8. package/lib/config.js +13 -3
  9. package/lib/config.js.map +1 -1
  10. package/lib/providers/macos.js +79 -14
  11. package/lib/providers/macos.js.map +1 -1
  12. package/lib/providers/native-helper.js +240 -17
  13. package/lib/providers/native-helper.js.map +1 -1
  14. package/lib/providers/unsupported.js +6 -0
  15. package/lib/providers/unsupported.js.map +1 -1
  16. package/lib/service.js +74 -32
  17. package/lib/service.js.map +1 -1
  18. package/lib/types/backend.d.ts +18 -3
  19. package/lib/types/backend.d.ts.map +1 -1
  20. package/lib/types/client/index.d.ts +7 -2
  21. package/lib/types/client/index.d.ts.map +1 -1
  22. package/lib/types/config.d.ts +7 -1
  23. package/lib/types/config.d.ts.map +1 -1
  24. package/lib/types/providers/macos.d.ts +8 -2
  25. package/lib/types/providers/macos.d.ts.map +1 -1
  26. package/lib/types/providers/native-helper.d.ts +9 -1
  27. package/lib/types/providers/native-helper.d.ts.map +1 -1
  28. package/lib/types/providers/unsupported.d.ts +6 -2
  29. package/lib/types/providers/unsupported.d.ts.map +1 -1
  30. package/lib/types/service.d.ts.map +1 -1
  31. package/native/macos/Sources/Fixture/main.swift +46 -0
  32. package/native/macos/Sources/Helper/CursorMotion.swift +67 -0
  33. package/native/macos/Sources/Helper/CursorOverlay.swift +334 -60
  34. package/native/macos/Sources/Helper/TargetedPointer.swift +23 -10
  35. package/native/macos/Sources/Helper/main.swift +80 -5
  36. package/native/macos/Sources/Monitor/main.swift +15 -1
  37. package/native/macos/bin/dsh-computer-use-helper +0 -0
  38. package/native/macos/manifest.json +3 -3
  39. package/package.json +16 -18
  40. package/scripts/build-client.mjs +0 -0
  41. package/scripts/build-native.mjs +0 -0
  42. package/scripts/model-e2e.mjs +0 -0
  43. package/scripts/validate.mjs +0 -0
  44. package/src/backend.ts +18 -3
  45. package/src/client/index.tsx +50 -13
  46. package/src/config.ts +20 -4
  47. package/src/providers/macos.ts +102 -14
  48. package/src/providers/native-helper.ts +234 -17
  49. package/src/providers/unsupported.ts +11 -1
  50. package/src/service.ts +100 -37
@@ -70,6 +70,9 @@ private final class FixtureDelegate: NSObject, NSApplicationDelegate {
70
70
  private var insertedHarmlessSibling = false
71
71
  private var keyMonitor: Any?
72
72
  private var reorderTimer: Timer?
73
+ private var activationTimer: Timer?
74
+ private var activationReleaseTimer: Timer?
75
+ private var activationHoldUntil: Date?
73
76
  private var pointerClickCount = 0
74
77
  private var pointerScrollCount = 0
75
78
  private var pointerDragCount = 0
@@ -80,6 +83,9 @@ private final class FixtureDelegate: NSObject, NSApplicationDelegate {
80
83
  private var activationCount = 0
81
84
  private let transcriptPath: String?
82
85
  private let reorderTriggerPath: String?
86
+ private let activationTriggerPath: String?
87
+ private let activationReleaseTriggerPath: String?
88
+ private let activationOnly: Bool
83
89
  private let launchInBackground: Bool
84
90
 
85
91
  override init() {
@@ -94,6 +100,17 @@ private final class FixtureDelegate: NSObject, NSApplicationDelegate {
94
100
  } else {
95
101
  reorderTriggerPath = nil
96
102
  }
103
+ if let index = arguments.firstIndex(of: "--activation-trigger"), arguments.indices.contains(index + 1) {
104
+ activationTriggerPath = arguments[index + 1]
105
+ } else {
106
+ activationTriggerPath = nil
107
+ }
108
+ if let index = arguments.firstIndex(of: "--activation-release-trigger"), arguments.indices.contains(index + 1) {
109
+ activationReleaseTriggerPath = arguments[index + 1]
110
+ } else {
111
+ activationReleaseTriggerPath = nil
112
+ }
113
+ activationOnly = arguments.contains("--activation-only")
97
114
  launchInBackground = arguments.contains("--background")
98
115
  super.init()
99
116
  }
@@ -113,6 +130,9 @@ private final class FixtureDelegate: NSObject, NSApplicationDelegate {
113
130
  window.makeKeyAndOrderFront(nil)
114
131
  }
115
132
  writeTranscript(event: "ready")
133
+ if activationOnly {
134
+ activationHoldUntil = Date().addingTimeInterval(5)
135
+ }
116
136
  if let reorderTriggerPath {
117
137
  reorderTimer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { [weak self] timer in
118
138
  guard FileManager.default.fileExists(atPath: reorderTriggerPath) else { return }
@@ -120,6 +140,30 @@ private final class FixtureDelegate: NSObject, NSApplicationDelegate {
120
140
  self?.insertHarmlessSibling()
121
141
  }
122
142
  }
143
+ if activationTriggerPath != nil || activationOnly {
144
+ activationTimer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { [weak self] _ in
145
+ guard let self else { return }
146
+ if let activationTriggerPath,
147
+ FileManager.default.fileExists(atPath: activationTriggerPath) {
148
+ try? FileManager.default.removeItem(atPath: activationTriggerPath)
149
+ self.activationHoldUntil = Date().addingTimeInterval(5)
150
+ }
151
+ guard let holdUntil = self.activationHoldUntil else { return }
152
+ guard Date() < holdUntil else {
153
+ self.activationHoldUntil = nil
154
+ return
155
+ }
156
+ NSApplication.shared.activate(ignoringOtherApps: true)
157
+ self.window.makeKeyAndOrderFront(nil)
158
+ }
159
+ }
160
+ if let activationReleaseTriggerPath {
161
+ activationReleaseTimer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { [weak self] _ in
162
+ guard FileManager.default.fileExists(atPath: activationReleaseTriggerPath) else { return }
163
+ try? FileManager.default.removeItem(atPath: activationReleaseTriggerPath)
164
+ self?.activationHoldUntil = nil
165
+ }
166
+ }
123
167
  }
124
168
 
125
169
  func applicationDidBecomeActive(_ notification: Notification) {
@@ -130,6 +174,8 @@ private final class FixtureDelegate: NSObject, NSApplicationDelegate {
130
174
  func applicationWillTerminate(_ notification: Notification) {
131
175
  if let keyMonitor { NSEvent.removeMonitor(keyMonitor) }
132
176
  reorderTimer?.invalidate()
177
+ activationTimer?.invalidate()
178
+ activationReleaseTimer?.invalidate()
133
179
  }
134
180
 
135
181
  func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
@@ -0,0 +1,67 @@
1
+ import CoreGraphics
2
+ import Foundation
3
+
4
+ private func rawCursorMotionDuration(distance: Double, speed: Double, acceleration: Double) -> Double {
5
+ let accelerationDistance = speed * speed / acceleration
6
+ if distance <= accelerationDistance {
7
+ return 2 * sqrt(distance / acceleration)
8
+ }
9
+ return 2 * speed / acceleration + (distance - accelerationDistance) / speed
10
+ }
11
+
12
+ func cursorMotionDuration(distance: Double, speed: Double, acceleration: Double) -> Double {
13
+ min(2, max(0.048, rawCursorMotionDuration(distance: distance, speed: speed, acceleration: acceleration)))
14
+ }
15
+
16
+ func cursorMotionFraction(
17
+ progress: Double,
18
+ distance: Double,
19
+ speed: Double,
20
+ acceleration: Double
21
+ ) -> Double {
22
+ guard distance > 0 else { return 1 }
23
+ let rawDuration = rawCursorMotionDuration(distance: distance, speed: speed, acceleration: acceleration)
24
+ let elapsed = min(1, max(0, progress)) * rawDuration
25
+ let accelerationTime = min(speed / acceleration, rawDuration / 2)
26
+ let peakSpeed = acceleration * accelerationTime
27
+ let cruiseTime = max(0, rawDuration - 2 * accelerationTime)
28
+ let accelerationDistance = 0.5 * acceleration * accelerationTime * accelerationTime
29
+ let traveled: Double
30
+ if elapsed <= accelerationTime {
31
+ traveled = 0.5 * acceleration * elapsed * elapsed
32
+ } else if elapsed <= accelerationTime + cruiseTime {
33
+ traveled = accelerationDistance + peakSpeed * (elapsed - accelerationTime)
34
+ } else {
35
+ let remaining = rawDuration - elapsed
36
+ traveled = distance - 0.5 * acceleration * remaining * remaining
37
+ }
38
+ return min(1, max(0, traveled / distance))
39
+ }
40
+
41
+ func cursorMotionPoint(from origin: CGPoint, to target: CGPoint, fraction: Double) -> CGPoint {
42
+ let distance = hypot(target.x - origin.x, target.y - origin.y)
43
+ guard distance > 0 else { return target }
44
+ let delta = CGPoint(x: target.x - origin.x, y: target.y - origin.y)
45
+ let directionSign: CGFloat = Int(abs(origin.x + origin.y + target.x + target.y)) % 2 == 0 ? 1 : -1
46
+ let bend = min(18, distance * 0.06) * directionSign
47
+ let normal = CGPoint(x: -delta.y / distance, y: delta.x / distance)
48
+ let control1 = CGPoint(
49
+ x: origin.x + delta.x * 0.32 + normal.x * bend,
50
+ y: origin.y + delta.y * 0.32 + normal.y * bend
51
+ )
52
+ let control2 = CGPoint(
53
+ x: origin.x + delta.x * 0.72 + normal.x * bend * 0.45,
54
+ y: origin.y + delta.y * 0.72 + normal.y * bend * 0.45
55
+ )
56
+ let inverse = 1 - fraction
57
+ return CGPoint(
58
+ x: inverse * inverse * inverse * origin.x
59
+ + 3 * inverse * inverse * fraction * control1.x
60
+ + 3 * inverse * fraction * fraction * control2.x
61
+ + fraction * fraction * fraction * target.x,
62
+ y: inverse * inverse * inverse * origin.y
63
+ + 3 * inverse * inverse * fraction * control1.y
64
+ + 3 * inverse * fraction * fraction * control2.y
65
+ + fraction * fraction * fraction * target.y
66
+ )
67
+ }