@jhizzard/termdeck 0.2.3 → 0.2.5

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/README.md CHANGED
@@ -4,9 +4,7 @@
4
4
 
5
5
  A browser-based terminal multiplexer with an onboarding tour, rich per-panel metadata, and **Flashback** — automatic recall of similar past errors, surfaced the moment a panel hits a problem. No asking, no querying, no manual search. TermDeck notices you're stuck and offers the memory.
6
6
 
7
- ![TermDeck dashboardbrowser-based terminal multiplexer with per-panel metadata overlays](assets/hero.jpg)
8
-
9
- *(A fresh hero screenshot with a live Flashback toast is coming in v0.2.1 — see [docs/FOLLOWUP.md](docs/FOLLOWUP.md).)*
7
+ ![TermDeck Flashback in action proactive memory recall on panel error, captured live against the production Supabase store](docs/screenshots/flashback-demo.gif)
10
8
 
11
9
  ---
12
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jhizzard/termdeck",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Browser-based terminal multiplexer with metadata overlays, panel flashback memory recall, and AI-aware session management",
5
5
  "bin": {
6
6
  "termdeck": "./packages/cli/src/index.js"
@@ -24,6 +24,7 @@
24
24
  "dev": "node packages/server/src/index.js",
25
25
  "server": "node packages/server/src/index.js",
26
26
  "start": "NODE_ENV=production node packages/cli/src/index.js",
27
+ "test": "node --test packages/server/tests/**/*.test.js",
27
28
  "install:app": "bash install.sh"
28
29
  },
29
30
  "dependencies": {
@@ -41,7 +41,13 @@ class RAGIntegration {
41
41
  payload,
42
42
  project,
43
43
  timestamp: new Date().toISOString()
44
- }).catch(() => {}); // Silent fail, sync will retry
44
+ }).catch((err) => {
45
+ // Non-fatal — the periodic sync loop will retry this event on its next
46
+ // tick because it's still marked unsynced in the outbox. Log at debug
47
+ // level so the first failure is visible in verbose logs without
48
+ // flooding stdout on routine transient errors.
49
+ console.debug('[mnestra] immediate push failed (sync loop will retry):', err && err.message);
50
+ });
45
51
  }
46
52
  }
47
53
 
@@ -33,7 +33,16 @@ const PATTERNS = {
33
33
  django: /Starting development server/,
34
34
  httpServer: /Serving HTTP on/,
35
35
  request: /(?:^|\s|")(GET|POST|PUT|DELETE|PATCH)\s+\S+.*?\s(\d{3})/m,
36
- port: /(?:port\s+(\d+)|(?:on|at)\s+(?:https?:\/\/)?[\w.\[\]:]*:(\d+))/i
36
+ // Port detection — matches any of:
37
+ // • "port NNNN" phrase (capture group 1)
38
+ // • URL with http/https scheme, optionally prefixed with "on " or "at "
39
+ // (capture group 2)
40
+ // • bare "on HOST:NNNN" or "at HOST:NNNN" even without a scheme
41
+ // (capture group 2)
42
+ // Port must be 2–5 digits to avoid matching timestamps like "23:40".
43
+ // `_detectPort` reads `match[1] || match[2]`, so both capture groups
44
+ // are live.
45
+ port: /(?:\bport\s+(\d{2,5})\b|(?:https?:\/\/|\bon\s+|\bat\s+)[a-zA-Z0-9.\-_\[\]]+:(\d{2,5})\b)/i
37
46
  },
38
47
  shell: {
39
48
  prompt: /[\$#%❯>]\s*$/m,
@@ -370,16 +379,36 @@ class SessionManager {
370
379
  return Array.from(this.sessions.values()).map(s => s.toJSON());
371
380
  }
372
381
 
382
+ // Fields a client is allowed to modify via PATCH /api/sessions/:id.
383
+ // Explicit whitelist so a malicious or buggy client cannot inject
384
+ // arbitrary metadata (e.g. overwriting `pid`, `exitCode`, `lastCommands`,
385
+ // or mutating internal pattern state). Server-mutable fields like
386
+ // `status`, `lastActivity`, `detectedPort` are intentionally excluded —
387
+ // those are driven by the output analyzer, not the client.
388
+ static PATCHABLE_META_FIELDS = new Set([
389
+ 'theme',
390
+ 'label',
391
+ 'project',
392
+ 'ragEnabled',
393
+ 'flashbackEnabled'
394
+ ]);
395
+
373
396
  updateMeta(id, updates) {
374
397
  const session = this.sessions.get(id);
375
398
  if (!session) return null;
399
+ if (!updates || typeof updates !== 'object') return session;
376
400
 
377
- Object.assign(session.meta, updates);
401
+ const applied = {};
402
+ for (const [key, val] of Object.entries(updates)) {
403
+ if (!SessionManager.PATCHABLE_META_FIELDS.has(key)) continue;
404
+ session.meta[key] = val;
405
+ applied[key] = val;
406
+ }
378
407
 
379
408
  // Persist theme changes to SQLite
380
- if (updates.theme && this.db) {
409
+ if (applied.theme && this.db) {
381
410
  this.db.prepare('UPDATE sessions SET theme = ? WHERE id = ?')
382
- .run(updates.theme, id);
411
+ .run(applied.theme, id);
383
412
  }
384
413
 
385
414
  this._emit('session:updated', session);