mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-18 23:02:14 +00:00
Integrate JailJS for CSP-restricted execution in browser extension
Major changes: - Migrate browser-extension to use web-ui package (85% code reduction) - Add JailJS content script with ES6+ transform support - Expose DOM constructors (Event, KeyboardEvent, etc.) to JailJS - Support top-level await by wrapping code in async IIFE - Add returnFile() support in JailJS execution - Refactor KeyStore into pluggable storage-adapter pattern - Make ChatPanel configurable with sandboxUrlProvider and additionalTools - Update jailjs to 0.1.1 Files deleted (33 duplicate files): - All browser-extension components, dialogs, state, tools, utils - Now using web-ui versions via @mariozechner/pi-web-ui Files added: - packages/browser-extension/src/content.ts (JailJS content script) - packages/web-ui/src/state/storage-adapter.ts - packages/web-ui/src/state/key-store.ts Browser extension now has only 5 source files (down from 38).
This commit is contained in:
parent
f2eecb78d2
commit
aaea0f4600
61 changed files with 633 additions and 9270 deletions
|
|
@ -1,96 +0,0 @@
|
|||
import { getProviders } from "@mariozechner/pi-ai";
|
||||
|
||||
/**
|
||||
* Generic storage adapter interface
|
||||
*/
|
||||
export interface StorageAdapter {
|
||||
get(key: string): Promise<string | null>;
|
||||
set(key: string, value: string): Promise<void>;
|
||||
remove(key: string): Promise<void>;
|
||||
getAll(): Promise<Record<string, string>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for API key storage
|
||||
*/
|
||||
export interface KeyStore {
|
||||
getKey(provider: string): Promise<string | null>;
|
||||
setKey(provider: string, key: string): Promise<void>;
|
||||
removeKey(provider: string): Promise<void>;
|
||||
getAllKeys(): Promise<Record<string, boolean>>; // provider -> isConfigured
|
||||
}
|
||||
|
||||
/**
|
||||
* Default localStorage implementation for web
|
||||
*/
|
||||
class LocalStorageAdapter implements StorageAdapter {
|
||||
async get(key: string): Promise<string | null> {
|
||||
return localStorage.getItem(key);
|
||||
}
|
||||
|
||||
async set(key: string, value: string): Promise<void> {
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
async remove(key: string): Promise<void> {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
|
||||
async getAll(): Promise<Record<string, string>> {
|
||||
const result: Record<string, string> = {};
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
if (key) {
|
||||
const value = localStorage.getItem(key);
|
||||
if (value) result[key] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic KeyStore implementation
|
||||
*/
|
||||
class GenericKeyStore implements KeyStore {
|
||||
private readonly prefix = "apiKey_";
|
||||
private readonly storage: StorageAdapter;
|
||||
|
||||
constructor(storage?: StorageAdapter) {
|
||||
this.storage = storage || new LocalStorageAdapter();
|
||||
}
|
||||
|
||||
async getKey(provider: string): Promise<string | null> {
|
||||
const key = `${this.prefix}${provider}`;
|
||||
return await this.storage.get(key);
|
||||
}
|
||||
|
||||
async setKey(provider: string, key: string): Promise<void> {
|
||||
const storageKey = `${this.prefix}${provider}`;
|
||||
await this.storage.set(storageKey, key);
|
||||
}
|
||||
|
||||
async removeKey(provider: string): Promise<void> {
|
||||
const key = `${this.prefix}${provider}`;
|
||||
await this.storage.remove(key);
|
||||
}
|
||||
|
||||
async getAllKeys(): Promise<Record<string, boolean>> {
|
||||
const providers = getProviders();
|
||||
const allStorage = await this.storage.getAll();
|
||||
const result: Record<string, boolean> = {};
|
||||
|
||||
for (const provider of providers) {
|
||||
const key = `${this.prefix}${provider}`;
|
||||
result[provider] = !!allStorage[key];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance (uses localStorage by default)
|
||||
export const keyStore = new GenericKeyStore();
|
||||
|
||||
// Export class for custom storage implementations
|
||||
export { GenericKeyStore as KeyStoreImpl };
|
||||
67
packages/web-ui/src/state/key-store.ts
Normal file
67
packages/web-ui/src/state/key-store.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { getProviders } from "@mariozechner/pi-ai";
|
||||
import { LocalStorageAdapter, type StorageAdapter } from "./storage-adapter.js";
|
||||
|
||||
/**
|
||||
* API key storage interface
|
||||
*/
|
||||
export interface KeyStore {
|
||||
getKey(provider: string): Promise<string | null>;
|
||||
setKey(provider: string, key: string): Promise<void>;
|
||||
removeKey(provider: string): Promise<void>;
|
||||
getAllKeys(): Promise<Record<string, boolean>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* API key storage implementation using a pluggable storage adapter
|
||||
*/
|
||||
export class LocalStorageKeyStore implements KeyStore {
|
||||
private readonly prefix = "apiKey_";
|
||||
|
||||
constructor(private readonly storage: StorageAdapter) {}
|
||||
|
||||
async getKey(provider: string): Promise<string | null> {
|
||||
const key = `${this.prefix}${provider}`;
|
||||
return await this.storage.get(key);
|
||||
}
|
||||
|
||||
async setKey(provider: string, key: string): Promise<void> {
|
||||
const storageKey = `${this.prefix}${provider}`;
|
||||
await this.storage.set(storageKey, key);
|
||||
}
|
||||
|
||||
async removeKey(provider: string): Promise<void> {
|
||||
const key = `${this.prefix}${provider}`;
|
||||
await this.storage.remove(key);
|
||||
}
|
||||
|
||||
async getAllKeys(): Promise<Record<string, boolean>> {
|
||||
const providers = getProviders();
|
||||
const allStorage = await this.storage.getAll();
|
||||
const result: Record<string, boolean> = {};
|
||||
|
||||
for (const provider of providers) {
|
||||
const key = `${this.prefix}${provider}`;
|
||||
result[provider] = !!allStorage[key];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Default instance using localStorage
|
||||
let _keyStore: KeyStore = new LocalStorageKeyStore(new LocalStorageAdapter());
|
||||
|
||||
/**
|
||||
* Get the current KeyStore instance
|
||||
*/
|
||||
export function getKeyStore(): KeyStore {
|
||||
return _keyStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom KeyStore implementation
|
||||
* Call this once at application startup before any components are initialized
|
||||
*/
|
||||
export function setKeyStore(store: KeyStore): void {
|
||||
_keyStore = store;
|
||||
}
|
||||
77
packages/web-ui/src/state/storage-adapter.ts
Normal file
77
packages/web-ui/src/state/storage-adapter.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* Generic storage adapter interface for key/value persistence
|
||||
*/
|
||||
export interface StorageAdapter {
|
||||
get(key: string): Promise<string | null>;
|
||||
set(key: string, value: string): Promise<void>;
|
||||
remove(key: string): Promise<void>;
|
||||
getAll(): Promise<Record<string, string>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* LocalStorage implementation
|
||||
*/
|
||||
export class LocalStorageAdapter implements StorageAdapter {
|
||||
async get(key: string): Promise<string | null> {
|
||||
return localStorage.getItem(key);
|
||||
}
|
||||
|
||||
async set(key: string, value: string): Promise<void> {
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
async remove(key: string): Promise<void> {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
|
||||
async getAll(): Promise<Record<string, string>> {
|
||||
const result: Record<string, string> = {};
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
if (key) {
|
||||
const value = localStorage.getItem(key);
|
||||
if (value) result[key] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Chrome/Firefox extension storage implementation
|
||||
*/
|
||||
export class ChromeStorageAdapter implements StorageAdapter {
|
||||
private readonly storage: any;
|
||||
|
||||
constructor() {
|
||||
const isBrowser = typeof globalThis !== "undefined";
|
||||
const hasChrome = isBrowser && (globalThis as any).chrome?.storage;
|
||||
const hasBrowser = isBrowser && (globalThis as any).browser?.storage;
|
||||
|
||||
if (hasBrowser) {
|
||||
this.storage = (globalThis as any).browser.storage.local;
|
||||
} else if (hasChrome) {
|
||||
this.storage = (globalThis as any).chrome.storage.local;
|
||||
} else {
|
||||
throw new Error("Chrome/Browser storage not available");
|
||||
}
|
||||
}
|
||||
|
||||
async get(key: string): Promise<string | null> {
|
||||
const result = await this.storage.get(key);
|
||||
return result[key] || null;
|
||||
}
|
||||
|
||||
async set(key: string, value: string): Promise<void> {
|
||||
await this.storage.set({ [key]: value });
|
||||
}
|
||||
|
||||
async remove(key: string): Promise<void> {
|
||||
await this.storage.remove(key);
|
||||
}
|
||||
|
||||
async getAll(): Promise<Record<string, string>> {
|
||||
const result = await this.storage.get();
|
||||
return result || {};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { type AgentContext, agentLoop, type Message, type PromptConfig, type UserMessage } from "@mariozechner/pi-ai";
|
||||
import { keyStore } from "../KeyStore.js";
|
||||
import { getKeyStore } from "../key-store.js";
|
||||
import type { AgentRunConfig, AgentTransport } from "./types.js";
|
||||
|
||||
export class DirectTransport implements AgentTransport {
|
||||
|
|
@ -7,7 +7,7 @@ export class DirectTransport implements AgentTransport {
|
|||
|
||||
async *run(userMessage: Message, cfg: AgentRunConfig, signal?: AbortSignal) {
|
||||
// Get API key from KeyStore
|
||||
const apiKey = await keyStore.getKey(cfg.model.provider);
|
||||
const apiKey = await getKeyStore().getKey(cfg.model.provider);
|
||||
if (!apiKey) {
|
||||
throw new Error("no-api-key");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue