@ts-org/jenkins-cli 3.1.1 → 3.1.3

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 +137 -119
  2. package/dist/cli.js +28 -23
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,63 +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
17
  pnpm install -g @ts-org/jenkins-cli
18
18
  ```
19
19
 
20
- ### 🚀 使用
20
+ ### 🚀 Usage
21
21
 
22
- 在你的项目根目录下执行:
22
+ Run from your project root:
23
23
 
24
24
  ```bash
25
25
  jenkins-cli
26
26
  ```
27
27
 
28
- CLI 会自动加载配置,列出 Git 分支和部署环境供你选择。
28
+ The CLI will auto-load your config, then list Git branches and deployment environments for you to choose from.
29
29
 
30
30
  ![Demo](https://cdn.jsdelivr.net/npm/@ts-org/jenkins-cli@latest/docs/images/demo.png)
31
31
 
32
- ### ⚙️ 配置
32
+ ### ⚙️ Configuration
33
33
 
34
- 在项目根目录创建 `jenkins-cli.yaml`:
34
+ Create `jenkins-cli.yaml` in your project root:
35
35
 
36
36
  ```yaml
37
37
  # yaml-language-server: $schema=https://cdn.jsdelivr.net/npm/@ts-org/jenkins-cli@latest/schemas/jenkins.json
38
38
 
39
- # Jenkins API 地址,格式: http(s)://username:token@host:port
39
+ # Jenkins API URL format: http(s)://username:token@host:port
40
40
  apiToken: http://user:token@jenkins.example.com
41
41
 
42
- # Jenkins Job 名称 (可被 -j 参数覆盖,也可用函数引用)
42
+ # Jenkins job name (can be overridden with -j, or provided via function reference)
43
43
  job: your-project-job
44
44
 
45
- # 部署环境列表 (将作为参数 `mode` 传入 Jenkins,也可用函数引用)
46
- # 选项要与Jenkins配置的对上
47
- # 这里配置了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.
48
49
  modes:
49
50
  - dev
50
51
  - sit
51
52
  - uat
52
53
  ```
53
54
 
54
- 建议在 `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.
55
56
 
56
- #### 动态 job / modes
57
+ #### Dynamic `job` / `modes`
57
58
 
58
- `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.
59
60
 
60
- 示例:
61
+ Example:
61
62
 
62
63
  ```yaml
63
64
  job: jenkins-utils.ts:dynamicJob
@@ -74,47 +75,47 @@ export function dynamicModes(answers: Record<string, unknown>) {
74
75
  }
75
76
  ```
76
77
 
77
- #### 扩展交互参数
78
+ #### Extended Interactive Parameters
78
79
 
79
- 通过 `configs` 字段,你可以添加自定义的交互式提问,其结果将作为参数传递给 Jenkins
80
+ With the `configs` field, you can add custom interactive prompts. Their results are passed to Jenkins as build parameters.
80
81
 
81
- - `type`: 支持 `input`, `number`, `confirm`, `list`, `rawlist`, `checkbox`, `password`等类型。
82
- - `choices`, `default`, `validate`, `when`, `filter`, `transformer`: 支持从本地 TS/JS 文件动态加载。
83
- - `path:exportName`:自动判断并使用导出值;如果是函数则调用 (支持 async)
84
- - `offset`: 负数可调整提问顺序
85
- - `-1`:插到 branch 后、modes
86
- - `<= -2`:插到 branch
87
- - `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`.
88
89
 
89
- **示例:**
90
+ **Example:**
90
91
 
91
92
  ```yaml
92
93
  configs:
93
94
  - type: number
94
95
  name: buildNumber
95
- message: '请选择构建号:'
96
+ message: 'Select build number:'
96
97
  offset: -2
97
98
  - type: input
98
99
  name: version
99
- message: '请输入版本号:'
100
+ message: 'Enter version:'
100
101
  default: '1.0.0'
101
102
  filter: src/config.ts:filterVersion
102
103
  transformer: src/config.ts:transformVersion
103
104
  - type: list
104
105
  name: service
105
- message: '请选择服务:'
106
+ message: 'Select service:'
106
107
  choices: src/config.ts:getServices
107
108
  - type: confirm
108
109
  name: smokeTest
109
- message: '是否开启冒烟测试?'
110
+ message: 'Enable smoke test?'
110
111
  when: src/config.ts:whenSmokeTest
111
112
  - type: input
112
113
  name: ticket
113
- message: '请输入关联的工单号:'
114
+ message: 'Enter ticket ID:'
114
115
  validate: src/config.ts:validateTicket
115
116
  ```
116
117
 
117
- 对应的 `src/config.ts`:
118
+ Corresponding `src/config.ts`:
118
119
 
119
120
  ```ts
120
121
  export async function getServices() {
@@ -139,233 +140,250 @@ export function whenSmokeTest(answers: { mode?: string }) {
139
140
  }
140
141
 
141
142
  export function validateTicket(input: unknown, answers: Record<string, unknown>) {
142
- return /^(feat|fix|refactor)-[a-zA-Z0-9]+$/.test(input) || '工单号格式不正确'
143
+ return /^(feat|fix|refactor)-[a-zA-Z0-9]+$/.test(input) || 'Invalid ticket format'
143
144
  }
144
145
  ```
145
146
 
146
- **四个函数的完整入参说明**:
147
+ **Complete parameter details for the four functions:**
147
148
 
148
149
  - `when(answers)`
149
- - `answers`: 当前已回答的所有答案对象(键为问题的 `name`)。
150
- - 返回 `boolean | Promise<boolean>`,用于决定是否显示该问题。
150
+ - `answers`: all answers collected so far (keys are prompt `name` values).
151
+ - Returns `boolean | Promise<boolean>` to decide whether to show the prompt.
151
152
 
152
153
  - `validate(input, answers)`
153
- - `input`: 用户当前输入的原始值。
154
- - `answers`: 当前已回答的所有答案对象。
155
- - 返回 `true` 表示通过;返回 `string` 作为错误提示;也可返回 `Promise<true | string>`。
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.
156
157
 
157
158
  - `filter(input, answers)`
158
- - `input`: 用户当前输入的原始值。
159
- - `answers`: 当前已回答的所有答案对象。
160
- - 返回转换后的值(可同步或返回 Promise)。
159
+ - `input`: raw value currently entered by the user.
160
+ - `answers`: all answers collected so far.
161
+ - Returns the transformed value (sync or async).
161
162
 
162
163
  - `transformer(input, answers, meta)`
163
- - `input`: 用户当前输入的原始值。
164
- - `answers`: 当前已回答的所有答案对象。
165
- - `meta`: 渲染相关的元信息(例如 `isFinal`),不同版本可能略有差异。
166
- - 返回显示用的字符串,不会修改answers的值(可同步或返回 Promise)。
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).
167
168
 
168
- > **提示**: 配置也支持写在 `package.json` `jenkins-cli` 字段里。
169
+ > **Tip**: You can also put configuration under the `jenkins-cli` field in `package.json`.
169
170
  >
170
- > **查找顺序**: `jenkins-cli.yaml` > `package.json` > 祖先目录的 `jenkins-cli.yaml`。
171
+ > **Lookup order**: `jenkins-cli.yaml` > `package.json` > ancestor directory `jenkins-cli.yaml`.
171
172
 
172
- ### 🤖 命令参考
173
+ ### 🤖 Command Reference
173
174
 
174
- #### `build` (非交互式触发)
175
+ #### `build` (non-interactive trigger)
175
176
 
176
- 适用于 CI 或其他脚本化场景。
177
+ Useful for CI or other scripted scenarios.
177
178
 
178
179
  ```bash
179
- # 触发 dev 环境构建
180
+ # Trigger a build for the dev environment
180
181
  jenkins-cli build -b origin/develop -m dev
181
182
 
182
- # 同时触发多个环境
183
+ # Trigger multiple environments at once
183
184
  jenkins-cli build -b origin/develop -m dev,sit
184
185
 
185
- # 传入额外参数
186
+ # Pass extra parameters
186
187
  jenkins-cli build -b origin/develop -m dev --param version=1.2.3 --param force=true
187
188
  ```
188
189
 
189
- 说明:默认会读取 `git config user.name` 并自动追加参数 `triggered_by`。
190
- 如需覆盖,可显式传入 `--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.
191
192
 
192
193
  ---
193
194
 
194
- #### `builds` - 构建管理
195
+ #### `builds` - build management
195
196
 
196
197
  ```bash
197
- # 查看最近 20 次构建
198
+ # Show the latest 20 builds
198
199
  jenkins-cli builds
199
200
 
200
- # 查看运行中的构建
201
+ # Show running builds
201
202
  jenkins-cli builds active
202
203
 
203
- # 查看 Jenkins 上所有运行中的构建
204
+ # Show all running builds on Jenkins
204
205
  jenkins-cli builds active -a
205
206
 
206
207
  ```
207
208
 
208
- #### `changes` - 变更记录
209
+ #### `changes` - change logs
209
210
 
210
211
  ```bash
211
- # 查看最近 20 次构建的变更记录
212
+ # Show change logs for the latest 20 builds
212
213
  jenkins-cli changes
213
214
 
214
- # 查看最近 50 次构建的变更记录
215
+ # Show change logs for the latest 50 builds
215
216
  jenkins-cli changes -n 50
216
217
 
217
- # 查看指定构建的变更记录
218
+ # Show change logs for a specific build
218
219
  jenkins-cli changes 1234
219
220
  ```
220
221
 
221
- #### `config` - Job 配置
222
+ #### `config` - job configuration
222
223
 
223
224
  ```bash
224
- # 读取当前 Job XML 配置
225
+ # Read current job XML config
225
226
  jenkins-cli config show
226
227
 
227
- # JSON 格式输出
228
+ # Output as JSON
228
229
  jenkins-cli config show -f json
229
230
 
230
- # 备份当前 Job 配置
231
+ # Back up current job config
231
232
  jenkins-cli config backup -d ./jenkins-backup
232
233
 
233
- # 备份指定 Job 配置
234
+ # Back up a specific job config
234
235
  jenkins-cli config backup -d ./jenkins-backup -j remote-factory-admin
235
236
 
236
- # glob 过滤备份多个 Job 配置
237
+ # Back up multiple job configs filtered by glob
237
238
  jenkins-cli config backup -d ./jenkins-backup --filter 'remote-*'
238
239
 
239
- # 备份所有 Job 配置
240
+ # Back up all job configs
240
241
  jenkins-cli config backup -d ./jenkins-backup -a
241
242
  ```
242
243
 
243
- #### `console` - 查看日志
244
+ #### `plugin` - plugin management
244
245
 
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
+ #### `backup` - full Jenkins backup
255
+
256
+ ```bash
257
+ # Back up Jenkins global config, jobs, plugins, and credentials
258
+ jenkins-cli backup -d ./jenkins-backup
259
+ ```
260
+
261
+ #### `console` - view logs
262
+
263
+ Supports live log following, enabled by default.
246
264
 
247
265
  ```bash
248
- # 查看指定构建号的日志,如果正在构建则追踪,否则直接输出日志
266
+ # Show logs for a specific build number; follow if running, otherwise print once
249
267
  jenkins-cli console 1234
250
268
 
251
- # 查看最后一次构建日志,如果正在构建则追踪,否则直接输出日志
269
+ # Show logs for the latest build; follow if running, otherwise print once
252
270
  jenkins-cli console last
253
271
 
254
- # 仅输出当前日志内容,不追踪
272
+ # Print current logs only, without following
255
273
  jenkins-cli console last --no-follow
256
274
 
257
- # 查看最后一次构建成功日志
275
+ # Show logs from the latest successful build
258
276
  jenkins-cli console last -s success
259
277
 
260
- # 查看最后一次构建失败日志
278
+ # Show logs from the latest failed build
261
279
  jenkins-cli console last -s failed
262
280
  ```
263
281
 
264
- #### `job` - Job 管理
282
+ #### `job` - job management
265
283
 
266
284
  ```bash
267
- # 列出所有 Job (支持 glob 过滤)
285
+ # List all jobs (supports glob filtering)
268
286
  jenkins-cli job list --search 'project-*'
269
287
 
270
- # 查看当前 Job 信息
288
+ # Show current job info
271
289
  jenkins-cli job info
272
290
  ```
273
291
 
274
- #### `me` - 用户信息
292
+ #### `me` - user information
275
293
 
276
294
  ```bash
277
- # 验证 API Token 并查看当前用户
295
+ # Verify API token and show current user
278
296
  jenkins-cli me
279
297
  ```
280
298
 
281
- #### `params` - Job 参数
299
+ #### `params` - job parameters
282
300
 
283
301
  ```bash
284
- # 查看当前 Job 的参数定义
302
+ # Show parameter definitions for the current job
285
303
  jenkins-cli params
286
304
  ```
287
305
 
288
- #### `queue` - 等待构建队列管理
306
+ #### `queue` - pending build queue management
289
307
 
290
308
  ```bash
291
- # 查看等待构建的队列
309
+ # Show pending build queue
292
310
  jenkins-cli queue list
293
311
 
294
- # 取消一个等待构建队列项
312
+ # Cancel one queued item
295
313
  jenkins-cli queue cancel 5678
296
314
  ```
297
315
 
298
- #### `stop` - 停止构建
316
+ #### `stop` - stop builds
299
317
 
300
318
  ```bash
301
- # 停止一个构建
319
+ # Stop one build
302
320
  jenkins-cli stop 1234
303
321
 
304
- # 清理当前 Job 的队列和所有运行中的构建
322
+ # Clear queue and stop all running builds for current job
305
323
  jenkins-cli stop -a
306
324
 
307
- # 停止 Jenkins 上所有的构建和队列项 (慎用)
325
+ # Stop all builds and queue items on Jenkins (use with caution)
308
326
  jenkins-cli stop -A
309
327
  ```
310
328
 
311
- #### `view` - 视图管理
329
+ #### `view` - view management
312
330
 
313
331
  ```bash
314
- # 查看所有视图
332
+ # Show all views
315
333
  jenkins-cli view list
316
334
 
317
- # 查看视图包含的 Job
335
+ # Show jobs in a view
318
336
  jenkins-cli view show MyView
319
337
 
320
- # 添加 Job 到视图
338
+ # Add a job to a view
321
339
  jenkins-cli view add MyView my-job
322
340
 
323
- # 从视图移除 Job
341
+ # Remove a job from a view
324
342
  jenkins-cli view remove MyView my-job
325
343
 
326
- # 编辑视图中的 Job(勾选/取消)
344
+ # Edit jobs in a view (check/uncheck)
327
345
  jenkins-cli view edit MyView
328
346
 
329
- # 创建视图
347
+ # Create a view
330
348
  jenkins-cli view create MyView
331
349
 
332
- # 删除视图
350
+ # Delete a view
333
351
  jenkins-cli view delete MyView
334
352
  ```
335
353
 
336
- #### `workspace` - 工作区
354
+ #### `workspace` - workspace
337
355
 
338
356
  ```bash
339
- # 查看当前 Job 的工作区(默认读取 jenkins-cli.yaml 里的 job)
357
+ # Show workspace for current job (reads `job` from jenkins-cli.yaml by default)
340
358
  jenkins-cli ws
341
359
 
342
- # 查看子目录(当前 Job)
360
+ # Show subdirectory (current job)
343
361
  jenkins-cli ws -p dist
344
362
 
345
- # 指定 Job
363
+ # Specify job
346
364
  jenkins-cli ws -j my-job
347
365
 
348
- # 查看文件内容
366
+ # Show file contents
349
367
  jenkins-cli ws cat dist/index.html
350
368
 
351
- # 指定 Job 查看文件内容
369
+ # Show file contents for a specific job
352
370
  jenkins-cli ws cat dist/index.html -j my-job
353
371
 
354
- # 保存文件到本地
372
+ # Save file locally
355
373
  jenkins-cli ws cat dist/index.html -o ./index.html
356
374
 
357
- # 下载并用默认应用打开(适合图片)
375
+ # Download and open with default app (useful for images)
358
376
  jenkins-cli ws cat public/favicon.ico --open
359
377
 
360
- # 清空工作区(会提示确认)
378
+ # Wipe workspace (will prompt for confirmation)
361
379
  jenkins-cli ws wipe
362
380
 
363
- # 指定 Job 清空工作区
381
+ # Wipe workspace for a specific job
364
382
  jenkins-cli ws wipe -j my-job
365
383
  ```
366
384
 
367
385
  ### ❓ FAQ
368
386
 
369
- **Q: `stop` `queue cancel` 命令返回 403 Forbidden?**
387
+ **Q: Why does `stop` or `queue cancel` return 403 Forbidden?**
370
388
 
371
- A: 通常是 Jenkins 用户权限不足 (需要 `Job > Cancel` 权限)。请确保你使用的是 API Token 而非密码,并检查 Jenkins CSRF 设置。工具会自动处理 CSRF,但权限问题需要手动排查。
389
+ 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
@@ -1,42 +1,47 @@
1
1
  #!/usr/bin/env node
2
2
  import { program, CommanderError } from 'commander';
3
- import oe from 'inquirer';
3
+ import ce from 'inquirer';
4
4
  import y from 'chalk';
5
- import en from 'ora';
5
+ import un from 'ora';
6
6
  import { exec, spawn } from 'child_process';
7
- import En, { promisify } from 'util';
8
- import M from 'fs-extra';
9
- import fn from 'js-yaml';
10
- import z from 'path';
11
- import bn from 'axios';
7
+ import _n, { promisify } from 'util';
8
+ import _ from 'fs-extra';
9
+ import vn from 'js-yaml';
10
+ import W from 'path';
11
+ import En from 'axios';
12
12
  import { Buffer } from 'buffer';
13
- import vn from 'jiti';
13
+ import Vn from 'jiti';
14
14
  import { fileURLToPath } from 'url';
15
15
  import { promises } from 'fs';
16
16
  import { XMLParser } from 'fast-xml-parser';
17
- import Gn from 'os';
17
+ import lr from 'os';
18
18
 
19
- var Ze="3.1.1",et="Jenkins deployment CLI";var d=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 ln(n){return n instanceof d?n:n instanceof CommanderError?new d(n.message,{hint:"Check the command usage and options.",example:"jenkins-cli --help",exitCode:n.exitCode??1}):n instanceof Error?new d(n.message,{hint:"Check your input and try again.",example:"jenkins-cli --help"}):new d("Unknown error",{example:"jenkins-cli --help"})}function q(n){let t=ln(n);if(console.error(y.red(`
19
+ var it="3.1.3",st="Jenkins deployment CLI";var g=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 jn(n){return n instanceof g?n:n instanceof CommanderError?new g(n.message,{hint:"Check the command usage and options.",example:"jenkins-cli --help",exitCode:n.exitCode??1}):n instanceof Error?new g(n.message,{hint:"Check your input and try again.",example:"jenkins-cli --help"}):new g("Unknown error",{example:"jenkins-cli --help"})}function M(n){let t=jn(n);if(console.error(y.red(`
20
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 Ee=promisify(exec);async function tt(){try{let{stdout:n}=await Ee("git branch --show-current"),t=n.trim();if(!t)throw new d("Not on any branch (detached HEAD state).",{hint:"Checkout a branch before running this command.",example:"git checkout main"});return t}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 nt(){try{let{stdout:n}=await Ee('git branch -r --format="%(refname:short)"');return n.split(`
22
- `).filter(t=>t.includes("/")&&!t.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 le(){try{let{stdout:n}=await Ee("git config user.name"),t=n.trim();return t||void 0}catch{return}}var D="jenkins-cli.yaml",me="package.json",ue="jenkins-cli",pn="jenkinsCli";function ot(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 dn(n=process.cwd()){return ot(D,n)}function gn(n=process.cwd()){let t=ot(me,n);return t?z.dirname(t):null}function wn(n){let t=Array.isArray(n.modes)&&n.modes.length>0||typeof n.modes=="string";return !!(n.apiToken&&n.job&&t)}async function rt(n){let t=await M.readFile(n,"utf-8"),e=fn.load(t);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}:
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 Ue=promisify(exec);async function at(){try{let{stdout:n}=await Ue("git branch --show-current"),t=n.trim();if(!t)throw new g("Not on any branch (detached HEAD state).",{hint:"Checkout a branch before running this command.",example:"git checkout main"});return t}catch{throw new g("Failed to get current branch.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}async function ct(){try{let{stdout:n}=await Ue('git branch -r --format="%(refname:short)"');return n.split(`
22
+ `).filter(t=>t.includes("/")&&!t.includes("HEAD"))}catch{throw new g("Failed to list branches.",{hint:"Make sure you are inside a git repository.",example:"cd /path/to/repo && jenkins-cli"})}}async function fe(){try{let{stdout:n}=await Ue("git config user.name"),t=n.trim();return t||void 0}catch{return}}var z="jenkins-cli.yaml",ge="package.json",de="jenkins-cli",$n="jenkinsCli";function ut(n,t=process.cwd()){let e=t;for(;;){let r=W.join(e,n);if(_.existsSync(r))return r;let o=W.dirname(e);if(o===e)return null;e=o;}}function Jn(n=process.cwd()){return ut(z,n)}function Pn(n=process.cwd()){let t=ut(ge,n);return t?W.dirname(t):null}function Rn(n){let t=Array.isArray(n.modes)&&n.modes.length>0||typeof n.modes=="string";return !!(n.apiToken&&n.job&&t)}async function lt(n){let t=await _.readFile(n,"utf-8"),e=vn.load(t);if(!e||typeof e!="object")throw new g(`Invalid config in ${z}. Expected a YAML object.`,{hint:"Top-level config must be key/value pairs.",example:`${z}:
23
23
  apiToken: http://user:token@host
24
24
  job: my-job
25
- modes: [dev]`});return e}async function hn(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[pn];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new d(`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 it(){let n=process.cwd(),t=gn(n)??n,e=z.join(t,D),r={},o=z.dirname(t),s=dn(o);s&&await M.pathExists(s)&&(r=await rt(s));let i=await hn(t);r={...r,...i};let a=await M.pathExists(e)?await rt(e):{};if(r={...r,...a},!wn(r))throw new d("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}:
25
+ modes: [dev]`});return e}async function Nn(n){let t=W.join(n,ge);if(!await _.pathExists(t))return {};let e=await _.readFile(t,"utf-8"),r=JSON.parse(e),o=r[de]??r[$n];if(o==null)return {};if(typeof o!="object"||Array.isArray(o))throw new g(`Invalid ${ge} config: "${de}" must be an object.`,{hint:"Use an object for the config section.",example:`"${de}": { "apiToken": "http://user:token@host", "job": "my-job", "modes": ["dev"] }`});return o}async function mt(){let n=process.cwd(),t=Pn(n)??n,e=W.join(t,z),r={},o=W.dirname(t),s=Jn(o);s&&await _.pathExists(s)&&(r=await lt(s));let i=await Nn(t);r={...r,...i};let a=await _.pathExists(e)?await lt(e):{};if(r={...r,...a},!Rn(r))throw new g("Config incomplete or not found.",{hint:`Tried ${z} in project root, "${de}" in ${ge}, and walking up from cwd. Required: apiToken, job, modes (non-empty array).`,example:`${z}:
26
26
  apiToken: http://user:token@host
27
27
  job: my-job
28
28
  modes:
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 st(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 yn(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 at(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 N(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 kn(n){return /<!doctype\s+html|<html\b|<body\b/i.test(n)}function xn(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 jn(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 S of a){let L=S[1]??"",R=_(fe(S[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",f=_(c.split("?")[0]??"").replace(/\/+$/,"")||l,m=i.match(/class="last-modified"[^>]*>([\s\S]*?)<\/td>/i),p=m?_(fe(m[1]??"")).trim():"",g=p?Z(p):void 0,b=`${l}|${f}`;r.has(b)||(r.add(b),e.push({name:l,path:f,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(kn(c)){let l=xn(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=jn(a.data);if(c.length)return {children:c}}}catch(a){i=a;}throw i||new Error("Workspace listing not found")}function Cn(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 Cn(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,f=String(e?.href??e?.url??e?.link??"").trim()||void 0;return {name:i,path:a,url:f,size:l,modified:u,type:c}})}function ct(n){let t=n.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!t)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]=t,i=`${e}://${s.replace(/\/$/,"")}/`,a=bn.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(),f=new URL(String(l.url??""),l.baseURL??i).toString(),m=l.params,p=l.data;p instanceof URLSearchParams?p=Object.fromEntries(p):typeof p=="string"?String(l.headers?.["Content-Type"]??l.headers?.["content-type"]??"").includes("application/x-www-form-urlencoded")?p=Object.fromEntries(new URLSearchParams(p)):p=p.trim()?p:void 0:Buffer.isBuffer(p)&&(p=`[Buffer ${p.length} bytes]`);let g={method:u,url:f};return m&&Object.keys(m).length>0&&(g.params=m),p!=null&&p!==""&&(g.data=p),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 lt(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=lt(n,5e3)),n.crumbPromise}async function W(n){await Ve(n),n.crumbForm||(n.crumbPromise=lt(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 yn(n,i.url),c=st(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 d(`View not found: ${t}`,{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 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 d(`View not found: ${t}`,{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 ut(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 d(`View already exists: ${t}`,{hint:'Use "jenkins-cli view list" to see existing views.'}):o===403?new d(`Forbidden to create view: ${t}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Create".'}):r}}async function mt(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 d(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):o===403?new d(`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=(f,m)=>{let p=f.actions?.find(b=>b.parameters);return p?p.parameters.find(b=>b.name===m)?.value:void 0},l=e[r];if(s&&l){let f=i.find(m=>c(m,o)===s&&c(m,r)===l);if(f)return new URL(`queue/item/${f.id}/`,n.baseURL).toString()}let u=!1;if(s)for(let f of a)c(f,o)===s&&(console.log(`Stopping running build #${f.number} (mode=${s})...`),await ne(n,t,f.number),u=!0);u&&await new Promise(f=>setTimeout(f,1e3));try{return await W(n),(await n.axios.post(`${x(t)}/buildWithParameters/`,new URLSearchParams({...e}),B)).headers.location||""}catch(f){let m=f?.response?.status,p=at(f?.response?.data);if(m===400){let g=p?`Jenkins response (snippet):
31
- ${p}`: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=p?`Jenkins response (snippet):
32
- ${p}`: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 f}}async function ft(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 pt(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 f=String(u.name??"").trim();if(f&&f===r&&u.url){let m=st(String(u.url));m&&c.push(m);}Array.isArray(u.jobs)&&l.push(...u.jobs);}if(c.length)return c[0]}async function we(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 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(t??"").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 dt(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 gt(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 he(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 wt(n,t,e){let r=await n.axios.get(`${x(t)}/${e}/consoleText`,{responseType:"text"});return String(r.data??"")}async function ht(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 De(n,t,e){let r=String(e?.path??"").trim(),o=r?`${N(r)}/`:"",s=await Te(n,x(t),o);return Oe(s??{})}async function bt(n,t,e,r){let o=String(r?.path??"").trim(),s=o?`${N(o)}/`:"",i=`view/${encodeURIComponent(t)}/${x(e)}`,a=await Te(n,i,s);return Oe(a??{})}async function yt(n,t,e,r){let o=String(r?.path??"").trim(),s=o?`${N(o)}/`:"",i=new URL(`${x(e)}/`,t).toString(),a=await Te(n,i,s);return Oe(a??{})}async function kt(n,t,e){let r=N(String(e??"").trim());return await Le(n,x(t),r)}async function xt(n,t,e){let r=N(String(e??"").trim());return await Ue(n,x(t),r)}async function jt(n,t,e,r){let o=N(String(r??"").trim()),s=`view/${encodeURIComponent(t)}/${x(e)}`;return await Le(n,s,o)}async function Ct(n,t,e,r){let o=N(String(r??"").trim()),s=`view/${encodeURIComponent(t)}/${x(e)}`;return await Ue(n,s,o)}async function St(n,t,e,r){let o=N(String(r??"").trim()),s=new URL(`${x(e)}/`,t).toString();return await Le(n,s,o)}async function $t(n,t,e,r){let o=N(String(r??"").trim()),s=new URL(`${x(e)}/`,t).toString();return await Ue(n,s,o)}async function vt(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=at(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 Me(n,t){let e=await n.axios.get(`${x(t)}/config.xml`,{responseType:"text"});return String(e.data??"")}async function Jt(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 d(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".',example:"jenkins-cli view show MyView"}):r}}async function Rt(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 h(){let n=en("Loading configuration...").start();try{let t=await it();n.succeed("Configuration loaded");let e=ct(t.apiToken);return Ve(e),{config:t,jenkins:e}}catch(t){q(t),process.exit(1);}}var Rn=vn(fileURLToPath(import.meta.url),{interopDefault:!0});async function ye(n){return await Promise.resolve(n)}function Pn(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 An(n,t){return z.isAbsolute(t)?t:z.join(n,t)}function Nt(n){return !!n&&typeof n=="object"&&!Array.isArray(n)}async function ke(n,t){let e=Pn(t);if(!e)return null;let r=An(n,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=Rn(r),s=Nt(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: ${t}`,{hint:"Make sure the export exists in the referenced file.",example:'export const choices = ["dev", "uat"]'});return {value:i}}async function Et(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 d(e.error(t),{hint:e.hint,example:e.example});return r.value}async function At(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(!Nt(s))continue;let i={...s};if(i.choices!==void 0){i.choices=await At(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 d(`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 At(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 Ft(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 Bt(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 E(n,t){let e=Number(n);if(!Number.isInteger(e)||e<0)throw new d(`${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 Wt(){console.log(y.bold.blue(`
29
+ - dev`});return {...r,projectRoot:t}}var U={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 he(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 Oe(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 An(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:he(r.actions)}}function pt(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 ee(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 te(n){let t=ee(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 Ve(n,t){let e=n.replace(/\/+$/,""),r=t.replace(/^\/+/,"");return /^https?:\/\//i.test(e)?new URL(`${r}`,`${e}/`).toString():`${e}/${r}`}function Q(n){return n.replace(/&nbsp;/g," ").replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'")}function we(n){return n.replace(/<[^>]*>/g,"")}function Fn(n){return /<!doctype\s+html|<html\b|<body\b/i.test(n)}function Bn(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 Q(we(s))}function Wn(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 O=v[1]??"",P=Q(we(v[2]??"")).trim();if(!(!P||P==="..")&&!/all\s+files\s+in\s+zip/i.test(P)){c=O,l=P;break}}if(!c||!l||c==="../"||c==="..")continue;let u=/class="[^"]*folder[^"]*"/i.test(i)||c.endsWith("/")?"dir":"file",p=Q(c.split("?")[0]??"").replace(/\/+$/,"")||l,m=i.match(/class="last-modified"[^>]*>([\s\S]*?)<\/td>/i),f=m?Q(we(m[1]??"")).trim():"",d=f?te(f):void 0,b=`${l}|${p}`;r.has(b)||(r.add(b),e.push({name:l,path:p,type:u,modified:d}));}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=Q(we(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=Q(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 Ie(n,t,e){let r=Ve(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 qe(n,t,e){let r=Ve(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(Fn(c)){let l=Bn(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 De(n,t,e){let r=Ve(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=Wn(a.data);if(c.length)return {children:c}}}catch(a){i=a;}throw i||new Error("Workspace listing not found")}function Un(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 Me(n){return Un(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=ee(e?.size)??ee(e?.length)??ee(e?.fileSize)??ee(e?.bytes)??void 0,u=te(e?.lastModified)??te(e?.modified)??te(e?.mtime)??te(e?.timestamp)??void 0,p=String(e?.href??e?.url??e?.link??"").trim()||void 0;return {name:i,path:a,url:p,size:l,modified:u,type:c}})}function ft(n){let t=n.match(/^(https?):\/\/([^:]+):([^@]+)@(.+)$/);if(!t)throw new g("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=En.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(),p=new URL(String(l.url??""),l.baseURL??i).toString(),m=l.params,f=l.data;f instanceof URLSearchParams?f=Object.fromEntries(f):typeof f=="string"?String(l.headers?.["Content-Type"]??l.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 d={method:u,url:p};return m&&Object.keys(m).length>0&&(d.params=m),f!=null&&f!==""&&(d.data=f),console.log(y.cyan("[Jenkins Debug] Request")),console.log(JSON.stringify(d,null,2)),l}),{baseURL:i,auth:{username:r,password:o},axios:a,crumbForm:void 0}}async function dt(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 ze(n){return n.crumbPromise||(n.crumbPromise=dt(n,5e3)),n.crumbPromise}async function L(n){await ze(n),n.crumbForm||(n.crumbPromise=dt(n,15e3),await n.crumbPromise);}async function G(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 ne(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:he(o.actions)}))}async function be(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 An(n,i.url),c=Oe(String(a?.url??i.url));return {...a,job:c}}))).filter(i=>i?.building)}async function re(n,t){return await L(n),await n.axios.post("queue/cancelItem",new URLSearchParams({id:t.toString()}),U),!0}async function oe(n,t,e){try{await L(n),await n.axios.post(`${x(t)}/${e}/stop/`,new URLSearchParams({}),U);}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 _e(n,t,e){await L(n);let r=`view/${encodeURIComponent(t)}/addJobToView`;try{await n.axios.post(r,new URLSearchParams({name:e}),U);}catch(o){let s=o?.response?.status;throw s===404?new g(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new g(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function He(n,t,e){await L(n);let r=`view/${encodeURIComponent(t)}/removeJobFromView`;try{await n.axios.post(r,new URLSearchParams({name:e}),U);}catch(o){let s=o?.response?.status;throw s===404?new g(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):s===400?new g(`Job not found: ${e}`,{hint:'Check the job name using "jenkins-cli job list".'}):o}}async function gt(n,t){await L(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,U);}catch(r){let o=r?.response?.status;throw o===400?new g(`View already exists: ${t}`,{hint:'Use "jenkins-cli view list" to see existing views.'}):o===403?new g(`Forbidden to create view: ${t}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Create".'}):r}}async function wt(n,t){await L(n);let e=`view/${encodeURIComponent(t)}/doDelete`;try{await n.axios.post(e,new URLSearchParams({}),U);}catch(r){let o=r?.response?.status;throw o===404?new g(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".'}):o===403?new g(`Forbidden to delete view: ${t}`,{hint:'Check Jenkins permissions for "Overall/Manage" or "View/Delete".'}):r}}async function ye(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([G(n,t),ne(n,t)]),c=(p,m)=>{let f=p.actions?.find(b=>b.parameters);return f?f.parameters.find(b=>b.name===m)?.value:void 0},l=e[r];if(s&&l){let p=i.find(m=>c(m,o)===s&&c(m,r)===l);if(p)return new URL(`queue/item/${p.id}/`,n.baseURL).toString()}let u=!1;if(s)for(let p of a)c(p,o)===s&&(console.log(`Stopping running build #${p.number} (mode=${s})...`),await oe(n,t,p.number),u=!0);u&&await new Promise(p=>setTimeout(p,1e3));try{return await L(n),(await n.axios.post(`${x(t)}/buildWithParameters/`,new URLSearchParams({...e}),U)).headers.location||""}catch(p){let m=p?.response?.status,f=pt(p?.response?.data);if(m===400){let d=f?`Jenkins response (snippet):
31
+ ${f}`:void 0;throw new g("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:d})}if(m){let d=f?`Jenkins response (snippet):
32
+ ${f}`:void 0;throw new g(`Request failed with status code ${m}.`,{hint:"Check Jenkins job permissions and parameters.",example:"jenkins-cli me",details:d})}throw p}}async function ht(n){return (await n.axios.get("whoAmI/api/json")).data}async function V(n){return (await n.axios.get("api/json?tree=jobs[name,url,color]")).data.jobs??[]}async function ie(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 bt(n){let t=await n.axios.get("config.xml",{responseType:"text"});return String(t.data??"")}async function yt(n){let t=await n.axios.get("credentials/store/system/domain/_/config.xml",{responseType:"text"});return String(t.data??"")}async function kt(n){return (await n.axios.get("credentials/store/system/domain/_/api/json?tree=credentials[id,displayName,description,typeName]")).data??{}}function Ln(n){let t="jobs[name,url]";for(let e=0;e<n;e+=1)t=`jobs[name,url,${t}]`;return t}async function xt(n,t){let e=Math.max(1,t?.depth??8),r=Ln(e),o=await n.axios.get(`api/json?tree=${encodeURIComponent(r)}`),s=Array.isArray(o.data?.jobs)?o.data.jobs:[],i=new Set,a=[...s];for(;a.length;){let c=a.shift();if(!c)continue;let l=String(c.name??"").trim(),u=String(c.url??"").trim();if(l&&u){let p=Oe(u);p?i.add(p):i.add(l);}Array.isArray(c.jobs)&&a.push(...c.jobs);}return [...i].sort((c,l)=>c.localeCompare(l,"en",{sensitivity:"base"}))}async function jt(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 p=String(u.name??"").trim();if(p&&p===r&&u.url){let m=Oe(String(u.url));m&&c.push(m);}Array.isArray(u.jobs)&&l.push(...u.jobs);}if(c.length)return c[0]}async function ke(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 g(`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 g(`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 g(`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 Ct(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:he(i.actions)}))}async function St(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 xe(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:he(o.actions)}}async function vt(n,t,e){let r=await n.axios.get(`${x(t)}/${e}/consoleText`,{responseType:"text"});return String(r.data??"")}async function $t(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 Qe(n,t,e){let r=String(e?.path??"").trim(),o=r?`${E(r)}/`:"",s=await De(n,x(t),o);return Me(s??{})}async function Jt(n,t,e,r){let o=String(r?.path??"").trim(),s=o?`${E(o)}/`:"",i=`view/${encodeURIComponent(t)}/${x(e)}`,a=await De(n,i,s);return Me(a??{})}async function Pt(n,t,e,r){let o=String(r?.path??"").trim(),s=o?`${E(o)}/`:"",i=new URL(`${x(e)}/`,t).toString(),a=await De(n,i,s);return Me(a??{})}async function Rt(n,t,e){let r=E(String(e??"").trim());return await qe(n,x(t),r)}async function Nt(n,t,e){let r=E(String(e??"").trim());return await Ie(n,x(t),r)}async function Et(n,t,e,r){let o=E(String(r??"").trim()),s=`view/${encodeURIComponent(t)}/${x(e)}`;return await qe(n,s,o)}async function At(n,t,e,r){let o=E(String(r??"").trim()),s=`view/${encodeURIComponent(t)}/${x(e)}`;return await Ie(n,s,o)}async function Ft(n,t,e,r){let o=E(String(r??"").trim()),s=new URL(`${x(e)}/`,t).toString();return await qe(n,s,o)}async function Bt(n,t,e,r){let o=E(String(r??"").trim()),s=new URL(`${x(e)}/`,t).toString();return await Ie(n,s,o)}async function Wt(n,t){try{return await L(n),await n.axios.post(`${x(t)}/doWipeOutWorkspace`,new URLSearchParams({}),U),!0}catch(e){let r=e?.response?.status;if(r){let o=pt(e?.response?.data);throw new g(`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 se(n,t){let e=await n.axios.get(`${x(t)}/config.xml`,{responseType:"text"});return String(e.data??"")}async function Ut(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 ae(n,t){let e=t?.raw?"views[*]":"views[name,url]";return (await n.axios.get(`api/json?tree=${e}`)).data.views??[]}async function K(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 g(`View not found: ${t}`,{hint:'Check the view name using "jenkins-cli view list".',example:"jenkins-cli view show MyView"}):r}}async function Lt(n,t){let e=String(t??"").trim();if(!e)return;let r=e.split("/").filter(Boolean).pop()??e,o=await ae(n,{raw:!1});for(let s of o){let i=String(s?.name??"").trim();if(i)try{if((await K(n,i)).some(c=>{let l=String(c?.name??"").trim();return l===e||l===r}))return i}catch{}}}async function h(){let n=un("Loading configuration...").start();try{let t=await mt();n.succeed("Configuration loaded");let e=ft(t.apiToken);return ze(e),{config:t,jenkins:e}}catch(t){M(t),process.exit(1);}}var qn=Vn(fileURLToPath(import.meta.url),{interopDefault:!0});async function Ce(n){return await Promise.resolve(n)}function Dn(n){let t=String(n??"").trim();if(!t)return null;let e=t.lastIndexOf(":");if(e<=0||e===t.length-1)return null;let r=t.slice(0,e).trim(),o=t.slice(e+1).trim();if(!r||!o)return null;let s=r.lastIndexOf(":");if(s>0){let i=r.slice(s+1).trim(),a=r.slice(0,s).trim();i&&a&&!/[\\/]/.test(i)&&(r=a);}return {file:r,exportName:o}}function Mn(n,t){return W.isAbsolute(t)?t:W.join(n,t)}function Vt(n){return !!n&&typeof n=="object"&&!Array.isArray(n)}async function Se(n,t){let e=Dn(t);if(!e)return null;let r=Mn(n,e.file);if(!await _.pathExists(r))throw new g(`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=qn(r),s=Vt(o)?o:{default:o},i=e.exportName==="default"?s.default:s[e.exportName];if(i===void 0)throw new g(`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 It(n,t){if(typeof t!="string")return {isRef:!1,isFunction:!1,value:t};let e=await Se(n,t);return e?{isRef:!0,isFunction:typeof e.value=="function",value:e.value}:{isRef:!1,isFunction:!1,value:t}}async function je(n,t,e){if(typeof t!="string")return t;let r=await Se(n,t);if(!r)return t;if(!e.allow(r.value))throw new g(e.error(t),{hint:e.hint,example:e.example});return r.value}async function Ot(n,t){if(typeof t!="string")return t;let e=await Se(n,t);if(!e)return t;if(typeof e.value=="function"){let r=e.value();return await Ce(r)}return await Ce(e.value)}async function I(n,t,e){if(typeof t!="string")return t;let r=await Se(n,t);if(!r)return t;if(typeof r.value=="function"){let o=r.value(e);return await Ce(o)}return await Ce(r.value)}async function ve(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(!Vt(s))continue;let i={...s};if(i.choices!==void 0){i.choices=await Ot(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 g(`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 Ot(r,i.default)),i.when!==void 0&&(i.when=await je(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 je(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 je(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 je(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 qt(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 Dt(n,t,e={}){let r=(t??"").trim();if(r)return r;let o=e.projectRoot??process.cwd(),s=await I(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 g(`${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 $e(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 Je(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 Mt(){console.log(y.bold.blue(`
34
34
  \u{1F680} Jenkins CLI - Jenkins Deployment CLI
35
- `));let{config:n,jenkins:t}=await h(),[e,r]=await Promise.all([nt(),tt()]),o=()=>{process.exit(130);},s,i=n.job,a={};try{process.prependOnceListener("SIGINT",o);let p=await xe(n.configs,{projectRoot:n.projectRoot}),g=new Set(["branch","modes","mode","triggered_by"]);for(let $ of p){let I=String($?.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 b={type:"list",name:"branch",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u5206\u652F:",choices:e,default:e.indexOf(r)},S=[],L=[],R=[];for(let $ of p){let I=Number($.offset);if(Number.isFinite(I)&&I<=-2){S.push($);continue}if(I===-1){L.push($);continue}R.push($);}let C={};Object.assign(C,await oe.prompt(S,C)),Object.assign(C,await oe.prompt([b],C)),Object.assign(C,await oe.prompt(L,C));let Ke=String(n.projectRoot??"").trim()||process.cwd(),G=await O(Ke,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 sn={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([sn],C)),Object.assign(C,await oe.prompt(R,C)),s={branch:String(C.branch??""),modes:C.modes??[]},a=Ft(C,["branch","modes"]);let an=await O(Ke,n.job,C),Ye=String(an??"").trim();Ye&&(i=Ye);}catch(p){let g=p,b=g?.message?String(g.message):String(g),S=g?.name?String(g.name):"";Ce(p)&&process.exit(0),q(new d(`Prompt failed: ${b}`,{hint:S?`Error: ${S}`:""})),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 m=s.modes.map(async p=>{let g=en(`Triggering build for ${y.yellow(p)}...`).start();try{let b=await ge(t,i,{...a,branch:s.branch,mode:p});l(),g.succeed(`${y.green(p)} - Triggered! Queue: ${b}`);}catch(b){throw l(),g.fail(`${y.red(p)} - ${b.message}`),b}});try{await Promise.all(m);}catch{console.log(y.bold.red(`
35
+ `));let{config:n,jenkins:t}=await h(),[e,r]=await Promise.all([ct(),at()]),o=()=>{process.exit(130);},s,i=n.job,a={};try{process.prependOnceListener("SIGINT",o);let f=await ve(n.configs,{projectRoot:n.projectRoot}),d=new Set(["branch","modes","mode","triggered_by"]);for(let $ of f){let D=String($?.name??"").trim();if(d.has(D))throw new g(`configs name "${D}" 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=[],O=[],P=[];for(let $ of f){let D=Number($.offset);if(Number.isFinite(D)&&D<=-2){v.push($);continue}if(D===-1){O.push($);continue}P.push($);}let C={};Object.assign(C,await ce.prompt(v,C)),Object.assign(C,await ce.prompt([b],C)),Object.assign(C,await ce.prompt(O,C));let nt=String(n.projectRoot??"").trim()||process.cwd(),X=await I(nt,n.modes,C),Y=[];if(Array.isArray(X))Y=X.map($=>String($).trim()).filter(Boolean);else if(X!=null){let $=String(X).trim();$&&(Y=[$]);}Y.length===0&&Array.isArray(n.modes)&&(Y=n.modes);let yn={type:"checkbox",name:"modes",message:"\u8BF7\u9009\u62E9\u8981\u6253\u5305\u7684\u73AF\u5883:",choices:Y,validate:$=>$.length===0?"You must select at least one environment":!0};Object.assign(C,await ce.prompt([yn],C)),Object.assign(C,await ce.prompt(P,C)),s={branch:String(C.branch??""),modes:C.modes??[]},a=qt(C,["branch","modes"]);let kn=await I(nt,n.job,C),rt=String(kn??"").trim();rt&&(i=rt);}catch(f){let d=f,b=d?.message?String(d.message):String(d),v=d?.name?String(d.name):"";Je(f)&&process.exit(0),M(new g(`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 fe();u&&!("triggered_by"in a)&&(a.triggered_by=u);let m=s.modes.map(async f=>{let d=un(`Triggering build for ${y.yellow(f)}...`).start();try{let b=await ye(t,i,{...a,branch:s.branch,mode:f});l(),d.succeed(`${y.green(f)} - Triggered! Queue: ${b}`);}catch(b){throw l(),d.fail(`${y.red(f)} - ${b.message}`),b}});try{await Promise.all(m);}catch{console.log(y.bold.red(`
36
36
  \u274C Some builds failed. Check the output above.
37
- `)),process.exit(1);}}function Ut(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 h(),s=e.job?await Bt(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 h(),o=Array.isArray(e)?e:[String(e??"")],s=await Promise.all(o.map(async a=>{let c=E(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 w(n){return {[En.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 v(n,t){return w(U(n,t))}function J(n){let t=String(n??""),e=t.trim();if(!e)return w(y.gray("-"));let r=s=>w(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 w(y.cyan("BUILDING"));let e=String(n??"").toUpperCase();return w(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 w("-");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 w(o)}function Lt(n){let t=Number(n);if(!Number.isFinite(t)||t<0)return w("-");if(t<1024)return w(`${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 w(`${s} ${e[o]}`)}var Se=64;function Bn(n){return n!=null&&typeof n=="object"?n.value??n.name??n:n}function Ot(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 Vt(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 Wn(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=Vt(Ot(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(Bn);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 Un(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 Ln(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=Wn(s,e);if(!a?.length||o.length*a.length>Se)continue;let c=[];for(let l of o){if(!await Un(s,l)){c.push(l);continue}for(let u of a)c.push({...l,[i]:u});}c.length&&(o=c);}return o}function Tn(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 On(n){return n.split(`
37
+ `)),process.exit(1);}}function zt(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 h(),s=e.job?await Dt(r.job,e.job,{projectRoot:r.projectRoot}):void 0,i=await G(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 h(),o=Array.isArray(e)?e:[String(e??"")],s=await Promise.all(o.map(async a=>{let c=A(String(a),"id");try{return await re(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 w(n){return {[_n.inspect.custom]:()=>n}}function T(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 w(T(n,t))}function J(n){let t=String(n??""),e=t.trim();if(!e)return w(y.gray("-"));let r=s=>w(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 R(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 ue(n,t){if(t===!0)return w(y.cyan("BUILDING"));let e=String(n??"").toUpperCase();return w(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 me(n){let t=String(n??"");if(!t)return w("-");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 w(o)}function _t(n){let t=Number(n);if(!Number.isFinite(t)||t<0)return w("-");if(t<1024)return w(`${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 w(`${s} ${e[o]}`)}var Pe=64;function Qn(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 Gt(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>Pe?o.slice(0,Pe):o}function Gn(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=Gt(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(Qn);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 Kn(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 Xn(n,t,e){let r=await ve(n.configs,{projectRoot:t}),o=[{}];for(let s of r){let i=String(s.name??"").trim();if(!i)continue;let a=Gn(s,e);if(!a?.length||o.length*a.length>Pe)continue;let c=[];for(let l of o){if(!await Kn(s,l)){c.push(l);continue}for(let u of a)c.push({...l,[i]:u});}c.length&&(o=c);}return o}function Yn(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
38
  `).filter(t=>{let e=t.trim();return e&&!e.startsWith("//")}).join(`
39
- `)}function Vn(n){let t=Ot(n);if(!t)return [];t=On(t);let e=Vt(t),r=Tn(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 f of Tt(l))f&&o.add(f);let u;for(;u=i.exec(l);){let f=u[1];if(!f.includes("${"))continue;let m=f.match(a),p=m&&r.has(m[1])?r.get(m[1]):m?[]:e,g=f.split(/\$\{[^}]*\}/);if(g.length<2)continue;let b=g[0]??"",S=g.slice(1).join("");for(let L of p){let R=b+String(L)+S;R&&o.add(R);}}}if(o.size===0&&!t.includes("{"))for(let l of Tt(t))l&&o.add(l);return Array.from(o).sort((l,u)=>l.localeCompare(u,"en",{sensitivity:"base"}))}function Tt(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 In(n,t,e){let r=new Set;for(let i of Vn(n))r.add(i);let o=await Ln(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 d("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 Et(r,n.job);if(o.isRef&&o.isFunction){let i=await In(o.value,n,r);if(!i.length)throw new d("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 $e(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 It(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 qt(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 h(),s=e.job??$e("builds")??$e(),i=await k(r,s),c=(await dt(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:w(P(Number(u.timestamp??0))),parameters:It(u.parameters),url:J(String(u.url??""))})),l=j(c.map(u=>u.parameters));console.table(c.map(u=>({...u,parameters:v(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 h(),s=e.job??$e("active")??$e("builds"),i=await k(r,s),c=((e.all?await de(o):await ee(o,i))??[]).map(u=>({...e.all?{job:w(String(u.job??""))}:{},number:u.number,date:w(u.timestamp?P(Number(u.timestamp)):"-"),parameters:It(u.parameters),url:J(String(u.url??""))})),l=j(c.map(u=>u.parameters));console.table(c.map(u=>({...u,parameters:v(u.parameters,l)})));});}function qn(n){let t=String(n?.authorEmail??"").trim(),e=t?t.split("@")[0]??"":"";return e||t}function Dt(n){let t=[];for(let e of n){let r=Number(e?.number),o=Number.isInteger(r)?`#${r}`:w("-"),s=e?.timestamp?P(Number(e.timestamp)):"-",i=e?.changeSet?.items??[];if(!i.length){t.push({build:o,date:w(s),author:w("-"),message:w("-")});continue}for(let a of i){let c=qn(a),l=String(a?.msg??"").trim();t.push({build:o,date:w(s),author:w(c||"-"),message:w(l||"-")});}}return t}function Mt(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 h(),s=await k(r,e.job);if(t){let c=E(t,"id"),l=await he(o,s,c);if(e.raw){console.log(JSON.stringify({build:l.number,changeSet:l.changeSet??{}},null,2));return}console.table(Dt([l]));return}let i=Math.max(1,E(e.limit,"limit")),a=await gt(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(Dt(a));});}function zt(){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 _t(n){let t=n.command("console").description("Fetch build console log"),e=async(r,o,s,i)=>{if(!i.follow){let l=await wt(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:f}=await Jt(r,o,s,a);if(l.length>0&&(process.stdout.write(l),a=u),c&&l.length>0&&(c=!1),l.length>0&&f)continue;if(!f){let p=(await he(r,o,s)).result||"UNKNOWN",g=p==="SUCCESS"?y.green:y.red;console.log(g(`
40
- --- Build Finished: ${p} ---`));break}await new Promise(m=>setTimeout(m,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 h(),a=o.job??zt();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 c=await k(s,a),l=E(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 h(),i=r.job??zt();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=await k(o,i),c=String(r.status??"any").toLowerCase();if(!["any","success","failed"].includes(c))throw new d(`Invalid status: ${r.status}.`,{hint:"Use success | failed | any.",example:"jenkins-cli console last -s success"});let l=await we(s,a),u=c==="success"?l?.lastSuccessfulBuild:c==="failed"?l?.lastFailedBuild:l?.lastBuild,f=Number(u?.number);if(!f){let m=c==="success"?"successful":c==="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,f,r);});}function Ht(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 h(),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,E(t,"id")),console.log(y.green("Stop requested"));});}function Qt(n){n.command("params").description("Show job parameter definitions").option("-j, --job <job>","Specify job").action(async t=>{let{config:e,jenkins:r}=await h(),o=await k(e,t.job),s=await ht(r,o),i=j(s.map(a=>a?.name));console.table((s??[]).map(a=>({name:v(a?.name??"",i),type:w(String(a?.type??"")),description:w(String(a?.description??"")),default:w(a?.default===void 0?"-":String(a.default)),choices:w(Array.isArray(a?.choices)?a.choices.join(", "):a?.choices??"-")})));});}function Gt(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 h(),r=await ft(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,f)=>{let m=u.length;if(m>=f)return u;let p=f-m,g=Math.floor(p/2),b=p-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=w(s(a,c));for(let u of Object.keys(o).sort((f,m)=>f.localeCompare(m,"en"))){if(u==="name")continue;let f=o[u],m=u.toLowerCase();if((m==="url"||m.endsWith("url")||m.includes("url"))&&(typeof f=="string"||typeof f=="number")){l[u]=J(String(f));continue}let g=f==null?"":typeof f=="object"?JSON.stringify(f):String(f);l[u]=w(g);}console.table([l]);});}function Kt(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 h(),s=await k(r,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 Me(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 h(),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 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 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 c=[];for(let l of a){let u=String(l?.name??"").trim();if(!u)continue;let f=await Me(o,u),m=z.join(s,u,"config.xml");await promises.mkdir(z.dirname(m),{recursive:!0}),await promises.writeFile(m,f),c.push(m);}console.log(`Backup completed. ${c.length} file(s) saved to ${z.resolve(s)}`);});}function Yt(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 h(),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:v(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 h(),s=await k(r,e.job),i=await we(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:w(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:w(a?.number?`#${a.number}`:"-"),lastResult:se(a?.result,a?.building),"lastDuration(s)":a?.duration===void 0?"-":Number((Number(a.duration)/1e3).toFixed(3)),lastDate:w(a?.timestamp?P(Number(a.timestamp)):"-"),lastCompleted:w(c?.number?`#${c.number}`:"-"),lastCompletedResult:se(c?.result,!1),lastCompletedDate:w(c?.timestamp?P(Number(c.timestamp)):"-")}]);});}function zn(n){return n.split(",").map(t=>t.trim()).filter(Boolean)}function _n(n){let t=n.split("=");if(t.length<2)throw new d(`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 d(`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 Xt(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(zn(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 h(),o=await k(e,t.job),s=String(t.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=(t.mode??[]).map(String).map(p=>p.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 c={};for(let p of t.param??[]){let{key:g,value:b}=_n(String(p));c[g]=b;}let l=await le();l&&!("triggered_by"in c)&&(c.triggered_by=l),console.log();let u=!1,f=()=>{u||(u=!0,console.log());},m=a.map(async p=>{let g=en(`Triggering build for ${y.yellow(p)}...`).start();try{let b=await ge(r,o,{...c,branch:s,mode:p});f(),g.succeed(`${y.green(p)} - Triggered! Queue: ${b}`);}catch(b){throw f(),g.fail(`${y.red(p)} - ${b.message}`),b}});try{await Promise.all(m);}catch{console.log(y.bold.red(`
39
+ `)}function er(n){let t=Qt(n);if(!t)return [];t=Zn(t);let e=Gt(t),r=Yn(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 p of Ht(l))p&&o.add(p);let u;for(;u=i.exec(l);){let p=u[1];if(!p.includes("${"))continue;let m=p.match(a),f=m&&r.has(m[1])?r.get(m[1]):m?[]:e,d=p.split(/\$\{[^}]*\}/);if(d.length<2)continue;let b=d[0]??"",v=d.slice(1).join("");for(let O of f){let P=b+String(O)+v;P&&o.add(P);}}}if(o.size===0&&!t.includes("{"))for(let l of Ht(t))l&&o.add(l);return Array.from(o).sort((l,u)=>l.localeCompare(u,"en",{sensitivity:"base"}))}function Ht(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 tr(n,t,e){let r=new Set;for(let i of er(n))r.add(i);let o=await Xn(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>=Pe};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 g("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 It(r,n.job);if(o.isRef&&o.isFunction){let i=await tr(o.value,n,r);if(!i.length)throw new g("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 ce.prompt([{type:"list",name:"job",message:"\u8BF7\u9009\u62E9\u8981\u64CD\u4F5C\u7684 Job:",choices:i}]);return String(a??"").trim()}let s=await I(r,n.job,{});return String(s??n.job??"").trim()||String(n.job??"")}function Re(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 Kt(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 Xt(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 h(),s=e.job??Re("builds")??Re(),i=await k(r,s),c=(await Ct(o,i,{limit:Number(e.limit)})).map(u=>({number:u.number,result:ue(u.result,u.building),building:u.building,"duration(s)":Number((Number(u.duration??0)/1e3).toFixed(3)),date:w(R(Number(u.timestamp??0))),parameters:Kt(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 h(),s=e.job??Re("active")??Re("builds"),i=await k(r,s),c=((e.all?await be(o):await ne(o,i))??[]).map(u=>({...e.all?{job:w(String(u.job??""))}:{},number:u.number,date:w(u.timestamp?R(Number(u.timestamp)):"-"),parameters:Kt(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 nr(n){let t=String(n?.authorEmail??"").trim(),e=t?t.split("@")[0]??"":"";return e||t}function Yt(n){let t=[];for(let e of n){let r=Number(e?.number),o=Number.isInteger(r)?`#${r}`:w("-"),s=e?.timestamp?R(Number(e.timestamp)):"-",i=e?.changeSet?.items??[];if(!i.length){t.push({build:o,date:w(s),author:w("-"),message:w("-")});continue}for(let a of i){let c=nr(a),l=String(a?.msg??"").trim();t.push({build:o,date:w(s),author:w(c||"-"),message:w(l||"-")});}}return t}function Zt(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 h(),s=await k(r,e.job);if(t){let c=A(t,"id"),l=await xe(o,s,c);if(e.raw){console.log(JSON.stringify({build:l.number,changeSet:l.changeSet??{}},null,2));return}console.table(Yt([l]));return}let i=Math.max(1,A(e.limit,"limit")),a=await St(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(Yt(a));});}function en(){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 tn(n){let t=n.command("console").description("Fetch build console log"),e=async(r,o,s,i)=>{if(!i.follow){let l=await vt(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:p}=await Ut(r,o,s,a);if(l.length>0&&(process.stdout.write(l),a=u),c&&l.length>0&&(c=!1),l.length>0&&p)continue;if(!p){let f=(await xe(r,o,s)).result||"UNKNOWN",d=f==="SUCCESS"?y.green:y.red;console.log(d(`
40
+ --- Build Finished: ${f} ---`));break}await new Promise(m=>setTimeout(m,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 h(),a=o.job??en();if(a==="")throw new g("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 h(),i=r.job??en();if(i==="")throw new g("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 g(`Invalid status: ${r.status}.`,{hint:"Use success | failed | any.",example:"jenkins-cli console last -s success"});let l=await ke(s,a),u=c==="success"?l?.lastSuccessfulBuild:c==="failed"?l?.lastFailedBuild:l?.lastBuild,p=Number(u?.number);if(!p){let m=c==="success"?"successful":c==="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 nn(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 h(),s=await k(r,e.job);if(e.ALL){let[i,a]=await Promise.all([G(o),be(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 re(o,u);for(let u of l)await oe(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([G(o,s),ne(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 re(o,u);for(let u of l)await oe(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 oe(o,s,A(t,"id")),console.log(y.green("Stop requested"));});}function rn(n){n.command("params").description("Show job parameter definitions").option("-j, --job <job>","Specify job").action(async t=>{let{config:e,jenkins:r}=await h(),o=await k(e,t.job),s=await $t(r,o),i=j(s.map(a=>a?.name));console.table((s??[]).map(a=>({name:S(a?.name??"",i),type:w(String(a?.type??"")),description:w(String(a?.description??"")),default:w(a?.default===void 0?"-":String(a.default)),choices:w(Array.isArray(a?.choices)?a.choices.join(", "):a?.choices??"-")})));});}function on(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 h(),r=await ht(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,p)=>{let m=u.length;if(m>=p)return u;let f=p-m,d=Math.floor(f/2),b=f-d;return `${" ".repeat(d)}${u}${" ".repeat(b)}`},i=o.name??o.fullName??o.id??"",a=i==null?"":String(i),c=Math.max(20,a.length),l={};l.name=w(s(a,c));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")){l[u]=J(String(p));continue}let d=p==null?"":typeof p=="object"?JSON.stringify(p):String(p);l[u]=w(d);}console.table([l]);});}function sn(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 h(),s=await k(r,e.job),i=String(e.format??"xml").toLowerCase();if(i!=="xml"&&i!=="json")throw new g(`Invalid format: ${e.format}.`,{hint:"Use xml or json.",example:"jenkins-cli config show -f json"});let a=await se(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 h(),s=String(e.destination??"").trim();if(!s)throw new g("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 g("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 V(o):i?(await V(o)).filter(l=>$e(String(l.name??""),i)):[{name:await k(r,e.job)}];if(a.length===0)throw new g(`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 p=await se(o,u),m=W.join(s,u,"config.xml");await promises.mkdir(W.dirname(m),{recursive:!0}),await promises.writeFile(m,p),c.push(m);}console.log(`Backup completed. ${c.length} file(s) saved to ${W.resolve(s)}`);});}function an(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 h(),o=await V(r),s=(e.search??"").trim(),i=s?o.filter(l=>$e(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:me(l.color),url:J(T(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 h(),s=await k(r,e.job),i=await ke(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:w(String(i?.name??"")),url:J(String(i?.url??"")),color:me(i?.color),buildable:i?.buildable,inQueue:i?.inQueue,nextBuildNumber:i?.nextBuildNumber,healthScore:l[0]?.score??"-",lastBuild:w(a?.number?`#${a.number}`:"-"),lastResult:ue(a?.result,a?.building),"lastDuration(s)":a?.duration===void 0?"-":Number((Number(a.duration)/1e3).toFixed(3)),lastDate:w(a?.timestamp?R(Number(a.timestamp)):"-"),lastCompleted:w(c?.number?`#${c.number}`:"-"),lastCompletedResult:ue(c?.result,!1),lastCompletedDate:w(c?.timestamp?R(Number(c.timestamp)):"-")}]);});}function ir(n){return n.split(",").map(t=>t.trim()).filter(Boolean)}function sr(n){let t=n.split("=");if(t.length<2)throw new g(`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 g(`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 cn(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(ir(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 h(),o=await k(e,t.job),s=String(t.branch??"").trim();if(!s)throw new g("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(f=>f.trim()).filter(Boolean),a=Array.from(new Set(i));if(a.length===0)throw new g("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 f of t.param??[]){let{key:d,value:b}=sr(String(f));c[d]=b;}let l=await fe();l&&!("triggered_by"in c)&&(c.triggered_by=l),console.log();let u=!1,p=()=>{u||(u=!0,console.log());},m=a.map(async f=>{let d=un(`Triggering build for ${y.yellow(f)}...`).start();try{let b=await ye(r,o,{...c,branch:s,mode:f});p(),d.succeed(`${y.green(f)} - Triggered! Queue: ${b}`);}catch(b){throw p(),d.fail(`${y.red(f)} - ${b.message}`),b}});try{await Promise.all(m);}catch{console.log(y.bold.red(`
41
41
  \u274C Some builds failed. Check the output above.
42
- `)),process.exit(1);}});}function Zt(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 h(),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:v(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 h(),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:v(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 h();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 h();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 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 h(),[o,s]=await Promise.all([T(r),Q(r,e)]);if(!o.length){console.log("No jobs found.");return}o.sort((m,p)=>String(m?.name??"").localeCompare(String(p?.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)})),c=await oe.prompt([{type:"checkbox",name:"jobs",message:`Select jobs for view "${e}"`,choices:a,pageSize:20}]),l=new Set((c.jobs??[]).map(m=>String(m).trim())),u=[...l].filter(m=>!i.has(m)),f=[...i].filter(m=>m&&!l.has(m));if(!u.length&&!f.length){console.log(y.yellow("No changes to apply."));return}for(let m of u)await Ie(r,e,m);for(let m of f)await qe(r,e,m);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 h();await ut(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 h();await mt(r,e),console.log(y.green(`View "${e}" deleted.`));});}function Yn(n){return w(n?P(n):"-")}function Ae(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 Qe(n,t,e){let r=t?.parent?.opts()?.job,o=Ae(e)??Ae(t?.name())??Ae("ws")??Ae("workspace");return n??r??o}function tn(n){return n.replace(/^\/+/,"").replace(/\/+$/,"")}function Xn(n,t){let e=tn(n),r=tn(t);return e?r?r===e||r.startsWith(`${e}/`)?r:`${e}/${r}`:e:r||""}function Zn(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 A(n){try{return await n()}catch(t){if(t?.response?.status===404)return null;throw t}}async function er(n,t){try{return await pt(n,t)}catch{return}}async function tr(n,t){try{return await Rt(n,t)}catch{return}}async function nr(n){try{return await re(n,{raw:!1})}catch{return []}}async function Ge(n,t,e){let r=await e.direct(t);if(r!==null)return r;let o=await er(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 tr(n,t);if(s){let a=await e.byViewName(s);if(a!==null)return a}let i=await nr(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 rr(n,t,e){return await Ge(n,t,{direct:r=>A(()=>De(n,r,{path:e})),byViewName:r=>A(()=>bt(n,r,t,{path:e})),byViewUrl:r=>A(()=>yt(n,r,t,{path:e})),beforeView:async()=>{if(e)return null;let r=t.split("/").pop();return r?await A(()=>De(n,t,{path:r})):null}})}async function or(n,t,e){return await Ge(n,t,{direct:r=>A(()=>kt(n,r,e)),byViewName:r=>A(()=>jt(n,r,t,e)),byViewUrl:r=>A(()=>St(n,r,t,e))})}async function ir(n,t,e){return await Ge(n,t,{direct:r=>A(()=>xt(n,r,e)),byViewName:r=>A(()=>Ct(n,r,t,e)),byViewUrl:r=>A(()=>$t(n,r,t,e))})}function nn(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 h(),i=Qe(e.job,r,"workspace"),a=await k(o,i),c=String(e.path??"").trim(),l=en(`Loading workspace for job "${a}"...`).start(),u=await rr(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 f=u.map(g=>({name:String(g.name??""),typeRaw:g.type,size:Lt(g.size),modified:Yn(g.modified),path:Xn(c,String(g.path??g.name??""))})),m=j(f.map(g=>g.name)),p=j(f.map(g=>g.path));console.table(f.map(g=>{let b=U(String(g.name??""),m),S=String(g.typeRaw??"");return {name:S==="dir"?w(y.bold.cyan(b)):S==="file"?w(y.green(b)):w(y.gray(b)),size:g.size,modified:g.modified,path:v(String(g.path??""),p)}}));}),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 h(),a=Qe(r.job,o,"cat"),c=await k(s,a),l=String(e??"").trim();if(!l)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(),f=r.open===!0;if(u||f){let p=await ir(i,c,l);if(p===null){console.log(y.yellow(`Workspace file not found: ${l} (job: ${c}).`));return}let g=u||z.join(Gn.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,p.data),f?(Zn(g),console.log(y.green(`Opened: ${g}`))):console.log(y.green(`Saved to: ${g}`));return}let m=await or(i,c,l);if(m===null){console.log(y.yellow(`Workspace file not found: ${l} (job: ${c}).`));return}process.stdout.write(String(m));}),t.command("wipe").description("Wipe out current workspace").option("-j, --job <job>","Specify job").action(async(e,r)=>{let{config:o,jenkins:s}=await h(),i=Qe(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=en(`Wiping workspace for job "${a}"...`).start();try{await vt(s,a),c.succeed(`Workspace wiped for job: ${a}`);}catch(l){throw c.fail(`Failed to wipe workspace for job: ${a}`),l}});}function rn(n){qt(n),Mt(n),Kt(n),Yt(n),_t(n),Qt(n),Ut(n),Ht(n),Xt(n),Gt(n),Zt(n),nn(n);}function on(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(et).version(Ze,"-v, --version").helpOption("-h, --help").option("--debug","Print Jenkins request params").allowExcessArguments(!1).configureHelp({sortSubcommands:!0,sortOptions:!0}).action(Wt);rn(program);on(program,"--debug","Print Jenkins request params");async function sr(){try{await program.parseAsync(process.argv);}catch(n){q(n);}}sr();
42
+ `)),process.exit(1);}});}function ln(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 h(),o=await ae(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(T(c.url,a))})));}),t.command("show").description("Show jobs in a view").argument("<name>","View name").action(async e=>{let{jenkins:r}=await h(),o=await K(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:me(a.color),url:J(T(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 h();if((await K(o,e)).some(i=>i.name===r)){console.log(y.yellow(`Job "${r}" is already in view "${e}".`));return}await _e(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 h();if(!(await K(o,e)).some(i=>i.name===r))throw new g(`Job "${r}" is not in view "${e}".`,{hint:'Use "jc view show <name>" to see jobs in this view.'});await He(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 h(),[o,s]=await Promise.all([V(r),K(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)})),c=await ce.prompt([{type:"checkbox",name:"jobs",message:`Select jobs for view "${e}"`,choices:a,pageSize:20}]),l=new Set((c.jobs??[]).map(m=>String(m).trim())),u=[...l].filter(m=>!i.has(m)),p=[...i].filter(m=>m&&!l.has(m));if(!u.length&&!p.length){console.log(y.yellow("No changes to apply."));return}for(let m of u)await _e(r,e,m);for(let m of p)await He(r,e,m);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 h();await gt(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 h();await wt(r,e),console.log(y.green(`View "${e}" deleted.`));});}function mr(n){return w(n?R(n):"-")}function Be(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 Ye(n,t,e){let r=t?.parent?.opts()?.job,o=Be(e)??Be(t?.name())??Be("ws")??Be("workspace");return n??r??o}function mn(n){return n.replace(/^\/+/,"").replace(/\/+$/,"")}function pr(n,t){let e=mn(n),r=mn(t);return e?r?r===e||r.startsWith(`${e}/`)?r:`${e}/${r}`:e:r||""}function fr(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 dr(n,t){try{return await jt(n,t)}catch{return}}async function gr(n,t){try{return await Lt(n,t)}catch{return}}async function wr(n){try{return await ae(n,{raw:!1})}catch{return []}}async function Ze(n,t,e){let r=await e.direct(t);if(r!==null)return r;let o=await dr(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 gr(n,t);if(s){let a=await e.byViewName(s);if(a!==null)return a}let i=await wr(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 hr(n,t,e){return await Ze(n,t,{direct:r=>N(()=>Qe(n,r,{path:e})),byViewName:r=>N(()=>Jt(n,r,t,{path:e})),byViewUrl:r=>N(()=>Pt(n,r,t,{path:e})),beforeView:async()=>{if(e)return null;let r=t.split("/").pop();return r?await N(()=>Qe(n,t,{path:r})):null}})}async function br(n,t,e){return await Ze(n,t,{direct:r=>N(()=>Rt(n,r,e)),byViewName:r=>N(()=>Et(n,r,t,e)),byViewUrl:r=>N(()=>Ft(n,r,t,e))})}async function yr(n,t,e){return await Ze(n,t,{direct:r=>N(()=>Nt(n,r,e)),byViewName:r=>N(()=>At(n,r,t,e)),byViewUrl:r=>N(()=>Bt(n,r,t,e))})}function pn(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 h(),i=Ye(e.job,r,"workspace"),a=await k(o,i),c=String(e.path??"").trim(),l=un(`Loading workspace for job "${a}"...`).start(),u=await hr(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 p=u.map(d=>({name:String(d.name??""),typeRaw:d.type,size:_t(d.size),modified:mr(d.modified),path:pr(c,String(d.path??d.name??""))})),m=j(p.map(d=>d.name)),f=j(p.map(d=>d.path));console.table(p.map(d=>{let b=T(String(d.name??""),m),v=String(d.typeRaw??"");return {name:v==="dir"?w(y.bold.cyan(b)):v==="file"?w(y.green(b)):w(y.gray(b)),size:d.size,modified:d.modified,path:S(String(d.path??""),f)}}));}),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 h(),a=Ye(r.job,o,"cat"),c=await k(s,a),l=String(e??"").trim();if(!l)throw new g("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 yr(i,c,l);if(f===null){console.log(y.yellow(`Workspace file not found: ${l} (job: ${c}).`));return}let d=u||W.join(lr.tmpdir(),W.basename(l)||"file");try{u&&await _.pathExists(u)&&(await _.stat(u)).isDirectory()&&(d=W.join(u,W.basename(l)||"file"));}catch{}await _.ensureDir(W.dirname(d)),await _.writeFile(d,f.data),p?(fr(d),console.log(y.green(`Opened: ${d}`))):console.log(y.green(`Saved to: ${d}`));return}let m=await br(i,c,l);if(m===null){console.log(y.yellow(`Workspace file not found: ${l} (job: ${c}).`));return}process.stdout.write(String(m));}),t.command("wipe").description("Wipe out current workspace").option("-j, --job <job>","Specify job").action(async(e,r)=>{let{config:o,jenkins:s}=await h(),i=Ye(e.job,r,"wipe"),a=await k(o,i);try{if(!(await ce.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(Je(l)){console.log(y.yellow("Cancelled."));return}throw l}let c=un(`Wiping workspace for job "${a}"...`).start();try{await Wt(s,a),c.succeed(`Workspace wiped for job: ${a}`);}catch(l){throw c.fail(`Failed to wipe workspace for job: ${a}`),l}});}function fn(n){return n.active===!0||n.enabled===!0}function dn(n,t){return String(n.shortName??"").localeCompare(String(t.shortName??""),"en",{sensitivity:"base"})}function gn(n){let t=n.command("plugin").description("Plugin operations");t.command("show").description("Show used plugins").action(async()=>{let{jenkins:e}=await h(),o=(await ie(e)??[]).filter(a=>fn(a)&&String(a.shortName??"").trim());if(o.sort(dn),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?w("-"):a.enabled,active:a.active===void 0?w("-"):a.active,update:a.hasUpdate===void 0?w("-"):a.hasUpdate})));}),t.command("backup").description("Backup plugins list").requiredOption("-d, --destination <path>","Destination folder").action(async e=>{let{jenkins:r}=await h(),o=String(e.destination??"").trim();if(!o)throw new g("Destination is required.",{hint:"Provide a destination folder with -d or --destination.",example:"jenkins-cli plugin backup -d ./jenkins-backup"});let i=(await ie(r)??[]).filter(l=>fn(l)&&String(l.shortName??"").trim());if(i.sort(dn),i.length===0)throw new g("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=W.join(o,"plugins.json"),c=W.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(),p=String(l.version??"").trim();return p?`${u}:${p}`:u}).filter(Boolean).join(`
43
+ `)}
44
+ `),console.log(`Backup completed. ${i.length} plugin(s) saved to ${W.resolve(o)}`);});}function kr(n){return n.active===!0||n.enabled===!0}function xr(n){let t=String(n??"").trim();return t?!(/<credentials>/i.test(t)||/com\.cloudbees\.plugins\.credentials\.impl/i.test(t)||/org\.jenkinsci\.plugins\./i.test(t)):!0}function wn(n){n.command("backup").description("Backup Jenkins global config, jobs, plugins, and credentials").requiredOption("-d, --destination <path>","Destination folder").action(async t=>{let{jenkins:e}=await h(),r=String(t.destination??"").trim();if(!r)throw new g("Destination is required.",{hint:"Provide a destination folder with -d or --destination.",example:"jenkins-cli backup -d ./jenkins-backup"});await promises.mkdir(r,{recursive:!0});let o=W.resolve(r),s=[],i=await bt(e);await promises.writeFile(W.join(o,"config.xml"),i);let a=await xt(e,{depth:8}),c=0;for(let m of a){let f=await se(e,m),d=W.join(o,"jobs",...m.split("/"),"config.xml");await promises.mkdir(W.dirname(d),{recursive:!0}),await promises.writeFile(d,f),c+=1;}let u=(await ie(e)??[]).filter(m=>kr(m)&&String(m.shortName??"").trim()).sort((m,f)=>String(m.shortName??"").localeCompare(String(f.shortName??""),"en",{sensitivity:"base"})),p=W.join(o,"plugins");await promises.mkdir(p,{recursive:!0}),await promises.writeFile(W.join(p,"plugins.json"),JSON.stringify(u,null,2)),await promises.writeFile(W.join(p,"plugins.txt"),`${u.map(m=>{let f=String(m.shortName??"").trim(),d=String(m.version??"").trim();return d?`${f}:${d}`:f}).filter(Boolean).join(`
45
+ `)}
46
+ `);try{let m=await yt(e);await promises.writeFile(W.join(o,"credentials.xml"),m);try{let f=await kt(e);await promises.writeFile(W.join(o,"credentials.json"),JSON.stringify(f,null,2));}catch{}xr(m)&&s.push("credentials.xml looks like domain-only data. This often means no readable credentials in system store or missing credentials view permission.");}catch(m){let f=m?.response?.status;s.push(f?`credentials.xml backup skipped (HTTP ${f}, possibly missing permission or plugin).`:"credentials.xml backup skipped due to request failure.");}if(console.log(`Backup completed: ${o}`),console.log("- config.xml: saved"),console.log(`- jobs/: ${c} job config file(s)`),console.log(`- plugins/: ${u.length} plugin(s)`),console.log(`- credentials.xml: ${s.some(m=>m.startsWith("credentials.xml backup skipped"))?"skipped":"saved"}`),s.length){console.log(`
47
+ Warnings:`);for(let m of s)console.log(`- ${m}`);}});}function hn(n){wn(n),Xt(n),Zt(n),sn(n),an(n),tn(n),rn(n),gn(n),zt(n),nn(n),cn(n),on(n),ln(n),pn(n);}function bn(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(st).version(it,"-v, --version").helpOption("-h, --help").option("--debug","Print Jenkins request params").allowExcessArguments(!1).configureHelp({sortSubcommands:!0,sortOptions:!0}).action(Mt);hn(program);bn(program,"--debug","Print Jenkins request params");async function jr(){try{await program.parseAsync(process.argv);}catch(n){M(n);}}jr();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ts-org/jenkins-cli",
3
- "version": "3.1.1",
3
+ "version": "3.1.3",
4
4
  "description": "Jenkins deployment CLI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",