@vtstech/pi-ollama-sync 1.0.3

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.
Files changed (2) hide show
  1. package/ollama-sync.js +207 -0
  2. package/package.json +24 -0
package/ollama-sync.js ADDED
@@ -0,0 +1,207 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // .build-npm/ollama-sync/ollama-sync.temp.ts
20
+ var ollama_sync_temp_exports = {};
21
+ __export(ollama_sync_temp_exports, {
22
+ default: () => ollama_sync_temp_default
23
+ });
24
+ module.exports = __toCommonJS(ollama_sync_temp_exports);
25
+ var import_ollama = require("@vtstech/pi-shared/ollama");
26
+ var import_format = require("@vtstech/pi-shared/format");
27
+ var BRANDING = [
28
+ ` \u26A1 Pi Ollama Sync v1.0.3`,
29
+ ` Written by VTSTech`,
30
+ ` GitHub: https://github.com/VTSTech`,
31
+ ` Website: www.vts-tech.org`
32
+ ].join("\n");
33
+ function getProviderConfig(existing) {
34
+ const defaultUrl = (0, import_ollama.getOllamaBaseUrl)();
35
+ const ollama = existing.providers["ollama"];
36
+ return {
37
+ baseUrl: ollama?.baseUrl ?? defaultUrl + "/v1",
38
+ api: ollama?.api ?? "openai-completions",
39
+ apiKey: ollama?.apiKey ?? "ollama",
40
+ compat: ollama?.compat ?? {
41
+ supportsDeveloperRole: false,
42
+ supportsReasoningEffort: false
43
+ }
44
+ };
45
+ }
46
+ function buildModelEntry(m) {
47
+ return {
48
+ id: m.name,
49
+ reasoning: (0, import_ollama.isReasoningModel)(m.name),
50
+ parameterSize: m.details.parameter_size,
51
+ quantizationLevel: m.details.quantization_level,
52
+ modelFamily: m.details.family || m.details.families?.[0] || "unknown"
53
+ };
54
+ }
55
+ function mergeModels(newModels, oldModels) {
56
+ const oldModelMap = new Map(oldModels.map((m) => [m.id, m]));
57
+ return newModels.map((m) => {
58
+ const old = oldModelMap.get(m.id);
59
+ if (old) {
60
+ const merged = { ...m };
61
+ for (const [k, v] of Object.entries(old)) {
62
+ if (!(k in m)) merged[k] = v;
63
+ }
64
+ return merged;
65
+ }
66
+ return m;
67
+ });
68
+ }
69
+ function ollama_sync_temp_default(pi) {
70
+ pi.registerCommand("ollama-sync", {
71
+ description: "Sync models from Ollama into models.json. Use: /ollama-sync [url]",
72
+ getArgumentCompletions: async () => {
73
+ const url = (0, import_ollama.getOllamaBaseUrl)();
74
+ return [
75
+ { value: url, label: url, description: "Default Ollama URL..." }
76
+ ];
77
+ },
78
+ async handler(args, ctx) {
79
+ const arg = args.trim();
80
+ const overrideUrl = arg || void 0;
81
+ ctx.ui.setStatus("ollama-sync", "Fetching models from Ollama...");
82
+ try {
83
+ const existing = (0, import_ollama.readModelsJson)();
84
+ const config = getProviderConfig(existing);
85
+ const ollamaBaseUrl = overrideUrl ? overrideUrl.replace(/\/v1$/, "").replace(/\/+$/, "") : config.baseUrl?.replace(/\/v1$/, "") ?? (0, import_ollama.getOllamaBaseUrl)();
86
+ const models = await (0, import_ollama.fetchOllamaModels)(ollamaBaseUrl);
87
+ if (models.length === 0) {
88
+ ctx.ui.notify("No models found in Ollama", "info");
89
+ ctx.ui.setStatus("ollama-sync", void 0);
90
+ return;
91
+ }
92
+ const sorted = [...models].sort((a, b) => a.size - b.size);
93
+ const newModels = sorted.map(buildModelEntry);
94
+ const oldIds = new Set(
95
+ existing.providers["ollama"]?.models?.map((m) => m.id) ?? []
96
+ );
97
+ const added = newModels.filter((m) => !oldIds.has(m.id));
98
+ const removed = [...oldIds].filter((id) => !newModels.some((m) => m.id === id));
99
+ const mergedModels = mergeModels(
100
+ newModels,
101
+ existing.providers["ollama"]?.models ?? []
102
+ );
103
+ existing.providers["ollama"] = {
104
+ ...config,
105
+ baseUrl: ollamaBaseUrl + "/v1",
106
+ models: mergedModels
107
+ };
108
+ (0, import_ollama.writeModelsJson)(existing);
109
+ const lines = [""];
110
+ lines.push(` Ollama: ${ollamaBaseUrl}`);
111
+ lines.push(` Synced ${newModels.length} models from Ollama`);
112
+ lines.push((0, import_format.section)("Synced Models"));
113
+ for (const m of newModels) {
114
+ lines.push((0, import_format.ok)(m.id));
115
+ lines.push(
116
+ ` Params: ${m.parameterSize ?? "?"} \xB7 Quant: ${m.quantizationLevel ?? "?"} \xB7 Family: ${m.modelFamily ?? "?"}`
117
+ );
118
+ }
119
+ if (added.length > 0 || removed.length > 0) {
120
+ lines.push((0, import_format.section)("Changes"));
121
+ if (added.length > 0) {
122
+ lines.push((0, import_format.ok)(`Added ${added.length}: ${added.map((m) => m.id).join(", ")}`));
123
+ }
124
+ if (removed.length > 0) {
125
+ lines.push((0, import_format.warn)(`Removed ${removed.length}: ${removed.join(", ")}`));
126
+ }
127
+ } else {
128
+ lines.push((0, import_format.info)("No changes \u2014 already in sync"));
129
+ }
130
+ lines.push("");
131
+ lines.push(` Written to ${import_ollama.MODELS_JSON_PATH}`);
132
+ lines.push(` Run /reload to pick up changes`);
133
+ lines.push(BRANDING);
134
+ const report = lines.join("\n");
135
+ const summary = [`Synced ${newModels.length} models`];
136
+ if (added.length > 0) summary.push(`+${added.map((m) => m.id).join(", ")}`);
137
+ if (removed.length > 0) summary.push(`-${removed.join(", ")}`);
138
+ ctx.ui.notify(summary.join(" \xB7 "), "success");
139
+ pi.sendMessage({
140
+ customType: "ollama-sync-report",
141
+ content: report,
142
+ display: { type: "content", content: report },
143
+ details: { timestamp: (/* @__PURE__ */ new Date()).toISOString(), added: added.length, removed: removed.length }
144
+ });
145
+ } catch (err) {
146
+ ctx.ui.notify(`Failed: ${err.message}`, "error");
147
+ }
148
+ ctx.ui.setStatus("ollama-sync", void 0);
149
+ }
150
+ });
151
+ pi.registerTool({
152
+ name: "ollama_sync",
153
+ label: "Ollama Sync",
154
+ description: "Sync available models from an Ollama instance into Pi's models.json config file. Supports local or remote Ollama.\n\n" + BRANDING,
155
+ parameters: {
156
+ type: "object",
157
+ properties: {
158
+ url: {
159
+ type: "string",
160
+ description: "Ollama base URL (e.g. http://192.168.1.100:11434). If omitted, uses models.json or OLLAMA_HOST env var."
161
+ }
162
+ }
163
+ },
164
+ async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
165
+ const overrideUrl = params?.url;
166
+ const existing = (0, import_ollama.readModelsJson)();
167
+ const config = getProviderConfig(existing);
168
+ const ollamaBaseUrl = overrideUrl ? overrideUrl.replace(/\/v1$/, "").replace(/\/+$/, "") : config.baseUrl?.replace(/\/v1$/, "") ?? (0, import_ollama.getOllamaBaseUrl)();
169
+ try {
170
+ const models = await (0, import_ollama.fetchOllamaModels)(ollamaBaseUrl);
171
+ const sorted = [...models].sort((a, b) => a.size - b.size);
172
+ const newModels = sorted.map(buildModelEntry);
173
+ const mergedModels = mergeModels(
174
+ newModels,
175
+ existing.providers["ollama"]?.models ?? []
176
+ );
177
+ existing.providers["ollama"] = {
178
+ ...config,
179
+ baseUrl: ollamaBaseUrl + "/v1",
180
+ models: mergedModels
181
+ };
182
+ (0, import_ollama.writeModelsJson)(existing);
183
+ const modelDetails = newModels.map(
184
+ (m) => ` \u2022 ${m.id} (${m.parameterSize}, ${m.quantizationLevel}, family: ${m.modelFamily})`
185
+ ).join("\n");
186
+ return {
187
+ content: [
188
+ {
189
+ type: "text",
190
+ text: `${BRANDING}
191
+
192
+ Synced ${newModels.length} models from ${ollamaBaseUrl} to ${import_ollama.MODELS_JSON_PATH}. Run /reload to pick up changes.
193
+
194
+ ${modelDetails}`
195
+ }
196
+ ],
197
+ details: { models: newModels }
198
+ };
199
+ } catch (err) {
200
+ return {
201
+ content: [{ type: "text", text: `Error: ${err.message}` }],
202
+ details: {}
203
+ };
204
+ }
205
+ }
206
+ });
207
+ }
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@vtstech/pi-ollama-sync",
3
+ "version": "1.0.3",
4
+ "description": "Ollama model sync extension for Pi Coding Agent",
5
+ "main": "ollama-sync.js",
6
+ "keywords": ["pi-package", "pi-extensions"],
7
+ "license": "MIT",
8
+ "access": "public",
9
+ "author": "VTSTech",
10
+ "homepage": "https://www.vts-tech.org",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/VTSTech/pi-coding-agent"
14
+ },
15
+ "dependencies": {
16
+ "@vtstech/pi-shared": "1.0.3"
17
+ },
18
+ "peerDependencies": {
19
+ "@mariozechner/pi-coding-agent": ">=0.66"
20
+ },
21
+ "pi": {
22
+ "extensions": ["./ollama-sync.js"]
23
+ }
24
+ }