@finebi/finebi-cli 1.2.1 → 1.4.0

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 (67) hide show
  1. package/README.md +63 -39
  2. package/package.json +40 -37
  3. package/skills/finebi-cli-agent/SKILL.md +133 -0
  4. package/skills/finebi-cli-agent/agents/openai.yaml +7 -0
  5. package/skills/finebi-cli-agent/references/analysis-dsl-api.md +322 -0
  6. package/skills/finebi-cli-agent/references/artifact-ordering.md +29 -0
  7. package/skills/finebi-cli-agent/references/command-workflows.md +199 -0
  8. package/skills/finebi-cli-agent/references/dashboard-dsl-api.md +1037 -0
  9. package/skills/finebi-cli-agent/references/dsl-handoff-contract.md +195 -0
  10. package/skills/finebi-cli-agent/references/file-to-dashboard.md +65 -0
  11. package/skills/finebi-cli-agent/references/formula-basics.md +102 -0
  12. package/skills/finebi-cli-agent/references/formula-contexts.md +66 -0
  13. package/skills/finebi-cli-agent/references/formula-functions.md +247 -0
  14. package/skills/finebi-cli-agent/references/formula-generation.md +121 -0
  15. package/skills/finebi-cli-agent/references/formula-glossary.md +17 -0
  16. package/skills/finebi-cli-agent/references/formula-help-boundaries.md +87 -0
  17. package/skills/finebi-cli-agent/references/formula-help-index.md +264 -0
  18. package/skills/finebi-cli-agent/references/formula-parameters-aggregate.md +143 -0
  19. package/skills/finebi-cli-agent/references/formula-parameters-analysis.md +223 -0
  20. package/skills/finebi-cli-agent/references/formula-parameters-date.md +236 -0
  21. package/skills/finebi-cli-agent/references/formula-parameters-math-logic.md +271 -0
  22. package/skills/finebi-cli-agent/references/formula-parameters-text.md +217 -0
  23. package/skills/finebi-cli-agent/references/formula-recipes-analysis.md +124 -0
  24. package/skills/finebi-cli-agent/references/formula-recipes-text-date.md +86 -0
  25. package/skills/finebi-cli-agent/references/formula-semantics.md +39 -0
  26. package/skills/finebi-cli-agent/references/formula-syntax.md +70 -0
  27. package/skills/finebi-cli-agent/references/formula-troubleshooting.md +63 -0
  28. package/skills/finebi-cli-agent/references/formula-validation.md +36 -0
  29. package/skills/finebi-cli-agent/references/formula.md +37 -0
  30. package/skills/finebi-cli-agent/references/handoff-contract.md +210 -0
  31. package/skills/finebi-cli-agent/references/mapping-rules.md +423 -0
  32. package/skills/finebi-cli-agent/references/mutation-confirmation-contract.md +150 -0
  33. package/skills/finebi-cli-agent/references/scenario-workflows.md +75 -0
  34. package/skills/finebi-cli-agent/references/widget-dsl-api-examples.md +457 -0
  35. package/skills/finebi-cli-agent/references/widget-dsl-api.md +1340 -0
  36. package/skills/finebi-cli-agent/scripts/finebi-cli/package.json +11 -0
  37. package/skills/finebi-cli-agent/scripts/finebi-cli/src/access-key-contract.js +2 -0
  38. package/skills/finebi-cli-agent/scripts/finebi-cli/src/access-key.js +174 -0
  39. package/skills/finebi-cli-agent/scripts/finebi-cli/src/auth-failure.js +18 -0
  40. package/skills/finebi-cli-agent/scripts/finebi-cli/src/auth-headers.js +21 -0
  41. package/skills/finebi-cli-agent/scripts/finebi-cli/src/auth.js +116 -0
  42. package/skills/finebi-cli-agent/scripts/finebi-cli/src/branding.js +31 -0
  43. package/skills/finebi-cli-agent/scripts/finebi-cli/src/browser-auth.js +331 -0
  44. package/skills/finebi-cli-agent/scripts/finebi-cli/src/capability.js +122 -0
  45. package/skills/finebi-cli-agent/scripts/finebi-cli/src/config-permissions.js +87 -0
  46. package/skills/finebi-cli-agent/scripts/finebi-cli/src/config.js +189 -0
  47. package/skills/finebi-cli-agent/scripts/finebi-cli/src/dynamic-command.js +324 -0
  48. package/skills/finebi-cli-agent/scripts/finebi-cli/src/error-catalog.js +482 -0
  49. package/skills/finebi-cli-agent/scripts/finebi-cli/src/errors.js +22 -0
  50. package/skills/finebi-cli-agent/scripts/finebi-cli/src/help.js +467 -0
  51. package/skills/finebi-cli-agent/scripts/finebi-cli/src/index.js +1351 -0
  52. package/skills/finebi-cli-agent/scripts/finebi-cli/src/install-skill.js +89 -0
  53. package/skills/finebi-cli-agent/scripts/finebi-cli/src/output-limits.js +188 -0
  54. package/skills/finebi-cli-agent/scripts/finebi-cli/src/output.js +33 -0
  55. package/skills/finebi-cli-agent/scripts/finebi-cli/src/profiles.js +158 -0
  56. package/skills/finebi-cli-agent/scripts/finebi-cli/src/schema-options.js +265 -0
  57. package/skills/finebi-cli-agent/scripts/finebi-cli/src/schema-validator.js +194 -0
  58. package/skills/finebi-cli-agent/scripts/finebi-cli/src/skill-files.js +94 -0
  59. package/skills/finebi-cli-agent/scripts/finebi-cli-runner-core.mjs +399 -0
  60. package/skills/finebi-cli-agent/scripts/finebi-cli-runner.mjs +29 -0
  61. package/src/config.js +27 -27
  62. package/src/error-catalog.js +12 -0
  63. package/src/help.js +70 -55
  64. package/src/index.js +17 -4
  65. package/src/install-skill.js +89 -0
  66. package/src/profiles.js +158 -158
  67. package/src/skill-files.js +94 -0
@@ -0,0 +1,322 @@
1
+ # Analysis Table DSL API
2
+
3
+ **能力门禁(必读)**:自助数据集步骤链由所连服务器的能力清单决定。使用任何步骤链语句前,先
4
+ `finebi-cli capability refresh` 并查看 `finebi-cli analysis-ds --help`:清单包含 `update` / `dsl` /
5
+ `operator-schema`(内部试用开关已开启的服务器)→ 下述全量 API 可用;清单不包含(正式版默认态)→
6
+ 仅支持原样引用创建(`create` / `from` / `fromExcel`),携带步骤链的脚本会被拒绝,此时不得规划
7
+ 「读取 → 修改 → 写回」改表工作流。步骤级命令不存在时直接按能力面收窄执行,不要重试。
8
+
9
+ 这份文档只列当前文本 DSL 可以直接使用的自助数据集(AnalysisTableDsl)API,以及各 API 的业务含义。
10
+ 本语句面同时是 `analysis-ds dsl --id <表id>` 回读命令的输出面(平台 Bean 反向翻译的 DSL 文本,
11
+ 见 docs/commands/)。创建用 `analysis-ds add --subject-id <id> --file <x.js>`,覆盖步骤链(来源不变)
12
+ 用 `analysis-ds update --id <id> --file <x.js>`;已保存表的数据预览用 `analysis-ds preview --id <id>`。
13
+ 模板示例见 `analysis-ds template --scenario <basic|formula|group|full>`(场景集同样以能力清单为准,
14
+ 默认态仅 `basic`),语句面速查见 `analysis-ds operator-schema`。
15
+
16
+ ## 1. 根入口
17
+
18
+ ```js
19
+ AnalysisTableDsl.create("表名")
20
+ ```
21
+
22
+ 业务含义:
23
+
24
+ - 新建一个自助数据集 Builder;表名是保存后的展示名。
25
+ - 完成链式调用后必须以 `.build()` 结尾。
26
+
27
+ ## 2. 来源与字段
28
+
29
+ ```js
30
+ .from("来源表原始名")
31
+ .fromExcel("附件id", "Sheet1", ...) // Excel 来源(create 专用;省略 sheetNames 默认仅取首个有效 Sheet)
32
+ .mergeSheets() // 可选:合并所选 Sheet;省略 sheetNames 时合并全部有效 Sheet
33
+ .select("字段名", ...) // 可选:引用白名单(只允许后续步骤引用这些字段)
34
+ ```
35
+
36
+ 业务含义:
37
+
38
+ - `from` 必选第一步,来源可以是公共数据集、主题内表或 Excel 表的**原始名**(全局唯一,即
39
+ `analysis-ds search` / `public-ds search` 返回的 `id`)。转义名/展示名允许重名,不作来源标识。
40
+ - `fromExcel` 的附件 id 来自 `analysis-ds upload-excel`;引擎默认 Spider。
41
+ - `fromExcel` 一脚本一表:未声明 `.mergeSheets()` 时,省略 `sheetNames` 仅取首个有效 Sheet;
42
+ 显式指定多个 Sheet 会被拒绝。声明 `.mergeSheets()` 时合并指定的 Sheet;省略 `sheetNames`
43
+ 时合并全部有效 Sheet。合并要求至少两个 Sheet,且字段名、类型及顺序一致,否则报错。
44
+ - `from` 原始名直查失败时,错误会列出两棵树的近似候选(转义名 + id + 位置),可据此修正。
45
+ - `select` 可选:声明后续步骤可引用的字段范围(白名单);**不产生字段收窄**——7.0 前端
46
+ 第一步恒保存来源全字段,字段收窄由字段设置步骤的 `hide` 表达。
47
+ - `analysis-ds dsl --id` 反读时 `from("原始名")` 行尾带 `// 转义名` 注释(可读性补偿,非真值源)。
48
+
49
+ ## 3. 过滤
50
+
51
+ ```js
52
+ .filter(eq("字段名", "值"))
53
+ .filter(and(eq("字段名", "值"), gt("数值字段", 500)))
54
+ ```
55
+
56
+ 条件工厂(全局函数,可组合成树):
57
+
58
+ | 工厂 | 语义 | 支持字段类型 |
59
+ |---|---|---|
60
+ | `eq(field, value)` / `ne(field, value)` | 等于 / 不等于 | 字符串、数值 |
61
+ | `gt(field, value)` / `ge(field, value)` / `lt(field, value)` / `le(field, value)` | 大于 / 大于等于 / 小于 / 小于等于 | 数值 |
62
+ | `in(field, [值...])` / `notIn(field, [值...])` | 枚举包含 / 不包含 | 字符串 |
63
+ | `between(field, from, to)` | 闭区间 | 数值、日期(日期值写 `"2024-01-01"`) |
64
+ | `like(field, pattern)` | 模糊匹配 | 字符串 |
65
+ | `isNull(field)` / `notNull(field)` | 为空 / 非空 | 全部 |
66
+ | `and(...)` / `or(...)` / `not(cond)` | 组合树 | —(`not` 当前适配器不支持,使用会报错) |
67
+
68
+ 业务含义:
69
+
70
+ - 每次 `.filter(...)` 产生一个过滤步骤;多步过滤按声明顺序执行(语义为 AND,保留步骤结构)。
71
+ - 过滤条件只支持字段与字面量比较;字段间比较请先建公式列。
72
+
73
+ ## 4. 排序
74
+
75
+ ```js
76
+ .sort(desc("字段名"), asc("字段名"))
77
+ ```
78
+
79
+ | DSL API | 语义 |
80
+ |---|---|
81
+ | `asc("字段名")` | 升序 |
82
+ | `desc("字段名")` | 降序 |
83
+
84
+ 业务含义:一次 `.sort(...)` 产生一个排序步骤,支持多字段;排序字段必须是当前步骤可用字段。
85
+
86
+ ## 5. 字段设置
87
+
88
+ ```js
89
+ .rename("旧名", "新名")
90
+ .hide("字段名", ...)
91
+ .convertType("字段名", NUMBER)
92
+ .order("字段A", "字段B", "字段C")
93
+ ```
94
+
95
+ | DSL API | 语义 |
96
+ |---|---|
97
+ | `rename(old, new)` | 重命名;后续步骤引用新名 |
98
+ | `hide(field...)` | 隐藏字段(从输出移除,链内保留) |
99
+ | `convertType(field, type)` | 改字段类型:`STRING` / `NUMBER` / `DATE` / `DATE_TIME` |
100
+ | `order(field...)` | 声明输出字段序(全量**输入名**列表——rename 前名字,恰好覆盖全部字段一次) |
101
+
102
+ 业务含义:连续多个字段设置调用(不被其他类型步骤打断)合并为**一个**字段设置步骤;
103
+ 被其他类型步骤打断后重新开新步骤。
104
+
105
+ ## 6. 公式列
106
+
107
+ ```js
108
+ .addFormulaColumn("分差", "${数学均分}-${AvgReading}", NUMBER)
109
+ ```
110
+
111
+ 业务含义:
112
+
113
+ - 追加一个由 FineBI 公式计算的新列;`${字段名}` 引用当前步骤字段(保存时引擎改写为 fieldId)。
114
+ - 结果类型:`STRING` / `NUMBER` / `DATE` / `AUTO`(`DATE_TIME` 并入 DATE 族;`AUTO` = 引擎自动推断,
115
+ 前端建表默认,`analysis-ds dsl` 反读前端表时会原样输出 `AUTO`)。
116
+
117
+ ## 7. 分组汇总
118
+
119
+ ```js
120
+ .groupBy("County")
121
+ .sum("AvgMath", "数学总和")
122
+ .count("校数")
123
+ ```
124
+
125
+ | DSL API | 语义 |
126
+ |---|---|
127
+ | `groupBy(field...)` | 分组维度(日期字段必须为 DATE 类型) |
128
+ | `sum(field[, alias])` / `avg` / `max` / `min` | 数值字段聚合,alias 可选 |
129
+ | `countDistinct(field[, alias])` | 去重计数 |
130
+ | `count([alias])` | 记录数;载体是第一个分组字段,**alias 必填**(缺省名与分组字段重名会报错) |
131
+
132
+ 业务含义:分组汇总后字段集重置为「分组结果名 + 汇总结果名」;`groupBy` 是子上下文入口,
133
+ 聚合调用后直接继续主链方法(无 `.end()`)。
134
+
135
+ ### 7.1 自定义分组 / 日期分组 / 指标条件(2026-08-31)
136
+
137
+ ```js
138
+ .groupBy(customStringGroup("地区",
139
+ group("一线", "北京", "上海"),
140
+ useOther("其他"))) // 文本自定义分组(useOther 可选)
141
+ .sum("回款金额").where(gt("回款金额", 1000)) // 指标条件:聚合前过滤
142
+
143
+ .groupBy(customNumberGroup("销售额",
144
+ range("低", 0, 1000), // [0, 1000) 闭开区间
145
+ range("高", 1000, null), // null = 无界
146
+ useOther("其他"))) // 数值自定义分组
147
+ .count("校数")
148
+
149
+ .groupBy(autoNumberGroup("销售额", 1000)) // 数值自动分组:等距 1000
150
+ .sum("回款金额")
151
+
152
+ .groupBy(dateGroup("签约时间", dateGroupType.MONTH)) // 日期分组粒度(dateGroupType 命名空间)
153
+ .sum("回款金额")
154
+ ```
155
+
156
+ | DSL API | 语义 |
157
+ |---|---|
158
+ | `customStringGroup(field, group(name, member...), useOther(name)?)` | 文本自定义分组:组名→成员值;未列出的值默认各自成组,`useOther` 指定时归入该组 |
159
+ | `customNumberGroup(field, range(name, min, max), useOther(name)?)` | 数值自定义分组:`[min, max)` 半开区间→组名,null = 无界 |
160
+ | `autoNumberGroup(field, interval)` | 数值自动分组:等距区间(区间数由引擎按数据范围决定) |
161
+ | `dateGroup(field, dateGroupType.X)` | 日期字段分组粒度:简单 `YEAR/QUARTER/MONTH/WEEKDAY/DAY/WEEK_COUNT/HOUR/MINUTE/SECOND` + 复合 `YQ/YM/YW/YMDH/YMDHM/YMDHMS/MONTH_DAY`(2026-09-01 支持;复合粒度落 `DimensionGroupDoubleValueBean{MORE_GROUP, childValue}`,引擎 `HelperForGroup` 实证) |
162
+ | `聚合().where(condition)` | 指标条件:对最近一次聚合设置聚合前过滤(同 filter 步骤条件函数) |
163
+
164
+ 约束:自定义分组/日期分组声明在 `groupBy(...)` 参数中携带(**同一 `groupBy(...)` 调用内**与普通字段名不可混用;混合维度用 `.by(普通字段)` 追加,特殊分组必须先于普通维度);自定义分组与日期分组不能混用(`groupBy` 重载互斥);`where` 必须紧跟聚合调用;`dateGroupType` 是命名空间前缀(枚举常量全局变量域隔离)。复合粒度(YQ/YM/YW/YMDH/YMDHM/YMDHMS/MONTH_DAY)与简单粒度同约束(DATE 字段、单字段单粒度)。
165
+
166
+ ## 7.2 左右合并(join)
167
+
168
+ ```js
169
+ .join("右表原始名", joinType.LEFT) // 连接类型:joinType.INNER/LEFT/RIGHT/OUTER
170
+ .on("订单ID", "订单ID") // 合并依据(可多对;左右类型一致)
171
+ .on(joinFormula("数学2倍", "${数学均分}*2", NUMBER), "金额") // 公式依据:左公式 vs 右字段
172
+ .renameResult("订单ID", "合同ID") // 结果字段改名(默认名:合并依据/左表字段 = 左字段名,右表字段 = 右表转义名)
173
+ ```
174
+
175
+ | DSL API | 语义 |
176
+ |---|---|
177
+ | `join(rightTableName, joinType.X)` | 左右合并入口:右表**只支持同主题内数据集**(原始名);结果字段不声明,由引擎推导——**抽取表**:合并依据去重成一个合并字段 + 左/右非依据字段;**直连表**:左全字段 + 右全字段(关联依据列全保留) |
178
+ | `on(leftField, rightField)` | 合并依据对:左 = 当前步骤字段名,右 = 右表字段转义名;同侧依据互不重复;任一侧或双侧可为 `joinFormula(...)` 公式键 |
179
+ | `joinFormula(name, formula, TYPE)` | 公式合并依据(只能用在 `on()` 参数位):左公式 `${字段名}` 引用当前步骤字段,右公式 `${转义名}` 引用右表字段;TYPE = `STRING/NUMBER/DATE/AUTO`(**DATE_TIME 拒绝**——平台公式生成类型无日期时间常量,声明即报错;AUTO 跳过左右类型匹配,引擎推断);抽取表左公式键产出以公式名命名的合并结果字段(公式键不消费同名真实字段,同名真实字段保留并优先命名),直连表公式仅参与匹配不产结果字段 |
180
+ | `renameResult(resultFieldName, newName)` | 结果字段改名(避开字段设置的 `rename`);旧名 = 推导默认名(公式键 = 公式名),**必须是真实结果字段(拼错报错,不会静默忽略)**;direct 左右同名依据字段时只改第一个(左表侧)——**只改右侧同名字段无法表达(反读 fail-loud)** |
181
+
182
+ ## 7.3 上下合并(union)
183
+
184
+ ```js
185
+ .union("表A原始名") // 每张表一次调用,可链式多张(同主题内)
186
+ .union("表B原始名")
187
+ .alignField("省份", "省") // 不同名对齐(同名+同类型自动对齐,无需声明)
188
+ .withFields("邮编") // 追加输出右表独有字段(默认输出 = 当前表字段)
189
+ .withAllFields() // 追加全部右表独有字段
190
+ .renameResult("省份", "地区") // 结果字段改名(默认名 = 当前表字段名/追加字段转义名)
191
+ ```
192
+
193
+ | DSL API | 语义 |
194
+ |---|---|
195
+ | `union(tableName)` | 上下合并入口:合并表**只支持同主题内数据集**(原始名);结果字段默认 = 当前表字段(对齐组名 = 当前表字段名) |
196
+ | `alignField(leftField, rightField)` | 不同名对齐(作用于最近 `union` 加入的表;类型一致校验) |
197
+ | `withFields(field...)` / `withAllFields()` | 追加输出本表独有字段(当前表无此字段) |
198
+ | `renameResult(resultFieldName, newName)` | 结果字段改名 |
199
+
200
+ 约束:`alignField/withFields/withAllFields` 作用于**最近一次 union 加入的表**;`alignField` 同一左字段对同一表只能声明一次;`withFields/withAllFields` 只接受当前表没有的字段名——右表与当前表**同名异类型**的字段无法声明追加,反读遇到该形态(平台可将其追加为独立列)fail-loud;**一个字段只进一列**(已被 alignField 对齐的字段不能再 withFields 追加、同表 withFields 不得重复声明同一字段);`dsl` 反读差分输出改名/对齐/追加差异,「取消自动对齐」与计算字段参与合并的表反读 fail-loud。
201
+
202
+ ## 7.4 列转行(toRows)
203
+
204
+ ```js
205
+ .toRows("数学均分", "AvgReading") // 依据字段(折叠为「列名列 + 值列」两列)
206
+ .column("列字段") // 列名列(显式声明)
207
+ .value("值字段") // 值列名(显式声明)
208
+ .sort(asc("列字段")) // 后续步骤引用声明名
209
+ ```
210
+
211
+ | DSL API | 语义 |
212
+ |---|---|
213
+ | `toRows(field...)` | 列转行(宽表转长表):把若干值列折叠为「列名列 + 值列」两列——列名列 = 原列名(String),值列 = 原列的值(类型 = 折叠字段类型合并:任一字符串→字符串、数值+日期混合→字符串、全数值→数值、日期族→日期) |
214
+ | `column(name)` / `value(name)` | 列名列/值列名(**必须紧跟 toRows 声明**;显式声明值与语言无关,round-trip 恒等——平台默认名是 i18n LocText 随环境渲染,省略声明无法跨语言判定) |
215
+
216
+ 业务含义:
217
+
218
+ - 结果字段 = 保留字段(未折叠)+ 列名列 + 值列;**列名列/值列名必填声明**(`column(...)`/`value(...)`,
219
+ 未声明报错;CLI 保存链路不走前端刷新默认名生成——空名落库 DATA_ERROR 真机实证)。
220
+ - 依据字段必须为当前步骤可用字段、互不重复;声明列名/值名与保留字段重名报错
221
+ (平台防重名改名后的名字 DSL 无法表达)。
222
+ - `analysis-ds dsl --id` 反读:恒输出 `.column(...).value(...)` 声明(Bean 名原样还原);
223
+ 空列名/值列名(API 构造)fail-loud。
224
+
225
+ ## 8.1 步骤字段改名(mapField,2026-09-01)
226
+
227
+ ```js
228
+ .filter(gt("合同金额", 1000))
229
+ .mapField("合同金额", "合同金额总额") // 步骤把来源字段改名为结果字段
230
+ .groupBy("合同类型")
231
+ .sum("合同金额总额") // 下游步骤引用结果名
232
+ ```
233
+
234
+ | DSL API | 语义 |
235
+ |---|---|
236
+ | `mapField(sourceFieldName, resultFieldName)` | 声明步骤 `sourceToResultFieldNameMap` 的改名条目:该步骤把来源字段改名为结果字段,**下游步骤引用结果名**(来源名对下游不再存在) |
237
+
238
+ 业务含义:
239
+
240
+ - 作用于**最近声明的步骤**(紧跟其后),仅 `filter`/`sort`/`addFormulaColumn`/`toRows`
241
+ 支持(其他步骤类型报错)。
242
+ - **改名时机按步骤类型分**(引擎算子顺序):
243
+ - `addFormulaColumn` 在公式列**之前**改名——公式引用**结果名**(典型场景:公式列名与
244
+ 现有字段撞名时,用 mapField 把现有字段让位改名,公式列占用原名);
245
+ - `filter`/`sort`/`toRows` 在步骤**之后**改名——步骤体引用**来源名**,改名对下游生效。
246
+ - 编译器按字段集演化校验引用名,写错名报错并附可用字段列表。
247
+ - 只声明非恒等条目(来源==结果为平台默认);`analysis-ds dsl --id` 反读输出全部
248
+ 非恒等条目,round-trip 恒成立。
249
+
250
+ ## 8. 别名声明(可选)
251
+
252
+ ```js
253
+ .resultName("数学均分", "AvgMath")
254
+ ```
255
+
256
+ | DSL API | 语义 |
257
+ |---|---|
258
+ | `resultName(resultFieldName, identifyName)` | 声明一个结果字段的对外 identify name(别名层) |
259
+
260
+ 业务含义:
261
+
262
+ - **可选**:只在需要时声明,未声明时别名层完全交给平台维护。
263
+ - `resultFieldName` 必须是**最后一步结果字段名**(同时是该字段转义名);`identifyName` 是自助数据集对外字段名(组件按 identify name 引用,别名存在时 rename 该字段不影响引用旧别名的组件)。
264
+ - 只声明需要别名的字段(恒等条目 = 平台默认,声明会被拒绝);表级元信息,不构成步骤,链上任意位置可写。
265
+ - `analysis-ds dsl --id` 反读会把 Bean 的别名差异(value≠key 条目)原样输出为 `.resultName(...)` 声明,写回即幂等保留。
266
+
267
+ ## 8.1 步骤公共属性(所有步骤通用,2026-09-01)
268
+
269
+ ```js
270
+ .filter(eq("County", "Alameda"))
271
+ .stepName("加州过滤") // customName:步骤自定义名
272
+ .comment("只保留阿拉米达县") // comment:步骤注释
273
+ .disable() // disable:禁用步骤(引擎运行时跳过)
274
+
275
+ // 子上下文内可用(不打断聚合/on 链,作用于该步骤)
276
+ .groupBy("County")
277
+ .sum("AvgMath")
278
+ .stepName("分组汇总")
279
+ .count("校数") // 仍在同一 groupBy 内
280
+ ```
281
+
282
+ | DSL API | 语义 |
283
+ |---|---|
284
+ | `stepName(name)` | 步骤自定义名(`AbstractOperatorBean.customName`,前端历史面板重命名);作用于最近声明的步骤 |
285
+ | `comment(text)` | 步骤注释(`comment`);作用于最近声明的步骤 |
286
+ | `disable()` | 禁用步骤(`disable=true`)——引擎运行时跳过该步骤、字段集透传上一步;作用于最近声明的步骤 |
287
+
288
+ 业务含义:
289
+
290
+ - 修饰语句**紧跟步骤声明之后**(中间隔了其他步骤声明则作用于后者——目标是「最近声明的步骤」)。
291
+ - **第一步(from/select)不可修饰**(fx SourceStep 同语义:数据源步骤不可禁用);无任何步骤前调用报错。
292
+ - 反读(`analysis-ds dsl --id`)仅差异输出:customName/comment 非空、disable=true 才输出对应修饰语句。
293
+ - 禁用步骤的产物字段仍可在后续步骤引用(声明完整 Bean,保存成功)——引擎执行时禁用步骤被跳过,预览/出数报错由引擎承担(与前端禁用后行为一致)。
294
+
295
+ ## 9. 完整示例
296
+
297
+ ```js
298
+ AnalysisTableDsl.create("订单宽表")
299
+ .from("California Schools")
300
+ .select("County", "AvgMath", "AvgReading")
301
+ .filter(and(eq("County", "Alameda"), notNull("AvgMath")))
302
+ .stepName("加州过滤")
303
+ .rename("AvgMath", "数学均分")
304
+ .addFormulaColumn("分差", "${数学均分}-${AvgReading}", NUMBER)
305
+ .sort(desc("分差"))
306
+ .groupBy(customStringGroup("County",
307
+ group("湾区", "Alameda", "San Francisco"),
308
+ useOther("其他")))
309
+ .sum("分差").where(gt("分差", 100))
310
+ .count("校数")
311
+ .join("订单明细", joinType.LEFT) // 右表 = 同主题内数据集
312
+ .on("County", "地区")
313
+ .renameResult("County", "县区")
314
+ .union("城市表") // 合并表 = 同主题内数据集
315
+ .alignField("County", "省")
316
+ .withAllFields()
317
+ .resultName("数学均分", "AvgMath")
318
+ .build();
319
+ ```
320
+
321
+ 字段引用一律使用**当前步骤输入字段名**(如已重命名则用新名);`analysis-ds dsl --id` 反读输出
322
+ 与本语句面一致,可「读取 → 修改 → `update` 写回」。
@@ -0,0 +1,29 @@
1
+ # 制品来源与生成顺序
2
+
3
+ ## 1. 组件来源
4
+
5
+ - `requestedWidgets` 中的组件需要生成独立 Widget DSL 文件。
6
+ - `existingWidgets` 仅作为仪表板引用证据,不生成或覆盖其 DSL 文件。
7
+ - `widgetRefs[].source: REQUESTED` 必须引用 `requestedWidgets`。
8
+ - `widgetRefs[].source: EXISTING` 必须引用 `existingWidgets`。
9
+
10
+ 引用来源不明确、名称重复或 key 无法唯一解析时返回 `BLOCKED`。
11
+
12
+ ## 2. 文件生成顺序
13
+
14
+ 按以下顺序写入:
15
+
16
+ 1. 校验完整交接对象和文件映射。
17
+ 2. 为每个 `requestedWidgets[]` 生成一个 Widget DSL 文件。
18
+ 3. 重新读取并自检全部 Widget 文件。
19
+ 4. 当场景为 `CREATE_DASHBOARD` 时生成 Dashboard DSL 文件。
20
+ 5. 重新读取并自检 Dashboard 文件。
21
+
22
+ Dashboard DSL 只通过 `.reference("组件名")` 挂载组件。即使组件属于本次请求,也不把 Widget DSL 内联到 Dashboard 文件。
23
+
24
+ ## 3. 失败处理
25
+
26
+ - 任一 Widget 无法映射时停止,不生成 Dashboard。
27
+ - 已写入文件若随后发现自检失败,只修正文件映射中对应的目标文件;不创建替代文件。
28
+ - 不删除、创建或修改 FineBI 服务端资源。
29
+ - 不负责决定 CLI 写入顺序、确认计划或失败清理,这些属于 Plan Skill。
@@ -0,0 +1,199 @@
1
+ # 命令工作流
2
+
3
+ ## 1. 使用规则
4
+
5
+ 本文件记录命令工作流约定,不限制命令名称或固定命令数量。当前连续任务首次使用某个命令时须读取实时 capability 和目标命令 help;只读及非删除操作在连接、缓存指纹、目标命令和 schema 未变化且缓存未过期时可以复用。确认后的删除操作仍须实时复检;矩阵不替代运行时 schema。
6
+
7
+ 多环境任务先通过 `auth list` 确认用户指定的环境名称,后续 CLI 调用统一携带 `--profile <name>`;runner 的 `inspect` / `run` 同样支持该参数,并将同一环境传给能力检查、help 和执行进程。也可为当前进程链设置 `FINEBI_PROFILE`,显式参数优先。普通业务任务不执行 `auth use` 改变机器默认环境,未知名称不回退。新环境用 `auth login --profile <name> --base-url <url> --no-use` 保存并保留默认值;登录方式、删除与注销的具体契约以实时内置 help 为准。
8
+
9
+ 命令在文档中写作 `resource.command`,JSON `argv` 中必须拆成独立参数,例如:
10
+
11
+ ```json
12
+ ["finebi-cli", "subject", "search", "分析主题", "--json"]
13
+ ```
14
+
15
+ 搜索结果存在分页时必须遍历完整结果。结果存在多个合理候选时,展示名称、ID 和归属信息,不得静默选择。
16
+
17
+ ## 2. 当前命令矩阵
18
+
19
+ 以下矩阵仅列出常见资源命令示例;实际可执行范围以实时 capability 为准。
20
+
21
+ | 资源 | 只读(`mutation:false`) | 非只读(`mutation:true`) |
22
+ |---|---|---|
23
+ | `analysis-ds` | `dsl`†, `info`, `operator-schema`†, `preview`, `search` | `add`, `update`†, `data-update`, `delete`, `publish` |
24
+ | `calculated-field` | `context`, `dsl`, `info`, `validate` | `add`, `delete`, `update` |
25
+ | `dashboard` | `search`, `info`, `preview` | `add`, `delete`, `update` |
26
+ | `data-connection` | `search`, `tables` | — |
27
+ | `model` | `info`, `search` | `add`, `delete` |
28
+ | `metric-condition` | `dsl` | `update` |
29
+ | `measure` | `info`, `search` | `copy`, `transform`, `delete` |
30
+ | `geographic-role` | `info`、`dsl`、`catalog`、`match`(显式输入值) | `set`、`clear` |
31
+ | `drill-directory` | `dsl` | `update` |
32
+ | `public-ds` | `definition`, `info`, `preview`, `search`, `source-preview` | `add`, `data-update`, `update` |
33
+ | `public-ds-folder` | `search` | `add` |
34
+ | `relationship` | `info` | `add`, `delete` |
35
+ | `subject` | `info`, `search` | `add`, `data-update`, `delete` |
36
+ | `widget` | `data`, `info`, `preview`, `search` | `add`, `delete`, `update` |
37
+
38
+ `geographic-role match` 的 `--values` 使用单个 JSON 数组参数,如 `'["上海","江苏"]'`;最多 200 个值,不代表整列扫描。`catalog` 按区域路径去重后分页。
39
+
40
+ `measure` 管理主题 measure 池记录。组件命令需要主题侧记录时才引用它:把**物理数值字段当维度/分类/粒度**用时,池内必须先有该字段的已注册副本,缺副本时 `widget add/update/data` 报 `UNSUPPORTED_FEATURE` 并在 `nextActions` 给出 `measure copy --subject-id <S> --field-id <F> --as dimension`——照该命令注册后重跑组件命令即可,不得改写 DSL 规避。`measure transform --subject-id <S> --field-id <F> --as dimension` 原地把计算字段等记录转成维度形态(字段 ID 不变);`measure delete` 只清理无人引用的记录,被组件引用时返回 `DEPENDENCY_CONFLICT` 与引用清单。写命令前用 `measure search/info` 确认目标记录的 `kind`、`role` 与 `source`;DSL `measureID("<配置 ID>")` 的配置 ID 取 `measure search/info` 响应条目的 `measureId` 字段——条目的 `id` 是实际字段 ID(作 `--field-id` / `--id` 入参),填进 `measureID()` 会解析失败。
41
+
42
+ 转成**指标形态**的文本/日期字段(`--as measure`:100/101/106/107)用 `from("表").countDistinct("字段"[, "别名"])`(已保存计算字段用 `measureID("配置 ID").countDistinct()`)供组件去重计数;组件命令要求该字段已在池内转成指标形态,缺转换时报 `UNSUPPORTED_FEATURE` 并在 `nextActions` 给出 `measure transform --subject-id <S> --field-id <F> --as measure`——照该命令转换后重跑即可。计算字段变体(106/107)的公式引用聚合形态字段(转指标/转日期/带聚合的计算字段变体)时同样报 `UNSUPPORTED_FEATURE`(平台取数校验会把公式再包一层去重计数而报聚合嵌套聚合,`message` 列出被引用字段)。转换是**跨组件生效**的:`measure transform` 不改写引用方,响应里的 `affectedResources` / `referencingCalculatedFields` 列出受影响的组件、仪表板、文档与池内公式引用,存量组件下次取数才报错——按这份清单先摘引用再转换。MAX/AVG 等按表字段类型校验仍拒绝文本/日期,数值字段的 `--as dimension` / `--as measure` 两个方向都可被组件消费。业务模型字段不写主题池(随组件 Bean 携带),`measure transform` 对模型字段 ID 报 `RESOURCE_NOT_FOUND`,模型字段的维度/指标用法直接按模型 DSL 编写。
43
+
44
+ 命令名为 `template` 的命令不再硬性排除,按实时 help/schema 和命令名确认规则执行。
45
+
46
+ †:`analysis-ds` 的 `dsl` / `operator-schema` / `update` 与步骤链脚本能力由服务器内部试用开关控制,
47
+ 正式版默认不存在(同时 `add` 仅支持原样引用创建,`template` 仅 `basic` 场景)。以 `capability refresh`
48
+ 后的实际清单为准:命令不存在时按收窄后的能力面规划,不要规划「读取 → 修改 → 写回」改表工作流,也不得重试。
49
+
50
+ ## 3. 资源搜索优先级
51
+
52
+ 根据用户提供的表名解析资源时,必须按以下层级执行:
53
+
54
+ 1. 使用完整表名搜索 `analysis-ds` 和 `public-ds`,遍历全部分页,并以返回资源的真实名称全等作为精确匹配依据。唯一精确匹配出现后视为来源已解析,不再扩大搜索范围。
55
+ 2. 没有精确匹配时,提取保留业务含义的关键词再次搜索数据集,并结合字段、位置、主题归属、资源类型、引擎类型和用户意图判断候选。
56
+ 3. 多个候选均合理时展示名称、ID、归属和匹配证据,要求用户选择;不得按结果顺序静默选定。
57
+ 4. 已解析来源为 `public-ds`、但下游只接受 `analysis-ds` 时,使用该公共数据集创建目标主题内的分析表,记录 `analysis-ds.add` 返回的全部实际 ID,并逐一通过 `analysis-ds.info` 和必要的 `preview` 回查。转换成功后只向下游传递实际分析表资源;不得把公共数据集 ID 当作分析表 ID,也不得继续查询数据连接。
58
+ 5. 只有分析表和公共数据集都没有合理候选时,才使用 `data-connection.search` 和 `data-connection.tables` 查询物理表。物理表不能直接作为数据集 ID,后续仍需创建并验证所需数据集资源。
59
+
60
+ ## 4. 数据集引擎语义
61
+
62
+ 解析数据集后先通过 `info` 确认引擎类型,再解释状态字段:
63
+
64
+ - `DIRECT`:保留 ID、名称、字段、类型、归属和唯一性校验;忽略用于抽取更新门禁的 `dataStatus`、`updateStatus` 及摘要 `usable`,以只读 `preview` 验证实际查询能力;不得因这些状态规划或执行 `analysis-ds.data-update` 或 `public-ds.data-update`。
65
+ - `SPIDER`:校验更新状态与可用性,并结合只读 `preview` 判断是否能够被后续资源消费;确需更新时才按运行时命令能力规划 `data-update`。
66
+ - 引擎类型无法确认、字段缺失、归属错误、资源歧义或 preview 失败时进入 `BLOCKED`,不得用猜测补齐。
67
+
68
+ ## 5. 只读命令选择
69
+
70
+ - `search`:定位候选资源;在宣称唯一前遍历全部分页。
71
+ - `info`:确认准确 ID、名称、归属、状态和字段/关系元数据。
72
+ - `preview`:预览已有资源(`--id`),不代表写入。
73
+ - `widget.data`:组件取数,用于数据核对与探查。`--id` 核对已保存组件的数据;`--subject-id` + `--file` 用临时 DSL 探查未落地的组件形态,不创建组件。与 `widget preview` 都返回字段与行数据,分工在:`preview` 附组件类型与后端摘要、仅限已保存组件;`widget.data` 可控取数范围(`--page`/`--page-size` 按页取数、`--all-data`/`--expand-all` 全量/展开档位),适合逐行核对与大数据量的分页检查。范围参数依赖平台构件提供取数范围 API:旧构件回 `UNSUPPORTED_FEATURE`,改用默认全量取数。
74
+ - `definition`:回读公共数据集 Definition,常用于 update 前审查或恢复设计。
75
+ - `source-preview`:验证公共数据集 JSON Definition 的源数据效果。
76
+ - `operator-schema`:返回分析数据集 DSL 语句面(可写步骤类型 + 表级声明 + 签名 + 约束;`--type` 过滤单个类型)。
77
+ - `validate`:编译并验证计算字段 JavaScript,不执行保存。
78
+ - `data-connection.tables`:仅在数据集层没有合理候选后,列出指定数据连接下的表。
79
+ - `model.search`:在已确认主题内定位当前用户可查询的模型指标集;`model.info`:展开选定模型的指标、数据集字段和模型计算字段目录;`model.add`:将 `public-ds.search` 中已确认的 `type=METRIC_SET` 来源 ID 挂入可编辑主题,返回的主题内模型 ID 才可用于 `model.info/delete`;`model.delete`:移除该主题内模型并保留来源指标集。
80
+ - `metric-condition.dsl`:按 `--field-id` 回读主题共享指标条件的可编辑 `MetricConditionModel` DSL;字段无条件时返回 `clear()` 形态,写回即幂等。
81
+ - `public-ds-folder.search`:定位公共数据目录。
82
+ - `relationship.info`:确认当前关系;执行 add/delete 前必须调用。
83
+
84
+ 只读和非删除命令可以自动执行,但仍须先确认命令存在于实时 capability,并完整读取 help/schema;`delete` 命令必须进入人工确认流程。
85
+
86
+ ## 6. 文件输入
87
+
88
+ | 文件类型 | 命令 |
89
+ |---|---|
90
+ | JSON | `public-ds.add`, `public-ds.source-preview`, `public-ds.update` |
91
+ | JavaScript 文本 | `analysis-ds.add`, `analysis-ds.update`, `calculated-field.validate`, `calculated-field.add`, `calculated-field.update`, `widget.add`, `widget.update`, `dashboard.add`, `dashboard.update` |
92
+ | Model DSL 文本 | `metric-condition.update`, `drill-directory.update` |
93
+
94
+ 以命令 help 返回的实际参数名为准。只读文件命令只需验证规范路径、存在性和格式;非只读文件命令还必须在冻结计划和执行前复检中记录 SHA-256,删除命令的摘要同时纳入人工确认。
95
+
96
+ ## 7. 资源工作流
97
+
98
+ ### 7.1 Analysis DS
99
+
100
+ **能力门禁**:本节的步骤链与 `update`/`dsl`/`operator-schema` 工作流仅在能力清单包含这些命令时适用
101
+ (内部试用开关开启的服务器);正式版默认态下 `add` 仅支持原样引用创建(create/from/fromExcel),
102
+ `template` 仅 `basic` 场景,不得规划改表工作流(见第 2 节 † 注)。
103
+
104
+ 创建与步骤覆盖使用 AnalysisTableDsl JS 文件:`add --subject-id <id> --file <x.js>` 创建新表(DSL `from(表原始名)` 声明来源,原始名= search 返回的 `id`,全局唯一;Excel 源用 `fromExcel(attachId, "Sheet1", ...)`(Sheet 名逐个传字符串),attachId 来自 `upload-excel`),`update --id <id> --file <x.js>` 覆盖步骤链(来源不变,DSL `from` 必须匹配现有来源)。创建后使用响应中的实际 `id`/`ids` 逐一调用 `info`,失败清理必须覆盖 `ids` 中的全部实际 ID。`dsl --id <id>` 反读已保存表的可编辑 DSL 文本(Bean→DSL),适用于「读取 → 修改 → 写回」;写回用 `update`。**影响面透出(2026-08-31)**:`update` 响应带 `changedFields`(保存前后字段差分,rename 以旧名消失 + 新名出现表达);`dsl` 反读响应带 `resultToIdentifyNameMap`(结果字段名 ≠ identify name 的别名差异条目,组件引用按 identify name 解析)。**别名保留(2026-08-31)**:DSL 可选 `resultName(resultFieldName, identifyName)` 声明(key=最后一步结果字段名=字段转义名,value=对外 identify name;未声明完全交给平台);`dsl` 反读把别名差异输出为 `.resultName(...)` 声明,写回即幂等——rename 涉及被组件引用的字段时,声明 `resultName("新名", "旧名")` 保留 identify name,组件不失效。改表工作流:`dsl` 看别名层(评估 rename 风险)→ `update` → 看 `changedFields` → 需要保留别名时在 DSL 声明 `resultName(...)` → `verify-data` 验证。AnalysisTableDsl 语句面见 handoff-dsl-writer 的 `analysis-dsl-api.md`(八类步骤 + 可选 resultName 声明:来源/过滤/排序/字段设置/公式列/分组汇总/左右合并/上下合并;`template --scenario <basic|formula|group|full>` 给可替换模板)。**合并步骤(2026-08-31)**:`join("右表原始名", joinType.X).on(左字段, 右字段).renameResult(旧名, 新名)` 左右合并、`union("表A").union("表B").alignField(左, 右).withFields(...).withAllFields().renameResult(...)` 上下合并——右表/合并表**只支持同主题内数据集**(用 `analysis-ds.search` 确认表在当前主题);结果字段由引擎推导(抽取表合并依据去重成一个字段、直连表全保留);改名语句是 `renameResult`(避让字段设置的 `rename`)。先按第 4 节区分引擎类型;DIRECT 数据集不得因更新状态或摘要 `usable` 规划、执行 `data-update`。SPIDER 的 `data-update` 可能触发数据重算,不能视为可逆。`delete` 必须独立确认。
105
+
106
+ 主题共享钻取目录:`drill-directory.update --subject-id <id> --file <directory.js>` 使用 `DrillDirectoryModel.builder("目录名").levels([from("表").dim("省"), from("表").dim("市")]).build()` 创建,用 `.dissolve()` 解散。`drill-directory.dsl --subject-id <id> --name <目录名>` 回读;目录按主题内精确名称定位。解散保留原字段,操作影响主题共享字段面板;目录重名且层级不同、字段已属于其他目录时拒绝写入,不能隐式覆盖。
107
+
108
+ ### 7.2 Calculated Field
109
+
110
+ 生成或修改公式前,必须调用 `formula functions --subject-id <目标主题ID>`(可带 `--query` / `--name`),核对 `context.subjectId` 和 `profiles`,按该面板选择函数。标准主题应返回 `direct-standard`;通用目录和其他主题的结果不能代替本次查询。同一任务、同一连接和主题且 profile 未变化时可复用;切换连接或主题后重新查询,新增函数时补查。面板查询失败先解决失败原因;未收录函数不等于引擎绝对不支持,优先选择面板提供的写法,最终合法性由目标主题原生校验判断。
111
+
112
+ 每个任务首次处理计算字段时先刷新 capability,确认实时命令只有 `info/dsl/context/validate/add/update/delete`。调用 `context --subject-id <id>` 获取 `dslSource`、字段目录和引用规则,基于它生成 `CalculatedFieldModel` JavaScript;同一份 `.js` 先交给 `validate --subject-id <id> --file <x.js>`,通过后再交给 `add`,或带原 `id` 交给 `update`。公共计算字段命令不接收 JSON Definition,也不存在 `add-dsl/update-dsl`。
113
+
114
+ 需要审阅或修改已保存字段时先调用只读 `dsl`,以回读的 JavaScript 为基础编辑。创建或更新后调用 `info` 回查;更新还应再次调用 `dsl` 核对实际脚本。update 不能通过 delete 安全回滚到旧状态;只有完整保存并可验证旧 JavaScript 时才允许提出恢复方案。
115
+
116
+ ### 7.3 Dashboard 与 Widget
117
+
118
+ 仅知道仪表板名称时,用 `dashboard search "关键词"` 搜索「我的分析」和发布目录中有预览权限的仪表板;结果按 ID 合并并通过 `locations` 保留命中的来源。目录挂载的是主题,只有发布清单中的仪表板参与搜索,匹配仪表板名而非挂载主题名。用名称、主题、目录位置消歧;仅目录来源须保留 `id/entryId/publishId`,不能据此假定可使用原主题的 `info/dsl/preview` 或写命令,也不能自行拼接预览 URL。
119
+
120
+ 组件名称使用 `widget search "关键词"`,只搜索「我的分析」。两类搜索均支持省略关键词分页枚举,`--limit` 默认 100(1–500),`--offset` 默认 0 且必须为 limit 的整数倍,遍历 `pagination.hasNext/nextOffset`。仪表板的 total 和分页以合并后的结果为准。已知主题时用 `subject info` 枚举主题内资源,搜索命令不接受 `--subject-id`。搜索失败不能解释为没有资源。
121
+
122
+ `add`/`update` 仅消费已经验证的 JS 文件。涉及自然语言组件或仪表板设计时遵循 `scenario-workflows.md` 和 `handoff-contract.md`。创建后用响应实际 ID 调用 `info`,必要时调用 `preview`;需要核对取数(逐行、分页或展开档位)时用 `widget data`(见第 5 节)。Dashboard 引用新 Widget 时,先创建并回查 Widget,再进入 Dashboard 阶段。
123
+
124
+ `widget template` 的 `basic` 支持 `seriesLine`、`ROSE`、`MULTI_PIE`。`seriesLine` 使用 `dimensionField` 分类轴、指标求和折线和 `seriesField` 颜色;`ROSE` 使用 `dimensionField` 分类,数值字段 `measureField` 求和后同时绑定角度与半径;`MULTI_PIE` 使用 `dimensionField` 颜色、指标求和大小和必填 `granularityField` 粒度,不配置 x/y 或 tooltip。公共变量为 `widgetName`、`tableName`、数值字段 `measureField`,变量按 JSON 字面量替换。
125
+
126
+ `LINE basic` 使用 `dateField`(DATE/DATE_TIME,默认年月日)和两个不同的数值字段 `measureField1`、`measureField2`,分别以 `sum` 绘制上下分区折线。多指标自动按指标名称着色并生成图例;颜色由转换器绑定到各指标图形,不在“全部”卡片显式绑定。`widgetName`、`tableName` 为公共变量;仍编译为 `sliceLine`,既有输入别名保持兼容。
127
+
128
+ **读取已有资源的 DSL 形态**:`widget dsl --id <widgetId>` / `dashboard dsl --id <dashboardId>` 把平台 Bean 反向翻译为可编辑的 DSL 文本。改板先 `dashboard info --id <dashboardId>` 清点条目与板级留痕(轻量),定位到目标后再 dsl 回读。只看一个条目时用单条目切片 `dashboard dsl --id <dashboardId> --entry <名>`(重名板用 `--entry-id <entries[].entryId>` 消歧),`warnings`/组件回显同步过滤到目标条目。`dashboard dsl` 的响应附带所引用数据组件的 Widget DSL 回显,便于一次读取;仪表板 DSL 本身只引用已有组件,附带回显不代表 `dashboard update` 会保存组件定义。
129
+
130
+ **按修改对象选择写入命令**:
131
+
132
+ - 仅修改数据组件的字段、指标、聚合或图表配置时,从上述回显或 `widget dsl` 取得目标组件的完整 DSL,保留无关配置,修改后用 `widget update --id <widgetId> --file <widget.js>` 写回原组件。例如把“销售额”改成“利润”,只更新对应 Widget;仪表板布局和引用不变时无需执行 `dashboard update`。写后回查组件配置与数据,并核对仪表板中的目标组件。
133
+ - 仅修改仪表板上一个条目(控件 / 文本 / 图片 / web / 复合过滤 / 组件引用条目的板侧面)时,用单条目写:`dashboard update-entry --id <dashboardId> --file <fragment.js>`(fragment 与 `dsl --entry` 切片同文法,是条目的完整新定义——切片读全条 → 改 → 写回;重名板带 `--entry-id` 消歧)。未点名条目原样保留;省略 `position` 沿用原槽位。新增 / 删除条目用 `dashboard add-entry` / `remove-entry`(被控件圈定或联动的条目删除会被拒,按错误信息先改引用);`add-entry` 的 fragment 无切片可抄、需手写——单条目语句 + 可选 `position` 行,不包整板脚本,缺省 position 落首个空位。并发编辑带 `--expected-update-time`(上次写响应的 `lastUpdateTime`),不匹配报 `VERSION_CONFLICT`,重读后重试。
134
+ - 修改仪表板布局、组件引用或板级配置时,保留完整仪表板 DSL 中的其他条目和配置,用 `dashboard update --id <dashboardId> --file <dashboard.js>` 写回;不能用只含目标条目的局部片段覆盖整板。若同时修改数据组件,先完成 `widget update` 并回查,再执行必要的仪表板更新。`dashboard update` 不接受内联数据组件定义,不能代替 `widget update` 修改组件字段。
135
+
136
+ DSL 语句面与 `widget template` / `dashboard template` 一致(见 handoff-dsl-writer 的 api 文档);主题模型字段会按作用域保持根级指标、数据集字段或模型计算字段路径,该回显可作为模型名/字段名证据。回读含 warnings 字段(Bean 上文本 DSL 无法表达、被丢弃的配置会带出登记),改动前应审视 warnings,避免把不可达配置覆盖丢失——条目级 `*_DROPPED` 留痕的条目整板 `dashboard update` 会被写侧防丢守卫以 `ENTRY_LOSS_BLOCKED` 拒绝(处置口径见 handoff-dsl-writer 的 dashboard-dsl-api)。
137
+
138
+ ### 7.4 Data Connection
139
+
140
+ 只有第 3 节的数据集搜索层没有合理候选时,才用 `search` 枚举可用连接并用 `tables` 查询连接中的表。两者均为只读,但不得把连接凭证写入输出。
141
+
142
+ ### 7.5 Public DS
143
+
144
+ `add`/`update` 前使用 `source-preview` 验证 JSON Definition;更新已有资源前再调用 `definition` 保存可审查的旧状态。先按第 4 节区分引擎类型;DIRECT 数据集不得因更新状态或摘要 `usable` 规划、执行 `data-update`。写响应可能部分成功,必须逐项检查并使用 `info`、`definition` 或 `preview` 回查。当前运行时没有 `public-ds.delete`,因此 `add` 不具备 CLI 删除清理能力,只能单独执行或放在批次最后。
145
+
146
+ ### 7.6 Model Metric Set
147
+
148
+ 先用 `subject.info` 或 `model.search` 确认目标主题中的既有模型。需要将公共来源指标集加入主题时,先用 `public-ds.search` 确认唯一的 `type=METRIC_SET` 候选和来源 ID,再确认目标主题可编辑;`model.add --subject-id <subjectId> --id <sourceId>` 成功后必须使用响应中的主题内模型 ID 调用 `model.info` 回查名称、归属与字段目录。来源 ID 不能用于 `model.info` 或 `model.delete`,也不能写入 DSL。
149
+
150
+ `model.delete` 前重新用 `model.search` 或 `model.info` 确认主题内模型 ID、名称和归属。它只移除主题关联,保留来源指标集;因此必须按删除确认流程执行。模型字段生成 DSL 时,根级指标使用 `model(name).metrics()`,模型表字段使用 `model(name).dataset(datasetName)`,模型计算字段使用 `model(name).calculated()`;模型根级维度和记录数不支持。
151
+
152
+ ### 7.7 Relationship
153
+
154
+ `add` 是新增或替换语义。执行前用 `info` 判断关系是否已存在:不存在时可把 `delete` 作为创建后的清理命令;已存在时必须保存完整旧关系并以 `relationship.add` 作为恢复方案,不能把 `delete` 当作回滚。`delete` 前也要用 `info` 确认精确关系。
155
+
156
+ ### 7.8 Subject
157
+
158
+ 创建前用 `search` 排除名称/路径冲突;任务包含数据来源时,先解析来源并按第 4 节确认引擎类型,再选择主题类型:
159
+
160
+ - 默认创建标准分析主题,显式传入 `analysisType=DIRECT_STANDARD`。
161
+ - 用户要求使用抽取类型数据,或已选来源为 `SPIDER` 时,创建自助分析主题,显式传入 `analysisType=DEFAULT`。
162
+ - 用户明确指定主题类型时尊重其选择;若与已确认的数据来源不兼容,说明冲突并请用户选择后再创建。复用已有主题时先用 `info` 核对类型与来源兼容性。
163
+
164
+ 执行 `subject add` 前通过实时 help 确认 `analysisType` 对应的 CLI 参数名和枚举值,并显式传参;省略参数的服务端默认值不能代替上述选择。创建后使用响应实际 ID 调用 `info`,核对名称、位置及 `analysisType` 与计划一致。`data-update` 不具备通用逆操作。`delete` 必须独立确认,并在计划中列出其包含资源影响。
165
+
166
+ ### 7.9 Metric Condition
167
+
168
+ 主题共享指标条件是挂在原始数值字段或模型指标上的聚合前过滤,保存后**影响主题内所有使用该指标的组件**;向用户确认时必须透出这一影响面。工作流:`metric-condition.dsl` 回读当前条件 → 与用户确认期望条件 → `metric-condition.update` 提交 DSL 文件 → 再次 `metric-condition.dsl` 验证。`update` 是整份替换:请求 DSL 的 `where(...)` 列表就是保存后的完整条件,修改前必须先回读完整现状;清空用显式 `clear()`。字段定位必须使用实际字段 ID(`analysis-ds info`、`model info` 或 `metric-condition.update` 响应返回),展示名不进命令。计算字段、记录数、带分组/别名/快速计算的引用和跨引擎条件字段会被拒绝,条件固定 AND 合并;保存后服务端回读比对不一致时返回 `UPDATE_FAILED`,按失败处理而不是当作已生效。
169
+
170
+ ## 8. 写后验证与清理映射
171
+
172
+ | 写命令 | 首选只读验证 | 失败后清理能力 |
173
+ |---|---|---|
174
+ | `analysis-ds.add` | `analysis-ds.info` | `DELETE`(覆盖全部返回 `ids`) |
175
+ | `analysis-ds.update` | `analysis-ds.info` / `dsl` | `RESTORE_ONLY`(仅在旧 DSL 可完整恢复时) |
176
+ | `analysis-ds.data-update` | `analysis-ds.info` / `preview` | `NONE` |
177
+ | `analysis-ds.delete` | `analysis-ds.info` 应不存在 | `NONE` |
178
+ | `calculated-field.add` | `calculated-field.info` | `calculated-field.delete` |
179
+ | `calculated-field.update` | `calculated-field.info` / `dsl` | `RESTORE_ONLY`(仅在旧 JavaScript 可完整恢复时) |
180
+ | `calculated-field.delete` | `calculated-field.info` 应不存在 | `NONE` |
181
+ | `dashboard.add` | `dashboard.dsl` | `dashboard.delete` |
182
+ | `dashboard.update` | `dashboard.dsl` / `preview` | `RESTORE_ONLY`(仅有已验证旧 JS 时) |
183
+ | `dashboard.delete` | `dashboard.dsl` 应不存在 | `NONE` |
184
+ | `model.add` | `model.info` | `model.delete` |
185
+ | `model.delete` | `model.info` 应不存在 | `NONE` |
186
+ | `metric-condition.update` | `metric-condition.dsl` | `RESTORE_ONLY`(仅旧 DSL 可完整恢复时) |
187
+ | `public-ds.add` | `public-ds.info` / `definition` | `NONE`(运行时无 delete) |
188
+ | `public-ds.update` | `public-ds.info` / `definition` | `RESTORE_ONLY`(仅有完整旧 Definition 时) |
189
+ | `public-ds.data-update` | `public-ds.info` / `preview` | `NONE` |
190
+ | `relationship.add` | `relationship.info` | 新增关系为 `DELETE`;替换关系为 `RESTORE_ONLY` |
191
+ | `relationship.delete` | `relationship.info` 应不存在 | `RESTORE_ONLY`(仅保存完整旧关系时) |
192
+ | `subject.add` | `subject.info` | `subject.delete` |
193
+ | `subject.data-update` | `subject.info` | `NONE` |
194
+ | `subject.delete` | `subject.info` 应不存在 | `NONE` |
195
+ | `widget.add` | `widget.info` | `widget.delete` |
196
+ | `widget.update` | `widget.info` / `preview` | `RESTORE_ONLY`(仅有已验证旧 JS 时) |
197
+ | `widget.delete` | `widget.info` 应不存在 | `NONE` |
198
+
199
+ 恢复命令不是删除命令,必须在计划中明确区分。不得为了声称“可回滚”而删除原本就存在、仅被更新的资源。