ollanet 0.6.0 → 0.6.1

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 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
package/dist/argv.d.ts ADDED
@@ -0,0 +1,17 @@
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 takeValue(args: string[], flag: string, usage: UsageFn): string;
7
+ /** `--name value` or `--name=value`. Returns undefined if `arg` is not this flag. */
8
+ export declare function takeFlag(arg: string, name: string, args: string[], usage: UsageFn): string | undefined;
9
+ export declare function parseNumberFlag(raw: string, flag: string, usage: UsageFn): number;
10
+ export declare function parseKeepAlive(raw: string): string | number;
11
+ export interface SettingsFlagOpts {
12
+ temperature?: boolean;
13
+ numPredict?: boolean;
14
+ system?: boolean;
15
+ }
16
+ /** Shared generate-settings flags. Returns true when `arg` was consumed. */
17
+ export declare function consumeSettingsFlag(arg: string, args: string[], settings: GenerateSettings, usage: UsageFn, opts?: SettingsFlagOpts): boolean;
package/dist/argv.js ADDED
@@ -0,0 +1,73 @@
1
+ export function takeValue(args, flag, usage) {
2
+ const value = args.shift();
3
+ if (!value) {
4
+ console.error(`${flag} requires a value`);
5
+ usage();
6
+ }
7
+ return value;
8
+ }
9
+ /** `--name value` or `--name=value`. Returns undefined if `arg` is not this flag. */
10
+ export function takeFlag(arg, name, args, usage) {
11
+ if (arg === name)
12
+ return takeValue(args, name, usage);
13
+ if (arg.startsWith(`${name}=`))
14
+ return arg.slice(name.length + 1);
15
+ return undefined;
16
+ }
17
+ export function parseNumberFlag(raw, flag, usage) {
18
+ const n = Number(raw);
19
+ if (!Number.isFinite(n)) {
20
+ console.error(`${flag} must be a number (got "${raw}")`);
21
+ usage();
22
+ }
23
+ return n;
24
+ }
25
+ export function parseKeepAlive(raw) {
26
+ const asNum = Number(raw);
27
+ return raw.trim() !== "" && Number.isFinite(asNum) && String(asNum) === raw ? asNum : raw;
28
+ }
29
+ /** Shared generate-settings flags. Returns true when `arg` was consumed. */
30
+ export function consumeSettingsFlag(arg, args, settings, usage, opts = {}) {
31
+ if (arg === "--think") {
32
+ settings.think = true;
33
+ return true;
34
+ }
35
+ if (arg === "--no-think") {
36
+ settings.think = false;
37
+ return true;
38
+ }
39
+ if (opts.system) {
40
+ const system = takeFlag(arg, "--system", args, usage);
41
+ if (system !== undefined) {
42
+ settings.system = system;
43
+ return true;
44
+ }
45
+ }
46
+ if (opts.temperature) {
47
+ if (arg === "-t" || arg === "--temperature" || arg.startsWith("--temperature=")) {
48
+ const raw = arg === "-t" || arg === "--temperature"
49
+ ? takeValue(args, "--temperature", usage)
50
+ : arg.slice("--temperature=".length);
51
+ settings.temperature = parseNumberFlag(raw, "--temperature", usage);
52
+ return true;
53
+ }
54
+ }
55
+ if (opts.numPredict) {
56
+ const raw = takeFlag(arg, "--num-predict", args, usage);
57
+ if (raw !== undefined) {
58
+ settings.num_predict = Math.trunc(parseNumberFlag(raw, "--num-predict", usage));
59
+ return true;
60
+ }
61
+ }
62
+ const numCtx = takeFlag(arg, "--num-ctx", args, usage);
63
+ if (numCtx !== undefined) {
64
+ settings.num_ctx = Math.trunc(parseNumberFlag(numCtx, "--num-ctx", usage));
65
+ return true;
66
+ }
67
+ const keepAlive = takeFlag(arg, "--keep-alive", args, usage);
68
+ if (keepAlive !== undefined) {
69
+ settings.keep_alive = parseKeepAlive(keepAlive);
70
+ return true;
71
+ }
72
+ return false;
73
+ }
package/dist/bench.js CHANGED
@@ -5,7 +5,9 @@ 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 { discoverHosts, envInt, ollamaBaseUrl, resolveHost, shortName, } from "./hosts.js";
8
+ import { envInt, ollamaBaseUrl, shortName } from "./hosts.js";
9
+ import { consumeSettingsFlag, parseNumberFlag, 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;
@@ -41,22 +43,6 @@ Options:
41
43
  See docs/bench-spec.md for measurement rules.`);
42
44
  process.exit(1);
43
45
  }
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;
51
- }
52
- function parseNumberFlag(raw, flag) {
53
- const n = Number(raw);
54
- if (!Number.isFinite(n)) {
55
- console.error(`${flag} must be a number (got "${raw}")`);
56
- usage();
57
- }
58
- return n;
59
- }
60
46
  function isLoopback(host) {
61
47
  return (host.source === "localhost" ||
62
48
  host.ip === "127.0.0.1" ||
@@ -157,57 +143,35 @@ function parseArgs(argv) {
157
143
  failOnError = true;
158
144
  continue;
159
145
  }
160
- if (arg === "--think") {
161
- settings.think = true;
162
- continue;
163
- }
164
- if (arg === "--no-think") {
165
- settings.think = false;
166
- continue;
167
- }
168
146
  if (arg === "--judge") {
169
147
  judge = true;
170
148
  continue;
171
149
  }
172
- if (arg === "--suite" || arg.startsWith("--suite=")) {
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;
179
- continue;
180
- }
181
- if (arg === "--runs" || arg.startsWith("--runs=")) {
182
- const raw = arg.includes("=") ? arg.slice("--runs=".length) : takeValue(args, "--runs");
183
- runs = Math.max(1, Math.trunc(parseNumberFlag(raw, "--runs")));
150
+ if (consumeSettingsFlag(arg, args, settings, usage)) {
184
151
  continue;
185
152
  }
186
- if (arg === "--num-predict" || arg.startsWith("--num-predict=")) {
187
- const raw = arg.includes("=")
188
- ? arg.slice("--num-predict=".length)
189
- : takeValue(args, "--num-predict");
190
- throughputNumPredict = Math.trunc(parseNumberFlag(raw, "--num-predict"));
153
+ const suiteRaw = takeFlag(arg, "--suite", args, usage);
154
+ if (suiteRaw !== undefined) {
155
+ if (suiteRaw !== "quick" && suiteRaw !== "full") {
156
+ console.error(`Unknown suite "${suiteRaw}" (use quick|full)`);
157
+ usage();
158
+ }
159
+ suite = suiteRaw;
191
160
  continue;
192
161
  }
193
- if (arg === "--num-ctx" || arg.startsWith("--num-ctx=")) {
194
- const raw = arg.includes("=") ? arg.slice("--num-ctx=".length) : takeValue(args, "--num-ctx");
195
- settings.num_ctx = Math.trunc(parseNumberFlag(raw, "--num-ctx"));
162
+ const runsRaw = takeFlag(arg, "--runs", args, usage);
163
+ if (runsRaw !== undefined) {
164
+ runs = Math.max(1, Math.trunc(parseNumberFlag(runsRaw, "--runs", usage)));
196
165
  continue;
197
166
  }
198
- if (arg === "--keep-alive" || arg.startsWith("--keep-alive=")) {
199
- const raw = arg.includes("=")
200
- ? arg.slice("--keep-alive=".length)
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;
167
+ const predictRaw = takeFlag(arg, "--num-predict", args, usage);
168
+ if (predictRaw !== undefined) {
169
+ throughputNumPredict = Math.trunc(parseNumberFlag(predictRaw, "--num-predict", usage));
205
170
  continue;
206
171
  }
207
- if (arg === "--judge-model" || arg.startsWith("--judge-model=")) {
208
- judgeModel = arg.includes("=")
209
- ? arg.slice("--judge-model=".length)
210
- : takeValue(args, "--judge-model");
172
+ const judgeRaw = takeFlag(arg, "--judge-model", args, usage);
173
+ if (judgeRaw !== undefined) {
174
+ judgeModel = judgeRaw;
211
175
  continue;
212
176
  }
213
177
  if (arg.startsWith("-")) {
@@ -527,11 +491,7 @@ function pad(s, n) {
527
491
  export async function main() {
528
492
  const parsed = parseArgs(process.argv.slice(2));
529
493
  const config = await loadConfig();
530
- const { hosts: targets } = await discoverHosts({
531
- hosts: config.hosts,
532
- discovery: config.discovery,
533
- });
534
- const host = resolveHost(targets, parsed.machine);
494
+ const host = await resolveTarget(parsed.machine, config);
535
495
  const baseUrl = ollamaBaseUrl(host);
536
496
  const machineLabel = shortName(host);
537
497
  const tags = await ollamaTags(baseUrl, Math.min(BENCH_TIMEOUT_MS, 15_000));
package/dist/chats.js CHANGED
@@ -7,6 +7,7 @@
7
7
  * ollanet chats --id a1b2c3d4e5f6
8
8
  */
9
9
  import { loadChat, listChats, responsesDir, } from "./chat-store.js";
10
+ import { takeFlag } from "./argv.js";
10
11
  function usage() {
11
12
  console.error(`Usage: ollanet chats [--json] [--id <hash>]
12
13
 
@@ -31,12 +32,9 @@ function parseArgs(argv) {
31
32
  json = true;
32
33
  continue;
33
34
  }
34
- if (arg === "--id" || arg.startsWith("--id=")) {
35
- id = arg.includes("=") ? arg.slice("--id=".length) : args.shift();
36
- if (!id) {
37
- console.error("--id requires a value");
38
- usage();
39
- }
35
+ const idFlag = takeFlag(arg, "--id", args, usage);
36
+ if (idFlag !== undefined) {
37
+ id = idFlag;
40
38
  continue;
41
39
  }
42
40
  console.error(`Unknown flag: ${arg}`);
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 { discoverHosts, envInt, ollamaBaseUrl, resolveHost, shortName } from "./hosts.js";
8
+ import { envInt, ollamaBaseUrl, shortName } from "./hosts.js";
9
+ import { consumeSettingsFlag, 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";
@@ -50,22 +52,6 @@ Options:
50
52
  --json Emit the full record on stdout`);
51
53
  process.exit(1);
52
54
  }
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;
60
- }
61
- function parseNumberFlag(raw, flag) {
62
- const n = Number(raw);
63
- if (!Number.isFinite(n)) {
64
- console.error(`${flag} must be a number (got "${raw}")`);
65
- usage();
66
- }
67
- return n;
68
- }
69
55
  function parseArgs(argv) {
70
56
  const args = [...argv];
71
57
  let prompt;
@@ -93,48 +79,19 @@ function parseArgs(argv) {
93
79
  unload = true;
94
80
  continue;
95
81
  }
96
- if (arg === "--think") {
97
- settings.think = true;
98
- continue;
99
- }
100
- if (arg === "--no-think") {
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");
82
+ if (consumeSettingsFlag(arg, args, settings, usage, {
83
+ temperature: true,
84
+ numPredict: true,
85
+ system: true,
86
+ })) {
106
87
  continue;
107
88
  }
108
- if (arg === "--file" || arg === "-f" || arg.startsWith("--file=")) {
109
- file = arg.includes("=") ? arg.slice("--file=".length) : takeValue(args, "--file");
89
+ if (arg === "-p" || arg === "--prompt" || arg.startsWith("--prompt=")) {
90
+ prompt = arg === "-p" ? takeValue(args, "--prompt", usage) : takeFlag(arg, "--prompt", args, usage);
110
91
  continue;
111
92
  }
112
- if (arg === "--system" || arg.startsWith("--system=")) {
113
- settings.system = arg.includes("=")
114
- ? arg.slice("--system=".length)
115
- : takeValue(args, "--system");
116
- continue;
117
- }
118
- if (arg === "--temperature" || arg === "-t" || arg.startsWith("--temperature=")) {
119
- const raw = arg.includes("=") ? arg.slice("--temperature=".length) : takeValue(args, "--temperature");
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;
93
+ if (arg === "-f" || arg === "--file" || arg.startsWith("--file=")) {
94
+ file = arg === "-f" ? takeValue(args, "--file", usage) : takeFlag(arg, "--file", args, usage);
138
95
  continue;
139
96
  }
140
97
  if (arg.startsWith("-")) {
@@ -189,14 +146,7 @@ export async function runCompare(opts) {
189
146
  const defaulted = !assembled;
190
147
  const prompt = assembled || DEFAULT_COMPARE_PROMPT;
191
148
  const config = opts.config ? configFromPartial(opts.config) : await loadConfig();
192
- const { hosts } = await discoverHosts({
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
- }
149
+ const host = await resolveTarget(opts.machine, config);
200
150
  const baseUrl = ollamaBaseUrl(host);
201
151
  const timeoutMs = opts.timeoutMs ?? compareTimeoutMs();
202
152
  let available = null;
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/prompt.js CHANGED
@@ -14,9 +14,11 @@
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 { discoverHosts, envInt, findDiscoveredHost, ollamaBaseUrl, resolveHost, shortName, } from "./hosts.js";
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, 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);
@@ -119,22 +121,6 @@ async function maybePeelModel(host, promptParts, fromStdin) {
119
121
  }
120
122
  return { promptParts };
121
123
  }
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
124
  function parseArgs(argv) {
139
125
  const args = [...argv];
140
126
  let machineFlag;
@@ -165,59 +151,34 @@ function parseArgs(argv) {
165
151
  save = false;
166
152
  continue;
167
153
  }
168
- if (arg === "--think") {
169
- settings.think = true;
154
+ if (consumeSettingsFlag(arg, args, settings, usage, {
155
+ temperature: true,
156
+ numPredict: true,
157
+ system: true,
158
+ })) {
170
159
  continue;
171
160
  }
172
- if (arg === "--no-think") {
173
- settings.think = false;
161
+ const chat = takeFlag(arg, "--chat", args, usage);
162
+ if (chat !== undefined) {
163
+ chatId = chat;
174
164
  continue;
175
165
  }
176
- if (arg === "--chat" || arg.startsWith("--chat=")) {
177
- chatId = arg.includes("=") ? arg.slice("--chat=".length) : takeValue(args, "--chat");
166
+ const machine = takeFlag(arg, "--machine", args, usage);
167
+ if (machine !== undefined) {
168
+ machineFlag = machine;
178
169
  continue;
179
170
  }
180
- if (arg === "--machine" || arg.startsWith("--machine=")) {
181
- machineFlag = arg.includes("=") ? arg.slice("--machine=".length) : takeValue(args, "--machine");
171
+ const model = takeFlag(arg, "--model", args, usage);
172
+ if (model !== undefined) {
173
+ modelFlag = model;
182
174
  continue;
183
175
  }
184
- if (arg === "--model" || arg.startsWith("--model=")) {
185
- modelFlag = arg.includes("=") ? arg.slice("--model=".length) : takeValue(args, "--model");
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;
176
+ if (arg === "-f" || arg === "--file" || arg.startsWith("--file=")) {
177
+ file = arg === "-f" ? takeValue(args, "--file", usage) : takeFlag(arg, "--file", args, usage);
217
178
  continue;
218
179
  }
219
180
  if (arg === "--format" || arg.startsWith("--format=")) {
220
- const raw = arg.includes("=") ? arg.slice("--format=".length) : takeValue(args, "--format");
181
+ const raw = takeFlag(arg, "--format", args, usage) ?? "";
221
182
  const format = parseFormat(raw);
222
183
  if (format !== undefined)
223
184
  settings.format = format;
@@ -325,10 +286,6 @@ export async function runPrompt(opts) {
325
286
  const writeStdout = opts.writeStdout !== false;
326
287
  let promptParts = opts.prompt ? [opts.prompt] : [];
327
288
  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
289
  let chat;
333
290
  let isNewChat = false;
334
291
  let machineQuery = opts.machine;
@@ -339,10 +296,7 @@ export async function runPrompt(opts) {
339
296
  if (!machineQuery) {
340
297
  throw new Error("Machine is required for a new chat (or pass chatId).");
341
298
  }
342
- const host = resolveHost(targets, machineQuery);
343
- if (!host.online && !host.isSelf && host.source === "tailscale") {
344
- throw new Error(`Machine "${shortName(host)}" appears offline.`);
345
- }
299
+ const host = await resolveTarget(machineQuery, config);
346
300
  let peeledModel;
347
301
  if (!opts.chatId && !opts.model) {
348
302
  const peeled = await maybePeelModel(host, promptParts, "");
@@ -456,10 +410,7 @@ export async function main() {
456
410
  }
457
411
  }
458
412
  const config = await loadConfig();
459
- const { hosts: targets } = await discoverHosts({
460
- hosts: config.hosts,
461
- discovery: config.discovery,
462
- });
413
+ const targets = await listTargets(config);
463
414
  let machineQuery = parsed.machine;
464
415
  if (parsed.chatId) {
465
416
  // Allow `ollanet prompt <host> --chat HASH "follow-up"` only when the
package/dist/ps.js CHANGED
@@ -7,6 +7,7 @@
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 { takeFlag } from "./argv.js";
10
11
  import { resolveTarget } from "./target.js";
11
12
  import { looksTuned } from "./tuned.js";
12
13
  function psTimeoutMs() {
@@ -29,14 +30,6 @@ Options:
29
30
  --json Emit result JSON on stdout`);
30
31
  process.exit(1);
31
32
  }
32
- function takeValue(args, flag) {
33
- const value = args.shift();
34
- if (!value) {
35
- console.error(`${flag} requires a value`);
36
- usage();
37
- }
38
- return value;
39
- }
40
33
  function parseArgs(argv) {
41
34
  const args = [...argv];
42
35
  let machineFlag;
@@ -52,8 +45,9 @@ function parseArgs(argv) {
52
45
  json = true;
53
46
  continue;
54
47
  }
55
- if (arg === "--machine") {
56
- machineFlag = takeValue(args, "--machine");
48
+ const machine = takeFlag(arg, "--machine", args, usage);
49
+ if (machine !== undefined) {
50
+ machineFlag = machine;
57
51
  continue;
58
52
  }
59
53
  if (arg.startsWith("-")) {
package/dist/pull.js CHANGED
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { envInt, ollamaBaseUrl, shortName } from "./hosts.js";
10
10
  import { ollamaPull } from "./ollama-chat.js";
11
+ import { 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() {
@@ -35,14 +36,6 @@ Options:
35
36
  --json Emit result JSON on stdout`);
36
37
  process.exit(1);
37
38
  }
38
- function takeValue(args, flag) {
39
- const value = args.shift();
40
- if (!value) {
41
- console.error(`${flag} requires a value`);
42
- usage();
43
- }
44
- return value;
45
- }
46
39
  function parseArgs(argv) {
47
40
  const args = [...argv];
48
41
  let machineFlag;
@@ -69,12 +62,14 @@ function parseArgs(argv) {
69
62
  json = true;
70
63
  continue;
71
64
  }
72
- if (arg === "--machine") {
73
- machineFlag = takeValue(args, "--machine");
65
+ const machine = takeFlag(arg, "--machine", args, usage);
66
+ if (machine !== undefined) {
67
+ machineFlag = machine;
74
68
  continue;
75
69
  }
76
- if (arg === "--model") {
77
- modelFlag = takeValue(args, "--model");
70
+ const model = takeFlag(arg, "--model", args, usage);
71
+ if (model !== undefined) {
72
+ modelFlag = model;
78
73
  continue;
79
74
  }
80
75
  if (arg.startsWith("-")) {
package/dist/rm.js CHANGED
@@ -6,6 +6,7 @@
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 { 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);
@@ -29,14 +30,6 @@ Options:
29
30
  --json Emit result JSON on stdout`);
30
31
  process.exit(1);
31
32
  }
32
- function takeValue(args, flag) {
33
- const value = args.shift();
34
- if (!value) {
35
- console.error(`${flag} requires a value`);
36
- usage();
37
- }
38
- return value;
39
- }
40
33
  function parseArgs(argv) {
41
34
  const args = [...argv];
42
35
  let machineFlag;
@@ -58,12 +51,14 @@ function parseArgs(argv) {
58
51
  json = true;
59
52
  continue;
60
53
  }
61
- if (arg === "--machine") {
62
- machineFlag = takeValue(args, "--machine");
54
+ const machine = takeFlag(arg, "--machine", args, usage);
55
+ if (machine !== undefined) {
56
+ machineFlag = machine;
63
57
  continue;
64
58
  }
65
- if (arg === "--model") {
66
- modelFlag = takeValue(args, "--model");
59
+ const model = takeFlag(arg, "--model", args, usage);
60
+ if (model !== undefined) {
61
+ modelFlag = model;
67
62
  continue;
68
63
  }
69
64
  if (arg.startsWith("-")) {
package/dist/show.js CHANGED
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { envInt, ollamaBaseUrl, shortName } from "./hosts.js";
7
7
  import { ollamaShow } from "./ollama-chat.js";
8
+ import { takeFlag } from "./argv.js";
8
9
  import { resolveTarget } from "./target.js";
9
10
  import { looksTuned } from "./tuned.js";
10
11
  function showTimeoutMs() {
@@ -28,14 +29,6 @@ Options:
28
29
  --json Emit result JSON on stdout`);
29
30
  process.exit(1);
30
31
  }
31
- function takeValue(args, flag) {
32
- const value = args.shift();
33
- if (!value) {
34
- console.error(`${flag} requires a value`);
35
- usage();
36
- }
37
- return value;
38
- }
39
32
  function parseArgs(argv) {
40
33
  const args = [...argv];
41
34
  let machineFlag;
@@ -52,12 +45,14 @@ function parseArgs(argv) {
52
45
  json = true;
53
46
  continue;
54
47
  }
55
- if (arg === "--machine") {
56
- machineFlag = takeValue(args, "--machine");
48
+ const machine = takeFlag(arg, "--machine", args, usage);
49
+ if (machine !== undefined) {
50
+ machineFlag = machine;
57
51
  continue;
58
52
  }
59
- if (arg === "--model") {
60
- modelFlag = takeValue(args, "--model");
53
+ const model = takeFlag(arg, "--model", args, usage);
54
+ if (model !== undefined) {
55
+ modelFlag = model;
61
56
  continue;
62
57
  }
63
58
  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
- /** Resolve a machine name the same way pull / prompt / bench do. */
4
- export async function resolveTarget(machine, config) {
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.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ollanet",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Discover and talk to Ollama on any reachable network — CLI, MCP, and a Node library",
5
5
  "type": "module",
6
6
  "bin": {