dx-devkit 0.0.0-stage → 1.1.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.
Files changed (172) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/LICENSE +21 -0
  3. package/README.md +475 -2
  4. package/bin/dx-mcp.js +2 -0
  5. package/dist/command.d.ts +9 -0
  6. package/dist/command.d.ts.map +1 -0
  7. package/dist/command.js +2 -0
  8. package/dist/command.js.map +1 -0
  9. package/dist/commands/ai.d.ts +7 -0
  10. package/dist/commands/ai.d.ts.map +1 -0
  11. package/dist/commands/ai.js +148 -0
  12. package/dist/commands/ai.js.map +1 -0
  13. package/dist/commands/clean.d.ts +3 -0
  14. package/dist/commands/clean.d.ts.map +1 -0
  15. package/dist/commands/clean.js +72 -0
  16. package/dist/commands/clean.js.map +1 -0
  17. package/dist/commands/deps.d.ts +3 -0
  18. package/dist/commands/deps.d.ts.map +1 -0
  19. package/dist/commands/deps.js +98 -0
  20. package/dist/commands/deps.js.map +1 -0
  21. package/dist/commands/go.d.ts +3 -0
  22. package/dist/commands/go.d.ts.map +1 -0
  23. package/dist/commands/go.js +202 -0
  24. package/dist/commands/go.js.map +1 -0
  25. package/dist/commands/log.d.ts +3 -0
  26. package/dist/commands/log.d.ts.map +1 -0
  27. package/dist/commands/log.js +51 -0
  28. package/dist/commands/log.js.map +1 -0
  29. package/dist/commands/new.d.ts +3 -0
  30. package/dist/commands/new.d.ts.map +1 -0
  31. package/dist/commands/new.js +117 -0
  32. package/dist/commands/new.js.map +1 -0
  33. package/dist/commands/port.d.ts +4 -0
  34. package/dist/commands/port.d.ts.map +1 -0
  35. package/dist/commands/port.js +106 -0
  36. package/dist/commands/port.js.map +1 -0
  37. package/dist/commands/projects.d.ts +5 -0
  38. package/dist/commands/projects.d.ts.map +1 -0
  39. package/dist/commands/projects.js +46 -0
  40. package/dist/commands/projects.js.map +1 -0
  41. package/dist/commands/stack.d.ts +34 -0
  42. package/dist/commands/stack.d.ts.map +1 -0
  43. package/dist/commands/stack.js +140 -0
  44. package/dist/commands/stack.js.map +1 -0
  45. package/dist/commands/summary.d.ts +3 -0
  46. package/dist/commands/summary.d.ts.map +1 -0
  47. package/dist/commands/summary.js +88 -0
  48. package/dist/commands/summary.js.map +1 -0
  49. package/dist/commands/system.d.ts +5 -0
  50. package/dist/commands/system.d.ts.map +1 -0
  51. package/dist/commands/system.js +104 -0
  52. package/dist/commands/system.js.map +1 -0
  53. package/dist/commands/ui.d.ts +3 -0
  54. package/dist/commands/ui.d.ts.map +1 -0
  55. package/dist/commands/ui.js +96 -0
  56. package/dist/commands/ui.js.map +1 -0
  57. package/dist/index.d.ts +3 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +103 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/ui.d.ts +27 -0
  62. package/dist/ui.d.ts.map +1 -0
  63. package/dist/ui.js +104 -0
  64. package/dist/ui.js.map +1 -0
  65. package/node_modules/@dx/core/dist/config.d.ts +46 -0
  66. package/node_modules/@dx/core/dist/config.d.ts.map +1 -0
  67. package/node_modules/@dx/core/dist/config.js +131 -0
  68. package/node_modules/@dx/core/dist/config.js.map +1 -0
  69. package/node_modules/@dx/core/dist/fuzzy.d.ts +15 -0
  70. package/node_modules/@dx/core/dist/fuzzy.d.ts.map +1 -0
  71. package/node_modules/@dx/core/dist/fuzzy.js +63 -0
  72. package/node_modules/@dx/core/dist/fuzzy.js.map +1 -0
  73. package/node_modules/@dx/core/dist/index.d.ts +17 -0
  74. package/node_modules/@dx/core/dist/index.d.ts.map +1 -0
  75. package/node_modules/@dx/core/dist/index.js +19 -0
  76. package/node_modules/@dx/core/dist/index.js.map +1 -0
  77. package/node_modules/@dx/core/dist/runs.d.ts +19 -0
  78. package/node_modules/@dx/core/dist/runs.d.ts.map +1 -0
  79. package/node_modules/@dx/core/dist/runs.js +41 -0
  80. package/node_modules/@dx/core/dist/runs.js.map +1 -0
  81. package/node_modules/@dx/core/dist/tools/aiSetup.d.ts +58 -0
  82. package/node_modules/@dx/core/dist/tools/aiSetup.d.ts.map +1 -0
  83. package/node_modules/@dx/core/dist/tools/aiSetup.js +243 -0
  84. package/node_modules/@dx/core/dist/tools/aiSetup.js.map +1 -0
  85. package/node_modules/@dx/core/dist/tools/cleanup.d.ts +61 -0
  86. package/node_modules/@dx/core/dist/tools/cleanup.d.ts.map +1 -0
  87. package/node_modules/@dx/core/dist/tools/cleanup.js +171 -0
  88. package/node_modules/@dx/core/dist/tools/cleanup.js.map +1 -0
  89. package/node_modules/@dx/core/dist/tools/deps.d.ts +57 -0
  90. package/node_modules/@dx/core/dist/tools/deps.d.ts.map +1 -0
  91. package/node_modules/@dx/core/dist/tools/deps.js +151 -0
  92. package/node_modules/@dx/core/dist/tools/deps.js.map +1 -0
  93. package/node_modules/@dx/core/dist/tools/devErrors.d.ts +74 -0
  94. package/node_modules/@dx/core/dist/tools/devErrors.d.ts.map +1 -0
  95. package/node_modules/@dx/core/dist/tools/devErrors.js +261 -0
  96. package/node_modules/@dx/core/dist/tools/devErrors.js.map +1 -0
  97. package/node_modules/@dx/core/dist/tools/doctor.d.ts +12 -0
  98. package/node_modules/@dx/core/dist/tools/doctor.d.ts.map +1 -0
  99. package/node_modules/@dx/core/dist/tools/doctor.js +176 -0
  100. package/node_modules/@dx/core/dist/tools/doctor.js.map +1 -0
  101. package/node_modules/@dx/core/dist/tools/github.d.ts +31 -0
  102. package/node_modules/@dx/core/dist/tools/github.d.ts.map +1 -0
  103. package/node_modules/@dx/core/dist/tools/github.js +80 -0
  104. package/node_modules/@dx/core/dist/tools/github.js.map +1 -0
  105. package/node_modules/@dx/core/dist/tools/journal.d.ts +23 -0
  106. package/node_modules/@dx/core/dist/tools/journal.d.ts.map +1 -0
  107. package/node_modules/@dx/core/dist/tools/journal.js +80 -0
  108. package/node_modules/@dx/core/dist/tools/journal.js.map +1 -0
  109. package/node_modules/@dx/core/dist/tools/launch.d.ts +24 -0
  110. package/node_modules/@dx/core/dist/tools/launch.d.ts.map +1 -0
  111. package/node_modules/@dx/core/dist/tools/launch.js +137 -0
  112. package/node_modules/@dx/core/dist/tools/launch.js.map +1 -0
  113. package/node_modules/@dx/core/dist/tools/ports.d.ts +42 -0
  114. package/node_modules/@dx/core/dist/tools/ports.d.ts.map +1 -0
  115. package/node_modules/@dx/core/dist/tools/ports.js +220 -0
  116. package/node_modules/@dx/core/dist/tools/ports.js.map +1 -0
  117. package/node_modules/@dx/core/dist/tools/projects.d.ts +58 -0
  118. package/node_modules/@dx/core/dist/tools/projects.d.ts.map +1 -0
  119. package/node_modules/@dx/core/dist/tools/projects.js +208 -0
  120. package/node_modules/@dx/core/dist/tools/projects.js.map +1 -0
  121. package/node_modules/@dx/core/dist/tools/scaffold.d.ts +40 -0
  122. package/node_modules/@dx/core/dist/tools/scaffold.d.ts.map +1 -0
  123. package/node_modules/@dx/core/dist/tools/scaffold.js +188 -0
  124. package/node_modules/@dx/core/dist/tools/scaffold.js.map +1 -0
  125. package/node_modules/@dx/core/dist/tools/stacks.d.ts +119 -0
  126. package/node_modules/@dx/core/dist/tools/stacks.d.ts.map +1 -0
  127. package/node_modules/@dx/core/dist/tools/stacks.js +386 -0
  128. package/node_modules/@dx/core/dist/tools/stacks.js.map +1 -0
  129. package/node_modules/@dx/core/dist/tools/summary.d.ts +87 -0
  130. package/node_modules/@dx/core/dist/tools/summary.d.ts.map +1 -0
  131. package/node_modules/@dx/core/dist/tools/summary.js +331 -0
  132. package/node_modules/@dx/core/dist/tools/summary.js.map +1 -0
  133. package/node_modules/@dx/core/package.json +20 -0
  134. package/node_modules/@dx/mcp/dist/index.d.ts +3 -0
  135. package/node_modules/@dx/mcp/dist/index.d.ts.map +1 -0
  136. package/node_modules/@dx/mcp/dist/index.js +540 -0
  137. package/node_modules/@dx/mcp/dist/index.js.map +1 -0
  138. package/node_modules/@dx/mcp/package.json +8 -0
  139. package/node_modules/@dx/web/dist/api.d.ts +13 -0
  140. package/node_modules/@dx/web/dist/api.d.ts.map +1 -0
  141. package/node_modules/@dx/web/dist/api.js +351 -0
  142. package/node_modules/@dx/web/dist/api.js.map +1 -0
  143. package/node_modules/@dx/web/dist/background.d.ts +2 -0
  144. package/node_modules/@dx/web/dist/background.d.ts.map +1 -0
  145. package/node_modules/@dx/web/dist/background.js +24 -0
  146. package/node_modules/@dx/web/dist/background.js.map +1 -0
  147. package/node_modules/@dx/web/dist/check.d.ts +39 -0
  148. package/node_modules/@dx/web/dist/check.d.ts.map +1 -0
  149. package/node_modules/@dx/web/dist/check.js +97 -0
  150. package/node_modules/@dx/web/dist/check.js.map +1 -0
  151. package/node_modules/@dx/web/dist/dev.d.ts +109 -0
  152. package/node_modules/@dx/web/dist/dev.d.ts.map +1 -0
  153. package/node_modules/@dx/web/dist/dev.js +645 -0
  154. package/node_modules/@dx/web/dist/dev.js.map +1 -0
  155. package/node_modules/@dx/web/dist/link.d.ts +45 -0
  156. package/node_modules/@dx/web/dist/link.d.ts.map +1 -0
  157. package/node_modules/@dx/web/dist/link.js +150 -0
  158. package/node_modules/@dx/web/dist/link.js.map +1 -0
  159. package/node_modules/@dx/web/dist/proxy.d.ts +21 -0
  160. package/node_modules/@dx/web/dist/proxy.d.ts.map +1 -0
  161. package/node_modules/@dx/web/dist/proxy.js +140 -0
  162. package/node_modules/@dx/web/dist/proxy.js.map +1 -0
  163. package/node_modules/@dx/web/dist/server.d.ts +33 -0
  164. package/node_modules/@dx/web/dist/server.d.ts.map +1 -0
  165. package/node_modules/@dx/web/dist/server.js +184 -0
  166. package/node_modules/@dx/web/dist/server.js.map +1 -0
  167. package/node_modules/@dx/web/package.json +26 -0
  168. package/node_modules/@dx/web/public/app.js +1553 -0
  169. package/node_modules/@dx/web/public/icon.svg +1 -0
  170. package/node_modules/@dx/web/public/index.html +47 -0
  171. package/node_modules/@dx/web/public/styles.css +1124 -0
  172. package/package.json +52 -4
@@ -0,0 +1,42 @@
1
+ export interface PortEntry {
2
+ port: number;
3
+ pid: number;
4
+ /** Every local address this process listens on for the port, e.g. ["0.0.0.0", "::"]. */
5
+ addresses: string[];
6
+ processName?: string;
7
+ commandLine?: string;
8
+ /** True for the OS itself (PID 0 / 4 on Windows, 0 / 1 elsewhere). These are never killed. */
9
+ system: boolean;
10
+ }
11
+ /** Lists TCP ports that are listening, optionally only one port. Sorted by port. */
12
+ export declare function listListeningPorts(port?: number): Promise<PortEntry[]>;
13
+ export interface KillResult {
14
+ pid: number;
15
+ ok: boolean;
16
+ message: string;
17
+ }
18
+ /** Kills a process and its children. Refuses to touch system processes or dx itself. */
19
+ export declare function killProcess(pid: number): Promise<KillResult>;
20
+ /** Kills everything listening on a port. */
21
+ export declare function killPort(port: number): Promise<{
22
+ entries: PortEntry[];
23
+ results: KillResult[];
24
+ }>;
25
+ export declare function isSystemPid(pid: number): boolean;
26
+ /**
27
+ * Uses Get-NetTCPConnection rather than parsing `netstat`, because netstat's state column is
28
+ * translated on non-English Windows. Process names and command lines come from one CIM query.
29
+ */
30
+ export declare function windowsPortScript(port?: number): string;
31
+ interface RawEntry {
32
+ port: number;
33
+ address: string;
34
+ pid: number;
35
+ name?: string | null;
36
+ cmd?: string | null;
37
+ }
38
+ export declare function parseWindowsJson(stdout: string): RawEntry[];
39
+ export declare function parseLsof(stdout: string): RawEntry[];
40
+ export declare function mergeEntries(rows: RawEntry[]): PortEntry[];
41
+ export {};
42
+ //# sourceMappingURL=ports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../../src/tools/ports.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,wFAAwF;IACxF,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8FAA8F;IAC9F,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,oFAAoF;AACpF,wBAAsB,kBAAkB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAI5E;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wFAAwF;AACxF,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAqClE;AAED,4CAA4C;AAC5C,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IAAC,OAAO,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC,CASrG;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhD;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAyBvD;AAED,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAYD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,EAAE,CAe3D;AAmBD,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,EAAE,CAoBpD;AAmBD,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,SAAS,EAAE,CAmB1D"}
@@ -0,0 +1,220 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ const run = promisify(execFile);
4
+ /** Lists TCP ports that are listening, optionally only one port. Sorted by port. */
5
+ export async function listListeningPorts(port) {
6
+ if (port !== undefined)
7
+ assertPort(port);
8
+ const raw = process.platform === "win32" ? await listWindows(port) : await listUnix(port);
9
+ return mergeEntries(raw);
10
+ }
11
+ /** Kills a process and its children. Refuses to touch system processes or dx itself. */
12
+ export async function killProcess(pid) {
13
+ if (!Number.isInteger(pid) || pid <= 0)
14
+ return { pid, ok: false, message: `Not a valid PID: ${pid}` };
15
+ if (isSystemPid(pid))
16
+ return { pid, ok: false, message: `PID ${pid} belongs to the operating system; dx won't kill it.` };
17
+ if (pid === process.pid)
18
+ return { pid, ok: false, message: "That's dx itself." };
19
+ if (process.platform === "win32") {
20
+ try {
21
+ await run("taskkill", ["/PID", String(pid), "/T", "/F"], { windowsHide: true });
22
+ return { pid, ok: true, message: `Killed PID ${pid} and its child processes.` };
23
+ }
24
+ catch (err) {
25
+ const e = err;
26
+ const detail = (e.stderr || e.stdout || e.message).trim();
27
+ const hint = /denied|access/i.test(detail)
28
+ ? " The process may belong to another user or need admin rights: try again from a terminal opened as administrator."
29
+ : "";
30
+ return { pid, ok: false, message: `Couldn't kill PID ${pid}: ${detail}${hint}` };
31
+ }
32
+ }
33
+ try {
34
+ process.kill(pid, "SIGTERM");
35
+ }
36
+ catch (err) {
37
+ return { pid, ok: false, message: `Couldn't kill PID ${pid}: ${err.message}` };
38
+ }
39
+ for (let i = 0; i < 30; i++) {
40
+ await sleep(100);
41
+ if (!isAlive(pid))
42
+ return { pid, ok: true, message: `Stopped PID ${pid}.` };
43
+ }
44
+ try {
45
+ process.kill(pid, "SIGKILL");
46
+ }
47
+ catch {
48
+ // already gone
49
+ }
50
+ await sleep(100);
51
+ return isAlive(pid)
52
+ ? { pid, ok: false, message: `PID ${pid} is still running after SIGKILL.` }
53
+ : { pid, ok: true, message: `Force-killed PID ${pid}.` };
54
+ }
55
+ /** Kills everything listening on a port. */
56
+ export async function killPort(port) {
57
+ const entries = await listListeningPorts(port);
58
+ const pids = [...new Set(entries.filter((e) => !e.system).map((e) => e.pid))];
59
+ const results = [];
60
+ for (const pid of pids)
61
+ results.push(await killProcess(pid));
62
+ for (const e of entries.filter((x) => x.system)) {
63
+ results.push({ pid: e.pid, ok: false, message: `PID ${e.pid} belongs to the operating system; dx won't kill it.` });
64
+ }
65
+ return { entries, results };
66
+ }
67
+ export function isSystemPid(pid) {
68
+ return process.platform === "win32" ? pid === 0 || pid === 4 : pid === 0 || pid === 1;
69
+ }
70
+ // ---------------------------------------------------------------- Windows
71
+ /**
72
+ * Uses Get-NetTCPConnection rather than parsing `netstat`, because netstat's state column is
73
+ * translated on non-English Windows. Process names and command lines come from one CIM query.
74
+ */
75
+ export function windowsPortScript(port) {
76
+ const filter = port !== undefined ? ` -LocalPort ${port}` : "";
77
+ return `
78
+ [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
79
+ $ErrorActionPreference = 'SilentlyContinue'
80
+ $conns = @(Get-NetTCPConnection -State Listen${filter})
81
+ $ids = @($conns | ForEach-Object { [int]$_.OwningProcess } | Sort-Object -Unique)
82
+ $procs = @{}
83
+ if ($ids.Count -gt 0) {
84
+ $f = ($ids | ForEach-Object { "ProcessId=$_" }) -join ' OR '
85
+ Get-CimInstance Win32_Process -Filter $f | ForEach-Object { $procs[[int]$_.ProcessId] = $_ }
86
+ }
87
+ $list = New-Object System.Collections.Generic.List[object]
88
+ foreach ($c in $conns) {
89
+ $p = $procs[[int]$c.OwningProcess]
90
+ $list.Add([pscustomobject]@{
91
+ port = [int]$c.LocalPort
92
+ address = [string]$c.LocalAddress
93
+ pid = [int]$c.OwningProcess
94
+ name = $p.Name
95
+ cmd = $p.CommandLine
96
+ })
97
+ }
98
+ if ($list.Count -eq 0) { '[]' } else { ConvertTo-Json -InputObject $list.ToArray() -Compress -Depth 3 }
99
+ `;
100
+ }
101
+ async function listWindows(port) {
102
+ const encoded = Buffer.from(windowsPortScript(port), "utf16le").toString("base64");
103
+ const { stdout } = await run("powershell.exe", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-EncodedCommand", encoded], { windowsHide: true, maxBuffer: 16 * 1024 * 1024, encoding: "utf8" });
104
+ return parseWindowsJson(stdout);
105
+ }
106
+ export function parseWindowsJson(stdout) {
107
+ const text = stdout.replace(/^/, "").trim();
108
+ if (!text)
109
+ return [];
110
+ const parsed = JSON.parse(text);
111
+ const rows = Array.isArray(parsed) ? parsed : [parsed];
112
+ return rows
113
+ .filter((r) => Boolean(r) && typeof r === "object")
114
+ .map((r) => ({
115
+ port: Number(r.port),
116
+ address: String(r.address ?? ""),
117
+ pid: Number(r.pid),
118
+ name: typeof r.name === "string" ? r.name : null,
119
+ cmd: typeof r.cmd === "string" ? r.cmd : null,
120
+ }))
121
+ .filter((r) => Number.isFinite(r.port) && Number.isFinite(r.pid));
122
+ }
123
+ // ---------------------------------------------------------------- macOS / Linux
124
+ async function listUnix(port) {
125
+ const args = ["-nP", port !== undefined ? `-iTCP:${port}` : "-iTCP", "-sTCP:LISTEN", "-Fpcn"];
126
+ let stdout = "";
127
+ try {
128
+ ({ stdout } = await run("lsof", args, { maxBuffer: 16 * 1024 * 1024 }));
129
+ }
130
+ catch (err) {
131
+ const e = err;
132
+ if (e.code === "ENOENT")
133
+ throw new Error("lsof isn't installed, so dx can't list ports on this system.");
134
+ stdout = e.stdout ?? ""; // lsof exits 1 when nothing matches
135
+ }
136
+ const rows = parseLsof(stdout);
137
+ const cmds = await unixCommandLines([...new Set(rows.map((r) => r.pid))]);
138
+ return rows.map((r) => ({ ...r, cmd: cmds.get(r.pid) ?? null }));
139
+ }
140
+ export function parseLsof(stdout) {
141
+ const rows = [];
142
+ let pid = 0;
143
+ let name;
144
+ for (const line of stdout.split(/\r?\n/)) {
145
+ if (!line)
146
+ continue;
147
+ const tag = line[0];
148
+ const value = line.slice(1);
149
+ if (tag === "p") {
150
+ pid = Number(value);
151
+ name = undefined;
152
+ }
153
+ else if (tag === "c") {
154
+ name = value;
155
+ }
156
+ else if (tag === "n") {
157
+ const m = /^(.*):(\d+)$/.exec(value);
158
+ if (!m)
159
+ continue;
160
+ rows.push({ port: Number(m[2]), address: m[1].replace(/^\[|\]$/g, ""), pid, name: name ?? null });
161
+ }
162
+ }
163
+ return rows;
164
+ }
165
+ async function unixCommandLines(pids) {
166
+ const map = new Map();
167
+ if (pids.length === 0)
168
+ return map;
169
+ try {
170
+ const { stdout } = await run("ps", ["-o", "pid=,command=", "-p", pids.join(",")]);
171
+ for (const line of stdout.split("\n")) {
172
+ const m = /^\s*(\d+)\s+(.*)$/.exec(line);
173
+ if (m)
174
+ map.set(Number(m[1]), m[2]);
175
+ }
176
+ }
177
+ catch {
178
+ // command lines are a nice-to-have
179
+ }
180
+ return map;
181
+ }
182
+ // ---------------------------------------------------------------- shared
183
+ export function mergeEntries(rows) {
184
+ const byKey = new Map();
185
+ for (const r of rows) {
186
+ const key = `${r.port}:${r.pid}`;
187
+ const existing = byKey.get(key);
188
+ if (existing) {
189
+ if (r.address && !existing.addresses.includes(r.address))
190
+ existing.addresses.push(r.address);
191
+ continue;
192
+ }
193
+ byKey.set(key, {
194
+ port: r.port,
195
+ pid: r.pid,
196
+ addresses: r.address ? [r.address] : [],
197
+ processName: r.name ?? undefined,
198
+ commandLine: r.cmd ?? undefined,
199
+ system: isSystemPid(r.pid),
200
+ });
201
+ }
202
+ return [...byKey.values()].sort((a, b) => a.port - b.port || a.pid - b.pid);
203
+ }
204
+ function assertPort(port) {
205
+ if (!Number.isInteger(port) || port < 1 || port > 65535)
206
+ throw new Error(`Not a valid port: ${port}`);
207
+ }
208
+ function isAlive(pid) {
209
+ try {
210
+ process.kill(pid, 0);
211
+ return true;
212
+ }
213
+ catch (err) {
214
+ return err.code === "EPERM";
215
+ }
216
+ }
217
+ function sleep(ms) {
218
+ return new Promise((r) => setTimeout(r, ms));
219
+ }
220
+ //# sourceMappingURL=ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.js","sourceRoot":"","sources":["../../src/tools/ports.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import { type DxConfig } from "../config.js";
2
+ export type ProjectKind = "node" | "go" | "rust" | "python" | "dotnet" | "other";
3
+ export type PackageManager = "npm" | "pnpm" | "yarn" | "bun";
4
+ export interface Project {
5
+ name: string;
6
+ dir: string;
7
+ /** Which projects root it was found under. */
8
+ root: string;
9
+ kind: ProjectKind;
10
+ packageManager?: PackageManager;
11
+ /** Command that starts the project locally, from .devrc.json or detected. */
12
+ devCommand?: string;
13
+ /** Where the dev server will be, from .devrc.json or guessed from the dev script. */
14
+ url?: string;
15
+ /** True when `url` was guessed from the framework rather than set in .devrc.json. */
16
+ urlGuessed?: boolean;
17
+ /** Editor override from .devrc.json. */
18
+ editor?: string;
19
+ scripts: string[];
20
+ hasGit: boolean;
21
+ /** Last time something happened in the project (ms since epoch). */
22
+ lastActive: number;
23
+ }
24
+ /**
25
+ * Optional per-project file, `.devrc.json` in the project folder:
26
+ * { "dev": "npm run dev -- --turbo", "url": "http://localhost:3000", "name": "shop", "editor": "cursor" }
27
+ */
28
+ export interface DevRc {
29
+ name?: string;
30
+ dev?: string;
31
+ url?: string;
32
+ editor?: string;
33
+ }
34
+ export declare function scanProjects(config?: DxConfig): Project[];
35
+ /** Returns project info if `dir` looks like a project, otherwise undefined. */
36
+ export declare function readProject(dir: string, root?: string): Project | undefined;
37
+ interface PackageJson {
38
+ name?: string;
39
+ scripts?: Record<string, string>;
40
+ packageManager?: string;
41
+ }
42
+ export declare function detectPackageManager(dir: string, pkg?: PackageJson): PackageManager;
43
+ /**
44
+ * Best guess at the local URL from a dev script such as "next dev -p 3001" or "vite --port 5174".
45
+ * Only used to open the browser once that port is actually listening, so a wrong guess is harmless.
46
+ */
47
+ export declare function guessDevUrl(script: string): string | undefined;
48
+ export declare function getProjectPort(project: Project): number | undefined;
49
+ export interface FindResult {
50
+ /** Set when one project is a clear winner. */
51
+ match?: Project;
52
+ /** Ranked candidates (best first), for asking the user to pick. */
53
+ candidates: Project[];
54
+ }
55
+ /** Fuzzy-find projects by name. An empty query returns the most recently active ones. */
56
+ export declare function findProjects(query: string, projects: Project[]): FindResult;
57
+ export {};
58
+ //# sourceMappingURL=projects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/tools/projects.ts"],"names":[],"mappings":"AAEA,OAAO,EAAwB,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAGnE,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AACjF,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE7D,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;IAClB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qFAAqF;IACrF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qFAAqF;IACrF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,oEAAoE;IACpE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,KAAK;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAcD,wBAAgB,YAAY,CAAC,MAAM,GAAE,QAAuB,GAAG,OAAO,EAAE,CA4BvE;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,SAAoB,GAAG,OAAO,GAAG,SAAS,CAgDtF;AAED,UAAU,WAAW;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,WAAW,GAAG,cAAc,CAQnF;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK9D;AAcD,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CASnE;AAED,MAAM,WAAW,UAAU;IACzB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,mEAAmE;IACnE,UAAU,EAAE,OAAO,EAAE,CAAC;CACvB;AAED,yFAAyF;AACzF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,UAAU,CAW3E"}
@@ -0,0 +1,208 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { loadConfig, stripBom } from "../config.js";
4
+ import { fuzzyRank } from "../fuzzy.js";
5
+ const MARKERS = [
6
+ ["package.json", "node"],
7
+ ["go.mod", "go"],
8
+ ["Cargo.toml", "rust"],
9
+ ["pyproject.toml", "python"],
10
+ ["requirements.txt", "python"],
11
+ ["manage.py", "python"],
12
+ ];
13
+ /** Files that make a folder a project without telling its language (Rails, a stack of containers, …). */
14
+ const OTHER_MARKERS = ["Gemfile", "Procfile", "Procfile.dev", "compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml"];
15
+ export function scanProjects(config = loadConfig()) {
16
+ const found = new Map();
17
+ const ignore = new Set(config.ignore.map((n) => n.toLowerCase()));
18
+ for (const root of config.projectsRoots) {
19
+ if (!isDir(root))
20
+ continue;
21
+ walk(root, root, 1);
22
+ }
23
+ return [...found.values()].sort((a, b) => b.lastActive - a.lastActive);
24
+ function walk(dir, root, depth) {
25
+ let entries;
26
+ try {
27
+ entries = fs.readdirSync(dir, { withFileTypes: true });
28
+ }
29
+ catch {
30
+ return;
31
+ }
32
+ for (const entry of entries) {
33
+ if (!entry.isDirectory())
34
+ continue;
35
+ if (entry.name.startsWith(".") || ignore.has(entry.name.toLowerCase()))
36
+ continue;
37
+ const child = path.join(dir, entry.name);
38
+ const project = readProject(child, root);
39
+ if (project) {
40
+ found.set(child.toLowerCase(), project);
41
+ }
42
+ else if (depth < config.scanDepth) {
43
+ walk(child, root, depth + 1);
44
+ }
45
+ }
46
+ }
47
+ }
48
+ /** Returns project info if `dir` looks like a project, otherwise undefined. */
49
+ export function readProject(dir, root = path.dirname(dir)) {
50
+ const has = (f) => fs.existsSync(path.join(dir, f));
51
+ const hasGit = has(".git");
52
+ const devrc = readJson(path.join(dir, ".devrc.json"));
53
+ let kind = MARKERS.find(([f]) => has(f))?.[1];
54
+ if (!kind && hasDotnetProject(dir))
55
+ kind = "dotnet";
56
+ if (!kind && (hasGit || devrc || OTHER_MARKERS.some(has)))
57
+ kind = "other";
58
+ if (!kind)
59
+ return undefined;
60
+ const pkg = kind === "node" ? readJson(path.join(dir, "package.json")) : undefined;
61
+ const scripts = Object.keys(pkg?.scripts ?? {});
62
+ const packageManager = kind === "node" ? detectPackageManager(dir, pkg) : undefined;
63
+ let devCommand = devrc?.dev;
64
+ let devScript;
65
+ if (!devCommand) {
66
+ if (kind === "node" && pkg?.scripts) {
67
+ const script = ["dev", "start", "serve", "develop"].find((s) => pkg.scripts?.[s]);
68
+ if (script) {
69
+ devCommand = `${packageManager ?? "npm"} run ${script}`;
70
+ devScript = pkg.scripts[script];
71
+ }
72
+ }
73
+ else if (kind === "go")
74
+ devCommand = "go run .";
75
+ else if (kind === "rust")
76
+ devCommand = "cargo run";
77
+ else if (kind === "python" && has("manage.py"))
78
+ devCommand = "python manage.py runserver";
79
+ }
80
+ let url = devrc?.url;
81
+ let urlGuessed = false;
82
+ if (!url && devScript) {
83
+ url = guessDevUrl(devScript);
84
+ urlGuessed = Boolean(url);
85
+ }
86
+ return {
87
+ name: devrc?.name ?? path.basename(dir),
88
+ dir,
89
+ root,
90
+ kind,
91
+ packageManager,
92
+ devCommand,
93
+ url,
94
+ urlGuessed: url ? urlGuessed : undefined,
95
+ editor: devrc?.editor,
96
+ scripts,
97
+ hasGit,
98
+ lastActive: lastActivity(dir, hasGit),
99
+ };
100
+ }
101
+ export function detectPackageManager(dir, pkg) {
102
+ const declared = pkg?.packageManager?.split("@")[0];
103
+ if (declared === "pnpm" || declared === "yarn" || declared === "bun" || declared === "npm")
104
+ return declared;
105
+ const has = (f) => fs.existsSync(path.join(dir, f));
106
+ if (has("pnpm-lock.yaml"))
107
+ return "pnpm";
108
+ if (has("yarn.lock"))
109
+ return "yarn";
110
+ if (has("bun.lockb") || has("bun.lock"))
111
+ return "bun";
112
+ return "npm";
113
+ }
114
+ /**
115
+ * Best guess at the local URL from a dev script such as "next dev -p 3001" or "vite --port 5174".
116
+ * Only used to open the browser once that port is actually listening, so a wrong guess is harmless.
117
+ */
118
+ export function guessDevUrl(script) {
119
+ const explicit = /(?:^|\s)(?:-p|--port)(?:\s+|=)(\d{2,5})\b/.exec(script) ?? /\bPORT=(\d{2,5})\b/.exec(script);
120
+ const port = explicit ? Number(explicit[1]) : frameworkDefaultPort(script);
121
+ return port ? `http://localhost:${port}` : undefined;
122
+ }
123
+ function frameworkDefaultPort(script) {
124
+ const s = script.toLowerCase();
125
+ if (/\bnext\b/.test(s) || /\bnuxt\b/.test(s) || /\bremix dev\b/.test(s))
126
+ return 3000;
127
+ if (/\bvite\b/.test(s) || /react-router dev/.test(s) || /\bsvelte-kit dev\b/.test(s))
128
+ return 5173;
129
+ if (/\bastro\b/.test(s))
130
+ return 4321;
131
+ if (/\bng serve\b/.test(s))
132
+ return 4200;
133
+ if (/\bgatsby develop\b/.test(s))
134
+ return 8000;
135
+ if (/\bexpo start\b/.test(s))
136
+ return 8081;
137
+ if (/webpack(-dev-server| serve)/.test(s))
138
+ return 8080;
139
+ return undefined;
140
+ }
141
+ export function getProjectPort(project) {
142
+ if (!project.url)
143
+ return undefined;
144
+ try {
145
+ const u = new URL(project.url);
146
+ if (u.port)
147
+ return Number(u.port);
148
+ return u.protocol === "https:" ? 443 : 80;
149
+ }
150
+ catch {
151
+ return undefined;
152
+ }
153
+ }
154
+ /** Fuzzy-find projects by name. An empty query returns the most recently active ones. */
155
+ export function findProjects(query, projects) {
156
+ const q = query.trim();
157
+ if (!q)
158
+ return { candidates: projects };
159
+ const ranked = fuzzyRank(q, projects, (p) => [p.name, path.basename(p.dir)]);
160
+ // On equal scores prefer the project touched most recently.
161
+ ranked.sort((a, b) => b.score - a.score || b.item.lastActive - a.item.lastActive);
162
+ const candidates = ranked.map((r) => r.item);
163
+ if (ranked.length === 0)
164
+ return { candidates };
165
+ const [first, second] = ranked;
166
+ const clear = ranked.length === 1 || first.score >= 1000 || first.score - (second?.score ?? 0) >= 150;
167
+ return { match: clear ? first.item : undefined, candidates };
168
+ }
169
+ function lastActivity(dir, hasGit) {
170
+ const files = [dir, path.join(dir, "package.json")];
171
+ if (hasGit)
172
+ files.push(path.join(dir, ".git", "index"), path.join(dir, ".git", "HEAD"), path.join(dir, ".git", "FETCH_HEAD"));
173
+ let latest = 0;
174
+ for (const f of files) {
175
+ try {
176
+ latest = Math.max(latest, fs.statSync(f).mtimeMs);
177
+ }
178
+ catch {
179
+ // file not present
180
+ }
181
+ }
182
+ return latest;
183
+ }
184
+ function hasDotnetProject(dir) {
185
+ try {
186
+ return fs.readdirSync(dir).some((f) => f.endsWith(".sln") || f.endsWith(".csproj"));
187
+ }
188
+ catch {
189
+ return false;
190
+ }
191
+ }
192
+ function isDir(p) {
193
+ try {
194
+ return fs.statSync(p).isDirectory();
195
+ }
196
+ catch {
197
+ return false;
198
+ }
199
+ }
200
+ function readJson(file) {
201
+ try {
202
+ return JSON.parse(stripBom(fs.readFileSync(file, "utf8")));
203
+ }
204
+ catch {
205
+ return undefined;
206
+ }
207
+ }
208
+ //# sourceMappingURL=projects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/tools/projects.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { type DxConfig } from "../config.js";
2
+ /**
3
+ * `dx new`: start a project from a template folder (~/.dx/templates/<name>) or from a copy of an
4
+ * existing project. Copies leave out what belongs to the old project: git history, installed
5
+ * packages, build output and .env files with secrets (.env.example is kept).
6
+ */
7
+ export interface Template {
8
+ name: string;
9
+ dir: string;
10
+ }
11
+ export type SourceKind = "template" | "project" | "folder";
12
+ export interface Source {
13
+ kind: SourceKind;
14
+ name: string;
15
+ dir: string;
16
+ }
17
+ /** Placeholder replaced with the new project's name in template files and file names. */
18
+ export declare const NAME_PLACEHOLDER = "{{name}}";
19
+ export declare function templatesDir(config?: DxConfig): string;
20
+ export declare function listTemplates(config?: DxConfig): Template[];
21
+ /** A template by exact name, else a folder path, else a project with a clear name match. */
22
+ export declare function resolveSource(input: string, config?: DxConfig): Source | {
23
+ candidates: string[];
24
+ };
25
+ /** Lowercase letters, digits, dots, dashes and underscores: valid as a folder and an npm package name. */
26
+ export declare function isValidProjectName(name: string): boolean;
27
+ export interface CopyResult {
28
+ dir: string;
29
+ files: number;
30
+ /** Files whose {{name}} placeholder was filled in. */
31
+ filled: number;
32
+ }
33
+ /**
34
+ * Copies `source` to `dest` (which must not exist yet) and gives the copy its new name:
35
+ * package.json and .devrc.json get `name`, and template files get {{name}} filled in.
36
+ */
37
+ export declare function copyProject(source: Source, dest: string, name: string): CopyResult;
38
+ /** Saves a copy of a project as a template, leaving out the same things a copy does. */
39
+ export declare function saveAsTemplate(source: Source, templateName: string, config?: DxConfig): CopyResult;
40
+ //# sourceMappingURL=scaffold.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/tools/scaffold.ts"],"names":[],"mappings":"AAGA,OAAO,EAA4C,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAGvF;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE3D,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAyBD,yFAAyF;AACzF,eAAO,MAAM,gBAAgB,aAAa,CAAC;AAG3C,wBAAgB,YAAY,CAAC,MAAM,GAAE,QAAuB,GAAG,MAAM,CAEpE;AAED,wBAAgB,aAAa,CAAC,MAAM,GAAE,QAAuB,GAAG,QAAQ,EAAE,CAWzE;AAED,4FAA4F;AAC5F,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,QAAuB,GAAG,MAAM,GAAG;IAAE,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAQ/G;AAED,0GAA0G;AAC1G,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAiClF;AA2DD,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAE,QAAuB,GAAG,UAAU,CAGhH"}