clay-server 2.33.1 → 2.34.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/ask-user-mcp-server.js +120 -0
- package/lib/config.js +9 -13
- package/lib/daemon.js +116 -55
- package/lib/mate-datastore.js +359 -0
- package/lib/mates.js +2 -2
- package/lib/os-users.js +70 -37
- package/lib/project-connection.js +16 -9
- package/lib/project-http.js +3 -4
- package/lib/project-image.js +3 -2
- package/lib/project-mate-datastore.js +232 -0
- package/lib/project-sessions.js +110 -7
- package/lib/project-user-message.js +4 -3
- package/lib/project.js +126 -10
- package/lib/public/app.js +2 -0
- package/lib/public/css/mates.css +228 -11
- package/lib/public/css/messages.css +23 -0
- package/lib/public/css/mobile-nav.css +0 -14
- package/lib/public/css/notifications-center.css +80 -0
- package/lib/public/css/sidebar.css +326 -101
- package/lib/public/index.html +24 -29
- package/lib/public/modules/app-dm.js +0 -2
- package/lib/public/modules/app-messages.js +23 -0
- package/lib/public/modules/app-rendering.js +0 -2
- package/lib/public/modules/diff.js +21 -7
- package/lib/public/modules/mate-datastore-ui.js +280 -0
- package/lib/public/modules/mate-sidebar.js +3 -9
- package/lib/public/modules/mate-wizard.js +15 -15
- package/lib/public/modules/sidebar-mobile.js +10 -20
- package/lib/public/modules/sidebar-sessions.js +490 -113
- package/lib/public/modules/sidebar.js +8 -6
- package/lib/public/modules/tools.js +115 -18
- package/lib/public/sw.js +1 -1
- package/lib/sdk-bridge.js +56 -41
- package/lib/sdk-message-processor.js +21 -4
- package/lib/server.js +28 -72
- package/lib/sessions.js +157 -20
- package/lib/updater.js +2 -2
- package/lib/users.js +2 -2
- package/lib/ws-schema.js +16 -0
- package/lib/yoke/adapters/claude-worker.js +114 -2
- package/lib/yoke/adapters/claude.js +56 -5
- package/lib/yoke/adapters/codex.js +350 -58
- package/lib/yoke/index.js +93 -48
- package/lib/yoke/instructions.js +0 -1
- package/lib/yoke/mcp-bridge-server.js +14 -6
- package/package.json +1 -2
- package/lib/yoke/adapters/gemini.js +0 -709
package/lib/yoke/index.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
var iface = require("./interface");
|
|
5
5
|
var instructions = require("./instructions");
|
|
6
6
|
var createClaudeAdapter = require("./adapters/claude").createClaudeAdapter;
|
|
7
|
-
var createGeminiAdapter = require("./adapters/gemini").createGeminiAdapter;
|
|
8
7
|
var createCodexAdapter = require("./adapters/codex").createCodexAdapter;
|
|
9
8
|
|
|
10
9
|
/**
|
|
@@ -20,6 +19,7 @@ function wrapCreateQuery(adapter, defaultCwd) {
|
|
|
20
19
|
var originalCreateQuery = adapter.createQuery.bind(adapter);
|
|
21
20
|
|
|
22
21
|
adapter.createQuery = function(queryOpts) {
|
|
22
|
+
queryOpts = queryOpts || {};
|
|
23
23
|
var projectDir = (queryOpts && queryOpts.cwd) || defaultCwd;
|
|
24
24
|
var merged = instructions.scanAndMerge(projectDir, adapter.vendor);
|
|
25
25
|
|
|
@@ -48,15 +48,13 @@ function createAdapter(opts) {
|
|
|
48
48
|
var adapter;
|
|
49
49
|
if (vendor === "claude") {
|
|
50
50
|
adapter = createClaudeAdapter(opts);
|
|
51
|
-
} else if (vendor === "gemini") {
|
|
52
|
-
adapter = createGeminiAdapter(opts);
|
|
53
51
|
} else if (vendor === "codex") {
|
|
54
52
|
adapter = createCodexAdapter(opts);
|
|
55
53
|
} else {
|
|
56
54
|
throw new Error("[YOKE] Unknown adapter vendor: " + vendor);
|
|
57
55
|
}
|
|
58
56
|
iface.validateAdapter(adapter);
|
|
59
|
-
wrapCreateQuery(adapter, opts.cwd);
|
|
57
|
+
wrapCreateQuery(adapter, opts && opts.cwd);
|
|
60
58
|
return adapter;
|
|
61
59
|
}
|
|
62
60
|
|
|
@@ -82,6 +80,18 @@ function checkAuth() {
|
|
|
82
80
|
if (_authCache) return _authCache;
|
|
83
81
|
|
|
84
82
|
var execSync = require("child_process").execSync;
|
|
83
|
+
var execFileSync = require("child_process").execFileSync;
|
|
84
|
+
|
|
85
|
+
function lookupBinary(name) {
|
|
86
|
+
try {
|
|
87
|
+
if (process.platform === "win32") {
|
|
88
|
+
return execFileSync("where", [name], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim().split(/\r?\n/)[0] || null;
|
|
89
|
+
}
|
|
90
|
+
return execFileSync("which", [name], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim().split(/\r?\n/)[0] || null;
|
|
91
|
+
} catch (e) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
85
95
|
|
|
86
96
|
function parseClaudeAuthStatusJson(out) {
|
|
87
97
|
if (!out) return null;
|
|
@@ -101,7 +111,31 @@ function checkAuth() {
|
|
|
101
111
|
return false;
|
|
102
112
|
}
|
|
103
113
|
|
|
114
|
+
function hasThirdPartyProviderAuth() {
|
|
115
|
+
// Claude Code supports third-party providers via env vars. When these are set,
|
|
116
|
+
// `claude auth status` reports "not logged in" because there is no OAuth session,
|
|
117
|
+
// but Claude Code itself authenticates directly through the provider.
|
|
118
|
+
var env = process.env;
|
|
119
|
+
if (env.CLAUDE_CODE_USE_BEDROCK === "1"
|
|
120
|
+
&& (env.AWS_BEARER_TOKEN_BEDROCK
|
|
121
|
+
|| env.AWS_ACCESS_KEY_ID
|
|
122
|
+
|| env.AWS_PROFILE
|
|
123
|
+
|| env.AWS_SESSION_TOKEN)) {
|
|
124
|
+
return "bedrock";
|
|
125
|
+
}
|
|
126
|
+
if (env.CLAUDE_CODE_USE_VERTEX === "1") return "vertex";
|
|
127
|
+
if (env.ANTHROPIC_API_KEY) return "api_key";
|
|
128
|
+
if (env.ANTHROPIC_AUTH_TOKEN) return "auth_token";
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
104
132
|
function checkClaude() {
|
|
133
|
+
var provider = hasThirdPartyProviderAuth();
|
|
134
|
+
if (provider) {
|
|
135
|
+
console.log("[yoke] Claude auth via third-party provider: " + provider);
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
|
|
105
139
|
try {
|
|
106
140
|
var out = execSync("claude auth status --json", { timeout: 5000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
|
|
107
141
|
var parsed = parseClaudeAuthStatusJson(out);
|
|
@@ -127,47 +161,52 @@ function checkAuth() {
|
|
|
127
161
|
function resolveCodexBinary() {
|
|
128
162
|
var fs = require("fs");
|
|
129
163
|
var findCodexPath = require("./codex-app-server").findCodexPath;
|
|
130
|
-
var pathProbeCmd = process.platform === "win32" ? "where codex" : "which codex";
|
|
131
164
|
|
|
132
165
|
try {
|
|
133
166
|
var codexBin = findCodexPath();
|
|
134
167
|
if (codexBin && fs.existsSync(codexBin)) return codexBin;
|
|
135
168
|
} catch (e) {}
|
|
136
169
|
|
|
137
|
-
|
|
138
|
-
var whichOut = execSync(pathProbeCmd, { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
|
|
139
|
-
var codexPath = whichOut.trim().split(/\r?\n/)[0];
|
|
140
|
-
if (codexPath) return codexPath;
|
|
141
|
-
} catch (e) {}
|
|
142
|
-
|
|
143
|
-
return null;
|
|
170
|
+
return lookupBinary("codex");
|
|
144
171
|
}
|
|
145
172
|
|
|
146
173
|
function checkCodex() {
|
|
147
174
|
try {
|
|
148
175
|
var codexBin = resolveCodexBinary();
|
|
149
176
|
if (!codexBin) return false;
|
|
150
|
-
|
|
177
|
+
execFileSync(codexBin, ["login", "status"], { timeout: 5000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
|
|
151
178
|
return true;
|
|
152
179
|
} catch (e) {
|
|
153
180
|
return false;
|
|
154
181
|
}
|
|
155
182
|
}
|
|
156
183
|
|
|
157
|
-
_authCache = { claude: checkClaude(), codex: checkCodex()
|
|
184
|
+
_authCache = { claude: checkClaude(), codex: checkCodex() };
|
|
158
185
|
logAuthCheck(_authCache);
|
|
159
186
|
return _authCache;
|
|
160
187
|
}
|
|
161
188
|
|
|
162
189
|
/**
|
|
163
190
|
* Check which vendor binaries are installed (regardless of auth status).
|
|
191
|
+
*
|
|
192
|
+
* Result is cached at module scope because the check runs two execFileSync
|
|
193
|
+
* calls per invocation and is triggered once per project context on daemon
|
|
194
|
+
* startup. With N projects this used to cost ~2N synchronous subprocesses;
|
|
195
|
+
* caching collapses it to two total. The cache is invalidated alongside
|
|
196
|
+
* the auth cache (via invalidateAuthCache) since "just installed" is the
|
|
197
|
+
* same situation as "just logged in" from the daemon's perspective.
|
|
164
198
|
*/
|
|
199
|
+
var _installedCache = null;
|
|
200
|
+
|
|
165
201
|
function checkInstalled() {
|
|
202
|
+
if (_installedCache) return _installedCache;
|
|
203
|
+
|
|
166
204
|
var fs = require("fs");
|
|
167
|
-
var
|
|
205
|
+
var execFileSync = require("child_process").execFileSync;
|
|
168
206
|
var result = { claude: false, codex: false };
|
|
169
207
|
try {
|
|
170
|
-
|
|
208
|
+
if (process.platform === "win32") execFileSync("where", ["claude"], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
209
|
+
else execFileSync("which", ["claude"], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
171
210
|
result.claude = true;
|
|
172
211
|
} catch (e) {}
|
|
173
212
|
try {
|
|
@@ -177,53 +216,66 @@ function checkInstalled() {
|
|
|
177
216
|
codexBin = findCodexPath();
|
|
178
217
|
if (codexBin && fs.existsSync(codexBin)) {
|
|
179
218
|
result.codex = true;
|
|
219
|
+
_installedCache = result;
|
|
180
220
|
return result;
|
|
181
221
|
}
|
|
182
222
|
} catch (e) {}
|
|
183
223
|
|
|
184
|
-
var
|
|
185
|
-
|
|
224
|
+
var whichOut = process.platform === "win32"
|
|
225
|
+
? execFileSync("where", ["codex"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] })
|
|
226
|
+
: execFileSync("which", ["codex"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
|
|
186
227
|
if (whichOut.trim()) result.codex = true;
|
|
187
228
|
} catch (e) {}
|
|
229
|
+
_installedCache = result;
|
|
188
230
|
return result;
|
|
189
231
|
}
|
|
190
232
|
|
|
191
233
|
function invalidateAuthCache() {
|
|
192
234
|
_authCache = null;
|
|
235
|
+
_installedCache = null;
|
|
193
236
|
}
|
|
194
237
|
|
|
195
238
|
/**
|
|
196
239
|
* Create adapters for all authenticated vendors.
|
|
197
|
-
*
|
|
198
|
-
*
|
|
240
|
+
* Claude may be shared across projects, but Codex is instantiated per project
|
|
241
|
+
* so its app-server and bridge stay scoped to a single project slug.
|
|
199
242
|
* Returns { adapters: { vendor: Adapter }, auth: { vendor: boolean } }
|
|
200
243
|
*/
|
|
201
|
-
var
|
|
202
|
-
var _sharedAuth = null;
|
|
244
|
+
var _sharedClaudeAdapter = null;
|
|
203
245
|
|
|
204
246
|
function createAdapters(opts) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
247
|
+
opts = opts || {};
|
|
248
|
+
// Gate adapter creation on binary installation, not OAuth auth status.
|
|
249
|
+
// Claude Code supports multiple auth modes (OAuth, Bedrock, Vertex, API key)
|
|
250
|
+
// that `claude auth status` does not always detect. Runtime auth failures are
|
|
251
|
+
// handled downstream via query-level error detection.
|
|
252
|
+
var installed = checkInstalled();
|
|
253
|
+
var auth = { claude: false, codex: false };
|
|
210
254
|
var adapters = {};
|
|
211
|
-
var vendors = Object.keys(auth);
|
|
212
255
|
|
|
213
|
-
|
|
214
|
-
var vendor = vendors[i];
|
|
215
|
-
if (!auth[vendor]) continue;
|
|
256
|
+
if (installed.claude) {
|
|
216
257
|
try {
|
|
217
|
-
|
|
218
|
-
|
|
258
|
+
if (!_sharedClaudeAdapter) {
|
|
259
|
+
_sharedClaudeAdapter = createAdapter({ vendor: "claude", cwd: opts.cwd });
|
|
260
|
+
}
|
|
261
|
+
adapters.claude = _sharedClaudeAdapter;
|
|
262
|
+
auth.claude = true;
|
|
263
|
+
console.log("[yoke] Adapter created: claude");
|
|
219
264
|
} catch (e) {
|
|
220
|
-
console.error("[yoke] Failed to create adapter for
|
|
221
|
-
|
|
265
|
+
console.error("[yoke] Failed to create adapter for claude:", e.message);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (installed.codex) {
|
|
270
|
+
try {
|
|
271
|
+
adapters.codex = createAdapter({ vendor: "codex", cwd: opts.cwd, slug: opts.slug });
|
|
272
|
+
auth.codex = true;
|
|
273
|
+
console.log("[yoke] Adapter created: codex");
|
|
274
|
+
} catch (e) {
|
|
275
|
+
console.error("[yoke] Failed to create adapter for codex:", e.message);
|
|
222
276
|
}
|
|
223
277
|
}
|
|
224
278
|
|
|
225
|
-
_sharedAdapters = adapters;
|
|
226
|
-
_sharedAuth = auth;
|
|
227
279
|
return { adapters: adapters, auth: auth };
|
|
228
280
|
}
|
|
229
281
|
|
|
@@ -233,26 +285,19 @@ function createAdapters(opts) {
|
|
|
233
285
|
* Returns the adapter or null.
|
|
234
286
|
*/
|
|
235
287
|
async function lazyCreateAdapter(adapters, vendor, opts) {
|
|
236
|
-
|
|
237
|
-
adapters[vendor] = _sharedAdapters[vendor];
|
|
238
|
-
return adapters[vendor];
|
|
239
|
-
}
|
|
288
|
+
opts = opts || {};
|
|
240
289
|
|
|
241
290
|
// Force re-check since user may have logged in after server start
|
|
242
291
|
invalidateAuthCache();
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
var auth = checkAuth();
|
|
246
|
-
if (!auth[vendor]) return null;
|
|
292
|
+
var installed = checkInstalled();
|
|
293
|
+
if (!installed[vendor]) return null;
|
|
247
294
|
|
|
248
295
|
try {
|
|
249
|
-
var ad = createAdapter({ vendor: vendor, cwd: opts.cwd });
|
|
296
|
+
var ad = createAdapter({ vendor: vendor, cwd: opts.cwd, slug: opts.slug });
|
|
250
297
|
if (typeof ad.init === "function") {
|
|
251
298
|
await ad.init(opts || {});
|
|
252
299
|
}
|
|
253
300
|
console.log("[yoke] Lazy adapter created: " + vendor);
|
|
254
|
-
if (_sharedAdapters) _sharedAdapters[vendor] = ad;
|
|
255
|
-
if (_sharedAuth) _sharedAuth[vendor] = true;
|
|
256
301
|
adapters[vendor] = ad;
|
|
257
302
|
return ad;
|
|
258
303
|
} catch (e) {
|
package/lib/yoke/instructions.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
// --------------------------------------------------
|
|
4
4
|
// Codex spawns this as a native MCP server via config.mcp_servers["clay-tools"].
|
|
5
5
|
// It implements the MCP protocol on stdio (JSON-RPC) and proxies tool
|
|
6
|
-
// list/call requests to Clay's HTTP endpoint
|
|
6
|
+
// list/call requests to Clay's project-scoped HTTP endpoint
|
|
7
|
+
// (/p/{slug}/api/mcp-bridge) when a slug is provided.
|
|
7
8
|
//
|
|
8
9
|
// Usage: node mcp-bridge-server.js --port 2633 --slug my-project
|
|
9
10
|
//
|
|
@@ -37,8 +38,10 @@ for (var i = 0; i < args.length; i++) {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
var CLAY_PROTOCOL = clayTls ? "https" : "http";
|
|
40
|
-
// Use global endpoint (not project-scoped) so bridge works regardless of which project was active at init
|
|
41
41
|
var CLAY_BASE_URL = CLAY_PROTOCOL + "://127.0.0.1:" + clayPort;
|
|
42
|
+
var CLAY_MCP_PATH = claySlug
|
|
43
|
+
? ("/p/" + claySlug + "/api/mcp-bridge")
|
|
44
|
+
: "/api/mcp-bridge";
|
|
42
45
|
|
|
43
46
|
// --- Auth ---
|
|
44
47
|
var clayAuthToken = process.env.CLAY_AUTH_TOKEN || "";
|
|
@@ -91,8 +94,13 @@ function postJson(urlPath, body) {
|
|
|
91
94
|
reject(err);
|
|
92
95
|
});
|
|
93
96
|
|
|
94
|
-
//
|
|
95
|
-
|
|
97
|
+
// 10 min safety ceiling. ask_user_questions is stateless (returns
|
|
98
|
+
// immediately; the user's answer arrives on the next turn as a new
|
|
99
|
+
// user message), so no tool call should legitimately block long.
|
|
100
|
+
// The generous ceiling is just a belt-and-suspenders against slow
|
|
101
|
+
// browser automation or external MCP servers, not the primary
|
|
102
|
+
// mechanism for human-in-the-loop tools.
|
|
103
|
+
req.setTimeout(600000, function () {
|
|
96
104
|
req.destroy(new Error("Request timed out"));
|
|
97
105
|
});
|
|
98
106
|
|
|
@@ -103,7 +111,7 @@ function postJson(urlPath, body) {
|
|
|
103
111
|
|
|
104
112
|
// --- Fetch tools from Clay ---
|
|
105
113
|
function fetchTools() {
|
|
106
|
-
return postJson(
|
|
114
|
+
return postJson(CLAY_MCP_PATH, { action: "list_tools" }).then(function (resp) {
|
|
107
115
|
if (resp.error) {
|
|
108
116
|
log("Failed to fetch tools: " + resp.error);
|
|
109
117
|
return [];
|
|
@@ -124,7 +132,7 @@ function fetchTools() {
|
|
|
124
132
|
|
|
125
133
|
// --- Call a tool via Clay ---
|
|
126
134
|
function callTool(serverName, toolName, args) {
|
|
127
|
-
return postJson(
|
|
135
|
+
return postJson(CLAY_MCP_PATH, {
|
|
128
136
|
action: "call_tool",
|
|
129
137
|
server: serverName,
|
|
130
138
|
tool: toolName,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clay-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.34.0-beta.10",
|
|
4
4
|
"description": "Self-hosted Claude Code in your browser. Multi-session, multi-user, push notifications.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"clay-server": "./bin/cli.js",
|
|
@@ -37,7 +37,6 @@
|
|
|
37
37
|
"author": "Chad",
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@anthropic-ai/claude-agent-sdk": "^0.2.112",
|
|
40
|
-
"@google/genai": "^1.49.0",
|
|
41
40
|
"@lydell/node-pty": "^1.2.0-beta.3",
|
|
42
41
|
"@openai/codex": "^0.121.0",
|
|
43
42
|
"imapflow": "^1.3.1",
|