junis 0.2.4 → 0.2.6
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 +129 -93
- package/dist/cli/index.js +190 -40
- package/dist/server/mcp.js +17 -13
- package/dist/server/stdio.js +17 -13
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -3,160 +3,196 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/junis)
|
|
4
4
|
[](./LICENSE)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
AI 에이전트가 내 컴퓨터를 완전 제어할 수 있게 해주는 로컬 MCP 도구 서버입니다.
|
|
7
|
+
셸 명령 실행, 파일 관리, 브라우저 자동화, 노트북 편집, 화면/카메라 캡처 등 28개 도구를 제공합니다.
|
|
7
8
|
|
|
8
9
|
---
|
|
9
10
|
|
|
10
|
-
##
|
|
11
|
+
## 특징
|
|
11
12
|
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
13
|
+
- **제로 설정 로컬 모드** — `npx junis --local` 한 줄이면 바로 사용 가능
|
|
14
|
+
- **클라우드 릴레이 모드** — `wss://api.junis.ai`를 통해 원격 디바이스 제어
|
|
15
|
+
- **파일 & 셸 도구** — 읽기, 쓰기, 검색, 명령 실행, 프로세스 관리
|
|
16
|
+
- **브라우저 자동화** — Playwright 기반 웹 탐색, 클릭, 입력, 스크린샷, PDF 저장
|
|
17
|
+
- **노트북 도구** — Jupyter 노트북 셀 읽기, 수정, 추가, 삭제, 실행
|
|
18
|
+
- **디바이스 도구** — 화면 캡처, 카메라, 알림, 클립보드, 녹화, 위치, 오디오
|
|
19
|
+
- **MCP 호환** — Claude Code, Claude Desktop 등 모든 MCP 클라이언트와 연동
|
|
20
|
+
- **TypeScript** — `@modelcontextprotocol/sdk` 기반
|
|
18
21
|
|
|
19
22
|
---
|
|
20
23
|
|
|
21
|
-
##
|
|
24
|
+
## 설치 및 실행
|
|
22
25
|
|
|
23
|
-
###
|
|
26
|
+
### 로컬 모드 (계정 불필요)
|
|
24
27
|
|
|
25
28
|
```bash
|
|
26
29
|
npx junis --local
|
|
27
30
|
```
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
`http://localhost:3000/mcp`에 MCP 서버가 시작됩니다. MCP 클라이언트에서 이 주소를 지정하면 바로 사용할 수 있습니다.
|
|
30
33
|
|
|
31
|
-
###
|
|
34
|
+
### 클라우드 모드
|
|
32
35
|
|
|
33
36
|
```bash
|
|
34
37
|
npx junis
|
|
35
38
|
```
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
최초 실행 시 브라우저가 열려 OAuth 로그인을 진행합니다. 인증 후 디바이스가 `wss://api.junis.ai`에 연결되어 원격 AI 에이전트 세션을 받을 수 있습니다. 백그라운드 데몬으로 실행되며 부팅 시 자동 시작됩니다.
|
|
41
|
+
|
|
42
|
+
### 포트 변경
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npx junis --local --port 8080
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 제공 도구 목록
|
|
51
|
+
|
|
52
|
+
### FilesystemTools (8개)
|
|
53
|
+
|
|
54
|
+
| 도구 | 설명 |
|
|
55
|
+
|------|------|
|
|
56
|
+
| `execute_command` | 터미널 명령 실행 (타임아웃, 백그라운드 실행 지원) |
|
|
57
|
+
| `read_file` | 파일 읽기 (utf-8, base64 인코딩 지원) |
|
|
58
|
+
| `write_file` | 파일 쓰기/생성 (상위 디렉토리 자동 생성) |
|
|
59
|
+
| `edit_block` | 파일의 특정 텍스트 블록을 교체 (diff 기반 부분 수정) |
|
|
60
|
+
| `list_directory` | 디렉토리 내 파일/폴더 목록 조회 |
|
|
61
|
+
| `search_code` | 코드/텍스트 패턴 검색 (ripgrep 우선, fallback으로 glob 검색) |
|
|
62
|
+
| `list_processes` | 실행 중인 프로세스 목록 (CPU 사용률 순) |
|
|
63
|
+
| `kill_process` | 프로세스 종료 (SIGTERM 후 3초 대기, 미종료 시 SIGKILL 자동 적용) |
|
|
64
|
+
|
|
65
|
+
### BrowserTools (7개)
|
|
66
|
+
|
|
67
|
+
> Playwright 설치 필요: `npx playwright install chromium`
|
|
68
|
+
> 미설치 시 브라우저 도구 호출 시 경고 메시지만 출력되며 서버가 중단되지 않습니다.
|
|
69
|
+
|
|
70
|
+
| 도구 | 설명 |
|
|
71
|
+
|------|------|
|
|
72
|
+
| `browser_navigate` | URL로 이동 |
|
|
73
|
+
| `browser_click` | CSS 선택자로 요소 클릭 |
|
|
74
|
+
| `browser_type` | 텍스트 입력 (기존 내용 삭제 후 입력 옵션) |
|
|
75
|
+
| `browser_screenshot` | 현재 페이지 스크린샷 (파일 저장 또는 base64 반환) |
|
|
76
|
+
| `browser_snapshot` | 페이지 접근성 트리(aria snapshot) 조회 |
|
|
77
|
+
| `browser_evaluate` | 페이지 내 JavaScript 실행 |
|
|
78
|
+
| `browser_pdf` | 현재 페이지를 PDF로 저장 |
|
|
79
|
+
|
|
80
|
+
### NotebookTools (5개)
|
|
81
|
+
|
|
82
|
+
> Jupyter 설치 필요: `pip install jupyter`
|
|
83
|
+
|
|
84
|
+
| 도구 | 설명 |
|
|
85
|
+
|------|------|
|
|
86
|
+
| `notebook_read` | .ipynb 노트북 셀 목록 및 내용 조회 |
|
|
87
|
+
| `notebook_edit_cell` | 특정 셀 소스 코드 수정 |
|
|
88
|
+
| `notebook_add_cell` | 새 셀 추가 (code/markdown, 위치 지정 가능) |
|
|
89
|
+
| `notebook_delete_cell` | 특정 셀 삭제 |
|
|
90
|
+
| `notebook_execute` | 노트북 전체 실행 (nbconvert --execute) |
|
|
91
|
+
|
|
92
|
+
### DeviceTools (8개)
|
|
93
|
+
|
|
94
|
+
| 도구 | 설명 |
|
|
95
|
+
|------|------|
|
|
96
|
+
| `screen_capture` | 화면 스크린샷 (macOS: screencapture, Linux: scrot) |
|
|
97
|
+
| `camera_capture` | 카메라 사진 촬영 (macOS: imagesnap, Linux: fswebcam) |
|
|
98
|
+
| `notification_send` | OS 알림 전송 |
|
|
99
|
+
| `clipboard_read` | 클립보드 내용 읽기 |
|
|
100
|
+
| `clipboard_write` | 클립보드에 텍스트 쓰기 |
|
|
101
|
+
| `screen_record` | 화면 녹화 시작/중지 (macOS: screencapture -v, 기타: ffmpeg) |
|
|
102
|
+
| `location_get` | 현재 위치 조회 (macOS: CoreLocation, fallback: IP 기반) |
|
|
103
|
+
| `audio_play` | 오디오 파일 재생 (macOS: afplay, 기타: ffplay) |
|
|
38
104
|
|
|
39
105
|
---
|
|
40
106
|
|
|
41
|
-
## CLI
|
|
107
|
+
## CLI 명령어
|
|
42
108
|
|
|
43
109
|
```
|
|
44
110
|
Usage: npx junis [options] [command]
|
|
111
|
+
```
|
|
45
112
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
113
|
+
| 명령어 | 설명 |
|
|
114
|
+
|--------|------|
|
|
115
|
+
| `start` (기본) | Junis 에이전트 시작. 인증 후 백그라운드 데몬으로 실행 |
|
|
116
|
+
| `stop` | 백그라운드 서비스 중지 및 자동시작 해제 |
|
|
117
|
+
| `logout` | 저장된 인증 정보 삭제 |
|
|
118
|
+
| `status` | 현재 연결 및 디바이스 상태 확인 |
|
|
50
119
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
120
|
+
### 옵션
|
|
121
|
+
|
|
122
|
+
| 옵션 | 설명 |
|
|
123
|
+
|------|------|
|
|
124
|
+
| `--local` | 클라우드 연결 없이 로컬 MCP 서버만 실행 |
|
|
125
|
+
| `--port <number>` | 로컬 MCP 서버 포트 (기본: 3000) |
|
|
126
|
+
| `--reset` | 기존 인증 초기화 후 재로그인 |
|
|
55
127
|
|
|
56
|
-
###
|
|
128
|
+
### 사용 예시
|
|
57
129
|
|
|
58
130
|
```bash
|
|
59
|
-
#
|
|
131
|
+
# 로컬 MCP 서버 실행 (기본 포트)
|
|
60
132
|
npx junis --local
|
|
61
133
|
|
|
62
|
-
#
|
|
134
|
+
# 로컬 MCP 서버 실행 (포트 지정)
|
|
63
135
|
npx junis --local --port 8080
|
|
64
136
|
|
|
65
|
-
#
|
|
137
|
+
# 클라우드 모드로 시작 (최초 실행 시 인증)
|
|
138
|
+
npx junis
|
|
139
|
+
|
|
140
|
+
# 인증 초기화 후 재로그인
|
|
66
141
|
npx junis --reset
|
|
67
142
|
|
|
68
|
-
#
|
|
143
|
+
# 백그라운드 서비스 중지
|
|
144
|
+
npx junis stop
|
|
145
|
+
|
|
146
|
+
# 연결 상태 확인
|
|
69
147
|
npx junis status
|
|
70
148
|
|
|
71
|
-
#
|
|
149
|
+
# 로그아웃
|
|
72
150
|
npx junis logout
|
|
73
151
|
```
|
|
74
152
|
|
|
75
153
|
---
|
|
76
154
|
|
|
77
|
-
##
|
|
155
|
+
## Claude Code 연동
|
|
78
156
|
|
|
79
|
-
|
|
157
|
+
junis를 stdio MCP 서버로 등록하면 Claude Code가 자동으로 실행합니다:
|
|
80
158
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
| `read_file` | Read file contents |
|
|
85
|
-
| `write_file` | Write or overwrite a file |
|
|
86
|
-
| `list_directory` | List files and directories |
|
|
87
|
-
| `search_code` | Search for text patterns across files |
|
|
88
|
-
| `list_processes` | List running processes |
|
|
89
|
-
| `kill_process` | Kill a process by PID |
|
|
90
|
-
|
|
91
|
-
### Browser (requires Playwright)
|
|
92
|
-
|
|
93
|
-
| Tool | Description |
|
|
94
|
-
|------|-------------|
|
|
95
|
-
| `browser_navigate` | Navigate to a URL |
|
|
96
|
-
| `browser_click` | Click an element by selector |
|
|
97
|
-
| `browser_type` | Type text into an input |
|
|
98
|
-
| `browser_screenshot` | Take a screenshot of the page |
|
|
99
|
-
| `browser_snapshot` | Capture DOM snapshot for accessibility |
|
|
100
|
-
| `browser_evaluate` | Execute JavaScript in the page |
|
|
101
|
-
| `browser_pdf` | Save the page as a PDF |
|
|
102
|
-
|
|
103
|
-
> Playwright is optional. If not installed, browser tools emit a warning instead of crashing.
|
|
104
|
-
|
|
105
|
-
### Notebook (requires Jupyter)
|
|
106
|
-
|
|
107
|
-
| Tool | Description |
|
|
108
|
-
|------|-------------|
|
|
109
|
-
| `notebook_read` | Read Jupyter notebook cells |
|
|
110
|
-
| `notebook_edit_cell` | Edit a specific cell in a notebook |
|
|
111
|
-
| `notebook_execute` | Execute notebook via nbconvert |
|
|
112
|
-
|
|
113
|
-
> Install Jupyter: `pip install jupyter`
|
|
114
|
-
|
|
115
|
-
### Device (macOS / Linux / Windows)
|
|
116
|
-
|
|
117
|
-
| Tool | Description |
|
|
118
|
-
|------|-------------|
|
|
119
|
-
| `screen_capture` | Capture a screenshot using OS native tools |
|
|
120
|
-
| `camera_capture` | Take a photo using the webcam |
|
|
121
|
-
| `notification_send` | Send an OS notification |
|
|
122
|
-
| `clipboard_read` | Read clipboard contents |
|
|
123
|
-
| `clipboard_write` | Write text to clipboard |
|
|
159
|
+
```bash
|
|
160
|
+
claude mcp add junis -- npx junis --local
|
|
161
|
+
```
|
|
124
162
|
|
|
125
|
-
|
|
163
|
+
등록 후 Claude Code에서 별도 서버 시작 없이 모든 도구를 바로 사용할 수 있습니다.
|
|
126
164
|
|
|
127
165
|
---
|
|
128
166
|
|
|
129
|
-
##
|
|
130
|
-
|
|
131
|
-
Register junis as a stdio MCP server so Claude Code can launch it automatically:
|
|
132
|
-
|
|
133
|
-
```bash
|
|
134
|
-
claude mcp add junis node /Users/leehyeonjin/development/junis-mcp/dist/server/stdio.js
|
|
135
|
-
```
|
|
167
|
+
## 알려진 제한사항
|
|
136
168
|
|
|
137
|
-
|
|
169
|
+
- **camera_capture (macOS 데몬 모드)**: macOS에서 데몬(백그라운드) 모드로 실행 시 카메라에 접근하려면 **시스템 설정 > 개인정보 보호 및 보안 > 카메라**에서 `node`에 권한을 직접 부여해야 합니다.
|
|
170
|
+
- **screen_capture (macOS)**: 화면 녹화 권한이 필요합니다. **시스템 설정 > 개인정보 보호 및 보안 > 화면 및 시스템 오디오 녹음**에서 터미널 앱에 권한을 부여하세요.
|
|
171
|
+
- **browser 도구**: Playwright가 설치되어 있지 않으면 브라우저 도구 호출 시 경고만 출력됩니다. `npx playwright install chromium`으로 설치하세요.
|
|
172
|
+
- **notebook 도구**: Jupyter가 설치되어 있지 않으면 노트북 실행(`notebook_execute`)이 실패합니다. `pip install jupyter`로 설치하세요.
|
|
173
|
+
- **location_get**: macOS에서 CoreLocationCLI가 없으면 IP 기반 위치 추정으로 자동 전환됩니다 (정확도 낮음).
|
|
138
174
|
|
|
139
175
|
---
|
|
140
176
|
|
|
141
|
-
##
|
|
177
|
+
## 환경 변수
|
|
142
178
|
|
|
143
|
-
|
|
|
144
|
-
|
|
145
|
-
| `JUNIS_API_URL` | `https://api.junis.ai` | REST API
|
|
146
|
-
| `JUNIS_WS_URL` | `wss://api.junis.ai` | WebSocket
|
|
147
|
-
| `JUNIS_WEB_URL` | `https://junis.ai` |
|
|
179
|
+
| 변수 | 기본값 | 설명 |
|
|
180
|
+
|------|--------|------|
|
|
181
|
+
| `JUNIS_API_URL` | `https://api.junis.ai` | REST API 기본 URL |
|
|
182
|
+
| `JUNIS_WS_URL` | `wss://api.junis.ai` | WebSocket 릴레이 URL |
|
|
183
|
+
| `JUNIS_WEB_URL` | `https://junis.ai` | 웹 UI URL (OAuth 시 사용) |
|
|
148
184
|
|
|
149
185
|
---
|
|
150
186
|
|
|
151
|
-
##
|
|
187
|
+
## 요구 사항
|
|
152
188
|
|
|
153
189
|
- Node.js >= 18
|
|
154
|
-
- (
|
|
155
|
-
- (
|
|
156
|
-
- (
|
|
190
|
+
- (선택) Playwright — 브라우저 도구: `npx playwright install chromium`
|
|
191
|
+
- (선택) Jupyter — 노트북 도구: `pip install jupyter`
|
|
192
|
+
- (선택) imagesnap — macOS 카메라 캡처: `brew install imagesnap`
|
|
157
193
|
|
|
158
194
|
---
|
|
159
195
|
|
|
160
|
-
##
|
|
196
|
+
## 라이선스
|
|
161
197
|
|
|
162
198
|
MIT
|
package/dist/cli/index.js
CHANGED
|
@@ -31,7 +31,7 @@ var require_package = __commonJS({
|
|
|
31
31
|
"package.json"(exports2, module2) {
|
|
32
32
|
module2.exports = {
|
|
33
33
|
name: "junis",
|
|
34
|
-
version: "0.2.
|
|
34
|
+
version: "0.2.6",
|
|
35
35
|
description: "One-line device control for AI agents",
|
|
36
36
|
bin: {
|
|
37
37
|
junis: "dist/cli/index.js"
|
|
@@ -43,9 +43,11 @@ var require_package = __commonJS({
|
|
|
43
43
|
prepublishOnly: "npm run build"
|
|
44
44
|
},
|
|
45
45
|
dependencies: {
|
|
46
|
+
"@inquirer/prompts": "^8.2.1",
|
|
46
47
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
47
48
|
commander: "^12.0.0",
|
|
48
49
|
glob: "^11.0.0",
|
|
50
|
+
"node-notifier": "^10.0.1",
|
|
49
51
|
open: "^10.1.0",
|
|
50
52
|
playwright: "^1.49.0",
|
|
51
53
|
ws: "^8.18.0",
|
|
@@ -53,6 +55,7 @@ var require_package = __commonJS({
|
|
|
53
55
|
},
|
|
54
56
|
devDependencies: {
|
|
55
57
|
"@types/node": "^20.0.0",
|
|
58
|
+
"@types/node-notifier": "^8.0.5",
|
|
56
59
|
"@types/ws": "^8.5.0",
|
|
57
60
|
tsup: "^8.0.0",
|
|
58
61
|
tsx: "^4.0.0",
|
|
@@ -70,6 +73,7 @@ var require_package = __commonJS({
|
|
|
70
73
|
|
|
71
74
|
// src/cli/index.ts
|
|
72
75
|
var import_commander = require("commander");
|
|
76
|
+
var import_prompts = require("@inquirer/prompts");
|
|
73
77
|
|
|
74
78
|
// src/cli/config.ts
|
|
75
79
|
var import_fs = __toESM(require("fs"));
|
|
@@ -836,6 +840,7 @@ var NotebookTools = class {
|
|
|
836
840
|
var import_child_process3 = require("child_process");
|
|
837
841
|
var import_util3 = require("util");
|
|
838
842
|
var import_zod4 = require("zod");
|
|
843
|
+
var import_node_notifier = __toESM(require("node-notifier"));
|
|
839
844
|
var execAsync3 = (0, import_util3.promisify)(import_child_process3.exec);
|
|
840
845
|
var screenRecordPid = null;
|
|
841
846
|
function platform() {
|
|
@@ -924,20 +929,23 @@ var DeviceTools = class {
|
|
|
924
929
|
message: import_zod4.z.string().describe("\uC54C\uB9BC \uB0B4\uC6A9")
|
|
925
930
|
},
|
|
926
931
|
async ({ title, message }) => {
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
932
|
+
try {
|
|
933
|
+
await new Promise((resolve, reject) => {
|
|
934
|
+
import_node_notifier.default.notify(
|
|
935
|
+
{ title, message },
|
|
936
|
+
(err) => {
|
|
937
|
+
if (err) reject(err);
|
|
938
|
+
else resolve();
|
|
939
|
+
}
|
|
940
|
+
);
|
|
941
|
+
});
|
|
942
|
+
return { content: [{ type: "text", text: "\uC54C\uB9BC \uC804\uC1A1 \uC644\uB8CC" }] };
|
|
943
|
+
} catch (err) {
|
|
944
|
+
return {
|
|
945
|
+
content: [{ type: "text", text: `\uC54C\uB9BC \uC804\uC1A1 \uC2E4\uD328: ${err.message}` }],
|
|
946
|
+
isError: true
|
|
947
|
+
};
|
|
938
948
|
}
|
|
939
|
-
await execAsync3(cmd);
|
|
940
|
-
return { content: [{ type: "text", text: "\uC54C\uB9BC \uC804\uC1A1 \uC644\uB8CC" }] };
|
|
941
949
|
}
|
|
942
950
|
);
|
|
943
951
|
server.tool(
|
|
@@ -1522,8 +1530,154 @@ function printStep1(port) {
|
|
|
1522
1530
|
console.log(` \u25C9 Local MCP endpoint ........... http://localhost:${port}/mcp`);
|
|
1523
1531
|
console.log("");
|
|
1524
1532
|
}
|
|
1525
|
-
|
|
1533
|
+
async function runForeground(config, port) {
|
|
1534
|
+
const deviceName = config.device_name;
|
|
1535
|
+
const platformName = process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : "linux";
|
|
1536
|
+
const actualPort = await startMCPServer(port);
|
|
1537
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1538
|
+
console.log(" STEP 5 \xB7 Starting Foreground Service");
|
|
1539
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1540
|
+
console.log(` \u25C9 MCP server started on port ${actualPort}`);
|
|
1541
|
+
const relay = new RelayClient(config, handleMCPRequest, async () => {
|
|
1542
|
+
console.log("[junis] \uC138\uC158 \uB9CC\uB8CC - \uC7AC\uC778\uC99D \uD544\uC694");
|
|
1543
|
+
try {
|
|
1544
|
+
let waitingPrinted = false;
|
|
1545
|
+
const authResult = await authenticate(
|
|
1546
|
+
deviceName,
|
|
1547
|
+
platformName,
|
|
1548
|
+
(uri) => {
|
|
1549
|
+
console.log(`[junis] \uBE0C\uB77C\uC6B0\uC800 \uC7AC\uC778\uC99D: ${uri}`);
|
|
1550
|
+
},
|
|
1551
|
+
() => {
|
|
1552
|
+
if (!waitingPrinted) waitingPrinted = true;
|
|
1553
|
+
}
|
|
1554
|
+
);
|
|
1555
|
+
config.token = authResult.token;
|
|
1556
|
+
saveConfig(config);
|
|
1557
|
+
relay.restart();
|
|
1558
|
+
} catch (e) {
|
|
1559
|
+
console.error("[junis] \uC7AC\uC778\uC99D \uC2E4\uD328:", e);
|
|
1560
|
+
process.exit(1);
|
|
1561
|
+
}
|
|
1562
|
+
});
|
|
1563
|
+
await relay.connect();
|
|
1564
|
+
const webUrl = process.env.JUNIS_WEB_URL ?? "https://junis.ai";
|
|
1565
|
+
console.log(" \u25C9 Relay connected");
|
|
1566
|
+
console.log("");
|
|
1567
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1568
|
+
console.log(" \u2705 ALL SET \u2014 Junis\uAC00 \uD3EC\uADF8\uB77C\uC6B4\uB4DC\uC5D0\uC11C \uC2E4\uD589 \uC911\uC785\uB2C8\uB2E4.");
|
|
1569
|
+
console.log(" Ctrl+C\uB97C \uB20C\uB7EC \uC885\uB8CC\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
1570
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1571
|
+
console.log("");
|
|
1572
|
+
console.log(` \u2192 ${webUrl}`);
|
|
1573
|
+
console.log("");
|
|
1574
|
+
const shutdown = () => {
|
|
1575
|
+
console.log("\n[junis] Shutting down...");
|
|
1576
|
+
relay.destroy();
|
|
1577
|
+
process.exit(0);
|
|
1578
|
+
};
|
|
1579
|
+
process.on("SIGINT", shutdown);
|
|
1580
|
+
process.on("SIGTERM", shutdown);
|
|
1581
|
+
}
|
|
1582
|
+
async function runBackground(config, port) {
|
|
1583
|
+
void config;
|
|
1584
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1585
|
+
console.log(" STEP 5 \xB7 Starting Background Service");
|
|
1586
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1587
|
+
const svc = new ServiceManager();
|
|
1588
|
+
try {
|
|
1589
|
+
await svc.install();
|
|
1590
|
+
console.log(" \u25C9 Service registered ........... \u2705");
|
|
1591
|
+
console.log(" \u25C9 Auto-start on boot ........... \u2705");
|
|
1592
|
+
} catch (e) {
|
|
1593
|
+
console.warn(` \u26A0\uFE0F \uC11C\uBE44\uC2A4 \uB4F1\uB85D \uC2E4\uD328: ${e.message}`);
|
|
1594
|
+
console.warn(" \uBC31\uADF8\uB77C\uC6B4\uB4DC \uD504\uB85C\uC138\uC2A4\uB85C\uB9CC \uC2E4\uD589\uD569\uB2C8\uB2E4.");
|
|
1595
|
+
startDaemon(port);
|
|
1596
|
+
}
|
|
1597
|
+
const webUrl = process.env.JUNIS_WEB_URL ?? "https://junis.ai";
|
|
1598
|
+
console.log("");
|
|
1599
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1600
|
+
console.log(" \u2705 ALL SET \u2014 Junis\uAC00 \uBC31\uADF8\uB77C\uC6B4\uB4DC\uC5D0\uC11C \uC2E4\uD589 \uC911\uC785\uB2C8\uB2E4.");
|
|
1601
|
+
console.log(" \uBD80\uD305 \uC2DC \uC790\uB3D9\uC73C\uB85C \uC2DC\uC791\uB429\uB2C8\uB2E4.");
|
|
1602
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1603
|
+
console.log("");
|
|
1604
|
+
console.log(` \u2192 ${webUrl}`);
|
|
1605
|
+
console.log("");
|
|
1606
|
+
console.log(" \uC885\uB8CC\uD558\uB824\uBA74: npx junis stop");
|
|
1607
|
+
console.log("");
|
|
1608
|
+
process.exit(0);
|
|
1609
|
+
}
|
|
1610
|
+
import_commander.program.command("start", { isDefault: true }).description("Junis \uC5D0\uC774\uC804\uD2B8\uC640 \uC5F0\uACB0 \uC2DC\uC791").option("--local", "\uB85C\uCEEC MCP \uC11C\uBC84\uB9CC \uC2E4\uD589 (\uD074\uB77C\uC6B0\uB4DC \uC5F0\uACB0 \uC5C6\uC74C)").option("--port <number>", "\uD3EC\uD2B8 \uBC88\uD638", "3000").option("--reset", "\uAE30\uC874 \uC778\uC99D \uCD08\uAE30\uD654 \uD6C4 \uC7AC\uB85C\uADF8\uC778").option("--daemon", "\uB370\uBAAC \uBAA8\uB4DC\uB85C \uC2E4\uD589 (\uB0B4\uBD80\uC6A9, launchd/systemd\uC5D0\uC11C \uC0AC\uC6A9)").option("--foreground", "\uD3EC\uADF8\uB77C\uC6B4\uB4DC \uBAA8\uB4DC\uB85C \uC2E4\uD589 (\uD504\uB86C\uD504\uD2B8 \uC5C6\uC774)").action(async (options) => {
|
|
1526
1611
|
const port = parseInt(options.port, 10);
|
|
1612
|
+
if (options.foreground) {
|
|
1613
|
+
printBanner();
|
|
1614
|
+
let config2 = options.reset ? null : loadConfig();
|
|
1615
|
+
const deviceName2 = config2?.device_name ?? `${process.env["USER"] ?? "user"}'s ${getDeviceName()}`;
|
|
1616
|
+
const platformName2 = process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : "linux";
|
|
1617
|
+
printStep1(port);
|
|
1618
|
+
if (!config2) {
|
|
1619
|
+
let waitingPrinted = false;
|
|
1620
|
+
const authResult = await authenticate(
|
|
1621
|
+
deviceName2,
|
|
1622
|
+
platformName2,
|
|
1623
|
+
(uri) => {
|
|
1624
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1625
|
+
console.log(" STEP 2 \xB7 Connect to Junis Cloud");
|
|
1626
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1627
|
+
console.log(" Opening browser...");
|
|
1628
|
+
console.log(` \u2192 ${uri}`);
|
|
1629
|
+
process.stdout.write(" Waiting for login \xB7");
|
|
1630
|
+
},
|
|
1631
|
+
() => {
|
|
1632
|
+
if (!waitingPrinted) {
|
|
1633
|
+
waitingPrinted = true;
|
|
1634
|
+
} else {
|
|
1635
|
+
process.stdout.write("\xB7");
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
);
|
|
1639
|
+
console.log("");
|
|
1640
|
+
console.log(` \u2705 Authenticated as ${authResult.email ?? "your account"}`);
|
|
1641
|
+
console.log("");
|
|
1642
|
+
config2 = {
|
|
1643
|
+
device_key: authResult.device_key,
|
|
1644
|
+
token: authResult.token,
|
|
1645
|
+
device_name: deviceName2,
|
|
1646
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1647
|
+
};
|
|
1648
|
+
saveConfig(config2);
|
|
1649
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1650
|
+
console.log(" STEP 3 \xB7 Register Device");
|
|
1651
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1652
|
+
console.log(` \u25C9 Device name .................. ${deviceName2}`);
|
|
1653
|
+
console.log(` \u25C9 Device key ................... ${authResult.device_key}`);
|
|
1654
|
+
console.log(` \u25C9 Cloud relay connected ........ wss://junis.ai/ws/devices/${authResult.device_key}`);
|
|
1655
|
+
console.log(" \u25C9 Status ....................... \u{1F7E2} online");
|
|
1656
|
+
console.log("");
|
|
1657
|
+
if (authResult.agent_id) {
|
|
1658
|
+
const workspaceName = `${deviceName2} Workspace`;
|
|
1659
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1660
|
+
console.log(" STEP 4 \xB7 Create AI Team");
|
|
1661
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1662
|
+
console.log(` \u25C9 Workspace created ............ ${workspaceName}`);
|
|
1663
|
+
console.log(` \u25C9 Agent created ................ ${authResult.agent_name}`);
|
|
1664
|
+
console.log(` \u25C9 Device linked ................ ${deviceName2} \u2192 ${authResult.agent_name}`);
|
|
1665
|
+
console.log(" \u25C9 Tools assigned ............... call_device_mcp, list_device_mcp_tools");
|
|
1666
|
+
console.log("");
|
|
1667
|
+
}
|
|
1668
|
+
} else {
|
|
1669
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1670
|
+
console.log(" STEP 3 \xB7 Register Device");
|
|
1671
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1672
|
+
console.log(` \u25C9 Device name .................. ${config2.device_name}`);
|
|
1673
|
+
console.log(` \u25C9 Device key ................... ${config2.device_key}`);
|
|
1674
|
+
console.log(` \u25C9 Cloud relay connected ........ wss://junis.ai/ws/devices/${config2.device_key}`);
|
|
1675
|
+
console.log(" \u25C9 Status ....................... \u{1F7E2} online");
|
|
1676
|
+
console.log("");
|
|
1677
|
+
}
|
|
1678
|
+
await runForeground(config2, port);
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1527
1681
|
if (options.daemon) {
|
|
1528
1682
|
writePid(process.pid);
|
|
1529
1683
|
if (options.local) {
|
|
@@ -1565,8 +1719,8 @@ import_commander.program.command("start", { isDefault: true }).description("Juni
|
|
|
1565
1719
|
console.log("[junis daemon] relay connected");
|
|
1566
1720
|
return;
|
|
1567
1721
|
}
|
|
1568
|
-
printBanner();
|
|
1569
1722
|
if (options.local) {
|
|
1723
|
+
printBanner();
|
|
1570
1724
|
const actualPort = await startMCPServer(port);
|
|
1571
1725
|
printStep1(actualPort);
|
|
1572
1726
|
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
@@ -1574,6 +1728,7 @@ import_commander.program.command("start", { isDefault: true }).description("Juni
|
|
|
1574
1728
|
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1575
1729
|
return;
|
|
1576
1730
|
}
|
|
1731
|
+
printBanner();
|
|
1577
1732
|
const { running, pid } = isRunning();
|
|
1578
1733
|
if (running) {
|
|
1579
1734
|
console.log(`\u2705 Junis \uC2E4\uD589 \uC911\uC785\uB2C8\uB2E4. (PID: ${pid})`);
|
|
@@ -1644,31 +1799,26 @@ import_commander.program.command("start", { isDefault: true }).description("Juni
|
|
|
1644
1799
|
console.log(" \u25C9 Status ....................... \u{1F7E2} online");
|
|
1645
1800
|
console.log("");
|
|
1646
1801
|
}
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1802
|
+
const mode = await (0, import_prompts.select)({
|
|
1803
|
+
message: "Select run mode:",
|
|
1804
|
+
choices: [
|
|
1805
|
+
{
|
|
1806
|
+
name: "Foreground",
|
|
1807
|
+
value: "foreground",
|
|
1808
|
+
description: "Runs in the current terminal. Press Ctrl+C to stop.\n Full OS access: camera, notifications, and more."
|
|
1809
|
+
},
|
|
1810
|
+
{
|
|
1811
|
+
name: "Background (daemon)",
|
|
1812
|
+
value: "background",
|
|
1813
|
+
description: "Runs as a background service. Stays alive after\n closing the terminal. Auto-starts on reboot."
|
|
1814
|
+
}
|
|
1815
|
+
]
|
|
1816
|
+
});
|
|
1817
|
+
if (mode === "foreground") {
|
|
1818
|
+
await runForeground(config, port);
|
|
1819
|
+
} else {
|
|
1820
|
+
await runBackground(config, port);
|
|
1659
1821
|
}
|
|
1660
|
-
const webUrl = process.env.JUNIS_WEB_URL ?? "https://junis.ai";
|
|
1661
|
-
console.log("");
|
|
1662
|
-
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1663
|
-
console.log(" \u2705 ALL SET \u2014 Junis\uAC00 \uBC31\uADF8\uB77C\uC6B4\uB4DC\uC5D0\uC11C \uC2E4\uD589 \uC911\uC785\uB2C8\uB2E4.");
|
|
1664
|
-
console.log(" \uBD80\uD305 \uC2DC \uC790\uB3D9\uC73C\uB85C \uC2DC\uC791\uB429\uB2C8\uB2E4.");
|
|
1665
|
-
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1666
|
-
console.log("");
|
|
1667
|
-
console.log(` \u2192 ${webUrl}`);
|
|
1668
|
-
console.log("");
|
|
1669
|
-
console.log(" \uC885\uB8CC\uD558\uB824\uBA74: npx junis stop");
|
|
1670
|
-
console.log("");
|
|
1671
|
-
process.exit(0);
|
|
1672
1822
|
});
|
|
1673
1823
|
import_commander.program.command("stop").description("\uBC31\uADF8\uB77C\uC6B4\uB4DC \uC11C\uBE44\uC2A4 \uC911\uC9C0 \uBC0F \uC790\uB3D9\uC2DC\uC791 \uD574\uC81C").action(async () => {
|
|
1674
1824
|
const stopped = stopDaemon();
|
package/dist/server/mcp.js
CHANGED
|
@@ -573,6 +573,7 @@ var NotebookTools = class {
|
|
|
573
573
|
var import_child_process3 = require("child_process");
|
|
574
574
|
var import_util3 = require("util");
|
|
575
575
|
var import_zod4 = require("zod");
|
|
576
|
+
var import_node_notifier = __toESM(require("node-notifier"));
|
|
576
577
|
var execAsync3 = (0, import_util3.promisify)(import_child_process3.exec);
|
|
577
578
|
var screenRecordPid = null;
|
|
578
579
|
function platform() {
|
|
@@ -661,20 +662,23 @@ var DeviceTools = class {
|
|
|
661
662
|
message: import_zod4.z.string().describe("\uC54C\uB9BC \uB0B4\uC6A9")
|
|
662
663
|
},
|
|
663
664
|
async ({ title, message }) => {
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
665
|
+
try {
|
|
666
|
+
await new Promise((resolve, reject) => {
|
|
667
|
+
import_node_notifier.default.notify(
|
|
668
|
+
{ title, message },
|
|
669
|
+
(err) => {
|
|
670
|
+
if (err) reject(err);
|
|
671
|
+
else resolve();
|
|
672
|
+
}
|
|
673
|
+
);
|
|
674
|
+
});
|
|
675
|
+
return { content: [{ type: "text", text: "\uC54C\uB9BC \uC804\uC1A1 \uC644\uB8CC" }] };
|
|
676
|
+
} catch (err) {
|
|
677
|
+
return {
|
|
678
|
+
content: [{ type: "text", text: `\uC54C\uB9BC \uC804\uC1A1 \uC2E4\uD328: ${err.message}` }],
|
|
679
|
+
isError: true
|
|
680
|
+
};
|
|
675
681
|
}
|
|
676
|
-
await execAsync3(cmd);
|
|
677
|
-
return { content: [{ type: "text", text: "\uC54C\uB9BC \uC804\uC1A1 \uC644\uB8CC" }] };
|
|
678
682
|
}
|
|
679
683
|
);
|
|
680
684
|
server.tool(
|
package/dist/server/stdio.js
CHANGED
|
@@ -562,6 +562,7 @@ var NotebookTools = class {
|
|
|
562
562
|
var import_child_process3 = require("child_process");
|
|
563
563
|
var import_util3 = require("util");
|
|
564
564
|
var import_zod4 = require("zod");
|
|
565
|
+
var import_node_notifier = __toESM(require("node-notifier"));
|
|
565
566
|
var execAsync3 = (0, import_util3.promisify)(import_child_process3.exec);
|
|
566
567
|
var screenRecordPid = null;
|
|
567
568
|
function platform() {
|
|
@@ -650,20 +651,23 @@ var DeviceTools = class {
|
|
|
650
651
|
message: import_zod4.z.string().describe("\uC54C\uB9BC \uB0B4\uC6A9")
|
|
651
652
|
},
|
|
652
653
|
async ({ title, message }) => {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
654
|
+
try {
|
|
655
|
+
await new Promise((resolve, reject) => {
|
|
656
|
+
import_node_notifier.default.notify(
|
|
657
|
+
{ title, message },
|
|
658
|
+
(err) => {
|
|
659
|
+
if (err) reject(err);
|
|
660
|
+
else resolve();
|
|
661
|
+
}
|
|
662
|
+
);
|
|
663
|
+
});
|
|
664
|
+
return { content: [{ type: "text", text: "\uC54C\uB9BC \uC804\uC1A1 \uC644\uB8CC" }] };
|
|
665
|
+
} catch (err) {
|
|
666
|
+
return {
|
|
667
|
+
content: [{ type: "text", text: `\uC54C\uB9BC \uC804\uC1A1 \uC2E4\uD328: ${err.message}` }],
|
|
668
|
+
isError: true
|
|
669
|
+
};
|
|
664
670
|
}
|
|
665
|
-
await execAsync3(cmd);
|
|
666
|
-
return { content: [{ type: "text", text: "\uC54C\uB9BC \uC804\uC1A1 \uC644\uB8CC" }] };
|
|
667
671
|
}
|
|
668
672
|
);
|
|
669
673
|
server.tool(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "junis",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "One-line device control for AI agents",
|
|
5
5
|
"bin": {
|
|
6
6
|
"junis": "dist/cli/index.js"
|
|
@@ -12,9 +12,11 @@
|
|
|
12
12
|
"prepublishOnly": "npm run build"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
+
"@inquirer/prompts": "^8.2.1",
|
|
15
16
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
16
17
|
"commander": "^12.0.0",
|
|
17
18
|
"glob": "^11.0.0",
|
|
19
|
+
"node-notifier": "^10.0.1",
|
|
18
20
|
"open": "^10.1.0",
|
|
19
21
|
"playwright": "^1.49.0",
|
|
20
22
|
"ws": "^8.18.0",
|
|
@@ -22,6 +24,7 @@
|
|
|
22
24
|
},
|
|
23
25
|
"devDependencies": {
|
|
24
26
|
"@types/node": "^20.0.0",
|
|
27
|
+
"@types/node-notifier": "^8.0.5",
|
|
25
28
|
"@types/ws": "^8.5.0",
|
|
26
29
|
"tsup": "^8.0.0",
|
|
27
30
|
"tsx": "^4.0.0",
|