@coreplane/switchboard 1.239.0 → 1.240.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/config/config.example.yaml +6 -0
- package/dist/assets/deploy/cloudflare-memory/worker.ts +115 -1
- package/dist/assets/deploy/cloudflare-memory/wrangler.template.jsonc +7 -1
- package/dist/assets/deploy/cloudflare-resident/worker.ts +223 -70
- package/dist/assets/deploy/secrets.manifest.json +1 -1
- package/dist/assets/package-lock.json +207 -3
- package/dist/assets/package.json +2 -1
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/core/authz/actor.ts +33 -2
- package/dist/assets/src/core/authz/policy.ts +6 -0
- package/dist/assets/src/core/coordinator/contract.ts +12 -1
- package/dist/assets/src/core/coordinator/driver.ts +13 -0
- package/dist/assets/src/core/costs.ts +1114 -0
- package/dist/assets/src/core/costsSnapshotStore.ts +209 -0
- package/dist/assets/src/core/delivery.ts +4 -11
- package/dist/assets/src/core/deliverySnapshotStore.ts +12 -28
- package/dist/assets/src/core/ingressTokens.ts +27 -13
- package/dist/assets/src/core/runLedger/types.ts +3 -0
- package/dist/assets/src/core/runRecord.ts +10 -3
- package/dist/assets/src/core/schedules.ts +2 -1
- package/dist/assets/src/core/ship/coordinator.ts +70 -0
- package/dist/assets/src/core/snapshotAge.ts +30 -0
- package/dist/assets/src/core/stateWorkerRef.ts +41 -0
- package/dist/assets/src/execution/residentAutoRebuild.ts +73 -0
- package/dist/assets/src/execution/residentRefresh.ts +25 -3
- package/dist/assets/src/execution/restoreWaiters.ts +61 -0
- package/dist/assets/src/mcp/registry.ts +7 -0
- package/dist/assets/web/dist/.vite/manifest.json +507 -427
- package/dist/assets/web/dist/assets/AppShell-CM_fh-WW.js +1 -0
- package/dist/assets/web/dist/assets/CostsPage-BD3w5j_S.js +2 -0
- package/dist/assets/web/dist/assets/DeliveryPage-BtvzKA1m.js +1 -0
- package/dist/assets/web/dist/assets/HomePage-BU6aRCSe.js +2 -0
- package/dist/assets/web/dist/assets/NotFoundPage-DwOX1HaZ.js +1 -0
- package/dist/assets/web/dist/assets/{ResidentDetailPage-CrfCd8fx.js → ResidentDetailPage-CUAOh5d9.js} +1 -1
- package/dist/assets/web/dist/assets/ResidentsIndexPage-B3KUlYy2.js +1 -0
- package/dist/assets/web/dist/assets/RunFoldRow-Dulcbg25.js +1 -0
- package/dist/assets/web/dist/assets/RunRoutePage-DGoGrQZq.js +6 -0
- package/dist/assets/web/dist/assets/RunsIndexPage-37W7OZbC.js +1 -0
- package/dist/assets/web/dist/assets/{RunsTabs-XpCqeWu4.js → RunsTabs-BnkvCBrM.js} +1 -1
- package/dist/assets/web/dist/assets/ScheduledPage-C1y9jgAk.js +1 -0
- package/dist/assets/web/dist/assets/SettingsPage-ggfB-Rqr.js +1 -0
- package/dist/assets/web/dist/assets/SlackMark-VDNs7Vjh.js +1 -0
- package/dist/assets/web/dist/assets/Slideover-CX-LEklF.js +1 -0
- package/dist/assets/web/dist/assets/{StatusDot-DkXALvKf.js → StatusDot-CiByEtoR.js} +1 -1
- package/dist/assets/web/dist/assets/{Tooltip-DtLIyi5P.js → Tooltip-DyRxk8A8.js} +1 -1
- package/dist/assets/web/dist/assets/UnitRoutePage-CnWyOizV.js +1 -0
- package/dist/assets/web/dist/assets/{angular-html-DYtV9uIN.js → angular-html-DAQ_1T1v.js} +1 -1
- package/dist/assets/web/dist/assets/{angular-ts-DF2P25Zp.js → angular-ts-D4kxRVp1.js} +1 -1
- package/dist/assets/web/dist/assets/{apl-Ff96wxko.js → apl-Dy07l64y.js} +1 -1
- package/dist/assets/web/dist/assets/{astro-LbBnZna-.js → astro-DZIbTlc7.js} +1 -1
- package/dist/assets/web/dist/assets/{blade-DltuxeBW.js → blade-CmSXWX3l.js} +1 -1
- package/dist/assets/web/dist/assets/{c-DBg7yuPx.js → c-ggzVdJLK.js} +1 -1
- package/dist/assets/web/dist/assets/{chapel-C83hUAds.js → chapel-Bo1wktsG.js} +1 -1
- package/dist/assets/web/dist/assets/{cobol-DB0yV_2D.js → cobol-DJUmtKM2.js} +1 -1
- package/dist/assets/web/dist/assets/{coffee-Bpih6Kzl.js → coffee-BZWOVz4C.js} +1 -1
- package/dist/assets/web/dist/assets/{cpp-CU00ZeRn.js → cpp-Dtk39yMw.js} +1 -1
- package/dist/assets/web/dist/assets/{crystal-f8ImJkux.js → crystal-CGf_-Zoj.js} +1 -1
- package/dist/assets/web/dist/assets/{css-4VrC4ISo.js → css-CX2fcL4u.js} +1 -1
- package/dist/assets/web/dist/assets/delivery-CTdOqpVK.js +1 -0
- package/dist/assets/web/dist/assets/{dist-DtA7JiH5.js → dist-D8TaC961.js} +2 -2
- package/dist/assets/web/dist/assets/durationTone-DXG-3R7_.js +1 -0
- package/dist/assets/web/dist/assets/{edge-BwFDzfS_.js → edge-BYS8FtgQ.js} +1 -1
- package/dist/assets/web/dist/assets/{elixir-wwIYIpJm.js → elixir-fhUpi3nO.js} +1 -1
- package/dist/assets/web/dist/assets/{elm-DyDuM3v3.js → elm-DAQNiwsN.js} +1 -1
- package/dist/assets/web/dist/assets/{erb-DFJBEVrh.js → erb-CWzEys6U.js} +1 -1
- package/dist/assets/web/dist/assets/eventSource-tRprhqm7.js +1 -0
- package/dist/assets/web/dist/assets/favicon-D1hkyC5j.js +1 -0
- package/dist/assets/web/dist/assets/{git-rebase-CuDv-44X.js → git-rebase-9rY3p-Rw.js} +1 -1
- package/dist/assets/web/dist/assets/{glimmer-js-ZFnrm4J3.js → glimmer-js-tY7hy0nE.js} +1 -1
- package/dist/assets/web/dist/assets/{glimmer-ts-XY4Yqshp.js → glimmer-ts-CTrLv5JF.js} +1 -1
- package/dist/assets/web/dist/assets/{glsl-xnR8efx0.js → glsl-BRJbiDfq.js} +1 -1
- package/dist/assets/web/dist/assets/{graphql-D-gO5tnP.js → graphql-B-MtVLh2.js} +1 -1
- package/dist/assets/web/dist/assets/{hack-CSxbW4ax.js → hack-BtIkOypQ.js} +1 -1
- package/dist/assets/web/dist/assets/{haml-D4ar42QT.js → haml-CoOH6gfa.js} +1 -1
- package/dist/assets/web/dist/assets/{handlebars-CMLbDE7o.js → handlebars-DgrRu-GP.js} +1 -1
- package/dist/assets/web/dist/assets/{html-CVekU4Fs.js → html-DZaaI7ca.js} +1 -1
- package/dist/assets/web/dist/assets/{html-derivative-St3HHw_N.js → html-derivative-DgHvT-qZ.js} +1 -1
- package/dist/assets/web/dist/assets/{http-Mon0DWHS.js → http-CvAd51yz.js} +1 -1
- package/dist/assets/web/dist/assets/{hurl-Cbl5IvBs.js → hurl-CXGqAfzx.js} +1 -1
- package/dist/assets/web/dist/assets/{localIso-CNA-bhS-.js → indexFormat-B-pd4r7Y.js} +1 -1
- package/dist/assets/web/dist/assets/{indexRow-FLJu5mR9.js → indexRow-Cse3JGb0.js} +1 -1
- package/dist/assets/web/dist/assets/{java-Bprck1l-.js → java-CIQStIXF.js} +1 -1
- package/dist/assets/web/dist/assets/{javascript-C73G1-UR.js → javascript-WbH8s7E3.js} +1 -1
- package/dist/assets/web/dist/assets/{jinja-C4DuRxYJ.js → jinja-DzyWEVKC.js} +1 -1
- package/dist/assets/web/dist/assets/{jison-B2U03GEC.js → jison-CrT-zCa-.js} +1 -1
- package/dist/assets/web/dist/assets/{json-CvTZUDzx.js → json-lHjpjXMQ.js} +1 -1
- package/dist/assets/web/dist/assets/{jsx-DMWKCzwo.js → jsx-WTwYCHW6.js} +1 -1
- package/dist/assets/web/dist/assets/{julia-Czn7bV_3.js → julia-Bqpk9H7E.js} +1 -1
- package/dist/assets/web/dist/assets/{just-BBxpNfvC.js → just-LdXk1r3m.js} +1 -1
- package/dist/assets/web/dist/assets/{latex-Bw9yg5mr.js → latex-h_y2A5KD.js} +1 -1
- package/dist/assets/web/dist/assets/{liquid-BRCjaydE.js → liquid-CY7bF2_p.js} +1 -1
- package/dist/assets/web/dist/assets/localIso-L06jV29p.js +1 -0
- package/dist/assets/web/dist/assets/{lua-DUu2KIC0.js → lua-oQ7XVZOY.js} +1 -1
- package/dist/assets/web/dist/assets/main-CGO6fQ8k.js +28 -0
- package/dist/assets/web/dist/assets/main-O8UXRuEv.css +1 -0
- package/dist/assets/web/dist/assets/{marko-CZJZIlSQ.js → marko-zv_qZ4QV.js} +1 -1
- package/dist/assets/web/dist/assets/{mdc-BmClq6fd.js → mdc-Dl4kL_YK.js} +1 -1
- package/dist/assets/web/dist/assets/{nginx-Bmt3xSEm.js → nginx-CAHCZj4F.js} +1 -1
- package/dist/assets/web/dist/assets/{nim-Da-i0utK.js → nim-BOtmwCJH.js} +1 -1
- package/dist/assets/web/dist/assets/{org-BvAre2qP.js → org-DspQk3HQ.js} +1 -1
- package/dist/assets/web/dist/assets/{perl-v9WwFFtB.js → perl-CAXmvVl0.js} +1 -1
- package/dist/assets/web/dist/assets/{php-DTU4Zyze.js → php-CMUx4VyM.js} +1 -1
- package/dist/assets/web/dist/assets/{pug-DhTwEqTO.js → pug-B55xVJTv.js} +1 -1
- package/dist/assets/web/dist/assets/{qml-BUD9AC_c.js → qml-DsNKCCRk.js} +1 -1
- package/dist/assets/web/dist/assets/{r-CAnu56n7.js → r-DbhP0VvO.js} +1 -1
- package/dist/assets/web/dist/assets/{razor-Dn0P4gAq.js → razor-BCpE6P-p.js} +1 -1
- package/dist/assets/web/dist/assets/{regexp-CZyBRaVW.js → regexp-BllsjiN4.js} +1 -1
- package/dist/assets/web/dist/assets/{rst-Ckyd5UsY.js → rst-CO5Rpq9V.js} +1 -1
- package/dist/assets/web/dist/assets/{ruby-BBQ62fsx.js → ruby-C2A4OgQq.js} +1 -1
- package/dist/assets/web/dist/assets/{sas-CW35hMxG.js → sas-Butzoyql.js} +1 -1
- package/dist/assets/web/dist/assets/{scss-CpV8pF4J.js → scss-PEUwJBpf.js} +1 -1
- package/dist/assets/web/dist/assets/settingsApi-priOPiwu.js +1 -0
- package/dist/assets/web/dist/assets/{shellscript-CRJvsmE1.js → shellscript-3Yhq0khE.js} +1 -1
- package/dist/assets/web/dist/assets/{shellsession-CyriUZxf.js → shellsession-04Hkb7m3.js} +1 -1
- package/dist/assets/web/dist/assets/{soy-D5QzYUeg.js → soy-BnId73cP.js} +1 -1
- package/dist/assets/web/dist/assets/{sql-CUBf_Mse.js → sql-BKwdc0ch.js} +1 -1
- package/dist/assets/web/dist/assets/sseReplay-DutHNghE.js +9 -0
- package/dist/assets/web/dist/assets/{stata-B-4n4ITz.js → stata-BPPtNkay.js} +1 -1
- package/dist/assets/web/dist/assets/{surrealql-RuWZBLtr.js → surrealql-CDPChcYP.js} +1 -1
- package/dist/assets/web/dist/assets/{svelte-CO5YzCsK.js → svelte-B15DIQ-z.js} +1 -1
- package/dist/assets/web/dist/assets/{templ-DbkWVaBF.js → templ-D-rp_AiB.js} +1 -1
- package/dist/assets/web/dist/assets/{tex-IuxoEcId.js → tex-BnVDCD7p.js} +1 -1
- package/dist/assets/web/dist/assets/{ts-tags-g6dGQPjd.js → ts-tags-cpujdyAR.js} +1 -1
- package/dist/assets/web/dist/assets/{tsx-WnXwiyH2.js → tsx-D1vO4L72.js} +1 -1
- package/dist/assets/web/dist/assets/{twig-Lx1G7PnY.js → twig-oQGnQGRc.js} +1 -1
- package/dist/assets/web/dist/assets/{typescript-BQd7biLF.js → typescript-CKSFpXiH.js} +1 -1
- package/dist/assets/web/dist/assets/{typst-D2lXPHK7.js → typst-DKHFo8BF.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-4wVQD9y9.js → vue-BstokpiS.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-html-DZPaRte4.js → vue-html-DpCp0_Sz.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-vine-DbmZCmu-.js → vue-vine-y5d5wUb1.js} +1 -1
- package/dist/assets/web/dist/assets/{xml-DV16szyo.js → xml-Br2hx5Ax.js} +1 -1
- package/dist/assets/web/dist/assets/{xsl-eo5-1sk0.js → xsl-Bc8Cobap.js} +1 -1
- package/dist/assets/web/dist/assets/{yaml-BXdX8HZf.js → yaml-LOITy_I1.js} +1 -1
- package/dist/cli.js +2314 -1571
- package/package.json +1 -1
- package/dist/assets/web/dist/assets/AppShell-MYHNxJks.js +0 -1
- package/dist/assets/web/dist/assets/CostsPage-DKk9OjB0.js +0 -2
- package/dist/assets/web/dist/assets/DeliveryPage-Kz6js49i.js +0 -1
- package/dist/assets/web/dist/assets/NotFoundPage-BXLrQQzy.js +0 -1
- package/dist/assets/web/dist/assets/ResidentsIndexPage-BdW0k2h3.js +0 -1
- package/dist/assets/web/dist/assets/RunFoldRow-CbTd9SiS.js +0 -9
- package/dist/assets/web/dist/assets/RunRoutePage-br0H-8Pz.js +0 -6
- package/dist/assets/web/dist/assets/RunsIndexPage-Bnzb15zH.js +0 -1
- package/dist/assets/web/dist/assets/ScheduledPage-NLhjzHAz.js +0 -1
- package/dist/assets/web/dist/assets/SettingsPage-D-i0uVw-.js +0 -1
- package/dist/assets/web/dist/assets/UnitRoutePage-DGXukEAH.js +0 -1
- package/dist/assets/web/dist/assets/durationTone-CYMux25J.js +0 -1
- package/dist/assets/web/dist/assets/favicon-CSt-qDvc.js +0 -1
- package/dist/assets/web/dist/assets/main-CFHF2RvQ.css +0 -1
- package/dist/assets/web/dist/assets/main-Dht0in4v.js +0 -28
|
@@ -323,6 +323,9 @@ workspaceDir: ./workspaces
|
|
|
323
323
|
# ship:
|
|
324
324
|
# maxRounds: 3 # review rounds per unit
|
|
325
325
|
# maxMinutes: 120 # the ship preset's wall-clock budget in minutes (the registry's default)
|
|
326
|
+
# addressSeverity: minor # the severity an approve's findings are held to (blocking|major|minor|nit);
|
|
327
|
+
# # a finding at or above it sends the round into a fix step instead of merge-ready.
|
|
328
|
+
# # Per channel/user: config set … --ship.addressSeverity; per run: severity:<level>.
|
|
326
329
|
|
|
327
330
|
# `harness` is a retired key (docs/reference/specs/harness-pi.md item 1). It
|
|
328
331
|
# moved the presets onto pi one at a time; every preset runs on pi now — the pi
|
|
@@ -386,6 +389,9 @@ workspaceDir: ./workspaces
|
|
|
386
389
|
# cloudflareAccountName: acme-infra # optional: how the page names the account beside its share (else the id's first hex)
|
|
387
390
|
# cloudflareTokenEnv: CF_ANALYTICS_TOKEN # token with Account Analytics:Read
|
|
388
391
|
# anthropicAdminKeyEnv: ANTHROPIC_ADMIN_KEY # optional: sk-ant-admin… for LLM spend
|
|
392
|
+
# snapshot: # the page serves a snapshot of both billing sources, never a live read;
|
|
393
|
+
# everyHours: 24 # how often the bot reads them (1–168, default 24). `costs snapshot`
|
|
394
|
+
# # takes one now. Kept on the state Worker any `*.worker` block names
|
|
389
395
|
# groups:
|
|
390
396
|
# myapp:
|
|
391
397
|
# label: My App
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from "../../src/core/memory/engine.ts";
|
|
11
11
|
import { tokenize } from "../../src/core/memory/scorer.ts";
|
|
12
12
|
import { FIRING_DETAIL_MAX, isScheduleFiring, type ScheduleFiring } from "../../src/core/schedules.ts";
|
|
13
|
+
import { isCostsSnapshot, type CostsSnapshot } from "../../src/core/costsSnapshotStore.ts";
|
|
13
14
|
import { REPO_SLUG } from "../../src/core/delivery.ts";
|
|
14
15
|
import {
|
|
15
16
|
isDeliverySnapshot,
|
|
@@ -176,6 +177,8 @@ export interface Env {
|
|
|
176
177
|
SESSION_LOGS: DurableObjectNamespace<SessionLogDO>;
|
|
177
178
|
/** Delivery snapshots (delivery item 10): ONE DeliveryDO (named "delivery"), one snapshot per repository. */
|
|
178
179
|
DELIVERY: DurableObjectNamespace<DeliveryDO>;
|
|
180
|
+
/** The costs snapshot (costs item 6): ONE CostsSnapshotDO (named "costs"), one snapshot per installation. */
|
|
181
|
+
COSTS: DurableObjectNamespace<CostsSnapshotDO>;
|
|
179
182
|
/** The ship coordinator Workflow in the bot's shim Worker (run-history item
|
|
180
183
|
* 47): where `RunHistoryDO.finish` sends `run-finished-<runId>` for a record
|
|
181
184
|
* carrying `parentInstanceId`. Optional: this Worker deploys without it (the
|
|
@@ -188,6 +191,9 @@ export interface Env {
|
|
|
188
191
|
/** The single DeliveryDO's name — every repository's snapshot lives in one object. */
|
|
189
192
|
const DELIVERY_OBJECT = "delivery";
|
|
190
193
|
|
|
194
|
+
/** The single CostsSnapshotDO's name — the installation has one costs snapshot. */
|
|
195
|
+
const COSTS_OBJECT = "costs";
|
|
196
|
+
|
|
191
197
|
/** The single ConfigDO's name. */
|
|
192
198
|
const CONFIG_OBJECT = "config";
|
|
193
199
|
/** A config document key: short, lowercase, like `overrides`. */
|
|
@@ -929,6 +935,110 @@ async function handleDelivery(pathname: string, body: unknown, env: Env): Promis
|
|
|
929
935
|
return json({ error: "not found" }, 404);
|
|
930
936
|
}
|
|
931
937
|
|
|
938
|
+
// ---------------------------------------------------------------------------
|
|
939
|
+
// The costs snapshot (docs/reference/specs/costs.md item 6)
|
|
940
|
+
// ---------------------------------------------------------------------------
|
|
941
|
+
|
|
942
|
+
/** The datasets a snapshot's `usage` carries, each stored as its own row. */
|
|
943
|
+
const USAGE_PARTS = [
|
|
944
|
+
"containers",
|
|
945
|
+
"durableObjectRequests",
|
|
946
|
+
"durableObjectDays",
|
|
947
|
+
"durableObjectStorage",
|
|
948
|
+
"workers",
|
|
949
|
+
"r2Storage",
|
|
950
|
+
"r2Operations",
|
|
951
|
+
"workflows",
|
|
952
|
+
] as const;
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
* The installation's one costs snapshot: both billing sources' rows over the
|
|
956
|
+
* page's widest range plus the run history's per-user usage, and when they
|
|
957
|
+
* were read (`CostsSnapshot`, the shape the bot validates too). Stored as one
|
|
958
|
+
* row per part — the meta, each usage dataset, the LLM rows, the run usage —
|
|
959
|
+
* so no single value nears the row limit as the window's rows grow; replaced
|
|
960
|
+
* whole on every put, in one transaction, and read back as one document.
|
|
961
|
+
*/
|
|
962
|
+
export class CostsSnapshotDO extends DurableObject<Env> {
|
|
963
|
+
private readonly sql: SqlStorage;
|
|
964
|
+
|
|
965
|
+
constructor(ctx: DurableObjectState, env: Env) {
|
|
966
|
+
super(ctx, env);
|
|
967
|
+
this.sql = ctx.storage.sql;
|
|
968
|
+
this.sql.exec(`
|
|
969
|
+
CREATE TABLE IF NOT EXISTS parts (
|
|
970
|
+
part TEXT PRIMARY KEY,
|
|
971
|
+
body TEXT NOT NULL
|
|
972
|
+
);
|
|
973
|
+
`);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
/** Replace the snapshot whole: every part rewritten in one transaction. */
|
|
977
|
+
async put(snapshot: CostsSnapshot): Promise<void> {
|
|
978
|
+
const { usage, llm, runUsage, ...meta } = snapshot;
|
|
979
|
+
const rows: Array<[string, unknown]> = [
|
|
980
|
+
["meta", meta],
|
|
981
|
+
...USAGE_PARTS.map((name): [string, unknown] => [`usage.${name}`, usage[name]]),
|
|
982
|
+
["llm", llm],
|
|
983
|
+
["runUsage", runUsage],
|
|
984
|
+
];
|
|
985
|
+
this.ctx.storage.transactionSync(() => {
|
|
986
|
+
this.sql.exec(`DELETE FROM parts`);
|
|
987
|
+
for (const [part, body] of rows)
|
|
988
|
+
this.sql.exec(`INSERT INTO parts (part, body) VALUES (?, ?)`, part, JSON.stringify(body));
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
/** The stored snapshot reassembled from its parts, or null when none was stored (or the parts do not make one). */
|
|
993
|
+
async get(): Promise<CostsSnapshot | null> {
|
|
994
|
+
const parts = new Map(
|
|
995
|
+
this.sql
|
|
996
|
+
.exec<{ part: string; body: string }>(`SELECT part, body FROM parts`)
|
|
997
|
+
.toArray()
|
|
998
|
+
.map((r) => [r.part, r.body]),
|
|
999
|
+
);
|
|
1000
|
+
const meta = parts.get("meta");
|
|
1001
|
+
if (meta === undefined) return null;
|
|
1002
|
+
const read = (part: string): unknown => {
|
|
1003
|
+
const body = parts.get(part);
|
|
1004
|
+
return body === undefined ? undefined : (JSON.parse(body) as unknown);
|
|
1005
|
+
};
|
|
1006
|
+
const usage = Object.fromEntries(USAGE_PARTS.map((name) => [name, read(`usage.${name}`)]));
|
|
1007
|
+
const snapshot = {
|
|
1008
|
+
...(JSON.parse(meta) as Record<string, unknown>),
|
|
1009
|
+
usage,
|
|
1010
|
+
llm: read("llm"),
|
|
1011
|
+
runUsage: read("runUsage"),
|
|
1012
|
+
};
|
|
1013
|
+
return isCostsSnapshot(snapshot) ? snapshot : null;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
const COSTS_ROUTES = new Set(["/costs/snapshot/get", "/costs/snapshot/put"]);
|
|
1018
|
+
|
|
1019
|
+
async function handleCosts(pathname: string, body: unknown, env: Env): Promise<Response> {
|
|
1020
|
+
const b = (typeof body === "object" && body !== null ? body : {}) as Record<string, unknown>;
|
|
1021
|
+
const dO = env.COSTS.get(env.COSTS.idFromName(COSTS_OBJECT));
|
|
1022
|
+
if (pathname === "/costs/snapshot/get") {
|
|
1023
|
+
const snapshot = await dO.get();
|
|
1024
|
+
console.log(
|
|
1025
|
+
`[costs/snapshot/get] -> ${snapshot ? `snapshot taken ${snapshot.takenAt} by ${snapshot.takenBy}` : "none"}`,
|
|
1026
|
+
);
|
|
1027
|
+
return json({ snapshot });
|
|
1028
|
+
}
|
|
1029
|
+
if (pathname === "/costs/snapshot/put") {
|
|
1030
|
+
if (!isCostsSnapshot(b.snapshot))
|
|
1031
|
+
return json(
|
|
1032
|
+
{ error: "snapshot must be a CostsSnapshot (takenAt, takenBy, durationMs, range, usage, llm, runUsage)" },
|
|
1033
|
+
400,
|
|
1034
|
+
);
|
|
1035
|
+
await dO.put(b.snapshot);
|
|
1036
|
+
console.log(`[costs/snapshot/put] <- snapshot taken ${b.snapshot.takenAt} by ${b.snapshot.takenBy}`);
|
|
1037
|
+
return json({ ok: true });
|
|
1038
|
+
}
|
|
1039
|
+
return json({ error: "not found" }, 404);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
932
1042
|
/** Documents are small; a body over this is refused before storage. */
|
|
933
1043
|
const MAX_CONFIG_DOCUMENT_BYTES = 256 * 1024;
|
|
934
1044
|
|
|
@@ -3229,6 +3339,8 @@ const MAX_SNAPSHOT_BODY_BYTES = 16 * 1024 * 1024;
|
|
|
3229
3339
|
function bodyFenceFor(pathname: string): number {
|
|
3230
3340
|
if (WIDE_BODY_ROUTES.has(pathname)) return MAX_RUN_PUT_BODY_BYTES;
|
|
3231
3341
|
if (pathname === "/delivery/put" || pathname === "/delivery/merge") return MAX_SNAPSHOT_BODY_BYTES;
|
|
3342
|
+
// A ninety-day costs snapshot is a few hundred KB today and grows with the account's rows.
|
|
3343
|
+
if (pathname === "/costs/snapshot/put") return MAX_SNAPSHOT_BODY_BYTES;
|
|
3232
3344
|
return MAX_BODY_BYTES;
|
|
3233
3345
|
}
|
|
3234
3346
|
|
|
@@ -3707,6 +3819,7 @@ async function handleRuns(pathname: string, body: unknown, env: Env): Promise<Re
|
|
|
3707
3819
|
const ROUTES = new Set([
|
|
3708
3820
|
...CONFIG_ROUTES,
|
|
3709
3821
|
...DELIVERY_ROUTES,
|
|
3822
|
+
...COSTS_ROUTES,
|
|
3710
3823
|
"/retrieve",
|
|
3711
3824
|
"/write",
|
|
3712
3825
|
"/list",
|
|
@@ -3735,7 +3848,7 @@ interface Admission {
|
|
|
3735
3848
|
async function handleRequest(request: Request, env: Env, admission: Admission): Promise<Response> {
|
|
3736
3849
|
const url = new URL(request.url);
|
|
3737
3850
|
if (url.pathname === "/healthz" && request.method === "GET")
|
|
3738
|
-
return json({ ok: true, build: BUILD, features: ["memory", "schedules", "runs", "config", "delivery"] });
|
|
3851
|
+
return json({ ok: true, build: BUILD, features: ["memory", "schedules", "runs", "config", "delivery", "costs"] });
|
|
3739
3852
|
if (!admission.known) return json({ error: "not found" }, 404);
|
|
3740
3853
|
if (request.method !== "POST") return json({ error: "method not allowed" }, 405);
|
|
3741
3854
|
if (!admission.authorized) return json({ error: "unauthorized" }, 401);
|
|
@@ -3787,6 +3900,7 @@ async function handleRequest(request: Request, env: Env, admission: Admission):
|
|
|
3787
3900
|
if (url.pathname.startsWith("/runs/")) return handleRuns(url.pathname, body, env);
|
|
3788
3901
|
if (url.pathname.startsWith("/config/")) return handleConfig(url.pathname, body, env);
|
|
3789
3902
|
if (url.pathname.startsWith("/delivery/")) return handleDelivery(url.pathname, body, env);
|
|
3903
|
+
if (url.pathname.startsWith("/costs/")) return handleCosts(url.pathname, body, env);
|
|
3790
3904
|
|
|
3791
3905
|
if (url.pathname === "/retrieve") {
|
|
3792
3906
|
const parsed = parseRetrieve(body);
|
|
@@ -36,6 +36,11 @@
|
|
|
36
36
|
// repository — the merged pull requests' facts over the window, one row
|
|
37
37
|
// each, and when they were read (docs/reference/specs/delivery.md item 10).
|
|
38
38
|
{ "name": "DELIVERY", "class_name": "DeliveryDO" },
|
|
39
|
+
// ONE CostsSnapshotDO (named "costs"): the installation's costs snapshot —
|
|
40
|
+
// both billing sources' rows over the costs page's widest range and the run
|
|
41
|
+
// history's per-user usage, one row per part, replaced whole on every take
|
|
42
|
+
// (docs/reference/specs/costs.md item 6).
|
|
43
|
+
{ "name": "COSTS", "class_name": "CostsSnapshotDO" },
|
|
39
44
|
// One SessionLogDO per session — a thread and an agent (the DO name is
|
|
40
45
|
// `<threadKey>:<agent>`): the transcript rows of every run of the
|
|
41
46
|
// session, a full-text index over them, and the notepad
|
|
@@ -74,6 +79,7 @@
|
|
|
74
79
|
// retired with its rows — docs/reference/specs/self-improvement.md item 1.
|
|
75
80
|
{ "tag": "v7", "deleted_classes": ["FrictionDO"] },
|
|
76
81
|
{ "tag": "v8", "new_sqlite_classes": ["DeliveryDO"] },
|
|
77
|
-
{ "tag": "v9", "new_sqlite_classes": ["SessionLogDO"] }
|
|
82
|
+
{ "tag": "v9", "new_sqlite_classes": ["SessionLogDO"] },
|
|
83
|
+
{ "tag": "v10", "new_sqlite_classes": ["CostsSnapshotDO"] }
|
|
78
84
|
]
|
|
79
85
|
}
|