@lark-apaas/miaoda-cli 0.1.0-alpha.c11e8f1 → 0.1.0-alpha.ca2912e

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.
@@ -19,7 +19,7 @@ function ensureInnerSuccess(body) {
19
19
  const code = body.status_code ?? body.ErrorCode ?? "0";
20
20
  if (code === "0" || code === "")
21
21
  return;
22
- const message = body.error_msg ?? body.ErrorMessage ?? `dataloom API error [${code}]`;
22
+ const message = stripPgPrefix(body.error_msg ?? body.ErrorMessage ?? `dataloom API error [${code}]`);
23
23
  // k_dl_1300002 是 PG 执行透传错误;error_msg 里常带 SQLSTATE,优先按 SQLSTATE 映射
24
24
  if (code === "k_dl_1300002") {
25
25
  const sqlstate = extractSqlstate(message);
@@ -49,6 +49,14 @@ function ensureInnerSuccess(body) {
49
49
  // 兜底:dataloom 未映射的 code 原样透传
50
50
  throw new error_1.AppError(`DB_API_${code}`, message);
51
51
  }
52
+ /**
53
+ * 剥掉 PG 透传错误的 "ERROR:" 前缀:CLI 输出本身就会前缀 "Error:",
54
+ * 不去掉就会变成 `Error: ERROR: relation ...` 双重前缀,PRD 不要这种冗余。
55
+ * 大小写敏感,只匹配 PG 标准格式。
56
+ */
57
+ function stripPgPrefix(msg) {
58
+ return msg.replace(/^ERROR:\s*/, "");
59
+ }
52
60
  /** 从 PG 执行错误消息里提取 "(SQLSTATE XXXXX)"。 */
53
61
  function extractSqlstate(msg) {
54
62
  const m = /SQLSTATE\s+([0-9A-Z]{5})/.exec(msg);
@@ -27,7 +27,7 @@ function parseSqlResult(r) {
27
27
  recordCount: r.recordCount ?? rows.length,
28
28
  };
29
29
  }
30
- if (r.sqlType === "INSERT" || r.sqlType === "UPDATE" || r.sqlType === "DELETE") {
30
+ if (r.sqlType === "INSERT" || r.sqlType === "UPDATE" || r.sqlType === "DELETE" || r.sqlType === "DML") {
31
31
  const affected = r.affectedRows ?? extractRowCount(r.data);
32
32
  return {
33
33
  kind: "dml",
@@ -7,7 +7,9 @@ exports.withHelp = withHelp;
7
7
  exports.failArgs = failArgs;
8
8
  const commander_1 = require("commander");
9
9
  const error_1 = require("../../utils/error");
10
- /** --app-id option,需要应用上下文的命令自行 .addOption(appIdOption()) */
10
+ /** --app-id option,需要应用上下文的命令自行 .addOption(appIdOption())
11
+ * Commander 的 .env() 只接受单个变量名,第二个兜底 `app_id` 在 resolveAppId 里手动检查。
12
+ */
11
13
  function appIdOption() {
12
14
  return new commander_1.Option("--app-id <id>", "指定目标应用").env("MIAODA_APP_ID");
13
15
  }
@@ -17,9 +19,12 @@ function appIdOption() {
17
19
  function softRequiredOption(name, desc) {
18
20
  return new commander_1.Option(name, desc);
19
21
  }
20
- /** 解析 appId,CLI flag > env > 抛错 */
22
+ /**
23
+ * 解析 appId,优先级:CLI flag > MIAODA_APP_ID > app_id > 抛错。
24
+ * app_id 是部分外部沙箱环境注入应用 ID 的别名,作为兜底兼容(小写下划线形态)。
25
+ */
21
26
  function resolveAppId(opts) {
22
- const id = opts.appId ?? process.env.MIAODA_APP_ID;
27
+ const id = opts.appId ?? process.env.MIAODA_APP_ID ?? process.env.app_id;
23
28
  if (!id) {
24
29
  throw new error_1.AppError("APP_ID_MISSING", "未指定应用", {
25
30
  next_actions: [
@@ -133,20 +133,22 @@ function toJson(parsed) {
133
133
  return { data: parsed.rows };
134
134
  }
135
135
  if (parsed.kind === "dml") {
136
+ // PRD:DML 时 data = {command: "UPDATE", rows_affected: 3}
136
137
  return {
137
138
  data: {
138
- sql_type: parsed.sqlType.toLowerCase(),
139
- affected_rows: parsed.affectedRows,
139
+ command: parsed.sqlType,
140
+ rows_affected: parsed.affectedRows,
140
141
  },
141
142
  };
142
143
  }
143
- return { data: { sql_type: "ddl" } };
144
+ return { data: { command: "DDL" } };
144
145
  }
145
146
  function dmlVerb(type) {
146
147
  switch (type) {
147
148
  case "INSERT": return "inserted";
148
149
  case "UPDATE": return "updated";
149
150
  case "DELETE": return "deleted";
151
+ case "DML": return "affected"; // MERGE / 未识别子类的兜底
150
152
  }
151
153
  }
152
154
  /** 从第一行收集列顺序;缺失列保留空白(兼容稀疏行)。 */
@@ -32,19 +32,19 @@ function emit(data) {
32
32
  /**
33
33
  * 输出错误(写入 stderr)
34
34
  * - pretty 模式:Error: message\n hint: ...
35
- * - json 模式:{"error_code": "...", "message": "...", "hint": "..."}
35
+ * - json 模式:PRD 约定 envelope 为 `{error: {code, message, hint?}}`
36
36
  */
37
37
  function emitError(err) {
38
38
  const info = toErrorInfo(err);
39
39
  if (isJsonMode()) {
40
- const jsonErr = {
41
- error_code: info.code,
40
+ const errObj = {
41
+ code: info.code,
42
42
  message: info.message,
43
43
  };
44
44
  if (info.next_actions && info.next_actions.length > 0) {
45
- jsonErr.hint = info.next_actions.join(" ");
45
+ errObj.hint = info.next_actions.join(" ");
46
46
  }
47
- process.stderr.write(JSON.stringify(jsonErr) + "\n");
47
+ process.stderr.write(JSON.stringify({ error: errObj }) + "\n");
48
48
  }
49
49
  else {
50
50
  process.stderr.write(`Error: ${info.message}\n`);
@@ -71,9 +71,39 @@ function formatTime(iso, isTty) {
71
71
  * 算进列宽(比如 dim "NULL" 实际占 4 字符但 .length=11),导致表格对齐错位。
72
72
  */
73
73
  const ANSI_SGR_RE = /\[[0-9;]*m/g;
74
- /** cell 可见字符宽度:剥离 ANSI 转义后再算长度。 */
74
+ /**
75
+ * 单字符的终端列宽:CJK 全角字符(汉字/假名/全角符号等)占 2 列,其它占 1 列。
76
+ * 对齐 Unicode East Asian Width 的 W/F 类,等价于 wcwidth 简化版。
77
+ * 不实现合字 / 零宽字符(ZWJ / 变体选择符)等极端情况,CLI 表格场景够用。
78
+ */
79
+ function charWidth(cp) {
80
+ if ((cp >= 0x1100 && cp <= 0x115F) || // Hangul Jamo
81
+ cp === 0x2329 || cp === 0x232A ||
82
+ (cp >= 0x2E80 && cp <= 0x303E) || // CJK Radicals / Punctuation
83
+ (cp >= 0x3041 && cp <= 0x33FF) || // Hiragana / Katakana / CJK Symbols
84
+ (cp >= 0x3400 && cp <= 0x4DBF) || // CJK Ext A
85
+ (cp >= 0x4E00 && cp <= 0x9FFF) || // CJK Unified
86
+ (cp >= 0xA000 && cp <= 0xA4CF) || // Yi
87
+ (cp >= 0xAC00 && cp <= 0xD7A3) || // Hangul Syllables
88
+ (cp >= 0xF900 && cp <= 0xFAFF) || // CJK Compat Ideographs
89
+ (cp >= 0xFE30 && cp <= 0xFE4F) || // CJK Compat Forms
90
+ (cp >= 0xFF00 && cp <= 0xFF60) || // Fullwidth Forms
91
+ (cp >= 0xFFE0 && cp <= 0xFFE6) ||
92
+ (cp >= 0x20000 && cp <= 0x2FFFD) || // CJK Ext B-F
93
+ (cp >= 0x30000 && cp <= 0x3FFFD) // CJK Ext G-H
94
+ ) {
95
+ return 2;
96
+ }
97
+ return 1;
98
+ }
99
+ /** cell 可见字符宽度:剥离 ANSI 转义后按终端列宽逐字符累加(CJK 算 2 列)。 */
75
100
  function visibleWidth(s) {
76
- return s.replace(ANSI_SGR_RE, "").length;
101
+ const stripped = s.replace(ANSI_SGR_RE, "");
102
+ let w = 0;
103
+ for (const c of stripped) {
104
+ w += charWidth(c.codePointAt(0) ?? 0);
105
+ }
106
+ return w;
77
107
  }
78
108
  function padVisibleEnd(s, targetWidth) {
79
109
  const w = visibleWidth(s);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/miaoda-cli",
3
- "version": "0.1.0-alpha.c11e8f1",
3
+ "version": "0.1.0-alpha.ca2912e",
4
4
  "description": "Miaoda 平台命令行工具,面向 Agent 调用",
5
5
  "type": "commonjs",
6
6
  "bin": {