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
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ TokenStep 是一个 macOS 菜单栏 App,用来本地统计你在 Codex、Claud

下载最新版 DMG,打开后把 `TokenStep.app` 拖进「应用程序」即可使用:

[下载 TokenStep 最新版](https://github.com/Backtthefuture/TokenStep/releases/latest/download/TokenStep-0.2.13.dmg)
[下载 TokenStep 最新版](https://github.com/Backtthefuture/TokenStep/releases/latest/download/TokenStep-0.2.14.dmg)

也可以从 Release 页面查看所有版本:

Expand All @@ -32,6 +32,10 @@ TokenStep 已使用 Developer ID 签名并通过 Apple 公证。首次打开时

Windows版本由十七做了移植,欢迎大家前往使用:https://github.com/canyexuanfan/TokenStep-Windows/releases

## 0.2.14 更新:恢复 AgentWork 榜单关联

兼容 Token Rank 新版状态文件中的账号信息,同时保留旧版格式支持。已绑定的用户升级后可重新识别身份,无需删除状态文件或重新绑定。详见 [0.2.14 发布说明](docs/RELEASE_NOTES_0.2.14.md)。

## 0.2.13 更新:修复自动更新的 Apple 公证门禁

0.2.13 修复了 0.2.12 因发布包遗漏 Apple 公证票据而无法自动安装的问题。新版已经过 Developer ID 签名、Apple 公证、App/DMG 票据装订、macOS 分发策略检查和隔离更新安装验证;不需要关闭 Gatekeeper 或更改系统安全设置。发布流程也改为“先草稿上传、回下载验收,再公开”,未公证产物无法进入公开 Release。完整说明见 [0.2.13 发布说明](docs/RELEASE_NOTES_0.2.13.md)。
Expand Down Expand Up @@ -150,7 +154,7 @@ TokenStep 第一次从“更换配色”升级为完整的**主题皮肤包系
- 新增奥德赛弓箭阶梯 Logo;用量采用骨金、冷金或余烬橙,绿色只保留给同步成功等状态反馈。
- 关闭排行榜后浮层会自动收短,多来源额度则使用紧凑布局完整展示。

打开 `设置 → 通用 → 主题皮肤包` 即可切换。完整说明见 [0.2.4 发布说明](docs/RELEASE_NOTES_0.2.4.md),或直接[下载已签名并通过 Apple 公证的最新版](https://github.com/Backtthefuture/TokenStep/releases/latest/download/TokenStep-0.2.13.dmg)。
打开 `设置 → 通用 → 主题皮肤包` 即可切换。完整说明见 [0.2.4 发布说明](docs/RELEASE_NOTES_0.2.4.md),或直接[下载已签名并通过 Apple 公证的最新版](https://github.com/Backtthefuture/TokenStep/releases/latest/download/TokenStep-0.2.14.dmg)。

## TokenStep 适合谁?

Expand Down Expand Up @@ -209,7 +213,7 @@ TokenStep 默认只做本地统计。

## 安装方式

1. 下载 [TokenStep 最新版 DMG](https://github.com/Backtthefuture/TokenStep/releases/latest/download/TokenStep-0.2.13.dmg)。
1. 下载 [TokenStep 最新版 DMG](https://github.com/Backtthefuture/TokenStep/releases/latest/download/TokenStep-0.2.14.dmg)。
2. 打开 DMG。
3. 把 `TokenStep.app` 拖到「应用程序」。
4. 启动 TokenStep。
Expand Down Expand Up @@ -269,7 +273,7 @@ TokenStepSwift/dist/TokenStep.app
公开发布强制执行 Developer ID 签名、Apple 公证、票据装订、系统分发检查和隔离安装验证。不再生成可被误上传的“仅签名、未公证”发布包:

```bash
TOKENSTEP_VERSION=0.2.13 \
TOKENSTEP_VERSION=0.2.14 \
CODE_SIGN_IDENTITY="Developer ID Application: Your Name (TEAMID)" \
TOKENSTEP_NOTARY_PROFILE="tokenstep-notary" \
./script/package_release.sh --notarize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ enum AgentWorkRankService {
.appendingPathComponent(".token-rank/client-state.json")
) -> AgentWorkRankIdentity? {
guard let data = try? Data(contentsOf: clientStateURL),
let state = try? JSONDecoder().decode(LocalClientState.self, from: data),
let state = try? JSONDecoder().decode(LocalClientStateDocument.self, from: data).state,
let user = state.user,
user.id > 0
else {
Expand All @@ -91,6 +91,34 @@ enum AgentWorkRankService {
}
}

// Read a public identity projection only. Credential and checksum validation
// remain the uploader's responsibility; this reader never writes its state.
private struct LocalClientStateDocument: Decodable {
let state: LocalClientState

enum CodingKeys: String, CodingKey {
case schemaVersion = "schema_version"
case payload
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if container.contains(.schemaVersion) || container.contains(.payload) {
let version = try container.decode(Int.self, forKey: .schemaVersion)
guard version == 2 || version == 3 else {
throw DecodingError.dataCorruptedError(
forKey: .schemaVersion,
in: container,
debugDescription: "Unsupported Token Rank state format"
)
}
state = try container.decode(LocalClientState.self, forKey: .payload)
} else {
state = try LocalClientState(from: decoder)
}
}
}

private struct LocalClientState: Decodable {
var user: LocalClientUser?
var lastSuccessfulSyncAt: String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,53 @@ final class AgentWorkRankServiceTests: XCTestCase {
XCTAssertNotNil(identity.lastSyncedAt)
}

private func identity(from json: String) throws -> AgentWorkRankIdentity? {
let url = FileManager.default.temporaryDirectory
.appendingPathComponent("rank-state-\(UUID().uuidString).json")
defer { try? FileManager.default.removeItem(at: url) }
try Data(json.utf8).write(to: url)
return AgentWorkRankService.loadLocalIdentity(clientStateURL: url)
}

func testVersionedStatesReadPayloadIdentityAndSyncTime() throws {
for version in [2, 3] {
let result = try XCTUnwrap(identity(from: """
{
"schema_version": \(version), "state_revision": 7,
"checksum": "ignored-by-public-identity-reader",
"user": {"id": 99, "name": "Stale root identity"},
"payload": {
"device_token": "never-decoded",
"last_successful_sync_at": "2026-09-05T08:00:00Z",
"user": {"id": 4, "name": "Agent User", "avatar_url": "https://example.com/a.png"}
}
}
"""))
XCTAssertEqual(result.id, 4)
XCTAssertEqual(result.name, "Agent User")
XCTAssertEqual(result.avatarURL, "https://example.com/a.png")
XCTAssertEqual(result.lastSyncedAt, ISO8601DateFormatter().date(from: "2026-09-05T08:00:00Z"))
}
}

func testInvalidEnvelopesNeverFallBackToRootIdentity() throws {
let rootUser = "\"user\":{\"id\":99,\"name\":\"Stale\"}"
for fields in [
"\"schema_version\":4,\"payload\":{}",
"\"schema_version\":3",
"\"schema_version\":3,\"payload\":null",
"\"schema_version\":3,\"payload\":[]",
"\"schema_version\":3,\"payload\":{}",
"\"schema_version\":null,\"payload\":{}",
"\"payload\":{}",
"\"schema_version\":3,\"payload\":{\"user\":{\"id\":0,\"name\":\"Invalid\"}}"
] {
XCTAssertNil(try identity(from: "{\(rootUser),\(fields)}"), fields)
}
XCTAssertNil(try identity(from: "not json"))
XCTAssertNil(try identity(from: "{}"))
}

func testLegacyShengcaiSettingsMigrateToAutomaticAgentWorkRank() throws {
let data = Data("""
{
Expand Down
9 changes: 9 additions & 0 deletions docs/RELEASE_NOTES_0.2.14.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# TokenStep 0.2.14

修复升级 Token Rank 后,TokenStep 显示“尚未关联”或自动隐藏 AgentWork 榜单的问题。

- 同时兼容旧版平面状态与 v2/v3 封装状态,正确读取账号及同步时间。
- 遇到无效或未知版本的封装,不回退使用顶层残留账号。
- 保留榜单三种显示模式;不修改采集器状态,不读取凭据用于联网请求。

已绑定用户无需重新绑定,安装新版后刷新即可。此更新修复身份识别,不改变采集计量与上传逻辑。
2 changes: 1 addition & 1 deletion script/build_swiftui_and_run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ HELPER_EXECUTABLE="$BUILD_DIR/$HELPER_NAME"
ICON_FILE="$ROOT_DIR/TokenUsageMenuApp/assets/TokenStepIcon.icns"
ODYSSEY_ASSET_DIR="$ROOT_DIR/TokenUsageMenuApp/assets/odyssey"
INTERSTELLAR_ASSET_DIR="$ROOT_DIR/TokenUsageMenuApp/assets/interstellar"
VERSION="${TOKENSTEP_VERSION:-0.2.13}"
VERSION="${TOKENSTEP_VERSION:-0.2.14}"
LAUNCH=true
VERIFY=false

Expand Down
Loading