companionbot 0.3.0 → 0.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/README.md CHANGED
@@ -1,46 +1,47 @@
1
1
  # CompanionBot
2
2
 
3
- Claude 기반의 개인화된 페르소나를 가진 AI Companion Bot
3
+ > Claude 기반의 개인화된 페르소나를 가진 AI Companion Bot
4
4
 
5
- ## 기능
5
+ [![npm version](https://badge.fury.io/js/companionbot.svg)](https://www.npmjs.com/package/companionbot)
6
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen)](https://nodejs.org)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
8
 
7
- - 자연스러운 대화 (Claude Sonnet/Opus/Haiku)
8
- - 첫 실행 시 온보딩으로 페르소나 설정
9
- - 이미지 분석 (사진 보내면 분석)
10
- - 링크 요약 (URL 보내면 내용 요약)
11
- - 날씨 조회 ("서울 날씨 어때?")
12
- - 리마인더 ("10분 뒤에 알려줘")
13
- - Google Calendar 연동
14
- - 일일 브리핑 (매일 아침 날씨/일정)
15
- - Heartbeat (주기적 체크 후 알림)
16
- - 일일 메모리 자동 저장
9
+ ## 주요 기능
17
10
 
18
- ## 설치
11
+ ### 💬 대화
12
+ - **자연스러운 대화** - Claude Sonnet/Opus/Haiku 모델 선택 가능
13
+ - **페르소나 커스터마이징** - 첫 실행 시 온보딩으로 봇의 성격, 말투, 이름 설정
14
+ - **이미지 분석** - 사진을 보내면 분석
15
+ - **링크 요약** - URL을 보내면 내용을 읽고 요약
19
16
 
20
- ### 간편 설치 (일반 사용자)
17
+ ### 🔍 정보 검색 (v0.3.0)
18
+ - **웹 검색** - "최신 React 뉴스 검색해줘" (Brave Search API)
19
+ - **웹 페이지 읽기** - URL 내용을 가져와서 분석/요약
21
20
 
22
- ```bash
23
- npm install -g companionbot
24
- companionbot
25
- ```
21
+ ### ⏰ 일정 관리
22
+ - **리마인더** - "10분 뒤에 알려줘"
23
+ - **Google Calendar 연동** - 일정 조회/추가/삭제
24
+ - **일일 브리핑** - 매일 아침 날씨와 일정 알림
25
+ - **Heartbeat** - 주기적으로 체크리스트 확인 후 알림
26
26
 
27
- 실행 자동으로 설정을 안내합니다.
27
+ ### 🕐 스케줄링 (v0.3.0)
28
+ - **Cron 작업** - "매일 아침 9시에 뉴스 알려줘", "평일 오후 6시에 퇴근 알림"
29
+ - **일회성 예약** - "내일 오전 9시에 알려줘"
30
+ - **반복 작업** - "30분마다 주식 가격 확인해줘"
28
31
 
29
- ### 개발자 설치 (소스코드 수정)
32
+ ### 🤖 고급 기능 (v0.3.0)
33
+ - **서브 에이전트** - 복잡한 작업을 백그라운드에서 처리
34
+ - **백그라운드 실행** - 긴 명령어를 백그라운드에서 실행하고 결과 확인
35
+ - **파일 시스템** - 워크스페이스 내 파일 읽기/쓰기/편집
36
+ - **일일 메모리** - 대화 내용 자동 저장
30
37
 
31
- ```bash
32
- git clone https://github.com/DinN0000/CompanionBot.git
33
- cd companionbot
34
- npm install
35
- npm run build
36
- npm start
37
- ```
38
+ ## 설치
38
39
 
39
40
  ### 사전 준비
40
41
 
41
- - **Node.js 18+**
42
- - **Telegram Bot Token** - @BotFather에서 발급
43
- - **Anthropic API Key** - console.anthropic.com
42
+ - **Node.js 18+** ([다운로드](https://nodejs.org))
43
+ - **Telegram Bot Token** - [@BotFather](https://t.me/BotFather)에서 봇 생성 후 발급
44
+ - **Anthropic API Key** - [console.anthropic.com](https://console.anthropic.com)에서 발급
44
45
 
45
46
  #### Linux 사용자 (keytar 의존성)
46
47
 
@@ -55,8 +56,27 @@ sudo dnf install libsecret-devel
55
56
  sudo pacman -S libsecret
56
57
  ```
57
58
 
59
+ ### 간편 설치
60
+
61
+ ```bash
62
+ npm install -g companionbot
63
+ companionbot
64
+ ```
65
+
66
+ ### 개발자 설치
67
+
68
+ ```bash
69
+ git clone https://github.com/DinN0000/CompanionBot.git
70
+ cd companionbot
71
+ npm install
72
+ npm run build
73
+ npm start
74
+ ```
75
+
58
76
  ## 첫 실행
59
77
 
78
+ 처음 실행하면 대화형 설정이 시작됩니다:
79
+
60
80
  ```
61
81
  🤖 CompanionBot 첫 실행입니다!
62
82
 
@@ -74,39 +94,98 @@ sudo pacman -S libsecret
74
94
  🚀 봇을 시작합니다!
75
95
  ```
76
96
 
97
+ 설정 완료 후 **Telegram에서 봇에게 `/start`를 보내면** 온보딩이 시작됩니다:
98
+ - 봇 이름 짓기
99
+ - 성격과 말투 설정
100
+ - 사용자 정보 입력
101
+
77
102
  ## 명령어
78
103
 
104
+ ### 기본 명령어
105
+
79
106
  | 명령어 | 설명 |
80
107
  |--------|------|
81
108
  | `/start` | 봇 시작 (첫 실행 시 온보딩) |
82
- | `/setup` | 기능 설정 메뉴 |
109
+ | `/compact` | 대화 정리 (토큰 절약) |
110
+ | `/memory` | 최근 일주일 기억 보기 |
111
+ | `/model [id]` | AI 모델 변경 (sonnet/opus/haiku) |
112
+ | `/reset` | 페르소나 초기화 (온보딩 다시) |
113
+
114
+ ### 기능 설정
115
+
116
+ | 명령어 | 설명 |
117
+ |--------|------|
118
+ | `/setup` | 전체 기능 설정 메뉴 |
119
+ | `/setup weather` | 날씨 API 설정 |
120
+ | `/setup calendar` | Google Calendar 설정 |
121
+ | `/setup briefing` | 일일 브리핑 설정 |
122
+ | `/setup heartbeat` | Heartbeat 설정 |
123
+
124
+ ### 빠른 명령어
125
+
126
+ | 명령어 | 설명 |
127
+ |--------|------|
83
128
  | `/briefing` | 일일 브리핑 토글 |
84
129
  | `/heartbeat` | Heartbeat 토글 |
85
- | `/reminders` | 알림 목록 |
86
- | `/calendar` | 오늘 일정 |
87
- | `/compact` | 대화 정리 |
88
- | `/memory` | 최근 기억 |
89
- | `/reset` | 페르소나 초기화 |
130
+ | `/reminders` | 알림 목록 보기 |
131
+ | `/calendar` | 오늘 일정 보기 |
90
132
 
91
133
  ### 자연어 명령
92
134
 
93
- 명령어 대신 자연어로 말해도 됩니다:
135
+ 명령어 대신 자연스럽게 말해도 됩니다:
94
136
 
95
- - "하이쿠로 바꿔줘" → 모델 변경
96
- - "10분 뒤에 알려줘" → 리마인더
97
- - "브리핑 꺼줘" 브리핑 비활성화
98
- - "아침 9시에 브리핑 해줘" 브리핑 시간 설정
99
- - "지금 브리핑 해줘" → 즉시 브리핑
100
- - "하트비트 켜줘" Heartbeat 활성화
101
- - "서울 날씨 어때?" → 날씨 조회
102
- - "이거 기억해둬" 메모리 저장
137
+ ```
138
+ 모델 변경 "하이쿠로 바꿔줘" / "opus로 변경해줘"
139
+ 리마인더 "10분 뒤에 알려줘" / "내일 9시에 회의 알림"
140
+ 브리핑 "브리핑 켜줘" / "지금 브리핑 해줘" / "아침 9시에 브리핑"
141
+ Heartbeat "하트비트 켜줘" / "10분마다 체크해줘"
142
+ 날씨 "서울 날씨 어때?" / "도쿄 날씨 알려줘"
143
+ 메모리 "이거 기억해둬"
144
+ 검색 "React 19 검색해줘" / "최신 뉴스 찾아줘"
145
+ Cron "매일 아침 9시에 뉴스 알려줘" / "평일 오후 6시에 퇴근 알림"
146
+ 서브에이전트 "이 코드 분석해줘" (복잡한 작업은 자동으로 서브에이전트 사용)
147
+ ```
148
+
149
+ ## 선택적 기능 설정
150
+
151
+ ### 🌤️ 날씨 (OpenWeatherMap)
152
+
153
+ 1. [openweathermap.org](https://openweathermap.org) 가입
154
+ 2. API Keys에서 무료 키 발급
155
+ 3. 봇에게 DM으로: `/weather_setup YOUR_API_KEY`
156
+
157
+ ### 📅 Google Calendar
158
+
159
+ 1. [Google Cloud Console](https://console.cloud.google.com) 접속
160
+ 2. 프로젝트 생성 → Calendar API 활성화
161
+ 3. OAuth 동의 화면 설정 (앱 이름, 범위 추가)
162
+ 4. 사용자 인증 정보 → OAuth 클라이언트 ID (데스크톱 앱)
163
+ 5. 봇에게 DM으로: `/calendar_setup CLIENT_ID CLIENT_SECRET`
164
+ 6. 인증 링크 클릭하여 Google 로그인
165
+
166
+ ### 🔍 웹 검색 (Brave Search)
167
+
168
+ 1. [Brave Search API](https://api.search.brave.com) 가입
169
+ 2. API 키 발급
170
+ 3. 터미널에서: `npm run setup brave YOUR_API_KEY`
103
171
 
104
172
  ## PM2로 상시 실행
105
173
 
106
174
  ```bash
175
+ # PM2 설치
107
176
  npm install -g pm2
177
+
178
+ # 봇 시작
108
179
  pm2 start npm --name companionbot -- start
180
+
181
+ # 부팅 시 자동 시작
109
182
  pm2 startup && pm2 save
183
+
184
+ # 로그 확인
185
+ pm2 logs companionbot
186
+
187
+ # 재시작
188
+ pm2 restart companionbot
110
189
  ```
111
190
 
112
191
  ## 워크스페이스
@@ -114,36 +193,111 @@ pm2 startup && pm2 save
114
193
  `~/.companionbot/` 구조:
115
194
 
116
195
  ```
117
- ├── AGENTS.md # 운영 지침
118
- ├── BOOTSTRAP.md # 온보딩 (완료 후 삭제)
196
+ ├── AGENTS.md # 운영 지침 (봇 행동 규칙)
197
+ ├── BOOTSTRAP.md # 온보딩 프롬프트 (완료 후 삭제됨)
119
198
  ├── HEARTBEAT.md # 주기적 체크 항목
120
- ├── IDENTITY.md # 봇 정체성
199
+ ├── IDENTITY.md # 봇 정체성 (이름, 이모지, 소개)
121
200
  ├── MEMORY.md # 장기 기억
122
- ├── SOUL.md # 봇 성격
123
- ├── TOOLS.md # 도구 설정
201
+ ├── SOUL.md # 봇 성격과 말투
202
+ ├── TOOLS.md # 도구 설정 노트
124
203
  ├── USER.md # 사용자 정보
125
204
  ├── canvas/ # 봇 작업 디렉토리
205
+ ├── cron.json # 크론 작업 저장
126
206
  └── memory/ # 일일 로그
127
207
  └── YYYY-MM-DD.md
128
208
  ```
129
209
 
210
+ ### 파일 커스터마이징
211
+
212
+ - **SOUL.md** - 봇의 성격, 말투, 관심사 수정
213
+ - **HEARTBEAT.md** - 주기적으로 체크할 항목 설정
214
+ - **AGENTS.md** - 봇의 행동 지침 수정
215
+
130
216
  ## 시크릿 저장
131
217
 
132
- OS 키체인에 안전하게 저장됩니다:
133
- - macOS: Keychain Access
134
- - Windows: Credential Manager
135
- - Linux: libsecret
218
+ API 키는 OS 키체인에 안전하게 저장됩니다:
219
+
220
+ | OS | 저장 위치 |
221
+ |----|-----------|
222
+ | macOS | Keychain Access |
223
+ | Windows | Credential Manager |
224
+ | Linux | libsecret (GNOME Keyring 등) |
225
+
226
+ 완전 초기화: `~/.companionbot/` 폴더 삭제 후 다시 실행
227
+
228
+ ## 트러블슈팅
229
+
230
+ ### 봇이 응답하지 않아요
231
+
232
+ 1. **API 키 확인**: Anthropic API 키가 유효한지 확인
233
+ 2. **토큰 확인**: Telegram Bot Token이 정확한지 확인
234
+ 3. **로그 확인**: `pm2 logs companionbot` 또는 터미널 출력 확인
136
235
 
137
- 재설정: `~/.companionbot/` 삭제 다시 실행
236
+ ### "rate limit" 오류가 나요
237
+
238
+ - Anthropic API 사용량 한도 초과
239
+ - 잠시 후 다시 시도하거나 API 플랜 업그레이드
240
+
241
+ ### Linux에서 설치 오류
242
+
243
+ ```bash
244
+ # keytar 의존성 설치 필요
245
+ sudo apt-get install libsecret-1-dev # Debian/Ubuntu
246
+ ```
247
+
248
+ ### Google Calendar 인증 실패
249
+
250
+ 1. OAuth 클라이언트 ID가 "데스크톱 앱" 유형인지 확인
251
+ 2. 리디렉션 URI에 `http://localhost:3847/oauth2callback` 추가
252
+ 3. `/setup calendar off` 후 `/calendar_setup`으로 다시 시도
253
+
254
+ ### 온보딩이 다시 안 나와요
255
+
256
+ ```bash
257
+ # 페르소나 리셋
258
+ # Telegram에서: /reset
259
+
260
+ # 또는 완전 초기화
261
+ rm -rf ~/.companionbot
262
+ companionbot
263
+ ```
138
264
 
139
265
  ## 개발
140
266
 
141
267
  ```bash
142
- npm run dev # 개발 모드
143
- npm run build # 빌드
144
- npm start # 실행
268
+ npm run dev # 개발 모드 (tsx)
269
+ npm run build # TypeScript 빌드
270
+ npm start # 빌드된 코드 실행
271
+ npm test # 테스트 실행
145
272
  ```
146
273
 
147
- ## License
274
+ ## 버전 히스토리
275
+
276
+ ### v0.3.0 (현재)
277
+ - 🔍 웹 검색 (Brave Search API)
278
+ - 🕐 Cron 스케줄링 (한국어 지원)
279
+ - 🤖 서브 에이전트 (백그라운드 작업)
280
+ - 🖥️ 백그라운드 명령어 실행
281
+ - 📝 파일 편집 도구 추가
282
+
283
+ ### v0.2.0
284
+ - 📅 Google Calendar 연동
285
+ - ☀️ 일일 브리핑
286
+ - 💓 Heartbeat 시스템
287
+ - 🌤️ 날씨 조회
288
+
289
+ ### v0.1.0
290
+ - 🚀 초기 릴리스
291
+ - 💬 Claude 기반 대화
292
+ - 🎭 페르소나 온보딩
293
+ - ⏰ 리마인더
294
+ - 🖼️ 이미지 분석
295
+ - 🔗 링크 요약
296
+
297
+ ## 라이선스
298
+
299
+ [MIT](LICENSE)
300
+
301
+ ---
148
302
 
149
- MIT
303
+ **문제가 있거나 기능 요청이 있으면** [Issues](https://github.com/DinN0000/CompanionBot/issues)에 등록해주세요!
@@ -10,6 +10,8 @@ import Anthropic from "@anthropic-ai/sdk";
10
10
  import { randomUUID } from "crypto";
11
11
  // Agent 저장소
12
12
  const agents = new Map();
13
+ // AbortController 저장소 (실행 중인 API 호출 취소용)
14
+ const abortControllers = new Map();
13
15
  // Bot 인스턴스 (결과 전송용)
14
16
  let botInstance = null;
15
17
  // Anthropic 클라이언트
@@ -50,6 +52,9 @@ export async function spawnAgent(task, chatId) {
50
52
  */
51
53
  async function runAgent(agent) {
52
54
  const client = getClient();
55
+ // AbortController 생성 및 저장
56
+ const controller = new AbortController();
57
+ abortControllers.set(agent.id, controller);
53
58
  const systemPrompt = `You are a sub-agent assistant. Your job is to complete a specific task and report the result concisely.
54
59
 
55
60
  TASK: ${agent.task}
@@ -73,7 +78,14 @@ Complete the task and provide your final answer.`;
73
78
  content: `Please complete this task: ${agent.task}`,
74
79
  },
75
80
  ],
81
+ }, {
82
+ signal: controller.signal,
76
83
  });
84
+ // 취소됐으면 결과 무시
85
+ if (agent.status === "cancelled") {
86
+ console.log(`[Agent ${agent.id}] Was cancelled, ignoring result`);
87
+ return;
88
+ }
77
89
  // 결과 추출
78
90
  const textBlock = response.content.find((block) => block.type === "text");
79
91
  const result = textBlock?.text ?? "No response generated.";
@@ -86,6 +98,11 @@ Complete the task and provide your final answer.`;
86
98
  await sendAgentResult(agent);
87
99
  }
88
100
  catch (error) {
101
+ // 취소로 인한 abort는 무시
102
+ if (agent.status === "cancelled") {
103
+ console.log(`[Agent ${agent.id}] Aborted due to cancellation`);
104
+ return;
105
+ }
89
106
  agent.status = "failed";
90
107
  agent.completedAt = new Date();
91
108
  agent.error = error instanceof Error ? error.message : String(error);
@@ -93,6 +110,10 @@ Complete the task and provide your final answer.`;
93
110
  // 실패도 알림
94
111
  await sendAgentResult(agent);
95
112
  }
113
+ finally {
114
+ // Controller 정리
115
+ abortControllers.delete(agent.id);
116
+ }
96
117
  }
97
118
  /**
98
119
  * Agent 결과를 chat에 전송
@@ -143,10 +164,15 @@ export function cancelAgent(agentId) {
143
164
  if (agent.status !== "running") {
144
165
  return false; // 이미 완료된 agent는 취소 불가
145
166
  }
146
- // 실제로 실행 중인 API 호출을 취소할 수는 없지만
147
- // 상태를 cancelled로 표시하고 결과 전송 시 무시되도록 함
167
+ // 상태를 먼저 cancelled로 설정 (race condition 방지)
148
168
  agent.status = "cancelled";
149
169
  agent.completedAt = new Date();
170
+ // 실행 중인 API 호출 취소
171
+ const controller = abortControllers.get(agentId);
172
+ if (controller) {
173
+ controller.abort();
174
+ abortControllers.delete(agentId);
175
+ }
150
176
  console.log(`[Agent ${agentId}] Cancelled`);
151
177
  return true;
152
178
  }
@@ -158,14 +184,44 @@ export function getAgent(agentId) {
158
184
  }
159
185
  /**
160
186
  * 오래된 agent 정리 (1시간 이상)
187
+ * - 완료된 agent: completedAt 기준 1시간
188
+ * - running 상태도 createdAt 기준 1시간 지나면 정리 (stuck 방지)
161
189
  */
162
190
  export function cleanupOldAgents() {
163
191
  const oneHourAgo = Date.now() - 60 * 60 * 1000;
164
192
  for (const [id, agent] of agents.entries()) {
193
+ // 완료된 agent: completedAt 기준
165
194
  if (agent.completedAt && agent.completedAt.getTime() < oneHourAgo) {
166
195
  agents.delete(id);
196
+ continue;
167
197
  }
198
+ // running 상태도 1시간 지나면 정리 (stuck agent 방지)
199
+ if (agent.status === "running" && agent.createdAt.getTime() < oneHourAgo) {
200
+ console.log(`[Agent ${id}] Cleaning up stuck agent (running > 1h)`);
201
+ agents.delete(id);
202
+ }
203
+ }
204
+ }
205
+ // Cleanup interval 참조 저장
206
+ let cleanupIntervalId = null;
207
+ /**
208
+ * 정기 cleanup 시작
209
+ */
210
+ export function startCleanup() {
211
+ if (cleanupIntervalId)
212
+ return; // 이미 실행 중
213
+ cleanupIntervalId = setInterval(cleanupOldAgents, 10 * 60 * 1000);
214
+ console.log("[AgentManager] Cleanup interval started");
215
+ }
216
+ /**
217
+ * 정기 cleanup 중지
218
+ */
219
+ export function stopCleanup() {
220
+ if (cleanupIntervalId) {
221
+ clearInterval(cleanupIntervalId);
222
+ cleanupIntervalId = null;
223
+ console.log("[AgentManager] Cleanup interval stopped");
168
224
  }
169
225
  }
170
- // 10분마다 정리
171
- setInterval(cleanupOldAgents, 10 * 60 * 1000);
226
+ // 자동 시작
227
+ startCleanup();