diff --git a/claude/com.streamdock.ai-monitor.claude.sdPlugin/en.json b/claude/com.streamdock.ai-monitor.claude.sdPlugin/en.json
deleted file mode 100644
index 57d72b0..0000000
--- a/claude/com.streamdock.ai-monitor.claude.sdPlugin/en.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Name": "Claude Desktop Monitor",
- "Description": "Monitor Claude Desktop AI workflow status"
-}
\ No newline at end of file
diff --git a/claude/com.streamdock.ai-monitor.claude.sdPlugin/manifest.json b/claude/com.streamdock.ai-monitor.claude.sdPlugin/manifest.json
deleted file mode 100644
index 793c7de..0000000
--- a/claude/com.streamdock.ai-monitor.claude.sdPlugin/manifest.json
+++ /dev/null
@@ -1,53 +0,0 @@
-{
- "Actions": [
- {
- "Icon": "static/img/claude-icon.svg",
- "Name": "Claude Desktop",
- "States": [
- {
- "Image": "static/img/claude-icon.svg",
- "TitleAlignment": "center",
- "FontSize": "11"
- }
- ],
- "Settings": {
- "pollInterval": 1000
- },
- "Controllers": [
- "Keypad",
- "Information"
- ],
- "UserTitleEnabled": true,
- "SupportedInMultiActions": true,
- "Tooltip": "Claude Desktop 工作流状态监控",
- "UUID": "com.streamdock.ai-monitor.claude"
- }
- ],
- "SDKVersion": 1,
- "Author": "StreamDock",
- "Name": "AI Monitor - Claude Desktop",
- "Icon": "static/img/claude-icon.svg",
- "Category": "AI Monitor",
- "CategoryIcon": "static/img/claude-icon.svg",
- "CodePathWin": "plugin/index.js",
- "CodePathMac": "plugin/index.js",
- "Description": "监控 Claude Desktop AI 工作流状态,在 N4 按钮上实时显示",
- "Version": "1.0.0",
- "URL": "http://10.10.10.14:18003/v6ole/streamdock-ai-monitor",
- "OS": [
- {
- "Platform": "windows",
- "MinimumVersion": "7"
- },
- {
- "Platform": "mac",
- "MinimumVersion": "10.11"
- }
- ],
- "Software": {
- "MinimumVersion": "3.10.188.226"
- },
- "Nodejs": {
- "Version": "20"
- }
-}
\ No newline at end of file
diff --git a/claude/com.streamdock.ai-monitor.claude.sdPlugin/plugin/index.js b/claude/com.streamdock.ai-monitor.claude.sdPlugin/plugin/index.js
deleted file mode 100644
index 6f044b0..0000000
--- a/claude/com.streamdock.ai-monitor.claude.sdPlugin/plugin/index.js
+++ /dev/null
@@ -1,153 +0,0 @@
-const { Plugins, Actions, log } = require('./utils/plugin');
-const fs = require('fs');
-const path = require('path');
-const os = require('os');
-
-const plugin = new Plugins('ai-monitor-claude');
-const timers = {};
-const sessionOffsets = {};
-
-const HOME = os.homedir();
-const SCAN_DIR = path.join(HOME, '.claude', 'projects');
-const POLL_INTERVAL = 1000;
-const IDLE_TIMEOUT_MS = 3000;
-
-// Load logo
-var logoUri = '';
-try {
- var logoPath = path.join(__dirname, '..', 'static', 'img', 'claude-icon.svg');
- logoUri = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(fs.readFileSync(logoPath, 'utf-8'));
-} catch (_) {}
-
-var STATUS = {
- idle: { title: 'Ready', fill: '#22c55e', blink: false, breathe: false },
- prompt: { title: 'Prompt', fill: '#3b82f6', blink: false, breathe: false },
- thinking: { title: 'Thinking', fill: '#f59e0b', blink: false, breathe: true },
- working: { title: 'Working', fill: '#f59e0b', blink: true, breathe: false },
- typing: { title: 'Typing', fill: '#f59e0b', blink: false, breathe: true },
- done: { title: 'Done', fill: '#22c55e', blink: true, breathe: false },
- error: { title: 'Error', fill: '#ef4444', blink: false, breathe: false },
-};
-
-var PRIORITY = ['error', 'working', 'typing', 'thinking', 'prompt', 'done', 'idle'];
-var currentCtx = null, currentState = 'idle', lastActivityTime = 0;
-var animationHandle = null;
-
-function makeSvg(fill, label, blink, breathe) {
- var anim = '';
- if (blink) anim = '';
- else if (breathe) anim = '';
- return '';
-}
-
-function setDisplay(ctx, state) {
- var d = STATUS[state] || STATUS['idle'];
- plugin.setTitle(ctx, d.title);
- if (state === 'idle' && logoUri) plugin.setImage(ctx, logoUri);
- else plugin.setImage(ctx, 'data:image/svg+xml;charset=utf-8,' + makeSvg(d.fill, d.title, d.blink, d.breathe));
-}
-
-function animateTick() {
- if (!currentCtx) return;
- var d = STATUS[currentState] || STATUS['idle'];
- if (d.blink || d.breathe) {
- var now = Date.now(), period = d.blink ? 800 : 2000, phase = (now % period) / period;
- var alpha = d.blink ? (phase < 0.5 ? 1 : 0.2) : 0.4 + 0.6 * Math.sin(phase * Math.PI);
- var r = parseInt(d.fill.slice(1,3),16), g = parseInt(d.fill.slice(3,5),16), b = parseInt(d.fill.slice(5,7),16);
- var color = 'rgb(' + Math.round(16+(r-16)*alpha) + ',' + Math.round(16+(g-16)*alpha) + ',' + Math.round(16+(b-16)*alpha) + ')';
- var svg = '';
- plugin.setImage(currentCtx, 'data:image/svg+xml;charset=utf-8,' + svg);
- }
-}
-
-function setState(ctx, state) {
- if (state === currentState) return;
- if (state === 'done') { setDisplay(ctx, 'done'); currentState = 'done'; setTimeout(function() { if (currentState === 'done') { currentState = 'idle'; setDisplay(ctx, 'idle'); } }, 800); }
- else { currentState = state; setDisplay(ctx, state); }
-}
-
-function normalize(obj) {
- var t = obj.type || '';
- if (t === 'user') return 'prompt';
- if (t === 'assistant') {
- var content = (obj.message || {}).content || [];
- for (var i = 0; i < content.length; i++) {
- var ct = content[i].type || '';
- if (ct === 'tool_use') return 'working';
- if (ct === 'thinking') return 'thinking';
- }
- for (var j = 0; j < content.length; j++) {
- if ((content[j].type || '') === 'text') return 'typing';
- }
- return 'typing';
- }
- if (t === 'system') { if (obj.subtype === 'turn_duration') return 'done'; return null; }
- return null;
-}
-
-function findFiles(rootDir, suffix) {
- var results = [];
- if (!fs.existsSync(rootDir)) return results;
- (function walk(dir, depth) {
- if (depth > 16) return;
- var names; try { names = fs.readdirSync(dir); } catch (_) { return; }
- for (var i = 0; i < names.length; i++) {
- var full = path.join(dir, names[i]);
- var st; try { st = fs.statSync(full); } catch (_) { continue; }
- if (st.isDirectory()) walk(full, depth + 1);
- else if (st.isFile() && names[i].endsWith(suffix)) results.push(full);
- }
- })(rootDir, 0);
- return results;
-}
-
-function tailFile(filePath, ctx) {
- var detected = false;
- try {
- var stat = fs.statSync(filePath);
- var off = sessionOffsets[filePath] || 0;
- if (stat.size < off) { sessionOffsets[filePath] = 0; return false; }
- if (stat.size === off) return false;
- var fd = fs.openSync(filePath, 'r');
- var grow = Math.min(stat.size - off, 65536);
- var buf = Buffer.alloc(grow);
- fs.readSync(fd, buf, 0, grow, off);
- fs.closeSync(fd);
- sessionOffsets[filePath] = stat.size;
- var lines = buf.toString('utf-8').split('\n').filter(function(l) { return l.trim(); });
- var bestPri = PRIORITY.length, bestState = null;
- for (var i = 0; i < lines.length; i++) {
- try { var obj = JSON.parse(lines[i]); var s = normalize(obj); if (s) { var pri = PRIORITY.indexOf(s); if (pri >= 0 && pri < bestPri) { bestPri = pri; bestState = s; } } } catch (_) {}
- }
- if (bestState) { setState(ctx, bestState); detected = true; }
- } catch (_) { delete sessionOffsets[filePath]; }
- return detected;
-}
-
-function poll(ctx) {
- var detected = false;
- var files = findFiles(SCAN_DIR, '.jsonl');
- files.sort(function(a,b) { return fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs; });
- for (var i = 0; i < Math.min(files.length, 3); i++) { if (tailFile(files[i], ctx)) detected = true; }
- if (detected) lastActivityTime = Date.now();
- if (currentState !== 'idle' && currentState !== 'done' && Date.now() - lastActivityTime > IDLE_TIMEOUT_MS) { setState(ctx, 'idle'); }
-}
-
-plugin.claude = new Actions({
- default: { pollInterval: POLL_INTERVAL },
- _willAppear: function(data) {
- var ctx = data.context;
- currentCtx = ctx; currentState = 'idle'; lastActivityTime = Date.now();
- setDisplay(ctx, 'idle');
- poll(ctx);
- timers[ctx] = setInterval(function() { poll(ctx); }, POLL_INTERVAL);
- if (!animationHandle) animationHandle = setInterval(animateTick, 200);
- },
- _willDisappear: function(data) {
- var ctx = data.context;
- if (timers[ctx]) { clearInterval(timers[ctx]); delete timers[ctx]; }
- if (currentCtx === ctx) { currentCtx = null; currentState = 'idle'; }
- if (Object.keys(timers).length === 0 && animationHandle) { clearInterval(animationHandle); animationHandle = null; }
- },
- keyUp: function(data) { poll(data.context); }
-});
\ No newline at end of file
diff --git a/claude/com.streamdock.ai-monitor.claude.sdPlugin/plugin/package-lock.json b/claude/com.streamdock.ai-monitor.claude.sdPlugin/plugin/package-lock.json
deleted file mode 100644
index a1679d1..0000000
--- a/claude/com.streamdock.ai-monitor.claude.sdPlugin/plugin/package-lock.json
+++ /dev/null
@@ -1,186 +0,0 @@
-{
- "name": "ai-monitor-claude",
- "version": "1.0.0",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "ai-monitor-claude",
- "version": "1.0.0",
- "license": "MIT",
- "dependencies": {
- "fs-extra": "^11.3.0",
- "log4js": "^6.9.1",
- "ws": "^8.14.2"
- }
- },
- "node_modules/date-format": {
- "version": "4.0.14",
- "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz",
- "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==",
- "license": "MIT",
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/debug": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/flatted": {
- "version": "3.4.2",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
- "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
- "license": "ISC"
- },
- "node_modules/fs-extra": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz",
- "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==",
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=14.14"
- }
- },
- "node_modules/graceful-fs": {
- "version": "4.2.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
- "license": "ISC"
- },
- "node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/log4js": {
- "version": "6.9.1",
- "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz",
- "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==",
- "license": "Apache-2.0",
- "dependencies": {
- "date-format": "^4.0.14",
- "debug": "^4.3.4",
- "flatted": "^3.2.7",
- "rfdc": "^1.3.0",
- "streamroller": "^3.1.5"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "license": "MIT"
- },
- "node_modules/rfdc": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
- "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
- "license": "MIT"
- },
- "node_modules/streamroller": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz",
- "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==",
- "license": "MIT",
- "dependencies": {
- "date-format": "^4.0.14",
- "debug": "^4.3.4",
- "fs-extra": "^8.1.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/streamroller/node_modules/fs-extra": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
- "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^4.0.0",
- "universalify": "^0.1.0"
- },
- "engines": {
- "node": ">=6 <7 || >=8"
- }
- },
- "node_modules/streamroller/node_modules/jsonfile": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
- "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
- "license": "MIT",
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/streamroller/node_modules/universalify": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
- "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
- "license": "MIT",
- "engines": {
- "node": ">= 4.0.0"
- }
- },
- "node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
- "node_modules/ws": {
- "version": "8.21.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
- "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
- "license": "MIT",
- "engines": {
- "node": ">=10.0.0"
- },
- "peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": ">=5.0.2"
- },
- "peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
- "optional": true
- }
- }
- }
- }
-}
diff --git a/claude/com.streamdock.ai-monitor.claude.sdPlugin/plugin/package.json b/claude/com.streamdock.ai-monitor.claude.sdPlugin/plugin/package.json
deleted file mode 100644
index 122c141..0000000
--- a/claude/com.streamdock.ai-monitor.claude.sdPlugin/plugin/package.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "name": "ai-monitor-claude",
- "version": "1.0.0",
- "author": "StreamDock",
- "main": "index.js",
- "description": "Claude Desktop AI workflow status monitor",
- "license": "MIT",
- "dependencies": {
- "fs-extra": "^11.3.0",
- "log4js": "^6.9.1",
- "ws": "^8.14.2"
- }
-}
\ No newline at end of file
diff --git a/claude/com.streamdock.ai-monitor.claude.sdPlugin/zh_CN.json b/claude/com.streamdock.ai-monitor.claude.sdPlugin/zh_CN.json
deleted file mode 100644
index f63422c..0000000
--- a/claude/com.streamdock.ai-monitor.claude.sdPlugin/zh_CN.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Name": "Claude Desktop 监控",
- "Description": "监控 Claude Desktop AI 工作流状态"
-}
\ No newline at end of file
diff --git a/codex/com.streamdock.ai-monitor.codex.sdPlugin/.gitignore b/codex/com.streamdock.ai-monitor.codex.sdPlugin/.gitignore
deleted file mode 100644
index ca5cc30..0000000
--- a/codex/com.streamdock.ai-monitor.codex.sdPlugin/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-node_modules/
-*.log
-build/
\ No newline at end of file
diff --git a/codex/com.streamdock.ai-monitor.codex.sdPlugin/en.json b/codex/com.streamdock.ai-monitor.codex.sdPlugin/en.json
deleted file mode 100644
index 6d220c6..0000000
--- a/codex/com.streamdock.ai-monitor.codex.sdPlugin/en.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Name": "Codex Desktop Monitor",
- "Description": "Monitor Codex Desktop AI workflow status"
-}
\ No newline at end of file
diff --git a/codex/com.streamdock.ai-monitor.codex.sdPlugin/manifest.json b/codex/com.streamdock.ai-monitor.codex.sdPlugin/manifest.json
deleted file mode 100644
index 79526e9..0000000
--- a/codex/com.streamdock.ai-monitor.codex.sdPlugin/manifest.json
+++ /dev/null
@@ -1,53 +0,0 @@
-{
- "Actions": [
- {
- "Icon": "static/img/codex-icon.svg",
- "Name": "Codex Desktop",
- "States": [
- {
- "Image": "static/img/codex-icon.svg",
- "TitleAlignment": "center",
- "FontSize": "11"
- }
- ],
- "Settings": {
- "pollInterval": 1000
- },
- "Controllers": [
- "Keypad",
- "Information"
- ],
- "UserTitleEnabled": true,
- "SupportedInMultiActions": true,
- "Tooltip": "Codex Desktop 工作流状态监控",
- "UUID": "com.streamdock.ai-monitor.codex"
- }
- ],
- "SDKVersion": 1,
- "Author": "StreamDock",
- "Name": "AI Monitor - Codex Desktop",
- "Icon": "static/img/codex-icon.svg",
- "Category": "AI Monitor",
- "CategoryIcon": "static/img/codex-icon.svg",
- "CodePathWin": "plugin/index.js",
- "CodePathMac": "plugin/index.js",
- "Description": "监控 Codex Desktop AI 工作流状态,在 N4 按钮上实时显示",
- "Version": "1.0.0",
- "URL": "http://10.10.10.14:18003/v6ole/streamdock-ai-monitor",
- "OS": [
- {
- "Platform": "windows",
- "MinimumVersion": "7"
- },
- {
- "Platform": "mac",
- "MinimumVersion": "10.11"
- }
- ],
- "Software": {
- "MinimumVersion": "3.10.188.226"
- },
- "Nodejs": {
- "Version": "20"
- }
-}
\ No newline at end of file
diff --git a/codex/com.streamdock.ai-monitor.codex.sdPlugin/plugin/autofile.js b/codex/com.streamdock.ai-monitor.codex.sdPlugin/plugin/autofile.js
deleted file mode 100644
index 38c45b6..0000000
--- a/codex/com.streamdock.ai-monitor.codex.sdPlugin/plugin/autofile.js
+++ /dev/null
@@ -1,47 +0,0 @@
-const path = require('path');
-const fs = require('fs-extra');
-
-console.log('开始执行自动化构建...');
-
-const currentDir = __dirname;
-
-// 获取父文件夹的路径
-const parentDir = path.join(currentDir, '..');
-// 获取父文件夹的名称
-const PluginName = path.basename(parentDir);
-
-
-const PluginPath = path.join(process.env.APPDATA, 'HotSpot/StreamDock/plugins', PluginName);
-
-try {
- // 删除旧的插件目录
- fs.removeSync(PluginPath);
-
- // 确保目标目录存在
- fs.ensureDirSync(path.dirname(PluginPath));
-
- // 复制当前目录到目标路径,排除 node_modules
- fs.copySync(path.resolve(__dirname, '..'), PluginPath, {
- filter: (src) => {
- const relativePath = path.relative(path.resolve(__dirname, '..'), src);
- // 排除 'node_modules' 和 '.git' 目录及其子文件
- return !relativePath.startsWith('plugin\\node_modules')
- &&!relativePath.startsWith('plugin\\index.js')
- &&!relativePath.startsWith('plugin\\package.json')
- &&!relativePath.startsWith('plugin\\package-lock.json')
- &&!relativePath.startsWith('plugin\\pnpm-lock.yaml')
- &&!relativePath.startsWith('plugin\\yarn.lock')
- &&!relativePath.startsWith('plugin\\build')
- &&!relativePath.startsWith('plugin\\log')
- &&!relativePath.startsWith('.git')
- &&!relativePath.startsWith('.vscode');
- }
- });
-
- fs.copySync( path.join(__dirname, "build"), path.join(PluginPath,'plugin'))
-
- console.log(`插件 "${PluginName}" 已成功复制到 "${PluginPath}"`);
- console.log('构建成功-------------');
-} catch (err) {
- console.error(`复制出错 "${PluginName}":`, err);
-}
\ No newline at end of file
diff --git a/codex/com.streamdock.ai-monitor.codex.sdPlugin/plugin/index.js b/codex/com.streamdock.ai-monitor.codex.sdPlugin/plugin/index.js
deleted file mode 100644
index 8b168cf..0000000
--- a/codex/com.streamdock.ai-monitor.codex.sdPlugin/plugin/index.js
+++ /dev/null
@@ -1,159 +0,0 @@
-const { Plugins, Actions, log } = require('./utils/plugin');
-const fs = require('fs');
-const path = require('path');
-const os = require('os');
-
-const plugin = new Plugins('ai-monitor-codex');
-const timers = {};
-const sessionOffsets = {};
-
-const HOME = os.homedir();
-const SCAN_DIR = path.join(HOME, '.codex', 'sessions');
-const POLL_INTERVAL = 1000;
-const IDLE_TIMEOUT_MS = 3000;
-
-// Load logo
-var logoUri = '';
-try {
- var logoPath = path.join(__dirname, '..', 'static', 'img', 'codex-icon.svg');
- logoUri = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(fs.readFileSync(logoPath, 'utf-8'));
-} catch (_) {}
-
-var STATUS = {
- idle: { title: 'Ready', fill: '#22c55e', blink: false, breathe: false },
- prompt: { title: 'Prompt', fill: '#3b82f6', blink: false, breathe: false },
- thinking: { title: 'Thinking', fill: '#f59e0b', blink: false, breathe: true },
- working: { title: 'Working', fill: '#f59e0b', blink: true, breathe: false },
- typing: { title: 'Typing', fill: '#f59e0b', blink: false, breathe: true },
- done: { title: 'Done', fill: '#22c55e', blink: true, breathe: false },
- error: { title: 'Error', fill: '#ef4444', blink: false, breathe: false },
-};
-
-var PRIORITY = ['error', 'working', 'typing', 'thinking', 'prompt', 'done', 'idle'];
-var currentCtx = null, currentState = 'idle', lastActivityTime = 0;
-var animationHandle = null;
-
-function makeSvg(fill, label, blink, breathe) {
- var anim = '';
- if (blink) anim = '';
- else if (breathe) anim = '';
- return '';
-}
-
-function setDisplay(ctx, state) {
- var d = STATUS[state] || STATUS['idle'];
- plugin.setTitle(ctx, d.title);
- if (state === 'idle' && logoUri) plugin.setImage(ctx, logoUri);
- else plugin.setImage(ctx, 'data:image/svg+xml;charset=utf-8,' + makeSvg(d.fill, d.title, d.blink, d.breathe));
-}
-
-function animateTick() {
- if (!currentCtx) return;
- var d = STATUS[currentState] || STATUS['idle'];
- if (d.blink || d.breathe) {
- var now = Date.now(), period = d.blink ? 800 : 2000, phase = (now % period) / period;
- var alpha = d.blink ? (phase < 0.5 ? 1 : 0.2) : 0.4 + 0.6 * Math.sin(phase * Math.PI);
- var r = parseInt(d.fill.slice(1,3),16), g = parseInt(d.fill.slice(3,5),16), b = parseInt(d.fill.slice(5,7),16);
- var color = 'rgb(' + Math.round(16+(r-16)*alpha) + ',' + Math.round(16+(g-16)*alpha) + ',' + Math.round(16+(b-16)*alpha) + ')';
- var svg = '';
- plugin.setImage(currentCtx, 'data:image/svg+xml;charset=utf-8,' + svg);
- }
-}
-
-function setState(ctx, state) {
- if (state === currentState) return;
- if (state === 'done') { setDisplay(ctx, 'done'); currentState = 'done'; setTimeout(function() { if (currentState === 'done') { currentState = 'idle'; setDisplay(ctx, 'idle'); } }, 800); }
- else { currentState = state; setDisplay(ctx, state); }
-}
-
-function normalize(obj) {
- var t = obj.type || '';
- var pt = (obj.payload || {}).type || '';
- var it = ((obj.payload || {}).item || {}).type || '';
- if (t === 'event_msg') {
- if (pt === 'task_started') return 'thinking';
- if (pt === 'task_complete') return 'done';
- if (pt === 'turn_aborted') return 'error';
- if (pt === 'agent_message') return 'typing';
- if (pt === 'user_message') return 'prompt';
- if (pt === 'web_search_end') return 'working';
- if (pt === 'agent_reasoning') return 'thinking';
- return null;
- }
- if (t === 'response_item') {
- var nt = pt || it;
- if (nt === 'function_call' || nt === 'custom_tool_call' || nt === 'function_call_output' || nt === 'custom_tool_call_output') return 'working';
- if (nt === 'web_search_call') return 'working';
- if (nt === 'reasoning') return 'thinking';
- if (nt === 'message') return 'typing';
- return null;
- }
- return null;
-}
-
-function findFiles(rootDir, suffix) {
- var results = [];
- if (!fs.existsSync(rootDir)) return results;
- (function walk(dir, depth) {
- if (depth > 16) return;
- var names; try { names = fs.readdirSync(dir); } catch (_) { return; }
- for (var i = 0; i < names.length; i++) {
- var full = path.join(dir, names[i]);
- var st; try { st = fs.statSync(full); } catch (_) { continue; }
- if (st.isDirectory()) walk(full, depth + 1);
- else if (st.isFile() && names[i].endsWith(suffix)) results.push(full);
- }
- })(rootDir, 0);
- return results;
-}
-
-function tailFile(filePath, ctx) {
- var detected = false;
- try {
- var stat = fs.statSync(filePath);
- var off = sessionOffsets[filePath] || 0;
- if (stat.size < off) { sessionOffsets[filePath] = 0; return false; }
- if (stat.size === off) return false;
- var fd = fs.openSync(filePath, 'r');
- var grow = Math.min(stat.size - off, 65536);
- var buf = Buffer.alloc(grow);
- fs.readSync(fd, buf, 0, grow, off);
- fs.closeSync(fd);
- sessionOffsets[filePath] = stat.size;
- var lines = buf.toString('utf-8').split('\n').filter(function(l) { return l.trim(); });
- var bestPri = PRIORITY.length, bestState = null;
- for (var i = 0; i < lines.length; i++) {
- try { var obj = JSON.parse(lines[i]); var s = normalize(obj); if (s) { var pri = PRIORITY.indexOf(s); if (pri >= 0 && pri < bestPri) { bestPri = pri; bestState = s; } } } catch (_) {}
- }
- if (bestState) { setState(ctx, bestState); detected = true; }
- } catch (_) { delete sessionOffsets[filePath]; }
- return detected;
-}
-
-function poll(ctx) {
- var detected = false;
- var files = findFiles(SCAN_DIR, '.jsonl');
- files.sort(function(a,b) { return fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs; });
- for (var i = 0; i < Math.min(files.length, 3); i++) { if (tailFile(files[i], ctx)) detected = true; }
- if (detected) lastActivityTime = Date.now();
- if (currentState !== 'idle' && currentState !== 'done' && Date.now() - lastActivityTime > IDLE_TIMEOUT_MS) { setState(ctx, 'idle'); }
-}
-
-plugin.codex = new Actions({
- default: { pollInterval: POLL_INTERVAL },
- _willAppear: function(data) {
- var ctx = data.context;
- currentCtx = ctx; currentState = 'idle'; lastActivityTime = Date.now();
- setDisplay(ctx, 'idle');
- poll(ctx);
- timers[ctx] = setInterval(function() { poll(ctx); }, POLL_INTERVAL);
- if (!animationHandle) animationHandle = setInterval(animateTick, 200);
- },
- _willDisappear: function(data) {
- var ctx = data.context;
- if (timers[ctx]) { clearInterval(timers[ctx]); delete timers[ctx]; }
- if (currentCtx === ctx) { currentCtx = null; currentState = 'idle'; }
- if (Object.keys(timers).length === 0 && animationHandle) { clearInterval(animationHandle); animationHandle = null; }
- },
- keyUp: function(data) { poll(data.context); }
-});
\ No newline at end of file
diff --git a/codex/com.streamdock.ai-monitor.codex.sdPlugin/plugin/package.json b/codex/com.streamdock.ai-monitor.codex.sdPlugin/plugin/package.json
deleted file mode 100644
index 0853b88..0000000
--- a/codex/com.streamdock.ai-monitor.codex.sdPlugin/plugin/package.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "name": "ai-monitor-codex",
- "version": "1.0.0",
- "author": "StreamDock",
- "main": "index.js",
- "description": "Codex Desktop AI workflow status monitor",
- "license": "MIT",
- "dependencies": {
- "fs-extra": "^11.3.0",
- "log4js": "^6.9.1",
- "ws": "^8.14.2"
- }
-}
\ No newline at end of file
diff --git a/codex/com.streamdock.ai-monitor.codex.sdPlugin/plugin/utils/plugin.js b/codex/com.streamdock.ai-monitor.codex.sdPlugin/plugin/utils/plugin.js
deleted file mode 100644
index 673de5c..0000000
--- a/codex/com.streamdock.ai-monitor.codex.sdPlugin/plugin/utils/plugin.js
+++ /dev/null
@@ -1,213 +0,0 @@
-// 配置日志文件
-const now = new Date();
-const log = require('log4js').configure({
- appenders: {
- file: { type: 'file', filename: `./log/${now.getFullYear()}.${now.getMonth() + 1}.${now.getDate()}.log` }
- },
- categories: {
- default: { appenders: ['file'], level: 'info' }
- }
-}).getLogger();
-
-//##################################################
-//##################全局异常捕获#####################
-process.on('uncaughtException', (error) => {
- log.error('Uncaught Exception:', error);
-});
-process.on('unhandledRejection', (reason) => {
- log.error('Unhandled Rejection:', reason);
-});
-//##################################################
-//##################################################
-
-
-// 插件类
-const ws = require('ws');
-class Plugins {
- static language = JSON.parse(process.argv[9]).application.language;
- static globalSettings = {};
- getGlobalSettingsFlag = true;
- constructor() {
- if (Plugins.instance) {
- return Plugins.instance;
- }
- // log.info("process.argv", process.argv);
- this.ws = new ws("ws://127.0.0.1:" + process.argv[3]);
- this.ws.on('open', () => this.ws.send(JSON.stringify({ uuid: process.argv[5], event: process.argv[7] })));
- this.ws.on('close', process.exit);
- this.ws.on('message', e => {
- if (this.getGlobalSettingsFlag) {
- // 只获取一次
- this.getGlobalSettingsFlag = false;
- this.getGlobalSettings();
- }
- const data = JSON.parse(e.toString());
- const action = data.action?.split('.').pop();
- this[action]?.[data.event]?.(data);
- if (data.event === 'didReceiveGlobalSettings') {
- Plugins.globalSettings = data.payload.settings;
- }
- this[data.event]?.(data);
- });
- Plugins.instance = this;
- }
-
- setGlobalSettings(payload) {
- Plugins.globalSettings = payload;
- this.ws.send(JSON.stringify({
- event: "setGlobalSettings",
- context: process.argv[5], payload
- }));
- }
-
- getGlobalSettings() {
- this.ws.send(JSON.stringify({
- event: "getGlobalSettings",
- context: process.argv[5],
- }));
- }
- // 设置标题
- setTitle(context, str, row = 0, num = 6) {
- let newStr = null;
- if (row && str) {
- let nowRow = 1, strArr = str.split('');
- strArr.forEach((item, index) => {
- if (nowRow < row && index >= nowRow * num) { nowRow++; newStr += '\n'; }
- if (nowRow <= row && index < nowRow * num) { newStr += item; }
- });
- if (strArr.length > row * num) { newStr = newStr.substring(0, newStr.length - 1); newStr += '..'; }
- }
- this.ws.send(JSON.stringify({
- event: "setTitle",
- context, payload: {
- target: 0,
- title: newStr || str + ''
- }
- }));
- }
- // 设置背景
- setImage(context, url) {
- this.ws.send(JSON.stringify({
- event: "setImage",
- context, payload: {
- target: 0,
- image: url
- }
- }));
- }
- // 设置状态
- setState(context, state) {
- this.ws.send(JSON.stringify({
- event: "setState",
- context, payload: { state }
- }));
- }
- // 保存持久化数据
- setSettings(context, payload) {
- this.ws.send(JSON.stringify({
- event: "setSettings",
- context, payload
- }));
- }
-
- // 在按键上展示警告
- showAlert(context) {
- this.ws.send(JSON.stringify({
- event: "showAlert",
- context
- }));
- }
-
- // 在按键上展示成功
- showOk(context) {
- this.ws.send(JSON.stringify({
- event: "showOk",
- context
- }));
- }
- // 发送给属性检测器
- sendToPropertyInspector(payload) {
- this.ws.send(JSON.stringify({
- action: Actions.currentAction,
- context: Actions.currentContext,
- payload, event: "sendToPropertyInspector"
- }));
- }
- // 用默认浏览器打开网页
- openUrl(url) {
- this.ws.send(JSON.stringify({
- event: "openUrl",
- payload: { url }
- }));
- }
-};
-
-// 操作类
-class Actions {
- constructor(data) {
- this.data = {};
- this.default = {};
- Object.assign(this, data);
- }
- // 属性检查器显示时
- static currentAction = null;
- static currentContext = null;
- static actions = {};
- propertyInspectorDidAppear(data) {
- Actions.currentAction = data.action;
- Actions.currentContext = data.context;
- this._propertyInspectorDidAppear?.(data);
- }
- // 初始化数据
- willAppear(data) {
- Plugins.globalContext = data.context;
- Actions.actions[data.context] = data.action
- const { context, payload: { settings } } = data;
- this.data[context] = Object.assign({ ...this.default }, settings);
- this._willAppear?.(data);
- }
-
- didReceiveSettings(data) {
- this.data[data.context] = data.payload.settings;
- this._didReceiveSettings?.(data);
- }
- // 行动销毁
- willDisappear(data) {
- this._willDisappear?.(data);
- delete this.data[data.context];
- }
-}
-
-class EventEmitter {
- constructor() {
- this.events = {};
- }
-
- // 订阅事件
- subscribe(event, listener) {
- if (!this.events[event]) {
- this.events[event] = [];
- }
- this.events[event].push(listener);
- }
-
- // 取消订阅
- unsubscribe(event, listenerToRemove) {
- if (!this.events[event]) return;
-
- this.events[event] = this.events[event].filter(listener => listener !== listenerToRemove);
- }
-
- // 发布事件
- emit(event, data) {
- if (!this.events[event]) return;
- this.events[event].forEach(listener => listener(data));
- }
-}
-
-module.exports = {
- log,
- Plugins,
- Actions,
- EventEmitter
-};
\ No newline at end of file
diff --git a/codex/com.streamdock.ai-monitor.codex.sdPlugin/zh_CN.json b/codex/com.streamdock.ai-monitor.codex.sdPlugin/zh_CN.json
deleted file mode 100644
index 298279d..0000000
--- a/codex/com.streamdock.ai-monitor.codex.sdPlugin/zh_CN.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Name": "Codex Desktop 监控",
- "Description": "监控 Codex Desktop AI 工作流状态"
-}
\ No newline at end of file
diff --git a/claude/com.streamdock.ai-monitor.claude.sdPlugin/.gitignore b/com.streamdock.ai-monitor.sdPlugin/.gitignore
similarity index 100%
rename from claude/com.streamdock.ai-monitor.claude.sdPlugin/.gitignore
rename to com.streamdock.ai-monitor.sdPlugin/.gitignore
diff --git a/com.streamdock.ai-monitor.sdPlugin/en.json b/com.streamdock.ai-monitor.sdPlugin/en.json
new file mode 100644
index 0000000..98604dc
--- /dev/null
+++ b/com.streamdock.ai-monitor.sdPlugin/en.json
@@ -0,0 +1,4 @@
+{
+ "Name": "AI Monitor",
+ "Description": "Real-time AI agent workflow status on N4"
+}
\ No newline at end of file
diff --git a/com.streamdock.ai-monitor.sdPlugin/manifest.json b/com.streamdock.ai-monitor.sdPlugin/manifest.json
new file mode 100644
index 0000000..fe3d977
--- /dev/null
+++ b/com.streamdock.ai-monitor.sdPlugin/manifest.json
@@ -0,0 +1,97 @@
+{
+ "Actions": [
+ {
+ "UUID": "com.streamdock.ai-monitor.codex",
+ "Icon": "static/img/codex-icon.svg",
+ "Name": "Codex Desktop",
+ "States": [
+ {
+ "Image": "static/img/codex-icon.svg",
+ "TitleAlignment": "center",
+ "FontSize": "11"
+ }
+ ],
+ "Settings": {
+ "pollInterval": 1000
+ },
+ "Controllers": [
+ "Keypad",
+ "Information"
+ ],
+ "UserTitleEnabled": true,
+ "SupportedInMultiActions": true,
+ "Tooltip": "Codex Desktop 工作流状态监控"
+ },
+ {
+ "UUID": "com.streamdock.ai-monitor.claude",
+ "Icon": "static/img/claude-icon.svg",
+ "Name": "Claude Desktop",
+ "States": [
+ {
+ "Image": "static/img/claude-icon.svg",
+ "TitleAlignment": "center",
+ "FontSize": "11"
+ }
+ ],
+ "Settings": {
+ "pollInterval": 1000
+ },
+ "Controllers": [
+ "Keypad",
+ "Information"
+ ],
+ "UserTitleEnabled": true,
+ "SupportedInMultiActions": true,
+ "Tooltip": "Claude Desktop 工作流状态监控"
+ },
+ {
+ "UUID": "com.streamdock.ai-monitor.opencode",
+ "Icon": "static/img/opencode-icon.svg",
+ "Name": "opencode",
+ "States": [
+ {
+ "Image": "static/img/opencode-icon.svg",
+ "TitleAlignment": "center",
+ "FontSize": "11"
+ }
+ ],
+ "Settings": {
+ "pollInterval": 1000
+ },
+ "Controllers": [
+ "Keypad",
+ "Information"
+ ],
+ "UserTitleEnabled": true,
+ "SupportedInMultiActions": true,
+ "Tooltip": "opencode 工作流状态监控"
+ }
+ ],
+ "SDKVersion": 1,
+ "Author": "StreamDock",
+ "Name": "AI Monitor",
+ "Icon": "static/img/codex-icon.svg",
+ "Category": "AI Monitor",
+ "CategoryIcon": "static/img/codex-icon.svg",
+ "CodePathWin": "plugin/index.js",
+ "CodePathMac": "plugin/index.js",
+ "Description": "监控 Codex Desktop / Claude Desktop / opencode AI 工作流状态",
+ "Version": "2.0.0",
+ "URL": "http://10.10.10.14:18003/v6ole/streamdock-ai-monitor",
+ "OS": [
+ {
+ "Platform": "windows",
+ "MinimumVersion": "7"
+ },
+ {
+ "Platform": "mac",
+ "MinimumVersion": "10.11"
+ }
+ ],
+ "Software": {
+ "MinimumVersion": "3.10.188.226"
+ },
+ "Nodejs": {
+ "Version": "20"
+ }
+}
\ No newline at end of file
diff --git a/claude/com.streamdock.ai-monitor.claude.sdPlugin/plugin/autofile.js b/com.streamdock.ai-monitor.sdPlugin/plugin/autofile.js
similarity index 100%
rename from claude/com.streamdock.ai-monitor.claude.sdPlugin/plugin/autofile.js
rename to com.streamdock.ai-monitor.sdPlugin/plugin/autofile.js
diff --git a/com.streamdock.ai-monitor.sdPlugin/plugin/index.js b/com.streamdock.ai-monitor.sdPlugin/plugin/index.js
new file mode 100644
index 0000000..fd90a6b
--- /dev/null
+++ b/com.streamdock.ai-monitor.sdPlugin/plugin/index.js
@@ -0,0 +1,195 @@
+const { Plugins, Actions, log } = require('./utils/plugin');
+const fs = require('fs');
+const path = require('path');
+const os = require('os');
+
+const plugin = new Plugins('ai-monitor');
+const HOME = os.homedir();
+const POLL_INTERVAL = 1000;
+const IDLE_TIMEOUT_MS = 3000;
+
+// Agent configs
+var AGENTS = {
+ codex: {
+ label: 'Codex',
+ scanDir: path.join(HOME, '.codex', 'sessions'),
+ logoFile: 'codex-icon.svg',
+ normalize: function(obj) {
+ var t = obj.type || '', pt = (obj.payload || {}).type || '', it = ((obj.payload || {}).item || {}).type || '';
+ if (t === 'event_msg') {
+ if (pt === 'task_started') return 'thinking';
+ if (pt === 'task_complete') return 'done';
+ if (pt === 'turn_aborted') return 'error';
+ if (pt === 'agent_message') return 'typing';
+ if (pt === 'user_message') return 'prompt';
+ if (pt === 'web_search_end') return 'working';
+ if (pt === 'agent_reasoning') return 'thinking';
+ return null;
+ }
+ if (t === 'response_item') {
+ var nt = pt || it;
+ if (nt === 'function_call' || nt === 'custom_tool_call' || nt === 'function_call_output' || nt === 'custom_tool_call_output') return 'working';
+ if (nt === 'web_search_call') return 'working';
+ if (nt === 'reasoning') return 'thinking';
+ if (nt === 'message') return 'typing';
+ return null;
+ }
+ return null;
+ }
+ },
+ claude: {
+ label: 'Claude',
+ scanDir: path.join(HOME, '.claude', 'projects'),
+ logoFile: 'claude-icon.svg',
+ normalize: function(obj) {
+ var t = obj.type || '';
+ if (t === 'user') return 'prompt';
+ if (t === 'assistant') {
+ var content = (obj.message || {}).content || [];
+ for (var i = 0; i < content.length; i++) {
+ var ct = content[i].type || '';
+ if (ct === 'tool_use') return 'working';
+ if (ct === 'thinking') return 'thinking';
+ }
+ for (var j = 0; j < content.length; j++) { if ((content[j].type || '') === 'text') return 'typing'; }
+ return 'typing';
+ }
+ if (t === 'system') { if (obj.subtype === 'turn_duration') return 'done'; return null; }
+ return null;
+ }
+ },
+ opencode: {
+ label: 'opencode',
+ scanDir: path.join(HOME, '.opencode', 'sessions'),
+ logoFile: 'opencode-icon.svg',
+ normalize: function(obj) {
+ var t = obj.type || '';
+ if (t === 'user') return 'prompt';
+ if (t === 'assistant') return 'typing';
+ if (t === 'tool' || t === 'tool_call') return 'working';
+ if (t === 'thinking' || t === 'reasoning') return 'thinking';
+ if (t === 'done' || t === 'complete') return 'done';
+ if (t === 'error') return 'error';
+ return null;
+ }
+ }
+};
+
+// Load logos as data URIs
+var LOGOS = {};
+function loadLogo(filename) {
+ try { return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(fs.readFileSync(path.join(__dirname, '..', 'static', 'img', filename), 'utf-8')); }
+ catch (_) { return ''; }
+}
+Object.keys(AGENTS).forEach(function(k) { LOGOS[k] = loadLogo(AGENTS[k].logoFile); });
+
+// Shared display machinery
+var STATUS = {
+ idle: { title: 'Ready', fill: '#22c55e', blink: false, breathe: false },
+ prompt: { title: 'Prompt', fill: '#3b82f6', blink: false, breathe: false },
+ thinking: { title: 'Thinking', fill: '#f59e0b', blink: false, breathe: true },
+ working: { title: 'Working', fill: '#f59e0b', blink: true, breathe: false },
+ typing: { title: 'Typing', fill: '#f59e0b', blink: false, breathe: true },
+ done: { title: 'Done', fill: '#22c55e', blink: true, breathe: false },
+ error: { title: 'Error', fill: '#ef4444', blink: false, breathe: false },
+};
+var PRIORITY = ['error','working','typing','thinking','prompt','done','idle'];
+var states = {}; // per-context state: { agent, currentState, lastActivity, offsets, timer, logoUri }
+
+function makeSvg(fill, label, blink, breathe) {
+ var anim = '';
+ if (blink) anim = '';
+ else if (breathe) anim = '';
+ return '';
+}
+
+function setDisplay(ctx, state) {
+ var d = STATUS[state] || STATUS['idle'];
+ var ss = states[ctx];
+ plugin.setTitle(ctx, d.title);
+ if (state === 'idle' && ss && ss.logoUri) plugin.setImage(ctx, ss.logoUri);
+ else plugin.setImage(ctx, 'data:image/svg+xml;charset=utf-8,' + makeSvg(d.fill, d.title, d.blink, d.breathe));
+}
+
+function setState(ctx, state) {
+ var ss = states[ctx]; if (!ss) return;
+ if (state === ss.currentState) return;
+ if (state === 'done') { setDisplay(ctx, 'done'); ss.currentState = 'done'; setTimeout(function() { if (states[ctx] && states[ctx].currentState === 'done') { states[ctx].currentState = 'idle'; setDisplay(ctx, 'idle'); } }, 800); }
+ else { ss.currentState = state; setDisplay(ctx, state); }
+}
+
+function findFiles(rootDir, suffix) {
+ var results = [];
+ if (!fs.existsSync(rootDir)) return results;
+ (function walk(dir, depth) {
+ if (depth > 16) return;
+ var names; try { names = fs.readdirSync(dir); } catch (_) { return; }
+ for (var i = 0; i < names.length; i++) {
+ var full = path.join(dir, names[i]);
+ var st; try { st = fs.statSync(full); } catch (_) { continue; }
+ if (st.isDirectory()) walk(full, depth + 1);
+ else if (st.isFile() && names[i].endsWith(suffix)) results.push(full);
+ }
+ })(rootDir, 0);
+ return results;
+}
+
+function tailFile(filePath, ctx, agent) {
+ var ss = states[ctx]; if (!ss) return false;
+ var detected = false;
+ try {
+ var stat = fs.statSync(filePath);
+ var off = ss.offsets[filePath] || 0;
+ if (stat.size < off) { ss.offsets[filePath] = 0; return false; }
+ if (stat.size === off) return false;
+ var fd = fs.openSync(filePath, 'r');
+ var grow = Math.min(stat.size - off, 65536);
+ var buf = Buffer.alloc(grow);
+ fs.readSync(fd, buf, 0, grow, off);
+ fs.closeSync(fd);
+ ss.offsets[filePath] = stat.size;
+ var lines = buf.toString('utf-8').split('\n').filter(function(l) { return l.trim(); });
+ var bestPri = PRIORITY.length, bestState = null;
+ for (var i = 0; i < lines.length; i++) {
+ try { var obj = JSON.parse(lines[i]); var s = agent.normalize(obj); if (s) { var pri = PRIORITY.indexOf(s); if (pri >= 0 && pri < bestPri) { bestPri = pri; bestState = s; } } } catch (_) {}
+ }
+ if (bestState) { setState(ctx, bestState); detected = true; }
+ } catch (_) { delete ss.offsets[filePath]; }
+ return detected;
+}
+
+function poll(ctx) {
+ var ss = states[ctx]; if (!ss || !ss.agent) return;
+ var detected = false;
+ var files = findFiles(ss.agent.scanDir, '.jsonl');
+ files.sort(function(a,b) { return fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs; });
+ for (var i = 0; i < Math.min(files.length, 3); i++) { if (tailFile(files[i], ctx, ss.agent)) detected = true; }
+ if (detected) ss.lastActivity = Date.now();
+ if (ss.currentState !== 'idle' && ss.currentState !== 'done' && Date.now() - ss.lastActivity > IDLE_TIMEOUT_MS) { setState(ctx, 'idle'); }
+}
+
+function makeActionFn(agentKey) {
+ var agent = AGENTS[agentKey];
+ return {
+ default: { pollInterval: POLL_INTERVAL },
+ _willAppear: function(data) {
+ var ctx = data.context;
+ var logoUri = LOGOS[agentKey] || '';
+ states[ctx] = { agent: agent, currentState: 'idle', lastActivity: Date.now(), offsets: {}, timer: null, logoUri: logoUri };
+ setDisplay(ctx, 'idle');
+ poll(ctx);
+ states[ctx].timer = setInterval(function() { poll(ctx); }, POLL_INTERVAL);
+ },
+ _willDisappear: function(data) {
+ var ctx = data.context;
+ if (states[ctx] && states[ctx].timer) { clearInterval(states[ctx].timer); }
+ delete states[ctx];
+ },
+ keyUp: function(data) { poll(data.context); },
+ };
+}
+
+// Register each agent as a separate action
+plugin.codex = new Actions(makeActionFn('codex'));
+plugin.claude = new Actions(makeActionFn('claude'));
+plugin.opencode = new Actions(makeActionFn('opencode'));
\ No newline at end of file
diff --git a/codex/com.streamdock.ai-monitor.codex.sdPlugin/plugin/package-lock.json b/com.streamdock.ai-monitor.sdPlugin/plugin/package-lock.json
similarity index 98%
rename from codex/com.streamdock.ai-monitor.codex.sdPlugin/plugin/package-lock.json
rename to com.streamdock.ai-monitor.sdPlugin/plugin/package-lock.json
index 882a5a9..e61075b 100644
--- a/codex/com.streamdock.ai-monitor.codex.sdPlugin/plugin/package-lock.json
+++ b/com.streamdock.ai-monitor.sdPlugin/plugin/package-lock.json
@@ -1,12 +1,12 @@
{
- "name": "ai-monitor-codex",
- "version": "1.0.0",
+ "name": "ai-monitor",
+ "version": "2.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "ai-monitor-codex",
- "version": "1.0.0",
+ "name": "ai-monitor",
+ "version": "2.0.0",
"license": "MIT",
"dependencies": {
"fs-extra": "^11.3.0",
diff --git a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/plugin/package.json b/com.streamdock.ai-monitor.sdPlugin/plugin/package.json
similarity index 59%
rename from opencode/com.streamdock.ai-monitor.opencode.sdPlugin/plugin/package.json
rename to com.streamdock.ai-monitor.sdPlugin/plugin/package.json
index 0f022e0..aaa59c4 100644
--- a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/plugin/package.json
+++ b/com.streamdock.ai-monitor.sdPlugin/plugin/package.json
@@ -1,9 +1,9 @@
{
- "name": "ai-monitor-opencode",
- "version": "1.0.0",
+ "name": "ai-monitor",
+ "version": "2.0.0",
"author": "StreamDock",
"main": "index.js",
- "description": "opencode AI workflow status monitor",
+ "description": "AI agent workflow status monitor",
"license": "MIT",
"dependencies": {
"fs-extra": "^11.3.0",
diff --git a/claude/com.streamdock.ai-monitor.claude.sdPlugin/plugin/utils/plugin.js b/com.streamdock.ai-monitor.sdPlugin/plugin/utils/plugin.js
similarity index 100%
rename from claude/com.streamdock.ai-monitor.claude.sdPlugin/plugin/utils/plugin.js
rename to com.streamdock.ai-monitor.sdPlugin/plugin/utils/plugin.js
diff --git a/claude/com.streamdock.ai-monitor.claude.sdPlugin/static/img/claude-icon.svg b/com.streamdock.ai-monitor.sdPlugin/static/img/claude-icon.svg
similarity index 100%
rename from claude/com.streamdock.ai-monitor.claude.sdPlugin/static/img/claude-icon.svg
rename to com.streamdock.ai-monitor.sdPlugin/static/img/claude-icon.svg
diff --git a/codex/com.streamdock.ai-monitor.codex.sdPlugin/static/img/codex-icon.svg b/com.streamdock.ai-monitor.sdPlugin/static/img/codex-icon.svg
similarity index 100%
rename from codex/com.streamdock.ai-monitor.codex.sdPlugin/static/img/codex-icon.svg
rename to com.streamdock.ai-monitor.sdPlugin/static/img/codex-icon.svg
diff --git a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/static/img/opencode-icon.svg b/com.streamdock.ai-monitor.sdPlugin/static/img/opencode-icon.svg
similarity index 100%
rename from opencode/com.streamdock.ai-monitor.opencode.sdPlugin/static/img/opencode-icon.svg
rename to com.streamdock.ai-monitor.sdPlugin/static/img/opencode-icon.svg
diff --git a/com.streamdock.ai-monitor.sdPlugin/zh_CN.json b/com.streamdock.ai-monitor.sdPlugin/zh_CN.json
new file mode 100644
index 0000000..50a7b19
--- /dev/null
+++ b/com.streamdock.ai-monitor.sdPlugin/zh_CN.json
@@ -0,0 +1,4 @@
+{
+ "Name": "AI 监控",
+ "Description": "在 N4 上实时显示 AI Agent 工作流状态"
+}
\ No newline at end of file
diff --git a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/.gitignore b/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/.gitignore
deleted file mode 100644
index ca5cc30..0000000
--- a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-node_modules/
-*.log
-build/
\ No newline at end of file
diff --git a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/en.json b/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/en.json
deleted file mode 100644
index 274858d..0000000
--- a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/en.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Name": "opencode Monitor",
- "Description": "Monitor opencode AI workflow status"
-}
\ No newline at end of file
diff --git a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/manifest.json b/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/manifest.json
deleted file mode 100644
index 8ccc5ec..0000000
--- a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/manifest.json
+++ /dev/null
@@ -1,53 +0,0 @@
-{
- "Actions": [
- {
- "Icon": "static/img/opencode-icon.svg",
- "Name": "opencode",
- "States": [
- {
- "Image": "static/img/opencode-icon.svg",
- "TitleAlignment": "center",
- "FontSize": "11"
- }
- ],
- "Settings": {
- "pollInterval": 1000
- },
- "Controllers": [
- "Keypad",
- "Information"
- ],
- "UserTitleEnabled": true,
- "SupportedInMultiActions": true,
- "Tooltip": "opencode 工作流状态监控",
- "UUID": "com.streamdock.ai-monitor.opencode"
- }
- ],
- "SDKVersion": 1,
- "Author": "StreamDock",
- "Name": "AI Monitor - opencode",
- "Icon": "static/img/opencode-icon.svg",
- "Category": "AI Monitor",
- "CategoryIcon": "static/img/opencode-icon.svg",
- "CodePathWin": "plugin/index.js",
- "CodePathMac": "plugin/index.js",
- "Description": "监控 opencode AI 工作流状态,在 N4 按钮上实时显示",
- "Version": "1.0.0",
- "URL": "http://10.10.10.14:18003/v6ole/streamdock-ai-monitor",
- "OS": [
- {
- "Platform": "windows",
- "MinimumVersion": "7"
- },
- {
- "Platform": "mac",
- "MinimumVersion": "10.11"
- }
- ],
- "Software": {
- "MinimumVersion": "3.10.188.226"
- },
- "Nodejs": {
- "Version": "20"
- }
-}
\ No newline at end of file
diff --git a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/plugin/autofile.js b/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/plugin/autofile.js
deleted file mode 100644
index 38c45b6..0000000
--- a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/plugin/autofile.js
+++ /dev/null
@@ -1,47 +0,0 @@
-const path = require('path');
-const fs = require('fs-extra');
-
-console.log('开始执行自动化构建...');
-
-const currentDir = __dirname;
-
-// 获取父文件夹的路径
-const parentDir = path.join(currentDir, '..');
-// 获取父文件夹的名称
-const PluginName = path.basename(parentDir);
-
-
-const PluginPath = path.join(process.env.APPDATA, 'HotSpot/StreamDock/plugins', PluginName);
-
-try {
- // 删除旧的插件目录
- fs.removeSync(PluginPath);
-
- // 确保目标目录存在
- fs.ensureDirSync(path.dirname(PluginPath));
-
- // 复制当前目录到目标路径,排除 node_modules
- fs.copySync(path.resolve(__dirname, '..'), PluginPath, {
- filter: (src) => {
- const relativePath = path.relative(path.resolve(__dirname, '..'), src);
- // 排除 'node_modules' 和 '.git' 目录及其子文件
- return !relativePath.startsWith('plugin\\node_modules')
- &&!relativePath.startsWith('plugin\\index.js')
- &&!relativePath.startsWith('plugin\\package.json')
- &&!relativePath.startsWith('plugin\\package-lock.json')
- &&!relativePath.startsWith('plugin\\pnpm-lock.yaml')
- &&!relativePath.startsWith('plugin\\yarn.lock')
- &&!relativePath.startsWith('plugin\\build')
- &&!relativePath.startsWith('plugin\\log')
- &&!relativePath.startsWith('.git')
- &&!relativePath.startsWith('.vscode');
- }
- });
-
- fs.copySync( path.join(__dirname, "build"), path.join(PluginPath,'plugin'))
-
- console.log(`插件 "${PluginName}" 已成功复制到 "${PluginPath}"`);
- console.log('构建成功-------------');
-} catch (err) {
- console.error(`复制出错 "${PluginName}":`, err);
-}
\ No newline at end of file
diff --git a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/plugin/index.js b/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/plugin/index.js
deleted file mode 100644
index 7f2b554..0000000
--- a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/plugin/index.js
+++ /dev/null
@@ -1,145 +0,0 @@
-const { Plugins, Actions, log } = require('./utils/plugin');
-const fs = require('fs');
-const path = require('path');
-const os = require('os');
-
-const plugin = new Plugins('ai-monitor-opencode');
-const timers = {};
-const sessionOffsets = {};
-
-const HOME = os.homedir();
-const SCAN_DIR = path.join(HOME, '.opencode', 'sessions');
-const POLL_INTERVAL = 1000;
-const IDLE_TIMEOUT_MS = 3000;
-
-// Load logo
-var logoUri = '';
-try {
- var logoPath = path.join(__dirname, '..', 'static', 'img', 'opencode-icon.svg');
- logoUri = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(fs.readFileSync(logoPath, 'utf-8'));
-} catch (_) {}
-
-var STATUS = {
- idle: { title: 'Ready', fill: '#22c55e', blink: false, breathe: false },
- prompt: { title: 'Prompt', fill: '#3b82f6', blink: false, breathe: false },
- thinking: { title: 'Thinking', fill: '#f59e0b', blink: false, breathe: true },
- working: { title: 'Working', fill: '#f59e0b', blink: true, breathe: false },
- typing: { title: 'Typing', fill: '#f59e0b', blink: false, breathe: true },
- done: { title: 'Done', fill: '#22c55e', blink: true, breathe: false },
- error: { title: 'Error', fill: '#ef4444', blink: false, breathe: false },
-};
-
-var PRIORITY = ['error', 'working', 'typing', 'thinking', 'prompt', 'done', 'idle'];
-var currentCtx = null, currentState = 'idle', lastActivityTime = 0;
-var animationHandle = null;
-
-function makeSvg(fill, label, blink, breathe) {
- var anim = '';
- if (blink) anim = '';
- else if (breathe) anim = '';
- return '';
-}
-
-function setDisplay(ctx, state) {
- var d = STATUS[state] || STATUS['idle'];
- plugin.setTitle(ctx, d.title);
- if (state === 'idle' && logoUri) plugin.setImage(ctx, logoUri);
- else plugin.setImage(ctx, 'data:image/svg+xml;charset=utf-8,' + makeSvg(d.fill, d.title, d.blink, d.breathe));
-}
-
-function animateTick() {
- if (!currentCtx) return;
- var d = STATUS[currentState] || STATUS['idle'];
- if (d.blink || d.breathe) {
- var now = Date.now(), period = d.blink ? 800 : 2000, phase = (now % period) / period;
- var alpha = d.blink ? (phase < 0.5 ? 1 : 0.2) : 0.4 + 0.6 * Math.sin(phase * Math.PI);
- var r = parseInt(d.fill.slice(1,3),16), g = parseInt(d.fill.slice(3,5),16), b = parseInt(d.fill.slice(5,7),16);
- var color = 'rgb(' + Math.round(16+(r-16)*alpha) + ',' + Math.round(16+(g-16)*alpha) + ',' + Math.round(16+(b-16)*alpha) + ')';
- var svg = '';
- plugin.setImage(currentCtx, 'data:image/svg+xml;charset=utf-8,' + svg);
- }
-}
-
-function setState(ctx, state) {
- if (state === currentState) return;
- if (state === 'done') { setDisplay(ctx, 'done'); currentState = 'done'; setTimeout(function() { if (currentState === 'done') { currentState = 'idle'; setDisplay(ctx, 'idle'); } }, 800); }
- else { currentState = state; setDisplay(ctx, state); }
-}
-
-function normalize(obj) {
- var t = obj.type || '';
- if (t === 'user') return 'prompt';
- if (t === 'assistant') return 'typing';
- if (t === 'tool' || t === 'tool_call') return 'working';
- if (t === 'thinking' || t === 'reasoning') return 'thinking';
- if (t === 'done' || t === 'complete') return 'done';
- if (t === 'error') return 'error';
- return null;
-}
-
-function findFiles(rootDir, suffix) {
- var results = [];
- if (!fs.existsSync(rootDir)) return results;
- (function walk(dir, depth) {
- if (depth > 16) return;
- var names; try { names = fs.readdirSync(dir); } catch (_) { return; }
- for (var i = 0; i < names.length; i++) {
- var full = path.join(dir, names[i]);
- var st; try { st = fs.statSync(full); } catch (_) { continue; }
- if (st.isDirectory()) walk(full, depth + 1);
- else if (st.isFile() && names[i].endsWith(suffix)) results.push(full);
- }
- })(rootDir, 0);
- return results;
-}
-
-function tailFile(filePath, ctx) {
- var detected = false;
- try {
- var stat = fs.statSync(filePath);
- var off = sessionOffsets[filePath] || 0;
- if (stat.size < off) { sessionOffsets[filePath] = 0; return false; }
- if (stat.size === off) return false;
- var fd = fs.openSync(filePath, 'r');
- var grow = Math.min(stat.size - off, 65536);
- var buf = Buffer.alloc(grow);
- fs.readSync(fd, buf, 0, grow, off);
- fs.closeSync(fd);
- sessionOffsets[filePath] = stat.size;
- var lines = buf.toString('utf-8').split('\n').filter(function(l) { return l.trim(); });
- var bestPri = PRIORITY.length, bestState = null;
- for (var i = 0; i < lines.length; i++) {
- try { var obj = JSON.parse(lines[i]); var s = normalize(obj); if (s) { var pri = PRIORITY.indexOf(s); if (pri >= 0 && pri < bestPri) { bestPri = pri; bestState = s; } } } catch (_) {}
- }
- if (bestState) { setState(ctx, bestState); detected = true; }
- } catch (_) { delete sessionOffsets[filePath]; }
- return detected;
-}
-
-function poll(ctx) {
- var detected = false;
- var files = findFiles(SCAN_DIR, '.jsonl');
- files.sort(function(a,b) { return fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs; });
- for (var i = 0; i < Math.min(files.length, 3); i++) { if (tailFile(files[i], ctx)) detected = true; }
- if (detected) lastActivityTime = Date.now();
- if (currentState !== 'idle' && currentState !== 'done' && Date.now() - lastActivityTime > IDLE_TIMEOUT_MS) { setState(ctx, 'idle'); }
-}
-
-plugin.opencode = new Actions({
- default: { pollInterval: POLL_INTERVAL },
- _willAppear: function(data) {
- var ctx = data.context;
- currentCtx = ctx; currentState = 'idle'; lastActivityTime = Date.now();
- setDisplay(ctx, 'idle');
- poll(ctx);
- timers[ctx] = setInterval(function() { poll(ctx); }, POLL_INTERVAL);
- if (!animationHandle) animationHandle = setInterval(animateTick, 200);
- },
- _willDisappear: function(data) {
- var ctx = data.context;
- if (timers[ctx]) { clearInterval(timers[ctx]); delete timers[ctx]; }
- if (currentCtx === ctx) { currentCtx = null; currentState = 'idle'; }
- if (Object.keys(timers).length === 0 && animationHandle) { clearInterval(animationHandle); animationHandle = null; }
- },
- keyUp: function(data) { poll(data.context); }
-});
\ No newline at end of file
diff --git a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/plugin/package-lock.json b/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/plugin/package-lock.json
deleted file mode 100644
index b58c86f..0000000
--- a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/plugin/package-lock.json
+++ /dev/null
@@ -1,186 +0,0 @@
-{
- "name": "ai-monitor-opencode",
- "version": "1.0.0",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "ai-monitor-opencode",
- "version": "1.0.0",
- "license": "MIT",
- "dependencies": {
- "fs-extra": "^11.3.0",
- "log4js": "^6.9.1",
- "ws": "^8.14.2"
- }
- },
- "node_modules/date-format": {
- "version": "4.0.14",
- "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz",
- "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==",
- "license": "MIT",
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/debug": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/flatted": {
- "version": "3.4.2",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
- "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
- "license": "ISC"
- },
- "node_modules/fs-extra": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz",
- "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==",
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=14.14"
- }
- },
- "node_modules/graceful-fs": {
- "version": "4.2.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
- "license": "ISC"
- },
- "node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/log4js": {
- "version": "6.9.1",
- "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz",
- "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==",
- "license": "Apache-2.0",
- "dependencies": {
- "date-format": "^4.0.14",
- "debug": "^4.3.4",
- "flatted": "^3.2.7",
- "rfdc": "^1.3.0",
- "streamroller": "^3.1.5"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "license": "MIT"
- },
- "node_modules/rfdc": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
- "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
- "license": "MIT"
- },
- "node_modules/streamroller": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz",
- "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==",
- "license": "MIT",
- "dependencies": {
- "date-format": "^4.0.14",
- "debug": "^4.3.4",
- "fs-extra": "^8.1.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/streamroller/node_modules/fs-extra": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
- "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^4.0.0",
- "universalify": "^0.1.0"
- },
- "engines": {
- "node": ">=6 <7 || >=8"
- }
- },
- "node_modules/streamroller/node_modules/jsonfile": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
- "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
- "license": "MIT",
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/streamroller/node_modules/universalify": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
- "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
- "license": "MIT",
- "engines": {
- "node": ">= 4.0.0"
- }
- },
- "node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
- "node_modules/ws": {
- "version": "8.21.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
- "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
- "license": "MIT",
- "engines": {
- "node": ">=10.0.0"
- },
- "peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": ">=5.0.2"
- },
- "peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
- "optional": true
- }
- }
- }
- }
-}
diff --git a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/plugin/utils/plugin.js b/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/plugin/utils/plugin.js
deleted file mode 100644
index 673de5c..0000000
--- a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/plugin/utils/plugin.js
+++ /dev/null
@@ -1,213 +0,0 @@
-// 配置日志文件
-const now = new Date();
-const log = require('log4js').configure({
- appenders: {
- file: { type: 'file', filename: `./log/${now.getFullYear()}.${now.getMonth() + 1}.${now.getDate()}.log` }
- },
- categories: {
- default: { appenders: ['file'], level: 'info' }
- }
-}).getLogger();
-
-//##################################################
-//##################全局异常捕获#####################
-process.on('uncaughtException', (error) => {
- log.error('Uncaught Exception:', error);
-});
-process.on('unhandledRejection', (reason) => {
- log.error('Unhandled Rejection:', reason);
-});
-//##################################################
-//##################################################
-
-
-// 插件类
-const ws = require('ws');
-class Plugins {
- static language = JSON.parse(process.argv[9]).application.language;
- static globalSettings = {};
- getGlobalSettingsFlag = true;
- constructor() {
- if (Plugins.instance) {
- return Plugins.instance;
- }
- // log.info("process.argv", process.argv);
- this.ws = new ws("ws://127.0.0.1:" + process.argv[3]);
- this.ws.on('open', () => this.ws.send(JSON.stringify({ uuid: process.argv[5], event: process.argv[7] })));
- this.ws.on('close', process.exit);
- this.ws.on('message', e => {
- if (this.getGlobalSettingsFlag) {
- // 只获取一次
- this.getGlobalSettingsFlag = false;
- this.getGlobalSettings();
- }
- const data = JSON.parse(e.toString());
- const action = data.action?.split('.').pop();
- this[action]?.[data.event]?.(data);
- if (data.event === 'didReceiveGlobalSettings') {
- Plugins.globalSettings = data.payload.settings;
- }
- this[data.event]?.(data);
- });
- Plugins.instance = this;
- }
-
- setGlobalSettings(payload) {
- Plugins.globalSettings = payload;
- this.ws.send(JSON.stringify({
- event: "setGlobalSettings",
- context: process.argv[5], payload
- }));
- }
-
- getGlobalSettings() {
- this.ws.send(JSON.stringify({
- event: "getGlobalSettings",
- context: process.argv[5],
- }));
- }
- // 设置标题
- setTitle(context, str, row = 0, num = 6) {
- let newStr = null;
- if (row && str) {
- let nowRow = 1, strArr = str.split('');
- strArr.forEach((item, index) => {
- if (nowRow < row && index >= nowRow * num) { nowRow++; newStr += '\n'; }
- if (nowRow <= row && index < nowRow * num) { newStr += item; }
- });
- if (strArr.length > row * num) { newStr = newStr.substring(0, newStr.length - 1); newStr += '..'; }
- }
- this.ws.send(JSON.stringify({
- event: "setTitle",
- context, payload: {
- target: 0,
- title: newStr || str + ''
- }
- }));
- }
- // 设置背景
- setImage(context, url) {
- this.ws.send(JSON.stringify({
- event: "setImage",
- context, payload: {
- target: 0,
- image: url
- }
- }));
- }
- // 设置状态
- setState(context, state) {
- this.ws.send(JSON.stringify({
- event: "setState",
- context, payload: { state }
- }));
- }
- // 保存持久化数据
- setSettings(context, payload) {
- this.ws.send(JSON.stringify({
- event: "setSettings",
- context, payload
- }));
- }
-
- // 在按键上展示警告
- showAlert(context) {
- this.ws.send(JSON.stringify({
- event: "showAlert",
- context
- }));
- }
-
- // 在按键上展示成功
- showOk(context) {
- this.ws.send(JSON.stringify({
- event: "showOk",
- context
- }));
- }
- // 发送给属性检测器
- sendToPropertyInspector(payload) {
- this.ws.send(JSON.stringify({
- action: Actions.currentAction,
- context: Actions.currentContext,
- payload, event: "sendToPropertyInspector"
- }));
- }
- // 用默认浏览器打开网页
- openUrl(url) {
- this.ws.send(JSON.stringify({
- event: "openUrl",
- payload: { url }
- }));
- }
-};
-
-// 操作类
-class Actions {
- constructor(data) {
- this.data = {};
- this.default = {};
- Object.assign(this, data);
- }
- // 属性检查器显示时
- static currentAction = null;
- static currentContext = null;
- static actions = {};
- propertyInspectorDidAppear(data) {
- Actions.currentAction = data.action;
- Actions.currentContext = data.context;
- this._propertyInspectorDidAppear?.(data);
- }
- // 初始化数据
- willAppear(data) {
- Plugins.globalContext = data.context;
- Actions.actions[data.context] = data.action
- const { context, payload: { settings } } = data;
- this.data[context] = Object.assign({ ...this.default }, settings);
- this._willAppear?.(data);
- }
-
- didReceiveSettings(data) {
- this.data[data.context] = data.payload.settings;
- this._didReceiveSettings?.(data);
- }
- // 行动销毁
- willDisappear(data) {
- this._willDisappear?.(data);
- delete this.data[data.context];
- }
-}
-
-class EventEmitter {
- constructor() {
- this.events = {};
- }
-
- // 订阅事件
- subscribe(event, listener) {
- if (!this.events[event]) {
- this.events[event] = [];
- }
- this.events[event].push(listener);
- }
-
- // 取消订阅
- unsubscribe(event, listenerToRemove) {
- if (!this.events[event]) return;
-
- this.events[event] = this.events[event].filter(listener => listener !== listenerToRemove);
- }
-
- // 发布事件
- emit(event, data) {
- if (!this.events[event]) return;
- this.events[event].forEach(listener => listener(data));
- }
-}
-
-module.exports = {
- log,
- Plugins,
- Actions,
- EventEmitter
-};
\ No newline at end of file
diff --git a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/zh_CN.json b/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/zh_CN.json
deleted file mode 100644
index f10ed77..0000000
--- a/opencode/com.streamdock.ai-monitor.opencode.sdPlugin/zh_CN.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Name": "opencode 监控",
- "Description": "监控 opencode AI 工作流状态"
-}
\ No newline at end of file
diff --git a/shared/plugin.js b/shared/plugin.js
deleted file mode 100644
index 673de5c..0000000
--- a/shared/plugin.js
+++ /dev/null
@@ -1,213 +0,0 @@
-// 配置日志文件
-const now = new Date();
-const log = require('log4js').configure({
- appenders: {
- file: { type: 'file', filename: `./log/${now.getFullYear()}.${now.getMonth() + 1}.${now.getDate()}.log` }
- },
- categories: {
- default: { appenders: ['file'], level: 'info' }
- }
-}).getLogger();
-
-//##################################################
-//##################全局异常捕获#####################
-process.on('uncaughtException', (error) => {
- log.error('Uncaught Exception:', error);
-});
-process.on('unhandledRejection', (reason) => {
- log.error('Unhandled Rejection:', reason);
-});
-//##################################################
-//##################################################
-
-
-// 插件类
-const ws = require('ws');
-class Plugins {
- static language = JSON.parse(process.argv[9]).application.language;
- static globalSettings = {};
- getGlobalSettingsFlag = true;
- constructor() {
- if (Plugins.instance) {
- return Plugins.instance;
- }
- // log.info("process.argv", process.argv);
- this.ws = new ws("ws://127.0.0.1:" + process.argv[3]);
- this.ws.on('open', () => this.ws.send(JSON.stringify({ uuid: process.argv[5], event: process.argv[7] })));
- this.ws.on('close', process.exit);
- this.ws.on('message', e => {
- if (this.getGlobalSettingsFlag) {
- // 只获取一次
- this.getGlobalSettingsFlag = false;
- this.getGlobalSettings();
- }
- const data = JSON.parse(e.toString());
- const action = data.action?.split('.').pop();
- this[action]?.[data.event]?.(data);
- if (data.event === 'didReceiveGlobalSettings') {
- Plugins.globalSettings = data.payload.settings;
- }
- this[data.event]?.(data);
- });
- Plugins.instance = this;
- }
-
- setGlobalSettings(payload) {
- Plugins.globalSettings = payload;
- this.ws.send(JSON.stringify({
- event: "setGlobalSettings",
- context: process.argv[5], payload
- }));
- }
-
- getGlobalSettings() {
- this.ws.send(JSON.stringify({
- event: "getGlobalSettings",
- context: process.argv[5],
- }));
- }
- // 设置标题
- setTitle(context, str, row = 0, num = 6) {
- let newStr = null;
- if (row && str) {
- let nowRow = 1, strArr = str.split('');
- strArr.forEach((item, index) => {
- if (nowRow < row && index >= nowRow * num) { nowRow++; newStr += '\n'; }
- if (nowRow <= row && index < nowRow * num) { newStr += item; }
- });
- if (strArr.length > row * num) { newStr = newStr.substring(0, newStr.length - 1); newStr += '..'; }
- }
- this.ws.send(JSON.stringify({
- event: "setTitle",
- context, payload: {
- target: 0,
- title: newStr || str + ''
- }
- }));
- }
- // 设置背景
- setImage(context, url) {
- this.ws.send(JSON.stringify({
- event: "setImage",
- context, payload: {
- target: 0,
- image: url
- }
- }));
- }
- // 设置状态
- setState(context, state) {
- this.ws.send(JSON.stringify({
- event: "setState",
- context, payload: { state }
- }));
- }
- // 保存持久化数据
- setSettings(context, payload) {
- this.ws.send(JSON.stringify({
- event: "setSettings",
- context, payload
- }));
- }
-
- // 在按键上展示警告
- showAlert(context) {
- this.ws.send(JSON.stringify({
- event: "showAlert",
- context
- }));
- }
-
- // 在按键上展示成功
- showOk(context) {
- this.ws.send(JSON.stringify({
- event: "showOk",
- context
- }));
- }
- // 发送给属性检测器
- sendToPropertyInspector(payload) {
- this.ws.send(JSON.stringify({
- action: Actions.currentAction,
- context: Actions.currentContext,
- payload, event: "sendToPropertyInspector"
- }));
- }
- // 用默认浏览器打开网页
- openUrl(url) {
- this.ws.send(JSON.stringify({
- event: "openUrl",
- payload: { url }
- }));
- }
-};
-
-// 操作类
-class Actions {
- constructor(data) {
- this.data = {};
- this.default = {};
- Object.assign(this, data);
- }
- // 属性检查器显示时
- static currentAction = null;
- static currentContext = null;
- static actions = {};
- propertyInspectorDidAppear(data) {
- Actions.currentAction = data.action;
- Actions.currentContext = data.context;
- this._propertyInspectorDidAppear?.(data);
- }
- // 初始化数据
- willAppear(data) {
- Plugins.globalContext = data.context;
- Actions.actions[data.context] = data.action
- const { context, payload: { settings } } = data;
- this.data[context] = Object.assign({ ...this.default }, settings);
- this._willAppear?.(data);
- }
-
- didReceiveSettings(data) {
- this.data[data.context] = data.payload.settings;
- this._didReceiveSettings?.(data);
- }
- // 行动销毁
- willDisappear(data) {
- this._willDisappear?.(data);
- delete this.data[data.context];
- }
-}
-
-class EventEmitter {
- constructor() {
- this.events = {};
- }
-
- // 订阅事件
- subscribe(event, listener) {
- if (!this.events[event]) {
- this.events[event] = [];
- }
- this.events[event].push(listener);
- }
-
- // 取消订阅
- unsubscribe(event, listenerToRemove) {
- if (!this.events[event]) return;
-
- this.events[event] = this.events[event].filter(listener => listener !== listenerToRemove);
- }
-
- // 发布事件
- emit(event, data) {
- if (!this.events[event]) return;
- this.events[event].forEach(listener => listener(data));
- }
-}
-
-module.exports = {
- log,
- Plugins,
- Actions,
- EventEmitter
-};
\ No newline at end of file