ollanet 0.6.0 → 0.6.2
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 +2 -1
- package/dist/argv.d.ts +22 -0
- package/dist/argv.js +86 -0
- package/dist/bench.d.ts +1 -0
- package/dist/bench.js +43 -73
- package/dist/chats.d.ts +1 -0
- package/dist/chats.js +12 -12
- package/dist/cli.js +29 -1
- package/dist/compare.d.ts +1 -0
- package/dist/compare.js +20 -68
- package/dist/help.d.ts +7 -0
- package/dist/help.js +51 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +23 -0
- package/dist/prompt.d.ts +1 -0
- package/dist/prompt.js +30 -77
- package/dist/ps.d.ts +1 -0
- package/dist/ps.js +11 -15
- package/dist/pull.d.ts +1 -0
- package/dist/pull.js +14 -17
- package/dist/rm.d.ts +1 -0
- package/dist/rm.js +14 -17
- package/dist/scan.d.ts +1 -0
- package/dist/scan.js +24 -0
- package/dist/show.d.ts +1 -0
- package/dist/show.js +14 -17
- package/dist/target.d.ts +2 -0
- package/dist/target.js +10 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -176,7 +176,7 @@ await scanNetwork({
|
|
|
176
176
|
|
|
177
177
|
`pullModel({ machine, model })` asks that host to download (or update) a library model. The bits never transit ollanet. `showModel`, `removeModel({ yes: true })`, and `listLoaded` are the inspect / delete / VRAM trio.
|
|
178
178
|
|
|
179
|
-
Requires **ollanet ≥ 0.4.0** (library). **Model management (`pull` / `show` / `rm` / `ps`) needs ≥ 0.5.0.** **Compare, `lastScan`, and bench `--hot` need ≥ 0.6.0.** Bundlers (Vite / SvelteKit SSR):
|
|
179
|
+
Requires **ollanet ≥ 0.4.0** (library). **Model management (`pull` / `show` / `rm` / `ps`) needs ≥ 0.5.0.** **Compare, `lastScan`, and bench `--hot` need ≥ 0.6.0.** **`resolveTarget` / `listTargets` need ≥ 0.6.1.** Bundlers (Vite / SvelteKit SSR):
|
|
180
180
|
|
|
181
181
|
```ts
|
|
182
182
|
// vite.config.ts
|
|
@@ -198,6 +198,7 @@ optimizeDeps: { exclude: ["ollanet"] },
|
|
|
198
198
|
| `ollanet chats` | List or inspect saved transcripts |
|
|
199
199
|
| `ollanet bench …` | Benchmark models for tok/s + lightweight quality checks |
|
|
200
200
|
| `ollanet mcp` | Stdio [MCP](https://modelcontextprotocol.io) server for agents |
|
|
201
|
+
| `ollanet help [command]` | This overview, or one command’s flags (`ollanet help bench`, `ollanet bench help`) |
|
|
201
202
|
|
|
202
203
|
### `scan` options
|
|
203
204
|
|
package/dist/argv.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared CLI flag helpers. Commands keep their own usage() and unique flags.
|
|
3
|
+
*/
|
|
4
|
+
import type { GenerateSettings } from "./config.js";
|
|
5
|
+
export type UsageFn = () => never;
|
|
6
|
+
export declare function isHelpFlag(arg: string): boolean;
|
|
7
|
+
/** Intentional help: stdout, exit 0. */
|
|
8
|
+
export declare function printHelp(text: string): never;
|
|
9
|
+
/** Usage error: stderr, exit 1. */
|
|
10
|
+
export declare function failUsage(text: string): never;
|
|
11
|
+
export declare function takeValue(args: string[], flag: string, usage: UsageFn): string;
|
|
12
|
+
/** `--name value` or `--name=value`. Returns undefined if `arg` is not this flag. */
|
|
13
|
+
export declare function takeFlag(arg: string, name: string, args: string[], usage: UsageFn): string | undefined;
|
|
14
|
+
export declare function parseNumberFlag(raw: string, flag: string, usage: UsageFn): number;
|
|
15
|
+
export declare function parseKeepAlive(raw: string): string | number;
|
|
16
|
+
export interface SettingsFlagOpts {
|
|
17
|
+
temperature?: boolean;
|
|
18
|
+
numPredict?: boolean;
|
|
19
|
+
system?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** Shared generate-settings flags. Returns true when `arg` was consumed. */
|
|
22
|
+
export declare function consumeSettingsFlag(arg: string, args: string[], settings: GenerateSettings, usage: UsageFn, opts?: SettingsFlagOpts): boolean;
|
package/dist/argv.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export function isHelpFlag(arg) {
|
|
2
|
+
return arg === "--help" || arg === "-h";
|
|
3
|
+
}
|
|
4
|
+
/** Intentional help: stdout, exit 0. */
|
|
5
|
+
export function printHelp(text) {
|
|
6
|
+
process.stdout.write(text.endsWith("\n") ? text : `${text}\n`);
|
|
7
|
+
process.exit(0);
|
|
8
|
+
}
|
|
9
|
+
/** Usage error: stderr, exit 1. */
|
|
10
|
+
export function failUsage(text) {
|
|
11
|
+
console.error(text);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
export function takeValue(args, flag, usage) {
|
|
15
|
+
const value = args.shift();
|
|
16
|
+
if (!value) {
|
|
17
|
+
console.error(`${flag} requires a value`);
|
|
18
|
+
usage();
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
/** `--name value` or `--name=value`. Returns undefined if `arg` is not this flag. */
|
|
23
|
+
export function takeFlag(arg, name, args, usage) {
|
|
24
|
+
if (arg === name)
|
|
25
|
+
return takeValue(args, name, usage);
|
|
26
|
+
if (arg.startsWith(`${name}=`))
|
|
27
|
+
return arg.slice(name.length + 1);
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
export function parseNumberFlag(raw, flag, usage) {
|
|
31
|
+
const n = Number(raw);
|
|
32
|
+
if (!Number.isFinite(n)) {
|
|
33
|
+
console.error(`${flag} must be a number (got "${raw}")`);
|
|
34
|
+
usage();
|
|
35
|
+
}
|
|
36
|
+
return n;
|
|
37
|
+
}
|
|
38
|
+
export function parseKeepAlive(raw) {
|
|
39
|
+
const asNum = Number(raw);
|
|
40
|
+
return raw.trim() !== "" && Number.isFinite(asNum) && String(asNum) === raw ? asNum : raw;
|
|
41
|
+
}
|
|
42
|
+
/** Shared generate-settings flags. Returns true when `arg` was consumed. */
|
|
43
|
+
export function consumeSettingsFlag(arg, args, settings, usage, opts = {}) {
|
|
44
|
+
if (arg === "--think") {
|
|
45
|
+
settings.think = true;
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
if (arg === "--no-think") {
|
|
49
|
+
settings.think = false;
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
if (opts.system) {
|
|
53
|
+
const system = takeFlag(arg, "--system", args, usage);
|
|
54
|
+
if (system !== undefined) {
|
|
55
|
+
settings.system = system;
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (opts.temperature) {
|
|
60
|
+
if (arg === "-t" || arg === "--temperature" || arg.startsWith("--temperature=")) {
|
|
61
|
+
const raw = arg === "-t" || arg === "--temperature"
|
|
62
|
+
? takeValue(args, "--temperature", usage)
|
|
63
|
+
: arg.slice("--temperature=".length);
|
|
64
|
+
settings.temperature = parseNumberFlag(raw, "--temperature", usage);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (opts.numPredict) {
|
|
69
|
+
const raw = takeFlag(arg, "--num-predict", args, usage);
|
|
70
|
+
if (raw !== undefined) {
|
|
71
|
+
settings.num_predict = Math.trunc(parseNumberFlag(raw, "--num-predict", usage));
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const numCtx = takeFlag(arg, "--num-ctx", args, usage);
|
|
76
|
+
if (numCtx !== undefined) {
|
|
77
|
+
settings.num_ctx = Math.trunc(parseNumberFlag(numCtx, "--num-ctx", usage));
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
const keepAlive = takeFlag(arg, "--keep-alive", args, usage);
|
|
81
|
+
if (keepAlive !== undefined) {
|
|
82
|
+
settings.keep_alive = parseKeepAlive(keepAlive);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
package/dist/bench.d.ts
CHANGED
package/dist/bench.js
CHANGED
|
@@ -5,57 +5,51 @@ import { defaultModelForHost, loadConfig, machineSettingsForHost, mergeSettings,
|
|
|
5
5
|
import { comparabilityKey, getSuiteCases, median, suiteRevision, } from "./bench-suite.js";
|
|
6
6
|
import { benchmarksDir, newBenchId, saveBenchmark } from "./bench-store.js";
|
|
7
7
|
import { readFile } from "node:fs/promises";
|
|
8
|
-
import {
|
|
8
|
+
import { envInt, ollamaBaseUrl, shortName } from "./hosts.js";
|
|
9
|
+
import { consumeSettingsFlag, failUsage, isHelpFlag, parseNumberFlag, printHelp, takeFlag } from "./argv.js";
|
|
10
|
+
import { resolveTarget } from "./target.js";
|
|
9
11
|
import { projectPath } from "./paths.js";
|
|
10
12
|
import { contextLengthForModel, isCompletionCapable, isVisionCapable, ollamaChat, ollamaShow, ollamaTags, ollamaUnload, ollamaVersion, ollamaPs, psHasModel, shouldWarnNoThinking, vramForModel, waitUntilUnloaded, } from "./ollama-chat.js";
|
|
11
13
|
const MODEL_COL = 34;
|
|
12
14
|
const BENCH_TIMEOUT_MS = envInt("OLLAMA_BENCH_TIMEOUT_MS", 60_000);
|
|
13
15
|
const UNLOAD_WAIT_MS = envInt("OLLAMA_BENCH_UNLOAD_WAIT_MS", 60_000);
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
+
export function helpText() {
|
|
17
|
+
return `Usage:
|
|
16
18
|
ollanet bench <machine|ip> [model...] [options]
|
|
17
19
|
ollanet bench <machine|ip> --all [options]
|
|
18
20
|
|
|
19
21
|
Examples:
|
|
20
22
|
ollanet bench studio gemma3:12b --runs 5
|
|
21
23
|
ollanet bench studio gemma3:12b --hot --runs 5
|
|
24
|
+
ollanet help bench
|
|
25
|
+
|
|
26
|
+
Fixed suite (not compare). Checks run once; throughput repeats --runs times.
|
|
27
|
+
Median tok/s excludes early-stops. Unload is between models, not between repeats.
|
|
22
28
|
|
|
23
29
|
Options:
|
|
24
30
|
--all Every completion-capable model from /api/tags
|
|
25
31
|
--exclude-vision With --all, skip models that advertise vision
|
|
26
32
|
--suite quick|full Prompt suite (default quick)
|
|
27
|
-
--runs <n>
|
|
33
|
+
--runs <n> Counted throughput repeats (default 3)
|
|
28
34
|
--hot Discard first throughput run; keep models loaded
|
|
29
|
-
--warmup
|
|
35
|
+
--warmup Short discarded call before timed cases (default on)
|
|
36
|
+
--no-warmup Skip the short warmup (--hot implies this)
|
|
30
37
|
--cold-load Measure cold load via unload + /api/ps
|
|
31
38
|
--num-predict <n> Pin throughput length (default 256)
|
|
32
|
-
--num-ctx <n>
|
|
33
|
-
--keep-alive <value>
|
|
34
|
-
--think / --no-think
|
|
35
|
-
--judge
|
|
39
|
+
--num-ctx <n> Context window override
|
|
40
|
+
--keep-alive <value> Keep model loaded (e.g. 5m, 0, -1)
|
|
41
|
+
--think / --no-think Thinking tokens (default off)
|
|
42
|
+
--judge Second pass: score check answers 1–5
|
|
43
|
+
--judge-model <name> Required with --judge (no default)
|
|
36
44
|
--json Machine-readable stdout
|
|
37
|
-
--save / --no-save
|
|
38
|
-
--fail-fast
|
|
45
|
+
--save / --no-save Persist under benchmarks/ (default on)
|
|
46
|
+
--fail-fast Stop remaining models after the first hard error
|
|
39
47
|
--fail-on-error Exit 2 if any model/case errored
|
|
40
48
|
|
|
41
|
-
See docs/bench-spec.md for measurement rules
|
|
42
|
-
process.exit(1);
|
|
43
|
-
}
|
|
44
|
-
function takeValue(args, flag) {
|
|
45
|
-
const value = args.shift();
|
|
46
|
-
if (!value) {
|
|
47
|
-
console.error(`${flag} requires a value`);
|
|
48
|
-
usage();
|
|
49
|
-
}
|
|
50
|
-
return value;
|
|
49
|
+
See docs/bench-spec.md for measurement rules.`;
|
|
51
50
|
}
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
if (!Number.isFinite(n)) {
|
|
55
|
-
console.error(`${flag} must be a number (got "${raw}")`);
|
|
56
|
-
usage();
|
|
57
|
-
}
|
|
58
|
-
return n;
|
|
51
|
+
function usage() {
|
|
52
|
+
failUsage(helpText());
|
|
59
53
|
}
|
|
60
54
|
function isLoopback(host) {
|
|
61
55
|
return (host.source === "localhost" ||
|
|
@@ -104,7 +98,9 @@ function parseArgs(argv) {
|
|
|
104
98
|
const positional = [];
|
|
105
99
|
while (args.length > 0) {
|
|
106
100
|
const arg = args.shift();
|
|
107
|
-
if (arg
|
|
101
|
+
if (isHelpFlag(arg))
|
|
102
|
+
printHelp(helpText());
|
|
103
|
+
if (arg === "--")
|
|
108
104
|
usage();
|
|
109
105
|
if (arg === "--all") {
|
|
110
106
|
all = true;
|
|
@@ -157,57 +153,35 @@ function parseArgs(argv) {
|
|
|
157
153
|
failOnError = true;
|
|
158
154
|
continue;
|
|
159
155
|
}
|
|
160
|
-
if (arg === "--think") {
|
|
161
|
-
settings.think = true;
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
if (arg === "--no-think") {
|
|
165
|
-
settings.think = false;
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
156
|
if (arg === "--judge") {
|
|
169
157
|
judge = true;
|
|
170
158
|
continue;
|
|
171
159
|
}
|
|
172
|
-
if (arg
|
|
173
|
-
const raw = arg.includes("=") ? arg.slice("--suite=".length) : takeValue(args, "--suite");
|
|
174
|
-
if (raw !== "quick" && raw !== "full") {
|
|
175
|
-
console.error(`Unknown suite "${raw}" (use quick|full)`);
|
|
176
|
-
usage();
|
|
177
|
-
}
|
|
178
|
-
suite = raw;
|
|
160
|
+
if (consumeSettingsFlag(arg, args, settings, usage)) {
|
|
179
161
|
continue;
|
|
180
162
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
? arg.slice("--num-predict=".length)
|
|
189
|
-
: takeValue(args, "--num-predict");
|
|
190
|
-
throughputNumPredict = Math.trunc(parseNumberFlag(raw, "--num-predict"));
|
|
163
|
+
const suiteRaw = takeFlag(arg, "--suite", args, usage);
|
|
164
|
+
if (suiteRaw !== undefined) {
|
|
165
|
+
if (suiteRaw !== "quick" && suiteRaw !== "full") {
|
|
166
|
+
console.error(`Unknown suite "${suiteRaw}" (use quick|full)`);
|
|
167
|
+
usage();
|
|
168
|
+
}
|
|
169
|
+
suite = suiteRaw;
|
|
191
170
|
continue;
|
|
192
171
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
172
|
+
const runsRaw = takeFlag(arg, "--runs", args, usage);
|
|
173
|
+
if (runsRaw !== undefined) {
|
|
174
|
+
runs = Math.max(1, Math.trunc(parseNumberFlag(runsRaw, "--runs", usage)));
|
|
196
175
|
continue;
|
|
197
176
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
: takeValue(args, "--keep-alive");
|
|
202
|
-
const asNum = Number(raw);
|
|
203
|
-
settings.keep_alive =
|
|
204
|
-
raw.trim() !== "" && Number.isFinite(asNum) && String(asNum) === raw ? asNum : raw;
|
|
177
|
+
const predictRaw = takeFlag(arg, "--num-predict", args, usage);
|
|
178
|
+
if (predictRaw !== undefined) {
|
|
179
|
+
throughputNumPredict = Math.trunc(parseNumberFlag(predictRaw, "--num-predict", usage));
|
|
205
180
|
continue;
|
|
206
181
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
: takeValue(args, "--judge-model");
|
|
182
|
+
const judgeRaw = takeFlag(arg, "--judge-model", args, usage);
|
|
183
|
+
if (judgeRaw !== undefined) {
|
|
184
|
+
judgeModel = judgeRaw;
|
|
211
185
|
continue;
|
|
212
186
|
}
|
|
213
187
|
if (arg.startsWith("-")) {
|
|
@@ -527,11 +501,7 @@ function pad(s, n) {
|
|
|
527
501
|
export async function main() {
|
|
528
502
|
const parsed = parseArgs(process.argv.slice(2));
|
|
529
503
|
const config = await loadConfig();
|
|
530
|
-
const
|
|
531
|
-
hosts: config.hosts,
|
|
532
|
-
discovery: config.discovery,
|
|
533
|
-
});
|
|
534
|
-
const host = resolveHost(targets, parsed.machine);
|
|
504
|
+
const host = await resolveTarget(parsed.machine, config);
|
|
535
505
|
const baseUrl = ollamaBaseUrl(host);
|
|
536
506
|
const machineLabel = shortName(host);
|
|
537
507
|
const tags = await ollamaTags(baseUrl, Math.min(BENCH_TIMEOUT_MS, 15_000));
|
package/dist/chats.d.ts
CHANGED
package/dist/chats.js
CHANGED
|
@@ -7,15 +7,18 @@
|
|
|
7
7
|
* ollanet chats --id a1b2c3d4e5f6
|
|
8
8
|
*/
|
|
9
9
|
import { loadChat, listChats, responsesDir, } from "./chat-store.js";
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
import { failUsage, isHelpFlag, printHelp, takeFlag } from "./argv.js";
|
|
11
|
+
export function helpText() {
|
|
12
|
+
return `Usage: ollanet chats [--json] [--id <hash>]
|
|
12
13
|
|
|
13
14
|
Lists transcripts in ${responsesDir()}.
|
|
14
15
|
|
|
15
16
|
Options:
|
|
16
17
|
--json Machine-readable output
|
|
17
|
-
--id <hash> Show one chat (messages included with --json, preview otherwise)
|
|
18
|
-
|
|
18
|
+
--id <hash> Show one chat (messages included with --json, preview otherwise)`;
|
|
19
|
+
}
|
|
20
|
+
function usage() {
|
|
21
|
+
failUsage(helpText());
|
|
19
22
|
}
|
|
20
23
|
function parseArgs(argv) {
|
|
21
24
|
const args = [...argv];
|
|
@@ -25,18 +28,15 @@ function parseArgs(argv) {
|
|
|
25
28
|
const arg = args.shift();
|
|
26
29
|
if (arg === "--")
|
|
27
30
|
continue;
|
|
28
|
-
if (arg
|
|
29
|
-
|
|
31
|
+
if (isHelpFlag(arg))
|
|
32
|
+
printHelp(helpText());
|
|
30
33
|
if (arg === "--json") {
|
|
31
34
|
json = true;
|
|
32
35
|
continue;
|
|
33
36
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
console.error("--id requires a value");
|
|
38
|
-
usage();
|
|
39
|
-
}
|
|
37
|
+
const idFlag = takeFlag(arg, "--id", args, usage);
|
|
38
|
+
if (idFlag !== undefined) {
|
|
39
|
+
id = idFlag;
|
|
40
40
|
continue;
|
|
41
41
|
}
|
|
42
42
|
console.error(`Unknown flag: ${arg}`);
|
package/dist/cli.js
CHANGED
|
@@ -28,6 +28,7 @@ Usage:
|
|
|
28
28
|
ollanet chats [--json] [--id <hash>]
|
|
29
29
|
ollanet bench <machine|ip> [model...] [--all] [options]
|
|
30
30
|
ollanet mcp
|
|
31
|
+
ollanet help [command]
|
|
31
32
|
|
|
32
33
|
Examples:
|
|
33
34
|
ollanet scan
|
|
@@ -46,6 +47,7 @@ Examples:
|
|
|
46
47
|
ollanet bench localhost --all
|
|
47
48
|
ollanet bench localhost llama3.2:1b --runs 3
|
|
48
49
|
ollanet bench localhost gemma3:12b --hot --runs 5
|
|
50
|
+
ollanet help bench # flags for one command (also: ollanet bench help)
|
|
49
51
|
ollanet mcp # stdio MCP server for agents
|
|
50
52
|
|
|
51
53
|
Config: ~/.ollanet/config.json when installed, ./config.json in a checkout
|
|
@@ -58,10 +60,36 @@ async function main() {
|
|
|
58
60
|
// pnpm/npm often forward a bare `--`; ignore it.
|
|
59
61
|
const argv = process.argv.slice(2).filter((arg) => arg !== "--");
|
|
60
62
|
const cmd = argv[0];
|
|
61
|
-
if (!cmd || cmd === "-h" || cmd === "--help"
|
|
63
|
+
if (!cmd || cmd === "-h" || cmd === "--help") {
|
|
62
64
|
process.stdout.write(HELP);
|
|
63
65
|
return;
|
|
64
66
|
}
|
|
67
|
+
if (cmd === "help") {
|
|
68
|
+
const topic = argv[1];
|
|
69
|
+
if (!topic || topic === "-h" || topic === "--help") {
|
|
70
|
+
process.stdout.write(HELP);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const { commandHelp, commandNames } = await import("./help.js");
|
|
74
|
+
const text = await commandHelp(topic);
|
|
75
|
+
if (!text) {
|
|
76
|
+
console.error(`Unknown command: ${topic}`);
|
|
77
|
+
console.error(`Try: ollanet help (commands: ${commandNames().join(", ")})`);
|
|
78
|
+
process.exitCode = 1;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
process.stdout.write(text.endsWith("\n") ? text : `${text}\n`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const rest = argv.slice(1);
|
|
85
|
+
const { isHelpRequest, commandHelp } = await import("./help.js");
|
|
86
|
+
if (isHelpRequest(rest)) {
|
|
87
|
+
const text = await commandHelp(cmd);
|
|
88
|
+
if (text) {
|
|
89
|
+
process.stdout.write(text.endsWith("\n") ? text : `${text}\n`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
65
93
|
if (cmd === "-v" || cmd === "--version" || cmd === "version") {
|
|
66
94
|
const { readFile } = await import("node:fs/promises");
|
|
67
95
|
const { projectPath } = await import("./paths.js");
|
package/dist/compare.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { type AppConfig, type GenerateSettings } from "./config.js";
|
|
8
8
|
/** Used when --prompt, --file, and stdin are all omitted. */
|
|
9
9
|
export declare const DEFAULT_COMPARE_PROMPT: string;
|
|
10
|
+
export declare function helpText(): string;
|
|
10
11
|
export interface CompareModelResult {
|
|
11
12
|
model: string;
|
|
12
13
|
content?: string;
|
package/dist/compare.js
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* ollanet compare studio m1 m2 --file task.md
|
|
6
6
|
*/
|
|
7
7
|
import { configFromPartial, loadConfig, machineSettingsForHost, mergeSettings, settingsFromEnv, } from "./config.js";
|
|
8
|
-
import {
|
|
8
|
+
import { envInt, ollamaBaseUrl, shortName } from "./hosts.js";
|
|
9
|
+
import { consumeSettingsFlag, failUsage, isHelpFlag, printHelp, takeFlag, takeValue } from "./argv.js";
|
|
10
|
+
import { resolveTarget } from "./target.js";
|
|
9
11
|
import { ollamaChat, ollamaTags, ollamaUnload, waitUntilUnloaded, } from "./ollama-chat.js";
|
|
10
12
|
import { assemblePrompt, readPromptFile } from "./prompt-input.js";
|
|
11
13
|
import { comparesDir, newCompareId, saveCompare } from "./compare-store.js";
|
|
@@ -21,8 +23,8 @@ function compareTimeoutMs() {
|
|
|
21
23
|
function unloadWaitMs() {
|
|
22
24
|
return envInt("OLLAMA_COMPARE_UNLOAD_WAIT_MS", 60_000);
|
|
23
25
|
}
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
+
export function helpText() {
|
|
27
|
+
return `Usage:
|
|
26
28
|
ollanet compare <machine> <model> <model> [model...]
|
|
27
29
|
ollanet compare <machine> <model> <model> [model...] --prompt <text>
|
|
28
30
|
ollanet compare <machine> <model> <model> [model...] --file <path.txt|.md>
|
|
@@ -47,24 +49,10 @@ Options:
|
|
|
47
49
|
--think / --no-think
|
|
48
50
|
--unload Unload each model before the next (fairer tok/s)
|
|
49
51
|
--no-save Do not write compares/*
|
|
50
|
-
--json Emit the full record on stdout
|
|
51
|
-
process.exit(1);
|
|
52
|
-
}
|
|
53
|
-
function takeValue(args, flag) {
|
|
54
|
-
const value = args.shift();
|
|
55
|
-
if (!value) {
|
|
56
|
-
console.error(`${flag} requires a value`);
|
|
57
|
-
usage();
|
|
58
|
-
}
|
|
59
|
-
return value;
|
|
52
|
+
--json Emit the full record on stdout`;
|
|
60
53
|
}
|
|
61
|
-
function
|
|
62
|
-
|
|
63
|
-
if (!Number.isFinite(n)) {
|
|
64
|
-
console.error(`${flag} must be a number (got "${raw}")`);
|
|
65
|
-
usage();
|
|
66
|
-
}
|
|
67
|
-
return n;
|
|
54
|
+
function usage() {
|
|
55
|
+
failUsage(helpText());
|
|
68
56
|
}
|
|
69
57
|
function parseArgs(argv) {
|
|
70
58
|
const args = [...argv];
|
|
@@ -79,8 +67,8 @@ function parseArgs(argv) {
|
|
|
79
67
|
const arg = args.shift();
|
|
80
68
|
if (arg === "--")
|
|
81
69
|
continue;
|
|
82
|
-
if (arg
|
|
83
|
-
|
|
70
|
+
if (isHelpFlag(arg))
|
|
71
|
+
printHelp(helpText());
|
|
84
72
|
if (arg === "--json") {
|
|
85
73
|
json = true;
|
|
86
74
|
continue;
|
|
@@ -93,48 +81,19 @@ function parseArgs(argv) {
|
|
|
93
81
|
unload = true;
|
|
94
82
|
continue;
|
|
95
83
|
}
|
|
96
|
-
if (arg
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
settings.think = false;
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
if (arg === "--prompt" || arg === "-p" || arg.startsWith("--prompt=")) {
|
|
105
|
-
prompt = arg.includes("=") ? arg.slice("--prompt=".length) : takeValue(args, "--prompt");
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
if (arg === "--file" || arg === "-f" || arg.startsWith("--file=")) {
|
|
109
|
-
file = arg.includes("=") ? arg.slice("--file=".length) : takeValue(args, "--file");
|
|
84
|
+
if (consumeSettingsFlag(arg, args, settings, usage, {
|
|
85
|
+
temperature: true,
|
|
86
|
+
numPredict: true,
|
|
87
|
+
system: true,
|
|
88
|
+
})) {
|
|
110
89
|
continue;
|
|
111
90
|
}
|
|
112
|
-
if (arg === "--
|
|
113
|
-
|
|
114
|
-
? arg.slice("--system=".length)
|
|
115
|
-
: takeValue(args, "--system");
|
|
91
|
+
if (arg === "-p" || arg === "--prompt" || arg.startsWith("--prompt=")) {
|
|
92
|
+
prompt = arg === "-p" ? takeValue(args, "--prompt", usage) : takeFlag(arg, "--prompt", args, usage);
|
|
116
93
|
continue;
|
|
117
94
|
}
|
|
118
|
-
if (arg === "
|
|
119
|
-
|
|
120
|
-
settings.temperature = parseNumberFlag(raw, "--temperature");
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
if (arg === "--num-predict" || arg.startsWith("--num-predict=")) {
|
|
124
|
-
const raw = arg.includes("=") ? arg.slice("--num-predict=".length) : takeValue(args, "--num-predict");
|
|
125
|
-
settings.num_predict = Math.trunc(parseNumberFlag(raw, "--num-predict"));
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
if (arg === "--num-ctx" || arg.startsWith("--num-ctx=")) {
|
|
129
|
-
const raw = arg.includes("=") ? arg.slice("--num-ctx=".length) : takeValue(args, "--num-ctx");
|
|
130
|
-
settings.num_ctx = Math.trunc(parseNumberFlag(raw, "--num-ctx"));
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
if (arg === "--keep-alive" || arg.startsWith("--keep-alive=")) {
|
|
134
|
-
const raw = arg.includes("=") ? arg.slice("--keep-alive=".length) : takeValue(args, "--keep-alive");
|
|
135
|
-
const asNum = Number(raw);
|
|
136
|
-
settings.keep_alive =
|
|
137
|
-
raw.trim() !== "" && Number.isFinite(asNum) && String(asNum) === raw ? asNum : raw;
|
|
95
|
+
if (arg === "-f" || arg === "--file" || arg.startsWith("--file=")) {
|
|
96
|
+
file = arg === "-f" ? takeValue(args, "--file", usage) : takeFlag(arg, "--file", args, usage);
|
|
138
97
|
continue;
|
|
139
98
|
}
|
|
140
99
|
if (arg.startsWith("-")) {
|
|
@@ -189,14 +148,7 @@ export async function runCompare(opts) {
|
|
|
189
148
|
const defaulted = !assembled;
|
|
190
149
|
const prompt = assembled || DEFAULT_COMPARE_PROMPT;
|
|
191
150
|
const config = opts.config ? configFromPartial(opts.config) : await loadConfig();
|
|
192
|
-
const
|
|
193
|
-
hosts: config.hosts,
|
|
194
|
-
discovery: config.discovery,
|
|
195
|
-
});
|
|
196
|
-
const host = resolveHost(hosts, opts.machine);
|
|
197
|
-
if (!host.online && !host.isSelf && host.source === "tailscale") {
|
|
198
|
-
throw new Error(`Machine "${shortName(host)}" appears offline.`);
|
|
199
|
-
}
|
|
151
|
+
const host = await resolveTarget(opts.machine, config);
|
|
200
152
|
const baseUrl = ollamaBaseUrl(host);
|
|
201
153
|
const timeoutMs = opts.timeoutMs ?? compareTimeoutMs();
|
|
202
154
|
let available = null;
|
package/dist/help.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-command help: `ollanet help bench` and `ollanet bench help`.
|
|
3
|
+
*/
|
|
4
|
+
export declare function canonicalCommand(name: string): string | undefined;
|
|
5
|
+
export declare function commandNames(): string[];
|
|
6
|
+
export declare function commandHelp(name: string): Promise<string | undefined>;
|
|
7
|
+
export declare function isHelpRequest(args: string[]): boolean;
|
package/dist/help.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-command help: `ollanet help bench` and `ollanet bench help`.
|
|
3
|
+
*/
|
|
4
|
+
const LOADERS = {
|
|
5
|
+
scan: () => import("./scan.js"),
|
|
6
|
+
prompt: () => import("./prompt.js"),
|
|
7
|
+
chats: () => import("./chats.js"),
|
|
8
|
+
pull: () => import("./pull.js"),
|
|
9
|
+
show: () => import("./show.js"),
|
|
10
|
+
rm: () => import("./rm.js"),
|
|
11
|
+
ps: () => import("./ps.js"),
|
|
12
|
+
compare: () => import("./compare.js"),
|
|
13
|
+
bench: () => import("./bench.js"),
|
|
14
|
+
mcp: () => import("./mcp.js"),
|
|
15
|
+
};
|
|
16
|
+
const ALIASES = {
|
|
17
|
+
"ls-servers": "scan",
|
|
18
|
+
ask: "prompt",
|
|
19
|
+
chat: "prompt",
|
|
20
|
+
ls: "chats",
|
|
21
|
+
history: "chats",
|
|
22
|
+
download: "pull",
|
|
23
|
+
inspect: "show",
|
|
24
|
+
delete: "rm",
|
|
25
|
+
loaded: "ps",
|
|
26
|
+
vs: "compare",
|
|
27
|
+
h2h: "compare",
|
|
28
|
+
benchmark: "bench",
|
|
29
|
+
};
|
|
30
|
+
export function canonicalCommand(name) {
|
|
31
|
+
const key = name.trim().toLowerCase();
|
|
32
|
+
if (LOADERS[key])
|
|
33
|
+
return key;
|
|
34
|
+
return ALIASES[key];
|
|
35
|
+
}
|
|
36
|
+
export function commandNames() {
|
|
37
|
+
return Object.keys(LOADERS);
|
|
38
|
+
}
|
|
39
|
+
export async function commandHelp(name) {
|
|
40
|
+
const canon = canonicalCommand(name);
|
|
41
|
+
if (!canon)
|
|
42
|
+
return undefined;
|
|
43
|
+
const load = LOADERS[canon];
|
|
44
|
+
if (!load)
|
|
45
|
+
return undefined;
|
|
46
|
+
const mod = await load();
|
|
47
|
+
return mod.helpText();
|
|
48
|
+
}
|
|
49
|
+
export function isHelpRequest(args) {
|
|
50
|
+
return args.length === 1 && (args[0] === "help" || args[0] === "--help" || args[0] === "-h");
|
|
51
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -29,5 +29,6 @@ export type { RemoveOptions, RemoveResult } from "./rm.js";
|
|
|
29
29
|
export { listLoaded } from "./ps.js";
|
|
30
30
|
export type { PsOptions, PsResult, LoadedHost, LoadedModel } from "./ps.js";
|
|
31
31
|
export { looksTuned } from "./tuned.js";
|
|
32
|
+
export { resolveTarget, listTargets } from "./target.js";
|
|
32
33
|
export { ollamaChat, ollamaTags, ollamaShow, ollamaPs, ollamaPsRequired, ollamaPull, ollamaDelete, } from "./ollama-chat.js";
|
|
33
34
|
export type { PullChunk, OllamaPullOptions, OllamaPullResult, OllamaShowInfo, } from "./ollama-chat.js";
|
package/dist/index.js
CHANGED
|
@@ -19,4 +19,5 @@ export { showModel } from "./show.js";
|
|
|
19
19
|
export { removeModel } from "./rm.js";
|
|
20
20
|
export { listLoaded } from "./ps.js";
|
|
21
21
|
export { looksTuned } from "./tuned.js";
|
|
22
|
+
export { resolveTarget, listTargets } from "./target.js";
|
|
22
23
|
export { ollamaChat, ollamaTags, ollamaShow, ollamaPs, ollamaPsRequired, ollamaPull, ollamaDelete, } from "./ollama-chat.js";
|
package/dist/mcp.d.ts
CHANGED
package/dist/mcp.js
CHANGED
|
@@ -21,6 +21,7 @@ import { removeModel } from "./rm.js";
|
|
|
21
21
|
import { lastScan } from "./scan-store.js";
|
|
22
22
|
import { scanNetwork } from "./scan.js";
|
|
23
23
|
import { showModel } from "./show.js";
|
|
24
|
+
import { isHelpFlag, printHelp } from "./argv.js";
|
|
24
25
|
// Pin to the oldest widely-supported MCP revision. Clients may request a newer
|
|
25
26
|
// date; we still answer with this — legal negotiation, max compatibility.
|
|
26
27
|
const PROTOCOL_VERSION = "2024-11-05";
|
|
@@ -487,7 +488,29 @@ async function handleRequest(msg, version) {
|
|
|
487
488
|
fail(id, -32601, `Method not found: ${method}`);
|
|
488
489
|
}
|
|
489
490
|
}
|
|
491
|
+
export function helpText() {
|
|
492
|
+
return `Usage:
|
|
493
|
+
ollanet mcp
|
|
494
|
+
|
|
495
|
+
Stdio MCP server (no extra npm deps). stdout is JSON-RPC; logs go to stderr.
|
|
496
|
+
|
|
497
|
+
Tools:
|
|
498
|
+
ollanet_scan Discover hosts + models (lan, all, last)
|
|
499
|
+
ollanet_prompt Prompt a host or continue with chat_id
|
|
500
|
+
ollanet_compare Same prompt on 2–5 models
|
|
501
|
+
ollanet_pull Pull / update a model on a host
|
|
502
|
+
ollanet_show Inspect a model
|
|
503
|
+
ollanet_rm Delete a model (confirm: true)
|
|
504
|
+
ollanet_ps Models loaded in VRAM
|
|
505
|
+
ollanet_list_chats Saved transcripts
|
|
506
|
+
ollanet_get_chat One chat by id
|
|
507
|
+
|
|
508
|
+
Example Cursor config: { "command": "npx", "args": ["-y", "ollanet", "mcp"] }`;
|
|
509
|
+
}
|
|
490
510
|
export async function main() {
|
|
511
|
+
if (process.argv.slice(2).some((arg) => isHelpFlag(arg))) {
|
|
512
|
+
printHelp(helpText());
|
|
513
|
+
}
|
|
491
514
|
const version = await packageVersion();
|
|
492
515
|
console.error(`ollanet mcp ${version} (stdio) — tools: ${TOOLS.map((t) => t.name).join(", ")}`);
|
|
493
516
|
const rl = createInterface({ input: process.stdin, crlfDelay: Infinity });
|
package/dist/prompt.d.ts
CHANGED
package/dist/prompt.js
CHANGED
|
@@ -14,15 +14,17 @@
|
|
|
14
14
|
import { stdin as stdinStream } from "node:process";
|
|
15
15
|
import { appendMessage, cleanTopic, createChat, loadChat, saveChat, toApiMessages, topicFromPrompt, } from "./chat-store.js";
|
|
16
16
|
import { configPath, defaultModelForHost, loadConfig, machineSettingsForHost, mergeSettings, parseFormat, settingsFromEnv, settingsSummary, configFromPartial, } from "./config.js";
|
|
17
|
-
import {
|
|
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";
|
|
20
|
+
import { consumeSettingsFlag, failUsage, isHelpFlag, printHelp, takeFlag, takeValue } from "./argv.js";
|
|
21
|
+
import { listTargets, resolveTarget } from "./target.js";
|
|
20
22
|
/** Prompt/chat HTTP timeout (ms). 0 = no timeout. Separate from scan's OLLAMA_TIMEOUT_MS. */
|
|
21
23
|
function promptTimeoutMs() {
|
|
22
24
|
return envInt("OLLAMA_PROMPT_TIMEOUT_MS", 600_000);
|
|
23
25
|
}
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
+
export function helpText() {
|
|
27
|
+
return `Usage:
|
|
26
28
|
ollanet prompt <machine> [model] <prompt...>
|
|
27
29
|
ollanet prompt --chat <hash> <prompt...>
|
|
28
30
|
ollanet prompt <machine> --chat <hash> <prompt...>
|
|
@@ -55,8 +57,10 @@ Options:
|
|
|
55
57
|
--no-think Disable thinking (default) so tokens go to the reply
|
|
56
58
|
--file <path> Prompt from a .txt or .md file (joins argv / stdin)
|
|
57
59
|
--no-stream Wait for the full response
|
|
58
|
-
--json Emit final response JSON (implies --no-stream)
|
|
59
|
-
|
|
60
|
+
--json Emit final response JSON (implies --no-stream)`;
|
|
61
|
+
}
|
|
62
|
+
function usage() {
|
|
63
|
+
failUsage(helpText());
|
|
60
64
|
}
|
|
61
65
|
/**
|
|
62
66
|
* Heuristic for an optional positional model token.
|
|
@@ -119,22 +123,6 @@ async function maybePeelModel(host, promptParts, fromStdin) {
|
|
|
119
123
|
}
|
|
120
124
|
return { promptParts };
|
|
121
125
|
}
|
|
122
|
-
function takeValue(args, flag) {
|
|
123
|
-
const value = args.shift();
|
|
124
|
-
if (!value) {
|
|
125
|
-
console.error(`${flag} requires a value`);
|
|
126
|
-
usage();
|
|
127
|
-
}
|
|
128
|
-
return value;
|
|
129
|
-
}
|
|
130
|
-
function parseNumberFlag(raw, flag) {
|
|
131
|
-
const n = Number(raw);
|
|
132
|
-
if (!Number.isFinite(n)) {
|
|
133
|
-
console.error(`${flag} must be a number (got "${raw}")`);
|
|
134
|
-
usage();
|
|
135
|
-
}
|
|
136
|
-
return n;
|
|
137
|
-
}
|
|
138
126
|
function parseArgs(argv) {
|
|
139
127
|
const args = [...argv];
|
|
140
128
|
let machineFlag;
|
|
@@ -150,8 +138,8 @@ function parseArgs(argv) {
|
|
|
150
138
|
const arg = args.shift();
|
|
151
139
|
if (arg === "--")
|
|
152
140
|
continue;
|
|
153
|
-
if (arg
|
|
154
|
-
|
|
141
|
+
if (isHelpFlag(arg))
|
|
142
|
+
printHelp(helpText());
|
|
155
143
|
if (arg === "--no-stream") {
|
|
156
144
|
stream = false;
|
|
157
145
|
continue;
|
|
@@ -165,59 +153,34 @@ function parseArgs(argv) {
|
|
|
165
153
|
save = false;
|
|
166
154
|
continue;
|
|
167
155
|
}
|
|
168
|
-
if (arg
|
|
169
|
-
|
|
156
|
+
if (consumeSettingsFlag(arg, args, settings, usage, {
|
|
157
|
+
temperature: true,
|
|
158
|
+
numPredict: true,
|
|
159
|
+
system: true,
|
|
160
|
+
})) {
|
|
170
161
|
continue;
|
|
171
162
|
}
|
|
172
|
-
|
|
173
|
-
|
|
163
|
+
const chat = takeFlag(arg, "--chat", args, usage);
|
|
164
|
+
if (chat !== undefined) {
|
|
165
|
+
chatId = chat;
|
|
174
166
|
continue;
|
|
175
167
|
}
|
|
176
|
-
|
|
177
|
-
|
|
168
|
+
const machine = takeFlag(arg, "--machine", args, usage);
|
|
169
|
+
if (machine !== undefined) {
|
|
170
|
+
machineFlag = machine;
|
|
178
171
|
continue;
|
|
179
172
|
}
|
|
180
|
-
|
|
181
|
-
|
|
173
|
+
const model = takeFlag(arg, "--model", args, usage);
|
|
174
|
+
if (model !== undefined) {
|
|
175
|
+
modelFlag = model;
|
|
182
176
|
continue;
|
|
183
177
|
}
|
|
184
|
-
if (arg === "--
|
|
185
|
-
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
if (arg === "--file" || arg === "-f" || arg.startsWith("--file=")) {
|
|
189
|
-
file = arg.includes("=") ? arg.slice("--file=".length) : takeValue(args, "--file");
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (arg === "--system" || arg.startsWith("--system=")) {
|
|
193
|
-
settings.system = arg.includes("=")
|
|
194
|
-
? arg.slice("--system=".length)
|
|
195
|
-
: takeValue(args, "--system");
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
if (arg === "--temperature" || arg === "-t" || arg.startsWith("--temperature=")) {
|
|
199
|
-
const raw = arg.includes("=") ? arg.slice("--temperature=".length) : takeValue(args, "--temperature");
|
|
200
|
-
settings.temperature = parseNumberFlag(raw, "--temperature");
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
if (arg === "--num-predict" || arg.startsWith("--num-predict=")) {
|
|
204
|
-
const raw = arg.includes("=") ? arg.slice("--num-predict=".length) : takeValue(args, "--num-predict");
|
|
205
|
-
settings.num_predict = Math.trunc(parseNumberFlag(raw, "--num-predict"));
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
if (arg === "--num-ctx" || arg.startsWith("--num-ctx=")) {
|
|
209
|
-
const raw = arg.includes("=") ? arg.slice("--num-ctx=".length) : takeValue(args, "--num-ctx");
|
|
210
|
-
settings.num_ctx = Math.trunc(parseNumberFlag(raw, "--num-ctx"));
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
if (arg === "--keep-alive" || arg.startsWith("--keep-alive=")) {
|
|
214
|
-
const raw = arg.includes("=") ? arg.slice("--keep-alive=".length) : takeValue(args, "--keep-alive");
|
|
215
|
-
const asNum = Number(raw);
|
|
216
|
-
settings.keep_alive = raw.trim() !== "" && Number.isFinite(asNum) && String(asNum) === raw ? asNum : raw;
|
|
178
|
+
if (arg === "-f" || arg === "--file" || arg.startsWith("--file=")) {
|
|
179
|
+
file = arg === "-f" ? takeValue(args, "--file", usage) : takeFlag(arg, "--file", args, usage);
|
|
217
180
|
continue;
|
|
218
181
|
}
|
|
219
182
|
if (arg === "--format" || arg.startsWith("--format=")) {
|
|
220
|
-
const raw = arg
|
|
183
|
+
const raw = takeFlag(arg, "--format", args, usage) ?? "";
|
|
221
184
|
const format = parseFormat(raw);
|
|
222
185
|
if (format !== undefined)
|
|
223
186
|
settings.format = format;
|
|
@@ -325,10 +288,6 @@ export async function runPrompt(opts) {
|
|
|
325
288
|
const writeStdout = opts.writeStdout !== false;
|
|
326
289
|
let promptParts = opts.prompt ? [opts.prompt] : [];
|
|
327
290
|
const config = opts.config ? configFromPartial(opts.config) : await loadConfig();
|
|
328
|
-
const { hosts: targets } = await discoverHosts({
|
|
329
|
-
hosts: config.hosts,
|
|
330
|
-
discovery: config.discovery,
|
|
331
|
-
});
|
|
332
291
|
let chat;
|
|
333
292
|
let isNewChat = false;
|
|
334
293
|
let machineQuery = opts.machine;
|
|
@@ -339,10 +298,7 @@ export async function runPrompt(opts) {
|
|
|
339
298
|
if (!machineQuery) {
|
|
340
299
|
throw new Error("Machine is required for a new chat (or pass chatId).");
|
|
341
300
|
}
|
|
342
|
-
const host =
|
|
343
|
-
if (!host.online && !host.isSelf && host.source === "tailscale") {
|
|
344
|
-
throw new Error(`Machine "${shortName(host)}" appears offline.`);
|
|
345
|
-
}
|
|
301
|
+
const host = await resolveTarget(machineQuery, config);
|
|
346
302
|
let peeledModel;
|
|
347
303
|
if (!opts.chatId && !opts.model) {
|
|
348
304
|
const peeled = await maybePeelModel(host, promptParts, "");
|
|
@@ -456,10 +412,7 @@ export async function main() {
|
|
|
456
412
|
}
|
|
457
413
|
}
|
|
458
414
|
const config = await loadConfig();
|
|
459
|
-
const
|
|
460
|
-
hosts: config.hosts,
|
|
461
|
-
discovery: config.discovery,
|
|
462
|
-
});
|
|
415
|
+
const targets = await listTargets(config);
|
|
463
416
|
let machineQuery = parsed.machine;
|
|
464
417
|
if (parsed.chatId) {
|
|
465
418
|
// Allow `ollanet prompt <host> --chat HASH "follow-up"` only when the
|
package/dist/ps.d.ts
CHANGED
package/dist/ps.js
CHANGED
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
import { configFromPartial, loadConfig } from "./config.js";
|
|
8
8
|
import { discoverHosts, envInt, ollamaBaseUrl, shortName, } from "./hosts.js";
|
|
9
9
|
import { ollamaPs, ollamaPsRequired } from "./ollama-chat.js";
|
|
10
|
+
import { failUsage, isHelpFlag, printHelp, takeFlag } from "./argv.js";
|
|
10
11
|
import { resolveTarget } from "./target.js";
|
|
11
12
|
import { looksTuned } from "./tuned.js";
|
|
12
13
|
function psTimeoutMs() {
|
|
13
14
|
return envInt("OLLAMA_PS_TIMEOUT_MS", 10_000);
|
|
14
15
|
}
|
|
15
|
-
function
|
|
16
|
-
|
|
16
|
+
export function helpText() {
|
|
17
|
+
return `Usage:
|
|
17
18
|
ollanet ps [machine]
|
|
18
19
|
ollanet ps --machine <name>
|
|
19
20
|
|
|
@@ -26,16 +27,10 @@ scan lists models on disk. ps lists what is loaded in VRAM right now.
|
|
|
26
27
|
|
|
27
28
|
Options:
|
|
28
29
|
--machine <name> One host (default: every discovered host)
|
|
29
|
-
--json Emit result JSON on stdout
|
|
30
|
-
process.exit(1);
|
|
30
|
+
--json Emit result JSON on stdout`;
|
|
31
31
|
}
|
|
32
|
-
function
|
|
33
|
-
|
|
34
|
-
if (!value) {
|
|
35
|
-
console.error(`${flag} requires a value`);
|
|
36
|
-
usage();
|
|
37
|
-
}
|
|
38
|
-
return value;
|
|
32
|
+
function usage() {
|
|
33
|
+
failUsage(helpText());
|
|
39
34
|
}
|
|
40
35
|
function parseArgs(argv) {
|
|
41
36
|
const args = [...argv];
|
|
@@ -46,14 +41,15 @@ function parseArgs(argv) {
|
|
|
46
41
|
const arg = args.shift();
|
|
47
42
|
if (arg === "--")
|
|
48
43
|
continue;
|
|
49
|
-
if (arg
|
|
50
|
-
|
|
44
|
+
if (isHelpFlag(arg))
|
|
45
|
+
printHelp(helpText());
|
|
51
46
|
if (arg === "--json") {
|
|
52
47
|
json = true;
|
|
53
48
|
continue;
|
|
54
49
|
}
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
const machine = takeFlag(arg, "--machine", args, usage);
|
|
51
|
+
if (machine !== undefined) {
|
|
52
|
+
machineFlag = machine;
|
|
57
53
|
continue;
|
|
58
54
|
}
|
|
59
55
|
if (arg.startsWith("-")) {
|
package/dist/pull.d.ts
CHANGED
package/dist/pull.js
CHANGED
|
@@ -8,13 +8,14 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { envInt, ollamaBaseUrl, shortName } from "./hosts.js";
|
|
10
10
|
import { ollamaPull } from "./ollama-chat.js";
|
|
11
|
+
import { failUsage, isHelpFlag, printHelp, takeFlag } from "./argv.js";
|
|
11
12
|
import { resolveTarget } from "./target.js";
|
|
12
13
|
/** Pull HTTP timeout (ms). 0 = none. Large models can take hours. */
|
|
13
14
|
function pullTimeoutMs() {
|
|
14
15
|
return envInt("OLLAMA_PULL_TIMEOUT_MS", 0);
|
|
15
16
|
}
|
|
16
|
-
function
|
|
17
|
-
|
|
17
|
+
export function helpText() {
|
|
18
|
+
return `Usage:
|
|
18
19
|
ollanet pull <machine> <model>
|
|
19
20
|
ollanet pull --machine <name> --model <name>
|
|
20
21
|
|
|
@@ -32,16 +33,10 @@ Options:
|
|
|
32
33
|
--model <name> Model to pull (e.g. gemma3:12b)
|
|
33
34
|
--insecure Allow HTTP / self-signed model registries
|
|
34
35
|
--no-stream Wait for a single final response (no progress chunks)
|
|
35
|
-
--json Emit result JSON on stdout
|
|
36
|
-
process.exit(1);
|
|
36
|
+
--json Emit result JSON on stdout`;
|
|
37
37
|
}
|
|
38
|
-
function
|
|
39
|
-
|
|
40
|
-
if (!value) {
|
|
41
|
-
console.error(`${flag} requires a value`);
|
|
42
|
-
usage();
|
|
43
|
-
}
|
|
44
|
-
return value;
|
|
38
|
+
function usage() {
|
|
39
|
+
failUsage(helpText());
|
|
45
40
|
}
|
|
46
41
|
function parseArgs(argv) {
|
|
47
42
|
const args = [...argv];
|
|
@@ -55,8 +50,8 @@ function parseArgs(argv) {
|
|
|
55
50
|
const arg = args.shift();
|
|
56
51
|
if (arg === "--")
|
|
57
52
|
continue;
|
|
58
|
-
if (arg
|
|
59
|
-
|
|
53
|
+
if (isHelpFlag(arg))
|
|
54
|
+
printHelp(helpText());
|
|
60
55
|
if (arg === "--insecure") {
|
|
61
56
|
insecure = true;
|
|
62
57
|
continue;
|
|
@@ -69,12 +64,14 @@ function parseArgs(argv) {
|
|
|
69
64
|
json = true;
|
|
70
65
|
continue;
|
|
71
66
|
}
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
const machine = takeFlag(arg, "--machine", args, usage);
|
|
68
|
+
if (machine !== undefined) {
|
|
69
|
+
machineFlag = machine;
|
|
74
70
|
continue;
|
|
75
71
|
}
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
const model = takeFlag(arg, "--model", args, usage);
|
|
73
|
+
if (model !== undefined) {
|
|
74
|
+
modelFlag = model;
|
|
78
75
|
continue;
|
|
79
76
|
}
|
|
80
77
|
if (arg.startsWith("-")) {
|
package/dist/rm.d.ts
CHANGED
package/dist/rm.js
CHANGED
|
@@ -6,12 +6,13 @@
|
|
|
6
6
|
import { createInterface } from "node:readline";
|
|
7
7
|
import { envInt, ollamaBaseUrl, shortName } from "./hosts.js";
|
|
8
8
|
import { ollamaDelete } from "./ollama-chat.js";
|
|
9
|
+
import { failUsage, isHelpFlag, printHelp, takeFlag } from "./argv.js";
|
|
9
10
|
import { resolveTarget } from "./target.js";
|
|
10
11
|
function rmTimeoutMs() {
|
|
11
12
|
return envInt("OLLAMA_RM_TIMEOUT_MS", 30_000);
|
|
12
13
|
}
|
|
13
|
-
function
|
|
14
|
-
|
|
14
|
+
export function helpText() {
|
|
15
|
+
return `Usage:
|
|
15
16
|
ollanet rm <machine> <model> --yes
|
|
16
17
|
ollanet rm --machine <name> --model <name> --yes
|
|
17
18
|
|
|
@@ -26,16 +27,10 @@ Options:
|
|
|
26
27
|
--machine <name> Host (discovered name, MagicDNS, hostname, or IP[:port])
|
|
27
28
|
--model <name> Model to delete
|
|
28
29
|
--yes Required unless stdin is a TTY and you confirm
|
|
29
|
-
--json Emit result JSON on stdout
|
|
30
|
-
process.exit(1);
|
|
30
|
+
--json Emit result JSON on stdout`;
|
|
31
31
|
}
|
|
32
|
-
function
|
|
33
|
-
|
|
34
|
-
if (!value) {
|
|
35
|
-
console.error(`${flag} requires a value`);
|
|
36
|
-
usage();
|
|
37
|
-
}
|
|
38
|
-
return value;
|
|
32
|
+
function usage() {
|
|
33
|
+
failUsage(helpText());
|
|
39
34
|
}
|
|
40
35
|
function parseArgs(argv) {
|
|
41
36
|
const args = [...argv];
|
|
@@ -48,8 +43,8 @@ function parseArgs(argv) {
|
|
|
48
43
|
const arg = args.shift();
|
|
49
44
|
if (arg === "--")
|
|
50
45
|
continue;
|
|
51
|
-
if (arg
|
|
52
|
-
|
|
46
|
+
if (isHelpFlag(arg))
|
|
47
|
+
printHelp(helpText());
|
|
53
48
|
if (arg === "--yes" || arg === "-y") {
|
|
54
49
|
yes = true;
|
|
55
50
|
continue;
|
|
@@ -58,12 +53,14 @@ function parseArgs(argv) {
|
|
|
58
53
|
json = true;
|
|
59
54
|
continue;
|
|
60
55
|
}
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
const machine = takeFlag(arg, "--machine", args, usage);
|
|
57
|
+
if (machine !== undefined) {
|
|
58
|
+
machineFlag = machine;
|
|
63
59
|
continue;
|
|
64
60
|
}
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
const model = takeFlag(arg, "--model", args, usage);
|
|
62
|
+
if (model !== undefined) {
|
|
63
|
+
modelFlag = model;
|
|
67
64
|
continue;
|
|
68
65
|
}
|
|
69
66
|
if (arg.startsWith("-")) {
|
package/dist/scan.d.ts
CHANGED
|
@@ -54,4 +54,5 @@ export interface ScanPayload {
|
|
|
54
54
|
}
|
|
55
55
|
/** Programmatic scan used by CLI `--json`, MCP, and `import { scanNetwork } from "ollanet"`. */
|
|
56
56
|
export declare function scanNetwork(options?: ScanOptions): Promise<ScanPayload>;
|
|
57
|
+
export declare function helpText(): string;
|
|
57
58
|
export declare function main(): Promise<void>;
|
package/dist/scan.js
CHANGED
|
@@ -7,6 +7,7 @@ import { configFromPartial, loadConfig } from "./config.js";
|
|
|
7
7
|
import { discoverHosts, envInt, ollamaPort, shortName, } from "./hosts.js";
|
|
8
8
|
import { lastScanPath, loadLastScan, saveLastScan } from "./scan-store.js";
|
|
9
9
|
import { looksTuned } from "./tuned.js";
|
|
10
|
+
import { isHelpFlag, printHelp } from "./argv.js";
|
|
10
11
|
function scanTimeoutMs() {
|
|
11
12
|
return envInt("OLLAMA_TIMEOUT_MS", 2500);
|
|
12
13
|
}
|
|
@@ -184,7 +185,30 @@ export async function scanNetwork(options = {}) {
|
|
|
184
185
|
}
|
|
185
186
|
return payload;
|
|
186
187
|
}
|
|
188
|
+
export function helpText() {
|
|
189
|
+
return `Usage:
|
|
190
|
+
ollanet scan [--json] [--all] [--lan] [--last]
|
|
191
|
+
|
|
192
|
+
Examples:
|
|
193
|
+
ollanet scan
|
|
194
|
+
ollanet scan --lan
|
|
195
|
+
ollanet scan --last
|
|
196
|
+
ollanet scan --json
|
|
197
|
+
|
|
198
|
+
Discovers Ollama hosts (localhost, config, OLLANET_HOSTS, Tailscale) and lists
|
|
199
|
+
models. --lan is an opt-in TCP sweep of local /24s on port 11434.
|
|
200
|
+
|
|
201
|
+
Options:
|
|
202
|
+
--json Machine-readable output
|
|
203
|
+
--all Also probe offline Tailscale peers
|
|
204
|
+
--lan TCP-scan local LAN CIDRs for open Ollama ports
|
|
205
|
+
--last Reprint the last saved scan (no network)
|
|
206
|
+
--cached Same as --last`;
|
|
207
|
+
}
|
|
187
208
|
export async function main() {
|
|
209
|
+
if (process.argv.slice(2).some((arg) => isHelpFlag(arg))) {
|
|
210
|
+
printHelp(helpText());
|
|
211
|
+
}
|
|
188
212
|
const includeOffline = process.argv.includes("--all");
|
|
189
213
|
const jsonOut = process.argv.includes("--json");
|
|
190
214
|
const lanScan = process.argv.includes("--lan");
|
package/dist/show.d.ts
CHANGED
package/dist/show.js
CHANGED
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { envInt, ollamaBaseUrl, shortName } from "./hosts.js";
|
|
7
7
|
import { ollamaShow } from "./ollama-chat.js";
|
|
8
|
+
import { failUsage, isHelpFlag, printHelp, takeFlag } from "./argv.js";
|
|
8
9
|
import { resolveTarget } from "./target.js";
|
|
9
10
|
import { looksTuned } from "./tuned.js";
|
|
10
11
|
function showTimeoutMs() {
|
|
11
12
|
return envInt("OLLAMA_SHOW_TIMEOUT_MS", 30_000);
|
|
12
13
|
}
|
|
13
|
-
function
|
|
14
|
-
|
|
14
|
+
export function helpText() {
|
|
15
|
+
return `Usage:
|
|
15
16
|
ollanet show <machine> <model>
|
|
16
17
|
ollanet show --machine <name> --model <name>
|
|
17
18
|
|
|
@@ -25,16 +26,10 @@ Tuned Finetuna-style names are marked [tuned].
|
|
|
25
26
|
Options:
|
|
26
27
|
--machine <name> Host (discovered name, MagicDNS, hostname, or IP[:port])
|
|
27
28
|
--model <name> Model to inspect
|
|
28
|
-
--json Emit result JSON on stdout
|
|
29
|
-
process.exit(1);
|
|
29
|
+
--json Emit result JSON on stdout`;
|
|
30
30
|
}
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
if (!value) {
|
|
34
|
-
console.error(`${flag} requires a value`);
|
|
35
|
-
usage();
|
|
36
|
-
}
|
|
37
|
-
return value;
|
|
31
|
+
function usage() {
|
|
32
|
+
failUsage(helpText());
|
|
38
33
|
}
|
|
39
34
|
function parseArgs(argv) {
|
|
40
35
|
const args = [...argv];
|
|
@@ -46,18 +41,20 @@ function parseArgs(argv) {
|
|
|
46
41
|
const arg = args.shift();
|
|
47
42
|
if (arg === "--")
|
|
48
43
|
continue;
|
|
49
|
-
if (arg
|
|
50
|
-
|
|
44
|
+
if (isHelpFlag(arg))
|
|
45
|
+
printHelp(helpText());
|
|
51
46
|
if (arg === "--json") {
|
|
52
47
|
json = true;
|
|
53
48
|
continue;
|
|
54
49
|
}
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
const machine = takeFlag(arg, "--machine", args, usage);
|
|
51
|
+
if (machine !== undefined) {
|
|
52
|
+
machineFlag = machine;
|
|
57
53
|
continue;
|
|
58
54
|
}
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
const model = takeFlag(arg, "--model", args, usage);
|
|
56
|
+
if (model !== undefined) {
|
|
57
|
+
modelFlag = model;
|
|
61
58
|
continue;
|
|
62
59
|
}
|
|
63
60
|
if (arg.startsWith("-")) {
|
package/dist/target.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type AppConfig } from "./config.js";
|
|
2
2
|
import { type HostTarget } from "./hosts.js";
|
|
3
|
+
/** Discover hosts the same way every command does (config + Tailscale + …). */
|
|
4
|
+
export declare function listTargets(config?: Partial<AppConfig>): Promise<HostTarget[]>;
|
|
3
5
|
/** Resolve a machine name the same way pull / prompt / bench do. */
|
|
4
6
|
export declare function resolveTarget(machine: string, config?: Partial<AppConfig>): Promise<HostTarget>;
|
package/dist/target.js
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { configFromPartial, loadConfig } from "./config.js";
|
|
2
2
|
import { discoverHosts, resolveHost, shortName } from "./hosts.js";
|
|
3
|
-
/**
|
|
4
|
-
export async function
|
|
5
|
-
const query = machine.trim();
|
|
6
|
-
if (!query)
|
|
7
|
-
throw new Error("Machine is required.");
|
|
3
|
+
/** Discover hosts the same way every command does (config + Tailscale + …). */
|
|
4
|
+
export async function listTargets(config) {
|
|
8
5
|
const app = config ? configFromPartial(config) : await loadConfig();
|
|
9
6
|
const { hosts } = await discoverHosts({
|
|
10
7
|
hosts: app.hosts,
|
|
11
8
|
discovery: app.discovery,
|
|
12
9
|
});
|
|
10
|
+
return hosts;
|
|
11
|
+
}
|
|
12
|
+
/** Resolve a machine name the same way pull / prompt / bench do. */
|
|
13
|
+
export async function resolveTarget(machine, config) {
|
|
14
|
+
const query = machine.trim();
|
|
15
|
+
if (!query)
|
|
16
|
+
throw new Error("Machine is required.");
|
|
17
|
+
const hosts = await listTargets(config);
|
|
13
18
|
const host = resolveHost(hosts, query);
|
|
14
19
|
if (!host.online && !host.isSelf && host.source === "tailscale") {
|
|
15
20
|
throw new Error(`Machine "${shortName(host)}" appears offline.`);
|