ninja-terminals 2.1.1 → 2.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ninja-terminals",
3
- "version": "2.1.1",
3
+ "version": "2.1.3",
4
4
  "description": "Multi-terminal Claude Code orchestrator with DAG task management, permission hooks, and resilience",
5
5
  "main": "server.js",
6
6
  "bin": {
package/public/app.js CHANGED
@@ -202,10 +202,8 @@ const state = {
202
202
  const grid = document.getElementById('grid');
203
203
  const sidebar = document.getElementById('sidebar');
204
204
  const activityFeed = document.getElementById('activity-feed');
205
- const taskQueue = document.getElementById('task-queue');
206
205
  const statusCounts = document.getElementById('status-counts');
207
206
  const statusProgress = document.getElementById('status-progress');
208
- const addTaskBtn = document.getElementById('add-task-btn');
209
207
  const sidebarToggle = document.getElementById('sidebar-toggle');
210
208
 
211
209
  // ── State Color Map ──────────────────────────────────────────
@@ -347,7 +345,7 @@ function createTerminalUI(termData) {
347
345
  closeBtn.addEventListener('mousedown', (e) => {
348
346
  e.preventDefault();
349
347
  e.stopPropagation();
350
- removeTerminal(id);
348
+ closeTerminal(id);
351
349
  });
352
350
 
353
351
  header.appendChild(labelEl);
@@ -892,41 +890,6 @@ function connectSSE() {
892
890
  return evtSource;
893
891
  }
894
892
 
895
- // ── Task Queue ───────────────────────────────────────────────
896
-
897
- async function fetchTasks() {
898
- try {
899
- const res = await fetch(`${API_BASE}/api/tasks`, { headers: auth.getAuthHeader() });
900
- if (!res.ok) {
901
- taskQueue.innerHTML = '<div class="no-tasks">No tasks</div>';
902
- return;
903
- }
904
- const tasks = await res.json();
905
- renderTasks(tasks);
906
- } catch {
907
- taskQueue.innerHTML = '<div class="no-tasks">No tasks</div>';
908
- }
909
- }
910
-
911
- function renderTasks(tasks) {
912
- if (!tasks || !Array.isArray(tasks) || tasks.length === 0) {
913
- taskQueue.innerHTML = '<div class="no-tasks">No tasks</div>';
914
- return;
915
- }
916
-
917
- taskQueue.innerHTML = '';
918
- for (const task of tasks) {
919
- const item = document.createElement('div');
920
- item.className = 'task-item';
921
- const dotClass = task.status || 'pending';
922
- item.innerHTML = `
923
- <span class="task-dot ${dotClass}"></span>
924
- <span class="task-name" title="${escapeHtml(task.name || task.id || '')}">${escapeHtml(task.name || task.id || 'Unnamed')}</span>
925
- <span class="task-status">${escapeHtml(task.status || 'pending')}</span>
926
- `;
927
- taskQueue.appendChild(item);
928
- }
929
- }
930
893
 
931
894
  // ── Status Polling (fallback + elapsed updates) ──────────────
932
895
 
@@ -996,14 +959,6 @@ function setupSidebar() {
996
959
  document.body.appendChild(mobileToggle);
997
960
  }
998
961
 
999
- // ── Add Task ─────────────────────────────────────────────────
1000
-
1001
- function setupAddTask() {
1002
- addTaskBtn.addEventListener('click', () => {
1003
- addNewTerminal();
1004
- });
1005
- }
1006
-
1007
962
  // ── Resize Handler ───────────────────────────────────────────
1008
963
 
1009
964
  let resizeTimeout;
@@ -1020,7 +975,6 @@ async function startApp() {
1020
975
 
1021
976
  // Setup sidebar
1022
977
  setupSidebar();
1023
- setupAddTask();
1024
978
  setupLearnings();
1025
979
  setupAddTerminal();
1026
980
 
@@ -1049,15 +1003,9 @@ async function startApp() {
1049
1003
  // Connect SSE for real-time updates
1050
1004
  connectSSE();
1051
1005
 
1052
- // Fetch initial task queue
1053
- fetchTasks();
1054
-
1055
1006
  // Poll status every 3 seconds (fallback for SSE + elapsed updates)
1056
1007
  setInterval(pollStatus, 3000);
1057
1008
 
1058
- // Poll task queue every 5 seconds
1059
- setInterval(fetchTasks, 5000);
1060
-
1061
1009
  // Initial status bar
1062
1010
  updateStatusBar();
1063
1011
 
@@ -1108,7 +1056,7 @@ async function clearAllTerminals() {
1108
1056
 
1109
1057
  const ids = Array.from(state.terminals.keys());
1110
1058
  for (const id of ids) {
1111
- await removeTerminal(id);
1059
+ await closeTerminal(id);
1112
1060
  }
1113
1061
  addFeedEntry('All terminals cleared');
1114
1062
  }
package/public/index.html CHANGED
@@ -77,13 +77,7 @@
77
77
  <h3 class="sidebar-title">Activity</h3>
78
78
  <div id="activity-feed"></div>
79
79
  </div>
80
- <hr>
81
- <div class="sidebar-section">
82
- <h3 class="sidebar-title">Task Queue</h3>
83
- <div id="task-queue"></div>
84
- <button id="add-task-btn" class="add-task-btn">+ Add Task</button>
85
- </div>
86
- </aside>
80
+ </aside>
87
81
  <main>
88
82
  <div id="grid"></div>
89
83
  </main>
package/server.js CHANGED
@@ -346,14 +346,30 @@ app.get('/health', (_req, res) => {
346
346
 
347
347
  // ── Session Endpoints ───────────────────────────────────────
348
348
 
349
- // Create session — validates token and spawns terminals
349
+ // Create session — validates token and returns existing or spawns terminals
350
350
  app.post('/api/session', requireAuth, (req, res) => {
351
351
  try {
352
352
  const { tier, terminalsMax, features, token } = req.ninjaUser;
353
353
 
354
- // Clear any existing session
354
+ // If session already exists with same token, return existing terminals
355
+ if (activeSession && activeSession.token === token) {
356
+ const existingTerminals = activeSession.terminalIds
357
+ .map(id => terminals.get(id))
358
+ .filter(Boolean)
359
+ .map(t => ({ id: t.id, label: t.label, status: t.status, cwd: t.cwd }));
360
+
361
+ console.log(`[session] Returning existing session: tier=${tier}, terminals=${existingTerminals.length}`);
362
+
363
+ return res.json({
364
+ tier,
365
+ terminalsMax,
366
+ features,
367
+ terminals: existingTerminals,
368
+ });
369
+ }
370
+
371
+ // Clear any existing session with different token
355
372
  if (activeSession) {
356
- // Kill existing terminals
357
373
  for (const id of activeSession.terminalIds) {
358
374
  const terminal = terminals.get(id);
359
375
  if (terminal) {
@@ -364,7 +380,7 @@ app.post('/api/session', requireAuth, (req, res) => {
364
380
  }
365
381
  }
366
382
 
367
- // Create new session
383
+ // Create new session (but don't auto-spawn terminals - let user add them)
368
384
  activeSession = {
369
385
  token,
370
386
  tier,
@@ -374,28 +390,14 @@ app.post('/api/session', requireAuth, (req, res) => {
374
390
  createdAt: Date.now(),
375
391
  };
376
392
 
377
- // Spawn terminals up to the tier limit
378
- const cwd = req.body?.cwd || DEFAULT_CWD;
379
- const spawnedTerminals = [];
380
-
381
- for (let i = 0; i < terminalsMax; i++) {
382
- const terminal = spawnTerminal(`T${i + 1}`, [], cwd, tier);
383
- activeSession.terminalIds.push(terminal.id);
384
- spawnedTerminals.push({
385
- id: terminal.id,
386
- label: terminal.label,
387
- status: terminal.status,
388
- cwd: terminal.cwd,
389
- });
390
- }
391
-
392
- console.log(`[session] Created session: tier=${tier}, terminals=${terminalsMax}`);
393
+ console.log(`[session] Created new session: tier=${tier}, terminalsMax=${terminalsMax}`);
393
394
 
395
+ // Return empty terminals - user can add via + button
394
396
  res.json({
395
397
  tier,
396
398
  terminalsMax,
397
399
  features,
398
- terminals: spawnedTerminals,
400
+ terminals: [],
399
401
  });
400
402
  } catch (err) {
401
403
  res.status(500).json({ error: 'Failed to create session', detail: err.message });