deckide 3.5.10 → 3.5.12

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/deckide.js CHANGED
@@ -317,8 +317,8 @@ if (command === 'auth') {
317
317
  const genUser = user || 'admin';
318
318
  const genPassword = password || crypto.randomBytes(16).toString('base64url');
319
319
 
320
- if (password && password.length < 8) {
321
- console.error('Error: password must be at least 8 characters.');
320
+ if (password && password.length < 12) {
321
+ console.error('Error: password must be at least 12 characters.');
322
322
  process.exit(1);
323
323
  }
324
324
 
@@ -370,13 +370,14 @@ if (command === 'logs') {
370
370
  if (follow) {
371
371
  const tail = spawn('tail', ['-f', logFile], { stdio: 'inherit' });
372
372
  tail.on('exit', () => process.exit(0));
373
+ await new Promise(() => {}); // Block until tail exits
373
374
  } else {
374
375
  const lines = fs.readFileSync(logFile, 'utf-8');
375
376
  // Show last 50 lines
376
377
  const arr = lines.split('\n');
377
378
  console.log(arr.slice(-51).join('\n'));
379
+ process.exit(0);
378
380
  }
379
- if (!args.includes('-f') && !args.includes('--follow')) process.exit(0);
380
381
  }
381
382
 
382
383
  // ── deckide stop ──
@@ -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, updateTerminalBuffer, loadTerminals } from '../utils/database.js';
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, updateTerminalBuffer, } from './utils/database.js';
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
- // Save terminal buffers and kill all terminals
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');
@@ -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')
package/dist/websocket.js CHANGED
@@ -88,9 +88,12 @@ export function setupWebSocketServer(server, terminals) {
88
88
  const wss = new WebSocketServer({ server });
89
89
  const WS_ALLOWED_ORIGINS = new Set([
90
90
  `http://localhost:${PORT}`,
91
- 'http://localhost:5173',
92
- 'http://localhost:3000',
93
91
  ]);
92
+ // Add dev origins only in development mode
93
+ if (NODE_ENV !== 'production') {
94
+ WS_ALLOWED_ORIGINS.add('http://localhost:5173');
95
+ WS_ALLOWED_ORIGINS.add('http://localhost:3000');
96
+ }
94
97
  // Allow configured CORS origin for WebSocket too
95
98
  if (CORS_ORIGIN && CORS_ORIGIN !== '*') {
96
99
  WS_ALLOWED_ORIGINS.add(CORS_ORIGIN);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deckide",
3
- "version": "3.5.10",
3
+ "version": "3.5.12",
4
4
  "description": "Deck IDE - Browser-based IDE with terminal, file explorer, and git integration",
5
5
  "type": "module",
6
6
  "bin": {