@ai-wendao/ant 0.3.14 → 0.3.16
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 +228 -85
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,13 +6,13 @@ ANT (AI-native Teams) connects a DeepSeek Harness Desktop profile to its team's
|
|
|
6
6
|
|
|
7
7
|
## 企业精选技能
|
|
8
8
|
|
|
9
|
-
团队管理员可在 ANT 管理台从 GitHub 目录、ZIP、TAR、TAR.GZ/TGZ 或单份 `SKILL.md` 导入技能,核对来源、文件和依赖后发布。已连接团队的成员从 DSH
|
|
9
|
+
团队管理员可在 ANT 管理台从 GitHub 目录、ZIP、TAR、TAR.GZ/TGZ 或单份 `SKILL.md` 导入技能,核对来源、文件和依赖后发布。已连接团队的成员从 DSH 左侧“企业扩展”入口进入统一目录,默认同时浏览技能和插件,并可按类型、状态筛选或搜索;ANT 插件设置页只处理连接、模型和更新。ANT Host 将固定版本的完整文件集放进 DSH 的用户技能目录,并重新核对本机文件;启用本地技能的 DSH 会话随后由原生技能提供者发现和使用。ANT 不用 Host 全局技能列表判断会话是否已发现技能。
|
|
10
10
|
|
|
11
11
|
安装不会自动运行技能脚本或安装 Python、Node 等系统依赖。ANT 只更新或卸载自己管理且本机内容未被修改的技能目录;发现同名个人技能或本机改动时会停止覆盖。
|
|
12
12
|
|
|
13
13
|
## 企业精选插件
|
|
14
14
|
|
|
15
|
-
管理员可从 npm 官方源的 DSH Bundle
|
|
15
|
+
管理员可从 npm 官方源的 DSH Bundle 精确版本创建草稿,填写源码、许可证、依赖和安装脚本的审核记录后发布。成员在“企业扩展”总览或“插件”分类查看审核信息和本机状态。对无新增依赖、无安装阶段脚本且归档符合首批准入条件的草稿,管理员可从 npm 官方源锁定实际 `.tgz` 并查看文件清单、下载核验制品;这类草稿经审核发布后,成员可让 DSH 从 ANT 的固定制品地址一键安装。其他版本仍是目录项,需联系管理员安排。安全撤回版本会停止新的 ANT 下载,但不会自动删除本机副本;ANT 设置页继续显示相应风险公告。范围与限制见[上线边界](../../docs/decisions/2026-10-15-plugin-center-rollout.md)。
|
|
16
16
|
|
|
17
17
|
ANT 自身更新使用单独的已审核安装包。管理员将与 npm 发布一致的 `.tgz` 上传到 ANT 服务端并登记版本;新版 ANT 页面可点击“更新”,DSH 原位替换同名 Bundle 后提示重启。旧版 `@ai-wendao/ant` 用户可打开团队服务的 `/ant-update` 指引,在 DSH“添加插件”中粘贴该安装包地址完成一次性原位更新,无需先卸载或使用终端。此入口只分发 ANT 自身,不启用扩展中心其他插件的一键安装。
|
|
18
18
|
|
package/client.js
CHANGED
|
@@ -3,7 +3,7 @@ window.__ModuleLoader__.load({
|
|
|
3
3
|
factory: (require) => {
|
|
4
4
|
var module = { exports: {} }
|
|
5
5
|
var exports = module.exports
|
|
6
|
-
const { createElement, useEffect, useState } = require('react')
|
|
6
|
+
const { createElement, useEffect, useRef, useState } = require('react')
|
|
7
7
|
const h = createElement
|
|
8
8
|
const SERVICE_URL_STORAGE_KEY = 'ant-team-service-url'
|
|
9
9
|
const DEVICE_ID_STORAGE_KEY = 'ant-team-device-id'
|
|
@@ -34,14 +34,15 @@ window.__ModuleLoader__.load({
|
|
|
34
34
|
border: '0.5px solid var(--dsw-alias-border-l2)', borderRadius: 8,
|
|
35
35
|
cursor: 'pointer',
|
|
36
36
|
}
|
|
37
|
+
const categoryButtonStyle = { ...secondaryButtonStyle, minHeight: 34, padding: '6px 12px', whiteSpace: 'nowrap' }
|
|
37
38
|
function ExtensionRequestNotice() {
|
|
38
39
|
return h('aside', { role: 'note', style: {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
display: 'flex', alignItems: 'baseline', gap: 8, padding: '9px 12px',
|
|
41
|
+
borderRadius: 8, backgroundColor: 'var(--dsw-alias-bg-layer-2)',
|
|
42
|
+
color: 'var(--dsw-alias-label-secondary)', fontSize: 13, lineHeight: 1.6,
|
|
42
43
|
} },
|
|
43
|
-
h('
|
|
44
|
-
h('
|
|
44
|
+
h('strong', { style: { flexShrink: 0, color: 'var(--dsw-alias-label-primary)' } }, '提示'),
|
|
45
|
+
h('span', null, '没有找到想用的扩展,或插件没有安装按钮?联系企业管理员,说明用途或提供地址。已开放的扩展可直接安装;未经审核的版本请勿自行安装。'))
|
|
45
46
|
}
|
|
46
47
|
const normalizeServiceUrl = (value) => {
|
|
47
48
|
let url
|
|
@@ -149,8 +150,38 @@ window.__ModuleLoader__.load({
|
|
|
149
150
|
function TeamServiceConfig({ remote, openInstalledPlugin }) {
|
|
150
151
|
const [serviceUrl, setServiceUrl] = useState(loadServiceUrl)
|
|
151
152
|
const [draft, setDraft] = useState(serviceUrl)
|
|
153
|
+
const [binding, setBinding] = useState({ state: 'checking', url: null })
|
|
154
|
+
const bindingRevision = useRef(0)
|
|
152
155
|
const [message, setMessage] = useState('')
|
|
153
156
|
const [pluginAlerts, setPluginAlerts] = useState([])
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
let disposed = false
|
|
159
|
+
const revision = bindingRevision.current
|
|
160
|
+
const current = () => !disposed && bindingRevision.current === revision
|
|
161
|
+
const inspectBinding = async () => {
|
|
162
|
+
try {
|
|
163
|
+
const response = await fetch('/api/ant-teams/connection-origin', { cache: 'no-store' })
|
|
164
|
+
const result = await readLocalJsonResponse(response)
|
|
165
|
+
if (!response.ok) throw new Error()
|
|
166
|
+
const pinned = result.service_url == null ? null : normalizeServiceUrl(result.service_url)
|
|
167
|
+
if (result.service_url != null && !pinned) throw new Error()
|
|
168
|
+
if (pinned) {
|
|
169
|
+
if (current()) {
|
|
170
|
+
setBinding({ state: 'ready', url: pinned })
|
|
171
|
+
setServiceUrl(pinned)
|
|
172
|
+
setDraft(pinned)
|
|
173
|
+
try { localStorage.setItem(SERVICE_URL_STORAGE_KEY, pinned) } catch {}
|
|
174
|
+
}
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
const described = await remote?.credentials?.describe?.(['ANT_MODEL_API_KEY'])
|
|
178
|
+
if (!described?.ok) throw new Error()
|
|
179
|
+
if (current()) setBinding({ state: described.value?.ANT_MODEL_API_KEY?.configured ? 'unverified' : 'ready', url: null })
|
|
180
|
+
} catch { if (current()) setBinding({ state: 'error', url: null }) }
|
|
181
|
+
}
|
|
182
|
+
void inspectBinding()
|
|
183
|
+
return () => { disposed = true }
|
|
184
|
+
}, [remote])
|
|
154
185
|
useEffect(() => {
|
|
155
186
|
let disposed = false
|
|
156
187
|
const inspect = async () => {
|
|
@@ -175,6 +206,10 @@ window.__ModuleLoader__.load({
|
|
|
175
206
|
}, [remote])
|
|
176
207
|
const save = (event) => {
|
|
177
208
|
event.preventDefault()
|
|
209
|
+
if (binding.state !== 'ready' || binding.url) {
|
|
210
|
+
setMessage(binding.url ? '当前已连接团队;切换前请先在下方断开当前设备。' : '尚未核对这台 DSH 的团队绑定,不能修改地址。')
|
|
211
|
+
return
|
|
212
|
+
}
|
|
178
213
|
const normalized = normalizeServiceUrl(draft)
|
|
179
214
|
if (!normalized) {
|
|
180
215
|
setMessage('请输入 HTTPS 团队服务地址;本机测试可使用 localhost。')
|
|
@@ -189,25 +224,39 @@ window.__ModuleLoader__.load({
|
|
|
189
224
|
setMessage('浏览器无法保存此地址,请检查本地存储权限。')
|
|
190
225
|
}
|
|
191
226
|
}
|
|
192
|
-
const link = consoleUrl(serviceUrl)
|
|
227
|
+
const link = binding.state === 'ready' ? consoleUrl(binding.url || serviceUrl) : null
|
|
228
|
+
const onConnectionChange = (url) => {
|
|
229
|
+
bindingRevision.current += 1
|
|
230
|
+
setBinding({ state: 'ready', url: url || null })
|
|
231
|
+
if (url) {
|
|
232
|
+
setServiceUrl(url)
|
|
233
|
+
setDraft(url)
|
|
234
|
+
try { localStorage.setItem(SERVICE_URL_STORAGE_KEY, url) } catch {}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
193
237
|
return h('section', { style: { display: 'grid', gap: 16, width: '100%', maxWidth: 1120 } },
|
|
194
238
|
h('p', { style: mutedTextStyle }, '连接团队服务后,即可同步获准模型并使用团队协作。'),
|
|
195
239
|
h('section', { style: panelStyle },
|
|
196
240
|
h('div', null,
|
|
197
241
|
h('h3', { style: { margin: 0, fontSize: 16, fontWeight: 650 } }, '团队服务'),
|
|
198
242
|
h('p', { style: { ...mutedTextStyle, marginTop: 4 } }, '由团队管理员提供服务地址;管理员可在控制台管理成员、模型和协作设置。')),
|
|
199
|
-
h('form', { onSubmit: save, style: { display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) auto', alignItems: 'end', gap: 10 } },
|
|
200
|
-
h('label', { htmlFor: 'ant-team-service-url', style: { gridColumn: '1 / -1', fontSize: 13, fontWeight: 550 } }, '服务地址'),
|
|
243
|
+
h('form', { onSubmit: save, style: { display: 'grid', gridTemplateColumns: binding.url ? '1fr' : 'minmax(0, 1fr) auto', alignItems: 'end', gap: 10 } },
|
|
244
|
+
h('label', { htmlFor: 'ant-team-service-url', style: { gridColumn: '1 / -1', fontSize: 13, fontWeight: 550 } }, binding.url ? '当前连接的团队服务地址' : '服务地址'),
|
|
201
245
|
h('input', {
|
|
202
246
|
id: 'ant-team-service-url',
|
|
203
247
|
type: 'url',
|
|
204
248
|
required: true,
|
|
205
|
-
value: draft,
|
|
249
|
+
value: binding.url || draft,
|
|
250
|
+
readOnly: binding.state !== 'ready' || Boolean(binding.url),
|
|
206
251
|
placeholder: 'https://ant.example.com',
|
|
207
|
-
onChange: (event) => setDraft(event.target.value),
|
|
252
|
+
onChange: (event) => { if (binding.state === 'ready' && !binding.url) setDraft(event.target.value) },
|
|
208
253
|
style: inputStyle,
|
|
209
254
|
}),
|
|
210
|
-
h('button', { type: 'submit', style: primaryButtonStyle }, '保存地址')),
|
|
255
|
+
binding.url ? null : h('button', { type: 'submit', disabled: binding.state !== 'ready', style: primaryButtonStyle }, '保存地址')),
|
|
256
|
+
binding.url ? h('p', { style: mutedTextStyle }, '若要切换团队,请先在下方断开当前设备,再填写新地址并验证邮箱。') : null,
|
|
257
|
+
binding.state === 'checking' ? h('p', { role: 'status', style: mutedTextStyle }, '正在核对这台 DSH 绑定的团队…') : null,
|
|
258
|
+
binding.state === 'unverified' ? h('p', { role: 'status', style: mutedTextStyle }, '本机仍有 ANT 凭证,但无法核对所属团队。请联系原团队管理员核查并撤销旧设备;确认后可在下方断开或仅清理本机。') : null,
|
|
259
|
+
binding.state === 'error' ? h('p', { role: 'status', style: mutedTextStyle }, '暂时无法核对这台 DSH 的团队绑定,请重新打开 ANT 或联系管理员。') : null,
|
|
211
260
|
h('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 12 } },
|
|
212
261
|
message ? h('p', { role: 'status', style: mutedTextStyle }, message) : null,
|
|
213
262
|
link ? h('a', { href: link, target: '_blank', rel: 'noopener noreferrer', style: { color: 'var(--dsw-alias-link)', fontSize: 13 } }, '打开团队管理 ↗') : null)),
|
|
@@ -217,17 +266,22 @@ window.__ModuleLoader__.load({
|
|
|
217
266
|
`${plugin.title} ${plugin.version}${plugin.versionUnknown ? '(本机版本暂无法读取,请核对是否受影响)' : ''}:${plugin.withdrawal_reason}`)),
|
|
218
267
|
...pluginAlerts.map((plugin) => h('button', { key: `${plugin.id}-open`, type: 'button',
|
|
219
268
|
onClick: () => openInstalledPlugin(plugin.package_name), style: secondaryButtonStyle }, `在 DSH 中查看 ${plugin.title}`))) : null,
|
|
220
|
-
h(ModelSyncFooter, { remote, serviceUrl,
|
|
269
|
+
h(ModelSyncFooter, { remote, serviceUrl, bindingReady: binding.state === 'ready',
|
|
270
|
+
onServiceUrlChange: setServiceUrl, onConnectionChange }))
|
|
221
271
|
}
|
|
222
272
|
|
|
223
|
-
function SkillCenterPage({ onBack, onPlugins
|
|
273
|
+
function SkillCenterPage({ remote, onBack, onAll, onSkills, onPlugins, openBundle,
|
|
274
|
+
includePlugins, query, onQueryChange }) {
|
|
224
275
|
const [skills, setSkills] = useState([])
|
|
225
276
|
const [loading, setLoading] = useState(false)
|
|
277
|
+
const [loadError, setLoadError] = useState(false)
|
|
226
278
|
const [workingId, setWorkingId] = useState('')
|
|
227
279
|
const [message, setMessage] = useState('')
|
|
228
|
-
const [query, setQuery] = useState('')
|
|
229
280
|
const [filter, setFilter] = useState('all')
|
|
281
|
+
const [refreshToken, setRefreshToken] = useState(0)
|
|
282
|
+
const refreshGeneration = useRef(0)
|
|
230
283
|
const refresh = async () => {
|
|
284
|
+
const generation = ++refreshGeneration.current
|
|
231
285
|
setLoading(true)
|
|
232
286
|
try {
|
|
233
287
|
const response = await fetch('/api/ant-teams/skills', { cache: 'no-store' })
|
|
@@ -236,13 +290,17 @@ window.__ModuleLoader__.load({
|
|
|
236
290
|
? '请返回 ANT 插件设置,先验证工作邮箱并连接团队。'
|
|
237
291
|
: result.message || `读取技能失败(${response.status})。`)
|
|
238
292
|
if (!Array.isArray(result.skills)) throw new Error('企业技能目录格式无效。')
|
|
293
|
+
if (generation !== refreshGeneration.current) return
|
|
239
294
|
setSkills(result.skills)
|
|
295
|
+
setLoadError(false)
|
|
240
296
|
setMessage(result.skills.length ? '' : '团队暂未发布精选技能。')
|
|
241
297
|
} catch (error) {
|
|
298
|
+
if (generation !== refreshGeneration.current) return
|
|
242
299
|
setSkills([])
|
|
300
|
+
setLoadError(true)
|
|
243
301
|
setMessage(error instanceof Error ? error.message : '无法读取企业精选技能。')
|
|
244
302
|
}
|
|
245
|
-
finally { setLoading(false) }
|
|
303
|
+
finally { if (generation === refreshGeneration.current) setLoading(false) }
|
|
246
304
|
}
|
|
247
305
|
useEffect(() => { void refresh() }, [])
|
|
248
306
|
const changeInstall = async (skill, remove = false) => {
|
|
@@ -267,38 +325,58 @@ window.__ModuleLoader__.load({
|
|
|
267
325
|
finally { setWorkingId('') }
|
|
268
326
|
}
|
|
269
327
|
const visible = skills.filter((skill) => {
|
|
270
|
-
const matchesFilter = filter === 'all'
|
|
328
|
+
const matchesFilter = filter === 'all'
|
|
329
|
+
|| filter === 'available' && ['not-installed', 'update-available'].includes(skill.local_status)
|
|
330
|
+
|| filter === 'installed' && ['installed', 'update-available'].includes(skill.local_status)
|
|
331
|
+
|| filter === 'updates' && skill.local_status === 'update-available'
|
|
271
332
|
const matchesQuery = !query.trim() || `${skill.title} ${skill.description} ${skill.dsh_name}`.toLowerCase().includes(query.trim().toLowerCase())
|
|
272
333
|
return matchesFilter && matchesQuery
|
|
273
334
|
})
|
|
274
335
|
const statusLabel = { installed: '已放入本机目录', 'update-available': '可更新', 'not-installed': '未安装', conflict: '本机冲突' }
|
|
275
|
-
return h('section', { 'aria-label': '企业精选技能', style: {
|
|
336
|
+
return h('section', { 'aria-label': includePlugins ? '企业扩展' : '企业精选技能', style: {
|
|
276
337
|
height: '100%', overflowY: 'auto', boxSizing: 'border-box',
|
|
277
338
|
padding: 'calc(var(--dsh-frame-top-clearance, 0px) + 20px) clamp(16px, 3vw, 36px) 48px',
|
|
278
339
|
} }, h('div', { style: { maxWidth: 1440, margin: '0 auto', display: 'grid', gap: 22 } },
|
|
279
|
-
h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 8 } },
|
|
280
|
-
h('
|
|
281
|
-
|
|
340
|
+
h('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } },
|
|
341
|
+
h('nav', { 'aria-label': '企业扩展分类', style: { display: 'flex', gap: 6 } },
|
|
342
|
+
includePlugins
|
|
343
|
+
? h('span', { 'aria-current': 'page', style: { ...categoryButtonStyle, display: 'inline-flex', alignItems: 'center', cursor: 'default', backgroundColor: 'var(--dsw-alias-bg-layer-2)' } }, '总览')
|
|
344
|
+
: h('button', { type: 'button', onClick: onAll, style: categoryButtonStyle }, '总览'),
|
|
345
|
+
includePlugins
|
|
346
|
+
? h('button', { type: 'button', onClick: onSkills, style: categoryButtonStyle }, '技能')
|
|
347
|
+
: h('span', { 'aria-current': 'page', style: { ...categoryButtonStyle, display: 'inline-flex', alignItems: 'center', cursor: 'default', backgroundColor: 'var(--dsw-alias-bg-layer-2)' } }, '技能'),
|
|
348
|
+
h('button', { type: 'button', onClick: onPlugins, style: categoryButtonStyle }, '插件')),
|
|
349
|
+
h('button', { type: 'button', onClick: onBack, style: { ...categoryButtonStyle, marginLeft: 'auto' } }, 'ANT 设置')),
|
|
282
350
|
h('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'end', justifyContent: 'space-between', gap: 16 } },
|
|
283
351
|
h('div', null,
|
|
284
|
-
h('h1', { style: { margin: 0, fontSize: 28 } }, '企业精选技能'),
|
|
285
|
-
h('p', { style: { ...mutedTextStyle, marginTop: 6 } },
|
|
286
|
-
|
|
352
|
+
h('h1', { style: { margin: 0, fontSize: 28 } }, includePlugins ? '企业扩展' : '企业精选技能'),
|
|
353
|
+
h('p', { style: { ...mutedTextStyle, marginTop: 6 } }, includePlugins
|
|
354
|
+
? '浏览团队审核的技能与插件;选择需要的扩展安装到这台 DSH。'
|
|
355
|
+
: '团队管理员审核后发布;安装到这台 DSH 后,由 DSH 原生技能系统发现和使用。')),
|
|
356
|
+
h('button', { type: 'button', disabled: loading || Boolean(workingId), onClick: () => {
|
|
357
|
+
void refresh()
|
|
358
|
+
if (includePlugins) setRefreshToken((value) => value + 1)
|
|
359
|
+
}, style: secondaryButtonStyle }, loading ? '读取中…' : '刷新目录')),
|
|
287
360
|
h(ExtensionRequestNotice),
|
|
288
361
|
h('div', { style: { ...panelStyle, gap: 12 } },
|
|
289
|
-
h('input', { type: 'search', value: query, onChange: (event) =>
|
|
362
|
+
h('input', { type: 'search', value: query, onChange: (event) => onQueryChange(event.target.value),
|
|
363
|
+
placeholder: includePlugins ? '搜索技能或插件名称、用途' : '搜索技能名称或说明',
|
|
364
|
+
'aria-label': includePlugins ? '搜索企业扩展' : '搜索企业精选技能', style: inputStyle }),
|
|
290
365
|
h('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } },
|
|
291
|
-
...[['all', '全部'], ['
|
|
366
|
+
...[['all', '全部'], ['available', '可安装或更新'], ['installed', '已安装'], ['updates', '可更新']].map(([value, label]) =>
|
|
292
367
|
h('button', { key: value, type: 'button', onClick: () => setFilter(value), 'aria-pressed': filter === value,
|
|
293
368
|
style: { ...secondaryButtonStyle, minHeight: 34, padding: '6px 12px', backgroundColor: filter === value ? 'var(--dsw-alias-bg-layer-2)' : secondaryButtonStyle.backgroundColor } }, label)),
|
|
294
|
-
h('span', { style: { ...mutedTextStyle, marginLeft: 'auto' } }, `显示 ${visible.length} / ${skills.length} 个技能`))),
|
|
295
|
-
|
|
369
|
+
includePlugins ? null : h('span', { style: { ...mutedTextStyle, marginLeft: 'auto' } }, `显示 ${visible.length} / ${skills.length} 个技能`))),
|
|
370
|
+
includePlugins ? h('h2', { style: { margin: 0, fontSize: 20 } }, loading ? '技能 · 读取中…' : `技能 · ${visible.length}`) : null,
|
|
371
|
+
message && (!includePlugins || loadError || skills.length) ? h('p', { role: loadError ? 'alert' : 'status', style: mutedTextStyle }, message) : null,
|
|
296
372
|
visible.length ? h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 420px), 1fr))', gap: 16 } },
|
|
297
373
|
...visible.map((skill) => {
|
|
298
374
|
const description = skill.description || ''
|
|
299
375
|
return h('article', { key: skill.id, style: { ...panelStyle, gap: 13 } },
|
|
300
376
|
h('div', { style: { display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'baseline', gap: 8 } },
|
|
301
|
-
h('
|
|
377
|
+
h('div', { style: { display: 'flex', alignItems: 'baseline', flexWrap: 'wrap', gap: 8 } },
|
|
378
|
+
h('h2', { style: { margin: 0, fontSize: 19 } }, skill.title),
|
|
379
|
+
includePlugins ? h('span', { style: mutedTextStyle }, '技能') : null),
|
|
302
380
|
h('span', { style: mutedTextStyle }, statusLabel[skill.local_status] || '状态未知')),
|
|
303
381
|
h('p', { style: mutedTextStyle }, description.length > 180 ? `${description.slice(0, 180)}…` : description),
|
|
304
382
|
description.length > 180 ? h('details', null,
|
|
@@ -309,25 +387,33 @@ window.__ModuleLoader__.load({
|
|
|
309
387
|
skill.local_status === 'conflict'
|
|
310
388
|
? h('p', { role: 'status', style: mutedTextStyle }, '本机同名技能有未受 ANT 管理的内容、文件已被修改或备份损坏;请联系管理员处理。')
|
|
311
389
|
: h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 'auto' } },
|
|
312
|
-
h('button', { type: 'button', disabled: Boolean(workingId)
|
|
390
|
+
skill.local_status === 'installed' ? null : h('button', { type: 'button', disabled: Boolean(workingId),
|
|
313
391
|
onClick: () => void changeInstall(skill), style: primaryButtonStyle },
|
|
314
|
-
skill.local_status === '
|
|
392
|
+
skill.local_status === 'update-available' ? '更新技能' : '安装到这台 DSH'),
|
|
315
393
|
skill.local_status !== 'not-installed' ? h('button', { type: 'button', disabled: Boolean(workingId),
|
|
316
394
|
onClick: () => void changeInstall(skill, true), style: secondaryButtonStyle }, '卸载') : null))
|
|
317
|
-
})) : !loading && skills.length ? h('p', { style: mutedTextStyle }, '没有符合筛选条件的技能。') : null
|
|
395
|
+
})) : !includePlugins && !loading && skills.length ? h('p', { style: mutedTextStyle }, '没有符合筛选条件的技能。') : null,
|
|
396
|
+
includePlugins ? h(PluginCenterPage, { remote, onBack, onAll, onSkills, openBundle,
|
|
397
|
+
embedded: true, query, onQueryChange, externalFilter: filter,
|
|
398
|
+
hasSkillMatches: visible.length > 0, skillCatalogReady: !loading && !loadError,
|
|
399
|
+
refreshToken }) : null))
|
|
318
400
|
}
|
|
319
401
|
|
|
320
|
-
function PluginCenterPage({ remote, onBack, onSkills, openBundle
|
|
402
|
+
function PluginCenterPage({ remote, onBack, onAll, onSkills, openBundle,
|
|
403
|
+
embedded = false, query, onQueryChange, externalFilter = 'all',
|
|
404
|
+
hasSkillMatches = false, skillCatalogReady = false, refreshToken }) {
|
|
321
405
|
const [plugins, setPlugins] = useState([])
|
|
322
406
|
const [bundles, setBundles] = useState([])
|
|
323
|
-
const [loading, setLoading] = useState(
|
|
407
|
+
const [loading, setLoading] = useState(true)
|
|
408
|
+
const [loadError, setLoadError] = useState(false)
|
|
324
409
|
const [workingId, setWorkingId] = useState('')
|
|
325
410
|
const [uncertainId, setUncertainId] = useState('')
|
|
326
411
|
const [completedId, setCompletedId] = useState('')
|
|
327
412
|
const [message, setMessage] = useState('')
|
|
328
|
-
const [query, setQuery] = useState('')
|
|
329
413
|
const [filter, setFilter] = useState('all')
|
|
414
|
+
const refreshGeneration = useRef(0)
|
|
330
415
|
const refresh = async () => {
|
|
416
|
+
const generation = ++refreshGeneration.current
|
|
331
417
|
setLoading(true)
|
|
332
418
|
try {
|
|
333
419
|
const manager = remote?.pluginManager
|
|
@@ -339,16 +425,20 @@ window.__ModuleLoader__.load({
|
|
|
339
425
|
if (!response.ok) throw new Error(result.message || `读取插件目录失败(${response.status})。`)
|
|
340
426
|
if (!Array.isArray(result.plugins)) throw new Error('企业插件目录格式无效。')
|
|
341
427
|
if (!listed.ok) throw new Error(listed.error?.message || '无法读取 DSH 本机插件。')
|
|
428
|
+
if (generation !== refreshGeneration.current) return
|
|
342
429
|
setPlugins(result.plugins)
|
|
343
430
|
setBundles(listed.value)
|
|
431
|
+
setLoadError(false)
|
|
344
432
|
setMessage(result.plugins.length ? '' : '团队暂未发布精选插件。')
|
|
345
433
|
} catch (error) {
|
|
434
|
+
if (generation !== refreshGeneration.current) return
|
|
346
435
|
setPlugins([])
|
|
347
436
|
setBundles([])
|
|
437
|
+
setLoadError(true)
|
|
348
438
|
setMessage(error instanceof Error ? error.message : '无法读取企业精选插件。')
|
|
349
|
-
} finally { setLoading(false) }
|
|
439
|
+
} finally { if (generation === refreshGeneration.current) setLoading(false) }
|
|
350
440
|
}
|
|
351
|
-
useEffect(() => { void refresh() }, [])
|
|
441
|
+
useEffect(() => { void refresh() }, [refreshToken])
|
|
352
442
|
const install = async (plugin) => {
|
|
353
443
|
const artifactUrl = approvedPluginArtifactUrl(plugin)
|
|
354
444
|
if (!artifactUrl || workingId || uncertainId === plugin.id || completedId === plugin.id) return
|
|
@@ -433,37 +523,51 @@ window.__ModuleLoader__.load({
|
|
|
433
523
|
setMessage(error instanceof Error ? error.message : '插件安装未完成,请稍后重试。')
|
|
434
524
|
} finally { setWorkingId('') }
|
|
435
525
|
}
|
|
526
|
+
const activeFilter = embedded ? externalFilter : filter
|
|
436
527
|
const visible = plugins.filter((plugin) => {
|
|
437
528
|
const installed = bundles.find((bundle) => bundle.name === plugin.package_name && bundle.installed)
|
|
438
529
|
const artifactUrl = approvedPluginArtifactUrl(plugin)
|
|
439
530
|
const matches = !query.trim() || `${plugin.title} ${plugin.description} ${plugin.package_name}`.toLowerCase().includes(query.trim().toLowerCase())
|
|
440
|
-
return matches && (
|
|
441
|
-
||
|
|
442
|
-
||
|
|
531
|
+
return matches && (activeFilter === 'all' || activeFilter === 'installed' && Boolean(installed)
|
|
532
|
+
|| activeFilter === 'available' && artifactUrl && (!installed || (installed.version && installed.version !== plugin.version))
|
|
533
|
+
|| activeFilter === 'updates' && artifactUrl && installed && installed.version && installed.version !== plugin.version
|
|
534
|
+
|| activeFilter === 'retired' && plugin.status !== 'published')
|
|
443
535
|
})
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
536
|
+
const noMatches = embedded
|
|
537
|
+
? skillCatalogReady ? '没有找到符合条件的企业扩展。' : '插件中没有符合条件的结果。'
|
|
538
|
+
: activeFilter === 'available'
|
|
539
|
+
? '当前没有可一键安装的插件。可在“全部”查看审核目录,或联系企业管理员。'
|
|
540
|
+
: '没有符合筛选条件的插件。'
|
|
541
|
+
return h('section', { 'aria-label': '企业精选插件', style: embedded
|
|
542
|
+
? { display: 'grid', gap: 16 }
|
|
543
|
+
: { height: '100%', overflowY: 'auto', boxSizing: 'border-box',
|
|
544
|
+
padding: 'calc(var(--dsh-frame-top-clearance, 0px) + 20px) clamp(16px, 3vw, 36px) 48px' },
|
|
545
|
+
}, h('div', { style: embedded ? { display: 'grid', gap: 16 } : { maxWidth: 1440, margin: '0 auto', display: 'grid', gap: 22 } },
|
|
546
|
+
embedded ? h('h2', { style: { margin: 0, fontSize: 20 } }, loading ? '插件 · 读取中…' : `插件 · ${visible.length}`) : h('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } },
|
|
547
|
+
h('nav', { 'aria-label': '企业扩展分类', style: { display: 'flex', gap: 6 } },
|
|
548
|
+
h('button', { type: 'button', onClick: onAll, style: categoryButtonStyle }, '总览'),
|
|
549
|
+
h('button', { type: 'button', onClick: onSkills, style: categoryButtonStyle }, '技能'),
|
|
550
|
+
h('span', { 'aria-current': 'page', style: { ...categoryButtonStyle, display: 'inline-flex', alignItems: 'center', cursor: 'default', backgroundColor: 'var(--dsw-alias-bg-layer-2)' } }, '插件')),
|
|
551
|
+
h('button', { type: 'button', onClick: onBack, style: { ...categoryButtonStyle, marginLeft: 'auto' } }, 'ANT 设置')),
|
|
552
|
+
embedded ? null : h('div', { style: { display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between', gap: 16 } },
|
|
452
553
|
h('div', null,
|
|
453
554
|
h('h1', { style: { margin: 0, fontSize: 28 } }, '企业精选插件'),
|
|
454
555
|
h('p', { style: { ...mutedTextStyle, marginTop: 6 } }, '这里展示团队审核过的插件版本及本机状态。')),
|
|
455
556
|
h('button', { type: 'button', disabled: loading, onClick: () => void refresh(), style: secondaryButtonStyle }, loading ? '读取中…' : '刷新目录')),
|
|
456
|
-
|
|
457
|
-
h('
|
|
458
|
-
h('
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
557
|
+
!loading && !plugins.length && !loadError ? embedded
|
|
558
|
+
? h('p', { role: 'status', style: mutedTextStyle }, '团队暂未发布精选插件。')
|
|
559
|
+
: h('section', { role: 'status', style: panelStyle },
|
|
560
|
+
h('h2', { style: { margin: 0, fontSize: 18 } }, '团队暂未发布精选插件'),
|
|
561
|
+
h('p', { style: mutedTextStyle }, '需要某个插件?请联系企业管理员,说明用途或提供插件地址。审核发布后会显示在这里。')) : null,
|
|
562
|
+
!loading && loadError ? h('p', { role: 'alert', style: mutedTextStyle }, message) : null,
|
|
563
|
+
plugins.length && !embedded ? h(ExtensionRequestNotice) : null,
|
|
564
|
+
plugins.length && !embedded ? h('div', { style: panelStyle },
|
|
565
|
+
h('input', { type: 'search', value: query, onChange: (event) => onQueryChange(event.target.value), placeholder: '搜索插件名称或用途', 'aria-label': '搜索企业精选插件', style: inputStyle }),
|
|
462
566
|
h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 8 } },
|
|
463
567
|
...[['all', '全部'], ['available', '可安装或升级'], ['installed', '已安装'], ['retired', '已废弃']].map(([value, label]) =>
|
|
464
568
|
h('button', { key: value, type: 'button', onClick: () => setFilter(value), 'aria-pressed': filter === value,
|
|
465
|
-
style: { ...secondaryButtonStyle, minHeight: 34, backgroundColor: filter === value ? 'var(--dsw-alias-bg-layer-2)' : secondaryButtonStyle.backgroundColor } }, label)))),
|
|
466
|
-
message ? h('p', { role: 'status', style: mutedTextStyle }, message) : null,
|
|
569
|
+
style: { ...secondaryButtonStyle, minHeight: 34, backgroundColor: filter === value ? 'var(--dsw-alias-bg-layer-2)' : secondaryButtonStyle.backgroundColor } }, label)))) : null,
|
|
570
|
+
plugins.length && message ? h('p', { role: 'status', style: mutedTextStyle }, message) : null,
|
|
467
571
|
visible.length ? h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 420px), 1fr))', gap: 16 } },
|
|
468
572
|
...visible.map((plugin) => {
|
|
469
573
|
const local = bundles.find((bundle) => bundle.name === plugin.package_name && bundle.installed)
|
|
@@ -483,7 +587,9 @@ window.__ModuleLoader__.load({
|
|
|
483
587
|
return h('article', { key: plugin.id, style: security
|
|
484
588
|
? { ...panelStyle, borderColor: 'var(--dsw-alias-label-danger, #d44)' } : panelStyle },
|
|
485
589
|
h('div', { style: { display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between', gap: 8 } },
|
|
486
|
-
h('
|
|
590
|
+
h('div', { style: { display: 'flex', alignItems: 'baseline', flexWrap: 'wrap', gap: 8 } },
|
|
591
|
+
h('h2', { style: { margin: 0, fontSize: 19 } }, plugin.title),
|
|
592
|
+
embedded ? h('span', { style: mutedTextStyle }, '插件') : null),
|
|
487
593
|
h('span', { style: security
|
|
488
594
|
? { ...mutedTextStyle, color: 'var(--dsw-alias-label-danger, #d44)', fontWeight: 650 } : mutedTextStyle }, status)),
|
|
489
595
|
h('p', { style: mutedTextStyle }, plugin.description),
|
|
@@ -510,12 +616,11 @@ window.__ModuleLoader__.load({
|
|
|
510
616
|
local || uncertainId === plugin.id ? h('button', { type: 'button', onClick: () => openBundle(plugin.package_name), style: secondaryButtonStyle }, '打开 DSH 插件管理') : null,
|
|
511
617
|
local && !sameVersion && plugin.status === 'published' && !artifactUrl
|
|
512
618
|
? h('p', { style: mutedTextStyle }, '本机版本与团队审核版本不同;请联系企业管理员核对。') : null))
|
|
513
|
-
})) : !loading && plugins.length
|
|
514
|
-
|
|
515
|
-
: '没有符合筛选条件的插件。') : null))
|
|
619
|
+
})) : !loading && plugins.length && (!embedded || !hasSkillMatches)
|
|
620
|
+
? h('p', { style: mutedTextStyle }, noMatches) : null))
|
|
516
621
|
}
|
|
517
622
|
|
|
518
|
-
function ModelSyncFooter({ remote, serviceUrl, onServiceUrlChange }) {
|
|
623
|
+
function ModelSyncFooter({ remote, serviceUrl, bindingReady, onServiceUrlChange, onConnectionChange }) {
|
|
519
624
|
const [email, setEmail] = useState('')
|
|
520
625
|
const [code, setCode] = useState('')
|
|
521
626
|
const [message, setMessage] = useState('')
|
|
@@ -540,13 +645,13 @@ window.__ModuleLoader__.load({
|
|
|
540
645
|
: `${message} 本机无法确认旧设备编号;请让原团队管理员按成员查看设备清单,勿用新编号猜测。`
|
|
541
646
|
}
|
|
542
647
|
|
|
543
|
-
const inspectServiceCompatibility = async (rawServiceUrl, useSavedConnection = false) => {
|
|
648
|
+
const inspectServiceCompatibility = async (rawServiceUrl, useSavedConnection = false, isCurrent = () => true) => {
|
|
544
649
|
const normalized = normalizeServiceUrl(rawServiceUrl)
|
|
545
650
|
if (!normalized && !useSavedConnection) {
|
|
546
|
-
setServiceCompatibility({ state: 'idle', message: '' })
|
|
651
|
+
if (isCurrent()) setServiceCompatibility({ state: 'idle', message: '' })
|
|
547
652
|
return null
|
|
548
653
|
}
|
|
549
|
-
setServiceCompatibility({ state: 'checking', message: '正在识别 ANT 客户端与服务端版本…' })
|
|
654
|
+
if (isCurrent()) setServiceCompatibility({ state: 'checking', message: '正在识别 ANT 客户端与服务端版本…' })
|
|
550
655
|
try {
|
|
551
656
|
const manager = remote?.pluginManager
|
|
552
657
|
if (typeof manager?.listBundles !== 'function') throw new Error('当前 DSH 未提供 Bundle 版本读取接口。')
|
|
@@ -577,14 +682,14 @@ window.__ModuleLoader__.load({
|
|
|
577
682
|
: `已识别 ANT Bundle v${result.bundle_version} 和服务端 v${result.server_version};服务端未提供协议能力,兼容性未知,仍允许继续。`
|
|
578
683
|
const state = compatibility === true ? 'compatible' : compatibility === false ? 'incompatible' : 'unknown'
|
|
579
684
|
const status = { state, message, ...result }
|
|
580
|
-
setServiceCompatibility(status)
|
|
685
|
+
if (isCurrent()) setServiceCompatibility(status)
|
|
581
686
|
return status
|
|
582
687
|
} catch (error) {
|
|
583
688
|
const status = {
|
|
584
689
|
state: 'unknown',
|
|
585
690
|
message: `无法确认 ANT 版本兼容性,仍允许继续:${error instanceof Error ? error.message : '连接检查失败。'}`,
|
|
586
691
|
}
|
|
587
|
-
setServiceCompatibility(status)
|
|
692
|
+
if (isCurrent()) setServiceCompatibility(status)
|
|
588
693
|
return status
|
|
589
694
|
}
|
|
590
695
|
}
|
|
@@ -629,6 +734,10 @@ window.__ModuleLoader__.load({
|
|
|
629
734
|
}
|
|
630
735
|
|
|
631
736
|
const refreshModels = async () => {
|
|
737
|
+
if (!bindingReady) {
|
|
738
|
+
setMessage('请先等待团队绑定检查完成,再刷新团队模型。')
|
|
739
|
+
return
|
|
740
|
+
}
|
|
632
741
|
setBusy(true)
|
|
633
742
|
setMessage('正在从 ANT 刷新获准模型…')
|
|
634
743
|
try {
|
|
@@ -806,6 +915,7 @@ window.__ModuleLoader__.load({
|
|
|
806
915
|
const credentialResult = await remote.credentials.unset('ANT_MODEL_API_KEY')
|
|
807
916
|
if (!credentialResult.ok) throw new Error(credentialResult.error?.message || 'ANT 成员凭证未能清除。')
|
|
808
917
|
setCredentialConfigured(false)
|
|
918
|
+
onConnectionChange?.(null)
|
|
809
919
|
setNeedsReconnect(false)
|
|
810
920
|
setReauthenticate(false)
|
|
811
921
|
setAllowLocalCleanup(false)
|
|
@@ -857,13 +967,13 @@ window.__ModuleLoader__.load({
|
|
|
857
967
|
if (!current) return
|
|
858
968
|
const configured = result.ok ? Boolean(result.value?.ANT_MODEL_API_KEY?.configured) : null
|
|
859
969
|
setCredentialConfigured(configured)
|
|
860
|
-
if (configured) {
|
|
970
|
+
if (configured && bindingReady) {
|
|
861
971
|
void refreshModels()
|
|
862
972
|
void checkBundleUpdate()
|
|
863
973
|
}
|
|
864
974
|
}).catch(() => { if (current) setCredentialConfigured(null) })
|
|
865
975
|
return () => { current = false }
|
|
866
|
-
}, [remote])
|
|
976
|
+
}, [remote, bindingReady])
|
|
867
977
|
|
|
868
978
|
useEffect(() => {
|
|
869
979
|
let current = true
|
|
@@ -883,11 +993,21 @@ window.__ModuleLoader__.load({
|
|
|
883
993
|
}, [remote, credentialConfigured])
|
|
884
994
|
|
|
885
995
|
useEffect(() => {
|
|
886
|
-
|
|
887
|
-
|
|
996
|
+
if (!bindingReady) {
|
|
997
|
+
setServiceCompatibility({ state: 'idle', message: '' })
|
|
998
|
+
return
|
|
999
|
+
}
|
|
1000
|
+
let current = true
|
|
1001
|
+
void inspectServiceCompatibility(serviceUrl, credentialConfigured === true, () => current)
|
|
1002
|
+
return () => { current = false }
|
|
1003
|
+
}, [remote, serviceUrl, credentialConfigured, bindingReady])
|
|
888
1004
|
|
|
889
1005
|
const requestCode = async (event) => {
|
|
890
1006
|
event.preventDefault()
|
|
1007
|
+
if (!bindingReady) {
|
|
1008
|
+
setMessage('请先等待团队绑定检查完成,再验证工作邮箱。')
|
|
1009
|
+
return
|
|
1010
|
+
}
|
|
891
1011
|
const normalized = normalizeServiceUrl(serviceUrl)
|
|
892
1012
|
const normalizedEmail = email.trim()
|
|
893
1013
|
if (!normalized) {
|
|
@@ -929,6 +1049,10 @@ window.__ModuleLoader__.load({
|
|
|
929
1049
|
|
|
930
1050
|
const connect = async (event) => {
|
|
931
1051
|
event.preventDefault()
|
|
1052
|
+
if (!bindingReady) {
|
|
1053
|
+
setMessage('请先等待团队绑定检查完成,再连接团队。')
|
|
1054
|
+
return
|
|
1055
|
+
}
|
|
932
1056
|
const normalized = normalizeServiceUrl(serviceUrl)
|
|
933
1057
|
const normalizedEmail = email.trim()
|
|
934
1058
|
const normalizedCode = code.trim()
|
|
@@ -945,6 +1069,7 @@ window.__ModuleLoader__.load({
|
|
|
945
1069
|
}
|
|
946
1070
|
setBusy(true)
|
|
947
1071
|
setMessage('正在验证邮箱并同步团队模型…')
|
|
1072
|
+
let credentialSaved = false
|
|
948
1073
|
try {
|
|
949
1074
|
await requireCompatibleService(normalized)
|
|
950
1075
|
localStorage.setItem(SERVICE_URL_STORAGE_KEY, normalized)
|
|
@@ -968,24 +1093,34 @@ window.__ModuleLoader__.load({
|
|
|
968
1093
|
}
|
|
969
1094
|
const credentialWrite = await remote.credentials.set('ANT_MODEL_API_KEY', result.token)
|
|
970
1095
|
if (!credentialWrite.ok) throw new Error(credentialWrite.error?.message || 'DSH 无法安全保存 ANT 凭证。')
|
|
1096
|
+
credentialSaved = true
|
|
971
1097
|
await applyAuthorizedModels(result.models, normalized)
|
|
972
1098
|
setCode('')
|
|
973
1099
|
onServiceUrlChange(normalized)
|
|
974
1100
|
setCodeRequested(false)
|
|
975
1101
|
setCredentialConfigured(true)
|
|
1102
|
+
onConnectionChange?.(normalized)
|
|
976
1103
|
setAllowLocalCleanup(false)
|
|
977
1104
|
setNeedsReconnect(false)
|
|
978
1105
|
setReauthenticate(false)
|
|
979
1106
|
setMessage(`已连接 ${result.email || normalizedEmail},同步 ${result.models.length} 个获准模型。它们现已出现在 DSH 模型选择器中。`)
|
|
980
1107
|
} catch (error) {
|
|
981
|
-
|
|
1108
|
+
if (credentialSaved) {
|
|
1109
|
+
setCredentialConfigured(true)
|
|
1110
|
+
setCode('')
|
|
1111
|
+
setCodeRequested(false)
|
|
1112
|
+
onConnectionChange?.(normalized)
|
|
1113
|
+
setMessage(`ANT 凭证已保存,但团队模型未能同步:${error instanceof Error ? error.message : '请稍后重试。'} 请点击“刷新团队模型”重试。`)
|
|
1114
|
+
} else {
|
|
1115
|
+
setMessage(error instanceof Error ? error.message : '连接失败,请稍后重试。')
|
|
1116
|
+
}
|
|
982
1117
|
} finally {
|
|
983
1118
|
setBusy(false)
|
|
984
1119
|
}
|
|
985
1120
|
}
|
|
986
1121
|
|
|
987
1122
|
const showConnectForm = credentialConfigured !== true || reauthenticate || needsReconnect
|
|
988
|
-
const canCheckUpdate = credentialConfigured === true && !needsReconnect
|
|
1123
|
+
const canCheckUpdate = bindingReady && credentialConfigured === true && !needsReconnect
|
|
989
1124
|
const updateGuideUrl = pinnedServiceUrl
|
|
990
1125
|
? new URL('/ant-update', pinnedServiceUrl).href : null
|
|
991
1126
|
return h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 400px), 1fr))', alignItems: 'start', gap: 16 } },
|
|
@@ -996,11 +1131,13 @@ window.__ModuleLoader__.load({
|
|
|
996
1131
|
h('p', { style: { ...mutedTextStyle, marginTop: 4 } }, '使用上方团队服务地址验证工作邮箱并同步授权模型。连接状态变化时可随时刷新。')),
|
|
997
1132
|
h('span', { style: {
|
|
998
1133
|
flexShrink: 0, padding: '4px 9px', borderRadius: 999,
|
|
999
|
-
color: credentialConfigured === true ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-label-secondary)',
|
|
1000
|
-
backgroundColor: credentialConfigured === true ? 'var(--dsw-alias-state-success-tertiary)' : 'var(--dsw-alias-bg-layer-2)',
|
|
1134
|
+
color: credentialConfigured === true && bindingReady ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-label-secondary)',
|
|
1135
|
+
backgroundColor: credentialConfigured === true && bindingReady ? 'var(--dsw-alias-state-success-tertiary)' : 'var(--dsw-alias-bg-layer-2)',
|
|
1001
1136
|
fontSize: 12,
|
|
1002
|
-
} }, credentialConfigured === true ? '已连接' : credentialConfigured === false ? '未连接' : '检查中')),
|
|
1003
|
-
h('p', { role: 'status', style: mutedTextStyle }, credentialConfigured === true
|
|
1137
|
+
} }, credentialConfigured === true ? bindingReady ? '已连接' : '待核对' : credentialConfigured === false ? '未连接' : '检查中')),
|
|
1138
|
+
h('p', { role: 'status', style: mutedTextStyle }, credentialConfigured === true && !bindingReady
|
|
1139
|
+
? '本机有 ANT 凭证,但尚未确认它绑定的团队;请先完成上方的绑定检查。'
|
|
1140
|
+
: credentialConfigured === true
|
|
1004
1141
|
? 'DSH 已安全保存此设备的 ANT 团队凭证。'
|
|
1005
1142
|
: credentialConfigured === false ? '验证工作邮箱后即可连接团队模型。' : '正在检查 ANT 连接状态…'),
|
|
1006
1143
|
serviceCompatibility.state !== 'idle' ? h('p', {
|
|
@@ -1011,10 +1148,10 @@ window.__ModuleLoader__.load({
|
|
|
1011
1148
|
},
|
|
1012
1149
|
}, serviceCompatibility.message) : null,
|
|
1013
1150
|
credentialConfigured === true && !showConnectForm ? h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 8 } },
|
|
1014
|
-
h('button', { type: 'button', disabled: busy, onClick: () => void refreshModels(), style: primaryButtonStyle }, busy ? '刷新中…' : '刷新团队模型'),
|
|
1015
|
-
h('button', { type: 'button', disabled: busy, onClick: () => { setReauthenticate(true); setMessage('') }, style: secondaryButtonStyle }, '重新验证邮箱'),
|
|
1151
|
+
h('button', { type: 'button', disabled: busy || !bindingReady, onClick: () => void refreshModels(), style: primaryButtonStyle }, busy ? '刷新中…' : '刷新团队模型'),
|
|
1152
|
+
h('button', { type: 'button', disabled: busy || !bindingReady, onClick: () => { setReauthenticate(true); setMessage('') }, style: secondaryButtonStyle }, '重新验证邮箱'),
|
|
1016
1153
|
h('button', { type: 'button', disabled: busy, onClick: () => { if (window.confirm('断开当前设备与 ANT 的连接?本机 ANT 凭证和模型设置将被清除,团队服务地址会保留。')) void disconnect() }, style: secondaryButtonStyle }, '断开当前设备')) : null,
|
|
1017
|
-
h('section', { style: {
|
|
1154
|
+
h('section', { style: { display: 'grid', gap: 8, paddingTop: 12, borderTop: '0.5px solid var(--dsw-alias-border-l2)' } },
|
|
1018
1155
|
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12 } },
|
|
1019
1156
|
h('h4', { style: { margin: 0, fontSize: 14, fontWeight: 650 } }, 'ANT 插件更新'),
|
|
1020
1157
|
h('button', { type: 'button', disabled: busy || !canCheckUpdate || bundleUpdate.state === 'checking', onClick: () => void checkBundleUpdate(), style: secondaryButtonStyle }, bundleUpdate.state === 'checking' ? '检查中…' : '检查更新')),
|
|
@@ -1034,7 +1171,7 @@ window.__ModuleLoader__.load({
|
|
|
1034
1171
|
id: 'ant-team-model-email', type: 'email', autoComplete: 'email', required: true, value: email,
|
|
1035
1172
|
onChange: (event) => setEmail(event.target.value), style: inputStyle,
|
|
1036
1173
|
}),
|
|
1037
|
-
h('button', { type: 'submit', disabled: busy, style: primaryButtonStyle }, busy ? '发送中…' : '发送验证码')) : null,
|
|
1174
|
+
h('button', { type: 'submit', disabled: busy || !bindingReady, style: primaryButtonStyle }, busy ? '发送中…' : '发送验证码')) : null,
|
|
1038
1175
|
credentialConfigured === true && showConnectForm ? h('button', {
|
|
1039
1176
|
type: 'button', disabled: busy,
|
|
1040
1177
|
onClick: () => { if (window.confirm('断开当前设备与 ANT 的连接?本机 ANT 凭证和模型设置将被清除,团队服务地址会保留。')) void disconnect() },
|
|
@@ -1047,7 +1184,7 @@ window.__ModuleLoader__.load({
|
|
|
1047
1184
|
required: true, maxLength: 8, value: code, placeholder: '8 位验证码',
|
|
1048
1185
|
onChange: (event) => setCode(event.target.value.replace(/\D/gu, '').slice(0, 8)), style: inputStyle,
|
|
1049
1186
|
}),
|
|
1050
|
-
h('button', { type: 'submit', disabled: busy || code.length !== 8, style: primaryButtonStyle }, busy ? '连接中…' : '验证并连接')) : null,
|
|
1187
|
+
h('button', { type: 'submit', disabled: busy || !bindingReady || code.length !== 8, style: primaryButtonStyle }, busy ? '连接中…' : '验证并连接')) : null,
|
|
1051
1188
|
allowLocalCleanup ? h('section', { style: { ...panelStyle, padding: 14, gap: 9 } },
|
|
1052
1189
|
h('p', { style: mutedTextStyle }, savedDeviceId()
|
|
1053
1190
|
? `已保存的设备编号:${savedDeviceId()}。请交给原团队管理员核对。`
|
|
@@ -1077,10 +1214,16 @@ window.__ModuleLoader__.load({
|
|
|
1077
1214
|
}
|
|
1078
1215
|
|
|
1079
1216
|
function EnterpriseExtensionsPage({ remote, onBack, openBundle }) {
|
|
1080
|
-
const [tab, setTab] = useState('
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
:
|
|
1217
|
+
const [tab, setTab] = useState('all')
|
|
1218
|
+
const [query, setQuery] = useState('')
|
|
1219
|
+
const navigation = {
|
|
1220
|
+
onAll: () => setTab('all'), onSkills: () => setTab('skills'),
|
|
1221
|
+
onPlugins: () => setTab('plugins'),
|
|
1222
|
+
}
|
|
1223
|
+
return tab === 'plugins'
|
|
1224
|
+
? h(PluginCenterPage, { remote, onBack, openBundle, query, onQueryChange: setQuery, ...navigation })
|
|
1225
|
+
: h(SkillCenterPage, { remote, onBack, openBundle, query, onQueryChange: setQuery,
|
|
1226
|
+
includePlugins: tab === 'all', ...navigation })
|
|
1084
1227
|
}
|
|
1085
1228
|
|
|
1086
1229
|
function apply(ctx) {
|