@ai-wendao/ant 0.4.5 → 0.4.6
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 +1 -1
- package/client.js +288 -89
- package/delegation.js +29 -0
- package/index.js +10 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ ANT (AI-native Teams) connects a DeepSeek Harness Desktop profile to its team's
|
|
|
8
8
|
|
|
9
9
|
成员在 ANT 插件设置页选择一个 DSH-IM 飞书机器人并启用协作收件。每段私聊、群聊或话题由同一受限 DSH 会话自然回答和追问;只有 DSH 准备执行任务时,才通过 ANT 工具提交任务卡 ID 和参数,由服务端再次核对真实请求人、群、参数及授权。成员按需创建任务卡,设置意图、参数、请求人、适用会话和有效期;PR 审查只是可选示例。普通文字任务由没有本机工具权限的 DSH 会话处理。单个 Gitea PR 参数还可选择 Codex 专用只读 CLI,或 DSH 已注册的 Claude Code 子代理试用:ANT 用只读 Gitea 令牌为每项 PR 审查能力维护本机仓库缓存,并按已核验的 PR Head SHA 创建独立工作树;令牌不会交给执行器。Codex 使用固定只读权限配置,Claude Code 沿用 DSH 提供方的本机权限设置。执行器可查看完整源码和已核验 diff;工作区不含 `.git`、符号链接或子模块,也不会运行仓库脚本。Claude Code 仍是所有者手动开放的本机试用,不宣称具备操作系统级只读隔离。成员可查询或取消任务;每台电脑默认同时处理三项任务,成员可调整,旧版 PR 审查与新任务共用名额。
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
私聊默认开启,所有者可按机器人单独关闭或重新开启;关闭后新私聊消息不会进入 DSH,机器人每五分钟最多向同一请求人发送一次静态恢复指引;所有者身份绑定命令仍可用。关闭前已提交的任务不会撤销,仍回复原私聊;群聊设置不受影响。群聊由所有者逐群启用普通聊天,任务授权再按卡单独设置。群里有人 @ 机器人后,群 ID 出现在插件列表;所有者也可手动填写群 ID。若所有者在群里,可先私聊机器人绑定飞书身份,再在群内提出“在本群启用聊天”,或按提示确认“在本群启用任务 任务名称”。群消息只在明确 @ 时处理,任务结果公开回复原群/原话题;要求私聊回复的群消息会提示请求人改为私聊发起。当前 DSH-IM 未提供可核验的机器人进/出群发起者,所以不会因被拉群自动启用。不同群、话题和私聊的上下文默认隔离;每项能力当前只能关联一个范围,即私聊或一个群;要让多个群或私聊都能执行,需分别建立能力卡,并分别设定请求人和有效期。群历史按需读取,默认先看十条、最多检查一百条平台记录,实际可见范围由飞书权限决定。
|
|
12
12
|
|
|
13
13
|
新建任务卡的 Codex 与 Claude Code 执行方目前只接受一个必填 Gitea PR 参数。Gitea PR 源仓库和目标仓库大小按 API KiB 换算为 bytes,并在 fetch 前与 512 MiB 单仓库快照上限及本机总缓存预算核对。每台 DSH 的长期 bare 仓库缓存默认最多 10 GiB,可在插件的“本机处理设置”调整为 1–100 GiB;按缓存文件逻辑大小计算,不含临时工作目录及文件系统分配开销。预算也为即将拉取的仓库预留空间。Git fetch 强制使用单 pack;POSIX 系统用进程文件大小限制约束 pack 与 index 的总预算,并监控缓存总量。正常情况下不会淘汰仍启用能力的缓存;若实际 fetch 越界,会清理本项能力的仓库缓存(旧 PR 可在后续任务重新下载)。Windows 暂未实现等效的原生 fetch 硬限制,Codex/Claude Code PR 快照会失败关闭,界面会禁用对应选项并提示 DSH 差异审查。停用/过期能力的缓存会在刷新时回收。任务工作树是短期目录,受单快照 512 MiB、最多 10,000 个文件、单文件最多 64 MiB 限制,不计入长期缓存预算,完成后删除。快照不支持符号链接或子模块,也不下载 Git LFS 实际文件;快照中的 LFS 指针不能代表已审查的实际文件内容。Codex/Claude Code 的 granular Gitea Token 还需 `read:repository` 和 `read:user`,后者仅用于本机获取 Git HTTPS 用户名。超限时任务失败,不降级为 diff-only。快照与权限验收范围见[Claude Code 任务卡计划](../../docs/plans/2026-10-22-claude-code-task-card.md)及[执行方隔离](../../docs/decisions/2026-10-22-im-native-executor-isolation.md)。普通对话和 DSH 文字任务不直接获得文件、命令或网络工具。机器人独占收件模式由 DSH-IM 保存;停止 ANT 授权后若需恢复 DSH-IM 原有聊天,需要在 DSH-IM 中切回普通模式。
|
|
14
14
|
|
package/client.js
CHANGED
|
@@ -13,7 +13,7 @@ window.__ModuleLoader__.load({
|
|
|
13
13
|
const ANT_SETTINGS_TAB_STORAGE_KEY = 'ant-settings-tab'
|
|
14
14
|
const ANT_SETTINGS_TABS = [
|
|
15
15
|
['connection', '团队连接'], ['models', '团队模型'], ['bot-chat', '机器人与聊天'],
|
|
16
|
-
['capability', '能力授权'], ['local', '本机与任务'], ['updates', '
|
|
16
|
+
['capability', '能力授权'], ['local', '本机与任务'], ['updates', '关于'],
|
|
17
17
|
]
|
|
18
18
|
const loadAntSettingsTab = () => {
|
|
19
19
|
try {
|
|
@@ -113,6 +113,7 @@ window.__ModuleLoader__.load({
|
|
|
113
113
|
backgroundColor: 'var(--dsw-alias-bg-module-platform)', boxSizing: 'border-box',
|
|
114
114
|
}
|
|
115
115
|
const mutedTextStyle = { margin: 0, color: 'var(--dsw-alias-label-secondary)', fontSize: 13, lineHeight: 1.6 }
|
|
116
|
+
const sectionTitleStyle = { margin: 0, fontSize: 16, fontWeight: 650, lineHeight: 1.4 }
|
|
116
117
|
const inputStyle = {
|
|
117
118
|
width: '100%', boxSizing: 'border-box', padding: '10px 12px',
|
|
118
119
|
color: 'var(--dsw-alias-label-primary)', backgroundColor: 'var(--dsw-specific-input-major)',
|
|
@@ -359,7 +360,7 @@ window.__ModuleLoader__.load({
|
|
|
359
360
|
}
|
|
360
361
|
const teamServicePanel = h('section', { style: { ...panelStyle, display: activeTab === 'connection' ? 'grid' : 'none' } },
|
|
361
362
|
h('div', null,
|
|
362
|
-
h('h3', { style:
|
|
363
|
+
h('h3', { style: sectionTitleStyle }, '团队服务'),
|
|
363
364
|
h('p', { style: { ...mutedTextStyle, marginTop: 4 } }, '由团队管理员提供服务地址;管理员可在控制台管理成员、模型和协作设置。')),
|
|
364
365
|
h('form', { onSubmit: save, style: { display: 'grid', gridTemplateColumns: binding.url ? '1fr' : 'minmax(0, 1fr) auto', alignItems: 'end', gap: 10 } },
|
|
365
366
|
h('label', { htmlFor: 'ant-team-service-url', style: { gridColumn: '1 / -1', fontSize: 13, fontWeight: 550 } }, binding.url ? '当前连接的团队服务地址' : '服务地址'),
|
|
@@ -375,7 +376,9 @@ window.__ModuleLoader__.load({
|
|
|
375
376
|
message ? h('p', { role: 'status', style: mutedTextStyle }, message) : null,
|
|
376
377
|
link ? h('a', { href: link, target: '_blank', rel: 'noopener noreferrer', style: { color: 'var(--dsw-alias-link)', fontSize: 13 } }, '打开团队管理 ↗') : null))
|
|
377
378
|
return h('section', { style: { display: 'grid', gap: 16, width: '100%', maxWidth: 1120 } },
|
|
378
|
-
h('
|
|
379
|
+
h('div', { style: { display: 'grid', gap: 4 } },
|
|
380
|
+
h('h2', { style: { margin: 0, fontSize: 20, fontWeight: 650, lineHeight: 1.35 } }, '协作机器人'),
|
|
381
|
+
h('p', { style: mutedTextStyle }, '同事可以像平常一样聊天;需要使用你电脑的能力时,ANT 才核验授权。')),
|
|
379
382
|
h('div', { role: 'status', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
|
380
383
|
flexWrap: 'wrap', gap: 10, padding: '10px 12px', borderRadius: 8,
|
|
381
384
|
backgroundColor: bindingReadyForSummary ? 'var(--dsw-alias-state-success-tertiary)' : 'var(--dsw-alias-bg-layer-2)' } },
|
|
@@ -386,7 +389,7 @@ window.__ModuleLoader__.load({
|
|
|
386
389
|
backgroundColor: 'var(--dsw-alias-bg-module-platform)' } },
|
|
387
390
|
pluginAlerts.length ? h('section', { role: 'alert', style: { ...panelStyle,
|
|
388
391
|
borderColor: 'var(--dsw-alias-label-danger, #d44)', maxHeight: '28vh', overflowY: 'auto' } },
|
|
389
|
-
h('h3', { style:
|
|
392
|
+
h('h3', { style: sectionTitleStyle }, '已安装插件存在安全撤回公告'),
|
|
390
393
|
...pluginAlerts.map((plugin) => h('p', { key: plugin.id, style: mutedTextStyle },
|
|
391
394
|
`${plugin.title} ${plugin.version}${plugin.versionUnknown ? '(本机版本暂无法读取,请核对是否受影响)' : ''}:${plugin.withdrawal_reason}`)),
|
|
392
395
|
...pluginAlerts.map((plugin) => h('button', { key: `${plugin.id}-open`, type: 'button',
|
|
@@ -404,7 +407,7 @@ window.__ModuleLoader__.load({
|
|
|
404
407
|
? `有新版 ANT:${updateSummary.currentVersion || '当前版本未知'} → ${updateSummary.version}`
|
|
405
408
|
: updateSummary.state === 'done' ? `ANT ${updateSummary.version} 已安装,需重启 DSH`
|
|
406
409
|
: updateSummary.state === 'revoked' ? '当前 ANT 安装包已撤回' : 'ANT 更新需要处理'),
|
|
407
|
-
h('span', { style: mutedTextStyle }, updateSummary.message || '
|
|
410
|
+
h('span', { style: mutedTextStyle }, updateSummary.message || '打开“关于”查看处理方式。')),
|
|
408
411
|
h('button', { type: 'button', onClick: () => selectSettingsTab('updates'), style: primaryButtonStyle },
|
|
409
412
|
updateSummary.state === 'available' ? '查看并更新' : '查看处理方式')) : null,
|
|
410
413
|
h('nav', { role: 'tablist', 'aria-label': 'ANT 插件设置', style: {
|
|
@@ -462,6 +465,7 @@ window.__ModuleLoader__.load({
|
|
|
462
465
|
const [loadFailures, setLoadFailures] = useState([])
|
|
463
466
|
const [createOpen, setCreateOpen] = useState(false)
|
|
464
467
|
const [message, setMessage] = useState('')
|
|
468
|
+
const [draftNotice, setDraftNotice] = useState('')
|
|
465
469
|
const load = async () => {
|
|
466
470
|
setLoading(true)
|
|
467
471
|
setMessage('')
|
|
@@ -504,8 +508,22 @@ window.__ModuleLoader__.load({
|
|
|
504
508
|
const botConnected = Boolean(selectedBot?.available && selectedBot?.connected)
|
|
505
509
|
const selectedEnrollment = enrolled.find((bot) => bot.bot_fingerprint === selectedBot?.fingerprint)
|
|
506
510
|
const selectedEnrollmentBound = selectedEnrollment?.device_bound_to_current === true
|
|
511
|
+
const dmChatSettingAvailable = typeof selectedEnrollment?.dm_chat_enabled === 'boolean'
|
|
512
|
+
const dmChatEnabled = selectedEnrollment?.dm_chat_enabled !== false
|
|
513
|
+
const dmChatStatus = !selectedBot ? '先选择机器人'
|
|
514
|
+
: loadFailures.includes('enrolled-bots') ? '暂时无法读取'
|
|
515
|
+
: !selectedEnrollmentBound ? '先绑定本机'
|
|
516
|
+
: !dmChatSettingAvailable ? '需升级服务端' : dmChatEnabled ? '已开启' : '已关闭'
|
|
517
|
+
const dmChatDescription = !selectedBot ? '选择机器人后查看或修改私聊设置。'
|
|
518
|
+
: loadFailures.includes('enrolled-bots') ? '暂时无法读取私聊设置,请刷新状态后再试。'
|
|
519
|
+
: !selectedEnrollmentBound ? '请先把机器人绑定到这台 DSH,再修改私聊设置。'
|
|
520
|
+
: !dmChatSettingAvailable ? '当前团队服务端尚未提供私聊开关,请先升级服务端。'
|
|
521
|
+
: dmChatEnabled
|
|
522
|
+
? '允许此机器人接收私聊;任务仍按能力授权执行。'
|
|
523
|
+
: '新私聊消息不会交给 DSH;所有者身份绑定仍可用,已提交任务仍会在原私聊回复。已开放能力不撤销,重新开启后未过期能力恢复触发。'
|
|
507
524
|
const availableGroups = groups.filter((group) => group.bot_fingerprint === selectedBot?.fingerprint)
|
|
508
525
|
const selectedChatGroup = availableGroups.find((group) => group.group_id === chatGroupId)
|
|
526
|
+
const groupChatLoadFailed = loadFailures.includes('groups')
|
|
509
527
|
const activeCards = cards.filter((card) => !card.revoked_at && card.expires_at * 1000 > Date.now())
|
|
510
528
|
const selectedGroupHasTasks = Boolean(selectedBot && chatGroupId && (
|
|
511
529
|
activeCards.some((card) => card.bot_fingerprint === selectedBot.fingerprint
|
|
@@ -524,6 +542,10 @@ window.__ModuleLoader__.load({
|
|
|
524
542
|
? `仅指定成员:${senderIds.trim() || '尚未填写成员 ID'}`
|
|
525
543
|
: conversationKind === 'group' ? `群 ${groupId || '尚未选择'} 的所有成员` : '任何能私聊此机器人的人'
|
|
526
544
|
const resultRouteSummary = conversationKind === 'group' ? '回复到原群(群内可见)' : '回复到发起任务的私聊'
|
|
545
|
+
const executorSummary = runner === 'codex' ? '本机 Codex · PR 快照'
|
|
546
|
+
: runner === 'claude-code' ? '本机 Claude Code · PR 快照' : 'DSH · 当前授权模型'
|
|
547
|
+
const resourceSummary = selectedRunnerNeedsPr ? '指定仓库的已核验 PR 快照'
|
|
548
|
+
: '当前会话中 DSH 实际可见的内容'
|
|
527
549
|
const cardBlockReason = enrollmentLoadError ? '暂时无法核对机器人是否已启用协作,请刷新状态后再试。'
|
|
528
550
|
: !botConnected || !selectedEnrollmentBound ? '先在上方把机器人绑定到这台 DSH。'
|
|
529
551
|
: conversationKind === 'group' && loadFailures.includes('groups') ? '暂时无法核对目标群,请刷新状态后再试。'
|
|
@@ -589,7 +611,7 @@ window.__ModuleLoader__.load({
|
|
|
589
611
|
if (!response.ok) throw new Error(result.message || '协作机器人启用失败。')
|
|
590
612
|
await load()
|
|
591
613
|
if (origin) setTrustedOrigin(origin)
|
|
592
|
-
setMessage('
|
|
614
|
+
setMessage('协作机器人已启用。你可以分别设置私聊和群聊,再开放需要的能力。')
|
|
593
615
|
} catch (error) { setMessage(error.message) }
|
|
594
616
|
finally { setBusy(false) }
|
|
595
617
|
}
|
|
@@ -626,9 +648,8 @@ window.__ModuleLoader__.load({
|
|
|
626
648
|
} catch (error) { setMessage(error.message) }
|
|
627
649
|
finally { setBusy(false) }
|
|
628
650
|
}
|
|
629
|
-
const saveGroupChat = async () => {
|
|
651
|
+
const saveGroupChat = async (enabled) => {
|
|
630
652
|
if (!selectedBot || !selectedEnrollmentBound || !chatGroupId.trim()) return
|
|
631
|
-
const enabled = !selectedChatGroup?.chat_enabled
|
|
632
653
|
if (!enabled && selectedGroupHasTasks) {
|
|
633
654
|
setMessage('这个群还有开放能力。请先停止这些能力,再关闭群聊。')
|
|
634
655
|
return
|
|
@@ -654,6 +675,27 @@ window.__ModuleLoader__.load({
|
|
|
654
675
|
} catch (error) { setMessage(error.message) }
|
|
655
676
|
finally { setBusy(false) }
|
|
656
677
|
}
|
|
678
|
+
const saveDmChat = async (enabled) => {
|
|
679
|
+
if (!selectedBot || !selectedEnrollmentBound || !dmChatSettingAvailable) return
|
|
680
|
+
if (!window.confirm(enabled
|
|
681
|
+
? '开启后,此机器人会在私聊中正常对话;私聊任务仍逐项按能力授权核验。'
|
|
682
|
+
: '关闭后,新私聊消息不会进入 DSH,包括普通聊天、任务请求和状态查询;所有者身份绑定命令仍可用。关闭前已提交的任务不会被撤销,仍按原私聊回复。已开放的私聊能力不会删除;重新开启后,未过期的能力会恢复触发。群聊设置不受影响。')) return
|
|
683
|
+
setBusy(true)
|
|
684
|
+
try {
|
|
685
|
+
const response = await fetch('/api/ant-teams/delegation/dm-chat', {
|
|
686
|
+
method: 'PUT', headers: { 'content-type': 'application/json' },
|
|
687
|
+
body: JSON.stringify({ bot_id: selectedBot.bot_id,
|
|
688
|
+
bot_fingerprint: selectedBot.fingerprint, enabled }),
|
|
689
|
+
})
|
|
690
|
+
if (!response.ok) {
|
|
691
|
+
const result = await readLocalJsonResponse(response)
|
|
692
|
+
throw new Error(result.message || '私聊设置保存失败。')
|
|
693
|
+
}
|
|
694
|
+
await load()
|
|
695
|
+
setMessage(enabled ? '已开启此机器人的私聊。' : '已关闭此机器人的私聊;群聊设置保持不变。')
|
|
696
|
+
} catch (error) { setMessage(error.message) }
|
|
697
|
+
finally { setBusy(false) }
|
|
698
|
+
}
|
|
657
699
|
const saveLocalProcessingSettings = async (event) => {
|
|
658
700
|
event.preventDefault()
|
|
659
701
|
setBusy(true)
|
|
@@ -673,8 +715,51 @@ window.__ModuleLoader__.load({
|
|
|
673
715
|
} catch (error) { setMessage(error.message) }
|
|
674
716
|
finally { setBusy(false) }
|
|
675
717
|
}
|
|
718
|
+
const setupStepper = h('ol', { 'aria-label': '设置步骤', style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 150px), 1fr))',
|
|
719
|
+
gap: 8, padding: 0, margin: 0, listStyle: 'none' } },
|
|
720
|
+
...[
|
|
721
|
+
{ label: '绑定机器人', detail: '连接到这台 DSH' },
|
|
722
|
+
{ label: '设置聊天范围', detail: '私聊和群聊分别控制' },
|
|
723
|
+
{ label: '开放能力', detail: selectedTaskCount ? `${selectedTaskCount} 项已开放` : '按需要逐项开放' },
|
|
724
|
+
{ label: '复核授权', detail: createOpen ? '正在配置授权范围' : selectedTaskCount ? '可随时查看与停止' : '请求范围由所有者设定' },
|
|
725
|
+
].map((step, index) => {
|
|
726
|
+
const currentStep = !botConnected || !selectedEnrollmentBound ? 0 : createOpen ? 3 : selectedTaskCount ? 4 : 2
|
|
727
|
+
const state = index < currentStep ? '完成' : index === currentStep ? '当前' : '待完成'
|
|
728
|
+
return h('li', { key: step.label, style: { display: 'grid', gap: 3, padding: '8px 10px',
|
|
729
|
+
borderTop: `2px solid ${state === '完成' ? 'var(--dsw-alias-state-success-primary)' : state === '当前' ? 'var(--dsw-alias-state-business-primary)' : 'var(--dsw-alias-border-l2)'}`,
|
|
730
|
+
minWidth: 0 } },
|
|
731
|
+
h('strong', { style: { fontSize: 13, fontWeight: 600 } }, step.label),
|
|
732
|
+
h('span', { style: mutedTextStyle }, step.detail))
|
|
733
|
+
}))
|
|
734
|
+
const chatScopePanel = h('div', { style: { border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8, padding: 12,
|
|
735
|
+
display: 'grid', gap: 8 } },
|
|
736
|
+
h('strong', null, '机器人和聊天范围'),
|
|
737
|
+
h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 240px), 1fr))', gap: 8 } },
|
|
738
|
+
h('div', { style: { display: 'grid', gap: 5, padding: 10, borderRadius: 8,
|
|
739
|
+
backgroundColor: 'var(--dsw-alias-bg-layer-2)' } },
|
|
740
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8 } },
|
|
741
|
+
h('label', { style: { display: 'flex', gap: 8, alignItems: 'center' } },
|
|
742
|
+
h('input', { type: 'checkbox', checked: dmChatEnabled,
|
|
743
|
+
disabled: busy || !selectedBot || !selectedEnrollmentBound
|
|
744
|
+
|| enrollmentLoadError || !dmChatSettingAvailable,
|
|
745
|
+
onChange: (event) => void saveDmChat(event.target.checked),
|
|
746
|
+
'aria-label': '允许机器人私聊' }), h('strong', null, '私聊')),
|
|
747
|
+
h('span', { style: { ...mutedTextStyle, whiteSpace: 'nowrap' } }, dmChatStatus)),
|
|
748
|
+
h('p', { style: mutedTextStyle }, dmChatDescription)),
|
|
749
|
+
h('div', { style: { display: 'grid', gap: 5, padding: 10, borderRadius: 8,
|
|
750
|
+
backgroundColor: 'var(--dsw-alias-bg-layer-2)' } },
|
|
751
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8 } },
|
|
752
|
+
h('label', { style: { display: 'flex', gap: 8, alignItems: 'center' } },
|
|
753
|
+
h('input', { type: 'checkbox', checked: Boolean(selectedChatGroup?.chat_enabled),
|
|
754
|
+
disabled: busy || groupChatLoadFailed || !selectedEnrollmentBound || !botConnected || !chatGroupId.trim()
|
|
755
|
+
|| (Boolean(selectedChatGroup?.chat_enabled) && selectedGroupHasTasks),
|
|
756
|
+
onChange: (event) => void saveGroupChat(event.target.checked),
|
|
757
|
+
'aria-label': '允许所选群进行普通聊天' }), h('strong', null, '群聊')),
|
|
758
|
+
h('span', { style: { ...mutedTextStyle, whiteSpace: 'nowrap' } }, `${availableGroups.length} 个已发现`)),
|
|
759
|
+
h('p', { style: mutedTextStyle }, '选择一个群后可单独启用;不影响私聊设置,群任务仍需单独授权。'))))
|
|
676
760
|
const createCard = async (event) => {
|
|
677
761
|
event.preventDefault()
|
|
762
|
+
setDraftNotice('')
|
|
678
763
|
if (!selectedBot || !botConnected || !selectedEnrollmentBound) { setMessage('请先在这台 DSH 绑定选中的协作机器人。'); return }
|
|
679
764
|
if (!title.trim() || !intent.trim() || !taskPrompt.trim()) {
|
|
680
765
|
setMessage('请填写能力名称、适用请求说明和处理要求。'); return
|
|
@@ -781,6 +866,30 @@ window.__ModuleLoader__.load({
|
|
|
781
866
|
} catch (error) { setMessage(error.message) }
|
|
782
867
|
finally { setBusy(false) }
|
|
783
868
|
}
|
|
869
|
+
const copyCardToDraft = (card) => {
|
|
870
|
+
const cardBot = bots.find((bot) => bot.fingerprint === card.bot_fingerprint)
|
|
871
|
+
const cardEnrollment = enrolled.find((bot) => bot.bot_fingerprint === card.bot_fingerprint)
|
|
872
|
+
const titleChars = Array.from(`复制:${card.title || '未命名能力'}`)
|
|
873
|
+
while (new TextEncoder().encode(titleChars.join('')).length > 100) titleChars.pop()
|
|
874
|
+
setBotId(card.bot_id || cardBot?.bot_id || '')
|
|
875
|
+
setTrustedOrigin(cardEnrollment?.gitea_origin || '')
|
|
876
|
+
setTitle(titleChars.join(''))
|
|
877
|
+
setIntent(card.intent || '')
|
|
878
|
+
setExamples(Array.isArray(card.examples) ? card.examples.join('\n') : '')
|
|
879
|
+
setTaskPrompt(card.task_prompt || '')
|
|
880
|
+
setRunner(['dsh', 'codex', 'claude-code'].includes(card.runner) ? card.runner : 'dsh')
|
|
881
|
+
setParameters(Array.isArray(card.parameters) ? card.parameters.map((rule) => ({
|
|
882
|
+
...rule, min: String(rule.min ?? ''), max: String(rule.max ?? ''), choices: Array.isArray(rule.choices) ? rule.choices.join(',') : String(rule.choices ?? ''),
|
|
883
|
+
})) : [])
|
|
884
|
+
setConversationKind(card.conversation_kind === 'group' ? 'group' : 'dm')
|
|
885
|
+
setGroupId(card.conversation_id || '')
|
|
886
|
+
setSenderScope(card.sender_scope === 'allowlist' ? 'allowlist' : 'any')
|
|
887
|
+
setSenderIds(Array.isArray(card.sender_ids) ? card.sender_ids.join('\n') : '')
|
|
888
|
+
setDays(7)
|
|
889
|
+
setDraftNotice('已复制到新草稿。复制不保留原有效期;开放前请重新核对群、请求人和执行方,默认有效期为 7 天。')
|
|
890
|
+
setCreateOpen(true)
|
|
891
|
+
onSelectTab('capability')
|
|
892
|
+
}
|
|
784
893
|
const cancelTask = async (task) => {
|
|
785
894
|
if (!window.confirm(`取消任务 ${task.task_id}?已经发生的读取不能撤回。`)) return
|
|
786
895
|
setBusy(true)
|
|
@@ -798,19 +907,11 @@ window.__ModuleLoader__.load({
|
|
|
798
907
|
'aria-label': '协作机器人与开放能力' },
|
|
799
908
|
message ? h('p', { role: 'status', style: mutedTextStyle }, message) : null,
|
|
800
909
|
h('div', { role: 'region', 'aria-label': '机器人连接和聊天范围', style: { display: activeTab === 'bot-chat' ? 'grid' : 'none', gap: 14 } },
|
|
801
|
-
h('h3', { style:
|
|
802
|
-
h('p', { style: mutedTextStyle }, '
|
|
910
|
+
h('h3', { style: sectionTitleStyle }, '协作机器人'),
|
|
911
|
+
h('p', { style: mutedTextStyle }, '在所有者开启的私聊或群聊中,同事可以像平常一样和机器人聊天。DSH 负责理解与回答;只有准备执行你开放的能力时,ANT 才核对请求人、群、参数和权限。群里的任务结果会回复原群。'),
|
|
803
912
|
h('p', { role: 'status', style: mutedTextStyle }, '每项能力都单独设定执行方和请求范围。普通对话不会自动获得读取电脑、调用工具或访问其他群的权限。'),
|
|
804
913
|
loadFailures.length ? h('p', { role: 'status', style: mutedTextStyle },
|
|
805
914
|
`部分协作信息暂时无法读取:${loadFailures.map((path) => loadNames[path]).join('、')}。已读取的信息仍显示,请稍后刷新状态。`) : null,
|
|
806
|
-
h('div', { style: { border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8, padding: 12,
|
|
807
|
-
display: 'grid', gap: 8 } },
|
|
808
|
-
h('strong', null, '机器人和聊天范围'),
|
|
809
|
-
h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 240px), 1fr))', gap: 8 } },
|
|
810
|
-
h('div', { style: { padding: 10, borderRadius: 8, backgroundColor: 'var(--dsw-alias-bg-layer-2)' } },
|
|
811
|
-
h('strong', null, '私聊'), h('p', { style: { ...mutedTextStyle, marginTop: 4 } }, '默认可用,可正常聊天;执行任务仍需单独授权。')),
|
|
812
|
-
h('div', { style: { padding: 10, borderRadius: 8, backgroundColor: 'var(--dsw-alias-bg-layer-2)' } },
|
|
813
|
-
h('strong', null, '群聊'), h('p', { style: { ...mutedTextStyle, marginTop: 4 } }, '可同时启用;群成员是否能执行任务由每项能力授权决定。'))),
|
|
814
915
|
h('span', { role: 'status', style: mutedTextStyle }, loading ? '正在读取机器人与任务状态…'
|
|
815
916
|
: botLoadError ? '暂时无法读取机器人状态。请刷新后再试。'
|
|
816
917
|
: connectedBots.length === 0 ? '尚未发现已连接的机器人。请先在 DSH-IM 中连接飞书机器人。'
|
|
@@ -820,12 +921,13 @@ window.__ModuleLoader__.load({
|
|
|
820
921
|
: '机器人已连接;协作启用状态暂未读取,请刷新状态。'
|
|
821
922
|
: selectedBot && !selectedEnrollmentBound ? selectedEnrollment
|
|
822
923
|
? `“${selectedBot.name}”已连接;原协作配置尚未绑定到这台 DSH,请在本机重新启用。若原设备令牌曾被撤销,旧任务权限已关闭,绑定后需重新开放。`
|
|
823
|
-
: `“${selectedBot.name}
|
|
824
|
-
: enrolledCount === 0 ? '
|
|
825
|
-
: !selectedBot ? '
|
|
826
|
-
: authorizationLoadError ? `“${selectedBot.name}
|
|
827
|
-
:
|
|
828
|
-
: `“${selectedBot.name}
|
|
924
|
+
: `“${selectedBot.name}”已连接;请先启用本机协作。`
|
|
925
|
+
: enrolledCount === 0 ? '机器人已连接;请选择并启用协作。'
|
|
926
|
+
: !selectedBot ? '已有协作机器人;请选择一台,查看已开放的能力和聊天范围。'
|
|
927
|
+
: authorizationLoadError ? `“${selectedBot.name}”已启用协作;已开放任务暂未读取,请刷新状态。`
|
|
928
|
+
: !dmChatEnabled ? `“${selectedBot.name}”已启用协作;私聊已关闭,群聊仍按各自设置工作。`
|
|
929
|
+
: selectedTaskCount === 0 ? `“${selectedBot.name}”已启用协作;下一步开放一项能力。你也可以先私聊自测普通聊天。`
|
|
930
|
+
: `“${selectedBot.name}”已启用协作 · ${selectedTaskCount} 项能力已开放。可以私聊自测。`),
|
|
829
931
|
bots.length ? h('label', null, '我的机器人', h('select', { value: botId,
|
|
830
932
|
onChange: (event) => { setBotId(event.target.value); setGroupId(''); setChatGroupId(''); setBindingCode('');
|
|
831
933
|
setTrustedOrigin(enrolled.find((bot) => bot.bot_id === event.target.value)?.gitea_origin || '') }, style: inputStyle },
|
|
@@ -841,8 +943,6 @@ window.__ModuleLoader__.load({
|
|
|
841
943
|
selectedBot && !selectedEnrollmentBound ? h('button', { type: 'button', disabled: busy || !botConnected || enrollmentLoadError,
|
|
842
944
|
onClick: () => void enrollBot(), style: secondaryButtonStyle },
|
|
843
945
|
selectedEnrollment ? '绑定原协作配置到这台 DSH' : '启用并绑定到这台 DSH') : null,
|
|
844
|
-
selectedEnrollmentBound && botConnected ? h('p', { style: mutedTextStyle },
|
|
845
|
-
`自测:私聊“${selectedBot.name}”发送“你能做什么”;发起任务后可问“刚才的任务怎么样了”。群聊请先在目标群 @机器人,再开放能力时选择该群。`) : null,
|
|
846
946
|
selectedBot ? h('div', { style: { display: 'grid', gap: 8, padding: 10,
|
|
847
947
|
border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8 } },
|
|
848
948
|
h('strong', null, `可信 Gitea 地址 · ${selectedBot.name}`),
|
|
@@ -854,41 +954,104 @@ window.__ModuleLoader__.load({
|
|
|
854
954
|
h('button', { type: 'button', disabled: busy || !trustedOrigin.trim() || (!selectedEnrollmentBound && !botConnected),
|
|
855
955
|
onClick: () => void (selectedEnrollmentBound ? saveTrustedOrigin() : enrollBot()), style: secondaryButtonStyle },
|
|
856
956
|
selectedEnrollmentBound ? '保存可信地址' : botConnected ? '绑定机器人并保存地址' : '先连接机器人'))) : null,
|
|
957
|
+
setupStepper,
|
|
958
|
+
chatScopePanel,
|
|
959
|
+
selectedEnrollmentBound && botConnected ? h('p', { style: mutedTextStyle },
|
|
960
|
+
dmChatEnabled
|
|
961
|
+
? `自测:私聊“${selectedBot.name}”发送“你能做什么”;发起任务后可问“刚才的任务怎么样了”。群聊请先在目标群 @机器人,再开放能力时选择该群。`
|
|
962
|
+
: '私聊当前已关闭;如要恢复,请在上方开启。群聊请先在目标群 @机器人,再开放能力时选择该群。') : null,
|
|
857
963
|
selectedEnrollmentBound && botConnected ? h('div', { style: { display: 'grid', gap: 8 } },
|
|
858
|
-
h('strong', null, '
|
|
859
|
-
h('span', { style: mutedTextStyle },
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
964
|
+
h('strong', null, '群聊白名单'),
|
|
965
|
+
h('span', { role: 'status', style: mutedTextStyle }, groupChatLoadFailed
|
|
966
|
+
? '群聊列表与当前权限暂时无法读取,请刷新后再更改;仍可填写群 ID。'
|
|
967
|
+
: availableGroups.length
|
|
968
|
+
? '从机器人已发现的群中选择;仍可手动填写群 ID。允许普通聊天不等于允许群成员执行任务。'
|
|
969
|
+
: '先在目标群 @机器人并刷新列表;若平台暂未返回群列表,也可以手动填写群 ID。'),
|
|
970
|
+
h('div', { style: { display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) auto', gap: 8, alignItems: 'end' } },
|
|
971
|
+
h('label', { style: { display: 'grid', gap: 5 } },
|
|
972
|
+
h('span', { style: mutedTextStyle }, '选择已记录的群'),
|
|
973
|
+
h('select', { 'aria-label': '已记录的群', value: availableGroups.some((group) => group.group_id === chatGroupId) ? chatGroupId : '',
|
|
974
|
+
onChange: (event) => setChatGroupId(event.target.value), style: inputStyle },
|
|
975
|
+
h('option', { value: '' }, '选择已发现的群聊'),
|
|
976
|
+
...availableGroups.map((group) => h('option', { key: group.group_id, value: group.group_id },
|
|
977
|
+
`${group.group_code} · ${group.chat_enabled ? '已允许普通聊天' : '未允许普通聊天'}`)))),
|
|
978
|
+
h('button', { type: 'button', disabled: busy || !selectedEnrollmentBound,
|
|
979
|
+
onClick: () => void load().catch((error) => setMessage(error.message)), style: secondaryButtonStyle }, '刷新群列表')),
|
|
980
|
+
h('label', { style: { display: 'grid', gap: 5 } },
|
|
981
|
+
h('span', { style: mutedTextStyle }, '群 ID(平台未返回群名时可手动填写)'),
|
|
982
|
+
h('input', { 'aria-label': '群 ID', value: chatGroupId, maxLength: 256,
|
|
983
|
+
onChange: (event) => setChatGroupId(event.target.value.trim()),
|
|
984
|
+
placeholder: '输入群 ID', style: inputStyle })),
|
|
873
985
|
selectedChatGroup?.chat_enabled && selectedGroupHasTasks ? h('span', { style: mutedTextStyle },
|
|
874
|
-
'
|
|
986
|
+
'本群还有开放能力;关闭普通聊天前请先停止这些能力。') : null,
|
|
987
|
+
h('div', { style: { display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap', paddingTop: 4,
|
|
988
|
+
borderTop: '1px solid var(--dsw-alias-border-l2)' } },
|
|
989
|
+
h('span', { style: mutedTextStyle }, '群内所有者身份'),
|
|
990
|
+
h('span', { style: { ...mutedTextStyle, color: selectedEnrollment.owner_bound
|
|
991
|
+
? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } },
|
|
992
|
+
selectedEnrollment.owner_bound ? '已绑定' : '尚未绑定'),
|
|
993
|
+
h('button', { type: 'button', disabled: busy || !chatGroupId.trim(),
|
|
994
|
+
onClick: () => void issueBindingCode(), style: secondaryButtonStyle },
|
|
995
|
+
selectedEnrollment.owner_bound ? '重新获取绑定码' : '获取绑定码')),
|
|
996
|
+
h('span', { style: mutedTextStyle }, '仅当允许群成员发起受限能力时需要;验证后由机器人所有者在群内确认。'),
|
|
997
|
+
bindingCode ? h('p', { role: 'status', style: mutedTextStyle },
|
|
998
|
+
`请你本人私聊机器人发送“绑定身份 ${bindingCode}”,再回到群里 @机器人完成确认。`) : null) : null,
|
|
875
999
|
h('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap' } },
|
|
876
1000
|
h('button', { type: 'button', onClick: () => { setCreateOpen(true); onSelectTab('capability') },
|
|
877
1001
|
style: primaryButtonStyle }, '开放新能力'),
|
|
878
1002
|
h('button', { type: 'button', onClick: () => { preparePrExample(); onSelectTab('capability') }, style: secondaryButtonStyle }, '使用 PR 差异审查示例'),
|
|
879
1003
|
h('button', { type: 'button', onClick: () => void load().catch((error) => setMessage(error.message)),
|
|
880
|
-
style: secondaryButtonStyle }, '刷新状态')))
|
|
1004
|
+
style: secondaryButtonStyle }, '刷新状态'))),
|
|
881
1005
|
h('div', { role: 'region', 'aria-label': '已开放能力列表', style: { display: activeTab === 'capability' ? 'grid' : 'none', gap: 12 } },
|
|
882
|
-
h('h4', { style:
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
1006
|
+
h('h4', { style: sectionTitleStyle }, `我开放的能力 · ${authorizationLoadError ? '暂未读取' : activeCards.length}`),
|
|
1007
|
+
h('p', { style: mutedTextStyle }, '当前一项能力只能绑定一个范围:私聊,或某一个群。若要给其他群或私聊开放相同任务,目前需分别建立能力卡;它们的请求人和有效期独立。'),
|
|
1008
|
+
...activeCards.map((card) => {
|
|
1009
|
+
const expiresAt = Number(card.expires_at) * 1000
|
|
1010
|
+
const remainingDays = Math.max(1, Math.ceil((expiresAt - Date.now()) / 86_400_000))
|
|
1011
|
+
const expiry = remainingDays <= 7 ? `${remainingDays} 天后到期` : `至 ${new Date(expiresAt).toLocaleDateString('zh-CN')}`
|
|
1012
|
+
const conversation = card.conversation_kind === 'group'
|
|
1013
|
+
? `群 ${groups.find((group) => group.group_id === card.conversation_id)?.group_code || card.conversation_id}` : '私聊'
|
|
1014
|
+
const requester = card.sender_scope === 'allowlist' ? '指定成员可请求'
|
|
1015
|
+
: card.conversation_kind === 'group' ? '群成员可请求' : '可私聊者可请求'
|
|
1016
|
+
const runnerName = card.runner === 'codex' ? '本机 Codex · PR 快照'
|
|
1017
|
+
: card.runner === 'claude-code' ? '本机 Claude Code · PR 快照' : 'DSH 文字处理'
|
|
1018
|
+
const parametersText = (card.parameters || []).map((rule) => {
|
|
1019
|
+
const constraint = rule.kind === 'gitea_pr' && rule.repository ? `仓库 ${rule.repository}`
|
|
1020
|
+
: rule.kind === 'choice' ? `选项 ${(rule.choices || []).join('、')}`
|
|
1021
|
+
: ['integer', 'gitea_pr'].includes(rule.kind)
|
|
1022
|
+
? `范围 ${rule.min ?? 1}–${rule.max ?? '无上限'}` : ''
|
|
1023
|
+
return `${rule.name}:${rule.description || rule.kind}${rule.required ? '(必填)' : '(可选)'}${constraint ? ` · ${constraint}` : ''}`
|
|
1024
|
+
}).join(';')
|
|
1025
|
+
const examplesText = Array.isArray(card.examples) ? card.examples.join('、') : ''
|
|
1026
|
+
const requesterIds = card.sender_scope === 'allowlist' && Array.isArray(card.sender_ids) ? card.sender_ids.join('、') : ''
|
|
1027
|
+
const remainingDaysLabel = remainingDays <= 7 ? 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))'
|
|
1028
|
+
: 'var(--dsw-alias-label-secondary)'
|
|
1029
|
+
return h('div', { key: card.id, style: { display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) auto',
|
|
1030
|
+
alignItems: 'start', gap: 12, borderTop: '1px solid var(--dsw-alias-border-l2)', padding: '10px 0' } },
|
|
1031
|
+
h('div', { style: { display: 'grid', gap: 4, minWidth: 0 } },
|
|
1032
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } },
|
|
1033
|
+
h('strong', { style: { overflowWrap: 'anywhere' } }, card.title || '未命名能力'),
|
|
1034
|
+
h('span', { style: { ...mutedTextStyle, padding: '2px 8px', borderRadius: 999,
|
|
1035
|
+
backgroundColor: 'var(--dsw-alias-bg-layer-2)', color: remainingDaysLabel, whiteSpace: 'nowrap' } }, expiry)),
|
|
1036
|
+
h('span', { style: mutedTextStyle }, `${runnerName} · ${conversation} · ${requester} · 结果回复原${card.conversation_kind === 'group' ? '群' : '私聊'}`),
|
|
1037
|
+
h('details', null,
|
|
1038
|
+
h('summary', { style: { cursor: 'pointer', color: 'var(--dsw-alias-link)', fontSize: 13 } }, '查看配置'),
|
|
1039
|
+
h('div', { style: { display: 'grid', gap: 4, paddingTop: 6 } },
|
|
1040
|
+
card.intent ? h('p', { style: mutedTextStyle }, `适用请求:${card.intent}`) : null,
|
|
1041
|
+
parametersText ? h('p', { style: { ...mutedTextStyle, overflowWrap: 'anywhere' } }, `请求参数:${parametersText}`)
|
|
1042
|
+
: h('p', { style: mutedTextStyle }, '请求参数:无'),
|
|
1043
|
+
requesterIds ? h('p', { style: { ...mutedTextStyle, overflowWrap: 'anywhere' } }, `允许请求的 IM 用户:${requesterIds}`) : null,
|
|
1044
|
+
examplesText ? h('p', { style: mutedTextStyle }, `请求示例:${examplesText}`) : null,
|
|
1045
|
+
card.task_prompt ? h('p', { style: mutedTextStyle }, `处理要求:${card.task_prompt}`) : null))),
|
|
1046
|
+
h('div', { style: { display: 'flex', gap: 6, flexWrap: 'wrap', justifyContent: 'flex-end' } },
|
|
1047
|
+
h('button', { type: 'button', disabled: busy, onClick: () => copyCardToDraft(card), style: secondaryButtonStyle }, '复制'),
|
|
1048
|
+
h('button', { type: 'button', disabled: busy, onClick: () => void revokeCard(card), style: secondaryButtonStyle }, '停止')))
|
|
1049
|
+
}),
|
|
887
1050
|
activeCards.length === 0 ? h('p', { style: mutedTextStyle },
|
|
888
1051
|
authorizationLoadError ? '暂时无法确认已开放的能力,请刷新状态后再试。'
|
|
889
1052
|
: '还没有开放能力。先选机器人,再开放一项能力;PR 审查只是可选示例。') : null),
|
|
890
1053
|
h('div', { role: 'region', 'aria-label': '最近任务', style: { display: activeTab === 'local' ? 'grid' : 'none', gap: 12 } },
|
|
891
|
-
h('h4', { style:
|
|
1054
|
+
h('h4', { style: sectionTitleStyle }, '最近任务'),
|
|
892
1055
|
...(tasks.filter((task) => task.task_id).length ? tasks.filter((task) => task.task_id)
|
|
893
1056
|
.map((task) => {
|
|
894
1057
|
const title = task.title || (task.pr_number
|
|
@@ -913,10 +1076,14 @@ window.__ModuleLoader__.load({
|
|
|
913
1076
|
onToggle: (event) => setCreateOpen(event.currentTarget.open),
|
|
914
1077
|
style: { border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8, padding: 12 } },
|
|
915
1078
|
h('summary', { style: { cursor: 'pointer', fontWeight: 600 } }, '开放一项能力'),
|
|
916
|
-
h('
|
|
1079
|
+
h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 320px), 1fr))',
|
|
1080
|
+
gap: 16, alignItems: 'start', marginTop: 12 } },
|
|
1081
|
+
h('form', { id: 'ant-capability-create-form', onSubmit: createCard, style: { display: 'grid', gap: 9, minWidth: 0 } },
|
|
917
1082
|
h('p', { style: mutedTextStyle }, selectedBot
|
|
918
1083
|
? `当前机器人:${selectedBot.name}${selectedEnrollmentBound ? ' · 已绑定此设备' : ' · 请先绑定到此设备'}`
|
|
919
1084
|
: '请先在上方选择机器人并启用协作。'),
|
|
1085
|
+
draftNotice ? h('p', { role: 'status', style: { ...mutedTextStyle,
|
|
1086
|
+
color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } }, draftNotice) : null,
|
|
920
1087
|
selectedEnrollmentBound ? h('p', { style: mutedTextStyle },
|
|
921
1088
|
'机器人及其任务能力只在这台 DSH 设备接收和执行;同一账号的其他设备不会继承本机授权。') : null,
|
|
922
1089
|
selectedEnrollment && !selectedEnrollmentBound ? h('p', { style: mutedTextStyle },
|
|
@@ -946,8 +1113,6 @@ window.__ModuleLoader__.load({
|
|
|
946
1113
|
'Claude Code 通过 DSH 已注册的子代理运行,可查看本次 PR 的临时代码快照。ANT 不会传 Gitea 令牌;Claude Code 沿用 DSH 提供方的本机权限,不具备 Codex 路径相同的固定只读隔离。Git LFS 实际文件不会下载。') : null,
|
|
947
1114
|
selectedRunnerNeedsPr ? h('p', { role: 'status', style: mutedTextStyle },
|
|
948
1115
|
'本机 PR 执行方需要 Gitea granular token 的只读权限 read:repository 和 read:user;read:user 仅用于这台 DSH 获取 Git HTTPS 登录名。') : null,
|
|
949
|
-
h('p', { role: 'status', style: mutedTextStyle },
|
|
950
|
-
`开放后:${requesterSummary}可请求 · 有效期 ${days} 天 · ${resultRouteSummary}`),
|
|
951
1116
|
h('details', { style: { border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8, padding: 10 } },
|
|
952
1117
|
h('summary', { style: { cursor: 'pointer' } }, '参数与授权范围'),
|
|
953
1118
|
h('div', null, h('strong', null, '需要从请求中确认的参数'),
|
|
@@ -963,7 +1128,7 @@ window.__ModuleLoader__.load({
|
|
|
963
1128
|
onChange: (event) => updateParameter(index, { kind: event.target.value }), style: { ...inputStyle, flex: '1 1 150px' } },
|
|
964
1129
|
h('option', { value: 'text' }, '文本'), h('option', { value: 'integer' }, '整数'),
|
|
965
1130
|
h('option', { value: 'choice' }, '选项'), h('option', { value: 'gitea_pr' }, 'Gitea PR 编号')),
|
|
966
|
-
h('label', null, h('input', { type: 'checkbox', checked: rule.required,
|
|
1131
|
+
h('label', null, h('input', { type: 'checkbox', checked: rule.required, 'aria-label': '参数必填',
|
|
967
1132
|
onChange: (event) => updateParameter(index, { required: event.target.checked }) }), ' 必填'),
|
|
968
1133
|
['integer', 'gitea_pr'].includes(rule.kind) ? h('input', { 'aria-label': '最小值', type: 'number', value: rule.min,
|
|
969
1134
|
onChange: (event) => updateParameter(index, { min: event.target.value }), placeholder: '最小值', style: { ...inputStyle, width: 120 } }) : null,
|
|
@@ -996,12 +1161,6 @@ window.__ModuleLoader__.load({
|
|
|
996
1161
|
`识别码 ${group.group_code} · ${group.group_id}`)))) : null,
|
|
997
1162
|
conversationKind === 'group' ? h('button', { type: 'button', disabled: busy,
|
|
998
1163
|
onClick: () => void load().catch((error) => setMessage(error.message)), style: secondaryButtonStyle }, '刷新群列表') : null,
|
|
999
|
-
conversationKind === 'group' && selectedEnrollmentBound ? h('div', { style: { display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' } },
|
|
1000
|
-
h('span', { style: mutedTextStyle }, selectedEnrollment.owner_bound ? '已绑定你的飞书身份,可在群内批准任务' : '群内批准前需绑定你的飞书身份'),
|
|
1001
|
-
h('button', { type: 'button', disabled: busy, onClick: () => void issueBindingCode(), style: secondaryButtonStyle },
|
|
1002
|
-
selectedEnrollment.owner_bound ? '重新获取绑定码' : '获取身份绑定码')) : null,
|
|
1003
|
-
conversationKind === 'group' && bindingCode ? h('p', { role: 'status', style: mutedTextStyle },
|
|
1004
|
-
`请你本人私聊机器人发送“绑定身份 ${bindingCode}”。完成后在群里 @机器人发送“在本群启用任务 任务名称”,并按提示确认;结果会公开回复该群。`) : null,
|
|
1005
1164
|
h('label', null, '谁可以请求', h('select', { value: senderScope,
|
|
1006
1165
|
onChange: (event) => setSenderScope(event.target.value), style: inputStyle },
|
|
1007
1166
|
h('option', { value: 'any' }, conversationKind === 'group' ? '此群任何成员' : '任何能私聊此机器人者'),
|
|
@@ -1010,9 +1169,29 @@ window.__ModuleLoader__.load({
|
|
|
1010
1169
|
onChange: (event) => setSenderIds(event.target.value), placeholder: '多个 ID 用逗号分隔', style: inputStyle }) : null,
|
|
1011
1170
|
h('label', null, '有效期', h('select', { value: days, onChange: (event) => setDays(Number(event.target.value)), style: inputStyle },
|
|
1012
1171
|
h('option', { value: 1 }, '1 天'), h('option', { value: 7 }, '7 天'), h('option', { value: 30 }, '30 天')))),
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1172
|
+
),
|
|
1173
|
+
h('aside', { role: 'region', 'aria-label': '开放前复核', style: { ...panelStyle, padding: 14, gap: 10,
|
|
1174
|
+
position: 'sticky', top: 12, alignSelf: 'start' } },
|
|
1175
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8 } },
|
|
1176
|
+
h('h4', { style: sectionTitleStyle }, '开放前复核'),
|
|
1177
|
+
h('span', { style: { ...mutedTextStyle, padding: '3px 8px', borderRadius: 999,
|
|
1178
|
+
backgroundColor: 'var(--dsw-alias-bg-layer-2)' } }, '草稿 · 未开放')),
|
|
1179
|
+
h('p', { style: mutedTextStyle }, '保存后,机器人只会在以下范围内执行这项能力。'),
|
|
1180
|
+
h('dl', { style: { display: 'grid', gap: 8, margin: 0 } },
|
|
1181
|
+
...[
|
|
1182
|
+
['触发位置', conversationKind === 'group' ? `群 ${groupId || '尚未选择'}` : '私聊'],
|
|
1183
|
+
['可请求人', requesterSummary],
|
|
1184
|
+
['执行方', executorSummary],
|
|
1185
|
+
['读取资料', resourceSummary],
|
|
1186
|
+
['有效期', `${days} 天`],
|
|
1187
|
+
['回复位置', resultRouteSummary],
|
|
1188
|
+
].map(([label, value]) => h('div', { key: label, style: { display: 'grid', gridTemplateColumns: '84px minmax(0, 1fr)', gap: 8 } },
|
|
1189
|
+
h('dt', { style: mutedTextStyle }, label),
|
|
1190
|
+
h('dd', { style: { margin: 0, minWidth: 0, overflowWrap: 'anywhere' } }, value)))),
|
|
1191
|
+
cardBlockReason ? h('p', { role: 'status', style: { ...mutedTextStyle,
|
|
1192
|
+
color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } }, cardBlockReason) : null,
|
|
1193
|
+
h('button', { type: 'submit', form: 'ant-capability-create-form', disabled: busy || Boolean(cardBlockReason),
|
|
1194
|
+
style: { ...primaryButtonStyle, width: '100%' } }, '确认并开放能力'))))),
|
|
1016
1195
|
h('div', { role: 'region', 'aria-label': '本机处理设置', style: { display: activeTab === 'local' ? 'grid' : 'none', gap: 12 } },
|
|
1017
1196
|
h('section', { style: { ...panelStyle, padding: 14 } },
|
|
1018
1197
|
h('strong', null, 'Gitea 本机凭据'),
|
|
@@ -2056,19 +2235,25 @@ window.__ModuleLoader__.load({
|
|
|
2056
2235
|
const updateGuideUrl = pinnedServiceUrl
|
|
2057
2236
|
? new URL('/ant-update', pinnedServiceUrl).href : null
|
|
2058
2237
|
const modelRows = authorizedModels.map((model) => h('div', { key: `${model.protocol}:${model.model_id}`, role: 'listitem',
|
|
2059
|
-
style: { display: '
|
|
2060
|
-
padding: '10px 2px', borderBottom: '1px solid var(--dsw-alias-border-l2)' } },
|
|
2061
|
-
h('strong',
|
|
2062
|
-
h('span', { style:
|
|
2063
|
-
|
|
2238
|
+
style: { display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(120px, auto) minmax(130px, auto)',
|
|
2239
|
+
gap: 12, alignItems: 'center', padding: '10px 2px', borderBottom: '1px solid var(--dsw-alias-border-l2)' } },
|
|
2240
|
+
h('strong', { style: { minWidth: 0, overflowWrap: 'anywhere' } }, model.display_name || model.model_id),
|
|
2241
|
+
h('span', { style: { display: 'inline-flex', justifySelf: 'start', padding: '3px 8px', borderRadius: 999,
|
|
2242
|
+
backgroundColor: 'var(--dsw-alias-bg-layer-2)', color: 'var(--dsw-alias-label-secondary)', fontSize: 12 } },
|
|
2243
|
+
`${model.protocol === 'anthropic' ? 'Anthropic' : 'OpenAI'} 协议`),
|
|
2244
|
+
h('span', { style: { ...mutedTextStyle, textAlign: 'right' } }, '已同步到 DSH')))
|
|
2064
2245
|
const connectionPanel = h('section', { style: { ...panelStyle, display: activeTab === 'connection' ? 'grid' : 'none' } },
|
|
2065
2246
|
h('div', null,
|
|
2066
|
-
h('h3', { style:
|
|
2247
|
+
h('h3', { style: sectionTitleStyle }, 'ANT 团队账号'),
|
|
2067
2248
|
h('p', { style: { ...mutedTextStyle, marginTop: 4 } }, '邮箱验证建立这台 DSH 的成员凭证;断开后会清除此设备凭证和同步的模型设置。')),
|
|
2068
2249
|
h('span', { role: 'status', style: mutedTextStyle }, credentialConfigured === true && !bindingReady
|
|
2069
2250
|
? '本机有 ANT 凭证,但尚未确认它绑定的团队;请完成上方的绑定检查。'
|
|
2070
2251
|
: credentialConfigured === true ? 'DSH 已安全保存此设备的 ANT 团队凭证。'
|
|
2071
2252
|
: credentialConfigured === false ? '验证工作邮箱后即可连接团队服务。' : '正在检查 ANT 连接状态…'),
|
|
2253
|
+
h('div', { role: 'note', style: { display: 'grid', gap: 3, padding: '9px 11px', borderRadius: 8,
|
|
2254
|
+
backgroundColor: 'var(--dsw-alias-bg-layer-2)' } },
|
|
2255
|
+
h('strong', { style: { fontSize: 13, fontWeight: 600 } }, '配置保存范围'),
|
|
2256
|
+
h('p', { style: mutedTextStyle }, '团队模型与能力授权由 ANT 管理;成员凭证、Gitea 令牌、本机缓存和任务记录保存在这台 DSH。')),
|
|
2072
2257
|
serviceCompatibility.state !== 'idle' ? h('p', { role: 'status', style: { ...mutedTextStyle,
|
|
2073
2258
|
color: serviceCompatibility.state === 'incompatible' ? 'var(--dsw-alias-state-error-primary)' : mutedTextStyle.color } },
|
|
2074
2259
|
serviceCompatibility.message) : null,
|
|
@@ -2113,7 +2298,7 @@ window.__ModuleLoader__.load({
|
|
|
2113
2298
|
const modelPanel = h('section', { style: { ...panelStyle, display: activeTab === 'models' ? 'grid' : 'none' } },
|
|
2114
2299
|
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12 } },
|
|
2115
2300
|
h('div', null,
|
|
2116
|
-
h('h3', { style:
|
|
2301
|
+
h('h3', { style: sectionTitleStyle }, '团队模型'),
|
|
2117
2302
|
h('p', { style: { ...mutedTextStyle, marginTop: 4 } }, '每个获准模型单独列出;刷新后同步到 DSH 模型选择器。')),
|
|
2118
2303
|
h('span', { style: { flexShrink: 0, padding: '4px 9px', borderRadius: 999,
|
|
2119
2304
|
color: credentialConfigured === true && bindingReady ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-label-secondary)',
|
|
@@ -2133,36 +2318,50 @@ window.__ModuleLoader__.load({
|
|
|
2133
2318
|
lastModelSync ? h('span', { style: mutedTextStyle }, `最近同步:${lastModelSync.toLocaleString('zh-CN')}`) : null),
|
|
2134
2319
|
modelMessage && !modelMessage.startsWith('正在') ? h('p', { role: 'status', style: mutedTextStyle }, modelMessage) : null)
|
|
2135
2320
|
|
|
2321
|
+
const updateStatusLabels = {
|
|
2322
|
+
available: '有新版', 'artifact-missing': '等待发布制品', revoked: '安装包已撤回',
|
|
2323
|
+
'migration-required': '需要迁移', unknown: '状态待确认', failed: '检查失败',
|
|
2324
|
+
installing: '正在安装', done: '需重启 DSH', current: '已是最新', checking: '正在检查', none: '尚未发布',
|
|
2325
|
+
}
|
|
2326
|
+
const updateStatusNeedsDetails = ['artifact-missing', 'revoked', 'migration-required', 'unknown', 'failed', 'installing', 'done']
|
|
2327
|
+
const updateVersionSummary = bundleUpdate.currentVersion && bundleRelease?.version
|
|
2328
|
+
? bundleUpdate.currentVersion === bundleRelease.version
|
|
2329
|
+
? `当前版本 ${bundleUpdate.currentVersion} · 已是最新`
|
|
2330
|
+
: `当前版本 ${bundleUpdate.currentVersion} · 最新版本 ${bundleRelease.version}`
|
|
2331
|
+
: bundleUpdate.currentVersion ? `当前版本 ${bundleUpdate.currentVersion}`
|
|
2332
|
+
: bundleRelease?.version ? `最新版本 ${bundleRelease.version}` : ''
|
|
2333
|
+
const showReleaseNotes = canCheckUpdate && bundleRelease?.version && bundleRelease.release_notes
|
|
2334
|
+
&& ['current', 'available', 'artifact-missing'].includes(bundleUpdate.state)
|
|
2136
2335
|
const updatePanel = h('section', { style: { ...panelStyle, display: activeTab === 'updates' ? 'grid' : 'none' } },
|
|
2137
2336
|
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12 } },
|
|
2138
2337
|
h('div', null,
|
|
2139
|
-
h('h3', { style:
|
|
2140
|
-
h('p', { style: { ...mutedTextStyle, marginTop: 4 } }, '
|
|
2141
|
-
h('span', { style: { ...mutedTextStyle,
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2338
|
+
h('h3', { style: sectionTitleStyle }, '关于 ANT'),
|
|
2339
|
+
h('p', { style: { ...mutedTextStyle, marginTop: 4 } }, '查看版本、更新说明和兼容状态。')),
|
|
2340
|
+
h('span', { style: { ...mutedTextStyle, flexShrink: 0,
|
|
2341
|
+
color: bundleUpdate.state === 'revoked' || serviceCompatibility.state === 'incompatible'
|
|
2342
|
+
? 'var(--dsw-alias-state-error-primary)' : mutedTextStyle.color } },
|
|
2343
|
+
updateStatusLabels[bundleUpdate.state] || '等待检查')),
|
|
2344
|
+
updateVersionSummary ? h('p', { style: { ...mutedTextStyle, color: 'var(--dsw-alias-label-primary)', fontWeight: 550 } }, updateVersionSummary) : null,
|
|
2345
|
+
showReleaseNotes ? h('section', { style: { display: 'grid', gap: 6, padding: 12, borderRadius: 8,
|
|
2346
|
+
backgroundColor: 'var(--dsw-alias-bg-layer-2)' } },
|
|
2347
|
+
h('strong', { style: { fontSize: 14, lineHeight: 1.4 } }, '本版更新'),
|
|
2348
|
+
h('p', { style: mutedTextStyle }, bundleRelease.release_notes)) : null,
|
|
2349
|
+
['incompatible', 'unknown'].includes(serviceCompatibility.state) ? h('p', { role: 'status', style: {
|
|
2148
2350
|
...mutedTextStyle,
|
|
2149
|
-
color: serviceCompatibility.state === 'incompatible' ? 'var(--dsw-alias-state-error-primary)'
|
|
2150
|
-
|
|
2151
|
-
|
|
2351
|
+
color: serviceCompatibility.state === 'incompatible' ? 'var(--dsw-alias-state-error-primary)'
|
|
2352
|
+
: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))',
|
|
2353
|
+
} }, `服务端兼容性:${serviceCompatibility.message}`) : null,
|
|
2354
|
+
bundleUpdate.message && updateStatusNeedsDetails.includes(bundleUpdate.state) ? h('p', { role: 'status', style: bundleUpdate.state === 'revoked'
|
|
2152
2355
|
? { ...mutedTextStyle, color: 'var(--dsw-alias-state-error-primary)' } : mutedTextStyle }, bundleUpdate.message) : null,
|
|
2153
2356
|
!canCheckUpdate && updateGuideUrl ? h('a', { href: updateGuideUrl, target: '_blank', rel: 'noopener noreferrer',
|
|
2154
|
-
style: { color: 'var(--dsw-alias-link)', fontSize: 13 } },
|
|
2357
|
+
style: { color: 'var(--dsw-alias-link)', fontSize: 13 } }, '打开更新指引 ↗') : null,
|
|
2155
2358
|
!canCheckUpdate && !updateGuideUrl ? h('p', { style: mutedTextStyle }, '无法核对原团队地址时,请向企业管理员索取更新指引。') : null,
|
|
2156
2359
|
!canCheckUpdate ? h('button', { type: 'button', onClick: () => onSelectTab('connection'), style: secondaryButtonStyle },
|
|
2157
2360
|
'前往团队连接') : null,
|
|
2158
|
-
canCheckUpdate &&
|
|
2159
|
-
? h('
|
|
2160
|
-
bundleRelease.release_notes ? h('div', { style: { ...panelStyle, padding: 12 } },
|
|
2161
|
-
h('strong', null, '更新说明'), h('p', { style: mutedTextStyle }, bundleRelease.release_notes)) : null,
|
|
2162
|
-
bundleUpdate.state === 'available'
|
|
2163
|
-
? h('button', { type: 'button', disabled: busy, onClick: () => void installBundleUpdate(), style: primaryButtonStyle }, busy ? '更新中…' : '更新 ANT') : null) : null,
|
|
2361
|
+
canCheckUpdate && bundleUpdate.state === 'available'
|
|
2362
|
+
? h('button', { type: 'button', disabled: busy, onClick: () => void installBundleUpdate(), style: primaryButtonStyle }, busy ? '更新中…' : '更新 ANT') : null,
|
|
2164
2363
|
h('button', { type: 'button', disabled: busy || !canCheckUpdate || bundleUpdate.state === 'checking',
|
|
2165
|
-
onClick: () => void checkBundleUpdate(), style: secondaryButtonStyle }, bundleUpdate.state === 'checking' ? '
|
|
2364
|
+
onClick: () => void checkBundleUpdate(), style: secondaryButtonStyle }, bundleUpdate.state === 'checking' ? '正在检查…' : '检查更新'))
|
|
2166
2365
|
|
|
2167
2366
|
return h('div', { style: { display: 'grid', gap: 16, width: '100%' } },
|
|
2168
2367
|
connectionPanel, modelPanel, updatePanel)
|
package/delegation.js
CHANGED
|
@@ -1048,6 +1048,8 @@ export function startDelegationHost(ctx, readConnection, { ledger = new Delegati
|
|
|
1048
1048
|
const consumers = new Map()
|
|
1049
1049
|
let enrolledFingerprints = new Set()
|
|
1050
1050
|
let enrolledOrigins = new Map()
|
|
1051
|
+
let dmChatEnabledByFingerprint = new Map()
|
|
1052
|
+
const dmDisabledNoticeAt = new Map()
|
|
1051
1053
|
let legacyDmGrants = []
|
|
1052
1054
|
let legacyGroupGrants = []
|
|
1053
1055
|
let enrollmentKnown = false
|
|
@@ -1270,6 +1272,25 @@ export function startDelegationHost(ctx, readConnection, { ledger = new Delegati
|
|
|
1270
1272
|
scheduleProcess(entry, 'ANT owner binding reply stalled:')
|
|
1271
1273
|
return { accepted: true }
|
|
1272
1274
|
}
|
|
1275
|
+
if (dmChatEnabledByFingerprint.get(event.fingerprint) === false) {
|
|
1276
|
+
const noticeKey = `${event.fingerprint}\0${event.actor.id}`
|
|
1277
|
+
const now = Date.now()
|
|
1278
|
+
for (const [key, at] of dmDisabledNoticeAt) if (now - at >= 5 * 60_000) dmDisabledNoticeAt.delete(key)
|
|
1279
|
+
if (now - (dmDisabledNoticeAt.get(noticeKey) || 0) >= 5 * 60_000) {
|
|
1280
|
+
dmDisabledNoticeAt.set(noticeKey, now)
|
|
1281
|
+
while (dmDisabledNoticeAt.size > 500) dmDisabledNoticeAt.delete(dmDisabledNoticeAt.keys().next().value)
|
|
1282
|
+
try {
|
|
1283
|
+
const notice = await ctx.dshIm.replyChecked(event.botId, event.reply,
|
|
1284
|
+
'机器人所有者已关闭此机器人的私聊。请联系所有者在 ANT 插件的“机器人与聊天”页重新开启;群聊设置不受影响。',
|
|
1285
|
+
{ expectedFingerprint: event.fingerprint, signal: signal || controller.signal })
|
|
1286
|
+
if (notice?.sent !== true) dmDisabledNoticeAt.delete(noticeKey)
|
|
1287
|
+
} catch (error) {
|
|
1288
|
+
dmDisabledNoticeAt.delete(noticeKey)
|
|
1289
|
+
logger.warn?.('ANT 私聊关闭提示暂不可用:', error?.message)
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
return { accepted: true }
|
|
1293
|
+
}
|
|
1273
1294
|
const proposedLegacy = parseDelegatedPrRequest(text)
|
|
1274
1295
|
const matchingLegacy = proposedLegacy.kind === 'pr_review' && legacyDmGrants.some((grant) =>
|
|
1275
1296
|
grant.bot_fingerprint === event.fingerprint && grant.pr_number === proposedLegacy.pr_number
|
|
@@ -1482,6 +1503,7 @@ export function startDelegationHost(ctx, readConnection, { ledger = new Delegati
|
|
|
1482
1503
|
consumers.clear()
|
|
1483
1504
|
enrolledFingerprints = new Set()
|
|
1484
1505
|
enrolledOrigins = new Map()
|
|
1506
|
+
dmChatEnabledByFingerprint = new Map()
|
|
1485
1507
|
legacyDmGrants = []
|
|
1486
1508
|
legacyGroupGrants = []
|
|
1487
1509
|
enrollmentKnown = false
|
|
@@ -1523,6 +1545,8 @@ export function startDelegationHost(ctx, readConnection, { ledger = new Delegati
|
|
|
1523
1545
|
if (enrollmentKnown) {
|
|
1524
1546
|
enrolledFingerprints = new Set(deviceEnrolled.map((bot) => bot.bot_fingerprint))
|
|
1525
1547
|
enrolledOrigins = new Map(deviceEnrolled.map((bot) => [bot.bot_fingerprint, bot.gitea_origin]))
|
|
1548
|
+
dmChatEnabledByFingerprint = new Map(deviceEnrolled.map((bot) =>
|
|
1549
|
+
[bot.bot_fingerprint, bot.dm_chat_enabled !== false]))
|
|
1526
1550
|
}
|
|
1527
1551
|
let groups = []
|
|
1528
1552
|
try {
|
|
@@ -1605,6 +1629,11 @@ export function startDelegationHost(ctx, readConnection, { ledger = new Delegati
|
|
|
1605
1629
|
if (refreshing) await refreshDone
|
|
1606
1630
|
if (!stopped) await refresh()
|
|
1607
1631
|
}
|
|
1632
|
+
stop.setDmChat = (fingerprint, enabled) => {
|
|
1633
|
+
if (typeof fingerprint === 'string' && typeof enabled === 'boolean') {
|
|
1634
|
+
dmChatEnabledByFingerprint.set(fingerprint.toLowerCase(), enabled)
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1608
1637
|
stop.cancelTask = async (taskId, serviceUrl) => {
|
|
1609
1638
|
await ready
|
|
1610
1639
|
if (stopped || !ledgerReady) return false
|
package/index.js
CHANGED
|
@@ -25,6 +25,7 @@ const DELEGATION_BOTS_PATH = '/api/ant-teams/delegation/bots'
|
|
|
25
25
|
const DELEGATION_GRANTS_PATH = '/api/ant-teams/delegation/grants'
|
|
26
26
|
const DELEGATION_ENROLLED_BOTS_PATH = '/api/ant-teams/delegation/enrolled-bots'
|
|
27
27
|
const DELEGATION_GROUPS_PATH = '/api/ant-teams/delegation/groups'
|
|
28
|
+
const DELEGATION_DM_CHAT_PATH = '/api/ant-teams/delegation/dm-chat'
|
|
28
29
|
const DELEGATION_OWNER_CHALLENGE_PATH = '/api/ant-teams/delegation/owner-challenge'
|
|
29
30
|
const DELEGATION_STATUS_PATH = '/api/ant-teams/delegation/status'
|
|
30
31
|
const DELEGATION_CARDS_PATH = '/api/ant-teams/delegation/cards'
|
|
@@ -281,7 +282,8 @@ async function fetchDelegationGrants(request, ctx, credentials, settings) {
|
|
|
281
282
|
}
|
|
282
283
|
|
|
283
284
|
async function fetchDelegationGroupSettings(request, ctx, credentials, settings, kind, getHost) {
|
|
284
|
-
const methods = kind === 'groups' ? ['GET', 'PUT'] : kind === '
|
|
285
|
+
const methods = kind === 'groups' ? ['GET', 'PUT'] : kind === 'dm-chat' ? ['PUT']
|
|
286
|
+
: kind === 'challenge' ? ['POST'] : ['GET', 'POST', 'DELETE']
|
|
285
287
|
if (!methods.includes(request.method)) return jsonResponse(405, { message: '请求方式不支持。' })
|
|
286
288
|
let connection
|
|
287
289
|
try { connection = await readMemberConnection(credentials, settings) } catch {
|
|
@@ -312,6 +314,7 @@ async function fetchDelegationGroupSettings(request, ctx, credentials, settings,
|
|
|
312
314
|
return jsonResponse(400, { message: '机器人编号无效。' })
|
|
313
315
|
}
|
|
314
316
|
const path = kind === 'groups' ? '/api/me/im-delegations/groups'
|
|
317
|
+
: kind === 'dm-chat' ? '/api/me/im-delegations/dm-chat'
|
|
315
318
|
: kind === 'challenge' ? '/api/me/im-delegations/owner-challenge'
|
|
316
319
|
: request.method === 'DELETE'
|
|
317
320
|
? `/api/me/im-delegations/bots/${encodeURIComponent(body.bot_id)}`
|
|
@@ -331,11 +334,13 @@ async function fetchDelegationGroupSettings(request, ctx, credentials, settings,
|
|
|
331
334
|
redirect: 'manual', signal: AbortSignal.timeout(12_000),
|
|
332
335
|
})
|
|
333
336
|
if (response.status === 404 && request.method !== 'DELETE') {
|
|
334
|
-
return jsonResponse(426, { message: '
|
|
337
|
+
return jsonResponse(426, { message: '团队服务端尚未支持这项协作设置,请联系管理员升级 ANT 服务端。' })
|
|
335
338
|
}
|
|
336
339
|
if (response.status === 204) {
|
|
337
|
-
if (
|
|
338
|
-
const
|
|
340
|
+
if (['groups', 'dm-chat'].includes(kind) && request.method === 'PUT') {
|
|
341
|
+
const host = getHost?.()
|
|
342
|
+
if (kind === 'dm-chat') host?.setDmChat?.(body.bot_fingerprint, body.enabled)
|
|
343
|
+
const refresh = host?.refresh
|
|
339
344
|
if (refresh) await refresh().catch(() => undefined)
|
|
340
345
|
}
|
|
341
346
|
return new Response(null, { status: 204 })
|
|
@@ -943,6 +948,7 @@ export function apply(ctx) {
|
|
|
943
948
|
for (const [path, methods, kind] of [
|
|
944
949
|
[DELEGATION_ENROLLED_BOTS_PATH, ['GET', 'POST', 'DELETE'], 'bots'],
|
|
945
950
|
[DELEGATION_GROUPS_PATH, ['GET', 'PUT'], 'groups'],
|
|
951
|
+
[DELEGATION_DM_CHAT_PATH, ['PUT'], 'dm-chat'],
|
|
946
952
|
[DELEGATION_OWNER_CHALLENGE_PATH, ['POST'], 'challenge'],
|
|
947
953
|
]) {
|
|
948
954
|
connectionCtx.effect(() => connectionCtx.connection.fetch.register({
|