@modootoday/datalab-extension-mcp 1.11.2 → 1.11.4

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 CHANGED
@@ -14,11 +14,11 @@ and it answers with the **real numbers** from your Naver blog, keywords, and ads
14
14
 
15
15
  Set this up once per computer.
16
16
 
17
- | Need | How |
18
- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
19
- | Chrome + the DataLab Tools extension | If you don't have it yet, install from [app.datalab.tools](https://app.datalab.tools/). |
20
- | An AI app | The program that answers your questions. It must be an **app installed on your computer** (websites won't work). Claude Desktop is free at [claude.ai](https://claude.ai/download); ChatGPT works as the **desktop app only**, from [openai.com](https://openai.com/). |
21
- | Node.js | The free program that runs the connector. Get it from [nodejs.org](https://nodejs.org/) via the **LTS** button. If that's a hassle, the one-liner in [Step 2](#step-2--install) installs Node.js for you in one go — no admin rights needed. |
17
+ | Need | How |
18
+ | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
19
+ | Chrome + the DataLab Tools extension | If you don't have it yet, install from [app.datalab.tools](https://app.datalab.tools/). |
20
+ | An AI app | The program that answers your questions. It must be an **app installed on your computer** (websites won't work). Claude Desktop is free at [claude.ai](https://claude.ai/download); ChatGPT works as the **desktop app only**, from [openai.com](https://openai.com/). |
21
+ | Node.js | The free program that runs the connector. Get it from [nodejs.org](https://nodejs.org/) via the **LTS** button. On Windows, `winget install OpenJS.NodeJS.LTS` also installs it. On Mac and Linux, the one-liner in [Step 2](#step-2--install) installs Node.js for you in one go. |
22
22
 
23
23
  > A work computer may block installs by policy. Check with your IT team first to avoid wasting time.
24
24
 
@@ -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.11.2 install
64
+ npx -y @modootoday/datalab-extension-mcp@1.11.4 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.
@@ -96,12 +96,12 @@ One command finds **every** AI app on your computer and connects them automatica
96
96
  <details>
97
97
  <summary><strong>No Node.js? — install it in one go</strong></summary>
98
98
 
99
- If the command errors with `'npx' is not recognized`, Node.js isn't installed. This one command installs Node.js first (no admin rights) and then continues to connect. If Node.js is already there, it skips the install. You can use this instead of the Node.js step in [What you need](#what-you-need).
99
+ If the command errors with `'npx' is not recognized`, Node.js isn't installed. On Windows, install Node.js first and run the command again. On Mac and Linux, this one command installs Node.js (no admin rights) and then continues to connect, skipping the install if Node.js is already there.
100
100
 
101
- - **Windows** (open PowerShell and paste):
101
+ - **Windows** (open PowerShell and paste): installs the official Node.js package; then open a new PowerShell window and run the Step 2 command again. Without winget, get the LTS installer from [nodejs.org](https://nodejs.org/).
102
102
 
103
103
  ```
104
- irm https://app.datalab.tools/install/mcp.ps1 | iex
104
+ winget install OpenJS.NodeJS.LTS
105
105
  ```
106
106
 
107
107
  - **Mac · Linux** (open the terminal and paste):
@@ -151,10 +151,10 @@ diagnosis, Place reputation, Pumasi, photo/video production, and automatic tool
151
151
  discovery. Press Enter to keep the recommended set.
152
152
 
153
153
  ```
154
- npx -y @modootoday/datalab-extension-mcp@1.11.2 skills list
155
- npx -y @modootoday/datalab-extension-mcp@1.11.2 skills status
156
- npx -y @modootoday/datalab-extension-mcp@1.11.2 skills attach <name>
157
- npx -y @modootoday/datalab-extension-mcp@1.11.2 skills detach <name>
154
+ npx -y @modootoday/datalab-extension-mcp@1.11.4 skills list
155
+ npx -y @modootoday/datalab-extension-mcp@1.11.4 skills status
156
+ npx -y @modootoday/datalab-extension-mcp@1.11.4 skills attach <name>
157
+ npx -y @modootoday/datalab-extension-mcp@1.11.4 skills detach <name>
158
158
  ```
159
159
 
160
160
  - `list` shows everything in this version, name and description.
@@ -244,7 +244,7 @@ Exactly what it says. Open Chrome, keep the DataLab Tools side panel open, and i
244
244
 
245
245
  That's expected — **it's deliberate.**
246
246
 
247
- There are 211 tools. Sending all of them as a list runs into the cap each AI app
247
+ There are 213 tools. Sending all of them as a list runs into the cap each AI app
248
248
  sets: some quietly drop whatever doesn't fit, some refuse the connection outright.
249
249
  So only the discovery tools go in the list, and **the rest are selected from the
250
250
  user's intent and handed over as an answer.**
@@ -275,10 +275,10 @@ Start it again after a full quit and it picks the tools up. If they still aren't
275
275
  - **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.)
276
276
 
277
277
  ```
278
- npx -y @modootoday/datalab-extension-mcp@1.11.2 install
278
+ npx -y @modootoday/datalab-extension-mcp@1.11.4 install
279
279
  ```
280
280
 
281
- - **`'npx' is not recognized...`** — Node.js isn't installed. The "No Node.js?" one-liner in Step 2 is the simplest fix.
281
+ - **`'npx' is not recognized...`** — Node.js isn't installed. Follow "No Node.js?" in Step 2, then run the command again.
282
282
 
283
283
  - **None of the above** — the connector writes down what happened to it. One file:
284
284
 
@@ -305,7 +305,7 @@ Start it again after a full quit and it picks the tools up. If they still aren't
305
305
  1. Run this in a terminal (it removes the entries registered in your AI apps).
306
306
 
307
307
  ```
308
- npx -y @modootoday/datalab-extension-mcp@1.11.2 uninstall
308
+ npx -y @modootoday/datalab-extension-mcp@1.11.4 uninstall
309
309
  ```
310
310
 
311
311
  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.
@@ -322,6 +322,7 @@ Deleting the extension also renders any leftover connector inert. A leftover lin
322
322
  - **Do I keep it always on?** No. It connects only while the panel is open.
323
323
  - **What data does it read?** Blog stats, keywords, ads — what DataLab Tools shows. Scoped to the Naver account you're logged into in Chrome.
324
324
  - **Other computers?** Yes, but set it up once per computer; the connection key is per-computer too.
325
+
325
326
  </details>
326
327
 
327
328
  ## Developer appendix
package/README.md CHANGED
@@ -12,11 +12,11 @@ Claude 같은 AI 앱을 데이터랩툴즈에 연결해 주는 작은 무료 프
12
12
 
13
13
  컴퓨터마다 한 번씩만 준비하면 돼요.
14
14
 
15
- | 준비물 | 어떻게 |
16
- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
17
- | Chrome + 데이터랩툴즈 확장 | 아직 없다면 [app.datalab.tools](https://app.datalab.tools/)에서 설치해요. |
18
- | AI 앱 | 질문을 받을 프로그램이에요. **컴퓨터에 설치하는 앱**만 돼요(웹사이트는 안 돼요). Claude Desktop은 [claude.ai](https://claude.ai/download)에서 무료로, ChatGPT는 **데스크톱 앱만** 되고 [openai.com](https://openai.com/)에서 받아요. |
19
- | Node.js | 연결 프로그램을 실행하는 무료 프로그램이에요. [nodejs.org](https://nodejs.org/)의 **LTS** 버튼으로 받아요. 번거로우면 아래 [2단계](#2단계--설치)의 한 줄 설치가 Node.js까지 한 번에 깔아 줘요(관리자 권한도 필요 없어요). |
15
+ | 준비물 | 어떻게 |
16
+ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
17
+ | Chrome + 데이터랩툴즈 확장 | 아직 없다면 [app.datalab.tools](https://app.datalab.tools/)에서 설치해요. |
18
+ | AI 앱 | 질문을 받을 프로그램이에요. **컴퓨터에 설치하는 앱**만 돼요(웹사이트는 안 돼요). Claude Desktop은 [claude.ai](https://claude.ai/download)에서 무료로, ChatGPT는 **데스크톱 앱만** 되고 [openai.com](https://openai.com/)에서 받아요. |
19
+ | Node.js | 연결 프로그램을 실행하는 무료 프로그램이에요. [nodejs.org](https://nodejs.org/)의 **LTS** 버튼으로 받아요. Windows는 `winget install OpenJS.NodeJS.LTS` 로도 설치돼요. Mac · Linux는 아래 [2단계](#2단계--설치)의 한 줄 설치가 Node.js까지 한 번에 깔아 줘요. |
20
20
 
21
21
  > 회사 컴퓨터는 보안 정책 때문에 설치가 막혀 있을 수 있어요. 시작 전에 회사 IT 담당자와 먼저 상의하면 시간을 아낄 수 있어요.
22
22
 
@@ -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.11.2 install
58
+ npx -y @modootoday/datalab-extension-mcp@1.11.4 install
59
59
  ```
60
60
 
61
61
  영어 글자가 여러 줄 올라오는 건 정상이에요. 이 명령은 연결 프로그램을 다운로드해 실행할 뿐, 다른 건 건드리지 않아요.
@@ -84,12 +84,12 @@ Claude 같은 AI 앱을 데이터랩툴즈에 연결해 주는 작은 무료 프
84
84
  <details>
85
85
  <summary><strong>Node.js가 없다면 — 한 줄로 한 번에 설치</strong></summary>
86
86
 
87
- 위 명령이 `'npx'을(를) 찾을 수 없습니다` 오류를 내면 Node.js가 없는 거예요. 아래 명령 하나면 Node.js까지 설치(관리자 권한 없이)한 뒤 연결까지 이어 줘요. 이미 있으면 설치는 건너뛰어요. 이 방법을 [준비물](#준비물)의 Node.js 설치 대신 바로 써도 돼요.
87
+ 위 명령이 `'npx'을(를) 찾을 수 없습니다` 오류를 내면 Node.js가 없는 거예요. Windows는 Node.js를 먼저 설치하고 위 명령을 다시 실행해요. Mac · Linux는 아래 명령 하나면 Node.js까지 설치(관리자 권한 없이)한 뒤 연결까지 이어 주고, 이미 있으면 설치는 건너뛰어요.
88
88
 
89
- - **Windows** (PowerShell을 열고 붙여넣어요):
89
+ - **Windows** (PowerShell을 열고 붙여넣어요): Node.js 공식 패키지를 설치한 뒤, PowerShell을 새로 열고 위 2단계 명령을 다시 실행해요. winget이 없으면 [nodejs.org](https://nodejs.org/)에서 LTS를 받아 설치해도 돼요.
90
90
 
91
91
  ```
92
- irm https://app.datalab.tools/install/mcp.ps1 | iex
92
+ winget install OpenJS.NodeJS.LTS
93
93
  ```
94
94
 
95
95
  - **Mac · Linux** (터미널을 열고 붙여넣어요):
@@ -137,10 +137,10 @@ Claude 같은 AI 앱을 데이터랩툴즈에 연결해 주는 작은 무료 프
137
137
  도움말을 번호로 여러 개 고를 수 있어요. Enter를 누르면 추천 구성만 설치됩니다.
138
138
 
139
139
  ```
140
- npx -y @modootoday/datalab-extension-mcp@1.11.2 skills list
141
- npx -y @modootoday/datalab-extension-mcp@1.11.2 skills status
142
- npx -y @modootoday/datalab-extension-mcp@1.11.2 skills attach <이름>
143
- npx -y @modootoday/datalab-extension-mcp@1.11.2 skills detach <이름>
140
+ npx -y @modootoday/datalab-extension-mcp@1.11.4 skills list
141
+ npx -y @modootoday/datalab-extension-mcp@1.11.4 skills status
142
+ npx -y @modootoday/datalab-extension-mcp@1.11.4 skills attach <이름>
143
+ npx -y @modootoday/datalab-extension-mcp@1.11.4 skills detach <이름>
144
144
  ```
145
145
 
146
146
  - `list` 는 이 버전에 들어 있는 전부를 이름과 설명으로 보여 줘요.
@@ -214,7 +214,7 @@ MCP 를 켜 두었지만 **네이버 페이지가 한 곳도 열려 있지 않
214
214
 
215
215
  정상이에요. **일부러 그렇게 보냅니다.**
216
216
 
217
- 전체 도구는 211개인데, 그걸 전부 목록으로 보내면 AI 앱마다 정해 둔 상한에 걸려요.
217
+ 전체 도구는 213개인데, 그걸 전부 목록으로 보내면 AI 앱마다 정해 둔 상한에 걸려요.
218
218
  어떤 앱은 넘치는 만큼 조용히 잘라내고, 어떤 앱은 아예 연결을 거부해요. 그래서 자주 쓰는
219
219
  탐색 도구만 목록에 올리고, 나머지는 **AI가 요청 의도를 찾아 답으로 건네받습니다.**
220
220
 
@@ -242,10 +242,10 @@ AI가 "그런 기능은 없다" 고 답하면, **"숨은 도구를 찾아보고
242
242
  - **연결이 사라졌을 때** — 처음 실행했던 그 명령을 한 번 더 실행하면 안전하게 다시 등록돼요. (일부 AI 앱이 설정 파일을 정리하며 연결을 지우기도 해요.)
243
243
 
244
244
  ```
245
- npx -y @modootoday/datalab-extension-mcp@1.11.2 install
245
+ npx -y @modootoday/datalab-extension-mcp@1.11.4 install
246
246
  ```
247
247
 
248
- - **`'npx'을(는) 내부 또는 외부 명령...`** — Node.js가 없는 거예요. 위 2단계의 "Node.js가 없다면" 한 줄 설치가 가장 간단해요.
248
+ - **`'npx'을(는) 내부 또는 외부 명령...`** — Node.js가 없는 거예요. 위 2단계의 "Node.js가 없다면"을 따라 설치한 뒤 다시 실행해요.
249
249
 
250
250
  - **위 어느 것도 아닐 때** — 연결 프로그램이 무슨 일을 겪었는지 기록해 둬요. 파일 하나만 열어 보면 돼요.
251
251
 
@@ -272,7 +272,7 @@ AI가 "그런 기능은 없다" 고 답하면, **"숨은 도구를 찾아보고
272
272
  1. 터미널에서 아래 명령을 실행해요(AI 앱에 등록된 설정을 지워 줘요).
273
273
 
274
274
  ```
275
- npx -y @modootoday/datalab-extension-mcp@1.11.2 uninstall
275
+ npx -y @modootoday/datalab-extension-mcp@1.11.4 uninstall
276
276
  ```
277
277
 
278
278
  2. 확장 설정에서 **MCP 연결을 꺼요.** 패널이 브릿지라, 꺼 두면 어딘가 설정 줄이 남아 있어도 AI 앱이 아무것도 조회할 수 없어요.
@@ -289,6 +289,7 @@ AI가 "그런 기능은 없다" 고 답하면, **"숨은 도구를 찾아보고
289
289
  - **항상 켜 둬야 하나요?** 아니요. 패널이 열려 있을 때만 연결돼요.
290
290
  - **어떤 데이터를 읽나요?** 블로그 통계·키워드·광고 등 데이터랩툴즈가 보여 주는 것들이에요. 기준은 지금 Chrome에 로그인해 둔 네이버 계정이에요.
291
291
  - **다른 컴퓨터에서도 되나요?** 네. 컴퓨터마다 한 번씩 설치하고, 연결 키도 컴퓨터마다 따로예요.
292
+
292
293
  </details>
293
294
 
294
295
  ## 개발자 부록
@@ -2,7 +2,7 @@ import {
2
2
  __export
3
3
  } from "./chunk-MLKGABMK.js";
4
4
 
5
- // ../mcp-server/dist/chunk-X7PKNPNJ.js
5
+ // ../mcp-server/dist/chunk-PCVVHMPP.js
6
6
  import { createHash } from "crypto";
7
7
  import { createHash as createHash2, randomUUID } from "crypto";
8
8
  import { mkdir, readFile, readdir, rm, writeFile } from "fs/promises";
@@ -20074,6 +20074,8 @@ var TOOL_TIERS = Object.freeze({
20074
20074
  // ── Post editor: reads ──────────────────────────────────────────
20075
20075
  editor_read: "read",
20076
20076
  editor_read_structure: "read",
20077
+ editor_read_document: "read",
20078
+ editor_operation_status: "read",
20077
20079
  // ── Post editor: writes ─────────────────────────────────────────
20078
20080
  //
20079
20081
  // Every injection is bracketed by a vendor autosave, plus a separate copy
@@ -22749,41 +22751,57 @@ function runDaemon(env, deps = {}) {
22749
22751
 
22750
22752
  // ../mcp-server/dist/index.js
22751
22753
  import { spawnSync } from "child_process";
22752
- function killPortOwnerCommand(platform, port) {
22753
- const p = String(port);
22754
+ function parseNetstatListeners(output3, port) {
22755
+ const pids = /* @__PURE__ */ new Set();
22756
+ for (const line of output3.split(/\r?\n/)) {
22757
+ const cols = line.trim().split(/\s+/);
22758
+ if (cols.length < 5 || cols[0]?.toUpperCase() !== "TCP") continue;
22759
+ if (cols[3]?.toUpperCase() !== "LISTENING") continue;
22760
+ const local = cols[1] ?? "";
22761
+ if (local.slice(local.lastIndexOf(":") + 1) !== String(port)) continue;
22762
+ const pid = Number(cols[4]);
22763
+ if (Number.isInteger(pid) && pid > 0) pids.add(pid);
22764
+ }
22765
+ return [...pids];
22766
+ }
22767
+ function parseLsofPids(output3) {
22768
+ return output3.split(/\r?\n/).map((l) => Number(l.trim())).filter((n) => Number.isInteger(n) && n > 0);
22769
+ }
22770
+ function listenerQuery(platform, port) {
22754
22771
  if (platform === "win32") {
22755
- return {
22756
- command: "powershell",
22757
- args: [
22758
- "-NoProfile",
22759
- "-Command",
22760
- `Get-NetTCPConnection -LocalPort ${p} -State Listen -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }`
22761
- ],
22762
- shell: true
22763
- };
22772
+ return { command: "netstat", args: ["-ano", "-p", "TCP"] };
22764
22773
  }
22765
22774
  if (platform === "darwin" || platform === "linux") {
22766
- return {
22767
- command: "sh",
22768
- args: [
22769
- "-c",
22770
- `pids=$(lsof -ti tcp:${p} -sTCP:LISTEN 2>/dev/null); if [ -n "$pids" ]; then kill -9 $pids; fi`
22771
- ],
22772
- shell: false
22773
- };
22775
+ return { command: "lsof", args: ["-ti", `tcp:${port}`, "-sTCP:LISTEN"] };
22774
22776
  }
22775
22777
  return null;
22776
22778
  }
22779
+ function listenerPids(platform, port, run, selfPid) {
22780
+ const query = listenerQuery(platform, port);
22781
+ if (query === null) return [];
22782
+ const output3 = run(query.command, query.args);
22783
+ const pids = platform === "win32" ? parseNetstatListeners(output3, port) : parseLsofPids(output3);
22784
+ return pids.filter((pid) => pid !== selfPid);
22785
+ }
22786
+ function runQuiet(command, args) {
22787
+ const res = spawnSync(command, args, {
22788
+ encoding: "utf8",
22789
+ windowsHide: true,
22790
+ timeout: 1e4
22791
+ });
22792
+ return typeof res.stdout === "string" ? res.stdout : "";
22793
+ }
22777
22794
  function killPortOwner(port, platform = process.platform) {
22778
22795
  if (process.env["VITEST"] !== void 0) return false;
22779
- const cmd = killPortOwnerCommand(platform, port);
22780
- if (cmd === null) return false;
22781
- try {
22782
- spawnSync(cmd.command, cmd.args, { shell: cmd.shell, stdio: "ignore" });
22783
- return true;
22784
- } catch {
22785
- return false;
22796
+ let signalled = false;
22797
+ for (const pid of listenerPids(platform, port, runQuiet, process.pid)) {
22798
+ try {
22799
+ process.kill(pid, "SIGKILL");
22800
+ signalled = true;
22801
+ } catch {
22802
+ }
22786
22803
  }
22804
+ return signalled;
22787
22805
  }
22788
22806
 
22789
22807
  // src/adapter.ts
@@ -42888,6 +42906,8 @@ var TOOL_TIERS2 = Object.freeze({
42888
42906
  // ── Post editor: reads ──────────────────────────────────────────
42889
42907
  editor_read: "read",
42890
42908
  editor_read_structure: "read",
42909
+ editor_read_document: "read",
42910
+ editor_operation_status: "read",
42891
42911
  // ── Post editor: writes ─────────────────────────────────────────
42892
42912
  //
42893
42913
  // Every injection is bracketed by a vendor autosave, plus a separate copy
@@ -43631,6 +43651,7 @@ async function pushToolChanges(deps) {
43631
43651
  prev = wait;
43632
43652
  }
43633
43653
  }
43654
+ var SESSION_EXIT_GRACE_MS = 200;
43634
43655
  async function selfUpdateAndRestart(selfVersion, log, deps = {}) {
43635
43656
  if (!selfVersion) return false;
43636
43657
  const target = await (deps.resolve ?? canonicalVersion)();
@@ -43699,11 +43720,22 @@ async function runAdapter(deps = {}) {
43699
43720
  const port = deps.port ?? DEFAULT_PORT;
43700
43721
  const controller = new AbortController();
43701
43722
  const prevOnClose = transport.onclose;
43723
+ const exit = deps.exit ?? (deps.transport ? null : process.exit.bind(process));
43702
43724
  transport.onclose = () => {
43703
43725
  if (watch) clearInterval(watch);
43704
43726
  controller.abort();
43705
43727
  prevOnClose?.();
43728
+ if (exit) setTimeout(exit, SESSION_EXIT_GRACE_MS, 0);
43706
43729
  };
43730
+ const stdin = deps.stdin ?? (deps.transport ? null : process.stdin);
43731
+ if (stdin) {
43732
+ const endSession = () => {
43733
+ void transport.close().catch(() => {
43734
+ });
43735
+ };
43736
+ stdin.once("end", endSession);
43737
+ stdin.once("close", endSession);
43738
+ }
43707
43739
  void pushToolChanges({
43708
43740
  mcpUrl: `http://${host}:${port}/mcp`,
43709
43741
  notifyHost: () => server.notification({ method: "notifications/tools/list_changed" }).catch(() => {
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  dispatchCli,
4
4
  runAdapter,
5
5
  runDaemon
6
- } from "./chunk-U4NWY74P.js";
6
+ } from "./chunk-RF6L4PSA.js";
7
7
  import "./chunk-MLKGABMK.js";
8
8
 
9
9
  // src/cli.ts
@@ -15,7 +15,7 @@ function log(message) {
15
15
  `);
16
16
  }
17
17
  async function runInstaller(sub, argv) {
18
- const { createNodeIo, runInstall, runUninstall } = await import("./dist-X23C5DSV.js");
18
+ const { createNodeIo, runInstall, runUninstall } = await import("./dist-ITPO7PMQ.js");
19
19
  const opts = { version: VERSION };
20
20
  for (let i = 0; i < argv.length; i += 1) {
21
21
  const flag = argv[i];
@@ -72,7 +72,7 @@ async function main() {
72
72
  attachSkill,
73
73
  detachSkill,
74
74
  skillsPayloadRoot
75
- } = await import("./dist-X23C5DSV.js");
75
+ } = await import("./dist-ITPO7PMQ.js");
76
76
  const io = createNodeIo();
77
77
  const root = skillsPayloadRoot();
78
78
  const code = await runSkills(argv, {
@@ -3,7 +3,7 @@ import "./chunk-MLKGABMK.js";
3
3
  // ../mcp-installer/dist/index.js
4
4
  import { join as joinNative } from "path";
5
5
  import { fileURLToPath } from "url";
6
- import { spawn as nodeSpawn } from "child_process";
6
+ import { execFile, spawn as nodeSpawn } from "child_process";
7
7
  import { promises as fs } from "fs";
8
8
  import { homedir } from "os";
9
9
  import { createInterface } from "readline";
@@ -1260,6 +1260,47 @@ function validateUninstallOptions(opts) {
1260
1260
  }
1261
1261
  return null;
1262
1262
  }
1263
+ function parseNetstatListeners(output, port) {
1264
+ const pids = /* @__PURE__ */ new Set();
1265
+ for (const line of output.split(/\r?\n/)) {
1266
+ const cols = line.trim().split(/\s+/);
1267
+ if (cols.length < 5 || cols[0]?.toUpperCase() !== "TCP") continue;
1268
+ if (cols[3]?.toUpperCase() !== "LISTENING") continue;
1269
+ const local = cols[1] ?? "";
1270
+ if (local.slice(local.lastIndexOf(":") + 1) !== String(port)) continue;
1271
+ const pid = Number(cols[4]);
1272
+ if (Number.isInteger(pid) && pid > 0) pids.add(pid);
1273
+ }
1274
+ return [...pids];
1275
+ }
1276
+ function parseLsofPids(output) {
1277
+ return output.split(/\r?\n/).map((l) => Number(l.trim())).filter((n) => Number.isInteger(n) && n > 0);
1278
+ }
1279
+ function listenerQuery(platform, port) {
1280
+ if (platform === "win32") {
1281
+ return { command: "netstat", args: ["-ano", "-p", "TCP"] };
1282
+ }
1283
+ if (platform === "darwin" || platform === "linux") {
1284
+ return { command: "lsof", args: ["-ti", `tcp:${port}`, "-sTCP:LISTEN"] };
1285
+ }
1286
+ return null;
1287
+ }
1288
+ async function endPortListener(platform, port, deps) {
1289
+ const query = listenerQuery(platform, port);
1290
+ if (query === null) return false;
1291
+ const output = await deps.capture(query.command, query.args);
1292
+ const pids = platform === "win32" ? parseNetstatListeners(output, port) : parseLsofPids(output);
1293
+ let signalled = false;
1294
+ for (const pid of pids) {
1295
+ if (pid === deps.selfPid) continue;
1296
+ try {
1297
+ deps.killPid(pid);
1298
+ signalled = true;
1299
+ } catch {
1300
+ }
1301
+ }
1302
+ return signalled;
1303
+ }
1263
1304
  var DEFAULT_PORT2 = 8765;
1264
1305
  var HOST = "127.0.0.1";
1265
1306
  var CONNECTOR_NAME = "datalab-extension-mcp-server";
@@ -1319,31 +1360,6 @@ async function probePort(fetchImpl, port) {
1319
1360
  return { state: "foreign", version: null };
1320
1361
  }
1321
1362
  }
1322
- function killCommand(platform, port) {
1323
- const p = String(port);
1324
- if (platform === "win32") {
1325
- return {
1326
- command: "powershell",
1327
- args: [
1328
- "-NoProfile",
1329
- "-Command",
1330
- `Get-NetTCPConnection -LocalPort ${p} -State Listen -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }`
1331
- ],
1332
- shell: true
1333
- };
1334
- }
1335
- if (platform === "darwin" || platform === "linux") {
1336
- return {
1337
- command: "sh",
1338
- args: [
1339
- "-c",
1340
- `pids=$(lsof -ti tcp:${p} -sTCP:LISTEN 2>/dev/null); if [ -n "$pids" ]; then kill -9 $pids; fi`
1341
- ],
1342
- shell: false
1343
- };
1344
- }
1345
- return null;
1346
- }
1347
1363
  async function reclaimPort(io, token, deps = {}) {
1348
1364
  const asked = typeof deps.port === "string" ? Number(deps.port) : deps.port;
1349
1365
  const port = asked !== void 0 && Number.isInteger(asked) && asked > 0 ? asked : DEFAULT_PORT2;
@@ -1367,9 +1383,12 @@ async function reclaimPort(io, token, deps = {}) {
1367
1383
  return false;
1368
1384
  };
1369
1385
  if (stood?.status === 200 && await gone()) return { kind: "retired" };
1370
- const kill = killCommand(io.platform, port);
1371
- if (kill === null) return { kind: "failed" };
1372
- await io.spawn(kill.command, kill.args, { shell: kill.shell });
1386
+ const ended = await endPortListener(io.platform, port, {
1387
+ capture: io.capture,
1388
+ killPid: io.killPid,
1389
+ selfPid: process.pid
1390
+ });
1391
+ if (!ended) return { kind: "failed" };
1373
1392
  return await gone() ? { kind: "forced" } : { kind: "failed" };
1374
1393
  }
1375
1394
  var BACKUP_KEEP = 3;
@@ -2185,7 +2204,7 @@ async function runInstall(opts, io) {
2185
2204
  if (staged === null || staged.length === 0) {
2186
2205
  printBody(
2187
2206
  (line) => io.out(line),
2188
- "\uC774 \uC124\uCE58 \uD30C\uC77C\uC5D0\uB294 \uC2A4\uD0AC\uC774 \uC5C6\uC5B4 \uC5F0\uACB0 \uD504\uB85C\uADF8\uB7A8\uB9CC \uC124\uCE58\uD569\uB2C8\uB2E4."
2207
+ "\uC774 \uC124\uCE58 \uD30C\uC77C\uC5D0\uB294 \uC2A4\uD0AC\uC774 \uC5C6\uC5B4 \uC5F0\uACB0 \uD504\uB85C\uADF8\uB7A8\uB9CC \uC124\uCE58\uD574\uC694."
2189
2208
  );
2190
2209
  } else {
2191
2210
  skillPlan = await chooseSkillSelection(io, staged, {
@@ -2199,7 +2218,7 @@ async function runInstall(opts, io) {
2199
2218
  }
2200
2219
  printBody(
2201
2220
  (line) => io.out(line),
2202
- `\uCD94\uCC9C ${skillPlan.defaultCount}\uAC1C + \uC120\uD0DD ${skillPlan.selected.size}\uAC1C = ${skillPlan.total}\uAC1C\uB97C \uC124\uCE58\uD569\uB2C8\uB2E4.`
2221
+ `\uCD94\uCC9C ${skillPlan.defaultCount}\uAC1C + \uC120\uD0DD ${skillPlan.selected.size}\uAC1C = ${skillPlan.total}\uAC1C\uB97C \uC124\uCE58\uD574\uC694.`
2203
2222
  );
2204
2223
  }
2205
2224
  }
@@ -2525,6 +2544,21 @@ function createNodeIo() {
2525
2544
  });
2526
2545
  });
2527
2546
  },
2547
+ capture(command, args) {
2548
+ return new Promise((resolve) => {
2549
+ execFile(
2550
+ command,
2551
+ args,
2552
+ { windowsHide: true, timeout: 1e4 },
2553
+ (err, stdout) => {
2554
+ resolve(err && !stdout ? "" : String(stdout ?? ""));
2555
+ }
2556
+ );
2557
+ });
2558
+ },
2559
+ killPid(pid) {
2560
+ process.kill(pid, "SIGKILL");
2561
+ },
2528
2562
  async ask(question) {
2529
2563
  const rl = createInterface({
2530
2564
  input: process.stdin,
package/dist/index.d.ts CHANGED
@@ -132,6 +132,10 @@ interface RunAdapterDeps extends AdapterServerDeps {
132
132
  subscribe?: SubscribeImpl;
133
133
  /** false pins this build — `--no-self-update`. Default promotes to canonical. */
134
134
  selfUpdate?: boolean;
135
+ /** Injected in tests; the host's end of the session. Defaults to process.stdin. */
136
+ stdin?: NodeJS.EventEmitter;
137
+ /** Injected in tests; defaults to process.exit. */
138
+ exit?: (code: number) => void;
135
139
  }
136
140
  declare function runAdapter(deps?: RunAdapterDeps): Promise<void>;
137
141
  interface CliHandlers {
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  dispatchCli,
4
4
  ensureDaemonRunning,
5
5
  runAdapter
6
- } from "./chunk-U4NWY74P.js";
6
+ } from "./chunk-RF6L4PSA.js";
7
7
  import "./chunk-MLKGABMK.js";
8
8
  export {
9
9
  createAdapterServer,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modootoday/datalab-extension-mcp",
3
- "version": "1.11.2",
3
+ "version": "1.11.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",