feat: kakao_send_message 에이전트 도구화 + 멀티기기 메인챗 세션 동기화 + 터미널 전체화면

- src/tools/kakao.ts: 기존 KakaoNotify를 kakao_send_message 도구로 노출, 코드에디터에서는 차단
- app.js/server-v2.ts: 메인챗 마지막 활성 세션을 서버에 기록해 새 기기에서 열 때 자동 이어보기
- code.html/code.js/styles.css: 서버 터미널 패널 전체화면 토글 (Esc로 종료)
- companion.py: .vbs 자동시작 시 cwd가 System32로 잡히는 문제 방지 위해 홈 디렉터리로 고정
- papa 워크스페이스: 우즈베크어 학습 진행상황, 서버 이름(지서버/클로서버) 메모 갱신

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
kim
2026-07-05 14:41:57 +09:00
co-authored by Claude Sonnet 5
parent ce1a99b723
commit 186dc4cb23
11 changed files with 193 additions and 4 deletions
+61
View File
@@ -0,0 +1,61 @@
import { ToolResult } from '../types.js';
import { getConfig } from '../config/config.js';
import { getVault } from '../security/vault.js';
import { KakaoNotify } from '../gateway/kakao-notify.js';
function getKakaoNotify(): KakaoNotify | null {
const cm = getConfig();
const cfg = cm.getConfig() as any;
const raw = cfg.channels?.kakao || {};
const vault = getVault(cm.getConfigDir());
const appKey = cm.resolveSecret(raw.appKey) || vault.get('channels.kakao.appKey', 'kakao:init')?.expose() || '';
const refreshToken = cm.resolveSecret(raw.refreshToken) || vault.get('channels.kakao.refreshToken', 'kakao:init')?.expose() || '';
if (!appKey || !refreshToken) return null;
return new KakaoNotify({
appKey,
refreshToken,
onTokenRefreshed: (newToken) => vault.set('channels.kakao.refreshToken', newToken, 'kakao:token'),
});
}
// ── kakao_send_message ──────────────────────────────────────────────────────
export const kakaoSendTool = {
name: 'kakao_send_message',
description: '카카오톡 "나에게 보내기"로 텍스트를 본인 카카오톡에 전송합니다. 차트 링크, 요약, 알림 등을 카카오톡으로 보내달라는 요청에 사용. 이미지 첨부는 지원하지 않으므로 링크/텍스트로만 전송합니다.',
schema: {
text: '보낼 메시지 내용 (URL은 텍스트에 그대로 포함)',
},
jsonSchema: {
type: 'object',
properties: {
text: { type: 'string', description: '보낼 메시지 (URL 포함 가능, 최대 2000자 단위로 자동 분할)' },
},
required: ['text'],
additionalProperties: false,
},
execute: async (args: any): Promise<ToolResult> => {
const text = String(args?.text || '').trim();
if (!text) return { success: false, error: 'text(보낼 내용)가 필요합니다.' };
const notifier = getKakaoNotify();
if (!notifier) {
return {
success: false,
error: '카카오 연동이 설정되어 있지 않습니다. 서버 설정에서 /api/kakao/auth/start 로 먼저 카카오 로그인을 완료하세요.',
};
}
try {
await notifier.send(text);
return {
success: true,
stdout: `✅ 카카오톡(나에게 보내기)으로 전송 완료.\n내용: ${text.slice(0, 200)}${text.length > 200 ? '...' : ''}`,
};
} catch (err: any) {
return { success: false, error: `카카오톡 전송 실패: ${err.message}` };
}
},
};
+3
View File
@@ -22,6 +22,7 @@ import { pythonEvalTool } from './python.js';
import { sqliteTool } from './sqlite.js';
import { weatherSearchTool, weatherAirPollutionTool, weatherOpenMeteoTool, weatherKmaTool, weatherAirKoreaTool, weatherNasaPowerTool, weatherEra5Tool, weatherCdsTool, weatherCmip6Tool } from './weather.js';
import { emailListTool, emailReadTool, emailSendTool, emailSearchTool, emailDeleteTool } from './email.js';
import { kakaoSendTool } from './kakao.js';
import { koreanLawSearchTool, koreanLawFetchTool, usCaseSearchTool } from './legal.js';
import { excelReadTool, excelWriteTool } from './excel.js';
@@ -222,6 +223,8 @@ class ToolRegistry {
this.registerSafe(emailSendTool);
this.registerSafe(emailSearchTool);
this.registerSafe(emailDeleteTool);
// Kakao push (나에게 보내기)
this.registerSafe(kakaoSendTool);
// Legal research tools
this.registerSafe(koreanLawSearchTool);
this.registerSafe(koreanLawFetchTool);