@modootoday/datalab-extension-mcp 1.8.15 → 1.8.17

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
@@ -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.15 install
64
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 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.15 skills list
149
- npx -y @modootoday/datalab-extension-mcp@1.8.15 skills status
150
- npx -y @modootoday/datalab-extension-mcp@1.8.15 skills attach <name>
151
- npx -y @modootoday/datalab-extension-mcp@1.8.15 skills detach <name>
148
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 skills list
149
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 skills status
150
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 skills attach <name>
151
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 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.15 browsers add "Work Chrome"
171
- npx -y @modootoday/datalab-extension-mcp@1.8.15 browsers list
172
- npx -y @modootoday/datalab-extension-mcp@1.8.15 browsers revoke <handle>
173
- npx -y @modootoday/datalab-extension-mcp@1.8.15 browsers repair
170
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 browsers add "Work Chrome"
171
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 browsers list
172
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 browsers revoke <handle>
173
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 browsers repair
174
174
  ```
175
175
 
176
176
  `add` **asks you to paste the install key** — the same value you used to
@@ -271,7 +271,7 @@ Start it again after a full quit and it picks the tools up. If they still aren't
271
271
  - **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
272
 
273
273
  ```
274
- npx -y @modootoday/datalab-extension-mcp@1.8.15 install
274
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 install
275
275
  ```
276
276
 
277
277
  - **`'npx' is not recognized...`** — Node.js isn't installed. The "No Node.js?" one-liner in Step 2 is the simplest fix.
@@ -301,7 +301,7 @@ Start it again after a full quit and it picks the tools up. If they still aren't
301
301
  1. Run this in a terminal (it removes the entries registered in your AI apps).
302
302
 
303
303
  ```
304
- npx -y @modootoday/datalab-extension-mcp@1.8.15 uninstall
304
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 uninstall
305
305
  ```
306
306
 
307
307
  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.15 install
58
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 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.15 skills list
137
- npx -y @modootoday/datalab-extension-mcp@1.8.15 skills status
138
- npx -y @modootoday/datalab-extension-mcp@1.8.15 skills attach <이름>
139
- npx -y @modootoday/datalab-extension-mcp@1.8.15 skills detach <이름>
136
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 skills list
137
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 skills status
138
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 skills attach <이름>
139
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 skills detach <이름>
140
140
  ```
141
141
 
142
142
  - `list` 는 이 버전에 들어 있는 전부를 이름과 설명으로 보여 줘요.
@@ -155,10 +155,10 @@ npx -y @modootoday/datalab-extension-mcp@1.8.15 skills detach <이름>
155
155
  확장 설정 → **다른 AI 앱에 연결 (MCP)** → 3번 아래의 **[다른 브라우저 추가 (선택)]** 를 펼쳐 [등록 코드 만들기]를 눌러도 되고, 터미널을 쓰셔도 돼요.
156
156
 
157
157
  ```
158
- npx -y @modootoday/datalab-extension-mcp@1.8.15 browsers add "회사 크롬"
159
- npx -y @modootoday/datalab-extension-mcp@1.8.15 browsers list
160
- npx -y @modootoday/datalab-extension-mcp@1.8.15 browsers revoke <handle>
161
- npx -y @modootoday/datalab-extension-mcp@1.8.15 browsers repair
158
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 browsers add "회사 크롬"
159
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 browsers list
160
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 browsers revoke <handle>
161
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 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.15 install
240
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 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.15 uninstall
270
+ npx -y @modootoday/datalab-extension-mcp@1.8.17 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-QHEJF6FK.js";
4
+ } from "./chunk-G34WU5WC.js";
5
5
  import {
6
6
  TOKEN_PROMPT_GUIDE,
7
7
  TOKEN_PROMPT_QUESTION,
8
8
  TOKEN_REQUIRED_NON_INTERACTIVE
9
- } from "./chunk-5IYKI4EG.js";
9
+ } from "./chunk-6GGVN4TT.js";
10
10
  import "./chunk-MLKGABMK.js";
11
11
 
12
12
  // src/browsers.ts
@@ -2,9 +2,10 @@ import {
2
2
  DEFAULT_HOST,
3
3
  DEFAULT_PORT,
4
4
  mediaToolDescriptors,
5
+ readTakeoverSecret,
5
6
  spawnDaemon,
6
7
  tryConnect
7
- } from "./chunk-QHEJF6FK.js";
8
+ } from "./chunk-G34WU5WC.js";
8
9
  import {
9
10
  __export
10
11
  } from "./chunk-MLKGABMK.js";
@@ -15630,6 +15631,15 @@ async function ensureDaemonRunning(deps = {}) {
15630
15631
  const readVersion = deps.readVersion ?? defaultReadVersion;
15631
15632
  const shutdown = deps.shutdown ?? defaultShutdown;
15632
15633
  const checkAuthority = deps.checkAuthority ?? defaultCheckAuthority;
15634
+ const readTakeover = deps.readTakeover ?? (() => readTakeoverSecret());
15635
+ const waitForPortRelease = async () => {
15636
+ for (let i = 0; i < attempts; i += 1) {
15637
+ const socket = await connect(host, port, PROBE_TIMEOUT_MS);
15638
+ if (!socket) break;
15639
+ socket.destroy();
15640
+ await sleep(intervalMs);
15641
+ }
15642
+ };
15633
15643
  const spawnAndWait = async () => {
15634
15644
  spawn(daemonEntry, ["serve"]);
15635
15645
  for (let i = 0; i < attempts; i += 1) {
@@ -15659,8 +15669,15 @@ async function ensureDaemonRunning(deps = {}) {
15659
15669
  }
15660
15670
  if (!isOlderVersion(running, selfVersion)) {
15661
15671
  if (await checkAuthority(base, token)) return;
15672
+ const secret = readTakeover();
15673
+ if (secret !== null && await shutdown(base, secret)) {
15674
+ log("retiring the connector that holds this port with a different token");
15675
+ await waitForPortRelease();
15676
+ await spawnAndWait();
15677
+ return;
15678
+ }
15662
15679
  throw new Error(
15663
- `connector authority conflict on port ${String(port)}; every MCP host on this machine must use the same DataLab workbench token`
15680
+ `connector authority conflict on port ${String(port)}; a connector started with a different DataLab workbench token holds it. Quit every AI app so it idles out (about 5 minutes at zero connections), then start this one again.`
15664
15681
  );
15665
15682
  }
15666
15683
  log(`updating the connector: replacing ${running} with ${selfVersion}`);
@@ -15673,12 +15690,7 @@ async function ensureDaemonRunning(deps = {}) {
15673
15690
  `connector authority conflict on port ${String(port)}; the running connector did not accept this workbench token`
15674
15691
  );
15675
15692
  }
15676
- for (let i = 0; i < attempts; i += 1) {
15677
- const socket = await connect(host, port, PROBE_TIMEOUT_MS);
15678
- if (!socket) break;
15679
- socket.destroy();
15680
- await sleep(intervalMs);
15681
- }
15693
+ await waitForPortRelease();
15682
15694
  await spawnAndWait();
15683
15695
  }
15684
15696
  function createAdapterServer(deps = {}) {
@@ -1171,6 +1171,98 @@ function validateUninstallOptions(opts) {
1171
1171
  }
1172
1172
  return null;
1173
1173
  }
1174
+ var DEFAULT_PORT2 = 8765;
1175
+ var HOST = "127.0.0.1";
1176
+ var CONNECTOR_NAME = "datalab-extension-mcp-server";
1177
+ var PROBE_TIMEOUT_MS = 2e3;
1178
+ var RELEASE_ATTEMPTS = 20;
1179
+ var RELEASE_INTERVAL_MS = 150;
1180
+ function url(port, path) {
1181
+ return `http://${HOST}:${String(port)}${path}`;
1182
+ }
1183
+ async function get(fetchImpl, target) {
1184
+ try {
1185
+ return await fetchImpl(target, {
1186
+ signal: AbortSignal.timeout(PROBE_TIMEOUT_MS)
1187
+ });
1188
+ } catch {
1189
+ return null;
1190
+ }
1191
+ }
1192
+ async function post(fetchImpl, target, body) {
1193
+ try {
1194
+ return await fetchImpl(target, {
1195
+ method: "POST",
1196
+ headers: { "content-type": "application/json" },
1197
+ body: JSON.stringify(body),
1198
+ signal: AbortSignal.timeout(PROBE_TIMEOUT_MS)
1199
+ });
1200
+ } catch {
1201
+ return null;
1202
+ }
1203
+ }
1204
+ async function probePort(fetchImpl, port) {
1205
+ const res = await get(fetchImpl, url(port, "/bridge/health"));
1206
+ if (res === null) return "free";
1207
+ if (!res.ok) return "foreign";
1208
+ try {
1209
+ const body = await res.json();
1210
+ return body.name === CONNECTOR_NAME ? "ours" : "foreign";
1211
+ } catch {
1212
+ return "foreign";
1213
+ }
1214
+ }
1215
+ function killCommand(platform, port) {
1216
+ const p = String(port);
1217
+ if (platform === "win32") {
1218
+ return {
1219
+ command: "powershell",
1220
+ args: [
1221
+ "-NoProfile",
1222
+ "-Command",
1223
+ `Get-NetTCPConnection -LocalPort ${p} -State Listen -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }`
1224
+ ],
1225
+ shell: true
1226
+ };
1227
+ }
1228
+ if (platform === "darwin" || platform === "linux") {
1229
+ return {
1230
+ command: "sh",
1231
+ args: [
1232
+ "-c",
1233
+ `pids=$(lsof -ti tcp:${p} -sTCP:LISTEN 2>/dev/null); if [ -n "$pids" ]; then kill -9 $pids; fi`
1234
+ ],
1235
+ shell: false
1236
+ };
1237
+ }
1238
+ return null;
1239
+ }
1240
+ async function reclaimPort(io, token, deps = {}) {
1241
+ const asked = typeof deps.port === "string" ? Number(deps.port) : deps.port;
1242
+ const port = asked !== void 0 && Number.isInteger(asked) && asked > 0 ? asked : DEFAULT_PORT2;
1243
+ const fetchImpl = deps.fetchImpl ?? io.fetch ?? globalThis.fetch;
1244
+ const sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
1245
+ const state = await probePort(fetchImpl, port);
1246
+ if (state === "free") return { kind: "not-needed" };
1247
+ if (state === "foreign") return { kind: "foreign" };
1248
+ const authorised = await post(fetchImpl, url(port, "/mcp/authority"), {
1249
+ token
1250
+ });
1251
+ if (authorised?.status === 204) return { kind: "not-needed" };
1252
+ const stood = await post(fetchImpl, url(port, "/mcp/shutdown"), { token });
1253
+ const gone = async () => {
1254
+ for (let i = 0; i < RELEASE_ATTEMPTS; i += 1) {
1255
+ await sleep(RELEASE_INTERVAL_MS);
1256
+ if (await probePort(fetchImpl, port) === "free") return true;
1257
+ }
1258
+ return false;
1259
+ };
1260
+ if (stood?.status === 200 && await gone()) return { kind: "retired" };
1261
+ const kill = killCommand(io.platform, port);
1262
+ if (kill === null) return { kind: "failed" };
1263
+ await io.spawn(kill.command, kill.args, { shell: kill.shell });
1264
+ return await gone() ? { kind: "forced" } : { kind: "failed" };
1265
+ }
1174
1266
  var BACKUP_KEEP = 3;
1175
1267
  function formatBackupTimestamp(date) {
1176
1268
  const pad = (n) => String(n).padStart(2, "0");
@@ -1855,6 +1947,22 @@ async function runInstall(opts, io) {
1855
1947
  });
1856
1948
  }
1857
1949
  }
1950
+ const reclaimed = await reclaimPort(
1951
+ io,
1952
+ entryOpts.token,
1953
+ entryOpts.port === void 0 ? {} : { port: entryOpts.port }
1954
+ );
1955
+ if (reclaimed.kind === "retired" || reclaimed.kind === "forced") {
1956
+ io.out(" \uC608\uC804 \uC5F0\uACB0 \uD504\uB85C\uADF8\uB7A8\uC744 \uC815\uB9AC\uD588\uC5B4\uC694.");
1957
+ } else if (reclaimed.kind === "foreign") {
1958
+ io.out(
1959
+ " \uD3EC\uD2B8 8765\uB97C \uB2E4\uB978 \uD504\uB85C\uADF8\uB7A8\uC774 \uC4F0\uACE0 \uC788\uC5B4\uC694. \uADF8 \uD504\uB85C\uADF8\uB7A8\uC744 \uB044\uACE0 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC138\uC694."
1960
+ );
1961
+ } else if (reclaimed.kind === "failed") {
1962
+ io.out(
1963
+ " \uC608\uC804 \uC5F0\uACB0 \uD504\uB85C\uADF8\uB7A8\uC744 \uC815\uB9AC\uD558\uC9C0 \uBABB\uD588\uC5B4\uC694. AI \uC571\uC744 \uBAA8\uB450 \uC885\uB8CC\uD55C \uB4A4 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC138\uC694."
1964
+ );
1965
+ }
1858
1966
  if (opts.skills !== false) {
1859
1967
  results.push(
1860
1968
  ...await placeSkills(
@@ -2059,6 +2167,7 @@ async function detachSkill(io, payloadRoot, slug) {
2059
2167
  function createNodeIo() {
2060
2168
  return {
2061
2169
  platform: process.platform,
2170
+ fetch: globalThis.fetch,
2062
2171
  homedir() {
2063
2172
  return homedir();
2064
2173
  },
@@ -1,4 +1,4 @@
1
- // ../mcp-server/dist/chunk-HS7SL22A.js
1
+ // ../mcp-server/dist/chunk-FEN47OJU.js
2
2
  import { createHash } from "crypto";
3
3
  import {
4
4
  appendFileSync,
@@ -26,8 +26,13 @@ import { basename, isAbsolute } from "path";
26
26
  import { createConnection } from "net";
27
27
  import { spawn } from "child_process";
28
28
  import { closeSync } from "fs";
29
- import { createRequire } from "module";
29
+ import { randomBytes as randomBytes2 } from "crypto";
30
+ import { chmodSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
31
+ import { homedir as homedir2 } from "os";
30
32
  import { join as join4 } from "path";
33
+ import { timingSafeEqual as timingSafeEqual2 } from "crypto";
34
+ import { createRequire } from "module";
35
+ import { join as join5 } from "path";
31
36
  var __defProp = Object.defineProperty;
32
37
  var __export = (target, all) => {
33
38
  for (var name in all)
@@ -17640,7 +17645,8 @@ function createHttpBridge(opts) {
17640
17645
  if (req.method === "POST" && url2.pathname === "/mcp/shutdown") {
17641
17646
  void readJson(req, MAX_BODY_BYTES).then((body) => {
17642
17647
  const token = body?.token;
17643
- if (!opts.bridge.verifyRootToken(typeof token === "string" ? token : "")) {
17648
+ const presented = typeof token === "string" ? token : "";
17649
+ if (!opts.bridge.verifyRootToken(presented) && !(opts.verifyTakeover?.(presented) ?? false)) {
17644
17650
  json2(res, 403, { error: "unauthorized" });
17645
17651
  return;
17646
17652
  }
@@ -18140,6 +18146,35 @@ var Lifecycle = class {
18140
18146
  }
18141
18147
  }
18142
18148
  };
18149
+ var FILE_NAME3 = "takeover";
18150
+ var OWNER_ONLY = 384;
18151
+ function takeoverPath(home = homedir2()) {
18152
+ return join4(logDir(home), FILE_NAME3);
18153
+ }
18154
+ function writeTakeoverSecret(home = homedir2()) {
18155
+ try {
18156
+ mkdirSync2(logDir(home), { recursive: true });
18157
+ const secret = randomBytes2(32).toString("hex");
18158
+ const path = takeoverPath(home);
18159
+ writeFileSync(path, `${secret}
18160
+ `, { encoding: "utf8", mode: OWNER_ONLY });
18161
+ try {
18162
+ chmodSync(path, OWNER_ONLY);
18163
+ } catch {
18164
+ }
18165
+ return secret;
18166
+ } catch {
18167
+ return null;
18168
+ }
18169
+ }
18170
+ function readTakeoverSecret(home = homedir2()) {
18171
+ try {
18172
+ const raw = readFileSync2(takeoverPath(home), "utf8").trim();
18173
+ return raw === "" ? null : raw;
18174
+ } catch {
18175
+ return null;
18176
+ }
18177
+ }
18143
18178
  var NAME = MCP_SERVER_HEALTH_NAME;
18144
18179
  function readVersion() {
18145
18180
  return createRequire(import.meta.url)("../package.json").version;
@@ -18156,6 +18191,7 @@ function runDaemon(env, deps = {}) {
18156
18191
  }
18157
18192
  const { port, host, token, extensionIds } = resolved.config;
18158
18193
  const version2 = deps.version ?? readVersion();
18194
+ let takeoverSecret = null;
18159
18195
  const lifecycle = new Lifecycle({
18160
18196
  idleMs: deps.idleMs,
18161
18197
  onIdle: () => {
@@ -18201,7 +18237,7 @@ function runDaemon(env, deps = {}) {
18201
18237
  log
18202
18238
  });
18203
18239
  const stateDir = logDir(deps.home);
18204
- const mediaStage = createMediaStage({ root: join4(stateDir, "media") });
18240
+ const mediaStage = createMediaStage({ root: join5(stateDir, "media") });
18205
18241
  const registry2 = createBrowserRegistry({
18206
18242
  path: ledgerPath(deps.home),
18207
18243
  claimed: claimedBrowsers,
@@ -18223,6 +18259,7 @@ function runDaemon(env, deps = {}) {
18223
18259
  release: () => lifecycle.release(),
18224
18260
  bump: () => lifecycle.bump()
18225
18261
  },
18262
+ verifyTakeover: (presented) => takeoverSecret !== null && presented.length === takeoverSecret.length && timingSafeEqual2(Buffer.from(presented), Buffer.from(takeoverSecret)),
18226
18263
  onShutdown: () => {
18227
18264
  log("shutdown requested by an updating adapter; stepping aside");
18228
18265
  void http.close().finally(() => exit(0));
@@ -18240,6 +18277,12 @@ function runDaemon(env, deps = {}) {
18240
18277
  () => {
18241
18278
  log(`listening on http://${host}:${port} \u2014 waiting for the side panel`);
18242
18279
  lifecycle.start();
18280
+ takeoverSecret = writeTakeoverSecret(deps.home);
18281
+ if (takeoverSecret === null) {
18282
+ log(
18283
+ "could not write the takeover file; a host configured with a different token will not be able to retire this connector automatically"
18284
+ );
18285
+ }
18243
18286
  },
18244
18287
  () => {
18245
18288
  log("another instance already owns the port; exiting");
@@ -18268,5 +18311,6 @@ export {
18268
18311
  DEFAULT_HOST,
18269
18312
  tryConnect,
18270
18313
  spawnDaemon,
18314
+ readTakeoverSecret,
18271
18315
  runDaemon
18272
18316
  };
package/dist/cli.js CHANGED
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  dispatchCli,
4
4
  runAdapter
5
- } from "./chunk-M24B42VT.js";
5
+ } from "./chunk-2XHQ7OBB.js";
6
6
  import {
7
7
  runDaemon
8
- } from "./chunk-QHEJF6FK.js";
8
+ } from "./chunk-G34WU5WC.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-TFZ67X6Q.js");
20
+ const { createNodeIo, runInstall, runUninstall } = await import("./dist-3PKDQQAT.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-HOYBKMXR.js");
66
- const { createNodeIo } = await import("./dist-TFZ67X6Q.js");
65
+ const { runBrowsers } = await import("./browsers-WK72EBEK.js");
66
+ const { createNodeIo } = await import("./dist-3PKDQQAT.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-TFZ67X6Q.js");
91
+ } = await import("./dist-3PKDQQAT.js");
92
92
  const io = createNodeIo();
93
93
  const root = skillsPayloadRoot();
94
94
  const code = await runSkills(argv, {
@@ -118,5 +118,7 @@ async function main() {
118
118
  }
119
119
  main().catch((err) => {
120
120
  log(err instanceof Error ? err.stack ?? err.message : String(err));
121
- process.exit(1);
121
+ process.exitCode = 1;
122
+ const bail = setTimeout(() => process.exit(1), 2e3);
123
+ bail.unref?.();
122
124
  });
@@ -49,7 +49,7 @@ import {
49
49
  upsertServerKey,
50
50
  validateInstallOptions,
51
51
  validateUninstallOptions
52
- } from "./chunk-5IYKI4EG.js";
52
+ } from "./chunk-6GGVN4TT.js";
53
53
  import "./chunk-MLKGABMK.js";
54
54
  export {
55
55
  BACKUP_KEEP,
package/dist/index.d.ts CHANGED
@@ -50,6 +50,11 @@ interface EnsureRunningDeps {
50
50
  log?: (message: string) => void;
51
51
  attempts?: number;
52
52
  intervalMs?: number;
53
+ /**
54
+ * Reads the running daemon's takeover secret from disk. Injected in tests;
55
+ * defaults to the real file.
56
+ */
57
+ readTakeover?: () => string | null;
53
58
  /**
54
59
  * This adapter's own version. When set, a running daemon older than this is
55
60
  * replaced; one at this version or newer is left alone, so an older adapter
package/dist/index.js CHANGED
@@ -3,8 +3,8 @@ import {
3
3
  dispatchCli,
4
4
  ensureDaemonRunning,
5
5
  runAdapter
6
- } from "./chunk-M24B42VT.js";
7
- import "./chunk-QHEJF6FK.js";
6
+ } from "./chunk-2XHQ7OBB.js";
7
+ import "./chunk-G34WU5WC.js";
8
8
  import "./chunk-MLKGABMK.js";
9
9
  export {
10
10
  createAdapterServer,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modootoday/datalab-extension-mcp",
3
- "version": "1.8.15",
3
+ "version": "1.8.17",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",