@roblourens/dap-cli 0.1.0 → 0.3.0

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/dist/index.js CHANGED
@@ -11,7 +11,7 @@ var CliError = class extends Error {
11
11
  adapter;
12
12
  data;
13
13
  constructor(message, category, exitCode, options = {}) {
14
- super(message);
14
+ super(message, options.cause !== void 0 ? { cause: options.cause } : void 0);
15
15
  this.name = "CliError";
16
16
  this.code = options.code ?? `${category}_error`;
17
17
  this.category = category;
@@ -846,8 +846,10 @@ function breakpointBindingGuidance(options = {}) {
846
846
  }
847
847
 
848
848
  // src/controller/ipc.ts
849
+ import { createHash } from "crypto";
849
850
  import { promises as fs } from "fs";
850
851
  import net from "net";
852
+ import { tmpdir } from "os";
851
853
  import path2 from "path";
852
854
  import { z as z2 } from "zod";
853
855
 
@@ -869,6 +871,10 @@ function getDapCliLogDir(env = process.env) {
869
871
  return path.join(getDapCliHome(env), "logs");
870
872
  }
871
873
  function getDapCliAdaptersDir(env = process.env) {
874
+ const configured = env.DAP_CLI_ADAPTERS_DIR;
875
+ if (configured !== void 0 && configured.trim().length > 0) {
876
+ return path.resolve(configured);
877
+ }
872
878
  return path.join(getDapCliHome(env), "adapters");
873
879
  }
874
880
  function getDapCliVenvPythonPath(env = process.env) {
@@ -897,6 +903,7 @@ var controllerDiscoverySchema = z2.object({
897
903
  startedAt: z2.string().min(1),
898
904
  lastHeartbeatAt: z2.string().min(1)
899
905
  });
906
+ var maximumPortableUnixSocketPathLength = 100;
900
907
  function resolveControllerDiscoveryPath(options = {}) {
901
908
  return path2.join(resolveStateDir(options), "controller.json");
902
909
  }
@@ -978,7 +985,12 @@ function createControllerEndpoint(stateDir, platform) {
978
985
  if (platform === "win32") {
979
986
  return { kind: "ipc", path: `\\\\.\\pipe\\dap-cli-${Buffer.from(stateDir).toString("hex").slice(0, 24)}` };
980
987
  }
981
- return { kind: "ipc", path: path2.join(stateDir, "controller.sock") };
988
+ const endpointPath = path2.join(stateDir, "controller.sock");
989
+ if (Buffer.byteLength(endpointPath, "utf8") <= maximumPortableUnixSocketPathLength) {
990
+ return { kind: "ipc", path: endpointPath };
991
+ }
992
+ const stateHash = createHash("sha256").update(stateDir).digest("hex").slice(0, 24);
993
+ return { kind: "ipc", path: path2.join(tmpdir(), `dap-cli-${stateHash}.sock`) };
982
994
  }
983
995
  function resolveStateDir(options) {
984
996
  return getDapCliStateDir(createEnv(options));
@@ -1153,8 +1165,8 @@ function isSessionErrorCode(code) {
1153
1165
  }
1154
1166
 
1155
1167
  // src/controller/server.ts
1156
- import path8 from "path";
1157
- import { randomBytes as randomBytes2 } from "crypto";
1168
+ import path17 from "path";
1169
+ import { randomBytes as randomBytes6 } from "crypto";
1158
1170
 
1159
1171
  // src/adapters/descriptor.ts
1160
1172
  import { z as z3 } from "zod";
@@ -1306,9 +1318,16 @@ function createSocketTransport(options) {
1306
1318
  readable: options.socket,
1307
1319
  writable: options.socket,
1308
1320
  close() {
1309
- options.socket.end();
1310
- options.socket.destroy();
1311
- return Promise.resolve();
1321
+ if (options.socket.closed) {
1322
+ return Promise.resolve();
1323
+ }
1324
+ return new Promise((resolve) => {
1325
+ options.socket.once("close", resolve);
1326
+ if (!options.socket.destroyed) {
1327
+ options.socket.end();
1328
+ options.socket.destroy();
1329
+ }
1330
+ });
1312
1331
  }
1313
1332
  };
1314
1333
  }
@@ -1342,7 +1361,7 @@ async function connectSocketAdapter(adapterId, descriptor) {
1342
1361
  }
1343
1362
  async function startServerSocketAdapter(adapterId, descriptor, logDir) {
1344
1363
  const port = await getFreePort(descriptor.host);
1345
- const args = descriptor.args.map((arg) => arg === "${port}" ? String(port) : arg);
1364
+ const args = descriptor.args.map((arg) => arg.replaceAll("${port}", String(port)));
1346
1365
  const child = spawn2(descriptor.command, args, {
1347
1366
  cwd: descriptor.cwd,
1348
1367
  env: descriptor.env === void 0 ? process.env : { ...process.env, ...descriptor.env },
@@ -1383,88 +1402,1243 @@ async function startServerSocketAdapter(adapterId, descriptor, logDir) {
1383
1402
  logStream.end(() => resolve());
1384
1403
  });
1385
1404
  }
1386
- };
1387
- } catch (error) {
1388
- await terminateChild2(child);
1389
- await new Promise((resolve) => {
1390
- logStream.end(() => resolve());
1405
+ };
1406
+ } catch (error) {
1407
+ await terminateChild2(child);
1408
+ await new Promise((resolve) => {
1409
+ logStream.end(() => resolve());
1410
+ });
1411
+ throw error;
1412
+ }
1413
+ }
1414
+ async function connectWithRetry(adapterId, host, port) {
1415
+ const deadline = Date.now() + 5e3;
1416
+ let lastError;
1417
+ while (Date.now() < deadline) {
1418
+ try {
1419
+ return await connectSocketTransport({ name: adapterId, host, port, timeoutMs: 500 });
1420
+ } catch (error) {
1421
+ lastError = error;
1422
+ await delay(50);
1423
+ }
1424
+ }
1425
+ throw lastError instanceof Error ? lastError : new Error("Timed out connecting DAP server adapter.");
1426
+ }
1427
+ function getFreePort(host) {
1428
+ return new Promise((resolve, reject) => {
1429
+ const server = net3.createServer();
1430
+ server.once("error", reject);
1431
+ server.listen(0, host, () => {
1432
+ const address = server.address();
1433
+ server.close(() => {
1434
+ if (typeof address === "object" && address !== null) {
1435
+ resolve(address.port);
1436
+ return;
1437
+ }
1438
+ reject(new Error("Failed to allocate a local adapter server port."));
1439
+ });
1440
+ });
1441
+ });
1442
+ }
1443
+ async function terminateChild2(child) {
1444
+ if (child.exitCode !== null || child.signalCode !== null) {
1445
+ return;
1446
+ }
1447
+ child.kill("SIGTERM");
1448
+ if (await waitForExit2(child, 100)) {
1449
+ return;
1450
+ }
1451
+ child.kill("SIGKILL");
1452
+ await waitForExit2(child, 100);
1453
+ }
1454
+ function waitForExit2(child, timeoutMs) {
1455
+ if (child.exitCode !== null || child.signalCode !== null) {
1456
+ return Promise.resolve(true);
1457
+ }
1458
+ return new Promise((resolve) => {
1459
+ const timer = setTimeout(() => {
1460
+ child.off("exit", onExit);
1461
+ resolve(false);
1462
+ }, timeoutMs);
1463
+ const onExit = () => {
1464
+ clearTimeout(timer);
1465
+ resolve(true);
1466
+ };
1467
+ child.once("exit", onExit);
1468
+ });
1469
+ }
1470
+ function appendStderrTail2(stderrTail, text) {
1471
+ for (const line of text.split(/\r?\n/).filter((value) => value.length > 0)) {
1472
+ stderrTail.push(line);
1473
+ while (stderrTail.length > 100) {
1474
+ stderrTail.shift();
1475
+ }
1476
+ }
1477
+ }
1478
+ async function delay(ms) {
1479
+ await new Promise((resolve) => setTimeout(resolve, ms));
1480
+ }
1481
+
1482
+ // src/adapters/builtins/jsDebug.ts
1483
+ import { promises as fs11 } from "fs";
1484
+ import path14 from "path";
1485
+
1486
+ // src/cli/confirm.ts
1487
+ import * as readline from "readline/promises";
1488
+ async function confirm(options) {
1489
+ if (options.assumeYes) {
1490
+ return true;
1491
+ }
1492
+ const stdin = options.stdin ?? process.stdin;
1493
+ const stderr = options.stderr ?? process.stderr;
1494
+ if (stdin.isTTY !== true) {
1495
+ throw usageError("Confirmation required but stdin is not a TTY.", {
1496
+ code: "provision_consent_required",
1497
+ diagnostics: [
1498
+ options.question,
1499
+ "Re-run with `--yes` / `-y` or set `DAP_CLI_ASSUME_YES=1` to pre-consent."
1500
+ ],
1501
+ data: { question: options.question }
1502
+ });
1503
+ }
1504
+ stderr.write(`
1505
+ ${options.question}
1506
+ `);
1507
+ for (const line of options.details ?? []) {
1508
+ stderr.write(` ${line}
1509
+ `);
1510
+ }
1511
+ stderr.write("Proceed? [y/N] ");
1512
+ const rl = readline.createInterface({ input: stdin, output: stderr, terminal: false });
1513
+ try {
1514
+ const answer = (await rl.question("")).trim().toLowerCase();
1515
+ if (answer === "y" || answer === "yes") {
1516
+ return true;
1517
+ }
1518
+ throw usageError("User declined provisioning consent.", {
1519
+ code: "provision_consent_declined",
1520
+ diagnostics: ["Re-run with `--yes` to pre-consent."],
1521
+ data: { question: options.question }
1522
+ });
1523
+ } finally {
1524
+ rl.close();
1525
+ }
1526
+ }
1527
+ function resolveAssumeYes(cliYes, env) {
1528
+ if (cliYes === true) {
1529
+ return true;
1530
+ }
1531
+ const value = env.DAP_CLI_ASSUME_YES;
1532
+ return value === "1" || value === "true";
1533
+ }
1534
+
1535
+ // src/adapters/provision/jsDebug.ts
1536
+ import path9 from "path";
1537
+ import { createHash as createHash2, randomBytes as randomBytes2 } from "crypto";
1538
+ import { promises as fs6, createReadStream as createReadStream2 } from "fs";
1539
+
1540
+ // src/adapters/provision/atomicInstall.ts
1541
+ import path5 from "path";
1542
+ import { promises as fs2 } from "fs";
1543
+ import { randomBytes } from "crypto";
1544
+ var CACHE_UNWRITABLE_CODES = /* @__PURE__ */ new Set(["EACCES", "EROFS", "ENOSPC", "EPERM"]);
1545
+ function isCacheUnwritableError(error) {
1546
+ if (!(error instanceof Error) || !("code" in error)) {
1547
+ return false;
1548
+ }
1549
+ const code = error.code;
1550
+ return typeof code === "string" && CACHE_UNWRITABLE_CODES.has(code);
1551
+ }
1552
+ function cacheUnwritableError(adaptersDir, errnoCode, adapterId) {
1553
+ return usageError("Adapter cache directory is not writable.", {
1554
+ code: "provision_cache_unwritable",
1555
+ diagnostics: [
1556
+ `Adapter cache: ${adaptersDir}`,
1557
+ `Filesystem error: ${errnoCode}`,
1558
+ "Override with `DAP_CLI_ADAPTERS_DIR=<writable-path>`."
1559
+ ],
1560
+ data: { adaptersDir, errnoCode, adapterId }
1561
+ });
1562
+ }
1563
+ function stagingName(adapterId) {
1564
+ return `.${adapterId}.tmp.${process.pid}.${randomBytes(4).toString("hex")}`;
1565
+ }
1566
+ async function verifyEntrypoints(stagingDir, entrypoints) {
1567
+ for (const rel of entrypoints) {
1568
+ const target = path5.join(stagingDir, rel);
1569
+ try {
1570
+ await fs2.stat(target);
1571
+ } catch (cause) {
1572
+ throw usageError("Adapter install completed but the expected entry point is missing.", {
1573
+ code: "provision_extract_failed",
1574
+ diagnostics: [`Missing entry point: ${rel}`, `Staging directory: ${stagingDir}`],
1575
+ data: { entrypoint: rel, stagingDir },
1576
+ cause
1577
+ });
1578
+ }
1579
+ }
1580
+ }
1581
+ async function atomicInstall(options) {
1582
+ const { adaptersDir, adapterId, populate, expectedEntrypoints } = options;
1583
+ try {
1584
+ await fs2.mkdir(adaptersDir, { recursive: true });
1585
+ } catch (error) {
1586
+ if (isCacheUnwritableError(error)) {
1587
+ throw cacheUnwritableError(adaptersDir, error.code ?? "unknown", adapterId);
1588
+ }
1589
+ throw error;
1590
+ }
1591
+ const canonical = path5.join(adaptersDir, adapterId);
1592
+ const staging = path5.join(adaptersDir, stagingName(adapterId));
1593
+ try {
1594
+ await fs2.mkdir(staging, { recursive: true });
1595
+ } catch (error) {
1596
+ if (isCacheUnwritableError(error)) {
1597
+ throw cacheUnwritableError(adaptersDir, error.code ?? "unknown", adapterId);
1598
+ }
1599
+ throw error;
1600
+ }
1601
+ try {
1602
+ await populate(staging);
1603
+ await verifyEntrypoints(staging, expectedEntrypoints);
1604
+ await fs2.rm(canonical, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
1605
+ await fs2.rename(staging, canonical);
1606
+ return canonical;
1607
+ } catch (error) {
1608
+ await fs2.rm(staging, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }).catch(() => void 0);
1609
+ if (!(error instanceof CliError) && isCacheUnwritableError(error)) {
1610
+ throw cacheUnwritableError(adaptersDir, error.code ?? "unknown", adapterId);
1611
+ }
1612
+ throw error;
1613
+ }
1614
+ }
1615
+
1616
+ // src/adapters/provision/http.ts
1617
+ import { createWriteStream as createWriteStream3 } from "fs";
1618
+ import { promises as fs3 } from "fs";
1619
+ import { Readable } from "stream";
1620
+ import { pipeline } from "stream/promises";
1621
+ import path6 from "path";
1622
+ import { fetch, ProxyAgent } from "undici";
1623
+ var LOCAL_HOSTS = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1"]);
1624
+ function pickProxyEnv(env, protocol) {
1625
+ if (protocol === "https:") {
1626
+ return env.HTTPS_PROXY ?? env.https_proxy;
1627
+ }
1628
+ return env.HTTP_PROXY ?? env.http_proxy;
1629
+ }
1630
+ function matchesNoProxy(host, noProxy) {
1631
+ if (noProxy === void 0 || noProxy.length === 0) {
1632
+ return false;
1633
+ }
1634
+ const normalized = host.toLowerCase();
1635
+ for (const raw of noProxy.split(",")) {
1636
+ const pattern = raw.trim().toLowerCase();
1637
+ if (pattern.length === 0) {
1638
+ continue;
1639
+ }
1640
+ if (pattern === "*") {
1641
+ return true;
1642
+ }
1643
+ const stripped = pattern.startsWith(".") ? pattern.slice(1) : pattern;
1644
+ if (normalized === stripped || normalized.endsWith(`.${stripped}`)) {
1645
+ return true;
1646
+ }
1647
+ }
1648
+ return false;
1649
+ }
1650
+ function resolveProxy(targetUrl, env) {
1651
+ const proxyUrl = pickProxyEnv(env, targetUrl.protocol);
1652
+ if (proxyUrl === void 0 || proxyUrl.length === 0) {
1653
+ return { dispatcher: void 0, proxyUrl: void 0 };
1654
+ }
1655
+ if (matchesNoProxy(targetUrl.hostname, env.NO_PROXY ?? env.no_proxy)) {
1656
+ return { dispatcher: void 0, proxyUrl: void 0 };
1657
+ }
1658
+ return { dispatcher: new ProxyAgent(proxyUrl), proxyUrl };
1659
+ }
1660
+ function isLocalHost(host) {
1661
+ return LOCAL_HOSTS.has(host.toLowerCase());
1662
+ }
1663
+ function sanitizeUrl(raw) {
1664
+ try {
1665
+ const parsed = new URL(raw);
1666
+ parsed.username = "";
1667
+ parsed.password = "";
1668
+ parsed.search = "";
1669
+ parsed.hash = "";
1670
+ return parsed.toString();
1671
+ } catch {
1672
+ return raw.split("?")[0]?.split("#")[0] ?? raw;
1673
+ }
1674
+ }
1675
+ function rateLimitError(url, retryAfter) {
1676
+ const safeUrl = sanitizeUrl(url);
1677
+ return usageError("GitHub rate limit exceeded.", {
1678
+ code: "provision_rate_limited",
1679
+ diagnostics: [
1680
+ `URL: ${safeUrl}`,
1681
+ retryAfter !== null && retryAfter.length > 0 ? `Retry after epoch ${retryAfter}.` : "Retry-After header not provided.",
1682
+ "Set `GITHUB_TOKEN` to raise the GitHub API rate limit."
1683
+ ],
1684
+ data: { url: safeUrl, status: 403, retryAfter: retryAfter ?? void 0 }
1685
+ });
1686
+ }
1687
+ function networkError(url, status, statusText) {
1688
+ const safeUrl = sanitizeUrl(url);
1689
+ return usageError("Adapter download failed.", {
1690
+ code: "provision_network_error",
1691
+ diagnostics: [`URL: ${safeUrl}`, `HTTP ${status} ${statusText}`],
1692
+ data: { url: safeUrl, status, statusText }
1693
+ });
1694
+ }
1695
+ function networkCauseError(url, code, message, cause) {
1696
+ const safeUrl = sanitizeUrl(url);
1697
+ return usageError("Adapter download failed.", {
1698
+ code: "provision_network_error",
1699
+ diagnostics: [`URL: ${safeUrl}`, code !== void 0 ? `Cause: ${code}` : `Cause: ${message}`],
1700
+ data: { url: safeUrl, causeCode: code },
1701
+ cause
1702
+ });
1703
+ }
1704
+ function proxyError(url, proxyUrl, code, message, cause) {
1705
+ const safeUrl = sanitizeUrl(url);
1706
+ const safeProxy = sanitizeUrl(proxyUrl);
1707
+ return usageError("Adapter download failed through configured proxy.", {
1708
+ code: "provision_proxy_error",
1709
+ diagnostics: [
1710
+ `URL: ${safeUrl}`,
1711
+ `Proxy: ${safeProxy}`,
1712
+ code !== void 0 ? `Cause: ${code}` : `Cause: ${message}`,
1713
+ "Verify `HTTPS_PROXY` is correct or set `NO_PROXY=github.com` to bypass."
1714
+ ],
1715
+ data: { url: safeUrl, proxyUrl: safeProxy, causeCode: code },
1716
+ cause
1717
+ });
1718
+ }
1719
+ function extractCause(error) {
1720
+ if (error instanceof Error && "cause" in error) {
1721
+ const cause = error.cause;
1722
+ if (cause !== void 0 && cause !== null && typeof cause === "object") {
1723
+ const codeValue = cause.code;
1724
+ const messageValue = cause.message;
1725
+ const result = {};
1726
+ if (typeof codeValue === "string") {
1727
+ result.code = codeValue;
1728
+ }
1729
+ if (typeof messageValue === "string") {
1730
+ result.message = messageValue;
1731
+ }
1732
+ return result;
1733
+ }
1734
+ }
1735
+ return void 0;
1736
+ }
1737
+ async function downloadToFile(options) {
1738
+ const env = options.env ?? process.env;
1739
+ let target;
1740
+ try {
1741
+ target = new URL(options.url);
1742
+ } catch {
1743
+ throw usageError("Invalid download URL.", {
1744
+ code: "provision_network_error",
1745
+ diagnostics: [`URL: ${sanitizeUrl(options.url)}`, "URL could not be parsed."],
1746
+ data: { url: sanitizeUrl(options.url) }
1747
+ });
1748
+ }
1749
+ if (target.protocol !== "https:" && !(target.protocol === "http:" && isLocalHost(target.hostname))) {
1750
+ throw usageError("Refusing non-HTTPS download URL.", {
1751
+ code: "provision_network_error",
1752
+ diagnostics: [`URL: ${sanitizeUrl(options.url)}`, "Downloads must use https:// (http://localhost is allowed for tests)."],
1753
+ data: { url: sanitizeUrl(options.url) }
1754
+ });
1755
+ }
1756
+ const { dispatcher, proxyUrl } = resolveProxy(target, env);
1757
+ await fs3.mkdir(path6.dirname(options.destPath), { recursive: true });
1758
+ let response;
1759
+ try {
1760
+ response = await fetch(options.url, dispatcher !== void 0 ? { dispatcher } : void 0);
1761
+ } catch (error) {
1762
+ const cause = extractCause(error);
1763
+ const message = error instanceof Error ? error.message : String(error);
1764
+ if (proxyUrl !== void 0) {
1765
+ throw proxyError(options.url, proxyUrl, cause?.code, cause?.message ?? message, error);
1766
+ }
1767
+ throw networkCauseError(options.url, cause?.code, cause?.message ?? message, error);
1768
+ }
1769
+ if (!response.ok) {
1770
+ if (response.status === 403 && response.headers.get("x-ratelimit-remaining") === "0") {
1771
+ throw rateLimitError(options.url, response.headers.get("x-ratelimit-reset"));
1772
+ }
1773
+ throw networkError(options.url, response.status, response.statusText);
1774
+ }
1775
+ if (response.body === null) {
1776
+ throw usageError("Adapter download returned an empty body.", {
1777
+ code: "provision_network_error",
1778
+ diagnostics: [`URL: ${sanitizeUrl(options.url)}`],
1779
+ data: { url: sanitizeUrl(options.url) }
1780
+ });
1781
+ }
1782
+ const contentLengthHeader = response.headers.get("content-length");
1783
+ const totalBytes = contentLengthHeader !== null ? Number.parseInt(contentLengthHeader, 10) : void 0;
1784
+ const total = Number.isFinite(totalBytes) ? totalBytes : void 0;
1785
+ let bytesRead = 0;
1786
+ const onProgress = options.onProgress;
1787
+ const body = Readable.fromWeb(response.body);
1788
+ if (onProgress !== void 0) {
1789
+ body.on("data", (chunk) => {
1790
+ bytesRead += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
1791
+ onProgress(bytesRead, total);
1792
+ });
1793
+ }
1794
+ try {
1795
+ await pipeline(body, createWriteStream3(options.destPath));
1796
+ } catch (error) {
1797
+ await fs3.rm(options.destPath, { force: true }).catch(() => void 0);
1798
+ const message = error instanceof Error ? error.message : String(error);
1799
+ throw usageError("Adapter download stream failed.", {
1800
+ code: "provision_network_error",
1801
+ diagnostics: [`URL: ${sanitizeUrl(options.url)}`, message],
1802
+ data: { url: sanitizeUrl(options.url) },
1803
+ cause: error
1804
+ });
1805
+ }
1806
+ }
1807
+
1808
+ // src/adapters/provision/extractTarGz.ts
1809
+ import { createReadStream } from "fs";
1810
+ import * as tar from "tar";
1811
+ async function extractTarGz(archivePath, destDir, options = {}) {
1812
+ await new Promise((resolve, reject) => {
1813
+ const source = createReadStream(archivePath);
1814
+ const extract = tar.x({
1815
+ cwd: destDir,
1816
+ strict: true,
1817
+ strip: options.strip ?? 0
1818
+ });
1819
+ source.on("error", reject);
1820
+ extract.on("error", reject);
1821
+ extract.on("finish", resolve);
1822
+ source.pipe(extract);
1823
+ }).catch((error) => {
1824
+ const message = error instanceof Error ? error.message : String(error);
1825
+ throw usageError("Archive extraction failed.", {
1826
+ code: "provision_extract_failed",
1827
+ diagnostics: [archivePath, message],
1828
+ data: { archivePath },
1829
+ cause: error
1830
+ });
1831
+ });
1832
+ }
1833
+
1834
+ // src/adapters/provision/lock.ts
1835
+ import path7 from "path";
1836
+ import { promises as fs4 } from "fs";
1837
+ import * as lockfile from "proper-lockfile";
1838
+ var PRODUCTION_RETRY = {
1839
+ retries: 60,
1840
+ minTimeout: 500,
1841
+ maxTimeout: 2e3,
1842
+ factor: 1
1843
+ };
1844
+ var STALE_MS = 5 * 60 * 1e3;
1845
+ var CACHE_UNWRITABLE_CODES2 = /* @__PURE__ */ new Set(["EACCES", "EROFS", "ENOSPC", "EPERM"]);
1846
+ function getErrnoCode(error) {
1847
+ if (error instanceof Error && "code" in error) {
1848
+ const code = error.code;
1849
+ if (typeof code === "string") {
1850
+ return code;
1851
+ }
1852
+ }
1853
+ return void 0;
1854
+ }
1855
+ function cacheUnwritableError2(adaptersDir, errnoCode, adapterId) {
1856
+ return usageError("Adapter cache directory is not writable.", {
1857
+ code: "provision_cache_unwritable",
1858
+ diagnostics: [
1859
+ `Adapter cache: ${adaptersDir}`,
1860
+ `Filesystem error: ${errnoCode}`,
1861
+ "Override with `DAP_CLI_ADAPTERS_DIR=<writable-path>`."
1862
+ ],
1863
+ data: { adaptersDir, errnoCode, adapterId }
1864
+ });
1865
+ }
1866
+ function resolveRetry(options) {
1867
+ if (options?.retryOverride !== void 0) {
1868
+ return options.retryOverride;
1869
+ }
1870
+ const env = process.env.DAP_CLI_LOCK_RETRY_OVERRIDE;
1871
+ if (env !== void 0 && env.length > 0) {
1872
+ try {
1873
+ const parsed = JSON.parse(env);
1874
+ if (parsed !== null && typeof parsed === "object") {
1875
+ const candidate = parsed;
1876
+ const retries = candidate.retries;
1877
+ const minTimeout = candidate.minTimeout;
1878
+ const maxTimeout = candidate.maxTimeout;
1879
+ const factor = candidate.factor;
1880
+ if (typeof retries === "number" && typeof minTimeout === "number" && typeof maxTimeout === "number" && typeof factor === "number") {
1881
+ return { retries, minTimeout, maxTimeout, factor };
1882
+ }
1883
+ }
1884
+ } catch {
1885
+ }
1886
+ }
1887
+ return PRODUCTION_RETRY;
1888
+ }
1889
+ function lockTimeoutError(adapterId, sentinel) {
1890
+ return usageError("Timed out waiting for adapter install lock.", {
1891
+ code: "provision_lock_timeout",
1892
+ diagnostics: [
1893
+ `Adapter: ${adapterId}`,
1894
+ `Lock sentinel: ${sentinel}`,
1895
+ `Another dap-cli process may be installing; if none, delete ${sentinel} and retry.`
1896
+ ],
1897
+ data: { adapterId, sentinel }
1898
+ });
1899
+ }
1900
+ async function withAdapterLock(adaptersDir, adapterId, fn, options) {
1901
+ try {
1902
+ await fs4.mkdir(adaptersDir, { recursive: true });
1903
+ } catch (error) {
1904
+ const errnoCode = getErrnoCode(error);
1905
+ if (errnoCode !== void 0 && CACHE_UNWRITABLE_CODES2.has(errnoCode)) {
1906
+ throw cacheUnwritableError2(adaptersDir, errnoCode, adapterId);
1907
+ }
1908
+ throw error;
1909
+ }
1910
+ const sentinel = path7.join(adaptersDir, `.${adapterId}.lock-target`);
1911
+ try {
1912
+ await fs4.writeFile(sentinel, "", { flag: "a" });
1913
+ } catch (error) {
1914
+ const errnoCode = getErrnoCode(error);
1915
+ if (errnoCode !== void 0 && CACHE_UNWRITABLE_CODES2.has(errnoCode)) {
1916
+ throw cacheUnwritableError2(adaptersDir, errnoCode, adapterId);
1917
+ }
1918
+ throw error;
1919
+ }
1920
+ const retry = resolveRetry(options);
1921
+ let release;
1922
+ try {
1923
+ release = await lockfile.lock(sentinel, {
1924
+ stale: STALE_MS,
1925
+ realpath: false,
1926
+ retries: { ...retry }
1927
+ });
1928
+ } catch (error) {
1929
+ const errnoCode = getErrnoCode(error);
1930
+ if (errnoCode === "ELOCKED" || errnoCode === "EEXIST") {
1931
+ throw lockTimeoutError(adapterId, sentinel);
1932
+ }
1933
+ if (errnoCode !== void 0 && CACHE_UNWRITABLE_CODES2.has(errnoCode)) {
1934
+ throw cacheUnwritableError2(adaptersDir, errnoCode, adapterId);
1935
+ }
1936
+ throw error;
1937
+ }
1938
+ try {
1939
+ return await fn();
1940
+ } finally {
1941
+ if (release !== void 0) {
1942
+ await release().catch(() => void 0);
1943
+ }
1944
+ }
1945
+ }
1946
+
1947
+ // src/adapters/provision/consent.ts
1948
+ import path8 from "path";
1949
+ import { promises as fs5 } from "fs";
1950
+ function markerPath(adaptersDir, adapterId, version) {
1951
+ return path8.join(adaptersDir, adapterId, `.consent-${version}`);
1952
+ }
1953
+ async function hasConsentMarker(adaptersDir, adapterId, version) {
1954
+ try {
1955
+ await fs5.stat(markerPath(adaptersDir, adapterId, version));
1956
+ return true;
1957
+ } catch {
1958
+ return false;
1959
+ }
1960
+ }
1961
+ async function writeConsentMarker(adaptersDir, adapterId, version) {
1962
+ const filePath = markerPath(adaptersDir, adapterId, version);
1963
+ await fs5.mkdir(path8.dirname(filePath), { recursive: true });
1964
+ await fs5.writeFile(filePath, `${(/* @__PURE__ */ new Date()).toISOString()}
1965
+ `, { flag: "w" });
1966
+ }
1967
+
1968
+ // src/adapters/provision/checksums.ts
1969
+ var JS_DEBUG_VERSION = "1.117.0";
1970
+ var DEBUGPY_VERSION = "1.8.20";
1971
+ var DELVE_VERSION = "v1.26.3";
1972
+ var CODELLDB_VERSION = "v1.12.2";
1973
+ var JS_DEBUG_CHECKSUMS = {
1974
+ "1.117.0": "ad8d04ede9d4b75cc290fd5438a65047a06f786d04f604b6112485b36f090772"
1975
+ };
1976
+ var DELVE_CHECKSUMS = {
1977
+ "v1.26.3": {
1978
+ darwin_arm64: "7f28483a42f0a911f29b236aa40d24d7099f1b0ec54c56c4d439a6903d478a3d",
1979
+ darwin_amd64: "6827a438473167a1e0805b4546e5bf2d53401530f694deb35e41c6e7b46e27c8",
1980
+ linux_amd64: "cdd4d6b2a638d8f26468d82a76b766df594641490bea566629305d90fbccc06e",
1981
+ linux_arm64: "5b03fd74895d676c4435bec1aade0863be1489a4be1bb5c9269c6ef389bf5d2d",
1982
+ windows_amd64: "f9e15b8f3628e4c7bfe481011bea458df754d0e75c6ff4ab01c71294165950fd"
1983
+ }
1984
+ };
1985
+ var CODELLDB_CHECKSUMS = {
1986
+ "v1.12.2": {
1987
+ darwin_arm64: "c836b81c6f2da467b5920a376a7bfc849dc4b4d81b19779dedf1c685cb4aa1a0"
1988
+ }
1989
+ };
1990
+
1991
+ // src/adapters/provision/jsDebug.ts
1992
+ var ADAPTER_ID = "js-debug";
1993
+ var ENTRYPOINTS = ["src/dapDebugServer.js", "src/bootloader.js"];
1994
+ var PACKAGE_BOUNDARY = '{"type":"commonjs"}\n';
1995
+ function resolveBaseUrl(env) {
1996
+ const override = env.DAP_CLI_PROVISION_RELEASE_BASE_URL;
1997
+ return override !== void 0 && override.length > 0 ? override : "https://github.com";
1998
+ }
1999
+ function downloadUrl(env) {
2000
+ return `${resolveBaseUrl(env)}/microsoft/vscode-js-debug/releases/download/v${JS_DEBUG_VERSION}/js-debug-dap-v${JS_DEBUG_VERSION}.tar.gz`;
2001
+ }
2002
+ async function fileSha256(filePath) {
2003
+ return await new Promise((resolve, reject) => {
2004
+ const hash = createHash2("sha256");
2005
+ const stream = createReadStream2(filePath);
2006
+ stream.on("error", reject);
2007
+ stream.on("data", (chunk) => hash.update(chunk));
2008
+ stream.on("end", () => resolve(hash.digest("hex")));
2009
+ });
2010
+ }
2011
+ async function entrypointsExist(installRoot) {
2012
+ for (const rel of ENTRYPOINTS) {
2013
+ try {
2014
+ await fs6.access(path9.join(installRoot, rel));
2015
+ } catch {
2016
+ return false;
2017
+ }
2018
+ }
2019
+ return true;
2020
+ }
2021
+ async function provisionJsDebug(ctx) {
2022
+ const installRoot = path9.join(ctx.adaptersDir, ADAPTER_ID);
2023
+ const entrypoint = path9.join(installRoot, "src", "dapDebugServer.js");
2024
+ if (await hasConsentMarker(ctx.adaptersDir, ADAPTER_ID, JS_DEBUG_VERSION) && await entrypointsExist(installRoot)) {
2025
+ return {
2026
+ adapterId: "js-debug",
2027
+ version: JS_DEBUG_VERSION,
2028
+ installRoot,
2029
+ entrypoint,
2030
+ fromCache: true
2031
+ };
2032
+ }
2033
+ const url = downloadUrl(ctx.env);
2034
+ const expectedSha = JS_DEBUG_CHECKSUMS[JS_DEBUG_VERSION];
2035
+ if (expectedSha === void 0) {
2036
+ throw usageError(`No SHA-256 checksum recorded for js-debug v${JS_DEBUG_VERSION}.`, {
2037
+ code: "provision_checksum_mismatch",
2038
+ diagnostics: [
2039
+ `Adapter: js-debug ${JS_DEBUG_VERSION}`,
2040
+ "Edit src/adapters/provision/checksums.ts and add the hash for this version.",
2041
+ "Re-run setup or report at https://github.com/roblourens/dap-cli/issues if persistent."
2042
+ ],
2043
+ data: {
2044
+ adapterId: "js-debug",
2045
+ version: JS_DEBUG_VERSION
2046
+ }
2047
+ });
2048
+ }
2049
+ await confirm({
2050
+ assumeYes: ctx.assumeYes,
2051
+ question: `Install vscode-js-debug ${JS_DEBUG_VERSION} into ${installRoot} (~10MB)?`,
2052
+ details: [`Source: ${url}`],
2053
+ ...ctx.stdin === void 0 ? {} : { stdin: ctx.stdin },
2054
+ ...ctx.stderr === void 0 ? {} : { stderr: ctx.stderr }
2055
+ });
2056
+ await withAdapterLock(ctx.adaptersDir, ADAPTER_ID, async () => {
2057
+ if (await hasConsentMarker(ctx.adaptersDir, ADAPTER_ID, JS_DEBUG_VERSION) && await entrypointsExist(installRoot)) {
2058
+ return;
2059
+ }
2060
+ await atomicInstall({
2061
+ adaptersDir: ctx.adaptersDir,
2062
+ adapterId: ADAPTER_ID,
2063
+ expectedEntrypoints: ENTRYPOINTS,
2064
+ populate: async (stagingDir) => {
2065
+ const archivePath = path9.join(
2066
+ ctx.adaptersDir,
2067
+ `.${ADAPTER_ID}.archive.${process.pid}.${randomBytes2(4).toString("hex")}.tar.gz`
2068
+ );
2069
+ try {
2070
+ await downloadToFile({ url, destPath: archivePath, env: ctx.env });
2071
+ const actualSha = await fileSha256(archivePath);
2072
+ if (actualSha !== expectedSha) {
2073
+ throw usageError("Adapter download failed SHA-256 verification.", {
2074
+ code: "provision_checksum_mismatch",
2075
+ diagnostics: [
2076
+ `URL: ${url}`,
2077
+ `Expected: ${expectedSha}`,
2078
+ `Actual: ${actualSha}`,
2079
+ "Re-run setup or report at https://github.com/roblourens/dap-cli/issues if persistent."
2080
+ ],
2081
+ data: {
2082
+ adapterId: "js-debug",
2083
+ version: JS_DEBUG_VERSION,
2084
+ url,
2085
+ expectedSha,
2086
+ actualSha
2087
+ }
2088
+ });
2089
+ }
2090
+ await extractTarGz(archivePath, stagingDir, { strip: 1 });
2091
+ await fs6.writeFile(
2092
+ path9.join(stagingDir, "package.json"),
2093
+ PACKAGE_BOUNDARY,
2094
+ "utf8"
2095
+ );
2096
+ } finally {
2097
+ await fs6.rm(archivePath, { force: true }).catch(() => void 0);
2098
+ }
2099
+ }
2100
+ });
2101
+ await writeConsentMarker(ctx.adaptersDir, ADAPTER_ID, JS_DEBUG_VERSION);
2102
+ });
2103
+ return {
2104
+ adapterId: "js-debug",
2105
+ version: JS_DEBUG_VERSION,
2106
+ installRoot,
2107
+ entrypoint,
2108
+ fromCache: false
2109
+ };
2110
+ }
2111
+
2112
+ // src/adapters/provision/debugpy.ts
2113
+ import path10 from "path";
2114
+ import { promises as fs7 } from "fs";
2115
+ import { execFile } from "child_process";
2116
+ import { promisify } from "util";
2117
+ var execFileAsync = promisify(execFile);
2118
+ function venvPythonRel() {
2119
+ return process.platform === "win32" ? path10.join("venv", "Scripts", "python.exe") : path10.join("venv", "bin", "python");
2120
+ }
2121
+ function venvPipRel() {
2122
+ return process.platform === "win32" ? path10.join("venv", "Scripts", "pip.exe") : path10.join("venv", "bin", "pip");
2123
+ }
2124
+ function tail(text, max = 2048) {
2125
+ if (text.length <= max) {
2126
+ return text;
2127
+ }
2128
+ return `...${text.slice(text.length - max)}`;
2129
+ }
2130
+ async function exists(p) {
2131
+ try {
2132
+ await fs7.access(p);
2133
+ return true;
2134
+ } catch {
2135
+ return false;
2136
+ }
2137
+ }
2138
+ async function provisionDebugpy(ctx) {
2139
+ const { env, assumeYes, adaptersDir, stdin, stderr } = ctx;
2140
+ const installRoot = path10.join(adaptersDir, "debugpy");
2141
+ const entrypoint = path10.join(installRoot, venvPythonRel());
2142
+ if (await hasConsentMarker(adaptersDir, "debugpy", DEBUGPY_VERSION) && await exists(entrypoint)) {
2143
+ return {
2144
+ adapterId: "debugpy",
2145
+ version: DEBUGPY_VERSION,
2146
+ installRoot,
2147
+ entrypoint,
2148
+ fromCache: true
2149
+ };
2150
+ }
2151
+ await confirm({
2152
+ assumeYes,
2153
+ question: `Install debugpy ${DEBUGPY_VERSION} into a venv at ${installRoot}/ (~6MB)?`,
2154
+ details: [
2155
+ "Requires python3 (>=3.8) on PATH.",
2156
+ "Creates an isolated venv and pip-installs debugpy. The venv python becomes the adapter command."
2157
+ ],
2158
+ ...stdin === void 0 ? {} : { stdin },
2159
+ ...stderr === void 0 ? {} : { stderr }
2160
+ });
2161
+ await withAdapterLock(adaptersDir, "debugpy", async () => {
2162
+ if (await hasConsentMarker(adaptersDir, "debugpy", DEBUGPY_VERSION) && await exists(entrypoint)) {
2163
+ return;
2164
+ }
2165
+ const python3 = env.DAP_CLI_PROVISION_PYTHON3 ?? "python3";
2166
+ try {
2167
+ await execFileAsync(python3, ["--version"]);
2168
+ } catch (error) {
2169
+ const message = error.stderr ?? error.message ?? "unknown error";
2170
+ throw usageError("python3 is not available on PATH.", {
2171
+ code: "provision_python3_missing",
2172
+ diagnostics: [
2173
+ "Install Python 3.8+ and ensure `python3` is on PATH.",
2174
+ "macOS: `brew install python`",
2175
+ "Ubuntu/Debian: `apt install python3 python3-venv`",
2176
+ 'Windows: install from python.org and check "Add to PATH".',
2177
+ `Underlying error: ${tail(message, 512)}`
2178
+ ],
2179
+ data: { python3 },
2180
+ cause: error
2181
+ });
2182
+ }
2183
+ await atomicInstall({
2184
+ adaptersDir,
2185
+ adapterId: "debugpy",
2186
+ expectedEntrypoints: [venvPythonRel()],
2187
+ populate: async (stagingDir) => {
2188
+ const venvDir = path10.join(stagingDir, "venv");
2189
+ try {
2190
+ await execFileAsync(python3, ["-m", "venv", venvDir]);
2191
+ } catch (error) {
2192
+ const stderrText = error.stderr ?? "";
2193
+ throw usageError("Failed to create Python virtual environment.", {
2194
+ code: "provision_python3_venv_unavailable",
2195
+ diagnostics: [
2196
+ "The `python3 -m venv` command failed. On Debian/Ubuntu install `python3-venv`:",
2197
+ " sudo apt install python3-venv",
2198
+ "On other distros ensure the standard library `venv` and `ensurepip` modules are present.",
2199
+ `stderr tail: ${tail(stderrText)}`
2200
+ ],
2201
+ data: { python3 },
2202
+ cause: error
2203
+ });
2204
+ }
2205
+ const pipPath = path10.join(stagingDir, venvPipRel());
2206
+ try {
2207
+ await execFileAsync(pipPath, [
2208
+ "install",
2209
+ "--no-warn-script-location",
2210
+ "--disable-pip-version-check",
2211
+ `debugpy==${DEBUGPY_VERSION}`
2212
+ ]);
2213
+ } catch (error) {
2214
+ const stderrText = error.stderr ?? "";
2215
+ throw usageError(`Failed to install debugpy==${DEBUGPY_VERSION} via pip.`, {
2216
+ code: "provision_pip_install_failed",
2217
+ diagnostics: [
2218
+ "pip install failed. Common causes: no network access, restricted PyPI mirror, missing build tools.",
2219
+ "Workaround: set `PIP_INDEX_URL` to your mirror, or pre-install debugpy into the venv and re-run.",
2220
+ `Underlying pip command: ${pipPath} install debugpy==${DEBUGPY_VERSION}`,
2221
+ `stderr tail: ${tail(stderrText)}`
2222
+ ],
2223
+ data: { pipPath, version: DEBUGPY_VERSION },
2224
+ cause: error
2225
+ });
2226
+ }
2227
+ }
2228
+ });
2229
+ await writeConsentMarker(adaptersDir, "debugpy", DEBUGPY_VERSION);
2230
+ });
2231
+ return {
2232
+ adapterId: "debugpy",
2233
+ version: DEBUGPY_VERSION,
2234
+ installRoot,
2235
+ entrypoint,
2236
+ fromCache: false
2237
+ };
2238
+ }
2239
+
2240
+ // src/adapters/provision/delve.ts
2241
+ import path12 from "path";
2242
+ import { promises as fs9 } from "fs";
2243
+ import { createHash as createHash3 } from "crypto";
2244
+ import { randomBytes as randomBytes3 } from "crypto";
2245
+
2246
+ // src/adapters/provision/extractZip.ts
2247
+ import { promises as fs8, createWriteStream as createWriteStream4 } from "fs";
2248
+ import path11 from "path";
2249
+ import yauzl from "yauzl";
2250
+ var SYMLINK_MODE = 40960;
2251
+ var UNIX_MODE_MASK = 61440;
2252
+ function isUnsafeFileName(fileName) {
2253
+ if (path11.isAbsolute(fileName)) {
2254
+ return true;
2255
+ }
2256
+ if (/^[A-Za-z]:[\\/]/.test(fileName)) {
2257
+ return true;
2258
+ }
2259
+ return fileName.split(/[\\/]/).includes("..");
2260
+ }
2261
+ function isSymlinkEntry(entry) {
2262
+ const unixMode = entry.externalFileAttributes >>> 16 & UNIX_MODE_MASK;
2263
+ return unixMode === SYMLINK_MODE;
2264
+ }
2265
+ function unsafeEntryError(archivePath, fileName) {
2266
+ return usageError("Archive contains unsafe entry path.", {
2267
+ code: "provision_extract_failed",
2268
+ diagnostics: [`Entry: ${fileName}`, `Archive: ${archivePath}`],
2269
+ data: { archivePath, entry: fileName }
2270
+ });
2271
+ }
2272
+ function wrapExtractError(archivePath, error) {
2273
+ if (error instanceof CliError) {
2274
+ return error;
2275
+ }
2276
+ const message = error instanceof Error ? error.message : String(error);
2277
+ return usageError("Archive extraction failed.", {
2278
+ code: "provision_extract_failed",
2279
+ diagnostics: [archivePath, message],
2280
+ data: { archivePath },
2281
+ cause: error
2282
+ });
2283
+ }
2284
+ async function extractZip(archivePath, destDir) {
2285
+ await fs8.mkdir(destDir, { recursive: true });
2286
+ await new Promise((resolve, reject) => {
2287
+ yauzl.open(archivePath, { lazyEntries: true }, (openErr, zip) => {
2288
+ if (openErr || zip === void 0) {
2289
+ reject(openErr ?? new Error("zip open failed"));
2290
+ return;
2291
+ }
2292
+ const handleError = (err) => {
2293
+ try {
2294
+ zip.close();
2295
+ } catch {
2296
+ }
2297
+ reject(err instanceof Error ? err : new Error(String(err)));
2298
+ };
2299
+ zip.on("error", handleError);
2300
+ zip.on("end", () => {
2301
+ resolve();
2302
+ });
2303
+ zip.on("entry", (entry) => {
2304
+ if (isUnsafeFileName(entry.fileName)) {
2305
+ handleError(unsafeEntryError(archivePath, entry.fileName));
2306
+ return;
2307
+ }
2308
+ if (isSymlinkEntry(entry)) {
2309
+ handleError(unsafeEntryError(archivePath, entry.fileName));
2310
+ return;
2311
+ }
2312
+ const dest = path11.join(destDir, entry.fileName);
2313
+ if (entry.fileName.endsWith("/")) {
2314
+ fs8.mkdir(dest, { recursive: true }).then(() => {
2315
+ zip.readEntry();
2316
+ }).catch(handleError);
2317
+ return;
2318
+ }
2319
+ fs8.mkdir(path11.dirname(dest), { recursive: true }).then(() => {
2320
+ zip.openReadStream(entry, (readErr, readStream) => {
2321
+ if (readErr || readStream === void 0) {
2322
+ handleError(readErr ?? new Error("zip read failed"));
2323
+ return;
2324
+ }
2325
+ const out = createWriteStream4(dest);
2326
+ out.on("finish", () => {
2327
+ zip.readEntry();
2328
+ });
2329
+ out.on("error", handleError);
2330
+ readStream.on("error", handleError);
2331
+ readStream.pipe(out);
2332
+ });
2333
+ }).catch(handleError);
2334
+ });
2335
+ zip.readEntry();
2336
+ });
2337
+ }).catch((error) => {
2338
+ throw wrapExtractError(archivePath, error);
2339
+ });
2340
+ }
2341
+
2342
+ // src/adapters/provision/delve.ts
2343
+ var DEFAULT_RELEASE_BASE_URL = "https://github.com";
2344
+ function bareVersion() {
2345
+ return DELVE_VERSION.startsWith("v") ? DELVE_VERSION.slice(1) : DELVE_VERSION;
2346
+ }
2347
+ function resolveDelveAsset(env) {
2348
+ const override = env.DAP_CLI_PROVISION_DELVE_PLATFORM_OVERRIDE;
2349
+ const key = override !== void 0 && override.length > 0 ? override : `${process.platform}_${process.arch}`;
2350
+ const matrix = {
2351
+ "darwin_arm64": "darwin_arm64",
2352
+ "darwin_x64": "darwin_amd64",
2353
+ "linux_x64": "linux_amd64",
2354
+ "linux_arm64": "linux_arm64",
2355
+ "win32_x64": "windows_amd64"
2356
+ };
2357
+ const platformKey = matrix[key];
2358
+ if (platformKey === void 0) {
2359
+ throw usageError(`Delve provisioning does not support platform '${key}'.`, {
2360
+ code: "provision_arch_unsupported",
2361
+ diagnostics: [
2362
+ `Detected platform: ${key}`,
2363
+ "Supported platforms: darwin_arm64, darwin_x64, linux_x64, linux_arm64, win32_x64.",
2364
+ "Install `dlv` manually on PATH or provision a compatible binary."
2365
+ ],
2366
+ data: {
2367
+ adapterId: "delve",
2368
+ detected: key,
2369
+ supported: ["darwin_arm64", "darwin_x64", "linux_x64", "linux_arm64", "win32_x64"]
2370
+ }
2371
+ });
2372
+ }
2373
+ const archiveKind = platformKey === "windows_amd64" ? "zip" : "tar.gz";
2374
+ const ext = archiveKind === "zip" ? "zip" : "tar.gz";
2375
+ const archiveName = `dlv_${bareVersion()}_${platformKey}.${ext}`;
2376
+ const executableName = platformKey === "windows_amd64" ? "dlv.exe" : "dlv";
2377
+ return { platformKey, archiveName, archiveKind, executableName };
2378
+ }
2379
+ async function exists2(p) {
2380
+ try {
2381
+ await fs9.access(p);
2382
+ return true;
2383
+ } catch {
2384
+ return false;
2385
+ }
2386
+ }
2387
+ async function computeSha256(filePath) {
2388
+ const buffer = await fs9.readFile(filePath);
2389
+ return createHash3("sha256").update(buffer).digest("hex");
2390
+ }
2391
+ async function provisionDelve(ctx) {
2392
+ const { env, assumeYes, adaptersDir, stdin, stderr } = ctx;
2393
+ const asset = resolveDelveAsset(env);
2394
+ const installRoot = path12.join(adaptersDir, "delve");
2395
+ const entrypoint = path12.join(installRoot, asset.executableName);
2396
+ const expectedSha = DELVE_CHECKSUMS[DELVE_VERSION]?.[asset.platformKey];
2397
+ if (expectedSha === void 0) {
2398
+ throw usageError(`No pinned SHA-256 for delve ${DELVE_VERSION} on ${asset.platformKey}.`, {
2399
+ code: "provision_checksum_mismatch",
2400
+ diagnostics: [
2401
+ `Adapter: delve ${DELVE_VERSION} (${asset.platformKey})`,
2402
+ "src/adapters/provision/checksums.ts must list a checksum for every supported platform.",
2403
+ "Re-run setup or report at https://github.com/roblourens/dap-cli/issues if persistent."
2404
+ ],
2405
+ data: {
2406
+ adapterId: "delve",
2407
+ version: DELVE_VERSION,
2408
+ platform: asset.platformKey
2409
+ }
2410
+ });
2411
+ }
2412
+ if (await hasConsentMarker(adaptersDir, "delve", DELVE_VERSION) && await exists2(entrypoint)) {
2413
+ return {
2414
+ adapterId: "delve",
2415
+ version: DELVE_VERSION,
2416
+ installRoot,
2417
+ entrypoint,
2418
+ fromCache: true
2419
+ };
2420
+ }
2421
+ await confirm({
2422
+ assumeYes,
2423
+ question: `Install delve ${DELVE_VERSION} into ${installRoot}/ (~10MB)?`,
2424
+ details: [
2425
+ `Downloads the official release asset ${asset.archiveName} from github.com/go-delve/delve.`,
2426
+ "The archive SHA-256 is verified against an embedded checksum before installation."
2427
+ ],
2428
+ ...stdin === void 0 ? {} : { stdin },
2429
+ ...stderr === void 0 ? {} : { stderr }
2430
+ });
2431
+ await withAdapterLock(adaptersDir, "delve", async () => {
2432
+ if (await hasConsentMarker(adaptersDir, "delve", DELVE_VERSION) && await exists2(entrypoint)) {
2433
+ return;
2434
+ }
2435
+ const releaseBase = env.DAP_CLI_PROVISION_RELEASE_BASE_URL ?? DEFAULT_RELEASE_BASE_URL;
2436
+ const url = `${releaseBase}/go-delve/delve/releases/download/${DELVE_VERSION}/${asset.archiveName}`;
2437
+ const archivePath = path12.join(
2438
+ adaptersDir,
2439
+ `.delve.archive.${process.pid}.${randomBytes3(4).toString("hex")}.${asset.archiveKind === "zip" ? "zip" : "tar.gz"}`
2440
+ );
2441
+ try {
2442
+ await fs9.mkdir(adaptersDir, { recursive: true });
2443
+ await downloadToFile({ url, destPath: archivePath, env });
2444
+ const actualSha = await computeSha256(archivePath);
2445
+ if (actualSha !== expectedSha) {
2446
+ throw usageError("Delve archive failed SHA-256 verification.", {
2447
+ code: "provision_checksum_mismatch",
2448
+ diagnostics: [
2449
+ `URL: ${url}`,
2450
+ `Expected: ${expectedSha}`,
2451
+ `Actual: ${actualSha}`,
2452
+ "Re-run setup or report at https://github.com/roblourens/dap-cli/issues if persistent."
2453
+ ],
2454
+ data: {
2455
+ adapterId: "delve",
2456
+ version: DELVE_VERSION,
2457
+ url,
2458
+ expectedSha,
2459
+ actualSha
2460
+ }
2461
+ });
2462
+ }
2463
+ await atomicInstall({
2464
+ adaptersDir,
2465
+ adapterId: "delve",
2466
+ expectedEntrypoints: [asset.executableName],
2467
+ populate: async (stagingDir) => {
2468
+ if (asset.archiveKind === "zip") {
2469
+ await extractZip(archivePath, stagingDir);
2470
+ } else {
2471
+ await extractTarGz(archivePath, stagingDir);
2472
+ }
2473
+ if (process.platform !== "win32") {
2474
+ await fs9.chmod(path12.join(stagingDir, asset.executableName), 493);
2475
+ }
2476
+ }
2477
+ });
2478
+ await writeConsentMarker(adaptersDir, "delve", DELVE_VERSION);
2479
+ } finally {
2480
+ await fs9.rm(archivePath, { force: true }).catch(() => void 0);
2481
+ }
2482
+ });
2483
+ return {
2484
+ adapterId: "delve",
2485
+ version: DELVE_VERSION,
2486
+ installRoot,
2487
+ entrypoint,
2488
+ fromCache: false
2489
+ };
2490
+ }
2491
+
2492
+ // src/adapters/provision/codelldb.ts
2493
+ import path13 from "path";
2494
+ import { promises as fs10 } from "fs";
2495
+ import { createHash as createHash4, randomBytes as randomBytes4 } from "crypto";
2496
+ var ADAPTER_ID2 = "codelldb";
2497
+ var DEFAULT_RELEASE_BASE_URL2 = "https://github.com";
2498
+ var ENTRYPOINT = "extension/adapter/codelldb";
2499
+ var REQUIRED_RUNTIME_PATHS = [
2500
+ ENTRYPOINT,
2501
+ "extension/adapter/scripts/codelldb/__init__.py",
2502
+ "extension/lldb/bin/lldb",
2503
+ "extension/lldb/bin/lldb-argdumper",
2504
+ "extension/lldb/bin/lldb-server",
2505
+ "extension/lldb/lib/liblldb.dylib",
2506
+ "extension/lldb/lib/libpython312.dylib",
2507
+ "extension/lldb/lib/python3.12/os.py",
2508
+ "extension/lang_support/rust.py",
2509
+ "extension/package.json"
2510
+ ];
2511
+ var EXECUTABLE_PATHS = [
2512
+ ENTRYPOINT,
2513
+ "extension/lldb/bin/lldb",
2514
+ "extension/lldb/bin/lldb-argdumper",
2515
+ "extension/lldb/bin/lldb-server"
2516
+ ];
2517
+ function resolveCodeLldbAsset(env) {
2518
+ const override = env.DAP_CLI_PROVISION_CODELLDB_PLATFORM_OVERRIDE;
2519
+ const detected = override !== void 0 && override.length > 0 ? override : `${process.platform}_${process.arch}`;
2520
+ if (detected !== "darwin_arm64") {
2521
+ throw usageError(`CodeLLDB provisioning does not support platform '${detected}'.`, {
2522
+ code: "provision_arch_unsupported",
2523
+ diagnostics: [
2524
+ `Detected platform: ${detected}`,
2525
+ "Supported platforms: darwin_arm64.",
2526
+ "Only the official CodeLLDB darwin-arm64 artifact has passed verification."
2527
+ ],
2528
+ data: {
2529
+ adapterId: ADAPTER_ID2,
2530
+ detected,
2531
+ supported: ["darwin_arm64"]
2532
+ }
1391
2533
  });
1392
- throw error;
1393
2534
  }
2535
+ return {
2536
+ platformKey: "darwin_arm64",
2537
+ archiveName: "codelldb-darwin-arm64.vsix"
2538
+ };
1394
2539
  }
1395
- async function connectWithRetry(adapterId, host, port) {
1396
- const deadline = Date.now() + 5e3;
1397
- let lastError;
1398
- while (Date.now() < deadline) {
2540
+ async function isCodeLldbRuntimeReady(installRoot) {
2541
+ for (const relativePath of REQUIRED_RUNTIME_PATHS) {
1399
2542
  try {
1400
- return await connectSocketTransport({ name: adapterId, host, port, timeoutMs: 500 });
1401
- } catch (error) {
1402
- lastError = error;
1403
- await delay(50);
2543
+ await fs10.access(path13.join(installRoot, relativePath));
2544
+ } catch {
2545
+ return false;
1404
2546
  }
1405
2547
  }
1406
- throw lastError instanceof Error ? lastError : new Error("Timed out connecting DAP server adapter.");
2548
+ return true;
1407
2549
  }
1408
- function getFreePort(host) {
1409
- return new Promise((resolve, reject) => {
1410
- const server = net3.createServer();
1411
- server.once("error", reject);
1412
- server.listen(0, host, () => {
1413
- const address = server.address();
1414
- server.close(() => {
1415
- if (typeof address === "object" && address !== null) {
1416
- resolve(address.port);
1417
- return;
1418
- }
1419
- reject(new Error("Failed to allocate a local adapter server port."));
1420
- });
2550
+ async function computeSha2562(filePath) {
2551
+ const buffer = await fs10.readFile(filePath);
2552
+ return createHash4("sha256").update(buffer).digest("hex");
2553
+ }
2554
+ async function provisionCodeLldb(ctx) {
2555
+ const asset = resolveCodeLldbAsset(ctx.env);
2556
+ const installRoot = path13.join(ctx.adaptersDir, ADAPTER_ID2);
2557
+ const entrypoint = path13.join(installRoot, ENTRYPOINT);
2558
+ const expectedSha = CODELLDB_CHECKSUMS[CODELLDB_VERSION]?.[asset.platformKey];
2559
+ if (expectedSha === void 0) {
2560
+ throw usageError(`No pinned SHA-256 for CodeLLDB ${CODELLDB_VERSION} on ${asset.platformKey}.`, {
2561
+ code: "provision_checksum_mismatch",
2562
+ diagnostics: [
2563
+ `Adapter: ${ADAPTER_ID2} ${CODELLDB_VERSION} (${asset.platformKey})`,
2564
+ "src/adapters/provision/checksums.ts must list a verified checksum for every supported platform.",
2565
+ "Re-run setup or report at https://github.com/roblourens/dap-cli/issues if persistent."
2566
+ ],
2567
+ data: { adapterId: ADAPTER_ID2, version: CODELLDB_VERSION, platform: asset.platformKey }
1421
2568
  });
1422
- });
1423
- }
1424
- async function terminateChild2(child) {
1425
- if (child.exitCode !== null || child.signalCode !== null) {
1426
- return;
1427
- }
1428
- child.kill("SIGTERM");
1429
- if (await waitForExit2(child, 100)) {
1430
- return;
1431
2569
  }
1432
- child.kill("SIGKILL");
1433
- await waitForExit2(child, 100);
1434
- }
1435
- function waitForExit2(child, timeoutMs) {
1436
- if (child.exitCode !== null || child.signalCode !== null) {
1437
- return Promise.resolve(true);
2570
+ if (await hasConsentMarker(ctx.adaptersDir, ADAPTER_ID2, CODELLDB_VERSION) && await isCodeLldbRuntimeReady(installRoot)) {
2571
+ return { adapterId: ADAPTER_ID2, version: CODELLDB_VERSION, installRoot, entrypoint, fromCache: true };
1438
2572
  }
1439
- return new Promise((resolve) => {
1440
- const timer = setTimeout(() => {
1441
- child.off("exit", onExit);
1442
- resolve(false);
1443
- }, timeoutMs);
1444
- const onExit = () => {
1445
- clearTimeout(timer);
1446
- resolve(true);
1447
- };
1448
- child.once("exit", onExit);
2573
+ await confirm({
2574
+ assumeYes: ctx.assumeYes,
2575
+ question: `Install CodeLLDB ${CODELLDB_VERSION} into ${installRoot}/ (~44MB download)?`,
2576
+ details: [
2577
+ `Downloads the official release asset ${asset.archiveName} from github.com/vadimcn/codelldb.`,
2578
+ "The full VSIX runtime tree is cached locally after SHA-256 verification."
2579
+ ],
2580
+ ...ctx.stdin === void 0 ? {} : { stdin: ctx.stdin },
2581
+ ...ctx.stderr === void 0 ? {} : { stderr: ctx.stderr }
1449
2582
  });
1450
- }
1451
- function appendStderrTail2(stderrTail, text) {
1452
- for (const line of text.split(/\r?\n/).filter((value) => value.length > 0)) {
1453
- stderrTail.push(line);
1454
- while (stderrTail.length > 100) {
1455
- stderrTail.shift();
2583
+ await withAdapterLock(ctx.adaptersDir, ADAPTER_ID2, async () => {
2584
+ if (await hasConsentMarker(ctx.adaptersDir, ADAPTER_ID2, CODELLDB_VERSION) && await isCodeLldbRuntimeReady(installRoot)) {
2585
+ return;
1456
2586
  }
1457
- }
2587
+ const releaseBase = ctx.env.DAP_CLI_PROVISION_RELEASE_BASE_URL ?? DEFAULT_RELEASE_BASE_URL2;
2588
+ const url = `${releaseBase}/vadimcn/codelldb/releases/download/${CODELLDB_VERSION}/${asset.archiveName}`;
2589
+ const archivePath = path13.join(ctx.adaptersDir, `.${ADAPTER_ID2}.archive.${process.pid}.${randomBytes4(4).toString("hex")}.vsix`);
2590
+ try {
2591
+ await fs10.mkdir(ctx.adaptersDir, { recursive: true });
2592
+ await downloadToFile({ url, destPath: archivePath, env: ctx.env });
2593
+ const actualSha = await computeSha2562(archivePath);
2594
+ if (actualSha !== expectedSha) {
2595
+ throw usageError("CodeLLDB archive failed SHA-256 verification.", {
2596
+ code: "provision_checksum_mismatch",
2597
+ diagnostics: [
2598
+ `URL: ${url}`,
2599
+ `Expected: ${expectedSha}`,
2600
+ `Actual: ${actualSha}`,
2601
+ "Re-run setup or report at https://github.com/roblourens/dap-cli/issues if persistent."
2602
+ ],
2603
+ data: { adapterId: ADAPTER_ID2, version: CODELLDB_VERSION, url, expectedSha, actualSha }
2604
+ });
2605
+ }
2606
+ await atomicInstall({
2607
+ adaptersDir: ctx.adaptersDir,
2608
+ adapterId: ADAPTER_ID2,
2609
+ expectedEntrypoints: REQUIRED_RUNTIME_PATHS,
2610
+ populate: async (stagingDir) => {
2611
+ await extractZip(archivePath, stagingDir);
2612
+ for (const executablePath of EXECUTABLE_PATHS) {
2613
+ await fs10.chmod(path13.join(stagingDir, executablePath), 493);
2614
+ }
2615
+ }
2616
+ });
2617
+ await writeConsentMarker(ctx.adaptersDir, ADAPTER_ID2, CODELLDB_VERSION);
2618
+ } finally {
2619
+ await fs10.rm(archivePath, { force: true }).catch(() => void 0);
2620
+ }
2621
+ });
2622
+ return { adapterId: ADAPTER_ID2, version: CODELLDB_VERSION, installRoot, entrypoint, fromCache: false };
1458
2623
  }
1459
- async function delay(ms) {
1460
- await new Promise((resolve) => setTimeout(resolve, ms));
2624
+
2625
+ // src/adapters/provision/index.ts
2626
+ async function provisionAdapter(id, ctx) {
2627
+ switch (id) {
2628
+ case "js-debug":
2629
+ return provisionJsDebug(ctx);
2630
+ case "debugpy":
2631
+ return provisionDebugpy(ctx);
2632
+ case "delve":
2633
+ return provisionDelve(ctx);
2634
+ case "codelldb":
2635
+ return provisionCodeLldb(ctx);
2636
+ }
1461
2637
  }
1462
2638
 
1463
2639
  // src/adapters/builtins/jsDebug.ts
1464
- import { existsSync } from "fs";
1465
- import path5 from "path";
1466
- function createJsDebugDescriptor(jsDebugPath) {
1467
- const dapServerPath = jsDebugPath ?? resolveDefaultJsDebugPath();
2640
+ async function createJsDebugDescriptor(jsDebugPath) {
2641
+ const dapServerPath = jsDebugPath ?? await resolveDefaultJsDebugPath();
1468
2642
  return {
1469
2643
  id: "js-debug",
1470
2644
  label: "JavaScript Debug Adapter (Node, Chrome, Electron)",
@@ -1488,26 +2662,35 @@ function applyJsDebugTraceDefaults(config, logDir) {
1488
2662
  ...record,
1489
2663
  trace: {
1490
2664
  stdio: false,
1491
- logFile: path5.join(logDir, `js-debug-trace-${Date.now()}.log`)
2665
+ logFile: path14.join(logDir, `js-debug-trace-${Date.now()}.log`)
1492
2666
  }
1493
2667
  };
1494
2668
  }
1495
- function resolveDefaultJsDebugPath() {
1496
- const candidates = [
1497
- path5.join(getDapCliAdaptersDir(), "js-debug", "src", "dapDebugServer.js"),
1498
- path5.join(process.cwd(), "node_modules", "vscode-js-debug", "src", "dapDebugServer.js")
1499
- ];
1500
- const found = candidates.find((candidate) => existsSync(candidate));
1501
- if (found !== void 0) {
1502
- return found;
2669
+ async function pathExists(candidate) {
2670
+ try {
2671
+ await fs11.access(candidate);
2672
+ return true;
2673
+ } catch {
2674
+ return false;
1503
2675
  }
1504
- throw usageError("js-debug adapter is not installed.", {
1505
- code: "js_debug_not_found",
1506
- diagnostics: [
1507
- "Run npm run setup-adapters to provision js-debug, or see docs/ADAPTER-SETUP.md for advanced manual provisioning.",
1508
- `Checked: ${candidates.join(", ")}`
1509
- ]
2676
+ }
2677
+ async function resolveDefaultJsDebugPath() {
2678
+ const env = process.env;
2679
+ const adaptersDir = getDapCliAdaptersDir(env);
2680
+ const provisionedEntrypoint = path14.join(adaptersDir, "js-debug", "src", "dapDebugServer.js");
2681
+ const repoEntrypoint = path14.join(process.cwd(), "node_modules", "vscode-js-debug", "src", "dapDebugServer.js");
2682
+ if (await pathExists(provisionedEntrypoint)) {
2683
+ return provisionedEntrypoint;
2684
+ }
2685
+ if (await pathExists(repoEntrypoint)) {
2686
+ return repoEntrypoint;
2687
+ }
2688
+ const result = await provisionAdapter("js-debug", {
2689
+ env,
2690
+ assumeYes: resolveAssumeYes(void 0, env),
2691
+ adaptersDir
1510
2692
  });
2693
+ return result.entrypoint;
1511
2694
  }
1512
2695
 
1513
2696
  // src/protocol/dapClient.ts
@@ -1601,14 +2784,16 @@ function parseBody(body) {
1601
2784
 
1602
2785
  // src/protocol/dapClient.ts
1603
2786
  var DapResponseError = class extends Error {
1604
- constructor(command, requestSeq, message) {
2787
+ constructor(command, requestSeq, message, responseBody) {
1605
2788
  super(message);
1606
2789
  this.command = command;
1607
2790
  this.requestSeq = requestSeq;
2791
+ this.responseBody = responseBody;
1608
2792
  this.name = "DapResponseError";
1609
2793
  }
1610
2794
  command;
1611
2795
  requestSeq;
2796
+ responseBody;
1612
2797
  };
1613
2798
  var DapTransportClosedError = class extends Error {
1614
2799
  constructor(message = "DAP transport closed.") {
@@ -1629,6 +2814,9 @@ var DapClient = class {
1629
2814
  transport.readable.on("close", this.handleClosed);
1630
2815
  transport.readable.on("end", this.handleClosed);
1631
2816
  transport.readable.on("error", this.handleTransportError);
2817
+ if (!Object.is(transport.writable, transport.readable)) {
2818
+ transport.writable.on("error", this.handleTransportError);
2819
+ }
1632
2820
  }
1633
2821
  transport;
1634
2822
  options;
@@ -1691,9 +2879,12 @@ var DapClient = class {
1691
2879
  }
1692
2880
  async close() {
1693
2881
  this.handleClosed();
1694
- this.detachTransportHandlers();
1695
- await this.terminateChildProcesses();
1696
- await this.transport.close();
2882
+ try {
2883
+ await this.terminateChildProcesses();
2884
+ await this.transport.close();
2885
+ } finally {
2886
+ this.detachTransportHandlers();
2887
+ }
1697
2888
  }
1698
2889
  handleData = (chunk) => {
1699
2890
  try {
@@ -1713,7 +2904,8 @@ var DapClient = class {
1713
2904
  this.rejectPending(new DapTransportClosedError());
1714
2905
  };
1715
2906
  handleTransportError = (error) => {
1716
- this.rejectPending(error);
2907
+ this.closed = true;
2908
+ this.rejectPending(error instanceof DapTransportClosedError ? error : new DapTransportClosedError(error.message));
1717
2909
  };
1718
2910
  handleMessage(message) {
1719
2911
  if (message.type === "event") {
@@ -1736,7 +2928,7 @@ var DapClient = class {
1736
2928
  clearTimeout(pending.timeout);
1737
2929
  }
1738
2930
  if (!response.success) {
1739
- pending.reject(new DapResponseError(response.command, response.request_seq, response.message ?? `DAP request failed: ${response.command}`));
2931
+ pending.reject(new DapResponseError(response.command, response.request_seq, response.message ?? `DAP request failed: ${response.command}`, response.body));
1740
2932
  return;
1741
2933
  }
1742
2934
  pending.resolve(response.body);
@@ -1818,6 +3010,9 @@ var DapClient = class {
1818
3010
  this.transport.readable.removeListener("close", this.handleClosed);
1819
3011
  this.transport.readable.removeListener("end", this.handleClosed);
1820
3012
  this.transport.readable.removeListener("error", this.handleTransportError);
3013
+ if (!Object.is(this.transport.writable, this.transport.readable)) {
3014
+ this.transport.writable.removeListener("error", this.handleTransportError);
3015
+ }
1821
3016
  }
1822
3017
  handleRunInTerminal(argumentsValue) {
1823
3018
  const parsed = runInTerminalArgumentsSchema.safeParse(argumentsValue);
@@ -2149,14 +3344,14 @@ function getDapGeneratedCommand(command) {
2149
3344
  }
2150
3345
 
2151
3346
  // src/sessions/session.ts
2152
- import { randomBytes } from "crypto";
3347
+ import { randomBytes as randomBytes5 } from "crypto";
2153
3348
  var REMOVABLE_LIFECYCLES = /* @__PURE__ */ new Set([
2154
3349
  "terminated",
2155
3350
  "disconnected",
2156
3351
  "failed"
2157
3352
  ]);
2158
3353
  function createSessionId() {
2159
- return `sess_${randomBytes(12).toString("base64url")}`;
3354
+ return `sess_${randomBytes5(12).toString("base64url")}`;
2160
3355
  }
2161
3356
  function projectSessionSummary(session) {
2162
3357
  const summary = {
@@ -2269,9 +3464,9 @@ function createAmbiguousSessionDiagnostics(targetId, matches) {
2269
3464
  }
2270
3465
 
2271
3466
  // src/sessions/sessionStore.ts
2272
- import { promises as fs2 } from "fs";
3467
+ import { promises as fs12 } from "fs";
2273
3468
  import { randomUUID } from "crypto";
2274
- import path6 from "path";
3469
+ import path15 from "path";
2275
3470
  import { z as z5 } from "zod";
2276
3471
  var ownedAdapterSchema = z5.object({
2277
3472
  pid: z5.number().int().positive().optional(),
@@ -2313,14 +3508,14 @@ var SessionStore = class {
2313
3508
  storePath;
2314
3509
  constructor(options = {}) {
2315
3510
  const env = options.dapCliHome === void 0 ? process.env : { ...process.env, DAP_CLI_HOME: options.dapCliHome };
2316
- this.storePath = path6.join(getDapCliStateDir(env), "sessions.json");
3511
+ this.storePath = path15.join(getDapCliStateDir(env), "sessions.json");
2317
3512
  }
2318
3513
  get path() {
2319
3514
  return this.storePath;
2320
3515
  }
2321
3516
  async read() {
2322
3517
  try {
2323
- const raw = await fs2.readFile(this.storePath, "utf8");
3518
+ const raw = await fs12.readFile(this.storePath, "utf8");
2324
3519
  const parsed = sessionStoreSchema.parse(JSON.parse(raw));
2325
3520
  return {
2326
3521
  ...parsed.activeSessionId !== void 0 ? { activeSessionId: parsed.activeSessionId } : {},
@@ -2333,7 +3528,7 @@ var SessionStore = class {
2333
3528
  if (error instanceof SyntaxError || error instanceof z5.ZodError) {
2334
3529
  const backupPath = `${this.storePath}.corrupt.${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.bak`;
2335
3530
  try {
2336
- await fs2.rename(this.storePath, backupPath);
3531
+ await fs12.rename(this.storePath, backupPath);
2337
3532
  process.stderr.write(`dap-cli: sessions.json was unparseable; moved to ${backupPath} and continuing with empty state.
2338
3533
  `);
2339
3534
  } catch {
@@ -2344,12 +3539,12 @@ var SessionStore = class {
2344
3539
  }
2345
3540
  }
2346
3541
  async write(data) {
2347
- await fs2.mkdir(path6.dirname(this.storePath), { recursive: true });
3542
+ await fs12.mkdir(path15.dirname(this.storePath), { recursive: true });
2348
3543
  const validated = sessionStoreSchema.parse(data);
2349
3544
  const tempPath = `${this.storePath}.${process.pid}.${randomUUID()}.tmp`;
2350
- await fs2.writeFile(tempPath, `${JSON.stringify(validated, null, 2)}
3545
+ await fs12.writeFile(tempPath, `${JSON.stringify(validated, null, 2)}
2351
3546
  `, "utf8");
2352
- await fs2.rename(tempPath, this.storePath);
3547
+ await fs12.rename(tempPath, this.storePath);
2353
3548
  }
2354
3549
  };
2355
3550
  function isNodeError2(error) {
@@ -3082,22 +4277,72 @@ var ChildSessionCoordinator = class {
3082
4277
  try {
3083
4278
  await this.options.sessionManager.updateLifecycle(childId, command === "launch" ? "launching" : "attaching").catch(() => void 0);
3084
4279
  const requestPromise = client.request(command, config);
3085
- requestPromise.catch(() => void 0);
4280
+ let launchAttachOutcome = "pending";
4281
+ requestPromise.then(
4282
+ () => {
4283
+ launchAttachOutcome = "fulfilled";
4284
+ },
4285
+ (error) => {
4286
+ launchAttachOutcome = { error };
4287
+ }
4288
+ );
3086
4289
  await runtime.initializedPromise;
3087
4290
  for (const breakpointArgs of this.pendingSetBreakpoints) {
3088
4291
  await client.request("setBreakpoints", breakpointArgs);
3089
4292
  }
3090
4293
  await client.request("configurationDone");
3091
- await requestPromise;
4294
+ await Promise.resolve();
4295
+ const getLaunchAttachOutcome = () => launchAttachOutcome;
4296
+ const outcomeAfterConfiguration = getLaunchAttachOutcome();
4297
+ if (typeof outcomeAfterConfiguration === "object") {
4298
+ throw outcomeAfterConfiguration.error;
4299
+ }
3092
4300
  await this.options.sessionManager.updateLifecycle(childId, "running").catch(() => void 0);
3093
4301
  if (!runtime.readySeen) {
3094
4302
  runtime.readySeen = true;
3095
4303
  runtime.resolveReady();
3096
4304
  }
4305
+ if (outcomeAfterConfiguration === "pending") {
4306
+ this.observeTrailingLaunchAttachResponse(childId, command, requestPromise);
4307
+ }
3097
4308
  } catch (error) {
3098
4309
  await this.markChildFailed(childId, error);
3099
4310
  }
3100
4311
  }
4312
+ /**
4313
+ * Observe a launch/attach response that arrives — or times out — AFTER the
4314
+ * child has already been marked `running` off the back of `configurationDone`.
4315
+ *
4316
+ * A trailing rejection no longer fails the child: the session is configured
4317
+ * and usable, and gating readiness on this response wedged js-debug page
4318
+ * sessions (see {@link runChildLifecycle}). Instead it is surfaced as a
4319
+ * non-fatal `output` warning on the parent so the condition stays debuggable
4320
+ * from the agent side. Transport-closed rejections during normal teardown are
4321
+ * ignored — they are not an attach anomaly.
4322
+ */
4323
+ observeTrailingLaunchAttachResponse(childId, command, requestPromise) {
4324
+ void requestPromise.then(
4325
+ () => void 0,
4326
+ (error) => {
4327
+ if (error instanceof DapTransportClosedError) {
4328
+ return;
4329
+ }
4330
+ const message = error instanceof Error ? error.message : String(error);
4331
+ const synthetic = {
4332
+ seq: 0,
4333
+ type: "event",
4334
+ event: "output",
4335
+ body: {
4336
+ category: "stderr",
4337
+ output: `child session ${childId} ${command} response not received: ${message}
4338
+ `,
4339
+ child_session_id: childId
4340
+ }
4341
+ };
4342
+ this.options.parentEventCache.append(this.options.parentSessionId, synthetic);
4343
+ }
4344
+ );
4345
+ }
3101
4346
  async markChildFailed(childId, error) {
3102
4347
  await this.options.sessionManager.updateLifecycle(childId, "failed").catch(() => void 0);
3103
4348
  const runtime = this.children.get(childId);
@@ -3755,12 +5000,12 @@ function sourceMatches(eventPath, requestPath) {
3755
5000
  if (eventPath === requestPath) {
3756
5001
  return true;
3757
5002
  }
3758
- const normalize = (path14) => path14.replace(/^file:\/\//, "").replace(/\\/g, "/");
5003
+ const normalize = (path26) => path26.replace(/^file:\/\//, "").replace(/\\/g, "/");
3759
5004
  return normalize(eventPath) === normalize(requestPath);
3760
5005
  }
3761
5006
 
3762
5007
  // src/sessions/helperProcessDetection.ts
3763
- import { execFile } from "child_process";
5008
+ import { execFile as execFile2 } from "child_process";
3764
5009
  var helperProcessWarningEventName = "dapCli.helperProcessWarning";
3765
5010
  var helperHint = "Likely attached to an adapter-spawned helper process; you probably meant `dap-cli attach`, not `dap-cli launch`.";
3766
5011
  function createHelperProcessDetector(options) {
@@ -3839,7 +5084,7 @@ function defaultLookupPpid(pid) {
3839
5084
  return Promise.resolve(void 0);
3840
5085
  }
3841
5086
  return new Promise((resolve) => {
3842
- execFile("ps", ["-o", "ppid=", "-p", String(pid)], { timeout: psLookupTimeoutMs }, (error, stdout) => {
5087
+ execFile2("ps", ["-o", "ppid=", "-p", String(pid)], { timeout: psLookupTimeoutMs }, (error, stdout) => {
3843
5088
  if (error !== null) {
3844
5089
  resolve(void 0);
3845
5090
  return;
@@ -3860,8 +5105,8 @@ function defaultLookupPpid(pid) {
3860
5105
  }
3861
5106
 
3862
5107
  // src/controller/buildId.ts
3863
- import { promises as fs3 } from "fs";
3864
- import path7 from "path";
5108
+ import { promises as fs13 } from "fs";
5109
+ import path16 from "path";
3865
5110
  import { fileURLToPath } from "url";
3866
5111
  var cachedBuildId;
3867
5112
  async function computeBuildId() {
@@ -3873,14 +5118,14 @@ async function computeBuildId() {
3873
5118
  cachedBuildId = envOverride;
3874
5119
  return cachedBuildId;
3875
5120
  }
3876
- const here = path7.dirname(fileURLToPath(import.meta.url));
5121
+ const here = path16.dirname(fileURLToPath(import.meta.url));
3877
5122
  const pkgPath = await findPackageJson(here);
3878
5123
  let version = "0.0.0";
3879
5124
  let pkgDir = here;
3880
5125
  if (pkgPath !== void 0) {
3881
- pkgDir = path7.dirname(pkgPath);
5126
+ pkgDir = path16.dirname(pkgPath);
3882
5127
  try {
3883
- const raw = await fs3.readFile(pkgPath, "utf8");
5128
+ const raw = await fs13.readFile(pkgPath, "utf8");
3884
5129
  const parsed = JSON.parse(raw);
3885
5130
  if (typeof parsed.version === "string" && parsed.version.length > 0) {
3886
5131
  version = parsed.version;
@@ -3888,9 +5133,9 @@ async function computeBuildId() {
3888
5133
  } catch {
3889
5134
  }
3890
5135
  }
3891
- const distPath = path7.join(pkgDir, "dist", "index.js");
5136
+ const distPath = path16.join(pkgDir, "dist", "index.js");
3892
5137
  try {
3893
- const stat = await fs3.stat(distPath);
5138
+ const stat = await fs13.stat(distPath);
3894
5139
  cachedBuildId = `${version}:dist:${stat.mtimeMs}:${stat.size}`;
3895
5140
  } catch {
3896
5141
  cachedBuildId = `${version}:src`;
@@ -3900,12 +5145,12 @@ async function computeBuildId() {
3900
5145
  async function findPackageJson(startDir) {
3901
5146
  let dir = startDir;
3902
5147
  for (let depth = 0; depth < 8; depth += 1) {
3903
- const candidate = path7.join(dir, "package.json");
5148
+ const candidate = path16.join(dir, "package.json");
3904
5149
  try {
3905
- await fs3.access(candidate);
5150
+ await fs13.access(candidate);
3906
5151
  return candidate;
3907
5152
  } catch {
3908
- const parent = path7.dirname(dir);
5153
+ const parent = path16.dirname(dir);
3909
5154
  if (parent === dir) {
3910
5155
  return void 0;
3911
5156
  }
@@ -4271,12 +5516,12 @@ var ControllerServer = class {
4271
5516
  }
4272
5517
  if (request.method === "sessions.stop") {
4273
5518
  const target = getOptionalStringParam(request.params, "name");
4274
- await this.disconnectRuntimeForTarget(target);
5519
+ await this.disconnectRuntimeForTarget(target, { terminateDebuggee: true });
4275
5520
  return manager.stopSession(target);
4276
5521
  }
4277
5522
  if (request.method === "sessions.detach") {
4278
5523
  const target = getOptionalStringParam(request.params, "name");
4279
- await this.disconnectRuntimeForTarget(target);
5524
+ await this.disconnectRuntimeForTarget(target, { terminateDebuggee: false });
4280
5525
  return manager.detachSession(target);
4281
5526
  }
4282
5527
  if (request.method === "sessions.close") {
@@ -4658,7 +5903,7 @@ var ControllerServer = class {
4658
5903
  return { sources: [] };
4659
5904
  }
4660
5905
  if (source !== void 0) {
4661
- const entry = map.get(path8.resolve(source));
5906
+ const entry = map.get(path17.resolve(source));
4662
5907
  return entry === void 0 ? { sources: [] } : { sources: [{ source: entry.source, breakpoints: entry.response, requested: entry.requested }] };
4663
5908
  }
4664
5909
  return {
@@ -4683,7 +5928,7 @@ var ControllerServer = class {
4683
5928
  };
4684
5929
  const cleared = [];
4685
5930
  if (source !== void 0) {
4686
- const key = path8.resolve(source);
5931
+ const key = path17.resolve(source);
4687
5932
  const entry = map.get(key);
4688
5933
  if (entry === void 0) {
4689
5934
  return { cleared: [] };
@@ -4835,7 +6080,7 @@ var ControllerServer = class {
4835
6080
  }
4836
6081
  });
4837
6082
  }
4838
- async disconnectRuntimeForTarget(target) {
6083
+ async disconnectRuntimeForTarget(target, opts) {
4839
6084
  let status;
4840
6085
  try {
4841
6086
  status = this.requireSessionManager().status(target);
@@ -4849,7 +6094,9 @@ var ControllerServer = class {
4849
6094
  if (runtime.children !== void 0) {
4850
6095
  await runtime.children.dispose().catch(() => void 0);
4851
6096
  }
4852
- await runtime.lifecycle.disconnect().catch(() => void 0);
6097
+ const disconnect = runtime.lifecycle.disconnect({ terminateDebuggee: opts.terminateDebuggee });
6098
+ disconnect.catch(() => void 0);
6099
+ await this.waitForDisconnect(disconnect, controllerDisconnectTimeoutMs);
4853
6100
  await runtime.client.close().catch(() => void 0);
4854
6101
  await runtime.adapter.close().catch(() => void 0);
4855
6102
  this.runtimes.delete(status.id);
@@ -5046,7 +6293,7 @@ function parseDapStartCompoundMemberParams(value) {
5046
6293
  return parsed;
5047
6294
  }
5048
6295
  function createCompoundId() {
5049
- return `compound_${randomBytes2(12).toString("base64url")}`;
6296
+ return `compound_${randomBytes6(12).toString("base64url")}`;
5050
6297
  }
5051
6298
  function findFailedCompoundMemberName(error, members, startedMemberNames) {
5052
6299
  const failed = members.find((member) => !startedMemberNames.includes(member.memberName));
@@ -5172,7 +6419,7 @@ function toDapCliError(error, context) {
5172
6419
  }
5173
6420
  return dapError(error.message, {
5174
6421
  code: "dap_request_failed",
5175
- diagnostics: [`DAP request failed: ${error.command}. Inspect adapter diagnostics and session state.`],
6422
+ diagnostics: createDapResponseDiagnostics(error),
5176
6423
  sessionId: context.sessionId,
5177
6424
  request: { command: error.command, seq: error.requestSeq },
5178
6425
  adapter: context.adapter
@@ -5204,6 +6451,33 @@ function toDapCliError(error, context) {
5204
6451
  diagnostics: ["The adapter failed while processing the DAP request. Check adapter stderr and log path."]
5205
6452
  }, context));
5206
6453
  }
6454
+ function createDapResponseDiagnostics(error) {
6455
+ const diagnostics = [`DAP request failed: ${error.command}. Inspect adapter diagnostics and session state.`];
6456
+ const responseDetail = extractDapResponseDetail(error.responseBody);
6457
+ if (responseDetail !== void 0 && responseDetail !== error.message) {
6458
+ diagnostics.push(`Adapter detail: ${responseDetail}`);
6459
+ }
6460
+ return diagnostics;
6461
+ }
6462
+ function extractDapResponseDetail(body) {
6463
+ if (!isRecord5(body)) {
6464
+ return void 0;
6465
+ }
6466
+ if (typeof body.message === "string" && body.message.trim().length > 0) {
6467
+ return body.message.trim();
6468
+ }
6469
+ const error = body.error;
6470
+ if (!isRecord5(error)) {
6471
+ return void 0;
6472
+ }
6473
+ if (typeof error.format === "string" && error.format.trim().length > 0) {
6474
+ return error.format.trim();
6475
+ }
6476
+ if (typeof error.message === "string" && error.message.trim().length > 0) {
6477
+ return error.message.trim();
6478
+ }
6479
+ return void 0;
6480
+ }
5207
6481
  function toControllerErrorPayload(error) {
5208
6482
  const payload = {
5209
6483
  code: error.code,
@@ -5480,7 +6754,7 @@ async function delay2(ms) {
5480
6754
  }
5481
6755
 
5482
6756
  // src/cli/commands/dapAliases.ts
5483
- import path9 from "path";
6757
+ import path18 from "path";
5484
6758
 
5485
6759
  // src/cli/commands/jsonOptions.ts
5486
6760
  function parseJsonOption(value) {
@@ -5608,7 +6882,7 @@ function registerDapAliasCommands(program, output) {
5608
6882
  breakpoints.command("set").requiredOption("--source <path>", "source path").requiredOption("--line <number...>", "breakpoint line").option("--name <name>", "session name or id").option("--condition <expr>", "breakpoint condition").option("--hit-condition <expr>", "breakpoint hit condition").option("--log-message <text>", "breakpoint log message").description("Set breakpoints at source file line numbers").action(async (options) => {
5609
6883
  const lines = parseIntegerValues(options.line, "line");
5610
6884
  const args = {
5611
- source: { path: path9.resolve(options.source) },
6885
+ source: { path: path18.resolve(options.source) },
5612
6886
  breakpoints: lines.map((line) => compactObject({
5613
6887
  line,
5614
6888
  condition: options.condition,
@@ -5641,7 +6915,7 @@ function registerDapAliasCommands(program, output) {
5641
6915
  breakpoints.command("list").option("--source <path>", "filter to a single source path").option("--name <name>", "session name or id").description("List breakpoints currently tracked for a session (per source, with verified state)").action(async (options) => {
5642
6916
  const params = compactObject({
5643
6917
  name: options.name,
5644
- source: options.source === void 0 ? void 0 : path9.resolve(options.source)
6918
+ source: options.source === void 0 ? void 0 : path18.resolve(options.source)
5645
6919
  });
5646
6920
  const client = await createControllerClient({ dapCliHome: process.env.DAP_CLI_HOME });
5647
6921
  try {
@@ -5654,7 +6928,7 @@ function registerDapAliasCommands(program, output) {
5654
6928
  breakpoints.command("clear").option("--source <path>", "clear only the named source (otherwise: clear all tracked sources)").option("--name <name>", "session name or id").description("Clear breakpoints in a source (DAP setBreakpoints empty-list semantics)").action(async (options) => {
5655
6929
  const params = compactObject({
5656
6930
  name: options.name,
5657
- source: options.source === void 0 ? void 0 : path9.resolve(options.source)
6931
+ source: options.source === void 0 ? void 0 : path18.resolve(options.source)
5658
6932
  });
5659
6933
  const client = await createControllerClient({ dapCliHome: process.env.DAP_CLI_HOME });
5660
6934
  try {
@@ -5682,10 +6956,10 @@ function registerDapAliasCommands(program, output) {
5682
6956
  await sendAliasRequest(output, "variables", { variablesReference: parseRequiredIntegerOption(options.variablesReference, "variables-reference") }, options.name, "variables");
5683
6957
  });
5684
6958
  program.command("source").helpGroup("Paused-state inspection").requiredOption("--source-reference <number>", "source reference").option("--path <path>", "source path").option("--name <name>", "session name or id").description("Return source content").action(async (options) => {
5685
- const path14 = options.path;
6959
+ const path26 = options.path;
5686
6960
  await sendAliasRequest(output, "source", compactObject({
5687
6961
  sourceReference: parseRequiredIntegerOption(options.sourceReference, "source-reference"),
5688
- source: path14 === void 0 ? void 0 : { path: path14 }
6962
+ source: path26 === void 0 ? void 0 : { path: path26 }
5689
6963
  }), options.name, "source");
5690
6964
  });
5691
6965
  program.command("evaluate").helpGroup("Paused-state inspection").requiredOption("--expression <expr>", "expression").option("--frame-id <number>", "frame id (auto-resolved to topmost paused frame when omitted)").option("--context <context>", "evaluation context").option("--name <name>", "session name or id").description("Evaluate an expression (auto-uses topmost frame of most-recently-stopped thread when paused)").action(async (options) => {
@@ -5942,9 +7216,9 @@ async function buildVerificationDiagnostic(client, requestedPath, breakpointsRes
5942
7216
  }
5943
7217
  const sources = loaded.sources ?? [];
5944
7218
  const loadedSourcesCount = sources.length;
5945
- const wantBasename = path9.basename(requestedPath);
7219
+ const wantBasename = path18.basename(requestedPath);
5946
7220
  const cmp = process.platform === "win32" ? (a, b) => a.toLowerCase() === b.toLowerCase() : (a, b) => a === b;
5947
- const matchingLoadedSources = sources.filter((s) => typeof s.path === "string" && (cmp(s.path, requestedPath) || cmp(path9.basename(s.path), wantBasename))).map((s) => {
7221
+ const matchingLoadedSources = sources.filter((s) => typeof s.path === "string" && (cmp(s.path, requestedPath) || cmp(path18.basename(s.path), wantBasename))).map((s) => {
5948
7222
  const sourcePath = s.path;
5949
7223
  return typeof s.name === "string" ? { path: sourcePath, name: s.name } : { path: sourcePath };
5950
7224
  });
@@ -5986,12 +7260,12 @@ async function countChildSessions(client, parentName) {
5986
7260
  }
5987
7261
 
5988
7262
  // src/cli/commands/dapCore.ts
5989
- import path13 from "path";
7263
+ import path24 from "path";
5990
7264
 
5991
7265
  // src/adapters/config.ts
5992
- import { promises as fs4 } from "fs";
7266
+ import { promises as fs14 } from "fs";
5993
7267
  import { randomUUID as randomUUID2 } from "crypto";
5994
- import path10 from "path";
7268
+ import path19 from "path";
5995
7269
  import { z as z6 } from "zod";
5996
7270
  var configuredAdapterDescriptorSchema = adapterDescriptorSchema.and(z6.object({
5997
7271
  launchDefaults: z6.record(z6.string(), z6.unknown()).optional(),
@@ -6004,7 +7278,7 @@ var adapterConfigSchema = z6.object({
6004
7278
  async function loadAdapterConfig(dapCliHome) {
6005
7279
  const configPath = getAdapterConfigPath(dapCliHome);
6006
7280
  try {
6007
- const raw = await fs4.readFile(configPath, "utf8");
7281
+ const raw = await fs14.readFile(configPath, "utf8");
6008
7282
  return parseAdapterConfig(raw);
6009
7283
  } catch (error) {
6010
7284
  if (isNodeError4(error) && error.code === "ENOENT") {
@@ -6018,7 +7292,7 @@ async function loadAdapterConfig(dapCliHome) {
6018
7292
  }
6019
7293
  function getAdapterConfigPath(dapCliHome) {
6020
7294
  const home = dapCliHome === void 0 ? getDapCliHome() : getDapCliHome({ ...process.env, DAP_CLI_HOME: dapCliHome });
6021
- return path10.join(home, "config", "adapters.json");
7295
+ return path19.join(home, "config", "adapters.json");
6022
7296
  }
6023
7297
  function parseAdapterConfig(raw) {
6024
7298
  return adapterConfigSchema.parse(JSON.parse(raw));
@@ -6034,10 +7308,12 @@ function isNodeError4(error) {
6034
7308
  }
6035
7309
 
6036
7310
  // src/adapters/builtins/debugpy.ts
6037
- import { existsSync as existsSync2 } from "fs";
6038
- import { spawnSync } from "child_process";
6039
- function createDebugpyDescriptor(pythonPath) {
6040
- const resolvedPythonPath = pythonPath ?? resolveDefaultDebugpyPythonPath();
7311
+ import { promises as fs15 } from "fs";
7312
+ import { execFile as execFile3 } from "child_process";
7313
+ import { promisify as promisify2 } from "util";
7314
+ var execFileAsync2 = promisify2(execFile3);
7315
+ async function createDebugpyDescriptor(pythonPath) {
7316
+ const resolvedPythonPath = pythonPath ?? await resolveDefaultDebugpyPythonPath();
6041
7317
  return {
6042
7318
  id: "debugpy",
6043
7319
  label: "Python Debug Adapter (debugpy)",
@@ -6048,26 +7324,158 @@ function createDebugpyDescriptor(pythonPath) {
6048
7324
  }
6049
7325
  };
6050
7326
  }
6051
- function resolveDefaultDebugpyPythonPath() {
6052
- const provisionedPython = getDapCliVenvPythonPath();
6053
- const candidates = [provisionedPython, "python3"];
6054
- if (existsSync2(provisionedPython) && pythonHasDebugpy(provisionedPython)) {
6055
- return provisionedPython;
7327
+ async function pathExists2(p) {
7328
+ try {
7329
+ await fs15.access(p);
7330
+ return true;
7331
+ } catch {
7332
+ return false;
7333
+ }
7334
+ }
7335
+ async function pythonHasDebugpy(pythonPath) {
7336
+ try {
7337
+ await execFileAsync2(pythonPath, ["-c", "import debugpy"]);
7338
+ return true;
7339
+ } catch {
7340
+ return false;
7341
+ }
7342
+ }
7343
+ async function resolveDefaultDebugpyPythonPath(env = process.env) {
7344
+ const legacyVenvPython = getDapCliVenvPythonPath(env);
7345
+ if (await pathExists2(legacyVenvPython) && await pythonHasDebugpy(legacyVenvPython)) {
7346
+ return legacyVenvPython;
6056
7347
  }
6057
- if (pythonHasDebugpy("python3")) {
7348
+ if (await pythonHasDebugpy("python3")) {
6058
7349
  return "python3";
6059
7350
  }
6060
- throw usageError("debugpy adapter is not installed.", {
6061
- code: "debugpy_not_found",
7351
+ const adaptersDir = getDapCliAdaptersDir(env);
7352
+ const result = await provisionAdapter("debugpy", {
7353
+ env,
7354
+ assumeYes: resolveAssumeYes(void 0, env),
7355
+ adaptersDir
7356
+ });
7357
+ return result.entrypoint;
7358
+ }
7359
+
7360
+ // src/adapters/builtins/delve.ts
7361
+ import { promises as fs16 } from "fs";
7362
+ import { spawnSync } from "child_process";
7363
+ import path20 from "path";
7364
+ async function createDelveDescriptor(delvePath) {
7365
+ const resolvedDelvePath = delvePath ?? await resolveDefaultDelvePath();
7366
+ assertSupportedProvisionedDelveToolchain(resolvedDelvePath);
7367
+ const toolchainEnvironment = createGoToolchainEnvironment();
7368
+ return {
7369
+ id: "delve",
7370
+ label: "Go Debug Adapter (Delve)",
7371
+ transport: {
7372
+ kind: "server",
7373
+ command: resolvedDelvePath,
7374
+ args: ["dap", "--listen=127.0.0.1:${port}"],
7375
+ host: "127.0.0.1",
7376
+ ...toolchainEnvironment === void 0 ? {} : { env: toolchainEnvironment }
7377
+ }
7378
+ };
7379
+ }
7380
+ function createGoToolchainEnvironment() {
7381
+ const goToolchain = process.env.GOTOOLCHAIN;
7382
+ return goToolchain === void 0 || goToolchain.length === 0 ? void 0 : { GOTOOLCHAIN: goToolchain };
7383
+ }
7384
+ async function pathExists3(p) {
7385
+ try {
7386
+ await fs16.access(p);
7387
+ return true;
7388
+ } catch {
7389
+ return false;
7390
+ }
7391
+ }
7392
+ async function resolveDefaultDelvePath(env = process.env) {
7393
+ if (delveIsUsable("dlv")) {
7394
+ return "dlv";
7395
+ }
7396
+ const provisionedDelve = getProvisionedDelvePath(env);
7397
+ if (await pathExists3(provisionedDelve) && delveIsUsable(provisionedDelve)) {
7398
+ return provisionedDelve;
7399
+ }
7400
+ const adaptersDir = getDapCliAdaptersDir(env);
7401
+ const result = await provisionAdapter("delve", {
7402
+ env,
7403
+ assumeYes: resolveAssumeYes(void 0, env),
7404
+ adaptersDir
7405
+ });
7406
+ return result.entrypoint;
7407
+ }
7408
+ function getProvisionedDelvePath(env = process.env) {
7409
+ return path20.join(getDapCliAdaptersDir(env), "delve", process.platform === "win32" ? "dlv.exe" : "dlv");
7410
+ }
7411
+ function delveIsUsable(command) {
7412
+ const result = spawnSync(command, ["version"], { encoding: "utf8" });
7413
+ return result.status === 0;
7414
+ }
7415
+ function assertSupportedProvisionedDelveToolchain(delvePath) {
7416
+ const delveVersion = readCommandVersion(delvePath, ["version"], /Version:\s*v?([0-9]+\.[0-9]+\.[0-9]+)/i);
7417
+ if (delveVersion !== "1.26.3") {
7418
+ return;
7419
+ }
7420
+ const goVersion = readCommandVersion("go", ["version"], /go version go([0-9]+\.[0-9]+(?:\.[0-9]+)?)/i);
7421
+ if (goVersion === void 0 || compareNumericVersions(goVersion, "1.24.0") >= 0) {
7422
+ return;
7423
+ }
7424
+ throw usageError("Provisioned Delve is incompatible with the active Go toolchain.", {
7425
+ code: "delve_go_version_incompatible",
6062
7426
  diagnostics: [
6063
- "Run npm run setup-adapters to provision debugpy, or see docs/ADAPTER-SETUP.md for advanced manual provisioning.",
6064
- `Checked: ${candidates.join(", ")}`
7427
+ `Delve ${delveVersion} requires Go 1.24+ for debuggee builds; current \`go\` is ${goVersion}.`,
7428
+ "Use `GOTOOLCHAIN=go1.24.0` for the dap-cli launch, or update the active Go installation.",
7429
+ "Run `go version` in the same shell/environment used for dap-cli to confirm the effective toolchain."
6065
7430
  ]
6066
7431
  });
6067
7432
  }
6068
- function pythonHasDebugpy(pythonPath) {
6069
- const result = spawnSync(pythonPath, ["-c", "import debugpy"], { encoding: "utf8" });
6070
- return result.status === 0;
7433
+ function readCommandVersion(command, args, pattern) {
7434
+ const result = spawnSync(command, [...args], { encoding: "utf8" });
7435
+ if (result.status !== 0) {
7436
+ return void 0;
7437
+ }
7438
+ const output = `${result.stdout ?? ""}
7439
+ ${result.stderr ?? ""}`;
7440
+ return pattern.exec(output)?.[1];
7441
+ }
7442
+ function compareNumericVersions(left, right) {
7443
+ const leftParts = left.split(".").map((part) => Number.parseInt(part, 10));
7444
+ const rightParts = right.split(".").map((part) => Number.parseInt(part, 10));
7445
+ const width = Math.max(leftParts.length, rightParts.length);
7446
+ for (let index = 0; index < width; index += 1) {
7447
+ const leftPart = leftParts[index] ?? 0;
7448
+ const rightPart = rightParts[index] ?? 0;
7449
+ if (leftPart !== rightPart) {
7450
+ return leftPart - rightPart;
7451
+ }
7452
+ }
7453
+ return 0;
7454
+ }
7455
+
7456
+ // src/adapters/builtins/codelldb.ts
7457
+ import path21 from "path";
7458
+ async function createCodeLldbDescriptor(codelldbPath) {
7459
+ const entrypoint = codelldbPath ?? await resolveDefaultCodeLldbPath();
7460
+ const libLldbPath = path21.join(path21.dirname(entrypoint), "..", "lldb", "lib", "liblldb.dylib");
7461
+ return {
7462
+ id: "codelldb",
7463
+ label: "Rust Debug Adapter (CodeLLDB)",
7464
+ transport: {
7465
+ kind: "server",
7466
+ command: entrypoint,
7467
+ args: ["--liblldb", libLldbPath, "--port", "${port}"],
7468
+ host: "127.0.0.1"
7469
+ }
7470
+ };
7471
+ }
7472
+ async function resolveDefaultCodeLldbPath(env = process.env) {
7473
+ const result = await provisionAdapter("codelldb", {
7474
+ env,
7475
+ assumeYes: resolveAssumeYes(void 0, env),
7476
+ adaptersDir: getDapCliAdaptersDir(env)
7477
+ });
7478
+ return result.entrypoint;
6071
7479
  }
6072
7480
 
6073
7481
  // src/adapters/registry.ts
@@ -6086,6 +7494,16 @@ var AdapterRegistry = class {
6086
7494
  label: "Python Debug Adapter (debugpy)",
6087
7495
  create: () => createDebugpyDescriptor()
6088
7496
  });
7497
+ this.builtInAdapters.set("delve", {
7498
+ id: "delve",
7499
+ label: "Go Debug Adapter (Delve)",
7500
+ create: () => createDelveDescriptor()
7501
+ });
7502
+ this.builtInAdapters.set("codelldb", {
7503
+ id: "codelldb",
7504
+ label: "Rust Debug Adapter (CodeLLDB)",
7505
+ create: () => createCodeLldbDescriptor()
7506
+ });
6089
7507
  }
6090
7508
  for (const descriptor of options.builtInAdapters ?? []) {
6091
7509
  this.builtInAdapters.set(descriptor.id, {
@@ -6098,7 +7516,7 @@ var AdapterRegistry = class {
6098
7516
  this.customAdapters.set(descriptor.id, descriptor);
6099
7517
  }
6100
7518
  }
6101
- resolve(id) {
7519
+ async resolve(id) {
6102
7520
  const builtInAdapter = this.builtInAdapters.get(id);
6103
7521
  if (builtInAdapter !== void 0) {
6104
7522
  return builtInAdapter.create();
@@ -6127,9 +7545,9 @@ var AdapterRegistry = class {
6127
7545
  };
6128
7546
 
6129
7547
  // src/config/launchConfig.ts
6130
- import { promises as fs5 } from "fs";
7548
+ import { promises as fs17 } from "fs";
6131
7549
  import os from "os";
6132
- import path11 from "path";
7550
+ import path22 from "path";
6133
7551
  import { parse } from "jsonc-parser";
6134
7552
  import { z as z7 } from "zod";
6135
7553
  var launchConfigTypeMap = {
@@ -6138,7 +7556,9 @@ var launchConfigTypeMap = {
6138
7556
  chrome: "js-debug",
6139
7557
  "pwa-chrome": "js-debug",
6140
7558
  python: "debugpy",
6141
- debugpy: "debugpy"
7559
+ debugpy: "debugpy",
7560
+ go: "delve",
7561
+ lldb: "codelldb"
6142
7562
  };
6143
7563
  var maxLaunchJsonBytes = 256 * 1024;
6144
7564
  var platformKeys = /* @__PURE__ */ new Set(["osx", "mac", "linux", "windows"]);
@@ -6165,12 +7585,12 @@ function resolveLaunchConfig(sources) {
6165
7585
  };
6166
7586
  }
6167
7587
  function resolveLaunchConfigurationConfig(config, options) {
6168
- const workspaceFolder = path11.resolve(options.workspaceFolder);
7588
+ const workspaceFolder = path22.resolve(options.workspaceFolder);
6169
7589
  const platform = options.platform ?? process.platform;
6170
7590
  const merged = applyPlatformOverlay(config, platform);
6171
7591
  const context = {
6172
7592
  workspaceFolder,
6173
- workspaceFolderBasename: path11.basename(workspaceFolder),
7593
+ workspaceFolderBasename: path22.basename(workspaceFolder),
6174
7594
  userHome: options.userHome ?? os.homedir(),
6175
7595
  execPath: options.execPath ?? process.execPath,
6176
7596
  env: options.env ?? process.env
@@ -6185,20 +7605,20 @@ function resolveLaunchConfigurationConfig(config, options) {
6185
7605
  for (const key of vscodeOnlyLaunchConfigKeys) {
6186
7606
  delete resolved[key];
6187
7607
  }
6188
- return resolved;
7608
+ return normalizeGoLaunchConfigProgram(resolved, workspaceFolder);
6189
7609
  }
6190
7610
  async function loadVSCodeLaunchJson(cwd) {
6191
- const workspaceFolder = path11.resolve(cwd);
6192
- const launchJsonPath = path11.join(cwd, ".vscode", "launch.json");
7611
+ const workspaceFolder = path22.resolve(cwd);
7612
+ const launchJsonPath = path22.join(cwd, ".vscode", "launch.json");
6193
7613
  try {
6194
- const stat = await fs5.stat(launchJsonPath);
7614
+ const stat = await fs17.stat(launchJsonPath);
6195
7615
  if (stat.size > maxLaunchJsonBytes) {
6196
7616
  throw usageError("Invalid launch.json.", {
6197
7617
  code: "invalid_launch_json",
6198
7618
  diagnostics: [".vscode/launch.json is larger than 256KB."]
6199
7619
  });
6200
7620
  }
6201
- const raw = await fs5.readFile(launchJsonPath, "utf8");
7621
+ const raw = await fs17.readFile(launchJsonPath, "utf8");
6202
7622
  const normalized = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
6203
7623
  const parsed = launchJsonSchema.parse(parseJsonc(normalized));
6204
7624
  return {
@@ -6303,7 +7723,7 @@ async function applyJsDebugSourceMapDefaults(config, options) {
6303
7723
  if (!isJsDebugLaunchType(config.type)) {
6304
7724
  return config;
6305
7725
  }
6306
- const hasTsConfig = await pathExists(path11.join(options.workspaceFolder, "tsconfig.json"));
7726
+ const hasTsConfig = await pathExists4(path22.join(options.workspaceFolder, "tsconfig.json"));
6307
7727
  if (!hasTsConfig) {
6308
7728
  return config;
6309
7729
  }
@@ -6313,9 +7733,9 @@ async function applyJsDebugSourceMapDefaults(config, options) {
6313
7733
  }
6314
7734
  if (mapped.outFiles === void 0) {
6315
7735
  mapped.outFiles = [
6316
- path11.join(options.workspaceFolder, "dist", "**", "*.js"),
6317
- path11.join(options.workspaceFolder, "out", "**", "*.js"),
6318
- path11.join(options.workspaceFolder, "build", "**", "*.js")
7736
+ path22.join(options.workspaceFolder, "dist", "**", "*.js"),
7737
+ path22.join(options.workspaceFolder, "out", "**", "*.js"),
7738
+ path22.join(options.workspaceFolder, "build", "**", "*.js")
6319
7739
  ];
6320
7740
  }
6321
7741
  return mapped;
@@ -6328,6 +7748,29 @@ function mapDebugpyFlags(flags) {
6328
7748
  }
6329
7749
  return mapped;
6330
7750
  }
7751
+ function validateCodeLldbNativeConfig(config) {
7752
+ if (Object.prototype.hasOwnProperty.call(config, "cargo")) {
7753
+ throw usageError("CodeLLDB Cargo launch configurations are not supported by dap-cli.", {
7754
+ code: "codelldb_cargo_config_unsupported",
7755
+ diagnostics: [
7756
+ "The `cargo` property is resolved by the VS Code CodeLLDB extension and cannot be forwarded to the standalone adapter.",
7757
+ "Build an explicitly built Rust binary and pass its executable path with `program`."
7758
+ ],
7759
+ data: { adapterId: "codelldb", unsupportedField: "cargo", requiredField: "program" }
7760
+ });
7761
+ }
7762
+ return config;
7763
+ }
7764
+ function normalizeGoLaunchConfigProgram(config, workspaceFolder) {
7765
+ if (config.type !== "go" || config.request === "attach" || typeof config.program !== "string" || path22.isAbsolute(config.program)) {
7766
+ return config;
7767
+ }
7768
+ const cwd = typeof config.cwd === "string" ? config.cwd : workspaceFolder;
7769
+ return {
7770
+ ...config,
7771
+ program: path22.resolve(cwd, config.program)
7772
+ };
7773
+ }
6331
7774
  function applyPlatformOverlay(config, platform) {
6332
7775
  const base = {};
6333
7776
  for (const [key, value] of Object.entries(config)) {
@@ -6410,9 +7853,9 @@ function resolveLaunchString(value, jsonPath, context) {
6410
7853
  });
6411
7854
  });
6412
7855
  }
6413
- async function pathExists(filePath) {
7856
+ async function pathExists4(filePath) {
6414
7857
  try {
6415
- await fs5.stat(filePath);
7858
+ await fs17.stat(filePath);
6416
7859
  return true;
6417
7860
  } catch (error) {
6418
7861
  if (isNodeError5(error) && error.code === "ENOENT") {
@@ -6454,9 +7897,10 @@ function isNodeError5(error) {
6454
7897
  }
6455
7898
 
6456
7899
  // src/config/programInference.ts
6457
- import path12 from "path";
7900
+ import path23 from "path";
6458
7901
  var extensionTable = {
6459
7902
  ".py": { adapterId: "debugpy", type: "python" },
7903
+ ".go": { adapterId: "delve", type: "go" },
6460
7904
  ".js": { adapterId: "js-debug", type: "pwa-node" },
6461
7905
  ".mjs": { adapterId: "js-debug", type: "pwa-node" },
6462
7906
  ".cjs": { adapterId: "js-debug", type: "pwa-node" },
@@ -6484,7 +7928,7 @@ function inferAdapterAndType(args) {
6484
7928
  return { adapterId, type, inferred: { adapter: true, type: false } };
6485
7929
  }
6486
7930
  if (program !== void 0) {
6487
- const extension = path12.extname(program).toLowerCase();
7931
+ const extension = path23.extname(program).toLowerCase();
6488
7932
  const match = extensionTable[extension];
6489
7933
  if (match === void 0) {
6490
7934
  throw usageError(`Cannot infer adapter from program extension '${extension}'. Pass --adapter or --type explicitly.`, {
@@ -6500,7 +7944,7 @@ function inferAdapterAndType(args) {
6500
7944
  function defaultTypeForAdapter(adapterId, program) {
6501
7945
  if (adapterId === "js-debug") {
6502
7946
  if (program !== void 0) {
6503
- const ext = path12.extname(program).toLowerCase();
7947
+ const ext = path23.extname(program).toLowerCase();
6504
7948
  if (ext === ".html" || ext === ".htm") {
6505
7949
  return "pwa-chrome";
6506
7950
  }
@@ -6510,6 +7954,12 @@ function defaultTypeForAdapter(adapterId, program) {
6510
7954
  if (adapterId === "debugpy") {
6511
7955
  return "python";
6512
7956
  }
7957
+ if (adapterId === "delve") {
7958
+ return "go";
7959
+ }
7960
+ if (adapterId === "codelldb") {
7961
+ return "lldb";
7962
+ }
6513
7963
  return void 0;
6514
7964
  }
6515
7965
 
@@ -6551,7 +8001,7 @@ function createNameParams(name) {
6551
8001
  return name === void 0 ? {} : { name };
6552
8002
  }
6553
8003
  async function startDap(output, mode, options) {
6554
- const workspace = path13.resolve(options.workspace ?? process.cwd());
8004
+ const workspace = path24.resolve(options.workspace ?? process.cwd());
6555
8005
  if (options.listConfigs === true) {
6556
8006
  output.success(listLaunchConfigEntries(await loadVSCodeLaunchJson(workspace)), { command: "launch configs" });
6557
8007
  return;
@@ -6595,7 +8045,7 @@ async function startDap(output, mode, options) {
6595
8045
  ...await mapConfigForAdapter(adapterId, resolveLaunchConfig({ namedConfig: { ...adapterDefaults, ...namedConfig }, jsonOverrides, jsonConfig, flags: adapterFlags }), workspace),
6596
8046
  request: effectiveMode
6597
8047
  };
6598
- const descriptor = adapterId === "fake" ? createFakeDescriptor(options.script ?? (effectiveMode === "attach" ? "attach-stopped" : "stopped-on-entry"), effectiveMode) : new AdapterRegistry({ config: adapterConfig }).resolve(adapterId);
8048
+ const descriptor = adapterId === "fake" ? createFakeDescriptor(options.script ?? (effectiveMode === "attach" ? "attach-stopped" : "stopped-on-entry"), effectiveMode) : await new AdapterRegistry({ config: adapterConfig }).resolve(adapterId);
6599
8049
  const client = await createControllerClient({ dapCliHome: process.env.DAP_CLI_HOME, timeoutMs: startControllerRequestTimeoutMs });
6600
8050
  try {
6601
8051
  const response = await client.request("dap.start", {
@@ -6625,7 +8075,7 @@ async function createCompoundStartMember(configuration, memberName, workspaceFol
6625
8075
  ...await mapConfigForAdapter(adapterId, resolveLaunchConfig({ namedConfig: { ...adapterDefaults, ...resolvedConfig }, jsonOverrides, jsonConfig, flags: adapterFlags }), workspaceFolder),
6626
8076
  request: memberMode
6627
8077
  };
6628
- const descriptor = adapterId === "fake" ? createFakeDescriptor(options.script ?? (memberMode === "attach" ? "attach-stopped" : "stopped-on-entry"), memberMode) : new AdapterRegistry({ config: adapterConfig }).resolve(adapterId);
8078
+ const descriptor = adapterId === "fake" ? createFakeDescriptor(options.script ?? (memberMode === "attach" ? "attach-stopped" : "stopped-on-entry"), memberMode) : await new AdapterRegistry({ config: adapterConfig }).resolve(adapterId);
6629
8079
  return { memberName, mode: memberMode, descriptor, config };
6630
8080
  }
6631
8081
  function getAdapterDefaults(adapterConfig, adapterId, mode) {
@@ -6704,6 +8154,12 @@ async function mapConfigForAdapter(adapterId, config, workspaceFolder) {
6704
8154
  if (adapterId === "debugpy") {
6705
8155
  return mapDebugpyFlags(config);
6706
8156
  }
8157
+ if (adapterId === "delve") {
8158
+ return normalizeGoLaunchConfigProgram(config, workspaceFolder);
8159
+ }
8160
+ if (adapterId === "codelldb") {
8161
+ return validateCodeLldbNativeConfig(config);
8162
+ }
6707
8163
  return config;
6708
8164
  }
6709
8165
  function setIfDefined(target, key, value) {
@@ -6727,7 +8183,7 @@ function createFakeDescriptor(script, mode) {
6727
8183
  transport: {
6728
8184
  kind: "stdio",
6729
8185
  command: process.execPath,
6730
- args: ["--experimental-strip-types", path13.join(process.cwd(), "tests", "fixtures", "fake-adapter-entry.ts"), "--script", script, "--mode", mode]
8186
+ args: ["--experimental-strip-types", path24.join(process.cwd(), "tests", "fixtures", "fake-adapter-entry.ts"), "--script", script, "--mode", mode]
6731
8187
  }
6732
8188
  };
6733
8189
  }
@@ -6843,6 +8299,139 @@ function createNameParams2(name) {
6843
8299
  return name === void 0 ? {} : { name };
6844
8300
  }
6845
8301
 
8302
+ // src/cli/commands/setupAdapters.ts
8303
+ import path25 from "path";
8304
+ import { promises as fs18 } from "fs";
8305
+ import { Option } from "commander";
8306
+ var ALL_ADAPTERS = ["js-debug", "debugpy", "delve", "codelldb"];
8307
+ var ADAPTER_VERSIONS = {
8308
+ "js-debug": JS_DEBUG_VERSION,
8309
+ "debugpy": DEBUGPY_VERSION,
8310
+ "delve": DELVE_VERSION,
8311
+ "codelldb": CODELLDB_VERSION
8312
+ };
8313
+ function expectedEntrypoint(adaptersDir, id) {
8314
+ switch (id) {
8315
+ case "js-debug":
8316
+ return path25.join(adaptersDir, "js-debug", "src", "dapDebugServer.js");
8317
+ case "debugpy":
8318
+ return path25.join(
8319
+ adaptersDir,
8320
+ "debugpy",
8321
+ process.platform === "win32" ? path25.join("venv", "Scripts", "python.exe") : path25.join("venv", "bin", "python")
8322
+ );
8323
+ case "delve":
8324
+ return path25.join(adaptersDir, "delve", process.platform === "win32" ? "dlv.exe" : "dlv");
8325
+ case "codelldb":
8326
+ return path25.join(adaptersDir, "codelldb", "extension", "adapter", "codelldb");
8327
+ }
8328
+ }
8329
+ async function pathExists5(p) {
8330
+ try {
8331
+ await fs18.access(p);
8332
+ return true;
8333
+ } catch {
8334
+ return false;
8335
+ }
8336
+ }
8337
+ function isAdapterId(value) {
8338
+ return value === "js-debug" || value === "debugpy" || value === "delve" || value === "codelldb";
8339
+ }
8340
+ async function isRuntimeReady(adaptersDir, id) {
8341
+ return id === "codelldb" ? isCodeLldbRuntimeReady(path25.join(adaptersDir, id)) : pathExists5(expectedEntrypoint(adaptersDir, id));
8342
+ }
8343
+ async function runSetupAdaptersAction(opts) {
8344
+ const targets = opts.adapter !== void 0 ? [opts.adapter] : ALL_ADAPTERS;
8345
+ const adaptersDir = getDapCliAdaptersDir(opts.env);
8346
+ await fs18.mkdir(adaptersDir, { recursive: true });
8347
+ const pending = [];
8348
+ for (const id of targets) {
8349
+ const version = ADAPTER_VERSIONS[id];
8350
+ const cached = await hasConsentMarker(adaptersDir, id, version) && await isRuntimeReady(adaptersDir, id);
8351
+ if (!cached) {
8352
+ pending.push(id);
8353
+ }
8354
+ }
8355
+ if (pending.length > 0 && !opts.assumeYes) {
8356
+ const pendingDescription = pending.map((id) => `${id} ${ADAPTER_VERSIONS[id]}`).join(", ");
8357
+ await confirm({
8358
+ assumeYes: false,
8359
+ question: `Install ${pending.length} adapter${pending.length === 1 ? "" : "s"} (${pendingDescription}) into ${adaptersDir}/?`,
8360
+ ...opts.stdin === void 0 ? {} : { stdin: opts.stdin },
8361
+ ...opts.stderr === void 0 ? {} : { stderr: opts.stderr }
8362
+ });
8363
+ }
8364
+ const innerCtxBase = {
8365
+ env: opts.env,
8366
+ adaptersDir,
8367
+ ...opts.stdin === void 0 ? {} : { stdin: opts.stdin },
8368
+ ...opts.stderr === void 0 ? {} : { stderr: opts.stderr }
8369
+ };
8370
+ const entries = [];
8371
+ for (const id of targets) {
8372
+ const version = ADAPTER_VERSIONS[id];
8373
+ try {
8374
+ const result = await provisionAdapter(id, { ...innerCtxBase, assumeYes: true });
8375
+ const entry = {
8376
+ id,
8377
+ version: result.version,
8378
+ status: result.fromCache ? "cached" : "installed",
8379
+ installRoot: result.installRoot
8380
+ };
8381
+ entries.push(entry);
8382
+ if (opts.output !== void 0) {
8383
+ const verb = result.fromCache ? "already cached" : "installed";
8384
+ opts.output.warn(`${id} ${result.version}: ${verb} (${result.installRoot})`);
8385
+ }
8386
+ } catch (err) {
8387
+ const cliError = err instanceof CliError ? err : null;
8388
+ const code = cliError?.code ?? "internal_error";
8389
+ const message = err instanceof Error ? err.message : String(err);
8390
+ entries.push({
8391
+ id,
8392
+ version,
8393
+ status: "failed",
8394
+ error: {
8395
+ code,
8396
+ message,
8397
+ diagnostics: cliError?.diagnostics ?? [message]
8398
+ }
8399
+ });
8400
+ if (opts.output !== void 0) {
8401
+ opts.output.warn(`${id} ${version}: FAILED (${code})`);
8402
+ }
8403
+ }
8404
+ }
8405
+ return { adapters: entries };
8406
+ }
8407
+ function registerSetupAdaptersCommand(program, output) {
8408
+ program.command("setup-adapters").helpGroup("Adapters").description("Install or update built-in debug adapters (js-debug, debugpy, delve, codelldb) into ~/.dap-cli/adapters/").addOption(
8409
+ new Option("--adapter <id>", "install only the named adapter").choices(["js-debug", "debugpy", "delve", "codelldb"])
8410
+ ).action(async (cmdOpts) => {
8411
+ const adapterId = cmdOpts.adapter !== void 0 && isAdapterId(cmdOpts.adapter) ? cmdOpts.adapter : void 0;
8412
+ const assumeYes = resolveAssumeYes(void 0, process.env);
8413
+ const result = await runSetupAdaptersAction({
8414
+ ...adapterId === void 0 ? {} : { adapter: adapterId },
8415
+ assumeYes,
8416
+ env: process.env,
8417
+ stdin: process.stdin,
8418
+ stderr: process.stderr,
8419
+ stdout: process.stdout,
8420
+ output
8421
+ });
8422
+ const failed = result.adapters.filter((a) => a.status === "failed");
8423
+ if (failed.length > 0) {
8424
+ const failedNames = failed.map((a) => `${a.id} (${a.error?.code ?? "internal_error"})`).join(", ");
8425
+ throw usageError(`Adapter setup failed for: ${failedNames}`, {
8426
+ code: "provision_setup_failed",
8427
+ diagnostics: failed.flatMap((a) => a.error?.diagnostics ?? []),
8428
+ data: { adapters: result.adapters }
8429
+ });
8430
+ }
8431
+ output.success(result, { command: "setup-adapters" });
8432
+ });
8433
+ }
8434
+
6846
8435
  // src/cli/program.ts
6847
8436
  var require2 = createRequire(import.meta.url);
6848
8437
  function loadPackageJson() {
@@ -6867,12 +8456,19 @@ function createProgram(options = {}) {
6867
8456
  resolveMode: () => resolveOutputMode({ cliHuman: getProgramHumanOption(program), isStdoutTTY: stdout.isTTY === true, env: process.env })
6868
8457
  });
6869
8458
  program[PROGRAM_OUTPUT_WRITER] = output;
6870
- program.name("dap-cli").description("A Debug Adapter Protocol CLI for agents. Control debug sessions from shell commands.").version(getPackageVersion(packageJson)).option("--human", "render human-readable output (default when stdout is a TTY and DAP_CLI_HUMAN is set)").option("--no-human", "render machine-readable JSON output even if DAP_CLI_HUMAN is set or stdout is a TTY").showHelpAfterError().exitOverride();
8459
+ program.name("dap-cli").description("A Debug Adapter Protocol CLI for agents. Control debug sessions from shell commands.").version(getPackageVersion(packageJson)).option("--human", "render human-readable output (default when stdout is a TTY and DAP_CLI_HUMAN is set)").option("--no-human", "render machine-readable JSON output even if DAP_CLI_HUMAN is set or stdout is a TTY").option("-y, --yes", "pre-consent to adapter provisioning prompts (equivalent to DAP_CLI_ASSUME_YES=1)").showHelpAfterError().exitOverride();
8460
+ program.hook("preAction", (thisCommand) => {
8461
+ const opts = thisCommand.opts();
8462
+ if (opts.yes === true) {
8463
+ process.env.DAP_CLI_ASSUME_YES = "1";
8464
+ }
8465
+ });
6871
8466
  registerControllerCommands(program, output);
6872
8467
  registerSessionCommands(program, output);
6873
8468
  registerDapCoreCommands(program, output);
6874
8469
  registerGeneratedDapCommands(program, output);
6875
8470
  registerDapAliasCommands(program, output);
8471
+ registerSetupAdaptersCommand(program, output);
6876
8472
  program.helpCommand(false);
6877
8473
  program.command("help [command...]").description("Display help for a command. Pass multiple words to drill into subcommands (e.g. `dap-cli help breakpoints set`).").action((commandPath) => {
6878
8474
  const segments = Array.isArray(commandPath) ? commandPath : [];