clay-server 2.31.0 → 2.32.0-beta.10
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/lib/browser-mcp-server.js +32 -44
- package/lib/codex-defaults.js +18 -0
- package/lib/debate-mcp-server.js +14 -31
- package/lib/mcp-local.js +31 -1
- package/lib/project-connection.js +9 -6
- package/lib/project-debate.js +8 -0
- package/lib/project-filesystem.js +47 -1
- package/lib/project-http.js +75 -8
- package/lib/project-mate-interaction.js +102 -16
- package/lib/project-mcp.js +4 -0
- package/lib/project-notifications.js +9 -0
- package/lib/project-sessions.js +94 -51
- package/lib/project-user-message.js +12 -7
- package/lib/project.js +234 -99
- package/lib/public/app.js +135 -454
- package/lib/public/codex-avatar.png +0 -0
- package/lib/public/css/debate.css +3 -2
- package/lib/public/css/filebrowser.css +91 -1
- package/lib/public/css/icon-strip.css +21 -5
- package/lib/public/css/input.css +338 -104
- package/lib/public/css/mates.css +43 -0
- package/lib/public/css/mention.css +48 -4
- package/lib/public/css/menus.css +1 -1
- package/lib/public/css/messages.css +2 -0
- package/lib/public/css/notifications-center.css +26 -0
- package/lib/public/css/tooltip.css +47 -0
- package/lib/public/index.html +78 -26
- package/lib/public/modules/app-connection.js +138 -37
- package/lib/public/modules/app-cursors.js +18 -17
- package/lib/public/modules/app-debate-ui.js +9 -9
- package/lib/public/modules/app-dm.js +175 -131
- package/lib/public/modules/app-favicon.js +28 -26
- package/lib/public/modules/app-header.js +79 -68
- package/lib/public/modules/app-home-hub.js +55 -47
- package/lib/public/modules/app-loop-ui.js +34 -18
- package/lib/public/modules/app-loop-wizard.js +6 -6
- package/lib/public/modules/app-messages.js +199 -153
- package/lib/public/modules/app-misc.js +23 -12
- package/lib/public/modules/app-notifications.js +119 -9
- package/lib/public/modules/app-panels.js +203 -49
- package/lib/public/modules/app-projects.js +161 -150
- package/lib/public/modules/app-rate-limit.js +5 -4
- package/lib/public/modules/app-rendering.js +149 -101
- package/lib/public/modules/app-skills-install.js +4 -4
- package/lib/public/modules/context-sources.js +102 -66
- package/lib/public/modules/dom-refs.js +21 -0
- package/lib/public/modules/filebrowser.js +173 -2
- package/lib/public/modules/input.js +122 -0
- package/lib/public/modules/markdown.js +5 -1
- package/lib/public/modules/mate-sidebar.js +38 -0
- package/lib/public/modules/mention.js +24 -6
- package/lib/public/modules/scheduler.js +1 -1
- package/lib/public/modules/sidebar-mates.js +79 -35
- package/lib/public/modules/sidebar-mobile.js +34 -30
- package/lib/public/modules/sidebar-projects.js +60 -57
- package/lib/public/modules/sidebar-sessions.js +75 -69
- package/lib/public/modules/sidebar.js +12 -20
- package/lib/public/modules/skills.js +8 -9
- package/lib/public/modules/sticky-notes.js +1 -2
- package/lib/public/modules/store.js +9 -2
- package/lib/public/modules/stt.js +4 -1
- package/lib/public/modules/terminal.js +12 -0
- package/lib/public/modules/tools.js +18 -13
- package/lib/public/modules/tooltip.js +32 -5
- package/lib/sdk-bridge.js +562 -1114
- package/lib/sdk-message-processor.js +150 -135
- package/lib/sdk-worker.js +4 -0
- package/lib/server-dm.js +1 -0
- package/lib/server.js +86 -1
- package/lib/sessions.js +81 -37
- package/lib/ws-schema.js +2 -0
- package/lib/yoke/adapters/claude-worker.js +559 -0
- package/lib/yoke/adapters/claude.js +1483 -0
- package/lib/yoke/adapters/codex.js +1121 -0
- package/lib/yoke/adapters/gemini.js +709 -0
- package/lib/yoke/codex-app-server.js +307 -0
- package/lib/yoke/index.js +199 -0
- package/lib/yoke/instructions.js +62 -0
- package/lib/yoke/interface.js +98 -0
- package/lib/yoke/mcp-bridge-server.js +294 -0
- package/lib/yoke/package.json +7 -0
- package/package.json +3 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// Browser MCP Server for Clay
|
|
2
|
-
// Provides browser automation
|
|
3
|
-
//
|
|
1
|
+
// Browser MCP Server for Clay
|
|
2
|
+
// Provides browser automation tool definitions.
|
|
3
|
+
// SDK-free: returns runtime-agnostic tool definitions for YOKE adapter.
|
|
4
4
|
//
|
|
5
5
|
// Usage:
|
|
6
6
|
// var browserMcp = require("./browser-mcp-server");
|
|
7
|
-
// var
|
|
8
|
-
//
|
|
7
|
+
// var toolDefs = browserMcp.getToolDefs(sendExtensionCommandAny, getTabList, contextOps);
|
|
8
|
+
// var mcpConfig = adapter.createToolServer({ name: "clay-browser", version: "1.0.0", tools: toolDefs });
|
|
9
9
|
|
|
10
10
|
var z;
|
|
11
11
|
try { z = require("zod"); } catch (e) { z = null; }
|
|
@@ -30,21 +30,14 @@ function buildShape(props, required) {
|
|
|
30
30
|
return shape;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
// Helper: convert positional args (name, desc, schema, handler) to tool definition object
|
|
34
|
+
function def(name, description, inputSchema, handler) {
|
|
35
|
+
return { name: name, description: description, inputSchema: inputSchema, handler: handler };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getToolDefs(sendCommand, getTabList, contextOps) {
|
|
34
39
|
// sendCommand(command, args, timeout) -> Promise<result>
|
|
35
40
|
// getTabList() -> array of { id, url, title, favIconUrl }
|
|
36
|
-
var sdk;
|
|
37
|
-
try { sdk = require("@anthropic-ai/claude-agent-sdk"); } catch (e) {
|
|
38
|
-
console.error("[browser-mcp] Failed to load SDK:", e.message);
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
var createSdkMcpServer = sdk.createSdkMcpServer;
|
|
43
|
-
var tool = sdk.tool;
|
|
44
|
-
if (!createSdkMcpServer || !tool) {
|
|
45
|
-
console.error("[browser-mcp] SDK missing createSdkMcpServer or tool helper");
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
41
|
|
|
49
42
|
// Helper: ensure inject.js loaded (best-effort)
|
|
50
43
|
function ensureInjected(tabId) {
|
|
@@ -54,7 +47,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
54
47
|
var tools = [];
|
|
55
48
|
|
|
56
49
|
// --- browser_list_tabs ---
|
|
57
|
-
tools.push(
|
|
50
|
+
tools.push(def(
|
|
58
51
|
"browser_list_tabs",
|
|
59
52
|
"List all open browser tabs with their IDs, URLs, and titles",
|
|
60
53
|
buildShape({}, []),
|
|
@@ -65,7 +58,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
65
58
|
));
|
|
66
59
|
|
|
67
60
|
// --- browser_open ---
|
|
68
|
-
tools.push(
|
|
61
|
+
tools.push(def(
|
|
69
62
|
"browser_open",
|
|
70
63
|
"Open a new browser tab and return its tab ID",
|
|
71
64
|
buildShape({
|
|
@@ -80,7 +73,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
80
73
|
));
|
|
81
74
|
|
|
82
75
|
// --- browser_close ---
|
|
83
|
-
tools.push(
|
|
76
|
+
tools.push(def(
|
|
84
77
|
"browser_close",
|
|
85
78
|
"Close a browser tab",
|
|
86
79
|
buildShape({
|
|
@@ -94,7 +87,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
94
87
|
));
|
|
95
88
|
|
|
96
89
|
// --- browser_navigate ---
|
|
97
|
-
tools.push(
|
|
90
|
+
tools.push(def(
|
|
98
91
|
"browser_navigate",
|
|
99
92
|
"Navigate a tab to a new URL",
|
|
100
93
|
buildShape({
|
|
@@ -109,7 +102,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
109
102
|
));
|
|
110
103
|
|
|
111
104
|
// --- browser_screenshot ---
|
|
112
|
-
tools.push(
|
|
105
|
+
tools.push(def(
|
|
113
106
|
"browser_screenshot",
|
|
114
107
|
"Capture a screenshot of a browser tab. Skip if the tab is already attached as a context source (data is auto-injected).",
|
|
115
108
|
buildShape({
|
|
@@ -132,7 +125,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
132
125
|
));
|
|
133
126
|
|
|
134
127
|
// --- browser_console ---
|
|
135
|
-
tools.push(
|
|
128
|
+
tools.push(def(
|
|
136
129
|
"browser_console",
|
|
137
130
|
"Read captured console logs from a tab. Skip if the tab is already a context source (data is auto-injected).",
|
|
138
131
|
buildShape({
|
|
@@ -155,7 +148,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
155
148
|
));
|
|
156
149
|
|
|
157
150
|
// --- browser_network ---
|
|
158
|
-
tools.push(
|
|
151
|
+
tools.push(def(
|
|
159
152
|
"browser_network",
|
|
160
153
|
"Read captured network requests (fetch/XHR) from a tab. Skip if the tab is already a context source.",
|
|
161
154
|
buildShape({
|
|
@@ -179,7 +172,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
179
172
|
));
|
|
180
173
|
|
|
181
174
|
// --- browser_read_page ---
|
|
182
|
-
tools.push(
|
|
175
|
+
tools.push(def(
|
|
183
176
|
"browser_read_page",
|
|
184
177
|
"Read page text content (innerText). Skip if the tab is already a context source (text is auto-injected). Use for tabs NOT in context sources, or to read a specific element via selector.",
|
|
185
178
|
buildShape({
|
|
@@ -204,7 +197,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
204
197
|
));
|
|
205
198
|
|
|
206
199
|
// --- browser_dom ---
|
|
207
|
-
tools.push(
|
|
200
|
+
tools.push(def(
|
|
208
201
|
"browser_dom",
|
|
209
202
|
"Get a simplified DOM tree (tag, id, class, children) for structural analysis",
|
|
210
203
|
buildShape({
|
|
@@ -247,7 +240,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
247
240
|
));
|
|
248
241
|
|
|
249
242
|
// --- browser_styles ---
|
|
250
|
-
tools.push(
|
|
243
|
+
tools.push(def(
|
|
251
244
|
"browser_styles",
|
|
252
245
|
"Get computed styles of an element (display, position, size, colors, etc.)",
|
|
253
246
|
buildShape({
|
|
@@ -276,7 +269,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
276
269
|
));
|
|
277
270
|
|
|
278
271
|
// --- browser_storage ---
|
|
279
|
-
tools.push(
|
|
272
|
+
tools.push(def(
|
|
280
273
|
"browser_storage",
|
|
281
274
|
"Read browser storage (localStorage, sessionStorage, or cookies)",
|
|
282
275
|
buildShape({
|
|
@@ -300,7 +293,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
300
293
|
));
|
|
301
294
|
|
|
302
295
|
// --- browser_evaluate ---
|
|
303
|
-
tools.push(
|
|
296
|
+
tools.push(def(
|
|
304
297
|
"browser_evaluate",
|
|
305
298
|
"Execute arbitrary JavaScript in the page context and return the result",
|
|
306
299
|
buildShape({
|
|
@@ -317,7 +310,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
317
310
|
));
|
|
318
311
|
|
|
319
312
|
// --- browser_click ---
|
|
320
|
-
tools.push(
|
|
313
|
+
tools.push(def(
|
|
321
314
|
"browser_click",
|
|
322
315
|
"Click an element on the page",
|
|
323
316
|
buildShape({
|
|
@@ -339,7 +332,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
339
332
|
));
|
|
340
333
|
|
|
341
334
|
// --- browser_type ---
|
|
342
|
-
tools.push(
|
|
335
|
+
tools.push(def(
|
|
343
336
|
"browser_type",
|
|
344
337
|
"Type text into an input element (sets value and dispatches input/change events)",
|
|
345
338
|
buildShape({
|
|
@@ -366,7 +359,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
366
359
|
));
|
|
367
360
|
|
|
368
361
|
// --- browser_scroll ---
|
|
369
|
-
tools.push(
|
|
362
|
+
tools.push(def(
|
|
370
363
|
"browser_scroll",
|
|
371
364
|
"Scroll the page or scroll a specific element into view",
|
|
372
365
|
buildShape({
|
|
@@ -396,7 +389,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
396
389
|
));
|
|
397
390
|
|
|
398
391
|
// --- browser_wait ---
|
|
399
|
-
tools.push(
|
|
392
|
+
tools.push(def(
|
|
400
393
|
"browser_wait",
|
|
401
394
|
"Wait for an element matching a CSS selector to appear in the DOM",
|
|
402
395
|
buildShape({
|
|
@@ -432,7 +425,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
432
425
|
));
|
|
433
426
|
|
|
434
427
|
// --- browser_wait_navigation ---
|
|
435
|
-
tools.push(
|
|
428
|
+
tools.push(def(
|
|
436
429
|
"browser_wait_navigation",
|
|
437
430
|
"Wait for page navigation to complete (URL change + load event)",
|
|
438
431
|
buildShape({
|
|
@@ -450,7 +443,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
450
443
|
|
|
451
444
|
// --- browser_watch_tab ---
|
|
452
445
|
if (contextOps && contextOps.watchTab) {
|
|
453
|
-
tools.push(
|
|
446
|
+
tools.push(def(
|
|
454
447
|
"browser_watch_tab",
|
|
455
448
|
"Add a browser tab as a persistent context source. Its screenshot and text will be automatically included in every subsequent message.",
|
|
456
449
|
buildShape({
|
|
@@ -470,7 +463,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
470
463
|
}
|
|
471
464
|
));
|
|
472
465
|
|
|
473
|
-
tools.push(
|
|
466
|
+
tools.push(def(
|
|
474
467
|
"browser_unwatch_tab",
|
|
475
468
|
"Remove a browser tab from persistent context sources. Stops auto-including its content.",
|
|
476
469
|
buildShape({
|
|
@@ -485,12 +478,7 @@ function create(sendCommand, getTabList, contextOps) {
|
|
|
485
478
|
));
|
|
486
479
|
}
|
|
487
480
|
|
|
488
|
-
|
|
489
|
-
return createSdkMcpServer({
|
|
490
|
-
name: "clay-browser",
|
|
491
|
-
version: "1.0.0",
|
|
492
|
-
tools: tools,
|
|
493
|
-
});
|
|
481
|
+
return tools;
|
|
494
482
|
}
|
|
495
483
|
|
|
496
|
-
module.exports = {
|
|
484
|
+
module.exports = { getToolDefs: getToolDefs };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
var CODEX_DEFAULTS = {
|
|
2
|
+
approval: "on-failure",
|
|
3
|
+
sandbox: "danger-full-access",
|
|
4
|
+
webSearch: "live",
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
function getCodexConfig(sm) {
|
|
8
|
+
return {
|
|
9
|
+
approval: (sm && sm.codexApproval) || CODEX_DEFAULTS.approval,
|
|
10
|
+
sandbox: (sm && sm.codexSandbox) || CODEX_DEFAULTS.sandbox,
|
|
11
|
+
webSearch: (sm && sm.codexWebSearch) || CODEX_DEFAULTS.webSearch,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = {
|
|
16
|
+
CODEX_DEFAULTS: CODEX_DEFAULTS,
|
|
17
|
+
getCodexConfig: getCodexConfig,
|
|
18
|
+
};
|
package/lib/debate-mcp-server.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// Debate MCP Server for Clay
|
|
2
|
-
// Provides the propose_debate tool
|
|
3
|
-
//
|
|
1
|
+
// Debate MCP Server for Clay
|
|
2
|
+
// Provides the propose_debate tool definition.
|
|
3
|
+
// SDK-free: returns runtime-agnostic tool definitions for YOKE adapter.
|
|
4
4
|
//
|
|
5
5
|
// Usage:
|
|
6
6
|
// var debateMcp = require("./debate-mcp-server");
|
|
7
|
-
// var
|
|
8
|
-
//
|
|
7
|
+
// var toolDefs = debateMcp.getToolDefs(onPropose);
|
|
8
|
+
// var mcpConfig = adapter.createToolServer({ name: "clay-debate", version: "1.0.0", tools: toolDefs });
|
|
9
9
|
|
|
10
10
|
var z;
|
|
11
11
|
try { z = require("zod"); } catch (e) { z = null; }
|
|
@@ -31,33 +31,20 @@ function buildShape(props, required) {
|
|
|
31
31
|
|
|
32
32
|
// onPropose(briefData) -> Promise<{action: "start"|"cancel"}>
|
|
33
33
|
// The returned Promise blocks the tool until the user approves or cancels.
|
|
34
|
-
function
|
|
35
|
-
var sdk;
|
|
36
|
-
try { sdk = require("@anthropic-ai/claude-agent-sdk"); } catch (e) {
|
|
37
|
-
console.error("[debate-mcp] Failed to load SDK:", e.message);
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
var createSdkMcpServer = sdk.createSdkMcpServer;
|
|
42
|
-
var tool = sdk.tool;
|
|
43
|
-
if (!createSdkMcpServer || !tool) {
|
|
44
|
-
console.error("[debate-mcp] SDK missing createSdkMcpServer or tool helper");
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
|
|
34
|
+
function getToolDefs(onPropose) {
|
|
48
35
|
var tools = [];
|
|
49
36
|
|
|
50
|
-
tools.push(
|
|
51
|
-
"propose_debate",
|
|
52
|
-
"Propose a structured debate among Clay Mates. The user will see an inline approval card. The tool blocks until the user approves or cancels.",
|
|
53
|
-
buildShape({
|
|
37
|
+
tools.push({
|
|
38
|
+
name: "propose_debate",
|
|
39
|
+
description: "Propose a structured debate among Clay Mates. The user will see an inline approval card. The tool blocks until the user approves or cancels.",
|
|
40
|
+
inputSchema: buildShape({
|
|
54
41
|
topic: { type: "string", description: "The debate topic" },
|
|
55
42
|
format: { type: "string", description: "Debate format, e.g. free_discussion (default)" },
|
|
56
43
|
context: { type: "string", description: "Key context from the conversation that panelists should know" },
|
|
57
44
|
specialRequests: { type: "string", description: "Special instructions for the debate, or empty" },
|
|
58
45
|
panelists: { type: "string", description: "JSON array of panelist objects: [{\"mateId\": \"<UUID>\", \"role\": \"perspective\", \"brief\": \"guidance\"}]" },
|
|
59
46
|
}, ["topic", "panelists"]),
|
|
60
|
-
function (args) {
|
|
47
|
+
handler: function (args) {
|
|
61
48
|
var panelists;
|
|
62
49
|
try {
|
|
63
50
|
panelists = JSON.parse(args.panelists);
|
|
@@ -82,13 +69,9 @@ function create(onPropose) {
|
|
|
82
69
|
return { content: [{ type: "text", text: "Debate proposal was cancelled by the user." }] };
|
|
83
70
|
});
|
|
84
71
|
}
|
|
85
|
-
));
|
|
86
|
-
|
|
87
|
-
return createSdkMcpServer({
|
|
88
|
-
name: "clay-debate",
|
|
89
|
-
version: "1.0.0",
|
|
90
|
-
tools: tools,
|
|
91
72
|
});
|
|
73
|
+
|
|
74
|
+
return tools;
|
|
92
75
|
}
|
|
93
76
|
|
|
94
|
-
module.exports = {
|
|
77
|
+
module.exports = { getToolDefs: getToolDefs };
|
package/lib/mcp-local.js
CHANGED
|
@@ -352,4 +352,34 @@ function createLocalMcp() {
|
|
|
352
352
|
};
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
|
|
355
|
+
// Standalone config reader (no process spawning).
|
|
356
|
+
// Returns merged server definitions from ~/.clay/mcp.json + includes.
|
|
357
|
+
// Used by Codex adapter to pass server configs for native MCP management.
|
|
358
|
+
function readMergedServers() {
|
|
359
|
+
var dir = path.dirname(CLAY_CONFIG_PATH);
|
|
360
|
+
if (!fs.existsSync(dir)) return {};
|
|
361
|
+
var config;
|
|
362
|
+
try {
|
|
363
|
+
config = JSON.parse(fs.readFileSync(CLAY_CONFIG_PATH, "utf8"));
|
|
364
|
+
} catch (e) {
|
|
365
|
+
return {};
|
|
366
|
+
}
|
|
367
|
+
var merged = Object.assign({}, config.mcpServers || {});
|
|
368
|
+
var includes = config.include || [];
|
|
369
|
+
for (var i = 0; i < includes.length; i++) {
|
|
370
|
+
var resolved = includes[i].replace(/^~/, os.homedir());
|
|
371
|
+
try {
|
|
372
|
+
var ext = JSON.parse(fs.readFileSync(resolved, "utf8"));
|
|
373
|
+
var extServers = ext.mcpServers || {};
|
|
374
|
+
var names = Object.keys(extServers);
|
|
375
|
+
for (var j = 0; j < names.length; j++) {
|
|
376
|
+
if (!merged[names[j]]) merged[names[j]] = extServers[names[j]];
|
|
377
|
+
}
|
|
378
|
+
} catch (e) {
|
|
379
|
+
// Skip unreadable files
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return merged;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
module.exports = { createLocalMcp: createLocalMcp, readMergedServers: readMergedServers };
|
|
@@ -3,6 +3,7 @@ var path = require("path");
|
|
|
3
3
|
var usersModule = require("./users");
|
|
4
4
|
var userPresence = require("./user-presence");
|
|
5
5
|
var emailAccounts = require("./email-accounts");
|
|
6
|
+
var { getCodexConfig } = require("./codex-defaults");
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Attach connection/disconnection handlers to a project context.
|
|
@@ -84,17 +85,18 @@ function attachConnection(ctx) {
|
|
|
84
85
|
var project = getProject();
|
|
85
86
|
var ownerLocked = !!(osUsers && osUsers.length > 0 && /^\/home\/[^/]+\//.test(cwd));
|
|
86
87
|
sendTo(ws, { type: "info", cwd: cwd, slug: slug, project: title || project, version: currentVersion, debug: !!debug, dangerouslySkipPermissions: dangerouslySkipPermissions, osUsers: osUsers, lanHost: lanHost, projectCount: _filteredProjects.length, projects: _filteredProjects, projectOwnerId: projectOwnerId, ownerLocked: ownerLocked });
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
88
|
+
// Update notifications are pushed on a scheduled interval (see
|
|
89
|
+
// scheduleUpdateBroadcast). We no longer push on connect to avoid
|
|
90
|
+
// re-triggering the banner on every page refresh.
|
|
91
91
|
if (sm.slashCommands) {
|
|
92
92
|
sendTo(ws, { type: "slash_commands", commands: sm.slashCommands });
|
|
93
93
|
}
|
|
94
94
|
if (sm.currentModel) {
|
|
95
|
-
|
|
95
|
+
// Vendor is resolved per-session in session_switched; send default here
|
|
96
|
+
sendTo(ws, { type: "model_info", model: sm.currentModel, models: sm.availableModels || [], vendor: sm.defaultVendor || "claude", availableVendors: sm.availableVendors || [], installedVendors: sm.installedVendors || [] });
|
|
96
97
|
}
|
|
97
98
|
sendTo(ws, { type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode || "default", effort: sm.currentEffort || "medium", betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
99
|
+
sendTo(ws, Object.assign({ type: "codex_config" }, getCodexConfig(sm)));
|
|
98
100
|
sendTo(ws, { type: "term_list", terminals: tm.list() });
|
|
99
101
|
// Context sources sent after session is resolved (per-session storage)
|
|
100
102
|
// Send email accounts list for context sources picker
|
|
@@ -180,7 +182,8 @@ function attachConnection(ctx) {
|
|
|
180
182
|
sm.saveSessionFile(active);
|
|
181
183
|
}
|
|
182
184
|
ws._clayActiveSession = active.localId;
|
|
183
|
-
|
|
185
|
+
var _vendorCaps = (sm.capabilitiesByVendor && sm.capabilitiesByVendor[active.vendor || sm.defaultVendor || "claude"]) || {};
|
|
186
|
+
sendTo(ws, { type: "session_switched", id: active.localId, cliSessionId: active.cliSessionId || null, loop: active.loop || null, vendor: active.vendor || null, hasHistory: (active.history && active.history.length > 0), capabilities: _vendorCaps });
|
|
184
187
|
// Send per-session context sources
|
|
185
188
|
var sessionSources = loadContextSources(slug, active.localId);
|
|
186
189
|
sendTo(ws, { type: "context_sources_state", active: sessionSources });
|
package/lib/project-debate.js
CHANGED
|
@@ -601,7 +601,9 @@ function attachDebate(ctx) {
|
|
|
601
601
|
var digests = ctx.loadMateDigests(mateCtx, debate.moderatorId, debate.topic);
|
|
602
602
|
|
|
603
603
|
var briefText = "";
|
|
604
|
+
var _modMate = matesModule.getMate(mateCtx, debate.moderatorId);
|
|
604
605
|
ctx.sdk.createMentionSession({
|
|
606
|
+
vendor: _modMate ? _modMate.vendor : null,
|
|
605
607
|
claudeMd: claudeMd,
|
|
606
608
|
initialContext: digests,
|
|
607
609
|
initialMessage: quickBriefPrompt,
|
|
@@ -794,7 +796,9 @@ function attachDebate(ctx) {
|
|
|
794
796
|
var digests = ctx.loadMateDigests(mateCtx, debate.moderatorId, debate.topic);
|
|
795
797
|
var moderatorContext = buildModeratorContext(debate) + digests;
|
|
796
798
|
|
|
799
|
+
var _modMate2 = matesModule.getMate(mateCtx, debate.moderatorId);
|
|
797
800
|
ctx.sdk.createMentionSession({
|
|
801
|
+
vendor: _modMate2 ? _modMate2.vendor : null,
|
|
798
802
|
claudeMd: claudeMd,
|
|
799
803
|
initialContext: moderatorContext,
|
|
800
804
|
initialMessage: "Begin the debate on: " + debate.topic,
|
|
@@ -987,7 +991,9 @@ function attachDebate(ctx) {
|
|
|
987
991
|
historyContext += "---";
|
|
988
992
|
}
|
|
989
993
|
|
|
994
|
+
var _panMate = matesModule.getMate(debate.mateCtx, mateId);
|
|
990
995
|
ctx.sdk.createMentionSession({
|
|
996
|
+
vendor: _panMate ? _panMate.vendor : null,
|
|
991
997
|
claudeMd: claudeMd,
|
|
992
998
|
initialContext: panelistContext + historyContext,
|
|
993
999
|
initialMessage: "The moderator addresses you:\n\n" + moderatorText,
|
|
@@ -1521,7 +1527,9 @@ function attachDebate(ctx) {
|
|
|
1521
1527
|
}
|
|
1522
1528
|
moderatorContext += "---\n";
|
|
1523
1529
|
|
|
1530
|
+
var _modMate3 = matesModule.getMate(mateCtx, debate.moderatorId);
|
|
1524
1531
|
ctx.sdk.createMentionSession({
|
|
1532
|
+
vendor: _modMate3 ? _modMate3.vendor : null,
|
|
1525
1533
|
claudeMd: claudeMd,
|
|
1526
1534
|
initialContext: moderatorContext,
|
|
1527
1535
|
initialMessage: resumePrompt,
|
|
@@ -41,7 +41,7 @@ function attachFilesystem(ctx) {
|
|
|
41
41
|
|
|
42
42
|
function handleFilesystemMessage(ws, msg) {
|
|
43
43
|
// --- File browser permission gate ---
|
|
44
|
-
if (msg.type === "fs_list" || msg.type === "fs_read" || msg.type === "fs_write" || msg.type === "fs_delete" || msg.type === "fs_rename" || msg.type === "fs_mkdir" || msg.type === "fs_upload") {
|
|
44
|
+
if (msg.type === "fs_list" || msg.type === "fs_read" || msg.type === "fs_write" || msg.type === "fs_delete" || msg.type === "fs_rename" || msg.type === "fs_mkdir" || msg.type === "fs_upload" || msg.type === "fs_search") {
|
|
45
45
|
if (ws._clayUser) {
|
|
46
46
|
var fbPerms = usersModule.getEffectivePermissions(ws._clayUser, osUsers);
|
|
47
47
|
if (!fbPerms.fileBrowser) {
|
|
@@ -98,6 +98,52 @@ function attachFilesystem(ctx) {
|
|
|
98
98
|
return true;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
// --- fs_search ---
|
|
102
|
+
if (msg.type === "fs_search") {
|
|
103
|
+
var query = (msg.query || "").trim().toLowerCase();
|
|
104
|
+
if (!query) {
|
|
105
|
+
sendTo(ws, { type: "fs_search_result", query: msg.query, entries: [] });
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
var searchResults = [];
|
|
110
|
+
var MAX_RESULTS = 50;
|
|
111
|
+
var searchUserInfo = getOsUserInfoForWs(ws);
|
|
112
|
+
|
|
113
|
+
function walkDir(dir, relPrefix) {
|
|
114
|
+
if (searchResults.length >= MAX_RESULTS) return;
|
|
115
|
+
var items;
|
|
116
|
+
try {
|
|
117
|
+
if (searchUserInfo) {
|
|
118
|
+
items = fsAsUser("list", { dir: dir }, searchUserInfo);
|
|
119
|
+
} else {
|
|
120
|
+
items = fs.readdirSync(dir, { withFileTypes: true }).map(function (d) {
|
|
121
|
+
return { name: d.name, isDir: d.isDirectory() };
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
} catch (e) { return; }
|
|
125
|
+
for (var i = 0; i < items.length; i++) {
|
|
126
|
+
if (searchResults.length >= MAX_RESULTS) return;
|
|
127
|
+
var it = items[i];
|
|
128
|
+
if (it.isDir && IGNORED_DIRS.has(it.name)) continue;
|
|
129
|
+
var rel = relPrefix ? relPrefix + "/" + it.name : it.name;
|
|
130
|
+
if (it.name.toLowerCase().indexOf(query) !== -1) {
|
|
131
|
+
searchResults.push({ name: it.name, type: it.isDir ? "dir" : "file", path: rel });
|
|
132
|
+
}
|
|
133
|
+
if (it.isDir) {
|
|
134
|
+
walkDir(path.join(dir, it.name), rel);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
walkDir(cwd, "");
|
|
140
|
+
sendTo(ws, { type: "fs_search_result", query: msg.query, entries: searchResults });
|
|
141
|
+
} catch (e) {
|
|
142
|
+
sendTo(ws, { type: "fs_search_result", query: msg.query, entries: [], error: e.message });
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
|
|
101
147
|
// --- fs_read ---
|
|
102
148
|
if (msg.type === "fs_read") {
|
|
103
149
|
var fsFile = safePath(cwd, msg.path);
|
package/lib/project-http.js
CHANGED
|
@@ -504,6 +504,10 @@ function attachHTTP(ctx) {
|
|
|
504
504
|
return true;
|
|
505
505
|
}
|
|
506
506
|
|
|
507
|
+
// Skill update check cache (avoid redundant GitHub fetches)
|
|
508
|
+
if (!ctx._skillCheckCache) ctx._skillCheckCache = {};
|
|
509
|
+
var SKILL_CHECK_TTL = 5 * 60 * 1000; // 5 minutes
|
|
510
|
+
|
|
507
511
|
// Check skill updates (compare installed vs remote versions)
|
|
508
512
|
if (req.method === "POST" && urlPath === "/api/check-skill-updates") {
|
|
509
513
|
parseJsonBody(req).then(function (body) {
|
|
@@ -572,7 +576,16 @@ function attachHTTP(ctx) {
|
|
|
572
576
|
(function (skill) {
|
|
573
577
|
var installedVer = getInstalledVersion(skill.name);
|
|
574
578
|
var installed = !!installedVer;
|
|
575
|
-
|
|
579
|
+
|
|
580
|
+
// Return cached result if fresh
|
|
581
|
+
var cacheKey = skill.name + ":" + (installedVer || "");
|
|
582
|
+
var cached = ctx._skillCheckCache[cacheKey];
|
|
583
|
+
if (cached && (Date.now() - cached.ts) < SKILL_CHECK_TTL) {
|
|
584
|
+
results.push(cached.result);
|
|
585
|
+
finishOne();
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
|
|
576
589
|
// Convert GitHub repo URL to raw SKILL.md URL
|
|
577
590
|
var rawUrl = "";
|
|
578
591
|
var ghMatch = skill.url.match(/github\.com\/([^/]+)\/([^/]+)/);
|
|
@@ -580,17 +593,16 @@ function attachHTTP(ctx) {
|
|
|
580
593
|
rawUrl = "https://raw.githubusercontent.com/" + ghMatch[1] + "/" + ghMatch[2] + "/main/SKILL.md";
|
|
581
594
|
}
|
|
582
595
|
if (!rawUrl) {
|
|
583
|
-
|
|
584
|
-
|
|
596
|
+
var r0 = { name: skill.name, installed: installed, installedVersion: installedVer, remoteVersion: "", status: installed ? "ok" : "missing" };
|
|
597
|
+
ctx._skillCheckCache[cacheKey] = { ts: Date.now(), result: r0 };
|
|
598
|
+
results.push(r0);
|
|
585
599
|
finishOne();
|
|
586
600
|
return;
|
|
587
601
|
}
|
|
588
|
-
console.log("[skill-check] " + skill.name + " fetching remote: " + rawUrl);
|
|
589
602
|
// Fetch remote SKILL.md
|
|
590
603
|
var https = require("https");
|
|
591
604
|
https.get(rawUrl, function (resp) {
|
|
592
|
-
|
|
593
|
-
var data = "";
|
|
605
|
+
var data = "";
|
|
594
606
|
resp.on("data", function (chunk) { data += chunk; });
|
|
595
607
|
resp.on("end", function () {
|
|
596
608
|
try {
|
|
@@ -601,8 +613,9 @@ function attachHTTP(ctx) {
|
|
|
601
613
|
} else if (remoteVer && compareVersions(installedVer, remoteVer) < 0) {
|
|
602
614
|
status = "outdated";
|
|
603
615
|
}
|
|
604
|
-
|
|
605
|
-
|
|
616
|
+
var r1 = { name: skill.name, installed: installed, installedVersion: installedVer, remoteVersion: remoteVer, status: status };
|
|
617
|
+
ctx._skillCheckCache[cacheKey] = { ts: Date.now(), result: r1 };
|
|
618
|
+
results.push(r1);
|
|
606
619
|
finishOne();
|
|
607
620
|
} catch (e) {
|
|
608
621
|
console.error("[skill-check] " + skill.name + " version parse failed:", e.message || e);
|
|
@@ -666,6 +679,60 @@ function attachHTTP(ctx) {
|
|
|
666
679
|
return true;
|
|
667
680
|
}
|
|
668
681
|
|
|
682
|
+
// MCP bridge endpoint: allows Codex's mcp-bridge-server.js to list/call
|
|
683
|
+
// in-app and remote MCP tools via HTTP (localhost only).
|
|
684
|
+
if (req.method === "POST" && urlPath === "/api/mcp-bridge") {
|
|
685
|
+
parseJsonBody(req).then(function (body) {
|
|
686
|
+
var action = body.action;
|
|
687
|
+
var getMcpBridgeHandler = ctx.getMcpBridgeHandler;
|
|
688
|
+
if (!getMcpBridgeHandler) {
|
|
689
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
690
|
+
res.end('{"error":"MCP bridge not configured"}');
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
var handler = getMcpBridgeHandler();
|
|
694
|
+
if (!handler) {
|
|
695
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
696
|
+
res.end('{"error":"MCP bridge handler unavailable"}');
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
if (action === "list_tools") {
|
|
701
|
+
handler.listTools().then(function (tools) {
|
|
702
|
+
var serverCounts = {};
|
|
703
|
+
for (var ti = 0; ti < tools.length; ti++) {
|
|
704
|
+
serverCounts[tools[ti].server] = (serverCounts[tools[ti].server] || 0) + 1;
|
|
705
|
+
}
|
|
706
|
+
console.log("[mcp-bridge-http] list_tools:", tools.length, "tools -", Object.keys(serverCounts).map(function(s) { return s + "(" + serverCounts[s] + ")"; }).join(", ") || "(none)");
|
|
707
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
708
|
+
res.end(JSON.stringify({ tools: tools }));
|
|
709
|
+
}).catch(function (err) {
|
|
710
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
711
|
+
res.end(JSON.stringify({ error: err.message || "Failed to list tools" }));
|
|
712
|
+
});
|
|
713
|
+
} else if (action === "call_tool") {
|
|
714
|
+
var server = body.server;
|
|
715
|
+
var tool = body.tool;
|
|
716
|
+
var args = body.args || {};
|
|
717
|
+
console.log("[mcp-bridge-http] call_tool:", server + "/" + tool);
|
|
718
|
+
handler.callTool(server, tool, args).then(function (result) {
|
|
719
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
720
|
+
res.end(JSON.stringify({ result: result }));
|
|
721
|
+
}).catch(function (err) {
|
|
722
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
723
|
+
res.end(JSON.stringify({ error: err.message || "Tool call failed" }));
|
|
724
|
+
});
|
|
725
|
+
} else {
|
|
726
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
727
|
+
res.end('{"error":"Unknown action: ' + (action || '') + '"}');
|
|
728
|
+
}
|
|
729
|
+
}).catch(function () {
|
|
730
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
731
|
+
res.end('{"error":"Invalid JSON body"}');
|
|
732
|
+
});
|
|
733
|
+
return true;
|
|
734
|
+
}
|
|
735
|
+
|
|
669
736
|
// Info endpoint
|
|
670
737
|
if (req.method === "GET" && urlPath === "/info") {
|
|
671
738
|
res.writeHead(200, {
|