clay-server 2.13.0-beta.2 → 2.13.0-beta.4
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 +48 -0
- package/lib/daemon.js +16 -3
- package/lib/notes.js +1 -1
- package/lib/project.js +275 -7
- package/lib/public/app.js +250 -34
- package/lib/public/css/admin.css +71 -0
- package/lib/public/css/command-palette.css +319 -0
- package/lib/public/css/icon-strip.css +19 -3
- package/lib/public/css/input.css +2 -1
- package/lib/public/css/loop.css +26 -0
- package/lib/public/css/mates.css +73 -0
- package/lib/public/css/overlays.css +3 -0
- package/lib/public/css/scheduler.css +29 -5
- package/lib/public/css/sidebar.css +28 -6
- package/lib/public/css/sticky-notes.css +61 -12
- package/lib/public/css/title-bar.css +24 -30
- package/lib/public/index.html +48 -13
- package/lib/public/modules/admin.js +109 -1
- package/lib/public/modules/command-palette.js +549 -0
- package/lib/public/modules/input.js +10 -2
- package/lib/public/modules/mate-wizard.js +17 -6
- package/lib/public/modules/scheduler.js +56 -64
- package/lib/public/modules/session-search.js +6 -2
- package/lib/public/modules/sidebar.js +128 -72
- package/lib/public/modules/sticky-notes.js +37 -0
- package/lib/public/style.css +1 -0
- package/lib/scheduler.js +4 -0
- package/lib/sdk-bridge.js +10 -8
- package/lib/server.js +334 -7
- package/lib/sessions.js +5 -0
- package/lib/users.js +64 -0
- package/lib/worktree.js +2 -2
- package/package.json +1 -1
package/lib/public/app.js
CHANGED
|
@@ -25,6 +25,7 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
|
|
|
25
25
|
import { initSessionSearch, toggleSearch, closeSearch, isSearchOpen, handleFindInSessionResults, onHistoryPrepended as onSessionSearchHistoryPrepended } from './modules/session-search.js';
|
|
26
26
|
import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
27
27
|
import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } from './modules/mate-wizard.js';
|
|
28
|
+
import { initCommandPalette, handlePaletteSessionSwitch, setPaletteVersion } from './modules/command-palette.js';
|
|
28
29
|
|
|
29
30
|
// --- Base path for multi-project routing ---
|
|
30
31
|
var slugMatch = location.pathname.match(/^\/p\/([a-z0-9_-]+)/);
|
|
@@ -1133,6 +1134,7 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
1133
1134
|
isProcessing: p.isProcessing,
|
|
1134
1135
|
onlineUsers: p.onlineUsers || [],
|
|
1135
1136
|
unread: p.unread || 0,
|
|
1137
|
+
pendingPermissions: p.pendingPermissions || 0,
|
|
1136
1138
|
isWorktree: p.isWorktree || false,
|
|
1137
1139
|
parentSlug: p.parentSlug || null,
|
|
1138
1140
|
branch: p.branch || null,
|
|
@@ -1245,7 +1247,9 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
1245
1247
|
var loopMaxIterations = 0;
|
|
1246
1248
|
var ralphPhase = "idle"; // idle | wizard | crafting | approval | executing | done
|
|
1247
1249
|
var ralphCraftingSessionId = null;
|
|
1250
|
+
var ralphCraftingSource = null; // "ralph" or null (task)
|
|
1248
1251
|
var wizardStep = 1;
|
|
1252
|
+
var wizardSource = "ralph"; // "ralph" or "task"
|
|
1249
1253
|
var wizardData = { name: "", task: "", maxIterations: 3, cron: null };
|
|
1250
1254
|
var ralphFilesReady = { promptReady: false, judgeReady: false, bothReady: false };
|
|
1251
1255
|
var ralphPreviewContent = { prompt: "", judge: "" };
|
|
@@ -1473,7 +1477,7 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
1473
1477
|
switchProject: function (slug) { switchProject(slug); },
|
|
1474
1478
|
openTerminal: function () { openTerminal(); },
|
|
1475
1479
|
showHomeHub: function () { showHomeHub(); },
|
|
1476
|
-
openRalphWizard: function () { openRalphWizard(); },
|
|
1480
|
+
openRalphWizard: function (source) { openRalphWizard(source); },
|
|
1477
1481
|
getUpcomingSchedules: getUpcomingSchedules,
|
|
1478
1482
|
get multiUser() { return isMultiUserMode; },
|
|
1479
1483
|
get myUserId() { return myUserId; },
|
|
@@ -1484,6 +1488,7 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
1484
1488
|
sendWs: function (msg) { if (ws && ws.readyState === 1) ws.send(JSON.stringify(msg)); },
|
|
1485
1489
|
onDmRemoveUser: function (userId) { dmRemovedUsers[userId] = true; },
|
|
1486
1490
|
getHistoryFrom: function () { return historyFrom; },
|
|
1491
|
+
get permissions() { return myPermissions; },
|
|
1487
1492
|
};
|
|
1488
1493
|
initSidebar(sidebarCtx);
|
|
1489
1494
|
initIconStrip(sidebarCtx);
|
|
@@ -1494,6 +1499,32 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
1494
1499
|
function (mate) { handleMateCreatedInApp(mate); }
|
|
1495
1500
|
);
|
|
1496
1501
|
|
|
1502
|
+
initCommandPalette({
|
|
1503
|
+
switchProject: function (slug) { switchProject(slug); },
|
|
1504
|
+
currentSlug: function () { return currentSlug; },
|
|
1505
|
+
projectList: function () { return cachedProjects || []; },
|
|
1506
|
+
matesList: function () { return cachedMatesList || []; },
|
|
1507
|
+
allUsers: function () { return cachedAllUsers || []; },
|
|
1508
|
+
dmConversations: function () { return cachedDmConversations || []; },
|
|
1509
|
+
myUserId: function () { return myUserId; },
|
|
1510
|
+
selectSession: function (id) {
|
|
1511
|
+
if (ws && ws.readyState === 1) {
|
|
1512
|
+
ws.send(JSON.stringify({ type: "switch_session", id: id }));
|
|
1513
|
+
}
|
|
1514
|
+
},
|
|
1515
|
+
openDm: function (userId) { openDm(userId); },
|
|
1516
|
+
runAction: function (action) {
|
|
1517
|
+
switch (action) {
|
|
1518
|
+
case "createMate": openMateWizard(); break;
|
|
1519
|
+
case "openSettings":
|
|
1520
|
+
var sb = document.getElementById("server-settings-btn");
|
|
1521
|
+
if (sb) sb.click();
|
|
1522
|
+
break;
|
|
1523
|
+
case "showHome": showHomeHub(); break;
|
|
1524
|
+
}
|
|
1525
|
+
},
|
|
1526
|
+
});
|
|
1527
|
+
|
|
1497
1528
|
// --- Connect overlay (animated ASCII logo) ---
|
|
1498
1529
|
var asciiLogoCanvas = $("ascii-logo-canvas");
|
|
1499
1530
|
initAsciiLogo(asciiLogoCanvas);
|
|
@@ -3109,7 +3140,9 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
3109
3140
|
disconnectNotifTimer = null;
|
|
3110
3141
|
}
|
|
3111
3142
|
// Only show "restored" notification if "lost" was actually shown
|
|
3112
|
-
|
|
3143
|
+
var isMobileDevice = /Mobi|Android|iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
|
3144
|
+
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
|
|
3145
|
+
if (wasConnected && disconnectNotifShown && !isMobileDevice && isNotifAlertEnabled() && !document.hasFocus() && "serviceWorker" in navigator) {
|
|
3113
3146
|
navigator.serviceWorker.ready.then(function (reg) {
|
|
3114
3147
|
reg.showNotification("Clay", {
|
|
3115
3148
|
body: "Server connection restored",
|
|
@@ -3159,7 +3192,9 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
3159
3192
|
disconnectNotifTimer = setTimeout(function () {
|
|
3160
3193
|
disconnectNotifTimer = null;
|
|
3161
3194
|
disconnectNotifShown = true;
|
|
3162
|
-
|
|
3195
|
+
var isMobileDevice = /Mobi|Android|iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
|
3196
|
+
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
|
|
3197
|
+
if (!isMobileDevice && isNotifAlertEnabled() && !document.hasFocus() && "serviceWorker" in navigator) {
|
|
3163
3198
|
navigator.serviceWorker.ready.then(function (reg) {
|
|
3164
3199
|
reg.showNotification("Clay", {
|
|
3165
3200
|
body: "Server connection lost",
|
|
@@ -3260,8 +3295,7 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
3260
3295
|
if (tbProjectName) tbProjectName.textContent = msg.title || projectName;
|
|
3261
3296
|
updatePageTitle();
|
|
3262
3297
|
if (msg.version) {
|
|
3263
|
-
|
|
3264
|
-
if (vEl) vEl.textContent = "v" + msg.version;
|
|
3298
|
+
setPaletteVersion(msg.version);
|
|
3265
3299
|
}
|
|
3266
3300
|
if (msg.projectOwnerId !== undefined) currentProjectOwnerId = msg.projectOwnerId;
|
|
3267
3301
|
if (msg.osUsers !== undefined) isOsUsers = !!msg.osUsers;
|
|
@@ -3429,6 +3463,7 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
3429
3463
|
|
|
3430
3464
|
case "session_list":
|
|
3431
3465
|
renderSessionList(msg.sessions || []);
|
|
3466
|
+
handlePaletteSessionSwitch();
|
|
3432
3467
|
break;
|
|
3433
3468
|
|
|
3434
3469
|
case "session_presence":
|
|
@@ -4168,6 +4203,7 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
4168
4203
|
case "ralph_phase":
|
|
4169
4204
|
ralphPhase = msg.phase || "idle";
|
|
4170
4205
|
if (msg.craftingSessionId) ralphCraftingSessionId = msg.craftingSessionId;
|
|
4206
|
+
if (msg.source !== undefined) ralphCraftingSource = msg.source;
|
|
4171
4207
|
updateLoopButton();
|
|
4172
4208
|
updateRalphBars();
|
|
4173
4209
|
break;
|
|
@@ -4175,6 +4211,7 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
4175
4211
|
case "ralph_crafting_started":
|
|
4176
4212
|
ralphPhase = "crafting";
|
|
4177
4213
|
ralphCraftingSessionId = msg.sessionId || activeSessionId;
|
|
4214
|
+
ralphCraftingSource = msg.source || null;
|
|
4178
4215
|
updateLoopButton();
|
|
4179
4216
|
updateRalphBars();
|
|
4180
4217
|
if (msg.source !== "ralph") {
|
|
@@ -4192,7 +4229,7 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
4192
4229
|
};
|
|
4193
4230
|
if (msg.bothReady && (ralphPhase === "crafting" || ralphPhase === "approval")) {
|
|
4194
4231
|
ralphPhase = "approval";
|
|
4195
|
-
if (isSchedulerOpen()) {
|
|
4232
|
+
if (ralphCraftingSource !== "ralph" || isSchedulerOpen()) {
|
|
4196
4233
|
// Task crafting in scheduler: switch from crafting chat to detail view showing files
|
|
4197
4234
|
exitCraftingMode(msg.taskId);
|
|
4198
4235
|
} else {
|
|
@@ -4495,11 +4532,40 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
4495
4532
|
initAdmin({
|
|
4496
4533
|
get projectList() { return cachedProjects; },
|
|
4497
4534
|
});
|
|
4535
|
+
var myPermissions = null; // null = single-user, all allowed
|
|
4498
4536
|
fetch("/api/me").then(function (r) { return r.json(); }).then(function (d) {
|
|
4499
4537
|
if (d.multiUser) isMultiUserMode = true;
|
|
4500
4538
|
if (d.user && d.user.id) myUserId = d.user.id;
|
|
4539
|
+
if (d.permissions) myPermissions = d.permissions;
|
|
4501
4540
|
if (d.mustChangePin) showForceChangePinOverlay();
|
|
4502
4541
|
initCursorToggle();
|
|
4542
|
+
// Apply RBAC UI gating
|
|
4543
|
+
if (myPermissions) {
|
|
4544
|
+
if (!myPermissions.terminal) {
|
|
4545
|
+
var termBtn = document.getElementById("terminal-toggle-btn");
|
|
4546
|
+
if (termBtn) termBtn.style.display = "none";
|
|
4547
|
+
var termSideBtn = document.getElementById("terminal-sidebar-btn");
|
|
4548
|
+
if (termSideBtn) termSideBtn.style.display = "none";
|
|
4549
|
+
}
|
|
4550
|
+
if (!myPermissions.fileBrowser) {
|
|
4551
|
+
var fbBtn = document.getElementById("file-browser-btn");
|
|
4552
|
+
if (fbBtn) fbBtn.style.display = "none";
|
|
4553
|
+
}
|
|
4554
|
+
if (!myPermissions.skills) {
|
|
4555
|
+
var sBtn = document.getElementById("skills-btn");
|
|
4556
|
+
if (sBtn) sBtn.style.display = "none";
|
|
4557
|
+
var msBtn = document.getElementById("mate-skills-btn");
|
|
4558
|
+
if (msBtn) msBtn.style.display = "none";
|
|
4559
|
+
}
|
|
4560
|
+
if (!myPermissions.scheduledTasks) {
|
|
4561
|
+
var schBtn = document.getElementById("scheduler-btn");
|
|
4562
|
+
if (schBtn) schBtn.style.display = "none";
|
|
4563
|
+
}
|
|
4564
|
+
if (!myPermissions.createProject) {
|
|
4565
|
+
var addProjBtn = document.getElementById("icon-strip-add");
|
|
4566
|
+
if (addProjBtn) addProjBtn.style.display = "none";
|
|
4567
|
+
}
|
|
4568
|
+
}
|
|
4503
4569
|
}).catch(function () {});
|
|
4504
4570
|
// Hide server settings and update controls for non-admin users in multi-user mode
|
|
4505
4571
|
checkAdminAccess().then(function (isAdmin) {
|
|
@@ -4799,19 +4865,28 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
4799
4865
|
}
|
|
4800
4866
|
|
|
4801
4867
|
function updateRalphBars() {
|
|
4868
|
+
// Task source uses the scheduler panel, not the sticky bar
|
|
4869
|
+
var isTaskSource = ralphCraftingSource !== "ralph";
|
|
4802
4870
|
var onCraftingSession = ralphCraftingSessionId && activeSessionId === ralphCraftingSessionId;
|
|
4803
4871
|
// If approval phase but no craftingSessionId (recovered after server restart), show bar anyway
|
|
4804
4872
|
var recoveredApproval = ralphPhase === "approval" && !ralphCraftingSessionId;
|
|
4805
|
-
if (ralphPhase === "crafting" && onCraftingSession) {
|
|
4873
|
+
if (!isTaskSource && ralphPhase === "crafting" && onCraftingSession) {
|
|
4806
4874
|
showRalphCraftingBar(true);
|
|
4807
4875
|
} else {
|
|
4808
4876
|
showRalphCraftingBar(false);
|
|
4809
4877
|
}
|
|
4810
|
-
if (ralphPhase === "approval" && (onCraftingSession || recoveredApproval)) {
|
|
4878
|
+
if (!isTaskSource && ralphPhase === "approval" && (onCraftingSession || recoveredApproval)) {
|
|
4811
4879
|
showRalphApprovalBar(true);
|
|
4812
4880
|
} else {
|
|
4813
4881
|
showRalphApprovalBar(false);
|
|
4814
4882
|
}
|
|
4883
|
+
// Restore running loop banner on session switch
|
|
4884
|
+
if (loopActive && ralphPhase === "executing") {
|
|
4885
|
+
showLoopBanner(true);
|
|
4886
|
+
if (loopIteration > 0) {
|
|
4887
|
+
updateLoopBanner(loopIteration, loopMaxIterations, "running");
|
|
4888
|
+
}
|
|
4889
|
+
}
|
|
4815
4890
|
}
|
|
4816
4891
|
|
|
4817
4892
|
// --- Skill install dialog (generic) ---
|
|
@@ -4829,18 +4904,35 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
4829
4904
|
var knownInstalledSkills = {}; // client-side cache of installed skills
|
|
4830
4905
|
|
|
4831
4906
|
function renderSkillInstallDialog(opts, missing) {
|
|
4832
|
-
|
|
4833
|
-
|
|
4907
|
+
var hasOutdated = false;
|
|
4908
|
+
var hasMissing = false;
|
|
4909
|
+
for (var c = 0; c < missing.length; c++) {
|
|
4910
|
+
if (missing[c].status === "outdated") hasOutdated = true;
|
|
4911
|
+
else hasMissing = true;
|
|
4912
|
+
}
|
|
4913
|
+
var defaultTitle = hasMissing ? "Skill Installation Required" : "Skill Update Available";
|
|
4914
|
+
var defaultReason = hasMissing
|
|
4915
|
+
? "This feature requires the following skill(s) to be installed."
|
|
4916
|
+
: "Newer versions of the following skill(s) are available.";
|
|
4917
|
+
if (hasMissing && hasOutdated) {
|
|
4918
|
+
defaultTitle = "Skill Installation / Update Required";
|
|
4919
|
+
defaultReason = "Some skills need to be installed or updated.";
|
|
4920
|
+
}
|
|
4921
|
+
skillInstallTitle.textContent = opts.title || defaultTitle;
|
|
4922
|
+
skillInstallReason.textContent = opts.reason || defaultReason;
|
|
4834
4923
|
skillInstallList.innerHTML = "";
|
|
4835
4924
|
for (var i = 0; i < missing.length; i++) {
|
|
4836
4925
|
var s = missing[i];
|
|
4926
|
+
var badge = s.status === "outdated"
|
|
4927
|
+
? '<span class="skill-badge skill-badge-update">Update ' + escapeHtml(s.installedVersion || "") + ' → ' + escapeHtml(s.remoteVersion || "") + '</span>'
|
|
4928
|
+
: '<span class="skill-badge skill-badge-new">New</span>';
|
|
4837
4929
|
var item = document.createElement("div");
|
|
4838
4930
|
item.className = "skill-install-item";
|
|
4839
4931
|
item.setAttribute("data-skill", s.name);
|
|
4840
4932
|
item.innerHTML = '<span class="skill-icon">🧩</span>' +
|
|
4841
4933
|
'<div class="skill-info">' +
|
|
4842
4934
|
'<span class="skill-name">' + escapeHtml(s.name) + '</span>' +
|
|
4843
|
-
|
|
4935
|
+
badge +
|
|
4844
4936
|
'</div>' +
|
|
4845
4937
|
'<span class="skill-status"></span>';
|
|
4846
4938
|
skillInstallList.appendChild(item);
|
|
@@ -4848,7 +4940,9 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
4848
4940
|
skillInstallStatus.classList.add("hidden");
|
|
4849
4941
|
skillInstallStatus.innerHTML = "";
|
|
4850
4942
|
skillInstallOk.disabled = false;
|
|
4851
|
-
|
|
4943
|
+
var btnLabel = hasMissing ? "Install" : "Update";
|
|
4944
|
+
if (hasMissing && hasOutdated) btnLabel = "Install / Update";
|
|
4945
|
+
skillInstallOk.textContent = btnLabel;
|
|
4852
4946
|
skillInstallOk.className = "confirm-btn confirm-delete";
|
|
4853
4947
|
skillInstallModal.classList.remove("hidden");
|
|
4854
4948
|
}
|
|
@@ -4899,7 +4993,12 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
4899
4993
|
});
|
|
4900
4994
|
|
|
4901
4995
|
function updateSkillInstallProgress(done, total) {
|
|
4902
|
-
|
|
4996
|
+
var hasUpdates = false;
|
|
4997
|
+
for (var u = 0; u < pendingSkillInstalls.length; u++) {
|
|
4998
|
+
if (pendingSkillInstalls[u].status === "outdated") { hasUpdates = true; break; }
|
|
4999
|
+
}
|
|
5000
|
+
var label = hasUpdates ? "Updating" : "Installing";
|
|
5001
|
+
skillInstallStatus.innerHTML = '<div class="skills-spinner small"></div> ' + label + ' skills... (' + done + '/' + total + ')';
|
|
4903
5002
|
}
|
|
4904
5003
|
|
|
4905
5004
|
function updateSkillListItems() {
|
|
@@ -4949,7 +5048,12 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
4949
5048
|
|
|
4950
5049
|
if (doneCount === totalCount) {
|
|
4951
5050
|
skillInstallDone = true;
|
|
4952
|
-
|
|
5051
|
+
var hasUpdates = false;
|
|
5052
|
+
for (var u = 0; u < pendingSkillInstalls.length; u++) {
|
|
5053
|
+
if (pendingSkillInstalls[u].status === "outdated") { hasUpdates = true; break; }
|
|
5054
|
+
}
|
|
5055
|
+
var doneMsg = hasUpdates ? "All skills updated successfully." : "All skills installed successfully.";
|
|
5056
|
+
skillInstallStatus.innerHTML = '<span class="skill-status-ok">' + iconHtml("circle-check") + '</span> ' + doneMsg;
|
|
4953
5057
|
refreshIcons();
|
|
4954
5058
|
skillInstallOk.disabled = false;
|
|
4955
5059
|
skillInstallOk.textContent = "Proceed";
|
|
@@ -4958,21 +5062,39 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
4958
5062
|
}
|
|
4959
5063
|
|
|
4960
5064
|
function requireSkills(opts, cb) {
|
|
4961
|
-
fetch(basePath + "api/
|
|
5065
|
+
fetch(basePath + "api/check-skill-updates", {
|
|
5066
|
+
method: "POST",
|
|
5067
|
+
headers: { "Content-Type": "application/json" },
|
|
5068
|
+
body: JSON.stringify({ skills: opts.skills }),
|
|
5069
|
+
})
|
|
4962
5070
|
.then(function (res) { return res.json(); })
|
|
4963
5071
|
.then(function (data) {
|
|
4964
|
-
var
|
|
4965
|
-
var
|
|
4966
|
-
for (var i = 0; i <
|
|
4967
|
-
var
|
|
4968
|
-
if (
|
|
4969
|
-
|
|
5072
|
+
var results = data.results || [];
|
|
5073
|
+
var actionable = [];
|
|
5074
|
+
for (var i = 0; i < results.length; i++) {
|
|
5075
|
+
var r = results[i];
|
|
5076
|
+
if (r.status === "missing" || r.status === "outdated") {
|
|
5077
|
+
// Find the original skill entry for url/scope
|
|
5078
|
+
var orig = null;
|
|
5079
|
+
for (var j = 0; j < opts.skills.length; j++) {
|
|
5080
|
+
if (opts.skills[j].name === r.name) { orig = opts.skills[j]; break; }
|
|
5081
|
+
}
|
|
5082
|
+
if (!orig) continue;
|
|
5083
|
+
actionable.push({
|
|
5084
|
+
name: r.name,
|
|
5085
|
+
url: orig.url,
|
|
5086
|
+
scope: orig.scope || "global",
|
|
5087
|
+
installed: false,
|
|
5088
|
+
status: r.status,
|
|
5089
|
+
installedVersion: r.installedVersion,
|
|
5090
|
+
remoteVersion: r.remoteVersion,
|
|
5091
|
+
});
|
|
4970
5092
|
}
|
|
4971
5093
|
}
|
|
4972
|
-
if (
|
|
4973
|
-
pendingSkillInstalls =
|
|
5094
|
+
if (actionable.length === 0) { cb(); return; }
|
|
5095
|
+
pendingSkillInstalls = actionable;
|
|
4974
5096
|
skillInstallCallback = cb;
|
|
4975
|
-
renderSkillInstallDialog(opts,
|
|
5097
|
+
renderSkillInstallDialog(opts, actionable);
|
|
4976
5098
|
})
|
|
4977
5099
|
.catch(function () { cb(); });
|
|
4978
5100
|
}
|
|
@@ -4995,18 +5117,56 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
4995
5117
|
|
|
4996
5118
|
// --- Ralph Wizard ---
|
|
4997
5119
|
|
|
4998
|
-
|
|
5120
|
+
var wizardMode = "draft"; // "draft" or "own"
|
|
5121
|
+
|
|
5122
|
+
function openRalphWizard(source) {
|
|
4999
5123
|
requireClayRalph(function () {
|
|
5124
|
+
wizardSource = source || "ralph";
|
|
5000
5125
|
wizardData = { name: "", task: "", maxIterations: 3 };
|
|
5001
5126
|
var el = document.getElementById("ralph-wizard");
|
|
5002
5127
|
if (!el) return;
|
|
5003
5128
|
|
|
5004
5129
|
var taskEl = document.getElementById("ralph-task");
|
|
5005
5130
|
if (taskEl) taskEl.value = "";
|
|
5131
|
+
var promptInput = document.getElementById("ralph-prompt-input");
|
|
5132
|
+
if (promptInput) promptInput.value = "";
|
|
5133
|
+
var judgeInput = document.getElementById("ralph-judge-input");
|
|
5134
|
+
if (judgeInput) judgeInput.value = "";
|
|
5006
5135
|
var iterEl = document.getElementById("ralph-max-iterations");
|
|
5007
5136
|
if (iterEl) iterEl.value = "25";
|
|
5008
5137
|
|
|
5009
|
-
|
|
5138
|
+
// Update text based on source
|
|
5139
|
+
var isTask = wizardSource === "task";
|
|
5140
|
+
var headerSpan = el.querySelector(".ralph-wizard-header > span");
|
|
5141
|
+
if (headerSpan) headerSpan.textContent = isTask ? "New Task" : "New Ralph Loop";
|
|
5142
|
+
|
|
5143
|
+
var step2heading = el.querySelector('.ralph-step[data-step="2"] h3');
|
|
5144
|
+
if (step2heading) step2heading.textContent = isTask ? "Describe your task" : "What do you want to build?";
|
|
5145
|
+
|
|
5146
|
+
var draftHint = el.querySelector('.ralph-mode-panel[data-mode="draft"] .ralph-hint');
|
|
5147
|
+
if (draftHint) draftHint.textContent = isTask
|
|
5148
|
+
? "Describe what you want done. Clay will craft a precise prompt and you can review it before scheduling."
|
|
5149
|
+
: "Write a rough idea, Clay will refine it into detailed instructions. You can review and edit everything before the loop starts.";
|
|
5150
|
+
|
|
5151
|
+
var ownHint = el.querySelector('.ralph-mode-panel[data-mode="own"] .ralph-hint');
|
|
5152
|
+
if (ownHint) ownHint.textContent = isTask
|
|
5153
|
+
? "Paste the prompt to run. It will execute as-is when triggered."
|
|
5154
|
+
: "Paste your PROMPT.md content. JUDGE.md is optional; if omitted, Clay will generate it for you.";
|
|
5155
|
+
|
|
5156
|
+
// Update task description placeholder
|
|
5157
|
+
if (taskEl) taskEl.placeholder = isTask
|
|
5158
|
+
? "e.g. Check for dependency updates and create a summary"
|
|
5159
|
+
: "e.g. Add dark mode toggle to the settings page";
|
|
5160
|
+
|
|
5161
|
+
wizardMode = "draft";
|
|
5162
|
+
updateWizardModeTabs();
|
|
5163
|
+
|
|
5164
|
+
if (wizardSource === "task") {
|
|
5165
|
+
// Tasks skip step 1 (Ralph intro), go directly to step 2
|
|
5166
|
+
wizardStep = 2;
|
|
5167
|
+
} else {
|
|
5168
|
+
wizardStep = 1;
|
|
5169
|
+
}
|
|
5010
5170
|
el.classList.remove("hidden");
|
|
5011
5171
|
var statusEl = document.getElementById("ralph-install-status");
|
|
5012
5172
|
if (statusEl) { statusEl.classList.add("hidden"); statusEl.innerHTML = ""; }
|
|
@@ -5014,6 +5174,25 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
5014
5174
|
});
|
|
5015
5175
|
}
|
|
5016
5176
|
|
|
5177
|
+
function updateWizardModeTabs() {
|
|
5178
|
+
var tabs = document.querySelectorAll(".ralph-mode-tab");
|
|
5179
|
+
var panels = document.querySelectorAll(".ralph-mode-panel");
|
|
5180
|
+
for (var i = 0; i < tabs.length; i++) {
|
|
5181
|
+
if (tabs[i].getAttribute("data-mode") === wizardMode) {
|
|
5182
|
+
tabs[i].classList.add("active");
|
|
5183
|
+
} else {
|
|
5184
|
+
tabs[i].classList.remove("active");
|
|
5185
|
+
}
|
|
5186
|
+
}
|
|
5187
|
+
for (var j = 0; j < panels.length; j++) {
|
|
5188
|
+
if (panels[j].getAttribute("data-mode") === wizardMode) {
|
|
5189
|
+
panels[j].classList.add("active");
|
|
5190
|
+
} else {
|
|
5191
|
+
panels[j].classList.remove("active");
|
|
5192
|
+
}
|
|
5193
|
+
}
|
|
5194
|
+
}
|
|
5195
|
+
|
|
5017
5196
|
function closeRalphWizard() {
|
|
5018
5197
|
var el = document.getElementById("ralph-wizard");
|
|
5019
5198
|
if (el) el.classList.add("hidden");
|
|
@@ -5040,18 +5219,33 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
5040
5219
|
var backBtn = document.getElementById("ralph-wizard-back");
|
|
5041
5220
|
var skipBtn = document.getElementById("ralph-wizard-skip");
|
|
5042
5221
|
var nextBtn = document.getElementById("ralph-wizard-next");
|
|
5043
|
-
if (backBtn)
|
|
5222
|
+
if (backBtn) {
|
|
5223
|
+
backBtn.style.visibility = (wizardStep === 1 && wizardSource !== "task") ? "hidden" : "visible";
|
|
5224
|
+
backBtn.textContent = (wizardSource === "task" && wizardStep <= 2) ? "Cancel" : "Back";
|
|
5225
|
+
}
|
|
5044
5226
|
if (skipBtn) skipBtn.style.display = "none";
|
|
5045
5227
|
if (nextBtn) nextBtn.textContent = wizardStep === 2 ? "Launch" : "Get Started";
|
|
5046
5228
|
}
|
|
5047
5229
|
|
|
5048
5230
|
function collectWizardData() {
|
|
5049
|
-
var taskEl = document.getElementById("ralph-task");
|
|
5050
5231
|
var iterEl = document.getElementById("ralph-max-iterations");
|
|
5051
5232
|
wizardData.name = "";
|
|
5052
|
-
wizardData.task = taskEl ? taskEl.value.trim() : "";
|
|
5053
5233
|
wizardData.maxIterations = iterEl ? parseInt(iterEl.value, 10) || 3 : 3;
|
|
5054
5234
|
wizardData.cron = null;
|
|
5235
|
+
wizardData.mode = wizardMode;
|
|
5236
|
+
|
|
5237
|
+
if (wizardMode === "draft") {
|
|
5238
|
+
var taskEl = document.getElementById("ralph-task");
|
|
5239
|
+
wizardData.task = taskEl ? taskEl.value.trim() : "";
|
|
5240
|
+
wizardData.promptText = null;
|
|
5241
|
+
wizardData.judgeText = null;
|
|
5242
|
+
} else {
|
|
5243
|
+
var promptInput = document.getElementById("ralph-prompt-input");
|
|
5244
|
+
var judgeInput = document.getElementById("ralph-judge-input");
|
|
5245
|
+
wizardData.task = "";
|
|
5246
|
+
wizardData.promptText = promptInput ? promptInput.value.trim() : "";
|
|
5247
|
+
wizardData.judgeText = judgeInput ? judgeInput.value.trim() : "";
|
|
5248
|
+
}
|
|
5055
5249
|
}
|
|
5056
5250
|
|
|
5057
5251
|
function buildWizardCron() {
|
|
@@ -5116,10 +5310,18 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
5116
5310
|
}
|
|
5117
5311
|
|
|
5118
5312
|
if (wizardStep === 2) {
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
if (
|
|
5122
|
-
|
|
5313
|
+
if (wizardMode === "draft") {
|
|
5314
|
+
var taskEl = document.getElementById("ralph-task");
|
|
5315
|
+
if (!wizardData.task) {
|
|
5316
|
+
if (taskEl) { taskEl.focus(); taskEl.style.borderColor = "#e74c3c"; setTimeout(function() { taskEl.style.borderColor = ""; }, 2000); }
|
|
5317
|
+
return;
|
|
5318
|
+
}
|
|
5319
|
+
} else {
|
|
5320
|
+
var promptInput = document.getElementById("ralph-prompt-input");
|
|
5321
|
+
if (!wizardData.promptText) {
|
|
5322
|
+
if (promptInput) { promptInput.focus(); promptInput.style.borderColor = "#e74c3c"; setTimeout(function() { promptInput.style.borderColor = ""; }, 2000); }
|
|
5323
|
+
return;
|
|
5324
|
+
}
|
|
5123
5325
|
}
|
|
5124
5326
|
wizardSubmit();
|
|
5125
5327
|
return;
|
|
@@ -5129,6 +5331,10 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
5129
5331
|
}
|
|
5130
5332
|
|
|
5131
5333
|
function wizardBack() {
|
|
5334
|
+
if (wizardSource === "task" && wizardStep <= 2) {
|
|
5335
|
+
closeRalphWizard();
|
|
5336
|
+
return;
|
|
5337
|
+
}
|
|
5132
5338
|
if (wizardStep > 1) {
|
|
5133
5339
|
collectWizardData();
|
|
5134
5340
|
wizardStep--;
|
|
@@ -5145,6 +5351,7 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
5145
5351
|
|
|
5146
5352
|
function wizardSubmit() {
|
|
5147
5353
|
collectWizardData();
|
|
5354
|
+
wizardData.source = wizardSource === "task" ? "task" : undefined;
|
|
5148
5355
|
closeRalphWizard();
|
|
5149
5356
|
if (ws && ws.readyState === 1) {
|
|
5150
5357
|
ws.send(JSON.stringify({ type: "ralph_wizard_complete", data: wizardData }));
|
|
@@ -5164,6 +5371,15 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
5164
5371
|
if (wizardSkipBtn) wizardSkipBtn.addEventListener("click", wizardSkip);
|
|
5165
5372
|
if (wizardNextBtn) wizardNextBtn.addEventListener("click", wizardNext);
|
|
5166
5373
|
|
|
5374
|
+
// Mode tab switching
|
|
5375
|
+
var modeTabs = document.querySelectorAll(".ralph-mode-tab");
|
|
5376
|
+
for (var mt = 0; mt < modeTabs.length; mt++) {
|
|
5377
|
+
modeTabs[mt].addEventListener("click", function () {
|
|
5378
|
+
wizardMode = this.getAttribute("data-mode");
|
|
5379
|
+
updateWizardModeTabs();
|
|
5380
|
+
});
|
|
5381
|
+
}
|
|
5382
|
+
|
|
5167
5383
|
// --- Repeat picker handlers ---
|
|
5168
5384
|
var repeatSelect = document.getElementById("ralph-repeat");
|
|
5169
5385
|
var repeatTimeRow = document.getElementById("ralph-time-row");
|
|
@@ -5450,7 +5666,7 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
5450
5666
|
get activeSessionId() { return activeSessionId; },
|
|
5451
5667
|
basePath: basePath,
|
|
5452
5668
|
currentSlug: currentSlug,
|
|
5453
|
-
openRalphWizard: function () { openRalphWizard(); },
|
|
5669
|
+
openRalphWizard: function (source) { openRalphWizard(source); },
|
|
5454
5670
|
requireClayRalph: function (cb) { requireClayRalph(cb); },
|
|
5455
5671
|
getProjects: function () { return cachedProjects; },
|
|
5456
5672
|
});
|
package/lib/public/css/admin.css
CHANGED
|
@@ -663,3 +663,74 @@
|
|
|
663
663
|
width: 12px;
|
|
664
664
|
height: 12px;
|
|
665
665
|
}
|
|
666
|
+
|
|
667
|
+
/* --- Permissions modal --- */
|
|
668
|
+
.admin-perms-list {
|
|
669
|
+
display: flex;
|
|
670
|
+
flex-direction: column;
|
|
671
|
+
gap: 2px;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
.admin-perm-row {
|
|
675
|
+
display: flex;
|
|
676
|
+
align-items: center;
|
|
677
|
+
justify-content: space-between;
|
|
678
|
+
padding: 10px 12px;
|
|
679
|
+
border-radius: 8px;
|
|
680
|
+
cursor: pointer;
|
|
681
|
+
transition: background 0.15s;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
.admin-perm-row:hover {
|
|
685
|
+
background: rgba(var(--overlay-rgb), 0.06);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
.admin-perm-info {
|
|
689
|
+
display: flex;
|
|
690
|
+
flex-direction: column;
|
|
691
|
+
gap: 2px;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
.admin-perm-label {
|
|
695
|
+
font-size: 14px;
|
|
696
|
+
font-weight: 600;
|
|
697
|
+
color: var(--text);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
.admin-perm-desc {
|
|
701
|
+
font-size: 12px;
|
|
702
|
+
color: var(--text-muted);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
.admin-perm-toggle {
|
|
706
|
+
width: 36px;
|
|
707
|
+
height: 20px;
|
|
708
|
+
appearance: none;
|
|
709
|
+
-webkit-appearance: none;
|
|
710
|
+
background: var(--border);
|
|
711
|
+
border-radius: 10px;
|
|
712
|
+
position: relative;
|
|
713
|
+
cursor: pointer;
|
|
714
|
+
transition: background 0.2s;
|
|
715
|
+
flex-shrink: 0;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
.admin-perm-toggle::before {
|
|
719
|
+
content: "";
|
|
720
|
+
position: absolute;
|
|
721
|
+
top: 2px;
|
|
722
|
+
left: 2px;
|
|
723
|
+
width: 16px;
|
|
724
|
+
height: 16px;
|
|
725
|
+
border-radius: 50%;
|
|
726
|
+
background: #fff;
|
|
727
|
+
transition: transform 0.2s;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
.admin-perm-toggle:checked {
|
|
731
|
+
background: var(--accent);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
.admin-perm-toggle:checked::before {
|
|
735
|
+
transform: translateX(16px);
|
|
736
|
+
}
|