codeblog-app 1.1.0 → 1.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/package.json +1 -1
- package/src/ai/provider.ts +43 -4
- package/src/cli/cmd/config.ts +17 -5
- package/src/index.ts +1 -1
package/package.json
CHANGED
package/src/ai/provider.ts
CHANGED
|
@@ -53,7 +53,7 @@ export namespace AIProvider {
|
|
|
53
53
|
// Provider env key mapping
|
|
54
54
|
// ---------------------------------------------------------------------------
|
|
55
55
|
const PROVIDER_ENV: Record<string, string[]> = {
|
|
56
|
-
anthropic: ["ANTHROPIC_API_KEY"],
|
|
56
|
+
anthropic: ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN"],
|
|
57
57
|
openai: ["OPENAI_API_KEY"],
|
|
58
58
|
google: ["GOOGLE_GENERATIVE_AI_API_KEY", "GOOGLE_API_KEY"],
|
|
59
59
|
"amazon-bedrock": ["AWS_ACCESS_KEY_ID"],
|
|
@@ -70,6 +70,22 @@ export namespace AIProvider {
|
|
|
70
70
|
"openai-compatible": ["OPENAI_COMPATIBLE_API_KEY"],
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// Provider base URL env mapping (for third-party API proxies)
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
const PROVIDER_BASE_URL_ENV: Record<string, string[]> = {
|
|
77
|
+
anthropic: ["ANTHROPIC_BASE_URL"],
|
|
78
|
+
openai: ["OPENAI_BASE_URL", "OPENAI_API_BASE"],
|
|
79
|
+
google: ["GOOGLE_API_BASE_URL"],
|
|
80
|
+
azure: ["AZURE_OPENAI_BASE_URL"],
|
|
81
|
+
xai: ["XAI_BASE_URL"],
|
|
82
|
+
mistral: ["MISTRAL_BASE_URL"],
|
|
83
|
+
groq: ["GROQ_BASE_URL"],
|
|
84
|
+
deepinfra: ["DEEPINFRA_BASE_URL"],
|
|
85
|
+
openrouter: ["OPENROUTER_BASE_URL"],
|
|
86
|
+
"openai-compatible": ["OPENAI_COMPATIBLE_BASE_URL"],
|
|
87
|
+
}
|
|
88
|
+
|
|
73
89
|
// ---------------------------------------------------------------------------
|
|
74
90
|
// Provider → npm package mapping
|
|
75
91
|
// ---------------------------------------------------------------------------
|
|
@@ -177,6 +193,18 @@ export namespace AIProvider {
|
|
|
177
193
|
return cfg.providers?.[providerID]?.api_key
|
|
178
194
|
}
|
|
179
195
|
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
// Get base URL for a provider (env var or config)
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
export async function getBaseUrl(providerID: string): Promise<string | undefined> {
|
|
200
|
+
const envKeys = PROVIDER_BASE_URL_ENV[providerID] || []
|
|
201
|
+
for (const key of envKeys) {
|
|
202
|
+
if (process.env[key]) return process.env[key]
|
|
203
|
+
}
|
|
204
|
+
const cfg = await Config.load()
|
|
205
|
+
return cfg.providers?.[providerID]?.base_url
|
|
206
|
+
}
|
|
207
|
+
|
|
180
208
|
// ---------------------------------------------------------------------------
|
|
181
209
|
// List all available providers with their models
|
|
182
210
|
// ---------------------------------------------------------------------------
|
|
@@ -223,7 +251,8 @@ export namespace AIProvider {
|
|
|
223
251
|
if (builtin) {
|
|
224
252
|
const apiKey = await getApiKey(builtin.providerID)
|
|
225
253
|
if (!apiKey) throw noKeyError(builtin.providerID)
|
|
226
|
-
|
|
254
|
+
const base = await getBaseUrl(builtin.providerID)
|
|
255
|
+
return getLanguageModel(builtin.providerID, id, apiKey, undefined, base)
|
|
227
256
|
}
|
|
228
257
|
|
|
229
258
|
// Try models.dev
|
|
@@ -234,7 +263,8 @@ export namespace AIProvider {
|
|
|
234
263
|
const apiKey = await getApiKey(providerID)
|
|
235
264
|
if (!apiKey) throw noKeyError(providerID)
|
|
236
265
|
const npm = p.models[id].provider?.npm || p.npm || "@ai-sdk/openai-compatible"
|
|
237
|
-
|
|
266
|
+
const base = await getBaseUrl(providerID)
|
|
267
|
+
return getLanguageModel(providerID, id, apiKey, npm, base || p.api)
|
|
238
268
|
}
|
|
239
269
|
}
|
|
240
270
|
|
|
@@ -244,7 +274,8 @@ export namespace AIProvider {
|
|
|
244
274
|
const mid = rest.join("/")
|
|
245
275
|
const apiKey = await getApiKey(providerID)
|
|
246
276
|
if (!apiKey) throw noKeyError(providerID)
|
|
247
|
-
|
|
277
|
+
const base = await getBaseUrl(providerID)
|
|
278
|
+
return getLanguageModel(providerID, mid, apiKey, undefined, base)
|
|
248
279
|
}
|
|
249
280
|
|
|
250
281
|
throw new Error(`Unknown model: ${id}. Run: codeblog config --list`)
|
|
@@ -293,10 +324,18 @@ export namespace AIProvider {
|
|
|
293
324
|
// Check if any AI provider has a key configured
|
|
294
325
|
// ---------------------------------------------------------------------------
|
|
295
326
|
export async function hasAnyKey(): Promise<boolean> {
|
|
327
|
+
// Check env vars
|
|
296
328
|
for (const providerID of Object.keys(PROVIDER_ENV)) {
|
|
297
329
|
const key = await getApiKey(providerID)
|
|
298
330
|
if (key) return true
|
|
299
331
|
}
|
|
332
|
+
// Check config file (covers third-party providers not in PROVIDER_ENV)
|
|
333
|
+
const cfg = await Config.load()
|
|
334
|
+
if (cfg.providers) {
|
|
335
|
+
for (const p of Object.values(cfg.providers)) {
|
|
336
|
+
if (p.api_key) return true
|
|
337
|
+
}
|
|
338
|
+
}
|
|
300
339
|
return false
|
|
301
340
|
}
|
|
302
341
|
|
package/src/cli/cmd/config.ts
CHANGED
|
@@ -36,6 +36,10 @@ export const ConfigCommand: CommandModule = {
|
|
|
36
36
|
type: "boolean",
|
|
37
37
|
default: false,
|
|
38
38
|
})
|
|
39
|
+
.option("base-url", {
|
|
40
|
+
describe: "Custom base URL for the provider (for third-party API proxies)",
|
|
41
|
+
type: "string",
|
|
42
|
+
})
|
|
39
43
|
.option("language", {
|
|
40
44
|
describe: "Default content language for posts (e.g. English, 中文, 日本語)",
|
|
41
45
|
type: "string",
|
|
@@ -80,12 +84,18 @@ export const ConfigCommand: CommandModule = {
|
|
|
80
84
|
return
|
|
81
85
|
}
|
|
82
86
|
|
|
83
|
-
if (args.provider && args.apiKey) {
|
|
87
|
+
if (args.provider && (args.apiKey || args.baseUrl)) {
|
|
84
88
|
const cfg = await Config.load()
|
|
85
89
|
const providers = cfg.providers || {}
|
|
86
|
-
|
|
90
|
+
const existing = providers[args.provider as string] || {} as Config.ProviderConfig
|
|
91
|
+
if (args.apiKey) existing.api_key = args.apiKey as string
|
|
92
|
+
if (args.baseUrl) existing.base_url = args.baseUrl as string
|
|
93
|
+
providers[args.provider as string] = existing
|
|
87
94
|
await Config.save({ providers })
|
|
88
|
-
|
|
95
|
+
const parts: string[] = []
|
|
96
|
+
if (args.apiKey) parts.push("API key")
|
|
97
|
+
if (args.baseUrl) parts.push(`base URL (${args.baseUrl})`)
|
|
98
|
+
UI.success(`${args.provider} ${parts.join(" + ")} saved`)
|
|
89
99
|
return
|
|
90
100
|
}
|
|
91
101
|
|
|
@@ -125,12 +135,14 @@ export const ConfigCommand: CommandModule = {
|
|
|
125
135
|
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}AI Providers${UI.Style.TEXT_NORMAL}`)
|
|
126
136
|
for (const [id, p] of Object.entries(providers)) {
|
|
127
137
|
const masked = p.api_key ? p.api_key.slice(0, 8) + "..." : "not set"
|
|
128
|
-
|
|
138
|
+
const url = p.base_url ? ` → ${p.base_url}` : ""
|
|
139
|
+
console.log(` ${UI.Style.TEXT_SUCCESS}✓${UI.Style.TEXT_NORMAL} ${id}: ${UI.Style.TEXT_DIM}${masked}${url}${UI.Style.TEXT_NORMAL}`)
|
|
129
140
|
}
|
|
130
141
|
} else {
|
|
131
142
|
console.log(` ${UI.Style.TEXT_DIM}No AI providers configured.${UI.Style.TEXT_NORMAL}`)
|
|
132
143
|
console.log(` ${UI.Style.TEXT_DIM}Set one: codeblog config --provider anthropic --api-key sk-...${UI.Style.TEXT_NORMAL}`)
|
|
133
|
-
console.log(` ${UI.Style.TEXT_DIM}
|
|
144
|
+
console.log(` ${UI.Style.TEXT_DIM}Third-party proxy: codeblog config --provider anthropic --api-key sk-... --base-url https://proxy.example.com${UI.Style.TEXT_NORMAL}`)
|
|
145
|
+
console.log(` ${UI.Style.TEXT_DIM}Or use env: ANTHROPIC_API_KEY + ANTHROPIC_BASE_URL${UI.Style.TEXT_NORMAL}`)
|
|
134
146
|
}
|
|
135
147
|
console.log("")
|
|
136
148
|
} catch (err) {
|
package/src/index.ts
CHANGED
|
@@ -35,7 +35,7 @@ import { WeeklyDigestCommand } from "./cli/cmd/weekly-digest"
|
|
|
35
35
|
import { TagsCommand } from "./cli/cmd/tags"
|
|
36
36
|
import { ExploreCommand } from "./cli/cmd/explore"
|
|
37
37
|
|
|
38
|
-
const VERSION = "1.
|
|
38
|
+
const VERSION = "1.2.0"
|
|
39
39
|
|
|
40
40
|
process.on("unhandledRejection", (e) => {
|
|
41
41
|
Log.Default.error("rejection", {
|