Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dashboard/src/content/copy.csv
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,7 @@ limits.provider.copilot,ui,LimitsPage,UsageLimitsPanel,provider_copilot,"GitHub
limits.provider.antigravity,ui,LimitsPage,UsageLimitsPanel,provider_antigravity,"Antigravity",,active
limits.provider.zcode,ui,LimitsPage,UsageLimitsPanel,provider_zcode,"ZCode",,active
provider.display.omp,ui,*,ProviderDisplay,omp,"oh-my-pi",,active
provider.display.deepseek_harness,ui,*,ProviderDisplay,deepseek_harness,"DeepSeek Harness",,active
limits.label.zcode_glm52,ui,LimitsPage,UsageLimitsPanel,zcode_glm52,"GLM-5.2",,active
limits.label.zcode_glm5t,ui,LimitsPage,UsageLimitsPanel,zcode_glm5t,"GLM-5-Turbo",,active
limits.label.zcode_5h,ui,LimitsPage,UsageLimitsPanel,zcode_5h,"5h",,active
Expand Down
1 change: 1 addition & 0 deletions dashboard/src/content/i18n/zh-TW/core.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@
"limits.provider.grok": "Grok Build",
"limits.provider.copilot": "GitHub Copilot",
"provider.display.omp": "oh-my-pi",
"provider.display.deepseek_harness": "DeepSeek Harness",
"limits.provider.antigravity": "Antigravity",
"limits.provider.zcode": "ZCode",
"limits.provider.opencode_go": "OpenCode Go",
Expand Down
1 change: 1 addition & 0 deletions dashboard/src/content/i18n/zh/core.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@
"limits.provider.grok": "Grok Build",
"limits.provider.copilot": "GitHub Copilot",
"provider.display.omp": "oh-my-pi",
"provider.display.deepseek_harness": "DeepSeek Harness",
"limits.provider.antigravity": "Antigravity",
"limits.provider.zcode": "ZCode",
"limits.provider.opencode_go": "OpenCode Go",
Expand Down
39 changes: 39 additions & 0 deletions dashboard/src/lib/model-breakdown.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,45 @@ describe("buildFleetData", () => {
expect(fleet[2].totalPercentValue).toBeGreaterThan(0);
expect(fleet[2].totalPercentValue).toBeLessThan(0.01);
});

it("folds the deprecated deepseek source alias into dsh and merges models", () => {
const fleet = buildFleetData({
sources: [
{
source: "deepseek",
totals: { billable_total_tokens: 100, total_cost_usd: "1.00" },
models: [
{ model_id: "deepseek-v4-pro", totals: { billable_total_tokens: 70 } },
{ model_id: "deepseek-v4-flash", totals: { billable_total_tokens: 30 } },
],
},
{
source: "dsh",
totals: { billable_total_tokens: 400, total_cost_usd: "4.00" },
models: [
{ model_id: "deepseek-v4-pro", totals: { billable_total_tokens: 300 } },
{ model_id: "deepseek-v4-flash", totals: { billable_total_tokens: 100 } },
],
},
],
});

expect(fleet.map(({ source }) => source)).toEqual(["dsh"]);
expect(fleet[0].usage).toBe(500);
expect(fleet[0].usd).toBe(5);
const byId = Object.fromEntries(fleet[0].models.map((m: any) => [m.id, m.usage]));
expect(byId).toEqual({ "deepseek-v4-pro": 370, "deepseek-v4-flash": 130 });
});

it("ignores malformed non-array model collections while preserving source totals", () => {
expect(() => buildFleetData({
sources: [{
source: "dsh",
totals: { billable_total_tokens: 42 },
models: { model_id: "not-an-array" },
}],
})).not.toThrow();
});
});

describe("buildAllModels", () => {
Expand Down
82 changes: 81 additions & 1 deletion dashboard/src/lib/model-breakdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,89 @@ export function resolveDisplayTokens(totals: any, fallback = 0) {
return billableTokens ?? totalTokens ?? fallback;
}

// Deprecated source names folded into their canonical spelling at display
// time. "deepseek" was the first dsh-integration source name; it was renamed
// to "dsh", but a cloud upload from before the rename still carries the old
// name. Folding it back keeps one "DeepSeek Harness" provider instead of two
// ("DEEPSEEK" + "DeepSeek Harness"). This only changes presentation; the
// underlying cloud rows are not rewritten.
const SOURCE_ALIASES: Record<string, string> = {
deepseek: "dsh",
};

function canonicalSource(source: any): string {
const raw = typeof source === "string" ? source : "";
return SOURCE_ALIASES[raw] || raw;
}

const TOKEN_TOTAL_KEYS = [
"total_tokens",
"billable_total_tokens",
"input_tokens",
"output_tokens",
"cached_input_tokens",
"cache_creation_input_tokens",
"reasoning_output_tokens",
];

function addTotalsInto(target: any, add: any) {
for (const key of TOKEN_TOTAL_KEYS) {
target[key] = (Number(target[key]) || 0) + (Number(add?.[key]) || 0);
}
target.total_cost_usd = String(
Number(target.total_cost_usd || 0) + Number(add?.total_cost_usd || 0),
);
}

function emptyTotals() {
return {
total_tokens: 0,
billable_total_tokens: 0,
input_tokens: 0,
output_tokens: 0,
cached_input_tokens: 0,
cache_creation_input_tokens: 0,
reasoning_output_tokens: 0,
total_cost_usd: "0",
};
}

// Merge API sources whose canonical name collides after alias resolution
// (e.g. a stale "deepseek" plus the current "dsh"), summing totals and
// combining per-model rows keyed by model id.
function mergeSourcesByAlias(sources: any[]) {
const bySource = new Map();
for (const entry of sources || []) {
const source = canonicalSource(entry?.source);
if (!source) continue;
let merged = bySource.get(source);
if (!merged) {
merged = { source, source_scope: entry?.source_scope, totals: emptyTotals(), models: new Map() };
bySource.set(source, merged);
}
addTotalsInto(merged.totals, entry?.totals);
for (const model of Array.isArray(entry?.models) ? entry.models : []) {
const id = model?.model_id || model?.model || "";
if (!id) continue;
let modelRow = merged.models.get(id);
if (!modelRow) {
modelRow = { model: model?.model || id, model_id: id, totals: emptyTotals() };
merged.models.set(id, modelRow);
}
addTotalsInto(modelRow.totals, model?.totals);
}
}
return Array.from(bySource.values()).map((s) => ({
...s,
models: Array.from(s.models.values()),
}));
}

export function buildFleetData(modelBreakdown: any, { copyFn }: AnyRecord = {}) {
const safeCopy = typeof copyFn === "function" ? copyFn : (key: string) => key;
const sources: any[] = Array.isArray(modelBreakdown?.sources) ? modelBreakdown.sources : [];
const sources: any[] = mergeSourcesByAlias(
Array.isArray(modelBreakdown?.sources) ? modelBreakdown.sources : [],
);
const normalizedSources = sources
.map((entry: any) => {
const totalTokens = resolveDisplayTokens(entry?.totals);
Expand Down
2 changes: 2 additions & 0 deletions dashboard/src/lib/provider-display.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const SPECIAL_PROVIDER_NAMES = {
};

const SPECIAL_PROVIDER_COPY_KEYS = {
deepseek: "provider.display.deepseek_harness",
dsh: "provider.display.deepseek_harness",
omp: "provider.display.omp",
};

Expand Down
5 changes: 5 additions & 0 deletions dashboard/src/lib/provider-display.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,9 @@ describe("formatProviderDisplayName", () => {
expect(formatProviderDisplayName("omp")).toBe("oh-my-pi");
expect(formatProviderDisplayName("OMP")).toBe("oh-my-pi");
});

it("uses the registered DeepSeek Harness product name for current and legacy sources", () => {
expect(formatProviderDisplayName("dsh")).toBe("DeepSeek Harness");
expect(formatProviderDisplayName("deepseek")).toBe("DeepSeek Harness");
});
});
22 changes: 16 additions & 6 deletions dashboard/src/ui/dashboard/components/ProviderIcon.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -263,11 +263,21 @@ function ZedIcon({ size = 16, className = "" }) {
);
}

// DeepSeek whale mark, mono path from @lobehub/icons.
function DeepSeekIcon({ size = 16, className = "" }) {
// Official DeepSeek Harness FishLogo geometry from deepseek-ai/deepseek-harness.
// Harness deliberately renders the shared fish silhouette in currentColor so
// it follows the product's wordmark ink in both light and dark themes.
function DeepSeekHarnessIcon({ size = 16, className = "" }) {
return (
<svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor" fillRule="evenodd" xmlns="http://www.w3.org/2000/svg" className={className}>
<path d="M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z" />
<svg
width={size}
height={size}
viewBox="0 0 23.16 17.04"
fill="none"
className={className}
data-brand="deepseek-harness"
aria-hidden="true"
>
<path fill="currentColor" d="M22.9168 1.43018C22.6713 1.31018 22.5658 1.53918 22.4223 1.65519C22.3733 1.69269 22.3318 1.74169 22.2903 1.78669C21.9317 2.1697 21.5127 2.42121 20.9657 2.39121C20.1657 2.34621 19.4827 2.59771 18.8787 3.20973C18.7502 2.45521 18.3236 2.0047 17.6746 1.71569C17.3351 1.56568 16.9916 1.41518 16.7536 1.08867C16.5876.856163 16.5421.597155 16.4591.341647C16.4061.187643 16.3536.0301382 16.1761.00363739C15.9836-.0263635 15.9081.135141 15.8326.270145C15.5306.822162 15.4136 1.43018 15.4251 2.0462C15.4516 3.43174 16.0366 4.53527 17.1991 5.3203C17.3311 5.4103 17.3651 5.5003 17.3236 5.63181C17.2441 5.90231 17.1501 6.16482 17.0671 6.43533C17.0141 6.60784 16.9351 6.64584 16.7501 6.57033C16.1121 6.30383 15.5611 5.90931 15.074 5.4328C14.2475 4.63328 13.5 3.75075 12.568 3.05973C12.349 2.89822 12.13 2.74822 11.9034 2.60522C10.9524 1.68169 12.028.923165 12.277.833162C12.5375.739159 12.3675.41615 11.5259.42015C10.6844.42365 9.91439.705658 8.93286 1.08117C8.78935 1.13767 8.63835 1.17867 8.48384 1.21267C7.59332 1.04367 6.66829 1.00617 5.70226 1.11517C3.88321 1.31768 2.43016 2.1777 1.36213 3.64575C.0790928 5.4103-.222916 7.41536.146595 9.50642C.535106 11.7105 1.66014 13.535 3.38869 14.9616C5.18125 16.4406 7.24581 17.1657 9.60138 17.0266C11.0319 16.9441 12.6245 16.7526 14.421 15.2321C14.874 15.4576 15.3496 15.5476 16.1381 15.6151C16.7456 15.6716 17.3306 15.5851 17.7836 15.4911C18.4931 15.3411 18.4441 14.6841 18.1876 14.5636C16.1081 13.595 16.5646 13.9891 16.1496 13.67C17.2061 12.42 18.8202 10.1979 19.3182 7.17235C19.3672 6.83834 19.4297 6.36783 19.4222 6.09732C19.4182 5.93231 19.4562 5.86831 19.6447 5.84931C20.1657 5.78931 20.6712 5.64681 21.1357 5.3913C22.4833 4.65528 23.0268 3.44624 23.1548 1.9972C23.1738 1.77569 23.1508 1.54668 22.9168 1.43018ZM11.1749 14.4736C9.15936 12.889 8.18184 12.3675 7.77832 12.39C7.40081 12.4125 7.46881 12.8445 7.55182 13.126C7.63882 13.404 7.75182 13.5955 7.91033 13.8396C8.01983 14.0011 8.09533 14.2411 7.80083 14.4216C7.15181 14.8231 6.02327 14.2866 5.97027 14.2601C4.65673 13.4865 3.5587 12.4655 2.78467 11.069C2.03715 9.72493 1.60314 8.28289 1.53164 6.74384C1.51264 6.37233 1.62214 6.24082 1.99215 6.17332C2.47916 6.08332 2.98118 6.06432 3.46769 6.13582C5.52476 6.43633 7.27581 7.35586 8.74385 8.8129C9.58188 9.64243 10.2159 10.634 10.8689 11.6025C11.5634 12.631 12.3105 13.611 13.262 14.4146C13.598 14.6961 13.866 14.9101 14.1225 15.0681C13.349 15.1546 12.058 15.1731 11.1749 14.4746L11.1749 14.4736ZM12.141 8.25988C12.141 8.09488 12.273 7.96338 12.439 7.96338C12.4765 7.96338 12.5105 7.97088 12.541 7.98188C12.5825 7.99688 12.6205 8.01938 12.6505 8.05338C12.7035 8.10588 12.7335 8.18088 12.7335 8.25988C12.7335 8.42489 12.6015 8.55639 12.4355 8.55639C12.2695 8.55639 12.141 8.42489 12.141 8.25988ZM15.1415 9.79893C14.949 9.87793 14.7565 9.94544 14.5715 9.95294C14.2845 9.96794 13.9715 9.85143 13.8015 9.70893C13.5375 9.48742 13.3485 9.36342 13.2695 8.97691C13.2355 8.8119 13.2545 8.55639 13.2845 8.40989C13.3525 8.09438 13.277 7.89187 13.0545 7.70787C12.8735 7.55786 12.643 7.51636 12.39 7.51636C12.2955 7.51636 12.209 7.47486 12.1445 7.44136C12.039 7.38886 11.9519 7.25735 12.035 7.09585C12.0615 7.04335 12.19 6.91584 12.22 6.89334C12.5635 6.69784 12.9595 6.76184 13.326 6.90834C13.6655 7.04735 13.9225 7.30236 14.292 7.66287C14.6695 8.09838 14.7375 8.21838 14.9525 8.54539C15.1225 8.8009 15.277 9.06341 15.3831 9.36392C15.4471 9.55142 15.3641 9.70493 15.1415 9.79893Z" />
</svg>
);
}
Expand All @@ -282,7 +292,8 @@ const PROVIDER_ICON_MAP = {
COPILOT: CopilotIcon,
CRAFT: CraftIcon,
CURSOR: CursorIcon,
DEEPSEEK: DeepSeekIcon,
DEEPSEEK: DeepSeekHarnessIcon,
DSH: DeepSeekHarnessIcon,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
DROID: DroidIcon,
"EVERY-CODE": CodexIcon,
GEMINI: GeminiIcon,
Expand Down Expand Up @@ -315,7 +326,6 @@ const PROVIDER_LOGO_MAP = {
CLAUDE: "/brand-logos/claude-code.svg",
"CLAUDE-SCIENCE": "/brand-logos/claude-code.svg",
CODEX: "/brand-logos/codex.svg",
DEEPSEEK: "/brand-logos/deepseek.svg",
GEMINI: "/brand-logos/gemini.svg",
"KILO-CLI": "/brand-logos/kilo.svg",
"KILO-CODE": "/brand-logos/kilo.svg",
Expand Down
13 changes: 13 additions & 0 deletions dashboard/src/ui/dashboard/components/ProviderIcon.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,19 @@ describe("ProviderIcon", () => {
expect(icon).toHaveAttribute("width", "20");
expect(icon).toHaveAttribute("height", "20");
});

it("renders the official theme-aware DeepSeek Harness fish instead of the legacy blue asset", () => {
for (const provider of ["dsh", "deepseek"]) {
const { container } = render(<ProviderIcon provider={provider} size={20} />);
const icon = container.querySelector('svg[data-brand="deepseek-harness"]');

expect(icon).not.toBeNull();
expect(icon).toHaveAttribute("width", "20");
expect(icon).toHaveAttribute("viewBox", "0 0 23.16 17.04");
expect(container.querySelector('img[src="/brand-logos/deepseek.svg"]')).toBeNull();
expect(icon?.querySelector('path[fill="currentColor"]')).not.toBeNull();
}
});
});

it("renders the Qoder CN green-crescent mark from its own asset", () => {
Expand Down
1 change: 1 addition & 0 deletions dashboard/src/ui/dashboard/components/UsageOverview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ function parseAnimatedCounterValue(displayValue) {
// Provider color mapping for visual distinction
const PROVIDER_COLORS = {
CODEX: "#3b82f6", // blue-500
DSH: "var(--community-deepseek)", // DeepSeek Harness brand blue
CLAUDE: "#d97757", // Anthropic Japonica orange-red
OPENCODE: "#f59e0b", // amber-500
GEMINI: "#2196f3", // Google Gemini bright blue
Expand Down
2 changes: 1 addition & 1 deletion scripts/validate-locale-coverage.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const SOURCE_IDENTICAL_KEY_ALLOWLIST = [
/^settings[.]menubar[.]iconStyle[.]clawd$/,
/^settings[.]menubar[.]updates[.]footerCore$/,
/^limits[.]provider[.]/,
/^provider[.]display[.]omp$/,
/^provider[.]display[.](?:omp|deepseek_harness)$/,
/^limits[.]label[.](?:cursor_api|zcode_glm52|zcode_glm5t|claude_opus|codex_spark_[57][hd]|gemini_(?:pro|flash|lite)|antigravity_)/,
/^skills[.]mode[.]skillssh$/,
/^skills[.]repo[.]placeholder$/,
Expand Down
12 changes: 12 additions & 0 deletions src/commands/status.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ const {
resolveGooseDbPath,
listDroidSettingsFiles,
resolveDroidSessionsDir,
resolveDshHome,
resolveDshSessionFiles,
resolveTraeStoragePath,
readTraeEntitlementFromStorage,
resolveGrokBuildSessions,
Expand Down Expand Up @@ -617,6 +619,10 @@ async function cmdStatus(argv = []) {
const droidSessionsDir = resolveDroidSessionsDir(process.env);
const droidSettingsFiles = listDroidSettingsFiles(process.env);
const droidInstalled = droidSettingsFiles.length > 0;
const dshHome = resolveDshHome(process.env);
const dshSessionsDir = path.join(dshHome, "sessions");
const dshSessionFiles = await resolveDshSessionFiles(process.env);
const dshInstalled = dshSessionFiles.length > 0;

// Trae SOLO (ByteDance AI IDE) — passive entitlement snapshot reader.
const traeStoragePath = resolveTraeStoragePath(process.env);
Expand Down Expand Up @@ -907,6 +913,9 @@ async function cmdStatus(argv = []) {
droid: droidInstalled
? { installed: true, files: droidSettingsFiles.length, detail: droidSessionsDir }
: { installed: false },
dsh: dshInstalled
? { installed: true, files: dshSessionFiles.length, detail: dshSessionsDir }
: { installed: false },
trae: traeInstalled
? {
installed: true,
Expand Down Expand Up @@ -1069,6 +1078,9 @@ async function cmdStatus(argv = []) {
droidInstalled
? `- Droid (Factory): passive reader (${droidSettingsFiles.length} session${droidSettingsFiles.length !== 1 ? "s" : ""} in ${droidSessionsDir}, cumulative-delta)`
: null,
dshInstalled
? `- DeepSeek Harness: passive reader (${dshSessionFiles.length} session${dshSessionFiles.length !== 1 ? "s" : ""} in ${dshSessionsDir})`
: null,
traeInstalled
// Deliberately NOT "passive reader": every other line with that wording
// means tokens are being counted. Trae encrypts its session transcripts
Expand Down
Loading
Loading