@modootoday/datalab-extension-mcp 1.8.22 → 1.8.24
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 +11 -11
- package/README.md +11 -11
- package/dist/{browsers-XF3K77T6.js → browsers-WRKF6F25.js} +2 -2
- package/dist/{chunk-VPDNMKVF.js → chunk-4EOWBJT2.js} +1 -1
- package/dist/{chunk-WCKI5Z4D.js → chunk-KAEK57JP.js} +35 -5
- package/dist/{chunk-AZ2NBESD.js → chunk-MHRCW2YZ.js} +186 -23
- package/dist/cli.js +6 -6
- package/dist/{dist-5JTDE7RQ.js → dist-VANK7DDE.js} +1 -1
- package/dist/index.js +2 -2
- package/package.json +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.24 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.24 skills list
|
|
149
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.24 skills status
|
|
150
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.24 skills attach <name>
|
|
151
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.24 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.24 browsers add "Work Chrome"
|
|
171
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.24 browsers list
|
|
172
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.24 browsers revoke <handle>
|
|
173
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.24 browsers repair
|
|
174
174
|
```
|
|
175
175
|
|
|
176
176
|
`add` **asks you to paste the install key** — the same value you used to
|
|
@@ -272,7 +272,7 @@ Start it again after a full quit and it picks the tools up. If they still aren't
|
|
|
272
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.)
|
|
273
273
|
|
|
274
274
|
```
|
|
275
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
275
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.24 install
|
|
276
276
|
```
|
|
277
277
|
|
|
278
278
|
- **`'npx' is not recognized...`** — Node.js isn't installed. The "No Node.js?" one-liner in Step 2 is the simplest fix.
|
|
@@ -302,7 +302,7 @@ Start it again after a full quit and it picks the tools up. If they still aren't
|
|
|
302
302
|
1. Run this in a terminal (it removes the entries registered in your AI apps).
|
|
303
303
|
|
|
304
304
|
```
|
|
305
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
305
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.24 uninstall
|
|
306
306
|
```
|
|
307
307
|
|
|
308
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
|
@@ -55,7 +55,7 @@ Claude 같은 AI 앱을 데이터랩툴즈에 연결해 주는 작은 무료 프
|
|
|
55
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.24 install
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
영어 글자가 여러 줄 올라오는 건 정상이에요. 이 명령은 연결 프로그램을 다운로드해 실행할 뿐, 다른 건 건드리지 않아요.
|
|
@@ -133,10 +133,10 @@ Claude 같은 AI 앱을 데이터랩툴즈에 연결해 주는 작은 무료 프
|
|
|
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.24 skills list
|
|
137
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.24 skills status
|
|
138
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.24 skills attach <이름>
|
|
139
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.24 skills detach <이름>
|
|
140
140
|
```
|
|
141
141
|
|
|
142
142
|
- `list` 는 이 버전에 들어 있는 전부를 이름과 설명으로 보여 줘요.
|
|
@@ -155,10 +155,10 @@ npx -y @modootoday/datalab-extension-mcp@1.8.22 skills detach <이름>
|
|
|
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.24 browsers add "회사 크롬"
|
|
159
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.24 browsers list
|
|
160
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.24 browsers revoke <handle>
|
|
161
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.24 browsers repair
|
|
162
162
|
```
|
|
163
163
|
|
|
164
164
|
`add` 를 실행하면 **설치 인증키를 붙여넣으라고 물어봐요** — 설치할 때와 같은 값이고, 설정 화면 1번의 `복사` 버튼으로 얻습니다. 그러면 브라우저 등록 코드가 **한 번만** 출력돼요. 연결 프로그램은 그 코드를 저장하지 않고 지문만 남기므로 다시 보여 줄 수 없어요.
|
|
@@ -237,7 +237,7 @@ 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.24 install
|
|
241
241
|
```
|
|
242
242
|
|
|
243
243
|
- **`'npx'을(는) 내부 또는 외부 명령...`** — Node.js가 없는 거예요. 위 2단계의 "Node.js가 없다면" 한 줄 설치가 가장 간단해요.
|
|
@@ -267,7 +267,7 @@ AI가 "그런 기능은 없다" 고 답하면, **"도구 목록을 확인해 보
|
|
|
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.24 uninstall
|
|
271
271
|
```
|
|
272
272
|
|
|
273
273
|
2. 확장 설정에서 **MCP 연결을 꺼요.** 패널이 브릿지라, 꺼 두면 어딘가 설정 줄이 남아 있어도 AI 앱이 아무것도 조회할 수 없어요.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_HOST,
|
|
3
3
|
DEFAULT_PORT
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-KAEK57JP.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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ../mcp-server/dist/chunk-
|
|
1
|
+
// ../mcp-server/dist/chunk-6LCDUQNO.js
|
|
2
2
|
import { createHash } from "crypto";
|
|
3
3
|
import {
|
|
4
4
|
appendFileSync,
|
|
@@ -15802,6 +15802,31 @@ var Bridge = class {
|
|
|
15802
15802
|
verifyRootToken(presented) {
|
|
15803
15803
|
return checkToken(presented ?? void 0, this.#deps.token).ok;
|
|
15804
15804
|
}
|
|
15805
|
+
/**
|
|
15806
|
+
* May this credential add a browser to this machine?
|
|
15807
|
+
*
|
|
15808
|
+
* The root token, or a browser already registered here. Binding this to the
|
|
15809
|
+
* root alone tied the right to add a browser to whoever installed last, so
|
|
15810
|
+
* the second browser's only offered path was to install again -- which moved
|
|
15811
|
+
* the right away from the first one, which then had the same problem.
|
|
15812
|
+
*
|
|
15813
|
+
* Deliberately NOT the same predicate as verifyRootToken: that one also
|
|
15814
|
+
* authorises shutdown, and a browser must not be able to end another's
|
|
15815
|
+
* unattended run. Widening is limited to adding and listing; revoking still
|
|
15816
|
+
* needs the root, because taking a browser off is the destructive half.
|
|
15817
|
+
*/
|
|
15818
|
+
verifyBrowserAddToken(presented) {
|
|
15819
|
+
if (presented === void 0 || presented === null || presented === "") {
|
|
15820
|
+
return false;
|
|
15821
|
+
}
|
|
15822
|
+
if (this.verifyRootToken(presented)) return true;
|
|
15823
|
+
const hash2 = digestToken(presented);
|
|
15824
|
+
if (this.#deps.wasRevoked(hash2)) return false;
|
|
15825
|
+
for (const current of this.#deps.claimedBrowsers.values()) {
|
|
15826
|
+
if (timingSafeEqual(hash2, current)) return true;
|
|
15827
|
+
}
|
|
15828
|
+
return false;
|
|
15829
|
+
}
|
|
15805
15830
|
/**
|
|
15806
15831
|
* Does the presented value belong to somebody currently holding the bridge?
|
|
15807
15832
|
*
|
|
@@ -16664,6 +16689,11 @@ function createBrowserRegistry(opts) {
|
|
|
16664
16689
|
};
|
|
16665
16690
|
});
|
|
16666
16691
|
},
|
|
16692
|
+
async settled() {
|
|
16693
|
+
while (pendingMutations > 0) {
|
|
16694
|
+
await mutationTail.catch(() => void 0);
|
|
16695
|
+
}
|
|
16696
|
+
},
|
|
16667
16697
|
status() {
|
|
16668
16698
|
if (unreadable !== null) {
|
|
16669
16699
|
return {
|
|
@@ -17664,13 +17694,13 @@ function createHttpBridge(opts) {
|
|
|
17664
17694
|
res.setHeader("cache-control", "no-store");
|
|
17665
17695
|
void readJson(req, MAX_BODY_BYTES).then(async (body) => {
|
|
17666
17696
|
const b = body;
|
|
17667
|
-
|
|
17668
|
-
|
|
17669
|
-
))
|
|
17697
|
+
const presented = typeof b?.["token"] === "string" ? b["token"] : "";
|
|
17698
|
+
const action = b?.["action"];
|
|
17699
|
+
const mayAdd = action === "add" || action === "list" ? opts.bridge.verifyBrowserAddToken(presented) : opts.bridge.verifyRootToken(presented);
|
|
17700
|
+
if (!mayAdd) {
|
|
17670
17701
|
json2(res, 403, { error: "unauthorized" });
|
|
17671
17702
|
return;
|
|
17672
17703
|
}
|
|
17673
|
-
const action = b?.["action"];
|
|
17674
17704
|
if (action === "list") {
|
|
17675
17705
|
const active = new Set(
|
|
17676
17706
|
opts.bridge.connectedBrowsers().map((row) => row.id)
|
|
@@ -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);
|
|
@@ -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
|
`);
|
|
@@ -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-4EOWBJT2.js";
|
|
6
6
|
import {
|
|
7
7
|
runDaemon
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-KAEK57JP.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-WRKF6F25.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,
|
|
@@ -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