electron-ollama
Version:
Bundle Ollama with your Electron.js app for seamless user experience
240 lines • 9.52 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ElectronOllama = exports.ElectronOllamaServer = void 0;
const path = __importStar(require("path"));
const fs = __importStar(require("fs/promises"));
const os = __importStar(require("os"));
const github_fetch_1 = require("./github-fetch");
const unzip_1 = require("./unzip");
const untgz_1 = require("./untgz");
const server_1 = require("./server");
Object.defineProperty(exports, "ElectronOllamaServer", { enumerable: true, get: function () { return server_1.ElectronOllamaServer; } });
const stream_1 = require("stream");
class ElectronOllama {
constructor(config) {
this.config = {
directory: 'electron-ollama',
...config,
};
}
/**
* Get the current platform configuration
*/
currentPlatformConfig() {
const platform = os.platform();
const arch = os.arch();
let osType;
let architecture;
// Map platform
switch (platform) {
case 'win32':
osType = 'windows';
break;
case 'darwin':
osType = 'darwin';
break;
case 'linux':
osType = 'linux';
break;
default:
throw new Error(`Unsupported platform: ${platform}`);
}
// Map architecture
switch (arch) {
case 'arm64':
architecture = 'arm64';
break;
case 'x64':
architecture = 'amd64';
break;
default:
throw new Error(`Unsupported architecture: ${arch}`);
}
return {
os: osType,
arch: architecture,
};
}
/**
* Get the name of the asset for the given platform configuration (e.g. "ollama-windows-amd64.zip" or "ollama-darwin.tgz")
*/
getAssetName(platformConfig) {
const { os, arch: architecture } = platformConfig;
switch (os) {
case 'windows':
return `ollama-windows-${architecture}.zip`;
case 'darwin':
return 'ollama-darwin.tgz';
case 'linux':
return `ollama-linux-${architecture}.tgz`;
}
}
/**
* Get metadata for a specific version ('latest' by default) and platform
*/
async getMetadata(version = 'latest', platformConfig = this.currentPlatformConfig()) {
const { os, arch: architecture } = platformConfig;
const releaseUrlPath = version === 'latest' ? `latest` : `tags/${version}`;
const gitHubResponse = await (0, github_fetch_1.githubFetch)(`https://api.github.com/repos/ollama/ollama/releases/${releaseUrlPath}`);
const releaseData = await gitHubResponse.json();
const assetName = this.getAssetName(platformConfig);
const asset = releaseData.assets.find((asset) => asset.name === assetName);
if (!asset) {
throw new Error(`${os}-${architecture} is not supported by Ollama ${releaseData.tag_name}`);
}
return {
digest: asset.digest,
size: asset.size,
fileName: asset.name,
contentType: asset.content_type,
version: releaseData.tag_name,
downloads: asset.download_count,
downloadUrl: asset.browser_download_url,
releaseUrl: releaseData.html_url,
body: releaseData.body,
};
}
/**
* Download Ollama for the specified version ('latest' by default) and platform
*/
async download(version = 'latest', platformConfig = this.currentPlatformConfig()) {
const metadata = await this.getMetadata(version, platformConfig);
const versionDir = this.getBinPath(metadata.version, platformConfig);
// 1. Create directory if it doesn't exist
console.log('Creating directory if it doesn\'t exist');
await fs.mkdir(versionDir, { recursive: true });
// 2. Download the file
console.log(`Downloading file to ${versionDir}`);
const response = await fetch(metadata.downloadUrl);
// 3. Extract the archive
console.log(`Extracting archive ${metadata.fileName} in ${versionDir}`);
if (metadata.contentType === 'application/zip') {
const buffer = await response.arrayBuffer();
await fs.writeFile(path.join(versionDir, metadata.fileName), Buffer.from(buffer));
await (0, unzip_1.unzipFile)(path.join(versionDir, metadata.fileName), versionDir, true);
}
else if (['application/x-gtar', 'application/x-tar', 'application/x-gzip', 'application/tar', 'application/gzip', 'application/x-tgz'].includes(metadata.contentType)) {
const stream = stream_1.Readable.from(response.body);
await (0, untgz_1.untgzStream)(stream, versionDir);
}
else {
throw new Error(`The Ollama asset type ${metadata.contentType} is not supported`);
}
console.log(`Extracted archive ${metadata.fileName} to ${versionDir}`);
// 4. Verify checksum
}
/**
* Check if a version is downloaded for the given platform configuration
*/
async isDownloaded(version, platformConfig = this.currentPlatformConfig()) {
const binPath = this.getBinPath(version, platformConfig);
return fs.access(binPath).then(() => true).catch(() => false);
}
/**
* List all downloaded versions for the given platform configuration
*/
async downloadedVersions(platformConfig = this.currentPlatformConfig()) {
let versions = [];
try {
versions = await fs.readdir(path.join(this.config.basePath, this.config.directory));
}
catch {
return []; // directory does not exist - nothing to list
}
const downloaded = await Promise.all(versions.map((version) => this.isDownloaded(version, platformConfig)));
return versions.filter((_version, index) => downloaded[index]);
}
/**
* Get the path to the directory for the given version and platform configuration
*/
getBinPath(version, platformConfig = this.currentPlatformConfig()) {
return path.join(this.config.basePath, this.config.directory, version, platformConfig.os, platformConfig.arch);
}
/**
* Get the name of the executable for the given platform configuration
*/
getExecutableName(platformConfig) {
switch (platformConfig.os) {
case 'windows':
return 'ollama.exe';
case 'darwin':
return 'ollama';
case 'linux':
return 'bin/ollama';
}
}
/**
* Start serving Ollama with the specified version
*/
async serve(version) {
const platformConfig = this.currentPlatformConfig();
const binPath = this.getBinPath(version, platformConfig);
// Ensure the binary exists
if (!await this.isDownloaded(version, platformConfig)) {
await this.download(version, platformConfig);
}
const server = new server_1.ElectronOllamaServer({
binPath,
log: this.config.serveLog || (() => { }),
});
server.start(this.getExecutableName(platformConfig));
// Wait for the server to start
for (let i = 0; i < 50; i++) {
await new Promise(resolve => setTimeout(resolve, 100));
if (await this.isRunning()) {
return server;
}
}
throw new Error('Ollama server failed to start in 5 seconds');
}
/**
* Check if Ollama is running
*/
async isRunning() {
try {
const response = await fetch('http://localhost:11434');
const text = await response.text();
return text.includes('Ollama is running');
}
catch {
return false;
}
}
}
exports.ElectronOllama = ElectronOllama;
// Export default instance
exports.default = ElectronOllama;
//# sourceMappingURL=index.js.map