@ai-wendao/ant 0.3.14 → 0.3.15
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/client.js +141 -51
- package/package.json +1 -1
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,7 +266,8 @@ 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
273
|
function SkillCenterPage({ onBack, onPlugins }) {
|
|
@@ -276,9 +326,11 @@ window.__ModuleLoader__.load({
|
|
|
276
326
|
height: '100%', overflowY: 'auto', boxSizing: 'border-box',
|
|
277
327
|
padding: 'calc(var(--dsh-frame-top-clearance, 0px) + 20px) clamp(16px, 3vw, 36px) 48px',
|
|
278
328
|
} }, 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
|
-
|
|
329
|
+
h('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } },
|
|
330
|
+
h('nav', { 'aria-label': '企业扩展分类', style: { display: 'flex', gap: 6 } },
|
|
331
|
+
h('span', { 'aria-current': 'page', style: { ...categoryButtonStyle, display: 'inline-flex', alignItems: 'center', cursor: 'default', backgroundColor: 'var(--dsw-alias-bg-layer-2)' } }, '技能'),
|
|
332
|
+
h('button', { type: 'button', onClick: onPlugins, style: categoryButtonStyle }, '插件')),
|
|
333
|
+
h('button', { type: 'button', onClick: onBack, style: { ...categoryButtonStyle, marginLeft: 'auto' } }, 'ANT 设置')),
|
|
282
334
|
h('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'end', justifyContent: 'space-between', gap: 16 } },
|
|
283
335
|
h('div', null,
|
|
284
336
|
h('h1', { style: { margin: 0, fontSize: 28 } }, '企业精选技能'),
|
|
@@ -309,9 +361,9 @@ window.__ModuleLoader__.load({
|
|
|
309
361
|
skill.local_status === 'conflict'
|
|
310
362
|
? h('p', { role: 'status', style: mutedTextStyle }, '本机同名技能有未受 ANT 管理的内容、文件已被修改或备份损坏;请联系管理员处理。')
|
|
311
363
|
: h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 'auto' } },
|
|
312
|
-
h('button', { type: 'button', disabled: Boolean(workingId)
|
|
364
|
+
skill.local_status === 'installed' ? null : h('button', { type: 'button', disabled: Boolean(workingId),
|
|
313
365
|
onClick: () => void changeInstall(skill), style: primaryButtonStyle },
|
|
314
|
-
skill.local_status === '
|
|
366
|
+
skill.local_status === 'update-available' ? '更新技能' : '安装到这台 DSH'),
|
|
315
367
|
skill.local_status !== 'not-installed' ? h('button', { type: 'button', disabled: Boolean(workingId),
|
|
316
368
|
onClick: () => void changeInstall(skill, true), style: secondaryButtonStyle }, '卸载') : null))
|
|
317
369
|
})) : !loading && skills.length ? h('p', { style: mutedTextStyle }, '没有符合筛选条件的技能。') : null))
|
|
@@ -320,7 +372,8 @@ window.__ModuleLoader__.load({
|
|
|
320
372
|
function PluginCenterPage({ remote, onBack, onSkills, openBundle }) {
|
|
321
373
|
const [plugins, setPlugins] = useState([])
|
|
322
374
|
const [bundles, setBundles] = useState([])
|
|
323
|
-
const [loading, setLoading] = useState(
|
|
375
|
+
const [loading, setLoading] = useState(true)
|
|
376
|
+
const [loadError, setLoadError] = useState(false)
|
|
324
377
|
const [workingId, setWorkingId] = useState('')
|
|
325
378
|
const [uncertainId, setUncertainId] = useState('')
|
|
326
379
|
const [completedId, setCompletedId] = useState('')
|
|
@@ -341,10 +394,12 @@ window.__ModuleLoader__.load({
|
|
|
341
394
|
if (!listed.ok) throw new Error(listed.error?.message || '无法读取 DSH 本机插件。')
|
|
342
395
|
setPlugins(result.plugins)
|
|
343
396
|
setBundles(listed.value)
|
|
397
|
+
setLoadError(false)
|
|
344
398
|
setMessage(result.plugins.length ? '' : '团队暂未发布精选插件。')
|
|
345
399
|
} catch (error) {
|
|
346
400
|
setPlugins([])
|
|
347
401
|
setBundles([])
|
|
402
|
+
setLoadError(true)
|
|
348
403
|
setMessage(error instanceof Error ? error.message : '无法读取企业精选插件。')
|
|
349
404
|
} finally { setLoading(false) }
|
|
350
405
|
}
|
|
@@ -437,7 +492,7 @@ window.__ModuleLoader__.load({
|
|
|
437
492
|
const installed = bundles.find((bundle) => bundle.name === plugin.package_name && bundle.installed)
|
|
438
493
|
const artifactUrl = approvedPluginArtifactUrl(plugin)
|
|
439
494
|
const matches = !query.trim() || `${plugin.title} ${plugin.description} ${plugin.package_name}`.toLowerCase().includes(query.trim().toLowerCase())
|
|
440
|
-
return matches && (filter === 'all' || filter === 'installed' && installed
|
|
495
|
+
return matches && (filter === 'all' || filter === 'installed' && Boolean(installed)
|
|
441
496
|
|| filter === 'available' && artifactUrl && (!installed || (installed.version && installed.version !== plugin.version))
|
|
442
497
|
|| filter === 'retired' && plugin.status !== 'published')
|
|
443
498
|
})
|
|
@@ -445,25 +500,28 @@ window.__ModuleLoader__.load({
|
|
|
445
500
|
height: '100%', overflowY: 'auto', boxSizing: 'border-box',
|
|
446
501
|
padding: 'calc(var(--dsh-frame-top-clearance, 0px) + 20px) clamp(16px, 3vw, 36px) 48px',
|
|
447
502
|
} }, h('div', { style: { maxWidth: 1440, margin: '0 auto', display: 'grid', gap: 22 } },
|
|
448
|
-
h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 8 } },
|
|
449
|
-
h('
|
|
450
|
-
|
|
503
|
+
h('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } },
|
|
504
|
+
h('nav', { 'aria-label': '企业扩展分类', style: { display: 'flex', gap: 6 } },
|
|
505
|
+
h('button', { type: 'button', onClick: onSkills, style: categoryButtonStyle }, '技能'),
|
|
506
|
+
h('span', { 'aria-current': 'page', style: { ...categoryButtonStyle, display: 'inline-flex', alignItems: 'center', cursor: 'default', backgroundColor: 'var(--dsw-alias-bg-layer-2)' } }, '插件')),
|
|
507
|
+
h('button', { type: 'button', onClick: onBack, style: { ...categoryButtonStyle, marginLeft: 'auto' } }, 'ANT 设置')),
|
|
451
508
|
h('div', { style: { display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between', gap: 16 } },
|
|
452
509
|
h('div', null,
|
|
453
510
|
h('h1', { style: { margin: 0, fontSize: 28 } }, '企业精选插件'),
|
|
454
511
|
h('p', { style: { ...mutedTextStyle, marginTop: 6 } }, '这里展示团队审核过的插件版本及本机状态。')),
|
|
455
512
|
h('button', { type: 'button', disabled: loading, onClick: () => void refresh(), style: secondaryButtonStyle }, loading ? '读取中…' : '刷新目录')),
|
|
456
|
-
h('
|
|
457
|
-
h('
|
|
458
|
-
h('p', { style: mutedTextStyle }, '
|
|
459
|
-
h(
|
|
460
|
-
h(
|
|
513
|
+
!loading && !plugins.length && !loadError ? h('section', { role: 'status', style: panelStyle },
|
|
514
|
+
h('h2', { style: { margin: 0, fontSize: 18 } }, '团队暂未发布精选插件'),
|
|
515
|
+
h('p', { style: mutedTextStyle }, '需要某个插件?请联系企业管理员,说明用途或提供插件地址。审核发布后会显示在这里。')) : null,
|
|
516
|
+
!loading && loadError ? h('p', { role: 'alert', style: mutedTextStyle }, message) : null,
|
|
517
|
+
plugins.length ? h(ExtensionRequestNotice) : null,
|
|
518
|
+
plugins.length ? h('div', { style: panelStyle },
|
|
461
519
|
h('input', { type: 'search', value: query, onChange: (event) => setQuery(event.target.value), placeholder: '搜索插件名称或用途', 'aria-label': '搜索企业精选插件', style: inputStyle }),
|
|
462
520
|
h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 8 } },
|
|
463
521
|
...[['all', '全部'], ['available', '可安装或升级'], ['installed', '已安装'], ['retired', '已废弃']].map(([value, label]) =>
|
|
464
522
|
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,
|
|
523
|
+
style: { ...secondaryButtonStyle, minHeight: 34, backgroundColor: filter === value ? 'var(--dsw-alias-bg-layer-2)' : secondaryButtonStyle.backgroundColor } }, label)))) : null,
|
|
524
|
+
plugins.length && message ? h('p', { role: 'status', style: mutedTextStyle }, message) : null,
|
|
467
525
|
visible.length ? h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 420px), 1fr))', gap: 16 } },
|
|
468
526
|
...visible.map((plugin) => {
|
|
469
527
|
const local = bundles.find((bundle) => bundle.name === plugin.package_name && bundle.installed)
|
|
@@ -515,7 +573,7 @@ window.__ModuleLoader__.load({
|
|
|
515
573
|
: '没有符合筛选条件的插件。') : null))
|
|
516
574
|
}
|
|
517
575
|
|
|
518
|
-
function ModelSyncFooter({ remote, serviceUrl, onServiceUrlChange }) {
|
|
576
|
+
function ModelSyncFooter({ remote, serviceUrl, bindingReady, onServiceUrlChange, onConnectionChange }) {
|
|
519
577
|
const [email, setEmail] = useState('')
|
|
520
578
|
const [code, setCode] = useState('')
|
|
521
579
|
const [message, setMessage] = useState('')
|
|
@@ -540,13 +598,13 @@ window.__ModuleLoader__.load({
|
|
|
540
598
|
: `${message} 本机无法确认旧设备编号;请让原团队管理员按成员查看设备清单,勿用新编号猜测。`
|
|
541
599
|
}
|
|
542
600
|
|
|
543
|
-
const inspectServiceCompatibility = async (rawServiceUrl, useSavedConnection = false) => {
|
|
601
|
+
const inspectServiceCompatibility = async (rawServiceUrl, useSavedConnection = false, isCurrent = () => true) => {
|
|
544
602
|
const normalized = normalizeServiceUrl(rawServiceUrl)
|
|
545
603
|
if (!normalized && !useSavedConnection) {
|
|
546
|
-
setServiceCompatibility({ state: 'idle', message: '' })
|
|
604
|
+
if (isCurrent()) setServiceCompatibility({ state: 'idle', message: '' })
|
|
547
605
|
return null
|
|
548
606
|
}
|
|
549
|
-
setServiceCompatibility({ state: 'checking', message: '正在识别 ANT 客户端与服务端版本…' })
|
|
607
|
+
if (isCurrent()) setServiceCompatibility({ state: 'checking', message: '正在识别 ANT 客户端与服务端版本…' })
|
|
550
608
|
try {
|
|
551
609
|
const manager = remote?.pluginManager
|
|
552
610
|
if (typeof manager?.listBundles !== 'function') throw new Error('当前 DSH 未提供 Bundle 版本读取接口。')
|
|
@@ -577,14 +635,14 @@ window.__ModuleLoader__.load({
|
|
|
577
635
|
: `已识别 ANT Bundle v${result.bundle_version} 和服务端 v${result.server_version};服务端未提供协议能力,兼容性未知,仍允许继续。`
|
|
578
636
|
const state = compatibility === true ? 'compatible' : compatibility === false ? 'incompatible' : 'unknown'
|
|
579
637
|
const status = { state, message, ...result }
|
|
580
|
-
setServiceCompatibility(status)
|
|
638
|
+
if (isCurrent()) setServiceCompatibility(status)
|
|
581
639
|
return status
|
|
582
640
|
} catch (error) {
|
|
583
641
|
const status = {
|
|
584
642
|
state: 'unknown',
|
|
585
643
|
message: `无法确认 ANT 版本兼容性,仍允许继续:${error instanceof Error ? error.message : '连接检查失败。'}`,
|
|
586
644
|
}
|
|
587
|
-
setServiceCompatibility(status)
|
|
645
|
+
if (isCurrent()) setServiceCompatibility(status)
|
|
588
646
|
return status
|
|
589
647
|
}
|
|
590
648
|
}
|
|
@@ -629,6 +687,10 @@ window.__ModuleLoader__.load({
|
|
|
629
687
|
}
|
|
630
688
|
|
|
631
689
|
const refreshModels = async () => {
|
|
690
|
+
if (!bindingReady) {
|
|
691
|
+
setMessage('请先等待团队绑定检查完成,再刷新团队模型。')
|
|
692
|
+
return
|
|
693
|
+
}
|
|
632
694
|
setBusy(true)
|
|
633
695
|
setMessage('正在从 ANT 刷新获准模型…')
|
|
634
696
|
try {
|
|
@@ -806,6 +868,7 @@ window.__ModuleLoader__.load({
|
|
|
806
868
|
const credentialResult = await remote.credentials.unset('ANT_MODEL_API_KEY')
|
|
807
869
|
if (!credentialResult.ok) throw new Error(credentialResult.error?.message || 'ANT 成员凭证未能清除。')
|
|
808
870
|
setCredentialConfigured(false)
|
|
871
|
+
onConnectionChange?.(null)
|
|
809
872
|
setNeedsReconnect(false)
|
|
810
873
|
setReauthenticate(false)
|
|
811
874
|
setAllowLocalCleanup(false)
|
|
@@ -857,13 +920,13 @@ window.__ModuleLoader__.load({
|
|
|
857
920
|
if (!current) return
|
|
858
921
|
const configured = result.ok ? Boolean(result.value?.ANT_MODEL_API_KEY?.configured) : null
|
|
859
922
|
setCredentialConfigured(configured)
|
|
860
|
-
if (configured) {
|
|
923
|
+
if (configured && bindingReady) {
|
|
861
924
|
void refreshModels()
|
|
862
925
|
void checkBundleUpdate()
|
|
863
926
|
}
|
|
864
927
|
}).catch(() => { if (current) setCredentialConfigured(null) })
|
|
865
928
|
return () => { current = false }
|
|
866
|
-
}, [remote])
|
|
929
|
+
}, [remote, bindingReady])
|
|
867
930
|
|
|
868
931
|
useEffect(() => {
|
|
869
932
|
let current = true
|
|
@@ -883,11 +946,21 @@ window.__ModuleLoader__.load({
|
|
|
883
946
|
}, [remote, credentialConfigured])
|
|
884
947
|
|
|
885
948
|
useEffect(() => {
|
|
886
|
-
|
|
887
|
-
|
|
949
|
+
if (!bindingReady) {
|
|
950
|
+
setServiceCompatibility({ state: 'idle', message: '' })
|
|
951
|
+
return
|
|
952
|
+
}
|
|
953
|
+
let current = true
|
|
954
|
+
void inspectServiceCompatibility(serviceUrl, credentialConfigured === true, () => current)
|
|
955
|
+
return () => { current = false }
|
|
956
|
+
}, [remote, serviceUrl, credentialConfigured, bindingReady])
|
|
888
957
|
|
|
889
958
|
const requestCode = async (event) => {
|
|
890
959
|
event.preventDefault()
|
|
960
|
+
if (!bindingReady) {
|
|
961
|
+
setMessage('请先等待团队绑定检查完成,再验证工作邮箱。')
|
|
962
|
+
return
|
|
963
|
+
}
|
|
891
964
|
const normalized = normalizeServiceUrl(serviceUrl)
|
|
892
965
|
const normalizedEmail = email.trim()
|
|
893
966
|
if (!normalized) {
|
|
@@ -929,6 +1002,10 @@ window.__ModuleLoader__.load({
|
|
|
929
1002
|
|
|
930
1003
|
const connect = async (event) => {
|
|
931
1004
|
event.preventDefault()
|
|
1005
|
+
if (!bindingReady) {
|
|
1006
|
+
setMessage('请先等待团队绑定检查完成,再连接团队。')
|
|
1007
|
+
return
|
|
1008
|
+
}
|
|
932
1009
|
const normalized = normalizeServiceUrl(serviceUrl)
|
|
933
1010
|
const normalizedEmail = email.trim()
|
|
934
1011
|
const normalizedCode = code.trim()
|
|
@@ -945,6 +1022,7 @@ window.__ModuleLoader__.load({
|
|
|
945
1022
|
}
|
|
946
1023
|
setBusy(true)
|
|
947
1024
|
setMessage('正在验证邮箱并同步团队模型…')
|
|
1025
|
+
let credentialSaved = false
|
|
948
1026
|
try {
|
|
949
1027
|
await requireCompatibleService(normalized)
|
|
950
1028
|
localStorage.setItem(SERVICE_URL_STORAGE_KEY, normalized)
|
|
@@ -968,24 +1046,34 @@ window.__ModuleLoader__.load({
|
|
|
968
1046
|
}
|
|
969
1047
|
const credentialWrite = await remote.credentials.set('ANT_MODEL_API_KEY', result.token)
|
|
970
1048
|
if (!credentialWrite.ok) throw new Error(credentialWrite.error?.message || 'DSH 无法安全保存 ANT 凭证。')
|
|
1049
|
+
credentialSaved = true
|
|
971
1050
|
await applyAuthorizedModels(result.models, normalized)
|
|
972
1051
|
setCode('')
|
|
973
1052
|
onServiceUrlChange(normalized)
|
|
974
1053
|
setCodeRequested(false)
|
|
975
1054
|
setCredentialConfigured(true)
|
|
1055
|
+
onConnectionChange?.(normalized)
|
|
976
1056
|
setAllowLocalCleanup(false)
|
|
977
1057
|
setNeedsReconnect(false)
|
|
978
1058
|
setReauthenticate(false)
|
|
979
1059
|
setMessage(`已连接 ${result.email || normalizedEmail},同步 ${result.models.length} 个获准模型。它们现已出现在 DSH 模型选择器中。`)
|
|
980
1060
|
} catch (error) {
|
|
981
|
-
|
|
1061
|
+
if (credentialSaved) {
|
|
1062
|
+
setCredentialConfigured(true)
|
|
1063
|
+
setCode('')
|
|
1064
|
+
setCodeRequested(false)
|
|
1065
|
+
onConnectionChange?.(normalized)
|
|
1066
|
+
setMessage(`ANT 凭证已保存,但团队模型未能同步:${error instanceof Error ? error.message : '请稍后重试。'} 请点击“刷新团队模型”重试。`)
|
|
1067
|
+
} else {
|
|
1068
|
+
setMessage(error instanceof Error ? error.message : '连接失败,请稍后重试。')
|
|
1069
|
+
}
|
|
982
1070
|
} finally {
|
|
983
1071
|
setBusy(false)
|
|
984
1072
|
}
|
|
985
1073
|
}
|
|
986
1074
|
|
|
987
1075
|
const showConnectForm = credentialConfigured !== true || reauthenticate || needsReconnect
|
|
988
|
-
const canCheckUpdate = credentialConfigured === true && !needsReconnect
|
|
1076
|
+
const canCheckUpdate = bindingReady && credentialConfigured === true && !needsReconnect
|
|
989
1077
|
const updateGuideUrl = pinnedServiceUrl
|
|
990
1078
|
? new URL('/ant-update', pinnedServiceUrl).href : null
|
|
991
1079
|
return h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 400px), 1fr))', alignItems: 'start', gap: 16 } },
|
|
@@ -996,11 +1084,13 @@ window.__ModuleLoader__.load({
|
|
|
996
1084
|
h('p', { style: { ...mutedTextStyle, marginTop: 4 } }, '使用上方团队服务地址验证工作邮箱并同步授权模型。连接状态变化时可随时刷新。')),
|
|
997
1085
|
h('span', { style: {
|
|
998
1086
|
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)',
|
|
1087
|
+
color: credentialConfigured === true && bindingReady ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-label-secondary)',
|
|
1088
|
+
backgroundColor: credentialConfigured === true && bindingReady ? 'var(--dsw-alias-state-success-tertiary)' : 'var(--dsw-alias-bg-layer-2)',
|
|
1001
1089
|
fontSize: 12,
|
|
1002
|
-
} }, credentialConfigured === true ? '已连接' : credentialConfigured === false ? '未连接' : '检查中')),
|
|
1003
|
-
h('p', { role: 'status', style: mutedTextStyle }, credentialConfigured === true
|
|
1090
|
+
} }, credentialConfigured === true ? bindingReady ? '已连接' : '待核对' : credentialConfigured === false ? '未连接' : '检查中')),
|
|
1091
|
+
h('p', { role: 'status', style: mutedTextStyle }, credentialConfigured === true && !bindingReady
|
|
1092
|
+
? '本机有 ANT 凭证,但尚未确认它绑定的团队;请先完成上方的绑定检查。'
|
|
1093
|
+
: credentialConfigured === true
|
|
1004
1094
|
? 'DSH 已安全保存此设备的 ANT 团队凭证。'
|
|
1005
1095
|
: credentialConfigured === false ? '验证工作邮箱后即可连接团队模型。' : '正在检查 ANT 连接状态…'),
|
|
1006
1096
|
serviceCompatibility.state !== 'idle' ? h('p', {
|
|
@@ -1011,10 +1101,10 @@ window.__ModuleLoader__.load({
|
|
|
1011
1101
|
},
|
|
1012
1102
|
}, serviceCompatibility.message) : null,
|
|
1013
1103
|
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 }, '重新验证邮箱'),
|
|
1104
|
+
h('button', { type: 'button', disabled: busy || !bindingReady, onClick: () => void refreshModels(), style: primaryButtonStyle }, busy ? '刷新中…' : '刷新团队模型'),
|
|
1105
|
+
h('button', { type: 'button', disabled: busy || !bindingReady, onClick: () => { setReauthenticate(true); setMessage('') }, style: secondaryButtonStyle }, '重新验证邮箱'),
|
|
1016
1106
|
h('button', { type: 'button', disabled: busy, onClick: () => { if (window.confirm('断开当前设备与 ANT 的连接?本机 ANT 凭证和模型设置将被清除,团队服务地址会保留。')) void disconnect() }, style: secondaryButtonStyle }, '断开当前设备')) : null,
|
|
1017
|
-
h('section', { style: {
|
|
1107
|
+
h('section', { style: { display: 'grid', gap: 8, paddingTop: 12, borderTop: '0.5px solid var(--dsw-alias-border-l2)' } },
|
|
1018
1108
|
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12 } },
|
|
1019
1109
|
h('h4', { style: { margin: 0, fontSize: 14, fontWeight: 650 } }, 'ANT 插件更新'),
|
|
1020
1110
|
h('button', { type: 'button', disabled: busy || !canCheckUpdate || bundleUpdate.state === 'checking', onClick: () => void checkBundleUpdate(), style: secondaryButtonStyle }, bundleUpdate.state === 'checking' ? '检查中…' : '检查更新')),
|
|
@@ -1034,7 +1124,7 @@ window.__ModuleLoader__.load({
|
|
|
1034
1124
|
id: 'ant-team-model-email', type: 'email', autoComplete: 'email', required: true, value: email,
|
|
1035
1125
|
onChange: (event) => setEmail(event.target.value), style: inputStyle,
|
|
1036
1126
|
}),
|
|
1037
|
-
h('button', { type: 'submit', disabled: busy, style: primaryButtonStyle }, busy ? '发送中…' : '发送验证码')) : null,
|
|
1127
|
+
h('button', { type: 'submit', disabled: busy || !bindingReady, style: primaryButtonStyle }, busy ? '发送中…' : '发送验证码')) : null,
|
|
1038
1128
|
credentialConfigured === true && showConnectForm ? h('button', {
|
|
1039
1129
|
type: 'button', disabled: busy,
|
|
1040
1130
|
onClick: () => { if (window.confirm('断开当前设备与 ANT 的连接?本机 ANT 凭证和模型设置将被清除,团队服务地址会保留。')) void disconnect() },
|
|
@@ -1047,7 +1137,7 @@ window.__ModuleLoader__.load({
|
|
|
1047
1137
|
required: true, maxLength: 8, value: code, placeholder: '8 位验证码',
|
|
1048
1138
|
onChange: (event) => setCode(event.target.value.replace(/\D/gu, '').slice(0, 8)), style: inputStyle,
|
|
1049
1139
|
}),
|
|
1050
|
-
h('button', { type: 'submit', disabled: busy || code.length !== 8, style: primaryButtonStyle }, busy ? '连接中…' : '验证并连接')) : null,
|
|
1140
|
+
h('button', { type: 'submit', disabled: busy || !bindingReady || code.length !== 8, style: primaryButtonStyle }, busy ? '连接中…' : '验证并连接')) : null,
|
|
1051
1141
|
allowLocalCleanup ? h('section', { style: { ...panelStyle, padding: 14, gap: 9 } },
|
|
1052
1142
|
h('p', { style: mutedTextStyle }, savedDeviceId()
|
|
1053
1143
|
? `已保存的设备编号:${savedDeviceId()}。请交给原团队管理员核对。`
|