@oar-sh/oar 0.9.16 → 0.9.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/extensions/web-relay/polling/polling-loop.mjs +14 -87
- package/.github/extensions/web-relay/skills/tool-activity.mjs +5 -4
- package/CHANGELOG.md +412 -0
- package/README.md +15 -13
- package/package.json +2 -1
- package/server/claude-api-worker/claude-api-backend.mjs +345 -0
- package/server/claude-api-worker/claude-api-session-worker.mjs +29 -0
- package/server/claude-worker/claude-attachments.mjs +7 -4
- package/server/claude-worker/claude-cli-pinned-env.mjs +150 -0
- package/server/claude-worker/claude-remote-relay-tool.mjs +3 -3
- package/server/claude-worker/claude-sdk-adapter.mjs +199 -15
- package/server/claude-worker/claude-session-process.mjs +276 -33
- package/server/claude-worker/claude-session-worker-main.mjs +269 -0
- package/server/claude-worker/claude-session-worker.mjs +2 -253
- package/server/claude-worker/claude-subscription-key-check.mjs +64 -0
- package/server/claude-worker/claude-turn-failure.mjs +8 -0
- package/server/claude-worker/claude-turn-publisher.mjs +394 -13
- package/server/claude-worker/claude-usage-totals.mjs +161 -0
- package/server/claude-worker/sdk-message-normalizer.mjs +56 -1
- package/server/copilot-worker/copilot-remote-relay-tool.mjs +2 -2
- package/server/copilot-worker/copilot-sdk-adapter.mjs +18 -4
- package/server/copilot-worker/copilot-sdk-event-normalizer.mjs +1 -1
- package/server/copilot-worker/copilot-sdk-session-process.mjs +80 -14
- package/server/cursor-worker/cursor-remote-relay-tool.mjs +3 -3
- package/server/cursor-worker/cursor-turn-runner.mjs +4 -4
- package/server/cursor-worker/sdk-message-normalizer.mjs +2 -2
- package/server/db-schema.mjs +96 -1
- package/server/deepseek-worker/deepseek-claude-backend.mjs +317 -0
- package/server/deepseek-worker/deepseek-session-worker.mjs +37 -0
- package/server/grok-worker/acp-client.mjs +1 -1
- package/server/grok-worker/grok-turn-runner.mjs +4 -4
- package/server/grok-worker/sdk-message-normalizer.mjs +1 -1
- package/server/mcp/oar-mcp-server.mjs +11 -5
- package/server/migrations/0024-deepseek-usage-totals.mjs +151 -0
- package/server/migrations/0025-deepseek-usage-requests-daily.mjs +179 -0
- package/server/migrations/0026-deepseek-usage-served-models.mjs +222 -0
- package/server/migrations/0027-deepseek-usage-checkpoint-runs.mjs +101 -0
- package/server/migrations/0028-deepseek-usage-checkpoint-request-runs.mjs +99 -0
- package/server/migrations/0029-deepseek-balance-rises.mjs +99 -0
- package/server/migrations/0030-deepseek-usage-actuals.mjs +123 -0
- package/server/migrations/0031-queue-provider.mjs +143 -0
- package/server/migrations/0032-runtime-session-billing-switch.mjs +94 -0
- package/server/migrations/0033-claude-api-usage-checkpoints.mjs +138 -0
- package/server/migrations/0034-claude-api-usage-totals.mjs +112 -0
- package/server/public/app/activity-replay-state.mjs +4 -3
- package/server/public/app/agent-sessions-settings-ui.js +1 -1
- package/server/public/app/agent-sessions-settings.mjs +1 -1
- package/server/public/app/api-client.js +105 -0
- package/server/public/app/bootstrap.js +243 -29
- package/server/public/app/claude-api-settings-ui.js +362 -0
- package/server/public/app/claude-billing-toggle-ui.js +224 -0
- package/server/public/app/claude-billing-toggle.mjs +112 -0
- package/server/public/app/context-tier-options.mjs +10 -2
- package/server/public/app/context-usage-view.mjs +1 -1
- package/server/public/app/conversation-provider-indicator.mjs +18 -0
- package/server/public/app/conversation-view.js +31 -3
- package/server/public/app/deepseek-settings-ui.js +342 -0
- package/server/public/app/inbox-ui.js +14 -3
- package/server/public/app/inbox-view.mjs +1 -1
- package/server/public/app/journal-view.js +156 -11
- package/server/public/app/model-selector-options.mjs +28 -1
- package/server/public/app/model-vision-support.mjs +2 -0
- package/server/public/app/new-conversation-model-choice.mjs +21 -0
- package/server/public/app/no-conversation-state.mjs +1 -1
- package/server/public/app/plan-usage-view.mjs +165 -13
- package/server/public/app/provider-readiness.mjs +8 -2
- package/server/public/app/relay-error-ctas.mjs +58 -0
- package/server/public/app/relay-schedules-ui.js +1 -1
- package/server/public/app/settings-modal.js +22 -0
- package/server/public/app/settings-tabs.js +1 -1
- package/server/public/app/socket-handlers.js +28 -1
- package/server/public/app/tool-activity-emoji.mjs +4 -3
- package/server/public/app/usage-limit-ui.js +136 -5
- package/server/public/index.html +596 -35
- package/server/relay-tools.md +4 -4
- package/server/repositories/conversation-group-repository.mjs +14 -7
- package/server/repositories/message-repository.mjs +7 -0
- package/server/repositories/session-repository.mjs +8 -1
- package/server/routes/ask-user-routes.mjs +1 -1
- package/server/routes/conversation-group-routes.mjs +3 -2
- package/server/routes/deepseek-usage-import-routes.mjs +72 -0
- package/server/routes/messages-routes.mjs +831 -36
- package/server/routes/relay-ask-routes.mjs +3 -3
- package/server/routes/relay-event-routes.mjs +2 -2
- package/server/routes/relay-schedule-routes.mjs +1 -1
- package/server/routes/remote-relay-routes.mjs +3 -3
- package/server/routes/sessions-routes.mjs +670 -63
- package/server/server-runtime.mjs +476 -26
- package/server/services/about-service.mjs +2 -0
- package/server/services/background-work-view.mjs +2 -1
- package/server/services/claude-api-settings-service.mjs +575 -0
- package/server/services/claude-api-usage-service.mjs +361 -0
- package/server/services/claude-billing-swap-service.mjs +274 -0
- package/server/services/claude-billing-switch-service.mjs +343 -0
- package/server/services/claude-session-leftovers.mjs +226 -0
- package/server/services/deepseek-balance-service.mjs +102 -0
- package/server/services/deepseek-settings-service.mjs +380 -0
- package/server/services/deepseek-usage-import.mjs +471 -0
- package/server/services/model-variant-catalog-service.mjs +1 -0
- package/server/services/plan-usage-claude-api.mjs +445 -0
- package/server/services/plan-usage-contract.mjs +100 -5
- package/server/services/plan-usage-deepseek.mjs +931 -0
- package/server/services/plan-usage-service.mjs +436 -0
- package/server/services/provider-model-selection.mjs +4 -2
- package/server/services/provider-reasoning-effort.mjs +24 -2
- package/server/services/relay-ask-service.mjs +1 -1
- package/server/services/relay-event-service.mjs +3 -2
- package/server/services/relay-schedule-service.mjs +1 -1
- package/server/services/remote-relay-caller-context.mjs +7 -0
- package/server/services/remote-relay-dispatcher.mjs +84 -14
- package/server/services/remote-relay-registry-service.mjs +1 -1
- package/server/services/session-worker-launch-service.mjs +294 -84
- package/server/services/session-worker-registry-service.mjs +28 -1
- package/server/services/session-worker-websocket-service.mjs +17 -0
- package/server/services/usage-limit-pause-service.mjs +55 -4
- package/server/services/worker-secret-redaction.mjs +320 -0
- package/shared/anthropic-api.mjs +202 -0
- package/shared/context-window-fallbacks.mjs +3 -0
- package/shared/deepseek-api.mjs +137 -0
- package/shared/deepseek-models.mjs +114 -0
- package/shared/deepseek-pricing.mjs +146 -0
- package/shared/host-claude-session-env.mjs +101 -0
- package/shared/model-id.mjs +7 -0
- package/shared/preview-tool-core.mjs +1 -1
- package/shared/provider-routing.mjs +5 -3
- package/shared/relay-asks.mjs +11 -5
- package/shared/relay-events.mjs +2 -2
- package/shared/relay-schedules.mjs +2 -2
- package/shared/remote-relay-contract.mjs +29 -9
- package/shared/remote-relay-tool-core.mjs +23 -6
- package/shared/steer-settle-failure.mjs +2 -0
- package/shared/worker-runtime/api-client.mjs +2 -2
- package/shared/worker-runtime/direct-openai-image.mjs +128 -0
- package/shared/worker-runtime/heartbeat.mjs +5 -0
- package/shared/worker-runtime/worker-log-file.mjs +60 -0
- package/shared/worker-runtime/worker-websocket-link.mjs +6 -0
package/server/public/index.html
CHANGED
|
@@ -295,6 +295,18 @@
|
|
|
295
295
|
--provider-cursor: #a78bfa;
|
|
296
296
|
--provider-grok: #e879f9;
|
|
297
297
|
--provider-claude-cloud: #7dd3fc;
|
|
298
|
+
/* DeepSeek's blue, toward indigo so it reads apart from Copilot's. */
|
|
299
|
+
--provider-deepseek: #8b9dff;
|
|
300
|
+
/* Claude on an API key: gold, beside the subscription's terracotta. */
|
|
301
|
+
--provider-claude-api: #e3b341;
|
|
302
|
+
/* The usage charts' series, in a fixed order (blue, orange, green,
|
|
303
|
+
amber; grey for "other"), stepped for the dark surface and checked
|
|
304
|
+
for colour-vision deficiency and contrast against it. */
|
|
305
|
+
--plan-usage-series-1: #3987e5;
|
|
306
|
+
--plan-usage-series-2: #d95926;
|
|
307
|
+
--plan-usage-series-3: #199e70;
|
|
308
|
+
--plan-usage-series-4: #c98500;
|
|
309
|
+
--plan-usage-series-5: #8b949e;
|
|
298
310
|
--viewport-top-offset: 0px;
|
|
299
311
|
--header-safe-top: max(env(safe-area-inset-top, 0px), var(--viewport-top-offset, 0px));
|
|
300
312
|
--chat-header-top-base-desktop: 14px;
|
|
@@ -359,6 +371,14 @@
|
|
|
359
371
|
--provider-cursor: #6b3fd4;
|
|
360
372
|
--provider-grok: #a21caf;
|
|
361
373
|
--provider-claude-cloud: #0369a1;
|
|
374
|
+
--provider-deepseek: #3b5bdb;
|
|
375
|
+
--provider-claude-api: #8a6100;
|
|
376
|
+
/* The same series order, stepped for the white surface. */
|
|
377
|
+
--plan-usage-series-1: #2a78d6;
|
|
378
|
+
--plan-usage-series-2: #eb6834;
|
|
379
|
+
--plan-usage-series-3: #1baf7a;
|
|
380
|
+
--plan-usage-series-4: #eda100;
|
|
381
|
+
--plan-usage-series-5: #656d76;
|
|
362
382
|
--inline-code-bg: #eff1f3;
|
|
363
383
|
}
|
|
364
384
|
:root {
|
|
@@ -987,7 +1007,9 @@
|
|
|
987
1007
|
.conv-provider-indicator[data-provider="claude"] { color: var(--provider-claude); }
|
|
988
1008
|
.conv-provider-indicator[data-provider="cursor"] { color: var(--provider-cursor); }
|
|
989
1009
|
.conv-provider-indicator[data-provider="grok"] { color: var(--provider-grok); }
|
|
1010
|
+
.conv-provider-indicator[data-provider="deepseek"] { color: var(--provider-deepseek); }
|
|
990
1011
|
.conv-provider-indicator[data-provider="claude-cloud"] { color: var(--provider-claude-cloud); }
|
|
1012
|
+
.conv-provider-indicator[data-provider="claude-api"] { color: var(--provider-claude-api); }
|
|
991
1013
|
.conv-item.worker-ui-offline .conv-title,
|
|
992
1014
|
#chat-title[data-ui-state="offline"] { color: var(--conversation-title-offline); }
|
|
993
1015
|
/* Ready and idle: the darker green. Ready and working (a turn or
|
|
@@ -1455,6 +1477,112 @@
|
|
|
1455
1477
|
font-size: var(--fs-meta-md);
|
|
1456
1478
|
width: min(260px, 100%);
|
|
1457
1479
|
}
|
|
1480
|
+
/* The DeepSeek tab's key, base URL and monthly budget take the text
|
|
1481
|
+
inputs' look (an input left to the browser is a white box in dark). */
|
|
1482
|
+
#settings-provider-panel-deepseek input[type="password"],
|
|
1483
|
+
#settings-provider-panel-deepseek input[type="url"],
|
|
1484
|
+
#settings-provider-panel-deepseek input[type="number"] {
|
|
1485
|
+
background: var(--bg);
|
|
1486
|
+
color: var(--text);
|
|
1487
|
+
border: 1px solid var(--border);
|
|
1488
|
+
border-radius: 6px;
|
|
1489
|
+
padding: 6px 12px;
|
|
1490
|
+
outline: none;
|
|
1491
|
+
font-size: var(--fs-meta-md);
|
|
1492
|
+
width: min(360px, 100%);
|
|
1493
|
+
}
|
|
1494
|
+
/* A select is as wide as its longest option (the commit attribution's
|
|
1495
|
+
runs to ~290 px): kept inside the panel on a narrow phone. */
|
|
1496
|
+
#settings-provider-panel-deepseek select {
|
|
1497
|
+
max-width: 100%;
|
|
1498
|
+
}
|
|
1499
|
+
/* Its links in the accent colour, as the usage card's: the browser's own
|
|
1500
|
+
blue is unreadable on the dark panel. */
|
|
1501
|
+
#settings-provider-panel-claude-api .settings-help a { color: var(--accent); text-decoration: none; }
|
|
1502
|
+
#settings-provider-panel-claude-api .settings-help a:hover { text-decoration: underline; }
|
|
1503
|
+
/* The Claude API tab's key and cap: the same text-input look. */
|
|
1504
|
+
#settings-provider-panel-claude-api input[type="password"],
|
|
1505
|
+
#settings-provider-panel-claude-api input[type="number"] {
|
|
1506
|
+
background: var(--bg);
|
|
1507
|
+
color: var(--text);
|
|
1508
|
+
border: 1px solid var(--border);
|
|
1509
|
+
border-radius: 6px;
|
|
1510
|
+
padding: 6px 12px;
|
|
1511
|
+
outline: none;
|
|
1512
|
+
font-size: var(--fs-meta-md);
|
|
1513
|
+
width: min(360px, 100%);
|
|
1514
|
+
}
|
|
1515
|
+
#settings-provider-panel-claude-api select {
|
|
1516
|
+
max-width: 100%;
|
|
1517
|
+
}
|
|
1518
|
+
/* A cap the tab refuses: said under the field, which is marked too. */
|
|
1519
|
+
/* Two classes: .settings-help, later in this sheet, would grey it out. */
|
|
1520
|
+
.settings-help.claude-api-field-error { color: var(--danger); }
|
|
1521
|
+
.settings-help.claude-api-field-error[hidden] { display: none; }
|
|
1522
|
+
/* The native spin buttons of a number field follow the theme: light ones
|
|
1523
|
+
on a dark field read as a glitch. */
|
|
1524
|
+
:root:not([data-theme="light"]) input[type="number"] { color-scheme: dark; }
|
|
1525
|
+
#settings-provider-panel-claude-api input[aria-invalid="true"] { border-color: var(--danger); }
|
|
1526
|
+
/* Three buttons: they wrap on a phone instead of pushing the panel wide. */
|
|
1527
|
+
.claude-api-actions {
|
|
1528
|
+
display: flex;
|
|
1529
|
+
flex-wrap: wrap;
|
|
1530
|
+
gap: 8px;
|
|
1531
|
+
margin-top: 8px;
|
|
1532
|
+
}
|
|
1533
|
+
.claude-api-shared-note .summary-btn {
|
|
1534
|
+
margin-left: 6px;
|
|
1535
|
+
padding: 2px 10px;
|
|
1536
|
+
}
|
|
1537
|
+
/* "No longer used" under the starting balance: the warning look of the
|
|
1538
|
+
composer's model banner, apart from the help text above it. */
|
|
1539
|
+
#deepseek-starting-balance-status {
|
|
1540
|
+
background: var(--bg2);
|
|
1541
|
+
background: color-mix(in srgb, var(--warn) 14%, var(--bg2));
|
|
1542
|
+
border: 1px solid var(--warn);
|
|
1543
|
+
color: var(--warn);
|
|
1544
|
+
border-radius: 8px;
|
|
1545
|
+
padding: 6px 10px;
|
|
1546
|
+
margin-top: 6px;
|
|
1547
|
+
}
|
|
1548
|
+
/* Import usage export: the two file pickers in the panel's colours, their
|
|
1549
|
+
button drawn like the panel's other buttons. */
|
|
1550
|
+
.deepseek-usage-import {
|
|
1551
|
+
margin-top: 14px;
|
|
1552
|
+
padding-top: 12px;
|
|
1553
|
+
border-top: 1px solid var(--border);
|
|
1554
|
+
display: flex;
|
|
1555
|
+
flex-direction: column;
|
|
1556
|
+
gap: 6px;
|
|
1557
|
+
min-width: 0;
|
|
1558
|
+
}
|
|
1559
|
+
.deepseek-usage-import-files {
|
|
1560
|
+
display: grid;
|
|
1561
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
1562
|
+
gap: 6px 10px;
|
|
1563
|
+
align-items: center;
|
|
1564
|
+
}
|
|
1565
|
+
.deepseek-usage-import input[type="file"] {
|
|
1566
|
+
color: var(--text);
|
|
1567
|
+
font-size: var(--fs-meta-md);
|
|
1568
|
+
min-width: 0;
|
|
1569
|
+
max-width: 100%;
|
|
1570
|
+
}
|
|
1571
|
+
.deepseek-usage-import input[type="file"]::file-selector-button {
|
|
1572
|
+
border: 1px solid var(--border);
|
|
1573
|
+
background: var(--bg2);
|
|
1574
|
+
color: var(--text);
|
|
1575
|
+
border-radius: 8px;
|
|
1576
|
+
height: 30px;
|
|
1577
|
+
padding: 0 10px;
|
|
1578
|
+
margin-right: 8px;
|
|
1579
|
+
font: inherit;
|
|
1580
|
+
cursor: pointer;
|
|
1581
|
+
}
|
|
1582
|
+
.deepseek-usage-import-status:empty { display: none; }
|
|
1583
|
+
@media (max-width: 520px) {
|
|
1584
|
+
.deepseek-usage-import-files { grid-template-columns: minmax(0, 1fr); }
|
|
1585
|
+
}
|
|
1458
1586
|
.settings-row input[type="checkbox"] {
|
|
1459
1587
|
width: 18px;
|
|
1460
1588
|
height: 18px;
|
|
@@ -1476,9 +1604,9 @@
|
|
|
1476
1604
|
font-size: var(--fs-meta-sm);
|
|
1477
1605
|
line-height: 1.35;
|
|
1478
1606
|
}
|
|
1479
|
-
.settings-provider-status[data-state="active"] { color:
|
|
1607
|
+
.settings-provider-status[data-state="active"] { color: var(--success); }
|
|
1480
1608
|
.settings-provider-status[data-state="saved"] { color: var(--accent); }
|
|
1481
|
-
.settings-provider-status[data-state="error"] { color:
|
|
1609
|
+
.settings-provider-status[data-state="error"] { color: var(--danger); }
|
|
1482
1610
|
/* These rows are laid out as flex and hidden from script via `.hidden = true`.
|
|
1483
1611
|
Their `display: flex` lives HERE rather than in a style="" attribute on
|
|
1484
1612
|
purpose: an inline declaration outranks every selector-based rule (short of
|
|
@@ -2863,6 +2991,25 @@
|
|
|
2863
2991
|
padding: 2px 0;
|
|
2864
2992
|
}
|
|
2865
2993
|
.plan-usage-details summary:hover { color: var(--text); }
|
|
2994
|
+
/* A card's figures (plan-usage-view.mjs renderFact): label and hint on the
|
|
2995
|
+
left, the value on the right, one row each. */
|
|
2996
|
+
.plan-usage-facts {
|
|
2997
|
+
display: grid;
|
|
2998
|
+
gap: 8px;
|
|
2999
|
+
margin: 12px 0 0;
|
|
3000
|
+
padding-top: 10px;
|
|
3001
|
+
border-top: 1px solid var(--border);
|
|
3002
|
+
}
|
|
3003
|
+
.plan-usage-fact {
|
|
3004
|
+
display: flex;
|
|
3005
|
+
align-items: baseline;
|
|
3006
|
+
justify-content: space-between;
|
|
3007
|
+
gap: 12px;
|
|
3008
|
+
font-size: 0.84rem;
|
|
3009
|
+
}
|
|
3010
|
+
.plan-usage-fact dt { display: flex; flex-direction: column; gap: 1px; min-width: 0; font-weight: 500; }
|
|
3011
|
+
.plan-usage-fact-hint { font-size: 0.72rem; font-weight: 400; color: var(--muted); }
|
|
3012
|
+
.plan-usage-fact dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
2866
3013
|
.plan-usage-detail-note {
|
|
2867
3014
|
font-size: 0.74rem;
|
|
2868
3015
|
color: var(--muted);
|
|
@@ -2872,14 +3019,22 @@
|
|
|
2872
3019
|
.plan-usage-detail-table { width: 100%; border-collapse: collapse; margin-top: 6px; }
|
|
2873
3020
|
.plan-usage-detail-table td { padding: 5px 2px; border-bottom: 1px solid var(--border); }
|
|
2874
3021
|
.plan-usage-detail-table tr:last-child td { border-bottom: none; }
|
|
2875
|
-
|
|
2876
|
-
|
|
3022
|
+
/* The label cell stays a table cell (a flex cell does not stretch to the
|
|
3023
|
+
row, and its border then steps against the value's): the hint goes
|
|
3024
|
+
under the label as a block. */
|
|
3025
|
+
.plan-usage-detail-hint { display: block; margin-top: 2px; font-size: 0.7rem; color: var(--muted); }
|
|
2877
3026
|
.plan-usage-detail-value {
|
|
2878
3027
|
text-align: right;
|
|
2879
3028
|
font-variant-numeric: tabular-nums;
|
|
2880
3029
|
white-space: nowrap;
|
|
2881
3030
|
vertical-align: top;
|
|
2882
3031
|
}
|
|
3032
|
+
/* On a phone a long value (tokens by model) wraps instead of pushing the
|
|
3033
|
+
card wider than the screen. */
|
|
3034
|
+
@media (max-width: 520px) {
|
|
3035
|
+
.plan-usage-details .plan-usage-detail-value { white-space: normal; }
|
|
3036
|
+
}
|
|
3037
|
+
.plan-usage-detail-part { white-space: nowrap; }
|
|
2883
3038
|
.plan-usage-card-foot {
|
|
2884
3039
|
display: flex;
|
|
2885
3040
|
align-items: center;
|
|
@@ -2892,6 +3047,145 @@
|
|
|
2892
3047
|
}
|
|
2893
3048
|
.plan-usage-link { color: var(--accent); text-decoration: none; }
|
|
2894
3049
|
.plan-usage-link:hover { text-decoration: underline; }
|
|
3050
|
+
/* Usage charts (plan-usage-view.mjs renderChart): stacked columns and
|
|
3051
|
+
bars from the theme's series colours; text stays in the text colours. */
|
|
3052
|
+
.plan-usage-chart {
|
|
3053
|
+
margin: 12px 0 0;
|
|
3054
|
+
padding-top: 10px;
|
|
3055
|
+
border-top: 1px solid var(--border);
|
|
3056
|
+
}
|
|
3057
|
+
.plan-usage-chart-head {
|
|
3058
|
+
display: flex;
|
|
3059
|
+
align-items: baseline;
|
|
3060
|
+
justify-content: space-between;
|
|
3061
|
+
gap: 10px;
|
|
3062
|
+
flex-wrap: wrap;
|
|
3063
|
+
margin-bottom: 8px;
|
|
3064
|
+
}
|
|
3065
|
+
.plan-usage-chart-label { font-size: 0.88rem; font-weight: 600; }
|
|
3066
|
+
.plan-usage-chart-summary { font-size: 0.82rem; color: var(--text); font-variant-numeric: tabular-nums; }
|
|
3067
|
+
.plan-usage-chart-columns {
|
|
3068
|
+
position: relative;
|
|
3069
|
+
display: grid;
|
|
3070
|
+
grid-template-columns: repeat(var(--plan-usage-columns, 30), minmax(0, 1fr));
|
|
3071
|
+
gap: 2px;
|
|
3072
|
+
height: 72px;
|
|
3073
|
+
padding-bottom: 18px;
|
|
3074
|
+
border-bottom: 1px solid var(--border);
|
|
3075
|
+
}
|
|
3076
|
+
.plan-usage-chart-col {
|
|
3077
|
+
position: relative;
|
|
3078
|
+
display: flex;
|
|
3079
|
+
align-items: flex-end;
|
|
3080
|
+
min-width: 0;
|
|
3081
|
+
height: 100%;
|
|
3082
|
+
border-radius: 2px;
|
|
3083
|
+
outline: none;
|
|
3084
|
+
}
|
|
3085
|
+
.plan-usage-chart-col:hover, .plan-usage-chart-col:focus-visible { background: var(--bg3); }
|
|
3086
|
+
.plan-usage-chart-col:focus-visible { box-shadow: 0 0 0 2px var(--accent); }
|
|
3087
|
+
.plan-usage-chart-stack {
|
|
3088
|
+
display: flex;
|
|
3089
|
+
flex-direction: column-reverse;
|
|
3090
|
+
width: 100%;
|
|
3091
|
+
height: 100%;
|
|
3092
|
+
justify-content: flex-start;
|
|
3093
|
+
}
|
|
3094
|
+
.plan-usage-chart-col-empty .plan-usage-chart-stack::before {
|
|
3095
|
+
content: '';
|
|
3096
|
+
display: block;
|
|
3097
|
+
height: 2px;
|
|
3098
|
+
background: var(--border);
|
|
3099
|
+
}
|
|
3100
|
+
.plan-usage-chart-columns .plan-usage-chart-seg {
|
|
3101
|
+
display: block;
|
|
3102
|
+
width: 100%;
|
|
3103
|
+
min-height: 2px;
|
|
3104
|
+
margin-top: 2px;
|
|
3105
|
+
}
|
|
3106
|
+
.plan-usage-chart-columns .plan-usage-chart-seg:last-child { border-radius: 4px 4px 0 0; }
|
|
3107
|
+
.plan-usage-chart-axis {
|
|
3108
|
+
position: absolute;
|
|
3109
|
+
left: 0;
|
|
3110
|
+
right: 0;
|
|
3111
|
+
bottom: 0;
|
|
3112
|
+
display: flex;
|
|
3113
|
+
justify-content: space-between;
|
|
3114
|
+
font-size: 0.68rem;
|
|
3115
|
+
color: var(--muted);
|
|
3116
|
+
}
|
|
3117
|
+
.plan-usage-chart-bars { display: flex; flex-direction: column; gap: 8px; }
|
|
3118
|
+
.plan-usage-chart-row {
|
|
3119
|
+
position: relative;
|
|
3120
|
+
display: grid;
|
|
3121
|
+
grid-template-columns: minmax(80px, 1fr) 3fr auto;
|
|
3122
|
+
align-items: center;
|
|
3123
|
+
gap: 10px;
|
|
3124
|
+
font-size: 0.82rem;
|
|
3125
|
+
border-radius: 4px;
|
|
3126
|
+
outline: none;
|
|
3127
|
+
}
|
|
3128
|
+
.plan-usage-chart-row:focus-visible { box-shadow: 0 0 0 2px var(--accent); }
|
|
3129
|
+
/* A model's name wraps on a narrow screen rather than being cut. */
|
|
3130
|
+
.plan-usage-chart-row-label { min-width: 0; line-height: 1.25; overflow-wrap: anywhere; }
|
|
3131
|
+
.plan-usage-chart-row-value { font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
3132
|
+
.plan-usage-chart-bar {
|
|
3133
|
+
display: flex;
|
|
3134
|
+
gap: 2px;
|
|
3135
|
+
height: 14px;
|
|
3136
|
+
border-radius: 4px;
|
|
3137
|
+
overflow: hidden;
|
|
3138
|
+
background: var(--bg3);
|
|
3139
|
+
}
|
|
3140
|
+
.plan-usage-chart-bar .plan-usage-chart-seg { display: block; height: 100%; min-width: 2px; }
|
|
3141
|
+
.plan-usage-chart-bar .plan-usage-chart-seg:first-child { border-radius: 4px 0 0 4px; }
|
|
3142
|
+
.plan-usage-chart-bar .plan-usage-chart-seg:last-child { border-radius: 0 4px 4px 0; }
|
|
3143
|
+
.plan-usage-chart-tip {
|
|
3144
|
+
display: none;
|
|
3145
|
+
position: absolute;
|
|
3146
|
+
bottom: calc(100% + 4px);
|
|
3147
|
+
left: 50%;
|
|
3148
|
+
transform: translateX(-50%);
|
|
3149
|
+
z-index: 2;
|
|
3150
|
+
max-width: 260px;
|
|
3151
|
+
padding: 4px 8px;
|
|
3152
|
+
border: 1px solid var(--border);
|
|
3153
|
+
border-radius: 6px;
|
|
3154
|
+
background: var(--bg2);
|
|
3155
|
+
color: var(--text);
|
|
3156
|
+
font-size: 0.74rem;
|
|
3157
|
+
line-height: 1.4;
|
|
3158
|
+
white-space: normal;
|
|
3159
|
+
text-align: center;
|
|
3160
|
+
pointer-events: none;
|
|
3161
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
|
|
3162
|
+
}
|
|
3163
|
+
.plan-usage-chart-col:hover .plan-usage-chart-tip,
|
|
3164
|
+
.plan-usage-chart-col:focus .plan-usage-chart-tip,
|
|
3165
|
+
.plan-usage-chart-row:hover .plan-usage-chart-tip,
|
|
3166
|
+
.plan-usage-chart-row:focus .plan-usage-chart-tip { display: block; }
|
|
3167
|
+
.plan-usage-chart-col .plan-usage-chart-tip { width: 180px; }
|
|
3168
|
+
.plan-usage-chart-col:first-child .plan-usage-chart-tip,
|
|
3169
|
+
.plan-usage-chart-col:nth-child(-n+6) .plan-usage-chart-tip { left: 0; transform: none; text-align: left; }
|
|
3170
|
+
.plan-usage-chart-col:nth-last-child(-n+7):not(.plan-usage-chart-axis) .plan-usage-chart-tip { left: auto; right: 0; transform: none; text-align: right; }
|
|
3171
|
+
.plan-usage-chart-legend {
|
|
3172
|
+
display: flex;
|
|
3173
|
+
flex-wrap: wrap;
|
|
3174
|
+
gap: 4px 14px;
|
|
3175
|
+
margin: 8px 0 0;
|
|
3176
|
+
padding: 0;
|
|
3177
|
+
list-style: none;
|
|
3178
|
+
font-size: 0.74rem;
|
|
3179
|
+
color: var(--muted);
|
|
3180
|
+
}
|
|
3181
|
+
.plan-usage-chart-legend li { display: inline-flex; align-items: center; gap: 5px; }
|
|
3182
|
+
.plan-usage-chart-swatch { display: inline-block; width: 10px; height: 10px; border-radius: 3px; }
|
|
3183
|
+
/* A table wider than a narrow screen scrolls inside the card. */
|
|
3184
|
+
.plan-usage-chart-table { margin-top: 6px; font-size: 0.78rem; max-width: 100%; overflow-x: auto; }
|
|
3185
|
+
.plan-usage-chart-table summary { cursor: pointer; color: var(--muted); padding: 2px 0; }
|
|
3186
|
+
.plan-usage-chart-table summary:hover { color: var(--text); }
|
|
3187
|
+
.plan-usage-chart-table th { text-align: left; font-weight: 500; color: var(--muted); padding: 4px 2px; border-bottom: 1px solid var(--border); }
|
|
3188
|
+
.plan-usage-chart-table thead th:not(:first-child) { text-align: right; }
|
|
2895
3189
|
/* About OAR: the plan-usage cards and tables, stacked. */
|
|
2896
3190
|
.about-modal { display: flex; flex-direction: column; gap: 12px; }
|
|
2897
3191
|
.about-section .plan-usage-card-head { margin-bottom: 4px; }
|
|
@@ -3083,6 +3377,9 @@
|
|
|
3083
3377
|
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
|
|
3084
3378
|
pointer-events: none;
|
|
3085
3379
|
}
|
|
3380
|
+
/* The light theme's --warn on the toast's tinted ground is about 3.6:1;
|
|
3381
|
+
its text takes a darker amber (about 5.6:1), the border keeps --warn. */
|
|
3382
|
+
:root[data-theme="light"] #relay-toast { color: #7d4e00; }
|
|
3086
3383
|
#relay-toast.visible { display: block; }
|
|
3087
3384
|
#relay-toast.has-action { pointer-events: auto; }
|
|
3088
3385
|
.relay-toast-action {
|
|
@@ -3186,7 +3483,50 @@
|
|
|
3186
3483
|
#model-lock-note[data-provider="claude"] { color: var(--provider-claude); }
|
|
3187
3484
|
#model-lock-note[data-provider="cursor"] { color: var(--provider-cursor); }
|
|
3188
3485
|
#model-lock-note[data-provider="grok"] { color: var(--provider-grok); }
|
|
3486
|
+
#model-lock-note[data-provider="deepseek"] { color: var(--provider-deepseek); }
|
|
3189
3487
|
#model-lock-note[data-provider="claude-cloud"] { color: var(--provider-claude-cloud); }
|
|
3488
|
+
#model-lock-note[data-provider="claude-api"] { color: var(--provider-claude-api); }
|
|
3489
|
+
/* The billing toggle of a Claude chat: a two-way switch in the provider
|
|
3490
|
+
colours, with what a pending switch waits for beside it. It wraps under
|
|
3491
|
+
the switch on a phone instead of widening the composer. */
|
|
3492
|
+
.claude-billing-toggle {
|
|
3493
|
+
display: flex;
|
|
3494
|
+
flex-wrap: wrap;
|
|
3495
|
+
align-items: center;
|
|
3496
|
+
gap: 4px 10px;
|
|
3497
|
+
margin-bottom: 6px;
|
|
3498
|
+
min-width: 0;
|
|
3499
|
+
font-size: var(--fs-meta-sm);
|
|
3500
|
+
}
|
|
3501
|
+
.claude-billing-toggle[hidden] { display: none; }
|
|
3502
|
+
.claude-billing-label { color: var(--muted); font-weight: 600; }
|
|
3503
|
+
.claude-billing-options {
|
|
3504
|
+
display: inline-flex;
|
|
3505
|
+
border: 1px solid var(--border);
|
|
3506
|
+
border-radius: 999px;
|
|
3507
|
+
overflow: hidden;
|
|
3508
|
+
flex: 0 0 auto;
|
|
3509
|
+
}
|
|
3510
|
+
.claude-billing-option {
|
|
3511
|
+
border: 0;
|
|
3512
|
+
background: transparent;
|
|
3513
|
+
color: var(--muted);
|
|
3514
|
+
font: inherit;
|
|
3515
|
+
font-weight: 600;
|
|
3516
|
+
padding: 3px 12px;
|
|
3517
|
+
min-height: 26px;
|
|
3518
|
+
cursor: pointer;
|
|
3519
|
+
}
|
|
3520
|
+
.claude-billing-option + .claude-billing-option { border-left: 1px solid var(--border); }
|
|
3521
|
+
.claude-billing-option[aria-pressed="true"] { cursor: default; color: var(--bg); }
|
|
3522
|
+
.claude-billing-option[aria-pressed="true"][data-billing="claude"] { background: var(--provider-claude); }
|
|
3523
|
+
.claude-billing-option[aria-pressed="true"][data-billing="claude-api"] { background: var(--provider-claude-api); }
|
|
3524
|
+
.claude-billing-option[aria-disabled="true"] { opacity: 0.55; cursor: not-allowed; }
|
|
3525
|
+
.claude-billing-option:not([aria-pressed="true"]):not([aria-disabled="true"]):hover { color: var(--text); }
|
|
3526
|
+
.claude-billing-option:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
|
3527
|
+
.claude-billing-status { flex: 1 1 14em; min-width: 0; color: var(--muted); line-height: 1.4; }
|
|
3528
|
+
.claude-billing-status:empty { display: none; }
|
|
3529
|
+
.claude-billing-status[data-kind="warning"] { color: var(--warn); }
|
|
3190
3530
|
/* Claude Cloud conversations (claude-cloud-conversation-ui.js): where the
|
|
3191
3531
|
session runs and what it pushed, above the composer like the lock note.
|
|
3192
3532
|
The items wrap instead of scrolling, so a long branch name can never push
|
|
@@ -4092,6 +4432,7 @@
|
|
|
4092
4432
|
body.shared-reader-mode #reasoning-effort-select,
|
|
4093
4433
|
body.shared-reader-mode #send-btn,
|
|
4094
4434
|
body.shared-reader-mode #model-lock-note,
|
|
4435
|
+
body.shared-reader-mode #claude-billing-toggle,
|
|
4095
4436
|
body.shared-reader-mode #cloud-session-line,
|
|
4096
4437
|
body.shared-reader-mode #model-pricing-details {
|
|
4097
4438
|
display: none !important;
|
|
@@ -4578,6 +4919,14 @@
|
|
|
4578
4919
|
min-height: 42px;
|
|
4579
4920
|
margin-top: 8px;
|
|
4580
4921
|
}
|
|
4922
|
+
/* The selects take the inputs' look too: left to the browser they stay a
|
|
4923
|
+
white box with black text in the dark theme. */
|
|
4924
|
+
#new-conversation-provider-select,
|
|
4925
|
+
#new-conversation-model-select,
|
|
4926
|
+
#new-conversation-reasoning-select,
|
|
4927
|
+
#new-conversation-context-select,
|
|
4928
|
+
#new-conversation-size-select,
|
|
4929
|
+
#new-conversation-cwd-select,
|
|
4581
4930
|
#new-conversation-cwd-manual,
|
|
4582
4931
|
#new-conversation-cloud-repo,
|
|
4583
4932
|
#new-conversation-cloud-branch {
|
|
@@ -4589,6 +4938,18 @@
|
|
|
4589
4938
|
color: var(--text);
|
|
4590
4939
|
}
|
|
4591
4940
|
#new-conversation-cwd-manual[hidden] { display: none; }
|
|
4941
|
+
/* With the inputs' colours a disabled select (the read-only context row,
|
|
4942
|
+
the effort before a model is chosen) would look editable: dimmed as in
|
|
4943
|
+
the composer. */
|
|
4944
|
+
#new-conversation-provider-select:disabled,
|
|
4945
|
+
#new-conversation-model-select:disabled,
|
|
4946
|
+
#new-conversation-reasoning-select:disabled,
|
|
4947
|
+
#new-conversation-context-select:disabled,
|
|
4948
|
+
#new-conversation-size-select:disabled,
|
|
4949
|
+
#new-conversation-cwd-select:disabled {
|
|
4950
|
+
opacity: 0.55;
|
|
4951
|
+
cursor: not-allowed;
|
|
4952
|
+
}
|
|
4592
4953
|
#new-conversation-cloud-repo[aria-invalid="true"],
|
|
4593
4954
|
#new-conversation-cloud-branch[aria-invalid="true"] { border-color: var(--danger); }
|
|
4594
4955
|
/* Warning lines under Repository/Branch: what the cloud clone will not see. */
|
|
@@ -4961,6 +5322,13 @@
|
|
|
4961
5322
|
line-height: 1.5;
|
|
4962
5323
|
}
|
|
4963
5324
|
.new-conversation-setup-note[hidden] { display: none; }
|
|
5325
|
+
.new-conversation-refusal { border-left-color: var(--warn); }
|
|
5326
|
+
.new-conversation-refusal-actions {
|
|
5327
|
+
display: flex;
|
|
5328
|
+
flex-wrap: wrap;
|
|
5329
|
+
gap: 8px;
|
|
5330
|
+
margin-top: 8px;
|
|
5331
|
+
}
|
|
4964
5332
|
.summary-close {
|
|
4965
5333
|
width: 32px;
|
|
4966
5334
|
padding: 0;
|
|
@@ -6389,37 +6757,51 @@
|
|
|
6389
6757
|
.conv-item.conv-drag-ghost { margin: -18px 0 0; }
|
|
6390
6758
|
.conv-item.conv-drop-before::before,
|
|
6391
6759
|
.conv-item.conv-drop-after::after { height: 4px; }
|
|
6760
|
+
/* Two rows on a phone: ☰, the title and the buttons on the first; the
|
|
6761
|
+
origin line, the chips and the folder on a second row across the whole
|
|
6762
|
+
header. The title column dissolves (display: contents) so its lines
|
|
6763
|
+
become the header's own flex items; ::after breaks the row after the
|
|
6764
|
+
buttons. The row gap is 0 and the second row's items carry their own
|
|
6765
|
+
top margin, so a header with nothing below the title stays one row. */
|
|
6392
6766
|
#chat-header {
|
|
6393
6767
|
padding: var(--chat-header-top-pad-mobile) 10px 10px;
|
|
6394
|
-
gap:
|
|
6395
|
-
flex-wrap:
|
|
6768
|
+
gap: 0 8px;
|
|
6769
|
+
flex-wrap: wrap;
|
|
6396
6770
|
align-items: center;
|
|
6397
6771
|
position: relative;
|
|
6398
6772
|
z-index: 30;
|
|
6399
6773
|
}
|
|
6400
|
-
#chat-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
align-items: flex-start;
|
|
6406
|
-
gap: 6px;
|
|
6407
|
-
}
|
|
6408
|
-
#chat-title-slot {
|
|
6409
|
-
display: flex;
|
|
6410
|
-
flex-direction: column;
|
|
6411
|
-
flex: 1 1 auto;
|
|
6412
|
-
width: auto;
|
|
6413
|
-
margin: 0;
|
|
6414
|
-
min-width: 0;
|
|
6774
|
+
#chat-header::after {
|
|
6775
|
+
content: '';
|
|
6776
|
+
order: 4;
|
|
6777
|
+
flex: 0 0 100%;
|
|
6778
|
+
height: 0;
|
|
6415
6779
|
}
|
|
6780
|
+
#chat-title-wrap,
|
|
6781
|
+
#chat-title-slot { display: contents; }
|
|
6416
6782
|
#chat-title-primary {
|
|
6417
|
-
|
|
6783
|
+
order: 2;
|
|
6784
|
+
flex: 1 1 0;
|
|
6418
6785
|
min-width: 0;
|
|
6419
6786
|
}
|
|
6787
|
+
#chat-title-origin,
|
|
6788
|
+
#chat-title-relay-events,
|
|
6789
|
+
#chat-title-relay-schedules,
|
|
6790
|
+
#chat-title-cwd,
|
|
6791
|
+
#chat-title-session-usage {
|
|
6792
|
+
order: 5;
|
|
6793
|
+
flex: 0 1 auto;
|
|
6794
|
+
max-width: 100%;
|
|
6795
|
+
margin-top: 4px;
|
|
6796
|
+
}
|
|
6797
|
+
#chat-title-relay-events,
|
|
6798
|
+
#chat-title-relay-schedules { flex-shrink: 0; }
|
|
6799
|
+
#chat-title-relay-events .relay-watch-chip,
|
|
6800
|
+
#chat-title-relay-events .conv-watched-badge,
|
|
6801
|
+
#chat-title-relay-schedules .relay-watch-chip { white-space: nowrap; }
|
|
6802
|
+
#chat-title-primary { display: block; }
|
|
6420
6803
|
#chat-title-cwd {
|
|
6421
6804
|
display: block;
|
|
6422
|
-
margin-top: 1px;
|
|
6423
6805
|
font-size: clamp(0.62rem, 2vw, 0.7rem);
|
|
6424
6806
|
line-height: 1.2;
|
|
6425
6807
|
min-width: 0;
|
|
@@ -6429,7 +6811,6 @@
|
|
|
6429
6811
|
}
|
|
6430
6812
|
#chat-title-session-usage {
|
|
6431
6813
|
display: block;
|
|
6432
|
-
margin-top: 1px;
|
|
6433
6814
|
font-size: clamp(0.62rem, 2vw, 0.7rem);
|
|
6434
6815
|
line-height: 1.2;
|
|
6435
6816
|
min-width: 0;
|
|
@@ -6437,10 +6818,7 @@
|
|
|
6437
6818
|
overflow: hidden;
|
|
6438
6819
|
text-overflow: ellipsis;
|
|
6439
6820
|
}
|
|
6440
|
-
#chat-title-origin {
|
|
6441
|
-
margin-top: 1px;
|
|
6442
|
-
font-size: clamp(0.62rem, 2vw, 0.7rem);
|
|
6443
|
-
}
|
|
6821
|
+
#chat-title-origin { font-size: clamp(0.62rem, 2vw, 0.7rem); }
|
|
6444
6822
|
#chat-header-actions {
|
|
6445
6823
|
margin-left: auto;
|
|
6446
6824
|
}
|
|
@@ -6449,6 +6827,7 @@
|
|
|
6449
6827
|
flex: 0 0 auto;
|
|
6450
6828
|
}
|
|
6451
6829
|
#chat-title-editor {
|
|
6830
|
+
order: 2;
|
|
6452
6831
|
flex: 1 0 100%;
|
|
6453
6832
|
width: 100%;
|
|
6454
6833
|
flex-wrap: wrap;
|
|
@@ -6457,9 +6836,6 @@
|
|
|
6457
6836
|
body.chat-title-editing #chat-header-actions {
|
|
6458
6837
|
align-self: flex-start;
|
|
6459
6838
|
}
|
|
6460
|
-
body.chat-title-editing #chat-title-wrap {
|
|
6461
|
-
z-index: 40;
|
|
6462
|
-
}
|
|
6463
6839
|
body.chat-title-editing #chat-title-editor {
|
|
6464
6840
|
position: fixed;
|
|
6465
6841
|
left: 10px;
|
|
@@ -6504,7 +6880,6 @@
|
|
|
6504
6880
|
color: var(--relay-tone-tunnelled);
|
|
6505
6881
|
text-shadow: 0 0 8px rgba(210, 153, 34, 0.35);
|
|
6506
6882
|
}
|
|
6507
|
-
#chat-title-wrap { order: 2; }
|
|
6508
6883
|
#chat-header-actions { order: 3; }
|
|
6509
6884
|
#chat-title { font-size: clamp(0.92rem, 3vw, 0.98rem); }
|
|
6510
6885
|
#chat-title-input,
|
|
@@ -6777,13 +7152,20 @@
|
|
|
6777
7152
|
flex: 0 0 auto;
|
|
6778
7153
|
min-width: 40px;
|
|
6779
7154
|
}
|
|
6780
|
-
/* About OAR: the same, with Refresh
|
|
6781
|
-
|
|
7155
|
+
/* About OAR, Plan usage and Settings: the same, with Refresh (where
|
|
7156
|
+
there is one) beside the close button, and the title cut with an
|
|
7157
|
+
ellipsis if it must be. */
|
|
7158
|
+
#summary-modal[data-kind="about"] .summary-header-actions,
|
|
7159
|
+
#summary-modal[data-kind="usage"] .summary-header-actions,
|
|
7160
|
+
#settings-modal .summary-header-actions {
|
|
6782
7161
|
width: auto;
|
|
6783
7162
|
flex: 0 0 auto;
|
|
6784
7163
|
}
|
|
6785
7164
|
#summary-modal[data-kind="about"] .summary-header-actions .summary-btn,
|
|
6786
|
-
#summary-modal[data-kind="about"] .summary-header-actions .summary-close
|
|
7165
|
+
#summary-modal[data-kind="about"] .summary-header-actions .summary-close,
|
|
7166
|
+
#summary-modal[data-kind="usage"] .summary-header-actions .summary-btn,
|
|
7167
|
+
#summary-modal[data-kind="usage"] .summary-header-actions .summary-close,
|
|
7168
|
+
#settings-modal .summary-header-actions .summary-close {
|
|
6787
7169
|
flex: 0 0 auto;
|
|
6788
7170
|
min-width: 40px;
|
|
6789
7171
|
}
|
|
@@ -7111,6 +7493,8 @@
|
|
|
7111
7493
|
<div id="model-banner"></div>
|
|
7112
7494
|
<div id="usage-limit-banner" role="status" aria-live="polite" hidden></div>
|
|
7113
7495
|
<div id="model-lock-note" hidden></div>
|
|
7496
|
+
<!-- Subscription | API key in a Claude chat (claude-billing-toggle-ui.js). -->
|
|
7497
|
+
<div id="claude-billing-toggle" class="claude-billing-toggle" hidden></div>
|
|
7114
7498
|
<div id="cloud-session-line" aria-label="Cloud session" hidden></div>
|
|
7115
7499
|
<div id="model-metadata-blocker" aria-live="polite">
|
|
7116
7500
|
<span id="model-metadata-blocker-text">Model metadata is unavailable. Refresh to choose a model and reasoning effort.</span>
|
|
@@ -7580,6 +7964,16 @@
|
|
|
7580
7964
|
tabindex="-1"
|
|
7581
7965
|
data-settings-provider-tab="claude"
|
|
7582
7966
|
>Claude</button>
|
|
7967
|
+
<button
|
|
7968
|
+
id="settings-provider-tab-claude-api"
|
|
7969
|
+
class="settings-tab settings-subtab"
|
|
7970
|
+
type="button"
|
|
7971
|
+
role="tab"
|
|
7972
|
+
aria-selected="false"
|
|
7973
|
+
aria-controls="settings-provider-panel-claude-api"
|
|
7974
|
+
tabindex="-1"
|
|
7975
|
+
data-settings-provider-tab="claude-api"
|
|
7976
|
+
>Claude API</button>
|
|
7583
7977
|
<button
|
|
7584
7978
|
id="settings-provider-tab-claude-cloud"
|
|
7585
7979
|
class="settings-tab settings-subtab"
|
|
@@ -7610,6 +8004,16 @@
|
|
|
7610
8004
|
tabindex="-1"
|
|
7611
8005
|
data-settings-provider-tab="cursor"
|
|
7612
8006
|
>Cursor</button>
|
|
8007
|
+
<button
|
|
8008
|
+
id="settings-provider-tab-deepseek"
|
|
8009
|
+
class="settings-tab settings-subtab"
|
|
8010
|
+
type="button"
|
|
8011
|
+
role="tab"
|
|
8012
|
+
aria-selected="false"
|
|
8013
|
+
aria-controls="settings-provider-panel-deepseek"
|
|
8014
|
+
tabindex="-1"
|
|
8015
|
+
data-settings-provider-tab="deepseek"
|
|
8016
|
+
>DeepSeek</button>
|
|
7613
8017
|
</div>
|
|
7614
8018
|
<div
|
|
7615
8019
|
id="settings-provider-panel-copilot"
|
|
@@ -7804,6 +8208,63 @@
|
|
|
7804
8208
|
<div class="settings-help">Runs Claude conversations through the Claude Agent SDK using the relay host's logged-in Claude credentials (no API key stored). Disabling also rebinds conversations that have not sent a message yet.</div>
|
|
7805
8209
|
</div>
|
|
7806
8210
|
</div>
|
|
8211
|
+
<!-- Claude API (claude-api-settings-ui.js): Claude chats on a Claude Console
|
|
8212
|
+
API key. The key is write-only; the relay never sends it back. -->
|
|
8213
|
+
<div
|
|
8214
|
+
id="settings-provider-panel-claude-api"
|
|
8215
|
+
class="settings-panel"
|
|
8216
|
+
role="tabpanel"
|
|
8217
|
+
tabindex="0"
|
|
8218
|
+
aria-labelledby="settings-provider-tab-claude-api"
|
|
8219
|
+
data-settings-provider-panel="claude-api"
|
|
8220
|
+
hidden
|
|
8221
|
+
>
|
|
8222
|
+
<div class="settings-row settings-row-column" style="margin-top: 12px; margin-bottom: 0;">
|
|
8223
|
+
<label for="claude-api-key-input">Claude API</label>
|
|
8224
|
+
<div class="settings-row" style="width: 100%; margin-bottom: 8px;">
|
|
8225
|
+
<label for="claude-api-enabled-toggle">Enable Claude API for New Chat and the billing switch</label>
|
|
8226
|
+
<input id="claude-api-enabled-toggle" type="checkbox" onchange="toggleClaudeApiProvider(this.checked)" disabled>
|
|
8227
|
+
</div>
|
|
8228
|
+
<input
|
|
8229
|
+
id="claude-api-key-input"
|
|
8230
|
+
type="password"
|
|
8231
|
+
autocomplete="new-password"
|
|
8232
|
+
autocapitalize="off"
|
|
8233
|
+
spellcheck="false"
|
|
8234
|
+
placeholder="Claude Console API key…"
|
|
8235
|
+
aria-label="Claude API key"
|
|
8236
|
+
aria-describedby="claude-api-key-help"
|
|
8237
|
+
>
|
|
8238
|
+
<div id="claude-api-key-help" class="settings-help">An API key from the Claude Console (<a href="https://platform.claude.com/settings/keys" target="_blank" rel="noopener noreferrer">platform.claude.com → API keys</a>). It is stored on the relay host, never sent back to a browser, and used only for Claude API chats and for listing the key's models. Claude login tokens and Admin keys are refused.</div>
|
|
8239
|
+
<label for="claude-api-model-select" style="margin-top: 8px;">Default model</label>
|
|
8240
|
+
<select id="claude-api-model-select" aria-label="Claude API default model"></select>
|
|
8241
|
+
<div id="claude-api-models-status" class="settings-help" hidden></div>
|
|
8242
|
+
<label for="claude-api-monthly-cap-input" style="margin-top: 8px;">Monthly cap in US dollars (optional)</label>
|
|
8243
|
+
<input
|
|
8244
|
+
id="claude-api-monthly-cap-input"
|
|
8245
|
+
type="number"
|
|
8246
|
+
inputmode="decimal"
|
|
8247
|
+
min="0.01"
|
|
8248
|
+
max="1000000"
|
|
8249
|
+
step="0.01"
|
|
8250
|
+
placeholder="No cap"
|
|
8251
|
+
aria-label="Claude API monthly cap in US dollars"
|
|
8252
|
+
aria-describedby="claude-api-monthly-cap-error claude-api-monthly-cap-help"
|
|
8253
|
+
>
|
|
8254
|
+
<div id="claude-api-monthly-cap-error" class="settings-help claude-api-field-error" role="alert" hidden></div>
|
|
8255
|
+
<div id="claude-api-monthly-cap-help" class="settings-help">Once this relay's estimated Claude API spend of the calendar month reaches the cap, new API key turns and switches to the API key are refused; a running turn finishes. Leave it empty for no cap. Spend limits set in the Claude Console apply on top.</div>
|
|
8256
|
+
<div id="claude-api-settings-status" class="settings-help settings-provider-status">Loading Claude API settings…</div>
|
|
8257
|
+
<div class="claude-api-actions">
|
|
8258
|
+
<button id="claude-api-save-btn" type="button" class="summary-btn" onclick="saveClaudeApiSettings()">Save Claude API settings</button>
|
|
8259
|
+
<button id="claude-api-refresh-btn" type="button" class="summary-btn" onclick="refreshClaudeApiModels()" disabled>Refresh models</button>
|
|
8260
|
+
<button id="claude-api-remove-btn" type="button" class="summary-btn" onclick="removeClaudeApiSettings()" disabled>Remove saved key</button>
|
|
8261
|
+
</div>
|
|
8262
|
+
<div class="settings-help">Claude API chats run Claude Code on the relay host like Claude chats, on this key instead of the host's Claude login, and are billed to the Claude Console organisation the key belongs to. A Claude chat can switch between the subscription and the API key from its composer. Credit and spend limits are in the Console: <a href="https://platform.claude.com/settings/billing" target="_blank" rel="noopener noreferrer">billing</a>, <a href="https://platform.claude.com/settings/limits" target="_blank" rel="noopener noreferrer">limits</a>.</div>
|
|
8263
|
+
<div class="settings-help claude-api-shared-note">Commit attribution, the background command limit, the effort levels and the compaction window are shared with Claude chats and set on the Claude tab.
|
|
8264
|
+
<button id="claude-api-open-claude-tab-btn" type="button" class="summary-btn" onclick="openClaudeTabFromClaudeApi()">Claude tab</button>
|
|
8265
|
+
</div>
|
|
8266
|
+
</div>
|
|
8267
|
+
</div>
|
|
7807
8268
|
<!-- Claude Cloud (claude-cloud-settings-ui.js). The login itself is managed on
|
|
7808
8269
|
the Claude tab; this panel only says which account pays. -->
|
|
7809
8270
|
<div
|
|
@@ -8047,6 +8508,104 @@
|
|
|
8047
8508
|
<div class="settings-help">Unlocks the live percentage bars on Check Usage: "Plan usage (total)", "Auto + Composer" and "API". Detected automatically from this machine's Cursor IDE login when one exists; paste the <code>WorkosCursorSessionToken</code> cookie from a logged-in cursor.com tab (DevTools → Application → Cookies) only for headless relay hosts or to use a different account. Headless hosts can also set <code>CURSOR_SESSION_TOKEN</code> in the relay environment. Tokens stay on the relay.</div>
|
|
8048
8509
|
</div>
|
|
8049
8510
|
</div>
|
|
8511
|
+
<div
|
|
8512
|
+
id="settings-provider-panel-deepseek"
|
|
8513
|
+
class="settings-panel"
|
|
8514
|
+
role="tabpanel"
|
|
8515
|
+
tabindex="0"
|
|
8516
|
+
aria-labelledby="settings-provider-tab-deepseek"
|
|
8517
|
+
data-settings-provider-panel="deepseek"
|
|
8518
|
+
hidden
|
|
8519
|
+
>
|
|
8520
|
+
<div class="settings-row settings-row-column" style="margin-top: 12px; margin-bottom: 0;">
|
|
8521
|
+
<label for="deepseek-api-key-input">DeepSeek</label>
|
|
8522
|
+
<div class="settings-row" style="width: 100%; margin-bottom: 8px;">
|
|
8523
|
+
<label for="deepseek-enabled-toggle">Enable DeepSeek for New Chat model selection</label>
|
|
8524
|
+
<input id="deepseek-enabled-toggle" type="checkbox" onchange="toggleDeepSeekProvider(this.checked)" disabled>
|
|
8525
|
+
</div>
|
|
8526
|
+
<input
|
|
8527
|
+
id="deepseek-api-key-input"
|
|
8528
|
+
type="password"
|
|
8529
|
+
autocomplete="off"
|
|
8530
|
+
placeholder="DeepSeek API key…"
|
|
8531
|
+
aria-label="DeepSeek API key"
|
|
8532
|
+
>
|
|
8533
|
+
<label for="deepseek-model-select" style="margin-top: 8px;">Default model</label>
|
|
8534
|
+
<select id="deepseek-model-select" aria-label="DeepSeek default model">
|
|
8535
|
+
<option value="deepseek-flash">deepseek-flash</option>
|
|
8536
|
+
<option value="deepseek-v4-pro">deepseek-v4-pro</option>
|
|
8537
|
+
</select>
|
|
8538
|
+
<label for="deepseek-attribution-select" style="margin-top: 8px;">Commit attribution</label>
|
|
8539
|
+
<select id="deepseek-attribution-select" aria-label="DeepSeek commit attribution">
|
|
8540
|
+
<option value="oar">OAR: Co-authored-by: Open Agent Relay (DeepSeek …)</option>
|
|
8541
|
+
<option value="off">Off: no attribution</option>
|
|
8542
|
+
</select>
|
|
8543
|
+
<label for="deepseek-monthly-budget-input" style="margin-top: 8px;">Monthly budget (optional)</label>
|
|
8544
|
+
<input
|
|
8545
|
+
id="deepseek-monthly-budget-input"
|
|
8546
|
+
type="number"
|
|
8547
|
+
inputmode="decimal"
|
|
8548
|
+
min="0"
|
|
8549
|
+
step="0.01"
|
|
8550
|
+
autocomplete="off"
|
|
8551
|
+
placeholder="No budget bar"
|
|
8552
|
+
aria-label="DeepSeek monthly budget"
|
|
8553
|
+
>
|
|
8554
|
+
<label for="deepseek-monthly-budget-currency-select" style="margin-top: 8px;">Budget currency</label>
|
|
8555
|
+
<select id="deepseek-monthly-budget-currency-select" aria-label="DeepSeek monthly budget currency">
|
|
8556
|
+
<option value="USD">USD ($)</option>
|
|
8557
|
+
<option value="CNY">CNY (¥)</option>
|
|
8558
|
+
<option value="">The balance's currency (may change)</option>
|
|
8559
|
+
</select>
|
|
8560
|
+
<div class="settings-help">The usage card shows this relay's estimated DeepSeek spend of the calendar month (UTC) against the budget, in the budget's currency. The bar changes colour at 50 % and 75 % of the budget and turns red at 90 %. Leave the amount empty for no budget bar.</div>
|
|
8561
|
+
<label for="deepseek-starting-balance-input" style="margin-top: 8px;">Starting balance (optional)</label>
|
|
8562
|
+
<input
|
|
8563
|
+
id="deepseek-starting-balance-input"
|
|
8564
|
+
type="number"
|
|
8565
|
+
inputmode="decimal"
|
|
8566
|
+
min="0"
|
|
8567
|
+
step="0.01"
|
|
8568
|
+
autocomplete="off"
|
|
8569
|
+
placeholder="The balance after the last top-up"
|
|
8570
|
+
aria-label="DeepSeek starting balance"
|
|
8571
|
+
aria-describedby="deepseek-starting-balance-help"
|
|
8572
|
+
>
|
|
8573
|
+
<div id="deepseek-starting-balance-help" class="settings-help">The usage card's balance bar measures what is left against this amount, in the balance's currency, for example your first top-up. Leave it empty to measure from the balance after the last top-up this relay has seen (before it has seen one, the highest balance it has read).</div>
|
|
8574
|
+
<div id="deepseek-starting-balance-status" class="settings-help" hidden></div>
|
|
8575
|
+
<details id="deepseek-advanced" style="margin-top: 8px;">
|
|
8576
|
+
<summary>Advanced</summary>
|
|
8577
|
+
<label for="deepseek-base-url-input" style="margin-top: 8px;">Base URL</label>
|
|
8578
|
+
<input
|
|
8579
|
+
id="deepseek-base-url-input"
|
|
8580
|
+
type="url"
|
|
8581
|
+
autocomplete="off"
|
|
8582
|
+
placeholder="https://api.deepseek.com"
|
|
8583
|
+
aria-label="DeepSeek base URL"
|
|
8584
|
+
>
|
|
8585
|
+
<div class="settings-help">Leave empty for DeepSeek's own API. Another address must use https (http only on this machine, for a local proxy); the key is sent there and nowhere else.</div>
|
|
8586
|
+
</details>
|
|
8587
|
+
<div id="deepseek-settings-status" class="settings-help settings-provider-status">Loading DeepSeek settings…</div>
|
|
8588
|
+
<div style="display: flex; gap: 8px; margin-top: 8px;">
|
|
8589
|
+
<button id="deepseek-save-btn" type="button" class="summary-btn" onclick="saveDeepSeekSettings()">Save DeepSeek settings</button>
|
|
8590
|
+
<button id="deepseek-remove-btn" type="button" class="summary-btn" onclick="removeDeepSeekSettings()">Remove saved key</button>
|
|
8591
|
+
</div>
|
|
8592
|
+
<div class="settings-help">DeepSeek chats run Claude Code on the relay host against DeepSeek's Anthropic-compatible API, with all four modes, tools and question cards. The key is stored on the relay host and never sent to browsers; saving it lists DeepSeek's models. Turns are billed to the DeepSeek account the key belongs to.</div>
|
|
8593
|
+
<div id="deepseek-usage-import" class="deepseek-usage-import" role="group" aria-labelledby="deepseek-usage-import-title">
|
|
8594
|
+
<label id="deepseek-usage-import-title">Import usage export</label>
|
|
8595
|
+
<div class="settings-help">DeepSeek's usage page exports two files for a period, amount and cost. Imported here, their figures for the saved key replace the estimate on the usage card for the days they cover; the other days stay estimated.</div>
|
|
8596
|
+
<div class="deepseek-usage-import-files">
|
|
8597
|
+
<label for="deepseek-usage-amount-input" class="settings-help">Amount file</label>
|
|
8598
|
+
<input id="deepseek-usage-amount-input" type="file" accept=".csv,text/csv" aria-label="DeepSeek usage export: amount file">
|
|
8599
|
+
<label for="deepseek-usage-cost-input" class="settings-help">Cost file</label>
|
|
8600
|
+
<input id="deepseek-usage-cost-input" type="file" accept=".csv,text/csv" aria-label="DeepSeek usage export: cost file">
|
|
8601
|
+
</div>
|
|
8602
|
+
<div>
|
|
8603
|
+
<button id="deepseek-usage-import-btn" type="button" class="summary-btn" onclick="importDeepSeekUsageExport()">Import usage export</button>
|
|
8604
|
+
</div>
|
|
8605
|
+
<div id="deepseek-usage-import-status" class="settings-help settings-provider-status deepseek-usage-import-status" role="status" aria-live="polite"></div>
|
|
8606
|
+
</div>
|
|
8607
|
+
</div>
|
|
8608
|
+
</div>
|
|
8050
8609
|
</div>
|
|
8051
8610
|
<div
|
|
8052
8611
|
id="settings-panel-relays"
|
|
@@ -8383,6 +8942,8 @@
|
|
|
8383
8942
|
Settings is the main button; Start stays as "anyway", because the
|
|
8384
8943
|
Copilot signal can be wrong. -->
|
|
8385
8944
|
<div id="new-conversation-setup-note" class="new-conversation-setup-note" role="status" hidden></div>
|
|
8945
|
+
<!-- Claude API refused to start the chat (journal-view.js). -->
|
|
8946
|
+
<div id="new-conversation-refusal" class="new-conversation-setup-note new-conversation-refusal" role="alert" hidden></div>
|
|
8386
8947
|
<div class="summary-modal-actions">
|
|
8387
8948
|
<button type="button" class="summary-btn" onclick="closeNewConversationModelModal()">Cancel</button>
|
|
8388
8949
|
<button id="new-conversation-open-settings" type="button" class="summary-btn summary-btn-accent" hidden>Open Settings → Providers</button>
|