mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-07-10 14:28:43 +02:00
195 lines
6.3 KiB
JavaScript
195 lines
6.3 KiB
JavaScript
/* global chrome */
|
|
|
|
const STORAGE_KEYS = {
|
|
host: 'csai_host',
|
|
port: 'csai_port',
|
|
https: 'csai_https',
|
|
lastProjectId: 'csai_last_project',
|
|
lastRole: 'csai_last_role',
|
|
lastAgentMode: 'csai_last_agent_mode',
|
|
lastInstruction: 'csai_last_instruction',
|
|
filterApiOnly: 'csai_filter_api_only',
|
|
captureEnabled: 'csai_capture_enabled',
|
|
renderMarkdown: 'csai_render_markdown',
|
|
showDebugEvents: 'csai_show_debug',
|
|
};
|
|
|
|
const SESSION_TOKEN_KEY = 'csai_token';
|
|
|
|
const DEFAULTS = {
|
|
host: '127.0.0.1',
|
|
port: '8080',
|
|
https: true,
|
|
lastInstruction: CSAI_DEFAULT_INSTRUCTION,
|
|
filterApiOnly: true,
|
|
captureEnabled: true,
|
|
renderMarkdown: true,
|
|
showDebugEvents: false,
|
|
};
|
|
|
|
function extensionContextAlive() {
|
|
try {
|
|
return !!(typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.id);
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function extensionContextError() {
|
|
const err = new Error('Extension context invalidated');
|
|
err.name = 'ExtensionContextError';
|
|
err.contextInvalidated = true;
|
|
return err;
|
|
}
|
|
|
|
function isExtensionContextError(err) {
|
|
if (!err) return false;
|
|
if (err.contextInvalidated || err.name === 'ExtensionContextError') return true;
|
|
return /extension context invalidated/i.test(String(err.message || err));
|
|
}
|
|
|
|
function normalizeStorageError(err) {
|
|
if (isExtensionContextError(err)) return extensionContextError();
|
|
return err instanceof Error ? err : new Error(String(err));
|
|
}
|
|
|
|
function rejectLastError(reject) {
|
|
const msg = chrome.runtime.lastError && chrome.runtime.lastError.message;
|
|
if (!msg) return false;
|
|
reject(/invalidated/i.test(msg) ? extensionContextError() : new Error(msg));
|
|
return true;
|
|
}
|
|
|
|
function localGet(keys) {
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
if (!extensionContextAlive()) {
|
|
reject(extensionContextError());
|
|
return;
|
|
}
|
|
chrome.storage.local.get(keys, (data) => {
|
|
if (rejectLastError(reject)) return;
|
|
resolve(data);
|
|
});
|
|
} catch (err) {
|
|
reject(normalizeStorageError(err));
|
|
}
|
|
});
|
|
}
|
|
|
|
function localSet(obj) {
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
if (!extensionContextAlive()) {
|
|
reject(extensionContextError());
|
|
return;
|
|
}
|
|
chrome.storage.local.set(obj, () => {
|
|
if (rejectLastError(reject)) return;
|
|
resolve();
|
|
});
|
|
} catch (err) {
|
|
reject(normalizeStorageError(err));
|
|
}
|
|
});
|
|
}
|
|
|
|
function sessionGet(keys) {
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
if (!extensionContextAlive()) {
|
|
reject(extensionContextError());
|
|
return;
|
|
}
|
|
const store = chrome.storage.session || chrome.storage.local;
|
|
store.get(keys, (data) => {
|
|
if (rejectLastError(reject)) return;
|
|
resolve(data);
|
|
});
|
|
} catch (err) {
|
|
reject(normalizeStorageError(err));
|
|
}
|
|
});
|
|
}
|
|
|
|
function sessionSet(obj) {
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
if (!extensionContextAlive()) {
|
|
reject(extensionContextError());
|
|
return;
|
|
}
|
|
const store = chrome.storage.session || chrome.storage.local;
|
|
store.set(obj, () => {
|
|
if (rejectLastError(reject)) return;
|
|
resolve();
|
|
});
|
|
} catch (err) {
|
|
reject(normalizeStorageError(err));
|
|
}
|
|
});
|
|
}
|
|
|
|
async function loadConfig() {
|
|
const data = await localGet(Object.values(STORAGE_KEYS));
|
|
const sess = await sessionGet([SESSION_TOKEN_KEY, SESSION_TOKEN_EXPIRY_KEY]);
|
|
return {
|
|
host: data[STORAGE_KEYS.host] || DEFAULTS.host,
|
|
port: data[STORAGE_KEYS.port] || DEFAULTS.port,
|
|
https: data[STORAGE_KEYS.https] !== false,
|
|
token: sess[SESSION_TOKEN_KEY] || '',
|
|
tokenExpiresAt: sess[SESSION_TOKEN_EXPIRY_KEY] || '',
|
|
lastProjectId: data[STORAGE_KEYS.lastProjectId] || '',
|
|
lastRole: data[STORAGE_KEYS.lastRole] || '',
|
|
lastAgentMode: data[STORAGE_KEYS.lastAgentMode] || 'eino_single',
|
|
lastInstruction: data[STORAGE_KEYS.lastInstruction] || DEFAULTS.lastInstruction,
|
|
filterApiOnly: data[STORAGE_KEYS.filterApiOnly] !== false,
|
|
captureEnabled: data[STORAGE_KEYS.captureEnabled] !== false,
|
|
renderMarkdown: data[STORAGE_KEYS.renderMarkdown] !== false,
|
|
showDebugEvents: data[STORAGE_KEYS.showDebugEvents] === true,
|
|
};
|
|
}
|
|
|
|
async function saveConfig(partial) {
|
|
const localMap = {};
|
|
if (partial.host != null) localMap[STORAGE_KEYS.host] = partial.host;
|
|
if (partial.port != null) localMap[STORAGE_KEYS.port] = partial.port;
|
|
if (partial.https != null) localMap[STORAGE_KEYS.https] = partial.https;
|
|
if (partial.lastProjectId != null) localMap[STORAGE_KEYS.lastProjectId] = partial.lastProjectId;
|
|
if (partial.lastRole != null) localMap[STORAGE_KEYS.lastRole] = partial.lastRole;
|
|
if (partial.lastAgentMode != null) localMap[STORAGE_KEYS.lastAgentMode] = partial.lastAgentMode;
|
|
if (partial.lastInstruction != null) localMap[STORAGE_KEYS.lastInstruction] = partial.lastInstruction;
|
|
if (partial.filterApiOnly != null) localMap[STORAGE_KEYS.filterApiOnly] = partial.filterApiOnly;
|
|
if (partial.captureEnabled != null) localMap[STORAGE_KEYS.captureEnabled] = partial.captureEnabled;
|
|
if (partial.renderMarkdown != null) localMap[STORAGE_KEYS.renderMarkdown] = partial.renderMarkdown;
|
|
if (partial.showDebugEvents != null) localMap[STORAGE_KEYS.showDebugEvents] = partial.showDebugEvents;
|
|
if (Object.keys(localMap).length) await localSet(localMap);
|
|
const sessionMap = {};
|
|
if (partial.token != null) sessionMap[SESSION_TOKEN_KEY] = partial.token;
|
|
if (partial.tokenExpiresAt != null) sessionMap[SESSION_TOKEN_EXPIRY_KEY] = partial.tokenExpiresAt;
|
|
if (Object.keys(sessionMap).length) await sessionSet(sessionMap);
|
|
}
|
|
|
|
function baseUrlFrom(cfg) {
|
|
const scheme = cfg.https ? 'https' : 'http';
|
|
return `${scheme}://${cfg.host}:${cfg.port}`;
|
|
}
|
|
|
|
/** Request optional host permission for the configured CyberStrikeAI origin. */
|
|
async function ensureHostPermission(baseUrl) {
|
|
if (!extensionContextAlive()) throw extensionContextError();
|
|
if (!chrome.permissions || !chrome.permissions.request) return;
|
|
let origin;
|
|
try {
|
|
origin = new URL(baseUrl).origin + '/*';
|
|
} catch (_) {
|
|
throw new Error('Invalid Host/Port');
|
|
}
|
|
const has = await chrome.permissions.contains({ origins: [origin] });
|
|
if (has) return;
|
|
const granted = await chrome.permissions.request({ origins: [origin] });
|
|
if (!granted) {
|
|
throw new Error('Permission required to access the CyberStrikeAI server');
|
|
}
|
|
}
|