deckide 3.5.10 → 3.5.11
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/dist/routes/terminals.js +1 -101
- package/dist/server.js +3 -6
- package/dist/utils/database.js +6 -0
- package/package.json +1 -1
package/dist/routes/terminals.js
CHANGED
|
@@ -4,7 +4,7 @@ import { spawn } from 'node-pty';
|
|
|
4
4
|
import { TERMINAL_BUFFER_LIMIT } from '../config.js';
|
|
5
5
|
import { createHttpError, handleError, readJson } from '../utils/error.js';
|
|
6
6
|
import { getDefaultShell } from '../utils/shell.js';
|
|
7
|
-
import { saveTerminal, deleteTerminal as deleteTerminalFromDb
|
|
7
|
+
import { saveTerminal, deleteTerminal as deleteTerminalFromDb } from '../utils/database.js';
|
|
8
8
|
// Track terminal index per deck for unique naming
|
|
9
9
|
const deckTerminalCounters = new Map();
|
|
10
10
|
export function createTerminalRouter(db, decks, terminals) {
|
|
@@ -143,106 +143,6 @@ export function createTerminalRouter(db, decks, terminals) {
|
|
|
143
143
|
terminals.set(id, session);
|
|
144
144
|
return session;
|
|
145
145
|
}
|
|
146
|
-
// Restore persisted terminals from database (re-spawn PTY processes)
|
|
147
|
-
function restoreTerminals() {
|
|
148
|
-
const saved = loadTerminals(db);
|
|
149
|
-
for (const row of saved) {
|
|
150
|
-
if (!decks.has(row.deckId)) {
|
|
151
|
-
// Deck no longer exists, clean up
|
|
152
|
-
deleteTerminalFromDb(db, row.id);
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
const deck = decks.get(row.deckId);
|
|
156
|
-
let shell;
|
|
157
|
-
let shellArgs = [];
|
|
158
|
-
if (row.command) {
|
|
159
|
-
shell = getDefaultShell();
|
|
160
|
-
if (process.platform === 'win32') {
|
|
161
|
-
if (shell.toLowerCase().includes('powershell')) {
|
|
162
|
-
shellArgs = ['-NoExit', '-Command', row.command];
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
shellArgs = ['/K', row.command];
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
else {
|
|
169
|
-
shellArgs = ['-c', row.command];
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
shell = getDefaultShell();
|
|
174
|
-
}
|
|
175
|
-
const env = {};
|
|
176
|
-
for (const [key, value] of Object.entries(process.env)) {
|
|
177
|
-
if (value !== undefined)
|
|
178
|
-
env[key] = value;
|
|
179
|
-
}
|
|
180
|
-
env.TERM = env.TERM || 'xterm-256color';
|
|
181
|
-
env.COLORTERM = 'truecolor';
|
|
182
|
-
env.TERM_PROGRAM = 'xterm.js';
|
|
183
|
-
env.TERM_PROGRAM_VERSION = '5.0.0';
|
|
184
|
-
env.LANG = env.LANG || 'en_US.UTF-8';
|
|
185
|
-
env.LC_ALL = env.LC_ALL || 'en_US.UTF-8';
|
|
186
|
-
env.LC_CTYPE = env.LC_CTYPE || 'en_US.UTF-8';
|
|
187
|
-
try {
|
|
188
|
-
const isWindows = process.platform === 'win32';
|
|
189
|
-
const term = spawn(shell, shellArgs, {
|
|
190
|
-
cwd: deck.root,
|
|
191
|
-
cols: 120,
|
|
192
|
-
rows: 32,
|
|
193
|
-
env,
|
|
194
|
-
encoding: 'utf8',
|
|
195
|
-
...(isWindows ? { useConpty: true } : {}),
|
|
196
|
-
});
|
|
197
|
-
const session = {
|
|
198
|
-
id: row.id,
|
|
199
|
-
deckId: row.deckId,
|
|
200
|
-
title: row.title,
|
|
201
|
-
command: row.command,
|
|
202
|
-
createdAt: row.createdAt,
|
|
203
|
-
sockets: new Set(),
|
|
204
|
-
buffer: row.buffer,
|
|
205
|
-
lastActive: Date.now(),
|
|
206
|
-
write: (data) => { try {
|
|
207
|
-
term.write(data);
|
|
208
|
-
}
|
|
209
|
-
catch { /* terminal may be dying */ } },
|
|
210
|
-
resize: (cols, rows) => { try {
|
|
211
|
-
term.resize(cols, rows);
|
|
212
|
-
}
|
|
213
|
-
catch { /* terminal may be dying */ } },
|
|
214
|
-
kill: () => { try {
|
|
215
|
-
term.kill();
|
|
216
|
-
}
|
|
217
|
-
catch { /* already dead */ } },
|
|
218
|
-
};
|
|
219
|
-
term.onData((data) => {
|
|
220
|
-
appendToTerminalBuffer(session, data);
|
|
221
|
-
session.lastActive = Date.now();
|
|
222
|
-
broadcastToSockets(session, data);
|
|
223
|
-
});
|
|
224
|
-
term.onExit(() => {
|
|
225
|
-
handleTerminalExit(row.id);
|
|
226
|
-
});
|
|
227
|
-
terminals.set(row.id, session);
|
|
228
|
-
console.log(`[TERMINAL] Restored terminal ${row.id}: shell=${shell}, cwd=${deck.root}, pid=${term.pid}`);
|
|
229
|
-
}
|
|
230
|
-
catch (error) {
|
|
231
|
-
console.error(`[TERMINAL] Failed to restore terminal ${row.id}:`, error);
|
|
232
|
-
deleteTerminalFromDb(db, row.id);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
restoreTerminals();
|
|
237
|
-
// Periodically save terminal buffers to database
|
|
238
|
-
const bufferSaveInterval = setInterval(() => {
|
|
239
|
-
terminals.forEach((session) => {
|
|
240
|
-
try {
|
|
241
|
-
updateTerminalBuffer(db, session.id, session.buffer);
|
|
242
|
-
}
|
|
243
|
-
catch { /* ignore */ }
|
|
244
|
-
});
|
|
245
|
-
}, 10_000); // Every 10 seconds
|
|
246
146
|
router.get('/', (c) => {
|
|
247
147
|
const deckId = c.req.query('deckId');
|
|
248
148
|
if (!deckId) {
|
package/dist/server.js
CHANGED
|
@@ -11,7 +11,7 @@ import { PORT, HOST, NODE_ENV, BASIC_AUTH_USER, BASIC_AUTH_PASSWORD, CORS_ORIGIN
|
|
|
11
11
|
import { securityHeaders } from './middleware/security.js';
|
|
12
12
|
import { corsMiddleware } from './middleware/cors.js';
|
|
13
13
|
import { basicAuthMiddleware, generateWsToken, isBasicAuthEnabled } from './middleware/auth.js';
|
|
14
|
-
import { checkDatabaseIntegrity, handleDatabaseCorruption, initializeDatabase, loadPersistedState,
|
|
14
|
+
import { checkDatabaseIntegrity, handleDatabaseCorruption, initializeDatabase, loadPersistedState, clearOrphanedTerminals, } from './utils/database.js';
|
|
15
15
|
import { createWorkspaceRouter, getConfigHandler } from './routes/workspaces.js';
|
|
16
16
|
import { createDeckRouter } from './routes/decks.js';
|
|
17
17
|
import { createFileRouter } from './routes/files.js';
|
|
@@ -42,6 +42,7 @@ export async function createServer() {
|
|
|
42
42
|
}
|
|
43
43
|
const db = new DatabaseSync(dbPath);
|
|
44
44
|
initializeDatabase(db);
|
|
45
|
+
clearOrphanedTerminals(db);
|
|
45
46
|
// Initialize state
|
|
46
47
|
const workspaces = new Map();
|
|
47
48
|
const workspacePathIndex = new Map();
|
|
@@ -118,12 +119,8 @@ export async function createServer() {
|
|
|
118
119
|
if (shutdownPromise)
|
|
119
120
|
return shutdownPromise;
|
|
120
121
|
shutdownPromise = (async () => {
|
|
121
|
-
//
|
|
122
|
+
// Kill all terminals
|
|
122
123
|
terminals.forEach((session) => {
|
|
123
|
-
try {
|
|
124
|
-
updateTerminalBuffer(db, session.id, session.buffer);
|
|
125
|
-
}
|
|
126
|
-
catch { /* ignore */ }
|
|
127
124
|
session.sockets.forEach((socket) => {
|
|
128
125
|
try {
|
|
129
126
|
socket.close(1000, 'Server shutting down');
|
package/dist/utils/database.js
CHANGED
|
@@ -67,6 +67,12 @@ export function initializeDatabase(db) {
|
|
|
67
67
|
db.exec(`CREATE INDEX IF NOT EXISTS idx_decks_workspace_id ON decks(workspace_id);`);
|
|
68
68
|
db.exec(`CREATE INDEX IF NOT EXISTS idx_terminals_deck_id ON terminals(deck_id);`);
|
|
69
69
|
}
|
|
70
|
+
export function clearOrphanedTerminals(db) {
|
|
71
|
+
try {
|
|
72
|
+
db.exec('DELETE FROM terminals');
|
|
73
|
+
}
|
|
74
|
+
catch { /* table may not exist yet */ }
|
|
75
|
+
}
|
|
70
76
|
export function loadPersistedState(db, workspaces, workspacePathIndex, decks) {
|
|
71
77
|
const workspaceRows = db
|
|
72
78
|
.prepare('SELECT id, name, path, created_at FROM workspaces ORDER BY created_at ASC')
|