clay-server 2.9.0 → 2.9.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/bin/cli.js +47 -30
- package/lib/pages.js +18 -17
- package/lib/project.js +77 -63
- package/lib/public/app.js +250 -72
- package/lib/public/css/base.css +3 -0
- package/lib/public/css/filebrowser.css +8 -1
- package/lib/public/css/home-hub.css +1 -1
- package/lib/public/css/input.css +4 -0
- package/lib/public/css/menus.css +1 -44
- package/lib/public/css/messages.css +1 -0
- package/lib/public/css/mobile-nav.css +2 -2
- package/lib/public/css/overlays.css +175 -16
- package/lib/public/css/scheduler.css +1 -0
- package/lib/public/css/server-settings.css +37 -34
- package/lib/public/css/title-bar.css +0 -7
- package/lib/public/index.html +63 -22
- package/lib/public/manifest.json +2 -2
- package/lib/public/modules/notifications.js +15 -103
- package/lib/public/modules/scheduler.js +55 -48
- package/lib/public/modules/server-settings.js +13 -4
- package/lib/sdk-bridge.js +4 -6
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -28,7 +28,7 @@ if (_isDev) process.env.CLAY_DEV = "1";
|
|
|
28
28
|
var { loadConfig, saveConfig, configPath, socketPath, logPath, ensureConfigDir, isDaemonAlive, isDaemonAliveAsync, generateSlug, clearStaleConfig, loadClayrc, saveClayrc, readCrashInfo } = require("../lib/config");
|
|
29
29
|
var { sendIPCCommand } = require("../lib/ipc");
|
|
30
30
|
var { generateAuthToken } = require("../lib/server");
|
|
31
|
-
var { enableMultiUser, hasAdmin, isMultiUser } = require("../lib/users");
|
|
31
|
+
var { enableMultiUser, disableMultiUser, hasAdmin, isMultiUser } = require("../lib/users");
|
|
32
32
|
|
|
33
33
|
function openUrl(url) {
|
|
34
34
|
try {
|
|
@@ -2319,7 +2319,7 @@ function showSettingsMenu(config, ip) {
|
|
|
2319
2319
|
items.push({ label: "Set PIN", value: "pin" });
|
|
2320
2320
|
}
|
|
2321
2321
|
if (muEnabled) {
|
|
2322
|
-
items.push({ label: "
|
|
2322
|
+
items.push({ label: "Disable multi-user mode", value: "disable_multi_user" });
|
|
2323
2323
|
} else {
|
|
2324
2324
|
items.push({ label: "Enable multi-user mode", value: "multi_user" });
|
|
2325
2325
|
}
|
|
@@ -2365,39 +2365,51 @@ function showSettingsMenu(config, ip) {
|
|
|
2365
2365
|
break;
|
|
2366
2366
|
|
|
2367
2367
|
case "multi_user":
|
|
2368
|
-
|
|
2368
|
+
var muResult = enableMultiUser();
|
|
2369
|
+
log(sym.bar);
|
|
2370
|
+
log(sym.bar + " " + a.yellow + sym.warn + " Experimental Feature" + a.reset);
|
|
2371
|
+
log(sym.bar);
|
|
2372
|
+
log(sym.bar + " " + a.dim + "Multi-user mode is experimental and may change in future releases." + a.reset);
|
|
2373
|
+
log(sym.bar + " " + a.dim + "Sharing access to AI-powered tools may be subject to your provider's" + a.reset);
|
|
2374
|
+
log(sym.bar + " " + a.dim + "terms of service. Please review the applicable usage policies before" + a.reset);
|
|
2375
|
+
log(sym.bar + " " + a.dim + "granting access to other users." + a.reset);
|
|
2376
|
+
log(sym.bar);
|
|
2377
|
+
if (muResult.setupCode) {
|
|
2378
|
+
log(sym.bar + " " + a.green + "Multi-user mode enabled." + a.reset);
|
|
2369
2379
|
log(sym.bar);
|
|
2370
|
-
log(sym.bar + " " + a.
|
|
2371
|
-
log(sym.bar + " " + a.dim + "No changes made." + a.reset);
|
|
2380
|
+
log(sym.bar + " Setup code: " + a.bold + muResult.setupCode + a.reset);
|
|
2372
2381
|
log(sym.bar);
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
});
|
|
2382
|
+
log(sym.bar + " " + a.dim + "Open Clay in your browser and enter this code to create the admin account." + a.reset);
|
|
2383
|
+
log(sym.bar + " " + a.dim + "The code is single-use and will be cleared once the admin is set up." + a.reset);
|
|
2376
2384
|
} else {
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2385
|
+
log(sym.bar + " " + a.dim + "Multi-user mode is already enabled." + a.reset);
|
|
2386
|
+
}
|
|
2387
|
+
log(sym.bar);
|
|
2388
|
+
promptSelect("Back?", [{ label: "Back", value: "back" }], function () {
|
|
2389
|
+
showSettingsMenu(config, ip);
|
|
2390
|
+
});
|
|
2391
|
+
break;
|
|
2392
|
+
|
|
2393
|
+
case "disable_multi_user":
|
|
2394
|
+
log(sym.bar);
|
|
2395
|
+
log(sym.bar + " " + a.yellow + sym.warn + " Disable multi-user mode?" + a.reset);
|
|
2396
|
+
log(sym.bar);
|
|
2397
|
+
log(sym.bar + " " + a.dim + "Sessions created by other users will no longer be visible." + a.reset);
|
|
2398
|
+
log(sym.bar + " " + a.dim + "User accounts will be preserved and restored if re-enabled." + a.reset);
|
|
2399
|
+
log(sym.bar);
|
|
2400
|
+
promptSelect("Confirm", [
|
|
2401
|
+
{ label: "Disable multi-user mode", value: "confirm" },
|
|
2402
|
+
{ label: "Cancel", value: "cancel" },
|
|
2403
|
+
], function (confirmChoice) {
|
|
2404
|
+
if (confirmChoice === "confirm") {
|
|
2405
|
+
disableMultiUser();
|
|
2388
2406
|
log(sym.bar);
|
|
2389
|
-
log(sym.
|
|
2407
|
+
log(sym.done + " " + a.green + "Multi-user mode disabled." + a.reset);
|
|
2408
|
+
log(sym.bar + " " + a.dim + "Restart the daemon for changes to take full effect." + a.reset);
|
|
2390
2409
|
log(sym.bar);
|
|
2391
|
-
log(sym.bar + " " + a.dim + "Open Clay in your browser and enter this code to create the admin account." + a.reset);
|
|
2392
|
-
log(sym.bar + " " + a.dim + "The code is single-use and will be cleared once the admin is set up." + a.reset);
|
|
2393
|
-
} else {
|
|
2394
|
-
log(sym.bar + " " + a.dim + "Multi-user mode is already enabled." + a.reset);
|
|
2395
2410
|
}
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
showSettingsMenu(config, ip);
|
|
2399
|
-
});
|
|
2400
|
-
}
|
|
2411
|
+
showSettingsMenu(config, ip);
|
|
2412
|
+
});
|
|
2401
2413
|
break;
|
|
2402
2414
|
|
|
2403
2415
|
case "logs":
|
|
@@ -2456,7 +2468,12 @@ var currentVersion = require("../package.json").version;
|
|
|
2456
2468
|
clearStaleConfig();
|
|
2457
2469
|
await new Promise(function (resolve) { setTimeout(resolve, 500); });
|
|
2458
2470
|
}
|
|
2459
|
-
//
|
|
2471
|
+
// No running daemon — clear config so setup runs fresh
|
|
2472
|
+
if (!devAlive && devConfig) {
|
|
2473
|
+
if (devConfig.pid) clearStaleConfig();
|
|
2474
|
+
devConfig = null;
|
|
2475
|
+
}
|
|
2476
|
+
// No config — go through setup (disclaimer, port, PIN, etc.)
|
|
2460
2477
|
if (!devConfig) {
|
|
2461
2478
|
setup(function (pin, keepAwake) {
|
|
2462
2479
|
devMode(pin, keepAwake, null);
|
package/lib/pages.js
CHANGED
|
@@ -22,11 +22,10 @@ function pinPageHtml() {
|
|
|
22
22
|
'if(d.ok){location.reload();return}' +
|
|
23
23
|
'if(d.locked){for(var i=0;i<boxes.length;i++)boxes[i].disabled=true;' +
|
|
24
24
|
'err.textContent="Too many attempts. Try again in "+Math.ceil(d.retryAfter/60)+" min";' +
|
|
25
|
-
'setTimeout(function(){for(var i=0;i<boxes.length;i++)
|
|
26
|
-
'
|
|
25
|
+
'setTimeout(function(){for(var i=0;i<boxes.length;i++)boxes[i].disabled=false;' +
|
|
26
|
+
'err.textContent="";resetPinBoxes()},d.retryAfter*1000);return}' +
|
|
27
27
|
'var msg="Wrong PIN";if(typeof d.attemptsLeft==="number"&&d.attemptsLeft<=3)msg+=" ("+d.attemptsLeft+" left)";' +
|
|
28
|
-
'err.textContent=msg;
|
|
29
|
-
'document.getElementById("pin").value="";boxes[0].focus()})' +
|
|
28
|
+
'err.textContent=msg;resetPinBoxes()})' +
|
|
30
29
|
'.catch(function(){err.textContent="Connection error"})}' +
|
|
31
30
|
'initPinBoxes("pin-boxes","pin",submitPin);' +
|
|
32
31
|
'</script></div></body></html>';
|
|
@@ -727,9 +726,9 @@ var authPageStyles =
|
|
|
727
726
|
// PIN digit boxes
|
|
728
727
|
'.pin-wrap{display:flex;gap:8px;justify-content:center}' +
|
|
729
728
|
'.pin-digit{width:44px;height:56px;background:var(--input-bg);border:1.5px solid var(--border);border-radius:8px;' +
|
|
730
|
-
'color:var(--
|
|
729
|
+
'color:var(--text);font-family:"Courier New",Courier,"Roboto Mono",monospace;font-size:28px;font-weight:700;' +
|
|
731
730
|
'text-align:center;line-height:56px;outline:none;caret-color:transparent;' +
|
|
732
|
-
'
|
|
731
|
+
'transition:border-color 0.15s,box-shadow 0.15s}' +
|
|
733
732
|
'.pin-digit:focus{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-20)}' +
|
|
734
733
|
'.pin-digit.filled{color:var(--text)}' +
|
|
735
734
|
// Legacy single-input fallback
|
|
@@ -754,28 +753,30 @@ var pinBoxScript =
|
|
|
754
753
|
'function initPinBoxes(cId,hId,onComplete){' +
|
|
755
754
|
'var wrap=document.getElementById(cId),hidden=document.getElementById(hId);' +
|
|
756
755
|
'var boxes=wrap.querySelectorAll(".pin-digit");' +
|
|
756
|
+
'var digits=["","","","","",""];' +
|
|
757
757
|
'boxes[0].focus();' +
|
|
758
|
+
'function setDigit(idx,v){digits[idx]=v;boxes[idx].value=v?"\\u2022":"";boxes[idx].classList.toggle("filled",v.length>0);syncHidden()}' +
|
|
758
759
|
'for(var i=0;i<boxes.length;i++){(function(idx){' +
|
|
759
760
|
'boxes[idx].addEventListener("input",function(e){' +
|
|
760
|
-
'var
|
|
761
|
-
'if(
|
|
762
|
-
'
|
|
763
|
-
'
|
|
764
|
-
'syncHidden();' +
|
|
761
|
+
'var raw=this.value.replace(/[^0-9]/g,"");' +
|
|
762
|
+
'if(!raw){setDigit(idx,"");return}' +
|
|
763
|
+
'var v=raw.charAt(raw.length-1);' +
|
|
764
|
+
'setDigit(idx,v);' +
|
|
765
765
|
'if(v&&idx<5)boxes[idx+1].focus();' +
|
|
766
766
|
'if(hidden.value.length===6&&onComplete)onComplete()});' +
|
|
767
767
|
'boxes[idx].addEventListener("keydown",function(e){' +
|
|
768
|
-
'if(e.key==="Backspace"
|
|
768
|
+
'if(e.key==="Backspace"){if(!digits[idx]&&idx>0){setDigit(idx-1,"");boxes[idx-1].focus()}else{setDigit(idx,"")}syncHidden();return}' +
|
|
769
769
|
'if(e.key==="ArrowLeft"&&idx>0)boxes[idx-1].focus();' +
|
|
770
770
|
'if(e.key==="ArrowRight"&&idx<5)boxes[idx+1].focus();' +
|
|
771
771
|
'if(e.key==="Enter"&&hidden.value.length===6&&onComplete){e.preventDefault();onComplete()}});' +
|
|
772
772
|
'boxes[idx].addEventListener("paste",function(e){' +
|
|
773
773
|
'e.preventDefault();var d=(e.clipboardData||window.clipboardData).getData("text").replace(/[^0-9]/g,"").slice(0,6);' +
|
|
774
|
-
'for(var j=0;j<d.length&&j<6;j++){
|
|
775
|
-
'
|
|
774
|
+
'for(var j=0;j<d.length&&j<6;j++){setDigit(j,d.charAt(j))}' +
|
|
775
|
+
'if(d.length>=6){boxes[5].focus();if(onComplete)onComplete()}else if(d.length>0)boxes[d.length].focus()});' +
|
|
776
776
|
'boxes[idx].addEventListener("focus",function(){this.select()});' +
|
|
777
777
|
'})(i)}' +
|
|
778
|
-
'function syncHidden(){var v="";for(var j=0;j<
|
|
778
|
+
'function syncHidden(){var v="";for(var j=0;j<digits.length;j++)v+=digits[j];hidden.value=v}' +
|
|
779
|
+
'window.resetPinBoxes=function(){for(var j=0;j<6;j++){digits[j]="";boxes[j].value="";boxes[j].classList.remove("filled")}hidden.value="";boxes[0].focus()}' +
|
|
779
780
|
'}';
|
|
780
781
|
|
|
781
782
|
// HTML fragment for 6 PIN digit boxes + hidden input
|
|
@@ -938,8 +939,8 @@ function multiUserLoginPageHtml() {
|
|
|
938
939
|
|
|
939
940
|
'function resetPin(){' +
|
|
940
941
|
'var boxes=document.querySelectorAll(".pin-digit");' +
|
|
941
|
-
'for(var i=0;i<boxes.length;i++)
|
|
942
|
-
'
|
|
942
|
+
'for(var i=0;i<boxes.length;i++)boxes[i].disabled=false;' +
|
|
943
|
+
'if(window.resetPinBoxes)resetPinBoxes();btns[1].disabled=true}' +
|
|
943
944
|
|
|
944
945
|
'function goBackToUsername(){' +
|
|
945
946
|
'steps[1].classList.remove("active");dots[1].classList.remove("current");dots[1].classList.remove("done");' +
|
package/lib/project.js
CHANGED
|
@@ -156,6 +156,24 @@ function createProjectContext(opts) {
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
function sendToSession(sessionId, obj) {
|
|
160
|
+
var data = JSON.stringify(obj);
|
|
161
|
+
for (var ws of clients) {
|
|
162
|
+
if (ws.readyState === 1 && ws._clayActiveSession === sessionId) {
|
|
163
|
+
ws.send(data);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function sendToSessionOthers(sender, sessionId, obj) {
|
|
169
|
+
var data = JSON.stringify(obj);
|
|
170
|
+
for (var ws of clients) {
|
|
171
|
+
if (ws !== sender && ws.readyState === 1 && ws._clayActiveSession === sessionId) {
|
|
172
|
+
ws.send(data);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
159
177
|
// --- File watcher ---
|
|
160
178
|
var fileWatcher = null;
|
|
161
179
|
var watchedPath = null;
|
|
@@ -703,7 +721,7 @@ function createProjectContext(opts) {
|
|
|
703
721
|
session.isProcessing = true;
|
|
704
722
|
onProcessingChanged();
|
|
705
723
|
session.sentToolResults = {};
|
|
706
|
-
|
|
724
|
+
sendToSession(session.localId, { type: "status", status: "processing" });
|
|
707
725
|
session.acceptEditsAfterStart = true;
|
|
708
726
|
session.singleTurn = true;
|
|
709
727
|
sdk.startQuery(session, loopState.promptText);
|
|
@@ -1016,12 +1034,14 @@ function createProjectContext(opts) {
|
|
|
1016
1034
|
});
|
|
1017
1035
|
}
|
|
1018
1036
|
|
|
1019
|
-
// Session list (filtered for
|
|
1037
|
+
// Session list (filtered for access control)
|
|
1020
1038
|
var allSessions = [].concat(Array.from(sm.sessions.values())).filter(function (s) { return !s.hidden; });
|
|
1021
1039
|
if (usersModule.isMultiUser() && wsUser) {
|
|
1022
1040
|
allSessions = allSessions.filter(function (s) {
|
|
1023
1041
|
return usersModule.canAccessSession(wsUser.id, s, { visibility: "public" });
|
|
1024
1042
|
});
|
|
1043
|
+
} else if (!usersModule.isMultiUser()) {
|
|
1044
|
+
allSessions = allSessions.filter(function (s) { return !s.ownerId; });
|
|
1025
1045
|
}
|
|
1026
1046
|
sendTo(ws, {
|
|
1027
1047
|
type: "session_list",
|
|
@@ -1048,12 +1068,23 @@ function createProjectContext(opts) {
|
|
|
1048
1068
|
}),
|
|
1049
1069
|
});
|
|
1050
1070
|
|
|
1051
|
-
// Restore active session for this client (check access
|
|
1071
|
+
// Restore active session for this client (check access)
|
|
1052
1072
|
var active = sm.getActiveSession();
|
|
1053
1073
|
if (active && usersModule.isMultiUser() && wsUser) {
|
|
1054
1074
|
if (!usersModule.canAccessSession(wsUser.id, active, { visibility: "public" })) {
|
|
1055
1075
|
active = null;
|
|
1056
1076
|
}
|
|
1077
|
+
} else if (active && !usersModule.isMultiUser() && active.ownerId) {
|
|
1078
|
+
active = null;
|
|
1079
|
+
}
|
|
1080
|
+
// Fallback: pick the most recent accessible session
|
|
1081
|
+
if (!active && allSessions.length > 0) {
|
|
1082
|
+
active = allSessions[0];
|
|
1083
|
+
for (var fi = 1; fi < allSessions.length; fi++) {
|
|
1084
|
+
if ((allSessions[fi].lastActivity || 0) > (active.lastActivity || 0)) {
|
|
1085
|
+
active = allSessions[fi];
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1057
1088
|
}
|
|
1058
1089
|
if (active) {
|
|
1059
1090
|
ws._clayActiveSession = active.localId;
|
|
@@ -1101,6 +1132,10 @@ function createProjectContext(opts) {
|
|
|
1101
1132
|
}
|
|
1102
1133
|
|
|
1103
1134
|
// --- WS message handler ---
|
|
1135
|
+
function getSessionForWs(ws) {
|
|
1136
|
+
return sm.sessions.get(ws._clayActiveSession) || null;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1104
1139
|
function handleMessage(ws, msg) {
|
|
1105
1140
|
if (msg.type === "push_subscribe") {
|
|
1106
1141
|
if (pushModule && msg.subscription) pushModule.addSubscription(msg.subscription, msg.replaceEndpoint);
|
|
@@ -1108,7 +1143,7 @@ function createProjectContext(opts) {
|
|
|
1108
1143
|
}
|
|
1109
1144
|
|
|
1110
1145
|
if (msg.type === "load_more_history") {
|
|
1111
|
-
var session =
|
|
1146
|
+
var session = getSessionForWs(ws);
|
|
1112
1147
|
if (!session || typeof msg.before !== "number") return;
|
|
1113
1148
|
var before = msg.before;
|
|
1114
1149
|
var from = sm.findTurnBoundary(session.history, Math.max(0, before - sm.HISTORY_PAGE_SIZE));
|
|
@@ -1126,9 +1161,9 @@ function createProjectContext(opts) {
|
|
|
1126
1161
|
var sessionOpts = {};
|
|
1127
1162
|
if (ws._clayUser) sessionOpts.ownerId = ws._clayUser.id;
|
|
1128
1163
|
if (msg.sessionVisibility) sessionOpts.sessionVisibility = msg.sessionVisibility;
|
|
1129
|
-
var newSess = sm.createSession(sessionOpts,
|
|
1164
|
+
var newSess = sm.createSession(sessionOpts, ws);
|
|
1165
|
+
ws._clayActiveSession = newSess.localId;
|
|
1130
1166
|
if (usersModule.isMultiUser()) {
|
|
1131
|
-
ws._clayActiveSession = newSess.localId;
|
|
1132
1167
|
broadcastPresence();
|
|
1133
1168
|
}
|
|
1134
1169
|
return;
|
|
@@ -1152,11 +1187,11 @@ function createProjectContext(opts) {
|
|
|
1152
1187
|
break;
|
|
1153
1188
|
}
|
|
1154
1189
|
}
|
|
1155
|
-
var
|
|
1156
|
-
|
|
1190
|
+
var resumed = sm.resumeSession(msg.cliSessionId, { history: history, title: title }, ws);
|
|
1191
|
+
if (resumed) ws._clayActiveSession = resumed.localId;
|
|
1157
1192
|
}).catch(function () {
|
|
1158
|
-
var
|
|
1159
|
-
|
|
1193
|
+
var resumed = sm.resumeSession(msg.cliSessionId, undefined, ws);
|
|
1194
|
+
if (resumed) ws._clayActiveSession = resumed.localId;
|
|
1160
1195
|
});
|
|
1161
1196
|
return;
|
|
1162
1197
|
}
|
|
@@ -1201,7 +1236,8 @@ function createProjectContext(opts) {
|
|
|
1201
1236
|
sm.switchSession(msg.id, ws);
|
|
1202
1237
|
broadcastPresence();
|
|
1203
1238
|
} else {
|
|
1204
|
-
|
|
1239
|
+
ws._clayActiveSession = msg.id;
|
|
1240
|
+
sm.switchSession(msg.id, ws);
|
|
1205
1241
|
}
|
|
1206
1242
|
}
|
|
1207
1243
|
return;
|
|
@@ -1209,7 +1245,7 @@ function createProjectContext(opts) {
|
|
|
1209
1245
|
|
|
1210
1246
|
if (msg.type === "delete_session") {
|
|
1211
1247
|
if (msg.id && sm.sessions.has(msg.id)) {
|
|
1212
|
-
sm.deleteSession(msg.id,
|
|
1248
|
+
sm.deleteSession(msg.id, ws);
|
|
1213
1249
|
}
|
|
1214
1250
|
return;
|
|
1215
1251
|
}
|
|
@@ -1276,7 +1312,7 @@ function createProjectContext(opts) {
|
|
|
1276
1312
|
}
|
|
1277
1313
|
|
|
1278
1314
|
if (msg.type === "stop") {
|
|
1279
|
-
var session =
|
|
1315
|
+
var session = getSessionForWs(ws);
|
|
1280
1316
|
if (session && session.abortController && session.isProcessing) {
|
|
1281
1317
|
session.abortController.abort();
|
|
1282
1318
|
}
|
|
@@ -1297,22 +1333,22 @@ function createProjectContext(opts) {
|
|
|
1297
1333
|
// Verify target is actually a claude process before killing
|
|
1298
1334
|
if (!sdk.isClaudeProcess(pid)) {
|
|
1299
1335
|
console.error("[project] Refused to kill PID " + pid + ": not a claude process");
|
|
1300
|
-
|
|
1336
|
+
sendTo(ws, { type: "error", text: "Process " + pid + " is not a Claude process." });
|
|
1301
1337
|
return;
|
|
1302
1338
|
}
|
|
1303
1339
|
try {
|
|
1304
1340
|
process.kill(pid, "SIGTERM");
|
|
1305
1341
|
console.log("[project] Sent SIGTERM to conflicting Claude process PID " + pid);
|
|
1306
|
-
|
|
1342
|
+
sendTo(ws, { type: "process_killed", pid: pid });
|
|
1307
1343
|
} catch (e) {
|
|
1308
1344
|
console.error("[project] Failed to kill PID " + pid + ":", e.message);
|
|
1309
|
-
|
|
1345
|
+
sendTo(ws, { type: "error", text: "Failed to kill process " + pid + ": " + (e.message || e) });
|
|
1310
1346
|
}
|
|
1311
1347
|
return;
|
|
1312
1348
|
}
|
|
1313
1349
|
|
|
1314
1350
|
if (msg.type === "set_model" && msg.model) {
|
|
1315
|
-
var session =
|
|
1351
|
+
var session = getSessionForWs(ws);
|
|
1316
1352
|
if (session) {
|
|
1317
1353
|
sdk.setModel(session, msg.model);
|
|
1318
1354
|
}
|
|
@@ -1323,7 +1359,7 @@ function createProjectContext(opts) {
|
|
|
1323
1359
|
if (typeof opts.onSetServerDefaultModel === "function") {
|
|
1324
1360
|
opts.onSetServerDefaultModel(msg.model);
|
|
1325
1361
|
}
|
|
1326
|
-
var session =
|
|
1362
|
+
var session = getSessionForWs(ws);
|
|
1327
1363
|
if (session) {
|
|
1328
1364
|
sdk.setModel(session, msg.model);
|
|
1329
1365
|
}
|
|
@@ -1334,7 +1370,7 @@ function createProjectContext(opts) {
|
|
|
1334
1370
|
if (typeof opts.onSetProjectDefaultModel === "function") {
|
|
1335
1371
|
opts.onSetProjectDefaultModel(slug, msg.model);
|
|
1336
1372
|
}
|
|
1337
|
-
var session =
|
|
1373
|
+
var session = getSessionForWs(ws);
|
|
1338
1374
|
if (session) {
|
|
1339
1375
|
sdk.setModel(session, msg.model);
|
|
1340
1376
|
}
|
|
@@ -1348,7 +1384,7 @@ function createProjectContext(opts) {
|
|
|
1348
1384
|
return;
|
|
1349
1385
|
}
|
|
1350
1386
|
sm.currentPermissionMode = msg.mode;
|
|
1351
|
-
var session =
|
|
1387
|
+
var session = getSessionForWs(ws);
|
|
1352
1388
|
if (session) {
|
|
1353
1389
|
sdk.setPermissionMode(session, msg.mode);
|
|
1354
1390
|
}
|
|
@@ -1362,7 +1398,7 @@ function createProjectContext(opts) {
|
|
|
1362
1398
|
}
|
|
1363
1399
|
if (!dangerouslySkipPermissions) {
|
|
1364
1400
|
sm.currentPermissionMode = msg.mode;
|
|
1365
|
-
var session =
|
|
1401
|
+
var session = getSessionForWs(ws);
|
|
1366
1402
|
if (session) {
|
|
1367
1403
|
sdk.setPermissionMode(session, msg.mode);
|
|
1368
1404
|
}
|
|
@@ -1377,7 +1413,7 @@ function createProjectContext(opts) {
|
|
|
1377
1413
|
}
|
|
1378
1414
|
if (!dangerouslySkipPermissions) {
|
|
1379
1415
|
sm.currentPermissionMode = msg.mode;
|
|
1380
|
-
var session =
|
|
1416
|
+
var session = getSessionForWs(ws);
|
|
1381
1417
|
if (session) {
|
|
1382
1418
|
sdk.setPermissionMode(session, msg.mode);
|
|
1383
1419
|
}
|
|
@@ -1417,7 +1453,7 @@ function createProjectContext(opts) {
|
|
|
1417
1453
|
}
|
|
1418
1454
|
|
|
1419
1455
|
if (msg.type === "rewind_preview") {
|
|
1420
|
-
var session =
|
|
1456
|
+
var session = getSessionForWs(ws);
|
|
1421
1457
|
if (!session || !session.cliSessionId || !msg.uuid) return;
|
|
1422
1458
|
|
|
1423
1459
|
(async function () {
|
|
@@ -1446,7 +1482,7 @@ function createProjectContext(opts) {
|
|
|
1446
1482
|
}
|
|
1447
1483
|
|
|
1448
1484
|
if (msg.type === "rewind_execute") {
|
|
1449
|
-
var session =
|
|
1485
|
+
var session = getSessionForWs(ws);
|
|
1450
1486
|
if (!session || !session.cliSessionId || !msg.uuid) return;
|
|
1451
1487
|
var mode = msg.mode || "both";
|
|
1452
1488
|
|
|
@@ -1501,11 +1537,11 @@ function createProjectContext(opts) {
|
|
|
1501
1537
|
onProcessingChanged();
|
|
1502
1538
|
|
|
1503
1539
|
sm.saveSessionFile(session);
|
|
1504
|
-
sm.switchSession(session.localId,
|
|
1540
|
+
sm.switchSession(session.localId, ws);
|
|
1505
1541
|
sm.sendAndRecord(session, { type: "rewind_complete", mode: mode });
|
|
1506
1542
|
sm.broadcastSessionList();
|
|
1507
1543
|
} catch (err) {
|
|
1508
|
-
|
|
1544
|
+
sendTo(ws, { type: "rewind_error", text: "Rewind failed: " + err.message });
|
|
1509
1545
|
} finally {
|
|
1510
1546
|
if (result && result.isTemp) result.cleanup();
|
|
1511
1547
|
}
|
|
@@ -1514,7 +1550,7 @@ function createProjectContext(opts) {
|
|
|
1514
1550
|
}
|
|
1515
1551
|
|
|
1516
1552
|
if (msg.type === "ask_user_response") {
|
|
1517
|
-
var session =
|
|
1553
|
+
var session = getSessionForWs(ws);
|
|
1518
1554
|
if (!session) return;
|
|
1519
1555
|
var toolId = msg.toolId;
|
|
1520
1556
|
var answers = msg.answers || {};
|
|
@@ -1530,12 +1566,12 @@ function createProjectContext(opts) {
|
|
|
1530
1566
|
}
|
|
1531
1567
|
|
|
1532
1568
|
if (msg.type === "input_sync") {
|
|
1533
|
-
|
|
1569
|
+
sendToSessionOthers(ws, ws._clayActiveSession, msg);
|
|
1534
1570
|
return;
|
|
1535
1571
|
}
|
|
1536
1572
|
|
|
1537
1573
|
if (msg.type === "permission_response") {
|
|
1538
|
-
var session =
|
|
1574
|
+
var session = getSessionForWs(ws);
|
|
1539
1575
|
if (!session) return;
|
|
1540
1576
|
var requestId = msg.requestId;
|
|
1541
1577
|
var decision = msg.decision;
|
|
@@ -1603,17 +1639,17 @@ function createProjectContext(opts) {
|
|
|
1603
1639
|
newSession.title = "Plan execution (cleared context)";
|
|
1604
1640
|
sm.saveSessionFile(newSession);
|
|
1605
1641
|
sm.broadcastSessionList();
|
|
1606
|
-
|
|
1642
|
+
sendToSession(newSession.localId, userMsg);
|
|
1607
1643
|
|
|
1608
1644
|
newSession.isProcessing = true;
|
|
1609
1645
|
onProcessingChanged();
|
|
1610
1646
|
newSession.sentToolResults = {};
|
|
1611
|
-
|
|
1647
|
+
sendToSession(newSession.localId, { type: "status", status: "processing" });
|
|
1612
1648
|
newSession.acceptEditsAfterStart = true;
|
|
1613
1649
|
sdk.startQuery(newSession, planPrompt);
|
|
1614
1650
|
} catch (e) {
|
|
1615
1651
|
console.error("[project] Error starting plan execution:", e);
|
|
1616
|
-
|
|
1652
|
+
sendTo(ws, { type: "error", text: "Failed to start plan execution: " + (e.message || e) });
|
|
1617
1653
|
}
|
|
1618
1654
|
});
|
|
1619
1655
|
return;
|
|
@@ -1631,13 +1667,13 @@ function createProjectContext(opts) {
|
|
|
1631
1667
|
var userMsg = { type: "user_message", text: feedback };
|
|
1632
1668
|
session.history.push(userMsg);
|
|
1633
1669
|
sm.appendToSessionFile(session, userMsg);
|
|
1634
|
-
|
|
1670
|
+
sendToSession(session.localId, userMsg);
|
|
1635
1671
|
|
|
1636
1672
|
if (!session.isProcessing) {
|
|
1637
1673
|
session.isProcessing = true;
|
|
1638
1674
|
onProcessingChanged();
|
|
1639
1675
|
session.sentToolResults = {};
|
|
1640
|
-
|
|
1676
|
+
sendToSession(session.localId, { type: "status", status: "processing" });
|
|
1641
1677
|
if (!session.queryInstance) {
|
|
1642
1678
|
sdk.startQuery(session, feedback);
|
|
1643
1679
|
} else {
|
|
@@ -2406,11 +2442,11 @@ function createProjectContext(opts) {
|
|
|
2406
2442
|
// Send crafting prompt and start the conversation with Claude.
|
|
2407
2443
|
craftingSession.history.push({ type: "user_message", text: craftingPrompt });
|
|
2408
2444
|
sm.appendToSessionFile(craftingSession, { type: "user_message", text: craftingPrompt });
|
|
2409
|
-
|
|
2445
|
+
sendToSession(craftingSession.localId, { type: "user_message", text: craftingPrompt });
|
|
2410
2446
|
craftingSession.isProcessing = true;
|
|
2411
2447
|
onProcessingChanged();
|
|
2412
2448
|
craftingSession.sentToolResults = {};
|
|
2413
|
-
|
|
2449
|
+
sendToSession(craftingSession.localId, { type: "status", status: "processing" });
|
|
2414
2450
|
sdk.startQuery(craftingSession, craftingPrompt);
|
|
2415
2451
|
|
|
2416
2452
|
send({ type: "ralph_crafting_started", sessionId: craftingSession.localId, taskId: newLoopId, source: recordSource });
|
|
@@ -2600,7 +2636,7 @@ function createProjectContext(opts) {
|
|
|
2600
2636
|
if (msg.type !== "message") return;
|
|
2601
2637
|
if (!msg.text && (!msg.images || msg.images.length === 0) && (!msg.pastes || msg.pastes.length === 0)) return;
|
|
2602
2638
|
|
|
2603
|
-
var session =
|
|
2639
|
+
var session = getSessionForWs(ws);
|
|
2604
2640
|
if (!session) return;
|
|
2605
2641
|
|
|
2606
2642
|
var userMsg = { type: "user_message", text: msg.text || "" };
|
|
@@ -2612,7 +2648,7 @@ function createProjectContext(opts) {
|
|
|
2612
2648
|
}
|
|
2613
2649
|
session.history.push(userMsg);
|
|
2614
2650
|
sm.appendToSessionFile(session, userMsg);
|
|
2615
|
-
|
|
2651
|
+
sendToSessionOthers(ws, session.localId, userMsg);
|
|
2616
2652
|
|
|
2617
2653
|
if (!session.title) {
|
|
2618
2654
|
session.title = (msg.text || "Image").substring(0, 50);
|
|
@@ -2632,7 +2668,7 @@ function createProjectContext(opts) {
|
|
|
2632
2668
|
session.isProcessing = true;
|
|
2633
2669
|
onProcessingChanged();
|
|
2634
2670
|
session.sentToolResults = {};
|
|
2635
|
-
|
|
2671
|
+
sendToSession(session.localId, { type: "status", status: "processing" });
|
|
2636
2672
|
if (!session.queryInstance) {
|
|
2637
2673
|
sdk.startQuery(session, fullText, msg.images);
|
|
2638
2674
|
} else {
|
|
@@ -2845,7 +2881,8 @@ function createProjectContext(opts) {
|
|
|
2845
2881
|
return;
|
|
2846
2882
|
}
|
|
2847
2883
|
var spawnCwd = scope === "global" ? os.homedir() : cwd;
|
|
2848
|
-
var
|
|
2884
|
+
var scopeFlag = scope === "global" ? "--global" : "--project";
|
|
2885
|
+
var child = spawn("npx", ["skills", "add", url, "--skill", skill, "--yes", scopeFlag], {
|
|
2849
2886
|
cwd: spawnCwd,
|
|
2850
2887
|
stdio: "ignore",
|
|
2851
2888
|
detached: false,
|
|
@@ -3116,29 +3153,6 @@ function createProjectContext(opts) {
|
|
|
3116
3153
|
},
|
|
3117
3154
|
warmup: function () {
|
|
3118
3155
|
sdk.warmup();
|
|
3119
|
-
// Auto-install clay-ralph skill globally if not present
|
|
3120
|
-
var clayRalphDir = path.join(os.homedir(), ".claude", "skills", "clay-ralph", "SKILL.md");
|
|
3121
|
-
try {
|
|
3122
|
-
fs.accessSync(clayRalphDir, fs.constants.R_OK);
|
|
3123
|
-
} catch (e) {
|
|
3124
|
-
console.log("[project] Auto-installing clay-ralph skill...");
|
|
3125
|
-
var child = spawn("npx", ["skills", "add", "https://github.com/chadbyte/clay-ralph", "--skill", "clay-ralph"], {
|
|
3126
|
-
cwd: os.homedir(),
|
|
3127
|
-
stdio: "ignore",
|
|
3128
|
-
detached: false,
|
|
3129
|
-
});
|
|
3130
|
-
child.on("close", function (code) {
|
|
3131
|
-
if (code === 0) {
|
|
3132
|
-
console.log("[project] clay-ralph skill installed successfully");
|
|
3133
|
-
send({ type: "skill_installed", skill: "clay-ralph", scope: "global", success: true, error: null });
|
|
3134
|
-
} else {
|
|
3135
|
-
console.log("[project] clay-ralph skill install failed (code " + code + ")");
|
|
3136
|
-
}
|
|
3137
|
-
});
|
|
3138
|
-
child.on("error", function (err) {
|
|
3139
|
-
console.log("[project] clay-ralph skill install error: " + err.message);
|
|
3140
|
-
});
|
|
3141
|
-
}
|
|
3142
3156
|
},
|
|
3143
3157
|
destroy: destroy,
|
|
3144
3158
|
};
|