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/dist/prompt.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
2
|
* Run a prompt / continue a chat against an Ollama server on the network.
|
|
4
3
|
*
|
|
@@ -14,11 +13,13 @@
|
|
|
14
13
|
*/
|
|
15
14
|
import { stdin as stdinStream } from "node:process";
|
|
16
15
|
import { appendMessage, cleanTopic, createChat, loadChat, saveChat, toApiMessages, topicFromPrompt, } from "./chat-store.js";
|
|
17
|
-
import { configPath, defaultModelForHost, loadConfig, machineSettingsForHost, mergeSettings, parseFormat, settingsFromEnv, settingsSummary, } from "./config.js";
|
|
16
|
+
import { configPath, defaultModelForHost, loadConfig, machineSettingsForHost, mergeSettings, parseFormat, settingsFromEnv, settingsSummary, configFromPartial, } from "./config.js";
|
|
18
17
|
import { discoverHosts, envInt, findDiscoveredHost, ollamaBaseUrl, resolveHost, shortName, } from "./hosts.js";
|
|
19
18
|
import { ollamaChat } from "./ollama-chat.js";
|
|
20
19
|
/** Prompt/chat HTTP timeout (ms). 0 = no timeout. Separate from scan's OLLAMA_TIMEOUT_MS. */
|
|
21
|
-
|
|
20
|
+
function promptTimeoutMs() {
|
|
21
|
+
return envInt("OLLAMA_PROMPT_TIMEOUT_MS", 600_000);
|
|
22
|
+
}
|
|
22
23
|
function usage() {
|
|
23
24
|
console.error(`Usage:
|
|
24
25
|
ollanet prompt <machine> [model] <prompt...>
|
|
@@ -77,7 +78,7 @@ function modelNameMatches(available, candidate) {
|
|
|
77
78
|
async function listModelNames(host) {
|
|
78
79
|
const url = `${ollamaBaseUrl(host)}/api/tags`;
|
|
79
80
|
const controller = new AbortController();
|
|
80
|
-
const timer = setTimeout(() => controller.abort(), Math.min(
|
|
81
|
+
const timer = setTimeout(() => controller.abort(), Math.min(promptTimeoutMs() || 2500, 5000));
|
|
81
82
|
try {
|
|
82
83
|
const res = await fetch(url, {
|
|
83
84
|
signal: controller.signal,
|
|
@@ -278,7 +279,7 @@ async function generateTopic(opts) {
|
|
|
278
279
|
model: opts.model,
|
|
279
280
|
stream: false,
|
|
280
281
|
writeStdout: false,
|
|
281
|
-
timeoutMs:
|
|
282
|
+
timeoutMs: promptTimeoutMs(),
|
|
282
283
|
settings: mergeSettings(opts.settings, {
|
|
283
284
|
temperature: 0.2,
|
|
284
285
|
num_predict: 24,
|
|
@@ -308,62 +309,48 @@ function formatDuration(ns) {
|
|
|
308
309
|
return `${ms.toFixed(0)}ms`;
|
|
309
310
|
return `${(ms / 1000).toFixed(1)}s`;
|
|
310
311
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
const
|
|
316
|
-
const
|
|
312
|
+
/** Programmatic prompt used by CLI `--json` and the MCP server. */
|
|
313
|
+
export async function runPrompt(opts) {
|
|
314
|
+
const save = opts.save !== false;
|
|
315
|
+
const stream = opts.stream !== false && opts.writeStdout !== false;
|
|
316
|
+
const quiet = opts.quiet === true || opts.writeStdout === false;
|
|
317
|
+
const writeStdout = opts.writeStdout !== false;
|
|
318
|
+
let promptParts = opts.prompt ? [opts.prompt] : [];
|
|
319
|
+
const config = opts.config ? configFromPartial(opts.config) : await loadConfig();
|
|
317
320
|
const { hosts: targets } = await discoverHosts({
|
|
318
321
|
hosts: config.hosts,
|
|
319
322
|
discovery: config.discovery,
|
|
320
323
|
});
|
|
321
324
|
let chat;
|
|
322
325
|
let isNewChat = false;
|
|
323
|
-
let machineQuery =
|
|
324
|
-
if (
|
|
325
|
-
chat = await loadChat(
|
|
326
|
-
// Allow `ollanet prompt <host> --chat HASH "follow-up"` only when the
|
|
327
|
-
// leading token matches an already-discovered host (not direct-address fallback).
|
|
328
|
-
if (!machineQuery && promptParts.length >= 2) {
|
|
329
|
-
const hit = findDiscoveredHost(targets, promptParts[0]);
|
|
330
|
-
if (hit) {
|
|
331
|
-
machineQuery = promptParts[0];
|
|
332
|
-
promptParts = promptParts.slice(1);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
326
|
+
let machineQuery = opts.machine;
|
|
327
|
+
if (opts.chatId) {
|
|
328
|
+
chat = await loadChat(opts.chatId);
|
|
335
329
|
}
|
|
336
330
|
machineQuery = machineQuery ?? chat?.machine;
|
|
337
331
|
if (!machineQuery) {
|
|
338
|
-
|
|
339
|
-
usage();
|
|
332
|
+
throw new Error("Machine is required for a new chat (or pass chatId).");
|
|
340
333
|
}
|
|
341
334
|
const host = resolveHost(targets, machineQuery);
|
|
342
335
|
if (!host.online && !host.isSelf && host.source === "tailscale") {
|
|
343
336
|
throw new Error(`Machine "${shortName(host)}" appears offline.`);
|
|
344
337
|
}
|
|
345
338
|
let peeledModel;
|
|
346
|
-
if (!
|
|
347
|
-
const peeled = await maybePeelModel(host, promptParts,
|
|
339
|
+
if (!opts.chatId && !opts.model) {
|
|
340
|
+
const peeled = await maybePeelModel(host, promptParts, "");
|
|
348
341
|
peeledModel = peeled.model;
|
|
349
342
|
promptParts = peeled.promptParts;
|
|
350
343
|
}
|
|
351
|
-
const promptText =
|
|
344
|
+
const promptText = promptParts.join(" ").trim();
|
|
352
345
|
if (!promptText) {
|
|
353
|
-
|
|
354
|
-
usage();
|
|
346
|
+
throw new Error("No prompt provided.");
|
|
355
347
|
}
|
|
356
|
-
const model =
|
|
357
|
-
peeledModel ??
|
|
358
|
-
chat?.model ??
|
|
359
|
-
defaultModelForHost(config, host);
|
|
348
|
+
const model = opts.model ?? peeledModel ?? chat?.model ?? defaultModelForHost(config, host);
|
|
360
349
|
if (!model) {
|
|
361
350
|
throw new Error(`No model specified for "${shortName(host)}" and no default in ${configPath()}.\n` +
|
|
362
|
-
`Pass a model
|
|
351
|
+
`Pass a model or add it under defaultModels.`);
|
|
363
352
|
}
|
|
364
|
-
const settings = mergeSettings(
|
|
365
|
-
// Thinking models otherwise spend the whole num_predict budget on hidden reasoning.
|
|
366
|
-
{ think: false }, config.defaults, machineSettingsForHost(config, host), settingsFromEnv(), chat?.system ? { system: chat.system } : undefined, parsed.settings);
|
|
353
|
+
const settings = mergeSettings({ think: false }, config.defaults, machineSettingsForHost(config, host), settingsFromEnv(), chat?.system ? { system: chat.system } : undefined, opts.settings);
|
|
367
354
|
if (!chat && save) {
|
|
368
355
|
chat = createChat({
|
|
369
356
|
machine: shortName(host),
|
|
@@ -374,7 +361,6 @@ export async function main() {
|
|
|
374
361
|
isNewChat = true;
|
|
375
362
|
}
|
|
376
363
|
else if (!chat) {
|
|
377
|
-
// --no-save one-shot: ephemeral transcript for the API call only
|
|
378
364
|
chat = createChat({
|
|
379
365
|
machine: shortName(host),
|
|
380
366
|
model,
|
|
@@ -383,27 +369,27 @@ export async function main() {
|
|
|
383
369
|
isNewChat = true;
|
|
384
370
|
}
|
|
385
371
|
else {
|
|
386
|
-
if (
|
|
387
|
-
chat.system =
|
|
372
|
+
if (opts.settings?.system && !chat.system) {
|
|
373
|
+
chat.system = opts.settings.system;
|
|
388
374
|
}
|
|
389
375
|
chat.model = model;
|
|
390
376
|
chat.machine = shortName(host);
|
|
391
377
|
}
|
|
392
378
|
appendMessage(chat, { role: "user", content: promptText });
|
|
393
379
|
const baseUrl = ollamaBaseUrl(host);
|
|
394
|
-
if (!
|
|
395
|
-
const src =
|
|
380
|
+
if (!quiet) {
|
|
381
|
+
const src = opts.model ? "explicit" : chat && !isNewChat ? "chat" : "default";
|
|
396
382
|
const extras = settingsSummary(settings);
|
|
397
|
-
const chatTag =
|
|
383
|
+
const chatTag = opts.chatId ? ` chat=${chat.id}` : "";
|
|
398
384
|
console.error(`→ ${shortName(host)} (${host.ip}) model=${model} [${src}]${chatTag}${extras ? ` ${extras}` : ""}`);
|
|
399
385
|
}
|
|
400
386
|
const { content, thinking, chunk } = await ollamaChat({
|
|
401
387
|
baseUrl,
|
|
402
388
|
model,
|
|
403
389
|
messages: toApiMessages(chat),
|
|
404
|
-
stream:
|
|
405
|
-
writeStdout
|
|
406
|
-
timeoutMs:
|
|
390
|
+
stream: writeStdout ? stream : false,
|
|
391
|
+
writeStdout,
|
|
392
|
+
timeoutMs: promptTimeoutMs(),
|
|
407
393
|
settings,
|
|
408
394
|
});
|
|
409
395
|
appendMessage(chat, {
|
|
@@ -429,30 +415,100 @@ export async function main() {
|
|
|
429
415
|
}
|
|
430
416
|
if (save) {
|
|
431
417
|
const file = await saveChat(chat);
|
|
432
|
-
if (!
|
|
418
|
+
if (!quiet) {
|
|
433
419
|
console.error(`chat ${chat.id} topic: ${chat.topic}`);
|
|
434
420
|
console.error(`saved ${file}`);
|
|
435
421
|
console.error(`continue: ollanet prompt --chat ${chat.id} "your follow-up"`);
|
|
436
422
|
}
|
|
437
423
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
424
|
+
return {
|
|
425
|
+
chat_id: save ? chat.id : null,
|
|
426
|
+
topic: chat.topic,
|
|
427
|
+
content,
|
|
428
|
+
thinking: thinking || null,
|
|
429
|
+
machine: shortName(host),
|
|
430
|
+
model,
|
|
431
|
+
ollama: chunk,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
export async function main() {
|
|
435
|
+
const parsed = parseArgs(process.argv.slice(2));
|
|
436
|
+
const { stream, json, save } = parsed;
|
|
437
|
+
let promptParts = parsed.promptParts;
|
|
438
|
+
const fromStdin = await readStdinIfPiped();
|
|
439
|
+
const config = await loadConfig();
|
|
440
|
+
const { hosts: targets } = await discoverHosts({
|
|
441
|
+
hosts: config.hosts,
|
|
442
|
+
discovery: config.discovery,
|
|
443
|
+
});
|
|
444
|
+
let machineQuery = parsed.machine;
|
|
445
|
+
if (parsed.chatId) {
|
|
446
|
+
// Allow `ollanet prompt <host> --chat HASH "follow-up"` only when the
|
|
447
|
+
// leading token matches an already-discovered host (not direct-address fallback).
|
|
448
|
+
if (!machineQuery && promptParts.length >= 2) {
|
|
449
|
+
const hit = findDiscoveredHost(targets, promptParts[0]);
|
|
450
|
+
if (hit) {
|
|
451
|
+
machineQuery = promptParts[0];
|
|
452
|
+
promptParts = promptParts.slice(1);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
447
455
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
456
|
+
let peeledModel;
|
|
457
|
+
if (!parsed.chatId && !parsed.model) {
|
|
458
|
+
if (!machineQuery) {
|
|
459
|
+
console.error("Machine is required for a new chat (or use --chat <hash>).");
|
|
460
|
+
usage();
|
|
461
|
+
}
|
|
462
|
+
const host = resolveHost(targets, machineQuery);
|
|
463
|
+
const peeled = await maybePeelModel(host, promptParts, fromStdin);
|
|
464
|
+
peeledModel = peeled.model;
|
|
465
|
+
promptParts = peeled.promptParts;
|
|
466
|
+
}
|
|
467
|
+
const promptText = [promptParts.join(" ").trim(), fromStdin].filter(Boolean).join("\n\n").trim();
|
|
468
|
+
if (!promptText) {
|
|
469
|
+
console.error("No prompt provided (pass args and/or pipe stdin).");
|
|
470
|
+
usage();
|
|
471
|
+
}
|
|
472
|
+
try {
|
|
473
|
+
const result = await runPrompt({
|
|
474
|
+
machine: machineQuery,
|
|
475
|
+
model: parsed.model ?? peeledModel,
|
|
476
|
+
prompt: promptText,
|
|
477
|
+
chatId: parsed.chatId,
|
|
478
|
+
save,
|
|
479
|
+
settings: parsed.settings,
|
|
480
|
+
writeStdout: !json,
|
|
481
|
+
stream: json ? false : stream,
|
|
482
|
+
quiet: json,
|
|
483
|
+
});
|
|
484
|
+
if (json) {
|
|
485
|
+
console.log(JSON.stringify({
|
|
486
|
+
chat_id: result.chat_id,
|
|
487
|
+
topic: result.topic,
|
|
488
|
+
content: result.content,
|
|
489
|
+
thinking: result.thinking,
|
|
490
|
+
ollama: result.ollama,
|
|
491
|
+
}, null, 2));
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
const chunk = result.ollama;
|
|
495
|
+
const stats = [
|
|
496
|
+
chunk.eval_count != null ? `${chunk.eval_count} tokens` : "",
|
|
497
|
+
chunk.eval_duration
|
|
498
|
+
? `${((Number(chunk.eval_count) || 0) / (Number(chunk.eval_duration) / 1e9)).toFixed(1)} tok/s`
|
|
499
|
+
: "",
|
|
500
|
+
chunk.total_duration ? formatDuration(Number(chunk.total_duration)) : "",
|
|
501
|
+
].filter(Boolean);
|
|
502
|
+
if (stats.length > 0) {
|
|
503
|
+
console.error(`(${stats.join(" · ")})`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
catch (err) {
|
|
507
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
508
|
+
if (message.startsWith("Machine is required") || message.startsWith("No prompt")) {
|
|
509
|
+
console.error(message);
|
|
510
|
+
usage();
|
|
511
|
+
}
|
|
512
|
+
throw err;
|
|
457
513
|
}
|
|
458
514
|
}
|
package/dist/scan.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scan reachable networks for Ollama servers and list available models.
|
|
3
|
+
*
|
|
4
|
+
* Usage: ollanet scan [--lan] [--json] [--all]
|
|
5
|
+
*/
|
|
6
|
+
import { type AppConfig } from "./config.js";
|
|
7
|
+
export interface ScanOptions {
|
|
8
|
+
includeOffline?: boolean;
|
|
9
|
+
/** TCP-scan local /24s. Opt-in; never the default. */
|
|
10
|
+
lanScan?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* In-memory config. When set, the config file (`~/.ollanet` / `OLLANET_CONFIG`)
|
|
13
|
+
* is not read. Omit to keep CLI/MCP file behavior.
|
|
14
|
+
*/
|
|
15
|
+
config?: Partial<AppConfig>;
|
|
16
|
+
}
|
|
17
|
+
export interface ScannedServer {
|
|
18
|
+
hostname: string;
|
|
19
|
+
dnsName: string;
|
|
20
|
+
ip: string;
|
|
21
|
+
port: number;
|
|
22
|
+
os: string;
|
|
23
|
+
source: string;
|
|
24
|
+
self: boolean;
|
|
25
|
+
endpoint: string;
|
|
26
|
+
models: Array<{
|
|
27
|
+
name: string;
|
|
28
|
+
size?: number;
|
|
29
|
+
modified_at?: string;
|
|
30
|
+
parameter_size?: string;
|
|
31
|
+
quantization_level?: string;
|
|
32
|
+
family?: string;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
export interface ScanPayload {
|
|
36
|
+
network: string;
|
|
37
|
+
sources: string[];
|
|
38
|
+
port: number;
|
|
39
|
+
scanned: number;
|
|
40
|
+
servers: ScannedServer[];
|
|
41
|
+
}
|
|
42
|
+
/** Programmatic scan used by CLI `--json`, MCP, and `import { scanNetwork } from "ollanet"`. */
|
|
43
|
+
export declare function scanNetwork(options?: ScanOptions): Promise<ScanPayload>;
|
|
44
|
+
export declare function main(): Promise<void>;
|
package/dist/scan.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
2
|
* Scan reachable networks for Ollama servers and list available models.
|
|
4
3
|
*
|
|
5
4
|
* Usage: ollanet scan [--lan] [--json] [--all]
|
|
6
5
|
*/
|
|
7
|
-
import { loadConfig } from "./config.js";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
import { configFromPartial, loadConfig } from "./config.js";
|
|
7
|
+
import { discoverHosts, envInt, ollamaPort, shortName, } from "./hosts.js";
|
|
8
|
+
function scanTimeoutMs() {
|
|
9
|
+
return envInt("OLLAMA_TIMEOUT_MS", 2500);
|
|
10
|
+
}
|
|
11
|
+
function scanConcurrency() {
|
|
12
|
+
return Math.max(1, envInt("OLLAMA_CONCURRENCY", 16));
|
|
13
|
+
}
|
|
11
14
|
function formatBytes(bytes) {
|
|
12
15
|
if (bytes == null || !Number.isFinite(bytes))
|
|
13
16
|
return "";
|
|
@@ -22,8 +25,9 @@ function formatBytes(bytes) {
|
|
|
22
25
|
}
|
|
23
26
|
async function fetchModels(host) {
|
|
24
27
|
const url = `http://${host.ip.includes(":") ? `[${host.ip}]` : host.ip}:${host.port}/api/tags`;
|
|
28
|
+
const timeoutMs = scanTimeoutMs();
|
|
25
29
|
const controller = new AbortController();
|
|
26
|
-
const timer = setTimeout(() => controller.abort(),
|
|
30
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
27
31
|
try {
|
|
28
32
|
const res = await fetch(url, {
|
|
29
33
|
signal: controller.signal,
|
|
@@ -66,7 +70,7 @@ async function scanHost(host) {
|
|
|
66
70
|
const name = err instanceof Error ? err.name : "";
|
|
67
71
|
const message = err instanceof Error ? err.message : String(err);
|
|
68
72
|
const error = name === "AbortError" || message.toLowerCase().includes("abort")
|
|
69
|
-
? `timeout after ${
|
|
73
|
+
? `timeout after ${scanTimeoutMs()}ms`
|
|
70
74
|
: message;
|
|
71
75
|
return { host, ok: false, models: [], error, url };
|
|
72
76
|
}
|
|
@@ -113,6 +117,49 @@ function printResults(results, networkLabel, sources) {
|
|
|
113
117
|
}
|
|
114
118
|
console.log(`Found ${found.length} Ollama server(s) with ${found.reduce((n, r) => n + r.models.length, 0)} model entrie(s).`);
|
|
115
119
|
}
|
|
120
|
+
function toPayload(results, networkLabel, sources) {
|
|
121
|
+
return {
|
|
122
|
+
network: networkLabel,
|
|
123
|
+
sources,
|
|
124
|
+
port: ollamaPort(),
|
|
125
|
+
scanned: results.length,
|
|
126
|
+
servers: results
|
|
127
|
+
.filter((r) => r.ok)
|
|
128
|
+
.map((r) => ({
|
|
129
|
+
hostname: r.host.hostname,
|
|
130
|
+
dnsName: r.host.dnsName,
|
|
131
|
+
ip: r.host.ip,
|
|
132
|
+
port: r.host.port,
|
|
133
|
+
os: r.host.os,
|
|
134
|
+
source: r.host.source,
|
|
135
|
+
self: r.host.isSelf,
|
|
136
|
+
endpoint: `http://${r.host.ip}:${r.host.port}`,
|
|
137
|
+
models: r.models.map((m) => ({
|
|
138
|
+
name: m.name,
|
|
139
|
+
size: m.size,
|
|
140
|
+
modified_at: m.modified_at,
|
|
141
|
+
parameter_size: m.details?.parameter_size,
|
|
142
|
+
quantization_level: m.details?.quantization_level,
|
|
143
|
+
family: m.details?.family,
|
|
144
|
+
})),
|
|
145
|
+
})),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/** Programmatic scan used by CLI `--json`, MCP, and `import { scanNetwork } from "ollanet"`. */
|
|
149
|
+
export async function scanNetwork(options = {}) {
|
|
150
|
+
const config = options.config ? configFromPartial(options.config) : await loadConfig();
|
|
151
|
+
const { hosts, sources, networkLabel } = await discoverHosts({
|
|
152
|
+
hosts: config.hosts,
|
|
153
|
+
discovery: config.discovery,
|
|
154
|
+
includeOffline: options.includeOffline ?? false,
|
|
155
|
+
lanScan: options.lanScan ?? false,
|
|
156
|
+
});
|
|
157
|
+
if (hosts.length === 0) {
|
|
158
|
+
throw new Error("No hosts to scan. Add config.hosts, set OLLANET_HOSTS, or enable Tailscale/LAN discovery.");
|
|
159
|
+
}
|
|
160
|
+
const results = await mapPool(hosts, scanConcurrency(), scanHost);
|
|
161
|
+
return toPayload(results, networkLabel, sources);
|
|
162
|
+
}
|
|
116
163
|
export async function main() {
|
|
117
164
|
const includeOffline = process.argv.includes("--all");
|
|
118
165
|
const jsonOut = process.argv.includes("--json");
|
|
@@ -129,34 +176,9 @@ export async function main() {
|
|
|
129
176
|
process.exitCode = 1;
|
|
130
177
|
return;
|
|
131
178
|
}
|
|
132
|
-
const results = await mapPool(hosts,
|
|
179
|
+
const results = await mapPool(hosts, scanConcurrency(), scanHost);
|
|
133
180
|
if (jsonOut) {
|
|
134
|
-
console.log(JSON.stringify(
|
|
135
|
-
network: networkLabel,
|
|
136
|
-
sources,
|
|
137
|
-
port: OLLAMA_PORT,
|
|
138
|
-
scanned: results.length,
|
|
139
|
-
servers: results
|
|
140
|
-
.filter((r) => r.ok)
|
|
141
|
-
.map((r) => ({
|
|
142
|
-
hostname: r.host.hostname,
|
|
143
|
-
dnsName: r.host.dnsName,
|
|
144
|
-
ip: r.host.ip,
|
|
145
|
-
port: r.host.port,
|
|
146
|
-
os: r.host.os,
|
|
147
|
-
source: r.host.source,
|
|
148
|
-
self: r.host.isSelf,
|
|
149
|
-
endpoint: `http://${r.host.ip}:${r.host.port}`,
|
|
150
|
-
models: r.models.map((m) => ({
|
|
151
|
-
name: m.name,
|
|
152
|
-
size: m.size,
|
|
153
|
-
modified_at: m.modified_at,
|
|
154
|
-
parameter_size: m.details?.parameter_size,
|
|
155
|
-
quantization_level: m.details?.quantization_level,
|
|
156
|
-
family: m.details?.family,
|
|
157
|
-
})),
|
|
158
|
-
})),
|
|
159
|
-
}, null, 2));
|
|
181
|
+
console.log(JSON.stringify(toPayload(results, networkLabel, sources), null, 2));
|
|
160
182
|
return;
|
|
161
183
|
}
|
|
162
184
|
printResults(results, networkLabel, sources);
|
package/package.json
CHANGED
|
@@ -1,28 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ollanet",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Discover and talk to Ollama on any reachable network — CLI, MCP, and a Node library",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"ollanet": "./dist/cli.js"
|
|
8
8
|
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./scan": {
|
|
15
|
+
"types": "./dist/scan.d.ts",
|
|
16
|
+
"import": "./dist/scan.js"
|
|
17
|
+
},
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
9
21
|
"files": [
|
|
10
22
|
"dist",
|
|
11
23
|
"README.md",
|
|
12
24
|
"LICENSE"
|
|
13
25
|
],
|
|
14
26
|
"scripts": {
|
|
15
|
-
"build": "tsc -p tsconfig.build.json",
|
|
16
|
-
"prepare": "tsc -p tsconfig.build.json",
|
|
27
|
+
"build": "tsc -p tsconfig.build.json && node scripts/fix-dts-extensions.mjs",
|
|
28
|
+
"prepare": "tsc -p tsconfig.build.json && node scripts/fix-dts-extensions.mjs",
|
|
17
29
|
"ollanet": "tsx src/cli.ts",
|
|
18
30
|
"scan": "tsx src/cli.ts scan",
|
|
19
31
|
"prompt": "tsx src/cli.ts prompt",
|
|
20
32
|
"chats": "tsx src/cli.ts chats",
|
|
21
33
|
"bench": "tsx src/cli.ts bench",
|
|
34
|
+
"mcp": "tsx src/cli.ts mcp",
|
|
22
35
|
"start": "tsx src/cli.ts scan",
|
|
23
36
|
"typecheck": "tsc --noEmit",
|
|
24
|
-
"test": "tsc -p tsconfig.build.json && node --test test/*.test.mjs",
|
|
25
|
-
"test:only": "node --test test/*.test.mjs"
|
|
37
|
+
"test": "tsc -p tsconfig.build.json && node scripts/fix-dts-extensions.mjs && node --test test/*.test.mjs",
|
|
38
|
+
"test:only": "node --test test/*.test.mjs",
|
|
39
|
+
"site:dev": "pnpm --dir site dev",
|
|
40
|
+
"site:build": "pnpm --dir site build",
|
|
41
|
+
"site:deploy": "pnpm --dir site deploy"
|
|
26
42
|
},
|
|
27
43
|
"keywords": [
|
|
28
44
|
"ollama",
|
|
@@ -31,7 +47,10 @@
|
|
|
31
47
|
"chat",
|
|
32
48
|
"lan",
|
|
33
49
|
"tailscale",
|
|
34
|
-
"network"
|
|
50
|
+
"network",
|
|
51
|
+
"mcp",
|
|
52
|
+
"model-context-protocol",
|
|
53
|
+
"library"
|
|
35
54
|
],
|
|
36
55
|
"author": "Catalyst Forge LLC",
|
|
37
56
|
"license": "MIT",
|
|
@@ -39,7 +58,7 @@
|
|
|
39
58
|
"type": "git",
|
|
40
59
|
"url": "git+https://github.com/Catalyst-Forge-LLC/ollanet.git"
|
|
41
60
|
},
|
|
42
|
-
"homepage": "https://
|
|
61
|
+
"homepage": "https://ollanet.dev",
|
|
43
62
|
"bugs": {
|
|
44
63
|
"url": "https://github.com/Catalyst-Forge-LLC/ollanet/issues"
|
|
45
64
|
},
|