@lark-apaas/miaoda-cli 0.1.3 → 0.1.4-alpha.abaa17f

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 (103) hide show
  1. package/dist/api/app/api.js +3 -3
  2. package/dist/api/app/schemas.js +43 -43
  3. package/dist/api/db/api.js +398 -55
  4. package/dist/api/db/client.js +155 -28
  5. package/dist/api/db/index.js +12 -1
  6. package/dist/api/db/parsers.js +20 -20
  7. package/dist/api/db/sql-keywords.js +87 -87
  8. package/dist/api/deploy/api.js +5 -5
  9. package/dist/api/deploy/index.js +12 -1
  10. package/dist/api/deploy/modern-types.js +23 -0
  11. package/dist/api/deploy/modern.js +70 -0
  12. package/dist/api/deploy/plugin-instances-types.js +6 -0
  13. package/dist/api/deploy/plugin-instances.js +30 -0
  14. package/dist/api/deploy/schemas.js +32 -32
  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/observability/api.js +6 -6
  21. package/dist/api/observability/schemas.js +14 -14
  22. package/dist/api/plugin/api.js +31 -31
  23. package/dist/cli/commands/app/index.js +94 -13
  24. package/dist/cli/commands/db/index.js +602 -54
  25. package/dist/cli/commands/deploy/index.js +28 -28
  26. package/dist/cli/commands/deploy/modern.js +48 -0
  27. package/dist/cli/commands/file/index.js +85 -58
  28. package/dist/cli/commands/index.js +31 -5
  29. package/dist/cli/commands/observability/index.js +69 -69
  30. package/dist/cli/commands/plugin/index.js +27 -27
  31. package/dist/cli/commands/shared.js +10 -10
  32. package/dist/cli/handlers/app/index.js +6 -1
  33. package/dist/cli/handlers/app/init.js +86 -0
  34. package/dist/cli/handlers/app/update.js +2 -2
  35. package/dist/cli/handlers/db/_destructive.js +67 -0
  36. package/dist/cli/handlers/db/_env.js +26 -0
  37. package/dist/cli/handlers/db/_operator.js +35 -0
  38. package/dist/cli/handlers/db/audit.js +383 -0
  39. package/dist/cli/handlers/db/changelog.js +160 -0
  40. package/dist/cli/handlers/db/data.js +32 -31
  41. package/dist/cli/handlers/db/index.js +17 -1
  42. package/dist/cli/handlers/db/migration.js +234 -0
  43. package/dist/cli/handlers/db/quota.js +68 -0
  44. package/dist/cli/handlers/db/recovery.js +413 -0
  45. package/dist/cli/handlers/db/schema.js +33 -33
  46. package/dist/cli/handlers/db/sql.js +69 -69
  47. package/dist/cli/handlers/deploy/deploy.js +4 -4
  48. package/dist/cli/handlers/deploy/error-log.js +1 -1
  49. package/dist/cli/handlers/deploy/get.js +3 -3
  50. package/dist/cli/handlers/deploy/modern.js +32 -0
  51. package/dist/cli/handlers/deploy/polling.js +11 -11
  52. package/dist/cli/handlers/file/cp.js +30 -30
  53. package/dist/cli/handlers/file/index.js +3 -1
  54. package/dist/cli/handlers/file/ls.js +5 -5
  55. package/dist/cli/handlers/file/quota.js +66 -0
  56. package/dist/cli/handlers/file/rm.js +32 -30
  57. package/dist/cli/handlers/file/sign.js +3 -3
  58. package/dist/cli/handlers/file/stat.js +10 -9
  59. package/dist/cli/handlers/observability/analytics.js +47 -47
  60. package/dist/cli/handlers/observability/helpers.js +2 -2
  61. package/dist/cli/handlers/observability/log.js +9 -9
  62. package/dist/cli/handlers/observability/metric.js +26 -26
  63. package/dist/cli/handlers/observability/trace.js +5 -5
  64. package/dist/cli/handlers/plugin/plugin-local.js +53 -53
  65. package/dist/cli/handlers/plugin/plugin.js +15 -15
  66. package/dist/cli/help.js +16 -16
  67. package/dist/main.js +12 -12
  68. package/dist/services/app/init/index.js +14 -0
  69. package/dist/services/app/init/install.js +101 -0
  70. package/dist/services/app/init/steering.js +74 -0
  71. package/dist/services/app/init/template.js +85 -0
  72. package/dist/services/deploy/modern/atoms/build.js +54 -0
  73. package/dist/services/deploy/modern/atoms/context.js +30 -0
  74. package/dist/services/deploy/modern/atoms/index.js +17 -0
  75. package/dist/services/deploy/modern/atoms/local-release.js +27 -0
  76. package/dist/services/deploy/modern/atoms/pre-release.js +13 -0
  77. package/dist/services/deploy/modern/atoms/save-plugin-instances.js +72 -0
  78. package/dist/services/deploy/modern/atoms/upload.js +192 -0
  79. package/dist/services/deploy/modern/check.js +58 -0
  80. package/dist/services/deploy/modern/constants.js +13 -0
  81. package/dist/services/deploy/modern/index.js +16 -0
  82. package/dist/services/deploy/modern/pipelines/index.js +5 -0
  83. package/dist/services/deploy/modern/pipelines/local.js +75 -0
  84. package/dist/services/deploy/modern/protocol.js +43 -0
  85. package/dist/services/deploy/modern/run-types.js +4 -0
  86. package/dist/services/deploy/modern/run.js +13 -0
  87. package/dist/services/deploy/modern/template-key-map.js +29 -0
  88. package/dist/utils/args.js +1 -1
  89. package/dist/utils/colors.js +2 -2
  90. package/dist/utils/config.js +2 -2
  91. package/dist/utils/devops-error.js +9 -9
  92. package/dist/utils/error.js +2 -2
  93. package/dist/utils/git.js +4 -4
  94. package/dist/utils/http.js +27 -21
  95. package/dist/utils/index.js +3 -1
  96. package/dist/utils/npm-pack.js +55 -0
  97. package/dist/utils/output.js +67 -45
  98. package/dist/utils/poll.js +35 -0
  99. package/dist/utils/render.js +27 -27
  100. package/dist/utils/spark-meta.js +48 -0
  101. package/dist/utils/spinner.js +46 -0
  102. package/dist/utils/time.js +47 -42
  103. package/package.json +1 -1
@@ -16,12 +16,12 @@ const COMMON_TIME_HELP = `
16
16
  `;
17
17
  function registerObservabilityCommands(program) {
18
18
  const obCmd = program
19
- .command("observability")
20
- .description("可观测性:查询线上日志、链路、监控指标、运营指标");
19
+ .command('observability')
20
+ .description('可观测性:查询线上日志、链路、监控指标、运营指标');
21
21
  obCmd.action(() => {
22
22
  obCmd.outputHelp();
23
23
  });
24
- obCmd.addHelpText("after", `
24
+ obCmd.addHelpText('after', `
25
25
  作用范围
26
26
  仅供 Agent 在妙搭沙箱内调用,查询当前应用的线上观测数据。所有命令为只读。
27
27
  应用上下文:--app-id <id> 或环境变量 MIAODA_APP_ID。
@@ -37,26 +37,26 @@ function registerObservabilityCommands(program) {
37
37
  // ── log ──
38
38
  function registerLog(parent) {
39
39
  const cmd = parent
40
- .command("log")
41
- .description("查询线上运行日志(按时间倒序,分页)")
40
+ .command('log')
41
+ .description('查询线上运行日志(按时间倒序,分页)')
42
42
  .addOption((0, shared_1.appIdOption)().hideHelp())
43
- .addOption(new commander_1.Option("--level <level>", "日志级别(大小写不敏感)")
44
- .choices(["DEBUG", "INFO", "WARN", "ERROR"])
45
- .argParser((0, shared_1.caseInsensitiveChoice)(["DEBUG", "INFO", "WARN", "ERROR"])))
46
- .option("--since <time>", "开始时间")
47
- .option("--until <time>", "截止时间")
48
- .option("--log-id <id>", "按请求 logid 过滤(attributes.ob_data_id)")
49
- .option("--trace-id <id>", "按 trace ID 过滤")
50
- .option("--grep <pattern>", "按关键字模糊搜索 body")
51
- .option("--module <name>", "按模块名过滤")
52
- .option("--user-id <user-id>", "按触发用户 ID 过滤")
53
- .option("--page <path>", "按页面路径过滤")
54
- .option("--api <path>", "按接口路径过滤")
55
- .option("--min-duration <ms>", "最小耗时过滤(毫秒)")
56
- .option("--max-duration <ms>", "最大耗时过滤(毫秒)")
57
- .option("--limit <n>", "返回条数上限(1~100)", parseLimit, 50)
58
- .option("--cursor <token>", "下一页游标,从上一次返回的 next_cursor 取值")
59
- .addHelpText("after", `${COMMON_TIME_HELP}
43
+ .addOption(new commander_1.Option('--level <level>', '日志级别(大小写不敏感)')
44
+ .choices(['DEBUG', 'INFO', 'WARN', 'ERROR'])
45
+ .argParser((0, shared_1.caseInsensitiveChoice)(['DEBUG', 'INFO', 'WARN', 'ERROR'])))
46
+ .option('--since <time>', '开始时间')
47
+ .option('--until <time>', '截止时间')
48
+ .option('--log-id <id>', '按请求 logid 过滤(attributes.ob_data_id)')
49
+ .option('--trace-id <id>', '按 trace ID 过滤')
50
+ .option('--grep <pattern>', '按关键字模糊搜索 body')
51
+ .option('--module <name>', '按模块名过滤')
52
+ .option('--user-id <user-id>', '按触发用户 ID 过滤')
53
+ .option('--page <path>', '按页面路径过滤')
54
+ .option('--api <path>', '按接口路径过滤')
55
+ .option('--min-duration <ms>', '最小耗时过滤(毫秒)')
56
+ .option('--max-duration <ms>', '最大耗时过滤(毫秒)')
57
+ .option('--limit <n>', '返回条数上限(1~100)', parseLimit, 50)
58
+ .option('--cursor <token>', '下一页游标,从上一次返回的 next_cursor 取值')
59
+ .addHelpText('after', `${COMMON_TIME_HELP}
60
60
  JSON 输出
61
61
  {"data": [...], "next_cursor": "...", "has_more": true|false}
62
62
 
@@ -67,8 +67,8 @@ JSON 输出
67
67
  $ miaoda observability log --api /api/orders --min-duration 200 --json
68
68
  `);
69
69
  cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
70
- (0, shared_1.validateTimeOptions)(rawOpts, "since", "until");
71
- (0, shared_1.rejectCliOverride)(cmd, "appId");
70
+ (0, shared_1.validateTimeOptions)(rawOpts, 'since', 'until');
71
+ (0, shared_1.rejectCliOverride)(cmd, 'appId');
72
72
  await (0, index_1.handleObservabilityLog)({
73
73
  appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
74
74
  level: rawOpts.level,
@@ -90,22 +90,22 @@ JSON 输出
90
90
  }
91
91
  // ── trace ──
92
92
  function registerTrace(parent) {
93
- const traceCmd = parent.command("trace").description("链路追踪:列表 / 详情");
93
+ const traceCmd = parent.command('trace').description('链路追踪:列表 / 详情');
94
94
  traceCmd.action(() => {
95
95
  traceCmd.outputHelp();
96
96
  });
97
97
  const listCmd = traceCmd
98
- .command("list")
99
- .description("查询线上链路列表(按链路时间倒序,分页)")
98
+ .command('list')
99
+ .description('查询线上链路列表(按链路时间倒序,分页)')
100
100
  .addOption((0, shared_1.appIdOption)().hideHelp())
101
- .option("--since <time>", "开始时间")
102
- .option("--until <time>", "截止时间")
103
- .option("--trace-id <id>", "按 trace ID 过滤")
104
- .option("--root-span <span-name>", "按入口节点关键词过滤")
105
- .option("--user-id <user-id>", "按触发用户 ID 过滤")
106
- .option("--limit <n>", "返回条数上限(1~100)", parseLimit, 50)
107
- .option("--cursor <token>", "下一页游标")
108
- .addHelpText("after", `${COMMON_TIME_HELP}
101
+ .option('--since <time>', '开始时间')
102
+ .option('--until <time>', '截止时间')
103
+ .option('--trace-id <id>', '按 trace ID 过滤')
104
+ .option('--root-span <span-name>', '按入口节点关键词过滤')
105
+ .option('--user-id <user-id>', '按触发用户 ID 过滤')
106
+ .option('--limit <n>', '返回条数上限(1~100)', parseLimit, 50)
107
+ .option('--cursor <token>', '下一页游标')
108
+ .addHelpText('after', `${COMMON_TIME_HELP}
109
109
  JSON 输出
110
110
  {"data": [...], "next_cursor": "...", "has_more": true|false}
111
111
 
@@ -114,8 +114,8 @@ JSON 输出
114
114
  $ miaoda observability trace list --root-span api-gateway --limit 20 --json
115
115
  `);
116
116
  listCmd.action((0, shared_1.withHelp)(listCmd, async (rawOpts) => {
117
- (0, shared_1.validateTimeOptions)(rawOpts, "since", "until");
118
- (0, shared_1.rejectCliOverride)(listCmd, "appId");
117
+ (0, shared_1.validateTimeOptions)(rawOpts, 'since', 'until');
118
+ (0, shared_1.rejectCliOverride)(listCmd, 'appId');
119
119
  await (0, index_1.handleObservabilityTraceList)({
120
120
  appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
121
121
  since: rawOpts.since,
@@ -128,12 +128,12 @@ JSON 输出
128
128
  });
129
129
  }));
130
130
  const getCmd = traceCmd
131
- .command("get")
132
- .description("查询指定链路的完整详情")
133
- .argument("<trace-id>", "链路 ID")
131
+ .command('get')
132
+ .description('查询指定链路的完整详情')
133
+ .argument('<trace-id>', '链路 ID')
134
134
  .addOption((0, shared_1.appIdOption)().hideHelp())
135
- .option("--with-log-severity-count", "返回每个 Span 各日志级别的数量", false)
136
- .addHelpText("after", `
135
+ .option('--with-log-severity-count', '返回每个 Span 各日志级别的数量', false)
136
+ .addHelpText('after', `
137
137
  JSON 输出
138
138
  {"data": {"spans": [...], "isBreak": false}}
139
139
 
@@ -146,7 +146,7 @@ JSON 输出
146
146
  hint: 检查 trace-id 是否正确,或链路已超出保留时长
147
147
  `);
148
148
  getCmd.action((0, shared_1.withHelp)(getCmd, async (traceId, rawOpts) => {
149
- (0, shared_1.rejectCliOverride)(getCmd, "appId");
149
+ (0, shared_1.rejectCliOverride)(getCmd, 'appId');
150
150
  await (0, index_1.handleObservabilityTraceGet)({
151
151
  traceId,
152
152
  appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
@@ -157,20 +157,20 @@ JSON 输出
157
157
  // ── metric ──
158
158
  function registerMetric(parent) {
159
159
  const cmd = parent
160
- .command("metric")
161
- .description("查询监控指标:requests / latency / cpu / memory")
162
- .argument("<metric-name>", "指标名:requests | latency | cpu | memory")
160
+ .command('metric')
161
+ .description('查询监控指标:requests / latency / cpu / memory')
162
+ .argument('<metric-name>', '指标名:requests | latency | cpu | memory')
163
163
  .addOption((0, shared_1.appIdOption)().hideHelp())
164
- .option("--page <path>", "按页面路径过滤(仅对 requests/latency 生效)")
165
- .option("--api <path>", "按接口路径过滤(仅对 requests/latency 生效)")
166
- .option("--series <name>", "过滤图表中的某条线:latency 取 p50/p99;requests 取 total/error;缺省返回所有相关线")
167
- .option("--since <time>", "开始时间")
168
- .option("--until <time>", "截止时间")
169
- .addOption(new commander_1.Option("--down-sample <duration>", "降采样粒度(1m=1分钟 / 1h=1小时 / 1d=1天,大小写不敏感)")
170
- .choices(["1m", "1h", "1d"])
171
- .argParser((0, shared_1.caseInsensitiveChoice)(["1m", "1h", "1d"]))
172
- .default("1m"))
173
- .addHelpText("after", `${COMMON_TIME_HELP}
164
+ .option('--page <path>', '按页面路径过滤(仅对 requests/latency 生效)')
165
+ .option('--api <path>', '按接口路径过滤(仅对 requests/latency 生效)')
166
+ .option('--series <name>', '过滤图表中的某条线:latency 取 p50/p99;requests 取 total/error;缺省返回所有相关线')
167
+ .option('--since <time>', '开始时间')
168
+ .option('--until <time>', '截止时间')
169
+ .addOption(new commander_1.Option('--down-sample <duration>', '降采样粒度(1m=1分钟 / 1h=1小时 / 1d=1天,大小写不敏感)')
170
+ .choices(['1m', '1h', '1d'])
171
+ .argParser((0, shared_1.caseInsensitiveChoice)(['1m', '1h', '1d']))
172
+ .default('1m'))
173
+ .addHelpText('after', `${COMMON_TIME_HELP}
174
174
  JSON 输出
175
175
  {"data": [{"metricName": "...", "dimensions": {...}, "dataPoints": [...]}], "next_cursor": null, "has_more": false}
176
176
 
@@ -181,8 +181,8 @@ JSON 输出
181
181
  $ miaoda observability metric cpu --since 1d --down-sample 1d --json
182
182
  `);
183
183
  cmd.action((0, shared_1.withHelp)(cmd, async (metricName, rawOpts) => {
184
- (0, shared_1.validateTimeOptions)(rawOpts, "since", "until");
185
- (0, shared_1.rejectCliOverride)(cmd, "appId");
184
+ (0, shared_1.validateTimeOptions)(rawOpts, 'since', 'until');
185
+ (0, shared_1.rejectCliOverride)(cmd, 'appId');
186
186
  await (0, index_1.handleObservabilityMetric)({
187
187
  metricName,
188
188
  appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
@@ -198,16 +198,16 @@ JSON 输出
198
198
  // ── analytics ──
199
199
  function registerAnalytics(parent) {
200
200
  const cmd = parent
201
- .command("analytics")
202
- .description("查询运营指标:users(用户数)/ page-view(页面访问)")
203
- .argument("<analytics-name>", "指标名:users | page-view")
201
+ .command('analytics')
202
+ .description('查询运营指标:users(用户数)/ page-view(页面访问)')
203
+ .argument('<analytics-name>', '指标名:users | page-view')
204
204
  .addOption((0, shared_1.appIdOption)().hideHelp())
205
- .option("--page <path>", "按页面路径过滤(仅对 page-view 生效)")
206
- .option("--series <name>", "过滤图表中的某条线:users 取 active/new/total(兼容 active-users/new-users/total-users);page-view 取 all/desktop/mobile(兼容 all-view/desktop-view/mobile-view)")
207
- .option("--since <time>", "开始时间")
208
- .option("--until <time>", "截止时间")
209
- .option("--granularity <duration>", "时间粒度:day | week | month", "day")
210
- .addHelpText("after", `${COMMON_TIME_HELP}
205
+ .option('--page <path>', '按页面路径过滤(仅对 page-view 生效)')
206
+ .option('--series <name>', '过滤图表中的某条线:users 取 active/new/total(兼容 active-users/new-users/total-users);page-view 取 all/desktop/mobile(兼容 all-view/desktop-view/mobile-view)')
207
+ .option('--since <time>', '开始时间')
208
+ .option('--until <time>', '截止时间')
209
+ .option('--granularity <duration>', '时间粒度:day | week | month', 'day')
210
+ .addHelpText('after', `${COMMON_TIME_HELP}
211
211
  JSON 输出
212
212
  {"data": [{"metricType": "...", "points": [{"timestampNs": "...", "value": ..., "dimensions": {...}}]}], "next_cursor": null, "has_more": false}
213
213
 
@@ -217,8 +217,8 @@ JSON 输出
217
217
  $ miaoda observability analytics page-view --granularity week --json
218
218
  `);
219
219
  cmd.action((0, shared_1.withHelp)(cmd, async (analyticsName, rawOpts) => {
220
- (0, shared_1.validateTimeOptions)(rawOpts, "since", "until");
221
- (0, shared_1.rejectCliOverride)(cmd, "appId");
220
+ (0, shared_1.validateTimeOptions)(rawOpts, 'since', 'until');
221
+ (0, shared_1.rejectCliOverride)(cmd, 'appId');
222
222
  await (0, index_1.handleObservabilityAnalytics)({
223
223
  analyticsName,
224
224
  appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
@@ -4,13 +4,13 @@ exports.registerPluginCommands = registerPluginCommands;
4
4
  const index_1 = require("../../../cli/handlers/plugin/index");
5
5
  function registerPluginCommands(program) {
6
6
  const pluginCmd = program
7
- .command("plugin")
8
- .description("插件管理:安装/更新/移除插件包,查询 capability 实例")
9
- .usage("<command> [flags]");
7
+ .command('plugin')
8
+ .description('插件管理:安装/更新/移除插件包,查询 capability 实例')
9
+ .usage('<command> [flags]');
10
10
  pluginCmd.action(() => {
11
11
  pluginCmd.outputHelp();
12
12
  });
13
- pluginCmd.addHelpText("after", `
13
+ pluginCmd.addHelpText('after', `
14
14
  概念
15
15
  - 插件包 (plugin package):.tgz 包,释放到 ./node_modules/<name>
16
16
  - actionPlugins:./package.json 里的字段,记录 { "@scope/name": "version" },作为
@@ -26,10 +26,10 @@ function registerPluginCommands(program) {
26
26
  - remove / list / list-packages:纯本地 FS,不走网络
27
27
  `);
28
28
  pluginCmd
29
- .command("install")
30
- .description("拉取插件包并安装到当前应用项目")
31
- .argument("<names...>", "插件名,格式 @scope/name 或 @scope/name@version;未带版本默认 latest")
32
- .addHelpText("after", `
29
+ .command('install')
30
+ .description('拉取插件包并安装到当前应用项目')
31
+ .argument('<names...>', '插件名,格式 @scope/name 或 @scope/name@version;未带版本默认 latest')
32
+ .addHelpText('after', `
33
33
  行为
34
34
  - 拉取 .tgz 到本地缓存,解压到 ./node_modules/<name>
35
35
  - 在 ./package.json 的 actionPlugins 字段记录 <name>: <version>
@@ -62,10 +62,10 @@ JSON 输出
62
62
  await (0, index_1.handlePluginInstall)({ names });
63
63
  });
64
64
  pluginCmd
65
- .command("update")
66
- .description("把已安装插件升级到 latest 版本")
67
- .argument("<names...>", "插件名,格式 @scope/name;带 @version 也只取 name 部分")
68
- .addHelpText("after", `
65
+ .command('update')
66
+ .description('把已安装插件升级到 latest 版本')
67
+ .argument('<names...>', '插件名,格式 @scope/name;带 @version 也只取 name 部分')
68
+ .addHelpText('after', `
69
69
  行为
70
70
  - 仅对已在 actionPlugins 里的插件生效;未安装的插件返回 notInstalled
71
71
  - 拉取 latest 版本 .tgz,覆盖安装到 ./node_modules/<name>
@@ -97,10 +97,10 @@ JSON 输出
97
97
  await (0, index_1.handlePluginUpdate)({ names });
98
98
  });
99
99
  pluginCmd
100
- .command("remove")
101
- .description("从当前项目移除一个已安装的插件")
102
- .argument("<name>", "插件名,格式 @scope/name")
103
- .addHelpText("after", `
100
+ .command('remove')
101
+ .description('从当前项目移除一个已安装的插件')
102
+ .argument('<name>', '插件名,格式 @scope/name')
103
+ .addHelpText('after', `
104
104
  行为
105
105
  - 删除 ./node_modules/<name> 目录
106
106
  - 从 ./package.json 的 actionPlugins 字段移除对应条目
@@ -123,9 +123,9 @@ JSON 输出
123
123
  (0, index_1.handlePluginRemove)({ name });
124
124
  });
125
125
  pluginCmd
126
- .command("init")
127
- .description("按 package.json 的 actionPlugins 批量安装所有插件")
128
- .addHelpText("after", `
126
+ .command('init')
127
+ .description('按 package.json 的 actionPlugins 批量安装所有插件')
128
+ .addHelpText('after', `
129
129
  行为
130
130
  - 读取 ./package.json 的 actionPlugins,对每个条目按固定版本安装
131
131
  - 已经是目标版本的跳过(幂等),用于 clone 项目后首次同步 / CI
@@ -156,11 +156,11 @@ JSON 输出
156
156
  await (0, index_1.handlePluginInit)();
157
157
  });
158
158
  pluginCmd
159
- .command("list")
160
- .description("列出当前项目的 capability 实例(./server/capabilities/*.json)")
161
- .option("--summary", "不 hydrate(不充血),直接返回 capability 文件的原始 JSON")
162
- .option("--id <id>", "只查询指定 id 的单个 capability")
163
- .addHelpText("after", `
159
+ .command('list')
160
+ .description('列出当前项目的 capability 实例(./server/capabilities/*.json)')
161
+ .option('--summary', '不 hydrate(不充血),直接返回 capability 文件的原始 JSON')
162
+ .option('--id <id>', '只查询指定 id 的单个 capability')
163
+ .addHelpText('after', `
164
164
  行为
165
165
  - 扫描 ./server/capabilities/*.json(排除 capabilities.json)
166
166
  - 默认对每条 capability 做 hydrate:读取 ./node_modules/<pluginKey>/manifest.json,
@@ -190,9 +190,9 @@ JSON 输出
190
190
  await (0, index_1.handlePluginList)(opts);
191
191
  });
192
192
  pluginCmd
193
- .command("list-packages")
194
- .description("列出 package.json actionPlugins 里已声明的插件包")
195
- .addHelpText("after", `
193
+ .command('list-packages')
194
+ .description('列出 package.json actionPlugins 里已声明的插件包')
195
+ .addHelpText('after', `
196
196
  行为
197
197
  - 读取 ./package.json 的 actionPlugins 字段
198
198
  - 只看声明,不校验 ./node_modules 下是否实际存在
@@ -16,10 +16,10 @@ const args_1 = require("../../utils/args");
16
16
  Object.defineProperty(exports, "failArgs", { enumerable: true, get: function () { return args_1.failArgs; } });
17
17
  /** --app-id option,需要应用上下文的命令自行 .addOption(appIdOption()) */
18
18
  function appIdOption() {
19
- return new commander_1.Option("--app-id <id>", "指定目标应用").env("MIAODA_APP_ID");
19
+ return new commander_1.Option('--app-id <id>', '指定目标应用').env('MIAODA_APP_ID');
20
20
  }
21
21
  function branchOption() {
22
- return new commander_1.Option("--branch <branch>", "分支(优先级:--branch > 当前仓库 HEAD 分支;非 git 目录回退应用默认分支)").env("LOCAL_MOCK_MIAODA_DEPLOY_BRANCH");
22
+ return new commander_1.Option('--branch <branch>', '分支(优先级:--branch > 当前仓库 HEAD 分支;非 git 目录回退应用默认分支)').env('LOCAL_MOCK_MIAODA_DEPLOY_BRANCH');
23
23
  }
24
24
  /**
25
25
  * soft-required: Commander 类型上 optional,runtime 校验必填。
@@ -36,8 +36,8 @@ function softRequiredOption(name, desc) {
36
36
  function resolveAppId(opts) {
37
37
  const id = opts.appId ?? process.env.MIAODA_APP_ID ?? process.env.app_id;
38
38
  if (!id) {
39
- throw new error_1.AppError("APP_ID_MISSING", "未指定应用", {
40
- next_actions: ["设置 export MIAODA_APP_ID=<id>"],
39
+ throw new error_1.AppError('APP_ID_MISSING', '未指定应用', {
40
+ next_actions: ['设置 export MIAODA_APP_ID=<id>'],
41
41
  });
42
42
  }
43
43
  return id;
@@ -53,7 +53,7 @@ function withHelp(cmd, handler) {
53
53
  await handler(...args);
54
54
  }
55
55
  catch (err) {
56
- if (err instanceof error_1.AppError && err.code === "ARGS_INVALID") {
56
+ if (err instanceof error_1.AppError && err.code === 'ARGS_INVALID') {
57
57
  process.stderr.write(`Error: ${err.message}\n`);
58
58
  cmd.outputHelp();
59
59
  process.exitCode = 2;
@@ -81,7 +81,7 @@ function caseInsensitiveChoice(canonical) {
81
81
  return (raw) => {
82
82
  const hit = map.get(raw.toLowerCase());
83
83
  if (hit === undefined) {
84
- throw new commander_1.InvalidArgumentError(`Allowed choices are ${canonical.join(", ")} (case-insensitive).`);
84
+ throw new commander_1.InvalidArgumentError(`Allowed choices are ${canonical.join(', ')} (case-insensitive).`);
85
85
  }
86
86
  return hit;
87
87
  };
@@ -98,8 +98,8 @@ function validateTimeOptions(opts, ...names) {
98
98
  const value = opts[name];
99
99
  if (value === undefined)
100
100
  continue;
101
- if (typeof value !== "string") {
102
- (0, args_1.failArgs)(`--${name.replace(/([A-Z])/g, "-$1").toLowerCase()} 必须是时间字符串`);
101
+ if (typeof value !== 'string') {
102
+ (0, args_1.failArgs)(`--${name.replace(/([A-Z])/g, '-$1').toLowerCase()} 必须是时间字符串`);
103
103
  }
104
104
  (0, time_1.parseTimeToMs)(value);
105
105
  }
@@ -118,8 +118,8 @@ function validateTimeOptions(opts, ...names) {
118
118
  */
119
119
  function rejectCliOverride(cmd, ...optNames) {
120
120
  for (const name of optNames) {
121
- if (cmd.getOptionValueSource(name) === "cli") {
122
- const flag = "--" + name.replace(/([A-Z])/g, "-$1").toLowerCase();
121
+ if (cmd.getOptionValueSource(name) === 'cli') {
122
+ const flag = '--' + name.replace(/([A-Z])/g, '-$1').toLowerCase();
123
123
  (0, args_1.failArgs)(`${flag} 由沙箱/本地配置注入,不允许显式传递`);
124
124
  }
125
125
  }
@@ -1,7 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleAppUpdate = exports.handleAppGet = void 0;
3
+ exports.SUPPORTED_STACKS = exports.handleAppInit = exports.handleAppUpdate = exports.handleAppGet = void 0;
4
4
  var get_1 = require("./get");
5
5
  Object.defineProperty(exports, "handleAppGet", { enumerable: true, get: function () { return get_1.handleAppGet; } });
6
6
  var update_1 = require("./update");
7
7
  Object.defineProperty(exports, "handleAppUpdate", { enumerable: true, get: function () { return update_1.handleAppUpdate; } });
8
+ var init_1 = require("./init");
9
+ Object.defineProperty(exports, "handleAppInit", { enumerable: true, get: function () { return init_1.handleAppInit; } });
10
+ // commands 层渲染 help 时需要这份枚举;从 handler barrel 转发,避免 commands → services 越界
11
+ var index_1 = require("../../../services/app/init/index");
12
+ Object.defineProperty(exports, "SUPPORTED_STACKS", { enumerable: true, get: function () { return index_1.SUPPORTED_STACKS; } });
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.handleAppInit = handleAppInit;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const index_1 = require("../../../services/app/init/index");
10
+ const error_1 = require("../../../utils/error");
11
+ const output_1 = require("../../../utils/output");
12
+ /** miaoda app init [--template <stack>] [--conf <json>] */
13
+ async function handleAppInit(opts) {
14
+ await Promise.resolve();
15
+ const targetDir = opts.targetDir ?? process.cwd();
16
+ const metaPath = node_path_1.default.join(targetDir, '.spark', 'meta.json');
17
+ // 幂等:.spark/meta.json 是 init 流程最后才写的,存在即代表"已完整成功一次"。
18
+ // 用 package.json 判幂等会让 install 失败、CWD 留下半渲染状态的应用永远 init 不了。
19
+ if (node_fs_1.default.existsSync(metaPath)) {
20
+ if (!(0, output_1.isJsonMode)()) {
21
+ process.stdout.write(`Already initialized: ${metaPath}\n`);
22
+ }
23
+ (0, output_1.emit)({
24
+ data: {
25
+ initialized: false,
26
+ reason: 'already_initialized',
27
+ targetDir,
28
+ },
29
+ });
30
+ return;
31
+ }
32
+ const stack = opts.template;
33
+ if (!stack) {
34
+ throw new error_1.AppError('ARGS_INVALID', '缺少 --template <stack>', {
35
+ next_actions: [`可用 stack:${index_1.SUPPORTED_STACKS.join(', ')}`],
36
+ });
37
+ }
38
+ if (!index_1.SUPPORTED_STACKS.includes(stack)) {
39
+ throw new error_1.AppError('ARGS_INVALID', `不支持的 template: ${stack}`, {
40
+ next_actions: [`可用 stack:${index_1.SUPPORTED_STACKS.join(', ')}`],
41
+ });
42
+ }
43
+ const version = opts.conf?.version;
44
+ const projectName = node_path_1.default.basename(targetDir);
45
+ const tplResult = (0, index_1.renderTemplate)({ stack, version, targetDir, projectName });
46
+ const steeringResult = (0, index_1.syncSteering)({
47
+ stack,
48
+ targetDir,
49
+ version: opts.conf?.steeringVersion,
50
+ });
51
+ const installResult = (0, index_1.installDependencies)({
52
+ targetDir,
53
+ skip: opts.skipInstall,
54
+ quietStdout: (0, output_1.isJsonMode)(),
55
+ });
56
+ (0, index_1.writeSparkMeta)(targetDir, {
57
+ appId: opts.appId,
58
+ template: stack,
59
+ templatePackage: tplResult.packageName,
60
+ templateVersion: tplResult.version,
61
+ steeringVersion: steeringResult.version,
62
+ archType: tplResult.archType,
63
+ installSource: installResult.source,
64
+ installHash: installResult.hash,
65
+ });
66
+ if (!(0, output_1.isJsonMode)()) {
67
+ process.stdout.write(`✓ Initialized ${stack} (template ${tplResult.version}, steering ${steeringResult.version}, install ${installResult.source}) in ${targetDir}\n`);
68
+ }
69
+ (0, output_1.emit)({
70
+ data: {
71
+ initialized: true,
72
+ appId: opts.appId,
73
+ template: stack,
74
+ templatePackage: tplResult.packageName,
75
+ templateVersion: tplResult.version,
76
+ steeringVersion: steeringResult.version,
77
+ archType: tplResult.archType,
78
+ syncedSkills: steeringResult.syncedSkills,
79
+ techSynced: steeringResult.techSynced,
80
+ installed: installResult.installed,
81
+ installSource: installResult.source,
82
+ installHash: installResult.hash,
83
+ cacheZip: installResult.cacheZip,
84
+ },
85
+ });
86
+ }
@@ -41,7 +41,7 @@ const index_1 = require("../../../api/app/index");
41
41
  /** miaoda app update [--app-id <id>] [--name <n>] [--description <d>] */
42
42
  async function handleAppUpdate(opts) {
43
43
  if (opts.name === undefined && opts.description === undefined) {
44
- (0, args_1.failArgs)("至少需要 --name 或 --description 中的一个");
44
+ (0, args_1.failArgs)('至少需要 --name 或 --description 中的一个');
45
45
  }
46
46
  const appID = opts.appId;
47
47
  await api.app.updateAppMeta({
@@ -53,7 +53,7 @@ async function handleAppUpdate(opts) {
53
53
  const resp = await api.app.getAppInfo(appID);
54
54
  const meta = resp.appInfo?.appMeta ?? {};
55
55
  if (!(0, output_1.isJsonMode)()) {
56
- process.stdout.write("✓ App updated successfully\n");
56
+ process.stdout.write('✓ App updated successfully\n');
57
57
  }
58
58
  (0, output_1.emit)({ data: meta }, index_1.appMetaSchema);
59
59
  }
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ // db 域内不可逆 / destructive 操作的统一确认门。
3
+ //
4
+ // 设计要点:
5
+ // 1. --yes:放行(CI / 脚本显式确认场景)
6
+ // 2. 非 TTY 且无 --yes:抛 DESTRUCTIVE_REQUIRES_CONFIRM,不要默认放行。
7
+ // 非 TTY 通常是 CI / agent / 管道场景,无人能交互回答 y/N,必须靠 --yes 显式确认。
8
+ // 3. TTY 且无 --yes:交互式 y/N(prompt 写 stderr,避免污染 --json 模式 stdout)
9
+ //
10
+ // 反例(已修复,见这次 commit):把 !isJsonMode() 拿来做确认门 —— --json 只改输出
11
+ // 格式,跟"用户已确认"不是同一语义。CI / agent 几乎总会带 --json 解析输出,等价于
12
+ // 把不可逆操作的确认变成了空门,跟 file rm 的模型也不一致。
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.confirmDestructive = confirmDestructive;
18
+ exports.assertDestructiveAllowedInTty = assertDestructiveAllowedInTty;
19
+ exports.askYesNo = askYesNo;
20
+ const node_readline_1 = __importDefault(require("node:readline"));
21
+ const render_1 = require("../../../utils/render");
22
+ const error_1 = require("../../../utils/error");
23
+ const DESTRUCTIVE_REQUIRES_CONFIRM_MSG = 'This operation is destructive. Rerun with --yes to confirm.';
24
+ /**
25
+ * 完整的"yes/tty/交互"决策门。直接用于无需 fetch 预览的简单确认场景(如 migration init)。
26
+ *
27
+ * 返回值:
28
+ * - true → 已确认,可执行
29
+ * - false → TTY 下用户输了 n,调用方应渲染 Aborted 并退出
30
+ *
31
+ * 异常:
32
+ * - DESTRUCTIVE_REQUIRES_CONFIRM:非 TTY 且无 --yes
33
+ */
34
+ async function confirmDestructive(prompt, yes) {
35
+ if (yes === true)
36
+ return true;
37
+ if (!(0, render_1.isStdoutTty)()) {
38
+ throw new error_1.AppError('DESTRUCTIVE_REQUIRES_CONFIRM', DESTRUCTIVE_REQUIRES_CONFIRM_MSG);
39
+ }
40
+ return askYesNo(prompt);
41
+ }
42
+ /**
43
+ * 给"需要在 confirm 之前先 fetch 预览"的场景(migration apply / recovery apply 可选优化)用:
44
+ * 在非 TTY 无 --yes 时提前抛错,避免无意义触发后端 dry-run RPC。
45
+ *
46
+ * 调用后 TTY 路径仍需自行调 askYesNo / confirmDestructive 完成交互。
47
+ */
48
+ function assertDestructiveAllowedInTty(yes) {
49
+ if (yes === true)
50
+ return;
51
+ if (!(0, render_1.isStdoutTty)()) {
52
+ throw new error_1.AppError('DESTRUCTIVE_REQUIRES_CONFIRM', DESTRUCTIVE_REQUIRES_CONFIRM_MSG);
53
+ }
54
+ }
55
+ /**
56
+ * 交互式 y/N。输入读 stdin,prompt 写 stderr —— stderr 避免污染 --json 模式 stdout 的 envelope。
57
+ * 仅 'y'(忽略大小写)算确认;其余一律视为否决。
58
+ */
59
+ async function askYesNo(prompt) {
60
+ const rl = node_readline_1.default.createInterface({ input: process.stdin, output: process.stderr });
61
+ return new Promise((resolve) => {
62
+ rl.question(prompt, (answer) => {
63
+ rl.close();
64
+ resolve(answer.trim().toLowerCase() === 'y');
65
+ });
66
+ });
67
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ // db 域内 --env 参数的 CLI vocab 校验。
3
+ //
4
+ // 用户视角的 vocab:
5
+ // - dev → 多环境沙箱分支
6
+ // - online → 生产分支(向后端发请求时映射成 main,client.ts/api.ts 已处理)
7
+ // - main → online 的别名,兼容历史脚本
8
+ //
9
+ // 配合 client.ts::buildUnknownEnvError,把 vocab 错(这里)跟 backend state 错
10
+ // (client.ts 处理)统一成同一类 UNKNOWN_ENV_VALUE,避免用户在两种失败之间困惑。
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.validateEnv = validateEnv;
13
+ const client_1 = require("../../../api/db/client");
14
+ const VOCAB = ['dev', 'main', 'online'];
15
+ const VOCAB_SET = new Set(VOCAB);
16
+ /**
17
+ * CLI vocab 校验。空值(未传 --env)放行,由后端按 workspace 默认 branch 处理。
18
+ * 不在 vocab 内 → 抛 UNKNOWN_ENV_VALUE,preAction hook 兜底,避免无意义触发 RPC。
19
+ */
20
+ function validateEnv(env) {
21
+ if (env === undefined || env === '')
22
+ return;
23
+ if (VOCAB_SET.has(env))
24
+ return;
25
+ throw (0, client_1.buildUnknownEnvError)(env);
26
+ }