@lark-apaas/miaoda-cli 0.1.2 → 0.1.3-alpha.2a09432

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 (84) hide show
  1. package/README.md +8 -7
  2. package/dist/api/app/api.js +25 -0
  3. package/dist/api/app/index.js +15 -0
  4. package/dist/api/app/schemas.js +79 -0
  5. package/dist/api/app/types.js +58 -0
  6. package/dist/api/db/api.js +361 -54
  7. package/dist/api/db/client.js +60 -24
  8. package/dist/api/db/index.js +11 -1
  9. package/dist/api/db/parsers.js +20 -20
  10. package/dist/api/db/sql-keywords.js +87 -87
  11. package/dist/api/deploy/api.js +60 -0
  12. package/dist/api/deploy/index.js +16 -0
  13. package/dist/api/deploy/schemas.js +105 -0
  14. package/dist/api/deploy/types.js +22 -0
  15. package/dist/api/file/api.js +89 -87
  16. package/dist/api/file/client.js +62 -22
  17. package/dist/api/file/detect.js +3 -3
  18. package/dist/api/file/index.js +2 -1
  19. package/dist/api/file/parsers.js +18 -7
  20. package/dist/api/index.js +7 -1
  21. package/dist/api/observability/api.js +52 -0
  22. package/dist/api/observability/index.js +16 -0
  23. package/dist/api/observability/schemas.js +60 -0
  24. package/dist/api/observability/types.js +27 -0
  25. package/dist/api/plugin/api.js +31 -31
  26. package/dist/cli/commands/app/index.js +62 -0
  27. package/dist/cli/commands/db/index.js +599 -59
  28. package/dist/cli/commands/deploy/index.js +155 -0
  29. package/dist/cli/commands/file/index.js +90 -62
  30. package/dist/cli/commands/index.js +10 -6
  31. package/dist/cli/commands/observability/index.js +240 -0
  32. package/dist/cli/commands/plugin/index.js +27 -27
  33. package/dist/cli/commands/shared.js +86 -10
  34. package/dist/cli/handlers/app/get.js +47 -0
  35. package/dist/cli/handlers/app/index.js +7 -0
  36. package/dist/cli/handlers/app/update.js +59 -0
  37. package/dist/cli/handlers/db/_operator.js +35 -0
  38. package/dist/cli/handlers/db/audit.js +303 -0
  39. package/dist/cli/handlers/db/changelog.js +132 -0
  40. package/dist/cli/handlers/db/data.js +34 -34
  41. package/dist/cli/handlers/db/index.js +17 -1
  42. package/dist/cli/handlers/db/migration.js +235 -0
  43. package/dist/cli/handlers/db/quota.js +68 -0
  44. package/dist/cli/handlers/db/recovery.js +357 -0
  45. package/dist/cli/handlers/db/schema.js +35 -36
  46. package/dist/cli/handlers/db/sql.js +70 -71
  47. package/dist/cli/handlers/deploy/deploy.js +84 -0
  48. package/dist/cli/handlers/deploy/error-log.js +60 -0
  49. package/dist/cli/handlers/deploy/format.js +39 -0
  50. package/dist/cli/handlers/deploy/get.js +71 -0
  51. package/dist/cli/handlers/deploy/helpers.js +41 -0
  52. package/dist/cli/handlers/deploy/history.js +70 -0
  53. package/dist/cli/handlers/deploy/index.js +14 -0
  54. package/dist/cli/handlers/deploy/polling.js +162 -0
  55. package/dist/cli/handlers/file/cp.js +31 -32
  56. package/dist/cli/handlers/file/index.js +3 -1
  57. package/dist/cli/handlers/file/ls.js +6 -7
  58. package/dist/cli/handlers/file/quota.js +66 -0
  59. package/dist/cli/handlers/file/rm.js +25 -26
  60. package/dist/cli/handlers/file/sign.js +4 -5
  61. package/dist/cli/handlers/file/stat.js +11 -11
  62. package/dist/cli/handlers/observability/analytics.js +212 -0
  63. package/dist/cli/handlers/observability/helpers.js +66 -0
  64. package/dist/cli/handlers/observability/index.js +12 -0
  65. package/dist/cli/handlers/observability/log.js +94 -0
  66. package/dist/cli/handlers/observability/metric.js +208 -0
  67. package/dist/cli/handlers/observability/trace.js +102 -0
  68. package/dist/cli/handlers/plugin/plugin-local.js +53 -53
  69. package/dist/cli/handlers/plugin/plugin.js +15 -15
  70. package/dist/cli/help.js +16 -16
  71. package/dist/main.js +13 -9
  72. package/dist/utils/args.js +8 -0
  73. package/dist/utils/colors.js +2 -2
  74. package/dist/utils/config.js +2 -2
  75. package/dist/utils/devops-error.js +28 -0
  76. package/dist/utils/error.js +2 -2
  77. package/dist/utils/git.js +29 -0
  78. package/dist/utils/http.js +119 -1
  79. package/dist/utils/index.js +15 -1
  80. package/dist/utils/output.js +373 -20
  81. package/dist/utils/poll.js +27 -0
  82. package/dist/utils/render.js +27 -27
  83. package/dist/utils/time.js +208 -0
  84. package/package.json +7 -5
@@ -39,15 +39,14 @@ const api = __importStar(require("../../../api/index"));
39
39
  const error_1 = require("../../../utils/error");
40
40
  const output_1 = require("../../../utils/output");
41
41
  const render_1 = require("../../../utils/render");
42
- const shared_1 = require("../../../cli/commands/shared");
43
42
  const colors_1 = require("../../../utils/colors");
44
43
  const index_1 = require("../../../api/db/index");
45
44
  // ── schema list ──
46
45
  async function handleDbSchemaList(opts) {
47
- const appId = (0, shared_1.resolveAppId)(opts);
46
+ const appId = opts.appId;
48
47
  const resp = await api.db.getSchema({
49
48
  appId,
50
- format: "schema",
49
+ format: 'schema',
51
50
  includeStats: true,
52
51
  dbBranch: opts.env,
53
52
  });
@@ -57,30 +56,30 @@ async function handleDbSchemaList(opts) {
57
56
  return;
58
57
  }
59
58
  if (tables.length === 0) {
60
- (0, output_1.emit)("No tables found.");
59
+ (0, output_1.emit)('No tables found.');
61
60
  return;
62
61
  }
63
62
  const tty = (0, render_1.isStdoutTty)();
64
63
  // PRD 对齐:TTY 表头用 `size`(友好格式),non-TTY 用 `size_bytes`(原始整数)。
65
64
  // updated_at 暂时不展示——PG pg_catalog 不存真实表时间,详见 renderDetail 注释。
66
65
  const headers = [
67
- "name",
68
- "description",
69
- "estimated_row_count",
70
- tty ? "size" : "size_bytes",
71
- "columns",
66
+ 'name',
67
+ 'description',
68
+ 'estimated_row_count',
69
+ tty ? 'size' : 'size_bytes',
70
+ 'columns',
72
71
  ];
73
72
  const rows = tables.map((t) => [
74
73
  t.name,
75
- t.description ?? "",
76
- t.estimated_row_count === null ? "" : String(t.estimated_row_count),
77
- t.size_bytes === null ? "" : tty ? (0, render_1.formatSize)(t.size_bytes) : String(t.size_bytes),
74
+ t.description ?? '',
75
+ t.estimated_row_count === null ? '' : String(t.estimated_row_count),
76
+ t.size_bytes === null ? '' : tty ? (0, render_1.formatSize)(t.size_bytes) : String(t.size_bytes),
78
77
  String(t.columns),
79
78
  ]);
80
79
  (0, output_1.emit)(tty ? (0, render_1.renderAlignedTable)(headers, rows) : (0, render_1.renderTsv)(headers, rows));
81
80
  }
82
81
  async function handleDbSchemaGet(table, opts) {
83
- const appId = (0, shared_1.resolveAppId)(opts);
82
+ const appId = opts.appId;
84
83
  const tty = (0, render_1.isStdoutTty)();
85
84
  const forceDdl = Boolean(opts.ddl);
86
85
  const wantsStructured = (0, output_1.isJsonMode)() || (tty && !forceDdl);
@@ -88,13 +87,13 @@ async function handleDbSchemaGet(table, opts) {
88
87
  // non-TTY 或 --ddl:直接取完整 DDL 输出
89
88
  const ddlResp = await api.db.getSchema({
90
89
  appId,
91
- format: "ddl",
90
+ format: 'ddl',
92
91
  tableNames: table,
93
92
  dbBranch: opts.env,
94
93
  });
95
94
  const sql = ddlResp.ddl?.[table];
96
95
  if (!sql) {
97
- throw new error_1.AppError("TABLE_NOT_FOUND", `Table '${table}' does not exist`, {
96
+ throw new error_1.AppError('TABLE_NOT_FOUND', `Table '${table}' does not exist`, {
98
97
  next_actions: [
99
98
  `Did you mean another table? Run "miaoda db schema list" to see all tables.`,
100
99
  ],
@@ -106,14 +105,14 @@ async function handleDbSchemaGet(table, opts) {
106
105
  // TTY / JSON:取结构化
107
106
  const resp = await api.db.getSchema({
108
107
  appId,
109
- format: "schema",
108
+ format: 'schema',
110
109
  tableNames: table,
111
110
  includeStats: true,
112
111
  dbBranch: opts.env,
113
112
  });
114
113
  const detail = (0, index_1.pickTableDetail)(resp.schema, table);
115
114
  if (!detail) {
116
- throw new error_1.AppError("TABLE_NOT_FOUND", `Table '${table}' does not exist`, {
115
+ throw new error_1.AppError('TABLE_NOT_FOUND', `Table '${table}' does not exist`, {
117
116
  next_actions: [`Did you mean another table? Run "miaoda db schema list" to see all tables.`],
118
117
  });
119
118
  }
@@ -124,54 +123,54 @@ async function handleDbSchemaGet(table, opts) {
124
123
  (0, output_1.emit)(renderDetail(detail, tty));
125
124
  }
126
125
  function renderDetail(d, tty) {
127
- const systemFields = d.columns.filter((c) => c.name.startsWith("_"));
128
- const userFields = d.columns.filter((c) => !c.name.startsWith("_"));
126
+ const systemFields = d.columns.filter((c) => c.name.startsWith('_'));
127
+ const userFields = d.columns.filter((c) => !c.name.startsWith('_'));
129
128
  // header 布局:Name / Description / Columns(含"+ N system") / Estimated Rows / Size。
130
129
  // 不展示 Created / Updated:PG pg_catalog 不存表创建时间,dataloom 用 OID
131
130
  // 构造的伪时间戳(baseTime=2020-01-01 + OID 秒偏移),仅保排序意义、绝对值
132
131
  // 误导性强,先去掉。后续如果接 ddl_change_log 取真实时间再加回。
133
132
  const header = [
134
133
  // 表名做 cyan 强调(spec:spec 里的"命令名/表名"类强调值都走 highlight)
135
- ["Name", tty ? colors_1.c.highlight(d.name) : d.name],
136
- ["Description", d.description ?? ""],
134
+ ['Name', tty ? colors_1.c.highlight(d.name) : d.name],
135
+ ['Description', d.description ?? ''],
137
136
  [
138
- "Columns",
137
+ 'Columns',
139
138
  systemFields.length > 0
140
139
  ? `${String(userFields.length)} (+ ${String(systemFields.length)} system)`
141
140
  : String(userFields.length),
142
141
  ],
143
- ["Estimated Rows", d.estimated_row_count === null ? "" : String(d.estimated_row_count)],
144
- ["Size", d.size_bytes === null ? "" : (0, render_1.formatSize)(d.size_bytes)],
142
+ ['Estimated Rows', d.estimated_row_count === null ? '' : String(d.estimated_row_count)],
143
+ ['Size', d.size_bytes === null ? '' : (0, render_1.formatSize)(d.size_bytes)],
145
144
  ];
146
- const colHeaders = ["column", "type", "nullable", "default", "comment"];
145
+ const colHeaders = ['column', 'type', 'nullable', 'default', 'comment'];
147
146
  const colRows = userFields.map((c) => [
148
147
  c.name,
149
148
  c.type,
150
- c.nullable ? "yes" : "no",
151
- c.default ?? "",
152
- c.comment ?? "",
149
+ c.nullable ? 'yes' : 'no',
150
+ c.default ?? '',
151
+ c.comment ?? '',
153
152
  ]);
154
153
  const parts = [];
155
154
  parts.push((0, render_1.renderKeyValue)(header, tty));
156
- parts.push("");
155
+ parts.push('');
157
156
  parts.push(tty ? (0, render_1.renderAlignedTable)(colHeaders, colRows) : (0, render_1.renderTsv)(colHeaders, colRows));
158
157
  // Constraints 段(PRIMARY KEY / UNIQUE):表级约束独立成段,与普通索引分离展示
159
158
  if (d.constraints.length > 0) {
160
- parts.push("");
161
- parts.push(tty ? " Constraints:" : "Constraints:");
159
+ parts.push('');
160
+ parts.push(tty ? ' Constraints:' : 'Constraints:');
162
161
  for (const c of d.constraints) {
163
- const line = `${c.type} (${c.columns.join(", ")})`;
162
+ const line = `${c.type} (${c.columns.join(', ')})`;
164
163
  parts.push(tty ? ` ${line}` : line);
165
164
  }
166
165
  }
167
166
  // Indexes 段:普通索引,格式 "<name> ON <col1, col2> USING <method>"
168
167
  if (d.indexes.length > 0) {
169
- parts.push("");
170
- parts.push(tty ? " Indexes:" : "Indexes:");
168
+ parts.push('');
169
+ parts.push(tty ? ' Indexes:' : 'Indexes:');
171
170
  for (const idx of d.indexes) {
172
- const line = `${idx.name} ON ${idx.columns.join(", ")} USING ${idx.method}`;
171
+ const line = `${idx.name} ON ${idx.columns.join(', ')} USING ${idx.method}`;
173
172
  parts.push(tty ? ` ${line}` : line);
174
173
  }
175
174
  }
176
- return parts.join("\n");
175
+ return parts.join('\n');
177
176
  }
@@ -42,7 +42,6 @@ const error_1 = require("../../../utils/error");
42
42
  const output_1 = require("../../../utils/output");
43
43
  const config_1 = require("../../../utils/config");
44
44
  const logger_1 = require("../../../utils/logger");
45
- const shared_1 = require("../../../cli/commands/shared");
46
45
  const render_1 = require("../../../utils/render");
47
46
  const colors_1 = require("../../../utils/colors");
48
47
  const fuzzy_match_1 = require("../../../utils/fuzzy-match");
@@ -62,10 +61,10 @@ const node_path_1 = __importDefault(require("node:path"));
62
61
  * 末尾汇总,--json 输出 data 数组。
63
62
  */
64
63
  async function handleDbSql(query, opts) {
65
- const appId = (0, shared_1.resolveAppId)(opts);
64
+ const appId = opts.appId;
66
65
  const sql = await readSql(query);
67
66
  if (!sql.trim()) {
68
- 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)');
69
68
  }
70
69
  let results;
71
70
  try {
@@ -87,7 +86,7 @@ async function handleDbSql(query, opts) {
87
86
  (0, output_1.emit)({ data: [] });
88
87
  return;
89
88
  }
90
- (0, output_1.emit)("✓ No results");
89
+ (0, output_1.emit)((0, render_1.isStdoutTty)() ? colors_1.c.success('✓ No results') : 'OK No results');
91
90
  return;
92
91
  }
93
92
  if (results.length === 1) {
@@ -122,7 +121,7 @@ async function handleDbSql(query, opts) {
122
121
  * statement 都执行成功,无需再校验逐条状态。
123
122
  */
124
123
  /** agent runtime 中固定的项目根目录;放在此处便于以后调整。 */
125
- const AGENT_PROJECT_ROOT = "/home/gem/workspace/code";
124
+ const AGENT_PROJECT_ROOT = '/home/gem/workspace/code';
126
125
  /** drain 子进程 pipe stream:默认静默模式下消费 chunk 防止缓冲区反压。 */
127
126
  function drainChunk(_chunk) {
128
127
  // 显式接 chunk 参数让 ESLint 不再当成 empty function;不做任何处理
@@ -132,7 +131,7 @@ async function maybeSyncAgentSchema(results) {
132
131
  return;
133
132
  const projectRoot = await resolveAgentProjectRoot();
134
133
  if (!projectRoot) {
135
- (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');
136
135
  return;
137
136
  }
138
137
  const verbose = (0, config_1.getConfig)().verbose;
@@ -141,8 +140,8 @@ async function maybeSyncAgentSchema(results) {
141
140
  (0, logger_1.debug)(`[db sql] DDL detected, running \`npm run gen:db-schema\` in ${projectRoot}`);
142
141
  try {
143
142
  await new Promise((resolve) => {
144
- const proc = (0, node_child_process_1.spawn)("npm", ["run", "gen:db-schema"], {
145
- stdio: ["ignore", "pipe", "pipe"],
143
+ const proc = (0, node_child_process_1.spawn)('npm', ['run', 'gen:db-schema'], {
144
+ stdio: ['ignore', 'pipe', 'pipe'],
146
145
  cwd: projectRoot,
147
146
  });
148
147
  // --verbose:实时透传到 stderr 看进度;默认完全静默丢弃。
@@ -153,15 +152,15 @@ async function maybeSyncAgentSchema(results) {
153
152
  proc.stderr.pipe(process.stderr);
154
153
  }
155
154
  else {
156
- proc.stdout.on("data", drainChunk);
157
- proc.stderr.on("data", drainChunk);
155
+ proc.stdout.on('data', drainChunk);
156
+ proc.stderr.on('data', drainChunk);
158
157
  }
159
158
  let timedOut = false;
160
159
  const timer = setTimeout(() => {
161
160
  timedOut = true;
162
- proc.kill("SIGKILL");
161
+ proc.kill('SIGKILL');
163
162
  }, TIMEOUT_MS);
164
- proc.on("close", (code) => {
163
+ proc.on('close', (code) => {
165
164
  clearTimeout(timer);
166
165
  if (timedOut) {
167
166
  (0, logger_1.debug)(`[db sql] gen:db-schema timed out after ${String(TIMEOUT_MS / 1000)}s, killed`);
@@ -170,11 +169,11 @@ async function maybeSyncAgentSchema(results) {
170
169
  (0, logger_1.debug)(`[db sql] gen:db-schema exited with code ${String(code)}`);
171
170
  }
172
171
  else {
173
- (0, logger_1.debug)("[db sql] gen:db-schema completed");
172
+ (0, logger_1.debug)('[db sql] gen:db-schema completed');
174
173
  }
175
174
  resolve();
176
175
  });
177
- proc.on("error", (err) => {
176
+ proc.on('error', (err) => {
178
177
  clearTimeout(timer);
179
178
  (0, logger_1.debug)(`[db sql] gen:db-schema spawn error: ${err.message}`);
180
179
  resolve();
@@ -194,10 +193,10 @@ async function maybeSyncAgentSchema(results) {
194
193
  */
195
194
  async function resolveAgentProjectRoot() {
196
195
  try {
197
- const pkgPath = node_path_1.default.join(AGENT_PROJECT_ROOT, "package.json");
198
- 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');
199
198
  const pkg = JSON.parse(raw);
200
- if (pkg.scripts?.["gen:db-schema"])
199
+ if (pkg.scripts?.['gen:db-schema'])
201
200
  return AGENT_PROJECT_ROOT;
202
201
  }
203
202
  catch {
@@ -207,7 +206,7 @@ async function resolveAgentProjectRoot() {
207
206
  }
208
207
  /** 是否有任意一条 statement 是 DDL(CREATE / ALTER / DROP / GRANT / TRUNCATE / COMMENT 等)。 */
209
208
  function hasDdl(results) {
210
- return results.some((r) => (0, index_1.parseSqlResult)(r).kind === "ddl");
209
+ return results.some((r) => (0, index_1.parseSqlResult)(r).kind === 'ddl');
211
210
  }
212
211
  /** 读取 stdin 并返回完整 SQL 文本(stdin 不是 TTY 即认为被 pipe)。 */
213
212
  async function readSql(inline) {
@@ -215,17 +214,17 @@ async function readSql(inline) {
215
214
  return inline;
216
215
  const stdin = process.stdin;
217
216
  if (stdin.isTTY)
218
- return "";
217
+ return '';
219
218
  return new Promise((resolve, reject) => {
220
- let data = "";
221
- process.stdin.setEncoding("utf8");
222
- process.stdin.on("data", (chunk) => {
219
+ let data = '';
220
+ process.stdin.setEncoding('utf8');
221
+ process.stdin.on('data', (chunk) => {
223
222
  data += chunk;
224
223
  });
225
- process.stdin.on("end", () => {
224
+ process.stdin.on('end', () => {
226
225
  resolve(data);
227
226
  });
228
- process.stdin.on("error", reject);
227
+ process.stdin.on('error', reject);
229
228
  });
230
229
  }
231
230
  function renderSingle(raw) {
@@ -235,9 +234,9 @@ function renderSingle(raw) {
235
234
  (0, output_1.emit)(toJson(parsed));
236
235
  return;
237
236
  }
238
- if (parsed.kind === "select") {
237
+ if (parsed.kind === 'select') {
239
238
  if (parsed.rows.length === 0) {
240
- (0, output_1.emit)("(0 rows)");
239
+ (0, output_1.emit)('(0 rows)');
241
240
  return;
242
241
  }
243
242
  const cols = collectColumns(parsed.rows);
@@ -245,22 +244,22 @@ function renderSingle(raw) {
245
244
  (0, output_1.emit)(tty ? (0, render_1.renderAlignedTable)(cols, rows) : (0, render_1.renderTsv)(cols, rows));
246
245
  return;
247
246
  }
248
- if (parsed.kind === "dml") {
247
+ if (parsed.kind === 'dml') {
249
248
  const verb = dmlVerb(parsed.sqlType);
250
249
  (0, output_1.emit)(tty
251
- ? 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}`)
252
251
  : `OK ${String(parsed.affectedRows)} rows ${verb}`);
253
252
  return;
254
253
  }
255
254
  // DDL
256
- (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');
257
256
  }
258
257
  function toJson(parsed) {
259
- if (parsed.kind === "select") {
258
+ if (parsed.kind === 'select') {
260
259
  // PRD 单 SELECT:data 直接是行数组(按 --json 字段投影裁剪)
261
260
  return { data: projectRows(parsed.rows) };
262
261
  }
263
- if (parsed.kind === "dml") {
262
+ if (parsed.kind === 'dml') {
264
263
  // PRD 单 DML:data = {command, rows_affected}
265
264
  return {
266
265
  data: {
@@ -278,10 +277,10 @@ function toJson(parsed) {
278
277
  * {command:"SELECT", rows:[...]}(PRD 约定,避免数组里嵌套数组造成歧义)。
279
278
  */
280
279
  function toMultiElement(parsed) {
281
- if (parsed.kind === "select") {
282
- return { command: "SELECT", rows: projectRows(parsed.rows) };
280
+ if (parsed.kind === 'select') {
281
+ return { command: 'SELECT', rows: projectRows(parsed.rows) };
283
282
  }
284
- if (parsed.kind === "dml") {
283
+ if (parsed.kind === 'dml') {
285
284
  return { command: parsed.sqlType, rows_affected: parsed.affectedRows };
286
285
  }
287
286
  // DDL:用后端给的细粒度 command
@@ -307,10 +306,10 @@ function projectRows(rows) {
307
306
  /** 读取 --json [fields]:返回字段列表;boolean true 或 undefined 返回 null(不裁剪)。 */
308
307
  function parseJsonFields() {
309
308
  const v = (0, config_1.getConfig)().json;
310
- if (typeof v !== "string" || v === "")
309
+ if (typeof v !== 'string' || v === '')
311
310
  return null;
312
311
  return v
313
- .split(",")
312
+ .split(',')
314
313
  .map((s) => s.trim())
315
314
  .filter((s) => s.length > 0);
316
315
  }
@@ -321,9 +320,9 @@ function renderMultiPretty(results) {
321
320
  for (let i = 0; i < results.length; i++) {
322
321
  const parsed = (0, index_1.parseSqlResult)(results[i]);
323
322
  const idx = i + 1;
324
- if (parsed.kind === "select") {
323
+ if (parsed.kind === 'select') {
325
324
  const n = parsed.rows.length;
326
- 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'})`);
327
326
  if (n > 0) {
328
327
  const cols = collectColumns(parsed.rows);
329
328
  const tbl = parsed.rows.map((r) => cols.map((col) => formatCell(r[col], tty)));
@@ -331,37 +330,37 @@ function renderMultiPretty(results) {
331
330
  }
332
331
  // 块间空行(最后一条不留)
333
332
  if (i < results.length - 1)
334
- lines.push("");
333
+ lines.push('');
335
334
  continue;
336
335
  }
337
- if (parsed.kind === "dml") {
336
+ if (parsed.kind === 'dml') {
338
337
  const verb = dmlVerb(parsed.sqlType);
339
338
  const n = parsed.affectedRows;
340
- const body = `${String(n)} row${n === 1 ? "" : "s"} ${verb}`;
341
- 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}`);
342
341
  continue;
343
342
  }
344
343
  // DDL
345
- 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'}`);
346
345
  }
347
346
  // 汇总行:所有 statement 都跑完了
348
347
  lines.push(tty
349
348
  ? colors_1.c.success(`✓ ${String(results.length)} statements executed`)
350
349
  : `OK ${String(results.length)} statements executed`);
351
- (0, output_1.emit)(lines.join("\n"));
350
+ (0, output_1.emit)(lines.join('\n'));
352
351
  }
353
352
  function dmlVerb(type) {
354
353
  switch (type) {
355
- case "INSERT":
356
- return "inserted";
357
- case "UPDATE":
358
- return "updated";
359
- case "DELETE":
360
- return "deleted";
361
- case "MERGE":
362
- return "merged";
363
- case "DML":
364
- 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'; // 未识别子类的兜底
365
364
  }
366
365
  }
367
366
  /** 从第一行收集列顺序;缺失列保留空白(兼容稀疏行)。 */
@@ -381,9 +380,9 @@ function collectColumns(rows) {
381
380
  */
382
381
  function formatCell(v, tty) {
383
382
  if (v === null || v === undefined) {
384
- return tty ? colors_1.c.muted("NULL") : "NULL";
383
+ return tty ? colors_1.c.muted('NULL') : 'NULL';
385
384
  }
386
- if (typeof v === "string") {
385
+ if (typeof v === 'string') {
387
386
  // TTY 下检测到 ISO 8601 时间字符串 → 转成相对时间("3h ago" / "2d ago" /
388
387
  // "2026-03-15"),方便 _created_at / _updated_at 等列直观可读
389
388
  if (tty && ISO_TIMESTAMP_RE.test(v) && !Number.isNaN(Date.parse(v))) {
@@ -391,7 +390,7 @@ function formatCell(v, tty) {
391
390
  }
392
391
  return v;
393
392
  }
394
- if (typeof v === "number" || typeof v === "boolean")
393
+ if (typeof v === 'number' || typeof v === 'boolean')
395
394
  return String(v);
396
395
  // object / array → JSON
397
396
  return JSON.stringify(v);
@@ -488,7 +487,7 @@ async function loadTableMap(ctx) {
488
487
  try {
489
488
  const resp = await api.db.getSchema({
490
489
  appId: ctx.appId,
491
- format: "schema",
490
+ format: 'schema',
492
491
  dbBranch: ctx.env,
493
492
  });
494
493
  return extractTableFieldMap(resp.schema);
@@ -501,7 +500,7 @@ function enrichRelationNotExist(err, relation, tableMap) {
501
500
  const guess = (0, fuzzy_match_1.suggest)(relation, Object.keys(tableMap));
502
501
  err.next_actions.push(guess
503
502
  ? `Did you mean '${guess}'? Run \`miaoda db schema list\` to see all tables.`
504
- : "Run `miaoda db schema list` to see all tables.");
503
+ : 'Run `miaoda db schema list` to see all tables.');
505
504
  }
506
505
  function enrichColumnNotExist(err, colMatch, tableMap) {
507
506
  const colName = colMatch[1];
@@ -523,7 +522,7 @@ function enrichColumnNotExist(err, colMatch, tableMap) {
523
522
  const guess = (0, fuzzy_match_1.suggest)(colName, allCols);
524
523
  err.next_actions.push(guess
525
524
  ? `Did you mean '${guess}'? Run \`miaoda db schema get <table>\` to see columns.`
526
- : "Run `miaoda db schema get <table>` to see all columns.");
525
+ : 'Run `miaoda db schema get <table>` to see all columns.');
527
526
  }
528
527
  /**
529
528
  * 扫整段 SQL 找一个看似"拼错的关键字"token:纯字母 + 长度 ≥ 3 + 不是已知 keyword
@@ -594,7 +593,7 @@ function enrichMultiStatementError(err, sql) {
594
593
  }
595
594
  // rolled_back=true 时追加 spec hint:"Transaction rolled back; no changes persisted."
596
595
  if (err.rolled_back) {
597
- err.next_actions.push("Transaction rolled back; no changes persisted.");
596
+ err.next_actions.push('Transaction rolled back; no changes persisted.');
598
597
  }
599
598
  }
600
599
  /**
@@ -606,9 +605,9 @@ function enrichMultiStatementError(err, sql) {
606
605
  function inferRolledBack(completed) {
607
606
  let depth = 0;
608
607
  for (const e of completed) {
609
- if (e.command === "BEGIN")
608
+ if (e.command === 'BEGIN')
610
609
  depth++;
611
- else if (e.command === "COMMIT" || e.command === "ROLLBACK")
610
+ else if (e.command === 'COMMIT' || e.command === 'ROLLBACK')
612
611
  depth--;
613
612
  }
614
613
  return depth > 0;
@@ -631,25 +630,25 @@ function writeMultiStatementBreakdown(err, completed) {
631
630
  const lines = [];
632
631
  for (let i = 0; i < completed.length; i++) {
633
632
  const e = completed[i];
634
- const verb = e.command === "BEGIN" || e.command === "COMMIT" || e.command === "ROLLBACK"
633
+ const verb = e.command === 'BEGIN' || e.command === 'COMMIT' || e.command === 'ROLLBACK'
635
634
  ? e.command
636
635
  : describeCompleted(e);
637
- 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}`);
638
637
  }
639
638
  // 失败那条
640
639
  const failedIdx = err.statement_index ?? completed.length;
641
- lines.push(`Statement ${String(failedIdx + 1)}: ${tty ? colors_1.c.fail("") : ""}${err.message}`);
642
- 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');
643
642
  }
644
643
  /** completed 单条结果的人类可读描述(用于 stderr breakdown 行尾)。 */
645
644
  function describeCompleted(e) {
646
645
  const r = e;
647
- if (r.command === "SELECT" && Array.isArray(r.rows)) {
648
- 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'})`;
649
648
  }
650
- if (typeof r.rows_affected === "number") {
649
+ if (typeof r.rows_affected === 'number') {
651
650
  const verb = dmlVerb(r.command);
652
- 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}`;
653
652
  }
654
653
  return `${r.command} executed`;
655
654
  }
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.handleDeploy = handleDeploy;
37
+ const api = __importStar(require("../../../api/index"));
38
+ const output_1 = require("../../../utils/output");
39
+ const index_1 = require("../../../api/deploy/index");
40
+ const polling_1 = require("./polling");
41
+ const format_1 = require("./format");
42
+ /** miaoda deploy [--branch ...] [--wait] [--timeout 300] */
43
+ async function handleDeploy(opts) {
44
+ const appID = opts.appId;
45
+ const resp = await api.deploy.createRelease({ appID, branch: opts.branch });
46
+ const pipelineTaskID = resp.pipelineTaskID ?? '';
47
+ if (!opts.wait) {
48
+ if (!(0, output_1.isJsonMode)()) {
49
+ process.stdout.write(`✓ Deployment triggered. (deploy-id: ${pipelineTaskID})\n`);
50
+ process.stdout.write(` hint: No terminal status yet. Poll every 2s with \`miaoda deploy get ${pipelineTaskID} --json\`; if no deploy-id is visible, first get it from \`miaoda deploy history --limit 1 --json\`.\n`);
51
+ }
52
+ (0, output_1.emit)({
53
+ data: { pipelineTaskID },
54
+ next_cursor: null,
55
+ has_more: false,
56
+ });
57
+ return;
58
+ }
59
+ if (!(0, output_1.isJsonMode)()) {
60
+ process.stdout.write(`Waiting for deployment to complete... (deploy-id: ${pipelineTaskID})\n`);
61
+ }
62
+ const detail = await (0, polling_1.waitForPipeline)({
63
+ appID,
64
+ pipelineTaskID,
65
+ timeoutSec: opts.timeout ?? 300,
66
+ });
67
+ if (!(0, output_1.isJsonMode)()) {
68
+ if (detail.status === index_1.NodeStatus.SUCCESS) {
69
+ process.stdout.write('✓ 发布成功\n');
70
+ }
71
+ else if (detail.status === index_1.NodeStatus.FAILED) {
72
+ process.stdout.write('✗ 发布失败\n');
73
+ process.stdout.write(` hint: Run \`miaoda deploy error-log ${pipelineTaskID}\` to view error logs\n`);
74
+ }
75
+ else {
76
+ process.stdout.write('• 发布已取消\n');
77
+ }
78
+ }
79
+ (0, output_1.emit)({
80
+ data: { pipelineTaskID, detail: (0, format_1.formatPipelineInstance)(detail) },
81
+ next_cursor: null,
82
+ has_more: false,
83
+ });
84
+ }