Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad11414def | ||
|
|
9419e8a4a2 | ||
|
|
41a86d05fa | ||
|
|
fa4b6a96c0 | ||
|
|
d03d77b110 | ||
|
|
15de1d6190 | ||
|
|
812facf024 | ||
|
|
2e39d2607a |
20
.github/workflows/release.yml
vendored
20
.github/workflows/release.yml
vendored
@@ -4,6 +4,9 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
release:
|
||||||
|
types:
|
||||||
|
- published
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: auto-release-${{ github.ref }}
|
group: auto-release-${{ github.ref }}
|
||||||
@@ -12,6 +15,7 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
release-please:
|
release-please:
|
||||||
name: Release Please
|
name: Release Please
|
||||||
|
if: ${{ github.event_name == 'push' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -30,8 +34,7 @@ jobs:
|
|||||||
|
|
||||||
publish-npm:
|
publish-npm:
|
||||||
name: Publish to npm
|
name: Publish to npm
|
||||||
needs: release-please
|
if: ${{ github.event_name == 'release' }}
|
||||||
if: ${{ needs.release-please.outputs.release_created == 'true' }}
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
environment: release
|
environment: release
|
||||||
permissions:
|
permissions:
|
||||||
@@ -41,13 +44,13 @@ jobs:
|
|||||||
- name: Checkout release tag
|
- name: Checkout release tag
|
||||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.release-please.outputs.tag_name }}
|
ref: ${{ github.event.release.tag_name }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 24
|
||||||
registry-url: https://registry.npmjs.org
|
registry-url: https://registry.npmjs.org
|
||||||
|
|
||||||
- name: Set up Bun
|
- name: Set up Bun
|
||||||
@@ -70,14 +73,19 @@ jobs:
|
|||||||
- name: Dry-run package
|
- name: Dry-run package
|
||||||
run: npm pack --dry-run
|
run: npm pack --dry-run
|
||||||
|
|
||||||
|
- name: Clear token auth for trusted publishing
|
||||||
|
run: |
|
||||||
|
unset NODE_AUTH_TOKEN
|
||||||
|
echo "NODE_AUTH_TOKEN=" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- name: Publish to npm
|
- name: Publish to npm
|
||||||
run: npm publish --access public --provenance
|
run: npm publish --access public --provenance
|
||||||
|
|
||||||
- name: Release summary
|
- name: Release summary
|
||||||
run: |
|
run: |
|
||||||
{
|
{
|
||||||
echo "## Released ${{ needs.release-please.outputs.tag_name }}"
|
echo "## Released ${{ github.event.release.tag_name }}"
|
||||||
echo
|
echo
|
||||||
echo "- npm: https://www.npmjs.com/package/@gitlawb/openclaude"
|
echo "- npm: https://www.npmjs.com/package/@gitlawb/openclaude"
|
||||||
echo "- GitHub: https://github.com/Gitlawb/openclaude/releases/tag/${{ needs.release-please.outputs.tag_name }}"
|
echo "- GitHub: https://github.com/Gitlawb/openclaude/releases/tag/${{ github.event.release.tag_name }}"
|
||||||
} >> "$GITHUB_STEP_SUMMARY"
|
} >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
".": "0.2.0"
|
".": "0.2.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.2.1](https://github.com/Gitlawb/openclaude/compare/v0.2.0...v0.2.1) (2026-04-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **provider:** add recovery guidance for missing OpenAI API key ([#616](https://github.com/Gitlawb/openclaude/issues/616)) ([9419e8a](https://github.com/Gitlawb/openclaude/commit/9419e8a4a21b3771d9ddb10f7072e0a8c5b5b631))
|
||||||
|
|
||||||
## [0.2.0](https://github.com/Gitlawb/openclaude/compare/v0.1.8...v0.2.0) (2026-04-12)
|
## [0.2.0](https://github.com/Gitlawb/openclaude/compare/v0.1.8...v0.2.0) (2026-04-12)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@gitlawb/openclaude",
|
"name": "@gitlawb/openclaude",
|
||||||
"version": "0.2.0",
|
"version": "0.2.1",
|
||||||
"description": "Claude Code opened to any LLM — OpenAI, Gemini, DeepSeek, Ollama, and 200+ models",
|
"description": "Claude Code opened to any LLM — OpenAI, Gemini, DeepSeek, Ollama, and 200+ models",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://gitlawb.com/z6MkqDnb7Siv3Cwj7pGJq4T5EsUisECqR8KpnDLwcaZq5TPr/openclaude"
|
"url": "https://github.com/Gitlawb/openclaude.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"claude-code",
|
"claude-code",
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ import { afterEach, expect, test } from 'bun:test'
|
|||||||
import { getProviderValidationError } from './providerValidation.ts'
|
import { getProviderValidationError } from './providerValidation.ts'
|
||||||
|
|
||||||
const originalEnv = {
|
const originalEnv = {
|
||||||
|
CLAUDE_CODE_USE_OPENAI: process.env.CLAUDE_CODE_USE_OPENAI,
|
||||||
|
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
|
||||||
|
OPENAI_BASE_URL: process.env.OPENAI_BASE_URL,
|
||||||
CLAUDE_CODE_USE_GEMINI: process.env.CLAUDE_CODE_USE_GEMINI,
|
CLAUDE_CODE_USE_GEMINI: process.env.CLAUDE_CODE_USE_GEMINI,
|
||||||
GEMINI_API_KEY: process.env.GEMINI_API_KEY,
|
GEMINI_API_KEY: process.env.GEMINI_API_KEY,
|
||||||
GOOGLE_API_KEY: process.env.GOOGLE_API_KEY,
|
GOOGLE_API_KEY: process.env.GOOGLE_API_KEY,
|
||||||
@@ -20,6 +23,9 @@ function restoreEnv(key: string, value: string | undefined): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
restoreEnv('CLAUDE_CODE_USE_OPENAI', originalEnv.CLAUDE_CODE_USE_OPENAI)
|
||||||
|
restoreEnv('OPENAI_API_KEY', originalEnv.OPENAI_API_KEY)
|
||||||
|
restoreEnv('OPENAI_BASE_URL', originalEnv.OPENAI_BASE_URL)
|
||||||
restoreEnv('CLAUDE_CODE_USE_GEMINI', originalEnv.CLAUDE_CODE_USE_GEMINI)
|
restoreEnv('CLAUDE_CODE_USE_GEMINI', originalEnv.CLAUDE_CODE_USE_GEMINI)
|
||||||
restoreEnv('GEMINI_API_KEY', originalEnv.GEMINI_API_KEY)
|
restoreEnv('GEMINI_API_KEY', originalEnv.GEMINI_API_KEY)
|
||||||
restoreEnv('GOOGLE_API_KEY', originalEnv.GOOGLE_API_KEY)
|
restoreEnv('GOOGLE_API_KEY', originalEnv.GOOGLE_API_KEY)
|
||||||
@@ -71,3 +77,19 @@ test('still errors when no Gemini credential source is available', async () => {
|
|||||||
'GEMINI_API_KEY, GOOGLE_API_KEY, GEMINI_ACCESS_TOKEN, or Google ADC credentials are required when CLAUDE_CODE_USE_GEMINI=1.',
|
'GEMINI_API_KEY, GOOGLE_API_KEY, GEMINI_ACCESS_TOKEN, or Google ADC credentials are required when CLAUDE_CODE_USE_GEMINI=1.',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('openai missing key error includes recovery guidance and config locations', async () => {
|
||||||
|
process.env.CLAUDE_CODE_USE_OPENAI = '1'
|
||||||
|
process.env.OPENAI_BASE_URL = 'https://api.openai.com/v1'
|
||||||
|
delete process.env.OPENAI_API_KEY
|
||||||
|
|
||||||
|
const message = await getProviderValidationError(process.env)
|
||||||
|
expect(message).toContain(
|
||||||
|
'OPENAI_API_KEY is required when CLAUDE_CODE_USE_OPENAI=1 and OPENAI_BASE_URL is not local.',
|
||||||
|
)
|
||||||
|
expect(message).toContain(
|
||||||
|
'set CLAUDE_CODE_USE_OPENAI=0 in your shell environment',
|
||||||
|
)
|
||||||
|
expect(message).toContain('Saved startup settings can come from')
|
||||||
|
expect(message).toContain('.openclaude-profile.json')
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
|
import { resolve } from 'node:path'
|
||||||
import {
|
import {
|
||||||
getGithubEndpointType,
|
getGithubEndpointType,
|
||||||
isLocalProviderUrl,
|
isLocalProviderUrl,
|
||||||
resolveCodexApiCredentials,
|
resolveCodexApiCredentials,
|
||||||
resolveProviderRequest,
|
resolveProviderRequest,
|
||||||
} from '../services/api/providerConfig.js'
|
} from '../services/api/providerConfig.js'
|
||||||
|
import { getGlobalClaudeFile } from './env.js'
|
||||||
import {
|
import {
|
||||||
type GeminiResolvedCredential,
|
type GeminiResolvedCredential,
|
||||||
resolveGeminiCredential,
|
resolveGeminiCredential,
|
||||||
} from './geminiAuth.js'
|
} from './geminiAuth.js'
|
||||||
import { redactSecretValueForDisplay } from './providerProfile.js'
|
import { PROFILE_FILE_NAME, redactSecretValueForDisplay } from './providerProfile.js'
|
||||||
|
|
||||||
function isEnvTruthy(value: string | undefined): boolean {
|
function isEnvTruthy(value: string | undefined): boolean {
|
||||||
if (!value) return false
|
if (!value) return false
|
||||||
@@ -61,6 +63,17 @@ function checkGithubTokenStatus(
|
|||||||
return 'valid'
|
return 'valid'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getOpenAIMissingKeyMessage(): string {
|
||||||
|
const globalConfigPath = getGlobalClaudeFile()
|
||||||
|
const profilePath = resolve(process.cwd(), PROFILE_FILE_NAME)
|
||||||
|
|
||||||
|
return [
|
||||||
|
'OPENAI_API_KEY is required when CLAUDE_CODE_USE_OPENAI=1 and OPENAI_BASE_URL is not local.',
|
||||||
|
`To recover, run /provider and switch provider, or set CLAUDE_CODE_USE_OPENAI=0 in your shell environment.`,
|
||||||
|
`Saved startup settings can come from ${globalConfigPath} or ${profilePath}.`,
|
||||||
|
].join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
export async function getProviderValidationError(
|
export async function getProviderValidationError(
|
||||||
env: NodeJS.ProcessEnv = process.env,
|
env: NodeJS.ProcessEnv = process.env,
|
||||||
options?: {
|
options?: {
|
||||||
@@ -137,7 +150,7 @@ export async function getProviderValidationError(
|
|||||||
if (useGithub && hasGithubToken) {
|
if (useGithub && hasGithubToken) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return 'OPENAI_API_KEY is required when CLAUDE_CODE_USE_OPENAI=1 and OPENAI_BASE_URL is not local.'
|
return getOpenAIMissingKeyMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
|||||||
Reference in New Issue
Block a user