refactor!: rebrand bterminal to agor (agents-orchestrator)
Rename Cargo crates (bterminal-core→agor-core, bterminal-relay→agor-relay), env vars (BTERMINAL_*→AGOR_*), config paths (~/.config/agor), CSS custom properties, plugin API object, package names, and all documentation. BREAKING CHANGE: config/data paths changed from bterminal to agor.
This commit is contained in:
parent
ef3548a569
commit
a63e6711ac
52 changed files with 3889 additions and 169 deletions
|
|
@ -11,7 +11,7 @@ export interface PluginMeta {
|
|||
permissions: string[];
|
||||
}
|
||||
|
||||
/** Discover all plugins in ~/.config/bterminal/plugins/ */
|
||||
/** Discover all plugins in ~/.config/agor/plugins/ */
|
||||
export async function discoverPlugins(): Promise<PluginMeta[]> {
|
||||
return invoke<PluginMeta[]>('plugins_discover');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -748,7 +748,7 @@
|
|||
flex: 1;
|
||||
overflow-y: auto;
|
||||
container-type: inline-size;
|
||||
padding: 0.5rem var(--bterminal-pane-padding-inline, 0.75rem);
|
||||
padding: 0.5rem var(--agor-pane-padding-inline, 0.75rem);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.125rem;
|
||||
|
|
@ -1270,7 +1270,7 @@
|
|||
|
||||
/* === Status strip === */
|
||||
.status-strip {
|
||||
padding: 0.25rem var(--bterminal-pane-padding-inline, 0.75rem);
|
||||
padding: 0.25rem var(--agor-pane-padding-inline, 0.75rem);
|
||||
border-top: 1px solid var(--ctp-surface1);
|
||||
flex-shrink: 0;
|
||||
font-size: 0.8125rem;
|
||||
|
|
@ -1408,7 +1408,7 @@
|
|||
.session-controls {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
padding: 0.375rem var(--bterminal-pane-padding-inline, 0.75rem);
|
||||
padding: 0.375rem var(--agor-pane-padding-inline, 0.75rem);
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
|
@ -1451,7 +1451,7 @@
|
|||
|
||||
/* === Prompt container === */
|
||||
.prompt-container {
|
||||
padding: 0.5rem var(--bterminal-pane-padding-inline, 0.75rem);
|
||||
padding: 0.5rem var(--agor-pane-padding-inline, 0.75rem);
|
||||
flex-shrink: 0;
|
||||
border-top: 1px solid var(--ctp-surface0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@
|
|||
}
|
||||
|
||||
.markdown-body {
|
||||
padding: 1.5rem var(--bterminal-pane-padding-inline, 2rem);
|
||||
padding: 1.5rem var(--agor-pane-padding-inline, 2rem);
|
||||
font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.7;
|
||||
|
|
|
|||
|
|
@ -885,7 +885,7 @@
|
|||
<section class="settings-section">
|
||||
<h2>Plugins</h2>
|
||||
{#if pluginEntries.length === 0}
|
||||
<p class="empty-notice">No plugins found in ~/.config/bterminal/plugins/</p>
|
||||
<p class="empty-notice">No plugins found in ~/.config/agor/plugins/</p>
|
||||
{:else}
|
||||
<div class="plugin-list">
|
||||
{#each pluginEntries as entry (entry.meta.id)}
|
||||
|
|
|
|||
|
|
@ -73,15 +73,15 @@ class MockWorker {
|
|||
const permissions = (data.permissions as string[]) || [];
|
||||
const meta = data.meta as Record<string, unknown>;
|
||||
|
||||
// Build a mock bterminal API that mimics worker-side behavior
|
||||
// Build a mock agor API that mimics worker-side behavior
|
||||
// by sending messages back to the main thread (this.sendToMain)
|
||||
const bterminal: Record<string, unknown> = {
|
||||
const agor: Record<string, unknown> = {
|
||||
meta: Object.freeze({ ...meta }),
|
||||
};
|
||||
|
||||
if (permissions.includes('palette')) {
|
||||
let cbId = 0;
|
||||
bterminal.palette = {
|
||||
agor.palette = {
|
||||
registerCommand: (label: string, callback: () => void) => {
|
||||
if (typeof label !== 'string' || !label.trim()) {
|
||||
throw new Error('Command label must be a non-empty string');
|
||||
|
|
@ -96,14 +96,14 @@ class MockWorker {
|
|||
}
|
||||
|
||||
if (permissions.includes('bttask:read')) {
|
||||
bterminal.tasks = {
|
||||
agor.tasks = {
|
||||
list: () => this.rpc('tasks.list', {}),
|
||||
comments: (taskId: string) => this.rpc('tasks.comments', { taskId }),
|
||||
};
|
||||
}
|
||||
|
||||
if (permissions.includes('btmsg:read')) {
|
||||
bterminal.messages = {
|
||||
agor.messages = {
|
||||
inbox: () => this.rpc('messages.inbox', {}),
|
||||
channels: () => this.rpc('messages.channels', {}),
|
||||
};
|
||||
|
|
@ -111,7 +111,7 @@ class MockWorker {
|
|||
|
||||
if (permissions.includes('events')) {
|
||||
let cbId = 0;
|
||||
bterminal.events = {
|
||||
agor.events = {
|
||||
on: (event: string, callback: (data: unknown) => void) => {
|
||||
if (typeof event !== 'string' || typeof callback !== 'function') {
|
||||
throw new Error('event.on requires (string, function)');
|
||||
|
|
@ -125,12 +125,12 @@ class MockWorker {
|
|||
};
|
||||
}
|
||||
|
||||
Object.freeze(bterminal);
|
||||
Object.freeze(agor);
|
||||
|
||||
// Execute the plugin code
|
||||
try {
|
||||
const fn = new Function('bterminal', `"use strict"; ${code}`);
|
||||
fn(bterminal);
|
||||
const fn = new Function('agor', `"use strict"; ${code}`);
|
||||
fn(agor);
|
||||
this.sendToMain({ type: 'loaded' });
|
||||
} catch (err) {
|
||||
this.sendToMain({ type: 'error', message: String(err) });
|
||||
|
|
@ -242,7 +242,7 @@ describe('plugin-host Worker isolation', () => {
|
|||
const meta = makeMeta({ id: 'freeze-test', permissions: [] });
|
||||
mockPluginCode(`
|
||||
try {
|
||||
bterminal.hacked = true;
|
||||
agor.hacked = true;
|
||||
throw new Error('FREEZE FAILED: could add property');
|
||||
} catch (e) {
|
||||
if (e.message === 'FREEZE FAILED: could add property') throw e;
|
||||
|
|
@ -255,7 +255,7 @@ describe('plugin-host Worker isolation', () => {
|
|||
const meta = makeMeta({ id: 'freeze-delete-test', permissions: [] });
|
||||
mockPluginCode(`
|
||||
try {
|
||||
delete bterminal.meta;
|
||||
delete agor.meta;
|
||||
throw new Error('FREEZE FAILED: could delete property');
|
||||
} catch (e) {
|
||||
if (e.message === 'FREEZE FAILED: could delete property') throw e;
|
||||
|
|
@ -267,14 +267,14 @@ describe('plugin-host Worker isolation', () => {
|
|||
it('meta is accessible and frozen', async () => {
|
||||
const meta = makeMeta({ id: 'meta-access', permissions: [] });
|
||||
mockPluginCode(`
|
||||
if (bterminal.meta.id !== 'meta-access') {
|
||||
if (agor.meta.id !== 'meta-access') {
|
||||
throw new Error('meta.id mismatch');
|
||||
}
|
||||
if (bterminal.meta.name !== 'Test Plugin') {
|
||||
if (agor.meta.name !== 'Test Plugin') {
|
||||
throw new Error('meta.name mismatch');
|
||||
}
|
||||
try {
|
||||
bterminal.meta.id = 'hacked';
|
||||
agor.meta.id = 'hacked';
|
||||
throw new Error('META FREEZE FAILED');
|
||||
} catch (e) {
|
||||
if (e.message === 'META FREEZE FAILED') throw e;
|
||||
|
|
@ -291,7 +291,7 @@ describe('plugin-host permissions', () => {
|
|||
it('plugin with palette permission can register commands', async () => {
|
||||
const meta = makeMeta({ id: 'palette-plugin', permissions: ['palette'] });
|
||||
mockPluginCode(`
|
||||
bterminal.palette.registerCommand('Test Command', function() {});
|
||||
agor.palette.registerCommand('Test Command', function() {});
|
||||
`);
|
||||
|
||||
await loadPlugin(meta, GROUP_ID, AGENT_ID);
|
||||
|
|
@ -306,7 +306,7 @@ describe('plugin-host permissions', () => {
|
|||
it('plugin without palette permission has no palette API', async () => {
|
||||
const meta = makeMeta({ id: 'no-palette-plugin', permissions: [] });
|
||||
mockPluginCode(`
|
||||
if (bterminal.palette !== undefined) {
|
||||
if (agor.palette !== undefined) {
|
||||
throw new Error('palette API should not be available');
|
||||
}
|
||||
`);
|
||||
|
|
@ -316,7 +316,7 @@ describe('plugin-host permissions', () => {
|
|||
it('palette.registerCommand rejects non-string label', async () => {
|
||||
const meta = makeMeta({ id: 'bad-label-plugin', permissions: ['palette'] });
|
||||
mockPluginCode(`
|
||||
bterminal.palette.registerCommand(123, function() {});
|
||||
agor.palette.registerCommand(123, function() {});
|
||||
`);
|
||||
await expect(loadPlugin(meta, GROUP_ID, AGENT_ID)).rejects.toThrow(
|
||||
'execution failed',
|
||||
|
|
@ -326,7 +326,7 @@ describe('plugin-host permissions', () => {
|
|||
it('palette.registerCommand rejects non-function callback', async () => {
|
||||
const meta = makeMeta({ id: 'bad-cb-plugin', permissions: ['palette'] });
|
||||
mockPluginCode(`
|
||||
bterminal.palette.registerCommand('Test', 'not-a-function');
|
||||
agor.palette.registerCommand('Test', 'not-a-function');
|
||||
`);
|
||||
await expect(loadPlugin(meta, GROUP_ID, AGENT_ID)).rejects.toThrow(
|
||||
'execution failed',
|
||||
|
|
@ -336,7 +336,7 @@ describe('plugin-host permissions', () => {
|
|||
it('palette.registerCommand rejects empty label', async () => {
|
||||
const meta = makeMeta({ id: 'empty-label-plugin', permissions: ['palette'] });
|
||||
mockPluginCode(`
|
||||
bterminal.palette.registerCommand(' ', function() {});
|
||||
agor.palette.registerCommand(' ', function() {});
|
||||
`);
|
||||
await expect(loadPlugin(meta, GROUP_ID, AGENT_ID)).rejects.toThrow(
|
||||
'execution failed',
|
||||
|
|
@ -348,7 +348,7 @@ describe('plugin-host permissions', () => {
|
|||
it('plugin with bttask:read can call tasks.list', async () => {
|
||||
const meta = makeMeta({ id: 'task-plugin', permissions: ['bttask:read'] });
|
||||
mockPluginCode(`
|
||||
bterminal.tasks.list();
|
||||
agor.tasks.list();
|
||||
`);
|
||||
await expect(loadPlugin(meta, GROUP_ID, AGENT_ID)).resolves.toBeUndefined();
|
||||
});
|
||||
|
|
@ -356,7 +356,7 @@ describe('plugin-host permissions', () => {
|
|||
it('plugin without bttask:read has no tasks API', async () => {
|
||||
const meta = makeMeta({ id: 'no-task-plugin', permissions: [] });
|
||||
mockPluginCode(`
|
||||
if (bterminal.tasks !== undefined) {
|
||||
if (agor.tasks !== undefined) {
|
||||
throw new Error('tasks API should not be available');
|
||||
}
|
||||
`);
|
||||
|
|
@ -368,7 +368,7 @@ describe('plugin-host permissions', () => {
|
|||
it('plugin with btmsg:read can call messages.inbox', async () => {
|
||||
const meta = makeMeta({ id: 'msg-plugin', permissions: ['btmsg:read'] });
|
||||
mockPluginCode(`
|
||||
bterminal.messages.inbox();
|
||||
agor.messages.inbox();
|
||||
`);
|
||||
await expect(loadPlugin(meta, GROUP_ID, AGENT_ID)).resolves.toBeUndefined();
|
||||
});
|
||||
|
|
@ -376,7 +376,7 @@ describe('plugin-host permissions', () => {
|
|||
it('plugin without btmsg:read has no messages API', async () => {
|
||||
const meta = makeMeta({ id: 'no-msg-plugin', permissions: [] });
|
||||
mockPluginCode(`
|
||||
if (bterminal.messages !== undefined) {
|
||||
if (agor.messages !== undefined) {
|
||||
throw new Error('messages API should not be available');
|
||||
}
|
||||
`);
|
||||
|
|
@ -388,7 +388,7 @@ describe('plugin-host permissions', () => {
|
|||
it('plugin with events permission can subscribe', async () => {
|
||||
const meta = makeMeta({ id: 'events-plugin', permissions: ['events'] });
|
||||
mockPluginCode(`
|
||||
bterminal.events.on('test-event', function(data) {});
|
||||
agor.events.on('test-event', function(data) {});
|
||||
`);
|
||||
await loadPlugin(meta, GROUP_ID, AGENT_ID);
|
||||
expect(pluginEventBus.on).toHaveBeenCalledWith('test-event', expect.any(Function));
|
||||
|
|
@ -397,7 +397,7 @@ describe('plugin-host permissions', () => {
|
|||
it('plugin without events permission has no events API', async () => {
|
||||
const meta = makeMeta({ id: 'no-events-plugin', permissions: [] });
|
||||
mockPluginCode(`
|
||||
if (bterminal.events !== undefined) {
|
||||
if (agor.events !== undefined) {
|
||||
throw new Error('events API should not be available');
|
||||
}
|
||||
`);
|
||||
|
|
@ -437,7 +437,7 @@ describe('plugin-host lifecycle', () => {
|
|||
it('unloadPlugin removes the plugin and cleans up commands', async () => {
|
||||
const meta = makeMeta({ id: 'lifecycle-unload', permissions: ['palette'] });
|
||||
mockPluginCode(`
|
||||
bterminal.palette.registerCommand('Cmd1', function() {});
|
||||
agor.palette.registerCommand('Cmd1', function() {});
|
||||
`);
|
||||
|
||||
await loadPlugin(meta, GROUP_ID, AGENT_ID);
|
||||
|
|
@ -491,7 +491,7 @@ describe('plugin-host lifecycle', () => {
|
|||
it('unloadPlugin cleans up event subscriptions', async () => {
|
||||
const meta = makeMeta({ id: 'events-cleanup', permissions: ['events'] });
|
||||
mockPluginCode(`
|
||||
bterminal.events.on('my-event', function() {});
|
||||
agor.events.on('my-event', function() {});
|
||||
`);
|
||||
|
||||
await loadPlugin(meta, GROUP_ID, AGENT_ID);
|
||||
|
|
@ -507,11 +507,11 @@ describe('plugin-host lifecycle', () => {
|
|||
describe('plugin-host RPC routing', () => {
|
||||
it('tasks.list RPC is routed to main thread', async () => {
|
||||
const meta = makeMeta({ id: 'rpc-tasks', permissions: ['bttask:read'] });
|
||||
mockPluginCode(`bterminal.tasks.list();`);
|
||||
mockPluginCode(`agor.tasks.list();`);
|
||||
|
||||
// Mock the bttask bridge
|
||||
mockInvoke.mockImplementation((cmd: string) => {
|
||||
if (cmd === 'plugin_read_file') return Promise.resolve('bterminal.tasks.list();');
|
||||
if (cmd === 'plugin_read_file') return Promise.resolve('agor.tasks.list();');
|
||||
if (cmd === 'bttask_list') return Promise.resolve([]);
|
||||
return Promise.reject(new Error(`Unexpected: ${cmd}`));
|
||||
});
|
||||
|
|
@ -521,10 +521,10 @@ describe('plugin-host RPC routing', () => {
|
|||
|
||||
it('messages.inbox RPC is routed to main thread', async () => {
|
||||
const meta = makeMeta({ id: 'rpc-messages', permissions: ['btmsg:read'] });
|
||||
mockPluginCode(`bterminal.messages.inbox();`);
|
||||
mockPluginCode(`agor.messages.inbox();`);
|
||||
|
||||
mockInvoke.mockImplementation((cmd: string) => {
|
||||
if (cmd === 'plugin_read_file') return Promise.resolve('bterminal.messages.inbox();');
|
||||
if (cmd === 'plugin_read_file') return Promise.resolve('agor.messages.inbox();');
|
||||
if (cmd === 'btmsg_get_unread') return Promise.resolve([]);
|
||||
return Promise.reject(new Error(`Unexpected: ${cmd}`));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Plugin Host — Web Worker sandbox for BTerminal plugins.
|
||||
* Plugin Host — Web Worker sandbox for Agents Orchestrator plugins.
|
||||
*
|
||||
* Each plugin runs in a dedicated Web Worker, providing true process-level
|
||||
* isolation from the main thread. The Worker has no access to the DOM,
|
||||
|
|
@ -38,7 +38,7 @@ const loadedPlugins = new Map<string, LoadedPlugin>();
|
|||
|
||||
/**
|
||||
* Build the Worker script as an inline blob.
|
||||
* The Worker receives plugin code + permissions and builds a sandboxed bterminal API
|
||||
* The Worker receives plugin code + permissions and builds a sandboxed agor API
|
||||
* that proxies all calls to the main thread via postMessage.
|
||||
*/
|
||||
function buildWorkerScript(): string {
|
||||
|
|
@ -73,7 +73,7 @@ self.onmessage = function(e) {
|
|||
const permissions = msg.permissions || [];
|
||||
const meta = msg.meta;
|
||||
|
||||
// Build the bterminal API based on permissions
|
||||
// Build the agor API based on permissions
|
||||
const api = { meta: Object.freeze(meta) };
|
||||
|
||||
if (permissions.includes('palette')) {
|
||||
|
|
@ -128,7 +128,7 @@ self.onmessage = function(e) {
|
|||
// Execute the plugin code
|
||||
try {
|
||||
const fn = (0, eval)(
|
||||
'(function(bterminal) { "use strict"; ' + msg.code + '\\n})'
|
||||
'(function(agor) { "use strict"; ' + msg.code + '\\n})'
|
||||
);
|
||||
fn(api);
|
||||
self.postMessage({ type: 'loaded' });
|
||||
|
|
|
|||
|
|
@ -57,5 +57,5 @@
|
|||
--border-radius: 4px;
|
||||
|
||||
/* Pane content padding — shared between AgentPane and MarkdownPane */
|
||||
--bterminal-pane-padding-inline: clamp(0.75rem, 3.5cqi, 2rem);
|
||||
--agor-pane-padding-inline: clamp(0.75rem, 3.5cqi, 2rem);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ function buildEnvironmentSection(group: GroupConfig): string {
|
|||
|
||||
return `## Environment
|
||||
|
||||
**Platform:** BTerminal Mission Control — multi-agent orchestration system
|
||||
**Platform:** Agents Orchestrator Mission Control — multi-agent orchestration system
|
||||
**Group:** ${group.name}
|
||||
**Your working directory:** Same as the monorepo root (shared across Tier 1 agents)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export async function detachPane(pane: Pane): Promise<void> {
|
|||
|
||||
const webview = new WebviewWindow(label, {
|
||||
url: `index.html?${params.toString()}`,
|
||||
title: `BTerminal — ${pane.title}`,
|
||||
title: `Agents Orchestrator — ${pane.title}`,
|
||||
width: 800,
|
||||
height: 600,
|
||||
decorations: true,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue