@newbeebox/newbeebox-app-engine-cli 1.1.0 → 1.3.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.
package/README.md CHANGED
@@ -61,6 +61,7 @@ nae apps
61
61
  |------|------|
62
62
  | `nae whoami` | 当前身份(uid/用户名/角色/命名空间) |
63
63
  | `nae quota` | 配额用量(CPU/内存/存储/应用数 的 已用/上限) |
64
+ | `nae llm` | 内置 LLM 网关状态:月度额度/本月用量/令牌前缀/内置环境变量名/按模型明细 |
64
65
  | `nae config` | 查看本地配置(密钥脱敏) |
65
66
 
66
67
  ### 应用
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newbeebox/newbeebox-app-engine-cli",
3
- "version": "1.1.0",
3
+ "version": "1.3.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.1.0')
18
+ .version('1.3.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
@@ -129,6 +149,15 @@ program
129
149
  })
130
150
  )
131
151
 
152
+ program
153
+ .command('llm')
154
+ .description('查询内置 LLM 网关状态:月度额度/本月用量/令牌前缀/内置环境变量名/按模型用量明细')
155
+ .action(
156
+ run(async () => {
157
+ emit(await request(cfg(), 'GET', '/me/llm'))
158
+ })
159
+ )
160
+
132
161
  // --- 应用 ---
133
162
 
134
163
  program
@@ -174,6 +203,7 @@ program
174
203
  .option('--storage <size>', '模板应用:持久卷大小,如 10Gi')
175
204
  .option('--replicas <n>', '副本数', (v) => parseInt(v, 10))
176
205
  .option('--env <json>', '环境变量 JSON,如 \'{"KEY":"val"}\'')
206
+ .option('--env-file <path>', '从文件读环境变量 JSON(绕开 Windows PowerShell 引号问题;与 --env 二选一,优先 --env-file)')
177
207
  .option(
178
208
  '--config <json>',
179
209
  "模板应用的服务特性参数 JSON。可用字段随模板而定,运行 'nae templates' 看每个模板的 ConfigSchema(key/类型/默认值/可选项)。redis: {\"password\",\"persistence\":\"rdb|aof|none\"};postgres: {\"database\",\"username\",\"password\"};milvus: {\"root_password\"}"
@@ -217,7 +247,7 @@ program
217
247
  template_key: opts.template,
218
248
  storage_size: opts.storage,
219
249
  replicas: opts.replicas,
220
- env: parseJSONArg('--env', opts.env),
250
+ env: envFromOpts(opts),
221
251
  config: parseJSONArg('--config', opts.config),
222
252
  owner_id: opts.owner,
223
253
  }
@@ -225,6 +255,48 @@ program
225
255
  })
226
256
  )
227
257
 
258
+ program
259
+ .command('update <appid>')
260
+ .description(
261
+ '修改应用配置(端口/资源/env/跟踪tag/重启上限/路由前缀)。读-改-写:只覆盖你传的字段,其余保持不变。改后需 restart(运行中)或下次 start 才生效。env/端口/tag/前缀仅普通应用可改;AppID/类型/归属/存储/模板不可改(需删除重建)'
262
+ )
263
+ .option('--port <n>', '容器端口(仅普通应用)', (v) => parseInt(v, 10))
264
+ .option('--cpu <limit>', 'CPU 上限,如 1 / 500m')
265
+ .option('--mem <limit>', '内存上限,如 512Mi / 1Gi')
266
+ .option('--tag <tag>', '跟踪的镜像 tag(仅普通应用)')
267
+ .option('--max-restarts <n>', '失败重启上限,0=不封顶(仅普通应用)', (v) => parseInt(v, 10))
268
+ .option('--keep-path-prefix', '不剥离 /apps/<appid> 前缀(仅普通应用)')
269
+ .option('--strip-path-prefix', '剥离 /apps/<appid> 前缀(仅普通应用;与 --keep-path-prefix 互斥)')
270
+ .option('--env <json>', '环境变量 JSON(整体替换,仅普通应用)')
271
+ .option('--env-file <path>', '从文件读环境变量 JSON(整体替换;绕开 PowerShell 引号;与 --env 二选一,优先 --env-file)')
272
+ .action(
273
+ run(async (appid, opts) => {
274
+ if (opts.keepPathPrefix && opts.stripPathPrefix) {
275
+ throw new Error('--keep-path-prefix 与 --strip-path-prefix 互斥,只能给一个')
276
+ }
277
+ const c = cfg()
278
+ // 读-改-写:PATCH 是整体替换语义,必须以当前配置为基线,只覆盖本次传入的字段,
279
+ // 否则没传的字段会被抹成零值。三态 path 前缀:两个 flag 都不传则沿用当前值。
280
+ const cur = await request(c, 'GET', `/apps/${appid}`)
281
+ const envOverride = envFromOpts(opts)
282
+ const body = {
283
+ cpu_limit: opts.cpu ?? cur.CPULimit,
284
+ mem_limit: opts.mem ?? cur.MemLimit,
285
+ container_port: opts.port ?? cur.ContainerPort,
286
+ tracked_tag: opts.tag ?? cur.TrackedTag,
287
+ max_restarts: opts.maxRestarts ?? cur.MaxRestarts,
288
+ keep_path_prefix: opts.keepPathPrefix
289
+ ? true
290
+ : opts.stripPathPrefix
291
+ ? false
292
+ : cur.KeepPathPrefix,
293
+ env: envOverride ?? JSON.parse(cur.EnvJSON || '{}'),
294
+ sensitive_keys: JSON.parse(cur.SensitiveEnvJSON || '[]'),
295
+ }
296
+ emit(await request(c, 'PATCH', `/apps/${appid}`, { body }))
297
+ })
298
+ )
299
+
228
300
  program
229
301
  .command('delete <appid>')
230
302
  .description('删除应用(模板级联清子服务/PVC/secret),幂等')