dsh-monkeyai-sessions 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/README.zh.md +96 -0
- package/client.js +136 -136
- package/lib/client-runtime.cjs +136 -136
- package/package.json +4 -3
- package/src/client/index.js +119 -119
- package/src/host/monkeyai-remote.js +76 -25
- package/src/shared/projection.mjs +10 -10
package/src/client/index.js
CHANGED
|
@@ -91,7 +91,7 @@ const PANEL_CSS = `
|
|
|
91
91
|
`
|
|
92
92
|
|
|
93
93
|
function errorText(error) {
|
|
94
|
-
return typeof error?.message === 'string' ? error.message : typeof error === 'string' ? error : '
|
|
94
|
+
return typeof error?.message === 'string' ? error.message : typeof error === 'string' ? error : '远程操作失败'
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
// Lightweight inline markdown: fenced code blocks, `code`, **bold**,
|
|
@@ -221,8 +221,8 @@ function ApprovalCard({ request, hasControl, onRespond, answerText, setAnswerTex
|
|
|
221
221
|
const disabled = !hasControl
|
|
222
222
|
return h('div', { style: style.approval, role: 'alert' },
|
|
223
223
|
h('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
224
|
-
h('span', { style: { fontSize: 12.5, fontWeight: 650 } }, isPermission ? '
|
|
225
|
-
h('span', { style: { fontSize: 11, color: t.text3 } }, '
|
|
224
|
+
h('span', { style: { fontSize: 12.5, fontWeight: 650 } }, isPermission ? '请求授权' : 'MonkeyAI 需要你的输入'),
|
|
225
|
+
h('span', { style: { fontSize: 11, color: t.text3 } }, '回复需要 session:control 权限')),
|
|
226
226
|
questionText !== '' ? h('div', { style: { fontSize: 12.5, color: t.text2, whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' } }, questionText) : null,
|
|
227
227
|
options.length > 0
|
|
228
228
|
? h('div', { style: style.row },
|
|
@@ -230,26 +230,26 @@ function ApprovalCard({ request, hasControl, onRespond, answerText, setAnswerTex
|
|
|
230
230
|
type: 'button', style: option.kind === 'allow_once' || option.kind === 'allow_always' ? style.primaryButton : style.button,
|
|
231
231
|
key: option.optionId, disabled, onClick: () => onRespond(request, { outcome: 'selected', optionId: option.optionId }),
|
|
232
232
|
}, option.name || option.optionId)),
|
|
233
|
-
h('button', { type: 'button', style: style.button, disabled, onClick: () => onRespond(request, { outcome: 'cancelled' }) }, '
|
|
233
|
+
h('button', { type: 'button', style: style.button, disabled, onClick: () => onRespond(request, { outcome: 'cancelled' }) }, '拒绝'))
|
|
234
234
|
: null,
|
|
235
235
|
options.length === 0 && !isPermission
|
|
236
236
|
? h(React.Fragment, null,
|
|
237
237
|
h('textarea', {
|
|
238
238
|
style: { ...style.textarea, minHeight: 44 },
|
|
239
|
-
placeholder: disabled ? '
|
|
239
|
+
placeholder: disabled ? '只读模式:回复需要 session:control 权限' : '输入你的回答',
|
|
240
240
|
value: answerText, disabled,
|
|
241
241
|
onChange: event => setAnswerText(event.target.value),
|
|
242
242
|
}),
|
|
243
243
|
h('div', { style: style.row },
|
|
244
|
-
h('button', { type: 'button', style: style.primaryButton, disabled: disabled || answerText.trim() === '', onClick: () => onAnswer(request) }, '
|
|
245
|
-
h('button', { type: 'button', style: style.button, disabled, onClick: () => onRespond(request, { action: 'decline' }) }, '
|
|
244
|
+
h('button', { type: 'button', style: style.primaryButton, disabled: disabled || answerText.trim() === '', onClick: () => onAnswer(request) }, '回复'),
|
|
245
|
+
h('button', { type: 'button', style: style.button, disabled, onClick: () => onRespond(request, { action: 'decline' }) }, '拒绝')))
|
|
246
246
|
: null,
|
|
247
247
|
options.length === 0 && isPermission
|
|
248
248
|
? h('div', { style: style.row },
|
|
249
|
-
h('button', { type: 'button', style: style.button, disabled, onClick: () => onRespond(request, { outcome: 'cancelled' }) }, '
|
|
249
|
+
h('button', { type: 'button', style: style.button, disabled, onClick: () => onRespond(request, { outcome: 'cancelled' }) }, '拒绝'))
|
|
250
250
|
: null,
|
|
251
251
|
h('details', { style: style.details },
|
|
252
|
-
h('summary', { style: style.summary }, '
|
|
252
|
+
h('summary', { style: style.summary }, '请求详情'),
|
|
253
253
|
h('pre', { style: { margin: '6px 0 0', whiteSpace: 'pre-wrap', overflowWrap: 'anywhere', fontSize: 11.5 } }, JSON.stringify(request.data, null, 2))),
|
|
254
254
|
)
|
|
255
255
|
}
|
|
@@ -271,7 +271,7 @@ function RemoteSessionPage({ api }) {
|
|
|
271
271
|
const [pendingRequests, setPendingRequests] = useState([])
|
|
272
272
|
const [prompt, setPrompt] = useState('')
|
|
273
273
|
const [answerText, setAnswerText] = useState('')
|
|
274
|
-
const [status, setStatus] = useState('
|
|
274
|
+
const [status, setStatus] = useState('未连接')
|
|
275
275
|
const [error, setError] = useState('')
|
|
276
276
|
const [loading, setLoading] = useState(false)
|
|
277
277
|
const [lastDelivery, setLastDelivery] = useState(undefined)
|
|
@@ -353,7 +353,7 @@ function RemoteSessionPage({ api }) {
|
|
|
353
353
|
if (loading || !serverAddress || !signInEmail.trim() || signInPassword === '') return
|
|
354
354
|
setError('')
|
|
355
355
|
setLoading(true)
|
|
356
|
-
setStatus('
|
|
356
|
+
setStatus('正在登录 MonkeyAI')
|
|
357
357
|
try {
|
|
358
358
|
const account = await unwrap(api.signIn({
|
|
359
359
|
serverAddress,
|
|
@@ -364,7 +364,7 @@ function RemoteSessionPage({ api }) {
|
|
|
364
364
|
adoptAccount(account)
|
|
365
365
|
} catch (reason) {
|
|
366
366
|
setError(errorText(reason))
|
|
367
|
-
setStatus('
|
|
367
|
+
setStatus('登录失败')
|
|
368
368
|
} finally {
|
|
369
369
|
setLoading(false)
|
|
370
370
|
}
|
|
@@ -380,9 +380,9 @@ function RemoteSessionPage({ api }) {
|
|
|
380
380
|
setAuthorizationMode(mode)
|
|
381
381
|
setCopied(false)
|
|
382
382
|
if (mode === 'loopback') {
|
|
383
|
-
setStatus('
|
|
383
|
+
setStatus('等待批准')
|
|
384
384
|
} else {
|
|
385
|
-
setStatus('
|
|
385
|
+
setStatus('批准授权后,粘贴回调 URL')
|
|
386
386
|
}
|
|
387
387
|
// Keep the opener so the loopback page can post the callback back.
|
|
388
388
|
// Without it the browser cannot return the code automatically.
|
|
@@ -433,12 +433,12 @@ function RemoteSessionPage({ api }) {
|
|
|
433
433
|
setAccounts(current => [result.account, ...current.filter(item => item.accountId !== result.account.accountId)])
|
|
434
434
|
setAccountId(result.account.accountId)
|
|
435
435
|
setServerAddress(result.account.serverOrigin)
|
|
436
|
-
setStatus('MonkeyAI
|
|
436
|
+
setStatus('MonkeyAI 账号已连接')
|
|
437
437
|
} else if (result.status === 'failed') {
|
|
438
|
-
setError(result.error || '
|
|
439
|
-
setStatus('
|
|
438
|
+
setError(result.error || '自动授权失败,请改为粘贴回调 URL。')
|
|
439
|
+
setStatus('自动授权失败')
|
|
440
440
|
} else {
|
|
441
|
-
setStatus('
|
|
441
|
+
setStatus('授权请求已过期')
|
|
442
442
|
}
|
|
443
443
|
setAuthorizationRequestId('')
|
|
444
444
|
setAuthorizationUrl('')
|
|
@@ -452,7 +452,7 @@ function RemoteSessionPage({ api }) {
|
|
|
452
452
|
setAccounts(current => [account, ...current.filter(item => item.accountId !== account.accountId)])
|
|
453
453
|
setAccountId(account.accountId)
|
|
454
454
|
setServerAddress(account.serverOrigin)
|
|
455
|
-
setStatus('MonkeyAI
|
|
455
|
+
setStatus('MonkeyAI 账号已连接')
|
|
456
456
|
setAddingServer(false)
|
|
457
457
|
}
|
|
458
458
|
|
|
@@ -477,9 +477,9 @@ function RemoteSessionPage({ api }) {
|
|
|
477
477
|
try {
|
|
478
478
|
await navigator.clipboard.writeText(authorizationUrl)
|
|
479
479
|
setCopied(true)
|
|
480
|
-
setStatus('
|
|
480
|
+
setStatus('授权链接已复制')
|
|
481
481
|
} catch {
|
|
482
|
-
setStatus('
|
|
482
|
+
setStatus('复制失败,请手动选中链接文本')
|
|
483
483
|
}
|
|
484
484
|
}
|
|
485
485
|
|
|
@@ -497,7 +497,7 @@ function RemoteSessionPage({ api }) {
|
|
|
497
497
|
setSessionId('')
|
|
498
498
|
streamRef.current?.dispose()
|
|
499
499
|
streamRef.current = null
|
|
500
|
-
setStatus(result.items.length > 0 ? `${result.items.length} device(s) found` : '
|
|
500
|
+
setStatus(result.items.length > 0 ? `${result.items.length} device(s) found` : '暂无已注册设备')
|
|
501
501
|
if (first) await loadSessions(first)
|
|
502
502
|
} catch (reason) {
|
|
503
503
|
setError(errorText(reason))
|
|
@@ -522,10 +522,10 @@ function RemoteSessionPage({ api }) {
|
|
|
522
522
|
])
|
|
523
523
|
const errors = []
|
|
524
524
|
if (results[0].status === 'fulfilled') setSummarySessions(results[0].value.items)
|
|
525
|
-
else errors.push(
|
|
525
|
+
else errors.push(`会话摘要索引:${errorText(results[0].reason)}`)
|
|
526
526
|
if (results[1].status === 'fulfilled') setRuntimeSessions(results[1].value.sessions)
|
|
527
|
-
else errors.push(`ACP
|
|
528
|
-
setStatus(errors.length === 0 ? '
|
|
527
|
+
else errors.push(`ACP 会话列表:${errorText(results[1].reason)}`)
|
|
528
|
+
setStatus(errors.length === 0 ? '会话已刷新' : '部分会话来源不可用')
|
|
529
529
|
setError(errors.join('\n'))
|
|
530
530
|
setLoading(false)
|
|
531
531
|
}
|
|
@@ -545,38 +545,38 @@ function RemoteSessionPage({ api }) {
|
|
|
545
545
|
setCurrentModel('')
|
|
546
546
|
setVisibleCount(RECENT_LIMIT)
|
|
547
547
|
setHistoryLoading(true)
|
|
548
|
-
setStatus('
|
|
548
|
+
setStatus('正在连接远程会话')
|
|
549
549
|
try {
|
|
550
550
|
const stream = api.followSession({ accountId, machineId, sessionId: session.id, cwd: session.cwd })
|
|
551
551
|
streamRef.current = stream
|
|
552
|
-
setStatus('
|
|
552
|
+
setStatus('远程会话已连接')
|
|
553
553
|
for await (const frame of stream) {
|
|
554
554
|
setFrames(current => [...current, frame])
|
|
555
|
-
if (frame.type === 'ready' && !historyLoadedRef.current) setStatus('
|
|
556
|
-
if (frame.type === 'reconnecting') setStatus(frame.message || '
|
|
555
|
+
if (frame.type === 'ready' && !historyLoadedRef.current) setStatus('正在回放会话历史')
|
|
556
|
+
if (frame.type === 'reconnecting') setStatus(frame.message || '正在重连远程 runtime')
|
|
557
557
|
if (frame.type === 'history_loaded') {
|
|
558
558
|
historyLoadedRef.current = true
|
|
559
|
-
setStatus('
|
|
559
|
+
setStatus('历史已加载')
|
|
560
560
|
setHistoryLoading(false)
|
|
561
561
|
if (frame.model) setCurrentModel(frame.model)
|
|
562
562
|
}
|
|
563
563
|
if (frame.type === 'history_incomplete') {
|
|
564
|
-
setStatus('
|
|
564
|
+
setStatus('历史不完整:' + (frame.message || '回放被中断'))
|
|
565
565
|
setHistoryLoading(false)
|
|
566
566
|
}
|
|
567
567
|
if (frame.type === 'model_switched') {
|
|
568
568
|
if (frame.data?.model) setCurrentModel(frame.data.model)
|
|
569
569
|
setSwitchingModel(false)
|
|
570
|
-
setStatus('
|
|
570
|
+
setStatus('模型已切换')
|
|
571
571
|
}
|
|
572
572
|
if (frame.type === 'model_switch_failed') {
|
|
573
573
|
setSwitchingModel(false)
|
|
574
|
-
setError(frame.message || '
|
|
575
|
-
setStatus('
|
|
574
|
+
setError(frame.message || '模型切换被拒绝')
|
|
575
|
+
setStatus('模型切换失败')
|
|
576
576
|
}
|
|
577
577
|
if (frame.type === 'session_missing') {
|
|
578
|
-
setError(frame.message || '
|
|
579
|
-
setStatus('
|
|
578
|
+
setError(frame.message || '该会话在所选设备上已不存在。')
|
|
579
|
+
setStatus('设备上找不到该会话')
|
|
580
580
|
setHistoryLoading(false)
|
|
581
581
|
}
|
|
582
582
|
if (frame.type === 'request') {
|
|
@@ -589,31 +589,31 @@ function RemoteSessionPage({ api }) {
|
|
|
589
589
|
setStopping(false)
|
|
590
590
|
if (accepted) {
|
|
591
591
|
const cancelled = frame.data?.result?.stopReason === 'cancelled'
|
|
592
|
-
setStatus(cancelled ? '
|
|
592
|
+
setStatus(cancelled ? '回合已停止' : '回合已完成')
|
|
593
593
|
} else {
|
|
594
594
|
setPrompt(current => current === '' ? (lastDraftRef.current || '') : current)
|
|
595
|
-
setStatus('MonkeyAI
|
|
595
|
+
setStatus('MonkeyAI 未接受该指令,草稿已保留')
|
|
596
596
|
if (frame.message) setError(frame.message)
|
|
597
597
|
}
|
|
598
598
|
}
|
|
599
599
|
if (frame.type === 'cancel_sent') {
|
|
600
600
|
setStopping(false)
|
|
601
|
-
setStatus('
|
|
601
|
+
setStatus('已请求停止,等待 runtime 取消')
|
|
602
602
|
}
|
|
603
603
|
if (frame.type === 'response_sent') {
|
|
604
604
|
setPendingRequests(current => current.filter(item => item.requestId !== frame.requestId))
|
|
605
605
|
}
|
|
606
606
|
if (frame.type === 'response_error') {
|
|
607
|
-
setError(
|
|
607
|
+
setError(`远程回复被拒绝:${frame.code || '未知'}`)
|
|
608
608
|
}
|
|
609
|
-
if (frame.type === 'error') setError(frame.message || frame.code || '
|
|
609
|
+
if (frame.type === 'error') setError(frame.message || frame.code || '远程会话错误')
|
|
610
610
|
}
|
|
611
|
-
setStatus('
|
|
611
|
+
setStatus('远程会话已断开')
|
|
612
612
|
setHistoryLoading(false)
|
|
613
613
|
setTurnActive(false)
|
|
614
614
|
setStopping(false)
|
|
615
615
|
} catch (reason) {
|
|
616
|
-
setStatus('
|
|
616
|
+
setStatus('远程会话不可用')
|
|
617
617
|
setError(errorText(reason))
|
|
618
618
|
setHistoryLoading(false)
|
|
619
619
|
setTurnActive(false)
|
|
@@ -632,7 +632,7 @@ function RemoteSessionPage({ api }) {
|
|
|
632
632
|
setPrompt('')
|
|
633
633
|
setTurnActive(true)
|
|
634
634
|
setStopping(false)
|
|
635
|
-
setStatus('
|
|
635
|
+
setStatus('指令已发送,MonkeyAI 正在处理')
|
|
636
636
|
} catch (reason) {
|
|
637
637
|
setError(errorText(reason))
|
|
638
638
|
}
|
|
@@ -644,7 +644,7 @@ function RemoteSessionPage({ api }) {
|
|
|
644
644
|
try {
|
|
645
645
|
stream.send({ type: 'cancel' })
|
|
646
646
|
setStopping(true)
|
|
647
|
-
setStatus('
|
|
647
|
+
setStatus('停止中…')
|
|
648
648
|
} catch (reason) {
|
|
649
649
|
setError(errorText(reason))
|
|
650
650
|
}
|
|
@@ -652,7 +652,7 @@ function RemoteSessionPage({ api }) {
|
|
|
652
652
|
|
|
653
653
|
function respond(request, result) {
|
|
654
654
|
if (!hasControl) {
|
|
655
|
-
setError('MonkeyAI
|
|
655
|
+
setError('回复 MonkeyAI 需要 session:control 权限')
|
|
656
656
|
return
|
|
657
657
|
}
|
|
658
658
|
try {
|
|
@@ -684,7 +684,7 @@ function RemoteSessionPage({ api }) {
|
|
|
684
684
|
setCallbackUrl('')
|
|
685
685
|
setAuthorizationUrl('')
|
|
686
686
|
setAuthorizationMode('')
|
|
687
|
-
setStatus('
|
|
687
|
+
setStatus('未连接')
|
|
688
688
|
setAddingServer(false)
|
|
689
689
|
} catch (reason) {
|
|
690
690
|
setError(errorText(reason))
|
|
@@ -739,7 +739,7 @@ function RemoteSessionPage({ api }) {
|
|
|
739
739
|
setDevices(current => [entry, ...current.filter(item => item.machineId !== machineId)])
|
|
740
740
|
setMachineId(current => current || machineId)
|
|
741
741
|
}
|
|
742
|
-
setStatus(
|
|
742
|
+
setStatus(`已添加设备 ${entry.displayName}`)
|
|
743
743
|
}
|
|
744
744
|
|
|
745
745
|
function removeManualDevice(machineId) {
|
|
@@ -757,14 +757,14 @@ function RemoteSessionPage({ api }) {
|
|
|
757
757
|
if (created.model) setCurrentModel(created.model)
|
|
758
758
|
setRuntimeSessions(current => {
|
|
759
759
|
if (current.some(item => item.id === created.sessionId)) return current
|
|
760
|
-
return [{ id: created.sessionId, title: created.title || '
|
|
760
|
+
return [{ id: created.sessionId, title: created.title || '新建远程会话', cwd: created.cwd }, ...current]
|
|
761
761
|
})
|
|
762
762
|
setSummarySessions(current => current.filter(item => item.id !== created.sessionId))
|
|
763
|
-
setStatus('
|
|
764
|
-
await openSession({ id: created.sessionId, title: created.title || '
|
|
763
|
+
setStatus('会话已创建')
|
|
764
|
+
await openSession({ id: created.sessionId, title: created.title || '新建远程会话', cwd: created.cwd })
|
|
765
765
|
} catch (reason) {
|
|
766
766
|
setError(errorText(reason))
|
|
767
|
-
setStatus('
|
|
767
|
+
setStatus('无法创建远程会话')
|
|
768
768
|
} finally {
|
|
769
769
|
setNewSessionBusy(false)
|
|
770
770
|
}
|
|
@@ -780,10 +780,10 @@ function RemoteSessionPage({ api }) {
|
|
|
780
780
|
// Switch on the live session connection: a separate connection cannot
|
|
781
781
|
// see the mounted session and the runtime rejects the switch.
|
|
782
782
|
stream.send({ type: 'switch_model', model: modelId })
|
|
783
|
-
setStatus('
|
|
783
|
+
setStatus('正在切换模型…')
|
|
784
784
|
} catch (reason) {
|
|
785
785
|
setError(errorText(reason))
|
|
786
|
-
setStatus('
|
|
786
|
+
setStatus('模型切换失败')
|
|
787
787
|
setSwitchingModel(false)
|
|
788
788
|
}
|
|
789
789
|
}
|
|
@@ -841,32 +841,32 @@ function RemoteSessionPage({ api }) {
|
|
|
841
841
|
h('header', { style: style.header },
|
|
842
842
|
h('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
843
843
|
h(MonkeyIcon, { size: 22 }),
|
|
844
|
-
h('h1', { style: style.title }, 'MonkeyAI
|
|
844
|
+
h('h1', { style: style.title }, 'MonkeyAI 会话')),
|
|
845
845
|
h(StatusBadge, { status, live: isLive })),
|
|
846
846
|
error ? h('div', { role: 'alert', style: style.error },
|
|
847
847
|
h('span', { style: { flex: 1 } }, error),
|
|
848
848
|
h('button', {
|
|
849
|
-
type: 'button', 'aria-label': '
|
|
849
|
+
type: 'button', 'aria-label': '关闭错误提示', onClick: () => setError(''),
|
|
850
850
|
style: { border: 0, background: 'transparent', cursor: 'pointer', color: t.danger, fontWeight: 700, padding: '0 2px', fontSize: 13 },
|
|
851
851
|
}, '×')) : null,
|
|
852
852
|
showConnectCard ? h('section', { style: style.card },
|
|
853
853
|
addingServer && !authorizing ? h('div', { style: style.row },
|
|
854
|
-
h('h2', { style: { ...style.sectionTitle, flex: 1 } }, '
|
|
855
|
-
h('button', { type: 'button', style: style.button, onClick: () => { setAddingServer(false); setServerAddress(currentAccount?.serverOrigin || ''); setStatus('MonkeyAI
|
|
854
|
+
h('h2', { style: { ...style.sectionTitle, flex: 1 } }, '添加 MonkeyAI 服务器'),
|
|
855
|
+
h('button', { type: 'button', style: style.button, onClick: () => { setAddingServer(false); setServerAddress(currentAccount?.serverOrigin || ''); setStatus('MonkeyAI 账号已连接') } }, '取消')) : null,
|
|
856
856
|
authorizing ? h(React.Fragment, null,
|
|
857
857
|
h('div', { style: style.row },
|
|
858
|
-
h('h2', { style: { ...style.sectionTitle, flex: 1 } }, addingServer ? '
|
|
859
|
-
addingServer ? h('button', { type: 'button', style: style.button, onClick: () => { setAuthorizationRequestId(''); setAuthorizationUrl(''); setAuthorizationMode(''); setCallbackUrl(''); setAddingServer(false); setStatus('MonkeyAI
|
|
858
|
+
h('h2', { style: { ...style.sectionTitle, flex: 1 } }, addingServer ? '正在授权新服务器' : '等待授权'),
|
|
859
|
+
addingServer ? h('button', { type: 'button', style: style.button, onClick: () => { setAuthorizationRequestId(''); setAuthorizationUrl(''); setAuthorizationMode(''); setCallbackUrl(''); setAddingServer(false); setStatus('MonkeyAI 账号已连接') } }, '取消') : null),
|
|
860
860
|
h('ol', { style: { margin: 0, paddingLeft: 18, display: 'grid', gap: 6, fontSize: 12.5, color: t.text2 } },
|
|
861
|
-
h('li', null, '
|
|
861
|
+
h('li', null, '打开授权页并在 MonkeyAI 中批准访问。',
|
|
862
862
|
h('span', { style: style.row, title: authorizationUrl },
|
|
863
|
-
h('a', { href: authorizationUrl, target: '_blank', rel: 'noopener noreferrer', style: { color: t.accent, fontWeight: 600, fontSize: 12 } }, '
|
|
864
|
-
h('button', { type: 'button', style: style.button, onClick: copyAuthorizationUrl }, copied ? '
|
|
863
|
+
h('a', { href: authorizationUrl, target: '_blank', rel: 'noopener noreferrer', style: { color: t.accent, fontWeight: 600, fontSize: 12 } }, '打开授权页'),
|
|
864
|
+
h('button', { type: 'button', style: style.button, onClick: copyAuthorizationUrl }, copied ? '已复制' : '复制链接'))),
|
|
865
865
|
authorizationMode === 'loopback'
|
|
866
|
-
? h('li', null, '
|
|
867
|
-
: h('li', null, '
|
|
866
|
+
? h('li', null, '在浏览器中批准后面板会自动完成。若浏览器无法回传到 DSH,请复制回调 URL 粘贴到下方。')
|
|
867
|
+
: h('li', null, '批准后浏览器会显示 monkeyai-desktop:// 回调,复制地址栏完整 URL 粘贴到下方。')),
|
|
868
868
|
h('details', { style: style.details },
|
|
869
|
-
h('summary', { style: style.summary }, '
|
|
869
|
+
h('summary', { style: style.summary }, '没有自动完成?手动粘贴回调'),
|
|
870
870
|
h('div', { style: { ...style.row, paddingTop: 8 } },
|
|
871
871
|
h('input', {
|
|
872
872
|
type: 'url',
|
|
@@ -877,14 +877,14 @@ function RemoteSessionPage({ api }) {
|
|
|
877
877
|
style: { ...style.input, flex: '1 1 260px' },
|
|
878
878
|
onChange: event => setCallbackUrl(event.target.value),
|
|
879
879
|
}),
|
|
880
|
-
h('button', { type: 'button', style: style.primaryButton, disabled: loading || !callbackUrl, onClick: completeAuthorization }, '
|
|
880
|
+
h('button', { type: 'button', style: style.primaryButton, disabled: loading || !callbackUrl, onClick: completeAuthorization }, '完成授权'))))
|
|
881
881
|
: h(React.Fragment, null,
|
|
882
|
-
h('h2', { style: style.sectionTitle }, '
|
|
882
|
+
h('h2', { style: style.sectionTitle }, '连接 MonkeyAI'),
|
|
883
883
|
h('form', {
|
|
884
884
|
style: { display: 'grid', gap: 10 },
|
|
885
885
|
onSubmit: event => { event.preventDefault(); signIn() },
|
|
886
886
|
},
|
|
887
|
-
h(Field, { labelText: 'MonkeyAI
|
|
887
|
+
h(Field, { labelText: 'MonkeyAI 服务器地址' },
|
|
888
888
|
h('input', {
|
|
889
889
|
type: 'url',
|
|
890
890
|
value: serverAddress,
|
|
@@ -893,7 +893,7 @@ function RemoteSessionPage({ api }) {
|
|
|
893
893
|
autoComplete: 'url',
|
|
894
894
|
onChange: event => setServerAddress(event.target.value),
|
|
895
895
|
})),
|
|
896
|
-
h(Field, { labelText: '
|
|
896
|
+
h(Field, { labelText: '账号邮箱' },
|
|
897
897
|
h('input', {
|
|
898
898
|
type: 'email',
|
|
899
899
|
value: signInEmail,
|
|
@@ -902,11 +902,11 @@ function RemoteSessionPage({ api }) {
|
|
|
902
902
|
autoComplete: 'username',
|
|
903
903
|
onChange: event => setSignInEmail(event.target.value),
|
|
904
904
|
})),
|
|
905
|
-
h(Field, { labelText: '
|
|
905
|
+
h(Field, { labelText: '密码' },
|
|
906
906
|
h('input', {
|
|
907
907
|
type: 'password',
|
|
908
908
|
value: signInPassword,
|
|
909
|
-
placeholder: 'MonkeyAI
|
|
909
|
+
placeholder: 'MonkeyAI 账号密码',
|
|
910
910
|
style: style.input,
|
|
911
911
|
autoComplete: 'current-password',
|
|
912
912
|
onChange: event => setSignInPassword(event.target.value),
|
|
@@ -914,70 +914,70 @@ function RemoteSessionPage({ api }) {
|
|
|
914
914
|
h('button', {
|
|
915
915
|
type: 'submit', style: style.primaryButton,
|
|
916
916
|
disabled: loading || !serverAddress || !signInEmail.trim() || signInPassword === '',
|
|
917
|
-
}, loading ? '
|
|
917
|
+
}, loading ? '登录中…' : '登录'),
|
|
918
918
|
h('p', { style: { margin: 0, fontSize: 11.5, color: t.text3 } },
|
|
919
|
-
'
|
|
919
|
+
'登录在 DSH Host 上完成,手机同样可用。密码仅用于本次授权,不会被存储。')),
|
|
920
920
|
h('details', { style: style.details },
|
|
921
|
-
h('summary', { style: style.summary }, '
|
|
921
|
+
h('summary', { style: style.summary }, '改用浏览器授权'),
|
|
922
922
|
h('div', { style: { display: 'grid', gap: 8, paddingTop: 8 } },
|
|
923
|
-
h('button', { type: 'button', style: style.button, disabled: loading || !serverAddress, onClick: () => beginAuthorization('loopback') }, '
|
|
924
|
-
h('button', { type: 'button', style: style.button, disabled: loading || !serverAddress, onClick: () => beginAuthorization('scheme') }, '
|
|
925
|
-
h('p', { style: { margin: 0, fontSize: 12, color: t.text3 } }, '
|
|
926
|
-
accounts.length > 0 ? h('p', { style: { margin: 0, fontSize: 12, color: t.text3 } }, '
|
|
923
|
+
h('button', { type: 'button', style: style.button, disabled: loading || !serverAddress, onClick: () => beginAuthorization('loopback') }, '在浏览器中授权'),
|
|
924
|
+
h('button', { type: 'button', style: style.button, disabled: loading || !serverAddress, onClick: () => beginAuthorization('scheme') }, '授权(粘贴回调)'),
|
|
925
|
+
h('p', { style: { margin: 0, fontSize: 12, color: t.text3 } }, '浏览器授权需要回调能访问到本机 Host。手机上请改用上方账号登录。'))),
|
|
926
|
+
accounts.length > 0 ? h('p', { style: { margin: 0, fontSize: 12, color: t.text3 } }, '已连接的账号会出现在设备列表上方的账号选择器中。') : null))
|
|
927
927
|
: null,
|
|
928
928
|
currentAccount ? h('section', { style: style.card },
|
|
929
929
|
h('div', { style: style.row },
|
|
930
|
-
h('h2', { style: { ...style.sectionTitle, flex: 1 } }, '
|
|
930
|
+
h('h2', { style: { ...style.sectionTitle, flex: 1 } }, '远程会话'),
|
|
931
931
|
h('span', { style: { fontSize: 11, color: t.text3 } }, currentAccount.scopes.filter(s => s !== 'device:connect').join(' · '))),
|
|
932
932
|
h('div', { style: style.row },
|
|
933
|
-
h('select', { style: { ...style.input, flex: '1 1 200px' }, value: accountId, onChange: event => setAccountId(event.target.value), 'aria-label': '
|
|
933
|
+
h('select', { style: { ...style.input, flex: '1 1 200px' }, value: accountId, onChange: event => setAccountId(event.target.value), 'aria-label': '已授权账号' },
|
|
934
934
|
accounts.map(account => h('option', { key: account.accountId, value: account.accountId }, account.serverOrigin))),
|
|
935
|
-
h('button', { type: 'button', style: { ...style.button, flex: '0 0 auto', padding: '6px 10px', fontWeight: 700 }, disabled: loading || addingServer, onClick: () => { setAddingServer(true); setServerAddress(''); setAuthorizationRequestId(''); setAuthorizationUrl(''); setAuthorizationMode(''); setCallbackUrl(''); setStatus('
|
|
935
|
+
h('button', { type: 'button', style: { ...style.button, flex: '0 0 auto', padding: '6px 10px', fontWeight: 700 }, disabled: loading || addingServer, onClick: () => { setAddingServer(true); setServerAddress(''); setAuthorizationRequestId(''); setAuthorizationUrl(''); setAuthorizationMode(''); setCallbackUrl(''); setStatus('请输入 MonkeyAI 服务器地址') }, title: '授权另一个 MonkeyAI 服务器' }, '+ 添加服务器'),
|
|
936
936
|
h('select', { style: { ...style.input, flex: '1 1 220px' }, value: machineId, onChange: event => {
|
|
937
937
|
const next = event.target.value
|
|
938
938
|
setMachineId(next)
|
|
939
939
|
if (next) void loadSessions(next)
|
|
940
|
-
}, 'aria-label': '
|
|
941
|
-
h('option', { value: '' }, '
|
|
942
|
-
allDevices.map(device => h('option', { key: device.machineId, value: device.machineId }, `${device.displayName}${device.status === 'online' ? '' : ` · ${device.status}`}${device.stale ? ' ·
|
|
943
|
-
h('button', { type: 'button', style: style.button, disabled: loading, onClick: machineId ? () => loadSessions() : loadDevices, title: machineId ? '
|
|
944
|
-
h('button', { type: 'button', style: style.dangerButton, disabled: !accountId, onClick: disconnectAccount, title: '
|
|
945
|
-
!hasRead ? h('p', { style: { margin: 0, fontSize: 12, color: t.text3 } }, 'session:read
|
|
940
|
+
}, 'aria-label': '远程设备' },
|
|
941
|
+
h('option', { value: '' }, '选择设备'),
|
|
942
|
+
allDevices.map(device => h('option', { key: device.machineId, value: device.machineId }, `${device.displayName}${device.status === 'online' ? '' : ` · ${device.status === 'offline' ? '离线' : device.status}`}${device.stale ? ' · 已过期' : ''}`))),
|
|
943
|
+
h('button', { type: 'button', style: style.button, disabled: loading, onClick: machineId ? () => loadSessions() : loadDevices, title: machineId ? '刷新会话' : '刷新设备' }, '刷新'),
|
|
944
|
+
h('button', { type: 'button', style: style.dangerButton, disabled: !accountId, onClick: disconnectAccount, title: '撤销该账号授权' }, '断开连接')),
|
|
945
|
+
!hasRead ? h('p', { style: { margin: 0, fontSize: 12, color: t.text3 } }, '列出和读取远程会话需要 session:read 权限。') : null,
|
|
946
946
|
manualDevices.length > 0
|
|
947
947
|
? h('div', { style: style.row },
|
|
948
948
|
manualDevices.map(device => h('span', { key: device.machineId, style: style.chip },
|
|
949
949
|
device.displayName,
|
|
950
950
|
h('button', {
|
|
951
951
|
type: 'button', style: { border: 0, background: 'transparent', cursor: 'pointer', padding: 0, color: t.danger, fontWeight: 700, fontSize: 12 },
|
|
952
|
-
'aria-label':
|
|
952
|
+
'aria-label': `移除 ${device.displayName}`,
|
|
953
953
|
onClick: () => removeManualDevice(device.machineId),
|
|
954
954
|
}, '×'))))
|
|
955
955
|
: null,
|
|
956
956
|
h('details', { style: style.details },
|
|
957
|
-
h('summary', { style: style.summary }, '
|
|
957
|
+
h('summary', { style: style.summary }, '找不到设备?按 machine ID 添加'),
|
|
958
958
|
h('div', { style: { display: 'grid', gap: 8, paddingTop: 8 } },
|
|
959
959
|
h('div', { style: style.row },
|
|
960
960
|
h('input', {
|
|
961
961
|
style: { ...style.input, flex: '2 1 200px' },
|
|
962
962
|
value: manualDeviceId,
|
|
963
|
-
placeholder: 'Machine ID
|
|
963
|
+
placeholder: 'Machine ID,例如 6f1c2a34-... 或 headless-host-01',
|
|
964
964
|
onChange: event => setManualDeviceId(event.target.value),
|
|
965
965
|
}),
|
|
966
966
|
h('input', {
|
|
967
967
|
style: { ...style.input, flex: '1 1 140px' },
|
|
968
968
|
value: manualDeviceName,
|
|
969
|
-
placeholder: '
|
|
969
|
+
placeholder: '显示名称(可选)',
|
|
970
970
|
onChange: event => setManualDeviceName(event.target.value),
|
|
971
971
|
}),
|
|
972
|
-
h('button', { type: 'button', style: style.button, disabled: !manualDeviceId.trim(), onClick: addManualDevice }, '
|
|
972
|
+
h('button', { type: 'button', style: style.button, disabled: !manualDeviceId.trim(), onClick: addManualDevice }, '添加')))),
|
|
973
973
|
h('details', { style: style.details },
|
|
974
|
-
h('summary', { style: style.summary }, '
|
|
974
|
+
h('summary', { style: style.summary }, '新建远程会话'),
|
|
975
975
|
h('div', { style: { display: 'grid', gap: 8, paddingTop: 8 } },
|
|
976
976
|
h('div', { style: style.row },
|
|
977
977
|
h('input', {
|
|
978
978
|
style: { ...style.input, flex: '2 1 200px' },
|
|
979
979
|
value: newSessionCwd,
|
|
980
|
-
placeholder: '
|
|
980
|
+
placeholder: '工作目录,例如 /root',
|
|
981
981
|
onChange: event => setNewSessionCwd(event.target.value),
|
|
982
982
|
}),
|
|
983
983
|
h('select', {
|
|
@@ -985,16 +985,16 @@ function RemoteSessionPage({ api }) {
|
|
|
985
985
|
value: selectedModel,
|
|
986
986
|
disabled: models.length === 0,
|
|
987
987
|
onChange: event => setSelectedModel(event.target.value),
|
|
988
|
-
'aria-label': '
|
|
988
|
+
'aria-label': '新会话使用的模型',
|
|
989
989
|
}, models.map(m => h('option', { key: m.id, value: m.id }, m.displayName))),
|
|
990
990
|
h('button', {
|
|
991
991
|
type: 'button', style: style.primaryButton, flex: '0 0 auto',
|
|
992
992
|
disabled: loading || !machineId || !newSessionCwd.trim() || newSessionBusy,
|
|
993
993
|
onClick: createRemoteSession,
|
|
994
|
-
}, newSessionBusy ? '
|
|
994
|
+
}, newSessionBusy ? '创建中…' : '创建')))),
|
|
995
995
|
sessionOptions.length > 0
|
|
996
996
|
? h('div', { style: style.sessions }, sessionOptions.map(session => {
|
|
997
|
-
const flag = session.stale ? '
|
|
997
|
+
const flag = session.stale ? '已过期' : session.deviceStatus && session.deviceStatus !== 'online' ? (session.deviceStatus === 'offline' ? '离线' : session.deviceStatus) : ''
|
|
998
998
|
return h('button', {
|
|
999
999
|
style: { ...style.session, ...(session.id === sessionId ? style.selected : {}) },
|
|
1000
1000
|
className: 'mai-card-hover',
|
|
@@ -1002,26 +1002,26 @@ function RemoteSessionPage({ api }) {
|
|
|
1002
1002
|
type: 'button',
|
|
1003
1003
|
disabled: !session.cwd,
|
|
1004
1004
|
onClick: () => openSession(session),
|
|
1005
|
-
title: session.cwd ?
|
|
1005
|
+
title: session.cwd ? `打开 ${session.cwd}` : 'ACP 会话列表未提供工作目录',
|
|
1006
1006
|
},
|
|
1007
1007
|
h('span', { style: style.sessionTitle }, session.title || session.id),
|
|
1008
|
-
h('span', { style: style.sessionMeta }, session.cwd || '
|
|
1008
|
+
h('span', { style: style.sessionMeta }, session.cwd || '加载历史需要 runtime 提供工作目录'),
|
|
1009
1009
|
flag !== '' ? h('span', { style: { fontSize: 10.5, fontWeight: 650, color: session.stale ? t.text3 : '#8a6d1d', textTransform: 'uppercase', letterSpacing: '0.04em' } }, flag) : null)
|
|
1010
1010
|
}))
|
|
1011
|
-
: h('p', { style: style.subtitle }, machineId ? '
|
|
1011
|
+
: h('p', { style: style.subtitle }, machineId ? '该设备暂无会话。可在上方新建,或点击刷新。' : '请选择设备以列出其会话。'))
|
|
1012
1012
|
: null,
|
|
1013
1013
|
sessionId ? h('section', { style: style.card },
|
|
1014
1014
|
h('div', { style: style.row },
|
|
1015
1015
|
h('button', {
|
|
1016
|
-
type: 'button', style: { ...style.button, flexShrink: 0 }, title: '
|
|
1017
|
-
onClick: () => { streamRef.current?.dispose(); streamRef.current = null; setSessionId(''); setTurnActive(false); setStopping(false); setStatus('
|
|
1016
|
+
type: 'button', style: { ...style.button, flexShrink: 0 }, title: '关闭会话并返回列表',
|
|
1017
|
+
onClick: () => { streamRef.current?.dispose(); streamRef.current = null; setSessionId(''); setTurnActive(false); setStopping(false); setStatus('远程会话已关闭') },
|
|
1018
1018
|
}, '←'),
|
|
1019
1019
|
h('strong', { style: { flex: 1, fontSize: 13.5, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, title }, title),
|
|
1020
1020
|
h('select', {
|
|
1021
1021
|
style: { ...style.input, flex: '0 1 190px', minWidth: 130 },
|
|
1022
1022
|
value: currentModel,
|
|
1023
1023
|
disabled: !hasControl || switchingModel || models.length === 0,
|
|
1024
|
-
title: '
|
|
1024
|
+
title: '切换该会话的模型',
|
|
1025
1025
|
onChange: event => {
|
|
1026
1026
|
const next = event.target.value
|
|
1027
1027
|
if (next && next !== currentModel) switchSessionModel(next)
|
|
@@ -1041,17 +1041,17 @@ function RemoteSessionPage({ api }) {
|
|
|
1041
1041
|
} },
|
|
1042
1042
|
historyLoading
|
|
1043
1043
|
? h('div', { style: { fontSize: 12.5, color: t.text3, textAlign: 'center', padding: '32px 12px' } },
|
|
1044
|
-
displayEntries.length > 0 ?
|
|
1044
|
+
displayEntries.length > 0 ? `正在加载会话历史…(已加载 ${displayEntries.length} 条)` : '正在加载会话历史…')
|
|
1045
1045
|
: h(React.Fragment, null,
|
|
1046
1046
|
displayEntries.length > visibleCount
|
|
1047
|
-
? h('div', { style: { fontSize: 11, color: t.text3, textAlign: 'center', padding: '6px 0' } },
|
|
1047
|
+
? h('div', { style: { fontSize: 11, color: t.text3, textAlign: 'center', padding: '6px 0' } }, `还有 ${displayEntries.length - visibleCount} 条更早的消息 — 向上滚动加载`)
|
|
1048
1048
|
: null,
|
|
1049
1049
|
displayEntries.slice(-visibleCount).map(entry => {
|
|
1050
1050
|
if (entry.kind === 'thought') {
|
|
1051
1051
|
return h('div', { style: { display: 'flex', gap: 8, alignItems: 'flex-start', justifyContent: 'flex-start' }, key: entry.key },
|
|
1052
1052
|
h('div', { style: { ...style.avatar, background: '#8a5fd0' } }, '···'),
|
|
1053
1053
|
h('details', { style: style.thought },
|
|
1054
|
-
h('summary', { style: style.thoughtSummary }, '
|
|
1054
|
+
h('summary', { style: style.thoughtSummary }, '思考过程'),
|
|
1055
1055
|
h('p', { style: { margin: '6px 0 0', whiteSpace: 'pre-wrap', overflowWrap: 'anywhere', color: '#5b4a72', fontStyle: 'italic' } }, entry.text || '')))
|
|
1056
1056
|
}
|
|
1057
1057
|
if (entry.kind === 'message') {
|
|
@@ -1066,7 +1066,7 @@ function RemoteSessionPage({ api }) {
|
|
|
1066
1066
|
isUser
|
|
1067
1067
|
? h('p', { style: { margin: 0, whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' } }, entry.text || '')
|
|
1068
1068
|
: h('div', { className: 'mai-md' }, renderMarkdown(entry.text || ''))),
|
|
1069
|
-
isUser ? h('div', { style: { ...style.avatar, background: avatarColor } }, '
|
|
1069
|
+
isUser ? h('div', { style: { ...style.avatar, background: avatarColor } }, '我') : null)
|
|
1070
1070
|
}
|
|
1071
1071
|
if (entry.kind === 'tool') {
|
|
1072
1072
|
const statusLabel = entry.status ? (TOOL_STATUS_LABEL[entry.status] ?? entry.status) : 'running'
|
|
@@ -1075,15 +1075,15 @@ function RemoteSessionPage({ api }) {
|
|
|
1075
1075
|
const statusBg = entry.status === 'completed' ? '#e9f5ee' : entry.status === 'failed' ? t.dangerBg : '#fdf6e3'
|
|
1076
1076
|
return h('details', { style: { ...style.event, padding: '6px 11px', background: t.surface }, key: entry.key },
|
|
1077
1077
|
h('summary', { style: { ...style.row, cursor: 'pointer', userSelect: 'none', listStyle: 'none' } },
|
|
1078
|
-
h('span', { style: { fontSize: 10, fontWeight: 700, color: t.text3, textTransform: 'uppercase', letterSpacing: '0.05em', flexShrink: 0 } }, '
|
|
1079
|
-
h('span', { style: { fontSize: 12.5, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1, minWidth: 0 } }, entry.title || '
|
|
1078
|
+
h('span', { style: { fontSize: 10, fontWeight: 700, color: t.text3, textTransform: 'uppercase', letterSpacing: '0.05em', flexShrink: 0 } }, '工具'),
|
|
1079
|
+
h('span', { style: { fontSize: 12.5, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1, minWidth: 0 } }, entry.title || '执行中'),
|
|
1080
1080
|
running ? h('span', { className: 'mai-pulse', style: { ...style.dot, width: 6, height: 6, background: '#d4a72c' } }) : null,
|
|
1081
1081
|
h('span', { style: { fontSize: 10.5, padding: '1px 8px', borderRadius: 999, background: statusBg, color: statusColor, fontWeight: 650, flexShrink: 0 } }, statusLabel)),
|
|
1082
1082
|
entry.detail ? h('pre', { style: { margin: '6px 0 0', whiteSpace: 'pre-wrap', overflowWrap: 'anywhere', fontSize: 11.5, maxHeight: 320, overflowY: 'auto', background: t.surfaceAlt, borderRadius: 6, padding: '8px 10px' } }, entry.detail) : null)
|
|
1083
1083
|
}
|
|
1084
1084
|
if (entry.kind === 'plan') {
|
|
1085
1085
|
return h('article', { style: style.event, key: entry.key },
|
|
1086
|
-
h('div', { style: { fontSize: 10.5, fontWeight: 700, color: t.text3, marginBottom: 3, textTransform: 'uppercase', letterSpacing: '0.04em' } }, '
|
|
1086
|
+
h('div', { style: { fontSize: 10.5, fontWeight: 700, color: t.text3, marginBottom: 3, textTransform: 'uppercase', letterSpacing: '0.04em' } }, '计划'),
|
|
1087
1087
|
...entry.entries.map((item, index) => h('div', { style: { display: 'flex', gap: 6, fontSize: 12.5, margin: '2px 0' }, key: index },
|
|
1088
1088
|
h('span', { style: { opacity: 0.55 } }, index + 1 + '.'),
|
|
1089
1089
|
h('span', { style: { textDecoration: item.status === 'completed' ? 'line-through' : 'none', opacity: item.status === 'completed' ? 0.55 : 1 } },
|
|
@@ -1095,7 +1095,7 @@ function RemoteSessionPage({ api }) {
|
|
|
1095
1095
|
return h('article', { style: style.event, key: entry.key },
|
|
1096
1096
|
h('div', { style: { opacity: 0.6, fontSize: 11, marginBottom: 4 } }, entry.label),
|
|
1097
1097
|
h('details', null,
|
|
1098
|
-
h('summary', { style: style.summary }, '
|
|
1098
|
+
h('summary', { style: style.summary }, '查看详情'),
|
|
1099
1099
|
h('pre', { style: { margin: '5px 0 0', whiteSpace: 'pre-wrap', overflowWrap: 'anywhere', fontSize: 11.5 } }, JSON.stringify(entry.data ?? {}, null, 2))))
|
|
1100
1100
|
})
|
|
1101
1101
|
)
|
|
@@ -1115,26 +1115,26 @@ function RemoteSessionPage({ api }) {
|
|
|
1115
1115
|
sendPrompt()
|
|
1116
1116
|
}
|
|
1117
1117
|
},
|
|
1118
|
-
placeholder: hasControl ? '
|
|
1118
|
+
placeholder: hasControl ? '向远程会话下发指令(回车发送,Shift+回车换行)' : '只读模式:下发指令需要 session:control 权限',
|
|
1119
1119
|
disabled: !hasControl,
|
|
1120
1120
|
}),
|
|
1121
1121
|
turnActive
|
|
1122
1122
|
? h('button', {
|
|
1123
1123
|
type: 'button', style: style.stopButton, disabled: !hasControl || stopping,
|
|
1124
|
-
onClick: stopTurn, title: stopping ? '
|
|
1125
|
-
'aria-label': '
|
|
1124
|
+
onClick: stopTurn, title: stopping ? '停止中…' : '停止当前回合',
|
|
1125
|
+
'aria-label': '停止当前回合',
|
|
1126
1126
|
},
|
|
1127
1127
|
h('span', { style: style.stopIcon }))
|
|
1128
1128
|
: h('button', {
|
|
1129
1129
|
type: 'submit', style: style.sendButton,
|
|
1130
1130
|
disabled: !streamRef.current || !hasControl || prompt.trim() === '',
|
|
1131
|
-
title: '
|
|
1131
|
+
title: '发送(回车)', 'aria-label': '发送指令',
|
|
1132
1132
|
},
|
|
1133
1133
|
h('svg', { width: 16, height: 16, viewBox: '0 0 16 16', fill: 'none', 'aria-hidden': 'true', style: { display: 'block' } },
|
|
1134
1134
|
h('path', { d: 'M8 12.5V3.5M8 3.5L4 7.5M8 3.5L12 7.5', stroke: 'currentColor', strokeWidth: 1.8, strokeLinecap: 'round', strokeLinejoin: 'round' })))),
|
|
1135
1135
|
lastDelivery || turnActive ? h('div', { style: { fontSize: 11.5, color: turnActive ? t.accent : lastDelivery?.accepted ? t.success : lastDelivery?.frame === 'pending' ? t.text3 : t.danger, textAlign: 'right', display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: 6 } },
|
|
1136
1136
|
turnActive ? h('span', { className: 'mai-pulse', style: { ...style.dot, background: t.accent } }) : null,
|
|
1137
|
-
turnActive ? (stopping ? '
|
|
1137
|
+
turnActive ? (stopping ? '正在停止当前回合…' : 'MonkeyAI 正在处理…') : lastDelivery?.accepted ? '回合已结束' : lastDelivery?.frame === 'pending' ? '等待投递' : '投递被拒绝') : null)
|
|
1138
1138
|
: null)
|
|
1139
1139
|
}
|
|
1140
1140
|
|
|
@@ -1166,8 +1166,8 @@ export async function apply(ctx) {
|
|
|
1166
1166
|
type: 'button',
|
|
1167
1167
|
style: { width: '100%', height: '100%', padding: 0, border: 0, background: 'transparent', color: 'inherit', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' },
|
|
1168
1168
|
onClick: () => owner.layout.selectPanel(PANEL_ID),
|
|
1169
|
-
'aria-label': 'MonkeyAI
|
|
1170
|
-
title: 'MonkeyAI
|
|
1169
|
+
'aria-label': 'MonkeyAI 会话',
|
|
1170
|
+
title: 'MonkeyAI 会话',
|
|
1171
1171
|
}, h(MonkeyIcon, { size: 18 }))
|
|
1172
1172
|
owner.slots.inject('main', () => owner.slots.register({
|
|
1173
1173
|
name: 'main', key: PANEL_ID, inject: () => ({ api }),
|