@chatroomcp/chatroom 0.1.6 → 0.2.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/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/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/cloud/tunnel-client.d.ts +15 -1
- package/dist/plugins/cloud/tunnel-client.js +69 -3
- 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 +26 -0
- package/dist/plugins/computer/computer-native-host.js +245 -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 +317 -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-BXlxEk8f.css +1 -0
- package/dist/web/assets/index-DUM6zVHS.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,58 @@
|
|
|
1
|
+
import { ChatRoomError } from "../../core/errors/chatroom-error.js";
|
|
2
|
+
import { ComputerNativeHost } from "./computer-native-host.js";
|
|
3
|
+
import { parseNativeResult } from "./computer-protocol.js";
|
|
4
|
+
export class NativeComputerBackend {
|
|
5
|
+
host = new ComputerNativeHost();
|
|
6
|
+
restartWhenGranted = new Set();
|
|
7
|
+
async status() {
|
|
8
|
+
const value = await this.readStatus().catch(() => ({
|
|
9
|
+
platform: this.host.platform,
|
|
10
|
+
helper: "unavailable",
|
|
11
|
+
permissions: {
|
|
12
|
+
accessibility: "unknown",
|
|
13
|
+
screenRecording: "unknown",
|
|
14
|
+
},
|
|
15
|
+
displays: [],
|
|
16
|
+
}));
|
|
17
|
+
if (this.host.platform !== "macos" || value.helper !== "running")
|
|
18
|
+
return value;
|
|
19
|
+
const newlyGranted = [...this.restartWhenGranted].filter((permission) => value.permissions[permission] === "granted");
|
|
20
|
+
if (!newlyGranted.length || !this.host.idle)
|
|
21
|
+
return value;
|
|
22
|
+
for (const permission of newlyGranted)
|
|
23
|
+
this.restartWhenGranted.delete(permission);
|
|
24
|
+
this.host.restart("Computer helper restarting after permission change");
|
|
25
|
+
return this.readStatus();
|
|
26
|
+
}
|
|
27
|
+
async requestPermission(permission) {
|
|
28
|
+
if (this.host.platform !== "macos")
|
|
29
|
+
throw new ChatRoomError("UNSUPPORTED", "Permission requests are currently supported only on macOS");
|
|
30
|
+
const result = await this.host.request("requestPermission", { permission });
|
|
31
|
+
const value = parseNativeResult("requestPermission", result);
|
|
32
|
+
if (value.permissions[permission] !== "granted") {
|
|
33
|
+
this.restartWhenGranted.add(permission);
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
this.restartWhenGranted.delete(permission);
|
|
37
|
+
this.host.restart("Computer helper restarting after permission change");
|
|
38
|
+
return this.readStatus();
|
|
39
|
+
}
|
|
40
|
+
async snapshot(request, revision) {
|
|
41
|
+
const result = await this.host.request("snapshot", {
|
|
42
|
+
...request,
|
|
43
|
+
revision,
|
|
44
|
+
});
|
|
45
|
+
return parseNativeResult("snapshot", result);
|
|
46
|
+
}
|
|
47
|
+
async action(request, revision) {
|
|
48
|
+
const result = await this.host.request("action", { ...request, revision });
|
|
49
|
+
return parseNativeResult("action", result);
|
|
50
|
+
}
|
|
51
|
+
async dispose() {
|
|
52
|
+
this.restartWhenGranted.clear();
|
|
53
|
+
await this.host.dispose();
|
|
54
|
+
}
|
|
55
|
+
async readStatus() {
|
|
56
|
+
return parseNativeResult("status", await this.host.request("status", {}));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ComputerNativeMethod } from "./computer-protocol.js";
|
|
2
|
+
import type { ComputerPlatform } from "./types.js";
|
|
3
|
+
export declare class ComputerNativeHost {
|
|
4
|
+
private child;
|
|
5
|
+
private socket;
|
|
6
|
+
private socketServer;
|
|
7
|
+
private socketPath;
|
|
8
|
+
private lines;
|
|
9
|
+
private starting;
|
|
10
|
+
private readonly pending;
|
|
11
|
+
private sequence;
|
|
12
|
+
get platform(): ComputerPlatform;
|
|
13
|
+
get idle(): boolean;
|
|
14
|
+
request(method: ComputerNativeMethod, params: unknown): Promise<unknown>;
|
|
15
|
+
restart(reason?: string): void;
|
|
16
|
+
dispose(): Promise<void>;
|
|
17
|
+
private ensureStarted;
|
|
18
|
+
private startMacHelper;
|
|
19
|
+
private attachSocket;
|
|
20
|
+
private startWindowsHelper;
|
|
21
|
+
private failTransport;
|
|
22
|
+
private reset;
|
|
23
|
+
private rejectPending;
|
|
24
|
+
private cleanupSocketPath;
|
|
25
|
+
private handleLine;
|
|
26
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { spawn } 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
|
+
return "unsupported";
|
|
25
|
+
}
|
|
26
|
+
get idle() {
|
|
27
|
+
return this.pending.size === 0;
|
|
28
|
+
}
|
|
29
|
+
async request(method, params) {
|
|
30
|
+
await this.ensureStarted();
|
|
31
|
+
const id = `computer_${++this.sequence}`;
|
|
32
|
+
const timeoutMs = requestTimeoutMs(method);
|
|
33
|
+
const promise = new Promise((resolve, reject) => {
|
|
34
|
+
const timer = setTimeout(() => {
|
|
35
|
+
this.pending.delete(id);
|
|
36
|
+
const error = new ChatRoomError("INTERNAL", `Computer helper ${method} request timed out after ${timeoutMs} ms`);
|
|
37
|
+
reject(error);
|
|
38
|
+
this.reset(error);
|
|
39
|
+
}, timeoutMs);
|
|
40
|
+
timer.unref();
|
|
41
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
42
|
+
});
|
|
43
|
+
const line = `${JSON.stringify({
|
|
44
|
+
protocol: COMPUTER_NATIVE_PROTOCOL_VERSION,
|
|
45
|
+
id,
|
|
46
|
+
method,
|
|
47
|
+
params,
|
|
48
|
+
})}\n`;
|
|
49
|
+
if (this.platform === "macos")
|
|
50
|
+
this.socket.write(line);
|
|
51
|
+
else
|
|
52
|
+
this.child.stdin.write(line);
|
|
53
|
+
return promise;
|
|
54
|
+
}
|
|
55
|
+
restart(reason = "Computer helper restarting") {
|
|
56
|
+
this.reset(new Error(reason));
|
|
57
|
+
}
|
|
58
|
+
async dispose() {
|
|
59
|
+
this.reset(new Error("Computer helper stopped"));
|
|
60
|
+
}
|
|
61
|
+
async ensureStarted() {
|
|
62
|
+
if (this.platform === "macos" && this.socket && !this.socket.destroyed)
|
|
63
|
+
return;
|
|
64
|
+
if (this.platform === "windows" && this.child && !this.child.killed)
|
|
65
|
+
return;
|
|
66
|
+
if (this.starting)
|
|
67
|
+
return this.starting;
|
|
68
|
+
this.starting =
|
|
69
|
+
this.platform === "macos"
|
|
70
|
+
? this.startMacHelper()
|
|
71
|
+
: this.platform === "windows"
|
|
72
|
+
? this.startWindowsHelper()
|
|
73
|
+
: Promise.reject(new ChatRoomError("UNSUPPORTED", "Computer Use is supported only on macOS and Windows"));
|
|
74
|
+
try {
|
|
75
|
+
await this.starting;
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
this.starting = null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async startMacHelper() {
|
|
82
|
+
const app = macHelperAppPath();
|
|
83
|
+
if (!app)
|
|
84
|
+
throw new ChatRoomError("UNSUPPORTED", "macOS Computer helper is missing");
|
|
85
|
+
this.cleanupSocketPath();
|
|
86
|
+
const socketPath = path.join("/tmp", `chatroom-c-${process.pid}-${randomUUID().slice(0, 8)}.sock`);
|
|
87
|
+
this.socketPath = socketPath;
|
|
88
|
+
await new Promise((resolve, reject) => {
|
|
89
|
+
const server = createServer();
|
|
90
|
+
this.socketServer = server;
|
|
91
|
+
let settled = false;
|
|
92
|
+
const timer = setTimeout(() => fail(new Error("Computer helper connection timed out")), 10_000);
|
|
93
|
+
timer.unref();
|
|
94
|
+
const cleanupServer = () => {
|
|
95
|
+
clearTimeout(timer);
|
|
96
|
+
if (this.socketServer === server)
|
|
97
|
+
this.socketServer = null;
|
|
98
|
+
server.close();
|
|
99
|
+
};
|
|
100
|
+
const fail = (error) => {
|
|
101
|
+
if (settled)
|
|
102
|
+
return;
|
|
103
|
+
settled = true;
|
|
104
|
+
cleanupServer();
|
|
105
|
+
this.cleanupSocketPath();
|
|
106
|
+
reject(error);
|
|
107
|
+
};
|
|
108
|
+
server.once("error", fail);
|
|
109
|
+
server.once("connection", (socket) => {
|
|
110
|
+
if (settled) {
|
|
111
|
+
socket.destroy();
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
settled = true;
|
|
115
|
+
server.removeListener("error", fail);
|
|
116
|
+
cleanupServer();
|
|
117
|
+
this.attachSocket(socket);
|
|
118
|
+
resolve();
|
|
119
|
+
});
|
|
120
|
+
server.listen(socketPath, () => {
|
|
121
|
+
chmodSync(socketPath, 0o600);
|
|
122
|
+
const launcher = spawn("/usr/bin/open", ["-n", "-g", app, "--args", "--connect", socketPath], { stdio: "ignore" });
|
|
123
|
+
launcher.once("error", fail);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
attachSocket(socket) {
|
|
128
|
+
this.socket = socket;
|
|
129
|
+
this.lines = createInterface({ input: socket });
|
|
130
|
+
this.lines.on("line", (line) => this.handleLine(line));
|
|
131
|
+
socket.once("error", (error) => this.failTransport(socket, error));
|
|
132
|
+
socket.once("close", () => this.failTransport(socket, new Error("Computer helper exited")));
|
|
133
|
+
}
|
|
134
|
+
async startWindowsHelper() {
|
|
135
|
+
const executable = windowsHelperPath();
|
|
136
|
+
if (!executable)
|
|
137
|
+
throw new ChatRoomError("UNSUPPORTED", "Windows Computer helper is missing");
|
|
138
|
+
const child = spawn(executable, [], {
|
|
139
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
140
|
+
windowsHide: true,
|
|
141
|
+
});
|
|
142
|
+
this.child = child;
|
|
143
|
+
this.lines = createInterface({ input: child.stdout });
|
|
144
|
+
this.lines.on("line", (line) => this.handleLine(line));
|
|
145
|
+
child.stderr.on("data", (chunk) => console.error("[computer-helper]", String(chunk).trimEnd()));
|
|
146
|
+
child.once("error", (error) => this.failTransport(child, error));
|
|
147
|
+
child.once("exit", () => this.failTransport(child, new Error("Computer helper exited")));
|
|
148
|
+
}
|
|
149
|
+
failTransport(transport, error) {
|
|
150
|
+
const isCurrentSocket = transport === this.socket;
|
|
151
|
+
const isCurrentChild = transport === this.child;
|
|
152
|
+
if (!isCurrentSocket && !isCurrentChild)
|
|
153
|
+
return;
|
|
154
|
+
if (isCurrentSocket)
|
|
155
|
+
this.socket = null;
|
|
156
|
+
if (isCurrentChild)
|
|
157
|
+
this.child = null;
|
|
158
|
+
this.lines?.close();
|
|
159
|
+
this.lines = null;
|
|
160
|
+
this.cleanupSocketPath();
|
|
161
|
+
this.rejectPending(error);
|
|
162
|
+
}
|
|
163
|
+
reset(error) {
|
|
164
|
+
this.lines?.close();
|
|
165
|
+
this.lines = null;
|
|
166
|
+
this.socket?.destroy();
|
|
167
|
+
this.socket = null;
|
|
168
|
+
this.socketServer?.close();
|
|
169
|
+
this.socketServer = null;
|
|
170
|
+
this.child?.kill();
|
|
171
|
+
this.child = null;
|
|
172
|
+
this.cleanupSocketPath();
|
|
173
|
+
this.rejectPending(error);
|
|
174
|
+
}
|
|
175
|
+
rejectPending(error) {
|
|
176
|
+
for (const item of this.pending.values()) {
|
|
177
|
+
clearTimeout(item.timer);
|
|
178
|
+
item.reject(error);
|
|
179
|
+
}
|
|
180
|
+
this.pending.clear();
|
|
181
|
+
}
|
|
182
|
+
cleanupSocketPath() {
|
|
183
|
+
if (!this.socketPath)
|
|
184
|
+
return;
|
|
185
|
+
rmSync(this.socketPath, { force: true });
|
|
186
|
+
this.socketPath = null;
|
|
187
|
+
}
|
|
188
|
+
handleLine(line) {
|
|
189
|
+
let raw;
|
|
190
|
+
try {
|
|
191
|
+
raw = JSON.parse(line);
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const rawId = raw && typeof raw === "object" && "id" in raw
|
|
197
|
+
? raw.id
|
|
198
|
+
: undefined;
|
|
199
|
+
if (typeof rawId !== "string")
|
|
200
|
+
return;
|
|
201
|
+
const pending = this.pending.get(rawId);
|
|
202
|
+
if (!pending)
|
|
203
|
+
return;
|
|
204
|
+
let message;
|
|
205
|
+
try {
|
|
206
|
+
message = parseNativeEnvelope(raw);
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
this.pending.delete(rawId);
|
|
210
|
+
clearTimeout(pending.timer);
|
|
211
|
+
pending.reject(error);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
this.pending.delete(message.id);
|
|
215
|
+
clearTimeout(pending.timer);
|
|
216
|
+
if (message.error)
|
|
217
|
+
pending.reject(nativeError(message.error));
|
|
218
|
+
else
|
|
219
|
+
pending.resolve(message.result);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function requestTimeoutMs(method) {
|
|
223
|
+
switch (method) {
|
|
224
|
+
case "status":
|
|
225
|
+
return 5_000;
|
|
226
|
+
case "snapshot":
|
|
227
|
+
return 15_000;
|
|
228
|
+
case "requestPermission":
|
|
229
|
+
return 30_000;
|
|
230
|
+
case "action":
|
|
231
|
+
return 60_000;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function projectRoot() {
|
|
235
|
+
const current = fileURLToPath(import.meta.url);
|
|
236
|
+
return path.resolve(path.dirname(current), "../../..");
|
|
237
|
+
}
|
|
238
|
+
function macHelperAppPath() {
|
|
239
|
+
const app = path.join(projectRoot(), "dist", "native", "macos", "ChatRoomComputerHelper.app");
|
|
240
|
+
return existsSync(app) ? app : null;
|
|
241
|
+
}
|
|
242
|
+
function windowsHelperPath() {
|
|
243
|
+
const executable = path.join(projectRoot(), "dist", "native", "windows", "chatroom-computer-helper.exe");
|
|
244
|
+
return existsSync(executable) ? executable : null;
|
|
245
|
+
}
|
|
@@ -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
|
+
}
|