Skip to content
Open
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
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,13 @@ jobs:
run: |
test "$MACOS_RESULT" = success
test "$FIRMWARE_RESULT" = success

resources:
needs: build-test
runs-on: macos-15
env:
DEVELOPER_DIR: /Applications/Xcode_26.3.app/Contents/Developer
steps:
- uses: actions/checkout@v4
- name: Enforce local-app resource budgets
run: bash scripts/resource_check.sh
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ Notable TokenLink changes are recorded here. The project follows semantic
versioning while it is pre-1.0; hardware verification is reported separately in
`docs/validation`.

## Unreleased

- Added an opt-in Costs beta that keeps provider-reported OpenRouter and
DeepSeek balances separate from coding-plan quota.
- Added seven-day local `Estimated/API-equivalent` cost estimates for Codex,
Claude, and Kimi, backed by a versioned, reviewed price catalog.
- Added bounded streaming transcript scans, explicit cost provenance and
freshness, and CI gates for privacy, memory, runtime, and executable size.

## 0.2.2 — Unreleased

- Added a Universal 2 `TokenLink-0.2.2.dmg` with a drag-to-Applications layout,
Expand Down
34 changes: 31 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ bash scripts/test.sh
swift format lint --strict Package.swift
swift format lint --recursive --strict Sources Tests
bash scripts/privacy_scan.sh
bash scripts/resource_check.sh
```

On a Command-Line-Tools-only machine, compilation may work while the Swift
Expand All @@ -100,10 +101,13 @@ use the macOS CI result as the test evidence.

TokenLink uses a narrow, adapter-oriented architecture:

- `TokenLinkCore` owns provider-neutral identifiers, quota snapshots, and state;
- `TokenLinkCore` owns provider-neutral identifiers, quota and cost snapshots,
and state;
- `TokenLinkProviders` contains isolated provider parsers and fetchers;
- providers implement `QuotaProvider` and emit a normalized `QuotaSnapshot`;
and
- quota providers implement `QuotaProvider` and emit a normalized
`QuotaSnapshot`;
- authoritative cost providers use their own capability, adapter, snapshot,
store, and refresh path; and
- the app supplies credentials and HTTP access through narrow interfaces.

Adapters are currently compiled into TokenLink. They are plugin-like extension
Expand Down Expand Up @@ -147,6 +151,30 @@ home paths, subscription identifiers, or exact quota values when they could
identify an account. It is acceptable to obscure account-specific values while
showing that the expected windows, reset behavior, and status are present.

## Cost provider and pricing contributions

Quota, authoritative balances, and local cost estimates are separate domains.
A cost-only provider must not receive a synthetic quota snapshot, affect quota
severity or notifications, or enter a StopWatch payload.

An authoritative cost adapter must:

- use an official account or billing endpoint with a narrow HTTPS host policy;
- require an explicit Keychain credential rather than browser state or an
unrelated CLI credential;
- preserve returned currencies and valid zero values without conversion or
inference from balance changes;
- distinguish authentication, timeout, decoding, and partial-source failures;
and
- include synthetic fixture tests without real account payloads or amounts.

A price-catalog change must include the catalog version and effective date,
first-party pricing references, explicit model aliases, and independent rates
for every supported token bucket. Never guess an unknown model price, silently
price a partially covered record, convert currencies, or remove the visible
`Estimated/API-equivalent` label. Update estimator tests and the resource
workload when a new local transcript format is introduced.

## Hardware adapter contributions

Hardware and firmware access is not assumed. A hardware contribution must state:
Expand Down
5 changes: 4 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ let package = Package(
],
targets: [
.target(name: "TokenLinkCore"),
.target(name: "TokenLinkProviders", dependencies: ["TokenLinkCore"]),
.target(
name: "TokenLinkProviders",
dependencies: ["TokenLinkCore"],
resources: [.process("Resources")]),
.target(name: "TokenLinkDevice", dependencies: ["TokenLinkCore"]),
.executableTarget(
name: "TokenLinkApp",
Expand Down
60 changes: 55 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,18 @@ or M5Stack. Provider names and trademarks belong to their respective owners.

## What it does

- Native `MenuBarExtra` plus a four-route Control Center: Overview, Providers,
StopWatch, and Settings & Diagnostics.
- Native `MenuBarExtra` plus a five-route Control Center: Overview, Providers,
Costs, StopWatch, and Settings & Diagnostics.
- Normalizes several quota windows without inventing plan limits.
- Projects burn rate per window ("runs out in ~3h at this pace") from recent
local samples — no extra API calls.
- Optionally draws a fair-pace marker showing where a window would be under
even consumption.
- Offers an opt-in beta scan of documented local Codex, Claude, and Kimi CLI
transcript directories to summarize recent token counters on-device.
- Keeps cost data in a separate opt-in beta: official OpenRouter/DeepSeek
balances stay authoritative, while local Codex/Claude/Kimi usage is always
labelled `Estimated/API-equivalent` and priced from a reviewed bundled catalog.
- Sends macOS notifications when a window runs low, a window resets, or a
stored credential is rejected (toggle in Settings).
- Keeps last-known-good snapshots and marks them stale when refresh fails.
Expand Down Expand Up @@ -290,6 +293,35 @@ TokenLink supports the current numeric-unit/camelCase quota shape plus an explic
legacy compatibility branch. It preserves returned windows and does not estimate
limits from a plan name.

### Costs beta

Enable **Settings & Diagnostics → Beta → Costs** to load the separate Costs
page. Cost refresh, failure, and caching are independent from quota refresh,
notifications, menu-bar severity, and StopWatch payloads. The menu bar can show
one fixed cost metric after the primary quota; unavailable selections fall back
to quota-only text.

The two cost domains are intentionally not merged:

- **Authoritative balances** come from an official provider account API.
OpenRouter calls `/api/v1/credits` and `/api/v1/key` independently. An explicit
Management Key can expose account credits; a regular API key may expose only
current-key spend, which TokenLink presents as partial data instead of
inventing a balance. DeepSeek calls `/user/balance` and preserves every
returned currency separately, including valid zero balances. TokenLink does
not convert currencies or infer spend from balance changes.
- **`Estimated/API-equivalent`** values price the last seven days of supported
local CLI token records using the bundled catalog's version and effective
date. They estimate what equivalent API traffic would cost; they do not
estimate, allocate, or assign monetary value to a Coding Plan subscription.
Unknown models and token categories without a reviewed price are excluded and
surfaced as warnings.

Cost credentials are explicit Keychain entries. TokenLink does not reuse
browser state, organization-admin credentials, or unrelated CLI credentials for
OpenRouter or DeepSeek. Balances and estimated monetary totals remain in memory
and are not written to configuration or diagnostics.

Provider and Codex-path changes are persisted immediately and take effect
after restarting the app. Region, account, and refresh-interval changes take
effect immediately.
Expand Down Expand Up @@ -347,9 +379,12 @@ candidate validation layer. See the latest report in [`docs/validation`](docs/va
with user-only permissions.
- No browser-cookie access, Full Disk Access, analytics, or remote TokenLink
service.
- The optional local-usage beta reads only `.codex/sessions`, `.claude/projects`,
and `.kimi-code/sessions`; it extracts token counters locally and never sends
transcript data over the network.
- The optional local-usage and cost betas read only `.codex/sessions`,
`.claude/projects`, and `.kimi-code/sessions`; they extract token counters
locally and never send transcript data over the network.
- Local scans stream 64 KiB chunks, process files sequentially, skip files over
50 MiB and records over 1 MiB, and retain neither raw transcript content nor
monetary snapshots. There is no telemetry.
- Provider URLs are HTTPS and checked against narrow official-host allowlists
before credential-bearing requests.
- Diagnostics are redacted before they are written to a user-selected file.
Expand Down Expand Up @@ -401,6 +436,21 @@ Each provider owns a fixture-tested parser and emits a shared `QuotaSnapshot`.
The app is the only UI state owner. The device layer receives deliberate v1 or
v2 projections and never receives provider credentials.

Cost-only providers use separate adapters, state, refresh coordination, and UI
models; they never receive synthetic quota snapshots or enter watch payloads.

## Resource and privacy gates

CI runs `scripts/resource_check.sh` after the regular macOS test job. The gate
streams and parses a deterministic 64 MiB workload through the production JSONL
reader, then enforces a maximum 160 MiB RSS, 30-second elapsed time, and 15 MiB
release executable. Compiler processes are excluded from the measurement.

`scripts/privacy_scan.sh` rejects secret-like values and production logging of
balances, raw monetary values, authorization headers, raw response bodies, or
transcript paths. Diagnostics separately test that amounts, model identifiers,
account labels/UUIDs, error text, and paths cannot enter exported metadata.

## Protocol-v2 status

The Mac side implements payload projection, capability negotiation, v1 fallback,
Expand Down
41 changes: 39 additions & 2 deletions README.zh-Hans.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,14 @@ TokenLink 是独立的开源项目,与 OpenAI、Anthropic、Moonshot AI、Mini

## 功能

- 原生 `MenuBarExtra` 菜单栏 + 管控中心四个页面:概览、额度源、StopWatch、设置与诊断。
- 原生 `MenuBarExtra` 菜单栏 + 管控中心五个页面:概览、额度源、成本、StopWatch、设置与诊断。
- 统一归一化多家、多窗口额度,绝不凭空推算套餐限额。
- 烧速预测:根据本地最近样本推算"按当前速度约几小时后耗尽",不额外调用 API。
- 可选合理用量参考线,标出额度窗口在均匀消耗情况下应该所处的位置。
- 可选 Beta 本地用量观测:只读扫描 Codex、Claude、Kimi CLI 的已知会话目录,
仅在本机汇总近期 token 计数。
- 成本作为独立的可选 Beta:OpenRouter/DeepSeek 官方余额保持权威数据语义;
Codex/Claude/Kimi 本地用量始终标注 `Estimated/API-equivalent`,并使用随包审阅的价格目录计价。
- macOS 系统通知:窗口额度告急、窗口重置、凭据被拒时提醒(可在设置中关闭)。
- 刷新失败时保留最近一次成功快照并标记为 stale,绝不显示虚构的实时值。
- API key 只存 macOS 钥匙串(显示名称 `TokenLink`,service
Expand Down Expand Up @@ -248,6 +250,26 @@ Token Plan」页面获取,与按量付费 API Key 不互通),并选择 Glo
保存 GLM Coding Plan API key,并选择 Global(Z.AI)或中国(BigModel)区域。
解析器保留服务端实际返回的各窗口,不根据套餐名称推算额度。

### 成本 Beta

在 **设置与诊断 → Beta 功能 → 成本** 启用后,会出现独立的成本页面。成本刷新、失败与
缓存均不影响额度刷新、通知、菜单栏严重程度或 StopWatch payload。菜单栏可以在主额度
后显示一个固定成本指标;所选数据不可用时自动退回仅显示额度。

两类成本数据绝不混为一谈:

- **权威余额**来自服务商官方账户 API。OpenRouter 独立请求 `/api/v1/credits` 与
`/api/v1/key`:显式 Management Key 可以读取账户余额,普通 API key 可能只能读取
当前 key 的支出;后者会显示为部分数据,不会凭空推算余额。DeepSeek 通过
`/user/balance` 返回余额,TokenLink 保留每一种币种和有效的零余额,不换汇,也不从
余额变化反推支出。
- **`Estimated/API-equivalent`** 根据价格目录的版本和生效日期,为近 7 天支持的本地
CLI token 记录估算等价 API 流量成本。它不估算编程订阅套餐的价值,也不会把额度百分比
换算成金额。未知模型或缺少已审阅价格的 token 类别会从金额中排除并显示警告。

成本凭据必须显式存入钥匙串;OpenRouter/DeepSeek 不复用浏览器状态、组织管理员凭据或
无关 CLI 凭据。余额和估算金额仅保存在内存中,不写入配置与诊断。

额度源启用状态和 Codex 路径的修改在重启 app 后生效;区域、账户、刷新间隔、语言、
通知开关的修改立即生效。

Expand Down Expand Up @@ -289,8 +311,10 @@ payload 送达 C152。0.2.2 候选新增完整分页、稳定优先级槽位、
- 显式 API key:只存 macOS 钥匙串。
- 非敏感配置:`~/Library/Application Support/TokenLink/config.json`,仅本人权限。
- 不读浏览器 Cookie,不需要完全磁盘访问权限,无埋点,无远端服务。
- 可选本地用量 Beta 只读取 `.codex/sessions`、`.claude/projects`、
- 可选本地用量与成本 Beta 只读取 `.codex/sessions`、`.claude/projects`、
`.kimi-code/sessions`,仅在本机提取 token 计数,不上传会话内容。
- 本地扫描按 64 KiB 分块、逐文件处理,跳过超过 50 MiB 的文件与超过 1 MiB 的记录;
不保留原始会话内容或金额快照,也没有遥测。
- 所有厂商请求都是 HTTPS,且在携带凭据前校验官方 host allowlist。
- 诊断导出前脱敏。

Expand Down Expand Up @@ -337,6 +361,19 @@ firmware/
声明式 `ProviderSpec` 注册表接入。app 是唯一的 UI 状态所有者;设备层只接收经过
显式 v1/v2 投影的字段,不接触任何凭据。

纯成本 provider 使用独立的适配器、状态、刷新协调与 UI 模型,不会获得伪造的额度快照,
也不会进入手表 payload。

## 资源与隐私门禁

常规 macOS 测试任务通过后,CI 会运行 `scripts/resource_check.sh`:将确定性的 64 MiB
工作负载流过生产 JSONL reader 与解析器,并限制最大 RSS 为 160 MiB、耗时为 30 秒、
release 可执行文件为 15 MiB。编译器进程不计入测量。

`scripts/privacy_scan.sh` 会拒绝疑似密钥,以及把余额、原始金额、Authorization header、
原始响应 body 或会话路径写入生产日志的代码。诊断测试另行保证金额、模型标识、账户
标签/UUID、错误文本与路径不会进入导出的元数据。

## 协议 v2 状态

Mac 端已经实现 payload 投影、能力协商、v1 回退、provider 轮转、三个可见工作单元、
Expand Down
8 changes: 8 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,15 @@ the old items as a recovery fallback.
token field. Neither adapter writes a CLI credential or refresh token.
- The initial destination of each credential-bearing request is validated
against its provider's HTTPS host allowlist before the request is sent.
- Authoritative cost adapters use only explicitly configured Keychain
credentials. Provider balances, spend values, local estimates, and priceable
model identifiers remain in memory and are excluded from diagnostics.
- Codex quota and task state reuse the local Codex CLI sign-in. The optional
usage observer reads only documented CLI session roots and summarizes token
counters locally.
- Local usage and cost scans accept regular files only, refuse symbolic links
and special files, enforce 50 MiB per-file and 1 MiB per-record limits while
reading, and never retain prompt or response content.
- Protocol v2 sends provider/window labels, quota values, display settings, and
up to three short visible Codex task titles/states to the explicitly bound
watch. It does not send credentials, account identifiers, raw prompts,
Expand Down Expand Up @@ -66,5 +72,7 @@ investigation before public disclosure.
Provider and hardware adapter contributions require compatibility evidence, but
that evidence must not weaken user security. Public pull requests should contain
only synthetic fixtures and redacted screenshots, recordings, or log excerpts.
Cost-provider evidence must also obscure account-specific monetary values and
must not include raw billing responses or organization identifiers.
If reproducing a security-sensitive integration requires private material,
coordinate through the reporting channels above before submitting the adapter.
Loading
Loading