@lark-apaas/miaoda-cli 0.1.3 → 0.1.4

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 (71) hide show
  1. package/dist/api/app/api.js +3 -3
  2. package/dist/api/app/schemas.js +43 -43
  3. package/dist/api/db/api.js +398 -55
  4. package/dist/api/db/client.js +155 -28
  5. package/dist/api/db/index.js +12 -1
  6. package/dist/api/db/parsers.js +20 -20
  7. package/dist/api/db/sql-keywords.js +87 -87
  8. package/dist/api/deploy/api.js +5 -5
  9. package/dist/api/deploy/schemas.js +32 -32
  10. package/dist/api/file/api.js +89 -87
  11. package/dist/api/file/client.js +62 -22
  12. package/dist/api/file/detect.js +3 -3
  13. package/dist/api/file/index.js +2 -1
  14. package/dist/api/file/parsers.js +18 -7
  15. package/dist/api/observability/api.js +6 -6
  16. package/dist/api/observability/schemas.js +14 -14
  17. package/dist/api/plugin/api.js +31 -31
  18. package/dist/cli/commands/app/index.js +12 -12
  19. package/dist/cli/commands/db/index.js +602 -54
  20. package/dist/cli/commands/deploy/index.js +28 -28
  21. package/dist/cli/commands/file/index.js +85 -58
  22. package/dist/cli/commands/observability/index.js +69 -69
  23. package/dist/cli/commands/plugin/index.js +27 -27
  24. package/dist/cli/commands/shared.js +10 -10
  25. package/dist/cli/handlers/app/update.js +2 -2
  26. package/dist/cli/handlers/db/_destructive.js +67 -0
  27. package/dist/cli/handlers/db/_env.js +26 -0
  28. package/dist/cli/handlers/db/_operator.js +35 -0
  29. package/dist/cli/handlers/db/audit.js +383 -0
  30. package/dist/cli/handlers/db/changelog.js +160 -0
  31. package/dist/cli/handlers/db/data.js +32 -31
  32. package/dist/cli/handlers/db/index.js +17 -1
  33. package/dist/cli/handlers/db/migration.js +234 -0
  34. package/dist/cli/handlers/db/quota.js +68 -0
  35. package/dist/cli/handlers/db/recovery.js +413 -0
  36. package/dist/cli/handlers/db/schema.js +33 -33
  37. package/dist/cli/handlers/db/sql.js +69 -69
  38. package/dist/cli/handlers/deploy/deploy.js +4 -4
  39. package/dist/cli/handlers/deploy/error-log.js +1 -1
  40. package/dist/cli/handlers/deploy/get.js +3 -3
  41. package/dist/cli/handlers/deploy/polling.js +11 -11
  42. package/dist/cli/handlers/file/cp.js +30 -30
  43. package/dist/cli/handlers/file/index.js +3 -1
  44. package/dist/cli/handlers/file/ls.js +5 -5
  45. package/dist/cli/handlers/file/quota.js +66 -0
  46. package/dist/cli/handlers/file/rm.js +32 -30
  47. package/dist/cli/handlers/file/sign.js +3 -3
  48. package/dist/cli/handlers/file/stat.js +10 -9
  49. package/dist/cli/handlers/observability/analytics.js +47 -47
  50. package/dist/cli/handlers/observability/helpers.js +2 -2
  51. package/dist/cli/handlers/observability/log.js +9 -9
  52. package/dist/cli/handlers/observability/metric.js +26 -26
  53. package/dist/cli/handlers/observability/trace.js +5 -5
  54. package/dist/cli/handlers/plugin/plugin-local.js +53 -53
  55. package/dist/cli/handlers/plugin/plugin.js +15 -15
  56. package/dist/cli/help.js +16 -16
  57. package/dist/main.js +12 -12
  58. package/dist/utils/args.js +1 -1
  59. package/dist/utils/colors.js +2 -2
  60. package/dist/utils/config.js +2 -2
  61. package/dist/utils/devops-error.js +9 -9
  62. package/dist/utils/error.js +2 -2
  63. package/dist/utils/git.js +4 -4
  64. package/dist/utils/http.js +19 -19
  65. package/dist/utils/index.js +3 -1
  66. package/dist/utils/output.js +67 -45
  67. package/dist/utils/poll.js +35 -0
  68. package/dist/utils/render.js +27 -27
  69. package/dist/utils/spinner.js +46 -0
  70. package/dist/utils/time.js +47 -42
  71. package/package.json +1 -1
@@ -64,7 +64,7 @@ async function handleDbSql(query, opts) {
64
64
  const appId = opts.appId;
65
65
  const sql = await readSql(query);
66
66
  if (!sql.trim()) {
67
- throw new error_1.AppError("ARGS_INVALID", "Empty SQL (no inline query and stdin is empty)");
67
+ throw new error_1.AppError('ARGS_INVALID', 'Empty SQL (no inline query and stdin is empty)');
68
68
  }
69
69
  let results;
70
70
  try {
@@ -86,7 +86,7 @@ async function handleDbSql(query, opts) {
86
86
  (0, output_1.emit)({ data: [] });
87
87
  return;
88
88
  }
89
- (0, output_1.emit)("✓ No results");
89
+ (0, output_1.emit)((0, render_1.isStdoutTty)() ? colors_1.c.success('✓ No results') : 'OK No results');
90
90
  return;
91
91
  }
92
92
  if (results.length === 1) {
@@ -121,7 +121,7 @@ async function handleDbSql(query, opts) {
121
121
  * statement 都执行成功,无需再校验逐条状态。
122
122
  */
123
123
  /** agent runtime 中固定的项目根目录;放在此处便于以后调整。 */
124
- const AGENT_PROJECT_ROOT = "/home/gem/workspace/code";
124
+ const AGENT_PROJECT_ROOT = '/home/gem/workspace/code';
125
125
  /** drain 子进程 pipe stream:默认静默模式下消费 chunk 防止缓冲区反压。 */
126
126
  function drainChunk(_chunk) {
127
127
  // 显式接 chunk 参数让 ESLint 不再当成 empty function;不做任何处理
@@ -131,7 +131,7 @@ async function maybeSyncAgentSchema(results) {
131
131
  return;
132
132
  const projectRoot = await resolveAgentProjectRoot();
133
133
  if (!projectRoot) {
134
- (0, logger_1.debug)("[db sql] agent project root not found or missing gen:db-schema script, skip schema sync");
134
+ (0, logger_1.debug)('[db sql] agent project root not found or missing gen:db-schema script, skip schema sync');
135
135
  return;
136
136
  }
137
137
  const verbose = (0, config_1.getConfig)().verbose;
@@ -140,8 +140,8 @@ async function maybeSyncAgentSchema(results) {
140
140
  (0, logger_1.debug)(`[db sql] DDL detected, running \`npm run gen:db-schema\` in ${projectRoot}`);
141
141
  try {
142
142
  await new Promise((resolve) => {
143
- const proc = (0, node_child_process_1.spawn)("npm", ["run", "gen:db-schema"], {
144
- stdio: ["ignore", "pipe", "pipe"],
143
+ const proc = (0, node_child_process_1.spawn)('npm', ['run', 'gen:db-schema'], {
144
+ stdio: ['ignore', 'pipe', 'pipe'],
145
145
  cwd: projectRoot,
146
146
  });
147
147
  // --verbose:实时透传到 stderr 看进度;默认完全静默丢弃。
@@ -152,15 +152,15 @@ async function maybeSyncAgentSchema(results) {
152
152
  proc.stderr.pipe(process.stderr);
153
153
  }
154
154
  else {
155
- proc.stdout.on("data", drainChunk);
156
- proc.stderr.on("data", drainChunk);
155
+ proc.stdout.on('data', drainChunk);
156
+ proc.stderr.on('data', drainChunk);
157
157
  }
158
158
  let timedOut = false;
159
159
  const timer = setTimeout(() => {
160
160
  timedOut = true;
161
- proc.kill("SIGKILL");
161
+ proc.kill('SIGKILL');
162
162
  }, TIMEOUT_MS);
163
- proc.on("close", (code) => {
163
+ proc.on('close', (code) => {
164
164
  clearTimeout(timer);
165
165
  if (timedOut) {
166
166
  (0, logger_1.debug)(`[db sql] gen:db-schema timed out after ${String(TIMEOUT_MS / 1000)}s, killed`);
@@ -169,11 +169,11 @@ async function maybeSyncAgentSchema(results) {
169
169
  (0, logger_1.debug)(`[db sql] gen:db-schema exited with code ${String(code)}`);
170
170
  }
171
171
  else {
172
- (0, logger_1.debug)("[db sql] gen:db-schema completed");
172
+ (0, logger_1.debug)('[db sql] gen:db-schema completed');
173
173
  }
174
174
  resolve();
175
175
  });
176
- proc.on("error", (err) => {
176
+ proc.on('error', (err) => {
177
177
  clearTimeout(timer);
178
178
  (0, logger_1.debug)(`[db sql] gen:db-schema spawn error: ${err.message}`);
179
179
  resolve();
@@ -193,10 +193,10 @@ async function maybeSyncAgentSchema(results) {
193
193
  */
194
194
  async function resolveAgentProjectRoot() {
195
195
  try {
196
- const pkgPath = node_path_1.default.join(AGENT_PROJECT_ROOT, "package.json");
197
- const raw = await node_fs_1.promises.readFile(pkgPath, "utf8");
196
+ const pkgPath = node_path_1.default.join(AGENT_PROJECT_ROOT, 'package.json');
197
+ const raw = await node_fs_1.promises.readFile(pkgPath, 'utf8');
198
198
  const pkg = JSON.parse(raw);
199
- if (pkg.scripts?.["gen:db-schema"])
199
+ if (pkg.scripts?.['gen:db-schema'])
200
200
  return AGENT_PROJECT_ROOT;
201
201
  }
202
202
  catch {
@@ -206,7 +206,7 @@ async function resolveAgentProjectRoot() {
206
206
  }
207
207
  /** 是否有任意一条 statement 是 DDL(CREATE / ALTER / DROP / GRANT / TRUNCATE / COMMENT 等)。 */
208
208
  function hasDdl(results) {
209
- return results.some((r) => (0, index_1.parseSqlResult)(r).kind === "ddl");
209
+ return results.some((r) => (0, index_1.parseSqlResult)(r).kind === 'ddl');
210
210
  }
211
211
  /** 读取 stdin 并返回完整 SQL 文本(stdin 不是 TTY 即认为被 pipe)。 */
212
212
  async function readSql(inline) {
@@ -214,17 +214,17 @@ async function readSql(inline) {
214
214
  return inline;
215
215
  const stdin = process.stdin;
216
216
  if (stdin.isTTY)
217
- return "";
217
+ return '';
218
218
  return new Promise((resolve, reject) => {
219
- let data = "";
220
- process.stdin.setEncoding("utf8");
221
- process.stdin.on("data", (chunk) => {
219
+ let data = '';
220
+ process.stdin.setEncoding('utf8');
221
+ process.stdin.on('data', (chunk) => {
222
222
  data += chunk;
223
223
  });
224
- process.stdin.on("end", () => {
224
+ process.stdin.on('end', () => {
225
225
  resolve(data);
226
226
  });
227
- process.stdin.on("error", reject);
227
+ process.stdin.on('error', reject);
228
228
  });
229
229
  }
230
230
  function renderSingle(raw) {
@@ -234,9 +234,9 @@ function renderSingle(raw) {
234
234
  (0, output_1.emit)(toJson(parsed));
235
235
  return;
236
236
  }
237
- if (parsed.kind === "select") {
237
+ if (parsed.kind === 'select') {
238
238
  if (parsed.rows.length === 0) {
239
- (0, output_1.emit)("(0 rows)");
239
+ (0, output_1.emit)('(0 rows)');
240
240
  return;
241
241
  }
242
242
  const cols = collectColumns(parsed.rows);
@@ -244,22 +244,22 @@ function renderSingle(raw) {
244
244
  (0, output_1.emit)(tty ? (0, render_1.renderAlignedTable)(cols, rows) : (0, render_1.renderTsv)(cols, rows));
245
245
  return;
246
246
  }
247
- if (parsed.kind === "dml") {
247
+ if (parsed.kind === 'dml') {
248
248
  const verb = dmlVerb(parsed.sqlType);
249
249
  (0, output_1.emit)(tty
250
- ? colors_1.c.success(`✓ ${String(parsed.affectedRows)} row${parsed.affectedRows === 1 ? "" : "s"} ${verb}`)
250
+ ? colors_1.c.success(`✓ ${String(parsed.affectedRows)} row${parsed.affectedRows === 1 ? '' : 's'} ${verb}`)
251
251
  : `OK ${String(parsed.affectedRows)} rows ${verb}`);
252
252
  return;
253
253
  }
254
254
  // DDL
255
- (0, output_1.emit)(tty ? colors_1.c.success("✓ DDL executed") : "OK DDL executed");
255
+ (0, output_1.emit)(tty ? colors_1.c.success('✓ DDL executed') : 'OK DDL executed');
256
256
  }
257
257
  function toJson(parsed) {
258
- if (parsed.kind === "select") {
258
+ if (parsed.kind === 'select') {
259
259
  // PRD 单 SELECT:data 直接是行数组(按 --json 字段投影裁剪)
260
260
  return { data: projectRows(parsed.rows) };
261
261
  }
262
- if (parsed.kind === "dml") {
262
+ if (parsed.kind === 'dml') {
263
263
  // PRD 单 DML:data = {command, rows_affected}
264
264
  return {
265
265
  data: {
@@ -277,10 +277,10 @@ function toJson(parsed) {
277
277
  * {command:"SELECT", rows:[...]}(PRD 约定,避免数组里嵌套数组造成歧义)。
278
278
  */
279
279
  function toMultiElement(parsed) {
280
- if (parsed.kind === "select") {
281
- return { command: "SELECT", rows: projectRows(parsed.rows) };
280
+ if (parsed.kind === 'select') {
281
+ return { command: 'SELECT', rows: projectRows(parsed.rows) };
282
282
  }
283
- if (parsed.kind === "dml") {
283
+ if (parsed.kind === 'dml') {
284
284
  return { command: parsed.sqlType, rows_affected: parsed.affectedRows };
285
285
  }
286
286
  // DDL:用后端给的细粒度 command
@@ -306,10 +306,10 @@ function projectRows(rows) {
306
306
  /** 读取 --json [fields]:返回字段列表;boolean true 或 undefined 返回 null(不裁剪)。 */
307
307
  function parseJsonFields() {
308
308
  const v = (0, config_1.getConfig)().json;
309
- if (typeof v !== "string" || v === "")
309
+ if (typeof v !== 'string' || v === '')
310
310
  return null;
311
311
  return v
312
- .split(",")
312
+ .split(',')
313
313
  .map((s) => s.trim())
314
314
  .filter((s) => s.length > 0);
315
315
  }
@@ -320,9 +320,9 @@ function renderMultiPretty(results) {
320
320
  for (let i = 0; i < results.length; i++) {
321
321
  const parsed = (0, index_1.parseSqlResult)(results[i]);
322
322
  const idx = i + 1;
323
- if (parsed.kind === "select") {
323
+ if (parsed.kind === 'select') {
324
324
  const n = parsed.rows.length;
325
- lines.push(`Statement ${String(idx)}: SELECT (${String(n)} row${n === 1 ? "" : "s"})`);
325
+ lines.push(`Statement ${String(idx)}: SELECT (${String(n)} row${n === 1 ? '' : 's'})`);
326
326
  if (n > 0) {
327
327
  const cols = collectColumns(parsed.rows);
328
328
  const tbl = parsed.rows.map((r) => cols.map((col) => formatCell(r[col], tty)));
@@ -330,37 +330,37 @@ function renderMultiPretty(results) {
330
330
  }
331
331
  // 块间空行(最后一条不留)
332
332
  if (i < results.length - 1)
333
- lines.push("");
333
+ lines.push('');
334
334
  continue;
335
335
  }
336
- if (parsed.kind === "dml") {
336
+ if (parsed.kind === 'dml') {
337
337
  const verb = dmlVerb(parsed.sqlType);
338
338
  const n = parsed.affectedRows;
339
- const body = `${String(n)} row${n === 1 ? "" : "s"} ${verb}`;
340
- lines.push(`Statement ${String(idx)}: ${tty ? colors_1.c.success("" + body) : body}`);
339
+ const body = `${String(n)} row${n === 1 ? '' : 's'} ${verb}`;
340
+ lines.push(`Statement ${String(idx)}: ${tty ? colors_1.c.success('' + body) : body}`);
341
341
  continue;
342
342
  }
343
343
  // DDL
344
- lines.push(`Statement ${String(idx)}: ${tty ? colors_1.c.success("✓ DDL executed") : "DDL executed"}`);
344
+ lines.push(`Statement ${String(idx)}: ${tty ? colors_1.c.success('✓ DDL executed') : 'DDL executed'}`);
345
345
  }
346
346
  // 汇总行:所有 statement 都跑完了
347
347
  lines.push(tty
348
348
  ? colors_1.c.success(`✓ ${String(results.length)} statements executed`)
349
349
  : `OK ${String(results.length)} statements executed`);
350
- (0, output_1.emit)(lines.join("\n"));
350
+ (0, output_1.emit)(lines.join('\n'));
351
351
  }
352
352
  function dmlVerb(type) {
353
353
  switch (type) {
354
- case "INSERT":
355
- return "inserted";
356
- case "UPDATE":
357
- return "updated";
358
- case "DELETE":
359
- return "deleted";
360
- case "MERGE":
361
- return "merged";
362
- case "DML":
363
- return "affected"; // 未识别子类的兜底
354
+ case 'INSERT':
355
+ return 'inserted';
356
+ case 'UPDATE':
357
+ return 'updated';
358
+ case 'DELETE':
359
+ return 'deleted';
360
+ case 'MERGE':
361
+ return 'merged';
362
+ case 'DML':
363
+ return 'affected'; // 未识别子类的兜底
364
364
  }
365
365
  }
366
366
  /** 从第一行收集列顺序;缺失列保留空白(兼容稀疏行)。 */
@@ -380,9 +380,9 @@ function collectColumns(rows) {
380
380
  */
381
381
  function formatCell(v, tty) {
382
382
  if (v === null || v === undefined) {
383
- return tty ? colors_1.c.muted("NULL") : "NULL";
383
+ return tty ? colors_1.c.muted('NULL') : 'NULL';
384
384
  }
385
- if (typeof v === "string") {
385
+ if (typeof v === 'string') {
386
386
  // TTY 下检测到 ISO 8601 时间字符串 → 转成相对时间("3h ago" / "2d ago" /
387
387
  // "2026-03-15"),方便 _created_at / _updated_at 等列直观可读
388
388
  if (tty && ISO_TIMESTAMP_RE.test(v) && !Number.isNaN(Date.parse(v))) {
@@ -390,7 +390,7 @@ function formatCell(v, tty) {
390
390
  }
391
391
  return v;
392
392
  }
393
- if (typeof v === "number" || typeof v === "boolean")
393
+ if (typeof v === 'number' || typeof v === 'boolean')
394
394
  return String(v);
395
395
  // object / array → JSON
396
396
  return JSON.stringify(v);
@@ -487,7 +487,7 @@ async function loadTableMap(ctx) {
487
487
  try {
488
488
  const resp = await api.db.getSchema({
489
489
  appId: ctx.appId,
490
- format: "schema",
490
+ format: 'schema',
491
491
  dbBranch: ctx.env,
492
492
  });
493
493
  return extractTableFieldMap(resp.schema);
@@ -500,7 +500,7 @@ function enrichRelationNotExist(err, relation, tableMap) {
500
500
  const guess = (0, fuzzy_match_1.suggest)(relation, Object.keys(tableMap));
501
501
  err.next_actions.push(guess
502
502
  ? `Did you mean '${guess}'? Run \`miaoda db schema list\` to see all tables.`
503
- : "Run `miaoda db schema list` to see all tables.");
503
+ : 'Run `miaoda db schema list` to see all tables.');
504
504
  }
505
505
  function enrichColumnNotExist(err, colMatch, tableMap) {
506
506
  const colName = colMatch[1];
@@ -522,7 +522,7 @@ function enrichColumnNotExist(err, colMatch, tableMap) {
522
522
  const guess = (0, fuzzy_match_1.suggest)(colName, allCols);
523
523
  err.next_actions.push(guess
524
524
  ? `Did you mean '${guess}'? Run \`miaoda db schema get <table>\` to see columns.`
525
- : "Run `miaoda db schema get <table>` to see all columns.");
525
+ : 'Run `miaoda db schema get <table>` to see all columns.');
526
526
  }
527
527
  /**
528
528
  * 扫整段 SQL 找一个看似"拼错的关键字"token:纯字母 + 长度 ≥ 3 + 不是已知 keyword
@@ -593,7 +593,7 @@ function enrichMultiStatementError(err, sql) {
593
593
  }
594
594
  // rolled_back=true 时追加 spec hint:"Transaction rolled back; no changes persisted."
595
595
  if (err.rolled_back) {
596
- err.next_actions.push("Transaction rolled back; no changes persisted.");
596
+ err.next_actions.push('Transaction rolled back; no changes persisted.');
597
597
  }
598
598
  }
599
599
  /**
@@ -605,9 +605,9 @@ function enrichMultiStatementError(err, sql) {
605
605
  function inferRolledBack(completed) {
606
606
  let depth = 0;
607
607
  for (const e of completed) {
608
- if (e.command === "BEGIN")
608
+ if (e.command === 'BEGIN')
609
609
  depth++;
610
- else if (e.command === "COMMIT" || e.command === "ROLLBACK")
610
+ else if (e.command === 'COMMIT' || e.command === 'ROLLBACK')
611
611
  depth--;
612
612
  }
613
613
  return depth > 0;
@@ -630,25 +630,25 @@ function writeMultiStatementBreakdown(err, completed) {
630
630
  const lines = [];
631
631
  for (let i = 0; i < completed.length; i++) {
632
632
  const e = completed[i];
633
- const verb = e.command === "BEGIN" || e.command === "COMMIT" || e.command === "ROLLBACK"
633
+ const verb = e.command === 'BEGIN' || e.command === 'COMMIT' || e.command === 'ROLLBACK'
634
634
  ? e.command
635
635
  : describeCompleted(e);
636
- lines.push(`Statement ${String(i + 1)}: ${tty ? colors_1.c.success("") : ""}${verb}`);
636
+ lines.push(`Statement ${String(i + 1)}: ${tty ? colors_1.c.success('') : ''}${verb}`);
637
637
  }
638
638
  // 失败那条
639
639
  const failedIdx = err.statement_index ?? completed.length;
640
- lines.push(`Statement ${String(failedIdx + 1)}: ${tty ? colors_1.c.fail("") : ""}${err.message}`);
641
- process.stderr.write(lines.join("\n") + "\n\n");
640
+ lines.push(`Statement ${String(failedIdx + 1)}: ${tty ? colors_1.c.fail('') : ''}${err.message}`);
641
+ process.stderr.write(lines.join('\n') + '\n\n');
642
642
  }
643
643
  /** completed 单条结果的人类可读描述(用于 stderr breakdown 行尾)。 */
644
644
  function describeCompleted(e) {
645
645
  const r = e;
646
- if (r.command === "SELECT" && Array.isArray(r.rows)) {
647
- return `SELECT (${String(r.rows.length)} row${r.rows.length === 1 ? "" : "s"})`;
646
+ if (r.command === 'SELECT' && Array.isArray(r.rows)) {
647
+ return `SELECT (${String(r.rows.length)} row${r.rows.length === 1 ? '' : 's'})`;
648
648
  }
649
- if (typeof r.rows_affected === "number") {
649
+ if (typeof r.rows_affected === 'number') {
650
650
  const verb = dmlVerb(r.command);
651
- return `${String(r.rows_affected)} row${r.rows_affected === 1 ? "" : "s"} ${verb}`;
651
+ return `${String(r.rows_affected)} row${r.rows_affected === 1 ? '' : 's'} ${verb}`;
652
652
  }
653
653
  return `${r.command} executed`;
654
654
  }
@@ -43,7 +43,7 @@ const format_1 = require("./format");
43
43
  async function handleDeploy(opts) {
44
44
  const appID = opts.appId;
45
45
  const resp = await api.deploy.createRelease({ appID, branch: opts.branch });
46
- const pipelineTaskID = resp.pipelineTaskID ?? "";
46
+ const pipelineTaskID = resp.pipelineTaskID ?? '';
47
47
  if (!opts.wait) {
48
48
  if (!(0, output_1.isJsonMode)()) {
49
49
  process.stdout.write(`✓ Deployment triggered. (deploy-id: ${pipelineTaskID})\n`);
@@ -66,14 +66,14 @@ async function handleDeploy(opts) {
66
66
  });
67
67
  if (!(0, output_1.isJsonMode)()) {
68
68
  if (detail.status === index_1.NodeStatus.SUCCESS) {
69
- process.stdout.write("✓ 发布成功\n");
69
+ process.stdout.write('✓ 发布成功\n');
70
70
  }
71
71
  else if (detail.status === index_1.NodeStatus.FAILED) {
72
- process.stdout.write("✗ 发布失败\n");
72
+ process.stdout.write('✗ 发布失败\n');
73
73
  process.stdout.write(` hint: Run \`miaoda deploy error-log ${pipelineTaskID}\` to view error logs\n`);
74
74
  }
75
75
  else {
76
- process.stdout.write("• 发布已取消\n");
76
+ process.stdout.write('• 发布已取消\n');
77
77
  }
78
78
  }
79
79
  (0, output_1.emit)({
@@ -42,7 +42,7 @@ const helpers_1 = require("./helpers");
42
42
  /** miaoda deploy error-log <deploy-id> */
43
43
  async function handleDeployErrorLog(opts) {
44
44
  if (!opts.deployId)
45
- (0, args_1.failArgs)("<deploy-id> 必填");
45
+ (0, args_1.failArgs)('<deploy-id> 必填');
46
46
  const appID = opts.appId;
47
47
  (0, helpers_1.parseDeployId)(opts.deployId); // 仅校验数字形式;URL 直接用原字符串
48
48
  const instanceID = opts.deployId;
@@ -54,7 +54,7 @@ const DEPLOY_GET_LOOKUP_LIMIT = 500;
54
54
  */
55
55
  async function handleDeployGet(opts) {
56
56
  if (!opts.deployId)
57
- (0, args_1.failArgs)("<deploy-id> 必填");
57
+ (0, args_1.failArgs)('<deploy-id> 必填');
58
58
  const appID = opts.appId;
59
59
  const deployId = (0, helpers_1.parseDeployId)(opts.deployId);
60
60
  const resp = await api.deploy.listPipelineInstances({
@@ -63,8 +63,8 @@ async function handleDeployGet(opts) {
63
63
  });
64
64
  const instance = resp.instances?.find((it) => Number(it.ID) === deployId);
65
65
  if (!instance) {
66
- throw new error_1.AppError("DEPLOY_NOT_FOUND", `未在最近 ${String(DEPLOY_GET_LOOKUP_LIMIT)} 条记录中找到 deploy-id=${opts.deployId}`, {
67
- next_actions: ["运行 `miaoda deploy history` 确认 deploy-id 是否在最近窗口内"],
66
+ throw new error_1.AppError('DEPLOY_NOT_FOUND', `未在最近 ${String(DEPLOY_GET_LOOKUP_LIMIT)} 条记录中找到 deploy-id=${opts.deployId}`, {
67
+ next_actions: ['运行 `miaoda deploy history` 确认 deploy-id 是否在最近窗口内'],
68
68
  });
69
69
  }
70
70
  (0, output_1.emit)({ data: (0, format_1.formatSimpleInstance)(instance), next_cursor: null, has_more: false }, index_1.deployGetSchema);
@@ -74,7 +74,7 @@ async function waitForPipeline(opts) {
74
74
  const showProgress = !(0, output_1.isJsonMode)() && process.stdout.isTTY;
75
75
  const printedStages = new Set();
76
76
  const spinner = showProgress
77
- ? (0, ora_1.default)({ text: "等待调度", spinner: "dots", stream: process.stdout }).start()
77
+ ? (0, ora_1.default)({ text: '等待调度', spinner: 'dots', stream: process.stdout }).start()
78
78
  : null;
79
79
  try {
80
80
  for (;;) {
@@ -89,7 +89,7 @@ async function waitForPipeline(opts) {
89
89
  return detail;
90
90
  }
91
91
  if (now() - start >= timeoutMs) {
92
- throw new error_1.AppError("DEPLOY_TIMEOUT", `轮询发布状态超时(${String(opts.timeoutSec)}s)`, {
92
+ throw new error_1.AppError('DEPLOY_TIMEOUT', `轮询发布状态超时(${String(opts.timeoutSec)}s)`, {
93
93
  retryable: true,
94
94
  next_actions: [
95
95
  `Run \`miaoda deploy get <deploy-id>\` 查看最新状态`,
@@ -122,7 +122,7 @@ function advanceProgress(detail, printed, spinner) {
122
122
  printed.add(stage.id);
123
123
  }
124
124
  const running = stages.find((s) => s.status === index_1.NodeStatus.RUNNING);
125
- const text = running ? `${running.name} 执行中` : "等待调度";
125
+ const text = running ? `${running.name} 执行中` : '等待调度';
126
126
  if (spinner.isSpinning) {
127
127
  spinner.text = text;
128
128
  }
@@ -131,13 +131,13 @@ function advanceProgress(detail, printed, spinner) {
131
131
  }
132
132
  }
133
133
  function normalizePipelineInstance(resp) {
134
- const keyedDetail = readPipelineDetail(resp, "pipelineInstanceDetail") ?? readPipelineDetail(resp, "detail");
134
+ const keyedDetail = readPipelineDetail(resp, 'pipelineInstanceDetail') ?? readPipelineDetail(resp, 'detail');
135
135
  if (keyedDetail)
136
136
  return keyedDetail;
137
137
  if (isPipelineInstance(resp))
138
138
  return resp;
139
- throw new error_1.AppError("DEPLOY_RESPONSE_INVALID", "发布状态响应缺少 pipeline detail", {
140
- next_actions: ["使用 --verbose 重试以查看请求 logid,并联系平台排查响应结构"],
139
+ throw new error_1.AppError('DEPLOY_RESPONSE_INVALID', '发布状态响应缺少 pipeline detail', {
140
+ next_actions: ['使用 --verbose 重试以查看请求 logid,并联系平台排查响应结构'],
141
141
  });
142
142
  }
143
143
  function readPipelineDetail(resp, key) {
@@ -147,16 +147,16 @@ function readPipelineDetail(resp, key) {
147
147
  return isPipelineInstance(detail) ? detail : undefined;
148
148
  }
149
149
  function isPipelineInstance(value) {
150
- if (typeof value !== "object" || value === null)
150
+ if (typeof value !== 'object' || value === null)
151
151
  return false;
152
152
  const item = value;
153
- return typeof item.status === "number";
153
+ return typeof item.status === 'number';
154
154
  }
155
155
  function stageSymbol(stage) {
156
- return stage.status === index_1.NodeStatus.SUCCESS ? "" : stage.status === index_1.NodeStatus.FAILED ? "" : "";
156
+ return stage.status === index_1.NodeStatus.SUCCESS ? '' : stage.status === index_1.NodeStatus.FAILED ? '' : '';
157
157
  }
158
158
  function formatStageBody(stage) {
159
- const cost = stage.costTime >= 0 ? `(${(stage.costTime / 1000).toFixed(1)}s)` : "";
159
+ const cost = stage.costTime >= 0 ? `(${(stage.costTime / 1000).toFixed(1)}s)` : '';
160
160
  const status = (0, index_1.nodeStatusText)(stage.status) ?? String(stage.status);
161
- return `${stage.name}${cost ? ` ${cost}` : ""} ${status}`.trim();
161
+ return `${stage.name}${cost ? ` ${cost}` : ''} ${status}`.trim();
162
162
  }
@@ -56,9 +56,9 @@ const MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
56
56
  * 不存在即远程 path(download,由 resolveRemotePath 处理)。
57
57
  */
58
58
  function isLocalSrc(src) {
59
- if (src.startsWith("./") || src.startsWith("../") || src.startsWith("~/"))
59
+ if (src.startsWith('./') || src.startsWith('../') || src.startsWith('~/'))
60
60
  return true;
61
- if (!src.includes("/"))
61
+ if (!src.includes('/'))
62
62
  return true;
63
63
  // `/` 开头:可能是本地绝对路径,也可能是远程 path;交给 fs 探测。
64
64
  const expanded = expandHome(src);
@@ -70,38 +70,38 @@ function isLocalSrc(src) {
70
70
  }
71
71
  }
72
72
  function expandHome(p) {
73
- if (p.startsWith("~/"))
73
+ if (p.startsWith('~/'))
74
74
  return node_path_1.default.join(node_os_1.default.homedir(), p.slice(2));
75
75
  return p;
76
76
  }
77
77
  function detectMime(filePath) {
78
78
  const ext = node_path_1.default.extname(filePath).toLowerCase();
79
79
  const map = {
80
- ".png": "image/png",
81
- ".jpg": "image/jpeg",
82
- ".jpeg": "image/jpeg",
83
- ".gif": "image/gif",
84
- ".webp": "image/webp",
85
- ".svg": "image/svg+xml",
86
- ".pdf": "application/pdf",
87
- ".json": "application/json",
88
- ".csv": "text/csv",
89
- ".txt": "text/plain",
90
- ".html": "text/html",
91
- ".zip": "application/zip",
92
- ".tar": "application/x-tar",
93
- ".gz": "application/gzip",
94
- ".mp4": "video/mp4",
95
- ".mp3": "audio/mpeg",
80
+ '.png': 'image/png',
81
+ '.jpg': 'image/jpeg',
82
+ '.jpeg': 'image/jpeg',
83
+ '.gif': 'image/gif',
84
+ '.webp': 'image/webp',
85
+ '.svg': 'image/svg+xml',
86
+ '.pdf': 'application/pdf',
87
+ '.json': 'application/json',
88
+ '.csv': 'text/csv',
89
+ '.txt': 'text/plain',
90
+ '.html': 'text/html',
91
+ '.zip': 'application/zip',
92
+ '.tar': 'application/x-tar',
93
+ '.gz': 'application/gzip',
94
+ '.mp4': 'video/mp4',
95
+ '.mp3': 'audio/mpeg',
96
96
  };
97
- return map[ext] ?? "application/octet-stream";
97
+ return map[ext] ?? 'application/octet-stream';
98
98
  }
99
99
  /** 把 src 的远程形式(`/path` / `file_name`)解析成后端 sign 可用的 remote path。 */
100
100
  async function resolveRemotePath(appId, input) {
101
- if (input.startsWith("/"))
101
+ if (input.startsWith('/'))
102
102
  return input;
103
103
  const [resolved] = await api.file.resolveInputs({ appId, inputs: [input] });
104
- if (resolved.status === "error") {
104
+ if (resolved.status === 'error') {
105
105
  throw new error_1.AppError(resolved.error.code, resolved.error.message, {
106
106
  next_actions: resolved.error.hint ? [resolved.error.hint] : undefined,
107
107
  });
@@ -111,15 +111,15 @@ async function resolveRemotePath(appId, input) {
111
111
  async function handleUpload(appId, localRaw, remoteRaw, rename) {
112
112
  const localPath = expandHome(localRaw);
113
113
  if (!node_fs_1.default.existsSync(localPath) || !node_fs_1.default.statSync(localPath).isFile()) {
114
- throw new error_1.AppError("FILE_SRC_NOT_FOUND", `Local file '${localRaw}' does not exist`, {
114
+ throw new error_1.AppError('FILE_SRC_NOT_FOUND', `Local file '${localRaw}' does not exist`, {
115
115
  // 引导本地路径自检;远程下载请用 `/path` 形态,避免裸名/相对路径误入上传分支
116
- next_actions: ["Check the local file path; use `/path` prefix for remote download."],
116
+ next_actions: ['Check the local file path; use `/path` prefix for remote download.'],
117
117
  });
118
118
  }
119
119
  const stat = node_fs_1.default.statSync(localPath);
120
120
  if (stat.size > MAX_UPLOAD_BYTES) {
121
- throw new error_1.AppError("FILE_SIZE_EXCEEDED", `File size ${(0, render_1.formatSize)(stat.size)} exceeds the 100 MB upload limit`, {
122
- next_actions: ["Split the file, or use the web console for large uploads."],
121
+ throw new error_1.AppError('FILE_SIZE_EXCEEDED', `File size ${(0, render_1.formatSize)(stat.size)} exceeds the 100 MB upload limit`, {
122
+ next_actions: ['Split the file, or use the web console for large uploads.'],
123
123
  });
124
124
  }
125
125
  // PRD:path 末段**始终**由平台生成 16 位 ID 确保唯一,不受 dst 形态或
@@ -134,12 +134,12 @@ async function handleUpload(appId, localRaw, remoteRaw, rename) {
134
134
  // 完整对象 key 模式。--rename 始终覆盖 file_name 推断结果(PRD 优先级)。
135
135
  let fileName;
136
136
  let remotePath;
137
- if (remoteRaw === "" || remoteRaw.endsWith("/")) {
137
+ if (remoteRaw === '' || remoteRaw.endsWith('/')) {
138
138
  fileName = rename ?? node_path_1.default.basename(localPath);
139
139
  remotePath = remoteRaw;
140
140
  }
141
141
  else {
142
- const lastSlash = remoteRaw.lastIndexOf("/");
142
+ const lastSlash = remoteRaw.lastIndexOf('/');
143
143
  fileName = rename ?? remoteRaw.slice(lastSlash + 1);
144
144
  remotePath = remoteRaw.slice(0, lastSlash + 1);
145
145
  }
@@ -187,7 +187,7 @@ async function handleUpload(appId, localRaw, remoteRaw, rename) {
187
187
  lines.push(`download_url\t${result.download_url}`);
188
188
  }
189
189
  }
190
- (0, output_1.emit)(lines.join("\n"));
190
+ (0, output_1.emit)(lines.join('\n'));
191
191
  }
192
192
  async function handleDownload(appId, remoteRaw, localRaw) {
193
193
  const filePath = await resolveRemotePath(appId, remoteRaw);
@@ -198,7 +198,7 @@ async function handleDownload(appId, remoteRaw, localRaw) {
198
198
  });
199
199
  const baseName = node_path_1.default.basename(filePath);
200
200
  let localTarget = expandHome(localRaw);
201
- if (localTarget === "./" || localTarget === "." || localTarget.endsWith("/")) {
201
+ if (localTarget === './' || localTarget === '.' || localTarget.endsWith('/')) {
202
202
  localTarget = node_path_1.default.join(localTarget, baseName);
203
203
  }
204
204
  else if (node_fs_1.default.existsSync(localTarget) && node_fs_1.default.statSync(localTarget).isDirectory()) {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleFileSign = exports.handleFileRm = exports.handleFileCp = exports.handleFileStat = exports.handleFileLs = void 0;
3
+ exports.handleFileQuota = exports.handleFileSign = exports.handleFileRm = exports.handleFileCp = exports.handleFileStat = exports.handleFileLs = void 0;
4
4
  var ls_1 = require("./ls");
5
5
  Object.defineProperty(exports, "handleFileLs", { enumerable: true, get: function () { return ls_1.handleFileLs; } });
6
6
  var stat_1 = require("./stat");
@@ -11,3 +11,5 @@ var rm_1 = require("./rm");
11
11
  Object.defineProperty(exports, "handleFileRm", { enumerable: true, get: function () { return rm_1.handleFileRm; } });
12
12
  var sign_1 = require("./sign");
13
13
  Object.defineProperty(exports, "handleFileSign", { enumerable: true, get: function () { return sign_1.handleFileSign; } });
14
+ var quota_1 = require("./quota");
15
+ Object.defineProperty(exports, "handleFileQuota", { enumerable: true, get: function () { return quota_1.handleFileQuota; } });