ollanet 0.6.3 → 0.6.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.
- package/README.md +16 -441
- package/dist/alias.d.ts +9 -0
- package/dist/alias.js +147 -0
- package/dist/bench.js +14 -5
- package/dist/cli.d.ts +4 -1
- package/dist/cli.js +16 -3
- package/dist/compare.js +12 -3
- package/dist/config.d.ts +23 -0
- package/dist/config.js +73 -1
- package/dist/help.js +2 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/mcp.d.ts +2 -1
- package/dist/mcp.js +2 -1
- package/dist/prompt.js +22 -3
- package/dist/ps.js +8 -3
- package/dist/pull.js +11 -5
- package/dist/rm.d.ts +1 -1
- package/dist/rm.js +11 -4
- package/dist/show.d.ts +1 -1
- package/dist/show.js +13 -3
- package/package.json +2 -1
package/dist/bench.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ollanet bench — speed + lightweight quality suite against Ollama models.
|
|
3
3
|
*/
|
|
4
|
-
import { defaultModelForHost, loadConfig, machineSettingsForHost, mergeSettings, settingsFromEnv, } from "./config.js";
|
|
4
|
+
import { defaultModelForHost, loadConfig, lookupAlias, machineSettingsForHost, mergeSettings, settingsFromEnv, } from "./config.js";
|
|
5
5
|
import { comparabilityKey, getSuiteCases, LONG_THROUGHPUT_NUM_PREDICT, median, suiteRevision, } from "./bench-suite.js";
|
|
6
6
|
import { benchmarksDir, newBenchId, saveBenchmark } from "./bench-store.js";
|
|
7
7
|
import { readFile } from "node:fs/promises";
|
|
@@ -542,7 +542,16 @@ function pad(s, n) {
|
|
|
542
542
|
export async function main() {
|
|
543
543
|
const parsed = parseArgs(process.argv.slice(2));
|
|
544
544
|
const config = await loadConfig();
|
|
545
|
-
const
|
|
545
|
+
const alias = lookupAlias(config, parsed.machine);
|
|
546
|
+
let machineQuery = parsed.machine;
|
|
547
|
+
let models = [...parsed.models];
|
|
548
|
+
if (alias) {
|
|
549
|
+
machineQuery = alias.machine;
|
|
550
|
+
if (models.length === 0 && !parsed.all) {
|
|
551
|
+
models = [alias.model];
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
const host = await resolveTarget(machineQuery, config);
|
|
546
555
|
const baseUrl = ollamaBaseUrl(host);
|
|
547
556
|
const machineLabel = shortName(host);
|
|
548
557
|
const tags = await ollamaTags(baseUrl, Math.min(BENCH_TIMEOUT_MS, 15_000));
|
|
@@ -569,8 +578,8 @@ export async function main() {
|
|
|
569
578
|
selected.push(t.name);
|
|
570
579
|
}
|
|
571
580
|
}
|
|
572
|
-
else if (
|
|
573
|
-
for (const q of
|
|
581
|
+
else if (models.length > 0) {
|
|
582
|
+
for (const q of models) {
|
|
574
583
|
const resolved = resolveModelName(tags, q);
|
|
575
584
|
if (!resolved) {
|
|
576
585
|
console.error(`Unknown model "${q}". Available: ${tagNames.join(", ")}`);
|
|
@@ -587,7 +596,7 @@ export async function main() {
|
|
|
587
596
|
else {
|
|
588
597
|
const def = defaultModelForHost(config, host);
|
|
589
598
|
if (!def) {
|
|
590
|
-
console.error(`No model specified for "${machineLabel}". Pass model names, use --all, or
|
|
599
|
+
console.error(`No model specified for "${machineLabel}". Pass model names, use --all, set defaultModels, or use an alias.`);
|
|
591
600
|
process.exit(1);
|
|
592
601
|
}
|
|
593
602
|
const name = resolveModelName(tags, def);
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* ollanet —
|
|
3
|
+
* ollanet — Ollama over any reachable network (CLI, MCP, library).
|
|
4
4
|
*
|
|
5
5
|
* ollanet scan
|
|
6
6
|
* ollanet prompt <machine-or-ip> "hello"
|
|
7
|
+
* ollanet compare <machine> <model> <model>
|
|
7
8
|
* ollanet pull <machine> <model>
|
|
8
9
|
* ollanet show <machine> <model>
|
|
9
10
|
* ollanet rm <machine> <model> --yes
|
|
10
11
|
* ollanet ps [machine]
|
|
12
|
+
* ollanet bench <machine> [model...]
|
|
11
13
|
* ollanet chats
|
|
14
|
+
* ollanet mcp
|
|
12
15
|
*/
|
|
13
16
|
export {};
|
package/dist/cli.js
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* ollanet —
|
|
3
|
+
* ollanet — Ollama over any reachable network (CLI, MCP, library).
|
|
4
4
|
*
|
|
5
5
|
* ollanet scan
|
|
6
6
|
* ollanet prompt <machine-or-ip> "hello"
|
|
7
|
+
* ollanet compare <machine> <model> <model>
|
|
7
8
|
* ollanet pull <machine> <model>
|
|
8
9
|
* ollanet show <machine> <model>
|
|
9
10
|
* ollanet rm <machine> <model> --yes
|
|
10
11
|
* ollanet ps [machine]
|
|
12
|
+
* ollanet bench <machine> [model...]
|
|
11
13
|
* ollanet chats
|
|
14
|
+
* ollanet mcp
|
|
12
15
|
*/
|
|
13
16
|
const HELP = `ollanet — Ollama over your network
|
|
14
17
|
|
|
15
|
-
Discover
|
|
16
|
-
|
|
18
|
+
Discover hosts, manage models, prompt and compare, bench tok/s, and continue
|
|
19
|
+
chats by hash. CLI, MCP, and a Node library — same mesh.
|
|
17
20
|
|
|
18
21
|
Usage:
|
|
19
22
|
ollanet scan [--json] [--all] [--lan] [--last]
|
|
@@ -27,6 +30,7 @@ Usage:
|
|
|
27
30
|
ollanet ps [machine]
|
|
28
31
|
ollanet chats [--json] [--id <hash>]
|
|
29
32
|
ollanet bench <machine|ip> [model...] [--all] [options]
|
|
33
|
+
ollanet alias list|add|rm …
|
|
30
34
|
ollanet mcp
|
|
31
35
|
ollanet help [command]
|
|
32
36
|
|
|
@@ -44,6 +48,8 @@ Examples:
|
|
|
44
48
|
ollanet rm studio llama3.2:1b --yes
|
|
45
49
|
ollanet ps studio
|
|
46
50
|
ollanet chats
|
|
51
|
+
ollanet alias add desk studio gemma3:12b
|
|
52
|
+
ollanet prompt desk "hello" # expands machine + model
|
|
47
53
|
ollanet bench localhost --all
|
|
48
54
|
ollanet bench localhost llama3.2:1b --runs 3
|
|
49
55
|
ollanet bench localhost gemma3:12b --hot --runs 5
|
|
@@ -53,6 +59,7 @@ Examples:
|
|
|
53
59
|
Config: ~/.ollanet/config.json when installed, ./config.json in a checkout
|
|
54
60
|
(override with OLLANET_CONFIG)
|
|
55
61
|
Hosts: config.hosts, OLLANET_HOSTS, optional Tailscale + --lan
|
|
62
|
+
Aliases: config.aliases — ollanet alias add|list|rm
|
|
56
63
|
Chats: ~/.ollanet/responses or ./responses (override with OLLANET_RESPONSES_DIR)
|
|
57
64
|
Scan: ~/.ollanet/last-scan.json or ./last-scan.json (override with OLLANET_LAST_SCAN)
|
|
58
65
|
`;
|
|
@@ -162,6 +169,12 @@ async function main() {
|
|
|
162
169
|
await run();
|
|
163
170
|
return;
|
|
164
171
|
}
|
|
172
|
+
case "alias":
|
|
173
|
+
case "aliases": {
|
|
174
|
+
const { main: run } = await import("./alias.js");
|
|
175
|
+
await run();
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
165
178
|
default:
|
|
166
179
|
console.error(`Unknown command: ${cmd}\n`);
|
|
167
180
|
process.stderr.write(HELP);
|
package/dist/compare.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* ollanet compare studio gemma3:12b llama3.2:3b --prompt "Explain MagicDNS"
|
|
5
5
|
* ollanet compare studio m1 m2 --file task.md
|
|
6
6
|
*/
|
|
7
|
-
import { configFromPartial, loadConfig, machineSettingsForHost, mergeSettings, settingsFromEnv, } from "./config.js";
|
|
7
|
+
import { configFromPartial, loadConfig, lookupAlias, machineSettingsForHost, mergeSettings, settingsFromEnv, } from "./config.js";
|
|
8
8
|
import { envInt, ollamaBaseUrl, shortName } from "./hosts.js";
|
|
9
9
|
import { consumeSettingsFlag, failUsage, isHelpFlag, printHelp, takeFlag, takeValue } from "./argv.js";
|
|
10
10
|
import { resolveTarget } from "./target.js";
|
|
@@ -244,6 +244,14 @@ export async function main() {
|
|
|
244
244
|
console.error("Machine is required.");
|
|
245
245
|
usage();
|
|
246
246
|
}
|
|
247
|
+
const config = await loadConfig();
|
|
248
|
+
const alias = lookupAlias(config, parsed.machine);
|
|
249
|
+
const machine = alias ? alias.machine : parsed.machine;
|
|
250
|
+
let models = [...parsed.models];
|
|
251
|
+
if (alias && models.length === 1) {
|
|
252
|
+
// `compare desk other:7b` → alias model vs other on alias host
|
|
253
|
+
models = [alias.model, models[0]];
|
|
254
|
+
}
|
|
247
255
|
const { stdin: stdinStream } = await import("node:process");
|
|
248
256
|
let fromStdin = "";
|
|
249
257
|
if (!stdinStream.isTTY) {
|
|
@@ -265,14 +273,15 @@ export async function main() {
|
|
|
265
273
|
}
|
|
266
274
|
const prompt = assemblePrompt({ argv: parsed.prompt, file: fileText, stdin: fromStdin });
|
|
267
275
|
const record = await runCompare({
|
|
268
|
-
machine
|
|
269
|
-
models
|
|
276
|
+
machine,
|
|
277
|
+
models,
|
|
270
278
|
prompt: prompt || undefined,
|
|
271
279
|
settings: parsed.settings,
|
|
272
280
|
save: parsed.save,
|
|
273
281
|
unload: parsed.unload,
|
|
274
282
|
writeStdout: !parsed.json,
|
|
275
283
|
quiet: parsed.json,
|
|
284
|
+
config,
|
|
276
285
|
});
|
|
277
286
|
if (record.default_prompt) {
|
|
278
287
|
process.stderr.write("Using default compare prompt (pass --prompt or --file to override).\n");
|
package/dist/config.d.ts
CHANGED
|
@@ -17,6 +17,11 @@ export interface GenerateSettings {
|
|
|
17
17
|
*/
|
|
18
18
|
think?: boolean;
|
|
19
19
|
}
|
|
20
|
+
/** Named shortcut for a frequent machine + model pair. */
|
|
21
|
+
export interface ModelAlias {
|
|
22
|
+
machine: string;
|
|
23
|
+
model: string;
|
|
24
|
+
}
|
|
20
25
|
export interface AppConfig {
|
|
21
26
|
/** Explicit hosts/IPs to probe (LAN, VPN, MagicDNS, etc.). */
|
|
22
27
|
hosts: HostConfigEntry[];
|
|
@@ -28,10 +33,28 @@ export interface AppConfig {
|
|
|
28
33
|
machineDefaults: Record<string, GenerateSettings & {
|
|
29
34
|
model?: string;
|
|
30
35
|
}>;
|
|
36
|
+
/** Shortcuts: `ollanet prompt <alias> "…"` expands to machine + model. */
|
|
37
|
+
aliases: Record<string, ModelAlias>;
|
|
31
38
|
}
|
|
32
39
|
/** Build an AppConfig from an in-memory partial (no file I/O). */
|
|
33
40
|
export declare function configFromPartial(partial?: Partial<AppConfig>): AppConfig;
|
|
34
41
|
export declare function loadConfig(): Promise<AppConfig>;
|
|
42
|
+
/**
|
|
43
|
+
* Read-modify-write the config JSON on disk. Preserves unknown keys.
|
|
44
|
+
* Creates the parent directory when missing (installed `~/.ollanet/`).
|
|
45
|
+
*/
|
|
46
|
+
export declare function mutateConfigFile(mutate: (file: Record<string, unknown>) => void): Promise<void>;
|
|
47
|
+
/** Alias names: letter start, then letters/digits/_/- (no colons — those look like models). */
|
|
48
|
+
export declare function isValidAliasName(name: string): boolean;
|
|
49
|
+
export declare function lookupAlias(config: AppConfig, name: string | undefined): ModelAlias | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* If `machine` is an alias name, expand to that alias's host.
|
|
52
|
+
* When `model` is omitted, fill it from the alias too.
|
|
53
|
+
*/
|
|
54
|
+
export declare function expandMachineModel(config: AppConfig, machine: string | undefined, model: string | undefined): {
|
|
55
|
+
machine?: string;
|
|
56
|
+
model?: string;
|
|
57
|
+
};
|
|
35
58
|
export declare function normalizeSettings(input: unknown): GenerateSettings;
|
|
36
59
|
export declare function parseFormat(value: unknown): GenerateSettings["format"] | undefined;
|
|
37
60
|
export declare function hostLookupKeys(host: HostTarget): string[];
|
package/dist/config.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
2
3
|
import { shortName, } from "./hosts.js";
|
|
3
4
|
import { defaultConfigPath } from "./paths.js";
|
|
4
5
|
function emptyConfig() {
|
|
@@ -8,6 +9,7 @@ function emptyConfig() {
|
|
|
8
9
|
defaultModels: {},
|
|
9
10
|
defaults: {},
|
|
10
11
|
machineDefaults: {},
|
|
12
|
+
aliases: {},
|
|
11
13
|
};
|
|
12
14
|
}
|
|
13
15
|
/** Build an AppConfig from an in-memory partial (no file I/O). */
|
|
@@ -19,6 +21,7 @@ export function configFromPartial(partial = {}) {
|
|
|
19
21
|
defaultModels: partial.defaultModels ?? empty.defaultModels,
|
|
20
22
|
defaults: partial.defaults ?? empty.defaults,
|
|
21
23
|
machineDefaults: partial.machineDefaults ?? empty.machineDefaults,
|
|
24
|
+
aliases: partial.aliases ?? empty.aliases,
|
|
22
25
|
};
|
|
23
26
|
}
|
|
24
27
|
const CONFIG_PATH = process.env.OLLANET_CONFIG ??
|
|
@@ -42,6 +45,7 @@ export async function loadConfig() {
|
|
|
42
45
|
defaultModels,
|
|
43
46
|
defaults: normalizeSettings(parsed.defaults),
|
|
44
47
|
machineDefaults,
|
|
48
|
+
aliases: normalizeAliases(parsed.aliases),
|
|
45
49
|
};
|
|
46
50
|
}
|
|
47
51
|
catch (err) {
|
|
@@ -52,6 +56,29 @@ export async function loadConfig() {
|
|
|
52
56
|
throw new Error(`Failed to read config at ${CONFIG_PATH}: ${err instanceof Error ? err.message : err}`);
|
|
53
57
|
}
|
|
54
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Read-modify-write the config JSON on disk. Preserves unknown keys.
|
|
61
|
+
* Creates the parent directory when missing (installed `~/.ollanet/`).
|
|
62
|
+
*/
|
|
63
|
+
export async function mutateConfigFile(mutate) {
|
|
64
|
+
let file = {};
|
|
65
|
+
try {
|
|
66
|
+
const raw = await readFile(CONFIG_PATH, "utf8");
|
|
67
|
+
const parsed = JSON.parse(raw);
|
|
68
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
69
|
+
file = parsed;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
const code = err.code;
|
|
74
|
+
if (code !== "ENOENT") {
|
|
75
|
+
throw new Error(`Failed to read config at ${CONFIG_PATH}: ${err instanceof Error ? err.message : err}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
mutate(file);
|
|
79
|
+
await mkdir(path.dirname(CONFIG_PATH), { recursive: true });
|
|
80
|
+
await writeFile(CONFIG_PATH, `${JSON.stringify(file, null, 2)}\n`, "utf8");
|
|
81
|
+
}
|
|
55
82
|
function normalizeDiscovery(input) {
|
|
56
83
|
if (!input || typeof input !== "object")
|
|
57
84
|
return {};
|
|
@@ -77,6 +104,51 @@ function normalizeStringMap(map) {
|
|
|
77
104
|
}
|
|
78
105
|
return out;
|
|
79
106
|
}
|
|
107
|
+
function normalizeAliases(map) {
|
|
108
|
+
if (!map || typeof map !== "object" || Array.isArray(map))
|
|
109
|
+
return {};
|
|
110
|
+
const out = {};
|
|
111
|
+
for (const [key, value] of Object.entries(map)) {
|
|
112
|
+
const name = key.trim().toLowerCase();
|
|
113
|
+
if (!name || !value || typeof value !== "object" || Array.isArray(value))
|
|
114
|
+
continue;
|
|
115
|
+
const row = value;
|
|
116
|
+
const machine = typeof row.machine === "string" ? row.machine.trim() : "";
|
|
117
|
+
const model = typeof row.model === "string" ? row.model.trim() : "";
|
|
118
|
+
if (!machine || !model)
|
|
119
|
+
continue;
|
|
120
|
+
out[name] = { machine, model };
|
|
121
|
+
}
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
/** Alias names: letter start, then letters/digits/_/- (no colons — those look like models). */
|
|
125
|
+
export function isValidAliasName(name) {
|
|
126
|
+
return /^[a-z][a-z0-9_-]*$/i.test(name.trim());
|
|
127
|
+
}
|
|
128
|
+
export function lookupAlias(config, name) {
|
|
129
|
+
if (!name?.trim())
|
|
130
|
+
return undefined;
|
|
131
|
+
return config.aliases[name.trim().toLowerCase()];
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* If `machine` is an alias name, expand to that alias's host.
|
|
135
|
+
* When `model` is omitted, fill it from the alias too.
|
|
136
|
+
*/
|
|
137
|
+
export function expandMachineModel(config, machine, model) {
|
|
138
|
+
if (!machine?.trim())
|
|
139
|
+
return { machine, model };
|
|
140
|
+
const alias = lookupAlias(config, machine);
|
|
141
|
+
if (!alias) {
|
|
142
|
+
return {
|
|
143
|
+
machine: machine.trim(),
|
|
144
|
+
model: model?.trim() ? model.trim() : undefined,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
machine: alias.machine,
|
|
149
|
+
model: model?.trim() ? model.trim() : alias.model,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
80
152
|
function normalizeMachineDefaults(map) {
|
|
81
153
|
const out = {};
|
|
82
154
|
for (const [key, value] of Object.entries(map ?? {})) {
|
package/dist/help.js
CHANGED
|
@@ -11,6 +11,7 @@ const LOADERS = {
|
|
|
11
11
|
ps: () => import("./ps.js"),
|
|
12
12
|
compare: () => import("./compare.js"),
|
|
13
13
|
bench: () => import("./bench.js"),
|
|
14
|
+
alias: () => import("./alias.js"),
|
|
14
15
|
mcp: () => import("./mcp.js"),
|
|
15
16
|
};
|
|
16
17
|
const ALIASES = {
|
|
@@ -26,6 +27,7 @@ const ALIASES = {
|
|
|
26
27
|
vs: "compare",
|
|
27
28
|
h2h: "compare",
|
|
28
29
|
benchmark: "bench",
|
|
30
|
+
aliases: "alias",
|
|
29
31
|
};
|
|
30
32
|
export function canonicalCommand(name) {
|
|
31
33
|
const key = name.trim().toLowerCase();
|
package/dist/index.d.ts
CHANGED
|
@@ -13,8 +13,8 @@ export { lastScan, lastScanPath, loadLastScan } from "./scan-store.js";
|
|
|
13
13
|
export type { StoredScan } from "./scan-store.js";
|
|
14
14
|
export { discoverHosts, resolveHost, ollamaBaseUrl, shortName, ollamaPort, } from "./hosts.js";
|
|
15
15
|
export type { DiscoverOptions, DiscoveryResult, HostTarget, HostSource, HostConfigEntry, } from "./hosts.js";
|
|
16
|
-
export { loadConfig, defaultModelForHost, mergeSettings, configFromPartial, } from "./config.js";
|
|
17
|
-
export type { AppConfig, DiscoveryConfig, GenerateSettings } from "./config.js";
|
|
16
|
+
export { loadConfig, defaultModelForHost, mergeSettings, configFromPartial, lookupAlias, expandMachineModel, } from "./config.js";
|
|
17
|
+
export type { AppConfig, DiscoveryConfig, GenerateSettings, ModelAlias } from "./config.js";
|
|
18
18
|
export { runPrompt } from "./prompt.js";
|
|
19
19
|
export type { PromptRunOptions, PromptRunResult } from "./prompt.js";
|
|
20
20
|
export { runCompare, DEFAULT_COMPARE_PROMPT } from "./compare.js";
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
export { scanNetwork } from "./scan.js";
|
|
11
11
|
export { lastScan, lastScanPath, loadLastScan } from "./scan-store.js";
|
|
12
12
|
export { discoverHosts, resolveHost, ollamaBaseUrl, shortName, ollamaPort, } from "./hosts.js";
|
|
13
|
-
export { loadConfig, defaultModelForHost, mergeSettings, configFromPartial, } from "./config.js";
|
|
13
|
+
export { loadConfig, defaultModelForHost, mergeSettings, configFromPartial, lookupAlias, expandMachineModel, } from "./config.js";
|
|
14
14
|
export { runPrompt } from "./prompt.js";
|
|
15
15
|
export { runCompare, DEFAULT_COMPARE_PROMPT } from "./compare.js";
|
|
16
16
|
export { assemblePrompt, readPromptFile } from "./prompt-input.js";
|
package/dist/mcp.d.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* ollanet mcp — stdio MCP server (zero deps).
|
|
3
3
|
*
|
|
4
4
|
* Speaks the Model Context Protocol over stdin/stdout so agents (Cursor,
|
|
5
|
-
* Claude Desktop, etc.) can discover hosts,
|
|
5
|
+
* Claude Desktop, etc.) can discover hosts, manage models, prompt, compare,
|
|
6
|
+
* and continue chats.
|
|
6
7
|
*
|
|
7
8
|
* ollanet mcp
|
|
8
9
|
*
|
package/dist/mcp.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* ollanet mcp — stdio MCP server (zero deps).
|
|
3
3
|
*
|
|
4
4
|
* Speaks the Model Context Protocol over stdin/stdout so agents (Cursor,
|
|
5
|
-
* Claude Desktop, etc.) can discover hosts,
|
|
5
|
+
* Claude Desktop, etc.) can discover hosts, manage models, prompt, compare,
|
|
6
|
+
* and continue chats.
|
|
6
7
|
*
|
|
7
8
|
* ollanet mcp
|
|
8
9
|
*
|
package/dist/prompt.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { stdin as stdinStream } from "node:process";
|
|
15
15
|
import { appendMessage, cleanTopic, createChat, loadChat, saveChat, toApiMessages, topicFromPrompt, } from "./chat-store.js";
|
|
16
|
-
import { configPath, defaultModelForHost, loadConfig, machineSettingsForHost, mergeSettings, parseFormat, settingsFromEnv, settingsSummary, configFromPartial, } from "./config.js";
|
|
16
|
+
import { configPath, defaultModelForHost, loadConfig, lookupAlias, machineSettingsForHost, mergeSettings, parseFormat, settingsFromEnv, settingsSummary, configFromPartial, } from "./config.js";
|
|
17
17
|
import { envInt, findDiscoveredHost, ollamaBaseUrl, resolveHost, shortName, } from "./hosts.js";
|
|
18
18
|
import { ollamaChat } from "./ollama-chat.js";
|
|
19
19
|
import { assemblePrompt, readPromptFile } from "./prompt-input.js";
|
|
@@ -31,14 +31,20 @@ export function helpText() {
|
|
|
31
31
|
|
|
32
32
|
Examples:
|
|
33
33
|
ollanet prompt localhost "Explain MagicDNS"
|
|
34
|
+
ollanet prompt desk "hello" # alias → machine + model
|
|
34
35
|
ollanet prompt --chat a1b2c3d4e5f6 "Give an example"
|
|
35
36
|
ollanet prompt localhost --temperature 0.2 --num-predict 64 "2+2?"
|
|
36
37
|
ollanet prompt localhost --file ./notes.md
|
|
37
38
|
ollanet chats
|
|
39
|
+
ollanet alias add desk studio gemma3:12b
|
|
38
40
|
|
|
39
41
|
Each reply is saved under responses/<hash>.json and the hash is printed so you
|
|
40
42
|
can continue later with --chat. Use --no-save to skip persistence.
|
|
41
43
|
|
|
44
|
+
The first token may be an alias from config (\`ollanet alias\`) instead of a
|
|
45
|
+
machine name — that fills both host and model unless you pass --model / a
|
|
46
|
+
positional model.
|
|
47
|
+
|
|
42
48
|
Config: ${configPath()}
|
|
43
49
|
Precedence: CLI > env > machineDefaults > defaults
|
|
44
50
|
|
|
@@ -298,6 +304,9 @@ export async function runPrompt(opts) {
|
|
|
298
304
|
if (!machineQuery) {
|
|
299
305
|
throw new Error("Machine is required for a new chat (or pass chatId).");
|
|
300
306
|
}
|
|
307
|
+
const alias = lookupAlias(config, machineQuery);
|
|
308
|
+
if (alias)
|
|
309
|
+
machineQuery = alias.machine;
|
|
301
310
|
const host = await resolveTarget(machineQuery, config);
|
|
302
311
|
let peeledModel;
|
|
303
312
|
if (!opts.chatId && !opts.model) {
|
|
@@ -309,7 +318,11 @@ export async function runPrompt(opts) {
|
|
|
309
318
|
if (!promptText) {
|
|
310
319
|
throw new Error("No prompt provided.");
|
|
311
320
|
}
|
|
312
|
-
const model = opts.model ??
|
|
321
|
+
const model = opts.model ??
|
|
322
|
+
peeledModel ??
|
|
323
|
+
alias?.model ??
|
|
324
|
+
chat?.model ??
|
|
325
|
+
defaultModelForHost(config, host);
|
|
313
326
|
if (!model) {
|
|
314
327
|
throw new Error(`No model specified for "${shortName(host)}" and no default in ${configPath()}.\n` +
|
|
315
328
|
`Pass a model or add it under defaultModels.`);
|
|
@@ -414,6 +427,9 @@ export async function main() {
|
|
|
414
427
|
const config = await loadConfig();
|
|
415
428
|
const targets = await listTargets(config);
|
|
416
429
|
let machineQuery = parsed.machine;
|
|
430
|
+
const alias = machineQuery ? lookupAlias(config, machineQuery) : undefined;
|
|
431
|
+
if (alias)
|
|
432
|
+
machineQuery = alias.machine;
|
|
417
433
|
if (parsed.chatId) {
|
|
418
434
|
// Allow `ollanet prompt <host> --chat HASH "follow-up"` only when the
|
|
419
435
|
// leading token matches an already-discovered host (not direct-address fallback).
|
|
@@ -447,7 +463,9 @@ export async function main() {
|
|
|
447
463
|
}
|
|
448
464
|
try {
|
|
449
465
|
const result = await runPrompt({
|
|
450
|
-
|
|
466
|
+
// Pass the original alias name when present so runPrompt can apply alias.model
|
|
467
|
+
// after peel; otherwise pass the resolved machine.
|
|
468
|
+
machine: alias ? parsed.machine : machineQuery,
|
|
451
469
|
model: parsed.model ?? peeledModel,
|
|
452
470
|
prompt: promptText,
|
|
453
471
|
chatId: parsed.chatId,
|
|
@@ -456,6 +474,7 @@ export async function main() {
|
|
|
456
474
|
writeStdout: !json,
|
|
457
475
|
stream: json ? false : stream,
|
|
458
476
|
quiet: json,
|
|
477
|
+
config,
|
|
459
478
|
});
|
|
460
479
|
if (json) {
|
|
461
480
|
console.log(JSON.stringify({
|
package/dist/ps.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* ollanet ps
|
|
5
5
|
* ollanet ps studio
|
|
6
6
|
*/
|
|
7
|
-
import { configFromPartial, loadConfig } from "./config.js";
|
|
7
|
+
import { configFromPartial, expandMachineModel, loadConfig } from "./config.js";
|
|
8
8
|
import { discoverHosts, envInt, ollamaBaseUrl, shortName, } from "./hosts.js";
|
|
9
9
|
import { ollamaPs, ollamaPsRequired } from "./ollama-chat.js";
|
|
10
10
|
import { failUsage, isHelpFlag, printHelp, takeFlag } from "./argv.js";
|
|
@@ -21,10 +21,11 @@ export function helpText() {
|
|
|
21
21
|
Examples:
|
|
22
22
|
ollanet ps
|
|
23
23
|
ollanet ps studio
|
|
24
|
+
ollanet ps desk
|
|
24
25
|
ollanet ps studio --json
|
|
25
26
|
|
|
26
27
|
scan lists models on disk. ps lists what is loaded and the CPU/GPU split
|
|
27
|
-
(same rule as ollama ps: size_vram / size).
|
|
28
|
+
(same rule as ollama ps: size_vram / size). An alias name resolves to its machine.
|
|
28
29
|
|
|
29
30
|
Options:
|
|
30
31
|
--machine <name> One host (default: every discovered host)
|
|
@@ -173,7 +174,11 @@ function printPs(result) {
|
|
|
173
174
|
}
|
|
174
175
|
export async function main() {
|
|
175
176
|
const parsed = parseArgs(process.argv.slice(2));
|
|
176
|
-
const
|
|
177
|
+
const config = await loadConfig();
|
|
178
|
+
const machine = parsed.machine
|
|
179
|
+
? expandMachineModel(config, parsed.machine, undefined).machine
|
|
180
|
+
: undefined;
|
|
181
|
+
const result = await listLoaded({ machine, config });
|
|
177
182
|
if (parsed.json) {
|
|
178
183
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
179
184
|
return;
|
package/dist/pull.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* `studio` is the machine (discovered name, MagicDNS, hostname, or IP).
|
|
7
7
|
* The *server* downloads from the registry; ollanet only sends POST /api/pull.
|
|
8
8
|
*/
|
|
9
|
+
import { expandMachineModel, loadConfig } from "./config.js";
|
|
9
10
|
import { envInt, ollamaBaseUrl, shortName } from "./hosts.js";
|
|
10
11
|
import { ollamaPull } from "./ollama-chat.js";
|
|
11
12
|
import { failUsage, isHelpFlag, printHelp, takeFlag } from "./argv.js";
|
|
@@ -17,16 +18,18 @@ function pullTimeoutMs() {
|
|
|
17
18
|
export function helpText() {
|
|
18
19
|
return `Usage:
|
|
19
20
|
ollanet pull <machine> <model>
|
|
21
|
+
ollanet pull <alias>
|
|
20
22
|
ollanet pull --machine <name> --model <name>
|
|
21
23
|
|
|
22
24
|
Examples:
|
|
23
25
|
ollanet pull studio gemma3:12b
|
|
26
|
+
ollanet pull desk
|
|
24
27
|
ollanet pull localhost llama3.2:1b
|
|
25
28
|
ollanet pull 192.168.1.50 gemma3:12b --json
|
|
26
29
|
|
|
27
30
|
The named machine downloads the model onto its own disk. ollanet does not
|
|
28
31
|
upload weights. Re-pulling the same name updates it when the registry has a
|
|
29
|
-
newer digest.
|
|
32
|
+
newer digest. A single alias name expands to that alias's machine + model.
|
|
30
33
|
|
|
31
34
|
Options:
|
|
32
35
|
--machine <name> Host (discovered name, MagicDNS, hostname, or IP[:port])
|
|
@@ -171,20 +174,23 @@ export async function pullModel(opts) {
|
|
|
171
174
|
}
|
|
172
175
|
export async function main() {
|
|
173
176
|
const parsed = parseArgs(process.argv.slice(2));
|
|
174
|
-
|
|
175
|
-
|
|
177
|
+
const config = await loadConfig();
|
|
178
|
+
const expanded = expandMachineModel(config, parsed.machine, parsed.model);
|
|
179
|
+
if (!expanded.machine || !expanded.model) {
|
|
180
|
+
if (!expanded.machine)
|
|
176
181
|
console.error("Machine is required.");
|
|
177
182
|
else
|
|
178
183
|
console.error("Model is required.");
|
|
179
184
|
usage();
|
|
180
185
|
}
|
|
181
186
|
const result = await pullModel({
|
|
182
|
-
machine:
|
|
183
|
-
model:
|
|
187
|
+
machine: expanded.machine,
|
|
188
|
+
model: expanded.model,
|
|
184
189
|
insecure: parsed.insecure,
|
|
185
190
|
stream: parsed.stream,
|
|
186
191
|
writeStdout: true,
|
|
187
192
|
quiet: false,
|
|
193
|
+
config,
|
|
188
194
|
});
|
|
189
195
|
if (parsed.json) {
|
|
190
196
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
package/dist/rm.d.ts
CHANGED
package/dist/rm.js
CHANGED
|
@@ -8,20 +8,24 @@ import { envInt, ollamaBaseUrl, shortName } from "./hosts.js";
|
|
|
8
8
|
import { ollamaDelete } from "./ollama-chat.js";
|
|
9
9
|
import { failUsage, isHelpFlag, printHelp, takeFlag } from "./argv.js";
|
|
10
10
|
import { resolveTarget } from "./target.js";
|
|
11
|
+
import { expandMachineModel, loadConfig } from "./config.js";
|
|
11
12
|
function rmTimeoutMs() {
|
|
12
13
|
return envInt("OLLAMA_RM_TIMEOUT_MS", 30_000);
|
|
13
14
|
}
|
|
14
15
|
export function helpText() {
|
|
15
16
|
return `Usage:
|
|
16
17
|
ollanet rm <machine> <model> --yes
|
|
18
|
+
ollanet rm <alias> --yes
|
|
17
19
|
ollanet rm --machine <name> --model <name> --yes
|
|
18
20
|
|
|
19
21
|
Examples:
|
|
20
22
|
ollanet rm studio llama3.2:1b --yes
|
|
23
|
+
ollanet rm desk --yes
|
|
21
24
|
ollanet rm localhost old-model:7b --yes --json
|
|
22
25
|
|
|
23
26
|
Deletes the named model from that host's disk (POST /api/delete).
|
|
24
27
|
Non-interactive use (pipes, MCP, CI) requires --yes / confirm: true.
|
|
28
|
+
A single alias name expands to that alias's machine + model.
|
|
25
29
|
|
|
26
30
|
Options:
|
|
27
31
|
--machine <name> Host (discovered name, MagicDNS, hostname, or IP[:port])
|
|
@@ -111,17 +115,20 @@ export async function removeModel(opts) {
|
|
|
111
115
|
}
|
|
112
116
|
export async function main() {
|
|
113
117
|
const parsed = parseArgs(process.argv.slice(2));
|
|
114
|
-
|
|
115
|
-
|
|
118
|
+
const config = await loadConfig();
|
|
119
|
+
const expanded = expandMachineModel(config, parsed.machine, parsed.model);
|
|
120
|
+
if (!expanded.machine || !expanded.model) {
|
|
121
|
+
if (!expanded.machine)
|
|
116
122
|
console.error("Machine is required.");
|
|
117
123
|
else
|
|
118
124
|
console.error("Model is required.");
|
|
119
125
|
usage();
|
|
120
126
|
}
|
|
121
127
|
const result = await removeModel({
|
|
122
|
-
machine:
|
|
123
|
-
model:
|
|
128
|
+
machine: expanded.machine,
|
|
129
|
+
model: expanded.model,
|
|
124
130
|
yes: parsed.yes,
|
|
131
|
+
config,
|
|
125
132
|
});
|
|
126
133
|
if (parsed.json) {
|
|
127
134
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
package/dist/show.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* ollanet show studio gemma4-ctx32k
|
|
5
5
|
*/
|
|
6
6
|
import { type OllamaShowInfo } from "./ollama-chat.js";
|
|
7
|
-
import type
|
|
7
|
+
import { type AppConfig } from "./config.js";
|
|
8
8
|
export declare function helpText(): string;
|
|
9
9
|
export interface ShowOptions {
|
|
10
10
|
machine: string;
|