junis 0.2.3 → 0.2.5
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 +41 -18
- package/dist/server/mcp.js +17 -13
- package/dist/server/stdio.js +17 -13
- package/package.json +3 -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.5",
|
|
35
35
|
description: "One-line device control for AI agents",
|
|
36
36
|
bin: {
|
|
37
37
|
junis: "dist/cli/index.js"
|
|
@@ -46,6 +46,7 @@ var require_package = __commonJS({
|
|
|
46
46
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
47
47
|
commander: "^12.0.0",
|
|
48
48
|
glob: "^11.0.0",
|
|
49
|
+
"node-notifier": "^10.0.1",
|
|
49
50
|
open: "^10.1.0",
|
|
50
51
|
playwright: "^1.49.0",
|
|
51
52
|
ws: "^8.18.0",
|
|
@@ -53,6 +54,7 @@ var require_package = __commonJS({
|
|
|
53
54
|
},
|
|
54
55
|
devDependencies: {
|
|
55
56
|
"@types/node": "^20.0.0",
|
|
57
|
+
"@types/node-notifier": "^8.0.5",
|
|
56
58
|
"@types/ws": "^8.5.0",
|
|
57
59
|
tsup: "^8.0.0",
|
|
58
60
|
tsx: "^4.0.0",
|
|
@@ -836,6 +838,7 @@ var NotebookTools = class {
|
|
|
836
838
|
var import_child_process3 = require("child_process");
|
|
837
839
|
var import_util3 = require("util");
|
|
838
840
|
var import_zod4 = require("zod");
|
|
841
|
+
var import_node_notifier = __toESM(require("node-notifier"));
|
|
839
842
|
var execAsync3 = (0, import_util3.promisify)(import_child_process3.exec);
|
|
840
843
|
var screenRecordPid = null;
|
|
841
844
|
function platform() {
|
|
@@ -924,20 +927,23 @@ var DeviceTools = class {
|
|
|
924
927
|
message: import_zod4.z.string().describe("\uC54C\uB9BC \uB0B4\uC6A9")
|
|
925
928
|
},
|
|
926
929
|
async ({ title, message }) => {
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
930
|
+
try {
|
|
931
|
+
await new Promise((resolve, reject) => {
|
|
932
|
+
import_node_notifier.default.notify(
|
|
933
|
+
{ title, message },
|
|
934
|
+
(err) => {
|
|
935
|
+
if (err) reject(err);
|
|
936
|
+
else resolve();
|
|
937
|
+
}
|
|
938
|
+
);
|
|
939
|
+
});
|
|
940
|
+
return { content: [{ type: "text", text: "\uC54C\uB9BC \uC804\uC1A1 \uC644\uB8CC" }] };
|
|
941
|
+
} catch (err) {
|
|
942
|
+
return {
|
|
943
|
+
content: [{ type: "text", text: `\uC54C\uB9BC \uC804\uC1A1 \uC2E4\uD328: ${err.message}` }],
|
|
944
|
+
isError: true
|
|
945
|
+
};
|
|
938
946
|
}
|
|
939
|
-
await execAsync3(cmd);
|
|
940
|
-
return { content: [{ type: "text", text: "\uC54C\uB9BC \uC804\uC1A1 \uC644\uB8CC" }] };
|
|
941
947
|
}
|
|
942
948
|
);
|
|
943
949
|
server.tool(
|
|
@@ -1339,7 +1345,9 @@ function startDaemon(port) {
|
|
|
1339
1345
|
const err = import_fs2.default.openSync(LOG_FILE, "a");
|
|
1340
1346
|
const child = (0, import_child_process4.spawn)(nodePath, [scriptPath, "start", "--daemon", "--port", String(port)], {
|
|
1341
1347
|
detached: true,
|
|
1342
|
-
stdio: ["ignore", out, err]
|
|
1348
|
+
stdio: ["ignore", out, err],
|
|
1349
|
+
env: { ...process.env }
|
|
1350
|
+
// JUNIS_API_URL, JUNIS_WS_URL 등 현재 env 상속
|
|
1343
1351
|
});
|
|
1344
1352
|
child.unref();
|
|
1345
1353
|
const pid = child.pid;
|
|
@@ -1388,6 +1396,12 @@ var ServiceManager = class {
|
|
|
1388
1396
|
<string>${import_os2.default.homedir()}</string>
|
|
1389
1397
|
<key>PATH</key>
|
|
1390
1398
|
<string>${process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin"}</string>
|
|
1399
|
+
${process.env.JUNIS_API_URL ? `<key>JUNIS_API_URL</key>
|
|
1400
|
+
<string>${process.env.JUNIS_API_URL}</string>` : ""}
|
|
1401
|
+
${process.env.JUNIS_WS_URL ? `<key>JUNIS_WS_URL</key>
|
|
1402
|
+
<string>${process.env.JUNIS_WS_URL}</string>` : ""}
|
|
1403
|
+
${process.env.JUNIS_WEB_URL ? `<key>JUNIS_WEB_URL</key>
|
|
1404
|
+
<string>${process.env.JUNIS_WEB_URL}</string>` : ""}
|
|
1391
1405
|
</dict>
|
|
1392
1406
|
<key>RunAtLoad</key>
|
|
1393
1407
|
<true/>
|
|
@@ -1419,6 +1433,9 @@ Restart=always
|
|
|
1419
1433
|
RestartSec=5
|
|
1420
1434
|
Environment=HOME=${import_os2.default.homedir()}
|
|
1421
1435
|
Environment=PATH=${process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin"}
|
|
1436
|
+
${process.env.JUNIS_API_URL ? `Environment=JUNIS_API_URL=${process.env.JUNIS_API_URL}` : ""}
|
|
1437
|
+
${process.env.JUNIS_WS_URL ? `Environment=JUNIS_WS_URL=${process.env.JUNIS_WS_URL}` : ""}
|
|
1438
|
+
${process.env.JUNIS_WEB_URL ? `Environment=JUNIS_WEB_URL=${process.env.JUNIS_WEB_URL}` : ""}
|
|
1422
1439
|
StandardOutput=append:${LOG_FILE}
|
|
1423
1440
|
StandardError=append:${LOG_FILE}
|
|
1424
1441
|
|
|
@@ -1514,6 +1531,7 @@ function printStep1(port) {
|
|
|
1514
1531
|
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)").action(async (options) => {
|
|
1515
1532
|
const port = parseInt(options.port, 10);
|
|
1516
1533
|
if (options.daemon) {
|
|
1534
|
+
writePid(process.pid);
|
|
1517
1535
|
if (options.local) {
|
|
1518
1536
|
await startMCPServer(port);
|
|
1519
1537
|
return;
|
|
@@ -1661,13 +1679,18 @@ import_commander.program.command("start", { isDefault: true }).description("Juni
|
|
|
1661
1679
|
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 () => {
|
|
1662
1680
|
const stopped = stopDaemon();
|
|
1663
1681
|
const svc = new ServiceManager();
|
|
1664
|
-
|
|
1665
|
-
|
|
1682
|
+
let serviceUninstalled = false;
|
|
1683
|
+
try {
|
|
1684
|
+
await svc.uninstall();
|
|
1685
|
+
serviceUninstalled = true;
|
|
1686
|
+
} catch {
|
|
1687
|
+
}
|
|
1688
|
+
if (stopped || serviceUninstalled) {
|
|
1666
1689
|
console.log("\u2705 Junis \uC11C\uBE44\uC2A4\uAC00 \uC911\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
1690
|
+
console.log(" \uC790\uB3D9\uC2DC\uC791\uC774 \uD574\uC81C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
1667
1691
|
} else {
|
|
1668
1692
|
console.log("\u2139\uFE0F \uC2E4\uD589 \uC911\uC778 Junis \uD504\uB85C\uC138\uC2A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1669
1693
|
}
|
|
1670
|
-
console.log(" \uC790\uB3D9\uC2DC\uC791\uC774 \uD574\uC81C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
1671
1694
|
});
|
|
1672
1695
|
import_commander.program.command("logout").description("\uC778\uC99D \uC815\uBCF4 \uC0AD\uC81C").action(() => {
|
|
1673
1696
|
clearConfig();
|
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.5",
|
|
4
4
|
"description": "One-line device control for AI agents",
|
|
5
5
|
"bin": {
|
|
6
6
|
"junis": "dist/cli/index.js"
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
16
16
|
"commander": "^12.0.0",
|
|
17
17
|
"glob": "^11.0.0",
|
|
18
|
+
"node-notifier": "^10.0.1",
|
|
18
19
|
"open": "^10.1.0",
|
|
19
20
|
"playwright": "^1.49.0",
|
|
20
21
|
"ws": "^8.18.0",
|
|
@@ -22,6 +23,7 @@
|
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@types/node": "^20.0.0",
|
|
26
|
+
"@types/node-notifier": "^8.0.5",
|
|
25
27
|
"@types/ws": "^8.5.0",
|
|
26
28
|
"tsup": "^8.0.0",
|
|
27
29
|
"tsx": "^4.0.0",
|