@jingyi0605/codingns4dsh 0.2.1-rc.6 → 0.2.1-rc.7

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.
Files changed (124) hide show
  1. package/README.en.md +3 -3
  2. package/README.md +3 -3
  3. package/data/build/dist/client/bundle.js +1412 -103
  4. package/data/build/dist/client/bundle.js.map +1 -1
  5. package/data/build/dist/client/client.native-workspace-store.js +1 -1
  6. package/data/build/dist/client/client.native-workspace-store.js.map +1 -1
  7. package/data/build/dist/client/client.transport.js +202 -0
  8. package/data/build/dist/client/client.transport.js.map +1 -1
  9. package/data/build/dist/client/desktop-assistant.js +202 -0
  10. package/data/build/dist/client/features/index.d.ts +2 -1
  11. package/data/build/dist/client/features/index.d.ts.map +1 -1
  12. package/data/build/dist/client/features/index.js +3 -1
  13. package/data/build/dist/client/features/index.js.map +1 -1
  14. package/data/build/dist/client/features/magpie-management-page.d.ts +7 -3
  15. package/data/build/dist/client/features/magpie-management-page.d.ts.map +1 -1
  16. package/data/build/dist/client/features/magpie-management-page.js +142 -8
  17. package/data/build/dist/client/features/magpie-management-page.js.map +1 -1
  18. package/data/build/dist/client/features/workspace-mcp.d.ts +3 -0
  19. package/data/build/dist/client/features/workspace-mcp.d.ts.map +1 -0
  20. package/data/build/dist/client/features/workspace-mcp.js +320 -0
  21. package/data/build/dist/client/features/workspace-mcp.js.map +1 -0
  22. package/data/build/dist/client/locale.d.ts.map +1 -1
  23. package/data/build/dist/client/locale.js +176 -0
  24. package/data/build/dist/client/locale.js.map +1 -1
  25. package/data/build/dist/client/locales/magpie.d.ts.map +1 -1
  26. package/data/build/dist/client/locales/magpie.js +12 -0
  27. package/data/build/dist/client/locales/magpie.js.map +1 -1
  28. package/data/build/dist/client/locales/usage.d.ts.map +1 -1
  29. package/data/build/dist/client/locales/usage.js +14 -0
  30. package/data/build/dist/client/locales/usage.js.map +1 -1
  31. package/data/build/dist/client/subscription-slot.d.ts.map +1 -1
  32. package/data/build/dist/client/subscription-slot.js +104 -11
  33. package/data/build/dist/client/subscription-slot.js.map +1 -1
  34. package/data/build/dist/dsh-capabilities/host/workspace-approval-adapter.d.ts +18 -0
  35. package/data/build/dist/dsh-capabilities/host/workspace-approval-adapter.d.ts.map +1 -0
  36. package/data/build/dist/dsh-capabilities/host/workspace-approval-adapter.js +172 -0
  37. package/data/build/dist/dsh-capabilities/host/workspace-approval-adapter.js.map +1 -0
  38. package/data/build/dist/dsh-capabilities/host/workspace-pending-questions-adapter.d.ts +28 -0
  39. package/data/build/dist/dsh-capabilities/host/workspace-pending-questions-adapter.d.ts.map +1 -0
  40. package/data/build/dist/dsh-capabilities/host/workspace-pending-questions-adapter.js +103 -0
  41. package/data/build/dist/dsh-capabilities/host/workspace-pending-questions-adapter.js.map +1 -0
  42. package/data/build/dist/dsh-capabilities/host/workspace-session-control-adapter.d.ts +26 -0
  43. package/data/build/dist/dsh-capabilities/host/workspace-session-control-adapter.d.ts.map +1 -0
  44. package/data/build/dist/dsh-capabilities/host/workspace-session-control-adapter.js +86 -0
  45. package/data/build/dist/dsh-capabilities/host/workspace-session-control-adapter.js.map +1 -0
  46. package/data/build/dist/dsh-capabilities/matrix.d.ts.map +1 -1
  47. package/data/build/dist/dsh-capabilities/matrix.js +3 -0
  48. package/data/build/dist/dsh-capabilities/matrix.js.map +1 -1
  49. package/data/build/dist/dsh-capabilities/routes.d.ts.map +1 -1
  50. package/data/build/dist/dsh-capabilities/routes.js +9 -0
  51. package/data/build/dist/dsh-capabilities/routes.js.map +1 -1
  52. package/data/build/dist/dsh-capabilities/types.d.ts +1 -1
  53. package/data/build/dist/dsh-capabilities/types.d.ts.map +1 -1
  54. package/data/build/dist/dsh-capabilities/types.js.map +1 -1
  55. package/data/build/dist/host/cli-adapters/claude-model-options.d.ts.map +1 -1
  56. package/data/build/dist/host/cli-adapters/claude-model-options.js +13 -2
  57. package/data/build/dist/host/cli-adapters/claude-model-options.js.map +1 -1
  58. package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts.map +1 -1
  59. package/data/build/dist/host/cli-adapters/dsh-message-projector.js +1 -0
  60. package/data/build/dist/host/cli-adapters/dsh-message-projector.js.map +1 -1
  61. package/data/build/dist/host/cli-adapters/feature.d.ts.map +1 -1
  62. package/data/build/dist/host/cli-adapters/feature.js +5 -1
  63. package/data/build/dist/host/cli-adapters/feature.js.map +1 -1
  64. package/data/build/dist/host/cli-adapters/magpie-usage.d.ts +13 -2
  65. package/data/build/dist/host/cli-adapters/magpie-usage.d.ts.map +1 -1
  66. package/data/build/dist/host/cli-adapters/magpie-usage.js +180 -13
  67. package/data/build/dist/host/cli-adapters/magpie-usage.js.map +1 -1
  68. package/data/build/dist/host/features/index.d.ts +1 -0
  69. package/data/build/dist/host/features/index.d.ts.map +1 -1
  70. package/data/build/dist/host/features/index.js +3 -0
  71. package/data/build/dist/host/features/index.js.map +1 -1
  72. package/data/build/dist/host/features/workspace-mcp.d.ts +9 -0
  73. package/data/build/dist/host/features/workspace-mcp.d.ts.map +1 -0
  74. package/data/build/dist/host/features/workspace-mcp.js +197 -0
  75. package/data/build/dist/host/features/workspace-mcp.js.map +1 -0
  76. package/data/build/dist/host/index.d.ts.map +1 -1
  77. package/data/build/dist/host/index.js +6 -2
  78. package/data/build/dist/host/index.js.map +1 -1
  79. package/data/build/dist/host/native-session-bridge.d.ts +2 -0
  80. package/data/build/dist/host/native-session-bridge.d.ts.map +1 -1
  81. package/data/build/dist/host/native-session-bridge.js +30 -0
  82. package/data/build/dist/host/native-session-bridge.js.map +1 -1
  83. package/data/build/dist/host/rpc.d.ts +4 -1
  84. package/data/build/dist/host/rpc.d.ts.map +1 -1
  85. package/data/build/dist/host/rpc.js +57 -9
  86. package/data/build/dist/host/rpc.js.map +1 -1
  87. package/data/build/dist/host/session-handle-eviction.d.ts +4 -2
  88. package/data/build/dist/host/session-handle-eviction.d.ts.map +1 -1
  89. package/data/build/dist/host/session-handle-eviction.js +10 -9
  90. package/data/build/dist/host/session-handle-eviction.js.map +1 -1
  91. package/data/build/dist/host/terminal/terminal-process-service.d.ts +22 -0
  92. package/data/build/dist/host/terminal/terminal-process-service.d.ts.map +1 -1
  93. package/data/build/dist/host/terminal/terminal-process-service.js +102 -0
  94. package/data/build/dist/host/terminal/terminal-process-service.js.map +1 -1
  95. package/data/build/dist/host/terminal/terminal-service.d.ts +5 -1
  96. package/data/build/dist/host/terminal/terminal-service.d.ts.map +1 -1
  97. package/data/build/dist/host/terminal/terminal-service.js +32 -0
  98. package/data/build/dist/host/terminal/terminal-service.js.map +1 -1
  99. package/data/build/dist/host/workspace-mcp/api-key-store.d.ts +16 -0
  100. package/data/build/dist/host/workspace-mcp/api-key-store.d.ts.map +1 -0
  101. package/data/build/dist/host/workspace-mcp/api-key-store.js +65 -0
  102. package/data/build/dist/host/workspace-mcp/api-key-store.js.map +1 -0
  103. package/data/build/dist/host/workspace-mcp/tools.d.ts +54 -0
  104. package/data/build/dist/host/workspace-mcp/tools.d.ts.map +1 -0
  105. package/data/build/dist/host/workspace-mcp/tools.js +800 -0
  106. package/data/build/dist/host/workspace-mcp/tools.js.map +1 -0
  107. package/data/build/dist/host/workspace-mcp/transport.d.ts +35 -0
  108. package/data/build/dist/host/workspace-mcp/transport.d.ts.map +1 -0
  109. package/data/build/dist/host/workspace-mcp/transport.js +575 -0
  110. package/data/build/dist/host/workspace-mcp/transport.js.map +1 -0
  111. package/data/build/dist/shared/contracts/magpie-management.d.ts +15 -1
  112. package/data/build/dist/shared/contracts/magpie-management.d.ts.map +1 -1
  113. package/data/build/dist/shared/contracts/subscription.d.ts +7 -0
  114. package/data/build/dist/shared/contracts/subscription.d.ts.map +1 -1
  115. package/data/build/dist/shared/contracts/subscription.js.map +1 -1
  116. package/data/build/dist/shared/contracts/terminal.d.ts +2 -0
  117. package/data/build/dist/shared/contracts/terminal.d.ts.map +1 -1
  118. package/data/build/dist/shared/contracts/version.d.ts +1 -1
  119. package/data/build/dist/shared/contracts/version.js +1 -1
  120. package/package.json +1 -1
  121. package/profile/README.md +2 -2
  122. package/profile/package.json +2 -2
  123. package/profile/version.json +1 -1
  124. package/version.json +1 -1
@@ -8392,7 +8392,7 @@ window.__ModuleLoader__.load({
8392
8392
  return /^0\.1\.5(?:-|$)/u.test(version);
8393
8393
  }
8394
8394
  /** Codingns4DSH 插件自身的 npm 版本。 */
8395
- const CODINGNS_VERSION = "0.2.1-rc.6";
8395
+ const CODINGNS_VERSION = "0.2.1-rc.7";
8396
8396
  /** Accept any valid host version at or above the supported minimum. */
8397
8397
  function isDshVersionCompatible(version) {
8398
8398
  return isDshVersionAtLeast(version, minimumSupportedDshVersion());
@@ -19490,6 +19490,13 @@ window.__ModuleLoader__.load({
19490
19490
  "usage.magpieTotalTokens": "Input + output",
19491
19491
  "usage.magpieCost": "Estimated cost",
19492
19492
  "usage.magpieCacheHitRate": "Cache hit rate",
19493
+ "usage.magpieAccountPolicies": "Codex account controls",
19494
+ "usage.magpieResetCountLabel": "Reset cards",
19495
+ "usage.magpieResetCount": "{count} reset cards",
19496
+ "usage.magpieResetWhenWeek": "Auto-use a reset when the weekly window is exhausted",
19497
+ "usage.magpieResetBeforeExpiry": "Auto-use a reset before it expires",
19498
+ "usage.magpieUseCredits": "Use credits after quota is exhausted",
19499
+ "usage.magpieUseReset": "Use one reset card",
19493
19500
  "usage.magpieUpdated": "Updated {time}",
19494
19501
  "usage.magpieAgent": "Agent: {agent}",
19495
19502
  "usage.magpieSessionNoUsage": "No matching session usage today.",
@@ -19696,6 +19703,13 @@ window.__ModuleLoader__.load({
19696
19703
  "usage.magpieTotalTokens": "输入 + 输出",
19697
19704
  "usage.magpieCost": "预估费用",
19698
19705
  "usage.magpieCacheHitRate": "缓存命中率",
19706
+ "usage.magpieAccountPolicies": "Codex 账号控制",
19707
+ "usage.magpieResetCountLabel": "重置卡",
19708
+ "usage.magpieResetCount": "{count} 张重置卡",
19709
+ "usage.magpieResetWhenWeek": "周额度用尽时自动使用重置卡",
19710
+ "usage.magpieResetBeforeExpiry": "重置卡到期前自动使用",
19711
+ "usage.magpieUseCredits": "额度用尽后使用点数",
19712
+ "usage.magpieUseReset": "使用一张重置卡",
19699
19713
  "usage.magpieUpdated": "更新于 {time}",
19700
19714
  "usage.magpieAgent": "Agent:{agent}",
19701
19715
  "usage.magpieSessionNoUsage": "今天还没有找到此会话的用量。",
@@ -21004,6 +21018,12 @@ window.__ModuleLoader__.load({
21004
21018
  "magpie.hostRequestFailed": "The Host request failed. Check the Host connection and retry.",
21005
21019
  "magpie.usageUnavailable": "Usage data could not be read. Other sections may still be available.",
21006
21020
  "magpie.quotaUnavailable": "Subscription data could not be read.",
21021
+ "magpie.codexControlsUnavailable": "Codex account identity was not reported; account controls are unavailable.",
21022
+ "magpie.codexPolicyFailed": "Could not update the Codex account setting. Refresh and try again.",
21023
+ "magpie.codexPolicyUnavailable": "Codex policy settings are unavailable.",
21024
+ "magpie.codexResetFailed": "Could not use a reset card. Refresh and try again.",
21025
+ "magpie.resetDialogAccount": "Account: {account}",
21026
+ "magpie.resetDialogBody": "This uses one reset card from the {count} available and immediately restores the current quota window. This cannot be undone. Continue?",
21007
21027
  "magpie.historyUnavailable": "Quota history could not be read.",
21008
21028
  "magpie.noUsage": "No usage was reported for this period.",
21009
21029
  "magpie.noSessions": "No sessions were reported for this period.",
@@ -21354,6 +21374,12 @@ window.__ModuleLoader__.load({
21354
21374
  "magpie.hostRequestFailed": "Host 请求失败。请检查 Host 连接后重试。",
21355
21375
  "magpie.usageUnavailable": "无法读取用量数据;其他区块仍可能可用。",
21356
21376
  "magpie.quotaUnavailable": "无法读取订阅数据。",
21377
+ "magpie.codexControlsUnavailable": "未报告 Codex 账号身份,无法显示账号控制项。",
21378
+ "magpie.codexPolicyFailed": "无法更新 Codex 账号设置,请刷新后重试。",
21379
+ "magpie.codexPolicyUnavailable": "暂时无法读取 Codex 使用规则。",
21380
+ "magpie.codexResetFailed": "无法使用重置卡,请刷新后重试。",
21381
+ "magpie.resetDialogAccount": "账号:{account}",
21382
+ "magpie.resetDialogBody": "将使用当前 {count} 张重置卡中的 1 张,并立即恢复当前额度窗口。此操作不可撤销,确定继续吗?",
21357
21383
  "magpie.historyUnavailable": "无法读取额度历史。",
21358
21384
  "magpie.noUsage": "此周期没有用量记录。",
21359
21385
  "magpie.noSessions": "此周期没有会话记录。",
@@ -21755,6 +21781,94 @@ window.__ModuleLoader__.load({
21755
21781
  "feature.cliAdapters.description": "View installed external Agents, versions, command paths, available models, and enable them independently. External Agent context panels keep only the occupancy percentage and totals.",
21756
21782
  "feature.workspaceSession.label": "Workspace session enhancement",
21757
21783
  "feature.workspaceSession.description": "Show Agent logos, archived-session entries, workspace hiding controls, subscription or usage information, local quick sessions, and an optional remembered conversation/right-sidebar width ratio.",
21784
+ "feature.workspaceMcp.label": "Workspace MCP server",
21785
+ "feature.workspaceMcp.description": "Expose managed workspace tools to local MCP clients through an authenticated Host listener.",
21786
+ "workspaceMcp.setupTitle": "MCP access key",
21787
+ "workspaceMcp.setupInstruction": "Generate a key to save it on the Host and enable the local MCP listener.",
21788
+ "workspaceMcp.generateKey": "Generate key and enable",
21789
+ "workspaceMcp.rotateKey": "Rotate key",
21790
+ "workspaceMcp.generating": "Generating…",
21791
+ "workspaceMcp.generatedKey": "Generated Workspace MCP API key",
21792
+ "workspaceMcp.keyGenerated": "Workspace MCP key generated and module enabled.",
21793
+ "workspaceMcp.copyKey": "Copy key",
21794
+ "workspaceMcp.keyCopied": "Workspace MCP key copied.",
21795
+ "workspaceMcp.copyFailed": "Could not copy the key. Select and copy it manually.",
21796
+ "workspaceMcp.keyShownOnce": "This key is shown only now. Copy it into your MCP client before closing or refreshing this panel.",
21797
+ "workspaceMcp.capabilityDescription": "MCP capability description",
21798
+ "workspaceMcp.capabilityTitle": "Supported MCP capabilities",
21799
+ "workspaceMcp.capabilitySubtitle": "The list below describes the fixed Workspace MCP tool catalog and its access boundaries.",
21800
+ "workspaceMcp.capabilityName": "Name",
21801
+ "workspaceMcp.capabilityPermission": "Permission",
21802
+ "workspaceMcp.capabilityDescriptionField": "Description",
21803
+ "workspaceMcp.capabilityParameters": "Invocation parameters",
21804
+ "workspaceMcp.capabilityRemarks": "Remarks",
21805
+ "workspaceMcp.closeCapabilities": "Close",
21806
+ "workspaceMcp.installPromptTitle": "Install the Agent Skill",
21807
+ "workspaceMcp.installPromptHint": "Copy this prompt to your Agent. It will install the root SKILL.md into that Agent’s personal skills directory.",
21808
+ "workspaceMcp.copyInstallPrompt": "Copy install prompt",
21809
+ "workspaceMcp.installPromptCopied": "Agent Skill installation prompt copied.",
21810
+ "workspaceMcp.installPromptText": "Fetch https://github.com/jingyi0605/Codingns4DSH/blob/main/SKILL.md and install it into your personal Agent Skills directory using your documented Skill installation method.",
21811
+ "workspaceMcp.capability.workspaceList.permission": "Read managed workspace metadata",
21812
+ "workspaceMcp.capability.workspaceList.description": "Lists managed workspaces available to the local Host.",
21813
+ "workspaceMcp.capability.workspaceList.parameters": "{}",
21814
+ "workspaceMcp.capability.workspaceList.remarks": "Returns names and active session counts; filesystem roots are never exposed.",
21815
+ "workspaceMcp.capability.sessionList.permission": "Read bounded session metadata",
21816
+ "workspaceMcp.capability.sessionList.description": "Lists sessions belonging to managed workspaces.",
21817
+ "workspaceMcp.capability.sessionList.parameters": "{ workspaceId?, limit?, offset? }",
21818
+ "workspaceMcp.capability.sessionList.remarks": "Results are paginated and contain metadata only, without full conversation history.",
21819
+ "workspaceMcp.capability.sessionCreate.permission": "Create a managed session and optionally queue a prompt",
21820
+ "workspaceMcp.capability.sessionCreate.description": "Creates a native DSH session and can select an installed adapter/model.",
21821
+ "workspaceMcp.capability.sessionCreate.parameters": "{ workspaceId, adapterId?, modelId?, prompt? }",
21822
+ "workspaceMcp.capability.sessionCreate.remarks": "External adapter sessions require an initial prompt; uncertain outcomes include the created session ID.",
21823
+ "workspaceMcp.capability.sessionContinue.permission": "Queue a message in an existing session",
21824
+ "workspaceMcp.capability.sessionContinue.description": "Continues one exact managed session without replacing its identity.",
21825
+ "workspaceMcp.capability.sessionContinue.parameters": "{ sessionId, message }",
21826
+ "workspaceMcp.capability.sessionContinue.remarks": "The session is revalidated immediately before prompt admission.",
21827
+ "workspaceMcp.capability.sessionQuestions.permission": "Read unresolved native questions",
21828
+ "workspaceMcp.capability.sessionQuestions.description": "Lists questions currently awaiting an answer in a managed session.",
21829
+ "workspaceMcp.capability.sessionQuestions.parameters": "{ sessionId }",
21830
+ "workspaceMcp.capability.sessionQuestions.remarks": "Question content and options are bounded; archived or unrelated sessions are rejected.",
21831
+ "workspaceMcp.capability.sessionAnswer.permission": "Answer one unresolved native question",
21832
+ "workspaceMcp.capability.sessionAnswer.description": "Submits validated answers through the native DSH question mechanism.",
21833
+ "workspaceMcp.capability.sessionAnswer.parameters": "{ sessionId, callId, answer }",
21834
+ "workspaceMcp.capability.sessionAnswer.remarks": "The call ID must come from the current question list and answers are checked against its options.",
21835
+ "workspaceMcp.capability.sessionRequestPermission.permission": "Request human approval",
21836
+ "workspaceMcp.capability.sessionRequestPermission.description": "Asks the native DSH approval UI to decide an operation.",
21837
+ "workspaceMcp.capability.sessionRequestPermission.parameters": "{ sessionId, toolName, reason, callId? }",
21838
+ "workspaceMcp.capability.sessionRequestPermission.remarks": "Requires an active DSH turn; the MCP client cannot approve its own request.",
21839
+ "workspaceMcp.capability.sessionPermissionStatus.permission": "Read an owned approval outcome",
21840
+ "workspaceMcp.capability.sessionPermissionStatus.description": "Reads the status of an approval request created by this MCP feature.",
21841
+ "workspaceMcp.capability.sessionPermissionStatus.parameters": "{ sessionId, requestId }",
21842
+ "workspaceMcp.capability.sessionPermissionStatus.remarks": "Only DSH-issued request IDs retained by the live feature are queryable.",
21843
+ "workspaceMcp.capability.terminalList.permission": "Read live terminal metadata",
21844
+ "workspaceMcp.capability.terminalList.description": "Lists terminal instances attached to one managed session.",
21845
+ "workspaceMcp.capability.terminalList.parameters": "{ workspaceId, sessionId }",
21846
+ "workspaceMcp.capability.terminalList.remarks": "Only live terminals in the specified workspace/session scope are returned.",
21847
+ "workspaceMcp.capability.terminalRead.permission": "Read terminal output",
21848
+ "workspaceMcp.capability.terminalRead.description": "Returns a bounded output snapshot from one live terminal.",
21849
+ "workspaceMcp.capability.terminalRead.parameters": "{ workspaceId, sessionId, terminalId, limitBytes? }",
21850
+ "workspaceMcp.capability.terminalRead.remarks": "Output is limited to 64 KiB and is never read from an unrelated terminal.",
21851
+ "workspaceMcp.capability.terminalWrite.permission": "Write terminal input",
21852
+ "workspaceMcp.capability.terminalWrite.description": "Writes bounded text to one existing live terminal process.",
21853
+ "workspaceMcp.capability.terminalWrite.parameters": "{ workspaceId, sessionId, terminalId, data }",
21854
+ "workspaceMcp.capability.terminalWrite.remarks": "Input is limited to 16 KiB, rejects NUL bytes, and cannot create arbitrary processes.",
21855
+ "workspaceMcp.statusTitle": "Host status",
21856
+ "workspaceMcp.loading": "Reading Workspace MCP status…",
21857
+ "workspaceMcp.statusFailed": "Unable to read Workspace MCP status from the Host.",
21858
+ "workspaceMcp.apiKeyStatus": "API key: {status}",
21859
+ "workspaceMcp.enabledStatus": "Module: {status}",
21860
+ "workspaceMcp.listeningStatus": "Listener: {status}",
21861
+ "workspaceMcp.configured": "configured",
21862
+ "workspaceMcp.notConfigured": "not configured",
21863
+ "workspaceMcp.on": "on",
21864
+ "workspaceMcp.off": "off",
21865
+ "workspaceMcp.url": "Endpoint",
21866
+ "workspaceMcp.refresh": "Refresh status",
21867
+ "workspaceMcp.reason.keyMissing": "Generate a Workspace MCP API key to start the listener.",
21868
+ "workspaceMcp.reason.keyStoreFailed": "The Host could not access the Workspace MCP key file.",
21869
+ "workspaceMcp.reason.disabled": "The Workspace MCP module is disabled.",
21870
+ "workspaceMcp.reason.listenFailed": "The local MCP listener could not start.",
21871
+ "workspaceMcp.reason.other": "The Host reported an additional status reason.",
21758
21872
  "feature.assistant.label": "Global smart assistant (Testing)",
21759
21873
  "feature.assistant.description": "Summarize progress across selected workspaces and route voice or text instructions to an explicit target session. After enabling this module, fully quit and restart DSH Desktop.",
21760
21874
  "voice.globalAssistant": "Global voice assistant (Testing)",
@@ -22215,6 +22329,94 @@ window.__ModuleLoader__.load({
22215
22329
  "feature.cliAdapters.description": "管理外部 Agent",
22216
22330
  "feature.workspaceSession.label": "工作区会话增强",
22217
22331
  "feature.workspaceSession.description": "增强工作区会话体验",
22332
+ "feature.workspaceMcp.label": "工作区 MCP 服务",
22333
+ "feature.workspaceMcp.description": "通过 Host 本机认证监听器向 MCP 客户端提供受管理工作区工具。",
22334
+ "workspaceMcp.setupTitle": "MCP 访问密钥",
22335
+ "workspaceMcp.setupInstruction": "生成密钥后会保存在 Host 并自动启用本机 MCP 监听器。",
22336
+ "workspaceMcp.generateKey": "生成密钥并启用",
22337
+ "workspaceMcp.rotateKey": "轮换密钥",
22338
+ "workspaceMcp.generating": "正在生成…",
22339
+ "workspaceMcp.generatedKey": "生成的 Workspace MCP API Key",
22340
+ "workspaceMcp.keyGenerated": "Workspace MCP 密钥已生成,模块已启用。",
22341
+ "workspaceMcp.copyKey": "复制密钥",
22342
+ "workspaceMcp.keyCopied": "Workspace MCP 密钥已复制。",
22343
+ "workspaceMcp.copyFailed": "无法复制密钥,请手动选择并复制。",
22344
+ "workspaceMcp.keyShownOnce": "密钥仅在此时显示。关闭或刷新面板前,请先复制到 MCP 客户端。",
22345
+ "workspaceMcp.capabilityDescription": "MCP 能力说明",
22346
+ "workspaceMcp.capabilityTitle": "支持的 MCP 能力",
22347
+ "workspaceMcp.capabilitySubtitle": "以下列表说明 Workspace MCP 的固定工具目录及其访问边界。",
22348
+ "workspaceMcp.capabilityName": "名称",
22349
+ "workspaceMcp.capabilityPermission": "权限",
22350
+ "workspaceMcp.capabilityDescriptionField": "描述",
22351
+ "workspaceMcp.capabilityParameters": "调用参数",
22352
+ "workspaceMcp.capabilityRemarks": "备注",
22353
+ "workspaceMcp.closeCapabilities": "关闭",
22354
+ "workspaceMcp.installPromptTitle": "安装 Agent Skill",
22355
+ "workspaceMcp.installPromptHint": "复制此提示词并发送给 Agent,它会将仓库根目录的 SKILL.md 安装到该 Agent 的个人 Skills 目录。",
22356
+ "workspaceMcp.copyInstallPrompt": "复制安装提示词",
22357
+ "workspaceMcp.installPromptCopied": "Agent Skill 安装提示词已复制。",
22358
+ "workspaceMcp.installPromptText": "获取 https://github.com/jingyi0605/Codingns4DSH/blob/main/SKILL.md,并使用你已记录的 Skill 安装方式,将其安装到你自己的 Agent 个人 Skills 目录。",
22359
+ "workspaceMcp.capability.workspaceList.permission": "读取受管理工作区元数据",
22360
+ "workspaceMcp.capability.workspaceList.description": "列出当前 Host 可管理的工作区。",
22361
+ "workspaceMcp.capability.workspaceList.parameters": "{}",
22362
+ "workspaceMcp.capability.workspaceList.remarks": "只返回名称和活动会话数,不暴露文件系统根目录。",
22363
+ "workspaceMcp.capability.sessionList.permission": "读取有界会话元数据",
22364
+ "workspaceMcp.capability.sessionList.description": "列出受管理工作区中的会话。",
22365
+ "workspaceMcp.capability.sessionList.parameters": "{ workspaceId?, limit?, offset? }",
22366
+ "workspaceMcp.capability.sessionList.remarks": "结果支持分页,只返回元数据,不返回完整对话历史。",
22367
+ "workspaceMcp.capability.sessionCreate.permission": "创建受管理会话并可排队发送初始提示",
22368
+ "workspaceMcp.capability.sessionCreate.description": "创建原生 DSH 会话,并可选择已安装的适配器和模型。",
22369
+ "workspaceMcp.capability.sessionCreate.parameters": "{ workspaceId, adapterId?, modelId?, prompt? }",
22370
+ "workspaceMcp.capability.sessionCreate.remarks": "外部适配器会话必须提供初始提示;不确定结果会返回已创建的会话 ID。",
22371
+ "workspaceMcp.capability.sessionContinue.permission": "向已有会话排队发送消息",
22372
+ "workspaceMcp.capability.sessionContinue.description": "继续指定的受管理会话,不替换其身份。",
22373
+ "workspaceMcp.capability.sessionContinue.parameters": "{ sessionId, message }",
22374
+ "workspaceMcp.capability.sessionContinue.remarks": "消息接纳前会重新校验会话归属和可用性。",
22375
+ "workspaceMcp.capability.sessionQuestions.permission": "读取未解决的原生问题",
22376
+ "workspaceMcp.capability.sessionQuestions.description": "列出受管理会话中等待回答的问题。",
22377
+ "workspaceMcp.capability.sessionQuestions.parameters": "{ sessionId }",
22378
+ "workspaceMcp.capability.sessionQuestions.remarks": "问题内容和选项有大小限制;归档或无关会话会被拒绝。",
22379
+ "workspaceMcp.capability.sessionAnswer.permission": "回答一个未解决的原生问题",
22380
+ "workspaceMcp.capability.sessionAnswer.description": "通过 DSH 原生问题机制提交经过校验的答案。",
22381
+ "workspaceMcp.capability.sessionAnswer.parameters": "{ sessionId, callId, answer }",
22382
+ "workspaceMcp.capability.sessionAnswer.remarks": "callId 必须来自当前问题列表,答案会按原生选项校验。",
22383
+ "workspaceMcp.capability.sessionRequestPermission.permission": "请求人工审批",
22384
+ "workspaceMcp.capability.sessionRequestPermission.description": "通过 DSH 原生审批界面请求对某项操作作出决定。",
22385
+ "workspaceMcp.capability.sessionRequestPermission.parameters": "{ sessionId, toolName, reason, callId? }",
22386
+ "workspaceMcp.capability.sessionRequestPermission.remarks": "必须存在活动 DSH 回合;MCP 客户端不能批准自己的请求。",
22387
+ "workspaceMcp.capability.sessionPermissionStatus.permission": "读取已拥有的审批结果",
22388
+ "workspaceMcp.capability.sessionPermissionStatus.description": "读取当前 MCP 模块创建的审批请求状态。",
22389
+ "workspaceMcp.capability.sessionPermissionStatus.parameters": "{ sessionId, requestId }",
22390
+ "workspaceMcp.capability.sessionPermissionStatus.remarks": "只能查询当前模块保留的 DSH 原生请求 ID。",
22391
+ "workspaceMcp.capability.terminalList.permission": "读取活动终端元数据",
22392
+ "workspaceMcp.capability.terminalList.description": "列出一个受管理会话附属的终端实例。",
22393
+ "workspaceMcp.capability.terminalList.parameters": "{ workspaceId, sessionId }",
22394
+ "workspaceMcp.capability.terminalList.remarks": "只返回指定工作区和会话范围内的活动终端。",
22395
+ "workspaceMcp.capability.terminalRead.permission": "读取终端输出",
22396
+ "workspaceMcp.capability.terminalRead.description": "读取一个活动终端的有界输出快照。",
22397
+ "workspaceMcp.capability.terminalRead.parameters": "{ workspaceId, sessionId, terminalId, limitBytes? }",
22398
+ "workspaceMcp.capability.terminalRead.remarks": "输出上限为 64 KiB,不会读取其他终端。",
22399
+ "workspaceMcp.capability.terminalWrite.permission": "写入终端输入",
22400
+ "workspaceMcp.capability.terminalWrite.description": "向一个已有的活动终端进程写入有界文本。",
22401
+ "workspaceMcp.capability.terminalWrite.parameters": "{ workspaceId, sessionId, terminalId, data }",
22402
+ "workspaceMcp.capability.terminalWrite.remarks": "输入上限为 16 KiB,拒绝 NUL 字节,不能创建任意进程。",
22403
+ "workspaceMcp.statusTitle": "Host 状态",
22404
+ "workspaceMcp.loading": "正在读取工作区 MCP 状态…",
22405
+ "workspaceMcp.statusFailed": "无法从 Host 读取工作区 MCP 状态。",
22406
+ "workspaceMcp.apiKeyStatus": "API Key:{status}",
22407
+ "workspaceMcp.enabledStatus": "模块:{status}",
22408
+ "workspaceMcp.listeningStatus": "监听器:{status}",
22409
+ "workspaceMcp.configured": "已配置",
22410
+ "workspaceMcp.notConfigured": "未配置",
22411
+ "workspaceMcp.on": "已开启",
22412
+ "workspaceMcp.off": "已关闭",
22413
+ "workspaceMcp.url": "端点",
22414
+ "workspaceMcp.refresh": "刷新状态",
22415
+ "workspaceMcp.reason.keyMissing": "请生成 Workspace MCP API Key 以启动监听器。",
22416
+ "workspaceMcp.reason.keyStoreFailed": "Host 无法访问 Workspace MCP 密钥文件。",
22417
+ "workspaceMcp.reason.disabled": "工作区 MCP 模块已关闭。",
22418
+ "workspaceMcp.reason.listenFailed": "本机 MCP 监听器无法启动。",
22419
+ "workspaceMcp.reason.other": "Host 返回了其他状态原因。",
22218
22420
  "feature.assistant.label": "全局智能助理(测试中)",
22219
22421
  "feature.assistant.description": "汇总受管工作区进展,并把语音或文字指令派发到明确的目标会话。启用该模块后,请彻底重启 DSH Desktop。",
22220
22422
  "voice.globalAssistant": "全局语音助理(测试中)",
@@ -25376,12 +25578,12 @@ window.__ModuleLoader__.load({
25376
25578
  if (typeof fields.status === "number" && fields.status >= 400) return true;
25377
25579
  if (typeof fields.state === "string" && /^(failed|disconnected|closed|degraded)$/iu.test(fields.state)) return true;
25378
25580
  if (typeof fields.event === "string" && fields.event !== event) {
25379
- const nestedFields = isRecord$11(fields.fields) ? fields.fields : {};
25581
+ const nestedFields = isRecord$12(fields.fields) ? fields.fields : {};
25380
25582
  return isDshTransportWarningEvent(fields.event, nestedFields);
25381
25583
  }
25382
25584
  return false;
25383
25585
  }
25384
- function isRecord$11(value) {
25586
+ function isRecord$12(value) {
25385
25587
  return typeof value === "object" && value !== null && !Array.isArray(value);
25386
25588
  }
25387
25589
  /**
@@ -27423,7 +27625,7 @@ window.__ModuleLoader__.load({
27423
27625
  async onMessage(event) {
27424
27626
  const sourceWindow = event.source;
27425
27627
  if (this.disposed || sourceWindow === null || !this.iframeWindows.has(sourceWindow)) return;
27426
- if (!isRecord$10(event.data) || typeof event.data.kind !== "string") return;
27628
+ if (!isRecord$11(event.data) || typeof event.data.kind !== "string") return;
27427
27629
  const message = event.data;
27428
27630
  if (message.kind !== "dsh-web-debug" && typeof message.id !== "string") return;
27429
27631
  if (typeof message.id === "string") this.messageWindows.set(message.id, sourceWindow);
@@ -27434,7 +27636,7 @@ window.__ModuleLoader__.load({
27434
27636
  try {
27435
27637
  if (message.kind === "dsh-web-debug") {
27436
27638
  const eventName = typeof message.event === "string" ? message.event : "unknown";
27437
- const fields = isRecord$10(message.fields) ? message.fields : {};
27639
+ const fields = isRecord$11(message.fields) ? message.fields : {};
27438
27640
  this.debug.log(`iframe.${eventName}`, fields);
27439
27641
  if (/^(bridge\.ws\.|client\.connection\.)/u.test(eventName)) await this.options.transport.webRequest("web.debug", {
27440
27642
  event: eventName,
@@ -27443,7 +27645,7 @@ window.__ModuleLoader__.load({
27443
27645
  return;
27444
27646
  }
27445
27647
  if (message.kind === "fetch") {
27446
- const input = isRecord$10(message.input) ? message.input : {};
27648
+ const input = isRecord$11(message.input) ? message.input : {};
27447
27649
  const path = resolveRemotePath(typeof input.path === "string" ? input.path : "/");
27448
27650
  const body = typeof message.body === "string" ? message.body : typeof input.body === "string" ? input.body : void 0;
27449
27651
  const response = path.startsWith("/assets/") || path.startsWith("/plugins/") ? {
@@ -27469,7 +27671,7 @@ window.__ModuleLoader__.load({
27469
27671
  return;
27470
27672
  }
27471
27673
  if (message.kind === "script") {
27472
- const input = isRecord$10(message.input) ? message.input : {};
27674
+ const input = isRecord$11(message.input) ? message.input : {};
27473
27675
  const path = resolveRemotePath(typeof input.path === "string" ? input.path : "/");
27474
27676
  const url = await this.loadScript(path, void 0);
27475
27677
  this.postResponse(message.id, {
@@ -27479,7 +27681,7 @@ window.__ModuleLoader__.load({
27479
27681
  return;
27480
27682
  }
27481
27683
  if (message.kind === "image") {
27482
- const input = isRecord$10(message.input) ? message.input : {};
27684
+ const input = isRecord$11(message.input) ? message.input : {};
27483
27685
  const path = resolveRemotePath(typeof input.path === "string" ? input.path : "/");
27484
27686
  const url = await this.loadImage(path);
27485
27687
  this.postResponse(message.id, {
@@ -27489,7 +27691,7 @@ window.__ModuleLoader__.load({
27489
27691
  return;
27490
27692
  }
27491
27693
  if (message.kind === "style") {
27492
- const input = isRecord$10(message.input) ? message.input : {};
27694
+ const input = isRecord$11(message.input) ? message.input : {};
27493
27695
  const path = resolveRemotePath(typeof input.path === "string" ? input.path : "/");
27494
27696
  const url = await this.loadStyle(path, void 0);
27495
27697
  this.postResponse(message.id, {
@@ -27499,7 +27701,7 @@ window.__ModuleLoader__.load({
27499
27701
  return;
27500
27702
  }
27501
27703
  if (message.kind === "ws.open") {
27502
- const input = isRecord$10(message.input) ? message.input : {};
27704
+ const input = isRecord$11(message.input) ? message.input : {};
27503
27705
  const opened = this.options.transport.openWebStreamWithId("web.ws.open", {
27504
27706
  sessionId: this.sessionIdValue,
27505
27707
  path: resolveRemotePath(typeof input.path === "string" ? input.path : "/")
@@ -27591,7 +27793,7 @@ window.__ModuleLoader__.load({
27591
27793
  for await (const value of stream) {
27592
27794
  if (!opened) {
27593
27795
  opened = true;
27594
- if (isRecord$10(value) && value.opened === true) {
27796
+ if (isRecord$11(value) && value.opened === true) {
27595
27797
  this.socketReady.get(id)?.resolve();
27596
27798
  this.socketReady.delete(id);
27597
27799
  continue;
@@ -28245,7 +28447,7 @@ window.__ModuleLoader__.load({
28245
28447
  const policy = "default-src 'self' blob: data:; script-src 'self' blob: data: 'unsafe-inline' 'unsafe-eval'; style-src 'self' blob: data: 'unsafe-inline'; img-src 'self' blob: data:; font-src 'self' blob: data:; media-src 'self' blob: data:; worker-src 'self' blob:; connect-src 'none'; frame-src 'self' blob: data:";
28246
28448
  for (const node of documentValue.querySelectorAll("meta[http-equiv]")) if ((node.getAttribute("http-equiv") ?? "").toLowerCase() === "content-security-policy") node.setAttribute("content", policy);
28247
28449
  }
28248
- function isRecord$10(value) {
28450
+ function isRecord$11(value) {
28249
28451
  return typeof value === "object" && value !== null && !Array.isArray(value);
28250
28452
  }
28251
28453
  function toBytes(value) {
@@ -29535,11 +29737,11 @@ window.__ModuleLoader__.load({
29535
29737
  };
29536
29738
  }
29537
29739
  function readMarker(event) {
29538
- if (!isRecord$9(event)) return null;
29740
+ if (!isRecord$10(event)) return null;
29539
29741
  const data = event.data;
29540
- if (!isRecord$9(data)) return null;
29541
- const candidate = event.type === "assistant/live-chunk" ? isRecord$9(data.chunk) ? data.chunk.codingnsExternalTool : void 0 : event.type === "assistant/attempt" ? data.codingnsExternalTool : void 0;
29542
- if (!isRecord$9(candidate) || candidate.source !== "codingns-external-tool") return null;
29742
+ if (!isRecord$10(data)) return null;
29743
+ const candidate = event.type === "assistant/live-chunk" ? isRecord$10(data.chunk) ? data.chunk.codingnsExternalTool : void 0 : event.type === "assistant/attempt" ? data.codingnsExternalTool : void 0;
29744
+ if (!isRecord$10(candidate) || candidate.source !== "codingns-external-tool") return null;
29543
29745
  if (typeof candidate.callId !== "string" || candidate.callId === "") return null;
29544
29746
  if (typeof candidate.name !== "string" || typeof candidate.arguments !== "string") return null;
29545
29747
  if (candidate.phase !== "start" && candidate.phase !== "update") return null;
@@ -29555,7 +29757,7 @@ window.__ModuleLoader__.load({
29555
29757
  ...typeof candidate.error === "string" ? { error: candidate.error } : {}
29556
29758
  };
29557
29759
  }
29558
- function isRecord$9(value) {
29760
+ function isRecord$10(value) {
29559
29761
  return typeof value === "object" && value !== null && !Array.isArray(value);
29560
29762
  }
29561
29763
  //#endregion
@@ -30033,9 +30235,9 @@ window.__ModuleLoader__.load({
30033
30235
  return {
30034
30236
  async targets(signal, query) {
30035
30237
  const rows = /* @__PURE__ */ new Map();
30036
- const localRows = record$1(record$1(sessions.list?.getSnapshot())?.byId) ?? {};
30238
+ const localRows = record$2(record$2(sessions.list?.getSnapshot())?.byId) ?? {};
30037
30239
  for (const [id, value] of Object.entries(localRows)) rows.set(id, {
30038
- ...record$1(value),
30240
+ ...record$2(value),
30039
30241
  sessionId: id
30040
30242
  });
30041
30243
  const remote = service(context, "remote.session");
@@ -30047,17 +30249,17 @@ window.__ModuleLoader__.load({
30047
30249
  return query === void 0 ? targets : selectRecentTerminalShareTargets(targets, snapshot, query.sessionId, query.limit);
30048
30250
  };
30049
30251
  signal?.throwIfAborted();
30050
- const ids = array$1((query === void 0 ? void 0 : array$1(record$1(workspaceSnapshot())?.items).map(record$1).find((item) => array$1(item?.sessionIds).includes(query.sessionId)))?.sessionIds);
30252
+ const ids = array$1((query === void 0 ? void 0 : array$1(record$2(workspaceSnapshot())?.items).map(record$2).find((item) => array$1(item?.sessionIds).includes(query.sessionId)))?.sessionIds);
30051
30253
  if (query !== void 0 && ids.length > 0 && ids.every((id) => typeof id === "string" && rows.has(id))) return selectTargets();
30052
30254
  do {
30053
30255
  signal?.throwIfAborted();
30054
30256
  if (remote?.list === void 0) break;
30055
- const page = record$1(unwrap$1(await remote.list(cursor === void 0 ? {} : { cursor }, signal)));
30257
+ const page = record$2(unwrap$1(await remote.list(cursor === void 0 ? {} : { cursor }, signal)));
30056
30258
  for (const value of array$1(page?.items)) {
30057
- const id = string$3(record$1(value)?.sessionId);
30259
+ const id = string$3(record$2(value)?.sessionId);
30058
30260
  if (id !== "") rows.set(id, {
30059
- ...record$1(value),
30060
- ...record$1(localRows[id]),
30261
+ ...record$2(value),
30262
+ ...record$2(localRows[id]),
30061
30263
  sessionId: id
30062
30264
  });
30063
30265
  }
@@ -30070,7 +30272,7 @@ window.__ModuleLoader__.load({
30070
30272
  return selectTargets();
30071
30273
  },
30072
30274
  source(sessionId) {
30073
- const workspace = array$1(record$1(workspaceSnapshot())?.items).map(record$1).find((item) => array$1(item?.sessionIds).includes(sessionId));
30275
+ const workspace = array$1(record$2(workspaceSnapshot())?.items).map(record$2).find((item) => array$1(item?.sessionIds).includes(sessionId));
30074
30276
  return {
30075
30277
  hostId: require_primitives_adapter.parseVirtualSessionId(sessionId)?.hostId ?? "local",
30076
30278
  workspaceId: string$3(workspace?.workspaceId)
@@ -30087,7 +30289,7 @@ window.__ModuleLoader__.load({
30087
30289
  });
30088
30290
  },
30089
30291
  async createTarget(sourceSessionId) {
30090
- const workspaceId = string$3(array$1(record$1(workspaceSnapshot())?.items).map(record$1).find((item) => array$1(item?.sessionIds).includes(sourceSessionId))?.workspaceId);
30292
+ const workspaceId = string$3(array$1(record$2(workspaceSnapshot())?.items).map(record$2).find((item) => array$1(item?.sessionIds).includes(sourceSessionId))?.workspaceId);
30091
30293
  if (workspaceId === "") throw new Error("当前会话的工作区尚未就绪,请稍后重试");
30092
30294
  if (typeof sessions.create !== "function") throw new Error("当前 DSH 缺少新建会话能力");
30093
30295
  const sessionId = await sessions.create({ workspaceId });
@@ -30125,12 +30327,12 @@ window.__ModuleLoader__.load({
30125
30327
  }
30126
30328
  /** 原生工作区快照同时包含 PeerHost 投影和归档集合。 */
30127
30329
  function readTerminalShareTargets(rows, snapshot) {
30128
- const state = record$1(snapshot);
30129
- const workspaces = array$1(state?.items).map(record$1);
30330
+ const state = record$2(snapshot);
30331
+ const workspaces = array$1(state?.items).map(record$2);
30130
30332
  const archived = new Set(array$1(state?.archivedSessionIds));
30131
30333
  for (const workspace of workspaces) for (const id of array$1(workspace?.archivedSessionIds)) archived.add(id);
30132
30334
  return rows.flatMap((raw) => {
30133
- const item = record$1(raw);
30335
+ const item = record$2(raw);
30134
30336
  const sessionId = string$3(item?.sessionId) || string$3(item?.id);
30135
30337
  if (sessionId === "" || item?.blank === true || archived.has(sessionId) || Number(item?.archivedAt) > 0) return [];
30136
30338
  const workspace = workspaces.find((entry) => array$1(entry?.sessionIds).includes(sessionId));
@@ -30138,7 +30340,7 @@ window.__ModuleLoader__.load({
30138
30340
  const hostId = require_primitives_adapter.parseVirtualSessionId(sessionId)?.hostId ?? require_primitives_adapter.parseVirtualWorkspaceId(workspaceId)?.hostId ?? "local";
30139
30341
  return [{
30140
30342
  sessionId,
30141
- title: string$3(record$1(record$1(item?.projections)?.values)?.title) || string$3(item?.title) || sessionId,
30343
+ title: string$3(record$2(record$2(item?.projections)?.values)?.title) || string$3(item?.title) || sessionId,
30142
30344
  workspaceId,
30143
30345
  hostId,
30144
30346
  workspaceTitle: string$3(workspace?.title) || string$3(workspace?.path) || workspaceId,
@@ -30149,7 +30351,7 @@ window.__ModuleLoader__.load({
30149
30351
  }
30150
30352
  /** 新建入口由菜单单独提供;已有会话只按更新时间排序,初始最多五项。 */
30151
30353
  function selectRecentTerminalShareTargets(targets, snapshot, sessionId, limit = 5) {
30152
- const workspace = array$1(record$1(snapshot)?.items).map(record$1).find((item) => array$1(item?.sessionIds).includes(sessionId));
30354
+ const workspace = array$1(record$2(snapshot)?.items).map(record$2).find((item) => array$1(item?.sessionIds).includes(sessionId));
30153
30355
  const workspaceId = string$3(workspace?.workspaceId) || targets.find((item) => item.sessionId === sessionId)?.workspaceId;
30154
30356
  const workspaceOrder = array$1(workspace?.sessionIds);
30155
30357
  return targets.filter((item) => workspaceId ? item.workspaceId === workspaceId : item.sessionId === sessionId).sort((left, right) => (right.updatedAt ?? 0) - (left.updatedAt ?? 0) || workspaceOrder.indexOf(left.sessionId) - workspaceOrder.indexOf(right.sessionId)).slice(0, Math.max(1, limit));
@@ -30159,8 +30361,8 @@ window.__ModuleLoader__.load({
30159
30361
  return Number.isFinite(time) ? time : 0;
30160
30362
  }
30161
30363
  function isArchivedSession(sessionId, snapshot) {
30162
- const state = record$1(snapshot);
30163
- return array$1(state?.archivedSessionIds).includes(sessionId) || array$1(state?.items).some((workspace) => array$1(record$1(workspace)?.archivedSessionIds).includes(sessionId));
30364
+ const state = record$2(snapshot);
30365
+ return array$1(state?.archivedSessionIds).includes(sessionId) || array$1(state?.items).some((workspace) => array$1(record$2(workspace)?.archivedSessionIds).includes(sessionId));
30164
30366
  }
30165
30367
  function service(context, name) {
30166
30368
  try {
@@ -30170,7 +30372,7 @@ window.__ModuleLoader__.load({
30170
30372
  return;
30171
30373
  }
30172
30374
  }
30173
- function record$1(value) {
30375
+ function record$2(value) {
30174
30376
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
30175
30377
  }
30176
30378
  function string$3(value) {
@@ -30180,8 +30382,8 @@ window.__ModuleLoader__.load({
30180
30382
  return Array.isArray(value) ? value : [];
30181
30383
  }
30182
30384
  function unwrap$1(value) {
30183
- const item = record$1(value);
30184
- if (item?.ok === false) throw new Error(string$3(record$1(item.error)?.message) || "读取会话列表失败");
30385
+ const item = record$2(value);
30386
+ if (item?.ok === false) throw new Error(string$3(record$2(item.error)?.message) || "读取会话列表失败");
30185
30387
  return item?.ok === true ? item.value : value;
30186
30388
  }
30187
30389
  //#endregion
@@ -30237,7 +30439,7 @@ window.__ModuleLoader__.load({
30237
30439
  const call = block?.kind === void 0 ? block : block.call;
30238
30440
  const args = parseRecord(call?.argsRaw);
30239
30441
  const questions = args?.questions;
30240
- if (!Array.isArray(questions) || !questions.every(isQuestion)) return void 0;
30442
+ if (!Array.isArray(questions) || !questions.every(isQuestion$1)) return void 0;
30241
30443
  const output = parseRecord(Array.isArray(block?.content) && block.content.length === 1 && block.content[0]?.type === "text" ? block.content[0].text : void 0);
30242
30444
  return {
30243
30445
  args,
@@ -30306,7 +30508,7 @@ window.__ModuleLoader__.load({
30306
30508
  return;
30307
30509
  }
30308
30510
  }
30309
- function isQuestion(value) {
30511
+ function isQuestion$1(value) {
30310
30512
  return value !== null && typeof value === "object" && typeof value.id === "string" && typeof value.question === "string" && (value.header === void 0 || typeof value.header === "string") && (value.detail === void 0 || typeof value.detail === "string") && (value.options === void 0 || Array.isArray(value.options) && value.options.every((o) => o !== null && typeof o?.label === "string" && (o.description === void 0 || typeof o.description === "string")));
30311
30513
  }
30312
30514
  function validAnswers(value) {
@@ -30620,6 +30822,311 @@ window.__ModuleLoader__.load({
30620
30822
  } };
30621
30823
  }
30622
30824
  //#endregion
30825
+ //#region src/dsh-capabilities/host/workspace-session-control-adapter.ts
30826
+ /** Narrow typed adapter over DSH's public SessionController surface. */
30827
+ function supportsWorkspaceSessionControl(context) {
30828
+ const controller = asController(readContext(context, "sessionController"));
30829
+ const agents = asRecord$10(readContext(context, "agents"));
30830
+ return controller !== void 0 && typeof controller.create === "function" && typeof controller.list === "function" && typeof controller.prompt === "function" && typeof controller.modelCatalog === "function" && typeof agents?.get === "function";
30831
+ }
30832
+ function createWorkspaceSessionControlAdapter(context) {
30833
+ const controller = asController(readContext(context, "sessionController"));
30834
+ if (!supportsWorkspaceSessionControl(context) || controller === void 0) throw new TypeError("DSH SessionController workspace control is unavailable");
30835
+ const agents = controller.agents;
30836
+ const agentRegistry = asRecord$10(readContext(context, "agents"));
30837
+ const llm = asRecord$10(readContext(context, "llm"));
30838
+ const selectModel = async (sessionId, selection, signal) => {
30839
+ signal.throwIfAborted();
30840
+ if (agents?.selectForNextRequest === void 0 || llm === void 0 || typeof llm.resolveCallConfig !== "function") throw new Error("Session-local DSH model selection is unavailable");
30841
+ if (controller.resolveAgent === void 0) throw new Error("DSH Session Agent resolution is unavailable");
30842
+ const found = asRecord$10(await controller.resolveAgent.call(controller, sessionId));
30843
+ if (found === void 0 || "error" in found || found.agent === void 0) throw new Error("DSH session Agent is unavailable for model selection");
30844
+ const resolved = asRecord$10(await llm.resolveCallConfig.call(llm, selection));
30845
+ if (typeof resolved?.provider !== "string" || typeof resolved.model !== "string") throw new Error("The requested DSH model route is unavailable");
30846
+ const liveAgent = (agentRegistry?.get)?.call(agentRegistry, sessionId);
30847
+ if (liveAgent === void 0 || liveAgent !== found.agent) throw new Error("DSH did not retain the selected Session Agent");
30848
+ agents.selectForNextRequest.call(agents, found.agent, {
30849
+ provider: resolved.provider,
30850
+ model: resolved.model,
30851
+ ...typeof resolved.reasoningEffort === "string" ? { reasoningEffort: resolved.reasoningEffort } : {}
30852
+ });
30853
+ return { selected: {
30854
+ provider: resolved.provider,
30855
+ model: resolved.model,
30856
+ ...typeof resolved.reasoningEffort === "string" ? { reasoningEffort: resolved.reasoningEffort } : {}
30857
+ } };
30858
+ };
30859
+ return {
30860
+ create: (request, signal) => {
30861
+ signal.throwIfAborted();
30862
+ return controller.create.call(controller, request);
30863
+ },
30864
+ list: (signal) => {
30865
+ signal.throwIfAborted();
30866
+ return controller.list.call(controller, {}, signal);
30867
+ },
30868
+ prompt: (request, signal) => controller.prompt.call(controller, request, signal),
30869
+ modelCatalog: () => controller.modelCatalog.call(controller),
30870
+ ...typeof controller.resolveAgent === "function" ? { prepareSession: async (sessionId, signal) => {
30871
+ signal.throwIfAborted();
30872
+ const result = asRecord$10(await controller.resolveAgent.call(controller, sessionId));
30873
+ if (result === void 0 || "error" in result || result.agent === void 0) throw new Error("DSH Session Agent could not be prepared");
30874
+ const liveAgent = (agentRegistry?.get)?.call(agentRegistry, sessionId);
30875
+ if (liveAgent === void 0 || liveAgent !== result.agent) throw new Error("DSH did not retain the prepared Session Agent");
30876
+ } } : {},
30877
+ ...typeof controller.agents?.selectForNextRequest === "function" && typeof llm?.resolveCallConfig === "function" ? { selectModel: async (sessionId, selection, signal) => await selectModel(sessionId, selection, signal) } : {}
30878
+ };
30879
+ }
30880
+ function asController(value) {
30881
+ return asRecord$10(value);
30882
+ }
30883
+ function readContext(context, name) {
30884
+ if (typeof context !== "object" || context === null || typeof context.get !== "function") return void 0;
30885
+ try {
30886
+ return context.get(name);
30887
+ } catch {
30888
+ return;
30889
+ }
30890
+ }
30891
+ function asRecord$10(value) {
30892
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
30893
+ }
30894
+ //#endregion
30895
+ //#region src/dsh-capabilities/host/workspace-pending-questions-adapter.ts
30896
+ function supportsWorkspacePendingQuestions(context) {
30897
+ const get = contextGet(context);
30898
+ return hasMethod$2(get?.("sessions"), "get") && hasMethod$2(get?.("agents"), "get") && hasMethod$2(get?.("agents"), "roots") && hasMethod$2(get?.("sessionProjections"), "stateOf") && hasMethod$2(get?.("userQuestions"), "answer");
30899
+ }
30900
+ function createWorkspacePendingQuestionsAdapter(context) {
30901
+ const get = contextGet(context);
30902
+ const sessions = get?.("sessions");
30903
+ const agents = get?.("agents");
30904
+ const projections = get?.("sessionProjections");
30905
+ const questions = get?.("userQuestions");
30906
+ if (!supportsWorkspacePendingQuestions(context) || !sessions || !agents || !projections || !questions) throw new TypeError("DSH pending questions capability is unavailable");
30907
+ return {
30908
+ list(sessionId) {
30909
+ let session;
30910
+ try {
30911
+ session = sessions.get(sessionId);
30912
+ } catch {
30913
+ return null;
30914
+ }
30915
+ if (session === void 0) return null;
30916
+ let agent;
30917
+ try {
30918
+ agent = agents.get(sessionId);
30919
+ } catch {
30920
+ return null;
30921
+ }
30922
+ if (agent === void 0 || record$1(agent)?.session !== session || !(agents.roots?.() ?? []).includes(agent)) return null;
30923
+ try {
30924
+ const userQuestions = record$1(record$1(projections.stateOf(session, "userQuestions"))?.questions);
30925
+ let active;
30926
+ try {
30927
+ active = userQuestions?.active;
30928
+ } catch {
30929
+ return null;
30930
+ }
30931
+ if (!Array.isArray(active)) return null;
30932
+ return active.flatMap((raw) => {
30933
+ const pending = record$1(raw);
30934
+ if (typeof pending?.callId !== "string" || pending.state !== "open" && pending.state !== "continued" || !Array.isArray(pending.questions)) return [];
30935
+ return [{
30936
+ callId: pending.callId,
30937
+ state: pending.state,
30938
+ questions: pending.questions.filter(isQuestion)
30939
+ }];
30940
+ });
30941
+ } catch {
30942
+ return null;
30943
+ }
30944
+ },
30945
+ answer(sessionId, callId, answer) {
30946
+ let agent;
30947
+ try {
30948
+ agent = agents.get(sessionId);
30949
+ } catch {
30950
+ return false;
30951
+ }
30952
+ let session;
30953
+ try {
30954
+ session = sessions.get(sessionId);
30955
+ } catch {
30956
+ return false;
30957
+ }
30958
+ if (agent === void 0 || session === void 0 || record$1(agent)?.session !== session || !(agents.roots?.() ?? []).includes(agent)) return false;
30959
+ try {
30960
+ return questions.answer(agent, callId, answer) === true;
30961
+ } catch {
30962
+ return false;
30963
+ }
30964
+ }
30965
+ };
30966
+ }
30967
+ function contextGet(context) {
30968
+ if (typeof context !== "object" || context === null || typeof context.get !== "function") return void 0;
30969
+ return context.get.bind(context);
30970
+ }
30971
+ function hasMethod$2(value, name) {
30972
+ return typeof value === "object" && value !== null && typeof value[name] === "function";
30973
+ }
30974
+ function record$1(value) {
30975
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
30976
+ }
30977
+ function isQuestion(value) {
30978
+ const item = record$1(value);
30979
+ return typeof item?.id === "string" && typeof item.question === "string";
30980
+ }
30981
+ //#endregion
30982
+ //#region src/dsh-capabilities/host/workspace-approval-adapter.ts
30983
+ function supportsWorkspaceApproval(context) {
30984
+ const nativeContext = asContext(context);
30985
+ const get = nativeContext?.get.bind(nativeContext);
30986
+ return nativeContext?.on !== void 0 && hasMethod$1(safeGet(get, "agents"), "get") && hasMethod$1(safeGet(get, "approval"), "request");
30987
+ }
30988
+ function createWorkspaceApprovalAdapter(context) {
30989
+ const nativeContext = asContext(context);
30990
+ const get = nativeContext?.get.bind(nativeContext);
30991
+ if (!supportsWorkspaceApproval(context)) throw new TypeError("DSH approval capability is unavailable");
30992
+ const activeRequests = /* @__PURE__ */ new Set();
30993
+ let disposed = false;
30994
+ return {
30995
+ dispose() {
30996
+ disposed = true;
30997
+ for (const cancel of activeRequests) cancel();
30998
+ activeRequests.clear();
30999
+ },
31000
+ startRequest(input) {
31001
+ if (disposed || input.signal.aborted) return null;
31002
+ const agents = safeGet(get, "agents");
31003
+ const approval = safeGet(get, "approval");
31004
+ if (agents === void 0 || approval === void 0) return null;
31005
+ let agent;
31006
+ try {
31007
+ agent = asAgent(agents.get(input.sessionId));
31008
+ } catch {
31009
+ return null;
31010
+ }
31011
+ if (agent?.status !== "running" || agent.session === void 0 || typeof agent.session.seq !== "number" || nativeContext?.on === void 0) return null;
31012
+ const startingSeq = agent.session.seq;
31013
+ const requestLifetime = new AbortController();
31014
+ let requestDisposed = false;
31015
+ let disposeTurnListener;
31016
+ let disposeApprovalListener;
31017
+ let approvalEvent;
31018
+ const abortRequest = () => requestLifetime.abort(input.signal.reason);
31019
+ const cleanup = () => {
31020
+ if (requestDisposed) return;
31021
+ requestDisposed = true;
31022
+ input.signal.removeEventListener("abort", abortRequest);
31023
+ requestLifetime.signal.removeEventListener("abort", cleanup);
31024
+ if (typeof disposeTurnListener === "function") disposeTurnListener();
31025
+ if (typeof disposeApprovalListener === "function") disposeApprovalListener();
31026
+ activeRequests.delete(abortRequest);
31027
+ };
31028
+ requestLifetime.signal.addEventListener("abort", cleanup, { once: true });
31029
+ if (input.signal.aborted) abortRequest();
31030
+ else input.signal.addEventListener("abort", abortRequest, { once: true });
31031
+ try {
31032
+ disposeTurnListener = nativeContext.on("session/event", (sessionValue, eventValue) => {
31033
+ const session = asRecord$9(sessionValue);
31034
+ const event = asRecord$9(eventValue);
31035
+ if ((typeof session?.id === "string" ? session.id : asRecord$9(session?.header)?.id) === input.sessionId && event?.type === "turn/end") requestLifetime.abort();
31036
+ }, { global: true });
31037
+ } catch {
31038
+ cleanup();
31039
+ return null;
31040
+ }
31041
+ if (typeof disposeTurnListener !== "function") {
31042
+ cleanup();
31043
+ return null;
31044
+ }
31045
+ activeRequests.add(abortRequest);
31046
+ try {
31047
+ disposeApprovalListener = nativeContext.on("session/event", (sessionValue, eventValue) => {
31048
+ const session = asRecord$9(sessionValue);
31049
+ const event = asRecord$9(eventValue);
31050
+ const data = asRecord$9(event?.data);
31051
+ const sessionId = typeof session?.id === "string" ? session.id : asRecord$9(session?.header)?.id;
31052
+ if (sessionId !== input.sessionId || event?.type !== "approval/asked" || event.seq !== startingSeq || typeof data?.id !== "string" || data.toolName !== input.toolName || data.callId !== input.callId || data.reason !== input.reason) return;
31053
+ approvalEvent = {
31054
+ sessionId,
31055
+ seq: startingSeq,
31056
+ id: data.id,
31057
+ toolName: data.toolName,
31058
+ ...typeof data.callId === "string" ? { callId: data.callId } : {},
31059
+ ...typeof data.reason === "string" ? { reason: data.reason } : {}
31060
+ };
31061
+ }, { global: true });
31062
+ } catch {
31063
+ cleanup();
31064
+ return null;
31065
+ }
31066
+ if (typeof disposeApprovalListener !== "function") {
31067
+ cleanup();
31068
+ return null;
31069
+ }
31070
+ let outcome;
31071
+ try {
31072
+ outcome = Promise.resolve(approval.request({
31073
+ agent,
31074
+ toolName: input.toolName,
31075
+ ...input.callId === void 0 ? {} : { callId: input.callId },
31076
+ ...input.reason === void 0 ? {} : { reason: input.reason },
31077
+ signal: requestLifetime.signal
31078
+ }));
31079
+ } catch {
31080
+ cleanup();
31081
+ return null;
31082
+ }
31083
+ disposeApprovalListener();
31084
+ disposeApprovalListener = void 0;
31085
+ const event = approvalEvent;
31086
+ if (event === void 0 || event.seq !== startingSeq || event.sessionId !== input.sessionId || event.toolName !== input.toolName || event.callId !== input.callId || event.reason !== input.reason) {
31087
+ cleanup();
31088
+ outcome.catch(() => void 0);
31089
+ return null;
31090
+ }
31091
+ let onCancellation;
31092
+ const cancelled = requestLifetime.signal.aborted ? Promise.resolve("cancelled") : new Promise((resolve) => {
31093
+ onCancellation = () => resolve("cancelled");
31094
+ requestLifetime.signal.addEventListener("abort", onCancellation, { once: true });
31095
+ });
31096
+ return {
31097
+ requestId: event.id,
31098
+ outcome: Promise.race([outcome.then((result) => isOutcome(result) ? result : "unavailable", () => requestLifetime.signal.aborted ? "cancelled" : "unavailable"), cancelled]).finally(() => {
31099
+ if (onCancellation !== void 0) requestLifetime.signal.removeEventListener("abort", onCancellation);
31100
+ cleanup();
31101
+ })
31102
+ };
31103
+ }
31104
+ };
31105
+ }
31106
+ function safeGet(get, name) {
31107
+ try {
31108
+ return get?.(name);
31109
+ } catch {
31110
+ return;
31111
+ }
31112
+ }
31113
+ function asContext(context) {
31114
+ if (typeof context !== "object" || context === null || typeof context.get !== "function") return void 0;
31115
+ return context;
31116
+ }
31117
+ function hasMethod$1(value, name) {
31118
+ return typeof value === "object" && value !== null && typeof value[name] === "function";
31119
+ }
31120
+ function asAgent(value) {
31121
+ return typeof value === "object" && value !== null ? value : void 0;
31122
+ }
31123
+ function asRecord$9(value) {
31124
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
31125
+ }
31126
+ function isOutcome(value) {
31127
+ return value === "allowed-once" || value === "rejected" || value === "cancelled" || value === "unavailable";
31128
+ }
31129
+ //#endregion
30623
31130
  //#region src/dsh-capabilities/host/session-prompt-adapter.ts
30624
31131
  function supportsSessionPromptAssembly(value) {
30625
31132
  const ctx = value;
@@ -31434,6 +31941,39 @@ window.__ModuleLoader__.load({
31434
31941
  detect: (ctx) => hasMethods(read(ctx, "sessionController"), ["create", "resolveAgent"]),
31435
31942
  create: (ctx) => read(ctx, "sessionController")
31436
31943
  });
31944
+ add({
31945
+ id: "workspace-session-control-020",
31946
+ capability: "workspace.session-control",
31947
+ supportedDsh: range020,
31948
+ runtime,
31949
+ priority: 10,
31950
+ status: "supported",
31951
+ introducedIn: "0.2.0-rc.2",
31952
+ detect: supportsWorkspaceSessionControl,
31953
+ create: createWorkspaceSessionControlAdapter
31954
+ });
31955
+ add({
31956
+ id: "workspace-pending-questions-020",
31957
+ capability: "workspace.pending-questions",
31958
+ supportedDsh: range020,
31959
+ runtime,
31960
+ priority: 10,
31961
+ status: "supported",
31962
+ introducedIn: "0.2.0-rc.2",
31963
+ detect: supportsWorkspacePendingQuestions,
31964
+ create: createWorkspacePendingQuestionsAdapter
31965
+ });
31966
+ add({
31967
+ id: "workspace-approval-020",
31968
+ capability: "workspace.approval",
31969
+ supportedDsh: range020,
31970
+ runtime,
31971
+ priority: 10,
31972
+ status: "supported",
31973
+ introducedIn: "0.2.0-rc.2",
31974
+ detect: supportsWorkspaceApproval,
31975
+ create: createWorkspaceApprovalAdapter
31976
+ });
31437
31977
  for (const capability of ["connection.peer", "connection.uplink"]) add({
31438
31978
  id: `${capability}-021`,
31439
31979
  capability,
@@ -34596,7 +35136,7 @@ window.__ModuleLoader__.load({
34596
35136
  }
34597
35137
  /** 只接受形如探测结果的载荷;字段缺失或类型不符时返回 null,而不是猜测“已安装”。 */
34598
35138
  function normalizeDetection(value) {
34599
- if (!isRecord$8(value)) return null;
35139
+ if (!isRecord$9(value)) return null;
34600
35140
  const { installed, source, command, version, reason, checkedAt, platform } = value;
34601
35141
  if (typeof installed !== "boolean" || typeof checkedAt !== "string") return null;
34602
35142
  if (platform !== "macos" && platform !== "windows" && platform !== "linux") return null;
@@ -34614,7 +35154,7 @@ window.__ModuleLoader__.load({
34614
35154
  checkedAt
34615
35155
  };
34616
35156
  }
34617
- function isRecord$8(value) {
35157
+ function isRecord$9(value) {
34618
35158
  return typeof value === "object" && value !== null && !Array.isArray(value);
34619
35159
  }
34620
35160
  //#endregion
@@ -36424,6 +36964,12 @@ window.__ModuleLoader__.load({
36424
36964
  const [dashboardLoading, setDashboardLoading] = (0, react.useState)(true);
36425
36965
  const [dashboardError, setDashboardError] = (0, react.useState)(false);
36426
36966
  const [dashboardIssue, setDashboardIssue] = (0, react.useState)(null);
36967
+ const [quotaPolicyPending, setQuotaPolicyPending] = (0, react.useState)(null);
36968
+ const [quotaPolicyFailed, setQuotaPolicyFailed] = (0, react.useState)(false);
36969
+ const [quotaResetTarget, setQuotaResetTarget] = (0, react.useState)(null);
36970
+ const [quotaResetPending, setQuotaResetPending] = (0, react.useState)(false);
36971
+ const [quotaResetResult, setQuotaResetResult] = (0, react.useState)(null);
36972
+ const [quotaResetFailed, setQuotaResetFailed] = (0, react.useState)(false);
36427
36973
  const [chartMetric, setChartMetric] = (0, react.useState)("calls");
36428
36974
  const [sessionSearch, setSessionSearch] = (0, react.useState)("");
36429
36975
  const [selectedSession, setSelectedSession] = (0, react.useState)(null);
@@ -36446,14 +36992,17 @@ window.__ModuleLoader__.load({
36446
36992
  const dashboardSequence = (0, react.useRef)(0);
36447
36993
  const requestSequence = (0, react.useRef)(0);
36448
36994
  const managementSequence = (0, react.useRef)(0);
36449
- const loadDashboard = (0, react.useCallback)(async (signal) => {
36995
+ const loadDashboard = (0, react.useCallback)(async (signal, force = false) => {
36450
36996
  const sequence = dashboardSequence.current + 1;
36451
36997
  dashboardSequence.current = sequence;
36452
36998
  setDashboardLoading(true);
36453
36999
  setDashboardError(false);
36454
37000
  setDashboardIssue(null);
36455
37001
  try {
36456
- const next = await callMagpieRpc(rpc, "usage/dashboard", { period }, signal);
37002
+ const next = await callMagpieRpc(rpc, "usage/dashboard", {
37003
+ period,
37004
+ ...force ? { force: true } : {}
37005
+ }, signal);
36457
37006
  if (signal.aborted || sequence !== dashboardSequence.current) return;
36458
37007
  setDashboard(next);
36459
37008
  const issue = next?.availabilityIssue ?? (next === null ? "host-rpc-outdated" : null);
@@ -36468,6 +37017,48 @@ window.__ModuleLoader__.load({
36468
37017
  if (!signal.aborted && sequence === dashboardSequence.current) setDashboardLoading(false);
36469
37018
  }
36470
37019
  }, [period, rpc]);
37020
+ const updateQuotaPolicy = (0, react.useCallback)(async (user, policy, enabled) => {
37021
+ if (quotaPolicyPending !== null) return;
37022
+ setQuotaPolicyPending(`${user}:${policy}`);
37023
+ setQuotaPolicyFailed(false);
37024
+ try {
37025
+ await callMagpieRpc(rpc, "usage/account/update", {
37026
+ user,
37027
+ policy,
37028
+ enabled
37029
+ });
37030
+ await loadDashboard(new AbortController().signal, true);
37031
+ } catch {
37032
+ setQuotaPolicyFailed(true);
37033
+ } finally {
37034
+ setQuotaPolicyPending(null);
37035
+ }
37036
+ }, [
37037
+ loadDashboard,
37038
+ quotaPolicyPending,
37039
+ rpc
37040
+ ]);
37041
+ const closeQuotaReset = () => {
37042
+ if (quotaResetPending) return;
37043
+ setQuotaResetTarget(null);
37044
+ setQuotaResetResult(null);
37045
+ setQuotaResetFailed(false);
37046
+ };
37047
+ const confirmQuotaReset = async () => {
37048
+ const target = quotaResetTarget;
37049
+ if (target === null || quotaResetPending) return;
37050
+ setQuotaResetPending(true);
37051
+ setQuotaResetFailed(false);
37052
+ try {
37053
+ const result = await callMagpieRpc(rpc, "usage/reset", { user: target.user });
37054
+ setQuotaResetResult(result.outcome);
37055
+ await loadDashboard(new AbortController().signal, true);
37056
+ } catch {
37057
+ setQuotaResetFailed(true);
37058
+ } finally {
37059
+ setQuotaResetPending(false);
37060
+ }
37061
+ };
36471
37062
  (0, react.useEffect)(() => {
36472
37063
  const controller = new AbortController();
36473
37064
  loadDashboard(controller.signal);
@@ -36800,8 +37391,30 @@ window.__ModuleLoader__.load({
36800
37391
  dashboard,
36801
37392
  loading: dashboardLoading,
36802
37393
  quotaView,
36803
- t
36804
- })));
37394
+ t,
37395
+ policyPending: quotaPolicyPending,
37396
+ policyFailed: quotaPolicyFailed,
37397
+ onPolicyChange: updateQuotaPolicy,
37398
+ onResetRequest: (quota) => {
37399
+ if (quota.user === null || quota.resets?.count === null || quota.resets?.count === void 0 || quota.resets.count <= 0) return;
37400
+ setQuotaResetTarget({
37401
+ user: quota.user,
37402
+ account: quota.alias ?? quota.name,
37403
+ count: quota.resets.count
37404
+ });
37405
+ setQuotaResetResult(null);
37406
+ setQuotaResetFailed(false);
37407
+ }
37408
+ })), quotaResetTarget !== null && (0, react.createElement)(MagpieQuotaResetDialog, {
37409
+ target: quotaResetTarget,
37410
+ pending: quotaResetPending,
37411
+ result: quotaResetResult,
37412
+ failed: quotaResetFailed,
37413
+ t,
37414
+ onCancel: closeQuotaReset,
37415
+ onConfirm: confirmQuotaReset,
37416
+ onClose: closeQuotaReset
37417
+ }));
36805
37418
  }
36806
37419
  function OverviewPage({ dashboard, loading, error, chartMetric, onChartMetricChange, t, onOpenSession }) {
36807
37420
  const [modelBreakdownMode, setModelBreakdownMode] = (0, react.useState)("aggregate");
@@ -37312,7 +37925,7 @@ window.__ModuleLoader__.load({
37312
37925
  fill: "currentColor"
37313
37926
  }));
37314
37927
  }
37315
- function QuotasPage({ dashboard, loading, quotaView = require_native_workspace_store.DEFAULT_SUBSCRIPTION_REPORT_SETTINGS.quotaView, t }) {
37928
+ function QuotasPage({ dashboard, loading, quotaView = require_native_workspace_store.DEFAULT_SUBSCRIPTION_REPORT_SETTINGS.quotaView, t, policyPending = null, policyFailed = false, onPolicyChange = () => void 0, onResetRequest = () => void 0 }) {
37316
37929
  const quotaUnavailable = dashboard?.quotaStatus !== "available";
37317
37930
  const quotas = dashboard?.quotas ?? [];
37318
37931
  const quotaHistory = dashboard?.quotaHistory ?? [];
@@ -37323,7 +37936,11 @@ window.__ModuleLoader__.load({
37323
37936
  key: group.provider,
37324
37937
  group,
37325
37938
  quotaView,
37326
- t
37939
+ t,
37940
+ policyPending,
37941
+ policyFailed,
37942
+ onPolicyChange,
37943
+ onResetRequest
37327
37944
  }))), (0, react.createElement)("section", { className: "codingns-magpie-panel codingns-magpie-history-panel" }, (0, react.createElement)("div", { className: "codingns-magpie-panel-head" }, (0, react.createElement)("div", void 0, (0, react.createElement)("h2", void 0, t("magpie.quotaHistory")), (0, react.createElement)("p", void 0, t("magpie.quotaHistoryNote")))), quotaHistory.length === 0 ? (0, react.createElement)("div", { className: "codingns-magpie-inline-empty" }, t(dashboard === null && loading ? "magpie.loading" : dashboard?.quotaHistoryStatus === "available" ? "magpie.noHistory" : "magpie.historyUnavailable")) : (0, react.createElement)("div", { className: "codingns-magpie-history-grid" }, ...quotaHistory.map((history) => (0, react.createElement)(QuotaHistoryCard, {
37328
37945
  key: `${history.provider}:${history.user ?? ""}`,
37329
37946
  history
@@ -37403,7 +38020,7 @@ window.__ModuleLoader__.load({
37403
38020
  * 同一厂商的多个账号(如 WorkBuddy 的 4 个)共用一张卡,避免每个账号各占一张卡导致
37404
38021
  * 同类账号被拆散在网格各处;单账号厂商的读法与改造前一致。
37405
38022
  */
37406
- function QuotaGroupCard({ group, quotaView = require_native_workspace_store.DEFAULT_SUBSCRIPTION_REPORT_SETTINGS.quotaView, t }) {
38023
+ function QuotaGroupCard({ group, quotaView = require_native_workspace_store.DEFAULT_SUBSCRIPTION_REPORT_SETTINGS.quotaView, t, policyPending, policyFailed, onPolicyChange, onResetRequest }) {
37407
38024
  const first = group.accounts[0];
37408
38025
  const [collapsed, setCollapsed] = (0, react.useState)(false);
37409
38026
  const multi = group.accounts.length > 1;
@@ -37425,6 +38042,10 @@ window.__ModuleLoader__.load({
37425
38042
  quota,
37426
38043
  quotaView,
37427
38044
  t,
38045
+ policyPending,
38046
+ policyFailed,
38047
+ onPolicyChange,
38048
+ onResetRequest,
37428
38049
  showHeading: multi
37429
38050
  })));
37430
38051
  }
@@ -37435,11 +38056,18 @@ window.__ModuleLoader__.load({
37435
38056
  * Sub2api 的钱包余额、按积分计量的 WorkBuddy)单独显示余额行,与 Magpie 原生卡片
37436
38057
  * 一致——过去这些账号只剩一条空进度条,余额被完全藏起来。
37437
38058
  */
37438
- function QuotaAccountSection({ quota, quotaView = require_native_workspace_store.DEFAULT_SUBSCRIPTION_REPORT_SETTINGS.quotaView, t, showHeading }) {
38059
+ function QuotaAccountSection({ quota, quotaView = require_native_workspace_store.DEFAULT_SUBSCRIPTION_REPORT_SETTINGS.quotaView, t, showHeading, policyPending, policyFailed, onPolicyChange, onResetRequest }) {
37439
38060
  const dailyValues = quota.daily.days.map((point) => point.used).filter((value) => value !== null);
37440
38061
  const balance = resolveQuotaAccountBalance(quota);
37441
38062
  const percentWindows = quota.windows.filter((window) => window.usedPercent !== null || window.remainingPercent !== null);
37442
- return (0, react.createElement)("div", { className: "codingns-magpie-quota-account-block" }, showHeading && (0, react.createElement)("div", { className: "codingns-magpie-quota-account-head" }, (0, react.createElement)("strong", void 0, quota.alias ?? quota.name), quota.user !== null && (0, react.createElement)("span", void 0, t("magpie.account", { account: quota.user })), quota.plan !== null && (0, react.createElement)("small", void 0, quota.plan)), !showHeading && quota.user !== null && (0, react.createElement)("div", { className: "codingns-magpie-quota-account" }, t("magpie.account", { account: quota.user })), balance !== null && (0, react.createElement)("div", { className: "codingns-magpie-quota-balance" }, (0, react.createElement)("span", { className: "codingns-magpie-quota-balance-label" }, t("magpie.balance")), (0, react.createElement)("strong", {
38063
+ return (0, react.createElement)("div", { className: "codingns-magpie-quota-account-block" }, showHeading && (0, react.createElement)("div", { className: "codingns-magpie-quota-account-head" }, (0, react.createElement)("strong", void 0, quota.alias ?? quota.name), quota.user !== null && (0, react.createElement)("span", void 0, t("magpie.account", { account: quota.user })), quota.plan !== null && (0, react.createElement)("small", void 0, quota.plan)), !showHeading && quota.user !== null && (0, react.createElement)("div", { className: "codingns-magpie-quota-account" }, t("magpie.account", { account: quota.user })), quota.provider.toLocaleLowerCase() === "codex" && (0, react.createElement)(MagpieQuotaControls, {
38064
+ quota,
38065
+ t,
38066
+ policyPending,
38067
+ policyFailed,
38068
+ onPolicyChange,
38069
+ onResetRequest
38070
+ }), balance !== null && (0, react.createElement)("div", { className: "codingns-magpie-quota-balance" }, (0, react.createElement)("span", { className: "codingns-magpie-quota-balance-label" }, t("magpie.balance")), (0, react.createElement)("strong", {
37443
38071
  className: balance.money ? "codingns-magpie-quota-balance-value is-money" : "codingns-magpie-quota-balance-value",
37444
38072
  title: balance.raw
37445
38073
  }, balance.text)), quota.windows.length === 0 ? balance === null && (0, react.createElement)("div", { className: "codingns-magpie-inline-empty" }, t("magpie.noLimit")) : percentWindows.length > 0 && percentWindows.map((window) => {
@@ -37453,6 +38081,84 @@ window.__ModuleLoader__.load({
37453
38081
  }, (0, react.createElement)("div", { className: "codingns-magpie-quota-window-head" }, (0, react.createElement)("strong", void 0, window.name), (0, react.createElement)("span", void 0, headline)), (0, react.createElement)("div", { className: "codingns-magpie-track quota-track" }, used === null || remaining === null ? null : (0, react.createElement)("span", { style: { width: `${Math.max(0, Math.min(100, barPercent ?? 0))}%` } })), (0, react.createElement)("div", { className: "codingns-magpie-quota-foot" }, (0, react.createElement)("span", void 0, window.amount !== null && window.limit !== null ? `${formatValue(window.amount)} / ${formatValue(window.limit)} ${window.unit ?? ""}` : window.display ?? ""), window.resetsAt !== null && (0, react.createElement)("span", void 0, t("magpie.resets", { time: formatTimestamp(window.resetsAt) }))));
37454
38082
  }), balance !== null && percentWindows.length === 0 && quota.windows.some((window) => window.resetsAt !== null) && (0, react.createElement)("div", { className: "codingns-magpie-quota-foot" }, ...quota.windows.filter((window) => window.resetsAt !== null).map((window) => (0, react.createElement)("span", { key: window.name }, t("magpie.resets", { time: formatTimestamp(window.resetsAt) })))), quota.daily.days.length > 1 && (0, react.createElement)("div", { className: "codingns-magpie-daily-chart" }, (0, react.createElement)(Sparkline, { values: dailyValues }), (0, react.createElement)("small", void 0, t("magpie.dailyUsage"))));
37455
38083
  }
38084
+ function MagpieQuotaControls({ quota, t, policyPending, policyFailed, onPolicyChange, onResetRequest }) {
38085
+ const user = quota.user;
38086
+ const resetCount = quota.resets?.count;
38087
+ const policyUnavailable = user === null || quota.policyStatus === "unavailable" || quota.autoReset === void 0 || quota.useCredits === void 0;
38088
+ return (0, react.createElement)("div", { className: "codingns-magpie-codex-controls" }, (0, react.createElement)("div", { className: "codingns-magpie-codex-reset-count" }, (0, react.createElement)("span", void 0, t("usage.magpieResetCountLabel")), (0, react.createElement)("strong", void 0, resetCount === null || resetCount === void 0 ? "—" : t("usage.magpieResetCount", { count: resetCount }))), policyUnavailable ? (0, react.createElement)("div", {
38089
+ className: "codingns-magpie-codex-unavailable",
38090
+ role: "status"
38091
+ }, t("magpie.codexPolicyUnavailable")) : quota.autoReset !== void 0 && (0, react.createElement)(MagpieQuotaPolicySwitch, {
38092
+ label: t("usage.magpieResetWhenWeek"),
38093
+ checked: quota.autoReset.week,
38094
+ pending: policyPending === `${user}:week`,
38095
+ disabled: policyPending !== null,
38096
+ onChange: (enabled) => onPolicyChange(user, "week", enabled)
38097
+ }), !policyUnavailable && quota.autoReset !== void 0 && (0, react.createElement)(MagpieQuotaPolicySwitch, {
38098
+ label: t("usage.magpieResetBeforeExpiry"),
38099
+ checked: quota.autoReset.expiring,
38100
+ pending: policyPending === `${user}:expiring`,
38101
+ disabled: policyPending !== null,
38102
+ onChange: (enabled) => onPolicyChange(user, "expiring", enabled)
38103
+ }), !policyUnavailable && quota.useCredits !== void 0 && (0, react.createElement)(MagpieQuotaPolicySwitch, {
38104
+ label: t("usage.magpieUseCredits"),
38105
+ checked: quota.useCredits,
38106
+ pending: policyPending === `${user}:credits`,
38107
+ disabled: policyPending !== null,
38108
+ onChange: (enabled) => onPolicyChange(user, "credits", enabled)
38109
+ }), policyFailed && (0, react.createElement)("div", {
38110
+ className: "codingns-magpie-codex-error",
38111
+ role: "alert"
38112
+ }, t("magpie.codexPolicyFailed")), user !== null && resetCount !== null && resetCount !== void 0 && (0, react.createElement)("button", {
38113
+ type: "button",
38114
+ className: "codingns-magpie-codex-reset-button",
38115
+ disabled: resetCount <= 0,
38116
+ onClick: () => onResetRequest(quota)
38117
+ }, t("usage.magpieUseReset")));
38118
+ }
38119
+ function MagpieQuotaPolicySwitch({ label, checked, pending, disabled, onChange }) {
38120
+ return (0, react.createElement)("button", {
38121
+ type: "button",
38122
+ role: "switch",
38123
+ "aria-checked": checked,
38124
+ disabled,
38125
+ "aria-busy": pending,
38126
+ className: "codingns-magpie-codex-policy",
38127
+ onClick: () => onChange(!checked)
38128
+ }, (0, react.createElement)("span", void 0, label), (0, react.createElement)("span", { className: `codingns-magpie-codex-switch${checked ? " is-on" : ""}` }, (0, react.createElement)("span")));
38129
+ }
38130
+ function MagpieQuotaResetDialog({ target, pending, result, failed, t, onCancel, onConfirm, onClose }) {
38131
+ const finished = result !== null || failed;
38132
+ const outcomeMessage = result === null ? null : t({
38133
+ reset: "usage.resetOutcomeReset",
38134
+ alreadyRedeemed: "usage.resetOutcomeAlreadyRedeemed",
38135
+ nothingToReset: "usage.resetOutcomeNothingToReset",
38136
+ noCredit: "usage.resetOutcomeNoCredit"
38137
+ }[result]);
38138
+ return (0, react.createElement)("div", {
38139
+ className: "codingns-magpie-codex-reset-overlay",
38140
+ role: "presentation",
38141
+ onMouseDown: (event) => {
38142
+ if (event.target === event.currentTarget && !pending) onCancel();
38143
+ }
38144
+ }, (0, react.createElement)("div", {
38145
+ className: "codingns-magpie-codex-reset-dialog",
38146
+ role: "dialog",
38147
+ "aria-modal": true,
38148
+ "aria-label": t("usage.resetDialogTitle")
38149
+ }, (0, react.createElement)("strong", void 0, t("usage.resetDialogTitle")), (0, react.createElement)("span", void 0, t("magpie.resetDialogAccount", { account: target.account })), (0, react.createElement)("span", { className: failed ? "is-error" : result === "reset" || result === "alreadyRedeemed" ? "is-success" : "" }, pending ? t("usage.resetDialogPending") : failed ? t("magpie.codexResetFailed") : outcomeMessage ?? t("magpie.resetDialogBody", { count: target.count })), (0, react.createElement)("div", { className: "codingns-magpie-codex-reset-actions" }, finished ? (0, react.createElement)("button", {
38150
+ type: "button",
38151
+ onClick: onClose
38152
+ }, t("usage.resetDialogClose")) : (0, react.createElement)("button", {
38153
+ type: "button",
38154
+ disabled: pending,
38155
+ onClick: onCancel
38156
+ }, t("usage.resetDialogCancel")), !finished && (0, react.createElement)("button", {
38157
+ type: "button",
38158
+ disabled: pending,
38159
+ onClick: onConfirm
38160
+ }, t("usage.resetDialogConfirm")))));
38161
+ }
37456
38162
  function QuotaHistoryCard({ history }) {
37457
38163
  return (0, react.createElement)("div", { className: "codingns-magpie-history-card" }, (0, react.createElement)("div", { className: "codingns-magpie-provider-inline" }, (0, react.createElement)(MagpieProviderLogo, {
37458
38164
  identities: [history.provider],
@@ -37763,6 +38469,13 @@ window.__ModuleLoader__.load({
37763
38469
  .codingns-magpie-quota-balance-label{flex:0 0 auto;color:var(--mp-muted);font-size:${require_theme.uiFontSize(13)}}
37764
38470
  .codingns-magpie-quota-balance-value{min-width:0;overflow:hidden;color:var(--mp-ink);font-size:${require_theme.uiFontSize(15)};font-weight:600;font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap}
37765
38471
  .codingns-magpie-quota-balance-value.is-money{font-size:${require_theme.uiFontSize(17)};letter-spacing:-.01em}
38472
+ .codingns-magpie-codex-controls{display:grid;gap:2px;margin:9px 0 10px;padding:8px 10px;border-top:.5px solid var(--mp-line);border-bottom:.5px solid var(--mp-line);background:color-mix(in srgb,var(--mp-layer) 45%,transparent)}
38473
+ .codingns-magpie-codex-reset-count{display:flex;align-items:center;justify-content:space-between;gap:8px;min-width:0;padding:2px 0 5px;color:var(--mp-muted);font-size:${require_theme.uiFontSize(12)}}.codingns-magpie-codex-reset-count strong{color:var(--mp-ink);font-size:${require_theme.uiFontSize(13)};font-variant-numeric:tabular-nums}
38474
+ .codingns-magpie-codex-policy{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;min-width:0;min-height:32px;padding:4px 0;border:0;background:transparent;color:var(--mp-muted);font:inherit;font-size:${require_theme.uiFontSize(12)};text-align:left;cursor:pointer}.codingns-magpie-codex-policy>span:first-child{min-width:0;line-height:1.4;overflow-wrap:anywhere}.codingns-magpie-codex-policy:disabled{opacity:.58;cursor:wait}.codingns-magpie-codex-policy:focus-visible,.codingns-magpie-codex-reset-button:focus-visible{outline:2px solid color-mix(in srgb,var(--mp-accent) 60%,transparent);outline-offset:2px}
38475
+ .codingns-magpie-codex-switch{display:grid;place-items:center;flex:0 0 34px;width:34px;height:20px;padding:2px;border-radius:999px;background:var(--mp-line-strong);transition:background .15s}.codingns-magpie-codex-switch>span{justify-self:start;width:16px;height:16px;border-radius:50%;background:var(--mp-surface);box-shadow:0 1px 2px rgba(0,0,0,.18);transition:transform .15s}.codingns-magpie-codex-switch.is-on{background:var(--mp-accent)}.codingns-magpie-codex-switch.is-on>span{transform:translateX(14px)}
38476
+ .codingns-magpie-codex-unavailable,.codingns-magpie-codex-error{padding:5px 0;color:var(--mp-caption);font-size:${require_theme.uiFontSize(12)}}.codingns-magpie-codex-error{color:var(--dsw-alias-state-error-primary,#c54b4b)}
38477
+ .codingns-magpie-codex-reset-button{justify-self:start;min-height:27px;margin-top:4px;padding:0 9px;border:.5px solid var(--mp-line-strong);border-radius:var(--dsw-radius-sm,6px);background:transparent;color:var(--mp-ink);font:inherit;font-size:${require_theme.uiFontSize(12)};cursor:pointer}.codingns-magpie-codex-reset-button:hover:not(:disabled){border-color:var(--mp-accent);color:var(--mp-accent)}.codingns-magpie-codex-reset-button:disabled{opacity:.45;cursor:not-allowed}
38478
+ .codingns-magpie-codex-reset-overlay{position:absolute;inset:0;z-index:30;display:flex;align-items:center;justify-content:center;overflow:auto;padding:20px;background:color-mix(in srgb,var(--mp-surface) 70%,rgba(0,0,0,.3));backdrop-filter:blur(2px)}.codingns-magpie-codex-reset-dialog{display:grid;gap:10px;width:min(420px,100%);padding:16px;border:.5px solid var(--mp-line-strong);border-radius:var(--mp-radius);background:var(--mp-surface);box-shadow:var(--dsw-elevation-prominent,0 16px 44px rgba(0,0,0,.2));font-size:${require_theme.uiFontSize(13)};line-height:1.5}.codingns-magpie-codex-reset-dialog>strong{font-size:${require_theme.uiFontSize(14)}}.codingns-magpie-codex-reset-dialog>span{color:var(--mp-muted);overflow-wrap:anywhere}.codingns-magpie-codex-reset-dialog>span.is-error{color:var(--dsw-alias-state-error-primary,#c54b4b)}.codingns-magpie-codex-reset-dialog>span.is-success{color:var(--dsw-alias-state-success-primary,#3f9d6b)}.codingns-magpie-codex-reset-actions{display:flex;justify-content:flex-end;gap:8px;padding-top:3px}.codingns-magpie-codex-reset-actions button{min-height:30px;padding:0 11px;border:.5px solid var(--mp-line-strong);border-radius:var(--dsw-radius-sm,6px);background:transparent;color:var(--mp-ink);font:inherit;font-size:${require_theme.uiFontSize(12)};cursor:pointer}.codingns-magpie-codex-reset-actions button:last-child{border-color:var(--mp-accent);background:var(--mp-accent);color:var(--dsw-alias-label-primary-foreground,#fff)}.codingns-magpie-codex-reset-actions button:disabled{opacity:.5;cursor:wait}
37766
38479
  .codingns-magpie-quota-window{padding:11px 0;border-top:.5px solid var(--mp-line)}.codingns-magpie-quota-window-head,.codingns-magpie-quota-foot,.codingns-magpie-history-line-head{display:flex;align-items:center;justify-content:space-between;gap:9px}.codingns-magpie-quota-window-head{font-size:${require_theme.uiFontSize(13)};font-weight:500}.codingns-magpie-quota-window-head span{color:var(--mp-muted);font-variant-numeric:tabular-nums}.quota-track{height:6px}.quota-track span{background:linear-gradient(90deg,var(--mp-accent),color-mix(in srgb,var(--mp-accent) 55%,transparent))}.codingns-magpie-quota-foot{color:var(--mp-caption);font-size:${require_theme.uiFontSize(12)}}
37767
38480
  .codingns-magpie-daily-chart{display:grid;grid-template-columns:1fr auto;align-items:center;gap:9px;margin-top:4px;padding-top:10px;border-top:.5px solid var(--mp-line);color:var(--mp-muted);font-size:${require_theme.uiFontSize(12)}}.codingns-magpie-sparkline{width:100%;height:30px;overflow:visible}.codingns-magpie-sparkline polyline{stroke:var(--mp-accent);stroke-width:2;vector-effect:non-scaling-stroke}
37768
38481
  .codingns-magpie-history-panel{padding:17px;margin-top:14px}.codingns-magpie-history-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px}.codingns-magpie-history-card{padding:12px;border:1px solid var(--mp-line);border-radius:9px;background:var(--mp-card)}.codingns-magpie-history-card>strong,.codingns-magpie-history-card>small{display:block}.codingns-magpie-history-card>strong{font-size:${require_theme.uiFontSize(13)};font-weight:600}.codingns-magpie-history-card>small{margin-top:3px;color:var(--mp-caption);font-size:${require_theme.uiFontSize(12)}}.codingns-magpie-history-line{margin-top:10px}.codingns-magpie-history-line-head{font-size:${require_theme.uiFontSize(12)}}.codingns-magpie-history-line-head span:last-child{font-variant-numeric:tabular-nums;font-weight:650}
@@ -38528,7 +39241,7 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
38528
39241
  const fiberKey = Object.getOwnPropertyNames(row).find((key) => key.startsWith("__reactFiber$"));
38529
39242
  if (fiberKey === void 0) return void 0;
38530
39243
  let current = carrier[fiberKey];
38531
- for (let depth = 0; depth < 32 && isRecord$7(current); depth += 1) {
39244
+ for (let depth = 0; depth < 32 && isRecord$8(current); depth += 1) {
38532
39245
  const fiber = current;
38533
39246
  const sessionId = sessionIdFromProps(fiber.memoizedProps) ?? sessionIdFromProps(fiber.pendingProps);
38534
39247
  if (sessionId !== void 0) return sessionId;
@@ -38536,15 +39249,15 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
38536
39249
  }
38537
39250
  }
38538
39251
  function sessionIdFromProps(value) {
38539
- if (!isRecord$7(value)) return void 0;
39252
+ if (!isRecord$8(value)) return void 0;
38540
39253
  for (const key of ["node", "result"]) {
38541
39254
  const candidate = value[key];
38542
- if (!isRecord$7(candidate) || typeof candidate.id !== "string") continue;
39255
+ if (!isRecord$8(candidate) || typeof candidate.id !== "string") continue;
38543
39256
  const sessionId = candidate.id.trim();
38544
39257
  if (sessionId !== "") return sessionId;
38545
39258
  }
38546
39259
  }
38547
- function isRecord$7(value) {
39260
+ function isRecord$8(value) {
38548
39261
  return typeof value === "object" && value !== null && !Array.isArray(value);
38549
39262
  }
38550
39263
  //#endregion
@@ -38907,7 +39620,7 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
38907
39620
  }
38908
39621
  function findWorkspaceIdInFiber(value) {
38909
39622
  let current = value;
38910
- for (let depth = 0; depth < 24 && isRecord$6(current); depth += 1) {
39623
+ for (let depth = 0; depth < 24 && isRecord$7(current); depth += 1) {
38911
39624
  for (const props of [current.memoizedProps, current.pendingProps]) {
38912
39625
  const id = findWorkspaceIdInProps(props);
38913
39626
  if (id !== void 0) return id;
@@ -38916,7 +39629,7 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
38916
39629
  }
38917
39630
  }
38918
39631
  function findWorkspaceIdInProps(value) {
38919
- if (!isRecord$6(value)) return void 0;
39632
+ if (!isRecord$7(value)) return void 0;
38920
39633
  for (const key of ["workspaceId", "workspaceID"]) {
38921
39634
  const id = readString$1(value[key]);
38922
39635
  if (id !== void 0) return id;
@@ -39368,7 +40081,7 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
39368
40081
  dom.querySelector(`[${WORKSPACE_SESSION_ARCHIVE_MODAL_ATTRIBUTE}]`)?.remove();
39369
40082
  }
39370
40083
  function normalizeRemote(value) {
39371
- if (!isRecord$6(value)) return {};
40084
+ if (!isRecord$7(value)) return {};
39372
40085
  const workspace = asRecord$7(readRemoteProperty(value, "workspace")) ?? asRecord$7(readRemoteProperty(value, "workspaceController"));
39373
40086
  const session = asRecord$7(readRemoteProperty(value, "session")) ?? asRecord$7(readRemoteProperty(value, "sessionController"));
39374
40087
  return {
@@ -39394,7 +40107,7 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
39394
40107
  }) : [];
39395
40108
  }
39396
40109
  function asRecord$7(value) {
39397
- return isRecord$6(value) ? value : void 0;
40110
+ return isRecord$7(value) ? value : void 0;
39398
40111
  }
39399
40112
  /** DSH Remote 的直接调用返回 RemoteResult;测试桩和旧宿主可能直接返回 value。 */
39400
40113
  function unwrapRemoteValue$1(value) {
@@ -39402,7 +40115,7 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
39402
40115
  if (record === void 0 || typeof record.ok !== "boolean") return value;
39403
40116
  return record.ok ? record.value : void 0;
39404
40117
  }
39405
- function isRecord$6(value) {
40118
+ function isRecord$7(value) {
39406
40119
  return typeof value === "object" && value !== null && !Array.isArray(value);
39407
40120
  }
39408
40121
  //#endregion
@@ -40510,6 +41223,8 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
40510
41223
  const [resetPending, setResetPending] = (0, react.useState)(false);
40511
41224
  const [resetResult, setResetResult] = (0, react.useState)(null);
40512
41225
  const [resetError, setResetError] = (0, react.useState)(null);
41226
+ const [resetTargetUser, setResetTargetUser] = (0, react.useState)(null);
41227
+ const [magpiePolicyPending, setMagpiePolicyPending] = (0, react.useState)(null);
40513
41228
  const sessionRef = (0, react.useRef)(void 0);
40514
41229
  const routeKeyRef = (0, react.useRef)(null);
40515
41230
  const modelSelectionRevision = props.useSession?.((value) => JSON.stringify([value.modelSelection, value.running]));
@@ -40724,20 +41439,22 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
40724
41439
  setResetOpen(false);
40725
41440
  setResetResult(null);
40726
41441
  setResetError(null);
41442
+ setResetTargetUser(null);
40727
41443
  };
40728
41444
  const confirmReset = () => {
40729
- if (resetPending || adapterId === null) return;
41445
+ if (resetPending) return;
40730
41446
  setResetPending(true);
40731
41447
  setResetError(null);
40732
41448
  (async () => {
40733
41449
  try {
40734
- const result = await require_magpie_model_tag.callCliRpc(props.rpc, "subscription/reset", {
41450
+ const result = resetTargetUser === null ? adapterId === null ? null : await require_magpie_model_tag.callCliRpc(props.rpc, "subscription/reset", {
40735
41451
  sessionId: props.sessionId?.trim() ?? "",
40736
41452
  adapterId,
40737
41453
  ...providerId === null ? {} : { providerId }
40738
- });
41454
+ }) : await require_magpie_model_tag.callCliRpc(props.rpc, "magpie/usage/reset", { user: resetTargetUser });
41455
+ if (result === null) throw new Error(t("usage.resetFailed"));
40739
41456
  setResetResult(result.outcome);
40740
- invalidateSubscriptionUsageCache(subscriptionUsageCache, adapterId, providerId);
41457
+ if (adapterId !== null) invalidateSubscriptionUsageCache(subscriptionUsageCache, adapterId, providerId);
40741
41458
  await refreshRef.current?.();
40742
41459
  } catch (error) {
40743
41460
  setResetError(error instanceof Error ? error.message : "");
@@ -40746,6 +41463,23 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
40746
41463
  }
40747
41464
  })();
40748
41465
  };
41466
+ const requestMagpieReset = (user) => {
41467
+ setOpen(false);
41468
+ setResetResult(null);
41469
+ setResetError(null);
41470
+ setResetTargetUser(user);
41471
+ setResetOpen(true);
41472
+ };
41473
+ const updateMagpiePolicy = (user, policy, enabled) => {
41474
+ const key = `${user}:${policy}`;
41475
+ if (magpiePolicyPending !== null) return;
41476
+ setMagpiePolicyPending(key);
41477
+ require_magpie_model_tag.callCliRpc(props.rpc, "magpie/usage/account/update", {
41478
+ user,
41479
+ policy,
41480
+ enabled
41481
+ }).then(() => refreshRef.current?.()).catch(() => void 0).finally(() => setMagpiePolicyPending(null));
41482
+ };
40749
41483
  useDismissOnOutsidePointer(rootRef, open, () => setOpen(false), { portal: magpieRequestsDialogRef });
40750
41484
  useDismissOnOutsidePointer(resetDialogRef, resetOpen && !resetPending, closeResetDialog);
40751
41485
  if (!eligible || snapshot === null) return null;
@@ -40753,6 +41487,7 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
40753
41487
  const magpie = snapshot.magpie;
40754
41488
  const readout = resolveMagpieTriggerReadout(magpie);
40755
41489
  const label = t("usage.magpieUsageLabel");
41490
+ const resetCount = resetTargetUser === null ? 0 : magpie.quotas.find((quota) => quota.provider === "codex" && quota.user?.toLocaleLowerCase() === resetTargetUser.toLocaleLowerCase())?.resets?.count ?? 0;
40756
41491
  return (0, react.createElement)("div", {
40757
41492
  ref: rootRef,
40758
41493
  style: subscriptionRootStyle
@@ -40776,6 +41511,9 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
40776
41511
  rpc: props.rpc,
40777
41512
  usage: magpie,
40778
41513
  t,
41514
+ onPolicyChange: updateMagpiePolicy,
41515
+ policyPending: magpiePolicyPending,
41516
+ onResetRequest: requestMagpieReset,
40779
41517
  tab: magpieTab,
40780
41518
  onTabChange: setMagpieTab,
40781
41519
  expandedGroups: expandedQuotaGroups,
@@ -40783,6 +41521,16 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
40783
41521
  placement: magpiePlacement,
40784
41522
  popoverRef: magpiePopoverRef,
40785
41523
  requestsDialogRef: magpieRequestsDialogRef
41524
+ }), resetOpen && resetTargetUser !== null && (0, react.createElement)(ResetConfirmDialog, {
41525
+ count: resetCount,
41526
+ pending: resetPending,
41527
+ result: resetResult,
41528
+ error: resetError,
41529
+ t,
41530
+ onCancel: closeResetDialog,
41531
+ onConfirm: confirmReset,
41532
+ onClose: closeResetDialog,
41533
+ dialogRef: resetDialogRef
40786
41534
  }));
40787
41535
  }
40788
41536
  const usage = snapshot.providerUsage;
@@ -40827,6 +41575,7 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
40827
41575
  setOpen(false);
40828
41576
  setResetResult(null);
40829
41577
  setResetError(null);
41578
+ setResetTargetUser(null);
40830
41579
  setResetOpen(true);
40831
41580
  }
40832
41581
  } : null;
@@ -40914,7 +41663,7 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
40914
41663
  dialogRef: resetDialogRef
40915
41664
  }));
40916
41665
  }
40917
- function MagpieUsagePopover({ rpc, usage, t, tab = "usage", onTabChange = () => {}, expandedGroups = [], onToggleGroup = () => {}, placement = {
41666
+ function MagpieUsagePopover({ rpc, usage, t, onPolicyChange = () => {}, policyPending = null, onResetRequest = () => {}, tab = "usage", onTabChange = () => {}, expandedGroups = [], onToggleGroup = () => {}, placement = {
40918
41667
  left: 0,
40919
41668
  width: 400
40920
41669
  }, popoverRef, requestsDialogRef }) {
@@ -40985,7 +41734,10 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
40985
41734
  usage,
40986
41735
  aggregateRows,
40987
41736
  sessionRows,
40988
- t
41737
+ t,
41738
+ onPolicyChange,
41739
+ policyPending,
41740
+ onResetRequest
40989
41741
  }) : MagpieRoutePanel({
40990
41742
  rpc,
40991
41743
  usage,
@@ -40996,11 +41748,56 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
40996
41748
  ...requestsDialogRef === void 0 ? {} : { requestsDialogRef }
40997
41749
  })));
40998
41750
  }
40999
- function MagpieUsagePanel({ usage, aggregateRows, sessionRows, t }) {
41751
+ function MagpieUsagePanel({ usage, aggregateRows, sessionRows, t, onPolicyChange = () => {}, policyPending = null, onResetRequest = () => {} }) {
41752
+ const policyQuotas = [...usage.quotas ?? [], ...usage.providerAccounts ?? []].filter((quota) => quota.provider === "codex" && quota.user !== null && (quota.autoReset !== void 0 || quota.useCredits !== void 0 || quota.resets !== void 0)).filter((quota, index, rows) => rows.findIndex((item) => item.user?.toLocaleLowerCase() === quota.user?.toLocaleLowerCase()) === index);
41000
41753
  return (0, react.createElement)("div", void 0, (0, react.createElement)("section", { style: magpieUsageSectionStyle }, (0, react.createElement)("strong", { style: magpieSectionTitleStyle }, t("usage.magpieAggregateTitle")), usage.usageStatus === "available" ? createMagpieRows(aggregateRows) : (0, react.createElement)("div", { style: magpieMutedTextStyle }, t("usage.magpieUsageUnavailable"))), (0, react.createElement)("section", { style: magpieUsageSectionStyle }, (0, react.createElement)("strong", { style: magpieSectionTitleStyle }, t(usage.sessionPeriod === "today" ? "usage.magpieSessionTodayTitle" : "usage.magpieSessionTitle")), usage.sessionId !== null && (0, react.createElement)("div", { style: magpieSessionIdentityStyle }, (0, react.createElement)("div", { style: magpieSessionNameRowStyle }, (0, react.createElement)("span", { style: { flex: "none" } }, t("usage.magpieAgent", { agent: usage.agent })), usage.sessionName !== void 0 && (0, react.createElement)("strong", {
41001
41754
  style: magpieSessionNameStyle,
41002
41755
  title: usage.sessionName
41003
- }, usage.sessionName)), (0, react.createElement)("code", { style: magpieSessionIdStyle }, usage.sessionId)), usage.session === null ? (0, react.createElement)("div", { style: magpieMutedTextStyle }, usage.sessionId === null ? t("usage.magpieSessionIdUnavailable") : t("usage.magpieSessionNoUsage")) : createMagpieRows(sessionRows)));
41756
+ }, usage.sessionName)), (0, react.createElement)("code", { style: magpieSessionIdStyle }, usage.sessionId)), usage.session === null ? (0, react.createElement)("div", { style: magpieMutedTextStyle }, usage.sessionId === null ? t("usage.magpieSessionIdUnavailable") : t("usage.magpieSessionNoUsage")) : createMagpieRows(sessionRows)), policyQuotas.length > 0 && (0, react.createElement)("section", { style: magpieUsageSectionStyle }, (0, react.createElement)("strong", { style: magpieSectionTitleStyle }, t("usage.magpieAccountPolicies")), ...policyQuotas.map((quota, index) => createMagpieAccountPolicyCard(quota, t, index, onPolicyChange, policyPending, onResetRequest))));
41757
+ }
41758
+ function createMagpieAccountPolicyCard(quota, t, index, onPolicyChange, policyPending, onResetRequest) {
41759
+ const user = quota.user ?? "";
41760
+ const resetCount = quota.resets?.count;
41761
+ return (0, react.createElement)("div", {
41762
+ key: `codex-policy:${user}:${index}`,
41763
+ style: magpieAccountPolicyCardStyle
41764
+ }, (0, react.createElement)("div", { style: magpieAccountPolicyHeadingStyle }, (0, react.createElement)("strong", { title: user }, user), resetCount !== null && resetCount !== void 0 && (0, react.createElement)("span", { style: magpieHeaderMutedStyle }, t("usage.magpieResetCount", { count: resetCount }))), quota.autoReset !== void 0 && (0, react.createElement)(MagpiePolicySwitch, {
41765
+ label: t("usage.magpieResetWhenWeek"),
41766
+ checked: quota.autoReset.week,
41767
+ pending: policyPending === `${user}:week`,
41768
+ onChange: (enabled) => onPolicyChange(user, "week", enabled)
41769
+ }), quota.autoReset !== void 0 && (0, react.createElement)(MagpiePolicySwitch, {
41770
+ label: t("usage.magpieResetBeforeExpiry"),
41771
+ checked: quota.autoReset.expiring,
41772
+ pending: policyPending === `${user}:expiring`,
41773
+ onChange: (enabled) => onPolicyChange(user, "expiring", enabled)
41774
+ }), quota.useCredits !== void 0 && (0, react.createElement)(MagpiePolicySwitch, {
41775
+ label: t("usage.magpieUseCredits"),
41776
+ checked: quota.useCredits,
41777
+ pending: policyPending === `${user}:credits`,
41778
+ onChange: (enabled) => onPolicyChange(user, "credits", enabled)
41779
+ }), resetCount !== null && resetCount !== void 0 && (0, react.createElement)("button", {
41780
+ type: "button",
41781
+ disabled: resetCount <= 0,
41782
+ onClick: () => onResetRequest(user),
41783
+ style: require_theme.dshSettingsButtonStyle
41784
+ }, t("usage.magpieUseReset")));
41785
+ }
41786
+ function MagpiePolicySwitch({ label, checked, pending, onChange }) {
41787
+ return (0, react.createElement)("button", {
41788
+ type: "button",
41789
+ role: "switch",
41790
+ "aria-checked": checked,
41791
+ disabled: pending,
41792
+ onClick: () => onChange(!checked),
41793
+ style: magpiePolicySwitchStyle
41794
+ }, (0, react.createElement)("span", void 0, label), (0, react.createElement)("span", { style: {
41795
+ ...magpiePolicySwitchTrackStyle,
41796
+ ...checked ? magpiePolicySwitchOnStyle : {}
41797
+ } }, (0, react.createElement)("span", { style: {
41798
+ ...magpiePolicySwitchThumbStyle,
41799
+ ...checked ? magpiePolicySwitchThumbOnStyle : {}
41800
+ } })));
41004
41801
  }
41005
41802
  function magpieQuotaUnavailableMessage(usage, quotas, t) {
41006
41803
  return usage.quotaStatus === "unavailable" ? t("usage.magpieQuotaUnavailable") : usage.quotaStatus === "provider-unavailable" ? t("usage.magpieQuotaProviderUnavailable") : quotas.length === 0 ? t("usage.magpieQuotaNoData") : null;
@@ -41235,7 +42032,7 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
41235
42032
  }, signal);
41236
42033
  if (!result.ok) throw new Error(result.error.message);
41237
42034
  const value = result.value;
41238
- return isRecord$5(value) && Array.isArray(value.items) ? value : null;
42035
+ return isRecord$6(value) && Array.isArray(value.items) ? value : null;
41239
42036
  }
41240
42037
  /**
41241
42038
  * 请求模态框:外壳(遮罩、标题、关闭)由弹层提供,明细直接复用管理页的请求用量视图。
@@ -41324,17 +42121,17 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
41324
42121
  }
41325
42122
  function normalizeSubscriptionSnapshot(value) {
41326
42123
  if (value === null || value === void 0) return null;
41327
- if (!isRecord$5(value)) return null;
42124
+ if (!isRecord$6(value)) return null;
41328
42125
  if ("providerUsage" in value || "magpie" in value) return {
41329
- providerUsage: isRecord$5(value.providerUsage) ? value.providerUsage : null,
41330
- magpie: isRecord$5(value.magpie) ? value.magpie : null
42126
+ providerUsage: isRecord$6(value.providerUsage) ? value.providerUsage : null,
42127
+ magpie: isRecord$6(value.magpie) ? value.magpie : null
41331
42128
  };
41332
42129
  return {
41333
42130
  providerUsage: value,
41334
42131
  magpie: null
41335
42132
  };
41336
42133
  }
41337
- function isRecord$5(value) {
42134
+ function isRecord$6(value) {
41338
42135
  return typeof value === "object" && value !== null && !Array.isArray(value);
41339
42136
  }
41340
42137
  function SubscriptionPopover({ usage, providerName, t, nowMs, reset }) {
@@ -42162,6 +42959,60 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
42162
42959
  marginTop: 7,
42163
42960
  borderTop: `1px solid ${require_theme.dshThemeColor.border}`
42164
42961
  };
42962
+ const magpieAccountPolicyCardStyle = {
42963
+ display: "grid",
42964
+ gap: 5,
42965
+ marginTop: 7,
42966
+ padding: "8px 9px",
42967
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
42968
+ borderRadius: 9,
42969
+ background: require_theme.dshThemeColor.surfaceSubtle
42970
+ };
42971
+ const magpieAccountPolicyHeadingStyle = {
42972
+ display: "flex",
42973
+ justifyContent: "space-between",
42974
+ alignItems: "baseline",
42975
+ gap: 8,
42976
+ minWidth: 0,
42977
+ fontSize: require_theme.uiFontSize(12)
42978
+ };
42979
+ const magpiePolicySwitchStyle = {
42980
+ display: "flex",
42981
+ alignItems: "center",
42982
+ justifyContent: "space-between",
42983
+ gap: 8,
42984
+ width: "100%",
42985
+ padding: "2px 0",
42986
+ border: 0,
42987
+ background: "transparent",
42988
+ color: require_theme.dshThemeColor.labelSecondary,
42989
+ fontFamily: "inherit",
42990
+ fontSize: require_theme.uiFontSize(11),
42991
+ lineHeight: "15px",
42992
+ textAlign: "left",
42993
+ cursor: "pointer"
42994
+ };
42995
+ const magpiePolicySwitchTrackStyle = {
42996
+ display: "inline-flex",
42997
+ alignItems: "center",
42998
+ flex: "none",
42999
+ width: 28,
43000
+ height: 16,
43001
+ padding: 2,
43002
+ boxSizing: "border-box",
43003
+ borderRadius: 999,
43004
+ background: require_theme.dshThemeColor.border,
43005
+ transition: "background .15s ease"
43006
+ };
43007
+ const magpiePolicySwitchOnStyle = { background: require_theme.dshThemeColor.accent };
43008
+ const magpiePolicySwitchThumbStyle = {
43009
+ width: 12,
43010
+ height: 12,
43011
+ borderRadius: "50%",
43012
+ background: require_theme.dshThemeColor.menuBackground,
43013
+ transition: "transform .15s ease"
43014
+ };
43015
+ const magpiePolicySwitchThumbOnStyle = { transform: "translateX(12px)" };
42165
43016
  const magpieStatsGridStyle = {
42166
43017
  display: "grid",
42167
43018
  gridTemplateColumns: "repeat(3, minmax(0, 1fr))",
@@ -45779,7 +46630,7 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
45779
46630
  //#endregion
45780
46631
  //#region src/client/features/workspace-session-enhancement.ts
45781
46632
  /** descriptor 的 label/description 只是词典缺失时的兜底,取内置中文词典。 */
45782
- const fallbackT$2 = require_transport.resolveCodingNsTranslator();
46633
+ const fallbackT$3 = require_transport.resolveCodingNsTranslator();
45783
46634
  /** DSH 0.1.6 原生会话行增强:Logo 与归档会话入口共用同一生命周期。 */
45784
46635
  const workspaceSessionEnhancementFeature = {
45785
46636
  descriptor: {
@@ -45789,8 +46640,8 @@ html[data-codingns-mobile-sidebar="on"] .codingns-magpie-page{padding-left:0;--m
45789
46640
  dependencies: ["cliAdapters"],
45790
46641
  runtime: "client",
45791
46642
  ui: {
45792
- label: fallbackT$2("feature.workspaceSession.label"),
45793
- description: fallbackT$2("feature.workspaceSession.description"),
46643
+ label: fallbackT$3("feature.workspaceSession.label"),
46644
+ description: fallbackT$3("feature.workspaceSession.description"),
45794
46645
  labelKey: "feature.workspaceSession.label",
45795
46646
  descriptionKey: "feature.workspaceSession.description",
45796
46647
  order: 35,
@@ -47673,7 +48524,7 @@ ${SCOPE} article>div:last-child>button { float:none!important; order:1; min-heig
47673
48524
  //#endregion
47674
48525
  //#region src/client/features/mobile-access.ts
47675
48526
  /** descriptor 的 label/description 只是词典缺失时的兜底,取内置中文词典。 */
47676
- const fallbackT$1 = require_transport.resolveCodingNsTranslator();
48527
+ const fallbackT$2 = require_transport.resolveCodingNsTranslator();
47677
48528
  /**
47678
48529
  * 移动端访问增强。
47679
48530
  *
@@ -47697,8 +48548,8 @@ ${SCOPE} article>div:last-child>button { float:none!important; order:1; min-heig
47697
48548
  fallback: "disable"
47698
48549
  }],
47699
48550
  ui: {
47700
- label: fallbackT$1("feature.mobileAccess.label"),
47701
- description: fallbackT$1("feature.mobileAccess.description"),
48551
+ label: fallbackT$2("feature.mobileAccess.label"),
48552
+ description: fallbackT$2("feature.mobileAccess.description"),
47702
48553
  labelKey: "feature.mobileAccess.label",
47703
48554
  descriptionKey: "feature.mobileAccess.description",
47704
48555
  order: 36,
@@ -53955,7 +54806,7 @@ ${SCOPE} article>div:last-child>button { float:none!important; order:1; min-heig
53955
54806
  //#endregion
53956
54807
  //#region src/client/features/file-management.ts
53957
54808
  /** descriptor 的 label/description 只是词典缺失时的兜底,取内置中文词典。 */
53958
- const fallbackT = require_transport.resolveCodingNsTranslator();
54809
+ const fallbackT$1 = require_transport.resolveCodingNsTranslator();
53959
54810
  /** 文件管理增强:只在模块启用期间挂载右键菜单和文本编辑器。 */
53960
54811
  const fileManagementFeature = {
53961
54812
  descriptor: {
@@ -53965,8 +54816,8 @@ ${SCOPE} article>div:last-child>button { float:none!important; order:1; min-heig
53965
54816
  dependencies: [],
53966
54817
  runtime: "client",
53967
54818
  ui: {
53968
- label: fallbackT("feature.fileManagement.label"),
53969
- description: fallbackT("feature.fileManagement.description"),
54819
+ label: fallbackT$1("feature.fileManagement.label"),
54820
+ description: fallbackT$1("feature.fileManagement.description"),
53970
54821
  labelKey: "feature.fileManagement.label",
53971
54822
  descriptionKey: "feature.fileManagement.description",
53972
54823
  order: 40,
@@ -68927,7 +69778,7 @@ button[${ACCOUNT_ATTRIBUTE}]:focus-visible{outline:2px solid ${require_theme.dsh
68927
69778
  const store = require_native_workspace_store.readNativeWorkspaceListStore(input.uiContext);
68928
69779
  if (store === void 0) return () => void 0;
68929
69780
  const service = require_native_workspace_store.readNativeService(input.uiContext, "workspaces");
68930
- const serviceRecord = isRecord$4(service) ? service : void 0;
69781
+ const serviceRecord = isRecord$5(service) ? service : void 0;
68931
69782
  const sessionStoreValue = require_native_workspace_store.readNativeService(input.uiContext, "sessions", "list");
68932
69783
  const sessionStore = isSnapshotStore(sessionStoreValue) ? sessionStoreValue : void 0;
68933
69784
  const ownsGetSnapshot = Object.hasOwn(store, "getSnapshot");
@@ -68990,7 +69841,7 @@ button[${ACCOUNT_ATTRIBUTE}]:focus-visible{outline:2px solid ${require_theme.dsh
68990
69841
  async function refreshPeerHostNativeSessions(uiContext) {
68991
69842
  if (uiContext === void 0) return;
68992
69843
  const sessions = require_native_workspace_store.readNativeService(uiContext, "sessions");
68993
- const refresh = isRecord$4(sessions) ? sessions.refresh : void 0;
69844
+ const refresh = isRecord$5(sessions) ? sessions.refresh : void 0;
68994
69845
  if (typeof refresh !== "function") return;
68995
69846
  const owner = uiContext;
68996
69847
  const state = refreshStates.get(owner);
@@ -69110,7 +69961,7 @@ button[${ACCOUNT_ATTRIBUTE}]:focus-visible{outline:2px solid ${require_theme.dsh
69110
69961
  const byId = state?.byId;
69111
69962
  const workspaceState = asRecord$4(workspaceSnapshot);
69112
69963
  const items = Array.isArray(workspaceState?.items) ? workspaceState.items : [];
69113
- if (state === null || !isRecord$4(byId) || items.length === 0) return snapshot;
69964
+ if (state === null || !isRecord$5(byId) || items.length === 0) return snapshot;
69114
69965
  let changed = false;
69115
69966
  const nextById = { ...byId };
69116
69967
  for (const [id, value] of Object.entries(byId)) {
@@ -69260,13 +70111,13 @@ button[${ACCOUNT_ATTRIBUTE}]:focus-visible{outline:2px solid ${require_theme.dsh
69260
70111
  return merged.length === original.length && merged.every((id, index) => id === original[index]) ? void 0 : merged;
69261
70112
  }
69262
70113
  function asRecord$4(value) {
69263
- return isRecord$4(value) ? value : null;
70114
+ return isRecord$5(value) ? value : null;
69264
70115
  }
69265
- function isRecord$4(value) {
70116
+ function isRecord$5(value) {
69266
70117
  return typeof value === "object" && value !== null && !Array.isArray(value);
69267
70118
  }
69268
70119
  function isSnapshotStore(value) {
69269
- return isRecord$4(value) && typeof value.getSnapshot === "function" && typeof value.subscribe === "function";
70120
+ return isRecord$5(value) && typeof value.getSnapshot === "function" && typeof value.subscribe === "function";
69270
70121
  }
69271
70122
  //#endregion
69272
70123
  //#region src/client/peer-host-workspace-tag.ts
@@ -69437,7 +70288,7 @@ button[${ACCOUNT_ATTRIBUTE}]:focus-visible{outline:2px solid ${require_theme.dsh
69437
70288
  const fiberKey = Object.getOwnPropertyNames(row).find((key) => key.startsWith("__reactFiber$"));
69438
70289
  if (fiberKey === void 0) return void 0;
69439
70290
  let current = row[fiberKey];
69440
- for (let depth = 0; depth < 24 && isRecord$3(current); depth += 1) {
70291
+ for (let depth = 0; depth < 24 && isRecord$4(current); depth += 1) {
69441
70292
  for (const props of [current.memoizedProps, current.pendingProps]) {
69442
70293
  const found = readWorkspaceIdFromProps(props);
69443
70294
  if (found !== void 0) return found;
@@ -69446,7 +70297,7 @@ button[${ACCOUNT_ATTRIBUTE}]:focus-visible{outline:2px solid ${require_theme.dsh
69446
70297
  }
69447
70298
  }
69448
70299
  function readWorkspaceIdFromProps(value) {
69449
- if (!isRecord$3(value)) return void 0;
70300
+ if (!isRecord$4(value)) return void 0;
69450
70301
  for (const key of ["workspaceId", "workspaceID"]) {
69451
70302
  const candidate = value[key];
69452
70303
  if (typeof candidate === "string" && candidate.trim() !== "") return candidate.trim();
@@ -69460,7 +70311,7 @@ button[${ACCOUNT_ATTRIBUTE}]:focus-visible{outline:2px solid ${require_theme.dsh
69460
70311
  "data"
69461
70312
  ]) {
69462
70313
  const nested = value[key];
69463
- if (!isRecord$3(nested)) continue;
70314
+ if (!isRecord$4(nested)) continue;
69464
70315
  const candidate = nested.workspaceId ?? nested.id;
69465
70316
  if (typeof candidate === "string" && candidate.trim() !== "") return candidate.trim();
69466
70317
  }
@@ -69496,7 +70347,7 @@ button[${ACCOUNT_ATTRIBUTE}]:focus-visible{outline:2px solid ${require_theme.dsh
69496
70347
  });
69497
70348
  host.append(element);
69498
70349
  }
69499
- function isRecord$3(value) {
70350
+ function isRecord$4(value) {
69500
70351
  return typeof value === "object" && value !== null && !Array.isArray(value);
69501
70352
  }
69502
70353
  //#endregion
@@ -72943,11 +73794,11 @@ button[${ACCOUNT_ATTRIBUTE}]:focus-visible{outline:2px solid ${require_theme.dsh
72943
73794
  if (this.leaseRequest === leaseRequest) this.leaseRequest = void 0;
72944
73795
  });
72945
73796
  if (!lease.ok) throw new Error(lease.error.message);
72946
- if (isRecord$2(lease.value) && lease.value.unavailable === true) throw new Error(typeof lease.value.message === "string" ? lease.value.message : "本地 Sherpa 语音运行时不可用");
73797
+ if (isRecord$3(lease.value) && lease.value.unavailable === true) throw new Error(typeof lease.value.message === "string" ? lease.value.message : "本地 Sherpa 语音运行时不可用");
72947
73798
  if (abort.signal.aborted) throw new Error("语音启动已取消");
72948
73799
  this.hostLeaseActive = true;
72949
- this.epoch = readEpoch(isRecord$2(lease.value) ? lease.value : {}, this.epoch + 1);
72950
- if (isRecord$2(lease.value) && lease.value.diagnosticsEnabled === true) {
73800
+ this.epoch = readEpoch(isRecord$3(lease.value) ? lease.value : {}, this.epoch + 1);
73801
+ if (isRecord$3(lease.value) && lease.value.diagnosticsEnabled === true) {
72951
73802
  this.diagnostics = new ClientVoiceDiagnostics(this.services, {
72952
73803
  ownerId: this.configuredOwner,
72953
73804
  callId
@@ -73056,7 +73907,7 @@ button[${ACCOUNT_ATTRIBUTE}]:focus-visible{outline:2px solid ${require_theme.dsh
73056
73907
  }
73057
73908
  this.capture = void 0;
73058
73909
  const pendingLease = await leaseRequest?.catch(() => void 0);
73059
- if (pendingLease?.ok && !(isRecord$2(pendingLease.value) && pendingLease.value.unavailable === true)) this.hostLeaseActive = true;
73910
+ if (pendingLease?.ok && !(isRecord$3(pendingLease.value) && pendingLease.value.unavailable === true)) this.hostLeaseActive = true;
73060
73911
  if (this.hostLeaseActive) {
73061
73912
  this.hostLeaseActive = false;
73062
73913
  await callCodingNsRpcResult(this.services.rpc, "assistant/voice/stop", { ownerId: this.configuredOwner }).catch(() => void 0);
@@ -73082,7 +73933,7 @@ button[${ACCOUNT_ATTRIBUTE}]:focus-visible{outline:2px solid ${require_theme.dsh
73082
73933
  const result = await callCodingNsRpcResult(this.services.rpc, "assistant/voice/interrupt", { ownerId: this.configuredOwner });
73083
73934
  if (!result.ok) throw new Error(result.error.message);
73084
73935
  if (!this.active) return;
73085
- this.epoch = readEpoch(isRecord$2(result.value) ? result.value : {}, this.epoch + 1);
73936
+ this.epoch = readEpoch(isRecord$3(result.value) ? result.value : {}, this.epoch + 1);
73086
73937
  this.emit({
73087
73938
  type: "barge-in",
73088
73939
  epoch: this.epoch
@@ -73319,12 +74170,12 @@ button[${ACCOUNT_ATTRIBUTE}]:focus-visible{outline:2px solid ${require_theme.dsh
73319
74170
  while (isCurrent()) {
73320
74171
  if (!result.ok) throw new Error(result.error.message);
73321
74172
  const pushed = reply.updates.take();
73322
- if (pushed !== void 0 && (!isRecord$2(result.value) || result.value.state === "running" || pushed.state !== "running")) result = {
74173
+ if (pushed !== void 0 && (!isRecord$3(result.value) || result.value.state === "running" || pushed.state !== "running")) result = {
73323
74174
  ok: true,
73324
74175
  value: pushed
73325
74176
  };
73326
74177
  const run = result.value;
73327
- if (!isRecord$2(run) || typeof run.text !== "string" || ![
74178
+ if (!isRecord$3(run) || typeof run.text !== "string" || ![
73328
74179
  "running",
73329
74180
  "completed",
73330
74181
  "failed",
@@ -73462,11 +74313,11 @@ button[${ACCOUNT_ATTRIBUTE}]:focus-visible{outline:2px solid ${require_theme.dsh
73462
74313
  function readEpoch(value, fallback) {
73463
74314
  return typeof value.epoch === "number" && Number.isInteger(value.epoch) ? value.epoch : fallback;
73464
74315
  }
73465
- function isRecord$2(value) {
74316
+ function isRecord$3(value) {
73466
74317
  return typeof value === "object" && value !== null && !Array.isArray(value);
73467
74318
  }
73468
74319
  function isVoiceChatRun(value) {
73469
- return isRecord$2(value) && typeof value.requestId === "string" && typeof value.text === "string" && value.text.length <= 16e3 && [
74320
+ return isRecord$3(value) && typeof value.requestId === "string" && typeof value.text === "string" && value.text.length <= 16e3 && [
73470
74321
  "running",
73471
74322
  "completed",
73472
74323
  "failed",
@@ -79122,6 +79973,463 @@ button[${ACCOUNT_ATTRIBUTE}]:focus-visible{outline:2px solid ${require_theme.dsh
79122
79973
  start: () => void 0
79123
79974
  };
79124
79975
  //#endregion
79976
+ //#region src/client/features/workspace-mcp.ts
79977
+ const fallbackT = require_transport.resolveCodingNsTranslator();
79978
+ const workspaceMcpFeature = {
79979
+ descriptor: {
79980
+ name: "workspaceMcp",
79981
+ version: "0.1.0",
79982
+ enabledByDefault: false,
79983
+ dependencies: [],
79984
+ runtime: "client",
79985
+ ui: {
79986
+ label: fallbackT("feature.workspaceMcp.label"),
79987
+ description: fallbackT("feature.workspaceMcp.description"),
79988
+ labelKey: "feature.workspaceMcp.label",
79989
+ descriptionKey: "feature.workspaceMcp.description",
79990
+ order: 60,
79991
+ defaultOpen: false
79992
+ }
79993
+ },
79994
+ start() {},
79995
+ settingsPanel: WorkspaceMcpPanel
79996
+ };
79997
+ const WORKSPACE_MCP_CAPABILITIES = [
79998
+ {
79999
+ name: "workspace_list",
80000
+ permissionKey: "workspaceMcp.capability.workspaceList.permission",
80001
+ descriptionKey: "workspaceMcp.capability.workspaceList.description",
80002
+ parametersKey: "workspaceMcp.capability.workspaceList.parameters",
80003
+ remarksKey: "workspaceMcp.capability.workspaceList.remarks"
80004
+ },
80005
+ {
80006
+ name: "session_list",
80007
+ permissionKey: "workspaceMcp.capability.sessionList.permission",
80008
+ descriptionKey: "workspaceMcp.capability.sessionList.description",
80009
+ parametersKey: "workspaceMcp.capability.sessionList.parameters",
80010
+ remarksKey: "workspaceMcp.capability.sessionList.remarks"
80011
+ },
80012
+ {
80013
+ name: "session_create",
80014
+ permissionKey: "workspaceMcp.capability.sessionCreate.permission",
80015
+ descriptionKey: "workspaceMcp.capability.sessionCreate.description",
80016
+ parametersKey: "workspaceMcp.capability.sessionCreate.parameters",
80017
+ remarksKey: "workspaceMcp.capability.sessionCreate.remarks"
80018
+ },
80019
+ {
80020
+ name: "session_continue",
80021
+ permissionKey: "workspaceMcp.capability.sessionContinue.permission",
80022
+ descriptionKey: "workspaceMcp.capability.sessionContinue.description",
80023
+ parametersKey: "workspaceMcp.capability.sessionContinue.parameters",
80024
+ remarksKey: "workspaceMcp.capability.sessionContinue.remarks"
80025
+ },
80026
+ {
80027
+ name: "session_questions",
80028
+ permissionKey: "workspaceMcp.capability.sessionQuestions.permission",
80029
+ descriptionKey: "workspaceMcp.capability.sessionQuestions.description",
80030
+ parametersKey: "workspaceMcp.capability.sessionQuestions.parameters",
80031
+ remarksKey: "workspaceMcp.capability.sessionQuestions.remarks"
80032
+ },
80033
+ {
80034
+ name: "session_answer",
80035
+ permissionKey: "workspaceMcp.capability.sessionAnswer.permission",
80036
+ descriptionKey: "workspaceMcp.capability.sessionAnswer.description",
80037
+ parametersKey: "workspaceMcp.capability.sessionAnswer.parameters",
80038
+ remarksKey: "workspaceMcp.capability.sessionAnswer.remarks"
80039
+ },
80040
+ {
80041
+ name: "session_request_permission",
80042
+ permissionKey: "workspaceMcp.capability.sessionRequestPermission.permission",
80043
+ descriptionKey: "workspaceMcp.capability.sessionRequestPermission.description",
80044
+ parametersKey: "workspaceMcp.capability.sessionRequestPermission.parameters",
80045
+ remarksKey: "workspaceMcp.capability.sessionRequestPermission.remarks"
80046
+ },
80047
+ {
80048
+ name: "session_permission_status",
80049
+ permissionKey: "workspaceMcp.capability.sessionPermissionStatus.permission",
80050
+ descriptionKey: "workspaceMcp.capability.sessionPermissionStatus.description",
80051
+ parametersKey: "workspaceMcp.capability.sessionPermissionStatus.parameters",
80052
+ remarksKey: "workspaceMcp.capability.sessionPermissionStatus.remarks"
80053
+ },
80054
+ {
80055
+ name: "terminal_list",
80056
+ permissionKey: "workspaceMcp.capability.terminalList.permission",
80057
+ descriptionKey: "workspaceMcp.capability.terminalList.description",
80058
+ parametersKey: "workspaceMcp.capability.terminalList.parameters",
80059
+ remarksKey: "workspaceMcp.capability.terminalList.remarks"
80060
+ },
80061
+ {
80062
+ name: "terminal_read",
80063
+ permissionKey: "workspaceMcp.capability.terminalRead.permission",
80064
+ descriptionKey: "workspaceMcp.capability.terminalRead.description",
80065
+ parametersKey: "workspaceMcp.capability.terminalRead.parameters",
80066
+ remarksKey: "workspaceMcp.capability.terminalRead.remarks"
80067
+ },
80068
+ {
80069
+ name: "terminal_write",
80070
+ permissionKey: "workspaceMcp.capability.terminalWrite.permission",
80071
+ descriptionKey: "workspaceMcp.capability.terminalWrite.description",
80072
+ parametersKey: "workspaceMcp.capability.terminalWrite.parameters",
80073
+ remarksKey: "workspaceMcp.capability.terminalWrite.remarks"
80074
+ }
80075
+ ];
80076
+ const REASON_KEYS = {
80077
+ "api-key-missing": "workspaceMcp.reason.keyMissing",
80078
+ "api-key-not-configured": "workspaceMcp.reason.keyMissing",
80079
+ "key-store-failed": "workspaceMcp.reason.keyStoreFailed",
80080
+ disabled: "workspaceMcp.reason.disabled",
80081
+ "listen-failed": "workspaceMcp.reason.listenFailed",
80082
+ "listener-error": "workspaceMcp.reason.listenFailed",
80083
+ "startup-failed": "workspaceMcp.reason.listenFailed"
80084
+ };
80085
+ function WorkspaceMcpPanel({ services, enabled, notify }) {
80086
+ const t = require_transport.useCodingNsTranslator(services.locale);
80087
+ const [status, setStatus] = (0, react.useState)(null);
80088
+ const [loading, setLoading] = (0, react.useState)(true);
80089
+ const [failed, setFailed] = (0, react.useState)(false);
80090
+ const [refreshCount, setRefreshCount] = (0, react.useState)(0);
80091
+ const [busy, setBusy] = (0, react.useState)(false);
80092
+ const [generatedKey, setGeneratedKey] = (0, react.useState)(null);
80093
+ const [capabilitiesOpen, setCapabilitiesOpen] = (0, react.useState)(false);
80094
+ (0, react.useEffect)(() => {
80095
+ let active = true;
80096
+ setLoading(true);
80097
+ setFailed(false);
80098
+ services.rpc.call(require_transport.CODINGNS_RPC_CHANNEL, "workspaceMcp/status", {}).then((result) => {
80099
+ if (!result.ok) throw new Error("Workspace MCP status request failed");
80100
+ const next = normalizeStatus(result.value);
80101
+ if (next === null) throw new Error("Invalid Workspace MCP status");
80102
+ if (active) setStatus(next);
80103
+ }).catch(() => {
80104
+ if (active) {
80105
+ setStatus(null);
80106
+ setFailed(true);
80107
+ }
80108
+ }).finally(() => {
80109
+ if (active) setLoading(false);
80110
+ });
80111
+ return () => {
80112
+ active = false;
80113
+ };
80114
+ }, [
80115
+ services.rpc,
80116
+ enabled,
80117
+ refreshCount
80118
+ ]);
80119
+ (0, react.useEffect)(() => {
80120
+ if (!capabilitiesOpen) return;
80121
+ const closeOnEscape = (event) => {
80122
+ if (event.key === "Escape") setCapabilitiesOpen(false);
80123
+ };
80124
+ globalThis.addEventListener("keydown", closeOnEscape);
80125
+ return () => globalThis.removeEventListener("keydown", closeOnEscape);
80126
+ }, [capabilitiesOpen]);
80127
+ const reasonKey = status?.reason === null || status?.reason === void 0 ? null : Object.prototype.hasOwnProperty.call(REASON_KEYS, status.reason) ? REASON_KEYS[status.reason] ?? "workspaceMcp.reason.other" : "workspaceMcp.reason.other";
80128
+ const url = safeWorkspaceMcpUrl(status?.url);
80129
+ const generateKey = async () => {
80130
+ setBusy(true);
80131
+ setGeneratedKey(null);
80132
+ try {
80133
+ const result = await services.rpc.call(require_transport.CODINGNS_RPC_CHANNEL, "workspaceMcp/generate-key", {});
80134
+ if (!result.ok || !isRecord$2(result.value) || typeof result.value.apiKey !== "string") throw new Error(result.ok ? "Invalid Workspace MCP key response" : result.error.message);
80135
+ setGeneratedKey(result.value.apiKey);
80136
+ setRefreshCount((count) => count + 1);
80137
+ notify({
80138
+ kind: "success",
80139
+ message: t("workspaceMcp.keyGenerated")
80140
+ });
80141
+ } catch (error) {
80142
+ notify({
80143
+ kind: "error",
80144
+ message: error instanceof Error ? error.message : String(error)
80145
+ });
80146
+ } finally {
80147
+ setBusy(false);
80148
+ }
80149
+ };
80150
+ const copyKey = async () => {
80151
+ if (generatedKey === null) return;
80152
+ try {
80153
+ await navigator.clipboard.writeText(generatedKey);
80154
+ notify({
80155
+ kind: "success",
80156
+ message: t("workspaceMcp.keyCopied")
80157
+ });
80158
+ } catch {
80159
+ notify({
80160
+ kind: "error",
80161
+ message: t("workspaceMcp.copyFailed")
80162
+ });
80163
+ }
80164
+ };
80165
+ const installPrompt = t("workspaceMcp.installPromptText");
80166
+ const copyInstallPrompt = async () => {
80167
+ try {
80168
+ await navigator.clipboard.writeText(installPrompt);
80169
+ notify({
80170
+ kind: "success",
80171
+ message: t("workspaceMcp.installPromptCopied")
80172
+ });
80173
+ } catch {
80174
+ notify({
80175
+ kind: "error",
80176
+ message: t("workspaceMcp.copyFailed")
80177
+ });
80178
+ }
80179
+ };
80180
+ const compactButtonStyle = {
80181
+ ...require_theme.dshSettingsButtonStyle,
80182
+ flex: "1 1 0",
80183
+ minWidth: 0,
80184
+ minHeight: 32,
80185
+ padding: "6px 9px",
80186
+ fontSize: require_theme.uiFontSize(12),
80187
+ lineHeight: 1.25,
80188
+ whiteSpace: "normal"
80189
+ };
80190
+ return (0, react.createElement)("div", { style: {
80191
+ display: "flex",
80192
+ flexDirection: "column",
80193
+ gap: 12
80194
+ } }, (0, react.createElement)("div", { style: require_theme.dshSettingsNoteStyle }, (0, react.createElement)("strong", { style: {
80195
+ display: "block",
80196
+ marginBottom: 4
80197
+ } }, t("workspaceMcp.setupTitle")), (0, react.createElement)("span", void 0, t("workspaceMcp.setupInstruction"))), (0, react.createElement)("div", { style: {
80198
+ display: "flex",
80199
+ flexWrap: "nowrap",
80200
+ gap: 8,
80201
+ minWidth: 0
80202
+ } }, (0, react.createElement)("button", {
80203
+ type: "button",
80204
+ style: compactButtonStyle,
80205
+ onClick: () => setCapabilitiesOpen(true)
80206
+ }, t("workspaceMcp.capabilityDescription")), (0, react.createElement)("button", {
80207
+ type: "button",
80208
+ disabled: busy || loading,
80209
+ onClick: () => void generateKey(),
80210
+ style: compactButtonStyle
80211
+ }, busy ? t("workspaceMcp.generating") : t(status?.apiKeyConfigured ? "workspaceMcp.rotateKey" : "workspaceMcp.generateKey")), (0, react.createElement)("button", {
80212
+ type: "button",
80213
+ disabled: loading || busy,
80214
+ onClick: () => setRefreshCount((count) => count + 1),
80215
+ style: compactButtonStyle
80216
+ }, t("workspaceMcp.refresh"))), generatedKey === null ? null : (0, react.createElement)("div", { style: {
80217
+ display: "flex",
80218
+ flexWrap: "wrap",
80219
+ gap: 8,
80220
+ alignItems: "center"
80221
+ } }, (0, react.createElement)("input", {
80222
+ type: "text",
80223
+ readOnly: true,
80224
+ value: generatedKey,
80225
+ autoComplete: "off",
80226
+ spellCheck: false,
80227
+ "aria-label": t("workspaceMcp.generatedKey"),
80228
+ style: {
80229
+ ...require_theme.dshSettingsFieldStyle,
80230
+ flex: "1 1 260px",
80231
+ minWidth: 0
80232
+ },
80233
+ onFocus: (event) => event.currentTarget.select()
80234
+ }), (0, react.createElement)("button", {
80235
+ type: "button",
80236
+ onClick: () => void copyKey(),
80237
+ style: require_theme.dshSettingsButtonStyle
80238
+ }, t("workspaceMcp.copyKey")), (0, react.createElement)("span", { style: {
80239
+ flexBasis: "100%",
80240
+ color: require_theme.dshThemeColor.warning,
80241
+ fontSize: require_theme.uiFontSize(12),
80242
+ lineHeight: 1.5
80243
+ } }, t("workspaceMcp.keyShownOnce"))), (0, react.createElement)("div", {
80244
+ "aria-live": "polite",
80245
+ style: {
80246
+ display: "flex",
80247
+ flexDirection: "column",
80248
+ gap: 6,
80249
+ fontSize: require_theme.uiFontSize(12),
80250
+ lineHeight: 1.5,
80251
+ overflowWrap: "anywhere"
80252
+ }
80253
+ }, (0, react.createElement)("strong", void 0, t("workspaceMcp.statusTitle")), loading ? (0, react.createElement)("span", void 0, t("workspaceMcp.loading")) : null, failed ? (0, react.createElement)("span", {
80254
+ role: "alert",
80255
+ style: { color: require_theme.dshThemeColor.error }
80256
+ }, t("workspaceMcp.statusFailed")) : null, status !== null && !loading ? (0, react.createElement)("span", void 0, t("workspaceMcp.apiKeyStatus", { status: t(status.apiKeyConfigured ? "workspaceMcp.configured" : "workspaceMcp.notConfigured") })) : null, status !== null && !loading ? (0, react.createElement)("span", void 0, t("workspaceMcp.enabledStatus", { status: t(status.enabled ? "workspaceMcp.on" : "workspaceMcp.off") })) : null, status !== null && !loading ? (0, react.createElement)("span", void 0, t("workspaceMcp.listeningStatus", { status: t(status.listening ? "workspaceMcp.on" : "workspaceMcp.off") })) : null, status !== null && !loading && url !== null ? (0, react.createElement)("span", void 0, (0, react.createElement)("span", { style: { color: require_theme.dshThemeColor.labelSecondary } }, `${t("workspaceMcp.url")}: `), (0, react.createElement)("code", void 0, url)) : null, reasonKey !== null && status !== null && !loading ? (0, react.createElement)("span", { style: { color: require_theme.dshThemeColor.labelSecondary } }, t(reasonKey)) : null), (0, react.createElement)("div", { style: {
80257
+ display: "flex",
80258
+ flexDirection: "column",
80259
+ gap: 8,
80260
+ paddingTop: 12,
80261
+ borderTop: `1px solid ${require_theme.dshThemeColor.border}`
80262
+ } }, (0, react.createElement)("strong", { style: { fontSize: require_theme.uiFontSize(13) } }, t("workspaceMcp.installPromptTitle")), (0, react.createElement)("span", { style: {
80263
+ color: require_theme.dshThemeColor.labelSecondary,
80264
+ fontSize: require_theme.uiFontSize(12),
80265
+ lineHeight: 1.5
80266
+ } }, t("workspaceMcp.installPromptHint")), (0, react.createElement)("pre", { style: {
80267
+ margin: 0,
80268
+ padding: 10,
80269
+ maxHeight: 112,
80270
+ overflow: "auto",
80271
+ whiteSpace: "pre-wrap",
80272
+ overflowWrap: "anywhere",
80273
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
80274
+ borderRadius: 6,
80275
+ color: require_theme.dshThemeColor.labelSecondary,
80276
+ background: require_theme.dshThemeColor.pageBackground,
80277
+ fontSize: require_theme.uiFontSize(11),
80278
+ lineHeight: 1.45
80279
+ } }, installPrompt), (0, react.createElement)("button", {
80280
+ type: "button",
80281
+ onClick: () => void copyInstallPrompt(),
80282
+ style: {
80283
+ ...require_theme.dshSettingsButtonStyle,
80284
+ alignSelf: "flex-end",
80285
+ minHeight: 32,
80286
+ padding: "6px 10px",
80287
+ fontSize: require_theme.uiFontSize(12)
80288
+ }
80289
+ }, t("workspaceMcp.copyInstallPrompt"))), capabilitiesOpen ? createWorkspaceMcpCapabilitiesModal(t, () => setCapabilitiesOpen(false)) : null);
80290
+ }
80291
+ function createWorkspaceMcpCapabilitiesModal(t, onClose) {
80292
+ return (0, react.createElement)("div", {
80293
+ role: "presentation",
80294
+ onClick: onClose,
80295
+ style: {
80296
+ position: "fixed",
80297
+ inset: 0,
80298
+ zIndex: 2e3,
80299
+ display: "flex",
80300
+ alignItems: "center",
80301
+ justifyContent: "center",
80302
+ padding: 16,
80303
+ background: "rgba(0, 0, 0, 0.62)",
80304
+ boxSizing: "border-box"
80305
+ },
80306
+ "data-workspace-mcp-capabilities": true
80307
+ }, (0, react.createElement)("div", {
80308
+ role: "dialog",
80309
+ "aria-modal": true,
80310
+ "aria-labelledby": "codingns-workspace-mcp-capability-title",
80311
+ onClick: (event) => event.stopPropagation(),
80312
+ style: {
80313
+ display: "flex",
80314
+ flexDirection: "column",
80315
+ width: "min(960px, 100%)",
80316
+ maxHeight: "min(820px, calc(100dvh - 32px))",
80317
+ overflow: "hidden",
80318
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
80319
+ borderRadius: 12,
80320
+ background: require_theme.dshThemeColor.menuBackground,
80321
+ color: require_theme.dshThemeColor.labelPrimary,
80322
+ boxShadow: "0 20px 70px rgba(0, 0, 0, 0.45)"
80323
+ }
80324
+ }, (0, react.createElement)("div", { style: {
80325
+ display: "flex",
80326
+ alignItems: "flex-start",
80327
+ justifyContent: "space-between",
80328
+ gap: 16,
80329
+ padding: "18px 20px 14px",
80330
+ borderBottom: `1px solid ${require_theme.dshThemeColor.border}`
80331
+ } }, (0, react.createElement)("div", { style: { minWidth: 0 } }, (0, react.createElement)("h2", {
80332
+ id: "codingns-workspace-mcp-capability-title",
80333
+ style: {
80334
+ margin: 0,
80335
+ fontSize: require_theme.uiFontSize(18),
80336
+ lineHeight: 1.3
80337
+ }
80338
+ }, t("workspaceMcp.capabilityTitle")), (0, react.createElement)("p", { style: {
80339
+ margin: "6px 0 0",
80340
+ color: require_theme.dshThemeColor.labelSecondary,
80341
+ fontSize: require_theme.uiFontSize(12),
80342
+ lineHeight: 1.5
80343
+ } }, t("workspaceMcp.capabilitySubtitle"))), (0, react.createElement)("button", {
80344
+ type: "button",
80345
+ "aria-label": t("workspaceMcp.closeCapabilities"),
80346
+ onClick: onClose,
80347
+ style: {
80348
+ ...require_theme.dshSettingsButtonStyle,
80349
+ minWidth: 36,
80350
+ padding: "8px 10px",
80351
+ fontSize: require_theme.uiFontSize(18),
80352
+ lineHeight: 1
80353
+ }
80354
+ }, "×")), (0, react.createElement)("div", { style: {
80355
+ padding: 14,
80356
+ overflowY: "auto",
80357
+ overflowX: "auto",
80358
+ overscrollBehavior: "contain"
80359
+ } }, (0, react.createElement)("table", { style: {
80360
+ width: "100%",
80361
+ minWidth: 900,
80362
+ borderCollapse: "collapse",
80363
+ tableLayout: "fixed",
80364
+ fontSize: require_theme.uiFontSize(13),
80365
+ lineHeight: 1.5
80366
+ } }, (0, react.createElement)("colgroup", void 0, (0, react.createElement)("col", { style: { width: 170 } }), (0, react.createElement)("col", { style: { width: 190 } }), (0, react.createElement)("col", { style: { width: 230 } }), (0, react.createElement)("col", { style: { width: 185 } }), (0, react.createElement)("col", { style: { width: 235 } })), (0, react.createElement)("thead", void 0, (0, react.createElement)("tr", void 0, ...[
80367
+ "workspaceMcp.capabilityName",
80368
+ "workspaceMcp.capabilityPermission",
80369
+ "workspaceMcp.capabilityDescriptionField",
80370
+ "workspaceMcp.capabilityParameters",
80371
+ "workspaceMcp.capabilityRemarks"
80372
+ ].map((key) => (0, react.createElement)("th", {
80373
+ key,
80374
+ scope: "col",
80375
+ style: {
80376
+ padding: "10px 12px",
80377
+ textAlign: "left",
80378
+ verticalAlign: "top",
80379
+ color: require_theme.dshThemeColor.labelSecondary,
80380
+ fontSize: require_theme.uiFontSize(12),
80381
+ fontWeight: 650,
80382
+ borderBottom: `1px solid ${require_theme.dshThemeColor.border}`,
80383
+ background: require_theme.dshThemeColor.pageBackground
80384
+ }
80385
+ }, t(key))))), (0, react.createElement)("tbody", void 0, ...WORKSPACE_MCP_CAPABILITIES.map((capability, index) => (0, react.createElement)("tr", {
80386
+ key: capability.name,
80387
+ style: { background: index % 2 === 0 ? "rgba(255, 255, 255, 0.025)" : "transparent" }
80388
+ }, (0, react.createElement)("td", { style: capabilityCellStyle }, (0, react.createElement)("code", { style: { overflowWrap: "anywhere" } }, capability.name)), (0, react.createElement)("td", { style: capabilityCellStyle }, t(capability.permissionKey)), (0, react.createElement)("td", { style: capabilityCellStyle }, t(capability.descriptionKey)), (0, react.createElement)("td", { style: capabilityCellStyle }, (0, react.createElement)("code", { style: {
80389
+ whiteSpace: "pre-wrap",
80390
+ overflowWrap: "anywhere"
80391
+ } }, t(capability.parametersKey))), (0, react.createElement)("td", { style: capabilityCellStyle }, t(capability.remarksKey))))))), (0, react.createElement)("div", { style: {
80392
+ display: "flex",
80393
+ justifyContent: "flex-end",
80394
+ padding: "12px 20px 16px",
80395
+ borderTop: `1px solid ${require_theme.dshThemeColor.border}`
80396
+ } }, (0, react.createElement)("button", {
80397
+ type: "button",
80398
+ onClick: onClose,
80399
+ style: require_theme.dshSettingsPrimaryButtonStyle
80400
+ }, t("workspaceMcp.closeCapabilities")))));
80401
+ }
80402
+ const capabilityCellStyle = {
80403
+ padding: "11px 12px",
80404
+ textAlign: "left",
80405
+ verticalAlign: "top",
80406
+ overflowWrap: "anywhere",
80407
+ borderBottom: `1px solid ${require_theme.dshThemeColor.border}`
80408
+ };
80409
+ function normalizeStatus(value) {
80410
+ if (!isRecord$2(value) || typeof value.apiKeyConfigured !== "boolean" || typeof value.enabled !== "boolean" || typeof value.listening !== "boolean" || value.url !== null && typeof value.url !== "string" || value.reason !== null && typeof value.reason !== "string") return null;
80411
+ return {
80412
+ apiKeyConfigured: value.apiKeyConfigured,
80413
+ enabled: value.enabled,
80414
+ listening: value.listening,
80415
+ url: value.url,
80416
+ reason: value.reason
80417
+ };
80418
+ }
80419
+ function safeWorkspaceMcpUrl(value) {
80420
+ if (value === null || value === void 0) return null;
80421
+ try {
80422
+ const url = new URL(value);
80423
+ if (url.protocol !== "http:" || url.hostname !== "127.0.0.1" || url.search !== "" || url.hash !== "" || url.pathname !== "/mcp" || value !== `${url.origin}/mcp`) return null;
80424
+ return url.toString();
80425
+ } catch {
80426
+ return null;
80427
+ }
80428
+ }
80429
+ function isRecord$2(value) {
80430
+ return typeof value === "object" && value !== null && !Array.isArray(value);
80431
+ }
80432
+ //#endregion
79125
80433
  //#region src/features/host-router.ts
79126
80434
  /** 统一管理当前 Host/PeerHost 的完整作用域和 generation。 */
79127
80435
  var HostRouter = class {
@@ -79340,6 +80648,7 @@ button[${ACCOUNT_ATTRIBUTE}]:focus-visible{outline:2px solid ${require_theme.dsh
79340
80648
  /** Client 侧功能模块清单:新增模块在这里登记一行,不需要改动设置页和入口。 */
79341
80649
  const CLIENT_FEATURES = [
79342
80650
  pluginUpdateFeature,
80651
+ workspaceMcpFeature,
79343
80652
  lanAccessFeature,
79344
80653
  loginProtectionFeature,
79345
80654
  reverseProxyFeature,
@@ -79470,7 +80779,7 @@ button.${settingsPromoClass.version}:focus-visible{${focusRing}}
79470
80779
  return (0, react.createElement)("aside", {
79471
80780
  className: settingsPromoClass.root,
79472
80781
  "aria-label": t("settings.promo.sectionLabel")
79473
- }, (0, react.createElement)("div", { className: settingsPromoClass.row }, (0, react.createElement)("div", { className: settingsPromoClass.brand }, (0, react.createElement)("div", { className: settingsPromoClass.headline }, (0, react.createElement)(StarBurstIcon, { className: settingsPromoClass.logo }), (0, react.createElement)("h3", { className: settingsPromoClass.title }, t("common.brand"))), versionControl ?? (0, react.createElement)("span", { className: settingsPromoClass.version }, t("settings.promo.version", { version: "0.2.1-rc.6" }))), (0, react.createElement)("p", { className: settingsPromoClass.text }, t("settings.promo.starHint"), (0, react.createElement)("span", {
80782
+ }, (0, react.createElement)("div", { className: settingsPromoClass.row }, (0, react.createElement)("div", { className: settingsPromoClass.brand }, (0, react.createElement)("div", { className: settingsPromoClass.headline }, (0, react.createElement)(StarBurstIcon, { className: settingsPromoClass.logo }), (0, react.createElement)("h3", { className: settingsPromoClass.title }, t("common.brand"))), versionControl ?? (0, react.createElement)("span", { className: settingsPromoClass.version }, t("settings.promo.version", { version: "0.2.1-rc.7" }))), (0, react.createElement)("p", { className: settingsPromoClass.text }, t("settings.promo.starHint"), (0, react.createElement)("span", {
79474
80783
  className: settingsPromoClass.hand,
79475
80784
  "aria-hidden": true
79476
80785
  }, "👉")), (0, react.createElement)("a", {
@@ -79529,7 +80838,7 @@ button.${settingsPromoClass.version}:focus-visible{${focusRing}}
79529
80838
  }
79530
80839
  //#endregion
79531
80840
  //#region src/client/update-store.ts
79532
- const DEFAULT_PLUGIN_UPDATE_CHANNEL = "0.2.1-rc.6".includes("-") ? "next" : "latest";
80841
+ const DEFAULT_PLUGIN_UPDATE_CHANNEL = "0.2.1-rc.7".includes("-") ? "next" : "latest";
79533
80842
  var PluginUpdateStore = class {
79534
80843
  rpc;
79535
80844
  snapshot = {
@@ -85968,7 +87277,7 @@ button.${settingsPromoClass.version}:focus-visible{${focusRing}}
85968
87277
  "dsh-capabilities/host/session-prompt-adapter.ts",
85969
87278
  "host/features/session-prompt-translation.ts",
85970
87279
  "host/cli-adapters/feature.ts"
85971
- ]), route("session.external-model-selection", "session-external-model-selection-021", "host", ">=0.2.1-alpha.3", "supported", ["dsh-capabilities/host/session-model-selection-adapter.ts", "host/cli-adapters/feature.ts"]), route("session.activation", "session-activation-020", "host", ">=0.2.0-rc.2 <=0.2.1-alpha.2", "supported", ["dsh-capabilities/host/session-activation-adapter.ts", "host/native-session-bridge.ts"]), route("session.activation", "session-activation-021", "host", ">=0.2.1-alpha.3", "supported", ["dsh-capabilities/host/session-activation-adapter.ts", "host/native-session-bridge.ts"]), route("conversation.message-presentation", "message-presentation-020", "client", ">=0.2.0-rc.2 <=0.2.1-alpha.2", "supported", ["dsh-capabilities/client/message-presentation-adapter.ts", "client/session-translation-chat.ts"]), route("conversation.message-presentation", "message-presentation-021", "client", ">=0.2.1-alpha.3", "supported", ["dsh-capabilities/client/message-presentation-adapter.ts", "client/session-translation-chat.ts"]), route("connection.peer", "connection.peer-021", "host", ">=0.2.1-alpha.3", "supported", [
87280
+ ]), route("session.external-model-selection", "session-external-model-selection-021", "host", ">=0.2.1-alpha.3", "supported", ["dsh-capabilities/host/session-model-selection-adapter.ts", "host/cli-adapters/feature.ts"]), route("session.activation", "session-activation-020", "host", ">=0.2.0-rc.2 <=0.2.1-alpha.2", "supported", ["dsh-capabilities/host/session-activation-adapter.ts", "host/native-session-bridge.ts"]), route("session.activation", "session-activation-021", "host", ">=0.2.1-alpha.3", "supported", ["dsh-capabilities/host/session-activation-adapter.ts", "host/native-session-bridge.ts"]), route("workspace.session-control", "workspace-session-control-020", "host", require_native_workspace_store.DSH_COMPATIBILITY, "supported", ["dsh-capabilities/host/workspace-session-control-adapter.ts", "host/workspace-mcp/tools.ts"]), route("workspace.pending-questions", "workspace-pending-questions-020", "host", require_native_workspace_store.DSH_COMPATIBILITY, "supported", ["dsh-capabilities/host/workspace-pending-questions-adapter.ts", "host/workspace-mcp/tools.ts"]), route("workspace.approval", "workspace-approval-020", "host", require_native_workspace_store.DSH_COMPATIBILITY, "supported", ["dsh-capabilities/host/workspace-approval-adapter.ts", "host/workspace-mcp/tools.ts"]), route("conversation.message-presentation", "message-presentation-020", "client", ">=0.2.0-rc.2 <=0.2.1-alpha.2", "supported", ["dsh-capabilities/client/message-presentation-adapter.ts", "client/session-translation-chat.ts"]), route("conversation.message-presentation", "message-presentation-021", "client", ">=0.2.1-alpha.3", "supported", ["dsh-capabilities/client/message-presentation-adapter.ts", "client/session-translation-chat.ts"]), route("connection.peer", "connection.peer-021", "host", ">=0.2.1-alpha.3", "supported", [
85972
87281
  "dsh-capabilities/host/connection-peer-adapter.ts",
85973
87282
  "host/rpc.ts",
85974
87283
  "host/features/global-voice-rpc.ts",
@@ -92000,5 +93309,5 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
92000
93309
  });
92001
93310
 
92002
93311
  })();
92003
- // codingns-client-revision: cd7d0051b78ba2ffbee4f037966c1f1af54ec8f25faa8891c2d414d9cbe7e77a
93312
+ // codingns-client-revision: b72c0083de197d850d138dad49153dd85be9eb7fa81595c4642289baac3e4baf
92004
93313
  //# sourceMappingURL=bundle.js.map