@coreplane/switchboard 1.266.1 → 1.267.1

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 (46) hide show
  1. package/dist/assets/deploy/cloudflare-memory/package.json +1 -1
  2. package/dist/assets/deploy/cloudflare-memory/tsconfig.json +1 -1
  3. package/dist/assets/deploy/cloudflare-resident/gc.ts +1 -0
  4. package/dist/assets/deploy/cloudflare-resident/keyedAsyncLock.ts +20 -0
  5. package/dist/assets/deploy/cloudflare-resident/legacyCredentials.ts +57 -0
  6. package/dist/assets/deploy/cloudflare-resident/orphanThreadUsers.ts +14 -0
  7. package/dist/assets/deploy/cloudflare-resident/worker.ts +646 -204
  8. package/dist/assets/package-lock.json +4003 -4040
  9. package/dist/assets/package.json +2 -1
  10. package/dist/assets/source.json +3 -3
  11. package/dist/assets/src/core/budgets.ts +10 -0
  12. package/dist/assets/src/core/coordinator/contract.ts +59 -1
  13. package/dist/assets/src/core/coordinator/driver.ts +34 -5
  14. package/dist/assets/src/core/runRecord.ts +38 -3
  15. package/dist/assets/src/core/ship/coordinator.ts +121 -39
  16. package/dist/assets/src/core/trace/attrs.ts +3 -0
  17. package/dist/assets/src/execution/githubMintScope.ts +49 -0
  18. package/dist/assets/src/execution/residentCredentials.ts +9 -37
  19. package/dist/assets/src/execution/residentDestroyGate.ts +12 -0
  20. package/dist/assets/src/execution/residentPoolSpends.ts +96 -0
  21. package/dist/assets/src/execution/residentRecreateAdmission.ts +41 -0
  22. package/dist/assets/src/execution/residentSteps.ts +1 -0
  23. package/dist/assets/web/dist/.vite/manifest.json +67 -67
  24. package/dist/assets/web/dist/assets/{DeliveryPage-BUJckSuW.js → DeliveryPage-QyS6_Q0m.js} +1 -1
  25. package/dist/assets/web/dist/assets/{HomePage-BfpIrKDX.js → HomePage-Bm40r0wF.js} +1 -1
  26. package/dist/assets/web/dist/assets/{PendingTurnRow-C6hroa00.js → PendingTurnRow-BqY-EaZR.js} +1 -1
  27. package/dist/assets/web/dist/assets/{PlanePage-C2Cb1mkT.js → PlanePage-VmzjNLAz.js} +1 -1
  28. package/dist/assets/web/dist/assets/{ResidentDetailPage-Bd9_Q87l.js → ResidentDetailPage-hjX3tRCh.js} +1 -1
  29. package/dist/assets/web/dist/assets/{ResidentsIndexPage-C8eS7hxp.js → ResidentsIndexPage-BKGnlCEh.js} +1 -1
  30. package/dist/assets/web/dist/assets/{RunFoldRow-DrfFauYJ.js → RunFoldRow-BOWvSoIv.js} +1 -1
  31. package/dist/assets/web/dist/assets/{RunRoutePage-B_eXfNAF.js → RunRoutePage-D9ztf-uE.js} +3 -3
  32. package/dist/assets/web/dist/assets/{RunsIndexPage-BpQu36Dm.js → RunsIndexPage-DTYQbhtr.js} +1 -1
  33. package/dist/assets/web/dist/assets/{ScheduledPage-DGHcjcSt.js → ScheduledPage-BAYR0tuv.js} +1 -1
  34. package/dist/assets/web/dist/assets/{SettingsPage-IfQ71QjN.js → SettingsPage-BlYBmCG9.js} +1 -1
  35. package/dist/assets/web/dist/assets/{SilentTurn-D9ON15a8.js → SilentTurn-CHfzRQW8.js} +1 -1
  36. package/dist/assets/web/dist/assets/{StatusDot-DyFjGg4V.js → StatusDot-BMkUFVE8.js} +1 -1
  37. package/dist/assets/web/dist/assets/{Tooltip-BvPkV4kZ.js → Tooltip-Dfot16IA.js} +1 -1
  38. package/dist/assets/web/dist/assets/{UnitRoutePage-BNe3nBIG.js → UnitRoutePage-BdZoT-4d.js} +1 -1
  39. package/dist/assets/web/dist/assets/budgets-CJHiTZmM.js +1 -0
  40. package/dist/assets/web/dist/assets/{dist-v3Py3_QR.js → dist-XRrc0hhm.js} +1 -1
  41. package/dist/assets/web/dist/assets/{indexRow-BsWcHHVh.js → indexRow-BGjjLnN5.js} +1 -1
  42. package/dist/assets/web/dist/assets/{main-ks3kBojV.js → main-B32N1vUh.js} +2 -2
  43. package/dist/assets/web/dist/assets/{sseReplay-DuALaBPq.js → sseReplay-BsNQPLmJ.js} +2 -2
  44. package/dist/cli.js +1708 -625
  45. package/package.json +1 -1
  46. package/dist/assets/web/dist/assets/budgets-BQgcCyvb.js +0 -1
@@ -25,6 +25,8 @@ export interface AttrDomain {
25
25
  // slack.receive
26
26
  caughtUp: boolean;
27
27
  files: number;
28
+ /** Files found on the canonical source message after event metadata is normalized. */
29
+ resolvedFiles: number;
28
30
  dedupe: "fresh" | "duplicate";
29
31
  /** How the requester was found (slack-channel.md item 13): the sender, the configured relay app's footer, or the app itself. */
30
32
  requester: "message" | "relay-footer" | "bot";
@@ -193,6 +195,7 @@ const ATTR_TYPE: Record<SpanAttrKey, "string" | "number" | "boolean"> = {
193
195
  runId: "string",
194
196
  caughtUp: "boolean",
195
197
  files: "number",
198
+ resolvedFiles: "number",
196
199
  dedupe: "string",
197
200
  requester: "string",
198
201
  intake: "string",
@@ -0,0 +1,49 @@
1
+ /** Permissions for the trusted Git door's receive-pack operation. GitHub
2
+ * also requires Workflows when a pushed commit edits .github/workflows. */
3
+ export const DOOR_PUSH_PERMISSIONS = {
4
+ contents: "write",
5
+ workflows: "write",
6
+ metadata: "read",
7
+ } as const;
8
+
9
+ /** The resident's root-owned token only clones/fetches its mirror and reads
10
+ * pull request state for worktree reclamation. */
11
+ export const RESIDENT_MIRROR_PERMISSIONS = {
12
+ contents: "read",
13
+ pull_requests: "read",
14
+ metadata: "read",
15
+ } as const;
16
+
17
+ type Permission = "read" | "write";
18
+
19
+ function record(value: unknown): value is Record<string, unknown> {
20
+ return value !== null && typeof value === "object" && !Array.isArray(value);
21
+ }
22
+
23
+ /** Check GitHub's effective scope before a token enters either cache. GitHub
24
+ * may omit its implicit Metadata read grant from the response. Errors contain
25
+ * only a fixed reason; they never serialize a response or bearer. */
26
+ export function verifyGithubMintScope(
27
+ response: unknown,
28
+ repo: string,
29
+ requested: Readonly<Record<string, Permission>>,
30
+ ): void {
31
+ if (!record(response) || !record(response.permissions)) throw new Error("GitHub token scope mismatch");
32
+ const actual = response.permissions;
33
+ if (
34
+ Object.entries(actual).some(([name, value]) => requested[name] !== value) ||
35
+ Object.keys(requested).some((name) => !(name in actual) && !(name === "metadata" && requested[name] === "read"))
36
+ ) {
37
+ throw new Error("GitHub token scope mismatch");
38
+ }
39
+ if (
40
+ response.repository_selection !== "selected" ||
41
+ !Array.isArray(response.repositories) ||
42
+ response.repositories.length !== 1 ||
43
+ !record(response.repositories[0]) ||
44
+ typeof response.repositories[0].full_name !== "string" ||
45
+ response.repositories[0].full_name.toLowerCase() !== repo.toLowerCase()
46
+ ) {
47
+ throw new Error("GitHub token scope mismatch");
48
+ }
49
+ }
@@ -1,45 +1,17 @@
1
- /** The per-exec credential-refresh decision of the execution layer, kept pure
2
- * and dependency-free so it is unit-testable from src/ and imported across
3
- * packages by the Worker (like residentDetach/shellQuote) — the tested code
4
- * IS the shipped code. Two callers share it — the ONE credential refresher:
5
- * the resident Worker before every writable `/exec`
6
- * (deploy/cloudflare-resident/worker.ts `execThreadImpl`), and the sandbox
7
- * executor before every `/exec` it sends
8
- * (src/execution/sandboxCredentials.ts, issue 1915).
9
- *
10
- * Background: attach writes a 1-hour GitHub App installation token
11
- * into `<worktree>/.git/github-credentials` and points git's `store` helper
12
- * at it. Nothing used to refresh it during a run, so a push more than ~60
13
- * minutes after attach got 401 — and git's `store` helper ERASES a rejected
14
- * credential from its file, leaving the agent staring at an empty file. The
15
- * Worker now asks this function before every writable `/exec` and re-mints
16
- * (cached per slug) + rewrites the file when it says so.
17
- *
18
- * The refresh is driven by the TOKEN'S OWN EXPIRY, not the file's age.
19
- * `mintRepoScopedToken` caches per repository slug, so a token minted for an
20
- * earlier thread — with only a few minutes of life left — could be written
21
- * for a brand-new attach and, keyed on file age alone, read "fresh" for ~45
22
- * minutes while every writable exec in between ran on a dead token (the first
23
- * git write 401s, `store` blanks the file, and only the NEXT exec refreshed).
24
- * Now the binding records the token's `expiresAtMs`, and this predicate
25
- * refreshes once the token is within `CREDENTIAL_EXPIRY_MARGIN_MS` of expiry
26
- * — so a writable exec never starts on a token that cannot outlive it. */
1
+ /** The pure expiry decision retained for the optional legacy sandbox
2
+ * credential refresher. The production executor factory does not supply
3
+ * that credential source: model workspaces use a revocable Git-door run
4
+ * bearer, and the resident never refreshes a model-visible App token.
5
+ * The resident imports only the expiry margin for its root-owned mirror
6
+ * credential cache. */
27
7
 
28
8
  import { BASH_TIMEOUT_MAX_MS } from "./bashTimeout.js";
29
9
 
30
- /** Backstop for bindings that predate `tokenExpiresAtMs`: re-mint
31
- * this long after the file was written when the token's real expiry is
32
- * unknown — comfortably before the 60-minute token expiry. New bindings carry
33
- * the expiry and take the margin path below instead. */
10
+ /** Legacy refresher backstop when the stored token's expiry is unknown. */
34
11
  export const CREDENTIAL_REFRESH_AFTER_MS = 45 * 60_000;
35
12
 
36
- /** Refresh once the token is within this much of expiry, so a writable exec
37
- * never starts on a token that cannot outlive the command it is about to run.
38
- * Sized like the sandbox's per-command credential margin: the longest
39
- * single exec is `BASH_TIMEOUT_MAX_MS` (20 min), plus slack for the mint +
40
- * file write and clock skew ⇒ 25 min. This is also `mintRepoScopedToken`'s
41
- * cache serve threshold, so the cache and this predicate agree: a token the
42
- * cache would still hand out is a token this predicate would not refresh. */
13
+ /** Legacy refresh and root-mirror cache margin: the longest exec is
14
+ * `BASH_TIMEOUT_MAX_MS` (20 min), plus five minutes for mint and clock skew. */
43
15
  export const CREDENTIAL_EXPIRY_MARGIN_MS = BASH_TIMEOUT_MAX_MS + 5 * 60_000;
44
16
 
45
17
  export type CredentialRefreshReason =
@@ -0,0 +1,12 @@
1
+ /** Persist uncertainty before asking the platform to destroy a resident VM.
2
+ * A rejected or interrupted destroy leaves the fence in storage: callers may
3
+ * retry destruction, but may not provision or assign a pool user on the old
4
+ * disk. A failed clear is conservative too, even if the VM was destroyed. */
5
+ export async function destroyWithPersistentFence(
6
+ fence: { mark(): Promise<void>; clear(): Promise<void> },
7
+ destroy: () => Promise<void>,
8
+ ): Promise<void> {
9
+ await fence.mark();
10
+ await destroy();
11
+ await fence.clear();
12
+ }
@@ -0,0 +1,96 @@
1
+ /** Durable UID ownership within one VM generation. Missing or malformed
2
+ * records are unknown generations, never an empty pool. */
3
+ export interface SpentPoolUser {
4
+ user: string;
5
+ owner: string;
6
+ }
7
+
8
+ export function parseSpentPoolUsers(value: unknown, pool: readonly string[]): ReadonlyMap<string, string> | null {
9
+ if (
10
+ !Array.isArray(value) ||
11
+ !value.every(
12
+ (row): row is SpentPoolUser =>
13
+ row !== null &&
14
+ typeof row === "object" &&
15
+ "user" in row &&
16
+ "owner" in row &&
17
+ typeof row.user === "string" &&
18
+ typeof row.owner === "string" &&
19
+ /^(thread|op):\S+$/.test(row.owner),
20
+ )
21
+ )
22
+ return null;
23
+ const spent = new Map(value.map(({ user, owner }) => [user, owner]));
24
+ if (spent.size !== value.length || [...spent.keys()].some((user) => !pool.includes(user))) return null;
25
+ return spent;
26
+ }
27
+
28
+ /** A new owner must get an unused UID; only the exact recorded owner may
29
+ * reclaim its UID. Both paths record ownership before untrusted work. */
30
+ export function spendPoolUser(
31
+ value: unknown,
32
+ pool: readonly string[],
33
+ user: string,
34
+ owner: string,
35
+ ): SpentPoolUser[] | null {
36
+ const spent = parseSpentPoolUsers(value, pool);
37
+ if (!spent || !pool.includes(user) || !/^(thread|op):\S+$/.test(owner)) return null;
38
+ const prior = spent.get(user);
39
+ if (prior && prior !== owner) return null;
40
+ const rows = [...spent].map(([user, owner]) => ({ user, owner }));
41
+ return prior ? rows : [...rows, { user, owner }];
42
+ }
43
+
44
+ /** A UID-scoped command must have exactly one live owner. An old duplicate
45
+ * binding never gains authority merely because its UID is spent. */
46
+ export function mayRunAsPoolUser(
47
+ value: unknown,
48
+ pool: readonly string[],
49
+ user: string,
50
+ liveThreadKeys: readonly string[],
51
+ activeOpOwner: string | undefined,
52
+ expectedOwner?: string,
53
+ ): boolean {
54
+ const recorded = parseSpentPoolUsers(value, pool)?.get(user);
55
+ if (!recorded || (expectedOwner !== undefined && recorded !== expectedOwner)) return false;
56
+ if (activeOpOwner !== undefined) return liveThreadKeys.length === 0 && recorded === activeOpOwner;
57
+ return liveThreadKeys.length === 1 && recorded === `thread:${liveThreadKeys[0]}`;
58
+ }
59
+
60
+ /** One row per UID is rebuilt from primary bindings when a DO starts. A
61
+ * malformed or missing row cannot authorize a shell, even if the spend ledger
62
+ * names the caller. Keep every live claimant so duplicates remain visible. */
63
+ export function parsePoolBindings(value: unknown): string[] | null {
64
+ if (!Array.isArray(value) || !value.every((key): key is string => typeof key === "string" && key.length > 0))
65
+ return null;
66
+ if (new Set(value).size !== value.length) return null;
67
+ return value;
68
+ }
69
+
70
+ export function claimPoolBinding(value: unknown, threadKey: string): string[] | null {
71
+ const current = parsePoolBindings(value);
72
+ if (!current || !threadKey || current.includes(threadKey)) return null;
73
+ return [...current, threadKey];
74
+ }
75
+
76
+ export function releasePoolBinding(value: unknown, threadKey: string): string[] | null {
77
+ const current = parsePoolBindings(value);
78
+ if (!current || !threadKey || !current.includes(threadKey)) return null;
79
+ return current.filter((key) => key !== threadKey);
80
+ }
81
+
82
+ export function rebuildPoolBindingIndex(
83
+ bindings: Iterable<{ threadKey: string; user: string; evicted?: boolean }>,
84
+ pool: readonly string[],
85
+ ): Map<string, string[]> | null {
86
+ const byUser = new Map(pool.map((user) => [user, [] as string[]]));
87
+ for (const binding of bindings) {
88
+ if (binding?.evicted === true) continue;
89
+ if (!binding || typeof binding.threadKey !== "string" || !binding.threadKey) return null;
90
+ if (!binding.user) continue;
91
+ const keys = byUser.get(binding.user);
92
+ if (!keys || keys.includes(binding.threadKey)) return null;
93
+ keys.push(binding.threadKey);
94
+ }
95
+ return byUser;
96
+ }
@@ -0,0 +1,41 @@
1
+ /** An operator recreate closes new admission before its idle read and keeps a
2
+ * durable refusal across the container destroy. A new DO isolate may reclaim
3
+ * the mark only by re-running the operator's checked recreate. */
4
+ export class ResidentRecreateAdmission {
5
+ private claiming = false;
6
+
7
+ constructor(
8
+ private readonly store: {
9
+ mark(): Promise<void>;
10
+ clear(): Promise<void>;
11
+ held(): Promise<boolean>;
12
+ },
13
+ ) {}
14
+
15
+ get pending(): boolean {
16
+ return this.claiming;
17
+ }
18
+
19
+ async blocked(): Promise<boolean> {
20
+ return this.claiming || (await this.store.held());
21
+ }
22
+
23
+ async run<T>(action: () => Promise<T>): Promise<{ busy: true } | { busy: false; value: T }> {
24
+ if (this.claiming) return { busy: true };
25
+ this.claiming = true; // no await before a concurrent admission sees this
26
+ let marked = false;
27
+ try {
28
+ await this.store.mark();
29
+ marked = true;
30
+ return { busy: false, value: await action() };
31
+ } finally {
32
+ try {
33
+ // A failed mark may already have persisted. Leave it fail-closed for
34
+ // an explicit checked retry rather than guessing that it did not.
35
+ if (marked) await this.store.clear();
36
+ } finally {
37
+ this.claiming = false;
38
+ }
39
+ }
40
+ }
41
+ }
@@ -29,6 +29,7 @@ export const RESIDENT_STEP_LABELS = {
29
29
  git: "a git command",
30
30
  "git-setup": "configuring git",
31
31
  "stage-perms": "securing the credential stage",
32
+ "stage-write-cleanup": "removing a failed staged write",
32
33
  install: "installing dependencies",
33
34
  build: "building",
34
35
  test: "running the tests",
@@ -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-v3Py3_QR.js",
31
+ "file": "assets/dist-XRrc0hhm.js",
32
32
  "name": "dist",
33
33
  "src": "../node_modules/@pierre/diffs/dist/index.js",
34
34
  "isDynamicEntry": true,
@@ -2570,29 +2570,29 @@
2570
2570
  "_RunsTabs-C7Ua3XX5.js"
2571
2571
  ]
2572
2572
  },
2573
- "_PendingTurnRow-C6hroa00.js": {
2574
- "file": "assets/PendingTurnRow-C6hroa00.js",
2573
+ "_PendingTurnRow-BqY-EaZR.js": {
2574
+ "file": "assets/PendingTurnRow-BqY-EaZR.js",
2575
2575
  "name": "PendingTurnRow",
2576
2576
  "imports": [
2577
2577
  "_AppShell-DPJVhUGP.js",
2578
2578
  "_browser-CCImen5O.js",
2579
- "_indexRow-BsWcHHVh.js",
2579
+ "_indexRow-BGjjLnN5.js",
2580
2580
  "_format-BldUwl_R.js",
2581
- "_sseReplay-DuALaBPq.js"
2581
+ "_sseReplay-BsNQPLmJ.js"
2582
2582
  ]
2583
2583
  },
2584
- "_RunFoldRow-DrfFauYJ.js": {
2585
- "file": "assets/RunFoldRow-DrfFauYJ.js",
2584
+ "_RunFoldRow-BOWvSoIv.js": {
2585
+ "file": "assets/RunFoldRow-BOWvSoIv.js",
2586
2586
  "name": "RunFoldRow",
2587
2587
  "imports": [
2588
2588
  "_browser-CCImen5O.js",
2589
2589
  "_indexFormat-B-pd4r7Y.js",
2590
- "_indexRow-BsWcHHVh.js",
2590
+ "_indexRow-BGjjLnN5.js",
2591
2591
  "_format-BldUwl_R.js",
2592
2592
  "_SlackMark-DB0MGiF5.js",
2593
- "_sseReplay-DuALaBPq.js",
2593
+ "_sseReplay-BsNQPLmJ.js",
2594
2594
  "_durationTone-CVpX_yIk.js",
2595
- "_StatusDot-DyFjGg4V.js"
2595
+ "_StatusDot-BMkUFVE8.js"
2596
2596
  ]
2597
2597
  },
2598
2598
  "_RunsTabs-C7Ua3XX5.js": {
@@ -2611,17 +2611,17 @@
2611
2611
  "_browser-CCImen5O.js"
2612
2612
  ]
2613
2613
  },
2614
- "_SilentTurn-D9ON15a8.js": {
2615
- "file": "assets/SilentTurn-D9ON15a8.js",
2614
+ "_SilentTurn-CHfzRQW8.js": {
2615
+ "file": "assets/SilentTurn-CHfzRQW8.js",
2616
2616
  "name": "SilentTurn",
2617
2617
  "imports": [
2618
2618
  "_browser-CCImen5O.js",
2619
- "_PendingTurnRow-C6hroa00.js",
2619
+ "_PendingTurnRow-BqY-EaZR.js",
2620
2620
  "_eventSource-Cv0AFlMi.js",
2621
2621
  "_indexFormat-B-pd4r7Y.js",
2622
- "_indexRow-BsWcHHVh.js",
2622
+ "_indexRow-BGjjLnN5.js",
2623
2623
  "_format-BldUwl_R.js",
2624
- "_sseReplay-DuALaBPq.js",
2624
+ "_sseReplay-BsNQPLmJ.js",
2625
2625
  "_durationTone-CVpX_yIk.js"
2626
2626
  ]
2627
2627
  },
@@ -2632,16 +2632,16 @@
2632
2632
  "_browser-CCImen5O.js"
2633
2633
  ]
2634
2634
  },
2635
- "_StatusDot-DyFjGg4V.js": {
2636
- "file": "assets/StatusDot-DyFjGg4V.js",
2635
+ "_StatusDot-BMkUFVE8.js": {
2636
+ "file": "assets/StatusDot-BMkUFVE8.js",
2637
2637
  "name": "StatusDot",
2638
2638
  "imports": [
2639
2639
  "_browser-CCImen5O.js",
2640
- "_Tooltip-BvPkV4kZ.js"
2640
+ "_Tooltip-Dfot16IA.js"
2641
2641
  ]
2642
2642
  },
2643
- "_Tooltip-BvPkV4kZ.js": {
2644
- "file": "assets/Tooltip-BvPkV4kZ.js",
2643
+ "_Tooltip-Dfot16IA.js": {
2644
+ "file": "assets/Tooltip-Dfot16IA.js",
2645
2645
  "name": "Tooltip",
2646
2646
  "imports": [
2647
2647
  "_AppShell-DPJVhUGP.js",
@@ -2661,8 +2661,8 @@
2661
2661
  "file": "assets/browser-CCImen5O.js",
2662
2662
  "name": "browser"
2663
2663
  },
2664
- "_budgets-BQgcCyvb.js": {
2665
- "file": "assets/budgets-BQgcCyvb.js",
2664
+ "_budgets-CJHiTZmM.js": {
2665
+ "file": "assets/budgets-CJHiTZmM.js",
2666
2666
  "name": "budgets"
2667
2667
  },
2668
2668
  "_c-D4NGQ_Pd.js": {
@@ -2748,11 +2748,11 @@
2748
2748
  "file": "assets/indexFormat-B-pd4r7Y.js",
2749
2749
  "name": "indexFormat"
2750
2750
  },
2751
- "_indexRow-BsWcHHVh.js": {
2752
- "file": "assets/indexRow-BsWcHHVh.js",
2751
+ "_indexRow-BGjjLnN5.js": {
2752
+ "file": "assets/indexRow-BGjjLnN5.js",
2753
2753
  "name": "indexRow",
2754
2754
  "imports": [
2755
- "_budgets-BQgcCyvb.js",
2755
+ "_budgets-CJHiTZmM.js",
2756
2756
  "_format-BldUwl_R.js"
2757
2757
  ]
2758
2758
  },
@@ -2832,13 +2832,13 @@
2832
2832
  "_AppShell-DPJVhUGP.js"
2833
2833
  ]
2834
2834
  },
2835
- "_sseReplay-DuALaBPq.js": {
2836
- "file": "assets/sseReplay-DuALaBPq.js",
2835
+ "_sseReplay-BsNQPLmJ.js": {
2836
+ "file": "assets/sseReplay-BsNQPLmJ.js",
2837
2837
  "name": "sseReplay",
2838
2838
  "imports": [
2839
2839
  "_browser-CCImen5O.js",
2840
2840
  "_wallClock-x0hjr0J_.js",
2841
- "_indexRow-BsWcHHVh.js",
2841
+ "_indexRow-BGjjLnN5.js",
2842
2842
  "_format-BldUwl_R.js",
2843
2843
  "_durationTone-CVpX_yIk.js"
2844
2844
  ]
@@ -2888,7 +2888,7 @@
2888
2888
  ]
2889
2889
  },
2890
2890
  "src/main.ts": {
2891
- "file": "assets/main-ks3kBojV.js",
2891
+ "file": "assets/main-B32N1vUh.js",
2892
2892
  "name": "main",
2893
2893
  "src": "src/main.ts",
2894
2894
  "isEntry": true,
@@ -2938,7 +2938,7 @@
2938
2938
  ]
2939
2939
  },
2940
2940
  "src/pages/DeliveryPage.vue": {
2941
- "file": "assets/DeliveryPage-BUJckSuW.js",
2941
+ "file": "assets/DeliveryPage-QyS6_Q0m.js",
2942
2942
  "name": "DeliveryPage",
2943
2943
  "src": "src/pages/DeliveryPage.vue",
2944
2944
  "isDynamicEntry": true,
@@ -2947,28 +2947,28 @@
2947
2947
  "_browser-CCImen5O.js",
2948
2948
  "_delivery-CTdOqpVK.js",
2949
2949
  "_wallClock-x0hjr0J_.js",
2950
- "_budgets-BQgcCyvb.js"
2950
+ "_budgets-CJHiTZmM.js"
2951
2951
  ]
2952
2952
  },
2953
2953
  "src/pages/HomePage.vue": {
2954
- "file": "assets/HomePage-BfpIrKDX.js",
2954
+ "file": "assets/HomePage-Bm40r0wF.js",
2955
2955
  "name": "HomePage",
2956
2956
  "src": "src/pages/HomePage.vue",
2957
2957
  "isDynamicEntry": true,
2958
2958
  "imports": [
2959
2959
  "_AppShell-DPJVhUGP.js",
2960
2960
  "_browser-CCImen5O.js",
2961
- "_Tooltip-BvPkV4kZ.js",
2962
- "_PendingTurnRow-C6hroa00.js",
2961
+ "_Tooltip-Dfot16IA.js",
2962
+ "_PendingTurnRow-BqY-EaZR.js",
2963
2963
  "src/main.ts",
2964
2964
  "_eventSource-Cv0AFlMi.js",
2965
2965
  "_wallClock-x0hjr0J_.js",
2966
2966
  "_indexFormat-B-pd4r7Y.js",
2967
- "_indexRow-BsWcHHVh.js",
2967
+ "_indexRow-BGjjLnN5.js",
2968
2968
  "_format-BldUwl_R.js",
2969
- "_SilentTurn-D9ON15a8.js",
2969
+ "_SilentTurn-CHfzRQW8.js",
2970
2970
  "_SlackMark-DB0MGiF5.js",
2971
- "_sseReplay-DuALaBPq.js"
2971
+ "_sseReplay-BsNQPLmJ.js"
2972
2972
  ]
2973
2973
  },
2974
2974
  "src/pages/MetricsPage.vue": {
@@ -2983,23 +2983,23 @@
2983
2983
  ]
2984
2984
  },
2985
2985
  "src/pages/PlanePage.vue": {
2986
- "file": "assets/PlanePage-C2Cb1mkT.js",
2986
+ "file": "assets/PlanePage-VmzjNLAz.js",
2987
2987
  "name": "PlanePage",
2988
2988
  "src": "src/pages/PlanePage.vue",
2989
2989
  "isDynamicEntry": true,
2990
2990
  "imports": [
2991
2991
  "_AppShell-DPJVhUGP.js",
2992
2992
  "_browser-CCImen5O.js",
2993
- "_PendingTurnRow-C6hroa00.js",
2993
+ "_PendingTurnRow-BqY-EaZR.js",
2994
2994
  "_wallClock-x0hjr0J_.js",
2995
- "_indexRow-BsWcHHVh.js",
2995
+ "_indexRow-BGjjLnN5.js",
2996
2996
  "_format-BldUwl_R.js",
2997
- "_SilentTurn-D9ON15a8.js",
2998
- "_sseReplay-DuALaBPq.js"
2997
+ "_SilentTurn-CHfzRQW8.js",
2998
+ "_sseReplay-BsNQPLmJ.js"
2999
2999
  ]
3000
3000
  },
3001
3001
  "src/pages/ResidentDetailPage.vue": {
3002
- "file": "assets/ResidentDetailPage-Bd9_Q87l.js",
3002
+ "file": "assets/ResidentDetailPage-hjX3tRCh.js",
3003
3003
  "name": "ResidentDetailPage",
3004
3004
  "src": "src/pages/ResidentDetailPage.vue",
3005
3005
  "isDynamicEntry": true,
@@ -3007,46 +3007,46 @@
3007
3007
  "_AppShell-DPJVhUGP.js",
3008
3008
  "_browser-CCImen5O.js",
3009
3009
  "src/main.ts",
3010
- "_StatusDot-DyFjGg4V.js"
3010
+ "_StatusDot-BMkUFVE8.js"
3011
3011
  ]
3012
3012
  },
3013
3013
  "src/pages/ResidentsIndexPage.vue": {
3014
- "file": "assets/ResidentsIndexPage-C8eS7hxp.js",
3014
+ "file": "assets/ResidentsIndexPage-BKGnlCEh.js",
3015
3015
  "name": "ResidentsIndexPage",
3016
3016
  "src": "src/pages/ResidentsIndexPage.vue",
3017
3017
  "isDynamicEntry": true,
3018
3018
  "imports": [
3019
3019
  "_AppShell-DPJVhUGP.js",
3020
3020
  "_browser-CCImen5O.js",
3021
- "_Tooltip-BvPkV4kZ.js",
3021
+ "_Tooltip-Dfot16IA.js",
3022
3022
  "src/main.ts",
3023
3023
  "_eventSource-Cv0AFlMi.js",
3024
3024
  "_wallClock-x0hjr0J_.js",
3025
3025
  "_indexFormat-B-pd4r7Y.js",
3026
- "_indexRow-BsWcHHVh.js",
3026
+ "_indexRow-BGjjLnN5.js",
3027
3027
  "_format-BldUwl_R.js",
3028
- "_StatusDot-DyFjGg4V.js"
3028
+ "_StatusDot-BMkUFVE8.js"
3029
3029
  ]
3030
3030
  },
3031
3031
  "src/pages/RunRoutePage.vue": {
3032
- "file": "assets/RunRoutePage-B_eXfNAF.js",
3032
+ "file": "assets/RunRoutePage-D9ztf-uE.js",
3033
3033
  "name": "RunRoutePage",
3034
3034
  "src": "src/pages/RunRoutePage.vue",
3035
3035
  "isDynamicEntry": true,
3036
3036
  "imports": [
3037
3037
  "_AppShell-DPJVhUGP.js",
3038
3038
  "_browser-CCImen5O.js",
3039
- "_Tooltip-BvPkV4kZ.js",
3040
- "_PendingTurnRow-C6hroa00.js",
3039
+ "_Tooltip-Dfot16IA.js",
3040
+ "_PendingTurnRow-BqY-EaZR.js",
3041
3041
  "src/main.ts",
3042
3042
  "_eventSource-Cv0AFlMi.js",
3043
3043
  "_wallClock-x0hjr0J_.js",
3044
3044
  "_indexFormat-B-pd4r7Y.js",
3045
- "_indexRow-BsWcHHVh.js",
3045
+ "_indexRow-BGjjLnN5.js",
3046
3046
  "_format-BldUwl_R.js",
3047
- "_RunFoldRow-DrfFauYJ.js",
3047
+ "_RunFoldRow-BOWvSoIv.js",
3048
3048
  "_SlackMark-DB0MGiF5.js",
3049
- "_sseReplay-DuALaBPq.js",
3049
+ "_sseReplay-BsNQPLmJ.js",
3050
3050
  "_durationTone-CVpX_yIk.js",
3051
3051
  "_NotFoundPage-CDjMfCcj.js"
3052
3052
  ],
@@ -3058,36 +3058,36 @@
3058
3058
  ]
3059
3059
  },
3060
3060
  "src/pages/RunsIndexPage.vue": {
3061
- "file": "assets/RunsIndexPage-BpQu36Dm.js",
3061
+ "file": "assets/RunsIndexPage-DTYQbhtr.js",
3062
3062
  "name": "RunsIndexPage",
3063
3063
  "src": "src/pages/RunsIndexPage.vue",
3064
3064
  "isDynamicEntry": true,
3065
3065
  "imports": [
3066
3066
  "_AppShell-DPJVhUGP.js",
3067
3067
  "_browser-CCImen5O.js",
3068
- "_Tooltip-BvPkV4kZ.js",
3068
+ "_Tooltip-Dfot16IA.js",
3069
3069
  "_InputMenu-BRV35UWP.js",
3070
3070
  "src/main.ts",
3071
3071
  "_eventSource-Cv0AFlMi.js",
3072
3072
  "_wallClock-x0hjr0J_.js",
3073
3073
  "_indexFormat-B-pd4r7Y.js",
3074
- "_indexRow-BsWcHHVh.js",
3074
+ "_indexRow-BGjjLnN5.js",
3075
3075
  "_format-BldUwl_R.js",
3076
3076
  "_SlackMark-DB0MGiF5.js",
3077
3077
  "_durationTone-CVpX_yIk.js",
3078
3078
  "_RunsTabs-C7Ua3XX5.js",
3079
- "_StatusDot-DyFjGg4V.js"
3079
+ "_StatusDot-BMkUFVE8.js"
3080
3080
  ]
3081
3081
  },
3082
3082
  "src/pages/ScheduledPage.vue": {
3083
- "file": "assets/ScheduledPage-DGHcjcSt.js",
3083
+ "file": "assets/ScheduledPage-BAYR0tuv.js",
3084
3084
  "name": "ScheduledPage",
3085
3085
  "src": "src/pages/ScheduledPage.vue",
3086
3086
  "isDynamicEntry": true,
3087
3087
  "imports": [
3088
3088
  "_AppShell-DPJVhUGP.js",
3089
3089
  "_browser-CCImen5O.js",
3090
- "_Tooltip-BvPkV4kZ.js",
3090
+ "_Tooltip-Dfot16IA.js",
3091
3091
  "_wallClock-x0hjr0J_.js",
3092
3092
  "_indexFormat-B-pd4r7Y.js",
3093
3093
  "_format-BldUwl_R.js",
@@ -3095,7 +3095,7 @@
3095
3095
  ]
3096
3096
  },
3097
3097
  "src/pages/SettingsPage.vue": {
3098
- "file": "assets/SettingsPage-IfQ71QjN.js",
3098
+ "file": "assets/SettingsPage-BlYBmCG9.js",
3099
3099
  "name": "SettingsPage",
3100
3100
  "src": "src/pages/SettingsPage.vue",
3101
3101
  "isDynamicEntry": true,
@@ -3103,14 +3103,14 @@
3103
3103
  "_AppShell-DPJVhUGP.js",
3104
3104
  "_browser-CCImen5O.js",
3105
3105
  "_SettingSelect-CEeA4iuC.js",
3106
- "_Tooltip-BvPkV4kZ.js",
3106
+ "_Tooltip-Dfot16IA.js",
3107
3107
  "_InputMenu-BRV35UWP.js",
3108
3108
  "_settingsApi-Dm6eaQcu.js",
3109
- "_indexRow-BsWcHHVh.js"
3109
+ "_indexRow-BGjjLnN5.js"
3110
3110
  ]
3111
3111
  },
3112
3112
  "src/pages/UnitRoutePage.vue": {
3113
- "file": "assets/UnitRoutePage-BNe3nBIG.js",
3113
+ "file": "assets/UnitRoutePage-BdZoT-4d.js",
3114
3114
  "name": "UnitRoutePage",
3115
3115
  "src": "src/pages/UnitRoutePage.vue",
3116
3116
  "isDynamicEntry": true,
@@ -3120,11 +3120,11 @@
3120
3120
  "_SettingSelect-CEeA4iuC.js",
3121
3121
  "_wallClock-x0hjr0J_.js",
3122
3122
  "_indexFormat-B-pd4r7Y.js",
3123
- "_indexRow-BsWcHHVh.js",
3123
+ "_indexRow-BGjjLnN5.js",
3124
3124
  "_format-BldUwl_R.js",
3125
- "_RunFoldRow-DrfFauYJ.js",
3125
+ "_RunFoldRow-BOWvSoIv.js",
3126
3126
  "_SlackMark-DB0MGiF5.js",
3127
- "_sseReplay-DuALaBPq.js",
3127
+ "_sseReplay-BsNQPLmJ.js",
3128
3128
  "_NotFoundPage-CDjMfCcj.js"
3129
3129
  ]
3130
3130
  }