@newbeebox/newbeebox-app-engine-cli 1.0.0 → 1.0.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.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # nae —— NewBee App Engine CLI
2
2
 
3
- 平台命令行客户端。**参数直达、JSON 输出**,既可人用,也便于 CodeAgent 编排调用。
3
+ 平台命令行客户端。**参数直达、默认人类可读**,加 `-o json` 即出 JSON,便于脚本/管道里解析。
4
4
 
5
- > 📖 **完整文档(含 Agent 用法范例 / 调试技巧)**:<https://workshop.newbeebox.com/app_engine/documents/nae-cli/>
5
+ > 📖 **完整文档(含用法范例 / 调试技巧)**:<https://workshop.newbeebox.com/app_engine/documents/nae-cli/>
6
6
  > markdown 原文:<https://workshop.newbeebox.com/app_engine/documents/nae-cli/md/>
7
7
 
8
8
  ---
@@ -11,7 +11,6 @@
11
11
 
12
12
  ```bash
13
13
  npm i -g @newbeebox/newbeebox-app-engine-cli
14
- # 或在仓库内:npm install && npm link
15
14
  ```
16
15
 
17
16
  需要 Node ≥ 18(用到内置 `fetch`)。
@@ -32,7 +31,7 @@ nae login
32
31
  nae login --token nae_xxxxxxxx
33
32
  ```
34
33
 
35
- CI / Agent 场景免登录,直接用环境变量(优先级高于本地配置):
34
+ CI / 脚本场景免登录,直接用环境变量(优先级高于本地配置):
36
35
 
37
36
  ```bash
38
37
  export NAE_TOKEN=nae_xxxxxxxx
@@ -44,13 +43,13 @@ nae apps
44
43
 
45
44
  ---
46
45
 
47
- ## 输出契约(给 Agent)
46
+ ## 输出契约
48
47
 
49
- - **stdout**:只放数据,成功结果一律 JSON(默认美化,`-c/--compact` 输出单行)。日志命令直出文本行。
48
+ - **stdout**:默认人类可读文本;加 `-o json` 出美化 JSON、`-c/--compact` 出紧凑单行 JSON。`logs`/`exec` 始终直出原始文本。
50
49
  - **stderr**:只放给人看的提示、进度、错误。
51
50
  - **退出码**:`0` 成功;`1` 业务错误;`2` 未登录/密钥失效;`3` 网络不可达。
52
51
 
53
- 所以 Agent 可以安全地 `nae apps 2>/dev/null | jq '.[].AppID'`。
52
+ 所以脚本里可以安全地 `nae apps -o json 2>/dev/null | jq '.[].AppID'`。
54
53
 
55
54
  ---
56
55
 
@@ -115,14 +114,13 @@ nae create --kind template --app-id myredis \
115
114
  | `nae logs <appid> [--pod P] [--container C] [--follow] [--tail N]` | 日志(缺省自动取首个 Pod) |
116
115
  | `nae exec <appid> -- <cmd...>` | 在容器内执行一条命令(非交互;输出与退出码透传) |
117
116
 
118
- ### 模板 / 密钥
117
+ ### 模板
119
118
 
120
119
  | 命令 | 说明 |
121
120
  |------|------|
122
121
  | `nae templates` | 可用模板目录 |
123
- | `nae keys` | 我的 CLI 密钥列表 |
124
- | `nae keys:create --name <名> [--days 30\|60\|90\|0]` | 自建密钥(上限 5 条,`0`=永久) |
125
- | `nae keys:revoke <id>` | 吊销我的密钥 |
122
+
123
+ > CLI 密钥的创建/查看/吊销仅在网页端「CLI 配置」页操作,命令行不提供。
126
124
 
127
125
  ---
128
126
 
@@ -130,6 +128,7 @@ nae create --kind template --app-id myredis \
130
128
 
131
129
  | 选项 | 说明 |
132
130
  |------|------|
131
+ | `-o, --output <format>` | 输出格式:`text`(默认,人类可读)/ `json` |
132
+ | `-c, --compact` | 紧凑 JSON(单行,隐含 `-o json`) |
133
133
  | `--base-url <url>` | 覆盖平台地址(亦可 `NAE_BASE_URL`) |
134
134
  | `--token <token>` | 覆盖访问密钥(亦可 `NAE_TOKEN`) |
135
- | `-c, --compact` | 紧凑 JSON(单行) |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@newbeebox/newbeebox-app-engine-cli",
3
- "version": "1.0.0",
4
- "description": "NewBee App Engine 命令行客户端(nae)——参数直达、JSON 输出,便于 CodeAgent 调用平台能力。",
3
+ "version": "1.0.3",
4
+ "description": "NewBee App Engine 命令行客户端(nae)——参数直达、默认人类可读(加 -o json 出 JSON),便于在终端使用与脚本/管道里解析。",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "nae": "src/index.js"
@@ -20,7 +20,6 @@
20
20
  "paas",
21
21
  "kubernetes",
22
22
  "deploy",
23
- "codeagent",
24
23
  "nae"
25
24
  ],
26
25
  "homepage": "https://workshop.newbeebox.com/app_engine/documents/nae-cli/",
package/src/config.js CHANGED
@@ -10,7 +10,7 @@ const CONFIG_DIR = join(homedir(), '.nae')
10
10
  const CONFIG_FILE = join(CONFIG_DIR, 'config.json')
11
11
 
12
12
  // load 读配置;文件不存在/损坏一律回落到默认(不抛错,让首次使用顺畅)。
13
- // 环境变量优先级最高:NAE_BASE_URL / NAE_TOKEN 覆盖文件值(CI/agent 免登录直跑)。
13
+ // 环境变量优先级最高:NAE_BASE_URL / NAE_TOKEN 覆盖文件值(CI/脚本 免登录直跑)。
14
14
  export function load() {
15
15
  let cfg = { baseUrl: DEFAULT_BASE_URL, token: '' }
16
16
  try {
package/src/index.js CHANGED
@@ -13,11 +13,12 @@ const program = new Command()
13
13
 
14
14
  program
15
15
  .name('nae')
16
- .description('NewBee App Engine CLI —— 参数直达、JSON 输出,便于 CodeAgent 调用')
17
- .version('1.0.0')
16
+ .description('NewBee App Engine CLI —— 默认人类可读,-o json 输出 JSON 供脚本解析')
17
+ .version('1.0.3')
18
18
  .option('--base-url <url>', '覆盖平台地址(也可用环境变量 NAE_BASE_URL)')
19
19
  .option('--token <token>', '覆盖访问密钥(也可用环境变量 NAE_TOKEN)')
20
- .option('-c, --compact', '紧凑 JSON 输出(单行)')
20
+ .option('-o, --output <format>', '输出格式:text(默认,人类可读)| json', 'text')
21
+ .option('-c, --compact', '紧凑 JSON 输出(单行,隐含 -o json)')
21
22
 
22
23
  // cfg 取合并了全局 flag 的配置。
23
24
  function cfg() {
@@ -28,9 +29,18 @@ function cfg() {
28
29
  return c
29
30
  }
30
31
 
31
- // emit 按 --compact 输出数据。
32
+ // emit 按输出格式渲染数据:
33
+ // 默认 text(人类可读);-o json 出美化 JSON;-c/--compact 出紧凑 JSON(隐含 json)。
32
34
  function emit(value) {
33
- out.data(value, { pretty: !program.opts().compact })
35
+ const o = program.opts()
36
+ const fmt = o.compact ? 'json' : o.output
37
+ if (fmt === 'json') {
38
+ out.data(value, { pretty: !o.compact })
39
+ } else if (fmt === 'text') {
40
+ out.human(value)
41
+ } else {
42
+ throw new Error(`未知输出格式:${fmt}(支持 text | json)`)
43
+ }
34
44
  }
35
45
 
36
46
  // run 包裹命令体,集中处理三类错误:
@@ -164,8 +174,31 @@ program
164
174
  .option('--storage <size>', '模板应用:持久卷大小,如 10Gi')
165
175
  .option('--replicas <n>', '副本数', (v) => parseInt(v, 10))
166
176
  .option('--env <json>', '环境变量 JSON,如 \'{"KEY":"val"}\'')
167
- .option('--config <json>', '模板参数 JSON(密码/持久化模式等)')
177
+ .option(
178
+ '--config <json>',
179
+ "模板应用的服务特性参数 JSON。可用字段随模板而定,运行 'nae templates' 看每个模板的 ConfigSchema(key/类型/默认值/可选项)。redis: {\"password\",\"persistence\":\"rdb|aof|none\"};milvus: {\"root_password\"}"
180
+ )
168
181
  .option('--owner <uid>', '管理员代他人创建时的归属用户 ID', (v) => parseInt(v, 10))
182
+ .addHelpText(
183
+ 'after',
184
+ `
185
+ 示例:
186
+ # 普通应用:跟踪镜像 tag、监听 8080、限 0.5 核 512Mi、带环境变量
187
+ nae create --app-id myweb --tag latest --port 8080 --cpu 500m --mem 512Mi --env '{"FOO":"bar"}'
188
+
189
+ # 自带 basePath 的框架(Next.js Node server 等):保留 /apps/<appid> 前缀
190
+ nae create --app-id mynext --tag latest --port 3000 --keep-path-prefix
191
+
192
+ # 模板应用 Redis:--config 取该模板的服务特性参数(见 nae templates)
193
+ nae create --kind template --app-id myredis --template redis --storage 2Gi \\
194
+ --config '{"password":"s3cret","persistence":"rdb"}'
195
+
196
+ # 模板应用 Milvus
197
+ nae create --kind template --app-id myvec --template milvus --storage 10Gi \\
198
+ --config '{"root_password":"s3cret"}'
199
+
200
+ 提示:先 'nae templates' 查模板 key 与各自 ConfigSchema 的字段;--config 留空则用各字段默认(密码类留空自动生成随机值,仅创建成功时返回一次)。`
201
+ )
169
202
  .action(
170
203
  run(async (opts) => {
171
204
  const body = {
@@ -304,31 +337,6 @@ program
304
337
  .description('列出可用模板')
305
338
  .action(run(async () => emit(await request(cfg(), 'GET', '/templates'))))
306
339
 
307
- // --- 自己的 CLI 密钥 ---
308
-
309
- program
310
- .command('keys')
311
- .description('列出我的 CLI 密钥')
312
- .action(run(async () => emit(await request(cfg(), 'GET', '/me/api-keys'))))
313
-
314
- program
315
- .command('keys:create')
316
- .description('创建一枚 CLI 密钥(自建上限 5 条)')
317
- .requiredOption('--name <name>', '备注名')
318
- .option('--days <n>', '有效期天数:30/60/90,0=永久', (v) => parseInt(v, 10), 90)
319
- .action(
320
- run(async (opts) => {
321
- emit(await request(cfg(), 'POST', '/me/api-keys', { body: { name: opts.name, valid_days: opts.days } }))
322
- })
323
- )
324
-
325
- program
326
- .command('keys:revoke <id>')
327
- .description('吊销一枚我的 CLI 密钥')
328
- .action(
329
- run(async (id) => {
330
- emit(await request(cfg(), 'DELETE', `/me/api-keys/${parseInt(id, 10)}`))
331
- })
332
- )
340
+ // CLI 密钥的创建/吊销/查看仅限网页端「CLI 配置」页,命令行不提供——见 nae login。
333
341
 
334
342
  program.parseAsync(process.argv)
package/src/output.js CHANGED
@@ -1,14 +1,72 @@
1
- // 输出契约(给 CodeAgent 用的硬约定):
2
- // - stdout 只放「数据」:成功结果一律 JSON(默认美化),方便管道/解析。
1
+ // 输出契约:
2
+ // - 默认输出「人类可读」文本到 stdout;加 `-o json`(或 `-c` 紧凑)才出 JSON,供脚本解析。
3
3
  // - stderr 只放「给人看的」:提示、进度、错误。
4
4
  // - 退出码:成功 0,失败非 0。
5
- // 这样 agent 可以 `nae apps 2>/dev/null | jq ...`,人也能从 stderr 读懂发生了什么。
5
+ // 人读默认、机读按需——人直接看懂,脚本用 `nae -o json 2>/dev/null | jq …`。
6
6
 
7
7
  // data 把结果写到 stdout(JSON)。pretty=false 时输出紧凑单行。
8
8
  export function data(value, { pretty = true } = {}) {
9
9
  process.stdout.write(JSON.stringify(value, null, pretty ? 2 : 0) + '\n')
10
10
  }
11
11
 
12
+ // human 把结果渲染成人类可读文本写到 stdout:
13
+ // - 标量 → 原样一行
14
+ // - 对象 → 「键: 值」多行;嵌套对象/数组缩进展开
15
+ // - 对象数组 → 逐条编号块([1]/[2]…),宽数据也不会折行错乱
16
+ // 需要稳定可解析格式请用 `-o json`。
17
+ export function human(value) {
18
+ process.stdout.write(renderValue(value, '') + '\n')
19
+ }
20
+
21
+ function isScalar(v) {
22
+ return v === null || v === undefined || typeof v !== 'object'
23
+ }
24
+
25
+ function renderScalar(v) {
26
+ if (v === null || v === undefined) return ''
27
+ return String(v)
28
+ }
29
+
30
+ // renderValue 顶层分派。
31
+ function renderValue(v, pad) {
32
+ if (isScalar(v)) return pad + renderScalar(v)
33
+ if (Array.isArray(v)) return v.length === 0 ? pad + '(空)' : renderArray(v, pad)
34
+ return renderObject(v, pad)
35
+ }
36
+
37
+ // renderObject 「键: 值」多行;嵌套对象/数组换行缩进。
38
+ function renderObject(obj, pad) {
39
+ const keys = Object.keys(obj)
40
+ if (keys.length === 0) return pad + '(空)'
41
+ const lines = []
42
+ for (const k of keys) {
43
+ const v = obj[k]
44
+ if (isScalar(v)) {
45
+ lines.push(`${pad}${k}: ${renderScalar(v)}`)
46
+ } else if (Array.isArray(v) && v.length === 0) {
47
+ lines.push(`${pad}${k}: (空)`)
48
+ } else {
49
+ lines.push(`${pad}${k}:`)
50
+ lines.push(renderValue(v, pad + ' '))
51
+ }
52
+ }
53
+ return lines.join('\n')
54
+ }
55
+
56
+ // renderArray 标量数组每行一个;对象数组逐条编号块。
57
+ function renderArray(arr, pad) {
58
+ const blocks = []
59
+ arr.forEach((item, i) => {
60
+ if (isScalar(item)) {
61
+ blocks.push(`${pad}- ${renderScalar(item)}`)
62
+ } else {
63
+ blocks.push(`${pad}[${i + 1}]`)
64
+ blocks.push(renderValue(item, pad + ' '))
65
+ }
66
+ })
67
+ return blocks.join('\n')
68
+ }
69
+
12
70
  // line 直接写一行到 stdout(用于 logs 等本就是文本流的场景)。
13
71
  export function line(s) {
14
72
  process.stdout.write(s + '\n')