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 +1 -1
- package/public/app.js +2 -54
- package/public/index.html +1 -7
- package/server.js +23 -21
package/package.json
CHANGED
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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:
|
|
400
|
+
terminals: [],
|
|
399
401
|
});
|
|
400
402
|
} catch (err) {
|
|
401
403
|
res.status(500).json({ error: 'Failed to create session', detail: err.message });
|