@modootoday/datalab-extension-mcp 1.8.11 → 1.8.13
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 +31 -28
- package/README.md +26 -24
- package/dist/{browsers-EMS5V4M4.js → browsers-M2DRBQB6.js} +2 -2
- package/dist/{chunk-S6D7YVBB.js → chunk-CQRDBJXM.js} +3 -3
- package/dist/{chunk-65FDP6LF.js → chunk-M24B42VT.js} +1 -1
- package/dist/{chunk-Q6SOGGKF.js → chunk-QHEJF6FK.js} +469 -365
- package/dist/cli.js +6 -6
- package/dist/{dist-63JYX33Z.js → dist-R3VRQN24.js} +1 -1
- package/dist/index.js +2 -2
- package/package.json +1 -1
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
// ../mcp-server/dist/chunk-
|
|
1
|
+
// ../mcp-server/dist/chunk-HS7SL22A.js
|
|
2
2
|
import { createHash } from "crypto";
|
|
3
|
-
import { createHash as createHash2, randomUUID } from "crypto";
|
|
4
|
-
import { mkdir, readFile, readdir, rm, writeFile } from "fs/promises";
|
|
5
|
-
import { join } from "path";
|
|
6
|
-
import {
|
|
7
|
-
createServer
|
|
8
|
-
} from "http";
|
|
9
|
-
import { pipeline } from "stream/promises";
|
|
10
|
-
import { constants } from "fs";
|
|
11
|
-
import { lstat, open } from "fs/promises";
|
|
12
|
-
import { basename, isAbsolute } from "path";
|
|
13
3
|
import {
|
|
14
4
|
appendFileSync,
|
|
15
5
|
mkdirSync,
|
|
@@ -18,14 +8,24 @@ import {
|
|
|
18
8
|
statSync
|
|
19
9
|
} from "fs";
|
|
20
10
|
import { homedir } from "os";
|
|
21
|
-
import { join
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
import { randomBytes } from "crypto";
|
|
13
|
+
import { readFileSync } from "fs";
|
|
14
|
+
import { mkdir, rename, writeFile } from "fs/promises";
|
|
15
|
+
import { dirname, join as join2 } from "path";
|
|
16
|
+
import { createHash as createHash2, randomUUID } from "crypto";
|
|
17
|
+
import { mkdir as mkdir2, readFile, readdir, rm, writeFile as writeFile2 } from "fs/promises";
|
|
18
|
+
import { join as join3 } from "path";
|
|
19
|
+
import {
|
|
20
|
+
createServer
|
|
21
|
+
} from "http";
|
|
22
|
+
import { pipeline } from "stream/promises";
|
|
23
|
+
import { constants } from "fs";
|
|
24
|
+
import { lstat, open } from "fs/promises";
|
|
25
|
+
import { basename, isAbsolute } from "path";
|
|
22
26
|
import { createConnection } from "net";
|
|
23
27
|
import { spawn } from "child_process";
|
|
24
28
|
import { closeSync } from "fs";
|
|
25
|
-
import { randomBytes } from "crypto";
|
|
26
|
-
import { readFileSync } from "fs";
|
|
27
|
-
import { mkdir as mkdir2, rename, writeFile as writeFile2 } from "fs/promises";
|
|
28
|
-
import { dirname, join as join3 } from "path";
|
|
29
29
|
import { createRequire } from "module";
|
|
30
30
|
import { join as join4 } from "path";
|
|
31
31
|
var __defProp = Object.defineProperty;
|
|
@@ -16334,62 +16334,419 @@ var BridgeError = class extends Error {
|
|
|
16334
16334
|
if (timing) this.timing = timing;
|
|
16335
16335
|
}
|
|
16336
16336
|
};
|
|
16337
|
-
var
|
|
16338
|
-
var
|
|
16339
|
-
var
|
|
16340
|
-
function
|
|
16341
|
-
return
|
|
16337
|
+
var LOG_MAX_BYTES = 1024 * 1024;
|
|
16338
|
+
var DIR_NAME = ".datalab-mcp";
|
|
16339
|
+
var FILE_NAME = "connector.log";
|
|
16340
|
+
function logDir(home = homedir()) {
|
|
16341
|
+
return join(home, DIR_NAME);
|
|
16342
16342
|
}
|
|
16343
|
-
function
|
|
16344
|
-
return
|
|
16343
|
+
function logPath(home = homedir()) {
|
|
16344
|
+
return join(logDir(home), FILE_NAME);
|
|
16345
16345
|
}
|
|
16346
|
-
function
|
|
16347
|
-
|
|
16348
|
-
|
|
16349
|
-
|
|
16350
|
-
|
|
16351
|
-
|
|
16352
|
-
|
|
16353
|
-
|
|
16354
|
-
|
|
16355
|
-
|
|
16356
|
-
|
|
16357
|
-
|
|
16358
|
-
|
|
16359
|
-
|
|
16360
|
-
|
|
16361
|
-
|
|
16362
|
-
|
|
16346
|
+
function rolledLogPath(home = homedir()) {
|
|
16347
|
+
return `${logPath(home)}.1`;
|
|
16348
|
+
}
|
|
16349
|
+
function rollIfLarge(home = homedir(), maxBytes = LOG_MAX_BYTES) {
|
|
16350
|
+
try {
|
|
16351
|
+
const size = statSync(logPath(home)).size;
|
|
16352
|
+
if (size < maxBytes) return false;
|
|
16353
|
+
renameSync(logPath(home), rolledLogPath(home));
|
|
16354
|
+
return true;
|
|
16355
|
+
} catch {
|
|
16356
|
+
return false;
|
|
16357
|
+
}
|
|
16358
|
+
}
|
|
16359
|
+
function openLogFd(home = homedir()) {
|
|
16360
|
+
try {
|
|
16361
|
+
mkdirSync(logDir(home), { recursive: true });
|
|
16362
|
+
rollIfLarge(home);
|
|
16363
|
+
return openSync(logPath(home), "a");
|
|
16364
|
+
} catch {
|
|
16365
|
+
return null;
|
|
16366
|
+
}
|
|
16367
|
+
}
|
|
16368
|
+
var FILE_NAME2 = "browsers.json";
|
|
16369
|
+
function ledgerPath(home) {
|
|
16370
|
+
return join2(logDir(home), FILE_NAME2);
|
|
16371
|
+
}
|
|
16372
|
+
function isRegistration(row) {
|
|
16373
|
+
if (typeof row !== "object" || row === null) return false;
|
|
16374
|
+
const r = row;
|
|
16375
|
+
const id = r["browserId"];
|
|
16376
|
+
const hash2 = r["tokenHash"];
|
|
16377
|
+
return (id === null || typeof id === "string" && id.length > 0) && typeof hash2 === "string" && /^[a-f0-9]{64}$/.test(hash2) && typeof r["label"] === "string" && typeof r["createdAt"] === "string";
|
|
16378
|
+
}
|
|
16379
|
+
function readLedger(path) {
|
|
16380
|
+
let raw;
|
|
16381
|
+
try {
|
|
16382
|
+
raw = readFileSync(path, "utf8");
|
|
16383
|
+
} catch (err) {
|
|
16384
|
+
if (err.code === "ENOENT") {
|
|
16385
|
+
return { state: "absent" };
|
|
16363
16386
|
}
|
|
16387
|
+
return { state: "unreadable", reason: describe3(err) };
|
|
16364
16388
|
}
|
|
16365
|
-
|
|
16366
|
-
|
|
16367
|
-
|
|
16389
|
+
let parsed;
|
|
16390
|
+
try {
|
|
16391
|
+
parsed = JSON.parse(raw);
|
|
16392
|
+
} catch (err) {
|
|
16393
|
+
return { state: "unreadable", reason: describe3(err) };
|
|
16394
|
+
}
|
|
16395
|
+
if (!Array.isArray(parsed)) {
|
|
16396
|
+
return { state: "unreadable", reason: "not a list of browsers" };
|
|
16397
|
+
}
|
|
16398
|
+
if (!parsed.every(isRegistration)) {
|
|
16399
|
+
return { state: "unreadable", reason: "a row is not a browser" };
|
|
16400
|
+
}
|
|
16401
|
+
const browserIds = /* @__PURE__ */ new Set();
|
|
16402
|
+
const tokenHashes = /* @__PURE__ */ new Set();
|
|
16403
|
+
for (const row of parsed) {
|
|
16404
|
+
if (tokenHashes.has(row.tokenHash) || row.browserId !== null && browserIds.has(row.browserId)) {
|
|
16405
|
+
return { state: "unreadable", reason: "duplicate browser authority" };
|
|
16406
|
+
}
|
|
16407
|
+
tokenHashes.add(row.tokenHash);
|
|
16408
|
+
if (row.browserId !== null) browserIds.add(row.browserId);
|
|
16409
|
+
}
|
|
16410
|
+
return { state: "ok", rows: parsed };
|
|
16411
|
+
}
|
|
16412
|
+
function ledgerOverlapsToken(rows, tokenHash) {
|
|
16413
|
+
return rows.some((row) => row.tokenHash === tokenHash);
|
|
16414
|
+
}
|
|
16415
|
+
async function writeLedger(path, claims) {
|
|
16416
|
+
const queued = (writing.get(path) ?? Promise.resolve()).then(
|
|
16417
|
+
() => writeOnce(path, claims),
|
|
16418
|
+
() => writeOnce(path, claims)
|
|
16419
|
+
);
|
|
16420
|
+
writing.set(path, queued);
|
|
16421
|
+
try {
|
|
16422
|
+
await queued;
|
|
16423
|
+
} finally {
|
|
16424
|
+
if (writing.get(path) === queued) writing.delete(path);
|
|
16425
|
+
}
|
|
16426
|
+
}
|
|
16427
|
+
var writing = /* @__PURE__ */ new Map();
|
|
16428
|
+
async function writeOnce(path, claims) {
|
|
16429
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
16430
|
+
await mkdir(dirname(path), { recursive: true, mode: 448 });
|
|
16431
|
+
await writeFile(tmp, `${JSON.stringify(claims, null, 2)}
|
|
16432
|
+
`, {
|
|
16433
|
+
mode: 384
|
|
16434
|
+
});
|
|
16435
|
+
await rename(tmp, path);
|
|
16436
|
+
}
|
|
16437
|
+
function claimedDigestsFrom(claims) {
|
|
16438
|
+
const pairs = [];
|
|
16439
|
+
for (const claim of claims) {
|
|
16440
|
+
if (claim.browserId !== null && claim.browserId.length > 0) {
|
|
16441
|
+
pairs.push([claim.browserId, claim.tokenHash]);
|
|
16442
|
+
}
|
|
16443
|
+
}
|
|
16444
|
+
return pairs;
|
|
16445
|
+
}
|
|
16446
|
+
function describe3(err) {
|
|
16447
|
+
return err instanceof Error ? err.message : String(err);
|
|
16448
|
+
}
|
|
16449
|
+
var HANDLE_LENGTH = 8;
|
|
16450
|
+
var BrowserRegistryError = class extends Error {
|
|
16451
|
+
code;
|
|
16452
|
+
constructor(code) {
|
|
16453
|
+
super(code);
|
|
16454
|
+
this.name = "BrowserRegistryError";
|
|
16455
|
+
this.code = code;
|
|
16368
16456
|
}
|
|
16457
|
+
};
|
|
16458
|
+
function mintToken() {
|
|
16459
|
+
return randomBytes(32).toString("hex");
|
|
16460
|
+
}
|
|
16461
|
+
function handleOf(claim) {
|
|
16462
|
+
return claim.tokenHash.slice(0, HANDLE_LENGTH);
|
|
16463
|
+
}
|
|
16464
|
+
function matches(claim, handle) {
|
|
16465
|
+
return handleOf(claim) === handle || claim.browserId === handle;
|
|
16466
|
+
}
|
|
16467
|
+
function createBrowserRegistry(opts) {
|
|
16468
|
+
const now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
16469
|
+
const revoked = /* @__PURE__ */ new Set();
|
|
16470
|
+
const initial = readLedger(opts.path);
|
|
16471
|
+
const unreadable = initial.state === "unreadable" ? initial.reason : null;
|
|
16472
|
+
let rows = initial.state === "ok" ? initial.rows.map((row) => ({ ...row })) : [];
|
|
16473
|
+
let mutationTail = Promise.resolve();
|
|
16474
|
+
let pendingMutations = 0;
|
|
16475
|
+
const view = (source = rows) => {
|
|
16476
|
+
if (unreadable !== null) {
|
|
16477
|
+
throw new Error(`browser registry is unreadable: ${unreadable}`);
|
|
16478
|
+
}
|
|
16479
|
+
if (!opts.forbiddenTokenHash) return { rows: [...source], quarantined: [] };
|
|
16480
|
+
const quarantined = source.filter(
|
|
16481
|
+
(row) => timingSafeEqual(row.tokenHash, opts.forbiddenTokenHash)
|
|
16482
|
+
);
|
|
16483
|
+
return {
|
|
16484
|
+
rows: source.filter(
|
|
16485
|
+
(row) => !timingSafeEqual(row.tokenHash, opts.forbiddenTokenHash)
|
|
16486
|
+
),
|
|
16487
|
+
quarantined
|
|
16488
|
+
};
|
|
16489
|
+
};
|
|
16490
|
+
const current = () => view().rows;
|
|
16491
|
+
const serialize = (work) => {
|
|
16492
|
+
pendingMutations += 1;
|
|
16493
|
+
const result = mutationTail.then(work, work);
|
|
16494
|
+
mutationTail = result.then(
|
|
16495
|
+
() => {
|
|
16496
|
+
pendingMutations -= 1;
|
|
16497
|
+
},
|
|
16498
|
+
() => {
|
|
16499
|
+
pendingMutations -= 1;
|
|
16500
|
+
}
|
|
16501
|
+
);
|
|
16502
|
+
return result;
|
|
16503
|
+
};
|
|
16504
|
+
const transact = (propose) => {
|
|
16505
|
+
if (unreadable !== null) {
|
|
16506
|
+
return Promise.reject(
|
|
16507
|
+
new Error(`browser registry is unreadable: ${unreadable}`)
|
|
16508
|
+
);
|
|
16509
|
+
}
|
|
16510
|
+
return serialize(async () => {
|
|
16511
|
+
const proposal = propose(rows.map((row) => ({ ...row })));
|
|
16512
|
+
await writeLedger(opts.path, proposal.next);
|
|
16513
|
+
rows = proposal.next;
|
|
16514
|
+
proposal.commit?.();
|
|
16515
|
+
return proposal.result;
|
|
16516
|
+
});
|
|
16517
|
+
};
|
|
16369
16518
|
return {
|
|
16370
|
-
async
|
|
16371
|
-
const
|
|
16372
|
-
const
|
|
16373
|
-
|
|
16374
|
-
|
|
16519
|
+
async add(label) {
|
|
16520
|
+
const token = mintToken();
|
|
16521
|
+
const claim = {
|
|
16522
|
+
browserId: null,
|
|
16523
|
+
tokenHash: digestToken(token),
|
|
16524
|
+
label,
|
|
16525
|
+
createdAt: now().toISOString()
|
|
16526
|
+
};
|
|
16527
|
+
return transact((snapshot) => ({
|
|
16528
|
+
next: [...snapshot, claim],
|
|
16529
|
+
result: { handle: handleOf(claim), token }
|
|
16530
|
+
}));
|
|
16531
|
+
},
|
|
16532
|
+
async list() {
|
|
16533
|
+
return current().map((claim) => ({
|
|
16534
|
+
handle: handleOf(claim),
|
|
16535
|
+
label: claim.label,
|
|
16536
|
+
browserId: claim.browserId,
|
|
16537
|
+
createdAt: claim.createdAt
|
|
16538
|
+
}));
|
|
16539
|
+
},
|
|
16540
|
+
claim(browserId, tokenHash) {
|
|
16541
|
+
if (pendingMutations > 0) return false;
|
|
16542
|
+
if (opts.claimed.has(browserId)) return false;
|
|
16543
|
+
for (const held of opts.claimed.values()) {
|
|
16544
|
+
if (timingSafeEqual(held, tokenHash)) return false;
|
|
16375
16545
|
}
|
|
16376
|
-
|
|
16377
|
-
|
|
16378
|
-
|
|
16379
|
-
|
|
16380
|
-
|
|
16381
|
-
|
|
16382
|
-
|
|
16383
|
-
|
|
16384
|
-
|
|
16385
|
-
|
|
16386
|
-
|
|
16387
|
-
|
|
16388
|
-
|
|
16389
|
-
|
|
16390
|
-
|
|
16391
|
-
|
|
16392
|
-
|
|
16546
|
+
let claims;
|
|
16547
|
+
try {
|
|
16548
|
+
claims = current();
|
|
16549
|
+
} catch {
|
|
16550
|
+
return false;
|
|
16551
|
+
}
|
|
16552
|
+
const row = claims.find(
|
|
16553
|
+
(claim) => claim.browserId === null && timingSafeEqual(claim.tokenHash, tokenHash)
|
|
16554
|
+
);
|
|
16555
|
+
if (!row) return false;
|
|
16556
|
+
const stored = rows.find((claim) => claim.tokenHash === row.tokenHash);
|
|
16557
|
+
if (!stored) return false;
|
|
16558
|
+
stored.browserId = browserId;
|
|
16559
|
+
opts.claimed.set(browserId, row.tokenHash);
|
|
16560
|
+
opts.onClaim?.(browserId, stored.label);
|
|
16561
|
+
const snapshot = rows.map((entry) => ({ ...entry }));
|
|
16562
|
+
void serialize(async () => {
|
|
16563
|
+
try {
|
|
16564
|
+
await writeLedger(opts.path, snapshot);
|
|
16565
|
+
} catch (error51) {
|
|
16566
|
+
if (stored.browserId === browserId) stored.browserId = null;
|
|
16567
|
+
if (opts.claimed.get(browserId) === row.tokenHash) {
|
|
16568
|
+
opts.claimed.delete(browserId);
|
|
16569
|
+
opts.onRevoke?.(browserId);
|
|
16570
|
+
}
|
|
16571
|
+
throw error51;
|
|
16572
|
+
}
|
|
16573
|
+
}).catch(() => void 0);
|
|
16574
|
+
return true;
|
|
16575
|
+
},
|
|
16576
|
+
async revoke(handle) {
|
|
16577
|
+
return transact((snapshot) => {
|
|
16578
|
+
const claims = view(snapshot).rows;
|
|
16579
|
+
const removed = claims.filter((claim) => matches(claim, handle));
|
|
16580
|
+
if (removed.length === 0) {
|
|
16581
|
+
return { next: snapshot, result: false };
|
|
16582
|
+
}
|
|
16583
|
+
const removedHashes = new Set(removed.map((claim) => claim.tokenHash));
|
|
16584
|
+
return {
|
|
16585
|
+
next: snapshot.filter((claim) => !removedHashes.has(claim.tokenHash)),
|
|
16586
|
+
result: true,
|
|
16587
|
+
commit: () => {
|
|
16588
|
+
for (const claim of removed) {
|
|
16589
|
+
revoked.add(claim.tokenHash);
|
|
16590
|
+
if (claim.browserId !== null) {
|
|
16591
|
+
opts.claimed.delete(claim.browserId);
|
|
16592
|
+
opts.onRevoke?.(claim.browserId);
|
|
16593
|
+
}
|
|
16594
|
+
}
|
|
16595
|
+
}
|
|
16596
|
+
};
|
|
16597
|
+
});
|
|
16598
|
+
},
|
|
16599
|
+
async repair() {
|
|
16600
|
+
return transact((snapshot) => {
|
|
16601
|
+
const currentView = view(snapshot);
|
|
16602
|
+
const removed = currentView.quarantined.map(handleOf);
|
|
16603
|
+
const removedHashes = new Set(
|
|
16604
|
+
currentView.quarantined.map((row) => row.tokenHash)
|
|
16605
|
+
);
|
|
16606
|
+
return {
|
|
16607
|
+
next: snapshot.filter((row) => !removedHashes.has(row.tokenHash)),
|
|
16608
|
+
result: { removed }
|
|
16609
|
+
};
|
|
16610
|
+
});
|
|
16611
|
+
},
|
|
16612
|
+
async repairRegistration(browserId, requestedLabel) {
|
|
16613
|
+
if (browserId.length === 0) {
|
|
16614
|
+
throw new BrowserRegistryError("registration_not_found");
|
|
16615
|
+
}
|
|
16616
|
+
return transact((snapshot) => {
|
|
16617
|
+
const currentView = view(snapshot);
|
|
16618
|
+
if (currentView.rows.some((row) => row.browserId === browserId)) {
|
|
16619
|
+
throw new BrowserRegistryError("registration_already_valid");
|
|
16620
|
+
}
|
|
16621
|
+
const quarantined = currentView.quarantined.filter(
|
|
16622
|
+
(row) => row.browserId === browserId
|
|
16623
|
+
);
|
|
16624
|
+
if (quarantined.length === 0) {
|
|
16625
|
+
throw new BrowserRegistryError(
|
|
16626
|
+
currentView.quarantined.length > 0 ? "registration_not_quarantined" : "registration_not_found"
|
|
16627
|
+
);
|
|
16628
|
+
}
|
|
16629
|
+
const token = mintToken();
|
|
16630
|
+
const tokenHash = digestToken(token);
|
|
16631
|
+
const label = requestedLabel?.trim() || quarantined[0]?.label || "\uC774 \uBE0C\uB77C\uC6B0\uC800";
|
|
16632
|
+
const removedHashes = new Set(quarantined.map((row) => row.tokenHash));
|
|
16633
|
+
const next = {
|
|
16634
|
+
browserId: null,
|
|
16635
|
+
tokenHash,
|
|
16636
|
+
label,
|
|
16637
|
+
createdAt: now().toISOString()
|
|
16638
|
+
};
|
|
16639
|
+
return {
|
|
16640
|
+
next: [
|
|
16641
|
+
...snapshot.filter((row) => !removedHashes.has(row.tokenHash)),
|
|
16642
|
+
next
|
|
16643
|
+
],
|
|
16644
|
+
result: {
|
|
16645
|
+
outcome: "repaired",
|
|
16646
|
+
removed: quarantined.map(handleOf),
|
|
16647
|
+
registration: { handle: handleOf(next), label, token }
|
|
16648
|
+
},
|
|
16649
|
+
commit: () => {
|
|
16650
|
+
for (const row of quarantined) {
|
|
16651
|
+
revoked.add(row.tokenHash);
|
|
16652
|
+
if (row.browserId !== null && opts.claimed.get(row.browserId) === row.tokenHash) {
|
|
16653
|
+
opts.claimed.delete(row.browserId);
|
|
16654
|
+
opts.onRevoke?.(row.browserId);
|
|
16655
|
+
}
|
|
16656
|
+
}
|
|
16657
|
+
}
|
|
16658
|
+
};
|
|
16659
|
+
});
|
|
16660
|
+
},
|
|
16661
|
+
status() {
|
|
16662
|
+
if (unreadable !== null) {
|
|
16663
|
+
return {
|
|
16664
|
+
state: "disabled",
|
|
16665
|
+
error: "browser_registry_unreadable",
|
|
16666
|
+
detail: unreadable
|
|
16667
|
+
};
|
|
16668
|
+
}
|
|
16669
|
+
if (opts.forbiddenTokenHash && ledgerOverlapsToken(rows, opts.forbiddenTokenHash)) {
|
|
16670
|
+
return {
|
|
16671
|
+
state: "degraded",
|
|
16672
|
+
error: "browser_registry_root_authority_overlap",
|
|
16673
|
+
quarantinedHandles: rows.filter(
|
|
16674
|
+
(row) => timingSafeEqual(row.tokenHash, opts.forbiddenTokenHash)
|
|
16675
|
+
).map(handleOf),
|
|
16676
|
+
quarantined: rows.filter(
|
|
16677
|
+
(row) => timingSafeEqual(row.tokenHash, opts.forbiddenTokenHash)
|
|
16678
|
+
).map((row) => ({
|
|
16679
|
+
handle: handleOf(row),
|
|
16680
|
+
label: row.label,
|
|
16681
|
+
browserId: row.browserId,
|
|
16682
|
+
createdAt: row.createdAt
|
|
16683
|
+
})),
|
|
16684
|
+
recovery: "repair_registration"
|
|
16685
|
+
};
|
|
16686
|
+
}
|
|
16687
|
+
return { state: "ready" };
|
|
16688
|
+
},
|
|
16689
|
+
wasRevoked(tokenHash) {
|
|
16690
|
+
return revoked.has(tokenHash);
|
|
16691
|
+
}
|
|
16692
|
+
};
|
|
16693
|
+
}
|
|
16694
|
+
var MAX_STAGE_BYTES = 10 * 1024 * 1024;
|
|
16695
|
+
var DEFAULT_TOKEN_TTL_MS = 30 * 60 * 1e3;
|
|
16696
|
+
var STAGE_CAPABILITY = "datalab-mcp-stage-v1";
|
|
16697
|
+
function refuse(reason, retryable, message) {
|
|
16698
|
+
return { state: "REFUSED", reason, retryable, message };
|
|
16699
|
+
}
|
|
16700
|
+
function segment(...parts) {
|
|
16701
|
+
return createHash2("sha256").update(parts.join("\0")).digest("hex").slice(0, 32);
|
|
16702
|
+
}
|
|
16703
|
+
function createMediaStage(opts) {
|
|
16704
|
+
const now = opts.now ?? Date.now;
|
|
16705
|
+
const ttl = opts.tokenTtlMs ?? DEFAULT_TOKEN_TTL_MS;
|
|
16706
|
+
const ledgerDir = join3(opts.root, "ledger");
|
|
16707
|
+
const blobDir = join3(opts.root, "blob");
|
|
16708
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
16709
|
+
const active = /* @__PURE__ */ new Map();
|
|
16710
|
+
const keyOf = (j) => segment(j.blogId, j.runId, j.actionId, j.jobId);
|
|
16711
|
+
const ledgerPath2 = (key) => join3(ledgerDir, `${key}.json`);
|
|
16712
|
+
const blobPath = (key) => join3(blobDir, key);
|
|
16713
|
+
async function readRow(key) {
|
|
16714
|
+
try {
|
|
16715
|
+
const raw = await readFile(ledgerPath2(key), "utf8");
|
|
16716
|
+
return JSON.parse(raw);
|
|
16717
|
+
} catch (err) {
|
|
16718
|
+
if (err.code === "ENOENT") return null;
|
|
16719
|
+
return "unreadable";
|
|
16720
|
+
}
|
|
16721
|
+
}
|
|
16722
|
+
async function writeRow(key, row) {
|
|
16723
|
+
await mkdir2(ledgerDir, { recursive: true });
|
|
16724
|
+
await writeFile2(ledgerPath2(key), JSON.stringify(row), "utf8");
|
|
16725
|
+
}
|
|
16726
|
+
return {
|
|
16727
|
+
async begin(req) {
|
|
16728
|
+
const key = keyOf(req);
|
|
16729
|
+
const row = await readRow(key);
|
|
16730
|
+
if (row === "unreadable") {
|
|
16731
|
+
return refuse("uploads_unproven", true, "\uC791\uC5C5 \uC0C1\uD0DC\uB97C \uC77D\uC9C0 \uBABB\uD588\uC5B4\uC694.");
|
|
16732
|
+
}
|
|
16733
|
+
if (row) {
|
|
16734
|
+
if (req.fence < row.fence) {
|
|
16735
|
+
return refuse(
|
|
16736
|
+
"stale_fence",
|
|
16737
|
+
false,
|
|
16738
|
+
"\uB354 \uC0C8\uB85C\uC6B4 \uC2DC\uB3C4\uAC00 \uC774\uBBF8 \uC9C4\uD589\uB410\uC5B4\uC694."
|
|
16739
|
+
);
|
|
16740
|
+
}
|
|
16741
|
+
if (row.state === "SEALED") {
|
|
16742
|
+
return refuse("already_sealed", false, "\uC774\uBBF8 \uBD09\uC778\uB41C \uC790\uC0B0\uC774\uC5D0\uC694.");
|
|
16743
|
+
}
|
|
16744
|
+
if (row.state === "OPEN") {
|
|
16745
|
+
for (const [token2, held] of tokens) {
|
|
16746
|
+
if (held.key === key && held.expiresAt > now()) {
|
|
16747
|
+
return {
|
|
16748
|
+
state: "OPEN",
|
|
16749
|
+
jobId: req.jobId,
|
|
16393
16750
|
uploadToken: token2,
|
|
16394
16751
|
expiresAt: held.expiresAt,
|
|
16395
16752
|
capabilityVersion: STAGE_CAPABILITY
|
|
@@ -16424,7 +16781,7 @@ function createMediaStage(opts) {
|
|
|
16424
16781
|
}
|
|
16425
16782
|
active.set(held.key, (active.get(held.key) ?? 0) + 1);
|
|
16426
16783
|
try {
|
|
16427
|
-
await
|
|
16784
|
+
await mkdir2(blobDir, { recursive: true });
|
|
16428
16785
|
const chunks = [];
|
|
16429
16786
|
let total = 0;
|
|
16430
16787
|
for await (const chunk of body) {
|
|
@@ -16435,7 +16792,7 @@ function createMediaStage(opts) {
|
|
|
16435
16792
|
}
|
|
16436
16793
|
chunks.push(buf);
|
|
16437
16794
|
}
|
|
16438
|
-
await
|
|
16795
|
+
await writeFile2(blobPath(held.key), Buffer.concat(chunks));
|
|
16439
16796
|
return { ok: true, bytes: total };
|
|
16440
16797
|
} finally {
|
|
16441
16798
|
const left = (active.get(held.key) ?? 1) - 1;
|
|
@@ -16568,7 +16925,7 @@ function createMediaStage(opts) {
|
|
|
16568
16925
|
const out = [];
|
|
16569
16926
|
for (const dir of [ledgerDir, blobDir]) {
|
|
16570
16927
|
try {
|
|
16571
|
-
for (const name of await readdir(dir)) out.push(
|
|
16928
|
+
for (const name of await readdir(dir)) out.push(join3(dir, name));
|
|
16572
16929
|
} catch {
|
|
16573
16930
|
}
|
|
16574
16931
|
}
|
|
@@ -16838,7 +17195,15 @@ async function handleMcpRequest(bridge, body, accept, stage) {
|
|
|
16838
17195
|
}
|
|
16839
17196
|
if (params.name === BROWSERS_TOOL) {
|
|
16840
17197
|
const registryStatus = bridge.browserRegistryStatus();
|
|
16841
|
-
const { state: registryState
|
|
17198
|
+
const { state: registryState } = registryStatus;
|
|
17199
|
+
const registryDetail = registryStatus.state === "degraded" ? {
|
|
17200
|
+
error: registryStatus.error,
|
|
17201
|
+
quarantinedHandles: registryStatus.quarantinedHandles,
|
|
17202
|
+
recovery: registryStatus.recovery
|
|
17203
|
+
} : registryStatus.state === "disabled" ? {
|
|
17204
|
+
error: registryStatus.error,
|
|
17205
|
+
detail: registryStatus.detail
|
|
17206
|
+
} : {};
|
|
16842
17207
|
const payload = {
|
|
16843
17208
|
browsers: bridge.connectedBrowsers(),
|
|
16844
17209
|
registryStatus: registryState,
|
|
@@ -17018,6 +17383,11 @@ async function openLocalImage(path) {
|
|
|
17018
17383
|
};
|
|
17019
17384
|
}
|
|
17020
17385
|
}
|
|
17386
|
+
var MCP_SERVER_HEALTH_NAME = "datalab-extension-mcp-server";
|
|
17387
|
+
var MCP_SERVER_HEALTH_SCHEMA = "datalab-extension-mcp-health-v1";
|
|
17388
|
+
var MCP_MANAGEMENT_CAPABILITIES = Object.freeze([
|
|
17389
|
+
"browser-registration-repair-v1"
|
|
17390
|
+
]);
|
|
17021
17391
|
var MAX_BODY_BYTES = 64 * 1024;
|
|
17022
17392
|
var DEFAULT_TOOLS_CHANGED_DEBOUNCE_MS = 600;
|
|
17023
17393
|
var MAX_MCP_BODY_BYTES2 = 256 * 1024;
|
|
@@ -17188,8 +17558,9 @@ function createHttpBridge(opts) {
|
|
|
17188
17558
|
}
|
|
17189
17559
|
if (req.method === "GET" && url2.pathname === "/bridge/health") {
|
|
17190
17560
|
json2(res, 200, {
|
|
17191
|
-
name:
|
|
17192
|
-
version: opts.identity.version
|
|
17561
|
+
name: MCP_SERVER_HEALTH_NAME,
|
|
17562
|
+
version: opts.identity.version,
|
|
17563
|
+
schema: MCP_SERVER_HEALTH_SCHEMA
|
|
17193
17564
|
});
|
|
17194
17565
|
return;
|
|
17195
17566
|
}
|
|
@@ -17284,6 +17655,7 @@ function createHttpBridge(opts) {
|
|
|
17284
17655
|
json2(res, 404, { error: "not found" });
|
|
17285
17656
|
return;
|
|
17286
17657
|
}
|
|
17658
|
+
res.setHeader("cache-control", "no-store");
|
|
17287
17659
|
void readJson(req, MAX_BODY_BYTES).then(async (body) => {
|
|
17288
17660
|
const b = body;
|
|
17289
17661
|
if (!opts.bridge.verifyRootToken(
|
|
@@ -17304,7 +17676,8 @@ function createHttpBridge(opts) {
|
|
|
17304
17676
|
json2(res, 200, {
|
|
17305
17677
|
ok: true,
|
|
17306
17678
|
browsers,
|
|
17307
|
-
registryStatus: registry2.status()
|
|
17679
|
+
registryStatus: registry2.status(),
|
|
17680
|
+
capabilities: [...MCP_MANAGEMENT_CAPABILITIES]
|
|
17308
17681
|
});
|
|
17309
17682
|
return;
|
|
17310
17683
|
}
|
|
@@ -17325,10 +17698,33 @@ function createHttpBridge(opts) {
|
|
|
17325
17698
|
json2(res, 200, { ok: true, ...await registry2.repair() });
|
|
17326
17699
|
return;
|
|
17327
17700
|
}
|
|
17701
|
+
if (action === "repair_registration") {
|
|
17702
|
+
const browserId = typeof b?.["browserId"] === "string" ? b["browserId"] : "";
|
|
17703
|
+
const label = typeof b?.["label"] === "string" ? b["label"] : void 0;
|
|
17704
|
+
const repaired = await registry2.repairRegistration(
|
|
17705
|
+
browserId,
|
|
17706
|
+
label
|
|
17707
|
+
);
|
|
17708
|
+
json2(res, 200, {
|
|
17709
|
+
ok: true,
|
|
17710
|
+
...repaired,
|
|
17711
|
+
registryStatus: registry2.status(),
|
|
17712
|
+
capabilities: [...MCP_MANAGEMENT_CAPABILITIES]
|
|
17713
|
+
});
|
|
17714
|
+
return;
|
|
17715
|
+
}
|
|
17328
17716
|
json2(res, 400, { error: "unknown action" });
|
|
17329
17717
|
}).catch((err) => {
|
|
17330
17718
|
const why = err instanceof Error ? err.message : String(err);
|
|
17331
17719
|
opts.log?.(`browsers route failed: ${why}`);
|
|
17720
|
+
if (err instanceof BrowserRegistryError) {
|
|
17721
|
+
json2(res, err.code === "registration_not_found" ? 404 : 409, {
|
|
17722
|
+
error: err.code,
|
|
17723
|
+
detail: err.code,
|
|
17724
|
+
capabilities: [...MCP_MANAGEMENT_CAPABILITIES]
|
|
17725
|
+
});
|
|
17726
|
+
return;
|
|
17727
|
+
}
|
|
17332
17728
|
json2(res, 400, { error: "bad request", detail: why });
|
|
17333
17729
|
});
|
|
17334
17730
|
return;
|
|
@@ -17630,37 +18026,6 @@ function parsePort(raw) {
|
|
|
17630
18026
|
if (!Number.isInteger(n) || n < 1 || n > 65535) return null;
|
|
17631
18027
|
return n;
|
|
17632
18028
|
}
|
|
17633
|
-
var LOG_MAX_BYTES = 1024 * 1024;
|
|
17634
|
-
var DIR_NAME = ".datalab-mcp";
|
|
17635
|
-
var FILE_NAME = "connector.log";
|
|
17636
|
-
function logDir(home = homedir()) {
|
|
17637
|
-
return join2(home, DIR_NAME);
|
|
17638
|
-
}
|
|
17639
|
-
function logPath(home = homedir()) {
|
|
17640
|
-
return join2(logDir(home), FILE_NAME);
|
|
17641
|
-
}
|
|
17642
|
-
function rolledLogPath(home = homedir()) {
|
|
17643
|
-
return `${logPath(home)}.1`;
|
|
17644
|
-
}
|
|
17645
|
-
function rollIfLarge(home = homedir(), maxBytes = LOG_MAX_BYTES) {
|
|
17646
|
-
try {
|
|
17647
|
-
const size = statSync(logPath(home)).size;
|
|
17648
|
-
if (size < maxBytes) return false;
|
|
17649
|
-
renameSync(logPath(home), rolledLogPath(home));
|
|
17650
|
-
return true;
|
|
17651
|
-
} catch {
|
|
17652
|
-
return false;
|
|
17653
|
-
}
|
|
17654
|
-
}
|
|
17655
|
-
function openLogFd(home = homedir()) {
|
|
17656
|
-
try {
|
|
17657
|
-
mkdirSync(logDir(home), { recursive: true });
|
|
17658
|
-
rollIfLarge(home);
|
|
17659
|
-
return openSync(logPath(home), "a");
|
|
17660
|
-
} catch {
|
|
17661
|
-
return null;
|
|
17662
|
-
}
|
|
17663
|
-
}
|
|
17664
18029
|
function tryConnect(host, port, timeoutMs, deps = {}) {
|
|
17665
18030
|
const connect = deps.createConnection ?? createConnection;
|
|
17666
18031
|
return new Promise((resolve) => {
|
|
@@ -17775,268 +18140,7 @@ var Lifecycle = class {
|
|
|
17775
18140
|
}
|
|
17776
18141
|
}
|
|
17777
18142
|
};
|
|
17778
|
-
var
|
|
17779
|
-
function ledgerPath(home) {
|
|
17780
|
-
return join3(logDir(home), FILE_NAME2);
|
|
17781
|
-
}
|
|
17782
|
-
function isRegistration(row) {
|
|
17783
|
-
if (typeof row !== "object" || row === null) return false;
|
|
17784
|
-
const r = row;
|
|
17785
|
-
const id = r["browserId"];
|
|
17786
|
-
const hash2 = r["tokenHash"];
|
|
17787
|
-
return (id === null || typeof id === "string" && id.length > 0) && typeof hash2 === "string" && /^[a-f0-9]{64}$/.test(hash2) && typeof r["label"] === "string" && typeof r["createdAt"] === "string";
|
|
17788
|
-
}
|
|
17789
|
-
function readLedger(path) {
|
|
17790
|
-
let raw;
|
|
17791
|
-
try {
|
|
17792
|
-
raw = readFileSync(path, "utf8");
|
|
17793
|
-
} catch (err) {
|
|
17794
|
-
if (err.code === "ENOENT") {
|
|
17795
|
-
return { state: "absent" };
|
|
17796
|
-
}
|
|
17797
|
-
return { state: "unreadable", reason: describe3(err) };
|
|
17798
|
-
}
|
|
17799
|
-
let parsed;
|
|
17800
|
-
try {
|
|
17801
|
-
parsed = JSON.parse(raw);
|
|
17802
|
-
} catch (err) {
|
|
17803
|
-
return { state: "unreadable", reason: describe3(err) };
|
|
17804
|
-
}
|
|
17805
|
-
if (!Array.isArray(parsed)) {
|
|
17806
|
-
return { state: "unreadable", reason: "not a list of browsers" };
|
|
17807
|
-
}
|
|
17808
|
-
if (!parsed.every(isRegistration)) {
|
|
17809
|
-
return { state: "unreadable", reason: "a row is not a browser" };
|
|
17810
|
-
}
|
|
17811
|
-
const browserIds = /* @__PURE__ */ new Set();
|
|
17812
|
-
const tokenHashes = /* @__PURE__ */ new Set();
|
|
17813
|
-
for (const row of parsed) {
|
|
17814
|
-
if (tokenHashes.has(row.tokenHash) || row.browserId !== null && browserIds.has(row.browserId)) {
|
|
17815
|
-
return { state: "unreadable", reason: "duplicate browser authority" };
|
|
17816
|
-
}
|
|
17817
|
-
tokenHashes.add(row.tokenHash);
|
|
17818
|
-
if (row.browserId !== null) browserIds.add(row.browserId);
|
|
17819
|
-
}
|
|
17820
|
-
return { state: "ok", rows: parsed };
|
|
17821
|
-
}
|
|
17822
|
-
function ledgerOverlapsToken(rows, tokenHash) {
|
|
17823
|
-
return rows.some((row) => row.tokenHash === tokenHash);
|
|
17824
|
-
}
|
|
17825
|
-
async function writeLedger(path, claims) {
|
|
17826
|
-
const queued = (writing.get(path) ?? Promise.resolve()).then(
|
|
17827
|
-
() => writeOnce(path, claims),
|
|
17828
|
-
() => writeOnce(path, claims)
|
|
17829
|
-
);
|
|
17830
|
-
writing.set(path, queued);
|
|
17831
|
-
try {
|
|
17832
|
-
await queued;
|
|
17833
|
-
} finally {
|
|
17834
|
-
if (writing.get(path) === queued) writing.delete(path);
|
|
17835
|
-
}
|
|
17836
|
-
}
|
|
17837
|
-
var writing = /* @__PURE__ */ new Map();
|
|
17838
|
-
async function writeOnce(path, claims) {
|
|
17839
|
-
const tmp = `${path}.${process.pid}.tmp`;
|
|
17840
|
-
await mkdir2(dirname(path), { recursive: true, mode: 448 });
|
|
17841
|
-
await writeFile2(tmp, `${JSON.stringify(claims, null, 2)}
|
|
17842
|
-
`, {
|
|
17843
|
-
mode: 384
|
|
17844
|
-
});
|
|
17845
|
-
await rename(tmp, path);
|
|
17846
|
-
}
|
|
17847
|
-
function claimedDigestsFrom(claims) {
|
|
17848
|
-
const pairs = [];
|
|
17849
|
-
for (const claim of claims) {
|
|
17850
|
-
if (claim.browserId !== null && claim.browserId.length > 0) {
|
|
17851
|
-
pairs.push([claim.browserId, claim.tokenHash]);
|
|
17852
|
-
}
|
|
17853
|
-
}
|
|
17854
|
-
return pairs;
|
|
17855
|
-
}
|
|
17856
|
-
function describe3(err) {
|
|
17857
|
-
return err instanceof Error ? err.message : String(err);
|
|
17858
|
-
}
|
|
17859
|
-
var HANDLE_LENGTH = 8;
|
|
17860
|
-
function mintToken() {
|
|
17861
|
-
return randomBytes(32).toString("hex");
|
|
17862
|
-
}
|
|
17863
|
-
function handleOf(claim) {
|
|
17864
|
-
return claim.tokenHash.slice(0, HANDLE_LENGTH);
|
|
17865
|
-
}
|
|
17866
|
-
function matches(claim, handle) {
|
|
17867
|
-
return handleOf(claim) === handle || claim.browserId === handle;
|
|
17868
|
-
}
|
|
17869
|
-
function createBrowserRegistry(opts) {
|
|
17870
|
-
const now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
17871
|
-
const revoked = /* @__PURE__ */ new Set();
|
|
17872
|
-
const initial = readLedger(opts.path);
|
|
17873
|
-
const unreadable = initial.state === "unreadable" ? initial.reason : null;
|
|
17874
|
-
let rows = initial.state === "ok" ? initial.rows.map((row) => ({ ...row })) : [];
|
|
17875
|
-
let mutationTail = Promise.resolve();
|
|
17876
|
-
let pendingMutations = 0;
|
|
17877
|
-
const view = (source = rows) => {
|
|
17878
|
-
if (unreadable !== null) {
|
|
17879
|
-
throw new Error(`browser registry is unreadable: ${unreadable}`);
|
|
17880
|
-
}
|
|
17881
|
-
if (!opts.forbiddenTokenHash) return { rows: [...source], quarantined: [] };
|
|
17882
|
-
const quarantined = source.filter(
|
|
17883
|
-
(row) => timingSafeEqual(row.tokenHash, opts.forbiddenTokenHash)
|
|
17884
|
-
);
|
|
17885
|
-
return {
|
|
17886
|
-
rows: source.filter(
|
|
17887
|
-
(row) => !timingSafeEqual(row.tokenHash, opts.forbiddenTokenHash)
|
|
17888
|
-
),
|
|
17889
|
-
quarantined
|
|
17890
|
-
};
|
|
17891
|
-
};
|
|
17892
|
-
const current = () => view().rows;
|
|
17893
|
-
const serialize = (work) => {
|
|
17894
|
-
pendingMutations += 1;
|
|
17895
|
-
const result = mutationTail.then(work, work);
|
|
17896
|
-
mutationTail = result.then(
|
|
17897
|
-
() => {
|
|
17898
|
-
pendingMutations -= 1;
|
|
17899
|
-
},
|
|
17900
|
-
() => {
|
|
17901
|
-
pendingMutations -= 1;
|
|
17902
|
-
}
|
|
17903
|
-
);
|
|
17904
|
-
return result;
|
|
17905
|
-
};
|
|
17906
|
-
const transact = (propose) => {
|
|
17907
|
-
if (unreadable !== null) {
|
|
17908
|
-
return Promise.reject(
|
|
17909
|
-
new Error(`browser registry is unreadable: ${unreadable}`)
|
|
17910
|
-
);
|
|
17911
|
-
}
|
|
17912
|
-
return serialize(async () => {
|
|
17913
|
-
const proposal = propose(rows.map((row) => ({ ...row })));
|
|
17914
|
-
await writeLedger(opts.path, proposal.next);
|
|
17915
|
-
rows = proposal.next;
|
|
17916
|
-
proposal.commit?.();
|
|
17917
|
-
return proposal.result;
|
|
17918
|
-
});
|
|
17919
|
-
};
|
|
17920
|
-
return {
|
|
17921
|
-
async add(label) {
|
|
17922
|
-
const token = mintToken();
|
|
17923
|
-
const claim = {
|
|
17924
|
-
browserId: null,
|
|
17925
|
-
tokenHash: digestToken(token),
|
|
17926
|
-
label,
|
|
17927
|
-
createdAt: now().toISOString()
|
|
17928
|
-
};
|
|
17929
|
-
return transact((snapshot) => ({
|
|
17930
|
-
next: [...snapshot, claim],
|
|
17931
|
-
result: { handle: handleOf(claim), token }
|
|
17932
|
-
}));
|
|
17933
|
-
},
|
|
17934
|
-
async list() {
|
|
17935
|
-
return current().map((claim) => ({
|
|
17936
|
-
handle: handleOf(claim),
|
|
17937
|
-
label: claim.label,
|
|
17938
|
-
browserId: claim.browserId,
|
|
17939
|
-
createdAt: claim.createdAt
|
|
17940
|
-
}));
|
|
17941
|
-
},
|
|
17942
|
-
claim(browserId, tokenHash) {
|
|
17943
|
-
if (pendingMutations > 0) return false;
|
|
17944
|
-
if (opts.claimed.has(browserId)) return false;
|
|
17945
|
-
for (const held of opts.claimed.values()) {
|
|
17946
|
-
if (timingSafeEqual(held, tokenHash)) return false;
|
|
17947
|
-
}
|
|
17948
|
-
let claims;
|
|
17949
|
-
try {
|
|
17950
|
-
claims = current();
|
|
17951
|
-
} catch {
|
|
17952
|
-
return false;
|
|
17953
|
-
}
|
|
17954
|
-
const row = claims.find(
|
|
17955
|
-
(claim) => claim.browserId === null && timingSafeEqual(claim.tokenHash, tokenHash)
|
|
17956
|
-
);
|
|
17957
|
-
if (!row) return false;
|
|
17958
|
-
const stored = rows.find((claim) => claim.tokenHash === row.tokenHash);
|
|
17959
|
-
if (!stored) return false;
|
|
17960
|
-
stored.browserId = browserId;
|
|
17961
|
-
opts.claimed.set(browserId, row.tokenHash);
|
|
17962
|
-
opts.onClaim?.(browserId, stored.label);
|
|
17963
|
-
const snapshot = rows.map((entry) => ({ ...entry }));
|
|
17964
|
-
void serialize(async () => {
|
|
17965
|
-
try {
|
|
17966
|
-
await writeLedger(opts.path, snapshot);
|
|
17967
|
-
} catch (error51) {
|
|
17968
|
-
if (stored.browserId === browserId) stored.browserId = null;
|
|
17969
|
-
if (opts.claimed.get(browserId) === row.tokenHash) {
|
|
17970
|
-
opts.claimed.delete(browserId);
|
|
17971
|
-
opts.onRevoke?.(browserId);
|
|
17972
|
-
}
|
|
17973
|
-
throw error51;
|
|
17974
|
-
}
|
|
17975
|
-
}).catch(() => void 0);
|
|
17976
|
-
return true;
|
|
17977
|
-
},
|
|
17978
|
-
async revoke(handle) {
|
|
17979
|
-
return transact((snapshot) => {
|
|
17980
|
-
const claims = view(snapshot).rows;
|
|
17981
|
-
const removed = claims.filter((claim) => matches(claim, handle));
|
|
17982
|
-
if (removed.length === 0) {
|
|
17983
|
-
return { next: snapshot, result: false };
|
|
17984
|
-
}
|
|
17985
|
-
const removedHashes = new Set(removed.map((claim) => claim.tokenHash));
|
|
17986
|
-
return {
|
|
17987
|
-
next: snapshot.filter((claim) => !removedHashes.has(claim.tokenHash)),
|
|
17988
|
-
result: true,
|
|
17989
|
-
commit: () => {
|
|
17990
|
-
for (const claim of removed) {
|
|
17991
|
-
revoked.add(claim.tokenHash);
|
|
17992
|
-
if (claim.browserId !== null) {
|
|
17993
|
-
opts.claimed.delete(claim.browserId);
|
|
17994
|
-
opts.onRevoke?.(claim.browserId);
|
|
17995
|
-
}
|
|
17996
|
-
}
|
|
17997
|
-
}
|
|
17998
|
-
};
|
|
17999
|
-
});
|
|
18000
|
-
},
|
|
18001
|
-
async repair() {
|
|
18002
|
-
return transact((snapshot) => {
|
|
18003
|
-
const currentView = view(snapshot);
|
|
18004
|
-
const removed = currentView.quarantined.map(handleOf);
|
|
18005
|
-
const removedHashes = new Set(
|
|
18006
|
-
currentView.quarantined.map((row) => row.tokenHash)
|
|
18007
|
-
);
|
|
18008
|
-
return {
|
|
18009
|
-
next: snapshot.filter((row) => !removedHashes.has(row.tokenHash)),
|
|
18010
|
-
result: { removed }
|
|
18011
|
-
};
|
|
18012
|
-
});
|
|
18013
|
-
},
|
|
18014
|
-
status() {
|
|
18015
|
-
if (unreadable !== null) {
|
|
18016
|
-
return {
|
|
18017
|
-
state: "disabled",
|
|
18018
|
-
error: "browser_registry_unreadable",
|
|
18019
|
-
detail: unreadable
|
|
18020
|
-
};
|
|
18021
|
-
}
|
|
18022
|
-
if (opts.forbiddenTokenHash && ledgerOverlapsToken(rows, opts.forbiddenTokenHash)) {
|
|
18023
|
-
return {
|
|
18024
|
-
state: "degraded",
|
|
18025
|
-
error: "browser_registry_root_authority_overlap",
|
|
18026
|
-
quarantinedHandles: rows.filter(
|
|
18027
|
-
(row) => timingSafeEqual(row.tokenHash, opts.forbiddenTokenHash)
|
|
18028
|
-
).map(handleOf),
|
|
18029
|
-
recovery: "browsers repair"
|
|
18030
|
-
};
|
|
18031
|
-
}
|
|
18032
|
-
return { state: "ready" };
|
|
18033
|
-
},
|
|
18034
|
-
wasRevoked(tokenHash) {
|
|
18035
|
-
return revoked.has(tokenHash);
|
|
18036
|
-
}
|
|
18037
|
-
};
|
|
18038
|
-
}
|
|
18039
|
-
var NAME = "datalab-extension-mcp-server";
|
|
18143
|
+
var NAME = MCP_SERVER_HEALTH_NAME;
|
|
18040
18144
|
function readVersion() {
|
|
18041
18145
|
return createRequire(import.meta.url)("../package.json").version;
|
|
18042
18146
|
}
|