clay-server 2.45.0-beta.2 → 2.45.0-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.
@@ -145,7 +145,8 @@ function attachUserMessage(ctx) {
145
145
  return true;
146
146
  }
147
147
  }
148
- var t = tm.create(msg.cols || 80, msg.rows || 24, getOsUserInfoForWs(ws), ws);
148
+ var t = tm.create(msg.cols || 80, msg.rows || 24, getOsUserInfoForWs(ws), ws,
149
+ msg.initialCommand ? { initialInput: String(msg.initialCommand) } : undefined);
149
150
  if (!t) {
150
151
  sendTo(ws, { type: "term_error", error: "Cannot create terminal (node-pty not available or limit reached)" });
151
152
  return true;
@@ -349,6 +349,12 @@ body.tui-suspended #tui-resume-bar {
349
349
  border: 1px solid var(--border);
350
350
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.6);
351
351
  }
352
+ /* Compact variant for short content (e.g. login wizard): smaller box so the
353
+ terminal hugs the content instead of leaving a tall empty area below. */
354
+ .tui-modal.tui-modal-compact {
355
+ width: min(720px, 96%);
356
+ height: min(480px, 90%);
357
+ }
352
358
  .tui-modal-header {
353
359
  display: flex;
354
360
  align-items: center;
@@ -29,7 +29,7 @@ import { updateSettingsModels, updateSettingsStats, updateDaemonConfig, handleSe
29
29
  import { handleTermList, handleTermCreated, sendTerminalCommand, handleTermOutput, handleTermResized, handleTermExited, handleTermClosed } from './terminal.js';
30
30
  import { attachTuiView, detachTuiView, setTuiSuspendedView, tuiHandleTermOutput, tuiHandleTermResized, tuiHandleTermExited, tuiHandleTermClosed } from './session-tui-view.js';
31
31
  import { handleTuiTranscriptState } from './tui-grab.js';
32
- import { tuiModalHandleTermOutput, tuiModalHandleTermResized, tuiModalHandleTermExited, tuiModalHandleTermClosed } from './tui-attention.js';
32
+ import { tuiModalHandleTermOutput, tuiModalHandleTermResized, tuiModalHandleTermExited, tuiModalHandleTermClosed, openTuiModal } from './tui-attention.js';
33
33
  import { updateTerminalList, handleContextSourcesState, updateEmailAccountList, updateEmailUnreadCounts, handleEmailTestResult, handleEmailAddResult, handleEmailRemoveResult, handleEmailDefaults } from './context-sources.js';
34
34
  import { refreshEmailSettings } from './user-settings.js';
35
35
  import { handleNotesList, handleNoteCreated, handleNoteUpdated, handleNoteDeleted } from './sticky-notes.js';
@@ -57,7 +57,7 @@ import { handleRemoteCursorMove, handleRemoteCursorLeave, handleRemoteSelection,
57
57
  import { showLoopBanner, updateLoopBanner, updateLoopInputVisibility, showRalphApprovalBar, updateRalphApprovalStatus, openRalphPreviewModal, showExecModal, updateExecModalStatus } from './app-loop-ui.js';
58
58
  import { showDebateSticky, showDebateConcludeConfirm, showDebateUserFloor, exitDebateFloorMode, exitDebateConcludeMode, exitDebateEndedMode, updateDebateRound, renderDebateUserFloorDone } from './app-debate-ui.js';
59
59
  import { handleSkillInstallWs } from './app-skills-install.js';
60
- import { handleNotificationsState, handleNotificationCreated, handleNotificationDismissed, handleNotificationDismissedAll, showUpdateBanner } from './app-notifications.js';
60
+ import { handleNotificationsState, handleNotificationCreated, handleNotificationDismissed, handleNotificationDismissedAll, showUpdateBanner, autoStartLoginIfNeeded } from './app-notifications.js';
61
61
  import { handleDebatePreparing, handleDebateBriefReady, renderDebateBriefReady, handleDebateStarted, renderDebateStarted, handleDebateTurn, handleDebateActivity, handleDebateStream, handleDebateTurnDone, handleDebateCommentQueued, handleDebateCommentInjected, renderDebateCommentInjected, handleDebateResumed, handleDebateEnded, renderDebateEnded, handleDebateError, isDebateActive, renderMcpDebateProposal, renderDebateUserResume } from './debate.js';
62
62
  import { handleMentionStart, handleMentionActivity, handleMentionStream, handleMentionDone, handleMentionError, renderMentionUser, renderMentionResponse, renderUserMention } from './mention.js';
63
63
 
@@ -1156,6 +1156,9 @@ export function processMessage(msg) {
1156
1156
  appendDelta((msg.text || "Authentication required.") + "\n");
1157
1157
  setStatus("connected");
1158
1158
  if (!store.get('loopActive')) enableMainInput();
1159
+ // Auto-open the login modal terminal when the session can self-login.
1160
+ // No-op otherwise (the auth_required banner remains the manual path).
1161
+ autoStartLoginIfNeeded(msg);
1159
1162
  break;
1160
1163
 
1161
1164
  case "rate_limit":
@@ -1338,6 +1341,19 @@ export function processMessage(msg) {
1338
1341
  break;
1339
1342
 
1340
1343
  case "term_created":
1344
+ // Login-modal path: the terminal was created with the login command as
1345
+ // its initial input; attach the modal dialog to it (replays scrollback
1346
+ // so the login URL is visible) instead of the sidebar terminal.
1347
+ if (store.get('pendingLoginModal')) {
1348
+ var _lm = store.get('pendingLoginModal');
1349
+ store.set({ pendingLoginModal: null });
1350
+ openTuiModal(msg.id, _lm.slug, {
1351
+ sessionTitle: (_lm.vendor === "codex" ? "Codex" : "Claude") + " login",
1352
+ projectName: _lm.slug,
1353
+ compact: true,
1354
+ });
1355
+ break;
1356
+ }
1341
1357
  handleTermCreated(msg);
1342
1358
  if (store.get('pendingTermCommand')) {
1343
1359
  var cmd = store.get('pendingTermCommand');
@@ -252,7 +252,7 @@ function showBanner(notif, autoDismissMs) {
252
252
  removeBanner(banner);
253
253
  dismissNotif(notif.id);
254
254
  var authMeta = notif.meta || {};
255
- startLoginCommand(authMeta.loginCommand || ((authMeta.vendor || "claude") === "codex" ? "codex login --device-auth" : "claude login"));
255
+ startLoginInModal(authMeta.loginCommand || ((authMeta.vendor || "claude") === "codex" ? "codex login --device-auth" : "claude login"), authMeta.vendor || "claude");
256
256
  showLoginReminderBanner();
257
257
  });
258
258
  }
@@ -311,7 +311,42 @@ function showBanner(notif, autoDismissMs) {
311
311
  }
312
312
  }
313
313
 
314
+ // Open the login terminal as a modal dialog (the same modal used for TUI
315
+ // attention), running the vendor login command. The terminal is created with
316
+ // the command as its initial input, so when the modal attaches it replays the
317
+ // scrollback and the user sees the login URL / prompts immediately.
318
+ // Falls back to the sidebar terminal if the current project slug is unknown
319
+ // (the modal needs a slug to open its own WS to the project).
320
+ function currentProjectSlug() {
321
+ var slug = store.get('currentSlug') || "";
322
+ if (slug) return slug;
323
+ // Fallback: derive from the URL (/p/<slug>/...).
324
+ try {
325
+ var m = (window.location.pathname || "").match(/^\/p\/([a-z0-9_-]+)/);
326
+ if (m) return m[1];
327
+ } catch (e) {}
328
+ return "";
329
+ }
330
+
331
+ function startLoginInModal(loginCommand, vendor) {
332
+ if (authReminderVisible) return;
333
+ var ws = getWs();
334
+ if (!ws || ws.readyState !== 1) return;
335
+ var cmd = loginCommand || (vendor === "codex" ? "codex login --device-auth" : "claude login");
336
+ var slug = currentProjectSlug();
337
+ if (!slug) { startLoginCommand(cmd); return; }
338
+ store.set({ pendingLoginModal: { slug: slug, vendor: vendor || "claude" } });
339
+ ws.send(JSON.stringify({
340
+ type: "term_create",
341
+ cols: 100,
342
+ rows: 30,
343
+ initialCommand: cmd + "\n",
344
+ }));
345
+ }
346
+
347
+ // Sidebar-terminal fallback (used when no slug is available for the modal).
314
348
  function startLoginCommand(loginCommand) {
349
+ if (authReminderVisible) return;
315
350
  var ws = getWs();
316
351
  if (!ws || ws.readyState !== 1) return;
317
352
  var termCommand = (loginCommand || "claude login") + "\n";
@@ -320,6 +355,23 @@ function startLoginCommand(loginCommand) {
320
355
  openTerminal();
321
356
  }
322
357
 
358
+ // Auto-open the login modal and run the login command when the server reports
359
+ // the vendor isn't logged in. The popup terminal runs as the connected user's
360
+ // own OS identity (term_create -> getOsUserInfoForWs), so login always targets
361
+ // the right account — we pop it unconditionally rather than gating on
362
+ // canAutoLogin. Idempotent: startLoginInModal is guarded by authReminderVisible
363
+ // so repeat auth_required events no-op.
364
+ export function autoStartLoginIfNeeded(msg) {
365
+ if (!msg) return false;
366
+ if (authReminderVisible) return false;
367
+ var vendor = msg.vendor || "claude";
368
+ var cmd = msg.loginCommand
369
+ || (vendor === "codex" ? "codex login --device-auth" : "claude login");
370
+ startLoginInModal(cmd, vendor);
371
+ showLoginReminderBanner();
372
+ return true;
373
+ }
374
+
323
375
  function showLoginReminderBanner() {
324
376
  if (!bannerContainer) return;
325
377
  authReminderVisible = true;
@@ -554,6 +606,11 @@ export function handleNotificationCreated(msg) {
554
606
  unreadCount = msg.unreadCount;
555
607
  updateBadge();
556
608
 
609
+ // auth_required no longer pops a banner: the login terminal modal opens
610
+ // automatically (see autoStartLoginIfNeeded). Keep the notification in the
611
+ // center (above) for history, but don't show the redundant banner.
612
+ if (notif.type === "auth_required") return;
613
+
557
614
  // tui_attention drives the project icon shake and the favicon blink —
558
615
  // same visual cues the SDK already produces from pendingPermissions /
559
616
  // permission_request messages, so the user notices either kind without
@@ -147,6 +147,12 @@ export function openTuiModal(terminalId, sourceSlug, info) {
147
147
  });
148
148
  modalEl.classList.remove("hidden");
149
149
 
150
+ // Compact sizing for short, content-light terminals (e.g. the login
151
+ // wizard) so the box hugs the content instead of leaving a tall empty
152
+ // black area below it. Full-size for live TUI sessions that fill the screen.
153
+ var boxEl = modalEl.querySelector(".tui-modal");
154
+ if (boxEl) boxEl.classList.toggle("tui-modal-compact", !!infoObj.compact);
155
+
150
156
  var bodyEl = modalEl.querySelector(".tui-modal-body");
151
157
  bodyEl.innerHTML = "";
152
158
  modalXterm = new Terminal({
@@ -7,6 +7,7 @@
7
7
  var { spawn } = require("child_process");
8
8
  var readline = require("readline");
9
9
  var path = require("path");
10
+ var fs = require("fs");
10
11
  var { createRequire } = require("module");
11
12
 
12
13
  // --- Find the codex binary path ---
@@ -48,7 +49,17 @@ function findCodexPath() {
48
49
  var platformPkgJson = codexRequire.resolve(platformPkg + "/package.json");
49
50
  var vendorRoot = path.join(path.dirname(platformPkgJson), "vendor");
50
51
  var binaryName = process.platform === "win32" ? "codex.exe" : "codex";
51
- return path.join(vendorRoot, triple, "codex", binaryName);
52
+ // The binary layout changed across versions: 0.142+ ships it under
53
+ // vendor/<triple>/bin/, older builds used vendor/<triple>/codex/.
54
+ // Try the known layouts and return the first that exists.
55
+ var candidates = [
56
+ path.join(vendorRoot, triple, "bin", binaryName),
57
+ path.join(vendorRoot, triple, "codex", binaryName),
58
+ ];
59
+ for (var i = 0; i < candidates.length; i++) {
60
+ if (fs.existsSync(candidates[i])) return candidates[i];
61
+ }
62
+ throw new Error("codex binary not found in any known layout under " + path.join(vendorRoot, triple));
52
63
  } catch (e) {
53
64
  throw new Error("Could not find codex binary: " + e.message);
54
65
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.45.0-beta.2",
3
+ "version": "2.45.0-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",
@@ -50,7 +50,7 @@
50
50
  "dependencies": {
51
51
  "@anthropic-ai/claude-agent-sdk": "^0.3.196",
52
52
  "@lydell/node-pty": "^1.2.0-beta.3",
53
- "@openai/codex": "^0.124.0",
53
+ "@openai/codex": "^0.142.4",
54
54
  "imapflow": "^1.3.1",
55
55
  "nodemailer": "^6.10.1",
56
56
  "qrcode-terminal": "^0.12.0",