@lark-apaas/miaoda-cli 0.1.2 → 0.1.3-alpha.09899c4
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 +8 -7
- package/dist/api/app/api.js +25 -0
- package/dist/api/app/index.js +15 -0
- package/dist/api/app/schemas.js +79 -0
- package/dist/api/app/types.js +58 -0
- package/dist/api/db/api.js +390 -55
- package/dist/api/db/client.js +65 -25
- package/dist/api/db/index.js +12 -1
- package/dist/api/db/parsers.js +20 -20
- package/dist/api/db/sql-keywords.js +87 -87
- package/dist/api/deploy/api.js +60 -0
- package/dist/api/deploy/index.js +16 -0
- package/dist/api/deploy/schemas.js +105 -0
- package/dist/api/deploy/types.js +22 -0
- package/dist/api/file/api.js +89 -87
- package/dist/api/file/client.js +62 -22
- package/dist/api/file/detect.js +3 -3
- package/dist/api/file/index.js +2 -1
- package/dist/api/file/parsers.js +18 -7
- package/dist/api/index.js +7 -1
- package/dist/api/observability/api.js +52 -0
- package/dist/api/observability/index.js +16 -0
- package/dist/api/observability/schemas.js +60 -0
- package/dist/api/observability/types.js +27 -0
- package/dist/api/plugin/api.js +31 -31
- package/dist/cli/commands/app/index.js +62 -0
- package/dist/cli/commands/db/index.js +600 -59
- package/dist/cli/commands/deploy/index.js +155 -0
- package/dist/cli/commands/file/index.js +91 -63
- package/dist/cli/commands/index.js +10 -6
- package/dist/cli/commands/observability/index.js +240 -0
- package/dist/cli/commands/plugin/index.js +27 -27
- package/dist/cli/commands/shared.js +86 -10
- package/dist/cli/handlers/app/get.js +47 -0
- package/dist/cli/handlers/app/index.js +7 -0
- package/dist/cli/handlers/app/update.js +59 -0
- package/dist/cli/handlers/db/_operator.js +35 -0
- package/dist/cli/handlers/db/audit.js +383 -0
- package/dist/cli/handlers/db/changelog.js +160 -0
- package/dist/cli/handlers/db/data.js +34 -34
- package/dist/cli/handlers/db/index.js +17 -1
- package/dist/cli/handlers/db/migration.js +245 -0
- package/dist/cli/handlers/db/quota.js +68 -0
- package/dist/cli/handlers/db/recovery.js +387 -0
- package/dist/cli/handlers/db/schema.js +35 -36
- package/dist/cli/handlers/db/sql.js +70 -71
- package/dist/cli/handlers/deploy/deploy.js +84 -0
- package/dist/cli/handlers/deploy/error-log.js +60 -0
- package/dist/cli/handlers/deploy/format.js +39 -0
- package/dist/cli/handlers/deploy/get.js +71 -0
- package/dist/cli/handlers/deploy/helpers.js +41 -0
- package/dist/cli/handlers/deploy/history.js +70 -0
- package/dist/cli/handlers/deploy/index.js +14 -0
- package/dist/cli/handlers/deploy/polling.js +162 -0
- package/dist/cli/handlers/file/cp.js +31 -32
- package/dist/cli/handlers/file/index.js +3 -1
- package/dist/cli/handlers/file/ls.js +6 -7
- package/dist/cli/handlers/file/quota.js +66 -0
- package/dist/cli/handlers/file/rm.js +33 -32
- package/dist/cli/handlers/file/sign.js +4 -5
- package/dist/cli/handlers/file/stat.js +11 -11
- package/dist/cli/handlers/observability/analytics.js +212 -0
- package/dist/cli/handlers/observability/helpers.js +66 -0
- package/dist/cli/handlers/observability/index.js +12 -0
- package/dist/cli/handlers/observability/log.js +94 -0
- package/dist/cli/handlers/observability/metric.js +208 -0
- package/dist/cli/handlers/observability/trace.js +102 -0
- package/dist/cli/handlers/plugin/plugin-local.js +53 -53
- package/dist/cli/handlers/plugin/plugin.js +15 -15
- package/dist/cli/help.js +16 -16
- package/dist/main.js +13 -9
- package/dist/utils/args.js +8 -0
- package/dist/utils/colors.js +2 -2
- package/dist/utils/config.js +2 -2
- package/dist/utils/devops-error.js +28 -0
- package/dist/utils/error.js +2 -2
- package/dist/utils/git.js +29 -0
- package/dist/utils/http.js +119 -1
- package/dist/utils/index.js +15 -1
- package/dist/utils/output.js +373 -20
- package/dist/utils/poll.js +35 -0
- package/dist/utils/render.js +27 -27
- package/dist/utils/spinner.js +46 -0
- package/dist/utils/time.js +208 -0
- package/package.json +7 -5
|
@@ -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 =
|
|
64
|
+
const appId = opts.appId;
|
|
66
65
|
const sql = await readSql(query);
|
|
67
66
|
if (!sql.trim()) {
|
|
68
|
-
throw new error_1.AppError(
|
|
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)(
|
|
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 =
|
|
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)(
|
|
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)(
|
|
145
|
-
stdio: [
|
|
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(
|
|
157
|
-
proc.stderr.on(
|
|
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(
|
|
161
|
+
proc.kill('SIGKILL');
|
|
163
162
|
}, TIMEOUT_MS);
|
|
164
|
-
proc.on(
|
|
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)(
|
|
172
|
+
(0, logger_1.debug)('[db sql] gen:db-schema completed');
|
|
174
173
|
}
|
|
175
174
|
resolve();
|
|
176
175
|
});
|
|
177
|
-
proc.on(
|
|
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,
|
|
198
|
-
const raw = await node_fs_1.promises.readFile(pkgPath,
|
|
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?.[
|
|
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 ===
|
|
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(
|
|
222
|
-
process.stdin.on(
|
|
219
|
+
let data = '';
|
|
220
|
+
process.stdin.setEncoding('utf8');
|
|
221
|
+
process.stdin.on('data', (chunk) => {
|
|
223
222
|
data += chunk;
|
|
224
223
|
});
|
|
225
|
-
process.stdin.on(
|
|
224
|
+
process.stdin.on('end', () => {
|
|
226
225
|
resolve(data);
|
|
227
226
|
});
|
|
228
|
-
process.stdin.on(
|
|
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 ===
|
|
237
|
+
if (parsed.kind === 'select') {
|
|
239
238
|
if (parsed.rows.length === 0) {
|
|
240
|
-
(0, output_1.emit)(
|
|
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 ===
|
|
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 ?
|
|
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(
|
|
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 ===
|
|
258
|
+
if (parsed.kind === 'select') {
|
|
260
259
|
// PRD 单 SELECT:data 直接是行数组(按 --json 字段投影裁剪)
|
|
261
260
|
return { data: projectRows(parsed.rows) };
|
|
262
261
|
}
|
|
263
|
-
if (parsed.kind ===
|
|
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 ===
|
|
282
|
-
return { command:
|
|
280
|
+
if (parsed.kind === 'select') {
|
|
281
|
+
return { command: 'SELECT', rows: projectRows(parsed.rows) };
|
|
283
282
|
}
|
|
284
|
-
if (parsed.kind ===
|
|
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 !==
|
|
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 ===
|
|
323
|
+
if (parsed.kind === 'select') {
|
|
325
324
|
const n = parsed.rows.length;
|
|
326
|
-
lines.push(`Statement ${String(idx)}: SELECT (${String(n)} row${n === 1 ?
|
|
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 ===
|
|
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 ?
|
|
341
|
-
lines.push(`Statement ${String(idx)}: ${tty ? colors_1.c.success(
|
|
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(
|
|
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(
|
|
350
|
+
(0, output_1.emit)(lines.join('\n'));
|
|
352
351
|
}
|
|
353
352
|
function dmlVerb(type) {
|
|
354
353
|
switch (type) {
|
|
355
|
-
case
|
|
356
|
-
return
|
|
357
|
-
case
|
|
358
|
-
return
|
|
359
|
-
case
|
|
360
|
-
return
|
|
361
|
-
case
|
|
362
|
-
return
|
|
363
|
-
case
|
|
364
|
-
return
|
|
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(
|
|
383
|
+
return tty ? colors_1.c.muted('NULL') : 'NULL';
|
|
385
384
|
}
|
|
386
|
-
if (typeof v ===
|
|
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 ===
|
|
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:
|
|
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
|
-
:
|
|
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
|
-
:
|
|
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(
|
|
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 ===
|
|
608
|
+
if (e.command === 'BEGIN')
|
|
610
609
|
depth++;
|
|
611
|
-
else if (e.command ===
|
|
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 ===
|
|
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(
|
|
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(
|
|
642
|
-
process.stderr.write(lines.join(
|
|
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 ===
|
|
648
|
-
return `SELECT (${String(r.rows.length)} row${r.rows.length === 1 ?
|
|
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 ===
|
|
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 ?
|
|
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
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
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.handleDeployErrorLog = handleDeployErrorLog;
|
|
37
|
+
const api = __importStar(require("../../../api/index"));
|
|
38
|
+
const output_1 = require("../../../utils/output");
|
|
39
|
+
const args_1 = require("../../../utils/args");
|
|
40
|
+
const index_1 = require("../../../api/deploy/index");
|
|
41
|
+
const helpers_1 = require("./helpers");
|
|
42
|
+
/** miaoda deploy error-log <deploy-id> */
|
|
43
|
+
async function handleDeployErrorLog(opts) {
|
|
44
|
+
if (!opts.deployId)
|
|
45
|
+
(0, args_1.failArgs)('<deploy-id> 必填');
|
|
46
|
+
const appID = opts.appId;
|
|
47
|
+
(0, helpers_1.parseDeployId)(opts.deployId); // 仅校验数字形式;URL 直接用原字符串
|
|
48
|
+
const instanceID = opts.deployId;
|
|
49
|
+
const resp = await api.deploy.getErrorLog({ appID, instanceID });
|
|
50
|
+
(0, output_1.emit)({
|
|
51
|
+
data: resp.errorJobs,
|
|
52
|
+
next_cursor: null,
|
|
53
|
+
has_more: false,
|
|
54
|
+
}, index_1.errorJobSchema);
|
|
55
|
+
// pretty 模式额外打一行整体状态。
|
|
56
|
+
if (!process.stdout.isTTY)
|
|
57
|
+
return;
|
|
58
|
+
const statusText = (0, index_1.nodeStatusText)(resp.status) ?? String(resp.status);
|
|
59
|
+
process.stdout.write(`\n— pipeline status: ${statusText}\n`);
|
|
60
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatStatusMetadata = formatStatusMetadata;
|
|
4
|
+
exports.formatSimpleInstance = formatSimpleInstance;
|
|
5
|
+
exports.formatPipelineInstance = formatPipelineInstance;
|
|
6
|
+
const index_1 = require("../../../api/deploy/index");
|
|
7
|
+
function formatStatusMetadata(statusCode) {
|
|
8
|
+
return {
|
|
9
|
+
status: (0, index_1.nodeStatusText)(statusCode) ?? `unknown(${String(statusCode)})`,
|
|
10
|
+
status_code: statusCode,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function formatSimpleInstance(instance) {
|
|
14
|
+
if (instance.status === undefined)
|
|
15
|
+
return { ...instance };
|
|
16
|
+
return {
|
|
17
|
+
...instance,
|
|
18
|
+
...formatStatusMetadata(instance.status),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function formatPipelineInstance(detail) {
|
|
22
|
+
const stages = Array.isArray(detail.stages) ? detail.stages : [];
|
|
23
|
+
return {
|
|
24
|
+
...detail,
|
|
25
|
+
...formatStatusMetadata(detail.status),
|
|
26
|
+
stages: stages.map((stage) => ({
|
|
27
|
+
...stage,
|
|
28
|
+
...formatStatusMetadata(stage.status),
|
|
29
|
+
jobs: stage.jobs.map((group) => group.map((job) => ({
|
|
30
|
+
...job,
|
|
31
|
+
...formatStatusMetadata(job.status),
|
|
32
|
+
steps: job.steps?.map((step) => ({
|
|
33
|
+
...step,
|
|
34
|
+
...formatStatusMetadata(step.status),
|
|
35
|
+
})),
|
|
36
|
+
}))),
|
|
37
|
+
})),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
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.handleDeployGet = handleDeployGet;
|
|
37
|
+
const api = __importStar(require("../../../api/index"));
|
|
38
|
+
const output_1 = require("../../../utils/output");
|
|
39
|
+
const args_1 = require("../../../utils/args");
|
|
40
|
+
const error_1 = require("../../../utils/error");
|
|
41
|
+
const index_1 = require("../../../api/deploy/index");
|
|
42
|
+
const helpers_1 = require("./helpers");
|
|
43
|
+
const format_1 = require("./format");
|
|
44
|
+
/**
|
|
45
|
+
* 单页拉取上限:BAM listPipelineInstances 限制 1~500,取 500 让 deploy get
|
|
46
|
+
* 在最近 500 条记录里按 ID 命中。
|
|
47
|
+
*/
|
|
48
|
+
const DEPLOY_GET_LOOKUP_LIMIT = 500;
|
|
49
|
+
/**
|
|
50
|
+
* miaoda deploy get <deploy-id>
|
|
51
|
+
*
|
|
52
|
+
* BAM 没有 by-ID 的查询接口,复用 listPipelineInstances 在最近窗口里按 ID 筛一条。
|
|
53
|
+
* 命中失败时抛 DEPLOY_NOT_FOUND,提示用户用 history 查窗口。
|
|
54
|
+
*/
|
|
55
|
+
async function handleDeployGet(opts) {
|
|
56
|
+
if (!opts.deployId)
|
|
57
|
+
(0, args_1.failArgs)('<deploy-id> 必填');
|
|
58
|
+
const appID = opts.appId;
|
|
59
|
+
const deployId = (0, helpers_1.parseDeployId)(opts.deployId);
|
|
60
|
+
const resp = await api.deploy.listPipelineInstances({
|
|
61
|
+
appID,
|
|
62
|
+
limit: DEPLOY_GET_LOOKUP_LIMIT,
|
|
63
|
+
});
|
|
64
|
+
const instance = resp.instances?.find((it) => Number(it.ID) === deployId);
|
|
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 是否在最近窗口内'],
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
(0, output_1.emit)({ data: (0, format_1.formatSimpleInstance)(instance), next_cursor: null, has_more: false }, index_1.deployGetSchema);
|
|
71
|
+
}
|