@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,222 @@
1
+ /**
2
+ * `otto brain bench` — score a model's agentic coding ability on this machine.
3
+ * Either benchmarks an endpoint that is already serving (`--endpoint`) or loads
4
+ * each requested model itself. A long streaming run, so it prints a formatted
5
+ * report directly rather than going through the output layer.
6
+ */
7
+ import http from "node:http";
8
+ import path from "node:path";
9
+ import { forModel, getCalibration, loadBrainConfig, loadProfilesStore } from "../config/index.js";
10
+ import { query as queryGpu } from "../gpu.js";
11
+ import { pickModel, scanModels } from "../models/index.js";
12
+ import { CommandError } from "../output/types.js";
13
+ import { resolveRuntime } from "../runtime/index.js";
14
+ import { createRouter, Telemetry } from "../service/router.js";
15
+ import { Supervisor } from "../service/supervisor.js";
16
+ import * as bench from "../bench/index.js";
17
+ import { loadRepoTasks } from "../bench/repo-task.js";
18
+ import { describeCuratedRepos, findCuratedRepo } from "../bench/curated-repos.js";
19
+ import * as archive from "../ops/archive.js";
20
+ import * as results from "../ops/results.js";
21
+ import * as vram from "../vram.js";
22
+ function progress(p) {
23
+ if (p.phase === "start")
24
+ process.stderr.write(` ${(p.title ?? "").padEnd(20)} running…\n`);
25
+ if (p.phase === "done") {
26
+ process.stderr.write(` ${(p.title ?? "").padEnd(20)} ${((p.score ?? 0) * 100).toFixed(0)}% ${p.summary}\n`);
27
+ }
28
+ if (p.phase === "failed")
29
+ process.stderr.write(` failed: ${p.summary}\n`);
30
+ }
31
+ export function addBenchOptions(cmd) {
32
+ return cmd
33
+ .description("Score agentic coding ability on this machine")
34
+ .option("--model <fragments>", "model name fragment(s), comma-separated")
35
+ .option("--endpoint <host:port>", "benchmark an endpoint already serving")
36
+ .option("--only <a,b>", "run only these tasks")
37
+ .option("--depths <a,b,c>", "prompt depths for the depth-scaling task")
38
+ .option("--concurrency <n>", "requests in flight for the concurrency task", "3")
39
+ .option("--no-execute", "syntax-check generated code but do not run it")
40
+ .option("--port <n>", "internal port for the loaded model", "1251")
41
+ .option("--repo-workspace-dir <dir>", "mine SWE-bench tasks from a workspace, e.g. packages/protocol")
42
+ .option("--repo-workspace <name>", "npm workspace name for mined tasks, e.g. @otto-code/protocol")
43
+ .option("--repo-dir <dir>", "repo working copy to mine (default: current directory)")
44
+ .option("--repo-ref <ref>", "git ref to mine from", "origin/main")
45
+ .option("--repo-max <n>", "max mined tasks to run", "5")
46
+ .option("--curated <name>", "run a curated mined-repo preset (needs --repo-dir); pass an unknown name to list presets");
47
+ }
48
+ export async function runBenchCommand(options, _command) {
49
+ const config = loadBrainConfig();
50
+ const execute = options.execute !== false;
51
+ const depths = options.depths
52
+ ? options.depths.split(",").map((s) => Number(s.trim()))
53
+ : undefined;
54
+ const only = options.only ? options.only.split(",").map((s) => s.trim()) : null;
55
+ const concurrency = options.concurrency ? Math.max(1, Number(options.concurrency)) : 3;
56
+ // Opt-in: mining a repo replaces the static suite with real SWE-bench tasks.
57
+ // Absent these flags nothing touches a repo checkout.
58
+ let repoTasks;
59
+ if (options.curated) {
60
+ const preset = findCuratedRepo(options.curated);
61
+ if (!preset) {
62
+ throw new CommandError({
63
+ code: "NO_CURATED",
64
+ message: `unknown curated preset "${options.curated}". Available:\n${describeCuratedRepos()}`,
65
+ });
66
+ }
67
+ // The harness resets the working copy hard between tasks, so require an
68
+ // explicit --repo-dir rather than silently mutating the current checkout.
69
+ if (!options.repoDir) {
70
+ throw new CommandError({
71
+ code: "NO_REPO_DIR",
72
+ message: "--repo-dir is required with --curated: the working copy is reset hard " +
73
+ "(git reset --hard + git clean -fd) between tasks, so point it at a spare " +
74
+ "checkout, never one with uncommitted work.",
75
+ });
76
+ }
77
+ const dir = path.resolve(options.repoDir);
78
+ process.stderr.write(`\ncurated: mining ${preset.workspace} @ ${preset.ref} in ${dir}\n` +
79
+ ` note: this resets the working copy (git reset --hard + git clean -fd)\n`);
80
+ const tasks = await loadRepoTasks({
81
+ dir,
82
+ workspace: preset.workspace,
83
+ workspaceDir: preset.workspaceDir,
84
+ ref: preset.ref,
85
+ maxTasks: preset.maxTasks,
86
+ });
87
+ if (!tasks.length) {
88
+ throw new CommandError({
89
+ code: "NO_TASKS",
90
+ message: `no mineable fix commits found in ${preset.workspaceDir}`,
91
+ });
92
+ }
93
+ process.stderr.write(` ${tasks.length} mined task(s): ${tasks.map((t) => t.id).join(", ")}\n`);
94
+ repoTasks = tasks;
95
+ }
96
+ else if (options.repoWorkspaceDir) {
97
+ if (!options.repoWorkspace) {
98
+ throw new CommandError({
99
+ code: "NO_WORKSPACE",
100
+ message: "--repo-workspace <name> is required with --repo-workspace-dir",
101
+ });
102
+ }
103
+ const dir = options.repoDir ? path.resolve(options.repoDir) : process.cwd();
104
+ process.stderr.write(`\nmining tasks from ${options.repoWorkspaceDir} @ ${options.repoRef}\n`);
105
+ const tasks = await loadRepoTasks({
106
+ dir,
107
+ workspace: options.repoWorkspace,
108
+ workspaceDir: options.repoWorkspaceDir,
109
+ ref: options.repoRef,
110
+ maxTasks: options.repoMax ? Math.max(1, Number(options.repoMax)) : 5,
111
+ });
112
+ if (!tasks.length) {
113
+ throw new CommandError({
114
+ code: "NO_TASKS",
115
+ message: `no mineable fix commits found in ${options.repoWorkspaceDir}`,
116
+ });
117
+ }
118
+ process.stderr.write(` ${tasks.length} mined task(s): ${tasks.map((t) => t.id).join(", ")}\n`);
119
+ repoTasks = tasks;
120
+ }
121
+ if (options.endpoint) {
122
+ const [host, portStr] = options.endpoint.split(":");
123
+ const port = Number(portStr || config.listen.port);
124
+ process.stderr.write(`\nbenchmarking endpoint ${host}:${port}\n`);
125
+ const report = await bench.runSuite({
126
+ host,
127
+ port,
128
+ execute,
129
+ depths,
130
+ only,
131
+ concurrency,
132
+ tasks: repoTasks,
133
+ onProgress: progress,
134
+ });
135
+ process.stdout.write(`${bench.formatReport(report, { modelName: `${host}:${port}` })}\n`);
136
+ return;
137
+ }
138
+ const store = loadProfilesStore();
139
+ const catalog = scanModels(config);
140
+ const needles = options.model
141
+ ? options.model.split(",").map((s) => s.trim())
142
+ : [store.lastModelId].filter((v) => Boolean(v));
143
+ if (!needles.length) {
144
+ throw new CommandError({
145
+ code: "NO_MODEL",
146
+ message: "specify --model <fragment>[,<fragment>...] or --endpoint host:port",
147
+ });
148
+ }
149
+ const runtime = resolveRuntime(config);
150
+ if (!runtime) {
151
+ throw new CommandError({
152
+ code: "NO_RUNTIME",
153
+ message: "no llama.cpp runtime; run `otto brain runtime install`",
154
+ });
155
+ }
156
+ const port = Number(options.port || 1251);
157
+ const entries = [];
158
+ for (const needle of needles) {
159
+ const model = pickModel(catalog, needle);
160
+ let profile = forModel(store, model, config.defaults);
161
+ const gpu = await queryGpu();
162
+ if (gpu) {
163
+ const fit = vram.fitToBudget({
164
+ model,
165
+ profile,
166
+ calibration: getCalibration(store, model, profile),
167
+ totalVramBytes: gpu.totalBytes,
168
+ });
169
+ if (!fit.adjusted && !fit.budget.fits) {
170
+ process.stderr.write(`\nskipping ${model.displayName}: ${fit.reason}\n`);
171
+ continue;
172
+ }
173
+ profile = fit.profile;
174
+ }
175
+ process.stdout.write(`\n${"=".repeat(74)}\n${model.displayName}\n${"=".repeat(74)}\n`);
176
+ const supervisor = new Supervisor({ runtime, internalPort: port });
177
+ const telemetry = new Telemetry();
178
+ const server = http.createServer(createRouter({ supervisor, telemetry, getCatalog: () => catalog }));
179
+ await new Promise((resolve, reject) => {
180
+ server.once("error", reject);
181
+ server.listen(port + 1, "127.0.0.1", resolve);
182
+ });
183
+ try {
184
+ await supervisor.start(model, profile);
185
+ const archiveId = archive.runId(model);
186
+ const report = await bench.runSuite({
187
+ host: "127.0.0.1",
188
+ port: port + 1,
189
+ execute,
190
+ depths,
191
+ only,
192
+ concurrency,
193
+ reasoningBudget: profile.reasoningBudget ?? null,
194
+ contextWindow: profile.contextSize ?? null,
195
+ archiveId,
196
+ tasks: repoTasks,
197
+ onProgress: progress,
198
+ });
199
+ process.stdout.write(`${bench.formatReport(report, { modelName: model.displayName, profile })}\n`);
200
+ const runtimeLabel = `${runtime.label} v${runtime.version}`;
201
+ const { file } = results.save({
202
+ model,
203
+ profile,
204
+ report,
205
+ gpu,
206
+ runtime: runtimeLabel,
207
+ archiveId,
208
+ });
209
+ process.stderr.write(` saved to results/${path.basename(file)}\n`);
210
+ entries.push({ modelName: model.displayName, report });
211
+ }
212
+ finally {
213
+ await supervisor.stop();
214
+ await new Promise((resolve) => server.close(() => resolve()));
215
+ }
216
+ }
217
+ if (entries.length > 1) {
218
+ process.stdout.write(`\n${"=".repeat(74)}\n COMPARISON\n${"=".repeat(74)}\n`);
219
+ process.stdout.write(`${bench.formatComparison(entries)}\n`);
220
+ }
221
+ }
222
+ //# sourceMappingURL=bench.js.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `otto brain calibrate` — measure real KV bytes/token for a model and persist it,
3
+ * so the VRAM budget uses a measured figure instead of the (over-estimating)
4
+ * theoretical formula.
5
+ */
6
+ import type { Command } from "commander";
7
+ import type { AnyCommandResult } from "../output/index.js";
8
+ export interface CalibrateRow {
9
+ model: string;
10
+ measuredKvKB: string;
11
+ formulaKvKB: string;
12
+ overhead: string;
13
+ maxContext: string;
14
+ }
15
+ export declare function addCalibrateOptions(cmd: Command): Command;
16
+ export declare function runCalibrateCommand(options: {
17
+ model?: string;
18
+ }, _command: Command): Promise<AnyCommandResult<CalibrateRow>>;
19
+ //# sourceMappingURL=calibrate.d.ts.map
@@ -0,0 +1,75 @@
1
+ import { getCalibration, forModel, loadBrainConfig, loadProfilesStore, putCalibration, saveProfilesStore, } from "../config/index.js";
2
+ import { query as queryGpu } from "../gpu.js";
3
+ import { pickModel, scanModels } from "../models/index.js";
4
+ import { CommandError } from "../output/types.js";
5
+ import { calibrate } from "../ops/calibrate.js";
6
+ import { resolveRuntime } from "../runtime/index.js";
7
+ import * as vram from "../vram.js";
8
+ const calibrateSchema = {
9
+ idField: "model",
10
+ columns: [
11
+ { header: "MODEL", field: "model", width: 40 },
12
+ { header: "MEASURED KV", field: "measuredKvKB", width: 12, align: "right" },
13
+ { header: "FORMULA KV", field: "formulaKvKB", width: 12, align: "right" },
14
+ { header: "OVERHEAD", field: "overhead", width: 9, align: "right" },
15
+ { header: "MAX CTX", field: "maxContext", width: 12, align: "right" },
16
+ ],
17
+ };
18
+ export function addCalibrateOptions(cmd) {
19
+ return cmd
20
+ .description("Measure real KV bytes/token and save it")
21
+ .option("--model <fragment>", "model name fragment or catalog id");
22
+ }
23
+ export async function runCalibrateCommand(options, _command) {
24
+ const config = loadBrainConfig();
25
+ const runtime = resolveRuntime(config);
26
+ if (!runtime) {
27
+ throw new CommandError({
28
+ code: "NO_RUNTIME",
29
+ message: "no llama.cpp runtime available",
30
+ details: "run `otto brain runtime install`",
31
+ });
32
+ }
33
+ const store = loadProfilesStore();
34
+ const catalog = scanModels(config);
35
+ const model = pickModel(catalog, options.model ?? store.lastModelId ?? undefined);
36
+ const profile = forModel(store, model, config.defaults);
37
+ const measurement = await calibrate({
38
+ runtime,
39
+ model,
40
+ profile,
41
+ onProgress: (p) => {
42
+ if (p.phase === "loading")
43
+ process.stderr.write(` loading at ${p.contextSize?.toLocaleString()} ctx…\n`);
44
+ if (p.phase === "measured")
45
+ process.stderr.write(` used ${vram.formatGiB(p.deltaBytes ?? 0)}\n`);
46
+ if (p.phase === "skip")
47
+ process.stderr.write(` skipped ${p.contextSize}: ${p.reason}\n`);
48
+ },
49
+ });
50
+ putCalibration(store, model, profile, measurement);
51
+ saveProfilesStore(store);
52
+ const gpu = await queryGpu();
53
+ const maxCtx = gpu
54
+ ? vram.maxContextThatFits({
55
+ model,
56
+ profile,
57
+ calibration: getCalibration(store, model, profile),
58
+ totalVramBytes: gpu.totalBytes,
59
+ })
60
+ : null;
61
+ return {
62
+ type: "single",
63
+ data: {
64
+ model: model.displayName,
65
+ measuredKvKB: `${(measurement.kvBytesPerToken / 1024).toFixed(2)} KB`,
66
+ formulaKvKB: measurement.theoreticalKvBytesPerToken
67
+ ? `${(measurement.theoreticalKvBytesPerToken / 1024).toFixed(2)} KB`
68
+ : "-",
69
+ overhead: vram.formatGiB(measurement.baseOverheadBytes),
70
+ maxContext: maxCtx ? maxCtx.toLocaleString() : "unknown",
71
+ },
72
+ schema: calibrateSchema,
73
+ };
74
+ }
75
+ //# sourceMappingURL=calibrate.js.map
@@ -0,0 +1,32 @@
1
+ /**
2
+ * `otto brain catalog` — list the downloadable model catalog, each entry
3
+ * annotated with whether it is already installed. The installed flag reuses the
4
+ * authoritative catalog↔model join (enrichWithCatalog sets `catalogId` back-
5
+ * references on scanned models), so it agrees with `scan`/`pull` and works with
6
+ * no llama-server running. The daemon shells this out (`--json`) to power the
7
+ * Brain "Models" UI, which is why the row carries rich fields (repo, size,
8
+ * params, tier, use-cases) that only the JSON output surfaces.
9
+ */
10
+ import type { Command } from "commander";
11
+ import type { AnyCommandResult, OutputSchema } from "../output/index.js";
12
+ export interface CatalogRow {
13
+ id: string;
14
+ name: string;
15
+ installed: boolean;
16
+ publisher: string;
17
+ repo: string;
18
+ quant: string;
19
+ params: string;
20
+ sizeBytes: number | null;
21
+ size: string;
22
+ vision: boolean;
23
+ thinking: boolean;
24
+ contextMax: number | null;
25
+ tier: string;
26
+ useCases: string[];
27
+ why: string;
28
+ }
29
+ export declare const catalogSchema: OutputSchema<CatalogRow>;
30
+ export declare function addCatalogOptions(cmd: Command): Command;
31
+ export declare function runCatalogCommand(_options: unknown, _command: Command): Promise<AnyCommandResult<CatalogRow>>;
32
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1,48 @@
1
+ import { loadBrainConfig, loadCatalog } from "../config/index.js";
2
+ import { formatBytes, scanModels } from "../models/index.js";
3
+ export const catalogSchema = {
4
+ idField: "id",
5
+ columns: [
6
+ { header: "NAME", field: "name", width: 34 },
7
+ { header: "PARAMS", field: "params", width: 8 },
8
+ { header: "SIZE", field: "size", width: 9, align: "right" },
9
+ { header: "TIER", field: "tier", width: 8 },
10
+ {
11
+ // Format the boolean for the table only; JSON keeps the real `installed`.
12
+ header: "INSTALLED",
13
+ field: (row) => (row.installed ? "yes" : ""),
14
+ width: 9,
15
+ color: (v) => (v === "yes" ? "green" : undefined),
16
+ },
17
+ ],
18
+ };
19
+ export function addCatalogOptions(cmd) {
20
+ return cmd.description("List downloadable models from the catalog");
21
+ }
22
+ export async function runCatalogCommand(_options, _command) {
23
+ const config = loadBrainConfig();
24
+ const catalog = loadCatalog();
25
+ // Metadata-free scan: the catalog join is by path/repo, not GGUF headers, so
26
+ // skip the header reads for speed.
27
+ const installed = scanModels(config, process.env, { withMetadata: false });
28
+ const installedCatalogIds = new Set(installed.map((model) => model.catalogId).filter((id) => Boolean(id)));
29
+ const rows = catalog.models.map((model) => ({
30
+ id: model.id,
31
+ name: model.name,
32
+ installed: installedCatalogIds.has(model.id),
33
+ publisher: model.publisher ?? "",
34
+ repo: model.hfRepo,
35
+ quant: model.quant,
36
+ params: model.params ?? "",
37
+ sizeBytes: model.approxWeightsBytes ?? null,
38
+ size: model.approxWeightsBytes ? formatBytes(model.approxWeightsBytes) : "",
39
+ vision: model.vision ?? false,
40
+ thinking: model.thinking ?? false,
41
+ contextMax: model.contextMax ?? null,
42
+ tier: model.tier ?? "",
43
+ useCases: model.useCases ?? [],
44
+ why: model.why ?? "",
45
+ }));
46
+ return { type: "list", data: rows, schema: catalogSchema };
47
+ }
48
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `otto brain config show|set` — inspect and edit the persisted config at
3
+ * $OTTO_HOME/otto-brain/config.json. Opt-in flags (`enabled`, `autoStart`) live
4
+ * here; the Otto daemon reads them to decide whether to manage the brain.
5
+ */
6
+ import type { Command } from "commander";
7
+ import type { BrainConfig } from "../config/schema.js";
8
+ import type { AnyCommandResult } from "../output/index.js";
9
+ export declare function addConfigShowOptions(cmd: Command): Command;
10
+ export declare function runConfigShowCommand(_options: unknown, _command: Command): Promise<AnyCommandResult<BrainConfig>>;
11
+ export declare function addConfigSetOptions(cmd: Command): Command;
12
+ export declare function runConfigSetCommand(key: string, value: string, _options: unknown, _command: Command): Promise<AnyCommandResult<BrainConfig>>;
13
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1,90 @@
1
+ import { loadBrainConfig, loadPersistedConfig, saveBrainConfig } from "../config/index.js";
2
+ import { CommandError } from "../output/types.js";
3
+ const configSchema = {
4
+ idField: () => "config",
5
+ columns: [{ header: "CONFIG", field: () => "config" }],
6
+ renderHuman: (data) => JSON.stringify(data, null, 2),
7
+ serialize: (data) => data,
8
+ };
9
+ export function addConfigShowOptions(cmd) {
10
+ return cmd.description("Show the effective brain config");
11
+ }
12
+ export async function runConfigShowCommand(_options, _command) {
13
+ return { type: "single", data: loadBrainConfig(), schema: configSchema };
14
+ }
15
+ function parseValue(raw) {
16
+ if (raw === "true")
17
+ return true;
18
+ if (raw === "false")
19
+ return false;
20
+ const num = Number(raw);
21
+ return Number.isFinite(num) && raw.trim() !== "" ? num : raw;
22
+ }
23
+ /** Apply a dotted key (e.g. listen.port, runtime.source) to a mutable config. */
24
+ function applyKey(config, key, value) {
25
+ switch (key) {
26
+ case "enabled":
27
+ config.enabled = Boolean(value);
28
+ return;
29
+ case "autoStart":
30
+ config.autoStart = Boolean(value);
31
+ return;
32
+ case "listen.host":
33
+ config.listen.host = String(value);
34
+ return;
35
+ case "listen.port":
36
+ config.listen.port = Number(value);
37
+ return;
38
+ case "defaultModel":
39
+ config.defaultModel = String(value);
40
+ return;
41
+ case "hfToken":
42
+ config.hfToken = String(value);
43
+ return;
44
+ case "runtime.source":
45
+ config.runtime.source = value;
46
+ return;
47
+ case "auth.mode":
48
+ config.auth.mode = value;
49
+ return;
50
+ case "auth.token":
51
+ config.auth.token = String(value);
52
+ return;
53
+ case "tls.mode":
54
+ config.tls.mode = value;
55
+ return;
56
+ case "tls.certFile":
57
+ config.tls.certFile = String(value);
58
+ return;
59
+ case "tls.keyFile":
60
+ config.tls.keyFile = String(value);
61
+ return;
62
+ case "tls.hostname":
63
+ config.tls.hostname = String(value);
64
+ return;
65
+ case "tls.certDir":
66
+ config.tls.certDir = String(value);
67
+ return;
68
+ case "tls.renewBeforeDays":
69
+ config.tls.renewBeforeDays = Number(value);
70
+ return;
71
+ case "tls.tailscaleExe":
72
+ config.tls.tailscaleExe = String(value);
73
+ return;
74
+ default:
75
+ throw new CommandError({ code: "UNKNOWN_KEY", message: `unknown config key "${key}"` });
76
+ }
77
+ }
78
+ export function addConfigSetOptions(cmd) {
79
+ return cmd
80
+ .description("Set a config value")
81
+ .argument("<key>", "config key (e.g. enabled, listen.port, runtime.source)")
82
+ .argument("<value>", "new value");
83
+ }
84
+ export async function runConfigSetCommand(key, value, _options, _command) {
85
+ const config = loadPersistedConfig();
86
+ applyKey(config, key, parseValue(value));
87
+ saveBrainConfig(config);
88
+ return { type: "single", data: config, schema: configSchema };
89
+ }
90
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,29 @@
1
+ import type { Command } from "commander";
2
+ import type { AnyCommandResult, OutputSchema } from "../output/index.js";
3
+ export declare function addServeOptions(cmd: Command): Command;
4
+ export declare function runServeCommand(options: {
5
+ model?: string;
6
+ }, _command: Command): Promise<void>;
7
+ export interface LifecycleRow {
8
+ status: string;
9
+ pid: number | string;
10
+ host: string;
11
+ port: number | string;
12
+ }
13
+ export declare const lifecycleSchema: OutputSchema<LifecycleRow>;
14
+ export declare function addStartOptions(cmd: Command): Command;
15
+ export declare function runStartCommand(options: {
16
+ model?: string;
17
+ }, command: Command): Promise<AnyCommandResult<LifecycleRow>>;
18
+ export declare function addStopOptions(cmd: Command): Command;
19
+ export declare function runStopCommand(_options: unknown, _command: Command): Promise<AnyCommandResult<LifecycleRow>>;
20
+ export declare function addRestartOptions(cmd: Command): Command;
21
+ export declare function runRestartCommand(options: {
22
+ model?: string;
23
+ }, command: Command): Promise<AnyCommandResult<LifecycleRow>>;
24
+ export interface StatusRow extends LifecycleRow {
25
+ health: string;
26
+ }
27
+ export declare function addStatusOptions(cmd: Command): Command;
28
+ export declare function runStatusCommand(_options: unknown, _command: Command): Promise<AnyCommandResult<StatusRow>>;
29
+ //# sourceMappingURL=lifecycle.d.ts.map