diff --git a/AGENTS.md b/AGENTS.md index f17fc52..aebf8c6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -65,7 +65,7 @@ paseo agent archive # 测完归档,别 - **发布代码禁止 import `@getpaseo/client` / `@getpaseo/protocol`**(含 type-only)。宿主编译插件时会解析 type-only import,但只提供 SDK specifier、zod、react、react-native、@tanstack/react-query、@types/node。git 安装不跑 `npm install`(只跑 manifest `build`),npm 安装不装 devDependencies,所以其他类型依赖会让安装失败(0.11.1 实测 `Could not resolve type dependency`)。客户端类型从 `client/paseo-types.ts` 取,服务端从 `server/paseo-types.ts` 取(都由宿主 `PaseoApi` 推导)。`shared/` 不能 import `/client` 或 `/server` SDK,用结构类型和泛型。测试文件不进 bundle,可以继续用 devDependencies。 - 改动依赖或 import 后,用无 `node_modules` 的副本验证:复制源码到临时目录、改 manifest id、让 contribute 提前返回,`paseo plugin add <目录>`,看到 "Plugin ready" 后 `paseo plugin remove` 并删除目录。 -- 总览是 `addScreen`(id `ledger-overview` 不变),`range` 放在 screen params 里(`today`/`7d`/`30d`/`all`,缺省 all)。screen props 没有 setParams;切换范围用 `client.openScreen`(宿主 router.push,会产生历史记录),标题函数随 params 变化。 +- 总览是 `addScreen`(id `ledger-overview` 不变),`range` 放在 screen params 里(`today`/`7d`/`30d`/`all`,缺省 all)作为**初始**范围;屏内切换是组件 state。screen props 没有 setParams,`client.openScreen` 是宿主 router.push——每调一次叠一条历史,所以屏内切换不能走导航(曾导致点 range 按钮刷屏历史记录);标题用静态字符串,params 派生标题在本地切换后会过期。 - 侧边栏是 `addSidebarFooterItem` + `SidebarRow`,trailing 显示今日费用。"今天"按客户端本地午夜计算,以 ISO `since` 传给 `ledger.overview`;服务端按 `Date.parse(endedAt)` 过滤 read-model 行,不在服务端猜时区。 - `addSurface` / `addSidebarItem` / `openSurface` 已弃用,不要再用。不接 `registerUsageSource`(那是订阅额度窗口,不是逐轮 usage;用户已决定不放进 Usage 面板)。 - 0.11.1 仍未公开 cache-write 和 provider 内部子代理 usage。 diff --git a/README.md b/README.md index 7b5e990..59fed1c 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Per-turn LLM token usage and cost for [Paseo](https://paseo.sh) agents — an ag **Sidebar** — a **TokenLedger** row in the sidebar footer shows today's cost (or tokens when no price is known), with an activity icon while a turn is running. "Today" starts at local midnight on the device that shows it. -**Overview** (sidebar footer → TokenLedger, or the command center) — all sessions in one place: grand totals, then per-agent rows grouped by workspace with live-turn indicator, last activity, cost/tokens, and turn count. Pick **Today**, **Last 7 days**, **Last 30 days**, or **All time**; the range is part of the screen link and its title. A range lists agents with turns that ended in it, plus agents with a turn in flight. Tapping a row jumps to that agent. +**Overview** (sidebar footer → TokenLedger, or the command center) — all sessions in one place: grand totals, then per-agent rows grouped by workspace with live-turn indicator, last activity, cost/tokens, and turn count. Pick **Today**, **Last 7 days**, **Last 30 days**, or **All time**. The sidebar row opens it on Today; the command center opens All time. A range lists agents with turns that ended in it, plus agents with a turn in flight. Tapping a row jumps to that agent.

TokenLedger overview on Last 7 days with demo data: range buttons, totals for all sessions, and agent rows grouped by workspace; the TokenLedger row in the sidebar footer shows today's cost diff --git a/client/overview.tsx b/client/overview.tsx index 39b300f..838307a 100644 --- a/client/overview.tsx +++ b/client/overview.tsx @@ -2,7 +2,7 @@ import { useDenseLayout } from "./layout.ts"; import type { PluginTheme } from "@getpaseo/plugin"; import { type PluginScreenProps } from "@getpaseo/plugin/client"; import { ScrollView } from "@getpaseo/plugin/client/react-native"; -import { useMemo } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useOverview } from "./data.ts"; import { Pressable, Text, View } from "react-native"; import type { AgentUsageRow } from "../shared/ledger.ts"; @@ -99,15 +99,16 @@ function RangePicker({ range, theme, onRange }: { range: Range; theme: PluginThe ); } -/** The range lives in the screen params, so the header title and deep links follow it. */ +/** Params seed the initial range; switching is local — openScreen is router.push and stacks history. */ export function TokenLedgerOverview({ theme, layout, navigation, params, - onRange, -}: PluginScreenProps & { onRange: (range: Range) => void }) { - const range = parseRange(params.range); +}: PluginScreenProps) { + const paramRange = parseRange(params.range); + const [range, setRange] = useState(paramRange); + useEffect(() => setRange(paramRange), [paramRange]); const { data, error } = useOverview(useRangeStart(range)); const { dense, setWidth } = useDenseLayout(layout.compact); @@ -129,7 +130,7 @@ export function TokenLedgerOverview({ return ( setWidth(event.nativeEvent.layout.width)}> - + {error ? {String(error)} : null} {data ? ( <> diff --git a/index.client.tsx b/index.client.tsx index a5978d3..f8f3ae8 100644 --- a/index.client.tsx +++ b/index.client.tsx @@ -1,8 +1,8 @@ -import type { PluginClientContext, PluginScreenProps } from "@getpaseo/plugin/client"; +import type { PluginClientContext } from "@getpaseo/plugin/client"; import { TokenLedgerOverview } from "./client/overview.tsx"; import { TokenLedgerPanel } from "./client/panel.tsx"; import { contributePills } from "./client/pill.tsx"; -import { OVERVIEW_SCREEN, parseRange, RANGE_LABELS } from "./client/range.ts"; +import { OVERVIEW_SCREEN } from "./client/range.ts"; import { LedgerSidebarItem } from "./client/sidebar.tsx"; import { createPanelPlacement } from "./client/layout.ts"; @@ -22,13 +22,10 @@ export default function contribute(client: PluginClientContext) { locations: ["workspace", "explorer"], Component: TokenLedgerPanel, }); - function OverviewScreen(props: PluginScreenProps) { - return client.openScreen({ screenId: OVERVIEW_SCREEN, params: { range } })} />; - } client.addScreen({ id: OVERVIEW_SCREEN, - title: (params) => `TokenLedger · ${RANGE_LABELS[parseRange(params.range)]}`, - Component: OverviewScreen, + title: "TokenLedger", + Component: TokenLedgerOverview, }); client.addSidebarFooterItem({ id: "ledger-today", title: "TokenLedger", Component: LedgerSidebarItem }); client.addCommandCenterItem({