clideck 1.30.6 → 1.30.8

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/config.js CHANGED
@@ -107,6 +107,23 @@ function matchPreset(cmd) {
107
107
  return PRESETS.find(p => binName(p.command) === bin);
108
108
  }
109
109
 
110
+ function firstCommandToken(command) {
111
+ const raw = String(command || '').trim();
112
+ const m = raw.match(/^(['"])(.*?)\1|^(\S+)/);
113
+ return m ? (m[2] || m[3] || '') : '';
114
+ }
115
+
116
+ function commandPathMissing(command) {
117
+ const token = firstCommandToken(command);
118
+ return !token || (token.startsWith('/') && !existsSync(token));
119
+ }
120
+
121
+ function isShellCommand(cmd, preset) {
122
+ return preset?.presetId === 'shell'
123
+ || cmd.presetId === 'shell'
124
+ || (!cmd.isAgent && !cmd.presetId && String(cmd.label || '').toLowerCase() === 'shell');
125
+ }
126
+
110
127
  function migrate(cfg) {
111
128
  // Migrate profiles → defaultTheme
112
129
  if (cfg.profiles && !cfg.defaultTheme) {
@@ -118,7 +135,15 @@ function migrate(cfg) {
118
135
  if (!cfg.defaultTheme || cfg.defaultTheme === 'solarized-dark') cfg.defaultTheme = 'catppuccin-mocha';
119
136
  // Backfill and sync fields from presets
120
137
  for (const cmd of cfg.commands) {
121
- const preset = cmd.presetId ? PRESETS.find(p => p.presetId === cmd.presetId) : matchPreset(cmd);
138
+ let preset = cmd.presetId ? PRESETS.find(p => p.presetId === cmd.presetId) : matchPreset(cmd);
139
+ if (isShellCommand(cmd, preset)) {
140
+ preset = PRESETS.find(p => p.presetId === 'shell') || preset;
141
+ cmd.presetId = 'shell';
142
+ cmd.label = cmd.label || 'Shell';
143
+ }
144
+ if (preset?.presetId === 'shell' && commandPathMissing(cmd.command)) {
145
+ cmd.command = defaultShell;
146
+ }
122
147
  // Stamp presetId for reliable lookup
123
148
  if (preset && !cmd.presetId) cmd.presetId = preset.presetId;
124
149
  // Icon always syncs from preset — the preset is the source of truth for logos
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clideck",
3
- "version": "1.30.6",
3
+ "version": "1.30.8",
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
@@ -55,6 +55,9 @@ function connect() {
55
55
  state.resumable = msg.list;
56
56
  renderResumable();
57
57
  break;
58
+ case 'error':
59
+ showToast(msg.message || 'CliDeck action failed.', { type: 'error', title: 'CliDeck Error', duration: 5000 });
60
+ break;
58
61
  case 'sessions':
59
62
  {
60
63
  const liveIds = new Set(msg.list.map(s => s.id));
@@ -134,6 +134,40 @@ function ensureCommandForPreset(preset) {
134
134
  return cmd;
135
135
  }
136
136
 
137
+ function ensureShellCommand() {
138
+ const shellPreset = state.presets.find(p => p.presetId === 'shell');
139
+ const command = shellPreset?.command || state.cfg.defaultShell;
140
+ let cmd = state.cfg.commands.find(c => c.presetId === 'shell' || (!c.isAgent && !c.presetId && String(c.label || '').toLowerCase() === 'shell'));
141
+ if (cmd) {
142
+ if (!cmd.command || (cmd.command === '/bin/zsh' && command && command !== '/bin/zsh')) {
143
+ cmd.presetId = 'shell';
144
+ cmd.command = command;
145
+ send({ type: 'config.update', config: state.cfg });
146
+ }
147
+ return cmd;
148
+ }
149
+ if (!command) return null;
150
+ cmd = {
151
+ id: crypto.randomUUID(),
152
+ presetId: 'shell',
153
+ label: 'Shell',
154
+ icon: shellPreset?.icon || 'terminal',
155
+ command,
156
+ enabled: true,
157
+ defaultPath: '',
158
+ isAgent: false,
159
+ canResume: false,
160
+ resumeCommand: null,
161
+ sessionIdPattern: null,
162
+ outputMarker: null,
163
+ telemetryEnabled: false,
164
+ telemetryStatus: null,
165
+ };
166
+ state.cfg.commands.push(cmd);
167
+ send({ type: 'config.update', config: state.cfg });
168
+ return cmd;
169
+ }
170
+
137
171
  export function openCreator() {
138
172
  // Toggle off if already open
139
173
  if (document.getElementById('session-creator')) {
@@ -348,8 +382,11 @@ function showInstallToast(preset) {
348
382
  setTimeout(() => send({ type: 'telemetry.autosetup', presetId: preset.presetId }), 1000);
349
383
  }
350
384
  // Find or create the shell command, then spawn a session running the install
351
- const shellCmd = state.cfg.commands.find(c => c.presetId === 'shell' || (!c.isAgent && !c.presetId));
352
- if (!shellCmd) return;
385
+ const shellCmd = ensureShellCommand();
386
+ if (!shellCmd) {
387
+ showToast('Could not find a shell command to run the installer.', { type: 'error', title: 'Install Failed' });
388
+ return;
389
+ }
353
390
  const installId = crypto.randomUUID();
354
391
  send({ type: 'create', commandId: shellCmd.id, name: `Installing ${preset.name}`, installId, ...estimateSize() });
355
392
  const handler = (e) => {
package/server.js CHANGED
@@ -8,6 +8,10 @@ function terminalLink(url, text = url) {
8
8
  return `\u001B]8;;${url}\u0007${text}\u001B]8;;\u0007`;
9
9
  }
10
10
 
11
+ function openUrlHint() {
12
+ return process.platform === 'darwin' ? 'Cmd+click to open' : 'Ctrl+click to open';
13
+ }
14
+
11
15
  // --- Self-update check (runs before server starts) ---
12
16
  const currentVersion = require('./package.json').version;
13
17
  const { execFile, execSync } = require('child_process');
@@ -291,6 +295,7 @@ server.listen(PORT, HOST, () => {
291
295
  const v = require('./package.json').version;
292
296
  const url = `http://${HOST === '0.0.0.0' ? 'localhost' : HOST}:${PORT}`;
293
297
  const clickableUrl = terminalLink(url);
298
+ const urlHint = openUrlHint();
294
299
  console.log(`
295
300
  \x1b[38;5;105m ╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸\x1b[0m
296
301
 
@@ -305,7 +310,7 @@ server.listen(PORT, HOST, () => {
305
310
 
306
311
  \x1b[38;5;245m v${v}\x1b[0m
307
312
 
308
- \x1b[38;5;252m ▸ Ready at \x1b[38;5;44m${clickableUrl}\x1b[38;5;245m (Cmd+click to open)\x1b[0m
313
+ \x1b[38;5;252m ▸ Ready at \x1b[38;5;44m${clickableUrl}\x1b[38;5;245m (${urlHint})\x1b[0m
309
314
  \x1b[38;5;245m ▸ Stop with \x1b[38;5;252mCtrl+C\x1b[38;5;245m · Restart anytime with \x1b[38;5;252mclideck\x1b[0m
310
315
  ${HOST !== '127.0.0.1' ? '\x1b[38;5;208m ▸ Warning: listening on ' + HOST + ' — no authentication, anyone on the network can connect\x1b[0m\n' : ''}`);
311
316
  });
package/utils.js CHANGED
@@ -1,4 +1,4 @@
1
- const { chmodSync, statSync, readdirSync } = require('fs');
1
+ const { chmodSync, existsSync, statSync, readdirSync } = require('fs');
2
2
  const { dirname, join } = require('path');
3
3
 
4
4
  function ensurePtyHelper() {
@@ -55,7 +55,13 @@ function listDirs(path, showHidden) {
55
55
  }
56
56
  }
57
57
 
58
- const defaultShell = process.platform === 'win32' ? (process.env.COMSPEC || 'cmd.exe') : '/bin/zsh';
58
+ function resolveDefaultShell() {
59
+ if (process.platform === 'win32') return process.env.COMSPEC || 'cmd.exe';
60
+ const candidates = [process.env.SHELL, '/bin/zsh', '/bin/bash', '/bin/sh'].filter(Boolean);
61
+ return candidates.find(shell => existsSync(shell)) || '/bin/sh';
62
+ }
63
+
64
+ const defaultShell = resolveDefaultShell();
59
65
 
60
66
  function binName(command) {
61
67
  const m = command.match(/^(['"])(.*?)\1/);