openbot 0.2.12 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +8 -0
- package/AGENTS.md +68 -0
- package/CONTRIBUTING.md +74 -0
- package/LICENSE +21 -0
- package/README.md +117 -14
- package/dist/agents/system.js +106 -0
- package/dist/app/cli.js +27 -0
- package/dist/app/config.js +64 -0
- package/dist/app/server.js +237 -0
- package/dist/app/utils.js +35 -0
- package/dist/harness/agent-harness.js +45 -0
- package/dist/harness/mcp.js +61 -0
- package/dist/harness/orchestrator.js +273 -0
- package/dist/harness/process.js +7 -0
- package/dist/plugins/ai-sdk.js +141 -0
- package/dist/plugins/delegation.js +52 -0
- package/dist/plugins/mcp.js +140 -0
- package/dist/plugins/storage.js +502 -0
- package/dist/plugins/ui.js +47 -0
- package/dist/registry/plugins.js +73 -0
- package/dist/services/storage.js +724 -0
- package/docs/README.md +7 -0
- package/docs/agents.md +83 -0
- package/docs/architecture.md +34 -0
- package/docs/plugins.md +77 -0
- package/logo-black.png +0 -0
- package/{dist/assets/logo.js → logo-black.svg} +24 -24
- package/{dist/ui/sidebar.js → logo-white.svg} +23 -88
- package/package.json +6 -5
- package/src/agents/system.ts +112 -0
- package/src/app/cli.ts +38 -0
- package/src/app/config.ts +104 -0
- package/src/app/server.ts +284 -0
- package/src/app/types.ts +476 -0
- package/src/app/utils.ts +43 -0
- package/src/assets/icon.svg +1 -0
- package/src/harness/agent-harness.ts +58 -0
- package/src/harness/mcp.ts +78 -0
- package/src/harness/orchestrator.ts +342 -0
- package/src/harness/process.ts +9 -0
- package/src/harness/types.ts +34 -0
- package/src/plugins/ai-sdk.ts +197 -0
- package/src/plugins/delegation.ts +60 -0
- package/src/plugins/mcp.ts +154 -0
- package/src/plugins/storage.ts +725 -0
- package/src/plugins/ui.ts +57 -0
- package/src/registry/plugins.ts +85 -0
- package/src/services/storage.ts +957 -0
- package/tsconfig.json +18 -0
- package/dist/agents/agent-creator.js +0 -74
- package/dist/agents/browser-agent.js +0 -31
- package/dist/agents/os-agent.js +0 -32
- package/dist/agents/planner-agent.js +0 -32
- package/dist/agents/topic-agent.js +0 -46
- package/dist/architecture/execution-engine.js +0 -151
- package/dist/architecture/intent-classifier.js +0 -26
- package/dist/architecture/planner.js +0 -106
- package/dist/automation-worker.js +0 -121
- package/dist/automations.js +0 -52
- package/dist/cli.js +0 -279
- package/dist/config.js +0 -53
- package/dist/core/agents.js +0 -41
- package/dist/core/delegation.js +0 -230
- package/dist/core/manager.js +0 -96
- package/dist/core/plugins.js +0 -74
- package/dist/core/router.js +0 -191
- package/dist/handlers/init.js +0 -29
- package/dist/handlers/session-change.js +0 -21
- package/dist/handlers/settings.js +0 -47
- package/dist/handlers/tab-change.js +0 -14
- package/dist/installers.js +0 -156
- package/dist/marketplace.js +0 -80
- package/dist/model-catalog.js +0 -132
- package/dist/model-defaults.js +0 -25
- package/dist/models.js +0 -47
- package/dist/open-bot.js +0 -51
- package/dist/orchestrator/direct-invocation.js +0 -13
- package/dist/orchestrator/events.js +0 -36
- package/dist/orchestrator/state.js +0 -54
- package/dist/orchestrator.js +0 -422
- package/dist/plugins/agent/index.js +0 -81
- package/dist/plugins/approval/index.js +0 -100
- package/dist/plugins/brain/identity.js +0 -77
- package/dist/plugins/brain/index.js +0 -204
- package/dist/plugins/brain/memory.js +0 -120
- package/dist/plugins/brain/prompt.js +0 -46
- package/dist/plugins/brain/types.js +0 -45
- package/dist/plugins/brain/ui.js +0 -7
- package/dist/plugins/browser/index.js +0 -629
- package/dist/plugins/browser/ui.js +0 -13
- package/dist/plugins/file-system/index.js +0 -171
- package/dist/plugins/file-system/ui.js +0 -6
- package/dist/plugins/llm/context-budget.js +0 -139
- package/dist/plugins/llm/context-shaping.js +0 -177
- package/dist/plugins/llm/index.js +0 -380
- package/dist/plugins/memory/index.js +0 -220
- package/dist/plugins/memory/memory.js +0 -122
- package/dist/plugins/memory/prompt.js +0 -55
- package/dist/plugins/memory/types.js +0 -45
- package/dist/plugins/meta-agent/index.js +0 -570
- package/dist/plugins/meta-agent/ui.js +0 -11
- package/dist/plugins/shell/index.js +0 -100
- package/dist/plugins/shell/ui.js +0 -6
- package/dist/plugins/skills/index.js +0 -286
- package/dist/plugins/skills/types.js +0 -50
- package/dist/plugins/skills/ui.js +0 -12
- package/dist/registry/agent-registry.js +0 -35
- package/dist/registry/index.js +0 -2
- package/dist/registry/plugin-loader.js +0 -499
- package/dist/registry/plugin-registry.js +0 -44
- package/dist/registry/ts-agent-loader.js +0 -82
- package/dist/registry/yaml-agent-loader.js +0 -246
- package/dist/runtime/execution-trace.js +0 -41
- package/dist/runtime/intent-routing.js +0 -26
- package/dist/runtime/openbot-runtime.js +0 -354
- package/dist/server.js +0 -890
- package/dist/session.js +0 -179
- package/dist/ui/block.js +0 -12
- package/dist/ui/header.js +0 -52
- package/dist/ui/layout.js +0 -26
- package/dist/ui/navigation.js +0 -15
- package/dist/ui/settings.js +0 -106
- package/dist/ui/skills.js +0 -7
- package/dist/ui/thread.js +0 -16
- package/dist/ui/widgets/action-list.js +0 -2
- package/dist/ui/widgets/approval-card.js +0 -9
- package/dist/ui/widgets/code-snippet.js +0 -2
- package/dist/ui/widgets/data-block.js +0 -2
- package/dist/ui/widgets/data-table.js +0 -2
- package/dist/ui/widgets/delegation.js +0 -29
- package/dist/ui/widgets/empty-state.js +0 -2
- package/dist/ui/widgets/index.js +0 -23
- package/dist/ui/widgets/inquiry.js +0 -7
- package/dist/ui/widgets/key-value.js +0 -2
- package/dist/ui/widgets/progress-step.js +0 -2
- package/dist/ui/widgets/resource-card.js +0 -2
- package/dist/ui/widgets/status.js +0 -2
- package/dist/ui/widgets/todo-list.js +0 -2
- package/dist/version.js +0 -62
- /package/dist/{types.js → app/types.js} +0 -0
- /package/dist/{architecture/contracts.js → harness/types.js} +0 -0
package/dist/automations.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { DEFAULT_BASE_DIR, loadConfig, resolvePath } from "./config.js";
|
|
4
|
-
function getAutomationsPath() {
|
|
5
|
-
const cfg = loadConfig();
|
|
6
|
-
const baseDir = cfg.baseDir || DEFAULT_BASE_DIR;
|
|
7
|
-
const resolvedBaseDir = resolvePath(baseDir);
|
|
8
|
-
return path.join(resolvedBaseDir, "automations.json");
|
|
9
|
-
}
|
|
10
|
-
export async function listAutomations() {
|
|
11
|
-
const filePath = getAutomationsPath();
|
|
12
|
-
try {
|
|
13
|
-
const raw = await fs.readFile(filePath, "utf-8");
|
|
14
|
-
const parsed = JSON.parse(raw);
|
|
15
|
-
if (!Array.isArray(parsed))
|
|
16
|
-
return [];
|
|
17
|
-
return parsed
|
|
18
|
-
.filter((item) => item && typeof item === "object")
|
|
19
|
-
.map((item) => {
|
|
20
|
-
const targetType = item.targetType === "agent" ? "agent" : "orchestrator";
|
|
21
|
-
const agentName = typeof item.agentName === "string" && item.agentName.trim()
|
|
22
|
-
? item.agentName.trim()
|
|
23
|
-
: undefined;
|
|
24
|
-
return {
|
|
25
|
-
id: typeof item.id === "string" ? item.id : "",
|
|
26
|
-
name: typeof item.name === "string" ? item.name : "",
|
|
27
|
-
prompt: typeof item.prompt === "string" ? item.prompt : "",
|
|
28
|
-
cron: typeof item.cron === "string" ? item.cron : "",
|
|
29
|
-
targetType,
|
|
30
|
-
agentName: targetType === "agent" ? agentName : undefined,
|
|
31
|
-
enabled: Boolean(item.enabled),
|
|
32
|
-
createdAt: typeof item.createdAt === "string" ? item.createdAt : new Date().toISOString(),
|
|
33
|
-
updatedAt: typeof item.updatedAt === "string" ? item.updatedAt : new Date().toISOString(),
|
|
34
|
-
};
|
|
35
|
-
})
|
|
36
|
-
.filter((item) => {
|
|
37
|
-
if (!item.id || !item.name || !item.prompt || !item.cron)
|
|
38
|
-
return false;
|
|
39
|
-
if (item.targetType === "agent" && !item.agentName)
|
|
40
|
-
return false;
|
|
41
|
-
return true;
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
return [];
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
export async function saveAutomations(items) {
|
|
49
|
-
const filePath = getAutomationsPath();
|
|
50
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
51
|
-
await fs.writeFile(filePath, JSON.stringify(items, null, 2), "utf-8");
|
|
52
|
-
}
|
package/dist/cli.js
DELETED
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Command } from "commander";
|
|
3
|
-
import * as readline from "node:readline/promises";
|
|
4
|
-
import * as fs from "node:fs/promises";
|
|
5
|
-
import * as path from "node:path";
|
|
6
|
-
import { spawn } from "node:child_process";
|
|
7
|
-
import { saveConfig, resolvePath, DEFAULT_BASE_DIR } from "./config.js";
|
|
8
|
-
import { startServer } from "./server.js";
|
|
9
|
-
import { getPluginMetadata } from "./registry/plugin-loader.js";
|
|
10
|
-
import { checkGitHubRepo, checkNpmPackage, parsePluginInstallSource, parseAgentInstallSource, installPluginFromSource, installAgentFromSource, } from "./installers.js";
|
|
11
|
-
const program = new Command();
|
|
12
|
-
const REQUIRED_NODE_VERSION = "20.12.0";
|
|
13
|
-
function checkNodeVersion() {
|
|
14
|
-
const [major, minor, patch] = process.versions.node.split(".").map(Number);
|
|
15
|
-
const [reqMajor, reqMinor, reqPatch] = REQUIRED_NODE_VERSION.split(".").map(Number);
|
|
16
|
-
const isOld = major < reqMajor ||
|
|
17
|
-
(major === reqMajor && minor < reqMinor) ||
|
|
18
|
-
(major === reqMajor && minor === reqMinor && patch < reqPatch);
|
|
19
|
-
if (isOld) {
|
|
20
|
-
console.warn(`\n⚠️ WARNING: You are using Node.js ${process.version}.`);
|
|
21
|
-
console.warn(` OpenBot works best with Node.js >=${REQUIRED_NODE_VERSION}.`);
|
|
22
|
-
console.warn(` You may encounter "ERR_REQUIRE_ESM" or other compatibility issues on older versions.\n`);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
checkNodeVersion();
|
|
26
|
-
program
|
|
27
|
-
.name("openbot")
|
|
28
|
-
.description("OpenBot CLI - Secure and easy configuration")
|
|
29
|
-
.version("0.2.12");
|
|
30
|
-
async function installPlugin(source, id, quiet = false) {
|
|
31
|
-
try {
|
|
32
|
-
const parsed = parsePluginInstallSource(source);
|
|
33
|
-
const name = await installPluginFromSource(parsed, { quiet, id });
|
|
34
|
-
return name;
|
|
35
|
-
}
|
|
36
|
-
catch (err) {
|
|
37
|
-
if (!quiet)
|
|
38
|
-
console.error("\n❌ Plugin installation failed:", err instanceof Error ? err.message : String(err));
|
|
39
|
-
if (!quiet)
|
|
40
|
-
process.exit(1);
|
|
41
|
-
throw err;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
async function installAgent(source, id) {
|
|
45
|
-
try {
|
|
46
|
-
const parsed = parseAgentInstallSource(source);
|
|
47
|
-
const name = await installAgentFromSource(parsed, { id });
|
|
48
|
-
return name;
|
|
49
|
-
}
|
|
50
|
-
catch (err) {
|
|
51
|
-
console.error("\n❌ Agent installation failed:", err instanceof Error ? err.message : String(err));
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
function shellEscape(arg) {
|
|
56
|
-
// Keep simple tokens readable; quote only when needed so command
|
|
57
|
-
// tokenization remains stable when passed through concurrently.
|
|
58
|
-
if (/^[A-Za-z0-9_/:=+.-]+$/.test(arg))
|
|
59
|
-
return arg;
|
|
60
|
-
return `"${arg.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
61
|
-
}
|
|
62
|
-
program
|
|
63
|
-
.command("configure")
|
|
64
|
-
.description("Configure OpenBot model and settings")
|
|
65
|
-
.action(async () => {
|
|
66
|
-
const rl = readline.createInterface({
|
|
67
|
-
input: process.stdin,
|
|
68
|
-
output: process.stdout,
|
|
69
|
-
});
|
|
70
|
-
console.log("------------------------------------------");
|
|
71
|
-
console.log("🍎 OpenBot Configuration");
|
|
72
|
-
console.log("------------------------------------------");
|
|
73
|
-
const models = [
|
|
74
|
-
{ name: "GPT-5 Nano (OpenAI)", value: "openai/gpt-5-nano" },
|
|
75
|
-
{ name: "GPT-4o (OpenAI)", value: "openai/gpt-4o" },
|
|
76
|
-
{ name: "GPT-4o-mini (OpenAI)", value: "openai/gpt-4o-mini" },
|
|
77
|
-
{ name: "Claude Opus 4.5 (Anthropic)", value: "anthropic/claude-opus-4-5-20251101" },
|
|
78
|
-
{ name: "Claude Sonnet 4.5 (Anthropic)", value: "anthropic/claude-sonnet-4-5-20250929" },
|
|
79
|
-
{ name: "Claude 3.7 Sonnet (Anthropic)", value: "anthropic/claude-3-7-sonnet-20250219" },
|
|
80
|
-
{ name: "Claude 3.5 Sonnet (Anthropic)", value: "anthropic/claude-3-5-sonnet-20240620" },
|
|
81
|
-
];
|
|
82
|
-
console.log("Please choose a model:");
|
|
83
|
-
models.forEach((m, i) => console.log(`${i + 1}) ${m.name}`));
|
|
84
|
-
const choice = await rl.question(`\nSelection (1-${models.length}): `);
|
|
85
|
-
const selectedIndex = parseInt(choice) - 1;
|
|
86
|
-
if (isNaN(selectedIndex) || selectedIndex < 0 || selectedIndex >= models.length) {
|
|
87
|
-
console.error("❌ Invalid selection. Please run configure again.");
|
|
88
|
-
rl.close();
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
const selectedModel = models[selectedIndex].value;
|
|
92
|
-
const provider = selectedModel.startsWith("openai") ? "openai" : "anthropic";
|
|
93
|
-
saveConfig({
|
|
94
|
-
model: selectedModel,
|
|
95
|
-
});
|
|
96
|
-
console.log("\n✅ Configuration saved!");
|
|
97
|
-
console.log(`Selected model: ${selectedModel}`);
|
|
98
|
-
console.log("------------------------------------------");
|
|
99
|
-
if (provider === "openai") {
|
|
100
|
-
console.log("To start the server with your OpenAI key, use:");
|
|
101
|
-
console.log(`\n openbot server --openai-api-key YOUR_OPENAI_KEY\n`);
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
console.log("To start the server with your Anthropic key, use:");
|
|
105
|
-
console.log(`\n openbot server --anthropic-api-key YOUR_ANTHROPIC_KEY\n`);
|
|
106
|
-
}
|
|
107
|
-
console.log("Alternatively, you can set the environment variable:");
|
|
108
|
-
console.log(provider === "openai" ? " export OPENAI_API_KEY=your-key" : " export ANTHROPIC_API_KEY=your-key");
|
|
109
|
-
console.log("------------------------------------------");
|
|
110
|
-
console.log("\n🚀 TIP: Use 'openbot up' to start the server and web UI together.");
|
|
111
|
-
console.log("------------------------------------------\n");
|
|
112
|
-
rl.close();
|
|
113
|
-
});
|
|
114
|
-
program
|
|
115
|
-
.command("server")
|
|
116
|
-
.description("Start the OpenBot server")
|
|
117
|
-
.option("-p, --port <number>", "Port to listen on")
|
|
118
|
-
.option("--openai-api-key <key>", "OpenAI API Key")
|
|
119
|
-
.option("--anthropic-api-key <key>", "Anthropic API Key")
|
|
120
|
-
.action(async (options) => {
|
|
121
|
-
await startServer(options);
|
|
122
|
-
});
|
|
123
|
-
program
|
|
124
|
-
.command("up")
|
|
125
|
-
.description("Start OpenBot server and web dashboard together")
|
|
126
|
-
.option("-p, --port <number>", "Port to listen on")
|
|
127
|
-
.option("--openai-api-key <key>", "OpenAI API Key")
|
|
128
|
-
.option("--anthropic-api-key <key>", "Anthropic API Key")
|
|
129
|
-
.action(async (options) => {
|
|
130
|
-
const serverArgs = ["openbot", "server"];
|
|
131
|
-
if (options.port)
|
|
132
|
-
serverArgs.push("--port", String(options.port));
|
|
133
|
-
if (options.openaiApiKey)
|
|
134
|
-
serverArgs.push("--openai-api-key", options.openaiApiKey);
|
|
135
|
-
if (options.anthropicApiKey)
|
|
136
|
-
serverArgs.push("--anthropic-api-key", options.anthropicApiKey);
|
|
137
|
-
const serverCommand = serverArgs.map(shellEscape).join(" ");
|
|
138
|
-
await new Promise((resolve, reject) => {
|
|
139
|
-
const child = spawn("npx", [
|
|
140
|
-
"-y",
|
|
141
|
-
"concurrently",
|
|
142
|
-
"--kill-others",
|
|
143
|
-
"--names",
|
|
144
|
-
"SERVER,WEBUI",
|
|
145
|
-
"--prefix",
|
|
146
|
-
"{name}",
|
|
147
|
-
"--prefix-colors",
|
|
148
|
-
"blue.bold,green.bold",
|
|
149
|
-
serverCommand,
|
|
150
|
-
"openbot-web",
|
|
151
|
-
], { stdio: "inherit" });
|
|
152
|
-
child.on("error", reject);
|
|
153
|
-
child.on("exit", (code) => {
|
|
154
|
-
if (typeof code === "number")
|
|
155
|
-
process.exitCode = code;
|
|
156
|
-
resolve();
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
program
|
|
161
|
-
.command("add <name>")
|
|
162
|
-
.description("Add an agent or plugin by name (auto-resolves to GitHub/NPM)")
|
|
163
|
-
.action(async (name) => {
|
|
164
|
-
// 1. Try as Agent
|
|
165
|
-
const agentRepo = `meetopenbot/agent-${name}`;
|
|
166
|
-
if (checkGitHubRepo(agentRepo)) {
|
|
167
|
-
await installAgent(agentRepo, `agent-${name}`);
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
// 2. Try as Plugin
|
|
171
|
-
const baseDir = resolvePath(DEFAULT_BASE_DIR);
|
|
172
|
-
const agentPath = path.join(baseDir, "agents", name);
|
|
173
|
-
const pluginPath = path.join(baseDir, "plugins", name);
|
|
174
|
-
const agentExists = await fs.access(agentPath).then(() => true).catch(() => false);
|
|
175
|
-
const pluginExists = await fs.access(pluginPath).then(() => true).catch(() => false);
|
|
176
|
-
if (agentExists || pluginExists) {
|
|
177
|
-
console.log(`✅ Agent or Plugin "${name}" is already installed locally.`);
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
// Check GitHub Plugin
|
|
181
|
-
const pluginGhRepo = `meetopenbot/plugin-${name}`;
|
|
182
|
-
if (checkGitHubRepo(pluginGhRepo)) {
|
|
183
|
-
await installPlugin(pluginGhRepo, `plugin-${name}`);
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
// Check NPM Plugin
|
|
187
|
-
const pluginNpmPkg = `@melony/plugin-${name}`;
|
|
188
|
-
if (checkNpmPackage(pluginNpmPkg)) {
|
|
189
|
-
await installPlugin(pluginNpmPkg, `plugin-${name}`);
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
console.error(`❌ Could not find agent or plugin named "${name}" in official repositories.`);
|
|
193
|
-
process.exit(1);
|
|
194
|
-
});
|
|
195
|
-
const plugin = program.command("plugin").description("Manage OpenBot plugins");
|
|
196
|
-
plugin
|
|
197
|
-
.command("install <source>")
|
|
198
|
-
.description("Install a shared plugin from GitHub (user/repo) or a local path")
|
|
199
|
-
.action(async (source) => {
|
|
200
|
-
await installPlugin(source);
|
|
201
|
-
});
|
|
202
|
-
plugin
|
|
203
|
-
.command("list")
|
|
204
|
-
.description("List all installed shared plugins")
|
|
205
|
-
.action(async () => {
|
|
206
|
-
const baseDir = resolvePath(DEFAULT_BASE_DIR);
|
|
207
|
-
const pluginsDir = path.join(baseDir, "plugins");
|
|
208
|
-
try {
|
|
209
|
-
await fs.access(pluginsDir);
|
|
210
|
-
}
|
|
211
|
-
catch {
|
|
212
|
-
console.log("No shared plugins found.");
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
const entries = await fs.readdir(pluginsDir, { withFileTypes: true });
|
|
216
|
-
const plugins = [];
|
|
217
|
-
for (const entry of entries) {
|
|
218
|
-
if (!entry.isDirectory())
|
|
219
|
-
continue;
|
|
220
|
-
if (entry.name.startsWith(".") || entry.name.startsWith("_"))
|
|
221
|
-
continue;
|
|
222
|
-
const pluginDir = path.join(pluginsDir, entry.name);
|
|
223
|
-
const { name, version, description } = await getPluginMetadata(pluginDir);
|
|
224
|
-
plugins.push({ name, version, description });
|
|
225
|
-
}
|
|
226
|
-
if (plugins.length === 0) {
|
|
227
|
-
console.log("No shared plugins found.");
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
console.log("\n🔌 Installed Shared Plugins:");
|
|
231
|
-
console.log("------------------------------------------");
|
|
232
|
-
for (const p of plugins) {
|
|
233
|
-
console.log(`${p.name.padEnd(20)} (${p.version}) - ${p.description}`);
|
|
234
|
-
}
|
|
235
|
-
console.log("------------------------------------------\n");
|
|
236
|
-
});
|
|
237
|
-
const agent = program.command("agent").description("Manage OpenBot agents");
|
|
238
|
-
agent
|
|
239
|
-
.command("install <source>")
|
|
240
|
-
.description("Install a custom agent from GitHub (user/repo) or a local path")
|
|
241
|
-
.action(async (source) => {
|
|
242
|
-
await installAgent(source);
|
|
243
|
-
});
|
|
244
|
-
agent
|
|
245
|
-
.command("list")
|
|
246
|
-
.description("List all installed custom agents")
|
|
247
|
-
.action(async () => {
|
|
248
|
-
const baseDir = resolvePath(DEFAULT_BASE_DIR);
|
|
249
|
-
const agentsDir = path.join(baseDir, "agents");
|
|
250
|
-
try {
|
|
251
|
-
await fs.access(agentsDir);
|
|
252
|
-
}
|
|
253
|
-
catch {
|
|
254
|
-
console.log("No custom agents found.");
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
const entries = await fs.readdir(agentsDir, { withFileTypes: true });
|
|
258
|
-
const agents = [];
|
|
259
|
-
for (const entry of entries) {
|
|
260
|
-
if (!entry.isDirectory())
|
|
261
|
-
continue;
|
|
262
|
-
if (entry.name.startsWith(".") || entry.name.startsWith("_"))
|
|
263
|
-
continue;
|
|
264
|
-
const agentDir = path.join(agentsDir, entry.name);
|
|
265
|
-
const { name, version, description } = await getPluginMetadata(agentDir);
|
|
266
|
-
agents.push({ name, version, description });
|
|
267
|
-
}
|
|
268
|
-
if (agents.length === 0) {
|
|
269
|
-
console.log("No custom agents found.");
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
console.log("\n🤖 Installed Custom Agents:");
|
|
273
|
-
console.log("------------------------------------------");
|
|
274
|
-
for (const a of agents) {
|
|
275
|
-
console.log(`${a.name.padEnd(20)} (${a.version}) - ${a.description}`);
|
|
276
|
-
}
|
|
277
|
-
console.log("------------------------------------------\n");
|
|
278
|
-
});
|
|
279
|
-
program.parse();
|
package/dist/config.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
export const DEFAULT_BASE_DIR = "~/.openbot";
|
|
5
|
-
export const DEFAULT_PLUGINS_DIR = "plugins";
|
|
6
|
-
export const DEFAULT_AGENTS_DIR = "agents";
|
|
7
|
-
export function loadConfig() {
|
|
8
|
-
const configPath = path.join(os.homedir(), ".openbot", "config.json");
|
|
9
|
-
if (fs.existsSync(configPath)) {
|
|
10
|
-
try {
|
|
11
|
-
return JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
12
|
-
}
|
|
13
|
-
catch (error) {
|
|
14
|
-
console.error(`Warning: Failed to parse config at ${configPath}`, error);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return {};
|
|
18
|
-
}
|
|
19
|
-
export function saveConfig(config) {
|
|
20
|
-
const configDir = path.join(os.homedir(), ".openbot");
|
|
21
|
-
const configPath = path.join(configDir, "config.json");
|
|
22
|
-
if (!fs.existsSync(configDir)) {
|
|
23
|
-
fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
|
|
24
|
-
}
|
|
25
|
-
const currentConfig = loadConfig();
|
|
26
|
-
const newConfig = { ...currentConfig, ...config };
|
|
27
|
-
fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 2), { mode: 0o600 });
|
|
28
|
-
}
|
|
29
|
-
export function isConfigured() {
|
|
30
|
-
const configPath = path.join(os.homedir(), ".openbot", "config.json");
|
|
31
|
-
return fs.existsSync(configPath);
|
|
32
|
-
}
|
|
33
|
-
export function resolvePath(p) {
|
|
34
|
-
return p.startsWith("~/") ? path.join(os.homedir(), p.slice(2)) : path.resolve(p);
|
|
35
|
-
}
|
|
36
|
-
export const DEFAULT_AGENT_MD = `---
|
|
37
|
-
name: Agent
|
|
38
|
-
description: A specialized AI agent
|
|
39
|
-
plugins:
|
|
40
|
-
- shell
|
|
41
|
-
- file-system
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
# Agent Profile
|
|
45
|
-
|
|
46
|
-
You are a specialized AI agent within the OpenBot system.
|
|
47
|
-
Your role is defined by your configuration and the tools you have access to.
|
|
48
|
-
|
|
49
|
-
## Persona
|
|
50
|
-
- Helpful and precise
|
|
51
|
-
- Focused on my specific domain
|
|
52
|
-
- Professional in all interactions
|
|
53
|
-
`;
|
package/dist/core/agents.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { osAgent } from "../agents/os-agent.js";
|
|
2
|
-
import { agentCreatorAgent } from "../agents/agent-creator.js";
|
|
3
|
-
import { plannerAgent } from "../agents/planner-agent.js";
|
|
4
|
-
import { shellToolDefinitions } from "../plugins/shell/index.js";
|
|
5
|
-
import { fileSystemToolDefinitions } from "../plugins/file-system/index.js";
|
|
6
|
-
import { AgentRegistry, discoverYamlAgents, discoverTsAgents } from "../registry/index.js";
|
|
7
|
-
import path from "node:path";
|
|
8
|
-
export async function setupAgentRegistry(resolvedBaseDir, pluginRegistry, model, options) {
|
|
9
|
-
const agentRegistry = new AgentRegistry();
|
|
10
|
-
agentRegistry.register({
|
|
11
|
-
name: "os",
|
|
12
|
-
description: "Handles shell commands and file system operations",
|
|
13
|
-
capabilities: {
|
|
14
|
-
...Object.fromEntries(Object.entries(shellToolDefinitions).map(([k, v]) => [k, v.description])),
|
|
15
|
-
...Object.fromEntries(Object.entries(fileSystemToolDefinitions).map(([k, v]) => [k, v.description])),
|
|
16
|
-
},
|
|
17
|
-
plugin: osAgent({ model }),
|
|
18
|
-
});
|
|
19
|
-
agentRegistry.register({
|
|
20
|
-
name: "agent-creator",
|
|
21
|
-
description: "Helps the user create and configure new custom OpenBot agents via natural language.",
|
|
22
|
-
capabilities: {
|
|
23
|
-
...Object.fromEntries(Object.entries(fileSystemToolDefinitions).map(([k, v]) => [k, v.description])),
|
|
24
|
-
},
|
|
25
|
-
plugin: agentCreatorAgent({ model }),
|
|
26
|
-
});
|
|
27
|
-
agentRegistry.register({
|
|
28
|
-
name: "planner-agent",
|
|
29
|
-
description: "Creates concise execution plans from user intent for OpenBot to run.",
|
|
30
|
-
plugin: plannerAgent({ model }),
|
|
31
|
-
});
|
|
32
|
-
const agentsDir = path.join(resolvedBaseDir, "agents");
|
|
33
|
-
const [yamlAgents, tsAgents] = await Promise.all([
|
|
34
|
-
discoverYamlAgents(agentsDir, pluginRegistry, model, options),
|
|
35
|
-
discoverTsAgents(agentsDir, model, options),
|
|
36
|
-
]);
|
|
37
|
-
for (const agent of [...yamlAgents, ...tsAgents]) {
|
|
38
|
-
agentRegistry.register(agent);
|
|
39
|
-
}
|
|
40
|
-
return agentRegistry;
|
|
41
|
-
}
|
package/dist/core/delegation.js
DELETED
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
import { generateId } from "melony";
|
|
2
|
-
import { uiEvent } from "../ui/block.js";
|
|
3
|
-
import { widgets } from "../ui/widgets/index.js";
|
|
4
|
-
/**
|
|
5
|
-
* Simple helper to set a value in an object by a dot-separated path.
|
|
6
|
-
*/
|
|
7
|
-
function setByPath(obj, path, value) {
|
|
8
|
-
const parts = path.split(".");
|
|
9
|
-
let current = obj;
|
|
10
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
11
|
-
const part = parts[i];
|
|
12
|
-
if (!(part in current)) {
|
|
13
|
-
current[part] = {};
|
|
14
|
-
}
|
|
15
|
-
current = current[part];
|
|
16
|
-
}
|
|
17
|
-
current[parts[parts.length - 1]] = value;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Helper to emit a UI snapshot for a widget if applicable.
|
|
21
|
-
*/
|
|
22
|
-
function* maybeEmitWidget(key, value) {
|
|
23
|
-
if (!value || typeof value !== "object")
|
|
24
|
-
return;
|
|
25
|
-
let widgetName = value.widget;
|
|
26
|
-
let data = value;
|
|
27
|
-
// 1. Check for nested .todos array (common pattern for planner/task agents)
|
|
28
|
-
if (!widgetName && Array.isArray(value.todos)) {
|
|
29
|
-
widgetName = "todoList";
|
|
30
|
-
data = value.todos;
|
|
31
|
-
}
|
|
32
|
-
// 2. Fallback for direct arrays if key matches known patterns
|
|
33
|
-
if (!widgetName && Array.isArray(value) && ["todos", "todoList", "project_plan"].includes(key)) {
|
|
34
|
-
widgetName = "todoList";
|
|
35
|
-
data = value;
|
|
36
|
-
}
|
|
37
|
-
// If we found a valid widget and data is an array, emit the UI event
|
|
38
|
-
if (widgetName && widgets[widgetName] && Array.isArray(data)) {
|
|
39
|
-
const isTodo = widgetName === "todoList";
|
|
40
|
-
yield uiEvent(widgets[widgetName](data, {
|
|
41
|
-
placement: isTodo ? "attention" : "sidebar",
|
|
42
|
-
id: isTodo ? `attention-${key}` : `sidebar-${key}`,
|
|
43
|
-
meta: { title: key === "project_plan" ? "Project Plan" : (key.charAt(0).toUpperCase() + key.slice(1).replace(/_/g, ' ')) }
|
|
44
|
-
}));
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
export function setupDelegation(builder, agentRuntimes) {
|
|
48
|
-
builder.on("action:updateSessionState", async function* (event, context) {
|
|
49
|
-
const { path, value, toolCallId } = event.data;
|
|
50
|
-
const state = context.state;
|
|
51
|
-
try {
|
|
52
|
-
setByPath(state, path, value);
|
|
53
|
-
const topLevelKey = path.split(".")[0];
|
|
54
|
-
yield* maybeEmitWidget(topLevelKey, state[topLevelKey]);
|
|
55
|
-
yield {
|
|
56
|
-
type: "action:result",
|
|
57
|
-
data: {
|
|
58
|
-
action: "updateSessionState",
|
|
59
|
-
result: `Successfully updated state at path "${path}".`,
|
|
60
|
-
toolCallId,
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
catch (error) {
|
|
65
|
-
yield {
|
|
66
|
-
type: "action:result",
|
|
67
|
-
data: {
|
|
68
|
-
action: "updateSessionState",
|
|
69
|
-
result: `Error updating state: ${error.message}`,
|
|
70
|
-
toolCallId,
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
builder.on("action:delegateTask", async function* (event, context) {
|
|
76
|
-
const { agentId, toolCallId, task, stateKey, attachments } = event.data;
|
|
77
|
-
const agentRuntime = agentRuntimes.get(agentId);
|
|
78
|
-
// If the agent is not found, return an error
|
|
79
|
-
if (!agentRuntime) {
|
|
80
|
-
yield {
|
|
81
|
-
type: "action:result",
|
|
82
|
-
data: {
|
|
83
|
-
action: "delegateTask",
|
|
84
|
-
result: `Error: Agent "${agentId}" not found.`,
|
|
85
|
-
toolCallId,
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
const delegationId = `del_${generateId()}`;
|
|
91
|
-
// Signal delegation start for UI
|
|
92
|
-
yield {
|
|
93
|
-
type: "delegation:start",
|
|
94
|
-
meta: { delegationId, agentName: agentId },
|
|
95
|
-
data: { agent: agentId, task },
|
|
96
|
-
};
|
|
97
|
-
// Initialize agent isolated state if not present
|
|
98
|
-
const state = context.state;
|
|
99
|
-
if (!state.agentStates)
|
|
100
|
-
state.agentStates = {};
|
|
101
|
-
if (!state.agentStates[agentId])
|
|
102
|
-
state.agentStates[agentId] = {};
|
|
103
|
-
const agentState = state.agentStates[agentId];
|
|
104
|
-
const agentIterator = agentRuntime.run({
|
|
105
|
-
type: "agent:input",
|
|
106
|
-
data: { content: task, attachments },
|
|
107
|
-
}, {
|
|
108
|
-
runId: delegationId,
|
|
109
|
-
state: agentState,
|
|
110
|
-
});
|
|
111
|
-
let lastAgentOutput = "";
|
|
112
|
-
let pendingApprovalId;
|
|
113
|
-
try {
|
|
114
|
-
for await (const agentEvent of agentIterator) {
|
|
115
|
-
// Dedicated suspend event from approval plugin.
|
|
116
|
-
// Emit included UI event (if any), then park this delegation until approve/deny.
|
|
117
|
-
if (agentEvent.type === "suspend") {
|
|
118
|
-
const suspendData = agentEvent.data ?? {};
|
|
119
|
-
const suspendId = typeof suspendData.id === "string" ? suspendData.id : undefined;
|
|
120
|
-
const suspendUiEvent = suspendData.event;
|
|
121
|
-
if (suspendUiEvent && typeof suspendUiEvent === "object" && typeof suspendUiEvent.type === "string") {
|
|
122
|
-
yield {
|
|
123
|
-
...suspendUiEvent,
|
|
124
|
-
meta: { ...suspendUiEvent.meta, delegationId, agentName: agentId },
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
if (suspendId) {
|
|
128
|
-
pendingApprovalId = suspendId;
|
|
129
|
-
}
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
// Forward agent events to the main runtime so the user sees progress.
|
|
133
|
-
// We SKIP forwarding 'agent:input' because it triggers the manager's LLM again.
|
|
134
|
-
// Instead, we yield it as 'agent:sub-input' for logging/monitoring.
|
|
135
|
-
if (agentEvent.type === "agent:input") {
|
|
136
|
-
yield {
|
|
137
|
-
...agentEvent,
|
|
138
|
-
type: "agent:sub-input",
|
|
139
|
-
meta: { ...agentEvent.meta, delegationId, agentName: agentId },
|
|
140
|
-
};
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
// We wrap sub-agent actions to avoid triggering manager handlers if they share names.
|
|
144
|
-
if (agentEvent.type.startsWith("action:") && agentEvent.type !== "action:result") {
|
|
145
|
-
yield {
|
|
146
|
-
...agentEvent,
|
|
147
|
-
type: "agent:sub-action",
|
|
148
|
-
meta: { ...agentEvent.meta, delegationId, agentName: agentId },
|
|
149
|
-
data: { ...agentEvent.data, originalType: agentEvent.type },
|
|
150
|
-
};
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
// Wrap action results to avoid confusion with manager action results.
|
|
154
|
-
if (agentEvent.type === "action:result") {
|
|
155
|
-
yield {
|
|
156
|
-
...agentEvent,
|
|
157
|
-
type: "agent:sub-action-result",
|
|
158
|
-
meta: { ...agentEvent.meta, delegationId, agentName: agentId },
|
|
159
|
-
};
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
// Wrap usage updates to avoid confusion with manager usage.
|
|
163
|
-
if (agentEvent.type === "usage:update") {
|
|
164
|
-
yield {
|
|
165
|
-
...agentEvent,
|
|
166
|
-
type: "agent:sub-usage",
|
|
167
|
-
meta: { ...agentEvent.meta, delegationId, agentName: agentId },
|
|
168
|
-
};
|
|
169
|
-
continue;
|
|
170
|
-
}
|
|
171
|
-
// Pass through other events but tag them with delegationId and agentName in meta
|
|
172
|
-
yield {
|
|
173
|
-
...agentEvent,
|
|
174
|
-
meta: { ...agentEvent.meta, delegationId, agentName: agentId },
|
|
175
|
-
};
|
|
176
|
-
// accumulate agent output
|
|
177
|
-
if (agentEvent.type === "agent:output") {
|
|
178
|
-
const agentOutput = agentEvent.data;
|
|
179
|
-
// DETERMINISTIC SYNC: If agent returns structured data and stateKey is provided
|
|
180
|
-
const value = agentOutput?.result ?? agentOutput?.content ?? agentOutput?.message ?? agentOutput;
|
|
181
|
-
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
182
|
-
if (stateKey) {
|
|
183
|
-
context.state[stateKey] = value;
|
|
184
|
-
yield* maybeEmitWidget(stateKey, value);
|
|
185
|
-
}
|
|
186
|
-
if (lastAgentOutput)
|
|
187
|
-
lastAgentOutput += "\n\n";
|
|
188
|
-
lastAgentOutput += JSON.stringify(value, null, 2);
|
|
189
|
-
}
|
|
190
|
-
else if (typeof value === "string") {
|
|
191
|
-
if (lastAgentOutput)
|
|
192
|
-
lastAgentOutput += "\n\n";
|
|
193
|
-
lastAgentOutput += value;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
catch (error) {
|
|
199
|
-
console.error(`[delegation] Error running agent "${agentId}":`, error);
|
|
200
|
-
lastAgentOutput = `Error executing task: ${error.message}`;
|
|
201
|
-
}
|
|
202
|
-
// Option A behavior: if sub-agent suspended on approval,
|
|
203
|
-
// keep the manager tool call pending until approve/deny follow-up resolves it.
|
|
204
|
-
if (pendingApprovalId) {
|
|
205
|
-
state.pendingAgentTasks ?? (state.pendingAgentTasks = {});
|
|
206
|
-
state.pendingAgentTasks[pendingApprovalId] = {
|
|
207
|
-
toolCallId,
|
|
208
|
-
agentName: agentId,
|
|
209
|
-
delegationId,
|
|
210
|
-
stateKey: typeof stateKey === "string" ? stateKey : undefined,
|
|
211
|
-
};
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
// Signal delegation end for UI
|
|
215
|
-
yield {
|
|
216
|
-
type: "delegation:end",
|
|
217
|
-
meta: { delegationId, agentName: agentId },
|
|
218
|
-
data: { agent: agentId, result: lastAgentOutput || "Task completed." },
|
|
219
|
-
};
|
|
220
|
-
// Feedback the result back to the manager
|
|
221
|
-
yield {
|
|
222
|
-
type: "action:result",
|
|
223
|
-
data: {
|
|
224
|
-
action: "delegateTask",
|
|
225
|
-
result: lastAgentOutput || "Task completed with no output.",
|
|
226
|
-
toolCallId,
|
|
227
|
-
},
|
|
228
|
-
};
|
|
229
|
-
});
|
|
230
|
-
}
|