@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/README.md +33 -7
- package/dist/index.js +1432 -110
- package/dist/index.js.map +1 -1
- package/package.json +10 -3
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
|
|
1157
|
-
import { randomBytes as
|
|
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
|
|
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 {
|
|
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
|
-
|
|
1467
|
-
|
|
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:
|
|
2509
|
+
logFile: path13.join(logDir, `js-debug-trace-${Date.now()}.log`)
|
|
1492
2510
|
}
|
|
1493
2511
|
};
|
|
1494
2512
|
}
|
|
1495
|
-
function
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
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
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
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_${
|
|
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
|
|
3301
|
+
import { promises as fs11 } from "fs";
|
|
2273
3302
|
import { randomUUID } from "crypto";
|
|
2274
|
-
import
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
3379
|
+
await fs11.writeFile(tempPath, `${JSON.stringify(validated, null, 2)}
|
|
2351
3380
|
`, "utf8");
|
|
2352
|
-
await
|
|
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 = (
|
|
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
|
-
|
|
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
|
|
3864
|
-
import
|
|
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 =
|
|
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 =
|
|
4910
|
+
pkgDir = path15.dirname(pkgPath);
|
|
3882
4911
|
try {
|
|
3883
|
-
const raw = await
|
|
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 =
|
|
4920
|
+
const distPath = path15.join(pkgDir, "dist", "index.js");
|
|
3892
4921
|
try {
|
|
3893
|
-
const stat = await
|
|
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 =
|
|
4932
|
+
const candidate = path15.join(dir, "package.json");
|
|
3904
4933
|
try {
|
|
3905
|
-
await
|
|
4934
|
+
await fs12.access(candidate);
|
|
3906
4935
|
return candidate;
|
|
3907
4936
|
} catch {
|
|
3908
|
-
const parent =
|
|
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(
|
|
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 =
|
|
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_${
|
|
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:
|
|
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
|
|
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:
|
|
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 :
|
|
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 :
|
|
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
|
|
6741
|
+
const path24 = options.path;
|
|
5686
6742
|
await sendAliasRequest(output, "source", compactObject({
|
|
5687
6743
|
sourceReference: parseRequiredIntegerOption(options.sourceReference, "source-reference"),
|
|
5688
|
-
source:
|
|
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 =
|
|
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(
|
|
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
|
|
7045
|
+
import path22 from "path";
|
|
5990
7046
|
|
|
5991
7047
|
// src/adapters/config.ts
|
|
5992
|
-
import { promises as
|
|
7048
|
+
import { promises as fs13 } from "fs";
|
|
5993
7049
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
5994
|
-
import
|
|
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
|
|
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
|
|
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 {
|
|
6038
|
-
import {
|
|
6039
|
-
|
|
6040
|
-
|
|
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
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
7109
|
+
async function pathExists2(p) {
|
|
7110
|
+
try {
|
|
7111
|
+
await fs14.access(p);
|
|
7112
|
+
return true;
|
|
7113
|
+
} catch {
|
|
7114
|
+
return false;
|
|
6056
7115
|
}
|
|
6057
|
-
|
|
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
|
-
|
|
6061
|
-
|
|
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
|
-
|
|
6064
|
-
`
|
|
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
|
|
6069
|
-
const result = spawnSync(
|
|
6070
|
-
|
|
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
|
|
7300
|
+
import { promises as fs16 } from "fs";
|
|
6131
7301
|
import os from "os";
|
|
6132
|
-
import
|
|
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 =
|
|
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:
|
|
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 =
|
|
6192
|
-
const launchJsonPath =
|
|
7362
|
+
const workspaceFolder = path20.resolve(cwd);
|
|
7363
|
+
const launchJsonPath = path20.join(cwd, ".vscode", "launch.json");
|
|
6193
7364
|
try {
|
|
6194
|
-
const stat = await
|
|
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
|
|
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
|
|
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
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
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
|
|
7594
|
+
async function pathExists4(filePath) {
|
|
6414
7595
|
try {
|
|
6415
|
-
await
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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",
|
|
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 : [];
|