@adhdev/daemon-core 0.5.3
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/dist/index.d.ts +2662 -0
- package/dist/index.js +11341 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
- package/providers/_builtin/.github/workflows/generate-registry.yml +57 -0
- package/providers/_builtin/COMPATIBILITY.md +217 -0
- package/providers/_builtin/CONTRIBUTING.md +200 -0
- package/providers/_builtin/README.md +119 -0
- package/providers/_builtin/_helpers/index.js +188 -0
- package/providers/_builtin/acp/agentpool/provider.json +54 -0
- package/providers/_builtin/acp/amp/provider.json +52 -0
- package/providers/_builtin/acp/auggie/provider.json +57 -0
- package/providers/_builtin/acp/autodev/provider.json +54 -0
- package/providers/_builtin/acp/autohand/provider.json +52 -0
- package/providers/_builtin/acp/blackbox-ai/provider.json +54 -0
- package/providers/_builtin/acp/claude-agent/provider.json +57 -0
- package/providers/_builtin/acp/cline-acp/provider.json +54 -0
- package/providers/_builtin/acp/codebuddy/provider.json +54 -0
- package/providers/_builtin/acp/codex-cli/provider.json +57 -0
- package/providers/_builtin/acp/corust-agent/provider.json +52 -0
- package/providers/_builtin/acp/crow-cli/provider.json +54 -0
- package/providers/_builtin/acp/cursor-acp/provider.json +54 -0
- package/providers/_builtin/acp/deepagents/provider.json +52 -0
- package/providers/_builtin/acp/dimcode/provider.json +54 -0
- package/providers/_builtin/acp/docker-cagent/provider.json +57 -0
- package/providers/_builtin/acp/factory-droid/provider.json +60 -0
- package/providers/_builtin/acp/fast-agent/provider.json +52 -0
- package/providers/_builtin/acp/gemini-cli/provider.json +114 -0
- package/providers/_builtin/acp/github-copilot/provider.json +54 -0
- package/providers/_builtin/acp/goose/provider.json +57 -0
- package/providers/_builtin/acp/junie/provider.json +52 -0
- package/providers/_builtin/acp/kilo/provider.json +54 -0
- package/providers/_builtin/acp/kimi-cli/provider.json +57 -0
- package/providers/_builtin/acp/minion-code/provider.json +52 -0
- package/providers/_builtin/acp/mistral-vibe/provider.json +57 -0
- package/providers/_builtin/acp/nova/provider.json +54 -0
- package/providers/_builtin/acp/openclaw/provider.json +54 -0
- package/providers/_builtin/acp/opencode/provider.json +52 -0
- package/providers/_builtin/acp/openhands/provider.json +54 -0
- package/providers/_builtin/acp/pi-acp/provider.json +52 -0
- package/providers/_builtin/acp/qoder/provider.json +54 -0
- package/providers/_builtin/acp/qwen-code/provider.json +60 -0
- package/providers/_builtin/acp/stakpak/provider.json +54 -0
- package/providers/_builtin/acp/vtcode/provider.json +54 -0
- package/providers/_builtin/cli/claude-cli/provider.json +100 -0
- package/providers/_builtin/cli/codex-cli/provider.json +89 -0
- package/providers/_builtin/cli/gemini-cli/provider.json +93 -0
- package/providers/_builtin/docs/CDP_SELECTOR_GUIDE.md +370 -0
- package/providers/_builtin/docs/PROVIDER_GUIDE.md +916 -0
- package/providers/_builtin/extension/cline/provider.json +35 -0
- package/providers/_builtin/extension/cline/scripts/focus_editor.js +48 -0
- package/providers/_builtin/extension/cline/scripts/list_chats.js +100 -0
- package/providers/_builtin/extension/cline/scripts/list_models.js +43 -0
- package/providers/_builtin/extension/cline/scripts/list_modes.js +35 -0
- package/providers/_builtin/extension/cline/scripts/new_session.js +85 -0
- package/providers/_builtin/extension/cline/scripts/open_panel.js +25 -0
- package/providers/_builtin/extension/cline/scripts/read_chat.js +257 -0
- package/providers/_builtin/extension/cline/scripts/resolve_action.js +83 -0
- package/providers/_builtin/extension/cline/scripts/send_message.js +95 -0
- package/providers/_builtin/extension/cline/scripts/set_mode.js +36 -0
- package/providers/_builtin/extension/cline/scripts/set_model.js +36 -0
- package/providers/_builtin/extension/cline/scripts/switch_session.js +206 -0
- package/providers/_builtin/extension/cline/scripts.js +73 -0
- package/providers/_builtin/extension/roo-code/provider.json +35 -0
- package/providers/_builtin/extension/roo-code/scripts.js +659 -0
- package/providers/_builtin/ide/antigravity/provider.json +68 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/focus_editor.js +20 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/list_chats.js +137 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/list_models.js +38 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/list_modes.js +48 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/new_session.js +75 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/read_chat.js +262 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/resolve_action.js +68 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/scripts.js +57 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/send_message.js +56 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/set_mode.js +34 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/set_model.js +47 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/switch_session.js +114 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/focus_editor.js +20 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/list_chats.js +137 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/list_models.js +61 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/list_modes.js +72 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/new_session.js +75 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/read_chat.js +262 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/resolve_action.js +68 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/scripts.js +67 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/send_message.js +56 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/set_mode.js +67 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/set_model.js +72 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/switch_session.js +114 -0
- package/providers/_builtin/ide/cursor/provider.json +70 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/dismiss_notification.js +30 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/focus_editor.js +13 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/list_models.js +78 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/list_modes.js +40 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/list_notifications.js +23 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/list_sessions.js +42 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/new_session.js +20 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/open_panel.js +23 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/read_chat.js +75 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/resolve_action.js +19 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/scripts.js +78 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/send_message.js +23 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/set_mode.js +38 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/set_model.js +81 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/switch_session.js +28 -0
- package/providers/_builtin/ide/kiro/provider.json +67 -0
- package/providers/_builtin/ide/kiro/scripts/focus_editor.js +20 -0
- package/providers/_builtin/ide/kiro/scripts/open_panel.js +47 -0
- package/providers/_builtin/ide/kiro/scripts/resolve_action.js +54 -0
- package/providers/_builtin/ide/kiro/scripts/send_message.js +29 -0
- package/providers/_builtin/ide/kiro/scripts/webview_list_models.js +39 -0
- package/providers/_builtin/ide/kiro/scripts/webview_list_modes.js +39 -0
- package/providers/_builtin/ide/kiro/scripts/webview_list_sessions.js +21 -0
- package/providers/_builtin/ide/kiro/scripts/webview_new_session.js +34 -0
- package/providers/_builtin/ide/kiro/scripts/webview_read_chat.js +68 -0
- package/providers/_builtin/ide/kiro/scripts/webview_send_message.js +72 -0
- package/providers/_builtin/ide/kiro/scripts/webview_set_mode.js +15 -0
- package/providers/_builtin/ide/kiro/scripts/webview_set_model.js +15 -0
- package/providers/_builtin/ide/kiro/scripts/webview_switch_session.js +26 -0
- package/providers/_builtin/ide/kiro/scripts.js +62 -0
- package/providers/_builtin/ide/pearai/provider.json +67 -0
- package/providers/_builtin/ide/pearai/scripts/focus_editor.js +20 -0
- package/providers/_builtin/ide/pearai/scripts/list_sessions.js +38 -0
- package/providers/_builtin/ide/pearai/scripts/new_session.js +55 -0
- package/providers/_builtin/ide/pearai/scripts/open_panel.js +46 -0
- package/providers/_builtin/ide/pearai/scripts/resolve_action.js +54 -0
- package/providers/_builtin/ide/pearai/scripts/send_message.js +29 -0
- package/providers/_builtin/ide/pearai/scripts/webview_list_models.js +43 -0
- package/providers/_builtin/ide/pearai/scripts/webview_list_modes.js +35 -0
- package/providers/_builtin/ide/pearai/scripts/webview_list_sessions.js +62 -0
- package/providers/_builtin/ide/pearai/scripts/webview_new_session.js +49 -0
- package/providers/_builtin/ide/pearai/scripts/webview_read_chat.js +92 -0
- package/providers/_builtin/ide/pearai/scripts/webview_resolve_action.js +59 -0
- package/providers/_builtin/ide/pearai/scripts/webview_send_message.js +72 -0
- package/providers/_builtin/ide/pearai/scripts/webview_set_mode.js +36 -0
- package/providers/_builtin/ide/pearai/scripts/webview_set_model.js +36 -0
- package/providers/_builtin/ide/pearai/scripts/webview_switch_session.js +34 -0
- package/providers/_builtin/ide/pearai/scripts.js +74 -0
- package/providers/_builtin/ide/trae/provider.json +66 -0
- package/providers/_builtin/ide/trae/scripts/focus_editor.js +20 -0
- package/providers/_builtin/ide/trae/scripts/list_chats.js +24 -0
- package/providers/_builtin/ide/trae/scripts/list_models.js +39 -0
- package/providers/_builtin/ide/trae/scripts/list_modes.js +39 -0
- package/providers/_builtin/ide/trae/scripts/new_session.js +30 -0
- package/providers/_builtin/ide/trae/scripts/open_panel.js +44 -0
- package/providers/_builtin/ide/trae/scripts/read_chat.js +113 -0
- package/providers/_builtin/ide/trae/scripts/resolve_action.js +54 -0
- package/providers/_builtin/ide/trae/scripts/send_message.js +69 -0
- package/providers/_builtin/ide/trae/scripts/set_mode.js +15 -0
- package/providers/_builtin/ide/trae/scripts/set_model.js +15 -0
- package/providers/_builtin/ide/trae/scripts/switch_session.js +23 -0
- package/providers/_builtin/ide/trae/scripts.js +57 -0
- package/providers/_builtin/ide/vscode/provider.json +64 -0
- package/providers/_builtin/ide/vscode-insiders/provider.json +62 -0
- package/providers/_builtin/ide/vscodium/provider.json +63 -0
- package/providers/_builtin/ide/windsurf/provider.json +53 -0
- package/providers/_builtin/ide/windsurf/scripts/focus_editor.js +30 -0
- package/providers/_builtin/ide/windsurf/scripts/list_chats.js +117 -0
- package/providers/_builtin/ide/windsurf/scripts/list_models.js +39 -0
- package/providers/_builtin/ide/windsurf/scripts/list_modes.js +39 -0
- package/providers/_builtin/ide/windsurf/scripts/new_session.js +69 -0
- package/providers/_builtin/ide/windsurf/scripts/open_panel.js +58 -0
- package/providers/_builtin/ide/windsurf/scripts/read_chat.js +297 -0
- package/providers/_builtin/ide/windsurf/scripts/resolve_action.js +68 -0
- package/providers/_builtin/ide/windsurf/scripts/send_message.js +87 -0
- package/providers/_builtin/ide/windsurf/scripts/set_mode.js +15 -0
- package/providers/_builtin/ide/windsurf/scripts/set_model.js +15 -0
- package/providers/_builtin/ide/windsurf/scripts/switch_session.js +58 -0
- package/providers/_builtin/ide/windsurf/scripts.js +57 -0
- package/providers/_builtin/registry.json +266 -0
- package/providers/_builtin/validate.js +156 -0
- package/src/agent-stream/index.ts +6 -0
- package/src/agent-stream/manager.ts +286 -0
- package/src/agent-stream/poller.ts +154 -0
- package/src/agent-stream/provider-adapter.ts +138 -0
- package/src/agent-stream/types.ts +61 -0
- package/src/boot/daemon-lifecycle.ts +252 -0
- package/src/cdp/devtools.ts +335 -0
- package/src/cdp/initializer.ts +191 -0
- package/src/cdp/manager.ts +897 -0
- package/src/cdp/scanner.ts +185 -0
- package/src/cdp/setup.ts +150 -0
- package/src/cli-adapter-types.ts +25 -0
- package/src/cli-adapters/provider-cli-adapter.ts +448 -0
- package/src/commands/cdp-commands.ts +208 -0
- package/src/commands/chat-commands.ts +675 -0
- package/src/commands/cli-manager.ts +353 -0
- package/src/commands/handler.ts +328 -0
- package/src/commands/router.ts +258 -0
- package/src/commands/stream-commands.ts +325 -0
- package/src/config/chat-history.ts +211 -0
- package/src/config/config.ts +219 -0
- package/src/daemon/dev-server.ts +2378 -0
- package/src/daemon/scaffold-template.ts +394 -0
- package/src/daemon-core.ts +50 -0
- package/src/detection/cli-detector.ts +89 -0
- package/src/detection/ide-detector.ts +157 -0
- package/src/index.ts +103 -0
- package/src/installer.ts +263 -0
- package/src/ipc-protocol.ts +133 -0
- package/src/launch.ts +433 -0
- package/src/logging/command-log.ts +180 -0
- package/src/logging/logger.ts +316 -0
- package/src/providers/acp-provider-instance.ts +1140 -0
- package/src/providers/cli-provider-instance.ts +207 -0
- package/src/providers/contracts.ts +524 -0
- package/src/providers/extension-provider-instance.ts +156 -0
- package/src/providers/ide-provider-instance.ts +377 -0
- package/src/providers/index.ts +18 -0
- package/src/providers/provider-instance-manager.ts +182 -0
- package/src/providers/provider-instance.ts +112 -0
- package/src/providers/provider-loader.ts +1031 -0
- package/src/providers/status-monitor.ts +125 -0
- package/src/providers/version-archive.ts +266 -0
- package/src/status/reporter.ts +294 -0
- package/src/types.ts +206 -0
package/src/launch.ts
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADHDev Launcher — IDE Launch/Relaunch with CDP
|
|
3
|
+
*
|
|
4
|
+
* Launches IDE with Chrome DevTools Protocol (remote-debugging-port).
|
|
5
|
+
* If IDE is already running, terminates it and restarts with CDP option.
|
|
6
|
+
*
|
|
7
|
+
* Pipeline:
|
|
8
|
+
* 1. IDE process detection (already running?)
|
|
9
|
+
* 2. If already running with CDP → reuse as-is
|
|
10
|
+
* 3. If running without CDP → kill process → wait → restart with CDP
|
|
11
|
+
* 4. Not running → start fresh with CDP
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* adhdev launch — Launch configured IDE with CDP port
|
|
15
|
+
* adhdev launch cursor — Launch Cursor with CDP port
|
|
16
|
+
* adhdev launch --workspace /path — Open specific workspace
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { execSync, spawn, spawnSync } from 'child_process';
|
|
20
|
+
import * as net from 'net';
|
|
21
|
+
import * as os from 'os';
|
|
22
|
+
import * as path from 'path';
|
|
23
|
+
import { detectIDEs } from './detection/ide-detector.js';
|
|
24
|
+
import { IDEInfo } from './detection/ide-detector.js';
|
|
25
|
+
import { ProviderLoader } from './providers/provider-loader.js';
|
|
26
|
+
|
|
27
|
+
// ─── Provider-based dynamic IDE infrastructure ────────────────
|
|
28
|
+
// Reads cdpPorts, processNames from provider.js — only create provider.js to add new IDE
|
|
29
|
+
|
|
30
|
+
let _providerLoader: ProviderLoader | null = null;
|
|
31
|
+
|
|
32
|
+
function getProviderLoader(): ProviderLoader {
|
|
33
|
+
if (!_providerLoader) {
|
|
34
|
+
_providerLoader = new ProviderLoader({ logFn: () => {} }); // Suppress logs during launch
|
|
35
|
+
_providerLoader.loadAll();
|
|
36
|
+
_providerLoader.registerToDetector(); // IDE provider → detector registry
|
|
37
|
+
}
|
|
38
|
+
return _providerLoader;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getCdpPorts(): Record<string, [number, number]> {
|
|
42
|
+
return getProviderLoader().getCdpPortMap();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getMacAppIdentifiers(): Record<string, string> {
|
|
46
|
+
return getProviderLoader().getMacAppIdentifiers();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getWinProcessNames(): Record<string, string[]> {
|
|
50
|
+
return getProviderLoader().getWinProcessNames();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ─── Helpers ────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
/** Find available port (primary → secondary → sequential after) */
|
|
56
|
+
async function findFreePort(ports: [number, number]): Promise<number> {
|
|
57
|
+
for (const port of ports) {
|
|
58
|
+
const free = await checkPortFree(port);
|
|
59
|
+
if (free) return port;
|
|
60
|
+
}
|
|
61
|
+
// If both ports in use, scan from secondary+1
|
|
62
|
+
let port = ports[1] + 1;
|
|
63
|
+
while (port < ports[1] + 10) {
|
|
64
|
+
if (await checkPortFree(port)) return port;
|
|
65
|
+
port++;
|
|
66
|
+
}
|
|
67
|
+
throw new Error('No free port found');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function checkPortFree(port: number): Promise<boolean> {
|
|
71
|
+
return new Promise((resolve) => {
|
|
72
|
+
const server = net.createServer();
|
|
73
|
+
server.unref();
|
|
74
|
+
server.on('error', () => resolve(false));
|
|
75
|
+
server.listen(port, '127.0.0.1', () => {
|
|
76
|
+
server.close(() => resolve(true));
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Check if CDP responds on port */
|
|
82
|
+
async function isCdpActive(port: number): Promise<boolean> {
|
|
83
|
+
return new Promise((resolve) => {
|
|
84
|
+
const req = require('http').get(`http://127.0.0.1:${port}/json/version`, {
|
|
85
|
+
timeout: 2000,
|
|
86
|
+
}, (res: any) => {
|
|
87
|
+
let data = '';
|
|
88
|
+
res.on('data', (c: string) => data += c);
|
|
89
|
+
res.on('end', () => {
|
|
90
|
+
try {
|
|
91
|
+
const info = JSON.parse(data);
|
|
92
|
+
resolve(!!info['WebKit-Version'] || !!info['Browser']);
|
|
93
|
+
} catch {
|
|
94
|
+
resolve(false);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
req.on('error', () => resolve(false));
|
|
99
|
+
req.on('timeout', () => { req.destroy(); resolve(false); });
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Kill IDE process (graceful → force) */
|
|
104
|
+
async function killIdeProcess(ideId: string): Promise<boolean> {
|
|
105
|
+
const plat = os.platform();
|
|
106
|
+
const appName = getMacAppIdentifiers()[ideId];
|
|
107
|
+
const winProcesses = getWinProcessNames()[ideId];
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
if (plat === 'darwin' && appName) {
|
|
111
|
+
// macOS: graceful quit via osascript
|
|
112
|
+
try {
|
|
113
|
+
execSync(`osascript -e 'tell application "${appName}" to quit' 2>/dev/null`, {
|
|
114
|
+
timeout: 5000,
|
|
115
|
+
});
|
|
116
|
+
} catch {
|
|
117
|
+
try { execSync(`pkill -f "${appName}" 2>/dev/null`); } catch { }
|
|
118
|
+
}
|
|
119
|
+
} else if (plat === 'win32' && winProcesses) {
|
|
120
|
+
// Windows: taskkill for each process name
|
|
121
|
+
for (const proc of winProcesses) {
|
|
122
|
+
try {
|
|
123
|
+
execSync(`taskkill /IM "${proc}" /F 2>nul`, { timeout: 5000 });
|
|
124
|
+
} catch { }
|
|
125
|
+
}
|
|
126
|
+
// Process name may differ, so also try via WMIC
|
|
127
|
+
try {
|
|
128
|
+
const exeName = winProcesses[0].replace('.exe', '');
|
|
129
|
+
execSync(`powershell -Command "Get-Process -Name '${exeName}' -ErrorAction SilentlyContinue | Stop-Process -Force"`, {
|
|
130
|
+
timeout: 10000,
|
|
131
|
+
});
|
|
132
|
+
} catch { }
|
|
133
|
+
} else {
|
|
134
|
+
try { execSync(`pkill -f "${ideId}" 2>/dev/null`); } catch { }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Wait for process kill (max 15 seconds)
|
|
138
|
+
for (let i = 0; i < 30; i++) {
|
|
139
|
+
await new Promise(r => setTimeout(r, 500));
|
|
140
|
+
if (!isIdeRunning(ideId)) return true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Force terminate retry
|
|
144
|
+
if (plat === 'darwin' && appName) {
|
|
145
|
+
try { execSync(`pkill -9 -f "${appName}" 2>/dev/null`); } catch { }
|
|
146
|
+
} else if (plat === 'win32' && winProcesses) {
|
|
147
|
+
for (const proc of winProcesses) {
|
|
148
|
+
try { execSync(`taskkill /IM "${proc}" /F 2>nul`); } catch { }
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
153
|
+
return !isIdeRunning(ideId);
|
|
154
|
+
|
|
155
|
+
} catch {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Check if IDE process is running */
|
|
161
|
+
function isIdeRunning(ideId: string): boolean {
|
|
162
|
+
const plat = os.platform();
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
if (plat === 'darwin') {
|
|
166
|
+
const appName = getMacAppIdentifiers()[ideId];
|
|
167
|
+
if (!appName) return false;
|
|
168
|
+
const result = execSync(`pgrep -f "${appName}" 2>/dev/null`, { encoding: 'utf-8' });
|
|
169
|
+
return result.trim().length > 0;
|
|
170
|
+
} else if (plat === 'win32') {
|
|
171
|
+
const winProcesses = getWinProcessNames()[ideId];
|
|
172
|
+
if (!winProcesses) return false;
|
|
173
|
+
// Check each process name
|
|
174
|
+
for (const proc of winProcesses) {
|
|
175
|
+
try {
|
|
176
|
+
const result = execSync(`tasklist /FI "IMAGENAME eq ${proc}" /NH 2>nul`, { encoding: 'utf-8' });
|
|
177
|
+
if (result.includes(proc)) return true;
|
|
178
|
+
} catch { }
|
|
179
|
+
}
|
|
180
|
+
// Also check via PowerShell (when tasklist cannot find)
|
|
181
|
+
try {
|
|
182
|
+
const exeName = winProcesses[0].replace('.exe', '');
|
|
183
|
+
const result = execSync(
|
|
184
|
+
`powershell -Command "(Get-Process -Name '${exeName}' -ErrorAction SilentlyContinue).Count"`,
|
|
185
|
+
{ encoding: 'utf-8', timeout: 5000 }
|
|
186
|
+
);
|
|
187
|
+
return parseInt(result.trim()) > 0;
|
|
188
|
+
} catch { }
|
|
189
|
+
return false;
|
|
190
|
+
} else {
|
|
191
|
+
const result = execSync(`pgrep -f "${ideId}" 2>/dev/null`, { encoding: 'utf-8' });
|
|
192
|
+
return result.trim().length > 0;
|
|
193
|
+
}
|
|
194
|
+
} catch {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** Detect currently open workspace path */
|
|
200
|
+
function detectCurrentWorkspace(ideId: string): string | undefined {
|
|
201
|
+
const plat = os.platform();
|
|
202
|
+
|
|
203
|
+
if (plat === 'darwin') {
|
|
204
|
+
try {
|
|
205
|
+
const appName = getMacAppIdentifiers()[ideId];
|
|
206
|
+
if (!appName) return undefined;
|
|
207
|
+
const result = execSync(
|
|
208
|
+
`lsof -c "${appName}" 2>/dev/null | grep cwd | head -1 | awk '{print $NF}'`,
|
|
209
|
+
{ encoding: 'utf-8', timeout: 3000 }
|
|
210
|
+
);
|
|
211
|
+
const dir = result.trim();
|
|
212
|
+
if (dir && dir !== '/') return dir;
|
|
213
|
+
} catch { }
|
|
214
|
+
} else if (plat === 'win32') {
|
|
215
|
+
// Windows: read IDE recent workspaces from storage.json
|
|
216
|
+
try {
|
|
217
|
+
const fs = require('fs');
|
|
218
|
+
const appNameMap = getMacAppIdentifiers(); // Provider-based dynamic mapping
|
|
219
|
+
const appName = appNameMap[ideId];
|
|
220
|
+
if (appName) {
|
|
221
|
+
const storagePath = path.join(
|
|
222
|
+
process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'),
|
|
223
|
+
appName, 'storage.json'
|
|
224
|
+
);
|
|
225
|
+
if (fs.existsSync(storagePath)) {
|
|
226
|
+
const data = JSON.parse(fs.readFileSync(storagePath, 'utf-8'));
|
|
227
|
+
// openedPathsList.workspaces3 has recent folders
|
|
228
|
+
const workspaces = data?.openedPathsList?.workspaces3 || data?.openedPathsList?.entries || [];
|
|
229
|
+
if (workspaces.length > 0) {
|
|
230
|
+
const recent = workspaces[0];
|
|
231
|
+
// Can be object { folderUri: 'file:///...' } or string
|
|
232
|
+
const uri = typeof recent === 'string' ? recent : recent?.folderUri;
|
|
233
|
+
if (uri?.startsWith('file:///')) {
|
|
234
|
+
return decodeURIComponent(uri.replace('file:///', ''));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
} catch { }
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ─── Launch Logic ───────────────────────────────
|
|
246
|
+
|
|
247
|
+
export interface LaunchOptions {
|
|
248
|
+
ideId?: string;
|
|
249
|
+
workspace?: string;
|
|
250
|
+
newWindow?: boolean;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export interface LaunchResult {
|
|
254
|
+
success: boolean;
|
|
255
|
+
ideId: string;
|
|
256
|
+
ideName: string;
|
|
257
|
+
port: number;
|
|
258
|
+
action: 'started' | 'restarted' | 'reused' | 'failed';
|
|
259
|
+
message: string;
|
|
260
|
+
error?: string;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Execute IDE with CDP port (relaunch pipeline)
|
|
265
|
+
*
|
|
266
|
+
* 1. IDE detect
|
|
267
|
+
* 2. per-fixed IDE CDP port determine
|
|
268
|
+
* 3. CDP not active → reuse
|
|
269
|
+
* 4. IDE execute during but CDP none → terminate → restart with CDP
|
|
270
|
+
* 5. IDE not running → start fresh with CDP
|
|
271
|
+
*/
|
|
272
|
+
export async function launchWithCdp(options: LaunchOptions = {}): Promise<LaunchResult> {
|
|
273
|
+
const platform = os.platform();
|
|
274
|
+
|
|
275
|
+
// 1. IDE determine
|
|
276
|
+
let targetIde: IDEInfo | undefined;
|
|
277
|
+
const ides = await detectIDEs();
|
|
278
|
+
|
|
279
|
+
if (options.ideId) {
|
|
280
|
+
targetIde = ides.find(i => i.id === options.ideId && i.installed);
|
|
281
|
+
if (!targetIde) {
|
|
282
|
+
return {
|
|
283
|
+
success: false, ideId: options.ideId, ideName: options.ideId,
|
|
284
|
+
port: 0, action: 'failed',
|
|
285
|
+
message: '', error: `IDE '${options.ideId}' not found or not installed`,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
const { loadConfig } = await import('./config/config.js');
|
|
290
|
+
const config = loadConfig();
|
|
291
|
+
if (config.selectedIde) {
|
|
292
|
+
targetIde = ides.find(i => i.id === config.selectedIde && i.installed);
|
|
293
|
+
}
|
|
294
|
+
if (!targetIde) {
|
|
295
|
+
targetIde = ides.find(i => i.installed);
|
|
296
|
+
}
|
|
297
|
+
if (!targetIde) {
|
|
298
|
+
return {
|
|
299
|
+
success: false, ideId: 'unknown', ideName: 'Unknown',
|
|
300
|
+
port: 0, action: 'failed',
|
|
301
|
+
message: '', error: 'No IDE found. Install VS Code, Cursor, or Antigravity first.',
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// 2. per-fixed IDE CDP port determine
|
|
307
|
+
const portPair = getCdpPorts()[targetIde.id] || [9333, 9334];
|
|
308
|
+
|
|
309
|
+
// 3. Check if CDP is not yet enabled
|
|
310
|
+
for (const port of portPair) {
|
|
311
|
+
if (await isCdpActive(port)) {
|
|
312
|
+
return {
|
|
313
|
+
success: true, ideId: targetIde.id, ideName: targetIde.displayName,
|
|
314
|
+
port, action: 'reused',
|
|
315
|
+
message: `CDP already active on port ${port}`,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// 4. Check if IDE is currently running
|
|
321
|
+
const alreadyRunning = isIdeRunning(targetIde.id);
|
|
322
|
+
const workspace = options.workspace || (alreadyRunning ? detectCurrentWorkspace(targetIde.id) : undefined);
|
|
323
|
+
|
|
324
|
+
// 5. If IDE is running, terminate it
|
|
325
|
+
if (alreadyRunning) {
|
|
326
|
+
const killed = await killIdeProcess(targetIde.id);
|
|
327
|
+
if (!killed) {
|
|
328
|
+
return {
|
|
329
|
+
success: false, ideId: targetIde.id, ideName: targetIde.displayName,
|
|
330
|
+
port: 0, action: 'failed',
|
|
331
|
+
message: '', error: `Could not stop ${targetIde.displayName}. Close it manually and try again.`,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
// Wait for process full termination
|
|
335
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// 6. Find available port
|
|
339
|
+
const port = await findFreePort(portPair);
|
|
340
|
+
|
|
341
|
+
// 7. Execute with CDP
|
|
342
|
+
try {
|
|
343
|
+
if (platform === 'darwin') {
|
|
344
|
+
await launchMacOS(targetIde, port, workspace, options.newWindow);
|
|
345
|
+
} else if (platform === 'win32') {
|
|
346
|
+
await launchWindows(targetIde, port, workspace, options.newWindow);
|
|
347
|
+
} else {
|
|
348
|
+
await launchLinux(targetIde, port, workspace, options.newWindow);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Wait for CDP to enable (max 15 seconds)
|
|
352
|
+
let cdpReady = false;
|
|
353
|
+
for (let i = 0; i < 30; i++) {
|
|
354
|
+
await new Promise(r => setTimeout(r, 500));
|
|
355
|
+
if (await isCdpActive(port)) {
|
|
356
|
+
cdpReady = true;
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return {
|
|
362
|
+
success: true, ideId: targetIde.id, ideName: targetIde.displayName,
|
|
363
|
+
port, action: alreadyRunning ? 'restarted' : 'started',
|
|
364
|
+
message: cdpReady
|
|
365
|
+
? `${targetIde.displayName} launched with CDP on port ${port}`
|
|
366
|
+
: `${targetIde.displayName} launched (CDP may take a moment to initialize)`,
|
|
367
|
+
};
|
|
368
|
+
} catch (e: any) {
|
|
369
|
+
return {
|
|
370
|
+
success: false, ideId: targetIde.id, ideName: targetIde.displayName,
|
|
371
|
+
port, action: 'failed',
|
|
372
|
+
message: '', error: e?.message || String(e),
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ─── Platform Launch ────────────────────────────
|
|
378
|
+
|
|
379
|
+
async function launchMacOS(ide: IDEInfo, port: number, workspace?: string, newWindow?: boolean): Promise<void> {
|
|
380
|
+
const appName = getMacAppIdentifiers()[ide.id];
|
|
381
|
+
|
|
382
|
+
const args = ['--remote-debugging-port=' + port];
|
|
383
|
+
if (newWindow) args.push('--new-window');
|
|
384
|
+
if (workspace) args.push(workspace);
|
|
385
|
+
|
|
386
|
+
if (appName) {
|
|
387
|
+
// 'open -a' execution (ensures GUI session)
|
|
388
|
+
const openArgs = ['-a', appName, '--args', ...args];
|
|
389
|
+
spawn('open', openArgs, { detached: true, stdio: 'ignore' }).unref();
|
|
390
|
+
} else if (ide.cliCommand) {
|
|
391
|
+
// CLI based execute
|
|
392
|
+
spawn(ide.cliCommand, args, { detached: true, stdio: 'ignore' }).unref();
|
|
393
|
+
} else {
|
|
394
|
+
throw new Error(`No app identifier or CLI for ${ide.displayName}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async function launchWindows(ide: IDEInfo, port: number, workspace?: string, newWindow?: boolean): Promise<void> {
|
|
399
|
+
const cli = ide.cliCommand;
|
|
400
|
+
if (!cli) {
|
|
401
|
+
throw new Error(`No CLI command for ${ide.displayName}. Please add it to PATH.`);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Compose arguments for CLI command — IDE CLI wrapper (.cmd) handles Electron execution
|
|
405
|
+
const parts = [`"${cli}"`, `--remote-debugging-port=${port}`];
|
|
406
|
+
if (newWindow) parts.push('--new-window');
|
|
407
|
+
if (workspace) parts.push(`"${workspace}"`);
|
|
408
|
+
|
|
409
|
+
const fullCmd = parts.join(' ');
|
|
410
|
+
|
|
411
|
+
// exec fire-and-forget: delegate to CLI to properly start IDE process
|
|
412
|
+
const { exec: execCmd } = require('child_process');
|
|
413
|
+
execCmd(fullCmd, { windowsHide: true }, () => {
|
|
414
|
+
// IDE process runs independently even after CLI per-terminates wrap
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
async function launchLinux(ide: IDEInfo, port: number, workspace?: string, newWindow?: boolean): Promise<void> {
|
|
419
|
+
const cli = ide.cliCommand;
|
|
420
|
+
if (!cli) {
|
|
421
|
+
throw new Error(`No CLI command for ${ide.displayName}. Make sure it's in PATH.`);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const args = ['--remote-debugging-port=' + port];
|
|
425
|
+
if (newWindow) args.push('--new-window');
|
|
426
|
+
if (workspace) args.push(workspace);
|
|
427
|
+
|
|
428
|
+
spawn(cli, args, { detached: true, stdio: 'ignore' }).unref();
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export function getAvailableIdeIds(): string[] {
|
|
432
|
+
return getProviderLoader().getAvailableIdeTypes();
|
|
433
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADHDev Daemon — Command History Logger
|
|
3
|
+
*
|
|
4
|
+
* Record all commands from dashboard/WS/P2P/Extension/API to local file.
|
|
5
|
+
* Per-date JSONL file, 7-day retention, 5MB limit.
|
|
6
|
+
*
|
|
7
|
+
* Purpose:
|
|
8
|
+
* - Debugging: track what command came and when
|
|
9
|
+
* - Audit: record all commands executed from remote
|
|
10
|
+
* - Stats: identify frequently used features
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import * as os from 'os';
|
|
16
|
+
|
|
17
|
+
// ─── Config ──────────────────────────────────
|
|
18
|
+
const LOG_DIR = process.platform === 'darwin'
|
|
19
|
+
? path.join(os.homedir(), 'Library', 'Logs', 'adhdev')
|
|
20
|
+
: path.join(os.homedir(), '.local', 'share', 'adhdev', 'logs');
|
|
21
|
+
|
|
22
|
+
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
|
|
23
|
+
const MAX_DAYS = 7;
|
|
24
|
+
|
|
25
|
+
try { fs.mkdirSync(LOG_DIR, { recursive: true }); } catch { }
|
|
26
|
+
|
|
27
|
+
// ─── Types ───────────────────────────────────
|
|
28
|
+
export interface CommandLogEntry {
|
|
29
|
+
ts: string; // ISO timestamp
|
|
30
|
+
cmd: string; // command name
|
|
31
|
+
source: 'ws' | 'p2p' | 'ext' | 'api' | 'standalone' | 'unknown'; // where it came from
|
|
32
|
+
args?: Record<string, unknown>; // command arguments (sensitive values masked)
|
|
33
|
+
success?: boolean; // result
|
|
34
|
+
error?: string; // error message if failed
|
|
35
|
+
durationMs?: number; // execution time
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ─── Sensitive field masking ─────────────────
|
|
39
|
+
const SENSITIVE_KEYS = new Set([
|
|
40
|
+
'token', 'password', 'secret', 'apiKey', 'api_key',
|
|
41
|
+
'connectionToken', 'content', 'message', 'text',
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
function maskArgs(args: any): Record<string, unknown> | undefined {
|
|
45
|
+
if (!args || typeof args !== 'object') return undefined;
|
|
46
|
+
const masked: Record<string, unknown> = {};
|
|
47
|
+
for (const [key, value] of Object.entries(args)) {
|
|
48
|
+
if (SENSITIVE_KEYS.has(key)) {
|
|
49
|
+
masked[key] = typeof value === 'string'
|
|
50
|
+
? `[${value.length} chars]`
|
|
51
|
+
: '[masked]';
|
|
52
|
+
} else if (key.startsWith('_')) {
|
|
53
|
+
// internal fields: keep as-is (e.g. _targetType, _targetInstance)
|
|
54
|
+
masked[key] = value;
|
|
55
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
56
|
+
// Don't recurse deeply — just note the type
|
|
57
|
+
masked[key] = Array.isArray(value)
|
|
58
|
+
? `[Array(${value.length})]`
|
|
59
|
+
: `[Object]`;
|
|
60
|
+
} else {
|
|
61
|
+
masked[key] = value;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return masked;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ─── File management ─────────────────────────
|
|
68
|
+
function getDateStr(): string {
|
|
69
|
+
return new Date().toISOString().slice(0, 10);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let currentDate = getDateStr();
|
|
73
|
+
let currentFile = path.join(LOG_DIR, `commands-${currentDate}.jsonl`);
|
|
74
|
+
let writeCount = 0;
|
|
75
|
+
|
|
76
|
+
function checkRotation(): void {
|
|
77
|
+
const today = getDateStr();
|
|
78
|
+
if (today !== currentDate) {
|
|
79
|
+
currentDate = today;
|
|
80
|
+
currentFile = path.join(LOG_DIR, `commands-${currentDate}.jsonl`);
|
|
81
|
+
cleanOldFiles();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function cleanOldFiles(): void {
|
|
86
|
+
try {
|
|
87
|
+
const files = fs.readdirSync(LOG_DIR).filter(f => f.startsWith('commands-') && f.endsWith('.jsonl'));
|
|
88
|
+
const cutoff = new Date();
|
|
89
|
+
cutoff.setDate(cutoff.getDate() - MAX_DAYS);
|
|
90
|
+
const cutoffStr = cutoff.toISOString().slice(0, 10);
|
|
91
|
+
for (const file of files) {
|
|
92
|
+
const dateMatch = file.match(/commands-(\d{4}-\d{2}-\d{2})/);
|
|
93
|
+
if (dateMatch && dateMatch[1] < cutoffStr) {
|
|
94
|
+
try { fs.unlinkSync(path.join(LOG_DIR, file)); } catch { }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
} catch { }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function checkSize(): void {
|
|
101
|
+
try {
|
|
102
|
+
const stat = fs.statSync(currentFile);
|
|
103
|
+
if (stat.size > MAX_FILE_SIZE) {
|
|
104
|
+
const backup = currentFile.replace('.jsonl', '.1.jsonl');
|
|
105
|
+
try { fs.unlinkSync(backup); } catch { }
|
|
106
|
+
fs.renameSync(currentFile, backup);
|
|
107
|
+
}
|
|
108
|
+
} catch { /* file doesn't exist yet */ }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ─── Noise filter ────────────────────────────
|
|
112
|
+
// These commands are too frequent / low-value to log
|
|
113
|
+
const SKIP_COMMANDS = new Set([
|
|
114
|
+
'heartbeat',
|
|
115
|
+
'status_report',
|
|
116
|
+
]);
|
|
117
|
+
|
|
118
|
+
// ─── Public API ──────────────────────────────
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Log a command received from the dashboard/WS/P2P/extension/API.
|
|
122
|
+
* Call this at the entry point of command handling.
|
|
123
|
+
*/
|
|
124
|
+
export function logCommand(entry: CommandLogEntry): void {
|
|
125
|
+
if (SKIP_COMMANDS.has(entry.cmd)) return;
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
if (++writeCount % 500 === 0) {
|
|
129
|
+
checkRotation();
|
|
130
|
+
checkSize();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const line = JSON.stringify({
|
|
134
|
+
ts: entry.ts,
|
|
135
|
+
cmd: entry.cmd,
|
|
136
|
+
src: entry.source,
|
|
137
|
+
...(entry.args ? { args: maskArgs(entry.args) } : {}),
|
|
138
|
+
...(entry.success !== undefined ? { ok: entry.success } : {}),
|
|
139
|
+
...(entry.error ? { err: entry.error } : {}),
|
|
140
|
+
...(entry.durationMs !== undefined ? { ms: entry.durationMs } : {}),
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
fs.appendFileSync(currentFile, line + '\n');
|
|
144
|
+
} catch { /* never crash the daemon for logging */ }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Read recent command history (for dashboard display / debugging)
|
|
149
|
+
*/
|
|
150
|
+
export function getRecentCommands(count = 50): CommandLogEntry[] {
|
|
151
|
+
try {
|
|
152
|
+
if (!fs.existsSync(currentFile)) return [];
|
|
153
|
+
const content = fs.readFileSync(currentFile, 'utf-8');
|
|
154
|
+
const lines = content.trim().split('\n').filter(Boolean);
|
|
155
|
+
return lines.slice(-count).map(line => {
|
|
156
|
+
try {
|
|
157
|
+
const parsed = JSON.parse(line);
|
|
158
|
+
return {
|
|
159
|
+
ts: parsed.ts,
|
|
160
|
+
cmd: parsed.cmd,
|
|
161
|
+
source: parsed.src,
|
|
162
|
+
args: parsed.args,
|
|
163
|
+
success: parsed.ok,
|
|
164
|
+
error: parsed.err,
|
|
165
|
+
durationMs: parsed.ms,
|
|
166
|
+
};
|
|
167
|
+
} catch {
|
|
168
|
+
return { ts: '', cmd: 'parse_error', source: 'unknown' as const };
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
} catch {
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Current command log file path */
|
|
177
|
+
export function getCommandLogPath(): string { return currentFile; }
|
|
178
|
+
|
|
179
|
+
// Initial cleanup
|
|
180
|
+
cleanOldFiles();
|