clideck 1.22.6 → 1.23.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/.github/pull_request_template.md +27 -0
- package/CONTRIBUTING.md +53 -0
- package/README.md +9 -2
- package/activity.js +6 -1
- package/assets/clideck-themes.jpg +0 -0
- package/handlers.js +94 -20
- package/package.json +1 -1
- package/plugin-loader.js +14 -2
- package/plugins/trim-clip/clideck-plugin.json +3 -1
- package/plugins/voice-input/clideck-plugin.json +3 -1
- package/public/img/clideck-logo-icon.png +0 -0
- package/public/img/clideck-logo-terminal-panel.png +0 -0
- package/public/index.html +191 -2
- package/public/js/app.js +321 -5
- package/public/js/prompts.js +13 -3
- package/public/js/settings.js +1 -1
- package/public/js/terminals.js +29 -4
- package/public/tailwind.css +1 -1
- package/server.js +1 -1
- package/sessions.js +28 -12
- package/telemetry-receiver.js +0 -8
- package/transcript.js +168 -5
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
## What changed
|
|
2
|
+
|
|
3
|
+
<!-- One or two sentences. What does this PR do? -->
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
<!-- What problem does this solve? Link to an issue or discussion if one exists. -->
|
|
8
|
+
|
|
9
|
+
## How to verify
|
|
10
|
+
|
|
11
|
+
<!--
|
|
12
|
+
Steps to test this locally:
|
|
13
|
+
1. Run `node server.js`
|
|
14
|
+
2. Open http://localhost:4000
|
|
15
|
+
3. ...
|
|
16
|
+
-->
|
|
17
|
+
|
|
18
|
+
## What's out of scope
|
|
19
|
+
|
|
20
|
+
<!-- What did you intentionally NOT change? Helps reviewers stay focused. -->
|
|
21
|
+
|
|
22
|
+
## Checklist
|
|
23
|
+
|
|
24
|
+
- [ ] Tested locally with `node server.js`
|
|
25
|
+
- [ ] One focused change, not bundled with unrelated fixes
|
|
26
|
+
- [ ] No new external service dependencies
|
|
27
|
+
- [ ] Does not read, store, or intercept agent prompts/responses
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Contributing to CliDeck
|
|
2
|
+
|
|
3
|
+
This document explains what we accept, what needs discussion first, and how to structure a PR so it gets reviewed fast.
|
|
4
|
+
|
|
5
|
+
## What to contribute
|
|
6
|
+
|
|
7
|
+
**Bug fixes and small improvements** — open a PR directly.
|
|
8
|
+
Crashes, rendering glitches, typos, broken links, edge cases, performance fixes.
|
|
9
|
+
|
|
10
|
+
**New features, architecture changes, or new agent presets** — [start a Discussion](https://github.com/rustykuntz/clideck/discussions) first.
|
|
11
|
+
Describe the problem, your proposed approach, and any trade-offs. If the idea gets a thumbs up, then open a PR.
|
|
12
|
+
|
|
13
|
+
**Plugins** — the best way to contribute new functionality.
|
|
14
|
+
CliDeck has a plugin system so features can be added without touching core code. If your idea can be a plugin, it should be. See the [documentation](https://docs.clideck.dev/) for how to build one.
|
|
15
|
+
|
|
16
|
+
## What we will reject
|
|
17
|
+
|
|
18
|
+
- PRs that change multiple unrelated things. One change per PR.
|
|
19
|
+
- PRs with no description of what changed or why.
|
|
20
|
+
- Formatting-only changes (whitespace, semicolons, reordering imports).
|
|
21
|
+
- Features that add external service dependencies or phone home.
|
|
22
|
+
- Changes that break the zero-interference guarantee — CliDeck never reads, stores, or intercepts agent prompts and responses.
|
|
23
|
+
|
|
24
|
+
## Before you open a PR
|
|
25
|
+
|
|
26
|
+
1. **Test locally.** Run `node server.js`, open `http://localhost:4000`, and verify your change works.
|
|
27
|
+
2. **Keep it focused.** If you found a bug while working on a feature, that's two PRs.
|
|
28
|
+
3. **Match the existing style.** No linter is enforced, but stay consistent with surrounding code.
|
|
29
|
+
4. **Don't bundle dependency changes** unless your PR specifically requires them.
|
|
30
|
+
|
|
31
|
+
## PR structure
|
|
32
|
+
|
|
33
|
+
Fill out the PR template. The key fields:
|
|
34
|
+
|
|
35
|
+
- **What changed** — one or two sentences.
|
|
36
|
+
- **Why** — the problem this solves.
|
|
37
|
+
- **How to verify** — steps to confirm it works.
|
|
38
|
+
- **What's out of scope** — what you intentionally did not change.
|
|
39
|
+
|
|
40
|
+
Small PRs get reviewed faster. If your diff is over 300 lines, consider splitting it.
|
|
41
|
+
|
|
42
|
+
## Built a fork?
|
|
43
|
+
|
|
44
|
+
If you've forked CliDeck and built something — a new agent integration, a workflow improvement, a plugin — open a Discussion in **Show & Tell**. The best fork features get upstreamed.
|
|
45
|
+
|
|
46
|
+
## Licensing of contributions
|
|
47
|
+
|
|
48
|
+
By submitting a contribution that is accepted into the repository, you agree
|
|
49
|
+
that it will be licensed under the MIT License.
|
|
50
|
+
|
|
51
|
+
## Conduct
|
|
52
|
+
|
|
53
|
+
Be constructive. Review comments are about the code, not the person. If a PR is rejected, the reason will be explained.
|
package/README.md
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
One screen for all your AI coding agents.
|
|
4
4
|
|
|
5
|
+
[Documentation](https://docs.clideck.dev/) | [Video Demo](https://youtu.be/hICrtjGAeDk) | [Website](https://clideck.dev/)
|
|
6
|
+
|
|
5
7
|

|
|
6
8
|
|
|
7
|
-
You're running Claude Code, Codex,
|
|
9
|
+
You're running Claude Code, Codex, Gemini CLI, and OpenCode in separate terminals. You switch between them constantly, forget which one finished, and lose sessions when you close the lid. CliDeck puts them all on one screen with live status, so you always know what's happening.
|
|
8
10
|
|
|
9
11
|
CliDeck is a local dashboard that runs all your CLI agents in one browser tab. It tracks which agents are working, which are idle, and notifies you when they need attention. Everything runs on your machine — nothing leaves localhost.
|
|
12
|
+
Switch between agents as easily as switching between chats.
|
|
10
13
|
|
|
11
14
|
## Quick Start
|
|
12
15
|
|
|
@@ -72,6 +75,10 @@ Full setup guides, agent configuration, and plugin development:
|
|
|
72
75
|
|
|
73
76
|
**[Documentation](https://docs.clideck.dev/)**
|
|
74
77
|
|
|
78
|
+
## Acknowledgments
|
|
79
|
+
|
|
80
|
+
Built with [xterm.js](https://xtermjs.org/).
|
|
81
|
+
|
|
75
82
|
## License
|
|
76
83
|
|
|
77
|
-
MIT
|
|
84
|
+
MIT — see [LICENSE](LICENSE).
|
package/activity.js
CHANGED
|
@@ -51,6 +51,11 @@ function stop() {
|
|
|
51
51
|
interval = null;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function isActive(id) {
|
|
55
|
+
const s = stream[id];
|
|
56
|
+
return s ? (Date.now() - s.lastOutAt < 2000) : false;
|
|
57
|
+
}
|
|
58
|
+
|
|
54
59
|
function clear(id) { delete net[id]; delete stream[id]; }
|
|
55
60
|
|
|
56
|
-
module.exports = { start, stop, trackIn, trackOut, clear };
|
|
61
|
+
module.exports = { start, stop, trackIn, trackOut, isActive, clear };
|
|
Binary file
|
package/handlers.js
CHANGED
|
@@ -11,6 +11,23 @@ for (const p of presets) if (p.presetId === 'shell') p.command = defaultShell;
|
|
|
11
11
|
const transcript = require('./transcript');
|
|
12
12
|
const plugins = require('./plugin-loader');
|
|
13
13
|
|
|
14
|
+
const opencodePluginDir = join(
|
|
15
|
+
process.platform === 'win32' ? (process.env.APPDATA || join(os.homedir(), 'AppData', 'Roaming')) : join(os.homedir(), '.config'),
|
|
16
|
+
'opencode', 'plugins'
|
|
17
|
+
);
|
|
18
|
+
// Resolve opencode preset paths for current platform
|
|
19
|
+
for (const p of presets) {
|
|
20
|
+
if (p.presetId !== 'opencode') continue;
|
|
21
|
+
const bridgePath = join(opencodePluginDir, 'clideck-bridge.js');
|
|
22
|
+
if (p.pluginPath) p.pluginPath = bridgePath;
|
|
23
|
+
if (p.pluginSetup) {
|
|
24
|
+
const copyCmd = process.platform === 'win32'
|
|
25
|
+
? `copy opencode-plugin\\clideck-bridge.js "${opencodePluginDir}\\"`
|
|
26
|
+
: `cp opencode-plugin/clideck-bridge.js ${opencodePluginDir}/`;
|
|
27
|
+
p.pluginSetup = `Install the CliDeck bridge plugin to enable real-time status and resume.\n\n${copyCmd}`;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
14
31
|
// Check which agent binaries are available on PATH
|
|
15
32
|
const whichCmd = process.platform === 'win32' ? 'where' : 'which';
|
|
16
33
|
function checkAvailability() {
|
|
@@ -47,8 +64,7 @@ function detectTelemetryConfig(c) {
|
|
|
47
64
|
detected = !!s.telemetry?.enabled && (s.telemetry?.otlpEndpoint || '').includes(`localhost:${port}`);
|
|
48
65
|
} catch {}
|
|
49
66
|
} else if (preset.presetId === 'opencode') {
|
|
50
|
-
|
|
51
|
-
detected = existsSync(join(ocPlugins, 'clideck-bridge.js')) || existsSync(join(ocPlugins, 'termix-bridge.js'));
|
|
67
|
+
detected = existsSync(join(opencodePluginDir, 'clideck-bridge.js')) || existsSync(join(opencodePluginDir, 'termix-bridge.js'));
|
|
52
68
|
} else { continue; }
|
|
53
69
|
if (detected !== !!cmd.telemetryEnabled) {
|
|
54
70
|
cmd.telemetryEnabled = detected;
|
|
@@ -60,14 +76,18 @@ function detectTelemetryConfig(c) {
|
|
|
60
76
|
return changed;
|
|
61
77
|
}
|
|
62
78
|
|
|
79
|
+
function configForClient() {
|
|
80
|
+
return { ...cfg, pluginsDir: plugins.PLUGINS_DIR };
|
|
81
|
+
}
|
|
82
|
+
|
|
63
83
|
function onConnection(ws) {
|
|
64
84
|
sessions.clients.add(ws);
|
|
65
85
|
|
|
66
|
-
ws.send(JSON.stringify({ type: 'config', config:
|
|
86
|
+
ws.send(JSON.stringify({ type: 'config', config: configForClient() }));
|
|
67
87
|
ws.send(JSON.stringify({ type: 'themes', themes }));
|
|
68
88
|
ws.send(JSON.stringify({ type: 'presets', presets }));
|
|
69
89
|
ws.send(JSON.stringify({ type: 'sessions', list: sessions.list() }));
|
|
70
|
-
ws.send(JSON.stringify({ type: 'sessions.resumable', list: sessions.getResumable() }));
|
|
90
|
+
ws.send(JSON.stringify({ type: 'sessions.resumable', list: sessions.getResumable(cfg) }));
|
|
71
91
|
ws.send(JSON.stringify({ type: 'transcript.cache', cache: transcript.getCache() }));
|
|
72
92
|
ws.send(JSON.stringify({ type: 'plugins', list: plugins.getInfo() }));
|
|
73
93
|
sessions.sendBuffers(ws);
|
|
@@ -82,14 +102,20 @@ function onConnection(ws) {
|
|
|
82
102
|
case 'session.restart': console.log('[handler] session.restart', msg.id); sessions.restart(msg, ws, cfg); break;
|
|
83
103
|
case 'input': sessions.input(msg); break;
|
|
84
104
|
case 'session.statusReport':
|
|
85
|
-
if (sessions.getSessions().has(msg.id))
|
|
105
|
+
if (sessions.getSessions().has(msg.id)) {
|
|
106
|
+
plugins.notifyStatus(msg.id, !!msg.working);
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
case 'terminal.buffer':
|
|
110
|
+
require('./transcript').storeBuffer(msg.id, msg.lines);
|
|
111
|
+
sessions.broadcast({ type: 'screen.updated', id: msg.id });
|
|
86
112
|
break;
|
|
87
113
|
case 'resize': sessions.resize(msg); break;
|
|
88
114
|
case 'rename': sessions.rename(msg); break;
|
|
89
|
-
case 'close': sessions.close(msg); break;
|
|
115
|
+
case 'close': sessions.close(msg, cfg); break;
|
|
90
116
|
|
|
91
117
|
case 'config.get':
|
|
92
|
-
ws.send(JSON.stringify({ type: 'config', config:
|
|
118
|
+
ws.send(JSON.stringify({ type: 'config', config: configForClient() }));
|
|
93
119
|
break;
|
|
94
120
|
|
|
95
121
|
case 'checkAvailability':
|
|
@@ -98,10 +124,11 @@ function onConnection(ws) {
|
|
|
98
124
|
break;
|
|
99
125
|
|
|
100
126
|
case 'config.update':
|
|
127
|
+
delete msg.config.pluginsDir;
|
|
101
128
|
cfg = { ...cfg, ...msg.config };
|
|
102
129
|
detectTelemetryConfig(cfg);
|
|
103
130
|
config.save(cfg);
|
|
104
|
-
sessions.broadcast({ type: 'config', config:
|
|
131
|
+
sessions.broadcast({ type: 'config', config: configForClient() });
|
|
105
132
|
break;
|
|
106
133
|
|
|
107
134
|
case 'session.theme': {
|
|
@@ -129,7 +156,7 @@ function onConnection(ws) {
|
|
|
129
156
|
}
|
|
130
157
|
}
|
|
131
158
|
config.save(cfg);
|
|
132
|
-
sessions.broadcast({ type: 'config', config:
|
|
159
|
+
sessions.broadcast({ type: 'config', config: configForClient() });
|
|
133
160
|
ws.send(JSON.stringify({
|
|
134
161
|
type: 'telemetry.autosetup.result',
|
|
135
162
|
presetId: msg.presetId,
|
|
@@ -159,7 +186,7 @@ function onConnection(ws) {
|
|
|
159
186
|
}
|
|
160
187
|
}
|
|
161
188
|
config.save(cfg);
|
|
162
|
-
sessions.broadcast({ type: 'config', config:
|
|
189
|
+
sessions.broadcast({ type: 'config', config: configForClient() });
|
|
163
190
|
break;
|
|
164
191
|
}
|
|
165
192
|
|
|
@@ -185,11 +212,11 @@ function onConnection(ws) {
|
|
|
185
212
|
if (!proj) break;
|
|
186
213
|
// Kill all sessions in this project
|
|
187
214
|
for (const s of sessions.list()) {
|
|
188
|
-
if (s.projectId === msg.id) sessions.close({ id: s.id });
|
|
215
|
+
if (s.projectId === msg.id) sessions.close({ id: s.id }, cfg);
|
|
189
216
|
}
|
|
190
217
|
cfg.projects = cfg.projects.filter(p => p.id !== msg.id);
|
|
191
218
|
config.save(cfg);
|
|
192
|
-
sessions.broadcast({ type: 'config', config:
|
|
219
|
+
sessions.broadcast({ type: 'config', config: configForClient() });
|
|
193
220
|
break;
|
|
194
221
|
}
|
|
195
222
|
|
|
@@ -207,6 +234,55 @@ function onConnection(ws) {
|
|
|
207
234
|
sessions.broadcast({ type: 'plugins', list: plugins.getInfo() });
|
|
208
235
|
break;
|
|
209
236
|
|
|
237
|
+
case 'remote.status': {
|
|
238
|
+
let installed = false;
|
|
239
|
+
try { execFileSync(whichCmd, ['clideck-remote'], { stdio: 'ignore' }); installed = true; } catch {}
|
|
240
|
+
if (!installed) { ws.send(JSON.stringify({ type: 'remote.status', installed: false })); break; }
|
|
241
|
+
require('child_process').execFile('clideck-remote', ['status', '--json'], { timeout: 5000 }, (err, stdout) => {
|
|
242
|
+
if (err) { ws.send(JSON.stringify({ type: 'remote.status', installed: true })); return; }
|
|
243
|
+
try { ws.send(JSON.stringify({ type: 'remote.status', installed: true, ...JSON.parse(stdout) })); }
|
|
244
|
+
catch { ws.send(JSON.stringify({ type: 'remote.status', installed: true })); }
|
|
245
|
+
});
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
case 'remote.pair': {
|
|
250
|
+
require('child_process').execFile('clideck-remote', ['pair', '--json'], { timeout: 15000 }, (err, stdout) => {
|
|
251
|
+
if (err) { ws.send(JSON.stringify({ type: 'remote.error', error: err.message })); return; }
|
|
252
|
+
try { ws.send(JSON.stringify({ type: 'remote.paired', ...JSON.parse(stdout) })); }
|
|
253
|
+
catch { ws.send(JSON.stringify({ type: 'remote.error', error: 'Invalid response from clideck-remote' })); }
|
|
254
|
+
});
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
case 'remote.unpair': {
|
|
259
|
+
require('child_process').execFile('clideck-remote', ['unpair', '--json'], { timeout: 5000 }, (err) => {
|
|
260
|
+
if (err) {
|
|
261
|
+
ws.send(JSON.stringify({ type: 'remote.error', error: err.message }));
|
|
262
|
+
} else {
|
|
263
|
+
sessions.broadcast({ type: 'remote.unpaired' });
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
case 'remote.getHistory': {
|
|
270
|
+
const turns = transcript.getScreenTurns(msg.id, sessions.getSessions().get(msg.id)?.presetId)
|
|
271
|
+
|| transcript.getLastTurns(msg.id, msg.limit || 50);
|
|
272
|
+
ws.send(JSON.stringify({ type: 'remote.history', id: msg.id, turns: turns || [] }));
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
case 'remote.install': {
|
|
277
|
+
const proc = require('child_process').spawn('npm', ['install', '-g', 'clideck-remote'], {
|
|
278
|
+
shell: true, stdio: ['ignore', 'pipe', 'pipe'],
|
|
279
|
+
});
|
|
280
|
+
proc.stdout.on('data', d => ws.send(JSON.stringify({ type: 'remote.install.progress', text: d.toString() })));
|
|
281
|
+
proc.stderr.on('data', d => ws.send(JSON.stringify({ type: 'remote.install.progress', text: d.toString() })));
|
|
282
|
+
proc.on('close', code => ws.send(JSON.stringify({ type: 'remote.install.done', success: code === 0 })));
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
|
|
210
286
|
default:
|
|
211
287
|
if (msg.type?.startsWith('plugin.')) plugins.handleMessage(msg);
|
|
212
288
|
break;
|
|
@@ -256,14 +332,13 @@ function applyTelemetryConfig(preset) {
|
|
|
256
332
|
}
|
|
257
333
|
|
|
258
334
|
if (preset.presetId === 'opencode') {
|
|
259
|
-
const pluginDir = join(home, '.config', 'opencode', 'plugins');
|
|
260
335
|
const src = join(__dirname, 'opencode-plugin', 'clideck-bridge.js');
|
|
261
|
-
mkdirSync(
|
|
262
|
-
copyFileSync(src, join(
|
|
336
|
+
mkdirSync(opencodePluginDir, { recursive: true });
|
|
337
|
+
copyFileSync(src, join(opencodePluginDir, 'clideck-bridge.js'));
|
|
263
338
|
// Remove old termix-bridge.js if present
|
|
264
|
-
const old = join(
|
|
339
|
+
const old = join(opencodePluginDir, 'termix-bridge.js');
|
|
265
340
|
if (existsSync(old)) try { unlinkSync(old); } catch {}
|
|
266
|
-
return { success: true, message:
|
|
341
|
+
return { success: true, message: `Installed bridge plugin to ${opencodePluginDir}` };
|
|
267
342
|
}
|
|
268
343
|
|
|
269
344
|
return { success: false, message: `No auto-setup for ${preset.presetId}` };
|
|
@@ -297,9 +372,8 @@ function removeTelemetryConfig(preset) {
|
|
|
297
372
|
}
|
|
298
373
|
|
|
299
374
|
if (preset.presetId === 'opencode') {
|
|
300
|
-
|
|
301
|
-
try { unlinkSync(join(
|
|
302
|
-
try { unlinkSync(join(dir, 'termix-bridge.js')); } catch {}
|
|
375
|
+
try { unlinkSync(join(opencodePluginDir, 'clideck-bridge.js')); } catch {}
|
|
376
|
+
try { unlinkSync(join(opencodePluginDir, 'termix-bridge.js')); } catch {}
|
|
303
377
|
return { success: true, message: 'Removed bridge plugin' };
|
|
304
378
|
}
|
|
305
379
|
|
package/package.json
CHANGED
package/plugin-loader.js
CHANGED
|
@@ -32,6 +32,7 @@ const plugins = new Map();
|
|
|
32
32
|
const inputHooks = [];
|
|
33
33
|
const outputHooks = [];
|
|
34
34
|
const statusHooks = [];
|
|
35
|
+
const transcriptHooks = [];
|
|
35
36
|
const sessionStatus = new Map(); // sessionId → boolean (dedup multi-client reports)
|
|
36
37
|
const frontendHandlers = new Map();
|
|
37
38
|
let broadcastFn = null;
|
|
@@ -41,7 +42,7 @@ let saveConfigFn = null;
|
|
|
41
42
|
const settingsChangeHandlers = new Map(); // pluginId → [fn]
|
|
42
43
|
|
|
43
44
|
function removeHooks(pluginId) {
|
|
44
|
-
for (const arr of [inputHooks, outputHooks, statusHooks]) {
|
|
45
|
+
for (const arr of [inputHooks, outputHooks, statusHooks, transcriptHooks]) {
|
|
45
46
|
for (let i = arr.length - 1; i >= 0; i--) {
|
|
46
47
|
if (arr[i].pluginId === pluginId) arr.splice(i, 1);
|
|
47
48
|
}
|
|
@@ -111,6 +112,7 @@ function buildApi(pluginId, pluginDir, state) {
|
|
|
111
112
|
onSessionInput(fn) { inputHooks.push({ pluginId, fn }); },
|
|
112
113
|
onSessionOutput(fn) { outputHooks.push({ pluginId, fn }); },
|
|
113
114
|
onStatusChange(fn) { statusHooks.push({ pluginId, fn }); },
|
|
115
|
+
onTranscriptEntry(fn) { transcriptHooks.push({ pluginId, fn }); },
|
|
114
116
|
|
|
115
117
|
sendToFrontend(event, data = {}) {
|
|
116
118
|
broadcastFn?.({ ...data, type: `plugin.${pluginId}.${event}` });
|
|
@@ -184,6 +186,13 @@ function notifyStatus(id, working) {
|
|
|
184
186
|
}
|
|
185
187
|
}
|
|
186
188
|
|
|
189
|
+
function notifyTranscript(id, role, text) {
|
|
190
|
+
for (const h of transcriptHooks) {
|
|
191
|
+
try { h.fn(id, role, text); }
|
|
192
|
+
catch (e) { console.error(`[plugin:${h.pluginId}] transcript error: ${e.message}`); }
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
187
196
|
function updateSetting(pluginId, key, value) {
|
|
188
197
|
// Validate plugin exists (also prevents __proto__ pollution — Map lookup returns undefined)
|
|
189
198
|
const plugin = plugins.get(pluginId);
|
|
@@ -241,6 +250,8 @@ function getInfo() {
|
|
|
241
250
|
id: p.manifest.id,
|
|
242
251
|
name: p.manifest.name,
|
|
243
252
|
version: p.manifest.version,
|
|
253
|
+
author: p.manifest.author || '',
|
|
254
|
+
description: p.manifest.description || '',
|
|
244
255
|
settings: p.manifest.settings || [],
|
|
245
256
|
settingValues: cfg?.pluginSettings?.[p.manifest.id] || {},
|
|
246
257
|
actions: p.actions,
|
|
@@ -279,7 +290,8 @@ function shutdown() {
|
|
|
279
290
|
function clearStatus(id) { sessionStatus.delete(id); }
|
|
280
291
|
|
|
281
292
|
module.exports = {
|
|
293
|
+
PLUGINS_DIR,
|
|
282
294
|
init, shutdown,
|
|
283
|
-
transformInput, notifyOutput, notifyStatus, clearStatus,
|
|
295
|
+
transformInput, notifyOutput, notifyStatus, notifyTranscript, clearStatus,
|
|
284
296
|
handleMessage, updateSetting, getInfo, resolveFile,
|
|
285
297
|
};
|
|
Binary file
|
|
Binary file
|