@newbeebox/newbeebox-app-engine-cli 1.0.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +84 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newbeebox/newbeebox-app-engine-cli",
3
- "version": "1.0.3",
3
+ "version": "1.2.0",
4
4
  "description": "NewBee App Engine 命令行客户端(nae)——参数直达、默认人类可读(加 -o json 出 JSON),便于在终端使用与脚本/管道里解析。",
5
5
  "type": "module",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  // nae —— NewBee App Engine 命令行客户端。
3
3
  // 设计:薄壳。每条命令只做「取配置 → 调一个 HTTP 原语 → JSON 出 stdout」,
4
4
  // 鉴权/网络/业务错误集中在 run() 归类处理,命令体里不写 try/catch。
5
+ import { readFileSync } from 'fs'
5
6
  import { Command } from 'commander'
6
7
  import { load, save, configPath } from './config.js'
7
8
  import { request, AuthError, NetworkError } from './http.js'
@@ -14,7 +15,7 @@ const program = new Command()
14
15
  program
15
16
  .name('nae')
16
17
  .description('NewBee App Engine CLI —— 默认人类可读,-o json 输出 JSON 供脚本解析')
17
- .version('1.0.3')
18
+ .version('1.2.0')
18
19
  .option('--base-url <url>', '覆盖平台地址(也可用环境变量 NAE_BASE_URL)')
19
20
  .option('--token <token>', '覆盖访问密钥(也可用环境变量 NAE_TOKEN)')
20
21
  .option('-o, --output <format>', '输出格式:text(默认,人类可读)| json', 'text')
@@ -72,6 +73,25 @@ function parseJSONArg(label, s) {
72
73
  }
73
74
  }
74
75
 
76
+ // 环境变量来源:--env-file 优先(从文件读 JSON,绕开 shell 引号),否则 --env 内联 JSON。
77
+ // 两者都没给返回 undefined(create 表示不带 env / update 表示不改 env)。
78
+ function envFromOpts(opts) {
79
+ if (opts.envFile !== undefined) {
80
+ let raw
81
+ try {
82
+ raw = readFileSync(opts.envFile, 'utf8')
83
+ } catch (e) {
84
+ throw new Error(`--env-file 读取失败:${opts.envFile}(${e.message})`)
85
+ }
86
+ try {
87
+ return JSON.parse(raw)
88
+ } catch {
89
+ throw new Error(`--env-file 内容不是合法 JSON:${opts.envFile}`)
90
+ }
91
+ }
92
+ return parseJSONArg('--env', opts.env)
93
+ }
94
+
75
95
  // --- 认证 ---
76
96
 
77
97
  program
@@ -174,9 +194,10 @@ program
174
194
  .option('--storage <size>', '模板应用:持久卷大小,如 10Gi')
175
195
  .option('--replicas <n>', '副本数', (v) => parseInt(v, 10))
176
196
  .option('--env <json>', '环境变量 JSON,如 \'{"KEY":"val"}\'')
197
+ .option('--env-file <path>', '从文件读环境变量 JSON(绕开 Windows PowerShell 引号问题;与 --env 二选一,优先 --env-file)')
177
198
  .option(
178
199
  '--config <json>',
179
- "模板应用的服务特性参数 JSON。可用字段随模板而定,运行 'nae templates' 看每个模板的 ConfigSchema(key/类型/默认值/可选项)。redis: {\"password\",\"persistence\":\"rdb|aof|none\"};milvus: {\"root_password\"}"
200
+ "模板应用的服务特性参数 JSON。可用字段随模板而定,运行 'nae templates' 看每个模板的 ConfigSchema(key/类型/默认值/可选项)。redis: {\"password\",\"persistence\":\"rdb|aof|none\"};postgres: {\"database\",\"username\",\"password\"};milvus: {\"root_password\"}"
180
201
  )
181
202
  .option('--owner <uid>', '管理员代他人创建时的归属用户 ID', (v) => parseInt(v, 10))
182
203
  .addHelpText(
@@ -193,6 +214,10 @@ program
193
214
  nae create --kind template --app-id myredis --template redis --storage 2Gi \\
194
215
  --config '{"password":"s3cret","persistence":"rdb"}'
195
216
 
217
+ # 模板应用 PostgreSQL 14:持久化 + 库名/用户名/密码
218
+ nae create --kind template --app-id mypg --template postgres --storage 5Gi \\
219
+ --config '{"database":"appdb","username":"appuser","password":"s3cret"}'
220
+
196
221
  # 模板应用 Milvus
197
222
  nae create --kind template --app-id myvec --template milvus --storage 10Gi \\
198
223
  --config '{"root_password":"s3cret"}'
@@ -213,7 +238,7 @@ program
213
238
  template_key: opts.template,
214
239
  storage_size: opts.storage,
215
240
  replicas: opts.replicas,
216
- env: parseJSONArg('--env', opts.env),
241
+ env: envFromOpts(opts),
217
242
  config: parseJSONArg('--config', opts.config),
218
243
  owner_id: opts.owner,
219
244
  }
@@ -221,6 +246,48 @@ program
221
246
  })
222
247
  )
223
248
 
249
+ program
250
+ .command('update <appid>')
251
+ .description(
252
+ '修改应用配置(端口/资源/env/跟踪tag/重启上限/路由前缀)。读-改-写:只覆盖你传的字段,其余保持不变。改后需 restart(运行中)或下次 start 才生效。env/端口/tag/前缀仅普通应用可改;AppID/类型/归属/存储/模板不可改(需删除重建)'
253
+ )
254
+ .option('--port <n>', '容器端口(仅普通应用)', (v) => parseInt(v, 10))
255
+ .option('--cpu <limit>', 'CPU 上限,如 1 / 500m')
256
+ .option('--mem <limit>', '内存上限,如 512Mi / 1Gi')
257
+ .option('--tag <tag>', '跟踪的镜像 tag(仅普通应用)')
258
+ .option('--max-restarts <n>', '失败重启上限,0=不封顶(仅普通应用)', (v) => parseInt(v, 10))
259
+ .option('--keep-path-prefix', '不剥离 /apps/<appid> 前缀(仅普通应用)')
260
+ .option('--strip-path-prefix', '剥离 /apps/<appid> 前缀(仅普通应用;与 --keep-path-prefix 互斥)')
261
+ .option('--env <json>', '环境变量 JSON(整体替换,仅普通应用)')
262
+ .option('--env-file <path>', '从文件读环境变量 JSON(整体替换;绕开 PowerShell 引号;与 --env 二选一,优先 --env-file)')
263
+ .action(
264
+ run(async (appid, opts) => {
265
+ if (opts.keepPathPrefix && opts.stripPathPrefix) {
266
+ throw new Error('--keep-path-prefix 与 --strip-path-prefix 互斥,只能给一个')
267
+ }
268
+ const c = cfg()
269
+ // 读-改-写:PATCH 是整体替换语义,必须以当前配置为基线,只覆盖本次传入的字段,
270
+ // 否则没传的字段会被抹成零值。三态 path 前缀:两个 flag 都不传则沿用当前值。
271
+ const cur = await request(c, 'GET', `/apps/${appid}`)
272
+ const envOverride = envFromOpts(opts)
273
+ const body = {
274
+ cpu_limit: opts.cpu ?? cur.CPULimit,
275
+ mem_limit: opts.mem ?? cur.MemLimit,
276
+ container_port: opts.port ?? cur.ContainerPort,
277
+ tracked_tag: opts.tag ?? cur.TrackedTag,
278
+ max_restarts: opts.maxRestarts ?? cur.MaxRestarts,
279
+ keep_path_prefix: opts.keepPathPrefix
280
+ ? true
281
+ : opts.stripPathPrefix
282
+ ? false
283
+ : cur.KeepPathPrefix,
284
+ env: envOverride ?? JSON.parse(cur.EnvJSON || '{}'),
285
+ sensitive_keys: JSON.parse(cur.SensitiveEnvJSON || '[]'),
286
+ }
287
+ emit(await request(c, 'PATCH', `/apps/${appid}`, { body }))
288
+ })
289
+ )
290
+
224
291
  program
225
292
  .command('delete <appid>')
226
293
  .description('删除应用(模板级联清子服务/PVC/secret),幂等')
@@ -247,6 +314,20 @@ program
247
314
  .description('滚动重启应用')
248
315
  .action(run(async (id) => emit(await request(cfg(), 'POST', `/apps/${id}/restart`))))
249
316
 
317
+ program
318
+ .command('reset-password <appid>')
319
+ .description('重置模板应用连接密码(不清数据;当前支持 Redis/PostgreSQL)。留空 --password 则随机生成')
320
+ .option('--password <pw>', '指定新密码(留空=平台随机生成;可打印 ASCII,1-128 位)')
321
+ .action(
322
+ run(async (id, opts) => {
323
+ emit(
324
+ await request(cfg(), 'POST', `/apps/${id}/reset-credential`, {
325
+ body: { password: opts.password ?? '' },
326
+ })
327
+ )
328
+ })
329
+ )
330
+
250
331
  program
251
332
  .command('scale <appid> <replicas>')
252
333
  .description('扩缩容到指定副本数')