@otto-code/brain 0.7.5

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 (150) hide show
  1. package/bin/otto-brain +2 -0
  2. package/dist/bench/context-corpus.d.ts +58 -0
  3. package/dist/bench/context-corpus.js +210 -0
  4. package/dist/bench/corpus.d.ts +31 -0
  5. package/dist/bench/corpus.js +379 -0
  6. package/dist/bench/curated-repos.d.ts +35 -0
  7. package/dist/bench/curated-repos.js +41 -0
  8. package/dist/bench/health.d.ts +50 -0
  9. package/dist/bench/health.js +112 -0
  10. package/dist/bench/index.d.ts +103 -0
  11. package/dist/bench/index.js +209 -0
  12. package/dist/bench/mine.d.ts +32 -0
  13. package/dist/bench/mine.js +101 -0
  14. package/dist/bench/repo-task.d.ts +93 -0
  15. package/dist/bench/repo-task.js +380 -0
  16. package/dist/bench/repo.d.ts +125 -0
  17. package/dist/bench/repo.js +195 -0
  18. package/dist/bench/rescore.d.ts +41 -0
  19. package/dist/bench/rescore.js +127 -0
  20. package/dist/bench/tasks.d.ts +124 -0
  21. package/dist/bench/tasks.js +1249 -0
  22. package/dist/bench/verify.d.ts +142 -0
  23. package/dist/bench/verify.js +350 -0
  24. package/dist/cli.d.ts +13 -0
  25. package/dist/cli.js +72 -0
  26. package/dist/commands/bench.d.ts +20 -0
  27. package/dist/commands/bench.js +222 -0
  28. package/dist/commands/calibrate.d.ts +19 -0
  29. package/dist/commands/calibrate.js +75 -0
  30. package/dist/commands/catalog.d.ts +32 -0
  31. package/dist/commands/catalog.js +48 -0
  32. package/dist/commands/config.d.ts +13 -0
  33. package/dist/commands/config.js +90 -0
  34. package/dist/commands/lifecycle.d.ts +29 -0
  35. package/dist/commands/lifecycle.js +194 -0
  36. package/dist/commands/pull.d.ts +22 -0
  37. package/dist/commands/pull.js +135 -0
  38. package/dist/commands/report.d.ts +10 -0
  39. package/dist/commands/report.js +34 -0
  40. package/dist/commands/rescore.d.ts +11 -0
  41. package/dist/commands/rescore.js +48 -0
  42. package/dist/commands/runtime.d.ts +21 -0
  43. package/dist/commands/runtime.js +62 -0
  44. package/dist/commands/scan.d.ts +24 -0
  45. package/dist/commands/scan.js +55 -0
  46. package/dist/commands/search.d.ts +31 -0
  47. package/dist/commands/search.js +135 -0
  48. package/dist/commands/share.d.ts +26 -0
  49. package/dist/commands/share.js +148 -0
  50. package/dist/commands/sweep.d.ts +21 -0
  51. package/dist/commands/sweep.js +69 -0
  52. package/dist/commands/ui.d.ts +9 -0
  53. package/dist/commands/ui.js +28 -0
  54. package/dist/config/env.d.ts +10 -0
  55. package/dist/config/env.js +48 -0
  56. package/dist/config/index.d.ts +9 -0
  57. package/dist/config/index.js +9 -0
  58. package/dist/config/otto-home.d.ts +2 -0
  59. package/dist/config/otto-home.js +24 -0
  60. package/dist/config/paths.d.ts +20 -0
  61. package/dist/config/paths.js +33 -0
  62. package/dist/config/private-files.d.ts +3 -0
  63. package/dist/config/private-files.js +32 -0
  64. package/dist/config/profiles.d.ts +34 -0
  65. package/dist/config/profiles.js +116 -0
  66. package/dist/config/schema.d.ts +1035 -0
  67. package/dist/config/schema.js +191 -0
  68. package/dist/config/store.d.ts +11 -0
  69. package/dist/config/store.js +73 -0
  70. package/dist/gguf.d.ts +61 -0
  71. package/dist/gguf.js +231 -0
  72. package/dist/gpu.d.ts +12 -0
  73. package/dist/gpu.js +49 -0
  74. package/dist/index.d.ts +14 -0
  75. package/dist/index.js +13 -0
  76. package/dist/main.d.ts +2 -0
  77. package/dist/main.js +5 -0
  78. package/dist/models/dirs.d.ts +9 -0
  79. package/dist/models/dirs.js +21 -0
  80. package/dist/models/download.d.ts +31 -0
  81. package/dist/models/download.js +105 -0
  82. package/dist/models/enrich.d.ts +36 -0
  83. package/dist/models/enrich.js +68 -0
  84. package/dist/models/hf.d.ts +62 -0
  85. package/dist/models/hf.js +114 -0
  86. package/dist/models/index.d.ts +15 -0
  87. package/dist/models/index.js +49 -0
  88. package/dist/models/manage.d.ts +27 -0
  89. package/dist/models/manage.js +112 -0
  90. package/dist/models/pick.d.ts +3 -0
  91. package/dist/models/pick.js +36 -0
  92. package/dist/models/scan.d.ts +13 -0
  93. package/dist/models/scan.js +185 -0
  94. package/dist/ops/archive.d.ts +34 -0
  95. package/dist/ops/archive.js +105 -0
  96. package/dist/ops/calibrate.d.ts +49 -0
  97. package/dist/ops/calibrate.js +74 -0
  98. package/dist/ops/report.d.ts +26 -0
  99. package/dist/ops/report.js +532 -0
  100. package/dist/ops/results.d.ts +207 -0
  101. package/dist/ops/results.js +250 -0
  102. package/dist/ops/sweep.d.ts +77 -0
  103. package/dist/ops/sweep.js +126 -0
  104. package/dist/output/index.d.ts +5 -0
  105. package/dist/output/index.js +5 -0
  106. package/dist/output/render.d.ts +4 -0
  107. package/dist/output/render.js +96 -0
  108. package/dist/output/types.d.ts +52 -0
  109. package/dist/output/types.js +9 -0
  110. package/dist/output/with-output.d.ts +3 -0
  111. package/dist/output/with-output.js +42 -0
  112. package/dist/run.d.ts +5 -0
  113. package/dist/run.js +19 -0
  114. package/dist/runtime/args.d.ts +21 -0
  115. package/dist/runtime/args.js +71 -0
  116. package/dist/runtime/index.d.ts +13 -0
  117. package/dist/runtime/index.js +41 -0
  118. package/dist/runtime/lmstudio.d.ts +10 -0
  119. package/dist/runtime/lmstudio.js +88 -0
  120. package/dist/runtime/managed.d.ts +26 -0
  121. package/dist/runtime/managed.js +148 -0
  122. package/dist/service/model-selector.d.ts +36 -0
  123. package/dist/service/model-selector.js +123 -0
  124. package/dist/service/pid-lock.d.ts +18 -0
  125. package/dist/service/pid-lock.js +50 -0
  126. package/dist/service/router.d.ts +144 -0
  127. package/dist/service/router.js +746 -0
  128. package/dist/service/scheduler.d.ts +74 -0
  129. package/dist/service/scheduler.js +110 -0
  130. package/dist/service/serve.d.ts +41 -0
  131. package/dist/service/serve.js +278 -0
  132. package/dist/service/supervisor.d.ts +67 -0
  133. package/dist/service/supervisor.js +222 -0
  134. package/dist/service/tailscale.d.ts +19 -0
  135. package/dist/service/tailscale.js +62 -0
  136. package/dist/service/tls.d.ts +77 -0
  137. package/dist/service/tls.js +192 -0
  138. package/dist/sysmon.d.ts +50 -0
  139. package/dist/sysmon.js +103 -0
  140. package/dist/tui/app.d.ts +208 -0
  141. package/dist/tui/app.js +1801 -0
  142. package/dist/tui/screen.d.ts +72 -0
  143. package/dist/tui/screen.js +176 -0
  144. package/dist/types.d.ts +76 -0
  145. package/dist/types.js +7 -0
  146. package/dist/version.d.ts +2 -0
  147. package/dist/version.js +8 -0
  148. package/dist/vram.d.ts +67 -0
  149. package/dist/vram.js +132 -0
  150. package/package.json +58 -0
@@ -0,0 +1,36 @@
1
+ import type { RankedModel } from "../ops/results.js";
2
+ import type { GpuInfo, Model } from "../types.js";
3
+ export declare const MIN_TRUSTED_RUNS = 2;
4
+ export declare const MAX_TRUSTED_STD = 0.15;
5
+ export declare const PROBE_CONTEXT_TOKENS = 8192;
6
+ /** Whether a scanned model carries catalog metadata marking it a coder. */
7
+ export declare function isCodingCapable(model: Model): boolean;
8
+ export interface SelectCodingModelOptions {
9
+ /** Every scanned model (the catalog the router already has). */
10
+ models: Model[];
11
+ /** Track A's per-model bench ranking (mean score + runs + std). */
12
+ ranking: RankedModel[];
13
+ /**
14
+ * VRAM-fit predicate. Omit (or pass undefined) to skip the fit filter — the
15
+ * caller does this when GPU info is absent, mirroring serve.ts's "absent →
16
+ * skip" behaviour.
17
+ */
18
+ fits?: (model: Model) => boolean;
19
+ /** Id of the currently-loaded model, a tiebreak to avoid a needless swap. */
20
+ preferLoadedId?: string | null;
21
+ /** The existing default, returned when no candidate survives. */
22
+ fallback: Model | null;
23
+ }
24
+ /**
25
+ * Pick the best-ranked coding model that fits the VRAM budget. Pure and
26
+ * deterministic — no IO, no clock, no randomness.
27
+ */
28
+ export declare function selectCodingModel({ models, ranking, fits, preferLoadedId, fallback, }: SelectCodingModelOptions): Model | null;
29
+ /**
30
+ * Build the route-time VRAM-fit predicate, or undefined when GPU info is absent
31
+ * (no nvidia-smi) so the caller skips the fit filter entirely. The probe profile
32
+ * is built once and reused: a model fits when its weights + KV at
33
+ * PROBE_CONTEXT_TOKENS sit inside the card's total VRAM budget.
34
+ */
35
+ export declare function makeVramFitPredicate(gpu: GpuInfo | null): ((model: Model) => boolean) | undefined;
36
+ //# sourceMappingURL=model-selector.d.ts.map
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Route-time model selection for the UNNAMED request path.
3
+ *
4
+ * When a client hits the brain without naming a model, the old default served
5
+ * "whatever is loaded, else catalog[0]" — blind to which local model is actually
6
+ * the best coder. This picks the best-ranked coding-capable model that fits the
7
+ * VRAM budget instead, wiring together Track A (the bench ranking) and Track B1
8
+ * (the catalog coding metadata carried onto the scanned Model).
9
+ *
10
+ * `selectCodingModel` is PURE: it takes the models, the ranking, an optional
11
+ * VRAM-fit predicate, and a fallback, and returns the chosen model with no IO —
12
+ * so the decision logic is trivially testable. `makeVramFitPredicate` is the one
13
+ * impure edge (it reads GPU total VRAM and runs a vram.budget), deliberately kept
14
+ * out of the pure path.
15
+ */
16
+ import { ProfileSchema } from "../config/schema.js";
17
+ import * as vram from "../vram.js";
18
+ // --- Minimum-confidence gate. A bench score is only "trusted" for ranking when
19
+ // it is backed by enough repeated runs and its spread across those runs is
20
+ // tight. rankModels reports one entry per model with the MEAN overall score
21
+ // (0..1), the run COUNT, and the sample STD (also 0..1). A single run has std 0
22
+ // — falsely confident — so we require at least two runs, and we reject a mean
23
+ // whose runs disagree by more than MAX_TRUSTED_STD. Untrusted models stay
24
+ // eligible but sort below every trusted one.
25
+ export const MIN_TRUSTED_RUNS = 2;
26
+ export const MAX_TRUSTED_STD = 0.15;
27
+ // --- Route-time fit probe. We do not have the saved per-model profile or a
28
+ // measured calibration here, so fit is tested at a modest working context: a
29
+ // model whose weights + KV at PROBE_CONTEXT_TOKENS overflow VRAM is excluded,
30
+ // and one that fits is includable (serve.ts clamps the real context down at load
31
+ // time via fitToBudget).
32
+ export const PROBE_CONTEXT_TOKENS = 8192;
33
+ const CODING_USE_CASE = "coding";
34
+ // Matches curated tier labels like "coding", "coder", "code-specialist".
35
+ const CODING_TIER_RE = /cod(ing|er|e-)/i;
36
+ /** Whether a scanned model carries catalog metadata marking it a coder. */
37
+ export function isCodingCapable(model) {
38
+ if (model.useCases?.some((u) => u.toLowerCase() === CODING_USE_CASE))
39
+ return true;
40
+ if (model.tier && CODING_TIER_RE.test(model.tier))
41
+ return true;
42
+ return false;
43
+ }
44
+ /** Look a scanned model up in the ranking by id first, then display name. */
45
+ function rankingLookup(ranking) {
46
+ const byId = new Map();
47
+ const byName = new Map();
48
+ for (const entry of ranking) {
49
+ if (entry.id)
50
+ byId.set(entry.id, entry);
51
+ if (!byName.has(entry.displayName))
52
+ byName.set(entry.displayName, entry);
53
+ }
54
+ return (model) => byId.get(model.id) ?? byName.get(model.displayName) ?? null;
55
+ }
56
+ /**
57
+ * Decision order:
58
+ * trusted bench score (desc) → any bench score (desc, untrusted-with-data over
59
+ * none) → loaded model (avoid a swap) → larger advertised context → name.
60
+ * The trailing name comparison makes the order total, so the pick is fully
61
+ * deterministic given the same inputs.
62
+ */
63
+ function compareCandidates(a, b, preferLoadedId) {
64
+ if (a.trusted !== b.trusted)
65
+ return a.trusted ? -1 : 1;
66
+ if (a.score !== b.score)
67
+ return b.score - a.score;
68
+ const aLoaded = preferLoadedId != null && a.model.id === preferLoadedId;
69
+ const bLoaded = preferLoadedId != null && b.model.id === preferLoadedId;
70
+ if (aLoaded !== bLoaded)
71
+ return aLoaded ? -1 : 1;
72
+ const aCtx = a.model.contextMax ?? 0;
73
+ const bCtx = b.model.contextMax ?? 0;
74
+ if (aCtx !== bCtx)
75
+ return bCtx - aCtx;
76
+ return a.model.displayName.localeCompare(b.model.displayName);
77
+ }
78
+ /**
79
+ * Pick the best-ranked coding model that fits the VRAM budget. Pure and
80
+ * deterministic — no IO, no clock, no randomness.
81
+ */
82
+ export function selectCodingModel({ models, ranking, fits, preferLoadedId = null, fallback, }) {
83
+ if (models.length === 0)
84
+ return fallback;
85
+ // 1. Candidate set: coding-capable models. If nothing is tagged (a catalog
86
+ // with no coding metadata, or hand-placed models), don't fail closed —
87
+ // fall back to the whole set.
88
+ const tagged = models.filter(isCodingCapable);
89
+ let candidates = tagged.length > 0 ? tagged : models;
90
+ // 2. VRAM fit filter (skipped when no predicate — i.e. GPU info absent). If
91
+ // nothing coding-capable fits, keep the existing default rather than
92
+ // forcing a model that overflows the budget.
93
+ if (fits) {
94
+ const fitting = candidates.filter((m) => fits(m));
95
+ if (fitting.length === 0)
96
+ return fallback;
97
+ candidates = fitting;
98
+ }
99
+ // 3. Rank by bench score behind the confidence gate.
100
+ const lookup = rankingLookup(ranking);
101
+ const scored = candidates.map((model) => {
102
+ const entry = lookup(model);
103
+ const trusted = Boolean(entry && entry.runs >= MIN_TRUSTED_RUNS && entry.std <= MAX_TRUSTED_STD);
104
+ const score = entry ? entry.overall : Number.NEGATIVE_INFINITY;
105
+ return { model, trusted, score };
106
+ });
107
+ scored.sort((a, b) => compareCandidates(a, b, preferLoadedId));
108
+ return scored[0]?.model ?? fallback;
109
+ }
110
+ /**
111
+ * Build the route-time VRAM-fit predicate, or undefined when GPU info is absent
112
+ * (no nvidia-smi) so the caller skips the fit filter entirely. The probe profile
113
+ * is built once and reused: a model fits when its weights + KV at
114
+ * PROBE_CONTEXT_TOKENS sit inside the card's total VRAM budget.
115
+ */
116
+ export function makeVramFitPredicate(gpu) {
117
+ if (!gpu)
118
+ return undefined;
119
+ const profile = ProfileSchema.parse({ contextSize: PROBE_CONTEXT_TOKENS });
120
+ const totalVramBytes = gpu.totalBytes;
121
+ return (model) => vram.budget({ model, profile, totalVramBytes }).fits;
122
+ }
123
+ //# sourceMappingURL=model-selector.js.map
@@ -0,0 +1,18 @@
1
+ export interface PidRecord {
2
+ pid: number;
3
+ host: string;
4
+ port: number;
5
+ startedAt: string;
6
+ /** Whether the listener terminates TLS. Absent on records written before TLS support. */
7
+ secure?: boolean;
8
+ /** The user-facing address (MagicDNS/cert hostname when TLS is on). Optional for old records. */
9
+ displayHost?: string;
10
+ }
11
+ export declare function writePidFile(record: PidRecord, env?: NodeJS.ProcessEnv): void;
12
+ export declare function readPidFile(env?: NodeJS.ProcessEnv): PidRecord | null;
13
+ export declare function removePidFile(env?: NodeJS.ProcessEnv): void;
14
+ /** signal 0 probes liveness without delivering a signal; EPERM means alive. */
15
+ export declare function isProcessAlive(pid: number): boolean;
16
+ /** The running service record, clearing a stale pid file if the process is gone. */
17
+ export declare function readRunningService(env?: NodeJS.ProcessEnv): PidRecord | null;
18
+ //# sourceMappingURL=pid-lock.d.ts.map
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The service pid/lock file, following the daemon's `otto.pid` pattern: a small
3
+ * JSON record of the running brain's pid + bind address under
4
+ * `$OTTO_HOME/otto-brain/otto-brain.pid`. Lets `otto brain status`/`stop` find a
5
+ * running instance and lets the Otto daemon supervise a managed child.
6
+ */
7
+ import { existsSync, readFileSync, rmSync } from "node:fs";
8
+ import { resolveBrainPaths } from "../config/paths.js";
9
+ import { writePrivateFileAtomicSync } from "../config/private-files.js";
10
+ export function writePidFile(record, env = process.env) {
11
+ const { pidFile } = resolveBrainPaths(env);
12
+ writePrivateFileAtomicSync(pidFile, `${JSON.stringify(record, null, 2)}\n`);
13
+ }
14
+ export function readPidFile(env = process.env) {
15
+ const { pidFile } = resolveBrainPaths(env);
16
+ if (!existsSync(pidFile))
17
+ return null;
18
+ try {
19
+ return JSON.parse(readFileSync(pidFile, "utf8"));
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ export function removePidFile(env = process.env) {
26
+ const { pidFile } = resolveBrainPaths(env);
27
+ rmSync(pidFile, { force: true });
28
+ }
29
+ /** signal 0 probes liveness without delivering a signal; EPERM means alive. */
30
+ export function isProcessAlive(pid) {
31
+ try {
32
+ process.kill(pid, 0);
33
+ return true;
34
+ }
35
+ catch (error) {
36
+ return error.code === "EPERM";
37
+ }
38
+ }
39
+ /** The running service record, clearing a stale pid file if the process is gone. */
40
+ export function readRunningService(env = process.env) {
41
+ const record = readPidFile(env);
42
+ if (!record)
43
+ return null;
44
+ if (!isProcessAlive(record.pid)) {
45
+ removePidFile(env);
46
+ return null;
47
+ }
48
+ return record;
49
+ }
50
+ //# sourceMappingURL=pid-lock.js.map
@@ -0,0 +1,144 @@
1
+ import http from "node:http";
2
+ import type { Supervisor } from "./supervisor.js";
3
+ import { type RankedModel } from "../ops/results.js";
4
+ import type { GpuInfo, Model } from "../types.js";
5
+ import type { Profile } from "../config/schema.js";
6
+ type Verdict = "ok" | "reasoning-only" | "truncated" | "failed";
7
+ /** A logger sink; only `warn` is used by the router. */
8
+ export interface Logger {
9
+ warn(message: string): void;
10
+ }
11
+ /** A source of the catalog: a getter, a snapshot array, or nothing. */
12
+ export type GetCatalog = (() => Model[]) | Model[] | null;
13
+ interface TelemetryTotals {
14
+ requests: number;
15
+ ok: number;
16
+ reasoningOnly: number;
17
+ truncated: number;
18
+ failed: number;
19
+ }
20
+ /** One recorded completion outcome; fields vary by streaming vs. buffered. */
21
+ export interface TelemetryRecord {
22
+ verdict: string;
23
+ at?: string;
24
+ path?: string;
25
+ ms?: number;
26
+ streamed?: boolean;
27
+ finishReason?: string | null;
28
+ contentChars?: number;
29
+ reasoningChars?: number;
30
+ outputTokens?: number | null;
31
+ toolCalls?: number;
32
+ error?: string;
33
+ }
34
+ /** The verdict of classifying a completion body. */
35
+ export interface Analysis {
36
+ finishReason: string | null;
37
+ contentChars: number;
38
+ reasoningChars: number;
39
+ outputTokens: number | null;
40
+ toolCalls: number;
41
+ verdict: Verdict;
42
+ }
43
+ export declare class Telemetry {
44
+ keep: number;
45
+ records: TelemetryRecord[];
46
+ totals: TelemetryTotals;
47
+ constructor(keep?: number);
48
+ record(entry: TelemetryRecord): void;
49
+ /** Advice derived from observed behaviour, not guesswork. */
50
+ get warning(): string | null;
51
+ }
52
+ /** Classify a completion body (Anthropic or OpenAI shaped). */
53
+ export declare function analyse(bodyText: string): Analysis | null;
54
+ type ModelState = "loaded" | "loading" | "not-loaded";
55
+ interface DescribeOptions {
56
+ state?: ModelState;
57
+ profile?: Profile | null;
58
+ createdAt?: Date | null;
59
+ }
60
+ /** An LM Studio-style model description, an OpenAI model object enriched. */
61
+ export interface ModelEntry {
62
+ id: string;
63
+ object: "model";
64
+ created: number;
65
+ owned_by: string;
66
+ type: "vlm" | "llm";
67
+ publisher: string | null;
68
+ arch: string | null;
69
+ compatibility_type: "gguf";
70
+ quantization: string | null;
71
+ state: ModelState;
72
+ max_context_length: number | null;
73
+ loaded_context_length?: number;
74
+ }
75
+ /**
76
+ * Describe one catalog model the way LM Studio's API does. Returns an
77
+ * OpenAI-style model object enriched with LM Studio's fields (arch,
78
+ * quantization, state, context lengths) so clients can display the real name
79
+ * and know the context window. `state` is 'loaded' | 'loading' | 'not-loaded';
80
+ * `loaded_context_length` is only meaningful for the model actually running.
81
+ */
82
+ export declare function describeModel(model: Model, options?: DescribeOptions): ModelEntry;
83
+ export declare function describeModel(model: Model | null, options?: DescribeOptions): ModelEntry | null;
84
+ /**
85
+ * Build the LM Studio-style catalog: every model found on disk, with the one
86
+ * the supervisor is running marked 'loaded'. Falls back to just the running
87
+ * model when no catalog provider is wired in.
88
+ */
89
+ export declare function buildModelList(supervisor: Supervisor, getCatalog: GetCatalog): ModelEntry[];
90
+ export type ModelGateResult = {
91
+ ok: true;
92
+ model: Model;
93
+ } | {
94
+ ok: false;
95
+ status: number;
96
+ message: string;
97
+ };
98
+ /**
99
+ * Pure model-admission decision, factored out of the router so it is unit
100
+ * testable. `pinned` is the single model a locked host serves; `resolved` is the
101
+ * normal catalog resolution used when the lock is off. With the lock on, a
102
+ * request naming a model other than the pin is refused (409) rather than queuing
103
+ * a switch; an unnamed request rides the pin.
104
+ */
105
+ export declare function decideModelGate(params: {
106
+ lockModel: boolean;
107
+ requestedName: string | null;
108
+ pinned: Model | null;
109
+ resolved: Model | null;
110
+ }): ModelGateResult;
111
+ export interface RouterOptions {
112
+ supervisor: Supervisor;
113
+ telemetry: Telemetry;
114
+ logger?: Logger | null;
115
+ getCatalog?: GetCatalog;
116
+ loadModel?: ((model: Model) => Promise<void>) | null;
117
+ loadRanking?: () => RankedModel[];
118
+ queryGpuInfo?: () => Promise<GpuInfo | null>;
119
+ /** The brain package version, reported on `/__host/status` for the host UI. */
120
+ version?: string | null;
121
+ /** Effective config with secrets redacted — served on `/__host/config`. */
122
+ getConfig?: (() => unknown) | null;
123
+ /** Benchmark rankings/variance/latest — served on `/__host/evals`. */
124
+ getEvals?: (() => unknown) | null;
125
+ /** Live: pin the host to one model (refuse completions naming a different one). */
126
+ getLockModel?: () => boolean;
127
+ /** Live: the configured default model, the pin target before one is resident. */
128
+ getDefaultModel?: () => string | null;
129
+ /**
130
+ * Apply an editable config patch (write config.json, live-switch the model,
131
+ * update the lock), for POST /__host/config. Absent = the write endpoint is
132
+ * not offered. Returns the new effective config (secrets redacted).
133
+ */
134
+ applyConfigPatch?: ((patch: unknown) => Promise<unknown>) | null;
135
+ /**
136
+ * Live: whether remote clients may WRITE config (POST /__host/config). Off by
137
+ * default, so a shared brain can be used but not reconfigured over the network
138
+ * until its owner opts in. Read/use are unaffected.
139
+ */
140
+ getAllowConfigWrite?: () => boolean;
141
+ }
142
+ export declare function createRouter({ supervisor, telemetry, logger, getCatalog, loadModel, loadRanking, queryGpuInfo, version, getConfig, getEvals, getLockModel, getDefaultModel, applyConfigPatch, getAllowConfigWrite, }: RouterOptions): (req: http.IncomingMessage, res: http.ServerResponse) => void;
143
+ export {};
144
+ //# sourceMappingURL=router.d.ts.map