clay-server 2.34.0-beta.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/daemon.js +97 -38
- package/lib/mate-datastore.js +27 -5
- package/lib/mates.js +2 -2
- package/lib/os-users.js +13 -0
- package/lib/project-connection.js +16 -9
- package/lib/project-mate-datastore.js +16 -2
- package/lib/project-sessions.js +110 -7
- package/lib/project-user-message.js +4 -3
- package/lib/project.js +97 -10
- package/lib/public/css/mates.css +94 -52
- package/lib/public/css/mobile-nav.css +0 -14
- package/lib/public/css/notifications-center.css +23 -19
- package/lib/public/css/sidebar.css +326 -101
- package/lib/public/index.html +24 -57
- package/lib/public/modules/app-dm.js +0 -2
- package/lib/public/modules/app-messages.js +3 -5
- 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 +108 -98
- package/lib/public/modules/mate-sidebar.js +0 -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 +58 -13
- package/lib/public/sw.js +1 -1
- package/lib/sdk-bridge.js +36 -28
- package/lib/sdk-message-processor.js +14 -3
- package/lib/server.js +28 -72
- package/lib/sessions.js +157 -20
- package/lib/ws-schema.js +2 -0
- package/lib/yoke/adapters/claude-worker.js +114 -2
- package/lib/yoke/adapters/claude.js +56 -5
- package/lib/yoke/adapters/codex.js +349 -58
- package/lib/yoke/index.js +73 -35
- 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/project-sessions.js
CHANGED
|
@@ -3,6 +3,33 @@ var path = require("path");
|
|
|
3
3
|
var { execFileSync } = require("child_process");
|
|
4
4
|
var { CODEX_DEFAULTS, getCodexConfig } = require("./codex-defaults");
|
|
5
5
|
|
|
6
|
+
// Format a user's answer to an ask_user_questions card as a plain user
|
|
7
|
+
// message so the MCP path can feed it back to the agent on the next turn.
|
|
8
|
+
// The agent sees: its own question text, then the selected answer(s).
|
|
9
|
+
function formatAskUserAnswerAsMessage(input, answers) {
|
|
10
|
+
var questions = (input && Array.isArray(input.questions)) ? input.questions : [];
|
|
11
|
+
if (questions.length === 0) {
|
|
12
|
+
// Shouldn't happen, but be defensive.
|
|
13
|
+
try { return "(answered with: " + JSON.stringify(answers || {}) + ")"; }
|
|
14
|
+
catch (e) { return "(answered)"; }
|
|
15
|
+
}
|
|
16
|
+
var lines = [];
|
|
17
|
+
for (var i = 0; i < questions.length; i++) {
|
|
18
|
+
var q = questions[i];
|
|
19
|
+
var qText = (q && q.question) ? q.question : ("Question " + (i + 1));
|
|
20
|
+
var ans = (answers && answers[i] != null) ? String(answers[i]) : "";
|
|
21
|
+
if (!ans) continue;
|
|
22
|
+
if (questions.length === 1) {
|
|
23
|
+
// Single question: keep it tight.
|
|
24
|
+
lines.push(ans);
|
|
25
|
+
} else {
|
|
26
|
+
lines.push("- " + qText + " → " + ans);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (lines.length === 0) return "(no answer provided)";
|
|
30
|
+
return lines.join("\n");
|
|
31
|
+
}
|
|
32
|
+
|
|
6
33
|
/**
|
|
7
34
|
* Attach session management, config, project management, and mid-section
|
|
8
35
|
* message handlers to a project context.
|
|
@@ -12,7 +39,7 @@ var { CODEX_DEFAULTS, getCodexConfig } = require("./codex-defaults");
|
|
|
12
39
|
* sm, sdk, tm, clients,
|
|
13
40
|
* send, sendTo, sendToAdmins, sendToSession, sendToSessionOthers,
|
|
14
41
|
* opts, usersModule, userPresence, matesModule, pushModule,
|
|
15
|
-
* getSessionForWs, getLinuxUserForSession, getOsUserInfoForWs,
|
|
42
|
+
* getSessionForWs, getLinuxUserForSession, ensureProjectAccessForSession, getOsUserInfoForWs,
|
|
16
43
|
* hydrateImageRefs, onProcessingChanged, broadcastPresence,
|
|
17
44
|
* adapter, getProjectList, getProjectCount, getScheduleCount,
|
|
18
45
|
* moveScheduleToProject, moveAllSchedulesToProject, getHubSchedules,
|
|
@@ -43,6 +70,7 @@ function attachSessions(ctx) {
|
|
|
43
70
|
var pushModule = ctx.pushModule;
|
|
44
71
|
var getSessionForWs = ctx.getSessionForWs;
|
|
45
72
|
var getLinuxUserForSession = ctx.getLinuxUserForSession;
|
|
73
|
+
var ensureProjectAccessForSession = ctx.ensureProjectAccessForSession;
|
|
46
74
|
var getOsUserInfoForWs = ctx.getOsUserInfoForWs;
|
|
47
75
|
var hydrateImageRefs = ctx.hydrateImageRefs;
|
|
48
76
|
var onProcessingChanged = ctx.onProcessingChanged;
|
|
@@ -136,6 +164,39 @@ function attachSessions(ctx) {
|
|
|
136
164
|
return true;
|
|
137
165
|
}
|
|
138
166
|
|
|
167
|
+
if (msg.type === "reorder_session_bookmarks") {
|
|
168
|
+
if (typeof msg.sourceId === "number" && typeof msg.targetId === "number" && msg.sourceId !== msg.targetId) {
|
|
169
|
+
var source = sm.sessions.get(msg.sourceId);
|
|
170
|
+
var target = sm.sessions.get(msg.targetId);
|
|
171
|
+
if (!source || !target) return true;
|
|
172
|
+
if (usersModule.isMultiUser() && ws._clayUser) {
|
|
173
|
+
if (!usersModule.canAccessSession(ws._clayUser.id, source, { visibility: "public" })) return true;
|
|
174
|
+
if (!usersModule.canAccessSession(ws._clayUser.id, target, { visibility: "public" })) return true;
|
|
175
|
+
}
|
|
176
|
+
sm.reorderBookmarkedSessions(msg.sourceId, msg.targetId, msg.insertBefore !== false);
|
|
177
|
+
}
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (msg.type === "bulk_delete_sessions") {
|
|
182
|
+
if (!Array.isArray(msg.sessionIds) || msg.sessionIds.length === 0) return true;
|
|
183
|
+
var deletableIds = [];
|
|
184
|
+
for (var di = 0; di < msg.sessionIds.length; di++) {
|
|
185
|
+
var bulkId = msg.sessionIds[di];
|
|
186
|
+
if (typeof bulkId !== "number") continue;
|
|
187
|
+
var bulkTarget = sm.sessions.get(bulkId);
|
|
188
|
+
if (!bulkTarget) continue;
|
|
189
|
+
if (usersModule.isMultiUser() && ws._clayUser) {
|
|
190
|
+
if (!usersModule.canAccessSession(ws._clayUser.id, bulkTarget, { visibility: "public" })) continue;
|
|
191
|
+
}
|
|
192
|
+
deletableIds.push(bulkId);
|
|
193
|
+
}
|
|
194
|
+
if (deletableIds.length > 0) {
|
|
195
|
+
sm.deleteSessionsBulk(deletableIds, ws);
|
|
196
|
+
}
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
|
|
139
200
|
if (msg.type === "transfer_project_owner") {
|
|
140
201
|
// Home directory projects: ownership is permanently locked
|
|
141
202
|
if (osUsers && osUsers.length > 0 && /^\/home\/[^/]+\//.test(cwd)) {
|
|
@@ -243,6 +304,17 @@ function attachSessions(ctx) {
|
|
|
243
304
|
|
|
244
305
|
if (msg.type === "switch_session") {
|
|
245
306
|
if (msg.id && sm.sessions.has(msg.id)) {
|
|
307
|
+
// If the target session's vendor doesn't own the currently cached
|
|
308
|
+
// model, clear sm.currentModel so the UI and next query don't leak
|
|
309
|
+
// the previous session's vendor-specific model into this one.
|
|
310
|
+
var switchTargetSess = sm.sessions.get(msg.id);
|
|
311
|
+
if (switchTargetSess && sm.currentModel) {
|
|
312
|
+
var targetVendor = switchTargetSess.vendor || sm.defaultVendor || null;
|
|
313
|
+
var tvModels = (targetVendor && sm.modelsByVendor && sm.modelsByVendor[targetVendor]) || [];
|
|
314
|
+
if (tvModels.length > 0 && tvModels.indexOf(sm.currentModel) === -1) {
|
|
315
|
+
sm.currentModel = "";
|
|
316
|
+
}
|
|
317
|
+
}
|
|
246
318
|
// Check access in multi-user mode
|
|
247
319
|
if (usersModule.isMultiUser() && ws._clayUser) {
|
|
248
320
|
var switchTarget = sm.sessions.get(msg.id);
|
|
@@ -747,10 +819,41 @@ function attachSessions(ctx) {
|
|
|
747
819
|
if (!pending) return true;
|
|
748
820
|
delete session.pendingAskUser[toolId];
|
|
749
821
|
sm.sendAndRecord(session, { type: "ask_user_answered", toolId: toolId, answers: answers });
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
822
|
+
|
|
823
|
+
if (pending.mode === "mcp") {
|
|
824
|
+
// Stateless MCP path: the tool already returned. Inject the user's
|
|
825
|
+
// answer as a new user message so the conversation continues
|
|
826
|
+
// naturally on the next turn. This matches how the mate would see
|
|
827
|
+
// any other user input.
|
|
828
|
+
var answerText = formatAskUserAnswerAsMessage(pending.input, answers);
|
|
829
|
+
var userMsg = { type: "user_message", text: answerText };
|
|
830
|
+
session.history.push(userMsg);
|
|
831
|
+
sm.appendToSessionFile(session, userMsg);
|
|
832
|
+
sendToSession(session.localId, userMsg);
|
|
833
|
+
|
|
834
|
+
if (!session.isProcessing) {
|
|
835
|
+
session.isProcessing = true;
|
|
836
|
+
onProcessingChanged();
|
|
837
|
+
session.sentToolResults = {};
|
|
838
|
+
sendToSession(session.localId, { type: "status", status: "processing" });
|
|
839
|
+
if (!session.queryInstance && !session.worker) {
|
|
840
|
+
sdk.startQuery(session, answerText, undefined, ensureProjectAccessForSession(session));
|
|
841
|
+
} else {
|
|
842
|
+
sdk.pushMessage(session, answerText);
|
|
843
|
+
}
|
|
844
|
+
} else {
|
|
845
|
+
// Turn is still running; queue for the next turn.
|
|
846
|
+
sdk.pushMessage(session, answerText);
|
|
847
|
+
}
|
|
848
|
+
} else {
|
|
849
|
+
// Claude native AskUserQuestion path (canUseTool). The SDK is
|
|
850
|
+
// synchronously blocked on the permission callback, so we must
|
|
851
|
+
// resolve it with the standard permission shape.
|
|
852
|
+
pending.resolve({
|
|
853
|
+
behavior: "allow",
|
|
854
|
+
updatedInput: Object.assign({}, pending.input, { answers: answers }),
|
|
855
|
+
});
|
|
856
|
+
}
|
|
754
857
|
return true;
|
|
755
858
|
}
|
|
756
859
|
|
|
@@ -863,7 +966,7 @@ function attachSessions(ctx) {
|
|
|
863
966
|
newSession.sentToolResults = {};
|
|
864
967
|
sendToSession(newSession.localId, { type: "status", status: "processing" });
|
|
865
968
|
newSession.acceptEditsAfterStart = true;
|
|
866
|
-
sdk.startQuery(newSession, planPrompt, undefined,
|
|
969
|
+
sdk.startQuery(newSession, planPrompt, undefined, ensureProjectAccessForSession(newSession));
|
|
867
970
|
} catch (e) {
|
|
868
971
|
console.error("[project] Error starting plan execution:", e);
|
|
869
972
|
sendTo(ws, { type: "error", text: "Failed to start plan execution: " + (e.message || e) });
|
|
@@ -894,7 +997,7 @@ function attachSessions(ctx) {
|
|
|
894
997
|
session.sentToolResults = {};
|
|
895
998
|
sendToSession(session.localId, { type: "status", status: "processing" });
|
|
896
999
|
if (!session.queryInstance && !session.worker) {
|
|
897
|
-
sdk.startQuery(session, feedback, undefined,
|
|
1000
|
+
sdk.startQuery(session, feedback, undefined, ensureProjectAccessForSession(session));
|
|
898
1001
|
} else {
|
|
899
1002
|
sdk.pushMessage(session, feedback);
|
|
900
1003
|
}
|
|
@@ -13,7 +13,7 @@ var fs = require("fs");
|
|
|
13
13
|
* send, sendTo, sendToSession, sendToSessionOthers,
|
|
14
14
|
* clients, opts,
|
|
15
15
|
* usersModule, matesModule,
|
|
16
|
-
* getSessionForWs, getLinuxUserForSession, getOsUserInfoForWs,
|
|
16
|
+
* getSessionForWs, getLinuxUserForSession, ensureProjectAccessForSession, getOsUserInfoForWs,
|
|
17
17
|
* hydrateImageRefs, saveImageFile, imagesDir,
|
|
18
18
|
* onProcessingChanged, onSessionDone,
|
|
19
19
|
* _loop - { handleLoopMessage: fn(ws, msg) }
|
|
@@ -49,6 +49,7 @@ function attachUserMessage(ctx) {
|
|
|
49
49
|
|
|
50
50
|
var getSessionForWs = ctx.getSessionForWs;
|
|
51
51
|
var getLinuxUserForSession = ctx.getLinuxUserForSession;
|
|
52
|
+
var ensureProjectAccessForSession = ctx.ensureProjectAccessForSession;
|
|
52
53
|
var getOsUserInfoForWs = ctx.getOsUserInfoForWs;
|
|
53
54
|
|
|
54
55
|
var hydrateImageRefs = ctx.hydrateImageRefs;
|
|
@@ -281,7 +282,7 @@ function attachUserMessage(ctx) {
|
|
|
281
282
|
nowSession.isProcessing = true;
|
|
282
283
|
onProcessingChanged();
|
|
283
284
|
sendToSession(nowSession.localId, { type: "status", status: "processing" });
|
|
284
|
-
sdk.startQuery(nowSession, schedText, null,
|
|
285
|
+
sdk.startQuery(nowSession, schedText, null, ensureProjectAccessForSession(nowSession));
|
|
285
286
|
sm.broadcastSessionList();
|
|
286
287
|
return true;
|
|
287
288
|
}
|
|
@@ -484,7 +485,7 @@ function attachUserMessage(ctx) {
|
|
|
484
485
|
// No active query (or worker idle between queries): start a new query
|
|
485
486
|
session._queryStartTs = Date.now();
|
|
486
487
|
console.log("[PERF] project.js: startQuery called, localId=" + session.localId + " t=0ms");
|
|
487
|
-
sdk.startQuery(session, finalText, msg.images,
|
|
488
|
+
sdk.startQuery(session, finalText, msg.images, ensureProjectAccessForSession(session));
|
|
488
489
|
} else {
|
|
489
490
|
sdk.pushMessage(session, finalText, msg.images);
|
|
490
491
|
}
|
package/lib/project.js
CHANGED
|
@@ -9,7 +9,7 @@ var { createNotesManager } = require("./notes");
|
|
|
9
9
|
var { fetchLatestVersion, fetchVersion, isNewer } = require("./updater");
|
|
10
10
|
var { execFileSync, spawn } = require("child_process");
|
|
11
11
|
var usersModule = require("./users");
|
|
12
|
-
var { resolveOsUserInfo, fsAsUser } = require("./os-users");
|
|
12
|
+
var { resolveOsUserInfo, fsAsUser, grantProjectAccess } = require("./os-users");
|
|
13
13
|
var crisisSafety = require("./crisis-safety");
|
|
14
14
|
var matesModule = require("./mates");
|
|
15
15
|
var sessionSearch = require("./session-search");
|
|
@@ -160,9 +160,10 @@ function createProjectContext(opts) {
|
|
|
160
160
|
var serverTls = opts.tls || false;
|
|
161
161
|
var serverAuthToken = opts.authToken || null;
|
|
162
162
|
var latestVersion = null;
|
|
163
|
+
var sessionTitleMigrationScheduled = false;
|
|
163
164
|
|
|
164
165
|
// --- YOKE adapters (multi-vendor, lazy init) ---
|
|
165
|
-
var _yokeState = yoke.createAdapters({ cwd: cwd });
|
|
166
|
+
var _yokeState = yoke.createAdapters({ cwd: cwd, slug: slug });
|
|
166
167
|
var adapters = _yokeState.adapters;
|
|
167
168
|
var defaultVendor = adapters.claude ? "claude" : Object.keys(adapters)[0] || "claude";
|
|
168
169
|
var adapter = adapters[defaultVendor] || null;
|
|
@@ -187,6 +188,14 @@ function createProjectContext(opts) {
|
|
|
187
188
|
return user.linuxUser;
|
|
188
189
|
}
|
|
189
190
|
|
|
191
|
+
function ensureProjectAccessForSession(session) {
|
|
192
|
+
var linuxUser = getLinuxUserForSession(session);
|
|
193
|
+
if (linuxUser) {
|
|
194
|
+
grantProjectAccess(cwd, linuxUser);
|
|
195
|
+
}
|
|
196
|
+
return linuxUser;
|
|
197
|
+
}
|
|
198
|
+
|
|
190
199
|
function getLinuxUserForWs(ws) {
|
|
191
200
|
if (!osUsers) return null;
|
|
192
201
|
if (!ws._clayUser || !ws._clayUser.linuxUser) return null;
|
|
@@ -496,6 +505,63 @@ function createProjectContext(opts) {
|
|
|
496
505
|
console.error("[project] Failed to create debate MCP server:", e.message);
|
|
497
506
|
}
|
|
498
507
|
|
|
508
|
+
// Ask-user MCP server (mates only)
|
|
509
|
+
if (isMate) {
|
|
510
|
+
try {
|
|
511
|
+
var askUserMcp = require("./ask-user-mcp-server");
|
|
512
|
+
var askUserToolDefs = askUserMcp.getToolDefs(function onAsk(input) {
|
|
513
|
+
// Stateless: the tool's job is to *post* the question card.
|
|
514
|
+
// We do NOT hold a promise open waiting for the user. When the
|
|
515
|
+
// user answers, the answer is injected as a fresh user message
|
|
516
|
+
// on the next turn (see project-sessions.js ask_user_response).
|
|
517
|
+
// This avoids HTTP long-poll timeouts on the MCP bridge and
|
|
518
|
+
// matches the natural multi-turn agent loop.
|
|
519
|
+
var session = sm.getActiveSession();
|
|
520
|
+
if (!session) {
|
|
521
|
+
// No active session means we have no way to show a card or
|
|
522
|
+
// route the answer. Fail closed rather than pretend success.
|
|
523
|
+
return Promise.resolve({
|
|
524
|
+
content: [{ type: "text", text: "Error: no active session in " + slug + "; cannot display question card." }],
|
|
525
|
+
isError: true,
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
if (session.loop && session.loop.active && session.loop.role !== "crafting") {
|
|
529
|
+
return Promise.resolve({
|
|
530
|
+
content: [{ type: "text", text: "Error: Autonomous mode. Make your own decision." }],
|
|
531
|
+
isError: true,
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
var toolId = "ask_" + Date.now() + "_" + crypto.randomUUID().slice(0, 8);
|
|
536
|
+
// Track for UI card lifecycle + answer routing. No resolve function.
|
|
537
|
+
session.pendingAskUser[toolId] = {
|
|
538
|
+
input: input,
|
|
539
|
+
mode: "mcp",
|
|
540
|
+
sessionId: session.localId,
|
|
541
|
+
postedAt: Date.now(),
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
sm.sendAndRecord(session, {
|
|
545
|
+
type: "tool_executing",
|
|
546
|
+
id: toolId,
|
|
547
|
+
name: "AskUserQuestion",
|
|
548
|
+
input: input,
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
return Promise.resolve({
|
|
552
|
+
content: [{
|
|
553
|
+
type: "text",
|
|
554
|
+
text: "The question card has been posted to the user. End this turn now without further commentary; the user's answer will arrive as the next user message.",
|
|
555
|
+
}],
|
|
556
|
+
});
|
|
557
|
+
});
|
|
558
|
+
var askUserMcpConfig = adapter.createToolServer({ name: "clay-ask-user", version: "1.0.0", tools: askUserToolDefs });
|
|
559
|
+
if (askUserMcpConfig) servers[askUserMcpConfig.name || "clay-ask-user"] = askUserMcpConfig;
|
|
560
|
+
} catch (e) {
|
|
561
|
+
console.error("[project] Failed to create ask-user MCP server:", e.message);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
499
565
|
// Browser MCP server (main project only, not mates)
|
|
500
566
|
if (!isMate) {
|
|
501
567
|
try {
|
|
@@ -742,7 +808,7 @@ function createProjectContext(opts) {
|
|
|
742
808
|
session.isProcessing = true;
|
|
743
809
|
onProcessingChanged();
|
|
744
810
|
sendToSession(session.localId, { type: "status", status: "processing" });
|
|
745
|
-
sdk.startQuery(session, text, null,
|
|
811
|
+
sdk.startQuery(session, text, null, ensureProjectAccessForSession(session));
|
|
746
812
|
sm.broadcastSessionList();
|
|
747
813
|
}, schedDelay),
|
|
748
814
|
};
|
|
@@ -1076,6 +1142,7 @@ function createProjectContext(opts) {
|
|
|
1076
1142
|
pushModule: pushModule,
|
|
1077
1143
|
getSessionForWs: getSessionForWs,
|
|
1078
1144
|
getLinuxUserForSession: getLinuxUserForSession,
|
|
1145
|
+
ensureProjectAccessForSession: ensureProjectAccessForSession,
|
|
1079
1146
|
getOsUserInfoForWs: getOsUserInfoForWs,
|
|
1080
1147
|
hydrateImageRefs: hydrateImageRefs,
|
|
1081
1148
|
onProcessingChanged: onProcessingChanged,
|
|
@@ -1124,6 +1191,7 @@ function createProjectContext(opts) {
|
|
|
1124
1191
|
matesModule: matesModule,
|
|
1125
1192
|
getSessionForWs: getSessionForWs,
|
|
1126
1193
|
getLinuxUserForSession: getLinuxUserForSession,
|
|
1194
|
+
ensureProjectAccessForSession: ensureProjectAccessForSession,
|
|
1127
1195
|
getOsUserInfoForWs: getOsUserInfoForWs,
|
|
1128
1196
|
hydrateImageRefs: hydrateImageRefs,
|
|
1129
1197
|
saveImageFile: saveImageFile,
|
|
@@ -1318,6 +1386,22 @@ function createProjectContext(opts) {
|
|
|
1318
1386
|
getLatestVersion: function () { return latestVersion; },
|
|
1319
1387
|
getTitle: function () { return title; },
|
|
1320
1388
|
getProject: function () { return project; },
|
|
1389
|
+
// Exposed so the first websocket connection can lazily warm up the
|
|
1390
|
+
// adapters for this project (see project-connection handleConnection).
|
|
1391
|
+
warmup: function () {
|
|
1392
|
+
sdk.warmup();
|
|
1393
|
+
sdk.startIdleReaper();
|
|
1394
|
+
if (!osUsers && !sessionTitleMigrationScheduled) {
|
|
1395
|
+
sessionTitleMigrationScheduled = true;
|
|
1396
|
+
setTimeout(function () {
|
|
1397
|
+
try {
|
|
1398
|
+
sm.migrateSessionTitles(adapter, cwd);
|
|
1399
|
+
} catch (e) {
|
|
1400
|
+
console.error("[project] Session title migration failed for " + slug + ":", e && e.message ? e.message : e);
|
|
1401
|
+
}
|
|
1402
|
+
}, 5000);
|
|
1403
|
+
}
|
|
1404
|
+
},
|
|
1321
1405
|
});
|
|
1322
1406
|
|
|
1323
1407
|
// --- Destroy ---
|
|
@@ -1376,6 +1460,15 @@ function createProjectContext(opts) {
|
|
|
1376
1460
|
var tmpDir = path.join(os.tmpdir(), "clay-" + cwdHash);
|
|
1377
1461
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
1378
1462
|
} catch (e) {}
|
|
1463
|
+
|
|
1464
|
+
var codexShutdown = Promise.resolve(true);
|
|
1465
|
+
if (adapters && adapters.codex && typeof adapters.codex.shutdown === "function") {
|
|
1466
|
+
codexShutdown = adapters.codex.shutdown().catch(function(err) {
|
|
1467
|
+
console.error("[project] Codex shutdown failed for " + slug + ":", err && err.message ? err.message : err);
|
|
1468
|
+
return false;
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
return codexShutdown;
|
|
1379
1472
|
}
|
|
1380
1473
|
|
|
1381
1474
|
// --- Status info ---
|
|
@@ -1523,15 +1616,9 @@ function createProjectContext(opts) {
|
|
|
1523
1616
|
broadcastClientCount();
|
|
1524
1617
|
broadcastPresence();
|
|
1525
1618
|
},
|
|
1526
|
-
warmup: function () {
|
|
1527
|
-
sdk.warmup();
|
|
1528
|
-
sdk.startIdleReaper();
|
|
1529
|
-
// Migrate existing relay session titles to SDK format (one-time, async)
|
|
1530
|
-
sm.migrateSessionTitles(adapter, cwd);
|
|
1531
|
-
},
|
|
1532
1619
|
destroy: function () {
|
|
1533
1620
|
sdk.stopIdleReaper();
|
|
1534
|
-
destroy();
|
|
1621
|
+
return destroy();
|
|
1535
1622
|
},
|
|
1536
1623
|
};
|
|
1537
1624
|
}
|
package/lib/public/css/mates.css
CHANGED
|
@@ -410,18 +410,18 @@
|
|
|
410
410
|
line-height: 1.4;
|
|
411
411
|
}
|
|
412
412
|
|
|
413
|
-
/* Step 4:
|
|
414
|
-
.mate-
|
|
413
|
+
/* Step 4: Vendor options */
|
|
414
|
+
.mate-vendor-option-list {
|
|
415
415
|
display: flex;
|
|
416
416
|
flex-direction: column;
|
|
417
|
-
gap:
|
|
417
|
+
gap: 10px;
|
|
418
418
|
}
|
|
419
|
-
.mate-
|
|
419
|
+
.mate-vendor-option-btn {
|
|
420
420
|
display: flex;
|
|
421
|
-
flex-direction:
|
|
421
|
+
flex-direction: row;
|
|
422
422
|
align-items: flex-start;
|
|
423
|
-
gap:
|
|
424
|
-
padding:
|
|
423
|
+
gap: 14px;
|
|
424
|
+
padding: 14px 16px;
|
|
425
425
|
background: var(--input-bg, #1a1a2e);
|
|
426
426
|
border: 2px solid var(--border, #333);
|
|
427
427
|
border-radius: 10px;
|
|
@@ -430,21 +430,36 @@
|
|
|
430
430
|
text-align: left;
|
|
431
431
|
color: var(--text, #fff);
|
|
432
432
|
font-family: inherit;
|
|
433
|
+
width: 100%;
|
|
433
434
|
}
|
|
434
|
-
.mate-
|
|
435
|
+
.mate-vendor-option-btn:hover {
|
|
435
436
|
border-color: var(--text-dimmer, #6272a4);
|
|
436
437
|
}
|
|
437
|
-
.mate-
|
|
438
|
+
.mate-vendor-option-btn.active {
|
|
438
439
|
border-color: var(--accent, #6c5ce7);
|
|
439
440
|
background: var(--accent-bg, rgba(108,92,231,0.12));
|
|
440
441
|
}
|
|
441
|
-
.mate-
|
|
442
|
+
.mate-vendor-option-icon {
|
|
443
|
+
width: 36px;
|
|
444
|
+
height: 36px;
|
|
445
|
+
border-radius: 8px;
|
|
446
|
+
flex-shrink: 0;
|
|
447
|
+
object-fit: cover;
|
|
448
|
+
}
|
|
449
|
+
.mate-vendor-option-text {
|
|
450
|
+
display: flex;
|
|
451
|
+
flex-direction: column;
|
|
452
|
+
gap: 4px;
|
|
453
|
+
min-width: 0;
|
|
454
|
+
}
|
|
455
|
+
.mate-vendor-option-title {
|
|
442
456
|
font-weight: 600;
|
|
443
457
|
font-size: 14px;
|
|
444
458
|
}
|
|
445
|
-
.mate-
|
|
459
|
+
.mate-vendor-option-desc {
|
|
446
460
|
font-size: 12px;
|
|
447
461
|
color: var(--text-muted, #9ea8c7);
|
|
462
|
+
line-height: 1.4;
|
|
448
463
|
}
|
|
449
464
|
|
|
450
465
|
/* Footer */
|
|
@@ -1997,19 +2012,60 @@ body.mate-dm-active #layout.sidebar-collapsed .mate-collapsed-info {
|
|
|
1997
2012
|
}
|
|
1998
2013
|
|
|
1999
2014
|
/* Mate datastore inspector */
|
|
2000
|
-
#mate-
|
|
2015
|
+
#mate-datastore-panel {
|
|
2016
|
+
position: absolute;
|
|
2017
|
+
inset: 0;
|
|
2018
|
+
z-index: 40;
|
|
2019
|
+
background: var(--bg);
|
|
2001
2020
|
display: flex;
|
|
2002
2021
|
flex-direction: column;
|
|
2003
|
-
|
|
2004
|
-
|
|
2022
|
+
overflow: hidden;
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
#mate-datastore-panel.hidden {
|
|
2026
|
+
display: none !important;
|
|
2005
2027
|
}
|
|
2006
2028
|
|
|
2007
|
-
#mate-
|
|
2029
|
+
#main-column.mate-datastore-open > .title-bar-content,
|
|
2030
|
+
#main-column.mate-datastore-open > .dm-header-bar,
|
|
2031
|
+
#main-column.mate-datastore-open > #main-panels {
|
|
2008
2032
|
display: none !important;
|
|
2009
2033
|
}
|
|
2010
2034
|
|
|
2035
|
+
.mate-datastore-top-bar {
|
|
2036
|
+
display: flex;
|
|
2037
|
+
align-items: center;
|
|
2038
|
+
justify-content: space-between;
|
|
2039
|
+
height: 48px;
|
|
2040
|
+
padding: 0 16px;
|
|
2041
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
2042
|
+
flex-shrink: 0;
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
.mate-datastore-top-title {
|
|
2046
|
+
display: flex;
|
|
2047
|
+
align-items: center;
|
|
2048
|
+
gap: 6px;
|
|
2049
|
+
font-weight: 700;
|
|
2050
|
+
font-size: 15px;
|
|
2051
|
+
color: var(--text);
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
.mate-datastore-top-title .lucide {
|
|
2055
|
+
width: 14px;
|
|
2056
|
+
height: 14px;
|
|
2057
|
+
color: var(--accent);
|
|
2058
|
+
opacity: 0.85;
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
.mate-datastore-top-actions {
|
|
2062
|
+
display: flex;
|
|
2063
|
+
align-items: center;
|
|
2064
|
+
gap: 8px;
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2011
2067
|
.mate-db-status {
|
|
2012
|
-
padding:
|
|
2068
|
+
padding: 10px 14px 0;
|
|
2013
2069
|
font-size: 12px;
|
|
2014
2070
|
color: var(--text-secondary, #8e8e8e);
|
|
2015
2071
|
}
|
|
@@ -2028,9 +2084,9 @@ body.mate-dm-active #layout.sidebar-collapsed .mate-collapsed-info {
|
|
|
2028
2084
|
|
|
2029
2085
|
.mate-db-layout {
|
|
2030
2086
|
display: grid;
|
|
2031
|
-
grid-template-columns:
|
|
2087
|
+
grid-template-columns: 220px minmax(0, 1fr);
|
|
2032
2088
|
gap: 10px;
|
|
2033
|
-
padding:
|
|
2089
|
+
padding: 12px 10px 10px;
|
|
2034
2090
|
min-height: 0;
|
|
2035
2091
|
flex: 1;
|
|
2036
2092
|
}
|
|
@@ -2044,10 +2100,10 @@ body.mate-dm-active #layout.sidebar-collapsed .mate-collapsed-info {
|
|
|
2044
2100
|
|
|
2045
2101
|
.mate-db-table-list {
|
|
2046
2102
|
overflow-y: auto;
|
|
2047
|
-
border: 1px solid var(--border-subtle, rgba(255,255,255,0.
|
|
2048
|
-
border-radius:
|
|
2049
|
-
background: var(--bg
|
|
2050
|
-
padding:
|
|
2103
|
+
border: 1px solid var(--border-subtle, rgba(255,255,255,0.06));
|
|
2104
|
+
border-radius: 12px;
|
|
2105
|
+
background: var(--sidebar-bg, rgba(255,255,255,0.03));
|
|
2106
|
+
padding: 6px;
|
|
2051
2107
|
flex: 1;
|
|
2052
2108
|
}
|
|
2053
2109
|
|
|
@@ -2123,46 +2179,32 @@ body.mate-dm-active #layout.sidebar-collapsed .mate-collapsed-info {
|
|
|
2123
2179
|
}
|
|
2124
2180
|
|
|
2125
2181
|
.mate-db-table-schema {
|
|
2126
|
-
max-height:
|
|
2182
|
+
max-height: 180px;
|
|
2127
2183
|
}
|
|
2128
2184
|
|
|
2129
|
-
|
|
2130
|
-
.mate-db-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
padding: 10px;
|
|
2134
|
-
border-radius: 10px;
|
|
2135
|
-
border: 1px solid var(--border-subtle, rgba(255,255,255,0.1));
|
|
2136
|
-
background: var(--bg-secondary, rgba(255,255,255,0.04));
|
|
2137
|
-
color: var(--text, #fff);
|
|
2138
|
-
font-family: var(--mono-font, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
|
|
2139
|
-
font-size: 11px;
|
|
2140
|
-
resize: vertical;
|
|
2185
|
+
@media (max-width: 900px) {
|
|
2186
|
+
.mate-db-layout {
|
|
2187
|
+
grid-template-columns: 1fr;
|
|
2188
|
+
}
|
|
2141
2189
|
}
|
|
2142
2190
|
|
|
2143
|
-
.mate-
|
|
2144
|
-
|
|
2191
|
+
body.mate-dm-active .mate-datastore-top-bar {
|
|
2192
|
+
background: var(--mate-color);
|
|
2193
|
+
color: #fff;
|
|
2145
2194
|
}
|
|
2146
2195
|
|
|
2147
|
-
.mate-
|
|
2148
|
-
|
|
2149
|
-
|
|
2196
|
+
body.mate-dm-active .mate-datastore-top-title,
|
|
2197
|
+
body.mate-dm-active .mate-datastore-top-title .lucide {
|
|
2198
|
+
color: #fff;
|
|
2150
2199
|
}
|
|
2151
2200
|
|
|
2152
|
-
.mate-
|
|
2153
|
-
|
|
2154
|
-
height: 30px;
|
|
2155
|
-
border: none;
|
|
2156
|
-
border-radius: 8px;
|
|
2157
|
-
background: var(--sidebar-active, rgba(255,255,255,0.08));
|
|
2158
|
-
color: var(--text, #fff);
|
|
2159
|
-
font-size: 12px;
|
|
2160
|
-
font-weight: 600;
|
|
2161
|
-
cursor: pointer;
|
|
2201
|
+
body.mate-dm-active .mate-datastore-top-bar .scheduler-close-btn {
|
|
2202
|
+
color: rgba(255, 255, 255, 0.85);
|
|
2162
2203
|
}
|
|
2163
2204
|
|
|
2164
|
-
.mate-
|
|
2165
|
-
|
|
2205
|
+
body.mate-dm-active .mate-datastore-top-bar .scheduler-close-btn:hover {
|
|
2206
|
+
color: #fff;
|
|
2207
|
+
background: rgba(255, 255, 255, 0.15);
|
|
2166
2208
|
}
|
|
2167
2209
|
|
|
2168
2210
|
.mate-session-item.search-match {
|
|
@@ -526,20 +526,6 @@
|
|
|
526
526
|
white-space: nowrap;
|
|
527
527
|
}
|
|
528
528
|
|
|
529
|
-
.mobile-session-bookmark {
|
|
530
|
-
display: inline-flex;
|
|
531
|
-
align-items: center;
|
|
532
|
-
color: var(--accent, #ff7b54);
|
|
533
|
-
flex-shrink: 0;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
.mobile-session-bookmark .lucide,
|
|
537
|
-
.mobile-session-bookmark svg {
|
|
538
|
-
width: 13px;
|
|
539
|
-
height: 13px;
|
|
540
|
-
display: block;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
529
|
.mobile-session-processing {
|
|
544
530
|
width: 7px;
|
|
545
531
|
height: 7px;
|