From 4e0e235f97cb39931115ab1b8b5ce711cde1376e Mon Sep 17 00:00:00 2001 From: Codex Date: Tue, 14 Jul 2026 17:35:14 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=20Sub2API=20?= =?UTF-8?q?=E5=88=A9=E6=B6=A6=E6=B1=87=E6=80=BB=E6=8A=AB=E9=9C=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../unidesk-sub2api/references/codex-pool.md | 9 +- .../platform-infra-sub2api-codex/profit.ts | 144 +++++++++++++++--- .../remote-python-profit.ts | 32 +++- .../src/platform-infra-sub2api-codex/types.ts | 2 +- scripts/src/platform-infra/entry.ts | 4 +- 5 files changed, 155 insertions(+), 36 deletions(-) diff --git a/.agents/skills/unidesk-sub2api/references/codex-pool.md b/.agents/skills/unidesk-sub2api/references/codex-pool.md index 56049e47..13665de8 100644 --- a/.agents/skills/unidesk-sub2api/references/codex-pool.md +++ b/.agents/skills/unidesk-sub2api/references/codex-pool.md @@ -88,6 +88,8 @@ bun scripts/cli.ts platform-infra sub2api codex-pool runtime apply --target PK01 bun scripts/cli.ts platform-infra sub2api codex-pool runtime delete --target PK01 --account --kind model-mapping --model bun scripts/cli.ts platform-infra sub2api codex-pool profit --target PK01 --since 24h bun scripts/cli.ts platform-infra sub2api codex-pool profit --target PK01 --since 24h --json +bun scripts/cli.ts platform-infra sub2api codex-pool profit --target PK01 --since 24h --accounts +bun scripts/cli.ts platform-infra sub2api codex-pool profit --target PK01 --since 24h --missing-costs bun scripts/cli.ts platform-infra sub2api codex-pool trace --request-id bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-image status --target D601 bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-image build --target D601 --confirm @@ -99,8 +101,11 @@ bun scripts/cli.ts platform-infra sub2api codex-pool cleanup-probes --target D60 - `profit` 只读聚合 Sub2API 原生 `/api/v1/admin/usage`: - 默认窗口、盈利分组、自用分组、排除分组、人民币售价和账号采购成本来源由 `config/platform-infra/sub2api-codex-pool.yaml#profit` 声明; - 原生 `actual_cost` 是标准 API 美元计费基数;`saleRateCnyPerApiUsd` 和账号名称末尾成本都表示每 1 美元标准 API 计费对应的人民币单价,不做汇率换算; - - 默认 Kubernetes 列表按完整账号名称显示请求数、Token、标准 API 美元计费基数、人民币采购单价、人民币收入、人民币上游成本和人民币利润贡献; - - 汇总同时给出“不扣自用成本”和“扣除自用成本”两种口径,自用分组不计收入; + - 默认 Kubernetes 风格输出优先给出总收入、已确认成本、已确认利润、未知成本对应的 API USD 基数、完整性和参数化利润公式; + - 显式 `--json` 返回同一份稳定紧凑汇总,不携带完整账号数组,避免大窗口触发全局输出保护; + - `--accounts` 固定返回一页完整账号名称、账号 ID、请求数、Token、标准 API 美元计费基数、人民币采购单价、人民币收入、人民币上游成本和人民币利润贡献;存在后续页时返回基于账号 ID 的 `--page-token` 命令,不提供手工 `limit`; + - `--missing-costs` 单独返回缺失成本账号的完整名称、账号 ID、分组、分类和未知成本 API USD 基数,避免与账号分页叠加后再次触发全局输出保护; + - 汇总同时给出“不扣自用成本”和“扣除自用成本”两种口径,自用分组不计收入;缺失成本时已确认利润只表示待扣未知成本前的已知部分,最终利润必须按输出公式继续扣除未知账号成本,禁止把未知成本当作 0; - 账号名称末尾缺少采购成本系数,或存在未分类流量时,相关口径必须显示 `partial`,禁止把缺失值当作零; - `--json` 保留精确十进制字符串、原生来源、时间窗、分页数和完整性明细;命令不修改 runtime。 diff --git a/scripts/src/platform-infra-sub2api-codex/profit.ts b/scripts/src/platform-infra-sub2api-codex/profit.ts index f2199b3d..0e2b3b33 100644 --- a/scripts/src/platform-infra-sub2api-codex/profit.ts +++ b/scripts/src/platform-infra-sub2api-codex/profit.ts @@ -10,8 +10,13 @@ interface ProfitOptions { since: string; targetId: string; json: boolean; + accounts: boolean; + missingCosts: boolean; + pageToken: string | null; } +const PROFIT_ACCOUNT_PAGE_SIZE = 8; + export async function codexPoolProfit(config: UniDeskConfig, args: string[]): Promise | RenderedCliResult> { const pool = readCodexPoolConfig(); if (args.includes("--help")) return renderProfitHelp(pool.profit.defaultWindow); @@ -21,6 +26,9 @@ export async function codexPoolProfit(config: UniDeskConfig, args: string[]): Pr const remote = await runRemoteCodexPoolScript(config, "profit", profitScript(payload, pool, target), target); const report = parseJsonOutput(remote.stdout); const ok = remote.exitCode === 0 && boolField(report, "ok", false); + const projectedReport = projectProfitReport(report, options); + const baseCommand = `bun scripts/cli.ts platform-infra sub2api codex-pool profit --target ${target.id} --since ${options.since}`; + const accountPage = record(record(projectedReport).accountPage); const response = { ok, action: "platform-infra-sub2api-codex-pool-profit", @@ -30,7 +38,16 @@ export async function codexPoolProfit(config: UniDeskConfig, args: string[]): Pr mutation: false, configPath: "config/platform-infra/sub2api-codex-pool.yaml#profit", }, - report, + report: projectedReport, + disclosure: { + accountsIncluded: options.accounts, + missingCostsIncluded: options.missingCosts, + accountCount: arrayOfRecords(record(report).accounts).length, + accounts: `${baseCommand} --accounts`, + accountsNext: accountPage.nextPageToken ? `${baseCommand} --accounts --page-token ${text(accountPage.nextPageToken)}` : null, + missingCosts: `${baseCommand} --missing-costs`, + json: `${baseCommand} --json`, + }, remote: compactCapture(remote, { full: !ok || report === null }), valuesPrinted: false, }; @@ -41,6 +58,9 @@ function parseProfitOptions(args: string[], defaultWindow: string): ProfitOption let since = defaultWindow; let targetId = defaultCodexPoolRuntimeTargetId(); let json = false; + let accounts = false; + let missingCosts = false; + let pageToken: string | null = null; const readValue = (index: number, option: string): [string, number] => { const value = args[index + 1]; if (value === undefined || value.startsWith("--")) throw new Error(`${option} requires a value`); @@ -49,6 +69,11 @@ function parseProfitOptions(args: string[], defaultWindow: string): ProfitOption for (let index = 0; index < args.length; index += 1) { const arg = args[index]!; if (arg === "--json") json = true; + else if (arg === "--accounts") accounts = true; + else if (arg === "--missing-costs") missingCosts = true; + else if (arg === "--page-token") { + [pageToken, index] = readValue(index, "--page-token"); + } else if (arg.startsWith("--page-token=")) pageToken = arg.slice("--page-token=".length).trim(); else if (arg === "--since") { [since, index] = readValue(index, "--since"); } else if (arg.startsWith("--since=")) since = arg.slice("--since=".length).trim(); @@ -59,7 +84,10 @@ function parseProfitOptions(args: string[], defaultWindow: string): ProfitOption } if (!/^[1-9][0-9]*(?:s|m|h|d)$/u.test(since)) throw new Error("--since must be a duration such as 24h, 90m, or 7d"); if (!targetId || /[\r\n\0]/u.test(targetId)) throw new Error("--target has an unsupported format"); - return { since, targetId, json }; + if (accounts && missingCosts) throw new Error("--accounts and --missing-costs are separate semantic disclosures"); + if (pageToken !== null && !accounts) throw new Error("--page-token requires --accounts"); + if (pageToken !== null && (!pageToken || /[\r\n\0\s]/u.test(pageToken))) throw new Error("--page-token has an unsupported format"); + return { since, targetId, json, accounts, missingCosts, pageToken }; } function renderProfitHelp(defaultWindow: string): RenderedCliResult { @@ -69,13 +97,16 @@ function renderProfitHelp(defaultWindow: string): RenderedCliResult { renderedText: [ "SUB2API CODEX-POOL PROFIT", "Usage:", - " bun scripts/cli.ts platform-infra sub2api codex-pool profit [--since 24h] [--target PK01] [--json]", + " bun scripts/cli.ts platform-infra sub2api codex-pool profit [--since 24h] [--target PK01] [--accounts [--page-token ]|--missing-costs] [--json]", "Options:", ` --since Native usage window; YAML default is ${defaultWindow}.`, " --target ", + " --accounts Include one fixed-size account page with complete names and IDs.", + " --page-token Continue after the account ID returned by the previous page.", + " --missing-costs Include the missing-cost account list and API USD bases.", " --json Emit exact structured values.", "Notes:", - " Default output is a Kubernetes-style account list plus both profit scopes.", + " Default output is a compact summary; use --accounts or --missing-costs for semantic disclosure.", " This command is read-only and does not change Sub2API runtime state.", ].join("\n"), contentType: "text/plain", @@ -88,37 +119,57 @@ function renderProfit(response: Record): RenderedCliResult { const policy = record(report.policy); const summary = record(report.summary); const completeness = record(report.completeness); + const confirmedCosts = record(summary.confirmedCostsCny); + const confirmedProfit = record(summary.confirmedProfitCny); + const unknownBasis = record(summary.unknownCostBasisApiUsd); + const formulas = record(summary.profitFormula); + const disclosure = record(response.disclosure); const lines = [ `SUB2API PROFIT status=${text(report.status)} target=${text(record(response.target).id)} window=${text(window.since)}`, `WINDOW ${text(window.startAt)} -> ${text(window.endAt)} SOURCE native-admin-usage/${text(record(report.source).usageCostField)}`, `POLICY sale-rate=${text(policy.saleRateCnyPerApiUsd)}CNY/API_USD account-cost=${text(policy.accountCostSource)}`, "", - "ACCOUNTS", + "SUMMARY", ]; - const accounts = arrayOfRecords(report.accounts); lines.push(table( - ["GROUP", "CLASS", "ACCOUNT", "ID", "REQ", "TOKENS", "API_USD", "COST_CNY/USD", "REVENUE_CNY", "UPSTREAM_CNY", "PROFIT_CNY", "STATUS"], - accounts.map((row) => [ - text(row.groupName), text(row.class), text(row.accountName), text(row.accountId), text(row.requestCount), compactTokens(row.tokenCount), - apiUsd(row.apiAmountUsd), text(row.costRateCnyPerApiUsd), cny(row.revenueCny), cny(row.upstreamCostCny), cny(row.profitContributionCny), text(row.completeness), - ]), - )); - lines.push("", "PROFIT"); - lines.push(table( - ["SCOPE", "STATUS", "REVENUE_CNY", "PROFIT_POOL_CNY", "SELF_USE_CNY", "PROFIT_CNY"], + ["SCOPE", "STATUS", "REVENUE_CNY", "PROFIT_POOL_COST_CNY", "SELF_USE_COST_CNY", "CONFIRMED_PROFIT_CNY", "UNKNOWN_API_USD"], [ - ["不扣自用", text(record(summary.withoutSelfUse).status), cny(summary.revenueCny), cny(summary.profitPoolCostCny), "-", cny(record(summary.withoutSelfUse).profitCny)], - ["扣除自用", text(record(summary.withSelfUse).status), cny(summary.revenueCny), cny(summary.profitPoolCostCny), cny(summary.selfUseCostCny), cny(record(summary.withSelfUse).profitCny)], + ["不扣自用", text(record(summary.withoutSelfUse).status), cny(summary.revenueCny), cny(confirmedCosts.profitPool), "-", cny(confirmedProfit.withoutSelfUse), apiUsd(unknownBasis.profitPool)], + ["扣除自用", text(record(summary.withSelfUse).status), cny(summary.revenueCny), cny(confirmedCosts.profitPool), cny(confirmedCosts.selfUse), cny(confirmedProfit.withSelfUse), apiUsd(unknownBasis.total)], ], )); + lines.push("", "FORMULA"); + lines.push(`- 不扣自用: ${text(formulas.withoutSelfUse)}`); + lines.push(`- 扣除自用: ${text(formulas.withSelfUse)}`); + lines.push("", `COMPLETENESS status=${text(report.status)} missing-cost=${text(completeness.missingCostAccountCount)} unclassified=${text(completeness.unclassifiedTrafficCount)} missing-groups=${text(completeness.configuredGroupMissingCount)}`); + lines.push(`NEXT accounts: ${text(disclosure.accounts)}`); + lines.push(`NEXT missing-costs: ${text(disclosure.missingCosts)}`); + const accounts = arrayOfRecords(report.accounts); + if (accounts.length > 0) { + lines.push("", "ACCOUNTS"); + lines.push(table( + ["GROUP", "CLASS", "ACCOUNT", "ID", "REQ", "TOKENS", "API_USD", "COST_CNY/USD", "REVENUE_CNY", "UPSTREAM_CNY", "PROFIT_CNY", "STATUS"], + accounts.map((row) => [ + text(row.groupName), text(row.class), text(row.accountName), text(row.accountId), text(row.requestCount), compactTokens(row.tokenCount), + apiUsd(row.apiAmountUsd), text(row.costRateCnyPerApiUsd), cny(row.revenueCny), cny(row.upstreamCostCny), cny(row.profitContributionCny), text(row.completeness), + ]), + )); + const accountPage = record(report.accountPage); + if (accountPage.nextPageToken) lines.push(`NEXT account-page: ${text(disclosure.accountsNext)}`); + } const missingCosts = arrayOfRecords(completeness.missingCostAccounts); - const unclassified = arrayOfRecords(completeness.unclassifiedTraffic); - const missingGroups = Array.isArray(completeness.configuredGroupsMissingAtRuntime) ? completeness.configuredGroupsMissingAtRuntime : []; - if (missingCosts.length || unclassified.length || missingGroups.length) { - lines.push("", "COMPLETENESS"); - for (const row of missingCosts) lines.push(`- missing-cost group=${text(row.groupName)} account=${text(row.accountName)} id=${text(row.accountId)}`); - for (const row of unclassified) lines.push(`- unclassified-traffic group=${text(row.groupName)} account=${text(row.accountName)} requests=${text(row.requestCount)}`); - for (const group of missingGroups) lines.push(`- configured-group-missing ${text(group)}`); + if (missingCosts.length > 0) { + lines.push("", "MISSING COST ACCOUNTS"); + for (const row of missingCosts) lines.push(`- class=${text(row.class)} group=${text(row.groupName)} account=${text(row.accountName)} id=${text(row.accountId)} api-usd=${text(row.apiAmountUsd)}`); + } + if (accounts.length > 0 || missingCosts.length > 0) { + const unclassified = arrayOfRecords(completeness.unclassifiedTraffic); + const missingGroups = Array.isArray(completeness.configuredGroupsMissingAtRuntime) ? completeness.configuredGroupsMissingAtRuntime : []; + if (unclassified.length || missingGroups.length) { + lines.push("", "COMPLETENESS DETAILS"); + for (const row of unclassified) lines.push(`- unclassified-traffic group=${text(row.groupName)} account=${text(row.accountName)} requests=${text(row.requestCount)}`); + for (const group of missingGroups) lines.push(`- configured-group-missing ${text(group)}`); + } } return { ok: response.ok === true, @@ -129,6 +180,51 @@ function renderProfit(response: Record): RenderedCliResult { }; } +function projectProfitReport(report: Record | null, options: ProfitOptions): Record | null { + if (report === null) return null; + const { accounts: rawAccounts, completeness: rawCompleteness, ...summaryReport } = report; + const accounts = arrayOfRecords(rawAccounts); + const completeness = record(rawCompleteness); + const compactCompleteness = { + missingCostAccountCount: arrayOfRecords(completeness.missingCostAccounts).length, + unclassifiedTrafficCount: arrayOfRecords(completeness.unclassifiedTraffic).length, + configuredGroupMissingCount: Array.isArray(completeness.configuredGroupsMissingAtRuntime) ? completeness.configuredGroupsMissingAtRuntime.length : 0, + }; + const compactReport = { + ...summaryReport, + accountCount: accounts.length, + completeness: compactCompleteness, + }; + if (options.missingCosts) { + return { + ...compactReport, + completeness: { + ...compactCompleteness, + missingCostAccounts: arrayOfRecords(completeness.missingCostAccounts), + }, + }; + } + if (!options.accounts) return compactReport; + let startIndex = 0; + if (options.pageToken !== null) { + const tokenIndex = accounts.findIndex((row) => text(row.accountId) === options.pageToken); + if (tokenIndex < 0) throw new Error(`profit account page token not found: ${options.pageToken}`); + startIndex = tokenIndex + 1; + } + const pageAccounts = accounts.slice(startIndex, startIndex + PROFIT_ACCOUNT_PAGE_SIZE); + const hasNext = startIndex + pageAccounts.length < accounts.length; + const nextPageToken = hasNext ? text(pageAccounts.at(-1)?.accountId) : null; + return { + ...compactReport, + accounts: pageAccounts, + accountPage: { + pageSize: PROFIT_ACCOUNT_PAGE_SIZE, + returned: pageAccounts.length, + nextPageToken, + }, + }; +} + function renderProfitJson(response: Record): RenderedCliResult { return { ok: response.ok === true, diff --git a/scripts/src/platform-infra-sub2api-codex/remote-python-profit.ts b/scripts/src/platform-infra-sub2api-codex/remote-python-profit.ts index 405f7a34..15719312 100644 --- a/scripts/src/platform-infra-sub2api-codex/remote-python-profit.ts +++ b/scripts/src/platform-infra-sub2api-codex/remote-python-profit.ts @@ -178,8 +178,10 @@ def run_profit_report(): revenue = sum((profit_decimal(row["revenueCny"]) for row in profit_accounts), profit_decimal(0)) profit_cost = sum((profit_decimal(row["upstreamCostCny"]) for row in profit_accounts if row["upstreamCostCny"] is not None), profit_decimal(0)) self_cost = sum((profit_decimal(row["upstreamCostCny"]) for row in self_accounts if row["upstreamCostCny"] is not None), profit_decimal(0)) - without_self = revenue - profit_cost - with_self = without_self - self_cost + unknown_profit_api_usd = sum((profit_decimal(row["apiAmountUsd"]) for row in missing_profit), profit_decimal(0)) + unknown_self_api_usd = sum((profit_decimal(row["apiAmountUsd"]) for row in missing_self), profit_decimal(0)) + confirmed_without_self = revenue - profit_cost + confirmed_with_self = confirmed_without_self - self_cost without_complete = not missing_profit and not unclassified with_complete = without_complete and not missing_self account_rows.sort(key=lambda row: (row["class"], row["groupName"], -profit_decimal(row["apiAmountUsd"]), str(row["accountName"]))) @@ -194,13 +196,29 @@ def run_profit_report(): "accounts": account_rows, "summary": { "revenueCny": profit_decimal_text(revenue), - "profitPoolCostCny": profit_decimal_text(profit_cost), - "selfUseCostCny": profit_decimal_text(self_cost), - "withoutSelfUse": {"status": "complete" if without_complete else "partial", "profitCny": profit_decimal_text(without_self) if without_complete else None}, - "withSelfUse": {"status": "complete" if with_complete else "partial", "profitCny": profit_decimal_text(with_self) if with_complete else None}, + "confirmedCostsCny": { + "profitPool": profit_decimal_text(profit_cost), + "selfUse": profit_decimal_text(self_cost), + "total": profit_decimal_text(profit_cost + self_cost), + }, + "confirmedProfitCny": { + "withoutSelfUse": profit_decimal_text(confirmed_without_self), + "withSelfUse": profit_decimal_text(confirmed_with_self), + }, + "unknownCostBasisApiUsd": { + "profitPool": profit_decimal_text(unknown_profit_api_usd), + "selfUse": profit_decimal_text(unknown_self_api_usd), + "total": profit_decimal_text(unknown_profit_api_usd + unknown_self_api_usd), + }, + "profitFormula": { + "withoutSelfUse": "confirmedProfitCny.withoutSelfUse - sum(missing profit account apiAmountUsd * unknown costRateCnyPerApiUsd)", + "withSelfUse": "confirmedProfitCny.withSelfUse - sum(all missing account apiAmountUsd * unknown costRateCnyPerApiUsd)", + }, + "withoutSelfUse": {"status": "complete" if without_complete else "partial", "profitCny": profit_decimal_text(confirmed_without_self) if without_complete else None}, + "withSelfUse": {"status": "complete" if with_complete else "partial", "profitCny": profit_decimal_text(confirmed_with_self) if with_complete else None}, }, "completeness": { - "missingCostAccounts": [{"groupName": row["groupName"], "accountId": row["accountId"], "accountName": row["accountName"]} for row in missing_profit + missing_self], + "missingCostAccounts": [{"class": row["class"], "groupName": row["groupName"], "accountId": row["accountId"], "accountName": row["accountName"], "apiAmountUsd": row["apiAmountUsd"]} for row in missing_profit + missing_self], "unclassifiedTraffic": [{"groupName": row["groupName"], "accountId": row["accountId"], "accountName": row["accountName"], "requestCount": row["requestCount"]} for row in unclassified], "configuredGroupsMissingAtRuntime": sorted(configured_names - runtime_names), }, diff --git a/scripts/src/platform-infra-sub2api-codex/types.ts b/scripts/src/platform-infra-sub2api-codex/types.ts index 08d407ae..d20f6fb2 100644 --- a/scripts/src/platform-infra-sub2api-codex/types.ts +++ b/scripts/src/platform-infra-sub2api-codex/types.ts @@ -374,7 +374,7 @@ export function codexPoolHelp(): unknown { "bun scripts/cli.ts platform-infra sub2api codex-pool runtime get --account [--target PK01] [--json|--full|--raw]", "bun scripts/cli.ts platform-infra sub2api codex-pool runtime errors [--group |--all-groups] [--platform ] [--page-token ] [--account ] [--since 24h] [--tail 50000] [--target PK01] [--json|--full|--raw]", "bun scripts/cli.ts platform-infra sub2api codex-pool faults [--level P0|P1|P2] [--group ] [--account ] [--model ] [--stream sync|stream] [--endpoint ] [--request-id ] [--page-token ] [--target PK01] [--json]", - "bun scripts/cli.ts platform-infra sub2api codex-pool profit [--since 24h] [--target PK01] [--json]", + "bun scripts/cli.ts platform-infra sub2api codex-pool profit [--since 24h] [--target PK01] [--accounts [--page-token ]|--missing-costs] [--json]", "bun scripts/cli.ts platform-infra sub2api codex-pool runtime apply --account --template [--target PK01] [--confirm]", "bun scripts/cli.ts platform-infra sub2api codex-pool runtime apply --accounts --template [--target PK01] [--confirm] [--full|--raw]", "bun scripts/cli.ts platform-infra sub2api codex-pool runtime delete --account --kind temp-unschedulable [--target PK01] [--confirm]", diff --git a/scripts/src/platform-infra/entry.ts b/scripts/src/platform-infra/entry.ts index d8f39c4a..0e980894 100644 --- a/scripts/src/platform-infra/entry.ts +++ b/scripts/src/platform-infra/entry.ts @@ -424,7 +424,7 @@ export function platformInfraHelp(): unknown { "bun scripts/cli.ts platform-infra sub2api codex-pool validate", "bun scripts/cli.ts platform-infra sub2api codex-pool trace --request-id ", "bun scripts/cli.ts platform-infra sub2api codex-pool feedback --user [--window 30m] [--page-token |--request-id ] [--json]", - "bun scripts/cli.ts platform-infra sub2api codex-pool profit [--since 24h] [--target PK01] [--json]", + "bun scripts/cli.ts platform-infra sub2api codex-pool profit [--since 24h] [--target PK01] [--accounts [--page-token ]|--missing-costs] [--json]", "bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-image status", "bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-probe --account unidesk-codex-hy --confirm", "bun scripts/cli.ts platform-infra sub2rank plan [--target NC01]", @@ -508,7 +508,7 @@ export function platformInfraHelp(): unknown { "bun scripts/cli.ts platform-infra sub2api codex-pool validate", "bun scripts/cli.ts platform-infra sub2api codex-pool trace --request-id ", "bun scripts/cli.ts platform-infra sub2api codex-pool feedback --user [--window 30m] [--page-token |--request-id ] [--json]", - "bun scripts/cli.ts platform-infra sub2api codex-pool profit [--since 24h] [--target PK01] [--json]", + "bun scripts/cli.ts platform-infra sub2api codex-pool profit [--since 24h] [--target PK01] [--accounts [--page-token ]|--missing-costs] [--json]", "bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-image status", ], module: "scripts/src/platform-infra-sub2api-codex.ts",