@ts-org/jenkins-cli 3.1.0 → 3.1.2

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 (3) hide show
  1. package/README.md +135 -130
  2. package/dist/cli.js +24 -22
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,67 +1,64 @@
1
1
  ## Jenkins CLI
2
2
 
3
- 一个轻量的 Jenkins 部署工具,让你在命令行中通过交互式或非交互式的方式,轻松触发 Jenkins 参数化构建。
3
+ A lightweight Jenkins deployment tool that helps you trigger parameterized Jenkins builds from the command line, either interactively or non-interactively.
4
4
 
5
- ### ✨ 特性
5
+ ### ✨ Features
6
6
 
7
- - 交互式 CLI,引导你选择分支和部署环境。
8
- - 智能触发,自动停止进行中的构建,并复用队列中相同的任务。
9
- - 灵活的参数化构建,支持在运行时传入额外参数。
10
- - 支持全局 `--debug` 打印 Jenkins 请求信息。
11
- - 丰富的命令集,覆盖 Job、BuildsQueue 等常用操作。
12
- - 支持 `jenkins-cli.yaml` `package.json` 进行项目级配置。
7
+ - Interactive CLI flow for selecting branches and deployment environments.
8
+ - Smart triggering: automatically stops running builds and reuses identical queued tasks.
9
+ - Flexible parameterized builds with support for extra runtime parameters.
10
+ - Global `--debug` flag to print Jenkins request details.
11
+ - Rich command set covering common operations for Jobs, Builds, Queue, and more.
12
+ - Project-level config via `jenkins-cli.yaml` or `package.json`.
13
13
 
14
- ### 📦 安装
14
+ ### 📦 Installation
15
15
 
16
16
  ```bash
17
- # Node.js >= 16
18
- npm install -g @ts-org/jenkins-cli
19
-
20
- # Node.js >= 18,推荐使用pnpm
21
17
  pnpm install -g @ts-org/jenkins-cli
22
18
  ```
23
19
 
24
- ### 🚀 使用
20
+ ### 🚀 Usage
25
21
 
26
- 在你的项目根目录下执行:
22
+ Run from your project root:
27
23
 
28
24
  ```bash
29
25
  jenkins-cli
30
26
  ```
31
27
 
32
- CLI 会自动加载配置,列出 Git 分支和部署环境供你选择。
28
+ The CLI will auto-load your config, then list Git branches and deployment environments for you to choose from.
33
29
 
34
30
  ![Demo](https://cdn.jsdelivr.net/npm/@ts-org/jenkins-cli@latest/docs/images/demo.png)
35
31
 
36
- ### ⚙️ 配置
32
+ ### ⚙️ Configuration
37
33
 
38
- 在项目根目录创建 `jenkins-cli.yaml`:
34
+ Create `jenkins-cli.yaml` in your project root:
39
35
 
40
36
  ```yaml
41
37
  # yaml-language-server: $schema=https://cdn.jsdelivr.net/npm/@ts-org/jenkins-cli@latest/schemas/jenkins.json
42
38
 
43
- # Jenkins API 地址,格式: http(s)://username:token@host:port
39
+ # Jenkins API URL format: http(s)://username:token@host:port
44
40
  apiToken: http://user:token@jenkins.example.com
45
41
 
46
- # Jenkins Job 名称 (可被 -j 参数覆盖,也可用函数引用)
42
+ # Jenkins job name (can be overridden with -j, or provided via function reference)
47
43
  job: your-project-job
48
44
 
49
- # 部署环境列表 (将作为参数 `mode` 传入 Jenkins,也可用函数引用)
50
- # 选项要与Jenkins配置的对上
51
- # 这里配置了Jenkins上没有的,比如prod,那么在提交mode = prod时会报500
45
+ # Deployment environments (passed to Jenkins as the `mode` parameter, or provided via function reference)
46
+ # The options must match your Jenkins configuration.
47
+ # If you configure a mode that does not exist in Jenkins (for example `prod`),
48
+ # Jenkins may return HTTP 500 when `mode=prod` is submitted.
52
49
  modes:
53
50
  - dev
54
51
  - sit
55
52
  - uat
56
53
  ```
57
54
 
58
- 建议在 `jenkins-cli.yaml` 顶部添加 `# yaml-language-server: $schema=https://cdn.jsdelivr.net/npm/@ts-org/jenkins-cli@latest/schemas/jenkins.json`,以有更好的开发体验,如示例。
55
+ It is recommended to add `# yaml-language-server: $schema=https://cdn.jsdelivr.net/npm/@ts-org/jenkins-cli@latest/schemas/jenkins.json` at the top of `jenkins-cli.yaml` for a better editing experience, as shown above.
59
56
 
60
- #### 动态 job / modes
57
+ #### Dynamic `job` / `modes`
61
58
 
62
- `job` `modes` 支持函数引用(`path:exportName`),函数会收到 `answers` 参数,返回最终值。
59
+ `job` and `modes` support function references in the form `path:exportName`. The function receives `answers` and returns the final value.
63
60
 
64
- 示例:
61
+ Example:
65
62
 
66
63
  ```yaml
67
64
  job: jenkins-utils.ts:dynamicJob
@@ -74,53 +71,51 @@ export function dynamicJob(answers: Record<string, unknown>) {
74
71
  }
75
72
 
76
73
  export function dynamicModes(answers: Record<string, unknown>) {
77
- return Number(answers.buildNumber) > 100
78
- ? ['dev', 'sit', 'uat', 'prod']
79
- : ['dev', 'sit', 'uat']
74
+ return Number(answers.buildNumber) > 100 ? ['dev', 'sit', 'uat', 'prod'] : ['dev', 'sit', 'uat']
80
75
  }
81
76
  ```
82
77
 
83
- #### 扩展交互参数
78
+ #### Extended Interactive Parameters
84
79
 
85
- 通过 `configs` 字段,你可以添加自定义的交互式提问,其结果将作为参数传递给 Jenkins
80
+ With the `configs` field, you can add custom interactive prompts. Their results are passed to Jenkins as build parameters.
86
81
 
87
- - `type`: 支持 `input`, `number`, `confirm`, `list`, `rawlist`, `checkbox`, `password`等类型。
88
- - `choices`, `default`, `validate`, `when`, `filter`, `transformer`: 支持从本地 TS/JS 文件动态加载。
89
- - `path:exportName`:自动判断并使用导出值;如果是函数则调用 (支持 async)
90
- - `offset`: 负数可调整提问顺序
91
- - `-1`:插到 branch 后、modes
92
- - `<= -2`:插到 branch
93
- - `name`: 参数名,注意不要使用 `branch`, `mode`, `modes` 等保留字。
82
+ - `type`: supports `input`, `number`, `confirm`, `list`, `rawlist`, `checkbox`, `password`, and more.
83
+ - `choices`, `default`, `validate`, `when`, `filter`, `transformer`: can be dynamically loaded from local TS/JS files.
84
+ - `path:exportName`: auto-resolves exported values; calls them if they are functions (async supported).
85
+ - `offset`: negative values adjust prompt order.
86
+ - `-1`: inserted after `branch`, before `modes`
87
+ - `<= -2`: inserted before `branch`
88
+ - `name`: parameter key. Avoid reserved names such as `branch`, `mode`, `modes`.
94
89
 
95
- **示例:**
90
+ **Example:**
96
91
 
97
92
  ```yaml
98
93
  configs:
99
94
  - type: number
100
95
  name: buildNumber
101
- message: '请选择构建号:'
96
+ message: 'Select build number:'
102
97
  offset: -2
103
98
  - type: input
104
99
  name: version
105
- message: '请输入版本号:'
100
+ message: 'Enter version:'
106
101
  default: '1.0.0'
107
102
  filter: src/config.ts:filterVersion
108
103
  transformer: src/config.ts:transformVersion
109
104
  - type: list
110
105
  name: service
111
- message: '请选择服务:'
106
+ message: 'Select service:'
112
107
  choices: src/config.ts:getServices
113
108
  - type: confirm
114
109
  name: smokeTest
115
- message: '是否开启冒烟测试?'
110
+ message: 'Enable smoke test?'
116
111
  when: src/config.ts:whenSmokeTest
117
112
  - type: input
118
113
  name: ticket
119
- message: '请输入关联的工单号:'
114
+ message: 'Enter ticket ID:'
120
115
  validate: src/config.ts:validateTicket
121
116
  ```
122
117
 
123
- 对应的 `src/config.ts`:
118
+ Corresponding `src/config.ts`:
124
119
 
125
120
  ```ts
126
121
  export async function getServices() {
@@ -145,233 +140,243 @@ export function whenSmokeTest(answers: { mode?: string }) {
145
140
  }
146
141
 
147
142
  export function validateTicket(input: unknown, answers: Record<string, unknown>) {
148
- return /^(feat|fix|refactor)-[a-zA-Z0-9]+$/.test(input) || '工单号格式不正确'
143
+ return /^(feat|fix|refactor)-[a-zA-Z0-9]+$/.test(input) || 'Invalid ticket format'
149
144
  }
150
145
  ```
151
146
 
152
- **四个函数的完整入参说明**:
147
+ **Complete parameter details for the four functions:**
153
148
 
154
- - `when(answers)`
155
- - `answers`: 当前已回答的所有答案对象(键为问题的 `name`)。
156
- - 返回 `boolean | Promise<boolean>`,用于决定是否显示该问题。
149
+ - `when(answers)`
150
+ - `answers`: all answers collected so far (keys are prompt `name` values).
151
+ - Returns `boolean | Promise<boolean>` to decide whether to show the prompt.
157
152
 
158
- - `validate(input, answers)`
159
- - `input`: 用户当前输入的原始值。
160
- - `answers`: 当前已回答的所有答案对象。
161
- - 返回 `true` 表示通过;返回 `string` 作为错误提示;也可返回 `Promise<true | string>`。
153
+ - `validate(input, answers)`
154
+ - `input`: raw value currently entered by the user.
155
+ - `answers`: all answers collected so far.
156
+ - Return `true` to pass; return a `string` as an error message; `Promise<true | string>` is also supported.
162
157
 
163
- - `filter(input, answers)`
164
- - `input`: 用户当前输入的原始值。
165
- - `answers`: 当前已回答的所有答案对象。
166
- - 返回转换后的值(可同步或返回 Promise)。
158
+ - `filter(input, answers)`
159
+ - `input`: raw value currently entered by the user.
160
+ - `answers`: all answers collected so far.
161
+ - Returns the transformed value (sync or async).
167
162
 
168
- - `transformer(input, answers, meta)`
169
- - `input`: 用户当前输入的原始值。
170
- - `answers`: 当前已回答的所有答案对象。
171
- - `meta`: 渲染相关的元信息(例如 `isFinal`),不同版本可能略有差异。
172
- - 返回显示用的字符串,不会修改answers的值(可同步或返回 Promise)。
163
+ - `transformer(input, answers, meta)`
164
+ - `input`: raw value currently entered by the user.
165
+ - `answers`: all answers collected so far.
166
+ - `meta`: rendering metadata (for example `isFinal`), which may vary slightly across versions.
167
+ - Returns display text only and does not modify the value in `answers` (sync or async).
173
168
 
174
- > **提示**: 配置也支持写在 `package.json` `jenkins-cli` 字段里。
169
+ > **Tip**: You can also put configuration under the `jenkins-cli` field in `package.json`.
175
170
  >
176
- > **查找顺序**: `jenkins-cli.yaml` > `package.json` > 祖先目录的 `jenkins-cli.yaml`。
171
+ > **Lookup order**: `jenkins-cli.yaml` > `package.json` > ancestor directory `jenkins-cli.yaml`.
177
172
 
178
- ### 🤖 命令参考
173
+ ### 🤖 Command Reference
179
174
 
180
- #### `build` (非交互式触发)
175
+ #### `build` (non-interactive trigger)
181
176
 
182
- 适用于 CI 或其他脚本化场景。
177
+ Useful for CI or other scripted scenarios.
183
178
 
184
179
  ```bash
185
- # 触发 dev 环境构建
180
+ # Trigger a build for the dev environment
186
181
  jenkins-cli build -b origin/develop -m dev
187
182
 
188
- # 同时触发多个环境
183
+ # Trigger multiple environments at once
189
184
  jenkins-cli build -b origin/develop -m dev,sit
190
185
 
191
- # 传入额外参数
186
+ # Pass extra parameters
192
187
  jenkins-cli build -b origin/develop -m dev --param version=1.2.3 --param force=true
193
188
  ```
194
189
 
195
- 说明:默认会读取 `git config user.name` 并自动追加参数 `triggered_by`。
196
- 如需覆盖,可显式传入 `--param triggered_by=YourName`。
190
+ Note: by default, the CLI reads `git config user.name` and appends it as `triggered_by`.
191
+ To override it, pass `--param triggered_by=YourName` explicitly.
197
192
 
198
193
  ---
199
194
 
200
- #### `builds` - 构建管理
195
+ #### `builds` - build management
201
196
 
202
197
  ```bash
203
- # 查看最近 20 次构建
198
+ # Show the latest 20 builds
204
199
  jenkins-cli builds
205
200
 
206
- # 查看运行中的构建
201
+ # Show running builds
207
202
  jenkins-cli builds active
208
203
 
209
- # 查看 Jenkins 上所有运行中的构建
204
+ # Show all running builds on Jenkins
210
205
  jenkins-cli builds active -a
211
206
 
212
207
  ```
213
208
 
214
- #### `changes` - 变更记录
209
+ #### `changes` - change logs
215
210
 
216
211
  ```bash
217
- # 查看最近 20 次构建的变更记录
212
+ # Show change logs for the latest 20 builds
218
213
  jenkins-cli changes
219
214
 
220
- # 查看最近 50 次构建的变更记录
215
+ # Show change logs for the latest 50 builds
221
216
  jenkins-cli changes -n 50
222
217
 
223
- # 查看指定构建的变更记录
218
+ # Show change logs for a specific build
224
219
  jenkins-cli changes 1234
225
220
  ```
226
221
 
227
- #### `config` - Job 配置
222
+ #### `config` - job configuration
228
223
 
229
224
  ```bash
230
- # 读取当前 Job XML 配置
225
+ # Read current job XML config
231
226
  jenkins-cli config show
232
227
 
233
- # JSON 格式输出
228
+ # Output as JSON
234
229
  jenkins-cli config show -f json
235
230
 
236
- # 备份当前 Job 配置
231
+ # Back up current job config
237
232
  jenkins-cli config backup -d ./jenkins-backup
238
233
 
239
- # 备份指定 Job 配置
234
+ # Back up a specific job config
240
235
  jenkins-cli config backup -d ./jenkins-backup -j remote-factory-admin
241
236
 
242
- # glob 过滤备份多个 Job 配置
237
+ # Back up multiple job configs filtered by glob
243
238
  jenkins-cli config backup -d ./jenkins-backup --filter 'remote-*'
244
239
 
245
- # 备份所有 Job 配置
240
+ # Back up all job configs
246
241
  jenkins-cli config backup -d ./jenkins-backup -a
247
242
  ```
248
243
 
249
- #### `console` - 查看日志
244
+ #### `plugin` - plugin management
245
+
246
+ ```bash
247
+ # Show enabled plugins
248
+ jenkins-cli plugin show
249
+
250
+ # Back up enabled plugin list
251
+ jenkins-cli plugin backup -d ./jenkins-backup
252
+ ```
253
+
254
+ #### `console` - view logs
250
255
 
251
- 支持实时日志追踪,默认开启。
256
+ Supports live log following, enabled by default.
252
257
 
253
258
  ```bash
254
- # 查看指定构建号的日志,如果正在构建则追踪,否则直接输出日志
259
+ # Show logs for a specific build number; follow if running, otherwise print once
255
260
  jenkins-cli console 1234
256
261
 
257
- # 查看最后一次构建日志,如果正在构建则追踪,否则直接输出日志
262
+ # Show logs for the latest build; follow if running, otherwise print once
258
263
  jenkins-cli console last
259
264
 
260
- # 仅输出当前日志内容,不追踪
265
+ # Print current logs only, without following
261
266
  jenkins-cli console last --no-follow
262
267
 
263
- # 查看最后一次构建成功日志
268
+ # Show logs from the latest successful build
264
269
  jenkins-cli console last -s success
265
270
 
266
- # 查看最后一次构建失败日志
271
+ # Show logs from the latest failed build
267
272
  jenkins-cli console last -s failed
268
273
  ```
269
274
 
270
- #### `job` - Job 管理
275
+ #### `job` - job management
271
276
 
272
277
  ```bash
273
- # 列出所有 Job (支持 glob 过滤)
278
+ # List all jobs (supports glob filtering)
274
279
  jenkins-cli job list --search 'project-*'
275
280
 
276
- # 查看当前 Job 信息
281
+ # Show current job info
277
282
  jenkins-cli job info
278
283
  ```
279
284
 
280
- #### `me` - 用户信息
285
+ #### `me` - user information
281
286
 
282
287
  ```bash
283
- # 验证 API Token 并查看当前用户
288
+ # Verify API token and show current user
284
289
  jenkins-cli me
285
290
  ```
286
291
 
287
- #### `params` - Job 参数
292
+ #### `params` - job parameters
288
293
 
289
294
  ```bash
290
- # 查看当前 Job 的参数定义
295
+ # Show parameter definitions for the current job
291
296
  jenkins-cli params
292
297
  ```
293
298
 
294
- #### `queue` - 等待构建队列管理
299
+ #### `queue` - pending build queue management
295
300
 
296
301
  ```bash
297
- # 查看等待构建的队列
302
+ # Show pending build queue
298
303
  jenkins-cli queue list
299
304
 
300
- # 取消一个等待构建队列项
305
+ # Cancel one queued item
301
306
  jenkins-cli queue cancel 5678
302
307
  ```
303
308
 
304
- #### `stop` - 停止构建
309
+ #### `stop` - stop builds
305
310
 
306
311
  ```bash
307
- # 停止一个构建
312
+ # Stop one build
308
313
  jenkins-cli stop 1234
309
314
 
310
- # 清理当前 Job 的队列和所有运行中的构建
315
+ # Clear queue and stop all running builds for current job
311
316
  jenkins-cli stop -a
312
317
 
313
- # 停止 Jenkins 上所有的构建和队列项 (慎用)
318
+ # Stop all builds and queue items on Jenkins (use with caution)
314
319
  jenkins-cli stop -A
315
320
  ```
316
321
 
317
- #### `view` - 视图管理
322
+ #### `view` - view management
318
323
 
319
324
  ```bash
320
- # 查看所有视图
325
+ # Show all views
321
326
  jenkins-cli view list
322
327
 
323
- # 查看视图包含的 Job
328
+ # Show jobs in a view
324
329
  jenkins-cli view show MyView
325
330
 
326
- # 添加 Job 到视图
331
+ # Add a job to a view
327
332
  jenkins-cli view add MyView my-job
328
333
 
329
- # 从视图移除 Job
334
+ # Remove a job from a view
330
335
  jenkins-cli view remove MyView my-job
331
336
 
332
- # 编辑视图中的 Job(勾选/取消)
337
+ # Edit jobs in a view (check/uncheck)
333
338
  jenkins-cli view edit MyView
334
339
 
335
- # 创建视图
340
+ # Create a view
336
341
  jenkins-cli view create MyView
337
342
 
338
- # 删除视图
343
+ # Delete a view
339
344
  jenkins-cli view delete MyView
340
345
  ```
341
346
 
342
- #### `workspace` - 工作区
347
+ #### `workspace` - workspace
343
348
 
344
349
  ```bash
345
- # 查看当前 Job 的工作区(默认读取 jenkins-cli.yaml 里的 job)
350
+ # Show workspace for current job (reads `job` from jenkins-cli.yaml by default)
346
351
  jenkins-cli ws
347
352
 
348
- # 查看子目录(当前 Job)
353
+ # Show subdirectory (current job)
349
354
  jenkins-cli ws -p dist
350
355
 
351
- # 指定 Job
356
+ # Specify job
352
357
  jenkins-cli ws -j my-job
353
358
 
354
- # 查看文件内容
359
+ # Show file contents
355
360
  jenkins-cli ws cat dist/index.html
356
361
 
357
- # 指定 Job 查看文件内容
362
+ # Show file contents for a specific job
358
363
  jenkins-cli ws cat dist/index.html -j my-job
359
364
 
360
- # 保存文件到本地
365
+ # Save file locally
361
366
  jenkins-cli ws cat dist/index.html -o ./index.html
362
367
 
363
- # 下载并用默认应用打开(适合图片)
368
+ # Download and open with default app (useful for images)
364
369
  jenkins-cli ws cat public/favicon.ico --open
365
370
 
366
- # 清空工作区(会提示确认)
371
+ # Wipe workspace (will prompt for confirmation)
367
372
  jenkins-cli ws wipe
368
373
 
369
- # 指定 Job 清空工作区
374
+ # Wipe workspace for a specific job
370
375
  jenkins-cli ws wipe -j my-job
371
376
  ```
372
377
 
373
378
  ### ❓ FAQ
374
379
 
375
- **Q: `stop` `queue cancel` 命令返回 403 Forbidden?**
380
+ **Q: Why does `stop` or `queue cancel` return 403 Forbidden?**
376
381
 
377
- A: 通常是 Jenkins 用户权限不足 (需要 `Job > Cancel` 权限)。请确保你使用的是 API Token 而非密码,并检查 Jenkins CSRF 设置。工具会自动处理 CSRF,但权限问题需要手动排查。
382
+ A: Most likely the Jenkins user lacks permission (requires `Job > Cancel`). Make sure you use an API token instead of a password, and verify Jenkins CSRF settings. This tool handles CSRF automatically, but permission issues must be fixed manually.
package/dist/cli.js CHANGED
@@ -2,41 +2,43 @@
2
2
  import { program, CommanderError } from 'commander';
3
3
  import oe from 'inquirer';
4
4
  import y from 'chalk';
5
- import Qt from 'ora';
5
+ import rn from 'ora';
6
6
  import { exec, spawn } from 'child_process';
7
- import Jn, { promisify } from 'util';
7
+ import Tn, { promisify } from 'util';
8
8
  import M from 'fs-extra';
9
- import sn from 'js-yaml';
9
+ import bn from 'js-yaml';
10
10
  import z from 'path';
11
- import pn from 'axios';
11
+ import Sn from 'axios';
12
12
  import { Buffer } from 'buffer';
13
- import kn from 'jiti';
13
+ import An from 'jiti';
14
14
  import { fileURLToPath } from 'url';
15
15
  import { promises } from 'fs';
16
16
  import { XMLParser } from 'fast-xml-parser';
17
- import En from 'os';
17
+ import tr from 'os';
18
18
 
19
- var Ye="3.1.0",Xe="Jenkins deployment CLI";var d=class extends Error{hint;example;details;exitCode;constructor(n,e={}){super(n),this.name="UserError",this.hint=e.hint,this.example=e.example,this.details=e.details,this.exitCode=e.exitCode??1;}};function nn(t){return t instanceof d?t:t instanceof CommanderError?new d(t.message,{hint:"Check the command usage and options.",example:"jenkins-cli --help",exitCode:t.exitCode??1}):t instanceof Error?new d(t.message,{hint:"Check your input and try again.",example:"jenkins-cli --help"}):new d("Unknown error",{example:"jenkins-cli --help"})}function q(t){let n=nn(t);if(console.error(y.red(`
20
- \u274C ${n.message}
21
- `)),n.details&&console.error(y.gray(n.details)),n.hint&&console.error(y.yellow(`Hint: ${n.hint}`)),n.example!==""){let e=n.example??"jenkins-cli --help";console.error(y.cyan(`Example: ${e}`));}console.error(y.gray('Run "jenkins-cli --help" for usage.')),process.exitCode=n.exitCode;}var ve=promisify(exec);async function Ze(){try{let{stdout:t}=await ve("git branch --show-current"),n=t.trim();if(!n)throw new d("Not on any branch (detached HEAD state).",{hint:"Checkout a branch before running this command.",example:"git checkout main"});return n}catch{throw new d("Failed to get current branch.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}async function et(){try{let{stdout:t}=await ve('git branch -r --format="%(refname:short)"');return t.split(`
22
- `).filter(n=>n.includes("/")&&!n.includes("HEAD"))}catch{throw new d("Failed to list branches.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}async function ce(){try{let{stdout:t}=await ve("git config user.name"),n=t.trim();return n||void 0}catch{return}}var D="jenkins-cli.yaml",ue="package.json",le="jenkins-cli",an="jenkinsCli";function nt(t,n=process.cwd()){let e=n;for(;;){let r=z.join(e,t);if(M.existsSync(r))return r;let o=z.dirname(e);if(o===e)return null;e=o;}}function cn(t=process.cwd()){return nt(D,t)}function ln(t=process.cwd()){let n=nt(ue,t);return n?z.dirname(n):null}function un(t){let n=Array.isArray(t.modes)&&t.modes.length>0||typeof t.modes=="string";return !!(t.apiToken&&t.job&&n)}async function tt(t){let n=await M.readFile(t,"utf-8"),e=sn.load(n);if(!e||typeof e!="object")throw new d(`Invalid config in ${D}. Expected a YAML object.`,{hint:"Top-level config must be key/value pairs.",example:`${D}:
19
+ var nt="3.1.2",rt="Jenkins deployment CLI";var p=class extends Error{hint;example;details;exitCode;constructor(t,e={}){super(t),this.name="UserError",this.hint=e.hint,this.example=e.example,this.details=e.details,this.exitCode=e.exitCode??1;}};function gn(n){return n instanceof p?n:n instanceof CommanderError?new p(n.message,{hint:"Check the command usage and options.",example:"jenkins-cli --help",exitCode:n.exitCode??1}):n instanceof Error?new p(n.message,{hint:"Check your input and try again.",example:"jenkins-cli --help"}):new p("Unknown error",{example:"jenkins-cli --help"})}function q(n){let t=gn(n);if(console.error(y.red(`
20
+ \u274C ${t.message}
21
+ `)),t.details&&console.error(y.gray(t.details)),t.hint&&console.error(y.yellow(`Hint: ${t.hint}`)),t.example!==""){let e=t.example??"jenkins-cli --help";console.error(y.cyan(`Example: ${e}`));}console.error(y.gray('Run "jenkins-cli --help" for usage.')),process.exitCode=t.exitCode;}var Ae=promisify(exec);async function ot(){try{let{stdout:n}=await Ae("git branch --show-current"),t=n.trim();if(!t)throw new p("Not on any branch (detached HEAD state).",{hint:"Checkout a branch before running this command.",example:"git checkout main"});return t}catch{throw new p("Failed to get current branch.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}async function it(){try{let{stdout:n}=await Ae('git branch -r --format="%(refname:short)"');return n.split(`
22
+ `).filter(t=>t.includes("/")&&!t.includes("HEAD"))}catch{throw new p("Failed to list branches.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}async function le(){try{let{stdout:n}=await Ae("git config user.name"),t=n.trim();return t||void 0}catch{return}}var D="jenkins-cli.yaml",me="package.json",ue="jenkins-cli",yn="jenkinsCli";function at(n,t=process.cwd()){let e=t;for(;;){let r=z.join(e,n);if(M.existsSync(r))return r;let o=z.dirname(e);if(o===e)return null;e=o;}}function kn(n=process.cwd()){return at(D,n)}function xn(n=process.cwd()){let t=at(me,n);return t?z.dirname(t):null}function jn(n){let t=Array.isArray(n.modes)&&n.modes.length>0||typeof n.modes=="string";return !!(n.apiToken&&n.job&&t)}async function st(n){let t=await M.readFile(n,"utf-8"),e=bn.load(t);if(!e||typeof e!="object")throw new p(`Invalid config in ${D}. Expected a YAML object.`,{hint:"Top-level config must be key/value pairs.",example:`${D}:
23
23
  apiToken: http://user:token@host
24
24
  job: my-job
25
- modes: [dev]`});return e}async function mn(t){let n=z.join(t,ue);if(!await M.pathExists(n))return {};let e=await M.readFile(n,"utf-8"),r=JSON.parse(e),o=r[le]??r[an];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new d(`Invalid ${ue} config: "${le}" must be an object.`,{hint:"Use an object for the config section.",example:`"${le}": { "apiToken": "http://user:token@host", "job": "my-job", "modes": ["dev"] }`});return o}async function rt(){let t=process.cwd(),n=ln(t)??t,e=z.join(n,D),r={},o=z.dirname(n),s=cn(o);s&&await M.pathExists(s)&&(r=await tt(s));let i=await mn(n);r={...r,...i};let a=await M.pathExists(e)?await tt(e):{};if(r={...r,...a},!un(r))throw new d("Config incomplete or not found.",{hint:`Tried ${D} in project root, "${le}" in ${ue}, and walking up from cwd. Required: apiToken, job, modes (non-empty array).`,example:`${D}:
25
+ modes: [dev]`});return e}async function Cn(n){let t=z.join(n,me);if(!await M.pathExists(t))return {};let e=await M.readFile(t,"utf-8"),r=JSON.parse(e),o=r[ue]??r[yn];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new p(`Invalid ${me} config: "${ue}" must be an object.`,{hint:"Use an object for the config section.",example:`"${ue}": { "apiToken": "http://user:token@host", "job": "my-job", "modes": ["dev"] }`});return o}async function ct(){let n=process.cwd(),t=xn(n)??n,e=z.join(t,D),r={},o=z.dirname(t),s=kn(o);s&&await M.pathExists(s)&&(r=await st(s));let i=await Cn(t);r={...r,...i};let a=await M.pathExists(e)?await st(e):{};if(r={...r,...a},!jn(r))throw new p("Config incomplete or not found.",{hint:`Tried ${D} in project root, "${ue}" in ${me}, and walking up from cwd. Required: apiToken, job, modes (non-empty array).`,example:`${D}:
26
26
  apiToken: http://user:token@host
27
27
  job: my-job
28
28
  modes:
29
- - dev`});return {...r,projectRoot:n}}var W={maxRedirects:0,validateStatus:t=>t>=200&&t<400};function x(t){return `job/${t.split("/").map(e=>e.trim()).filter(Boolean).map(encodeURIComponent).join("/job/")}`}function pe(t){let n=Array.isArray(t)?t.find(o=>o?.parameters):void 0,e=Array.isArray(n?.parameters)?n.parameters:[],r={};for(let o of e)o?.name&&(r[String(o.name)]=o?.value);return r}function ot(t){try{let e=new URL(t).pathname.split("/").filter(Boolean),r=[];for(let o=0;o<e.length-1;o++)e[o]==="job"&&e[o+1]&&r.push(decodeURIComponent(e[o+1]));return r.join("/")}catch{return ""}}async function fn(t,n){let r=(await t.axios.get(new URL("api/json?tree=number,url,building,result,timestamp,duration,estimatedDuration,fullDisplayName,displayName,actions[parameters[name,value]]",n).toString())).data??{};return {...r,parameters:pe(r.actions)}}function it(t,n=400){try{let r=(typeof t=="string"?t:JSON.stringify(t??"",null,2)).trim();return r?r.length>n?`${r.slice(0,n)}...`:r:""}catch{return ""}}function X(t){if(typeof t=="number"&&Number.isFinite(t))return t;if(typeof t=="string"){let n=Number(t);if(Number.isFinite(n))return n}}function Z(t){let n=X(t);if(n!==void 0)return n;if(typeof t=="string"){let e=Date.parse(t);if(!Number.isNaN(e))return e}}function N(t){return t.split("/").map(e=>e.trim()).filter(Boolean).map(encodeURIComponent).join("/")}function Ne(t,n){let e=t.replace(/\/+$/,""),r=n.replace(/^\/+/,"");return /^https?:\/\//i.test(e)?new URL(`${r}`,`${e}/`).toString():`${e}/${r}`}function _(t){return t.replace(/&nbsp;/g," ").replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'")}function me(t){return t.replace(/<[^>]*>/g,"")}function dn(t){return /<!doctype\s+html|<html\b|<body\b/i.test(t)}function gn(t){let n=t.match(/<pre[^>]*>([\s\S]*?)<\/pre>/i),e=t.match(/<code[^>]*>([\s\S]*?)<\/code>/i),r=t.match(/<body[^>]*>([\s\S]*?)<\/body>/i),s=(n?.[1]??e?.[1]??r?.[1]??t).replace(/<br\s*\/?>/gi,`
30
- `);return _(me(s))}function hn(t){let n=[...t.matchAll(/<tr[^>]*>([\s\S]*?)<\/tr>/gi)],e=[],r=new Set;for(let s of n){let i=s[1]??"",a=[...i.matchAll(/<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi)],l="",c="";for(let $ of a){let O=$[1]??"",F=_(me($[2]??"")).trim();if(!(!F||F==="..")&&!/all\s+files\s+in\s+zip/i.test(F)){l=O,c=F;break}}if(!l||!c||l==="../"||l==="..")continue;let u=/class="[^"]*folder[^"]*"/i.test(i)||l.endsWith("/")?"dir":"file",p=_(l.split("?")[0]??"").replace(/\/+$/,"")||c,m=i.match(/class="last-modified"[^>]*>([\s\S]*?)<\/td>/i),f=m?_(me(m[1]??"")).trim():"",g=f?Z(f):void 0,w=`${c}|${p}`;r.has(w)||(r.add(w),e.push({name:c,path:p,type:u,modified:g}));}if(e.length)return e;let o=[...t.matchAll(/<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi)];for(let s of o){let i=String(s[1]??"").trim(),a=_(me(s[2]??"")).trim();if(!a||a===".."||/all\s+files\s+in\s+zip/i.test(a)||!i||i==="../"||i===".."||/^https?:\/\//i.test(i)||i.startsWith("/")||i.startsWith("?")||i.startsWith("#")||/\/job\//i.test(i)||/\/view\//i.test(i))continue;let l=i.endsWith("/")?"dir":"file",c=_(i.split("?")[0]??"").replace(/\/+$/,"")||a,u=`${a}|${c}`;r.has(u)||(r.add(u),e.push({name:a,path:c,type:l}));}return e}async function Ae(t,n,e){let r=Ne(n,`ws/${e}`),o=[`${r}?format=raw`,`${r}?format=txt`,`${r}?format=plain`,`${r}?plain=1`,`${r}?raw=1`,r],s;for(let i of o)try{let a=await t.axios.get(i,{responseType:"arraybuffer",headers:{Accept:"*/*"}}),l=a.data??new ArrayBuffer(0),c=Buffer.from(l);if(!c.length)continue;let u=a.headers?.["content-type"];return {data:c,contentType:u?String(u):void 0}}catch(a){if(s=a,a?.response?.status===404)continue;throw a}throw s||new Error("Workspace file not found")}async function Be(t,n,e){let r=Ne(n,`ws/${e}`),o=[`${r}?format=raw`,`${r}?format=txt`,`${r}?format=plain`,`${r}?plain=1`,`${r}?raw=1`,r],s;for(let i of o)try{let a=await t.axios.get(i,{responseType:"text",headers:{Accept:"text/plain,*/*"}}),l=typeof a.data=="string"?a.data:String(a.data??"");if(!l)continue;if(dn(l)){let c=gn(l);if(c.trim())return c;continue}return l}catch(a){if(s=a,a?.response?.status===404)continue;throw a}throw s||new Error("Workspace file not found")}async function We(t,n,e){let r=Ne(n,`ws/${e}`),o=r.endsWith("/")?r:`${r}/`,s=[`${o}api/json?depth=1`,`${o}?format=json&depth=1`,`${o}?depth=1&format=json`,`${o}?format=json`,`${o}?depth=1`],i;for(let a of s)try{let c=(await t.axios.get(a,{headers:{Accept:"application/json"}})).data;if(typeof c=="string")try{c=JSON.parse(c);}catch{continue}if(c&&typeof c=="object")return c}catch(l){if(i=l,l?.response?.status===404)continue;throw l}try{let a=await t.axios.get(o,{headers:{Accept:"text/html"}});if(typeof a.data=="string"){let l=hn(a.data);if(l.length)return {children:l}}}catch(a){i=a;}throw i||new Error("Workspace listing not found")}function bn(t){if(Array.isArray(t))return t;let n=[t?.children,t?.files,t?.childs,t?.items,t?.entries,t?.content,t?.tree];for(let e of n){if(Array.isArray(e))return e;if(e&&Array.isArray(e.children))return e.children}return t?.children&&Array.isArray(t.children?.children)?t.children.children:[]}function Ee(t){return bn(t).map(e=>{let r=String(e?.name??e?.displayName??"").trim(),o=String(e?.path??e?.relativePath??e?.filePath??e?.href??e?.url??"").trim(),s=o?o.split("/").filter(Boolean).pop()??o:"",i=r||s||"-",a=o||r||i,l=(()=>e?.directory===!0||e?.isDirectory===!0||e?.isFolder===!0||e?.type==="directory"||e?.type==="dir"||e?.kind==="directory"||e?.kind==="dir"?"dir":e?.file===!0||e?.type==="file"||e?.kind==="file"?"file":"unknown")(),c=X(e?.size)??X(e?.length)??X(e?.fileSize)??X(e?.bytes)??void 0,u=Z(e?.lastModified)??Z(e?.modified)??Z(e?.mtime)??Z(e?.timestamp)??void 0,p=String(e?.href??e?.url??e?.link??"").trim()||void 0;return {name:i,path:a,url:p,size:c,modified:u,type:l}})}function st(t){let n=t.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!n)throw new d("Invalid apiToken format.",{hint:"Expected: http(s)://username:token@host:port",example:"http://user:token@jenkins.example.com:8080"});let[,e,r,o,s]=n,i=`${e}://${s.replace(/\/$/,"")}/`,a=pn.create({baseURL:i,auth:{username:r,password:o},proxy:!1,timeout:3e4});return process.argv.includes("--debug")&&a.interceptors.request.use(c=>{let u=String(c.method??"get").toUpperCase(),p=new URL(String(c.url??""),c.baseURL??i).toString(),m=c.params,f=c.data;f instanceof URLSearchParams?f=Object.fromEntries(f):typeof f=="string"?String(c.headers?.["Content-Type"]??c.headers?.["content-type"]??"").includes("application/x-www-form-urlencoded")?f=Object.fromEntries(new URLSearchParams(f)):f=f.trim()?f:void 0:Buffer.isBuffer(f)&&(f=`[Buffer ${f.length} bytes]`);let g={method:u,url:p};return m&&Object.keys(m).length>0&&(g.params=m),f!=null&&f!==""&&(g.data=f),console.log(y.cyan("[Jenkins Debug] Request")),console.log(JSON.stringify(g,null,2)),c}),{baseURL:i,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function at(t,n=5e3){try{let e=await t.axios.get("crumbIssuer/api/json",{timeout:n}),{data:r}=e;r?.crumbRequestField&&r?.crumb&&(t.axios.defaults.headers.common[r.crumbRequestField]=r.crumb,t.crumbForm={field:r.crumbRequestField,value:r.crumb});let o=e.headers["set-cookie"];if(o){let s=Array.isArray(o)?o.map(i=>i.split(";")[0].trim()):[String(o).split(";")[0].trim()];t.axios.defaults.headers.common.Cookie=s.join("; ");}}catch{}}function Ue(t){return t.crumbPromise||(t.crumbPromise=at(t,5e3)),t.crumbPromise}async function E(t){await Ue(t),t.crumbForm||(t.crumbPromise=at(t,15e3),await t.crumbPromise);}async function H(t,n){let r=(await t.axios.get("queue/api/json?tree=items[id,task[name],actions[parameters[name,value]],why]")).data.items;if(n){let o=n.trim(),s=o.split("/").filter(Boolean).pop();return r.filter(i=>{let a=i.task?.name;return a===o||(s?a===s:!1)})}return r}async function ee(t,n){let e=await t.axios.get(`${x(n)}/api/json?tree=builds[number,url,building,result,timestamp,duration,estimatedDuration,actions[parameters[name,value]]]`);return (Array.isArray(e.data?.builds)?e.data.builds:[]).filter(o=>o?.building).map(o=>({...o,parameters:pe(o.actions)}))}async function fe(t){let n=await t.axios.get("computer/api/json?tree=computer[displayName,executors[currentExecutable[number,url]],oneOffExecutors[currentExecutable[number,url]]]"),e=Array.isArray(n.data?.computer)?n.data.computer:[],r=[];for(let i of e){let a=Array.isArray(i?.executors)?i.executors:[],l=Array.isArray(i?.oneOffExecutors)?i.oneOffExecutors:[];for(let c of [...a,...l]){let u=c?.currentExecutable;u?.url&&r.push({number:Number(u.number),url:String(u.url)});}}let o=new Map;for(let i of r)i.url&&o.set(i.url,i);return (await Promise.all([...o.values()].map(async i=>{let a=await fn(t,i.url),l=ot(String(a?.url??i.url));return {...a,job:l}}))).filter(i=>i?.building)}async function te(t,n){return await E(t),await t.axios.post("queue/cancelItem",new URLSearchParams({id:n.toString()}),W),!0}async function ne(t,n,e){try{await E(t),await t.axios.post(`${x(n)}/${e}/stop/`,new URLSearchParams({}),W);}catch(r){let o=r.response?.status;if(o===403){let s=typeof r.response?.data=="string"?r.response.data.slice(0,200):JSON.stringify(r.response?.data??"").slice(0,200),i=/crumb|csrf/i.test(s);console.log(y.red("[Error] 403 Forbidden: Jenkins refused the stop request (likely permissions or CSRF).")),console.log(y.yellow('[Hint] Confirm the current user has "Job" -> "Cancel" permission on this job.')),console.log(i?"[Hint] Jenkins returned a crumb/CSRF error. Ensure crumb + session cookie are sent, or use API token (not password).":s?`[Hint] Jenkins response: ${s}`:'[Hint] If "Job/Cancel" is already granted, also try granting "Run" -> "Update" (some versions use it for stop).');return}if(o===404||o===400||o===500){console.log(`[Info] Build #${e} could not be stopped (HTTP ${o}). Assuming it finished.`);return}throw r}}async function Fe(t,n,e){await E(t);let r=`view/${encodeURIComponent(n)}/addJobToView`;try{await t.axios.post(r,new URLSearchParams({name:e}),W);}catch(o){let s=o?.response?.status;throw s===404?new d(`View not found: ${n}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new d(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function Le(t,n,e){await E(t);let r=`view/${encodeURIComponent(n)}/removeJobFromView`;try{await t.axios.post(r,new URLSearchParams({name:e}),W);}catch(o){let s=o?.response?.status;throw s===404?new d(`View not found: ${n}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new d(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function ct(t,n){await E(t);let e=new URLSearchParams({name:n,mode:"hudson.model.ListView",json:JSON.stringify({name:n,mode:"hudson.model.ListView"})});try{await t.axios.post("createView",e,W);}catch(r){let o=r?.response?.status;throw o===400?new d(`View already exists: ${n}`,{hint:'Use "jenkins-cli view list" to see existing views.'}):o===403?new d(`Forbidden to create view: ${n}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Create".'}):r}}async function lt(t,n){await E(t);let e=`view/${encodeURIComponent(n)}/doDelete`;try{await t.axios.post(e,new URLSearchParams({}),W);}catch(r){let o=r?.response?.status;throw o===404?new d(`View not found: ${n}`,{hint:'Check the view name using "jenkins-cli view list".'}):o===403?new d(`Forbidden to delete view: ${n}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Delete".'}):r}}async function de(t,n,e){let r="branch",o="mode",s=e[o];s&&console.log(`Checking for conflicting builds for mode: ${s}...`);let[i,a]=await Promise.all([H(t,n),ee(t,n)]),l=(p,m)=>{let f=p.actions?.find(w=>w.parameters);return f?f.parameters.find(w=>w.name===m)?.value:void 0},c=e[r];if(s&&c){let p=i.find(m=>l(m,o)===s&&l(m,r)===c);if(p)return new URL(`queue/item/${p.id}/`,t.baseURL).toString()}let u=!1;if(s)for(let p of a)l(p,o)===s&&(console.log(`Stopping running build #${p.number} (mode=${s})...`),await ne(t,n,p.number),u=!0);u&&await new Promise(p=>setTimeout(p,1e3));try{return await E(t),(await t.axios.post(`${x(n)}/buildWithParameters/`,new URLSearchParams({...e}),W)).headers.location||""}catch(p){let m=p?.response?.status,f=it(p?.response?.data);if(m===400){let g=f?`Jenkins response (snippet):
31
- ${f}`:void 0;throw new d("Jenkins rejected the request (400).",{hint:`Common causes: job is not parameterized, parameter names do not match, or the job endpoint differs (e.g. multibranch jobs). This CLI sends parameters "${r}" and "${o}".`,example:"jenkins-cli build -b origin/main -m dev",details:g})}if(m){let g=f?`Jenkins response (snippet):
32
- ${f}`:void 0;throw new d(`Request failed with status code ${m}.`,{hint:"Check Jenkins job permissions and parameters.",example:"jenkins-cli me",details:g})}throw p}}async function ut(t){return (await t.axios.get("whoAmI/api/json")).data}async function L(t){return (await t.axios.get("api/json?tree=jobs[name,url,color]")).data.jobs??[]}async function mt(t,n,e){let r=String(n??"").trim();if(!r)return;let o=Math.max(1,e?.depth??5),s="jobs[name,url]";for(let u=0;u<o;u+=1)s=`jobs[name,url,${s}]`;let i=await t.axios.get(`api/json?tree=${encodeURIComponent(s)}`),a=Array.isArray(i.data?.jobs)?i.data.jobs:[],l=[],c=[...a];for(;c.length;){let u=c.shift();if(!u)continue;let p=String(u.name??"").trim();if(p&&p===r&&u.url){let m=ot(String(u.url));m&&l.push(m);}Array.isArray(u.jobs)&&c.push(...u.jobs);}if(l.length)return l[0]}async function ge(t,n){try{let r=(await t.axios.get(`${x(n)}/api/json?tree=name,fullName,url,buildable,inQueue,nextBuildNumber,color,healthReport[description,score],lastBuild[number,url,building,result,timestamp,duration],lastCompletedBuild[number,url,result,timestamp,duration],lastSuccessfulBuild[number,url,building,result,timestamp,duration],lastFailedBuild[number,url,building,result,timestamp,duration]`)).data??{},o=typeof r?.fullName=="string"?r.fullName:"",s=typeof r?.name=="string"?r.name:"",i=String(n??"").trim();if(!o&&!s)throw new d(`Job not found: ${i}`,{hint:"Check the job name or use -j/--job to specify the correct job.",example:"jenkins-cli job show -j my-job"});if(i&&i!==o&&i!==s)throw new d(`Job not found: ${i}`,{hint:`Jenkins returned job "${o||s}".`,example:"jenkins-cli console last -j my-job -s success"});return r}catch(e){if(e?.response?.status===404){let o=String(n??"").trim();throw new d(`Job not found: ${o}`,{hint:"Check the job name or use -j/--job to specify the correct job.",example:"jenkins-cli console last -j my-job -s success"})}throw e}}async function pt(t,n,e){let r=Math.max(1,e?.limit??20);return ((await t.axios.get(`${x(n)}/api/json?tree=builds[number,url,building,result,timestamp,duration,actions[parameters[name,value]]]{0,${r}}`)).data.builds??[]).map(i=>({number:i.number,result:i.result,building:i.building,timestamp:i.timestamp,duration:i.duration,url:i.url,parameters:pe(i.actions)}))}async function ft(t,n,e){let r=Math.max(1,e?.limit??20);return ((await t.axios.get(`${x(n)}/api/json?tree=builds[number,url,timestamp,changeSet[items[msg,author[fullName,id,absoluteUrl],authorEmail,authorId,date]]]{0,${r}}`)).data.builds??[]).map(i=>({number:i.number,timestamp:i.timestamp,url:i.url,changeSet:i.changeSet??{}}))}async function he(t,n,e){let o=(await t.axios.get(`${x(n)}/${e}/api/json?tree=number,url,building,result,timestamp,duration,estimatedDuration,fullDisplayName,displayName,description,actions[parameters[name,value]],changeSet[items[msg,author[fullName,id,absoluteUrl],authorEmail,authorId,date]],culprits[fullName],executor[number,progress]`)).data??{};return {...o,parameters:pe(o.actions)}}async function dt(t,n,e){let r=await t.axios.get(`${x(n)}/${e}/consoleText`,{responseType:"text"});return String(r.data??"")}async function gt(t,n){return ((await t.axios.get(`${x(n)}/api/json?tree=property[parameterDefinitions[name,type,description,defaultParameterValue[value],choices]]`)).data?.property??[]).flatMap(s=>s?.parameterDefinitions??[]).filter(Boolean).map(s=>({name:s.name,type:s.type,description:s.description,default:s.defaultParameterValue?.value,choices:s.choices}))}async function Te(t,n,e){let r=String(e?.path??"").trim(),o=r?`${N(r)}/`:"",s=await We(t,x(n),o);return Ee(s??{})}async function ht(t,n,e,r){let o=String(r?.path??"").trim(),s=o?`${N(o)}/`:"",i=`view/${encodeURIComponent(n)}/${x(e)}`,a=await We(t,i,s);return Ee(a??{})}async function bt(t,n,e,r){let o=String(r?.path??"").trim(),s=o?`${N(o)}/`:"",i=new URL(`${x(e)}/`,n).toString(),a=await We(t,i,s);return Ee(a??{})}async function wt(t,n,e){let r=N(String(e??"").trim());return await Be(t,x(n),r)}async function yt(t,n,e){let r=N(String(e??"").trim());return await Ae(t,x(n),r)}async function kt(t,n,e,r){let o=N(String(r??"").trim()),s=`view/${encodeURIComponent(n)}/${x(e)}`;return await Be(t,s,o)}async function xt(t,n,e,r){let o=N(String(r??"").trim()),s=`view/${encodeURIComponent(n)}/${x(e)}`;return await Ae(t,s,o)}async function jt(t,n,e,r){let o=N(String(r??"").trim()),s=new URL(`${x(e)}/`,n).toString();return await Be(t,s,o)}async function Ct(t,n,e,r){let o=N(String(r??"").trim()),s=new URL(`${x(e)}/`,n).toString();return await Ae(t,s,o)}async function St(t,n){try{return await E(t),await t.axios.post(`${x(n)}/doWipeOutWorkspace`,new URLSearchParams({}),W),!0}catch(e){let r=e?.response?.status;if(r){let o=it(e?.response?.data);throw new d(`Workspace wipe failed (HTTP ${r}).`,{hint:"Check Job/Workspace permission or CSRF settings.",example:"jenkins-cli workspace wipe -j my-job",details:o?`Jenkins response (snippet):
33
- ${o}`:void 0})}throw e}}async function Ve(t,n){let e=await t.axios.get(`${x(n)}/config.xml`,{responseType:"text"});return String(e.data??"")}async function $t(t,n,e,r){let o=`${x(n)}/${e}/logText/progressiveText`,s=new URLSearchParams;s.append("start",String(r));let i=await t.axios.post(o,s,{responseType:"text"}),a=String(i.data??""),l=i.headers["x-text-size"],c=i.headers["x-more-data"],u=l?Number(l):r+Buffer.byteLength(a);return {text:a,newOffset:u,hasMoreData:c==="true"}}async function re(t,n){let e=n?.raw?"views[*]":"views[name,url]";return (await t.axios.get(`api/json?tree=${e}`)).data.views??[]}async function Q(t,n){let e=`view/${encodeURIComponent(n)}`;try{return (await t.axios.get(`${e}/api/json?tree=jobs[name,url,color]`)).data.jobs??[]}catch(r){throw r?.response?.status===404?new d(`View not found: ${n}`,{hint:'Check the view name using "jenkins-cli view list".',example:"jenkins-cli view show MyView"}):r}}async function Jt(t,n){let e=String(n??"").trim();if(!e)return;let r=e.split("/").filter(Boolean).pop()??e,o=await re(t,{raw:!1});for(let s of o){let i=String(s?.name??"").trim();if(i)try{if((await Q(t,i)).some(l=>{let c=String(l?.name??"").trim();return c===e||c===r}))return i}catch{}}}async function b(){let t=Qt("Loading configuration...").start();try{let n=await rt();t.succeed("Configuration loaded");let e=st(n.apiToken);return Ue(e),{config:n,jenkins:e}}catch(n){q(n),process.exit(1);}}var jn=kn(fileURLToPath(import.meta.url),{interopDefault:!0});async function we(t){return await Promise.resolve(t)}function Cn(t){let n=String(t??"").trim();if(!n)return null;let e=n.split(":");if(e.length<2)return null;if(e.length===2){let[s,i]=e;return !s||!i?null:{file:s,exportName:i}}let r=e.at(-1);if(!r)return null;let o=e.slice(0,-2).join(":");return o?{file:o,exportName:r}:null}function Sn(t,n){return z.isAbsolute(n)?n:z.join(t,n)}function Rt(t){return !!t&&typeof t=="object"&&!Array.isArray(t)}async function Oe(t,n){let e=Cn(n);if(!e)return null;let r=Sn(t,e.file);if(!await M.pathExists(r))throw new d(`configs reference not found: ${e.file}`,{hint:"Check the file path (relative to project root) and file name.",example:'configs: [{ choices: "./scripts/prompts.ts:choices" }]'});let o=jn(r),s=Rt(o)?o:{default:o},i=e.exportName==="default"?s.default:s[e.exportName];if(i===void 0)throw new d(`configs reference export not found: ${n}`,{hint:"Make sure the export exists in the referenced file.",example:'export const choices = ["dev", "uat"]'});return {value:i}}async function be(t,n,e){if(typeof n!="string")return n;let r=await Oe(t,n);if(!r)return n;if(!e.allow(r.value))throw new d(e.error(n),{hint:e.hint,example:e.example});return r.value}async function Pt(t,n){if(typeof n!="string")return n;let e=await Oe(t,n);if(!e)return n;if(typeof e.value=="function"){let r=e.value();return await we(r)}return await we(e.value)}async function Ie(t,n,e){if(typeof n!="string")return n;let r=await Oe(t,n);if(!r)return n;if(typeof r.value=="function"){let o=r.value(e);return await we(o)}return await we(r.value)}async function Nt(t,n){let e=Array.isArray(t)?t:[];if(e.length===0)return [];let r=String(n.projectRoot??"").trim()||process.cwd(),o=[];for(let s of e){if(!Rt(s))continue;let i={...s};if(i.choices!==void 0){i.choices=await Pt(r,i.choices);let a=String(i.type??"").toLowerCase(),l=i.choices;if((a==="list"||a==="rawlist"||a==="checkbox")&&typeof l!="function"&&!Array.isArray(l))if(l==null)i.choices=[];else if(typeof l=="string"||typeof l=="number"||typeof l=="boolean")i.choices=[String(l)];else if(typeof l[Symbol.iterator]=="function")i.choices=Array.from(l).map(u=>String(u));else throw new d(`configs "${String(i.name??"")}" choices must resolve to an array (got ${typeof l}).`,{hint:"Return an array for list/rawlist/checkbox choices.",example:'export const choices = ["dev", "uat"]'})}i.default!==void 0&&(i.default=await Pt(r,i.default)),i.when!==void 0&&(i.when=await be(r,i.when,{allow:a=>typeof a=="function"||typeof a=="boolean",error:a=>`when reference must be a function or boolean: ${a}`,hint:"Export a function or boolean from the referenced file.",example:'export const when = (answers) => answers.env === "dev"'})),i.validate!==void 0&&(i.validate=await be(r,i.validate,{allow:a=>typeof a=="function",error:a=>`validate reference must be a function: ${a}`,hint:"Export a function from the referenced file.",example:'export const validate = (input) => !!input || "required"'})),i.filter!==void 0&&(i.filter=await be(r,i.filter,{allow:a=>typeof a=="function",error:a=>`filter reference must be a function: ${a}`,hint:"Export a function from the referenced file.",example:"export const filter = (input) => input.trim()"})),i.transformer!==void 0&&(i.transformer=await be(r,i.transformer,{allow:a=>typeof a=="function",error:a=>`transformer reference must be a function: ${a}`,hint:"Export a function from the referenced file.",example:"export const transformer = (input) => input.toUpperCase()"})),o.push(i);}return o}function At(t,n){let e=new Set(n),r={};for(let[o,s]of Object.entries(t))if(!e.has(o)&&s!==void 0){if(s===null){r[o]="";continue}if(Array.isArray(s)){r[o]=s.map(i=>String(i)).join(",");continue}if(typeof s=="object"){try{r[o]=JSON.stringify(s);}catch{r[o]=String(s);}continue}r[o]=String(s);}return r}async function Bt(){console.log(y.bold.blue(`
29
+ - dev`});return {...r,projectRoot:t}}var B={maxRedirects:0,validateStatus:n=>n>=200&&n<400};function x(n){return `job/${n.split("/").map(e=>e.trim()).filter(Boolean).map(encodeURIComponent).join("/job/")}`}function pe(n){let t=Array.isArray(n)?n.find(o=>o?.parameters):void 0,e=Array.isArray(t?.parameters)?t.parameters:[],r={};for(let o of e)o?.name&&(r[String(o.name)]=o?.value);return r}function lt(n){try{let e=new URL(n).pathname.split("/").filter(Boolean),r=[];for(let o=0;o<e.length-1;o++)e[o]==="job"&&e[o+1]&&r.push(decodeURIComponent(e[o+1]));return r.join("/")}catch{return ""}}async function vn(n,t){let r=(await n.axios.get(new URL("api/json?tree=number,url,building,result,timestamp,duration,estimatedDuration,fullDisplayName,displayName,actions[parameters[name,value]]",t).toString())).data??{};return {...r,parameters:pe(r.actions)}}function ut(n,t=400){try{let r=(typeof n=="string"?n:JSON.stringify(n??"",null,2)).trim();return r?r.length>t?`${r.slice(0,t)}...`:r:""}catch{return ""}}function X(n){if(typeof n=="number"&&Number.isFinite(n))return n;if(typeof n=="string"){let t=Number(n);if(Number.isFinite(t))return t}}function Z(n){let t=X(n);if(t!==void 0)return t;if(typeof n=="string"){let e=Date.parse(n);if(!Number.isNaN(e))return e}}function E(n){return n.split("/").map(e=>e.trim()).filter(Boolean).map(encodeURIComponent).join("/")}function We(n,t){let e=n.replace(/\/+$/,""),r=t.replace(/^\/+/,"");return /^https?:\/\//i.test(e)?new URL(`${r}`,`${e}/`).toString():`${e}/${r}`}function _(n){return n.replace(/&nbsp;/g," ").replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'")}function fe(n){return n.replace(/<[^>]*>/g,"")}function $n(n){return /<!doctype\s+html|<html\b|<body\b/i.test(n)}function Jn(n){let t=n.match(/<pre[^>]*>([\s\S]*?)<\/pre>/i),e=n.match(/<code[^>]*>([\s\S]*?)<\/code>/i),r=n.match(/<body[^>]*>([\s\S]*?)<\/body>/i),s=(t?.[1]??e?.[1]??r?.[1]??n).replace(/<br\s*\/?>/gi,`
30
+ `);return _(fe(s))}function Rn(n){let t=[...n.matchAll(/<tr[^>]*>([\s\S]*?)<\/tr>/gi)],e=[],r=new Set;for(let s of t){let i=s[1]??"",a=[...i.matchAll(/<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi)],c="",l="";for(let v of a){let L=v[1]??"",R=_(fe(v[2]??"")).trim();if(!(!R||R==="..")&&!/all\s+files\s+in\s+zip/i.test(R)){c=L,l=R;break}}if(!c||!l||c==="../"||c==="..")continue;let u=/class="[^"]*folder[^"]*"/i.test(i)||c.endsWith("/")?"dir":"file",m=_(c.split("?")[0]??"").replace(/\/+$/,"")||l,f=i.match(/class="last-modified"[^>]*>([\s\S]*?)<\/td>/i),d=f?_(fe(f[1]??"")).trim():"",g=d?Z(d):void 0,b=`${l}|${m}`;r.has(b)||(r.add(b),e.push({name:l,path:m,type:u,modified:g}));}if(e.length)return e;let o=[...n.matchAll(/<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi)];for(let s of o){let i=String(s[1]??"").trim(),a=_(fe(s[2]??"")).trim();if(!a||a===".."||/all\s+files\s+in\s+zip/i.test(a)||!i||i==="../"||i===".."||/^https?:\/\//i.test(i)||i.startsWith("/")||i.startsWith("?")||i.startsWith("#")||/\/job\//i.test(i)||/\/view\//i.test(i))continue;let c=i.endsWith("/")?"dir":"file",l=_(i.split("?")[0]??"").replace(/\/+$/,"")||a,u=`${a}|${l}`;r.has(u)||(r.add(u),e.push({name:a,path:l,type:c}));}return e}async function Ue(n,t,e){let r=We(t,`ws/${e}`),o=[`${r}?format=raw`,`${r}?format=txt`,`${r}?format=plain`,`${r}?plain=1`,`${r}?raw=1`,r],s;for(let i of o)try{let a=await n.axios.get(i,{responseType:"arraybuffer",headers:{Accept:"*/*"}}),c=a.data??new ArrayBuffer(0),l=Buffer.from(c);if(!l.length)continue;let u=a.headers?.["content-type"];return {data:l,contentType:u?String(u):void 0}}catch(a){if(s=a,a?.response?.status===404)continue;throw a}throw s||new Error("Workspace file not found")}async function Le(n,t,e){let r=We(t,`ws/${e}`),o=[`${r}?format=raw`,`${r}?format=txt`,`${r}?format=plain`,`${r}?plain=1`,`${r}?raw=1`,r],s;for(let i of o)try{let a=await n.axios.get(i,{responseType:"text",headers:{Accept:"text/plain,*/*"}}),c=typeof a.data=="string"?a.data:String(a.data??"");if(!c)continue;if($n(c)){let l=Jn(c);if(l.trim())return l;continue}return c}catch(a){if(s=a,a?.response?.status===404)continue;throw a}throw s||new Error("Workspace file not found")}async function Te(n,t,e){let r=We(t,`ws/${e}`),o=r.endsWith("/")?r:`${r}/`,s=[`${o}api/json?depth=1`,`${o}?format=json&depth=1`,`${o}?depth=1&format=json`,`${o}?format=json`,`${o}?depth=1`],i;for(let a of s)try{let l=(await n.axios.get(a,{headers:{Accept:"application/json"}})).data;if(typeof l=="string")try{l=JSON.parse(l);}catch{continue}if(l&&typeof l=="object")return l}catch(c){if(i=c,c?.response?.status===404)continue;throw c}try{let a=await n.axios.get(o,{headers:{Accept:"text/html"}});if(typeof a.data=="string"){let c=Rn(a.data);if(c.length)return {children:c}}}catch(a){i=a;}throw i||new Error("Workspace listing not found")}function Pn(n){if(Array.isArray(n))return n;let t=[n?.children,n?.files,n?.childs,n?.items,n?.entries,n?.content,n?.tree];for(let e of t){if(Array.isArray(e))return e;if(e&&Array.isArray(e.children))return e.children}return n?.children&&Array.isArray(n.children?.children)?n.children.children:[]}function Oe(n){return Pn(n).map(e=>{let r=String(e?.name??e?.displayName??"").trim(),o=String(e?.path??e?.relativePath??e?.filePath??e?.href??e?.url??"").trim(),s=o?o.split("/").filter(Boolean).pop()??o:"",i=r||s||"-",a=o||r||i,c=(()=>e?.directory===!0||e?.isDirectory===!0||e?.isFolder===!0||e?.type==="directory"||e?.type==="dir"||e?.kind==="directory"||e?.kind==="dir"?"dir":e?.file===!0||e?.type==="file"||e?.kind==="file"?"file":"unknown")(),l=X(e?.size)??X(e?.length)??X(e?.fileSize)??X(e?.bytes)??void 0,u=Z(e?.lastModified)??Z(e?.modified)??Z(e?.mtime)??Z(e?.timestamp)??void 0,m=String(e?.href??e?.url??e?.link??"").trim()||void 0;return {name:i,path:a,url:m,size:l,modified:u,type:c}})}function mt(n){let t=n.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!t)throw new p("Invalid apiToken format.",{hint:"Expected: http(s)://username:token@host:port",example:"http://user:token@jenkins.example.com:8080"});let[,e,r,o,s]=t,i=`${e}://${s.replace(/\/$/,"")}/`,a=Sn.create({baseURL:i,auth:{username:r,password:o},proxy:!1,timeout:3e4});return process.argv.includes("--debug")&&a.interceptors.request.use(l=>{let u=String(l.method??"get").toUpperCase(),m=new URL(String(l.url??""),l.baseURL??i).toString(),f=l.params,d=l.data;d instanceof URLSearchParams?d=Object.fromEntries(d):typeof d=="string"?String(l.headers?.["Content-Type"]??l.headers?.["content-type"]??"").includes("application/x-www-form-urlencoded")?d=Object.fromEntries(new URLSearchParams(d)):d=d.trim()?d:void 0:Buffer.isBuffer(d)&&(d=`[Buffer ${d.length} bytes]`);let g={method:u,url:m};return f&&Object.keys(f).length>0&&(g.params=f),d!=null&&d!==""&&(g.data=d),console.log(y.cyan("[Jenkins Debug] Request")),console.log(JSON.stringify(g,null,2)),l}),{baseURL:i,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function ft(n,t=5e3){try{let e=await n.axios.get("crumbIssuer/api/json",{timeout:t}),{data:r}=e;r?.crumbRequestField&&r?.crumb&&(n.axios.defaults.headers.common[r.crumbRequestField]=r.crumb,n.crumbForm={field:r.crumbRequestField,value:r.crumb});let o=e.headers["set-cookie"];if(o){let s=Array.isArray(o)?o.map(i=>i.split(";")[0].trim()):[String(o).split(";")[0].trim()];n.axios.defaults.headers.common.Cookie=s.join("; ");}}catch{}}function Ve(n){return n.crumbPromise||(n.crumbPromise=ft(n,5e3)),n.crumbPromise}async function W(n){await Ve(n),n.crumbForm||(n.crumbPromise=ft(n,15e3),await n.crumbPromise);}async function H(n,t){let r=(await n.axios.get("queue/api/json?tree=items[id,task[name],actions[parameters[name,value]],why]")).data.items;if(t){let o=t.trim(),s=o.split("/").filter(Boolean).pop();return r.filter(i=>{let a=i.task?.name;return a===o||(s?a===s:!1)})}return r}async function ee(n,t){let e=await n.axios.get(`${x(t)}/api/json?tree=builds[number,url,building,result,timestamp,duration,estimatedDuration,actions[parameters[name,value]]]`);return (Array.isArray(e.data?.builds)?e.data.builds:[]).filter(o=>o?.building).map(o=>({...o,parameters:pe(o.actions)}))}async function de(n){let t=await n.axios.get("computer/api/json?tree=computer[displayName,executors[currentExecutable[number,url]],oneOffExecutors[currentExecutable[number,url]]]"),e=Array.isArray(t.data?.computer)?t.data.computer:[],r=[];for(let i of e){let a=Array.isArray(i?.executors)?i.executors:[],c=Array.isArray(i?.oneOffExecutors)?i.oneOffExecutors:[];for(let l of [...a,...c]){let u=l?.currentExecutable;u?.url&&r.push({number:Number(u.number),url:String(u.url)});}}let o=new Map;for(let i of r)i.url&&o.set(i.url,i);return (await Promise.all([...o.values()].map(async i=>{let a=await vn(n,i.url),c=lt(String(a?.url??i.url));return {...a,job:c}}))).filter(i=>i?.building)}async function te(n,t){return await W(n),await n.axios.post("queue/cancelItem",new URLSearchParams({id:t.toString()}),B),!0}async function ne(n,t,e){try{await W(n),await n.axios.post(`${x(t)}/${e}/stop/`,new URLSearchParams({}),B);}catch(r){let o=r.response?.status;if(o===403){let s=typeof r.response?.data=="string"?r.response.data.slice(0,200):JSON.stringify(r.response?.data??"").slice(0,200),i=/crumb|csrf/i.test(s);console.log(y.red("[Error] 403 Forbidden: Jenkins refused the stop request (likely permissions or CSRF).")),console.log(y.yellow('[Hint] Confirm the current user has "Job" -> "Cancel" permission on this job.')),console.log(i?"[Hint] Jenkins returned a crumb/CSRF error. Ensure crumb + session cookie are sent, or use API token (not password).":s?`[Hint] Jenkins response: ${s}`:'[Hint] If "Job/Cancel" is already granted, also try granting "Run" -> "Update" (some versions use it for stop).');return}if(o===404||o===400||o===500){console.log(`[Info] Build #${e} could not be stopped (HTTP ${o}). Assuming it finished.`);return}throw r}}async function Ie(n,t,e){await W(n);let r=`view/${encodeURIComponent(t)}/addJobToView`;try{await n.axios.post(r,new URLSearchParams({name:e}),B);}catch(o){let s=o?.response?.status;throw s===404?new p(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new p(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function qe(n,t,e){await W(n);let r=`view/${encodeURIComponent(t)}/removeJobFromView`;try{await n.axios.post(r,new URLSearchParams({name:e}),B);}catch(o){let s=o?.response?.status;throw s===404?new p(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new p(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function pt(n,t){await W(n);let e=new URLSearchParams({name:t,mode:"hudson.model.ListView",json:JSON.stringify({name:t,mode:"hudson.model.ListView"})});try{await n.axios.post("createView",e,B);}catch(r){let o=r?.response?.status;throw o===400?new p(`View already exists: ${t}`,{hint:'Use "jenkins-cli view list" to see existing views.'}):o===403?new p(`Forbidden to create view: ${t}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Create".'}):r}}async function dt(n,t){await W(n);let e=`view/${encodeURIComponent(t)}/doDelete`;try{await n.axios.post(e,new URLSearchParams({}),B);}catch(r){let o=r?.response?.status;throw o===404?new p(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):o===403?new p(`Forbidden to delete view: ${t}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Delete".'}):r}}async function ge(n,t,e){let r="branch",o="mode",s=e[o];s&&console.log(`Checking for conflicting builds for mode: ${s}...`);let[i,a]=await Promise.all([H(n,t),ee(n,t)]),c=(m,f)=>{let d=m.actions?.find(b=>b.parameters);return d?d.parameters.find(b=>b.name===f)?.value:void 0},l=e[r];if(s&&l){let m=i.find(f=>c(f,o)===s&&c(f,r)===l);if(m)return new URL(`queue/item/${m.id}/`,n.baseURL).toString()}let u=!1;if(s)for(let m of a)c(m,o)===s&&(console.log(`Stopping running build #${m.number} (mode=${s})...`),await ne(n,t,m.number),u=!0);u&&await new Promise(m=>setTimeout(m,1e3));try{return await W(n),(await n.axios.post(`${x(t)}/buildWithParameters/`,new URLSearchParams({...e}),B)).headers.location||""}catch(m){let f=m?.response?.status,d=ut(m?.response?.data);if(f===400){let g=d?`Jenkins response (snippet):
31
+ ${d}`:void 0;throw new p("Jenkins rejected the request (400).",{hint:`Common causes: job is not parameterized, parameter names do not match, or the job endpoint differs (e.g. multibranch jobs). This CLI sends parameters "${r}" and "${o}".`,example:"jenkins-cli build -b origin/main -m dev",details:g})}if(f){let g=d?`Jenkins response (snippet):
32
+ ${d}`:void 0;throw new p(`Request failed with status code ${f}.`,{hint:"Check Jenkins job permissions and parameters.",example:"jenkins-cli me",details:g})}throw m}}async function gt(n){return (await n.axios.get("whoAmI/api/json")).data}async function T(n){return (await n.axios.get("api/json?tree=jobs[name,url,color]")).data.jobs??[]}async function De(n){let t="plugins[shortName,longName,version,enabled,active,hasUpdate,pinned,bundled,deleted]";return (await n.axios.get(`pluginManager/api/json?tree=${t}`)).data.plugins??[]}async function ht(n,t,e){let r=String(t??"").trim();if(!r)return;let o=Math.max(1,e?.depth??5),s="jobs[name,url]";for(let u=0;u<o;u+=1)s=`jobs[name,url,${s}]`;let i=await n.axios.get(`api/json?tree=${encodeURIComponent(s)}`),a=Array.isArray(i.data?.jobs)?i.data.jobs:[],c=[],l=[...a];for(;l.length;){let u=l.shift();if(!u)continue;let m=String(u.name??"").trim();if(m&&m===r&&u.url){let f=lt(String(u.url));f&&c.push(f);}Array.isArray(u.jobs)&&l.push(...u.jobs);}if(c.length)return c[0]}async function he(n,t){try{let r=(await n.axios.get(`${x(t)}/api/json?tree=name,fullName,url,buildable,inQueue,nextBuildNumber,color,healthReport[description,score],lastBuild[number,url,building,result,timestamp,duration],lastCompletedBuild[number,url,result,timestamp,duration],lastSuccessfulBuild[number,url,building,result,timestamp,duration],lastFailedBuild[number,url,building,result,timestamp,duration]`)).data??{},o=typeof r?.fullName=="string"?r.fullName:"",s=typeof r?.name=="string"?r.name:"",i=String(t??"").trim();if(!o&&!s)throw new p(`Job not found: ${i}`,{hint:"Check the job name or use -j/--job to specify the correct job.",example:"jenkins-cli job show -j my-job"});if(i&&i!==o&&i!==s)throw new p(`Job not found: ${i}`,{hint:`Jenkins returned job "${o||s}".`,example:"jenkins-cli console last -j my-job -s success"});return r}catch(e){if(e?.response?.status===404){let o=String(t??"").trim();throw new p(`Job not found: ${o}`,{hint:"Check the job name or use -j/--job to specify the correct job.",example:"jenkins-cli console last -j my-job -s success"})}throw e}}async function wt(n,t,e){let r=Math.max(1,e?.limit??20);return ((await n.axios.get(`${x(t)}/api/json?tree=builds[number,url,building,result,timestamp,duration,actions[parameters[name,value]]]{0,${r}}`)).data.builds??[]).map(i=>({number:i.number,result:i.result,building:i.building,timestamp:i.timestamp,duration:i.duration,url:i.url,parameters:pe(i.actions)}))}async function bt(n,t,e){let r=Math.max(1,e?.limit??20);return ((await n.axios.get(`${x(t)}/api/json?tree=builds[number,url,timestamp,changeSet[items[msg,author[fullName,id,absoluteUrl],authorEmail,authorId,date]]]{0,${r}}`)).data.builds??[]).map(i=>({number:i.number,timestamp:i.timestamp,url:i.url,changeSet:i.changeSet??{}}))}async function we(n,t,e){let o=(await n.axios.get(`${x(t)}/${e}/api/json?tree=number,url,building,result,timestamp,duration,estimatedDuration,fullDisplayName,displayName,description,actions[parameters[name,value]],changeSet[items[msg,author[fullName,id,absoluteUrl],authorEmail,authorId,date]],culprits[fullName],executor[number,progress]`)).data??{};return {...o,parameters:pe(o.actions)}}async function yt(n,t,e){let r=await n.axios.get(`${x(t)}/${e}/consoleText`,{responseType:"text"});return String(r.data??"")}async function kt(n,t){return ((await n.axios.get(`${x(t)}/api/json?tree=property[parameterDefinitions[name,type,description,defaultParameterValue[value],choices]]`)).data?.property??[]).flatMap(s=>s?.parameterDefinitions??[]).filter(Boolean).map(s=>({name:s.name,type:s.type,description:s.description,default:s.defaultParameterValue?.value,choices:s.choices}))}async function Me(n,t,e){let r=String(e?.path??"").trim(),o=r?`${E(r)}/`:"",s=await Te(n,x(t),o);return Oe(s??{})}async function xt(n,t,e,r){let o=String(r?.path??"").trim(),s=o?`${E(o)}/`:"",i=`view/${encodeURIComponent(t)}/${x(e)}`,a=await Te(n,i,s);return Oe(a??{})}async function jt(n,t,e,r){let o=String(r?.path??"").trim(),s=o?`${E(o)}/`:"",i=new URL(`${x(e)}/`,t).toString(),a=await Te(n,i,s);return Oe(a??{})}async function Ct(n,t,e){let r=E(String(e??"").trim());return await Le(n,x(t),r)}async function St(n,t,e){let r=E(String(e??"").trim());return await Ue(n,x(t),r)}async function vt(n,t,e,r){let o=E(String(r??"").trim()),s=`view/${encodeURIComponent(t)}/${x(e)}`;return await Le(n,s,o)}async function $t(n,t,e,r){let o=E(String(r??"").trim()),s=`view/${encodeURIComponent(t)}/${x(e)}`;return await Ue(n,s,o)}async function Jt(n,t,e,r){let o=E(String(r??"").trim()),s=new URL(`${x(e)}/`,t).toString();return await Le(n,s,o)}async function Rt(n,t,e,r){let o=E(String(r??"").trim()),s=new URL(`${x(e)}/`,t).toString();return await Ue(n,s,o)}async function Pt(n,t){try{return await W(n),await n.axios.post(`${x(t)}/doWipeOutWorkspace`,new URLSearchParams({}),B),!0}catch(e){let r=e?.response?.status;if(r){let o=ut(e?.response?.data);throw new p(`Workspace wipe failed (HTTP ${r}).`,{hint:"Check Job/Workspace permission or CSRF settings.",example:"jenkins-cli workspace wipe -j my-job",details:o?`Jenkins response (snippet):
33
+ ${o}`:void 0})}throw e}}async function ze(n,t){let e=await n.axios.get(`${x(t)}/config.xml`,{responseType:"text"});return String(e.data??"")}async function Nt(n,t,e,r){let o=`${x(t)}/${e}/logText/progressiveText`,s=new URLSearchParams;s.append("start",String(r));let i=await n.axios.post(o,s,{responseType:"text"}),a=String(i.data??""),c=i.headers["x-text-size"],l=i.headers["x-more-data"],u=c?Number(c):r+Buffer.byteLength(a);return {text:a,newOffset:u,hasMoreData:l==="true"}}async function re(n,t){let e=t?.raw?"views[*]":"views[name,url]";return (await n.axios.get(`api/json?tree=${e}`)).data.views??[]}async function Q(n,t){let e=`view/${encodeURIComponent(t)}`;try{return (await n.axios.get(`${e}/api/json?tree=jobs[name,url,color]`)).data.jobs??[]}catch(r){throw r?.response?.status===404?new p(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".',example:"jenkins-cli view show MyView"}):r}}async function Et(n,t){let e=String(t??"").trim();if(!e)return;let r=e.split("/").filter(Boolean).pop()??e,o=await re(n,{raw:!1});for(let s of o){let i=String(s?.name??"").trim();if(i)try{if((await Q(n,i)).some(c=>{let l=String(c?.name??"").trim();return l===e||l===r}))return i}catch{}}}async function w(){let n=rn("Loading configuration...").start();try{let t=await ct();n.succeed("Configuration loaded");let e=mt(t.apiToken);return Ve(e),{config:t,jenkins:e}}catch(t){q(t),process.exit(1);}}var Bn=An(fileURLToPath(import.meta.url),{interopDefault:!0});async function ye(n){return await Promise.resolve(n)}function Wn(n){let t=String(n??"").trim();if(!t)return null;let e=t.split(":");if(e.length<2)return null;if(e.length===2){let[s,i]=e;return !s||!i?null:{file:s,exportName:i}}let r=e.at(-1);if(!r)return null;let o=e.slice(0,-2).join(":");return o?{file:o,exportName:r}:null}function Un(n,t){return z.isAbsolute(t)?t:z.join(n,t)}function Bt(n){return !!n&&typeof n=="object"&&!Array.isArray(n)}async function ke(n,t){let e=Wn(t);if(!e)return null;let r=Un(n,e.file);if(!await M.pathExists(r))throw new p(`configs reference not found: ${e.file}`,{hint:"Check the file path (relative to project root) and file name.",example:'configs: [{ choices: "./scripts/prompts.ts:choices" }]'});let o=Bn(r),s=Bt(o)?o:{default:o},i=e.exportName==="default"?s.default:s[e.exportName];if(i===void 0)throw new p(`configs reference export not found: ${t}`,{hint:"Make sure the export exists in the referenced file.",example:'export const choices = ["dev", "uat"]'});return {value:i}}async function Wt(n,t){if(typeof t!="string")return {isRef:!1,isFunction:!1,value:t};let e=await ke(n,t);return e?{isRef:!0,isFunction:typeof e.value=="function",value:e.value}:{isRef:!1,isFunction:!1,value:t}}async function be(n,t,e){if(typeof t!="string")return t;let r=await ke(n,t);if(!r)return t;if(!e.allow(r.value))throw new p(e.error(t),{hint:e.hint,example:e.example});return r.value}async function Ft(n,t){if(typeof t!="string")return t;let e=await ke(n,t);if(!e)return t;if(typeof e.value=="function"){let r=e.value();return await ye(r)}return await ye(e.value)}async function O(n,t,e){if(typeof t!="string")return t;let r=await ke(n,t);if(!r)return t;if(typeof r.value=="function"){let o=r.value(e);return await ye(o)}return await ye(r.value)}async function xe(n,t){let e=Array.isArray(n)?n:[];if(e.length===0)return [];let r=String(t.projectRoot??"").trim()||process.cwd(),o=[];for(let s of e){if(!Bt(s))continue;let i={...s};if(i.choices!==void 0){i.choices=await Ft(r,i.choices);let a=String(i.type??"").toLowerCase(),c=i.choices;if((a==="list"||a==="rawlist"||a==="checkbox")&&typeof c!="function"&&!Array.isArray(c))if(c==null)i.choices=[];else if(typeof c=="string"||typeof c=="number"||typeof c=="boolean")i.choices=[String(c)];else if(typeof c[Symbol.iterator]=="function")i.choices=Array.from(c).map(u=>String(u));else throw new p(`configs "${String(i.name??"")}" choices must resolve to an array (got ${typeof c}).`,{hint:"Return an array for list/rawlist/checkbox choices.",example:'export const choices = ["dev", "uat"]'})}i.default!==void 0&&(i.default=await Ft(r,i.default)),i.when!==void 0&&(i.when=await be(r,i.when,{allow:a=>typeof a=="function"||typeof a=="boolean",error:a=>`when reference must be a function or boolean: ${a}`,hint:"Export a function or boolean from the referenced file.",example:'export const when = (answers) => answers.env === "dev"'})),i.validate!==void 0&&(i.validate=await be(r,i.validate,{allow:a=>typeof a=="function",error:a=>`validate reference must be a function: ${a}`,hint:"Export a function from the referenced file.",example:'export const validate = (input) => !!input || "required"'})),i.filter!==void 0&&(i.filter=await be(r,i.filter,{allow:a=>typeof a=="function",error:a=>`filter reference must be a function: ${a}`,hint:"Export a function from the referenced file.",example:"export const filter = (input) => input.trim()"})),i.transformer!==void 0&&(i.transformer=await be(r,i.transformer,{allow:a=>typeof a=="function",error:a=>`transformer reference must be a function: ${a}`,hint:"Export a function from the referenced file.",example:"export const transformer = (input) => input.toUpperCase()"})),o.push(i);}return o}function Ut(n,t){let e=new Set(t),r={};for(let[o,s]of Object.entries(n))if(!e.has(o)&&s!==void 0){if(s===null){r[o]="";continue}if(Array.isArray(s)){r[o]=s.map(i=>String(i)).join(",");continue}if(typeof s=="object"){try{r[o]=JSON.stringify(s);}catch{r[o]=String(s);}continue}r[o]=String(s);}return r}async function Lt(n,t,e={}){let r=(t??"").trim();if(r)return r;let o=e.projectRoot??process.cwd(),s=await O(o,n,e.answers??{});return String(s??"").trim()||n}function A(n,t){let e=Number(n);if(!Number.isInteger(e)||e<0)throw new p(`${t} must be a non-negative integer.`,{hint:`Provide a valid ${t}.`,example:`jenkins-cli console ${t==="id"?"123":"last -t 100"}`});return e}function je(n,t){let e=String(t??"").trim();if(!e)return !0;if(!/[*?]/.test(e))return n.includes(e);let s=`^${e.replace(/[$()*+.?[\\\]^{|}]/g,"\\$&").replace(/\\\*/g,".*").replace(/\\\?/g,".")}$`;try{return new RegExp(s).test(n)}catch{return n.includes(e)}}function Ce(n){let t=n,e=t?.name?String(t.name):"",r=t?.code?String(t.code):"",o=t?.message?String(t.message):String(t??"");if(e==="ExitPromptError"||e==="AbortError"||e==="PromptAbortError"||r==="ABORT_ERR")return !0;let s=o.toLowerCase();return s.includes("cancelled")||s.includes("canceled")}async function Tt(){console.log(y.bold.blue(`
34
34
  \u{1F680} Jenkins CLI - Jenkins Deployment CLI
35
- `));let{config:t,jenkins:n}=await b(),[e,r]=await Promise.all([et(),Ze()]),o=()=>{console.log(y.yellow(`
36
- \u{1F44B} Cancelled by user`)),process.exit(130);},s,i=t.job,a={};try{process.prependOnceListener("SIGINT",o);let f=await Nt(t.configs,{projectRoot:t.projectRoot}),g=new Set(["branch","modes","mode","triggered_by"]);for(let S of f){let I=String(S?.name??"").trim();if(g.has(I))throw new d(`configs name "${I}" is reserved.`,{hint:"Use another name in your extra prompt config.",example:"name: env"})}let w={type:"list",name:"branch",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u5206\u652F:",choices:e,default:e.indexOf(r)},$=[],O=[],F=[];for(let S of f){let I=Number(S.offset);if(Number.isFinite(I)&&I<=-2){$.push(S);continue}if(I===-1){O.push(S);continue}F.push(S);}let C={};Object.assign(C,await oe.prompt($,C)),Object.assign(C,await oe.prompt([w],C)),Object.assign(C,await oe.prompt(O,C));let Qe=String(t.projectRoot??"").trim()||process.cwd(),G=await Ie(Qe,t.modes,C),K=[];if(Array.isArray(G))K=G.map(S=>String(S).trim()).filter(Boolean);else if(G!=null){let S=String(G).trim();S&&(K=[S]);}K.length===0&&Array.isArray(t.modes)&&(K=t.modes);let Zt={type:"checkbox",name:"modes",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u73AF\u5883:",choices:K,validate:S=>S.length===0?"You must select at least one environment":!0};Object.assign(C,await oe.prompt([Zt],C)),Object.assign(C,await oe.prompt(F,C)),s={branch:String(C.branch??""),modes:C.modes??[]},a=At(C,["branch","modes"]);let en=await Ie(Qe,t.job,C),Ge=String(en??"").trim();Ge&&(i=Ge);}catch(f){let g=f,w=g?.message?String(g.message):String(g),$=g?.name?String(g.name):"";($==="ExitPromptError"||w.toLowerCase().includes("cancelled")||w.toLowerCase().includes("canceled"))&&(console.log(y.yellow(`
37
- \u{1F44B} Cancelled by user`)),process.exit(0)),q(new d(`Prompt failed: ${w}`,{hint:$?`Error: ${$}`:""})),process.exit(1);}finally{process.off("SIGINT",o);}console.log();let l=!1,c=()=>{l||(l=!0,console.log());},u=await ce();u&&!("triggered_by"in a)&&(a.triggered_by=u);let m=s.modes.map(async f=>{let g=Qt(`Triggering build for ${y.yellow(f)}...`).start();try{let w=await de(n,i,{...a,branch:s.branch,mode:f});c(),g.succeed(`${y.green(f)} - Triggered! Queue: ${w}`);}catch(w){throw c(),g.fail(`${y.red(f)} - ${w.message}`),w}});try{await Promise.all(m);}catch{console.log(y.bold.red(`
35
+ `));let{config:n,jenkins:t}=await w(),[e,r]=await Promise.all([it(),ot()]),o=()=>{process.exit(130);},s,i=n.job,a={};try{process.prependOnceListener("SIGINT",o);let d=await xe(n.configs,{projectRoot:n.projectRoot}),g=new Set(["branch","modes","mode","triggered_by"]);for(let $ of d){let I=String($?.name??"").trim();if(g.has(I))throw new p(`configs name "${I}" is reserved.`,{hint:"Use another name in your extra prompt config.",example:"name: env"})}let b={type:"list",name:"branch",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u5206\u652F:",choices:e,default:e.indexOf(r)},v=[],L=[],R=[];for(let $ of d){let I=Number($.offset);if(Number.isFinite(I)&&I<=-2){v.push($);continue}if(I===-1){L.push($);continue}R.push($);}let C={};Object.assign(C,await oe.prompt(v,C)),Object.assign(C,await oe.prompt([b],C)),Object.assign(C,await oe.prompt(L,C));let Ze=String(n.projectRoot??"").trim()||process.cwd(),G=await O(Ze,n.modes,C),K=[];if(Array.isArray(G))K=G.map($=>String($).trim()).filter(Boolean);else if(G!=null){let $=String(G).trim();$&&(K=[$]);}K.length===0&&Array.isArray(n.modes)&&(K=n.modes);let fn={type:"checkbox",name:"modes",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u73AF\u5883:",choices:K,validate:$=>$.length===0?"You must select at least one environment":!0};Object.assign(C,await oe.prompt([fn],C)),Object.assign(C,await oe.prompt(R,C)),s={branch:String(C.branch??""),modes:C.modes??[]},a=Ut(C,["branch","modes"]);let pn=await O(Ze,n.job,C),et=String(pn??"").trim();et&&(i=et);}catch(d){let g=d,b=g?.message?String(g.message):String(g),v=g?.name?String(g.name):"";Ce(d)&&process.exit(0),q(new p(`Prompt failed: ${b}`,{hint:v?`Error: ${v}`:""})),process.exit(1);}finally{process.off("SIGINT",o);}console.log();let c=!1,l=()=>{c||(c=!0,console.log());},u=await le();u&&!("triggered_by"in a)&&(a.triggered_by=u);let f=s.modes.map(async d=>{let g=rn(`Triggering build for ${y.yellow(d)}...`).start();try{let b=await ge(t,i,{...a,branch:s.branch,mode:d});l(),g.succeed(`${y.green(d)} - Triggered! Queue: ${b}`);}catch(b){throw l(),g.fail(`${y.red(d)} - ${b.message}`),b}});try{await Promise.all(f);}catch{console.log(y.bold.red(`
38
36
  \u274C Some builds failed. Check the output above.
39
- `)),process.exit(1);}}function k(t,n){return (n??"").trim()||t}function A(t,n){let e=Number(t);if(!Number.isInteger(e)||e<0)throw new d(`${n} must be a non-negative integer.`,{hint:`Provide a valid ${n}.`,example:`jenkins-cli console ${n==="id"?"123":"last -t 100"}`});return e}function ye(t,n){let e=String(n??"").trim();if(!e)return !0;if(!/[*?]/.test(e))return t.includes(e);let s=`^${e.replace(/[$()*+.?[\\\]^{|}]/g,"\\$&").replace(/\\\*/g,".*").replace(/\\\?/g,".")}$`;try{return new RegExp(s).test(t)}catch{return t.includes(e)}}function Wt(t){let n=t.command("queue").description("Operations for the build queue");n.command("list").alias("ls").description("List items waiting in the build queue").option("-j, --job <job>","Show only queue items for the specified job").action(async e=>{let{config:r,jenkins:o}=await b(),s=e.job?k(r.job,e.job):void 0,i=await H(o,s);console.table((i??[]).map(a=>({id:a.id,job:a.task?.name,why:a.why})));}),n.command("cancel").description("Cancel a queued build item").argument("<id...>","Queue id(s)").action(async e=>{let{jenkins:r}=await b(),o=Array.isArray(e)?e:[String(e??"")],s=await Promise.all(o.map(async a=>{let l=A(String(a),"id");try{return await te(r,l),{id:l,ok:!0}}catch(c){return {id:l,ok:!1,message:String(c?.message??c)}}})),i=s.filter(a=>!a.ok);if(i.length===0){console.log(y.green(`Cancelled ${s.length} queue item(s).`));return}console.log(y.yellow(`Cancelled ${s.length-i.length} item(s), failed ${i.length} item(s).`));for(let a of i)console.log(y.red(`- ${a.id}: ${a.message??"Cancel failed"}`));});}function h(t){return {[Jn.inspect.custom]:()=>t}}function U(t,n){return String(t??"").padEnd(n," ")}function j(t){return Math.max(0,...t.map(n=>String(n??"").length))}function J(t,n){return h(U(t,n))}function v(t){let n=String(t??""),e=n.trim();if(!e)return h(y.gray("-"));let r=s=>h(s),o=e.toLowerCase();if(o.startsWith("http://")||o.startsWith("https://"))try{let i=(new URL(e).hostname??"").toLowerCase(),a=i==="localhost"||i==="127.0.0.1"||i==="::1",l=/^10\./.test(i)||/^192\.168\./.test(i)||/^127\./.test(i)||/^172\.(1[6-9]|2\d|3[0-1])\./.test(i);return r(a||l?y.cyanBright(n):y.hex("#4EA1FF")(n))}catch{return r(y.hex("#4EA1FF")(n))}return o.startsWith("ws://")||o.startsWith("wss://")?r(y.cyan(n)):o.startsWith("ssh://")||o.startsWith("git+ssh://")||o.startsWith("git@")?r(y.magenta(n)):o.startsWith("file://")?r(y.yellow(n)):o.startsWith("mailto:")?r(y.green(n)):o.startsWith("javascript:")||o.startsWith("data:")?r(y.red(n)):e.startsWith("/")||e.startsWith("./")||e.startsWith("../")?r(y.cyan(n)):/^[a-z0-9.-]+(?::\d+)?(\/|$)/i.test(e)?r(y.blue(n)):r(n)}function P(t){let n=new Date(t),e=r=>String(r).padStart(2,"0");return `${n.getFullYear()}-${e(n.getMonth()+1)}-${e(n.getDate())} ${e(n.getHours())}:${e(n.getMinutes())}:${e(n.getSeconds())}`}function ie(t,n){if(n===!0)return h(y.cyan("BUILDING"));let e=String(t??"").toUpperCase();return h(e?e==="SUCCESS"?y.green(e):e==="ABORTED"?y.gray(e):e==="FAILURE"?y.red(e):e==="UNSTABLE"?y.yellow(e):e==="NOT_BUILT"?y.gray(e):e:"-")}function se(t){let n=String(t??"");if(!n)return h("-");let e=n.endsWith("_anime"),r=e?n.slice(0,-6):n,o=(()=>{switch(r){case"blue":return e?y.blueBright(n):y.blue(n);case"red":return e?y.redBright(n):y.red(n);case"yellow":return e?y.yellowBright(n):y.yellow(n);case"aborted":return y.gray(n);case"disabled":case"notbuilt":case"grey":case"gray":return y.gray(n);default:return n}})();return h(o)}function Et(t){let n=Number(t);if(!Number.isFinite(n)||n<0)return h("-");if(n<1024)return h(`${n} B`);let e=["KB","MB","GB","TB"],r=n,o=-1;for(;r>=1024&&o<e.length-1;)r/=1024,o+=1;let s=r>=10||o===0?r.toFixed(1):r.toFixed(2);return h(`${s} ${e[o]}`)}function ke(t){let n=process.argv,e=0;if(t){let r=n.lastIndexOf(t);r>=0&&(e=r+1);}for(let r=e;r<n.length;r+=1){let o=n[r];if(o==="-j"||o==="--job"){let s=n[r+1];return !s||s.startsWith("-")?"":s}}}function Ut(t){let n=Object.entries(t??{}).filter(([r])=>r);return n.length?(n.sort(([r],[o])=>r.localeCompare(o,"en",{sensitivity:"base"})),n.map(([r,o])=>{if(o===void 0)return `${r}=`;if(o===null)return `${r}=null`;if(typeof o=="string"||typeof o=="number"||typeof o=="boolean")return `${r}=${o}`;try{return `${r}=${JSON.stringify(o)}`}catch{return `${r}=${String(o)}`}}).join(", ")):"-"}function Ft(t){let n=t.command("builds").description("Build operations");n.option("-j, --job <job>","Specify job").option("-n, --limit <n>","Limit","20").action(async e=>{let{config:r,jenkins:o}=await b(),s=e.job??ke("builds")??ke(),i=k(r.job,s),l=(await pt(o,i,{limit:Number(e.limit)})).map(u=>({number:u.number,result:ie(u.result,u.building),building:u.building,"duration(s)":Number((Number(u.duration??0)/1e3).toFixed(3)),date:h(P(Number(u.timestamp??0))),parameters:Ut(u.parameters),url:v(String(u.url??""))})),c=j(l.map(u=>u.parameters));console.table(l.map(u=>({...u,parameters:J(u.parameters,c)})));}),n.command("active").description("List active builds").option("-j, --job <job>","Specify job").option("-a, --all","Query all running builds on Jenkins").action(async e=>{let{config:r,jenkins:o}=await b(),s=e.job??ke("active")??ke("builds"),a=((e.all?await fe(o):await ee(o,k(r.job,s)))??[]).map(c=>({...e.all?{job:h(String(c.job??""))}:{},number:c.number,date:h(c.timestamp?P(Number(c.timestamp)):"-"),parameters:Ut(c.parameters),url:v(String(c.url??""))})),l=j(a.map(c=>c.parameters));console.table(a.map(c=>({...c,parameters:J(c.parameters,l)})));});}function vn(t){let n=String(t?.authorEmail??"").trim(),e=n?n.split("@")[0]??"":"";return e||n}function Lt(t){let n=[];for(let e of t){let r=Number(e?.number),o=Number.isInteger(r)?`#${r}`:h("-"),s=e?.timestamp?P(Number(e.timestamp)):"-",i=e?.changeSet?.items??[];if(!i.length){n.push({build:o,date:h(s),author:h("-"),message:h("-")});continue}for(let a of i){let l=vn(a),c=String(a?.msg??"").trim();n.push({build:o,date:h(s),author:h(l||"-"),message:h(c||"-")});}}return n}function Tt(t){t.command("changes").description("List build changes").argument("[id]").option("-j, --job <job>","Specify job").option("-n, --limit <n>","Limit","20").option("--raw","Output raw changeSet payload").action(async(n,e)=>{let{config:r,jenkins:o}=await b(),s=k(r.job,e.job);if(n){let l=A(n,"id"),c=await he(o,s,l);if(e.raw){console.log(JSON.stringify({build:c.number,changeSet:c.changeSet??{}},null,2));return}console.table(Lt([c]));return}let i=Math.max(1,A(e.limit,"limit")),a=await ft(o,s,{limit:i});if(e.raw){console.log(JSON.stringify(a.map(l=>({build:l.number,changeSet:l.changeSet??{}})),null,2));return}console.table(Lt(a));});}function Vt(){let t=process.argv,n=t.findIndex(r=>r==="-j"||r==="--job");if(n<0)return;let e=t[n+1];return !e||e.startsWith("-")?"":e}function Ot(t){let n=t.command("console").description("Fetch build console log"),e=async(r,o,s,i)=>{if(!i.follow){let c=await dt(r,o,s);process.stdout.write(c);return}let a=0,l=!0;for(console.log(y.blue(`--- Following log for build #${s} ---`));;)try{let{text:c,newOffset:u,hasMoreData:p}=await $t(r,o,s,a);if(c.length>0&&(process.stdout.write(c),a=u),l&&c.length>0&&(l=!1),c.length>0&&p)continue;if(!p){let f=(await he(r,o,s)).result||"UNKNOWN",g=f==="SUCCESS"?y.green:y.red;console.log(g(`
40
- --- Build Finished: ${f} ---`));break}await new Promise(m=>setTimeout(m,1e3));}catch{await new Promise(c=>setTimeout(c,2e3));}};n.argument("<id>","Build number").option("-j, --job <job>","Specify job").option("--no-follow","Do not follow the build log").action(async(r,o)=>{let{config:s,jenkins:i}=await b(),a=o.job??Vt();if(a==="")throw new d("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli console 123 -j my-job"});let l=k(s.job,a),c=A(r,"id");await e(i,l,c,o);}),n.command("last").description("Fetch the latest build log").option("-j, --job <job>","Specify job").option("-s, --status <status>","success | failed | any","any").option("--no-follow","Do not follow the build log").action(async r=>{let{config:o,jenkins:s}=await b(),i=r.job??Vt();if(i==="")throw new d("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli console last -j my-job -s success"});let a=k(o.job,i),l=String(r.status??"any").toLowerCase();if(!["any","success","failed"].includes(l))throw new d(`Invalid status: ${r.status}.`,{hint:"Use success | failed | any.",example:"jenkins-cli console last -s success"});let c=await ge(s,a),u=l==="success"?c?.lastSuccessfulBuild:l==="failed"?c?.lastFailedBuild:c?.lastBuild,p=Number(u?.number);if(!p){let m=l==="success"?"successful":l==="failed"?"failed":"last";console.log(y.yellow(`No ${m} build found for job: ${a}. Try checking the job name or status filter.`));return}await e(s,a,p,r);});}function It(t){t.command("stop").description("Clear queued builds and stop running builds").argument("[id]","Build number").option("-j, --job <job>","Specify Jenkins job").option("-a, --all","Cancel queued items for this job and stop all running builds for it").option("-A, --ALL","Cancel all queued items and stop all running builds on Jenkins").action(async(n,e)=>{let{config:r,jenkins:o}=await b(),s=k(r.job,e.job);if(e.ALL){let[i,a]=await Promise.all([H(o),fe(o)]),l=(i??[]).map(u=>Number(u.id)).filter(u=>!Number.isNaN(u)),c=(a??[]).map(u=>({number:Number(u.number),job:String(u.job??"")})).filter(u=>u.job&&!Number.isNaN(u.number));for(let u of l)await te(o,u);for(let u of c)await ne(o,u.job,u.number);console.log(y.green(`Requested: cancelled ${l.length} queue item(s), stopped ${c.length} running build(s).`));return}if(e.all){let[i,a]=await Promise.all([H(o,s),ee(o,s)]),l=(i??[]).map(u=>Number(u.id)).filter(u=>!Number.isNaN(u)),c=(a??[]).map(u=>Number(u.number)).filter(u=>!Number.isNaN(u));for(let u of l)await te(o,u);for(let u of c)await ne(o,s,u);console.log(y.green(`Requested: cancelled ${l.length} queue item(s), stopped ${c.length} running build(s).`));return}if(!n){console.log(y.red("Missing build id. Provide <id> or use -a/--all."));return}await ne(o,s,A(n,"id")),console.log(y.green("Stop requested"));});}function qt(t){t.command("params").description("Show job parameter definitions").option("-j, --job <job>","Specify job").action(async n=>{let{config:e,jenkins:r}=await b(),o=k(e.job,n.job),s=await gt(r,o),i=j(s.map(a=>a?.name));console.table((s??[]).map(a=>({name:J(a?.name??"",i),type:h(String(a?.type??"")),description:h(String(a?.description??"")),default:h(a?.default===void 0?"-":String(a.default)),choices:h(Array.isArray(a?.choices)?a.choices.join(", "):a?.choices??"-")})));});}function Dt(t){t.command("me").description("Show Jenkins user info for the current token").option("--raw","Print raw response").action(async n=>{let{jenkins:e}=await b(),r=await ut(e);if(n.raw){console.log(r&&typeof r=="object"?JSON.stringify(r,null,2):String(r));return}let o=r&&typeof r=="object"?r:{value:r},s=(u,p)=>{let m=u.length;if(m>=p)return u;let f=p-m,g=Math.floor(f/2),w=f-g;return `${" ".repeat(g)}${u}${" ".repeat(w)}`},i=o.name??o.fullName??o.id??"",a=i==null?"":String(i),l=Math.max(20,a.length),c={};c.name=h(s(a,l));for(let u of Object.keys(o).sort((p,m)=>p.localeCompare(m,"en"))){if(u==="name")continue;let p=o[u],m=u.toLowerCase();if((m==="url"||m.endsWith("url")||m.includes("url"))&&(typeof p=="string"||typeof p=="number")){c[u]=v(String(p));continue}let g=p==null?"":typeof p=="object"?JSON.stringify(p):String(p);c[u]=h(g);}console.table([c]);});}function Mt(t){let n=t.command("config").description("Show Jenkins job configuration");n.command("show").description("Show job configuration").option("-j, --job <job>","Specify job").option("-f, --format <format>","Output format: xml or json","xml").action(async e=>{let{config:r,jenkins:o}=await b(),s=k(r.job,e.job),i=String(e.format??"xml").toLowerCase();if(i!=="xml"&&i!=="json")throw new d(`Invalid format: ${e.format}.`,{hint:"Use xml or json.",example:"jenkins-cli config show -f json"});let a=await Ve(o,s);if(i==="xml"){process.stdout.write(a);return}let c=new XMLParser({ignoreAttributes:!1,attributeNamePrefix:"@_"}).parse(a);console.log(JSON.stringify(c,null,2));}),n.command("backup").description("Backup job configuration to files").requiredOption("-d, --destination <path>","Destination folder").option("-a, --all","Backup all jobs").option("-j, --job <job>","Specify job").option("--filter <pattern>","Filter jobs by name (supports glob)").action(async e=>{let{config:r,jenkins:o}=await b(),s=String(e.destination??"").trim();if(!s)throw new d("Destination is required.",{hint:"Provide a destination folder with -d or --destination.",example:"jenkins-cli config backup -d ./jenkins-backup"});if(e.all&&e.filter)throw new d("Options --all and --filter are mutually exclusive.",{hint:"Use either -a/--all or --filter, not both.",example:"jenkins-cli config backup -d ./jenkins-backup -a"});let i=String(e.filter??"").trim(),a=e.all?await L(o):i?(await L(o)).filter(c=>ye(String(c.name??""),i)):[{name:k(r.job,e.job)}];if(a.length===0)throw new d(`No jobs matched filter: ${i}`,{hint:"Check the pattern or remove --filter to use the default job.",example:"jenkins-cli config backup -d ./jenkins-backup --filter 'remote-*'"});await promises.mkdir(s,{recursive:!0});let l=[];for(let c of a){let u=String(c?.name??"").trim();if(!u)continue;let p=await Ve(o,u),m=z.join(s,u,"config.xml");await promises.mkdir(z.dirname(m),{recursive:!0}),await promises.writeFile(m,p),l.push(m);}console.log(`Backup completed. ${l.length} file(s) saved to ${z.resolve(s)}`);});}function zt(t){let n=t.command("job").description("Job operations");n.command("list").alias("ls").description("List all jobs").option("--search <keyword>","Filter by name (supports glob)").action(async e=>{let{jenkins:r}=await b(),o=await L(r),s=(e.search??"").trim(),i=s?o.filter(c=>ye(String(c.name??""),s)):o;i.sort((c,u)=>String(c?.name??"").localeCompare(String(u?.name??""),"en",{sensitivity:"base"}));let a=j(i.map(c=>c?.name)),l=j(i.map(c=>c?.url));console.table((i??[]).map(c=>({name:J(c.name??"",a),color:se(c.color),url:v(U(c.url??"",l))})));}),n.command("info").description("Show job info").option("-j, --job <job>","Specify job").option("--json","Output raw JSON").action(async e=>{let{config:r,jenkins:o}=await b(),s=k(r.job,e.job),i=await ge(o,s);if(e.json){console.log(JSON.stringify(i,null,2));return}let a=i?.lastBuild,l=i?.lastCompletedBuild,c=Array.isArray(i?.healthReport)?i.healthReport:[];console.table([{name:h(String(i?.name??"")),url:v(String(i?.url??"")),color:se(i?.color),buildable:i?.buildable,inQueue:i?.inQueue,nextBuildNumber:i?.nextBuildNumber,healthScore:c[0]?.score??"-",lastBuild:h(a?.number?`#${a.number}`:"-"),lastResult:ie(a?.result,a?.building),"lastDuration(s)":a?.duration===void 0?"-":Number((Number(a.duration)/1e3).toFixed(3)),lastDate:h(a?.timestamp?P(Number(a.timestamp)):"-"),lastCompleted:h(l?.number?`#${l.number}`:"-"),lastCompletedResult:ie(l?.result,!1),lastCompletedDate:h(l?.timestamp?P(Number(l.timestamp)):"-")}]);});}function Nn(t){return t.split(",").map(n=>n.trim()).filter(Boolean)}function An(t){let n=t.split("=");if(n.length<2)throw new d(`Invalid --param: "${t}".`,{hint:"Use the format key=value.",example:"jenkins-cli build -b origin/main -m dev --param foo=bar"});let e=n[0].trim(),r=n.slice(1).join("=").trim();if(!e)throw new d(`Invalid --param: "${t}". Key is empty.`,{hint:'Provide a non-empty key before "=".',example:"jenkins-cli build -b origin/main -m dev --param foo=bar"});return {key:e,value:r}}function _t(t){t.command("build").description("Trigger Jenkins builds non-interactively").requiredOption("-b, --branch <branch>","Branch (e.g., origin/develop)").option("-m, --mode <mode>","Deployment environment (repeatable or comma-separated: -m dev -m uat / -m dev,uat)",(n,e)=>e.concat(Nn(n)),[]).option("--param <key=value>","Extra parameters (repeatable, e.g., --param foo=bar)",(n,e)=>e.concat(n),[]).option("-j, --job <job>","Specify job").action(async n=>{let{config:e,jenkins:r}=await b(),o=k(e.job,n.job),s=String(n.branch??"").trim();if(!s)throw new d("branch is required.",{hint:"Pass a branch with -b or --branch.",example:"jenkins-cli build -b origin/develop -m dev"});let i=(n.mode??[]).map(String).map(f=>f.trim()).filter(Boolean),a=Array.from(new Set(i));if(a.length===0)throw new d("mode is required.",{hint:"Pass at least one -m/--mode.",example:"jenkins-cli build -b origin/develop -m dev -m uat"});let l={};for(let f of n.param??[]){let{key:g,value:w}=An(String(f));l[g]=w;}let c=await ce();c&&!("triggered_by"in l)&&(l.triggered_by=c),console.log();let u=!1,p=()=>{u||(u=!0,console.log());},m=a.map(async f=>{let g=Qt(`Triggering build for ${y.yellow(f)}...`).start();try{let w=await de(r,o,{...l,branch:s,mode:f});p(),g.succeed(`${y.green(f)} - Triggered! Queue: ${w}`);}catch(w){throw p(),g.fail(`${y.red(f)} - ${w.message}`),w}});try{await Promise.all(m);}catch{console.log(y.bold.red(`
37
+ `)),process.exit(1);}}function Ot(n){let t=n.command("queue").description("Operations for the build queue");t.command("list").alias("ls").description("List items waiting in the build queue").option("-j, --job <job>","Show only queue items for the specified job").action(async e=>{let{config:r,jenkins:o}=await w(),s=e.job?await Lt(r.job,e.job,{projectRoot:r.projectRoot}):void 0,i=await H(o,s);console.table((i??[]).map(a=>({id:a.id,job:a.task?.name,why:a.why})));}),t.command("cancel").description("Cancel a queued build item").argument("<id...>","Queue id(s)").action(async e=>{let{jenkins:r}=await w(),o=Array.isArray(e)?e:[String(e??"")],s=await Promise.all(o.map(async a=>{let c=A(String(a),"id");try{return await te(r,c),{id:c,ok:!0}}catch(l){return {id:c,ok:!1,message:String(l?.message??l)}}})),i=s.filter(a=>!a.ok);if(i.length===0){console.log(y.green(`Cancelled ${s.length} queue item(s).`));return}console.log(y.yellow(`Cancelled ${s.length-i.length} item(s), failed ${i.length} item(s).`));for(let a of i)console.log(y.red(`- ${a.id}: ${a.message??"Cancel failed"}`));});}function h(n){return {[Tn.inspect.custom]:()=>n}}function U(n,t){return String(n??"").padEnd(t," ")}function j(n){return Math.max(0,...n.map(t=>String(t??"").length))}function S(n,t){return h(U(n,t))}function J(n){let t=String(n??""),e=t.trim();if(!e)return h(y.gray("-"));let r=s=>h(s),o=e.toLowerCase();if(o.startsWith("http://")||o.startsWith("https://"))try{let i=(new URL(e).hostname??"").toLowerCase(),a=i==="localhost"||i==="127.0.0.1"||i==="::1",c=/^10\./.test(i)||/^192\.168\./.test(i)||/^127\./.test(i)||/^172\.(1[6-9]|2\d|3[0-1])\./.test(i);return r(a||c?y.cyanBright(t):y.hex("#4EA1FF")(t))}catch{return r(y.hex("#4EA1FF")(t))}return o.startsWith("ws://")||o.startsWith("wss://")?r(y.cyan(t)):o.startsWith("ssh://")||o.startsWith("git+ssh://")||o.startsWith("git@")?r(y.magenta(t)):o.startsWith("file://")?r(y.yellow(t)):o.startsWith("mailto:")?r(y.green(t)):o.startsWith("javascript:")||o.startsWith("data:")?r(y.red(t)):e.startsWith("/")||e.startsWith("./")||e.startsWith("../")?r(y.cyan(t)):/^[a-z0-9.-]+(?::\d+)?(\/|$)/i.test(e)?r(y.blue(t)):r(t)}function P(n){let t=new Date(n),e=r=>String(r).padStart(2,"0");return `${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())} ${e(t.getHours())}:${e(t.getMinutes())}:${e(t.getSeconds())}`}function se(n,t){if(t===!0)return h(y.cyan("BUILDING"));let e=String(n??"").toUpperCase();return h(e?e==="SUCCESS"?y.green(e):e==="ABORTED"?y.gray(e):e==="FAILURE"?y.red(e):e==="UNSTABLE"?y.yellow(e):e==="NOT_BUILT"?y.gray(e):e:"-")}function ae(n){let t=String(n??"");if(!t)return h("-");let e=t.endsWith("_anime"),r=e?t.slice(0,-6):t,o=(()=>{switch(r){case"blue":return e?y.blueBright(t):y.blue(t);case"red":return e?y.redBright(t):y.red(t);case"yellow":return e?y.yellowBright(t):y.yellow(t);case"aborted":return y.gray(t);case"disabled":case"notbuilt":case"grey":case"gray":return y.gray(t);default:return t}})();return h(o)}function Vt(n){let t=Number(n);if(!Number.isFinite(t)||t<0)return h("-");if(t<1024)return h(`${t} B`);let e=["KB","MB","GB","TB"],r=t,o=-1;for(;r>=1024&&o<e.length-1;)r/=1024,o+=1;let s=r>=10||o===0?r.toFixed(1):r.toFixed(2);return h(`${s} ${e[o]}`)}var Se=64;function Vn(n){return n!=null&&typeof n=="object"?n.value??n.name??n:n}function qt(n){let t=String(n?.toString?.()??"").trim();if(!t)return "";let e=t.indexOf("=>");if(e>=0){let s=t.slice(e+2).trim();return s.startsWith("{")&&s.endsWith("}")&&(s=s.slice(1,-1)),s}let r=t.indexOf("{"),o=t.lastIndexOf("}");return r>=0&&o>r?t.slice(r+1,o):""}function Dt(n){let t=new Set,e,r=/\b(\d+)\b/g;for(;e=r.exec(n);){let s=parseInt(e[1],10);Number.isFinite(s)&&t.add(s);}let o=Array.from(t).sort((s,i)=>s-i);return o.length>Se?o.slice(0,Se):o}function In(n,t){let e=String(n.type??"").toLowerCase();if(e==="confirm")return [!0,!1];if(e==="number"){let s=n,i=typeof s.min=="number"&&Number.isFinite(s.min)?s.min:null,a=typeof s.max=="number"&&Number.isFinite(s.max)?s.max:null;if(i!=null&&a!=null)return i===a?[i]:i<a?[i,a]:[a,i];if(i!=null)return [i,i+1];if(a!=null)return [a-1,a];if(t){let c=Dt(qt(t));if(c.length)return c}return [0,1,99,100,101]}let r=Array.isArray(n.choices)?n.choices:null;if(!r?.length)return null;let o=r.map(Vn);if(e==="list"||e==="rawlist")return o;if(e==="checkbox"){let s=[[]];return o.forEach(i=>s.push([i])),o.length<=4&&s.push(o),s}return null}async function qn(n,t){let e=n.when;if(e===void 0||typeof e=="boolean")return e!==!1;if(typeof e!="function")return !0;try{return !!await Promise.resolve(e(t))}catch{return !0}}async function Dn(n,t,e){let r=await xe(n.configs,{projectRoot:t}),o=[{}];for(let s of r){let i=String(s.name??"").trim();if(!i)continue;let a=In(s,e);if(!a?.length||o.length*a.length>Se)continue;let c=[];for(let l of o){if(!await qn(s,l)){c.push(l);continue}for(let u of a)c.push({...l,[i]:u});}c.length&&(o=c);}return o}function Mn(n){let t=new Map,e=/answers\.(\w+)\s*===\s*(\d+)/g,r;for(;r=e.exec(n);){let s=r[1],i=parseInt(r[2],10);Number.isFinite(i)&&(t.has(s)||t.set(s,new Set),t.get(s).add(i));}let o=new Map;return t.forEach((s,i)=>o.set(i,Array.from(s).sort((a,c)=>a-c))),o}function zn(n){return n.split(`
38
+ `).filter(t=>{let e=t.trim();return e&&!e.startsWith("//")}).join(`
39
+ `)}function _n(n){let t=qt(n);if(!t)return [];t=zn(t);let e=Dt(t),r=Mn(t),o=new Set,s=/return\s+([^;]+?)(?=;|$)/gm,i=/`((?:[^`\\]|\\.|\$\{[^}]*\})*)`/g,a=/\$\{\s*answers\.(\w+)\s*\}/,c;for(;c=s.exec(t);){let l=c[1];for(let m of It(l))m&&o.add(m);let u;for(;u=i.exec(l);){let m=u[1];if(!m.includes("${"))continue;let f=m.match(a),d=f&&r.has(f[1])?r.get(f[1]):f?[]:e,g=m.split(/\$\{[^}]*\}/);if(g.length<2)continue;let b=g[0]??"",v=g.slice(1).join("");for(let L of d){let R=b+String(L)+v;R&&o.add(R);}}}if(o.size===0&&!t.includes("{"))for(let l of It(t))l&&o.add(l);return Array.from(o).sort((l,u)=>l.localeCompare(u,"en",{sensitivity:"base"}))}function It(n){let t=[],e=/(['"`])((?:\\.|(?!\1).)*)\1/g,r;for(;r=e.exec(n);)if(!(r[1]==="`"&&r[2].includes("${")))try{let o=r[1]==="`"?r[2]:JSON.parse(r[1]+r[2]+r[1]);String(o).trim()&&t.push(String(o).trim());}catch{String(r[2]??"").trim()&&t.push(String(r[2]).trim());}return t}async function Hn(n,t,e){let r=new Set;for(let i of _n(n))r.add(i);let o=await Dn(t,e,n),s=async i=>{let a=await Promise.resolve(n(i)),c=typeof a=="string"?String(a).trim():"";return c&&r.add(c),r.size>=Se};if(!o.length)await s({});else for(let i of o)if(await s(i))break;return Array.from(r).sort((i,a)=>i.localeCompare(a,"en",{sensitivity:"base"}))}async function k(n,t){if(t==="")throw new p("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli job info -j my-job"});let e=String(t??"").trim();if(e)return e;let r=String(n.projectRoot??"").trim()||process.cwd(),o=await Wt(r,n.job);if(o.isRef&&o.isFunction){let i=await Hn(o.value,n,r);if(!i.length)throw new p("Job choices not found from config reference.",{hint:"Ensure the job function returns a string (job name).",example:'export function dynamicJob(answers) { return "my-job" }'});if(i.length===1)return i[0];let{job:a}=await oe.prompt([{type:"list",name:"job",message:"\u8BF7\u9009\u62E9\u8981\u64CD\u4F5C\u7684 Job:",choices:i}]);return String(a??"").trim()}let s=await O(r,n.job,{});return String(s??n.job??"").trim()||String(n.job??"")}function ve(n){let t=process.argv,e=0;if(n){let r=t.lastIndexOf(n);r>=0&&(e=r+1);}for(let r=e;r<t.length;r+=1){let o=t[r];if(o==="-j"||o==="--job"){let s=t[r+1];return !s||s.startsWith("-")?"":s}}}function Mt(n){let t=Object.entries(n??{}).filter(([r])=>r);return t.length?(t.sort(([r],[o])=>r.localeCompare(o,"en",{sensitivity:"base"})),t.map(([r,o])=>{if(o===void 0)return `${r}=`;if(o===null)return `${r}=null`;if(typeof o=="string"||typeof o=="number"||typeof o=="boolean")return `${r}=${o}`;try{return `${r}=${JSON.stringify(o)}`}catch{return `${r}=${String(o)}`}}).join(", ")):"-"}function zt(n){let t=n.command("builds").description("Build operations");t.option("-j, --job <job>","Specify job").option("-n, --limit <n>","Limit","20").action(async e=>{let{config:r,jenkins:o}=await w(),s=e.job??ve("builds")??ve(),i=await k(r,s),c=(await wt(o,i,{limit:Number(e.limit)})).map(u=>({number:u.number,result:se(u.result,u.building),building:u.building,"duration(s)":Number((Number(u.duration??0)/1e3).toFixed(3)),date:h(P(Number(u.timestamp??0))),parameters:Mt(u.parameters),url:J(String(u.url??""))})),l=j(c.map(u=>u.parameters));console.table(c.map(u=>({...u,parameters:S(u.parameters,l)})));}),t.command("active").description("List active builds").option("-j, --job <job>","Specify job").option("-a, --all","Query all running builds on Jenkins").action(async e=>{let{config:r,jenkins:o}=await w(),s=e.job??ve("active")??ve("builds"),i=await k(r,s),c=((e.all?await de(o):await ee(o,i))??[]).map(u=>({...e.all?{job:h(String(u.job??""))}:{},number:u.number,date:h(u.timestamp?P(Number(u.timestamp)):"-"),parameters:Mt(u.parameters),url:J(String(u.url??""))})),l=j(c.map(u=>u.parameters));console.table(c.map(u=>({...u,parameters:S(u.parameters,l)})));});}function Qn(n){let t=String(n?.authorEmail??"").trim(),e=t?t.split("@")[0]??"":"";return e||t}function _t(n){let t=[];for(let e of n){let r=Number(e?.number),o=Number.isInteger(r)?`#${r}`:h("-"),s=e?.timestamp?P(Number(e.timestamp)):"-",i=e?.changeSet?.items??[];if(!i.length){t.push({build:o,date:h(s),author:h("-"),message:h("-")});continue}for(let a of i){let c=Qn(a),l=String(a?.msg??"").trim();t.push({build:o,date:h(s),author:h(c||"-"),message:h(l||"-")});}}return t}function Ht(n){n.command("changes").description("List build changes").argument("[id]").option("-j, --job <job>","Specify job").option("-n, --limit <n>","Limit","20").option("--raw","Output raw changeSet payload").action(async(t,e)=>{let{config:r,jenkins:o}=await w(),s=await k(r,e.job);if(t){let c=A(t,"id"),l=await we(o,s,c);if(e.raw){console.log(JSON.stringify({build:l.number,changeSet:l.changeSet??{}},null,2));return}console.table(_t([l]));return}let i=Math.max(1,A(e.limit,"limit")),a=await bt(o,s,{limit:i});if(e.raw){console.log(JSON.stringify(a.map(c=>({build:c.number,changeSet:c.changeSet??{}})),null,2));return}console.table(_t(a));});}function Qt(){let n=process.argv,t=n.findIndex(r=>r==="-j"||r==="--job");if(t<0)return;let e=n[t+1];return !e||e.startsWith("-")?"":e}function Gt(n){let t=n.command("console").description("Fetch build console log"),e=async(r,o,s,i)=>{if(!i.follow){let l=await yt(r,o,s);process.stdout.write(l);return}let a=0,c=!0;for(console.log(y.blue(`--- Following log for ${o} build #${s} ---`));;)try{let{text:l,newOffset:u,hasMoreData:m}=await Nt(r,o,s,a);if(l.length>0&&(process.stdout.write(l),a=u),c&&l.length>0&&(c=!1),l.length>0&&m)continue;if(!m){let d=(await we(r,o,s)).result||"UNKNOWN",g=d==="SUCCESS"?y.green:y.red;console.log(g(`
40
+ --- Build Finished: ${d} ---`));break}await new Promise(f=>setTimeout(f,1e3));}catch{await new Promise(l=>setTimeout(l,2e3));}};t.argument("<id>","Build number").option("-j, --job <job>","Specify job").option("--no-follow","Do not follow the build log").action(async(r,o)=>{let{config:s,jenkins:i}=await w(),a=o.job??Qt();if(a==="")throw new p("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli console 123 -j my-job"});let c=await k(s,a),l=A(r,"id");await e(i,c,l,o);}),t.command("last").description("Fetch the latest build log").option("-j, --job <job>","Specify job").option("-s, --status <status>","success | failed | any","any").option("--no-follow","Do not follow the build log").action(async r=>{let{config:o,jenkins:s}=await w(),i=r.job??Qt();if(i==="")throw new p("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli console last -j my-job -s success"});let a=await k(o,i),c=String(r.status??"any").toLowerCase();if(!["any","success","failed"].includes(c))throw new p(`Invalid status: ${r.status}.`,{hint:"Use success | failed | any.",example:"jenkins-cli console last -s success"});let l=await he(s,a),u=c==="success"?l?.lastSuccessfulBuild:c==="failed"?l?.lastFailedBuild:l?.lastBuild,m=Number(u?.number);if(!m){let f=c==="success"?"successful":c==="failed"?"failed":"last";console.log(y.yellow(`No ${f} build found for job: ${a}. Try checking the job name or status filter.`));return}await e(s,a,m,r);});}function Kt(n){n.command("stop").description("Clear queued builds and stop running builds").argument("[id]","Build number").option("-j, --job <job>","Specify Jenkins job").option("-a, --all","Cancel queued items for this job and stop all running builds for it").option("-A, --ALL","Cancel all queued items and stop all running builds on Jenkins").action(async(t,e)=>{let{config:r,jenkins:o}=await w(),s=await k(r,e.job);if(e.ALL){let[i,a]=await Promise.all([H(o),de(o)]),c=(i??[]).map(u=>Number(u.id)).filter(u=>!Number.isNaN(u)),l=(a??[]).map(u=>({number:Number(u.number),job:String(u.job??"")})).filter(u=>u.job&&!Number.isNaN(u.number));for(let u of c)await te(o,u);for(let u of l)await ne(o,u.job,u.number);console.log(y.green(`Requested: cancelled ${c.length} queue item(s), stopped ${l.length} running build(s).`));return}if(e.all){let[i,a]=await Promise.all([H(o,s),ee(o,s)]),c=(i??[]).map(u=>Number(u.id)).filter(u=>!Number.isNaN(u)),l=(a??[]).map(u=>Number(u.number)).filter(u=>!Number.isNaN(u));for(let u of c)await te(o,u);for(let u of l)await ne(o,s,u);console.log(y.green(`Requested: cancelled ${c.length} queue item(s), stopped ${l.length} running build(s).`));return}if(!t){console.log(y.red("Missing build id. Provide <id> or use -a/--all."));return}await ne(o,s,A(t,"id")),console.log(y.green("Stop requested"));});}function Yt(n){n.command("params").description("Show job parameter definitions").option("-j, --job <job>","Specify job").action(async t=>{let{config:e,jenkins:r}=await w(),o=await k(e,t.job),s=await kt(r,o),i=j(s.map(a=>a?.name));console.table((s??[]).map(a=>({name:S(a?.name??"",i),type:h(String(a?.type??"")),description:h(String(a?.description??"")),default:h(a?.default===void 0?"-":String(a.default)),choices:h(Array.isArray(a?.choices)?a.choices.join(", "):a?.choices??"-")})));});}function Xt(n){n.command("me").description("Show Jenkins user info for the current token").option("--raw","Print raw response").action(async t=>{let{jenkins:e}=await w(),r=await gt(e);if(t.raw){console.log(r&&typeof r=="object"?JSON.stringify(r,null,2):String(r));return}let o=r&&typeof r=="object"?r:{value:r},s=(u,m)=>{let f=u.length;if(f>=m)return u;let d=m-f,g=Math.floor(d/2),b=d-g;return `${" ".repeat(g)}${u}${" ".repeat(b)}`},i=o.name??o.fullName??o.id??"",a=i==null?"":String(i),c=Math.max(20,a.length),l={};l.name=h(s(a,c));for(let u of Object.keys(o).sort((m,f)=>m.localeCompare(f,"en"))){if(u==="name")continue;let m=o[u],f=u.toLowerCase();if((f==="url"||f.endsWith("url")||f.includes("url"))&&(typeof m=="string"||typeof m=="number")){l[u]=J(String(m));continue}let g=m==null?"":typeof m=="object"?JSON.stringify(m):String(m);l[u]=h(g);}console.table([l]);});}function Zt(n){let t=n.command("config").description("Show Jenkins job configuration");t.command("show").description("Show job configuration").option("-j, --job <job>","Specify job").option("-f, --format <format>","Output format: xml or json","xml").action(async e=>{let{config:r,jenkins:o}=await w(),s=await k(r,e.job),i=String(e.format??"xml").toLowerCase();if(i!=="xml"&&i!=="json")throw new p(`Invalid format: ${e.format}.`,{hint:"Use xml or json.",example:"jenkins-cli config show -f json"});let a=await ze(o,s);if(i==="xml"){process.stdout.write(a);return}let l=new XMLParser({ignoreAttributes:!1,attributeNamePrefix:"@_"}).parse(a);console.log(JSON.stringify(l,null,2));}),t.command("backup").description("Backup job configuration to files").requiredOption("-d, --destination <path>","Destination folder").option("-a, --all","Backup all jobs").option("-j, --job <job>","Specify job").option("--filter <pattern>","Filter jobs by name (supports glob)").action(async e=>{let{config:r,jenkins:o}=await w(),s=String(e.destination??"").trim();if(!s)throw new p("Destination is required.",{hint:"Provide a destination folder with -d or --destination.",example:"jenkins-cli config backup -d ./jenkins-backup"});if(e.all&&e.filter)throw new p("Options --all and --filter are mutually exclusive.",{hint:"Use either -a/--all or --filter, not both.",example:"jenkins-cli config backup -d ./jenkins-backup -a"});let i=String(e.filter??"").trim(),a=e.all?await T(o):i?(await T(o)).filter(l=>je(String(l.name??""),i)):[{name:await k(r,e.job)}];if(a.length===0)throw new p(`No jobs matched filter: ${i}`,{hint:"Check the pattern or remove --filter to use the default job.",example:"jenkins-cli config backup -d ./jenkins-backup --filter 'remote-*'"});await promises.mkdir(s,{recursive:!0});let c=[];for(let l of a){let u=String(l?.name??"").trim();if(!u)continue;let m=await ze(o,u),f=z.join(s,u,"config.xml");await promises.mkdir(z.dirname(f),{recursive:!0}),await promises.writeFile(f,m),c.push(f);}console.log(`Backup completed. ${c.length} file(s) saved to ${z.resolve(s)}`);});}function en(n){let t=n.command("job").description("Job operations");t.command("list").alias("ls").description("List all jobs").option("--search <keyword>","Filter by name (supports glob)").action(async e=>{let{jenkins:r}=await w(),o=await T(r),s=(e.search??"").trim(),i=s?o.filter(l=>je(String(l.name??""),s)):o;i.sort((l,u)=>String(l?.name??"").localeCompare(String(u?.name??""),"en",{sensitivity:"base"}));let a=j(i.map(l=>l?.name)),c=j(i.map(l=>l?.url));console.table((i??[]).map(l=>({name:S(l.name??"",a),color:ae(l.color),url:J(U(l.url??"",c))})));}),t.command("info").description("Show job info").option("-j, --job <job>","Specify job").option("--json","Output raw JSON").action(async e=>{let{config:r,jenkins:o}=await w(),s=await k(r,e.job),i=await he(o,s);if(e.json){console.log(JSON.stringify(i,null,2));return}let a=i?.lastBuild,c=i?.lastCompletedBuild,l=Array.isArray(i?.healthReport)?i.healthReport:[];console.table([{name:h(String(i?.name??"")),url:J(String(i?.url??"")),color:ae(i?.color),buildable:i?.buildable,inQueue:i?.inQueue,nextBuildNumber:i?.nextBuildNumber,healthScore:l[0]?.score??"-",lastBuild:h(a?.number?`#${a.number}`:"-"),lastResult:se(a?.result,a?.building),"lastDuration(s)":a?.duration===void 0?"-":Number((Number(a.duration)/1e3).toFixed(3)),lastDate:h(a?.timestamp?P(Number(a.timestamp)):"-"),lastCompleted:h(c?.number?`#${c.number}`:"-"),lastCompletedResult:se(c?.result,!1),lastCompletedDate:h(c?.timestamp?P(Number(c.timestamp)):"-")}]);});}function Yn(n){return n.split(",").map(t=>t.trim()).filter(Boolean)}function Xn(n){let t=n.split("=");if(t.length<2)throw new p(`Invalid --param: "${n}".`,{hint:"Use the format key=value.",example:"jenkins-cli build -b origin/main -m dev --param foo=bar"});let e=t[0].trim(),r=t.slice(1).join("=").trim();if(!e)throw new p(`Invalid --param: "${n}". Key is empty.`,{hint:'Provide a non-empty key before "=".',example:"jenkins-cli build -b origin/main -m dev --param foo=bar"});return {key:e,value:r}}function tn(n){n.command("build").description("Trigger Jenkins builds non-interactively").requiredOption("-b, --branch <branch>","Branch (e.g., origin/develop)").option("-m, --mode <mode>","Deployment environment (repeatable or comma-separated: -m dev -m uat / -m dev,uat)",(t,e)=>e.concat(Yn(t)),[]).option("--param <key=value>","Extra parameters (repeatable, e.g., --param foo=bar)",(t,e)=>e.concat(t),[]).option("-j, --job <job>","Specify job").action(async t=>{let{config:e,jenkins:r}=await w(),o=await k(e,t.job),s=String(t.branch??"").trim();if(!s)throw new p("branch is required.",{hint:"Pass a branch with -b or --branch.",example:"jenkins-cli build -b origin/develop -m dev"});let i=(t.mode??[]).map(String).map(d=>d.trim()).filter(Boolean),a=Array.from(new Set(i));if(a.length===0)throw new p("mode is required.",{hint:"Pass at least one -m/--mode.",example:"jenkins-cli build -b origin/develop -m dev -m uat"});let c={};for(let d of t.param??[]){let{key:g,value:b}=Xn(String(d));c[g]=b;}let l=await le();l&&!("triggered_by"in c)&&(c.triggered_by=l),console.log();let u=!1,m=()=>{u||(u=!0,console.log());},f=a.map(async d=>{let g=rn(`Triggering build for ${y.yellow(d)}...`).start();try{let b=await ge(r,o,{...c,branch:s,mode:d});m(),g.succeed(`${y.green(d)} - Triggered! Queue: ${b}`);}catch(b){throw m(),g.fail(`${y.red(d)} - ${b.message}`),b}});try{await Promise.all(f);}catch{console.log(y.bold.red(`
41
41
  \u274C Some builds failed. Check the output above.
42
- `)),process.exit(1);}});}function Ht(t){let n=t.command("view").description("View operations");n.command("list").alias("ls").description("List all views").option("--raw","Output raw JSON").action(async e=>{let{jenkins:r}=await b(),o=await re(r,{raw:e.raw});if(e.raw){console.log(JSON.stringify(o,null,2));return}o.sort((l,c)=>String(l?.name??"").localeCompare(String(c?.name??""),"en",{sensitivity:"base"}));let s=(o??[]).map(l=>({name:String(l?.name??""),url:String(l?.url??"")})),i=j(s.map(l=>l.name)),a=j(s.map(l=>l.url));console.table(s.map(l=>({name:J(l.name,i),url:v(U(l.url,a))})));}),n.command("show").description("Show jobs in a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await b(),o=await Q(r,e);if(!o.length){console.log("No jobs found in this view.");return}o.sort((a,l)=>String(a?.name??"").localeCompare(String(l?.name??""),"en",{sensitivity:"base"}));let s=j(o.map(a=>a?.name)),i=j(o.map(a=>a?.url));console.table(o.map(a=>({name:J(a.name??"",s),color:se(a.color),url:v(U(a.url??"",i))})));}),n.command("add").description("Add a job to a view").argument("<view>","View name").argument("<job>","Job name").action(async(e,r)=>{let{jenkins:o}=await b();if((await Q(o,e)).some(i=>i.name===r)){console.log(y.yellow(`Job "${r}" is already in view "${e}".`));return}await Fe(o,e,r),console.log(y.green(`Job "${r}" added to view "${e}".`));}),n.command("remove").alias("rm").description("Remove a job from a view").argument("<view>","View name").argument("<job>","Job name").action(async(e,r)=>{let{jenkins:o}=await b();if(!(await Q(o,e)).some(i=>i.name===r))throw new d(`Job "${r}" is not in view "${e}".`,{hint:'Use "jc view show <name>" to see jobs in this view.'});await Le(o,e,r),console.log(y.green(`Job "${r}" removed from view "${e}".`));}),n.command("edit").description("Edit jobs in a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await b(),[o,s]=await Promise.all([L(r),Q(r,e)]);if(!o.length){console.log("No jobs found.");return}o.sort((m,f)=>String(m?.name??"").localeCompare(String(f?.name??""),"en",{sensitivity:"base"}));let i=new Set(s.map(m=>String(m?.name??"").trim())),a=o.map(m=>String(m?.name??"").trim()).filter(Boolean).map(m=>({name:m,checked:i.has(m)})),l=await oe.prompt([{type:"checkbox",name:"jobs",message:`Select jobs for view "${e}"`,choices:a,pageSize:20}]),c=new Set((l.jobs??[]).map(m=>String(m).trim())),u=[...c].filter(m=>!i.has(m)),p=[...i].filter(m=>m&&!c.has(m));if(!u.length&&!p.length){console.log(y.yellow("No changes to apply."));return}for(let m of u)await Fe(r,e,m);for(let m of p)await Le(r,e,m);console.log(y.green(`View "${e}" updated.`));}),n.command("create").description("Create a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await b();await ct(r,e),console.log(y.green(`View "${e}" created.`));}),n.command("delete").alias("del").description("Delete a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await b();await lt(r,e),console.log(y.green(`View "${e}" deleted.`));});}function Fn(t){return h(t?P(t):"-")}function $e(t){let n=process.argv,e=0;if(t){let r=n.lastIndexOf(t);r>=0&&(e=r+1);}for(let r=e;r<n.length;r+=1){let o=n[r];if(o==="-j"||o==="--job"){let s=n[r+1];return !s||s.startsWith("-")?"":s}}}function ze(t,n,e){let r=n?.parent?.opts()?.job,o=$e(e)??$e(n?.name())??$e("ws")??$e("workspace");return t??r??o}function _e(t,n){if(n==="")throw new d("Job is required.",{hint:"Provide a job name after -j/--job.",example:"jenkins-cli ws -j my-job"});return k(t,n)}function Gt(t){return t.replace(/^\/+/,"").replace(/\/+$/,"")}function Ln(t,n){let e=Gt(t),r=Gt(n);return e?r?r===e||r.startsWith(`${e}/`)?r:`${e}/${r}`:e:r||""}function Tn(t){let n=process.platform,e="",r=[];n==="darwin"?(e="open",r=[t]):n==="win32"?(e="cmd",r=["/c","start","",t]):(e="xdg-open",r=[t]);try{spawn(e,r,{stdio:"ignore",detached:!0}).unref();}catch{}}async function R(t){try{return await t()}catch(n){if(n?.response?.status===404)return null;throw n}}async function Vn(t,n){try{return await mt(t,n)}catch{return}}async function On(t,n){try{return await Jt(t,n)}catch{return}}async function In(t){try{return await re(t,{raw:!1})}catch{return []}}async function He(t,n,e){let r=await e.direct(n);if(r!==null)return r;let o=await Vn(t,n);if(o&&o!==n){let a=await e.direct(o);if(a!==null)return a}if(e.beforeView){let a=await e.beforeView();if(a!==null)return a}let s=await On(t,n);if(s){let a=await e.byViewName(s);if(a!==null)return a}let i=await In(t);for(let a of i){let l=String(a?.url??"").trim();if(!l)continue;let c=await e.byViewUrl(l);if(c!==null)return c}return null}async function qn(t,n,e){return await He(t,n,{direct:r=>R(()=>Te(t,r,{path:e})),byViewName:r=>R(()=>ht(t,r,n,{path:e})),byViewUrl:r=>R(()=>bt(t,r,n,{path:e})),beforeView:async()=>{if(e)return null;let r=n.split("/").pop();return r?await R(()=>Te(t,n,{path:r})):null}})}async function Dn(t,n,e){return await He(t,n,{direct:r=>R(()=>wt(t,r,e)),byViewName:r=>R(()=>kt(t,r,n,e)),byViewUrl:r=>R(()=>jt(t,r,n,e))})}async function Mn(t,n,e){return await He(t,n,{direct:r=>R(()=>yt(t,r,e)),byViewName:r=>R(()=>xt(t,r,n,e)),byViewUrl:r=>R(()=>Ct(t,r,n,e))})}function Kt(t){let n=t.command("workspace").alias("ws").description("Workspace operations");n.option("-j, --job <job>","Specify job").option("-p, --path <path>","Workspace sub path").action(async(e,r)=>{let{config:o,jenkins:s}=await b(),i=ze(e.job,r,"workspace"),a=_e(o.job,i),l=String(e.path??"").trim(),c=Qt(`Loading workspace for job "${a}"...`).start(),u=await qn(s,a,l);if(!u){c.warn(`Workspace not found for job: ${a}. The job may be inside a folder, not run yet, or workspace is disabled.`);return}if(c.succeed(`Workspace loaded for job: ${a}`),!u.length){console.table([{name:"-",type:"-",size:"-",modified:"-",path:"-",url:"-"}]);return}let p=u.map(g=>({name:String(g.name??""),typeRaw:g.type,size:Et(g.size),modified:Fn(g.modified),path:Ln(l,String(g.path??g.name??""))})),m=j(p.map(g=>g.name)),f=j(p.map(g=>g.path));console.table(p.map(g=>{let w=U(String(g.name??""),m),$=String(g.typeRaw??"");return {name:$==="dir"?h(y.bold.cyan(w)):$==="file"?h(y.green(w)):h(y.gray(w)),size:g.size,modified:g.modified,path:J(String(g.path??""),f)}}));}),n.command("cat").description("Show file content in workspace").argument("<path>","Workspace file path").option("-j, --job <job>","Specify job").option("-o, --out <file>","Save file to path").option("--open","Open file with default application").action(async(e,r,o)=>{let{config:s,jenkins:i}=await b(),a=ze(r.job,o,"cat"),l=_e(s.job,a),c=String(e??"").trim();if(!c)throw new d("Path is required.",{hint:'Provide a file path after "cat".',example:"jenkins-cli ws cat dist/index.html"});let u=String(r.out??"").trim(),p=r.open===!0;if(u||p){let f=await Mn(i,l,c);if(f===null){console.log(y.yellow(`Workspace file not found: ${c} (job: ${l}).`));return}let g=u||z.join(En.tmpdir(),z.basename(c)||"file");try{u&&await M.pathExists(u)&&(await M.stat(u)).isDirectory()&&(g=z.join(u,z.basename(c)||"file"));}catch{}await M.ensureDir(z.dirname(g)),await M.writeFile(g,f.data),p?(Tn(g),console.log(y.green(`Opened: ${g}`))):console.log(y.green(`Saved to: ${g}`));return}let m=await Dn(i,l,c);if(m===null){console.log(y.yellow(`Workspace file not found: ${c} (job: ${l}).`));return}process.stdout.write(String(m));}),n.command("wipe").description("Wipe out current workspace").option("-j, --job <job>","Specify job").action(async(e,r)=>{let{config:o,jenkins:s}=await b(),i=ze(e.job,r,"wipe"),a=_e(o.job,i);try{if(!(await oe.prompt([{type:"confirm",name:"confirm",message:`Wipe workspace for job "${a}"? This cannot be undone.`,default:!1}]))?.confirm){console.log(y.yellow("Cancelled."));return}}catch(c){let u=c?.message?String(c.message):String(c);if((c?.name?String(c.name):"")==="ExitPromptError"||u.toLowerCase().includes("cancelled")||u.toLowerCase().includes("canceled")){console.log(y.yellow("Cancelled."));return}throw c}let l=Qt(`Wiping workspace for job "${a}"...`).start();try{await St(s,a),l.succeed(`Workspace wiped for job: ${a}`);}catch(c){throw l.fail(`Failed to wipe workspace for job: ${a}`),c}});}function Yt(t){Ft(t),Tt(t),Mt(t),zt(t),Ot(t),qt(t),Wt(t),It(t),_t(t),Dt(t),Ht(t),Kt(t);}function Xt(t,n,e){let r=s=>s.options.some(i=>i.long===n||i.short===n),o=s=>{r(s)||s.option(n,e);for(let i of s.commands)o(i);};for(let s of t.commands)o(s);}program.name("jenkins-cli").description(Xe).version(Ye,"-v, --version").helpOption("-h, --help").option("--debug","Print Jenkins request params").allowExcessArguments(!1).configureHelp({sortSubcommands:!0,sortOptions:!0}).action(Bt);Yt(program);Xt(program,"--debug","Print Jenkins request params");async function zn(){try{await program.parseAsync(process.argv);}catch(t){q(t);}}zn();
42
+ `)),process.exit(1);}});}function nn(n){let t=n.command("view").description("View operations");t.command("list").alias("ls").description("List all views").option("--raw","Output raw JSON").action(async e=>{let{jenkins:r}=await w(),o=await re(r,{raw:e.raw});if(e.raw){console.log(JSON.stringify(o,null,2));return}o.sort((c,l)=>String(c?.name??"").localeCompare(String(l?.name??""),"en",{sensitivity:"base"}));let s=(o??[]).map(c=>({name:String(c?.name??""),url:String(c?.url??"")})),i=j(s.map(c=>c.name)),a=j(s.map(c=>c.url));console.table(s.map(c=>({name:S(c.name,i),url:J(U(c.url,a))})));}),t.command("show").description("Show jobs in a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await w(),o=await Q(r,e);if(!o.length){console.log("No jobs found in this view.");return}o.sort((a,c)=>String(a?.name??"").localeCompare(String(c?.name??""),"en",{sensitivity:"base"}));let s=j(o.map(a=>a?.name)),i=j(o.map(a=>a?.url));console.table(o.map(a=>({name:S(a.name??"",s),color:ae(a.color),url:J(U(a.url??"",i))})));}),t.command("add").description("Add a job to a view").argument("<view>","View name").argument("<job>","Job name").action(async(e,r)=>{let{jenkins:o}=await w();if((await Q(o,e)).some(i=>i.name===r)){console.log(y.yellow(`Job "${r}" is already in view "${e}".`));return}await Ie(o,e,r),console.log(y.green(`Job "${r}" added to view "${e}".`));}),t.command("remove").alias("rm").description("Remove a job from a view").argument("<view>","View name").argument("<job>","Job name").action(async(e,r)=>{let{jenkins:o}=await w();if(!(await Q(o,e)).some(i=>i.name===r))throw new p(`Job "${r}" is not in view "${e}".`,{hint:'Use "jc view show <name>" to see jobs in this view.'});await qe(o,e,r),console.log(y.green(`Job "${r}" removed from view "${e}".`));}),t.command("edit").description("Edit jobs in a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await w(),[o,s]=await Promise.all([T(r),Q(r,e)]);if(!o.length){console.log("No jobs found.");return}o.sort((f,d)=>String(f?.name??"").localeCompare(String(d?.name??""),"en",{sensitivity:"base"}));let i=new Set(s.map(f=>String(f?.name??"").trim())),a=o.map(f=>String(f?.name??"").trim()).filter(Boolean).map(f=>({name:f,checked:i.has(f)})),c=await oe.prompt([{type:"checkbox",name:"jobs",message:`Select jobs for view "${e}"`,choices:a,pageSize:20}]),l=new Set((c.jobs??[]).map(f=>String(f).trim())),u=[...l].filter(f=>!i.has(f)),m=[...i].filter(f=>f&&!l.has(f));if(!u.length&&!m.length){console.log(y.yellow("No changes to apply."));return}for(let f of u)await Ie(r,e,f);for(let f of m)await qe(r,e,f);console.log(y.green(`View "${e}" updated.`));}),t.command("create").description("Create a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await w();await pt(r,e),console.log(y.green(`View "${e}" created.`));}),t.command("delete").alias("del").description("Delete a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await w();await dt(r,e),console.log(y.green(`View "${e}" deleted.`));});}function rr(n){return h(n?P(n):"-")}function Ne(n){let t=process.argv,e=0;if(n){let r=t.lastIndexOf(n);r>=0&&(e=r+1);}for(let r=e;r<t.length;r+=1){let o=t[r];if(o==="-j"||o==="--job"){let s=t[r+1];return !s||s.startsWith("-")?"":s}}}function Ge(n,t,e){let r=t?.parent?.opts()?.job,o=Ne(e)??Ne(t?.name())??Ne("ws")??Ne("workspace");return n??r??o}function on(n){return n.replace(/^\/+/,"").replace(/\/+$/,"")}function or(n,t){let e=on(n),r=on(t);return e?r?r===e||r.startsWith(`${e}/`)?r:`${e}/${r}`:e:r||""}function ir(n){let t=process.platform,e="",r=[];t==="darwin"?(e="open",r=[n]):t==="win32"?(e="cmd",r=["/c","start","",n]):(e="xdg-open",r=[n]);try{spawn(e,r,{stdio:"ignore",detached:!0}).unref();}catch{}}async function N(n){try{return await n()}catch(t){if(t?.response?.status===404)return null;throw t}}async function sr(n,t){try{return await ht(n,t)}catch{return}}async function ar(n,t){try{return await Et(n,t)}catch{return}}async function cr(n){try{return await re(n,{raw:!1})}catch{return []}}async function Ke(n,t,e){let r=await e.direct(t);if(r!==null)return r;let o=await sr(n,t);if(o&&o!==t){let a=await e.direct(o);if(a!==null)return a}if(e.beforeView){let a=await e.beforeView();if(a!==null)return a}let s=await ar(n,t);if(s){let a=await e.byViewName(s);if(a!==null)return a}let i=await cr(n);for(let a of i){let c=String(a?.url??"").trim();if(!c)continue;let l=await e.byViewUrl(c);if(l!==null)return l}return null}async function lr(n,t,e){return await Ke(n,t,{direct:r=>N(()=>Me(n,r,{path:e})),byViewName:r=>N(()=>xt(n,r,t,{path:e})),byViewUrl:r=>N(()=>jt(n,r,t,{path:e})),beforeView:async()=>{if(e)return null;let r=t.split("/").pop();return r?await N(()=>Me(n,t,{path:r})):null}})}async function ur(n,t,e){return await Ke(n,t,{direct:r=>N(()=>Ct(n,r,e)),byViewName:r=>N(()=>vt(n,r,t,e)),byViewUrl:r=>N(()=>Jt(n,r,t,e))})}async function mr(n,t,e){return await Ke(n,t,{direct:r=>N(()=>St(n,r,e)),byViewName:r=>N(()=>$t(n,r,t,e)),byViewUrl:r=>N(()=>Rt(n,r,t,e))})}function sn(n){let t=n.command("workspace").alias("ws").description("Workspace operations");t.option("-j, --job <job>","Specify job").option("-p, --path <path>","Workspace sub path").action(async(e,r)=>{let{config:o,jenkins:s}=await w(),i=Ge(e.job,r,"workspace"),a=await k(o,i),c=String(e.path??"").trim(),l=rn(`Loading workspace for job "${a}"...`).start(),u=await lr(s,a,c);if(!u){l.warn(`Workspace not found for job: ${a}. The job may be inside a folder, not run yet, or workspace is disabled.`);return}if(l.succeed(`Workspace loaded for job: ${a}`),!u.length){console.table([{name:"-",type:"-",size:"-",modified:"-",path:"-",url:"-"}]);return}let m=u.map(g=>({name:String(g.name??""),typeRaw:g.type,size:Vt(g.size),modified:rr(g.modified),path:or(c,String(g.path??g.name??""))})),f=j(m.map(g=>g.name)),d=j(m.map(g=>g.path));console.table(m.map(g=>{let b=U(String(g.name??""),f),v=String(g.typeRaw??"");return {name:v==="dir"?h(y.bold.cyan(b)):v==="file"?h(y.green(b)):h(y.gray(b)),size:g.size,modified:g.modified,path:S(String(g.path??""),d)}}));}),t.command("cat").description("Show file content in workspace").argument("<path>","Workspace file path").option("-j, --job <job>","Specify job").option("-o, --out <file>","Save file to path").option("--open","Open file with default application").action(async(e,r,o)=>{let{config:s,jenkins:i}=await w(),a=Ge(r.job,o,"cat"),c=await k(s,a),l=String(e??"").trim();if(!l)throw new p("Path is required.",{hint:'Provide a file path after "cat".',example:"jenkins-cli ws cat dist/index.html"});let u=String(r.out??"").trim(),m=r.open===!0;if(u||m){let d=await mr(i,c,l);if(d===null){console.log(y.yellow(`Workspace file not found: ${l} (job: ${c}).`));return}let g=u||z.join(tr.tmpdir(),z.basename(l)||"file");try{u&&await M.pathExists(u)&&(await M.stat(u)).isDirectory()&&(g=z.join(u,z.basename(l)||"file"));}catch{}await M.ensureDir(z.dirname(g)),await M.writeFile(g,d.data),m?(ir(g),console.log(y.green(`Opened: ${g}`))):console.log(y.green(`Saved to: ${g}`));return}let f=await ur(i,c,l);if(f===null){console.log(y.yellow(`Workspace file not found: ${l} (job: ${c}).`));return}process.stdout.write(String(f));}),t.command("wipe").description("Wipe out current workspace").option("-j, --job <job>","Specify job").action(async(e,r)=>{let{config:o,jenkins:s}=await w(),i=Ge(e.job,r,"wipe"),a=await k(o,i);try{if(!(await oe.prompt([{type:"confirm",name:"confirm",message:`Wipe workspace for job "${a}"? This cannot be undone.`,default:!1}]))?.confirm){console.log(y.yellow("Cancelled."));return}}catch(l){if(Ce(l)){console.log(y.yellow("Cancelled."));return}throw l}let c=rn(`Wiping workspace for job "${a}"...`).start();try{await Pt(s,a),c.succeed(`Workspace wiped for job: ${a}`);}catch(l){throw c.fail(`Failed to wipe workspace for job: ${a}`),l}});}function an(n){return n.active===!0||n.enabled===!0}function cn(n,t){return String(n.shortName??"").localeCompare(String(t.shortName??""),"en",{sensitivity:"base"})}function ln(n){let t=n.command("plugin").description("Plugin operations");t.command("show").description("Show used plugins").action(async()=>{let{jenkins:e}=await w(),o=(await De(e)??[]).filter(a=>an(a)&&String(a.shortName??"").trim());if(o.sort(cn),o.length===0){console.log("No plugins found.");return}let s=j(o.map(a=>a.shortName)),i=j(o.map(a=>a.version));console.table(o.map(a=>({name:S(a.shortName??"",s),version:S(a.version??"",i),enabled:a.enabled===void 0?h("-"):a.enabled,active:a.active===void 0?h("-"):a.active,update:a.hasUpdate===void 0?h("-"):a.hasUpdate})));}),t.command("backup").description("Backup plugins list").requiredOption("-d, --destination <path>","Destination folder").action(async e=>{let{jenkins:r}=await w(),o=String(e.destination??"").trim();if(!o)throw new p("Destination is required.",{hint:"Provide a destination folder with -d or --destination.",example:"jenkins-cli plugin backup -d ./jenkins-backup"});let i=(await De(r)??[]).filter(l=>an(l)&&String(l.shortName??"").trim());if(i.sort(cn),i.length===0)throw new p("No plugins found to back up.",{hint:"Check Jenkins permissions or ensure plugins are installed.",example:"jenkins-cli plugin show"});await promises.mkdir(o,{recursive:!0});let a=z.join(o,"plugins.json"),c=z.join(o,"plugins.txt");await promises.writeFile(a,JSON.stringify(i,null,2)),await promises.writeFile(c,`${i.map(l=>{let u=String(l.shortName??"").trim(),m=String(l.version??"").trim();return m?`${u}:${m}`:u}).filter(Boolean).join(`
43
+ `)}
44
+ `),console.log(`Backup completed. ${i.length} plugin(s) saved to ${z.resolve(o)}`);});}function un(n){zt(n),Ht(n),Zt(n),en(n),Gt(n),Yt(n),ln(n),Ot(n),Kt(n),tn(n),Xt(n),nn(n),sn(n);}function mn(n,t,e){let r=s=>s.options.some(i=>i.long===t||i.short===t),o=s=>{r(s)||s.option(t,e);for(let i of s.commands)o(i);};for(let s of n.commands)o(s);}program.name("jenkins-cli").description(rt).version(nt,"-v, --version").helpOption("-h, --help").option("--debug","Print Jenkins request params").allowExcessArguments(!1).configureHelp({sortSubcommands:!0,sortOptions:!0}).action(Tt);un(program);mn(program,"--debug","Print Jenkins request params");async function fr(){try{await program.parseAsync(process.argv);}catch(n){q(n);}}fr();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ts-org/jenkins-cli",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
4
4
  "description": "Jenkins deployment CLI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",