Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ jobs:
packages: write
steps:
- uses: actions/checkout@v6
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
- uses: docker/setup-buildx-action@v4
- uses: docker/login-action@v4
if: github.event_name != 'pull_request'
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5
- uses: docker/metadata-action@v6
id: meta
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
Expand All @@ -38,7 +38,7 @@ jobs:
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
- uses: docker/build-push-action@v6
- uses: docker/build-push-action@v7
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
Expand Down
2 changes: 1 addition & 1 deletion docs/vercel.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"ignoreCommand": "git diff --quiet HEAD^ HEAD -- .",
"ignoreCommand": "git diff --quiet HEAD^ HEAD -- . ../package.json",
"installCommand": "cd .. && npm install --include=dev --ignore-scripts",
"buildCommand": "cd .. && npx vitepress build docs",
"outputDirectory": ".vitepress/dist"
Expand Down
81 changes: 42 additions & 39 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/api/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export function checkApiKey(req: IncomingMessage, res: ServerResponse): boolean
const keyBuf = Buffer.alloc(COMPARE_LEN);
Buffer.from(token).copy(tokenBuf);
Buffer.from(apiKey).copy(keyBuf);
if (token.length !== apiKey.length || !timingSafeEqual(tokenBuf, keyBuf)) {
if (!timingSafeEqual(tokenBuf, keyBuf)) {
sendError(res, 401, "UNAUTHORIZED", "Invalid API key");
return false;
}
Expand Down
20 changes: 16 additions & 4 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import {
createPackFromSource,
} from "../core/packs.js";

import { execSync } from "node:child_process";
import { spawnSync } from "node:child_process";
import { createRequire } from "node:module";
import { FileWatcher, DEFAULT_WATCH_EXTENSIONS } from "../core/watcher.js";
import { indexRepository, parseRepoUrl } from "../core/repo.js";
Expand Down Expand Up @@ -2290,17 +2290,29 @@ program
console.log(`Current version: ${currentVersion}`);

try {
const latest = execSync("npm view libscope version", { encoding: "utf-8" }).trim();
const versionResult = spawnSync("npm", ["view", "libscope", "version"], {
encoding: "utf-8",
});
if (versionResult.error || versionResult.status !== 0) {
throw new Error(versionResult.stderr?.trim() || "Failed to check latest version");
}
const latest = versionResult.stdout.trim();
if (latest === currentVersion) {
console.log("✓ Already up to date.");
return;
}
console.log(`Latest version: ${latest}`);
console.log("Updating...");
execSync("npm install -g libscope@latest", { stdio: "inherit" });
const installResult = spawnSync("npm", ["install", "-g", "libscope@latest"], {
stdio: "inherit",
});
if (installResult.error || installResult.status !== 0) {
throw new Error("npm install failed");
}
console.log(`✓ Updated to ${latest}`);
} catch {
} catch (err) {
console.error("Failed to update. Try manually: npm install -g libscope@latest");
if (err instanceof Error) console.error(err.message);
process.exit(1);
}
});
Expand Down
21 changes: 21 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,18 @@ export function loadConfig(): LibScopeConfig {
const projectConfig = loadJsonFile(getProjectConfigPath());
const envOverrides = getEnvOverrides();

if (
userConfig.embedding?.openaiApiKey ||
userConfig.llm?.openaiApiKey ||
userConfig.llm?.anthropicApiKey
) {
getLogger().warn(
"API keys found in config file (~/.libscope/config.json). " +
"This is deprecated — please use environment variables (OPENAI_API_KEY, ANTHROPIC_API_KEY) instead. " +
"Keys in the config file will no longer be written back after the next save.",
);
}

const config: LibScopeConfig = {
embedding: {
...DEFAULT_CONFIG.embedding,
Expand Down Expand Up @@ -286,6 +298,15 @@ export function saveUserConfig(config: Partial<LibScopeConfig>): void {
...config.logging,
},
};

// Security: never persist API keys to disk — use environment variables instead.
// Keys are read from env vars (OPENAI_API_KEY, ANTHROPIC_API_KEY) at runtime.
delete merged.embedding.openaiApiKey;
if (merged.llm) {
delete merged.llm.openaiApiKey;
delete merged.llm.anthropicApiKey;
}

writeFileSync(getUserConfigPath(), JSON.stringify(merged, null, 2), "utf-8");
invalidateConfigCache();
}
Loading
Loading