@modootoday/datalab-extension-mcp 1.8.21 → 1.8.23
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.en.md +18 -17
- package/README.md +38 -38
- package/dist/{browsers-JLTAY3ZC.js → browsers-T6B6IUYR.js} +29 -19
- package/dist/{chunk-WCKI5Z4D.js → chunk-EGSZC33X.js} +6 -1
- package/dist/{chunk-VPDNMKVF.js → chunk-KE5ZJQKG.js} +1 -1
- package/dist/{chunk-5ETPLZ62.js → chunk-MHRCW2YZ.js} +192 -29
- package/dist/cli.js +7 -7
- package/dist/{dist-OJ5WOLYS.js → dist-VANK7DDE.js} +1 -1
- package/dist/index.js +2 -2
- package/dist/{skills-ACWHUGX7.js → skills-5JDMNON3.js} +2 -2
- package/package.json +1 -1
- package/skills/datalab-reader-simulation/SKILL.md +1 -1
- package/skills/datalab-social-repurpose/SKILL.md +1 -1
- package/skills/datalab-video-script/SKILL.md +1 -1
package/README.en.md
CHANGED
|
@@ -61,7 +61,7 @@ One command finds **every** AI app on your computer and connects them automatica
|
|
|
61
61
|
2. **Paste this command and press Enter.** The saved command names an exact bootstrap version. At startup and while idle, the connector checks the canonical release and promotes itself to the current secure, compatible version. Diagnostic environments can opt out with `--no-self-update`.
|
|
62
62
|
|
|
63
63
|
```
|
|
64
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
64
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 install
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
Lines of English scrolling by is normal. This only downloads and runs the connector — it touches nothing else.
|
|
@@ -145,10 +145,10 @@ Registration is once per browser. The connection itself lives only while the sid
|
|
|
145
145
|
Installing also places the bundled **writing skills**. An AI app reads the name and description of every installed skill on every start, so the more are installed the less room the conversation has. The default therefore places only the ones that work right away.
|
|
146
146
|
|
|
147
147
|
```
|
|
148
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
149
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
150
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
151
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
148
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 skills list
|
|
149
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 skills status
|
|
150
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 skills attach <name>
|
|
151
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 skills detach <name>
|
|
152
152
|
```
|
|
153
153
|
|
|
154
154
|
- `list` shows everything in this version, name and description.
|
|
@@ -167,10 +167,10 @@ The **workbench** is this computer, where the connector runs. If you use more th
|
|
|
167
167
|
Make the code in extension settings → **다른 AI 앱에 연결 (MCP)** → expand **[다른 브라우저 추가 (선택)]** (add another browser, optional) below box 3 → [등록 코드 만들기]. Or from the terminal.
|
|
168
168
|
|
|
169
169
|
```
|
|
170
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
171
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
172
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
173
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
170
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 browsers add "Work Chrome"
|
|
171
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 browsers list
|
|
172
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 browsers revoke <handle>
|
|
173
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 browsers repair
|
|
174
174
|
```
|
|
175
175
|
|
|
176
176
|
`add` **asks you to paste the install key** — the same value you used to
|
|
@@ -181,8 +181,9 @@ Open the other browser → extension settings → **다른 AI 앱에 연결 (MCP
|
|
|
181
181
|
that code into **3. 이 브라우저 등록** (register this browser). That browser does
|
|
182
182
|
not make a code of its own.
|
|
183
183
|
|
|
184
|
-
- `list` separates
|
|
185
|
-
|
|
184
|
+
- `list` separates `코드 대기` (a code exists, nobody has used it),
|
|
185
|
+
`연결됨` (connected) and `연결 끊김` (detached). The CLI speaks Korean —
|
|
186
|
+
this file is the derived English guide.
|
|
186
187
|
- After `revoke`, that browser cannot rejoin. To let it back in, **revoke and
|
|
187
188
|
then `add` again** — handing the same browser a new code alone is refused.
|
|
188
189
|
- If `list` asks for registry repair, run `repair`, then issue a fresh registration
|
|
@@ -208,15 +209,15 @@ Shown when MCP is switched on but **no Naver page is open anywhere**. In that st
|
|
|
208
209
|
the connector never starts, so starting your AI app will not connect it. Leave one
|
|
209
210
|
Naver tab open (a blog, SmartPlace, …) and it connects on its own.
|
|
210
211
|
|
|
211
|
-
### "
|
|
212
|
+
### "연결 프로그램이 예전 버전이에요. 새 설정을 AI 앱 설정에 붙여넣고…"
|
|
212
213
|
|
|
213
|
-
_(The connector is an older version.
|
|
214
|
+
_(The connector is an older version. Paste the new setup into your AI app, then fully restart it.)_
|
|
214
215
|
|
|
215
216
|
Press **[새 설정 복사]** (copy new setup) on the card, paste as instructed, then fully quit and restart your AI app.
|
|
216
217
|
|
|
217
|
-
### "AI 앱이 예전
|
|
218
|
+
### "AI 앱이 예전 설치 인증키를 쓰고 있어요…"
|
|
218
219
|
|
|
219
|
-
_(Your AI app is using an old
|
|
220
|
+
_(Your AI app is using an old install key.)_
|
|
220
221
|
|
|
221
222
|
The connector is running with the code its AI app handed it at startup. **[다시 연결하기] (reconnect) will not help here** — the other side keeps presenting that same old code.
|
|
222
223
|
|
|
@@ -271,7 +272,7 @@ Start it again after a full quit and it picks the tools up. If they still aren't
|
|
|
271
272
|
- **When the connection disappears** — run the very command you used at the start once more; it safely re-registers. (Some AI apps clean up their settings file and drop the connection.)
|
|
272
273
|
|
|
273
274
|
```
|
|
274
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
275
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 install
|
|
275
276
|
```
|
|
276
277
|
|
|
277
278
|
- **`'npx' is not recognized...`** — Node.js isn't installed. The "No Node.js?" one-liner in Step 2 is the simplest fix.
|
|
@@ -301,7 +302,7 @@ Start it again after a full quit and it picks the tools up. If they still aren't
|
|
|
301
302
|
1. Run this in a terminal (it removes the entries registered in your AI apps).
|
|
302
303
|
|
|
303
304
|
```
|
|
304
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
305
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 uninstall
|
|
305
306
|
```
|
|
306
307
|
|
|
307
308
|
2. Turn **MCP 연결** off in the extension settings. The panel is the bridge, so with it off a leftover config line can look nothing up.
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Claude 같은 AI 앱을 데이터랩툴즈에 연결해 주는 작은 무료 프
|
|
|
16
16
|
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
17
17
|
| Chrome + 데이터랩툴즈 확장 | 아직 없다면 [app.datalab.tools](https://app.datalab.tools/)에서 설치해요. |
|
|
18
18
|
| AI 앱 | 질문을 받을 프로그램이에요. **컴퓨터에 설치하는 앱**만 돼요(웹사이트는 안 돼요). Claude Desktop은 [claude.ai](https://claude.ai/download)에서 무료로, ChatGPT는 **데스크톱 앱만** 되고 [openai.com](https://openai.com/)에서 받아요. |
|
|
19
|
-
| Node.js |
|
|
19
|
+
| Node.js | 연결 프로그램을 실행하는 무료 프로그램이에요. [nodejs.org](https://nodejs.org/)의 **LTS** 버튼으로 받아요. 번거로우면 아래 [2단계](#2단계--설치)의 한 줄 설치가 Node.js까지 한 번에 깔아 줘요(관리자 권한도 필요 없어요). |
|
|
20
20
|
|
|
21
21
|
> 회사 컴퓨터는 보안 정책 때문에 설치가 막혀 있을 수 있어요. 시작 전에 회사 IT 담당자와 먼저 상의하면 시간을 아낄 수 있어요.
|
|
22
22
|
|
|
@@ -45,20 +45,20 @@ Claude 같은 AI 앱을 데이터랩툴즈에 연결해 주는 작은 무료 프
|
|
|
45
45
|
|
|
46
46
|
> "이 값은 이 컴퓨터 전용 비밀번호예요. 다른 사람에게 보내지 마세요."
|
|
47
47
|
|
|
48
|
-
캡처 공유·채팅 전송은 피해 주세요. 인증키는 AI 앱 설정과
|
|
48
|
+
캡처 공유·채팅 전송은 피해 주세요. 인증키는 AI 앱 설정과 등록된 브라우저 관리에만 씁니다. **3번의 등록 코드와는 다른 값**이라 등록 코드 칸에 붙여넣으면 안 돼요 — 브라우저마다 별도 등록 코드가 발급되고, 그 코드는 만든 직후 한 번만 보여요.
|
|
49
49
|
|
|
50
50
|
## 2단계 — 설치
|
|
51
51
|
|
|
52
52
|
명령 한 줄이면 컴퓨터에 있는 AI 앱을 **모두 자동으로** 찾아 연결해 줘요. 앱마다 따로 할 일은 없어요.
|
|
53
53
|
|
|
54
54
|
1. **터미널을 열어요** — Windows는 검색창에 `cmd` → "명령 프롬프트", Mac은 `⌘+스페이스`로 `터미널`, Linux는 터미널 앱(보통 `Ctrl+Alt+T`)이에요.
|
|
55
|
-
2. **아래 명령을 붙여넣고 Enter를 눌러요.** 저장되는 명령은 정확한 시작 버전을 적습니다.
|
|
55
|
+
2. **아래 명령을 붙여넣고 Enter를 눌러요.** 저장되는 명령은 정확한 시작 버전을 적습니다. 연결 프로그램은 시작할 때와 유휴 상태에서 정본 릴리스를 확인해 현재의 안전하고 호환되는 버전으로 승격합니다. 진단 환경에서는 `--no-self-update`로 승격을 끌 수 있어요.
|
|
56
56
|
|
|
57
57
|
```
|
|
58
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
58
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 install
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
영어 글자가 여러 줄 올라오는 건 정상이에요. 이 명령은
|
|
61
|
+
영어 글자가 여러 줄 올라오는 건 정상이에요. 이 명령은 연결 프로그램을 다운로드해 실행할 뿐, 다른 건 건드리지 않아요.
|
|
62
62
|
|
|
63
63
|
도우미는 네 대목으로 진행돼요 — `[1/4] 설치 인증키 확인`, `[2/4] 연결할 AI 프로그램 찾기`, `[3/4] 연결하는 중`, `[4/4] 결과`.
|
|
64
64
|
|
|
@@ -113,7 +113,7 @@ Claude 같은 AI 앱을 데이터랩툴즈에 연결해 주는 작은 무료 프
|
|
|
113
113
|
|
|
114
114
|
- **[이 브라우저 연결하기]** 버튼이 보이면 그것만 누르면 끝이에요. 등록 코드를 만들어 붙여넣는 과정을 화면이 대신 해 줘요.
|
|
115
115
|
- **"등록됨"** 이 이미 떠 있으면 할 일이 없어요.
|
|
116
|
-
- 이 컴퓨터의 **다른 브라우저에서 이미 설치**했다면 버튼 대신 등록 코드 칸이 나와요. 그 브라우저에서 만든 코드를 넣으세요(아래 [
|
|
116
|
+
- 이 컴퓨터의 **다른 브라우저에서 이미 설치**했다면 버튼 대신 등록 코드 칸이 나와요. 그 브라우저에서 만든 코드를 넣으세요(아래 [이 컴퓨터에 크롬 하나 더 올리기](#이-컴퓨터에-크롬-하나-더-올리기)).
|
|
117
117
|
|
|
118
118
|
등록은 브라우저마다 한 번이에요. 실제 연결은 사이드 패널을 열어 둔 동안 이루어져요.
|
|
119
119
|
|
|
@@ -125,18 +125,18 @@ Claude 같은 AI 앱을 데이터랩툴즈에 연결해 주는 작은 무료 프
|
|
|
125
125
|
## 알아 두면 좋은 것
|
|
126
126
|
|
|
127
127
|
- **패널이 열려 있는 동안에만 연결돼요.** 데이터랩툴즈 사이드 패널을 닫으면 연결도 함께 끊겨요. 다시 열면 자동으로 이어져요.
|
|
128
|
-
-
|
|
129
|
-
- 확장이 업데이트되면 새 기능이 자동으로 AI 앱에 나타나요.
|
|
128
|
+
- 연결 프로그램은 다리일 뿐이에요 — 아이디·비밀번호를 저장하지 않고, 내 컴퓨터 밖에서는 동작하지 않아요.
|
|
129
|
+
- 확장이 업데이트되면 새 기능이 자동으로 AI 앱에 나타나요. 연결 프로그램 자체는 그대로예요.
|
|
130
130
|
|
|
131
131
|
## 설치된 글쓰기 도움말 고르기
|
|
132
132
|
|
|
133
133
|
설치할 때 **글쓰기 도움말(스킬)** 도 함께 놓입니다. AI 앱은 켜질 때마다 설치된 도움말의 이름과 설명을 전부 읽기 때문에, 많이 깔수록 대화에 쓸 자리가 줄어들어요. 그래서 기본은 **바로 쓸 수 있는 것만** 놓습니다.
|
|
134
134
|
|
|
135
135
|
```
|
|
136
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
137
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
138
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
139
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
136
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 skills list
|
|
137
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 skills status
|
|
138
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 skills attach <이름>
|
|
139
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 skills detach <이름>
|
|
140
140
|
```
|
|
141
141
|
|
|
142
142
|
- `list` 는 이 버전에 들어 있는 전부를 이름과 설명으로 보여 줘요.
|
|
@@ -148,31 +148,31 @@ npx -y @modootoday/datalab-extension-mcp@1.8.21 skills detach <이름>
|
|
|
148
148
|
|
|
149
149
|
다른 도구로 설치한 같은 이름의 도움말이 있으면 **건드리지 않고 그렇게 말해요.** 뺄 때도 우리가 넣은 파일만 지웁니다.
|
|
150
150
|
|
|
151
|
-
##
|
|
151
|
+
## 이 컴퓨터에 크롬 하나 더 올리기
|
|
152
152
|
|
|
153
|
-
|
|
153
|
+
등록은 **연결 프로그램이 도는 이 컴퓨터**를 기준으로 해요. 크롬 프로필을 여러 개 쓰신다면 프로필마다 등록 코드를 하나씩 만들어 같은 컴퓨터에 올릴 수 있어요. **한 컴퓨터 안에서만** 됩니다 — 연결 프로그램은 127.0.0.1 에서만 듣기 때문에 다른 컴퓨터는 닿지 못하고, 코드는 남에게 보낼 것이 아니에요.
|
|
154
154
|
|
|
155
155
|
확장 설정 → **다른 AI 앱에 연결 (MCP)** → 3번 아래의 **[다른 브라우저 추가 (선택)]** 를 펼쳐 [등록 코드 만들기]를 눌러도 되고, 터미널을 쓰셔도 돼요.
|
|
156
156
|
|
|
157
157
|
```
|
|
158
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
159
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
160
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
161
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
158
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 browsers add "회사 크롬"
|
|
159
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 browsers list
|
|
160
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 browsers revoke <handle>
|
|
161
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 browsers repair
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
-
`add` 를 실행하면 **설치 인증키를 붙여넣으라고 물어봐요** — 설치할 때와 같은 값이고, 설정 화면 1번의 `복사` 버튼으로 얻습니다. 그러면 브라우저 등록 코드가 **한 번만** 출력돼요.
|
|
164
|
+
`add` 를 실행하면 **설치 인증키를 붙여넣으라고 물어봐요** — 설치할 때와 같은 값이고, 설정 화면 1번의 `복사` 버튼으로 얻습니다. 그러면 브라우저 등록 코드가 **한 번만** 출력돼요. 연결 프로그램은 그 코드를 저장하지 않고 지문만 남기므로 다시 보여 줄 수 없어요.
|
|
165
165
|
|
|
166
166
|
만든 코드는 올릴 크롬을 열고 → 확장 설정 → **다른 AI 앱에 연결 (MCP)** → **3. 이 브라우저 등록** 칸에 붙여넣습니다. 그 브라우저는 자기 코드를 따로 만들지 않아요.
|
|
167
167
|
|
|
168
|
-
- `list` 는
|
|
168
|
+
- `list` 는 `코드 대기`(코드는 만들었지만 아직 아무도 안 붙음) · `연결됨` · `연결 끊김` 을 구분해서 보여 줘요.
|
|
169
169
|
- `revoke` 뒤에는 그 브라우저가 다시 붙지 못해요. 다시 넣으려면 **`revoke` 한 뒤 새로 `add`** 하세요 — 같은 브라우저에 코드만 새로 줘도 거절돼요.
|
|
170
170
|
- `list` 가 등록부 복구를 안내하면 `repair`를 실행한 뒤, 영향받은 브라우저에 등록 코드를 새로 발급해 붙여넣으세요. 정상 등록은 유지됩니다.
|
|
171
|
-
-
|
|
171
|
+
- 연결 프로그램이 꺼져 있으면 이 명령은 아무것도 하지 않고 그렇게 말해요. 연결해 둔 AI 앱을 켜면 연결 프로그램이 함께 살아납니다.
|
|
172
172
|
|
|
173
173
|
## 문제해결
|
|
174
174
|
|
|
175
|
-
무슨 일이든 첫 질문은 하나예요 — **패널이 열려 있나요?** 카드 상태가 "문제 발생"이면 그 아래 빨간 문장을 아래 소제목에서 찾아보세요. 여기 없는 문장이 떴다면
|
|
175
|
+
무슨 일이든 첫 질문은 하나예요 — **패널이 열려 있나요?** 카드 상태가 "문제 발생"이면 그 아래 빨간 문장을 아래 소제목에서 찾아보세요. 여기 없는 문장이 떴다면 연결 프로그램이 그대로 전해 준 오류예요 — 아래 "위 어느 것도 아닐 때" 의 기록 파일을 보시면 돼요.
|
|
176
176
|
|
|
177
177
|
### "연결을 기다리고 있어요. AI 앱을 켜면 자동으로 연결돼요."
|
|
178
178
|
|
|
@@ -181,26 +181,26 @@ npx -y @modootoday/datalab-extension-mcp@1.8.21 browsers repair
|
|
|
181
181
|
### "네이버 페이지를 열면 연결돼요. MCP 는 네이버 탭이 열려 있는 동안에만 동작해요."
|
|
182
182
|
|
|
183
183
|
MCP 를 켜 두었지만 **네이버 페이지가 한 곳도 열려 있지 않을 때** 나와요. 이 상태에서는
|
|
184
|
-
|
|
184
|
+
연결 프로그램 자체가 시작되지 않기 때문에, AI 앱을 켜도 연결되지 않아요. 네이버 블로그나
|
|
185
185
|
스마트플레이스 같은 페이지를 한 탭 열어 두면 자동으로 연결돼요.
|
|
186
186
|
|
|
187
|
-
### "
|
|
187
|
+
### "연결 프로그램이 예전 버전이에요. 새 설정을 AI 앱 설정에 붙여넣고…"
|
|
188
188
|
|
|
189
189
|
카드의 **[새 설정 복사]** 버튼을 누르고 안내대로 붙여넣은 뒤, AI 앱을 완전히 껐다가 다시 켜요.
|
|
190
190
|
|
|
191
|
-
### "AI 앱이 예전
|
|
191
|
+
### "AI 앱이 예전 설치 인증키를 쓰고 있어요…"
|
|
192
192
|
|
|
193
|
-
AI 앱이 켜질 때 받은 옛
|
|
193
|
+
AI 앱이 켜질 때 받은 옛 설치 인증키로 연결 프로그램이 돌고 있어서 그래요. **여기서 [다시 연결하기]는 도움이 안 돼요** — 상대가 계속 같은 옛 코드를 내밀거든요.
|
|
194
194
|
|
|
195
195
|
1. 카드의 **[새 설정 복사]** 를 누르고
|
|
196
196
|
2. 그 내용을 AI 앱 설정에 붙여넣은 뒤
|
|
197
197
|
3. **그 AI 앱을 완전히 종료했다가 다시 켜요** (창 닫기 말고 — Windows 트레이 종료 / Mac `⌘+Q` / Linux 앱 메뉴 Quit)
|
|
198
198
|
|
|
199
|
-
3번까지 해야
|
|
199
|
+
3번까지 해야 연결 프로그램이 AI 앱 설정에 저장된 설치 인증키로 다시 떠요. 브라우저 등록 코드는 이 과정에서 바꾸지 않습니다.
|
|
200
200
|
|
|
201
201
|
### "연결 통로(포트)를 다른 프로그램이 쓰고 있어요."
|
|
202
202
|
|
|
203
|
-
카드의 **[업데이트 명령어 복사]** 버튼을 눌러 터미널에 붙여넣어 실행하면
|
|
203
|
+
카드의 **[업데이트 명령어 복사]** 버튼을 눌러 터미널에 붙여넣어 실행하면 연결 프로그램이 새 설정으로 다시 연결돼요.
|
|
204
204
|
|
|
205
205
|
### "크롬에서 데이터랩툴즈 패널이 닫혀 있어요. 크롬을 열고 데이터랩툴즈 패널을 열어 두면 바로 동작해요."
|
|
206
206
|
|
|
@@ -237,12 +237,12 @@ AI가 "그런 기능은 없다" 고 답하면, **"도구 목록을 확인해 보
|
|
|
237
237
|
- **연결이 사라졌을 때** — 처음 실행했던 그 명령을 한 번 더 실행하면 안전하게 다시 등록돼요. (일부 AI 앱이 설정 파일을 정리하며 연결을 지우기도 해요.)
|
|
238
238
|
|
|
239
239
|
```
|
|
240
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
240
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 install
|
|
241
241
|
```
|
|
242
242
|
|
|
243
243
|
- **`'npx'을(는) 내부 또는 외부 명령...`** — Node.js가 없는 거예요. 위 2단계의 "Node.js가 없다면" 한 줄 설치가 가장 간단해요.
|
|
244
244
|
|
|
245
|
-
- **위 어느 것도 아닐 때** —
|
|
245
|
+
- **위 어느 것도 아닐 때** — 연결 프로그램이 무슨 일을 겪었는지 기록해 둬요. 파일 하나만 열어 보면 돼요.
|
|
246
246
|
|
|
247
247
|
```
|
|
248
248
|
~/.datalab-mcp/connector.log
|
|
@@ -259,27 +259,27 @@ AI가 "그런 기능은 없다" 고 답하면, **"도구 목록을 확인해 보
|
|
|
259
259
|
- **아이디·비밀번호·쿠키를 저장하지 않아요.** 네이버에 직접 접속하지도 않아요. 모든 조회는 내가 로그인해 둔 내 브라우저 안에서 일어나요.
|
|
260
260
|
- **내 컴퓨터 안에서만 동작해요.** 밖에서는 접속할 수 없고, 설치 인증키와 확장 확인이라는 이중 잠금이 걸려 있어요.
|
|
261
261
|
- **데이터가 나가는 방식** — "연결된 AI 앱이 요청하면 네이버 데이터가 해당 앱과 그 AI 제공사 서버로 전달돼요." 이게 AI가 답하는 방식이라 처음 켤 때 미리 동의를 받아요. 전달된 데이터를 얼마나 보관·학습하는지는 각 AI 제공사 정책을 따르니, 쓰시는 AI 앱의 개인정보 정책을 확인해 주세요.
|
|
262
|
-
- **설정에는 정확한 버전이 기록되고, 실행 시 안전한 최신 버전을 확인해요.**
|
|
263
|
-
- **소스가 전부 공개된 오픈소스예요.** 다만 이미 같은 권한으로 실행 중인 악성코드까지 막아 주지는 못해요 — 이
|
|
262
|
+
- **설정에는 정확한 버전이 기록되고, 실행 시 안전한 최신 버전을 확인해요.** 연결 프로그램은 시작할 때와 유휴 상태에서 정본 버전을 확인해 최신 보안·호환 버전으로 승격합니다. 특정 버전을 유지해야 하는 진단 환경은 `--no-self-update`를 사용할 수 있어요.
|
|
263
|
+
- **소스가 전부 공개된 오픈소스예요.** 다만 이미 같은 권한으로 실행 중인 악성코드까지 막아 주지는 못해요 — 이 연결 프로그램의 잠금은 원격·타 출처 접근을 막는 장치예요.
|
|
264
264
|
|
|
265
265
|
## 해지
|
|
266
266
|
|
|
267
267
|
1. 터미널에서 아래 명령을 실행해요(AI 앱에 등록된 설정을 지워 줘요).
|
|
268
268
|
|
|
269
269
|
```
|
|
270
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
270
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.23 uninstall
|
|
271
271
|
```
|
|
272
272
|
|
|
273
273
|
2. 확장 설정에서 **MCP 연결을 꺼요.** 패널이 브릿지라, 꺼 두면 어딘가 설정 줄이 남아 있어도 AI 앱이 아무것도 조회할 수 없어요.
|
|
274
274
|
|
|
275
|
-
확장을 삭제해도 남은
|
|
275
|
+
확장을 삭제해도 남은 연결 프로그램은 즉시 무력해져요. 설정에 남은 한 줄은 위 명령으로 지울 수 있고, 안 지워도 아무 일도 하지 않아요.
|
|
276
276
|
|
|
277
277
|
## 자주 묻는 질문
|
|
278
278
|
|
|
279
279
|
<details>
|
|
280
280
|
<summary>유료인가요 / ChatGPT 웹 / 항상 켜 두기 / 어떤 데이터 / 다른 컴퓨터</summary>
|
|
281
281
|
|
|
282
|
-
- **유료인가요?**
|
|
282
|
+
- **유료인가요?** 연결 프로그램 자체는 무료이고 소스가 전부 공개된 오픈소스예요. 다만 AI로 사진을 만들거나 영상에 목소리를 넣는 것처럼 **요금이 드는 기능이 몇 가지 있고**, 그건 실행하기 전에 매번 물어봐요 — 물어보지 않고 요금이 나가는 일은 없어요.
|
|
283
283
|
- **ChatGPT 웹사이트에서도 되나요?** 아니요. 컴퓨터에 설치한 데스크톱 앱만 연결돼요.
|
|
284
284
|
- **항상 켜 둬야 하나요?** 아니요. 패널이 열려 있을 때만 연결돼요.
|
|
285
285
|
- **어떤 데이터를 읽나요?** 블로그 통계·키워드·광고 등 데이터랩툴즈가 보여 주는 것들이에요. 기준은 지금 Chrome에 로그인해 둔 네이버 계정이에요.
|
|
@@ -303,13 +303,13 @@ AI가 "그런 기능은 없다" 고 답하면, **"도구 목록을 확인해 보
|
|
|
303
303
|
| `DATALAB_MCP_PORT` | 선택 | `8765` | 수신 포트. |
|
|
304
304
|
| `DATALAB_MCP_HOST` | 선택 | `127.0.0.1` | 바인드 주소. loopback 외 값은 거부. |
|
|
305
305
|
|
|
306
|
-
**호출 한도** —
|
|
306
|
+
**호출 한도** — 연결 프로그램 하나가 `10`회까지 몰아 부를 수 있고 초당 `1`회씩 다시 찹니다. 브라우저를 여러 대 연결했다면 그 예산을 **함께 씁니다**. 요금 제한이 아니라 폭주 루프 완충용이라 평범한 사용에서는 닿지 않습니다.
|
|
307
307
|
|
|
308
308
|
**실패 응답** — 도구 호출 실패는 프로토콜 오류가 아니라 `isError` 결과로 오고, 본문은 성공과 같은 JSON입니다(`ok` / `reason` / `message` / `retryable`, 기다리면 풀리는 경우 `retryAfterMs`). 실패 코드 전체 목록과 각각의 재시도 여부 정본: <https://github.com/modootoday/datalab-extension-mcp/blob/main/mcp-core/src/errors.ts>
|
|
309
309
|
|
|
310
|
-
**아키텍처** —
|
|
310
|
+
**아키텍처** — 연결 프로그램은 자격증명도 egress도 없는 릴레이입니다. 도구 목록은 확장이 서빙하며 카탈로그 정본은 스토어 심사를 받는 확장 코드입니다. 전송은 루프백 HTTP+SSE이며 Origin 검증과 브라우저별 세션 검증을 거치고, 도구 등급 표(조회 / 편집 / 파괴적 / 유료 / 미개방)는 확장 쪽에서 집행됩니다 — 목록에 없는 이름은 거부되고, 파괴적·유료는 세션 허용으로 건너뛸 수 없습니다. allowlist 전문(제외 사유 포함)의 정본: <https://github.com/modootoday/datalab-extension-mcp/blob/main/mcp-core/src/allowlist.ts>
|
|
311
311
|
|
|
312
|
-
**공급망** — 공개 저장소가 전체 소스입니다. npm 발행은 OIDC trusted publishing + provenance로 이루어지고 장기 발행 토큰은 쓰지 않습니다(`npm audit signatures`로 검증 가능). 설치 설정은 정확한 bootstrap 버전을 기록하고,
|
|
312
|
+
**공급망** — 공개 저장소가 전체 소스입니다. npm 발행은 OIDC trusted publishing + provenance로 이루어지고 장기 발행 토큰은 쓰지 않습니다(`npm audit signatures`로 검증 가능). 설치 설정은 정확한 bootstrap 버전을 기록하고, 연결 프로그램은 정본 릴리스로 승격할 때도 같은 검증된 npm 패키지 경로를 사용합니다.
|
|
313
313
|
|
|
314
314
|
**라이선스** — MIT ([LICENSE](./LICENSE)).
|
|
315
315
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_HOST,
|
|
3
3
|
DEFAULT_PORT
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-EGSZC33X.js";
|
|
5
5
|
import {
|
|
6
6
|
TOKEN_PROMPT_GUIDE,
|
|
7
7
|
TOKEN_PROMPT_QUESTION,
|
|
8
8
|
TOKEN_REQUIRED_NON_INTERACTIVE
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MHRCW2YZ.js";
|
|
10
10
|
import "./chunk-MLKGABMK.js";
|
|
11
11
|
|
|
12
12
|
// src/browsers.ts
|
|
@@ -21,6 +21,11 @@ function withoutFlag(argv, flag) {
|
|
|
21
21
|
if (at === -1) return [...argv];
|
|
22
22
|
return [...argv.slice(0, at), ...argv.slice(at + 2)];
|
|
23
23
|
}
|
|
24
|
+
function padCells(text, cells) {
|
|
25
|
+
let width = 0;
|
|
26
|
+
for (const ch of text) width += ch.charCodeAt(0) > 4351 ? 2 : 1;
|
|
27
|
+
return text + " ".repeat(Math.max(0, cells - width));
|
|
28
|
+
}
|
|
24
29
|
async function ask(deps) {
|
|
25
30
|
if (!deps.prompt) return void 0;
|
|
26
31
|
deps.out(TOKEN_PROMPT_GUIDE);
|
|
@@ -42,7 +47,7 @@ async function runBrowsers(argv, deps) {
|
|
|
42
47
|
const action = rest[0] ?? "list";
|
|
43
48
|
if (action !== "add" && action !== "list" && action !== "revoke" && action !== "repair") {
|
|
44
49
|
deps.out(
|
|
45
|
-
|
|
50
|
+
"\uC0AC\uC6A9\uBC95: browsers [add <\uC774\uB984>|list|revoke <handle>|repair] [--token <\uAC12>]"
|
|
46
51
|
);
|
|
47
52
|
return 2;
|
|
48
53
|
}
|
|
@@ -50,14 +55,16 @@ async function runBrowsers(argv, deps) {
|
|
|
50
55
|
if (action === "add") {
|
|
51
56
|
const label = rest.slice(1).join(" ").trim();
|
|
52
57
|
if (!label) {
|
|
53
|
-
deps.out("
|
|
58
|
+
deps.out("\uC0AC\uC6A9\uBC95: browsers add <\uC774\uB984> (\uC608: browsers add \uD68C\uC0AC \uD06C\uB86C)");
|
|
54
59
|
return 2;
|
|
55
60
|
}
|
|
56
61
|
body = { token, action, label };
|
|
57
62
|
} else if (action === "revoke") {
|
|
58
63
|
const handle = rest[1]?.trim();
|
|
59
64
|
if (!handle) {
|
|
60
|
-
deps.out(
|
|
65
|
+
deps.out(
|
|
66
|
+
"\uC0AC\uC6A9\uBC95: browsers revoke <handle> (handle \uC740 browsers list \uB85C \uD655\uC778)"
|
|
67
|
+
);
|
|
61
68
|
return 2;
|
|
62
69
|
}
|
|
63
70
|
body = { token, action, handle };
|
|
@@ -72,21 +79,23 @@ async function runBrowsers(argv, deps) {
|
|
|
72
79
|
body: JSON.stringify(body)
|
|
73
80
|
}).then((r) => r.text());
|
|
74
81
|
} catch {
|
|
75
|
-
deps.out("
|
|
76
|
-
deps.out("
|
|
82
|
+
deps.out("\uC5F0\uACB0 \uD504\uB85C\uADF8\uB7A8\uC774 \uB3CC\uACE0 \uC788\uC9C0 \uC54A\uC544\uC694.");
|
|
83
|
+
deps.out("\uC5F0\uACB0\uD574 \uB454 AI \uC571\uC744 \uCF20 \uB4A4 \uC774 \uBA85\uB839\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC138\uC694.");
|
|
77
84
|
return 1;
|
|
78
85
|
}
|
|
79
86
|
let parsed;
|
|
80
87
|
try {
|
|
81
88
|
parsed = JSON.parse(raw);
|
|
82
89
|
} catch {
|
|
83
|
-
deps.out(
|
|
84
|
-
deps.out(
|
|
90
|
+
deps.out(`${port} \uBC88 \uD3EC\uD2B8\uC5D0\uC11C \uB2E4\uB978 \uD504\uB85C\uADF8\uB7A8\uC774 \uC751\uB2F5\uD558\uACE0 \uC788\uC5B4\uC694.`);
|
|
91
|
+
deps.out(
|
|
92
|
+
"\uADF8 \uD504\uB85C\uADF8\uB7A8\uC744 \uB044\uAC70\uB098 DATALAB_MCP_PORT \uB85C \uB2E4\uB978 \uD3EC\uD2B8\uB97C \uC9C0\uC815\uD55C \uB4A4 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC138\uC694."
|
|
93
|
+
);
|
|
85
94
|
return 1;
|
|
86
95
|
}
|
|
87
96
|
if (parsed["ok"] !== true) {
|
|
88
97
|
const why = String(parsed["error"] ?? "unknown");
|
|
89
|
-
deps.out(
|
|
98
|
+
deps.out(`\uAC70\uC808\uB410\uC5B4\uC694: ${why}`);
|
|
90
99
|
if (typeof parsed["detail"] === "string")
|
|
91
100
|
deps.out(String(parsed["detail"]));
|
|
92
101
|
return 1;
|
|
@@ -96,35 +105,36 @@ async function runBrowsers(argv, deps) {
|
|
|
96
105
|
const registryStatus = parsed["registryStatus"];
|
|
97
106
|
if (registryStatus?.state === "degraded") {
|
|
98
107
|
deps.out(
|
|
99
|
-
|
|
108
|
+
`\uB4F1\uB85D\uBD80\uC5D0 \uBB38\uC81C\uAC00 \uC788\uC5B4\uC694: ${String(registryStatus.error ?? "degraded")}`
|
|
100
109
|
);
|
|
101
|
-
deps.out("
|
|
110
|
+
deps.out("\uBCF5\uAD6C\uD558\uB824\uBA74: datalab-extension-mcp browsers repair");
|
|
102
111
|
}
|
|
103
112
|
if (rows.length === 0) {
|
|
104
|
-
deps.out("
|
|
113
|
+
deps.out("\uB4F1\uB85D\uB41C \uBE0C\uB77C\uC6B0\uC800\uAC00 \uC5C6\uC5B4\uC694.");
|
|
105
114
|
return 0;
|
|
106
115
|
}
|
|
107
116
|
for (const row of rows) {
|
|
108
|
-
const state = row.browserId === null ? "
|
|
109
|
-
deps.out(`${row.handle} ${state
|
|
117
|
+
const state = row.browserId === null ? "\uCF54\uB4DC \uB300\uAE30" : row.connected === true ? "\uC5F0\uACB0\uB428" : "\uC5F0\uACB0 \uB04A\uAE40";
|
|
118
|
+
deps.out(`${row.handle} ${padCells(state, 10)} ${row.label}`);
|
|
110
119
|
}
|
|
111
120
|
return 0;
|
|
112
121
|
}
|
|
113
122
|
if (action === "revoke") {
|
|
114
123
|
deps.out(
|
|
115
|
-
parsed["removed"] === true ? "
|
|
124
|
+
parsed["removed"] === true ? "\uB0B4\uB838\uC5B4\uC694. \uC774 \uBE0C\uB77C\uC6B0\uC800\uB294 \uB2E4\uC2DC \uBD99\uC9C0 \uBABB\uD574\uC694." : "\uADF8 handle \uC744 \uAC00\uC9C4 \uBE0C\uB77C\uC6B0\uC800\uAC00 \uC5C6\uC5B4\uC694. browsers list \uB85C \uD655\uC778\uD574 \uC8FC\uC138\uC694."
|
|
116
125
|
);
|
|
117
126
|
return parsed["removed"] === true ? 0 : 1;
|
|
118
127
|
}
|
|
119
128
|
if (action === "repair") {
|
|
120
129
|
const removed = Array.isArray(parsed["removed"]) ? parsed["removed"].length : 0;
|
|
121
130
|
deps.out(
|
|
122
|
-
removed > 0 ?
|
|
131
|
+
removed > 0 ? `\uCDA9\uB3CC\uD558\uB358 \uB4F1\uB85D ${String(removed)}\uAC1C\uB97C \uC815\uB9AC\uD588\uC5B4\uC694. \uD574\uB2F9 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uB294 \uB4F1\uB85D \uCF54\uB4DC\uB97C \uC0C8\uB85C \uB9CC\uB4E4\uC5B4 \uC8FC\uC138\uC694.` : "\uCDA9\uB3CC\uD558\uB294 \uB4F1\uB85D\uC774 \uC5C6\uC5B4\uC694."
|
|
123
132
|
);
|
|
124
133
|
return 0;
|
|
125
134
|
}
|
|
126
|
-
deps.out(
|
|
127
|
-
deps.out(
|
|
135
|
+
deps.out(`\uB4F1\uB85D\uD588\uC5B4\uC694. handle: ${String(parsed["handle"])}`);
|
|
136
|
+
deps.out("\uC544\uB798 \uB4F1\uB85D \uCF54\uB4DC\uB97C \uADF8 \uBE0C\uB77C\uC6B0\uC800\uC758 \uD655\uC7A5 \uC124\uC815 3\uBC88 \uCE78\uC5D0 \uBD99\uC5EC\uB123\uC73C\uC138\uC694.");
|
|
137
|
+
deps.out("\uC9C0\uAE08 \uD55C \uBC88\uB9CC \uBCF4\uC5EC\uC694 \u2014 \uB2E4\uC2DC \uBCFC \uC218 \uC5C6\uC5B4\uC694.");
|
|
128
138
|
deps.out(String(parsed["token"]));
|
|
129
139
|
return 0;
|
|
130
140
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ../mcp-server/dist/chunk-
|
|
1
|
+
// ../mcp-server/dist/chunk-5F4SKHY3.js
|
|
2
2
|
import { createHash } from "crypto";
|
|
3
3
|
import {
|
|
4
4
|
appendFileSync,
|
|
@@ -16664,6 +16664,11 @@ function createBrowserRegistry(opts) {
|
|
|
16664
16664
|
};
|
|
16665
16665
|
});
|
|
16666
16666
|
},
|
|
16667
|
+
async settled() {
|
|
16668
|
+
while (pendingMutations > 0) {
|
|
16669
|
+
await mutationTail.catch(() => void 0);
|
|
16670
|
+
}
|
|
16671
|
+
},
|
|
16667
16672
|
status() {
|
|
16668
16673
|
if (unreadable !== null) {
|
|
16669
16674
|
return {
|
|
@@ -15,8 +15,8 @@ function printStep(out, step, total, title) {
|
|
|
15
15
|
out("");
|
|
16
16
|
out(`[${String(step)}/${String(total)}] ${title}`);
|
|
17
17
|
}
|
|
18
|
-
var INSTALL_SUBTITLE = "\
|
|
19
|
-
var UNINSTALL_SUBTITLE = "\
|
|
18
|
+
var INSTALL_SUBTITLE = "\uC5F0\uACB0 \uD504\uB85C\uADF8\uB7A8 \uC124\uCE58 \uB3C4\uC6B0\uBBF8";
|
|
19
|
+
var UNINSTALL_SUBTITLE = "\uC5F0\uACB0 \uD504\uB85C\uADF8\uB7A8 \uC815\uB9AC \uB3C4\uC6B0\uBBF8";
|
|
20
20
|
var SERVER_NAME = "datalab";
|
|
21
21
|
var SERVER_PACKAGE = "@modootoday/datalab-extension-mcp";
|
|
22
22
|
var DEFAULT_PORT = "8765";
|
|
@@ -167,11 +167,11 @@ async function fileOrParentExists(io, path) {
|
|
|
167
167
|
if (await io.exists(path)) {
|
|
168
168
|
return true;
|
|
169
169
|
}
|
|
170
|
-
const
|
|
171
|
-
if (
|
|
170
|
+
const cut2 = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
171
|
+
if (cut2 <= 0) {
|
|
172
172
|
return false;
|
|
173
173
|
}
|
|
174
|
-
return io.exists(path.slice(0,
|
|
174
|
+
return io.exists(path.slice(0, cut2));
|
|
175
175
|
}
|
|
176
176
|
async function msixRoamingDir(io, familyPrefix, appDir) {
|
|
177
177
|
if (io.platform !== "win32") {
|
|
@@ -365,7 +365,9 @@ var codexConfigOnlySnippetHost = {
|
|
|
365
365
|
detectedPath(io) {
|
|
366
366
|
return codexConfigPath(io);
|
|
367
367
|
},
|
|
368
|
-
|
|
368
|
+
// Our own table in this file; set means the config is merged, not printed.
|
|
369
|
+
tomlServerName: SERVER_NAME,
|
|
370
|
+
reason: "\uC124\uC815 \uD30C\uC77C\uC758 \uD615\uD0DC\uB97C \uC54C\uC544\uBCF4\uC9C0 \uBABB\uD574 \uC790\uB3D9\uC73C\uB85C \uACE0\uCE58\uC9C0 \uC54A\uC558\uC5B4\uC694. \uC544\uB798 \uB0B4\uC6A9\uC744 \uC9C1\uC811 \uB123\uC5B4 \uC8FC\uC138\uC694.",
|
|
369
371
|
pasteWhere: "\uC544\uB798 \uB0B4\uC6A9\uC744 \uC704 \uC124\uC815 \uD30C\uC77C\uC5D0 \uC9C1\uC811 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.",
|
|
370
372
|
// The desktop app, the CLI, and the IDE extension share this one file.
|
|
371
373
|
createHint: "\uD30C\uC77C\uC774 \uC5C6\uC73C\uBA74 ChatGPT \uB370\uC2A4\uD06C\uD1B1\uC5D0\uC11C \uC124\uC815 \u2192 MCP \uC11C\uBC84 \u2192 \uC11C\uBC84 \uCD94\uAC00\uB97C \uD55C \uBC88 \uD558\uBA74 \uB9CC\uB4E4\uC5B4\uC838\uC694. \uD3F4\uB354\uAC00 \uC5C6\uC73C\uBA74 \uC9C1\uC811 \uB9CC\uB4E4\uC5B4\uB3C4 \uB3FC\uC694.",
|
|
@@ -854,8 +856,8 @@ function join(io, ...parts) {
|
|
|
854
856
|
return io.platform === "win32" ? parts.join("\\") : parts.join("/");
|
|
855
857
|
}
|
|
856
858
|
function dirOf(rel) {
|
|
857
|
-
const
|
|
858
|
-
return
|
|
859
|
+
const cut2 = rel.lastIndexOf("/");
|
|
860
|
+
return cut2 === -1 ? "" : rel.slice(0, cut2);
|
|
859
861
|
}
|
|
860
862
|
async function readMarker(io, dir) {
|
|
861
863
|
const path = join(io, dir, MARKER_NAME);
|
|
@@ -1058,7 +1060,7 @@ async function placeSkills(io, payloadRoot, detectedIds, version, all = false) {
|
|
|
1058
1060
|
tier: 2,
|
|
1059
1061
|
status: placed > 0 ? "success" : "skipped",
|
|
1060
1062
|
surface: "skills",
|
|
1061
|
-
message: refused.length > 0 ? `\uC2A4\uD0AC ${placed}\uAC1C \uC124\uCE58. ${refused.join(" ")}${heldNote}` : `\uC2A4\uD0AC ${placed}\uAC1C \uC124\uCE58.${heldNote}`
|
|
1063
|
+
message: refused.length > 0 ? `\uC2A4\uD0AC ${placed}\uAC1C\uB97C \uC124\uCE58\uD588\uC5B4\uC694. ${refused.join(" ")}${heldNote}` : `\uC2A4\uD0AC ${placed}\uAC1C\uB97C \uC124\uCE58\uD588\uC5B4\uC694.${heldNote}`
|
|
1062
1064
|
});
|
|
1063
1065
|
}
|
|
1064
1066
|
return results;
|
|
@@ -1093,7 +1095,7 @@ async function removeSkills(io, detectedIds, slugs) {
|
|
|
1093
1095
|
tier: 2,
|
|
1094
1096
|
status: "success",
|
|
1095
1097
|
surface: "skills",
|
|
1096
|
-
message: kept.length > 0 ? `\uC2A4\uD0AC ${removed}\uAC1C \uC81C\uAC70. ${kept.length}\uAC1C\uB294 \uC6B0\uB9AC\uAC00 \uC124\uCE58\uD55C \uAC83\uC774 \uC544\uB2C8\uB77C \uADF8\uB300\uB85C \uB480\uC5B4\uC694.` : `\uC2A4\uD0AC ${removed}\uAC1C \uC81C\uAC70
|
|
1098
|
+
message: kept.length > 0 ? `\uC2A4\uD0AC ${removed}\uAC1C\uB97C \uC81C\uAC70\uD588\uC5B4\uC694. ${kept.length}\uAC1C\uB294 \uC6B0\uB9AC\uAC00 \uC124\uCE58\uD55C \uAC83\uC774 \uC544\uB2C8\uB77C \uADF8\uB300\uB85C \uB480\uC5B4\uC694.` : `\uC2A4\uD0AC ${removed}\uAC1C\uB97C \uC81C\uAC70\uD588\uC5B4\uC694.`
|
|
1097
1099
|
});
|
|
1098
1100
|
}
|
|
1099
1101
|
return results;
|
|
@@ -1101,7 +1103,7 @@ async function removeSkills(io, detectedIds, slugs) {
|
|
|
1101
1103
|
var RESTART_NOTICE = "\uB9C8\uC9C0\uB9C9 \uD55C \uB2E8\uACC4: AI \uC571\uC744 \uC644\uC804\uD788 \uC885\uB8CC\uD588\uB2E4\uAC00 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC138\uC694.\n(Windows: \uC791\uC5C5 \uD45C\uC2DC\uC904 \uD2B8\uB808\uC774 \uC544\uC774\uCF58\uC5D0\uC11C \uC885\uB8CC)";
|
|
1102
1104
|
var NOTHING_CHANGED = "\uC544\uBB34\uAC83\uB3C4 \uBC14\uAFB8\uC9C0 \uC54A\uC558\uC5B4\uC694.";
|
|
1103
1105
|
var UNINSTALL_DONE = "\uC815\uB9AC\uAC00 \uB05D\uB0AC\uC5B4\uC694.";
|
|
1104
|
-
var UNINSTALL_TOKEN_REMINDER = "\uC774 \uBE0C\uB77C\uC6B0\uC800\uC758 \uB85C\uCEEC \uC5F0\uACB0 \uC815\uBCF4\uB3C4 \uC9C0\uC6B0\uB824\uBA74 \uB370\uC774\uD130\uB7A9\uD234\uC988 \uD328\uB110\uC5D0\uC11C '\uC774 \uBE0C\uB77C\uC6B0\uC800 \uC5F0\uACB0 \uB044\uAE30'\uB97C \uB20C\uB7EC \uC8FC\uC138\uC694. \
|
|
1106
|
+
var UNINSTALL_TOKEN_REMINDER = "\uC774 \uBE0C\uB77C\uC6B0\uC800\uC758 \uB85C\uCEEC \uC5F0\uACB0 \uC815\uBCF4\uB3C4 \uC9C0\uC6B0\uB824\uBA74 \uB370\uC774\uD130\uB7A9\uD234\uC988 \uD328\uB110\uC5D0\uC11C '\uC774 \uBE0C\uB77C\uC6B0\uC800 \uC5F0\uACB0 \uB044\uAE30'\uB97C \uB20C\uB7EC \uC8FC\uC138\uC694. \uC774 \uCEF4\uD4E8\uD130\uC758 \uB4F1\uB85D\uC740 \uB4F1\uB85D\uB41C \uBE0C\uB77C\uC6B0\uC800 \uBAA9\uB85D\uC5D0\uC11C \uBCC4\uB3C4\uB85C \uB0B4\uB9B4 \uC218 \uC788\uC5B4\uC694.";
|
|
1105
1107
|
var PERMISSION_DENIED_HINT = "\uD30C\uC77C \uAD8C\uD55C \uB54C\uBB38\uC5D0 \uC124\uC815\uC744 \uC4F8 \uC218 \uC5C6\uC5C8\uC5B4\uC694. \uD30C\uC77C \uAD8C\uD55C\uC744 \uD655\uC778\uD55C \uB4A4 \uAC19\uC740 \uBA85\uB839\uC5B4\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC138\uC694.";
|
|
1106
1108
|
var NO_HOSTS_DETECTED = "\uC5F0\uACB0\uD560 \uC218 \uC788\uB294 AI \uD504\uB85C\uADF8\uB7A8\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC5B4\uC694.";
|
|
1107
1109
|
var SUPPORTED_APPS_HEADER = "\uC9C0\uC6D0\uD558\uB294 \uD504\uB85C\uADF8\uB7A8\uACFC \uB2E4\uC6B4\uB85C\uB4DC \uC8FC\uC18C\uC608\uC694:";
|
|
@@ -1289,11 +1291,11 @@ function formatBackupTimestamp(date) {
|
|
|
1289
1291
|
return String(date.getUTCFullYear()) + pad(date.getUTCMonth() + 1) + pad(date.getUTCDate()) + pad(date.getUTCHours()) + pad(date.getUTCMinutes()) + pad(date.getUTCSeconds());
|
|
1290
1292
|
}
|
|
1291
1293
|
function splitPath(filePath) {
|
|
1292
|
-
const
|
|
1293
|
-
if (
|
|
1294
|
+
const cut2 = Math.max(filePath.lastIndexOf("/"), filePath.lastIndexOf("\\"));
|
|
1295
|
+
if (cut2 < 0) {
|
|
1294
1296
|
return { dir: ".", base: filePath };
|
|
1295
1297
|
}
|
|
1296
|
-
return { dir: filePath.slice(0,
|
|
1298
|
+
return { dir: filePath.slice(0, cut2), base: filePath.slice(cut2 + 1) };
|
|
1297
1299
|
}
|
|
1298
1300
|
function joinDir(dir, name) {
|
|
1299
1301
|
if (dir.includes("\\") && !dir.includes("/")) {
|
|
@@ -1475,6 +1477,118 @@ async function removeServerKey(io, filePath) {
|
|
|
1475
1477
|
}
|
|
1476
1478
|
return { ok: true, changed: true, backupPath };
|
|
1477
1479
|
}
|
|
1480
|
+
var isTableHeader = (line) => /^\s*\[/.test(line);
|
|
1481
|
+
function headerKind(line, server) {
|
|
1482
|
+
const m = /^\s*\[\s*mcp_servers\s*\.\s*([A-Za-z0-9_-]+)\s*(\.[^\]]*)?\]\s*$/.exec(
|
|
1483
|
+
line
|
|
1484
|
+
);
|
|
1485
|
+
if (m === null || m[1] !== server) return null;
|
|
1486
|
+
return m[2] === void 0 ? "base" : "sub";
|
|
1487
|
+
}
|
|
1488
|
+
var ownsHeader = (line, server) => headerKind(line, server) !== null;
|
|
1489
|
+
function isAmbiguous(lines, server) {
|
|
1490
|
+
let headers = 0;
|
|
1491
|
+
let inServersTable = false;
|
|
1492
|
+
for (const line of lines) {
|
|
1493
|
+
if (headerKind(line, server) === "base") {
|
|
1494
|
+
headers += 1;
|
|
1495
|
+
}
|
|
1496
|
+
if (isTableHeader(line)) {
|
|
1497
|
+
inServersTable = /^\s*\[\s*mcp_servers\s*\]\s*$/.test(line);
|
|
1498
|
+
continue;
|
|
1499
|
+
}
|
|
1500
|
+
if (new RegExp(`^\\s*mcp_servers\\s*\\.\\s*${server}\\s*=`).test(line)) {
|
|
1501
|
+
return true;
|
|
1502
|
+
}
|
|
1503
|
+
if (inServersTable && new RegExp(`^\\s*${server}\\s*=`).test(line)) {
|
|
1504
|
+
return true;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
return headers > 1;
|
|
1508
|
+
}
|
|
1509
|
+
function findSpan(lines, server) {
|
|
1510
|
+
let start = -1;
|
|
1511
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
1512
|
+
const line = lines[i] ?? "";
|
|
1513
|
+
if (start === -1) {
|
|
1514
|
+
if (ownsHeader(line, server)) start = i;
|
|
1515
|
+
continue;
|
|
1516
|
+
}
|
|
1517
|
+
if (isTableHeader(line) && !ownsHeader(line, server)) {
|
|
1518
|
+
return { start, end: i };
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
return { start, end: start === -1 ? -1 : lines.length };
|
|
1522
|
+
}
|
|
1523
|
+
function trimTrailingBlanks(lines, end) {
|
|
1524
|
+
let e = end;
|
|
1525
|
+
while (e > 0 && (lines[e - 1] ?? "").trim() === "") e -= 1;
|
|
1526
|
+
return e;
|
|
1527
|
+
}
|
|
1528
|
+
function splice(raw, snippet, server) {
|
|
1529
|
+
const lines = raw.split("\n");
|
|
1530
|
+
if (isAmbiguous(lines, server)) return null;
|
|
1531
|
+
const span = findSpan(lines, server);
|
|
1532
|
+
if (span.start === -1) {
|
|
1533
|
+
const body = raw.trimEnd();
|
|
1534
|
+
return body === "" ? `${snippet}
|
|
1535
|
+
` : `${body}
|
|
1536
|
+
|
|
1537
|
+
${snippet}
|
|
1538
|
+
`;
|
|
1539
|
+
}
|
|
1540
|
+
const end = trimTrailingBlanks(lines, span.end);
|
|
1541
|
+
const before = lines.slice(0, span.start);
|
|
1542
|
+
const after = lines.slice(end);
|
|
1543
|
+
return [...before, ...snippet.split("\n"), ...after].join("\n");
|
|
1544
|
+
}
|
|
1545
|
+
function cut(raw, server) {
|
|
1546
|
+
const lines = raw.split("\n");
|
|
1547
|
+
if (isAmbiguous(lines, server)) return { text: raw, found: false };
|
|
1548
|
+
const span = findSpan(lines, server);
|
|
1549
|
+
if (span.start === -1) return { text: raw, found: false };
|
|
1550
|
+
const end = trimTrailingBlanks(lines, span.end);
|
|
1551
|
+
const kept = [...lines.slice(0, span.start), ...lines.slice(end)];
|
|
1552
|
+
return { text: `${kept.join("\n").trimEnd()}
|
|
1553
|
+
`, found: true };
|
|
1554
|
+
}
|
|
1555
|
+
async function upsertTomlServer(io, filePath, snippet, server) {
|
|
1556
|
+
const exists = await io.exists(filePath);
|
|
1557
|
+
const raw = exists ? await io.readFile(filePath) : "";
|
|
1558
|
+
const next = splice(raw, snippet, server);
|
|
1559
|
+
if (next === null) {
|
|
1560
|
+
return { ok: false, changed: false, reason: "parse" };
|
|
1561
|
+
}
|
|
1562
|
+
if (exists && next === raw) {
|
|
1563
|
+
return { ok: true, changed: false };
|
|
1564
|
+
}
|
|
1565
|
+
const backupPath = exists ? await writeBackup(io, filePath, raw) : void 0;
|
|
1566
|
+
await atomicWrite(io, filePath, next);
|
|
1567
|
+
const verify = await io.readFile(filePath);
|
|
1568
|
+
if (verify !== next) {
|
|
1569
|
+
if (exists) await restoreNewestBackup(io, filePath);
|
|
1570
|
+
return { ok: false, changed: false, reason: "verify" };
|
|
1571
|
+
}
|
|
1572
|
+
return backupPath === void 0 ? { ok: true, changed: true } : { ok: true, changed: true, backupPath };
|
|
1573
|
+
}
|
|
1574
|
+
async function removeTomlServer(io, filePath, server) {
|
|
1575
|
+
if (!await io.exists(filePath)) {
|
|
1576
|
+
return { ok: true, changed: false, reason: "missing" };
|
|
1577
|
+
}
|
|
1578
|
+
const raw = await io.readFile(filePath);
|
|
1579
|
+
const { text, found } = cut(raw, server);
|
|
1580
|
+
if (!found) {
|
|
1581
|
+
return { ok: true, changed: false };
|
|
1582
|
+
}
|
|
1583
|
+
const backupPath = await writeBackup(io, filePath, raw);
|
|
1584
|
+
await atomicWrite(io, filePath, text);
|
|
1585
|
+
const verify = await io.readFile(filePath);
|
|
1586
|
+
if (verify !== text) {
|
|
1587
|
+
await restoreNewestBackup(io, filePath);
|
|
1588
|
+
return { ok: false, changed: false, reason: "verify" };
|
|
1589
|
+
}
|
|
1590
|
+
return { ok: true, changed: true, backupPath };
|
|
1591
|
+
}
|
|
1478
1592
|
async function detectHosts(io) {
|
|
1479
1593
|
const detected = [];
|
|
1480
1594
|
const useShell = io.platform === "win32";
|
|
@@ -1514,12 +1628,12 @@ async function detectHosts(io) {
|
|
|
1514
1628
|
}
|
|
1515
1629
|
let present = await io.exists(configPath);
|
|
1516
1630
|
if (!present) {
|
|
1517
|
-
const
|
|
1631
|
+
const cut2 = Math.max(
|
|
1518
1632
|
configPath.lastIndexOf("/"),
|
|
1519
1633
|
configPath.lastIndexOf("\\")
|
|
1520
1634
|
);
|
|
1521
|
-
if (
|
|
1522
|
-
present = await io.exists(configPath.slice(0,
|
|
1635
|
+
if (cut2 > 0) {
|
|
1636
|
+
present = await io.exists(configPath.slice(0, cut2));
|
|
1523
1637
|
}
|
|
1524
1638
|
}
|
|
1525
1639
|
if (present) {
|
|
@@ -1640,6 +1754,51 @@ function printResult(io, result) {
|
|
|
1640
1754
|
io.out(` \uBC31\uC5C5: ${result.backupPath}`);
|
|
1641
1755
|
}
|
|
1642
1756
|
}
|
|
1757
|
+
async function mergeSnippetToml(io, host, opts) {
|
|
1758
|
+
if (host.tomlServerName === void 0) {
|
|
1759
|
+
return null;
|
|
1760
|
+
}
|
|
1761
|
+
const path = host.detectedPath(io);
|
|
1762
|
+
if (path === null || !await io.exists(path)) {
|
|
1763
|
+
return null;
|
|
1764
|
+
}
|
|
1765
|
+
const outcome = await upsertTomlServer(
|
|
1766
|
+
io,
|
|
1767
|
+
path,
|
|
1768
|
+
host.buildSnippet(opts, io.platform),
|
|
1769
|
+
host.tomlServerName
|
|
1770
|
+
);
|
|
1771
|
+
if (!outcome.ok) {
|
|
1772
|
+
return null;
|
|
1773
|
+
}
|
|
1774
|
+
return {
|
|
1775
|
+
hostId: host.id,
|
|
1776
|
+
displayName: host.displayName,
|
|
1777
|
+
tier: 3,
|
|
1778
|
+
status: "success",
|
|
1779
|
+
message: outcome.changed ? `\uC124\uC815 \uD30C\uC77C\uC744 \uACE0\uCCE4\uC5B4\uC694: ${path}` : `\uC124\uC815 \uD30C\uC77C\uC774 \uC774\uBBF8 \uCD5C\uC2E0\uC774\uC5D0\uC694: ${path}`
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
async function removeSnippetToml(io, host) {
|
|
1783
|
+
if (host.tomlServerName === void 0) {
|
|
1784
|
+
return null;
|
|
1785
|
+
}
|
|
1786
|
+
const path = host.detectedPath(io);
|
|
1787
|
+
if (path === null || !await io.exists(path)) {
|
|
1788
|
+
return null;
|
|
1789
|
+
}
|
|
1790
|
+
const outcome = await removeTomlServer(io, path, host.tomlServerName);
|
|
1791
|
+
if (!outcome.ok) {
|
|
1792
|
+
return null;
|
|
1793
|
+
}
|
|
1794
|
+
return {
|
|
1795
|
+
hostId: host.id,
|
|
1796
|
+
displayName: host.displayName,
|
|
1797
|
+
tier: 3,
|
|
1798
|
+
status: "success",
|
|
1799
|
+
message: outcome.changed ? `\uC124\uC815 \uD30C\uC77C\uC5D0\uC11C \uC9C0\uC6E0\uC5B4\uC694: ${path}` : `\uC124\uC815 \uD30C\uC77C\uC5D0 \uC6B0\uB9AC \uD56D\uBAA9\uC774 \uC5C6\uC5C8\uC5B4\uC694: ${path}`
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1643
1802
|
async function bootstrapSnippetFile(io, host, opts) {
|
|
1644
1803
|
if (host.bootstrapWhenAbsent !== true) {
|
|
1645
1804
|
return null;
|
|
@@ -1648,9 +1807,9 @@ async function bootstrapSnippetFile(io, host, opts) {
|
|
|
1648
1807
|
if (path === null || await io.exists(path)) {
|
|
1649
1808
|
return null;
|
|
1650
1809
|
}
|
|
1651
|
-
const
|
|
1652
|
-
if (
|
|
1653
|
-
await io.mkdir(path.slice(0,
|
|
1810
|
+
const cut2 = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
1811
|
+
if (cut2 > 0) {
|
|
1812
|
+
await io.mkdir(path.slice(0, cut2));
|
|
1654
1813
|
}
|
|
1655
1814
|
await io.writeFile(path, `${host.buildSnippet(opts, io.platform)}
|
|
1656
1815
|
`);
|
|
@@ -1693,7 +1852,7 @@ function describeError(error) {
|
|
|
1693
1852
|
if (isPermissionError(error)) {
|
|
1694
1853
|
return PERMISSION_DENIED_HINT;
|
|
1695
1854
|
}
|
|
1696
|
-
const next = " \uAC19\uC740 \uBA85\uB839\uC744 \uD55C \uBC88 \uB354 \uC2E4\uD589\uD574 \uBCF4\uC2DC\uACE0, \uADF8\uB798\uB3C4 \uC548 \uB418\uBA74 \uC54C\uB824\uC8FC\uC138\uC694.";
|
|
1855
|
+
const next = " \uAC19\uC740 \uBA85\uB839\uC744 \uD55C \uBC88 \uB354 \uC2E4\uD589\uD574 \uBCF4\uC2DC\uACE0, \uADF8\uB798\uB3C4 \uC548 \uB418\uBA74 \uC54C\uB824 \uC8FC\uC138\uC694.";
|
|
1697
1856
|
if (error instanceof Error && error.message !== "") {
|
|
1698
1857
|
return `\uC608\uC0C1\uD558\uC9C0 \uBABB\uD55C \uBB38\uC81C\uAC00 \uC0DD\uACBC\uC5B4\uC694.${next} (${error.message})`;
|
|
1699
1858
|
}
|
|
@@ -1946,7 +2105,8 @@ async function runInstall(opts, io) {
|
|
|
1946
2105
|
} else if (d.tier === 2) {
|
|
1947
2106
|
results.push(await applyFileInstall(io, d, entryOpts));
|
|
1948
2107
|
} else if (d.snippet !== void 0) {
|
|
1949
|
-
const
|
|
2108
|
+
const merged = await mergeSnippetToml(io, d.snippet, entryOpts);
|
|
2109
|
+
const made = merged ?? await bootstrapSnippetFile(io, d.snippet, entryOpts);
|
|
1950
2110
|
results.push(
|
|
1951
2111
|
made ?? {
|
|
1952
2112
|
hostId: d.id,
|
|
@@ -2060,13 +2220,16 @@ async function runUninstall(opts, io) {
|
|
|
2060
2220
|
} else if (d.tier === 2) {
|
|
2061
2221
|
results.push(await applyFileUninstall(io, d));
|
|
2062
2222
|
} else if (d.snippet !== void 0) {
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2223
|
+
const removed = await removeSnippetToml(io, d.snippet);
|
|
2224
|
+
results.push(
|
|
2225
|
+
removed ?? {
|
|
2226
|
+
hostId: d.id,
|
|
2227
|
+
displayName: d.displayName,
|
|
2228
|
+
tier: 3,
|
|
2229
|
+
status: "skipped",
|
|
2230
|
+
message: `\uC790\uB3D9 \uC218\uC815\uD558\uC9C0 \uC54A\uC544\uC694. \uC124\uC815\uC5D0 "${SERVER_NAME}" \uD56D\uBAA9\uC774 \uC788\uB2E4\uBA74 \uC9C1\uC811 \uC9C0\uC6CC \uC8FC\uC138\uC694. \uC9C0\uC6B0\uC9C0 \uC54A\uC544\uB3C4 \uC544\uBB34 \uC77C\uB3C4 \uD558\uC9C0 \uC54A\uC544\uC694.`
|
|
2231
|
+
}
|
|
2232
|
+
);
|
|
2070
2233
|
}
|
|
2071
2234
|
} catch (error) {
|
|
2072
2235
|
results.push({
|
package/dist/cli.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
dispatchCli,
|
|
4
4
|
runAdapter
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-KE5ZJQKG.js";
|
|
6
6
|
import {
|
|
7
7
|
runDaemon
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-EGSZC33X.js";
|
|
9
9
|
import "./chunk-MLKGABMK.js";
|
|
10
10
|
|
|
11
11
|
// src/cli.ts
|
|
@@ -17,7 +17,7 @@ function log(message) {
|
|
|
17
17
|
`);
|
|
18
18
|
}
|
|
19
19
|
async function runInstaller(sub, argv) {
|
|
20
|
-
const { createNodeIo, runInstall, runUninstall } = await import("./dist-
|
|
20
|
+
const { createNodeIo, runInstall, runUninstall } = await import("./dist-VANK7DDE.js");
|
|
21
21
|
const opts = { version: VERSION };
|
|
22
22
|
for (let i = 0; i < argv.length; i += 1) {
|
|
23
23
|
const flag = argv[i];
|
|
@@ -62,8 +62,8 @@ async function main() {
|
|
|
62
62
|
// Loaded lazily, like the installer: the adapter path is what an MCP host
|
|
63
63
|
// spawns and must not pay for a command only an operator types.
|
|
64
64
|
browsers: async (argv) => {
|
|
65
|
-
const { runBrowsers } = await import("./browsers-
|
|
66
|
-
const { createNodeIo } = await import("./dist-
|
|
65
|
+
const { runBrowsers } = await import("./browsers-T6B6IUYR.js");
|
|
66
|
+
const { createNodeIo } = await import("./dist-VANK7DDE.js");
|
|
67
67
|
const io = createNodeIo();
|
|
68
68
|
const code = await runBrowsers(argv, {
|
|
69
69
|
env: process.env,
|
|
@@ -80,7 +80,7 @@ async function main() {
|
|
|
80
80
|
// Same lazy load, and the same reason: an MCP host spawning the adapter
|
|
81
81
|
// must not pay to resolve the installer.
|
|
82
82
|
skills: async (argv) => {
|
|
83
|
-
const { runSkills } = await import("./skills-
|
|
83
|
+
const { runSkills } = await import("./skills-5JDMNON3.js");
|
|
84
84
|
const {
|
|
85
85
|
createNodeIo,
|
|
86
86
|
listSkills,
|
|
@@ -88,7 +88,7 @@ async function main() {
|
|
|
88
88
|
attachSkill,
|
|
89
89
|
detachSkill,
|
|
90
90
|
skillsPayloadRoot
|
|
91
|
-
} = await import("./dist-
|
|
91
|
+
} = await import("./dist-VANK7DDE.js");
|
|
92
92
|
const io = createNodeIo();
|
|
93
93
|
const root = skillsPayloadRoot();
|
|
94
94
|
const code = await runSkills(argv, {
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "./chunk-MLKGABMK.js";
|
|
2
2
|
|
|
3
3
|
// src/skills.ts
|
|
4
|
-
var USAGE = "
|
|
4
|
+
var USAGE = "\uC0AC\uC6A9\uBC95: skills [list|status|attach <slug>|detach <slug>]";
|
|
5
5
|
var REFUSAL_TEXT = {
|
|
6
6
|
unmarked: "\uB2E4\uB978 \uB3C4\uAD6C\uAC00 \uB9CC\uB4E0 \uD3F4\uB354",
|
|
7
7
|
foreign: "\uB2E4\uB978 \uB3C4\uAD6C\uAC00 \uC124\uCE58\uD55C \uC2A4\uD0AC",
|
|
@@ -84,7 +84,7 @@ async function runSkills(argv, deps) {
|
|
|
84
84
|
}
|
|
85
85
|
if (action === "attach" || action === "detach") {
|
|
86
86
|
if (slug === "") {
|
|
87
|
-
deps.out(
|
|
87
|
+
deps.out(`\uC0AC\uC6A9\uBC95: skills ${action} <slug>`);
|
|
88
88
|
return 2;
|
|
89
89
|
}
|
|
90
90
|
const result = action === "attach" ? await deps.attach(slug) : await deps.detach(slug);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: datalab-reader-simulation
|
|
3
|
-
description:
|
|
3
|
+
description: 내 블로그 독자 데이터로 초안을 독자 입장에서 점검한다. 측정된 독자 프로필을 세우고 초안이 그 독자가 실제로 묻는 질문에 답이 되는지 본다. 독자 시뮬레이션, "독자 입장에서 봐줘", 예상 질문 점검에 사용한다. 결과는 사고 도구이지 실제 반응의 증거가 아니다.
|
|
4
4
|
license: Proprietary
|
|
5
5
|
metadata:
|
|
6
6
|
tools: my_audience, my_content_audience, my_followers, my_revisit, my_dwell, my_inflow, my_inflow_domain, my_device, my_country, my_content_info, my_content_read, my_content_detail, kin_question_demand, search_keywords, autocomplete_keywords, editor_read, editor_read_structure
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: datalab-social-repurpose
|
|
3
|
-
description: 긴 글 한 편을 X·Threads·Instagram·LinkedIn
|
|
3
|
+
description: 긴 글 한 편을 SNS 채널별 홍보 초안으로 다시 쓴다. X·Threads·Instagram·LinkedIn 각각의 분량과 어투에 맞춘다. 발행글이나 편집 중인 초안을 SNS 홍보문으로 바꿀 때 사용한다. 참여도 예측이나 게시는 하지 않는다.
|
|
4
4
|
license: Proprietary
|
|
5
5
|
metadata:
|
|
6
6
|
tools: editor_read, editor_read_structure, my_content_read, my_content_detail
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: datalab-video-script
|
|
3
|
-
description: 주제나
|
|
3
|
+
description: 주제나 아이디어를 장면별 스크립트로 만들어 영상 편집기에 넣는다. 숏폼·릴스 스크립트, 장면 구성, 자막과 AI 목소리 제작에 사용한다. 영상 편집기 타임라인이 열려 있어야 하고 AI 이미지·목소리는 유료다.
|
|
4
4
|
license: Proprietary
|
|
5
5
|
metadata:
|
|
6
6
|
tools: video_timeline_get, video_sources_list, video_canvas_resize, video_scene_add, video_subtitle_set, video_scene_update, video_scene_reorder, video_text_add, video_background_set, photo_gallery_list, video_image_add_from_gallery, video_scene_image_set, generate_images, video_narration_generate, my_content_read, my_content_info
|