@ljwei-stak/dsh-model-route-bot 0.66.4 → 0.67.0
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/.dsh-plugin/bot-product.mjs +81 -5
- package/.dsh-plugin/client.js +17490 -1791
- package/README.i18n.yaml +4 -4
- package/README.md +409 -364
- package/README.zh.md +409 -364
- package/apps/integration-client/README.md +31 -7
- package/apps/integration-client/api.mjs +14 -3
- package/apps/integration-client/binding-editor.jsx +308 -109
- package/apps/integration-client/binding-model.mjs +262 -0
- package/apps/integration-client/binding.css +144 -0
- package/apps/integration-client/bot-model-status.mjs +18 -0
- package/apps/integration-client/bot-profile-identity.jsx +197 -0
- package/apps/integration-client/bot-profile.css +57 -0
- package/apps/integration-client/bot-profile.mjs +97 -0
- package/apps/integration-client/bot-settings-dialog.css +18 -0
- package/apps/integration-client/bot-settings-dialog.jsx +111 -0
- package/apps/integration-client/bot-thread-header-bridge.css +20 -0
- package/apps/integration-client/bot-thread-header.jsx +278 -0
- package/apps/integration-client/bot-threads-panel.jsx +378 -0
- package/apps/integration-client/bot-threads.css +169 -0
- package/apps/integration-client/chat-design.mjs +250 -0
- package/apps/integration-client/chat-ui.jsx +175 -0
- package/apps/integration-client/chat.css +477 -0
- package/apps/integration-client/composer-draft-binding.mjs +50 -0
- package/apps/integration-client/composer-draft-bound.jsx +14 -0
- package/apps/integration-client/composer-draft-host.jsx +142 -0
- package/apps/integration-client/composer-draft-session.jsx +287 -0
- package/apps/integration-client/composer-draft-witness.mjs +115 -0
- package/apps/integration-client/composer-draft.css +4 -0
- package/apps/integration-client/composer-draft.mjs +322 -0
- package/apps/integration-client/contact-attention.jsx +54 -0
- package/apps/integration-client/data-panel.jsx +439 -0
- package/apps/integration-client/data.css +69 -0
- package/apps/integration-client/desktop-bridge.mjs +1 -0
- package/apps/integration-client/desktop-http-socket.mjs +49 -0
- package/apps/integration-client/desktop-keyboard.mjs +32 -0
- package/apps/integration-client/desktop-pane.jsx +99 -30
- package/apps/integration-client/desktop-stream.mjs +13 -3
- package/apps/integration-client/desktop-tools.jsx +19 -0
- package/apps/integration-client/desktop-touch.mjs +27 -0
- package/apps/integration-client/desktop.css +41 -0
- package/apps/integration-client/independent-chat-controller.mjs +224 -0
- package/apps/integration-client/independent-chat-entry.jsx +61 -0
- package/apps/integration-client/independent-chat-navigation.mjs +21 -0
- package/apps/integration-client/independent-chat-page.jsx +148 -0
- package/apps/integration-client/independent-chat-rich.jsx +64 -0
- package/apps/integration-client/independent-chat-rich.mjs +30 -0
- package/apps/integration-client/independent-chat.css +73 -0
- package/apps/integration-client/index.jsx +24 -16
- package/apps/integration-client/library-panel.jsx +4 -1
- package/apps/integration-client/licenses/jsqr-1.4.0-LICENSE.txt +202 -0
- package/apps/integration-client/main-route-editor.jsx +94 -0
- package/apps/integration-client/main-route-model.mjs +100 -0
- package/apps/integration-client/model-picker.css +37 -0
- package/apps/integration-client/model-picker.jsx +83 -0
- package/apps/integration-client/native-activity.css +141 -0
- package/apps/integration-client/native-activity.jsx +276 -0
- package/apps/integration-client/native-activity.mjs +384 -0
- package/apps/integration-client/notifications-panel.jsx +186 -0
- package/apps/integration-client/notifications.css +126 -0
- package/apps/integration-client/pairing-qr.jsx +79 -0
- package/apps/integration-client/panels.jsx +126 -115
- package/apps/integration-client/product-slots.jsx +46 -0
- package/apps/integration-client/routine-recorder.mjs +23 -2
- package/apps/integration-client/secure-pairing.jsx +84 -0
- package/apps/integration-client/tasks-panel.jsx +32 -10
- package/apps/integration-client/ui-icons.jsx +17 -0
- package/apps/integration-client/usage-explorer.jsx +43 -0
- package/apps/integration-client/usage-report.mjs +71 -0
- package/apps/integration-client/usage.css +34 -0
- package/apps/integration-client/web-panel-model.mjs +140 -0
- package/apps/integration-client/web-panel-page.jsx +221 -0
- package/apps/integration-client/web-panel-pins-live.jsx +28 -0
- package/apps/integration-client/web-panel.css +139 -0
- package/apps/integration-client/workspace-model.mjs +67 -0
- package/apps/integration-client/workspace-navigation.mjs +56 -0
- package/apps/integration-client/workspace-ui.jsx +173 -0
- package/apps/integration-client/workspace.css +332 -0
- package/modules/bot-runtime/SOURCE.json +176 -50
- package/modules/bot-runtime/index.mjs +169 -13
- package/modules/bot-runtime/product-overlay/OVERLAY.json +128 -2
- package/modules/bot-runtime/product-overlay/base/src/client/header.js +244 -0
- package/modules/bot-runtime/product-overlay/base/src/client/learning-card.js +202 -0
- package/modules/bot-runtime/product-overlay/base/src/client/model-menu.js +371 -0
- package/modules/bot-runtime/product-overlay/base/src/client/secret-card.js +363 -0
- package/modules/bot-runtime/product-overlay/base/src/client/sources.js +68 -0
- package/modules/bot-runtime/product-overlay/base/src/client/turn-error.js +260 -0
- package/modules/bot-runtime/product-overlay/base/src/host/channel.js +647 -0
- package/modules/bot-runtime/product-overlay/base/src/host/memory.js +2050 -0
- package/modules/bot-runtime/product-overlay/base/src/host/models.js +314 -0
- package/modules/bot-runtime/product-overlay/base/src/host/parts.js +1473 -0
- package/modules/bot-runtime/product-overlay/base/src/host/privacy.js +368 -0
- package/modules/bot-runtime/product-overlay/base/src/host/roster.js +776 -0
- package/modules/bot-runtime/product-overlay/base/src/host/store.js +512 -0
- package/modules/bot-runtime/product-overlay/base/src/host/usage.js +653 -0
- package/modules/bot-runtime/product-overlay/patches/src/client/details-drawer.js.patch +48 -2
- package/modules/bot-runtime/product-overlay/patches/src/client/header.js.patch +41 -0
- package/modules/bot-runtime/product-overlay/patches/src/client/index.js.patch +33 -1
- package/modules/bot-runtime/product-overlay/patches/src/client/learning-card.js.patch +20 -0
- package/modules/bot-runtime/product-overlay/patches/src/client/model-menu.js.patch +24 -0
- package/modules/bot-runtime/product-overlay/patches/src/client/secret-card.js.patch +25 -0
- package/modules/bot-runtime/product-overlay/patches/src/client/sources.js.patch +52 -0
- package/modules/bot-runtime/product-overlay/patches/src/client/turn-error.js.patch +23 -0
- package/modules/bot-runtime/product-overlay/patches/src/host/channel.js.patch +46 -0
- package/modules/bot-runtime/product-overlay/patches/src/host/memory.js.patch +11 -0
- package/modules/bot-runtime/product-overlay/patches/src/host/models.js.patch +16 -0
- package/modules/bot-runtime/product-overlay/patches/src/host/parts.js.patch +95 -0
- package/modules/bot-runtime/product-overlay/patches/src/host/privacy.js.patch +11 -0
- package/modules/bot-runtime/product-overlay/patches/src/host/roster.js.patch +145 -0
- package/modules/bot-runtime/product-overlay/patches/src/host/store.js.patch +68 -0
- package/modules/bot-runtime/product-overlay/patches/src/host/usage.js.patch +11 -0
- package/modules/bot-runtime/read-model.mjs +19 -4
- package/modules/bot-runtime/vendor/ds-bot/src/client/details-drawer.js +15 -5
- package/modules/bot-runtime/vendor/ds-bot/src/client/header.js +9 -2
- package/modules/bot-runtime/vendor/ds-bot/src/client/index.js +9 -2
- package/modules/bot-runtime/vendor/ds-bot/src/client/learning-card.js +2 -2
- package/modules/bot-runtime/vendor/ds-bot/src/client/model-menu.js +5 -3
- package/modules/bot-runtime/vendor/ds-bot/src/client/secret-card.js +3 -3
- package/modules/bot-runtime/vendor/ds-bot/src/client/sources.js +25 -2
- package/modules/bot-runtime/vendor/ds-bot/src/client/turn-error.js +3 -2
- package/modules/bot-runtime/vendor/ds-bot/src/host/channel.js +14 -2
- package/modules/bot-runtime/vendor/ds-bot/src/host/memory.js +1 -1
- package/modules/bot-runtime/vendor/ds-bot/src/host/models.js +7 -0
- package/modules/bot-runtime/vendor/ds-bot/src/host/parts.js +26 -9
- package/modules/bot-runtime/vendor/ds-bot/src/host/privacy.js +1 -1
- package/modules/bot-runtime/vendor/ds-bot/src/host/roster.js +112 -3
- package/modules/bot-runtime/vendor/ds-bot/src/host/store.js +34 -1
- package/modules/bot-runtime/vendor/ds-bot/src/host/usage.js +1 -1
- package/modules/independent-runtime/index.mjs +152 -62
- package/modules/independent-runtime/remote-tools.mjs +8 -5
- package/modules/openmaus-protocol/NOTICE +10 -0
- package/modules/openmaus-protocol/README.md +15 -0
- package/modules/openmaus-protocol/SOURCE.json +68 -0
- package/modules/openmaus-protocol/dist/protocol.mjs +1203 -0
- package/modules/openmaus-protocol/index.mjs +1 -0
- package/modules/openmaus-protocol/transport.mjs +77 -0
- package/modules/openmaus-protocol/vendor/LICENSE +202 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/README.md +405 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/credentials.ts +128 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/descriptor.ts +84 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/noise.ts +456 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/phone.ts +104 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/session.ts +417 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/vectors/v1.json +2565 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/vectors/v1.sha256 +1 -0
- package/modules/openmaus-protocol/vendor/companion/src/e2e/yamux.ts +335 -0
- package/package.json +18 -6
- package/packages/bot-acp/README.md +156 -0
- package/packages/bot-acp/licenses/LICENSE-OpenMausBot +202 -0
- package/packages/bot-acp/licenses/NOTICE.md +27 -0
- package/packages/bot-acp/src/acp-adapter.mjs +795 -0
- package/packages/bot-acp/src/acp-host.mjs +148 -0
- package/packages/bot-acp/src/native-launch.mjs +121 -0
- package/packages/bot-acp/src/permission-policy.mjs +283 -0
- package/packages/bot-acp/src/rpc-codec.mjs +265 -0
- package/packages/bot-acp/src/session-core.mjs +581 -0
- package/packages/bot-acp/src/stdio-rpc.mjs +329 -0
- package/packages/bot-engines/README.md +281 -0
- package/packages/bot-engines/licenses/LICENSE-OpenMausBot +202 -0
- package/packages/bot-engines/licenses/NOTICE.md +32 -0
- package/packages/bot-engines/src/claude-engine.mjs +772 -0
- package/packages/bot-engines/src/claude-llm-adapter.mjs +797 -0
- package/packages/bot-engines/src/claude-llm-turn.mjs +292 -0
- package/packages/bot-engines/src/claude-permission-broker.mjs +130 -0
- package/packages/bot-engines/src/claude-permission-proxy.mjs +87 -0
- package/packages/bot-engines/src/claude-protocol.mjs +177 -0
- package/packages/bot-engines/src/driver-catalog.mjs +334 -0
- package/packages/bot-engines/src/host-loop-request.mjs +95 -0
- package/packages/bot-engines/src/host-registrations.mjs +105 -0
- package/packages/capability-broker/src/cli-bootstrap.mjs +86 -16
- package/packages/capability-broker/src/cli-bridge-response.mjs +80 -0
- package/packages/capability-broker/src/cli-stdio-bridge.mjs +21 -8
- package/packages/cli-runtime/src/codex-session-core.mjs +4 -0
- package/packages/cli-runtime/src/native-cli-output.mjs +57 -0
- package/packages/cli-runtime/src/native-peer.ps1 +51 -3
- package/packages/cli-runtime/src/native-process.mjs +77 -14
- package/packages/model-router-plugin/package.json +1 -1
- package/packages/orchestration/src/binding-product.mjs +19 -0
- package/packages/orchestration/src/bot-data-actions.mjs +45 -0
- package/packages/orchestration/src/bot-data-chart.mjs +109 -0
- package/packages/orchestration/src/bot-data-context.mjs +44 -0
- package/packages/orchestration/src/bot-data-engine.mjs +809 -0
- package/packages/orchestration/src/bot-data-io.mjs +186 -0
- package/packages/orchestration/src/bot-data-sql.mjs +195 -0
- package/packages/orchestration/src/bot-data-surface.mjs +144 -0
- package/packages/orchestration/src/bot-data-tools.mjs +68 -0
- package/packages/orchestration/src/bot-data-worker.mjs +228 -0
- package/packages/orchestration/src/bot-independent-execution.mjs +195 -0
- package/packages/orchestration/src/bot-independent-stream.mjs +74 -0
- package/packages/orchestration/src/bot-loop.mjs +152 -19
- package/packages/orchestration/src/bot-notifications.mjs +159 -0
- package/packages/orchestration/src/bot-policy.mjs +43 -4
- package/packages/orchestration/src/bot-profile.mjs +171 -0
- package/packages/orchestration/src/bot-routine.mjs +4 -4
- package/packages/orchestration/src/bot-runtime-assembly.mjs +255 -8
- package/packages/orchestration/src/bot-runtime-switch.mjs +13 -2
- package/packages/orchestration/src/bot-serving.mjs +164 -0
- package/packages/orchestration/src/bot-session-store.mjs +105 -9
- package/packages/orchestration/src/bot-settings.mjs +50 -11
- package/packages/orchestration/src/bot-thread-admission.mjs +84 -0
- package/packages/orchestration/src/bot-thread-host.mjs +187 -0
- package/packages/orchestration/src/bot-threads.mjs +790 -0
- package/packages/orchestration/src/data-product-access.mjs +37 -0
- package/packages/orchestration/src/data-tool-contract.mjs +291 -0
- package/packages/orchestration/src/desktop-http-carrier.mjs +120 -0
- package/packages/orchestration/src/desktop-proxy.mjs +311 -0
- package/packages/orchestration/src/desktop-sessions.mjs +43 -9
- package/packages/orchestration/src/ds-lifecycle.mjs +31 -4
- package/packages/orchestration/src/grant-refresh-queue.mjs +17 -0
- package/packages/orchestration/src/independent-chat-page.mjs +39 -0
- package/packages/orchestration/src/independent-chat-service.mjs +308 -0
- package/packages/orchestration/src/independent-context-state.mjs +44 -0
- package/packages/orchestration/src/independent-message-wire.mjs +84 -0
- package/packages/orchestration/src/independent-tool-catalog.mjs +175 -0
- package/packages/orchestration/src/independent-tool-families.mjs +73 -0
- package/packages/orchestration/src/independent-tool-host.mjs +344 -0
- package/packages/orchestration/src/main-bot-grant.mjs +15 -0
- package/packages/orchestration/src/main-model-routes.mjs +208 -0
- package/packages/orchestration/src/model-accounting.mjs +99 -35
- package/packages/orchestration/src/model-auxiliary-dispatch.mjs +187 -0
- package/packages/orchestration/src/model-loop-frame.mjs +179 -0
- package/packages/orchestration/src/model-prepared-generation.mjs +175 -0
- package/packages/orchestration/src/model-request-bounds.mjs +111 -0
- package/packages/orchestration/src/native-activity-contract.mjs +491 -0
- package/packages/orchestration/src/native-activity-service.mjs +102 -0
- package/packages/orchestration/src/native-activity-store.mjs +249 -0
- package/packages/orchestration/src/native-conversation-tools.mjs +78 -0
- package/packages/orchestration/src/native-data-tools.mjs +100 -0
- package/packages/orchestration/src/native-remote-actions.mjs +742 -0
- package/packages/orchestration/src/native-team-tools.mjs +73 -0
- package/packages/orchestration/src/pairing-invite.mjs +37 -0
- package/packages/orchestration/src/product-composition.mjs +1559 -1001
- package/packages/orchestration/src/remote-machines.mjs +82 -14
- package/packages/orchestration/src/routine-execution.mjs +5 -3
- package/packages/orchestration/src/routine-session.mjs +19 -13
- package/packages/orchestration/src/secure-pairing-host.mjs +112 -0
- package/packages/orchestration/src/task-journal.mjs +29 -0
- package/packages/orchestration/src/task-product.mjs +17 -0
- package/packages/orchestration/src/team-assignment-store.mjs +120 -0
- package/packages/orchestration/src/team-bridge.mjs +282 -0
- package/packages/orchestration/src/team-scheduler.mjs +83 -0
- package/packages/orchestration/src/team-sdk-tools.mjs +84 -0
- package/packages/orchestration/src/team-tool-contract.mjs +284 -0
- package/packages/orchestration/src/ui-host-namespace.mjs +23 -0
- package/packages/orchestration/src/web-panel-actions.mjs +103 -0
- package/packages/orchestration/src/web-panel-framing.mjs +227 -0
- package/packages/orchestration/src/web-panel-store.mjs +206 -0
- package/remote-computer-agent/README.md +39 -29
- package/remote-computer-agent/build.mjs +3 -0
- package/remote-computer-agent/dist/OPENMAUSBOT_LICENSE +202 -0
- package/remote-computer-agent/dist/OPENMAUSBOT_NOTICE +10 -0
- package/remote-computer-agent/dist/OPENMAUSBOT_SOURCE.json +68 -0
- package/remote-computer-agent/dist/cli-stdio-bridge.mjs +215 -25
- package/remote-computer-agent/dist/cli-stdio-bridge.mjs.map +4 -4
- package/remote-computer-agent/dist/cli.mjs +1688 -168
- package/remote-computer-agent/dist/cli.mjs.map +4 -4
- package/remote-computer-agent/dist/index.mjs +1579 -122
- package/remote-computer-agent/dist/index.mjs.map +4 -4
- package/remote-computer-agent/dist/native-peer.ps1 +51 -3
- package/remote-computer-agent/package-lock.json +308 -2
- package/remote-computer-agent/package.json +2 -1
- package/remote-computer-agent/pair-device.ps1 +20 -0
- package/remote-computer-agent/setup.ps1 +38 -16
- package/remote-computer-agent/src/cli.ts +24 -3
- package/remote-computer-agent/src/desktop-session.ts +11 -3
- package/remote-computer-agent/src/e2e-server.ts +81 -0
- package/remote-computer-agent/src/pairing-page.ts +35 -0
- package/remote-computer-agent/src/protocol.ts +25 -25
- package/remote-computer-agent/src/secure-pairing.ts +105 -0
- package/remote-computer-agent/src/store.ts +20 -5
- package/remote-computer-agent/src/transport.ts +39 -15
|
@@ -1,14 +1,25 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react'
|
|
1
|
+
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react'
|
|
2
2
|
import { PRODUCT_PANELS, TASK_STATES, modelName, money } from './api.mjs'
|
|
3
3
|
import { TaskWorkspace } from './tasks-panel.jsx'
|
|
4
4
|
import { normalizeEndpoint, parsePairingPaste, secretNameFor } from './pairing.mjs'
|
|
5
5
|
import { framePoint } from '../../packages/orchestration/src/desktop-contract.mjs'
|
|
6
6
|
import { BotBindingEditor } from './binding-editor.jsx'
|
|
7
7
|
import { LibraryPanel } from './library-panel.jsx'
|
|
8
|
+
import { SecurePairingSetup } from './secure-pairing.jsx'
|
|
9
|
+
import { UsageExplorer } from './usage-explorer.jsx'
|
|
8
10
|
import { DesktopWorkbench } from './desktop-pane.jsx'
|
|
9
11
|
import { RoutinesPanel } from './routines-panel.jsx'
|
|
10
12
|
import { UiIcon } from './ui-icons.jsx'
|
|
13
|
+
import { NotificationsPanel } from './notifications-panel.jsx'
|
|
14
|
+
import { DataPanel } from './data-panel.jsx'
|
|
15
|
+
import { BotThreadsPanel } from './bot-threads-panel.jsx'
|
|
16
|
+
import { WebPanelsPanel } from './web-panel-page.jsx'
|
|
17
|
+
import { BotConversationLink } from './independent-chat-entry.jsx'
|
|
18
|
+
import { openIndependentChat } from './independent-chat-navigation.mjs'
|
|
19
|
+
import { requestWorkbenchView } from '../../.dsh-plugin/client/workbench-state.mjs'
|
|
11
20
|
import { routingSubmissions, submissionCode } from './routing-submissions.mjs'
|
|
21
|
+
import { WORKSPACE_SECTIONS, workspaceNavigation } from './workspace-navigation.mjs'
|
|
22
|
+
import { TeamDirectory, WorkspaceCommandPalette, WorkspaceHome, WorkspaceInbox, WorkspaceToolbar } from './workspace-ui.jsx'
|
|
12
23
|
|
|
13
24
|
const NAV_EVENT = 'bot-product:navigation'
|
|
14
25
|
let activePanel = null
|
|
@@ -18,7 +29,7 @@ const announcePanel = (id, clear = false) => {
|
|
|
18
29
|
document.dispatchEvent(new document.defaultView.CustomEvent(NAV_EVENT, { detail: { id: activePanel } }))
|
|
19
30
|
}
|
|
20
31
|
|
|
21
|
-
export function ProductNavigation({ openPanel, panels, compact = false }) {
|
|
32
|
+
export function ProductNavigation({ openPanel, panels, compact = false, onSearch }) {
|
|
22
33
|
const [active, setActive] = useState(() => activePanel)
|
|
23
34
|
useEffect(() => {
|
|
24
35
|
const change = event => setActive(event.detail.id)
|
|
@@ -27,15 +38,11 @@ export function ProductNavigation({ openPanel, panels, compact = false }) {
|
|
|
27
38
|
return () => document.removeEventListener(NAV_EVENT, change)
|
|
28
39
|
}, [])
|
|
29
40
|
return <nav className={`bp-nav${compact ? ' bp-nav-compact' : ''}`} aria-label="Bot 工作系统">
|
|
30
|
-
<
|
|
31
|
-
{[
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
onClick={() => { announcePanel(id); openPanel(id) }} title={label} aria-label={label}>
|
|
36
|
-
{/* A narrow column still says what each icon opens, so the row is readable without hovering. */}
|
|
37
|
-
<UiIcon name={icon} size={17} /><span className="bp-nav-name">{label}</span><span className="bp-nav-caption">{caption}</span>
|
|
38
|
-
</button>)}
|
|
41
|
+
<button type="button" className="bp-nav-search" aria-label="搜索工作空间" onClick={() => onSearch ? onSearch() : openPanel(panels.home)} title="搜索工作空间"><UiIcon name="search" size={17} /><span className="bp-nav-name">搜索工作空间</span><span className="bp-nav-caption">查找</span></button>
|
|
42
|
+
{['工作', '资源', '管理'].map(group => <div className="bp-nav-group" key={group}><p className="bp-nav-label">{group}</p>{WORKSPACE_SECTIONS.filter(row => row.group === group).map(({ section, label, icon }) => <button key={section} type="button" aria-current={active === panels[section] ? 'page' : undefined}
|
|
43
|
+
onClick={() => { announcePanel(panels[section]); openPanel(panels[section]) }} title={label} aria-label={label}>
|
|
44
|
+
<UiIcon name={icon} size={17} /><span className="bp-nav-name">{label}</span><span className="bp-nav-caption">{label.slice(0, 2)}</span>
|
|
45
|
+
</button>)}{group === '管理' && <button type="button" aria-current={active === 'model-router-galgame' ? 'page' : undefined} onClick={() => { announcePanel('model-router-galgame'); openPanel('model-router-galgame') }} title="高级工作台" aria-label="高级工作台"><UiIcon name="settings" size={17} /><span className="bp-nav-name">高级工作台</span><span className="bp-nav-caption">路由</span></button>}</div>)}
|
|
39
46
|
</nav>
|
|
40
47
|
}
|
|
41
48
|
|
|
@@ -54,85 +61,43 @@ export
|
|
|
54
61
|
* 运行时: which path serves this Bot. Only rendered when the Host exposes the switch, so an older
|
|
55
62
|
* Host shows nothing instead of a dead control.
|
|
56
63
|
*/
|
|
57
|
-
function BotRuntimeRow({ botId, dispatch, busy }) {
|
|
58
|
-
const [state, setState] = React.useState(null)
|
|
59
|
-
const
|
|
60
|
-
const [working, setWorking] = React.useState(false)
|
|
64
|
+
function BotRuntimeRow({ botId, dispatch, busy, onOpenChat }) {
|
|
65
|
+
const [state, setState] = React.useState(null), [error, setError] = React.useState(''), [working, setWorking] = React.useState(false)
|
|
66
|
+
const generation = React.useRef(0)
|
|
61
67
|
const load = React.useCallback(async () => {
|
|
62
|
-
|
|
68
|
+
const mine = ++generation.current
|
|
63
69
|
try {
|
|
64
70
|
const rows = await dispatch('botRuntimeStatus', {})
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
setState(null)
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
}, [botId, dispatch])
|
|
74
|
-
const [history, setHistory] = React.useState([])
|
|
75
|
-
const [historyError, setHistoryError] = React.useState('')
|
|
76
|
-
const loadHistory = React.useCallback(async () => {
|
|
77
|
-
if (typeof dispatch !== 'function') return
|
|
78
|
-
try {
|
|
79
|
-
const rows = await dispatch('botRuntimeHistory', { botId, limit: 20 })
|
|
80
|
-
setHistory(Array.isArray(rows) ? rows : [])
|
|
81
|
-
setHistoryError('')
|
|
82
|
-
} catch (failure) {
|
|
83
|
-
setHistory([])
|
|
84
|
-
setHistoryError(String(failure?.message ?? failure))
|
|
85
|
-
}
|
|
86
|
-
}, [botId, dispatch])
|
|
87
|
-
const [full, setFull] = React.useState(null)
|
|
88
|
-
const openFull = React.useCallback(async index => {
|
|
89
|
-
try {
|
|
90
|
-
const message = await dispatch('botRuntimeMessage', { botId, index })
|
|
91
|
-
setFull({ index, ...message })
|
|
92
|
-
setHistoryError('')
|
|
93
|
-
} catch (failure) {
|
|
94
|
-
setHistoryError(String(failure?.message ?? failure))
|
|
95
|
-
setFull(null)
|
|
96
|
-
}
|
|
71
|
+
if (mine !== generation.current) return
|
|
72
|
+
if (!Array.isArray(rows)) throw new Error('宿主未确认运行时状态。')
|
|
73
|
+
const row = rows.find(item => item.botId === botId)
|
|
74
|
+
const mode = row ? row.mode : 'integration'
|
|
75
|
+
if (!['integration', 'independent'].includes(mode)) throw new Error('未知运行时,已暂停操作。')
|
|
76
|
+
setState({ runtime: mode, changedAt: row?.changedAt ?? null }); setError('')
|
|
77
|
+
} catch (failure) { if (mine === generation.current) { setState(null); setError(String(failure?.message ?? failure)) } }
|
|
97
78
|
}, [botId, dispatch])
|
|
98
|
-
React.useEffect(() => { void load() }, [load])
|
|
99
|
-
if (!state && !error) return null
|
|
79
|
+
React.useEffect(() => { void load(); return () => { generation.current++ } }, [load])
|
|
100
80
|
const target = state?.runtime === 'independent' ? 'integration' : 'independent'
|
|
101
81
|
const move = async () => {
|
|
102
82
|
setWorking(true)
|
|
103
|
-
try {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
} catch (failure) {
|
|
107
|
-
setError(String(failure?.message ?? failure))
|
|
108
|
-
} finally {
|
|
109
|
-
setWorking(false)
|
|
110
|
-
}
|
|
83
|
+
try { await dispatch('botRuntimeMove', { botId, mode: target }); await load() }
|
|
84
|
+
catch (failure) { setError(String(failure?.message ?? failure)) }
|
|
85
|
+
finally { setWorking(false) }
|
|
111
86
|
}
|
|
112
87
|
return <div className="bp-runtime">
|
|
113
|
-
<dl className="bp-facts"><Value label="运行时">{state
|
|
114
|
-
|
|
115
|
-
<
|
|
116
|
-
<p className="bp-muted">迁移是原子的:Bot 正在对话时会被拒绝。搬过去后该 Bot 的会话写进独立会话库,界面与工具沿用现有授权。</p>
|
|
88
|
+
<dl className="bp-facts"><Value label="运行时">{state ? state.runtime === 'independent' ? '独立循环 + 独立会话库' : 'Harness 会话集成' : '等待宿主确认'}</Value><Value label="切换时间">{state?.changedAt ? new Date(state.changedAt).toLocaleString('zh-CN') : '从未切换'}</Value></dl>
|
|
89
|
+
<div className="bp-actions"><button type="button" disabled={!state || busy || working} onClick={() => { void move() }}>{working ? '切换中…' : target === 'independent' ? '迁移到独立运行时' : '迁回 Harness 会话'}</button>{state?.runtime === 'independent' && <button type="button" onClick={() => onOpenChat?.(botId)}>打开独立聊天</button>}</div>
|
|
90
|
+
<p className="bp-muted">Bot 正在对话时不能迁移。独立运行时请打开独立聊天继续发送,完整消息按稳定编号分页读取。迁回后可从“打开私聊”回到此 Bot 的真实原生会话。</p>
|
|
117
91
|
{error && <p className="bp-error" role="alert">{error}</p>}
|
|
118
|
-
{state ? <details className="bp-log"><summary>最近会话(仅预览)</summary>
|
|
119
|
-
<button type="button" disabled={working} onClick={() => { void loadHistory() }}>读取预览</button>
|
|
120
|
-
{historyError && <p className="bp-error" role="alert">{historyError}</p>}
|
|
121
|
-
{history.length === 0 ? <p className="bp-muted">点“读取预览”加载最近 20 条。</p> : <ul className="bp-checklist">{history.map((row, index) => (
|
|
122
|
-
<li key={index}><strong>{row.role}</strong>{row.name ? ` · ${row.name}` : ''} · {row.chars} 字
|
|
123
|
-
{row.at ? ` · ${new Date(row.at).toLocaleTimeString('zh-CN')}` : ''}
|
|
124
|
-
<small>{row.preview}{String(row.preview ?? '').length < row.chars ? '…' : ''}</small>
|
|
125
|
-
<button type="button" onClick={() => { void openFull(index) }}>查看全文</button></li>
|
|
126
|
-
))}</ul>}
|
|
127
|
-
{full ? <div className="bp-fulltext"><p className="bp-muted">第 {full.index + 1} 条(从最新往前数)· {full.role} · {full.chars} 字</p>
|
|
128
|
-
<pre>{full.content}</pre></div> : null}
|
|
129
|
-
</details> : null}
|
|
130
92
|
</div>
|
|
131
93
|
}
|
|
132
94
|
|
|
133
|
-
|
|
95
|
+
let bindingScopeKey = 0
|
|
96
|
+
export function BotBindings({ snapshot, openSession, dispatch, busy, focus, scope, onOpenTools, onOpenChatPanel }) {
|
|
97
|
+
const scopeKey = useMemo(() => ++bindingScopeKey, [scope])
|
|
134
98
|
const bots = snapshot.bots ?? []
|
|
135
99
|
const [selected, setSelected] = useState(bots[0]?.id ?? '')
|
|
100
|
+
useEffect(() => { if (focus?.botId && bots.some(row => row.id === focus.botId)) setSelected(focus.botId) }, [focus?.version, focus?.botId])
|
|
136
101
|
const bot = bots.find(row => row.id === selected) ?? bots[0]
|
|
137
102
|
const section = snapshot.botBindings ?? snapshot.bindings
|
|
138
103
|
const binding = section?.items?.find(row => row.botId === bot?.id)
|
|
@@ -143,12 +108,12 @@ export function BotBindings({ snapshot, openSession, dispatch, busy }) {
|
|
|
143
108
|
['资料库', /^tool\/bot_library_/], ['例行任务', /^tool\/bot_routine_/], ['远程工具', /^remote\//],
|
|
144
109
|
].filter(([, pattern]) => capabilities.some(value => pattern.test(value))).map(([label]) => label)
|
|
145
110
|
if (!bots.length) return <p>还没有 Bot。返回 Main 完成首次设置,或从侧栏新建 Bot。</p>
|
|
146
|
-
return <div className="bp-columns">
|
|
147
|
-
<
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
<dl className="bp-facts"><Value label="职责">{bot.role || '未填写'}</Value>
|
|
151
|
-
<Value label="
|
|
111
|
+
return <div className="bp-columns bp-team-layout">
|
|
112
|
+
<TeamDirectory snapshot={snapshot} selected={bot?.id} onSelect={setSelected} />
|
|
113
|
+
<article className="bp-card"><div className="bp-card-title"><div className="bp-person-heading"><span className="bp-person-avatar bp-person-avatar-large"><UiIcon name="bot" size={27} /></span><div><h2>{bot.name}</h2><p className="bp-muted">{bot.main ? '团队协调 Bot' : '团队成员'}</p></div></div><BotConversationLink productApi={scope} bot={bot} openSession={openSession} onOpenIndependent={id => openIndependentChat(scope, onOpenChatPanel, id)} available={snapshot.availableActions?.includes('independentChat.view')}/></div>
|
|
114
|
+
{dispatch && (!snapshot.availableActions || snapshot.availableActions.includes('binding.save')) ? <BotBindingEditor key={`${scopeKey}:${bot.id}`} hostScope={scope} bot={bot} record={binding} accounts={snapshot.fundingAccounts} dispatch={dispatch} busy={busy} onOpenTools={onOpenTools} /> : <p className="bp-muted">当前连接可以查看 Bot 配置,暂不支持保存修改。</p>}
|
|
115
|
+
<details className="bp-technical"><summary>当前权限与运行配置</summary><dl className="bp-facts"><Value label="职责">{bot.role || '未填写'}</Value>
|
|
116
|
+
<Value label="宿主已选模型">{modelName(bot.model)}</Value><Value label="最近应用模型">{bot.appliedModel ? modelName(bot.appliedModel) : '宿主尚未报告'}</Value><Value label="模型策略">精确绑定;不可用时暂停并说明原因</Value>
|
|
152
117
|
<Value label="工作区">{binding?.workspace?.root ?? binding?.workspaceRef?.rootId ?? bot.workDir ?? '未指定,请在 Bot 详情选择文件夹'}</Value>
|
|
153
118
|
<Value label="执行位置">{binding?.machineId ?? binding?.workspaceRef?.machineId ?? '尚未取得宿主绑定'}</Value>
|
|
154
119
|
<Value label="最大并发">{binding?.maxConcurrentTasks}</Value></dl>
|
|
@@ -158,8 +123,8 @@ export function BotBindings({ snapshot, openSession, dispatch, busy }) {
|
|
|
158
123
|
{binding.blockedReason && <Blocked value={{ reason: binding.blockedReason, nextAction: binding.nextAction }} />}
|
|
159
124
|
</> : <Blocked value={{ reason: '此 Bot 尚未取得有效绑定。', nextAction: '在宿主 Bot 绑定配置中授予明确工具、机器与工作区。' }} />}
|
|
160
125
|
<p className="bp-muted">角色文本不会授予工具或机器权限。职责、模型、记忆及会话设置沿用私聊右侧的 Bot 详情。</p>
|
|
161
|
-
|
|
162
|
-
<details className="bp-technical"><summary>连接与运行详情</summary><dl className="bp-facts"><Value label="稳定 Bot ID">{bot.id}</Value><Value label="预算范围">{binding?.budgetScope}</Value></dl><h3>完整权限标识</h3><p className="bp-code">{capabilities.join('、') || '无额外能力'}</p>{snapshot.availableActions?.includes('botRuntimeStatus') && <BotRuntimeRow botId={bot.id} dispatch={dispatch} busy={busy} />}</details>
|
|
126
|
+
</details>
|
|
127
|
+
<details className="bp-technical"><summary>连接与运行详情</summary><dl className="bp-facts"><Value label="稳定 Bot ID">{bot.id}</Value><Value label="预算范围">{binding?.budgetScope}</Value></dl><h3>完整权限标识</h3><p className="bp-code">{capabilities.join('、') || '无额外能力'}</p>{snapshot.availableActions?.includes('botRuntimeStatus') && <BotRuntimeRow key={bot.id} botId={bot.id} dispatch={dispatch} busy={busy} onOpenChat={id => openIndependentChat(scope, onOpenChatPanel, id)} />}</details>
|
|
163
128
|
</article>
|
|
164
129
|
</div>
|
|
165
130
|
}
|
|
@@ -189,7 +154,7 @@ const submissionFailureMessage = failure => {
|
|
|
189
154
|
return '原提交核对尚未完成,请恢复宿主连接后再次核对;请保留核对码,不会重新提交任务。'
|
|
190
155
|
}
|
|
191
156
|
|
|
192
|
-
function BotRoutingCard({ snapshot, dispatch, busy, submissionScope = dispatch }) {
|
|
157
|
+
function BotRoutingCard({ snapshot, dispatch, busy, submissionScope = dispatch, navigation, focus }) {
|
|
193
158
|
const submissions = useMemo(() => routingSubmissions(submissionScope), [submissionScope])
|
|
194
159
|
const submission = useSyncExternalStore(submissions.subscribe, submissions.getSnapshot, submissions.getSnapshot)
|
|
195
160
|
const [view, setView] = React.useState(null)
|
|
@@ -208,6 +173,8 @@ function BotRoutingCard({ snapshot, dispatch, busy, submissionScope = dispatch }
|
|
|
208
173
|
const control = useRef({ live: false, epoch: 0, draft: 0, active: null, prepared: null, ready: false })
|
|
209
174
|
const form = useRef({ text: '', pin: '', machine: '', root: '', effect: 'read' })
|
|
210
175
|
const busyRef = useRef(busy)
|
|
176
|
+
const draftFocus = useRef(null)
|
|
177
|
+
const card = useRef(null)
|
|
211
178
|
busyRef.current = busy
|
|
212
179
|
const call = useCallback(async (action, payload) => {
|
|
213
180
|
if (typeof dispatch !== 'function') throw new Error('宿主接口尚未就绪。')
|
|
@@ -222,11 +189,26 @@ function BotRoutingCard({ snapshot, dispatch, busy, submissionScope = dispatch }
|
|
|
222
189
|
const editDraft = changes => {
|
|
223
190
|
const next = { ...form.current, ...changes }
|
|
224
191
|
form.current = next
|
|
192
|
+
if (Object.hasOwn(changes, 'text')) navigation?.saveGoalDraft(next.text)
|
|
225
193
|
setText(next.text); setPin(next.pin); setMachine(next.machine); setRoot(next.root); setEffect(next.effect)
|
|
226
194
|
const previous = submissions.getSnapshot()
|
|
227
195
|
if (previous?.state === 'confirmed') submissions.clear(previous)
|
|
228
196
|
invalidate()
|
|
229
197
|
}
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
draftFocus.current = null
|
|
200
|
+
const restored = navigation?.getGoalDraft() ?? ''
|
|
201
|
+
form.current = { text: restored, pin: '', machine: '', root: '', effect: 'read' }
|
|
202
|
+
setText(restored); setPin(''); setMachine(''); setRoot(''); setEffect('read')
|
|
203
|
+
}, [navigation, submissionScope])
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
if (!focus?.compose || !viewReady || draftFocus.current === focus.version) return
|
|
206
|
+
draftFocus.current = focus.version
|
|
207
|
+
const draft = navigation?.claimDraft(focus.version)
|
|
208
|
+
if (draft !== null && draft !== undefined) editDraft({ text: draft })
|
|
209
|
+
card.current?.scrollIntoView?.({ block: 'start', behavior: 'auto' })
|
|
210
|
+
card.current?.querySelector('textarea')?.focus({ preventScroll: true })
|
|
211
|
+
}, [focus?.version, focus?.compose, viewReady, navigation])
|
|
230
212
|
const begin = useCallback(kind => {
|
|
231
213
|
const state = control.current
|
|
232
214
|
if (!state.live || state.active || busyRef.current) return null
|
|
@@ -300,6 +282,7 @@ function BotRoutingCard({ snapshot, dispatch, busy, submissionScope = dispatch }
|
|
|
300
282
|
if (next?.accepted !== true || typeof planId !== 'string' || !planId.trim()) throw new Error('未取得有效的任务计划编号。')
|
|
301
283
|
// The receipt belongs to the captured Host even if its card was unmounted meanwhile.
|
|
302
284
|
submissions.update(reference, { state: 'confirmed', planId, planState: next.planState })
|
|
285
|
+
navigation?.saveGoalDraft('')
|
|
303
286
|
if (current(token)) state.prepared = null
|
|
304
287
|
} catch (failure) {
|
|
305
288
|
if (reference) submissions.update(reference, { state: 'unknown' })
|
|
@@ -400,7 +383,7 @@ function BotRoutingCard({ snapshot, dispatch, busy, submissionScope = dispatch }
|
|
|
400
383
|
const machines = (snapshot.machines?.items ?? []).filter(item => item.status !== 'revoked')
|
|
401
384
|
const inputDisabled = busy || !viewReady || working === 'dispatch' || working === 'settings'
|
|
402
385
|
const nameOf = botId => view.candidates.find(row => row.botId === botId)?.name ?? botId
|
|
403
|
-
return <article className="bp-card"><div className="bp-card-title"><h2>Bot 路由</h2><Tag>{view.enabled ? `已开启 · ${routingPresetLabel(view.preset)}` : '未开启'}</Tag>
|
|
386
|
+
return <article className="bp-card bp-task-composer" ref={card}><div className="bp-card-title"><h2>Bot 路由</h2><Tag>{view.enabled ? `已开启 · ${routingPresetLabel(view.preset)}` : '未开启'}</Tag>
|
|
404
387
|
<button type="button" disabled={busy || Boolean(working)} onClick={() => { void load() }}>刷新候选</button></div>
|
|
405
388
|
<div className="bp-actions"><label className="bp-check"><input type="checkbox" checked={view.enabled} disabled={busy || Boolean(working) || !viewReady} onChange={event => { void saveSettings({ enabled: event.target.checked, preset: view.preset, autoGrant: view.autoGrant }) }} />开启 Bot 路由</label>
|
|
406
389
|
<label>策略<select value={view.preset} disabled={busy || Boolean(working) || !viewReady} onChange={event => { void saveSettings({ enabled: view.enabled, preset: event.target.value, autoGrant: view.autoGrant }) }}>
|
|
@@ -463,10 +446,10 @@ function BotRoutingCard({ snapshot, dispatch, busy, submissionScope = dispatch }
|
|
|
463
446
|
</article>
|
|
464
447
|
}
|
|
465
448
|
|
|
466
|
-
export function Tasks({ snapshot, dispatch, busy, submissionScope = dispatch, inspect, openSession }) {
|
|
449
|
+
export function Tasks({ snapshot, dispatch, busy, submissionScope = dispatch, inspect, openSession, focus, navigation }) {
|
|
467
450
|
if (snapshot.tasks?.status !== 'available') return <Blocked value={snapshot.tasks} />
|
|
468
451
|
return <TaskWorkspace snapshot={snapshot} dispatch={dispatch} busy={busy} scope={submissionScope} inspect={inspect} openSession={openSession}
|
|
469
|
-
routingCard={snapshot.availableActions?.includes('botRoutingInspect') ? <BotRoutingCard snapshot={snapshot} dispatch={dispatch} busy={busy} submissionScope={submissionScope} /> : null} />
|
|
452
|
+
focus={focus} routingCard={snapshot.availableActions?.includes('botRoutingInspect') ? <BotRoutingCard snapshot={snapshot} dispatch={dispatch} busy={busy} submissionScope={submissionScope} navigation={navigation ?? workspaceNavigation(submissionScope)} focus={focus} /> : null} />
|
|
470
453
|
}
|
|
471
454
|
|
|
472
455
|
|
|
@@ -599,6 +582,7 @@ function MachineSetup({ snapshot, dispatch, busy }) {
|
|
|
599
582
|
// The channel reports failures as {ok:false}; silence here is how a pairing looks successful
|
|
600
583
|
// while nothing was stored, so every step is checked.
|
|
601
584
|
const unwrap = response => {
|
|
585
|
+
if (response === undefined || response === null) throw new Error('宿主尚未确认这一步,请刷新后核对原始结果。')
|
|
602
586
|
if (response && response.ok === false) throw new Error(response.error?.message ?? response.error ?? '宿主拒绝了这一步。')
|
|
603
587
|
return response
|
|
604
588
|
}
|
|
@@ -625,7 +609,7 @@ function MachineSetup({ snapshot, dispatch, busy }) {
|
|
|
625
609
|
setWorking(false)
|
|
626
610
|
}
|
|
627
611
|
}
|
|
628
|
-
return
|
|
612
|
+
return <><SecurePairingSetup snapshot={snapshot} dispatch={dispatch} busy={busy} /><details className="bp-card bp-machine-setup"><summary>兼容已有电脑(导入长期配对文件)</summary>
|
|
629
613
|
<p className="bp-muted">在你自己的 Windows 电脑或云电脑上解压 Agent 包,双击 <code>start.cmd</code>,按向导选共享文件夹和连接地址。回到这里导入向导生成的 <code>pairing.private.json</code>,地址、目录和权限会自动填好。</p>
|
|
630
614
|
{available.includes('machine.secret') && available.includes('machine.pair') ? <div className="bp-form">
|
|
631
615
|
<label>配对文件<input type="file" accept=".json,application/json" onChange={event => { void importFile(event) }} /></label>
|
|
@@ -640,7 +624,7 @@ function MachineSetup({ snapshot, dispatch, busy }) {
|
|
|
640
624
|
{status !== '' && <p className="bp-muted" role="status">{status}</p>}
|
|
641
625
|
{error !== '' && <p className="bp-error" role="alert">{error}</p>}
|
|
642
626
|
</div> : <p className="bp-muted">宿主尚未提供配对操作。</p>}
|
|
643
|
-
</details
|
|
627
|
+
</details></>
|
|
644
628
|
}
|
|
645
629
|
|
|
646
630
|
function MachineControls({ machine, snapshot, dispatch, busy }) {
|
|
@@ -704,12 +688,18 @@ function ArtifactDownload({ machine, snapshot, dispatch, busy }) {
|
|
|
704
688
|
</details>
|
|
705
689
|
}
|
|
706
690
|
|
|
707
|
-
export function Machines({ snapshot, dispatch, busy }) {
|
|
691
|
+
export function Machines({ snapshot, dispatch, busy, focus, transport }) {
|
|
692
|
+
const machineCards = useRef(new Map())
|
|
693
|
+
useEffect(() => {
|
|
694
|
+
if (!focus?.machineId) return
|
|
695
|
+
const card = machineCards.current.get(focus.machineId)
|
|
696
|
+
card?.focus({ preventScroll: true }); card?.scrollIntoView?.({ block: 'start' })
|
|
697
|
+
}, [focus?.version, focus?.machineId])
|
|
708
698
|
const section = snapshot.machines
|
|
709
699
|
if (section?.status !== 'available') return <Blocked value={section} />
|
|
710
|
-
return <>{dispatch&&<><DesktopWorkbench snapshot={snapshot} dispatch={dispatch} />
|
|
700
|
+
return <>{dispatch&&<><DesktopWorkbench snapshot={snapshot} dispatch={dispatch} transport={transport} />
|
|
711
701
|
<details><summary>兼容截图操作</summary><DesktopControl snapshot={snapshot} dispatch={dispatch} busy={busy} /></details>
|
|
712
|
-
<MachineSetup snapshot={snapshot} dispatch={dispatch} busy={busy} /></>}{section.items.length ? <div className="bp-grid">{section.items.map(machine => <article className=
|
|
702
|
+
<MachineSetup snapshot={snapshot} dispatch={dispatch} busy={busy} /></>}{focus?.machineId && !section.items.some(machine => (machine.id ?? machine.machineId) === focus.machineId) && <p className="bp-blocked" role="status">所选电脑不在当前宿主记录中,请刷新后核对。</p>}{section.items.length ? <div className="bp-grid">{section.items.map(machine => <article className={`bp-card${focus?.machineId === (machine.id ?? machine.machineId) ? ' bp-selected-resource' : ''}`} key={machine.id ?? machine.machineId} tabIndex={-1} ref={node => { const id = machine.id ?? machine.machineId; if (node) machineCards.current.set(id, node); else machineCards.current.delete(id) }}>
|
|
713
703
|
<div className="bp-card-title"><h2>{machine.name ?? machine.id ?? machine.machineId}</h2><Tag>{machine.status ?? '未知'}</Tag></div>
|
|
714
704
|
<dl className="bp-facts"><Value label="平台">{machine.platform}</Value><Value label="Agent 版本">{machine.version}</Value><Value label="最后在线">{machine.lastSeenAt ? new Date(machine.lastSeenAt).toLocaleString('zh-CN') : '从未取得心跳'}</Value>
|
|
715
705
|
<Value label="能力">{machine.capabilities?.join('、')}</Value><Value label="桌面会话">{machine.desktop?.status ?? '未报告'}</Value></dl>
|
|
@@ -743,7 +733,7 @@ function UnknownReview({ item, dispatch, busy }) {
|
|
|
743
733
|
</article>
|
|
744
734
|
}
|
|
745
735
|
|
|
746
|
-
export function Usage({ snapshot, dispatch, busy, openAdvanced }) {
|
|
736
|
+
export function Usage({ snapshot, dispatch, busy, openAdvanced, scope }) {
|
|
747
737
|
const section = snapshot.usage
|
|
748
738
|
if (section?.status !== 'available') return <><Blocked value={section} /><button onClick={openAdvanced}>打开已有 MR 费用与订阅面板</button></>
|
|
749
739
|
const unresolved = section.unresolved ?? []
|
|
@@ -755,11 +745,7 @@ export function Usage({ snapshot, dispatch, busy, openAdvanced }) {
|
|
|
755
745
|
<p className="bp-muted">以下执行已经分派,但没有取得可核验的厂商账单;在核对完成前,其 Bot 与资金范围不会放行新的付费调用。核对说明会记录在运行记录中。</p>
|
|
756
746
|
<div className="bp-grid">{unresolved.map(item => <UnknownReview key={`${item.runId}/${item.packageId}`} item={item} dispatch={dispatch} busy={busy} />)}</div>
|
|
757
747
|
</section> : null}
|
|
758
|
-
{
|
|
759
|
-
<tbody>{section.items.map((row, i) => <tr key={row.id ?? row.receiptId ?? i}><td>{row.botName ?? row.botId ?? '未归属'}<small>{row.taskId ?? row.runId}</small></td>
|
|
760
|
-
<td>{({ bot: 'Bot 对话', aux: 'Bot 辅助调用', 'mr-api': 'MR API', cli: '官方 CLI', review: '评审' })[row.source] ?? row.source ?? '未报告'}</td>
|
|
761
|
-
<td>{modelName(row.model ?? (row.provider ? { provider: row.provider, model: row.modelId } : null))}</td><td>{row.billing === 'subscription' ? '订阅' : money(row.amountUsd ?? row.costUsd)}</td>
|
|
762
|
-
<td>{row.status ?? '未知'}{row.resolution && <Tag>{row.resolution.kind === 'waive' ? '已按零费用放行' : `已确认 ${money(row.resolution.costUsd)}`}</Tag>}</td></tr>)}</tbody></table></div>}
|
|
748
|
+
<UsageExplorer section={section} busy={busy} scope={scope}/>
|
|
763
749
|
<button onClick={openAdvanced}>打开高级费用、订阅与历史能力</button>
|
|
764
750
|
</>
|
|
765
751
|
}
|
|
@@ -866,6 +852,29 @@ export function RoleCatalog({ dispatch, busy }) {
|
|
|
866
852
|
}
|
|
867
853
|
|
|
868
854
|
export function ProductPage({ section, productApi, openSession, openPanel }) {
|
|
855
|
+
const pageRef = useRef(null)
|
|
856
|
+
useLayoutEffect(() => { if (pageRef.current) { pageRef.current.scrollTop = 0; pageRef.current.scrollLeft = 0 } }, [section, productApi])
|
|
857
|
+
const navigation = useMemo(() => workspaceNavigation(productApi), [productApi])
|
|
858
|
+
const route = useSyncExternalStore(navigation.subscribe, navigation.getSnapshot, navigation.getSnapshot)
|
|
859
|
+
const focus = route?.section === section ? route : null
|
|
860
|
+
const [searchOpen, setSearchOpen] = useState(false)
|
|
861
|
+
const onNavigate = useCallback(destination => {
|
|
862
|
+
navigation.navigate(destination)
|
|
863
|
+
openPanel?.(PRODUCT_PANELS[destination.section])
|
|
864
|
+
}, [navigation, openPanel])
|
|
865
|
+
useEffect(() => { setSearchOpen(false) }, [productApi])
|
|
866
|
+
useEffect(() => { if (focus?.search) setSearchOpen(true) }, [focus?.version, focus?.search])
|
|
867
|
+
useEffect(() => {
|
|
868
|
+
const onKey = event => {
|
|
869
|
+
if (event.defaultPrevented || event.isComposing || event.keyCode === 229 || event.altKey || event.shiftKey || !(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== 'k') return
|
|
870
|
+
if (typeof pageRef.current?.checkVisibility === 'function' && !pageRef.current.checkVisibility()) return
|
|
871
|
+
if (event.target instanceof Element && event.target.closest('.xterm, .bp-stream-canvas[data-control=true]')) return
|
|
872
|
+
if (document.querySelector('[aria-modal="true"]') && !document.querySelector('.bp-palette')) return
|
|
873
|
+
event.preventDefault(); event.stopPropagation(); setSearchOpen(value => !value)
|
|
874
|
+
}
|
|
875
|
+
document.addEventListener('keydown', onKey, true)
|
|
876
|
+
return () => document.removeEventListener('keydown', onKey, true)
|
|
877
|
+
}, [])
|
|
869
878
|
useEffect(() => {
|
|
870
879
|
const id = PRODUCT_PANELS[section]
|
|
871
880
|
announcePanel(id)
|
|
@@ -925,31 +934,33 @@ export function ProductPage({ section, productApi, openSession, openPanel }) {
|
|
|
925
934
|
}
|
|
926
935
|
}, [productApi, refresh])
|
|
927
936
|
const inspectTask = useCallback((payload, signal) => productApi.dispatch('task.inspect', payload, signal), [productApi])
|
|
937
|
+
const desktopTransport = useCallback((action,payload,signal) => productApi.dispatch(action,payload,signal),[productApi])
|
|
928
938
|
const disabled = busy || snapshotStale || snapshotApi.current !== productApi || Boolean(snapshot?.readOnly)
|
|
929
|
-
const
|
|
930
|
-
const
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
machines: ['computer', '连接你自己的电脑,随时查看桌面或接过控制权。'],
|
|
934
|
-
library: ['folder', '给 Bot 一份有序的资料,让每次协作都有据可循。'],
|
|
935
|
-
routine: ['repeat', '演示一次,保存步骤,把重复的工作交给 Bot。'],
|
|
936
|
-
usage: ['chart', '查看真实使用记录,清楚掌握每一笔费用。'],
|
|
937
|
-
}[section] ?? ['bot', '']
|
|
938
|
-
return <main className="bp-page" data-section={section}><div className="bp-page-inner"><header className="bp-page-header">
|
|
939
|
-
<div className="bp-page-heading"><p className="bp-eyebrow"><UiIcon name={meta[0]} size={14} />工作空间<UiIcon name="chevron" size={12} /><span>{title}</span></p><h1>{title}</h1><p className="bp-page-description">{meta[1]}</p></div>
|
|
939
|
+
const meta = WORKSPACE_SECTIONS.find(row => row.section === section) ?? { title: '工作空间', icon: 'home', description: '' }
|
|
940
|
+
const title = meta.title
|
|
941
|
+
return <div className="bp-workspace"><WorkspaceToolbar title={title} onHome={() => onNavigate({ section: 'home' })} onSearch={() => setSearchOpen(true)} /><main ref={pageRef} className="bp-page" data-section={section}><div className="bp-page-inner"><header className={`bp-page-header${section === 'home' ? ' bp-home-header' : ''}`}>
|
|
942
|
+
<div className="bp-page-heading">{section === 'home' ? <p className="bp-eyebrow"><span className="bp-live-dot" />团队工作空间</p> : <><p className="bp-eyebrow"><UiIcon name={meta.icon} size={14} />工作空间<UiIcon name="chevron" size={12} /><span>{title}</span></p><h1>{title}</h1><p className="bp-page-description">{meta.description}</p></>}</div>
|
|
940
943
|
<div className="bp-page-tools"><span className="bp-sync" data-state={snapshotStale ? 'stale' : snapshot ? 'ready' : 'loading'}>{snapshotStale ? '等待同步' : snapshot ? '已同步' : '正在连接'}</span>
|
|
941
944
|
<button className="bp-refresh" disabled={busy} onClick={() => refresh()} title="刷新当前页面"><UiIcon name="refresh" size={16} className={busy ? 'bp-spinning' : ''} /><span>刷新</span></button></div>
|
|
942
945
|
</header><div className="bp-page-body">
|
|
943
946
|
{error && <div role="alert" className="bp-blocked"><strong>无法取得最新宿主状态</strong><p>{error}</p>{snapshot && <p>{snapshotStale ? '下面保留上次读取结果;状态未更新时,操作暂不可用。' : '下面保留上次读取结果,操作以宿主当前授权为准。'}</p>}</div>}
|
|
944
947
|
{snapshot?.readOnly && <Blocked value={{ reason: snapshot.readOnly, nextAction: '关闭持有写锁的另一宿主后重新加载。' }} />}
|
|
945
948
|
{!snapshot && !error && <div className="bp-loading" role="status"><span className="bp-loading-line" aria-hidden="true" /><span className="bp-loading-line" aria-hidden="true" /><p>正在读取宿主状态…</p></div>}
|
|
946
|
-
{snapshot && <>{section === '
|
|
947
|
-
{section === '
|
|
948
|
-
{section === '
|
|
949
|
+
{snapshot && <>{section === 'home' && <WorkspaceHome snapshot={snapshot} onNavigate={onNavigate} openSession={openSession} disabled={disabled} />}
|
|
950
|
+
{section === 'inbox' && <WorkspaceInbox snapshot={snapshot} onNavigate={onNavigate} openSession={openSession} />}
|
|
951
|
+
{section === 'notifications' && <NotificationsPanel productApi={productApi} botIds={snapshot.bots.map(bot => bot.id)} available={!snapshotStale && snapshotApi.current === productApi && !snapshot.readOnly && snapshot.availableActions?.includes('notifications.list')} onNavigate={onNavigate} />}
|
|
952
|
+
{section === 'threads' && <BotThreadsPanel productApi={productApi} botId={focus?.botId ?? snapshot.mainBotId} onOpenSession={!snapshotStale && snapshotApi.current === productApi ? openSession : undefined} available={!snapshotStale && snapshotApi.current === productApi && !snapshot.readOnly && snapshot.availableActions?.includes('threads.list')} />}
|
|
953
|
+
{section === 'webPanel' && <WebPanelsPanel productApi={productApi} dispatch={dispatch} busy={disabled} snapshot={snapshot} focus={focus}
|
|
954
|
+
available={!snapshotStale && snapshotApi.current === productApi && !snapshot.readOnly && snapshot.availableActions?.includes('webPanels.list')}
|
|
955
|
+
selectedId={focus?.panelId} onSelect={panelId => navigation.navigate({ section: 'webPanel', panelId })} onLeave={() => onNavigate({ section: 'home' })} />}
|
|
956
|
+
{section === 'bindings' && <>{!snapshotStale && snapshotApi.current === productApi ? <BotBindings snapshot={snapshot} openSession={openSession} dispatch={dispatch} busy={disabled} focus={focus} scope={productApi} onOpenChatPanel={openPanel} onOpenTools={typeof openPanel === 'function' ? () => { requestWorkbenchView('tools'); openPanel('model-router-galgame') } : undefined} /> : <p role="status">正在读取当前连接的 Bot 配置…</p>}{snapshot.availableActions?.includes('roles.list') && <RoleCatalog dispatch={dispatch} busy={disabled} />}</>}
|
|
957
|
+
{section === 'tasks' && <Tasks snapshot={snapshot} dispatch={dispatch} busy={disabled} submissionScope={productApi} inspect={!snapshotStale && snapshotApi.current === productApi && !snapshot.readOnly ? inspectTask : undefined} openSession={openSession} focus={focus} navigation={navigation} />}
|
|
958
|
+
{section === 'machines' && <Machines snapshot={snapshot} dispatch={dispatch} busy={disabled} focus={focus} transport={desktopTransport} />}
|
|
949
959
|
{section === 'routine' && <RoutinesPanel snapshot={snapshot} dispatch={dispatch} busy={disabled} onOpenMachines={() => openPanel(PRODUCT_PANELS.machines)} />}
|
|
950
|
-
{section === 'library' && <Library snapshot={snapshot} dispatch={dispatch} busy={disabled} />}
|
|
951
|
-
{section === '
|
|
960
|
+
{section === 'library' && <Library snapshot={snapshot} dispatch={dispatch} busy={disabled} focus={focus} />}
|
|
961
|
+
{section === 'data' && <DataPanel snapshot={snapshot} dispatch={dispatch} busy={disabled || !snapshot.availableActions?.includes('data.catalog')} focus={focus} transport={desktopTransport} scope={productApi} />}
|
|
962
|
+
{section === 'usage' && <Usage snapshot={snapshot} dispatch={dispatch} busy={disabled} scope={productApi} openAdvanced={() => openPanel('model-router-galgame')} />}
|
|
952
963
|
<footer className="bp-page-footer bp-muted">上次同步 {new Date(snapshot.observedAt).toLocaleString('zh-CN')}</footer></>}
|
|
953
964
|
</div></div>
|
|
954
|
-
</main>
|
|
965
|
+
</main><WorkspaceCommandPalette open={searchOpen} onClose={() => setSearchOpen(false)} snapshot={snapshotApi.current === productApi ? snapshot : null} onNavigate={onNavigate} openSession={snapshotApi.current === productApi ? openSession : undefined} /></div>
|
|
955
966
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React, { useMemo } from 'react'
|
|
2
|
+
import { withComposerDraft } from './composer-draft-host.jsx'
|
|
3
|
+
import { ConversationTools } from './chat-ui.jsx'
|
|
4
|
+
import { ContactAttentionNavigation } from './contact-attention.jsx'
|
|
5
|
+
import { WebPanelPinsLive } from './web-panel-pins-live.jsx'
|
|
6
|
+
import { workspaceNavigation, PRODUCT_PANELS } from './workspace-navigation.mjs'
|
|
7
|
+
import { requestWorkbenchView } from '../../.dsh-plugin/client/workbench-state.mjs'
|
|
8
|
+
import { openIndependentChat } from './independent-chat-navigation.mjs'
|
|
9
|
+
|
|
10
|
+
/** Native slot identities stay intact so their registration watchers cannot select their own replacement. */
|
|
11
|
+
export function createProductSlots(slots,openPanel,productApi,TeamSidebar,client={}){
|
|
12
|
+
const navigate=input=>{workspaceNavigation(productApi).navigate(input);return openPanel(PRODUCT_PANELS[input.section])}
|
|
13
|
+
const conversationAddon=props=><ConversationTools {...props} productApi={productApi} onOpenSession={client.openSession} onOpenIndependentChat={botId=>openIndependentChat(productApi,openPanel,botId)} onNavigate={navigate} onOpenTools={()=>{requestWorkbenchView('tools');return openPanel('model-router-galgame')}}/>
|
|
14
|
+
return new Proxy(slots,{get(target,key){
|
|
15
|
+
if(key==='register')return(options,component)=>{
|
|
16
|
+
if(options.name==='conversation.session.header'&&typeof options.inject==='function'){
|
|
17
|
+
const original=options.inject
|
|
18
|
+
return target.register({...options,inject:(...args)=>{const face=original(...args)??{};return {...face,actions:{...face.actions,conversationAddon}}}},component)
|
|
19
|
+
}
|
|
20
|
+
// The composer draft rides the Host's own composer slot, beside the
|
|
21
|
+
// vendor's "+" extra: one wrapper keeps the registered component and its
|
|
22
|
+
// props untouched, and the draft asks the Host for its namespace through
|
|
23
|
+
// the product API this factory already carries.
|
|
24
|
+
if(options.name==='conversation.input.left'){
|
|
25
|
+
return target.register(options,withComposerDraft(component,{productApi,sessionBindingOf:client.sessionBindingOf}))
|
|
26
|
+
}
|
|
27
|
+
if(component!==TeamSidebar)return target.register(options,component)
|
|
28
|
+
const Sidebar=props=>{
|
|
29
|
+
const sidebarAddon=useMemo(()=>function Footer({wide}){
|
|
30
|
+
const go=id=>{props.actions?.setLeftDrawer?.(false);return openPanel(id)}
|
|
31
|
+
return <>
|
|
32
|
+
<ContactAttentionNavigation productApi={productApi} compact={!wide}
|
|
33
|
+
onNavigate={input=>{workspaceNavigation(productApi).navigate(input);return go(PRODUCT_PANELS[input.section])}}
|
|
34
|
+
onOpenAdvanced={()=>go('model-router-galgame')}/>
|
|
35
|
+
<WebPanelPinsLive productApi={productApi} onSelect={panelId=>navigate({section:'webPanel',panelId})}/>
|
|
36
|
+
</>
|
|
37
|
+
},[props.actions])
|
|
38
|
+
return <TeamSidebar {...props} actions={{...props.actions,sidebarAddon}}/>
|
|
39
|
+
}
|
|
40
|
+
Sidebar.dsBotTeamSidebar=TeamSidebar
|
|
41
|
+
return target.register(options,Sidebar)
|
|
42
|
+
}
|
|
43
|
+
const value=Reflect.get(target,key,target)
|
|
44
|
+
return typeof value==='function'?value.bind(target):value
|
|
45
|
+
}})
|
|
46
|
+
}
|
|
@@ -19,6 +19,12 @@ export function createAcknowledgedRoutineRecorder({ now = Date.now, onChange } =
|
|
|
19
19
|
while (pending.size) {
|
|
20
20
|
const [id, item] = pending.entries().next().value
|
|
21
21
|
if (!item.acknowledged) return
|
|
22
|
+
if (item.release) {
|
|
23
|
+
const at=Math.max(item.sentAt,times.at(-1)??item.sentAt),delayMs=times.length?Math.max(0,Math.round(at-times.at(-1))):0
|
|
24
|
+
if(steps.length>=MAX_ROUTINE_STEPS){invalidate('ROUTINE_TOO_LONG');return}
|
|
25
|
+
if(delayMs>60_000){invalidate('ROUTINE_PAUSE_TOO_LONG');return}
|
|
26
|
+
steps.push({action:'release-input',delayMs});times.push(at);moveGroup=null;inputReleased=true;pending.delete(id);continue
|
|
27
|
+
}
|
|
22
28
|
const movement = item.event.type === 'pointer' && item.event.action === 'move'
|
|
23
29
|
const at = Math.max(item.sentAt, times.at(-1) ?? item.sentAt)
|
|
24
30
|
const continuing = movement && moveGroup?.button === item.event.button
|
|
@@ -35,7 +41,7 @@ export function createAcknowledgedRoutineRecorder({ now = Date.now, onChange } =
|
|
|
35
41
|
if (!movement) moveGroup = null
|
|
36
42
|
else if (!continuing) moveGroup = { startedAt: at, bucket: 0, tailIndex: null, button: item.event.button }
|
|
37
43
|
else { moveGroup.bucket = bucket; moveGroup.tailIndex = index }
|
|
38
|
-
observedInputs++; if (replace) coalescedMoves++
|
|
44
|
+
observedInputs++;inputReleased=false; if (replace) coalescedMoves++
|
|
39
45
|
pending.delete(id)
|
|
40
46
|
}
|
|
41
47
|
}
|
|
@@ -71,6 +77,21 @@ export function createAcknowledgedRoutineRecorder({ now = Date.now, onChange } =
|
|
|
71
77
|
if (status !== 'recording') return
|
|
72
78
|
invalidate(meta?.failure?.status === 'unknown' || meta?.failure?.possiblyApplied !== false ? 'ROUTINE_ACK_LOST' : 'ROUTINE_DEMONSTRATION_FAILED')
|
|
73
79
|
},
|
|
80
|
+
releaseSent(meta) {
|
|
81
|
+
if(status!=='recording')return
|
|
82
|
+
if(!sameSession(meta)||!Number.isSafeInteger(meta.id)||meta.id<=lastId||!Number.isFinite(meta.sentAt)){invalidate('ROUTINE_RECORDING_SESSION_CHANGED');return}
|
|
83
|
+
if(pending.size>=MAX_PENDING_INPUTS){invalidate('ROUTINE_INPUT_PENDING_LIMIT');return}
|
|
84
|
+
pending.set(meta.id,{release:true,sentAt:meta.sentAt,acknowledged:false});lastId=meta.id;inputReleased=false;emit()
|
|
85
|
+
},
|
|
86
|
+
releaseAcknowledged(meta) {
|
|
87
|
+
if(status!=='recording')return
|
|
88
|
+
const item=pending.get(meta?.id)
|
|
89
|
+
if(!sameSession(meta)||!item?.release){invalidate('ROUTINE_ACK_MISMATCH');return}
|
|
90
|
+
item.acknowledged=true;flush();emit()
|
|
91
|
+
},
|
|
92
|
+
releaseFailed(meta) {
|
|
93
|
+
if(status==='recording')invalidate(meta?.failure?.possiblyApplied===false?'ROUTINE_DEMONSTRATION_FAILED':'ROUTINE_RELEASE_UNCONFIRMED')
|
|
94
|
+
},
|
|
74
95
|
metadataChanged(metadata) {
|
|
75
96
|
if (status !== 'recording' || !metadata?.width) return
|
|
76
97
|
let next
|
|
@@ -88,7 +109,7 @@ export function createAcknowledgedRoutineRecorder({ now = Date.now, onChange } =
|
|
|
88
109
|
if (pending.size) fail('ROUTINE_INPUT_PENDING')
|
|
89
110
|
if (!inputReleased) fail('ROUTINE_RELEASE_REQUIRED')
|
|
90
111
|
const value = normalizeRoutine({ name, description, steps, recording: { source: 'desktop-session', display,
|
|
91
|
-
recordedAt: startedAt, durationMs: Math.max(0, now() - startedAt), acknowledgedInputs: steps.length,
|
|
112
|
+
recordedAt: startedAt, durationMs: Math.max(0, now() - startedAt), acknowledgedInputs: steps.filter(step=>step.action==='input').length,
|
|
92
113
|
...(coalescedMoves ? { observedInputs, pointerSampleMs: ROUTINE_POINTER_SAMPLE_MS } : {}) } })
|
|
93
114
|
status = 'finished'; emit(); return clone(value)
|
|
94
115
|
},
|