clideck 1.30.3 → 1.30.4
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/agent-presets.json +22 -4
- package/config.js +2 -1
- package/handlers.js +61 -56
- package/package.json +1 -1
- package/public/img/clideck-agent-dark.svg +25 -0
- package/public/img/clideck-agent-light.svg +25 -0
- package/public/js/creator.js +7 -2
- package/public/js/settings.js +72 -2
- package/public/js/terminals.js +2 -0
- package/public/js/utils.js +1 -0
- package/sessions.js +2 -2
- package/telemetry-receiver.js +8 -1
package/agent-presets.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"canResume": true,
|
|
11
11
|
"resumeCommand": "claude --resume {{sessionId}}",
|
|
12
12
|
"sessionIdPattern": "Session ID:\\s+([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})",
|
|
13
|
-
"outputMarker": "
|
|
13
|
+
"outputMarker": "⏺",
|
|
14
14
|
"telemetryConfigPath": "~/.claude/settings.json",
|
|
15
15
|
"telemetrySetup": "Required for working/idle status, Autopilot, notifications, and mobile remote.\n\nCliDeck will add start/stop hooks to ~/.claude/settings.json. Claude will ask for one-time approval on next launch.",
|
|
16
16
|
"telemetryAutoSetup": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"canResume": true,
|
|
36
36
|
"resumeCommand": "codex resume {{sessionId}}",
|
|
37
37
|
"sessionIdPattern": "Session:\\s+([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})",
|
|
38
|
-
"outputMarker": "
|
|
38
|
+
"outputMarker": "•",
|
|
39
39
|
"telemetryConfigPath": "~/.codex/config.toml",
|
|
40
40
|
"telemetryEnv": {
|
|
41
41
|
"OTEL_LOGS_EXPORTER": "otlp",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"canResume": true,
|
|
60
60
|
"resumeCommand": "gemini --resume {{sessionId}}",
|
|
61
61
|
"sessionIdPattern": "Session ID:\\s+([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})",
|
|
62
|
-
"outputMarker": "
|
|
62
|
+
"outputMarker": "✦",
|
|
63
63
|
"telemetryConfigPath": "~/.gemini/settings.json",
|
|
64
64
|
"telemetrySetup": "Required for working/idle status, resume, Autopilot, notifications, and mobile remote.\n\nCliDeck will add BeforeAgent/AfterAgent/SessionEnd/BeforeTool hooks to ~/.gemini/settings.json.",
|
|
65
65
|
"telemetryAutoSetup": {
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"canResume": true,
|
|
78
78
|
"resumeCommand": "opencode --session {{sessionId}}",
|
|
79
79
|
"sessionIdPattern": "(ses_[a-zA-Z0-9]{10,})",
|
|
80
|
-
"outputMarker": "
|
|
80
|
+
"outputMarker": "│",
|
|
81
81
|
"bridge": "opencode",
|
|
82
82
|
"pluginPath": "~/.config/opencode/plugins/clideck-bridge.js",
|
|
83
83
|
"pluginSetup": "Install the CliDeck bridge plugin to enable real-time status and resume.\n\ncp opencode-plugin/clideck-bridge.js ~/.config/opencode/plugins/",
|
|
@@ -85,6 +85,24 @@
|
|
|
85
85
|
"label": "Install plugin"
|
|
86
86
|
}
|
|
87
87
|
},
|
|
88
|
+
{
|
|
89
|
+
"presetId": "clideck-agent",
|
|
90
|
+
"name": "Clideck Agent",
|
|
91
|
+
"icon": "/img/clideck-agent.svg",
|
|
92
|
+
"command": "clideck-agent",
|
|
93
|
+
"isAgent": true,
|
|
94
|
+
"canResume": true,
|
|
95
|
+
"resumeCommand": "clideck-agent --resume {{sessionId}}",
|
|
96
|
+
"sessionIdPattern": null,
|
|
97
|
+
"outputMarker": "•",
|
|
98
|
+
"telemetryEnabled": true,
|
|
99
|
+
"telemetryEnv": {
|
|
100
|
+
"OTEL_LOGS_EXPORTER": "otlp",
|
|
101
|
+
"OTEL_EXPORTER_OTLP_PROTOCOL": "http/json",
|
|
102
|
+
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:{{port}}",
|
|
103
|
+
"OTEL_LOGS_EXPORT_INTERVAL": "2000"
|
|
104
|
+
}
|
|
105
|
+
},
|
|
88
106
|
{
|
|
89
107
|
"presetId": "shell",
|
|
90
108
|
"name": "Shell",
|
package/config.js
CHANGED
|
@@ -124,7 +124,8 @@ function migrate(cfg) {
|
|
|
124
124
|
if (cmd.sessionIdPattern === undefined) cmd.sessionIdPattern = preset?.sessionIdPattern || null;
|
|
125
125
|
if (cmd.outputMarker === undefined) cmd.outputMarker = preset?.outputMarker || null;
|
|
126
126
|
// Claude Code telemetry is built-in, always on
|
|
127
|
-
if (preset?.
|
|
127
|
+
if (preset?.telemetryEnabled === true) cmd.telemetryEnabled = true;
|
|
128
|
+
else if (preset?.presetId === 'claude-code') cmd.telemetryEnabled = true;
|
|
128
129
|
else if (cmd.telemetryEnabled === undefined) cmd.telemetryEnabled = false;
|
|
129
130
|
if (cmd.telemetryStatus === undefined) cmd.telemetryStatus = null;
|
|
130
131
|
// Sync bridge config from preset
|
package/handlers.js
CHANGED
|
@@ -139,64 +139,69 @@ function detectTelemetryConfig(c) {
|
|
|
139
139
|
const home = os.homedir();
|
|
140
140
|
const port = '4000';
|
|
141
141
|
let changed = false;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
142
|
+
const attemptedRepairs = new Set();
|
|
143
|
+
|
|
144
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
145
|
+
let repairedAny = false;
|
|
146
|
+
for (const cmd of c.commands || []) {
|
|
147
|
+
const bin = binName(cmd.command);
|
|
148
|
+
const preset = presets.find(p => binName(p.command) === bin);
|
|
149
|
+
if (!preset) continue;
|
|
150
|
+
let detected = false;
|
|
151
|
+
let reason = '';
|
|
152
|
+
if (preset.presetId === 'claude-code') {
|
|
153
|
+
try {
|
|
154
|
+
const s = JSON.parse(readFileSync(join(home, '.claude', 'settings.json'), 'utf8'));
|
|
155
|
+
const hooks = s.hooks || {};
|
|
156
|
+
detected = hasExistingHook(hooks.UserPromptSubmit, 'claude-hook.js', 'start')
|
|
157
|
+
&& hasExistingHook(hooks.Stop, 'claude-hook.js', 'stop')
|
|
158
|
+
&& hasExistingHook(hooks.StopFailure, 'claude-hook.js', 'stop')
|
|
159
|
+
&& hasExistingHook(hooks.PreToolUse, 'claude-hook.js', 'menu')
|
|
160
|
+
&& hooks.Notification?.some(h => h.matcher === 'idle_prompt' && hasExistingHook([h], 'claude-hook.js', 'idle'));
|
|
161
|
+
if (!detected) reason = 'Needs re-patch';
|
|
162
|
+
} catch {}
|
|
163
|
+
} else if (preset.presetId === 'codex') {
|
|
164
|
+
try {
|
|
165
|
+
const content = readFileSync(join(home, '.codex', 'config.toml'), 'utf8');
|
|
166
|
+
detected = codexConfigLooksHealthy(content, port);
|
|
167
|
+
if (!detected) reason = 'Needs re-patch';
|
|
168
|
+
} catch {}
|
|
169
|
+
} else if (preset.presetId === 'gemini-cli') {
|
|
170
|
+
try {
|
|
171
|
+
const s = JSON.parse(readFileSync(join(home, '.gemini', 'settings.json'), 'utf8'));
|
|
172
|
+
const hooks = s.hooks || {};
|
|
173
|
+
detected = hasExistingHook(hooks.BeforeAgent, 'gemini-hook.js', 'start')
|
|
174
|
+
&& hasExistingHook(hooks.AfterAgent, 'gemini-hook.js', 'stop')
|
|
175
|
+
&& hasExistingHook(hooks.SessionEnd, 'gemini-hook.js', 'stop')
|
|
176
|
+
&& hasExistingHook(hooks.BeforeTool, 'gemini-hook.js', 'menu');
|
|
177
|
+
if (!detected) reason = 'Needs re-patch';
|
|
178
|
+
} catch {}
|
|
179
|
+
} else if (preset.presetId === 'opencode') {
|
|
180
|
+
detected = existsSync(join(opencodePluginDir, 'clideck-bridge.js')) || existsSync(join(opencodePluginDir, 'termix-bridge.js'));
|
|
174
181
|
if (!detected) reason = 'Needs re-patch';
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
repairedAny = true;
|
|
187
|
-
continue;
|
|
182
|
+
} else { continue; }
|
|
183
|
+
if (preset.available && preset.minVersion && !preset.versionOk) {
|
|
184
|
+
detected = false;
|
|
185
|
+
reason = `Update required (${preset.minVersion}+)`;
|
|
186
|
+
} else if (!detected && cmd.telemetryEnabled && preset.telemetryAutoSetup && preset.available && preset.versionOk && !attemptedRepairs.has(preset.presetId)) {
|
|
187
|
+
attemptedRepairs.add(preset.presetId);
|
|
188
|
+
const repaired = applyTelemetryConfig(preset);
|
|
189
|
+
if (repaired.success) {
|
|
190
|
+
repairedAny = true;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
188
193
|
}
|
|
194
|
+
const nextEnabled = detected || (!!cmd.telemetryEnabled && !reason.startsWith('Update required'));
|
|
195
|
+
const nextStatus = detected ? { ok: true } : { ok: false, error: reason || 'Needs setup' };
|
|
196
|
+
if (cmd.telemetryEnabled !== nextEnabled || JSON.stringify(cmd.telemetryStatus || null) !== JSON.stringify(nextStatus)) {
|
|
197
|
+
cmd.telemetryEnabled = nextEnabled;
|
|
198
|
+
cmd.telemetryStatus = nextStatus;
|
|
199
|
+
changed = true;
|
|
200
|
+
}
|
|
201
|
+
preset.health = detected ? { ok: true } : { ok: false, reason: reason || 'Needs setup' };
|
|
189
202
|
}
|
|
190
|
-
|
|
191
|
-
const nextStatus = detected ? { ok: true } : { ok: false, error: reason || 'Needs setup' };
|
|
192
|
-
if (cmd.telemetryEnabled !== nextEnabled || JSON.stringify(cmd.telemetryStatus || null) !== JSON.stringify(nextStatus)) {
|
|
193
|
-
cmd.telemetryEnabled = nextEnabled;
|
|
194
|
-
cmd.telemetryStatus = nextStatus;
|
|
195
|
-
changed = true;
|
|
196
|
-
}
|
|
197
|
-
preset.health = detected ? { ok: true } : { ok: false, reason: reason || 'Needs setup' };
|
|
203
|
+
if (!repairedAny) break;
|
|
198
204
|
}
|
|
199
|
-
if (repairedAny) return detectTelemetryConfig(c) || true;
|
|
200
205
|
if (changed) console.log('Config: synced telemetry/plugin state from detected config files');
|
|
201
206
|
return changed;
|
|
202
207
|
}
|
|
@@ -251,10 +256,10 @@ function onConnection(ws) {
|
|
|
251
256
|
if (choices && sess.presetId === 'codex') {
|
|
252
257
|
const last = require('./telemetry-receiver').getLastEvent(msg.id);
|
|
253
258
|
if (!last.startsWith('codex.sse_event:response.completed')) {
|
|
254
|
-
console.log(`[codex] menu rejected — lastEvent=${last} session=${msg.id.slice(0,8)}`);
|
|
259
|
+
// console.log(`[codex] menu rejected — lastEvent=${last} session=${msg.id.slice(0,8)}`);
|
|
255
260
|
choices = null;
|
|
256
261
|
} else {
|
|
257
|
-
console.log(`[codex] menu accepted session=${msg.id.slice(0,8)}`);
|
|
262
|
+
// console.log(`[codex] menu accepted session=${msg.id.slice(0,8)}`);
|
|
258
263
|
}
|
|
259
264
|
}
|
|
260
265
|
if (choices && sess.presetId === 'claude-code' && msg.menuVersion && (sess._menuConsumedVersion || 0) >= msg.menuVersion) {
|
package/package.json
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none">
|
|
2
|
+
<title>Clideck Agent</title>
|
|
3
|
+
<defs>
|
|
4
|
+
<linearGradient id="bubbleGradient" x1="64" y1="72" x2="448" y2="408" gradientUnits="userSpaceOnUse">
|
|
5
|
+
<stop offset="0" stop-color="#2C2F38"/>
|
|
6
|
+
<stop offset="1" stop-color="#171A20"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
<linearGradient id="glyphGradient" x1="122" y1="150" x2="395" y2="355" gradientUnits="userSpaceOnUse">
|
|
9
|
+
<stop offset="0" stop-color="#FFFFFF"/>
|
|
10
|
+
<stop offset="1" stop-color="#E7EAF0"/>
|
|
11
|
+
</linearGradient>
|
|
12
|
+
<filter id="glyphShadow" x="82" y="126" width="350" height="254" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
13
|
+
<feOffset dx="0" dy="10"/>
|
|
14
|
+
<feGaussianBlur stdDeviation="10"/>
|
|
15
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0.03 0 0 0 0 0.04 0 0 0 0 0.07 0 0 0 0.35 0"/>
|
|
16
|
+
<feBlend in2="SourceGraphic" result="shape"/>
|
|
17
|
+
</filter>
|
|
18
|
+
</defs>
|
|
19
|
+
<path d="M120 70H392C435.078 70 470 104.922 470 148V266C470 309.078 435.078 344 392 344H215.642L118.159 416.774C111.875 421.467 102.86 417.2 102.562 409.363L99.996 344H88C52.654 344 24 315.346 24 280V166C24 112.98 66.98 70 120 70Z" fill="url(#bubbleGradient)"/>
|
|
20
|
+
<path d="M120 70H392C435.078 70 470 104.922 470 148V266C470 309.078 435.078 344 392 344H215.642L118.159 416.774C111.875 421.467 102.86 417.2 102.562 409.363L99.996 344H88C52.654 344 24 315.346 24 280V166C24 112.98 66.98 70 120 70Z" stroke="#3E4350" stroke-width="2"/>
|
|
21
|
+
<g filter="url(#glyphShadow)" stroke="url(#glyphGradient)" stroke-linecap="round" stroke-linejoin="round">
|
|
22
|
+
<path d="M134 168L245 256L134 344" stroke-width="40"/>
|
|
23
|
+
<path d="M292 344H390" stroke-width="38"/>
|
|
24
|
+
</g>
|
|
25
|
+
</svg>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none">
|
|
2
|
+
<title>Clideck Agent</title>
|
|
3
|
+
<defs>
|
|
4
|
+
<linearGradient id="bubbleGradient" x1="64" y1="72" x2="448" y2="408" gradientUnits="userSpaceOnUse">
|
|
5
|
+
<stop offset="0" stop-color="#F7F8FB"/>
|
|
6
|
+
<stop offset="1" stop-color="#E5E9F1"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
<linearGradient id="glyphGradient" x1="122" y1="150" x2="395" y2="355" gradientUnits="userSpaceOnUse">
|
|
9
|
+
<stop offset="0" stop-color="#1D212B"/>
|
|
10
|
+
<stop offset="1" stop-color="#3D4656"/>
|
|
11
|
+
</linearGradient>
|
|
12
|
+
<filter id="glyphShadow" x="82" y="126" width="350" height="254" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
13
|
+
<feOffset dx="0" dy="6"/>
|
|
14
|
+
<feGaussianBlur stdDeviation="7"/>
|
|
15
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0.47 0 0 0 0 0.53 0 0 0 0 0.63 0 0 0 0.18 0"/>
|
|
16
|
+
<feBlend in2="SourceGraphic" result="shape"/>
|
|
17
|
+
</filter>
|
|
18
|
+
</defs>
|
|
19
|
+
<path d="M120 70H392C435.078 70 470 104.922 470 148V266C470 309.078 435.078 344 392 344H215.642L118.159 416.774C111.875 421.467 102.86 417.2 102.562 409.363L99.996 344H88C52.654 344 24 315.346 24 280V166C24 112.98 66.98 70 120 70Z" fill="url(#bubbleGradient)"/>
|
|
20
|
+
<path d="M120 70H392C435.078 70 470 104.922 470 148V266C470 309.078 435.078 344 392 344H215.642L118.159 416.774C111.875 421.467 102.86 417.2 102.562 409.363L99.996 344H88C52.654 344 24 315.346 24 280V166C24 112.98 66.98 70 120 70Z" stroke="#C6CEDA" stroke-width="2"/>
|
|
21
|
+
<g filter="url(#glyphShadow)" stroke="url(#glyphGradient)" stroke-linecap="round" stroke-linejoin="round">
|
|
22
|
+
<path d="M134 168L245 256L134 344" stroke-width="40"/>
|
|
23
|
+
<path d="M292 344H390" stroke-width="38"/>
|
|
24
|
+
</g>
|
|
25
|
+
</svg>
|
package/public/js/creator.js
CHANGED
|
@@ -27,6 +27,11 @@ function findCommandForPreset(p) {
|
|
|
27
27
|
|| state.cfg.commands.find(c => binName(c.command) === binName(p.command));
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
function telemetryEnabledForPreset(preset, existing) {
|
|
31
|
+
if (preset?.telemetryEnabled === true) return true;
|
|
32
|
+
return !!existing?.telemetryEnabled;
|
|
33
|
+
}
|
|
34
|
+
|
|
30
35
|
// True if preset binary is missing and the configured command is unchanged from the preset default
|
|
31
36
|
function isPresetMissing(p) {
|
|
32
37
|
if (p.available !== false) return false;
|
|
@@ -115,7 +120,7 @@ function createFromPreset(preset, sessionName, cwd, projectId) {
|
|
|
115
120
|
resumeCommand: preset.resumeCommand,
|
|
116
121
|
sessionIdPattern: preset.sessionIdPattern,
|
|
117
122
|
outputMarker: preset.outputMarker || null,
|
|
118
|
-
telemetryEnabled:
|
|
123
|
+
telemetryEnabled: telemetryEnabledForPreset(preset),
|
|
119
124
|
telemetryStatus: null,
|
|
120
125
|
bridge: preset.bridge,
|
|
121
126
|
};
|
|
@@ -270,7 +275,7 @@ export function openCreator() {
|
|
|
270
275
|
if (!preset) return;
|
|
271
276
|
let cmd = findCommandForPreset(preset);
|
|
272
277
|
if (!cmd) {
|
|
273
|
-
cmd = { id: crypto.randomUUID(), presetId: preset.presetId, label: preset.name, icon: preset.icon, command: preset.command, enabled: true, defaultPath: '', isAgent: preset.isAgent, canResume: preset.canResume, resumeCommand: preset.resumeCommand, sessionIdPattern: preset.sessionIdPattern, outputMarker: preset.outputMarker || null, telemetryEnabled:
|
|
278
|
+
cmd = { id: crypto.randomUUID(), presetId: preset.presetId, label: preset.name, icon: preset.icon, command: preset.command, enabled: true, defaultPath: '', isAgent: preset.isAgent, canResume: preset.canResume, resumeCommand: preset.resumeCommand, sessionIdPattern: preset.sessionIdPattern, outputMarker: preset.outputMarker || null, telemetryEnabled: telemetryEnabledForPreset(preset), telemetryStatus: null, bridge: preset.bridge };
|
|
274
279
|
state.cfg.commands.push(cmd);
|
|
275
280
|
send({ type: 'config.update', config: state.cfg });
|
|
276
281
|
}
|
package/public/js/settings.js
CHANGED
|
@@ -24,15 +24,69 @@ document.getElementById('settings-nav').addEventListener('click', (e) => {
|
|
|
24
24
|
if (btn) switchCategory(btn.dataset.cat);
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
+
function captureSettingsFocus() {
|
|
28
|
+
const active = document.activeElement;
|
|
29
|
+
if (!active || !(active instanceof HTMLElement)) return null;
|
|
30
|
+
if (!active.closest('#panel-settings')) return null;
|
|
31
|
+
|
|
32
|
+
const snapshot = {
|
|
33
|
+
id: active.id || null,
|
|
34
|
+
selector: null,
|
|
35
|
+
cardIdx: null,
|
|
36
|
+
selectionStart: null,
|
|
37
|
+
selectionEnd: null,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const card = active.closest('.agent-card');
|
|
41
|
+
if (card) {
|
|
42
|
+
snapshot.cardIdx = card.dataset.idx || null;
|
|
43
|
+
for (const cls of ['agent-name', 'agent-command', 'agent-enabled', 'agent-is-agent', 'agent-can-resume', 'agent-resume-cmd']) {
|
|
44
|
+
if (active.classList.contains(cls)) {
|
|
45
|
+
snapshot.selector = `.${cls}`;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (typeof active.selectionStart === 'number' && typeof active.selectionEnd === 'number') {
|
|
52
|
+
snapshot.selectionStart = active.selectionStart;
|
|
53
|
+
snapshot.selectionEnd = active.selectionEnd;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return snapshot;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function restoreSettingsFocus(snapshot) {
|
|
60
|
+
if (!snapshot) return;
|
|
61
|
+
|
|
62
|
+
let target = null;
|
|
63
|
+
if (snapshot.id) target = document.getElementById(snapshot.id);
|
|
64
|
+
if (!target && snapshot.cardIdx != null && snapshot.selector) {
|
|
65
|
+
target = document.querySelector(`.agent-card[data-idx="${snapshot.cardIdx}"] ${snapshot.selector}`);
|
|
66
|
+
}
|
|
67
|
+
if (!(target instanceof HTMLElement)) return;
|
|
68
|
+
|
|
69
|
+
target.focus({ preventScroll: true });
|
|
70
|
+
if (
|
|
71
|
+
typeof snapshot.selectionStart === 'number' &&
|
|
72
|
+
typeof snapshot.selectionEnd === 'number' &&
|
|
73
|
+
typeof target.setSelectionRange === 'function'
|
|
74
|
+
) {
|
|
75
|
+
target.setSelectionRange(snapshot.selectionStart, snapshot.selectionEnd);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
27
79
|
// ── Render all ──
|
|
28
80
|
|
|
29
81
|
export function renderSettings() {
|
|
82
|
+
const focusSnapshot = captureSettingsFocus();
|
|
30
83
|
document.getElementById('cfg-default-path').value = state.cfg.defaultPath || '';
|
|
31
84
|
document.getElementById('cfg-confirm-close').checked = state.cfg.confirmClose !== false;
|
|
32
85
|
renderAgentList();
|
|
33
86
|
renderThemeSection();
|
|
34
87
|
renderNotifications();
|
|
35
88
|
updateVersionFooter();
|
|
89
|
+
restoreSettingsFocus(focusSnapshot);
|
|
36
90
|
}
|
|
37
91
|
|
|
38
92
|
export function updateVersionFooter() {
|
|
@@ -107,6 +161,22 @@ function telemetryPreset(cmd) {
|
|
|
107
161
|
return (state.presets || []).find(p => binName(p.command) === bin);
|
|
108
162
|
}
|
|
109
163
|
|
|
164
|
+
function presetForCommand(existing, command) {
|
|
165
|
+
const presets = state.presets || [];
|
|
166
|
+
if (existing?.presetId) {
|
|
167
|
+
const byId = presets.find(p => p.presetId === existing.presetId);
|
|
168
|
+
if (byId) return byId;
|
|
169
|
+
}
|
|
170
|
+
const bin = binName(command || existing?.command || '');
|
|
171
|
+
return presets.find(p => binName(p.command) === bin) || null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function telemetryEnabledForCommand(existing, command) {
|
|
175
|
+
const preset = presetForCommand(existing, command);
|
|
176
|
+
if (preset?.telemetryEnabled === true) return true;
|
|
177
|
+
return !!existing?.telemetryEnabled;
|
|
178
|
+
}
|
|
179
|
+
|
|
110
180
|
function integrationSection(c) {
|
|
111
181
|
const preset = telemetryPreset(c);
|
|
112
182
|
if (!preset) return '';
|
|
@@ -217,7 +287,7 @@ function openPresetMenu(anchorEl) {
|
|
|
217
287
|
enabled: true, defaultPath: '', isAgent: p.isAgent, canResume: p.canResume,
|
|
218
288
|
resumeCommand: p.resumeCommand, sessionIdPattern: p.sessionIdPattern,
|
|
219
289
|
outputMarker: p.outputMarker || null,
|
|
220
|
-
telemetryEnabled:
|
|
290
|
+
telemetryEnabled: telemetryEnabledForCommand({ presetId: p.presetId, command: p.command }, p.command),
|
|
221
291
|
telemetryStatus: null,
|
|
222
292
|
bridge: p.bridge,
|
|
223
293
|
});
|
|
@@ -453,7 +523,7 @@ function saveConfig() {
|
|
|
453
523
|
resumeCommand: card.querySelector('.agent-resume-cmd')?.value.trim() || null,
|
|
454
524
|
sessionIdPattern: existing.sessionIdPattern || null,
|
|
455
525
|
outputMarker: existing.outputMarker || null,
|
|
456
|
-
telemetryEnabled: existing
|
|
526
|
+
telemetryEnabled: telemetryEnabledForCommand(existing, command),
|
|
457
527
|
telemetryStatus: existing.telemetryStatus || null,
|
|
458
528
|
bridge: existing.bridge,
|
|
459
529
|
};
|
package/public/js/terminals.js
CHANGED
|
@@ -185,6 +185,7 @@ function openMenu(sessionId, anchor) {
|
|
|
185
185
|
// Project submenu items
|
|
186
186
|
if (projects.length) {
|
|
187
187
|
html += `<div class="px-3 py-1 text-[10px] font-semibold uppercase tracking-wider text-slate-600">Move to project</div>`;
|
|
188
|
+
html += `<div class="tmx-scroll py-0.5" style="max-height:10rem;overflow-y:auto">`;
|
|
188
189
|
for (const p of projects) {
|
|
189
190
|
const active = entry?.projectId === p.id;
|
|
190
191
|
html += `<button class="menu-action flex items-center gap-2 w-full px-3 py-1.5 text-sm ${active ? 'text-blue-400' : 'text-slate-300'} hover:bg-slate-700 transition-colors text-left" data-action="project" data-project-id="${p.id}">
|
|
@@ -192,6 +193,7 @@ function openMenu(sessionId, anchor) {
|
|
|
192
193
|
${esc(p.name)}${active ? ' ✓' : ''}
|
|
193
194
|
</button>`;
|
|
194
195
|
}
|
|
196
|
+
html += `</div>`;
|
|
195
197
|
if (entry?.projectId) {
|
|
196
198
|
html += `<button class="menu-action flex items-center gap-2 w-full px-3 py-1.5 text-sm text-slate-500 hover:bg-slate-700 transition-colors text-left" data-action="unproject">
|
|
197
199
|
<span class="w-2 h-2 rounded-full flex-shrink-0 border border-slate-600"></span>
|
package/public/js/utils.js
CHANGED
|
@@ -29,6 +29,7 @@ const ICON_VARIANTS = {
|
|
|
29
29
|
'/img/codex.png': { dark: '/img/codex-dark.png', light: '/img/codex-light.png' },
|
|
30
30
|
'/img/gemini.png': { all: '/img/gemini-all.png' },
|
|
31
31
|
'/img/opencode.png': { all: '/img/opencode-all.png' },
|
|
32
|
+
'/img/clideck-agent.svg': { dark: '/img/clideck-agent-dark.svg', light: '/img/clideck-agent-light.svg' },
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
export function resolveIconPath(icon) {
|
package/sessions.js
CHANGED
|
@@ -93,7 +93,7 @@ function spawnSession(id, cmd, parts, cwd, name, themeId, commandId, savedToken,
|
|
|
93
93
|
const preset = PRESETS.find(p => binName(p.command) === bin);
|
|
94
94
|
const session = { name, themeId, commandId, cwd, pty: term, chunks: [], chunksSize: 0, sessionToken: savedToken || null, projectId: projectId || null, presetId: preset?.presetId || 'shell', working: undefined };
|
|
95
95
|
sessions.set(id, session);
|
|
96
|
-
transcript.setFinalizeOnIdle(id, ['claude-code', 'codex', 'gemini-cli', 'opencode'].includes(session.presetId) ? session.presetId : null);
|
|
96
|
+
transcript.setFinalizeOnIdle(id, ['claude-code', 'codex', 'gemini-cli', 'opencode', 'clideck-agent'].includes(session.presetId) ? session.presetId : null);
|
|
97
97
|
|
|
98
98
|
// Always watch telemetry-backed agents so OTLP fallback matching can attach
|
|
99
99
|
// early events to this session even when the agent omits clideck.session_id.
|
|
@@ -445,7 +445,7 @@ function getResumable(cfg) {
|
|
|
445
445
|
|
|
446
446
|
function sendBuffers(ws) {
|
|
447
447
|
for (const [id, s] of sessions) {
|
|
448
|
-
if (['claude-code', 'codex', 'gemini-cli', 'opencode'].includes(s.presetId) && !s.working) {
|
|
448
|
+
if (['claude-code', 'codex', 'gemini-cli', 'opencode', 'clideck-agent'].includes(s.presetId) && !s.working) {
|
|
449
449
|
const text = transcript.getReplayText(id, s.presetId);
|
|
450
450
|
if (text) {
|
|
451
451
|
ws.send(JSON.stringify({ type: 'session.history', id, text, replay: true }));
|
package/telemetry-receiver.js
CHANGED
|
@@ -71,7 +71,7 @@ function handleLogs(req, res) {
|
|
|
71
71
|
const resAttrs = parseAttrs(rl.resource?.attributes);
|
|
72
72
|
const sessionId = resAttrs['clideck.session_id'];
|
|
73
73
|
|
|
74
|
-
// service.name values: claude-code, codex_cli_rs, gemini-cli
|
|
74
|
+
// service.name values: claude-code, codex_cli_rs, gemini-cli, clideck-agent
|
|
75
75
|
const serviceName = resAttrs['service.name'] || 'unknown';
|
|
76
76
|
let resolvedId = sessionId;
|
|
77
77
|
|
|
@@ -135,6 +135,13 @@ function handleLogs(req, res) {
|
|
|
135
135
|
broadcastFn?.({ type: 'session.status', id: resolvedId, working: true, source: 'telemetry' });
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
if (serviceName === 'clideck-agent' && eventName === 'clideck.turn_start') {
|
|
139
|
+
broadcastFn?.({ type: 'session.status', id: resolvedId, working: true, source: 'telemetry' });
|
|
140
|
+
}
|
|
141
|
+
if (serviceName === 'clideck-agent' && eventName === 'clideck.agent_idle') {
|
|
142
|
+
broadcastFn?.({ type: 'session.status', id: resolvedId, working: false, source: 'telemetry' });
|
|
143
|
+
}
|
|
144
|
+
|
|
138
145
|
// Codex can announce a function-call phase before the later tool_decision
|
|
139
146
|
// event carries a call_id. Block idle as soon as the tool phase is known,
|
|
140
147
|
// then refine it to call-specific tracking when tool_decision arrives.
|