@chatroomcp/chatroom 0.1.7 → 0.2.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.
- package/dist/app/application.d.ts +1 -0
- package/dist/app/application.js +4 -0
- package/dist/app/event-bus.d.ts +4 -0
- package/dist/auth/ingress-policy.d.ts +2 -0
- package/dist/auth/ingress-policy.js +7 -1
- package/dist/infrastructure/database/app-database.js +7 -0
- package/dist/infrastructure/http/http-server.js +5 -1
- package/dist/mcp/server/plugin-mcp-registrar.d.ts +6 -1
- package/dist/mcp/server/plugin-mcp-registrar.js +4 -4
- package/dist/mcp/server/request-context.d.ts +3 -0
- package/dist/mcp/server/request-context.js +8 -0
- package/dist/mcp/server/tool-support.d.ts +1 -1
- package/dist/mcp/server/tool-support.js +3 -1
- package/dist/native/linux/chatroom-computer-helper +0 -0
- package/dist/native/macos/ChatRoomComputerHelper.app/Contents/Info.plist +14 -0
- package/dist/native/macos/ChatRoomComputerHelper.app/Contents/MacOS/chatroom-computer-helper +0 -0
- package/dist/native/macos/ChatRoomComputerHelper.app/Contents/_CodeSignature/CodeResources +115 -0
- package/dist/native/windows/chatroom-computer-helper.exe +0 -0
- package/dist/plugins/computer/audit.d.ts +34 -0
- package/dist/plugins/computer/audit.js +42 -0
- package/dist/plugins/computer/computer-native-backend.d.ts +11 -0
- package/dist/plugins/computer/computer-native-backend.js +58 -0
- package/dist/plugins/computer/computer-native-host.d.ts +27 -0
- package/dist/plugins/computer/computer-native-host.js +335 -0
- package/dist/plugins/computer/computer-protocol.d.ts +21 -0
- package/dist/plugins/computer/computer-protocol.js +72 -0
- package/dist/plugins/computer/computer-schemas.d.ts +318 -0
- package/dist/plugins/computer/computer-schemas.js +152 -0
- package/dist/plugins/computer/computer-service.d.ts +27 -0
- package/dist/plugins/computer/computer-service.js +108 -0
- package/dist/plugins/computer/computer-settings-repository.d.ts +8 -0
- package/dist/plugins/computer/computer-settings-repository.js +41 -0
- package/dist/plugins/computer/mcp.d.ts +3 -0
- package/dist/plugins/computer/mcp.js +126 -0
- package/dist/plugins/computer/plugin.d.ts +4 -0
- package/dist/plugins/computer/plugin.js +29 -0
- package/dist/plugins/computer/types.d.ts +174 -0
- package/dist/plugins/computer/types.js +1 -0
- package/dist/plugins/web/api-types.d.ts +15 -1
- package/dist/plugins/web/http/api-router.js +2 -0
- package/dist/plugins/web/http/computer-api-router.d.ts +5 -0
- package/dist/plugins/web/http/computer-api-router.js +68 -0
- package/dist/plugins/web/plugin.js +3 -1
- package/dist/plugins/web/runtime.d.ts +3 -1
- package/dist/plugins/web/runtime.js +3 -1
- package/dist/web/assets/index-B4vO7wVS.css +1 -0
- package/dist/web/assets/index-BU7LCgKB.js +26 -0
- package/dist/web/index.html +2 -2
- package/package.json +6 -3
- package/dist/web/assets/index-B7jL3mhD.css +0 -1
- package/dist/web/assets/index-L4-YdZVN.js +0 -26
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { spawn, spawnSync, } from "node:child_process";
|
|
3
|
+
import { chmodSync, existsSync, rmSync } from "node:fs";
|
|
4
|
+
import { createServer } from "node:net";
|
|
5
|
+
import { createInterface } from "node:readline";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { ChatRoomError } from "../../core/errors/chatroom-error.js";
|
|
9
|
+
import { COMPUTER_NATIVE_PROTOCOL_VERSION, nativeError, parseNativeEnvelope, } from "./computer-protocol.js";
|
|
10
|
+
export class ComputerNativeHost {
|
|
11
|
+
child = null;
|
|
12
|
+
socket = null;
|
|
13
|
+
socketServer = null;
|
|
14
|
+
socketPath = null;
|
|
15
|
+
lines = null;
|
|
16
|
+
starting = null;
|
|
17
|
+
pending = new Map();
|
|
18
|
+
sequence = 0;
|
|
19
|
+
get platform() {
|
|
20
|
+
if (process.platform === "darwin")
|
|
21
|
+
return "macos";
|
|
22
|
+
if (process.platform === "win32")
|
|
23
|
+
return "windows";
|
|
24
|
+
if (process.platform === "linux")
|
|
25
|
+
return "linux";
|
|
26
|
+
return "unsupported";
|
|
27
|
+
}
|
|
28
|
+
get idle() {
|
|
29
|
+
return this.pending.size === 0;
|
|
30
|
+
}
|
|
31
|
+
async request(method, params) {
|
|
32
|
+
await this.ensureStarted();
|
|
33
|
+
const id = `computer_${++this.sequence}`;
|
|
34
|
+
const timeoutMs = requestTimeoutMs(method);
|
|
35
|
+
const promise = new Promise((resolve, reject) => {
|
|
36
|
+
const timer = setTimeout(() => {
|
|
37
|
+
this.pending.delete(id);
|
|
38
|
+
const error = new ChatRoomError("INTERNAL", `Computer helper ${method} request timed out after ${timeoutMs} ms`);
|
|
39
|
+
reject(error);
|
|
40
|
+
this.reset(error);
|
|
41
|
+
}, timeoutMs);
|
|
42
|
+
timer.unref();
|
|
43
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
44
|
+
});
|
|
45
|
+
const line = `${JSON.stringify({
|
|
46
|
+
protocol: COMPUTER_NATIVE_PROTOCOL_VERSION,
|
|
47
|
+
id,
|
|
48
|
+
method,
|
|
49
|
+
params,
|
|
50
|
+
})}\n`;
|
|
51
|
+
if (this.platform === "macos")
|
|
52
|
+
this.socket.write(line);
|
|
53
|
+
else
|
|
54
|
+
this.child.stdin.write(line);
|
|
55
|
+
return promise;
|
|
56
|
+
}
|
|
57
|
+
restart(reason = "Computer helper restarting") {
|
|
58
|
+
this.reset(new Error(reason));
|
|
59
|
+
}
|
|
60
|
+
async dispose() {
|
|
61
|
+
this.reset(new Error("Computer helper stopped"));
|
|
62
|
+
}
|
|
63
|
+
async ensureStarted() {
|
|
64
|
+
const platform = this.platform;
|
|
65
|
+
if (platform === "macos" && this.socket && !this.socket.destroyed)
|
|
66
|
+
return;
|
|
67
|
+
if ((platform === "windows" || platform === "linux") &&
|
|
68
|
+
this.child &&
|
|
69
|
+
!this.child.killed)
|
|
70
|
+
return;
|
|
71
|
+
if (this.starting)
|
|
72
|
+
return this.starting;
|
|
73
|
+
this.starting = this.startHelper(platform);
|
|
74
|
+
try {
|
|
75
|
+
await this.starting;
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
this.starting = null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
startHelper(platform) {
|
|
82
|
+
switch (platform) {
|
|
83
|
+
case "macos":
|
|
84
|
+
return this.startMacHelper();
|
|
85
|
+
case "windows":
|
|
86
|
+
return this.startPipeHelper(windowsHelperPath(), "Windows");
|
|
87
|
+
case "linux":
|
|
88
|
+
return this.startPipeHelper(linuxHelperPath(), "Linux X11", linuxDesktopEnvironment());
|
|
89
|
+
case "unsupported":
|
|
90
|
+
return Promise.reject(new ChatRoomError("UNSUPPORTED", "Computer Use is supported only on macOS, Windows, and Linux X11"));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async startMacHelper() {
|
|
94
|
+
const app = macHelperAppPath();
|
|
95
|
+
if (!app)
|
|
96
|
+
throw new ChatRoomError("UNSUPPORTED", "macOS Computer helper is missing");
|
|
97
|
+
this.cleanupSocketPath();
|
|
98
|
+
const socketPath = path.join("/tmp", `chatroom-c-${process.pid}-${randomUUID().slice(0, 8)}.sock`);
|
|
99
|
+
this.socketPath = socketPath;
|
|
100
|
+
await new Promise((resolve, reject) => {
|
|
101
|
+
const server = createServer();
|
|
102
|
+
this.socketServer = server;
|
|
103
|
+
let settled = false;
|
|
104
|
+
const timer = setTimeout(() => fail(new Error("Computer helper connection timed out")), 10_000);
|
|
105
|
+
timer.unref();
|
|
106
|
+
const cleanupServer = () => {
|
|
107
|
+
clearTimeout(timer);
|
|
108
|
+
if (this.socketServer === server)
|
|
109
|
+
this.socketServer = null;
|
|
110
|
+
server.close();
|
|
111
|
+
};
|
|
112
|
+
const fail = (error) => {
|
|
113
|
+
if (settled)
|
|
114
|
+
return;
|
|
115
|
+
settled = true;
|
|
116
|
+
cleanupServer();
|
|
117
|
+
this.cleanupSocketPath();
|
|
118
|
+
reject(error);
|
|
119
|
+
};
|
|
120
|
+
server.once("error", fail);
|
|
121
|
+
server.once("connection", (socket) => {
|
|
122
|
+
if (settled) {
|
|
123
|
+
socket.destroy();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
settled = true;
|
|
127
|
+
server.removeListener("error", fail);
|
|
128
|
+
cleanupServer();
|
|
129
|
+
this.attachSocket(socket);
|
|
130
|
+
resolve();
|
|
131
|
+
});
|
|
132
|
+
server.listen(socketPath, () => {
|
|
133
|
+
chmodSync(socketPath, 0o600);
|
|
134
|
+
const launcher = spawn("/usr/bin/open", ["-n", "-g", app, "--args", "--connect", socketPath], { stdio: "ignore" });
|
|
135
|
+
launcher.once("error", fail);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
attachSocket(socket) {
|
|
140
|
+
this.socket = socket;
|
|
141
|
+
this.lines = createInterface({ input: socket });
|
|
142
|
+
this.lines.on("line", (line) => this.handleLine(line));
|
|
143
|
+
socket.once("error", (error) => this.failTransport(socket, error));
|
|
144
|
+
socket.once("close", () => this.failTransport(socket, new Error("Computer helper exited")));
|
|
145
|
+
}
|
|
146
|
+
async startPipeHelper(executable, platformName, env = process.env) {
|
|
147
|
+
if (!executable)
|
|
148
|
+
throw new ChatRoomError("UNSUPPORTED", `${platformName} Computer helper is missing`);
|
|
149
|
+
const child = spawn(executable, [], {
|
|
150
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
151
|
+
windowsHide: process.platform === "win32",
|
|
152
|
+
env,
|
|
153
|
+
});
|
|
154
|
+
this.child = child;
|
|
155
|
+
this.lines = createInterface({ input: child.stdout });
|
|
156
|
+
this.lines.on("line", (line) => this.handleLine(line));
|
|
157
|
+
child.stderr.on("data", (chunk) => console.error("[computer-helper]", String(chunk).trimEnd()));
|
|
158
|
+
child.once("error", (error) => this.failTransport(child, error));
|
|
159
|
+
child.once("exit", () => this.failTransport(child, new Error("Computer helper exited")));
|
|
160
|
+
}
|
|
161
|
+
failTransport(transport, error) {
|
|
162
|
+
const isCurrentSocket = transport === this.socket;
|
|
163
|
+
const isCurrentChild = transport === this.child;
|
|
164
|
+
if (!isCurrentSocket && !isCurrentChild)
|
|
165
|
+
return;
|
|
166
|
+
if (isCurrentSocket)
|
|
167
|
+
this.socket = null;
|
|
168
|
+
if (isCurrentChild)
|
|
169
|
+
this.child = null;
|
|
170
|
+
this.lines?.close();
|
|
171
|
+
this.lines = null;
|
|
172
|
+
this.cleanupSocketPath();
|
|
173
|
+
this.rejectPending(error);
|
|
174
|
+
}
|
|
175
|
+
reset(error) {
|
|
176
|
+
this.lines?.close();
|
|
177
|
+
this.lines = null;
|
|
178
|
+
this.socket?.destroy();
|
|
179
|
+
this.socket = null;
|
|
180
|
+
this.socketServer?.close();
|
|
181
|
+
this.socketServer = null;
|
|
182
|
+
this.child?.kill();
|
|
183
|
+
this.child = null;
|
|
184
|
+
this.cleanupSocketPath();
|
|
185
|
+
this.rejectPending(error);
|
|
186
|
+
}
|
|
187
|
+
rejectPending(error) {
|
|
188
|
+
for (const item of this.pending.values()) {
|
|
189
|
+
clearTimeout(item.timer);
|
|
190
|
+
item.reject(error);
|
|
191
|
+
}
|
|
192
|
+
this.pending.clear();
|
|
193
|
+
}
|
|
194
|
+
cleanupSocketPath() {
|
|
195
|
+
if (!this.socketPath)
|
|
196
|
+
return;
|
|
197
|
+
rmSync(this.socketPath, { force: true });
|
|
198
|
+
this.socketPath = null;
|
|
199
|
+
}
|
|
200
|
+
handleLine(line) {
|
|
201
|
+
let raw;
|
|
202
|
+
try {
|
|
203
|
+
raw = JSON.parse(line);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const rawId = raw && typeof raw === "object" && "id" in raw
|
|
209
|
+
? raw.id
|
|
210
|
+
: undefined;
|
|
211
|
+
if (typeof rawId !== "string")
|
|
212
|
+
return;
|
|
213
|
+
const pending = this.pending.get(rawId);
|
|
214
|
+
if (!pending)
|
|
215
|
+
return;
|
|
216
|
+
let message;
|
|
217
|
+
try {
|
|
218
|
+
message = parseNativeEnvelope(raw);
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
this.pending.delete(rawId);
|
|
222
|
+
clearTimeout(pending.timer);
|
|
223
|
+
pending.reject(error);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
this.pending.delete(message.id);
|
|
227
|
+
clearTimeout(pending.timer);
|
|
228
|
+
if (message.error)
|
|
229
|
+
pending.reject(nativeError(message.error));
|
|
230
|
+
else
|
|
231
|
+
pending.resolve(message.result);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function requestTimeoutMs(method) {
|
|
235
|
+
switch (method) {
|
|
236
|
+
case "status":
|
|
237
|
+
return 5_000;
|
|
238
|
+
case "snapshot":
|
|
239
|
+
return 15_000;
|
|
240
|
+
case "requestPermission":
|
|
241
|
+
return 30_000;
|
|
242
|
+
case "action":
|
|
243
|
+
return 60_000;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function projectRoot() {
|
|
247
|
+
const current = fileURLToPath(import.meta.url);
|
|
248
|
+
return path.resolve(path.dirname(current), "../../..");
|
|
249
|
+
}
|
|
250
|
+
function macHelperAppPath() {
|
|
251
|
+
return nativeHelperPath("macos", "ChatRoomComputerHelper.app");
|
|
252
|
+
}
|
|
253
|
+
function windowsHelperPath() {
|
|
254
|
+
return nativeHelperPath("windows", "chatroom-computer-helper.exe");
|
|
255
|
+
}
|
|
256
|
+
function linuxHelperPath() {
|
|
257
|
+
return nativeHelperPath("linux", "chatroom-computer-helper");
|
|
258
|
+
}
|
|
259
|
+
function nativeHelperPath(...parts) {
|
|
260
|
+
const target = path.join(projectRoot(), "dist", "native", ...parts);
|
|
261
|
+
return existsSync(target) ? target : null;
|
|
262
|
+
}
|
|
263
|
+
function linuxDesktopEnvironment() {
|
|
264
|
+
const env = { ...process.env };
|
|
265
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
266
|
+
if (!env.XDG_RUNTIME_DIR && uid !== null) {
|
|
267
|
+
const runtimeDir = `/run/user/${uid}`;
|
|
268
|
+
if (existsSync(runtimeDir))
|
|
269
|
+
env.XDG_RUNTIME_DIR = runtimeDir;
|
|
270
|
+
}
|
|
271
|
+
if (!env.DISPLAY) {
|
|
272
|
+
const display = activeX11Display(uid);
|
|
273
|
+
if (display) {
|
|
274
|
+
env.DISPLAY = display;
|
|
275
|
+
env.XDG_SESSION_TYPE = "x11";
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (!env.XAUTHORITY) {
|
|
279
|
+
const candidates = [
|
|
280
|
+
env.HOME ? path.join(env.HOME, ".Xauthority") : null,
|
|
281
|
+
env.XDG_RUNTIME_DIR
|
|
282
|
+
? path.join(env.XDG_RUNTIME_DIR, "gdm", "Xauthority")
|
|
283
|
+
: null,
|
|
284
|
+
];
|
|
285
|
+
const authority = candidates.find((candidate) => !!candidate && existsSync(candidate));
|
|
286
|
+
if (authority)
|
|
287
|
+
env.XAUTHORITY = authority;
|
|
288
|
+
}
|
|
289
|
+
if (!env.DBUS_SESSION_BUS_ADDRESS && env.XDG_RUNTIME_DIR) {
|
|
290
|
+
const bus = path.join(env.XDG_RUNTIME_DIR, "bus");
|
|
291
|
+
if (existsSync(bus))
|
|
292
|
+
env.DBUS_SESSION_BUS_ADDRESS = `unix:path=${bus}`;
|
|
293
|
+
}
|
|
294
|
+
return env;
|
|
295
|
+
}
|
|
296
|
+
function activeX11Display(uid) {
|
|
297
|
+
if (process.platform !== "linux" || uid === null)
|
|
298
|
+
return null;
|
|
299
|
+
const loginctl = existsSync("/usr/bin/loginctl")
|
|
300
|
+
? "/usr/bin/loginctl"
|
|
301
|
+
: "loginctl";
|
|
302
|
+
const sessions = spawnSync(loginctl, ["list-sessions", "--no-legend", "--no-pager"], {
|
|
303
|
+
encoding: "utf8",
|
|
304
|
+
timeout: 2_000,
|
|
305
|
+
});
|
|
306
|
+
if (sessions.status !== 0 || !sessions.stdout)
|
|
307
|
+
return null;
|
|
308
|
+
const userId = String(uid);
|
|
309
|
+
for (const line of sessions.stdout.split("\n")) {
|
|
310
|
+
const [sessionId, sessionUid] = line.trim().split(/\s+/, 3);
|
|
311
|
+
if (!sessionId || sessionUid !== userId)
|
|
312
|
+
continue;
|
|
313
|
+
const details = spawnSync(loginctl, [
|
|
314
|
+
"show-session",
|
|
315
|
+
sessionId,
|
|
316
|
+
"--no-pager",
|
|
317
|
+
"-p",
|
|
318
|
+
"Type",
|
|
319
|
+
"-p",
|
|
320
|
+
"Active",
|
|
321
|
+
"-p",
|
|
322
|
+
"Display",
|
|
323
|
+
], { encoding: "utf8", timeout: 2_000 });
|
|
324
|
+
if (details.status !== 0 || !details.stdout)
|
|
325
|
+
continue;
|
|
326
|
+
const values = Object.fromEntries(details.stdout
|
|
327
|
+
.split("\n")
|
|
328
|
+
.map((item) => item.split("=", 2))
|
|
329
|
+
.filter((item) => item.length === 2));
|
|
330
|
+
if (values.Type === "x11" && values.Active === "yes" && values.Display) {
|
|
331
|
+
return values.Display;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ChatRoomError } from "../../core/errors/chatroom-error.js";
|
|
2
|
+
import type { ComputerActionResult, ComputerSnapshot, ComputerStatus } from "./types.js";
|
|
3
|
+
export declare const COMPUTER_NATIVE_PROTOCOL_VERSION = 1;
|
|
4
|
+
export type ComputerNativeMethod = "status" | "requestPermission" | "snapshot" | "action";
|
|
5
|
+
export interface ComputerNativeResultMap {
|
|
6
|
+
status: Omit<ComputerStatus, "settings">;
|
|
7
|
+
requestPermission: Omit<ComputerStatus, "settings">;
|
|
8
|
+
snapshot: ComputerSnapshot;
|
|
9
|
+
action: ComputerActionResult;
|
|
10
|
+
}
|
|
11
|
+
export interface ComputerNativeError {
|
|
12
|
+
code?: string;
|
|
13
|
+
message: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function parseNativeEnvelope(value: unknown): {
|
|
16
|
+
id: string;
|
|
17
|
+
result?: unknown;
|
|
18
|
+
error?: ComputerNativeError;
|
|
19
|
+
};
|
|
20
|
+
export declare function parseNativeResult<M extends ComputerNativeMethod>(method: M, value: unknown): ComputerNativeResultMap[M];
|
|
21
|
+
export declare function nativeError(error: ComputerNativeError): ChatRoomError;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ChatRoomError, } from "../../core/errors/chatroom-error.js";
|
|
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).optional(),
|
|
8
|
+
id: z.string(),
|
|
9
|
+
result: z.unknown().optional(),
|
|
10
|
+
error: z
|
|
11
|
+
.object({
|
|
12
|
+
code: z.string().optional(),
|
|
13
|
+
message: z.string(),
|
|
14
|
+
})
|
|
15
|
+
.optional(),
|
|
16
|
+
})
|
|
17
|
+
.refine((value) => value.result !== undefined || value.error !== undefined, {
|
|
18
|
+
message: "Native response must contain result or error",
|
|
19
|
+
});
|
|
20
|
+
export function parseNativeEnvelope(value) {
|
|
21
|
+
const parsed = responseEnvelopeSchema.safeParse(value);
|
|
22
|
+
if (!parsed.success) {
|
|
23
|
+
throw new ChatRoomError("INTERNAL", "Computer helper returned an invalid protocol response", { issues: parsed.error.issues });
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
id: parsed.data.id,
|
|
27
|
+
...(parsed.data.result === undefined ? {} : { result: parsed.data.result }),
|
|
28
|
+
...(parsed.data.error === undefined
|
|
29
|
+
? {}
|
|
30
|
+
: {
|
|
31
|
+
error: {
|
|
32
|
+
message: parsed.data.error.message,
|
|
33
|
+
...(parsed.data.error.code === undefined
|
|
34
|
+
? {}
|
|
35
|
+
: { code: parsed.data.error.code }),
|
|
36
|
+
},
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function parseNativeResult(method, value) {
|
|
41
|
+
const schema = method === "status" || method === "requestPermission"
|
|
42
|
+
? computerNativeStatusSchema
|
|
43
|
+
: method === "snapshot"
|
|
44
|
+
? computerSnapshotSchema
|
|
45
|
+
: computerActionResultSchema;
|
|
46
|
+
const parsed = schema.safeParse(value);
|
|
47
|
+
if (!parsed.success) {
|
|
48
|
+
throw new ChatRoomError("INTERNAL", `Computer helper returned an invalid ${method} result`, { issues: parsed.error.issues });
|
|
49
|
+
}
|
|
50
|
+
return parsed.data;
|
|
51
|
+
}
|
|
52
|
+
export function nativeError(error) {
|
|
53
|
+
return new ChatRoomError(nativeErrorCode(error.code), error.message, {
|
|
54
|
+
nativeCode: error.code ?? null,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function nativeErrorCode(code) {
|
|
58
|
+
switch (code) {
|
|
59
|
+
case "invalid_request":
|
|
60
|
+
return "INVALID_INPUT";
|
|
61
|
+
case "not_found":
|
|
62
|
+
return "NOT_FOUND";
|
|
63
|
+
case "permission_required":
|
|
64
|
+
return "FORBIDDEN";
|
|
65
|
+
case "stale_snapshot":
|
|
66
|
+
return "CONFLICT";
|
|
67
|
+
case "unsupported":
|
|
68
|
+
return "UNSUPPORTED";
|
|
69
|
+
default:
|
|
70
|
+
return "INTERNAL";
|
|
71
|
+
}
|
|
72
|
+
}
|