@owloops/browserbird 1.2.11 → 1.3.1
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/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# BrowserBird
|
|
6
6
|
|
|
7
|
-
Self-hosted AI agent
|
|
7
|
+
Self-hosted AI agent orchestrator with a real browser, a cron scheduler, and a web dashboard.
|
|
8
8
|
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
[](https://www.npmjs.com/package/@owloops/browserbird)
|
|
@@ -25,7 +25,7 @@ Self-hosted AI agent for Slack with a real browser, a scheduler, and a web dashb
|
|
|
25
25
|
</tr>
|
|
26
26
|
</table>
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
Schedule AI agents to run on a cron, browse the web with a real Chromium browser you can watch live through VNC, and manage everything from a web dashboard or the CLI. Optionally connect Slack for conversational threads and slash commands. BrowserBird is the orchestration layer; the agent CLI ([claude](https://docs.anthropic.com/en/docs/claude-code/overview), [opencode](https://github.com/anomalyco/opencode)) handles reasoning, memory, tools, and sub-agents.
|
|
29
29
|
|
|
30
30
|
Built by [Owloops](https://github.com/Owloops), building browser automation tools since 2020.
|
|
31
31
|
|
|
@@ -46,7 +46,7 @@ These are starting points. Every bird has a full AI agent (Claude Code or openco
|
|
|
46
46
|
|
|
47
47
|
## Installation
|
|
48
48
|
|
|
49
|
-
On first run, open the web UI and complete the onboarding wizard. It walks through
|
|
49
|
+
On first run, open the web UI and complete the onboarding wizard. It walks through agent config, API keys, and optional integrations (Slack, browser).
|
|
50
50
|
|
|
51
51
|
### Docker (recommended)
|
|
52
52
|
|
|
@@ -63,7 +63,7 @@ The browser runs in **persistent** mode by default: logins and cookies are saved
|
|
|
63
63
|
|
|
64
64
|
[](https://railway.com/deploy/browserbird-1)
|
|
65
65
|
|
|
66
|
-
## Slack
|
|
66
|
+
## Slack (Optional)
|
|
67
67
|
|
|
68
68
|
[](https://api.slack.com/apps?new_app=1&manifest_json=%7B%22display_information%22%3A%7B%22name%22%3A%22BrowserBird%22%2C%22description%22%3A%22A%20self-hosted%20AI%20assistant%20in%20Slack%2C%20with%20a%20real%20browser%20and%20a%20scheduler.%22%2C%22background_color%22%3A%22%231a1a2e%22%7D%2C%22features%22%3A%7B%22assistant_view%22%3A%7B%22assistant_description%22%3A%22A%20self-hosted%20AI%20assistant%20in%20Slack%2C%20with%20a%20real%20browser%20and%20a%20scheduler.%22%7D%2C%22app_home%22%3A%7B%22home_tab_enabled%22%3Atrue%2C%22messages_tab_enabled%22%3Atrue%2C%22messages_tab_read_only_enabled%22%3Afalse%7D%2C%22bot_user%22%3A%7B%22display_name%22%3A%22BrowserBird%22%2C%22always_online%22%3Atrue%7D%2C%22slash_commands%22%3A%5B%7B%22command%22%3A%22%2Fbird%22%2C%22description%22%3A%22Manage%20BrowserBird%20birds%22%2C%22usage_hint%22%3A%22list%20%7C%20fly%20%7C%20logs%20%7C%20enable%20%7C%20disable%20%7C%20create%20%7C%20status%22%2C%22should_escape%22%3Afalse%7D%5D%7D%2C%22oauth_config%22%3A%7B%22scopes%22%3A%7B%22bot%22%3A%5B%22app_mentions%3Aread%22%2C%22assistant%3Awrite%22%2C%22channels%3Ahistory%22%2C%22channels%3Aread%22%2C%22chat%3Awrite%22%2C%22files%3Aread%22%2C%22files%3Awrite%22%2C%22groups%3Ahistory%22%2C%22groups%3Aread%22%2C%22im%3Ahistory%22%2C%22im%3Aread%22%2C%22im%3Awrite%22%2C%22mpim%3Ahistory%22%2C%22mpim%3Aread%22%2C%22reactions%3Aread%22%2C%22reactions%3Awrite%22%2C%22users%3Aread%22%2C%22commands%22%5D%7D%7D%2C%22settings%22%3A%7B%22event_subscriptions%22%3A%7B%22bot_events%22%3A%5B%22app_mention%22%2C%22assistant_thread_context_changed%22%2C%22assistant_thread_started%22%2C%22message.channels%22%2C%22message.groups%22%2C%22message.im%22%2C%22message.mpim%22%5D%7D%2C%22interactivity%22%3A%7B%22is_enabled%22%3Atrue%7D%2C%22org_deploy_enabled%22%3Afalse%2C%22socket_mode_enabled%22%3Atrue%2C%22token_rotation_enabled%22%3Afalse%7D%7D)
|
|
69
69
|
|
|
@@ -114,7 +114,7 @@ The top-level `timezone` field (IANA format, default `"UTC"`) is used for cron s
|
|
|
114
114
|
}
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
-
- `botToken`, `appToken`:
|
|
117
|
+
- `botToken`, `appToken`: Optional. Bot user OAuth token and app-level token for Socket Mode. Required only for Slack integration
|
|
118
118
|
- `requireMention`: Only respond in channels when `@mentioned`; DMs always respond
|
|
119
119
|
- `coalesce.debounceMs`: Wait N ms after last message before dispatching (groups rapid messages)
|
|
120
120
|
- `coalesce.bypassDms`: Skip debouncing for DMs
|
|
@@ -247,8 +247,8 @@ Authentication is handled via the web UI. On first visit, you create an account.
|
|
|
247
247
|
|
|
248
248
|
| Variable | Description |
|
|
249
249
|
| ------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
250
|
-
| `SLACK_BOT_TOKEN` | Bot user OAuth token
|
|
251
|
-
| `SLACK_APP_TOKEN` | App-level token for Socket Mode
|
|
250
|
+
| `SLACK_BOT_TOKEN` | Bot user OAuth token (optional, for Slack integration) |
|
|
251
|
+
| `SLACK_APP_TOKEN` | App-level token for Socket Mode (optional, for Slack integration) |
|
|
252
252
|
| `ANTHROPIC_API_KEY` | Anthropic API key (pay-per-token). Used by both claude and opencode providers |
|
|
253
253
|
| `CLAUDE_CODE_OAUTH_TOKEN` | OAuth token for claude provider only (uses your Claude Pro/Max subscription) |
|
|
254
254
|
| `BROWSER_MODE` | `persistent` (default) or `isolated`. Requires container restart |
|
|
@@ -295,6 +295,16 @@ options:
|
|
|
295
295
|
run 'browserbird <command> --help' for command-specific options.
|
|
296
296
|
```
|
|
297
297
|
|
|
298
|
+
### Standalone CLI Workflow
|
|
299
|
+
|
|
300
|
+
BrowserBird works without Slack. Create a bird, trigger it, and check results from the terminal:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
browserbird birds add --schedule "0 9 * * *" --prompt "Check Hacker News for AI news and summarize"
|
|
304
|
+
browserbird birds fly <name>
|
|
305
|
+
browserbird birds flights <name>
|
|
306
|
+
```
|
|
307
|
+
|
|
298
308
|
## Web UI
|
|
299
309
|
|
|
300
310
|
Runs at `http://localhost:18800` by default.
|
package/dist/index.mjs
CHANGED
|
@@ -122,8 +122,8 @@ function unknownSubcommand(subcommand, command, validCommands) {
|
|
|
122
122
|
/** @fileoverview ASCII banner displayed on daemon startup and in help text. */
|
|
123
123
|
const pkg = createRequire(import.meta.url)("../package.json");
|
|
124
124
|
const buildInfo = [];
|
|
125
|
-
buildInfo.push(`commit: ${"
|
|
126
|
-
buildInfo.push(`built: 2026-03-
|
|
125
|
+
buildInfo.push(`commit: ${"7a38fde28b18642c0e6dbfe7c7221777ed34033b".substring(0, 7)}`);
|
|
126
|
+
buildInfo.push(`built: 2026-03-10T11:08:01+04:00`);
|
|
127
127
|
const buildString = buildInfo.length > 0 ? ` (${buildInfo.join(", ")})` : "";
|
|
128
128
|
const VERSION = `browserbird ${pkg.version}${buildString}`;
|
|
129
129
|
const BIRD = [
|
|
@@ -267,31 +267,6 @@ function loadRawConfig(configPath) {
|
|
|
267
267
|
return deepMerge(DEFAULTS, parsed);
|
|
268
268
|
}
|
|
269
269
|
/**
|
|
270
|
-
* Checks whether both Slack tokens are present and resolvable.
|
|
271
|
-
* Literal strings must be non-empty; `"env:VAR"` references must point to a set env var.
|
|
272
|
-
*/
|
|
273
|
-
function hasSlackTokens(configPath) {
|
|
274
|
-
const filePath = configPath ?? resolve("browserbird.json");
|
|
275
|
-
if (!existsSync(filePath)) return false;
|
|
276
|
-
let parsed;
|
|
277
|
-
try {
|
|
278
|
-
parsed = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
279
|
-
} catch {
|
|
280
|
-
return false;
|
|
281
|
-
}
|
|
282
|
-
const slack = parsed["slack"];
|
|
283
|
-
if (!slack) return false;
|
|
284
|
-
return isTokenResolvable(slack["botToken"]) && isTokenResolvable(slack["appToken"]);
|
|
285
|
-
}
|
|
286
|
-
function isTokenResolvable(value) {
|
|
287
|
-
if (typeof value !== "string" || !value) return false;
|
|
288
|
-
if (value.startsWith("env:")) {
|
|
289
|
-
const envKey = value.slice(4);
|
|
290
|
-
return !!process.env[envKey];
|
|
291
|
-
}
|
|
292
|
-
return true;
|
|
293
|
-
}
|
|
294
|
-
/**
|
|
295
270
|
* When the browser is enabled but no explicit mcpConfigPath is set, generates
|
|
296
271
|
* a Playwright MCP config file using novncHost as the SSE server hostname.
|
|
297
272
|
* Writes to `<configDir>/mcp.json` and mutates `config.browser.mcpConfigPath`.
|
|
@@ -1232,7 +1207,7 @@ function buildRoutes(getConfig, startedAt, getDeps, options) {
|
|
|
1232
1207
|
broadcastSSE("invalidate", { resource: "secrets" });
|
|
1233
1208
|
json(res, {
|
|
1234
1209
|
success: true,
|
|
1235
|
-
requiresRestart:
|
|
1210
|
+
requiresRestart: false
|
|
1236
1211
|
});
|
|
1237
1212
|
} catch (err) {
|
|
1238
1213
|
jsonError(res, `Failed to save Slack tokens: ${err instanceof Error ? err.message : String(err)}`, 500);
|
|
@@ -3945,14 +3920,6 @@ function setupShutdown() {
|
|
|
3945
3920
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
3946
3921
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
3947
3922
|
}
|
|
3948
|
-
const stubDeps = {
|
|
3949
|
-
slackConnected: () => false,
|
|
3950
|
-
activeProcessCount: () => 0,
|
|
3951
|
-
serviceHealth: () => ({
|
|
3952
|
-
agent: { available: false },
|
|
3953
|
-
browser: { connected: false }
|
|
3954
|
-
})
|
|
3955
|
-
};
|
|
3956
3923
|
async function startDaemon(options) {
|
|
3957
3924
|
setupShutdown();
|
|
3958
3925
|
logger.setMode("daemon");
|
|
@@ -3965,56 +3932,68 @@ async function startDaemon(options) {
|
|
|
3965
3932
|
clearBrowserLock();
|
|
3966
3933
|
startWorker(controller.signal);
|
|
3967
3934
|
loadDotEnv(envPath);
|
|
3968
|
-
let currentConfig
|
|
3935
|
+
let currentConfig;
|
|
3969
3936
|
let slackHandle = null;
|
|
3970
|
-
let
|
|
3937
|
+
let schedulerStarted = false;
|
|
3938
|
+
let slackStarted = false;
|
|
3939
|
+
let healthStarted = false;
|
|
3940
|
+
try {
|
|
3941
|
+
currentConfig = loadConfig(configPath);
|
|
3942
|
+
ensureMcpConfig(currentConfig, configDir);
|
|
3943
|
+
} catch (err) {
|
|
3944
|
+
logger.warn(`config validation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
3945
|
+
currentConfig = loadRawConfig(configPath);
|
|
3946
|
+
}
|
|
3971
3947
|
const getConfig = () => currentConfig;
|
|
3972
|
-
const getDeps = () => {
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
};
|
|
3980
|
-
const startFull = (config) => {
|
|
3948
|
+
const getDeps = () => ({
|
|
3949
|
+
slackConnected: () => slackHandle?.isConnected() ?? false,
|
|
3950
|
+
activeProcessCount: () => slackHandle?.activeCount() ?? 0,
|
|
3951
|
+
serviceHealth: () => getServiceHealth(currentConfig)
|
|
3952
|
+
});
|
|
3953
|
+
let activated = false;
|
|
3954
|
+
const activateLayers = (config) => {
|
|
3981
3955
|
currentConfig = config;
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3956
|
+
if (!schedulerStarted && config.agents.length > 0) {
|
|
3957
|
+
logger.info("starting scheduler...");
|
|
3958
|
+
startScheduler(config, controller.signal, { postToSlack: (channel, text, opts) => slackHandle ? slackHandle.postMessage(channel, text, opts) : Promise.resolve() });
|
|
3959
|
+
schedulerStarted = true;
|
|
3960
|
+
}
|
|
3961
|
+
if (!healthStarted) {
|
|
3962
|
+
startHealthChecks(getConfig, controller.signal);
|
|
3963
|
+
healthStarted = true;
|
|
3964
|
+
}
|
|
3965
|
+
if (!slackStarted && config.slack.botToken && config.slack.appToken) {
|
|
3966
|
+
logger.info("connecting to slack...");
|
|
3967
|
+
slackHandle = createSlackChannel(config, controller.signal);
|
|
3968
|
+
slackHandle.start().catch((err) => {
|
|
3969
|
+
logger.error(`slack failed to start: ${err instanceof Error ? err.message : String(err)}`);
|
|
3970
|
+
});
|
|
3971
|
+
slackStarted = true;
|
|
3972
|
+
}
|
|
3973
|
+
if (!activated) {
|
|
3974
|
+
logger.success("browserbird orchestrator started");
|
|
3975
|
+
logger.info(`agents: ${config.agents.map((a) => a.id).join(", ") || "none"}`);
|
|
3976
|
+
if (!slackStarted) logger.info("slack: not configured");
|
|
3977
|
+
logger.info(`max concurrent sessions: ${config.sessions.maxConcurrent}`);
|
|
3978
|
+
if (config.browser.enabled) logger.info(`browser mode: ${getBrowserMode()}`);
|
|
3979
|
+
activated = true;
|
|
3980
|
+
}
|
|
3995
3981
|
};
|
|
3996
3982
|
const onLaunch = async () => {
|
|
3997
3983
|
loadDotEnv(envPath);
|
|
3998
3984
|
const config = loadConfig(configPath);
|
|
3999
3985
|
ensureMcpConfig(config, configDir);
|
|
4000
|
-
|
|
4001
|
-
startFull(config);
|
|
3986
|
+
activateLayers(config);
|
|
4002
3987
|
};
|
|
4003
3988
|
const reloadConfig = () => {
|
|
4004
3989
|
loadDotEnv(envPath);
|
|
4005
|
-
|
|
4006
|
-
ensureMcpConfig(
|
|
3990
|
+
const config = loadConfig(configPath);
|
|
3991
|
+
ensureMcpConfig(config, configDir);
|
|
3992
|
+
activateLayers(config);
|
|
4007
3993
|
logger.info("config reloaded");
|
|
4008
3994
|
};
|
|
4009
|
-
if (
|
|
4010
|
-
|
|
4011
|
-
ensureMcpConfig(currentConfig, configDir);
|
|
4012
|
-
setSetting("onboarding_completed", "true");
|
|
4013
|
-
startFull(currentConfig);
|
|
4014
|
-
} else {
|
|
4015
|
-
setSetting("onboarding_completed", "");
|
|
4016
|
-
logger.info("starting in setup mode (onboarding not completed)");
|
|
4017
|
-
}
|
|
3995
|
+
if (currentConfig.agents.length > 0) activateLayers(currentConfig);
|
|
3996
|
+
else logger.info("no agents configured");
|
|
4018
3997
|
let webServer = null;
|
|
4019
3998
|
const webConfig = getConfig();
|
|
4020
3999
|
if (webConfig.web.enabled) {
|
|
@@ -4027,7 +4006,6 @@ async function startDaemon(options) {
|
|
|
4027
4006
|
});
|
|
4028
4007
|
await webServer.start();
|
|
4029
4008
|
}
|
|
4030
|
-
if (setupMode) logger.info("waiting for onboarding to complete via web UI");
|
|
4031
4009
|
await new Promise((resolvePromise) => {
|
|
4032
4010
|
controller.signal.addEventListener("abort", () => {
|
|
4033
4011
|
resolvePromise();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owloops/browserbird",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.3.1",
|
|
4
|
+
"description": "AI agent orchestrator with a real browser, a cron scheduler, and a web dashboard",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"browserbird": "./bin/browserbird"
|
|
@@ -28,12 +28,14 @@
|
|
|
28
28
|
"prepublishOnly": "npm run lint && npm run format:check && npm run typecheck && npm run build && cd web && npm ci && npm run build"
|
|
29
29
|
},
|
|
30
30
|
"keywords": [
|
|
31
|
-
"slack",
|
|
32
|
-
"claude",
|
|
33
|
-
"claude-code",
|
|
34
|
-
"orchestrator",
|
|
35
31
|
"cli",
|
|
36
|
-
"
|
|
32
|
+
"ai-agent",
|
|
33
|
+
"orchestrator",
|
|
34
|
+
"browser-automation",
|
|
35
|
+
"scheduler",
|
|
36
|
+
"cron",
|
|
37
|
+
"claude",
|
|
38
|
+
"playwright"
|
|
37
39
|
],
|
|
38
40
|
"author": "Owloops",
|
|
39
41
|
"license": "FSL-1.1-MIT",
|