dsh-ant 0.6.1 → 0.6.3
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 +2 -2
- package/client.js +669 -194
- package/conversation.js +175 -80
- package/delegation.js +228 -49
- package/index.js +38 -7
- package/locale/messages.json +643 -144
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -12,6 +12,37 @@ window.__ModuleLoader__.load({
|
|
|
12
12
|
let localeMetadata = {}
|
|
13
13
|
let localeTranslate = (key) => key
|
|
14
14
|
let localeMessagePatterns = []
|
|
15
|
+
const pluginScopeBusyMessage = '本机有一项插件操作仍在完成;结束前不能启动其他插件安装。'
|
|
16
|
+
const pluginRuntimeState = {
|
|
17
|
+
uncertainInstalls: new Map(), uncertainInstallsRef: { current: {} },
|
|
18
|
+
serviceUrl: '', scopeRevision: 0, workingId: '', workingServiceUrl: '', completedId: '',
|
|
19
|
+
message: '', messagePluginId: { current: '' }, messagePackageName: { current: '' },
|
|
20
|
+
messageClearOnResolve: { current: false },
|
|
21
|
+
refreshMessage: '', refreshMessageIsError: false,
|
|
22
|
+
messageVersion: { current: 0 }, refreshGeneration: { current: 0 }, refreshRevision: 0,
|
|
23
|
+
listeners: new Set(),
|
|
24
|
+
}
|
|
25
|
+
const publishPluginRuntimeState = () => {
|
|
26
|
+
pluginRuntimeState.uncertainInstallsRef.current = Object.fromEntries(pluginRuntimeState.uncertainInstalls)
|
|
27
|
+
for (const listener of [...pluginRuntimeState.listeners]) listener()
|
|
28
|
+
}
|
|
29
|
+
const setPluginRuntimeServiceScope = (serviceUrl) => {
|
|
30
|
+
const nextServiceUrl = normalizeServiceUrl(serviceUrl) || ''
|
|
31
|
+
if (nextServiceUrl === pluginRuntimeState.serviceUrl) return
|
|
32
|
+
pluginRuntimeState.serviceUrl = nextServiceUrl
|
|
33
|
+
pluginRuntimeState.scopeRevision += 1
|
|
34
|
+
pluginRuntimeState.completedId = ''
|
|
35
|
+
pluginRuntimeState.message = pluginRuntimeState.workingId ? pluginScopeBusyMessage : ''
|
|
36
|
+
pluginRuntimeState.messagePluginId.current = ''
|
|
37
|
+
pluginRuntimeState.messagePackageName.current = ''
|
|
38
|
+
pluginRuntimeState.messageClearOnResolve.current = false
|
|
39
|
+
pluginRuntimeState.refreshMessage = ''
|
|
40
|
+
pluginRuntimeState.refreshMessageIsError = false
|
|
41
|
+
pluginRuntimeState.messageVersion.current += 1
|
|
42
|
+
pluginRuntimeState.refreshGeneration.current += 1
|
|
43
|
+
pluginRuntimeState.refreshRevision += 1
|
|
44
|
+
publishPluginRuntimeState()
|
|
45
|
+
}
|
|
15
46
|
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&')
|
|
16
47
|
const compileLocalePattern = (source) => {
|
|
17
48
|
const names = []
|
|
@@ -54,6 +85,45 @@ window.__ModuleLoader__.load({
|
|
|
54
85
|
}
|
|
55
86
|
return createElement(type, localizedProps, ...children.map(localizedChild))
|
|
56
87
|
}
|
|
88
|
+
const releaseNoteBlocks = (value) => {
|
|
89
|
+
const blocks = []
|
|
90
|
+
let paragraph = []
|
|
91
|
+
let list = []
|
|
92
|
+
let key = 0
|
|
93
|
+
const flushParagraph = () => {
|
|
94
|
+
if (!paragraph.length) return
|
|
95
|
+
blocks.push(h('p', { key: key++, style: { ...mutedTextStyle, whiteSpace: 'pre-line' } },
|
|
96
|
+
rawLocaleText(paragraph.join('\n'))))
|
|
97
|
+
paragraph = []
|
|
98
|
+
}
|
|
99
|
+
const flushList = () => {
|
|
100
|
+
if (!list.length) return
|
|
101
|
+
blocks.push(h('ul', { key: key++, style: { margin: 0, paddingLeft: 22, display: 'grid', gap: 4,
|
|
102
|
+
color: 'var(--dsw-alias-label-secondary)', fontSize: 13, lineHeight: 1.6 } },
|
|
103
|
+
...list.map((item) => h('li', { key: item.key }, rawLocaleText(item.text)))))
|
|
104
|
+
list = []
|
|
105
|
+
}
|
|
106
|
+
for (const line of String(value ?? '').replace(/\r\n?/gu, '\n').split('\n')) {
|
|
107
|
+
const heading = /^\s{0,3}(#{1,3})\s+(.+)$/u.exec(line)
|
|
108
|
+
const bullet = /^\s*(?:[-*•]|\d+[.)])\s+(.+)$/u.exec(line)
|
|
109
|
+
if (heading) {
|
|
110
|
+
flushParagraph(); flushList()
|
|
111
|
+
const level = heading[1].length
|
|
112
|
+
const tag = level === 1 ? 'h3' : level === 2 ? 'h4' : 'h5'
|
|
113
|
+
blocks.push(h(tag, { key: key++, style: { margin: 0,
|
|
114
|
+
fontSize: level === 1 ? 15 : 13, lineHeight: 1.45 } }, rawLocaleText(heading[2].trim())))
|
|
115
|
+
} else if (bullet) {
|
|
116
|
+
flushParagraph()
|
|
117
|
+
list.push({ key: key++, text: bullet[1].trim() })
|
|
118
|
+
} else if (!line.trim()) {
|
|
119
|
+
flushParagraph(); flushList()
|
|
120
|
+
} else {
|
|
121
|
+
flushList(); paragraph.push(line)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
flushParagraph(); flushList()
|
|
125
|
+
return blocks
|
|
126
|
+
}
|
|
57
127
|
const confirmLocalized = (message) => window.confirm(translateLocaleText(message))
|
|
58
128
|
const promptLocalized = (message, ...args) => window.prompt(translateLocaleText(message), ...args)
|
|
59
129
|
const useLocaleRevision = () => {
|
|
@@ -444,6 +514,7 @@ window.__ModuleLoader__.load({
|
|
|
444
514
|
if (result.service_url != null && !pinned) throw new Error()
|
|
445
515
|
if (pinned) {
|
|
446
516
|
if (current()) {
|
|
517
|
+
setPluginRuntimeServiceScope(pinned)
|
|
447
518
|
setBinding({ state: 'ready', url: pinned })
|
|
448
519
|
setServiceUrl(pinned)
|
|
449
520
|
setDraft(pinned)
|
|
@@ -494,6 +565,7 @@ window.__ModuleLoader__.load({
|
|
|
494
565
|
}
|
|
495
566
|
try {
|
|
496
567
|
localStorage.setItem(SERVICE_URL_STORAGE_KEY, normalized)
|
|
568
|
+
setPluginRuntimeServiceScope(normalized)
|
|
497
569
|
setServiceUrl(normalized)
|
|
498
570
|
setDraft(normalized)
|
|
499
571
|
setMessage('团队服务地址已保存。')
|
|
@@ -509,6 +581,7 @@ window.__ModuleLoader__.load({
|
|
|
509
581
|
const bindingReadyForSummary = binding.state === 'ready' && Boolean(binding.url)
|
|
510
582
|
const onConnectionChange = (url) => {
|
|
511
583
|
bindingRevision.current += 1
|
|
584
|
+
setPluginRuntimeServiceScope(url || '')
|
|
512
585
|
setBinding({ state: 'ready', url: url || null })
|
|
513
586
|
setDelegationRevision((value) => value + 1)
|
|
514
587
|
if (url) {
|
|
@@ -623,14 +696,17 @@ window.__ModuleLoader__.load({
|
|
|
623
696
|
const [localConcurrency, setLocalConcurrency] = useState(null)
|
|
624
697
|
const [repositoryCacheLimitGib, setRepositoryCacheLimitGib] = useState(10)
|
|
625
698
|
const [ownerChallengeCode, setOwnerChallengeCode] = useState('')
|
|
699
|
+
const ownerChallengeMetaRef = useRef(null)
|
|
700
|
+
const ownerChallengeTimerRef = useRef(null)
|
|
626
701
|
const [botId, setBotId] = useState('')
|
|
702
|
+
const botIdRef = useRef('')
|
|
627
703
|
const [conversationKind, setConversationKind] = useState('dm')
|
|
628
704
|
const [groupId, setGroupId] = useState('')
|
|
629
705
|
const [ruleDescription, setRuleDescription] = useState('')
|
|
630
|
-
const [triggerPhrases, setTriggerPhrases] = useState('')
|
|
631
706
|
const recognizedBaselineRef = useRef(null)
|
|
632
707
|
const recognizedDescriptionRef = useRef(null)
|
|
633
708
|
const recognitionRequestRef = useRef(0)
|
|
709
|
+
const recognitionControllerRef = useRef(null)
|
|
634
710
|
const [recognitionBusy, setRecognitionBusy] = useState(false)
|
|
635
711
|
const [recognitionMessage, setRecognitionMessage] = useState('')
|
|
636
712
|
const [missingRequired, setMissingRequired] = useState([])
|
|
@@ -641,7 +717,31 @@ window.__ModuleLoader__.load({
|
|
|
641
717
|
const [taskPrompt, setTaskPrompt] = useState('')
|
|
642
718
|
const [runner, setRunner] = useState('dsh')
|
|
643
719
|
const [reviewers, setReviewers] = useState(['dsh'])
|
|
720
|
+
const [ownerApprovalRequired, setOwnerApprovalRequired] = useState(true)
|
|
644
721
|
const [parameters, setParameters] = useState([])
|
|
722
|
+
const cancelRecognitionRequest = () => {
|
|
723
|
+
recognitionControllerRef.current?.abort()
|
|
724
|
+
recognitionControllerRef.current = null
|
|
725
|
+
setRecognitionBusy(false)
|
|
726
|
+
}
|
|
727
|
+
const clearOwnerChallenge = () => {
|
|
728
|
+
if (ownerChallengeTimerRef.current !== null) clearTimeout(ownerChallengeTimerRef.current)
|
|
729
|
+
ownerChallengeTimerRef.current = null
|
|
730
|
+
ownerChallengeMetaRef.current = null
|
|
731
|
+
setOwnerChallengeCode('')
|
|
732
|
+
}
|
|
733
|
+
const selectBot = (nextBotId) => {
|
|
734
|
+
if (nextBotId !== botIdRef.current) {
|
|
735
|
+
clearOwnerChallenge()
|
|
736
|
+
botIdRef.current = nextBotId
|
|
737
|
+
}
|
|
738
|
+
setBotId(nextBotId)
|
|
739
|
+
}
|
|
740
|
+
useEffect(() => () => {
|
|
741
|
+
recognitionRequestRef.current += 1
|
|
742
|
+
recognitionControllerRef.current?.abort()
|
|
743
|
+
if (ownerChallengeTimerRef.current !== null) clearTimeout(ownerChallengeTimerRef.current)
|
|
744
|
+
}, [])
|
|
645
745
|
const [giteaToken, setGiteaToken] = useState('')
|
|
646
746
|
const [tokenConfigured, setTokenConfigured] = useState(false)
|
|
647
747
|
const [senderScope, setSenderScope] = useState('any')
|
|
@@ -661,9 +761,12 @@ window.__ModuleLoader__.load({
|
|
|
661
761
|
setLoading(true)
|
|
662
762
|
setMessage('')
|
|
663
763
|
try {
|
|
664
|
-
const
|
|
764
|
+
const ownerChallengeAtLoad = ownerChallengeMetaRef.current
|
|
765
|
+
const paths = ['bots', 'enrolled-bots', 'groups', 'cards', 'settings', 'executors', 'local-settings', 'status',
|
|
766
|
+
...(ownerChallengeAtLoad ? ['owner-challenge'] : [])]
|
|
665
767
|
const responses = await Promise.all(paths.map((path) =>
|
|
666
|
-
fetch(`/api/ant-teams/delegation/${path}
|
|
768
|
+
fetch(`/api/ant-teams/delegation/${path}${path === 'owner-challenge'
|
|
769
|
+
? `?bot_fingerprint=${encodeURIComponent(ownerChallengeAtLoad.fingerprint)}` : ''}`, { cache: 'no-store',
|
|
667
770
|
...(path === 'enrolled-bots' ? { headers: { 'x-ant-include-unbound': 'true' } } : {}) })
|
|
668
771
|
.catch(() => null)))
|
|
669
772
|
const data = await Promise.all(responses.map((response) => response
|
|
@@ -675,12 +778,30 @@ window.__ModuleLoader__.load({
|
|
|
675
778
|
Array.isArray,
|
|
676
779
|
(value) => value && typeof value === 'object' && !Array.isArray(value),
|
|
677
780
|
(value) => Array.isArray(value?.items),
|
|
781
|
+
...(ownerChallengeAtLoad ? [(value) => value && typeof value === 'object'
|
|
782
|
+
&& typeof value.pending === 'boolean'] : []),
|
|
678
783
|
]
|
|
679
784
|
const readable = responses.map((response, index) => Boolean(response?.ok && valid[index](data[index])))
|
|
680
|
-
setLoadFailures(paths.filter((path, index) => !readable[index]))
|
|
785
|
+
setLoadFailures(paths.filter((path, index) => !readable[index] && path !== 'owner-challenge'))
|
|
681
786
|
setBots(readable[0] ? data[0].bots : [])
|
|
682
787
|
setBotLoadError(!readable[0])
|
|
683
|
-
|
|
788
|
+
const nextEnrolled = readable[1] ? data[1] : []
|
|
789
|
+
setEnrolled(nextEnrolled)
|
|
790
|
+
const ownerChallenge = ownerChallengeAtLoad
|
|
791
|
+
if (ownerChallenge) {
|
|
792
|
+
const selectedFingerprint = readable[0] ? data[0].bots.find((bot) => bot.bot_id === botId)?.fingerprint : null
|
|
793
|
+
const enrollmentRead = readable[1]
|
|
794
|
+
const nowBound = enrollmentRead && nextEnrolled.some((enrollment) => enrollment.bot_fingerprint === ownerChallenge.fingerprint
|
|
795
|
+
&& enrollment.owner_bound)
|
|
796
|
+
const challengeStatusIndex = paths.indexOf('owner-challenge')
|
|
797
|
+
const challengePending = challengeStatusIndex >= 0 && readable[challengeStatusIndex]
|
|
798
|
+
? data[challengeStatusIndex].pending : null
|
|
799
|
+
if (ownerChallengeMetaRef.current === ownerChallenge
|
|
800
|
+
&& (readable[0] && selectedFingerprint !== ownerChallenge.fingerprint
|
|
801
|
+
|| Date.now() >= ownerChallenge.expiresAt
|
|
802
|
+
|| (!ownerChallenge.ownerWasBound && nowBound)
|
|
803
|
+
|| challengePending === false)) clearOwnerChallenge()
|
|
804
|
+
}
|
|
684
805
|
setGroups(readable[2] ? data[2] : [])
|
|
685
806
|
setCards(readable[3] ? data[3] : [])
|
|
686
807
|
setDefaults(readable[4] ? data[4] : null)
|
|
@@ -696,6 +817,9 @@ window.__ModuleLoader__.load({
|
|
|
696
817
|
useEffect(() => { void load().catch((error) => setMessage(error.message)) }, [remote, revision])
|
|
697
818
|
useEffect(() => { setMessage('') }, [activeTab])
|
|
698
819
|
const selectedBot = bots.find((bot) => bot.bot_id === botId)
|
|
820
|
+
const botPlatformLabel = (bot) => bot?.channel === 'feishu' || !bot?.channel
|
|
821
|
+
? translateLocaleText('飞书')
|
|
822
|
+
: bot.channel === 'wecom' ? translateLocaleText('企业微信') : translateLocaleText('其他 IM')
|
|
699
823
|
const botConnected = Boolean(selectedBot?.available && selectedBot?.connected)
|
|
700
824
|
const selectedEnrollment = enrolled.find((bot) => bot.bot_fingerprint === selectedBot?.fingerprint)
|
|
701
825
|
const selectedGiteaOrigin = normalizeServiceUrl(selectedEnrollment?.gitea_origin)
|
|
@@ -757,6 +881,7 @@ window.__ModuleLoader__.load({
|
|
|
757
881
|
const dshAvailable = executors.some((item) => item.id === 'dsh' && item.available)
|
|
758
882
|
const executorUnavailableReason = executors.find((item) => item.reason)?.reason || null
|
|
759
883
|
const reviewerLabel = (id) => ({ dsh: 'DSH', codex: 'Codex', 'claude-code': 'Claude Code' })[id] || id
|
|
884
|
+
const usesExternalExecutor = reviewers.some((id) => ['codex', 'claude-code'].includes(id))
|
|
760
885
|
const cardReviewerSummary = (card) => {
|
|
761
886
|
const selected = Array.isArray(card.reviewers) && card.reviewers.length ? card.reviewers : [card.runner]
|
|
762
887
|
const unavailable = selected.filter((runner) => !executors.some((item) => item.id === runner && item.available))
|
|
@@ -767,9 +892,12 @@ window.__ModuleLoader__.load({
|
|
|
767
892
|
? translateLocaleText('默认 {executor};交叉审查:{reviewers}',
|
|
768
893
|
{ executor: reviewerLabel(card.runner), reviewers: additional.map(reviewerLabel).join('、') })
|
|
769
894
|
: translateLocaleText('{executor} 默认执行', { executor: reviewerLabel(card.runner) })
|
|
770
|
-
|
|
895
|
+
const summary = unavailable.length
|
|
771
896
|
? `${selectedSummary};${unavailable.map(reviewerLabel).join('、')} 暂不可用`
|
|
772
897
|
: selectedSummary
|
|
898
|
+
return card.owner_approval_required !== false
|
|
899
|
+
&& selected.some((id) => ['codex', 'claude-code'].includes(id))
|
|
900
|
+
? translateLocaleText('{summary};启动前需要主人审批', { summary }) : summary
|
|
773
901
|
}
|
|
774
902
|
const requesterSummary = senderScope === 'allowlist'
|
|
775
903
|
? translateLocaleText('仅指定成员:{member_ids}',
|
|
@@ -780,9 +908,8 @@ window.__ModuleLoader__.load({
|
|
|
780
908
|
const ruleSummaryCurrent = recognizedDescriptionRef.current === ruleDescription.trim()
|
|
781
909
|
const ruleSummaryPending = recognitionBusy
|
|
782
910
|
const ruleSummaryDirty = Boolean(ruleDescription.trim() && !ruleSummaryCurrent)
|
|
783
|
-
const
|
|
784
|
-
|
|
785
|
-
&& (title || intent || ruleTriggerSummary.length || parameters.length || taskPrompt))
|
|
911
|
+
const ruleSummaryReady = Boolean(!ruleSummaryPending && ruleSummaryCurrent
|
|
912
|
+
&& (title || intent || parameters.length || taskPrompt))
|
|
786
913
|
const missingHarnessInputs = [...new Set([
|
|
787
914
|
...missingRequired,
|
|
788
915
|
...(parameters.some((rule) => rule.kind === 'gitea_pr'
|
|
@@ -815,7 +942,7 @@ window.__ModuleLoader__.load({
|
|
|
815
942
|
? `请求时还需提供:${parameters.map(parameterSummary).join(';')}`
|
|
816
943
|
: '无需额外参数,按请求中的内容处理'
|
|
817
944
|
const ruleSummaryNarrative = ruleSummaryReady
|
|
818
|
-
? `${
|
|
945
|
+
? `${intent.trim() || title.trim()}${taskPrompt.trim() ? `;${taskPrompt.trim()}` : ''};${recognizedInputsSummary}`
|
|
819
946
|
: '识别后会用一段摘要呈现系统理解的任务。'
|
|
820
947
|
const executorSummary = translateLocaleText(runner === 'codex' ? 'DSH Codex 子 Agent'
|
|
821
948
|
: runner === 'claude-code' ? 'DSH Claude Code 子 Agent' : 'DSH · 当前授权模型'
|
|
@@ -823,13 +950,14 @@ window.__ModuleLoader__.load({
|
|
|
823
950
|
const cardBlockReason = enrollmentLoadError ? '暂时无法核对机器人是否已启用协作,请刷新状态后再试。'
|
|
824
951
|
: !botConnected || !selectedEnrollmentBound ? '先在上方把机器人绑定到这台 DSH。'
|
|
825
952
|
: loadFailures.includes('settings') || defaults?.task_rule_schema_version !== 3
|
|
826
|
-
? '
|
|
953
|
+
? '团队服务端尚未支持 AI 任务 Harness 与运行时授权核验;请先升级 ANT 服务端。'
|
|
827
954
|
: !ruleDescription.trim() ? '请先用自然语言描述要做的任务。'
|
|
955
|
+
: recognitionBusy ? 'AI 正在检查当前描述;分析完成后再开放。'
|
|
828
956
|
: recognizedDescriptionRef.current !== ruleDescription.trim()
|
|
829
|
-
?
|
|
957
|
+
? '描述已修改,请点击“立即分析”更新任务理解后再开放。'
|
|
830
958
|
: !title.trim() || !intent.trim() || !taskPrompt.trim() ? '等待 AI 生成任务理解和处理要求。'
|
|
831
|
-
:
|
|
832
|
-
|
|
959
|
+
: usesExternalExecutor && ownerApprovalRequired && !ownerApprovalBound
|
|
960
|
+
? '这项任务需要主人审批,请先绑定审批私聊,或取消“启动前需要主人审批”。'
|
|
833
961
|
: conversationKind === 'group' && loadFailures.includes('groups') ? '暂时无法核对目标群,请刷新状态后再试。'
|
|
834
962
|
: conversationKind === 'group' && !groupId ? '请在“请求范围”中选择目标群;若列表为空,让群成员先 @机器人,再刷新群列表。'
|
|
835
963
|
: senderScope === 'allowlist' && !senderIds.trim() ? '请填写可请求成员的 IM ID。'
|
|
@@ -867,16 +995,16 @@ window.__ModuleLoader__.load({
|
|
|
867
995
|
setRecognitionMessage('')
|
|
868
996
|
recognizedBaselineRef.current = null
|
|
869
997
|
recognizedDescriptionRef.current = null
|
|
998
|
+
cancelRecognitionRequest()
|
|
870
999
|
recognitionRequestRef.current += 1
|
|
871
1000
|
setRuleDescription('')
|
|
872
|
-
setTriggerPhrases('')
|
|
873
1001
|
setMissingRequired([]); setSuggestions([]); setRuleExamples([])
|
|
874
1002
|
setTitle(''); setIntent(''); setTaskPrompt(''); setRunner('dsh')
|
|
875
|
-
setReviewers(['dsh'])
|
|
1003
|
+
setReviewers(['dsh']); setOwnerApprovalRequired(true)
|
|
876
1004
|
setParameters([]); setConversationKind(context.conversationKind || 'dm')
|
|
877
1005
|
setGroupId(context.groupId || '')
|
|
878
1006
|
setSenderScope('any'); setSenderIds(''); setDays(7); setCreateOpen(true)
|
|
879
|
-
if (context.botId)
|
|
1007
|
+
if (context.botId) selectBot(context.botId)
|
|
880
1008
|
}
|
|
881
1009
|
const taskRuleDraftFromCard = (card, title) => {
|
|
882
1010
|
const rules = (card.parameters || []).map((rule) => ({
|
|
@@ -895,32 +1023,31 @@ window.__ModuleLoader__.load({
|
|
|
895
1023
|
else if (headIndex < 0 && rules.length < 12) rules.push(headRule)
|
|
896
1024
|
}
|
|
897
1025
|
return { title: title || '', intent: card.intent || '', task_prompt: card.task_prompt || '',
|
|
898
|
-
trigger_phrases: Array.isArray(card.trigger_phrases) ? card.trigger_phrases.map((item) => item.trim()).filter(Boolean) : [],
|
|
899
1026
|
parameters: rules, examples: Array.isArray(card.examples) ? card.examples : [],
|
|
900
1027
|
missing_required: [], suggestions: [] }
|
|
901
1028
|
}
|
|
902
1029
|
const editCard = (card, copy = false) => {
|
|
903
1030
|
setSubmissionError('')
|
|
904
|
-
|
|
1031
|
+
const cardBotId = card.bot_id || bots.find((bot) => bot.fingerprint === card.bot_fingerprint)?.bot_id || ''
|
|
1032
|
+
selectBot(cardBotId)
|
|
905
1033
|
setRecognitionMessage('')
|
|
1034
|
+
cancelRecognitionRequest()
|
|
906
1035
|
recognitionRequestRef.current += 1
|
|
907
1036
|
setEditingCardId(copy ? null : card.id); setEditingExpiresAt(copy ? null : card.expires_at)
|
|
908
1037
|
const taskDraft = taskRuleDraftFromCard(card, copy ? `${card.title.slice(0, 94)}(副本)` : card.title)
|
|
909
1038
|
recognizedBaselineRef.current = taskDraft
|
|
910
1039
|
const existingDescription = card.rule_description || ''
|
|
911
1040
|
recognizedDescriptionRef.current = existingDescription.trim() || null
|
|
912
|
-
setDraftNotice(
|
|
913
|
-
|
|
914
|
-
: existingDescription.trim() ? ''
|
|
915
|
-
: '这项旧任务没有自然语言描述。请补充任务描述,让 AI 生成 Harness 后再开放。')
|
|
1041
|
+
setDraftNotice(existingDescription.trim() ? ''
|
|
1042
|
+
: translateLocaleText('这项旧任务没有自然语言描述。请补充任务描述,让 AI 生成 Harness 后再开放。'))
|
|
916
1043
|
setRuleDescription(existingDescription)
|
|
917
|
-
setTriggerPhrases(taskDraft.trigger_phrases.join('\n'))
|
|
918
1044
|
setMissingRequired([]); setSuggestions(taskDraft.suggestions); setRuleExamples(taskDraft.examples)
|
|
919
1045
|
setTitle(taskDraft.title)
|
|
920
1046
|
if (copy) setDays(7)
|
|
921
1047
|
setIntent(taskDraft.intent)
|
|
922
1048
|
setTaskPrompt(taskDraft.task_prompt); setRunner(card.runner)
|
|
923
1049
|
setReviewers(Array.isArray(card.reviewers) && card.reviewers.length ? card.reviewers : [card.runner])
|
|
1050
|
+
setOwnerApprovalRequired(card.owner_approval_required !== false)
|
|
924
1051
|
setParameters(taskDraft.parameters)
|
|
925
1052
|
setConversationKind(card.conversation_kind); setGroupId(card.conversation_id || '')
|
|
926
1053
|
setSenderScope(card.sender_scope); setSenderIds((card.sender_ids || []).join(', '))
|
|
@@ -929,7 +1056,6 @@ window.__ModuleLoader__.load({
|
|
|
929
1056
|
const applyRuleDraft = (draft) => {
|
|
930
1057
|
setTitle(draft.title)
|
|
931
1058
|
setIntent(draft.intent)
|
|
932
|
-
setTriggerPhrases(draft.trigger_phrases.join('\n'))
|
|
933
1059
|
setTaskPrompt(draft.task_prompt)
|
|
934
1060
|
setParameters(draft.parameters)
|
|
935
1061
|
setMissingRequired(draft.missing_required || [])
|
|
@@ -942,19 +1068,24 @@ window.__ModuleLoader__.load({
|
|
|
942
1068
|
setRecognitionMessage('先描述这项任务何时触发、要做什么以及涉及哪些资料。')
|
|
943
1069
|
return
|
|
944
1070
|
}
|
|
1071
|
+
cancelRecognitionRequest()
|
|
945
1072
|
const requestNumber = ++recognitionRequestRef.current
|
|
1073
|
+
const controller = new AbortController()
|
|
1074
|
+
let timedOut = false
|
|
1075
|
+
recognitionControllerRef.current = controller
|
|
1076
|
+
const timeout = setTimeout(() => { timedOut = true; controller.abort() }, 190_000)
|
|
946
1077
|
setRecognitionBusy(true); setMessage(''); setRecognitionMessage('AI 正在分析必需范围、可选条件和执行方式…')
|
|
947
1078
|
try {
|
|
948
1079
|
const response = await fetch('/api/ant-teams/delegation/recognize', {
|
|
949
1080
|
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
950
1081
|
body: JSON.stringify({ description: requestedDescription }),
|
|
1082
|
+
signal: controller.signal,
|
|
951
1083
|
})
|
|
952
1084
|
const result = await readLocalJsonResponse(response)
|
|
953
1085
|
if (!response.ok) throw new Error(result.message || '规则识别失败。')
|
|
954
1086
|
const candidate = {
|
|
955
1087
|
title: typeof result.title === 'string' ? result.title : '',
|
|
956
1088
|
intent: typeof result.intent === 'string' ? result.intent : '',
|
|
957
|
-
trigger_phrases: Array.isArray(result.trigger_phrases) ? result.trigger_phrases : [],
|
|
958
1089
|
task_prompt: typeof result.task_prompt === 'string' ? result.task_prompt : '',
|
|
959
1090
|
missing_required: Array.isArray(result.missing_required) ? result.missing_required : [],
|
|
960
1091
|
suggestions: Array.isArray(result.suggestions) ? result.suggestions : [],
|
|
@@ -987,8 +1118,8 @@ window.__ModuleLoader__.load({
|
|
|
987
1118
|
}
|
|
988
1119
|
}
|
|
989
1120
|
if (requestNumber !== recognitionRequestRef.current || requestedDescription !== ruleDescription.trim()) return
|
|
990
|
-
if (!candidate.title || !candidate.intent || !candidate.task_prompt
|
|
991
|
-
throw new Error('AI
|
|
1121
|
+
if (!candidate.title || !candidate.intent || !candidate.task_prompt) {
|
|
1122
|
+
throw new Error('AI 返回的任务理解不完整。请补充描述后重试。')
|
|
992
1123
|
}
|
|
993
1124
|
const previous = recognizedBaselineRef.current
|
|
994
1125
|
applyRuleDraft(candidate)
|
|
@@ -998,8 +1129,11 @@ window.__ModuleLoader__.load({
|
|
|
998
1129
|
? 'AI 已更新任务理解。请复核必需信息、可选建议和运行方式;任务仍未开放。'
|
|
999
1130
|
: 'AI 已生成待复核的任务 Harness。检查摘要和范围后再开放。')
|
|
1000
1131
|
} catch (error) {
|
|
1001
|
-
if (requestNumber === recognitionRequestRef.current) setRecognitionMessage(
|
|
1132
|
+
if (requestNumber === recognitionRequestRef.current) setRecognitionMessage(timedOut
|
|
1133
|
+
? translateLocaleText('AI 分析等待超时,请检查团队模型后重试。') : error.message)
|
|
1002
1134
|
} finally {
|
|
1135
|
+
clearTimeout(timeout)
|
|
1136
|
+
if (recognitionControllerRef.current === controller) recognitionControllerRef.current = null
|
|
1003
1137
|
if (requestNumber === recognitionRequestRef.current) setRecognitionBusy(false)
|
|
1004
1138
|
}
|
|
1005
1139
|
}
|
|
@@ -1033,17 +1167,33 @@ window.__ModuleLoader__.load({
|
|
|
1033
1167
|
}
|
|
1034
1168
|
const requestOwnerApprovalBinding = async () => {
|
|
1035
1169
|
if (!selectedBot || !selectedEnrollmentBound) return
|
|
1036
|
-
|
|
1170
|
+
const requestedBot = selectedBot
|
|
1171
|
+
setBusy(true); clearOwnerChallenge()
|
|
1037
1172
|
try {
|
|
1038
1173
|
const response = await fetch('/api/ant-teams/delegation/owner-challenge', {
|
|
1039
1174
|
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
1040
|
-
body: JSON.stringify({ bot_id:
|
|
1175
|
+
body: JSON.stringify({ bot_id: requestedBot.bot_id, bot_fingerprint: requestedBot.fingerprint }),
|
|
1041
1176
|
})
|
|
1042
1177
|
const result = await readLocalJsonResponse(response)
|
|
1178
|
+
if (botIdRef.current !== requestedBot.bot_id) {
|
|
1179
|
+
await fetch(`/api/ant-teams/delegation/owner-challenge?bot_fingerprint=${encodeURIComponent(requestedBot.fingerprint)}`,
|
|
1180
|
+
{ method: 'DELETE', cache: 'no-store' }).catch(() => null)
|
|
1181
|
+
return
|
|
1182
|
+
}
|
|
1043
1183
|
if (!response.ok || typeof result.code !== 'string') throw new Error(result.message || '审批私聊绑定码获取失败。')
|
|
1184
|
+
// The server owns challenge validity. Use a response-relative local display window so clock skew
|
|
1185
|
+
// cannot make a valid server challenge disappear before the owner can use it.
|
|
1186
|
+
const expiresAt = Date.now() + 5 * 60_000
|
|
1187
|
+
const ownerChallenge = { fingerprint: requestedBot.fingerprint, ownerWasBound: ownerApprovalBound, expiresAt }
|
|
1188
|
+
ownerChallengeMetaRef.current = ownerChallenge
|
|
1044
1189
|
setOwnerChallengeCode(result.code)
|
|
1045
|
-
|
|
1046
|
-
|
|
1190
|
+
ownerChallengeTimerRef.current = setTimeout(() => {
|
|
1191
|
+
if (ownerChallengeMetaRef.current !== ownerChallenge) return
|
|
1192
|
+
clearOwnerChallenge()
|
|
1193
|
+
setMessage(translateLocaleText('绑定码已过期,请重新生成。'))
|
|
1194
|
+
}, Math.max(0, expiresAt - Date.now()))
|
|
1195
|
+
setMessage('绑定码已生成;请按“私聊入口”中的提示完成绑定。')
|
|
1196
|
+
} catch (error) { if (botIdRef.current === requestedBot.bot_id) setMessage(error.message) }
|
|
1047
1197
|
finally { setBusy(false) }
|
|
1048
1198
|
}
|
|
1049
1199
|
const saveGroupChat = async (enabled, targetGroupId) => {
|
|
@@ -1140,6 +1290,15 @@ window.__ModuleLoader__.load({
|
|
|
1140
1290
|
...collaborationGridStyle, gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 420px), 1fr))',
|
|
1141
1291
|
}
|
|
1142
1292
|
const collaborationCardStyle = { ...settingsPanelStyle, padding: 12, gap: 10 }
|
|
1293
|
+
const capabilityStepStyle = { display: 'grid', gap: 12, borderTop: '1px solid var(--dsw-alias-border-l2)', paddingTop: 14 }
|
|
1294
|
+
const capabilityStepHeading = (number, title, description) => h('div', { style: {
|
|
1295
|
+
display: 'flex', gap: 10, alignItems: 'flex-start',
|
|
1296
|
+
} },
|
|
1297
|
+
h('span', { style: { display: 'grid', placeItems: 'center', width: 26, height: 26, flex: '0 0 auto',
|
|
1298
|
+
borderRadius: '50%', backgroundColor: 'var(--dsw-alias-state-business-tertiary)',
|
|
1299
|
+
color: 'var(--dsw-alias-state-business-primary)', fontWeight: 700 } }, number),
|
|
1300
|
+
h('div', { style: { display: 'grid', gap: 3 } },
|
|
1301
|
+
h('strong', null, title), h('p', { style: mutedTextStyle }, description)))
|
|
1143
1302
|
const setupStepper = h('section', { 'aria-label': '首次设置引导', style: { display: 'grid', gap: 6 } },
|
|
1144
1303
|
h('p', { style: mutedTextStyle }, '首次设置可按顺序完成;你也可以直接切换页面。普通聊天不需要先开放任务。'),
|
|
1145
1304
|
h('ol', { 'aria-label': '设置步骤', style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 150px), 1fr))',
|
|
@@ -1172,7 +1331,26 @@ window.__ModuleLoader__.load({
|
|
|
1172
1331
|
disabled: busy || !selectedBot || !selectedEnrollmentBound || enrollmentLoadError || !dmChatSettingAvailable,
|
|
1173
1332
|
onChange: (event) => void saveDmChat(event.target.checked), 'aria-label': '允许机器人私聊' }),
|
|
1174
1333
|
h('strong', null, '允许私聊普通聊天'), h('span', { style: mutedTextStyle }, dmChatStatus)),
|
|
1175
|
-
h('p', { style: mutedTextStyle }, dmChatDescription)
|
|
1334
|
+
h('p', { style: mutedTextStyle }, dmChatDescription),
|
|
1335
|
+
selectedEnrollmentBound && botConnected ? h('section', { role: 'group', 'aria-label': '主人审批私聊', style: {
|
|
1336
|
+
display: 'grid', gap: 9, padding: 12, border: '1px solid var(--dsw-alias-state-business-tertiary)',
|
|
1337
|
+
borderRadius: 10, backgroundColor: 'var(--dsw-alias-state-business-tertiary)',
|
|
1338
|
+
} },
|
|
1339
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8 } },
|
|
1340
|
+
h('strong', null, '主人审批私聊'),
|
|
1341
|
+
h('span', { style: { ...mutedTextStyle, padding: '3px 8px', borderRadius: 999,
|
|
1342
|
+
backgroundColor: 'var(--dsw-alias-bg-layer-1)' } }, ownerApprovalBound ? '已绑定' : '未绑定')),
|
|
1343
|
+
h('p', { style: mutedTextStyle }, ownerApprovalBound
|
|
1344
|
+
? '没有匹配任务卡的明确任务会先发给你逐次审批;批准前不会启动执行器。'
|
|
1345
|
+
: '绑定一次后,没有匹配任务卡的明确任务会先发给你审批。发送验证码后,点击“刷新状态”确认绑定。'),
|
|
1346
|
+
h('div', { style: { display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' } },
|
|
1347
|
+
h('button', { type: 'button', disabled: busy,
|
|
1348
|
+
onClick: () => void requestOwnerApprovalBinding(), style: secondaryButtonStyle },
|
|
1349
|
+
ownerApprovalBound ? '重新绑定审批私聊' : '生成绑定码'),
|
|
1350
|
+
ownerChallengeCode ? h('span', { style: mutedTextStyle }, '有效期 5 分钟') : null),
|
|
1351
|
+
ownerChallengeCode ? h('div', { role: 'status', style: { ...feedbackNoticeStyle, position: 'static' } },
|
|
1352
|
+
translateLocaleText('请在 5 分钟内私聊此机器人发送完整指令:“绑定审批身份 {code}”。如果这台 DSH 未重启,也可直接发送 12 位验证码 {code}。',
|
|
1353
|
+
{ code: ownerChallengeCode })) : null) : null)
|
|
1176
1354
|
const createCard = async (event) => {
|
|
1177
1355
|
event.preventDefault()
|
|
1178
1356
|
setDraftNotice('')
|
|
@@ -1193,11 +1371,6 @@ window.__ModuleLoader__.load({
|
|
|
1193
1371
|
|| bytes(intent.trim()) > 1000 || bytes(taskPrompt.trim()) > 4000) {
|
|
1194
1372
|
reject('任务描述或识别摘要过长,请缩短描述并重新识别。'); return
|
|
1195
1373
|
}
|
|
1196
|
-
const triggerLines = triggerPhrases.split('\n').map((item) => item.normalize('NFKC').trim()).filter(Boolean)
|
|
1197
|
-
if (!triggerLines.length || triggerLines.length > 8 || triggerLines.some((item) => bytes(item) > 120)
|
|
1198
|
-
|| new Set(triggerLines.map((item) => item.toLowerCase())).size !== triggerLines.length) {
|
|
1199
|
-
reject('任务没有明确、唯一的触发开头;请在描述中写清楚消息以什么短语开头,再重新分析。'); return
|
|
1200
|
-
}
|
|
1201
1374
|
const normalized = parameters.map((rule) => ({ name: rule.name.trim(), kind: rule.kind,
|
|
1202
1375
|
description: rule.description.trim() || null,
|
|
1203
1376
|
required: rule.required, min: rule.min == null || rule.min === '' ? null : Number(rule.min),
|
|
@@ -1261,8 +1434,6 @@ window.__ModuleLoader__.load({
|
|
|
1261
1434
|
: translateLocaleText('私聊')
|
|
1262
1435
|
const resultDestination = translateLocaleText(conversationKind === 'group'
|
|
1263
1436
|
? '原群(群内可见)' : '发起任务的私聊')
|
|
1264
|
-
const triggerSummary = translateLocaleText('只有请求以“{triggers}”开头时才会触发此任务',
|
|
1265
|
-
{ triggers: triggerLines.join('”或“') })
|
|
1266
1437
|
const executionDetails = {
|
|
1267
1438
|
dsh: translateLocaleText('DSH 使用获准的团队模型处理对话内容和 ANT 已核验后提供的资源。'),
|
|
1268
1439
|
codex: translateLocaleText('Codex 使用这台 DSH 的 Provider Profile 权限;可访问范围由 Codex/DSH 本机配置决定。任务描述不会限制操作系统权限。'),
|
|
@@ -1274,10 +1445,10 @@ window.__ModuleLoader__.load({
|
|
|
1274
1445
|
details: reviewers.map((item) => executionDetails[item]).join(' ') })
|
|
1275
1446
|
: executionDetails[reviewers[0]]
|
|
1276
1447
|
const actionDescription = editingCardId
|
|
1277
|
-
? translateLocaleText('更新“{title}”后,{requesterSummary}可在{scope}{
|
|
1278
|
-
{ title, requesterSummary, scope,
|
|
1279
|
-
: translateLocaleText('开放“{title}”后,{requesterSummary}可在{scope}
|
|
1280
|
-
{ title, requesterSummary, scope,
|
|
1448
|
+
? translateLocaleText('更新“{title}”后,{requesterSummary}可在{scope}按任务描述中的规则发起请求。旧待确认请求将失效,已提交任务继续使用提交时的配置。结果{resultDestination}。到期时间保持为 {expiry}。',
|
|
1449
|
+
{ title, requesterSummary, scope, resultDestination, expiry: formatLocaleDate(editingExpiresAt * 1000) })
|
|
1450
|
+
: translateLocaleText('开放“{title}”后,{requesterSummary}可在{scope}按任务描述中的规则发起请求,有效期 {days} 天;结果{resultDestination}。',
|
|
1451
|
+
{ title, requesterSummary, scope, resultDestination, days: formatLocaleNumber(days) })
|
|
1281
1452
|
if (!confirmLocalized(`${actionDescription}${executionInfo}${translateLocaleText('请确认。')}`)) return
|
|
1282
1453
|
setBusy(true)
|
|
1283
1454
|
try {
|
|
@@ -1285,9 +1456,10 @@ window.__ModuleLoader__.load({
|
|
|
1285
1456
|
method: editingCardId ? 'PUT' : 'POST', headers: { 'content-type': 'application/json' },
|
|
1286
1457
|
body: JSON.stringify({ ...(editingCardId ? { id: editingCardId } : {}),
|
|
1287
1458
|
bot_id: selectedBot.bot_id, bot_fingerprint: selectedBot.fingerprint,
|
|
1288
|
-
title, rule_description: ruleDescription.trim(), trigger_phrases:
|
|
1459
|
+
title, rule_description: ruleDescription.trim(), trigger_phrases: [],
|
|
1289
1460
|
intent, examples: ruleExamples,
|
|
1290
|
-
parameters: normalized, task_prompt: taskPrompt, runner, reviewers,
|
|
1461
|
+
parameters: normalized, task_prompt: taskPrompt, runner, reviewers,
|
|
1462
|
+
owner_approval_required: ownerApprovalRequired, workspace: null,
|
|
1291
1463
|
sender_scope: senderScope, sender_ids: senderScope === 'allowlist'
|
|
1292
1464
|
? senderIds.split(/[\s,,]+/u).filter(Boolean) : [], conversation_kind: conversationKind,
|
|
1293
1465
|
...(conversationKind === 'group' ? { conversation_id: groupId } : {}),
|
|
@@ -1295,7 +1467,7 @@ window.__ModuleLoader__.load({
|
|
|
1295
1467
|
})
|
|
1296
1468
|
const result = await readLocalJsonResponse(response)
|
|
1297
1469
|
if (!response.ok && result.code === 'IM_TASK_RULE_REQUIRED') {
|
|
1298
|
-
throw new Error('当前 ANT
|
|
1470
|
+
throw new Error('当前 ANT 服务端版本过旧,请升级服务端后再开放任务。')
|
|
1299
1471
|
}
|
|
1300
1472
|
if (!response.ok && reviewers.includes('claude-code') && result.code === 'INVALID_REQUEST') {
|
|
1301
1473
|
throw new Error('当前团队服务端可能尚不支持 Claude Code 审查配置。请管理员升级服务端后刷新重试。')
|
|
@@ -1303,24 +1475,35 @@ window.__ModuleLoader__.load({
|
|
|
1303
1475
|
if (!response.ok) throw new Error(result.message || '能力保存失败。')
|
|
1304
1476
|
const savedReviewers = Array.isArray(result.reviewers) && result.reviewers.length
|
|
1305
1477
|
? result.reviewers : [result.runner]
|
|
1306
|
-
|
|
1307
|
-
|
|
1478
|
+
const reviewersDropped = [...savedReviewers].sort().join('\0') !== [...reviewers].sort().join('\0')
|
|
1479
|
+
const approvalDropped = usesExternalExecutor && ownerApprovalRequired && result.owner_approval_required !== true
|
|
1480
|
+
if (reviewersDropped || approvalDropped) {
|
|
1481
|
+
if ((!editingCardId || approvalDropped) && typeof result.id === 'string') {
|
|
1308
1482
|
const cleanup = await fetch('/api/ant-teams/delegation/cards', {
|
|
1309
1483
|
method: 'DELETE', headers: { 'content-type': 'application/json' },
|
|
1310
1484
|
body: JSON.stringify({ id: result.id }),
|
|
1311
1485
|
}).catch(() => null)
|
|
1312
1486
|
if (!cleanup?.ok) {
|
|
1313
1487
|
await load().catch(() => undefined)
|
|
1314
|
-
throw new Error(
|
|
1488
|
+
throw new Error(reviewersDropped
|
|
1489
|
+
? '团队服务端尚不支持多 Agent 审查配置;旧服务端可能已保存单 Agent 授权,请刷新列表并停止它,再升级 ANT 服务端。'
|
|
1490
|
+
: '团队服务端尚不支持任务卡主人审批;旧服务端可能已保存不经审批的授权,请刷新列表并停止它,再升级 ANT 服务端。')
|
|
1491
|
+
}
|
|
1492
|
+
if (editingCardId) {
|
|
1493
|
+
setEditingCardId(null); setEditingExpiresAt(null)
|
|
1494
|
+
await load().catch(() => undefined)
|
|
1315
1495
|
}
|
|
1316
1496
|
}
|
|
1317
|
-
throw new Error(
|
|
1497
|
+
throw new Error(reviewersDropped
|
|
1498
|
+
? '当前团队服务端尚不支持多 Agent 审查配置;请管理员升级 ANT 服务端后重试。'
|
|
1499
|
+
: '当前团队服务端尚不支持任务卡主人审批;请管理员升级 ANT 服务端后重试。')
|
|
1318
1500
|
}
|
|
1319
1501
|
recognizedBaselineRef.current = null
|
|
1320
1502
|
recognizedDescriptionRef.current = null
|
|
1503
|
+
cancelRecognitionRequest()
|
|
1321
1504
|
recognitionRequestRef.current += 1
|
|
1322
1505
|
setRuleDescription(''); setMissingRequired([]); setSuggestions([]); setRuleExamples([])
|
|
1323
|
-
setTitle(''); setIntent(''); setTaskPrompt(''); setParameters([]); setRunner('dsh'); setReviewers(['dsh'])
|
|
1506
|
+
setTitle(''); setIntent(''); setTaskPrompt(''); setParameters([]); setRunner('dsh'); setReviewers(['dsh']); setOwnerApprovalRequired(true)
|
|
1324
1507
|
setEditingCardId(null); setEditingExpiresAt(null)
|
|
1325
1508
|
await load()
|
|
1326
1509
|
setCreateOpen(false)
|
|
@@ -1347,29 +1530,28 @@ window.__ModuleLoader__.load({
|
|
|
1347
1530
|
setEditingCardId(null); setEditingExpiresAt(null)
|
|
1348
1531
|
const copiedDescription = card.rule_description || ''
|
|
1349
1532
|
recognizedDescriptionRef.current = copiedDescription.trim() || null
|
|
1533
|
+
cancelRecognitionRequest()
|
|
1350
1534
|
recognitionRequestRef.current += 1
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
: copiedDescription.trim()
|
|
1355
|
-
? '已复制到新草稿。复制不保留原有效期;请检查任务描述和请求范围。'
|
|
1356
|
-
: '这项旧任务没有自然语言描述。请补充任务描述,让 AI 生成 Harness 后再开放。')
|
|
1535
|
+
setDraftNotice(copiedDescription.trim()
|
|
1536
|
+
? translateLocaleText('已复制到新草稿。复制不保留原有效期;请检查任务描述和请求范围。')
|
|
1537
|
+
: translateLocaleText('这项旧任务没有自然语言描述。请补充任务描述,让 AI 生成 Harness 后再开放。'))
|
|
1357
1538
|
const cardBot = bots.find((bot) => bot.fingerprint === card.bot_fingerprint)
|
|
1539
|
+
const copiedBotId = card.bot_id || cardBot?.bot_id || ''
|
|
1540
|
+
selectBot(copiedBotId)
|
|
1358
1541
|
const titleChars = Array.from(translateLocaleText('复制:{title}', {
|
|
1359
1542
|
title: card.title || translateLocaleText('未命名能力'),
|
|
1360
1543
|
}))
|
|
1361
1544
|
while (new TextEncoder().encode(titleChars.join('')).length > 100) titleChars.pop()
|
|
1362
1545
|
const taskDraft = taskRuleDraftFromCard(card, titleChars.join(''))
|
|
1363
1546
|
recognizedBaselineRef.current = taskDraft
|
|
1364
|
-
setBotId(card.bot_id || cardBot?.bot_id || '')
|
|
1365
1547
|
setRuleDescription(copiedDescription)
|
|
1366
|
-
setTriggerPhrases(taskDraft.trigger_phrases.join('\n'))
|
|
1367
1548
|
setMissingRequired([]); setSuggestions(taskDraft.suggestions); setRuleExamples(taskDraft.examples)
|
|
1368
1549
|
setTitle(taskDraft.title)
|
|
1369
1550
|
setIntent(taskDraft.intent)
|
|
1370
1551
|
setTaskPrompt(taskDraft.task_prompt)
|
|
1371
1552
|
setRunner(['dsh', 'codex', 'claude-code'].includes(card.runner) ? card.runner : 'dsh')
|
|
1372
1553
|
setReviewers(Array.isArray(card.reviewers) && card.reviewers.length ? card.reviewers : [card.runner || 'dsh'])
|
|
1554
|
+
setOwnerApprovalRequired(card.owner_approval_required !== false)
|
|
1373
1555
|
setParameters(taskDraft.parameters)
|
|
1374
1556
|
setConversationKind(card.conversation_kind === 'group' ? 'group' : 'dm')
|
|
1375
1557
|
setGroupId(card.conversation_id || '')
|
|
@@ -1399,10 +1581,10 @@ window.__ModuleLoader__.load({
|
|
|
1399
1581
|
loading ? '读取中…' : '刷新状态')),
|
|
1400
1582
|
h('p', { role: 'status', style: mutedTextStyle }, collaborationStatus),
|
|
1401
1583
|
bots.length ? h('label', null, '我的机器人', h('select', { id: 'ant-bot-selector', value: botId,
|
|
1402
|
-
onChange: (event) => {
|
|
1584
|
+
onChange: (event) => { selectBot(event.target.value); setGroupId('') }, style: inputStyle },
|
|
1403
1585
|
h('option', { value: '' }, '请选择机器人'),
|
|
1404
1586
|
...bots.map((bot) => h('option', { key: bot.bot_id, value: bot.bot_id,
|
|
1405
|
-
disabled: !bot.available || !bot.connected }, rawLocaleText(bot.name),
|
|
1587
|
+
disabled: !bot.available || !bot.connected }, rawLocaleText(bot.name), ' · ', botPlatformLabel(bot),
|
|
1406
1588
|
bot.available && bot.connected ? null : '(未连接)')))) : null,
|
|
1407
1589
|
connectedBots.length === 0 && !loading && !botLoadError ? h('div', { style: { display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' } },
|
|
1408
1590
|
h('button', { type: 'button', onClick: openImSettings, style: secondaryButtonStyle }, '打开 DSH-IM 设置'),
|
|
@@ -1412,15 +1594,8 @@ window.__ModuleLoader__.load({
|
|
|
1412
1594
|
selectedBot && !selectedEnrollmentBound ? h('button', { type: 'button', disabled: busy || !botConnected || enrollmentLoadError,
|
|
1413
1595
|
onClick: () => void enrollBot(), style: primaryButtonStyle },
|
|
1414
1596
|
selectedEnrollment ? '绑定原协作配置到这台 DSH' : '启用并绑定到这台 DSH') : null,
|
|
1415
|
-
selectedEnrollmentBound && botConnected ? h('
|
|
1416
|
-
|
|
1417
|
-
? '审批私聊已绑定;没有匹配任务卡的明确任务会发给你逐次审批。'
|
|
1418
|
-
: '绑定一次你的 IM 私聊身份后,就能审批没有匹配任务卡的明确任务。'),
|
|
1419
|
-
h('button', { type: 'button', disabled: busy,
|
|
1420
|
-
onClick: () => void requestOwnerApprovalBinding(), style: secondaryButtonStyle },
|
|
1421
|
-
ownerApprovalBound ? '重新绑定审批私聊' : '绑定审批私聊'),
|
|
1422
|
-
ownerChallengeCode ? h('p', { role: 'status', style: feedbackNoticeStyle },
|
|
1423
|
-
`请在 5 分钟内私聊此机器人发送:绑定审批身份 ${ownerChallengeCode}`) : null) : null)
|
|
1597
|
+
selectedEnrollmentBound && botConnected ? h('p', { style: mutedTextStyle },
|
|
1598
|
+
'任务能力和设备绑定状态请在此处查看;主人审批身份在“私聊入口”中管理。') : null)
|
|
1424
1599
|
const groupTaskStatusPanel = selectedEnrollmentBound && botConnected ? h('section', {
|
|
1425
1600
|
id: 'ant-group-task-status', role: 'region', 'aria-label': '各群聊天与任务状态', tabIndex: -1,
|
|
1426
1601
|
style: { ...collaborationCardStyle, gap: 10 } },
|
|
@@ -1465,7 +1640,8 @@ window.__ModuleLoader__.load({
|
|
|
1465
1640
|
h('h3', { style: sectionTitleStyle }, '协作总览'),
|
|
1466
1641
|
h('p', { style: mutedTextStyle }, '选择机器人并绑定一次主人审批私聊。任务卡可预授权常用任务;其他请求由主人逐次决定。')),
|
|
1467
1642
|
h('span', { style: { ...mutedTextStyle, padding: '4px 9px', borderRadius: 999,
|
|
1468
|
-
backgroundColor: 'var(--dsw-alias-bg-layer-2)' } }, selectedBot
|
|
1643
|
+
backgroundColor: 'var(--dsw-alias-bg-layer-2)' } }, selectedBot
|
|
1644
|
+
? h('span', null, rawLocaleText(selectedBot.name), ' · ', botPlatformLabel(selectedBot)) : '尚未选择机器人')),
|
|
1469
1645
|
loadFailures.length ? h('p', { role: 'status', style: mutedTextStyle },
|
|
1470
1646
|
`部分协作信息暂时无法读取:${loadFailures.map((path) => loadNames[path]).join('、')}。已读取的信息仍显示,请稍后刷新状态。`) : null,
|
|
1471
1647
|
h('div', { id: 'ant-collaboration-settings-grid', style: collaborationSettingsGridStyle },
|
|
@@ -1501,14 +1677,12 @@ window.__ModuleLoader__.load({
|
|
|
1501
1677
|
: translateLocaleText('私聊')
|
|
1502
1678
|
const requester = translateLocaleText(card.sender_scope === 'allowlist' ? '指定成员可请求'
|
|
1503
1679
|
: card.conversation_kind === 'group' ? '群成员可请求' : '可私聊者可请求')
|
|
1504
|
-
const missingTriggerPhrase = !Array.isArray(card.trigger_phrases) || card.trigger_phrases.length === 0
|
|
1505
1680
|
const configuredCardRunners = Array.isArray(card.reviewers) && card.reviewers.length
|
|
1506
1681
|
? card.reviewers : [card.runner]
|
|
1507
1682
|
const blockedExternalExecutor = !configuredCardRunners.every((runner) =>
|
|
1508
1683
|
executors.some((executor) => executor.id === runner && executor.available))
|
|
1509
1684
|
const examplesText = Array.isArray(card.examples) ? card.examples.join('、') : ''
|
|
1510
1685
|
const requesterIds = card.sender_scope === 'allowlist' && Array.isArray(card.sender_ids) ? card.sender_ids.join('、') : ''
|
|
1511
|
-
const triggerSummary = Array.isArray(card.trigger_phrases) ? card.trigger_phrases.join('、') : ''
|
|
1512
1686
|
const remainingDaysLabel = remainingDays <= 7 ? 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))'
|
|
1513
1687
|
: 'var(--dsw-alias-label-secondary)'
|
|
1514
1688
|
return h('article', { key: card.id, style: { ...collaborationCardStyle, padding: 12, gap: 8 } },
|
|
@@ -1526,9 +1700,6 @@ window.__ModuleLoader__.load({
|
|
|
1526
1700
|
blockedExternalExecutor ? h('p', { role: 'status', style: { ...mutedTextStyle,
|
|
1527
1701
|
color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } },
|
|
1528
1702
|
'此任务暂不可执行:这台 DSH 尚未启用所选的全部执行器。请启用对应的 DSH 子 Agent 提供方后刷新。') : null,
|
|
1529
|
-
missingTriggerPhrase ? h('p', { role: 'status', style: { ...mutedTextStyle,
|
|
1530
|
-
color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } },
|
|
1531
|
-
'这项旧任务没有明确触发开头,不会自动启动。点击“编辑”,重新分析并复核触发短语。') : null,
|
|
1532
1703
|
card.rule_description
|
|
1533
1704
|
? h('span', { style: { ...mutedTextStyle, overflowWrap: 'anywhere' } }, rawLocaleText(card.rule_description))
|
|
1534
1705
|
: h('span', { role: 'status', style: { ...mutedTextStyle, color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } },
|
|
@@ -1537,7 +1708,6 @@ window.__ModuleLoader__.load({
|
|
|
1537
1708
|
h('summary', { style: { cursor: 'pointer', color: 'var(--dsw-alias-link)', fontSize: 13 } }, '查看规则说明'),
|
|
1538
1709
|
h('div', { style: { display: 'grid', gap: 4, paddingTop: 6 } },
|
|
1539
1710
|
card.rule_description ? h('p', { style: { ...mutedTextStyle, overflowWrap: 'anywhere' } }, rawLocaleText(card.rule_description)) : null,
|
|
1540
|
-
triggerSummary ? h('p', { style: { ...mutedTextStyle, overflowWrap: 'anywhere' } }, '触发开头:', rawLocaleText(triggerSummary)) : null,
|
|
1541
1711
|
requesterIds ? h('p', { style: { ...mutedTextStyle, overflowWrap: 'anywhere' } }, '允许请求的 IM 用户:', rawLocaleText(requesterIds)) : null,
|
|
1542
1712
|
examplesText ? h('p', { style: mutedTextStyle }, '请求示例:', rawLocaleText(examplesText)) : null,
|
|
1543
1713
|
Array.isArray(card.parameters) && card.parameters.length
|
|
@@ -1568,9 +1738,10 @@ window.__ModuleLoader__.load({
|
|
|
1568
1738
|
? task.repository
|
|
1569
1739
|
? translateLocaleText('{repository} PR #{pr_number} 差异审查', { repository: task.repository, pr_number: task.pr_number })
|
|
1570
1740
|
: translateLocaleText('PR #{pr_number} 差异审查', { pr_number: task.pr_number })
|
|
1571
|
-
: '任务'
|
|
1741
|
+
: translateLocaleText('任务')
|
|
1572
1742
|
const status = translateLocaleText(task.outcome === 'canceled' ? '已取消' : task.outcome === 'failed' ? '未完成' : ({
|
|
1573
|
-
pending: '排队中',
|
|
1743
|
+
pending: '排队中', claimed: '排队中', running: '处理中', finishing: '结果待核对', completed: '已完成',
|
|
1744
|
+
reply_pending: '结果待发送', reply_attempted: '回复状态待核对', done: '已回复',
|
|
1574
1745
|
}[task.status] || task.status))
|
|
1575
1746
|
const createdAt = task.created_at && Number.isFinite(Date.parse(task.created_at))
|
|
1576
1747
|
? formatLocaleDate(task.created_at) : ''
|
|
@@ -1584,7 +1755,9 @@ window.__ModuleLoader__.load({
|
|
|
1584
1755
|
h('div', { style: mutedTextStyle }, taskStatusLine),
|
|
1585
1756
|
task.failure_reason ? h('div', { role: 'alert', style: warningNoticeStyle },
|
|
1586
1757
|
h('span', { style: { ...mutedTextStyle, whiteSpace: 'pre-line' } }, translateLocaleText(
|
|
1587
|
-
'
|
|
1758
|
+
task.status === 'finishing'
|
|
1759
|
+
? '结果状态待确认:{reason}\n处理建议:恢复原团队连接或审批私聊权限后刷新状态;不要重复发起相同任务。'
|
|
1760
|
+
: '任务未完成:{reason}\n处理建议:按错误提示修复配置或请求内容后,在原群或私聊重新发起。',
|
|
1588
1761
|
{ reason: task.failure_reason }))) : null,
|
|
1589
1762
|
task.delivery_error ? h('div', { role: 'alert', style: warningNoticeStyle },
|
|
1590
1763
|
h('span', { style: { ...mutedTextStyle, whiteSpace: 'pre-line' } }, translateLocaleText(
|
|
@@ -1593,7 +1766,8 @@ window.__ModuleLoader__.load({
|
|
|
1593
1766
|
task.status === 'reply_attempted' && !task.delivery_error ? h('div', { role: 'status', style: warningNoticeStyle },
|
|
1594
1767
|
h('span', { style: mutedTextStyle }, translateLocaleText(
|
|
1595
1768
|
'ANT 尚未确认消息平台是否收到结果。请先检查原群或私聊,再决定是否重新发起。'))) : null),
|
|
1596
|
-
['card_task', 'legacy_pr_task'
|
|
1769
|
+
['card_task', 'legacy_pr_task', 'owner_approval'].includes(task.kind)
|
|
1770
|
+
&& ['pending', 'claimed', 'running'].includes(task.status) ? h('button', { type: 'button', disabled: busy,
|
|
1597
1771
|
onClick: () => void cancelTask(task), style: secondaryButtonStyle }, '取消任务') : null)
|
|
1598
1772
|
})
|
|
1599
1773
|
: [h('p', { key: 'none', style: mutedTextStyle }, loadFailures.includes('status')
|
|
@@ -1601,11 +1775,21 @@ window.__ModuleLoader__.load({
|
|
|
1601
1775
|
h('div', { role: 'region', 'aria-label': '任务规则编辑器', style: { display: activeTab === 'collaboration' ? 'grid' : 'none' } },
|
|
1602
1776
|
h('details', { id: 'ant-new-task', open: createOpen,
|
|
1603
1777
|
onToggle: (event) => setCreateOpen(event.currentTarget.open),
|
|
1604
|
-
style: { border: '1px solid var(--dsw-alias-border-l2)', borderRadius:
|
|
1605
|
-
|
|
1778
|
+
style: { border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 12, padding: 16,
|
|
1779
|
+
backgroundColor: 'var(--dsw-alias-bg-layer-1)' } },
|
|
1780
|
+
h('summary', { id: 'ant-new-task-summary', style: { display: 'flex', justifyContent: 'space-between',
|
|
1781
|
+
alignItems: 'center', gap: 12, flexWrap: 'wrap', cursor: 'pointer', listStylePosition: 'inside' } },
|
|
1782
|
+
h('span', { 'aria-hidden': 'true', style: { color: 'var(--dsw-alias-label-secondary)', flex: '0 0 auto' } },
|
|
1783
|
+
createOpen ? '▾' : '▸'),
|
|
1784
|
+
h('span', { style: { display: 'grid', gap: 3 } },
|
|
1785
|
+
h('strong', null, editingCardId ? '编辑能力配置' : '开放一项能力'),
|
|
1786
|
+
h('span', { style: mutedTextStyle }, '任务卡可预授权常用任务;其他明确任务由主人逐次审批。')),
|
|
1787
|
+
h('span', { style: { ...mutedTextStyle, padding: '3px 8px', borderRadius: 999,
|
|
1788
|
+
backgroundColor: 'var(--dsw-alias-bg-layer-2)' } }, editingCardId
|
|
1789
|
+
? translateLocaleText('编辑中 · 尚未保存') : translateLocaleText('草稿 · 未开放'))),
|
|
1606
1790
|
h('div', { style: { display: 'grid', gridTemplateColumns: 'minmax(0, 1fr)',
|
|
1607
|
-
gap: 12, alignItems: 'start', marginTop:
|
|
1608
|
-
h('form', { id: 'ant-capability-create-form', onSubmit: createCard, style: { display: 'grid', gap:
|
|
1791
|
+
gap: 12, alignItems: 'start', marginTop: 14 } },
|
|
1792
|
+
h('form', { id: 'ant-capability-create-form', onSubmit: createCard, style: { display: 'grid', gap: 15, minWidth: 0 } },
|
|
1609
1793
|
editingCardId ? h('p', { role: 'status', style: mutedTextStyle },
|
|
1610
1794
|
`保存会生成新的授权版本并停止旧版本;到期时间保持不变:${formatLocaleDate(editingExpiresAt * 1000)}。已提交任务继续使用原配置。`) : null,
|
|
1611
1795
|
h('p', { style: mutedTextStyle }, selectedBot
|
|
@@ -1613,55 +1797,76 @@ window.__ModuleLoader__.load({
|
|
|
1613
1797
|
: '请先在上方选择机器人并启用协作。'),
|
|
1614
1798
|
draftNotice ? h('p', { role: 'status', style: { ...mutedTextStyle,
|
|
1615
1799
|
color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } }, draftNotice) : null,
|
|
1800
|
+
!ruleDescription.trim() && draftNotice
|
|
1801
|
+
=== translateLocaleText('这项旧任务没有自然语言描述。请补充任务描述,让 AI 生成 Harness 后再开放。') && title
|
|
1802
|
+
? h('p', { role: 'status', style: mutedTextStyle }, translateLocaleText('已复制任务标题:{title}', {
|
|
1803
|
+
title,
|
|
1804
|
+
})) : null,
|
|
1616
1805
|
selectedEnrollmentBound ? h('p', { style: mutedTextStyle },
|
|
1617
1806
|
'机器人及其任务能力只在这台 DSH 设备接收和执行;同一账号的其他设备不会继承本机授权。') : null,
|
|
1618
1807
|
selectedEnrollment && !selectedEnrollmentBound ? h('p', { style: mutedTextStyle },
|
|
1619
1808
|
'这是尚未绑定到设备的机器人。请在要继续使用它的电脑重新启用;原协作配置会保留。若旧设备访问曾被撤销,原任务权限不会恢复,需要重新开放。') : null,
|
|
1620
1809
|
selectedBot && conversationKind === 'group' && !selectedBot.history_available ? h('p', { role: 'status', style: mutedTextStyle },
|
|
1621
1810
|
'这个机器人当前不能读取平台群历史;群聊只能参考实际送达的消息和本机近期记录。请检查 DSH-IM 权限。') : null,
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
h('label',
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
h('span', { style: mutedTextStyle }, '每次点击都会调用当前团队模型并计入用量;只生成建议和待复核草稿,不会开放任务。修改描述后需再次点击。')),
|
|
1638
|
-
recognitionMessage ? h('p', { role: 'status', 'aria-live': 'polite', style: feedbackNoticeStyle }, recognitionMessage) : null),
|
|
1639
|
-
h('section', { role: 'region', 'aria-label': 'AI 任务助手', style: { ...panelStyle, padding: 10, gap: 7, minWidth: 0 } },
|
|
1811
|
+
capabilityStepHeading('1', '描述任务', '写清要完成什么、处理范围和结果形式。'),
|
|
1812
|
+
h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 320px), 1.35fr))',
|
|
1813
|
+
gap: 12, alignItems: 'stretch' } },
|
|
1814
|
+
h('label', { style: { display: 'grid', alignContent: 'start', gap: 7, minWidth: 0 } }, '描述规则',
|
|
1815
|
+
h('textarea', { required: true, maxLength: 4000, rows: 5, value: ruleDescription,
|
|
1816
|
+
onChange: (event) => {
|
|
1817
|
+
cancelRecognitionRequest()
|
|
1818
|
+
recognitionRequestRef.current += 1
|
|
1819
|
+
setRuleDescription(event.target.value)
|
|
1820
|
+
setRecognitionBusy(false)
|
|
1821
|
+
setRecognitionMessage(event.target.value.trim() ? '描述已更新,点击“立即分析”检查任务。' : '')
|
|
1822
|
+
},
|
|
1823
|
+
placeholder: '用日常语言说明要完成什么、可处理哪些资料和希望怎样返回。例如:审查来自 https://gitea.example.com/team/shop 的 PR,指出可定位的问题,不修改代码;请求人可指定 Head SHA,未指定时用最新 Head。', style: { ...inputStyle, minHeight: 120, resize: 'vertical' } })),
|
|
1824
|
+
h('section', { role: 'region', 'aria-label': 'AI 任务助手', style: { ...panelStyle, padding: 14,
|
|
1825
|
+
gap: 9, minWidth: 0, border: '1px solid var(--dsw-alias-border-l2)', backgroundColor: 'var(--dsw-alias-bg-layer-2)' } },
|
|
1640
1826
|
h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 } },
|
|
1641
|
-
h('strong', null,
|
|
1827
|
+
h('strong', null, 'AI 任务助手'),
|
|
1642
1828
|
h('span', { style: mutedTextStyle }, ruleSummaryPending ? '正在分析' : ruleSummaryReady ? '待复核 · 尚未开放'
|
|
1643
|
-
: ruleSummaryDirty ? '描述已更新' : '
|
|
1644
|
-
h('p', { style:
|
|
1645
|
-
? 'AI
|
|
1646
|
-
:
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
h('
|
|
1660
|
-
|
|
1661
|
-
'
|
|
1829
|
+
: ruleSummaryDirty ? '描述已更新' : '待分析')),
|
|
1830
|
+
h('p', { style: mutedTextStyle }, ruleSummaryPending
|
|
1831
|
+
? 'AI 正在根据当前描述更新任务理解。上一次结果暂不用于授权。'
|
|
1832
|
+
: '分析只生成待复核草稿,不会自动开放能力。修改描述后需重新分析。'),
|
|
1833
|
+
h('div', { style: { display: 'flex', justifyContent: 'flex-end', alignItems: 'center', gap: 10, flexWrap: 'wrap' } },
|
|
1834
|
+
h('span', { style: mutedTextStyle }, '调用团队模型并计入用量'),
|
|
1835
|
+
h('button', { type: 'button', disabled: recognitionBusy || busy || !ruleDescription.trim(),
|
|
1836
|
+
onClick: () => void identifyRule(), style: primaryButtonStyle },
|
|
1837
|
+
recognitionBusy ? 'AI 正在分析…' : ruleSummaryReady ? '立即重新分析' : '立即分析')),
|
|
1838
|
+
recognitionMessage ? h('p', { role: 'status', 'aria-live': 'polite', style: feedbackNoticeStyle }, recognitionMessage) : null)),
|
|
1839
|
+
h('section', { role: 'region', 'aria-label': 'AI 分析结果', style: { display: 'grid', gap: 9, padding: 14,
|
|
1840
|
+
border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 10,
|
|
1841
|
+
backgroundColor: 'var(--dsw-alias-bg-layer-1)', minWidth: 0,
|
|
1842
|
+
height: ruleSummaryReady ? 'min(480px, 55vh)' : undefined,
|
|
1843
|
+
gridTemplateRows: ruleSummaryReady ? 'auto minmax(0, 1fr)' : undefined,
|
|
1844
|
+
overflow: ruleSummaryReady ? 'hidden' : undefined } },
|
|
1845
|
+
h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 } },
|
|
1846
|
+
h('strong', null, 'AI 分析结果'),
|
|
1847
|
+
h('span', { style: mutedTextStyle }, ruleSummaryReady ? '草稿 · 未开放' : ruleSummaryPending ? '正在分析' : '等待分析')),
|
|
1848
|
+
ruleSummaryReady ? h('div', { style: { display: 'grid', alignContent: 'start', gap: 8,
|
|
1849
|
+
minHeight: 0, overflowY: 'auto', overscrollBehavior: 'contain', overflowWrap: 'anywhere' } },
|
|
1850
|
+
title ? h('strong', { style: { color: 'var(--dsw-alias-label-primary)' } }, rawLocaleText(title)) : null,
|
|
1851
|
+
h('p', { style: { ...mutedTextStyle, color: 'var(--dsw-alias-label-primary)' } }, ruleSummaryNarrative),
|
|
1852
|
+
missingHarnessInputs.length ? h('p', { role: 'status', style: { ...mutedTextStyle,
|
|
1853
|
+
color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } },
|
|
1854
|
+
translateLocaleText('待补信息:{items}。一般资料和偏好可留到执行时向请求人追问;PR 来源和仓库仍须先通过资源范围校验。',
|
|
1855
|
+
{ items: missingHarnessInputs.join(';') }))
|
|
1856
|
+
: h('p', { role: 'status', style: mutedTextStyle }, translateLocaleText(
|
|
1857
|
+
'AI 暂未发现运行时待补信息;开放后,执行时如缺少必要资料,机器人仍会向请求人追问。')),
|
|
1858
|
+
suggestions.length ? h('p', { style: mutedTextStyle }, `可选补充:${suggestions.join(';')}`) : null,
|
|
1859
|
+
ruleExamples.length ? h('p', { style: mutedTextStyle }, `自然请求示例:${ruleExamples.join(';')}`) : null,
|
|
1860
|
+
h('p', { style: mutedTextStyle }, translateLocaleText(
|
|
1861
|
+
'待补信息和可选建议仅供复核,不会阻止开放。确定的授权范围与 PR 资源边界仍会单独校验。')))
|
|
1862
|
+
: h('p', { role: 'status', style: mutedTextStyle }, ruleSummaryPending
|
|
1863
|
+
? '分析完成后,摘要、缺失范围和可选条件会显示在这里。'
|
|
1864
|
+
: '填写任务描述后点击右侧“立即分析”,结果会显示在这里。')),
|
|
1865
|
+
h('section', { style: capabilityStepStyle },
|
|
1866
|
+
capabilityStepHeading('2', '执行方', '选择可用于此任务的执行器,并从已勾选项中设一个默认。'),
|
|
1867
|
+
h('div', { style: { ...panelStyle, padding: 12, gap: 8 } },
|
|
1868
|
+
h('p', { style: mutedTextStyle }, translateLocaleText('普通请求只运行默认执行方;发起人明确要求交叉审查时,会并行运行其他已选执行方。')),
|
|
1662
1869
|
h('div', { role: 'group', 'aria-label': '执行方', style: { display: 'grid', gap: 7 } },
|
|
1663
|
-
h('strong', null, '执行方 · 可多选'),
|
|
1664
|
-
h('p', { style: mutedTextStyle }, '勾选可用于此任务的执行方,并从已勾选项中设一个默认。普通请求只运行默认项;请求人明确要求交叉审查时并行运行其他已选项。'),
|
|
1665
1870
|
...[
|
|
1666
1871
|
{ id: 'dsh', title: 'DSH', detail: '使用获准的团队模型处理对话内容和 ANT 已核验资源。', available: dshAvailable },
|
|
1667
1872
|
{ id: 'codex', title: 'Codex', detail: '使用本机 DSH Provider Profile 权限;ANT 不会将文件、Git 和网络权限缩小到单项任务。', available: codexAvailable },
|
|
@@ -1685,7 +1890,11 @@ window.__ModuleLoader__.load({
|
|
|
1685
1890
|
}),
|
|
1686
1891
|
),
|
|
1687
1892
|
executorUnavailableReason ? h('p', { role: 'status', style: { ...mutedTextStyle, gridColumn: '1 / -1' } },
|
|
1688
|
-
`${executorUnavailableReason} 当前仍可使用 DSH 团队模型。`) : null,
|
|
1893
|
+
`${executorUnavailableReason} 当前仍可使用 DSH 团队模型。`) : null)),
|
|
1894
|
+
h('section', { style: capabilityStepStyle },
|
|
1895
|
+
capabilityStepHeading('3', '请求范围', '限定从哪里请求、哪些人可以请求,以及授权多久。'),
|
|
1896
|
+
h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 250px), 1fr))',
|
|
1897
|
+
gap: 12, alignItems: 'start' } },
|
|
1689
1898
|
h('label', null, '使用场景', h('select', { value: conversationKind,
|
|
1690
1899
|
onChange: (event) => { setConversationKind(event.target.value); setGroupId('') }, style: inputStyle },
|
|
1691
1900
|
h('option', { value: 'dm' }, '私聊'), h('option', { value: 'group' }, '指定群聊'))),
|
|
@@ -1709,10 +1918,16 @@ window.__ModuleLoader__.load({
|
|
|
1709
1918
|
`有效期:${formatLocaleDate(editingExpiresAt * 1000)}(编辑时保持不变)`)
|
|
1710
1919
|
: h('label', null, '有效期', h('select', { value: days, onChange: (event) => setDays(Number(event.target.value)), style: inputStyle },
|
|
1711
1920
|
h('option', { value: 1 }, '1 天'), h('option', { value: 7 }, '7 天'), h('option', { value: 30 }, '30 天'))),
|
|
1712
|
-
|
|
1713
|
-
'
|
|
1714
|
-
|
|
1715
|
-
|
|
1921
|
+
usesExternalExecutor ? h('p', { style: { ...mutedTextStyle, gridColumn: '1 / -1' } },
|
|
1922
|
+
'Codex 和 Claude Code 沿用本机 DSH Provider Profile 权限;任务授权不会把文件、Git 或网络权限缩小到单项任务。') : null,
|
|
1923
|
+
usesExternalExecutor ? h('label', { style: { gridColumn: '1 / -1' } },
|
|
1924
|
+
h('input', { type: 'checkbox', 'aria-label': '启动前需要主人审批', checked: ownerApprovalRequired,
|
|
1925
|
+
onChange: (event) => setOwnerApprovalRequired(event.target.checked) }),
|
|
1926
|
+
' ', '启动前需要主人审批') : null,
|
|
1927
|
+
usesExternalExecutor && !ownerApprovalRequired ? h('p', { role: 'note', style: { ...mutedTextStyle, gridColumn: '1 / -1' } },
|
|
1928
|
+
'取消后,模型匹配到这项任务就会直接启动 Codex 或 Claude Code,主人不再逐次确认。') : null)),
|
|
1929
|
+
h('section', { style: capabilityStepStyle },
|
|
1930
|
+
capabilityStepHeading('4', '开放前复核', '确认任务理解、执行方式和请求范围后再开放。'),
|
|
1716
1931
|
h('aside', { id: 'ant-authorization-review', role: 'region', 'aria-label': '开放前复核', tabIndex: -1,
|
|
1717
1932
|
style: { ...settingsPanelStyle, padding: 14, gap: 10, alignSelf: 'stretch' } },
|
|
1718
1933
|
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8 } },
|
|
@@ -1750,7 +1965,7 @@ window.__ModuleLoader__.load({
|
|
|
1750
1965
|
'aria-describedby': cardBlockReason ? 'ant-capability-block-reason'
|
|
1751
1966
|
: submissionError ? 'ant-capability-submission-error' : undefined,
|
|
1752
1967
|
style: { ...primaryButtonStyle, width: '100%' } }, busy ? translateLocaleText('正在处理…')
|
|
1753
|
-
: editingCardId ? '保存能力修改' : '确认并开放能力'))))),
|
|
1968
|
+
: editingCardId ? '保存能力修改' : '确认并开放能力'))))))),
|
|
1754
1969
|
h('div', { role: 'region', 'aria-label': '本机处理设置', style: { display: activeTab === 'local' ? 'grid' : 'none', gap: 12 } },
|
|
1755
1970
|
showPrSecuritySettings ? h('section', { style: { ...settingsPanelStyle, padding: 14 } },
|
|
1756
1971
|
h('strong', null, 'PR 只读凭据(本机)'),
|
|
@@ -1810,7 +2025,7 @@ window.__ModuleLoader__.load({
|
|
|
1810
2025
|
}
|
|
1811
2026
|
|
|
1812
2027
|
function SkillCenterPage({ remote, onBack, onAll, onSkills, onPlugins, openBundle,
|
|
1813
|
-
includePlugins, query, onQueryChange }) {
|
|
2028
|
+
includePlugins, query, onQueryChange, pluginPageState }) {
|
|
1814
2029
|
const [skills, setSkills] = useState([])
|
|
1815
2030
|
const [loading, setLoading] = useState(false)
|
|
1816
2031
|
const [loadError, setLoadError] = useState(false)
|
|
@@ -2159,6 +2374,7 @@ window.__ModuleLoader__.load({
|
|
|
2159
2374
|
style: { color: 'var(--dsw-alias-link)', fontSize: 13, lineHeight: 1.6, padding: '4px 0' } }, '英文原文 ↗'))))) : null,
|
|
2160
2375
|
includePlugins ? h(PluginCenterPage, { remote, onBack, onAll, onSkills, openBundle,
|
|
2161
2376
|
embedded: true, query, onQueryChange, externalFilter: filter, externalCategoryFilter: categoryFilter,
|
|
2377
|
+
pluginPageState,
|
|
2162
2378
|
externalScopeFilter: scopeFilter,
|
|
2163
2379
|
hasSkillMatches: visible.length + visibleOfficial.length > 0, skillCatalogReady: !loading && !loadError,
|
|
2164
2380
|
refreshToken }) : null))
|
|
@@ -2167,49 +2383,193 @@ window.__ModuleLoader__.load({
|
|
|
2167
2383
|
function PluginCenterPage({ remote, onBack, onAll, onSkills, openBundle,
|
|
2168
2384
|
embedded = false, query, onQueryChange, externalFilter = 'all',
|
|
2169
2385
|
externalCategoryFilter = 'all', externalScopeFilter = 'all',
|
|
2170
|
-
hasSkillMatches = false, skillCatalogReady = false, refreshToken }) {
|
|
2386
|
+
hasSkillMatches = false, skillCatalogReady = false, refreshToken, pluginPageState }) {
|
|
2387
|
+
const { uncertainInstalls, setUncertainInstalls, uncertainInstallsRef, workingId, setWorkingId,
|
|
2388
|
+
completedId, setCompletedId, message, setMessage, refreshMessage, refreshMessageIsError,
|
|
2389
|
+
setRefreshFeedback, messageVersion, messagePluginId, messagePackageName,
|
|
2390
|
+
messageClearOnResolve, refreshGeneration, refreshRevision, requestPluginRefresh,
|
|
2391
|
+
workingAny } = pluginPageState
|
|
2171
2392
|
const [plugins, setPlugins] = useState([])
|
|
2172
2393
|
const [bundles, setBundles] = useState([])
|
|
2173
2394
|
const [loading, setLoading] = useState(true)
|
|
2174
2395
|
const [loadError, setLoadError] = useState(false)
|
|
2175
|
-
const [
|
|
2176
|
-
const [uncertainId, setUncertainId] = useState('')
|
|
2177
|
-
const [completedId, setCompletedId] = useState('')
|
|
2178
|
-
const [message, setMessage] = useState('')
|
|
2396
|
+
const [bundleListReady, setBundleListReady] = useState(false)
|
|
2179
2397
|
const [filter, setFilter] = useState('all')
|
|
2180
2398
|
const [categoryFilter, setCategoryFilter] = useState('all')
|
|
2181
|
-
const
|
|
2182
|
-
|
|
2399
|
+
const waitForInstallBeforeDeadline = (manager, requestId) => {
|
|
2400
|
+
let timeoutId
|
|
2401
|
+
const waitPromise = Promise.resolve().then(() => manager.waitForInstall(requestId))
|
|
2402
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
2403
|
+
timeoutId = setTimeout(() => reject(new Error()), 30_000)
|
|
2404
|
+
})
|
|
2405
|
+
return Promise.race([waitPromise, timeoutPromise])
|
|
2406
|
+
.finally(() => { if (timeoutId !== undefined) clearTimeout(timeoutId) })
|
|
2407
|
+
}
|
|
2408
|
+
const refresh = async (focusPluginId = null) => {
|
|
2183
2409
|
const generation = ++refreshGeneration.current
|
|
2410
|
+
const uncertaintySnapshot = uncertainInstallsRef.current
|
|
2411
|
+
const messageVersionAtStart = messageVersion.current
|
|
2184
2412
|
setLoading(true)
|
|
2413
|
+
setBundleListReady(false)
|
|
2414
|
+
let catalogLoaded = false
|
|
2185
2415
|
try {
|
|
2186
2416
|
const manager = remote?.pluginManager
|
|
2187
2417
|
if (typeof manager?.listBundles !== 'function') throw new Error('当前 DSH 未提供插件管理接口。')
|
|
2188
|
-
const
|
|
2189
|
-
fetch('/api/ant-teams/plugins', { cache: 'no-store' }), manager.listBundles(),
|
|
2190
|
-
])
|
|
2418
|
+
const response = await fetch('/api/ant-teams/plugins', { cache: 'no-store' })
|
|
2191
2419
|
const result = await readLocalJsonResponse(response)
|
|
2192
2420
|
if (!response.ok) throw new Error(result.message || `读取插件目录失败(${response.status})。`)
|
|
2193
2421
|
if (!Array.isArray(result.plugins)) throw new Error('企业插件目录格式无效。')
|
|
2194
|
-
if (!listed.ok) throw new Error(listed.error?.message || '无法读取 DSH 本机插件。')
|
|
2195
2422
|
if (generation !== refreshGeneration.current) return
|
|
2196
2423
|
setPlugins(result.plugins)
|
|
2424
|
+
setLoadError(false)
|
|
2425
|
+
catalogLoaded = true
|
|
2426
|
+
const initialListed = await manager.listBundles()
|
|
2427
|
+
if (!initialListed.ok) throw new Error(initialListed.error?.message || '无法读取 DSH 本机插件。')
|
|
2428
|
+
if (generation !== refreshGeneration.current) return
|
|
2429
|
+
setBundles(initialListed.value)
|
|
2430
|
+
setBundleListReady(true)
|
|
2431
|
+
const catalogPackageNames = new Set(result.plugins
|
|
2432
|
+
.map((plugin) => plugin.package_name).filter((name) => typeof name === 'string' && name))
|
|
2433
|
+
const catalogUncertaintySnapshot = Object.fromEntries(Object.entries(uncertaintySnapshot)
|
|
2434
|
+
.filter(([, uncertainInstall]) => catalogPackageNames.has(uncertainInstall.packageName)))
|
|
2435
|
+
const requestIds = [...new Set(Object.values(catalogUncertaintySnapshot)
|
|
2436
|
+
.flatMap((uncertainInstall) => [uncertainInstall.requestId,
|
|
2437
|
+
...(uncertainInstall.relatedRequestIds || [])])
|
|
2438
|
+
.filter((requestId) => typeof requestId === 'string' && requestId))]
|
|
2439
|
+
if (requestIds.length) {
|
|
2440
|
+
if (typeof manager.waitForInstall !== 'function') {
|
|
2441
|
+
if (generation === refreshGeneration.current) setRefreshFeedback(translateLocaleText(
|
|
2442
|
+
'当前 DSH 无法确认上次安装是否结束;请稍后重新核对,勿重复安装。'), true)
|
|
2443
|
+
return
|
|
2444
|
+
}
|
|
2445
|
+
setRefreshFeedback(translateLocaleText('正在等待 DSH 上一次插件安装请求结束,再读取本机状态。'))
|
|
2446
|
+
for (const requestId of requestIds) {
|
|
2447
|
+
let waitResult
|
|
2448
|
+
try { waitResult = await waitForInstallBeforeDeadline(manager, requestId) }
|
|
2449
|
+
catch {
|
|
2450
|
+
if (generation === refreshGeneration.current) setRefreshFeedback(translateLocaleText(
|
|
2451
|
+
'无法确认 DSH 上一项插件安装是否已结束;待核对锁保持,请稍后重试。'), true)
|
|
2452
|
+
return
|
|
2453
|
+
}
|
|
2454
|
+
if (!waitResult?.ok) {
|
|
2455
|
+
if (generation === refreshGeneration.current) setRefreshFeedback(translateLocaleText(
|
|
2456
|
+
'无法确认 DSH 上一项插件安装是否已结束;待核对锁保持,请稍后重试。'), true)
|
|
2457
|
+
return
|
|
2458
|
+
}
|
|
2459
|
+
if (generation !== refreshGeneration.current) return
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
if (generation !== refreshGeneration.current) return
|
|
2463
|
+
let listed = initialListed
|
|
2464
|
+
if (requestIds.length) {
|
|
2465
|
+
listed = await manager.listBundles()
|
|
2466
|
+
if (!listed.ok) throw new Error(listed.error?.message || '无法读取 DSH 本机插件。')
|
|
2467
|
+
}
|
|
2468
|
+
if (generation !== refreshGeneration.current) return
|
|
2469
|
+
const canUpdateMessage = messageVersion.current === messageVersionAtStart
|
|
2470
|
+
const currentUncertainties = uncertainInstallsRef.current
|
|
2471
|
+
const currentCatalogUncertainties = Object.fromEntries(Object.entries(currentUncertainties)
|
|
2472
|
+
.filter(([, uncertainInstall]) => catalogPackageNames.has(uncertainInstall.packageName)))
|
|
2473
|
+
const focusPackageName = focusPluginId
|
|
2474
|
+
? plugins.find((plugin) => plugin.id === focusPluginId)?.package_name : null
|
|
2475
|
+
let nextUncertainties = currentUncertainties
|
|
2476
|
+
const resolvedPluginIds = new Set()
|
|
2477
|
+
for (const [pluginId, uncertainInstall] of Object.entries(catalogUncertaintySnapshot)) {
|
|
2478
|
+
if (currentUncertainties[pluginId] !== uncertainInstall) continue
|
|
2479
|
+
const installed = listed.value.find((bundle) => bundle.name === uncertainInstall.packageName && bundle.installed)
|
|
2480
|
+
const resolved = !installed
|
|
2481
|
+
|| (installed.version === uncertainInstall.targetVersion
|
|
2482
|
+
&& (!uncertainInstall.expectedSource || installed.source === uncertainInstall.expectedSource))
|
|
2483
|
+
|| Boolean(uncertainInstall.previousVersion
|
|
2484
|
+
&& installed.version === uncertainInstall.previousVersion
|
|
2485
|
+
&& (installed.source === uncertainInstall.previousSource
|
|
2486
|
+
|| installed.source === uncertainInstall.expectedPreviousSource)
|
|
2487
|
+
&& installed.enabled === uncertainInstall.previousEnabled)
|
|
2488
|
+
|| Boolean(focusPluginId
|
|
2489
|
+
&& (pluginId === focusPluginId || uncertainInstall.packageName === focusPackageName)
|
|
2490
|
+
&& installed && typeof installed.version === 'string' && installed.version.trim()
|
|
2491
|
+
&& installed.version !== uncertainInstall.targetVersion
|
|
2492
|
+
&& installed.version !== uncertainInstall.previousVersion)
|
|
2493
|
+
if (!resolved) continue
|
|
2494
|
+
if (nextUncertainties === currentUncertainties) nextUncertainties = { ...currentUncertainties }
|
|
2495
|
+
delete nextUncertainties[pluginId]
|
|
2496
|
+
resolvedPluginIds.add(pluginId)
|
|
2497
|
+
}
|
|
2498
|
+
if (nextUncertainties !== currentUncertainties) {
|
|
2499
|
+
uncertainInstallsRef.current = nextUncertainties
|
|
2500
|
+
setUncertainInstalls(nextUncertainties)
|
|
2501
|
+
}
|
|
2502
|
+
setPlugins(result.plugins)
|
|
2197
2503
|
setBundles(listed.value)
|
|
2198
2504
|
setLoadError(false)
|
|
2199
|
-
|
|
2505
|
+
if (canUpdateMessage && messageClearOnResolve.current && messagePluginId.current
|
|
2506
|
+
&& (resolvedPluginIds.has(messagePluginId.current)
|
|
2507
|
+
|| [...resolvedPluginIds].some((pluginId) => uncertaintySnapshot[pluginId]?.packageName
|
|
2508
|
+
=== messagePackageName.current))) setMessage('')
|
|
2509
|
+
const focusedUncertainty = focusPluginId
|
|
2510
|
+
? catalogUncertaintySnapshot[focusPluginId] || Object.values(catalogUncertaintySnapshot)
|
|
2511
|
+
.find((item) => item.packageName === focusPackageName) : null
|
|
2512
|
+
const focusedUncertaintyId = focusedUncertainty?.id
|
|
2513
|
+
const hasFocusedUncertainty = Boolean(focusedUncertainty
|
|
2514
|
+
&& currentUncertainties[focusedUncertaintyId] === focusedUncertainty)
|
|
2515
|
+
const refreshSnapshotIsCurrent = Object.keys(currentCatalogUncertainties).length
|
|
2516
|
+
=== Object.keys(catalogUncertaintySnapshot).length
|
|
2517
|
+
&& Object.entries(catalogUncertaintySnapshot)
|
|
2518
|
+
.every(([pluginId, uncertainInstall]) => currentCatalogUncertainties[pluginId] === uncertainInstall)
|
|
2519
|
+
const hasUncertainInstalls = Object.values(nextUncertainties)
|
|
2520
|
+
.some((uncertainInstall) => catalogPackageNames.has(uncertainInstall.packageName))
|
|
2521
|
+
if (canUpdateMessage && hasFocusedUncertainty && refreshSnapshotIsCurrent) {
|
|
2522
|
+
if (resolvedPluginIds.has(focusedUncertaintyId)) {
|
|
2523
|
+
setRefreshFeedback(hasUncertainInstalls
|
|
2524
|
+
? translateLocaleText('已重新读取本机列表,但安装状态仍无法确认;请在 DSH 插件管理核对后再操作。')
|
|
2525
|
+
: translateLocaleText('本机状态已确认;可以再次检查或按目录版本操作。'))
|
|
2526
|
+
} else {
|
|
2527
|
+
setRefreshFeedback(translateLocaleText('已重新读取本机列表,但安装状态仍无法确认;请在 DSH 插件管理核对后再操作。'))
|
|
2528
|
+
}
|
|
2529
|
+
} else if (canUpdateMessage && !focusPluginId && refreshSnapshotIsCurrent) {
|
|
2530
|
+
setRefreshFeedback(hasUncertainInstalls
|
|
2531
|
+
? translateLocaleText('已重新读取本机列表,但安装状态仍无法确认;请在 DSH 插件管理核对后再操作。')
|
|
2532
|
+
: result.plugins.length ? '' : '团队暂未发布精选插件。')
|
|
2533
|
+
}
|
|
2200
2534
|
} catch (error) {
|
|
2201
2535
|
if (generation !== refreshGeneration.current) return
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2536
|
+
setBundleListReady(false)
|
|
2537
|
+
const errorMessage = error instanceof Error ? error.message : '无法读取企业精选插件。'
|
|
2538
|
+
if (!catalogLoaded) {
|
|
2539
|
+
setPlugins([])
|
|
2540
|
+
setBundles([])
|
|
2541
|
+
setLoadError(true)
|
|
2542
|
+
}
|
|
2543
|
+
setRefreshFeedback(errorMessage, true)
|
|
2206
2544
|
} finally { if (generation === refreshGeneration.current) setLoading(false) }
|
|
2207
2545
|
}
|
|
2208
|
-
useEffect(() => { void refresh() }, [refreshToken])
|
|
2546
|
+
useEffect(() => { void refresh() }, [refreshToken, refreshRevision])
|
|
2547
|
+
const uncertainInstallFor = (plugin) => uncertainInstalls[plugin.id]
|
|
2548
|
+
|| Object.values(uncertainInstallsRef.current).find((item) => item.packageName === plugin.package_name)
|
|
2549
|
+
const markInstallUncertain = (plugin, previous = null, expectedPreviousSource = null,
|
|
2550
|
+
requestId = null, relatedRequestIds = []) => {
|
|
2551
|
+
const uncertainInstall = {
|
|
2552
|
+
id: plugin.id,
|
|
2553
|
+
packageName: plugin.package_name,
|
|
2554
|
+
targetVersion: plugin.version,
|
|
2555
|
+
requestId,
|
|
2556
|
+
relatedRequestIds: [...new Set(relatedRequestIds.filter((value) => typeof value === 'string' && value))],
|
|
2557
|
+
expectedSource: approvedPluginArtifactUrl(plugin),
|
|
2558
|
+
previousVersion: previous?.version || null,
|
|
2559
|
+
previousSource: previous?.source,
|
|
2560
|
+
expectedPreviousSource,
|
|
2561
|
+
previousEnabled: previous ? previous.enabled !== false : null,
|
|
2562
|
+
}
|
|
2563
|
+
const next = { ...uncertainInstallsRef.current, [plugin.id]: uncertainInstall }
|
|
2564
|
+
uncertainInstallsRef.current = next
|
|
2565
|
+
setUncertainInstalls(next)
|
|
2566
|
+
}
|
|
2209
2567
|
const install = async (plugin) => {
|
|
2210
2568
|
const artifactUrl = approvedPluginArtifactUrl(plugin)
|
|
2211
2569
|
const installSpec = pluginInstallSpec(plugin)
|
|
2212
|
-
|
|
2570
|
+
const uncertainInstall = uncertainInstallFor(plugin)
|
|
2571
|
+
if (!installSpec || workingId || pluginRuntimeState.workingId
|
|
2572
|
+
|| uncertainInstall || completedId === plugin.id) return
|
|
2213
2573
|
const manager = remote?.pluginManager
|
|
2214
2574
|
if (typeof manager?.installBundle !== 'function') {
|
|
2215
2575
|
setMessage('当前 DSH 未提供插件安装接口,请联系管理员。')
|
|
@@ -2229,8 +2589,15 @@ window.__ModuleLoader__.load({
|
|
|
2229
2589
|
'在这台 DSH {action}团队审核的 {plugin_title} {version}?{source_notice}{update_notice} 插件将在本机运行,完成后需要完全重启 DSH。',
|
|
2230
2590
|
{ action: translateLocaleText(action), plugin_title: plugin.title, version: plugin.version, source_notice: sourceNotice, update_notice: updateNotice })
|
|
2231
2591
|
if (!confirmLocalized(installConfirmation)) return
|
|
2592
|
+
const operationScopeRevision = pluginRuntimeState.scopeRevision
|
|
2593
|
+
const isOperationScopeCurrent = () => operationScopeRevision === pluginRuntimeState.scopeRevision
|
|
2594
|
+
const setInstallMessage = (value, staleScopeValue = '本机此前启动的插件操作已结束;请查看当前插件目录和本机状态。') => {
|
|
2595
|
+
setMessage(isOperationScopeCurrent() ? value : staleScopeValue,
|
|
2596
|
+
plugin.id, plugin.package_name, Boolean(uncertainInstallFor(plugin)))
|
|
2597
|
+
}
|
|
2232
2598
|
setWorkingId(plugin.id)
|
|
2233
|
-
|
|
2599
|
+
setInstallMessage(translateLocaleText('正在安装 {plugin_title},请不要退出 DSH…', { plugin_title: plugin.title }),
|
|
2600
|
+
'本机有一项插件操作仍在完成;结束前不能启动其他插件安装。')
|
|
2234
2601
|
try {
|
|
2235
2602
|
const response = await fetch('/api/ant-teams/plugins', { cache: 'no-store' })
|
|
2236
2603
|
const catalog = await readLocalJsonResponse(response)
|
|
@@ -2241,6 +2608,9 @@ window.__ModuleLoader__.load({
|
|
|
2241
2608
|
throw new Error('插件审核信息已变化,请刷新目录后重试。')
|
|
2242
2609
|
}
|
|
2243
2610
|
const requestId = crypto.randomUUID()
|
|
2611
|
+
let relatedRequestIds = []
|
|
2612
|
+
const markCurrentInstallUncertain = (previous = null, expectedPreviousSource = null) =>
|
|
2613
|
+
markInstallUncertain(plugin, previous, expectedPreviousSource, requestId, relatedRequestIds)
|
|
2244
2614
|
const requestReference = requestId.slice(0, 8).toUpperCase()
|
|
2245
2615
|
const previousSourceIsRegistry = Boolean(local && (!local.source
|
|
2246
2616
|
|| local.source === plugin.package_name || local.source.startsWith(`${plugin.package_name}@`)))
|
|
@@ -2280,17 +2650,26 @@ window.__ModuleLoader__.load({
|
|
|
2280
2650
|
if (installed !== null || !previousInstallSpec) return 'unknown'
|
|
2281
2651
|
if (previousSourceIsRegistry && !previousRegistry) return 'unknown'
|
|
2282
2652
|
const restoreRequestId = crypto.randomUUID()
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2653
|
+
const restoreOptions = {
|
|
2654
|
+
enabled: local.enabled !== false, requestId: restoreRequestId,
|
|
2655
|
+
saveExact: true,
|
|
2656
|
+
}
|
|
2657
|
+
if (previousSourceIsRegistry && previousRegistry) restoreOptions.registry = previousRegistry
|
|
2658
|
+
try { await manager.installBundle(previousInstallSpec, restoreOptions) } catch {}
|
|
2659
|
+
if (typeof manager.waitForInstall !== 'function') {
|
|
2660
|
+
relatedRequestIds = [restoreRequestId]
|
|
2661
|
+
return 'unknown'
|
|
2662
|
+
}
|
|
2663
|
+
let restoreWaitResult
|
|
2664
|
+
try { restoreWaitResult = await manager.waitForInstall(restoreRequestId) }
|
|
2665
|
+
catch {
|
|
2666
|
+
relatedRequestIds = [restoreRequestId]
|
|
2667
|
+
return 'unknown'
|
|
2668
|
+
}
|
|
2669
|
+
if (!restoreWaitResult?.ok) {
|
|
2670
|
+
relatedRequestIds = [restoreRequestId]
|
|
2671
|
+
return 'unknown'
|
|
2672
|
+
}
|
|
2294
2673
|
installed = await currentBundle()
|
|
2295
2674
|
return isPreviousVersion(installed) && installed.enabled === (local.enabled !== false)
|
|
2296
2675
|
? 'restored' : 'unknown'
|
|
@@ -2313,7 +2692,7 @@ window.__ModuleLoader__.load({
|
|
|
2313
2692
|
} catch (error) { installFailure = error; result = null }
|
|
2314
2693
|
} else {
|
|
2315
2694
|
if (replacingInstalled && typeof manager.removeBundle !== 'function') {
|
|
2316
|
-
|
|
2695
|
+
setInstallMessage('当前 DSH 不支持通过 ANT 安全替换已安装插件。请在 DSH 插件管理查看状态,或联系管理员。')
|
|
2317
2696
|
return
|
|
2318
2697
|
}
|
|
2319
2698
|
if (replacingInstalled) {
|
|
@@ -2324,7 +2703,7 @@ window.__ModuleLoader__.load({
|
|
|
2324
2703
|
if (!removed?.ok || !['applied', 'restart-required'].includes(removed.value?.application)) {
|
|
2325
2704
|
const diagnostic = safeInstallDiagnostic(removed?.value?.error || removed?.error || removeFailure)
|
|
2326
2705
|
const recovery = await restorePreviousVersion()
|
|
2327
|
-
if (recovery !== 'restored')
|
|
2706
|
+
if (recovery !== 'restored') markCurrentInstallUncertain(local, expectedPreviousSource)
|
|
2328
2707
|
const recoveryNotice = recovery === 'restored'
|
|
2329
2708
|
? translateLocaleText('旧版本号和原启用状态已恢复。{source_notice}', {
|
|
2330
2709
|
source_notice: previousSourceIsRegistry
|
|
@@ -2332,7 +2711,7 @@ window.__ModuleLoader__.load({
|
|
|
2332
2711
|
: recovery === 'target' ? translateLocaleText(
|
|
2333
2712
|
'本机列表显示审核版本 {version} 已安装,请核对后再操作。', { version: plugin.version })
|
|
2334
2713
|
: translateLocaleText('无法确认旧版本及启用状态,请先在 DSH 插件管理核对。')
|
|
2335
|
-
|
|
2714
|
+
setInstallMessage(`无法开始更新 ${plugin.title}:DSH 未能确认旧版本移除完成。${diagnostic
|
|
2336
2715
|
? `DSH 诊断:${diagnostic}。` : ''}${recoveryNotice}`)
|
|
2337
2716
|
return
|
|
2338
2717
|
}
|
|
@@ -2353,7 +2732,6 @@ window.__ModuleLoader__.load({
|
|
|
2353
2732
|
} catch (error) { waitFailure = error }
|
|
2354
2733
|
}
|
|
2355
2734
|
if (!recovered) {
|
|
2356
|
-
setUncertainId(plugin.id)
|
|
2357
2735
|
let restoreNotice = ''
|
|
2358
2736
|
if (removedForUpdate) {
|
|
2359
2737
|
const restoreState = await restorePreviousVersion()
|
|
@@ -2366,23 +2744,27 @@ window.__ModuleLoader__.load({
|
|
|
2366
2744
|
'本机列表显示审核版本 {version} 已安装,但操作结果未确认。', { version: plugin.version })}`
|
|
2367
2745
|
: ` ${translateLocaleText('DSH 未能确认旧版本恢复,请先在插件列表核对。')}`
|
|
2368
2746
|
}
|
|
2747
|
+
markCurrentInstallUncertain(local, expectedPreviousSource)
|
|
2369
2748
|
const details = [installFailure && `installBundle:${safeInstallDiagnostic(installFailure)}`,
|
|
2370
2749
|
waitFailure && `waitForInstall:${safeInstallDiagnostic(waitFailure)}`].filter(Boolean)
|
|
2371
|
-
|
|
2372
|
-
? `本机诊断:${details.join(';')}。` : ''}${restoreNotice}请先在 DSH
|
|
2750
|
+
setInstallMessage(`暂时无法确认 ${plugin.title} 的安装结果(请求编号 ${requestReference})。${details.length
|
|
2751
|
+
? `本机诊断:${details.join(';')}。` : ''}${restoreNotice}请先在 DSH 插件列表核对,勿重复点击安装。`,
|
|
2752
|
+
'本机插件安装结果待核对,请先确认本机状态,勿重复安装。')
|
|
2373
2753
|
return
|
|
2374
2754
|
}
|
|
2375
2755
|
result = recovered
|
|
2376
2756
|
}
|
|
2377
2757
|
const outcome = result.value
|
|
2378
2758
|
if (!outcome || typeof outcome !== 'object') {
|
|
2379
|
-
|
|
2380
|
-
|
|
2759
|
+
markCurrentInstallUncertain(local, expectedPreviousSource)
|
|
2760
|
+
setInstallMessage(`DSH 未返回 ${plugin.title} 的安装结果。请先在插件列表核对,勿重复点击安装。`,
|
|
2761
|
+
'本机插件安装结果待核对,请先确认本机状态,勿重复安装。')
|
|
2381
2762
|
return
|
|
2382
2763
|
}
|
|
2383
2764
|
if (outcome.application === 'failed' || outcome.application === 'cancelled') {
|
|
2384
2765
|
if (outcome.error?.code === 'ambiguous-install' && local?.version === plugin.version) {
|
|
2385
|
-
|
|
2766
|
+
setInstallMessage(`DSH 无法确定 ${plugin.title} 本次安装的唯一结果,未确认完成。请先在 DSH 插件列表核对;若已经正常使用同版则无需重复安装,仍需核对来源时请联系管理员。`,
|
|
2767
|
+
'本机插件安装结果待核对,请先确认本机状态,勿重复安装。')
|
|
2386
2768
|
return
|
|
2387
2769
|
}
|
|
2388
2770
|
const diagnostic = safeInstallDiagnostic({ name: outcome.error?.name,
|
|
@@ -2390,11 +2772,14 @@ window.__ModuleLoader__.load({
|
|
|
2390
2772
|
message: outcome.error?.diagnostic || outcome.error?.message || outcome.packageResult?.output })
|
|
2391
2773
|
let restoreNotice = ''
|
|
2392
2774
|
if (updateRestore) {
|
|
2393
|
-
|
|
2775
|
+
const restoreConfirmed = ['applied', 'restart-required'].includes(updateRestore.application)
|
|
2776
|
+
if (!restoreConfirmed) markCurrentInstallUncertain(local, expectedPreviousSource)
|
|
2777
|
+
restoreNotice = restoreConfirmed
|
|
2394
2778
|
? ` ${translateLocaleText('DSH 已恢复旧版本 {version}。', { version: local.version })}`
|
|
2395
2779
|
: ` ${translateLocaleText('DSH 未能确认旧版本恢复,请先在插件列表核对。')}`
|
|
2396
2780
|
} else if (removedForUpdate) {
|
|
2397
2781
|
const restoreState = await restorePreviousVersion()
|
|
2782
|
+
if (restoreState !== 'restored') markCurrentInstallUncertain(local, expectedPreviousSource)
|
|
2398
2783
|
restoreNotice = restoreState === 'restored'
|
|
2399
2784
|
? translateLocaleText('DSH 已恢复旧版本号 {version}。{source_notice}', {
|
|
2400
2785
|
version: local.version,
|
|
@@ -2404,14 +2789,15 @@ window.__ModuleLoader__.load({
|
|
|
2404
2789
|
'本机列表显示审核版本 {version} 已安装,但结果未确认。', { version: plugin.version })}`
|
|
2405
2790
|
: ` ${translateLocaleText('DSH 未能确认旧版本恢复,请先在插件列表核对。')}`
|
|
2406
2791
|
}
|
|
2407
|
-
|
|
2792
|
+
setInstallMessage(`${plugin.title} 安装未完成(请求编号 ${requestReference})。${diagnostic
|
|
2408
2793
|
? `DSH 诊断:${diagnostic}。` : 'DSH 没有返回失败诊断。'}${restoreNotice}请在 DSH 插件管理查看状态或联系管理员。`)
|
|
2409
2794
|
return
|
|
2410
2795
|
}
|
|
2411
2796
|
if (outcome.bundle !== plugin.package_name
|
|
2412
2797
|
|| !['restart-required', 'applied'].includes(outcome.application)) {
|
|
2413
|
-
|
|
2414
|
-
|
|
2798
|
+
markCurrentInstallUncertain(local, expectedPreviousSource)
|
|
2799
|
+
setInstallMessage(`DSH 未确认 ${plugin.title} 已安装。请先在插件列表核对,勿重复点击安装。`,
|
|
2800
|
+
'本机插件安装结果待核对,请先确认本机状态,勿重复安装。')
|
|
2415
2801
|
return
|
|
2416
2802
|
}
|
|
2417
2803
|
let observed = null
|
|
@@ -2422,19 +2808,24 @@ window.__ModuleLoader__.load({
|
|
|
2422
2808
|
}
|
|
2423
2809
|
} catch {}
|
|
2424
2810
|
if ((outcome.version && outcome.version !== plugin.version) || observed?.version !== plugin.version) {
|
|
2425
|
-
|
|
2426
|
-
|
|
2811
|
+
markCurrentInstallUncertain(local, expectedPreviousSource)
|
|
2812
|
+
setInstallMessage(`DSH 未确认 ${plugin.title} 的审核版本已安装。请先在插件列表核对,勿重复点击安装。`,
|
|
2813
|
+
'本机插件安装结果待核对,请先确认本机状态,勿重复安装。')
|
|
2427
2814
|
return
|
|
2428
2815
|
}
|
|
2429
|
-
await refresh()
|
|
2430
2816
|
if (artifactUrl && plugin.artifact_sha256) {
|
|
2431
2817
|
try { localStorage.setItem(`${PLUGIN_INSTALL_RECORD_PREFIX}${plugin.id}`, plugin.artifact_sha256) } catch {}
|
|
2432
2818
|
}
|
|
2433
|
-
setCompletedId(plugin.id)
|
|
2434
|
-
|
|
2819
|
+
if (isOperationScopeCurrent()) setCompletedId(plugin.id)
|
|
2820
|
+
setInstallMessage(`${plugin.title} 已安装。请完全退出并重新启动 DSH 后使用。`,
|
|
2821
|
+
'本机插件已安装或更新;请完全退出并重新启动 DSH 后使用。')
|
|
2822
|
+
requestPluginRefresh()
|
|
2435
2823
|
} catch (error) {
|
|
2436
|
-
|
|
2437
|
-
|
|
2824
|
+
setInstallMessage(error instanceof Error ? error.message : '插件安装未完成,请稍后重试。',
|
|
2825
|
+
'本机此前启动的插件操作已结束;请刷新本机状态后再操作。')
|
|
2826
|
+
} finally {
|
|
2827
|
+
setWorkingId('')
|
|
2828
|
+
}
|
|
2438
2829
|
}
|
|
2439
2830
|
const activeFilter = embedded ? externalFilter : filter
|
|
2440
2831
|
const activeCategoryFilter = embedded ? externalCategoryFilter : categoryFilter
|
|
@@ -2493,13 +2884,18 @@ window.__ModuleLoader__.load({
|
|
|
2493
2884
|
h('div', null,
|
|
2494
2885
|
h('h1', { style: { margin: 0, fontSize: 28 } }, '企业精选插件'),
|
|
2495
2886
|
h('p', { style: { ...mutedTextStyle, marginTop: 6 } }, '这里展示团队审核过的插件版本及本机状态。DSH 0.2.0-rc.2 不会自动更新 Bundle;插件自身的更新器可能绕过此目录安装其他版本。')),
|
|
2496
|
-
h('button', { type: 'button', disabled: loading, onClick: () =>
|
|
2887
|
+
h('button', { type: 'button', disabled: loading, onClick: () => {
|
|
2888
|
+
if (!workingAny) setMessage('')
|
|
2889
|
+
void refresh()
|
|
2890
|
+
}, style: secondaryButtonStyle }, loading ? '读取中…' : '刷新目录')),
|
|
2497
2891
|
!loading && !plugins.length && !loadError ? embedded
|
|
2498
2892
|
? h('p', { role: 'status', style: mutedTextStyle }, '团队暂未发布精选插件。')
|
|
2499
2893
|
: h('section', { role: 'status', style: panelStyle },
|
|
2500
2894
|
h('h2', { style: { margin: 0, fontSize: 18 } }, '团队暂未发布精选插件'),
|
|
2501
2895
|
h('p', { style: mutedTextStyle }, '需要某个插件?请联系企业管理员,说明用途或提供插件地址。审核发布后会显示在这里。')) : null,
|
|
2502
|
-
|
|
2896
|
+
refreshMessage && (refreshMessageIsError || plugins.length)
|
|
2897
|
+
? h('p', { role: refreshMessageIsError ? 'alert' : 'status', style: feedbackNoticeStyle }, refreshMessage) : null,
|
|
2898
|
+
message ? h('p', { role: 'status', style: feedbackNoticeStyle }, message) : null,
|
|
2503
2899
|
plugins.length && !embedded ? h(ExtensionRequestNotice) : null,
|
|
2504
2900
|
plugins.length && !embedded ? h('div', { style: panelStyle },
|
|
2505
2901
|
h('input', { type: 'search', value: query, onChange: (event) => onQueryChange(event.target.value), placeholder: '搜索插件名称或用途', 'aria-label': '搜索企业精选插件', style: inputStyle }),
|
|
@@ -2512,7 +2908,6 @@ window.__ModuleLoader__.load({
|
|
|
2512
2908
|
'aria-label': '筛选插件用途类别', style: { ...inputStyle, width: 'auto', minHeight: 34 } },
|
|
2513
2909
|
h('option', { value: 'all' }, '全部用途'),
|
|
2514
2910
|
...extensionCategories.map(([category, label]) => h('option', { key: category, value: category }, label)))))) : null,
|
|
2515
|
-
plugins.length && message ? h('p', { role: 'status', style: feedbackNoticeStyle }, message) : null,
|
|
2516
2911
|
visible.length ? h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 420px), 1fr))', gap: 16 } },
|
|
2517
2912
|
...visible.map((plugin) => {
|
|
2518
2913
|
const local = bundles.find((bundle) => bundle.name === plugin.package_name && bundle.installed)
|
|
@@ -2521,11 +2916,13 @@ window.__ModuleLoader__.load({
|
|
|
2521
2916
|
const artifactUrl = approvedPluginArtifactUrl(plugin)
|
|
2522
2917
|
const installSpec = pluginInstallSpec(plugin)
|
|
2523
2918
|
const localAhead = local?.version && newerVersion(local.version, plugin.version)
|
|
2919
|
+
const uncertainInstall = uncertainInstallFor(plugin)
|
|
2524
2920
|
const sourceKnown = typeof local?.source === 'string'
|
|
2525
2921
|
const verifiedLocal = sameVersion && artifactUrl && sourceKnown && local.source === artifactUrl
|
|
2526
2922
|
const recordedInstall = sameVersion && !sourceKnown && previouslyInstalledReviewedPlugin(plugin)
|
|
2527
2923
|
const status = security ? '已废弃(安全风险)'
|
|
2528
|
-
:
|
|
2924
|
+
: !bundleListReady ? loading ? '读取中…' : translateLocaleText('本机状态无法读取')
|
|
2925
|
+
: plugin.status === 'deprecated' ? local?.version ? `已废弃 · 本机 ${local.version}` : '已废弃' : local?.error ? '加载异常' : localAhead
|
|
2529
2926
|
? '本机版本高于团队目录版本' : sameVersion
|
|
2530
2927
|
? verifiedLocal ? local.enabled ? '本机已装(团队来源,已启用)' : '本机已装(团队来源,未启用)'
|
|
2531
2928
|
: sourceKnown ? '本机同版,来源不同' : recordedInstall
|
|
@@ -2562,14 +2959,18 @@ window.__ModuleLoader__.load({
|
|
|
2562
2959
|
h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 8 } },
|
|
2563
2960
|
installSpec && (!local || (local.version && local.version !== plugin.version && (!localAhead || artifactUrl))
|
|
2564
2961
|
|| (artifactUrl && sameVersion && !verifiedLocal))
|
|
2565
|
-
? h('button', { type: 'button', disabled:
|
|
2962
|
+
? h('button', { type: 'button', disabled: loading || !bundleListReady
|
|
2963
|
+
|| workingAny || Boolean(uncertainInstall) || completedId === plugin.id,
|
|
2566
2964
|
onClick: () => void install(plugin), style: primaryButtonStyle },
|
|
2567
|
-
workingId === plugin.id ? '安装中…' :
|
|
2965
|
+
workingId === plugin.id ? '安装中…' : uncertainInstall ? '先核对安装结果'
|
|
2568
2966
|
: completedId === plugin.id ? '重启 DSH 后生效'
|
|
2569
2967
|
: local ? sameVersion || localAhead ? '按团队审核包重新安装' : '更新到审核版本' : '安装到这台 DSH') : null,
|
|
2968
|
+
uncertainInstall ? h('button', { type: 'button', disabled: loading || workingAny,
|
|
2969
|
+
onClick: () => { if (!workingAny) setMessage(''); void refresh(plugin.id) },
|
|
2970
|
+
style: secondaryButtonStyle }, translateLocaleText('重新核对本机状态')) : null,
|
|
2570
2971
|
plugin.status === 'published' && !local && !installSpec
|
|
2571
2972
|
? h('p', { style: mutedTextStyle }, '如需安装,请联系企业管理员安排。') : null,
|
|
2572
|
-
local ||
|
|
2973
|
+
local || uncertainInstall ? h('button', { type: 'button', onClick: () => openBundle(plugin.package_name), style: secondaryButtonStyle }, '打开 DSH 插件管理') : null,
|
|
2573
2974
|
local && !sameVersion && plugin.status === 'published' && !installSpec
|
|
2574
2975
|
? h('p', { style: mutedTextStyle }, '本机版本与团队审核版本不同;请联系企业管理员核对。') : null))
|
|
2575
2976
|
})) : !loading && plugins.length && (!embedded || !hasSkillMatches)
|
|
@@ -3342,6 +3743,7 @@ window.__ModuleLoader__.load({
|
|
|
3342
3743
|
setMessage(`未清除本机凭证和模型设置:${error instanceof Error ? error.message : '请稍后重试。'}`)
|
|
3343
3744
|
return
|
|
3344
3745
|
}
|
|
3746
|
+
setPluginRuntimeServiceScope('')
|
|
3345
3747
|
try {
|
|
3346
3748
|
await clearLocalConnection()
|
|
3347
3749
|
setMessage('此设备的 ANT 凭证已撤销,ANT 模型设置已清除。团队服务地址已保留。')
|
|
@@ -3764,7 +4166,8 @@ window.__ModuleLoader__.load({
|
|
|
3764
4166
|
dshCompatibilityCard,
|
|
3765
4167
|
showReleaseNotes ? h('section', { style: { ...infoNoticeStyle, padding: 12 } },
|
|
3766
4168
|
h('strong', { style: { fontSize: 14, lineHeight: 1.4 } }, '本版更新'),
|
|
3767
|
-
h('
|
|
4169
|
+
h('div', { 'aria-label': '更新说明内容', style: { display: 'grid', gap: 8 } },
|
|
4170
|
+
...releaseNoteBlocks(bundleRelease.release_notes))) : null,
|
|
3768
4171
|
['incompatible', 'unknown'].includes(serviceCompatibility.state) ? h('p', { role: 'status', style: {
|
|
3769
4172
|
...feedbackNoticeStyle,
|
|
3770
4173
|
color: serviceCompatibility.state === 'incompatible' ? 'var(--dsw-alias-state-error-primary)'
|
|
@@ -3807,14 +4210,86 @@ window.__ModuleLoader__.load({
|
|
|
3807
4210
|
useLocaleRevision()
|
|
3808
4211
|
const [tab, setTab] = useState('all')
|
|
3809
4212
|
const [query, setQuery] = useState('')
|
|
4213
|
+
const serviceUrl = loadServiceUrl()
|
|
4214
|
+
const [, forcePluginRuntimeRender] = useState(0)
|
|
4215
|
+
useEffect(() => {
|
|
4216
|
+
const refresh = () => forcePluginRuntimeRender((revision) => revision + 1)
|
|
4217
|
+
pluginRuntimeState.listeners.add(refresh)
|
|
4218
|
+
setPluginRuntimeServiceScope(serviceUrl)
|
|
4219
|
+
return () => pluginRuntimeState.listeners.delete(refresh)
|
|
4220
|
+
}, [serviceUrl])
|
|
4221
|
+
const runtimeScopeMatches = pluginRuntimeState.serviceUrl === serviceUrl
|
|
4222
|
+
const uncertainInstalls = pluginRuntimeState.uncertainInstallsRef.current
|
|
4223
|
+
const uncertainInstallsRef = pluginRuntimeState.uncertainInstallsRef
|
|
4224
|
+
const workingAny = Boolean(pluginRuntimeState.workingId)
|
|
4225
|
+
const workingId = runtimeScopeMatches && pluginRuntimeState.workingServiceUrl === serviceUrl
|
|
4226
|
+
? pluginRuntimeState.workingId : ''
|
|
4227
|
+
const completedId = runtimeScopeMatches ? pluginRuntimeState.completedId : ''
|
|
4228
|
+
const message = runtimeScopeMatches ? pluginRuntimeState.message : ''
|
|
4229
|
+
const refreshMessage = runtimeScopeMatches ? pluginRuntimeState.refreshMessage : ''
|
|
4230
|
+
const refreshMessageIsError = runtimeScopeMatches && pluginRuntimeState.refreshMessageIsError
|
|
4231
|
+
const setUncertainInstalls = (next) => {
|
|
4232
|
+
const value = typeof next === 'function' ? next(pluginRuntimeState.uncertainInstallsRef.current) : next
|
|
4233
|
+
pluginRuntimeState.uncertainInstalls.clear()
|
|
4234
|
+
for (const [pluginId, uncertainInstall] of Object.entries(value)) {
|
|
4235
|
+
pluginRuntimeState.uncertainInstalls.set(pluginId, uncertainInstall)
|
|
4236
|
+
}
|
|
4237
|
+
publishPluginRuntimeState()
|
|
4238
|
+
}
|
|
4239
|
+
const setWorkingId = (value) => {
|
|
4240
|
+
pluginRuntimeState.workingId = value
|
|
4241
|
+
pluginRuntimeState.workingServiceUrl = value ? serviceUrl : ''
|
|
4242
|
+
if (!value && pluginRuntimeState.message === pluginScopeBusyMessage) {
|
|
4243
|
+
pluginRuntimeState.message = ''
|
|
4244
|
+
pluginRuntimeState.messagePluginId.current = ''
|
|
4245
|
+
pluginRuntimeState.messagePackageName.current = ''
|
|
4246
|
+
pluginRuntimeState.messageClearOnResolve.current = false
|
|
4247
|
+
pluginRuntimeState.messageVersion.current += 1
|
|
4248
|
+
}
|
|
4249
|
+
publishPluginRuntimeState()
|
|
4250
|
+
}
|
|
4251
|
+
const setCompletedId = (value) => {
|
|
4252
|
+
pluginRuntimeState.completedId = value
|
|
4253
|
+
publishPluginRuntimeState()
|
|
4254
|
+
}
|
|
4255
|
+
const setMessage = (value, pluginId = '', packageName = '', clearOnResolve = false) => {
|
|
4256
|
+
pluginRuntimeState.message = value
|
|
4257
|
+
pluginRuntimeState.messagePluginId.current = value ? pluginId : ''
|
|
4258
|
+
pluginRuntimeState.messagePackageName.current = value ? packageName : ''
|
|
4259
|
+
pluginRuntimeState.messageClearOnResolve.current = Boolean(value) && clearOnResolve
|
|
4260
|
+
pluginRuntimeState.messageVersion.current += 1
|
|
4261
|
+
pluginRuntimeState.refreshMessage = ''
|
|
4262
|
+
pluginRuntimeState.refreshMessageIsError = false
|
|
4263
|
+
publishPluginRuntimeState()
|
|
4264
|
+
}
|
|
4265
|
+
const setRefreshFeedback = (value, isError = false) => {
|
|
4266
|
+
pluginRuntimeState.refreshMessage = value
|
|
4267
|
+
pluginRuntimeState.refreshMessageIsError = isError
|
|
4268
|
+
publishPluginRuntimeState()
|
|
4269
|
+
}
|
|
4270
|
+
const requestPluginRefresh = () => {
|
|
4271
|
+
pluginRuntimeState.refreshRevision += 1
|
|
4272
|
+
publishPluginRuntimeState()
|
|
4273
|
+
}
|
|
4274
|
+
const pluginPageState = { uncertainInstalls, setUncertainInstalls, uncertainInstallsRef,
|
|
4275
|
+
workingId, setWorkingId, completedId, setCompletedId, message, setMessage,
|
|
4276
|
+
refreshMessage, refreshMessageIsError, setRefreshFeedback, requestPluginRefresh,
|
|
4277
|
+
workingAny, serviceUrl,
|
|
4278
|
+
messageVersion: pluginRuntimeState.messageVersion,
|
|
4279
|
+
messagePluginId: pluginRuntimeState.messagePluginId,
|
|
4280
|
+
messagePackageName: pluginRuntimeState.messagePackageName,
|
|
4281
|
+
messageClearOnResolve: pluginRuntimeState.messageClearOnResolve,
|
|
4282
|
+
refreshGeneration: pluginRuntimeState.refreshGeneration,
|
|
4283
|
+
refreshRevision: pluginRuntimeState.refreshRevision }
|
|
3810
4284
|
const navigation = {
|
|
3811
4285
|
onAll: () => setTab('all'), onSkills: () => setTab('skills'),
|
|
3812
4286
|
onPlugins: () => setTab('plugins'),
|
|
3813
4287
|
}
|
|
3814
4288
|
return tab === 'plugins'
|
|
3815
|
-
? h(PluginCenterPage, { remote, onBack, openBundle, query, onQueryChange: setQuery,
|
|
4289
|
+
? h(PluginCenterPage, { remote, onBack, openBundle, query, onQueryChange: setQuery,
|
|
4290
|
+
pluginPageState, ...navigation })
|
|
3816
4291
|
: h(SkillCenterPage, { remote, onBack, openBundle, query, onQueryChange: setQuery,
|
|
3817
|
-
includePlugins: tab === 'all', ...navigation })
|
|
4292
|
+
pluginPageState, includePlugins: tab === 'all', ...navigation })
|
|
3818
4293
|
}
|
|
3819
4294
|
|
|
3820
4295
|
function brandingCacheKey() {
|