Skip to content

Commit 661cfec

Browse files
committed
feat: align bridge with CommandCode 1.14.0
1 parent e110c98 commit 661cfec

22 files changed

Lines changed: 1039 additions & 416 deletions

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ COMMANDCODE_API_BASE=https://api.commandcode.ai
1515
COMMANDCODE_DEFAULT_MODEL=deepseek/deepseek-v4-pro
1616
COMMANDCODE_ALLOWED_MODELS=deepseek/deepseek-v4-pro,deepseek/deepseek-v4-flash
1717
COMMANDCODE_ALLOW_UNKNOWN_MODELS=false
18-
COMMANDCODE_CLI_VERSION=1.3.1
18+
COMMANDCODE_CLI_VERSION=1.14.0
1919
COMMANDCODE_TIMEOUT_MS=300000
2020
COMMANDCODE_EMPTY_VISIBLE_RESPONSE_POLICY=error_on_length
2121

.prettierignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ coverage/
44
.vitest-tmp/
55
release/
66
.internal/
7+
.commandcode/
8+
.omo/
79
*.tgz

DESIGN.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Dashboard Design Contract
2+
3+
The dashboard follows the Emil editorial system. This document is the frontend contract for future dashboard changes; implementation changes must preserve the existing IDs, translations, API behavior, and save/restart flow.
4+
5+
## Color tokens
6+
7+
Use the shared CSS custom properties rather than introducing local theme colors.
8+
9+
| Token | Value | Role |
10+
| ---------- | --------- | ---------------------------------------------------------- |
11+
| `--ink` | `#28231f` | Hero, primary text, and decisive controls |
12+
| `--canvas` | `#f1ede5` | Page canvas |
13+
| `--paper` | `#fffdf8` | Cards, fields, and fold surfaces |
14+
| `--rust` | `#9f4d2e` | Editorial accents, destructive actions, disclosure markers |
15+
| `--teal` | `#1f6f78` | Selected and positive interactive states |
16+
| `--gold` | `#b57920` | Metadata, focus rings, and restart emphasis |
17+
18+
Rules use `#d9d0c4`; muted copy uses `#6d665e`. Shadows stay restrained at `0 14px 34px rgba(65,49,35,.10)`. Do not add gradients, generic purple, or decorative pill collections.
19+
20+
## Typography
21+
22+
- Body, headings, and editorial hierarchy use Georgia with Times New Roman and `serif` fallbacks.
23+
- Interface metadata, compact labels, counters, and controls use the system sans stack.
24+
- Machine values and state tokens use the monospace stack.
25+
- Headings use tight tracking and compact line height; labels use small uppercase sans text.
26+
27+
## Spacing and materials
28+
29+
- The warm canvas surrounds a centered page with a maximum width of 1120px.
30+
- The dark hero includes a low-opacity inset hairline frame. Its metadata uses gold and rust accents.
31+
- Cards use the paper surface, a dark top rule, and one restrained shadow. Avoid nested ornamental containers.
32+
- Use the 4/8/12/16/24/32px rhythm. Dense controls may use the smaller steps; primary sections use 16px or more.
33+
- Controls are square-edged editorial elements, not pills. Borders and spacing establish hierarchy before color.
34+
35+
## Provider folds
36+
37+
- Models are grouped by their provider and provider groups are sorted alphabetically with a stable explicit comparator.
38+
- Every provider is a native `<details class="provider-fold">` with a native `<summary>` and no `open` attribute, so initial state is closed.
39+
- Summary text is visibly `Provider (enabled/total)` and exposes `data-provider`, `data-enabled`, and `data-total` for machine consumption.
40+
- The rust plus/minus marker must clearly communicate collapsed and expanded state.
41+
- Model IDs, notes, and toggles remain inside their provider fold.
42+
- A model toggle updates configuration and the fold's enabled metadata/count in place. It must not rebuild the model container or call `render()`, because doing so would close an open fold.
43+
44+
## Accessibility
45+
46+
- Preserve semantic landmarks, sections, headings, labels, native details/summary behavior, and button/input elements.
47+
- Every keyboard-operable element has a high-contrast gold `:focus-visible` outline with offset.
48+
- Toggle checkboxes remain in the accessibility and keyboard trees; visual hiding may use opacity, never `display: none`.
49+
- Color is never the only state indicator. Text, counters, disclosure markers, and enabled/disabled control states carry the same information.
50+
- Respect `prefers-reduced-motion: reduce` by removing transitions and animations.
51+
- Existing localized prose and ARIA labels remain translation-driven.
52+
53+
## Responsive behavior
54+
55+
- Below 760px, sections form one column and provider model rows form one column.
56+
- At 760px and above, the server and routing editors share two columns; credentials and models span the page. Credential cards and provider model rows may use two columns.
57+
- Below 560px, hero and card spacing contracts, headings stack, bind controls become one column, and credential controls wrap without overflow.
58+
- Below 520px, save and restart actions share two columns while status text occupies the full row.
59+
- Below 360px, compact language and API-key controls remain usable without horizontal scrolling.
60+
61+
## Change discipline
62+
63+
Visual renewal must not rename existing DOM IDs, alter API endpoints or payloads, replace translated strings, or change credential, routing, save, restart, and polling behavior. New visual behavior should be expressed through semantic HTML and CSS before adding JavaScript.

install.sh

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -298,15 +298,15 @@ validate_env_value COMMANDCODE_API_KEY "$COMMANDCODE_API_KEY"
298298
[ "${EUID:-$(id -u)}" -ne 0 ] || fail "do not run this rootless installer with sudo/root"
299299

300300
require_command node
301+
NODE_MAJOR="$(node -p "Number(process.versions.node.split('.')[0])")"
302+
if [ "$NODE_MAJOR" -lt 22 ]; then
303+
fail "Node.js >= 22 is required to install or use Command Code CLI 1.14.0; found $(node --version)"
304+
fi
305+
301306
require_command npm
302307
require_command systemctl
303308
ensure_commandcode_auth
304309

305-
NODE_MAJOR="$(node -p "Number(process.versions.node.split('.')[0])")"
306-
if [ "$NODE_MAJOR" -lt 20 ]; then
307-
fail "Node.js >= 20 is required; found $(node --version)"
308-
fi
309-
310310
if [ ! -f "package.json" ] || ! grep -q '"name": "commandcode-bridge"' package.json; then
311311
fail "run this script from the commandcode-bridge source checkout or package root"
312312
fi
@@ -382,7 +382,7 @@ fi
382382
write_env_line COMMANDCODE_DEFAULT_MODEL deepseek/deepseek-v4-pro
383383
write_env_line COMMANDCODE_ALLOWED_MODELS "$DEFAULT_ALLOWED_MODELS"
384384
write_env_line COMMANDCODE_ALLOW_UNKNOWN_MODELS false
385-
write_env_line COMMANDCODE_CLI_VERSION 1.3.1
385+
write_env_line COMMANDCODE_CLI_VERSION 1.14.0
386386
write_env_line COMMANDCODE_TIMEOUT_MS 300000
387387
write_env_line COMMANDCODE_EMPTY_VISIBLE_RESPONSE_POLICY error_on_length
388388
write_env_line COMMANDCODE_BALANCE_ALERT_ENABLED false

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "commandcode-bridge",
3-
"version": "1.3.1",
3+
"version": "1.14.0",
44
"description": "OpenAI-compatible HTTP bridge for CommandCode's DeepSeek V4 Pro API path.",
55
"type": "module",
66
"license": "MIT",

release/env.production.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ COMMANDCODE_CREDENTIAL_COOLDOWN_MS=60000
1515
COMMANDCODE_DEFAULT_MODEL=deepseek/deepseek-v4-pro
1616
COMMANDCODE_ALLOWED_MODELS=deepseek/deepseek-v4-pro,deepseek/deepseek-v4-flash
1717
COMMANDCODE_ALLOW_UNKNOWN_MODELS=false
18-
COMMANDCODE_CLI_VERSION=1.3.1
18+
COMMANDCODE_CLI_VERSION=1.14.0
1919
COMMANDCODE_TIMEOUT_MS=300000
2020
COMMANDCODE_EMPTY_VISIBLE_RESPONSE_POLICY=error_on_length
2121
# Balance alerts are opt-in and disabled by default.

src/config.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ export function loadBridgeConfig(options: LoadBridgeConfigOptions = {}): BridgeC
174174
host: serverFromFile.host,
175175
port: serverFromFile.port,
176176
apiBase: (env.COMMANDCODE_API_BASE?.trim() || "https://api.commandcode.ai").replace(/\/+$/, ""),
177-
cliVersion: env.COMMANDCODE_CLI_VERSION?.trim() || "1.3.1",
177+
cliVersion: env.COMMANDCODE_CLI_VERSION?.trim() || "1.14.0",
178178
defaultModel,
179179
allowedModels,
180180
allowUnknownModels: parseBoolean(env.COMMANDCODE_ALLOW_UNKNOWN_MODELS, false),
@@ -271,3 +271,35 @@ export function publicModelOwnedBy(model: string, config: BridgeConfig): string
271271
const catalogEntry = config.modelCatalog?.find((entry) => entry.id === upstreamModel);
272272
return ownedBySlug(catalogEntry?.provider ?? upstreamModel.split("/")[0]);
273273
}
274+
275+
export interface PublicModelObject {
276+
id: string;
277+
object: "model";
278+
created: number;
279+
owned_by: string;
280+
context_window?: number;
281+
context_length?: number;
282+
max_context_length?: number;
283+
}
284+
285+
export function publicModelObject(model: string, config: BridgeConfig): PublicModelObject {
286+
const upstreamModel =
287+
model === "default" || model === "commandcode/default"
288+
? config.defaultModel
289+
: normalizeModelName(model);
290+
const contextWindow = config.modelCatalog?.find(
291+
(entry) => entry.id === upstreamModel,
292+
)?.contextWindow;
293+
const object: PublicModelObject = {
294+
id: model,
295+
object: "model",
296+
created: 1_778_454_400,
297+
owned_by: publicModelOwnedBy(model, config),
298+
};
299+
if (contextWindow !== undefined) {
300+
object.context_window = contextWindow;
301+
object.context_length = contextWindow;
302+
object.max_context_length = contextWindow;
303+
}
304+
return object;
305+
}

src/credential-router.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ function daysUntil(value: string | null | undefined, now: number): number | unde
114114
return Math.max((end - now) / DAY_MS, 0);
115115
}
116116

117+
function hasUrgentExpiry(state: CommandCodeCredentialState, now: number): boolean {
118+
const end = Date.parse(state.billing?.currentPeriodEnd ?? "");
119+
if (!Number.isFinite(end)) return false;
120+
const remaining = end - now;
121+
return remaining > 0 && remaining <= DAY_MS;
122+
}
123+
117124
function scoringDaysUntil(value: string | null | undefined, now: number): number | undefined {
118125
const remaining = daysUntil(value, now);
119126
return remaining === undefined ? undefined : Math.max(remaining, MIN_DAYS_LEFT);
@@ -392,6 +399,9 @@ export class CommandCodeCredentialRouter {
392399
);
393400
}
394401

402+
const urgentCandidates = candidates.filter((state) => hasUrgentExpiry(state, now));
403+
if (urgentCandidates.length > 0) candidates = urgentCandidates;
404+
395405
const selected = this.selectForPolicy(this.policy, candidates, now);
396406
selected.inFlight += 1;
397407
selected.lastSelectedAt = now;

src/dashboard-config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { homedir } from "node:os";
33
import { dirname, resolve } from "node:path";
44
import { mkdirSync } from "node:fs";
55

6+
import { COMMANDCODE_MODEL_DEFINITIONS } from "./model-catalog.js";
67
import type {
78
CommandCodeCredential,
89
CommandCodeModelConfig,
@@ -237,6 +238,13 @@ export function normalizeModelUpdate(
237238
}
238239
const notes = stringValue(model.notes);
239240
if (notes) normalized.notes = notes;
241+
const definition = COMMANDCODE_MODEL_DEFINITIONS.find(
242+
(entry) => entry.id === id || entry.aliases?.includes(id),
243+
);
244+
const contextWindow = definition ? definition.contextWindow : model.contextWindow;
245+
if (contextWindow !== undefined && Number.isInteger(contextWindow) && contextWindow > 0) {
246+
normalized.contextWindow = contextWindow;
247+
}
240248
return normalized;
241249
})
242250
.filter((model): model is CommandCodeModelConfig => Boolean(model));

0 commit comments

Comments
 (0)