openbot 0.1.2 ā 0.1.4
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/dist/agent.js +12 -3
- package/dist/cli.js +60 -0
- package/dist/config.js +11 -1
- package/dist/server.js +88 -73
- package/package.json +7 -4
package/dist/agent.js
CHANGED
|
@@ -6,16 +6,22 @@ import { fileSystemPlugin, fileSystemToolDefinitions } from "@melony/plugin-file
|
|
|
6
6
|
import { metaAgentPlugin, metaAgentToolDefinitions, buildSystemPrompt } from "@melony/plugin-meta-agent";
|
|
7
7
|
import { aiSDKPlugin } from "@melony/plugin-ai-sdk";
|
|
8
8
|
import { openai } from "@ai-sdk/openai";
|
|
9
|
+
import { anthropic } from "@ai-sdk/anthropic";
|
|
9
10
|
import { initHandler } from "./handlers/init.js";
|
|
10
11
|
import { loadConfig, resolvePath, DEFAULT_BASE_DIR } from "./config.js";
|
|
11
12
|
/**
|
|
12
13
|
* Create the OpenBot meta-agent
|
|
13
14
|
* This bot has self-modification capabilities, skill management, and persistent identity
|
|
14
15
|
*/
|
|
15
|
-
export async function createOpenBot() {
|
|
16
|
+
export async function createOpenBot(options) {
|
|
16
17
|
const config = loadConfig();
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
const openaiKey = options?.openaiApiKey || config.openaiApiKey || process.env.OPENAI_API_KEY;
|
|
19
|
+
const anthropicKey = options?.anthropicApiKey || config.anthropicApiKey || process.env.ANTHROPIC_API_KEY;
|
|
20
|
+
if (openaiKey) {
|
|
21
|
+
process.env.OPENAI_API_KEY = openaiKey;
|
|
22
|
+
}
|
|
23
|
+
if (anthropicKey) {
|
|
24
|
+
process.env.ANTHROPIC_API_KEY = anthropicKey;
|
|
19
25
|
}
|
|
20
26
|
const baseDir = config.baseDir || DEFAULT_BASE_DIR;
|
|
21
27
|
const resolvedBaseDir = resolvePath(baseDir);
|
|
@@ -30,6 +36,9 @@ export async function createOpenBot() {
|
|
|
30
36
|
if (provider === "openai") {
|
|
31
37
|
model = openai(modelId);
|
|
32
38
|
}
|
|
39
|
+
else if (provider === "anthropic") {
|
|
40
|
+
model = anthropic(modelId);
|
|
41
|
+
}
|
|
33
42
|
}
|
|
34
43
|
// Build dynamic system prompt from identity files and skills
|
|
35
44
|
const systemPrompt = await buildSystemPrompt(resolvedBaseDir);
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import * as readline from "node:readline/promises";
|
|
4
|
+
import { saveConfig } from "./config.js";
|
|
5
|
+
const program = new Command();
|
|
6
|
+
program
|
|
7
|
+
.name("openbot")
|
|
8
|
+
.description("OpenBot CLI - Secure and easy configuration")
|
|
9
|
+
.version("0.1.0");
|
|
10
|
+
program
|
|
11
|
+
.command("configure")
|
|
12
|
+
.description("Configure OpenBot model and settings")
|
|
13
|
+
.action(async () => {
|
|
14
|
+
const rl = readline.createInterface({
|
|
15
|
+
input: process.stdin,
|
|
16
|
+
output: process.stdout,
|
|
17
|
+
});
|
|
18
|
+
console.log("------------------------------------------");
|
|
19
|
+
console.log("š OpenBot Configuration");
|
|
20
|
+
console.log("------------------------------------------");
|
|
21
|
+
const models = [
|
|
22
|
+
{ name: "GPT-5 Nano (OpenAI)", value: "openai:gpt-5-nano" },
|
|
23
|
+
{ name: "GPT-4o (OpenAI)", value: "openai:gpt-4o" },
|
|
24
|
+
{ name: "GPT-4o-mini (OpenAI)", value: "openai:gpt-4o-mini" },
|
|
25
|
+
{ name: "Claude Opus 4.5 (Anthropic)", value: "anthropic:claude-opus-4-5-20251101" },
|
|
26
|
+
{ name: "Claude Sonnet 4.5 (Anthropic)", value: "anthropic:claude-sonnet-4-5-20250929" },
|
|
27
|
+
{ name: "Claude 3.7 Sonnet (Anthropic)", value: "anthropic:claude-3-7-sonnet-20250219" },
|
|
28
|
+
{ name: "Claude 3.5 Sonnet (Anthropic)", value: "anthropic:claude-3-5-sonnet-20240620" },
|
|
29
|
+
];
|
|
30
|
+
console.log("Please choose a model:");
|
|
31
|
+
models.forEach((m, i) => console.log(`${i + 1}) ${m.name}`));
|
|
32
|
+
const choice = await rl.question(`\nSelection (1-${models.length}): `);
|
|
33
|
+
const selectedIndex = parseInt(choice) - 1;
|
|
34
|
+
if (isNaN(selectedIndex) || selectedIndex < 0 || selectedIndex >= models.length) {
|
|
35
|
+
console.error("ā Invalid selection. Please run configure again.");
|
|
36
|
+
rl.close();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const selectedModel = models[selectedIndex].value;
|
|
40
|
+
const provider = selectedModel.startsWith("openai") ? "openai" : "anthropic";
|
|
41
|
+
saveConfig({
|
|
42
|
+
model: selectedModel,
|
|
43
|
+
});
|
|
44
|
+
console.log("\nā
Configuration saved!");
|
|
45
|
+
console.log(`Selected model: ${selectedModel}`);
|
|
46
|
+
console.log("------------------------------------------");
|
|
47
|
+
if (provider === "openai") {
|
|
48
|
+
console.log("To start the server with your OpenAI key, use:");
|
|
49
|
+
console.log(`\n openbot-server --openai-api-key YOUR_OPENAI_KEY\n`);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.log("To start the server with your Anthropic key, use:");
|
|
53
|
+
console.log(`\n openbot-server --anthropic-api-key YOUR_ANTHROPIC_KEY\n`);
|
|
54
|
+
}
|
|
55
|
+
console.log("Alternatively, you can set the environment variable:");
|
|
56
|
+
console.log(provider === "openai" ? " export OPENAI_API_KEY=your-key" : " export ANTHROPIC_API_KEY=your-key");
|
|
57
|
+
console.log("------------------------------------------");
|
|
58
|
+
rl.close();
|
|
59
|
+
});
|
|
60
|
+
program.parse();
|
package/dist/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import os from "node:os";
|
|
4
|
-
export const DEFAULT_BASE_DIR = "~/
|
|
4
|
+
export const DEFAULT_BASE_DIR = "~/openbot";
|
|
5
5
|
export function loadConfig() {
|
|
6
6
|
const configPath = path.join(os.homedir(), ".openbot", "config.json");
|
|
7
7
|
if (fs.existsSync(configPath)) {
|
|
@@ -14,6 +14,16 @@ export function loadConfig() {
|
|
|
14
14
|
}
|
|
15
15
|
return {};
|
|
16
16
|
}
|
|
17
|
+
export function saveConfig(config) {
|
|
18
|
+
const configDir = path.join(os.homedir(), ".openbot");
|
|
19
|
+
const configPath = path.join(configDir, "config.json");
|
|
20
|
+
if (!fs.existsSync(configDir)) {
|
|
21
|
+
fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
|
|
22
|
+
}
|
|
23
|
+
const currentConfig = loadConfig();
|
|
24
|
+
const newConfig = { ...currentConfig, ...config };
|
|
25
|
+
fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 2), { mode: 0o600 });
|
|
26
|
+
}
|
|
17
27
|
export function resolvePath(p) {
|
|
18
28
|
return p.startsWith("~/") ? path.join(os.homedir(), p.slice(2)) : path.resolve(p);
|
|
19
29
|
}
|
package/dist/server.js
CHANGED
|
@@ -2,89 +2,104 @@
|
|
|
2
2
|
import "dotenv/config";
|
|
3
3
|
import express from "express";
|
|
4
4
|
import cors from "cors";
|
|
5
|
-
import {
|
|
5
|
+
import { Command } from "commander";
|
|
6
6
|
import { createOpenBot } from "./agent.js";
|
|
7
7
|
import { loadConfig } from "./config.js";
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
app.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
});
|
|
29
|
-
res.json(await response.json());
|
|
30
|
-
});
|
|
31
|
-
app.post("/api/chat", async (req, res) => {
|
|
32
|
-
const body = req.body;
|
|
33
|
-
if (!body.event || typeof body.event.type !== "string") {
|
|
34
|
-
return res.status(400).json({
|
|
35
|
-
error: "The request body must contain an `event` with a string `type`.",
|
|
8
|
+
const program = new Command();
|
|
9
|
+
program
|
|
10
|
+
.name("openbot-server")
|
|
11
|
+
.description("OpenBot server")
|
|
12
|
+
.option("-p, --port <number>", "Port to listen on")
|
|
13
|
+
.option("--openai-api-key <key>", "OpenAI API Key")
|
|
14
|
+
.option("--anthropic-api-key <key>", "Anthropic API Key")
|
|
15
|
+
.action(async (options) => {
|
|
16
|
+
const config = loadConfig();
|
|
17
|
+
const PORT = Number(options.port ?? config.port ?? process.env.PORT ?? 4001);
|
|
18
|
+
const app = express();
|
|
19
|
+
// In-memory state store (use a real database for production)
|
|
20
|
+
const stateStore = new Map();
|
|
21
|
+
app.use(cors());
|
|
22
|
+
app.use(express.json());
|
|
23
|
+
app.get("/", async (req, res) => {
|
|
24
|
+
res.json({
|
|
25
|
+
message: "Melony Express demo server",
|
|
26
|
+
chatEndpoint: "/api/chat",
|
|
27
|
+
stream: "Server-Sent Events (SSE)",
|
|
36
28
|
});
|
|
37
|
-
}
|
|
38
|
-
res.set({
|
|
39
|
-
"Content-Type": "text/event-stream",
|
|
40
|
-
"Cache-Control": "no-cache, no-transform",
|
|
41
|
-
Connection: "keep-alive",
|
|
42
29
|
});
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
30
|
+
app.get("/api/init", async (req, res) => {
|
|
31
|
+
const platform = req.query.platform || "web";
|
|
32
|
+
const openBot = await createOpenBot({
|
|
33
|
+
openaiApiKey: options.openaiApiKey,
|
|
34
|
+
anthropicApiKey: options.anthropicApiKey,
|
|
35
|
+
});
|
|
36
|
+
const response = await openBot.jsonResponse({
|
|
37
|
+
type: "init",
|
|
38
|
+
data: { platform }
|
|
39
|
+
});
|
|
40
|
+
res.json(await response.json());
|
|
51
41
|
});
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
42
|
+
app.post("/api/chat", async (req, res) => {
|
|
43
|
+
const body = req.body;
|
|
44
|
+
if (!body.event || typeof body.event.type !== "string") {
|
|
45
|
+
return res.status(400).json({
|
|
46
|
+
error: "The request body must contain an `event` with a string `type`.",
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
res.set({
|
|
50
|
+
"Content-Type": "text/event-stream",
|
|
51
|
+
"Cache-Control": "no-cache, no-transform",
|
|
52
|
+
Connection: "keep-alive",
|
|
53
|
+
});
|
|
54
|
+
res.flushHeaders?.();
|
|
55
|
+
const openBot = await createOpenBot({
|
|
56
|
+
openaiApiKey: options.openaiApiKey,
|
|
57
|
+
anthropicApiKey: options.anthropicApiKey,
|
|
58
|
+
});
|
|
59
|
+
const runtime = openBot.build();
|
|
60
|
+
const runId = body.runId ?? "default";
|
|
61
|
+
const state = stateStore.get(runId) ?? {};
|
|
62
|
+
const iterator = runtime.run(body.event, {
|
|
63
|
+
runId,
|
|
64
|
+
state,
|
|
65
|
+
});
|
|
66
|
+
const stopStreaming = () => {
|
|
67
|
+
iterator.return?.({ done: true, value: undefined });
|
|
68
|
+
};
|
|
69
|
+
res.on("close", stopStreaming);
|
|
70
|
+
try {
|
|
71
|
+
for await (const chunk of iterator) {
|
|
72
|
+
if (res.writableEnded) {
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
res.write(`data: ${JSON.stringify(chunk)}\n\n`);
|
|
60
76
|
}
|
|
61
|
-
|
|
77
|
+
// After the run finishes, the state might have been updated by plugins
|
|
78
|
+
stateStore.set(runId, state);
|
|
62
79
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
message: error instanceof Error ? error.message : String(error),
|
|
71
|
-
})}\n\n`);
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error("Melony stream error:", error);
|
|
82
|
+
if (!res.writableEnded) {
|
|
83
|
+
res.write(`event: error\ndata: ${JSON.stringify({
|
|
84
|
+
message: error instanceof Error ? error.message : String(error),
|
|
85
|
+
})}\n\n`);
|
|
86
|
+
}
|
|
72
87
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
88
|
+
finally {
|
|
89
|
+
res.off("close", stopStreaming);
|
|
90
|
+
if (!res.writableEnded) {
|
|
91
|
+
res.write("event: done\ndata: {}\n\n");
|
|
92
|
+
res.end();
|
|
93
|
+
}
|
|
79
94
|
}
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
const currentModule = fileURLToPath(import.meta.url);
|
|
83
|
-
const isMain = process.argv[1] === currentModule;
|
|
84
|
-
if (isMain) {
|
|
95
|
+
});
|
|
85
96
|
app.listen(PORT, () => {
|
|
86
97
|
console.log(`OpenBot server listening at http://localhost:${PORT}`);
|
|
87
98
|
console.log(` - Chat endpoint: POST /api/chat`);
|
|
99
|
+
if (options.openaiApiKey)
|
|
100
|
+
console.log(" - Using OpenAI API Key from CLI");
|
|
101
|
+
if (options.anthropicApiKey)
|
|
102
|
+
console.log(" - Using Anthropic API Key from CLI");
|
|
88
103
|
});
|
|
89
|
-
}
|
|
90
|
-
|
|
104
|
+
});
|
|
105
|
+
program.parse();
|
package/package.json
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openbot",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
+
"openbot": "./dist/cli.js",
|
|
7
8
|
"openbot-server": "./dist/server.js"
|
|
8
9
|
},
|
|
9
10
|
"dependencies": {
|
|
11
|
+
"@ai-sdk/anthropic": "^3.0.30",
|
|
10
12
|
"@ai-sdk/openai": "^3.0.13",
|
|
11
13
|
"@types/cors": "^2.8.19",
|
|
14
|
+
"commander": "^14.0.2",
|
|
12
15
|
"cors": "^2.8.6",
|
|
13
16
|
"dotenv": "^16.4.5",
|
|
14
17
|
"express": "^4.19.2",
|
|
15
18
|
"zod": "^4.3.5",
|
|
19
|
+
"@melony/plugin-ai-sdk": "0.1.0",
|
|
16
20
|
"@melony/plugin-file-system": "0.1.0",
|
|
17
21
|
"@melony/plugin-browser": "0.1.0",
|
|
18
|
-
"@melony/plugin-
|
|
22
|
+
"@melony/plugin-meta-agent": "0.1.0",
|
|
19
23
|
"@melony/plugin-shell": "0.1.0",
|
|
20
|
-
"melony": "0.2.
|
|
21
|
-
"@melony/plugin-meta-agent": "0.1.0"
|
|
24
|
+
"melony": "0.2.5"
|
|
22
25
|
},
|
|
23
26
|
"devDependencies": {
|
|
24
27
|
"@types/express": "^4.17.21",
|