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 .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ jobs:
npm ci
node --test test/openclaw-parser.test.js
node --test --test-name-pattern="native Windows" test/usage-limits.test.js
node --test test/command-runner.test.js

- name: Test Windows updater
run: dotnet test TokenTrackerWin.Tests/TokenTrackerWin.Tests.csproj --configuration Release
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ final class LimitsSettingsStore: ObservableObject {
static let shared = LimitsSettingsStore(userDefaults: .standard)

/// All known provider identifiers, in default display order.
static let allProviders: [String] = ["claude", "codex", "cursor", "gemini", "kimi", "kiro", "grok", "copilot", "antigravity", "zcode", "opencodeGo", "qoder", "qoderCn"]
static let allProviders: [String] = ["claude", "codex", "cursor", "gemini", "kimi", "kiro", "grok", "copilot", "antigravity", "zcode", "opencodeGo", "qoder", "qoderCn", "codingPlan"]

static let displayNames: [String: String] = [
"claude": "Claude",
Expand All @@ -51,6 +51,7 @@ final class LimitsSettingsStore: ObservableObject {
"opencodeGo": "OpenCode Go",
"qoder": "Qoder",
"qoderCn": "Qoder CN",
"codingPlan": "Ark Coding Plan",
]

static let iconNames: [String: String] = [
Expand Down
24 changes: 23 additions & 1 deletion TokenTrackerBar/TokenTrackerBar/Models/UsageLimits.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ struct UsageLimitsResponse: Codable, Equatable {
let opencodeGo: OpencodeGoLimits?
let qoder: QoderLimits?
let qoderCn: QoderLimits?
let codingPlan: CodingPlanLimits?

enum CodingKeys: String, CodingKey {
case fetchedAt = "fetched_at"
case claude, codex, cursor, gemini, kimi, kiro, grok, antigravity, copilot, zcode, qoder, qoderCn
case claude, codex, cursor, gemini, kimi, kiro, grok, antigravity, copilot, zcode, qoder, qoderCn, codingPlan
case opencodeGo = "opencodeGo"
}
}
Expand Down Expand Up @@ -493,6 +494,26 @@ struct QoderLimits: Codable, Equatable {
}
}

struct CodingPlanLimits: Codable, Equatable {
let configured: Bool
let error: String?
let planLabel: String?
let primaryWindow: GenericLimitWindow?
let secondaryWindow: GenericLimitWindow?
let tertiaryWindow: GenericLimitWindow?
let cachedAt: String?
let stale: Bool?

enum CodingKeys: String, CodingKey {
case configured, error, stale
case planLabel = "plan_label"
case primaryWindow = "primary_window"
case secondaryWindow = "secondary_window"
case tertiaryWindow = "tertiary_window"
case cachedAt = "cached_at"
}
}

struct AntigravityLimits: Codable, Equatable {
let configured: Bool
let error: String?
Expand Down Expand Up @@ -536,6 +557,7 @@ extension UsageLimitsResponse {
(opencodeGo?.configured ?? false, opencodeGo?.error),
(qoder?.configured ?? false, qoder?.error),
(qoderCn?.configured ?? false, qoderCn?.error),
(codingPlan?.configured ?? false, codingPlan?.error),
]
return providers.contains { $0.0 && $0.1 == nil }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ enum LimitResetProviderIconCatalog {
case "zcode": return "zcode.svg"
case "opencodeGo": return "opencode.svg"
case "qoder": return "qoder.svg"
case "codingPlan": return "volcano-ark.svg"
default: return nil
}
}
Expand Down Expand Up @@ -251,6 +252,13 @@ extension UsageLimitsResponse {
("secondary", Strings.qoderBonusLabel, qoderCn.secondaryWindow),
])
}
if let codingPlan {
addGeneric("codingPlan", codingPlan.configured, codingPlan.error, [
("primary", "5h", codingPlan.primaryWindow),
("secondary", "Weekly", codingPlan.secondaryWindow),
("tertiary", "Monthly", codingPlan.tertiaryWindow),
])
}

return out
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ struct LimitsSettingsView: View {
@ViewBuilder
private func providerIcon(id: String) -> some View {
switch id {
case "cursor", "kimi", "kiro", "grok", "copilot", "zcode", "opencodeGo", "qoder", "qoderCn":
case "cursor", "kimi", "kiro", "grok", "copilot", "zcode", "opencodeGo", "qoder", "qoderCn", "codingPlan":
let filename: String = {
switch id {
case "cursor": return "cursor.svg"
Expand All @@ -137,6 +137,7 @@ struct LimitsSettingsView: View {
case "opencodeGo": return "opencode.svg"
case "qoder": return "qoder.svg"
case "qoderCn": return "qoder-cn.svg"
case "codingPlan": return "volcano-ark.svg"
default: return "copilot.svg"
}
}()
Expand Down
15 changes: 14 additions & 1 deletion TokenTrackerBar/TokenTrackerBar/Views/UsageLimitsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ struct UsageLimitsView: View {
if let qoderCn = limits.qoderCn, qoderCn.configured, qoderCn.error == nil {
groups.append(AnyView(toolSection(id: id, title: planTitle("Qoder CN", qoderCn.planLabel), assetName: "QoderCnLogo", toolName: "Qoder CN", specs: qoderSpecs(qoderCn), updatedAtISO: qoderCn.cachedAt, isStale: qoderCn.stale ?? false)))
}
case "codingPlan":
if let codingPlan = limits.codingPlan, codingPlan.configured, codingPlan.error == nil {
groups.append(AnyView(toolSection(id: id, title: planTitle("Ark Coding Plan", codingPlan.planLabel), assetName: "VolcanoArkLogo", toolName: "Ark Coding Plan", specs: codingPlanSpecs(codingPlan), updatedAtISO: codingPlan.cachedAt, isStale: codingPlan.stale ?? false)))
}
default:
break
}
Expand Down Expand Up @@ -385,6 +389,14 @@ struct UsageLimitsView: View {
return specs
}

private func codingPlanSpecs(_ c: CodingPlanLimits) -> [LimitWindowSpec] {
var specs: [LimitWindowSpec] = []
if let w = c.primaryWindow { specs.append(makeSpec("5h", w.usedPercent, windowSeconds: 5 * 3600, iso: w.resetAt)) }
if let w = c.secondaryWindow { specs.append(makeSpec("Weekly", w.usedPercent, windowSeconds: 7 * 86400, iso: w.resetAt)) }
if let w = c.tertiaryWindow { specs.append(makeSpec("Monthly", w.usedPercent, iso: w.resetAt)) }
return specs
}

private func copilotSpecs(_ c: CopilotLimits) -> [LimitWindowSpec] {
var s: [LimitWindowSpec] = []
if let w = c.primaryWindow { s.append(makeSpec("Premium", w.usedPercent, iso: w.resetAt)) }
Expand Down Expand Up @@ -595,7 +607,7 @@ struct UsageLimitsView: View {
@ViewBuilder
private func brandIcon(_ name: String) -> some View {
switch name {
case "CursorLogo", "KimiLogo", "KiroLogo", "GrokLogo", "CopilotLogo", "ZcodeLogo", "OpenCodeLogo", "QoderLogo", "QoderCnLogo":
case "CursorLogo", "KimiLogo", "KiroLogo", "GrokLogo", "CopilotLogo", "ZcodeLogo", "OpenCodeLogo", "QoderLogo", "QoderCnLogo", "VolcanoArkLogo":
let filename: String = {
switch name {
case "CursorLogo": return "cursor.svg"
Expand All @@ -606,6 +618,7 @@ struct UsageLimitsView: View {
case "OpenCodeLogo": return "opencode.svg"
case "QoderLogo": return "qoder.svg"
case "QoderCnLogo": return "qoder-cn.svg"
case "VolcanoArkLogo": return "volcano-ark.svg"
default: return "copilot.svg"
}
}()
Expand Down
14 changes: 14 additions & 0 deletions dashboard/public/brand-logos/volcano-ark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions dashboard/src/content/copy.csv
Original file line number Diff line number Diff line change
Expand Up @@ -1472,3 +1472,17 @@ landing.v3.how.scene.files_changed,landing,LandingPage,HowItWorksSection,scene_f
landing.v3.how.scene.session_title,landing,LandingPage,HowItWorksSection,scene_session_title,"session log · last turn",,active
landing.v3.how.scene.appended,landing,LandingPage,HowItWorksSection,scene_appended,"appended locally",,active
landing.v3.how.scene.total_tokens,landing,LandingPage,HowItWorksSection,scene_total_tokens,"total tokens",,active
limits.provider.ark_coding_plan,ui,LimitsPage,UsageLimitsPanel,provider_ark_coding_plan,"Ark Coding Plan",,active
limits.label.ark_coding_plan_5h,ui,LimitsPage,UsageLimitsPanel,ark_coding_plan_5h,"5h",,active
limits.label.ark_coding_plan_weekly,ui,LimitsPage,UsageLimitsPanel,ark_coding_plan_weekly,"Weekly",,active
limits.label.ark_coding_plan_monthly,ui,LimitsPage,UsageLimitsPanel,ark_coding_plan_monthly,"Monthly",,active
limits.codingPlan.setupHint.title,ui,LimitsPage,UsageLimitsPanel,coding_plan_hint_title,"Connect Ark Coding Plan",,active
limits.codingPlan.setupHint.subtitle,ui,LimitsPage,UsageLimitsPanel,coding_plan_hint_subtitle,"Quota is read from the official Ark CLI (arkcli) on this machine.",,active
limits.codingPlan.setupHint.step1,ui,LimitsPage,UsageLimitsPanel,coding_plan_hint_step1,"Install the Ark CLI from npm.",,active
limits.codingPlan.setupHint.cta,ui,LimitsPage,UsageLimitsPanel,coding_plan_hint_cta,"View install guide",,active
limits.codingPlan.setupHint.step2,ui,LimitsPage,UsageLimitsPanel,coding_plan_hint_step2,"Sign in with your Volcengine account (browser SSO recommended).",,active
limits.codingPlan.setupHint.step2_remote,ui,LimitsPage,UsageLimitsPanel,coding_plan_hint_step2_remote,"Headless / remote terminals: arkcli auth login --no-browser",,active
limits.codingPlan.setupHint.step3,ui,LimitsPage,UsageLimitsPanel,coding_plan_hint_step3,"Run these, then refresh this page:",,active
limits.codingPlan.setupHint.note_app,ui,LimitsPage,UsageLimitsPanel,coding_plan_hint_note_app,"Refresh after signing in — no app restart needed.",,active
limits.codingPlan.setupHint.copy,ui,LimitsPage,UsageLimitsPanel,coding_plan_hint_copy,"Copy",,active
limits.codingPlan.setupHint.copied,ui,LimitsPage,UsageLimitsPanel,coding_plan_hint_copied,"Copied",,active
14 changes: 14 additions & 0 deletions dashboard/src/content/i18n/zh-TW/core.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@
"limits.label.qoder_ultimate": "Ultimate 免費呼叫",
"limits.label.qoder_cn_credits": "積分",
"limits.label.qoder_cn_ultimate": "Ultimate 免費呼叫",
"limits.provider.ark_coding_plan": "Ark Coding Plan",
"limits.label.ark_coding_plan_5h": "5h",
"limits.label.ark_coding_plan_weekly": "每週",
"limits.label.ark_coding_plan_monthly": "每月",
"limits.qoder_calls.detail": "已用 {{used}} / {{limit}} 次 · 剩餘 {{remaining}} 次",
"limits.hover.expires_at": "{{time}} 到期",
"limits.label.claude_5h": "5h",
Expand Down Expand Up @@ -536,6 +540,16 @@
"limits.opencodeGo.setupHint.note_app": "macOS App:使用 launchctl setenv 設定,然後重新開啟 App。",
"limits.opencodeGo.setupHint.copy": "複製",
"limits.opencodeGo.setupHint.copied": "已複製",
"limits.codingPlan.setupHint.title": "連接 Ark Coding Plan",
"limits.codingPlan.setupHint.subtitle": "額度透過本機的官方 Ark CLI(arkcli)讀取。",
"limits.codingPlan.setupHint.step1": "透過 npm 安裝 Ark CLI。",
"limits.codingPlan.setupHint.cta": "查看安裝指南",
"limits.codingPlan.setupHint.step2": "使用火山引擎帳號登入(推薦瀏覽器 SSO)。",
"limits.codingPlan.setupHint.step2_remote": "無瀏覽器 / 遠端終端:arkcli auth login --no-browser",
"limits.codingPlan.setupHint.step3": "依序執行以下指令,然後重新整理本頁:",
"limits.codingPlan.setupHint.note_app": "登入後重新整理頁面即可,無需重新啟動 App。",
"limits.codingPlan.setupHint.copy": "複製",
"limits.codingPlan.setupHint.copied": "已複製",
"skills.card.manage": "管理",
"skills.dot.synced_aria": "{{agent}}:已同步。點選可取消同步。",
"skills.dot.off_aria": "{{agent}}:未同步。點選可同步。",
Expand Down
14 changes: 14 additions & 0 deletions dashboard/src/content/i18n/zh/core.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@
"limits.label.qoder_ultimate": "Ultimate 免费调用",
"limits.label.qoder_cn_credits": "积分",
"limits.label.qoder_cn_ultimate": "Ultimate 免费调用",
"limits.provider.ark_coding_plan": "Ark Coding Plan",
"limits.label.ark_coding_plan_5h": "5h",
"limits.label.ark_coding_plan_weekly": "每周",
"limits.label.ark_coding_plan_monthly": "每月",
"limits.qoder_calls.detail": "已用 {{used}} / {{limit}} 次 · 剩余 {{remaining}} 次",
"limits.hover.expires_at": "{{time}} 到期",
"limits.label.claude_5h": "5h",
Expand Down Expand Up @@ -604,6 +608,16 @@
"limits.opencodeGo.setupHint.note_app": "macOS App:使用 launchctl setenv 设置,然后重新打开 App。",
"limits.opencodeGo.setupHint.copy": "复制",
"limits.opencodeGo.setupHint.copied": "已复制",
"limits.codingPlan.setupHint.title": "连接 Ark Coding Plan",
"limits.codingPlan.setupHint.subtitle": "额度通过本机的官方 Ark CLI(arkcli)读取。",
"limits.codingPlan.setupHint.step1": "通过 npm 安装 Ark CLI。",
"limits.codingPlan.setupHint.cta": "查看安装指南",
"limits.codingPlan.setupHint.step2": "使用火山引擎账号登录(推荐浏览器 SSO)。",
"limits.codingPlan.setupHint.step2_remote": "无浏览器 / 远程终端:arkcli auth login --no-browser",
"limits.codingPlan.setupHint.step3": "依次执行以下命令,然后刷新本页:",
"limits.codingPlan.setupHint.note_app": "登录后刷新页面即可,无需重启 App。",
"limits.codingPlan.setupHint.copy": "复制",
"limits.codingPlan.setupHint.copied": "已复制",
"skills.card.manage": "管理",
"skills.dot.synced_aria": "{{agent}}:已同步。点击可取消同步。",
"skills.dot.off_aria": "{{agent}}:未同步。点击可同步。",
Expand Down
1 change: 1 addition & 0 deletions dashboard/src/hooks/use-limits-display-prefs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ describe("useLimitsDisplayPrefs", () => {
"antigravity",
"claude",
"codex",
"codingPlan",
"copilot",
"cursor",
"gemini",
Expand Down
1 change: 1 addition & 0 deletions dashboard/src/hooks/use-usage-limits.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const existingLimits = {
zcode: { configured: false },
opencodeGo: { configured: false },
qoder: { configured: false },
codingPlan: { configured: false },
};

const freshLimits = {
Expand Down
12 changes: 12 additions & 0 deletions dashboard/src/hooks/use-usage-limits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ interface UsageLimitsData {
cached_at?: string | null;
source?: string | null;
};
codingPlan: {
configured: boolean;
error?: string | null;
plan_label?: string | null;
primary_window?: { used_percent: number; reset_at?: string | null } | null;
secondary_window?: { used_percent: number; reset_at?: string | null } | null;
tertiary_window?: { used_percent: number; reset_at?: string | null } | null;
cached?: boolean;
stale?: boolean;
cached_at?: string | null;
source?: string | null;
};
}

interface UsageLimitsInitialState {
Expand Down
5 changes: 5 additions & 0 deletions dashboard/src/lib/limits-providers.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const LIMIT_PROVIDER_IDS = [
"opencodeGo",
"qoder",
"qoderCn",
"codingPlan",
];

/** Keys for ProviderIcon — mono logos use inline SVG; colored logos use /brand-logos/. */
Expand All @@ -36,6 +37,8 @@ export const LIMIT_PROVIDER_ICON_KEYS = {
// The CN edition ships its own green-crescent brand mark, distinct from the
// international black double-crescent — resolved to the QODER-CN icon asset.
qoderCn: "QODER-CN",
// Volcano Engine Ark Coding Plan — own brand mark under /brand-logos/.
codingPlan: "VOLCANO-ARK",
};

export function limitProviderIconKey(id) {
Expand Down Expand Up @@ -70,6 +73,8 @@ export function limitProviderName(id) {
return copy("limits.provider.qoder");
case "qoderCn":
return copy("limits.provider.qoder_cn");
case "codingPlan":
return copy("limits.provider.ark_coding_plan");
default:
return String(id || "");
}
Expand Down
2 changes: 2 additions & 0 deletions dashboard/src/lib/pet-quips.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ const PET_LIMIT_PROVIDER_NAMES = {
zcode: "ZCode",
opencodeGo: "OpenCode Go",
qoder: "Qoder",
codingPlan: "Ark Coding Plan",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Localize the new pet-summary labels.

The new strings bypass the dashboard copy system. This causes untranslated provider and period names in pet summaries.

  • dashboard/src/lib/pet-quips.js#L310-L310: Resolve the Coding Plan provider name from localized copy.
  • dashboard/src/lib/pet-quips.js#L397-L397: Resolve the three Coding Plan period labels from localized copy.

As per coding guidelines: “Never hardcode user-facing text; add it to dashboard/src/content/copy.csv.” As per path instructions: “User-facing strings must come from dashboard/src/content/copy.csv.”

📍 Affects 1 file
  • dashboard/src/lib/pet-quips.js#L310-L310 (this comment)
  • dashboard/src/lib/pet-quips.js#L397-L397
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dashboard/src/lib/pet-quips.js` at line 310, Localize the new Coding Plan
labels in dashboard/src/lib/pet-quips.js at lines 310 and 397: resolve the
provider name and all three period labels through the dashboard copy system,
adding the required entries to dashboard/src/content/copy.csv instead of
hardcoding user-facing text.

Sources: Coding guidelines, Path instructions

};

// Unix timestamps are normally seconds; values above this order of magnitude
Expand Down Expand Up @@ -393,6 +394,7 @@ function collectPetLimitRows(limits) {
addGeneric("zcode", limits.zcode, [["GLM-5.2", limits.zcode?.primary_window], ["GLM-5 Turbo", limits.zcode?.secondary_window], ["Other", limits.zcode?.tertiary_window]]);
addGeneric("opencodeGo", limits.opencodeGo, [["5h", limits.opencodeGo?.primary_window], ["Weekly", limits.opencodeGo?.secondary_window], ["Month", limits.opencodeGo?.tertiary_window]]);
addGeneric("qoder", limits.qoder, [["Credits", limits.qoder?.primary_window], ["Ultimate Free Calls", limits.qoder?.secondary_window]]);
addGeneric("codingPlan", limits.codingPlan, [["5h", limits.codingPlan?.primary_window], ["Week", limits.codingPlan?.secondary_window], ["Month", limits.codingPlan?.tertiary_window]]);

rows.sort((a, b) => {
if (b.usedPercent !== a.usedPercent) return b.usedPercent - a.usedPercent;
Expand Down
1 change: 1 addition & 0 deletions dashboard/src/pages/LimitsPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export function LimitsPage() {
opencodeGo={usageLimits?.opencodeGo}
qoder={usageLimits?.qoder}
qoderCn={usageLimits?.qoderCn}
codingPlan={usageLimits?.codingPlan}
order={prefs.order}
visibility={prefs.visibility}
displayMode={prefs.displayMode}
Expand Down
2 changes: 2 additions & 0 deletions dashboard/src/ui/dashboard/components/ProviderIcon.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,8 @@ const PROVIDER_LOGO_MAP = {
// crescent) differs from the international black double-crescent, so it gets
// its own traced asset instead of reusing qoder.svg.
"QODER-CN": "/brand-logos/qoder-cn.svg",
// Volcano Ark (火山方舟) Coding Plan — the Volcengine 3-mountain mark.
"VOLCANO-ARK": "/brand-logos/volcano-ark.svg",
};

// AnythingLLM publishes this compact mark in white. Keep the official asset
Expand Down
Loading
Loading