@coreplane/switchboard 1.209.0 → 1.211.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 (23) hide show
  1. package/dist/assets/deploy/cloudflare-resident/worker.ts +37 -9
  2. package/dist/assets/deploy/cloudflare-sandbox/worker.ts +22 -1
  3. package/dist/assets/package-lock.json +3 -3
  4. package/dist/assets/package.json +1 -1
  5. package/dist/assets/project.json +1 -1
  6. package/dist/assets/source.json +3 -3
  7. package/dist/assets/src/agents/registry.ts +27 -0
  8. package/dist/assets/src/core/runEvents.ts +19 -1
  9. package/dist/assets/src/core/runFriction.ts +6 -5
  10. package/dist/assets/src/core/trace/streamSpans.ts +3 -0
  11. package/dist/assets/src/execution/binaryRead.ts +58 -0
  12. package/dist/assets/web/dist/.vite/manifest.json +18 -18
  13. package/dist/assets/web/dist/assets/{ResidentDetailPage-CV3wfAIP.js → ResidentDetailPage-CM5nWw-Z.js} +1 -1
  14. package/dist/assets/web/dist/assets/{ResidentsIndexPage-CLkWc50b.js → ResidentsIndexPage-BKBnuvp3.js} +1 -1
  15. package/dist/assets/web/dist/assets/{RunRoutePage-CQYRfQ_B.js → RunRoutePage-Bu4CwEzt.js} +4 -4
  16. package/dist/assets/web/dist/assets/{RunsIndexPage-BLRPp_gk.js → RunsIndexPage-w2jJP5xu.js} +1 -1
  17. package/dist/assets/web/dist/assets/{ScheduledPage-C-VO4Ddl.js → ScheduledPage-D3-k9DPz.js} +1 -1
  18. package/dist/assets/web/dist/assets/{StatusDot-CIAoBB5Y.js → StatusDot-BmFHnV8m.js} +1 -1
  19. package/dist/assets/web/dist/assets/{Tooltip-DEL1ic4g.js → Tooltip-DoThP2fW.js} +1 -1
  20. package/dist/assets/web/dist/assets/{dist-CawBR4t8.js → dist-YrRKtxsS.js} +1 -1
  21. package/dist/assets/web/dist/assets/{main-CctUbVOl.js → main-D6nzMf0k.js} +2 -2
  22. package/dist/cli.js +495 -107
  23. package/package.json +1 -1
@@ -95,6 +95,15 @@ import {
95
95
  type ThreadDepsMechanism,
96
96
  } from "../../src/execution/residentDepCache.js";
97
97
  import { parseWorktreeCleanliness, worktreeCleanlinessScript } from "../../src/execution/residentCleanliness.js";
98
+ import {
99
+ base64ByteLength,
100
+ MAX_READ_BASE64_CHARS,
101
+ MAX_READ_BYTES,
102
+ readCommandFor,
103
+ readEncodingOf,
104
+ type Base64ReadAnswer,
105
+ type ReadEncoding,
106
+ } from "../../src/execution/binaryRead.js";
98
107
  import {
99
108
  capBytesFor,
100
109
  capWrappedCommand,
@@ -5170,36 +5179,53 @@ export class ResidentDO extends Sandbox<Env> {
5170
5179
  }
5171
5180
 
5172
5181
  /** POST /read: cat the file AS THE THREAD USER — the OS layer (not just
5173
- * the prefix check) is what confines a symlink pointing outside. */
5174
- async readThreadFile(threadKey: string, path: string): Promise<{ content: string; truncated: boolean } | ThreadErr> {
5175
- return this.withThreadBusy(threadKey, () => this.readThreadFileImpl(threadKey, path));
5182
+ * the prefix check) is what confines a symlink pointing outside. A
5183
+ * `base64` read (src/execution/binaryRead.ts) runs `base64 -w0` instead,
5184
+ * under the binary cap; an overflow is the named refusal, never a slice
5185
+ * of the encoding. */
5186
+ async readThreadFile(
5187
+ threadKey: string,
5188
+ path: string,
5189
+ encoding: ReadEncoding = "utf8",
5190
+ ): Promise<{ content: string; truncated: boolean } | Base64ReadAnswer | ThreadErr> {
5191
+ return this.withThreadBusy(threadKey, () => this.readThreadFileImpl(threadKey, path, encoding));
5176
5192
  }
5177
5193
 
5178
5194
  private async readThreadFileImpl(
5179
5195
  threadKey: string,
5180
5196
  path: string,
5181
- ): Promise<{ content: string; truncated: boolean } | ThreadErr> {
5197
+ encoding: ReadEncoding,
5198
+ ): Promise<{ content: string; truncated: boolean } | Base64ReadAnswer | ThreadErr> {
5182
5199
  const pre = await this.threadPreflight(threadKey);
5183
5200
  if ("error" in pre) return pre;
5184
5201
  const resolved = confineThreadPath(pre.binding.worktreePath, path);
5185
5202
  if (!resolved)
5186
5203
  return { error: `path-escape: ${JSON.stringify(path)} does not stay inside the thread worktree`, status: 400 };
5204
+ const cap = encoding === "base64" ? MAX_READ_BASE64_CHARS : READ_CONTENT_CAP;
5187
5205
  let r: Awaited<ReturnType<ResidentDO["threadRun"]>>;
5188
5206
  try {
5189
5207
  r = await this.threadRun(
5190
5208
  pre.binding.user,
5191
5209
  pre.binding.worktreePath,
5192
- `cat -- ${resolved}`,
5210
+ readCommandFor(encoding, resolved),
5193
5211
  DEFAULT_EXEC_TIMEOUT_MS,
5194
- capBytesFor(READ_CONTENT_CAP),
5212
+ capBytesFor(cap),
5195
5213
  );
5196
5214
  } catch (err) {
5197
5215
  if (err instanceof RuntimeReplacedError) return runtimeReplacedErr(err);
5198
5216
  throw err;
5199
5217
  }
5200
5218
  if (r.exitCode !== 0 || r.timedOut) return { error: `read-failed: ${describeStepFailure(r)}`, status: 404 };
5201
- const truncated = r.stdout.length > READ_CONTENT_CAP;
5202
- return { content: truncated ? r.stdout.slice(0, READ_CONTENT_CAP) : r.stdout, truncated };
5219
+ const truncated = r.stdout.length > cap;
5220
+ if (encoding === "base64") {
5221
+ // Padding hides up to two bytes inside the cap's char count, so the
5222
+ // decoded size is checked too — the cap is bytes, not characters.
5223
+ const content = r.stdout.trimEnd();
5224
+ return truncated || base64ByteLength(content) > MAX_READ_BYTES
5225
+ ? { encoding, tooLarge: true }
5226
+ : { encoding, content };
5227
+ }
5228
+ return { content: truncated ? r.stdout.slice(0, cap) : r.stdout, truncated };
5203
5229
  }
5204
5230
 
5205
5231
  /** POST /write: content travels via the SDK file API into the thread's
@@ -7186,7 +7212,9 @@ async function handleRead(env: Env, body: Record<string, unknown>): Promise<Resp
7186
7212
  if (ctx instanceof Response) return ctx;
7187
7213
  if (typeof body.path !== "string")
7188
7214
  return json({ error: "path must be a string relative to the thread worktree" }, 400);
7189
- const result = await ctx.stub.readThreadFile(ctx.threadKey, body.path);
7215
+ const encoding = readEncodingOf(body);
7216
+ if (typeof encoding !== "string") return json({ error: encoding.error }, 400);
7217
+ const result = await ctx.stub.readThreadFile(ctx.threadKey, body.path, encoding);
7190
7218
  if ("error" in result) return threadErrResponse(result);
7191
7219
  return json(result);
7192
7220
  }
@@ -18,6 +18,12 @@
18
18
  // first deploy; the SDK is young and its surface may shift.
19
19
  import { getSandbox, Sandbox, type ExecOptions, type ExecResult } from "@cloudflare/sandbox";
20
20
  import { BASH_TIMEOUT_MAX_MS, clampBashTimeout } from "../../src/execution/bashTimeout.js";
21
+ import {
22
+ base64ByteLength,
23
+ MAX_READ_BYTES,
24
+ readEncodingOf,
25
+ type Base64ReadAnswer,
26
+ } from "../../src/execution/binaryRead.js";
21
27
  import {
22
28
  EXEC_KEEPALIVE_INTERVAL_MS,
23
29
  SANDBOX_SLEEP_AFTER,
@@ -265,7 +271,22 @@ export default {
265
271
  );
266
272
  }
267
273
  case "/read": {
268
- const file = await withSessionRecovery(sandbox, () => sandbox.readFile(abs(String(body.path ?? ""))));
274
+ // `encoding: "base64"` is a binary read (src/execution/binaryRead.ts):
275
+ // the SDK encodes the bytes, and a file over the cap is refused by
276
+ // name inside a 200 — the client counts a non-2xx as a sick Worker.
277
+ const encoding = readEncodingOf(body);
278
+ if (typeof encoding !== "string") return json({ error: encoding.error }, 400);
279
+ const path = abs(String(body.path ?? ""));
280
+ if (encoding === "base64") {
281
+ const file = await withSessionRecovery(sandbox, () => sandbox.readFile(path, { encoding: "base64" }));
282
+ const content = typeof file === "string" ? file : (file?.content ?? "");
283
+ const answer: Base64ReadAnswer =
284
+ base64ByteLength(content) > MAX_READ_BYTES
285
+ ? { encoding: "base64", tooLarge: true }
286
+ : { encoding: "base64", content };
287
+ return json(answer);
288
+ }
289
+ const file = await withSessionRecovery(sandbox, () => sandbox.readFile(path));
269
290
  return json({ content: typeof file === "string" ? file : (file?.content ?? "") });
270
291
  }
271
292
  case "/write": {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "switchboard",
3
- "version": "1.209.0",
3
+ "version": "1.211.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "switchboard",
9
- "version": "1.209.0",
9
+ "version": "1.211.0",
10
10
  "license": "Apache-2.0",
11
11
  "workspaces": [
12
12
  "web",
@@ -18999,7 +18999,7 @@
18999
18999
  },
19000
19000
  "packages/switchboard": {
19001
19001
  "name": "@coreplane/switchboard",
19002
- "version": "1.209.0",
19002
+ "version": "1.211.0",
19003
19003
  "license": "Apache-2.0",
19004
19004
  "dependencies": {
19005
19005
  "@anthropic-ai/sdk": "^0.124.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "switchboard",
3
- "version": "1.209.0",
3
+ "version": "1.211.0",
4
4
  "private": true,
5
5
  "description": "Mention it in Slack and an agent reviews the PR, ships the fix, or answers the question — on the model you choose, with its tools running where you decide.",
6
6
  "license": "Apache-2.0",
@@ -212,7 +212,7 @@
212
212
  "when": "Part of `check:consistency`."
213
213
  },
214
214
  "load": {
215
- "does": "Load harness: `-- history|resident|sandbox|e2e|cards|provider|pi`.",
215
+ "does": "Load harness: `-- history|resident|sandbox|e2e|cards|provider|pi|route`.",
216
216
  "when": "Capacity receipts (docs/reference/specs/load-harness.md)."
217
217
  }
218
218
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.209.0",
3
- "commit": "921bd6b7bd8c7cd979c68435a47bc7ad169a3893",
4
- "builtAt": "2026-09-14T05:12:19.226Z"
2
+ "version": "1.211.0",
3
+ "commit": "1cee216b3cca53760bd183254c56904d4b990aa9",
4
+ "builtAt": "2026-09-14T06:10:14.818Z"
5
5
  }
@@ -96,6 +96,14 @@ export interface AgentDef {
96
96
  * worktree flag and the token the sandbox env and the `repo-cold` vet mint
97
97
  * read this, through the run's effective profile. */
98
98
  identity: Identity;
99
+ /** Whether the request router (docs/reference/specs/routing-and-config.md
100
+ * item 21) may pick this preset for a plain message. Absent means yes: the
101
+ * router's table is rendered from this registry. `false` keeps a preset
102
+ * for a typed directive alone — structurally: it is absent from the table
103
+ * the model is shown and refused by the allowlist even if the model names
104
+ * it. `ship` (it holds the merge grant) and `conductor` (it starts other
105
+ * runs) opt out. */
106
+ routable?: false;
99
107
  /** System prompt variant for resident-repo runs (docs/reference/specs/resident-repos.md):
100
108
  * the workspace is a ready worktree — no cloning, no installs, no repo
101
109
  * discovery, no gh CLI. Selected by the dispatcher AFTER executor
@@ -170,6 +178,13 @@ const IMAGE_TOOLCHAIN = `Node 24 with npm and pnpm; python3, make and g++ (nativ
170
178
  const SANDBOX_TOOLCHAIN = `The sandbox image carries ${IMAGE_TOOLCHAIN}; and Docker (the engine starts on the first \`docker\` call).`;
171
179
  const RESIDENT_TOOLCHAIN = `The resident image carries ${IMAGE_TOOLCHAIN}; plus yarn and bun — and no Docker.`;
172
180
 
181
+ // Both coding prompts carry this verbatim (docs/reference/specs/agent-coding.md
182
+ // item 10): the one way a run's screenshot reaches the person. Said once so
183
+ // the sandbox and resident variants cannot drift on it.
184
+ const SHOW_FILES = `Files the person should SEE go through the attach_file tool: a screenshot from \`playwright screenshot\`, a rendered PDF, a recording — it posts the workspace file into this conversation, where an image renders inline. Use it whenever you produce an image worth showing (a visual change, a rendered page, a before/after); a link to a file on GitHub is not a picture.
185
+ SCREENSHOTS GO TO BOTH PLACES, ALL OF THEM: when the request asks for screenshots, or the change is visual, every capture is attached here with attach_file AND published on the pull request — commit the images to an assets branch (never the PR's own diff) and reference them from the description's validation section or a PR comment so they render inline there too — unless the request names one destination. Never attach a subset and link the rest.
186
+ Text stays in your message; do not attach what you can say.`;
187
+
173
188
  const CODING_SYSTEM = `You are Switchboard's coding agent, operating from a Slack request.
174
189
 
175
190
  You work inside a dedicated workspace directory with bash, read_file, and write_file tools. ${SANDBOX_TOOLCHAIN}
@@ -197,6 +212,8 @@ ${UNIT_HANDOFF}
197
212
 
198
213
  ${PR_DESCRIPTION_TEMPLATE}
199
214
 
215
+ ${SHOW_FILES}
216
+
200
217
  Maintain the user-facing status card with the update_status tool: right after you decide your plan, post it as a checklist (○ pending items), then update it whenever an item starts (✱) or finishes (✓). Items are short outcomes ("Clone repo and read the diff", "Run the test suite"), never commands. Mark an item ✓ only after it has actually happened — never pre-mark reporting/posting steps. This is the only progress the user sees while you work.
201
218
 
202
219
  If the request doesn't name a repository and you can't infer it, ask for it instead of guessing.
@@ -235,6 +252,8 @@ ${UNIT_HANDOFF}
235
252
 
236
253
  ${PR_DESCRIPTION_TEMPLATE}
237
254
 
255
+ ${SHOW_FILES}
256
+
238
257
  Maintain the user-facing status card with the update_status tool: right after you decide your plan, post it as a checklist (○ pending items), then update it whenever an item starts (✱) or finishes (✓). Items are short outcomes ("Implement the fix", "Run the test suite"), never commands. Mark an item ✓ only after it has actually happened — never pre-mark reporting/posting steps. This is the only progress the user sees while you work.
239
258
 
240
259
  Report outcomes faithfully: if tests fail or a step was skipped, say so plainly.
@@ -477,6 +496,10 @@ export const AGENTS: Record<string, AgentDef> = {
477
496
  toolset: "full",
478
497
  machine: "repo-resident",
479
498
  identity: "write",
499
+ // Never routed: ship is the plan runner and holds the merge grant, so a
500
+ // wrong route into it is code landing on main, not a stray pull request.
501
+ // A request that wants it names it — `agent:ship`.
502
+ routable: false,
480
503
  maxTurns: 1,
481
504
  maxTokens: 16000,
482
505
  maxMinutes: 120,
@@ -520,6 +543,10 @@ export const AGENTS: Record<string, AgentDef> = {
520
543
  // dispatcher, the GitHub reads are REST in the bot process.
521
544
  machine: "none",
522
545
  identity: "none",
546
+ // Never offered to the router: its children are runs under the requester's
547
+ // permissions with a directive of their own, so a routed conductor could
548
+ // fan a plain message out into runs nobody named — a person names it.
549
+ routable: false,
523
550
  maxTokens: 32000,
524
551
  ...loopBudget(120), // long enough to outlast a coding child; every child is capped by what remains of it
525
552
  // No built-in effort: the deployment decides, as for coding.
@@ -144,6 +144,13 @@ type _EveryKindListed = [RunNoteKind] extends [(typeof RUN_NOTE_KINDS)[number]]
144
144
  const _everyKindListed: _EveryKindListed = true;
145
145
  void _everyKindListed;
146
146
 
147
+ /** How a run's preset was chosen (docs/reference/specs/routing-and-config.md
148
+ * items 1–3 and 21), as `run_meta.agentSource` records it: a directive on
149
+ * the message, the thread's sticky preset, the user or the channel scope's
150
+ * `agent`, `defaults.agent`, or the request router. The replay harness
151
+ * (`load route`) reads it to tell a requester's own choice from a fallback. */
152
+ export type AgentSource = "directive" | "sticky" | "user" | "channel" | "default" | "route";
153
+
147
154
  /** How an operator asked a run to stop: `soft` — take no new steps and
148
155
  * wrap up through the normal finale; `hard` — abort the in-flight call now, no
149
156
  * finale, tear the workspace down. */
@@ -204,13 +211,14 @@ export function isSpanRecord(e: { type: string }): e is SpanStartEvent | SpanEnd
204
211
  }
205
212
 
206
213
  /** The protected head (docs/reference/specs/tracing.md; live-view item 2) — is this event head material? The root's start, `slack.receive` and the
207
- * `dispatch.*` span pairs, `input`, `context`, `run_meta`, and the
214
+ * `dispatch.*` span pairs, `input`, `context`, `run_meta`, `route`, and the
208
215
  * `mcp_unavailable` / `spans_dropped` notes. */
209
216
  export function isHeadMaterial(event: RunEvent): boolean {
210
217
  switch (event.type) {
211
218
  case "input":
212
219
  case "context":
213
220
  case "run_meta":
221
+ case "route":
214
222
  return true;
215
223
  case "run_note":
216
224
  return event.kind === "mcp_unavailable" || event.kind === "spans_dropped" || event.kind === "cold_sandbox";
@@ -361,6 +369,8 @@ export type RunEvent =
361
369
  | {
362
370
  type: "run_meta";
363
371
  agent: string;
372
+ /** How `agent` was chosen (`AgentSource`); absent on a command run and on records written before it existed. */
373
+ agentSource?: AgentSource;
364
374
  /** Absent on a command run, which resolves no model. */
365
375
  model?: string;
366
376
  /** The request's trace id (docs/reference/specs/tracing.md), once the root exists. */
@@ -476,6 +486,14 @@ export type RunEvent =
476
486
  * straight to the registry (like `pr_opened`), never through the runner.
477
487
  * Additive: unknown → ignored. */
478
488
  | { type: "ship_round"; index: number; agent: string; outcome: ShipRoundOutcome; seq?: number; at?: number }
489
+ /** The request router's decision (docs/reference/specs/routing-and-config.md
490
+ * item 21): the preset a plain message was routed to, the one-line reason
491
+ * the router gave (redacted, capped — the same text the card's `routed:`
492
+ * line carries) and the model that decided. Published by the dispatcher
493
+ * straight to the registry right after `run_meta`, once per routed run;
494
+ * absent on every run a directive, a sticky preset or a scope chose. Head
495
+ * material, like `run_meta`. Additive: unknown → ignored. */
496
+ | { type: "route"; preset: string; reason: string; model: string; seq?: number; at?: number }
479
497
  /** The span records (docs/reference/specs/tracing.md): published, counted and stored like
480
498
  * every other event, read as timing and never as content. */
481
499
  | SpanStartEvent
@@ -370,7 +370,7 @@ export function analyzeRunFriction(events: readonly RunEvent[], opts: FrictionOp
370
370
  // final answer (`answer`) — are the run's story, not its steps: none counts
371
371
  // toward `eventCount`.
372
372
  let narrativeEvents = 0;
373
- let sideFactEvents = 0; // skill_use / review_artifact / pr_description / pr_opened / review_posted / ship_round: facts about the run, not steps
373
+ let sideFactEvents = 0; // skill_use / review_artifact / pr_description / pr_opened / review_posted / ship_round / route: facts about the run, not steps
374
374
  let spanEvents = 0; // span_start / span_end (docs/reference/specs/tracing.md): timing records, not steps
375
375
  let wrapUp: { index: number; at?: number } | undefined;
376
376
  events.forEach((ev, index) => {
@@ -385,16 +385,17 @@ export function analyzeRunFriction(events: readonly RunEvent[], opts: FrictionOp
385
385
  }
386
386
  // Side facts about the run, not steps: skill_use rides beside a use_skill
387
387
  // call that already produced its own tool pair; review_artifact,
388
- // pr_description, pr_opened, review_posted and the ship_round boundaries
389
- // are published by the dispatcher/pipeline outside the model loop
390
- // entirely. Counting any of them would distort the story.
388
+ // pr_description, pr_opened, review_posted, the ship_round boundaries and
389
+ // the router's route are published by the dispatcher/pipeline outside the
390
+ // model loop entirely. Counting any of them would distort the story.
391
391
  if (
392
392
  ev.type === "skill_use" ||
393
393
  ev.type === "review_artifact" ||
394
394
  ev.type === "pr_description" ||
395
395
  ev.type === "pr_opened" ||
396
396
  ev.type === "review_posted" ||
397
- ev.type === "ship_round"
397
+ ev.type === "ship_round" ||
398
+ ev.type === "route"
398
399
  ) {
399
400
  sideFactEvents++;
400
401
  return;
@@ -38,6 +38,7 @@ export const STREAMED_SPANS = [
38
38
  "dispatch.refuse",
39
39
  "dispatch.ship_preflight",
40
40
  "dispatch.ledger_claim",
41
+ "dispatch.route",
41
42
  "run.agent",
42
43
  "run.command",
43
44
  "run.reading_diff",
@@ -89,6 +90,7 @@ const GETTING_READY: ReadonlySet<string> = new Set([
89
90
  "dispatch.refuse",
90
91
  "dispatch.ship_preflight",
91
92
  "dispatch.ledger_claim",
93
+ "dispatch.route",
92
94
  ]);
93
95
  const FINISHING_UP: ReadonlySet<string> = new Set([
94
96
  "run.observe_workspace",
@@ -145,6 +147,7 @@ export const PARENTS: Readonly<Record<string, readonly string[]>> = {
145
147
  "dispatch.refuse": ["request"],
146
148
  "dispatch.ship_preflight": ["request"],
147
149
  "dispatch.ledger_claim": ["request"],
150
+ "dispatch.route": ["request"],
148
151
  "run.agent": ["request", "ship.round", "run.settle_reviewed_head", "run.description_turn"],
149
152
  "run.command": ["request"],
150
153
  "run.reading_diff": ["request"],
@@ -0,0 +1,58 @@
1
+ // A binary read on the Executor seam (docs/reference/specs/execution.md item 19):
2
+ // the whole file as bytes, for a tool that hands a workspace artifact — a
3
+ // screenshot, a PDF — to somewhere that needs the bytes, not a text view. Both
4
+ // remote executors ask their Worker's `/read` route for `encoding: "base64"`;
5
+ // the Worker answers `{ content, encoding: "base64" }`. This module is the
6
+ // contract both ends share: the caps, the request/answer shape, the resident's
7
+ // read command. It is bundled into the Workers too, so it stays free of Node
8
+ // imports.
9
+
10
+ /** The most bytes one `readBytes` hands over. A binary cannot be truncated
11
+ * the way text output is, so a larger file is refused by name, never trimmed.
12
+ * 10 MiB covers a full-page 2× screenshot or a PDF several times over while
13
+ * keeping one read well inside a Worker isolate's memory. */
14
+ export const MAX_READ_BYTES = 10 * 1024 * 1024;
15
+
16
+ /** The base64 length a `MAX_READ_BYTES` file encodes to (four chars per three
17
+ * bytes, padded) — the Worker-side output cap for a base64 read. */
18
+ export const MAX_READ_BASE64_CHARS = Math.ceil(MAX_READ_BYTES / 3) * 4;
19
+
20
+ export type ReadEncoding = "utf8" | "base64";
21
+
22
+ /** The encoding a `/read` body asks for. Absent is the body every pre-binary
23
+ * client sends — a text read, exactly as before; `"base64"` asks for bytes;
24
+ * anything else is refused by name rather than silently read as text. */
25
+ export function readEncodingOf(body: Record<string, unknown>): ReadEncoding | { error: string } {
26
+ if (body.encoding === undefined) return "utf8";
27
+ if (body.encoding === "base64") return "base64";
28
+ return { error: `encoding must be "base64" or absent, got ${JSON.stringify(body.encoding)}` };
29
+ }
30
+
31
+ /** The command a resident runs for a read of an already-confined path: `cat`
32
+ * for text; `base64 -w0` for bytes — one unwrapped line, so the Durable
33
+ * Object's character cap slices a plain string and nothing else. */
34
+ export function readCommandFor(encoding: ReadEncoding, resolvedPath: string): string {
35
+ return encoding === "base64" ? `base64 -w0 -- ${resolvedPath}` : `cat -- ${resolvedPath}`;
36
+ }
37
+
38
+ /** How many bytes a base64 string decodes to, padding discounted. */
39
+ export function base64ByteLength(b64: string): number {
40
+ const trimmed = b64.replace(/\s+/g, "");
41
+ if (trimmed.length === 0) return 0;
42
+ const padding = trimmed.endsWith("==") ? 2 : trimmed.endsWith("=") ? 1 : 0;
43
+ return Math.floor((trimmed.length * 3) / 4) - padding;
44
+ }
45
+
46
+ /** A Worker's answer to a base64 read: the bytes, or the named refusal of a
47
+ * file over the cap — HTTP 200 either way. The clients classify a non-2xx or
48
+ * an in-body `error` as a sick Worker (fail-fast counts it); a large file is
49
+ * the model's mistake, not infrastructure, so it travels as a plain field. */
50
+ export type Base64ReadAnswer = { encoding: "base64"; content: string } | { encoding: "base64"; tooLarge: true };
51
+
52
+ /** One message for a file over the cap, for every implementation. `bytes` is
53
+ * the size when the reader could measure it; a resident sees only that its
54
+ * capped base64 stream overflowed. */
55
+ export function tooLargeMessage(path: string, bytes?: number): string {
56
+ const size = bytes === undefined ? "" : ` (${bytes} bytes)`;
57
+ return `${path}${size} is over the ${MAX_READ_BYTES}-byte cap of a binary read`;
58
+ }
@@ -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-CawBR4t8.js",
31
+ "file": "assets/dist-YrRKtxsS.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-CIAoBB5Y.js": {
2566
- "file": "assets/StatusDot-CIAoBB5Y.js",
2565
+ "_StatusDot-BmFHnV8m.js": {
2566
+ "file": "assets/StatusDot-BmFHnV8m.js",
2567
2567
  "name": "StatusDot",
2568
2568
  "imports": [
2569
2569
  "_seed-DeKSHrt2.js",
2570
- "_Tooltip-DEL1ic4g.js"
2570
+ "_Tooltip-DoThP2fW.js"
2571
2571
  ]
2572
2572
  },
2573
- "_Tooltip-DEL1ic4g.js": {
2574
- "file": "assets/Tooltip-DEL1ic4g.js",
2573
+ "_Tooltip-DoThP2fW.js": {
2574
+ "file": "assets/Tooltip-DoThP2fW.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-CctUbVOl.js",
2792
+ "file": "assets/main-D6nzMf0k.js",
2793
2793
  "name": "main",
2794
2794
  "src": "src/main.ts",
2795
2795
  "isEntry": true,
@@ -2841,19 +2841,19 @@
2841
2841
  ]
2842
2842
  },
2843
2843
  "src/pages/ResidentDetailPage.vue": {
2844
- "file": "assets/ResidentDetailPage-CV3wfAIP.js",
2844
+ "file": "assets/ResidentDetailPage-CM5nWw-Z.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-CIAoBB5Y.js",
2851
+ "_StatusDot-BmFHnV8m.js",
2852
2852
  "_residentDiskBudget-BMBKlYRH.js"
2853
2853
  ]
2854
2854
  },
2855
2855
  "src/pages/ResidentsIndexPage.vue": {
2856
- "file": "assets/ResidentsIndexPage-CLkWc50b.js",
2856
+ "file": "assets/ResidentsIndexPage-BKBnuvp3.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-CIAoBB5Y.js",
2864
+ "_StatusDot-BmFHnV8m.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-CQYRfQ_B.js",
2870
+ "file": "assets/RunRoutePage-Bu4CwEzt.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-DEL1ic4g.js",
2877
+ "_Tooltip-DoThP2fW.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-BLRPp_gk.js",
2893
+ "file": "assets/RunsIndexPage-w2jJP5xu.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-DEL1ic4g.js",
2900
+ "_Tooltip-DoThP2fW.js",
2901
2901
  "_wallClock-61NpVkY6.js",
2902
2902
  "_RunsTabs-DEICQ4FY.js",
2903
- "_StatusDot-CIAoBB5Y.js",
2903
+ "_StatusDot-BmFHnV8m.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-C-VO4Ddl.js",
2910
+ "file": "assets/ScheduledPage-D3-k9DPz.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-DEL1ic4g.js",
2917
+ "_Tooltip-DoThP2fW.js",
2918
2918
  "_wallClock-61NpVkY6.js",
2919
2919
  "_RunsTabs-DEICQ4FY.js",
2920
2920
  "_indexFormat-B-pd4r7Y.js",
@@ -1 +1 @@
1
- import{t as e}from"./AppShell-lYVcj-k7.js";import{Lr as t,Xt as n,_n as r,cn as i,gn as a,ir as o,ln as ee,on as s,r as c,sn as l,un as u,ur as d,vi as f,wi as p,yn as te}from"./seed-DeKSHrt2.js";import{t as m}from"./StatusDot-CIAoBB5Y.js";import{a as ne,c as re,d as ie,f as h,i as g,l as ae,n as oe,o as se,r as ce,s as _,t as le,u as ue}from"./residentDiskBudget-BMBKlYRH.js";var de={class:`mt-4 first:mt-0`},fe={class:`w-full border-collapse font-mono text-[0.8125rem]`},pe={class:`border-t border-muted`},me={class:`px-2 py-1 align-top`},v={class:`ml-1.5 font-medium`},y={class:`border-t border-muted`},b={class:`break-all px-2 py-1 align-top`},x={key:0},S={key:1,class:`text-dimmed`},C={class:`border-t border-muted`},w={class:`break-all px-2 py-1 align-top`},T={key:0},E={key:1,class:`text-dimmed`},D={class:`border-t border-muted`},O={class:`break-all px-2 py-1 align-top`},k={key:0},A={key:1,class:`text-dimmed`},j={class:`border-t border-muted`},M={class:`break-all px-2 py-1 align-top`},N={key:0},P={key:1,class:`text-dimmed`},F={class:`border-t border-muted`},I={class:`break-all px-2 py-1 align-top`},L={key:1,class:`text-dimmed`},R={class:`mt-4`},z={class:`w-full border-collapse font-mono text-[0.8125rem]`},he={class:`border-t border-muted`},ge={class:`break-all px-2 py-1 align-top`},_e=[`href`],ve={key:1},ye={class:`border-t border-muted`},be={class:`break-all px-2 py-1 align-top`},xe={class:`border-t border-muted`},Se={class:`break-all px-2 py-1 align-top`},Ce=[`href`],we={key:1},Te={key:2,class:`text-dimmed`},Ee={class:`border-t border-muted`},De={class:`break-all px-2 py-1 align-top`},Oe={key:0},ke={key:1,class:`text-dimmed`},Ae={class:`border-t border-muted`},je={class:`break-all px-2 py-1 align-top`},Me={key:0},Ne={key:1,class:`text-dimmed`},Pe={class:`border-t border-muted`},Fe={class:`break-all px-2 py-1 align-top`},Ie={key:0},Le={key:1,class:`text-dimmed`},Re={class:`mt-4`},ze={class:`w-full border-collapse font-mono text-[0.8125rem]`},Be={class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},Ve={class:`break-all px-2 py-1 align-top`},He={class:`w-full border-collapse font-mono text-[0.8125rem]`},Ue={class:`w-32 break-all px-2 py-1 align-top text-muted sm:w-96 sm:break-words`},We={class:`px-2 py-1 align-top`},Ge={key:1,class:`text-xs text-muted`},Ke={class:`mt-4`},qe={key:0,class:`w-full border-collapse font-mono text-[0.8125rem]`},Je={class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},Ye={class:`break-all px-2 py-1 align-top`},Xe={key:0},B={key:1,class:`text-dimmed`},Ze={key:1,class:`text-xs text-muted`},Qe={class:`mt-4`},$e={class:`mb-1 text-xs text-muted`},et={class:`overflow-x-auto`},tt={class:`w-full border-collapse font-mono text-[0.8125rem]`},nt={class:`break-all px-2 py-1 align-top`},rt={key:0,class:`text-xs text-muted`},it={class:`break-all px-2 py-1 align-top`},at=[`href`],ot={key:1},st={key:2,class:`text-dimmed`},ct={class:`break-all px-2 py-1 align-top`},lt={key:0},ut={key:1,class:`text-dimmed`},dt={key:2},ft={key:3,class:`text-dimmed`},pt={class:`break-all px-2 py-1 align-top`},mt={key:0},ht={key:1,class:`text-dimmed`},gt={key:2},_t={key:3,class:`text-dimmed`},vt={key:1,class:`text-xs text-muted`},yt={class:`mt-4`},bt={class:`w-full border-collapse font-mono text-[0.8125rem]`},xt={class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},St={class:`px-2 py-1 align-top`},Ct={class:`mt-4`},wt={key:0,class:`w-full border-collapse font-mono text-[0.8125rem]`},Tt={class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},Et={class:`break-all px-2 py-1 align-top`},Dt={class:`text-xs text-muted`},Ot={key:1,class:`text-xs text-muted`},kt={class:`mt-4`},At={class:`w-full border-collapse font-mono text-[0.8125rem]`},jt={class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},Mt={class:`break-all px-2 py-1 align-top`},Nt={key:0},Pt={key:1,class:`text-dimmed`},Ft={class:`mt-4 text-xs text-muted`},It={class:`rounded bg-elevated px-1 py-0.5`},Lt={class:`rounded bg-elevated px-1 py-0.5`},Rt={class:`rounded bg-elevated px-1 py-0.5`},V=te({__name:`ResidentDetailPage`,setup(te){let V=c(`resident`),H=s(()=>V?.record??{}),U=s(()=>ae(H.value)),W=s(()=>ue(H.value)||(V?.slug??``)),G=s(()=>h(U.value.sha)),K=s(()=>se.test(W.value)?`https://github.com/${W.value}`:void 0),q=s(()=>K.value&&/^[0-9a-f]{7,40}$/.test(G.value)?`${K.value}/commit/${G.value}`:void 0),J=s(()=>{let e=U.value.snapshot;return e&&typeof e==`object`?_(e):void 0}),zt=s(()=>_(U.value.schedules)),Y=s(()=>{let e=_(H.value.commands),t=_(H.value.effects);return Object.keys(e).sort().map(n=>({name:n,command:h(e[n]),effect:h(t[n])||`readonly`}))}),X=s(()=>{let e=U.value.threads;return(Array.isArray(e)?e.map(_):[]).map(e=>{let t=h(e.sha);return{threadKey:h(e.threadKey)||`?`,ref:h(e.ref)||`?`,sha:t?t.slice(0,8):``,commitHref:K.value&&/^[0-9a-f]{7,40}$/.test(t)?`${K.value}/commit/${t}`:void 0,user:h(e.user),deps:h(e.deps),boundAt:h(e.boundAt),lastAttachAt:h(e.lastAttachAt),evicted:e.evicted===!0,evictedAt:h(e.evictedAt),evictedWhy:h(e.evictedWhy)}}).sort((e,t)=>t.lastAttachAt.localeCompare(e.lastAttachAt))}),Z=s(()=>X.value.filter(e=>!e.evicted).length),Q=s(()=>{let e=U.value.lastRestore;return e?JSON.stringify(e):``}),$=s(()=>re(H.value)),Bt=s(()=>typeof H.value.diskBudgetMb==`number`?H.value.diskBudgetMb:void 0),Vt=s(()=>{let e=$.value;if(!e)return[];let t=le(e),{freeKiB:n,capped:r,capacityKiB:i}=oe(e,Bt.value),a=n-t.totalKiB,o=t=>{let n=ne(e.parts,t);return n===null?`? (checkout not measured)`:n===0?`?`:`${Math.max(0,Math.floor(a/n))} more (${g(n)} each)`};return[[`used / total`,ce(e)],[`free`,`${g(n)}${r?` under the ${g(i)} diskBudgetMb cap`:``}`],[`reserve`,`${g(t.totalKiB)} (snapshot staging ${g(t.stagingKiB)} + floor ${g(t.floorKiB)})`],[`headroom`,`${g(Math.max(0,a))} — room for ${o(`hardlink`)} hardlinked trees, ${o(`reconcile`)} lockfile-diverged (reconciling)`],[`measured`,e.at||`—`]]}),Ht=s(()=>{let e=$.value;if(!e)return[];let t=[[`mirror`,g(e.parts.mirror)],[`checkout deps (node_modules)`,g(e.parts.deps)],[`checkout (history + tree + build)`,g(e.parts.checkout)]];for(let[n,r]of Object.entries(e.parts.threads).sort((e,t)=>t[1]-e[1]))t.push([`thread ${n}`,g(r)]);for(let[n,r]of Object.entries(e.parts.homes).sort((e,t)=>t[1]-e[1]))r>=1024&&t.push([`home ${n}`,g(r)]);return t.push([`other (image, /tmp, …)`,g(e.parts.other)]),t});return(s,c)=>(o(),i(e,{title:W.value||`Resident`,nav:`residents`},{leading:t(()=>[...c[0]||=[l(`a`,{class:`back text-sm text-muted no-underline hover:text-primary`,href:`/residents`},`← All residents`,-1)]]),default:t(()=>[l(`section`,de,[c[8]||=l(`h2`,{class:`mb-1.5 font-mono text-xs font-medium uppercase tracking-wider text-muted`},`Lifecycle`,-1),l(`table`,fe,[l(`tbody`,null,[l(`tr`,pe,[c[1]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`state`,-1),l(`td`,me,[r(m,{tone:f(ie)(U.value.state),label:U.value.state},null,8,[`tone`,`label`]),l(`span`,v,p(U.value.state),1)])]),l(`tr`,y,[c[2]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`reason`,-1),l(`td`,b,[U.value.reason?(o(),u(`span`,x,p(U.value.reason),1)):(o(),u(`span`,S,`—`))])]),l(`tr`,C,[c[3]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`last refresh error`,-1),l(`td`,w,[f(h)(U.value.lastRefreshError)?(o(),u(`span`,T,p(f(h)(U.value.lastRefreshError)),1)):(o(),u(`span`,E,`—`))])]),l(`tr`,D,[c[4]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`last restore`,-1),l(`td`,O,[Q.value?(o(),u(`span`,k,p(Q.value),1)):(o(),u(`span`,A,`—`))])]),l(`tr`,j,[c[5]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`state updated`,-1),l(`td`,M,[f(h)(U.value.updatedAt)?(o(),u(`span`,N,p(f(h)(U.value.updatedAt)),1)):(o(),u(`span`,P,`—`))])]),l(`tr`,F,[c[7]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`idle since`,-1),l(`td`,I,[f(h)(U.value.idleSince)?(o(),u(n,{key:0},[a(p(f(h)(U.value.idleSince))+` `,1),c[6]||=l(`span`,{class:`text-xs text-muted`},`(refresh parked; container may sleep)`,-1)],64)):(o(),u(`span`,L,`awake`))])])])])]),l(`section`,R,[c[15]||=l(`h2`,{class:`mb-1.5 font-mono text-xs font-medium uppercase tracking-wider text-muted`},`Pinned facts`,-1),l(`table`,z,[l(`tbody`,null,[l(`tr`,he,[c[9]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`repository`,-1),l(`td`,ge,[K.value?(o(),u(`a`,{key:0,class:`text-primary`,href:K.value},p(K.value),9,_e)):(o(),u(`span`,ve,p(f(h)(H.value.resource)||`—`),1))])]),l(`tr`,ye,[c[10]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`default ref`,-1),l(`td`,be,p(f(h)(U.value.defaultRef)||f(h)(H.value.defaultRef)||`—`),1)]),l(`tr`,xe,[c[11]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`pinned sha`,-1),l(`td`,Se,[q.value?(o(),u(`a`,{key:0,class:`text-primary`,href:q.value},p(G.value),9,Ce)):G.value?(o(),u(`span`,we,p(G.value),1)):(o(),u(`span`,Te,`—`))])]),l(`tr`,Ee,[c[12]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`lockfile hash`,-1),l(`td`,De,[f(h)(U.value.lockfileHash)?(o(),u(`span`,Oe,p(f(h)(U.value.lockfileHash)),1)):(o(),u(`span`,ke,`—`))])]),l(`tr`,Ae,[c[13]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`provisioned`,-1),l(`td`,je,[f(h)(U.value.provisionedAt)?(o(),u(`span`,Me,p(f(h)(U.value.provisionedAt)),1)):(o(),u(`span`,Ne,`—`))])]),l(`tr`,Pe,[c[14]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`last refresh`,-1),l(`td`,Fe,[f(h)(U.value.lastRefreshAt)?(o(),u(`span`,Ie,p(f(h)(U.value.lastRefreshAt)),1)):(o(),u(`span`,Le,`—`))])])])])]),l(`section`,Re,[c[17]||=l(`h2`,{class:`mb-1.5 font-mono text-xs font-medium uppercase tracking-wider text-muted`},`Disk`,-1),$.value?(o(),u(n,{key:0},[l(`table`,ze,[l(`tbody`,null,[(o(!0),u(n,null,d(Vt.value,([e,t])=>(o(),u(`tr`,{key:e,class:`border-t border-muted`},[l(`td`,Be,p(e),1),l(`td`,Ve,p(t),1)]))),128))])]),c[16]||=l(`p`,{class:`mb-1 mt-2 text-xs text-muted`},` components (one du, hardlinks counted once — a thread tree shows only what it does not share with the checkout) `,-1),l(`table`,He,[l(`tbody`,null,[(o(!0),u(n,null,d(Ht.value,([e,t])=>(o(),u(`tr`,{key:e,class:`border-t border-muted`},[l(`td`,Ue,p(e),1),l(`td`,We,p(t),1)]))),128))])])],64)):(o(),u(`p`,Ge,` not measured yet — the first refresh cycle or attach of this container measures it `))]),l(`section`,Ke,[c[18]||=l(`h2`,{class:`mb-1.5 font-mono text-xs font-medium uppercase tracking-wider text-muted`},`Snapshot stamp`,-1),J.value?(o(),u(`table`,qe,[l(`tbody`,null,[(o(),u(n,null,d([`ref`,`sha`,`lockfileHash`,`createdAt`,`mirrorBackupId`,`checkoutBackupId`],e=>l(`tr`,{key:e,class:`border-t border-muted`},[l(`td`,Je,p(e),1),l(`td`,Ye,[f(h)(J.value[e])?(o(),u(`span`,Xe,p(f(h)(J.value[e])),1)):(o(),u(`span`,B,`—`))])])),64))])])):(o(),u(`p`,Ze,`no snapshot recorded`))]),l(`section`,Qe,[c[22]||=l(`h2`,{class:`mb-1.5 font-mono text-xs font-medium uppercase tracking-wider text-muted`},`Thread worktrees`,-1),X.value.length>0?(o(),u(n,{key:0},[l(`p`,$e,p(Z.value)+` live · `+p(X.value.length-Z.value)+` evicted`,1),l(`div`,et,[l(`table`,tt,[l(`tbody`,null,[c[21]||=l(`tr`,{class:`text-xs text-muted`},[l(`td`,{class:`px-2 py-1`},`thread`),l(`td`,{class:`px-2 py-1`},`ref · sha`),l(`td`,{class:`px-2 py-1`},`user · deps`),l(`td`,{class:`px-2 py-1`},`bound · last attach`)],-1),(o(!0),u(n,null,d(X.value,e=>(o(),u(`tr`,{key:e.threadKey,class:`border-t border-muted`},[l(`td`,nt,[e.evicted?(o(),u(`span`,rt,[a(`evicted `+p(e.evictedAt||`—`),1),e.evictedWhy?(o(),u(n,{key:0},[a(` · `+p(e.evictedWhy),1)],64)):ee(``,!0)])):(o(),i(m,{key:1,tone:`green`,label:`live`,tip:`live`})),a(` `+p(e.threadKey),1)]),l(`td`,it,[a(p(e.ref)+` · `,1),e.commitHref?(o(),u(`a`,{key:0,class:`text-primary`,href:e.commitHref},p(e.sha),9,at)):e.sha?(o(),u(`span`,ot,p(e.sha),1)):(o(),u(`span`,st,`—`))]),l(`td`,ct,[e.user?(o(),u(`span`,lt,p(e.user),1)):(o(),u(`span`,ut,`—`)),c[19]||=a(` · `,-1),e.deps?(o(),u(`span`,dt,p(e.deps),1)):(o(),u(`span`,ft,`—`))]),l(`td`,pt,[e.boundAt?(o(),u(`span`,mt,p(e.boundAt),1)):(o(),u(`span`,ht,`—`)),c[20]||=a(` · `,-1),e.lastAttachAt?(o(),u(`span`,gt,p(e.lastAttachAt),1)):(o(),u(`span`,_t,`—`))])]))),128))])])])],64)):(o(),u(`p`,vt,`no thread worktrees`))]),l(`section`,yt,[c[23]||=l(`h2`,{class:`mb-1.5 font-mono text-xs font-medium uppercase tracking-wider text-muted`},`Pending schedules`,-1),l(`table`,bt,[l(`tbody`,null,[(o(),u(n,null,d([[`refresh`,`refresh`],[`provision run`,`provisionRun`],[`provision deadline`,`provisionDeadline`]],([e,t])=>l(`tr`,{key:t,class:`border-t border-muted`},[l(`td`,xt,p(e),1),l(`td`,St,p(f(h)(zt.value[t]??0)||`0`),1)])),64))])])]),l(`section`,Ct,[c[25]||=l(`h2`,{class:`mb-1.5 font-mono text-xs font-medium uppercase tracking-wider text-muted`},`Command table`,-1),Y.value.length>0?(o(),u(`table`,wt,[l(`tbody`,null,[(o(!0),u(n,null,d(Y.value,e=>(o(),u(`tr`,{key:e.name,class:`border-t border-muted`},[l(`td`,Tt,p(e.name),1),l(`td`,Et,[l(`code`,null,p(e.command),1),c[24]||=a(),l(`span`,Dt,p(e.effect),1)])]))),128))])])):(o(),u(`p`,Ot,`no command table`))]),l(`section`,kt,[c[26]||=l(`h2`,{class:`mb-1.5 font-mono text-xs font-medium uppercase tracking-wider text-muted`},`Registry settings`,-1),l(`table`,At,[l(`tbody`,null,[(o(!0),u(n,null,d([[`onboarded`,f(h)(H.value.onboardedAt)],[`record updated`,f(h)(H.value.updatedAt)],[`provisioning timeout (ms)`,f(h)(H.value.provisioningTimeoutMs)],[`disk budget (MB)`,f(h)(H.value.diskBudgetMb)],[`worktree TTL (days)`,f(h)(H.value.worktreeTtlDays??7)]],([e,t])=>(o(),u(`tr`,{key:e,class:`border-t border-muted`},[l(`td`,jt,p(e),1),l(`td`,Mt,[t?(o(),u(`span`,Nt,p(t),1)):(o(),u(`span`,Pt,`—`))])]))),128))])])]),l(`p`,Ft,[c[27]||=a(` Manage from chat: `,-1),l(`code`,It,`repo rebuild `+p(W.value),1),c[28]||=a(` · `,-1),l(`code`,Lt,`repo reconfigure `+p(W.value)+` …`,1),c[29]||=a(` · `,-1),l(`code`,Rt,`repo offboard `+p(W.value)+` --dry-run`,1)])]),_:1},8,[`title`]))}});export{V as default};
1
+ import{t as e}from"./AppShell-lYVcj-k7.js";import{Lr as t,Xt as n,_n as r,cn as i,gn as a,ir as o,ln as ee,on as s,r as c,sn as l,un as u,ur as d,vi as f,wi as p,yn as te}from"./seed-DeKSHrt2.js";import{t as m}from"./StatusDot-BmFHnV8m.js";import{a as ne,c as re,d as ie,f as h,i as g,l as ae,n as oe,o as se,r as ce,s as _,t as le,u as ue}from"./residentDiskBudget-BMBKlYRH.js";var de={class:`mt-4 first:mt-0`},fe={class:`w-full border-collapse font-mono text-[0.8125rem]`},pe={class:`border-t border-muted`},me={class:`px-2 py-1 align-top`},v={class:`ml-1.5 font-medium`},y={class:`border-t border-muted`},b={class:`break-all px-2 py-1 align-top`},x={key:0},S={key:1,class:`text-dimmed`},C={class:`border-t border-muted`},w={class:`break-all px-2 py-1 align-top`},T={key:0},E={key:1,class:`text-dimmed`},D={class:`border-t border-muted`},O={class:`break-all px-2 py-1 align-top`},k={key:0},A={key:1,class:`text-dimmed`},j={class:`border-t border-muted`},M={class:`break-all px-2 py-1 align-top`},N={key:0},P={key:1,class:`text-dimmed`},F={class:`border-t border-muted`},I={class:`break-all px-2 py-1 align-top`},L={key:1,class:`text-dimmed`},R={class:`mt-4`},z={class:`w-full border-collapse font-mono text-[0.8125rem]`},he={class:`border-t border-muted`},ge={class:`break-all px-2 py-1 align-top`},_e=[`href`],ve={key:1},ye={class:`border-t border-muted`},be={class:`break-all px-2 py-1 align-top`},xe={class:`border-t border-muted`},Se={class:`break-all px-2 py-1 align-top`},Ce=[`href`],we={key:1},Te={key:2,class:`text-dimmed`},Ee={class:`border-t border-muted`},De={class:`break-all px-2 py-1 align-top`},Oe={key:0},ke={key:1,class:`text-dimmed`},Ae={class:`border-t border-muted`},je={class:`break-all px-2 py-1 align-top`},Me={key:0},Ne={key:1,class:`text-dimmed`},Pe={class:`border-t border-muted`},Fe={class:`break-all px-2 py-1 align-top`},Ie={key:0},Le={key:1,class:`text-dimmed`},Re={class:`mt-4`},ze={class:`w-full border-collapse font-mono text-[0.8125rem]`},Be={class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},Ve={class:`break-all px-2 py-1 align-top`},He={class:`w-full border-collapse font-mono text-[0.8125rem]`},Ue={class:`w-32 break-all px-2 py-1 align-top text-muted sm:w-96 sm:break-words`},We={class:`px-2 py-1 align-top`},Ge={key:1,class:`text-xs text-muted`},Ke={class:`mt-4`},qe={key:0,class:`w-full border-collapse font-mono text-[0.8125rem]`},Je={class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},Ye={class:`break-all px-2 py-1 align-top`},Xe={key:0},B={key:1,class:`text-dimmed`},Ze={key:1,class:`text-xs text-muted`},Qe={class:`mt-4`},$e={class:`mb-1 text-xs text-muted`},et={class:`overflow-x-auto`},tt={class:`w-full border-collapse font-mono text-[0.8125rem]`},nt={class:`break-all px-2 py-1 align-top`},rt={key:0,class:`text-xs text-muted`},it={class:`break-all px-2 py-1 align-top`},at=[`href`],ot={key:1},st={key:2,class:`text-dimmed`},ct={class:`break-all px-2 py-1 align-top`},lt={key:0},ut={key:1,class:`text-dimmed`},dt={key:2},ft={key:3,class:`text-dimmed`},pt={class:`break-all px-2 py-1 align-top`},mt={key:0},ht={key:1,class:`text-dimmed`},gt={key:2},_t={key:3,class:`text-dimmed`},vt={key:1,class:`text-xs text-muted`},yt={class:`mt-4`},bt={class:`w-full border-collapse font-mono text-[0.8125rem]`},xt={class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},St={class:`px-2 py-1 align-top`},Ct={class:`mt-4`},wt={key:0,class:`w-full border-collapse font-mono text-[0.8125rem]`},Tt={class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},Et={class:`break-all px-2 py-1 align-top`},Dt={class:`text-xs text-muted`},Ot={key:1,class:`text-xs text-muted`},kt={class:`mt-4`},At={class:`w-full border-collapse font-mono text-[0.8125rem]`},jt={class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},Mt={class:`break-all px-2 py-1 align-top`},Nt={key:0},Pt={key:1,class:`text-dimmed`},Ft={class:`mt-4 text-xs text-muted`},It={class:`rounded bg-elevated px-1 py-0.5`},Lt={class:`rounded bg-elevated px-1 py-0.5`},Rt={class:`rounded bg-elevated px-1 py-0.5`},V=te({__name:`ResidentDetailPage`,setup(te){let V=c(`resident`),H=s(()=>V?.record??{}),U=s(()=>ae(H.value)),W=s(()=>ue(H.value)||(V?.slug??``)),G=s(()=>h(U.value.sha)),K=s(()=>se.test(W.value)?`https://github.com/${W.value}`:void 0),q=s(()=>K.value&&/^[0-9a-f]{7,40}$/.test(G.value)?`${K.value}/commit/${G.value}`:void 0),J=s(()=>{let e=U.value.snapshot;return e&&typeof e==`object`?_(e):void 0}),zt=s(()=>_(U.value.schedules)),Y=s(()=>{let e=_(H.value.commands),t=_(H.value.effects);return Object.keys(e).sort().map(n=>({name:n,command:h(e[n]),effect:h(t[n])||`readonly`}))}),X=s(()=>{let e=U.value.threads;return(Array.isArray(e)?e.map(_):[]).map(e=>{let t=h(e.sha);return{threadKey:h(e.threadKey)||`?`,ref:h(e.ref)||`?`,sha:t?t.slice(0,8):``,commitHref:K.value&&/^[0-9a-f]{7,40}$/.test(t)?`${K.value}/commit/${t}`:void 0,user:h(e.user),deps:h(e.deps),boundAt:h(e.boundAt),lastAttachAt:h(e.lastAttachAt),evicted:e.evicted===!0,evictedAt:h(e.evictedAt),evictedWhy:h(e.evictedWhy)}}).sort((e,t)=>t.lastAttachAt.localeCompare(e.lastAttachAt))}),Z=s(()=>X.value.filter(e=>!e.evicted).length),Q=s(()=>{let e=U.value.lastRestore;return e?JSON.stringify(e):``}),$=s(()=>re(H.value)),Bt=s(()=>typeof H.value.diskBudgetMb==`number`?H.value.diskBudgetMb:void 0),Vt=s(()=>{let e=$.value;if(!e)return[];let t=le(e),{freeKiB:n,capped:r,capacityKiB:i}=oe(e,Bt.value),a=n-t.totalKiB,o=t=>{let n=ne(e.parts,t);return n===null?`? (checkout not measured)`:n===0?`?`:`${Math.max(0,Math.floor(a/n))} more (${g(n)} each)`};return[[`used / total`,ce(e)],[`free`,`${g(n)}${r?` under the ${g(i)} diskBudgetMb cap`:``}`],[`reserve`,`${g(t.totalKiB)} (snapshot staging ${g(t.stagingKiB)} + floor ${g(t.floorKiB)})`],[`headroom`,`${g(Math.max(0,a))} — room for ${o(`hardlink`)} hardlinked trees, ${o(`reconcile`)} lockfile-diverged (reconciling)`],[`measured`,e.at||`—`]]}),Ht=s(()=>{let e=$.value;if(!e)return[];let t=[[`mirror`,g(e.parts.mirror)],[`checkout deps (node_modules)`,g(e.parts.deps)],[`checkout (history + tree + build)`,g(e.parts.checkout)]];for(let[n,r]of Object.entries(e.parts.threads).sort((e,t)=>t[1]-e[1]))t.push([`thread ${n}`,g(r)]);for(let[n,r]of Object.entries(e.parts.homes).sort((e,t)=>t[1]-e[1]))r>=1024&&t.push([`home ${n}`,g(r)]);return t.push([`other (image, /tmp, …)`,g(e.parts.other)]),t});return(s,c)=>(o(),i(e,{title:W.value||`Resident`,nav:`residents`},{leading:t(()=>[...c[0]||=[l(`a`,{class:`back text-sm text-muted no-underline hover:text-primary`,href:`/residents`},`← All residents`,-1)]]),default:t(()=>[l(`section`,de,[c[8]||=l(`h2`,{class:`mb-1.5 font-mono text-xs font-medium uppercase tracking-wider text-muted`},`Lifecycle`,-1),l(`table`,fe,[l(`tbody`,null,[l(`tr`,pe,[c[1]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`state`,-1),l(`td`,me,[r(m,{tone:f(ie)(U.value.state),label:U.value.state},null,8,[`tone`,`label`]),l(`span`,v,p(U.value.state),1)])]),l(`tr`,y,[c[2]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`reason`,-1),l(`td`,b,[U.value.reason?(o(),u(`span`,x,p(U.value.reason),1)):(o(),u(`span`,S,`—`))])]),l(`tr`,C,[c[3]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`last refresh error`,-1),l(`td`,w,[f(h)(U.value.lastRefreshError)?(o(),u(`span`,T,p(f(h)(U.value.lastRefreshError)),1)):(o(),u(`span`,E,`—`))])]),l(`tr`,D,[c[4]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`last restore`,-1),l(`td`,O,[Q.value?(o(),u(`span`,k,p(Q.value),1)):(o(),u(`span`,A,`—`))])]),l(`tr`,j,[c[5]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`state updated`,-1),l(`td`,M,[f(h)(U.value.updatedAt)?(o(),u(`span`,N,p(f(h)(U.value.updatedAt)),1)):(o(),u(`span`,P,`—`))])]),l(`tr`,F,[c[7]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`idle since`,-1),l(`td`,I,[f(h)(U.value.idleSince)?(o(),u(n,{key:0},[a(p(f(h)(U.value.idleSince))+` `,1),c[6]||=l(`span`,{class:`text-xs text-muted`},`(refresh parked; container may sleep)`,-1)],64)):(o(),u(`span`,L,`awake`))])])])])]),l(`section`,R,[c[15]||=l(`h2`,{class:`mb-1.5 font-mono text-xs font-medium uppercase tracking-wider text-muted`},`Pinned facts`,-1),l(`table`,z,[l(`tbody`,null,[l(`tr`,he,[c[9]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`repository`,-1),l(`td`,ge,[K.value?(o(),u(`a`,{key:0,class:`text-primary`,href:K.value},p(K.value),9,_e)):(o(),u(`span`,ve,p(f(h)(H.value.resource)||`—`),1))])]),l(`tr`,ye,[c[10]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`default ref`,-1),l(`td`,be,p(f(h)(U.value.defaultRef)||f(h)(H.value.defaultRef)||`—`),1)]),l(`tr`,xe,[c[11]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`pinned sha`,-1),l(`td`,Se,[q.value?(o(),u(`a`,{key:0,class:`text-primary`,href:q.value},p(G.value),9,Ce)):G.value?(o(),u(`span`,we,p(G.value),1)):(o(),u(`span`,Te,`—`))])]),l(`tr`,Ee,[c[12]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`lockfile hash`,-1),l(`td`,De,[f(h)(U.value.lockfileHash)?(o(),u(`span`,Oe,p(f(h)(U.value.lockfileHash)),1)):(o(),u(`span`,ke,`—`))])]),l(`tr`,Ae,[c[13]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`provisioned`,-1),l(`td`,je,[f(h)(U.value.provisionedAt)?(o(),u(`span`,Me,p(f(h)(U.value.provisionedAt)),1)):(o(),u(`span`,Ne,`—`))])]),l(`tr`,Pe,[c[14]||=l(`td`,{class:`w-32 px-2 py-1 align-top text-muted sm:w-48 sm:whitespace-nowrap`},`last refresh`,-1),l(`td`,Fe,[f(h)(U.value.lastRefreshAt)?(o(),u(`span`,Ie,p(f(h)(U.value.lastRefreshAt)),1)):(o(),u(`span`,Le,`—`))])])])])]),l(`section`,Re,[c[17]||=l(`h2`,{class:`mb-1.5 font-mono text-xs font-medium uppercase tracking-wider text-muted`},`Disk`,-1),$.value?(o(),u(n,{key:0},[l(`table`,ze,[l(`tbody`,null,[(o(!0),u(n,null,d(Vt.value,([e,t])=>(o(),u(`tr`,{key:e,class:`border-t border-muted`},[l(`td`,Be,p(e),1),l(`td`,Ve,p(t),1)]))),128))])]),c[16]||=l(`p`,{class:`mb-1 mt-2 text-xs text-muted`},` components (one du, hardlinks counted once — a thread tree shows only what it does not share with the checkout) `,-1),l(`table`,He,[l(`tbody`,null,[(o(!0),u(n,null,d(Ht.value,([e,t])=>(o(),u(`tr`,{key:e,class:`border-t border-muted`},[l(`td`,Ue,p(e),1),l(`td`,We,p(t),1)]))),128))])])],64)):(o(),u(`p`,Ge,` not measured yet — the first refresh cycle or attach of this container measures it `))]),l(`section`,Ke,[c[18]||=l(`h2`,{class:`mb-1.5 font-mono text-xs font-medium uppercase tracking-wider text-muted`},`Snapshot stamp`,-1),J.value?(o(),u(`table`,qe,[l(`tbody`,null,[(o(),u(n,null,d([`ref`,`sha`,`lockfileHash`,`createdAt`,`mirrorBackupId`,`checkoutBackupId`],e=>l(`tr`,{key:e,class:`border-t border-muted`},[l(`td`,Je,p(e),1),l(`td`,Ye,[f(h)(J.value[e])?(o(),u(`span`,Xe,p(f(h)(J.value[e])),1)):(o(),u(`span`,B,`—`))])])),64))])])):(o(),u(`p`,Ze,`no snapshot recorded`))]),l(`section`,Qe,[c[22]||=l(`h2`,{class:`mb-1.5 font-mono text-xs font-medium uppercase tracking-wider text-muted`},`Thread worktrees`,-1),X.value.length>0?(o(),u(n,{key:0},[l(`p`,$e,p(Z.value)+` live · `+p(X.value.length-Z.value)+` evicted`,1),l(`div`,et,[l(`table`,tt,[l(`tbody`,null,[c[21]||=l(`tr`,{class:`text-xs text-muted`},[l(`td`,{class:`px-2 py-1`},`thread`),l(`td`,{class:`px-2 py-1`},`ref · sha`),l(`td`,{class:`px-2 py-1`},`user · deps`),l(`td`,{class:`px-2 py-1`},`bound · last attach`)],-1),(o(!0),u(n,null,d(X.value,e=>(o(),u(`tr`,{key:e.threadKey,class:`border-t border-muted`},[l(`td`,nt,[e.evicted?(o(),u(`span`,rt,[a(`evicted `+p(e.evictedAt||`—`),1),e.evictedWhy?(o(),u(n,{key:0},[a(` · `+p(e.evictedWhy),1)],64)):ee(``,!0)])):(o(),i(m,{key:1,tone:`green`,label:`live`,tip:`live`})),a(` `+p(e.threadKey),1)]),l(`td`,it,[a(p(e.ref)+` · `,1),e.commitHref?(o(),u(`a`,{key:0,class:`text-primary`,href:e.commitHref},p(e.sha),9,at)):e.sha?(o(),u(`span`,ot,p(e.sha),1)):(o(),u(`span`,st,`—`))]),l(`td`,ct,[e.user?(o(),u(`span`,lt,p(e.user),1)):(o(),u(`span`,ut,`—`)),c[19]||=a(` · `,-1),e.deps?(o(),u(`span`,dt,p(e.deps),1)):(o(),u(`span`,ft,`—`))]),l(`td`,pt,[e.boundAt?(o(),u(`span`,mt,p(e.boundAt),1)):(o(),u(`span`,ht,`—`)),c[20]||=a(` · `,-1),e.lastAttachAt?(o(),u(`span`,gt,p(e.lastAttachAt),1)):(o(),u(`span`,_t,`—`))])]))),128))])])])],64)):(o(),u(`p`,vt,`no thread worktrees`))]),l(`section`,yt,[c[23]||=l(`h2`,{class:`mb-1.5 font-mono text-xs font-medium uppercase tracking-wider text-muted`},`Pending schedules`,-1),l(`table`,bt,[l(`tbody`,null,[(o(),u(n,null,d([[`refresh`,`refresh`],[`provision run`,`provisionRun`],[`provision deadline`,`provisionDeadline`]],([e,t])=>l(`tr`,{key:t,class:`border-t border-muted`},[l(`td`,xt,p(e),1),l(`td`,St,p(f(h)(zt.value[t]??0)||`0`),1)])),64))])])]),l(`section`,Ct,[c[25]||=l(`h2`,{class:`mb-1.5 font-mono text-xs font-medium uppercase tracking-wider text-muted`},`Command table`,-1),Y.value.length>0?(o(),u(`table`,wt,[l(`tbody`,null,[(o(!0),u(n,null,d(Y.value,e=>(o(),u(`tr`,{key:e.name,class:`border-t border-muted`},[l(`td`,Tt,p(e.name),1),l(`td`,Et,[l(`code`,null,p(e.command),1),c[24]||=a(),l(`span`,Dt,p(e.effect),1)])]))),128))])])):(o(),u(`p`,Ot,`no command table`))]),l(`section`,kt,[c[26]||=l(`h2`,{class:`mb-1.5 font-mono text-xs font-medium uppercase tracking-wider text-muted`},`Registry settings`,-1),l(`table`,At,[l(`tbody`,null,[(o(!0),u(n,null,d([[`onboarded`,f(h)(H.value.onboardedAt)],[`record updated`,f(h)(H.value.updatedAt)],[`provisioning timeout (ms)`,f(h)(H.value.provisioningTimeoutMs)],[`disk budget (MB)`,f(h)(H.value.diskBudgetMb)],[`worktree TTL (days)`,f(h)(H.value.worktreeTtlDays??7)]],([e,t])=>(o(),u(`tr`,{key:e,class:`border-t border-muted`},[l(`td`,jt,p(e),1),l(`td`,Mt,[t?(o(),u(`span`,Nt,p(t),1)):(o(),u(`span`,Pt,`—`))])]))),128))])])]),l(`p`,Ft,[c[27]||=a(` Manage from chat: `,-1),l(`code`,It,`repo rebuild `+p(W.value),1),c[28]||=a(` · `,-1),l(`code`,Lt,`repo reconfigure `+p(W.value)+` …`,1),c[29]||=a(` · `,-1),l(`code`,Rt,`repo offboard `+p(W.value)+` --dry-run`,1)])]),_:1},8,[`title`]))}});export{V as default};
@@ -1 +1 @@
1
- import{t as e}from"./AppShell-lYVcj-k7.js";import{Lr as t,Xt as n,_n as r,cn as i,gn as a,ir as o,ln as s,mr as c,on as l,r as u,sn as d,un as f,ur as p,wi as m,xi as h,yn as g}from"./seed-DeKSHrt2.js";import{n as _}from"./wallClock-61NpVkY6.js";import{t as v}from"./StatusDot-CIAoBB5Y.js";import{c as y,d as b,f as x,l as S,o as C,r as w,u as T}from"./residentDiskBudget-BMBKlYRH.js";import{n as E}from"./indexFormat-B-pd4r7Y.js";var D={class:`mb-2 text-xs text-muted`},O={class:`m-0 list-none p-0`},k={class:`font-mono font-medium text-primary`},A={class:`font-medium`},j=[`title`],M=[`title`],N={key:0,class:`basis-full pl-5 text-xs text-warn`},P={key:1,class:`px-2 py-1.5 text-muted`},F=g({__name:`ResidentsIndexPage`,setup(g){let F=u(`residents`),I=_(),L=l(()=>(F?.residents??[]).map((e,t)=>{let n=e,r=T(n),i=S(n),a=x(i.sha),o=x(i.lastRefreshAt),s=o?Date.parse(o):NaN,c=y(n);return{key:`${r||`?`}-${t}`,display:r||x(n.resource)||`?`,state:i.state,tone:b(i.state),reason:i.reason,ref:x(n.defaultRef)||`?`,sha:a?a.slice(0,8):``,refreshed:o,refreshedLabel:Number.isFinite(s)?E(s,I):o,disk:c?w(c):``,diskAt:c?.at??``,href:C.test(r)?`/residents/${r}`:null}})),R=l(()=>x(F?.cap)||`?`),z=l(()=>x(F?.count)||String(L.value.length));return(l,u)=>(o(),i(e,{title:`Resident repos`,nav:`residents`},{default:t(()=>[L.value.length>0?(o(),f(n,{key:0},[d(`p`,D,m(z.value)+`/`+m(R.value)+` resident slots in use · live registry read, not cached `,1),d(`ul`,O,[(o(!0),f(n,null,p(L.value,e=>(o(),f(`li`,{key:e.key,class:`border-b border-muted first:border-t`},[(o(),i(c(e.href?`a`:`span`),{class:h([`row flex flex-wrap items-center gap-2.5 rounded-md px-2 py-1.5 text-inherit no-underline`,e.href?`hover:bg-elevated`:``]),href:e.href??void 0},{default:t(()=>[r(v,{tone:e.tone,label:e.state,tip:e.state},null,8,[`tone`,`label`,`tip`]),d(`span`,k,m(e.display),1),d(`span`,A,m(e.state),1),d(`span`,{class:`font-mono text-xs text-muted max-sm:basis-full max-sm:pl-5`,title:e.refreshed||void 0},[a(` ref `+m(e.ref),1),e.sha?(o(),f(n,{key:0},[a(` · sha `+m(e.sha),1)],64)):s(``,!0),e.refreshedLabel?(o(),f(n,{key:1},[a(` · refreshed `+m(e.refreshedLabel),1)],64)):s(``,!0),e.disk?(o(),f(n,{key:2},[u[0]||=a(` · `,-1),d(`span`,{title:e.diskAt?`measured ${e.diskAt}`:void 0},`disk `+m(e.disk),9,M)],64)):s(``,!0)],8,j),e.reason?(o(),f(`span`,N,m(e.reason),1)):s(``,!0)]),_:2},1032,[`class`,`href`]))]))),128))])],64)):(o(),f(`p`,P,[a(` No repos onboarded (0/`+m(R.value)+`). Onboard one from chat: `,1),u[1]||=d(`code`,{class:`rounded bg-elevated px-1.5 py-0.5`},`repo onboard <owner/name>`,-1),u[2]||=a(`. `,-1)]))]),_:1}))}});export{F as default};
1
+ import{t as e}from"./AppShell-lYVcj-k7.js";import{Lr as t,Xt as n,_n as r,cn as i,gn as a,ir as o,ln as s,mr as c,on as l,r as u,sn as d,un as f,ur as p,wi as m,xi as h,yn as g}from"./seed-DeKSHrt2.js";import{n as _}from"./wallClock-61NpVkY6.js";import{t as v}from"./StatusDot-BmFHnV8m.js";import{c as y,d as b,f as x,l as S,o as C,r as w,u as T}from"./residentDiskBudget-BMBKlYRH.js";import{n as E}from"./indexFormat-B-pd4r7Y.js";var D={class:`mb-2 text-xs text-muted`},O={class:`m-0 list-none p-0`},k={class:`font-mono font-medium text-primary`},A={class:`font-medium`},j=[`title`],M=[`title`],N={key:0,class:`basis-full pl-5 text-xs text-warn`},P={key:1,class:`px-2 py-1.5 text-muted`},F=g({__name:`ResidentsIndexPage`,setup(g){let F=u(`residents`),I=_(),L=l(()=>(F?.residents??[]).map((e,t)=>{let n=e,r=T(n),i=S(n),a=x(i.sha),o=x(i.lastRefreshAt),s=o?Date.parse(o):NaN,c=y(n);return{key:`${r||`?`}-${t}`,display:r||x(n.resource)||`?`,state:i.state,tone:b(i.state),reason:i.reason,ref:x(n.defaultRef)||`?`,sha:a?a.slice(0,8):``,refreshed:o,refreshedLabel:Number.isFinite(s)?E(s,I):o,disk:c?w(c):``,diskAt:c?.at??``,href:C.test(r)?`/residents/${r}`:null}})),R=l(()=>x(F?.cap)||`?`),z=l(()=>x(F?.count)||String(L.value.length));return(l,u)=>(o(),i(e,{title:`Resident repos`,nav:`residents`},{default:t(()=>[L.value.length>0?(o(),f(n,{key:0},[d(`p`,D,m(z.value)+`/`+m(R.value)+` resident slots in use · live registry read, not cached `,1),d(`ul`,O,[(o(!0),f(n,null,p(L.value,e=>(o(),f(`li`,{key:e.key,class:`border-b border-muted first:border-t`},[(o(),i(c(e.href?`a`:`span`),{class:h([`row flex flex-wrap items-center gap-2.5 rounded-md px-2 py-1.5 text-inherit no-underline`,e.href?`hover:bg-elevated`:``]),href:e.href??void 0},{default:t(()=>[r(v,{tone:e.tone,label:e.state,tip:e.state},null,8,[`tone`,`label`,`tip`]),d(`span`,k,m(e.display),1),d(`span`,A,m(e.state),1),d(`span`,{class:`font-mono text-xs text-muted max-sm:basis-full max-sm:pl-5`,title:e.refreshed||void 0},[a(` ref `+m(e.ref),1),e.sha?(o(),f(n,{key:0},[a(` · sha `+m(e.sha),1)],64)):s(``,!0),e.refreshedLabel?(o(),f(n,{key:1},[a(` · refreshed `+m(e.refreshedLabel),1)],64)):s(``,!0),e.disk?(o(),f(n,{key:2},[u[0]||=a(` · `,-1),d(`span`,{title:e.diskAt?`measured ${e.diskAt}`:void 0},`disk `+m(e.disk),9,M)],64)):s(``,!0)],8,j),e.reason?(o(),f(`span`,N,m(e.reason),1)):s(``,!0)]),_:2},1032,[`class`,`href`]))]))),128))])],64)):(o(),f(`p`,P,[a(` No repos onboarded (0/`+m(R.value)+`). Onboard one from chat: `,1),u[1]||=d(`code`,{class:`rounded bg-elevated px-1.5 py-0.5`},`repo onboard <owner/name>`,-1),u[2]||=a(`. `,-1)]))]),_:1}))}});export{F as default};