@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
@@ -92,6 +92,12 @@ export function MagpieManagementPage({ rpc, locale, initialPageTab = 'overview',
92
92
  const [dashboardLoading, setDashboardLoading] = useState(true);
93
93
  const [dashboardError, setDashboardError] = useState(false);
94
94
  const [dashboardIssue, setDashboardIssue] = useState(null);
95
+ const [quotaPolicyPending, setQuotaPolicyPending] = useState(null);
96
+ const [quotaPolicyFailed, setQuotaPolicyFailed] = useState(false);
97
+ const [quotaResetTarget, setQuotaResetTarget] = useState(null);
98
+ const [quotaResetPending, setQuotaResetPending] = useState(false);
99
+ const [quotaResetResult, setQuotaResetResult] = useState(null);
100
+ const [quotaResetFailed, setQuotaResetFailed] = useState(false);
95
101
  const [chartMetric, setChartMetric] = useState('calls');
96
102
  const [sessionSearch, setSessionSearch] = useState('');
97
103
  const [selectedSession, setSelectedSession] = useState(null);
@@ -114,14 +120,14 @@ export function MagpieManagementPage({ rpc, locale, initialPageTab = 'overview',
114
120
  const dashboardSequence = useRef(0);
115
121
  const requestSequence = useRef(0);
116
122
  const managementSequence = useRef(0);
117
- const loadDashboard = useCallback(async (signal) => {
123
+ const loadDashboard = useCallback(async (signal, force = false) => {
118
124
  const sequence = dashboardSequence.current + 1;
119
125
  dashboardSequence.current = sequence;
120
126
  setDashboardLoading(true);
121
127
  setDashboardError(false);
122
128
  setDashboardIssue(null);
123
129
  try {
124
- const next = await callMagpieRpc(rpc, 'usage/dashboard', { period }, signal);
130
+ const next = await callMagpieRpc(rpc, 'usage/dashboard', { period, ...(force ? { force: true } : {}) }, signal);
125
131
  if (signal.aborted || sequence !== dashboardSequence.current)
126
132
  return;
127
133
  setDashboard(next);
@@ -141,6 +147,47 @@ export function MagpieManagementPage({ rpc, locale, initialPageTab = 'overview',
141
147
  setDashboardLoading(false);
142
148
  }
143
149
  }, [period, rpc]);
150
+ const updateQuotaPolicy = useCallback(async (user, policy, enabled) => {
151
+ if (quotaPolicyPending !== null)
152
+ return;
153
+ setQuotaPolicyPending(`${user}:${policy}`);
154
+ setQuotaPolicyFailed(false);
155
+ try {
156
+ await callMagpieRpc(rpc, 'usage/account/update', { user, policy, enabled });
157
+ await loadDashboard(new AbortController().signal, true);
158
+ }
159
+ catch {
160
+ setQuotaPolicyFailed(true);
161
+ }
162
+ finally {
163
+ setQuotaPolicyPending(null);
164
+ }
165
+ }, [loadDashboard, quotaPolicyPending, rpc]);
166
+ const closeQuotaReset = () => {
167
+ if (quotaResetPending)
168
+ return;
169
+ setQuotaResetTarget(null);
170
+ setQuotaResetResult(null);
171
+ setQuotaResetFailed(false);
172
+ };
173
+ const confirmQuotaReset = async () => {
174
+ const target = quotaResetTarget;
175
+ if (target === null || quotaResetPending)
176
+ return;
177
+ setQuotaResetPending(true);
178
+ setQuotaResetFailed(false);
179
+ try {
180
+ const result = await callMagpieRpc(rpc, 'usage/reset', { user: target.user });
181
+ setQuotaResetResult(result.outcome);
182
+ await loadDashboard(new AbortController().signal, true);
183
+ }
184
+ catch {
185
+ setQuotaResetFailed(true);
186
+ }
187
+ finally {
188
+ setQuotaResetPending(false);
189
+ }
190
+ };
144
191
  useEffect(() => {
145
192
  const controller = new AbortController();
146
193
  void loadDashboard(controller.signal);
@@ -456,7 +503,28 @@ export function MagpieManagementPage({ rpc, locale, initialPageTab = 'overview',
456
503
  ? createElement(SessionsPage, { dashboard, sessions: filteredSessions, search: sessionSearch, onSearch: setSessionSearch, onOpenSession: openSession, t })
457
504
  : section === 'requests'
458
505
  ? createElement(RequestsPage, { page: requestPage, loading: requestLoading, error: requestError, selectedSession, offset: requestOffset, onOffsetChange: setRequestOffset, onClearSession: clearSession, t })
459
- : createElement(QuotasPage, { dashboard, loading: dashboardLoading, quotaView, t })));
506
+ : createElement(QuotasPage, {
507
+ dashboard, loading: dashboardLoading, quotaView, t,
508
+ policyPending: quotaPolicyPending,
509
+ policyFailed: quotaPolicyFailed,
510
+ onPolicyChange: updateQuotaPolicy,
511
+ onResetRequest: (quota) => {
512
+ if (quota.user === null || quota.resets?.count === null || quota.resets?.count === undefined || quota.resets.count <= 0)
513
+ return;
514
+ setQuotaResetTarget({ user: quota.user, account: quota.alias ?? quota.name, count: quota.resets.count });
515
+ setQuotaResetResult(null);
516
+ setQuotaResetFailed(false);
517
+ },
518
+ })), quotaResetTarget !== null && createElement(MagpieQuotaResetDialog, {
519
+ target: quotaResetTarget,
520
+ pending: quotaResetPending,
521
+ result: quotaResetResult,
522
+ failed: quotaResetFailed,
523
+ t,
524
+ onCancel: closeQuotaReset,
525
+ onConfirm: confirmQuotaReset,
526
+ onClose: closeQuotaReset,
527
+ }));
460
528
  }
461
529
  export function OverviewPage({ dashboard, loading, error, chartMetric, onChartMetricChange, t, onOpenSession }) {
462
530
  const [modelBreakdownMode, setModelBreakdownMode] = useState('aggregate');
@@ -761,7 +829,7 @@ function QuotaViewIcon() {
761
829
  className: 'codingns-magpie-quota-view-icon-svg',
762
830
  }, createElement('circle', { cx: 8, cy: 8, r: 6.25, stroke: 'currentColor', strokeWidth: 1.5, opacity: 0.45 }), createElement('path', { d: 'M8 1.75a6.25 6.25 0 0 1 0 12.5z', fill: 'currentColor' }));
763
831
  }
764
- export function QuotasPage({ dashboard, loading, quotaView = DEFAULT_SUBSCRIPTION_REPORT_SETTINGS.quotaView, t }) {
832
+ export function QuotasPage({ dashboard, loading, quotaView = DEFAULT_SUBSCRIPTION_REPORT_SETTINGS.quotaView, t, policyPending = null, policyFailed = false, onPolicyChange = () => undefined, onResetRequest = () => undefined }) {
765
833
  const quotaUnavailable = dashboard?.quotaStatus !== 'available';
766
834
  const quotas = dashboard?.quotas ?? [];
767
835
  const quotaHistory = dashboard?.quotaHistory ?? [];
@@ -769,7 +837,9 @@ export function QuotasPage({ dashboard, loading, quotaView = DEFAULT_SUBSCRIPTIO
769
837
  ? createElement(EmptyState, { message: t(dashboard === null && loading ? 'magpie.loading' : 'magpie.quotaUnavailable'), loading: dashboard === null && loading })
770
838
  : quotas.length === 0
771
839
  ? createElement(EmptyState, { message: t('magpie.noQuotas') })
772
- : createElement('div', { className: 'codingns-magpie-quota-grid' }, ...groupQuotaRowsByProvider(quotas).map((group) => createElement(QuotaGroupCard, { key: group.provider, group, quotaView, t }))), createElement('section', { className: 'codingns-magpie-panel codingns-magpie-history-panel' }, createElement('div', { className: 'codingns-magpie-panel-head' }, createElement('div', undefined, createElement('h2', undefined, t('magpie.quotaHistory')), createElement('p', undefined, t('magpie.quotaHistoryNote')))), quotaHistory.length === 0
840
+ : createElement('div', { className: 'codingns-magpie-quota-grid' }, ...groupQuotaRowsByProvider(quotas).map((group) => createElement(QuotaGroupCard, {
841
+ key: group.provider, group, quotaView, t, policyPending, policyFailed, onPolicyChange, onResetRequest,
842
+ }))), createElement('section', { className: 'codingns-magpie-panel codingns-magpie-history-panel' }, createElement('div', { className: 'codingns-magpie-panel-head' }, createElement('div', undefined, createElement('h2', undefined, t('magpie.quotaHistory')), createElement('p', undefined, t('magpie.quotaHistoryNote')))), quotaHistory.length === 0
773
843
  ? createElement('div', { className: 'codingns-magpie-inline-empty' }, t(dashboard === null && loading ? 'magpie.loading' : dashboard?.quotaHistoryStatus === 'available' ? 'magpie.noHistory' : 'magpie.historyUnavailable'))
774
844
  : createElement('div', { className: 'codingns-magpie-history-grid' }, ...quotaHistory.map((history) => createElement(QuotaHistoryCard, { key: `${history.provider}:${history.user ?? ''}`, history })))));
775
845
  }
@@ -815,7 +885,7 @@ function ProgressSection({ title, rows, t, onOpen, header, limit = 8 }) {
815
885
  * 同一厂商的多个账号(如 WorkBuddy 的 4 个)共用一张卡,避免每个账号各占一张卡导致
816
886
  * 同类账号被拆散在网格各处;单账号厂商的读法与改造前一致。
817
887
  */
818
- function QuotaGroupCard({ group, quotaView = DEFAULT_SUBSCRIPTION_REPORT_SETTINGS.quotaView, t }) {
888
+ function QuotaGroupCard({ group, quotaView = DEFAULT_SUBSCRIPTION_REPORT_SETTINGS.quotaView, t, policyPending, policyFailed, onPolicyChange, onResetRequest }) {
819
889
  const first = group.accounts[0];
820
890
  // 默认展开:只有用户主动收起后才是折叠态。
821
891
  const [collapsed, setCollapsed] = useState(false);
@@ -835,6 +905,10 @@ function QuotaGroupCard({ group, quotaView = DEFAULT_SUBSCRIPTION_REPORT_SETTING
835
905
  quota,
836
906
  quotaView,
837
907
  t,
908
+ policyPending,
909
+ policyFailed,
910
+ onPolicyChange,
911
+ onResetRequest,
838
912
  // 单账号时不重复显示账号段头,保持与改造前一致的读法。
839
913
  showHeading: multi,
840
914
  })));
@@ -846,13 +920,15 @@ function QuotaGroupCard({ group, quotaView = DEFAULT_SUBSCRIPTION_REPORT_SETTING
846
920
  * Sub2api 的钱包余额、按积分计量的 WorkBuddy)单独显示余额行,与 Magpie 原生卡片
847
921
  * 一致——过去这些账号只剩一条空进度条,余额被完全藏起来。
848
922
  */
849
- function QuotaAccountSection({ quota, quotaView = DEFAULT_SUBSCRIPTION_REPORT_SETTINGS.quotaView, t, showHeading }) {
923
+ function QuotaAccountSection({ quota, quotaView = DEFAULT_SUBSCRIPTION_REPORT_SETTINGS.quotaView, t, showHeading, policyPending, policyFailed, onPolicyChange, onResetRequest }) {
850
924
  // `daily` 现在带 `since`,按日点位于 `days` 里。
851
925
  const dailyValues = quota.daily.days.map((point) => point.used).filter((value) => value !== null);
852
926
  const balance = resolveQuotaAccountBalance(quota);
853
927
  // 只有真正带百分比的窗口才画进度条;纯金额窗口在余额行里已经有完整读法。
854
928
  const percentWindows = quota.windows.filter((window) => window.usedPercent !== null || window.remainingPercent !== null);
855
- return createElement('div', { className: 'codingns-magpie-quota-account-block' }, showHeading && createElement('div', { className: 'codingns-magpie-quota-account-head' }, createElement('strong', undefined, quota.alias ?? quota.name), quota.user !== null && createElement('span', undefined, t('magpie.account', { account: quota.user })), quota.plan !== null && createElement('small', undefined, quota.plan)), !showHeading && quota.user !== null && createElement('div', { className: 'codingns-magpie-quota-account' }, t('magpie.account', { account: quota.user })), balance !== null && createElement('div', { className: 'codingns-magpie-quota-balance' }, createElement('span', { className: 'codingns-magpie-quota-balance-label' }, t('magpie.balance')), createElement('strong', { className: balance.money ? 'codingns-magpie-quota-balance-value is-money' : 'codingns-magpie-quota-balance-value', title: balance.raw }, balance.text)), quota.windows.length === 0
929
+ return createElement('div', { className: 'codingns-magpie-quota-account-block' }, showHeading && createElement('div', { className: 'codingns-magpie-quota-account-head' }, createElement('strong', undefined, quota.alias ?? quota.name), quota.user !== null && createElement('span', undefined, t('magpie.account', { account: quota.user })), quota.plan !== null && createElement('small', undefined, quota.plan)), !showHeading && quota.user !== null && createElement('div', { className: 'codingns-magpie-quota-account' }, t('magpie.account', { account: quota.user })), quota.provider.toLocaleLowerCase() === 'codex' && createElement(MagpieQuotaControls, {
930
+ quota, t, policyPending, policyFailed, onPolicyChange, onResetRequest,
931
+ }), balance !== null && createElement('div', { className: 'codingns-magpie-quota-balance' }, createElement('span', { className: 'codingns-magpie-quota-balance-label' }, t('magpie.balance')), createElement('strong', { className: balance.money ? 'codingns-magpie-quota-balance-value is-money' : 'codingns-magpie-quota-balance-value', title: balance.raw }, balance.text)), quota.windows.length === 0
856
932
  ? balance === null && createElement('div', { className: 'codingns-magpie-inline-empty' }, t('magpie.noLimit'))
857
933
  : percentWindows.length > 0 && percentWindows.map((window) => {
858
934
  const used = window.usedPercent ?? (window.remainingPercent === null ? null : 100 - window.remainingPercent);
@@ -870,6 +946,57 @@ function QuotaAccountSection({ quota, quotaView = DEFAULT_SUBSCRIPTION_REPORT_SE
870
946
  // 有余额但没有百分比的账号,把重置时间单独留在余额下面。
871
947
  balance !== null && percentWindows.length === 0 && quota.windows.some((window) => window.resetsAt !== null) && createElement('div', { className: 'codingns-magpie-quota-foot' }, ...quota.windows.filter((window) => window.resetsAt !== null).map((window) => createElement('span', { key: window.name }, t('magpie.resets', { time: formatTimestamp(window.resetsAt) })))), quota.daily.days.length > 1 && createElement('div', { className: 'codingns-magpie-daily-chart' }, createElement(Sparkline, { values: dailyValues }), createElement('small', undefined, t('magpie.dailyUsage'))));
872
948
  }
949
+ function MagpieQuotaControls({ quota, t, policyPending, policyFailed, onPolicyChange, onResetRequest }) {
950
+ const user = quota.user;
951
+ const resetCount = quota.resets?.count;
952
+ const policyUnavailable = user === null || quota.policyStatus === 'unavailable'
953
+ || quota.autoReset === undefined || quota.useCredits === undefined;
954
+ return createElement('div', { className: 'codingns-magpie-codex-controls' }, createElement('div', { className: 'codingns-magpie-codex-reset-count' }, createElement('span', undefined, t('usage.magpieResetCountLabel')), createElement('strong', undefined, resetCount === null || resetCount === undefined ? '—' : t('usage.magpieResetCount', { count: resetCount }))), policyUnavailable
955
+ ? createElement('div', { className: 'codingns-magpie-codex-unavailable', role: 'status' }, t('magpie.codexPolicyUnavailable'))
956
+ : quota.autoReset !== undefined && createElement(MagpieQuotaPolicySwitch, {
957
+ label: t('usage.magpieResetWhenWeek'), checked: quota.autoReset.week,
958
+ pending: policyPending === `${user}:week`, disabled: policyPending !== null,
959
+ onChange: (enabled) => onPolicyChange(user, 'week', enabled),
960
+ }), !policyUnavailable && quota.autoReset !== undefined && createElement(MagpieQuotaPolicySwitch, {
961
+ label: t('usage.magpieResetBeforeExpiry'), checked: quota.autoReset.expiring,
962
+ pending: policyPending === `${user}:expiring`, disabled: policyPending !== null,
963
+ onChange: (enabled) => onPolicyChange(user, 'expiring', enabled),
964
+ }), !policyUnavailable && quota.useCredits !== undefined && createElement(MagpieQuotaPolicySwitch, {
965
+ label: t('usage.magpieUseCredits'), checked: quota.useCredits,
966
+ pending: policyPending === `${user}:credits`, disabled: policyPending !== null,
967
+ onChange: (enabled) => onPolicyChange(user, 'credits', enabled),
968
+ }), policyFailed && createElement('div', { className: 'codingns-magpie-codex-error', role: 'alert' }, t('magpie.codexPolicyFailed')), user !== null && resetCount !== null && resetCount !== undefined && createElement('button', {
969
+ type: 'button',
970
+ className: 'codingns-magpie-codex-reset-button',
971
+ disabled: resetCount <= 0,
972
+ onClick: () => onResetRequest(quota),
973
+ }, t('usage.magpieUseReset')));
974
+ }
975
+ function MagpieQuotaPolicySwitch({ label, checked, pending, disabled, onChange }) {
976
+ return createElement('button', {
977
+ type: 'button', role: 'switch', 'aria-checked': checked, disabled,
978
+ 'aria-busy': pending,
979
+ className: 'codingns-magpie-codex-policy',
980
+ onClick: () => onChange(!checked),
981
+ }, createElement('span', undefined, label), createElement('span', { className: `codingns-magpie-codex-switch${checked ? ' is-on' : ''}` }, createElement('span')));
982
+ }
983
+ function MagpieQuotaResetDialog({ target, pending, result, failed, t, onCancel, onConfirm, onClose }) {
984
+ const finished = result !== null || failed;
985
+ const outcomeKeys = {
986
+ reset: 'usage.resetOutcomeReset',
987
+ alreadyRedeemed: 'usage.resetOutcomeAlreadyRedeemed',
988
+ nothingToReset: 'usage.resetOutcomeNothingToReset',
989
+ noCredit: 'usage.resetOutcomeNoCredit',
990
+ };
991
+ const outcomeMessage = result === null ? null : t(outcomeKeys[result]);
992
+ return createElement('div', {
993
+ className: 'codingns-magpie-codex-reset-overlay', role: 'presentation',
994
+ onMouseDown: (event) => { if (event.target === event.currentTarget && !pending)
995
+ onCancel(); },
996
+ }, createElement('div', { className: 'codingns-magpie-codex-reset-dialog', role: 'dialog', 'aria-modal': true, 'aria-label': t('usage.resetDialogTitle') }, createElement('strong', undefined, t('usage.resetDialogTitle')), createElement('span', undefined, t('magpie.resetDialogAccount', { account: target.account })), 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 })), createElement('div', { className: 'codingns-magpie-codex-reset-actions' }, finished
997
+ ? createElement('button', { type: 'button', onClick: onClose }, t('usage.resetDialogClose'))
998
+ : createElement('button', { type: 'button', disabled: pending, onClick: onCancel }, t('usage.resetDialogCancel')), !finished && createElement('button', { type: 'button', disabled: pending, onClick: onConfirm }, t('usage.resetDialogConfirm')))));
999
+ }
873
1000
  function QuotaHistoryCard({ history }) {
874
1001
  return createElement('div', { className: 'codingns-magpie-history-card' }, createElement('div', { className: 'codingns-magpie-provider-inline' }, createElement(MagpieProviderLogo, { identities: [history.provider], size: 22, className: 'codingns-magpie-provider-logo codingns-magpie-provider-logo-compact', fallback: 'none' }), createElement('strong', undefined, history.provider)), history.user !== null && createElement('small', undefined, history.user), ...history.lines.map((line) => createElement('div', { key: line.name, className: 'codingns-magpie-history-line' }, createElement('div', { className: 'codingns-magpie-history-line-head' }, createElement('span', undefined, line.name), createElement('span', undefined, line.points.at(-1)?.left === null || line.points.at(-1)?.left === undefined ? '—' : formatValue(line.points.at(-1).left))), createElement(Sparkline, { values: line.points.map((point) => point.left).filter((value) => value !== null) }))));
875
1002
  }
@@ -1137,6 +1264,13 @@ const MAGPIE_PAGE_CSS = `
1137
1264
  .codingns-magpie-quota-balance-label{flex:0 0 auto;color:var(--mp-muted);font-size:${uiFontSize(13)}}
1138
1265
  .codingns-magpie-quota-balance-value{min-width:0;overflow:hidden;color:var(--mp-ink);font-size:${uiFontSize(15)};font-weight:600;font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap}
1139
1266
  .codingns-magpie-quota-balance-value.is-money{font-size:${uiFontSize(17)};letter-spacing:-.01em}
1267
+ .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)}
1268
+ .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:${uiFontSize(12)}}.codingns-magpie-codex-reset-count strong{color:var(--mp-ink);font-size:${uiFontSize(13)};font-variant-numeric:tabular-nums}
1269
+ .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:${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}
1270
+ .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)}
1271
+ .codingns-magpie-codex-unavailable,.codingns-magpie-codex-error{padding:5px 0;color:var(--mp-caption);font-size:${uiFontSize(12)}}.codingns-magpie-codex-error{color:var(--dsw-alias-state-error-primary,#c54b4b)}
1272
+ .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:${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}
1273
+ .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:${uiFontSize(13)};line-height:1.5}.codingns-magpie-codex-reset-dialog>strong{font-size:${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:${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}
1140
1274
  .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:${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:${uiFontSize(12)}}
1141
1275
  .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:${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}
1142
1276
  .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:${uiFontSize(13)};font-weight:600}.codingns-magpie-history-card>small{margin-top:3px;color:var(--mp-caption);font-size:${uiFontSize(12)}}.codingns-magpie-history-line{margin-top:10px}.codingns-magpie-history-line-head{font-size:${uiFontSize(12)}}.codingns-magpie-history-line-head span:last-child{font-variant-numeric:tabular-nums;font-weight:650}