clideck 1.25.0 → 1.25.1

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/handlers.js CHANGED
@@ -28,6 +28,31 @@ for (const p of presets) {
28
28
  }
29
29
  }
30
30
 
31
+ // Check for clideck-remote updates (cached, once per hour)
32
+ let remoteUpdateCache = null;
33
+ let remoteUpdateCheckedAt = 0;
34
+ const REMOTE_UPDATE_INTERVAL = 3600000;
35
+
36
+ function checkRemoteUpdate(ws) {
37
+ const now = Date.now();
38
+ if (remoteUpdateCache && now - remoteUpdateCheckedAt < REMOTE_UPDATE_INTERVAL) {
39
+ if (remoteUpdateCache.available) ws.send(JSON.stringify({ type: 'remote.update', ...remoteUpdateCache }));
40
+ return;
41
+ }
42
+ const shellOpt = process.platform === 'win32';
43
+ require('child_process').execFile('npm', ['list', '-g', 'clideck-remote', '--json', '--depth=0'], { shell: shellOpt, timeout: 10000 }, (err, stdout) => {
44
+ let installed;
45
+ try { installed = JSON.parse(stdout).dependencies['clideck-remote'].version; } catch { return; }
46
+ require('child_process').execFile('npm', ['view', 'clideck-remote', 'version'], { shell: shellOpt, timeout: 10000 }, (err2, stdout2) => {
47
+ if (err2) return;
48
+ const latest = stdout2.trim();
49
+ remoteUpdateCache = { installed, latest, available: latest !== installed };
50
+ remoteUpdateCheckedAt = now;
51
+ if (remoteUpdateCache.available) ws.send(JSON.stringify({ type: 'remote.update', ...remoteUpdateCache }));
52
+ });
53
+ });
54
+ }
55
+
31
56
  // Check which agent binaries are available on PATH
32
57
  const whichCmd = process.platform === 'win32' ? 'where' : 'which';
33
58
  function checkAvailability() {
@@ -253,6 +278,7 @@ function onConnection(ws) {
253
278
  try { ws.send(JSON.stringify({ type: 'remote.status', installed: true, ...JSON.parse(stdout) })); }
254
279
  catch { ws.send(JSON.stringify({ type: 'remote.status', installed: true })); }
255
280
  });
281
+ checkRemoteUpdate(ws);
256
282
  break;
257
283
  }
258
284
 
@@ -289,7 +315,10 @@ function onConnection(ws) {
289
315
  });
290
316
  proc.stdout.on('data', d => ws.send(JSON.stringify({ type: 'remote.install.progress', text: d.toString() })));
291
317
  proc.stderr.on('data', d => ws.send(JSON.stringify({ type: 'remote.install.progress', text: d.toString() })));
292
- proc.on('close', code => ws.send(JSON.stringify({ type: 'remote.install.done', success: code === 0 })));
318
+ proc.on('close', code => {
319
+ remoteUpdateCache = null;
320
+ ws.send(JSON.stringify({ type: 'remote.install.done', success: code === 0 }));
321
+ });
293
322
  break;
294
323
  }
295
324
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clideck",
3
- "version": "1.25.0",
3
+ "version": "1.25.1",
4
4
  "description": "One screen for all your AI coding agents — run, monitor, and manage multiple CLI agents from a single browser tab",
5
5
  "main": "server.js",
6
6
  "bin": {
package/public/js/app.js CHANGED
@@ -224,6 +224,9 @@ function connect() {
224
224
  case 'remote.install.done':
225
225
  handleInstallDone(msg.success);
226
226
  break;
227
+ case 'remote.update':
228
+ showRemoteUpdateToast(msg);
229
+ break;
227
230
  default:
228
231
  if (msg.type?.startsWith('plugin.')) dispatchPluginMessage(msg);
229
232
  break;
@@ -1117,6 +1120,55 @@ function handleInstallDone(success) {
1117
1120
  }
1118
1121
  }
1119
1122
 
1123
+ let remoteUpdateShown = false;
1124
+
1125
+ function showRemoteUpdateToast(msg) {
1126
+ if (remoteUpdateShown) return;
1127
+ remoteUpdateShown = true;
1128
+
1129
+ const toast = document.createElement('div');
1130
+ toast.className = 'fixed bottom-5 right-5 z-[500] w-[360px] bg-slate-800/95 backdrop-blur-sm border border-slate-700/60 rounded-xl shadow-2xl shadow-black/60';
1131
+ toast.style.cssText = 'opacity:0;transform:translateY(12px);transition:opacity 0.3s ease,transform 0.3s ease';
1132
+
1133
+ toast.innerHTML = `
1134
+ <div class="flex items-center gap-2.5 px-4 pt-3.5 pb-1">
1135
+ <svg class="w-5 h-5 flex-shrink-0 text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/></svg>
1136
+ <span class="text-[13px] font-semibold text-slate-200">CliDeck Remote Update</span>
1137
+ <button class="dismiss-btn ml-auto w-6 h-6 flex items-center justify-center rounded-md text-slate-500 hover:text-slate-300 hover:bg-slate-700/50 transition-colors">
1138
+ <svg class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
1139
+ </button>
1140
+ </div>
1141
+ <p class="px-4 pt-1 pb-2.5 text-xs text-slate-400 leading-relaxed">
1142
+ Version <span class="text-slate-300">${esc(msg.latest)}</span> is available (installed: ${esc(msg.installed)}).
1143
+ </p>
1144
+ <div class="px-4 pb-3.5 flex items-center gap-2">
1145
+ <button class="update-btn flex-1 px-3 py-2 text-xs font-medium bg-blue-600 hover:bg-blue-500 text-white rounded-lg transition-colors">Update</button>
1146
+ <button class="dismiss-btn px-3 py-2 text-xs text-slate-500 hover:text-slate-300 transition-colors">Later</button>
1147
+ </div>`;
1148
+
1149
+ const dismiss = () => {
1150
+ toast.style.opacity = '0';
1151
+ toast.style.transform = 'translateY(12px)';
1152
+ setTimeout(() => toast.remove(), 300);
1153
+ };
1154
+
1155
+ toast.querySelectorAll('.dismiss-btn').forEach(b => {
1156
+ b.onclick = () => { dismiss(); setTimeout(() => { remoteUpdateShown = false; }, 600000); };
1157
+ });
1158
+
1159
+ toast.querySelector('.update-btn').onclick = () => {
1160
+ dismiss();
1161
+ remoteUpdateShown = false;
1162
+ document.getElementById('remote-install-log').textContent = '';
1163
+ setRemotePane('installing');
1164
+ openRemoteModal();
1165
+ send({ type: 'remote.install' });
1166
+ };
1167
+
1168
+ document.body.appendChild(toast);
1169
+ requestAnimationFrame(() => { toast.style.opacity = '1'; toast.style.transform = 'translateY(0)'; });
1170
+ }
1171
+
1120
1172
  // Button click
1121
1173
  btnRemote.addEventListener('click', () => {
1122
1174
  if (remoteModalOpen && remoteState !== 'paired') { closeRemoteModal(); return; }
package/server.js CHANGED
@@ -3,6 +3,41 @@ const { readFileSync, existsSync } = require('fs');
3
3
  const { join, extname, resolve } = require('path');
4
4
  const { WebSocketServer } = require('ws');
5
5
  const { ensurePtyHelper } = require('./utils');
6
+
7
+ // --- Self-update check (runs before server starts) ---
8
+ const currentVersion = require('./package.json').version;
9
+ const { execFile, execSync } = require('child_process');
10
+ const shellOpt = process.platform === 'win32';
11
+
12
+ function checkSelfUpdate() {
13
+ return new Promise(ok => {
14
+ // Skip in non-interactive or local dev contexts
15
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return ok();
16
+ if (!__dirname.includes(join('node_modules', 'clideck'))) return ok();
17
+ execFile('npm', ['view', 'clideck', 'version'], { shell: shellOpt, timeout: 10000 }, (err, stdout) => {
18
+ if (err) return ok();
19
+ const latest = stdout.trim();
20
+ if (!latest || latest === currentVersion) return ok();
21
+ const rl = require('readline').createInterface({ input: process.stdin, output: process.stdout });
22
+ rl.question(`\n\x1b[38;5;105m Update available:\x1b[0m \x1b[38;5;245m${currentVersion}\x1b[0m → \x1b[38;5;44m${latest}\x1b[0m\n\n \x1b[38;5;252mUpdate now? [Y/n]\x1b[0m `, answer => {
23
+ rl.close();
24
+ if (answer.trim().toLowerCase() === 'n') return ok();
25
+ console.log('\n \x1b[38;5;245mUpdating...\x1b[0m\n');
26
+ try {
27
+ execSync('npm install -g clideck', { stdio: 'inherit', shell: true });
28
+ console.log('\n \x1b[38;5;44mUpdated to v' + latest + '.\x1b[0m Restart with: \x1b[38;5;252mclideck\x1b[0m\n');
29
+ process.exit(0);
30
+ } catch {
31
+ console.log('\n \x1b[38;5;196mUpdate failed.\x1b[0m Continuing with v' + currentVersion + '.\n');
32
+ ok();
33
+ }
34
+ });
35
+ });
36
+ });
37
+ }
38
+
39
+ checkSelfUpdate().then(() => {
40
+
6
41
  const { onConnection } = require('./handlers');
7
42
  const sessions = require('./sessions');
8
43
 
@@ -124,3 +159,5 @@ server.listen(PORT, '127.0.0.1', () => {
124
159
  \x1b[38;5;245m ▸ Stop with \x1b[38;5;252mCtrl+C\x1b[38;5;245m · Restart anytime with \x1b[38;5;252mclideck\x1b[0m
125
160
  `);
126
161
  });
162
+
163
+ }); // checkSelfUpdate