@zhanguym/chatroom 1.0.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 (223) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +52 -0
  3. package/assets/chatroom.svg +15 -0
  4. package/chatroom.config.example.json +24 -0
  5. package/dist/app/application.d.ts +20 -0
  6. package/dist/app/application.js +75 -0
  7. package/dist/app/event-bus.d.ts +25 -0
  8. package/dist/app/event-bus.js +11 -0
  9. package/dist/app/external-access-registry.d.ts +17 -0
  10. package/dist/app/external-access-registry.js +41 -0
  11. package/dist/app/lifecycle.d.ts +14 -0
  12. package/dist/app/lifecycle.js +130 -0
  13. package/dist/auth/auth-service.d.ts +88 -0
  14. package/dist/auth/auth-service.js +221 -0
  15. package/dist/auth/ingress-policy.d.ts +23 -0
  16. package/dist/auth/ingress-policy.js +61 -0
  17. package/dist/auth/passkey-service.d.ts +39 -0
  18. package/dist/auth/passkey-service.js +145 -0
  19. package/dist/cli/index.d.ts +2 -0
  20. package/dist/cli/index.js +10 -0
  21. package/dist/cli/run-cli.d.ts +1 -0
  22. package/dist/cli/run-cli.js +41 -0
  23. package/dist/config/load-config.d.ts +7 -0
  24. package/dist/config/load-config.js +197 -0
  25. package/dist/config/platform-paths.d.ts +7 -0
  26. package/dist/config/platform-paths.js +34 -0
  27. package/dist/config/types.d.ts +24 -0
  28. package/dist/config/types.js +1 -0
  29. package/dist/core/auth/passkey-repository.d.ts +18 -0
  30. package/dist/core/auth/passkey-repository.js +1 -0
  31. package/dist/core/auth/repository.d.ts +36 -0
  32. package/dist/core/auth/repository.js +1 -0
  33. package/dist/core/auth/session-repository.d.ts +6 -0
  34. package/dist/core/auth/session-repository.js +1 -0
  35. package/dist/core/errors/chatroom-error.d.ts +9 -0
  36. package/dist/core/errors/chatroom-error.js +31 -0
  37. package/dist/core/logging/types.d.ts +31 -0
  38. package/dist/core/logging/types.js +1 -0
  39. package/dist/core/operations/bounded-value.d.ts +6 -0
  40. package/dist/core/operations/bounded-value.js +57 -0
  41. package/dist/core/operations/redactor.d.ts +4 -0
  42. package/dist/core/operations/redactor.js +69 -0
  43. package/dist/core/operations/repository.d.ts +18 -0
  44. package/dist/core/operations/repository.js +1 -0
  45. package/dist/core/operations/types.d.ts +28 -0
  46. package/dist/core/operations/types.js +8 -0
  47. package/dist/core/runtime/child-environment.d.ts +1 -0
  48. package/dist/core/runtime/child-environment.js +39 -0
  49. package/dist/core/runtime/command-runner.d.ts +16 -0
  50. package/dist/core/runtime/command-runner.js +48 -0
  51. package/dist/core/runtime/identity.d.ts +2 -0
  52. package/dist/core/runtime/identity.js +9 -0
  53. package/dist/infrastructure/database/app-database.d.ts +6 -0
  54. package/dist/infrastructure/database/app-database.js +22 -0
  55. package/dist/infrastructure/database/mcp-tool-settings-repository.d.ts +8 -0
  56. package/dist/infrastructure/database/mcp-tool-settings-repository.js +22 -0
  57. package/dist/infrastructure/database/oauth-repository.d.ts +20 -0
  58. package/dist/infrastructure/database/oauth-repository.js +140 -0
  59. package/dist/infrastructure/database/operation-repository.d.ts +16 -0
  60. package/dist/infrastructure/database/operation-repository.js +120 -0
  61. package/dist/infrastructure/database/passkey-repository.d.ts +11 -0
  62. package/dist/infrastructure/database/passkey-repository.js +57 -0
  63. package/dist/infrastructure/database/schema.d.ts +1 -0
  64. package/dist/infrastructure/database/schema.js +90 -0
  65. package/dist/infrastructure/database/web-session-repository.d.ts +10 -0
  66. package/dist/infrastructure/database/web-session-repository.js +26 -0
  67. package/dist/infrastructure/http/http-server.d.ts +28 -0
  68. package/dist/infrastructure/http/http-server.js +146 -0
  69. package/dist/infrastructure/http/ingress-middleware.d.ts +6 -0
  70. package/dist/infrastructure/http/ingress-middleware.js +58 -0
  71. package/dist/infrastructure/logging/file-log-store.d.ts +9 -0
  72. package/dist/infrastructure/logging/file-log-store.js +173 -0
  73. package/dist/infrastructure/logging/system-log.d.ts +22 -0
  74. package/dist/infrastructure/logging/system-log.js +117 -0
  75. package/dist/mcp/server/create-mcp-server.d.ts +3 -0
  76. package/dist/mcp/server/create-mcp-server.js +16 -0
  77. package/dist/mcp/server/plugin-mcp-registrar.d.ts +31 -0
  78. package/dist/mcp/server/plugin-mcp-registrar.js +70 -0
  79. package/dist/mcp/server/request-context.d.ts +3 -0
  80. package/dist/mcp/server/request-context.js +8 -0
  81. package/dist/mcp/server/tool-control.d.ts +31 -0
  82. package/dist/mcp/server/tool-control.js +76 -0
  83. package/dist/mcp/server/tool-support.d.ts +44 -0
  84. package/dist/mcp/server/tool-support.js +72 -0
  85. package/dist/native/linux/chatroom-computer-helper +0 -0
  86. package/dist/native/macos/ChatRoomComputerHelper.app/Contents/Info.plist +14 -0
  87. package/dist/native/macos/ChatRoomComputerHelper.app/Contents/MacOS/chatroom-computer-helper +0 -0
  88. package/dist/native/macos/ChatRoomComputerHelper.app/Contents/_CodeSignature/CodeResources +115 -0
  89. package/dist/native/windows/chatroom-computer-helper.exe +0 -0
  90. package/dist/operations/operation-log.d.ts +24 -0
  91. package/dist/operations/operation-log.js +98 -0
  92. package/dist/plugins/cloud/api-client.d.ts +50 -0
  93. package/dist/plugins/cloud/api-client.js +112 -0
  94. package/dist/plugins/cloud/controller.d.ts +58 -0
  95. package/dist/plugins/cloud/controller.js +425 -0
  96. package/dist/plugins/cloud/device-proof.d.ts +9 -0
  97. package/dist/plugins/cloud/device-proof.js +17 -0
  98. package/dist/plugins/cloud/plugin.d.ts +4 -0
  99. package/dist/plugins/cloud/plugin.js +17 -0
  100. package/dist/plugins/cloud/state-store.d.ts +7 -0
  101. package/dist/plugins/cloud/state-store.js +79 -0
  102. package/dist/plugins/cloud/tunnel-client.d.ts +84 -0
  103. package/dist/plugins/cloud/tunnel-client.js +427 -0
  104. package/dist/plugins/cloud/types.d.ts +72 -0
  105. package/dist/plugins/cloud/types.js +60 -0
  106. package/dist/plugins/computer/audit.d.ts +34 -0
  107. package/dist/plugins/computer/audit.js +42 -0
  108. package/dist/plugins/computer/computer-native-backend.d.ts +11 -0
  109. package/dist/plugins/computer/computer-native-backend.js +58 -0
  110. package/dist/plugins/computer/computer-native-host.d.ts +30 -0
  111. package/dist/plugins/computer/computer-native-host.js +411 -0
  112. package/dist/plugins/computer/computer-protocol.d.ts +22 -0
  113. package/dist/plugins/computer/computer-protocol.js +74 -0
  114. package/dist/plugins/computer/computer-schemas.d.ts +285 -0
  115. package/dist/plugins/computer/computer-schemas.js +152 -0
  116. package/dist/plugins/computer/computer-service.d.ts +29 -0
  117. package/dist/plugins/computer/computer-service.js +117 -0
  118. package/dist/plugins/computer/computer-settings-repository.d.ts +8 -0
  119. package/dist/plugins/computer/computer-settings-repository.js +41 -0
  120. package/dist/plugins/computer/mcp.d.ts +3 -0
  121. package/dist/plugins/computer/mcp.js +126 -0
  122. package/dist/plugins/computer/plugin.d.ts +4 -0
  123. package/dist/plugins/computer/plugin.js +29 -0
  124. package/dist/plugins/computer/types.d.ts +174 -0
  125. package/dist/plugins/computer/types.js +1 -0
  126. package/dist/plugins/git/git-service.d.ts +30 -0
  127. package/dist/plugins/git/git-service.js +428 -0
  128. package/dist/plugins/git/plugin.d.ts +4 -0
  129. package/dist/plugins/git/plugin.js +12 -0
  130. package/dist/plugins/git/types.d.ts +32 -0
  131. package/dist/plugins/git/types.js +1 -0
  132. package/dist/plugins/plugin-manager.d.ts +11 -0
  133. package/dist/plugins/plugin-manager.js +63 -0
  134. package/dist/plugins/process/backend.d.ts +18 -0
  135. package/dist/plugins/process/backend.js +1 -0
  136. package/dist/plugins/process/head-tail-buffer.d.ts +17 -0
  137. package/dist/plugins/process/head-tail-buffer.js +49 -0
  138. package/dist/plugins/process/infrastructure/pipe-process-backend.d.ts +8 -0
  139. package/dist/plugins/process/infrastructure/pipe-process-backend.js +71 -0
  140. package/dist/plugins/process/infrastructure/pty-process-backend.d.ts +8 -0
  141. package/dist/plugins/process/infrastructure/pty-process-backend.js +51 -0
  142. package/dist/plugins/process/mcp.d.ts +3 -0
  143. package/dist/plugins/process/mcp.js +58 -0
  144. package/dist/plugins/process/plugin.d.ts +4 -0
  145. package/dist/plugins/process/plugin.js +25 -0
  146. package/dist/plugins/process/process-supervisor.d.ts +39 -0
  147. package/dist/plugins/process/process-supervisor.js +244 -0
  148. package/dist/plugins/process/types.d.ts +30 -0
  149. package/dist/plugins/process/types.js +1 -0
  150. package/dist/plugins/types.d.ts +35 -0
  151. package/dist/plugins/types.js +17 -0
  152. package/dist/plugins/web/api-types.d.ts +56 -0
  153. package/dist/plugins/web/api-types.js +1 -0
  154. package/dist/plugins/web/http/api-router.d.ts +13 -0
  155. package/dist/plugins/web/http/api-router.js +47 -0
  156. package/dist/plugins/web/http/auth-api-router.d.ts +8 -0
  157. package/dist/plugins/web/http/auth-api-router.js +123 -0
  158. package/dist/plugins/web/http/cloud-api-router.d.ts +4 -0
  159. package/dist/plugins/web/http/cloud-api-router.js +29 -0
  160. package/dist/plugins/web/http/computer-api-router.d.ts +5 -0
  161. package/dist/plugins/web/http/computer-api-router.js +69 -0
  162. package/dist/plugins/web/http/git-api-router.d.ts +3 -0
  163. package/dist/plugins/web/http/git-api-router.js +85 -0
  164. package/dist/plugins/web/http/logs-api-router.d.ts +3 -0
  165. package/dist/plugins/web/http/logs-api-router.js +49 -0
  166. package/dist/plugins/web/http/process-api-router.d.ts +3 -0
  167. package/dist/plugins/web/http/process-api-router.js +16 -0
  168. package/dist/plugins/web/http/runtime-api-router.d.ts +10 -0
  169. package/dist/plugins/web/http/runtime-api-router.js +34 -0
  170. package/dist/plugins/web/http/workspace-api-router.d.ts +3 -0
  171. package/dist/plugins/web/http/workspace-api-router.js +91 -0
  172. package/dist/plugins/web/plugin.d.ts +8 -0
  173. package/dist/plugins/web/plugin.js +17 -0
  174. package/dist/plugins/web/runtime.d.ts +18 -0
  175. package/dist/plugins/web/runtime.js +31 -0
  176. package/dist/plugins/workspace/mcp.d.ts +3 -0
  177. package/dist/plugins/workspace/mcp.js +24 -0
  178. package/dist/plugins/workspace/metadata.d.ts +6 -0
  179. package/dist/plugins/workspace/metadata.js +111 -0
  180. package/dist/plugins/workspace/plugin.d.ts +4 -0
  181. package/dist/plugins/workspace/plugin.js +22 -0
  182. package/dist/plugins/workspace/types.d.ts +23 -0
  183. package/dist/plugins/workspace/types.js +1 -0
  184. package/dist/plugins/workspace/workspace-fs.d.ts +28 -0
  185. package/dist/plugins/workspace/workspace-fs.js +200 -0
  186. package/dist/plugins/workspace/workspace-service.d.ts +15 -0
  187. package/dist/plugins/workspace/workspace-service.js +140 -0
  188. package/dist/presentation/http/http-utils.d.ts +9 -0
  189. package/dist/presentation/http/http-utils.js +89 -0
  190. package/dist/presentation/http/oauth-router.d.ts +5 -0
  191. package/dist/presentation/http/oauth-router.js +172 -0
  192. package/dist/web/assets/CloudView-CHjVbp-5.css +1 -0
  193. package/dist/web/assets/CloudView-CvX0dkgu.js +1 -0
  194. package/dist/web/assets/CodeViewer-CH1DEJge.css +1 -0
  195. package/dist/web/assets/CodeViewer.vue_vue_type_script_setup_true_lang-B5TxGkUW.js +3 -0
  196. package/dist/web/assets/ComputerView-91Z9Cefw.css +1 -0
  197. package/dist/web/assets/ComputerView-OOyEqWml.js +1 -0
  198. package/dist/web/assets/McpToolsView-BTnolyS6.css +1 -0
  199. package/dist/web/assets/McpToolsView-DjXWTKrZ.js +1 -0
  200. package/dist/web/assets/OperationsView-DqTY51Cr.js +4 -0
  201. package/dist/web/assets/ProcessesView-C13BykIa.js +4 -0
  202. package/dist/web/assets/StateChip.vue_vue_type_script_setup_true_lang-DyOhJmR6.js +1 -0
  203. package/dist/web/assets/SystemLogsView-Ci0u4BTb.js +1 -0
  204. package/dist/web/assets/VSelect-CAxEESw-.css +1 -0
  205. package/dist/web/assets/VSelect-DrRpKyYI.js +1 -0
  206. package/dist/web/assets/VSpacer-C3wAY3F0.js +1 -0
  207. package/dist/web/assets/VSpacer-DfbUir7X.css +1 -0
  208. package/dist/web/assets/VSwitch-Cos8PNSP.css +1 -0
  209. package/dist/web/assets/VSwitch-CxusR4iO.js +1 -0
  210. package/dist/web/assets/VTabs-DSWgbwhc.css +1 -0
  211. package/dist/web/assets/VTabs-qBcuuXh3.js +1 -0
  212. package/dist/web/assets/WorkspacesView-BzQ0aH74.css +1 -0
  213. package/dist/web/assets/WorkspacesView-CijIvfsp.js +2 -0
  214. package/dist/web/assets/_plugin-vue_export-helper-DlAUqK2U.js +1 -0
  215. package/dist/web/assets/chatroom-B5p-5n3y.svg +15 -0
  216. package/dist/web/assets/index-BDkarAkX.js +2 -0
  217. package/dist/web/assets/index-BegKQ3Yd.js +19 -0
  218. package/dist/web/assets/index-CkjqXRSj.css +1 -0
  219. package/dist/web/assets/syntax-highlight-B5ArnyrC.js +3 -0
  220. package/dist/web/assets/useOperationLabels-BDjzGDkT.js +1 -0
  221. package/dist/web/assets/utils-rtPwWNel.js +1 -0
  222. package/dist/web/index.html +19 -0
  223. package/package.json +125 -0
@@ -0,0 +1,74 @@
1
+ import { z } from "zod";
2
+ import { ChatRoomError } from "#core/errors/chatroom-error";
3
+ import { computerActionResultSchema, computerNativeStatusSchema, computerSnapshotSchema, } from "./computer-schemas.js";
4
+ export const COMPUTER_NATIVE_PROTOCOL_VERSION = 1;
5
+ const responseEnvelopeSchema = z
6
+ .object({
7
+ protocol: z.literal(COMPUTER_NATIVE_PROTOCOL_VERSION),
8
+ id: z.string().min(1),
9
+ result: z.unknown().optional(),
10
+ error: z
11
+ .object({
12
+ code: z.string().optional(),
13
+ message: z.string(),
14
+ })
15
+ .strict()
16
+ .optional(),
17
+ })
18
+ .strict()
19
+ .refine((value) => (value.result === undefined) !== (value.error === undefined), {
20
+ message: "Native response must contain exactly one of result or error",
21
+ });
22
+ export function parseNativeEnvelope(value) {
23
+ const parsed = responseEnvelopeSchema.safeParse(value);
24
+ if (!parsed.success) {
25
+ throw new ChatRoomError("INTERNAL", "Computer helper returned an invalid protocol response", { issues: parsed.error.issues });
26
+ }
27
+ return {
28
+ id: parsed.data.id,
29
+ ...(parsed.data.result === undefined ? {} : { result: parsed.data.result }),
30
+ ...(parsed.data.error === undefined
31
+ ? {}
32
+ : {
33
+ error: {
34
+ message: parsed.data.error.message,
35
+ ...(parsed.data.error.code === undefined
36
+ ? {}
37
+ : { code: parsed.data.error.code }),
38
+ },
39
+ }),
40
+ };
41
+ }
42
+ export function parseNativeResult(method, value) {
43
+ const schema = method === "status" || method === "requestPermission"
44
+ ? computerNativeStatusSchema
45
+ : method === "snapshot"
46
+ ? computerSnapshotSchema
47
+ : computerActionResultSchema;
48
+ const parsed = schema.safeParse(value);
49
+ if (!parsed.success) {
50
+ throw new ChatRoomError("INTERNAL", `Computer helper returned an invalid ${method} result`, { issues: parsed.error.issues });
51
+ }
52
+ return parsed.data;
53
+ }
54
+ export function nativeError(error) {
55
+ return new ChatRoomError(nativeErrorCode(error.code), error.message, {
56
+ nativeCode: error.code ?? null,
57
+ });
58
+ }
59
+ function nativeErrorCode(code) {
60
+ switch (code) {
61
+ case "invalid_request":
62
+ return "INVALID_INPUT";
63
+ case "not_found":
64
+ return "NOT_FOUND";
65
+ case "permission_required":
66
+ return "FORBIDDEN";
67
+ case "stale_snapshot":
68
+ return "CONFLICT";
69
+ case "unsupported":
70
+ return "UNSUPPORTED";
71
+ default:
72
+ return "INTERNAL";
73
+ }
74
+ }
@@ -0,0 +1,285 @@
1
+ import { z } from "zod";
2
+ export declare const computerNativeStatusSchema: z.ZodObject<{
3
+ platform: z.ZodEnum<{
4
+ macos: "macos";
5
+ windows: "windows";
6
+ linux: "linux";
7
+ unsupported: "unsupported";
8
+ }>;
9
+ helper: z.ZodEnum<{
10
+ running: "running";
11
+ stopped: "stopped";
12
+ unavailable: "unavailable";
13
+ }>;
14
+ permissions: z.ZodObject<{
15
+ accessibility: z.ZodEnum<{
16
+ granted: "granted";
17
+ denied: "denied";
18
+ unknown: "unknown";
19
+ "not-required": "not-required";
20
+ }>;
21
+ screenRecording: z.ZodEnum<{
22
+ granted: "granted";
23
+ denied: "denied";
24
+ unknown: "unknown";
25
+ "not-required": "not-required";
26
+ }>;
27
+ }, z.core.$strip>;
28
+ displays: z.ZodArray<z.ZodObject<{
29
+ id: z.ZodString;
30
+ name: z.ZodString;
31
+ width: z.ZodNumber;
32
+ height: z.ZodNumber;
33
+ scale: z.ZodNumber;
34
+ primary: z.ZodBoolean;
35
+ }, z.core.$strip>>;
36
+ }, z.core.$strip>;
37
+ export declare const computerSnapshotSchema: z.ZodObject<{
38
+ snapshotId: z.ZodString;
39
+ revision: z.ZodNumber;
40
+ display: z.ZodNullable<z.ZodObject<{
41
+ id: z.ZodString;
42
+ name: z.ZodString;
43
+ width: z.ZodNumber;
44
+ height: z.ZodNumber;
45
+ scale: z.ZodNumber;
46
+ primary: z.ZodBoolean;
47
+ }, z.core.$strip>>;
48
+ activeApp: z.ZodNullable<z.ZodString>;
49
+ activeWindow: z.ZodNullable<z.ZodString>;
50
+ cursor: z.ZodNullable<z.ZodObject<{
51
+ x: z.ZodNumber;
52
+ y: z.ZodNumber;
53
+ }, z.core.$strip>>;
54
+ elements: z.ZodArray<z.ZodObject<{
55
+ id: z.ZodNumber;
56
+ role: z.ZodString;
57
+ name: z.ZodNullable<z.ZodString>;
58
+ value: z.ZodNullable<z.ZodString>;
59
+ enabled: z.ZodBoolean;
60
+ focused: z.ZodBoolean;
61
+ selected: z.ZodBoolean;
62
+ sensitive: z.ZodBoolean;
63
+ bounds: z.ZodNullable<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
64
+ actions: z.ZodArray<z.ZodString>;
65
+ }, z.core.$strip>>;
66
+ screenshot: z.ZodOptional<z.ZodObject<{
67
+ mimeType: z.ZodEnum<{
68
+ "image/jpeg": "image/jpeg";
69
+ "image/png": "image/png";
70
+ }>;
71
+ data: z.ZodString;
72
+ }, z.core.$strip>>;
73
+ }, z.core.$strip>;
74
+ export declare const computerSnapshotMetadataSchema: z.ZodObject<{
75
+ display: z.ZodNullable<z.ZodObject<{
76
+ id: z.ZodString;
77
+ name: z.ZodString;
78
+ width: z.ZodNumber;
79
+ height: z.ZodNumber;
80
+ scale: z.ZodNumber;
81
+ primary: z.ZodBoolean;
82
+ }, z.core.$strip>>;
83
+ snapshotId: z.ZodString;
84
+ revision: z.ZodNumber;
85
+ activeApp: z.ZodNullable<z.ZodString>;
86
+ activeWindow: z.ZodNullable<z.ZodString>;
87
+ cursor: z.ZodNullable<z.ZodObject<{
88
+ x: z.ZodNumber;
89
+ y: z.ZodNumber;
90
+ }, z.core.$strip>>;
91
+ elements: z.ZodArray<z.ZodObject<{
92
+ id: z.ZodNumber;
93
+ role: z.ZodString;
94
+ name: z.ZodNullable<z.ZodString>;
95
+ value: z.ZodNullable<z.ZodString>;
96
+ enabled: z.ZodBoolean;
97
+ focused: z.ZodBoolean;
98
+ selected: z.ZodBoolean;
99
+ sensitive: z.ZodBoolean;
100
+ bounds: z.ZodNullable<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
101
+ actions: z.ZodArray<z.ZodString>;
102
+ }, z.core.$strip>>;
103
+ }, z.core.$strip>;
104
+ export declare const computerActionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
105
+ type: z.ZodLiteral<"move">;
106
+ x: z.ZodNumber;
107
+ y: z.ZodNumber;
108
+ }, z.core.$strip>, z.ZodObject<{
109
+ type: z.ZodLiteral<"click">;
110
+ x: z.ZodOptional<z.ZodNumber>;
111
+ y: z.ZodOptional<z.ZodNumber>;
112
+ elementId: z.ZodOptional<z.ZodNumber>;
113
+ }, z.core.$strip>, z.ZodObject<{
114
+ type: z.ZodLiteral<"double_click">;
115
+ x: z.ZodOptional<z.ZodNumber>;
116
+ y: z.ZodOptional<z.ZodNumber>;
117
+ elementId: z.ZodOptional<z.ZodNumber>;
118
+ }, z.core.$strip>, z.ZodObject<{
119
+ type: z.ZodLiteral<"right_click">;
120
+ x: z.ZodOptional<z.ZodNumber>;
121
+ y: z.ZodOptional<z.ZodNumber>;
122
+ elementId: z.ZodOptional<z.ZodNumber>;
123
+ }, z.core.$strip>, z.ZodObject<{
124
+ type: z.ZodLiteral<"drag">;
125
+ from: z.ZodObject<{
126
+ x: z.ZodNumber;
127
+ y: z.ZodNumber;
128
+ }, z.core.$strip>;
129
+ to: z.ZodObject<{
130
+ x: z.ZodNumber;
131
+ y: z.ZodNumber;
132
+ }, z.core.$strip>;
133
+ durationMs: z.ZodOptional<z.ZodNumber>;
134
+ }, z.core.$strip>, z.ZodObject<{
135
+ type: z.ZodLiteral<"scroll">;
136
+ deltaX: z.ZodOptional<z.ZodNumber>;
137
+ deltaY: z.ZodNumber;
138
+ elementId: z.ZodOptional<z.ZodNumber>;
139
+ }, z.core.$strip>, z.ZodObject<{
140
+ type: z.ZodLiteral<"keypress">;
141
+ keys: z.ZodArray<z.ZodString>;
142
+ }, z.core.$strip>, z.ZodObject<{
143
+ type: z.ZodLiteral<"type_text">;
144
+ text: z.ZodString;
145
+ elementId: z.ZodOptional<z.ZodNumber>;
146
+ }, z.core.$strip>, z.ZodObject<{
147
+ type: z.ZodLiteral<"invoke">;
148
+ elementId: z.ZodNumber;
149
+ }, z.core.$strip>, z.ZodObject<{
150
+ type: z.ZodLiteral<"set_value">;
151
+ elementId: z.ZodNumber;
152
+ value: z.ZodString;
153
+ }, z.core.$strip>, z.ZodObject<{
154
+ type: z.ZodLiteral<"select_text">;
155
+ elementId: z.ZodNumber;
156
+ start: z.ZodNumber;
157
+ length: z.ZodNumber;
158
+ }, z.core.$strip>, z.ZodObject<{
159
+ type: z.ZodLiteral<"activate_app">;
160
+ app: z.ZodString;
161
+ }, z.core.$strip>, z.ZodObject<{
162
+ type: z.ZodLiteral<"activate_window">;
163
+ elementId: z.ZodNumber;
164
+ }, z.core.$strip>, z.ZodObject<{
165
+ type: z.ZodLiteral<"move_window">;
166
+ elementId: z.ZodNumber;
167
+ x: z.ZodNumber;
168
+ y: z.ZodNumber;
169
+ }, z.core.$strip>, z.ZodObject<{
170
+ type: z.ZodLiteral<"resize_window">;
171
+ elementId: z.ZodNumber;
172
+ width: z.ZodNumber;
173
+ height: z.ZodNumber;
174
+ }, z.core.$strip>, z.ZodObject<{
175
+ type: z.ZodLiteral<"wait">;
176
+ ms: z.ZodNumber;
177
+ }, z.core.$strip>], "type">;
178
+ export declare const computerActionResultSchema: z.ZodObject<{
179
+ success: z.ZodLiteral<true>;
180
+ revision: z.ZodNumber;
181
+ snapshot: z.ZodOptional<z.ZodObject<{
182
+ snapshotId: z.ZodString;
183
+ revision: z.ZodNumber;
184
+ display: z.ZodNullable<z.ZodObject<{
185
+ id: z.ZodString;
186
+ name: z.ZodString;
187
+ width: z.ZodNumber;
188
+ height: z.ZodNumber;
189
+ scale: z.ZodNumber;
190
+ primary: z.ZodBoolean;
191
+ }, z.core.$strip>>;
192
+ activeApp: z.ZodNullable<z.ZodString>;
193
+ activeWindow: z.ZodNullable<z.ZodString>;
194
+ cursor: z.ZodNullable<z.ZodObject<{
195
+ x: z.ZodNumber;
196
+ y: z.ZodNumber;
197
+ }, z.core.$strip>>;
198
+ elements: z.ZodArray<z.ZodObject<{
199
+ id: z.ZodNumber;
200
+ role: z.ZodString;
201
+ name: z.ZodNullable<z.ZodString>;
202
+ value: z.ZodNullable<z.ZodString>;
203
+ enabled: z.ZodBoolean;
204
+ focused: z.ZodBoolean;
205
+ selected: z.ZodBoolean;
206
+ sensitive: z.ZodBoolean;
207
+ bounds: z.ZodNullable<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
208
+ actions: z.ZodArray<z.ZodString>;
209
+ }, z.core.$strip>>;
210
+ screenshot: z.ZodOptional<z.ZodObject<{
211
+ mimeType: z.ZodEnum<{
212
+ "image/jpeg": "image/jpeg";
213
+ "image/png": "image/png";
214
+ }>;
215
+ data: z.ZodString;
216
+ }, z.core.$strip>>;
217
+ }, z.core.$strip>>;
218
+ executionMode: z.ZodEnum<{
219
+ semantic: "semantic";
220
+ background: "background";
221
+ foreground: "foreground";
222
+ mixed: "mixed";
223
+ }>;
224
+ focusChanged: z.ZodBoolean;
225
+ }, z.core.$strip>;
226
+ export declare const computerActionMetadataSchema: z.ZodObject<{
227
+ success: z.ZodLiteral<true>;
228
+ revision: z.ZodNumber;
229
+ executionMode: z.ZodEnum<{
230
+ semantic: "semantic";
231
+ background: "background";
232
+ foreground: "foreground";
233
+ mixed: "mixed";
234
+ }>;
235
+ focusChanged: z.ZodBoolean;
236
+ snapshot: z.ZodOptional<z.ZodObject<{
237
+ display: z.ZodNullable<z.ZodObject<{
238
+ id: z.ZodString;
239
+ name: z.ZodString;
240
+ width: z.ZodNumber;
241
+ height: z.ZodNumber;
242
+ scale: z.ZodNumber;
243
+ primary: z.ZodBoolean;
244
+ }, z.core.$strip>>;
245
+ snapshotId: z.ZodString;
246
+ revision: z.ZodNumber;
247
+ activeApp: z.ZodNullable<z.ZodString>;
248
+ activeWindow: z.ZodNullable<z.ZodString>;
249
+ cursor: z.ZodNullable<z.ZodObject<{
250
+ x: z.ZodNumber;
251
+ y: z.ZodNumber;
252
+ }, z.core.$strip>>;
253
+ elements: z.ZodArray<z.ZodObject<{
254
+ id: z.ZodNumber;
255
+ role: z.ZodString;
256
+ name: z.ZodNullable<z.ZodString>;
257
+ value: z.ZodNullable<z.ZodString>;
258
+ enabled: z.ZodBoolean;
259
+ focused: z.ZodBoolean;
260
+ selected: z.ZodBoolean;
261
+ sensitive: z.ZodBoolean;
262
+ bounds: z.ZodNullable<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
263
+ actions: z.ZodArray<z.ZodString>;
264
+ }, z.core.$strip>>;
265
+ }, z.core.$strip>>;
266
+ }, z.core.$strip>;
267
+ export declare const computerSnapshotTargetSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
268
+ type: z.ZodLiteral<"desktop">;
269
+ }, z.core.$strip>, z.ZodObject<{
270
+ type: z.ZodLiteral<"display">;
271
+ displayId: z.ZodOptional<z.ZodString>;
272
+ }, z.core.$strip>, z.ZodObject<{
273
+ type: z.ZodLiteral<"app">;
274
+ app: z.ZodString;
275
+ }, z.core.$strip>, z.ZodObject<{
276
+ type: z.ZodLiteral<"window">;
277
+ elementId: z.ZodNumber;
278
+ }, z.core.$strip>, z.ZodObject<{
279
+ type: z.ZodLiteral<"region">;
280
+ displayId: z.ZodOptional<z.ZodString>;
281
+ x: z.ZodNumber;
282
+ y: z.ZodNumber;
283
+ width: z.ZodNumber;
284
+ height: z.ZodNumber;
285
+ }, z.core.$strip>], "type">;
@@ -0,0 +1,152 @@
1
+ import { z } from "zod";
2
+ const computerPermissionStateSchema = z.enum([
3
+ "granted",
4
+ "denied",
5
+ "unknown",
6
+ "not-required",
7
+ ]);
8
+ const computerDisplaySchema = z.object({
9
+ id: z.string(),
10
+ name: z.string(),
11
+ width: z.number(),
12
+ height: z.number(),
13
+ scale: z.number(),
14
+ primary: z.boolean(),
15
+ });
16
+ const computerElementSchema = z.object({
17
+ id: z.number().int(),
18
+ role: z.string(),
19
+ name: z.string().nullable(),
20
+ value: z.string().nullable(),
21
+ enabled: z.boolean(),
22
+ focused: z.boolean(),
23
+ selected: z.boolean(),
24
+ sensitive: z.boolean(),
25
+ bounds: z.tuple([z.number(), z.number(), z.number(), z.number()]).nullable(),
26
+ actions: z.array(z.string()),
27
+ });
28
+ const computerScreenshotSchema = z.object({
29
+ mimeType: z.enum(["image/jpeg", "image/png"]),
30
+ data: z.string(),
31
+ });
32
+ export const computerNativeStatusSchema = z.object({
33
+ platform: z.enum(["macos", "windows", "linux", "unsupported"]),
34
+ helper: z.enum(["running", "stopped", "unavailable"]),
35
+ permissions: z.object({
36
+ accessibility: computerPermissionStateSchema,
37
+ screenRecording: computerPermissionStateSchema,
38
+ }),
39
+ displays: z.array(computerDisplaySchema),
40
+ });
41
+ export const computerSnapshotSchema = z.object({
42
+ snapshotId: z.string().min(1),
43
+ revision: z.number().int(),
44
+ display: computerDisplaySchema.nullable(),
45
+ activeApp: z.string().nullable(),
46
+ activeWindow: z.string().nullable(),
47
+ cursor: z.object({ x: z.number(), y: z.number() }).nullable(),
48
+ elements: z.array(computerElementSchema),
49
+ screenshot: computerScreenshotSchema.optional(),
50
+ });
51
+ export const computerSnapshotMetadataSchema = computerSnapshotSchema.omit({
52
+ screenshot: true,
53
+ });
54
+ export const computerActionSchema = z.discriminatedUnion("type", [
55
+ z.object({ type: z.literal("move"), x: z.number(), y: z.number() }),
56
+ z.object({
57
+ type: z.literal("click"),
58
+ x: z.number().optional(),
59
+ y: z.number().optional(),
60
+ elementId: z.number().int().optional(),
61
+ }),
62
+ z.object({
63
+ type: z.literal("double_click"),
64
+ x: z.number().optional(),
65
+ y: z.number().optional(),
66
+ elementId: z.number().int().optional(),
67
+ }),
68
+ z.object({
69
+ type: z.literal("right_click"),
70
+ x: z.number().optional(),
71
+ y: z.number().optional(),
72
+ elementId: z.number().int().optional(),
73
+ }),
74
+ z.object({
75
+ type: z.literal("drag"),
76
+ from: z.object({ x: z.number(), y: z.number() }),
77
+ to: z.object({ x: z.number(), y: z.number() }),
78
+ durationMs: z.number().int().min(0).max(10_000).optional(),
79
+ }),
80
+ z.object({
81
+ type: z.literal("scroll"),
82
+ deltaX: z.number().optional(),
83
+ deltaY: z.number(),
84
+ elementId: z.number().int().optional(),
85
+ }),
86
+ z.object({
87
+ type: z.literal("keypress"),
88
+ keys: z.array(z.string().min(1)).min(1).max(8),
89
+ }),
90
+ z.object({
91
+ type: z.literal("type_text"),
92
+ text: z.string().max(100_000),
93
+ elementId: z.number().int().optional(),
94
+ }),
95
+ z.object({ type: z.literal("invoke"), elementId: z.number().int() }),
96
+ z.object({
97
+ type: z.literal("set_value"),
98
+ elementId: z.number().int(),
99
+ value: z.string().max(100_000),
100
+ }),
101
+ z.object({
102
+ type: z.literal("select_text"),
103
+ elementId: z.number().int(),
104
+ start: z.number().int().min(0),
105
+ length: z.number().int().min(0),
106
+ }),
107
+ z.object({ type: z.literal("activate_app"), app: z.string().min(1) }),
108
+ z.object({ type: z.literal("activate_window"), elementId: z.number().int() }),
109
+ z.object({
110
+ type: z.literal("move_window"),
111
+ elementId: z.number().int(),
112
+ x: z.number(),
113
+ y: z.number(),
114
+ }),
115
+ z.object({
116
+ type: z.literal("resize_window"),
117
+ elementId: z.number().int(),
118
+ width: z.number().positive(),
119
+ height: z.number().positive(),
120
+ }),
121
+ z.object({
122
+ type: z.literal("wait"),
123
+ ms: z.number().int().min(0).max(30_000),
124
+ }),
125
+ ]);
126
+ export const computerActionResultSchema = z.object({
127
+ success: z.literal(true),
128
+ revision: z.number().int(),
129
+ snapshot: computerSnapshotSchema.optional(),
130
+ executionMode: z.enum(["semantic", "background", "foreground", "mixed"]),
131
+ focusChanged: z.boolean(),
132
+ });
133
+ export const computerActionMetadataSchema = computerActionResultSchema.extend({
134
+ snapshot: computerSnapshotMetadataSchema.optional(),
135
+ });
136
+ export const computerSnapshotTargetSchema = z.discriminatedUnion("type", [
137
+ z.object({ type: z.literal("desktop") }),
138
+ z.object({
139
+ type: z.literal("display"),
140
+ displayId: z.string().optional(),
141
+ }),
142
+ z.object({ type: z.literal("app"), app: z.string().min(1) }),
143
+ z.object({ type: z.literal("window"), elementId: z.number().int() }),
144
+ z.object({
145
+ type: z.literal("region"),
146
+ displayId: z.string().optional(),
147
+ x: z.number(),
148
+ y: z.number(),
149
+ width: z.number().positive(),
150
+ height: z.number().positive(),
151
+ }),
152
+ ]);
@@ -0,0 +1,29 @@
1
+ import type { RuntimeEventBus } from "#app/event-bus";
2
+ import type { ComputerSettingsRepository } from "./computer-settings-repository.js";
3
+ import type { ComputerAccessScope, ComputerActionRequest, ComputerActionResult, ComputerBackend, ComputerPermission, ComputerSettings, ComputerSnapshot, ComputerSnapshotRequest, ComputerStatus } from "./types.js";
4
+ type ComputerSettingsStore = Pick<ComputerSettingsRepository, "get" | "set">;
5
+ export declare class ComputerService {
6
+ private readonly backend;
7
+ private readonly settingsRepository;
8
+ private readonly events;
9
+ private revision;
10
+ private currentSnapshotId;
11
+ private latestSnapshotValue;
12
+ private latestSnapshotCapturedAt;
13
+ private queue;
14
+ constructor(backend: ComputerBackend, settingsRepository: ComputerSettingsStore, events: RuntimeEventBus);
15
+ settings(): ComputerSettings;
16
+ latestSnapshot(scope: ComputerAccessScope): ComputerSnapshot | null;
17
+ latestSnapshotTimestamp(): string | null;
18
+ requestPermission(permission: ComputerPermission): Promise<ComputerStatus>;
19
+ status(): Promise<ComputerStatus>;
20
+ setSettings(patch: Partial<Pick<ComputerSettings, "enabled" | "remoteAccess">>): ComputerSettings;
21
+ snapshot(scope: ComputerAccessScope, request: ComputerSnapshotRequest): Promise<ComputerSnapshot>;
22
+ action(scope: ComputerAccessScope, request: ComputerActionRequest): Promise<ComputerActionResult>;
23
+ shutdown(): Promise<void>;
24
+ private assertActionBudget;
25
+ private assertAllowed;
26
+ private assertScopeAllowed;
27
+ private serial;
28
+ }
29
+ export {};
@@ -0,0 +1,117 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { ChatRoomError } from "#core/errors/chatroom-error";
3
+ export class ComputerService {
4
+ backend;
5
+ settingsRepository;
6
+ events;
7
+ revision = 0;
8
+ currentSnapshotId = null;
9
+ latestSnapshotValue = null;
10
+ latestSnapshotCapturedAt = null;
11
+ queue = Promise.resolve();
12
+ constructor(backend, settingsRepository, events) {
13
+ this.backend = backend;
14
+ this.settingsRepository = settingsRepository;
15
+ this.events = events;
16
+ }
17
+ settings() {
18
+ return this.settingsRepository.get();
19
+ }
20
+ latestSnapshot(scope) {
21
+ const settings = this.settings();
22
+ if (!settings.enabled)
23
+ return null;
24
+ this.assertScopeAllowed(scope, settings);
25
+ return this.latestSnapshotValue;
26
+ }
27
+ latestSnapshotTimestamp() {
28
+ return this.latestSnapshotValue ? this.latestSnapshotCapturedAt : null;
29
+ }
30
+ requestPermission(permission) {
31
+ return this.serial(async () => {
32
+ const base = await this.backend.requestPermission(permission);
33
+ return { ...base, settings: this.settings() };
34
+ });
35
+ }
36
+ async status() {
37
+ const base = await this.backend.status();
38
+ return { ...base, settings: this.settings() };
39
+ }
40
+ setSettings(patch) {
41
+ const next = this.settingsRepository.set({ ...this.settings(), ...patch });
42
+ if (!next.enabled) {
43
+ this.currentSnapshotId = null;
44
+ this.latestSnapshotValue = null;
45
+ this.latestSnapshotCapturedAt = null;
46
+ void this.backend.dispose().catch(() => undefined);
47
+ }
48
+ this.events.emit({ type: "computer-settings", settings: next });
49
+ return next;
50
+ }
51
+ snapshot(scope, request) {
52
+ return this.serial(async () => {
53
+ this.assertAllowed(scope);
54
+ this.currentSnapshotId = null;
55
+ const value = await this.backend.snapshot(request, this.revision);
56
+ this.currentSnapshotId = value.snapshotId || `snap_${randomUUID()}`;
57
+ value.snapshotId = this.currentSnapshotId;
58
+ value.revision = this.revision;
59
+ this.latestSnapshotValue = value;
60
+ this.latestSnapshotCapturedAt = new Date().toISOString();
61
+ return value;
62
+ });
63
+ }
64
+ action(scope, request) {
65
+ return this.serial(async () => {
66
+ this.assertAllowed(scope);
67
+ this.assertActionBudget(request);
68
+ const referencesElements = request.actions.some((action) => "elementId" in action && action.elementId !== undefined);
69
+ if (referencesElements && !request.snapshotId)
70
+ throw new ChatRoomError("CONFLICT", "Actions using elementId require the latest snapshotId");
71
+ if (request.snapshotId && request.snapshotId !== this.currentSnapshotId)
72
+ throw new ChatRoomError("CONFLICT", "Computer snapshot is stale; take a new snapshot before acting");
73
+ this.currentSnapshotId = null;
74
+ const result = await this.backend.action(request, ++this.revision);
75
+ this.currentSnapshotId = result.snapshot?.snapshotId ?? null;
76
+ if (result.snapshot) {
77
+ this.latestSnapshotValue = result.snapshot;
78
+ this.latestSnapshotCapturedAt = new Date().toISOString();
79
+ }
80
+ return result;
81
+ });
82
+ }
83
+ async shutdown() {
84
+ this.currentSnapshotId = null;
85
+ this.latestSnapshotValue = null;
86
+ this.latestSnapshotCapturedAt = null;
87
+ await this.backend.dispose();
88
+ }
89
+ assertActionBudget(request) {
90
+ const durationMs = request.actions.reduce((total, action) => {
91
+ if (action.type === "wait")
92
+ return total + action.ms;
93
+ if (action.type === "drag")
94
+ return total + (action.durationMs ?? 0);
95
+ return total;
96
+ }, 0);
97
+ if (durationMs > 30_000)
98
+ throw new ChatRoomError("INVALID_INPUT", "Computer action batch exceeds the 30 second execution budget");
99
+ }
100
+ assertAllowed(scope) {
101
+ const settings = this.settings();
102
+ if (!settings.enabled)
103
+ throw new ChatRoomError("FORBIDDEN", "Computer Use is disabled");
104
+ this.assertScopeAllowed(scope, settings);
105
+ }
106
+ assertScopeAllowed(scope, settings) {
107
+ if (scope === "remote" && !settings.remoteAccess)
108
+ throw new ChatRoomError("FORBIDDEN", "Remote Computer Use is disabled", {
109
+ reason: "remote_computer_disabled",
110
+ });
111
+ }
112
+ serial(action) {
113
+ const next = this.queue.then(action, action);
114
+ this.queue = next.then(() => undefined, () => undefined);
115
+ return next;
116
+ }
117
+ }
@@ -0,0 +1,8 @@
1
+ import type { AppDatabase } from "#infrastructure/database/app-database";
2
+ import type { ComputerSettings } from "./types.js";
3
+ export declare class ComputerSettingsRepository {
4
+ private readonly database;
5
+ constructor(database: AppDatabase);
6
+ get(): ComputerSettings;
7
+ set(value: Omit<ComputerSettings, "updatedAt"> | ComputerSettings): ComputerSettings;
8
+ }