openbot 0.1.27 → 0.1.29
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/agents/os-agent.js +9 -1
- package/dist/cli.js +1 -1
- package/dist/open-bot.js +91 -14
- package/dist/plugins/approval/index.js +99 -0
- package/dist/plugins/llm/index.js +2 -1
- package/dist/registry/index.js +1 -0
- package/dist/registry/plugin-loader.js +1 -1
- package/dist/registry/ts-agent-loader.js +82 -0
- package/dist/ui/settings.js +2 -2
- package/package.json +2 -2
package/dist/agents/os-agent.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { llmPlugin } from "../plugins/llm/index.js";
|
|
2
2
|
import { shellPlugin, shellToolDefinitions } from "../plugins/shell/index.js";
|
|
3
3
|
import { fileSystemPlugin, fileSystemToolDefinitions } from "../plugins/file-system/index.js";
|
|
4
|
+
import approvalPlugin from "../plugins/approval/index.js";
|
|
4
5
|
const DEFAULT_SYSTEM_PROMPT = `You are an OS Agent with access to the shell and file system.
|
|
5
6
|
Your job is to help the user with file operations and command execution.
|
|
6
7
|
You can read, write, list, and delete files, as well as execute shell commands.
|
|
@@ -11,12 +12,19 @@ export const osAgent = (options) => (builder) => {
|
|
|
11
12
|
builder
|
|
12
13
|
.use(shellPlugin({ cwd }))
|
|
13
14
|
.use(fileSystemPlugin({ baseDir: "/" }))
|
|
15
|
+
.use(approvalPlugin({
|
|
16
|
+
rules: [
|
|
17
|
+
{ action: "action:executeCommand", message: "The agent wants to execute a terminal command. Please review carefully." },
|
|
18
|
+
{ action: "action:writeFile", message: "The agent wants to write to a file." },
|
|
19
|
+
{ action: "action:deleteFile", message: "The agent wants to delete a file." },
|
|
20
|
+
],
|
|
21
|
+
}))
|
|
14
22
|
.use(llmPlugin({
|
|
15
23
|
model,
|
|
16
24
|
system: systemPrompt,
|
|
17
25
|
toolDefinitions: {
|
|
18
26
|
...shellToolDefinitions,
|
|
19
|
-
...fileSystemToolDefinitions
|
|
27
|
+
...fileSystemToolDefinitions
|
|
20
28
|
},
|
|
21
29
|
promptInputType: "agent:os:input",
|
|
22
30
|
actionResultInputType: "agent:os:result",
|
package/dist/cli.js
CHANGED
package/dist/open-bot.js
CHANGED
|
@@ -14,8 +14,9 @@ import { z } from "zod";
|
|
|
14
14
|
// Plugin imports for the registry
|
|
15
15
|
import { shellPlugin, shellToolDefinitions } from "./plugins/shell/index.js";
|
|
16
16
|
import { fileSystemPlugin, fileSystemToolDefinitions } from "./plugins/file-system/index.js";
|
|
17
|
+
import { approvalPlugin } from "./plugins/approval/index.js";
|
|
17
18
|
// Registry
|
|
18
|
-
import { PluginRegistry, AgentRegistry, discoverYamlAgents, loadPluginsFromDir } from "./registry/index.js";
|
|
19
|
+
import { PluginRegistry, AgentRegistry, discoverYamlAgents, discoverTsAgents, loadPluginsFromDir } from "./registry/index.js";
|
|
19
20
|
/**
|
|
20
21
|
* Create the OpenBot manager agent.
|
|
21
22
|
*
|
|
@@ -45,6 +46,12 @@ export async function createOpenBot(options) {
|
|
|
45
46
|
toolDefinitions: fileSystemToolDefinitions,
|
|
46
47
|
factory: () => fileSystemPlugin({ baseDir: "/" }),
|
|
47
48
|
});
|
|
49
|
+
pluginRegistry.register({
|
|
50
|
+
name: "approval",
|
|
51
|
+
description: "Require user approval for specific actions",
|
|
52
|
+
toolDefinitions: {},
|
|
53
|
+
factory: (options) => approvalPlugin(options),
|
|
54
|
+
});
|
|
48
55
|
// ─── Shared Plugins ──────────────────────────────────────────────
|
|
49
56
|
// Load community/user plugins from ~/.openbot/plugins/
|
|
50
57
|
const sharedPlugins = await loadPluginsFromDir(path.join(resolvedBaseDir, "plugins"));
|
|
@@ -74,8 +81,12 @@ export async function createOpenBot(options) {
|
|
|
74
81
|
subscribe: ["manager:completion"],
|
|
75
82
|
});
|
|
76
83
|
// Discover community / user agents from ~/.openbot/agents/
|
|
77
|
-
const
|
|
78
|
-
|
|
84
|
+
const agentsDir = path.join(resolvedBaseDir, "agents");
|
|
85
|
+
const [yamlAgents, tsAgents] = await Promise.all([
|
|
86
|
+
discoverYamlAgents(agentsDir, pluginRegistry, model, options),
|
|
87
|
+
discoverTsAgents(agentsDir, model, options),
|
|
88
|
+
]);
|
|
89
|
+
for (const agent of [...yamlAgents, ...tsAgents]) {
|
|
79
90
|
agentRegistry.register(agent);
|
|
80
91
|
}
|
|
81
92
|
// ─── Compose the Melony App ──────────────────────────────────────
|
|
@@ -124,6 +135,61 @@ export async function createOpenBot(options) {
|
|
|
124
135
|
return `- **${a.name}**: ${a.description}${tools ? `\n Capabilities:\n${tools}` : ""}`;
|
|
125
136
|
})
|
|
126
137
|
.join("\n\n");
|
|
138
|
+
// 2.5 Command Prefix Router
|
|
139
|
+
// Allows bypassing the manager using "/agent task" (e.g. "/os list files")
|
|
140
|
+
app.on("user:text", async function* (event, { state }) {
|
|
141
|
+
const content = event.data.content.trim();
|
|
142
|
+
if (content.startsWith("/")) {
|
|
143
|
+
const firstSpace = content.indexOf(" ");
|
|
144
|
+
const prefix = firstSpace === -1 ? content.slice(1) : content.slice(1, firstSpace);
|
|
145
|
+
const task = firstSpace === -1 ? "" : content.slice(firstSpace + 1).trim();
|
|
146
|
+
if (agentNames.includes(prefix)) {
|
|
147
|
+
// Direct route to specialized agent
|
|
148
|
+
state.lastDirectAgent = prefix;
|
|
149
|
+
yield {
|
|
150
|
+
type: `agent:${prefix}:input`,
|
|
151
|
+
data: { content: task },
|
|
152
|
+
};
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Default: send to manager for reasoning/delegation
|
|
157
|
+
state.lastDirectAgent = undefined;
|
|
158
|
+
yield { type: "manager:input", data: event.data };
|
|
159
|
+
});
|
|
160
|
+
// 2.6 Global Tool Result Dispatcher
|
|
161
|
+
// Routes tool results to the appropriate agent's internal loop.
|
|
162
|
+
// taskResult means that the loop is complete.
|
|
163
|
+
app.on("action:taskResult", async function* (event, { state, suspend }) {
|
|
164
|
+
const s = state;
|
|
165
|
+
// 1. Direct route (prefix command)
|
|
166
|
+
if (s.lastDirectAgent) {
|
|
167
|
+
// suspend not to go to infinite loop
|
|
168
|
+
// suspend({
|
|
169
|
+
// type: `agent:${s.lastDirectAgent}:result`,
|
|
170
|
+
// data: event.data,
|
|
171
|
+
// } as ChatEvent);
|
|
172
|
+
yield {
|
|
173
|
+
type: `agent:${s.lastDirectAgent}:result`,
|
|
174
|
+
data: event.data,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
// 2. Delegation route (manager-led)
|
|
178
|
+
// Find which agent has a pending task
|
|
179
|
+
const pendingAgentName = Object.keys(s.pendingAgentTasks || {}).find((name) => s.pendingAgentTasks[name]);
|
|
180
|
+
if (pendingAgentName) {
|
|
181
|
+
yield {
|
|
182
|
+
type: `agent:${pendingAgentName}:result`,
|
|
183
|
+
data: event.data,
|
|
184
|
+
};
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
// 3. Manager route (brain tools, etc.)
|
|
188
|
+
yield {
|
|
189
|
+
type: "manager:result",
|
|
190
|
+
data: event.data,
|
|
191
|
+
};
|
|
192
|
+
});
|
|
127
193
|
app.use(llmPlugin({
|
|
128
194
|
model: model,
|
|
129
195
|
system: async (context) => {
|
|
@@ -132,21 +198,23 @@ export async function createOpenBot(options) {
|
|
|
132
198
|
]);
|
|
133
199
|
return `${brainPrompt}
|
|
134
200
|
|
|
135
|
-
##
|
|
136
|
-
You are the **Manager Agent**.
|
|
137
|
-
|
|
201
|
+
## Core Role: Manager Agent
|
|
202
|
+
You are the **Manager Agent**. You have exactly two jobs:
|
|
203
|
+
1. **Task Delegation**: Orchestrate tasks by delegating them to specialized agents.
|
|
204
|
+
2. **Memory & Identity**: Manage your long-term memory and identity using your core brain tools (\`remember\`, \`recall\`, \`updateIdentity\`, etc.).
|
|
205
|
+
|
|
206
|
+
### Delegation & Reporting Guidelines:
|
|
207
|
+
- **Delegate by Default**: If a task requires capabilities outside of memory/identity management (like shell access, file operations, web browsing), you **MUST** delegate to the appropriate agent.
|
|
208
|
+
- **Be Concise**: When a sub-agent completes a task, provide a **nice, concise summary**. Sub-agents provide detailed outputs in the background; your response should be a brief, high-level answer to the user. Do not repeat details unless necessary.
|
|
209
|
+
- **Detailed Delegation**: When calling \`delegateTask\`, provide a thorough description of the task so the sub-agent has full context.
|
|
138
210
|
|
|
139
211
|
### Available Agents:
|
|
140
212
|
${agentDescriptions}
|
|
141
213
|
|
|
142
|
-
|
|
143
|
-
1. **Choose the Best Expert**: Analyze the user's request and select the agent whose description most closely matches the required expertise.
|
|
144
|
-
2. **Task Description**: When delegating, provide a clear and detailed task description. Include any context the agent might need to succeed.
|
|
145
|
-
3. **No "I Can't"**: If an agent is available that can handle a request, do not tell the user you cannot do it. Simply delegate.
|
|
146
|
-
4. **Summary**: Once an agent returns a result, summarize the findings or actions for the user.
|
|
147
|
-
|
|
148
|
-
Example: If the user asks to "check the weather", and you see a 'browser' agent, delegate the task to it.`;
|
|
214
|
+
Remember: You are the orchestrator. Let the specialized agents do the work, and you provide the concise final answer.`;
|
|
149
215
|
},
|
|
216
|
+
promptInputType: "manager:input",
|
|
217
|
+
actionResultInputType: "manager:result",
|
|
150
218
|
completionEventType: "manager:completion",
|
|
151
219
|
toolDefinitions: {
|
|
152
220
|
...brainToolDefinitions,
|
|
@@ -177,9 +245,10 @@ Example: If the user asks to "check the weather", and you see a 'browser' agent,
|
|
|
177
245
|
const s = state;
|
|
178
246
|
const pending = s.pendingAgentTasks?.[agent.name];
|
|
179
247
|
if (pending) {
|
|
248
|
+
// This was a delegated task — bridge back to the manager's tool call
|
|
180
249
|
delete s.pendingAgentTasks[agent.name];
|
|
181
250
|
yield {
|
|
182
|
-
type: "
|
|
251
|
+
type: "manager:result",
|
|
183
252
|
data: {
|
|
184
253
|
action: "delegateTask",
|
|
185
254
|
toolCallId: pending.toolCallId,
|
|
@@ -187,6 +256,14 @@ Example: If the user asks to "check the weather", and you see a 'browser' agent,
|
|
|
187
256
|
},
|
|
188
257
|
};
|
|
189
258
|
}
|
|
259
|
+
else {
|
|
260
|
+
// This was a direct command (prefix) — show output directly as assistant text
|
|
261
|
+
yield {
|
|
262
|
+
type: "assistant:text",
|
|
263
|
+
data: { content: event.data.content },
|
|
264
|
+
meta: { agent: agent.name }
|
|
265
|
+
};
|
|
266
|
+
}
|
|
190
267
|
});
|
|
191
268
|
}
|
|
192
269
|
// 6. Init handlers
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { generateId } from "melony";
|
|
2
|
+
import { ui } from "@melony/ui-kit/server";
|
|
3
|
+
/**
|
|
4
|
+
* Approval Plugin for OpenBot.
|
|
5
|
+
* Intercepts specific actions and requires user approval before proceeding.
|
|
6
|
+
* Optimized using the new melony intercept() feature.
|
|
7
|
+
*/
|
|
8
|
+
export const approvalPlugin = (options) => (builder) => {
|
|
9
|
+
const { rules = [] } = options;
|
|
10
|
+
// Register an interceptor that runs before any handlers.
|
|
11
|
+
// This is the correct way to handle HITL/Approval in Melony.
|
|
12
|
+
builder.intercept(async (event, { state, suspend }) => {
|
|
13
|
+
// Skip if already approved or if it's an internal approval event
|
|
14
|
+
// We cast event to any to access the meta property which is used for internal state tracking
|
|
15
|
+
const meta = event.meta;
|
|
16
|
+
if (meta?.approved ||
|
|
17
|
+
event.type === "action:approve" ||
|
|
18
|
+
event.type === "action:deny" ||
|
|
19
|
+
event.type === "ui" ||
|
|
20
|
+
event.type.endsWith(":status")) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const rule = rules.find(r => event.type.startsWith(r.action));
|
|
24
|
+
if (!rule)
|
|
25
|
+
return;
|
|
26
|
+
const approvalId = `approve_${generateId()}`;
|
|
27
|
+
if (!state.pendingApprovals) {
|
|
28
|
+
state.pendingApprovals = {};
|
|
29
|
+
}
|
|
30
|
+
state.pendingApprovals[approvalId] = event;
|
|
31
|
+
// Use suspend(event) to emit the UI and halt execution of any handlers for this event.
|
|
32
|
+
// This effectively "pauses" the run for user input.
|
|
33
|
+
suspend(ui.event(ui.card({
|
|
34
|
+
title: "Approval Required",
|
|
35
|
+
description: rule.message || `Approval required for: ${event.type}`,
|
|
36
|
+
}, [
|
|
37
|
+
ui.text(JSON.stringify(event.data, null, 2), { size: "xs" }),
|
|
38
|
+
ui.row({ gap: "sm" }, [
|
|
39
|
+
ui.button({
|
|
40
|
+
label: "Approve",
|
|
41
|
+
variant: "primary",
|
|
42
|
+
onClickAction: {
|
|
43
|
+
type: "action:approve",
|
|
44
|
+
data: { id: approvalId }
|
|
45
|
+
}
|
|
46
|
+
}),
|
|
47
|
+
ui.button({
|
|
48
|
+
label: "Deny",
|
|
49
|
+
variant: "outline",
|
|
50
|
+
onClickAction: {
|
|
51
|
+
type: "action:deny",
|
|
52
|
+
data: { id: approvalId }
|
|
53
|
+
}
|
|
54
|
+
}),
|
|
55
|
+
]),
|
|
56
|
+
])));
|
|
57
|
+
});
|
|
58
|
+
// Handle Approval response from user
|
|
59
|
+
builder.on("action:approve", async function* (event, { state }) {
|
|
60
|
+
const { id } = event.data;
|
|
61
|
+
const originalEvent = state.pendingApprovals?.[id];
|
|
62
|
+
if (originalEvent) {
|
|
63
|
+
delete state.pendingApprovals[id];
|
|
64
|
+
yield ui.event(ui.status("Action approved", "success"));
|
|
65
|
+
// Re-emit the original event with approved: true.
|
|
66
|
+
// The interceptor will see it, but bypass because of meta.approved.
|
|
67
|
+
// Then the appropriate handlers for the event will finally run.
|
|
68
|
+
yield {
|
|
69
|
+
...originalEvent,
|
|
70
|
+
meta: {
|
|
71
|
+
...originalEvent.meta,
|
|
72
|
+
approved: true,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
// Handle Denial response from user
|
|
78
|
+
builder.on("action:deny", async function* (event, { state }) {
|
|
79
|
+
const { id } = event.data;
|
|
80
|
+
const originalEvent = state.pendingApprovals?.[id];
|
|
81
|
+
if (originalEvent) {
|
|
82
|
+
delete state.pendingApprovals[id];
|
|
83
|
+
yield ui.event(ui.status("Action denied", "error"));
|
|
84
|
+
// If it was a tool call (action:*), return a taskResult error so the LLM knows it failed
|
|
85
|
+
if (originalEvent.data?.toolCallId) {
|
|
86
|
+
yield {
|
|
87
|
+
type: "action:taskResult",
|
|
88
|
+
data: {
|
|
89
|
+
action: originalEvent.type.replace("action:", ""),
|
|
90
|
+
toolCallId: originalEvent.data.toolCallId,
|
|
91
|
+
result: { error: "Action denied by user" },
|
|
92
|
+
success: false,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
export default approvalPlugin;
|
|
@@ -22,10 +22,11 @@ export const llmPlugin = (options) => (builder) => {
|
|
|
22
22
|
state.messages.push(newMessage);
|
|
23
23
|
// Evaluate dynamic system prompt if it's a function
|
|
24
24
|
const systemPrompt = typeof system === "function" ? await system(context) : system;
|
|
25
|
+
const recentMessages = getRecentHistory(state.messages, 20);
|
|
25
26
|
const result = streamText({
|
|
26
27
|
model,
|
|
27
28
|
system: systemPrompt,
|
|
28
|
-
messages:
|
|
29
|
+
messages: recentMessages,
|
|
29
30
|
tools: toolDefinitions,
|
|
30
31
|
});
|
|
31
32
|
let assistantText = "";
|
package/dist/registry/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { PluginRegistry } from "./plugin-registry.js";
|
|
2
2
|
export { AgentRegistry } from "./agent-registry.js";
|
|
3
3
|
export { discoverYamlAgents, listYamlAgents } from "./yaml-agent-loader.js";
|
|
4
|
+
export { discoverTsAgents } from "./ts-agent-loader.js";
|
|
4
5
|
export { loadPluginsFromDir } from "./plugin-loader.js";
|
|
@@ -39,7 +39,7 @@ export async function ensurePluginReady(pluginDir) {
|
|
|
39
39
|
// 1. Install dependencies if node_modules is missing
|
|
40
40
|
if (!hasNodeModules) {
|
|
41
41
|
console.log(`[plugins] Installing dependencies for ${path.basename(pluginDir)}...`);
|
|
42
|
-
execSync("npm install
|
|
42
|
+
execSync("npm install", { cwd: pluginDir, stdio: "inherit" });
|
|
43
43
|
}
|
|
44
44
|
// 2. Run build if dist is missing but build script exists
|
|
45
45
|
const distPath = path.join(pluginDir, "dist");
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { ensurePluginReady } from "./plugin-loader.js";
|
|
5
|
+
/**
|
|
6
|
+
* Discover and load TS-defined agents from a directory.
|
|
7
|
+
*
|
|
8
|
+
* Scans each subdirectory for a package.json and an index file.
|
|
9
|
+
*
|
|
10
|
+
* @param agentsDir Absolute path to the agents directory (e.g. ~/.openbot/agents)
|
|
11
|
+
* @param defaultModel Language model to use for agent LLMs
|
|
12
|
+
* @param options Optional API keys for creating specific models
|
|
13
|
+
* @returns Array of discovered agent entries ready for registration
|
|
14
|
+
*/
|
|
15
|
+
export async function discoverTsAgents(agentsDir, defaultModel, options) {
|
|
16
|
+
const agents = [];
|
|
17
|
+
try {
|
|
18
|
+
const entries = await fs.readdir(agentsDir, { withFileTypes: true });
|
|
19
|
+
for (const entry of entries) {
|
|
20
|
+
if (!entry.isDirectory())
|
|
21
|
+
continue;
|
|
22
|
+
if (entry.name.startsWith(".") || entry.name.startsWith("_"))
|
|
23
|
+
continue;
|
|
24
|
+
const agentDir = path.join(agentsDir, entry.name);
|
|
25
|
+
// We only consider it a TS agent if it doesn't have an agent.yaml
|
|
26
|
+
// (This avoids double-loading if someone has both for some reason)
|
|
27
|
+
const yamlPath = path.join(agentDir, "agent.yaml");
|
|
28
|
+
const hasYaml = await fs.access(yamlPath).then(() => true).catch(() => false);
|
|
29
|
+
if (hasYaml)
|
|
30
|
+
continue;
|
|
31
|
+
// Check for package.json to see if it's a package
|
|
32
|
+
const pkgPath = path.join(agentDir, "package.json");
|
|
33
|
+
const hasPackageJson = await fs.access(pkgPath).then(() => true).catch(() => false);
|
|
34
|
+
if (!hasPackageJson)
|
|
35
|
+
continue;
|
|
36
|
+
try {
|
|
37
|
+
// 1. Ensure dependencies and build are ready
|
|
38
|
+
await ensurePluginReady(agentDir);
|
|
39
|
+
// 2. Find index file
|
|
40
|
+
let indexPath;
|
|
41
|
+
const possibleIndices = ["dist/index.js", "index.js", "index.ts"];
|
|
42
|
+
for (const file of possibleIndices) {
|
|
43
|
+
try {
|
|
44
|
+
const fullPath = path.join(agentDir, file);
|
|
45
|
+
await fs.access(fullPath);
|
|
46
|
+
indexPath = fullPath;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (!indexPath)
|
|
54
|
+
continue;
|
|
55
|
+
// 3. Import and instantiate
|
|
56
|
+
const moduleUrl = pathToFileURL(indexPath).href;
|
|
57
|
+
const module = await import(moduleUrl);
|
|
58
|
+
// Support 'agent', 'plugin', 'default', or 'entry'
|
|
59
|
+
const definition = module.agent || module.plugin || module.default || module.entry;
|
|
60
|
+
if (definition && typeof definition.factory === "function") {
|
|
61
|
+
const name = definition.name || entry.name;
|
|
62
|
+
const description = definition.description || "TS Agent";
|
|
63
|
+
agents.push({
|
|
64
|
+
name,
|
|
65
|
+
description,
|
|
66
|
+
plugin: definition.factory({ ...options, model: defaultModel }),
|
|
67
|
+
capabilities: definition.capabilities,
|
|
68
|
+
subscribe: definition.subscribe,
|
|
69
|
+
});
|
|
70
|
+
console.log(`[agents] Loaded TS agent: ${name} — ${description}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
console.warn(`[agents] Failed to load TS agent package "${entry.name}":`, err);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Agents directory doesn't exist
|
|
80
|
+
}
|
|
81
|
+
return agents;
|
|
82
|
+
}
|
package/dist/ui/settings.js
CHANGED
|
@@ -32,9 +32,9 @@ export const settingsUI = async () => {
|
|
|
32
32
|
ui.heading("Model Configuration", 4),
|
|
33
33
|
ui.col({ gap: "sm" }, [
|
|
34
34
|
ui.input("model", undefined, {
|
|
35
|
-
placeholder: "e.g. gpt-4o
|
|
35
|
+
placeholder: "provider/model (e.g. openai/gpt-4o)",
|
|
36
36
|
width: "full",
|
|
37
|
-
defaultValue: config.model || "gpt-4o-mini"
|
|
37
|
+
defaultValue: config.model || "openai/gpt-4o-mini"
|
|
38
38
|
}),
|
|
39
39
|
]),
|
|
40
40
|
]),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openbot",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.29",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"express": "^4.19.2",
|
|
19
19
|
"gray-matter": "^4.0.3",
|
|
20
20
|
"js-yaml": "^4.1.1",
|
|
21
|
-
"melony": "^0.2.
|
|
21
|
+
"melony": "^0.2.9",
|
|
22
22
|
"zod": "^4.3.5"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|