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 +30 -1
- package/package.json +1 -1
- package/public/js/app.js +52 -0
- package/server.js +37 -0
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 =>
|
|
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
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
|