@newbeebox/newbeebox-app-engine-cli 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -10
- package/package.json +2 -2
- package/src/index.js +40 -32
- package/src/output.js +61 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# nae —— NewBee App Engine CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
平台命令行客户端。**参数直达、默认人类可读**,加 `-o json` 即出 JSON,便于 CodeAgent 编排调用。
|
|
4
4
|
|
|
5
5
|
> 📖 **完整文档(含 Agent 用法范例 / 调试技巧)**:<https://workshop.newbeebox.com/app_engine/documents/nae-cli/>
|
|
6
6
|
> markdown 原文:<https://workshop.newbeebox.com/app_engine/documents/nae-cli/md/>
|
|
@@ -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`)。
|
|
@@ -44,13 +43,13 @@ nae apps
|
|
|
44
43
|
|
|
45
44
|
---
|
|
46
45
|
|
|
47
|
-
##
|
|
46
|
+
## 输出契约
|
|
48
47
|
|
|
49
|
-
- **stdout
|
|
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
|
+
所以 Agent 可以安全地 `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
|
-
|
|
124
|
-
|
|
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.
|
|
4
|
-
"description": "NewBee App Engine 命令行客户端(nae
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "NewBee App Engine 命令行客户端(nae)——参数直达、默认人类可读(加 -o json 出 JSON),便于人用与 CodeAgent 调用平台能力。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"nae": "src/index.js"
|
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 ——
|
|
17
|
-
.version('1.0.
|
|
16
|
+
.description('NewBee App Engine CLI —— 默认人类可读,-o json 输出 JSON 供 CodeAgent 调用')
|
|
17
|
+
.version('1.0.2')
|
|
18
18
|
.option('--base-url <url>', '覆盖平台地址(也可用环境变量 NAE_BASE_URL)')
|
|
19
19
|
.option('--token <token>', '覆盖访问密钥(也可用环境变量 NAE_TOKEN)')
|
|
20
|
-
.option('-
|
|
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
|
|
32
|
+
// emit 按输出格式渲染数据:
|
|
33
|
+
// 默认 text(人类可读);-o json 出美化 JSON;-c/--compact 出紧凑 JSON(隐含 json)。
|
|
32
34
|
function emit(value) {
|
|
33
|
-
|
|
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(
|
|
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
|
-
//
|
|
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
|
-
//
|
|
2
|
-
// - stdout
|
|
1
|
+
// 输出契约:
|
|
2
|
+
// - 默认输出「人类可读」文本到 stdout;加 `-o json`(或 `-c` 紧凑)才出 JSON,供 Agent 解析。
|
|
3
3
|
// - stderr 只放「给人看的」:提示、进度、错误。
|
|
4
4
|
// - 退出码:成功 0,失败非 0。
|
|
5
|
-
//
|
|
5
|
+
// 人读默认、机读按需——人直接看懂,Agent 用 `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')
|