clawmate 1.2.0 → 1.4.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/electron-builder.yml +1 -1
- package/index.js +945 -17
- package/main/ai-bridge.js +76 -16
- package/main/ai-connector.js +94 -11
- package/main/autostart.js +3 -3
- package/main/file-command-parser.js +360 -0
- package/main/index.js +19 -5
- package/main/ipc-handlers.js +107 -2
- package/main/platform.js +48 -1
- package/main/smart-file-ops.js +373 -0
- package/main/telegram.js +593 -0
- package/main/tray.js +307 -39
- package/openclaw.plugin.json +1 -1
- package/package.json +4 -4
- package/preload/preload.js +19 -3
- package/renderer/first-run.html +2 -2
- package/renderer/index.html +2 -0
- package/renderer/js/ai-controller.js +312 -7
- package/renderer/js/app.js +19 -6
- package/renderer/js/browser-watcher.js +172 -0
- package/renderer/js/character.js +119 -22
- package/renderer/js/interactions.js +45 -2
- package/renderer/js/memory.js +108 -1
- package/renderer/js/metrics.js +607 -0
- package/renderer/js/mode-manager.js +53 -9
- package/renderer/js/pet-engine.js +372 -30
- package/renderer/js/state-machine.js +7 -0
- package/renderer/launcher.html +3 -3
- package/shared/messages.js +110 -0
- package/shared/personalities.js +37 -2
- package/skills/launch-pet/index.js +1 -1
- package/skills/launch-pet/skill.json +1 -1
package/main/tray.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
const { Tray, Menu, nativeImage, app } = require('electron');
|
|
1
|
+
const { Tray, Menu, nativeImage, app, shell, dialog, clipboard } = require('electron');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const { execSync } = require('child_process');
|
|
3
4
|
const Store = require('./store');
|
|
4
5
|
const { undoAllMoves, getFileManifest } = require('./file-ops');
|
|
5
6
|
const { isAutoStartEnabled, toggleAutoStart } = require('./autostart');
|
|
@@ -7,29 +8,111 @@ const { isAutoStartEnabled, toggleAutoStart } = require('./autostart');
|
|
|
7
8
|
let tray = null;
|
|
8
9
|
let aiBridge = null;
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
11
|
+
/**
|
|
12
|
+
* 16x16 Claw 픽셀아트 아이콘 생성
|
|
13
|
+
*/
|
|
14
|
+
const CLAW_ICON = [
|
|
15
|
+
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
|
16
|
+
[0,0,6,6,0,0,0,0,0,0,0,0,6,6,0,0],
|
|
17
|
+
[0,6,0,0,6,0,0,0,0,0,0,6,0,0,6,0],
|
|
18
|
+
[0,6,0,0,6,0,0,0,0,0,0,6,0,0,6,0],
|
|
19
|
+
[0,0,6,6,0,0,0,0,0,0,0,0,6,6,0,0],
|
|
20
|
+
[0,0,0,6,1,1,0,0,0,0,1,1,6,0,0,0],
|
|
21
|
+
[0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0],
|
|
22
|
+
[0,0,0,1,1,4,5,1,1,4,5,1,1,0,0,0],
|
|
23
|
+
[0,0,0,1,1,4,5,1,1,4,5,1,1,0,0,0],
|
|
24
|
+
[0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0],
|
|
25
|
+
[0,0,1,2,1,1,1,1,1,1,1,1,2,1,0,0],
|
|
26
|
+
[0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0],
|
|
27
|
+
[0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0],
|
|
28
|
+
[0,0,3,3,3,0,3,3,3,3,0,3,3,3,0,0],
|
|
29
|
+
[0,3,3,3,0,0,0,3,3,0,0,0,3,3,3,0],
|
|
30
|
+
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const COLOR_MAP = {
|
|
34
|
+
0: [0, 0, 0, 0],
|
|
35
|
+
1: [255, 79, 64, 255],
|
|
36
|
+
2: [255, 119, 95, 255],
|
|
37
|
+
3: [58, 10, 13, 255],
|
|
38
|
+
4: [255, 255, 255, 255],
|
|
39
|
+
5: [0, 0, 0, 255],
|
|
40
|
+
6: [255, 79, 64, 255],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 캐릭터 프리셋 목록
|
|
45
|
+
* 트레이에서 선택하면 set_character 명령으로 렌더러에 전달
|
|
46
|
+
*/
|
|
47
|
+
const CHARACTER_PRESETS = {
|
|
48
|
+
default: {
|
|
49
|
+
name: '기본 Claw (빨강)',
|
|
50
|
+
colorMap: { primary: '#ff4f40', secondary: '#ff775f', dark: '#8B4513', eye: '#ffffff', pupil: '#111111', claw: '#ff4f40' },
|
|
51
|
+
},
|
|
52
|
+
blue: {
|
|
53
|
+
name: '파란 Claw',
|
|
54
|
+
colorMap: { primary: '#4488ff', secondary: '#6699ff', dark: '#223388', eye: '#ffffff', pupil: '#111111', claw: '#4488ff' },
|
|
55
|
+
},
|
|
56
|
+
green: {
|
|
57
|
+
name: '초록 Claw',
|
|
58
|
+
colorMap: { primary: '#44cc44', secondary: '#66dd66', dark: '#226622', eye: '#ffffff', pupil: '#111111', claw: '#44cc44' },
|
|
59
|
+
},
|
|
60
|
+
purple: {
|
|
61
|
+
name: '보라 Claw',
|
|
62
|
+
colorMap: { primary: '#8844cc', secondary: '#aa66dd', dark: '#442266', eye: '#ffffff', pupil: '#111111', claw: '#8844cc' },
|
|
63
|
+
},
|
|
64
|
+
gold: {
|
|
65
|
+
name: '골드 Claw',
|
|
66
|
+
colorMap: { primary: '#ffcc00', secondary: '#ffdd44', dark: '#886600', eye: '#ffffff', pupil: '#111111', claw: '#ffcc00' },
|
|
67
|
+
},
|
|
68
|
+
pink: {
|
|
69
|
+
name: '핑크 Claw',
|
|
70
|
+
colorMap: { primary: '#ff69b4', secondary: '#ff8cc4', dark: '#8B3060', eye: '#ffffff', pupil: '#111111', claw: '#ff69b4' },
|
|
71
|
+
},
|
|
72
|
+
cat: {
|
|
73
|
+
name: '고양이',
|
|
74
|
+
colorMap: { primary: '#ff9944', secondary: '#ffbb66', dark: '#663300', eye: '#88ff88', pupil: '#111111', claw: '#ff9944' },
|
|
75
|
+
},
|
|
76
|
+
robot: {
|
|
77
|
+
name: '로봇',
|
|
78
|
+
colorMap: { primary: '#888888', secondary: '#aaaaaa', dark: '#444444', eye: '#66aaff', pupil: '#0044aa', claw: '#66aaff' },
|
|
79
|
+
},
|
|
80
|
+
ghost: {
|
|
81
|
+
name: '유령',
|
|
82
|
+
colorMap: { primary: '#ccccff', secondary: '#eeeeff', dark: '#6666aa', eye: '#ff6666', pupil: '#cc0000', claw: '#ccccff' },
|
|
83
|
+
},
|
|
84
|
+
dragon: {
|
|
85
|
+
name: '드래곤',
|
|
86
|
+
colorMap: { primary: '#cc2222', secondary: '#ff4444', dark: '#661111', eye: '#ffaa00', pupil: '#111111', claw: '#ffaa00' },
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
function createClawIcon() {
|
|
91
|
+
const size = 16;
|
|
92
|
+
const buffer = Buffer.alloc(size * size * 4);
|
|
93
|
+
for (let y = 0; y < size; y++) {
|
|
94
|
+
for (let x = 0; x < size; x++) {
|
|
95
|
+
const code = CLAW_ICON[y][x];
|
|
96
|
+
const color = COLOR_MAP[code] || COLOR_MAP[0];
|
|
97
|
+
const offset = (y * size + x) * 4;
|
|
98
|
+
buffer[offset + 0] = color[0];
|
|
99
|
+
buffer[offset + 1] = color[1];
|
|
100
|
+
buffer[offset + 2] = color[2];
|
|
101
|
+
buffer[offset + 3] = color[3];
|
|
29
102
|
}
|
|
30
|
-
icon = nativeImage.createFromBuffer(buffer, { width: size, height: size });
|
|
31
103
|
}
|
|
104
|
+
return nativeImage.createFromBuffer(buffer, { width: size, height: size });
|
|
105
|
+
}
|
|
32
106
|
|
|
107
|
+
function setupTray(mainWindow, bridge) {
|
|
108
|
+
aiBridge = bridge;
|
|
109
|
+
const store = new Store('clawmate-config', {
|
|
110
|
+
mode: 'pet',
|
|
111
|
+
character: 'default',
|
|
112
|
+
telegramToken: '',
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const icon = createClawIcon();
|
|
33
116
|
tray = new Tray(icon);
|
|
34
117
|
tray.setToolTip('ClawMate - 데스크톱 펫');
|
|
35
118
|
|
|
@@ -38,38 +121,94 @@ function setupTray(mainWindow, bridge) {
|
|
|
38
121
|
const fileInteraction = store.get('fileInteraction') !== false;
|
|
39
122
|
const aiConnected = aiBridge ? aiBridge.isConnected() : false;
|
|
40
123
|
const autoStart = isAutoStartEnabled();
|
|
124
|
+
const currentChar = store.get('character') || 'default';
|
|
125
|
+
const hasTelegramToken = !!(store.get('telegramToken'));
|
|
126
|
+
|
|
127
|
+
// 캐릭터 서브메뉴
|
|
128
|
+
const characterSubmenu = Object.entries(CHARACTER_PRESETS).map(([key, preset]) => ({
|
|
129
|
+
label: preset.name,
|
|
130
|
+
type: 'radio',
|
|
131
|
+
checked: currentChar === key,
|
|
132
|
+
click: () => {
|
|
133
|
+
store.set('character', key);
|
|
134
|
+
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
135
|
+
if (key === 'default') {
|
|
136
|
+
mainWindow.webContents.send('ai-command', {
|
|
137
|
+
type: 'reset_character', payload: {},
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
mainWindow.webContents.send('ai-command', {
|
|
141
|
+
type: 'set_character', payload: {
|
|
142
|
+
colorMap: preset.colorMap,
|
|
143
|
+
speech: `${preset.name}(으)로 변신!`,
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
buildAndSet();
|
|
149
|
+
},
|
|
150
|
+
}));
|
|
41
151
|
|
|
42
152
|
return Menu.buildFromTemplate([
|
|
43
153
|
{
|
|
44
|
-
label: `ClawMate (${mode === 'pet' ? 'Clawby' : '
|
|
154
|
+
label: `ClawMate (${mode === 'pet' ? 'Clawby' : mode === 'incarnation' ? 'Claw' : 'Clawby + Claw'})`,
|
|
45
155
|
enabled: false,
|
|
46
156
|
},
|
|
47
157
|
{
|
|
48
|
-
label: aiConnected ? 'AI: 연결됨' : 'AI: 자율 모드
|
|
158
|
+
label: aiConnected ? 'AI: 연결됨' : 'AI: 자율 모드',
|
|
49
159
|
enabled: false,
|
|
50
160
|
},
|
|
51
161
|
{ type: 'separator' },
|
|
162
|
+
|
|
163
|
+
// === 모드 선택 ===
|
|
52
164
|
{
|
|
53
|
-
label: '
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
165
|
+
label: '모드',
|
|
166
|
+
submenu: [
|
|
167
|
+
{
|
|
168
|
+
label: 'Pet 모드 (Clawby)',
|
|
169
|
+
sublabel: '귀여운 펫을 키우기',
|
|
170
|
+
type: 'radio',
|
|
171
|
+
checked: mode === 'pet',
|
|
172
|
+
click: () => {
|
|
173
|
+
store.set('mode', 'pet');
|
|
174
|
+
if (mainWindow) mainWindow.webContents.send('mode-changed', 'pet');
|
|
175
|
+
buildAndSet();
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
label: 'Incarnation 모드 (Claw)',
|
|
180
|
+
sublabel: '봇이 육체를 얻음',
|
|
181
|
+
type: 'radio',
|
|
182
|
+
checked: mode === 'incarnation',
|
|
183
|
+
click: () => {
|
|
184
|
+
store.set('mode', 'incarnation');
|
|
185
|
+
if (mainWindow) mainWindow.webContents.send('mode-changed', 'incarnation');
|
|
186
|
+
buildAndSet();
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
label: '둘 다 (Pet + Incarnation)',
|
|
191
|
+
sublabel: '펫도 키우고, 봇 인격도 반영',
|
|
192
|
+
type: 'radio',
|
|
193
|
+
checked: mode === 'both',
|
|
194
|
+
click: () => {
|
|
195
|
+
store.set('mode', 'both');
|
|
196
|
+
if (mainWindow) mainWindow.webContents.send('mode-changed', 'both');
|
|
197
|
+
buildAndSet();
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
],
|
|
61
201
|
},
|
|
202
|
+
|
|
203
|
+
// === 캐릭터 선택 ===
|
|
62
204
|
{
|
|
63
|
-
label: '
|
|
64
|
-
|
|
65
|
-
checked: mode === 'incarnation',
|
|
66
|
-
click: () => {
|
|
67
|
-
store.set('mode', 'incarnation');
|
|
68
|
-
if (mainWindow) mainWindow.webContents.send('mode-changed', 'incarnation');
|
|
69
|
-
buildAndSet();
|
|
70
|
-
},
|
|
205
|
+
label: '캐릭터',
|
|
206
|
+
submenu: characterSubmenu,
|
|
71
207
|
},
|
|
208
|
+
|
|
72
209
|
{ type: 'separator' },
|
|
210
|
+
|
|
211
|
+
// === 설정 ===
|
|
73
212
|
{
|
|
74
213
|
label: '파일 상호작용',
|
|
75
214
|
type: 'checkbox',
|
|
@@ -88,7 +227,89 @@ function setupTray(mainWindow, bridge) {
|
|
|
88
227
|
buildAndSet();
|
|
89
228
|
},
|
|
90
229
|
},
|
|
230
|
+
|
|
231
|
+
{ type: 'separator' },
|
|
232
|
+
|
|
233
|
+
// === 텔레그램 봇 ===
|
|
234
|
+
{
|
|
235
|
+
label: '텔레그램 봇',
|
|
236
|
+
submenu: [
|
|
237
|
+
{
|
|
238
|
+
label: hasTelegramToken ? '봇 토큰: 설정됨' : '봇 토큰: 미설정',
|
|
239
|
+
enabled: false,
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
label: '봇 토큰 설정...',
|
|
243
|
+
click: async () => {
|
|
244
|
+
const result = await dialog.showMessageBox({
|
|
245
|
+
type: 'question',
|
|
246
|
+
buttons: ['클립보드에서 붙여넣기', '직접 입력', '취소'],
|
|
247
|
+
title: 'ClawMate 텔레그램 봇',
|
|
248
|
+
message: '텔레그램 봇 토큰을 설정합니다.',
|
|
249
|
+
detail: '@BotFather에서 받은 봇 토큰을 입력하세요.\n현재 클립보드 내용을 붙여넣으려면 "클립보드에서 붙여넣기"를 선택하세요.',
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
let token = null;
|
|
253
|
+
if (result.response === 0) {
|
|
254
|
+
// 클립보드에서 붙여넣기
|
|
255
|
+
token = clipboard.readText().trim();
|
|
256
|
+
} else if (result.response === 1) {
|
|
257
|
+
// prompt가 없으므로 클립보드 안내
|
|
258
|
+
const promptResult = await dialog.showMessageBox({
|
|
259
|
+
type: 'info',
|
|
260
|
+
buttons: ['확인'],
|
|
261
|
+
title: '텔레그램 봇 토큰',
|
|
262
|
+
message: '봇 토큰을 클립보드에 복사한 후 다시 "봇 토큰 설정"을 선택하세요.',
|
|
263
|
+
detail: '텔레그램에서 @BotFather → /newbot → 토큰 복사',
|
|
264
|
+
});
|
|
265
|
+
return;
|
|
266
|
+
} else {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (token && token.includes(':')) {
|
|
271
|
+
store.set('telegramToken', token);
|
|
272
|
+
process.env.CLAWMATE_TELEGRAM_TOKEN = token;
|
|
273
|
+
buildAndSet();
|
|
274
|
+
|
|
275
|
+
// 펫 알림
|
|
276
|
+
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
277
|
+
mainWindow.webContents.send('ai-command', {
|
|
278
|
+
type: 'speak',
|
|
279
|
+
payload: { text: '텔레그램 봇 토큰 설정 완료!' },
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
await dialog.showMessageBox({
|
|
284
|
+
type: 'error',
|
|
285
|
+
buttons: ['확인'],
|
|
286
|
+
title: '잘못된 토큰',
|
|
287
|
+
message: '유효한 텔레그램 봇 토큰이 아닙니다.',
|
|
288
|
+
detail: '올바른 형식: 123456789:ABCdefGHIjklMNOpqrsTUVwxyz',
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
label: '봇 토큰 제거',
|
|
295
|
+
enabled: hasTelegramToken,
|
|
296
|
+
click: () => {
|
|
297
|
+
store.set('telegramToken', '');
|
|
298
|
+
delete process.env.CLAWMATE_TELEGRAM_TOKEN;
|
|
299
|
+
buildAndSet();
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
},
|
|
304
|
+
|
|
91
305
|
{ type: 'separator' },
|
|
306
|
+
|
|
307
|
+
{
|
|
308
|
+
label: '업데이트 확인',
|
|
309
|
+
click: async () => {
|
|
310
|
+
await checkForUpdateManual(mainWindow);
|
|
311
|
+
},
|
|
312
|
+
},
|
|
92
313
|
{
|
|
93
314
|
label: '파일 이동 되돌리기',
|
|
94
315
|
click: async () => {
|
|
@@ -122,4 +343,51 @@ function setupTray(mainWindow, bridge) {
|
|
|
122
343
|
return tray;
|
|
123
344
|
}
|
|
124
345
|
|
|
346
|
+
/**
|
|
347
|
+
* 수동 업데이트 확인
|
|
348
|
+
*/
|
|
349
|
+
async function checkForUpdateManual(mainWindow) {
|
|
350
|
+
if (app.isPackaged) {
|
|
351
|
+
try {
|
|
352
|
+
const { autoUpdater } = require('electron-updater');
|
|
353
|
+
autoUpdater.checkForUpdatesAndNotify();
|
|
354
|
+
} catch (err) {
|
|
355
|
+
console.error('[업데이트] electron-updater 실패:', err.message);
|
|
356
|
+
}
|
|
357
|
+
} else {
|
|
358
|
+
try {
|
|
359
|
+
const latest = execSync('npm view clawmate version', {
|
|
360
|
+
encoding: 'utf-8',
|
|
361
|
+
timeout: 10000,
|
|
362
|
+
}).trim();
|
|
363
|
+
const current = require('../package.json').version;
|
|
364
|
+
|
|
365
|
+
if (latest !== current) {
|
|
366
|
+
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
367
|
+
mainWindow.webContents.send('ai-command', {
|
|
368
|
+
type: 'speak',
|
|
369
|
+
payload: { text: `새 버전 v${latest} 사용 가능! (현재: v${current})` },
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
shell.openExternal('https://www.npmjs.com/package/clawmate');
|
|
373
|
+
} else {
|
|
374
|
+
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
375
|
+
mainWindow.webContents.send('ai-command', {
|
|
376
|
+
type: 'speak',
|
|
377
|
+
payload: { text: `v${current} — 이미 최신 버전이야!` },
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
} catch (err) {
|
|
382
|
+
console.error('[업데이트] npm 버전 확인 실패:', err.message);
|
|
383
|
+
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
384
|
+
mainWindow.webContents.send('ai-command', {
|
|
385
|
+
type: 'speak',
|
|
386
|
+
payload: { text: '업데이트 확인 실패...' },
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
125
393
|
module.exports = { setupTray };
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawmate",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "ClawMate - AI가 조종하는 화면 위의 살아있는 데스크톱 펫",
|
|
5
5
|
"main": "main/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"clawmate": "./skills/launch-pet/index.js"
|
|
@@ -18,17 +18,17 @@
|
|
|
18
18
|
"build:linux": "electron-builder --linux"
|
|
19
19
|
},
|
|
20
20
|
"keywords": [
|
|
21
|
-
"openclaw",
|
|
22
21
|
"desktop-pet",
|
|
23
22
|
"electron",
|
|
24
23
|
"clawmate",
|
|
25
24
|
"ai-pet"
|
|
26
25
|
],
|
|
27
|
-
"author": "
|
|
26
|
+
"author": "ClawMate",
|
|
28
27
|
"license": "MIT",
|
|
29
28
|
"homepage": "https://github.com/boqum/clawmate",
|
|
30
29
|
"dependencies": {
|
|
31
30
|
"electron-updater": "^6.0.0",
|
|
31
|
+
"node-telegram-bot-api": "^0.66.0",
|
|
32
32
|
"ws": "^8.18.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
package/preload/preload.js
CHANGED
|
@@ -42,9 +42,9 @@ contextBridge.exposeInMainWorld('clawmate', {
|
|
|
42
42
|
ipcRenderer.on('config-changed', (_, config) => callback(config));
|
|
43
43
|
},
|
|
44
44
|
|
|
45
|
-
// ===
|
|
45
|
+
// === AI 통신 ===
|
|
46
46
|
|
|
47
|
-
// AI 명령 수신 (
|
|
47
|
+
// AI 명령 수신 (AI → 펫)
|
|
48
48
|
onAICommand: (callback) => {
|
|
49
49
|
ipcRenderer.on('ai-command', (_, command) => callback(command));
|
|
50
50
|
},
|
|
@@ -57,9 +57,25 @@ contextBridge.exposeInMainWorld('clawmate', {
|
|
|
57
57
|
ipcRenderer.on('ai-disconnected', () => callback());
|
|
58
58
|
},
|
|
59
59
|
|
|
60
|
-
// 사용자 이벤트를
|
|
60
|
+
// 사용자 이벤트를 AI에 전달 (펫 → AI)
|
|
61
61
|
reportToAI: (event, data) => ipcRenderer.send('report-to-ai', event, data),
|
|
62
62
|
|
|
63
63
|
// AI 연결 상태 확인
|
|
64
64
|
isAIConnected: () => ipcRenderer.invoke('is-ai-connected'),
|
|
65
|
+
|
|
66
|
+
// 메트릭 보고 (렌더러 → main → AI)
|
|
67
|
+
reportMetrics: (summary) => ipcRenderer.send('report-metrics', summary),
|
|
68
|
+
|
|
69
|
+
// 활성 윈도우 제목 조회 (브라우저 감시)
|
|
70
|
+
getActiveWindowTitle: () => ipcRenderer.invoke('get-active-window-title'),
|
|
71
|
+
|
|
72
|
+
// 커서 위치 조회 (화면 좌표)
|
|
73
|
+
getCursorPosition: () => ipcRenderer.invoke('get-cursor-position'),
|
|
74
|
+
|
|
75
|
+
// === 스마트 파일 조작 ===
|
|
76
|
+
parseFileCommand: (text) => ipcRenderer.invoke('parse-file-command', text),
|
|
77
|
+
listFilteredFiles: (sourceDir, filter) => ipcRenderer.invoke('list-filtered-files', sourceDir, filter),
|
|
78
|
+
smartFileOp: (command) => ipcRenderer.invoke('smart-file-op', command),
|
|
79
|
+
undoSmartMove: (moveId) => ipcRenderer.invoke('undo-smart-move', moveId),
|
|
80
|
+
undoAllSmartMoves: () => ipcRenderer.invoke('undo-all-smart-moves'),
|
|
65
81
|
});
|
package/renderer/first-run.html
CHANGED
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
|
|
49
49
|
<div class="mode-card" id="mode-incarnation" onclick="selectMode('incarnation')">
|
|
50
50
|
<div class="mode-icon">\u{1F980}</div>
|
|
51
|
-
<h2>
|
|
51
|
+
<h2>Claw</h2>
|
|
52
52
|
<p>육체를 얻은 존재</p>
|
|
53
53
|
<span class="mode-desc">침착하고 카리스마 있는 Claw!</span>
|
|
54
54
|
</div>
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
document.getElementById('mode-' + mode).classList.add('selected');
|
|
73
73
|
const btn = document.getElementById('start-btn');
|
|
74
74
|
btn.disabled = false;
|
|
75
|
-
btn.textContent = mode === 'pet' ? 'Clawby와 시작!' : '
|
|
75
|
+
btn.textContent = mode === 'pet' ? 'Clawby와 시작!' : 'Claw과 시작!';
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
async function startFirstRun() {
|
package/renderer/index.html
CHANGED
|
@@ -60,6 +60,8 @@
|
|
|
60
60
|
<script src="js/time-aware.js"></script>
|
|
61
61
|
<script src="js/mode-manager.js"></script>
|
|
62
62
|
<script src="js/memory.js"></script>
|
|
63
|
+
<script src="js/metrics.js"></script>
|
|
64
|
+
<script src="js/browser-watcher.js"></script>
|
|
63
65
|
<script src="js/ai-controller.js"></script>
|
|
64
66
|
<script src="js/app.js"></script>
|
|
65
67
|
</body>
|