clay-server 2.36.1-beta.1 → 2.36.1-beta.3

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 CHANGED
@@ -1503,11 +1503,9 @@ async function forkDaemon(mode, keepAwake, extraProjects, addCwd, wantOsUsers) {
1503
1503
  break;
1504
1504
  }
1505
1505
  }
1506
- // Restore access settings from previous config
1506
+ // Restore project-level settings from previous config
1507
1507
  if (prevProjectMap[cwd]) {
1508
- if (prevProjectMap[cwd].visibility) cwdEntry.visibility = prevProjectMap[cwd].visibility;
1509
- if (prevProjectMap[cwd].allowedUsers) cwdEntry.allowedUsers = prevProjectMap[cwd].allowedUsers;
1510
- if (prevProjectMap[cwd].ownerId) cwdEntry.ownerId = prevProjectMap[cwd].ownerId;
1508
+ cwdEntry = Object.assign({}, prevProjectMap[cwd], cwdEntry);
1511
1509
  }
1512
1510
  allProjects.push(cwdEntry);
1513
1511
  usedSlugs.push(slug);
@@ -1522,21 +1520,19 @@ async function forkDaemon(mode, keepAwake, extraProjects, addCwd, wantOsUsers) {
1522
1520
  var rpSlug = generateSlug(rp.path, usedSlugs);
1523
1521
  usedSlugs.push(rpSlug);
1524
1522
  var rpEntry = { path: rp.path, slug: rpSlug, title: rp.title || undefined, icon: rp.icon || undefined, addedAt: rp.addedAt || Date.now() };
1525
- // Restore access settings from previous config
1523
+ // Restore project-level settings from previous config
1526
1524
  if (prevProjectMap[rp.path]) {
1527
- if (prevProjectMap[rp.path].visibility) rpEntry.visibility = prevProjectMap[rp.path].visibility;
1528
- if (prevProjectMap[rp.path].allowedUsers) rpEntry.allowedUsers = prevProjectMap[rp.path].allowedUsers;
1529
- if (prevProjectMap[rp.path].ownerId) rpEntry.ownerId = prevProjectMap[rp.path].ownerId;
1525
+ rpEntry = Object.assign({}, prevProjectMap[rp.path], rpEntry);
1530
1526
  }
1531
1527
  allProjects.push(rpEntry);
1532
1528
  }
1533
1529
  }
1534
1530
 
1535
- var config = {
1531
+ var config = Object.assign({}, prevConfig || {}, {
1536
1532
  pid: null,
1537
1533
  port: port,
1538
1534
  host: host,
1539
- pinHash: mode === "multi" && cliPin ? generateAuthToken(cliPin) : null,
1535
+ pinHash: mode === "multi" && cliPin ? generateAuthToken(cliPin) : (prevConfig && prevConfig.pinHash) || null,
1540
1536
  tls: hasTls,
1541
1537
  builtinCert: hasBuiltinCert,
1542
1538
  mkcertDetected: mkcertDetected,
@@ -1548,7 +1544,7 @@ async function forkDaemon(mode, keepAwake, extraProjects, addCwd, wantOsUsers) {
1548
1544
  mode: mode || "single",
1549
1545
  setupCompleted: true,
1550
1546
  projects: allProjects,
1551
- };
1547
+ });
1552
1548
 
1553
1549
  ensureConfigDir();
1554
1550
  saveConfig(config);
@@ -1687,8 +1683,7 @@ async function devMode(mode, keepAwake, existingPinHash, wantOsUsers) {
1687
1683
  }
1688
1684
  // Restore access settings for cwd from previous config
1689
1685
  if (prevDevProjectMap[cwd]) {
1690
- if (prevDevProjectMap[cwd].visibility) cwdDevEntry.visibility = prevDevProjectMap[cwd].visibility;
1691
- if (prevDevProjectMap[cwd].allowedUsers) cwdDevEntry.allowedUsers = prevDevProjectMap[cwd].allowedUsers;
1686
+ cwdDevEntry = Object.assign({}, prevDevProjectMap[cwd], cwdDevEntry);
1692
1687
  }
1693
1688
  var allProjects = [cwdDevEntry];
1694
1689
  var usedSlugs = [slug];
@@ -1697,15 +1692,14 @@ async function devMode(mode, keepAwake, existingPinHash, wantOsUsers) {
1697
1692
  var rpSlug = generateSlug(rp.path, usedSlugs);
1698
1693
  usedSlugs.push(rpSlug);
1699
1694
  var rpDevEntry = { path: rp.path, slug: rpSlug, title: rp.title || undefined, icon: rp.icon || undefined, addedAt: rp.addedAt || Date.now() };
1700
- // Restore access settings from previous config
1695
+ // Restore project-level settings from previous config
1701
1696
  if (prevDevProjectMap[rp.path]) {
1702
- if (prevDevProjectMap[rp.path].visibility) rpDevEntry.visibility = prevDevProjectMap[rp.path].visibility;
1703
- if (prevDevProjectMap[rp.path].allowedUsers) rpDevEntry.allowedUsers = prevDevProjectMap[rp.path].allowedUsers;
1697
+ rpDevEntry = Object.assign({}, prevDevProjectMap[rp.path], rpDevEntry);
1704
1698
  }
1705
1699
  allProjects.push(rpDevEntry);
1706
1700
  }
1707
1701
 
1708
- var config = {
1702
+ var config = Object.assign({}, prevDevConfig || {}, {
1709
1703
  pid: null,
1710
1704
  port: port,
1711
1705
  host: host,
@@ -1720,7 +1714,7 @@ async function devMode(mode, keepAwake, existingPinHash, wantOsUsers) {
1720
1714
  setupCompleted: true,
1721
1715
  projects: allProjects,
1722
1716
  osUsers: wantOsUsers || (prevDevConfig ? (prevDevConfig.osUsers || false) : false),
1723
- };
1717
+ });
1724
1718
 
1725
1719
  ensureConfigDir();
1726
1720
  saveConfig(config);
package/lib/project.js CHANGED
@@ -911,7 +911,19 @@ function createProjectContext(opts) {
911
911
  var firstModel = vendorModels[0] || "";
912
912
  // model value can be string or {value, displayName} object
913
913
  var defaultModel = typeof firstModel === "string" ? firstModel : (firstModel.value || "");
914
- sendTo(ws, { type: "model_info", model: defaultModel, models: vendorModels, vendor: msg.vendor, availableVendors: sm.availableVendors || [], installedVendors: sm.installedVendors || [] });
914
+ // Preserve the user's current model selection if it belongs to this
915
+ // vendor, rather than always snapping back to the vendor's default.
916
+ var modelToSend = defaultModel;
917
+ if (sm.currentModel) {
918
+ for (var mi = 0; mi < vendorModels.length; mi++) {
919
+ var mv = typeof vendorModels[mi] === "string" ? vendorModels[mi] : (vendorModels[mi].value || "");
920
+ if (mv === sm.currentModel) {
921
+ modelToSend = sm.currentModel;
922
+ break;
923
+ }
924
+ }
925
+ }
926
+ sendTo(ws, { type: "model_info", model: modelToSend, models: vendorModels, vendor: msg.vendor, availableVendors: sm.availableVendors || [], installedVendors: sm.installedVendors || [] });
915
927
  })();
916
928
  return;
917
929
  }
@@ -389,7 +389,11 @@ export function processMessage(msg) {
389
389
  if (_modelVal && typeof _modelVal === "object") _modelVal = _modelVal.value || _modelVal.displayName || "";
390
390
  var _miUpdate = { currentModels: msg.models || [] };
391
391
  if (Object.prototype.hasOwnProperty.call(msg, "model")) {
392
- _miUpdate.currentModel = _modelVal || "";
392
+ if (store.get('vendorSelectionLocked') && store.get('currentModel')) {
393
+ // Keep the user's existing selection; only update models list
394
+ } else {
395
+ _miUpdate.currentModel = _modelVal || "";
396
+ }
393
397
  } else {
394
398
  _miUpdate.currentModel = store.get('currentModel');
395
399
  }
@@ -1078,6 +1078,13 @@ export function initInput(_ctx) {
1078
1078
  return;
1079
1079
  }
1080
1080
  e.preventDefault();
1081
+ // While Claude is processing and the user has nothing queued, Enter
1082
+ // must not adopt a ghost suggestion — pressing Enter while waiting
1083
+ // should be a no-op, not a send of a stale suggestion the user never
1084
+ // typed.
1085
+ if (ctx.processing && !hasSendableContent()) {
1086
+ return;
1087
+ }
1081
1088
  // If input has no sendable content but ghost suggestion is showing, adopt it.
1082
1089
  // Use hasSendableContent() instead of checking inputEl.value alone so that
1083
1090
  // pending images, pastes, or files block the ghost-text adoption — otherwise
@@ -1097,8 +1104,17 @@ export function initInput(_ctx) {
1097
1104
  ctx.inputEl.setAttribute("enterkeyhint", "enter");
1098
1105
  }
1099
1106
 
1100
- // Send/Stop button — if sendable content exists, always send; otherwise stop
1107
+ // Send/Stop button — gate stop vs send on live state. If Claude is
1108
+ // processing and the user has nothing queued, the click is a Stop and
1109
+ // must not adopt a ghost suggestion. Otherwise it's a send.
1110
+ // (regression after #337 / 0753833)
1101
1111
  ctx.sendBtn.addEventListener("click", function () {
1112
+ if (ctx.processing && !hasSendableContent()) {
1113
+ if (ctx.connected) {
1114
+ ctx.ws.send(JSON.stringify({ type: "stop" }));
1115
+ }
1116
+ return;
1117
+ }
1102
1118
  // Adopt ghost suggestion if input is empty
1103
1119
  var ghost = ctx.getGhostSuggestion ? ctx.getGhostSuggestion() : "";
1104
1120
  if (!hasSendableContent() && ghost) {
@@ -1111,9 +1127,6 @@ export function initInput(_ctx) {
1111
1127
  sendMessage();
1112
1128
  return;
1113
1129
  }
1114
- if (ctx.processing && ctx.connected) {
1115
- ctx.ws.send(JSON.stringify({ type: "stop" }));
1116
- }
1117
1130
  });
1118
1131
  ctx.sendBtn.addEventListener("dblclick", function (e) { e.preventDefault(); });
1119
1132
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.36.1-beta.1",
3
+ "version": "2.36.1-beta.3",
4
4
  "description": "Self-hosted team workspace for Claude Code and Codex. Multi-user, browser-based, with persistent AI mates.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js",