clawmate 1.4.0 → 1.4.2

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.
Files changed (42) hide show
  1. package/index.js +441 -442
  2. package/main/ai-bridge.js +59 -59
  3. package/main/ai-connector.js +60 -60
  4. package/main/autostart.js +6 -6
  5. package/main/desktop-path.js +4 -4
  6. package/main/file-command-parser.js +46 -46
  7. package/main/file-ops.js +27 -27
  8. package/main/index.js +17 -17
  9. package/main/ipc-handlers.js +24 -24
  10. package/main/manifest.js +2 -2
  11. package/main/platform.js +16 -16
  12. package/main/smart-file-ops.js +64 -64
  13. package/main/store.js +1 -1
  14. package/main/telegram.js +137 -137
  15. package/main/tray.js +61 -61
  16. package/main/updater.js +13 -13
  17. package/openclaw.plugin.json +1 -1
  18. package/package.json +2 -2
  19. package/preload/preload.js +18 -18
  20. package/renderer/css/effects.css +6 -6
  21. package/renderer/css/pet.css +8 -8
  22. package/renderer/css/speech.css +5 -5
  23. package/renderer/first-run.html +14 -14
  24. package/renderer/index.html +4 -4
  25. package/renderer/js/ai-controller.js +91 -91
  26. package/renderer/js/app.js +24 -24
  27. package/renderer/js/browser-watcher.js +32 -32
  28. package/renderer/js/character.js +33 -33
  29. package/renderer/js/interactions.js +21 -21
  30. package/renderer/js/memory.js +60 -60
  31. package/renderer/js/metrics.js +141 -141
  32. package/renderer/js/mode-manager.js +13 -13
  33. package/renderer/js/pet-engine.js +236 -236
  34. package/renderer/js/speech.js +19 -19
  35. package/renderer/js/state-machine.js +23 -23
  36. package/renderer/js/time-aware.js +15 -15
  37. package/renderer/launcher.html +8 -8
  38. package/shared/constants.js +11 -11
  39. package/shared/messages.js +130 -130
  40. package/shared/personalities.js +44 -44
  41. package/skills/launch-pet/index.js +57 -47
  42. package/skills/launch-pet/skill.json +12 -23
package/main/autostart.js CHANGED
@@ -1,12 +1,12 @@
1
1
  /**
2
- * 시스템 시작 ClawMate 자동 실행 등록
2
+ * Register ClawMate auto-start on system boot
3
3
  *
4
- * Windows: 레지스트리 Run
4
+ * Windows: Registry Run key
5
5
  * macOS: LaunchAgent plist
6
- * Linux: ~/.config/autostart/ 디렉토리에 .desktop 파일 생성
6
+ * Linux: Create .desktop file in ~/.config/autostart/ directory
7
7
  *
8
- * AI 나중에 켜져도, ClawMate 이미 돌고 있어서 바로 연결됨.
9
- * ClawMate 먼저 혼자 자율 모드로 돌다가 AI 연결되면 AI 모드 전환.
8
+ * Even if AI starts later, ClawMate is already running for immediate connection.
9
+ * ClawMate runs in autonomous mode first -> switches to AI mode when AI connects.
10
10
  */
11
11
  const { app } = require('electron');
12
12
  const fs = require('fs');
@@ -53,7 +53,7 @@ function enableAutoStart() {
53
53
  }
54
54
  app.setLoginItemSettings({
55
55
  openAtLogin: true,
56
- openAsHidden: true, // 없이 백그라운드 시작
56
+ openAsHidden: true, // Start in background without window
57
57
  path: process.execPath,
58
58
  args: [path.resolve(__dirname, '..')],
59
59
  });
@@ -3,8 +3,8 @@ const path = require('path');
3
3
  const { execSync } = require('child_process');
4
4
 
5
5
  /**
6
- * OS별 바탕화면 경로 탐지
7
- * Windows: PowerShell로 정확한 경로 탐지 (OneDrive 등 대응)
6
+ * Detect desktop path per OS
7
+ * Windows: Accurate path detection via PowerShell (handles OneDrive, etc.)
8
8
  * macOS: ~/Desktop
9
9
  */
10
10
  function getDesktopPath() {
@@ -18,10 +18,10 @@ function getDesktopPath() {
18
18
  ).trim();
19
19
  if (result && result.length > 0) return result;
20
20
  } catch {
21
- // PowerShell 실패 폴백
21
+ // Fallback on PowerShell failure
22
22
  }
23
23
 
24
- // 환경 변수 기반 폴백
24
+ // Environment variable based fallback
25
25
  const userProfile = process.env.USERPROFILE || os.homedir();
26
26
  return path.join(userProfile, 'Desktop');
27
27
  }
@@ -1,20 +1,20 @@
1
1
  /**
2
- * 파일 조작 명령 파서
2
+ * File operation command parser
3
3
  *
4
- * 한국어/영어 자연어를 파싱하여 파일 조작 의도를 추출한다.
5
- * 텔레그램 메시지에서 파일 이동, 정리 등의 명령을 감지.
4
+ * Parses Korean/English natural language to extract file operation intent.
5
+ * Detects file move, organize, etc. commands from Telegram messages.
6
6
  *
7
- * 지원 패턴:
8
- * - "바탕화면의 .md 파일을 tata 폴더에 넣어줘"
9
- * - "스크린샷 폴더에 .png 정리해"
10
- * - "바탕화면 정리해"
7
+ * Supported patterns:
8
+ * - "바탕화면의 .md 파일을 tata 폴더에 넣어줘" (Korean: move .md files from desktop to tata folder)
9
+ * - "스크린샷 폴더에 .png 정리해" (Korean: organize .png into screenshots folder)
10
+ * - "바탕화면 정리해" (Korean: clean up desktop)
11
11
  * - "move .txt files to docs folder"
12
12
  */
13
13
 
14
14
  const os = require('os');
15
15
  const path = require('path');
16
16
 
17
- // 알려진 소스 경로 별칭 (한국어 + 영어)
17
+ // Known source path aliases (Korean + English)
18
18
  const SOURCE_ALIASES = {
19
19
  '바탕화면': () => _getDesktopPath(),
20
20
  '데스크탑': () => _getDesktopPath(),
@@ -27,7 +27,7 @@ const SOURCE_ALIASES = {
27
27
  'documents': () => path.join(os.homedir(), 'Documents'),
28
28
  };
29
29
 
30
- // 행동 명령 키워드 액션 매핑
30
+ // Action command keywords -> action mapping
31
31
  const ACTION_KEYWORDS = {
32
32
  '점프': 'jumping',
33
33
  '점프해': 'jumping',
@@ -58,24 +58,24 @@ const ACTION_KEYWORDS = {
58
58
  'rappel': 'rappelling',
59
59
  };
60
60
 
61
- // 파일 조작 감지 키워드
61
+ // File operation detection patterns
62
62
  const FILE_OP_PATTERNS = [
63
- // "~의 .ext 파일을 ~폴더에 넣어줘/옮겨줘/이동해"
63
+ // Korean: "~의 .ext 파일을 ~폴더에 넣어줘/옮겨줘/이동해" (move .ext files from ~ to ~ folder)
64
64
  /(?:(.+?)(?:의|에서|에 있는)\s+)?([.\w*]+)\s*파일(?:을|들을)?\s+(.+?)(?:폴더)?(?:에|으로)\s*(?:넣어|옮겨|이동|정리|보내)/,
65
- // "~폴더에 .ext 정리해"
65
+ // Korean: "~폴더에 .ext 정리해" (organize .ext into ~ folder)
66
66
  /(.+?)(?:폴더)?(?:에|으로)\s+([.\w*]+)\s*(?:파일\s*)?(?:정리|넣어|옮겨|이동)/,
67
- // "바탕화면 정리해"
67
+ // Korean: "바탕화면 정리해" (clean up desktop)
68
68
  /(.+?)\s*(?:정리|청소|깔끔하게)\s*(?:해|해줘|하자|좀)/,
69
- // 영어: "move .ext files to folder"
69
+ // English: "move .ext files to folder"
70
70
  /move\s+([.\w*]+)\s+files?\s+(?:to|into)\s+(\S+)/i,
71
- // 영어: "clean up desktop"
71
+ // English: "clean up desktop"
72
72
  /clean\s*(?:up)?\s+(\S+)/i,
73
- // 영어: "organize desktop"
73
+ // English: "organize desktop"
74
74
  /organize\s+(\S+)/i,
75
75
  ];
76
76
 
77
77
  /**
78
- * 데스크톱 경로 가져오기 (file-ops와 동일 로직)
78
+ * Get desktop path (same logic as file-ops)
79
79
  */
80
80
  function _getDesktopPath() {
81
81
  try {
@@ -87,9 +87,9 @@ function _getDesktopPath() {
87
87
  }
88
88
 
89
89
  /**
90
- * 소스 별칭을 실제 경로로 변환
91
- * @param {string} alias - 소스 별칭 (예: "바탕화면")
92
- * @returns {string|null} 실제 경로 또는 null
90
+ * Resolve source alias to actual path
91
+ * @param {string} alias - Source alias (e.g., "desktop", "바탕화면")
92
+ * @returns {string|null} Actual path or null
93
93
  */
94
94
  function resolveSource(alias) {
95
95
  if (!alias) return null;
@@ -103,8 +103,8 @@ function resolveSource(alias) {
103
103
  }
104
104
 
105
105
  /**
106
- * 자동 분류 확장자 폴더명 매핑
107
- * "바탕화면 정리해" 같은 범용 명령에서 사용
106
+ * Auto-categorize extension -> folder name mapping
107
+ * Used in generic commands like "clean up desktop"
108
108
  */
109
109
  const AUTO_CATEGORIES = {
110
110
  '.png': '이미지',
@@ -141,8 +141,8 @@ const AUTO_CATEGORIES = {
141
141
  };
142
142
 
143
143
  /**
144
- * 메시지에서 행동 명령을 감지
145
- * @param {string} text - 사용자 메시지
144
+ * Detect action commands from message
145
+ * @param {string} text - User message
146
146
  * @returns {{ type: 'action', action: string }|null}
147
147
  */
148
148
  function parseActionCommand(text) {
@@ -158,15 +158,15 @@ function parseActionCommand(text) {
158
158
  }
159
159
 
160
160
  /**
161
- * 메시지에서 파일 조작 명령을 감지
162
- * @param {string} text - 사용자 메시지
161
+ * Detect file operation commands from message
162
+ * @param {string} text - User message
163
163
  * @returns {{ type: 'smart_file_op', source: string, filter: string, target: string, autoCategory: boolean }|null}
164
164
  */
165
165
  function parseFileCommand(text) {
166
166
  if (!text) return null;
167
167
  const trimmed = text.trim();
168
168
 
169
- // 패턴 1: "바탕화면의 .md 파일을 tata 폴더에 넣어줘"
169
+ // Pattern 1: "바탕화면의 .md 파일을 tata 폴더에 넣어줘" (Korean: move files)
170
170
  const pattern1 = /(?:(.+?)(?:의|에서|에 있는)\s+)?([.\w*]+)\s*파일(?:을|들을)?\s+(.+?)(?:\s*폴더)?(?:에|으로)\s*(?:넣어|옮겨|이동|정리|보내)/;
171
171
  let match = trimmed.match(pattern1);
172
172
  if (match) {
@@ -184,7 +184,7 @@ function parseFileCommand(text) {
184
184
  };
185
185
  }
186
186
 
187
- // 패턴 2: "스크린샷 폴더에 .png 정리해"
187
+ // Pattern 2: "스크린샷 폴더에 .png 정리해" (Korean: organize into folder)
188
188
  const pattern2 = /(.+?)(?:\s*폴더)?(?:에|으로)\s+([.\w*]+)\s*(?:파일\s*)?(?:정리|넣어|옮겨|이동)/;
189
189
  match = trimmed.match(pattern2);
190
190
  if (match) {
@@ -200,7 +200,7 @@ function parseFileCommand(text) {
200
200
  };
201
201
  }
202
202
 
203
- // 패턴 3: "바탕화면 정리해" (자동 분류)
203
+ // Pattern 3: "바탕화면 정리해" (Korean: auto-categorize cleanup)
204
204
  const pattern3 = /(.+?)\s*(?:정리|청소|깔끔하게)\s*(?:해|해줘|하자|좀)?$/;
205
205
  match = trimmed.match(pattern3);
206
206
  if (match) {
@@ -217,7 +217,7 @@ function parseFileCommand(text) {
217
217
  }
218
218
  }
219
219
 
220
- // 패턴 4 (영어): "move .txt files to docs"
220
+ // Pattern 4 (English): "move .txt files to docs"
221
221
  const pattern4 = /move\s+([.\w*]+)\s+files?\s+(?:to|into)\s+(\S+)/i;
222
222
  match = trimmed.match(pattern4);
223
223
  if (match) {
@@ -233,7 +233,7 @@ function parseFileCommand(text) {
233
233
  };
234
234
  }
235
235
 
236
- // 패턴 5 (영어): "clean up desktop" / "organize desktop"
236
+ // Pattern 5 (English): "clean up desktop" / "organize desktop"
237
237
  const pattern5 = /(?:clean\s*(?:up)?|organize)\s+(\S+)/i;
238
238
  match = trimmed.match(pattern5);
239
239
  if (match) {
@@ -254,15 +254,15 @@ function parseFileCommand(text) {
254
254
  }
255
255
 
256
256
  /**
257
- * 캐릭터 변경 명령 감지
258
- * @param {string} text - 사용자 메시지
257
+ * Detect character change commands
258
+ * @param {string} text - User message
259
259
  * @returns {{ type: 'character_change', concept: string }|null}
260
260
  */
261
261
  function parseCharacterCommand(text) {
262
262
  if (!text) return null;
263
263
  const trimmed = text.trim();
264
264
 
265
- // 한국어 캐릭터 변경 패턴
265
+ // Korean character change patterns
266
266
  const krPatterns = [
267
267
  /(?:캐릭터|펫|모습|외형|외모)(?:를|을)?\s*(.+?)(?:로|으로)\s*(?:바꿔|변경|변신|만들어|바꿀래|바꾸고|바꿔줘|변경해|만들어줘)/,
268
268
  /(.+?)(?:로|으로)\s*(?:캐릭터|펫|모습|외형)\s*(?:바꿔|변경|변신|변경해|바꿔줘)/,
@@ -276,7 +276,7 @@ function parseCharacterCommand(text) {
276
276
  }
277
277
  }
278
278
 
279
- // 영어 캐릭터 변경 패턴
279
+ // English character change patterns
280
280
  const enPatterns = [
281
281
  /(?:change|switch|transform)\s+(?:character|pet|look)\s+(?:to|into)\s+(.+)/i,
282
282
  /(?:make|create|generate)\s+(?:a\s+)?(.+?)\s+(?:character|pet)/i,
@@ -293,25 +293,25 @@ function parseCharacterCommand(text) {
293
293
  }
294
294
 
295
295
  /**
296
- * 메시지 종합 파싱: 캐릭터 변경 > 파일 조작 > 행동 명령 > 일반 대화 순으로 판별
297
- * @param {string} text - 사용자 메시지
296
+ * Comprehensive message parsing: character change > file operation > action command > general chat
297
+ * @param {string} text - User message
298
298
  * @returns {{ type: string, ... }}
299
299
  */
300
300
  /**
301
- * 모드/설정 변경 명령 감지
302
- * @param {string} text - 사용자 메시지
301
+ * Detect mode/setting change commands
302
+ * @param {string} text - User message
303
303
  * @returns {{ type: 'mode_change', mode: string }|{ type: 'setting', key, value }|null}
304
304
  */
305
305
  function parseSettingCommand(text) {
306
306
  if (!text) return null;
307
307
  const t = text.trim().toLowerCase();
308
308
 
309
- // 모드 변경
309
+ // Mode change
310
310
  if (/(?:펫|pet)\s*모드/.test(t)) return { type: 'mode_change', mode: 'pet' };
311
311
  if (/(?:인카|인격|incarnation|claw)\s*모드/.test(t)) return { type: 'mode_change', mode: 'incarnation' };
312
312
  if (/둘\s*다\s*모드|both\s*mode/i.test(t)) return { type: 'mode_change', mode: 'both' };
313
313
 
314
- // 캐릭터 프리셋 선택 (트레이 프리셋과 동일)
314
+ // Character preset selection (same as tray presets)
315
315
  const presetMap = {
316
316
  '파란|파랑|blue': 'blue', '초록|green': 'green', '보라|purple': 'purple',
317
317
  '골드|금색|gold': 'gold', '핑크|pink': 'pink',
@@ -329,23 +329,23 @@ function parseSettingCommand(text) {
329
329
  }
330
330
 
331
331
  function parseMessage(text) {
332
- // 0순위: 설정/모드 변경 명령
332
+ // Priority 0: setting/mode change commands
333
333
  const settingCmd = parseSettingCommand(text);
334
334
  if (settingCmd) return settingCmd;
335
335
 
336
- // 1순위: 캐릭터 변경 명령 (AI 생성)
336
+ // Priority 1: character change commands (AI generation)
337
337
  const charCmd = parseCharacterCommand(text);
338
338
  if (charCmd) return charCmd;
339
339
 
340
- // 2순위: 파일 조작 명령
340
+ // Priority 2: file operation commands
341
341
  const fileCmd = parseFileCommand(text);
342
342
  if (fileCmd) return fileCmd;
343
343
 
344
- // 3순위: 행동 명령
344
+ // Priority 3: action commands
345
345
  const actionCmd = parseActionCommand(text);
346
346
  if (actionCmd) return actionCmd;
347
347
 
348
- // 4순위: 일반 대화 (speak)
348
+ // Priority 4: general chat (speak)
349
349
  return { type: 'speak', text };
350
350
  }
351
351
 
package/main/file-ops.js CHANGED
@@ -4,14 +4,14 @@ const { getDesktopPath } = require('./desktop-path');
4
4
  const manifest = require('./manifest');
5
5
 
6
6
  /**
7
- * 바탕화면 파일 이동 시스템 (안전장치 포함)
7
+ * Desktop file move system (with safety measures)
8
8
  *
9
- * 안전 규칙:
10
- * - 세션당 최대 3 파일만 이동
11
- * - 이동 최소 5분 쿨다운
12
- * - 위험한 확장자 제외
13
- * - 100MB 이상 파일 제외
14
- * - 바탕화면 폴더 내에서만 위치 변경
9
+ * Safety rules:
10
+ * - Max 3 files moved per session
11
+ * - Min 5 minute cooldown between moves
12
+ * - Dangerous extensions excluded
13
+ * - Files over 100MB excluded
14
+ * - Position changes only within desktop folder
15
15
  */
16
16
 
17
17
  const MAX_FILES_PER_SESSION = 3;
@@ -26,7 +26,7 @@ let sessionMoveCount = 0;
26
26
  let lastMoveTime = 0;
27
27
 
28
28
  /**
29
- * 바탕화면 파일 목록 가져오기 (안전한 파일만)
29
+ * Get desktop file list (safe files only)
30
30
  */
31
31
  async function getDesktopFiles() {
32
32
  const desktop = getDesktopPath();
@@ -60,49 +60,49 @@ async function getDesktopFiles() {
60
60
  }
61
61
 
62
62
  /**
63
- * 바탕화면 내에서 파일 이름 변경(위치 변경 시뮬레이션)
64
- * 실제로는 바탕화면 폴더 안에서만 이동 가능
65
- * newPosition 렌더러에서 전달한 좌표 (로그용)
63
+ * Rename file on desktop (simulates position change)
64
+ * Actually only moves within desktop folder
65
+ * newPosition is coordinates from renderer (for logging)
66
66
  */
67
67
  async function moveFile(fileName, newPosition) {
68
- // 안전장치 체크
68
+ // Safety check
69
69
  if (sessionMoveCount >= MAX_FILES_PER_SESSION) {
70
- return { success: false, error: '세션당 이동 한도(3) 초과' };
70
+ return { success: false, error: 'Session move limit (3) exceeded' };
71
71
  }
72
72
 
73
73
  const now = Date.now();
74
74
  if (now - lastMoveTime < COOLDOWN_MS && lastMoveTime > 0) {
75
75
  const remaining = Math.ceil((COOLDOWN_MS - (now - lastMoveTime)) / 1000);
76
- return { success: false, error: `쿨다운 (${remaining} 남음)` };
76
+ return { success: false, error: `Cooldown active (${remaining}s remaining)` };
77
77
  }
78
78
 
79
79
  const desktop = getDesktopPath();
80
80
  const filePath = path.join(desktop, fileName);
81
81
 
82
- // 파일 존재 확인
82
+ // Check file exists
83
83
  try {
84
84
  await fs.promises.access(filePath);
85
85
  } catch {
86
- return { success: false, error: '파일을 찾을 수 없음' };
86
+ return { success: false, error: 'File not found' };
87
87
  }
88
88
 
89
- // 확장자 체크
89
+ // Extension check
90
90
  const ext = path.extname(fileName).toLowerCase();
91
91
  if (EXCLUDED_EXTS.has(ext)) {
92
- return { success: false, error: '보호된 파일 유형' };
92
+ return { success: false, error: 'Protected file type' };
93
93
  }
94
94
 
95
- // 크기 체크
95
+ // Size check
96
96
  try {
97
97
  const stat = await fs.promises.stat(filePath);
98
98
  if (stat.size > MAX_FILE_SIZE) {
99
- return { success: false, error: '파일 크기 초과 (100MB)' };
99
+ return { success: false, error: 'File size exceeded (100MB)' };
100
100
  }
101
101
  } catch {
102
- return { success: false, error: '파일 정보 읽기 실패' };
102
+ return { success: false, error: 'Failed to read file info' };
103
103
  }
104
104
 
105
- // 이동 기록 (바탕화면 내부 이동이므로 실제 파일시스템 위치는 동일)
105
+ // Record move (filesystem location stays the same since it's within desktop)
106
106
  const entry = manifest.addEntry({
107
107
  fileName,
108
108
  originalPath: filePath,
@@ -117,19 +117,19 @@ async function moveFile(fileName, newPosition) {
117
117
  }
118
118
 
119
119
  /**
120
- * 단일 파일 이동 되돌리기
120
+ * Undo single file move
121
121
  */
122
122
  async function undoFileMove(moveId) {
123
123
  const entry = manifest.markRestored(moveId);
124
124
  if (!entry) {
125
- return { success: false, error: '이동 기록을 찾을 수 없음' };
125
+ return { success: false, error: 'Move record not found' };
126
126
  }
127
- // 실제 파일 위치는 바탕화면 내에서만 변경되므로 기록만 업데이트
127
+ // Only update record since actual file location only changes within desktop
128
128
  return { success: true };
129
129
  }
130
130
 
131
131
  /**
132
- * 모든 파일 이동 되돌리기
132
+ * Undo all file moves
133
133
  */
134
134
  async function undoAllMoves() {
135
135
  const count = manifest.markAllRestored();
@@ -137,7 +137,7 @@ async function undoAllMoves() {
137
137
  }
138
138
 
139
139
  /**
140
- * 파일 이동 이력 가져오기
140
+ * Get file move history
141
141
  */
142
142
  async function getFileManifest() {
143
143
  return manifest.getAll();
package/main/index.js CHANGED
@@ -31,7 +31,7 @@ function createMainWindow() {
31
31
  },
32
32
  });
33
33
 
34
- // 클릭 통과 영역만 클릭 가능하도록 렌더러에서 제어
34
+ // Click-throughrenderer controls which pet areas are clickable
35
35
  mainWindow.setIgnoreMouseEvents(true, { forward: true });
36
36
 
37
37
  mainWindow.loadFile(path.join(__dirname, '..', 'renderer', 'index.html'));
@@ -69,26 +69,26 @@ function createLauncherWindow() {
69
69
  }
70
70
 
71
71
  /**
72
- * AI Bridge 시작 AI 에이전트가 접속하면 펫을 조종
72
+ * Start AI Bridge -- AI agent connects to control the pet
73
73
  */
74
74
  function startAIBridge(win) {
75
75
  aiBridge = new AIBridge();
76
76
  aiBridge.start();
77
77
 
78
- // AI ClawMate 명령을 렌더러에 전달
78
+ // Forward AI -> ClawMate commands to renderer
79
79
  const commandTypes = [
80
80
  'action', 'move', 'emote', 'speak', 'think',
81
81
  'carry_file', 'drop_file', 'set_mode', 'evolve',
82
82
  'accessorize', 'ai_decision',
83
- // 공간 이동 명령 (펫이 집처럼 돌아다니기)
83
+ // Spatial movement commands (pet roams like it's home)
84
84
  'jump_to', 'rappel', 'release_thread', 'move_to_center', 'walk_on_window',
85
- // 커스텀 이동 패턴
85
+ // Custom movement patterns
86
86
  'register_movement', 'custom_move', 'stop_custom_move', 'list_movements',
87
- // 스마트 파일 조작 (텔레그램/AI에서 트리거한 파일 이동 애니메이션)
87
+ // Smart file operations (file move animations triggered by Telegram/AI)
88
88
  'smart_file_op',
89
- // 캐릭터 커스터마이징 (텔레그램에서 AI 생성)
89
+ // Character customization (AI-generated via Telegram)
90
90
  'set_character', 'reset_character',
91
- // 인격체 전환 (Incarnation 모드)
91
+ // Persona switching (Incarnation mode)
92
92
  'set_persona',
93
93
  ];
94
94
 
@@ -100,19 +100,19 @@ function startAIBridge(win) {
100
100
  });
101
101
  });
102
102
 
103
- // AI 윈도우 위치 정보 요청 처리
103
+ // Handle AI window position info request
104
104
  aiBridge.on('query_windows', async () => {
105
105
  try {
106
106
  const { getWindowPositions } = require('./platform');
107
107
  const windows = await getWindowPositions();
108
108
  aiBridge.send('window_positions', { windows });
109
109
  } catch (err) {
110
- console.error('[AI Bridge] 윈도우 목록 실패:', err.message);
110
+ console.error('[AI Bridge] Window list failed:', err.message);
111
111
  aiBridge.send('window_positions', { windows: [] });
112
112
  }
113
113
  });
114
114
 
115
- // AI 화면 캡처 요청 처리 (main process에서 직접 캡처)
115
+ // Handle AI screen capture request (captured directly in main process)
116
116
  aiBridge.on('query_screen', async () => {
117
117
  try {
118
118
  const primaryDisplay = screen.getPrimaryDisplay();
@@ -133,11 +133,11 @@ function startAIBridge(win) {
133
133
  );
134
134
  }
135
135
  } catch (err) {
136
- console.error('[AI Bridge] 화면 캡처 실패:', err.message);
136
+ console.error('[AI Bridge] Screen capture failed:', err.message);
137
137
  }
138
138
  });
139
139
 
140
- // 연결/해제 이벤트
140
+ // Connection/disconnection events
141
141
  aiBridge.on('connected', () => {
142
142
  if (win && !win.isDestroyed()) {
143
143
  win.webContents.send('ai-connected');
@@ -159,22 +159,22 @@ app.whenReady().then(() => {
159
159
  const bridge = startAIBridge(win);
160
160
  setupTray(win, bridge);
161
161
 
162
- // 텔레그램 초기화 (토큰 없으면 조용히 무시)
162
+ // Initialize Telegram bot (silently ignored if no token)
163
163
  telegramBot = new TelegramBot(bridge);
164
164
 
165
- // 최초 설치 자동 시작 등록
165
+ // Register auto-start on first install
166
166
  const { enableAutoStart, isAutoStartEnabled } = require('./autostart');
167
167
  if (!isAutoStartEnabled()) {
168
168
  enableAutoStart();
169
169
  }
170
170
 
171
- // 자동 업데이트 확인 (빌드된 앱에서만 동작)
171
+ // Auto-update check (only works in packaged app)
172
172
  const { checkForUpdates } = require('./updater');
173
173
  checkForUpdates();
174
174
  });
175
175
 
176
176
  app.on('window-all-closed', () => {
177
- // 트레이에서 계속 실행
177
+ // Keep running in tray
178
178
  });
179
179
 
180
180
  app.on('before-quit', () => {