@lark-apaas/miaoda-cli 0.1.0-alpha.097a394

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 (48) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +66 -0
  3. package/bin/miaoda.js +2 -0
  4. package/dist/api/db/api.js +160 -0
  5. package/dist/api/db/client.js +108 -0
  6. package/dist/api/db/index.js +16 -0
  7. package/dist/api/db/parsers.js +157 -0
  8. package/dist/api/db/types.js +10 -0
  9. package/dist/api/file/api.js +420 -0
  10. package/dist/api/file/client.js +161 -0
  11. package/dist/api/file/detect.js +56 -0
  12. package/dist/api/file/index.js +17 -0
  13. package/dist/api/file/parsers.js +72 -0
  14. package/dist/api/file/types.js +3 -0
  15. package/dist/api/index.js +42 -0
  16. package/dist/api/plugin/api.js +243 -0
  17. package/dist/api/plugin/index.js +12 -0
  18. package/dist/api/plugin/types.js +3 -0
  19. package/dist/cli/commands/db/index.js +73 -0
  20. package/dist/cli/commands/file/index.js +67 -0
  21. package/dist/cli/commands/index.js +11 -0
  22. package/dist/cli/commands/plugin/index.js +204 -0
  23. package/dist/cli/commands/shared.js +53 -0
  24. package/dist/cli/handlers/db/data.js +167 -0
  25. package/dist/cli/handlers/db/index.js +11 -0
  26. package/dist/cli/handlers/db/schema.js +161 -0
  27. package/dist/cli/handlers/db/sql.js +173 -0
  28. package/dist/cli/handlers/file/cp.js +220 -0
  29. package/dist/cli/handlers/file/helpers.js +16 -0
  30. package/dist/cli/handlers/file/index.js +13 -0
  31. package/dist/cli/handlers/file/ls.js +109 -0
  32. package/dist/cli/handlers/file/rm.js +243 -0
  33. package/dist/cli/handlers/file/sign.js +96 -0
  34. package/dist/cli/handlers/file/stat.js +97 -0
  35. package/dist/cli/handlers/index.js +19 -0
  36. package/dist/cli/handlers/plugin/index.js +10 -0
  37. package/dist/cli/handlers/plugin/plugin-local.js +382 -0
  38. package/dist/cli/handlers/plugin/plugin.js +308 -0
  39. package/dist/cli/handlers/shared.js +139 -0
  40. package/dist/main.js +31 -0
  41. package/dist/utils/config.js +31 -0
  42. package/dist/utils/error.js +35 -0
  43. package/dist/utils/http.js +46 -0
  44. package/dist/utils/index.js +25 -0
  45. package/dist/utils/log_id.js +13 -0
  46. package/dist/utils/logger.js +15 -0
  47. package/dist/utils/output.js +59 -0
  48. package/package.json +53 -0
@@ -0,0 +1,204 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerPluginCommands = registerPluginCommands;
4
+ const index_1 = require("../../../cli/handlers/plugin/index");
5
+ function registerPluginCommands(program) {
6
+ const pluginCmd = program
7
+ .command("plugin")
8
+ .description("插件管理:安装/更新/移除插件包,查询 capability 实例");
9
+ pluginCmd.action(() => {
10
+ pluginCmd.outputHelp();
11
+ });
12
+ pluginCmd.addHelpText("after", `
13
+ 概念
14
+ - 插件包 (plugin package):.tgz 包,释放到 ./node_modules/<name>
15
+ - actionPlugins:./package.json 里的字段,记录 { "@scope/name": "version" },作为
16
+ 当前应用项目依赖的插件清单;install/update/remove/init 读写此字段
17
+ - capability 实例:./server/capabilities/<id>.json,描述该应用下具体使用的
18
+ 插件实例(pluginKey + formValue + paramsSchema)
19
+ - 充血 (hydrate):读取插件 manifest.json,把 capability 与 manifest 的
20
+ inputSchema/outputSchema 合并输出完整 action 定义
21
+
22
+ 作用范围
23
+ 本地落点统一为 cwd 下的 package.json / node_modules / server/capabilities。
24
+ - install / update / init:通过 platform HTTP 拉取插件版本与 .tgz
25
+ - remove / list / list-packages:纯本地 FS,不走网络
26
+ `);
27
+ pluginCmd
28
+ .command("install")
29
+ .description("拉取插件包并安装到当前应用项目")
30
+ .argument("<names...>", "插件名,格式 @scope/name 或 @scope/name@version;未带版本默认 latest")
31
+ .addHelpText("after", `
32
+ 行为
33
+ - 拉取 .tgz 到本地缓存,解压到 ./node_modules/<name>
34
+ - 在 ./package.json 的 actionPlugins 字段记录 <name>: <version>
35
+ - 自动补齐插件声明的 peerDependencies
36
+ - 同版本已安装则跳过(幂等)
37
+
38
+ 前置
39
+ - 当前工作目录必须包含 package.json
40
+
41
+ JSON 输出
42
+ {
43
+ "installed": ["@demo/example-plugin@1.2.3", ...],
44
+ "skipped": ["@demo/existing-plugin", ...],
45
+ "failed": [{"name": "@demo/bad", "error": "..."}]
46
+ }
47
+
48
+ 退出码
49
+ 0:全部成功;1:至少一个失败(其余仍会尝试)
50
+
51
+ 示例
52
+ $ miaoda plugin install @demo/example-plugin
53
+ $ miaoda plugin install @demo/example-plugin@1.2.3 @demo/other-plugin --json
54
+
55
+ # 报错:包名格式不合法
56
+ $ miaoda plugin install bad-name
57
+ Error: Invalid plugin name format: bad-name. Expected: @scope/name or @scope/name@version
58
+ hint: 示例:@demo/example-plugin 或 @demo/example-plugin@1.2.3
59
+ `)
60
+ .action(async (names) => { await (0, index_1.handlePluginInstall)({ names }); });
61
+ pluginCmd
62
+ .command("update")
63
+ .description("把已安装插件升级到 latest 版本")
64
+ .argument("<names...>", "插件名,格式 @scope/name;带 @version 也只取 name 部分")
65
+ .addHelpText("after", `
66
+ 行为
67
+ - 仅对已在 actionPlugins 里的插件生效;未安装的插件返回 notInstalled
68
+ - 拉取 latest 版本 .tgz,覆盖安装到 ./node_modules/<name>
69
+ - 版本一致则跳过(幂等);版本变化时同步写回 actionPlugins
70
+
71
+ 前置
72
+ - 当前工作目录必须包含 package.json
73
+
74
+ JSON 输出
75
+ {
76
+ "updated": [{"name": "...", "from": "1.0.0", "to": "1.2.3"}],
77
+ "skipped": ["..."], // 已是 latest
78
+ "notInstalled": ["..."], // 未安装,未尝试
79
+ "failed": [{"name": "...", "error": "..."}]
80
+ }
81
+
82
+ 退出码
83
+ 0:全部成功或仅有 notInstalled;1:至少一个 failed
84
+
85
+ 示例
86
+ $ miaoda plugin update @demo/example-plugin
87
+ $ miaoda plugin update @demo/a @demo/b --json
88
+
89
+ # 场景:插件未登记(非抛错,出现在返回的 notInstalled)
90
+ $ miaoda plugin update @demo/not-installed --json
91
+ {"updated":[],"skipped":[],"notInstalled":["@demo/not-installed"],"failed":[]}
92
+ `)
93
+ .action(async (names) => { await (0, index_1.handlePluginUpdate)({ names }); });
94
+ pluginCmd
95
+ .command("remove")
96
+ .description("从当前项目移除一个已安装的插件")
97
+ .argument("<name>", "插件名,格式 @scope/name")
98
+ .addHelpText("after", `
99
+ 行为
100
+ - 删除 ./node_modules/<name> 目录
101
+ - 从 ./package.json 的 actionPlugins 字段移除对应条目
102
+
103
+ 前置
104
+ - 插件必须已登记在 actionPlugins 中,否则抛 NOT_FOUND
105
+
106
+ JSON 输出
107
+ {"removed": "@demo/example-plugin"}
108
+
109
+ 示例
110
+ $ miaoda plugin remove @demo/example-plugin
111
+
112
+ # 报错:插件未登记
113
+ $ miaoda plugin remove @demo/not-installed
114
+ Error: Plugin @demo/not-installed is not installed
115
+ hint: 运行 miaoda plugin list-packages 查看已安装插件
116
+ `)
117
+ .action((name) => { (0, index_1.handlePluginRemove)({ name }); });
118
+ pluginCmd
119
+ .command("init")
120
+ .description("按 package.json 的 actionPlugins 批量安装所有插件")
121
+ .addHelpText("after", `
122
+ 行为
123
+ - 读取 ./package.json 的 actionPlugins,对每个条目按固定版本安装
124
+ - 已经是目标版本的跳过(幂等),用于 clone 项目后首次同步 / CI
125
+
126
+ 前置
127
+ - 当前工作目录必须包含 package.json
128
+
129
+ JSON 输出
130
+ {
131
+ "installed": ["@demo/a@1.0.0", ...],
132
+ "skipped": ["@demo/b", ...],
133
+ "failed": [{"name": "...", "error": "..."}]
134
+ }
135
+ 当 actionPlugins 为空时输出 {"message": "No plugins found in package.json", "installed": [], "skipped": []}
136
+
137
+ 退出码
138
+ 0:全部成功;1:至少一个 failed
139
+
140
+ 示例
141
+ $ miaoda plugin init --json
142
+
143
+ # 报错:当前目录没有 package.json
144
+ $ miaoda plugin init
145
+ Error: package.json not found in current directory
146
+ hint: 在应用项目根目录运行
147
+ `)
148
+ .action(async () => { await (0, index_1.handlePluginInit)(); });
149
+ pluginCmd
150
+ .command("list")
151
+ .description("列出当前项目的 capability 实例(./server/capabilities/*.json)")
152
+ .option("--summary", "不 hydrate(不充血),直接返回 capability 文件的原始 JSON")
153
+ .option("--id <id>", "只查询指定 id 的单个 capability")
154
+ .addHelpText("after", `
155
+ 行为
156
+ - 扫描 ./server/capabilities/*.json(排除 capabilities.json)
157
+ - 默认对每条 capability 做 hydrate:读取 ./node_modules/<pluginKey>/manifest.json,
158
+ 合并得到每个 action 的最终 inputSchema / outputSchema
159
+ - hydrate 失败不抛错,每条结果上带 _hydrateError 字段
160
+
161
+ 前置
162
+ - ./server/capabilities 目录必须存在,否则抛 NOT_FOUND
163
+ - 充血模式下需要对应插件已安装在 ./node_modules,否则该条 _hydrateError
164
+
165
+ JSON 输出
166
+ - 无 --id:数组,元素为 hydrate 后的 capability,含 actions[].inputSchema 等
167
+ - 带 --id:单个对象
168
+ - 带 --summary:不做 hydrate,返回原始 RawCapability 结构
169
+
170
+ 示例
171
+ $ miaoda plugin list --json
172
+ $ miaoda plugin list --id cap_demo_xxx --json
173
+ $ miaoda plugin list --summary --json
174
+
175
+ # 报错:capability 目录不存在
176
+ $ miaoda plugin list
177
+ Error: server/capabilities directory not found
178
+ hint: 当前目录必须是含 server/capabilities/ 的应用项目
179
+ `)
180
+ .action(async (opts) => { await (0, index_1.handlePluginList)(opts); });
181
+ pluginCmd
182
+ .command("list-packages")
183
+ .description("列出 package.json actionPlugins 里已声明的插件包")
184
+ .addHelpText("after", `
185
+ 行为
186
+ - 读取 ./package.json 的 actionPlugins 字段
187
+ - 只看声明,不校验 ./node_modules 下是否实际存在
188
+
189
+ JSON 输出
190
+ {
191
+ "plugins": [{"name": "@demo/example-plugin", "version": "1.2.3"}, ...],
192
+ "total": <number>
193
+ }
194
+
195
+ 示例
196
+ $ miaoda plugin list-packages --json
197
+
198
+ # 报错:当前目录没有 package.json
199
+ $ miaoda plugin list-packages
200
+ Error: package.json not found in current directory
201
+ hint: 在应用项目根目录运行
202
+ `)
203
+ .action(() => { (0, index_1.handlePluginListPlugins)(); });
204
+ }
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.appIdOption = appIdOption;
4
+ exports.softRequiredOption = softRequiredOption;
5
+ exports.resolveAppId = resolveAppId;
6
+ exports.withHelp = withHelp;
7
+ exports.failArgs = failArgs;
8
+ const commander_1 = require("commander");
9
+ const error_1 = require("../../utils/error");
10
+ /** --app-id option,需要应用上下文的命令自行 .addOption(appIdOption()) */
11
+ function appIdOption() {
12
+ return new commander_1.Option("--app-id <id>", "指定目标应用").env("MIAODA_APP_ID");
13
+ }
14
+ /**
15
+ * soft-required: Commander 类型上 optional,runtime 校验必填。
16
+ */
17
+ function softRequiredOption(name, desc) {
18
+ return new commander_1.Option(name, desc);
19
+ }
20
+ /** 解析 appId,CLI flag > env > 抛错 */
21
+ function resolveAppId(opts) {
22
+ const id = opts.appId ?? process.env.MIAODA_APP_ID;
23
+ if (!id) {
24
+ throw new error_1.AppError("APP_ID_MISSING", "未指定应用", {
25
+ next_actions: [
26
+ "传入 --app-id <id>",
27
+ "设置 export MIAODA_APP_ID=<id>",
28
+ ],
29
+ });
30
+ }
31
+ return id;
32
+ }
33
+ /** 包裹 handler,缺 soft-required 参数时打 help 并退出 */
34
+ function withHelp(cmd, handler) {
35
+ return async (opts) => {
36
+ try {
37
+ await handler(opts);
38
+ }
39
+ catch (err) {
40
+ if (err instanceof error_1.AppError && err.code === "ARGS_INVALID") {
41
+ process.stderr.write(`Error: ${err.message}\n`);
42
+ cmd.outputHelp();
43
+ process.exitCode = 2;
44
+ return;
45
+ }
46
+ throw err;
47
+ }
48
+ };
49
+ }
50
+ /** 参数校验失败时抛出,配合 withHelp 自动打 help */
51
+ function failArgs(message) {
52
+ throw new error_1.AppError("ARGS_INVALID", message);
53
+ }
@@ -0,0 +1,167 @@
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.handleDbDataImport = handleDbDataImport;
37
+ exports.handleDbDataExport = handleDbDataExport;
38
+ const fs = __importStar(require("node:fs/promises"));
39
+ const path = __importStar(require("node:path"));
40
+ const api = __importStar(require("../../../api/index"));
41
+ const error_1 = require("../../../utils/error");
42
+ const output_1 = require("../../../utils/output");
43
+ const shared_1 = require("../../../cli/commands/shared");
44
+ const shared_2 = require("../../../cli/handlers/shared");
45
+ // P0 规格(对齐技术方案关键决策 2)
46
+ const MAX_SIZE_BYTES = 1 * 1024 * 1024; // 1 MB
47
+ const MAX_ROWS = 5000;
48
+ async function handleDbDataImport(file, opts) {
49
+ const appId = (0, shared_1.resolveAppId)(opts);
50
+ const ext = path.extname(file).toLowerCase();
51
+ const format = resolveFormat(opts.format, ext, "import");
52
+ let body;
53
+ try {
54
+ body = await fs.readFile(file);
55
+ }
56
+ catch (err) {
57
+ const code = err.code;
58
+ if (code === "ENOENT") {
59
+ throw new error_1.AppError("IMPORT_FILE_NOT_FOUND", `Local file '${file}' does not exist`, { next_actions: ["Check the file path."] });
60
+ }
61
+ throw err;
62
+ }
63
+ if (body.length > MAX_SIZE_BYTES) {
64
+ throw new error_1.AppError("IMPORT_SIZE_EXCEEDED", `Import exceeds 1 MB limit (file is ${String(body.length)} bytes)`, { next_actions: ["Split the file into chunks of ≤ 5000 rows / 1 MB and import separately."] });
65
+ }
66
+ const rowCount = countRows(body, format);
67
+ if (rowCount > MAX_ROWS) {
68
+ throw new error_1.AppError("IMPORT_ROWS_EXCEEDED", `Import exceeds 5000 rows limit (file has ${String(rowCount)} rows)`, { next_actions: ["Split the file into chunks of ≤ 5000 rows / 1 MB and import separately."] });
69
+ }
70
+ const tableName = opts.table ?? path.basename(file, ext);
71
+ if (!tableName) {
72
+ throw new error_1.AppError("ARGS_INVALID", "Cannot infer target table from file name; specify --table");
73
+ }
74
+ const result = await api.db.importData({
75
+ appId,
76
+ tableName,
77
+ format,
78
+ body,
79
+ });
80
+ if ((0, output_1.isJsonMode)()) {
81
+ (0, output_1.emit)({
82
+ data: {
83
+ file,
84
+ table: result.tableName,
85
+ rows: result.rows,
86
+ },
87
+ });
88
+ return;
89
+ }
90
+ const tty = (0, shared_2.isStdoutTty)();
91
+ (0, output_1.emit)(tty
92
+ ? `✓ Imported ${file} → table '${result.tableName}' (${String(result.rows)} rows)`
93
+ : `OK Imported ${file} -> table '${result.tableName}' (${String(result.rows)} rows)`);
94
+ }
95
+ async function handleDbDataExport(table, opts) {
96
+ const appId = (0, shared_1.resolveAppId)(opts);
97
+ const format = resolveFormat(opts.format, undefined, "export", "csv");
98
+ const outputPath = opts.file ?? `${table}.${format}`;
99
+ const limit = opts.limit ? Number(opts.limit) : MAX_ROWS;
100
+ if (!Number.isInteger(limit) || limit <= 0 || limit > MAX_ROWS) {
101
+ throw new error_1.AppError("ARGS_INVALID", `--limit must be a positive integer ≤ ${String(MAX_ROWS)}`);
102
+ }
103
+ const result = await api.db.exportData({
104
+ appId,
105
+ tableName: table,
106
+ format,
107
+ limit,
108
+ });
109
+ if (result.body.length > MAX_SIZE_BYTES) {
110
+ throw new error_1.AppError("EXPORT_SIZE_EXCEEDED", `Export exceeds 1 MB limit (body is ${String(result.body.length)} bytes)`, { next_actions: [`Filter the table with "miaoda db sql" (e.g. WHERE/LIMIT) and export smaller subsets.`] });
111
+ }
112
+ await fs.writeFile(outputPath, result.body);
113
+ const rows = countRows(result.body, format);
114
+ if ((0, output_1.isJsonMode)()) {
115
+ (0, output_1.emit)({
116
+ data: {
117
+ table,
118
+ file: outputPath,
119
+ format,
120
+ rows,
121
+ },
122
+ });
123
+ return;
124
+ }
125
+ const tty = (0, shared_2.isStdoutTty)();
126
+ (0, output_1.emit)(tty
127
+ ? `✓ Exported ${table} → ${outputPath} (${String(rows)} rows)`
128
+ : `OK Exported ${table} -> ${outputPath} (${String(rows)} rows)`);
129
+ }
130
+ // ── 共用辅助 ──
131
+ function resolveFormat(explicit, ext, scope, fallback) {
132
+ const raw = (explicit ?? ext ?? fallback ?? "").replace(/^\./, "").toLowerCase();
133
+ if (raw === "csv")
134
+ return "csv";
135
+ if (raw === "json")
136
+ return "json";
137
+ const code = scope === "import" ? "IMPORT_FORMAT_UNSUPPORTED" : "EXPORT_FORMAT_UNSUPPORTED";
138
+ throw new error_1.AppError(code, `Unrecognized format '${raw || "(unspecified)"}'`, {
139
+ next_actions: [
140
+ scope === "import"
141
+ ? "Supported formats: .csv, .json. Convert the file first, or rename with the correct extension."
142
+ : "Supported formats: csv, json. Pass --format csv|json.",
143
+ ],
144
+ });
145
+ }
146
+ function countRows(body, format) {
147
+ if (format === "csv") {
148
+ // 粗估:非空行数 - 表头 1 行
149
+ let lines = 0;
150
+ const text = body.toString("utf8");
151
+ for (const line of text.split(/\r?\n/)) {
152
+ if (line.length > 0)
153
+ lines += 1;
154
+ }
155
+ return Math.max(0, lines - 1);
156
+ }
157
+ // JSON:期望是顶层数组
158
+ try {
159
+ const parsed = JSON.parse(body.toString("utf8"));
160
+ if (Array.isArray(parsed))
161
+ return parsed.length;
162
+ return 1;
163
+ }
164
+ catch {
165
+ return 0;
166
+ }
167
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleDbDataExport = exports.handleDbDataImport = exports.handleDbSchemaGet = exports.handleDbSchemaList = exports.handleDbSql = void 0;
4
+ var sql_1 = require("./sql");
5
+ Object.defineProperty(exports, "handleDbSql", { enumerable: true, get: function () { return sql_1.handleDbSql; } });
6
+ var schema_1 = require("./schema");
7
+ Object.defineProperty(exports, "handleDbSchemaList", { enumerable: true, get: function () { return schema_1.handleDbSchemaList; } });
8
+ Object.defineProperty(exports, "handleDbSchemaGet", { enumerable: true, get: function () { return schema_1.handleDbSchemaGet; } });
9
+ var data_1 = require("./data");
10
+ Object.defineProperty(exports, "handleDbDataImport", { enumerable: true, get: function () { return data_1.handleDbDataImport; } });
11
+ Object.defineProperty(exports, "handleDbDataExport", { enumerable: true, get: function () { return data_1.handleDbDataExport; } });
@@ -0,0 +1,161 @@
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.handleDbSchemaList = handleDbSchemaList;
37
+ exports.handleDbSchemaGet = handleDbSchemaGet;
38
+ const api = __importStar(require("../../../api/index"));
39
+ const error_1 = require("../../../utils/error");
40
+ const output_1 = require("../../../utils/output");
41
+ const shared_1 = require("../../../cli/commands/shared");
42
+ const shared_2 = require("../../../cli/handlers/shared");
43
+ const index_1 = require("../../../api/db/index");
44
+ // ── schema list ──
45
+ async function handleDbSchemaList(opts) {
46
+ const appId = (0, shared_1.resolveAppId)(opts);
47
+ const resp = await api.db.getSchema({ appId, format: "schema", includeStats: true });
48
+ const tables = (0, index_1.flattenSchemaList)(resp.schema);
49
+ if ((0, output_1.isJsonMode)()) {
50
+ (0, output_1.emit)({ data: tables });
51
+ return;
52
+ }
53
+ if (tables.length === 0) {
54
+ (0, output_1.emit)("No tables found.");
55
+ return;
56
+ }
57
+ const tty = (0, shared_2.isStdoutTty)();
58
+ // PRD 对齐:TTY 表头用 `size`(友好格式),non-TTY 用 `size_bytes`(原始整数)
59
+ const headers = [
60
+ "name",
61
+ "description",
62
+ "estimated_row_count",
63
+ tty ? "size" : "size_bytes",
64
+ "columns",
65
+ "updated_at",
66
+ ];
67
+ const rows = tables.map((t) => [
68
+ t.name,
69
+ t.description ?? "—",
70
+ t.estimated_row_count === null ? "—" : String(t.estimated_row_count),
71
+ t.size_bytes === null ? "—" : (tty ? (0, shared_2.formatSize)(t.size_bytes) : String(t.size_bytes)),
72
+ String(t.columns),
73
+ (0, shared_2.formatTime)(t.updated_at, tty),
74
+ ]);
75
+ (0, output_1.emit)(tty ? (0, shared_2.renderAlignedTable)(headers, rows) : (0, shared_2.renderTsv)(headers, rows));
76
+ }
77
+ async function handleDbSchemaGet(table, opts) {
78
+ const appId = (0, shared_1.resolveAppId)(opts);
79
+ const tty = (0, shared_2.isStdoutTty)();
80
+ const forceDdl = Boolean(opts.ddl);
81
+ const wantsStructured = (0, output_1.isJsonMode)() || (tty && !forceDdl);
82
+ if (!wantsStructured) {
83
+ // non-TTY 或 --ddl:直接取完整 DDL 输出
84
+ const ddlResp = await api.db.getSchema({
85
+ appId,
86
+ format: "ddl",
87
+ tableNames: table,
88
+ });
89
+ const sql = ddlResp.ddl?.[table];
90
+ if (!sql) {
91
+ throw new error_1.AppError("TABLE_NOT_FOUND", `Table '${table}' does not exist`, {
92
+ next_actions: [
93
+ `Did you mean another table? Run "miaoda db schema list" to see all tables.`,
94
+ ],
95
+ });
96
+ }
97
+ (0, output_1.emit)(sql.trimEnd());
98
+ return;
99
+ }
100
+ // TTY / JSON:取结构化
101
+ const resp = await api.db.getSchema({
102
+ appId,
103
+ format: "schema",
104
+ tableNames: table,
105
+ includeStats: true,
106
+ });
107
+ const detail = (0, index_1.pickTableDetail)(resp.schema, table);
108
+ if (!detail) {
109
+ throw new error_1.AppError("TABLE_NOT_FOUND", `Table '${table}' does not exist`, {
110
+ next_actions: [
111
+ `Did you mean another table? Run "miaoda db schema list" to see all tables.`,
112
+ ],
113
+ });
114
+ }
115
+ if ((0, output_1.isJsonMode)()) {
116
+ (0, shared_2.emitOk)(detail);
117
+ return;
118
+ }
119
+ (0, output_1.emit)(renderDetail(detail, tty));
120
+ }
121
+ function renderDetail(d, tty) {
122
+ const systemFields = d.columns.filter((c) => c.name.startsWith("_"));
123
+ const userFields = d.columns.filter((c) => !c.name.startsWith("_"));
124
+ // PRD 的 header 布局:Name / Description / Columns(含"+ N system") / Estimated Rows / Size / Created / Updated
125
+ const header = [
126
+ ["Name", d.name],
127
+ ["Description", d.description ?? "—"],
128
+ [
129
+ "Columns",
130
+ systemFields.length > 0
131
+ ? `${String(userFields.length)} (+ ${String(systemFields.length)} system)`
132
+ : String(userFields.length),
133
+ ],
134
+ ["Estimated Rows", d.estimated_row_count === null ? "—" : String(d.estimated_row_count)],
135
+ ["Size", d.size_bytes === null ? "—" : (0, shared_2.formatSize)(d.size_bytes)],
136
+ ["Created", (0, shared_2.formatTime)(d.created_at, tty)],
137
+ ["Updated", (0, shared_2.formatTime)(d.updated_at, tty)],
138
+ ];
139
+ const colHeaders = ["column", "type", "nullable", "default", "comment"];
140
+ const colRows = userFields.map((c) => [
141
+ c.name,
142
+ c.type,
143
+ c.nullable ? "yes" : "no",
144
+ c.default ?? "—",
145
+ c.comment ?? "—",
146
+ ]);
147
+ const parts = [];
148
+ parts.push((0, shared_2.renderKeyValue)(header, tty));
149
+ parts.push("");
150
+ parts.push(tty ? (0, shared_2.renderAlignedTable)(colHeaders, colRows) : (0, shared_2.renderTsv)(colHeaders, colRows));
151
+ if (d.indexes.length > 0) {
152
+ parts.push("");
153
+ parts.push(tty ? " Indexes:" : "Indexes:");
154
+ // PRD 格式: "TYPE (col1, col2)",不展示索引名
155
+ for (const idx of d.indexes) {
156
+ const line = `${idx.type} (${idx.columns.join(", ")})`;
157
+ parts.push(tty ? ` ${line}` : line);
158
+ }
159
+ }
160
+ return parts.join("\n");
161
+ }