@owloops/browserbird 1.4.22 → 1.5.0
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/index.mjs +65 -8
- package/package.json +1 -1
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: ${"8a715575d2cfba34d6be3d948660af50a731ab0d".substring(0, 7)}`);
|
|
126
|
+
buildInfo.push(`built: 2026-03-17T23:32:28+04:00`);
|
|
127
127
|
const buildString = buildInfo.length > 0 ? ` (${buildInfo.join(", ")})` : "";
|
|
128
128
|
const VERSION = `browserbird ${pkg.version}${buildString}`;
|
|
129
129
|
const BIRD = [
|
|
@@ -2696,7 +2696,9 @@ function birdFlyBlocks(birdName, userId) {
|
|
|
2696
2696
|
}
|
|
2697
2697
|
function statusBlocks(opts) {
|
|
2698
2698
|
const slackStatus = opts.slackConnected ? "Connected" : "Disconnected";
|
|
2699
|
-
|
|
2699
|
+
const result = [header("BrowserBird Status"), fields(["Slack", slackStatus], ["Active Sessions", `${opts.activeCount}/${opts.maxConcurrent}`], ["Birds", String(opts.birdCount)], ["Uptime", opts.uptime])];
|
|
2700
|
+
if (opts.runningBirds && opts.runningBirds.length > 0) result.push(section(`*In flight:* ${opts.runningBirds.join(", ")}`));
|
|
2701
|
+
return result;
|
|
2700
2702
|
}
|
|
2701
2703
|
function truncate(text, maxLength) {
|
|
2702
2704
|
if (text.length <= maxLength) return text;
|
|
@@ -2706,6 +2708,17 @@ function truncate(text, maxLength) {
|
|
|
2706
2708
|
//#endregion
|
|
2707
2709
|
//#region src/cron/scheduler.ts
|
|
2708
2710
|
const BROWSER_TOOL_PREFIX$1 = "mcp__playwright__";
|
|
2711
|
+
const activeKills = /* @__PURE__ */ new Map();
|
|
2712
|
+
function killBird(cronJobUid) {
|
|
2713
|
+
const kill = activeKills.get(cronJobUid);
|
|
2714
|
+
if (!kill) return false;
|
|
2715
|
+
kill();
|
|
2716
|
+
activeKills.delete(cronJobUid);
|
|
2717
|
+
return true;
|
|
2718
|
+
}
|
|
2719
|
+
function getRunningBirdUids() {
|
|
2720
|
+
return [...activeKills.keys()];
|
|
2721
|
+
}
|
|
2709
2722
|
const TICK_INTERVAL_MS = 6e4;
|
|
2710
2723
|
const MAX_SCHEDULE_ERRORS = 3;
|
|
2711
2724
|
const systemHandlers = /* @__PURE__ */ new Map();
|
|
@@ -2746,13 +2759,14 @@ function startScheduler(getConfig, signal, deps) {
|
|
|
2746
2759
|
const needsBrowserLock = config.browser.enabled && getBrowserMode() === "persistent";
|
|
2747
2760
|
let browserLock = null;
|
|
2748
2761
|
try {
|
|
2749
|
-
const { events } = spawnProvider({
|
|
2762
|
+
const { events, kill } = spawnProvider({
|
|
2750
2763
|
message: payload.prompt,
|
|
2751
2764
|
agent,
|
|
2752
2765
|
mcpConfigPath: config.browser.mcpConfigPath,
|
|
2753
2766
|
timezone: config.timezone,
|
|
2754
2767
|
globalTimeoutMs: config.sessions.processTimeoutMs
|
|
2755
2768
|
}, signal);
|
|
2769
|
+
activeKills.set(payload.cronJobUid, kill);
|
|
2756
2770
|
if (payload.channelId) logMessage(payload.channelId, null, agent.id, "in", payload.prompt);
|
|
2757
2771
|
let result = "";
|
|
2758
2772
|
let completion;
|
|
@@ -2789,6 +2803,7 @@ function startScheduler(getConfig, signal, deps) {
|
|
|
2789
2803
|
} else logger.info(`bird ${shortUid(payload.cronJobUid)} completed (${result.length} chars)`);
|
|
2790
2804
|
return result;
|
|
2791
2805
|
} finally {
|
|
2806
|
+
activeKills.delete(payload.cronJobUid);
|
|
2792
2807
|
browserLock?.release();
|
|
2793
2808
|
}
|
|
2794
2809
|
});
|
|
@@ -2865,6 +2880,7 @@ function startScheduler(getConfig, signal, deps) {
|
|
|
2865
2880
|
clearInterval(timer);
|
|
2866
2881
|
scheduleCache.clear();
|
|
2867
2882
|
scheduleErrors.clear();
|
|
2883
|
+
activeKills.clear();
|
|
2868
2884
|
});
|
|
2869
2885
|
logger.info("bird scheduler started (60s tick)");
|
|
2870
2886
|
}
|
|
@@ -3029,9 +3045,10 @@ function createHandler(client, getConfig, signal, getTeamId, getChannelNameToId)
|
|
|
3029
3045
|
await safeStop({});
|
|
3030
3046
|
const blocks = sessionTimeoutBlocks(timedOutMs, { sessionUid });
|
|
3031
3047
|
await client.postMessage(channelId, threadTs, `Session timed out after ${Math.round(timedOutMs / 6e4)} minutes.`, { blocks });
|
|
3032
|
-
} else {
|
|
3033
|
-
|
|
3034
|
-
|
|
3048
|
+
} else if (completion) await safeStop({ blocks: completionFooterBlocks(completion, hasError, meta.birdName, userId) });
|
|
3049
|
+
else {
|
|
3050
|
+
if (!fullText) await safeAppend({ markdown_text: "_Stopped._" });
|
|
3051
|
+
await safeStop({});
|
|
3035
3052
|
}
|
|
3036
3053
|
}
|
|
3037
3054
|
async function uploadImages(images, channelId, threadTs) {
|
|
@@ -3290,6 +3307,9 @@ async function handleSlashCommand(body, webClient, channelClient, config, status
|
|
|
3290
3307
|
}
|
|
3291
3308
|
case "status": {
|
|
3292
3309
|
const cronJobs = listCronJobs(1, 1, false);
|
|
3310
|
+
const runningBirds = getRunningBirdUids().map((uid) => {
|
|
3311
|
+
return getCronJob(uid)?.name ?? uid;
|
|
3312
|
+
});
|
|
3293
3313
|
await say({
|
|
3294
3314
|
text: "BrowserBird status",
|
|
3295
3315
|
blocks: statusBlocks({
|
|
@@ -3297,16 +3317,35 @@ async function handleSlashCommand(body, webClient, channelClient, config, status
|
|
|
3297
3317
|
activeCount: status.activeCount(),
|
|
3298
3318
|
maxConcurrent: config.sessions.maxConcurrent,
|
|
3299
3319
|
birdCount: cronJobs.totalItems,
|
|
3300
|
-
uptime: formatUptime()
|
|
3320
|
+
uptime: formatUptime(),
|
|
3321
|
+
runningBirds
|
|
3301
3322
|
})
|
|
3302
3323
|
});
|
|
3303
3324
|
break;
|
|
3304
3325
|
}
|
|
3326
|
+
case "stop": {
|
|
3327
|
+
const birdName = parts.slice(1).join(" ");
|
|
3328
|
+
if (!birdName) {
|
|
3329
|
+
await say({ text: "Usage: `/bird stop <name or id>`" });
|
|
3330
|
+
return;
|
|
3331
|
+
}
|
|
3332
|
+
const bird = findBird(birdName);
|
|
3333
|
+
if (!bird) {
|
|
3334
|
+
await say({ text: `Bird not found: \`${birdName}\`` });
|
|
3335
|
+
return;
|
|
3336
|
+
}
|
|
3337
|
+
if (killBird(bird.uid)) {
|
|
3338
|
+
await say({ text: `Stopped *${bird.name}*.` });
|
|
3339
|
+
logger.info(`/bird stop: ${bird.name} killed by ${body.user_id}`);
|
|
3340
|
+
} else await say({ text: `*${bird.name}* is not currently in flight.` });
|
|
3341
|
+
break;
|
|
3342
|
+
}
|
|
3305
3343
|
default: await say({ text: [
|
|
3306
3344
|
"*Usage:* `/bird <command>`",
|
|
3307
3345
|
"",
|
|
3308
3346
|
"`/bird list` - Show all configured birds",
|
|
3309
3347
|
"`/bird fly <name>` - Trigger a bird immediately",
|
|
3348
|
+
"`/bird stop [name]` - Stop a running bird",
|
|
3310
3349
|
"`/bird logs <name>` - Show recent flights",
|
|
3311
3350
|
"`/bird enable <name>` - Enable a bird",
|
|
3312
3351
|
"`/bird disable <name>` - Disable a bird",
|
|
@@ -3470,6 +3509,15 @@ function createSlackChannel(getConfig, signal) {
|
|
|
3470
3509
|
const messageTs = event["ts"];
|
|
3471
3510
|
const cleanText = stripMention(text);
|
|
3472
3511
|
if (!cleanText.trim()) return;
|
|
3512
|
+
if (cleanText.trim().toLowerCase() === "stop") {
|
|
3513
|
+
const key = `${channelId}:${threadTs}`;
|
|
3514
|
+
if (handler.killSession(key)) webClient.reactions.add({
|
|
3515
|
+
channel: channelId,
|
|
3516
|
+
timestamp: messageTs,
|
|
3517
|
+
name: "octagonal_sign"
|
|
3518
|
+
}).catch(() => {});
|
|
3519
|
+
return;
|
|
3520
|
+
}
|
|
3473
3521
|
if (isDm && config.slack.coalesce.bypassDms) handler.handle({
|
|
3474
3522
|
channelId,
|
|
3475
3523
|
threadTs,
|
|
@@ -3495,6 +3543,15 @@ function createSlackChannel(getConfig, signal) {
|
|
|
3495
3543
|
const userId = event["user"] ?? "unknown";
|
|
3496
3544
|
const text = stripMention(event["text"] ?? "");
|
|
3497
3545
|
if (!text.trim()) return;
|
|
3546
|
+
if (text.trim().toLowerCase() === "stop") {
|
|
3547
|
+
const key = `${channelId}:${threadTs}`;
|
|
3548
|
+
if (handler.killSession(key)) webClient.reactions.add({
|
|
3549
|
+
channel: channelId,
|
|
3550
|
+
timestamp: messageTs,
|
|
3551
|
+
name: "octagonal_sign"
|
|
3552
|
+
}).catch(() => {});
|
|
3553
|
+
return;
|
|
3554
|
+
}
|
|
3498
3555
|
coalescer.push(channelId, threadTs, userId, text, messageTs);
|
|
3499
3556
|
});
|
|
3500
3557
|
socketClient.on("assistant_thread_started", async ({ ack, event }) => {
|