@modesto_ma/dsh-guanxiang 0.2.0-beta.1 → 0.2.0-beta.2

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.
package/README.md CHANGED
@@ -117,6 +117,9 @@ npm 发行包已包含构建产物;本地 `link:` 安装前必须先执行 `pn
117
117
  skillUpdateAuthor: # 可选:SKILL 改进提交的作者;省略时使用本机 git 的 user.name / user.email
118
118
  name: 观象
119
119
  email: guanxiang@example.com
120
+ analyze: # 日志平台分析接口,见下文
121
+ enabled: false # 默认关闭
122
+ analyzeTokenRef: GUANXIANG_ANALYZE_TOKEN # 接口访问令牌在 DSH 凭据库中的名称
120
123
  ```
121
124
 
122
125
  配置在加载时校验,不合法时插件加载失败。git 地址支持 https/http/ssh/git/file URL、`user@host:path` 和绝对路径,拉取时使用本机已有的 git 凭据(SSH key 或 credential helper),不会弹出交互式输入。
@@ -145,6 +148,7 @@ DSH Web 左侧栏的「观象」入口打开整页设置面板:
145
148
  | 应用仓库 | git 地址列表(分支可选)、每个仓库的拉取状态、提交号、应用描述与不可用原因,单个或全部重新拉取 |
146
149
  | 访问令牌 | 填 GitLab 地址、令牌别名和令牌三项即可添加;用户名固定为 oauth2,凭据库键名自动分配(`GUANXIANG_GITLAB_TOKEN`、`GUANXIANG_GITLAB_TOKEN_2`……)。每行显示令牌是否已设置及来源,可替换或清除 |
147
150
  | 日志下载 | 允许下载的域名、单文件大小上限、下载超时 |
151
+ | 分析接口 | [日志平台分析接口](#日志平台分析接口)的开关、访问令牌(只显示是否已设置)、飞书渠道是否就绪,以及调用地址与字段摘要 |
148
152
  | 高级 | 拉取目录、更新间隔、单次应用数上限、派发超时、SKILL 改进开关与提议上限,恢复 cordis.yml 配置 |
149
153
 
150
154
  面板保存的值写入 `$DSH_HOME/guanxiang/settings.json`(只保存与 cordis.yml 不同的字段),按与配置相同的规则校验;令牌值不在其中,存入 DSH 凭据库。面板接口 `/guanxiang/*` 使用 DSH 自己的登录与来源校验,写操作另做同源校验。
@@ -185,9 +189,66 @@ skill: log-analysis # 对应 .dsh/skills/log-analysis/SKILL.md
185
189
  - 合并请求以令牌主人的身份创建,提交作者是 `skillUpdateAuthor` 或本机 git 身份。飞书群里点「允许」的人可能不是令牌主人;合并请求描述写明了来源会话与分析提交。
186
190
  - 日志内容不可信,可能诱导模型写出不当的 SKILL。观象只限制改动范围,内容需要授权的人和合并请求的评审者把关。
187
191
 
192
+ ## 日志平台分析接口
193
+
194
+ 日志平台调用观象的分析接口:观象经 [dsh_channels](https://github.com/modestoma/dsh_channels) 的 `feishuTopics` 服务在指定群里发一张分析卡(发起人、触发时间、应用、错误码和日志详情页表格),在卡片下开话题,把平台给的 prompt 原样交给一个新 Session(prompt 开头的斜杠命令由 DSH 展开成对应 SKILL,例如 `/guanxiang`),分析过程在话题内流式输出。分析结束后分析卡的状态标签随之更新,群里通过准入的成员都可以在该话题内追问。日志包地址写在 prompt 里,下载、解压、超时都由 prompt 调用的 SKILL 处理(Session 的沙箱需要能访问日志存储地址)。
195
+
196
+ 接口原由 dsh_channels 0.7 提供,从 dsh_channels 0.8 起迁到观象,请求、响应、错误文本和去重记录都不变。
197
+
198
+ **前提**:
199
+
200
+ - dsh_channels 0.8 及以上已加载并提供 `feishuTopics` 服务(版本 1)。没有这个服务时观象不注册接口:dsh_channels 0.7 自带同名接口,升级前由它提供;服务版本不兼容时观象记一条警告,也不注册。设置面板「分析接口」分页显示飞书渠道是否就绪。
201
+ - 只有带 Web 的 profile(web、desktop)有这个接口,headless 没有。
202
+
203
+ **开启**:在设置面板「分析接口」打开开关(`analyze.enabled`,默认关,保存后立即生效),并在同一页保存访问令牌(建议 32 位以上随机串)。令牌存入 DSH 凭据库的 `GUANXIANG_ANALYZE_TOKEN`(名称可用 `analyzeTokenRef` 修改),也可以设置同名环境变量或写进 `.env`。接口挂在 DSH Web 服务上(与 Web 界面同一端口),路径 `/open-api/logalyze` 放在 DSH 的 `/api` 之外,网关可以把它与需要登录的 DSH 接口分开放行;DSH 通过反向代理挂在前缀下时,地址随之带上前缀(如 `https://<域名>/dsh/open-api/logalyze`)。该路由不走 DSH 登录 cookie,访问令牌是唯一凭据。开关关闭时所有请求返回 404;令牌每次请求都重新读取,轮换后下一次请求生效;令牌未设置时所有请求返回 503。
204
+
205
+ **从 dsh_channels 0.7 迁移**:把观象和 dsh_channels 一起升级;在观象面板「分析接口」打开开关并保存令牌(新名称 `GUANXIANG_ANALYZE_TOKEN`,可以填原来的令牌值,日志平台无需改动);然后从 cordis.yml 删掉 dsh_channels 的 `analyzeEnabled` 和 `analyzeToken`(新版 dsh_channels 会对这两项打印警告)。升级前接受过的 `Idempotency-Key` 升级后照常重放。
206
+
207
+ **请求**:
208
+
209
+ ```http
210
+ POST /open-api/logalyze
211
+ Authorization: Bearer <令牌>
212
+ Idempotency-Key: <平台生成的唯一值,1-200 个可见 ASCII 字符>
213
+ Content-Type: application/json
214
+
215
+ {
216
+ "requesterUserId": "a1b2c3d4",
217
+ "triggeredAt": "2026-10-22 19:30",
218
+ "app": "camera",
219
+ "errorCode": "E1001",
220
+ "logDetails": [
221
+ { "name": "设备 A 崩溃日志", "url": "https://logs.example.com/detail/1" },
222
+ { "name": "设备 B 崩溃日志", "url": "https://logs.example.com/detail/2" }
223
+ ],
224
+ "chatId": "oc_xxx",
225
+ "prompt": "/guanxiang https://logs.example.com/1.zip https://logs.example.com/2.zip 请分析这些日志"
226
+ }
227
+ ```
228
+
229
+ - `requesterUserId`:平台发起人的飞书 user_id,分析卡 @ 此人;路由记录里也以此人作为发起人。
230
+ - `triggeredAt`:平台上发起分析的时间,1–64 字符,原样显示在分析卡上(时区和格式由平台决定)。
231
+ - `app` / `errorCode`:只允许 `A-Z a-z 0-9 . _ -`(1–128 字符),决定工作区 `<dsh_channels baseDir>/analyze/<app>/<errorCode>/`;同一应用同一错误码的所有分析都在这个工作区里,每次分析各有一个话题目录。
232
+ - `logDetails`:触发分析的日志在日志平台上的详情页,1–50 条,每条 `name`(1–200 字符)+ `url`(http/https)。只在分析卡的表格里列出,观象不会访问这些地址。
233
+ - `chatId`:目标群,必须是 dsh_channels 已授权的群(`/auth group add` 或其面板「访问与授权」),机器人需在群内;dsh_channels `accessMode: open` 时不校验。
234
+ - `prompt`:1–100000 字符,原样作为首条用户消息,不追加任何内容。
235
+ - 只接受以上 7 个字段,多余字段返回 400。
236
+
237
+ **响应**:校验通过、分析卡发出后立即返回 `202`:
238
+
239
+ ```json
240
+ { "ok": true, "chatId": "oc_xxx", "messageId": "om_xxx" }
241
+ ```
242
+
243
+ `messageId` 是分析卡的消息 id,分析话题挂在它下面。同步错误都带中文 `error`:`401` 令牌无效、`404` 接口开关未打开(或接口未注册)、`405` 不是 POST、`400` 参数或 `Idempotency-Key` 不合法、`403` 目标群未授权、`409` 同一个 `Idempotency-Key` 已被内容不同的请求使用、`413` 请求体超过 1 MiB、`415` 不是 JSON、`502` 分析卡发送失败(如机器人不在群里)、`503` 飞书未连接或未设置令牌。
244
+
245
+ **去重**:同一个 `Idempotency-Key` 再次请求(包括并发重试)原样返回第一次的结果,不会再发卡、再分析。记录由 dsh_channels 永久保存。分析卡发出后的失败(话题创建、Session 创建)不会回滚这条记录:分析卡的状态变成「失败」并写明原因,需要重试时请换一个新的 `Idempotency-Key`。
246
+
247
+ **群里看到的**:先是分析卡「@发起人 于 <triggeredAt> 发起了对 X 应用 Y 错误码不良日志的分析」,下面是日志详情页表格,标题栏标签为「分析中」;紧接着话题内一张流式卡片,首轮分析在这里流式输出,带停止按钮。首轮结束后分析卡的标签变成「已完成」「已停止」或「失败」;DSH 在首轮进行中重启时,重新连上飞书后仍是「分析中」的分析卡会改成「已中断」,在话题内回复即可继续。分析话题是群内共享的:群里任何通过 dsh_channels 准入的成员都可以在话题内追问、停止或回答审批卡。卡片、话题与权限的细节见 dsh_channels README 的「群话题服务(feishuTopics)」。
248
+
188
249
  ## 通过飞书使用
189
250
 
190
- 观象与 [dsh_channels](https://github.com/modestoma/dsh_channels) 没有代码耦合。dsh_channels 把不在自己命令表中的 `/` 开头消息作为普通 prompt 交给 dsh,dsh 识别 `/guanxiang` 后展开入口 SKILL。飞书附件的宿主路径会出现在消息里,入口 SKILL 会把它作为日志路径派发。
251
+ 在飞书里使用 `/guanxiang` 不需要观象与 [dsh_channels](https://github.com/modestoma/dsh_channels) 之间的任何接口:dsh_channels 把不在自己命令表中的 `/` 开头消息作为普通 prompt 交给 dsh,dsh 识别 `/guanxiang` 后展开入口 SKILL。飞书附件的宿主路径会出现在消息里,入口 SKILL 会把它作为日志路径派发。只有[日志平台分析接口](#日志平台分析接口)经 dsh_channels 的 `feishuTopics` 服务发卡开话题;观象只在开发时依赖 dsh_channels 的类型,运行时按服务名查找,没有加载 dsh_channels 时其余功能不受影响。
191
252
 
192
253
  ## 开发
193
254
 
@@ -196,6 +257,8 @@ pnpm install
196
257
  pnpm check # 类型检查(Host 与 Client)+ 单元测试 + 构建 + 两个加载自检
197
258
  ```
198
259
 
260
+ `@modesto_ma/dsh-channels` 是开发依赖,只用来引入 `feishuTopics` 服务的类型(`@modesto_ma/dsh-channels/topics`,`import type`),构建产物不包含它。
261
+
199
262
  ## 发布
200
263
 
201
264
  版本号由 semantic-release 按 Conventional Commits 计算。推送到 `dev` 后,检查通过就自动发布 beta;正式版**只在手动触发时发布**,推送到 `main` 只跑检查。只有 `feat`、`fix` 或破坏性变更会产生新版本,其余类型的提交不发布。PR 标题需符合 `<type>(<scope>): <summary>`,type 取 `feat | fix | docs | test | refactor | chore | ci`。
@@ -215,6 +278,7 @@ pnpm check # 类型检查(Host 与 Client)+ 单元测试 + 构建 +
215
278
  | `src/skill/entry.ts` | `/guanxiang` 入口 SKILL |
216
279
  | `src/dispatch/` | 派发工具、日志下载、子 Agent 首条消息 |
217
280
  | `src/skill-update/` | SKILL 修改提议的校验、提交、授权与推送 |
281
+ | `src/analyze/` | 日志平台分析接口:请求校验、令牌、到 `feishuTopics` 的转换 |
218
282
  | `src/settings/` | 面板契约、覆盖文件、HTTP 路由 |
219
283
  | `src/client/` | 左侧栏入口与设置页面 |
220
284
  | `examples/order-service/` | 示例应用仓库 |
package/lib/client.js CHANGED
@@ -48,7 +48,7 @@ window.__ModuleLoader__.load({
48
48
  const SETTINGS_ROUTE = "/guanxiang/settings";
49
49
  /** POST:立即重新拉取全部仓库,或 body `{ url }` 指定的一个仓库。 */
50
50
  const SYNC_ROUTE = "/guanxiang/sync";
51
- /** PUT:body `{ tokenEnv, value }` 把令牌存入 DSH 凭据库;DELETE:body `{ tokenEnv }` 删除已存的值。只接受已保存的访问令牌配置引用的名称。 */
51
+ /** PUT:body `{ tokenEnv, value }` 把令牌存入 DSH 凭据库;DELETE:body `{ tokenEnv }` 删除已存的值。只接受已保存的访问令牌配置引用的名称和分析接口令牌的名称。 */
52
52
  const TOKEN_ROUTE = "/guanxiang/token";
53
53
  //#endregion
54
54
  //#region src/client/api.ts
@@ -157,9 +157,9 @@ window.__ModuleLoader__.load({
157
157
  //#endregion
158
158
  //#region src/client/page.tsx
159
159
  /**
160
- * 左侧栏「观象」主区页面:应用仓库、访问令牌、日志下载、高级(含 SKILL 改进)四个分页,共用
161
- * 一份草稿和底部保存条;令牌值不进草稿,单独存入 DSH 凭据库。仓库拉取中时
162
- * 每 2 秒刷新状态。
160
+ * 左侧栏「观象」主区页面:应用仓库、访问令牌、日志下载、分析接口、高级(含 SKILL 改进)五个
161
+ * 分页,共用一份草稿和底部保存条;令牌值不进草稿,单独存入 DSH 凭据库。
162
+ * 仓库拉取中时每 2 秒刷新状态。
163
163
  *
164
164
  * @module dsh-guanxiang/client/page
165
165
  */
@@ -181,6 +181,10 @@ window.__ModuleLoader__.load({
181
181
  id: "download",
182
182
  label: "日志下载"
183
183
  },
184
+ {
185
+ id: "analyze",
186
+ label: "分析接口"
187
+ },
184
188
  {
185
189
  id: "advanced",
186
190
  label: "高级"
@@ -202,7 +206,8 @@ window.__ModuleLoader__.load({
202
206
  timeoutSec: trimNumber(settings.download.timeoutMs / 1e3),
203
207
  skillUpdatesEnabled: settings.skillUpdates.enabled,
204
208
  skillMaxFiles: String(settings.skillUpdates.maxFiles),
205
- skillMaxKb: trimNumber(settings.skillUpdates.maxFileBytes / KB)
209
+ skillMaxKb: trimNumber(settings.skillUpdates.maxFileBytes / KB),
210
+ analyzeEnabled: settings.analyze.enabled
206
211
  };
207
212
  }
208
213
  function positive(text, label, allowZero = false) {
@@ -239,7 +244,8 @@ window.__ModuleLoader__.load({
239
244
  enabled: draft.skillUpdatesEnabled,
240
245
  maxFiles,
241
246
  maxFileBytes: Math.round(positive(draft.skillMaxKb, "提议单文件大小上限") * KB)
242
- }
247
+ },
248
+ analyze: { enabled: draft.analyzeEnabled }
243
249
  };
244
250
  }
245
251
  function sameSettings(draft, settings) {
@@ -485,6 +491,18 @@ window.__ModuleLoader__.load({
485
491
  setDraft,
486
492
  overridden: state.overridden
487
493
  }) : null,
494
+ tab === "analyze" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnalyzeTab, {
495
+ draft,
496
+ setDraft,
497
+ analyze: state.analyze,
498
+ credentialsAvailable: state.credentials.available,
499
+ overridden: state.overridden,
500
+ busy,
501
+ onSave: async (value) => await run(() => saveToken(state.analyze.tokenEnv, value), "已保存分析接口令牌,下一个请求起生效", false) !== void 0,
502
+ onClear: () => {
503
+ run(() => clearToken(state.analyze.tokenEnv), "已清除面板保存的分析接口令牌", false);
504
+ }
505
+ }) : null,
488
506
  tab === "advanced" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AdvancedTab, {
489
507
  draft,
490
508
  setDraft,
@@ -1077,6 +1095,224 @@ window.__ModuleLoader__.load({
1077
1095
  })]
1078
1096
  })] });
1079
1097
  }
1098
+ const TOPICS_STATUS = {
1099
+ ready: {
1100
+ pill: "gx__pill--ok",
1101
+ text: "飞书渠道就绪"
1102
+ },
1103
+ missing: {
1104
+ pill: "gx__pill--bad",
1105
+ text: "未找到飞书渠道"
1106
+ },
1107
+ incompatible: {
1108
+ pill: "gx__pill--bad",
1109
+ text: "飞书渠道版本不兼容"
1110
+ }
1111
+ };
1112
+ function AnalyzeTab({ draft, setDraft, analyze, credentialsAvailable, overridden, busy, onSave, onClear }) {
1113
+ const [value, setValue] = (0, react.useState)("");
1114
+ const token = analyze.token;
1115
+ const save = () => {
1116
+ const trimmed = value.trim();
1117
+ if (trimmed === "" || busy) return;
1118
+ onSave(trimmed).then((ok) => {
1119
+ if (ok) setValue("");
1120
+ });
1121
+ };
1122
+ const topics = TOPICS_STATUS[analyze.topics];
1123
+ const endpoint = new URL(analyze.route.slice(1), document.baseURI).href;
1124
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1125
+ className: "gx__sec",
1126
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
1127
+ className: "gx__secTitle",
1128
+ children: "日志平台分析接口"
1129
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1130
+ className: "gx__list",
1131
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1132
+ className: "gx__row",
1133
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1134
+ className: "gx__rowText",
1135
+ children: [
1136
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1137
+ className: "gx__rowLabel",
1138
+ children: [
1139
+ "开启接口 ",
1140
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Overridden, {
1141
+ keys: overridden,
1142
+ name: "analyze"
1143
+ }),
1144
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1145
+ className: `gx__pill ${topics.pill}`,
1146
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "gx__dot" }), topics.text]
1147
+ })
1148
+ ]
1149
+ }),
1150
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1151
+ className: "gx__rowSummary",
1152
+ children: "日志平台调用接口后,观象经飞书渠道(dsh-channels 0.8 及以上)在目标群发出分析卡片,并在卡片下的话题里开始分析;群内成员都可以在话题里继续追问。关闭时接口回复 404。"
1153
+ }),
1154
+ analyze.topics === "ready" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1155
+ className: "gx__rowError",
1156
+ children: analyze.topics === "missing" ? "接口需要 dsh-channels 0.8 及以上版本并已加载;dsh-channels 0.7 自带同名接口,升级前由它提供。" : "请把观象与 dsh-channels 升级到相互兼容的版本。"
1157
+ })
1158
+ ]
1159
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1160
+ className: "gx__rowCtl",
1161
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1162
+ type: "checkbox",
1163
+ className: "gx__check",
1164
+ "aria-label": "开启分析接口",
1165
+ checked: draft.analyzeEnabled,
1166
+ onChange: (event) => {
1167
+ setDraft({
1168
+ ...draft,
1169
+ analyzeEnabled: event.target.checked
1170
+ });
1171
+ }
1172
+ })
1173
+ })]
1174
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1175
+ className: "gx__row gx__row--top",
1176
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1177
+ className: "gx__rowText",
1178
+ children: [
1179
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1180
+ className: "gx__rowLabel",
1181
+ children: [
1182
+ "访问令牌 ",
1183
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1184
+ className: "gx__tag gx__mono",
1185
+ children: analyze.tokenEnv
1186
+ }),
1187
+ credentialsAvailable ? tokenPill(token) : null
1188
+ ]
1189
+ }),
1190
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1191
+ className: "gx__rowSummary",
1192
+ children: [
1193
+ "日志平台以 ",
1194
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1195
+ className: "gx__mono",
1196
+ children: "Authorization: Bearer <令牌>"
1197
+ }),
1198
+ " 调用接口。令牌未设置时接口回复 503。"
1199
+ ]
1200
+ }),
1201
+ credentialsAvailable ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1202
+ className: "gx__rowError",
1203
+ children: "当前 DSH 组合没有加载凭据服务(@deepseek-ai/dsh-credentials-local),无法保存或读取令牌。"
1204
+ }),
1205
+ token?.error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1206
+ className: "gx__rowError",
1207
+ children: token.error
1208
+ }),
1209
+ token !== void 0 && token.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1210
+ className: "gx__addRow gx__addRow--flush",
1211
+ children: [
1212
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1213
+ className: "gx__input gx__mono",
1214
+ type: "password",
1215
+ autoComplete: "new-password",
1216
+ spellCheck: false,
1217
+ placeholder: token.configured ? "输入新令牌以替换" : "粘贴令牌",
1218
+ value,
1219
+ onChange: (event) => {
1220
+ setValue(event.target.value);
1221
+ },
1222
+ onKeyDown: (event) => {
1223
+ if (event.key === "Enter") save();
1224
+ }
1225
+ }),
1226
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1227
+ type: "button",
1228
+ className: "gx__btn",
1229
+ disabled: busy || value.trim() === "",
1230
+ onClick: save,
1231
+ children: "保存令牌"
1232
+ }),
1233
+ token.configured && token.source === "file" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1234
+ type: "button",
1235
+ className: "gx__btn gx__btn--ghost",
1236
+ disabled: busy,
1237
+ onClick: onClear,
1238
+ children: "清除"
1239
+ }) : null
1240
+ ]
1241
+ }) : null
1242
+ ]
1243
+ })
1244
+ })]
1245
+ })]
1246
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1247
+ className: "gx__sec",
1248
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
1249
+ className: "gx__secTitle",
1250
+ children: "调用方式"
1251
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1252
+ className: "gx__list",
1253
+ children: [
1254
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1255
+ className: "gx__row",
1256
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1257
+ className: "gx__rowText",
1258
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1259
+ className: "gx__rowLabel",
1260
+ children: "地址"
1261
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1262
+ className: "gx__rowSummary gx__mono",
1263
+ children: ["POST ", endpoint]
1264
+ })]
1265
+ })
1266
+ }),
1267
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1268
+ className: "gx__row",
1269
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1270
+ className: "gx__rowText",
1271
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1272
+ className: "gx__rowLabel",
1273
+ children: "请求头"
1274
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1275
+ className: "gx__rowSummary gx__mono",
1276
+ children: "Authorization: Bearer <令牌> · Idempotency-Key: <1-200 个可见 ASCII 字符> · Content-Type: application/json"
1277
+ })]
1278
+ })
1279
+ }),
1280
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1281
+ className: "gx__row",
1282
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1283
+ className: "gx__rowText",
1284
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1285
+ className: "gx__rowLabel",
1286
+ children: "请求体"
1287
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1288
+ className: "gx__rowSummary gx__mono",
1289
+ children: [
1290
+ "requesterUserId, triggeredAt, app, errorCode, logDetails[",
1291
+ "{",
1292
+ "name, url",
1293
+ "}",
1294
+ "], chatId, prompt"
1295
+ ]
1296
+ })]
1297
+ })
1298
+ }),
1299
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1300
+ className: "gx__row",
1301
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1302
+ className: "gx__rowText",
1303
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1304
+ className: "gx__rowLabel",
1305
+ children: "响应"
1306
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1307
+ className: "gx__rowSummary",
1308
+ children: "202 已发出分析卡片(同一 Idempotency-Key 重试返回同一张卡片);400/401/404/405/409/413/415 请求问题;403 目标群未授权;502 卡片发送失败;503 飞书未连接或令牌未设置。字段说明见观象 README。"
1309
+ })]
1310
+ })
1311
+ })
1312
+ ]
1313
+ })]
1314
+ })] });
1315
+ }
1080
1316
  function AdvancedTab({ draft, setDraft, overridden, busy, onReset }) {
1081
1317
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1082
1318
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {