padrone 1.9.0 → 2.0.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 (328) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +2 -2
  3. package/dist/codegen/code-builder.d.mts +10 -0
  4. package/dist/codegen/code-builder.d.mts.map +1 -0
  5. package/dist/codegen/code-builder.mjs +185 -0
  6. package/dist/codegen/code-builder.mjs.map +1 -0
  7. package/dist/codegen/discovery.d.mts +44 -0
  8. package/dist/codegen/discovery.d.mts.map +1 -0
  9. package/dist/codegen/discovery.mjs +179 -0
  10. package/dist/codegen/discovery.mjs.map +1 -0
  11. package/dist/codegen/file-emitter.d.mts +10 -0
  12. package/dist/codegen/file-emitter.d.mts.map +1 -0
  13. package/dist/codegen/file-emitter.mjs +58 -0
  14. package/dist/codegen/file-emitter.mjs.map +1 -0
  15. package/dist/codegen/generators/barrel-file.d.mts +10 -0
  16. package/dist/codegen/generators/barrel-file.d.mts.map +1 -0
  17. package/dist/codegen/generators/barrel-file.mjs +16 -0
  18. package/dist/codegen/generators/barrel-file.mjs.map +1 -0
  19. package/dist/codegen/generators/command-file.d.mts +25 -0
  20. package/dist/codegen/generators/command-file.d.mts.map +1 -0
  21. package/dist/codegen/generators/command-file.mjs +130 -0
  22. package/dist/codegen/generators/command-file.mjs.map +1 -0
  23. package/dist/codegen/generators/command-tree.d.mts +17 -0
  24. package/dist/codegen/generators/command-tree.d.mts.map +1 -0
  25. package/dist/codegen/generators/command-tree.mjs +72 -0
  26. package/dist/codegen/generators/command-tree.mjs.map +1 -0
  27. package/dist/codegen/index.d.mts +15 -330
  28. package/dist/codegen/index.mjs +13 -1507
  29. package/dist/codegen/parsers/bash.d.mts +19 -0
  30. package/dist/codegen/parsers/bash.d.mts.map +1 -0
  31. package/dist/codegen/parsers/bash.mjs +112 -0
  32. package/dist/codegen/parsers/bash.mjs.map +1 -0
  33. package/dist/codegen/parsers/fish.d.mts +13 -0
  34. package/dist/codegen/parsers/fish.d.mts.map +1 -0
  35. package/dist/codegen/parsers/fish.mjs +108 -0
  36. package/dist/codegen/parsers/fish.mjs.map +1 -0
  37. package/dist/codegen/parsers/help.d.mts +15 -0
  38. package/dist/codegen/parsers/help.d.mts.map +1 -0
  39. package/dist/codegen/parsers/help.mjs +271 -0
  40. package/dist/codegen/parsers/help.mjs.map +1 -0
  41. package/dist/codegen/parsers/merge.d.mts +14 -0
  42. package/dist/codegen/parsers/merge.d.mts.map +1 -0
  43. package/dist/codegen/parsers/merge.mjs +76 -0
  44. package/dist/codegen/parsers/merge.mjs.map +1 -0
  45. package/dist/codegen/parsers/zsh.d.mts +16 -0
  46. package/dist/codegen/parsers/zsh.d.mts.map +1 -0
  47. package/dist/codegen/parsers/zsh.mjs +161 -0
  48. package/dist/codegen/parsers/zsh.mjs.map +1 -0
  49. package/dist/codegen/schema-to-code.d.mts +24 -0
  50. package/dist/codegen/schema-to-code.d.mts.map +1 -0
  51. package/dist/codegen/schema-to-code.mjs +147 -0
  52. package/dist/codegen/schema-to-code.mjs.map +1 -0
  53. package/dist/codegen/template.d.mts +18 -0
  54. package/dist/codegen/template.d.mts.map +1 -0
  55. package/dist/codegen/template.mjs +37 -0
  56. package/dist/codegen/template.mjs.map +1 -0
  57. package/dist/codegen/types.d.mts +144 -0
  58. package/dist/codegen/types.d.mts.map +1 -0
  59. package/dist/completion.d.mts +2 -0
  60. package/dist/completion.mjs +2 -0
  61. package/dist/core/args.d.mts +3 -0
  62. package/dist/{args-WmyGc59s.mjs → core/args.mjs} +5 -81
  63. package/dist/core/args.mjs.map +1 -0
  64. package/dist/core/commands.d.mts +13 -0
  65. package/dist/core/commands.d.mts.map +1 -0
  66. package/dist/{commands-ohEApqIw.mjs → core/commands.mjs} +4 -232
  67. package/dist/core/commands.mjs.map +1 -0
  68. package/dist/core/create.d.mts +63 -0
  69. package/dist/core/create.d.mts.map +1 -0
  70. package/dist/core/create.mjs +230 -0
  71. package/dist/core/create.mjs.map +1 -0
  72. package/dist/core/default-runtime.mjs +198 -0
  73. package/dist/core/default-runtime.mjs.map +1 -0
  74. package/dist/core/errors.d.mts +107 -0
  75. package/dist/core/errors.d.mts.map +1 -0
  76. package/dist/{errors-DA4KzK1M.mjs → core/errors.mjs} +2 -2
  77. package/dist/core/errors.mjs.map +1 -0
  78. package/dist/core/exec.mjs +191 -0
  79. package/dist/core/exec.mjs.map +1 -0
  80. package/dist/core/interceptors.d.mts +33 -0
  81. package/dist/core/interceptors.d.mts.map +1 -0
  82. package/dist/core/interceptors.mjs +253 -0
  83. package/dist/core/interceptors.mjs.map +1 -0
  84. package/dist/core/parse.mjs +181 -0
  85. package/dist/core/parse.mjs.map +1 -0
  86. package/dist/core/program-methods.mjs +256 -0
  87. package/dist/core/program-methods.mjs.map +1 -0
  88. package/dist/core/results.d.mts +13 -0
  89. package/dist/core/results.d.mts.map +1 -0
  90. package/dist/core/results.mjs +150 -0
  91. package/dist/core/results.mjs.map +1 -0
  92. package/dist/core/runtime.d.mts +213 -0
  93. package/dist/core/runtime.d.mts.map +1 -0
  94. package/dist/core/runtime.mjs +10 -0
  95. package/dist/core/runtime.mjs.map +1 -0
  96. package/dist/core/validate.mjs +220 -0
  97. package/dist/core/validate.mjs.map +1 -0
  98. package/dist/docs/index.d.mts +1 -1
  99. package/dist/docs/index.d.mts.map +1 -1
  100. package/dist/docs/index.mjs +2 -2
  101. package/dist/docs/index.mjs.map +1 -1
  102. package/dist/extension/auto-output.d.mts +53 -0
  103. package/dist/extension/auto-output.d.mts.map +1 -0
  104. package/dist/extension/auto-output.mjs +116 -0
  105. package/dist/extension/auto-output.mjs.map +1 -0
  106. package/dist/extension/color.d.mts +19 -0
  107. package/dist/extension/color.d.mts.map +1 -0
  108. package/dist/extension/color.mjs +37 -0
  109. package/dist/extension/color.mjs.map +1 -0
  110. package/dist/extension/completion.d.mts +25 -0
  111. package/dist/extension/completion.d.mts.map +1 -0
  112. package/dist/extension/completion.mjs +40 -0
  113. package/dist/extension/completion.mjs.map +1 -0
  114. package/dist/extension/config.d.mts +55 -0
  115. package/dist/extension/config.d.mts.map +1 -0
  116. package/dist/extension/config.mjs +162 -0
  117. package/dist/extension/config.mjs.map +1 -0
  118. package/dist/extension/env.d.mts +42 -0
  119. package/dist/extension/env.d.mts.map +1 -0
  120. package/dist/extension/env.mjs +44 -0
  121. package/dist/extension/env.mjs.map +1 -0
  122. package/dist/extension/help.d.mts +29 -0
  123. package/dist/extension/help.d.mts.map +1 -0
  124. package/dist/extension/help.mjs +161 -0
  125. package/dist/extension/help.mjs.map +1 -0
  126. package/dist/extension/index.d.mts +18 -0
  127. package/dist/extension/ink.d.mts +31 -0
  128. package/dist/extension/ink.d.mts.map +1 -0
  129. package/dist/extension/ink.mjs +69 -0
  130. package/dist/extension/ink.mjs.map +1 -0
  131. package/dist/extension/interactive.d.mts +16 -0
  132. package/dist/extension/interactive.d.mts.map +1 -0
  133. package/dist/extension/interactive.mjs +89 -0
  134. package/dist/extension/interactive.mjs.map +1 -0
  135. package/dist/extension/logger.d.mts +67 -0
  136. package/dist/extension/logger.d.mts.map +1 -0
  137. package/dist/extension/logger.mjs +188 -0
  138. package/dist/extension/logger.mjs.map +1 -0
  139. package/dist/extension/man.d.mts +25 -0
  140. package/dist/extension/man.d.mts.map +1 -0
  141. package/dist/extension/man.mjs +41 -0
  142. package/dist/extension/man.mjs.map +1 -0
  143. package/dist/extension/mcp.d.mts +28 -0
  144. package/dist/extension/mcp.d.mts.map +1 -0
  145. package/dist/extension/mcp.mjs +44 -0
  146. package/dist/extension/mcp.mjs.map +1 -0
  147. package/dist/extension/progress-renderer.d.mts +13 -0
  148. package/dist/extension/progress-renderer.d.mts.map +1 -0
  149. package/dist/extension/progress-renderer.mjs +351 -0
  150. package/dist/extension/progress-renderer.mjs.map +1 -0
  151. package/dist/extension/progress.d.mts +76 -0
  152. package/dist/extension/progress.d.mts.map +1 -0
  153. package/dist/extension/progress.mjs +225 -0
  154. package/dist/extension/progress.mjs.map +1 -0
  155. package/dist/extension/repl.d.mts +26 -0
  156. package/dist/extension/repl.d.mts.map +1 -0
  157. package/dist/extension/repl.mjs +67 -0
  158. package/dist/extension/repl.mjs.map +1 -0
  159. package/dist/extension/serve.d.mts +27 -0
  160. package/dist/extension/serve.d.mts.map +1 -0
  161. package/dist/extension/serve.mjs +41 -0
  162. package/dist/extension/serve.mjs.map +1 -0
  163. package/dist/extension/signal.d.mts +19 -0
  164. package/dist/extension/signal.d.mts.map +1 -0
  165. package/dist/extension/signal.mjs +81 -0
  166. package/dist/extension/signal.mjs.map +1 -0
  167. package/dist/extension/stdin.d.mts +19 -0
  168. package/dist/extension/stdin.d.mts.map +1 -0
  169. package/dist/extension/stdin.mjs +52 -0
  170. package/dist/extension/stdin.mjs.map +1 -0
  171. package/dist/extension/suggestions.d.mts +6 -0
  172. package/dist/extension/suggestions.d.mts.map +1 -0
  173. package/dist/extension/suggestions.mjs +113 -0
  174. package/dist/extension/suggestions.mjs.map +1 -0
  175. package/dist/extension/timing.d.mts +27 -0
  176. package/dist/extension/timing.d.mts.map +1 -0
  177. package/dist/extension/timing.mjs +71 -0
  178. package/dist/extension/timing.mjs.map +1 -0
  179. package/dist/extension/tracing.d.mts +85 -0
  180. package/dist/extension/tracing.d.mts.map +1 -0
  181. package/dist/extension/tracing.mjs +107 -0
  182. package/dist/extension/tracing.mjs.map +1 -0
  183. package/dist/extension/update-check.d.mts +20 -0
  184. package/dist/extension/update-check.d.mts.map +1 -0
  185. package/dist/extension/update-check.mjs +62 -0
  186. package/dist/extension/update-check.mjs.map +1 -0
  187. package/dist/extension/utils.mjs +43 -0
  188. package/dist/extension/utils.mjs.map +1 -0
  189. package/dist/extension/version.d.mts +20 -0
  190. package/dist/extension/version.d.mts.map +1 -0
  191. package/dist/extension/version.mjs +51 -0
  192. package/dist/extension/version.mjs.map +1 -0
  193. package/dist/{completion-D8qkAinX.mjs → feature/completion.mjs} +4 -3
  194. package/dist/feature/completion.mjs.map +1 -0
  195. package/dist/feature/interactive.mjs +145 -0
  196. package/dist/feature/interactive.mjs.map +1 -0
  197. package/dist/feature/mcp.d.mts +18 -0
  198. package/dist/feature/mcp.d.mts.map +1 -0
  199. package/dist/{mcp-wCoVFTXz.mjs → feature/mcp.mjs} +6 -6
  200. package/dist/feature/mcp.mjs.map +1 -0
  201. package/dist/feature/repl-loop.mjs +217 -0
  202. package/dist/feature/repl-loop.mjs.map +1 -0
  203. package/dist/feature/serve.d.mts +18 -0
  204. package/dist/feature/serve.d.mts.map +1 -0
  205. package/dist/{serve-ICZFl3xr.mjs → feature/serve.mjs} +7 -7
  206. package/dist/feature/serve.mjs.map +1 -0
  207. package/dist/feature/test.d.mts +109 -0
  208. package/dist/feature/test.d.mts.map +1 -0
  209. package/dist/feature/test.mjs +116 -0
  210. package/dist/feature/test.mjs.map +1 -0
  211. package/dist/feature/update-check.d.mts +35 -0
  212. package/dist/feature/update-check.d.mts.map +1 -0
  213. package/dist/{update-check-CZ2VqjnV.mjs → feature/update-check.mjs} +2 -2
  214. package/dist/feature/update-check.mjs.map +1 -0
  215. package/dist/feature/wrap.d.mts +58 -0
  216. package/dist/feature/wrap.d.mts.map +1 -0
  217. package/dist/feature/wrap.mjs +77 -0
  218. package/dist/feature/wrap.mjs.map +1 -0
  219. package/dist/index.d.mts +38 -902
  220. package/dist/index.mjs +25 -4377
  221. package/dist/ink.d.mts +2 -0
  222. package/dist/ink.mjs +2 -0
  223. package/dist/man.d.mts +2 -0
  224. package/dist/man.mjs +2 -0
  225. package/dist/mcp.d.mts +3 -0
  226. package/dist/mcp.mjs +2 -0
  227. package/dist/output/colorizer.d.mts +28 -0
  228. package/dist/output/colorizer.d.mts.map +1 -0
  229. package/dist/output/colorizer.mjs +94 -0
  230. package/dist/output/colorizer.mjs.map +1 -0
  231. package/dist/{formatter-CY3KrOEd.d.mts → output/formatter.d.mts} +2 -28
  232. package/dist/output/formatter.d.mts.map +1 -0
  233. package/dist/output/formatter.mjs +360 -0
  234. package/dist/output/formatter.mjs.map +1 -0
  235. package/dist/output/help.d.mts +18 -0
  236. package/dist/output/help.d.mts.map +1 -0
  237. package/dist/output/help.mjs +267 -0
  238. package/dist/output/help.mjs.map +1 -0
  239. package/dist/output/output-indicator.d.mts +27 -0
  240. package/dist/output/output-indicator.d.mts.map +1 -0
  241. package/dist/output/output-indicator.mjs +54 -0
  242. package/dist/output/output-indicator.mjs.map +1 -0
  243. package/dist/output/primitives.d.mts +33 -0
  244. package/dist/output/primitives.d.mts.map +1 -0
  245. package/dist/output/primitives.mjs +206 -0
  246. package/dist/output/primitives.mjs.map +1 -0
  247. package/dist/output/styling.d.mts +38 -0
  248. package/dist/output/styling.d.mts.map +1 -0
  249. package/dist/output/styling.mjs +146 -0
  250. package/dist/output/styling.mjs.map +1 -0
  251. package/dist/schema/zod.d.mts +6 -0
  252. package/dist/schema/zod.d.mts.map +1 -0
  253. package/dist/schema/zod.mjs +50 -0
  254. package/dist/schema/zod.mjs.map +1 -0
  255. package/dist/serve.d.mts +2 -0
  256. package/dist/serve.mjs +2 -0
  257. package/dist/test.d.mts +2 -109
  258. package/dist/test.mjs +1 -115
  259. package/dist/tracing.d.mts +2 -0
  260. package/dist/tracing.mjs +2 -0
  261. package/dist/types/args-meta.d.mts +129 -0
  262. package/dist/types/args-meta.d.mts.map +1 -0
  263. package/dist/types/builder.d.mts +212 -0
  264. package/dist/types/builder.d.mts.map +1 -0
  265. package/dist/types/command.d.mts +112 -0
  266. package/dist/types/command.d.mts.map +1 -0
  267. package/dist/types/index.d.mts +8 -0
  268. package/dist/types/interceptor.d.mts +232 -0
  269. package/dist/types/interceptor.d.mts.map +1 -0
  270. package/dist/types/preferences.d.mts +79 -0
  271. package/dist/types/preferences.d.mts.map +1 -0
  272. package/dist/types/result.d.mts +61 -0
  273. package/dist/types/result.d.mts.map +1 -0
  274. package/dist/types/schema.d.mts +24 -0
  275. package/dist/types/schema.d.mts.map +1 -0
  276. package/dist/util/dotenv.mjs +174 -0
  277. package/dist/util/dotenv.mjs.map +1 -0
  278. package/dist/util/shell-utils.mjs +80 -0
  279. package/dist/util/shell-utils.mjs.map +1 -0
  280. package/dist/util/stream.d.mts +33 -0
  281. package/dist/util/stream.d.mts.map +1 -0
  282. package/dist/{stream-DC4H8YTx.mjs → util/stream.mjs} +2 -2
  283. package/dist/util/stream.mjs.map +1 -0
  284. package/dist/util/type-helpers.d.mts +74 -0
  285. package/dist/util/type-helpers.d.mts.map +1 -0
  286. package/dist/util/type-utils.d.mts +186 -0
  287. package/dist/util/type-utils.d.mts.map +1 -0
  288. package/dist/util/utils.mjs +43 -0
  289. package/dist/util/utils.mjs.map +1 -0
  290. package/dist/zod.d.mts +1 -32
  291. package/dist/zod.mjs +1 -49
  292. package/package.json +65 -17
  293. package/src/completion.ts +2 -0
  294. package/src/extension/completion.ts +3 -0
  295. package/src/extension/index.ts +0 -12
  296. package/src/extension/ink.ts +2 -1
  297. package/src/extension/man.ts +3 -0
  298. package/src/extension/mcp.ts +3 -0
  299. package/src/extension/serve.ts +3 -0
  300. package/src/extension/tracing.ts +2 -0
  301. package/src/index.ts +0 -19
  302. package/src/ink.ts +2 -0
  303. package/src/man.ts +2 -0
  304. package/src/mcp.ts +3 -0
  305. package/src/serve.ts +2 -0
  306. package/src/tracing.ts +9 -0
  307. package/src/zod.ts +1 -1
  308. package/dist/args-WmyGc59s.mjs.map +0 -1
  309. package/dist/codegen/index.d.mts.map +0 -1
  310. package/dist/codegen/index.mjs.map +0 -1
  311. package/dist/commands-ohEApqIw.mjs.map +0 -1
  312. package/dist/completion-D8qkAinX.mjs.map +0 -1
  313. package/dist/errors-DA4KzK1M.mjs.map +0 -1
  314. package/dist/formatter-CY3KrOEd.d.mts.map +0 -1
  315. package/dist/help-CeI45CJx.mjs +0 -892
  316. package/dist/help-CeI45CJx.mjs.map +0 -1
  317. package/dist/index-C3Ed1LYN.d.mts +0 -1432
  318. package/dist/index-C3Ed1LYN.d.mts.map +0 -1
  319. package/dist/index.d.mts.map +0 -1
  320. package/dist/index.mjs.map +0 -1
  321. package/dist/mcp-wCoVFTXz.mjs.map +0 -1
  322. package/dist/serve-ICZFl3xr.mjs.map +0 -1
  323. package/dist/stream-DC4H8YTx.mjs.map +0 -1
  324. package/dist/test.d.mts.map +0 -1
  325. package/dist/test.mjs.map +0 -1
  326. package/dist/update-check-CZ2VqjnV.mjs.map +0 -1
  327. package/dist/zod.d.mts.map +0 -1
  328. package/dist/zod.mjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,4377 +1,25 @@
1
- import { _ as resolveStdin, c as getCommandRuntime, d as repathCommandTree, f as resolveAllCommands, g as createTerminalReplSession, h as suggestSimilar, i as commandSymbol, l as lazyResolver, n as buildReplCompleter, o as findCommandByName, p as resolveCommand, u as mergeCommands, v as resolveStdinAlways, y as REPL_SIGINT } from "./commands-ohEApqIw.mjs";
2
- import { a as getJsonSchema, c as parsePositionalConfig, i as extractSchemaMetadata, l as preprocessArgs, n as coerceArgs, o as isArrayField, r as detectUnknownArgs, s as isAsyncStreamField, t as applyValues } from "./args-WmyGc59s.mjs";
3
- import { i as createStdinStream, r as concatBytes, t as asyncStream } from "./stream-DC4H8YTx.mjs";
4
- import { a as escapeHtml, i as getVersion, o as resolveOutputFormat, r as getRootCommand, s as colorThemes, t as generateHelp } from "./help-CeI45CJx.mjs";
5
- import { a as SignalError, i as RoutingError, n as ConfigError, o as ValidationError, r as PadroneError, s as signalExitCode, t as ActionError } from "./errors-DA4KzK1M.mjs";
6
- //#region src/core/results.ts
7
- /**
8
- * Brands a schema as async, signaling that its `validate()` may return a Promise.
9
- * When an async-branded schema is passed to `.arguments()`, `.configFile()`, or `.env()`,
10
- * the command's `parse()` and `cli()` will return Promises.
11
- */
12
- function asyncSchema(schema) {
13
- return Object.assign(schema, { "~async": true });
14
- }
15
- const noop = () => void 0;
16
- /**
17
- * Maps over a value that may or may not be a Promise.
18
- * If the value is a Promise, chains with `.then()`. Otherwise, calls the function synchronously.
19
- * This preserves sync behavior for sync schemas and async behavior for async schemas.
20
- */
21
- function thenMaybe(value, fn) {
22
- if (value instanceof Promise) return value.then(fn);
23
- return fn(value);
24
- }
25
- /**
26
- * Makes a sync result object thenable by adding `.then()`, `.catch()`, and `.finally()` methods.
27
- * If the value is already a Promise, returns it as-is.
28
- * This allows users to write `await program.cli()` or `program.cli().then(...)` regardless of sync/async.
29
- *
30
- * The `.then()` resolves with a plain copy (without thenable methods) to avoid infinite
31
- * recursive unwrapping by the Promise resolution algorithm.
32
- */
33
- function makeThenable(value) {
34
- if (value instanceof Promise) return value;
35
- if (value !== null && typeof value === "object" && !("then" in value)) {
36
- const toPlain = () => {
37
- const plain = { ...value };
38
- delete plain.then;
39
- delete plain.catch;
40
- delete plain.finally;
41
- return plain;
42
- };
43
- value.then = (onfulfilled, onrejected) => {
44
- try {
45
- const result = onfulfilled ? onfulfilled(toPlain()) : toPlain();
46
- return Promise.resolve(result);
47
- } catch (err) {
48
- if (onrejected) return Promise.resolve(onrejected(err));
49
- return Promise.reject(err);
50
- }
51
- };
52
- value.catch = (onrejected) => value.then(void 0, onrejected);
53
- value.finally = (onfinally) => value.then((v) => {
54
- onfinally?.();
55
- return v;
56
- }, (err) => {
57
- onfinally?.();
58
- throw err;
59
- });
60
- }
61
- return value;
62
- }
63
- /**
64
- * Wraps a Promise to include a `drain()` method at the top level.
65
- * This allows `await promise.drain()` without first awaiting the promise.
66
- * Since cli/eval never reject, this just delegates to the resolved result's `drain()`.
67
- */
68
- function withPromiseDrain(promise) {
69
- promise.drain = async () => {
70
- return (await promise).drain();
71
- };
72
- return promise;
73
- }
74
- function isIterator(value) {
75
- return typeof value === "object" && value !== null && Symbol.iterator in value && typeof value[Symbol.iterator] === "function";
76
- }
77
- function isAsyncIterator(value) {
78
- return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
79
- }
80
- /**
81
- * Resolves a result value by unwrapping Promises and collecting iterables into arrays.
82
- * This is the runtime counterpart of the `Drained<T>` type.
83
- */
84
- async function drainValue(value) {
85
- if (value instanceof Promise) return drainValue(await value);
86
- if (isAsyncIterator(value)) {
87
- const items = [];
88
- const iter = value[Symbol.asyncIterator]();
89
- while (true) {
90
- const { done, value: item } = await iter.next();
91
- if (done) break;
92
- items.push(item);
93
- }
94
- return items;
95
- }
96
- if (typeof value !== "string" && !Array.isArray(value) && isIterator(value)) {
97
- const items = [];
98
- const iter = value[Symbol.iterator]();
99
- while (true) {
100
- const { done, value: item } = iter.next();
101
- if (done) break;
102
- items.push(item);
103
- }
104
- return items;
105
- }
106
- return value;
107
- }
108
- /**
109
- * Attaches a `drain()` method to a command result object.
110
- * If the result has an `error` field, `drain()` returns `{ error }`.
111
- * Otherwise, resolves the result (unwrapping Promises, collecting iterables), catches errors,
112
- * and returns a discriminated union `{ value } | { error }` that never throws.
113
- */
114
- function withDrain(obj) {
115
- obj.drain = async () => {
116
- if ("error" in obj && obj.error !== void 0) return { error: obj.error };
117
- try {
118
- return { value: await drainValue(obj.result) };
119
- } catch (err) {
120
- return { error: err };
121
- }
122
- };
123
- return obj;
124
- }
125
- /**
126
- * Creates an error command result with a `drain()` that returns the error.
127
- */
128
- function errorResult(error, partial) {
129
- return withDrain({
130
- error,
131
- result: void 0,
132
- command: partial?.command,
133
- args: partial?.args,
134
- argsResult: partial?.argsResult
135
- });
136
- }
137
- function isAsyncBranded(schema) {
138
- return !!schema && typeof schema === "object" && "~async" in schema && schema["~async"] === true;
139
- }
140
- function hasInteractiveConfig(meta) {
141
- if (!meta || typeof meta !== "object") return false;
142
- const m = meta;
143
- return m.interactive === true || Array.isArray(m.interactive) || m.optionalInteractive === true || Array.isArray(m.optionalInteractive);
144
- }
145
- function warnIfUnexpectedAsync(value, command) {
146
- const runtime = getCommandRuntime(command);
147
- if (runtime.env().NODE_ENV === "production") return value;
148
- if (value instanceof Promise && !command.isAsync) runtime.error(`[padrone] Command "${command.path || command.name}" returned a Promise from validation, but was not marked as async. Use \`.async()\` on the builder or \`asyncSchema()\` to brand your schema. Without this, TypeScript will infer a sync return type and the result will be a Promise at runtime.`);
149
- return value;
150
- }
151
- //#endregion
152
- //#region src/core/interceptors.ts
153
- function buildInterceptorFn(meta, factory) {
154
- Object.defineProperty(factory, "name", {
155
- value: meta.name,
156
- configurable: true
157
- });
158
- if (meta.id !== void 0) factory.id = meta.id;
159
- if (meta.order !== void 0) factory.order = meta.order;
160
- if (meta.disabled !== void 0) factory.disabled = meta.disabled;
161
- if (meta.inherit !== void 0) factory.inherit = meta.inherit;
162
- factory.provides = () => factory;
163
- factory.requires = () => factory;
164
- return factory;
165
- }
166
- function defineInterceptor(meta, factory) {
167
- if (factory) return buildInterceptorFn(meta, factory);
168
- const builder = {
169
- requires: () => builder,
170
- factory: (f) => buildInterceptorFn(meta, f)
171
- };
172
- return builder;
173
- }
174
- /**
175
- * Normalizes an interceptor input (single-value form or two-arg form) into the internal
176
- * `RegisteredInterceptor` storage format.
177
- */
178
- function toRegisteredInterceptor(metaOrFn, factory) {
179
- if (typeof metaOrFn === "function") return {
180
- meta: {
181
- name: metaOrFn.name,
182
- id: metaOrFn.id,
183
- order: metaOrFn.order,
184
- disabled: metaOrFn.disabled,
185
- inherit: metaOrFn.inherit
186
- },
187
- factory: metaOrFn
188
- };
189
- return {
190
- meta: metaOrFn,
191
- factory
192
- };
193
- }
194
- /**
195
- * Resolves registered interceptors by calling their factories and merging the resulting
196
- * phase handlers with the static metadata. Uses a cache to ensure each factory is called
197
- * at most once per execution (so root interceptor closures are shared across all phases).
198
- */
199
- function resolveRegisteredInterceptors(registered, cache) {
200
- return registered.map((reg) => {
201
- let resolved = cache.get(reg);
202
- if (!resolved) {
203
- resolved = {
204
- ...reg.meta,
205
- ...reg.factory()
206
- };
207
- cache.set(reg, resolved);
208
- }
209
- return resolved;
210
- });
211
- }
212
- /**
213
- * Deduplicates interceptors by `id`. When multiple interceptors share the same `id`,
214
- * only the last one in the array is kept. Interceptors without an `id` are always kept.
215
- */
216
- function deduplicateInterceptors(interceptors) {
217
- if (!interceptors.some((p) => p.id)) return interceptors;
218
- const lastIndex = /* @__PURE__ */ new Map();
219
- for (let i = 0; i < interceptors.length; i++) {
220
- const id = interceptors[i].id;
221
- if (id) lastIndex.set(id, i);
222
- }
223
- return interceptors.filter((p, i) => !p.id || lastIndex.get(p.id) === i);
224
- }
225
- /**
226
- * Runs an interceptor chain for a given phase using the onion/middleware pattern.
227
- * Interceptors are sorted by `order` (ascending, stable), then composed so that
228
- * the first interceptor in sorted order is the outermost wrapper.
229
- * If no interceptors handle this phase, `core` is called directly.
230
- *
231
- * Each interceptor's `next()` accepts optional partial overrides that are merged
232
- * into the context before passing to the next interceptor or core function.
233
- */
234
- function runInterceptorChain(phase, interceptors, ctx, core) {
235
- const phaseInterceptors = deduplicateInterceptors(interceptors).filter((p) => p[phase] && !p.disabled);
236
- if (phaseInterceptors.length === 0) return core(ctx);
237
- phaseInterceptors.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
238
- let next = core;
239
- for (let i = phaseInterceptors.length - 1; i >= 0; i--) {
240
- const handler = phaseInterceptors[i][phase];
241
- const prevNext = next;
242
- next = (currentCtx) => handler(currentCtx, (overrides) => {
243
- if (!overrides) return prevNext(currentCtx);
244
- if (overrides.context != null && typeof overrides.context === "object") overrides = {
245
- ...overrides,
246
- context: Object.assign({}, currentCtx.context, overrides.context)
247
- };
248
- return prevNext(Object.assign({}, currentCtx, overrides));
249
- });
250
- }
251
- return next(ctx);
252
- }
253
- /**
254
- * Wraps a pipeline with start → error → shutdown lifecycle hooks.
255
- * - `start` interceptors wrap the pipeline (onion pattern, root interceptors only).
256
- * - On error: `error` interceptors run (can transform/suppress the error).
257
- * - Always: `shutdown` interceptors run (success or failure).
258
- */
259
- function wrapWithLifecycle(interceptors, command, input, pipeline, wrapErrorResult, signal, context, runtime, program, caller = "eval", pipelineState) {
260
- const defaultSignal = typeof AbortSignal !== "undefined" ? AbortSignal.abort() : void 0;
261
- const hasStart = interceptors.some((p) => p.start);
262
- const hasError = interceptors.some((p) => p.error);
263
- const hasShutdown = interceptors.some((p) => p.shutdown);
264
- const effectivePipelineState = pipelineState ?? { phase: "start" };
265
- if (!hasStart && !hasError && !hasShutdown) return pipeline(signal ?? defaultSignal, context);
266
- let effectiveSignal = signal ?? defaultSignal;
267
- let effectiveContext = context;
268
- const runShutdown = (error, result) => {
269
- if (!hasShutdown) return;
270
- return runInterceptorChain("shutdown", interceptors, {
271
- command,
272
- input,
273
- error,
274
- result,
275
- signal: effectiveSignal,
276
- context: effectiveContext,
277
- runtime,
278
- program,
279
- caller,
280
- ...effectivePipelineState
281
- }, () => {});
282
- };
283
- const runError = (error) => {
284
- if (!hasError) {
285
- const s = runShutdown(error);
286
- if (s instanceof Promise) return s.then(() => {
287
- throw error;
288
- });
289
- throw error;
290
- }
291
- return thenMaybe(runInterceptorChain("error", interceptors, {
292
- command,
293
- input,
294
- error,
295
- signal: effectiveSignal,
296
- context: effectiveContext,
297
- runtime,
298
- program,
299
- caller,
300
- ...effectivePipelineState
301
- }, () => ({ error })), (er) => {
302
- if (er.error !== void 0) return thenMaybe(runShutdown(er.error), () => {
303
- throw er.error;
304
- });
305
- const wrapped = wrapErrorResult ? wrapErrorResult(er.result) : er.result;
306
- return thenMaybe(runShutdown(void 0, wrapped), () => wrapped);
307
- });
308
- };
309
- const handleSuccess = (result) => {
310
- const s = runShutdown(void 0, result);
311
- if (s instanceof Promise) return s.then(() => result);
312
- return result;
313
- };
314
- const startCtx = {
315
- command,
316
- signal: effectiveSignal,
317
- context: effectiveContext,
318
- runtime,
319
- program,
320
- input,
321
- caller
322
- };
323
- let result;
324
- try {
325
- result = hasStart ? runInterceptorChain("start", interceptors, startCtx, (ctx) => {
326
- effectiveSignal = ctx.signal;
327
- effectiveContext = ctx.context;
328
- return pipeline(ctx.signal, ctx.context);
329
- }) : pipeline(effectiveSignal, effectiveContext);
330
- } catch (e) {
331
- return runError(e);
332
- }
333
- if (result instanceof Promise) return result.then(handleSuccess, runError);
334
- return handleSuccess(result);
335
- }
336
- /**
337
- * Wraps a command-level pipeline (validate + execute) with error → shutdown lifecycle hooks.
338
- * Unlike `wrapWithLifecycle`, this has no `start` phase and uses the resolved command context.
339
- * Only interceptors exclusive to the command chain (not in root) should be passed here.
340
- */
341
- function wrapWithCommandLifecycle(interceptors, command, input, pipeline, wrapErrorResult, signal, context, runtime, program, caller, pipelineState) {
342
- const hasError = interceptors.some((p) => p.error);
343
- const hasShutdown = interceptors.some((p) => p.shutdown);
344
- if (!hasError && !hasShutdown) return pipeline();
345
- const runShutdown = (error, result) => {
346
- if (!hasShutdown) return;
347
- return runInterceptorChain("shutdown", interceptors, {
348
- command,
349
- input,
350
- error,
351
- result,
352
- signal,
353
- context,
354
- runtime,
355
- program,
356
- caller,
357
- ...pipelineState
358
- }, () => {});
359
- };
360
- const runError = (error) => {
361
- if (!hasError) {
362
- const s = runShutdown(error);
363
- if (s instanceof Promise) return s.then(() => {
364
- throw error;
365
- });
366
- throw error;
367
- }
368
- return thenMaybe(runInterceptorChain("error", interceptors, {
369
- command,
370
- input,
371
- error,
372
- signal,
373
- context,
374
- runtime,
375
- program,
376
- caller,
377
- ...pipelineState
378
- }, () => ({ error })), (er) => {
379
- if (er.error !== void 0) return thenMaybe(runShutdown(er.error), () => {
380
- throw er.error;
381
- });
382
- const wrapped = wrapErrorResult ? wrapErrorResult(er.result) : er.result;
383
- return thenMaybe(runShutdown(void 0, wrapped), () => wrapped);
384
- });
385
- };
386
- const handleSuccess = (result) => {
387
- const s = runShutdown(void 0, result);
388
- if (s instanceof Promise) return s.then(() => result);
389
- return result;
390
- };
391
- let result;
392
- try {
393
- result = pipeline();
394
- } catch (e) {
395
- return runError(e);
396
- }
397
- if (result instanceof Promise) return result.then(handleSuccess, runError);
398
- return handleSuccess(result);
399
- }
400
- //#endregion
401
- //#region src/output/primitives.ts
402
- function stringifyCell(value) {
403
- if (value === void 0 || value === null) return "";
404
- if (typeof value === "string") return value;
405
- if (typeof value === "number" || typeof value === "boolean") return String(value);
406
- return JSON.stringify(value);
407
- }
408
- function truncate(text, max) {
409
- if (max <= 0 || text.length <= max) return text;
410
- return max <= 1 ? "…" : `${text.slice(0, max - 1)}…`;
411
- }
412
- function padCell(text, width, alignment = "left") {
413
- const pad = width - text.length;
414
- if (pad <= 0) return text;
415
- if (alignment === "right") return " ".repeat(pad) + text;
416
- if (alignment === "center") {
417
- const left = Math.floor(pad / 2);
418
- return " ".repeat(left) + text + " ".repeat(pad - left);
419
- }
420
- return text + " ".repeat(pad);
421
- }
422
- function renderTable(data, options, ctx) {
423
- if (data.length === 0) return "";
424
- if (ctx.format === "json") return JSON.stringify(data, null, 2);
425
- const columns = options?.columns ?? Object.keys(data[0]);
426
- if (columns.length === 0) return "";
427
- const headers = columns.map((col) => options?.headers?.[col] ?? col);
428
- const maxCol = options?.maxColumnWidth;
429
- const rows = data.map((row) => columns.map((col) => {
430
- const text = stringifyCell(row[col]);
431
- return maxCol ? truncate(text, maxCol) : text;
432
- }));
433
- const colWidths = columns.map((_, i) => {
434
- const headerWidth = headers[i].length;
435
- const maxCellWidth = rows.reduce((max, row) => Math.max(max, row[i].length), 0);
436
- return Math.max(headerWidth, maxCellWidth);
437
- });
438
- const getAlign = (i) => options?.align?.[columns[i]] ?? "left";
439
- if (ctx.format === "markdown") return renderTableMarkdown(headers, rows, colWidths, getAlign);
440
- if (ctx.format === "html") return renderTableHtml(columns, headers, rows, data, getAlign);
441
- return renderTableText(headers, rows, colWidths, getAlign, options?.border !== false, ctx);
442
- }
443
- function renderTableText(headers, rows, colWidths, getAlign, border, ctx) {
444
- const { styler } = ctx;
445
- const formatRow = (cells, style) => cells.map((cell, i) => {
446
- const padded = padCell(cell, colWidths[i], getAlign(i));
447
- return style ? style(padded) : padded;
448
- });
449
- if (border) {
450
- const sep = ctx.styler.meta("─");
451
- const divider = colWidths.map((w) => sep.repeat(w + 2)).join(styler.meta("┼"));
452
- const row = (cells) => cells.map((c, i) => ` ${padCell(c, colWidths[i], getAlign(i))} `).join(styler.meta("│"));
453
- const headerRow = row(headers.map((h) => styler.label(h)));
454
- const dataRows = rows.map((r) => row(r.map((c) => styler.description(c))));
455
- return [
456
- headerRow,
457
- styler.meta("─") + divider + styler.meta("─"),
458
- ...dataRows
459
- ].join("\n");
460
- }
461
- const headerCells = formatRow(headers, styler.label);
462
- const dataCells = rows.map((r) => formatRow(r, styler.description));
463
- const gap = " ";
464
- return [headerCells.join(gap), ...dataCells.map((r) => r.join(gap))].join("\n");
465
- }
466
- function renderTableMarkdown(headers, rows, colWidths, getAlign) {
467
- return [
468
- `| ${headers.map((h, i) => padCell(h, colWidths[i], "left")).join(" | ")} |`,
469
- "| " + colWidths.map((w, i) => {
470
- const a = getAlign(i);
471
- const dashes = "─".repeat(Math.max(w, 3));
472
- if (a === "center") return `:${dashes}:`;
473
- if (a === "right") return `${dashes}:`;
474
- return dashes;
475
- }).join(" | ") + " |",
476
- ...rows.map((r) => `| ${r.map((c, i) => padCell(c, colWidths[i], "left")).join(" | ")} |`)
477
- ].join("\n");
478
- }
479
- function renderTableHtml(columns, headers, _rows, data, getAlign) {
480
- const ths = headers.map((h, i) => {
481
- const a = getAlign(i);
482
- return `<th${a !== "left" ? ` style="text-align: ${a};"` : ""}>${escapeHtml(h)}</th>`;
483
- });
484
- const trs = data.map((row) => "<tr>" + columns.map((col, i) => {
485
- const a = getAlign(i);
486
- return `<td${a !== "left" ? ` style="text-align: ${a};"` : ""}>${escapeHtml(stringifyCell(row[col]))}</td>`;
487
- }).join("") + "</tr>");
488
- return `<table><thead><tr>${ths.join("")}</tr></thead><tbody>${trs.join("")}</tbody></table>`;
489
- }
490
- function renderTree(data, options, ctx) {
491
- const nodes = Array.isArray(data) ? data : [data];
492
- if (nodes.length === 0) return "";
493
- if (ctx.format === "json") return JSON.stringify(nodes, null, 2);
494
- if (ctx.format === "markdown") return renderTreeMarkdown(nodes, 0);
495
- if (ctx.format === "html") return renderTreeHtml(nodes);
496
- return renderTreeText(nodes, "", options?.guides !== false, ctx).join("\n");
497
- }
498
- function renderTreeText(nodes, prefix, guides, ctx) {
499
- const lines = [];
500
- for (let i = 0; i < nodes.length; i++) {
501
- const node = nodes[i];
502
- const isLast = i === nodes.length - 1;
503
- if (guides) {
504
- const connector = isLast ? "└── " : "├── ";
505
- const childPrefix = isLast ? " " : "│ ";
506
- lines.push(prefix + ctx.styler.meta(connector) + ctx.styler.label(node.label));
507
- if (node.children?.length) lines.push(...renderTreeText(node.children, prefix + ctx.styler.meta(childPrefix), guides, ctx));
508
- } else {
509
- const indent = prefix ? `${prefix} ` : "";
510
- lines.push(indent + ctx.styler.label(node.label));
511
- if (node.children?.length) lines.push(...renderTreeText(node.children, indent, guides, ctx));
512
- }
513
- }
514
- return lines;
515
- }
516
- function renderTreeMarkdown(nodes, depth) {
517
- return nodes.map((node) => {
518
- const line = `${" ".repeat(depth)}- ${node.label}`;
519
- if (!node.children?.length) return line;
520
- return `${line}\n${renderTreeMarkdown(node.children, depth + 1)}`;
521
- }).join("\n");
522
- }
523
- function renderTreeHtml(nodes) {
524
- return `<ul>${nodes.map((node) => {
525
- const label = escapeHtml(node.label);
526
- if (!node.children?.length) return `<li>${label}</li>`;
527
- return `<li>${label}${renderTreeHtml(node.children)}</li>`;
528
- }).join("")}</ul>`;
529
- }
530
- function renderList(data, options, ctx) {
531
- if (data.length === 0) return "";
532
- if (ctx.format === "json") {
533
- const normalized = data.map((item) => typeof item === "string" ? { label: item } : item);
534
- return JSON.stringify(normalized, null, 2);
535
- }
536
- if (ctx.format === "markdown") return renderListMarkdown(data, options);
537
- if (ctx.format === "html") return renderListHtml(data, options);
538
- return renderListText(data, options, ctx);
539
- }
540
- function renderListText(data, options, ctx) {
541
- const { styler } = ctx;
542
- const numbered = options?.numbered ?? false;
543
- const bullet = options?.bullet ?? (ctx.format === "ansi" ? "•" : "-");
544
- const baseIndent = " ".repeat(options?.indent ?? 0);
545
- return data.map((item, i) => {
546
- const prefix = numbered ? `${i + 1}.` : bullet;
547
- const label = typeof item === "string" ? item : item.label;
548
- const desc = typeof item === "object" && item.description ? item.description : void 0;
549
- const line = `${baseIndent}${styler.meta(prefix)} ${styler.label(label)}`;
550
- if (!desc) return line;
551
- return `${line} ${styler.description(desc)}`;
552
- }).join("\n");
553
- }
554
- function renderListMarkdown(data, options) {
555
- const numbered = options?.numbered ?? false;
556
- return data.map((item, i) => {
557
- const prefix = numbered ? `${i + 1}.` : "-";
558
- const label = typeof item === "string" ? item : item.label;
559
- const desc = typeof item === "object" && item.description ? item.description : void 0;
560
- if (!desc) return `${prefix} ${label}`;
561
- return `${prefix} **${label}** — ${desc}`;
562
- }).join("\n");
563
- }
564
- function renderListHtml(data, options) {
565
- const tag = options?.numbered ? "ol" : "ul";
566
- return `<${tag}>${data.map((item) => {
567
- const label = typeof item === "string" ? item : item.label;
568
- const desc = typeof item === "object" && item.description ? item.description : void 0;
569
- if (!desc) return `<li>${escapeHtml(label)}</li>`;
570
- return `<li><strong>${escapeHtml(label)}</strong> — ${escapeHtml(desc)}</li>`;
571
- }).join("")}</${tag}>`;
572
- }
573
- function renderKeyValue(data, options, ctx) {
574
- const entries = Object.entries(data);
575
- if (entries.length === 0) return "";
576
- if (ctx.format === "json") return JSON.stringify(data, null, 2);
577
- if (ctx.format === "markdown") return renderKeyValueMarkdown(entries, options);
578
- if (ctx.format === "html") return renderKeyValueHtml(entries, options);
579
- return renderKeyValueText(entries, options, ctx);
580
- }
581
- function getLabel(key, labels) {
582
- return labels?.[key] ?? key;
583
- }
584
- function renderKeyValueText(entries, options, ctx) {
585
- const { styler } = ctx;
586
- const sep = options?.separator ?? ": ";
587
- const shouldAlign = options?.align !== false;
588
- const displayLabels = entries.map(([k]) => getLabel(k, options?.labels));
589
- const maxWidth = shouldAlign ? Math.max(...displayLabels.map((l) => l.length)) : 0;
590
- return entries.map(([_key, value], i) => {
591
- const label = displayLabels[i];
592
- const paddedLabel = shouldAlign ? label + " ".repeat(maxWidth - label.length) : label;
593
- return `${styler.label(paddedLabel)}${styler.meta(sep)}${styler.description(stringifyCell(value))}`;
594
- }).join("\n");
595
- }
596
- function renderKeyValueMarkdown(entries, options) {
597
- return entries.map(([key, value]) => `- **${getLabel(key, options?.labels)}**: ${stringifyCell(value)}`).join("\n");
598
- }
599
- function renderKeyValueHtml(entries, options) {
600
- return `<dl>${entries.map(([key, value]) => `<dt>${escapeHtml(getLabel(key, options?.labels))}</dt><dd>${escapeHtml(stringifyCell(value))}</dd>`).join("")}</dl>`;
601
- }
602
- //#endregion
603
- //#region src/output/output-indicator.ts
604
- /** Create an output indicator that renders through the given output function and format context. */
605
- function createOutputIndicator(outputFn, ctx) {
606
- let _called = false;
607
- const emit = (rendered) => {
608
- _called = true;
609
- outputFn(rendered);
610
- };
611
- return {
612
- table(data, options) {
613
- emit(renderTable(data, options, ctx));
614
- },
615
- tree(data, options) {
616
- emit(renderTree(data, options, ctx));
617
- },
618
- list(data, options) {
619
- emit(renderList(data, options, ctx));
620
- },
621
- kv(data, options) {
622
- emit(renderKeyValue(data, options, ctx));
623
- },
624
- raw(...args) {
625
- _called = true;
626
- outputFn(...args);
627
- },
628
- get called() {
629
- return _called;
630
- }
631
- };
632
- }
633
- /** Format a return value using a declarative output config. */
634
- function formatDeclarativeOutput(value, config, ctx) {
635
- const type = typeof config === "string" ? config : config.type;
636
- const options = typeof config === "object" ? config.options : void 0;
637
- switch (type) {
638
- case "table":
639
- if (!Array.isArray(value)) return void 0;
640
- return renderTable(value, options, ctx);
641
- case "tree": return renderTree(value, options, ctx);
642
- case "list":
643
- if (!Array.isArray(value)) return void 0;
644
- return renderList(value, options, ctx);
645
- case "kv":
646
- if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
647
- return renderKeyValue(value, options, ctx);
648
- case "json": return JSON.stringify(value, null, 2);
649
- default: return;
650
- }
651
- }
652
- //#endregion
653
- //#region src/extension/auto-output.ts
654
- /**
655
- * Outputs each value and collects into a result.
656
- * For iterators: outputs each yielded value, returns collected array.
657
- * For promises: awaits, then recurses.
658
- * For other values: outputs directly, returns as-is.
659
- */
660
- function outputAndCollect(value, output) {
661
- if (value == null) return value;
662
- if (isAsyncIterator(value)) return (async () => {
663
- const items = [];
664
- const iter = value[Symbol.asyncIterator]();
665
- while (true) {
666
- const { done, value: item } = await iter.next();
667
- if (done) break;
668
- items.push(item);
669
- if (item != null) output(item);
670
- }
671
- return items;
672
- })();
673
- if (typeof value !== "string" && !Array.isArray(value) && isIterator(value)) {
674
- const items = [];
675
- const iter = value[Symbol.iterator]();
676
- while (true) {
677
- const { done, value: item } = iter.next();
678
- if (done) break;
679
- items.push(item);
680
- if (item != null) output(item);
681
- }
682
- return items;
683
- }
684
- if (value instanceof Promise) return value.then((resolved) => outputAndCollect(resolved, output));
685
- output(value);
686
- return value;
687
- }
688
- const autoOutputMeta = {
689
- id: "padrone:auto-output",
690
- name: "padrone:auto-output",
691
- order: -1100
692
- };
693
- function createAutoOutputInterceptor(outputConfig, errorOutput) {
694
- return defineInterceptor(autoOutputMeta, () => ({
695
- error(ctx, next) {
696
- const handleResult = (er) => {
697
- if (!er.error || errorOutput === false || ctx.caller !== "cli" || ctx.phase !== "execute") return er;
698
- const message = er.error instanceof Error ? er.error.message : String(er.error);
699
- ctx.runtime.error(message);
700
- return er;
701
- };
702
- const result = next();
703
- if (result instanceof Promise) return result.then(handleResult);
704
- return handleResult(result);
705
- },
706
- execute(ctx, next) {
707
- const outputCtx = resolveOutputFormat(ctx.runtime, ctx.caller);
708
- const indicator = createOutputIndicator(ctx.runtime.output, outputCtx);
709
- const handleResult = (e) => {
710
- if (indicator.called) return e;
711
- const autoOutput = (value) => {
712
- if (value == null) return value;
713
- if (outputConfig) {
714
- const rendered = formatDeclarativeOutput(value, outputConfig, outputCtx);
715
- if (rendered !== void 0) {
716
- ctx.runtime.output(rendered);
717
- return value;
718
- }
719
- }
720
- return outputAndCollect(value, ctx.runtime.output);
721
- };
722
- if (e.result instanceof Promise) return { result: e.result.then(autoOutput) };
723
- const collected = autoOutput(e.result);
724
- if (collected instanceof Promise) return collected.then((v) => ({ result: v }));
725
- return { result: collected };
726
- };
727
- const executedOrPromise = next({ context: { output: indicator } });
728
- if (executedOrPromise instanceof Promise) return executedOrPromise.then(handleResult);
729
- return handleResult(executedOrPromise);
730
- }
731
- }));
732
- }
733
- /**
734
- * Extension that automatically writes a command's return value to output after execution.
735
- *
736
- * - Values are passed directly to the runtime's `output` function (no stringification).
737
- * - Promises are awaited before output.
738
- * - Iterators and async iterators are consumed, outputting each yielded value as it arrives.
739
- * The result is replaced with the collected array so `drain()` still works.
740
- * - `undefined` and `null` results produce no output.
741
- *
742
- * Also injects `ctx.context.output` with format-aware output primitives (table, tree, list, kv).
743
- * When action handlers use these methods, auto-output skips to avoid double output.
744
- *
745
- * Included in the default extensions. Can also be applied per-command:
746
- * ```ts
747
- * createPadrone('my-cli')
748
- * .command('users', (c) =>
749
- * c.extend(padroneAutoOutput({ output: 'table' }))
750
- * .action(() => fetchUsers())
751
- * )
752
- * ```
753
- */
754
- function padroneAutoOutput(options) {
755
- const interceptor = options?.disabled ? defineInterceptor({
756
- ...autoOutputMeta,
757
- disabled: true
758
- }, () => ({})) : createAutoOutputInterceptor(options?.output, options?.errorOutput);
759
- return ((builder) => builder.intercept(interceptor));
760
- }
761
- //#endregion
762
- //#region src/extension/color.ts
763
- const colorInterceptor = defineInterceptor({
764
- id: "padrone:color",
765
- name: "padrone:color",
766
- order: -1001
767
- }, () => ({ parse(ctx, next) {
768
- return thenMaybe(next(), (res) => {
769
- if ("color" in res.rawArgs) {
770
- const color = res.rawArgs.color;
771
- delete res.rawArgs.color;
772
- ctx.runtime.theme = color;
773
- }
774
- return res;
775
- });
776
- } }));
777
- /**
778
- * Extension that handles `--color` / `--no-color` flags:
779
- * - `--color` or `--color=true` → use default theme
780
- * - `--color=false` or `--no-color` → disable colors (text format)
781
- * - `--color=<theme>` → use the named theme
782
- *
783
- * Modifies the runtime's format and theme accordingly.
784
- *
785
- * Usage:
786
- * ```ts
787
- * createPadrone('my-cli').extend(padroneColor())
788
- * ```
789
- */
790
- function padroneColor() {
791
- return ((builder) => builder.intercept(colorInterceptor));
792
- }
793
- //#endregion
794
- //#region src/core/parse.ts
795
- /**
796
- * Split a string by a delimiter, respecting quoted segments and optional bracket nesting.
797
- * Handles escape sequences within quotes (\\" and \\\\).
798
- */
799
- function splitQuoteAware(input, delimiter, opts) {
800
- const results = [];
801
- let current = "";
802
- let inQuote = null;
803
- let bracketDepth = 0;
804
- let i = 0;
805
- while (i < input.length) {
806
- const char = input[i];
807
- if (inQuote) {
808
- if (char === "\\" && i + 1 < input.length) {
809
- const nextChar = input[i + 1];
810
- if (nextChar === inQuote || nextChar === "\\") {
811
- current += nextChar;
812
- i += 2;
813
- continue;
814
- }
815
- }
816
- if (char === inQuote) inQuote = null;
817
- else current += char;
818
- } else if (opts?.brackets && char === "[") {
819
- bracketDepth++;
820
- current += char;
821
- } else if (opts?.brackets && char === "]") {
822
- bracketDepth = Math.max(0, bracketDepth - 1);
823
- current += char;
824
- } else if (bracketDepth > 0) current += char;
825
- else if (char === "\"" || char === "'" || char === "`") inQuote = char;
826
- else if (char === delimiter || delimiter === " " && char === " ") {
827
- if (delimiter === " " ? current : true) {
828
- results.push(opts?.trim ? current.trim() : current);
829
- current = "";
830
- }
831
- } else current += char;
832
- i++;
833
- }
834
- if (delimiter === " " ? current : current || results.length > 0) results.push(opts?.trim ? current.trim() : current);
835
- return results;
836
- }
837
- function parseCliInputToParts(input) {
838
- const parts = splitQuoteAware(input.trim(), " ", { brackets: true });
839
- const result = [];
840
- let pendingIdx = -1;
841
- let allowTerm = true;
842
- let afterDoubleDash = false;
843
- for (const part of parts) {
844
- if (!part) continue;
845
- if (part === "--" && !afterDoubleDash) {
846
- pendingIdx = -1;
847
- afterDoubleDash = true;
848
- allowTerm = false;
849
- continue;
850
- }
851
- if (afterDoubleDash) {
852
- result.push({
853
- type: "arg",
854
- value: part
855
- });
856
- continue;
857
- }
858
- const hadPending = pendingIdx;
859
- pendingIdx = -1;
860
- if (part.startsWith("--no-") && part.length > 5) {
861
- const key = part.slice(5).split(".");
862
- result.push({
863
- type: "named",
864
- key,
865
- value: void 0,
866
- negated: true
867
- });
868
- } else if (part.startsWith("--")) {
869
- const [keyStr = "", value] = splitNamedArgValue(part.slice(2));
870
- const key = keyStr.split(".");
871
- result.push({
872
- type: "named",
873
- key,
874
- value
875
- });
876
- if (typeof value === "undefined") pendingIdx = result.length - 1;
877
- } else if (part.startsWith("-") && part.length > 1 && !/^-\d/.test(part)) {
878
- const [keyStr = "", value] = splitNamedArgValue(part.slice(1));
879
- if (keyStr.length > 1 && typeof value === "undefined") {
880
- for (let ci = 0; ci < keyStr.length - 1; ci++) result.push({
881
- type: "alias",
882
- key: [keyStr[ci]],
883
- value: void 0
884
- });
885
- result.push({
886
- type: "alias",
887
- key: [keyStr[keyStr.length - 1]],
888
- value: void 0
889
- });
890
- pendingIdx = result.length - 1;
891
- } else if (keyStr.length > 1 && typeof value !== "undefined") {
892
- for (let ci = 0; ci < keyStr.length - 1; ci++) result.push({
893
- type: "alias",
894
- key: [keyStr[ci]],
895
- value: void 0
896
- });
897
- result.push({
898
- type: "alias",
899
- key: [keyStr[keyStr.length - 1]],
900
- value
901
- });
902
- } else {
903
- result.push({
904
- type: "alias",
905
- key: [keyStr],
906
- value
907
- });
908
- if (typeof value === "undefined") pendingIdx = result.length - 1;
909
- }
910
- } else if (hadPending >= 0) result[hadPending].value = part;
911
- else if (/^[a-zA-Z0-9_-]+$/.test(part) && allowTerm) result.push({
912
- type: "term",
913
- value: part
914
- });
915
- else {
916
- result.push({
917
- type: "arg",
918
- value: part
919
- });
920
- allowTerm = false;
921
- }
922
- }
923
- return result;
924
- }
925
- /**
926
- * Split named arg key and value, handling quoted values after =.
927
- */
928
- function splitNamedArgValue(str) {
929
- const eqIndex = str.indexOf("=");
930
- if (eqIndex === -1) return [str, void 0];
931
- const key = str.slice(0, eqIndex);
932
- let value = str.slice(eqIndex + 1);
933
- if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'") || value.startsWith("`") && value.endsWith("`")) {
934
- value = value.slice(1, -1);
935
- return [key, value];
936
- }
937
- if (value.startsWith("[") && value.endsWith("]")) {
938
- const inner = value.slice(1, -1);
939
- if (inner === "") return [key, []];
940
- return [key, splitQuoteAware(inner, ",", { trim: true })];
941
- }
942
- return [key, value];
943
- }
944
- /**
945
- * Sets a value at a nested path in an object.
946
- * For example: setNestedValue(obj, ['user', 'profile', 'name'], 'John')
947
- * Creates intermediate objects as needed.
948
- */
949
- function setNestedValue(obj, path, value) {
950
- let current = obj;
951
- for (let i = 0; i < path.length - 1; i++) {
952
- const part = path[i];
953
- if (!(part in current) || typeof current[part] !== "object" || current[part] === null) current[part] = {};
954
- current = current[part];
955
- }
956
- const lastPart = path[path.length - 1];
957
- current[lastPart] = value;
958
- }
959
- /**
960
- * Gets a value at a nested path in an object.
961
- * Returns undefined if the path doesn't exist.
962
- */
963
- function getNestedValue(obj, path) {
964
- let current = obj;
965
- for (const part of path) {
966
- if (current === null || current === void 0 || typeof current !== "object") return;
967
- current = current[part];
968
- }
969
- return current;
970
- }
971
- //#endregion
972
- //#region src/core/validate.ts
973
- /**
974
- * Parses CLI input to find the command and extract raw arguments without validation.
975
- */
976
- function parseCommand(input, rootCommand, findCommandByName) {
977
- input ??= getCommandRuntime(rootCommand).argv().join(" ") || void 0;
978
- if (!input) {
979
- const defaultCommand = findCommandByName("", rootCommand.commands);
980
- if (defaultCommand) return {
981
- command: defaultCommand,
982
- rawArgs: {},
983
- args: [],
984
- unmatchedTerms: []
985
- };
986
- return {
987
- command: rootCommand,
988
- rawArgs: {},
989
- args: [],
990
- unmatchedTerms: []
991
- };
992
- }
993
- const parts = parseCliInputToParts(input);
994
- const terms = parts.filter((p) => p.type === "term").map((p) => p.value);
995
- const argTokens = parts.filter((p) => p.type === "arg").map((p) => p.value);
996
- let curCommand = rootCommand;
997
- let unmatchedTerms = [];
998
- if (terms[0] === rootCommand.name) terms.shift();
999
- for (let i = 0; i < terms.length; i++) {
1000
- const found = findCommandByName(terms[i] || "", curCommand.commands);
1001
- if (found) curCommand = found;
1002
- else {
1003
- unmatchedTerms = terms.slice(i);
1004
- argTokens.unshift(...unmatchedTerms);
1005
- break;
1006
- }
1007
- }
1008
- if (unmatchedTerms.length === 0 && curCommand.commands?.length) {
1009
- const defaultCommand = findCommandByName("", curCommand.commands);
1010
- if (defaultCommand) curCommand = defaultCommand;
1011
- }
1012
- if (!curCommand) return {
1013
- command: rootCommand,
1014
- rawArgs: {},
1015
- args: argTokens,
1016
- unmatchedTerms
1017
- };
1018
- const argsMeta = curCommand.meta?.fields;
1019
- const { flags, aliases, negatives, customNegation } = curCommand.argsSchema ? extractSchemaMetadata(curCommand.argsSchema, argsMeta, curCommand.meta?.autoAlias) : {
1020
- flags: {},
1021
- aliases: {},
1022
- negatives: {},
1023
- customNegation: /* @__PURE__ */ new Set()
1024
- };
1025
- const arrayArguments = /* @__PURE__ */ new Set();
1026
- if (curCommand.argsSchema) try {
1027
- const jsonSchema = getJsonSchema(curCommand.argsSchema);
1028
- if (jsonSchema.type === "object" && jsonSchema.properties) {
1029
- for (const [key, prop] of Object.entries(jsonSchema.properties)) if (prop?.type === "array") arrayArguments.add(key);
1030
- }
1031
- } catch {}
1032
- const argParts = parts.filter((p) => p.type === "named" || p.type === "alias");
1033
- const rawArgs = {};
1034
- for (const arg of argParts) {
1035
- let key;
1036
- if (arg.type === "alias" && arg.key.length === 1 && flags[arg.key[0]]) key = [flags[arg.key[0]]];
1037
- else if (arg.type === "named" && arg.key.length === 1 && aliases[arg.key[0]]) key = [aliases[arg.key[0]]];
1038
- else if (arg.type === "named" && arg.key.length === 1 && negatives[arg.key[0]]) {
1039
- setNestedValue(rawArgs, [negatives[arg.key[0]]], false);
1040
- continue;
1041
- } else key = arg.key;
1042
- const rootKey = key[0];
1043
- if (arg.type === "named" && arg.negated) {
1044
- if (customNegation.has(rootKey)) {
1045
- setNestedValue(rawArgs, [`no-${key.join(".")}`], false);
1046
- continue;
1047
- }
1048
- setNestedValue(rawArgs, key, false);
1049
- continue;
1050
- }
1051
- const value = arg.value ?? true;
1052
- if (arrayArguments.has(rootKey)) {
1053
- const existing = getNestedValue(rawArgs, key);
1054
- if (existing !== void 0) if (Array.isArray(existing)) if (Array.isArray(value)) existing.push(...value);
1055
- else existing.push(value);
1056
- else if (Array.isArray(value)) setNestedValue(rawArgs, key, [existing, ...value]);
1057
- else setNestedValue(rawArgs, key, [existing, value]);
1058
- else setNestedValue(rawArgs, key, Array.isArray(value) ? value : [value]);
1059
- } else {
1060
- const existing = getNestedValue(rawArgs, key);
1061
- if (existing !== void 0) if (Array.isArray(existing)) if (Array.isArray(value)) existing.push(...value);
1062
- else existing.push(value);
1063
- else if (Array.isArray(value)) setNestedValue(rawArgs, key, [existing, ...value]);
1064
- else setNestedValue(rawArgs, key, [existing, value]);
1065
- else setNestedValue(rawArgs, key, value);
1066
- }
1067
- }
1068
- return {
1069
- command: curCommand,
1070
- rawArgs,
1071
- args: argTokens,
1072
- unmatchedTerms
1073
- };
1074
- }
1075
- /**
1076
- * Preprocesses raw arguments: maps positional arguments and performs auto-coercion.
1077
- * External data sources (stdin, env, config) are handled by extensions before this runs.
1078
- */
1079
- function buildCommandArgs(command, rawArgs, positionalArgs) {
1080
- let preprocessedArgs = preprocessArgs(rawArgs, {
1081
- flags: {},
1082
- aliases: {}
1083
- });
1084
- let issues;
1085
- const positionalConfig = command.meta?.positional ? parsePositionalConfig(command.meta.positional) : [];
1086
- if (positionalConfig.length > 0) {
1087
- let argIndex = 0;
1088
- for (let i = 0; i < positionalConfig.length; i++) {
1089
- const { name, variadic } = positionalConfig[i];
1090
- if (argIndex >= positionalArgs.length) break;
1091
- if (name in preprocessedArgs) {
1092
- issues ??= [];
1093
- issues.push({
1094
- path: [name],
1095
- message: `Ambiguous argument "${name}": provided both positionally and as a named option`
1096
- });
1097
- continue;
1098
- }
1099
- if (variadic) {
1100
- const nonVariadicAfter = positionalConfig.slice(i + 1).filter((p) => !p.variadic).length;
1101
- const variadicEnd = positionalArgs.length - nonVariadicAfter;
1102
- preprocessedArgs[name] = positionalArgs.slice(argIndex, variadicEnd);
1103
- argIndex = variadicEnd;
1104
- } else if (i === positionalConfig.length - 1 && positionalArgs.length > argIndex + 1) {
1105
- preprocessedArgs[name] = positionalArgs.slice(argIndex).join(" ");
1106
- argIndex = positionalArgs.length;
1107
- } else {
1108
- preprocessedArgs[name] = positionalArgs[argIndex];
1109
- argIndex++;
1110
- }
1111
- }
1112
- }
1113
- if (command.argsSchema) preprocessedArgs = coerceArgs(preprocessedArgs, command.argsSchema);
1114
- return {
1115
- args: preprocessedArgs,
1116
- issues
1117
- };
1118
- }
1119
- /**
1120
- * Detects unknown options in args that aren't defined in the schema.
1121
- * Returns unknown key info with suggestions, or empty array if schema is loose.
1122
- */
1123
- function checkUnknownArgs(command, preprocessedArgs) {
1124
- if (!command.argsSchema) {
1125
- const unknowns = [];
1126
- for (const key of Object.keys(preprocessedArgs)) unknowns.push({ key });
1127
- return unknowns;
1128
- }
1129
- const argsMeta = command.meta?.fields;
1130
- const { flags, aliases, negatives } = extractSchemaMetadata(command.argsSchema, argsMeta, command.meta?.autoAlias);
1131
- return detectUnknownArgs(preprocessedArgs, command.argsSchema, flags, aliases, negatives);
1132
- }
1133
- /**
1134
- * Validates preprocessed arguments against the command's schema.
1135
- * First checks for unknown args (strict by default), then runs schema validation.
1136
- * Returns sync or async result depending on the schema's validate method.
1137
- */
1138
- function validateCommandArgs(command, preprocessedArgs) {
1139
- const unknownArgs = checkUnknownArgs(command, preprocessedArgs);
1140
- if (unknownArgs.length > 0) return {
1141
- args: void 0,
1142
- argsResult: { issues: unknownArgs.map(({ key }) => ({
1143
- path: [key],
1144
- message: `Unknown option: "${key}"`
1145
- })) }
1146
- };
1147
- const argsParsed = command.argsSchema ? command.argsSchema["~standard"].validate(preprocessedArgs) : { value: {} };
1148
- const buildResult = (parsed) => ({
1149
- args: parsed.issues ? void 0 : parsed.value,
1150
- argsResult: parsed
1151
- });
1152
- return thenMaybe(argsParsed, buildResult);
1153
- }
1154
- /**
1155
- * Returns the list of known option names from a command's schema (for fuzzy suggestion).
1156
- */
1157
- function getKnownOptionNames(command) {
1158
- if (!command.argsSchema) return [];
1159
- try {
1160
- const js = getJsonSchema(command.argsSchema);
1161
- if (js.type === "object" && js.properties) return Object.keys(js.properties);
1162
- } catch {}
1163
- return [];
1164
- }
1165
- /**
1166
- * Formats validation issue messages for display.
1167
- */
1168
- function formatIssueMessages(issues) {
1169
- return issues.map((i) => ` - ${i.path?.join(".") || "root"}: ${i.message}`).join("\n");
1170
- }
1171
- /**
1172
- * Core validate function for parse() — preprocesses and validates CLI args.
1173
- * Used by the parse program method (lighter weight than the full exec pipeline).
1174
- * External data sources (stdin, env, config) are not resolved here — use eval() for that.
1175
- */
1176
- function coreValidateForParse(command, rawArgs, positionalArgs) {
1177
- const { args: preprocessedArgs, issues } = buildCommandArgs(command, rawArgs, positionalArgs);
1178
- if (issues) return {
1179
- args: void 0,
1180
- argsResult: { issues }
1181
- };
1182
- return thenMaybe(validateCommandArgs(command, preprocessedArgs), (v) => v);
1183
- }
1184
- //#endregion
1185
- //#region src/extension/utils.ts
1186
- /** Minimal Standard Schema that passes through known fields, ignoring unknown ones. */
1187
- function passthroughSchema(fields) {
1188
- return { "~standard": {
1189
- version: 1,
1190
- vendor: "padrone",
1191
- jsonSchema: {
1192
- input: () => ({}),
1193
- output: () => ({})
1194
- },
1195
- validate: (value) => {
1196
- const input = value && typeof value === "object" ? value : {};
1197
- const result = {};
1198
- for (const [name, type] of Object.entries(fields)) {
1199
- const v = input[name];
1200
- if (v === void 0) continue;
1201
- if (type === "string[]") {
1202
- if (Array.isArray(v)) result[name] = v.map(String);
1203
- else if (typeof v === "string") result[name] = [v];
1204
- } else if (type === "string") {
1205
- if (typeof v === "string") result[name] = v;
1206
- else if (Array.isArray(v) && v.length > 0) result[name] = String(v[0]);
1207
- } else if (type === "boolean") result[name] = v === true || v === "true";
1208
- }
1209
- return { value: result };
1210
- }
1211
- } };
1212
- }
1213
- /** Find a command by space-separated name in the command tree. */
1214
- function findCommandInTree(name, rootCommand) {
1215
- const parts = name.split(" ").filter(Boolean);
1216
- let current = rootCommand;
1217
- for (const part of parts) {
1218
- const found = current.commands?.find((c) => c.name === part || c.aliases?.includes(part));
1219
- if (!found) return void 0;
1220
- current = found;
1221
- }
1222
- return current;
1223
- }
1224
- //#endregion
1225
- //#region src/extension/help.ts
1226
- const helpInterceptor = defineInterceptor({
1227
- id: "padrone:help",
1228
- name: "padrone:help",
1229
- order: -1e3
1230
- }, () => {
1231
- let helpText;
1232
- let showDefaultHelp = false;
1233
- return {
1234
- parse(ctx, next) {
1235
- return thenMaybe(next(), (res) => {
1236
- const hasHelpFlag = res.rawArgs.help || res.rawArgs.h;
1237
- const reverseHelp = !hasHelpFlag && res.positionalArgs?.length > 0 && res.positionalArgs[res.positionalArgs.length - 1] === "help";
1238
- if (hasHelpFlag || reverseHelp) {
1239
- delete res.rawArgs.help;
1240
- delete res.rawArgs.h;
1241
- const detail = res.rawArgs.detail;
1242
- const format = res.rawArgs.format;
1243
- const all = res.rawArgs.all;
1244
- delete res.rawArgs.detail;
1245
- delete res.rawArgs.format;
1246
- delete res.rawArgs.all;
1247
- delete res.rawArgs.d;
1248
- delete res.rawArgs.f;
1249
- const rootCommand = getRootCommand(res.command);
1250
- resolveAllCommands(rootCommand);
1251
- helpText = generateHelp(rootCommand, res.command, {
1252
- detail,
1253
- format: format ?? ctx.runtime.format,
1254
- theme: ctx.runtime.theme,
1255
- all,
1256
- terminal: ctx.runtime.terminal,
1257
- env: ctx.runtime.env()
1258
- });
1259
- return res;
1260
- }
1261
- if (helpText === void 0) {
1262
- const { command } = res;
1263
- const hasSubcommands = command.commands && command.commands.length > 0;
1264
- const hasSchema = command.argsSchema != null;
1265
- const hasUnmatchedTerms = res.positionalArgs?.length > 0 && !command.meta?.positional?.length;
1266
- if (!command.action && (hasSubcommands || !hasSchema) && !hasUnmatchedTerms) showDefaultHelp = true;
1267
- }
1268
- return res;
1269
- });
1270
- },
1271
- validate(_ctx, next) {
1272
- if (helpText !== void 0) return {
1273
- args: void 0,
1274
- argsResult: { value: void 0 }
1275
- };
1276
- return next();
1277
- },
1278
- execute(ctx, next) {
1279
- if (helpText !== void 0) return { result: helpText };
1280
- if (showDefaultHelp) {
1281
- const rootCommand = getRootCommand(ctx.command);
1282
- resolveAllCommands(rootCommand);
1283
- return { result: generateHelp(rootCommand, ctx.command, {
1284
- format: ctx.runtime.format,
1285
- theme: ctx.runtime.theme,
1286
- terminal: ctx.runtime.terminal,
1287
- env: ctx.runtime.env()
1288
- }) };
1289
- }
1290
- return next();
1291
- },
1292
- error(ctx, next) {
1293
- return thenMaybe(next(), (er) => {
1294
- if (ctx.caller !== "cli" || !er.error) return er;
1295
- const rootCommand = getRootCommand(ctx.command);
1296
- if (er.error instanceof RoutingError) {
1297
- const targetPath = er.error.command;
1298
- const sourceCmd = (targetPath ? findCommandInTree(targetPath, rootCommand) : void 0) ?? rootCommand;
1299
- ctx.runtime.error(er.error.message);
1300
- if (er.error.suggestions.length > 0) {
1301
- const visibleCommands = (sourceCmd.commands ?? []).filter((c) => !c.hidden && c.name);
1302
- if (visibleCommands.length > 0) {
1303
- for (const cmd of visibleCommands) resolveCommand(cmd);
1304
- const cmdList = visibleCommands.map((c) => c.name).join(", ");
1305
- ctx.runtime.output(`\nAvailable commands: ${cmdList}`);
1306
- }
1307
- } else {
1308
- resolveAllCommands(rootCommand);
1309
- const helpText = generateHelp(rootCommand, sourceCmd, {
1310
- format: ctx.runtime.format,
1311
- theme: ctx.runtime.theme,
1312
- terminal: ctx.runtime.terminal,
1313
- env: ctx.runtime.env()
1314
- });
1315
- ctx.runtime.error(helpText);
1316
- }
1317
- return er;
1318
- }
1319
- if (er.error instanceof ValidationError) {
1320
- const targetPath = er.error.command;
1321
- const targetCommand = targetPath ? findCommandInTree(targetPath, rootCommand) : void 0;
1322
- const issueMessages = formatIssueMessages(er.error.issues);
1323
- resolveAllCommands(rootCommand);
1324
- const helpText = generateHelp(rootCommand, targetCommand ?? rootCommand, {
1325
- format: ctx.runtime.format,
1326
- theme: ctx.runtime.theme,
1327
- terminal: ctx.runtime.terminal,
1328
- env: ctx.runtime.env()
1329
- });
1330
- ctx.runtime.error(`Validation error:\n${issueMessages}`);
1331
- ctx.runtime.error(helpText);
1332
- return er;
1333
- }
1334
- return er;
1335
- });
1336
- }
1337
- };
1338
- });
1339
- /**
1340
- * Extension that adds help support:
1341
- * - `help` command with aliases `h` and `` (empty = executes on root when no subcommand matches)
1342
- * - `--help` / `-h` flags
1343
- * - `<cmd> help` reverse syntax
1344
- * - Default help display when a command has no action
1345
- *
1346
- * Usage:
1347
- * ```ts
1348
- * createPadrone('my-cli').extend(padroneHelp())
1349
- * ```
1350
- */
1351
- function padroneHelp() {
1352
- return ((builder) => builder.command(["help", "h"], (c) => c.configure({
1353
- description: "Display help for a command",
1354
- hidden: true
1355
- }).arguments(passthroughSchema({
1356
- command: "string[]",
1357
- detail: "string",
1358
- format: "string",
1359
- all: "boolean"
1360
- }), { positional: ["...command"] }).action((args, ctx) => {
1361
- const rootCommand = getRootCommand(ctx.command);
1362
- resolveAllCommands(rootCommand);
1363
- const commandName = args.command?.join(" ");
1364
- return generateHelp(rootCommand, (commandName ? findCommandInTree(commandName, rootCommand) : rootCommand) ?? rootCommand, {
1365
- detail: args.detail,
1366
- format: args.format ?? ctx.runtime.format,
1367
- theme: ctx.runtime.theme,
1368
- all: args.all,
1369
- terminal: ctx.runtime.terminal,
1370
- env: ctx.runtime.env()
1371
- });
1372
- })).intercept(helpInterceptor));
1373
- }
1374
- //#endregion
1375
- //#region src/feature/interactive.ts
1376
- /**
1377
- * Auto-detect the prompt type for a field based on its JSON schema property definition.
1378
- */
1379
- function detectPromptConfig(name, propSchema, description) {
1380
- const message = description || propSchema?.description || name;
1381
- if (!propSchema) return {
1382
- name,
1383
- message,
1384
- type: "input"
1385
- };
1386
- if (propSchema.type === "boolean") return {
1387
- name,
1388
- message,
1389
- type: "confirm",
1390
- default: propSchema.default
1391
- };
1392
- if (propSchema.enum) return {
1393
- name,
1394
- message,
1395
- type: "select",
1396
- choices: propSchema.enum.map((v) => ({
1397
- label: String(v),
1398
- value: v
1399
- })),
1400
- default: propSchema.default
1401
- };
1402
- if (propSchema.type === "array" && propSchema.items?.enum) return {
1403
- name,
1404
- message,
1405
- type: "multiselect",
1406
- choices: propSchema.items.enum.map((v) => ({
1407
- label: String(v),
1408
- value: v
1409
- })),
1410
- default: propSchema.default
1411
- };
1412
- if (propSchema.format === "password") return {
1413
- name,
1414
- message,
1415
- type: "password",
1416
- default: propSchema.default
1417
- };
1418
- return {
1419
- name,
1420
- message,
1421
- type: "input",
1422
- default: propSchema.default
1423
- };
1424
- }
1425
- /**
1426
- * Prompt a single field and validate it against the command's schema.
1427
- * Re-prompts with a warning until the user provides a valid value.
1428
- */
1429
- async function promptWithValidation(field, config, currentData, command, runtime) {
1430
- let promptConfig = config;
1431
- while (true) {
1432
- const value = await runtime.prompt(promptConfig);
1433
- if (!command.argsSchema) return value;
1434
- const testData = {
1435
- ...currentData,
1436
- [field]: value
1437
- };
1438
- const validated = await command.argsSchema["~standard"].validate(testData);
1439
- if (!validated.issues) return value;
1440
- const fieldIssues = validated.issues.filter((issue) => {
1441
- const rootKey = issue.path?.[0];
1442
- return rootKey !== void 0 && String(rootKey) === field;
1443
- });
1444
- if (fieldIssues.length === 0) return value;
1445
- const messages = fieldIssues.map((i) => i.message).join("; ");
1446
- runtime.error(`Invalid value for "${field}": ${messages}`);
1447
- promptConfig = {
1448
- ...config,
1449
- default: value
1450
- };
1451
- }
1452
- }
1453
- /**
1454
- * Prompt for missing interactive fields.
1455
- * Runs after env/config preprocessing and before schema validation.
1456
- *
1457
- * When `force` is true, all configured interactive fields are prompted even if they already
1458
- * have values. The current values are used as defaults in the prompts.
1459
- */
1460
- async function promptInteractiveFields(data, command, runtime, force) {
1461
- if (!runtime.prompt) return data;
1462
- const meta = command.meta;
1463
- const interactiveConfig = meta?.interactive;
1464
- const optionalInteractiveConfig = meta?.optionalInteractive;
1465
- if (!interactiveConfig && !optionalInteractiveConfig) return data;
1466
- let jsonProperties = {};
1467
- let requiredFields = /* @__PURE__ */ new Set();
1468
- if (command.argsSchema) try {
1469
- const jsonSchema = getJsonSchema(command.argsSchema);
1470
- if (jsonSchema.type === "object" && jsonSchema.properties) jsonProperties = jsonSchema.properties;
1471
- if (Array.isArray(jsonSchema.required)) requiredFields = new Set(jsonSchema.required);
1472
- } catch {}
1473
- const fieldDescriptions = {};
1474
- if (meta?.fields) {
1475
- for (const [key, value] of Object.entries(meta.fields)) if (value?.description) fieldDescriptions[key] = value.description;
1476
- }
1477
- const result = { ...data };
1478
- let fieldsToPrompt = [];
1479
- if (interactiveConfig === true) if (force) fieldsToPrompt = [...requiredFields];
1480
- else fieldsToPrompt = [...requiredFields].filter((name) => result[name] === void 0);
1481
- else if (Array.isArray(interactiveConfig)) if (force) fieldsToPrompt = [...interactiveConfig];
1482
- else fieldsToPrompt = interactiveConfig.filter((name) => result[name] === void 0);
1483
- for (const field of fieldsToPrompt) {
1484
- const config = detectPromptConfig(field, jsonProperties[field], fieldDescriptions[field]);
1485
- if (force && result[field] !== void 0) config.default = result[field];
1486
- result[field] = await promptWithValidation(field, config, result, command, runtime);
1487
- }
1488
- let optionalFields = [];
1489
- if (optionalInteractiveConfig === true) if (force) optionalFields = Object.keys(jsonProperties).filter((name) => !requiredFields.has(name));
1490
- else optionalFields = Object.keys(jsonProperties).filter((name) => !requiredFields.has(name) && result[name] === void 0);
1491
- else if (Array.isArray(optionalInteractiveConfig)) if (force) optionalFields = [...optionalInteractiveConfig];
1492
- else optionalFields = optionalInteractiveConfig.filter((name) => result[name] === void 0);
1493
- if (optionalFields.length > 0) {
1494
- const selected = await runtime.prompt({
1495
- name: "_optionalFields",
1496
- message: "Would you also like to configure:",
1497
- type: "multiselect",
1498
- choices: optionalFields.map((f) => {
1499
- const label = fieldDescriptions[f] || jsonProperties[f]?.description || f;
1500
- const currentValue = result[f];
1501
- return {
1502
- label: force && currentValue !== void 0 ? `${label} (current: ${currentValue})` : label,
1503
- value: f
1504
- };
1505
- })
1506
- });
1507
- if (Array.isArray(selected)) for (const field of selected) {
1508
- const config = detectPromptConfig(field, jsonProperties[field], fieldDescriptions[field]);
1509
- if (force && result[field] !== void 0) config.default = result[field];
1510
- result[field] = await promptWithValidation(field, config, result, command, runtime);
1511
- }
1512
- }
1513
- return result;
1514
- }
1515
- //#endregion
1516
- //#region src/extension/interactive.ts
1517
- const interactiveInterceptor = defineInterceptor({
1518
- id: "padrone:interactive",
1519
- name: "padrone:interactive",
1520
- order: -999
1521
- }, () => ({ validate(ctx, next) {
1522
- let flagInteractive;
1523
- if (hasInteractiveConfig(ctx.command.meta)) {
1524
- if (ctx.rawArgs.interactive !== void 0) {
1525
- flagInteractive = ctx.rawArgs.interactive !== false && ctx.rawArgs.interactive !== "false";
1526
- delete ctx.rawArgs.interactive;
1527
- }
1528
- if (ctx.rawArgs.i !== void 0) {
1529
- flagInteractive = ctx.rawArgs.i !== false && ctx.rawArgs.i !== "false";
1530
- delete ctx.rawArgs.i;
1531
- }
1532
- }
1533
- const { runtime, command } = ctx;
1534
- const runtimeDefault = runtime.interactive === "forced" ? true : runtime.interactive === "disabled" ? false : void 0;
1535
- const effectiveInteractive = flagInteractive ?? ctx.evalInteractive ?? runtimeDefault;
1536
- const stdinIsPiped = !!command.meta?.stdin && (runtime.stdin ? !runtime.stdin.isTTY : runtime.terminal?.isTTY !== true);
1537
- const interactivitySuppressed = runtime.interactive === "unsupported" || effectiveInteractive === false || stdinIsPiped && effectiveInteractive !== true;
1538
- const forceInteractive = !interactivitySuppressed && effectiveInteractive === true;
1539
- if (!(!interactivitySuppressed && runtime.prompt && hasInteractiveConfig(command.meta))) return next();
1540
- const { args: preprocessedArgs, issues: positionalIssues } = buildCommandArgs(command, ctx.rawArgs, ctx.positionalArgs);
1541
- if (positionalIssues) return {
1542
- args: void 0,
1543
- argsResult: { issues: positionalIssues }
1544
- };
1545
- const unknowns = checkUnknownArgs(command, preprocessedArgs);
1546
- if (unknowns.length > 0) return {
1547
- args: void 0,
1548
- argsResult: { issues: unknowns.map(({ key }) => ({
1549
- path: [key],
1550
- message: `Unknown option: "${key}"`
1551
- })) }
1552
- };
1553
- const earlyValidateAndPrompt = () => {
1554
- if (command.argsSchema) {
1555
- const providedKeys = new Set(Object.keys(preprocessedArgs).filter((k) => preprocessedArgs[k] !== void 0));
1556
- const earlyCheck = command.argsSchema["~standard"].validate(preprocessedArgs);
1557
- const checkForProvidedFieldErrors = (result) => {
1558
- if (!result.issues) return void 0;
1559
- const providedFieldIssues = result.issues.filter((issue) => {
1560
- const rootKey = issue.path?.[0];
1561
- return rootKey !== void 0 && providedKeys.has(String(rootKey));
1562
- });
1563
- if (providedFieldIssues.length > 0) return {
1564
- args: void 0,
1565
- argsResult: { issues: providedFieldIssues }
1566
- };
1567
- };
1568
- const earlyResult = thenMaybe(earlyCheck, (result) => checkForProvidedFieldErrors(result) ?? void 0);
1569
- if (earlyResult instanceof Promise) return earlyResult.then((err) => err ? err : doPrompt());
1570
- if (earlyResult) return earlyResult;
1571
- }
1572
- return doPrompt();
1573
- };
1574
- const doPrompt = () => {
1575
- return thenMaybe(promptInteractiveFields(preprocessedArgs, command, runtime, forceInteractive || void 0), (filledArgs) => {
1576
- return next({
1577
- rawArgs: filledArgs,
1578
- positionalArgs: []
1579
- });
1580
- });
1581
- };
1582
- return earlyValidateAndPrompt();
1583
- } }));
1584
- /**
1585
- * Extension that handles interactive prompting for missing arguments.
1586
- * Extracts `--interactive` / `-i` flags, resolves effective interactivity,
1587
- * and prompts for missing fields before passing filled args to validation.
1588
- *
1589
- * Usage:
1590
- * ```ts
1591
- * createPadrone('my-cli').extend(padroneInteractive())
1592
- * ```
1593
- */
1594
- function padroneInteractive() {
1595
- return ((builder) => builder.intercept(interactiveInterceptor));
1596
- }
1597
- //#endregion
1598
- //#region src/extension/repl.ts
1599
- /**
1600
- * Extension that adds REPL support:
1601
- * - `repl` command that starts an interactive REPL
1602
- * - `--repl` flag that starts the REPL from any invocation
1603
- *
1604
- * Usage:
1605
- * ```ts
1606
- * createPadrone('my-cli').extend(padroneRepl())
1607
- * ```
1608
- */
1609
- function padroneRepl(defaults) {
1610
- const disabled = defaults?.disabled;
1611
- return ((builder) => builder.command("repl", (c) => c.configure({
1612
- description: "Start an interactive REPL",
1613
- hidden: true
1614
- }).arguments(passthroughSchema({ scope: "string" }), { positional: ["scope"] }).async().action(async (args, ctx) => {
1615
- const prefs = {
1616
- ...defaults,
1617
- scope: args.scope ?? defaults?.scope
1618
- };
1619
- const { value } = await ctx.program.repl(prefs).drain();
1620
- return value;
1621
- })).intercept(createReplInterceptor(defaults, disabled)));
1622
- }
1623
- function createReplInterceptor(defaults, disabled) {
1624
- return defineInterceptor({
1625
- id: "padrone:repl",
1626
- name: "padrone:repl",
1627
- order: -1e3,
1628
- disabled
1629
- }, () => ({ start(ctx, next) {
1630
- const replInfo = checkReplFlag(ctx.input, ctx.command);
1631
- if (!replInfo) return next();
1632
- const program = ctx.program;
1633
- if (!program?.repl) return next();
1634
- const prefs = {
1635
- ...defaults,
1636
- scope: replInfo.scope ?? defaults?.scope
1637
- };
1638
- return program.repl(prefs).drain().then((r) => withDrain({
1639
- command: ctx.command,
1640
- args: void 0,
1641
- result: r.value
1642
- }));
1643
- } }));
1644
- }
1645
- /** Check for --repl flag in input. */
1646
- function checkReplFlag(input, rootCommand) {
1647
- if (!input) return null;
1648
- const parts = parseCliInputToParts(input);
1649
- const terms = parts.filter((p) => p.type === "term").map((p) => p.value);
1650
- const args = parts.filter((p) => p.type === "named");
1651
- const keyIs = (key, name) => key.length === 1 && key[0] === name;
1652
- if (!args.some((p) => p.type === "named" && keyIs(p.key, "repl"))) return null;
1653
- const normalizedTerms = [...terms];
1654
- if (normalizedTerms[0] === rootCommand.name) normalizedTerms.shift();
1655
- return { scope: normalizedTerms.length > 0 ? normalizedTerms.join(" ") : void 0 };
1656
- }
1657
- //#endregion
1658
- //#region src/extension/signal.ts
1659
- const signalMeta = {
1660
- id: "padrone:signal",
1661
- name: "padrone:signal",
1662
- order: -2e3
1663
- };
1664
- const signalInterceptor = defineInterceptor(signalMeta, () => {
1665
- const abortController = new AbortController();
1666
- let receivedSignal;
1667
- let lastSigintTime = 0;
1668
- let unsubscribe;
1669
- const DOUBLE_SIGINT_MS = 2e3;
1670
- const cleanup = () => {
1671
- unsubscribe?.();
1672
- unsubscribe = void 0;
1673
- };
1674
- const attachSignalInfo = (result) => {
1675
- if (receivedSignal && result && typeof result === "object") {
1676
- result.signal = receivedSignal;
1677
- result.exitCode = signalExitCode(receivedSignal);
1678
- }
1679
- return result;
1680
- };
1681
- return {
1682
- start(ctx, next) {
1683
- const runtimeExit = ctx.runtime.exit;
1684
- unsubscribe = ctx.runtime.onSignal?.((sig) => {
1685
- if (abortController.signal.aborted) {
1686
- if (sig === "SIGINT") {
1687
- const elapsed = Date.now() - lastSigintTime;
1688
- if (elapsed > 0 && elapsed < DOUBLE_SIGINT_MS) runtimeExit?.(signalExitCode(sig));
1689
- lastSigintTime = Date.now();
1690
- }
1691
- return;
1692
- }
1693
- if (sig === "SIGINT") lastSigintTime = Date.now();
1694
- receivedSignal = sig;
1695
- abortController.abort(sig);
1696
- });
1697
- return thenMaybe(next({ signal: abortController.signal }), (r) => {
1698
- cleanup();
1699
- return attachSignalInfo(r);
1700
- });
1701
- },
1702
- error(_ctx, next) {
1703
- return thenMaybe(next(), (er) => {
1704
- if (receivedSignal && er.error instanceof Error) er.error = new SignalError(receivedSignal, { cause: er.error });
1705
- return er;
1706
- });
1707
- },
1708
- shutdown(_ctx, next) {
1709
- cleanup();
1710
- return next();
1711
- }
1712
- };
1713
- });
1714
- /**
1715
- * Extension that wires process signal handling (SIGINT, SIGTERM, SIGHUP) into the interceptor lifecycle.
1716
- *
1717
- * - Creates an `AbortController` whose signal is propagated to all downstream phases.
1718
- * - Subscribes to `runtime.onSignal` to forward OS signals to the abort controller.
1719
- * - Implements SIGINT double-tap: two SIGINTs within 2 seconds force-exits the process.
1720
- * - Attaches `signal` and `exitCode` to results and errors when interrupted.
1721
- * - Cleans up the signal subscription on completion or failure.
1722
- *
1723
- * Included in the default extensions. Runs at order `-2000` (outermost).
1724
- */
1725
- function padroneSignalHandling(options) {
1726
- const interceptor = options?.disabled ? defineInterceptor({
1727
- ...signalMeta,
1728
- disabled: true
1729
- }, () => ({})) : signalInterceptor;
1730
- return ((builder) => builder.intercept(interceptor));
1731
- }
1732
- //#endregion
1733
- //#region src/extension/stdin.ts
1734
- const stdinInterceptor = defineInterceptor({
1735
- id: "padrone:stdin",
1736
- name: "padrone:stdin",
1737
- order: -1001
1738
- }, () => ({ validate(ctx, next) {
1739
- const stdinField = ctx.command.meta?.stdin;
1740
- if (!stdinField) return next();
1741
- if (stdinField in ctx.rawArgs && ctx.rawArgs[stdinField] !== void 0) return next();
1742
- const streamInfo = isAsyncStreamField(ctx.command.argsSchema, stdinField);
1743
- if (streamInfo) {
1744
- const stdinForStream = resolveStdinAlways(ctx.runtime);
1745
- return next({ rawArgs: applyValues(ctx.rawArgs, { [stdinField]: createStdinStream(stdinForStream, streamInfo.itemSchema) }) });
1746
- }
1747
- const stdin = resolveStdin(ctx.runtime);
1748
- if (!stdin) return next();
1749
- if (isArrayField(ctx.command.argsSchema, stdinField)) return (async () => {
1750
- const lines = [];
1751
- for await (const line of stdin.lines()) lines.push(line);
1752
- return next({ rawArgs: applyValues(ctx.rawArgs, { [stdinField]: lines }) });
1753
- })();
1754
- return stdin.text().then((text) => {
1755
- if (!text) return next();
1756
- return next({ rawArgs: applyValues(ctx.rawArgs, { [stdinField]: text }) });
1757
- });
1758
- } }));
1759
- /**
1760
- * Extension that reads stdin data into the argument field specified by `meta.stdin`.
1761
- * Included by default via `createPadrone()`.
1762
- *
1763
- * Read mode is inferred from the schema type:
1764
- * - `string` field → reads all stdin as a single string
1765
- * - `string[]` field → reads stdin line-by-line into an array
1766
- * - `AsyncIterable` field → returns a stream for line-by-line async consumption
1767
- *
1768
- * Stdin is only read when piped (not a TTY) and the field wasn't already provided via CLI flags.
1769
- */
1770
- function padroneStdin(options) {
1771
- const interceptor = options?.disabled ? defineInterceptor({
1772
- ...stdinInterceptor,
1773
- disabled: true
1774
- }, () => ({})) : stdinInterceptor;
1775
- return ((builder) => builder.intercept(interceptor));
1776
- }
1777
- //#endregion
1778
- //#region src/extension/suggestions.ts
1779
- function formatSuggestions(names, prefix = "") {
1780
- if (names.length === 0) return "";
1781
- const quoted = names.map((n) => `"${prefix}${n}"`);
1782
- if (quoted.length === 1) return `Did you mean ${quoted[0]}?`;
1783
- return `Did you mean ${quoted.slice(0, -1).join(", ")} or ${quoted.at(-1)}?`;
1784
- }
1785
- function findSourceCommand(commandPath, root) {
1786
- if (!commandPath || commandPath === root.name || commandPath === root.path) return root;
1787
- const parts = commandPath.split(" ");
1788
- let current = root;
1789
- for (const part of parts) {
1790
- const found = current.commands?.find((c) => {
1791
- resolveCommand(c);
1792
- return c.name === part || c.aliases?.includes(part);
1793
- });
1794
- if (found) current = found;
1795
- else break;
1796
- }
1797
- return current;
1798
- }
1799
- function enrichRoutingError(err, rootCommand) {
1800
- if (!(err instanceof RoutingError)) return err;
1801
- const unknownMatch = err.message.match(/^Unknown command: (\S+)/);
1802
- const unexpectedMatch = err.message.match(/^Unexpected arguments for '[^']+': (\S+)/);
1803
- const term = unknownMatch?.[1] ?? unexpectedMatch?.[1];
1804
- if (!term) return err;
1805
- const sourceCmd = findSourceCommand(err.command, rootCommand);
1806
- const candidateNames = [];
1807
- if (sourceCmd.commands) for (const cmd of sourceCmd.commands) {
1808
- resolveCommand(cmd);
1809
- if (!cmd.hidden) {
1810
- candidateNames.push(cmd.name);
1811
- if (cmd.aliases) candidateNames.push(...cmd.aliases);
1812
- }
1813
- }
1814
- const suggestionText = formatSuggestions(suggestSimilar(term, candidateNames));
1815
- if (!suggestionText) return err;
1816
- const suggestions = [suggestionText];
1817
- return new RoutingError(`${err.message}\n\n ${suggestionText}`, {
1818
- suggestions,
1819
- command: err.command
1820
- });
1821
- }
1822
- function enrichIssuesWithSuggestions(issues, knownOptions) {
1823
- return issues.map((i) => {
1824
- const unknownMatch = i.message?.match(/^Unknown option: "([^"]+)"$/);
1825
- if (unknownMatch) {
1826
- const similar = suggestSimilar(unknownMatch[1], knownOptions());
1827
- if (similar.length) {
1828
- const hint = formatSuggestions(similar, "--");
1829
- return {
1830
- ...i,
1831
- message: `${i.message} ${hint}`
1832
- };
1833
- }
1834
- return i;
1835
- }
1836
- const keys = i.keys ?? i.message?.match(/[Uu]nrecognized key(?:s)?[^"]*"([^"]+)"/)?.slice(1);
1837
- if (!keys?.length) return i;
1838
- const hints = keys.flatMap((k) => {
1839
- const similar = suggestSimilar(k, knownOptions());
1840
- return similar.length ? [formatSuggestions(similar, "--")] : [];
1841
- });
1842
- if (!hints.length) return i;
1843
- return {
1844
- ...i,
1845
- message: `${i.message} ${hints.join(" ")}`
1846
- };
1847
- });
1848
- }
1849
- const suggestionsInterceptor = defineInterceptor({
1850
- id: "padrone:suggestions",
1851
- name: "padrone:suggestions",
1852
- order: -500
1853
- }, () => ({
1854
- parse(ctx, next) {
1855
- try {
1856
- const result = next();
1857
- if (result instanceof Promise) return result.catch((err) => {
1858
- throw enrichRoutingError(err, ctx.command);
1859
- });
1860
- return result;
1861
- } catch (err) {
1862
- throw enrichRoutingError(err, ctx.command);
1863
- }
1864
- },
1865
- validate(ctx, next) {
1866
- return thenMaybe(next(), (v) => {
1867
- if (!v.argsResult?.issues?.length) return v;
1868
- const enriched = enrichIssuesWithSuggestions(v.argsResult.issues, () => getKnownOptionNames(ctx.command));
1869
- return {
1870
- ...v,
1871
- argsResult: {
1872
- ...v.argsResult,
1873
- issues: enriched
1874
- }
1875
- };
1876
- });
1877
- }
1878
- }));
1879
- function padroneSuggestions() {
1880
- return ((builder) => builder.intercept(suggestionsInterceptor));
1881
- }
1882
- //#endregion
1883
- //#region src/extension/version.ts
1884
- const versionInterceptor = defineInterceptor({
1885
- id: "padrone:version",
1886
- name: "padrone:version",
1887
- order: -1e3
1888
- }, () => ({ parse(_ctx, next) {
1889
- return thenMaybe(next(), (res) => {
1890
- if ((res.rawArgs.version || res.rawArgs.v || res.rawArgs.V) && !res.command.parent) {
1891
- delete res.rawArgs.version;
1892
- delete res.rawArgs.v;
1893
- delete res.rawArgs.V;
1894
- const versionCmd = res.command.commands?.find((c) => c.name === "version");
1895
- if (versionCmd) {
1896
- resolveCommand(versionCmd);
1897
- return {
1898
- ...res,
1899
- command: versionCmd,
1900
- rawArgs: {},
1901
- positionalArgs: []
1902
- };
1903
- }
1904
- }
1905
- return res;
1906
- });
1907
- } }));
1908
- /**
1909
- * Extension that adds version support:
1910
- * - `version` command
1911
- * - `--version` / `-v` / `-V` flags (root command only)
1912
- *
1913
- * Usage:
1914
- * ```ts
1915
- * createPadrone('my-cli').extend(padroneVersion())
1916
- * ```
1917
- */
1918
- function padroneVersion() {
1919
- return ((builder) => builder.command("version", (c) => c.configure({
1920
- description: "Display the version number",
1921
- hidden: true
1922
- }).action((_args, ctx) => {
1923
- return getVersion(getRootCommand(ctx.command).version);
1924
- })).intercept(versionInterceptor));
1925
- }
1926
- //#endregion
1927
- //#region src/feature/wrap.ts
1928
- /**
1929
- * Converts parsed arguments to CLI arguments for an external command.
1930
- */
1931
- function argsToCliArgs(input, positional = []) {
1932
- const args = [];
1933
- if (!input) return args;
1934
- const positionalValues = {};
1935
- const regularArguments = {};
1936
- for (const [key, value] of Object.entries(input)) if (positional.includes(key) || positional.includes(`...${key}`)) positionalValues[key] = value;
1937
- else regularArguments[key] = value;
1938
- for (const [key, value] of Object.entries(regularArguments)) {
1939
- if (value === void 0 || value === null) continue;
1940
- const flag = `--${key}`;
1941
- if (typeof value === "boolean") {
1942
- if (value) args.push(flag);
1943
- } else if (Array.isArray(value)) for (const item of value) args.push(flag, String(item));
1944
- else args.push(flag, String(value));
1945
- }
1946
- for (const posKey of positional) {
1947
- const isVariadic = posKey.startsWith("...");
1948
- const value = positionalValues[isVariadic ? posKey.slice(3) : posKey];
1949
- if (value === void 0 || value === null) continue;
1950
- if (isVariadic && Array.isArray(value)) args.push(...value.map(String));
1951
- else args.push(String(value));
1952
- }
1953
- return args;
1954
- }
1955
- /**
1956
- * Creates an action handler that wraps an external CLI tool.
1957
- * @param config - Configuration for wrapping the external command (includes optional schema)
1958
- * @param commandArguments - The command's arguments schema
1959
- * @param commandPositional - Default positional config from the wrapping command
1960
- */
1961
- function createWrapHandler(config, commandArguments, commandPositional) {
1962
- return async (args) => {
1963
- const { command, args: fixedArgs = [], inheritStdio = true, positional = commandPositional, schema: wrapSchema } = config;
1964
- const validationResult = (wrapSchema ? typeof wrapSchema === "function" ? wrapSchema(commandArguments) : wrapSchema : commandArguments)["~standard"].validate(args);
1965
- const processResult = (result) => {
1966
- if (result.issues) throw new ValidationError(`Wrap schema validation failed:\n${result.issues.map((i) => ` - ${i.path?.join(".") || "root"}: ${i.message}`).join("\n")}`, result.issues);
1967
- return result.value;
1968
- };
1969
- const regularArgs = argsToCliArgs(validationResult instanceof Promise ? await validationResult.then(processResult) : processResult(validationResult), positional);
1970
- const allArgs = [...fixedArgs, ...regularArgs];
1971
- const { spawn } = await import("node:child_process");
1972
- return new Promise((resolve, reject) => {
1973
- const proc = spawn(command, allArgs, { stdio: inheritStdio ? "inherit" : [
1974
- "ignore",
1975
- "pipe",
1976
- "pipe"
1977
- ] });
1978
- const stdoutChunks = [];
1979
- const stderrChunks = [];
1980
- if (!inheritStdio) {
1981
- proc.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
1982
- proc.stderr.on("data", (chunk) => stderrChunks.push(chunk));
1983
- }
1984
- const decoder = new TextDecoder();
1985
- proc.on("error", reject);
1986
- proc.on("close", (code) => {
1987
- const exitCode = code ?? 1;
1988
- resolve({
1989
- exitCode,
1990
- stdout: inheritStdio ? void 0 : decoder.decode(concatBytes(stdoutChunks)),
1991
- stderr: inheritStdio ? void 0 : decoder.decode(concatBytes(stderrChunks)),
1992
- success: exitCode === 0
1993
- });
1994
- });
1995
- });
1996
- };
1997
- }
1998
- //#endregion
1999
- //#region src/core/exec.ts
2000
- /**
2001
- * Collects registered interceptors from the command's parent chain (root → ... → target).
2002
- * Root/program interceptors come first (outermost), target command's interceptors last (innermost).
2003
- */
2004
- function collectInterceptors(cmd, rootCommand) {
2005
- const chain = [];
2006
- let current = cmd;
2007
- while (current) {
2008
- const isTarget = current === cmd;
2009
- if (!current.parent) {
2010
- if (rootCommand.interceptors?.length) {
2011
- const isRootTarget = cmd === rootCommand || !cmd.parent;
2012
- chain.unshift(isRootTarget ? rootCommand.interceptors : rootCommand.interceptors.filter((i) => i.meta.inherit !== false));
2013
- }
2014
- } else if (current.interceptors?.length) chain.unshift(isTarget ? current.interceptors : current.interceptors.filter((i) => i.meta.inherit !== false));
2015
- current = current.parent;
2016
- }
2017
- return chain.flat();
2018
- }
2019
- /** Wraps an error into a result, preserving any signal info from the pipeline. */
2020
- function errorResultWithSignal(err) {
2021
- const result = errorResult(err);
2022
- if (err instanceof SignalError) {
2023
- result.signal = err.signal;
2024
- result.exitCode = err.exitCode;
2025
- }
2026
- return result;
2027
- }
2028
- /** Resolve context by walking the command parent chain and applying transforms from root to target. */
2029
- function resolveContext(command, initialContext) {
2030
- const chain = [];
2031
- let current = command;
2032
- while (current) {
2033
- chain.unshift(current);
2034
- current = current.parent;
2035
- }
2036
- let resolved = initialContext;
2037
- for (const cmd of chain) if (cmd.contextTransform) resolved = cmd.contextTransform(resolved);
2038
- return resolved;
2039
- }
2040
- /** Validate parse result — reject unmatched terms when the command doesn't accept positional args. */
2041
- function validateParseResult(parseResult, rootCommand) {
2042
- const { command, rawArgs, args, unmatchedTerms } = parseResult;
2043
- if (unmatchedTerms.length > 0) {
2044
- if (!(command.meta?.positional && command.meta.positional.length > 0)) {
2045
- const isRootCommand = command === rootCommand;
2046
- const commandDisplayName = command.name || command.aliases?.[0] || command.path || "(default)";
2047
- throw new RoutingError(isRootCommand ? `Unknown command: ${unmatchedTerms[0]}` : `Unexpected arguments for '${commandDisplayName}': ${unmatchedTerms.join(" ")}`, { command: command.path || command.name });
2048
- }
2049
- }
2050
- return {
2051
- command,
2052
- rawArgs,
2053
- positionalArgs: args
2054
- };
2055
- }
2056
- /** Handle validation issues based on error mode: throw (hard) or return result with issues (soft). */
2057
- function handleValidationIssues(argsResult, command, errorMode) {
2058
- if (errorMode === "hard") throw new ValidationError(`Validation error:\n${formatIssueMessages(argsResult.issues)}`, argsResult.issues, { command: command.path || command.name });
2059
- return withDrain({
2060
- command,
2061
- args: void 0,
2062
- argsResult,
2063
- result: void 0
2064
- });
2065
- }
2066
- /**
2067
- * Core execution logic shared by eval() and cli().
2068
- * errorMode controls validation error behavior:
2069
- * - 'soft': return result with issues (eval behavior)
2070
- * - 'hard': print error + help and throw (cli-without-input behavior)
2071
- */
2072
- function execCommand(resolvedInput, ctx, evalOptions, errorMode = "soft", caller = "eval") {
2073
- const { rootCommand, parseCommandFn, collectInterceptorsFn } = ctx;
2074
- const baseRuntime = getCommandRuntime(rootCommand);
2075
- const runtime = evalOptions?.runtime ? Object.assign({}, baseRuntime, Object.fromEntries(Object.entries(evalOptions.runtime).filter(([, v]) => v !== void 0))) : baseRuntime;
2076
- const inertSignal = new AbortController().signal;
2077
- const pipelineState = { phase: "start" };
2078
- const initialContext = evalOptions?.context;
2079
- const factoryCache = /* @__PURE__ */ new Map();
2080
- const rootInterceptors = resolveRegisteredInterceptors(rootCommand.interceptors ?? [], factoryCache);
2081
- const rootInterceptorSet = new Set(rootInterceptors);
2082
- const runPipeline = (signal, pipelineContext) => {
2083
- const parseCtx = {
2084
- input: resolvedInput,
2085
- command: rootCommand,
2086
- signal,
2087
- context: pipelineContext,
2088
- runtime,
2089
- program: ctx.builder,
2090
- caller
2091
- };
2092
- const coreParse = (parseCtx) => validateParseResult(parseCommandFn(parseCtx.input), rootCommand);
2093
- const parsedOrPromise = runInterceptorChain("parse", rootInterceptors, parseCtx, coreParse);
2094
- const continueAfterParse = (parsed) => {
2095
- const { command } = parsed;
2096
- pipelineState.phase = "parse";
2097
- pipelineState.rawArgs = parsed.rawArgs;
2098
- pipelineState.positionalArgs = parsed.positionalArgs;
2099
- const commandInterceptors = resolveRegisteredInterceptors(collectInterceptorsFn(command), factoryCache);
2100
- const commandOnlyInterceptors = commandInterceptors.filter((i) => !rootInterceptorSet.has(i));
2101
- const context = resolveContext(command, pipelineContext);
2102
- const routedOrPromise = runInterceptorChain("route", commandInterceptors, {
2103
- ...parseCtx,
2104
- command,
2105
- rawArgs: parsed.rawArgs,
2106
- positionalArgs: parsed.positionalArgs,
2107
- context
2108
- }, () => {});
2109
- const continueAfterRoute = () => {
2110
- pipelineState.phase = "route";
2111
- const runValidateAndExecute = () => {
2112
- const validateCtx = {
2113
- ...parseCtx,
2114
- command,
2115
- rawArgs: parsed.rawArgs,
2116
- positionalArgs: parsed.positionalArgs,
2117
- context,
2118
- evalInteractive: evalOptions?.interactive
2119
- };
2120
- const coreValidate = (validateCtx) => {
2121
- const { args: preprocessedArgs, issues } = buildCommandArgs(validateCtx.command, validateCtx.rawArgs, validateCtx.positionalArgs);
2122
- if (issues) return {
2123
- args: void 0,
2124
- argsResult: { issues }
2125
- };
2126
- return thenMaybe(validateCommandArgs(validateCtx.command, preprocessedArgs), (v) => v);
2127
- };
2128
- const validatedOrPromise = runInterceptorChain("validate", commandInterceptors, validateCtx, coreValidate);
2129
- const continueAfterValidate = (v) => {
2130
- pipelineState.phase = "validate";
2131
- pipelineState.args = v.args;
2132
- if (v.argsResult?.issues) return handleValidationIssues(v.argsResult, command, errorMode);
2133
- const executeCtx = {
2134
- ...validateCtx,
2135
- args: v.args
2136
- };
2137
- const coreExecute = (executeCtx) => {
2138
- const handler = command.action ?? noop;
2139
- const actionCtx = {
2140
- runtime: executeCtx.runtime,
2141
- command: executeCtx.command,
2142
- program: ctx.builder,
2143
- signal: executeCtx.signal,
2144
- context: executeCtx.context,
2145
- caller
2146
- };
2147
- return { result: handler(executeCtx.args, actionCtx) };
2148
- };
2149
- pipelineState.phase = "execute";
2150
- return thenMaybe(runInterceptorChain("execute", commandInterceptors, executeCtx, coreExecute), (e) => {
2151
- const finalize = (result) => withDrain({
2152
- command,
2153
- args: v.args,
2154
- argsResult: v.argsResult,
2155
- result
2156
- });
2157
- if (e.result instanceof Promise) return e.result.then(finalize);
2158
- return finalize(e.result);
2159
- });
2160
- };
2161
- return thenMaybe(warnIfUnexpectedAsync(validatedOrPromise, command), continueAfterValidate);
2162
- };
2163
- return wrapWithCommandLifecycle(commandOnlyInterceptors, command, resolvedInput, runValidateAndExecute, (result) => withDrain({
2164
- command,
2165
- args: void 0,
2166
- argsResult: void 0,
2167
- result
2168
- }), signal, context, runtime, ctx.builder, caller, pipelineState);
2169
- };
2170
- return thenMaybe(routedOrPromise, continueAfterRoute);
2171
- };
2172
- return thenMaybe(parsedOrPromise, continueAfterParse);
2173
- };
2174
- return wrapWithLifecycle(rootInterceptors, rootCommand, resolvedInput, runPipeline, (result) => withDrain({
2175
- command: rootCommand,
2176
- args: void 0,
2177
- argsResult: void 0,
2178
- result
2179
- }), inertSignal, initialContext, runtime, ctx.builder, caller, pipelineState);
2180
- }
2181
- //#endregion
2182
- //#region src/feature/repl-loop.ts
2183
- /**
2184
- * Creates a REPL async iterable for running commands interactively.
2185
- */
2186
- function createReplIterator(deps, options) {
2187
- const { existingCommand, evalCommand, replActiveRef } = deps;
2188
- if (replActiveRef.value) {
2189
- getCommandRuntime(existingCommand).error("REPL is already running. Nested REPL sessions are not supported.");
2190
- return (async function* () {})();
2191
- }
2192
- const runtime = getCommandRuntime(existingCommand);
2193
- const programName = existingCommand.name || "padrone";
2194
- const env = runtime.env();
2195
- const useAnsi = runtime.format === "ansi" || runtime.format === "auto" && !env.NO_COLOR && !env.CI && runtime.terminal?.isTTY === true;
2196
- const commandHistory = [];
2197
- const resolveScope = (scope) => {
2198
- const parts = scope.split(/\s+/);
2199
- const stack = [];
2200
- let current = existingCommand;
2201
- for (const part of parts) {
2202
- const found = findCommandByName(part, current.commands);
2203
- if (!found) break;
2204
- stack.push(found);
2205
- current = found;
2206
- }
2207
- return stack;
2208
- };
2209
- async function* replIterator() {
2210
- replActiveRef.value = true;
2211
- const showGreeting = options?.greeting !== false;
2212
- const showHint = options?.hint !== false;
2213
- if (showGreeting || showHint) runtime.output("");
2214
- if (showGreeting) if (options?.greeting) runtime.output(options.greeting);
2215
- else {
2216
- const displayName = existingCommand.title || programName;
2217
- const version = existingCommand.version ? await getVersion(existingCommand.version) : void 0;
2218
- const greeting = version ? `Welcome to ${displayName} v${version}` : `Welcome to ${displayName}`;
2219
- runtime.output(greeting);
2220
- }
2221
- if (showHint) {
2222
- const hintText = (typeof options?.hint === "string" ? options.hint : void 0) ?? "Type \".help\" for more information, \".exit\" to quit.";
2223
- runtime.output(useAnsi ? `\x1b[2m${hintText}\x1b[0m` : hintText);
2224
- }
2225
- if (showGreeting || showHint) runtime.output("");
2226
- const scopeStack = options?.scope ? resolveScope(options.scope) : [];
2227
- const getScopeCommand = () => scopeStack.length ? scopeStack[scopeStack.length - 1] : existingCommand;
2228
- const getScopePath = () => scopeStack.map((c) => c.name).join(" ");
2229
- const buildPrompt = () => {
2230
- if (options?.prompt) return typeof options.prompt === "function" ? options.prompt() : options.prompt;
2231
- const scopePath = getScopePath();
2232
- const label = scopePath ? `${programName}/${scopePath.replace(/ /g, "/")}` : programName;
2233
- return useAnsi ? `\x1b[1m${label}\x1b[0m ❯ ` : `${label} ❯ `;
2234
- };
2235
- const buildScopedCompleter = () => {
2236
- return buildReplCompleter(getScopeCommand(), { inScope: scopeStack.length > 0 });
2237
- };
2238
- const sessionConfig = { history: options?.history };
2239
- if (options?.completion !== false) sessionConfig.completer = buildScopedCompleter();
2240
- const session = runtime.readLine ? void 0 : createTerminalReplSession(sessionConfig);
2241
- const questionFn = session ? (prompt) => session.question(prompt) : runtime.readLine;
2242
- const updateCompleter = () => {
2243
- if (options?.completion === false) return;
2244
- const completer = buildScopedCompleter();
2245
- if (session) session.completer = completer;
2246
- sessionConfig.completer = completer;
2247
- };
2248
- let lastSigintTime = 0;
2249
- try {
2250
- while (true) {
2251
- const input = await questionFn(buildPrompt());
2252
- if (input === null) break;
2253
- if (input === REPL_SIGINT) {
2254
- const now = Date.now();
2255
- if (now - lastSigintTime < 2e3) break;
2256
- lastSigintTime = now;
2257
- runtime.output("(press Ctrl+C again to exit, or Ctrl+D)");
2258
- continue;
2259
- }
2260
- const trimmed = input.trim();
2261
- if (!trimmed) continue;
2262
- lastSigintTime = 0;
2263
- commandHistory.push(trimmed);
2264
- if (trimmed === ".exit" || trimmed === ".quit") break;
2265
- if (trimmed === ".clear") {
2266
- runtime.output("\x1B[2J\x1B[H");
2267
- continue;
2268
- }
2269
- if (trimmed === ".help") {
2270
- const lines = [
2271
- "REPL Commands:",
2272
- " . Execute the current scoped command",
2273
- " .help Print this help message",
2274
- " .exit Exit the REPL",
2275
- " .clear Clear the screen",
2276
- " .history Show command history",
2277
- " .scope <cmd> Scope into a subcommand",
2278
- " .scope .. Go up one scope level"
2279
- ];
2280
- lines.push("", "Keybindings:", " Ctrl+C Cancel current line (press twice to exit)", " Ctrl+D Exit the REPL", " Up/Down Navigate history", " Tab Auto-complete", "", "Type \"help\" to see available commands.");
2281
- runtime.output(lines.join("\n"));
2282
- continue;
2283
- }
2284
- if (trimmed === ".history") {
2285
- const entries = commandHistory.slice(0, -1);
2286
- if (entries.length === 0) runtime.output("No history.");
2287
- else runtime.output(entries.map((entry, i) => `${i + 1} ${entry}`).join("\n"));
2288
- continue;
2289
- }
2290
- if (trimmed.startsWith(".scope ") || trimmed === ".scope") {
2291
- const target = trimmed.slice(6).trim();
2292
- if (target === ".." || target === "") {
2293
- if (scopeStack.length > 0) {
2294
- scopeStack.pop();
2295
- updateCompleter();
2296
- }
2297
- } else {
2298
- const found = findCommandByName(target, getScopeCommand().commands);
2299
- if (found) if (found.commands?.length) {
2300
- scopeStack.push(found);
2301
- updateCompleter();
2302
- } else runtime.error(`"${target}" has no subcommands to scope into.`);
2303
- else runtime.error(`Unknown command: ${target}`);
2304
- }
2305
- continue;
2306
- }
2307
- if (trimmed === "..") {
2308
- if (scopeStack.length > 0) {
2309
- scopeStack.pop();
2310
- updateCompleter();
2311
- }
2312
- continue;
2313
- }
2314
- let evalInput = trimmed;
2315
- if (trimmed === ".") evalInput = "";
2316
- const prefix = options?.outputPrefix;
2317
- const prefixLines = prefix ? (text) => text.split("\n").map((l) => prefix + l).join("\n") : void 0;
2318
- const savedRuntimes = [];
2319
- if (prefixLines) {
2320
- const prefixedRuntime = {
2321
- ...existingCommand.runtime,
2322
- output: (...args) => {
2323
- const first = args[0];
2324
- runtime.output(typeof first === "string" ? prefixLines(first) : first, ...args.slice(1));
2325
- },
2326
- error: (text) => runtime.error(prefixLines(text))
2327
- };
2328
- const patchAll = (cmd) => {
2329
- savedRuntimes.push({
2330
- cmd,
2331
- runtime: cmd.runtime
2332
- });
2333
- cmd.runtime = prefixedRuntime;
2334
- cmd.commands?.forEach(patchAll);
2335
- };
2336
- patchAll(existingCommand);
2337
- }
2338
- const sp = options?.spacing;
2339
- const isSpacingObject = typeof sp === "object" && sp !== null && !Array.isArray(sp);
2340
- const spacingBefore = isSpacingObject ? sp.before : sp;
2341
- const spacingAfter = isSpacingObject ? sp.after : sp;
2342
- const emitSpacingLine = (value) => {
2343
- if (typeof value === "string") {
2344
- const sep = value.length === 1 ? value.repeat(runtime.terminal?.columns ?? 80) : value;
2345
- runtime.output(sep);
2346
- } else if (value) runtime.output("");
2347
- };
2348
- const emitSpacing = (value) => {
2349
- if (!value) return;
2350
- if (Array.isArray(value)) for (const line of value) emitSpacingLine(line);
2351
- else emitSpacingLine(value);
2352
- };
2353
- emitSpacing(spacingBefore);
2354
- const scopePath = getScopePath();
2355
- const scopedInput = scopePath ? evalInput ? `${scopePath} ${evalInput}` : scopePath : evalInput;
2356
- try {
2357
- const result = await evalCommand(scopedInput, { caller: "repl" });
2358
- if (result.error) {
2359
- const msg = result.error instanceof Error ? result.error.message : String(result.error);
2360
- runtime.error(prefixLines ? prefixLines(msg) : msg);
2361
- } else if (result.argsResult?.issues) {
2362
- const msg = `Validation error:\n${result.argsResult.issues.map((i) => ` - ${i.path?.join(".") || "root"}: ${i.message}`).join("\n")}`;
2363
- runtime.error(prefixLines ? prefixLines(msg) : msg);
2364
- }
2365
- yield result;
2366
- } catch (err) {
2367
- const msg = err instanceof Error ? err.message : String(err);
2368
- runtime.error(prefixLines ? prefixLines(msg) : msg);
2369
- } finally {
2370
- for (const { cmd, runtime: saved } of savedRuntimes) cmd.runtime = saved;
2371
- emitSpacing(spacingAfter);
2372
- }
2373
- }
2374
- } finally {
2375
- replActiveRef.value = false;
2376
- session?.close();
2377
- }
2378
- }
2379
- const iterable = replIterator();
2380
- iterable.drain = async () => {
2381
- try {
2382
- const results = [];
2383
- for await (const result of iterable) results.push(result);
2384
- return { value: results };
2385
- } catch (err) {
2386
- return { error: err };
2387
- }
2388
- };
2389
- return iterable;
2390
- }
2391
- //#endregion
2392
- //#region src/core/program-methods.ts
2393
- function createProgramMethods(ctx, evalCommand) {
2394
- const { rootCommand } = ctx;
2395
- const inertSignal = new AbortController().signal;
2396
- const stringify = (command = "", args) => {
2397
- const commandObj = typeof command === "string" ? findCommandByName(command, rootCommand.commands) : command;
2398
- if (!commandObj) throw new RoutingError(`Command "${command ?? ""}" not found`);
2399
- const parts = [];
2400
- if (commandObj.path) parts.push(commandObj.path);
2401
- const positionalConfig = commandObj.meta?.positional ? parsePositionalConfig(commandObj.meta.positional) : [];
2402
- const positionalNames = new Set(positionalConfig.map((p) => p.name));
2403
- const negativeKeyword = {};
2404
- if (commandObj.argsSchema) {
2405
- const { negatives } = extractSchemaMetadata(commandObj.argsSchema, commandObj.meta?.fields, commandObj.meta?.autoAlias);
2406
- for (const [keyword, argName] of Object.entries(negatives)) if (!(argName in negativeKeyword)) negativeKeyword[argName] = keyword;
2407
- }
2408
- if (args && typeof args === "object") {
2409
- for (const { name, variadic } of positionalConfig) {
2410
- const value = args[name];
2411
- if (value === void 0) continue;
2412
- if (variadic && Array.isArray(value)) for (const v of value) {
2413
- const vStr = String(v);
2414
- if (vStr.includes(" ")) parts.push(`"${vStr}"`);
2415
- else parts.push(vStr);
2416
- }
2417
- else {
2418
- const argStr = String(value);
2419
- if (argStr.includes(" ")) parts.push(`"${argStr}"`);
2420
- else parts.push(argStr);
2421
- }
2422
- }
2423
- const stringifyValue = (key, value) => {
2424
- if (value === void 0) return;
2425
- if (typeof value === "boolean") if (value) parts.push(`--${key}`);
2426
- else if (negativeKeyword[key]) parts.push(`--${negativeKeyword[key]}`);
2427
- else parts.push(`--no-${key}`);
2428
- else if (Array.isArray(value)) for (const v of value) {
2429
- const vStr = String(v);
2430
- if (vStr.includes(" ")) parts.push(`--${key}="${vStr}"`);
2431
- else parts.push(`--${key}=${vStr}`);
2432
- }
2433
- else if (typeof value === "object" && value !== null) for (const [nestedKey, nestedValue] of Object.entries(value)) stringifyValue(`${key}.${nestedKey}`, nestedValue);
2434
- else if (typeof value === "string") if (value.includes(" ")) parts.push(`--${key}="${value}"`);
2435
- else parts.push(`--${key}=${value}`);
2436
- else parts.push(`--${key}=${value}`);
2437
- };
2438
- for (const [key, value] of Object.entries(args)) {
2439
- if (value === void 0 || positionalNames.has(key)) continue;
2440
- stringifyValue(key, value);
2441
- }
2442
- }
2443
- return parts.join(" ");
2444
- };
2445
- const resolveContext = (command, initialContext) => {
2446
- const chain = [];
2447
- let current = command;
2448
- while (current) {
2449
- chain.unshift(current);
2450
- current = current.parent;
2451
- }
2452
- let resolved = initialContext;
2453
- for (const cmd of chain) if (cmd.contextTransform) resolved = cmd.contextTransform(resolved);
2454
- return resolved;
2455
- };
2456
- const run = (command, args, prefs) => {
2457
- try {
2458
- const commandObj = typeof command === "string" ? findCommandByName(command, rootCommand.commands) : command;
2459
- if (!commandObj) throw new RoutingError(`Command "${command ?? ""}" not found`);
2460
- if (!commandObj.action) throw new RoutingError(`Command "${commandObj.path}" has no action`, { command: commandObj.path });
2461
- const executeCtx = {
2462
- command: commandObj,
2463
- input: void 0,
2464
- rawArgs: {},
2465
- positionalArgs: [],
2466
- args,
2467
- signal: inertSignal,
2468
- context: resolveContext(commandObj, prefs?.context),
2469
- runtime: getCommandRuntime(commandObj),
2470
- program: ctx.builder,
2471
- caller: "run"
2472
- };
2473
- const coreExecute = (executeCtx) => {
2474
- const actionCtx = {
2475
- runtime: executeCtx.runtime,
2476
- command: executeCtx.command,
2477
- program: ctx.builder,
2478
- signal: inertSignal,
2479
- context: executeCtx.context,
2480
- caller: "run"
2481
- };
2482
- return { result: commandObj.action(executeCtx.args, actionCtx) };
2483
- };
2484
- const executedOrPromise = runInterceptorChain("execute", resolveRegisteredInterceptors(collectInterceptors(commandObj, rootCommand), /* @__PURE__ */ new Map()), executeCtx, coreExecute);
2485
- const toResult = (e) => withDrain({
2486
- command: commandObj,
2487
- args,
2488
- result: e.result
2489
- });
2490
- if (executedOrPromise instanceof Promise) return executedOrPromise.then(toResult).catch((err) => errorResult(err, {
2491
- command: commandObj,
2492
- args
2493
- }));
2494
- return toResult(executedOrPromise);
2495
- } catch (err) {
2496
- return errorResult(err);
2497
- }
2498
- };
2499
- const tool = () => {
2500
- resolveAllCommands(rootCommand);
2501
- const description = `Run a command. Pass the full command string including arguments. Use "help <command>" for detailed usage.\n\n${generateHelp(rootCommand, void 0, { format: "text" })}`;
2502
- return {
2503
- type: "function",
2504
- name: rootCommand.name,
2505
- strict: true,
2506
- title: rootCommand.description,
2507
- description,
2508
- inputExamples: [{ input: { command: "<command> [positionals...] [arguments...]" } }],
2509
- inputSchema: {
2510
- [Symbol.for("vercel.ai.schema")]: true,
2511
- jsonSchema: {
2512
- type: "object",
2513
- properties: { command: { type: "string" } },
2514
- additionalProperties: false
2515
- },
2516
- _type: void 0,
2517
- validate: (value) => {
2518
- const command = value?.command;
2519
- if (typeof command === "string") return {
2520
- success: true,
2521
- value: { command }
2522
- };
2523
- return {
2524
- success: false,
2525
- error: /* @__PURE__ */ new Error("Expected an object with command property as string.")
2526
- };
2527
- }
2528
- },
2529
- needsApproval: async (input) => {
2530
- const parsed = await parse(input.command);
2531
- if (typeof parsed.command.needsApproval === "function") return parsed.command.needsApproval(parsed.args);
2532
- if (parsed.command.needsApproval != null) return !!parsed.command.needsApproval;
2533
- return !!parsed.command.mutation;
2534
- },
2535
- execute: async (input) => {
2536
- const output = [];
2537
- const errors = [];
2538
- return {
2539
- result: (await evalCommand(input.command, {
2540
- caller: "tool",
2541
- runtime: {
2542
- output: (...args) => output.push(args.map(String).join(" ")),
2543
- error: (text) => errors.push(text),
2544
- interactive: "unsupported",
2545
- format: "text"
2546
- }
2547
- })).result,
2548
- logs: output.join("\n"),
2549
- error: errors.join("\n")
2550
- };
2551
- }
2552
- };
2553
- };
2554
- const replActiveRef = { value: false };
2555
- const replFn = (options) => createReplIterator({
2556
- existingCommand: rootCommand,
2557
- evalCommand,
2558
- replActiveRef
2559
- }, options);
2560
- const cli = (cliOptions) => {
2561
- try {
2562
- const result = execCommand(getCommandRuntime(rootCommand).argv().join(" ") || void 0, ctx, cliOptions, "hard", "cli");
2563
- if (result instanceof Promise) return withPromiseDrain(result.catch((err) => errorResultWithSignal(err)));
2564
- return makeThenable(result);
2565
- } catch (err) {
2566
- return makeThenable(errorResultWithSignal(err));
2567
- }
2568
- };
2569
- const find = (command) => {
2570
- if (typeof command !== "string") return findCommandByName(command.path, rootCommand.commands);
2571
- return findCommandByName(command, rootCommand.commands);
2572
- };
2573
- const parse = (input) => {
2574
- const { command, rawArgs, args } = ctx.parseCommandFn(input);
2575
- return makeThenable(warnIfUnexpectedAsync(thenMaybe(coreValidateForParse(command, rawArgs, args), (v) => ({
2576
- command,
2577
- args: v.args,
2578
- argsResult: v.argsResult
2579
- })), command));
2580
- };
2581
- const help = (command, prefs) => {
2582
- resolveAllCommands(rootCommand);
2583
- const commandObj = !command ? rootCommand : typeof command === "string" ? findCommandByName(command, rootCommand.commands) : command;
2584
- if (!commandObj) throw new RoutingError(`Command "${command ?? ""}" not found`);
2585
- const runtime = getCommandRuntime(rootCommand);
2586
- return generateHelp(rootCommand, commandObj, {
2587
- ...prefs,
2588
- format: prefs?.format ?? runtime.format,
2589
- theme: prefs?.theme ?? runtime.theme,
2590
- terminal: prefs?.terminal ?? runtime.terminal,
2591
- env: prefs?.env ?? runtime.env()
2592
- });
2593
- };
2594
- const api = () => {
2595
- resolveAllCommands(rootCommand);
2596
- function buildApi(command) {
2597
- const runCommand = ((args) => run(command, args).result);
2598
- if (!command.commands) return runCommand;
2599
- for (const cmd of command.commands) runCommand[cmd.name] = buildApi(cmd);
2600
- return runCommand;
2601
- }
2602
- return buildApi(rootCommand);
2603
- };
2604
- const completion = async (shell) => {
2605
- resolveAllCommands(rootCommand);
2606
- const { generateCompletionOutput } = await import("./completion-D8qkAinX.mjs");
2607
- return generateCompletionOutput(rootCommand, shell);
2608
- };
2609
- const mcp = async (prefs) => {
2610
- resolveAllCommands(rootCommand);
2611
- const { startMcpServer } = await import("./mcp-wCoVFTXz.mjs");
2612
- return startMcpServer(ctx.builder, rootCommand, evalCommand, prefs);
2613
- };
2614
- const serve = async (prefs) => {
2615
- resolveAllCommands(rootCommand);
2616
- const { startServeServer } = await import("./serve-ICZFl3xr.mjs");
2617
- return startServeServer(ctx.builder, rootCommand, evalCommand, prefs);
2618
- };
2619
- return {
2620
- find,
2621
- parse,
2622
- stringify,
2623
- run,
2624
- eval: evalCommand,
2625
- cli,
2626
- tool,
2627
- repl: replFn,
2628
- api,
2629
- help,
2630
- completion,
2631
- mcp,
2632
- serve
2633
- };
2634
- }
2635
- //#endregion
2636
- //#region src/core/create.ts
2637
- function createPadrone(name, options) {
2638
- let builder = createPadroneBuilder({
2639
- name,
2640
- path: "",
2641
- commands: []
2642
- });
2643
- const b = options?.builtins;
2644
- if (b?.help !== false) builder = builder.extend(padroneHelp());
2645
- if (b?.version !== false) builder = builder.extend(padroneVersion());
2646
- if (b?.repl !== false) builder = builder.extend(padroneRepl());
2647
- if (b?.color !== false) builder = builder.extend(padroneColor());
2648
- if (b?.suggestions !== false) builder = builder.extend(padroneSuggestions());
2649
- if (b?.signal !== false) builder = builder.extend(padroneSignalHandling());
2650
- if (b?.autoOutput !== false) builder = builder.extend(padroneAutoOutput());
2651
- if (b?.stdin !== false) builder = builder.extend(padroneStdin());
2652
- if (b?.interactive !== false) builder = builder.extend(padroneInteractive());
2653
- return builder;
2654
- }
2655
- function createPadroneBuilder(inputCommand) {
2656
- const existingCommand = inputCommand.commands?.length && inputCommand.commands.some((c) => c.parent && c.parent !== inputCommand) ? {
2657
- ...inputCommand,
2658
- commands: inputCommand.commands.map((c) => c.parent && c.parent !== inputCommand ? {
2659
- ...c,
2660
- parent: inputCommand
2661
- } : c)
2662
- } : inputCommand;
2663
- const parseCommandFn = (input) => parseCommand(input, existingCommand, findCommandByName);
2664
- const collectInterceptorsFn = (cmd) => collectInterceptors(cmd, existingCommand);
2665
- const execCtx = {
2666
- rootCommand: existingCommand,
2667
- builder: void 0,
2668
- parseCommandFn,
2669
- collectInterceptorsFn
2670
- };
2671
- const evalCommand = (input, evalOptions) => {
2672
- try {
2673
- const result = execCommand(input, execCtx, evalOptions, "soft", evalOptions?.caller ?? "eval");
2674
- if (result instanceof Promise) return withPromiseDrain(result.catch((err) => errorResultWithSignal(err)));
2675
- return makeThenable(result);
2676
- } catch (err) {
2677
- return makeThenable(errorResultWithSignal(err));
2678
- }
2679
- };
2680
- const builder = {
2681
- extend(extension) {
2682
- return extension(builder);
2683
- },
2684
- configure(config) {
2685
- return createPadroneBuilder({
2686
- ...existingCommand,
2687
- ...config
2688
- });
2689
- },
2690
- runtime(runtimeConfig) {
2691
- return createPadroneBuilder({
2692
- ...existingCommand,
2693
- runtime: {
2694
- ...existingCommand.runtime,
2695
- ...runtimeConfig
2696
- }
2697
- });
2698
- },
2699
- async() {
2700
- return createPadroneBuilder({
2701
- ...existingCommand,
2702
- isAsync: true
2703
- });
2704
- },
2705
- context(transform) {
2706
- if (!transform) return createPadroneBuilder({ ...existingCommand });
2707
- const existing = existingCommand.contextTransform;
2708
- const composed = existing ? (ctx) => transform(existing(ctx)) : transform;
2709
- return createPadroneBuilder({
2710
- ...existingCommand,
2711
- contextTransform: composed
2712
- });
2713
- },
2714
- arguments(schema, meta) {
2715
- const resolvedArgs = typeof schema === "function" ? schema(existingCommand.argsSchema) : schema;
2716
- const isAsync = existingCommand.isAsync || isAsyncBranded(resolvedArgs) || hasInteractiveConfig(meta);
2717
- return createPadroneBuilder({
2718
- ...existingCommand,
2719
- argsSchema: resolvedArgs,
2720
- meta,
2721
- isAsync
2722
- });
2723
- },
2724
- action(handler = noop) {
2725
- const baseHandler = existingCommand.action ?? noop;
2726
- return createPadroneBuilder({
2727
- ...existingCommand,
2728
- action: (args, ctx) => handler(args, ctx, baseHandler)
2729
- });
2730
- },
2731
- wrap(config) {
2732
- const handler = createWrapHandler(config, existingCommand.argsSchema, existingCommand.meta?.positional);
2733
- return createPadroneBuilder({
2734
- ...existingCommand,
2735
- action: handler
2736
- });
2737
- },
2738
- command(nameOrNames, builderFn) {
2739
- const name = Array.isArray(nameOrNames) ? nameOrNames[0] : nameOrNames;
2740
- const aliases = Array.isArray(nameOrNames) && nameOrNames.length > 1 ? nameOrNames.slice(1) : void 0;
2741
- const existingSubcommand = existingCommand.commands?.find((c) => c.name === name);
2742
- if (existingSubcommand) resolveCommand(existingSubcommand);
2743
- const initialCommand = existingSubcommand ? {
2744
- ...existingSubcommand,
2745
- aliases: aliases ?? existingSubcommand.aliases,
2746
- parent: existingCommand
2747
- } : {
2748
- name,
2749
- path: existingCommand.path ? `${existingCommand.path} ${name}` : name,
2750
- aliases,
2751
- parent: existingCommand,
2752
- "~types": {}
2753
- };
2754
- if (builderFn) {
2755
- const lazyCmd = { ...initialCommand };
2756
- lazyCmd[lazyResolver] = (target) => {
2757
- const savedParent = target.parent;
2758
- const commandObj = builderFn(createPadroneBuilder(target))?.[commandSymbol] ?? target;
2759
- const mergedCommandObj = existingSubcommand ? mergeCommands(existingSubcommand, commandObj) : commandObj;
2760
- Object.assign(target, mergedCommandObj);
2761
- target.parent = savedParent;
2762
- };
2763
- const commands = existingCommand.commands || [];
2764
- const existingIndex = commands.findIndex((c) => c.name === name);
2765
- const updatedCommands = existingIndex >= 0 ? [
2766
- ...commands.slice(0, existingIndex),
2767
- lazyCmd,
2768
- ...commands.slice(existingIndex + 1)
2769
- ] : [...commands, lazyCmd];
2770
- return createPadroneBuilder({
2771
- ...existingCommand,
2772
- commands: updatedCommands
2773
- });
2774
- }
2775
- const commands = existingCommand.commands || [];
2776
- const existingIndex = commands.findIndex((c) => c.name === name);
2777
- const updatedCommands = existingIndex >= 0 ? [
2778
- ...commands.slice(0, existingIndex),
2779
- initialCommand,
2780
- ...commands.slice(existingIndex + 1)
2781
- ] : [...commands, initialCommand];
2782
- return createPadroneBuilder({
2783
- ...existingCommand,
2784
- commands: updatedCommands
2785
- });
2786
- },
2787
- mount(nameOrNames, program, options) {
2788
- const name = Array.isArray(nameOrNames) ? nameOrNames[0] : nameOrNames;
2789
- const aliases = Array.isArray(nameOrNames) && nameOrNames.length > 1 ? nameOrNames.slice(1) : void 0;
2790
- const programCommand = program[commandSymbol];
2791
- if (!programCommand) throw new RoutingError("Cannot mount: not a valid Padrone program");
2792
- const remounted = repathCommandTree(programCommand, name, existingCommand.path || "", existingCommand);
2793
- remounted.aliases = aliases;
2794
- if (options?.context) {
2795
- const existing = remounted.contextTransform;
2796
- remounted.contextTransform = existing ? (ctx) => existing(options.context(ctx)) : options.context;
2797
- }
2798
- const existingSubcommand = existingCommand.commands?.find((c) => c.name === name);
2799
- const mergedCommandObj = existingSubcommand ? mergeCommands(existingSubcommand, remounted) : remounted;
2800
- const commands = existingCommand.commands || [];
2801
- const existingIndex = commands.findIndex((c) => c.name === name);
2802
- const updatedCommands = existingIndex >= 0 ? [
2803
- ...commands.slice(0, existingIndex),
2804
- mergedCommandObj,
2805
- ...commands.slice(existingIndex + 1)
2806
- ] : [...commands, mergedCommandObj];
2807
- return createPadroneBuilder({
2808
- ...existingCommand,
2809
- commands: updatedCommands
2810
- });
2811
- },
2812
- intercept(metaOrFn, factory) {
2813
- const registered = toRegisteredInterceptor(metaOrFn, factory);
2814
- return createPadroneBuilder({
2815
- ...existingCommand,
2816
- interceptors: [...existingCommand.interceptors ?? [], registered]
2817
- });
2818
- },
2819
- ...createProgramMethods(execCtx, evalCommand),
2820
- get info() {
2821
- return {
2822
- name: existingCommand.name,
2823
- title: existingCommand.title,
2824
- description: existingCommand.description,
2825
- version: existingCommand.version,
2826
- examples: existingCommand.examples,
2827
- deprecated: existingCommand.deprecated,
2828
- commands: (existingCommand.commands ?? []).map((c) => c.name)
2829
- };
2830
- },
2831
- "~types": {},
2832
- [commandSymbol]: existingCommand
2833
- };
2834
- execCtx.builder = builder;
2835
- return builder;
2836
- }
2837
- function defineCommand(fn) {
2838
- if (fn) return fn;
2839
- const builder = {
2840
- requires: () => builder,
2841
- define: (f) => f
2842
- };
2843
- return builder;
2844
- }
2845
- //#endregion
2846
- //#region src/extension/completion.ts
2847
- /**
2848
- * Extension that adds the `completion` command for shell completion script generation.
2849
- *
2850
- * Usage:
2851
- * ```ts
2852
- * createPadrone('my-cli').extend(padroneCompletion())
2853
- * ```
2854
- */
2855
- function padroneCompletion() {
2856
- return ((builder) => builder.command("completion", (c) => c.configure({
2857
- description: "Generate shell completion scripts",
2858
- hidden: true
2859
- }).arguments(passthroughSchema({
2860
- shell: "string",
2861
- setup: "boolean"
2862
- }), { positional: ["shell"] }).async().action(async (args, ctx) => {
2863
- const rootCommand = getRootCommand(ctx.command);
2864
- resolveAllCommands(rootCommand);
2865
- const { detectShell, generateCompletionOutput, setupCompletions } = await import("./completion-D8qkAinX.mjs");
2866
- const shell = args.shell;
2867
- if (args.setup) {
2868
- const resolvedShell = shell ?? await detectShell();
2869
- if (!resolvedShell) throw new Error("Could not detect shell. Specify one: completion bash --setup");
2870
- const setupResult = await setupCompletions(rootCommand.name, resolvedShell);
2871
- return `${setupResult.updated ? "Updated" : "Added"} ${rootCommand.name} completions in ${setupResult.file}`;
2872
- }
2873
- return generateCompletionOutput(rootCommand, shell);
2874
- })));
2875
- }
2876
- //#endregion
2877
- //#region src/extension/config.ts
2878
- let _fs$1;
2879
- let _path$1;
2880
- async function initNodeModules$1() {
2881
- if (_fs$1 && _path$1) return;
2882
- _fs$1 = await import("node:fs");
2883
- _path$1 = await import("node:path");
2884
- }
2885
- try {
2886
- if (typeof process !== "undefined") initNodeModules$1();
2887
- } catch {}
2888
- function getUserConfigDir(path, appName) {
2889
- const platform = process.platform;
2890
- const xdgHome = process.env.XDG_CONFIG_HOME;
2891
- if (xdgHome) return path.join(xdgHome, appName);
2892
- const home = process.env.HOME || process.env.USERPROFILE;
2893
- if (!home) return void 0;
2894
- if (platform === "win32") {
2895
- const appData = process.env.APPDATA;
2896
- return appData ? path.join(appData, appName) : path.join(home, "AppData", "Roaming", appName);
2897
- }
2898
- if (platform === "darwin") return path.join(home, "Library", "Application Support", appName);
2899
- return path.join(home, ".config", appName);
2900
- }
2901
- function resolveConfigPath(fs, path, cwd, files, xdgAppName) {
2902
- if (typeof files === "string") {
2903
- const abs = path.isAbsolute(files) ? files : path.resolve(cwd, files);
2904
- if (!fs.existsSync(abs)) {
2905
- console.error(`Config file not found: ${abs}`);
2906
- return;
2907
- }
2908
- return abs;
2909
- }
2910
- for (const candidate of files) {
2911
- const abs = path.isAbsolute(candidate) ? candidate : path.resolve(cwd, candidate);
2912
- if (fs.existsSync(abs)) return abs;
2913
- }
2914
- if (xdgAppName) {
2915
- const configDir = getUserConfigDir(path, xdgAppName);
2916
- if (configDir) for (const candidate of files) {
2917
- const abs = path.join(configDir, candidate);
2918
- if (fs.existsSync(abs)) return abs;
2919
- }
2920
- }
2921
- }
2922
- function loadConfigSync(fs, path, files, xdgAppName) {
2923
- const absolutePath = resolveConfigPath(fs, path, process.cwd(), files, xdgAppName);
2924
- if (!absolutePath) return void 0;
2925
- const getContent = () => fs.readFileSync(absolutePath, "utf-8");
2926
- const ext = path.extname(absolutePath).toLowerCase();
2927
- if (ext === ".yaml" || ext === ".yml") return Bun.YAML.parse(getContent());
2928
- if (ext === ".toml") return Bun.TOML.parse(getContent());
2929
- if (ext === ".jsonc") return Bun.JSONC.parse(getContent());
2930
- if (ext === ".json") {
2931
- if (Bun.JSONC) return Bun.JSONC.parse(getContent());
2932
- try {
2933
- return JSON.parse(getContent());
2934
- } catch {
2935
- return Bun.JSONC.parse(getContent());
2936
- }
2937
- }
2938
- if (ext === ".js" || ext === ".cjs" || ext === ".mjs" || ext === ".ts" || ext === ".cts" || ext === ".mts") return import(
2939
- /* @vite-ignore */
2940
- absolutePath
2941
- ).then((mod) => mod.default ?? mod);
2942
- try {
2943
- return JSON.parse(getContent());
2944
- } catch {
2945
- console.error(`Unable to parse config file: ${absolutePath}`);
2946
- return;
2947
- }
2948
- }
2949
- /**
2950
- * Built-in config file loader. Directly accesses the file system.
2951
- * Returns `undefined` in non-CLI environments where `node:fs` is unavailable.
2952
- */
2953
- function loadConfig(files, xdgAppName) {
2954
- if (typeof process === "undefined") return void 0;
2955
- try {
2956
- if (_fs$1 && _path$1) return loadConfigSync(_fs$1, _path$1, files, xdgAppName);
2957
- return initNodeModules$1().then(() => loadConfigSync(_fs$1, _path$1, files, xdgAppName));
2958
- } catch {
2959
- return;
2960
- }
2961
- }
2962
- /**
2963
- * Extension that handles config file loading, validation, and merging into command arguments.
2964
- *
2965
- * Features:
2966
- * - `--config` / `-c` flag for explicit config file path (can be disabled via `flag: false`)
2967
- * - Auto-detection of config files from a list of candidate names
2968
- * - Optional schema validation and transformation of config data
2969
- * - Directly accesses the file system (gracefully no-ops in non-CLI environments)
2970
- *
2971
- * Config values have the lowest precedence (CLI > stdin > env > config).
2972
- *
2973
- * Not included in the default built-in extensions — must be explicitly added:
2974
- * ```ts
2975
- * createPadrone('my-cli')
2976
- * .extend(padroneConfig({
2977
- * files: ['config.json', '.myapprc'],
2978
- * schema: z.object({ port: z.number(), host: z.string() }),
2979
- * }))
2980
- * ```
2981
- */
2982
- function padroneConfig(options) {
2983
- if (options?.disabled) {
2984
- const disabled = defineInterceptor({
2985
- id: "padrone:config",
2986
- name: "padrone:config",
2987
- order: -999,
2988
- disabled: true
2989
- }, () => ({}));
2990
- return ((builder) => builder.intercept(disabled));
2991
- }
2992
- const configFiles = options?.files ? Array.isArray(options.files) ? options.files : [options.files] : void 0;
2993
- const configSchema = options?.schema;
2994
- const flagEnabled = options?.flag !== false;
2995
- const inherit = options?.inherit;
2996
- const xdgOption = options?.xdg;
2997
- const configLoader = options?.loadConfig ?? loadConfig;
2998
- const interceptor = defineInterceptor({
2999
- id: "padrone:config",
3000
- name: "padrone:config",
3001
- order: -999,
3002
- ...inherit === false && { inherit: false }
3003
- }, () => ({ validate(ctx, next) {
3004
- let explicitConfigPath;
3005
- if (flagEnabled) {
3006
- explicitConfigPath = ctx.rawArgs.config ?? ctx.rawArgs.c;
3007
- if (typeof explicitConfigPath === "string") {
3008
- delete ctx.rawArgs.config;
3009
- delete ctx.rawArgs.c;
3010
- }
3011
- }
3012
- if (!explicitConfigPath && !configFiles) return next();
3013
- let xdgAppName;
3014
- if (typeof xdgOption === "string") xdgAppName = xdgOption;
3015
- else if (xdgOption === true) xdgAppName = getRootCommand(ctx.command).name;
3016
- const configDataOrPromise = configLoader(explicitConfigPath ?? configFiles ?? [], xdgAppName);
3017
- const applyConfig = (configData) => {
3018
- if (!configData) return next();
3019
- if (configSchema) return thenMaybe(configSchema["~standard"].validate(configData), (result) => {
3020
- if (result.issues) throw new ConfigError(`Invalid config file:\n${result.issues.map((i) => ` - ${i.path?.join(".") || "root"}: ${i.message}`).join("\n")}`, { command: ctx.command.path || ctx.command.name });
3021
- const validatedData = result.value;
3022
- return next({ rawArgs: applyValues(ctx.rawArgs, validatedData) });
3023
- });
3024
- return next({ rawArgs: applyValues(ctx.rawArgs, configData) });
3025
- };
3026
- return thenMaybe(configDataOrPromise, applyConfig);
3027
- } }));
3028
- return ((builder) => builder.intercept(interceptor));
3029
- }
3030
- //#endregion
3031
- //#region src/util/dotenv.ts
3032
- /** Returns ordered list of `.env` file names to load (no fs access). */
3033
- function resolveEnvFiles(modes = [], local = true, base = true) {
3034
- const files = [];
3035
- if (base) {
3036
- files.push(".env");
3037
- if (local) files.push(".env.local");
3038
- }
3039
- for (const mode of modes) {
3040
- files.push(`.env.${mode}`);
3041
- if (local) files.push(`.env.${mode}.local`);
3042
- }
3043
- return files;
3044
- }
3045
- /** Parse a `.env` file string into key-value pairs. */
3046
- function parseEnvFile(content) {
3047
- const result = {};
3048
- const lines = content.split("\n");
3049
- let i = 0;
3050
- while (i < lines.length) {
3051
- const line = lines[i].trim();
3052
- i++;
3053
- if (!line || line.startsWith("#")) continue;
3054
- const stripped = line.startsWith("export ") ? line.slice(7) : line;
3055
- const eqIndex = stripped.indexOf("=");
3056
- if (eqIndex === -1) continue;
3057
- const key = stripped.slice(0, eqIndex).trim();
3058
- let raw = stripped.slice(eqIndex + 1);
3059
- const trimmedRaw = raw.trimStart();
3060
- const quote = trimmedRaw[0];
3061
- if (quote === "\"" || quote === "'" || quote === "`") {
3062
- let value = trimmedRaw.slice(1);
3063
- const closeIndex = findClosingQuote(value, quote);
3064
- if (closeIndex !== -1) value = value.slice(0, closeIndex);
3065
- else while (i < lines.length) {
3066
- const nextLine = lines[i];
3067
- i++;
3068
- const ci = findClosingQuote(nextLine, quote);
3069
- if (ci !== -1) {
3070
- value += `\n${nextLine.slice(0, ci)}`;
3071
- break;
3072
- }
3073
- value += `\n${nextLine}`;
3074
- }
3075
- if (quote === "\"") value = unescapeDoubleQuoted(value);
3076
- result[key] = value;
3077
- } else {
3078
- const commentIndex = raw.indexOf(" #");
3079
- if (commentIndex !== -1) raw = raw.slice(0, commentIndex);
3080
- result[key] = raw.trim();
3081
- }
3082
- }
3083
- return result;
3084
- }
3085
- function findClosingQuote(s, quote) {
3086
- let i = 0;
3087
- while (i < s.length) {
3088
- if (s[i] === "\\" && quote === "\"") {
3089
- i += 2;
3090
- continue;
3091
- }
3092
- if (s[i] === quote) return i;
3093
- i++;
3094
- }
3095
- return -1;
3096
- }
3097
- function unescapeDoubleQuoted(s) {
3098
- return s.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, "\"").replace(/\\\\/g, "\\");
3099
- }
3100
- /**
3101
- * Expand `$VAR`, `${VAR}`, `${VAR:-default}`, `${VAR-default}` in a string.
3102
- * Escaped `\$` produces a literal `$`. Undefined variables resolve to `""`.
3103
- */
3104
- function expandVariables(value, env) {
3105
- let result = "";
3106
- let i = 0;
3107
- while (i < value.length) {
3108
- if (value[i] === "\\" && value[i + 1] === "$") {
3109
- result += "$";
3110
- i += 2;
3111
- continue;
3112
- }
3113
- if (value[i] === "$") {
3114
- i++;
3115
- if (i >= value.length) {
3116
- result += "$";
3117
- break;
3118
- }
3119
- if (value[i] === "{") {
3120
- i++;
3121
- const closeIdx = value.indexOf("}", i);
3122
- if (closeIdx === -1) {
3123
- result += `\${${value.slice(i)}`;
3124
- break;
3125
- }
3126
- const expr = value.slice(i, closeIdx);
3127
- i = closeIdx + 1;
3128
- const colonDashIdx = expr.indexOf(":-");
3129
- const dashIdx = colonDashIdx === -1 ? expr.indexOf("-") : -1;
3130
- if (colonDashIdx !== -1) {
3131
- const varName = expr.slice(0, colonDashIdx);
3132
- const fallback = expr.slice(colonDashIdx + 2);
3133
- const val = env[varName];
3134
- result += val ? val : expandVariables(fallback, env);
3135
- } else if (dashIdx !== -1) {
3136
- const varName = expr.slice(0, dashIdx);
3137
- const fallback = expr.slice(dashIdx + 1);
3138
- const val = env[varName];
3139
- result += val !== void 0 ? val : expandVariables(fallback, env);
3140
- } else result += env[expr] ?? "";
3141
- } else {
3142
- let varName = "";
3143
- while (i < value.length && /[\w]/.test(value[i])) {
3144
- varName += value[i];
3145
- i++;
3146
- }
3147
- if (varName) result += env[varName] ?? "";
3148
- else result += "$";
3149
- }
3150
- continue;
3151
- }
3152
- result += value[i];
3153
- i++;
3154
- }
3155
- return result;
3156
- }
3157
- /**
3158
- * Load and merge `.env` files, returning the combined key-value map.
3159
- * Variable expansion uses the merged file values + process env as lookup.
3160
- *
3161
- * Returns synchronously when `node:fs`/`node:path` are already cached (typical),
3162
- * or a Promise on the very first call.
3163
- */
3164
- function loadEnvFiles(options, processEnv) {
3165
- if (typeof process === "undefined") return {};
3166
- try {
3167
- if (_fs && _path) return loadEnvFilesSync(_fs, _path, options, processEnv);
3168
- return initNodeModules().then(() => loadEnvFilesSync(_fs, _path, options, processEnv)).catch(() => ({}));
3169
- } catch {
3170
- return {};
3171
- }
3172
- }
3173
- let _fs;
3174
- let _path;
3175
- async function initNodeModules() {
3176
- if (_fs && _path) return;
3177
- _fs = await import("node:fs");
3178
- _path = await import("node:path");
3179
- }
3180
- if (typeof process !== "undefined") initNodeModules();
3181
- function loadEnvFilesSync(fs, path, options, processEnv) {
3182
- const dir = options.dir ?? process.cwd();
3183
- const fileNames = resolveEnvFiles(options.modes, options.local ?? true, options.base ?? true);
3184
- const merged = {};
3185
- for (const name of fileNames) {
3186
- const filePath = path.resolve(dir, name);
3187
- if (!fs.existsSync(filePath)) continue;
3188
- const parsed = parseEnvFile(fs.readFileSync(filePath, "utf-8"));
3189
- Object.assign(merged, parsed);
3190
- }
3191
- const lookup = options.override ? {
3192
- ...processEnv,
3193
- ...merged
3194
- } : {
3195
- ...merged,
3196
- ...processEnv
3197
- };
3198
- for (const key of Object.keys(merged)) merged[key] = expandVariables(merged[key], lookup);
3199
- return merged;
3200
- }
3201
- //#endregion
3202
- //#region src/extension/env.ts
3203
- function isSchema(value) {
3204
- return value != null && typeof value === "object" && "~standard" in value;
3205
- }
3206
- function padroneEnv(schemaOrOptions, maybeOptions) {
3207
- const schema = isSchema(schemaOrOptions) ? schemaOrOptions : void 0;
3208
- const options = isSchema(schemaOrOptions) ? maybeOptions : schemaOrOptions;
3209
- const hasFiles = options?.modes !== void 0;
3210
- const fileOptions = hasFiles ? options : void 0;
3211
- const override = options?.override ?? false;
3212
- const interceptor = defineInterceptor({
3213
- id: "padrone:env",
3214
- name: "padrone:env",
3215
- order: -1e3
3216
- }, () => ({ validate(ctx, next) {
3217
- const processEnv = ctx.runtime.env();
3218
- const applyEnv = (envFromFiles) => {
3219
- const rawEnv = override ? {
3220
- ...processEnv,
3221
- ...envFromFiles
3222
- } : {
3223
- ...envFromFiles,
3224
- ...processEnv
3225
- };
3226
- if (schema) return thenMaybe(schema["~standard"].validate(rawEnv), (result) => {
3227
- if (result.issues || !result.value) return next();
3228
- return next({ rawArgs: applyValues(ctx.rawArgs, result.value) });
3229
- });
3230
- if (Object.keys(envFromFiles).length > 0) return next({ rawArgs: applyValues(ctx.rawArgs, envFromFiles) });
3231
- return next();
3232
- };
3233
- if (hasFiles) return thenMaybe(loadEnvFiles(fileOptions, processEnv), applyEnv);
3234
- return applyEnv({});
3235
- } }));
3236
- return ((builder) => builder.intercept(interceptor));
3237
- }
3238
- //#endregion
3239
- //#region src/extension/ink.ts
3240
- const reactElement = Symbol.for("react.element");
3241
- const reactTransitional = Symbol.for("react.transitional.element");
3242
- /** Checks whether a value is a React element (JSX) by inspecting its `$$typeof` symbol. */
3243
- function isReactElement(value) {
3244
- if (value === null || typeof value !== "object") return false;
3245
- const tag = value.$$typeof;
3246
- return tag === reactElement || tag === reactTransitional;
3247
- }
3248
- const inkMeta = {
3249
- id: "padrone:ink",
3250
- name: "padrone:ink",
3251
- order: -1050
3252
- };
3253
- function createInkInterceptor(rawOptions) {
3254
- return defineInterceptor(inkMeta).requires().factory(() => ({ execute(ctx, next) {
3255
- const options = {
3256
- ...ctx.context?.inkConfig,
3257
- ...rawOptions
3258
- };
3259
- const { waitUntilExit = true } = options;
3260
- const handleResult = async (e) => {
3261
- let value = e.result;
3262
- if (value instanceof Promise) value = await value;
3263
- if (!isReactElement(value)) return e;
3264
- const { render } = await import("ink");
3265
- const instance = render(value, options.render);
3266
- const onAbort = () => instance.unmount();
3267
- ctx.signal.addEventListener("abort", onAbort, { once: true });
3268
- if (waitUntilExit) try {
3269
- await instance.waitUntilExit();
3270
- } finally {
3271
- ctx.signal.removeEventListener("abort", onAbort);
3272
- }
3273
- return { result: void 0 };
3274
- };
3275
- const executedOrPromise = next();
3276
- if (executedOrPromise instanceof Promise) return executedOrPromise.then(handleResult);
3277
- return handleResult(executedOrPromise);
3278
- } }));
3279
- }
3280
- /**
3281
- * Extension that renders React (Ink) components returned from command actions.
3282
- *
3283
- * When a command's action returns a React element (JSX), this extension
3284
- * renders it using Ink instead of passing it to the normal output path.
3285
- *
3286
- * Requires `ink` and `react` as peer dependencies.
3287
- *
3288
- * ```ts
3289
- * import { createPadrone, padroneInk } from 'padrone';
3290
- *
3291
- * const program = createPadrone('my-tui')
3292
- * .extend(padroneInk())
3293
- * .command('dashboard', (c) =>
3294
- * c.action(() => <Dashboard />)
3295
- * );
3296
- * ```
3297
- */
3298
- function padroneInk(options) {
3299
- const interceptor = createInkInterceptor(options);
3300
- return ((builder) => builder.intercept(interceptor));
3301
- }
3302
- //#endregion
3303
- //#region src/extension/logger.ts
3304
- const LEVEL_ORDER = {
3305
- trace: 0,
3306
- debug: 1,
3307
- info: 2,
3308
- warn: 3,
3309
- error: 4,
3310
- silent: 5
3311
- };
3312
- const LEVEL_LABELS = {
3313
- trace: "TRACE",
3314
- debug: "DEBUG",
3315
- info: "INFO",
3316
- warn: "WARN",
3317
- error: "ERROR"
3318
- };
3319
- const VALID_LEVELS = new Set(Object.keys(LEVEL_ORDER));
3320
- /** Format specifier pattern: matches %s, %d, %i, %f, %o, %O, %j, %% */
3321
- const FORMAT_PATTERN = /%%|%[sdifjoO]/g;
3322
- /**
3323
- * Applies printf-style format specifiers to args, following the WHATWG Console Standard
3324
- * and Node.js `util.format` conventions. Remaining args are appended space-separated.
3325
- */
3326
- function formatArgs(args) {
3327
- if (args.length === 0) return "";
3328
- if (typeof args[0] !== "string" || !FORMAT_PATTERN.test(args[0])) return args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
3329
- const template = args[0];
3330
- let argIndex = 1;
3331
- const result = template.replace(FORMAT_PATTERN, (token) => {
3332
- if (token === "%%") return "%";
3333
- if (argIndex >= args.length) return token;
3334
- const val = args[argIndex++];
3335
- switch (token) {
3336
- case "%s": return String(val);
3337
- case "%d":
3338
- case "%i": return String(Math.trunc(Number(val)));
3339
- case "%f": return String(Number(val));
3340
- case "%j": try {
3341
- return JSON.stringify(val);
3342
- } catch {
3343
- return "[Circular]";
3344
- }
3345
- case "%o":
3346
- case "%O": return typeof val === "string" ? val : JSON.stringify(val);
3347
- default: return token;
3348
- }
3349
- });
3350
- const remaining = args.slice(argIndex);
3351
- if (remaining.length === 0) return result;
3352
- return `${result} ${remaining.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}`;
3353
- }
3354
- function resolveCliLevel(rawArgs) {
3355
- if ("trace" in rawArgs) {
3356
- const t = rawArgs.trace;
3357
- delete rawArgs.trace;
3358
- if (t !== false) return "trace";
3359
- }
3360
- if ("verbose" in rawArgs) {
3361
- const v = rawArgs.verbose;
3362
- delete rawArgs.verbose;
3363
- if (v !== false) return "debug";
3364
- }
3365
- if ("debug" in rawArgs) {
3366
- const d = rawArgs.debug;
3367
- delete rawArgs.debug;
3368
- if (d !== false) return "debug";
3369
- }
3370
- if ("silent" in rawArgs) {
3371
- const s = rawArgs.silent;
3372
- delete rawArgs.silent;
3373
- if (s !== false) return "silent";
3374
- }
3375
- if ("quiet" in rawArgs) {
3376
- const q = rawArgs.quiet;
3377
- delete rawArgs.quiet;
3378
- if (q !== false) return "silent";
3379
- }
3380
- if ("log-level" in rawArgs) {
3381
- const val = rawArgs["log-level"];
3382
- delete rawArgs["log-level"];
3383
- if (typeof val === "string" && VALID_LEVELS.has(val)) return val;
3384
- }
3385
- }
3386
- function createLogger(runtime, level, config, tracing) {
3387
- const threshold = LEVEL_ORDER[level];
3388
- function format(lvl, prefix, args) {
3389
- const parts = [];
3390
- if (config.timestamps) parts.push((/* @__PURE__ */ new Date()).toISOString());
3391
- parts.push(`[${LEVEL_LABELS[lvl]}]`);
3392
- if (prefix) parts.push(prefix);
3393
- parts.push(formatArgs(args));
3394
- return parts.join(" ");
3395
- }
3396
- function makeLogger(prefix) {
3397
- const emit = (lvl, args) => {
3398
- if (LEVEL_ORDER[lvl] < threshold) return;
3399
- const message = format(lvl, prefix, args);
3400
- tracing?.rootSpan.addEvent("log", {
3401
- "log.level": lvl,
3402
- "log.message": args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")
3403
- });
3404
- if (lvl === "error" || lvl === "warn") runtime.error(message);
3405
- else runtime.output(message);
3406
- };
3407
- return {
3408
- trace: (...args) => emit("trace", args),
3409
- debug: (...args) => emit("debug", args),
3410
- info: (...args) => emit("info", args),
3411
- warn: (...args) => emit("warn", args),
3412
- error: (...args) => emit("error", args),
3413
- level,
3414
- child: (label) => makeLogger(prefix ? `${prefix} [${label}]` : `[${label}]`)
3415
- };
3416
- }
3417
- return makeLogger(config.prefix);
3418
- }
3419
- function loggerInterceptor(rawConfig) {
3420
- return defineInterceptor({
3421
- id: "padrone:logger",
3422
- name: "padrone:logger"
3423
- }).requires().factory(() => {
3424
- let cliLevel;
3425
- return {
3426
- parse(_ctx, next) {
3427
- return thenMaybe(next(), (res) => {
3428
- cliLevel = resolveCliLevel(res.rawArgs);
3429
- return res;
3430
- });
3431
- },
3432
- execute(ctx, next) {
3433
- const ctxCfg = ctx.context?.loggerConfig;
3434
- const resolved = {
3435
- level: cliLevel ?? rawConfig?.level ?? ctxCfg?.level ?? "info",
3436
- prefix: rawConfig?.prefix ?? "",
3437
- timestamps: rawConfig?.timestamps ?? ctxCfg?.timestamps ?? false
3438
- };
3439
- return next({ context: { logger: createLogger(ctx.runtime, resolved.level, resolved, ctx.context?.tracing) } });
3440
- }
3441
- };
3442
- });
3443
- }
3444
- /**
3445
- * Extension that injects a structured logger into the command context.
3446
- *
3447
- * The logger respects a configurable log level threshold, supports prefixed
3448
- * child loggers, and routes output through the runtime's `output`/`error`
3449
- * functions so it works in any environment (terminal, test, web).
3450
- *
3451
- * Supports CLI flags for runtime level overrides:
3452
- * - `--trace` → sets level to `trace`
3453
- * - `--verbose` or `--debug` → sets level to `debug`
3454
- * - `--silent` or `--quiet` → sets level to `silent`
3455
- * - `--log-level=<level>` → sets an explicit level (`trace`, `debug`, `info`, `warn`, `error`, `silent`)
3456
- *
3457
- * CLI flags take precedence over the programmatic config.
3458
- *
3459
- * Provides `{ logger: PadroneLogger }` on the command context.
3460
- * Access it in action handlers as `ctx.context.logger`.
3461
- *
3462
- * Usage:
3463
- * ```ts
3464
- * createPadrone('my-cli')
3465
- * .extend(padroneLogger({ level: 'info' }))
3466
- * .command('sync', (c) =>
3467
- * c.action((_args, ctx) => {
3468
- * ctx.context.logger.info('starting sync');
3469
- * const db = ctx.context.logger.child('db');
3470
- * db.debug('connecting...');
3471
- * })
3472
- * )
3473
- * ```
3474
- *
3475
- * Then run:
3476
- * ```sh
3477
- * my-cli sync --verbose # debug level
3478
- * my-cli sync --quiet # silent
3479
- * my-cli sync --log-level=warn
3480
- * ```
3481
- */
3482
- function padroneLogger(config) {
3483
- return ((builder) => builder.intercept(loggerInterceptor(config)));
3484
- }
3485
- //#endregion
3486
- //#region src/extension/man.ts
3487
- /**
3488
- * Extension that adds the `man` command for man page generation.
3489
- *
3490
- * Usage:
3491
- * ```ts
3492
- * createPadrone('my-cli').extend(padroneMan())
3493
- * ```
3494
- */
3495
- function padroneMan() {
3496
- return ((builder) => builder.command("man", (c) => c.configure({
3497
- description: "Generate man pages",
3498
- hidden: true
3499
- }).arguments(passthroughSchema({
3500
- setup: "boolean",
3501
- remove: "boolean"
3502
- })).async().action(async (args, ctx) => {
3503
- const rootCommand = getRootCommand(ctx.command);
3504
- resolveAllCommands(rootCommand);
3505
- const { setupManPages, removeManPages, generateDocs } = await import("./docs/index.mjs");
3506
- if (args.setup) {
3507
- const setupResult = await setupManPages(rootCommand);
3508
- return `${setupResult.updated ? "Updated" : "Installed"} ${setupResult.written.length} man page(s) in ${setupResult.dir}`;
3509
- }
3510
- if (args.remove) {
3511
- const removeResult = await removeManPages(rootCommand);
3512
- return removeResult.removed.length > 0 ? `Removed ${removeResult.removed.length} man page(s) from ${removeResult.dir}` : "No man pages found to remove.";
3513
- }
3514
- return generateDocs(rootCommand, { format: "man" }).pages[0]?.content ?? "";
3515
- })));
3516
- }
3517
- //#endregion
3518
- //#region src/extension/mcp.ts
3519
- /**
3520
- * Extension that adds the `mcp` command for starting a Model Context Protocol server.
3521
- *
3522
- * Usage:
3523
- * ```ts
3524
- * createPadrone('my-cli').extend(padroneMcp())
3525
- * ```
3526
- */
3527
- function padroneMcp(defaults) {
3528
- return ((builder) => builder.command("mcp", (c) => c.configure({
3529
- description: "Start a Model Context Protocol server",
3530
- hidden: true
3531
- }).arguments(passthroughSchema({
3532
- transport: "string",
3533
- port: "string",
3534
- host: "string",
3535
- "base-path": "string"
3536
- }), { positional: ["transport"] }).async().action(async (args, ctx) => {
3537
- const rootCommand = getRootCommand(ctx.command);
3538
- resolveAllCommands(rootCommand);
3539
- const { startMcpServer } = await import("./mcp-wCoVFTXz.mjs");
3540
- const transport = args.transport === "stdio" || args.transport === "http" ? args.transport : void 0;
3541
- const port = args.port ? parseInt(args.port, 10) : void 0;
3542
- const prefs = {
3543
- ...defaults,
3544
- transport: transport ?? defaults?.transport,
3545
- port: port && !Number.isNaN(port) ? port : defaults?.port,
3546
- host: args.host ?? defaults?.host,
3547
- basePath: args["base-path"] ?? defaults?.basePath
3548
- };
3549
- await startMcpServer(ctx.program, rootCommand, ctx.program.eval, prefs);
3550
- })));
3551
- }
3552
- //#endregion
3553
- //#region src/extension/progress-renderer.ts
3554
- const spinnerPresets = {
3555
- dots: [
3556
- "⠋",
3557
- "⠙",
3558
- "⠹",
3559
- "⠸",
3560
- "⠼",
3561
- "⠴",
3562
- "⠦",
3563
- "⠧",
3564
- "⠇",
3565
- "⠏"
3566
- ],
3567
- line: [
3568
- "-",
3569
- "\\",
3570
- "|",
3571
- "/"
3572
- ],
3573
- arc: [
3574
- "◜",
3575
- "◠",
3576
- "◝",
3577
- "◞",
3578
- "◡",
3579
- "◟"
3580
- ],
3581
- bounce: [
3582
- "⠁",
3583
- "⠂",
3584
- "⠄",
3585
- "⡀",
3586
- "⢀",
3587
- "⠠",
3588
- "⠐",
3589
- "⠈"
3590
- ]
3591
- };
3592
- function resolveSpinnerConfig(config) {
3593
- if (config === false) return {
3594
- frames: [],
3595
- interval: 80,
3596
- show: "never"
3597
- };
3598
- if (config === true) return {
3599
- frames: spinnerPresets.dots,
3600
- interval: 80,
3601
- show: "always"
3602
- };
3603
- if (typeof config === "string") return {
3604
- frames: spinnerPresets[config],
3605
- interval: 80,
3606
- show: "auto"
3607
- };
3608
- if (typeof config === "object") return {
3609
- frames: config.frames ?? spinnerPresets.dots,
3610
- interval: config.interval ?? 80,
3611
- show: config.show ?? "auto"
3612
- };
3613
- return {
3614
- frames: spinnerPresets.dots,
3615
- interval: 80,
3616
- show: "auto"
3617
- };
3618
- }
3619
- const defaultBarConfig = {
3620
- width: 20,
3621
- filled: "█",
3622
- empty: "░",
3623
- animation: "bounce",
3624
- show: "auto"
3625
- };
3626
- function resolveBarConfig(bar) {
3627
- if (bar === false) return void 0;
3628
- if (!bar) return { ...defaultBarConfig };
3629
- if (bar === true) return {
3630
- ...defaultBarConfig,
3631
- show: "always"
3632
- };
3633
- return {
3634
- width: bar.width ?? 20,
3635
- filled: bar.filled ?? "█",
3636
- empty: bar.empty ?? "░",
3637
- animation: bar.animation ?? "bounce",
3638
- show: bar.show ?? "always"
3639
- };
3640
- }
3641
- const SEGMENT_RATIO = .25;
3642
- const pulseGradient = [
3643
- "░",
3644
- "▒",
3645
- "▓",
3646
- "█",
3647
- "▓",
3648
- "▒",
3649
- "░"
3650
- ];
3651
- function formatIndeterminate(cfg, frame) {
3652
- const { width, filled, empty, animation } = cfg;
3653
- const pad = "".padStart(4);
3654
- if (animation === "pulse") return `${pad} ${pulseGradient[frame % pulseGradient.length].repeat(width)}`;
3655
- const seg = Math.max(2, Math.round(width * SEGMENT_RATIO));
3656
- const travel = width - seg;
3657
- if (animation === "slide") {
3658
- const offset = frame % (travel + 1);
3659
- return `${pad} ${empty.repeat(offset)}${filled.repeat(seg)}${empty.repeat(travel - offset)}`;
3660
- }
3661
- const cycle = travel * 2;
3662
- const pos = frame % cycle;
3663
- const offset = pos <= travel ? pos : cycle - pos;
3664
- return `${pad} ${empty.repeat(offset)}${filled.repeat(seg)}${empty.repeat(width - offset - seg)}`;
3665
- }
3666
- function formatBar(progress, cfg, frame) {
3667
- if (progress === void 0) return formatIndeterminate(cfg, frame);
3668
- const { width, filled, empty } = cfg;
3669
- const clamped = Math.max(0, Math.min(1, progress));
3670
- const filledCount = Math.round(clamped * width);
3671
- return `${`${Math.round(clamped * 100)}%`.padStart(4)} ${filled.repeat(filledCount)}${empty.repeat(width - filledCount)}`;
3672
- }
3673
- function parseUpdate(value) {
3674
- if (typeof value === "string") return { message: value };
3675
- if (typeof value === "number") return { progress: value };
3676
- return value;
3677
- }
3678
- function formatDuration$1(ms) {
3679
- const totalSeconds = Math.floor(ms / 1e3);
3680
- const hours = Math.floor(totalSeconds / 3600);
3681
- const minutes = Math.floor(totalSeconds % 3600 / 60);
3682
- const seconds = totalSeconds % 60;
3683
- if (hours > 0) return `${hours}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
3684
- return `${minutes}:${String(seconds).padStart(2, "0")}`;
3685
- }
3686
- function estimateEta(samples) {
3687
- if (samples.length < 2) return void 0;
3688
- const first = samples[0];
3689
- const last = samples[samples.length - 1];
3690
- const progressDelta = last.progress - first.progress;
3691
- if (progressDelta <= 0) return void 0;
3692
- const rate = progressDelta / (last.time - first.time);
3693
- const remaining = 1 - last.progress;
3694
- if (remaining <= 0) return 0;
3695
- return remaining / rate;
3696
- }
3697
- /**
3698
- * Creates a terminal progress indicator (spinner, bar, or both).
3699
- * Returns a no-op indicator in non-TTY/CI environments.
3700
- */
3701
- function createTerminalProgress(message, options) {
3702
- const spinnerCfg = resolveSpinnerConfig(options?.spinner);
3703
- const successIcon = options?.successIndicator ?? "✔";
3704
- const errorIcon = options?.errorIndicator ?? "✖";
3705
- const barCfg = resolveBarConfig(options?.bar);
3706
- const formatFinal = (icon, msg) => icon ? `${icon} ${msg}\n` : `${msg}\n`;
3707
- if (typeof process === "undefined" || !process.stderr?.isTTY) return {
3708
- update() {},
3709
- eta: {
3710
- start() {},
3711
- stop() {},
3712
- reset() {}
3713
- },
3714
- succeed(msg, opts) {
3715
- if (msg === null) return;
3716
- const icon = opts?.indicator ?? successIcon;
3717
- if (msg || message) process?.stderr?.write?.(formatFinal(icon, msg || message));
3718
- },
3719
- fail(msg, opts) {
3720
- if (msg === null) return;
3721
- const icon = opts?.indicator ?? errorIcon;
3722
- if (msg || message) process?.stderr?.write?.(formatFinal(icon, msg || message));
3723
- },
3724
- stop() {},
3725
- pause() {},
3726
- resume() {}
3727
- };
3728
- const ansiPattern = /\x1b\[[0-9;]*m/g;
3729
- if (spinnerCfg.show === "never" && (!barCfg || barCfg.show === "never") && !message) return {
3730
- update() {},
3731
- eta: {
3732
- start() {},
3733
- stop() {},
3734
- reset() {}
3735
- },
3736
- succeed() {},
3737
- fail() {},
3738
- stop() {},
3739
- pause() {},
3740
- resume() {}
3741
- };
3742
- const showTime = options?.time ?? false;
3743
- let etaEnabled = options?.eta ?? false;
3744
- let spinnerFrame = 0;
3745
- let barFrame = 0;
3746
- let text = message;
3747
- let progress;
3748
- let indeterminate = false;
3749
- let stopped = false;
3750
- let paused = false;
3751
- let timeEnabled = showTime;
3752
- let startTime = showTime ? Date.now() : 0;
3753
- const etaSamples = [];
3754
- let etaMs;
3755
- let etaCalculatedAt = 0;
3756
- const writeStderr = process.stderr.write.bind(process.stderr);
3757
- const writeStdout = process.stdout.write.bind(process.stdout);
3758
- let prevLineCount = 0;
3759
- const clearLines = () => {
3760
- if (prevLineCount > 1) for (let i = 1; i < prevLineCount; i++) writeStderr("\x1B[1A\x1B[2K");
3761
- writeStderr("\x1B[2K\r");
3762
- prevLineCount = 0;
3763
- };
3764
- /** Count how many terminal rows `str` occupies, accounting for line wrapping. */
3765
- const lineCount = (str) => {
3766
- const cols = process.stderr.columns || 80;
3767
- const visible = str.replace(ansiPattern, "");
3768
- return Math.max(1, Math.ceil(visible.length / cols));
3769
- };
3770
- const render = () => {
3771
- if (paused || stopped) return;
3772
- const barVisible = barCfg && (barCfg.show === "always" || barCfg.show === "auto" && (progress !== void 0 || indeterminate));
3773
- const spinnerVisible = spinnerCfg.show === "always" || spinnerCfg.show === "auto" && !barVisible;
3774
- let line = "";
3775
- if (barVisible) line += formatBar(progress, barCfg, barFrame);
3776
- const hasEta = etaEnabled && progress !== void 0 && progress < 1 && etaMs !== void 0;
3777
- if (timeEnabled || hasEta) {
3778
- const parts = [];
3779
- if (timeEnabled) parts.push(`⏱ ${formatDuration$1(Date.now() - startTime)}`);
3780
- if (hasEta) {
3781
- const elapsed = Date.now() - etaCalculatedAt;
3782
- parts.push(`ETA ${formatDuration$1(Math.max(0, etaMs - elapsed))}`);
3783
- }
3784
- if (line) line += " ";
3785
- line += parts.join(" | ");
3786
- }
3787
- if (spinnerVisible) {
3788
- if (line) line += " ";
3789
- line += frames[spinnerFrame] ?? "";
3790
- }
3791
- if (text) {
3792
- if (line) line += " ";
3793
- line += text;
3794
- }
3795
- if (line) {
3796
- clearLines();
3797
- writeStderr(line);
3798
- prevLineCount = lineCount(line);
3799
- } else clearLines();
3800
- };
3801
- const { frames } = spinnerCfg;
3802
- const needsAnimation = spinnerCfg.show !== "never" || barCfg && barCfg.show !== "never";
3803
- const tickInterval = barCfg && barCfg.show !== "never" ? Math.min(80, spinnerCfg.interval) : spinnerCfg.interval;
3804
- const timer = needsAnimation ? setInterval(() => {
3805
- spinnerFrame = (spinnerFrame + 1) % (frames.length || 1);
3806
- barFrame++;
3807
- render();
3808
- }, tickInterval) : void 0;
3809
- if (timer && typeof timer === "object" && "unref" in timer) timer.unref();
3810
- render();
3811
- const clear = () => {
3812
- if (stopped) return;
3813
- stopped = true;
3814
- paused = false;
3815
- if (timer) clearInterval(timer);
3816
- clearLines();
3817
- };
3818
- return {
3819
- update(value) {
3820
- if (stopped) return;
3821
- const parsed = parseUpdate(value);
3822
- if (parsed.message !== void 0) text = parsed.message;
3823
- if (parsed.progress !== void 0) {
3824
- progress = parsed.progress;
3825
- if (etaEnabled) {
3826
- const now = Date.now();
3827
- etaSamples.push({
3828
- time: now,
3829
- progress: parsed.progress
3830
- });
3831
- const estimated = estimateEta(etaSamples);
3832
- if (estimated !== void 0) {
3833
- etaMs = estimated;
3834
- etaCalculatedAt = now;
3835
- }
3836
- }
3837
- }
3838
- if (parsed.indeterminate !== void 0) {
3839
- indeterminate = parsed.indeterminate;
3840
- if (indeterminate) progress = void 0;
3841
- }
3842
- if (parsed.time !== void 0) {
3843
- if (parsed.time && !timeEnabled) {
3844
- timeEnabled = true;
3845
- startTime = Date.now();
3846
- } else if (!parsed.time) timeEnabled = false;
3847
- }
3848
- render();
3849
- },
3850
- eta: {
3851
- start() {
3852
- if (stopped) return;
3853
- etaEnabled = true;
3854
- render();
3855
- },
3856
- stop() {
3857
- if (stopped) return;
3858
- etaEnabled = false;
3859
- etaMs = void 0;
3860
- render();
3861
- },
3862
- reset() {
3863
- if (stopped) return;
3864
- etaSamples.length = 0;
3865
- etaMs = void 0;
3866
- etaCalculatedAt = 0;
3867
- render();
3868
- }
3869
- },
3870
- succeed(msg, opts) {
3871
- clear();
3872
- if (msg === null) return;
3873
- const finalMsg = msg ?? text;
3874
- const icon = opts?.indicator ?? successIcon;
3875
- if (finalMsg) writeStderr(formatFinal(icon, finalMsg));
3876
- },
3877
- fail(msg, opts) {
3878
- clear();
3879
- if (msg === null) return;
3880
- const finalMsg = msg ?? text;
3881
- const icon = opts?.indicator ?? errorIcon;
3882
- if (finalMsg) writeStderr(formatFinal(icon, finalMsg));
3883
- },
3884
- stop() {
3885
- clear();
3886
- },
3887
- pause() {
3888
- if (stopped || paused) return;
3889
- paused = true;
3890
- clearLines();
3891
- writeStdout("\x1B[2K\r");
3892
- },
3893
- resume() {
3894
- if (stopped || !paused) return;
3895
- paused = false;
3896
- render();
3897
- }
3898
- };
3899
- }
3900
- //#endregion
3901
- //#region src/extension/progress.ts
3902
- const noopIndicator = {
3903
- update() {},
3904
- eta: {
3905
- start() {},
3906
- stop() {},
3907
- reset() {}
3908
- },
3909
- succeed() {},
3910
- fail() {},
3911
- stop() {},
3912
- pause() {},
3913
- resume() {}
3914
- };
3915
- function resolveMessage(field, value, fallback) {
3916
- const raw = typeof field === "function" ? field(value) : field;
3917
- if (raw === void 0) return { message: fallback };
3918
- if (raw === null || typeof raw === "string") return { message: raw };
3919
- if (typeof raw === "object" && raw !== null) {
3920
- const obj = raw;
3921
- return {
3922
- message: obj.message,
3923
- indicator: obj.indicator
3924
- };
3925
- }
3926
- return { message: fallback };
3927
- }
3928
- function cleanup(indicator, successConfig, errorConfig, error, result, isError) {
3929
- if (isError) {
3930
- const { message: errorMsg, indicator: errorIcon } = resolveMessage(errorConfig, error, error instanceof Error ? error.message : String(error));
3931
- indicator.fail(errorMsg, errorIcon !== void 0 ? { indicator: errorIcon } : void 0);
3932
- } else {
3933
- const { message: successMsg, indicator: successIcon } = resolveMessage(successConfig, result);
3934
- indicator.succeed(successMsg, successIcon !== void 0 ? { indicator: successIcon } : void 0);
3935
- }
3936
- }
3937
- function resolveMessages(raw) {
3938
- if (!raw || typeof raw === "string") return {
3939
- progress: raw || "Working...",
3940
- validation: "",
3941
- success: void 0,
3942
- error: void 0
3943
- };
3944
- return {
3945
- progress: raw.progress ?? "Working...",
3946
- validation: raw.validation ?? "",
3947
- success: raw.success,
3948
- error: raw.error
3949
- };
3950
- }
3951
- function mergeMessages(cmd, ctx, cmdRaw) {
3952
- if (typeof cmdRaw === "string") return cmd;
3953
- const obj = typeof cmdRaw === "object" ? cmdRaw : void 0;
3954
- return {
3955
- progress: obj?.progress !== void 0 ? cmd.progress : ctx.progress ?? cmd.progress,
3956
- validation: obj?.validation !== void 0 ? cmd.validation : ctx.validation || cmd.validation,
3957
- success: obj?.success !== void 0 ? cmd.success : ctx.success ?? cmd.success,
3958
- error: obj?.error !== void 0 ? cmd.error : ctx.error ?? cmd.error
3959
- };
3960
- }
3961
- function progressInterceptor(config) {
3962
- const isObj = typeof config === "object";
3963
- const rawMessage = typeof config === "string" ? config : isObj ? config.message : void 0;
3964
- const rawSpinner = isObj ? config.spinner : void 0;
3965
- const rawBar = isObj ? config.bar : void 0;
3966
- const rawRenderer = isObj ? config.renderer : void 0;
3967
- const rawTime = isObj ? config.time : void 0;
3968
- const rawEta = isObj ? config.eta : void 0;
3969
- const rawSilent = isObj ? config.silent : void 0;
3970
- return defineInterceptor({
3971
- id: "padrone:progress",
3972
- name: "padrone:progress"
3973
- }).requires().factory(() => {
3974
- let indicator;
3975
- let restoreOutput;
3976
- let resolvedRenderer;
3977
- let resolvedOptions;
3978
- let resolvedSilent = false;
3979
- let msgs;
3980
- function resolve(ctx) {
3981
- if (resolvedRenderer) return;
3982
- const ctxCfg = ctx.context?.progressConfig;
3983
- resolvedSilent = rawSilent ?? ctxCfg?.silent ?? false;
3984
- const spinner = rawSpinner ?? ctxCfg?.spinner;
3985
- const bar = rawBar ?? ctxCfg?.bar;
3986
- const time = rawTime ?? ctxCfg?.time;
3987
- const eta = rawEta ?? ctxCfg?.eta;
3988
- resolvedRenderer = rawRenderer ?? ctxCfg?.renderer ?? createTerminalProgress;
3989
- resolvedOptions = spinner !== void 0 || bar !== void 0 || time !== void 0 || eta !== void 0 ? {
3990
- spinner,
3991
- bar,
3992
- time,
3993
- eta
3994
- } : void 0;
3995
- msgs = mergeMessages(resolveMessages(rawMessage), resolveMessages(ctxCfg?.message), rawMessage);
3996
- }
3997
- const teardown = () => {
3998
- restoreOutput?.();
3999
- indicator = void 0;
4000
- restoreOutput = void 0;
4001
- };
4002
- return {
4003
- validate(ctx, next) {
4004
- resolve(ctx);
4005
- if (resolvedSilent) return next();
4006
- indicator = resolvedRenderer(msgs.validation || msgs.progress, resolvedOptions);
4007
- const originalOutput = ctx.runtime.output;
4008
- const originalError = ctx.runtime.error;
4009
- ctx.runtime.output = (...args) => {
4010
- indicator.pause();
4011
- originalOutput(...args);
4012
- indicator.resume();
4013
- };
4014
- ctx.runtime.error = (text) => {
4015
- indicator.pause();
4016
- originalError(text);
4017
- indicator.resume();
4018
- };
4019
- restoreOutput = () => {
4020
- ctx.runtime.output = originalOutput;
4021
- ctx.runtime.error = originalError;
4022
- };
4023
- const onValidationFailure = (error) => {
4024
- if (indicator) {
4025
- cleanup(indicator, msgs.success, msgs.error, error, void 0, true);
4026
- teardown();
4027
- }
4028
- };
4029
- const checkResult = (result) => {
4030
- if (result.argsResult?.issues) onValidationFailure(/* @__PURE__ */ new Error("Validation failed"));
4031
- return result;
4032
- };
4033
- let result;
4034
- try {
4035
- result = next();
4036
- } catch (err) {
4037
- onValidationFailure(err);
4038
- throw err;
4039
- }
4040
- if (result instanceof Promise) return result.then(checkResult, (err) => {
4041
- onValidationFailure(err);
4042
- throw err;
4043
- });
4044
- return checkResult(result);
4045
- },
4046
- execute(_ctx, next) {
4047
- if (resolvedSilent) return next({ context: { progress: noopIndicator } });
4048
- if (indicator && msgs.validation) indicator.update(msgs.progress);
4049
- const effectiveIndicator = indicator ?? noopIndicator;
4050
- const onSuccess = (value) => {
4051
- cleanup(effectiveIndicator, msgs.success, msgs.error, void 0, value, false);
4052
- teardown();
4053
- };
4054
- const onError = (err) => {
4055
- if (indicator) {
4056
- cleanup(indicator, msgs.success, msgs.error, err, void 0, true);
4057
- teardown();
4058
- }
4059
- throw err;
4060
- };
4061
- let result;
4062
- try {
4063
- result = next({ context: { progress: effectiveIndicator } });
4064
- } catch (err) {
4065
- onError(err);
4066
- }
4067
- if (result instanceof Promise) return result.then((r) => {
4068
- if (r.result instanceof Promise) return { result: r.result.then((value) => {
4069
- onSuccess(value);
4070
- return value;
4071
- }, (err) => onError(err)) };
4072
- onSuccess(r.result);
4073
- return r;
4074
- }, (err) => onError(err));
4075
- if (result.result instanceof Promise) return { result: result.result.then((value) => {
4076
- onSuccess(value);
4077
- return value;
4078
- }, (err) => onError(err)) };
4079
- onSuccess(result.result);
4080
- return result;
4081
- },
4082
- shutdown(ctx) {
4083
- if (indicator) {
4084
- cleanup(indicator, msgs.success, msgs.error, ctx.error, ctx.result, !!ctx.error);
4085
- teardown();
4086
- }
4087
- }
4088
- };
4089
- }).provides();
4090
- }
4091
- /**
4092
- * Extension that adds an auto-managed progress indicator to the command pipeline.
4093
- *
4094
- * - `string` — a single message used for all states.
4095
- * - `PadroneProgressConfig` — separate messages for validation, progress, success, and error.
4096
- *
4097
- * The indicator is automatically started before validation, updated at each phase transition,
4098
- * and stopped on success (`.succeed()`) or failure (`.fail()`).
4099
- *
4100
- * Provides `{ progress: PadroneProgress }` on the command context.
4101
- * Access it in action handlers as `ctx.context.progress`.
4102
- *
4103
- * Uses the built-in terminal renderer by default. Pass a custom `renderer` for non-terminal
4104
- * environments (web UIs, testing, etc).
4105
- *
4106
- * Usage:
4107
- * ```ts
4108
- * createPadrone('my-cli')
4109
- * .command('sync', (c) =>
4110
- * c.extend(padroneProgress('Syncing...'))
4111
- * .action((_args, ctx) => {
4112
- * ctx.context.progress.update('halfway');
4113
- * })
4114
- * )
4115
- * ```
4116
- */
4117
- function padroneProgress(config) {
4118
- return ((builder) => builder.intercept(progressInterceptor(config ?? "Working...")));
4119
- }
4120
- //#endregion
4121
- //#region src/extension/serve.ts
4122
- /**
4123
- * Extension that adds the `serve` command for starting a REST HTTP server.
4124
- *
4125
- * Usage:
4126
- * ```ts
4127
- * createPadrone('my-cli').extend(padroneServe())
4128
- * ```
4129
- */
4130
- function padroneServe(defaults) {
4131
- return ((builder) => builder.command("serve", (c) => c.configure({
4132
- description: "Start a REST HTTP server",
4133
- hidden: true
4134
- }).arguments(passthroughSchema({
4135
- port: "string",
4136
- host: "string",
4137
- "base-path": "string"
4138
- })).async().action(async (args, ctx) => {
4139
- const rootCommand = getRootCommand(ctx.command);
4140
- resolveAllCommands(rootCommand);
4141
- const { startServeServer } = await import("./serve-ICZFl3xr.mjs");
4142
- const port = args.port ? parseInt(args.port, 10) : void 0;
4143
- const prefs = {
4144
- ...defaults,
4145
- port: port && !Number.isNaN(port) ? port : defaults?.port,
4146
- host: args.host ?? defaults?.host,
4147
- basePath: args["base-path"] ?? defaults?.basePath
4148
- };
4149
- await startServeServer(ctx.program, rootCommand, ctx.program.eval, prefs);
4150
- })));
4151
- }
4152
- //#endregion
4153
- //#region src/extension/timing.ts
4154
- function formatDuration(ms) {
4155
- if (ms < 1e3) return `${ms.toFixed(0)}ms`;
4156
- if (ms < 6e4) return `${(ms / 1e3).toFixed(2)}s`;
4157
- return `${Math.floor(ms / 6e4)}m ${(ms % 6e4 / 1e3).toFixed(2)}s`;
4158
- }
4159
- const timingMeta = {
4160
- id: "padrone:timing",
4161
- name: "padrone:timing",
4162
- order: -1002
4163
- };
4164
- function createTimingInterceptor(enabledByDefault) {
4165
- return defineInterceptor(timingMeta, () => {
4166
- let enabled = enabledByDefault;
4167
- let startTime = 0;
4168
- return {
4169
- parse(_ctx, next) {
4170
- return thenMaybe(next(), (res) => {
4171
- if ("timing" in res.rawArgs) {
4172
- enabled = res.rawArgs.timing !== false;
4173
- delete res.rawArgs.timing;
4174
- }
4175
- if ("time" in res.rawArgs) {
4176
- enabled = res.rawArgs.time !== false;
4177
- delete res.rawArgs.time;
4178
- }
4179
- return res;
4180
- });
4181
- },
4182
- start(_ctx, next) {
4183
- startTime = performance.now();
4184
- return next();
4185
- },
4186
- shutdown(ctx, next) {
4187
- return thenMaybe(next(), (res) => {
4188
- if (enabled) {
4189
- const elapsed = performance.now() - startTime;
4190
- ctx.runtime.error(`\nDone in ${formatDuration(elapsed)}`);
4191
- }
4192
- return res;
4193
- });
4194
- }
4195
- };
4196
- });
4197
- }
4198
- /**
4199
- * Extension that tracks command execution time.
4200
- *
4201
- * - `--time` / `--timing` → enables timing output
4202
- * - `--no-time` / `--no-timing` → disables timing output
4203
- *
4204
- * Pass `{ enabled: true }` to enable timing by default (can be disabled via `--no-time`).
4205
- *
4206
- * Usage:
4207
- * ```ts
4208
- * // Opt-in via flag
4209
- * createPadrone('my-cli').extend(padroneTiming())
4210
- *
4211
- * // Always on, opt-out via --no-time
4212
- * createPadrone('my-cli').extend(padroneTiming({ enabled: true }))
4213
- * ```
4214
- */
4215
- function padroneTiming(options) {
4216
- return ((builder) => builder.intercept(createTimingInterceptor(options?.enabled ?? false)));
4217
- }
4218
- //#endregion
4219
- //#region src/extension/tracing.ts
4220
- const OTEL_ERROR = 2;
4221
- function tracingInterceptor(config) {
4222
- return defineInterceptor({
4223
- id: "padrone:tracing",
4224
- name: "padrone:tracing",
4225
- order: -1
4226
- }, () => {
4227
- let rootSpan;
4228
- let tracer;
4229
- return {
4230
- start(ctx, next) {
4231
- tracer = config.provider.getTracer(config.serviceName ?? ctx.command.name);
4232
- return next();
4233
- },
4234
- execute(ctx, next) {
4235
- rootSpan = tracer.startSpan(`cli ${ctx.command.name}`);
4236
- return next({ context: { tracing: {
4237
- tracer,
4238
- rootSpan,
4239
- span(name, fn) {
4240
- const child = tracer.startSpan(name);
4241
- try {
4242
- const result = fn(child);
4243
- if (result != null && typeof result.then === "function") return result.then((v) => {
4244
- child.end();
4245
- return v;
4246
- }, (err) => {
4247
- child.recordException(err);
4248
- child.setStatus({ code: OTEL_ERROR });
4249
- child.end();
4250
- throw err;
4251
- });
4252
- child.end();
4253
- return result;
4254
- } catch (err) {
4255
- child.recordException(err);
4256
- child.setStatus({ code: OTEL_ERROR });
4257
- child.end();
4258
- throw err;
4259
- }
4260
- }
4261
- } } });
4262
- },
4263
- error(ctx, next) {
4264
- rootSpan?.recordException(ctx.error);
4265
- rootSpan?.setStatus({ code: OTEL_ERROR });
4266
- return next();
4267
- },
4268
- shutdown(_ctx, next) {
4269
- return thenMaybe(next(), (res) => {
4270
- rootSpan?.end();
4271
- return res;
4272
- });
4273
- }
4274
- };
4275
- });
4276
- }
4277
- /**
4278
- * Extension that adds OpenTelemetry tracing to command execution.
4279
- *
4280
- * Creates a root span for each command invocation and provides a `PadroneTracer`
4281
- * on the command context for creating child spans in action handlers.
4282
- *
4283
- * When used with `padroneLogger()`, the logger automatically emits span events
4284
- * for each log call — no extra configuration needed. The logger detects the
4285
- * tracing context and bridges log output to span events.
4286
- *
4287
- * Uses minimal OTEL-compatible interfaces — pass any `TracerProvider` that
4288
- * implements `getTracer()`. Works with `@opentelemetry/api` or compatible
4289
- * libraries.
4290
- *
4291
- * Provides `{ tracing: PadroneTracer }` on the command context.
4292
- * Access it in action handlers as `ctx.context.tracing`.
4293
- *
4294
- * Usage:
4295
- * ```ts
4296
- * import { trace } from '@opentelemetry/api';
4297
- *
4298
- * createPadrone('my-cli')
4299
- * .extend(padroneTracing({ provider: trace.getTracerProvider() }))
4300
- * .extend(padroneLogger())
4301
- * .command('deploy', (c) =>
4302
- * c.action((_args, ctx) => {
4303
- * ctx.context.logger.info('deploying'); // also emits a span event
4304
- * ctx.context.tracing.span('build', (span) => {
4305
- * span.setAttribute('target', 'production');
4306
- * });
4307
- * })
4308
- * )
4309
- * ```
4310
- */
4311
- function padroneTracing(config) {
4312
- const resolved = {
4313
- provider: config.provider,
4314
- serviceName: config.serviceName
4315
- };
4316
- return ((builder) => builder.intercept(tracingInterceptor(resolved)));
4317
- }
4318
- //#endregion
4319
- //#region src/extension/update-check.ts
4320
- function createUpdateCheckInterceptor(config) {
4321
- return defineInterceptor({
4322
- id: "padrone:update-check",
4323
- name: "padrone:update-check",
4324
- order: 1e3
4325
- }, () => {
4326
- let checkPromise;
4327
- let suppressed = false;
4328
- return {
4329
- start(ctx, next) {
4330
- const rootCommand = ctx.command;
4331
- const runtime = ctx.runtime;
4332
- checkPromise = Promise.resolve(getVersion(rootCommand.version)).then((currentVersion) => import("./update-check-CZ2VqjnV.mjs").then(({ createUpdateChecker }) => createUpdateChecker(rootCommand.name, currentVersion, config, runtime)));
4333
- return next();
4334
- },
4335
- parse(_ctx, next) {
4336
- return thenMaybe(next(), (res) => {
4337
- if ("update-check" in res.rawArgs) {
4338
- if (res.rawArgs["update-check"] === false) suppressed = true;
4339
- delete res.rawArgs["update-check"];
4340
- }
4341
- return res;
4342
- });
4343
- },
4344
- shutdown(_ctx, next) {
4345
- const result = next();
4346
- if (suppressed || !checkPromise) return result;
4347
- let resolved = null;
4348
- checkPromise.then((fn) => {
4349
- resolved = fn;
4350
- }, () => {
4351
- resolved = void 0;
4352
- });
4353
- if (resolved !== null) resolved?.();
4354
- return result;
4355
- }
4356
- };
4357
- });
4358
- }
4359
- /**
4360
- * Extension that adds background update checking:
4361
- * - Checks for newer versions on npm (or custom registry) in the background
4362
- * - Shows an update notification after command execution
4363
- * - Respects `--no-update-check` flag to suppress
4364
- *
4365
- * Usage:
4366
- * ```ts
4367
- * createPadrone('my-cli')
4368
- * .extend(padroneUpdateCheck({ packageName: 'my-cli' }))
4369
- * ```
4370
- */
4371
- function padroneUpdateCheck(config = {}) {
4372
- return ((builder) => builder.intercept(createUpdateCheckInterceptor(config)));
4373
- }
4374
- //#endregion
4375
- export { ActionError, ConfigError, PadroneError, REPL_SIGINT, RoutingError, SignalError, ValidationError, asyncSchema, asyncStream, buildReplCompleter, colorThemes, createPadrone, createTerminalProgress, defineCommand, defineInterceptor, isReactElement, padroneAutoOutput, padroneColor, padroneCompletion, padroneConfig, padroneEnv, padroneHelp, padroneInk, padroneInteractive, padroneLogger, padroneMan, padroneMcp, padroneProgress, padroneRepl, padroneServe, padroneSignalHandling, padroneStdin, padroneSuggestions, padroneTiming, padroneTracing, padroneUpdateCheck, padroneVersion };
4376
-
4377
- //# sourceMappingURL=index.mjs.map
1
+ import { asyncStream } from "./util/stream.mjs";
2
+ import { REPL_SIGINT } from "./core/runtime.mjs";
3
+ import { buildReplCompleter } from "./core/commands.mjs";
4
+ import { asyncSchema } from "./core/results.mjs";
5
+ import { defineInterceptor } from "./core/interceptors.mjs";
6
+ import { colorThemes } from "./output/colorizer.mjs";
7
+ import { padroneAutoOutput } from "./extension/auto-output.mjs";
8
+ import { padroneColor } from "./extension/color.mjs";
9
+ import { ActionError, ConfigError, PadroneError, RoutingError, SignalError, ValidationError } from "./core/errors.mjs";
10
+ import { padroneHelp } from "./extension/help.mjs";
11
+ import { padroneInteractive } from "./extension/interactive.mjs";
12
+ import { padroneRepl } from "./extension/repl.mjs";
13
+ import { padroneSignalHandling } from "./extension/signal.mjs";
14
+ import { padroneStdin } from "./extension/stdin.mjs";
15
+ import { padroneSuggestions } from "./extension/suggestions.mjs";
16
+ import { padroneVersion } from "./extension/version.mjs";
17
+ import { createPadrone, defineCommand } from "./core/create.mjs";
18
+ import { padroneConfig } from "./extension/config.mjs";
19
+ import { padroneEnv } from "./extension/env.mjs";
20
+ import { padroneLogger } from "./extension/logger.mjs";
21
+ import { createTerminalProgress } from "./extension/progress-renderer.mjs";
22
+ import { padroneProgress } from "./extension/progress.mjs";
23
+ import { padroneTiming } from "./extension/timing.mjs";
24
+ import { padroneUpdateCheck } from "./extension/update-check.mjs";
25
+ export { ActionError, ConfigError, PadroneError, REPL_SIGINT, RoutingError, SignalError, ValidationError, asyncSchema, asyncStream, buildReplCompleter, colorThemes, createPadrone, createTerminalProgress, defineCommand, defineInterceptor, padroneAutoOutput, padroneColor, padroneConfig, padroneEnv, padroneHelp, padroneInteractive, padroneLogger, padroneProgress, padroneRepl, padroneSignalHandling, padroneStdin, padroneSuggestions, padroneTiming, padroneUpdateCheck, padroneVersion };