clawmate 1.4.2 → 1.5.0
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/main/ai-bridge.js +12 -0
- package/main/autostart.js +69 -2
- package/main/index.js +13 -2
- package/main/ipc-handlers.js +24 -1
- package/main/platform.js +17 -1
- package/main/proactive-monitor.js +1009 -0
- package/main/tray.js +26 -1
- package/package.json +1 -1
- package/preload/preload.js +7 -0
- package/renderer/index.html +1 -0
- package/renderer/js/app.js +5 -0
- package/renderer/js/browser-watcher.js +6 -0
- package/renderer/js/proactive-controller.js +205 -0
- package/shared/messages.js +408 -0
- package/skills/launch-pet/index.js +86 -4
package/main/ai-bridge.js
CHANGED
|
@@ -345,6 +345,18 @@ class AIBridge extends EventEmitter {
|
|
|
345
345
|
});
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
+
/**
|
|
349
|
+
* Report proactive trigger event to AI
|
|
350
|
+
* Sent when ProactiveMonitor detects user activity patterns
|
|
351
|
+
*/
|
|
352
|
+
reportProactiveEvent(triggerType, context) {
|
|
353
|
+
this.send('proactive_trigger', {
|
|
354
|
+
trigger: triggerType,
|
|
355
|
+
context,
|
|
356
|
+
timestamp: Date.now(),
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
348
360
|
// === State Updates ===
|
|
349
361
|
|
|
350
362
|
updatePetState(updates) {
|
package/main/autostart.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Windows: Registry Run key
|
|
5
5
|
* macOS: LaunchAgent plist
|
|
6
6
|
* Linux: Create .desktop file in ~/.config/autostart/ directory
|
|
7
|
+
* WSL: Create .bat file in Windows Startup folder
|
|
7
8
|
*
|
|
8
9
|
* Even if AI starts later, ClawMate is already running for immediate connection.
|
|
9
10
|
* ClawMate runs in autonomous mode first -> switches to AI mode when AI connects.
|
|
@@ -12,11 +13,46 @@ const { app } = require('electron');
|
|
|
12
13
|
const fs = require('fs');
|
|
13
14
|
const path = require('path');
|
|
14
15
|
const os = require('os');
|
|
15
|
-
const {
|
|
16
|
+
const { execSync } = require('child_process');
|
|
17
|
+
const { isLinux, isWSL } = require('./platform');
|
|
16
18
|
|
|
17
19
|
const LINUX_AUTOSTART_DIR = path.join(os.homedir(), '.config', 'autostart');
|
|
18
20
|
const LINUX_DESKTOP_FILE = path.join(LINUX_AUTOSTART_DIR, 'clawmate.desktop');
|
|
19
21
|
|
|
22
|
+
/**
|
|
23
|
+
* WSL: Windows 시작프로그램 폴더 경로 + 배치파일명
|
|
24
|
+
*/
|
|
25
|
+
function getWSLStartupBatPath() {
|
|
26
|
+
try {
|
|
27
|
+
const winUserProfile = execSync('cmd.exe /c "echo %USERPROFILE%"', {
|
|
28
|
+
encoding: 'utf-8',
|
|
29
|
+
}).trim().replace(/\r/g, '');
|
|
30
|
+
// Windows 경로를 Linux 경로로 변환하여 fs로 접근
|
|
31
|
+
const linuxStartup = execSync(
|
|
32
|
+
`wslpath "${winUserProfile}\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup"`,
|
|
33
|
+
{ encoding: 'utf-8' }
|
|
34
|
+
).trim();
|
|
35
|
+
return path.join(linuxStartup, 'clawmate.bat');
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* WSL: Windows Startup 폴더에 넣을 배치파일 내용
|
|
43
|
+
*/
|
|
44
|
+
function getWSLBatContent() {
|
|
45
|
+
try {
|
|
46
|
+
const appRoot = path.resolve(__dirname, '..');
|
|
47
|
+
const winAppRoot = execSync(`wslpath -w "${appRoot}"`, {
|
|
48
|
+
encoding: 'utf-8',
|
|
49
|
+
}).trim();
|
|
50
|
+
return `@echo off\r\nnpx.cmd electron ${winAppRoot}\r\n`;
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
20
56
|
function getDesktopFileContent() {
|
|
21
57
|
return [
|
|
22
58
|
'[Desktop Entry]',
|
|
@@ -31,6 +67,15 @@ function getDesktopFileContent() {
|
|
|
31
67
|
}
|
|
32
68
|
|
|
33
69
|
function isAutoStartEnabled() {
|
|
70
|
+
if (isWSL()) {
|
|
71
|
+
const batPath = getWSLStartupBatPath();
|
|
72
|
+
if (!batPath) return false;
|
|
73
|
+
try {
|
|
74
|
+
return fs.existsSync(batPath);
|
|
75
|
+
} catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
34
79
|
if (isLinux()) {
|
|
35
80
|
try {
|
|
36
81
|
return fs.existsSync(LINUX_DESKTOP_FILE);
|
|
@@ -42,6 +87,17 @@ function isAutoStartEnabled() {
|
|
|
42
87
|
}
|
|
43
88
|
|
|
44
89
|
function enableAutoStart() {
|
|
90
|
+
if (isWSL()) {
|
|
91
|
+
try {
|
|
92
|
+
const batPath = getWSLStartupBatPath();
|
|
93
|
+
const batContent = getWSLBatContent();
|
|
94
|
+
if (!batPath || !batContent) return false;
|
|
95
|
+
fs.writeFileSync(batPath, batContent, 'utf-8');
|
|
96
|
+
return true;
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
45
101
|
if (isLinux()) {
|
|
46
102
|
try {
|
|
47
103
|
fs.mkdirSync(LINUX_AUTOSTART_DIR, { recursive: true });
|
|
@@ -53,7 +109,7 @@ function enableAutoStart() {
|
|
|
53
109
|
}
|
|
54
110
|
app.setLoginItemSettings({
|
|
55
111
|
openAtLogin: true,
|
|
56
|
-
openAsHidden: true,
|
|
112
|
+
openAsHidden: true,
|
|
57
113
|
path: process.execPath,
|
|
58
114
|
args: [path.resolve(__dirname, '..')],
|
|
59
115
|
});
|
|
@@ -61,6 +117,17 @@ function enableAutoStart() {
|
|
|
61
117
|
}
|
|
62
118
|
|
|
63
119
|
function disableAutoStart() {
|
|
120
|
+
if (isWSL()) {
|
|
121
|
+
try {
|
|
122
|
+
const batPath = getWSLStartupBatPath();
|
|
123
|
+
if (batPath && fs.existsSync(batPath)) {
|
|
124
|
+
fs.unlinkSync(batPath);
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
} catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
64
131
|
if (isLinux()) {
|
|
65
132
|
try {
|
|
66
133
|
if (fs.existsSync(LINUX_DESKTOP_FILE)) {
|
package/main/index.js
CHANGED
|
@@ -4,11 +4,13 @@ const { setupTray } = require('./tray');
|
|
|
4
4
|
const { registerIpcHandlers } = require('./ipc-handlers');
|
|
5
5
|
const { AIBridge } = require('./ai-bridge');
|
|
6
6
|
const { TelegramBot } = require('./telegram');
|
|
7
|
+
const { ProactiveMonitor } = require('./proactive-monitor');
|
|
7
8
|
|
|
8
9
|
let mainWindow = null;
|
|
9
10
|
let launcherWindow = null;
|
|
10
11
|
let aiBridge = null;
|
|
11
12
|
let telegramBot = null;
|
|
13
|
+
let proactiveMonitor = null;
|
|
12
14
|
|
|
13
15
|
function createMainWindow() {
|
|
14
16
|
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
|
|
@@ -154,14 +156,22 @@ function startAIBridge(win) {
|
|
|
154
156
|
}
|
|
155
157
|
|
|
156
158
|
app.whenReady().then(() => {
|
|
157
|
-
registerIpcHandlers(() => mainWindow, () => aiBridge);
|
|
159
|
+
registerIpcHandlers(() => mainWindow, () => aiBridge, () => proactiveMonitor);
|
|
158
160
|
const win = createMainWindow();
|
|
159
161
|
const bridge = startAIBridge(win);
|
|
160
|
-
setupTray(win, bridge);
|
|
162
|
+
setupTray(win, bridge, () => proactiveMonitor);
|
|
161
163
|
|
|
162
164
|
// Initialize Telegram bot (silently ignored if no token)
|
|
163
165
|
telegramBot = new TelegramBot(bridge);
|
|
164
166
|
|
|
167
|
+
// Initialize Proactive Monitor
|
|
168
|
+
const Store = require('./store');
|
|
169
|
+
const configStore = new Store('clawmate-config', { proactiveEnabled: true });
|
|
170
|
+
proactiveMonitor = new ProactiveMonitor();
|
|
171
|
+
if (configStore.get('proactiveEnabled') !== false) {
|
|
172
|
+
proactiveMonitor.start(win, bridge);
|
|
173
|
+
}
|
|
174
|
+
|
|
165
175
|
// Register auto-start on first install
|
|
166
176
|
const { enableAutoStart, isAutoStartEnabled } = require('./autostart');
|
|
167
177
|
if (!isAutoStartEnabled()) {
|
|
@@ -178,6 +188,7 @@ app.on('window-all-closed', () => {
|
|
|
178
188
|
});
|
|
179
189
|
|
|
180
190
|
app.on('before-quit', () => {
|
|
191
|
+
if (proactiveMonitor) proactiveMonitor.stop();
|
|
181
192
|
if (telegramBot) telegramBot.stop();
|
|
182
193
|
if (aiBridge) aiBridge.stop();
|
|
183
194
|
});
|
package/main/ipc-handlers.js
CHANGED
|
@@ -17,7 +17,7 @@ const memoryStore = new Store('clawmate-memory', {
|
|
|
17
17
|
milestones: [],
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
function registerIpcHandlers(getMainWindow, getAIBridge) {
|
|
20
|
+
function registerIpcHandlers(getMainWindow, getAIBridge, getProactiveMonitor) {
|
|
21
21
|
// Click-through control
|
|
22
22
|
ipcMain.on('set-click-through', (event, ignore) => {
|
|
23
23
|
const win = getMainWindow();
|
|
@@ -239,6 +239,29 @@ function registerIpcHandlers(getMainWindow, getAIBridge) {
|
|
|
239
239
|
ipcMain.handle('undo-all-smart-moves', async () => {
|
|
240
240
|
return undoAllSmartMoves();
|
|
241
241
|
});
|
|
242
|
+
|
|
243
|
+
// === Proactive Monitor ===
|
|
244
|
+
|
|
245
|
+
ipcMain.handle('get-proactive-config', () => {
|
|
246
|
+
const monitor = getProactiveMonitor ? getProactiveMonitor() : null;
|
|
247
|
+
return {
|
|
248
|
+
enabled: monitor ? monitor.enabled : false,
|
|
249
|
+
};
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
ipcMain.handle('set-proactive-enabled', (_, enabled) => {
|
|
253
|
+
const monitor = getProactiveMonitor ? getProactiveMonitor() : null;
|
|
254
|
+
if (monitor) {
|
|
255
|
+
if (enabled && !monitor.enabled) {
|
|
256
|
+
monitor.start(getMainWindow(), getAIBridge());
|
|
257
|
+
} else if (!enabled && monitor.enabled) {
|
|
258
|
+
monitor.stop();
|
|
259
|
+
}
|
|
260
|
+
monitor.enabled = enabled;
|
|
261
|
+
}
|
|
262
|
+
store.set('proactiveEnabled', enabled);
|
|
263
|
+
return enabled;
|
|
264
|
+
});
|
|
242
265
|
}
|
|
243
266
|
|
|
244
267
|
module.exports = { registerIpcHandlers };
|
package/main/platform.js
CHANGED
|
@@ -7,6 +7,22 @@ const execAsync = promisify(exec);
|
|
|
7
7
|
|
|
8
8
|
const platform = os.platform();
|
|
9
9
|
|
|
10
|
+
let _isWSL = null;
|
|
11
|
+
function isWSL() {
|
|
12
|
+
if (_isWSL !== null) return _isWSL;
|
|
13
|
+
if (platform !== 'linux') {
|
|
14
|
+
_isWSL = false;
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const procVersion = require('fs').readFileSync('/proc/version', 'utf-8');
|
|
19
|
+
_isWSL = /microsoft/i.test(procVersion);
|
|
20
|
+
} catch {
|
|
21
|
+
_isWSL = false;
|
|
22
|
+
}
|
|
23
|
+
return _isWSL;
|
|
24
|
+
}
|
|
25
|
+
|
|
10
26
|
function getDesktopPath() {
|
|
11
27
|
if (platform === 'win32') {
|
|
12
28
|
try {
|
|
@@ -260,4 +276,4 @@ public class FGWin {
|
|
|
260
276
|
}
|
|
261
277
|
}
|
|
262
278
|
|
|
263
|
-
module.exports = { getDesktopPath, getTrayIconExt, isWindows, isMac, isLinux, platform, getWindowPositions, getActiveWindowTitle };
|
|
279
|
+
module.exports = { getDesktopPath, getTrayIconExt, isWindows, isMac, isLinux, isWSL, platform, getWindowPositions, getActiveWindowTitle };
|