ollanet 0.2.2 → 0.4.0
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 +112 -5
- package/dist/bench-store.d.ts +3 -0
- package/dist/bench-suite.d.ts +25 -0
- package/dist/bench.d.ts +1 -0
- package/dist/bench.js +0 -1
- package/dist/chat-store.d.ts +50 -0
- package/dist/chats.d.ts +9 -0
- package/dist/chats.js +0 -1
- package/dist/cli.d.ts +9 -0
- package/dist/cli.js +7 -0
- package/dist/config.d.ts +45 -0
- package/dist/config.js +11 -0
- package/dist/hosts.d.ts +55 -0
- package/dist/hosts.js +9 -6
- package/dist/index.d.ts +18 -0
- package/dist/index.js +14 -0
- package/dist/mcp.d.ts +11 -0
- package/dist/mcp.js +287 -0
- package/dist/ollama-chat.d.ts +87 -0
- package/dist/paths.d.ts +17 -0
- package/dist/prompt.d.ts +41 -0
- package/dist/prompt.js +122 -66
- package/dist/scan.d.ts +44 -0
- package/dist/scan.js +56 -34
- package/package.json +27 -8
package/README.md
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/Catalyst-Forge-LLC/ollanet/main/assets/ollanet-logo.png" alt="ollanet logo" width="360">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
1
5
|
# ollanet
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
Find the models. Talk to them. Keep the thread.
|
|
8
|
+
|
|
9
|
+
Chat with **Ollama** servers on **any network you can reach** — LAN, localhost, Tailscale, VPN, or a raw IP. CLI for humans, MCP for agents, **Node library for apps**.
|
|
10
|
+
|
|
11
|
+
Scan for models, fire prompts, and continue conversations later by a short hash. No browser UI required. **Node 20+ · zero runtime deps.**
|
|
4
12
|
|
|
5
|
-
|
|
13
|
+
**Site:** [ollanet.dev](https://ollanet.dev) — Downpress + Cloudflare Pages (`site/`).
|
|
6
14
|
|
|
7
15
|
## Origin
|
|
8
16
|
|
|
@@ -17,7 +25,7 @@ Typical loop:
|
|
|
17
25
|
1. On the host: `finetuna` → create something like `gemma4-ctx32k`
|
|
18
26
|
2. From anywhere on the network: `ollanet scan` → `ollanet prompt that-host gemma4-ctx32k "…"`
|
|
19
27
|
|
|
20
|
-
They share the same Ollama API; Finetuna shapes the models, ollanet finds and chats with them.
|
|
28
|
+
They share the same Ollama API; Finetuna shapes the models, ollanet finds and chats with them. Host tunes the model. Network finds and uses it. Same API, closed loop.
|
|
21
29
|
|
|
22
30
|
## Features
|
|
23
31
|
|
|
@@ -26,6 +34,28 @@ They share the same Ollama API; Finetuna shapes the models, ollanet finds and ch
|
|
|
26
34
|
- **Persist** chats as `responses/<hash>.json` with topic, machine, model, timestamps
|
|
27
35
|
- **Continue** any thread with `--chat <hash>`
|
|
28
36
|
- **Configure** per-machine defaults (model, temperature, context, …)
|
|
37
|
+
- **MCP** — `ollanet mcp` exposes scan/prompt/chats as Model Context Protocol tools for agents
|
|
38
|
+
- **Library** — `import { scanNetwork } from "ollanet"` for apps (Node 20+, not the browser)
|
|
39
|
+
|
|
40
|
+
## Use cases
|
|
41
|
+
|
|
42
|
+
The core idea: turn “I have to remember which IP has which models loaded” into a discoverable, stateful, network-native primitive. That unlocks more than interactive chat:
|
|
43
|
+
|
|
44
|
+
**For agents and automation** — prefer `ollanet mcp` (stdio MCP). The same flows also work via CLI flags:
|
|
45
|
+
|
|
46
|
+
- **Live model inventory / routing** — `ollanet_scan` (or `scan --json`) lists reachable hosts and models. Route each sub-task to the best host: the big-VRAM box for reasoning, the laptop for quick lookups.
|
|
47
|
+
- **Conversations that survive machines** — chats are stored by short hash with full history. Start a long thread on the GPU box, continue it later with `ollanet_prompt` + `chat_id` (or `--chat <hash>`).
|
|
48
|
+
- **A “talk to any Ollama on my network” tool** — `ollanet_prompt` / `prompt … --json --no-stream`; no hard-coded endpoints.
|
|
49
|
+
- **Embed in an app** — same scan + pick + call as a library. The app does not own the GPUs; it discovers them (FilePress, CLIs, Electron, a local companion for a hosted product).
|
|
50
|
+
- **Fleet health and speed checks** — periodic `ollanet bench <host> --json` builds a record of which model on which machine is currently fastest (or has silently gone offline). Saved results live in `benchmarks/`.
|
|
51
|
+
- **The Finetuna loop** — [Finetuna](https://github.com/Catalyst-Forge-LLC/finetuna) shapes a GPU-tuned named variant on the host; ollanet discovers and uses it from anywhere on the network.
|
|
52
|
+
|
|
53
|
+
**For humans:**
|
|
54
|
+
|
|
55
|
+
- **Tailscale-native access** — prompt any machine by MagicDNS name from a laptop or phone (Termux) without babysitting IPs or opening a browser.
|
|
56
|
+
- **A private model mesh** — everyone on a shared tailnet or LAN can discover and use the same Ollama hosts, no public exposure, no central UI.
|
|
57
|
+
- **Multi-GPU lab** — `scan --lan` treats several machines as a pool of inference endpoints; `bench` compares the same model across hardware.
|
|
58
|
+
- **Audit trail / notes** — persisted chat JSON doubles as a research log you can resume from any device.
|
|
29
59
|
|
|
30
60
|
## Requirements
|
|
31
61
|
|
|
@@ -39,7 +69,7 @@ Tailscale is optional. If the `tailscale` CLI is present, peers are included aut
|
|
|
39
69
|
Run without installing:
|
|
40
70
|
|
|
41
71
|
```bash
|
|
42
|
-
npx ollanet scan
|
|
72
|
+
npx ollanet scan
|
|
43
73
|
npx --allow-git=all github:Catalyst-Forge-LLC/ollanet scan # straight from GitHub
|
|
44
74
|
```
|
|
45
75
|
|
|
@@ -50,7 +80,7 @@ npx --allow-git=all github:Catalyst-Forge-LLC/ollanet scan # straight fro
|
|
|
50
80
|
Or install globally, which puts the `ollanet` command on your PATH:
|
|
51
81
|
|
|
52
82
|
```bash
|
|
53
|
-
npm install -g ollanet
|
|
83
|
+
npm install -g ollanet
|
|
54
84
|
npm install -g --allow-git=all github:Catalyst-Forge-LLC/ollanet # from GitHub
|
|
55
85
|
ollanet scan
|
|
56
86
|
```
|
|
@@ -97,6 +127,49 @@ ollanet chats
|
|
|
97
127
|
ollanet chats --id a1b2c3d4e5f6
|
|
98
128
|
```
|
|
99
129
|
|
|
130
|
+
## Use as a library
|
|
131
|
+
|
|
132
|
+
ollanet is a **Node 20+ library that happens to have a CLI**. Not for the browser — discovery uses `node:net`, `child_process` (`tailscale status`), and `os.networkInterfaces()`. Call it from a dev server, CLI, Electron main, or a Worker with Node, never from Svelte/Vite client code.
|
|
133
|
+
|
|
134
|
+
Hard-coding `http://127.0.0.1:11434` treats Ollama like a local daemon. This treats it like a **private inference mesh**: any machine you can already reach. The app does not own the GPUs; it discovers them. LAN scan is opt-in so you do not look like a port scanner. Tailscale is automatic if the CLI exists. Config lives in `~/.ollanet` unless you pass `config`.
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import { scanNetwork } from "ollanet";
|
|
138
|
+
|
|
139
|
+
const { servers, sources } = await scanNetwork({ lanScan: false });
|
|
140
|
+
for (const s of servers) {
|
|
141
|
+
console.log(s.endpoint, s.models.map((m) => m.name));
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
| Call | What it does | When apps should use it |
|
|
146
|
+
|---|---|---|
|
|
147
|
+
| `scanNetwork()` | localhost + config + `OLLANET_HOSTS` + Tailscale | Button / first “find servers” |
|
|
148
|
+
| `scanNetwork({ lanScan: true })` | plus TCP sweep of local `/24`s | Checkbox, never on every health check |
|
|
149
|
+
| `discoverHosts()` then probe yourself | candidates only, no `/api/tags` | If the app already has its own Ollama client |
|
|
150
|
+
|
|
151
|
+
Pass `config` to skip the file (useful when the app already has its own config):
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
await scanNetwork({
|
|
155
|
+
lanScan: false,
|
|
156
|
+
config: {
|
|
157
|
+
hosts: ["studio.tailnet.ts.net", "192.168.1.50"],
|
|
158
|
+
discovery: { localhost: true, tailscale: true, lan: false },
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`runPrompt({ save: false, writeStdout: false })` is a one-shot call that does not write `~/.ollanet/responses/`.
|
|
164
|
+
|
|
165
|
+
Requires **ollanet ≥ 0.4.0**. Bundlers (Vite / SvelteKit SSR):
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
// vite.config.ts
|
|
169
|
+
ssr: { external: ["ollanet"] },
|
|
170
|
+
optimizeDeps: { exclude: ["ollanet"] },
|
|
171
|
+
```
|
|
172
|
+
|
|
100
173
|
## Commands
|
|
101
174
|
|
|
102
175
|
| Command | Description |
|
|
@@ -105,6 +178,7 @@ ollanet chats --id a1b2c3d4e5f6
|
|
|
105
178
|
| `ollanet prompt …` | Send a prompt / continue a chat |
|
|
106
179
|
| `ollanet chats` | List or inspect saved transcripts |
|
|
107
180
|
| `ollanet bench …` | Benchmark models for tok/s + lightweight quality checks |
|
|
181
|
+
| `ollanet mcp` | Stdio [MCP](https://modelcontextprotocol.io) server for agents |
|
|
108
182
|
|
|
109
183
|
### `scan` options
|
|
110
184
|
|
|
@@ -142,6 +216,36 @@ Machine can be a discovered name, hostname, FQDN, or IP (`192.168.1.50`, `host:1
|
|
|
142
216
|
- `--json` — list summary JSON
|
|
143
217
|
- `--id <hash>` — show one chat (full JSON with `--json`)
|
|
144
218
|
|
|
219
|
+
### `mcp`
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
ollanet mcp
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Runs a **stdio MCP server** (no extra npm deps). Cursor / Claude Desktop / any MCP host can spawn it and call:
|
|
226
|
+
|
|
227
|
+
| Tool | What it does |
|
|
228
|
+
|---|---|
|
|
229
|
+
| `ollanet_scan` | Discover hosts + models (`lan`, `all` optional) |
|
|
230
|
+
| `ollanet_prompt` | Prompt a host or continue with `chat_id` |
|
|
231
|
+
| `ollanet_list_chats` | Summarize saved transcripts |
|
|
232
|
+
| `ollanet_get_chat` | Load one chat (full history) |
|
|
233
|
+
|
|
234
|
+
Example Cursor MCP config (`~/.cursor/mcp.json` or project `.cursor/mcp.json`):
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"mcpServers": {
|
|
239
|
+
"ollanet": {
|
|
240
|
+
"command": "npx",
|
|
241
|
+
"args": ["-y", "ollanet", "mcp"]
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Or, from a checkout: `"command": "pnpm"`, `"args": ["ollanet", "--", "mcp"]` (with `cwd` set to the repo). Uses the same config/chats dirs as the CLI (`~/.ollanet/` when installed).
|
|
248
|
+
|
|
145
249
|
### `bench`
|
|
146
250
|
|
|
147
251
|
```bash
|
|
@@ -250,6 +354,7 @@ The first turn asks the model for a short topic title (falls back to the prompt
|
|
|
250
354
|
```text
|
|
251
355
|
ollanet/
|
|
252
356
|
src/
|
|
357
|
+
index.ts # public library entry (scan, discover, prompt, ollama helpers)
|
|
253
358
|
cli.ts # subcommand router (CLI entry)
|
|
254
359
|
hosts.ts # discovery + host resolution
|
|
255
360
|
scan.ts
|
|
@@ -259,11 +364,13 @@ ollanet/
|
|
|
259
364
|
config.ts
|
|
260
365
|
paths.ts # checkout vs installed path resolution
|
|
261
366
|
ollama-chat.ts # shared /api/chat (+ tags/show/ps helpers)
|
|
367
|
+
mcp.ts # stdio MCP server
|
|
262
368
|
bench.ts # ollanet bench
|
|
263
369
|
bench-suite.ts
|
|
264
370
|
bench-store.ts
|
|
265
371
|
dist/ # compiled output (published to npm)
|
|
266
372
|
docs/bench-spec.md # bench measurement spec
|
|
373
|
+
docs/library-api-spec.md
|
|
267
374
|
config.json
|
|
268
375
|
responses/ # gitignored chat history
|
|
269
376
|
benchmarks/ # gitignored bench results
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type CaseRole = "check" | "live" | "throughput";
|
|
2
|
+
export interface BenchCase {
|
|
3
|
+
id: string;
|
|
4
|
+
role: CaseRole;
|
|
5
|
+
prompt: string;
|
|
6
|
+
/** Cap for this case; throughput uses settings.throughput_num_predict. */
|
|
7
|
+
num_predict?: number;
|
|
8
|
+
format?: "json";
|
|
9
|
+
check?: (content: string) => {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
detail?: string;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export type SuiteName = "quick" | "full";
|
|
15
|
+
export declare function getSuiteCases(suite: SuiteName): BenchCase[];
|
|
16
|
+
export declare function suiteRevision(suite: SuiteName): string;
|
|
17
|
+
export declare function comparabilityKey(opts: {
|
|
18
|
+
suite: SuiteName;
|
|
19
|
+
throughputNumPredict: number;
|
|
20
|
+
seed: number;
|
|
21
|
+
temperature: number;
|
|
22
|
+
think: boolean;
|
|
23
|
+
numCtx: number | null;
|
|
24
|
+
}): string;
|
|
25
|
+
export declare function median(values: number[]): number | undefined;
|
package/dist/bench.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function main(): Promise<void>;
|
package/dist/bench.js
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type ChatRole = "system" | "user" | "assistant";
|
|
2
|
+
export interface ChatMessage {
|
|
3
|
+
role: ChatRole;
|
|
4
|
+
content: string;
|
|
5
|
+
/** Present for thinking models when reasoning was returned separately. */
|
|
6
|
+
thinking?: string;
|
|
7
|
+
timestamp: string;
|
|
8
|
+
machine?: string;
|
|
9
|
+
model?: string;
|
|
10
|
+
stats?: {
|
|
11
|
+
eval_count?: number;
|
|
12
|
+
eval_duration?: number;
|
|
13
|
+
total_duration?: number;
|
|
14
|
+
prompt_eval_count?: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface ChatTranscript {
|
|
18
|
+
id: string;
|
|
19
|
+
topic: string;
|
|
20
|
+
created_at: string;
|
|
21
|
+
updated_at: string;
|
|
22
|
+
machine: string;
|
|
23
|
+
model: string;
|
|
24
|
+
system?: string;
|
|
25
|
+
messages: ChatMessage[];
|
|
26
|
+
}
|
|
27
|
+
export declare function responsesDir(): string;
|
|
28
|
+
export declare function newChatId(): string;
|
|
29
|
+
export declare function chatPath(id: string): string;
|
|
30
|
+
export declare function normalizeChatId(id: string): string;
|
|
31
|
+
export declare function ensureResponsesDir(): Promise<string>;
|
|
32
|
+
export declare function loadChat(id: string): Promise<ChatTranscript>;
|
|
33
|
+
export declare function saveChat(chat: ChatTranscript): Promise<string>;
|
|
34
|
+
export declare function createChat(opts: {
|
|
35
|
+
machine: string;
|
|
36
|
+
model: string;
|
|
37
|
+
system?: string;
|
|
38
|
+
topic?: string;
|
|
39
|
+
}): ChatTranscript;
|
|
40
|
+
export declare function appendMessage(chat: ChatTranscript, message: Omit<ChatMessage, "timestamp"> & {
|
|
41
|
+
timestamp?: string;
|
|
42
|
+
}): ChatMessage;
|
|
43
|
+
export declare function listChats(): Promise<ChatTranscript[]>;
|
|
44
|
+
/** Fallback topic from the first user message when model summarization fails. */
|
|
45
|
+
export declare function topicFromPrompt(prompt: string): string;
|
|
46
|
+
export declare function cleanTopic(raw: string, fallback: string): string;
|
|
47
|
+
export declare function toApiMessages(chat: ChatTranscript): Array<{
|
|
48
|
+
role: ChatRole;
|
|
49
|
+
content: string;
|
|
50
|
+
}>;
|
package/dist/chats.d.ts
ADDED
package/dist/chats.js
CHANGED
package/dist/cli.d.ts
ADDED
package/dist/cli.js
CHANGED
|
@@ -17,6 +17,7 @@ Usage:
|
|
|
17
17
|
ollanet prompt --chat <hash> <prompt...>
|
|
18
18
|
ollanet chats [--json] [--id <hash>]
|
|
19
19
|
ollanet bench <machine|ip> [model...] [--all] [options]
|
|
20
|
+
ollanet mcp
|
|
20
21
|
|
|
21
22
|
Examples:
|
|
22
23
|
ollanet scan
|
|
@@ -27,6 +28,7 @@ Examples:
|
|
|
27
28
|
ollanet chats
|
|
28
29
|
ollanet bench localhost --all
|
|
29
30
|
ollanet bench localhost llama3.2:1b --runs 3
|
|
31
|
+
ollanet mcp # stdio MCP server for agents
|
|
30
32
|
|
|
31
33
|
Config: ~/.ollanet/config.json when installed, ./config.json in a checkout
|
|
32
34
|
(override with OLLANET_CONFIG)
|
|
@@ -77,6 +79,11 @@ async function main() {
|
|
|
77
79
|
await run();
|
|
78
80
|
return;
|
|
79
81
|
}
|
|
82
|
+
case "mcp": {
|
|
83
|
+
const { main: run } = await import("./mcp.js");
|
|
84
|
+
await run();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
80
87
|
default:
|
|
81
88
|
console.error(`Unknown command: ${cmd}\n`);
|
|
82
89
|
process.stderr.write(HELP);
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type DiscoveryConfig, type HostConfigEntry, type HostTarget } from "./hosts.js";
|
|
2
|
+
export type { DiscoveryConfig, HostConfigEntry };
|
|
3
|
+
/** Runtime knobs sent to Ollama `/api/generate`. */
|
|
4
|
+
export interface GenerateSettings {
|
|
5
|
+
temperature?: number;
|
|
6
|
+
num_predict?: number;
|
|
7
|
+
num_ctx?: number;
|
|
8
|
+
/** Ollama `options.seed` (-1 = random). */
|
|
9
|
+
seed?: number;
|
|
10
|
+
keep_alive?: string | number;
|
|
11
|
+
/** `"json"` or a JSON-schema object */
|
|
12
|
+
format?: "json" | Record<string, unknown>;
|
|
13
|
+
system?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Ollama "thinking" models (e.g. qwen3). When false/omitted, requests send
|
|
16
|
+
* `think: false` so tokens go to the visible reply instead of hidden reasoning.
|
|
17
|
+
*/
|
|
18
|
+
think?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface AppConfig {
|
|
21
|
+
/** Explicit hosts/IPs to probe (LAN, VPN, MagicDNS, etc.). */
|
|
22
|
+
hosts: HostConfigEntry[];
|
|
23
|
+
discovery: DiscoveryConfig;
|
|
24
|
+
defaultModels: Record<string, string>;
|
|
25
|
+
/** Global defaults applied to every prompt. */
|
|
26
|
+
defaults: GenerateSettings;
|
|
27
|
+
/** Per-machine overrides (keyed by short name / hostname / DNS / IP). */
|
|
28
|
+
machineDefaults: Record<string, GenerateSettings & {
|
|
29
|
+
model?: string;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
/** Build an AppConfig from an in-memory partial (no file I/O). */
|
|
33
|
+
export declare function configFromPartial(partial?: Partial<AppConfig>): AppConfig;
|
|
34
|
+
export declare function loadConfig(): Promise<AppConfig>;
|
|
35
|
+
export declare function normalizeSettings(input: unknown): GenerateSettings;
|
|
36
|
+
export declare function parseFormat(value: unknown): GenerateSettings["format"] | undefined;
|
|
37
|
+
export declare function hostLookupKeys(host: HostTarget): string[];
|
|
38
|
+
/** Resolve a default model for a host using short name, hostname, DNS, or IP keys. */
|
|
39
|
+
export declare function defaultModelForHost(config: AppConfig, host: HostTarget): string | undefined;
|
|
40
|
+
export declare function machineSettingsForHost(config: AppConfig, host: HostTarget): GenerateSettings;
|
|
41
|
+
/** Later layers win. Undefined values are skipped. */
|
|
42
|
+
export declare function mergeSettings(...layers: Array<GenerateSettings | undefined>): GenerateSettings;
|
|
43
|
+
export declare function settingsFromEnv(): GenerateSettings;
|
|
44
|
+
export declare function settingsSummary(settings: GenerateSettings): string;
|
|
45
|
+
export declare function configPath(): string;
|
package/dist/config.js
CHANGED
|
@@ -10,6 +10,17 @@ function emptyConfig() {
|
|
|
10
10
|
machineDefaults: {},
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
+
/** Build an AppConfig from an in-memory partial (no file I/O). */
|
|
14
|
+
export function configFromPartial(partial = {}) {
|
|
15
|
+
const empty = emptyConfig();
|
|
16
|
+
return {
|
|
17
|
+
hosts: Array.isArray(partial.hosts) ? partial.hosts : empty.hosts,
|
|
18
|
+
discovery: partial.discovery ?? empty.discovery,
|
|
19
|
+
defaultModels: partial.defaultModels ?? empty.defaultModels,
|
|
20
|
+
defaults: partial.defaults ?? empty.defaults,
|
|
21
|
+
machineDefaults: partial.machineDefaults ?? empty.machineDefaults,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
13
24
|
const CONFIG_PATH = process.env.OLLANET_CONFIG ??
|
|
14
25
|
process.env.OLLAMA_CONFIG ??
|
|
15
26
|
defaultConfigPath();
|
package/dist/hosts.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** Parse a required numeric env var; invalid values throw instead of becoming NaN. */
|
|
2
|
+
export declare function envInt(name: string, fallback: number): number;
|
|
3
|
+
/** Default Ollama port. Read at call time so a bad env var cannot break `import`. */
|
|
4
|
+
export declare function ollamaPort(): number;
|
|
5
|
+
export type HostSource = "localhost" | "config" | "env" | "tailscale" | "lan" | "direct";
|
|
6
|
+
export type HostConfigEntry = string | {
|
|
7
|
+
name?: string;
|
|
8
|
+
host?: string;
|
|
9
|
+
ip?: string;
|
|
10
|
+
port?: number;
|
|
11
|
+
};
|
|
12
|
+
export interface DiscoveryConfig {
|
|
13
|
+
/** Always include localhost / 127.0.0.1 (default true). */
|
|
14
|
+
localhost?: boolean;
|
|
15
|
+
/** Use Tailscale peers when the CLI is available (default true). */
|
|
16
|
+
tailscale?: boolean;
|
|
17
|
+
/** TCP-scan local LAN CIDRs for open Ollama ports (default false). */
|
|
18
|
+
lan?: boolean;
|
|
19
|
+
/** Explicit CIDRs for LAN scan (default: guessed /24s from local interfaces). */
|
|
20
|
+
cidrs?: string[];
|
|
21
|
+
}
|
|
22
|
+
export interface HostTarget {
|
|
23
|
+
hostname: string;
|
|
24
|
+
dnsName: string;
|
|
25
|
+
ip: string;
|
|
26
|
+
online: boolean;
|
|
27
|
+
os: string;
|
|
28
|
+
isSelf: boolean;
|
|
29
|
+
source: HostSource;
|
|
30
|
+
port: number;
|
|
31
|
+
}
|
|
32
|
+
export interface DiscoveryResult {
|
|
33
|
+
hosts: HostTarget[];
|
|
34
|
+
sources: string[];
|
|
35
|
+
networkLabel: string;
|
|
36
|
+
}
|
|
37
|
+
export interface DiscoverOptions {
|
|
38
|
+
hosts?: HostConfigEntry[];
|
|
39
|
+
discovery?: DiscoveryConfig;
|
|
40
|
+
/** Include Tailscale peers marked offline. */
|
|
41
|
+
includeOffline?: boolean;
|
|
42
|
+
/** Probe local LAN CIDRs for open Ollama ports. */
|
|
43
|
+
lanScan?: boolean;
|
|
44
|
+
}
|
|
45
|
+
export declare function shortName(host: HostTarget): string;
|
|
46
|
+
export declare function ollamaBaseUrl(host: HostTarget, port?: number): string;
|
|
47
|
+
/** Discover candidate Ollama hosts from localhost, config, env, Tailscale, and optional LAN scan. */
|
|
48
|
+
export declare function discoverHosts(opts?: DiscoverOptions): Promise<DiscoveryResult>;
|
|
49
|
+
/**
|
|
50
|
+
* Match a query against already-discovered hosts only (no direct-address fallback).
|
|
51
|
+
* Used when peeling an optional leading hostname before a prompt.
|
|
52
|
+
*/
|
|
53
|
+
export declare function findDiscoveredHost(targets: HostTarget[], query: string): HostTarget | undefined;
|
|
54
|
+
/** Resolve a machine name against discovered hosts, or treat it as a direct address. */
|
|
55
|
+
export declare function resolveHost(targets: HostTarget[], query: string): HostTarget;
|
package/dist/hosts.js
CHANGED
|
@@ -14,7 +14,10 @@ export function envInt(name, fallback) {
|
|
|
14
14
|
}
|
|
15
15
|
return Math.trunc(n);
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
/** Default Ollama port. Read at call time so a bad env var cannot break `import`. */
|
|
18
|
+
export function ollamaPort() {
|
|
19
|
+
return envInt("OLLAMA_PORT", 11434);
|
|
20
|
+
}
|
|
18
21
|
function cleanDnsName(dnsName) {
|
|
19
22
|
if (!dnsName)
|
|
20
23
|
return "";
|
|
@@ -29,7 +32,7 @@ export function shortName(host) {
|
|
|
29
32
|
}
|
|
30
33
|
return host.hostname;
|
|
31
34
|
}
|
|
32
|
-
export function ollamaBaseUrl(host, port = host.port ||
|
|
35
|
+
export function ollamaBaseUrl(host, port = host.port || ollamaPort()) {
|
|
33
36
|
const hostPart = host.ip.includes(":") ? `[${host.ip}]` : host.ip;
|
|
34
37
|
return `http://${hostPart}:${port}`;
|
|
35
38
|
}
|
|
@@ -42,7 +45,7 @@ function makeHost(partial) {
|
|
|
42
45
|
os: partial.os ?? "unknown",
|
|
43
46
|
isSelf: partial.isSelf ?? false,
|
|
44
47
|
source: partial.source,
|
|
45
|
-
port: partial.port ??
|
|
48
|
+
port: partial.port ?? ollamaPort(),
|
|
46
49
|
};
|
|
47
50
|
}
|
|
48
51
|
function dedupeKey(host) {
|
|
@@ -114,7 +117,7 @@ function parseHostEntry(entry) {
|
|
|
114
117
|
ip: address,
|
|
115
118
|
source: "config",
|
|
116
119
|
online: true,
|
|
117
|
-
port: typeof entry.port === "number" ? entry.port :
|
|
120
|
+
port: typeof entry.port === "number" ? entry.port : ollamaPort(),
|
|
118
121
|
});
|
|
119
122
|
}
|
|
120
123
|
function envHostTargets() {
|
|
@@ -298,7 +301,7 @@ function parseDirectAddress(query) {
|
|
|
298
301
|
return null;
|
|
299
302
|
let hostname = q;
|
|
300
303
|
let ip = q;
|
|
301
|
-
let port =
|
|
304
|
+
let port = ollamaPort();
|
|
302
305
|
const ipv4WithPort = q.match(/^(\d{1,3}(?:\.\d{1,3}){3}):(\d+)$/);
|
|
303
306
|
if (ipv4WithPort) {
|
|
304
307
|
ip = ipv4WithPort[1];
|
|
@@ -365,7 +368,7 @@ export async function discoverHosts(opts = {}) {
|
|
|
365
368
|
const lanEnabled = opts.lanScan ?? discovery.lan === true;
|
|
366
369
|
if (lanEnabled) {
|
|
367
370
|
const cidrs = discovery.cidrs && discovery.cidrs.length > 0 ? discovery.cidrs : guessLocalCidrs();
|
|
368
|
-
const lanHosts = await lanScanTargets(cidrs,
|
|
371
|
+
const lanHosts = await lanScanTargets(cidrs, ollamaPort());
|
|
369
372
|
if (lanHosts.length > 0) {
|
|
370
373
|
collected.push(...lanHosts);
|
|
371
374
|
sources.push("lan");
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ollanet — Node library for discovering and talking to Ollama on any
|
|
3
|
+
* reachable network (loopback, LAN, Tailscale, VPN).
|
|
4
|
+
*
|
|
5
|
+
* Node 20+ only. Not for the browser. LAN scan is opt-in.
|
|
6
|
+
*
|
|
7
|
+
* import { scanNetwork } from "ollanet";
|
|
8
|
+
* const { servers } = await scanNetwork({ lanScan: false });
|
|
9
|
+
*/
|
|
10
|
+
export { scanNetwork } from "./scan.js";
|
|
11
|
+
export type { ScanOptions, ScanPayload, ScannedServer } from "./scan.js";
|
|
12
|
+
export { discoverHosts, resolveHost, ollamaBaseUrl, shortName, ollamaPort, } from "./hosts.js";
|
|
13
|
+
export type { DiscoverOptions, DiscoveryResult, HostTarget, HostSource, HostConfigEntry, } from "./hosts.js";
|
|
14
|
+
export { loadConfig, defaultModelForHost, mergeSettings, configFromPartial, } from "./config.js";
|
|
15
|
+
export type { AppConfig, DiscoveryConfig, GenerateSettings } from "./config.js";
|
|
16
|
+
export { runPrompt } from "./prompt.js";
|
|
17
|
+
export type { PromptRunOptions, PromptRunResult } from "./prompt.js";
|
|
18
|
+
export { ollamaChat, ollamaTags, ollamaShow, ollamaPs } from "./ollama-chat.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ollanet — Node library for discovering and talking to Ollama on any
|
|
3
|
+
* reachable network (loopback, LAN, Tailscale, VPN).
|
|
4
|
+
*
|
|
5
|
+
* Node 20+ only. Not for the browser. LAN scan is opt-in.
|
|
6
|
+
*
|
|
7
|
+
* import { scanNetwork } from "ollanet";
|
|
8
|
+
* const { servers } = await scanNetwork({ lanScan: false });
|
|
9
|
+
*/
|
|
10
|
+
export { scanNetwork } from "./scan.js";
|
|
11
|
+
export { discoverHosts, resolveHost, ollamaBaseUrl, shortName, ollamaPort, } from "./hosts.js";
|
|
12
|
+
export { loadConfig, defaultModelForHost, mergeSettings, configFromPartial, } from "./config.js";
|
|
13
|
+
export { runPrompt } from "./prompt.js";
|
|
14
|
+
export { ollamaChat, ollamaTags, ollamaShow, ollamaPs } from "./ollama-chat.js";
|
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ollanet mcp — stdio MCP server (zero deps).
|
|
3
|
+
*
|
|
4
|
+
* Speaks the Model Context Protocol over stdin/stdout so agents (Cursor,
|
|
5
|
+
* Claude Desktop, etc.) can discover hosts, prompt models, and continue chats.
|
|
6
|
+
*
|
|
7
|
+
* ollanet mcp
|
|
8
|
+
*
|
|
9
|
+
* stdout is reserved for JSON-RPC; log only to stderr.
|
|
10
|
+
*/
|
|
11
|
+
export declare function main(): Promise<void>;
|