@coreplane/switchboard 1.229.0 → 1.231.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.
Files changed (32) hide show
  1. package/dist/assets/config/config.example.yaml +10 -0
  2. package/dist/assets/deploy/cloudflare-memory/worker.ts +56 -29
  3. package/dist/assets/deploy/cloudflare-resident/worker.ts +39 -20
  4. package/dist/assets/package-lock.json +6 -3
  5. package/dist/assets/package.json +2 -1
  6. package/dist/assets/source.json +3 -3
  7. package/dist/assets/src/core/authz/actor.ts +24 -6
  8. package/dist/assets/src/core/coordinator/contract.ts +41 -14
  9. package/dist/assets/src/core/coordinator/driver.ts +4 -0
  10. package/dist/assets/src/core/runEvents.ts +22 -5
  11. package/dist/assets/src/core/runFriction.ts +4 -3
  12. package/dist/assets/src/core/runLedger/sessionLog.ts +13 -0
  13. package/dist/assets/src/core/runRecord.ts +33 -0
  14. package/dist/assets/src/core/ship/contract.ts +35 -29
  15. package/dist/assets/src/core/ship/coordinator.ts +89 -4
  16. package/dist/assets/src/core/trace/attrs.ts +3 -0
  17. package/dist/assets/src/core/trace/streamSpans.ts +4 -1
  18. package/dist/assets/src/execution/residentRebind.ts +27 -4
  19. package/dist/assets/web/dist/.vite/manifest.json +19 -19
  20. package/dist/assets/web/dist/assets/CostsPage-D-v8am88.js +2 -0
  21. package/dist/assets/web/dist/assets/{ResidentDetailPage-CkVYktwT.js → ResidentDetailPage-Df6D2emM.js} +1 -1
  22. package/dist/assets/web/dist/assets/{ResidentsIndexPage-BLanxf27.js → ResidentsIndexPage-B0yECe2z.js} +1 -1
  23. package/dist/assets/web/dist/assets/{RunRoutePage-U3nwL8Df.js → RunRoutePage-D4FG8JbC.js} +4 -4
  24. package/dist/assets/web/dist/assets/{RunsIndexPage-DrIVxmpl.js → RunsIndexPage-qxjCAVwQ.js} +1 -1
  25. package/dist/assets/web/dist/assets/{ScheduledPage-DpqubmIm.js → ScheduledPage-B0xQSZIS.js} +1 -1
  26. package/dist/assets/web/dist/assets/{StatusDot-BpD9MRge.js → StatusDot-D-h9p7Iv.js} +1 -1
  27. package/dist/assets/web/dist/assets/{Tooltip-BYv0WSrA.js → Tooltip-BuDnXjL7.js} +1 -1
  28. package/dist/assets/web/dist/assets/{dist-BZmA5qTt.js → dist-BjLBNLkl.js} +1 -1
  29. package/dist/assets/web/dist/assets/{main-C4GOEklV.js → main-BRrMt0Kr.js} +2 -2
  30. package/dist/cli.js +1895 -1039
  31. package/package.json +1 -1
  32. package/dist/assets/web/dist/assets/CostsPage-DQg30mHr.js +0 -2
@@ -69,7 +69,12 @@ export interface RunRecord {
69
69
  model?: string;
70
70
  /** Platform-namespaced ids (AGENTS.md invariant 4). */
71
71
  channelId: string;
72
+ /** The person the run was for — the message's sender, or the person an app
73
+ * relayed it for (slack-channel.md item 13); `slack:bot:<id>` only when no
74
+ * person could be found behind an app's post. */
72
75
  userId: string;
76
+ /** The app that posted the request for `userId`, by display name, when it was not their own message. */
77
+ relayedBy?: string;
73
78
  threadKey: string;
74
79
  /** How the run's channel may travel (authorization): stamped at dispatch
75
80
  * from the `ChannelDirectory`, read by `member-of` (a `public` run is
@@ -101,6 +106,11 @@ export interface RunRecord {
101
106
  stepCount?: number;
102
107
  schema?: number;
103
108
  status: RunStatus;
109
+ /** The failure by name, when a `failed` run has one (item 57):
110
+ * `policy_refusal`, the provider refused the run's model call under its
111
+ * usage policy. Absent on a run that did not fail, on one that failed for
112
+ * a reason without a name here, and on records written before the field. */
113
+ failure?: RunFailure;
104
114
  /** Events the run published in total — unchanged by truncation. */
105
115
  eventCount: number;
106
116
  /** Events actually present in `events` (= `events.length`). */
@@ -293,6 +303,23 @@ function isRunPullRequestShape(v: unknown): v is RunPullRequest {
293
303
  );
294
304
  }
295
305
 
306
+ /** Why a `failed` run failed, when the failure has a name a reader acts on
307
+ * (item 57). `policy_refusal`: the model provider refused the run's call
308
+ * under its usage policy — the stop reason its wire names, never the
309
+ * explanation's words — so the session's next seed leaves the refused
310
+ * request out of its tail (docs/reference/specs/session-log.md item 9). A
311
+ * failure without a name here leaves the record without the field. */
312
+ export const RUN_FAILURE_KINDS = ["policy_refusal"] as const;
313
+ export type RunFailureKind = (typeof RUN_FAILURE_KINDS)[number];
314
+ export interface RunFailure {
315
+ kind: RunFailureKind;
316
+ }
317
+
318
+ export function isRunFailure(v: unknown): v is RunFailure {
319
+ if (typeof v !== "object" || v === null) return false;
320
+ return RUN_FAILURE_KINDS.includes((v as Record<string, unknown>).kind as RunFailureKind);
321
+ }
322
+
296
323
  /** The three places a run's conversation can start (item 52): the thread's
297
324
  * channel history, a spawning parent's text turns, or the tail of its own
298
325
  * session's log (docs/reference/specs/session-log.md item 9). */
@@ -426,6 +453,9 @@ export interface RunListOptions {
426
453
  /** One thread's runs (`slack:C0123:1712.34`), newest first — the read behind
427
454
  * a thread's lineage and a child's thread-aware rows (agent-conductor item 10). */
428
455
  threadKey?: string;
456
+ /** The runs one run spawned or that continue a thread it opened
457
+ * (`RunRecord.parentRunId`, item 46) — a conductor's children as one listing. */
458
+ parentRunId?: string;
429
459
  /** What the ACTOR may see (authorization): the store predicate compiled
430
460
  * from the policy, pushed down so no surface loads rows and filters after.
431
461
  * Absent = no visibility constraint — only a caller that has already decided
@@ -719,6 +749,8 @@ export function isRunRecord(v: unknown): v is RunRecord {
719
749
  if (r.seed !== undefined && !RUN_SEEDS.includes(r.seed as RunSeed)) return false;
720
750
  // The run's place in its session's log (item 53), or absent.
721
751
  if (r.session !== undefined && !isRunSession(r.session)) return false;
752
+ // The failure by name (item 57): one of the named kinds, or absent.
753
+ if (r.failure !== undefined && !isRunFailure(r.failure)) return false;
722
754
  if (r.usage !== undefined && !isRunUsage(r.usage)) return false;
723
755
  // A coordinator's child (item 48): the instance id in the platform's alphabet
724
756
  // and the key `<instance>:<step>` — both or neither; one alone is no tag.
@@ -734,6 +766,7 @@ export function isRunRecord(v: unknown): v is RunRecord {
734
766
  )
735
767
  return false;
736
768
  if (typeof r.channelId !== "string" || typeof r.userId !== "string" || typeof r.threadKey !== "string") return false;
769
+ if (r.relayedBy !== undefined && typeof r.relayedBy !== "string") return false;
737
770
  // Absent on records written before the stamp existed (read as `unknown`); present → a known value.
738
771
  if (r.channelVisibility !== undefined && !CHANNEL_VISIBILITIES.includes(r.channelVisibility as ChannelVisibility))
739
772
  return false;
@@ -326,9 +326,30 @@ export function resolveSpecRow(ref: SpecItemRef, specMarkdown: string | undefine
326
326
 
327
327
  // ---- the builders ----------------------------------------------------------------------------------
328
328
 
329
- export interface ContractFromPlanInput {
330
- planMarkdown: string;
331
- unitId: string;
329
+ const TITLE_MAX = 80;
330
+
331
+ /** A unit's title from free text: its first non-blank line, cut to 80
332
+ * characters with an ellipsis — the one rule the hand-off's unit row and the
333
+ * generated contract share. */
334
+ export function unitTitleOf(text: string): string {
335
+ const firstLine =
336
+ text
337
+ .split("\n")
338
+ .find((l) => l.trim() !== "")
339
+ ?.trim() ?? "";
340
+ return firstLine.length > TITLE_MAX ? `${firstLine.slice(0, TITLE_MAX - 1).trimEnd()}…` : firstLine;
341
+ }
342
+
343
+ /** A generated plan's one unit (agent-ship item 16): the text is the whole
344
+ * section under a `### <id>.` heading titled by its first line. Built, never
345
+ * parsed back out of markdown — a heading line inside the text is the text's
346
+ * own, not the end of the section. */
347
+ export function generatedUnit(id: string, text: string): ContractUnit {
348
+ const title = unitTitleOf(text);
349
+ return { id, title, section: `### ${id}. ${title}\n\n${text}`, bullets: {} };
350
+ }
351
+
352
+ interface ContractInputCommon {
332
353
  /** The spec's text by file name (`resident-repos.md`); undefined when it is not there. */
333
354
  readSpec: (spec: string) => string | undefined;
334
355
  agentRules?: AgentRules;
@@ -337,8 +358,15 @@ export interface ContractFromPlanInput {
337
358
  issue?: { repo: string; number: number };
338
359
  }
339
360
 
361
+ /** The unit: parsed out of the plan's markdown by id (a seeded plan), or the
362
+ * unit itself when the caller already holds it (a generated plan's one unit,
363
+ * `generatedUnit`). */
364
+ export type ContractFromPlanInput = ContractInputCommon &
365
+ ({ planMarkdown: string; unitId: string } | { unit: ContractUnit });
366
+
340
367
  /** The contract for one unit of a plan. Throws when the plan has no such unit. */
341
368
  export function contractFromPlan(input: ContractFromPlanInput): ChildContract {
369
+ if ("unit" in input) return contractOf(input.unit, input);
342
370
  const unit = parsePlanUnit(input.planMarkdown, input.unitId);
343
371
  if (!unit) {
344
372
  const ids = planUnitIds(input.planMarkdown);
@@ -346,35 +374,13 @@ export function contractFromPlan(input: ContractFromPlanInput): ChildContract {
346
374
  `the plan has no unit ${input.unitId} (its units: ${ids.length > 0 ? ids.join(", ") : "none — no `### U<n>.` heading"})`,
347
375
  );
348
376
  }
349
- return {
350
- unit,
351
- specRows: specItemRefs(unit.section).map((ref) => resolveSpecRow(ref, input.readSpec(ref.spec))),
352
- agentRules: input.agentRules,
353
- guards: GUARDS,
354
- rebase: { branch: input.rebase?.branch, onto: input.rebase?.onto },
355
- issue: input.issue,
356
- };
377
+ return contractOf(unit, input);
357
378
  }
358
379
 
359
- const TITLE_MAX = 80;
360
-
361
- /** The compatibility path record 0031 names: a task string is a plan of one
362
- * unit with no spec rows. The task is the unit's whole section. */
363
- export function contractFromTask(input: {
364
- task: string;
365
- agentRules?: AgentRules;
366
- rebase?: { branch?: string; onto?: string };
367
- issue?: { repo: string; number: number };
368
- }): ChildContract {
369
- const firstLine =
370
- input.task
371
- .split("\n")
372
- .find((l) => l.trim() !== "")
373
- ?.trim() ?? "";
374
- const title = firstLine.length > TITLE_MAX ? `${firstLine.slice(0, TITLE_MAX - 1).trimEnd()}…` : firstLine;
380
+ function contractOf(unit: ContractUnit, input: ContractInputCommon): ChildContract {
375
381
  return {
376
- unit: { id: "task", title, section: input.task, bullets: {} },
377
- specRows: [],
382
+ unit,
383
+ specRows: specItemRefs(unit.section).map((ref) => resolveSpecRow(ref, input.readSpec(ref.spec))),
378
384
  agentRules: input.agentRules,
379
385
  guards: GUARDS,
380
386
  rebase: { branch: input.rebase?.branch, onto: input.rebase?.onto },
@@ -139,6 +139,85 @@ export function planInstanceId(planId: string, attempt = 1): string {
139
139
  return `${base.slice(0, INSTANCE_ID_MAX - suffix.length)}${suffix}`;
140
140
  }
141
141
 
142
+ // A compact sha-256 (FIPS 180-4) in plain TypeScript: this module is imported
143
+ // by the shim Worker, whose runtime has no `node:crypto`, so the generated
144
+ // plan id's hash cannot come from the Node API the preflight once used.
145
+ function sha256Hex(text: string): string {
146
+ const K = new Uint32Array([
147
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98,
148
+ 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
149
+ 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8,
150
+ 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
151
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
152
+ 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
153
+ 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
154
+ 0xc67178f2,
155
+ ]);
156
+ const h = new Uint32Array([
157
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
158
+ ]);
159
+ const bytes = new TextEncoder().encode(text);
160
+ const padded = new Uint8Array((((bytes.length + 8) >> 6) << 6) + 64);
161
+ padded.set(bytes);
162
+ padded[bytes.length] = 0x80;
163
+ new DataView(padded.buffer).setBigUint64(padded.length - 8, BigInt(bytes.length) * 8n);
164
+ const w = new Uint32Array(64);
165
+ const rotr = (x: number, n: number) => (x >>> n) | (x << (32 - n));
166
+ const view = new DataView(padded.buffer);
167
+ for (let i = 0; i < padded.length; i += 64) {
168
+ for (let t = 0; t < 16; t++) w[t] = view.getUint32(i + t * 4);
169
+ for (let t = 16; t < 64; t++) {
170
+ const s0 = rotr(w[t - 15]!, 7) ^ rotr(w[t - 15]!, 18) ^ (w[t - 15]! >>> 3);
171
+ const s1 = rotr(w[t - 2]!, 17) ^ rotr(w[t - 2]!, 19) ^ (w[t - 2]! >>> 10);
172
+ w[t] = (w[t - 16]! + s0 + w[t - 7]! + s1) >>> 0;
173
+ }
174
+ let [a, b, c, d, e, f, g, hh] = [h[0]!, h[1]!, h[2]!, h[3]!, h[4]!, h[5]!, h[6]!, h[7]!];
175
+ for (let t = 0; t < 64; t++) {
176
+ const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
177
+ const ch = (e & f) ^ (~e & g);
178
+ const t1 = (hh + S1 + ch + K[t]! + w[t]!) >>> 0;
179
+ const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
180
+ const maj = (a & b) ^ (a & c) ^ (b & c);
181
+ const t2 = (S0 + maj) >>> 0;
182
+ hh = g;
183
+ g = f;
184
+ f = e;
185
+ e = (d + t1) >>> 0;
186
+ d = c;
187
+ c = b;
188
+ b = a;
189
+ a = (t1 + t2) >>> 0;
190
+ }
191
+ h[0] = (h[0]! + a) >>> 0;
192
+ h[1] = (h[1]! + b) >>> 0;
193
+ h[2] = (h[2]! + c) >>> 0;
194
+ h[3] = (h[3]! + d) >>> 0;
195
+ h[4] = (h[4]! + e) >>> 0;
196
+ h[5] = (h[5]! + f) >>> 0;
197
+ h[6] = (h[6]! + g) >>> 0;
198
+ h[7] = (h[7]! + hh) >>> 0;
199
+ }
200
+ return [...h].map((x) => x.toString(16).padStart(8, "0")).join("");
201
+ }
202
+
203
+ /** The generated plan's id (agent-ship item 16): `<slug>-<hash>` — the slug
204
+ * from the request text (24 characters in the branch alphabet, the bound the
205
+ * ship branch namer used), the hash the first six hex of the sha256 of the
206
+ * thread key, a newline and the normalised request text. Byte-identical text
207
+ * in the same thread is the same plan; a rephrase, however it starts, is a
208
+ * new one; two threads never share an id. Always fits `PLAN_ID_PATTERN`. */
209
+ export function generatedPlanId(text: string, threadKey: string): string {
210
+ const normalised = text.toLowerCase().replace(/\s+/g, " ").trim();
211
+ const slug =
212
+ normalised
213
+ .replace(/[^a-z0-9]+/g, "-")
214
+ .replace(/^-+|-+$/g, "")
215
+ .slice(0, SLUG_MAX)
216
+ .replace(/-+$/, "") || "task";
217
+ const hash = sha256Hex(`${threadKey}\n${normalised}`).slice(0, 6);
218
+ return `${slug}-${hash}`;
219
+ }
220
+
142
221
  /** The Dependencies bullet's unit ids — lists, `to` ranges — the unit itself excluded. */
143
222
  function dependenciesOf(text: string, self: string): string[] {
144
223
  const out: string[] = [];
@@ -428,6 +507,10 @@ export interface UnitPipelineInput {
428
507
  * `runner` (a seeded plan, under its grant) or `person` (a task, or a
429
508
  * record without the field). */
430
509
  merge: "runner" | "person";
510
+ /** The instance's mark (agent-ship item 16): a generated one-unit plan — a
511
+ * `plan` with an id and no `path` — whose unit runs in the requesting
512
+ * thread and is re-issued with the request's own text, never a plan path. */
513
+ generated: boolean;
431
514
  /** Resume at review: an open pull request of ship's own the requester named. */
432
515
  resume?: { pr: number; headSha?: string; url?: string };
433
516
  }
@@ -1082,10 +1165,12 @@ export function renderUnitReport(s: UnitPipelineState): string {
1082
1165
  const rounds = `${e.reviewRounds} review round${e.reviewRounds === 1 ? "" : "s"}`;
1083
1166
  const prUrl = s.pr?.url;
1084
1167
  const prLine = prUrl ? ` PR: ${prUrl}` : "";
1085
- const reissue =
1086
- s.input.unit.id === "task"
1087
- ? `To continue, re-issue \`agent:ship\` in this thread${prUrl ? ` and include the PR URL (${prUrl})` : " — include the PR URL if a PR exists"}.`
1088
- : `The unit's dependents in this plan stay blocked; the unit runs again when the plan is re-issued.`;
1168
+ // The re-issue line keys on the instance's mark, never on who merges: a
1169
+ // generated plan is re-issued with the request's own text, a seeded one by
1170
+ // its plan path — and a seeded plan can be a person's merge too.
1171
+ const reissue = s.input.generated
1172
+ ? `To continue, re-issue \`agent:ship\` in this thread with the same text${prUrl ? ` and include the PR URL (${prUrl})` : " — include the PR URL if a PR exists"}.`
1173
+ : `The unit's dependents in this plan stay blocked; the unit runs again when the plan is re-issued.`;
1089
1174
  const declined = [...(s.dispositionsByRound[e.reviewRounds - 1] ?? [])].filter((d) => d.disposition === "declined");
1090
1175
  const declinedLine = `Declined findings: ${declined.length > 0 ? declined.map((d) => `${d.findingId}${d.note ? ` — ${d.note}` : ""}`).join("; ") : "none"}`;
1091
1176
  const verdictLine = `Verdict: LGTM${s.lastVerdictSummary ? ` — ${s.lastVerdictSummary}` : ""}`;
@@ -22,6 +22,8 @@ export interface AttrDomain {
22
22
  caughtUp: boolean;
23
23
  files: number;
24
24
  dedupe: "fresh" | "duplicate";
25
+ /** How the requester was found (slack-channel.md item 13): the sender, the relay footer's thread, the thread's parent, or the app itself. */
26
+ requester: "message" | "relay-footer" | "thread-parent" | "bot";
25
27
  // dispatch.* / run.* / post.*
26
28
  outcome: string;
27
29
  count: number;
@@ -151,6 +153,7 @@ const ATTR_TYPE: Record<SpanAttrKey, "string" | "number" | "boolean"> = {
151
153
  caughtUp: "boolean",
152
154
  files: "number",
153
155
  dedupe: "string",
156
+ requester: "string",
154
157
  outcome: "string",
155
158
  count: "number",
156
159
  backend: "string",
@@ -44,6 +44,7 @@ export const STREAMED_SPANS = [
44
44
  "run.reading_diff",
45
45
  "run.settle_reviewed_head",
46
46
  "run.description_turn",
47
+ "run.verdict_turn",
47
48
  "run.observe_workspace",
48
49
  "run.pr_post_step",
49
50
  "run.review_post_step",
@@ -105,6 +106,7 @@ const UNCOUNTED: ReadonlySet<string> = new Set([
105
106
  "ship.round",
106
107
  "run.settle_reviewed_head",
107
108
  "run.description_turn",
109
+ "run.verdict_turn",
108
110
  "post.card_close",
109
111
  "post.reply",
110
112
  ]);
@@ -148,11 +150,12 @@ export const PARENTS: Readonly<Record<string, readonly string[]>> = {
148
150
  "dispatch.ship_preflight": ["request"],
149
151
  "dispatch.ledger_claim": ["request"],
150
152
  "dispatch.route": ["request"],
151
- "run.agent": ["request", "ship.round", "run.settle_reviewed_head", "run.description_turn"],
153
+ "run.agent": ["request", "ship.round", "run.settle_reviewed_head", "run.description_turn", "run.verdict_turn"],
152
154
  "run.command": ["request"],
153
155
  "run.reading_diff": ["request"],
154
156
  "run.settle_reviewed_head": ["request", "ship.round"],
155
157
  "run.description_turn": ["request", "ship.round"],
158
+ "run.verdict_turn": ["request", "ship.round"],
156
159
  "run.observe_workspace": ["request", "ship.round"],
157
160
  "run.pr_post_step": ["request", "ship.round"],
158
161
  "run.review_post_step": ["request", "ship.round"],
@@ -24,7 +24,10 @@
24
24
  * - the branch is a local branch of the thread's OWN worktree — the physical
25
25
  * fact that this thread's run created it; a branch the tree never made is
26
26
  * refused whatever the caller says;
27
- * - the tree has no uncommitted tracked changes — never at the cost of work.
27
+ * - the tree has no uncommitted tracked changes — never at the cost of work —
28
+ * unless its HEAD is already the branch: the run made the branch in this
29
+ * tree and left an edit after pushing, so the record alone moves and no
30
+ * git command touches the tree.
28
31
  * The move is a `git checkout` inside the existing tree: same path, same pool
29
32
  * user, deps and snapshot lineage untouched. Every refusal is named in the
30
33
  * attach answer so the bot can say why the follow-up runs where it does. */
@@ -253,11 +256,20 @@ export interface RebindTreeFacts {
253
256
  /** `git status --porcelain -uno` listed a tracked change (untracked scratch
254
257
  * files are the thread's own state and survive a checkout). */
255
258
  dirty?: boolean;
259
+ /** `git rev-parse --abbrev-ref HEAD` in the tree — the branch checked out
260
+ * (`HEAD` when detached) — measured once the tree is dirty: the one fact
261
+ * that tells a dirty tree already on the branch from one elsewhere. */
262
+ head?: string;
256
263
  }
257
264
 
258
265
  export type RebindVerdict =
259
- /** Check the branch out in the existing tree. */
260
- | { kind: "rebind" }
266
+ /** Move the binding. `checkout: true`: check the branch out in the existing
267
+ * tree. `checkout: false`: the tree is dirty but its HEAD is already the
268
+ * branch — the run made it here and left an edit after pushing — so only
269
+ * the record moves; no checkout, no fetch, no reset, the tree not touched
270
+ * by the rebind (`note` says so for the log). */
271
+ | { kind: "rebind"; checkout: true }
272
+ | { kind: "rebind"; checkout: false; note: string }
261
273
  /** The tree is gone (a slept container) and the thread's own runs pushed the
262
274
  * branch: move the binding and let the attach recreate the tree at it. */
263
275
  | { kind: "recreate" }
@@ -297,6 +309,17 @@ export function rebindVerdict(plan: { to: string; pr: number; own?: boolean }, t
297
309
  };
298
310
  }
299
311
  if (tree.dirty === true) {
312
+ // A dirty tree whose HEAD is the branch has nothing a checkout could
313
+ // cost: the run created the branch in this very tree and left the edit
314
+ // after pushing, and only the record still names the old ref. Moving
315
+ // the record is the whole move. Any other HEAD keeps the guard.
316
+ if (tree.head === plan.to) {
317
+ return {
318
+ kind: "rebind",
319
+ checkout: false,
320
+ note: `the worktree is dirty but its HEAD is already ${JSON.stringify(plan.to)} (the run made the branch here); the binding moves, the tree is not touched`,
321
+ };
322
+ }
300
323
  return {
301
324
  kind: "refuse",
302
325
  refused: rebindRefused(
@@ -306,5 +329,5 @@ export function rebindVerdict(plan: { to: string; pr: number; own?: boolean }, t
306
329
  ),
307
330
  };
308
331
  }
309
- return { kind: "rebind" };
332
+ return { kind: "rebind", checkout: true };
310
333
  }
@@ -28,7 +28,7 @@
28
28
  "src": "../node_modules/@fontsource-variable/jetbrains-mono/files/jetbrains-mono-vietnamese-wght-normal.woff2"
29
29
  },
30
30
  "../node_modules/@pierre/diffs/dist/index.js": {
31
- "file": "assets/dist-BZmA5qTt.js",
31
+ "file": "assets/dist-BjLBNLkl.js",
32
32
  "name": "dist",
33
33
  "src": "../node_modules/@pierre/diffs/dist/index.js",
34
34
  "isDynamicEntry": true,
@@ -2562,16 +2562,16 @@
2562
2562
  "_seed-DeKSHrt2.js"
2563
2563
  ]
2564
2564
  },
2565
- "_StatusDot-BpD9MRge.js": {
2566
- "file": "assets/StatusDot-BpD9MRge.js",
2565
+ "_StatusDot-D-h9p7Iv.js": {
2566
+ "file": "assets/StatusDot-D-h9p7Iv.js",
2567
2567
  "name": "StatusDot",
2568
2568
  "imports": [
2569
2569
  "_seed-DeKSHrt2.js",
2570
- "_Tooltip-BYv0WSrA.js"
2570
+ "_Tooltip-BuDnXjL7.js"
2571
2571
  ]
2572
2572
  },
2573
- "_Tooltip-BYv0WSrA.js": {
2574
- "file": "assets/Tooltip-BYv0WSrA.js",
2573
+ "_Tooltip-BuDnXjL7.js": {
2574
+ "file": "assets/Tooltip-BuDnXjL7.js",
2575
2575
  "name": "Tooltip",
2576
2576
  "imports": [
2577
2577
  "_AppShell-lYVcj-k7.js",
@@ -2789,7 +2789,7 @@
2789
2789
  ]
2790
2790
  },
2791
2791
  "src/main.ts": {
2792
- "file": "assets/main-C4GOEklV.js",
2792
+ "file": "assets/main-BRrMt0Kr.js",
2793
2793
  "name": "main",
2794
2794
  "src": "src/main.ts",
2795
2795
  "isEntry": true,
@@ -2820,7 +2820,7 @@
2820
2820
  ]
2821
2821
  },
2822
2822
  "src/pages/CostsPage.vue": {
2823
- "file": "assets/CostsPage-DQg30mHr.js",
2823
+ "file": "assets/CostsPage-D-v8am88.js",
2824
2824
  "name": "CostsPage",
2825
2825
  "src": "src/pages/CostsPage.vue",
2826
2826
  "isDynamicEntry": true,
@@ -2841,19 +2841,19 @@
2841
2841
  ]
2842
2842
  },
2843
2843
  "src/pages/ResidentDetailPage.vue": {
2844
- "file": "assets/ResidentDetailPage-CkVYktwT.js",
2844
+ "file": "assets/ResidentDetailPage-Df6D2emM.js",
2845
2845
  "name": "ResidentDetailPage",
2846
2846
  "src": "src/pages/ResidentDetailPage.vue",
2847
2847
  "isDynamicEntry": true,
2848
2848
  "imports": [
2849
2849
  "_AppShell-lYVcj-k7.js",
2850
2850
  "_seed-DeKSHrt2.js",
2851
- "_StatusDot-BpD9MRge.js",
2851
+ "_StatusDot-D-h9p7Iv.js",
2852
2852
  "_residentDiskBudget-BMBKlYRH.js"
2853
2853
  ]
2854
2854
  },
2855
2855
  "src/pages/ResidentsIndexPage.vue": {
2856
- "file": "assets/ResidentsIndexPage-BLanxf27.js",
2856
+ "file": "assets/ResidentsIndexPage-B0yECe2z.js",
2857
2857
  "name": "ResidentsIndexPage",
2858
2858
  "src": "src/pages/ResidentsIndexPage.vue",
2859
2859
  "isDynamicEntry": true,
@@ -2861,20 +2861,20 @@
2861
2861
  "_AppShell-lYVcj-k7.js",
2862
2862
  "_seed-DeKSHrt2.js",
2863
2863
  "_wallClock-61NpVkY6.js",
2864
- "_StatusDot-BpD9MRge.js",
2864
+ "_StatusDot-D-h9p7Iv.js",
2865
2865
  "_residentDiskBudget-BMBKlYRH.js",
2866
2866
  "_indexFormat-B-pd4r7Y.js"
2867
2867
  ]
2868
2868
  },
2869
2869
  "src/pages/RunRoutePage.vue": {
2870
- "file": "assets/RunRoutePage-U3nwL8Df.js",
2870
+ "file": "assets/RunRoutePage-D4FG8JbC.js",
2871
2871
  "name": "RunRoutePage",
2872
2872
  "src": "src/pages/RunRoutePage.vue",
2873
2873
  "isDynamicEntry": true,
2874
2874
  "imports": [
2875
2875
  "_AppShell-lYVcj-k7.js",
2876
2876
  "_seed-DeKSHrt2.js",
2877
- "_Tooltip-BYv0WSrA.js",
2877
+ "_Tooltip-BuDnXjL7.js",
2878
2878
  "src/main.ts",
2879
2879
  "_wallClock-61NpVkY6.js",
2880
2880
  "_NotFoundPage-BzWS4Ca1.js",
@@ -2890,31 +2890,31 @@
2890
2890
  ]
2891
2891
  },
2892
2892
  "src/pages/RunsIndexPage.vue": {
2893
- "file": "assets/RunsIndexPage-DrIVxmpl.js",
2893
+ "file": "assets/RunsIndexPage-qxjCAVwQ.js",
2894
2894
  "name": "RunsIndexPage",
2895
2895
  "src": "src/pages/RunsIndexPage.vue",
2896
2896
  "isDynamicEntry": true,
2897
2897
  "imports": [
2898
2898
  "_AppShell-lYVcj-k7.js",
2899
2899
  "_seed-DeKSHrt2.js",
2900
- "_Tooltip-BYv0WSrA.js",
2900
+ "_Tooltip-BuDnXjL7.js",
2901
2901
  "_wallClock-61NpVkY6.js",
2902
2902
  "_RunsTabs-DEICQ4FY.js",
2903
- "_StatusDot-BpD9MRge.js",
2903
+ "_StatusDot-D-h9p7Iv.js",
2904
2904
  "_indexFormat-B-pd4r7Y.js",
2905
2905
  "_favicon-CWPcvWvp.js",
2906
2906
  "_localIso-L06jV29p.js"
2907
2907
  ]
2908
2908
  },
2909
2909
  "src/pages/ScheduledPage.vue": {
2910
- "file": "assets/ScheduledPage-DpqubmIm.js",
2910
+ "file": "assets/ScheduledPage-B0xQSZIS.js",
2911
2911
  "name": "ScheduledPage",
2912
2912
  "src": "src/pages/ScheduledPage.vue",
2913
2913
  "isDynamicEntry": true,
2914
2914
  "imports": [
2915
2915
  "_AppShell-lYVcj-k7.js",
2916
2916
  "_seed-DeKSHrt2.js",
2917
- "_Tooltip-BYv0WSrA.js",
2917
+ "_Tooltip-BuDnXjL7.js",
2918
2918
  "_wallClock-61NpVkY6.js",
2919
2919
  "_RunsTabs-DEICQ4FY.js",
2920
2920
  "_indexFormat-B-pd4r7Y.js",
@@ -0,0 +1,2 @@
1
+ import{t as e}from"./AppShell-lYVcj-k7.js";import{Ci as t,Lr as n,Lt as r,Vt as i,Xt as a,_n as o,ci as s,cn as c,gn as l,ir as u,ln as d,on as f,r as p,sn as m,un as h,ur as g,vi as _,wi as v,xi as y,yn as b,zr as x}from"./seed-DeKSHrt2.js";var S=`Durable Objects`,C=`Workers · storage · R2`,w=`LLM (Anthropic)`,T=(e,t=2)=>`$${e.toFixed(t)}`,E=e=>e.workersUsd+e.doRowsUsd+e.doStorageUsd+e.r2Usd+e.workflowsUsd;function D(e){let t=[];for(let n of e.days)for(let e of Object.keys(n.containers))t.includes(e)||t.push(e);return e.days.some(e=>Object.keys(e.durableObjects).length>0)&&t.push(S),e.days.some(e=>E(e)>0)&&t.push(C),e.llmAvailable&&t.push(w),t}function O(e,t){return t===`Durable Objects`?Object.values(e.durableObjects).reduce((e,t)=>e+t,0)+e.doRequestsUsd:t===`Workers · storage · R2`?E(e):t===`LLM (Anthropic)`?e.llmUsd:e.containers[t]?.total??0}var k=30.4;function ee(e){let t=e.range.partialLastDay?e.days.slice(0,-1):e.days,n=e.range.partialLastDay?e.days[e.days.length-1]:void 0,r=t[t.length-1],i=t.slice(-7),a=i.length?i.reduce((e,t)=>e+t.total,0)/i.length:void 0,o=e.account.cloudUsd>0?Math.round(e.totals.cloudUsd/e.account.cloudUsd*100):0;return{...r?{yesterday:{date:r.date,total:r.total}}:{},...n?{today:{date:n.date,total:n.total}}:{},...a===void 0?{}:{avg7:a},projection:M(e,t,n),llm:{range:e.totals.llmUsd,...r?{yesterday:r.llmUsd}:{},...n?{today:{usd:n.llmUsd,estimated:n.llmEstimated}}:{}},accountShare:o}}var A=e=>e.reduce((e,t)=>e+t,0)/e.length;function j(e,t){let n=Date.parse(`${t.date}T00:00:00Z`),r=(e.generatedAt-n)/864e5;return Math.min(1,Math.max(1/24,Number.isFinite(r)?r:1))}function M(e,t,n){let r=n?j(e,n):1,i=t.slice(-7),a=0,o=`full-days`;i.length?a=A(i.map(e=>e.cloudUsd)):n&&(a=n.cloudUsd/r,o=`today`);let s=0,c=`none`,l=0;if(e.llmAvailable){let e=t.findIndex(e=>e.llmUsd>0),i=e===-1?[]:t.slice(e).slice(-7);i.length?(s=A(i.map(e=>e.llmUsd)),c=`closed-days`,l=i.length):n&&n.llmUsd>0&&(s=n.llmUsd/r,c=`today`)}return{monthUsd:(a+s)*k,cloudRate:a,llmRate:s,cloudBasis:o,llmBasis:c,llmClosedDays:l}}function te(e){let t=`https://dash.cloudflare.com/${e.account.id}`;return{account:t,billing:`${t}/billing`,containers:`${t}/workers/containers`,durableObjects:`${t}/workers/durable-objects`,workers:`${t}/workers-and-pages`,r2:`${t}/r2/overview`,workflows:`${t}/workers/workflows`,worker:e=>`${t}/workers/services/view/${encodeURIComponent(e)}/production/metrics`,bucket:e=>`${t}/r2/default/buckets/${encodeURIComponent(e)}`,anthropic:`https://platform.claude.com/cost`}}var ne=e=>e.account.name??`${e.account.id.slice(0,8)}…`;function N(e,t,n){let r=[`${e.date} · total ${T(e.total)}${n?` (partial day)`:``}`];for(let n of t){let t=O(e,n);t<=0||r.push(`${n} ${T(t)}${n===`LLM (Anthropic)`&&e.llmEstimated?` (estimate)`:``}`)}return r.join(`
2
+ `)}function P(e){let t=new Date(`${e}T00:00:00Z`);return Number.isNaN(t.getTime())?e:`${[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`][t.getUTCMonth()]} ${t.getUTCDate()}`}function F(e,t,n){let r=t.map(t=>({series:t,usd:O(e,t),estimated:t===`LLM (Anthropic)`&&e.llmEstimated})).filter(e=>e.usd>0).sort((e,t)=>t.usd-e.usd);return{date:e.date,label:P(e.date),partial:n,total:e.total,rows:r}}function I(e,t){let n={t:12,r:12,b:30,l:48},r=960-n.l-n.r,i=260-n.t-n.b,a=e.days,o=Math.max(1e-9,...a.map(e=>e.total))*1.08,s=r/Math.max(1,a.length),c=Math.min(10,s*.28),l=e=>n.t+i-e/o*i,u=Array.from({length:5},(e,t)=>{let n=o/4*t;return{y:Number(l(n).toFixed(1)),label:T(n)}}),d=[],f=[],p=[];return a.forEach((r,o)=>{let u=0,m=n.l+o*s+c/2,h=s-c;f.push({x:Number((n.l+o*s).toFixed(1)),y:n.t,width:Number(s.toFixed(1)),height:i,title:N(r,t,e.range.partialLastDay&&o===a.length-1),tip:F(r,t,e.range.partialLastDay&&o===a.length-1)}),t.forEach((e,t)=>{let n=O(r,e);if(n<=0)return;let i=l(u+n),a=l(u),o=Math.max(0,a-i-2);d.push({seriesIndex:t,x:Number(m.toFixed(1)),y:Number(i.toFixed(1)),width:Number(h.toFixed(1)),height:Number(o.toFixed(1))}),u+=n}),(a.length<=16||o%Math.ceil(a.length/16)===0)&&p.push({x:Number((m+h/2).toFixed(1)),label:r.date.slice(5)})}),{width:960,height:260,gridLines:u,segments:d,dayHovers:f,dayLabels:p,axisY:Number(l(0).toFixed(1)),marginLeft:n.l,marginRight:n.r}}function re(e){let t=e.totals.byResource,n=[[`Memory (provisioned while awake)`,t.memory],[`vCPU (active use only)`,t.cpu],[`Durable Object duration + requests`,t.durableObjects],[`Disk (provisioned while awake)`,t.disk],[`Workers requests + CPU`,t.workers],[`Durable Object SQLite rows`,t.doRows],[`Durable Object SQLite storage`,t.doStorage],[`R2 storage + operations`,t.r2],[`Workflow steps + state`,t.workflows]],r=n.reduce((e,t)=>e+t[1],0)||1;return n.map(([e,t])=>({label:e,usd:t,percent:t/r*100}))}var L=[`fill-[#4478b8] dark:fill-[#7aa7e0]`,`fill-[#c8823b] dark:fill-[#e0a95a]`,`fill-[#4c8c57] dark:fill-[#6fae7a]`,`fill-[#b8963e] dark:fill-[#d4b45a]`,`fill-[#b0568c] dark:fill-[#d489b8]`,`fill-[#6b7bcc] dark:fill-[#98a5e6]`,`fill-[#d05252] dark:fill-[#e07a74]`,`fill-[#3e8e96] dark:fill-[#6fb9c0]`],R=[`bg-[#4478b8] dark:bg-[#7aa7e0]`,`bg-[#c8823b] dark:bg-[#e0a95a]`,`bg-[#4c8c57] dark:bg-[#6fae7a]`,`bg-[#b8963e] dark:bg-[#d4b45a]`,`bg-[#b0568c] dark:bg-[#d489b8]`,`bg-[#6b7bcc] dark:bg-[#98a5e6]`,`bg-[#d05252] dark:bg-[#e07a74]`,`bg-[#3e8e96] dark:bg-[#6fb9c0]`],z={class:`overflow-x-auto`},B=[`viewBox`],V=[`x1`,`x2`,`y1`,`y2`],H=[`x`,`y`],U=[`x`,`y`,`width`,`height`],W=[`aria-label`,`x`,`y`,`width`,`height`,`onPointerenter`,`onPointermove`],G=[`x`,`y`],K=[`x1`,`x2`,`y1`,`y2`],q={class:`tip-head mb-1.5 flex items-baseline justify-between gap-3 border-b border-muted pb-1.5`},J={class:`font-medium text-highlighted`},Y={key:0,class:`ml-1.5 font-normal text-dimmed`},X={class:`font-medium text-highlighted`},ie={class:`tip-name truncate text-muted`},ae={class:`text-highlighted`},oe={class:`tip-usd`},se={key:0,class:`ml-1 text-dimmed`},Z=256,Q=12,ce=b({__name:`CostChart`,props:{report:{},series:{}},setup(e){let n=e,r=f(()=>I(n.report,n.series)),i=e=>44+e*20,o=s(null),c=s(null);function p(e,t){let n=c.value,r=n?.getBoundingClientRect(),a=t.clientX-(r?.left??0),s=t.clientY-(r?.top??0),l=n?.clientWidth??0,u=n?.clientHeight??0,d=i(e.tip.rows.length),f=l>0&&a+Q+Z>l,p=u>0&&s+Q+d>u;o.value={x:Math.max(0,f?a-Q-Z:a+Q),y:Math.max(0,p?s-Q-d:s+Q),data:e.tip}}return(e,n)=>(u(),h(`div`,{ref_key:`host`,ref:c,class:`chart-host relative`},[m(`div`,z,[(u(),h(`svg`,{class:`block h-auto w-full font-mono`,viewBox:`0 0 ${r.value.width} ${r.value.height}`,role:`img`,"aria-label":`Daily cost, stacked by component`},[(u(!0),h(a,null,g(r.value.gridLines,(e,t)=>(u(),h(a,{key:`g${t}`},[m(`line`,{class:`stroke-(--ui-border-muted)`,x1:r.value.marginLeft,x2:r.value.width-r.value.marginRight,y1:e.y,y2:e.y},null,8,V),m(`text`,{class:`fill-(--ui-text-dimmed) text-[11px] tabular-nums`,x:r.value.marginLeft-8,y:e.y+4,"text-anchor":`end`},v(e.label),9,H)],64))),128)),(u(!0),h(a,null,g(r.value.segments,(e,t)=>(u(),h(`rect`,{key:`s${t}`,class:y([`seg hover:opacity-85`,_(L)[e.seriesIndex%_(L).length]]),x:e.x,y:e.y,width:e.width,height:e.height},null,10,U))),128)),(u(!0),h(a,null,g(r.value.dayHovers,(e,t)=>(u(),h(`rect`,{key:`h${t}`,class:`day fill-transparent`,role:`img`,"aria-label":e.title,x:e.x,y:e.y,width:e.width,height:e.height,onPointerenter:t=>p(e,t),onPointermove:t=>p(e,t),onPointerleave:n[0]||=e=>o.value=null},null,40,W))),128)),(u(!0),h(a,null,g(r.value.dayLabels,(e,t)=>(u(),h(`text`,{key:`d${t}`,class:`fill-(--ui-text-dimmed) text-[11px]`,x:e.x,y:r.value.height-10,"text-anchor":`middle`},v(e.label),9,G))),128)),m(`line`,{class:`stroke-(--ui-border)`,x1:r.value.marginLeft,x2:r.value.width-r.value.marginRight,y1:r.value.axisY,y2:r.value.axisY},null,8,K)],8,B))]),o.value?(u(),h(`div`,{key:0,class:`chart-tip pointer-events-none absolute z-10 w-64 rounded-md border border-default bg-elevated px-3 py-2 font-mono text-xs tabular-nums whitespace-nowrap shadow-lg`,role:`tooltip`,style:t({left:`${o.value.x}px`,top:`${o.value.y}px`})},[m(`div`,q,[m(`span`,J,[l(v(o.value.data.label),1),o.value.data.partial?(u(),h(`span`,Y,`partial day`)):d(``,!0)]),m(`span`,X,v(_(T)(o.value.data.total)),1)]),(u(!0),h(a,null,g(o.value.data.rows,e=>(u(),h(`div`,{key:e.series,class:`tip-row flex items-baseline justify-between gap-3 leading-5`},[m(`span`,ie,v(e.series),1),m(`span`,ae,[m(`span`,oe,v(_(T)(e.usd)),1),e.estimated?(u(),h(`span`,se,`est.`)):d(``,!0)])]))),128))],4)):d(``,!0)],512))}}),le={class:`by-user grid gap-3`},ue={class:`flex flex-wrap items-center gap-x-4 gap-y-2`},de={class:`flex items-center gap-2 text-sm text-muted`},fe=[`title`],pe=[`disabled`,`aria-describedby`],me={key:0,id:`me-hint`,class:`me-hint text-xs text-dimmed`},he={class:`coverage font-mono text-xs tabular-nums text-muted`},ge={class:`overflow-x-auto`},_e={class:`users w-full min-w-[40rem] border-collapse whitespace-nowrap font-mono text-[0.8125rem] tabular-nums`},ve=[`title`],ye={class:`text-highlighted`},be={class:`ml-1.5 text-xs text-dimmed`},xe={key:0,class:`ml-1.5 rounded-xs bg-accented px-1 text-xs text-highlighted`},Se=[`title`],Ce={class:`border-b border-muted px-2.5 py-1.5 text-right`},we={class:`border-b border-muted px-2.5 py-1.5 text-right`},Te={class:`border-b border-muted px-2.5 py-1.5 text-right`},Ee={class:`border-b border-muted px-2.5 py-1.5 text-right font-medium text-highlighted`},De={class:`border-b border-muted px-2.5 py-1.5 text-right`},Oe={key:0},ke={class:`empty px-2.5 py-3 text-sm text-muted`,colspan:`6`},Ae={key:0},je={class:`px-2.5 py-1.5 text-right text-xs text-muted`},Me={class:`px-2.5 py-1.5 text-right text-xs text-muted`},Ne={class:`reconciliation font-mono text-xs tabular-nums text-muted`},Pe=b({__name:`CostsByUser`,props:{report:{}},setup(e){let t=e,n=s(``),o=s(!1),c=f(()=>t.report.viewer.userIds.length>0),p=f(()=>{let e=n.value.trim().toLowerCase(),r=new Set(t.report.viewer.userIds);return t.report.users.filter(t=>o.value&&!r.has(t.userId)?!1:!e||t.userId.toLowerCase().includes(e)||(t.userName??``).toLowerCase().includes(e))}),b=f(()=>t.report.users.reduce((e,t)=>e+t.totalUsd,0)),S=f(()=>p.value.reduce((e,t)=>e+t.totalUsd,0)),C=e=>t.report.viewer.userIds.includes(e.userId),w=e=>e.userName??e.userId.replace(/^[a-z]+:/,``),E=e=>e.userId.split(`:`)[0]??``,D=e=>`${e} day${e===1?``:`s`}`;return(t,s)=>(u(),h(`section`,le,[m(`div`,ue,[m(`label`,de,[s[2]||=m(`span`,null,`Filter`,-1),x(m(`input`,{"onUpdate:modelValue":s[0]||=e=>n.value=e,class:`user-filter rounded-md border border-default bg-elevated px-2.5 py-1 font-mono text-xs text-highlighted`,type:`search`,placeholder:`name or id`,"aria-label":`Filter users`},null,512),[[i,n.value]])]),m(`label`,{class:y([`me-toggle flex items-center gap-2 text-sm`,c.value?`text-muted`:`text-dimmed`]),title:c.value?`Only your own runs`:void 0},[x(m(`input`,{"onUpdate:modelValue":s[1]||=e=>o.value=e,type:`checkbox`,disabled:!c.value,"aria-label":`Only me`,"aria-describedby":c.value?void 0:`me-hint`},null,8,pe),[[r,o.value]]),s[3]||=m(`span`,null,`me`,-1),c.value?d(``,!0):(u(),h(`span`,me,`— your sign-in email matched no Slack user in this range`))],10,fe),m(`p`,he,[e.report.coverage.historyOn?(u(),h(a,{key:1},[l(` runs from `+v(_(P)(e.report.coverage.from))+` to `+v(_(P)(e.report.range.to)),1),e.report.coverage.clamped?(u(),h(a,{key:0},[l(` · earlier days are past the history's `+v(e.report.coverage.retentionDays)+`-day window`,1)],64)):d(``,!0),e.report.pending>0?(u(),h(a,{key:1},[l(` · `+v(e.report.pending)+` run`+v(e.report.pending===1?``:`s`)+` still being priced`,1)],64)):d(``,!0)],64)):(u(),h(a,{key:0},[l(`run history is off — no runs to attribute`)],64))])]),m(`div`,ge,[m(`table`,_e,[s[5]||=m(`thead`,null,[m(`tr`,null,[m(`th`,{class:`border-b border-muted bg-(--ui-bg-muted) px-2.5 py-1.5 text-left text-xs font-medium text-muted`},` User `),m(`th`,{class:`border-b border-muted bg-(--ui-bg-muted) px-2.5 py-1.5 text-right text-xs font-medium text-muted`},` Runs `),m(`th`,{class:`border-b border-muted bg-(--ui-bg-muted) px-2.5 py-1.5 text-right text-xs font-medium text-muted`},` LLM `),m(`th`,{class:`border-b border-muted bg-(--ui-bg-muted) px-2.5 py-1.5 text-right text-xs font-medium text-muted`},[l(` Cloud `),m(`span`,{class:`font-normal text-dimmed`},`allocated`)]),m(`th`,{class:`border-b border-muted bg-(--ui-bg-muted) px-2.5 py-1.5 text-right text-xs font-medium text-muted`},` Total `),m(`th`,{class:`border-b border-muted bg-(--ui-bg-muted) px-2.5 py-1.5 text-right text-xs font-medium text-muted`},` Share `)])],-1),m(`tbody`,null,[(u(!0),h(a,null,g(p.value,e=>(u(),h(`tr`,{key:e.userId,class:y([`user-row`,C(e)?`is-me`:``])},[m(`td`,{class:`border-b border-muted px-2.5 py-1.5`,title:e.userId},[m(`span`,ye,v(w(e)),1),m(`span`,be,v(E(e)),1),C(e)?(u(),h(`span`,xe,`me`)):d(``,!0),e.unpricedTokens>0?(u(),h(`span`,{key:1,class:`ml-1.5 text-xs text-warn`,title:`${e.unpricedTokens} tokens ran under a model with no list price`},`unpriced tokens`,8,Se)):d(``,!0)],8,ve),m(`td`,Ce,v(e.runs),1),m(`td`,we,v(_(T)(e.llmUsd)),1),m(`td`,Te,v(_(T)(e.cloudUsd)),1),m(`td`,Ee,v(_(T)(e.totalUsd)),1),m(`td`,De,v(b.value>0?Math.round(e.totalUsd/b.value*100):0)+`% `,1)],2))),128)),p.value.length===0?(u(),h(`tr`,Oe,[m(`td`,ke,v(e.report.users.length===0?`no runs in this range`:`no user matches`),1)])):d(``,!0)]),p.value.length>0&&p.value.length<e.report.users.length?(u(),h(`tfoot`,Ae,[m(`tr`,null,[s[4]||=m(`td`,{class:`px-2.5 py-1.5 text-xs text-muted`,colspan:`4`},`shown`,-1),m(`td`,je,v(_(T)(S.value)),1),m(`td`,Me,v(b.value>0?Math.round(S.value/b.value*100):0)+`% `,1)])])):d(``,!0)])]),m(`p`,Ne,[e.report.reconciliation.comparedDays>0?(u(),h(a,{key:0},[l(` LLM attributed `+v(_(T)(e.report.reconciliation.attributedLlmUsd))+` of `+v(_(T)(e.report.reconciliation.workspaceLlmUsd))+` on the workspace over `+v(D(e.report.reconciliation.comparedDays))+` · `,1),e.report.reconciliation.unattributedLlmUsd>=0?(u(),h(a,{key:0},[l(v(_(T)(e.report.reconciliation.unattributedLlmUsd))+` unattributed (router, review abridges, runs without a record, list vs invoice) `,1)],64)):(u(),h(a,{key:1},[l(v(_(T)(-e.report.reconciliation.unattributedLlmUsd))+` more attributed than the workspace figure (list vs invoice, or spend billed outside this workspace on a compared day) `,1)],64))],64)):(u(),h(a,{key:1},[l(`no day in range has a workspace LLM figure to compare against`)],64)),e.report.reconciliation.uncomparedDays>0?(u(),h(a,{key:2},[l(` · `+v(D(e.report.reconciliation.uncomparedDays))+` with `+v(_(T)(e.report.reconciliation.uncomparedLlmUsd))+` of run tokens but no workspace figure (billed outside this workspace) not compared`,1)],64)):d(``,!0),l(` · cloud allocated `+v(_(T)(e.report.reconciliation.cloudAllocatedUsd)),1),e.report.reconciliation.cloudUnallocatedUsd>0?(u(),h(a,{key:3},[l(` · `+v(_(T)(e.report.reconciliation.cloudUnallocatedUsd))+` on days with no runs`,1)],64)):d(``,!0)]),s[6]||=m(`p`,{class:`text-xs text-dimmed`},` A user's LLM is their runs' tokens at Anthropic list (cache writes at the 5-minute rate); a child run bills to whoever started its parent. Cloud is each day's Cloudflare spend split by share of run wall-clock — an allocation, not a meter. `,-1)]))}}),Fe={class:`mb-4 flex flex-wrap items-center gap-x-4 gap-y-2`},Ie={class:`flex items-center gap-1`,"aria-label":`Range`},Le={key:0,class:`rounded-md bg-accented px-2.5 py-1 font-mono text-xs font-medium tabular-nums text-highlighted`,"aria-current":`page`},Re=[`href`],ze={key:0,class:`flex items-center gap-1`,"aria-label":`Cost groups`},Be={key:0,class:`rounded-md bg-accented px-2.5 py-1 text-xs font-medium text-highlighted`,"aria-current":`page`},Ve=[`href`],He={class:`font-mono text-sm tabular-nums text-muted`},Ue={class:`mb-5 grid grid-cols-[repeat(auto-fit,minmax(190px,1fr))] gap-3`},We={key:0,class:`grid gap-0.5 rounded-lg border border-default bg-elevated px-4 py-3.5`},Ge={class:`font-mono text-2xl font-medium tabular-nums`},Ke={class:`text-xs text-muted`},qe={key:1,class:`grid gap-0.5 rounded-lg border border-default bg-elevated px-4 py-3.5`},Je={class:`font-mono text-2xl font-medium tabular-nums`},Ye={class:`text-xs text-muted`},Xe={class:`grid gap-0.5 rounded-lg border border-default bg-elevated px-4 py-3.5`},Ze={class:`font-mono text-2xl font-medium tabular-nums`},Qe={class:`text-xs text-muted`},$e={class:`grid gap-0.5 rounded-lg border border-default bg-elevated px-4 py-3.5`},et={class:`font-mono text-2xl font-medium tabular-nums`},tt={class:`text-xs text-muted`},nt={class:`grid gap-0.5 rounded-lg border border-default bg-elevated px-4 py-3.5`},rt={class:`font-mono text-2xl font-medium tabular-nums`},it={class:`text-xs text-muted`},at={class:`grid gap-0.5 rounded-lg border border-default bg-elevated px-4 py-3.5`},ot={class:`font-mono text-2xl font-medium tabular-nums`},st={class:`text-xs text-muted`},ct=[`href`,`title`],lt=[`href`],ut={class:`mb-5 grid gap-3 rounded-lg border border-default bg-elevated px-5 py-4`},dt={class:`flex flex-wrap items-baseline justify-between gap-x-4 gap-y-2`},ft={class:`legend flex flex-wrap gap-x-4 gap-y-1.5 text-xs text-muted`},pt=[`href`,`title`],mt={key:0,class:`text-xs text-warn`},ht={key:1,class:`text-xs text-muted`},gt={class:`mb-3 flex items-center gap-1 border-b border-default`,"aria-label":`View`},_t={key:0,class:`-mb-px border-b-2 border-primary px-3 py-1.5 text-sm font-medium text-highlighted`,"aria-current":`page`},vt=[`href`],yt={key:0,class:`mb-5 grid gap-3 rounded-lg border border-default bg-elevated px-5 py-4`},bt={key:1,class:`text-sm text-warn`},xt={class:`text-xs text-muted`},St={class:`rounded bg-accented px-1 py-0.5`},Ct={key:1,class:`mb-5 grid gap-3 rounded-lg border border-default bg-elevated px-5 py-4`},wt={class:`w-full border-collapse font-mono text-[0.8125rem] tabular-nums`},Tt={class:`px-2.5 py-1.5`},Et=[`href`],Dt={class:`px-2.5 py-1.5 text-right`},Ot={class:`px-2.5 py-1.5 text-right`},kt={class:`w-2/5 px-2.5 py-1.5`},At={key:2,class:`mb-5 grid gap-3 rounded-lg border border-default bg-elevated px-5 py-4`},jt={open:``},Mt={class:`mt-2.5 overflow-x-auto`},Nt={class:`data w-full min-w-[38rem] border-collapse whitespace-nowrap font-mono text-[0.8125rem] tabular-nums`},$=[`title`],Pt={key:0,class:`text-xs text-dimmed`},Ft={key:1,class:`text-xs text-dimmed`},It={class:`border-b border-muted px-2.5 py-1.5 text-right font-medium`},Lt={class:`text-xs text-muted`},Rt={class:`rounded bg-accented px-1 py-0.5`},zt={class:`border-t border-default pt-3.5 text-xs text-dimmed`},Bt={class:`mt-2 grid gap-1.5`},Vt=[`href`],Ht={key:2},Ut=[`href`],Wt={key:2},Gt=[`href`],Kt=[`href`],qt=b({__name:`CostsPage`,setup(r){let i=p(`costs`),s=f(()=>i?.report??null),b=f(()=>i?.groups??[]),x=f(()=>i?.view??`daily`),S=f(()=>x.value===`users`?i?.users??null:null);function C(e,t,n){let r=[`days=${t}`];return n===`users`&&r.push(`view=users`),`/costs/${e}?${r.join(`&`)}`}let w=f(()=>s.value?D(s.value):[]),E=f(()=>s.value?ee(s.value):null),k=f(()=>s.value?re(s.value):[]),A=f(()=>s.value?te(s.value):null),j=f(()=>s.value?ne(s.value):``);function M(e){let t=A.value;if(t){if(/^(Memory|vCPU|Disk)/.test(e))return t.containers;if(e.startsWith(`Durable Object`))return t.durableObjects;if(e.startsWith(`Workers`))return t.workers;if(e.startsWith(`R2`))return t.r2;if(e.startsWith(`Workflow`))return t.workflows}}function N(e){let t=A.value;if(t)return e===`LLM (Anthropic)`?t.anthropic:e===`Durable Objects`?t.durableObjects:e===`Workers · storage · R2`?t.workers:t.containers}let P=f(()=>{let e=s.value,t=E.value;if(!e||!t)return``;let n=t.projection,r=`cloud ${T(n.cloudRate)}/day`,i=n.cloudBasis===`today`?`cloud from today so far`:`cloud from the last 7 full days`;if(!e.llmAvailable)return`${r} × 30.4 · ${i} · before plan fees and included allowances`;let a=`LLM ${T(n.llmRate)}/day`,o=n.llmBasis===`closed-days`?`LLM from ${n.llmClosedDays} closed day${n.llmClosedDays===1?``:`s`}`:n.llmBasis===`today`?`LLM from today so far`:`no LLM spend in range`;return`${r} + ${a} · × 30.4 · ${n.cloudBasis===`today`&&n.llmBasis===`today`?`both from today so far`:`${i} · ${o}`}`}),F=f(()=>s.value?[...s.value.days].reverse():[]),I=[1,7,30,90],L=e=>e===1?`today`:`${e}d`,z=f(()=>{let e=s.value,t=E.value;if(!e||!t)return``;if(!e.llmAvailable)return`LLM spend not configured`;let n=[];return t.llm.yesterday!==void 0&&n.push(`yesterday ${T(t.llm.yesterday)}`),t.llm.today&&n.push(`today ${T(t.llm.today.usd)}${t.llm.today.estimated?` (estimate)`:``}`),n.length?n.join(` · `):`in range`});function B(e){let t=new Date(`${e}T00:00:00Z`);return Number.isNaN(t.getTime())?e:`${[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`][t.getUTCMonth()]} ${t.getUTCDate()}`}return(r,i)=>s.value&&E.value?(u(),c(e,{key:0,title:`${s.value.label} spend`,nav:`costs`},{default:n(()=>[m(`div`,Fe,[m(`nav`,Ie,[(u(),h(a,null,g(I,e=>(u(),h(a,{key:e},[e===s.value.range.days?(u(),h(`span`,Le,v(L(e)),1)):(u(),h(`a`,{key:1,class:`rounded-md px-2.5 py-1 font-mono text-xs tabular-nums text-muted no-underline hover:bg-elevated hover:text-highlighted`,href:C(s.value.group,e,x.value)},v(L(e)),9,Re))],64))),64))]),b.value.length>1?(u(),h(`nav`,ze,[(u(!0),h(a,null,g(b.value,e=>(u(),h(a,{key:e},[e===s.value.group?(u(),h(`span`,Be,v(e),1)):(u(),h(`a`,{key:1,class:`rounded-md px-2.5 py-1 text-xs text-muted no-underline hover:bg-elevated hover:text-highlighted`,href:C(e,s.value.range.days,x.value)},v(e),9,Ve))],64))),128))])):d(``,!0),m(`p`,He,[l(v(B(s.value.range.from))+` → `+v(B(s.value.range.to))+` · `+v(s.value.range.days)+`d`,1),s.value.range.partialLastDay?(u(),h(a,{key:0},[l(` · today partial`)],64)):d(``,!0)])]),m(`section`,Ue,[E.value.yesterday?(u(),h(`div`,We,[i[0]||=m(`span`,{class:`font-mono text-[0.6875rem] font-medium uppercase tracking-widest text-dimmed`},`Yesterday`,-1),m(`span`,Ge,v(_(T)(E.value.yesterday.total)),1),m(`span`,Ke,v(E.value.yesterday.date)+` · last full day`,1)])):(u(),h(`div`,qe,[i[1]||=m(`span`,{class:`font-mono text-[0.6875rem] font-medium uppercase tracking-widest text-dimmed`},`Today so far`,-1),m(`span`,Je,v(E.value.today?_(T)(E.value.today.total):`—`),1),m(`span`,Ye,v(E.value.today?`${E.value.today.date} · partial day, UTC`:`no day in range`),1)])),m(`div`,Xe,[i[2]||=m(`span`,{class:`font-mono text-[0.6875rem] font-medium uppercase tracking-widest text-dimmed`},`7-day average`,-1),m(`span`,Ze,v(E.value.avg7===void 0?`—`:_(T)(E.value.avg7)),1),m(`span`,Qe,v(E.value.avg7===void 0?`no full day yet in this range`:`per day, full days only`),1)]),m(`div`,$e,[i[3]||=m(`span`,{class:`font-mono text-[0.6875rem] font-medium uppercase tracking-widest text-dimmed`},`Projected month`,-1),m(`span`,et,v(_(T)(E.value.projection.monthUsd,0)),1),m(`span`,tt,v(P.value),1)]),m(`div`,nt,[i[4]||=m(`span`,{class:`font-mono text-[0.6875rem] font-medium uppercase tracking-widest text-dimmed`},`LLM spend`,-1),m(`span`,rt,v(s.value.llmAvailable?_(T)(E.value.llm.range):`—`),1),m(`span`,it,v(z.value),1)]),m(`div`,at,[i[6]||=m(`span`,{class:`font-mono text-[0.6875rem] font-medium uppercase tracking-widest text-dimmed`},`Share of account`,-1),m(`span`,ot,v(E.value.accountShare)+`%`,1),m(`span`,st,[l(v(_(T)(s.value.totals.cloudUsd))+` of `+v(_(T)(s.value.account.cloudUsd))+` on `,1),A.value?(u(),h(`a`,{key:0,class:`text-primary hover:underline`,href:A.value.account,target:`_blank`,rel:`noopener noreferrer`,title:`Cloudflare account ${s.value.account.id}`},v(j.value),9,ct)):d(``,!0),i[5]||=l(` · Cloudflare spend in range · `,-1),A.value?(u(),h(`a`,{key:1,class:`text-primary hover:underline`,href:A.value.billing,target:`_blank`,rel:`noopener noreferrer`},`billing`,8,lt)):d(``,!0)])])]),m(`section`,ut,[m(`div`,dt,[i[7]||=m(`div`,null,[m(`h2`,{class:`text-[0.9375rem] font-medium`},`Daily cost`),m(`p`,{class:`text-sm text-muted`},`Stacked by component · USD list price`)],-1),m(`div`,ft,[(u(!0),h(a,null,g(w.value,(e,t)=>(u(),h(`a`,{key:e,class:`inline-flex items-center gap-1.5 no-underline hover:text-highlighted hover:underline`,href:N(e),target:`_blank`,rel:`noopener noreferrer`,title:`Open where ${e} is billed`},[m(`i`,{class:y([`inline-block size-2.5 rounded-xs`,_(R)[t%_(R).length]])},null,2),l(v(e),1)],8,pt))),128))])]),o(ce,{report:s.value,series:w.value},null,8,[`report`,`series`]),s.value.llmAvailable?s.value.days.some(e=>e.llmEstimated)?(u(),h(`p`,ht,[i[9]||=l(` A day the Anthropic cost report has not closed yet (today, and yesterday until a few hours after midnight UTC) shows its LLM figure as an estimate: the hourly usage report priced at list. The closed days are the cost report itself. `,-1),s.value.days.some(e=>e.llmUnpricedTokens>0)?(u(),h(a,{key:0},[l(` Some tokens ran under a model this page has no price for and are not in the estimate. `)],64)):d(``,!0)])):d(``,!0):(u(),h(`p`,mt,[...i[8]||=[l(` LLM spend not configured — set `,-1),m(`code`,null,`ANTHROPIC_ADMIN_KEY`,-1),l(` and the group's `,-1),m(`code`,null,`anthropicWorkspaceId`,-1),l(` to layer it in. `,-1)]]))]),m(`nav`,gt,[(u(),h(a,null,g([`daily`,`users`],e=>(u(),h(a,{key:e},[e===x.value?(u(),h(`span`,_t,v(e===`daily`?`Daily`:`By user`),1)):(u(),h(`a`,{key:1,class:`-mb-px border-b-2 border-transparent px-3 py-1.5 text-sm text-muted no-underline hover:border-muted hover:text-highlighted`,href:C(s.value.group,s.value.range.days,e)},v(e===`daily`?`Daily`:`By user`),9,vt))],64))),64))]),x.value===`users`?(u(),h(`section`,yt,[i[12]||=m(`div`,null,[m(`h2`,{class:`text-[0.9375rem] font-medium`},`Cost by user`),m(`p`,{class:`text-sm text-muted`},` Who started the runs · LLM at list from their tokens · cloud allocated by wall-clock `)],-1),S.value?(u(),c(Pe,{key:0,report:S.value},null,8,[`report`])):(u(),h(`p`,bt,`The by-user report did not load with this page.`)),m(`p`,xt,[i[10]||=l(` Machine-readable twin: `,-1),m(`code`,St,`GET /costs/`+v(s.value.group)+`/users.json`,1),i[11]||=l(` (same Access gate). `,-1)])])):d(``,!0),x.value===`daily`?(u(),h(`section`,Ct,[i[13]||=m(`div`,null,[m(`h2`,{class:`text-[0.9375rem] font-medium`},`What each cloud dollar buys`)],-1),m(`table`,wt,[m(`tbody`,null,[(u(!0),h(a,null,g(k.value,e=>(u(),h(`tr`,{key:e.label,class:`border-b border-muted`},[m(`td`,Tt,[M(e.label)?(u(),h(`a`,{key:0,class:`no-underline hover:text-highlighted hover:underline`,href:M(e.label),target:`_blank`,rel:`noopener noreferrer`,title:`Open where this is billed on Cloudflare`},v(e.label),9,Et)):(u(),h(a,{key:1},[l(v(e.label),1)],64))]),m(`td`,Dt,v(_(T)(e.usd)),1),m(`td`,Ot,v(Math.round(e.percent))+`%`,1),m(`td`,kt,[m(`i`,{class:`block h-2.5 rounded-xs bg-primary opacity-75`,style:t({width:`${e.percent.toFixed(1)}%`})},null,4)])]))),128))])])])):d(``,!0),x.value===`daily`?(u(),h(`section`,At,[m(`details`,jt,[i[16]||=m(`summary`,{class:`cursor-pointer text-sm text-muted`},`Table view — daily cost by component (USD)`,-1),m(`div`,Mt,[m(`table`,Nt,[m(`thead`,null,[m(`tr`,null,[i[14]||=m(`th`,{class:`border-b border-muted bg-(--ui-bg-muted) px-2.5 py-1.5 text-left text-xs font-medium text-muted`},` Date `,-1),(u(!0),h(a,null,g(w.value,e=>(u(),h(`th`,{key:e,class:`border-b border-muted bg-(--ui-bg-muted) px-2.5 py-1.5 text-right text-xs font-medium text-muted`},v(e),1))),128)),i[15]||=m(`th`,{class:`border-b border-muted bg-(--ui-bg-muted) px-2.5 py-1.5 text-right text-xs font-medium text-muted`},` Total `,-1)])]),m(`tbody`,null,[(u(!0),h(a,null,g(F.value,e=>(u(),h(`tr`,{key:e.date},[m(`td`,{class:`border-b border-muted px-2.5 py-1.5`,title:e.date},[l(v(B(e.date))+` `,1),s.value.range.partialLastDay&&e.date===s.value.range.to?(u(),h(`span`,Pt,`(partial day`+v(e.llmEstimated?` · LLM estimated`:``)+`)`,1)):e.llmEstimated?(u(),h(`span`,Ft,`(LLM estimated)`)):d(``,!0)],8,$),(u(!0),h(a,null,g(w.value,t=>(u(),h(`td`,{key:t,class:`border-b border-muted px-2.5 py-1.5 text-right`},v(_(T)(_(O)(e,t),3)),1))),128)),m(`td`,It,v(_(T)(e.total,3)),1)]))),128))])])])]),m(`p`,Lt,[i[17]||=l(` Machine-readable twin: `,-1),m(`code`,Rt,`GET /costs/`+v(s.value.group)+`.json`,1),i[18]||=l(` (same Access gate). `,-1)])])):d(``,!0),m(`footer`,zt,[m(`details`,null,[i[26]||=m(`summary`,{class:`cursor-pointer text-muted`},`How these numbers are computed`,-1),m(`div`,Bt,[i[24]||=m(`div`,null,[m(`b`,null,`Live.`),l(` Both billing sources are read live from this page — nothing cached, nothing stored. Cloudflare bills vCPU on active use only; memory and disk bill on the provisioned size for every second a container is awake. LLM spend is the Anthropic Admin API cost report for this group's workspace (gross, USD); a day the cost report has not closed is the Admin API usage report, hourly, priced at Anthropic list per model (input, output, cache writes, cache reads) and marked as an estimate. `)],-1),i[25]||=m(`div`,null,[m(`b`,null,`Method.`),l(` Cloudflare GraphQL Analytics, every meter a Workers deployment is billed on: `),m(`code`,null,`containersUsageAdaptiveGroups`),l(` (cpuTimeSec, allocatedMemory, allocatedDisk per app per UTC day), `),m(`code`,null,`durableObjectsPeriodicGroups`),l(` (billable `),m(`code`,null,`duration`),l(` GB-s and SQLite rows read and written per namespace), `),m(`code`,null,`durableObjectsInvocationsAdaptiveGroups`),l(` (requests per namespace), `),m(`code`,null,`durableObjectsSqlStorageGroups`),l(` (bytes stored), `),m(`code`,null,`workersInvocationsAdaptive`),l(` (requests, CPU time), `),m(`code`,null,`r2StorageAdaptiveGroups`),l(` and `),m(`code`,null,`r2OperationsAdaptiveGroups`),l(` (bytes stored, class A/B operations). Prices: vCPU $0.000020/s, memory $0.0000025/GiB-s, disk $0.00000007/GB-s, DO duration $12.50 per million GB-s, DO requests $0.15/M, SQLite rows $0.001/M read and $1.00/M written, DO storage $0.20/GB-month, Workers $0.30/M requests and $0.02/M CPU-ms, R2 $0.015/GB-month, $4.50/M class A and $0.36/M class B (deletes free). Storage is the day's peak, prorated over a 30.44-day month. Gross list price — plan fees and included allowances are not subtracted; Workers Logs volume has no analytics dataset and is not priced. `)],-1),m(`div`,null,[i[19]||=m(`b`,null,`Scope.`,-1),i[20]||=l(` Attributed to this group: the Workers `,-1),(u(!0),h(a,null,g(s.value.attribution.workers,(e,t)=>(u(),h(a,{key:e},[t>0?(u(),h(a,{key:0},[l(`, `)],64)):d(``,!0),A.value?(u(),h(`a`,{key:1,class:`font-mono hover:underline`,href:A.value.worker(e),target:`_blank`,rel:`noopener noreferrer`},v(e),9,Vt)):(u(),h(`code`,Ht,v(e),1))],64))),128)),l(`, every Durable Object namespace they host (`+v(Object.keys(s.value.attribution.durableObjectNamespaces).length)+`), the R2 buckets named after them (`,1),(u(!0),h(a,null,g(Object.keys(s.value.attribution.r2Buckets),(e,t)=>(u(),h(a,{key:e},[t>0?(u(),h(a,{key:0},[l(`, `)],64)):d(``,!0),A.value?(u(),h(`a`,{key:1,class:`font-mono hover:underline`,href:A.value.bucket(e),target:`_blank`,rel:`noopener noreferrer`},v(e),9,Ut)):(u(),h(`code`,Wt,v(e),1))],64))),128)),Object.keys(s.value.attribution.r2Buckets).length===0?(u(),h(a,{key:0},[l(`none`)],64)):d(``,!0),i[21]||=l(`), and the container apps mapped in `,-1),m(`code`,null,`costs.groups.`+v(s.value.group),1),l(` (`+v(Object.keys(s.value.attribution.containerApps).length)+`). Everything else in the account is priced the same way into the account total, which is what "share of account" divides by. Every figure above links to where it is billed: the `,1),A.value?(u(),h(`a`,{key:1,class:`hover:underline`,href:A.value.account,target:`_blank`,rel:`noopener noreferrer`},`Cloudflare dashboard`,8,Gt)):d(``,!0),i[22]||=l(` for the account `,-1),m(`code`,null,v(s.value.account.id),1),l(v(s.value.account.name?` (${s.value.account.name})`:``)+`, and `,1),A.value?(u(),h(`a`,{key:2,class:`hover:underline`,href:A.value.anthropic,target:`_blank`,rel:`noopener noreferrer`},`Anthropic's cost page`,8,Kt)):d(``,!0),i[23]||=l(` for the LLM line. `,-1)])])])])]),_:1},8,[`title`])):d(``,!0)}});export{qt as default};