abx-spec-behaviors
abx-spec-behaviors copied to clipboard
Alternative approach: implement it via browser extension exclusively
// manifest.json
{
"manifest_version": 3,
"name": "Behavior Extension",
"version": "1.0",
"permissions": [
"tabs",
"activeTab",
"storage",
"debugger",
"webNavigation",
"webRequest",
"scripting"
],
"host_permissions": [
"<all_urls>"
],
"background": {
"service_worker": "background.js",
"type": "module"
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["behavior_bus.js", "content_script.js"],
"run_at": "document_start"
}]
}
// background.js (Service Worker Context)
import { ServiceWorkerBehaviorBus, BehaviorEvent } from './behavior_bus.js';
// Create a BehaviorBus instance for the service worker context
const backgroundBus = new ServiceWorkerBehaviorBus();
// Load behaviors from extension storage or a remote source
let BEHAVIORS = [];
chrome.storage.local.get('behaviors', ({behaviors}) => {
BEHAVIORS = behaviors || [];
backgroundBus.attachBehaviors(BEHAVIORS);
});
// Listen for tab lifecycle events and forward them to the BehaviorBus
chrome.webNavigation.onCommitted.addListener((details) => {
if (details.frameId === 0) { // main frame only
backgroundBus.emit({
type: 'PAGE_SETUP',
url: details.url,
tabId: details.tabId
});
}
});
chrome.webNavigation.onCompleted.addListener((details) => {
if (details.frameId === 0) {
backgroundBus.emit({
type: 'PAGE_LOAD',
url: details.url,
tabId: details.tabId
});
}
});
// Handle messages from content scripts
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'BEHAVIOR_EVENT') {
// Forward events from content script to background BehaviorBus
backgroundBus.emit(message.event);
}
else if (message.type === 'FS_WRITE_FILE') {
// Handle file system operations using chrome.downloads API
chrome.downloads.download({
url: URL.createObjectURL(new Blob([message.content])),
filename: message.path,
saveAs: false
});
}
});
// content_script.js (Page Context)
import { WindowBehaviorBus, BehaviorEvent } from './behavior_bus.js';
// Create a BehaviorBus instance for the page context
const pageBus = new WindowBehaviorBus();
// Load behaviors from extension storage
chrome.storage.local.get('behaviors', ({behaviors}) => {
pageBus.attachBehaviors(behaviors || []);
pageBus.attachContext(window);
});
// Forward all events from page BehaviorBus to background service worker
pageBus.on('*', (event) => {
chrome.runtime.sendMessage({
type: 'BEHAVIOR_EVENT',
event: event.detail
});
});
// Listen for messages from background script
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'BEHAVIOR_EVENT') {
// Forward events from background to page BehaviorBus
pageBus.emit(message.event);
}
});
// Example implementation of a behavior
const ExtractArticleTextBehavior = {
name: 'ExtractArticleTextBehavior',
schema: '[email protected]',
hooks: {
window: {
PAGE_CAPTURE: async (event, BehaviorBus, window) => {
const text = window.document.body.innerText;
BehaviorBus.emit({
type: 'DISCOVERED_TEXT',
selector: 'body',
text
});
// This will be handled by the background script's download API
BehaviorBus.emit({
type: 'FS_WRITE_FILE',
path: 'article.txt',
content: text
});
}
},
serviceworker: {
// Hooks that run in extension background context
PAGE_SETUP: async (event, BehaviorBus, serviceWorkerGlobal) => {
// Setup debugger APIs or other extension-specific functionality
if (event.tabId) {
await chrome.debugger.attach({tabId: event.tabId}, '1.3');
// ... setup other debugger listeners
}
}
}
}
};
// behavior_bus.js - Core implementation
export class BehaviorEvent extends CustomEvent {
constructor(type, detail = {}) {
super(type, {
detail: {
type,
metadata: {
id: crypto.randomUUID(),
timestamp: Date.now(),
path: []
},
...detail
}
});
}
}
export class BaseBehaviorBus extends EventTarget {
constructor(behaviors = [], context = null) {
super();
this.behaviors = [];
this.context = null;
if (behaviors) this.attachBehaviors(behaviors);
if (context) this.attachContext(context);
}
attachContext(context) {
this.context = context;
}
attachBehaviors(behaviors) {
for (const behavior of behaviors) {
this.behaviors.push(behavior);
// Attach event listeners based on behavior hooks
// ... implementation details
}
}
emit(event) {
if (!(event instanceof BehaviorEvent)) {
event = new BehaviorEvent(event.type, event);
}
return this.dispatchEvent(event);
}
}
export class WindowBehaviorBus extends BaseBehaviorBus {
// Implementation for page context
}
export class ServiceWorkerBehaviorBus extends BaseBehaviorBus {
// Implementation for service worker context
}