feat: v2.8.0 — Artifacts uploads and plugin links, console tab combos, account avatars
- Artifacts hub: upload a file from the computer; make a skill by uploading a Markdown file, files, a folder or a .zip/.skill, or by writing it in the dialog; add plugins by picking from the list a marketplace link answers (POST /api/artifacts/plugin/inspect); Settings → AI tools → Artifacts Hub. - Console tabs are combos of up to four split panes (ConsoleTabs, PaneGrid). - Accounts: an own avatar (PUT/DELETE /api/me/avatar, raster only, ≤ 1 MB, <data dir>/avatars/), shown with name and role at the sidebar's foot and on the workspace owner groups (GET /api/users/:id/avatar, owner_avatar_at). - Users page: per-user storage (GET /api/users/storage, measured in a worker) and account-pool spend/tokens per account (server/lib/pool-usage.js). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -155,29 +155,12 @@ function livePanes(account) {
|
||||
}
|
||||
|
||||
/**
|
||||
* $ of the Codex sessions this account's runs started since `hours` ago.
|
||||
* $ and tokens of the last 5 h / 7 d (pool-usage.js).
|
||||
* ponytail: Codex usage has no hourly table, so a session counts whole once
|
||||
* its run started in the window; add hourly rows if that skews a ranking.
|
||||
*/
|
||||
function spendSince(account, hours) {
|
||||
const { db, stmts } = require("../db");
|
||||
const { calculateCost } = require("../routes/pricing");
|
||||
const since = new Date(Date.now() - hours * 3600e3).toISOString();
|
||||
const rows = db
|
||||
.prepare(
|
||||
`SELECT model, speed, inference_geo, service_tier,
|
||||
SUM(input_tokens) AS input_tokens, SUM(output_tokens) AS output_tokens,
|
||||
SUM(cache_read_tokens) AS cache_read_tokens,
|
||||
SUM(cache_write_tokens) AS cache_write_tokens
|
||||
FROM token_usage
|
||||
WHERE session_id IN (SELECT session_id FROM dashboard_runs
|
||||
WHERE tool = 'codex' AND account = ?
|
||||
AND session_id IS NOT NULL AND started_at >= ?)
|
||||
GROUP BY model, speed, inference_geo, service_tier`
|
||||
)
|
||||
.all(account, since);
|
||||
return rows.length ? calculateCost(rows, stmts.listPricing.all()).total_cost : 0;
|
||||
}
|
||||
const usageOf = (account) =>
|
||||
require("./pool-usage").accountUsage("codex", account, { wholeSession: true });
|
||||
|
||||
// ── Quota, from the account's own rollouts ─────────────────────────────────
|
||||
|
||||
@@ -262,8 +245,7 @@ function poolStatus() {
|
||||
kind: a.kind,
|
||||
addedAt: a.addedAt,
|
||||
livePanes: livePanes(a.name),
|
||||
spend5h: spendSince(a.name, 5),
|
||||
spend7d: spendSince(a.name, 24 * 7),
|
||||
...usageOf(a.name),
|
||||
quota: quotas().get(a.name) || null,
|
||||
quotaPercent: percent,
|
||||
full: percent != null && percent >= BLOCK_PERCENT,
|
||||
@@ -374,13 +356,14 @@ function pick(member) {
|
||||
const ranked = usable
|
||||
.map((a) => {
|
||||
const percent = quotaPercent(a.name);
|
||||
const u = usageOf(a.name);
|
||||
return {
|
||||
a,
|
||||
key: [
|
||||
percent != null && percent >= BLOCK_PERCENT ? 1 : 0,
|
||||
livePanes(a.name),
|
||||
spendSince(a.name, 5),
|
||||
spendSince(a.name, 24 * 7),
|
||||
u.spend5h,
|
||||
u.spend7d,
|
||||
],
|
||||
};
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user