agent-dealer 1.2.2 → 1.2.4

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 (34) hide show
  1. package/bundle/server/dist/capacity/claude-events.js +27 -10
  2. package/bundle/server/dist/capacity/claude-local-cache.js +794 -0
  3. package/bundle/server/dist/capacity/claude-local-cache.test.js +524 -0
  4. package/bundle/server/dist/capacity/cursor-individual-credentials.js +203 -16
  5. package/bundle/server/dist/capacity/cursor-individual-credentials.test.js +186 -2
  6. package/bundle/server/dist/capacity/cursor-individual.js +69 -2
  7. package/bundle/server/dist/capacity/cursor-individual.test.js +268 -2
  8. package/bundle/server/dist/capacity/muse-host.js +778 -0
  9. package/bundle/server/dist/capacity/muse-host.test.js +480 -0
  10. package/bundle/server/dist/capacity/muse-lifecycle.test.js +96 -0
  11. package/bundle/server/dist/capacity/muse-probe.js +250 -0
  12. package/bundle/server/dist/capacity/muse-probe.test.js +183 -0
  13. package/bundle/server/dist/capacity/muse.js +320 -63
  14. package/bundle/server/dist/capacity/muse.test.js +8 -51
  15. package/bundle/server/dist/coordinator/muse-spawn.js +143 -1
  16. package/bundle/server/dist/index.js +15 -2
  17. package/bundle/server/dist/routes/index.js +25 -9
  18. package/bundle/server/dist/routes/runtime-capacity.test.js +20 -7
  19. package/bundle/server/dist/runners/muse-code-jsonl.js +7 -1
  20. package/bundle/server/dist/runners/muse-serve-session.js +314 -0
  21. package/bundle/server/dist/runners/muse-serve-session.test.js +325 -0
  22. package/bundle/server/package.json +2 -2
  23. package/bundle/server/static-ui/assets/codex-YZSpL-SB.png +0 -0
  24. package/bundle/server/static-ui/assets/{index-DUYzboWh.css → index-CYqRh_-S.css} +1 -1
  25. package/bundle/server/static-ui/assets/index-UO4lHZw4.js +60 -0
  26. package/bundle/server/static-ui/assets/muse-CtdcC6ve.svg +33 -0
  27. package/bundle/server/static-ui/index.html +2 -2
  28. package/bundle/shared/package.json +1 -1
  29. package/dist/doctor.d.ts +72 -0
  30. package/dist/doctor.js +235 -1
  31. package/dist/doctor.test.d.ts +1 -0
  32. package/dist/doctor.test.js +276 -0
  33. package/package.json +1 -1
  34. package/bundle/server/static-ui/assets/index-0GDYfbaM.js +0 -60
@@ -0,0 +1,33 @@
1
+ <svg viewBox="0 0 15.184 10.109" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
2
+ <!-- Exact Meta infinity-mark paths extracted from Meta's first-party wordmark SVG. -->
3
+ <path d="M4.40165 0.0546875C4.39678 0.0546875 4.39194 0.0547047 4.38708 0.0547298L4.36719 1.69809C4.37174 1.69804 4.37629 1.698 4.38085 1.698H4.38086C5.46253 1.698 6.30154 2.55086 8.12513 5.62254L8.2363 5.80954L8.24357 5.82178L9.2644 4.29012L9.25733 4.27829C9.01709 3.88756 8.78629 3.52806 8.56494 3.1998C8.30824 2.81945 8.06274 2.47967 7.8245 2.17631C6.61813 0.640297 5.60899 0.0546875 4.40165 0.0546875Z" fill="url(#meta-0)"/>
4
+ <path d="M4.38698 0.0547298C3.17378 0.0609714 2.10123 0.845474 1.32675 2.04593C1.32448 2.04944 1.32221 2.05296 1.31995 2.05649L2.74159 2.83027C2.7439 2.82677 2.74621 2.82327 2.74853 2.81978C3.20078 2.13886 3.76359 1.70456 4.36709 1.69809C4.37164 1.69804 4.37619 1.698 4.38075 1.698L4.40155 0.0546875C4.39668 0.0546875 4.39184 0.0547047 4.38698 0.0547298Z" fill="url(#meta-1)"/>
5
+ <path d="M1.32666 2.04688C1.3244 2.05039 1.32213 2.05391 1.31986 2.05744C0.810944 2.84965 0.431562 3.8218 0.226532 4.87056C0.225644 4.8751 0.224759 4.87964 0.223877 4.88419L1.82103 5.26104C1.82187 5.25649 1.82271 5.25193 1.82355 5.24738C1.99427 4.32569 2.31929 3.47086 2.7415 2.83122C2.7438 2.82772 2.74612 2.82422 2.74844 2.82073L1.32666 2.04688Z" fill="url(#meta-2)"/>
6
+ <path d="M1.82364 5.24791L0.226615 4.87109C0.225727 4.87563 0.224842 4.88018 0.22396 4.88472C0.11216 5.4615 0.0552821 6.04757 0.0540887 6.63508C0.0540793 6.63983 0.0540757 6.64458 0.0540777 6.64932L1.69203 6.79586C1.6919 6.79109 1.69176 6.78638 1.69165 6.7816C1.69084 6.74752 1.69043 6.71299 1.69042 6.67799C1.69124 6.20287 1.73498 5.72881 1.82112 5.26157C1.82195 5.25702 1.82279 5.25247 1.82364 5.24791Z" fill="url(#meta-3)"/>
7
+ <path d="M1.74184 7.30462C1.71246 7.13577 1.69581 6.96494 1.69203 6.79359C1.6919 6.78883 1.69176 6.78412 1.69165 6.77933L0.0540887 6.63281C0.0540793 6.63756 0.0540757 6.64231 0.0540777 6.64706V6.64798C0.0540777 7.01363 0.0845699 7.35598 0.143831 7.67225C0.144688 7.67682 0.145531 7.68131 0.1464 7.68588L1.74432 7.3183C1.74348 7.31376 1.74266 7.30919 1.74184 7.30462Z" fill="url(#meta-4)"/>
8
+ <path d="M2.11498 8.1544C1.93675 7.95985 1.81061 7.67922 1.74429 7.32032C1.74345 7.31578 1.74264 7.31121 1.74182 7.30664L0.143799 7.67426C0.144655 7.67884 0.145498 7.68333 0.146368 7.6879C0.267152 8.32205 0.504031 8.85057 0.843261 9.2504C0.846277 9.25395 0.849304 9.2575 0.852342 9.26105L2.12451 8.16464C2.12133 8.16125 2.11813 8.15783 2.11498 8.1544Z" fill="url(#meta-5)"/>
9
+ <path d="M6.84924 3.60742C5.88628 5.08446 5.30292 6.01088 5.30292 6.01088C4.02016 8.02169 3.57639 8.47237 2.86221 8.47237C2.5642 8.47237 2.31524 8.36626 2.12451 8.16343C2.12133 8.16005 2.11814 8.15662 2.11499 8.15319L0.843262 9.24919C0.846278 9.25274 0.849305 9.25629 0.852343 9.25984C1.32074 9.80583 1.98189 10.1088 2.79981 10.1088C4.03726 10.1088 4.92725 9.52537 6.5094 6.75973C6.5094 6.75973 7.16894 5.59504 7.62266 4.79276C7.34161 4.33898 7.08584 3.94614 6.84924 3.60742Z" fill="#0082FB"/>
10
+ <path d="M8.56569 1.27734C8.56244 1.28079 8.55913 1.28431 8.55589 1.28777C8.30301 1.55719 8.06032 1.85807 7.82422 2.1773C8.06247 2.48067 8.30842 2.82111 8.56513 3.20145C8.86769 2.73447 9.15011 2.35626 9.42691 2.06637C9.4302 2.06294 9.43341 2.0596 9.43669 2.05618L8.56569 1.27734Z" fill="url(#meta-6)"/>
11
+ <path d="M13.2408 1.13125C12.5692 0.452783 11.7685 0.0546875 10.9125 0.0546875C10.0098 0.0546875 9.25054 0.549336 8.56571 1.27604C8.56246 1.27948 8.55915 1.283 8.55591 1.28646L9.42694 2.06506C9.43022 2.06163 9.43343 2.05829 9.43672 2.05488C9.88786 1.58561 10.3245 1.35131 10.8085 1.35131H10.8084C11.3294 1.35131 11.8171 1.59654 12.2394 2.02645C12.2427 2.02982 12.246 2.03316 12.2493 2.03655L13.2508 1.14135C13.2475 1.13796 13.2441 1.13462 13.2408 1.13125Z" fill="#0082FB"/>
12
+ <path d="M15.1822 6.41693C15.1446 4.23851 14.3823 2.29103 13.2508 1.14096C13.2474 1.13757 13.2441 1.13423 13.2407 1.13086L12.2394 2.02606C12.2427 2.02943 12.246 2.03277 12.2493 2.03616C13.1005 2.91055 13.6844 4.53694 13.7375 6.41646C13.7376 6.42119 13.7377 6.42592 13.7379 6.43065L15.1824 6.43111C15.1824 6.42639 15.1823 6.42166 15.1822 6.41693Z" fill="url(#meta-7)"/>
13
+ <path d="M15.1824 6.43263C15.1824 6.4279 15.1823 6.42317 15.1822 6.41843L13.7375 6.41797C13.7376 6.4227 13.7377 6.42743 13.7379 6.43216C13.7402 6.52038 13.7414 6.60913 13.7414 6.69841C13.7414 7.21086 13.6649 7.6251 13.5092 7.92417C13.5069 7.92861 13.5045 7.93306 13.5022 7.93744L14.5793 9.05771C14.582 9.05364 14.5846 9.0496 14.5873 9.04551C14.9783 8.44213 15.1836 7.60392 15.1836 6.58747C15.1836 6.53573 15.1832 6.48411 15.1824 6.43263Z" fill="url(#meta-8)"/>
14
+ <path d="M13.5093 7.92188C13.507 7.92631 13.5046 7.93076 13.5023 7.93515C13.3676 8.18724 13.1753 8.35538 12.924 8.4289L13.415 9.97644C13.48 9.95436 13.5433 9.92957 13.6049 9.90207C13.6233 9.89388 13.6416 9.88545 13.6597 9.87678C13.6701 9.87178 13.6805 9.86671 13.6908 9.86157C14.0155 9.69974 14.2899 9.46106 14.5118 9.15354C14.5254 9.13476 14.5388 9.11582 14.5519 9.09654C14.5612 9.08295 14.5704 9.06925 14.5794 9.05541C14.5821 9.05135 14.5847 9.0473 14.5874 9.04322L13.5093 7.92188Z" fill="url(#meta-9)"/>
15
+ <path d="M12.6111 8.47085C12.4465 8.47085 12.3012 8.44634 12.159 8.38281L11.6562 9.96718C11.9388 10.0637 12.2403 10.1072 12.5765 10.1072C12.8862 10.1072 13.1705 10.0609 13.4283 9.97153L12.9374 8.42452C12.8316 8.45623 12.7216 8.47184 12.6111 8.47085Z" fill="url(#meta-10)"/>
16
+ <path d="M11.6051 7.93071C11.602 7.92714 11.5989 7.92355 11.5957 7.91992L10.4388 9.12313C10.4421 9.12659 10.4453 9.13004 10.4485 9.13348C10.8505 9.56188 11.2344 9.82762 11.6697 9.97347L12.1721 8.39027C11.9887 8.31148 11.8112 8.16864 11.6051 7.93071Z" fill="url(#meta-11)"/>
17
+ <path d="M11.5958 7.91873C11.2488 7.51518 10.8195 6.84328 10.1443 5.75677L9.26431 4.28917L9.25724 4.27734L8.23621 5.80859L8.24348 5.82083L8.86699 6.86972C9.47138 7.88111 9.96376 8.61279 10.4389 9.12193C10.4422 9.1254 10.4454 9.12885 10.4486 9.13229L11.6051 7.92952C11.602 7.92596 11.5989 7.92236 11.5958 7.91873Z" fill="url(#meta-12)"/>
18
+ <defs>
19
+ <linearGradient id="meta-0" x1="8.58586" y1="5.1989" x2="5.19669" y2="0.757905" gradientUnits="userSpaceOnUse"><stop offset="0.0006" stop-color="#0867DF"/><stop offset="0.4539" stop-color="#0668E1"/><stop offset="0.8591" stop-color="#0064E0"/></linearGradient>
20
+ <linearGradient id="meta-1" x1="1.98772" y1="2.32206" x2="4.31121" y2="0.558086" gradientUnits="userSpaceOnUse"><stop offset="0.1323" stop-color="#0064DF"/><stop offset="0.9988" stop-color="#0064E0"/></linearGradient>
21
+ <linearGradient id="meta-2" x1="1.0028" y1="4.91157" x2="1.93484" y2="2.56535" gradientUnits="userSpaceOnUse"><stop offset="0.0147" stop-color="#0072EC"/><stop offset="0.6881" stop-color="#0064DF"/></linearGradient>
22
+ <linearGradient id="meta-3" x1="0.876315" y1="6.60704" x2="0.984158" y2="5.17414" gradientUnits="userSpaceOnUse"><stop offset="0.0731" stop-color="#007CF6"/><stop offset="0.9943" stop-color="#0072EC"/></linearGradient>
23
+ <linearGradient id="meta-4" x1="0.935623" y1="7.38311" x2="0.858481" y2="6.80902" gradientUnits="userSpaceOnUse"><stop offset="0.0731" stop-color="#007FF9"/><stop offset="1" stop-color="#007CF6"/></linearGradient>
24
+ <linearGradient id="meta-5" x1="0.89031" y1="7.53593" x2="1.37106" y2="8.55559" gradientUnits="userSpaceOnUse"><stop offset="0.0731" stop-color="#007FF9"/><stop offset="1" stop-color="#0082FB"/></linearGradient>
25
+ <linearGradient id="meta-6" x1="8.27941" y1="2.60226" x2="8.91497" y2="1.72324" gradientUnits="userSpaceOnUse"><stop offset="0.2799" stop-color="#007FF8"/><stop offset="0.9141" stop-color="#0082FB"/></linearGradient>
26
+ <linearGradient id="meta-7" x1="13.1116" y1="1.46132" x2="14.4412" y2="6.35237" gradientUnits="userSpaceOnUse"><stop stop-color="#0082FB"/><stop offset="0.9995" stop-color="#0081FA"/></linearGradient>
27
+ <linearGradient id="meta-8" x1="14.7623" y1="6.541" x2="13.9208" y2="8.24143" gradientUnits="userSpaceOnUse"><stop offset="0.0619" stop-color="#0081FA"/><stop offset="1" stop-color="#0080F9"/></linearGradient>
28
+ <linearGradient id="meta-9" x1="13.2521" y1="9.14064" x2="14.0387" y2="8.6049" gradientUnits="userSpaceOnUse"><stop stop-color="#027AF3"/><stop offset="1" stop-color="#0080F9"/></linearGradient>
29
+ <linearGradient id="meta-10" x1="12.0183" y1="9.24502" x2="13.1113" y2="9.24502" gradientUnits="userSpaceOnUse"><stop stop-color="#0377EF"/><stop offset="0.9994" stop-color="#0279F1"/></linearGradient>
30
+ <linearGradient id="meta-11" x1="11.068" y1="8.64479" x2="11.8538" y2="9.1071" gradientUnits="userSpaceOnUse"><stop offset="0.0019" stop-color="#0471E9"/><stop offset="1" stop-color="#0377EF"/></linearGradient>
31
+ <linearGradient id="meta-12" x1="8.67106" y1="5.23467" x2="11.1883" y2="8.3996" gradientUnits="userSpaceOnUse"><stop offset="0.2765" stop-color="#0867DF"/><stop offset="1" stop-color="#0471E9"/></linearGradient>
32
+ </defs>
33
+ </svg>
@@ -8,8 +8,8 @@
8
8
  <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
9
9
  <meta name="theme-color" content="#0a0e14" />
10
10
  <link rel="preload" href="/fonts/Monaco.ttf" as="font" type="font/ttf" crossorigin />
11
- <script type="module" crossorigin src="/assets/index-0GDYfbaM.js"></script>
12
- <link rel="stylesheet" crossorigin href="/assets/index-DUYzboWh.css">
11
+ <script type="module" crossorigin src="/assets/index-UO4lHZw4.js"></script>
12
+ <link rel="stylesheet" crossorigin href="/assets/index-CYqRh_-S.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="root"></div>
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-dealer/shared",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
package/dist/doctor.d.ts CHANGED
@@ -1 +1,73 @@
1
1
  export declare function runDoctor(): Promise<number>;
2
+ /** Which local login the Individual adapter would use, if any. */
3
+ export type CursorIndividualLoginKind = "desktop" | "agent" | "none";
4
+ export interface CursorIndividualLoginReport {
5
+ kind: CursorIndividualLoginKind;
6
+ /** Static operator-facing line — no paths, tokens, or user ids, ever. */
7
+ line: string;
8
+ }
9
+ export declare const CURSOR_INDIVIDUAL_LOGIN_LINES: Record<CursorIndividualLoginKind, string>;
10
+ /**
11
+ * Map a diagnostics-safe credential status to the doctor line. Pure (no
12
+ * I/O): unit tests pin all three states plus the degraded inputs here.
13
+ */
14
+ export declare function describeCursorIndividualLogin(status: {
15
+ present: boolean;
16
+ source: "desktop" | "agent" | null;
17
+ } | null | undefined): CursorIndividualLoginReport;
18
+ /**
19
+ * Probe the local Cursor logins the way the adapter would (desktop first,
20
+ * Agent auth as the fallback) and report the kind. Never throws, never
21
+ * prints secret material — the line is one of three static strings.
22
+ */
23
+ export declare function checkCursorIndividualLogin(): Promise<CursorIndividualLoginReport>;
24
+ /** Env override for the Claude cache file (tests/smoke). */
25
+ export declare const CLAUDE_CAPACITY_CACHE_FILE_ENV = "AGENT_DEALER_CLAUDE_CACHE_FILE";
26
+ /** Paid-fallback setting: enabled by default; `off` disables it. */
27
+ export declare const CLAUDE_CAPACITY_REFRESH_ENV = "AGENT_DEALER_CLAUDE_CAPACITY_REFRESH";
28
+ export declare const CLAUDE_CAPACITY_REFRESH_PAID_VALUE = "paid-after-1h";
29
+ export declare const CLAUDE_CAPACITY_REFRESH_OFF_VALUE = "off";
30
+ /** Which local 5H/1W cache state the capacity ladder would read. */
31
+ export type ClaudeCapacitySourceKind = "fresh" | "stale" | "missing";
32
+ export interface ClaudeCapacitySourceReport {
33
+ kind: ClaudeCapacitySourceKind;
34
+ /** Static operator-facing line — age labels only, never values or ids. */
35
+ line: string;
36
+ }
37
+ /**
38
+ * Map a cache-file presence/age probe to the doctor line. Pure (no I/O):
39
+ * unit tests pin all three states here.
40
+ */
41
+ export declare function describeClaudeCapacitySource(status: {
42
+ present: boolean;
43
+ ageMs: number | null;
44
+ } | null | undefined): ClaudeCapacitySourceReport;
45
+ /**
46
+ * Map the paid-fallback setting to its warning line, or null when explicitly
47
+ * disabled (or unrecognized). Unset/empty is the paid-after-1h default.
48
+ * Pure (no I/O): unit tests pin both states here.
49
+ */
50
+ export declare function describeClaudeProbeOptIn(setting: string | undefined): string | null;
51
+ /** Paid-fallback setting: enabled by default; `off` disables it. */
52
+ export declare const MUSE_CAPACITY_REFRESH_ENV = "AGENT_DEALER_MUSE_CAPACITY_REFRESH";
53
+ export declare const MUSE_CAPACITY_REFRESH_PAID_VALUE = "paid-after-1h";
54
+ export declare const MUSE_CAPACITY_REFRESH_OFF_VALUE = "off";
55
+ /** Legacy free-read throttle; `off` also preserves no-spend behavior when the
56
+ * new setting is absent. */
57
+ export declare const MUSE_CAPACITY_REFRESH_MS_ENV = "AGENT_DEALER_MUSE_CAPACITY_REFRESH_MS";
58
+ /**
59
+ * Match the server's Muse paid-fallback precedence exactly. The explicit new
60
+ * paid value overrides a legacy free-refresh `off`; otherwise that legacy
61
+ * switch remains no-spend for backward compatibility. Unknown new values
62
+ * fail closed. Pure (no I/O) so doctor never risks a provider request.
63
+ */
64
+ export declare function describeMuseProbeOptIn(setting: string | undefined, legacyRefreshMs: string | undefined): string | null;
65
+ /**
66
+ * Read the `cachedUsageUtilization.fetchedAtMs` timestamp out of Claude
67
+ * Code's config file (override first, then `~/.claude.json`) — the same key
68
+ * the server adapter normalizes, so doctor and the server always agree on
69
+ * freshness. File mtime is meaningless here: unrelated config writes touch
70
+ * it. Only the timestamp is read (never utilization values, ids, or
71
+ * siblings) and only an age label is reported. Never throws.
72
+ */
73
+ export declare function checkClaudeCapacitySource(nowMs?: number): Promise<ClaudeCapacitySourceReport>;
package/dist/doctor.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import net from "node:net";
2
2
  import fs from "node:fs";
3
+ import os from "node:os";
3
4
  import path from "node:path";
5
+ import { fileURLToPath, pathToFileURL } from "node:url";
4
6
  import { cursorAuthIssueFromOutput } from "@agent-dealer/shared";
5
7
  import { claudeAvailable, cursorAvailable } from "./cli-check.js";
6
8
  import { loadProdEnvFile, prodEnvFilePath, prodHomeDir, resolveBundledListenPort, shortenHome, } from "./env.js";
@@ -105,11 +107,41 @@ export async function runDoctor() {
105
107
  else {
106
108
  console.warn("⚠ LINEAR_API_KEY not set — Linear inbox disabled");
107
109
  }
110
+ // NOT-268: Claude account capacity source status (informational only —
111
+ // never fails doctor). Reports whether Claude Code's own local 5H/1W
112
+ // cache exists and how old it is, plus whether the paid fallback probe
113
+ // is armed. Age labels only: never utilization values, prompts, tokens,
114
+ // or account ids.
115
+ try {
116
+ console.log((await checkClaudeCapacitySource()).line);
117
+ const probe = describeClaudeProbeOptIn(process.env.AGENT_DEALER_CLAUDE_CAPACITY_REFRESH);
118
+ if (probe)
119
+ console.warn(probe);
120
+ const museProbe = describeMuseProbeOptIn(process.env.AGENT_DEALER_MUSE_CAPACITY_REFRESH, process.env.AGENT_DEALER_MUSE_CAPACITY_REFRESH_MS);
121
+ if (museProbe)
122
+ console.warn(museProbe);
123
+ }
124
+ catch {
125
+ // Informational only: a broken probe must never fail doctor.
126
+ }
108
127
  // NOT-250: experimental Cursor Individual dashboard adapter status
109
- // (informational only — never fails doctor, never reads credentials).
128
+ // (informational only — never fails doctor). NOT-267: when enabled,
129
+ // also report which local login the adapter would use — desktop first,
130
+ // Agent auth as the fallback. The probe only runs under the explicit
131
+ // opt-in, and prints kind labels only: never paths, tokens, or user ids.
110
132
  if (process.env.AGENT_DEALER_CURSOR_INDIVIDUAL_CAPACITY === "experimental") {
111
133
  console.warn("⚠ Cursor Individual capacity: EXPERIMENTAL dashboard adapter enabled " +
112
134
  "(undocumented API, no support guarantee — unset AGENT_DEALER_CURSOR_INDIVIDUAL_CAPACITY to disable)");
135
+ try {
136
+ const login = await checkCursorIndividualLogin();
137
+ if (login.kind === "none")
138
+ console.warn(login.line);
139
+ else
140
+ console.log(login.line);
141
+ }
142
+ catch {
143
+ // Informational only: a broken probe must never fail doctor.
144
+ }
113
145
  }
114
146
  }
115
147
  else {
@@ -151,6 +183,208 @@ export async function runDoctor() {
151
183
  }
152
184
  return failed ? 1 : 0;
153
185
  }
186
+ export const CURSOR_INDIVIDUAL_LOGIN_LINES = {
187
+ desktop: "✓ Cursor Individual capacity: desktop login found (experimental dashboard adapter can use it)",
188
+ agent: "✓ Cursor Individual capacity: Agent login found (fallback)",
189
+ none: "⚠ Cursor Individual capacity: no usable local login — log in to Cursor desktop or the Cursor Agent CLI",
190
+ };
191
+ /**
192
+ * Map a diagnostics-safe credential status to the doctor line. Pure (no
193
+ * I/O): unit tests pin all three states plus the degraded inputs here.
194
+ */
195
+ export function describeCursorIndividualLogin(status) {
196
+ if (status?.present === true && status.source === "desktop") {
197
+ return { kind: "desktop", line: CURSOR_INDIVIDUAL_LOGIN_LINES.desktop };
198
+ }
199
+ if (status?.present === true && status.source === "agent") {
200
+ return { kind: "agent", line: CURSOR_INDIVIDUAL_LOGIN_LINES.agent };
201
+ }
202
+ return { kind: "none", line: CURSOR_INDIVIDUAL_LOGIN_LINES.none };
203
+ }
204
+ /**
205
+ * Load the server's credential-status reader without depending on the
206
+ * server's daemon entrypoint (which has side effects on import). The
207
+ * checkout layout (TypeScript source, run under tsx) is tried first, then
208
+ * the installed layout (built dist) — whichever answers wins. Returns null
209
+ * when neither is loadable; the caller then reports "no usable local
210
+ * login", never a crash. The loaded reader itself returns presence/source
211
+ * only — secret material never crosses this boundary by construction.
212
+ */
213
+ async function loadCredentialStatusReader() {
214
+ const roots = [];
215
+ try {
216
+ const { resolveServerRoot } = await import("./paths.js");
217
+ roots.push(resolveServerRoot());
218
+ }
219
+ catch {
220
+ // The installed package entry (dist) may not be built in a checkout —
221
+ // the workspace fallback below still locates the source.
222
+ }
223
+ // Workspace-checkout fallback: packages/cli/src → packages/server.
224
+ // Absent in installed layouts (no src shipped), where existsSync skips it.
225
+ const here = path.dirname(fileURLToPath(import.meta.url));
226
+ roots.push(path.resolve(here, "..", "..", "server"));
227
+ const candidates = roots.flatMap((serverRoot) => [
228
+ path.join(serverRoot, "src", "capacity", "cursor-individual-credentials.ts"),
229
+ path.join(serverRoot, "dist", "capacity", "cursor-individual-credentials.js"),
230
+ ]);
231
+ for (const file of candidates) {
232
+ try {
233
+ if (!fs.existsSync(file))
234
+ continue;
235
+ const mod = (await import(pathToFileURL(file).href));
236
+ if (typeof mod.cursorIndividualCredentialStatus === "function") {
237
+ return mod;
238
+ }
239
+ }
240
+ catch {
241
+ continue;
242
+ }
243
+ }
244
+ return null;
245
+ }
246
+ /**
247
+ * Probe the local Cursor logins the way the adapter would (desktop first,
248
+ * Agent auth as the fallback) and report the kind. Never throws, never
249
+ * prints secret material — the line is one of three static strings.
250
+ */
251
+ export async function checkCursorIndividualLogin() {
252
+ try {
253
+ const reader = await loadCredentialStatusReader();
254
+ if (!reader)
255
+ return describeCursorIndividualLogin(null);
256
+ return describeCursorIndividualLogin(reader.cursorIndividualCredentialStatus());
257
+ }
258
+ catch {
259
+ return describeCursorIndividualLogin(null);
260
+ }
261
+ }
262
+ // ---------------------------------------------------------------------------
263
+ // NOT-268: Claude capacity source reporting for doctor.
264
+ // ---------------------------------------------------------------------------
265
+ /** Env override for the Claude cache file (tests/smoke). */
266
+ export const CLAUDE_CAPACITY_CACHE_FILE_ENV = "AGENT_DEALER_CLAUDE_CACHE_FILE";
267
+ /** Paid-fallback setting: enabled by default; `off` disables it. */
268
+ export const CLAUDE_CAPACITY_REFRESH_ENV = "AGENT_DEALER_CLAUDE_CAPACITY_REFRESH";
269
+ export const CLAUDE_CAPACITY_REFRESH_PAID_VALUE = "paid-after-1h";
270
+ export const CLAUDE_CAPACITY_REFRESH_OFF_VALUE = "off";
271
+ /**
272
+ * Map a cache-file presence/age probe to the doctor line. Pure (no I/O):
273
+ * unit tests pin all three states here.
274
+ */
275
+ export function describeClaudeCapacitySource(status) {
276
+ if (status?.present === true && typeof status.ageMs === "number" && status.ageMs >= 0) {
277
+ if (status.ageMs < 60 * 60 * 1000) {
278
+ return {
279
+ kind: "fresh",
280
+ line: `✓ Claude capacity: local 5H/1W cache fresh (${formatCacheAge(status.ageMs)} old)`,
281
+ };
282
+ }
283
+ return {
284
+ kind: "stale",
285
+ line: `⚠ Claude capacity: local 5H/1W cache stale (${formatCacheAge(status.ageMs)} old — refreshes on the next Claude run)`,
286
+ };
287
+ }
288
+ return {
289
+ kind: "missing",
290
+ line: "⚠ Claude capacity: no local 5H/1W cache yet — run Claude Code once to populate it",
291
+ };
292
+ }
293
+ function formatCacheAge(ageMs) {
294
+ const minutes = Math.floor(ageMs / 60_000);
295
+ if (minutes < 1)
296
+ return "under a minute";
297
+ if (minutes < 60)
298
+ return `${minutes}m`;
299
+ const hours = Math.floor(minutes / 60);
300
+ if (hours < 48)
301
+ return `${hours}h`;
302
+ return `${Math.floor(hours / 24)}d`;
303
+ }
304
+ /**
305
+ * Map the paid-fallback setting to its warning line, or null when explicitly
306
+ * disabled (or unrecognized). Unset/empty is the paid-after-1h default.
307
+ * Pure (no I/O): unit tests pin both states here.
308
+ */
309
+ export function describeClaudeProbeOptIn(setting) {
310
+ if (setting === undefined || setting === "" || setting === CLAUDE_CAPACITY_REFRESH_PAID_VALUE) {
311
+ return ("⚠ Claude capacity paid fallback: enabled (one ≤$0.01 Haiku probe after 1h stale — " +
312
+ `set ${CLAUDE_CAPACITY_REFRESH_ENV}=${CLAUDE_CAPACITY_REFRESH_OFF_VALUE} to disable)`);
313
+ }
314
+ return null;
315
+ }
316
+ // ---------------------------------------------------------------------------
317
+ // Muse capacity paid-fallback reporting for doctor.
318
+ // ---------------------------------------------------------------------------
319
+ /** Paid-fallback setting: enabled by default; `off` disables it. */
320
+ export const MUSE_CAPACITY_REFRESH_ENV = "AGENT_DEALER_MUSE_CAPACITY_REFRESH";
321
+ export const MUSE_CAPACITY_REFRESH_PAID_VALUE = "paid-after-1h";
322
+ export const MUSE_CAPACITY_REFRESH_OFF_VALUE = "off";
323
+ /** Legacy free-read throttle; `off` also preserves no-spend behavior when the
324
+ * new setting is absent. */
325
+ export const MUSE_CAPACITY_REFRESH_MS_ENV = "AGENT_DEALER_MUSE_CAPACITY_REFRESH_MS";
326
+ /**
327
+ * Match the server's Muse paid-fallback precedence exactly. The explicit new
328
+ * paid value overrides a legacy free-refresh `off`; otherwise that legacy
329
+ * switch remains no-spend for backward compatibility. Unknown new values
330
+ * fail closed. Pure (no I/O) so doctor never risks a provider request.
331
+ */
332
+ export function describeMuseProbeOptIn(setting, legacyRefreshMs) {
333
+ const enabled = setting === MUSE_CAPACITY_REFRESH_PAID_VALUE ||
334
+ ((setting === undefined || setting === "") && legacyRefreshMs !== "off");
335
+ if (!enabled)
336
+ return null;
337
+ return ("⚠ Muse capacity paid fallback: enabled when Muse is configured " +
338
+ "(one restricted contributor-model turn after 1h stale — " +
339
+ `set ${MUSE_CAPACITY_REFRESH_ENV}=${MUSE_CAPACITY_REFRESH_OFF_VALUE} to disable)`);
340
+ }
341
+ /**
342
+ * Read the `cachedUsageUtilization.fetchedAtMs` timestamp out of Claude
343
+ * Code's config file (override first, then `~/.claude.json`) — the same key
344
+ * the server adapter normalizes, so doctor and the server always agree on
345
+ * freshness. File mtime is meaningless here: unrelated config writes touch
346
+ * it. Only the timestamp is read (never utilization values, ids, or
347
+ * siblings) and only an age label is reported. Never throws.
348
+ */
349
+ export async function checkClaudeCapacitySource(nowMs = Date.now()) {
350
+ try {
351
+ const override = process.env[CLAUDE_CAPACITY_CACHE_FILE_ENV]?.trim();
352
+ const file = override || path.join(process.env.HOME ?? os.homedir(), ".claude.json");
353
+ let text;
354
+ try {
355
+ text = fs.readFileSync(file, "utf8");
356
+ }
357
+ catch {
358
+ return describeClaudeCapacitySource(null);
359
+ }
360
+ let parsed;
361
+ try {
362
+ parsed = JSON.parse(text);
363
+ }
364
+ catch {
365
+ return describeClaudeCapacitySource(null);
366
+ }
367
+ const root = parsed && typeof parsed === "object" && !Array.isArray(parsed)
368
+ ? parsed
369
+ : null;
370
+ // A bare cache object (no config envelope — the shape override fixtures
371
+ // use) is accepted as-is; otherwise only the subtree counts.
372
+ const subtreeRaw = root?.cachedUsageUtilization;
373
+ const subtree = subtreeRaw && typeof subtreeRaw === "object" && !Array.isArray(subtreeRaw)
374
+ ? subtreeRaw
375
+ : root;
376
+ const fetchedAtMs = subtree && typeof subtree.fetchedAtMs === "number" && Number.isFinite(subtree.fetchedAtMs)
377
+ ? subtree.fetchedAtMs
378
+ : null;
379
+ if (fetchedAtMs === null || fetchedAtMs <= 0 || fetchedAtMs > nowMs) {
380
+ return describeClaudeCapacitySource(null);
381
+ }
382
+ return describeClaudeCapacitySource({ present: true, ageMs: nowMs - fetchedAtMs });
383
+ }
384
+ catch {
385
+ return describeClaudeCapacitySource(null);
386
+ }
387
+ }
154
388
  function isPortFree(port) {
155
389
  return new Promise((resolve) => {
156
390
  const server = net.createServer();
@@ -0,0 +1 @@
1
+ export {};