clay-server 2.32.0 → 2.32.1-beta.2

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/sdk-bridge.js CHANGED
@@ -107,7 +107,14 @@ function createSDKBridge(opts) {
107
107
  var mateDisplayName = opts.mateDisplayName || "";
108
108
  var isMate = opts.isMate || (slug.indexOf("mate-") === 0);
109
109
  var dangerouslySkipPermissions = opts.dangerouslySkipPermissions || false;
110
- var mcpServers = opts.mcpServers || null;
110
+ // mcpServers may be either a static object or a getter function. The
111
+ // getter form lets callers gate individual servers at call time (e.g.
112
+ // clay-browser is only exposed while the Chrome extension is connected).
113
+ var _mcpServersSrc = opts.mcpServers || null;
114
+ function getMcpServers() {
115
+ if (typeof _mcpServersSrc === "function") return _mcpServersSrc() || null;
116
+ return _mcpServersSrc;
117
+ }
111
118
  var getRemoteMcpServers = opts.getRemoteMcpServers || null;
112
119
  var clayPort = opts.clayPort || 2633;
113
120
  var clayTls = opts.clayTls || false;
@@ -921,7 +928,7 @@ function createSDKBridge(opts) {
921
928
  type: "auth_required",
922
929
  text: vendorName + " is not logged in.",
923
930
  vendor: session.vendor,
924
- loginCommand: session.vendor === "codex" ? "codex --login" : session.vendor + " login",
931
+ loginCommand: session.vendor === "codex" ? "codex login" : session.vendor + " login",
925
932
  linuxUser: authLinuxUser,
926
933
  canAutoLogin: canAutoLogin,
927
934
  });
@@ -1004,7 +1011,10 @@ function createSDKBridge(opts) {
1004
1011
  if (dangerouslySkipPermissions) {
1005
1012
  claudeOpts.allowDangerouslySkipPermissions = true;
1006
1013
  }
1007
- var modeToApply = session._loopPermissionMode || (session.acceptEditsAfterStart ? "acceptEdits" : sm.currentPermissionMode);
1014
+ var globalMode = sm.currentPermissionMode || "default";
1015
+ var loopFloor = session.acceptEditsAfterStart ? "acceptEdits" : "default";
1016
+ var effectiveDefault = globalMode === "bypassPermissions" ? globalMode : (loopFloor !== "default" ? loopFloor : globalMode);
1017
+ var modeToApply = session._loopPermissionMode || effectiveDefault;
1008
1018
  if (session.acceptEditsAfterStart) delete session.acceptEditsAfterStart;
1009
1019
  if (modeToApply && modeToApply !== "default") {
1010
1020
  claudeOpts.permissionMode = modeToApply;
@@ -1043,7 +1053,7 @@ function createSDKBridge(opts) {
1043
1053
  cwd: cwd,
1044
1054
  model: queryModel,
1045
1055
  effort: ls.effort || sm.currentEffort || undefined,
1046
- toolServers: mergeMcpServers(mcpServers, getRemoteMcpServers) || undefined,
1056
+ toolServers: mergeMcpServers(getMcpServers(), getRemoteMcpServers) || undefined,
1047
1057
  resumeSessionId: session.cliSessionId || undefined,
1048
1058
  abortController: linuxUser ? undefined : session.abortController,
1049
1059
  canUseTool: function(toolName, input, toolOpts) {
@@ -1201,8 +1211,11 @@ function createSDKBridge(opts) {
1201
1211
  if (tryExec("which claude")) result.push("claude");
1202
1212
 
1203
1213
  // Codex: check bundled binary or PATH
1204
- var codexBin = path.join(__dirname, "../node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/codex/codex");
1205
- if (fs.existsSync(codexBin) || tryExec("which codex")) result.push("codex");
1214
+ var codexBin = null;
1215
+ try {
1216
+ codexBin = require("./yoke/codex-app-server").findCodexPath();
1217
+ } catch (e) {}
1218
+ if ((codexBin && fs.existsSync(codexBin)) || tryExec("which codex")) result.push("codex");
1206
1219
 
1207
1220
  return result;
1208
1221
  }
@@ -1386,7 +1399,7 @@ function createSDKBridge(opts) {
1386
1399
  cwd: cwd,
1387
1400
  systemPrompt: opts.claudeMd,
1388
1401
  model: opts.model || undefined,
1389
- toolServers: opts.includeMcpServers ? (mergeMcpServers(mcpServers, getRemoteMcpServers) || undefined) : undefined,
1402
+ toolServers: opts.includeMcpServers ? (mergeMcpServers(getMcpServers(), getRemoteMcpServers) || undefined) : undefined,
1390
1403
  abortController: abortController,
1391
1404
  canUseTool: opts.canUseTool || function (toolName, input) {
1392
1405
  var whitelisted = checkToolWhitelist(toolName, input);
@@ -450,6 +450,59 @@ function attachSettings(ctx) {
450
450
  return true;
451
451
  }
452
452
 
453
+ // GET /api/user/tool-palettes
454
+ if (req.method === "GET" && fullUrl === "/api/user/tool-palettes") {
455
+ var muGet = getMultiUserFromReq(req);
456
+ var palettes = {};
457
+ if (!muGet) {
458
+ if (typeof opts.onGetToolPalettes === "function") {
459
+ palettes = opts.onGetToolPalettes() || {};
460
+ }
461
+ } else {
462
+ palettes = users.getToolPalettes(muGet.id) || {};
463
+ }
464
+ res.writeHead(200, { "Content-Type": "application/json" });
465
+ res.end(JSON.stringify(palettes));
466
+ return true;
467
+ }
468
+
469
+ // PUT /api/user/tool-palettes
470
+ if (req.method === "PUT" && fullUrl === "/api/user/tool-palettes") {
471
+ var muPut = getMultiUserFromReq(req);
472
+ var bodyTp = "";
473
+ req.on("data", function (chunk) { bodyTp += chunk; });
474
+ req.on("end", function () {
475
+ try {
476
+ var dataTp = JSON.parse(bodyTp);
477
+ var paletteName = dataTp.palette;
478
+ var order = dataTp.order;
479
+ var hidden = dataTp.hidden;
480
+ var result;
481
+ if (!muPut) {
482
+ if (typeof opts.onSetToolPalette !== "function") {
483
+ res.writeHead(500, { "Content-Type": "application/json" });
484
+ res.end('{"error":"Not supported"}');
485
+ return;
486
+ }
487
+ result = opts.onSetToolPalette(paletteName, order, hidden);
488
+ } else {
489
+ result = users.setToolPalette(muPut.id, paletteName, order, hidden);
490
+ }
491
+ if (result && result.error) {
492
+ res.writeHead(400, { "Content-Type": "application/json" });
493
+ res.end(JSON.stringify({ error: result.error }));
494
+ return;
495
+ }
496
+ res.writeHead(200, { "Content-Type": "application/json" });
497
+ res.end(JSON.stringify(result));
498
+ } catch (e) {
499
+ res.writeHead(400, { "Content-Type": "application/json" });
500
+ res.end('{"error":"Invalid request"}');
501
+ }
502
+ });
503
+ return true;
504
+ }
505
+
453
506
  // GET /api/user/auto-continue
454
507
  if (req.method === "GET" && fullUrl === "/api/user/auto-continue") {
455
508
  var mu = getMultiUserFromReq(req);
package/lib/sessions.js CHANGED
@@ -723,7 +723,9 @@ function createSessionManager(opts) {
723
723
  }
724
724
  }
725
725
  var toMigrate = candidates.filter(function(item) {
726
- return sdkTitles[item.cliSessionId] !== item.title;
726
+ var relayTitle = (item.title || "").trim();
727
+ var sdkTitle = (sdkTitles[item.cliSessionId] || "").trim();
728
+ return sdkTitle !== relayTitle;
727
729
  });
728
730
  if (toMigrate.length === 0) return;
729
731
  var migrated = 0;
@@ -732,7 +734,7 @@ function createSessionManager(opts) {
732
734
  for (var j = 0; j < toMigrate.length; j++) {
733
735
  (function(item) {
734
736
  chain = chain.then(function() {
735
- return adapter.renameSession(item.cliSessionId, item.title, { dir: migrateCwd }).then(function() {
737
+ return adapter.renameSession(item.cliSessionId, item.title.trim(), { dir: migrateCwd }).then(function() {
736
738
  migrated++;
737
739
  }).catch(function(e) {
738
740
  failed++;
@@ -175,6 +175,52 @@ function attachPreferences(deps) {
175
175
  return { error: "User not found" };
176
176
  }
177
177
 
178
+ // --- Per-user tool palette preferences ---
179
+ //
180
+ // Each user can customize the sidebar tool grid by reordering or
181
+ // hiding individual tools. Stored as an object keyed by palette name
182
+ // ("session" or "mate"), each holding { order: [...ids], hidden: [...ids] }.
183
+ // Missing ids are treated as "use registry default at the end", so new
184
+ // tools added in future releases show up for existing users without a
185
+ // migration.
186
+
187
+ var VALID_PALETTES = { session: true, mate: true };
188
+
189
+ function getToolPalettes(userId) {
190
+ var data = loadUsers();
191
+ for (var i = 0; i < data.users.length; i++) {
192
+ if (data.users[i].id === userId) {
193
+ return data.users[i].toolPalettes || {};
194
+ }
195
+ }
196
+ return {};
197
+ }
198
+
199
+ function setToolPalette(userId, paletteName, order, hidden) {
200
+ if (!VALID_PALETTES[paletteName]) {
201
+ return { error: "Unknown palette" };
202
+ }
203
+ var safeOrder = Array.isArray(order)
204
+ ? order.filter(function (s) { return typeof s === "string"; })
205
+ : [];
206
+ var safeHidden = Array.isArray(hidden)
207
+ ? hidden.filter(function (s) { return typeof s === "string"; })
208
+ : [];
209
+ var data = loadUsers();
210
+ for (var i = 0; i < data.users.length; i++) {
211
+ if (data.users[i].id === userId) {
212
+ if (!data.users[i].toolPalettes) data.users[i].toolPalettes = {};
213
+ data.users[i].toolPalettes[paletteName] = {
214
+ order: safeOrder,
215
+ hidden: safeHidden,
216
+ };
217
+ saveUsers(data);
218
+ return { ok: true, palette: paletteName, order: safeOrder, hidden: safeHidden };
219
+ }
220
+ }
221
+ return { error: "User not found" };
222
+ }
223
+
178
224
  // --- Mate onboarding ---
179
225
 
180
226
  function setMateOnboarded(userId) {
@@ -203,6 +249,8 @@ function attachPreferences(deps) {
203
249
  setChatLayout: setChatLayout,
204
250
  getAutoContinue: getAutoContinue,
205
251
  setAutoContinue: setAutoContinue,
252
+ getToolPalettes: getToolPalettes,
253
+ setToolPalette: setToolPalette,
206
254
  setMateOnboarded: setMateOnboarded,
207
255
  };
208
256
  }
package/lib/users.js CHANGED
@@ -416,6 +416,8 @@ var getChatLayout = preferences.getChatLayout;
416
416
  var setChatLayout = preferences.setChatLayout;
417
417
  var getAutoContinue = preferences.getAutoContinue;
418
418
  var setAutoContinue = preferences.setAutoContinue;
419
+ var getToolPalettes = preferences.getToolPalettes;
420
+ var setToolPalette = preferences.setToolPalette;
419
421
  var setMateOnboarded = preferences.setMateOnboarded;
420
422
 
421
423
  module.exports = {
@@ -473,6 +475,8 @@ module.exports = {
473
475
  setMateOnboarded: setMateOnboarded,
474
476
  getAutoContinue: getAutoContinue,
475
477
  setAutoContinue: setAutoContinue,
478
+ getToolPalettes: getToolPalettes,
479
+ setToolPalette: setToolPalette,
476
480
  getDeletedBuiltinKeys: getDeletedBuiltinKeys,
477
481
  addDeletedBuiltinKey: addDeletedBuiltinKey,
478
482
  removeDeletedBuiltinKey: removeDeletedBuiltinKey,
package/lib/yoke/index.js CHANGED
@@ -84,8 +84,8 @@ function checkAuth() {
84
84
 
85
85
  function checkCodex() {
86
86
  try {
87
- var path = require("path");
88
- var codexBin = path.join(__dirname, "../../node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/codex/codex");
87
+ var findCodexPath = require("./codex-app-server").findCodexPath;
88
+ var codexBin = findCodexPath();
89
89
  execSync(codexBin + " login status", { timeout: 5000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
90
90
  return true;
91
91
  } catch (e) {
@@ -111,7 +111,8 @@ function checkInstalled() {
111
111
  result.claude = true;
112
112
  } catch (e) {}
113
113
  try {
114
- var codexBin = path.join(__dirname, "../../node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/codex/codex");
114
+ var findCodexPath = require("./codex-app-server").findCodexPath;
115
+ var codexBin = findCodexPath();
115
116
  if (fs.existsSync(codexBin)) result.codex = true;
116
117
  } catch (e) {}
117
118
  return result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.32.0",
3
+ "version": "2.32.1-beta.2",
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",