@harness-mix/cli 0.1.11 → 0.2.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/CHANGELOG.md +16 -0
- package/NOTICE +16 -2
- package/README.md +265 -3
- package/docs/cross-platform.md +3 -3
- package/docs/multi-agent-collaboration.md +37 -23
- package/docs/native-acp.md +2 -2
- package/docs/native-codex.md +5 -5
- package/docs/{superpowers/specs → specs}/2026-09-13-native-hardening-design.md +2 -2
- package/docs/storage-verification-design.md +5 -5
- package/licenses/lobe-icons-MIT.txt +21 -0
- package/output/native-build/desktop-controller.mjs +24 -24
- package/output/native-build/harness-mix-secret.exe +0 -0
- package/output/native-build/harness-mix-shim.exe +0 -0
- package/output/native-build/renderer-extension.js +1325 -658
- package/package.json +2 -2
- package/scripts/adapters-test.cjs +9 -1
- package/scripts/antigravity-adapter-test.cjs +381 -284
- package/scripts/build-native.cjs +64 -46
- package/scripts/codex-accounts-test.cjs +1 -1
- package/scripts/codex-accounts-ui-smoke.cjs +1 -1
- package/scripts/collaboration-recovery-test.cjs +11 -2
- package/scripts/collaboration-test.cjs +114 -27
- package/scripts/collaboration-ui-smoke.cjs +45 -8
- package/scripts/concurrency-layers-test.cjs +5 -0
- package/scripts/diagnostics.cjs +1 -1
- package/scripts/e2e-native-host.cjs +13 -13
- package/scripts/error-kind-test.cjs +3 -3
- package/scripts/integrations-test.cjs +2 -2
- package/scripts/integrations-ui-smoke.cjs +6 -6
- package/scripts/native-config-test.cjs +6 -6
- package/scripts/native-job-object-test.cjs +1 -1
- package/scripts/native-protocol-test.cjs +44 -38
- package/scripts/native-rollback-test.cjs +1 -1
- package/scripts/native-secret-test.cjs +1 -1
- package/scripts/native-shim-test.cjs +1 -1
- package/scripts/native-sidebar-test.cjs +4 -4
- package/scripts/native-ui-smoke.cjs +12 -12
- package/scripts/pets-test.cjs +223 -3
- package/scripts/platform-test.cjs +6 -6
- package/scripts/publish-native-package.cjs +1 -1
- package/scripts/test-live-mentions.cjs +2 -2
- package/scripts/workspace-test.cjs +4 -2
- package/src/assets/icons/PROVENANCE.md +92 -0
- package/src/main/adapters/antigravity.js +1415 -1368
- package/src/main/adapters/claude.js +1 -1
- package/src/main/adapters/codex-app-server.js +1 -1
- package/src/main/adapters/dsh.js +1 -1
- package/src/main/adapters/index.js +1 -1
- package/src/main/adapters/pi-usage.js +2 -2
- package/src/main/harness-adapter/error-kind.js +1 -1
- package/src/main/harness-adapter/manifest.js +7 -1
- package/src/main/host/collaboration-tools.js +8 -1
- package/src/main/host/collaboration-worktree.js +27 -1
- package/src/main/host/collaboration.js +348 -15
- package/src/main/host/runtime.js +23 -15
- package/src/main/native/codex-accounts.js +1 -1
- package/src/main/native/config.js +9 -9
- package/src/main/native/host.js +1 -1
- package/src/main/native/launcher.js +10 -8
- package/src/main/native/pets.js +188 -49
- package/src/main/native/platform.js +19 -9
- package/src/main/native/protocol.js +83 -80
- package/src/main/native/rs/crates/appx/Cargo.toml +1 -1
- package/src/main/native/rs/crates/secret/Cargo.toml +1 -1
- package/src/main/native/rs/crates/secret/src/main.rs +3 -3
- package/src/main/native/rs/crates/shim/Cargo.toml +1 -1
- package/src/main/native/rs/crates/shim/src/main.rs +2 -2
- package/src/main/native/thread-list.js +1 -1
- package/src/main/shared-contracts/capability.js +3 -1
- package/src/main/workspace/core-review.js +4 -3
- package/src/native-ui/README.md +2 -2
- package/src/native-ui/desktop-control/dist/contract-audit.js +4 -4
- package/src/native-ui/desktop-control/dist/index.d.ts +1 -1
- package/src/native-ui/desktop-control/dist/index.js +2 -2
- package/src/native-ui/desktop-control/dist/index.js.map +1 -1
- package/src/native-ui/desktop-control/dist/main-process-title-policy.js +5 -5
- package/src/native-ui/desktop-control/dist/main-process-title-policy.js.map +1 -1
- package/src/native-ui/desktop-control/dist/production-controller.js +3 -3
- package/src/native-ui/desktop-control/dist/production-controller.js.map +1 -1
- package/src/native-ui/desktop-control/dist/release-main.js +1 -1
- package/src/native-ui/desktop-control/dist/release-main.js.map +1 -1
- package/src/native-ui/desktop-control/dist/renderer-cdp-control-session.js +1 -1
- package/src/native-ui/desktop-control/dist/renderer-cdp-control-session.js.map +1 -1
- package/src/native-ui/desktop-control/dist/renderer-control-session.js +3 -3
- package/src/native-ui/desktop-control/dist/renderer-control-session.js.map +1 -1
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.js +25 -25
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.js.map +1 -1
- package/src/native-ui/desktop-control/dist/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/desktop-control/package.json +2 -2
- package/src/native-ui/desktop-control/scripts/build-release.mjs +2 -2
- package/src/native-ui/desktop-control/src/contract-audit.ts +4 -4
- package/src/native-ui/desktop-control/src/index.ts +2 -2
- package/src/native-ui/desktop-control/src/main-process-title-policy.ts +5 -5
- package/src/native-ui/desktop-control/src/production-controller.ts +3 -3
- package/src/native-ui/desktop-control/src/release-main.ts +1 -1
- package/src/native-ui/desktop-control/src/renderer-cdp-control-session.ts +1 -1
- package/src/native-ui/desktop-control/src/renderer-control-session.ts +3 -3
- package/src/native-ui/desktop-control/src/renderer-draft-prewarm-runtime.ts +25 -25
- package/src/native-ui/desktop-control/test/index.test.ts +1 -1
- package/src/native-ui/desktop-control/test/production-controller.test.ts +1 -1
- package/src/native-ui/desktop-control/test/renderer-cdp-control-session.test.ts +1 -1
- package/src/native-ui/desktop-control/test/renderer-draft-prewarm-policy.test.ts +43 -43
- package/src/native-ui/renderer-extension/dist/types/agent-group-preference.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/agent-group-preference.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/agent-selection-state.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/agent-selection-state.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/contract-audit.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/contract-audit.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/index.d.ts +3 -1
- package/src/native-ui/renderer-extension/dist/types/index.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/production-entry.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/production-entry.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-agent-picker.d.ts +2 -2
- package/src/native-ui/renderer-extension/dist/types/renderer-agent-picker.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts +3 -3
- package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-codex-account-options.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-codex-account-options.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-codex-account-state.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-codex-account-state.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-collab-cards.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-collab-cards.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-composer-dom.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-composer-dom.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-credits-control.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-credits-control.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-fork-control.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-fork-control.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-command-claim.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-command-claim.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-command-control.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-command-control.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-handoff.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-handoff.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-localization.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-localization.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-mentions.d.ts +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-mentions.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts +36 -31
- package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-model-picker.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-model-picker.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-new-thread-preference.d.ts +2 -2
- package/src/native-ui/renderer-extension/dist/types/renderer-new-thread-preference.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-permission-mode-picker.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-permission-mode-picker.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-permission-mode-preference.d.ts +2 -2
- package/src/native-ui/renderer-extension/dist/types/renderer-permission-mode-preference.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-request-sender.d.ts +18 -2
- package/src/native-ui/renderer-extension/dist/types/renderer-request-sender.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-session-import-client.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-session-import-client.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-sidebar-agent-icons.d.ts +2 -2
- package/src/native-ui/renderer-extension/dist/types/renderer-sidebar-agent-icons.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-team-cards.d.ts +58 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-team-cards.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-transcript-dom.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-trigger-chip-style.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-trigger-chip-style.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-usage-control.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-usage-control.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-list.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-list.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-page.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-page.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-usage.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-usage.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/connections-page.d.ts +3 -3
- package/src/native-ui/renderer-extension/dist/types/settings/connections-page.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/harness-accounts.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/harness-accounts.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts +4 -4
- package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/pet-market.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/session-import-list-controls.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/session-import-list-controls.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/session-import-page.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/session-import-page.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/shell.d.ts +2 -2
- package/src/native-ui/renderer-extension/dist/types/settings/shell.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/trigger.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/trigger.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/renderer-extension/dist/types/versioned-renderer-adapter.d.ts +18 -18
- package/src/native-ui/renderer-extension/dist/types/versioned-renderer-adapter.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/package.json +2 -2
- package/src/native-ui/renderer-extension/src/agent-group-preference.ts +1 -1
- package/src/native-ui/renderer-extension/src/agent-selection-state.ts +31 -16
- package/src/native-ui/renderer-extension/src/audit-entry.ts +1 -1
- package/src/native-ui/renderer-extension/src/contract-audit.ts +4 -4
- package/src/native-ui/renderer-extension/src/index.ts +4 -2
- package/src/native-ui/renderer-extension/src/install-renderer-binding.ts +2 -2
- package/src/native-ui/renderer-extension/src/production-entry.ts +3 -3
- package/src/native-ui/renderer-extension/src/renderer-agent-picker.ts +7 -7
- package/src/native-ui/renderer-extension/src/renderer-binding-probe.ts +112 -89
- package/src/native-ui/renderer-extension/src/renderer-codex-account-options.ts +1 -1
- package/src/native-ui/renderer-extension/src/renderer-codex-account-state.ts +1 -1
- package/src/native-ui/renderer-extension/src/renderer-collab-cards.ts +1 -1
- package/src/native-ui/renderer-extension/src/renderer-composer-dom.ts +12 -12
- package/src/native-ui/renderer-extension/src/renderer-credits-control.ts +7 -7
- package/src/native-ui/renderer-extension/src/renderer-external-steering.ts +3 -3
- package/src/native-ui/renderer-extension/src/renderer-fork-control.ts +1 -1
- package/src/native-ui/renderer-extension/src/renderer-harness-command-claim.ts +1 -1
- package/src/native-ui/renderer-extension/src/renderer-harness-command-control.ts +3 -3
- package/src/native-ui/renderer-extension/src/renderer-harness-handoff.ts +1 -1
- package/src/native-ui/renderer-extension/src/renderer-harness-localization.ts +1 -1
- package/src/native-ui/renderer-extension/src/renderer-harness-mentions.ts +4 -0
- package/src/native-ui/renderer-extension/src/renderer-integrations-client.ts +5 -5
- package/src/native-ui/renderer-extension/src/renderer-model-client.ts +63 -42
- package/src/native-ui/renderer-extension/src/renderer-model-picker.ts +16 -16
- package/src/native-ui/renderer-extension/src/renderer-new-thread-preference.ts +2 -2
- package/src/native-ui/renderer-extension/src/renderer-permission-mode-picker.ts +3 -3
- package/src/native-ui/renderer-extension/src/renderer-permission-mode-preference.ts +2 -2
- package/src/native-ui/renderer-extension/src/renderer-request-sender.ts +46 -3
- package/src/native-ui/renderer-extension/src/renderer-session-import-client.ts +4 -4
- package/src/native-ui/renderer-extension/src/renderer-sidebar-agent-icons.ts +24 -20
- package/src/native-ui/renderer-extension/src/renderer-team-cards.ts +312 -0
- package/src/native-ui/renderer-extension/src/renderer-transcript-dom.ts +1 -1
- package/src/native-ui/renderer-extension/src/renderer-trigger-chip-style.ts +2 -2
- package/src/native-ui/renderer-extension/src/renderer-usage-control.ts +4 -4
- package/src/native-ui/renderer-extension/src/settings/accounts-list.ts +1 -1
- package/src/native-ui/renderer-extension/src/settings/accounts-page.ts +1 -1
- package/src/native-ui/renderer-extension/src/settings/accounts-usage.ts +1 -1
- package/src/native-ui/renderer-extension/src/settings/connections-page.ts +7 -7
- package/src/native-ui/renderer-extension/src/settings/harness-accounts.ts +1 -1
- package/src/native-ui/renderer-extension/src/settings/icons.ts +2 -2
- package/src/native-ui/renderer-extension/src/settings/pages.ts +11 -11
- package/src/native-ui/renderer-extension/src/settings/pet-market.css +33 -0
- package/src/native-ui/renderer-extension/src/settings/pet-market.ts +173 -91
- package/src/native-ui/renderer-extension/src/settings/pets-client.ts +5 -5
- package/src/native-ui/renderer-extension/src/settings/release-notes.ts +1 -1
- package/src/native-ui/renderer-extension/src/settings/session-import-list-controls.ts +1 -1
- package/src/native-ui/renderer-extension/src/settings/session-import-page.ts +1 -1
- package/src/native-ui/renderer-extension/src/settings/shell.css +15 -15
- package/src/native-ui/renderer-extension/src/settings/shell.ts +11 -11
- package/src/native-ui/renderer-extension/src/settings/skin-runtime.ts +17 -17
- package/src/native-ui/renderer-extension/src/settings/trigger.ts +1 -1
- package/src/native-ui/renderer-extension/src/versioned-renderer-adapter.ts +20 -20
- package/src/native-ui/renderer-extension/test/agent-selection-state.test.ts +60 -1
- package/src/native-ui/renderer-extension/test/codebuddy-migration.test.ts +1 -1
- package/src/native-ui/renderer-extension/test/renderer-agent-picker.test.ts +1 -1
- package/src/native-ui/renderer-extension/test/renderer-binding-probe-host-catalog.test.ts +15 -9
- package/src/native-ui/renderer-extension/test/renderer-binding-probe.test.ts +15 -15
- package/src/native-ui/renderer-extension/test/renderer-codex-account-state.test.ts +1 -1
- package/src/native-ui/renderer-extension/test/renderer-composer-permissions.test.ts +4 -4
- package/src/native-ui/renderer-extension/test/renderer-external-steering-rpc.test.ts +2 -2
- package/src/native-ui/renderer-extension/test/renderer-external-steering.test.ts +3 -3
- package/src/native-ui/renderer-extension/test/renderer-fork-control.test.ts +4 -4
- package/src/native-ui/renderer-extension/test/renderer-harness-command-claim.test.ts +1 -1
- package/src/native-ui/renderer-extension/test/renderer-harness-command-control.test.ts +1 -1
- package/src/native-ui/renderer-extension/test/renderer-model-client.test.ts +12 -11
- package/src/native-ui/renderer-extension/test/renderer-model-picker.test.ts +1 -1
- package/src/native-ui/renderer-extension/test/renderer-native-thread-ownership.test.ts +6 -6
- package/src/native-ui/renderer-extension/test/renderer-new-thread-preference.test.ts +1 -1
- package/src/native-ui/renderer-extension/test/renderer-permission-mode-picker.test.ts +1 -1
- package/src/native-ui/renderer-extension/test/renderer-permission-mode-preference.test.ts +1 -1
- package/src/native-ui/renderer-extension/test/renderer-request-timeout.test.ts +80 -0
- package/src/native-ui/renderer-extension/test/renderer-settings-lifecycle.test.ts +2 -2
- package/src/native-ui/renderer-extension/test/renderer-sidebar-agent-icons.test.ts +10 -3
- package/src/native-ui/renderer-extension/test/renderer-unsupported-methods.test.ts +1 -1
- package/src/native-ui/renderer-extension/test/settings/pages.test.ts +5 -5
- package/src/native-ui/renderer-extension/test/settings/release-notes.test.ts +9 -9
- package/src/native-ui/renderer-extension/test/versioned-renderer-adapter.test.ts +3 -3
- package/src/native-ui/shared-contracts/dist/errors.d.ts +2 -2
- package/src/native-ui/shared-contracts/dist/errors.d.ts.map +1 -1
- package/src/native-ui/shared-contracts/dist/errors.js +1 -1
- package/src/native-ui/shared-contracts/dist/errors.js.map +1 -1
- package/src/native-ui/shared-contracts/dist/harness-models.d.ts +40 -0
- package/src/native-ui/shared-contracts/dist/harness-models.d.ts.map +1 -1
- package/src/native-ui/shared-contracts/dist/harness-models.js +23 -2
- package/src/native-ui/shared-contracts/dist/harness-models.js.map +1 -1
- package/src/native-ui/shared-contracts/dist/harness-route.d.ts +1 -1
- package/src/native-ui/shared-contracts/dist/harness-route.d.ts.map +1 -1
- package/src/native-ui/shared-contracts/dist/harness-route.js +1 -1
- package/src/native-ui/shared-contracts/dist/harness-route.js.map +1 -1
- package/src/native-ui/shared-contracts/dist/harness-session-import.d.ts +1 -1
- package/src/native-ui/shared-contracts/dist/harness-session-import.d.ts.map +1 -1
- package/src/native-ui/shared-contracts/dist/harness-session-import.js +1 -1
- package/src/native-ui/shared-contracts/dist/harness-session-import.js.map +1 -1
- package/src/native-ui/shared-contracts/dist/index.d.ts +3 -3
- package/src/native-ui/shared-contracts/dist/index.d.ts.map +1 -1
- package/src/native-ui/shared-contracts/dist/index.js +2 -2
- package/src/native-ui/shared-contracts/dist/index.js.map +1 -1
- package/src/native-ui/shared-contracts/dist/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/shared-contracts/dist/updates.js +1 -1
- package/src/native-ui/shared-contracts/dist/updates.js.map +1 -1
- package/src/native-ui/shared-contracts/package.json +1 -1
- package/src/native-ui/shared-contracts/src/errors.ts +3 -3
- package/src/native-ui/shared-contracts/src/harness-models.ts +23 -2
- package/src/native-ui/shared-contracts/src/harness-route.ts +1 -1
- package/src/native-ui/shared-contracts/src/harness-session-import.ts +1 -1
- package/src/native-ui/shared-contracts/src/index.ts +3 -3
- package/src/native-ui/shared-contracts/src/updates.ts +1 -1
- package/src/native-ui/shared-contracts/test/browser-bundle.test.ts +3 -3
- package/src/native-ui/shared-contracts/test/deepseek-modern-sessions.test.ts +1 -1
- package/src/native-ui/shared-contracts/test/errors.test.ts +9 -9
- package/src/native-ui/shared-contracts/test/harness-commands.test.ts +1 -1
- package/src/native-ui/shared-contracts/test/harness-models.test.ts +3 -3
- package/src/native-ui/shared-contracts/test/harness-permission-modes.test.ts +1 -1
- package/src/native-ui/shared-contracts/test/harness-plugins.test.ts +1 -1
- package/src/native-ui/shared-contracts/test/harness-session-import.test.ts +1 -1
- package/src/native-ui/shared-contracts/test/index.test.ts +3 -3
- package/src/native-ui/shared-contracts/test/json-rpc.test.ts +1 -1
- package/src/native-ui/shared-contracts/test/thread-usage.test.ts +1 -1
- package/src/native-ui/shared-contracts/test/updates.test.ts +4 -4
- /package/licenses/{codex-host-MIT.txt → bytepioneer-MIT.txt} +0 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { collaborationIcon } from './collaboration-icon.js';
|
|
2
|
+
|
|
3
|
+
export interface TeamMemberPayload { id: string; name: string; role: string; agent: string; childId?: string; child_thread_id?: string; display_status?: string }
|
|
4
|
+
export interface TeamTaskPayload { id: string; title: string; assignee: string; dependsOn?: string[]; status: string }
|
|
5
|
+
export interface TeamMessagePayload { id: string; fromName?: string; from: string; to: string; body: string; at: number }
|
|
6
|
+
export interface TeamCardPayload {
|
|
7
|
+
action?: string; team_id: string; name: string; goal: string; status: string; lead_thread_id?: string;
|
|
8
|
+
lead?: TeamMemberPayload; members: TeamMemberPayload[]; tasks: TeamTaskPayload[]; messages: TeamMessagePayload[]; updated_at?: number;
|
|
9
|
+
}
|
|
10
|
+
interface TeamSnapshot { id: string; action: string; at: number; team: TeamCardPayload }
|
|
11
|
+
interface TeamInspection { team: TeamCardPayload; snapshots: TeamSnapshot[] }
|
|
12
|
+
interface ActiveTeamContext { threadId: string; anchor: Element }
|
|
13
|
+
interface TeamCardOptions {
|
|
14
|
+
inspectTeam?: (threadId: string, teamId?: string) => Promise<unknown>;
|
|
15
|
+
openThread?: (threadId: string) => Promise<unknown> | unknown;
|
|
16
|
+
activeThread?: () => ActiveTeamContext | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function jsonObjectAt(value: string, start: number): string | null {
|
|
20
|
+
let depth = 0, quoted = false, escaped = false;
|
|
21
|
+
for (let index = start; index < value.length; index++) {
|
|
22
|
+
const char = value[index];
|
|
23
|
+
if (quoted) { if (escaped) escaped = false; else if (char === '\\') escaped = true; else if (char === '"') quoted = false; continue; }
|
|
24
|
+
if (char === '"') quoted = true; else if (char === '{') depth++; else if (char === '}' && --depth === 0) return value.slice(start, index + 1);
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function isTeam(value: unknown): value is TeamCardPayload {
|
|
29
|
+
if (!value || typeof value !== 'object') return false;
|
|
30
|
+
const team = value as Partial<TeamCardPayload>;
|
|
31
|
+
return typeof team.team_id === 'string' && typeof team.name === 'string' && Array.isArray(team.members) && Array.isArray(team.tasks) && Array.isArray(team.messages);
|
|
32
|
+
}
|
|
33
|
+
export function parseTeamPayload(value: string): TeamCardPayload | null {
|
|
34
|
+
if (!value.includes('"team_id"') || !value.includes('"members"') || !value.includes('"tasks"')) return null;
|
|
35
|
+
for (let start = value.indexOf('{'); start >= 0; start = value.indexOf('{', start + 1)) {
|
|
36
|
+
const candidate = jsonObjectAt(value, start); if (!candidate?.includes('"team_id"')) continue;
|
|
37
|
+
try { const parsed: unknown = JSON.parse(candidate); if (isTeam(parsed)) return parsed; } catch {}
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
function parseInspection(value: unknown): TeamInspection | null {
|
|
42
|
+
if (!value || typeof value !== 'object') return null;
|
|
43
|
+
const result = value as Partial<TeamInspection>;
|
|
44
|
+
if (!isTeam(result.team) || !Array.isArray(result.snapshots)) return null;
|
|
45
|
+
return { team: result.team, snapshots: result.snapshots.filter(entry => entry && typeof entry === 'object' && isTeam((entry as TeamSnapshot).team)) as TeamSnapshot[] };
|
|
46
|
+
}
|
|
47
|
+
const stateColor = (status?: string) => ({ ready: '#8b8b8b', pending: '#8b8b8b', working: '#2878e3', in_progress: '#2878e3', active: '#2878e3', completed: '#1f9d68', blocked: '#c17022', failed: '#d14343', interrupted: '#c17022' }[status ?? ''] ?? '#8b8b8b');
|
|
48
|
+
const stateLabel = (status?: string) => ({ ready: '就绪', pending: '待开始', working: '工作中', in_progress: '进行中', completed: '已完成', blocked: '等待依赖', failed: '失败', interrupted: '已中断', active: '协作中' }[status ?? ''] ?? status ?? '未知');
|
|
49
|
+
const actionLabel = (action?: string) => ({ team_created: '团队建立', task_assigned: '任务分配', task_updated: '任务更新', message_sent: '团队通信', task_started: '开始执行', member_session_ready: '会话就绪', task_settled: '任务结算', task_failed: '任务失败', task_cancelled: '任务取消', task_interrupted: '任务中断', task_resumed: '恢复执行', task_followup: '继续执行' }[action ?? ''] ?? action ?? '实时状态');
|
|
50
|
+
const memberPalette = ['#3b82f6', '#f59e0b', '#10b981', '#8b5cf6', '#f97316', '#ec4899', '#14b8a6', '#6366f1'];
|
|
51
|
+
const memberColor = (index: number) => memberPalette[index % memberPalette.length];
|
|
52
|
+
function el<K extends keyof HTMLElementTagNameMap>(tag: K, className?: string, css?: string): HTMLElementTagNameMap[K] { const node = document.createElement(tag); if (className) node.className = className; if (css) node.style.cssText = css; return node; }
|
|
53
|
+
function text(tag: keyof HTMLElementTagNameMap, value: string, css?: string) { const node = el(tag, undefined, css); node.textContent = value; return node; }
|
|
54
|
+
|
|
55
|
+
const TEAM_STYLE = `
|
|
56
|
+
.harness-mix-team-scroll{scrollbar-width:none}
|
|
57
|
+
.harness-mix-team-scroll::-webkit-scrollbar{display:none}
|
|
58
|
+
.harness-mix-team-workbench{container-type:inline-size}
|
|
59
|
+
.harness-mix-team-body{display:grid;grid-template-columns:minmax(0,1fr) clamp(228px,24vw,300px);min-height:0;overflow:hidden}
|
|
60
|
+
.harness-mix-team-lanes{display:flex;gap:16px;min-height:0;box-sizing:border-box;overflow-x:auto;overflow-y:hidden;padding:18px 16px 10px;justify-content:safe center;scroll-snap-type:x proximity}
|
|
61
|
+
.harness-mix-team-lane{position:relative;flex:1 1 0;min-width:216px;max-width:300px;display:flex;flex-direction:column;min-height:0;padding-top:16px;scroll-snap-align:start}
|
|
62
|
+
.harness-mix-team-lane::before{content:'';position:absolute;top:0;left:50%;height:16px;border-left:2px dashed color-mix(in srgb,currentColor 20%,transparent)}
|
|
63
|
+
.harness-mix-team-lane::after{content:'';position:absolute;top:0;left:-9px;right:-9px;border-top:2px dashed color-mix(in srgb,currentColor 20%,transparent)}
|
|
64
|
+
.harness-mix-team-lane:first-child::after{left:calc(50% - 1px)}
|
|
65
|
+
.harness-mix-team-lane:last-child::after{right:calc(50% - 1px)}
|
|
66
|
+
.harness-mix-team-lane:only-child::after{display:none}
|
|
67
|
+
.harness-mix-team-task-list{display:flex;flex-direction:column;gap:6px;flex:1;min-height:0;overflow-y:auto;padding:2px 8px 8px;scrollbar-width:none}
|
|
68
|
+
.harness-mix-team-task-list::-webkit-scrollbar{display:none}
|
|
69
|
+
.harness-mix-team-feed{display:flex;flex-direction:column;min-height:0;min-width:0;border-left:1px solid color-mix(in srgb,currentColor 9%,transparent)}
|
|
70
|
+
.harness-mix-team-feed-list{flex:1;min-height:0;overflow-y:auto;padding:0 12px 10px;scrollbar-width:thin}
|
|
71
|
+
.harness-mix-team-clamp2{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
|
|
72
|
+
.harness-mix-team-clickable{transition:border-color .15s ease,box-shadow .15s ease}
|
|
73
|
+
.harness-mix-team-clickable:hover{border-color:color-mix(in srgb,currentColor 28%,transparent)!important;box-shadow:0 4px 14px color-mix(in srgb,#000 10%,transparent)!important}
|
|
74
|
+
@container (max-width:720px){
|
|
75
|
+
.harness-mix-team-body{grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0,1fr) auto}
|
|
76
|
+
.harness-mix-team-feed{border-left:0;border-top:1px solid color-mix(in srgb,currentColor 9%,transparent);max-height:136px}
|
|
77
|
+
}`;
|
|
78
|
+
|
|
79
|
+
function metrics(payload: TeamCardPayload) {
|
|
80
|
+
const node = el('div', 'harness-mix-team-metrics', 'display:flex;gap:18px;align-items:center');
|
|
81
|
+
const values: Array<[string, string, string]> = [[`${payload.tasks.filter(task => task.status === 'completed').length}/${payload.tasks.length}`, '完成', '#1f9d68'], [`${payload.tasks.filter(task => task.status === 'in_progress').length}`, '进行中', '#2878e3'], [`${payload.tasks.filter(task => task.status === 'blocked').length}`, '等待', '#c17022']];
|
|
82
|
+
for (const [value, label, color] of values) { const item = el('span', undefined, 'display:grid;justify-items:center;line-height:1.1'); item.append(text('strong', value, `font-size:14px;color:${color}`), text('small', label, 'font-size:10px;margin-top:3px;opacity:.55')); node.append(item); }
|
|
83
|
+
return node;
|
|
84
|
+
}
|
|
85
|
+
const completionOf = (payload: TeamCardPayload) => { const total = payload.tasks.length; const done = payload.tasks.filter(task => task.status === 'completed').length; return { done, total, pct: total ? Math.round((done / total) * 100) : 0 }; };
|
|
86
|
+
function statItem(value: string, label: string, color: string) {
|
|
87
|
+
const item = el('span', undefined, 'display:grid;justify-items:center;line-height:1.1;min-width:30px');
|
|
88
|
+
item.append(text('strong', value, `font-size:15px;font-weight:750;color:${color}`), text('small', label, 'font-size:8.5px;margin-top:2px;opacity:.55;white-space:nowrap'));
|
|
89
|
+
return item;
|
|
90
|
+
}
|
|
91
|
+
function headerStats(payload: TeamCardPayload) {
|
|
92
|
+
const items = [
|
|
93
|
+
statItem(String(payload.tasks.length), '任务总数', 'inherit'),
|
|
94
|
+
statItem(String(payload.tasks.filter(task => task.status === 'in_progress').length), '进行中', '#2878e3'),
|
|
95
|
+
statItem(String(completionOf(payload).done), '已完成', '#1f9d68'),
|
|
96
|
+
statItem(String(payload.tasks.filter(task => task.status === 'blocked' || task.status === 'pending').length), '等待', '#c17022'),
|
|
97
|
+
];
|
|
98
|
+
const failed = payload.tasks.filter(task => task.status === 'failed').length;
|
|
99
|
+
if (failed) items.push(statItem(String(failed), '失败', '#d14343'));
|
|
100
|
+
return items;
|
|
101
|
+
}
|
|
102
|
+
const pillCss = (status?: string) => { const color = stateColor(status); return `font-size:8px;font-weight:750;padding:2px 7px;border-radius:99px;white-space:nowrap;color:${color};background:color-mix(in srgb,${color} 15%,transparent)`; };
|
|
103
|
+
function statusPill(status?: string) { return text('span', stateLabel(status), pillCss(status)); }
|
|
104
|
+
function statusDot(status: string | undefined, size = 7) { return el('span', undefined, `width:${size}px;height:${size}px;border-radius:50%;flex:none;background:${stateColor(status)}`); }
|
|
105
|
+
|
|
106
|
+
function renderBoard(payload: TeamCardPayload, openThread?: TeamCardOptions['openThread']) {
|
|
107
|
+
const board = el('main', 'harness-mix-team-board harness-mix-team-body', 'height:100%;min-height:0;color:inherit');
|
|
108
|
+
const lead = payload.lead ?? { id: 'lead', name: 'Team Lead', role: '协调与验收', agent: 'codex', display_status: 'working' };
|
|
109
|
+
const tasksOf = (memberId: string) => payload.tasks.filter(task => task.assignee === memberId);
|
|
110
|
+
|
|
111
|
+
const leadCard = () => {
|
|
112
|
+
const button = el('button', 'harness-mix-team-lead', 'appearance:none;border:1px solid color-mix(in srgb,#c35b24 34%,transparent);background:color-mix(in srgb,#c35b24 7%,transparent);box-shadow:0 2px 10px color-mix(in srgb,#c35b24 10%,transparent);color:inherit;font:inherit;display:flex;align-items:center;gap:10px;padding:8px 16px;border-radius:13px;cursor:default'); button.type = 'button'; button.dataset.agent = lead.agent;
|
|
113
|
+
const childId = lead.childId ?? lead.child_thread_id;
|
|
114
|
+
if (childId && openThread) { button.style.cursor = 'pointer'; button.classList.add('harness-mix-team-clickable'); button.title = `打开 ${lead.name} 的原生 Harness 会话`; button.addEventListener('click', () => void openThread(childId)); }
|
|
115
|
+
const avatar = el('span', undefined, 'position:relative;display:grid;place-items:center;width:42px;height:42px;flex:none;border-radius:12px;border:2px solid color-mix(in srgb,#c35b24 45%,transparent);background:Canvas');
|
|
116
|
+
avatar.append(collaborationIcon(lead.agent, lead.name, 32), text('span', '👑', 'position:absolute;top:-12px;right:-10px;font-size:13px;filter:drop-shadow(0 1px 1px rgb(0 0 0/.25))'));
|
|
117
|
+
const copy = el('span', undefined, 'display:grid;min-width:0;text-align:left;line-height:1.3');
|
|
118
|
+
copy.append(text('span', '主导者', 'font-size:8.5px;font-weight:800;letter-spacing:.14em;color:#c35b24'), text('strong', lead.name, 'font-size:13px;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap'));
|
|
119
|
+
const status = el('span', undefined, 'display:flex;align-items:center;gap:5px;margin-top:1px');
|
|
120
|
+
status.append(statusDot(lead.display_status), text('span', stateLabel(lead.display_status), `font-size:9px;color:${stateColor(lead.display_status)}`));
|
|
121
|
+
copy.append(status); button.append(avatar, copy); return button;
|
|
122
|
+
};
|
|
123
|
+
const memberCard = (member: TeamMemberPayload, index: number) => {
|
|
124
|
+
const color = memberColor(index), assigned = tasksOf(member.id), done = assigned.filter(task => task.status === 'completed').length;
|
|
125
|
+
const button = el('button', 'harness-mix-team-member', 'appearance:none;width:100%;box-sizing:border-box;border:1px solid color-mix(in srgb,currentColor 10%,transparent);background:color-mix(in srgb,Canvas 92%,transparent);box-shadow:0 2px 8px color-mix(in srgb,#000 6%,transparent);color:inherit;font:inherit;display:grid;grid-template-columns:40px minmax(0,1fr);align-items:center;gap:9px;padding:9px 10px;border-radius:12px;text-align:left;cursor:default'); button.type = 'button'; button.dataset.agent = member.agent;
|
|
126
|
+
const childId = member.childId ?? member.child_thread_id;
|
|
127
|
+
if (childId && openThread) { button.style.cursor = 'pointer'; button.classList.add('harness-mix-team-clickable'); button.title = `打开 ${member.name} 的原生 Harness 会话`; button.addEventListener('click', () => void openThread(childId)); }
|
|
128
|
+
const avatar = el('span', undefined, `position:relative;display:grid;place-items:center;width:40px;height:40px;flex:none;border-radius:11px;border:2px solid ${color};background:Canvas`);
|
|
129
|
+
avatar.append(collaborationIcon(member.agent, member.name, 30), el('span', undefined, `position:absolute;right:-4px;bottom:-4px;width:11px;height:11px;box-sizing:border-box;border-radius:50%;background:${stateColor(member.display_status)};border:2.5px solid Canvas`));
|
|
130
|
+
const copy = el('span', undefined, 'display:grid;min-width:0;line-height:1.25;gap:2px');
|
|
131
|
+
copy.append(text('strong', member.name, 'font-size:12px;font-weight:680;overflow:hidden;text-overflow:ellipsis;white-space:nowrap'));
|
|
132
|
+
const status = el('span', undefined, 'display:flex;align-items:center;gap:5px;min-width:0');
|
|
133
|
+
status.append(text('span', stateLabel(member.display_status), `font-size:9px;color:${stateColor(member.display_status)};white-space:nowrap`), text('span', `${done}/${assigned.length}`, 'font-size:9px;font-weight:650;opacity:.5;margin-left:auto'));
|
|
134
|
+
copy.append(status);
|
|
135
|
+
const progress = el('progress'); progress.max = Math.max(1, assigned.length); progress.value = done; progress.style.cssText = `width:100%;height:4px;margin:0;accent-color:${color}`; progress.title = `${done}/${assigned.length} 个任务完成`;
|
|
136
|
+
copy.append(progress); button.append(avatar, copy); return button;
|
|
137
|
+
};
|
|
138
|
+
const laneOf = (member: TeamMemberPayload, index: number) => {
|
|
139
|
+
const color = memberColor(index), assigned = tasksOf(member.id), done = assigned.filter(task => task.status === 'completed').length;
|
|
140
|
+
const lane = el('div', 'harness-mix-team-lane'); lane.dataset.memberId = member.id;
|
|
141
|
+
lane.append(memberCard(member, index));
|
|
142
|
+
const column = el('div', undefined, `margin-top:9px;flex:1;min-height:0;display:flex;flex-direction:column;border-radius:12px;border:1px solid color-mix(in srgb,${color} 30%,transparent);background:color-mix(in srgb,${color} 7%,transparent)`);
|
|
143
|
+
const columnHead = el('div', undefined, 'display:flex;align-items:flex-start;gap:6px;padding:8px 10px 5px');
|
|
144
|
+
const role = text('span', member.role, 'font-size:9.5px;font-weight:650;line-height:1.35;min-width:0;opacity:.78'); role.className = 'harness-mix-team-clamp2'; role.title = member.role;
|
|
145
|
+
columnHead.append(el('span', undefined, `width:7px;height:7px;border-radius:2.5px;background:${color};flex:none;margin-top:3px`), role, text('span', `${done}/${assigned.length}`, `margin-left:auto;font-size:8.5px;font-weight:750;color:${color};background:color-mix(in srgb,${color} 15%,transparent);padding:1px 6px;border-radius:99px;white-space:nowrap`));
|
|
146
|
+
column.append(columnHead);
|
|
147
|
+
const list = el('div', 'harness-mix-team-task-list');
|
|
148
|
+
for (const task of assigned) {
|
|
149
|
+
const card = el('article', 'harness-mix-team-task', 'display:grid;gap:5px;padding:7px 8px;border-radius:9px;border:1px solid color-mix(in srgb,currentColor 8%,transparent);background:color-mix(in srgb,Canvas 94%,transparent)'); card.dataset.taskId = task.id; card.dataset.taskStatus = task.status;
|
|
150
|
+
const topRow = el('div', undefined, 'display:flex;align-items:center;gap:6px;min-width:0');
|
|
151
|
+
const pill = statusPill(task.status); pill.style.marginLeft = 'auto';
|
|
152
|
+
topRow.append(text('span', `#${task.id}`, 'font-family:ui-monospace,monospace;font-size:8px;opacity:.45;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:92px'), pill);
|
|
153
|
+
const title = text('div', task.title, 'font-size:10.5px;font-weight:620;line-height:1.35'); title.className = 'harness-mix-team-clamp2'; title.title = task.title;
|
|
154
|
+
card.append(topRow, title);
|
|
155
|
+
if ((task.dependsOn?.length ?? 0) > 0) card.append(text('div', `依赖 ${task.dependsOn!.length} 项任务`, 'font-size:8px;opacity:.45'));
|
|
156
|
+
list.append(card);
|
|
157
|
+
}
|
|
158
|
+
if (!assigned.length) list.append(text('div', '等待主导者分配任务', 'font-size:9px;opacity:.42;padding:10px 2px;text-align:center'));
|
|
159
|
+
column.append(list); lane.append(column); return lane;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const left = el('section', undefined, 'display:grid;grid-template-rows:auto minmax(0,1fr);min-width:0;min-height:0');
|
|
163
|
+
const org = el('div', 'harness-mix-team-org', 'display:flex;flex-direction:column;align-items:center;gap:5px;padding:10px 14px 0');
|
|
164
|
+
org.append(leadCard(), text('div', '任务拆解 · 进度协调 · 质量验收 · 最终交付', 'font-size:8.5px;opacity:.45;letter-spacing:.04em'));
|
|
165
|
+
const lanes = el('div', 'harness-mix-team-lanes harness-mix-team-scroll');
|
|
166
|
+
if (payload.members.length) payload.members.forEach((member, index) => lanes.append(laneOf(member, index)));
|
|
167
|
+
else lanes.append(text('div', '尚未添加团队成员。', 'font-size:11px;opacity:.5'));
|
|
168
|
+
left.append(org, lanes);
|
|
169
|
+
|
|
170
|
+
const feed = el('aside', 'harness-mix-team-feed');
|
|
171
|
+
const feedHead = el('div', undefined, 'display:flex;align-items:center;gap:6px;padding:11px 12px 8px;flex:none');
|
|
172
|
+
feedHead.append(text('span', '团队动态', 'font-size:10px;font-weight:720;letter-spacing:.06em;opacity:.55'), text('span', String(payload.messages.length), 'font-size:8.5px;font-weight:700;opacity:.6;background:color-mix(in srgb,currentColor 8%,transparent);border-radius:99px;padding:1px 7px'));
|
|
173
|
+
const feedList = el('div', 'harness-mix-team-feed-list');
|
|
174
|
+
for (const message of payload.messages.slice().reverse()) {
|
|
175
|
+
const item = el('article', undefined, 'padding:9px 0;border-top:1px solid color-mix(in srgb,currentColor 7%,transparent)');
|
|
176
|
+
const sender = message.from === 'lead' ? lead : payload.members.find(member => member.id === message.from), receiver = message.to === 'lead' ? lead : payload.members.find(member => member.id === message.to);
|
|
177
|
+
const route = el('div', undefined, 'display:flex;align-items:center;gap:5px;min-width:0;font-size:10.5px;font-weight:700');
|
|
178
|
+
if (sender) route.append(collaborationIcon(sender.agent, sender.name, 16));
|
|
179
|
+
route.append(text('span', message.fromName ?? sender?.name ?? message.from, 'opacity:.75;overflow:hidden;text-overflow:ellipsis;white-space:nowrap'), text('span', '→', 'opacity:.4;font-weight:400'));
|
|
180
|
+
if (receiver) route.append(collaborationIcon(receiver.agent, receiver.name, 16));
|
|
181
|
+
route.append(text('span', message.to === '*' ? '全体' : receiver?.name ?? message.to, 'opacity:.75;overflow:hidden;text-overflow:ellipsis;white-space:nowrap'));
|
|
182
|
+
if (message.at > 0) route.append(text('span', new Date(message.at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), 'margin-left:auto;font-size:8.5px;font-weight:400;opacity:.4;flex:none'));
|
|
183
|
+
item.append(route, text('div', message.body, 'font-size:11px;line-height:1.5;margin-top:4px;white-space:pre-wrap;overflow-wrap:anywhere;opacity:.88')); feedList.append(item);
|
|
184
|
+
}
|
|
185
|
+
if (!payload.messages.length) feedList.append(text('div', '成员交接、审查请求和结果会实时显示在这里。', 'font-size:10px;line-height:1.5;opacity:.45;padding:10px 0'));
|
|
186
|
+
feed.append(feedHead, feedList);
|
|
187
|
+
board.append(left, feed); return board;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function renderSummary(payload: TeamCardPayload, open: () => void) {
|
|
191
|
+
const panel = el('section', 'harness-mix-team-panel', 'box-sizing:border-box;border:1px solid color-mix(in srgb,currentColor 12%,transparent);border-radius:14px;background:color-mix(in srgb,Canvas 90%,transparent);box-shadow:0 8px 28px color-mix(in srgb,#000 7%,transparent);backdrop-filter:blur(18px);color:inherit;font:13px/1.4 system-ui,-apple-system,"Segoe UI",sans-serif;padding:12px 14px;display:grid;gap:10px;min-width:0'); panel.dataset.teamId = payload.team_id; panel.dataset.teamStatus = payload.status;
|
|
192
|
+
const top = el('div', undefined, 'display:flex;align-items:center;gap:12px;min-width:0');
|
|
193
|
+
const identity = el('div', undefined, 'display:grid;grid-template-columns:38px minmax(0,1fr);align-items:center;gap:10px;min-width:0;flex:1');
|
|
194
|
+
const lead = payload.lead ?? { id: 'lead', name: 'Team Lead', role: '协调与验收', agent: 'codex', display_status: 'working' };
|
|
195
|
+
const leadIcon = el('span', undefined, 'position:relative;display:grid;place-items:center;width:38px;height:38px;flex:none;border:2px solid color-mix(in srgb,#c35b24 40%,transparent);border-radius:11px;background:Canvas');
|
|
196
|
+
leadIcon.append(collaborationIcon(lead.agent, lead.name, 28), text('span', '👑', 'position:absolute;top:-11px;right:-9px;font-size:11px'));
|
|
197
|
+
const copy = el('span', undefined, 'display:grid;min-width:0;gap:1px'), eyebrow = text('span', `AGENT TEAM · ${stateLabel(payload.status)}`, `font-size:9px;font-weight:760;letter-spacing:.11em;color:${stateColor(payload.status)}`), title = text('strong', payload.name, 'font-size:13px;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap'); title.className = 'harness-mix-team-name';
|
|
198
|
+
const goal = text('span', payload.goal, 'font-size:10px;opacity:.5;overflow:hidden;text-overflow:ellipsis;white-space:nowrap'); goal.title = payload.goal;
|
|
199
|
+
copy.append(eyebrow, title, goal); identity.append(leadIcon, copy);
|
|
200
|
+
const button = el('button', 'harness-mix-team-open', 'border:1px solid color-mix(in srgb,currentColor 13%,transparent);border-radius:8px;background:color-mix(in srgb,currentColor 5%,transparent);color:inherit;font:650 11px system-ui;padding:7px 10px;cursor:pointer;white-space:nowrap'); button.type = 'button'; button.textContent = '展开详情'; button.addEventListener('click', open); top.append(identity, metrics(payload), button);
|
|
201
|
+
const members = el('div', 'harness-mix-team-summary-members harness-mix-team-scroll', 'display:flex;align-items:center;gap:6px;min-width:0;overflow-x:auto;padding-bottom:1px');
|
|
202
|
+
payload.members.forEach((member, index) => {
|
|
203
|
+
const color = memberColor(index);
|
|
204
|
+
const chip = el('span', undefined, 'display:grid;grid-template-columns:26px minmax(0,1fr);align-items:center;gap:7px;min-width:128px;max-width:190px;padding:5px 9px;border-radius:10px;border:1px solid color-mix(in srgb,currentColor 8%,transparent);background:color-mix(in srgb,currentColor 3%,transparent)'); chip.title = `${member.name} · ${member.role}`;
|
|
205
|
+
const icon = el('span', undefined, `position:relative;display:grid;place-items:center;width:26px;height:26px;flex:none;border-radius:8px;border:1.5px solid ${color};background:Canvas`);
|
|
206
|
+
icon.append(collaborationIcon(member.agent, member.name, 18), el('span', undefined, `position:absolute;right:-3px;bottom:-3px;width:8px;height:8px;box-sizing:border-box;border-radius:50%;background:${stateColor(member.display_status)};border:2px solid Canvas`));
|
|
207
|
+
const label = el('span', undefined, 'display:grid;min-width:0;line-height:1.2');
|
|
208
|
+
label.append(text('strong', member.name, 'font-size:10px;font-weight:650;overflow:hidden;text-overflow:ellipsis;white-space:nowrap'), text('span', member.role, 'font-size:8.5px;opacity:.52;overflow:hidden;text-overflow:ellipsis;white-space:nowrap'), text('span', stateLabel(member.display_status), `font-size:8.5px;color:${stateColor(member.display_status)}`));
|
|
209
|
+
chip.append(icon, label); members.append(chip);
|
|
210
|
+
});
|
|
211
|
+
const completed = payload.tasks.filter(task => task.status === 'completed').length, progress = el('progress'); progress.max = Math.max(1, payload.tasks.length); progress.value = completed; progress.style.cssText = 'width:72px;height:5px;accent-color:#1f9d68;margin-left:auto;flex:none'; progress.title = `${completed}/${payload.tasks.length} 个任务完成`; members.append(progress, text('span', `${completed}/${payload.tasks.length}`, 'font-size:9px;font-weight:700;opacity:.52;white-space:nowrap'));
|
|
212
|
+
panel.append(top, members); return panel;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function installTeamCards(options: TeamCardOptions = {}) {
|
|
216
|
+
if (typeof document === 'undefined') return { scan: () => {}, dispose: () => {} };
|
|
217
|
+
const style = el('style'); style.dataset.harnessMixTeamStyle = 'true'; style.textContent = TEAM_STYLE; (document.head || document.documentElement).append(style);
|
|
218
|
+
let disposed = false, workbench: HTMLElement | null = null, refreshTimer: ReturnType<typeof setInterval> | null = null, playTimer: ReturnType<typeof setInterval> | null = null, activeTimer: ReturnType<typeof setInterval> | null = null;
|
|
219
|
+
let activePanel: HTMLElement | null = null, activeSignature = '', activeThreadId = '', activeRefreshPending = false;
|
|
220
|
+
const signatures = new WeakMap<Element, string>();
|
|
221
|
+
const close = () => { workbench?.remove(); workbench = null; if (refreshTimer) clearInterval(refreshTimer); refreshTimer = null; if (playTimer) clearInterval(playTimer); playTimer = null; };
|
|
222
|
+
const open = (seed: TeamCardPayload) => {
|
|
223
|
+
if (workbench?.dataset.teamId === seed.team_id) { close(); return; }
|
|
224
|
+
close();
|
|
225
|
+
const source = [...document.querySelectorAll<HTMLElement>('.harness-mix-team-panel')].find(node => node.dataset.teamId === seed.team_id);
|
|
226
|
+
if (!source?.parentElement) return;
|
|
227
|
+
workbench = el('section', 'harness-mix-team-workbench', 'box-sizing:border-box;margin:8px 0 12px;border:1px solid color-mix(in srgb,currentColor 11%,transparent);border-radius:14px;background:color-mix(in srgb,Canvas 94%,transparent);box-shadow:0 12px 34px color-mix(in srgb,#000 8%,transparent);backdrop-filter:blur(20px);color:inherit;font:13px/1.4 system-ui,-apple-system,"Segoe UI",sans-serif;display:grid;grid-template-rows:auto minmax(0,1fr);height:clamp(340px,64vh,660px);min-height:300px;overflow:hidden');
|
|
228
|
+
workbench.dataset.teamId = seed.team_id; workbench.dataset.teamSource = source.dataset.teamSource ?? 'tool';
|
|
229
|
+
let current = seed, snapshots: TeamSnapshot[] = [], index = -1;
|
|
230
|
+
const header = el('header', undefined, 'display:flex;align-items:center;gap:14px;flex-wrap:wrap;row-gap:8px;min-height:56px;padding:8px 14px;border-bottom:1px solid color-mix(in srgb,currentColor 9%,transparent);background:color-mix(in srgb,currentColor 2%,transparent)');
|
|
231
|
+
const title = el('div', undefined, 'display:grid;gap:1px;min-width:150px;max-width:min(320px,30vw)');
|
|
232
|
+
const nameRow = el('div', undefined, 'display:flex;align-items:center;gap:7px;min-width:0');
|
|
233
|
+
const teamName = text('strong', current.name, 'font-size:13px;font-weight:750;overflow:hidden;text-overflow:ellipsis;white-space:nowrap'); teamName.className = 'harness-mix-team-name';
|
|
234
|
+
const teamPill = statusPill(current.status);
|
|
235
|
+
nameRow.append(teamName, teamPill);
|
|
236
|
+
const subtitle = text('div', current.goal, 'font-size:9px;opacity:.5;overflow:hidden;text-overflow:ellipsis;white-space:nowrap'); subtitle.title = current.goal;
|
|
237
|
+
title.append(nameRow, subtitle);
|
|
238
|
+
const statsWrap = el('div', undefined, 'display:flex;align-items:center;gap:15px;flex-wrap:wrap');
|
|
239
|
+
const progressWrap = el('div', undefined, 'display:flex;align-items:center;gap:7px;flex:none');
|
|
240
|
+
progressWrap.append(text('span', '整体进度', 'font-size:9px;opacity:.55;white-space:nowrap'));
|
|
241
|
+
const progressBar = el('progress'); progressBar.style.cssText = 'width:92px;height:5px;accent-color:#1f9d68';
|
|
242
|
+
const progressPct = text('strong', '0%', 'font-size:11px;font-weight:750;color:#1f9d68;min-width:30px');
|
|
243
|
+
progressWrap.append(progressBar, progressPct);
|
|
244
|
+
const timeline = el('div', 'harness-mix-team-timeline', 'display:flex;align-items:center;gap:6px;margin-left:auto;min-width:0'), event = text('span', '实时状态', 'font-size:9px;opacity:.55;min-width:60px;text-align:right'), range = el('input'); range.type = 'range'; range.min = '0'; range.max = '0'; range.value = '0'; range.style.cssText = 'width:min(120px,16vw);accent-color:#2878e3';
|
|
245
|
+
const live = el('button', undefined, 'border:1px solid color-mix(in srgb,currentColor 15%,transparent);border-radius:7px;background:transparent;color:inherit;font:600 10px system-ui;padding:5px 7px;cursor:pointer'); live.type = 'button'; live.textContent = '实时';
|
|
246
|
+
const play = el('button', undefined, 'border:1px solid color-mix(in srgb,currentColor 15%,transparent);border-radius:7px;background:color-mix(in srgb,currentColor 6%,transparent);color:inherit;font:600 10px system-ui;padding:5px 8px;cursor:pointer'); play.type = 'button'; play.textContent = '回放';
|
|
247
|
+
const collapse = el('button', 'harness-mix-team-back', 'border:0;background:transparent;color:inherit;font:650 10px system-ui;cursor:pointer;padding:6px 7px;opacity:.68'); collapse.type = 'button'; collapse.textContent = '收起详情'; collapse.addEventListener('click', close); timeline.append(event, range, live, play, collapse); header.append(title, statsWrap, progressWrap, timeline);
|
|
248
|
+
const content = el('div', undefined, 'min-height:0'); workbench.append(header, content); source.after(workbench);
|
|
249
|
+
const render = () => {
|
|
250
|
+
const snapshot = index >= 0 ? snapshots[index] : undefined;
|
|
251
|
+
const visible = snapshot?.team ?? current;
|
|
252
|
+
content.replaceChildren(renderBoard(visible, async threadId => { close(); await options.openThread?.(threadId); }));
|
|
253
|
+
teamName.textContent = visible.name; subtitle.textContent = visible.goal; subtitle.title = visible.goal;
|
|
254
|
+
teamPill.textContent = stateLabel(visible.status); teamPill.style.cssText = pillCss(visible.status);
|
|
255
|
+
statsWrap.replaceChildren(...headerStats(visible));
|
|
256
|
+
const completion = completionOf(visible); progressBar.max = Math.max(1, completion.total); progressBar.value = completion.done; progressPct.textContent = `${completion.pct}%`;
|
|
257
|
+
range.max = String(Math.max(0, snapshots.length - 1));
|
|
258
|
+
range.value = String(index >= 0 ? index : Math.max(0, snapshots.length - 1));
|
|
259
|
+
event.textContent = snapshot ? `${actionLabel(snapshot.action)} · ${new Date(snapshot.at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}` : '● 实时状态';
|
|
260
|
+
};
|
|
261
|
+
range.addEventListener('input', () => { index = Number(range.value); render(); }); live.addEventListener('click', () => { index = -1; render(); });
|
|
262
|
+
play.addEventListener('click', () => { if (playTimer) { clearInterval(playTimer); playTimer = null; play.textContent = '回放'; return; } index = index < 0 ? 0 : index; play.textContent = '暂停'; render(); playTimer = setInterval(() => { if (index >= snapshots.length - 1) { if (playTimer) clearInterval(playTimer); playTimer = null; index = -1; play.textContent = '回放'; } else index++; render(); }, 800); });
|
|
263
|
+
const refresh = async () => { if (!options.inspectTeam || !seed.lead_thread_id || !workbench) return; try { const result = parseInspection(await options.inspectTeam(seed.lead_thread_id, seed.team_id)); if (!result || !workbench) return; current = result.team; snapshots = result.snapshots; render(); } catch {} };
|
|
264
|
+
render(); void refresh(); refreshTimer = setInterval(() => void refresh(), 1500);
|
|
265
|
+
};
|
|
266
|
+
const onKey = (event: KeyboardEvent) => { if (event.key === 'Escape' && workbench) close(); }; document.addEventListener('keydown', onKey);
|
|
267
|
+
const scan = () => {
|
|
268
|
+
if (disposed) return;
|
|
269
|
+
const selector = '[data-testid*="tool"], [data-turn-key], [data-local-conversation-item-target-ids], pre[data-testid*="tool"]';
|
|
270
|
+
for (const candidate of document.querySelectorAll<HTMLElement>(selector)) { if (candidate.closest('.harness-mix-team-panel,.harness-mix-team-workbench')) continue; const payload = parseTeamPayload(candidate.textContent ?? ''); if (!payload) continue; if ([...candidate.querySelectorAll<HTMLElement>(selector)].some(child => parseTeamPayload(child.textContent ?? ''))) continue; const signature = `${payload.updated_at ?? 0}:${payload.tasks.length}:${payload.messages.length}`; if (signatures.get(candidate) === signature) continue; const panel = renderSummary(payload, () => open(payload)); if (candidate.tagName === 'PRE') { if (!candidate.dataset.harnessMixTeamDisplay) candidate.dataset.harnessMixTeamDisplay = candidate.style.display || '__empty__'; candidate.style.display = 'none'; if (candidate.nextElementSibling?.classList.contains('harness-mix-team-panel')) candidate.nextElementSibling.remove(); candidate.after(panel); } else { candidate.querySelector(':scope > .harness-mix-team-panel')?.remove(); candidate.append(panel); } signatures.set(candidate, signature); }
|
|
271
|
+
};
|
|
272
|
+
const removeActivePanel = () => { if (workbench?.dataset.teamSource === 'active-thread') close(); activePanel?.remove(); activePanel = null; activeSignature = ''; activeThreadId = ''; };
|
|
273
|
+
const refreshActiveTeam = async () => {
|
|
274
|
+
if (disposed || activeRefreshPending || !options.inspectTeam || !options.activeThread) return;
|
|
275
|
+
const context = options.activeThread();
|
|
276
|
+
if (!context?.anchor.isConnected) { removeActivePanel(); return; }
|
|
277
|
+
activeRefreshPending = true;
|
|
278
|
+
try {
|
|
279
|
+
const result = parseInspection(await options.inspectTeam(context.threadId));
|
|
280
|
+
if (disposed) return;
|
|
281
|
+
const latest = options.activeThread();
|
|
282
|
+
if (!latest || latest.threadId !== context.threadId || !latest.anchor.isConnected) { removeActivePanel(); return; }
|
|
283
|
+
if (!result) { removeActivePanel(); return; }
|
|
284
|
+
const payload = result.team;
|
|
285
|
+
const signature = `${context.threadId}:${payload.team_id}:${payload.updated_at ?? 0}:${payload.tasks.length}:${payload.messages.length}`;
|
|
286
|
+
const inlinePanel = [...document.querySelectorAll<HTMLElement>('.harness-mix-team-panel')]
|
|
287
|
+
.find(node => node !== activePanel && node.dataset.teamId === payload.team_id);
|
|
288
|
+
if (inlinePanel) { removeActivePanel(); return; }
|
|
289
|
+
if (activePanel?.isConnected && activeSignature === signature && activeThreadId === context.threadId) return;
|
|
290
|
+
const reopen = workbench?.dataset.teamId === payload.team_id && workbench.dataset.teamSource === 'active-thread';
|
|
291
|
+
removeActivePanel();
|
|
292
|
+
activePanel = renderSummary(payload, () => open(payload));
|
|
293
|
+
activePanel.classList.add('harness-mix-team-launcher');
|
|
294
|
+
activePanel.dataset.teamSource = 'active-thread';
|
|
295
|
+
activePanel.style.cssText += ';margin:8px 16px 0;flex:none;position:relative;z-index:11';
|
|
296
|
+
const parent = latest.anchor.parentElement;
|
|
297
|
+
if (!parent) { removeActivePanel(); return; }
|
|
298
|
+
parent.insertBefore(activePanel, latest.anchor);
|
|
299
|
+
activeSignature = signature;
|
|
300
|
+
activeThreadId = context.threadId;
|
|
301
|
+
if (reopen) open(payload);
|
|
302
|
+
} catch {
|
|
303
|
+
removeActivePanel();
|
|
304
|
+
} finally {
|
|
305
|
+
activeRefreshPending = false;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
const observer = new MutationObserver(scan); observer.observe(document.body || document.documentElement, { childList: true, subtree: true, characterData: true }); scan();
|
|
309
|
+
void refreshActiveTeam();
|
|
310
|
+
activeTimer = setInterval(() => void refreshActiveTeam(), 1500);
|
|
311
|
+
return { scan, close, dispose() { disposed = true; observer.disconnect(); if (activeTimer) clearInterval(activeTimer); activeTimer = null; removeActivePanel(); close(); style.remove(); document.removeEventListener('keydown', onKey); document.querySelectorAll('.harness-mix-team-panel').forEach(node => node.remove()); document.querySelectorAll<HTMLElement>('[data-harness-mix-team-display]').forEach(node => { node.style.display = node.dataset.harnessMixTeamDisplay === '__empty__' ? '' : node.dataset.harnessMixTeamDisplay ?? ''; delete node.dataset.harnessMixTeamDisplay; }); } };
|
|
312
|
+
}
|
|
@@ -23,7 +23,7 @@ function itemIdCount(node: Element): number {
|
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Codex renders transcript text for the Command Execution lane only, and it is
|
|
26
|
-
* the lane
|
|
26
|
+
* the lane harnessmix projects external Harness Reasoning through. This records
|
|
27
27
|
* bounded structural counts so a Desktop update that drops the lane, or stops
|
|
28
28
|
* publishing Item ids, is detected instead of silently hiding projected text.
|
|
29
29
|
*/
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
* cannot express, while each control still sets its own inline
|
|
15
15
|
* height/padding/font-size to size itself.
|
|
16
16
|
*/
|
|
17
|
-
const STYLE_ATTRIBUTE = "data-
|
|
18
|
-
export const TRIGGER_CHIP_CLASS = "
|
|
17
|
+
const STYLE_ATTRIBUTE = "data-harnessmix-trigger-chip-style";
|
|
18
|
+
export const TRIGGER_CHIP_CLASS = "harnessmix-trigger-chip";
|
|
19
19
|
|
|
20
20
|
export function ensureRendererTriggerChipStyle(ownerDocument: Document): void {
|
|
21
21
|
if (ownerDocument.querySelector(`style[${STYLE_ATTRIBUTE}]`)) return;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ThreadUsageSnapshot } from "@
|
|
1
|
+
import type { ThreadUsageSnapshot } from "@harnessmix/shared-contracts";
|
|
2
2
|
|
|
3
3
|
import type { RendererSettingsLocale } from "./settings/localization.js";
|
|
4
4
|
|
|
@@ -400,7 +400,7 @@ export function mountRendererUsageControl(
|
|
|
400
400
|
const messages = rendererUsageMessages(locale);
|
|
401
401
|
|
|
402
402
|
const root = document.createElement("div");
|
|
403
|
-
root.dataset.
|
|
403
|
+
root.dataset.harnessmixUsageControl = composerId;
|
|
404
404
|
root.className = "relative min-w-0";
|
|
405
405
|
root.style.display = "none";
|
|
406
406
|
root.style.alignItems = "center";
|
|
@@ -436,7 +436,7 @@ export function mountRendererUsageControl(
|
|
|
436
436
|
trigger.style.letterSpacing = "0";
|
|
437
437
|
|
|
438
438
|
const ringSlot = document.createElement("span");
|
|
439
|
-
ringSlot.dataset.
|
|
439
|
+
ringSlot.dataset.harnessmixUsageRing = "";
|
|
440
440
|
ringSlot.style.display = "inline-flex";
|
|
441
441
|
ringSlot.style.flex = "0 0 auto";
|
|
442
442
|
ringSlot.style.alignItems = "center";
|
|
@@ -567,7 +567,7 @@ export function renderRendererUsageControl(
|
|
|
567
567
|
return false;
|
|
568
568
|
}
|
|
569
569
|
|
|
570
|
-
const ringSlot = control.trigger.querySelector<HTMLElement>("[data-
|
|
570
|
+
const ringSlot = control.trigger.querySelector<HTMLElement>("[data-harnessmix-usage-ring]");
|
|
571
571
|
const contextPercent =
|
|
572
572
|
usage?.contextUsagePercent !== undefined
|
|
573
573
|
? usage.contextUsagePercent
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AccountCreditsSnapshot } from "@
|
|
1
|
+
import type { AccountCreditsSnapshot } from "@harnessmix/shared-contracts";
|
|
2
2
|
|
|
3
3
|
import { formatRendererCreditsReset, rendererCreditsTone } from "../renderer-credits-control.js";
|
|
4
4
|
import { formatRendererCreditsPercent } from "../renderer-usage-control.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { HarnessMixError, HarnessInspection } from "@harnessmix/shared-contracts";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
getSharedAgentGroupPreferenceStore,
|
|
@@ -16,7 +16,7 @@ import type { RendererSettingsPageDefinition, RendererSettingsPageMountContext }
|
|
|
16
16
|
import { createRendererSettingsIcon } from "./icons.js";
|
|
17
17
|
import type { RendererSettingsMessages } from "./localization.js";
|
|
18
18
|
|
|
19
|
-
export const
|
|
19
|
+
export const HARNESSMIX_GITHUB_ISSUES_NEW_URL =
|
|
20
20
|
"https://github.com/emo-xiaoyu/harness-mix/issues/new";
|
|
21
21
|
|
|
22
22
|
export const HARNESS_INSTALL_COMMANDS: Readonly<Partial<Record<ExternalRendererAgent, { command: string }>>> = Object.freeze({
|
|
@@ -58,7 +58,7 @@ const HARNESS_INSTALL_URLS: Readonly<Record<ExternalRendererAgent, string>> = Ob
|
|
|
58
58
|
export interface RendererConnectionAgentSnapshot {
|
|
59
59
|
readonly agent: ExternalRendererAgent;
|
|
60
60
|
readonly availability: RendererAgentAvailability;
|
|
61
|
-
readonly error:
|
|
61
|
+
readonly error: HarnessMixError | null;
|
|
62
62
|
readonly webUiAvailable?: true;
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -89,7 +89,7 @@ interface ConnectionListItem {
|
|
|
89
89
|
readonly key: string;
|
|
90
90
|
readonly name: string;
|
|
91
91
|
readonly availability: ConnectionAvailability;
|
|
92
|
-
readonly error:
|
|
92
|
+
readonly error: HarnessMixError | null;
|
|
93
93
|
readonly agentSnapshot?: RendererConnectionAgentSnapshot;
|
|
94
94
|
readonly openWebUi?: () => Promise<void>;
|
|
95
95
|
}
|
|
@@ -130,7 +130,7 @@ function diagnosticText(
|
|
|
130
130
|
): string {
|
|
131
131
|
const error = item.error;
|
|
132
132
|
return [
|
|
133
|
-
"
|
|
133
|
+
"harnessmix connection diagnostics",
|
|
134
134
|
`host: ${hostId}`,
|
|
135
135
|
`agent: ${item.name}`,
|
|
136
136
|
`status: ${item.availability}`,
|
|
@@ -841,7 +841,7 @@ function renderConnectionInspector(
|
|
|
841
841
|
actions.className = "settings-connection-error-actions";
|
|
842
842
|
const issue = document.createElement("a");
|
|
843
843
|
issue.className = "settings-command-button settings-command-button--secondary";
|
|
844
|
-
issue.href =
|
|
844
|
+
issue.href = HARNESSMIX_GITHUB_ISSUES_NEW_URL;
|
|
845
845
|
issue.target = "_blank";
|
|
846
846
|
issue.rel = "noopener noreferrer";
|
|
847
847
|
issue.append(messages.connectionOpenIssue, createRendererSettingsIcon("external-link", 14));
|
|
@@ -1095,7 +1095,7 @@ export function createConnectionsSettingsPage(
|
|
|
1095
1095
|
tabs.setAttribute("role", "tablist");
|
|
1096
1096
|
tabs.setAttribute("aria-label", messages.connectionHosts);
|
|
1097
1097
|
const scrollRight = createHostScrollButton(document, "right", messages);
|
|
1098
|
-
const panelId = "
|
|
1098
|
+
const panelId = "harnessmix-settings-connection-host-panel";
|
|
1099
1099
|
|
|
1100
1100
|
snapshot.hosts.forEach((host, index) => {
|
|
1101
1101
|
const tab = document.createElement("button");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HarnessAccountListResult } from "@
|
|
1
|
+
import type { HarnessAccountListResult } from "@harnessmix/shared-contracts";
|
|
2
2
|
import { KNOWN_RENDERER_AGENTS } from "../agent-selection-state.js";
|
|
3
3
|
import { createRendererAgentIcon } from "../renderer-agent-icon.js";
|
|
4
4
|
import { createRendererSettingsIcon } from "./icons.js";
|
|
@@ -131,7 +131,7 @@ export function createRendererSettingsIcon(name: RendererSettingsIconName, size
|
|
|
131
131
|
"aria-hidden": "true",
|
|
132
132
|
focusable: "false",
|
|
133
133
|
});
|
|
134
|
-
icon.classList.add("
|
|
134
|
+
icon.classList.add("harnessmix-settings-icon");
|
|
135
135
|
return icon;
|
|
136
136
|
}
|
|
137
137
|
|
|
@@ -148,6 +148,6 @@ export function createRendererSettingsBrandIcon(size = 22, ownerDocument?: Docum
|
|
|
148
148
|
icon.style.width = `${size}px`;
|
|
149
149
|
icon.style.height = `${size}px`;
|
|
150
150
|
icon.style.objectFit = "contain";
|
|
151
|
-
icon.classList.add("
|
|
151
|
+
icon.classList.add("harnessmix-settings-icon");
|
|
152
152
|
return icon;
|
|
153
153
|
}
|
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
UpdateStartResult,
|
|
5
5
|
UpdateStatus,
|
|
6
6
|
UpdateStatusResult,
|
|
7
|
-
} from "@
|
|
7
|
+
} from "@harnessmix/shared-contracts";
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
createRendererSettingsPageRegistry,
|
|
@@ -46,9 +46,9 @@ import {
|
|
|
46
46
|
runBoundedRendererUpdateRequest,
|
|
47
47
|
} from "./update-request.js";
|
|
48
48
|
|
|
49
|
-
export const
|
|
50
|
-
export const
|
|
51
|
-
export const
|
|
49
|
+
export const HARNESSMIX_GITHUB_REPOSITORY_URL = "https://github.com/emo-xiaoyu/harness-mix";
|
|
50
|
+
export const HARNESSMIX_RELEASES_LATEST_URL = `${HARNESSMIX_GITHUB_REPOSITORY_URL}/releases/latest`;
|
|
51
|
+
export const HARNESSMIX_NPM_MANUAL_UPDATE_COMMAND = "npm install -g harness-mix@latest";
|
|
52
52
|
|
|
53
53
|
interface RendererUserAgentData {
|
|
54
54
|
readonly platform?: string;
|
|
@@ -198,7 +198,7 @@ export function aboutPage(
|
|
|
198
198
|
checkUpdateBtn.className = "settings-command-button settings-command-button--secondary";
|
|
199
199
|
checkUpdateBtn.append(createRendererSettingsIcon("updates", 14), messages.checkUpdateNow);
|
|
200
200
|
checkUpdateBtn.addEventListener("click", () => {
|
|
201
|
-
const shell = document.querySelector("[data-
|
|
201
|
+
const shell = document.querySelector("[data-harnessmix-settings-shell]");
|
|
202
202
|
const updatesNavBtn = (shell?.shadowRoot ?? document).querySelector<HTMLButtonElement>(
|
|
203
203
|
'button[data-page-id="updates"]',
|
|
204
204
|
);
|
|
@@ -221,11 +221,11 @@ export function aboutPage(
|
|
|
221
221
|
openSource.textContent = messages.aboutOpenSource;
|
|
222
222
|
const repository = document.createElement("a");
|
|
223
223
|
repository.className = "settings-about-repository-link";
|
|
224
|
-
repository.href =
|
|
224
|
+
repository.href = HARNESSMIX_GITHUB_REPOSITORY_URL;
|
|
225
225
|
repository.target = "_blank";
|
|
226
226
|
repository.rel = "noopener noreferrer";
|
|
227
227
|
const repositoryUrl = document.createElement("code");
|
|
228
|
-
repositoryUrl.textContent =
|
|
228
|
+
repositoryUrl.textContent = HARNESSMIX_GITHUB_REPOSITORY_URL;
|
|
229
229
|
repository.append(
|
|
230
230
|
createRendererSettingsIcon("external-link", 14),
|
|
231
231
|
messages.aboutRepository,
|
|
@@ -312,7 +312,7 @@ export function updatesPage(
|
|
|
312
312
|
const manualNpmCommandRow = document.createElement("div");
|
|
313
313
|
manualNpmCommandRow.className = "settings-update-command";
|
|
314
314
|
const manualNpmCommand = document.createElement("code");
|
|
315
|
-
manualNpmCommand.textContent =
|
|
315
|
+
manualNpmCommand.textContent = HARNESSMIX_NPM_MANUAL_UPDATE_COMMAND;
|
|
316
316
|
const copyCommand = document.createElement("button");
|
|
317
317
|
copyCommand.type = "button";
|
|
318
318
|
copyCommand.className = "settings-update-command__copy";
|
|
@@ -330,7 +330,7 @@ export function updatesPage(
|
|
|
330
330
|
restore(messages.updateCopyFailed);
|
|
331
331
|
return;
|
|
332
332
|
}
|
|
333
|
-
void clipboard.writeText(
|
|
333
|
+
void clipboard.writeText(HARNESSMIX_NPM_MANUAL_UPDATE_COMMAND).then(
|
|
334
334
|
() => restore(messages.updateCommandCopied),
|
|
335
335
|
() => restore(messages.updateCopyFailed),
|
|
336
336
|
);
|
|
@@ -347,7 +347,7 @@ export function updatesPage(
|
|
|
347
347
|
manualWindowsInstallerActions.className = "settings-update-actions";
|
|
348
348
|
const manualWindowsInstallerLink = document.createElement("a");
|
|
349
349
|
manualWindowsInstallerLink.className = "settings-update-link";
|
|
350
|
-
manualWindowsInstallerLink.href =
|
|
350
|
+
manualWindowsInstallerLink.href = HARNESSMIX_RELEASES_LATEST_URL;
|
|
351
351
|
manualWindowsInstallerLink.target = "_blank";
|
|
352
352
|
manualWindowsInstallerLink.rel = "noopener noreferrer";
|
|
353
353
|
manualWindowsInstallerLink.append(
|
|
@@ -363,7 +363,7 @@ export function updatesPage(
|
|
|
363
363
|
actions.className = "settings-update-actions";
|
|
364
364
|
const releaseLink = document.createElement("a");
|
|
365
365
|
releaseLink.className = "settings-update-link";
|
|
366
|
-
releaseLink.href =
|
|
366
|
+
releaseLink.href = HARNESSMIX_RELEASES_LATEST_URL;
|
|
367
367
|
releaseLink.target = "_blank";
|
|
368
368
|
releaseLink.rel = "noopener noreferrer";
|
|
369
369
|
releaseLink.append(
|
|
@@ -133,6 +133,15 @@
|
|
|
133
133
|
border-color: rgba(16, 163, 127, 0.35);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
.pet-card[data-state="installing"] .pet-card__stage,
|
|
137
|
+
.pet-card[data-state="uninstalling"] .pet-card__stage {
|
|
138
|
+
opacity: 0.55;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.pet-card[data-state="failed"] {
|
|
142
|
+
border-color: rgba(239, 68, 68, 0.35);
|
|
143
|
+
}
|
|
144
|
+
|
|
136
145
|
.pet-card__stage {
|
|
137
146
|
position: relative;
|
|
138
147
|
display: flex;
|
|
@@ -214,6 +223,30 @@
|
|
|
214
223
|
border: 1px solid rgba(16, 163, 127, 0.3);
|
|
215
224
|
}
|
|
216
225
|
|
|
226
|
+
.pet-card__badge--failed {
|
|
227
|
+
background: rgba(239, 68, 68, 0.15);
|
|
228
|
+
color: #f87171;
|
|
229
|
+
border: 1px solid rgba(239, 68, 68, 0.3);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.pet-card__status {
|
|
233
|
+
margin: 0 0 10px;
|
|
234
|
+
font-size: 11.5px;
|
|
235
|
+
color: var(--text-secondary, rgba(255, 255, 255, 0.6));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.pet-card__error {
|
|
239
|
+
margin: 0 0 10px;
|
|
240
|
+
padding: 6px 8px;
|
|
241
|
+
border-radius: 6px;
|
|
242
|
+
background: rgba(239, 68, 68, 0.08);
|
|
243
|
+
border: 1px solid rgba(239, 68, 68, 0.25);
|
|
244
|
+
color: #f87171;
|
|
245
|
+
font-size: 11.5px;
|
|
246
|
+
line-height: 1.4;
|
|
247
|
+
word-break: break-word;
|
|
248
|
+
}
|
|
249
|
+
|
|
217
250
|
.pet-card__description {
|
|
218
251
|
margin: 0 0 14px;
|
|
219
252
|
font-size: 12px;
|