loren-code 0.1.4 → 0.2.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 +9 -3
- package/package.json +3 -1
- package/scripts/claude-wrapper.js +4 -3
- package/scripts/install-claude-ollama.ps1 +32 -6
- package/scripts/loren.js +34 -4
- package/scripts/postinstall.js +16 -0
- package/scripts/uninstall-claude-ollama.ps1 +2 -1
- package/src/bootstrap.js +14 -3
- package/src/config.js +4 -4
- package/src/logger.js +9 -8
- package/src/paths.js +22 -0
- package/src/server.js +4 -3
package/README.md
CHANGED
|
@@ -20,7 +20,13 @@ loren help
|
|
|
20
20
|
|
|
21
21
|
## First Run
|
|
22
22
|
|
|
23
|
-
Loren
|
|
23
|
+
Loren stores user config in `%USERPROFILE%\.lorencode`.
|
|
24
|
+
|
|
25
|
+
On first run it creates:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
C:\Users\<you>\.lorencode\.env.local
|
|
29
|
+
```
|
|
24
30
|
|
|
25
31
|
You must add valid `OLLAMA_API_KEYS` before the bridge can make upstream requests.
|
|
26
32
|
If you configure multiple keys, Loren rotates them automatically.
|
|
@@ -107,11 +113,11 @@ npm.cmd install -g loren-code
|
|
|
107
113
|
|
|
108
114
|
### Missing API keys
|
|
109
115
|
|
|
110
|
-
Populate `OLLAMA_API_KEYS` in
|
|
116
|
+
Populate `OLLAMA_API_KEYS` in `%USERPROFILE%\.lorencode\.env.local`.
|
|
111
117
|
|
|
112
118
|
### Port already in use
|
|
113
119
|
|
|
114
|
-
Change `BRIDGE_PORT` in
|
|
120
|
+
Change `BRIDGE_PORT` in `%USERPROFILE%\.lorencode\.env.local`.
|
|
115
121
|
|
|
116
122
|
## Repository
|
|
117
123
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loren-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Ollama Cloud Model Manager - Dynamic model switching, API key rotation, and real-time configuration updates",
|
|
5
5
|
"author": "lorenzune",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"scripts/claude-wrapper.js",
|
|
34
34
|
"scripts/install-claude-ollama.ps1",
|
|
35
35
|
"scripts/loren.js",
|
|
36
|
+
"scripts/postinstall.js",
|
|
36
37
|
"scripts/uninstall-claude-ollama.ps1",
|
|
37
38
|
"src/*.js",
|
|
38
39
|
".env.example",
|
|
@@ -57,6 +58,7 @@
|
|
|
57
58
|
"check:publish": "node scripts/publish-check.js",
|
|
58
59
|
"prepack": "node scripts/sync-readme.js npm",
|
|
59
60
|
"postpack": "node scripts/sync-readme.js github",
|
|
61
|
+
"postinstall": "node scripts/postinstall.js",
|
|
60
62
|
"prepublishOnly": "npm test && npm run lint",
|
|
61
63
|
"test": "node scripts/publish-check.js",
|
|
62
64
|
"lint": "node -e \"console.log('No linter configured')\""
|
|
@@ -5,18 +5,19 @@ import process from "node:process";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { ensureEnvLocal, ensureRuntimeDir, getBridgeBaseUrl } from "../src/bootstrap.js";
|
|
7
7
|
import { loadConfig } from "../src/config.js";
|
|
8
|
+
import { getEnvFilePath, getRuntimeDir } from "../src/paths.js";
|
|
8
9
|
|
|
9
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
11
|
const __dirname = path.dirname(__filename);
|
|
11
12
|
const repoRoot = path.resolve(__dirname, "..");
|
|
12
|
-
const stateDir =
|
|
13
|
+
const stateDir = getRuntimeDir();
|
|
13
14
|
const bridgePidPath = path.join(stateDir, "bridge.pid");
|
|
14
15
|
const bridgeLogPath = path.join(stateDir, "bridge.log");
|
|
15
|
-
const envFilePath =
|
|
16
|
+
const envFilePath = getEnvFilePath();
|
|
16
17
|
|
|
17
18
|
async function main() {
|
|
18
19
|
process.chdir(repoRoot);
|
|
19
|
-
ensureRuntimeDir(
|
|
20
|
+
ensureRuntimeDir();
|
|
20
21
|
ensureEnvLocal(repoRoot);
|
|
21
22
|
const bridgeConfig = loadConfig();
|
|
22
23
|
const bridgeBaseUrl = getBridgeBaseUrl(bridgeConfig);
|
|
@@ -3,13 +3,16 @@ $ErrorActionPreference = "Stop"
|
|
|
3
3
|
$repoRoot = Split-Path -Parent $PSScriptRoot
|
|
4
4
|
$userProfile = [Environment]::GetFolderPath("UserProfile")
|
|
5
5
|
$appData = [Environment]::GetFolderPath("ApplicationData")
|
|
6
|
+
$lorenHome = if ($env:LOREN_HOME) { $env:LOREN_HOME } else { Join-Path $userProfile ".lorencode" }
|
|
6
7
|
$workspaceSettingsDir = Join-Path $appData "Code\\User"
|
|
7
8
|
$workspaceSettingsPath = Join-Path $workspaceSettingsDir "settings.json"
|
|
8
9
|
$claudeDir = Join-Path $userProfile ".claude"
|
|
9
10
|
$claudeSettingsPath = Join-Path $claudeDir "settings.json"
|
|
10
11
|
$launcherSourcePath = Join-Path $repoRoot "scripts\\ClaudeWrapperLauncher.cs"
|
|
11
12
|
$launcherExePath = Join-Path $repoRoot "scripts\\ClaudeWrapperLauncher.exe"
|
|
12
|
-
$
|
|
13
|
+
$envTemplatePath = Join-Path $repoRoot ".env.example"
|
|
14
|
+
$legacyEnvPath = Join-Path $repoRoot ".env.local"
|
|
15
|
+
$envPath = Join-Path $lorenHome ".env.local"
|
|
13
16
|
$npmBinDir = Join-Path $appData "npm"
|
|
14
17
|
$claudeCmdPath = Join-Path $npmBinDir "claude.cmd"
|
|
15
18
|
$claudeShellPath = Join-Path $npmBinDir "claude"
|
|
@@ -18,14 +21,21 @@ $claudeCmdBackupPath = Join-Path $npmBinDir "claude.loren-backup.cmd"
|
|
|
18
21
|
$claudeShellBackupPath = Join-Path $npmBinDir "claude.loren-backup"
|
|
19
22
|
$claudePs1BackupPath = Join-Path $npmBinDir "claude.loren-backup.ps1"
|
|
20
23
|
|
|
21
|
-
if (-not (Test-Path $envPath)) {
|
|
22
|
-
throw ".env.local not found. Create it first with OLLAMA_API_KEYS."
|
|
23
|
-
}
|
|
24
|
-
|
|
25
24
|
New-Item -ItemType Directory -Force -Path $workspaceSettingsDir | Out-Null
|
|
26
25
|
New-Item -ItemType Directory -Force -Path $claudeDir | Out-Null
|
|
26
|
+
New-Item -ItemType Directory -Force -Path $lorenHome | Out-Null
|
|
27
27
|
New-Item -ItemType Directory -Force -Path $npmBinDir | Out-Null
|
|
28
28
|
|
|
29
|
+
if (-not (Test-Path $envPath)) {
|
|
30
|
+
if (Test-Path $legacyEnvPath) {
|
|
31
|
+
Copy-Item -LiteralPath $legacyEnvPath -Destination $envPath -Force
|
|
32
|
+
} elseif (Test-Path $envTemplatePath) {
|
|
33
|
+
Copy-Item -LiteralPath $envTemplatePath -Destination $envPath -Force
|
|
34
|
+
} else {
|
|
35
|
+
Set-Content -LiteralPath $envPath -Value "OLLAMA_API_KEYS=`nBRIDGE_HOST=127.0.0.1`nBRIDGE_PORT=8788`n" -Encoding UTF8
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
29
39
|
function Read-JsonFile {
|
|
30
40
|
param([string]$Path)
|
|
31
41
|
|
|
@@ -197,6 +207,10 @@ $bridgeBaseUrl = "http://${bridgeHost}:${bridgePort}"
|
|
|
197
207
|
$workspaceSettings["claudeCode.claudeProcessWrapper"] = $launcherExePath
|
|
198
208
|
$workspaceSettings["claudeCode.disableLoginPrompt"] = $true
|
|
199
209
|
$workspaceSettings["claudeCode.environmentVariables"] = @(
|
|
210
|
+
@{
|
|
211
|
+
name = "LOREN_HOME"
|
|
212
|
+
value = $lorenHome
|
|
213
|
+
},
|
|
200
214
|
@{
|
|
201
215
|
name = "ANTHROPIC_BASE_URL"
|
|
202
216
|
value = $bridgeBaseUrl
|
|
@@ -233,7 +247,17 @@ if ($availableModels.Count -eq 0) {
|
|
|
233
247
|
throw "OLLAMA_MODEL_ALIASES does not contain any models"
|
|
234
248
|
}
|
|
235
249
|
|
|
236
|
-
$
|
|
250
|
+
$configuredDefaultModel = Get-EnvValue -Path $envPath -Name "DEFAULT_MODEL_ALIAS"
|
|
251
|
+
if (
|
|
252
|
+
-not [string]::IsNullOrWhiteSpace($configuredDefaultModel) -and
|
|
253
|
+
$availableModels.Contains($configuredDefaultModel)
|
|
254
|
+
) {
|
|
255
|
+
$defaultModel = $configuredDefaultModel
|
|
256
|
+
} elseif ($aliases.ContainsKey("ollama-free-auto")) {
|
|
257
|
+
$defaultModel = "ollama-free-auto"
|
|
258
|
+
} else {
|
|
259
|
+
$defaultModel = $availableModels[0]
|
|
260
|
+
}
|
|
237
261
|
$claudeSettings["model"] = $defaultModel
|
|
238
262
|
$claudeSettings["availableModels"] = $availableModels
|
|
239
263
|
Write-JsonFile -Path $claudeSettingsPath -Data $claudeSettings
|
|
@@ -261,9 +285,11 @@ $ps1Content = @"
|
|
|
261
285
|
Set-Content -LiteralPath $claudePs1Path -Value $ps1Content -Encoding UTF8
|
|
262
286
|
|
|
263
287
|
Write-Host "Installation completed."
|
|
288
|
+
Write-Host "Loren home:" $lorenHome
|
|
264
289
|
Write-Host "Claude launcher:" $launcherExePath
|
|
265
290
|
Write-Host "VS Code user settings:" $workspaceSettingsPath
|
|
266
291
|
Write-Host "Claude user settings:" $claudeSettingsPath
|
|
292
|
+
Write-Host "Loren config:" $envPath
|
|
267
293
|
Write-Host "Global Claude command:" $claudeCmdPath
|
|
268
294
|
Write-Host ""
|
|
269
295
|
Write-Host "Restart VS Code. Claude Code will use the bridge in any project."
|
package/scripts/loren.js
CHANGED
|
@@ -5,11 +5,13 @@ import { execFileSync, spawn } from "node:child_process";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { loadConfig, loadEnvFile, saveEnvFile } from "../src/config.js";
|
|
7
7
|
import { ensureEnvLocal, ensureRuntimeDir, getBridgeBaseUrl } from "../src/bootstrap.js";
|
|
8
|
+
import { getEnvFilePath, getLorenHome, getRuntimeDir } from "../src/paths.js";
|
|
8
9
|
|
|
9
10
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
11
|
const projectRoot = path.resolve(__dirname, "..");
|
|
11
|
-
const
|
|
12
|
-
const
|
|
12
|
+
const lorenHome = getLorenHome();
|
|
13
|
+
const envFilePath = getEnvFilePath();
|
|
14
|
+
const runtimeDir = getRuntimeDir();
|
|
13
15
|
const pidFilePath = path.join(runtimeDir, "loren.pid");
|
|
14
16
|
const logFilePath = path.join(runtimeDir, "bridge.log");
|
|
15
17
|
const errorLogFilePath = path.join(runtimeDir, "bridge.err.log");
|
|
@@ -18,8 +20,8 @@ const claudeSettingsPath = path.join(userHome, ".claude", "settings.json");
|
|
|
18
20
|
|
|
19
21
|
// Force working directory to project root for config loading
|
|
20
22
|
process.chdir(projectRoot);
|
|
21
|
-
ensureRuntimeDir(
|
|
22
|
-
ensureEnvLocal(projectRoot);
|
|
23
|
+
const runtimePath = ensureRuntimeDir();
|
|
24
|
+
const envStatus = ensureEnvLocal(projectRoot);
|
|
23
25
|
|
|
24
26
|
const ASCII_LOGO = `
|
|
25
27
|
██╗ ██████╗ ██████╗ ███████╗███╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗
|
|
@@ -56,9 +58,11 @@ const COMMANDS = {
|
|
|
56
58
|
function main() {
|
|
57
59
|
const args = process.argv.slice(2);
|
|
58
60
|
const [command] = args;
|
|
61
|
+
const config = loadConfig();
|
|
59
62
|
|
|
60
63
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
61
64
|
printHelp();
|
|
65
|
+
maybePrintSetupHint(config);
|
|
62
66
|
process.exit(0);
|
|
63
67
|
}
|
|
64
68
|
|
|
@@ -336,6 +340,9 @@ function showConfig() {
|
|
|
336
340
|
|
|
337
341
|
console.log("\nCurrent Configuration:");
|
|
338
342
|
console.log("─".repeat(40));
|
|
343
|
+
console.log(` Home: ${lorenHome}`);
|
|
344
|
+
console.log(` Env File: ${envFilePath}`);
|
|
345
|
+
console.log(` Runtime: ${runtimePath}`);
|
|
339
346
|
console.log(` Host: ${config.host}`);
|
|
340
347
|
console.log(` Port: ${config.port}`);
|
|
341
348
|
console.log(` Upstream: ${config.upstreamBaseUrl}`);
|
|
@@ -497,6 +504,29 @@ function syncClaudeSelectedModel(model) {
|
|
|
497
504
|
fs.writeFileSync(claudeSettingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf8");
|
|
498
505
|
}
|
|
499
506
|
|
|
507
|
+
function maybePrintSetupHint(config) {
|
|
508
|
+
if (!envStatus.created && config.apiKeys.length > 0) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
console.log("Setup:");
|
|
513
|
+
console.log(` Loren home: ${lorenHome}`);
|
|
514
|
+
console.log(` Config file: ${envFilePath}`);
|
|
515
|
+
|
|
516
|
+
if (envStatus.migrated) {
|
|
517
|
+
console.log(" Existing configuration was migrated automatically.");
|
|
518
|
+
} else if (envStatus.created) {
|
|
519
|
+
console.log(" A new config file was created automatically.");
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (config.apiKeys.length === 0) {
|
|
523
|
+
console.log(" Add OLLAMA_API_KEYS to finish setup.");
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
console.log(" Then run: loren start");
|
|
527
|
+
console.log("");
|
|
528
|
+
}
|
|
529
|
+
|
|
500
530
|
// ============== HELP ==============
|
|
501
531
|
|
|
502
532
|
function printHelp() {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { getEnvFilePath, getLorenHome } from "../src/paths.js";
|
|
3
|
+
|
|
4
|
+
console.log("");
|
|
5
|
+
console.log("Loren Code installed.");
|
|
6
|
+
console.log(`Loren home: ${getLorenHome()}`);
|
|
7
|
+
console.log(`Config file: ${getEnvFilePath()}`);
|
|
8
|
+
console.log("");
|
|
9
|
+
console.log("Next steps:");
|
|
10
|
+
console.log(" 1. Run: loren");
|
|
11
|
+
console.log(" 2. Add your OLLAMA_API_KEYS");
|
|
12
|
+
console.log(" 3. Start the bridge with: loren start");
|
|
13
|
+
console.log("");
|
|
14
|
+
console.log("Optional Windows Claude integration:");
|
|
15
|
+
console.log(" powershell -ExecutionPolicy Bypass -File \"$(npm prefix -g)\\node_modules\\loren-code\\scripts\\install-claude-ollama.ps1\"");
|
|
16
|
+
console.log("");
|
|
@@ -3,9 +3,10 @@ $ErrorActionPreference = "Stop"
|
|
|
3
3
|
$repoRoot = Split-Path -Parent $PSScriptRoot
|
|
4
4
|
$userProfile = [Environment]::GetFolderPath("UserProfile")
|
|
5
5
|
$appData = [Environment]::GetFolderPath("ApplicationData")
|
|
6
|
+
$lorenHome = if ($env:LOREN_HOME) { $env:LOREN_HOME } else { Join-Path $userProfile ".lorencode" }
|
|
6
7
|
$workspaceSettingsPath = Join-Path $appData "Code\\User\\settings.json"
|
|
7
8
|
$claudeSettingsPath = Join-Path $userProfile ".claude\\settings.json"
|
|
8
|
-
$bridgePidPath = Join-Path $
|
|
9
|
+
$bridgePidPath = Join-Path $lorenHome "runtime\\bridge.pid"
|
|
9
10
|
$launcherExePath = Join-Path $repoRoot "scripts\\ClaudeWrapperLauncher.exe"
|
|
10
11
|
$npmBinDir = Join-Path $appData "npm"
|
|
11
12
|
$claudeCmdPath = Join-Path $npmBinDir "claude.cmd"
|
package/src/bootstrap.js
CHANGED
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { getEnvFilePath, getLegacyEnvFilePath, getLorenHome, getRuntimeDir } from "./paths.js";
|
|
3
4
|
|
|
4
|
-
export function ensureRuntimeDir(
|
|
5
|
-
fs.mkdirSync(
|
|
5
|
+
export function ensureRuntimeDir() {
|
|
6
|
+
fs.mkdirSync(getLorenHome(), { recursive: true });
|
|
7
|
+
fs.mkdirSync(getRuntimeDir(), { recursive: true });
|
|
8
|
+
return getRuntimeDir();
|
|
6
9
|
}
|
|
7
10
|
|
|
8
11
|
export function ensureEnvLocal(projectRoot, options = {}) {
|
|
9
|
-
const envLocalPath =
|
|
12
|
+
const envLocalPath = getEnvFilePath();
|
|
13
|
+
const legacyEnvPath = getLegacyEnvFilePath(projectRoot);
|
|
10
14
|
const envExamplePath = path.join(projectRoot, ".env.example");
|
|
11
15
|
const logger = options.logger ?? console;
|
|
16
|
+
fs.mkdirSync(getLorenHome(), { recursive: true });
|
|
12
17
|
|
|
13
18
|
if (fs.existsSync(envLocalPath)) {
|
|
14
19
|
return { created: false, path: envLocalPath };
|
|
15
20
|
}
|
|
16
21
|
|
|
22
|
+
if (fs.existsSync(legacyEnvPath) && legacyEnvPath !== envLocalPath) {
|
|
23
|
+
fs.copyFileSync(legacyEnvPath, envLocalPath);
|
|
24
|
+
logger.warn?.(`Migrated existing config from ${legacyEnvPath} to ${envLocalPath}.`);
|
|
25
|
+
return { created: true, migrated: true, path: envLocalPath };
|
|
26
|
+
}
|
|
27
|
+
|
|
17
28
|
if (!fs.existsSync(envExamplePath)) {
|
|
18
29
|
fs.writeFileSync(envLocalPath, "OLLAMA_API_KEYS=\nBRIDGE_HOST=127.0.0.1\nBRIDGE_PORT=8788\n", "utf8");
|
|
19
30
|
logger.warn?.(`Created ${envLocalPath}. Add your Ollama API key(s) before starting the bridge.`);
|
package/src/config.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import
|
|
2
|
+
import { getEnvFilePath } from "./paths.js";
|
|
3
3
|
|
|
4
4
|
const DEFAULT_PORT = 8788;
|
|
5
5
|
const DEFAULT_HOST = "127.0.0.1";
|
|
6
6
|
const DEFAULT_UPSTREAM = "https://ollama.com";
|
|
7
7
|
|
|
8
|
-
export function loadEnvFile(filePath =
|
|
8
|
+
export function loadEnvFile(filePath = getEnvFilePath()) {
|
|
9
9
|
if (!fs.existsSync(filePath)) {
|
|
10
10
|
return {};
|
|
11
11
|
}
|
|
@@ -68,8 +68,8 @@ export function saveEnvFile(filePath, envVars) {
|
|
|
68
68
|
fs.writeFileSync(filePath, `${lines}\n`, "utf8");
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
export function loadConfig() {
|
|
72
|
-
const fileEnv = loadEnvFile();
|
|
71
|
+
export function loadConfig(filePath = getEnvFilePath()) {
|
|
72
|
+
const fileEnv = loadEnvFile(filePath);
|
|
73
73
|
const getValue = (name, fallback = undefined) => {
|
|
74
74
|
if (Object.prototype.hasOwnProperty.call(fileEnv, name)) {
|
|
75
75
|
return fileEnv[name];
|
package/src/logger.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import winston from 'winston';
|
|
2
|
+
import fs from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
4
|
+
import { getRuntimeDir } from './paths.js';
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
-
|
|
6
|
+
const runtimeDir = getRuntimeDir();
|
|
7
|
+
fs.mkdirSync(runtimeDir, { recursive: true });
|
|
7
8
|
|
|
8
9
|
const logFormat = winston.format.combine(
|
|
9
10
|
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
|
@@ -24,23 +25,23 @@ const logger = winston.createLogger({
|
|
|
24
25
|
)
|
|
25
26
|
}),
|
|
26
27
|
new winston.transports.File({
|
|
27
|
-
filename: path.join(
|
|
28
|
+
filename: path.join(runtimeDir, 'error.log'),
|
|
28
29
|
level: 'error',
|
|
29
30
|
maxsize: 5242880, // 5MB
|
|
30
31
|
maxFiles: 5
|
|
31
32
|
}),
|
|
32
33
|
new winston.transports.File({
|
|
33
|
-
filename: path.join(
|
|
34
|
+
filename: path.join(runtimeDir, 'combined.log'),
|
|
34
35
|
maxsize: 5242880, // 5MB
|
|
35
36
|
maxFiles: 5
|
|
36
37
|
})
|
|
37
38
|
],
|
|
38
39
|
exceptionHandlers: [
|
|
39
|
-
new winston.transports.File({ filename: path.join(
|
|
40
|
+
new winston.transports.File({ filename: path.join(runtimeDir, 'exceptions.log') })
|
|
40
41
|
],
|
|
41
42
|
rejectionHandlers: [
|
|
42
|
-
new winston.transports.File({ filename: path.join(
|
|
43
|
+
new winston.transports.File({ filename: path.join(runtimeDir, 'rejections.log') })
|
|
43
44
|
]
|
|
44
45
|
});
|
|
45
46
|
|
|
46
|
-
export default logger;
|
|
47
|
+
export default logger;
|
package/src/paths.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
|
|
4
|
+
export function getUserHomeDir() {
|
|
5
|
+
return process.env.USERPROFILE || process.env.HOME || process.cwd();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function getLorenHome() {
|
|
9
|
+
return process.env.LOREN_HOME || path.join(getUserHomeDir(), ".lorencode");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getEnvFilePath() {
|
|
13
|
+
return path.join(getLorenHome(), ".env.local");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getRuntimeDir() {
|
|
17
|
+
return path.join(getLorenHome(), "runtime");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getLegacyEnvFilePath(projectRoot) {
|
|
21
|
+
return path.join(projectRoot, ".env.local");
|
|
22
|
+
}
|
package/src/server.js
CHANGED
|
@@ -6,6 +6,7 @@ import path from "node:path";
|
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { loadConfig } from "./config.js";
|
|
8
8
|
import { ensureEnvLocal, ensureRuntimeDir } from "./bootstrap.js";
|
|
9
|
+
import { getEnvFilePath } from "./paths.js";
|
|
9
10
|
import logger from "./logger.js";
|
|
10
11
|
import { KeyManager } from "./key-manager.js";
|
|
11
12
|
import { validateInput, MessageSchema, CountTokensSchema } from "./schemas.js";
|
|
@@ -20,12 +21,12 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
20
21
|
const __dirname = path.dirname(__filename);
|
|
21
22
|
const projectRoot = path.resolve(__dirname, "..");
|
|
22
23
|
|
|
23
|
-
ensureRuntimeDir(
|
|
24
|
+
ensureRuntimeDir();
|
|
24
25
|
ensureEnvLocal(projectRoot, { logger });
|
|
25
26
|
|
|
26
27
|
let config = loadConfig();
|
|
27
28
|
let keyManager = new KeyManager(config.apiKeys);
|
|
28
|
-
const envFilePath =
|
|
29
|
+
const envFilePath = getEnvFilePath();
|
|
29
30
|
|
|
30
31
|
function reloadRuntimeConfig() {
|
|
31
32
|
config = loadConfig();
|
|
@@ -57,7 +58,7 @@ process.on('SIGTERM', () => {
|
|
|
57
58
|
});
|
|
58
59
|
|
|
59
60
|
if (!config.apiKeys.length) {
|
|
60
|
-
logger.error(
|
|
61
|
+
logger.error(`No Ollama API keys found. Set OLLAMA_API_KEYS or OLLAMA_API_KEY in the environment or ${envFilePath}.`);
|
|
61
62
|
process.exit(1);
|
|
62
63
|
}
|
|
63
64
|
|