@modesto_ma/dsh-guanxiang 0.1.0 → 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/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) {
@@ -223,6 +228,7 @@ window.__ModuleLoader__.load({
223
228
  }),
224
229
  gitCredentials: draft.gitCredentials.map((credential) => ({
225
230
  url: credential.url.trim(),
231
+ ...credential.alias === void 0 || credential.alias.trim() === "" ? {} : { alias: credential.alias.trim() },
226
232
  username: credential.username.trim(),
227
233
  tokenEnv: credential.tokenEnv.trim()
228
234
  })),
@@ -238,7 +244,8 @@ window.__ModuleLoader__.load({
238
244
  enabled: draft.skillUpdatesEnabled,
239
245
  maxFiles,
240
246
  maxFileBytes: Math.round(positive(draft.skillMaxKb, "提议单文件大小上限") * KB)
241
- }
247
+ },
248
+ analyze: { enabled: draft.analyzeEnabled }
242
249
  };
243
250
  }
244
251
  function sameSettings(draft, settings) {
@@ -336,18 +343,19 @@ window.__ModuleLoader__.load({
336
343
  setBusy(true);
337
344
  setNotice(void 0);
338
345
  try {
339
- adopt(await action(), resetDraft);
346
+ const next = await action();
347
+ adopt(next, resetDraft);
340
348
  setNotice({
341
349
  kind: "ok",
342
350
  text: success
343
351
  });
344
- return true;
352
+ return next;
345
353
  } catch (error) {
346
354
  setNotice({
347
355
  kind: "error",
348
356
  text: messageOf(error)
349
357
  });
350
- return false;
358
+ return;
351
359
  } finally {
352
360
  setBusy(false);
353
361
  }
@@ -461,14 +469,40 @@ window.__ModuleLoader__.load({
461
469
  setDraft,
462
470
  state,
463
471
  busy,
464
- onSave: (tokenEnv, value) => run(() => saveToken(tokenEnv, value), `已保存 ${tokenEnv},使用它的仓库会重新拉取`, false),
465
- onClear: (tokenEnv) => run(() => clearToken(tokenEnv), `已清除面板保存的 ${tokenEnv}`, false)
472
+ onAdd: async (credential, token) => {
473
+ const added = (await run(() => saveSettings({
474
+ ...state.settings,
475
+ gitCredentials: [...state.settings.gitCredentials, credential]
476
+ }), token === "" ? `已添加 ${credential.url},请填入令牌` : `已添加 ${credential.url}`, false))?.settings.gitCredentials[state.settings.gitCredentials.length];
477
+ if (added === void 0) return false;
478
+ setDraft((current) => current === void 0 || current.gitCredentials.some((item) => item.url === added.url) ? current : {
479
+ ...current,
480
+ gitCredentials: [...current.gitCredentials, added]
481
+ });
482
+ return token === "" || await run(() => saveToken(added.tokenEnv, token), `已添加 ${added.url} 并保存令牌,使用它的仓库会重新拉取`, false) !== void 0;
483
+ },
484
+ onSave: async (tokenEnv, value) => await run(() => saveToken(tokenEnv, value), `已保存 ${tokenEnv},使用它的仓库会重新拉取`, false) !== void 0,
485
+ onClear: (tokenEnv) => {
486
+ run(() => clearToken(tokenEnv), `已清除面板保存的 ${tokenEnv}`, false);
487
+ }
466
488
  }) : null,
467
489
  tab === "download" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DownloadTab, {
468
490
  draft,
469
491
  setDraft,
470
492
  overridden: state.overridden
471
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,
472
506
  tab === "advanced" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AdvancedTab, {
473
507
  draft,
474
508
  setDraft,
@@ -698,7 +732,8 @@ window.__ModuleLoader__.load({
698
732
  });
699
733
  }
700
734
  const DEFAULT_USERNAME = "oauth2";
701
- const DEFAULT_TOKEN_ENV = "GITLAB_TOKEN";
735
+ /** Prefix of the credential keys the panel allocates; namespaced so an unrelated `GITLAB_TOKEN` in the environment is not picked up. */
736
+ const DEFAULT_TOKEN_ENV = "GUANXIANG_GITLAB_TOKEN";
702
737
  /** Credential store layers of `@deepseek-ai/dsh-credentials-local` other than its own file. */
703
738
  const TOKEN_SOURCES = {
704
739
  "env": "启动环境变量",
@@ -728,29 +763,55 @@ window.__ModuleLoader__.load({
728
763
  ]
729
764
  });
730
765
  }
731
- function TokensTab({ draft, setDraft, state, busy, onSave, onClear }) {
766
+ /**
767
+ * First of `GUANXIANG_GITLAB_TOKEN`, `GUANXIANG_GITLAB_TOKEN_2`, … that no
768
+ * saved or drafted entry uses.
769
+ * @param used - keys already in use.
770
+ * @returns a free key.
771
+ */
772
+ function freeTokenEnv(used) {
773
+ if (!used.has(DEFAULT_TOKEN_ENV)) return DEFAULT_TOKEN_ENV;
774
+ let index = 2;
775
+ while (used.has(`${DEFAULT_TOKEN_ENV}_${index}`)) index += 1;
776
+ return `${DEFAULT_TOKEN_ENV}_${index}`;
777
+ }
778
+ function TokensTab({ draft, setDraft, state, busy, onAdd, onSave, onClear }) {
732
779
  const [url, setUrl] = (0, react.useState)("");
733
- const [username, setUsername] = (0, react.useState)(DEFAULT_USERNAME);
734
- const [tokenEnv, setTokenEnv] = (0, react.useState)(DEFAULT_TOKEN_ENV);
780
+ const [alias, setAlias] = (0, react.useState)("");
781
+ const [token, setToken] = (0, react.useState)("");
782
+ const [addError, setAddError] = (0, react.useState)();
783
+ const [adding, setAdding] = (0, react.useState)(false);
735
784
  const [values, setValues] = (0, react.useState)({});
736
- const tokens = (0, react.useMemo)(() => new Map(state.credentials.tokens.map((token) => [token.tokenEnv, token])), [state.credentials.tokens]);
785
+ const tokens = (0, react.useMemo)(() => new Map(state.credentials.tokens.map((item) => [item.tokenEnv, item])), [state.credentials.tokens]);
737
786
  const saved = (0, react.useMemo)(() => new Set(state.settings.gitCredentials.map((credential) => credential.tokenEnv)), [state.settings.gitCredentials]);
787
+ const complete = url.trim() !== "" && alias.trim() !== "" && token.trim() !== "";
738
788
  const add = () => {
739
- const trimmed = url.trim();
740
- if (trimmed === "" || draft.gitCredentials.some((credential) => credential.url === trimmed)) return;
741
- setDraft({
742
- ...draft,
743
- gitCredentials: [...draft.gitCredentials, {
744
- url: trimmed,
745
- username: username.trim() === "" ? DEFAULT_USERNAME : username.trim(),
746
- tokenEnv: tokenEnv.trim() === "" ? DEFAULT_TOKEN_ENV : tokenEnv.trim()
747
- }]
789
+ if (!complete || busy || adding) return;
790
+ const address = url.trim();
791
+ if (state.settings.gitCredentials.some((credential) => credential.url === address)) {
792
+ setAddError("这个地址已经添加过了,请在上面的列表里更新它的令牌。");
793
+ return;
794
+ }
795
+ setAddError(void 0);
796
+ setAdding(true);
797
+ const tokenEnv = freeTokenEnv(/* @__PURE__ */ new Set([...saved, ...draft.gitCredentials.map((credential) => credential.tokenEnv)]));
798
+ onAdd({
799
+ url: address,
800
+ alias: alias.trim(),
801
+ username: DEFAULT_USERNAME,
802
+ tokenEnv
803
+ }, token.trim()).then((ok) => {
804
+ if (!ok) return;
805
+ setUrl("");
806
+ setAlias("");
807
+ setToken("");
808
+ }).finally(() => {
809
+ setAdding(false);
748
810
  });
749
- setUrl("");
750
811
  };
751
812
  const save = (name) => {
752
813
  const value = values[name]?.trim() ?? "";
753
- if (value === "") return;
814
+ if (value === "" || busy || adding) return;
754
815
  onSave(name, value).then((ok) => {
755
816
  if (ok) setValues((current) => ({
756
817
  ...current,
@@ -758,12 +819,15 @@ window.__ModuleLoader__.load({
758
819
  }));
759
820
  });
760
821
  };
822
+ const submitOnEnter = (event) => {
823
+ if (event.key === "Enter") add();
824
+ };
761
825
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
762
826
  className: "gx__sec",
763
827
  children: [
764
828
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h2", {
765
829
  className: "gx__secTitle",
766
- children: ["HTTPS 访问令牌 ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Overridden, {
830
+ children: ["GitLab 访问令牌 ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Overridden, {
767
831
  keys: state.overridden,
768
832
  name: "gitCredentials"
769
833
  })]
@@ -777,10 +841,10 @@ window.__ModuleLoader__.load({
777
841
  children: [
778
842
  draft.gitCredentials.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
779
843
  className: "gx__emptyRow",
780
- children: "还没有访问令牌。用 https 地址拉取 GitLab 等私有仓库时,添加服务器地址后保存,再填入个人访问令牌。"
844
+ children: "还没有访问令牌。用 https 地址拉取 GitLab 私有仓库时,在下面填入 GitLab 地址、令牌别名和令牌。"
781
845
  }) : null,
782
846
  draft.gitCredentials.map((credential, index) => {
783
- const token = saved.has(credential.tokenEnv) ? tokens.get(credential.tokenEnv) : void 0;
847
+ const status = saved.has(credential.tokenEnv) ? tokens.get(credential.tokenEnv) : void 0;
784
848
  const value = values[credential.tokenEnv] ?? "";
785
849
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
786
850
  className: "gx__row gx__row--top",
@@ -789,30 +853,17 @@ window.__ModuleLoader__.load({
789
853
  children: [
790
854
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
791
855
  className: "gx__rowLabel",
792
- children: [
793
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
794
- className: "gx__mono",
795
- children: credential.url
796
- }),
797
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
798
- className: "gx__tag",
799
- children: ["用户名 ", credential.username]
800
- }),
801
- tokenPill(token)
802
- ]
856
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: credential.alias ?? credential.url }), tokenPill(status)]
803
857
  }),
804
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
805
- className: "gx__rowSummary",
806
- children: ["令牌名称 ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
807
- className: "gx__mono",
808
- children: credential.tokenEnv
809
- })]
858
+ credential.alias === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
859
+ className: "gx__rowSummary gx__mono",
860
+ children: credential.url
810
861
  }),
811
- token?.error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
862
+ status?.error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
812
863
  className: "gx__rowError",
813
- children: token.error
864
+ children: status.error
814
865
  }),
815
- token !== void 0 && token.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
866
+ status !== void 0 && status.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
816
867
  className: "gx__addRow gx__addRow--flush",
817
868
  children: [
818
869
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
@@ -820,7 +871,7 @@ window.__ModuleLoader__.load({
820
871
  type: "password",
821
872
  autoComplete: "new-password",
822
873
  spellCheck: false,
823
- placeholder: token.configured ? "输入新令牌以替换" : "粘贴个人访问令牌,例如 glpat-…",
874
+ placeholder: status.configured ? "输入新令牌以替换" : "粘贴令牌",
824
875
  value,
825
876
  onChange: (event) => {
826
877
  setValues({
@@ -835,13 +886,13 @@ window.__ModuleLoader__.load({
835
886
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
836
887
  type: "button",
837
888
  className: "gx__btn",
838
- disabled: busy || value.trim() === "",
889
+ disabled: busy || adding || value.trim() === "",
839
890
  onClick: () => {
840
891
  save(credential.tokenEnv);
841
892
  },
842
893
  children: "保存令牌"
843
894
  }),
844
- token.configured && token.source === "file" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
895
+ status.configured && status.source === "file" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
845
896
  type: "button",
846
897
  className: "gx__btn gx__btn--ghost",
847
898
  disabled: busy,
@@ -870,55 +921,67 @@ window.__ModuleLoader__.load({
870
921
  }, credential.url);
871
922
  }),
872
923
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
873
- className: "gx__addRow",
874
- children: [
875
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
876
- className: "gx__input",
877
- placeholder: "服务器地址,例如 https://gitlab.example.com",
878
- value: url,
879
- onChange: (event) => {
880
- setUrl(event.target.value);
881
- },
882
- onKeyDown: (event) => {
883
- if (event.key === "Enter") add();
884
- }
885
- }),
886
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
887
- className: "gx__input gx__input--branch",
888
- placeholder: `用户名(默认 ${DEFAULT_USERNAME})`,
889
- value: username,
890
- onChange: (event) => {
891
- setUsername(event.target.value);
892
- },
893
- onKeyDown: (event) => {
894
- if (event.key === "Enter") add();
895
- }
896
- }),
897
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
898
- className: "gx__input gx__input--branch gx__mono",
899
- placeholder: "令牌名称",
900
- value: tokenEnv,
901
- onChange: (event) => {
902
- setTokenEnv(event.target.value);
903
- },
904
- onKeyDown: (event) => {
905
- if (event.key === "Enter") add();
906
- }
907
- }),
908
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
909
- type: "button",
910
- className: "gx__btn",
911
- disabled: url.trim() === "",
912
- onClick: add,
913
- children: "添加"
914
- })
915
- ]
924
+ className: "gx__addForm",
925
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
926
+ className: "gx__addRow gx__addRow--flush",
927
+ children: [
928
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
929
+ className: "gx__field",
930
+ children: ["GitLab 地址", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
931
+ className: "gx__input",
932
+ placeholder: "https://gitlab.example.com",
933
+ value: url,
934
+ onChange: (event) => {
935
+ setUrl(event.target.value);
936
+ },
937
+ onKeyDown: submitOnEnter
938
+ })]
939
+ }),
940
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
941
+ className: "gx__field",
942
+ children: ["令牌别名", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
943
+ className: "gx__input",
944
+ placeholder: "例如 guanxiang",
945
+ value: alias,
946
+ onChange: (event) => {
947
+ setAlias(event.target.value);
948
+ },
949
+ onKeyDown: submitOnEnter
950
+ })]
951
+ }),
952
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
953
+ className: "gx__field",
954
+ children: ["令牌", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
955
+ className: "gx__input gx__mono",
956
+ type: "password",
957
+ autoComplete: "new-password",
958
+ spellCheck: false,
959
+ placeholder: "粘贴个人访问令牌",
960
+ value: token,
961
+ onChange: (event) => {
962
+ setToken(event.target.value);
963
+ },
964
+ onKeyDown: submitOnEnter
965
+ })]
966
+ }),
967
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
968
+ type: "button",
969
+ className: "gx__btn gx__btn--end",
970
+ disabled: busy || adding || !complete,
971
+ onClick: add,
972
+ children: "添加"
973
+ })
974
+ ]
975
+ }), addError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
976
+ className: "gx__rowError",
977
+ children: addError
978
+ })]
916
979
  })
917
980
  ]
918
981
  }),
919
982
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
920
983
  className: "gx__secFoot",
921
- children: "地址在这个前缀下的 https 仓库使用对应令牌拉取(多个匹配时取路径最长的),ssh 地址不受影响。GitLab 个人访问令牌需要 read_repository 权限,用户名填 oauth2 即可。令牌保存在 DSH 凭据库(默认 $DSH_HOME/.credentials.yaml,仅本机用户可读),面板只显示是否已设置;也可以设置与令牌名称同名的环境变量。拉取时令牌只经环境变量交给 git,不写入仓库配置、日志或模型上下文。"
984
+ children: "地址在这个前缀下的 https 仓库使用对应令牌(多个匹配时取路径最长的),ssh 地址不受影响。拉取需要 read_repository 权限,开启 SKILL 改进时还需要 write_repository。令牌保存在 DSH 凭据库(默认 $DSH_HOME/.credentials.yaml,仅本机用户可读),面板只显示是否已设置;令牌只经环境变量交给 git,不写入设置文件、仓库配置、日志或模型上下文。"
922
985
  })
923
986
  ]
924
987
  });
@@ -1032,6 +1095,224 @@ window.__ModuleLoader__.load({
1032
1095
  })]
1033
1096
  })] });
1034
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
+ }
1035
1316
  function AdvancedTab({ draft, setDraft, overridden, busy, onReset }) {
1036
1317
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1037
1318
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
@@ -1297,6 +1578,10 @@ window.__ModuleLoader__.load({
1297
1578
  .gx__addRow .gx__input{flex:1 1 160px;width:auto}
1298
1579
  .gx__addRow .gx__input--branch{flex:0 1 140px}
1299
1580
  .gx__addRow--flush{padding:6px 0 0}
1581
+ .gx__addForm{display:flex;flex-direction:column;gap:6px;padding:10px 14px}
1582
+ .gx__field{display:flex;flex-direction:column;gap:2px;flex:1 1 160px;color:var(--gx-fg3);font-size:12px}
1583
+ .gx__field .gx__input{flex:none;width:100%}
1584
+ .gx__btn--end{align-self:flex-end;height:30px}
1300
1585
  .gx__emptyRow{padding:11px 14px;color:var(--gx-fg3);font-size:12px}
1301
1586
  .gx__btn{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:4px;height:28px;padding:0 12px;border:1px solid var(--gx-line);border-radius:7px;background:transparent;color:inherit;font:inherit;font-size:13px;cursor:pointer;white-space:nowrap}
1302
1587
  .gx__btn:hover:not(:disabled){background:var(--gx-hover)}