@roblourens/dap-cli 0.1.0 → 0.2.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;
@@ -869,6 +869,10 @@ function getDapCliLogDir(env = process.env) {
869
869
  return path.join(getDapCliHome(env), "logs");
870
870
  }
871
871
  function getDapCliAdaptersDir(env = process.env) {
872
+ const configured = env.DAP_CLI_ADAPTERS_DIR;
873
+ if (configured !== void 0 && configured.trim().length > 0) {
874
+ return path.resolve(configured);
875
+ }
872
876
  return path.join(getDapCliHome(env), "adapters");
873
877
  }
874
878
  function getDapCliVenvPythonPath(env = process.env) {
@@ -1153,8 +1157,8 @@ function isSessionErrorCode(code) {
1153
1157
  }
1154
1158
 
1155
1159
  // src/controller/server.ts
1156
- import path8 from "path";
1157
- import { randomBytes as randomBytes2 } from "crypto";
1160
+ import path16 from "path";
1161
+ import { randomBytes as randomBytes5 } from "crypto";
1158
1162
 
1159
1163
  // src/adapters/descriptor.ts
1160
1164
  import { z as z3 } from "zod";
@@ -1342,7 +1346,7 @@ async function connectSocketAdapter(adapterId, descriptor) {
1342
1346
  }
1343
1347
  async function startServerSocketAdapter(adapterId, descriptor, logDir) {
1344
1348
  const port = await getFreePort(descriptor.host);
1345
- const args = descriptor.args.map((arg) => arg === "${port}" ? String(port) : arg);
1349
+ const args = descriptor.args.map((arg) => arg.replaceAll("${port}", String(port)));
1346
1350
  const child = spawn2(descriptor.command, args, {
1347
1351
  cwd: descriptor.cwd,
1348
1352
  env: descriptor.env === void 0 ? process.env : { ...process.env, ...descriptor.env },
@@ -1461,10 +1465,1024 @@ async function delay(ms) {
1461
1465
  }
1462
1466
 
1463
1467
  // src/adapters/builtins/jsDebug.ts
1464
- import { existsSync } from "fs";
1468
+ import { promises as fs10 } from "fs";
1469
+ import path13 from "path";
1470
+
1471
+ // src/cli/confirm.ts
1472
+ import * as readline from "readline/promises";
1473
+ async function confirm(options) {
1474
+ if (options.assumeYes) {
1475
+ return true;
1476
+ }
1477
+ const stdin = options.stdin ?? process.stdin;
1478
+ const stderr = options.stderr ?? process.stderr;
1479
+ if (stdin.isTTY !== true) {
1480
+ throw usageError("Confirmation required but stdin is not a TTY.", {
1481
+ code: "provision_consent_required",
1482
+ diagnostics: [
1483
+ options.question,
1484
+ "Re-run with `--yes` / `-y` or set `DAP_CLI_ASSUME_YES=1` to pre-consent."
1485
+ ],
1486
+ data: { question: options.question }
1487
+ });
1488
+ }
1489
+ stderr.write(`
1490
+ ${options.question}
1491
+ `);
1492
+ for (const line of options.details ?? []) {
1493
+ stderr.write(` ${line}
1494
+ `);
1495
+ }
1496
+ stderr.write("Proceed? [y/N] ");
1497
+ const rl = readline.createInterface({ input: stdin, output: stderr, terminal: false });
1498
+ try {
1499
+ const answer = (await rl.question("")).trim().toLowerCase();
1500
+ if (answer === "y" || answer === "yes") {
1501
+ return true;
1502
+ }
1503
+ throw usageError("User declined provisioning consent.", {
1504
+ code: "provision_consent_declined",
1505
+ diagnostics: ["Re-run with `--yes` to pre-consent."],
1506
+ data: { question: options.question }
1507
+ });
1508
+ } finally {
1509
+ rl.close();
1510
+ }
1511
+ }
1512
+ function resolveAssumeYes(cliYes, env) {
1513
+ if (cliYes === true) {
1514
+ return true;
1515
+ }
1516
+ const value = env.DAP_CLI_ASSUME_YES;
1517
+ return value === "1" || value === "true";
1518
+ }
1519
+
1520
+ // src/adapters/provision/jsDebug.ts
1521
+ import path9 from "path";
1522
+ import { createHash, randomBytes as randomBytes2 } from "crypto";
1523
+ import { promises as fs6, createReadStream as createReadStream2 } from "fs";
1524
+
1525
+ // src/adapters/provision/atomicInstall.ts
1465
1526
  import path5 from "path";
1466
- function createJsDebugDescriptor(jsDebugPath) {
1467
- const dapServerPath = jsDebugPath ?? resolveDefaultJsDebugPath();
1527
+ import { promises as fs2 } from "fs";
1528
+ import { randomBytes } from "crypto";
1529
+ var CACHE_UNWRITABLE_CODES = /* @__PURE__ */ new Set(["EACCES", "EROFS", "ENOSPC", "EPERM"]);
1530
+ function isCacheUnwritableError(error) {
1531
+ if (!(error instanceof Error) || !("code" in error)) {
1532
+ return false;
1533
+ }
1534
+ const code = error.code;
1535
+ return typeof code === "string" && CACHE_UNWRITABLE_CODES.has(code);
1536
+ }
1537
+ function cacheUnwritableError(adaptersDir, errnoCode, adapterId) {
1538
+ return usageError("Adapter cache directory is not writable.", {
1539
+ code: "provision_cache_unwritable",
1540
+ diagnostics: [
1541
+ `Adapter cache: ${adaptersDir}`,
1542
+ `Filesystem error: ${errnoCode}`,
1543
+ "Override with `DAP_CLI_ADAPTERS_DIR=<writable-path>`."
1544
+ ],
1545
+ data: { adaptersDir, errnoCode, adapterId }
1546
+ });
1547
+ }
1548
+ function stagingName(adapterId) {
1549
+ return `.${adapterId}.tmp.${process.pid}.${randomBytes(4).toString("hex")}`;
1550
+ }
1551
+ async function verifyEntrypoints(stagingDir, entrypoints) {
1552
+ for (const rel of entrypoints) {
1553
+ const target = path5.join(stagingDir, rel);
1554
+ try {
1555
+ await fs2.stat(target);
1556
+ } catch (cause) {
1557
+ throw usageError("Adapter install completed but the expected entry point is missing.", {
1558
+ code: "provision_extract_failed",
1559
+ diagnostics: [`Missing entry point: ${rel}`, `Staging directory: ${stagingDir}`],
1560
+ data: { entrypoint: rel, stagingDir },
1561
+ cause
1562
+ });
1563
+ }
1564
+ }
1565
+ }
1566
+ async function atomicInstall(options) {
1567
+ const { adaptersDir, adapterId, populate, expectedEntrypoints } = options;
1568
+ try {
1569
+ await fs2.mkdir(adaptersDir, { recursive: true });
1570
+ } catch (error) {
1571
+ if (isCacheUnwritableError(error)) {
1572
+ throw cacheUnwritableError(adaptersDir, error.code ?? "unknown", adapterId);
1573
+ }
1574
+ throw error;
1575
+ }
1576
+ const canonical = path5.join(adaptersDir, adapterId);
1577
+ const staging = path5.join(adaptersDir, stagingName(adapterId));
1578
+ try {
1579
+ await fs2.mkdir(staging, { recursive: true });
1580
+ } catch (error) {
1581
+ if (isCacheUnwritableError(error)) {
1582
+ throw cacheUnwritableError(adaptersDir, error.code ?? "unknown", adapterId);
1583
+ }
1584
+ throw error;
1585
+ }
1586
+ try {
1587
+ await populate(staging);
1588
+ await verifyEntrypoints(staging, expectedEntrypoints);
1589
+ await fs2.rm(canonical, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
1590
+ await fs2.rename(staging, canonical);
1591
+ return canonical;
1592
+ } catch (error) {
1593
+ await fs2.rm(staging, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }).catch(() => void 0);
1594
+ if (!(error instanceof CliError) && isCacheUnwritableError(error)) {
1595
+ throw cacheUnwritableError(adaptersDir, error.code ?? "unknown", adapterId);
1596
+ }
1597
+ throw error;
1598
+ }
1599
+ }
1600
+
1601
+ // src/adapters/provision/http.ts
1602
+ import { createWriteStream as createWriteStream3 } from "fs";
1603
+ import { promises as fs3 } from "fs";
1604
+ import { Readable } from "stream";
1605
+ import { pipeline } from "stream/promises";
1606
+ import path6 from "path";
1607
+ import { fetch, ProxyAgent } from "undici";
1608
+ var LOCAL_HOSTS = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1"]);
1609
+ function pickProxyEnv(env, protocol) {
1610
+ if (protocol === "https:") {
1611
+ return env.HTTPS_PROXY ?? env.https_proxy;
1612
+ }
1613
+ return env.HTTP_PROXY ?? env.http_proxy;
1614
+ }
1615
+ function matchesNoProxy(host, noProxy) {
1616
+ if (noProxy === void 0 || noProxy.length === 0) {
1617
+ return false;
1618
+ }
1619
+ const normalized = host.toLowerCase();
1620
+ for (const raw of noProxy.split(",")) {
1621
+ const pattern = raw.trim().toLowerCase();
1622
+ if (pattern.length === 0) {
1623
+ continue;
1624
+ }
1625
+ if (pattern === "*") {
1626
+ return true;
1627
+ }
1628
+ const stripped = pattern.startsWith(".") ? pattern.slice(1) : pattern;
1629
+ if (normalized === stripped || normalized.endsWith(`.${stripped}`)) {
1630
+ return true;
1631
+ }
1632
+ }
1633
+ return false;
1634
+ }
1635
+ function resolveProxy(targetUrl, env) {
1636
+ const proxyUrl = pickProxyEnv(env, targetUrl.protocol);
1637
+ if (proxyUrl === void 0 || proxyUrl.length === 0) {
1638
+ return { dispatcher: void 0, proxyUrl: void 0 };
1639
+ }
1640
+ if (matchesNoProxy(targetUrl.hostname, env.NO_PROXY ?? env.no_proxy)) {
1641
+ return { dispatcher: void 0, proxyUrl: void 0 };
1642
+ }
1643
+ return { dispatcher: new ProxyAgent(proxyUrl), proxyUrl };
1644
+ }
1645
+ function isLocalHost(host) {
1646
+ return LOCAL_HOSTS.has(host.toLowerCase());
1647
+ }
1648
+ function sanitizeUrl(raw) {
1649
+ try {
1650
+ const parsed = new URL(raw);
1651
+ parsed.username = "";
1652
+ parsed.password = "";
1653
+ parsed.search = "";
1654
+ parsed.hash = "";
1655
+ return parsed.toString();
1656
+ } catch {
1657
+ return raw.split("?")[0]?.split("#")[0] ?? raw;
1658
+ }
1659
+ }
1660
+ function rateLimitError(url, retryAfter) {
1661
+ const safeUrl = sanitizeUrl(url);
1662
+ return usageError("GitHub rate limit exceeded.", {
1663
+ code: "provision_rate_limited",
1664
+ diagnostics: [
1665
+ `URL: ${safeUrl}`,
1666
+ retryAfter !== null && retryAfter.length > 0 ? `Retry after epoch ${retryAfter}.` : "Retry-After header not provided.",
1667
+ "Set `GITHUB_TOKEN` to raise the GitHub API rate limit."
1668
+ ],
1669
+ data: { url: safeUrl, status: 403, retryAfter: retryAfter ?? void 0 }
1670
+ });
1671
+ }
1672
+ function networkError(url, status, statusText) {
1673
+ const safeUrl = sanitizeUrl(url);
1674
+ return usageError("Adapter download failed.", {
1675
+ code: "provision_network_error",
1676
+ diagnostics: [`URL: ${safeUrl}`, `HTTP ${status} ${statusText}`],
1677
+ data: { url: safeUrl, status, statusText }
1678
+ });
1679
+ }
1680
+ function networkCauseError(url, code, message, cause) {
1681
+ const safeUrl = sanitizeUrl(url);
1682
+ return usageError("Adapter download failed.", {
1683
+ code: "provision_network_error",
1684
+ diagnostics: [`URL: ${safeUrl}`, code !== void 0 ? `Cause: ${code}` : `Cause: ${message}`],
1685
+ data: { url: safeUrl, causeCode: code },
1686
+ cause
1687
+ });
1688
+ }
1689
+ function proxyError(url, proxyUrl, code, message, cause) {
1690
+ const safeUrl = sanitizeUrl(url);
1691
+ const safeProxy = sanitizeUrl(proxyUrl);
1692
+ return usageError("Adapter download failed through configured proxy.", {
1693
+ code: "provision_proxy_error",
1694
+ diagnostics: [
1695
+ `URL: ${safeUrl}`,
1696
+ `Proxy: ${safeProxy}`,
1697
+ code !== void 0 ? `Cause: ${code}` : `Cause: ${message}`,
1698
+ "Verify `HTTPS_PROXY` is correct or set `NO_PROXY=github.com` to bypass."
1699
+ ],
1700
+ data: { url: safeUrl, proxyUrl: safeProxy, causeCode: code },
1701
+ cause
1702
+ });
1703
+ }
1704
+ function extractCause(error) {
1705
+ if (error instanceof Error && "cause" in error) {
1706
+ const cause = error.cause;
1707
+ if (cause !== void 0 && cause !== null && typeof cause === "object") {
1708
+ const codeValue = cause.code;
1709
+ const messageValue = cause.message;
1710
+ const result = {};
1711
+ if (typeof codeValue === "string") {
1712
+ result.code = codeValue;
1713
+ }
1714
+ if (typeof messageValue === "string") {
1715
+ result.message = messageValue;
1716
+ }
1717
+ return result;
1718
+ }
1719
+ }
1720
+ return void 0;
1721
+ }
1722
+ async function downloadToFile(options) {
1723
+ const env = options.env ?? process.env;
1724
+ let target;
1725
+ try {
1726
+ target = new URL(options.url);
1727
+ } catch {
1728
+ throw usageError("Invalid download URL.", {
1729
+ code: "provision_network_error",
1730
+ diagnostics: [`URL: ${sanitizeUrl(options.url)}`, "URL could not be parsed."],
1731
+ data: { url: sanitizeUrl(options.url) }
1732
+ });
1733
+ }
1734
+ if (target.protocol !== "https:" && !(target.protocol === "http:" && isLocalHost(target.hostname))) {
1735
+ throw usageError("Refusing non-HTTPS download URL.", {
1736
+ code: "provision_network_error",
1737
+ diagnostics: [`URL: ${sanitizeUrl(options.url)}`, "Downloads must use https:// (http://localhost is allowed for tests)."],
1738
+ data: { url: sanitizeUrl(options.url) }
1739
+ });
1740
+ }
1741
+ const { dispatcher, proxyUrl } = resolveProxy(target, env);
1742
+ await fs3.mkdir(path6.dirname(options.destPath), { recursive: true });
1743
+ let response;
1744
+ try {
1745
+ response = await fetch(options.url, dispatcher !== void 0 ? { dispatcher } : void 0);
1746
+ } catch (error) {
1747
+ const cause = extractCause(error);
1748
+ const message = error instanceof Error ? error.message : String(error);
1749
+ if (proxyUrl !== void 0) {
1750
+ throw proxyError(options.url, proxyUrl, cause?.code, cause?.message ?? message, error);
1751
+ }
1752
+ throw networkCauseError(options.url, cause?.code, cause?.message ?? message, error);
1753
+ }
1754
+ if (!response.ok) {
1755
+ if (response.status === 403 && response.headers.get("x-ratelimit-remaining") === "0") {
1756
+ throw rateLimitError(options.url, response.headers.get("x-ratelimit-reset"));
1757
+ }
1758
+ throw networkError(options.url, response.status, response.statusText);
1759
+ }
1760
+ if (response.body === null) {
1761
+ throw usageError("Adapter download returned an empty body.", {
1762
+ code: "provision_network_error",
1763
+ diagnostics: [`URL: ${sanitizeUrl(options.url)}`],
1764
+ data: { url: sanitizeUrl(options.url) }
1765
+ });
1766
+ }
1767
+ const contentLengthHeader = response.headers.get("content-length");
1768
+ const totalBytes = contentLengthHeader !== null ? Number.parseInt(contentLengthHeader, 10) : void 0;
1769
+ const total = Number.isFinite(totalBytes) ? totalBytes : void 0;
1770
+ let bytesRead = 0;
1771
+ const onProgress = options.onProgress;
1772
+ const body = Readable.fromWeb(response.body);
1773
+ if (onProgress !== void 0) {
1774
+ body.on("data", (chunk) => {
1775
+ bytesRead += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
1776
+ onProgress(bytesRead, total);
1777
+ });
1778
+ }
1779
+ try {
1780
+ await pipeline(body, createWriteStream3(options.destPath));
1781
+ } catch (error) {
1782
+ await fs3.rm(options.destPath, { force: true }).catch(() => void 0);
1783
+ const message = error instanceof Error ? error.message : String(error);
1784
+ throw usageError("Adapter download stream failed.", {
1785
+ code: "provision_network_error",
1786
+ diagnostics: [`URL: ${sanitizeUrl(options.url)}`, message],
1787
+ data: { url: sanitizeUrl(options.url) },
1788
+ cause: error
1789
+ });
1790
+ }
1791
+ }
1792
+
1793
+ // src/adapters/provision/extractTarGz.ts
1794
+ import { createReadStream } from "fs";
1795
+ import * as tar from "tar";
1796
+ async function extractTarGz(archivePath, destDir, options = {}) {
1797
+ await new Promise((resolve, reject) => {
1798
+ const source = createReadStream(archivePath);
1799
+ const extract = tar.x({
1800
+ cwd: destDir,
1801
+ strict: true,
1802
+ strip: options.strip ?? 0
1803
+ });
1804
+ source.on("error", reject);
1805
+ extract.on("error", reject);
1806
+ extract.on("finish", resolve);
1807
+ source.pipe(extract);
1808
+ }).catch((error) => {
1809
+ const message = error instanceof Error ? error.message : String(error);
1810
+ throw usageError("Archive extraction failed.", {
1811
+ code: "provision_extract_failed",
1812
+ diagnostics: [archivePath, message],
1813
+ data: { archivePath },
1814
+ cause: error
1815
+ });
1816
+ });
1817
+ }
1818
+
1819
+ // src/adapters/provision/lock.ts
1820
+ import path7 from "path";
1821
+ import { promises as fs4 } from "fs";
1822
+ import * as lockfile from "proper-lockfile";
1823
+ var PRODUCTION_RETRY = {
1824
+ retries: 60,
1825
+ minTimeout: 500,
1826
+ maxTimeout: 2e3,
1827
+ factor: 1
1828
+ };
1829
+ var STALE_MS = 5 * 60 * 1e3;
1830
+ var CACHE_UNWRITABLE_CODES2 = /* @__PURE__ */ new Set(["EACCES", "EROFS", "ENOSPC", "EPERM"]);
1831
+ function getErrnoCode(error) {
1832
+ if (error instanceof Error && "code" in error) {
1833
+ const code = error.code;
1834
+ if (typeof code === "string") {
1835
+ return code;
1836
+ }
1837
+ }
1838
+ return void 0;
1839
+ }
1840
+ function cacheUnwritableError2(adaptersDir, errnoCode, adapterId) {
1841
+ return usageError("Adapter cache directory is not writable.", {
1842
+ code: "provision_cache_unwritable",
1843
+ diagnostics: [
1844
+ `Adapter cache: ${adaptersDir}`,
1845
+ `Filesystem error: ${errnoCode}`,
1846
+ "Override with `DAP_CLI_ADAPTERS_DIR=<writable-path>`."
1847
+ ],
1848
+ data: { adaptersDir, errnoCode, adapterId }
1849
+ });
1850
+ }
1851
+ function resolveRetry(options) {
1852
+ if (options?.retryOverride !== void 0) {
1853
+ return options.retryOverride;
1854
+ }
1855
+ const env = process.env.DAP_CLI_LOCK_RETRY_OVERRIDE;
1856
+ if (env !== void 0 && env.length > 0) {
1857
+ try {
1858
+ const parsed = JSON.parse(env);
1859
+ if (parsed !== null && typeof parsed === "object") {
1860
+ const candidate = parsed;
1861
+ const retries = candidate.retries;
1862
+ const minTimeout = candidate.minTimeout;
1863
+ const maxTimeout = candidate.maxTimeout;
1864
+ const factor = candidate.factor;
1865
+ if (typeof retries === "number" && typeof minTimeout === "number" && typeof maxTimeout === "number" && typeof factor === "number") {
1866
+ return { retries, minTimeout, maxTimeout, factor };
1867
+ }
1868
+ }
1869
+ } catch {
1870
+ }
1871
+ }
1872
+ return PRODUCTION_RETRY;
1873
+ }
1874
+ function lockTimeoutError(adapterId, sentinel) {
1875
+ return usageError("Timed out waiting for adapter install lock.", {
1876
+ code: "provision_lock_timeout",
1877
+ diagnostics: [
1878
+ `Adapter: ${adapterId}`,
1879
+ `Lock sentinel: ${sentinel}`,
1880
+ `Another dap-cli process may be installing; if none, delete ${sentinel} and retry.`
1881
+ ],
1882
+ data: { adapterId, sentinel }
1883
+ });
1884
+ }
1885
+ async function withAdapterLock(adaptersDir, adapterId, fn, options) {
1886
+ try {
1887
+ await fs4.mkdir(adaptersDir, { recursive: true });
1888
+ } catch (error) {
1889
+ const errnoCode = getErrnoCode(error);
1890
+ if (errnoCode !== void 0 && CACHE_UNWRITABLE_CODES2.has(errnoCode)) {
1891
+ throw cacheUnwritableError2(adaptersDir, errnoCode, adapterId);
1892
+ }
1893
+ throw error;
1894
+ }
1895
+ const sentinel = path7.join(adaptersDir, `.${adapterId}.lock-target`);
1896
+ try {
1897
+ await fs4.writeFile(sentinel, "", { flag: "a" });
1898
+ } catch (error) {
1899
+ const errnoCode = getErrnoCode(error);
1900
+ if (errnoCode !== void 0 && CACHE_UNWRITABLE_CODES2.has(errnoCode)) {
1901
+ throw cacheUnwritableError2(adaptersDir, errnoCode, adapterId);
1902
+ }
1903
+ throw error;
1904
+ }
1905
+ const retry = resolveRetry(options);
1906
+ let release;
1907
+ try {
1908
+ release = await lockfile.lock(sentinel, {
1909
+ stale: STALE_MS,
1910
+ realpath: false,
1911
+ retries: { ...retry }
1912
+ });
1913
+ } catch (error) {
1914
+ const errnoCode = getErrnoCode(error);
1915
+ if (errnoCode === "ELOCKED" || errnoCode === "EEXIST") {
1916
+ throw lockTimeoutError(adapterId, sentinel);
1917
+ }
1918
+ if (errnoCode !== void 0 && CACHE_UNWRITABLE_CODES2.has(errnoCode)) {
1919
+ throw cacheUnwritableError2(adaptersDir, errnoCode, adapterId);
1920
+ }
1921
+ throw error;
1922
+ }
1923
+ try {
1924
+ return await fn();
1925
+ } finally {
1926
+ if (release !== void 0) {
1927
+ await release().catch(() => void 0);
1928
+ }
1929
+ }
1930
+ }
1931
+
1932
+ // src/adapters/provision/consent.ts
1933
+ import path8 from "path";
1934
+ import { promises as fs5 } from "fs";
1935
+ function markerPath(adaptersDir, adapterId, version) {
1936
+ return path8.join(adaptersDir, adapterId, `.consent-${version}`);
1937
+ }
1938
+ async function hasConsentMarker(adaptersDir, adapterId, version) {
1939
+ try {
1940
+ await fs5.stat(markerPath(adaptersDir, adapterId, version));
1941
+ return true;
1942
+ } catch {
1943
+ return false;
1944
+ }
1945
+ }
1946
+ async function writeConsentMarker(adaptersDir, adapterId, version) {
1947
+ const filePath = markerPath(adaptersDir, adapterId, version);
1948
+ await fs5.mkdir(path8.dirname(filePath), { recursive: true });
1949
+ await fs5.writeFile(filePath, `${(/* @__PURE__ */ new Date()).toISOString()}
1950
+ `, { flag: "w" });
1951
+ }
1952
+
1953
+ // src/adapters/provision/checksums.ts
1954
+ var JS_DEBUG_VERSION = "1.117.0";
1955
+ var DEBUGPY_VERSION = "1.8.20";
1956
+ var DELVE_VERSION = "v1.26.3";
1957
+ var JS_DEBUG_CHECKSUMS = {
1958
+ "1.117.0": "ad8d04ede9d4b75cc290fd5438a65047a06f786d04f604b6112485b36f090772"
1959
+ };
1960
+ var DELVE_CHECKSUMS = {
1961
+ "v1.26.3": {
1962
+ darwin_arm64: "7f28483a42f0a911f29b236aa40d24d7099f1b0ec54c56c4d439a6903d478a3d",
1963
+ darwin_amd64: "6827a438473167a1e0805b4546e5bf2d53401530f694deb35e41c6e7b46e27c8",
1964
+ linux_amd64: "cdd4d6b2a638d8f26468d82a76b766df594641490bea566629305d90fbccc06e",
1965
+ linux_arm64: "5b03fd74895d676c4435bec1aade0863be1489a4be1bb5c9269c6ef389bf5d2d",
1966
+ windows_amd64: "f9e15b8f3628e4c7bfe481011bea458df754d0e75c6ff4ab01c71294165950fd"
1967
+ }
1968
+ };
1969
+
1970
+ // src/adapters/provision/jsDebug.ts
1971
+ var ADAPTER_ID = "js-debug";
1972
+ var ENTRYPOINTS = ["src/dapDebugServer.js", "src/bootloader.js"];
1973
+ var PACKAGE_BOUNDARY = '{"type":"commonjs"}\n';
1974
+ function resolveBaseUrl(env) {
1975
+ const override = env.DAP_CLI_PROVISION_RELEASE_BASE_URL;
1976
+ return override !== void 0 && override.length > 0 ? override : "https://github.com";
1977
+ }
1978
+ function downloadUrl(env) {
1979
+ return `${resolveBaseUrl(env)}/microsoft/vscode-js-debug/releases/download/v${JS_DEBUG_VERSION}/js-debug-dap-v${JS_DEBUG_VERSION}.tar.gz`;
1980
+ }
1981
+ async function fileSha256(filePath) {
1982
+ return await new Promise((resolve, reject) => {
1983
+ const hash = createHash("sha256");
1984
+ const stream = createReadStream2(filePath);
1985
+ stream.on("error", reject);
1986
+ stream.on("data", (chunk) => hash.update(chunk));
1987
+ stream.on("end", () => resolve(hash.digest("hex")));
1988
+ });
1989
+ }
1990
+ async function entrypointsExist(installRoot) {
1991
+ for (const rel of ENTRYPOINTS) {
1992
+ try {
1993
+ await fs6.access(path9.join(installRoot, rel));
1994
+ } catch {
1995
+ return false;
1996
+ }
1997
+ }
1998
+ return true;
1999
+ }
2000
+ async function provisionJsDebug(ctx) {
2001
+ const installRoot = path9.join(ctx.adaptersDir, ADAPTER_ID);
2002
+ const entrypoint = path9.join(installRoot, "src", "dapDebugServer.js");
2003
+ if (await hasConsentMarker(ctx.adaptersDir, ADAPTER_ID, JS_DEBUG_VERSION) && await entrypointsExist(installRoot)) {
2004
+ return {
2005
+ adapterId: "js-debug",
2006
+ version: JS_DEBUG_VERSION,
2007
+ installRoot,
2008
+ entrypoint,
2009
+ fromCache: true
2010
+ };
2011
+ }
2012
+ const url = downloadUrl(ctx.env);
2013
+ const expectedSha = JS_DEBUG_CHECKSUMS[JS_DEBUG_VERSION];
2014
+ if (expectedSha === void 0) {
2015
+ throw usageError(`No SHA-256 checksum recorded for js-debug v${JS_DEBUG_VERSION}.`, {
2016
+ code: "provision_checksum_mismatch",
2017
+ diagnostics: [
2018
+ `Adapter: js-debug ${JS_DEBUG_VERSION}`,
2019
+ "Edit src/adapters/provision/checksums.ts and add the hash for this version.",
2020
+ "Re-run setup or report at https://github.com/roblourens/dap-cli/issues if persistent."
2021
+ ],
2022
+ data: {
2023
+ adapterId: "js-debug",
2024
+ version: JS_DEBUG_VERSION
2025
+ }
2026
+ });
2027
+ }
2028
+ await confirm({
2029
+ assumeYes: ctx.assumeYes,
2030
+ question: `Install vscode-js-debug ${JS_DEBUG_VERSION} into ${installRoot} (~10MB)?`,
2031
+ details: [`Source: ${url}`],
2032
+ ...ctx.stdin === void 0 ? {} : { stdin: ctx.stdin },
2033
+ ...ctx.stderr === void 0 ? {} : { stderr: ctx.stderr }
2034
+ });
2035
+ await withAdapterLock(ctx.adaptersDir, ADAPTER_ID, async () => {
2036
+ if (await hasConsentMarker(ctx.adaptersDir, ADAPTER_ID, JS_DEBUG_VERSION) && await entrypointsExist(installRoot)) {
2037
+ return;
2038
+ }
2039
+ await atomicInstall({
2040
+ adaptersDir: ctx.adaptersDir,
2041
+ adapterId: ADAPTER_ID,
2042
+ expectedEntrypoints: ENTRYPOINTS,
2043
+ populate: async (stagingDir) => {
2044
+ const archivePath = path9.join(
2045
+ ctx.adaptersDir,
2046
+ `.${ADAPTER_ID}.archive.${process.pid}.${randomBytes2(4).toString("hex")}.tar.gz`
2047
+ );
2048
+ try {
2049
+ await downloadToFile({ url, destPath: archivePath, env: ctx.env });
2050
+ const actualSha = await fileSha256(archivePath);
2051
+ if (actualSha !== expectedSha) {
2052
+ throw usageError("Adapter download failed SHA-256 verification.", {
2053
+ code: "provision_checksum_mismatch",
2054
+ diagnostics: [
2055
+ `URL: ${url}`,
2056
+ `Expected: ${expectedSha}`,
2057
+ `Actual: ${actualSha}`,
2058
+ "Re-run setup or report at https://github.com/roblourens/dap-cli/issues if persistent."
2059
+ ],
2060
+ data: {
2061
+ adapterId: "js-debug",
2062
+ version: JS_DEBUG_VERSION,
2063
+ url,
2064
+ expectedSha,
2065
+ actualSha
2066
+ }
2067
+ });
2068
+ }
2069
+ await extractTarGz(archivePath, stagingDir, { strip: 1 });
2070
+ await fs6.writeFile(
2071
+ path9.join(stagingDir, "package.json"),
2072
+ PACKAGE_BOUNDARY,
2073
+ "utf8"
2074
+ );
2075
+ } finally {
2076
+ await fs6.rm(archivePath, { force: true }).catch(() => void 0);
2077
+ }
2078
+ }
2079
+ });
2080
+ await writeConsentMarker(ctx.adaptersDir, ADAPTER_ID, JS_DEBUG_VERSION);
2081
+ });
2082
+ return {
2083
+ adapterId: "js-debug",
2084
+ version: JS_DEBUG_VERSION,
2085
+ installRoot,
2086
+ entrypoint,
2087
+ fromCache: false
2088
+ };
2089
+ }
2090
+
2091
+ // src/adapters/provision/debugpy.ts
2092
+ import path10 from "path";
2093
+ import { promises as fs7 } from "fs";
2094
+ import { execFile } from "child_process";
2095
+ import { promisify } from "util";
2096
+ var execFileAsync = promisify(execFile);
2097
+ function venvPythonRel() {
2098
+ return process.platform === "win32" ? path10.join("venv", "Scripts", "python.exe") : path10.join("venv", "bin", "python");
2099
+ }
2100
+ function venvPipRel() {
2101
+ return process.platform === "win32" ? path10.join("venv", "Scripts", "pip.exe") : path10.join("venv", "bin", "pip");
2102
+ }
2103
+ function tail(text, max = 2048) {
2104
+ if (text.length <= max) {
2105
+ return text;
2106
+ }
2107
+ return `...${text.slice(text.length - max)}`;
2108
+ }
2109
+ async function exists(p) {
2110
+ try {
2111
+ await fs7.access(p);
2112
+ return true;
2113
+ } catch {
2114
+ return false;
2115
+ }
2116
+ }
2117
+ async function provisionDebugpy(ctx) {
2118
+ const { env, assumeYes, adaptersDir, stdin, stderr } = ctx;
2119
+ const installRoot = path10.join(adaptersDir, "debugpy");
2120
+ const entrypoint = path10.join(installRoot, venvPythonRel());
2121
+ if (await hasConsentMarker(adaptersDir, "debugpy", DEBUGPY_VERSION) && await exists(entrypoint)) {
2122
+ return {
2123
+ adapterId: "debugpy",
2124
+ version: DEBUGPY_VERSION,
2125
+ installRoot,
2126
+ entrypoint,
2127
+ fromCache: true
2128
+ };
2129
+ }
2130
+ await confirm({
2131
+ assumeYes,
2132
+ question: `Install debugpy ${DEBUGPY_VERSION} into a venv at ${installRoot}/ (~6MB)?`,
2133
+ details: [
2134
+ "Requires python3 (>=3.8) on PATH.",
2135
+ "Creates an isolated venv and pip-installs debugpy. The venv python becomes the adapter command."
2136
+ ],
2137
+ ...stdin === void 0 ? {} : { stdin },
2138
+ ...stderr === void 0 ? {} : { stderr }
2139
+ });
2140
+ await withAdapterLock(adaptersDir, "debugpy", async () => {
2141
+ if (await hasConsentMarker(adaptersDir, "debugpy", DEBUGPY_VERSION) && await exists(entrypoint)) {
2142
+ return;
2143
+ }
2144
+ const python3 = env.DAP_CLI_PROVISION_PYTHON3 ?? "python3";
2145
+ try {
2146
+ await execFileAsync(python3, ["--version"]);
2147
+ } catch (error) {
2148
+ const message = error.stderr ?? error.message ?? "unknown error";
2149
+ throw usageError("python3 is not available on PATH.", {
2150
+ code: "provision_python3_missing",
2151
+ diagnostics: [
2152
+ "Install Python 3.8+ and ensure `python3` is on PATH.",
2153
+ "macOS: `brew install python`",
2154
+ "Ubuntu/Debian: `apt install python3 python3-venv`",
2155
+ 'Windows: install from python.org and check "Add to PATH".',
2156
+ `Underlying error: ${tail(message, 512)}`
2157
+ ],
2158
+ data: { python3 },
2159
+ cause: error
2160
+ });
2161
+ }
2162
+ await atomicInstall({
2163
+ adaptersDir,
2164
+ adapterId: "debugpy",
2165
+ expectedEntrypoints: [venvPythonRel()],
2166
+ populate: async (stagingDir) => {
2167
+ const venvDir = path10.join(stagingDir, "venv");
2168
+ try {
2169
+ await execFileAsync(python3, ["-m", "venv", venvDir]);
2170
+ } catch (error) {
2171
+ const stderrText = error.stderr ?? "";
2172
+ throw usageError("Failed to create Python virtual environment.", {
2173
+ code: "provision_python3_venv_unavailable",
2174
+ diagnostics: [
2175
+ "The `python3 -m venv` command failed. On Debian/Ubuntu install `python3-venv`:",
2176
+ " sudo apt install python3-venv",
2177
+ "On other distros ensure the standard library `venv` and `ensurepip` modules are present.",
2178
+ `stderr tail: ${tail(stderrText)}`
2179
+ ],
2180
+ data: { python3 },
2181
+ cause: error
2182
+ });
2183
+ }
2184
+ const pipPath = path10.join(stagingDir, venvPipRel());
2185
+ try {
2186
+ await execFileAsync(pipPath, [
2187
+ "install",
2188
+ "--no-warn-script-location",
2189
+ "--disable-pip-version-check",
2190
+ `debugpy==${DEBUGPY_VERSION}`
2191
+ ]);
2192
+ } catch (error) {
2193
+ const stderrText = error.stderr ?? "";
2194
+ throw usageError(`Failed to install debugpy==${DEBUGPY_VERSION} via pip.`, {
2195
+ code: "provision_pip_install_failed",
2196
+ diagnostics: [
2197
+ "pip install failed. Common causes: no network access, restricted PyPI mirror, missing build tools.",
2198
+ "Workaround: set `PIP_INDEX_URL` to your mirror, or pre-install debugpy into the venv and re-run.",
2199
+ `Underlying pip command: ${pipPath} install debugpy==${DEBUGPY_VERSION}`,
2200
+ `stderr tail: ${tail(stderrText)}`
2201
+ ],
2202
+ data: { pipPath, version: DEBUGPY_VERSION },
2203
+ cause: error
2204
+ });
2205
+ }
2206
+ }
2207
+ });
2208
+ await writeConsentMarker(adaptersDir, "debugpy", DEBUGPY_VERSION);
2209
+ });
2210
+ return {
2211
+ adapterId: "debugpy",
2212
+ version: DEBUGPY_VERSION,
2213
+ installRoot,
2214
+ entrypoint,
2215
+ fromCache: false
2216
+ };
2217
+ }
2218
+
2219
+ // src/adapters/provision/delve.ts
2220
+ import path12 from "path";
2221
+ import { promises as fs9 } from "fs";
2222
+ import { createHash as createHash2 } from "crypto";
2223
+ import { randomBytes as randomBytes3 } from "crypto";
2224
+
2225
+ // src/adapters/provision/extractZip.ts
2226
+ import { promises as fs8, createWriteStream as createWriteStream4 } from "fs";
2227
+ import path11 from "path";
2228
+ import yauzl from "yauzl";
2229
+ var SYMLINK_MODE = 40960;
2230
+ var UNIX_MODE_MASK = 61440;
2231
+ function isUnsafeFileName(fileName) {
2232
+ if (path11.isAbsolute(fileName)) {
2233
+ return true;
2234
+ }
2235
+ if (/^[A-Za-z]:[\\/]/.test(fileName)) {
2236
+ return true;
2237
+ }
2238
+ return fileName.split(/[\\/]/).includes("..");
2239
+ }
2240
+ function isSymlinkEntry(entry) {
2241
+ const unixMode = entry.externalFileAttributes >>> 16 & UNIX_MODE_MASK;
2242
+ return unixMode === SYMLINK_MODE;
2243
+ }
2244
+ function unsafeEntryError(archivePath, fileName) {
2245
+ return usageError("Archive contains unsafe entry path.", {
2246
+ code: "provision_extract_failed",
2247
+ diagnostics: [`Entry: ${fileName}`, `Archive: ${archivePath}`],
2248
+ data: { archivePath, entry: fileName }
2249
+ });
2250
+ }
2251
+ function wrapExtractError(archivePath, error) {
2252
+ if (error instanceof CliError) {
2253
+ return error;
2254
+ }
2255
+ const message = error instanceof Error ? error.message : String(error);
2256
+ return usageError("Archive extraction failed.", {
2257
+ code: "provision_extract_failed",
2258
+ diagnostics: [archivePath, message],
2259
+ data: { archivePath },
2260
+ cause: error
2261
+ });
2262
+ }
2263
+ async function extractZip(archivePath, destDir) {
2264
+ await fs8.mkdir(destDir, { recursive: true });
2265
+ await new Promise((resolve, reject) => {
2266
+ yauzl.open(archivePath, { lazyEntries: true }, (openErr, zip) => {
2267
+ if (openErr || zip === void 0) {
2268
+ reject(openErr ?? new Error("zip open failed"));
2269
+ return;
2270
+ }
2271
+ const handleError = (err) => {
2272
+ try {
2273
+ zip.close();
2274
+ } catch {
2275
+ }
2276
+ reject(err instanceof Error ? err : new Error(String(err)));
2277
+ };
2278
+ zip.on("error", handleError);
2279
+ zip.on("end", () => {
2280
+ resolve();
2281
+ });
2282
+ zip.on("entry", (entry) => {
2283
+ if (isUnsafeFileName(entry.fileName)) {
2284
+ handleError(unsafeEntryError(archivePath, entry.fileName));
2285
+ return;
2286
+ }
2287
+ if (isSymlinkEntry(entry)) {
2288
+ handleError(unsafeEntryError(archivePath, entry.fileName));
2289
+ return;
2290
+ }
2291
+ const dest = path11.join(destDir, entry.fileName);
2292
+ if (entry.fileName.endsWith("/")) {
2293
+ fs8.mkdir(dest, { recursive: true }).then(() => {
2294
+ zip.readEntry();
2295
+ }).catch(handleError);
2296
+ return;
2297
+ }
2298
+ fs8.mkdir(path11.dirname(dest), { recursive: true }).then(() => {
2299
+ zip.openReadStream(entry, (readErr, readStream) => {
2300
+ if (readErr || readStream === void 0) {
2301
+ handleError(readErr ?? new Error("zip read failed"));
2302
+ return;
2303
+ }
2304
+ const out = createWriteStream4(dest);
2305
+ out.on("finish", () => {
2306
+ zip.readEntry();
2307
+ });
2308
+ out.on("error", handleError);
2309
+ readStream.on("error", handleError);
2310
+ readStream.pipe(out);
2311
+ });
2312
+ }).catch(handleError);
2313
+ });
2314
+ zip.readEntry();
2315
+ });
2316
+ }).catch((error) => {
2317
+ throw wrapExtractError(archivePath, error);
2318
+ });
2319
+ }
2320
+
2321
+ // src/adapters/provision/delve.ts
2322
+ var DEFAULT_RELEASE_BASE_URL = "https://github.com";
2323
+ function bareVersion() {
2324
+ return DELVE_VERSION.startsWith("v") ? DELVE_VERSION.slice(1) : DELVE_VERSION;
2325
+ }
2326
+ function resolveDelveAsset(env) {
2327
+ const override = env.DAP_CLI_PROVISION_DELVE_PLATFORM_OVERRIDE;
2328
+ const key = override !== void 0 && override.length > 0 ? override : `${process.platform}_${process.arch}`;
2329
+ const matrix = {
2330
+ "darwin_arm64": "darwin_arm64",
2331
+ "darwin_x64": "darwin_amd64",
2332
+ "linux_x64": "linux_amd64",
2333
+ "linux_arm64": "linux_arm64",
2334
+ "win32_x64": "windows_amd64"
2335
+ };
2336
+ const platformKey = matrix[key];
2337
+ if (platformKey === void 0) {
2338
+ throw usageError(`Delve provisioning does not support platform '${key}'.`, {
2339
+ code: "provision_arch_unsupported",
2340
+ diagnostics: [
2341
+ `Detected platform: ${key}`,
2342
+ "Supported platforms: darwin_arm64, darwin_x64, linux_x64, linux_arm64, win32_x64.",
2343
+ "Install `dlv` manually on PATH or provision a compatible binary."
2344
+ ],
2345
+ data: {
2346
+ adapterId: "delve",
2347
+ detected: key,
2348
+ supported: ["darwin_arm64", "darwin_x64", "linux_x64", "linux_arm64", "win32_x64"]
2349
+ }
2350
+ });
2351
+ }
2352
+ const archiveKind = platformKey === "windows_amd64" ? "zip" : "tar.gz";
2353
+ const ext = archiveKind === "zip" ? "zip" : "tar.gz";
2354
+ const archiveName = `dlv_${bareVersion()}_${platformKey}.${ext}`;
2355
+ const executableName = platformKey === "windows_amd64" ? "dlv.exe" : "dlv";
2356
+ return { platformKey, archiveName, archiveKind, executableName };
2357
+ }
2358
+ async function exists2(p) {
2359
+ try {
2360
+ await fs9.access(p);
2361
+ return true;
2362
+ } catch {
2363
+ return false;
2364
+ }
2365
+ }
2366
+ async function computeSha256(filePath) {
2367
+ const buffer = await fs9.readFile(filePath);
2368
+ return createHash2("sha256").update(buffer).digest("hex");
2369
+ }
2370
+ async function provisionDelve(ctx) {
2371
+ const { env, assumeYes, adaptersDir, stdin, stderr } = ctx;
2372
+ const asset = resolveDelveAsset(env);
2373
+ const installRoot = path12.join(adaptersDir, "delve");
2374
+ const entrypoint = path12.join(installRoot, asset.executableName);
2375
+ const expectedSha = DELVE_CHECKSUMS[DELVE_VERSION]?.[asset.platformKey];
2376
+ if (expectedSha === void 0) {
2377
+ throw usageError(`No pinned SHA-256 for delve ${DELVE_VERSION} on ${asset.platformKey}.`, {
2378
+ code: "provision_checksum_mismatch",
2379
+ diagnostics: [
2380
+ `Adapter: delve ${DELVE_VERSION} (${asset.platformKey})`,
2381
+ "src/adapters/provision/checksums.ts must list a checksum for every supported platform.",
2382
+ "Re-run setup or report at https://github.com/roblourens/dap-cli/issues if persistent."
2383
+ ],
2384
+ data: {
2385
+ adapterId: "delve",
2386
+ version: DELVE_VERSION,
2387
+ platform: asset.platformKey
2388
+ }
2389
+ });
2390
+ }
2391
+ if (await hasConsentMarker(adaptersDir, "delve", DELVE_VERSION) && await exists2(entrypoint)) {
2392
+ return {
2393
+ adapterId: "delve",
2394
+ version: DELVE_VERSION,
2395
+ installRoot,
2396
+ entrypoint,
2397
+ fromCache: true
2398
+ };
2399
+ }
2400
+ await confirm({
2401
+ assumeYes,
2402
+ question: `Install delve ${DELVE_VERSION} into ${installRoot}/ (~10MB)?`,
2403
+ details: [
2404
+ `Downloads the official release asset ${asset.archiveName} from github.com/go-delve/delve.`,
2405
+ "The archive SHA-256 is verified against an embedded checksum before installation."
2406
+ ],
2407
+ ...stdin === void 0 ? {} : { stdin },
2408
+ ...stderr === void 0 ? {} : { stderr }
2409
+ });
2410
+ await withAdapterLock(adaptersDir, "delve", async () => {
2411
+ if (await hasConsentMarker(adaptersDir, "delve", DELVE_VERSION) && await exists2(entrypoint)) {
2412
+ return;
2413
+ }
2414
+ const releaseBase = env.DAP_CLI_PROVISION_RELEASE_BASE_URL ?? DEFAULT_RELEASE_BASE_URL;
2415
+ const url = `${releaseBase}/go-delve/delve/releases/download/${DELVE_VERSION}/${asset.archiveName}`;
2416
+ const archivePath = path12.join(
2417
+ adaptersDir,
2418
+ `.delve.archive.${process.pid}.${randomBytes3(4).toString("hex")}.${asset.archiveKind === "zip" ? "zip" : "tar.gz"}`
2419
+ );
2420
+ try {
2421
+ await fs9.mkdir(adaptersDir, { recursive: true });
2422
+ await downloadToFile({ url, destPath: archivePath, env });
2423
+ const actualSha = await computeSha256(archivePath);
2424
+ if (actualSha !== expectedSha) {
2425
+ throw usageError("Delve archive failed SHA-256 verification.", {
2426
+ code: "provision_checksum_mismatch",
2427
+ diagnostics: [
2428
+ `URL: ${url}`,
2429
+ `Expected: ${expectedSha}`,
2430
+ `Actual: ${actualSha}`,
2431
+ "Re-run setup or report at https://github.com/roblourens/dap-cli/issues if persistent."
2432
+ ],
2433
+ data: {
2434
+ adapterId: "delve",
2435
+ version: DELVE_VERSION,
2436
+ url,
2437
+ expectedSha,
2438
+ actualSha
2439
+ }
2440
+ });
2441
+ }
2442
+ await atomicInstall({
2443
+ adaptersDir,
2444
+ adapterId: "delve",
2445
+ expectedEntrypoints: [asset.executableName],
2446
+ populate: async (stagingDir) => {
2447
+ if (asset.archiveKind === "zip") {
2448
+ await extractZip(archivePath, stagingDir);
2449
+ } else {
2450
+ await extractTarGz(archivePath, stagingDir);
2451
+ }
2452
+ if (process.platform !== "win32") {
2453
+ await fs9.chmod(path12.join(stagingDir, asset.executableName), 493);
2454
+ }
2455
+ }
2456
+ });
2457
+ await writeConsentMarker(adaptersDir, "delve", DELVE_VERSION);
2458
+ } finally {
2459
+ await fs9.rm(archivePath, { force: true }).catch(() => void 0);
2460
+ }
2461
+ });
2462
+ return {
2463
+ adapterId: "delve",
2464
+ version: DELVE_VERSION,
2465
+ installRoot,
2466
+ entrypoint,
2467
+ fromCache: false
2468
+ };
2469
+ }
2470
+
2471
+ // src/adapters/provision/index.ts
2472
+ async function provisionAdapter(id, ctx) {
2473
+ switch (id) {
2474
+ case "js-debug":
2475
+ return provisionJsDebug(ctx);
2476
+ case "debugpy":
2477
+ return provisionDebugpy(ctx);
2478
+ case "delve":
2479
+ return provisionDelve(ctx);
2480
+ }
2481
+ }
2482
+
2483
+ // src/adapters/builtins/jsDebug.ts
2484
+ async function createJsDebugDescriptor(jsDebugPath) {
2485
+ const dapServerPath = jsDebugPath ?? await resolveDefaultJsDebugPath();
1468
2486
  return {
1469
2487
  id: "js-debug",
1470
2488
  label: "JavaScript Debug Adapter (Node, Chrome, Electron)",
@@ -1488,26 +2506,35 @@ function applyJsDebugTraceDefaults(config, logDir) {
1488
2506
  ...record,
1489
2507
  trace: {
1490
2508
  stdio: false,
1491
- logFile: path5.join(logDir, `js-debug-trace-${Date.now()}.log`)
2509
+ logFile: path13.join(logDir, `js-debug-trace-${Date.now()}.log`)
1492
2510
  }
1493
2511
  };
1494
2512
  }
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;
2513
+ async function pathExists(candidate) {
2514
+ try {
2515
+ await fs10.access(candidate);
2516
+ return true;
2517
+ } catch {
2518
+ return false;
1503
2519
  }
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
- ]
2520
+ }
2521
+ async function resolveDefaultJsDebugPath() {
2522
+ const env = process.env;
2523
+ const adaptersDir = getDapCliAdaptersDir(env);
2524
+ const provisionedEntrypoint = path13.join(adaptersDir, "js-debug", "src", "dapDebugServer.js");
2525
+ const repoEntrypoint = path13.join(process.cwd(), "node_modules", "vscode-js-debug", "src", "dapDebugServer.js");
2526
+ if (await pathExists(provisionedEntrypoint)) {
2527
+ return provisionedEntrypoint;
2528
+ }
2529
+ if (await pathExists(repoEntrypoint)) {
2530
+ return repoEntrypoint;
2531
+ }
2532
+ const result = await provisionAdapter("js-debug", {
2533
+ env,
2534
+ assumeYes: resolveAssumeYes(void 0, env),
2535
+ adaptersDir
1510
2536
  });
2537
+ return result.entrypoint;
1511
2538
  }
1512
2539
 
1513
2540
  // src/protocol/dapClient.ts
@@ -1601,14 +2628,16 @@ function parseBody(body) {
1601
2628
 
1602
2629
  // src/protocol/dapClient.ts
1603
2630
  var DapResponseError = class extends Error {
1604
- constructor(command, requestSeq, message) {
2631
+ constructor(command, requestSeq, message, responseBody) {
1605
2632
  super(message);
1606
2633
  this.command = command;
1607
2634
  this.requestSeq = requestSeq;
2635
+ this.responseBody = responseBody;
1608
2636
  this.name = "DapResponseError";
1609
2637
  }
1610
2638
  command;
1611
2639
  requestSeq;
2640
+ responseBody;
1612
2641
  };
1613
2642
  var DapTransportClosedError = class extends Error {
1614
2643
  constructor(message = "DAP transport closed.") {
@@ -1736,7 +2765,7 @@ var DapClient = class {
1736
2765
  clearTimeout(pending.timeout);
1737
2766
  }
1738
2767
  if (!response.success) {
1739
- pending.reject(new DapResponseError(response.command, response.request_seq, response.message ?? `DAP request failed: ${response.command}`));
2768
+ pending.reject(new DapResponseError(response.command, response.request_seq, response.message ?? `DAP request failed: ${response.command}`, response.body));
1740
2769
  return;
1741
2770
  }
1742
2771
  pending.resolve(response.body);
@@ -2149,14 +3178,14 @@ function getDapGeneratedCommand(command) {
2149
3178
  }
2150
3179
 
2151
3180
  // src/sessions/session.ts
2152
- import { randomBytes } from "crypto";
3181
+ import { randomBytes as randomBytes4 } from "crypto";
2153
3182
  var REMOVABLE_LIFECYCLES = /* @__PURE__ */ new Set([
2154
3183
  "terminated",
2155
3184
  "disconnected",
2156
3185
  "failed"
2157
3186
  ]);
2158
3187
  function createSessionId() {
2159
- return `sess_${randomBytes(12).toString("base64url")}`;
3188
+ return `sess_${randomBytes4(12).toString("base64url")}`;
2160
3189
  }
2161
3190
  function projectSessionSummary(session) {
2162
3191
  const summary = {
@@ -2269,9 +3298,9 @@ function createAmbiguousSessionDiagnostics(targetId, matches) {
2269
3298
  }
2270
3299
 
2271
3300
  // src/sessions/sessionStore.ts
2272
- import { promises as fs2 } from "fs";
3301
+ import { promises as fs11 } from "fs";
2273
3302
  import { randomUUID } from "crypto";
2274
- import path6 from "path";
3303
+ import path14 from "path";
2275
3304
  import { z as z5 } from "zod";
2276
3305
  var ownedAdapterSchema = z5.object({
2277
3306
  pid: z5.number().int().positive().optional(),
@@ -2313,14 +3342,14 @@ var SessionStore = class {
2313
3342
  storePath;
2314
3343
  constructor(options = {}) {
2315
3344
  const env = options.dapCliHome === void 0 ? process.env : { ...process.env, DAP_CLI_HOME: options.dapCliHome };
2316
- this.storePath = path6.join(getDapCliStateDir(env), "sessions.json");
3345
+ this.storePath = path14.join(getDapCliStateDir(env), "sessions.json");
2317
3346
  }
2318
3347
  get path() {
2319
3348
  return this.storePath;
2320
3349
  }
2321
3350
  async read() {
2322
3351
  try {
2323
- const raw = await fs2.readFile(this.storePath, "utf8");
3352
+ const raw = await fs11.readFile(this.storePath, "utf8");
2324
3353
  const parsed = sessionStoreSchema.parse(JSON.parse(raw));
2325
3354
  return {
2326
3355
  ...parsed.activeSessionId !== void 0 ? { activeSessionId: parsed.activeSessionId } : {},
@@ -2333,7 +3362,7 @@ var SessionStore = class {
2333
3362
  if (error instanceof SyntaxError || error instanceof z5.ZodError) {
2334
3363
  const backupPath = `${this.storePath}.corrupt.${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.bak`;
2335
3364
  try {
2336
- await fs2.rename(this.storePath, backupPath);
3365
+ await fs11.rename(this.storePath, backupPath);
2337
3366
  process.stderr.write(`dap-cli: sessions.json was unparseable; moved to ${backupPath} and continuing with empty state.
2338
3367
  `);
2339
3368
  } catch {
@@ -2344,12 +3373,12 @@ var SessionStore = class {
2344
3373
  }
2345
3374
  }
2346
3375
  async write(data) {
2347
- await fs2.mkdir(path6.dirname(this.storePath), { recursive: true });
3376
+ await fs11.mkdir(path14.dirname(this.storePath), { recursive: true });
2348
3377
  const validated = sessionStoreSchema.parse(data);
2349
3378
  const tempPath = `${this.storePath}.${process.pid}.${randomUUID()}.tmp`;
2350
- await fs2.writeFile(tempPath, `${JSON.stringify(validated, null, 2)}
3379
+ await fs11.writeFile(tempPath, `${JSON.stringify(validated, null, 2)}
2351
3380
  `, "utf8");
2352
- await fs2.rename(tempPath, this.storePath);
3381
+ await fs11.rename(tempPath, this.storePath);
2353
3382
  }
2354
3383
  };
2355
3384
  function isNodeError2(error) {
@@ -3755,12 +4784,12 @@ function sourceMatches(eventPath, requestPath) {
3755
4784
  if (eventPath === requestPath) {
3756
4785
  return true;
3757
4786
  }
3758
- const normalize = (path14) => path14.replace(/^file:\/\//, "").replace(/\\/g, "/");
4787
+ const normalize = (path24) => path24.replace(/^file:\/\//, "").replace(/\\/g, "/");
3759
4788
  return normalize(eventPath) === normalize(requestPath);
3760
4789
  }
3761
4790
 
3762
4791
  // src/sessions/helperProcessDetection.ts
3763
- import { execFile } from "child_process";
4792
+ import { execFile as execFile2 } from "child_process";
3764
4793
  var helperProcessWarningEventName = "dapCli.helperProcessWarning";
3765
4794
  var helperHint = "Likely attached to an adapter-spawned helper process; you probably meant `dap-cli attach`, not `dap-cli launch`.";
3766
4795
  function createHelperProcessDetector(options) {
@@ -3839,7 +4868,7 @@ function defaultLookupPpid(pid) {
3839
4868
  return Promise.resolve(void 0);
3840
4869
  }
3841
4870
  return new Promise((resolve) => {
3842
- execFile("ps", ["-o", "ppid=", "-p", String(pid)], { timeout: psLookupTimeoutMs }, (error, stdout) => {
4871
+ execFile2("ps", ["-o", "ppid=", "-p", String(pid)], { timeout: psLookupTimeoutMs }, (error, stdout) => {
3843
4872
  if (error !== null) {
3844
4873
  resolve(void 0);
3845
4874
  return;
@@ -3860,8 +4889,8 @@ function defaultLookupPpid(pid) {
3860
4889
  }
3861
4890
 
3862
4891
  // src/controller/buildId.ts
3863
- import { promises as fs3 } from "fs";
3864
- import path7 from "path";
4892
+ import { promises as fs12 } from "fs";
4893
+ import path15 from "path";
3865
4894
  import { fileURLToPath } from "url";
3866
4895
  var cachedBuildId;
3867
4896
  async function computeBuildId() {
@@ -3873,14 +4902,14 @@ async function computeBuildId() {
3873
4902
  cachedBuildId = envOverride;
3874
4903
  return cachedBuildId;
3875
4904
  }
3876
- const here = path7.dirname(fileURLToPath(import.meta.url));
4905
+ const here = path15.dirname(fileURLToPath(import.meta.url));
3877
4906
  const pkgPath = await findPackageJson(here);
3878
4907
  let version = "0.0.0";
3879
4908
  let pkgDir = here;
3880
4909
  if (pkgPath !== void 0) {
3881
- pkgDir = path7.dirname(pkgPath);
4910
+ pkgDir = path15.dirname(pkgPath);
3882
4911
  try {
3883
- const raw = await fs3.readFile(pkgPath, "utf8");
4912
+ const raw = await fs12.readFile(pkgPath, "utf8");
3884
4913
  const parsed = JSON.parse(raw);
3885
4914
  if (typeof parsed.version === "string" && parsed.version.length > 0) {
3886
4915
  version = parsed.version;
@@ -3888,9 +4917,9 @@ async function computeBuildId() {
3888
4917
  } catch {
3889
4918
  }
3890
4919
  }
3891
- const distPath = path7.join(pkgDir, "dist", "index.js");
4920
+ const distPath = path15.join(pkgDir, "dist", "index.js");
3892
4921
  try {
3893
- const stat = await fs3.stat(distPath);
4922
+ const stat = await fs12.stat(distPath);
3894
4923
  cachedBuildId = `${version}:dist:${stat.mtimeMs}:${stat.size}`;
3895
4924
  } catch {
3896
4925
  cachedBuildId = `${version}:src`;
@@ -3900,12 +4929,12 @@ async function computeBuildId() {
3900
4929
  async function findPackageJson(startDir) {
3901
4930
  let dir = startDir;
3902
4931
  for (let depth = 0; depth < 8; depth += 1) {
3903
- const candidate = path7.join(dir, "package.json");
4932
+ const candidate = path15.join(dir, "package.json");
3904
4933
  try {
3905
- await fs3.access(candidate);
4934
+ await fs12.access(candidate);
3906
4935
  return candidate;
3907
4936
  } catch {
3908
- const parent = path7.dirname(dir);
4937
+ const parent = path15.dirname(dir);
3909
4938
  if (parent === dir) {
3910
4939
  return void 0;
3911
4940
  }
@@ -4271,12 +5300,12 @@ var ControllerServer = class {
4271
5300
  }
4272
5301
  if (request.method === "sessions.stop") {
4273
5302
  const target = getOptionalStringParam(request.params, "name");
4274
- await this.disconnectRuntimeForTarget(target);
5303
+ await this.disconnectRuntimeForTarget(target, { terminateDebuggee: true });
4275
5304
  return manager.stopSession(target);
4276
5305
  }
4277
5306
  if (request.method === "sessions.detach") {
4278
5307
  const target = getOptionalStringParam(request.params, "name");
4279
- await this.disconnectRuntimeForTarget(target);
5308
+ await this.disconnectRuntimeForTarget(target, { terminateDebuggee: false });
4280
5309
  return manager.detachSession(target);
4281
5310
  }
4282
5311
  if (request.method === "sessions.close") {
@@ -4658,7 +5687,7 @@ var ControllerServer = class {
4658
5687
  return { sources: [] };
4659
5688
  }
4660
5689
  if (source !== void 0) {
4661
- const entry = map.get(path8.resolve(source));
5690
+ const entry = map.get(path16.resolve(source));
4662
5691
  return entry === void 0 ? { sources: [] } : { sources: [{ source: entry.source, breakpoints: entry.response, requested: entry.requested }] };
4663
5692
  }
4664
5693
  return {
@@ -4683,7 +5712,7 @@ var ControllerServer = class {
4683
5712
  };
4684
5713
  const cleared = [];
4685
5714
  if (source !== void 0) {
4686
- const key = path8.resolve(source);
5715
+ const key = path16.resolve(source);
4687
5716
  const entry = map.get(key);
4688
5717
  if (entry === void 0) {
4689
5718
  return { cleared: [] };
@@ -4835,7 +5864,7 @@ var ControllerServer = class {
4835
5864
  }
4836
5865
  });
4837
5866
  }
4838
- async disconnectRuntimeForTarget(target) {
5867
+ async disconnectRuntimeForTarget(target, opts) {
4839
5868
  let status;
4840
5869
  try {
4841
5870
  status = this.requireSessionManager().status(target);
@@ -4849,7 +5878,7 @@ var ControllerServer = class {
4849
5878
  if (runtime.children !== void 0) {
4850
5879
  await runtime.children.dispose().catch(() => void 0);
4851
5880
  }
4852
- await runtime.lifecycle.disconnect().catch(() => void 0);
5881
+ await runtime.lifecycle.disconnect({ terminateDebuggee: opts.terminateDebuggee }).catch(() => void 0);
4853
5882
  await runtime.client.close().catch(() => void 0);
4854
5883
  await runtime.adapter.close().catch(() => void 0);
4855
5884
  this.runtimes.delete(status.id);
@@ -5046,7 +6075,7 @@ function parseDapStartCompoundMemberParams(value) {
5046
6075
  return parsed;
5047
6076
  }
5048
6077
  function createCompoundId() {
5049
- return `compound_${randomBytes2(12).toString("base64url")}`;
6078
+ return `compound_${randomBytes5(12).toString("base64url")}`;
5050
6079
  }
5051
6080
  function findFailedCompoundMemberName(error, members, startedMemberNames) {
5052
6081
  const failed = members.find((member) => !startedMemberNames.includes(member.memberName));
@@ -5172,7 +6201,7 @@ function toDapCliError(error, context) {
5172
6201
  }
5173
6202
  return dapError(error.message, {
5174
6203
  code: "dap_request_failed",
5175
- diagnostics: [`DAP request failed: ${error.command}. Inspect adapter diagnostics and session state.`],
6204
+ diagnostics: createDapResponseDiagnostics(error),
5176
6205
  sessionId: context.sessionId,
5177
6206
  request: { command: error.command, seq: error.requestSeq },
5178
6207
  adapter: context.adapter
@@ -5204,6 +6233,33 @@ function toDapCliError(error, context) {
5204
6233
  diagnostics: ["The adapter failed while processing the DAP request. Check adapter stderr and log path."]
5205
6234
  }, context));
5206
6235
  }
6236
+ function createDapResponseDiagnostics(error) {
6237
+ const diagnostics = [`DAP request failed: ${error.command}. Inspect adapter diagnostics and session state.`];
6238
+ const responseDetail = extractDapResponseDetail(error.responseBody);
6239
+ if (responseDetail !== void 0 && responseDetail !== error.message) {
6240
+ diagnostics.push(`Adapter detail: ${responseDetail}`);
6241
+ }
6242
+ return diagnostics;
6243
+ }
6244
+ function extractDapResponseDetail(body) {
6245
+ if (!isRecord5(body)) {
6246
+ return void 0;
6247
+ }
6248
+ if (typeof body.message === "string" && body.message.trim().length > 0) {
6249
+ return body.message.trim();
6250
+ }
6251
+ const error = body.error;
6252
+ if (!isRecord5(error)) {
6253
+ return void 0;
6254
+ }
6255
+ if (typeof error.format === "string" && error.format.trim().length > 0) {
6256
+ return error.format.trim();
6257
+ }
6258
+ if (typeof error.message === "string" && error.message.trim().length > 0) {
6259
+ return error.message.trim();
6260
+ }
6261
+ return void 0;
6262
+ }
5207
6263
  function toControllerErrorPayload(error) {
5208
6264
  const payload = {
5209
6265
  code: error.code,
@@ -5480,7 +6536,7 @@ async function delay2(ms) {
5480
6536
  }
5481
6537
 
5482
6538
  // src/cli/commands/dapAliases.ts
5483
- import path9 from "path";
6539
+ import path17 from "path";
5484
6540
 
5485
6541
  // src/cli/commands/jsonOptions.ts
5486
6542
  function parseJsonOption(value) {
@@ -5608,7 +6664,7 @@ function registerDapAliasCommands(program, output) {
5608
6664
  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
6665
  const lines = parseIntegerValues(options.line, "line");
5610
6666
  const args = {
5611
- source: { path: path9.resolve(options.source) },
6667
+ source: { path: path17.resolve(options.source) },
5612
6668
  breakpoints: lines.map((line) => compactObject({
5613
6669
  line,
5614
6670
  condition: options.condition,
@@ -5641,7 +6697,7 @@ function registerDapAliasCommands(program, output) {
5641
6697
  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
6698
  const params = compactObject({
5643
6699
  name: options.name,
5644
- source: options.source === void 0 ? void 0 : path9.resolve(options.source)
6700
+ source: options.source === void 0 ? void 0 : path17.resolve(options.source)
5645
6701
  });
5646
6702
  const client = await createControllerClient({ dapCliHome: process.env.DAP_CLI_HOME });
5647
6703
  try {
@@ -5654,7 +6710,7 @@ function registerDapAliasCommands(program, output) {
5654
6710
  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
6711
  const params = compactObject({
5656
6712
  name: options.name,
5657
- source: options.source === void 0 ? void 0 : path9.resolve(options.source)
6713
+ source: options.source === void 0 ? void 0 : path17.resolve(options.source)
5658
6714
  });
5659
6715
  const client = await createControllerClient({ dapCliHome: process.env.DAP_CLI_HOME });
5660
6716
  try {
@@ -5682,10 +6738,10 @@ function registerDapAliasCommands(program, output) {
5682
6738
  await sendAliasRequest(output, "variables", { variablesReference: parseRequiredIntegerOption(options.variablesReference, "variables-reference") }, options.name, "variables");
5683
6739
  });
5684
6740
  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;
6741
+ const path24 = options.path;
5686
6742
  await sendAliasRequest(output, "source", compactObject({
5687
6743
  sourceReference: parseRequiredIntegerOption(options.sourceReference, "source-reference"),
5688
- source: path14 === void 0 ? void 0 : { path: path14 }
6744
+ source: path24 === void 0 ? void 0 : { path: path24 }
5689
6745
  }), options.name, "source");
5690
6746
  });
5691
6747
  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 +6998,9 @@ async function buildVerificationDiagnostic(client, requestedPath, breakpointsRes
5942
6998
  }
5943
6999
  const sources = loaded.sources ?? [];
5944
7000
  const loadedSourcesCount = sources.length;
5945
- const wantBasename = path9.basename(requestedPath);
7001
+ const wantBasename = path17.basename(requestedPath);
5946
7002
  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) => {
7003
+ const matchingLoadedSources = sources.filter((s) => typeof s.path === "string" && (cmp(s.path, requestedPath) || cmp(path17.basename(s.path), wantBasename))).map((s) => {
5948
7004
  const sourcePath = s.path;
5949
7005
  return typeof s.name === "string" ? { path: sourcePath, name: s.name } : { path: sourcePath };
5950
7006
  });
@@ -5986,12 +7042,12 @@ async function countChildSessions(client, parentName) {
5986
7042
  }
5987
7043
 
5988
7044
  // src/cli/commands/dapCore.ts
5989
- import path13 from "path";
7045
+ import path22 from "path";
5990
7046
 
5991
7047
  // src/adapters/config.ts
5992
- import { promises as fs4 } from "fs";
7048
+ import { promises as fs13 } from "fs";
5993
7049
  import { randomUUID as randomUUID2 } from "crypto";
5994
- import path10 from "path";
7050
+ import path18 from "path";
5995
7051
  import { z as z6 } from "zod";
5996
7052
  var configuredAdapterDescriptorSchema = adapterDescriptorSchema.and(z6.object({
5997
7053
  launchDefaults: z6.record(z6.string(), z6.unknown()).optional(),
@@ -6004,7 +7060,7 @@ var adapterConfigSchema = z6.object({
6004
7060
  async function loadAdapterConfig(dapCliHome) {
6005
7061
  const configPath = getAdapterConfigPath(dapCliHome);
6006
7062
  try {
6007
- const raw = await fs4.readFile(configPath, "utf8");
7063
+ const raw = await fs13.readFile(configPath, "utf8");
6008
7064
  return parseAdapterConfig(raw);
6009
7065
  } catch (error) {
6010
7066
  if (isNodeError4(error) && error.code === "ENOENT") {
@@ -6018,7 +7074,7 @@ async function loadAdapterConfig(dapCliHome) {
6018
7074
  }
6019
7075
  function getAdapterConfigPath(dapCliHome) {
6020
7076
  const home = dapCliHome === void 0 ? getDapCliHome() : getDapCliHome({ ...process.env, DAP_CLI_HOME: dapCliHome });
6021
- return path10.join(home, "config", "adapters.json");
7077
+ return path18.join(home, "config", "adapters.json");
6022
7078
  }
6023
7079
  function parseAdapterConfig(raw) {
6024
7080
  return adapterConfigSchema.parse(JSON.parse(raw));
@@ -6034,10 +7090,12 @@ function isNodeError4(error) {
6034
7090
  }
6035
7091
 
6036
7092
  // 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();
7093
+ import { promises as fs14 } from "fs";
7094
+ import { execFile as execFile3 } from "child_process";
7095
+ import { promisify as promisify2 } from "util";
7096
+ var execFileAsync2 = promisify2(execFile3);
7097
+ async function createDebugpyDescriptor(pythonPath) {
7098
+ const resolvedPythonPath = pythonPath ?? await resolveDefaultDebugpyPythonPath();
6041
7099
  return {
6042
7100
  id: "debugpy",
6043
7101
  label: "Python Debug Adapter (debugpy)",
@@ -6048,26 +7106,133 @@ function createDebugpyDescriptor(pythonPath) {
6048
7106
  }
6049
7107
  };
6050
7108
  }
6051
- function resolveDefaultDebugpyPythonPath() {
6052
- const provisionedPython = getDapCliVenvPythonPath();
6053
- const candidates = [provisionedPython, "python3"];
6054
- if (existsSync2(provisionedPython) && pythonHasDebugpy(provisionedPython)) {
6055
- return provisionedPython;
7109
+ async function pathExists2(p) {
7110
+ try {
7111
+ await fs14.access(p);
7112
+ return true;
7113
+ } catch {
7114
+ return false;
6056
7115
  }
6057
- if (pythonHasDebugpy("python3")) {
7116
+ }
7117
+ async function pythonHasDebugpy(pythonPath) {
7118
+ try {
7119
+ await execFileAsync2(pythonPath, ["-c", "import debugpy"]);
7120
+ return true;
7121
+ } catch {
7122
+ return false;
7123
+ }
7124
+ }
7125
+ async function resolveDefaultDebugpyPythonPath(env = process.env) {
7126
+ const legacyVenvPython = getDapCliVenvPythonPath(env);
7127
+ if (await pathExists2(legacyVenvPython) && await pythonHasDebugpy(legacyVenvPython)) {
7128
+ return legacyVenvPython;
7129
+ }
7130
+ if (await pythonHasDebugpy("python3")) {
6058
7131
  return "python3";
6059
7132
  }
6060
- throw usageError("debugpy adapter is not installed.", {
6061
- code: "debugpy_not_found",
7133
+ const adaptersDir = getDapCliAdaptersDir(env);
7134
+ const result = await provisionAdapter("debugpy", {
7135
+ env,
7136
+ assumeYes: resolveAssumeYes(void 0, env),
7137
+ adaptersDir
7138
+ });
7139
+ return result.entrypoint;
7140
+ }
7141
+
7142
+ // src/adapters/builtins/delve.ts
7143
+ import { promises as fs15 } from "fs";
7144
+ import { spawnSync } from "child_process";
7145
+ import path19 from "path";
7146
+ async function createDelveDescriptor(delvePath) {
7147
+ const resolvedDelvePath = delvePath ?? await resolveDefaultDelvePath();
7148
+ assertSupportedProvisionedDelveToolchain(resolvedDelvePath);
7149
+ const toolchainEnvironment = createGoToolchainEnvironment();
7150
+ return {
7151
+ id: "delve",
7152
+ label: "Go Debug Adapter (Delve)",
7153
+ transport: {
7154
+ kind: "server",
7155
+ command: resolvedDelvePath,
7156
+ args: ["dap", "--listen=127.0.0.1:${port}"],
7157
+ host: "127.0.0.1",
7158
+ ...toolchainEnvironment === void 0 ? {} : { env: toolchainEnvironment }
7159
+ }
7160
+ };
7161
+ }
7162
+ function createGoToolchainEnvironment() {
7163
+ const goToolchain = process.env.GOTOOLCHAIN;
7164
+ return goToolchain === void 0 || goToolchain.length === 0 ? void 0 : { GOTOOLCHAIN: goToolchain };
7165
+ }
7166
+ async function pathExists3(p) {
7167
+ try {
7168
+ await fs15.access(p);
7169
+ return true;
7170
+ } catch {
7171
+ return false;
7172
+ }
7173
+ }
7174
+ async function resolveDefaultDelvePath(env = process.env) {
7175
+ if (delveIsUsable("dlv")) {
7176
+ return "dlv";
7177
+ }
7178
+ const provisionedDelve = getProvisionedDelvePath(env);
7179
+ if (await pathExists3(provisionedDelve) && delveIsUsable(provisionedDelve)) {
7180
+ return provisionedDelve;
7181
+ }
7182
+ const adaptersDir = getDapCliAdaptersDir(env);
7183
+ const result = await provisionAdapter("delve", {
7184
+ env,
7185
+ assumeYes: resolveAssumeYes(void 0, env),
7186
+ adaptersDir
7187
+ });
7188
+ return result.entrypoint;
7189
+ }
7190
+ function getProvisionedDelvePath(env = process.env) {
7191
+ return path19.join(getDapCliAdaptersDir(env), "delve", process.platform === "win32" ? "dlv.exe" : "dlv");
7192
+ }
7193
+ function delveIsUsable(command) {
7194
+ const result = spawnSync(command, ["version"], { encoding: "utf8" });
7195
+ return result.status === 0;
7196
+ }
7197
+ function assertSupportedProvisionedDelveToolchain(delvePath) {
7198
+ const delveVersion = readCommandVersion(delvePath, ["version"], /Version:\s*v?([0-9]+\.[0-9]+\.[0-9]+)/i);
7199
+ if (delveVersion !== "1.26.3") {
7200
+ return;
7201
+ }
7202
+ const goVersion = readCommandVersion("go", ["version"], /go version go([0-9]+\.[0-9]+(?:\.[0-9]+)?)/i);
7203
+ if (goVersion === void 0 || compareNumericVersions(goVersion, "1.24.0") >= 0) {
7204
+ return;
7205
+ }
7206
+ throw usageError("Provisioned Delve is incompatible with the active Go toolchain.", {
7207
+ code: "delve_go_version_incompatible",
6062
7208
  diagnostics: [
6063
- "Run npm run setup-adapters to provision debugpy, or see docs/ADAPTER-SETUP.md for advanced manual provisioning.",
6064
- `Checked: ${candidates.join(", ")}`
7209
+ `Delve ${delveVersion} requires Go 1.24+ for debuggee builds; current \`go\` is ${goVersion}.`,
7210
+ "Use `GOTOOLCHAIN=go1.24.0` for the dap-cli launch, or update the active Go installation.",
7211
+ "Run `go version` in the same shell/environment used for dap-cli to confirm the effective toolchain."
6065
7212
  ]
6066
7213
  });
6067
7214
  }
6068
- function pythonHasDebugpy(pythonPath) {
6069
- const result = spawnSync(pythonPath, ["-c", "import debugpy"], { encoding: "utf8" });
6070
- return result.status === 0;
7215
+ function readCommandVersion(command, args, pattern) {
7216
+ const result = spawnSync(command, [...args], { encoding: "utf8" });
7217
+ if (result.status !== 0) {
7218
+ return void 0;
7219
+ }
7220
+ const output = `${result.stdout ?? ""}
7221
+ ${result.stderr ?? ""}`;
7222
+ return pattern.exec(output)?.[1];
7223
+ }
7224
+ function compareNumericVersions(left, right) {
7225
+ const leftParts = left.split(".").map((part) => Number.parseInt(part, 10));
7226
+ const rightParts = right.split(".").map((part) => Number.parseInt(part, 10));
7227
+ const width = Math.max(leftParts.length, rightParts.length);
7228
+ for (let index = 0; index < width; index += 1) {
7229
+ const leftPart = leftParts[index] ?? 0;
7230
+ const rightPart = rightParts[index] ?? 0;
7231
+ if (leftPart !== rightPart) {
7232
+ return leftPart - rightPart;
7233
+ }
7234
+ }
7235
+ return 0;
6071
7236
  }
6072
7237
 
6073
7238
  // src/adapters/registry.ts
@@ -6086,6 +7251,11 @@ var AdapterRegistry = class {
6086
7251
  label: "Python Debug Adapter (debugpy)",
6087
7252
  create: () => createDebugpyDescriptor()
6088
7253
  });
7254
+ this.builtInAdapters.set("delve", {
7255
+ id: "delve",
7256
+ label: "Go Debug Adapter (Delve)",
7257
+ create: () => createDelveDescriptor()
7258
+ });
6089
7259
  }
6090
7260
  for (const descriptor of options.builtInAdapters ?? []) {
6091
7261
  this.builtInAdapters.set(descriptor.id, {
@@ -6098,7 +7268,7 @@ var AdapterRegistry = class {
6098
7268
  this.customAdapters.set(descriptor.id, descriptor);
6099
7269
  }
6100
7270
  }
6101
- resolve(id) {
7271
+ async resolve(id) {
6102
7272
  const builtInAdapter = this.builtInAdapters.get(id);
6103
7273
  if (builtInAdapter !== void 0) {
6104
7274
  return builtInAdapter.create();
@@ -6127,9 +7297,9 @@ var AdapterRegistry = class {
6127
7297
  };
6128
7298
 
6129
7299
  // src/config/launchConfig.ts
6130
- import { promises as fs5 } from "fs";
7300
+ import { promises as fs16 } from "fs";
6131
7301
  import os from "os";
6132
- import path11 from "path";
7302
+ import path20 from "path";
6133
7303
  import { parse } from "jsonc-parser";
6134
7304
  import { z as z7 } from "zod";
6135
7305
  var launchConfigTypeMap = {
@@ -6138,7 +7308,8 @@ var launchConfigTypeMap = {
6138
7308
  chrome: "js-debug",
6139
7309
  "pwa-chrome": "js-debug",
6140
7310
  python: "debugpy",
6141
- debugpy: "debugpy"
7311
+ debugpy: "debugpy",
7312
+ go: "delve"
6142
7313
  };
6143
7314
  var maxLaunchJsonBytes = 256 * 1024;
6144
7315
  var platformKeys = /* @__PURE__ */ new Set(["osx", "mac", "linux", "windows"]);
@@ -6165,12 +7336,12 @@ function resolveLaunchConfig(sources) {
6165
7336
  };
6166
7337
  }
6167
7338
  function resolveLaunchConfigurationConfig(config, options) {
6168
- const workspaceFolder = path11.resolve(options.workspaceFolder);
7339
+ const workspaceFolder = path20.resolve(options.workspaceFolder);
6169
7340
  const platform = options.platform ?? process.platform;
6170
7341
  const merged = applyPlatformOverlay(config, platform);
6171
7342
  const context = {
6172
7343
  workspaceFolder,
6173
- workspaceFolderBasename: path11.basename(workspaceFolder),
7344
+ workspaceFolderBasename: path20.basename(workspaceFolder),
6174
7345
  userHome: options.userHome ?? os.homedir(),
6175
7346
  execPath: options.execPath ?? process.execPath,
6176
7347
  env: options.env ?? process.env
@@ -6185,20 +7356,20 @@ function resolveLaunchConfigurationConfig(config, options) {
6185
7356
  for (const key of vscodeOnlyLaunchConfigKeys) {
6186
7357
  delete resolved[key];
6187
7358
  }
6188
- return resolved;
7359
+ return normalizeGoLaunchConfigProgram(resolved, workspaceFolder);
6189
7360
  }
6190
7361
  async function loadVSCodeLaunchJson(cwd) {
6191
- const workspaceFolder = path11.resolve(cwd);
6192
- const launchJsonPath = path11.join(cwd, ".vscode", "launch.json");
7362
+ const workspaceFolder = path20.resolve(cwd);
7363
+ const launchJsonPath = path20.join(cwd, ".vscode", "launch.json");
6193
7364
  try {
6194
- const stat = await fs5.stat(launchJsonPath);
7365
+ const stat = await fs16.stat(launchJsonPath);
6195
7366
  if (stat.size > maxLaunchJsonBytes) {
6196
7367
  throw usageError("Invalid launch.json.", {
6197
7368
  code: "invalid_launch_json",
6198
7369
  diagnostics: [".vscode/launch.json is larger than 256KB."]
6199
7370
  });
6200
7371
  }
6201
- const raw = await fs5.readFile(launchJsonPath, "utf8");
7372
+ const raw = await fs16.readFile(launchJsonPath, "utf8");
6202
7373
  const normalized = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
6203
7374
  const parsed = launchJsonSchema.parse(parseJsonc(normalized));
6204
7375
  return {
@@ -6303,7 +7474,7 @@ async function applyJsDebugSourceMapDefaults(config, options) {
6303
7474
  if (!isJsDebugLaunchType(config.type)) {
6304
7475
  return config;
6305
7476
  }
6306
- const hasTsConfig = await pathExists(path11.join(options.workspaceFolder, "tsconfig.json"));
7477
+ const hasTsConfig = await pathExists4(path20.join(options.workspaceFolder, "tsconfig.json"));
6307
7478
  if (!hasTsConfig) {
6308
7479
  return config;
6309
7480
  }
@@ -6313,9 +7484,9 @@ async function applyJsDebugSourceMapDefaults(config, options) {
6313
7484
  }
6314
7485
  if (mapped.outFiles === void 0) {
6315
7486
  mapped.outFiles = [
6316
- path11.join(options.workspaceFolder, "dist", "**", "*.js"),
6317
- path11.join(options.workspaceFolder, "out", "**", "*.js"),
6318
- path11.join(options.workspaceFolder, "build", "**", "*.js")
7487
+ path20.join(options.workspaceFolder, "dist", "**", "*.js"),
7488
+ path20.join(options.workspaceFolder, "out", "**", "*.js"),
7489
+ path20.join(options.workspaceFolder, "build", "**", "*.js")
6319
7490
  ];
6320
7491
  }
6321
7492
  return mapped;
@@ -6328,6 +7499,16 @@ function mapDebugpyFlags(flags) {
6328
7499
  }
6329
7500
  return mapped;
6330
7501
  }
7502
+ function normalizeGoLaunchConfigProgram(config, workspaceFolder) {
7503
+ if (config.type !== "go" || config.request === "attach" || typeof config.program !== "string" || path20.isAbsolute(config.program)) {
7504
+ return config;
7505
+ }
7506
+ const cwd = typeof config.cwd === "string" ? config.cwd : workspaceFolder;
7507
+ return {
7508
+ ...config,
7509
+ program: path20.resolve(cwd, config.program)
7510
+ };
7511
+ }
6331
7512
  function applyPlatformOverlay(config, platform) {
6332
7513
  const base = {};
6333
7514
  for (const [key, value] of Object.entries(config)) {
@@ -6410,9 +7591,9 @@ function resolveLaunchString(value, jsonPath, context) {
6410
7591
  });
6411
7592
  });
6412
7593
  }
6413
- async function pathExists(filePath) {
7594
+ async function pathExists4(filePath) {
6414
7595
  try {
6415
- await fs5.stat(filePath);
7596
+ await fs16.stat(filePath);
6416
7597
  return true;
6417
7598
  } catch (error) {
6418
7599
  if (isNodeError5(error) && error.code === "ENOENT") {
@@ -6454,9 +7635,10 @@ function isNodeError5(error) {
6454
7635
  }
6455
7636
 
6456
7637
  // src/config/programInference.ts
6457
- import path12 from "path";
7638
+ import path21 from "path";
6458
7639
  var extensionTable = {
6459
7640
  ".py": { adapterId: "debugpy", type: "python" },
7641
+ ".go": { adapterId: "delve", type: "go" },
6460
7642
  ".js": { adapterId: "js-debug", type: "pwa-node" },
6461
7643
  ".mjs": { adapterId: "js-debug", type: "pwa-node" },
6462
7644
  ".cjs": { adapterId: "js-debug", type: "pwa-node" },
@@ -6484,7 +7666,7 @@ function inferAdapterAndType(args) {
6484
7666
  return { adapterId, type, inferred: { adapter: true, type: false } };
6485
7667
  }
6486
7668
  if (program !== void 0) {
6487
- const extension = path12.extname(program).toLowerCase();
7669
+ const extension = path21.extname(program).toLowerCase();
6488
7670
  const match = extensionTable[extension];
6489
7671
  if (match === void 0) {
6490
7672
  throw usageError(`Cannot infer adapter from program extension '${extension}'. Pass --adapter or --type explicitly.`, {
@@ -6500,7 +7682,7 @@ function inferAdapterAndType(args) {
6500
7682
  function defaultTypeForAdapter(adapterId, program) {
6501
7683
  if (adapterId === "js-debug") {
6502
7684
  if (program !== void 0) {
6503
- const ext = path12.extname(program).toLowerCase();
7685
+ const ext = path21.extname(program).toLowerCase();
6504
7686
  if (ext === ".html" || ext === ".htm") {
6505
7687
  return "pwa-chrome";
6506
7688
  }
@@ -6510,6 +7692,9 @@ function defaultTypeForAdapter(adapterId, program) {
6510
7692
  if (adapterId === "debugpy") {
6511
7693
  return "python";
6512
7694
  }
7695
+ if (adapterId === "delve") {
7696
+ return "go";
7697
+ }
6513
7698
  return void 0;
6514
7699
  }
6515
7700
 
@@ -6551,7 +7736,7 @@ function createNameParams(name) {
6551
7736
  return name === void 0 ? {} : { name };
6552
7737
  }
6553
7738
  async function startDap(output, mode, options) {
6554
- const workspace = path13.resolve(options.workspace ?? process.cwd());
7739
+ const workspace = path22.resolve(options.workspace ?? process.cwd());
6555
7740
  if (options.listConfigs === true) {
6556
7741
  output.success(listLaunchConfigEntries(await loadVSCodeLaunchJson(workspace)), { command: "launch configs" });
6557
7742
  return;
@@ -6595,7 +7780,7 @@ async function startDap(output, mode, options) {
6595
7780
  ...await mapConfigForAdapter(adapterId, resolveLaunchConfig({ namedConfig: { ...adapterDefaults, ...namedConfig }, jsonOverrides, jsonConfig, flags: adapterFlags }), workspace),
6596
7781
  request: effectiveMode
6597
7782
  };
6598
- const descriptor = adapterId === "fake" ? createFakeDescriptor(options.script ?? (effectiveMode === "attach" ? "attach-stopped" : "stopped-on-entry"), effectiveMode) : new AdapterRegistry({ config: adapterConfig }).resolve(adapterId);
7783
+ const descriptor = adapterId === "fake" ? createFakeDescriptor(options.script ?? (effectiveMode === "attach" ? "attach-stopped" : "stopped-on-entry"), effectiveMode) : await new AdapterRegistry({ config: adapterConfig }).resolve(adapterId);
6599
7784
  const client = await createControllerClient({ dapCliHome: process.env.DAP_CLI_HOME, timeoutMs: startControllerRequestTimeoutMs });
6600
7785
  try {
6601
7786
  const response = await client.request("dap.start", {
@@ -6625,7 +7810,7 @@ async function createCompoundStartMember(configuration, memberName, workspaceFol
6625
7810
  ...await mapConfigForAdapter(adapterId, resolveLaunchConfig({ namedConfig: { ...adapterDefaults, ...resolvedConfig }, jsonOverrides, jsonConfig, flags: adapterFlags }), workspaceFolder),
6626
7811
  request: memberMode
6627
7812
  };
6628
- const descriptor = adapterId === "fake" ? createFakeDescriptor(options.script ?? (memberMode === "attach" ? "attach-stopped" : "stopped-on-entry"), memberMode) : new AdapterRegistry({ config: adapterConfig }).resolve(adapterId);
7813
+ const descriptor = adapterId === "fake" ? createFakeDescriptor(options.script ?? (memberMode === "attach" ? "attach-stopped" : "stopped-on-entry"), memberMode) : await new AdapterRegistry({ config: adapterConfig }).resolve(adapterId);
6629
7814
  return { memberName, mode: memberMode, descriptor, config };
6630
7815
  }
6631
7816
  function getAdapterDefaults(adapterConfig, adapterId, mode) {
@@ -6704,6 +7889,9 @@ async function mapConfigForAdapter(adapterId, config, workspaceFolder) {
6704
7889
  if (adapterId === "debugpy") {
6705
7890
  return mapDebugpyFlags(config);
6706
7891
  }
7892
+ if (adapterId === "delve") {
7893
+ return normalizeGoLaunchConfigProgram(config, workspaceFolder);
7894
+ }
6707
7895
  return config;
6708
7896
  }
6709
7897
  function setIfDefined(target, key, value) {
@@ -6727,7 +7915,7 @@ function createFakeDescriptor(script, mode) {
6727
7915
  transport: {
6728
7916
  kind: "stdio",
6729
7917
  command: process.execPath,
6730
- args: ["--experimental-strip-types", path13.join(process.cwd(), "tests", "fixtures", "fake-adapter-entry.ts"), "--script", script, "--mode", mode]
7918
+ args: ["--experimental-strip-types", path22.join(process.cwd(), "tests", "fixtures", "fake-adapter-entry.ts"), "--script", script, "--mode", mode]
6731
7919
  }
6732
7920
  };
6733
7921
  }
@@ -6843,6 +8031,133 @@ function createNameParams2(name) {
6843
8031
  return name === void 0 ? {} : { name };
6844
8032
  }
6845
8033
 
8034
+ // src/cli/commands/setupAdapters.ts
8035
+ import path23 from "path";
8036
+ import { promises as fs17 } from "fs";
8037
+ import { Option } from "commander";
8038
+ var ALL_ADAPTERS = ["js-debug", "debugpy", "delve"];
8039
+ var ADAPTER_VERSIONS = {
8040
+ "js-debug": JS_DEBUG_VERSION,
8041
+ "debugpy": DEBUGPY_VERSION,
8042
+ "delve": DELVE_VERSION
8043
+ };
8044
+ function expectedEntrypoint(adaptersDir, id) {
8045
+ switch (id) {
8046
+ case "js-debug":
8047
+ return path23.join(adaptersDir, "js-debug", "src", "dapDebugServer.js");
8048
+ case "debugpy":
8049
+ return path23.join(
8050
+ adaptersDir,
8051
+ "debugpy",
8052
+ process.platform === "win32" ? path23.join("venv", "Scripts", "python.exe") : path23.join("venv", "bin", "python")
8053
+ );
8054
+ case "delve":
8055
+ return path23.join(adaptersDir, "delve", process.platform === "win32" ? "dlv.exe" : "dlv");
8056
+ }
8057
+ }
8058
+ async function pathExists5(p) {
8059
+ try {
8060
+ await fs17.access(p);
8061
+ return true;
8062
+ } catch {
8063
+ return false;
8064
+ }
8065
+ }
8066
+ function isAdapterId(value) {
8067
+ return value === "js-debug" || value === "debugpy" || value === "delve";
8068
+ }
8069
+ async function runSetupAdaptersAction(opts) {
8070
+ const targets = opts.adapter !== void 0 ? [opts.adapter] : ALL_ADAPTERS;
8071
+ const adaptersDir = getDapCliAdaptersDir(opts.env);
8072
+ await fs17.mkdir(adaptersDir, { recursive: true });
8073
+ const pending = [];
8074
+ for (const id of targets) {
8075
+ const version = ADAPTER_VERSIONS[id];
8076
+ const cached = await hasConsentMarker(adaptersDir, id, version) && await pathExists5(expectedEntrypoint(adaptersDir, id));
8077
+ if (!cached) {
8078
+ pending.push(id);
8079
+ }
8080
+ }
8081
+ if (pending.length > 0 && !opts.assumeYes) {
8082
+ const pendingDescription = pending.map((id) => `${id} ${ADAPTER_VERSIONS[id]}`).join(", ");
8083
+ await confirm({
8084
+ assumeYes: false,
8085
+ question: `Install ${pending.length} adapter${pending.length === 1 ? "" : "s"} (${pendingDescription}) into ${adaptersDir}/?`,
8086
+ ...opts.stdin === void 0 ? {} : { stdin: opts.stdin },
8087
+ ...opts.stderr === void 0 ? {} : { stderr: opts.stderr }
8088
+ });
8089
+ }
8090
+ const innerCtxBase = {
8091
+ env: opts.env,
8092
+ adaptersDir,
8093
+ ...opts.stdin === void 0 ? {} : { stdin: opts.stdin },
8094
+ ...opts.stderr === void 0 ? {} : { stderr: opts.stderr }
8095
+ };
8096
+ const entries = [];
8097
+ for (const id of targets) {
8098
+ const version = ADAPTER_VERSIONS[id];
8099
+ try {
8100
+ const result = await provisionAdapter(id, { ...innerCtxBase, assumeYes: true });
8101
+ const entry = {
8102
+ id,
8103
+ version: result.version,
8104
+ status: result.fromCache ? "cached" : "installed",
8105
+ installRoot: result.installRoot
8106
+ };
8107
+ entries.push(entry);
8108
+ if (opts.output !== void 0) {
8109
+ const verb = result.fromCache ? "already cached" : "installed";
8110
+ opts.output.warn(`${id} ${result.version}: ${verb} (${result.installRoot})`);
8111
+ }
8112
+ } catch (err) {
8113
+ const cliError = err instanceof CliError ? err : null;
8114
+ const code = cliError?.code ?? "internal_error";
8115
+ const message = err instanceof Error ? err.message : String(err);
8116
+ entries.push({
8117
+ id,
8118
+ version,
8119
+ status: "failed",
8120
+ error: {
8121
+ code,
8122
+ message,
8123
+ diagnostics: cliError?.diagnostics ?? [message]
8124
+ }
8125
+ });
8126
+ if (opts.output !== void 0) {
8127
+ opts.output.warn(`${id} ${version}: FAILED (${code})`);
8128
+ }
8129
+ }
8130
+ }
8131
+ return { adapters: entries };
8132
+ }
8133
+ function registerSetupAdaptersCommand(program, output) {
8134
+ program.command("setup-adapters").helpGroup("Adapters").description("Install or update built-in debug adapters (js-debug, debugpy, delve) into ~/.dap-cli/adapters/").addOption(
8135
+ new Option("--adapter <id>", "install only the named adapter").choices(["js-debug", "debugpy", "delve"])
8136
+ ).action(async (cmdOpts) => {
8137
+ const adapterId = cmdOpts.adapter !== void 0 && isAdapterId(cmdOpts.adapter) ? cmdOpts.adapter : void 0;
8138
+ const assumeYes = resolveAssumeYes(void 0, process.env);
8139
+ const result = await runSetupAdaptersAction({
8140
+ ...adapterId === void 0 ? {} : { adapter: adapterId },
8141
+ assumeYes,
8142
+ env: process.env,
8143
+ stdin: process.stdin,
8144
+ stderr: process.stderr,
8145
+ stdout: process.stdout,
8146
+ output
8147
+ });
8148
+ const failed = result.adapters.filter((a) => a.status === "failed");
8149
+ if (failed.length > 0) {
8150
+ const failedNames = failed.map((a) => `${a.id} (${a.error?.code ?? "internal_error"})`).join(", ");
8151
+ throw usageError(`Adapter setup failed for: ${failedNames}`, {
8152
+ code: "provision_setup_failed",
8153
+ diagnostics: failed.flatMap((a) => a.error?.diagnostics ?? []),
8154
+ data: { adapters: result.adapters }
8155
+ });
8156
+ }
8157
+ output.success(result, { command: "setup-adapters" });
8158
+ });
8159
+ }
8160
+
6846
8161
  // src/cli/program.ts
6847
8162
  var require2 = createRequire(import.meta.url);
6848
8163
  function loadPackageJson() {
@@ -6867,12 +8182,19 @@ function createProgram(options = {}) {
6867
8182
  resolveMode: () => resolveOutputMode({ cliHuman: getProgramHumanOption(program), isStdoutTTY: stdout.isTTY === true, env: process.env })
6868
8183
  });
6869
8184
  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();
8185
+ 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();
8186
+ program.hook("preAction", (thisCommand) => {
8187
+ const opts = thisCommand.opts();
8188
+ if (opts.yes === true) {
8189
+ process.env.DAP_CLI_ASSUME_YES = "1";
8190
+ }
8191
+ });
6871
8192
  registerControllerCommands(program, output);
6872
8193
  registerSessionCommands(program, output);
6873
8194
  registerDapCoreCommands(program, output);
6874
8195
  registerGeneratedDapCommands(program, output);
6875
8196
  registerDapAliasCommands(program, output);
8197
+ registerSetupAdaptersCommand(program, output);
6876
8198
  program.helpCommand(false);
6877
8199
  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
8200
  const segments = Array.isArray(commandPath) ? commandPath : [];