@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/README.md +67 -2
- package/lib/client.js +382 -97
- package/lib/client.js.map +1 -1
- package/lib/index.js +472 -62
- package/lib/index.js.map +1 -1
- package/package.json +2 -1
- package/src/analyze/contract.ts +124 -0
- package/src/analyze/server.ts +195 -0
- package/src/analyze/topics.ts +109 -0
- package/src/client/page.tsx +172 -41
- package/src/client/styles.ts +4 -0
- package/src/config.ts +25 -3
- package/src/index.ts +10 -4
- package/src/repos/credentials.ts +2 -3
- package/src/service.ts +47 -12
- package/src/settings/contract.ts +41 -2
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
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
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
|
-
|
|
346
|
+
const next = await action();
|
|
347
|
+
adopt(next, resetDraft);
|
|
340
348
|
setNotice({
|
|
341
349
|
kind: "ok",
|
|
342
350
|
text: success
|
|
343
351
|
});
|
|
344
|
-
return
|
|
352
|
+
return next;
|
|
345
353
|
} catch (error) {
|
|
346
354
|
setNotice({
|
|
347
355
|
kind: "error",
|
|
348
356
|
text: messageOf(error)
|
|
349
357
|
});
|
|
350
|
-
return
|
|
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
|
-
|
|
465
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 [
|
|
734
|
-
const [
|
|
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((
|
|
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
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
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: ["
|
|
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
|
|
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.
|
|
805
|
-
className: "gx__rowSummary",
|
|
806
|
-
children:
|
|
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
|
-
|
|
862
|
+
status?.error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
812
863
|
className: "gx__rowError",
|
|
813
|
-
children:
|
|
864
|
+
children: status.error
|
|
814
865
|
}),
|
|
815
|
-
|
|
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:
|
|
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
|
-
|
|
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: "
|
|
874
|
-
children: [
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
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
|
|
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)}
|