clay-server 2.11.0-beta.2 → 2.11.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.
@@ -0,0 +1,243 @@
1
+ // settings-defaults.js — Shared rendering for model/mode/effort/thinking controls
2
+ // Used by both server-settings.js and project-settings.js
3
+
4
+ export var MODE_OPTIONS = [
5
+ { value: "default", label: "Default", desc: "Claude asks for permission before running tools and editing files." },
6
+ { value: "plan", label: "Plan", desc: "Claude creates a plan first and asks for approval before making changes." },
7
+ { value: "acceptEdits", label: "Auto-accept edits", desc: "File edits are applied automatically. Claude still asks before running commands." },
8
+ ];
9
+
10
+ export var EFFORT_LEVELS = [
11
+ { value: "low", desc: "Quick, concise responses. Best for simple questions." },
12
+ { value: "medium", desc: "Balanced responses with moderate reasoning. Good for most tasks." },
13
+ { value: "high", desc: "Thorough responses with deeper analysis. Good for complex tasks." },
14
+ { value: "max", desc: "Maximum reasoning depth. Best for the most difficult problems." },
15
+ ];
16
+
17
+ export var THINKING_OPTIONS = [
18
+ { value: "disabled", label: "Off", desc: "Disable extended thinking." },
19
+ { value: "adaptive", label: "Adaptive", desc: "Claude decides when to use extended thinking." },
20
+ { value: "budget", label: "Budget", desc: "Set a token budget for extended thinking." },
21
+ ];
22
+
23
+ export var MODEL_DESCRIPTIONS = {
24
+ "default": "Automatically selects the best model for the task.",
25
+ "sonnet": "Fast and capable. Great balance of speed and intelligence.",
26
+ "haiku": "Fastest model. Best for quick tasks and simple questions.",
27
+ "opus": "Most powerful model. Best for complex reasoning and analysis.",
28
+ };
29
+
30
+ export function getModelDesc(model) {
31
+ if (!model) return "";
32
+ var lower = (model.value || model).toLowerCase();
33
+ for (var key in MODEL_DESCRIPTIONS) {
34
+ if (lower.indexOf(key) !== -1) return MODEL_DESCRIPTIONS[key];
35
+ }
36
+ return "";
37
+ }
38
+
39
+ export function isSonnetModel(model) {
40
+ if (!model) return false;
41
+ return model.toLowerCase().indexOf("sonnet") !== -1;
42
+ }
43
+
44
+ // --- Render functions ---
45
+ // Each takes an element ID prefix (e.g. "ss" or "ps"), a send function, and state getters.
46
+
47
+ /**
48
+ * Render model list into `${prefix}-model-list`
49
+ * @param {string} prefix - Element ID prefix
50
+ * @param {object} opts - { models, currentModel, sendMsg, onModelSelect }
51
+ */
52
+ export function renderModelList(prefix, opts) {
53
+ var listEl = document.getElementById(prefix + "-model-list");
54
+ if (!listEl) return;
55
+
56
+ var models = opts.models || [];
57
+ var currentModel = opts.currentModel || "";
58
+
59
+ listEl.innerHTML = "";
60
+ if (models.length === 0) {
61
+ listEl.innerHTML = '<div style="font-size:13px;color:var(--text-dimmer);">No models available</div>';
62
+ return;
63
+ }
64
+
65
+ for (var i = 0; i < models.length; i++) {
66
+ (function (m) {
67
+ var value = m.value || "";
68
+ var label = m.displayName || value;
69
+ var item = document.createElement("div");
70
+ item.className = "settings-model-item" + (value === currentModel ? " active" : "");
71
+ item.dataset.model = value;
72
+
73
+ var nameSpan = document.createElement("span");
74
+ nameSpan.className = "settings-model-name";
75
+ nameSpan.textContent = label;
76
+ item.appendChild(nameSpan);
77
+
78
+ var desc = getModelDesc(value);
79
+ if (desc) {
80
+ var descSpan = document.createElement("span");
81
+ descSpan.className = "settings-model-desc";
82
+ descSpan.textContent = desc;
83
+ item.appendChild(descSpan);
84
+ }
85
+
86
+ item.addEventListener("click", function () {
87
+ opts.sendMsg(opts.modelMsgType, { model: value });
88
+ var items = listEl.querySelectorAll(".settings-model-item");
89
+ for (var j = 0; j < items.length; j++) items[j].classList.remove("active");
90
+ item.classList.add("active");
91
+ if (opts.onModelSelect) opts.onModelSelect(value);
92
+ });
93
+
94
+ listEl.appendChild(item);
95
+ })(models[i]);
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Render mode list into `${prefix}-mode-list`
101
+ */
102
+ export function renderModeList(prefix, opts) {
103
+ var listEl = document.getElementById(prefix + "-mode-list");
104
+ if (!listEl) return;
105
+
106
+ var currentMode = opts.currentMode || "default";
107
+ listEl.innerHTML = "";
108
+
109
+ for (var i = 0; i < MODE_OPTIONS.length; i++) {
110
+ (function (opt) {
111
+ var item = document.createElement("div");
112
+ item.className = "settings-model-item" + (opt.value === currentMode ? " active" : "");
113
+
114
+ var nameSpan = document.createElement("span");
115
+ nameSpan.className = "settings-model-name";
116
+ nameSpan.textContent = opt.label;
117
+ item.appendChild(nameSpan);
118
+
119
+ var descSpan = document.createElement("span");
120
+ descSpan.className = "settings-model-desc";
121
+ descSpan.textContent = opt.desc;
122
+ item.appendChild(descSpan);
123
+
124
+ item.addEventListener("click", function () {
125
+ opts.sendMsg(opts.modeMsgType, { mode: opt.value });
126
+ var items = listEl.querySelectorAll(".settings-model-item");
127
+ for (var j = 0; j < items.length; j++) items[j].classList.remove("active");
128
+ item.classList.add("active");
129
+ });
130
+
131
+ listEl.appendChild(item);
132
+ })(MODE_OPTIONS[i]);
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Render effort bar into `${prefix}-effort-bar`
138
+ */
139
+ export function renderEffortBar(prefix, opts) {
140
+ var bar = document.getElementById(prefix + "-effort-bar");
141
+ if (!bar) return;
142
+
143
+ var currentEffort = opts.currentEffort || "medium";
144
+ bar.innerHTML = "";
145
+
146
+ for (var i = 0; i < EFFORT_LEVELS.length; i++) {
147
+ (function (lvl) {
148
+ var btn = document.createElement("button");
149
+ btn.className = "settings-btn-option" + (lvl.value === currentEffort ? " active" : "");
150
+ btn.textContent = lvl.value.charAt(0).toUpperCase() + lvl.value.slice(1);
151
+ btn.title = lvl.desc;
152
+ btn.addEventListener("click", function () {
153
+ opts.sendMsg(opts.effortMsgType, { effort: lvl.value });
154
+ var btns = bar.querySelectorAll(".settings-btn-option");
155
+ for (var j = 0; j < btns.length; j++) btns[j].classList.remove("active");
156
+ btn.classList.add("active");
157
+ });
158
+ bar.appendChild(btn);
159
+ })(EFFORT_LEVELS[i]);
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Render thinking bar into `${prefix}-thinking-bar`
165
+ */
166
+ export function renderThinkingBar(prefix, opts) {
167
+ var bar = document.getElementById(prefix + "-thinking-bar");
168
+ if (!bar) return;
169
+
170
+ var currentThinking = opts.currentThinking || "adaptive";
171
+ var currentBudget = opts.currentThinkingBudget || 10000;
172
+ var budgetRow = document.getElementById(prefix + "-thinking-budget-row");
173
+ var budgetInput = document.getElementById(prefix + "-thinking-budget");
174
+ bar.innerHTML = "";
175
+
176
+ for (var i = 0; i < THINKING_OPTIONS.length; i++) {
177
+ (function (opt) {
178
+ var btn = document.createElement("button");
179
+ btn.className = "settings-btn-option" + (opt.value === currentThinking ? " active" : "");
180
+ btn.textContent = opt.label;
181
+ btn.title = opt.desc;
182
+ btn.addEventListener("click", function () {
183
+ var msg = { thinking: opt.value };
184
+ if (opt.value === "budget") {
185
+ msg.budgetTokens = budgetInput ? parseInt(budgetInput.value, 10) || 10000 : 10000;
186
+ }
187
+ opts.sendMsg("set_thinking", msg);
188
+ var btns = bar.querySelectorAll(".settings-btn-option");
189
+ for (var j = 0; j < btns.length; j++) btns[j].classList.remove("active");
190
+ btn.classList.add("active");
191
+ if (budgetRow) budgetRow.style.display = opt.value === "budget" ? "" : "none";
192
+ });
193
+ bar.appendChild(btn);
194
+ })(THINKING_OPTIONS[i]);
195
+ }
196
+
197
+ if (budgetRow) budgetRow.style.display = currentThinking === "budget" ? "" : "none";
198
+ if (budgetInput) {
199
+ budgetInput.value = currentBudget;
200
+ budgetInput.addEventListener("change", function () {
201
+ var val = Math.max(1024, Math.min(128000, parseInt(this.value, 10) || 10000));
202
+ this.value = val;
203
+ opts.sendMsg("set_thinking", { thinking: "budget", budgetTokens: val });
204
+ });
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Update beta card visibility and bind toggle
210
+ */
211
+ export function renderBetaCard(prefix, opts) {
212
+ var model = opts.overrideModel || opts.currentModel || "";
213
+ var card = document.getElementById(prefix + "-beta-card");
214
+ if (card) {
215
+ card.style.display = isSonnetModel(model) ? "" : "none";
216
+ }
217
+
218
+ var toggle = document.getElementById(prefix + "-beta-1m");
219
+ if (toggle) {
220
+ var betas = opts.currentBetas || [];
221
+ var hasBeta = false;
222
+ for (var i = 0; i < betas.length; i++) {
223
+ if (betas[i].indexOf("context-1m") !== -1) { hasBeta = true; break; }
224
+ }
225
+ toggle.checked = hasBeta;
226
+ toggle.onchange = function () {
227
+ var currentBetas = opts.currentBetas || [];
228
+ var newBetas;
229
+ if (this.checked) {
230
+ newBetas = currentBetas.slice();
231
+ newBetas.push("context-1m-2025-08-07");
232
+ } else {
233
+ newBetas = [];
234
+ for (var j = 0; j < currentBetas.length; j++) {
235
+ if (currentBetas[j].indexOf("context-1m") === -1) {
236
+ newBetas.push(currentBetas[j]);
237
+ }
238
+ }
239
+ }
240
+ opts.sendMsg("set_betas", { betas: newBetas });
241
+ };
242
+ }
243
+ }
package/lib/server.js CHANGED
@@ -348,6 +348,7 @@ function createServer(opts) {
348
348
  var onSetPin = opts.onSetPin || null;
349
349
  var onSetKeepAwake = opts.onSetKeepAwake || null;
350
350
  var onShutdown = opts.onShutdown || null;
351
+ var onSetUpdateChannel = opts.onSetUpdateChannel || null;
351
352
  var onUpgradePin = opts.onUpgradePin || null;
352
353
  var onSetProjectVisibility = opts.onSetProjectVisibility || null;
353
354
  var onSetProjectAllowedUsers = opts.onSetProjectAllowedUsers || null;
@@ -1894,6 +1895,8 @@ function createServer(opts) {
1894
1895
  onGetDaemonConfig: onGetDaemonConfig,
1895
1896
  onSetPin: onSetPin,
1896
1897
  onSetKeepAwake: onSetKeepAwake,
1898
+ onSetUpdateChannel: onSetUpdateChannel,
1899
+ updateChannel: onGetDaemonConfig ? (onGetDaemonConfig().updateChannel || "stable") : "stable",
1897
1900
  onShutdown: onShutdown,
1898
1901
  onDmMessage: handleDmMessage,
1899
1902
  });
package/lib/updater.js CHANGED
@@ -21,9 +21,10 @@ var sym = {
21
21
 
22
22
  function log(s) { console.log(" " + s); }
23
23
 
24
- function fetchLatestVersion() {
24
+ function fetchVersion(channel) {
25
+ var tag = channel === "beta" ? "beta" : "latest";
25
26
  return new Promise(function (resolve) {
26
- var req = https.get("https://registry.npmjs.org/clay-server/latest", function (res) {
27
+ var req = https.get("https://registry.npmjs.org/clay-server/" + tag, function (res) {
27
28
  var data = "";
28
29
  res.on("data", function (chunk) { data += chunk; });
29
30
  res.on("end", function () {
@@ -42,22 +43,48 @@ function fetchLatestVersion() {
42
43
  });
43
44
  }
44
45
 
46
+ function fetchLatestVersion() {
47
+ return fetchVersion("stable");
48
+ }
49
+
50
+ function parseVersion(v) {
51
+ var dashIdx = v.indexOf("-");
52
+ var base = dashIdx === -1 ? v : v.substring(0, dashIdx);
53
+ var pre = dashIdx === -1 ? null : v.substring(dashIdx + 1);
54
+ var parts = base.split(".").map(Number);
55
+ var preNum = null;
56
+ if (pre) {
57
+ var m = pre.match(/\.(\d+)$/);
58
+ preNum = m ? parseInt(m[1], 10) : 0;
59
+ }
60
+ return { parts: parts, pre: pre, preNum: preNum };
61
+ }
62
+
45
63
  function isNewer(latest, current) {
46
64
  if (!latest || !current) return false;
47
- var lp = latest.split(".").map(Number);
48
- var cp = current.split(".").map(Number);
65
+ var l = parseVersion(latest);
66
+ var c = parseVersion(current);
67
+ // Compare base version (major.minor.patch)
49
68
  for (var i = 0; i < 3; i++) {
50
- var l = lp[i] || 0;
51
- var c = cp[i] || 0;
52
- if (l > c) return true;
53
- if (l < c) return false;
69
+ var lv = l.parts[i] || 0;
70
+ var cv = c.parts[i] || 0;
71
+ if (lv > cv) return true;
72
+ if (lv < cv) return false;
73
+ }
74
+ // Bases are equal: stable (no pre-release) beats pre-release
75
+ if (!l.pre && c.pre) return true;
76
+ if (l.pre && !c.pre) return false;
77
+ // Both pre-release with same base: compare pre-release number
78
+ if (l.pre && c.pre) {
79
+ return l.preNum > c.preNum;
54
80
  }
55
81
  return false;
56
82
  }
57
83
 
58
- function performUpdate() {
84
+ function performUpdate(channel) {
85
+ var tag = channel === "beta" ? "beta" : "latest";
59
86
  try {
60
- execSync("npm install -g clay-server@latest", { stdio: "pipe" });
87
+ execSync("npm install -g clay-server@" + tag, { stdio: "pipe" });
61
88
  return true;
62
89
  } catch (e) {
63
90
  return false;
@@ -94,4 +121,4 @@ async function checkAndUpdate(currentVersion, skipUpdate) {
94
121
  return false;
95
122
  }
96
123
 
97
- module.exports = { checkAndUpdate, fetchLatestVersion, isNewer };
124
+ module.exports = { checkAndUpdate, fetchLatestVersion, fetchVersion, isNewer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.11.0-beta.2",
3
+ "version": "2.11.0-beta.4",
4
4
  "description": "Web UI for Claude Code. Any device. Push notifications.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js",