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
@@ -1,8 +1,8 @@
1
1
  /**
2
- * 말풍선 시스템
3
- * - 타자기 효과 (30ms/글자)
4
- * - 5초 유지 페이드 아웃
5
- * - 모드별 스타일: Pet(둥글고 빨간 테두리) / Incarnation(각지고 발광)
2
+ * Speech bubble system
3
+ * - Typewriter effect (30ms/char)
4
+ * - Hold for 5s -> fade out
5
+ * - Mode-based styling: Pet (rounded, red border) / Incarnation (angular, teal glow)
6
6
  */
7
7
  const Speech = (() => {
8
8
  const CHAR_DELAY = 30;
@@ -19,18 +19,18 @@ const Speech = (() => {
19
19
  }
20
20
 
21
21
  /**
22
- * 캐릭터 위치(edge)에 따라 말풍선 좌표를 계산
23
- * - bottom(바닥): 캐릭터 위에
24
- * - left(왼쪽 ): 캐릭터 오른쪽(머리 )
25
- * - right(오른쪽 ): 캐릭터 왼쪽(머리 )
26
- * - top(천장): 캐릭터 아래에
27
- * - 점프/낙하/레펠 중: 캐릭터 위에 (기본)
22
+ * Calculate speech bubble coordinates based on character position (edge)
23
+ * - bottom (floor): above character
24
+ * - left (left wall): to character's right (head side)
25
+ * - right (right wall): to character's left (head side)
26
+ * - top (ceiling): below character
27
+ * - jumping/falling/rappelling: above character (default)
28
28
  */
29
29
  function getBubblePosition(pos) {
30
30
  const edge = pos.edge;
31
31
  const mode = pos.movementMode;
32
32
 
33
- // 공중일 때는 항상 위에 표시
33
+ // Always show above when in mid-air
34
34
  if (mode === 'jumping' || mode === 'falling' || mode === 'rappelling') {
35
35
  return { left: pos.x - 30, top: pos.y - 60 };
36
36
  }
@@ -49,10 +49,10 @@ const Speech = (() => {
49
49
  }
50
50
 
51
51
  /**
52
- * 말풍선이 화면 밖으로 나가지 않도록 좌표를 제한
52
+ * Clamp coordinates to prevent speech bubble from going off-screen
53
53
  */
54
54
  function clampBubblePosition(left, top) {
55
- const maxW = window.innerWidth - 200; // 말풍선 대략 너비 고려
55
+ const maxW = window.innerWidth - 200; // Account for approximate bubble width
56
56
  const maxH = window.innerHeight - 50;
57
57
  return {
58
58
  left: Math.max(5, Math.min(left, maxW)),
@@ -61,7 +61,7 @@ const Speech = (() => {
61
61
  }
62
62
 
63
63
  function show(text) {
64
- hide(); // 기존 말풍선 제거
64
+ hide(); // Remove existing bubble
65
65
 
66
66
  const container = document.getElementById('speech-container');
67
67
  const pos = PetEngine.getPosition();
@@ -69,7 +69,7 @@ const Speech = (() => {
69
69
  const bubble = document.createElement('div');
70
70
  bubble.className = `speech-bubble speech-${mode}`;
71
71
 
72
- // edge별 말풍선 위치 계산 + 화면 방지 clamp
72
+ // Calculate bubble position per edge + off-screen prevention clamp
73
73
  const rawPos = getBubblePosition(pos);
74
74
  const clamped = clampBubblePosition(rawPos.left, rawPos.top);
75
75
  bubble.style.left = clamped.left + 'px';
@@ -82,7 +82,7 @@ const Speech = (() => {
82
82
  container.appendChild(bubble);
83
83
  currentBubble = bubble;
84
84
 
85
- // 타자기 효과
85
+ // Typewriter effect
86
86
  let charIndex = 0;
87
87
  typeTimer = setInterval(() => {
88
88
  if (charIndex < text.length) {
@@ -94,7 +94,7 @@ const Speech = (() => {
94
94
  }
95
95
  }, CHAR_DELAY);
96
96
 
97
- // 자동 숨김
97
+ // Auto-hide
98
98
  const totalTypeTime = text.length * CHAR_DELAY;
99
99
  hideTimer = setTimeout(() => {
100
100
  if (currentBubble) {
@@ -116,14 +116,14 @@ const Speech = (() => {
116
116
  function updatePosition() {
117
117
  if (!currentBubble) return;
118
118
  const pos = PetEngine.getPosition();
119
- // edge별 말풍선 위치 재계산 + 화면 밖 방지
119
+ // Recalculate bubble position per edge + off-screen prevention
120
120
  const rawPos = getBubblePosition(pos);
121
121
  const clamped = clampBubblePosition(rawPos.left, rawPos.top);
122
122
  currentBubble.style.left = clamped.left + 'px';
123
123
  currentBubble.style.top = clamped.top + 'px';
124
124
  }
125
125
 
126
- // --- 메시지 선택 헬퍼 ---
126
+ // --- Message selection helpers ---
127
127
  function randomFrom(arr) {
128
128
  return arr[Math.floor(Math.random() * arr.length)];
129
129
  }
@@ -1,11 +1,11 @@
1
1
  /**
2
- * 행동 유한 상태 머신 (FSM)
2
+ * Pet behavior finite state machine (FSM)
3
3
  *
4
- * 상태 전이도:
5
- * IDLE WALKING CLIMBING_UP CEILING_WALK
6
- * IDLE PLAYING CLIMBING_DOWN (천장/벽)
7
- * IDLE SLEEPING (23:00~06:00)
8
- * 인터럽트: 클릭→INTERACTING, 커서→SCARED/EXCITED
4
+ * State transition diagram:
5
+ * IDLE -> WALKING -> CLIMBING_UP -> CEILING_WALK
6
+ * IDLE <- PLAYING <- CLIMBING_DOWN <- (ceiling/wall)
7
+ * IDLE -> SLEEPING (23:00~06:00)
8
+ * Interrupts: click->INTERACTING, cursor->SCARED/EXCITED
9
9
  */
10
10
  const StateMachine = (() => {
11
11
  const STATES = {
@@ -20,13 +20,13 @@ const StateMachine = (() => {
20
20
  INTERACTING: 'interacting',
21
21
  SCARED: 'scared',
22
22
  EXCITED: 'excited',
23
- JUMPING: 'jumping', // 포물선 점프 (물리 엔진이 제어)
24
- RAPPELLING: 'rappelling', // 실(thread)을 타고 하강 중
25
- FALLING: 'falling', // 중력에 의한 자유 낙하 중
26
- CUSTOM: 'custom', // 커스텀 이동 패턴 실행 (Movement Registry)
23
+ JUMPING: 'jumping', // Parabolic jump in progress (controlled by physics engine)
24
+ RAPPELLING: 'rappelling', // Descending on thread
25
+ FALLING: 'falling', // Free fall under gravity
26
+ CUSTOM: 'custom', // Custom movement pattern in progress (Movement Registry)
27
27
  };
28
28
 
29
- // 상태의 최소/최대 지속 시간(ms)
29
+ // Min/max duration per state (ms)
30
30
  const DURATIONS = {
31
31
  [STATES.IDLE]: { min: 2000, max: 5000 },
32
32
  [STATES.WALKING]: { min: 3000, max: 8000 },
@@ -39,10 +39,10 @@ const StateMachine = (() => {
39
39
  [STATES.INTERACTING]: { min: 1500, max: 3000 },
40
40
  [STATES.SCARED]: { min: 1000, max: 2000 },
41
41
  [STATES.EXCITED]: { min: 1500, max: 3000 },
42
- [STATES.JUMPING]: { min: 500, max: 2000 }, // 점프 비행 시간
43
- [STATES.RAPPELLING]: { min: 2000, max: 8000 }, // 레펠 하강 시간
44
- [STATES.FALLING]: { min: 200, max: 1000 }, // 낙하 시간
45
- [STATES.CUSTOM]: { min: 500, max: 30000 }, // 커스텀 이동 (패턴에 따라 가변)
42
+ [STATES.JUMPING]: { min: 500, max: 2000 }, // Jump flight time
43
+ [STATES.RAPPELLING]: { min: 2000, max: 8000 }, // Rappel descent time
44
+ [STATES.FALLING]: { min: 200, max: 1000 }, // Fall time
45
+ [STATES.CUSTOM]: { min: 500, max: 30000 }, // Custom movement (varies by pattern)
46
46
  };
47
47
 
48
48
  let currentState = STATES.IDLE;
@@ -51,7 +51,7 @@ const StateMachine = (() => {
51
51
  let personality = null;
52
52
  let onStateChange = null;
53
53
 
54
- // 기본 전이 확률 (personality로 조정됨)
54
+ // Base transition probabilities (adjusted by personality)
55
55
  const BASE_TRANSITIONS = {
56
56
  [STATES.IDLE]: [
57
57
  { state: STATES.WALKING, weight: 0.5 },
@@ -63,7 +63,7 @@ const StateMachine = (() => {
63
63
  { state: STATES.CLIMBING_UP, weight: 0.2 },
64
64
  { state: STATES.WALKING, weight: 0.25 },
65
65
  { state: STATES.PLAYING, weight: 0.2 },
66
- { state: STATES.JUMPING, weight: 0.05 }, // 가끔 점프 (낮은 확률)
66
+ { state: STATES.JUMPING, weight: 0.05 }, // Occasional jump (low probability)
67
67
  ],
68
68
  [STATES.CLIMBING_UP]: [
69
69
  { state: STATES.CEILING_WALK, weight: 0.5 },
@@ -72,7 +72,7 @@ const StateMachine = (() => {
72
72
  [STATES.CEILING_WALK]: [
73
73
  { state: STATES.CLIMBING_DOWN, weight: 0.4 },
74
74
  { state: STATES.CEILING_WALK, weight: 0.4 },
75
- { state: STATES.RAPPELLING, weight: 0.2 }, // 천장에서 레펠로 하강
75
+ { state: STATES.RAPPELLING, weight: 0.2 }, // Rappel descent from ceiling
76
76
  ],
77
77
  [STATES.CLIMBING_DOWN]: [
78
78
  { state: STATES.WALKING, weight: 0.5 },
@@ -103,21 +103,21 @@ const StateMachine = (() => {
103
103
  { state: STATES.IDLE, weight: 0.5 },
104
104
  { state: STATES.WALKING, weight: 0.5 },
105
105
  ],
106
- // 점프 후: 착지하면 idle 또는 walking으로
106
+ // After jump: idle or walking on landing
107
107
  [STATES.JUMPING]: [
108
108
  { state: STATES.IDLE, weight: 0.5 },
109
109
  { state: STATES.WALKING, weight: 0.5 },
110
110
  ],
111
- // 레펠 후: 낙하하거나 착지
111
+ // After rappel: fall or land
112
112
  [STATES.RAPPELLING]: [
113
113
  { state: STATES.FALLING, weight: 0.3 },
114
114
  { state: STATES.IDLE, weight: 0.7 },
115
115
  ],
116
- // 낙하 후: 착지하면 idle
116
+ // After fall: idle on landing
117
117
  [STATES.FALLING]: [
118
118
  { state: STATES.IDLE, weight: 1.0 },
119
119
  ],
120
- // 커스텀 이동 완료 후: idle 또는 walking
120
+ // After custom movement: idle or walking
121
121
  [STATES.CUSTOM]: [
122
122
  { state: STATES.IDLE, weight: 0.6 },
123
123
  { state: STATES.WALKING, weight: 0.4 },
@@ -153,7 +153,7 @@ const StateMachine = (() => {
153
153
  if (forceState) {
154
154
  currentState = forceState;
155
155
  } else {
156
- // 수면 시간 확인 (23:00~06:00)
156
+ // Sleep time check (23:00~06:00)
157
157
  const hour = new Date().getHours();
158
158
  if (hour >= 23 || hour < 6) {
159
159
  if (currentState !== STATES.SLEEPING && Math.random() < 0.3) {
@@ -1,22 +1,22 @@
1
1
  /**
2
- * 시간대별 행동 변화 시스템
3
- * - 아침 인사, 점심 알림, 밤엔 잠자기
4
- * - 랜덤 혼잣말 (idle chatter)
5
- * - 메시지 랜덤 출현
2
+ * Time-based behavior change system
3
+ * - Morning greetings, lunch alerts, sleeping at night
4
+ * - Random idle chatter
5
+ * - Random tip messages
6
6
  */
7
7
  const TimeAware = (() => {
8
8
  let lastGreetingHour = -1;
9
9
  let lastChatterTime = 0;
10
10
  let lastTipTime = 0;
11
- const CHATTER_COOLDOWN = 60000; // 1 최소 간격
12
- const TIP_COOLDOWN = 5 * 60000; // 5 최소 간격
11
+ const CHATTER_COOLDOWN = 60000; // 1 minute minimum interval
12
+ const TIP_COOLDOWN = 5 * 60000; // 5 minute minimum interval
13
13
  let chatterChance = 0.15;
14
14
 
15
15
  function init() {
16
- // 시작 인사
16
+ // Greet on start
17
17
  showTimeGreeting();
18
- // 주기적 체크
19
- setInterval(tick, 30000); // 30초마다
18
+ // Periodic check
19
+ setInterval(tick, 30000); // Every 30 seconds
20
20
  }
21
21
 
22
22
  function setChatterChance(chance) {
@@ -27,13 +27,13 @@ const TimeAware = (() => {
27
27
  const hour = new Date().getHours();
28
28
  const now = Date.now();
29
29
 
30
- // 시간대 변경 감지 인사
30
+ // Detect time period change -> greet
31
31
  if (hour !== lastGreetingHour && [6, 12, 18, 23].includes(hour)) {
32
32
  showTimeGreeting();
33
33
  lastGreetingHour = hour;
34
34
  }
35
35
 
36
- // 수면 시간 체크 (23:00~06:00)
36
+ // Sleep time check (23:00~06:00)
37
37
  if (hour >= 23 || hour < 6) {
38
38
  const state = StateMachine.getState();
39
39
  if (state !== 'sleeping' && state !== 'interacting') {
@@ -44,7 +44,7 @@ const TimeAware = (() => {
44
44
  }
45
45
  }
46
46
 
47
- // idle 상태일 혼잣말
47
+ // Idle chatter when in idle state
48
48
  const state = StateMachine.getState();
49
49
  if (state === 'idle' && now - lastChatterTime > CHATTER_COOLDOWN) {
50
50
  if (Math.random() < chatterChance) {
@@ -57,7 +57,7 @@ const TimeAware = (() => {
57
57
  }
58
58
  }
59
59
 
60
- // 메시지 ( 드물게)
60
+ // Tip messages (less frequently)
61
61
  if (now - lastTipTime > TIP_COOLDOWN && Math.random() < 0.05) {
62
62
  const tip = Speech.getTipMessage();
63
63
  if (tip) {
@@ -75,7 +75,7 @@ const TimeAware = (() => {
75
75
 
76
76
  function showSleepEffect() {
77
77
  const pet = document.getElementById('pet-container');
78
- // Z-z-z 이펙트 추가
78
+ // Add Z-z-z sleep effect
79
79
  for (let i = 0; i < 3; i++) {
80
80
  const z = document.createElement('div');
81
81
  z.className = 'sleep-z';
@@ -83,7 +83,7 @@ const TimeAware = (() => {
83
83
  z.style.animationDelay = (i * 0.5) + 's';
84
84
  pet.appendChild(z);
85
85
  }
86
- // 5 후 제거
86
+ // Remove after 5 seconds
87
87
  setTimeout(() => {
88
88
  pet.querySelectorAll('.sleep-z').forEach(el => el.remove());
89
89
  }, 5000);
@@ -1,5 +1,5 @@
1
1
  <!DOCTYPE html>
2
- <html lang="ko">
2
+ <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <title>ClawMate Launcher</title>
@@ -12,27 +12,27 @@
12
12
  <span class="claw-icon">\u{1F99E}</span>
13
13
  </div>
14
14
  <h1>ClawMate</h1>
15
- <p class="subtitle">데스크톱 위의 살아있는 Claw</p>
15
+ <p class="subtitle">A living Claw on your desktop</p>
16
16
  </div>
17
17
 
18
18
  <div class="launcher-body">
19
19
  <div class="mode-card" id="mode-pet" onclick="selectMode('pet')">
20
20
  <div class="mode-icon">\u{1F990}</div>
21
21
  <h2>Clawby</h2>
22
- <p>귀여운 동반자</p>
23
- <span class="mode-desc">장난기 넘치는 꼬마 랍스터가 바탕화면을 돌아다녀요!</span>
22
+ <p>Cute companion</p>
23
+ <span class="mode-desc">A playful little lobster roaming your desktop!</span>
24
24
  </div>
25
25
 
26
26
  <div class="mode-card" id="mode-incarnation" onclick="selectMode('incarnation')">
27
27
  <div class="mode-icon">\u{1F980}</div>
28
28
  <h2>Claw</h2>
29
- <p>육체를 얻은 존재</p>
30
- <span class="mode-desc">침착하고 대담한 Claw 바탕화면에 강림합니다.</span>
29
+ <p>An entity given physical form</p>
30
+ <span class="mode-desc">The calm and bold Claw descends upon your desktop.</span>
31
31
  </div>
32
32
  </div>
33
33
 
34
34
  <button class="start-btn" id="start-btn" disabled onclick="startPet()">
35
- 모드를 선택해주세요
35
+ Select a mode
36
36
  </button>
37
37
  </div>
38
38
 
@@ -45,7 +45,7 @@
45
45
  document.getElementById('mode-' + mode).classList.add('selected');
46
46
  const btn = document.getElementById('start-btn');
47
47
  btn.disabled = false;
48
- btn.textContent = mode === 'pet' ? 'Clawby 시작!' : 'Claw 각성!';
48
+ btn.textContent = mode === 'pet' ? 'Start Clawby!' : 'Awaken Claw!';
49
49
  }
50
50
 
51
51
  async function startPet() {
@@ -1,24 +1,24 @@
1
- // 캐릭터 크기 (16x16 픽셀 4배 확대)
1
+ // Character size (16x16 pixels -> 4x scale)
2
2
  const PIXEL_SIZE = 4;
3
3
  const GRID_SIZE = 16;
4
4
  const CHAR_SIZE = PIXEL_SIZE * GRID_SIZE; // 64px
5
5
 
6
- // 이동 속도 (px/frame)
6
+ // Movement speed (px/frame)
7
7
  const BASE_SPEED = 1.5;
8
8
  const CLIMB_SPEED = 1.0;
9
9
 
10
- // 말풍선
10
+ // Speech bubble
11
11
  const SPEECH_CHAR_DELAY = 30; // ms per character
12
- const SPEECH_DISPLAY_TIME = 5000; // ms 표시 유지
13
- const SPEECH_FADE_TIME = 500; // ms 페이드 아웃
12
+ const SPEECH_DISPLAY_TIME = 5000; // ms display duration
13
+ const SPEECH_FADE_TIME = 500; // ms fade out
14
14
 
15
- // 파일 작업 안전장치
15
+ // File operation safeguards
16
16
  const MAX_FILES_PER_SESSION = 3;
17
- const FILE_MOVE_COOLDOWN = 5 * 60 * 1000; // 5
17
+ const FILE_MOVE_COOLDOWN = 5 * 60 * 1000; // 5 minutes
18
18
  const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB
19
19
  const EXCLUDED_EXTENSIONS = ['.exe', '.dll', '.sys', '.lnk', '.ini', '.bat', '.cmd', '.ps1'];
20
20
 
21
- // FSM 상태
21
+ // FSM states
22
22
  const STATES = {
23
23
  IDLE: 'idle',
24
24
  WALKING: 'walking',
@@ -33,7 +33,7 @@ const STATES = {
33
33
  EXCITED: 'excited',
34
34
  };
35
35
 
36
- // 화면 가장자리
36
+ // Screen edges
37
37
  const EDGES = {
38
38
  BOTTOM: 'bottom',
39
39
  LEFT: 'left',
@@ -41,13 +41,13 @@ const EDGES = {
41
41
  TOP: 'top',
42
42
  };
43
43
 
44
- // 방향
44
+ // Directions
45
45
  const DIRECTIONS = {
46
46
  LEFT: -1,
47
47
  RIGHT: 1,
48
48
  };
49
49
 
50
- // 색상 팔레트
50
+ // Color palette
51
51
  const COLORS = {
52
52
  pet: {
53
53
  primary: '#ff4f40',