Skip to content
Open
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
16 changes: 11 additions & 5 deletions assets/js/ai-assistant.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,14 @@ const QUICK_ACTIONS = Object.freeze({
},
});

// Free 模型专用 key。它只用于默认体验,Base64 不是安全存储。
const _k = [
// This is a free-tier OpenRouter key for the site's default AI experience. It ships
// in browser JavaScript and is fully recoverable by anyone who opens dev tools —
// it is NOT a secret and must not be treated as one. Splitting it into fragments
// below avoids naive plaintext secret-scanners flagging the source; it provides no
// real security benefit. Keep OpenRouter-side domain/usage restrictions and rate
// limits enabled on this key at all times, and never replace it with a
// production-scope credential.
const PUBLIC_AI_KEY_FRAGMENTS = [
'c2stb3ItdjEtMDk1',
'MGEzNDk1ODE1OGJh',
'M2E3MmNjZWMwNzEy',
Expand All @@ -119,8 +125,8 @@ const _k = [
'ZWZmZTdiMTUwZWNjMw==',
];

function _dk() {
try { return atob(_k.join('')); } catch (error) { return ''; }
function getPublicAIKey() {
try { return atob(PUBLIC_AI_KEY_FRAGMENTS.join('')); } catch (error) { return ''; }
}

const AI_ICON_SVG = `
Expand Down Expand Up @@ -173,7 +179,7 @@ function loadAIConfig() {
if (sessionAIConfig) return { ...sessionAIConfig };
const defaults = {
baseUrl: 'https://openrouter.ai/api/v1',
apiKey: _dk(),
apiKey: getPublicAIKey(),
model: AI_DEFAULT_MODEL,
};
try {
Expand Down
5 changes: 5 additions & 0 deletions tests/ai-assistant.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,11 @@ test('conversion response validation rejects safety classifiers and incomplete p
);
});

test('default AI key is documented as public rather than secret', () => {
assert.match(assistantSource, /is NOT a secret/);
assert.doesNotMatch(assistantSource, /\bDEFAULT_API_KEY\s*=/);
});

test('legacy dynamic free router migrates to the verified code model', () => {
assert.equal(AI_DEFAULT_MODEL, 'poolside/laguna-s-2.1:free');
assert.equal(normalizeConfiguredModel('openrouter/free'), AI_DEFAULT_MODEL);
Expand Down