@blaxel/core 0.2.36-dev.185 → 0.2.36-dev.190
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/cjs/.tsbuildinfo +1 -0
- package/dist/{common → cjs/common}/settings.js +2 -2
- package/dist/{tools → cjs/types/tools}/index.d.ts +1 -0
- package/dist/esm/.tsbuildinfo +1 -0
- package/dist/esm/agents/index.js +104 -0
- package/dist/esm/authentication/apikey.js +20 -0
- package/dist/esm/authentication/clientcredentials.js +81 -0
- package/dist/esm/authentication/credentials.js +13 -0
- package/dist/esm/authentication/deviceMode.js +66 -0
- package/dist/esm/authentication/index.js +56 -0
- package/dist/esm/authentication/types.js +1 -0
- package/dist/esm/cache/index.js +20 -0
- package/dist/esm/client/authentication.js +11 -0
- package/dist/esm/client/client.gen.js +5 -0
- package/dist/esm/client/client.js +1 -0
- package/dist/esm/client/index.js +3 -0
- package/dist/esm/client/interceptors.js +14 -0
- package/dist/esm/client/sdk.gen.js +1649 -0
- package/dist/esm/client/types.gen.js +3 -0
- package/dist/esm/common/autoload.js +23 -0
- package/dist/esm/common/env.js +36 -0
- package/dist/esm/common/errors.js +14 -0
- package/dist/esm/common/internal.js +182 -0
- package/dist/esm/common/internal.test.js +37 -0
- package/dist/esm/common/logger.js +65 -0
- package/dist/esm/common/node.js +20 -0
- package/dist/esm/common/settings.js +167 -0
- package/dist/esm/index.browser.test.js +10 -0
- package/dist/esm/index.js +17 -0
- package/dist/esm/jobs/index.js +3 -0
- package/dist/esm/jobs/jobs.js +86 -0
- package/dist/esm/jobs/start.js +62 -0
- package/dist/esm/jobs/types.js +1 -0
- package/dist/esm/mcp/client.js +243 -0
- package/dist/esm/mcp/index.js +2 -0
- package/dist/esm/mcp/server.js +176 -0
- package/dist/esm/models/index.js +25 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/sandbox/action.js +79 -0
- package/dist/esm/sandbox/client/client.gen.js +3 -0
- package/dist/esm/sandbox/client/index.js +3 -0
- package/dist/esm/sandbox/client/sdk.gen.js +270 -0
- package/dist/esm/sandbox/client/types.gen.js +2 -0
- package/dist/esm/sandbox/filesystem/filesystem.js +272 -0
- package/dist/esm/sandbox/filesystem/index.js +2 -0
- package/dist/esm/sandbox/filesystem/types.js +1 -0
- package/dist/esm/sandbox/index.js +7 -0
- package/dist/esm/sandbox/network/index.js +1 -0
- package/dist/esm/sandbox/network/network.js +6 -0
- package/dist/esm/sandbox/preview.js +141 -0
- package/dist/esm/sandbox/process/index.js +1 -0
- package/dist/esm/sandbox/process/process.js +185 -0
- package/dist/esm/sandbox/sandbox.js +174 -0
- package/dist/esm/sandbox/session.js +119 -0
- package/dist/esm/sandbox/types.js +76 -0
- package/dist/esm/telemetry/telemetry.js +74 -0
- package/dist/esm/tools/index.js +44 -0
- package/dist/esm/tools/mcpTool.js +213 -0
- package/dist/esm/tools/types.js +1 -0
- package/dist/esm/tools/zodSchema.js +43 -0
- package/dist/esm/volume/index.js +109 -0
- package/package.json +17 -30
- /package/dist/{agents → cjs/agents}/index.js +0 -0
- /package/dist/{authentication → cjs/authentication}/apikey.js +0 -0
- /package/dist/{authentication → cjs/authentication}/clientcredentials.js +0 -0
- /package/dist/{authentication → cjs/authentication}/credentials.js +0 -0
- /package/dist/{authentication → cjs/authentication}/deviceMode.js +0 -0
- /package/dist/{authentication → cjs/authentication}/index.js +0 -0
- /package/dist/{authentication → cjs/authentication}/types.js +0 -0
- /package/dist/{cache → cjs/cache}/index.js +0 -0
- /package/dist/{client → cjs/client}/authentication.js +0 -0
- /package/dist/{client → cjs/client}/client.gen.js +0 -0
- /package/dist/{client → cjs/client}/client.js +0 -0
- /package/dist/{client → cjs/client}/index.js +0 -0
- /package/dist/{client → cjs/client}/interceptors.js +0 -0
- /package/dist/{client → cjs/client}/sdk.gen.js +0 -0
- /package/dist/{client → cjs/client}/types.gen.js +0 -0
- /package/dist/{common → cjs/common}/autoload.js +0 -0
- /package/dist/{common → cjs/common}/env.js +0 -0
- /package/dist/{common → cjs/common}/errors.js +0 -0
- /package/dist/{common → cjs/common}/internal.js +0 -0
- /package/dist/{common → cjs/common}/internal.test.js +0 -0
- /package/dist/{common → cjs/common}/logger.js +0 -0
- /package/dist/{common → cjs/common}/node.js +0 -0
- /package/dist/{index.browser.test.js → cjs/index.browser.test.js} +0 -0
- /package/dist/{index.js → cjs/index.js} +0 -0
- /package/dist/{jobs → cjs/jobs}/index.js +0 -0
- /package/dist/{jobs → cjs/jobs}/jobs.js +0 -0
- /package/dist/{jobs → cjs/jobs}/start.js +0 -0
- /package/dist/{jobs → cjs/jobs}/types.js +0 -0
- /package/dist/{mcp → cjs/mcp}/client.js +0 -0
- /package/dist/{mcp → cjs/mcp}/index.js +0 -0
- /package/dist/{mcp → cjs/mcp}/server.js +0 -0
- /package/dist/{models → cjs/models}/index.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/action.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/client/client.gen.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/client/index.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/client/sdk.gen.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/client/types.gen.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/filesystem/filesystem.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/filesystem/index.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/filesystem/types.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/index.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/network/index.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/network/network.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/preview.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/process/index.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/process/process.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/sandbox.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/session.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/types.js +0 -0
- /package/dist/{telemetry → cjs/telemetry}/telemetry.js +0 -0
- /package/dist/{tools → cjs/tools}/index.js +0 -0
- /package/dist/{tools → cjs/tools}/mcpTool.js +0 -0
- /package/dist/{tools → cjs/tools}/types.js +0 -0
- /package/dist/{tools → cjs/tools}/zodSchema.js +0 -0
- /package/dist/{agents → cjs/types/agents}/index.d.ts +0 -0
- /package/dist/{authentication → cjs/types/authentication}/apikey.d.ts +0 -0
- /package/dist/{authentication → cjs/types/authentication}/clientcredentials.d.ts +0 -0
- /package/dist/{authentication → cjs/types/authentication}/credentials.d.ts +0 -0
- /package/dist/{authentication → cjs/types/authentication}/deviceMode.d.ts +0 -0
- /package/dist/{authentication → cjs/types/authentication}/index.d.ts +0 -0
- /package/dist/{authentication → cjs/types/authentication}/types.d.ts +0 -0
- /package/dist/{cache → cjs/types/cache}/index.d.ts +0 -0
- /package/dist/{client → cjs/types/client}/authentication.d.ts +0 -0
- /package/dist/{client → cjs/types/client}/client.d.ts +0 -0
- /package/dist/{client → cjs/types/client}/client.gen.d.ts +0 -0
- /package/dist/{client → cjs/types/client}/index.d.ts +0 -0
- /package/dist/{client → cjs/types/client}/interceptors.d.ts +0 -0
- /package/dist/{client → cjs/types/client}/sdk.gen.d.ts +0 -0
- /package/dist/{client → cjs/types/client}/types.gen.d.ts +0 -0
- /package/dist/{common → cjs/types/common}/autoload.d.ts +0 -0
- /package/dist/{common → cjs/types/common}/env.d.ts +0 -0
- /package/dist/{common → cjs/types/common}/errors.d.ts +0 -0
- /package/dist/{common → cjs/types/common}/internal.d.ts +0 -0
- /package/dist/{common → cjs/types/common}/internal.test.d.ts +0 -0
- /package/dist/{common → cjs/types/common}/logger.d.ts +0 -0
- /package/dist/{common → cjs/types/common}/node.d.ts +0 -0
- /package/dist/{common → cjs/types/common}/settings.d.ts +0 -0
- /package/dist/{index.browser.test.d.ts → cjs/types/index.browser.test.d.ts} +0 -0
- /package/dist/{index.d.ts → cjs/types/index.d.ts} +0 -0
- /package/dist/{jobs → cjs/types/jobs}/index.d.ts +0 -0
- /package/dist/{jobs → cjs/types/jobs}/jobs.d.ts +0 -0
- /package/dist/{jobs → cjs/types/jobs}/start.d.ts +0 -0
- /package/dist/{jobs → cjs/types/jobs}/types.d.ts +0 -0
- /package/dist/{mcp → cjs/types/mcp}/client.d.ts +0 -0
- /package/dist/{mcp → cjs/types/mcp}/index.d.ts +0 -0
- /package/dist/{mcp → cjs/types/mcp}/server.d.ts +0 -0
- /package/dist/{models → cjs/types/models}/index.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/action.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/client/client.gen.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/client/index.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/client/sdk.gen.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/client/types.gen.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/filesystem/filesystem.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/filesystem/index.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/filesystem/types.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/index.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/network/index.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/network/network.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/preview.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/process/index.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/process/process.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/sandbox.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/session.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/types.d.ts +0 -0
- /package/dist/{telemetry → cjs/types/telemetry}/telemetry.d.ts +0 -0
- /package/dist/{tools → cjs/types/tools}/mcpTool.d.ts +0 -0
- /package/dist/{tools → cjs/types/tools}/types.d.ts +0 -0
- /package/dist/{tools → cjs/types/tools}/zodSchema.d.ts +0 -0
- /package/dist/{volume → cjs/types/volume}/index.d.ts +0 -0
- /package/dist/{volume → cjs/volume}/index.js +0 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { authenticate } from '../common/autoload.js';
|
|
2
|
+
import { env } from '../common/env.js';
|
|
3
|
+
import { flush } from '../telemetry/telemetry.js';
|
|
4
|
+
class BlJobWrapper {
|
|
5
|
+
async getArguments() {
|
|
6
|
+
if (!env.BL_EXECUTION_DATA_URL) {
|
|
7
|
+
const args = this.parseCommandLineArgs();
|
|
8
|
+
return args;
|
|
9
|
+
}
|
|
10
|
+
const response = await fetch(env.BL_EXECUTION_DATA_URL);
|
|
11
|
+
const data = await response.json();
|
|
12
|
+
return data.tasks[this.index] ?? {};
|
|
13
|
+
}
|
|
14
|
+
parseCommandLineArgs() {
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
const result = {};
|
|
17
|
+
for (let i = 0; i < args.length; i++) {
|
|
18
|
+
const arg = args[i];
|
|
19
|
+
if (arg.startsWith('--')) {
|
|
20
|
+
const key = arg.slice(2);
|
|
21
|
+
const value = args[i + 1];
|
|
22
|
+
if (value && !value.startsWith('--')) {
|
|
23
|
+
result[key] = value;
|
|
24
|
+
i++;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
result[key] = 'true';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
get indexKey() {
|
|
34
|
+
return env.BL_TASK_KEY ?? "TASK_INDEX";
|
|
35
|
+
}
|
|
36
|
+
get index() {
|
|
37
|
+
return env[this.indexKey] ? Number(env[this.indexKey]) : 0;
|
|
38
|
+
}
|
|
39
|
+
/*
|
|
40
|
+
Run a job defined in a function, it's run in the current process
|
|
41
|
+
*/
|
|
42
|
+
async start(func) {
|
|
43
|
+
await authenticate();
|
|
44
|
+
const parsedArgs = await this.getArguments();
|
|
45
|
+
await func(parsedArgs);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export const blStartJob = (func) => {
|
|
49
|
+
const job = new BlJobWrapper();
|
|
50
|
+
job.start(func).then(async () => {
|
|
51
|
+
try {
|
|
52
|
+
await flush();
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
console.error('Error flushing telemetry:', error);
|
|
56
|
+
}
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}).catch((error) => {
|
|
59
|
+
console.error('Job execution failed:', error);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
});
|
|
62
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { JSONRPCMessageSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { logger } from "../common/logger.js";
|
|
3
|
+
import { settings } from "../common/settings.js";
|
|
4
|
+
// Detect environment
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
6
|
+
const isBrowser = typeof globalThis !== "undefined" && globalThis.window !== undefined;
|
|
7
|
+
// Conditional import for Node.js WebSocket
|
|
8
|
+
let NodeWebSocket;
|
|
9
|
+
if (!isBrowser) {
|
|
10
|
+
try {
|
|
11
|
+
// Dynamic import for Node.js environment
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-require-imports
|
|
13
|
+
NodeWebSocket = require("ws");
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
console.warn("ws is not available in this environment");
|
|
17
|
+
// ws is not available
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//const SUBPROTOCOL = "mcp";
|
|
21
|
+
// Helper function to wait
|
|
22
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
23
|
+
/**
|
|
24
|
+
* Client transport for WebSocket: this will connect to a server over the WebSocket protocol.
|
|
25
|
+
* Works in both browser and Node.js environments.
|
|
26
|
+
*/
|
|
27
|
+
export class BlaxelMcpClientTransport {
|
|
28
|
+
_socket;
|
|
29
|
+
_url;
|
|
30
|
+
_headers;
|
|
31
|
+
_isBrowser;
|
|
32
|
+
_retry_max;
|
|
33
|
+
_retry_delay;
|
|
34
|
+
onclose;
|
|
35
|
+
onerror;
|
|
36
|
+
onmessage;
|
|
37
|
+
constructor(url, headers, options) {
|
|
38
|
+
this._url = new URL(url.replace("http", "ws"));
|
|
39
|
+
this._retry_max = options?.retry?.max ?? 3;
|
|
40
|
+
this._retry_delay = options?.retry?.delay ?? 1000;
|
|
41
|
+
this._headers = headers ?? {};
|
|
42
|
+
this._isBrowser = isBrowser;
|
|
43
|
+
}
|
|
44
|
+
async start() {
|
|
45
|
+
if (this._socket) {
|
|
46
|
+
throw new Error("Blaxel already started! If using Client class, note that connect() calls start() automatically.");
|
|
47
|
+
}
|
|
48
|
+
let attempts = 0;
|
|
49
|
+
const maxAttempts = Math.max(1, this._retry_max + 1); // Ensure at least 1 attempt
|
|
50
|
+
while (attempts < maxAttempts) {
|
|
51
|
+
try {
|
|
52
|
+
await this._connect();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
attempts++;
|
|
57
|
+
if (error instanceof Error) {
|
|
58
|
+
logger.warn(error.stack ?? error.message);
|
|
59
|
+
}
|
|
60
|
+
if (attempts >= maxAttempts) {
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
logger.debug(`WebSocket connection attempt ${attempts} failed, retrying in ${this._retry_delay}ms...`);
|
|
64
|
+
await delay(this._retry_delay);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
_connect() {
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
70
|
+
return new Promise((resolve, reject) => {
|
|
71
|
+
try {
|
|
72
|
+
if (this._isBrowser) {
|
|
73
|
+
// Use native browser WebSocket
|
|
74
|
+
const url = `${this._url.toString()}?token=${settings.token}`;
|
|
75
|
+
this._socket = new WebSocket(url);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
// Use Node.js WebSocket
|
|
79
|
+
if (!NodeWebSocket) {
|
|
80
|
+
throw new Error("WebSocket library not available in Node.js environment");
|
|
81
|
+
}
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
83
|
+
this._socket = new NodeWebSocket(this._url, {
|
|
84
|
+
//protocols: SUBPROTOCOL,
|
|
85
|
+
headers: this._headers,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
this._socket.onerror = (event) => {
|
|
89
|
+
// Log websocket error with meaningful information instead of raw event
|
|
90
|
+
const errorInfo = {
|
|
91
|
+
type: 'WebSocket Error',
|
|
92
|
+
url: this._url.toString(),
|
|
93
|
+
readyState: this._socket?.readyState,
|
|
94
|
+
browser: this._isBrowser,
|
|
95
|
+
// Extract any available error details from the event
|
|
96
|
+
eventType: event && typeof event === 'object' && 'type' in event
|
|
97
|
+
? String(event.type)
|
|
98
|
+
: 'unknown',
|
|
99
|
+
// Browser events might have different properties than Node.js
|
|
100
|
+
message: this._isBrowser && event && typeof event === 'object' && 'message' in event
|
|
101
|
+
? String(event.message)
|
|
102
|
+
: undefined,
|
|
103
|
+
error: !this._isBrowser && event && typeof event === 'object' && 'error' in event
|
|
104
|
+
? String(event.error)
|
|
105
|
+
: undefined
|
|
106
|
+
};
|
|
107
|
+
logger.error('WebSocket connection error', errorInfo);
|
|
108
|
+
const error = this._isBrowser
|
|
109
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
110
|
+
? new Error(`WebSocket error: ${event.message}`)
|
|
111
|
+
: "error" in event
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
113
|
+
? event.error
|
|
114
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
115
|
+
: new Error(`WebSocket error: ${event.message}`);
|
|
116
|
+
reject(error);
|
|
117
|
+
this.onerror?.(error);
|
|
118
|
+
};
|
|
119
|
+
this._socket.onopen = () => {
|
|
120
|
+
resolve();
|
|
121
|
+
};
|
|
122
|
+
this._socket.onclose = () => {
|
|
123
|
+
this.onclose?.();
|
|
124
|
+
this._socket = undefined;
|
|
125
|
+
};
|
|
126
|
+
this._socket.onmessage = (event) => {
|
|
127
|
+
let message;
|
|
128
|
+
try {
|
|
129
|
+
let dataString;
|
|
130
|
+
if (this._isBrowser) {
|
|
131
|
+
// Browser WebSocket MessageEvent
|
|
132
|
+
const browserEvent = event;
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
134
|
+
dataString = typeof browserEvent.data === "string"
|
|
135
|
+
? browserEvent.data
|
|
136
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
137
|
+
: browserEvent.data.toString();
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
// Node.js WebSocket MessageEvent
|
|
141
|
+
const nodeEvent = event;
|
|
142
|
+
if (typeof nodeEvent.data === "string") {
|
|
143
|
+
dataString = nodeEvent.data;
|
|
144
|
+
}
|
|
145
|
+
else if (nodeEvent.data instanceof Buffer) {
|
|
146
|
+
dataString = nodeEvent.data.toString("utf-8");
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
throw new Error("Unsupported data type for event.data");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
message = JSONRPCMessageSchema.parse(JSON.parse(dataString));
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
logger.error(`Error parsing message: ${
|
|
156
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
157
|
+
typeof event.data === "object"
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
159
|
+
? JSON.stringify(event.data)
|
|
160
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
161
|
+
: event.data}`);
|
|
162
|
+
this.onerror?.(error);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
this.onmessage?.(message);
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
if (error instanceof Error && error.message.includes("ws does not work in the browser")) {
|
|
170
|
+
this._isBrowser = true;
|
|
171
|
+
return this._connect().then(resolve).catch(reject);
|
|
172
|
+
}
|
|
173
|
+
reject(error);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
get isConnected() {
|
|
178
|
+
if (!this._socket)
|
|
179
|
+
return false;
|
|
180
|
+
if (this._isBrowser) {
|
|
181
|
+
return this._socket.readyState === 1; // WebSocket.OPEN = 1
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
return this._socket.readyState === 1; // WebSocket.OPEN = 1
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async close() {
|
|
188
|
+
this._socket?.close();
|
|
189
|
+
this._socket = undefined;
|
|
190
|
+
this.onclose?.();
|
|
191
|
+
return Promise.resolve();
|
|
192
|
+
}
|
|
193
|
+
async send(message) {
|
|
194
|
+
let attempts = 0;
|
|
195
|
+
const maxAttempts = Math.max(1, this._retry_max + 1); // Ensure at least 1 attempt
|
|
196
|
+
while (attempts < maxAttempts) {
|
|
197
|
+
try {
|
|
198
|
+
if (!this._socket || !this.isConnected) {
|
|
199
|
+
if (!this._socket) {
|
|
200
|
+
// Only try to start if socket doesn't exist
|
|
201
|
+
await this.start();
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
throw new Error("WebSocket is not in OPEN state");
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
await new Promise((resolve, reject) => {
|
|
208
|
+
try {
|
|
209
|
+
const messageStr = JSON.stringify(message);
|
|
210
|
+
if (this._isBrowser) {
|
|
211
|
+
// Browser WebSocket
|
|
212
|
+
this._socket?.send(messageStr);
|
|
213
|
+
resolve();
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
// Node.js WebSocket
|
|
217
|
+
this._socket?.send(messageStr, (error) => {
|
|
218
|
+
if (error) {
|
|
219
|
+
reject(error);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
resolve();
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
reject(error);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
attempts++;
|
|
235
|
+
if (attempts >= maxAttempts) {
|
|
236
|
+
throw error;
|
|
237
|
+
}
|
|
238
|
+
logger.warn(`WebSocket send attempt ${attempts} failed, retrying in ${this._retry_delay}ms...`);
|
|
239
|
+
await delay(this._retry_delay);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from "uuid";
|
|
2
|
+
import WebSocket, { WebSocketServer } from "ws";
|
|
3
|
+
import { env } from "../common/env.js";
|
|
4
|
+
import { logger } from "../common/logger.js";
|
|
5
|
+
import { startSpan } from "../telemetry/telemetry.js";
|
|
6
|
+
const spans = new Map();
|
|
7
|
+
export class BlaxelMcpServerTransport {
|
|
8
|
+
port;
|
|
9
|
+
wss;
|
|
10
|
+
clients = new Map();
|
|
11
|
+
onclose;
|
|
12
|
+
onerror;
|
|
13
|
+
messageHandler;
|
|
14
|
+
onconnection;
|
|
15
|
+
ondisconnection;
|
|
16
|
+
set onmessage(handler) {
|
|
17
|
+
this.messageHandler = handler
|
|
18
|
+
? (msg, clientId) => {
|
|
19
|
+
if (!("id" in msg)) {
|
|
20
|
+
return handler(msg);
|
|
21
|
+
}
|
|
22
|
+
return handler({
|
|
23
|
+
...msg,
|
|
24
|
+
id: clientId + ":" + msg.id,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
: undefined;
|
|
28
|
+
}
|
|
29
|
+
constructor(port) {
|
|
30
|
+
this.port = port ?? parseInt(env.BL_SERVER_PORT ?? "8080", 10);
|
|
31
|
+
this.wss = new WebSocketServer({ port: this.port });
|
|
32
|
+
}
|
|
33
|
+
async start() {
|
|
34
|
+
logger.info("Starting WebSocket Server on port " + this.port);
|
|
35
|
+
this.wss.on("connection", (ws) => {
|
|
36
|
+
const clientId = uuidv4();
|
|
37
|
+
this.clients.set(clientId, {
|
|
38
|
+
ws,
|
|
39
|
+
});
|
|
40
|
+
this.onconnection?.(clientId);
|
|
41
|
+
ws.on("message", (data) => {
|
|
42
|
+
const span = startSpan("message", {
|
|
43
|
+
attributes: {
|
|
44
|
+
"mcp.client.id": clientId,
|
|
45
|
+
"span.type": "mcp.message",
|
|
46
|
+
},
|
|
47
|
+
isRoot: false,
|
|
48
|
+
});
|
|
49
|
+
try {
|
|
50
|
+
const msg = JSON.parse(data.toString());
|
|
51
|
+
this.messageHandler?.(msg, clientId);
|
|
52
|
+
if ("method" in msg && "id" in msg && "params" in msg) {
|
|
53
|
+
span.setAttributes({
|
|
54
|
+
"mcp.message.parsed": true,
|
|
55
|
+
"mcp.method": msg.method,
|
|
56
|
+
"mcp.messageId": msg.id,
|
|
57
|
+
"mcp.toolName": msg.params?.name,
|
|
58
|
+
});
|
|
59
|
+
spans.set(clientId + ":" + msg.id, span);
|
|
60
|
+
}
|
|
61
|
+
// Handle msg.id safely
|
|
62
|
+
const msgId = msg.id ? String(msg.id) : "";
|
|
63
|
+
const [cId, parsedMsgId] = msgId.split(":");
|
|
64
|
+
msg.id = parsedMsgId ? parseInt(parsedMsgId) : undefined;
|
|
65
|
+
// Use optional chaining for safe access
|
|
66
|
+
const client = this.clients.get(cId ?? "");
|
|
67
|
+
if (client?.ws?.readyState === WebSocket.OPEN) {
|
|
68
|
+
const msgSpan = spans.get(cId + ":" + (msg.id ?? ""));
|
|
69
|
+
try {
|
|
70
|
+
client.ws.send(JSON.stringify(msg));
|
|
71
|
+
if (msgSpan) {
|
|
72
|
+
msgSpan.setAttributes({
|
|
73
|
+
"mcp.message.response_sent": true,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
if (msgSpan) {
|
|
79
|
+
msgSpan.setStatus("error"); // Error status
|
|
80
|
+
msgSpan.recordException(err);
|
|
81
|
+
}
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
if (msgSpan) {
|
|
86
|
+
msgSpan.end();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
this.clients.delete(cId);
|
|
92
|
+
this.ondisconnection?.(cId);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
if (err instanceof Error) {
|
|
97
|
+
span.setStatus("error"); // Error status
|
|
98
|
+
span.recordException(err);
|
|
99
|
+
this.onerror?.(err);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
this.onerror?.(new Error(`Failed to parse message: ${String(err)}`));
|
|
103
|
+
}
|
|
104
|
+
span.end();
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
ws.on("close", () => {
|
|
108
|
+
this.clients.delete(clientId);
|
|
109
|
+
this.ondisconnection?.(clientId);
|
|
110
|
+
});
|
|
111
|
+
ws.on("error", (err) => {
|
|
112
|
+
this.onerror?.(err);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
return Promise.resolve();
|
|
116
|
+
}
|
|
117
|
+
async send(msg) {
|
|
118
|
+
const [cId, msgId] = msg.id ? String(msg.id).split(":") : [];
|
|
119
|
+
msg.id = parseInt(msgId);
|
|
120
|
+
const data = JSON.stringify(msg);
|
|
121
|
+
const deadClients = [];
|
|
122
|
+
if (cId) {
|
|
123
|
+
// Send to specific client
|
|
124
|
+
const client = this.clients.get(cId);
|
|
125
|
+
if (client?.ws?.readyState === WebSocket.OPEN) {
|
|
126
|
+
const msgSpan = spans.get(cId + ":" + msg.id);
|
|
127
|
+
try {
|
|
128
|
+
client.ws.send(data);
|
|
129
|
+
if (msgSpan) {
|
|
130
|
+
msgSpan.setAttributes({
|
|
131
|
+
"mcp.message.response_sent": true,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
if (msgSpan) {
|
|
137
|
+
msgSpan.setStatus("error"); // Error status
|
|
138
|
+
msgSpan.recordException(err);
|
|
139
|
+
}
|
|
140
|
+
throw err;
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
if (msgSpan) {
|
|
144
|
+
msgSpan.end();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
this.clients.delete(cId);
|
|
150
|
+
this.ondisconnection?.(cId);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
for (const [id, client] of this.clients.entries()) {
|
|
154
|
+
if (client.ws.readyState !== WebSocket.OPEN) {
|
|
155
|
+
deadClients.push(id);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Cleanup dead clients
|
|
159
|
+
deadClients.forEach((id) => {
|
|
160
|
+
this.clients.delete(id);
|
|
161
|
+
this.ondisconnection?.(id);
|
|
162
|
+
});
|
|
163
|
+
return Promise.resolve();
|
|
164
|
+
}
|
|
165
|
+
async broadcast(msg) {
|
|
166
|
+
return this.send(msg);
|
|
167
|
+
}
|
|
168
|
+
async close() {
|
|
169
|
+
return new Promise((resolve) => {
|
|
170
|
+
this.wss.close(() => {
|
|
171
|
+
this.clients.clear();
|
|
172
|
+
resolve();
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { findFromCache } from "../cache/index.js";
|
|
2
|
+
import { getModel } from "../client/sdk.gen.js";
|
|
3
|
+
export class BLModel {
|
|
4
|
+
modelName;
|
|
5
|
+
options;
|
|
6
|
+
constructor(modelName, options) {
|
|
7
|
+
this.modelName = modelName;
|
|
8
|
+
this.options = options || {};
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export const blModel = (modelName, options) => {
|
|
12
|
+
return new BLModel(modelName, options);
|
|
13
|
+
};
|
|
14
|
+
export const getModelMetadata = async (model) => {
|
|
15
|
+
const cacheData = await findFromCache("Model", model);
|
|
16
|
+
if (cacheData) {
|
|
17
|
+
return cacheData;
|
|
18
|
+
}
|
|
19
|
+
const { data } = await getModel({
|
|
20
|
+
path: {
|
|
21
|
+
modelName: model,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
return data || null;
|
|
25
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { createClient } from "@hey-api/client-fetch";
|
|
2
|
+
import { getForcedUrl, getGlobalUniqueHash } from "../common/internal.js";
|
|
3
|
+
import { settings } from "../common/settings.js";
|
|
4
|
+
import { client as defaultClient } from "./client/client.gen.js";
|
|
5
|
+
export class ResponseError extends Error {
|
|
6
|
+
response;
|
|
7
|
+
data;
|
|
8
|
+
error;
|
|
9
|
+
constructor(response, data, error) {
|
|
10
|
+
let dataError = {};
|
|
11
|
+
if (data && typeof data === 'object' && 'error' in data) {
|
|
12
|
+
dataError = data;
|
|
13
|
+
}
|
|
14
|
+
if (error && typeof error === 'object' && 'error' in error) {
|
|
15
|
+
dataError['error'] = error.error;
|
|
16
|
+
}
|
|
17
|
+
if (response.status) {
|
|
18
|
+
dataError['status'] = response.status;
|
|
19
|
+
}
|
|
20
|
+
if (response.statusText) {
|
|
21
|
+
dataError['statusText'] = response.statusText;
|
|
22
|
+
}
|
|
23
|
+
super(JSON.stringify(dataError));
|
|
24
|
+
this.response = response;
|
|
25
|
+
this.data = data;
|
|
26
|
+
this.error = error;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export class SandboxAction {
|
|
30
|
+
sandbox;
|
|
31
|
+
constructor(sandbox) {
|
|
32
|
+
this.sandbox = sandbox;
|
|
33
|
+
}
|
|
34
|
+
get name() {
|
|
35
|
+
return this.sandbox.metadata?.name ?? "";
|
|
36
|
+
}
|
|
37
|
+
get fallbackUrl() {
|
|
38
|
+
if (this.externalUrl != this.url) {
|
|
39
|
+
return this.externalUrl;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
get externalUrl() {
|
|
44
|
+
return `${settings.runUrl}/${settings.workspace}/sandboxes/${this.name}`;
|
|
45
|
+
}
|
|
46
|
+
get internalUrl() {
|
|
47
|
+
const hash = getGlobalUniqueHash(settings.workspace, "sandbox", this.name);
|
|
48
|
+
return `${settings.runInternalProtocol}://bl-${settings.env}-${hash}.${settings.runInternalHostname}`;
|
|
49
|
+
}
|
|
50
|
+
get client() {
|
|
51
|
+
if (this.sandbox.forceUrl) {
|
|
52
|
+
return createClient({
|
|
53
|
+
baseUrl: this.sandbox.forceUrl,
|
|
54
|
+
headers: this.sandbox.headers,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return defaultClient;
|
|
58
|
+
}
|
|
59
|
+
get forcedUrl() {
|
|
60
|
+
if (this.sandbox.forceUrl)
|
|
61
|
+
return this.sandbox.forceUrl;
|
|
62
|
+
return getForcedUrl('sandbox', this.name);
|
|
63
|
+
}
|
|
64
|
+
get url() {
|
|
65
|
+
if (this.forcedUrl) {
|
|
66
|
+
const url = this.forcedUrl.toString();
|
|
67
|
+
return url.endsWith('/') ? url.slice(0, -1) : url;
|
|
68
|
+
}
|
|
69
|
+
// Uncomment and use this when agent and mcp are available in mk3
|
|
70
|
+
// Update all requests made in this package to use fallbackUrl when internalUrl is not working
|
|
71
|
+
// if (settings.runInternalHostname) return this.internalUrl;
|
|
72
|
+
return this.externalUrl;
|
|
73
|
+
}
|
|
74
|
+
handleResponseError(response, data, error) {
|
|
75
|
+
if (!response.ok || !data) {
|
|
76
|
+
throw new ResponseError(response, data, error);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|