@lark-apaas/miaoda-cli 0.1.2-alpha.ccfdc05 → 0.1.2-alpha.d0d2ae1

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 (58) hide show
  1. package/README.md +7 -8
  2. package/dist/api/db/api.js +250 -6
  3. package/dist/api/db/client.js +36 -0
  4. package/dist/api/db/index.js +9 -1
  5. package/dist/api/file/api.js +15 -0
  6. package/dist/api/file/index.js +2 -1
  7. package/dist/api/index.js +1 -7
  8. package/dist/cli/commands/db/index.js +137 -0
  9. package/dist/cli/commands/file/index.js +7 -0
  10. package/dist/cli/commands/index.js +6 -10
  11. package/dist/cli/commands/shared.js +3 -81
  12. package/dist/cli/handlers/db/audit.js +285 -0
  13. package/dist/cli/handlers/db/changelog.js +117 -0
  14. package/dist/cli/handlers/db/data.js +1 -1
  15. package/dist/cli/handlers/db/index.js +17 -1
  16. package/dist/cli/handlers/db/migration.js +213 -0
  17. package/dist/cli/handlers/{app/update.js → db/quota.js} +29 -20
  18. package/dist/cli/handlers/db/recovery.js +228 -0
  19. package/dist/cli/handlers/file/index.js +3 -1
  20. package/dist/cli/handlers/{deploy/error-log.js → file/quota.js} +27 -22
  21. package/dist/main.js +7 -27
  22. package/dist/utils/http.js +0 -118
  23. package/dist/utils/index.js +1 -13
  24. package/dist/utils/output.js +29 -338
  25. package/package.json +5 -7
  26. package/dist/api/app/api.js +0 -25
  27. package/dist/api/app/index.js +0 -15
  28. package/dist/api/app/schemas.js +0 -79
  29. package/dist/api/app/types.js +0 -58
  30. package/dist/api/deploy/api.js +0 -60
  31. package/dist/api/deploy/index.js +0 -16
  32. package/dist/api/deploy/schemas.js +0 -103
  33. package/dist/api/deploy/types.js +0 -22
  34. package/dist/api/observability/api.js +0 -52
  35. package/dist/api/observability/index.js +0 -16
  36. package/dist/api/observability/schemas.js +0 -60
  37. package/dist/api/observability/types.js +0 -27
  38. package/dist/cli/commands/app/index.js +0 -62
  39. package/dist/cli/commands/deploy/index.js +0 -145
  40. package/dist/cli/commands/observability/index.js +0 -237
  41. package/dist/cli/handlers/app/get.js +0 -48
  42. package/dist/cli/handlers/app/index.js +0 -7
  43. package/dist/cli/handlers/deploy/deploy.js +0 -83
  44. package/dist/cli/handlers/deploy/get.js +0 -70
  45. package/dist/cli/handlers/deploy/helpers.js +0 -41
  46. package/dist/cli/handlers/deploy/history.js +0 -70
  47. package/dist/cli/handlers/deploy/index.js +0 -14
  48. package/dist/cli/handlers/deploy/polling.js +0 -139
  49. package/dist/cli/handlers/observability/analytics.js +0 -212
  50. package/dist/cli/handlers/observability/helpers.js +0 -66
  51. package/dist/cli/handlers/observability/index.js +0 -12
  52. package/dist/cli/handlers/observability/log.js +0 -94
  53. package/dist/cli/handlers/observability/metric.js +0 -208
  54. package/dist/cli/handlers/observability/trace.js +0 -102
  55. package/dist/cli/version.js +0 -15
  56. package/dist/utils/devops-error.js +0 -28
  57. package/dist/utils/git.js +0 -29
  58. package/dist/utils/time.js +0 -203
@@ -0,0 +1,213 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.handleDbMigrationInit = handleDbMigrationInit;
40
+ exports.handleDbMigrationDiff = handleDbMigrationDiff;
41
+ exports.handleDbMigrationApply = handleDbMigrationApply;
42
+ const node_readline_1 = __importDefault(require("node:readline"));
43
+ const api = __importStar(require("../../../api/index"));
44
+ const shared_1 = require("../../../cli/commands/shared");
45
+ const error_1 = require("../../../utils/error");
46
+ const output_1 = require("../../../utils/output");
47
+ const render_1 = require("../../../utils/render");
48
+ async function handleDbMigrationInit(opts) {
49
+ const appId = (0, shared_1.resolveAppId)(opts);
50
+ // 不可逆操作,TTY 默认要求 y/N;--yes 跳过
51
+ if (!opts.yes && !(0, output_1.isJsonMode)()) {
52
+ // 文案对齐 PRD:先讲不可逆,再问是否初始化;syncData 时追加一句而不是塞进同一句话
53
+ const suffix = opts.syncData ? " (existing data will be copied to dev)" : "";
54
+ const ok = await confirm(`? This action is irreversible. Initialize multi-env (dev / online)${suffix}? (y/N) `);
55
+ if (!ok) {
56
+ (0, output_1.emit)("Aborted.");
57
+ return;
58
+ }
59
+ }
60
+ let result;
61
+ try {
62
+ result = await api.db.migrationInit({ appId, syncData: opts.syncData });
63
+ }
64
+ catch (err) {
65
+ // PRD: 重复 init 报错带 hint,引导用户去 diff 看待发布变更
66
+ if (err instanceof error_1.AppError && err.code === "DB_API_k_dl_1300034") {
67
+ throw new error_1.AppError(err.code, err.message, {
68
+ next_actions: ["Run `miaoda db migration diff` to view pending changes."],
69
+ });
70
+ }
71
+ throw err;
72
+ }
73
+ if ((0, output_1.isJsonMode)()) {
74
+ (0, output_1.emitOk)((0, output_1.snakeCaseKeys)(result));
75
+ return;
76
+ }
77
+ // PRD 单行格式(不用 key:value 表格):
78
+ // 默认 ✓ / OK: "Multi-env initialized (dev / online)"
79
+ // --sync-data: "Multi-env initialized, data synced to dev"
80
+ const tty = (0, render_1.isStdoutTty)();
81
+ const prefix = tty ? "✓" : "OK";
82
+ const body = result.dataSynced
83
+ ? "Multi-env initialized, data synced to dev"
84
+ : `Multi-env initialized (${result.environments.join(" / ")})`;
85
+ (0, output_1.emit)(`${prefix} ${body}`);
86
+ }
87
+ async function handleDbMigrationDiff(opts) {
88
+ const appId = (0, shared_1.resolveAppId)(opts);
89
+ let result;
90
+ try {
91
+ result = await api.db.migrate({ appId, dryRun: true });
92
+ }
93
+ catch (err) {
94
+ throw decorateMigrationError(err);
95
+ }
96
+ // PRD: diff 在无待发布变更时报错带 hint,而不是渲染空列表
97
+ if (result.changes.length === 0) {
98
+ throw new error_1.AppError("DB_API_k_dl_1300035", `No pending changes between ${result.from} and ${result.to}`, {
99
+ next_actions: [
100
+ 'Make schema changes in dev first (e.g. `miaoda db sql "ALTER TABLE ..." --env dev`)',
101
+ ],
102
+ });
103
+ }
104
+ renderDiff(result);
105
+ }
106
+ async function handleDbMigrationApply(opts) {
107
+ const appId = (0, shared_1.resolveAppId)(opts);
108
+ // TTY 下先 diff 给用户审;--yes 直接打到 online
109
+ if (!opts.yes && !(0, output_1.isJsonMode)()) {
110
+ let preview;
111
+ try {
112
+ preview = await api.db.migrate({ appId, dryRun: true });
113
+ }
114
+ catch (err) {
115
+ throw decorateMigrationError(err);
116
+ }
117
+ if (preview.changes.length === 0) {
118
+ // PRD 文案 + hint
119
+ throw new error_1.AppError("DB_API_k_dl_1300035", `No pending changes between ${preview.from} and ${preview.to}`, {
120
+ next_actions: [
121
+ 'Make schema changes in dev first (e.g. `miaoda db sql "ALTER TABLE ..." --env dev`)',
122
+ ],
123
+ });
124
+ }
125
+ renderDiff(preview);
126
+ const ok = await confirm(`? Apply ${String(preview.changes.length)} change(s) to ${preview.to}? (y/N) `);
127
+ if (!ok) {
128
+ (0, output_1.emit)("Aborted.");
129
+ return;
130
+ }
131
+ }
132
+ let result;
133
+ try {
134
+ result = await api.db.migrate({ appId, dryRun: false });
135
+ }
136
+ catch (err) {
137
+ throw decorateMigrationError(err);
138
+ }
139
+ if ((0, output_1.isJsonMode)()) {
140
+ // PRD:{"status": "applied", "from": "dev", "to": "online", "changes_applied": 2}
141
+ (0, output_1.emitOk)((0, output_1.snakeCaseKeys)({
142
+ status: result.status ?? "applied",
143
+ from: result.from,
144
+ to: result.to,
145
+ changesApplied: result.changesApplied ?? result.changes.length,
146
+ }));
147
+ return;
148
+ }
149
+ const tty = (0, render_1.isStdoutTty)();
150
+ const prefix = tty ? "✓" : "OK";
151
+ const arrow = tty ? "→" : "->";
152
+ const applied = result.changesApplied ?? result.changes.length;
153
+ (0, output_1.emit)(`${prefix} Applied ${result.from} ${arrow} ${result.to} (${String(applied)} changes)`);
154
+ }
155
+ // ── helpers ──
156
+ // PRD diff 输出:
157
+ // dev → online (2 changes):
158
+ //
159
+ // ALTER TABLE users ADD COLUMN avatar_url text;
160
+ // CREATE INDEX idx_users_avatar ON users(avatar_url);
161
+ function renderDiff(result) {
162
+ if ((0, output_1.isJsonMode)()) {
163
+ (0, output_1.emitOk)((0, output_1.snakeCaseKeys)({
164
+ from: result.from,
165
+ to: result.to,
166
+ changes: result.changes.map((c) => ({
167
+ type: c.type,
168
+ table: c.table,
169
+ statement: c.statement,
170
+ })),
171
+ }));
172
+ return;
173
+ }
174
+ const tty = (0, render_1.isStdoutTty)();
175
+ if (result.changes.length === 0) {
176
+ (0, output_1.emit)(`No pending changes from ${result.from} to ${result.to}.`);
177
+ return;
178
+ }
179
+ const arrow = tty ? "→" : "->";
180
+ (0, output_1.emit)(`${result.from} ${arrow} ${result.to} (${String(result.changes.length)} changes):\n\n` +
181
+ result.changes.map((c) => ` ${c.statement}`).join("\n"));
182
+ }
183
+ // decorateMigrationError 给 migration / recovery 路径上的几个错误码补 PRD 规定的 hint。
184
+ // dataloom 后端只返 message + code,hint 由 CLI 端按错误码映射;其它错误码原样透传。
185
+ function decorateMigrationError(err) {
186
+ if (!(err instanceof error_1.AppError))
187
+ return err;
188
+ switch (err.code) {
189
+ case "DB_API_k_dl_1300039":
190
+ // 多环境未初始化:引导先 init
191
+ return new error_1.AppError(err.code, err.message, {
192
+ next_actions: ["Run `miaoda db migration init` to set up multi-env first."],
193
+ });
194
+ case "DB_API_k_dl_1300035":
195
+ // 无待发布变更:引导先在 dev 改 schema
196
+ return new error_1.AppError(err.code, err.message, {
197
+ next_actions: [
198
+ 'Make schema changes in dev first (e.g. `miaoda db sql "ALTER TABLE ..." --env dev`)',
199
+ ],
200
+ });
201
+ default:
202
+ return err;
203
+ }
204
+ }
205
+ async function confirm(prompt) {
206
+ const rl = node_readline_1.default.createInterface({ input: process.stdin, output: process.stderr });
207
+ return new Promise((resolve) => {
208
+ rl.question(prompt, (answer) => {
209
+ rl.close();
210
+ resolve(answer.trim().toLowerCase() === "y");
211
+ });
212
+ });
213
+ }
@@ -33,27 +33,36 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.handleAppUpdate = handleAppUpdate;
36
+ exports.handleDbQuota = handleDbQuota;
37
37
  const api = __importStar(require("../../../api/index"));
38
- const output_1 = require("../../../utils/output");
39
38
  const shared_1 = require("../../../cli/commands/shared");
40
- const index_1 = require("../../../api/app/index");
41
- /** miaoda app update [--app-id <id>] [--name <n>] [--description <d>] */
42
- async function handleAppUpdate(opts) {
43
- if (opts.name === undefined && opts.description === undefined) {
44
- (0, shared_1.failArgs)("至少需要 --name --description 中的一个");
45
- }
46
- const appID = (0, shared_1.resolveAppId)({ appId: opts.appId });
47
- await api.app.updateAppMeta({
48
- appID,
49
- name: opts.name,
50
- description: opts.description,
51
- });
52
- // 服务端不回写新 meta,本地再 fetch 一次拿最新值给用户
53
- const resp = await api.app.getAppInfo(appID);
54
- const meta = resp.appInfo?.appMeta ?? {};
55
- if (!(0, output_1.isJsonMode)()) {
56
- process.stdout.write("✓ App updated successfully\n");
39
+ const output_1 = require("../../../utils/output");
40
+ const render_1 = require("../../../utils/render");
41
+ async function handleDbQuota(opts) {
42
+ const appId = (0, shared_1.resolveAppId)(opts);
43
+ const data = await api.db.getDbQuota({ appId, dbBranch: opts.env });
44
+ if ((0, output_1.isJsonMode)()) {
45
+ // 配额未对接(storageQuotaBytes=0)时,quota / usage_percent 字段都不输出
46
+ const out = {
47
+ storageUsedBytes: data.storageUsedBytes,
48
+ tables: data.tables,
49
+ views: data.views,
50
+ };
51
+ if (data.storageQuotaBytes > 0) {
52
+ out.storageQuotaBytes = data.storageQuotaBytes;
53
+ out.usagePercent = data.usagePercent;
54
+ }
55
+ (0, output_1.emitOk)((0, output_1.snakeCaseKeys)(out));
56
+ return;
57
57
  }
58
- (0, output_1.emit)({ data: meta, next_cursor: null, has_more: false }, index_1.appMetaSchema);
58
+ // PRD:单行 "Storage: 14.9 MB / 1 GB (1.5%)";配额未对接时只显示 used
59
+ const tty = (0, render_1.isStdoutTty)();
60
+ const storageLine = data.storageQuotaBytes > 0
61
+ ? `${(0, render_1.formatSize)(data.storageUsedBytes)} / ${(0, render_1.formatSize)(data.storageQuotaBytes)} (${data.usagePercent.toFixed(1)}%)`
62
+ : (0, render_1.formatSize)(data.storageUsedBytes);
63
+ (0, output_1.emit)((0, render_1.renderKeyValue)([
64
+ ["Storage", storageLine],
65
+ ["Tables", String(data.tables)],
66
+ ["Views", String(data.views)],
67
+ ], tty));
59
68
  }
@@ -0,0 +1,228 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.handleDbRecoveryDiff = handleDbRecoveryDiff;
40
+ exports.handleDbRecoveryApply = handleDbRecoveryApply;
41
+ const node_readline_1 = __importDefault(require("node:readline"));
42
+ const api = __importStar(require("../../../api/index"));
43
+ const shared_1 = require("../../../cli/commands/shared");
44
+ const error_1 = require("../../../utils/error");
45
+ const output_1 = require("../../../utils/output");
46
+ const render_1 = require("../../../utils/render");
47
+ // ── recovery diff ──
48
+ async function handleDbRecoveryDiff(target, opts) {
49
+ const appId = (0, shared_1.resolveAppId)(opts);
50
+ const ts = normalizeTimestamp(target);
51
+ let result;
52
+ try {
53
+ result = await api.db.recover({ appId, target: ts, dryRun: true });
54
+ }
55
+ catch (err) {
56
+ throw decorateRecoveryError(err);
57
+ }
58
+ renderDiff(result);
59
+ }
60
+ async function handleDbRecoveryApply(target, opts) {
61
+ const appId = (0, shared_1.resolveAppId)(opts);
62
+ const ts = normalizeTimestamp(target);
63
+ // PITR 高危:TTY 下先 diff 给用户审;--yes 直接执行
64
+ if (!opts.yes && !(0, output_1.isJsonMode)()) {
65
+ let preview;
66
+ try {
67
+ preview = await api.db.recover({ appId, target: ts, dryRun: true });
68
+ }
69
+ catch (err) {
70
+ throw decorateRecoveryError(err);
71
+ }
72
+ renderDiff(preview);
73
+ const ok = await confirm(`? Restore database to ${preview.target}? This will overwrite current data. (y/N) `);
74
+ if (!ok) {
75
+ (0, output_1.emit)("Aborted.");
76
+ return;
77
+ }
78
+ }
79
+ let result;
80
+ try {
81
+ result = await api.db.recover({ appId, target: ts, dryRun: false });
82
+ }
83
+ catch (err) {
84
+ throw decorateRecoveryError(err);
85
+ }
86
+ if ((0, output_1.isJsonMode)()) {
87
+ // PRD:{"status": "restored", "target": "...", "tables_affected": 2, "elapsed_seconds": 30}
88
+ (0, output_1.emitOk)((0, output_1.snakeCaseKeys)({
89
+ status: result.status ?? "restored",
90
+ target: result.target,
91
+ tablesAffected: result.tablesAffected,
92
+ elapsedSeconds: result.elapsedSeconds ?? 0,
93
+ }));
94
+ return;
95
+ }
96
+ const tty = (0, render_1.isStdoutTty)();
97
+ const prefix = tty ? "✓" : "OK";
98
+ (0, output_1.emit)(`${prefix} Database restored to ${result.target} ` +
99
+ `(${String(result.tablesAffected)} tables affected, ${String(result.elapsedSeconds ?? 0)}s elapsed)`);
100
+ }
101
+ // ── helpers ──
102
+ /**
103
+ * 把用户传入的时间统一成 ISO 8601 UTC。
104
+ * 接受:`YYYY-MM-DD` / `YYYY-MM-DD HH:MM:SS` / 完整 ISO 8601。
105
+ */
106
+ function normalizeTimestamp(input) {
107
+ const FORMAT_HINT = "Use ISO 8601 / yyyy-mm-dd / yyyy-mm-dd HH:MM:SS.";
108
+ if (input === "") {
109
+ throw new error_1.AppError("ARGS_INVALID", "target timestamp is required", {
110
+ next_actions: ["Usage: miaoda db recovery diff|apply <timestamp>"],
111
+ });
112
+ }
113
+ if (/^\d{4}-\d{2}-\d{2}$/.test(input)) {
114
+ return new Date(`${input}T00:00:00Z`).toISOString();
115
+ }
116
+ if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(:\d{2})?$/.test(input)) {
117
+ const d = new Date(input.replace(" ", "T"));
118
+ if (Number.isNaN(d.getTime())) {
119
+ throw new error_1.AppError("INVALID_TIMESTAMP", `Invalid timestamp format '${input}'`, {
120
+ next_actions: [FORMAT_HINT],
121
+ });
122
+ }
123
+ return d.toISOString();
124
+ }
125
+ const d = new Date(input);
126
+ if (Number.isNaN(d.getTime())) {
127
+ throw new error_1.AppError("INVALID_TIMESTAMP", `Invalid timestamp format '${input}'`, {
128
+ next_actions: [FORMAT_HINT],
129
+ });
130
+ }
131
+ return d.toISOString();
132
+ }
133
+ // PRD diff 输出(结构化 prose):
134
+ // Recovery preview (→ 2026-04-15T10:00:00Z):
135
+ //
136
+ // tables affected: 2
137
+ // users: +3 rows, -1 row, ~5 rows modified
138
+ // orders: table will be restored (was dropped at 10:25:00)
139
+ //
140
+ // estimated time: ~30s
141
+ function renderDiff(result) {
142
+ if ((0, output_1.isJsonMode)()) {
143
+ (0, output_1.emitOk)((0, output_1.snakeCaseKeys)({
144
+ target: result.target,
145
+ tablesAffected: result.tablesAffected,
146
+ changes: result.changes.map((c) => ({
147
+ table: c.table,
148
+ inserted: c.inserted,
149
+ deleted: c.deleted,
150
+ modified: c.modified,
151
+ action: c.action,
152
+ droppedAt: c.droppedAt,
153
+ })),
154
+ estimatedSeconds: result.estimatedSeconds ?? 0,
155
+ }));
156
+ return;
157
+ }
158
+ const tty = (0, render_1.isStdoutTty)();
159
+ const arrow = tty ? "→" : "->";
160
+ if (result.changes.length === 0) {
161
+ (0, output_1.emit)(`Recovery preview (${arrow} ${result.target}):\n\n` +
162
+ ` No changes — database is already at this state.`);
163
+ return;
164
+ }
165
+ const lines = [
166
+ `Recovery preview (${arrow} ${result.target}):`,
167
+ "",
168
+ ` tables affected: ${String(result.tablesAffected)}`,
169
+ ];
170
+ for (const c of result.changes) {
171
+ lines.push(` ${c.table}: ${describeChange(c)}`);
172
+ }
173
+ if (result.estimatedSeconds !== undefined) {
174
+ lines.push("");
175
+ lines.push(` estimated time: ~${String(result.estimatedSeconds)}s`);
176
+ }
177
+ (0, output_1.emit)(lines.join("\n"));
178
+ }
179
+ function describeChange(c) {
180
+ if (c.action === "restore_table" || c.action === "drop" || c.action === "create") {
181
+ const ts = c.droppedAt ? ` (was dropped at ${c.droppedAt})` : "";
182
+ if (c.action === "restore_table")
183
+ return `table will be restored${ts}`;
184
+ if (c.action === "drop")
185
+ return `table will be dropped${ts}`;
186
+ return `table will be created${ts}`;
187
+ }
188
+ const parts = [];
189
+ if (c.inserted !== undefined && c.inserted !== 0)
190
+ parts.push(`+${String(c.inserted)} rows`);
191
+ if (c.deleted !== undefined && c.deleted !== 0) {
192
+ parts.push(`-${String(c.deleted)} ${c.deleted === 1 ? "row" : "rows"}`);
193
+ }
194
+ if (c.modified !== undefined && c.modified !== 0)
195
+ parts.push(`~${String(c.modified)} rows modified`);
196
+ return parts.length === 0 ? "no changes" : parts.join(", ");
197
+ }
198
+ // decorateRecoveryError 给 recovery 路径上的几个错误码补 PRD 规定的 hint。
199
+ // dataloom 后端只返 message + code,hint 由 CLI 端按错误码映射;其它错误码原样透传。
200
+ function decorateRecoveryError(err) {
201
+ if (!(err instanceof error_1.AppError))
202
+ return err;
203
+ switch (err.code) {
204
+ case "DB_API_k_dl_1300036":
205
+ // 窗口超限:引导用户检查 last migration apply 时间
206
+ return new error_1.AppError(err.code, err.message, {
207
+ next_actions: [
208
+ "PITR window is up to 7 days back, limited by your last `db migration apply` time.",
209
+ ],
210
+ });
211
+ case "DB_API_k_dl_1300038":
212
+ // 时间格式错误:引导 ISO 8601
213
+ return new error_1.AppError(err.code, err.message, {
214
+ next_actions: ["Use ISO 8601 format, e.g. 2026-04-15 or 2026-04-15T10:00:00Z"],
215
+ });
216
+ default:
217
+ return err;
218
+ }
219
+ }
220
+ async function confirm(prompt) {
221
+ const rl = node_readline_1.default.createInterface({ input: process.stdin, output: process.stderr });
222
+ return new Promise((resolve) => {
223
+ rl.question(prompt, (answer) => {
224
+ rl.close();
225
+ resolve(answer.trim().toLowerCase() === "y");
226
+ });
227
+ });
228
+ }
@@ -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; } });
@@ -33,29 +33,34 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.handleDeployErrorLog = handleDeployErrorLog;
36
+ exports.handleFileQuota = handleFileQuota;
37
37
  const api = __importStar(require("../../../api/index"));
38
- const output_1 = require("../../../utils/output");
39
38
  const shared_1 = require("../../../cli/commands/shared");
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, shared_1.failArgs)("<deploy-id> 必填");
46
- const appID = (0, shared_1.resolveAppId)({ 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
- // 信封带上 status 元信息(非 errorJobs 主体),便于 JSON 消费方读取
51
- (0, output_1.emit)({
52
- data: resp.errorJobs,
53
- next_cursor: null,
54
- has_more: false,
55
- }, index_1.errorJobSchema);
56
- // pretty 模式额外打一行整体状态(status 是 NodeStatus 数字)
57
- if (!process.stdout.isTTY)
39
+ const output_1 = require("../../../utils/output");
40
+ const render_1 = require("../../../utils/render");
41
+ async function handleFileQuota(opts) {
42
+ const appId = (0, shared_1.resolveAppId)(opts);
43
+ const data = await api.file.getStorageQuota({ appId });
44
+ if ((0, output_1.isJsonMode)()) {
45
+ // 配额未对接(storageQuotaBytes=0)时,quota / usage_percent 字段都不输出
46
+ const out = {
47
+ storageUsedBytes: data.storageUsedBytes,
48
+ files: data.files,
49
+ };
50
+ if (data.storageQuotaBytes > 0) {
51
+ out.storageQuotaBytes = data.storageQuotaBytes;
52
+ out.usagePercent = data.usagePercent;
53
+ }
54
+ (0, output_1.emitOk)((0, output_1.snakeCaseKeys)(out));
58
55
  return;
59
- const statusText = (0, index_1.nodeStatusText)(resp.status) ?? String(resp.status);
60
- process.stdout.write(`\n— pipeline status: ${statusText}\n`);
56
+ }
57
+ // PRD:单行 "Storage: 150 MB / 1 GB (15%)";配额未对接时只显示 used
58
+ const tty = (0, render_1.isStdoutTty)();
59
+ const storageLine = data.storageQuotaBytes > 0
60
+ ? `${(0, render_1.formatSize)(data.storageUsedBytes)} / ${(0, render_1.formatSize)(data.storageQuotaBytes)} (${data.usagePercent.toFixed(1)}%)`
61
+ : (0, render_1.formatSize)(data.storageUsedBytes);
62
+ (0, output_1.emit)((0, render_1.renderKeyValue)([
63
+ ["Storage", storageLine],
64
+ ["Files", String(data.files)],
65
+ ], tty));
61
66
  }
package/dist/main.js CHANGED
@@ -5,13 +5,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const commander_1 = require("commander");
7
7
  const index_1 = require("./cli/commands/index");
8
- const shared_1 = require("./cli/commands/shared");
9
8
  const help_1 = require("./cli/help");
10
9
  const config_1 = require("./utils/config");
11
10
  const log_id_1 = require("./utils/log_id");
12
11
  const output_1 = require("./utils/output");
13
- const error_1 = require("./utils/error");
14
- const version_1 = require("./cli/version");
15
12
  const package_json_1 = __importDefault(require("../package.json"));
16
13
  // MiaodaHelp 对齐 CLI 规范(描述置顶 / Flags / Global Flags / 段顺序):
17
14
  // 在 Command.prototype 上 patch createHelp,让所有命令实例(含动态注册的
@@ -23,14 +20,11 @@ const program = new commander_1.Command();
23
20
  const { version } = package_json_1.default;
24
21
  program
25
22
  .name("miaoda")
26
- .description("妙搭平台 CLI,提供数据服务、文件存储等命令行操作。")
23
+ .description("妙搭平台 CLI,提供数据服务、文件存储、插件管理等命令行操作。")
27
24
  .usage("<command> [flags]")
28
- .option("-v, --version", "显示版本号")
25
+ .version(version, "-v, --version", "显示版本号")
29
26
  .option("--json [fields]", "JSON 输出,可选字段级选择")
30
- .addOption(new commander_1.Option("--output <format>", "输出格式(pretty | json,大小写不敏感)")
31
- .choices(["pretty", "json"])
32
- .argParser((0, shared_1.caseInsensitiveChoice)(["pretty", "json"]))
33
- .default("pretty"))
27
+ .option("--output <format>", "输出格式(pretty|json", "pretty")
34
28
  .option("--verbose", "debug 日志到 stderr")
35
29
  .helpOption("-h, --help", "显示帮助信息")
36
30
  .hook("preAction", (_thisCmd, actionCmd) => {
@@ -39,21 +33,7 @@ program
39
33
  (0, config_1.initConfigFromOpts)(opts);
40
34
  });
41
35
  (0, index_1.registerCommands)(program);
42
- const versionArg = (0, version_1.resolveRootVersionArg)(process.argv.slice(2));
43
- if (versionArg === "show") {
44
- process.stdout.write(`${version}\n`);
45
- }
46
- else if (versionArg === "invalid-variant") {
47
- (0, output_1.emitError)(new error_1.AppError("ARGS_INVALID", "版本参数仅支持精确的 -v 或 --version"));
48
- process.exitCode = 2;
49
- }
50
- else if (versionArg === "invalid-placement") {
51
- (0, output_1.emitError)(new error_1.AppError("ARGS_INVALID", "-v / --version 仅可在根命令单独使用,不能与其他命令或参数混用"));
52
- process.exitCode = 2;
53
- }
54
- else {
55
- program.parseAsync(process.argv).catch((err) => {
56
- (0, output_1.emitError)(err);
57
- process.exitCode = 1;
58
- });
59
- }
36
+ program.parseAsync(process.argv).catch((err) => {
37
+ (0, output_1.emitError)(err);
38
+ process.exitCode = 1;
39
+ });