bosun 0.29.5 → 0.29.7
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 +3 -0
- package/cli.mjs +59 -2
- package/codex-config.mjs +58 -8
- package/codex-model-profiles.mjs +34 -0
- package/config.mjs +26 -10
- package/desktop/launch.mjs +2 -0
- package/desktop/main.mjs +2 -0
- package/github-reconciler.mjs +7 -0
- package/hook-profiles.mjs +14 -10
- package/monitor.mjs +20 -3
- package/package.json +2 -1
- package/repo-config.mjs +724 -0
- package/setup.mjs +393 -97
- package/task-executor.mjs +1 -1
- package/telegram-bot.mjs +1 -1
- package/ui/components/workspace-switcher.js +43 -68
- package/ui/index.html +1 -0
- package/ui/styles/components.css +1 -14
- package/ui/styles/sessions.css +14 -4
- package/ui/styles/variables.css +5 -5
- package/ui/styles/workspace-switcher.css +35 -0
- package/ui/tabs/settings.js +33 -17
package/README.md
CHANGED
package/cli.mjs
CHANGED
|
@@ -66,6 +66,7 @@ function showHelp() {
|
|
|
66
66
|
|
|
67
67
|
COMMANDS
|
|
68
68
|
--setup Run the interactive setup wizard
|
|
69
|
+
--where Show the resolved bosun config directory
|
|
69
70
|
--doctor Validate bosun .env/config setup
|
|
70
71
|
--help Show this help
|
|
71
72
|
--version Show version
|
|
@@ -203,6 +204,48 @@ function showHelp() {
|
|
|
203
204
|
`);
|
|
204
205
|
}
|
|
205
206
|
|
|
207
|
+
function isWslInteropRuntime() {
|
|
208
|
+
return Boolean(
|
|
209
|
+
process.env.WSL_DISTRO_NAME ||
|
|
210
|
+
process.env.WSL_INTEROP ||
|
|
211
|
+
(process.platform === "win32" &&
|
|
212
|
+
String(process.env.HOME || "")
|
|
213
|
+
.trim()
|
|
214
|
+
.startsWith("/home/")),
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function resolveConfigDirForCli() {
|
|
219
|
+
if (process.env.BOSUN_DIR) return resolve(process.env.BOSUN_DIR);
|
|
220
|
+
const preferWindowsDirs =
|
|
221
|
+
process.platform === "win32" && !isWslInteropRuntime();
|
|
222
|
+
const baseDir = preferWindowsDirs
|
|
223
|
+
? process.env.APPDATA ||
|
|
224
|
+
process.env.LOCALAPPDATA ||
|
|
225
|
+
process.env.USERPROFILE ||
|
|
226
|
+
process.env.HOME ||
|
|
227
|
+
process.cwd()
|
|
228
|
+
: process.env.HOME ||
|
|
229
|
+
process.env.XDG_CONFIG_HOME ||
|
|
230
|
+
process.env.USERPROFILE ||
|
|
231
|
+
process.env.APPDATA ||
|
|
232
|
+
process.env.LOCALAPPDATA ||
|
|
233
|
+
process.cwd();
|
|
234
|
+
return resolve(baseDir, "bosun");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function printConfigLocations() {
|
|
238
|
+
const configDir = resolveConfigDirForCli();
|
|
239
|
+
const envPath = resolve(configDir, ".env");
|
|
240
|
+
const configPath = resolve(configDir, "bosun.config.json");
|
|
241
|
+
const workspacesPath = resolve(configDir, "workspaces");
|
|
242
|
+
console.log("\n Bosun config directory");
|
|
243
|
+
console.log(` ${configDir}`);
|
|
244
|
+
console.log(` .env: ${envPath}`);
|
|
245
|
+
console.log(` bosun.config.json: ${configPath}`);
|
|
246
|
+
console.log(` workspaces: ${workspacesPath}\n`);
|
|
247
|
+
}
|
|
248
|
+
|
|
206
249
|
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
207
250
|
|
|
208
251
|
// ── Daemon Mode ──────────────────────────────────────────────────────────────
|
|
@@ -572,6 +615,12 @@ async function main() {
|
|
|
572
615
|
process.exit(0);
|
|
573
616
|
}
|
|
574
617
|
|
|
618
|
+
// Handle --where
|
|
619
|
+
if (args.includes("--where") || args.includes("where")) {
|
|
620
|
+
printConfigLocations();
|
|
621
|
+
process.exit(0);
|
|
622
|
+
}
|
|
623
|
+
|
|
575
624
|
// Handle desktop shortcut controls
|
|
576
625
|
if (args.includes("--desktop-shortcut")) {
|
|
577
626
|
const { installDesktopShortcut, getDesktopShortcutMethodName } =
|
|
@@ -721,15 +770,23 @@ async function main() {
|
|
|
721
770
|
);
|
|
722
771
|
}
|
|
723
772
|
|
|
773
|
+
// Auto-start sentinel in daemon mode when Telegram credentials are available
|
|
774
|
+
const hasTelegramCreds = !!(
|
|
775
|
+
(process.env.TELEGRAM_BOT_TOKEN || readEnvCredentials().TELEGRAM_BOT_TOKEN) &&
|
|
776
|
+
(process.env.TELEGRAM_CHAT_ID || readEnvCredentials().TELEGRAM_CHAT_ID)
|
|
777
|
+
);
|
|
724
778
|
const sentinelRequested =
|
|
725
779
|
args.includes("--sentinel") ||
|
|
726
|
-
parseBoolEnv(process.env.BOSUN_SENTINEL_AUTO_START, false)
|
|
780
|
+
parseBoolEnv(process.env.BOSUN_SENTINEL_AUTO_START, false) ||
|
|
781
|
+
(IS_DAEMON_CHILD && hasTelegramCreds);
|
|
727
782
|
if (sentinelRequested) {
|
|
728
783
|
const sentinel = await ensureSentinelRunning({ quiet: false });
|
|
729
784
|
if (!sentinel.ok) {
|
|
730
785
|
const mode = args.includes("--sentinel")
|
|
731
786
|
? "requested by --sentinel"
|
|
732
|
-
:
|
|
787
|
+
: IS_DAEMON_CHILD && hasTelegramCreds
|
|
788
|
+
? "auto-started in daemon mode (Telegram credentials detected)"
|
|
789
|
+
: "requested by BOSUN_SENTINEL_AUTO_START";
|
|
733
790
|
const strictSentinel = parseBoolEnv(
|
|
734
791
|
process.env.BOSUN_SENTINEL_STRICT,
|
|
735
792
|
false,
|
package/codex-config.mjs
CHANGED
|
@@ -312,6 +312,51 @@ export function hasSandboxPermissions(toml) {
|
|
|
312
312
|
return /^sandbox_permissions\s*=/m.test(toml);
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
+
function stripSandboxPermissions(toml) {
|
|
316
|
+
let next = toml.replace(
|
|
317
|
+
/^\s*#\s*Sandbox permissions.*(?:\r?\n)?/gim,
|
|
318
|
+
"",
|
|
319
|
+
);
|
|
320
|
+
next = next.replace(/^\s*sandbox_permissions\s*=.*(?:\r?\n)?/gim, "");
|
|
321
|
+
return next;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function extractSandboxPermissionsValue(toml) {
|
|
325
|
+
const match = toml.match(/^\s*sandbox_permissions\s*=\s*(.+)$/m);
|
|
326
|
+
if (!match) return "";
|
|
327
|
+
const raw = String(match[1] || "").split("#")[0].trim();
|
|
328
|
+
if (!raw) return "";
|
|
329
|
+
if (raw.startsWith("[")) {
|
|
330
|
+
const values = parseTomlArrayLiteral(raw);
|
|
331
|
+
return values.join(",");
|
|
332
|
+
}
|
|
333
|
+
const quoted = raw.match(/^"(.*)"$/) || raw.match(/^'(.*)'$/);
|
|
334
|
+
if (quoted) return quoted[1];
|
|
335
|
+
return raw;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function insertTopLevelSandboxPermissions(toml, permValue) {
|
|
339
|
+
const block = buildSandboxPermissions(permValue).trim();
|
|
340
|
+
const tableIdx = toml.search(/^\s*\[/m);
|
|
341
|
+
if (tableIdx === -1) {
|
|
342
|
+
return `${toml.trimEnd()}\n\n${block}\n`;
|
|
343
|
+
}
|
|
344
|
+
const head = toml.slice(0, tableIdx).trimEnd();
|
|
345
|
+
const tail = toml.slice(tableIdx).trimStart();
|
|
346
|
+
return `${head}\n\n${block}\n\n${tail}`;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function ensureTopLevelSandboxPermissions(toml, envValue) {
|
|
350
|
+
const existingValue = extractSandboxPermissionsValue(toml);
|
|
351
|
+
const permValue = envValue || existingValue || "disk-full-write-access";
|
|
352
|
+
const stripped = stripSandboxPermissions(toml);
|
|
353
|
+
const updated = insertTopLevelSandboxPermissions(stripped, permValue);
|
|
354
|
+
return {
|
|
355
|
+
toml: updated,
|
|
356
|
+
changed: updated !== toml,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
315
360
|
/**
|
|
316
361
|
* Build a [features] block with the recommended flags.
|
|
317
362
|
* Reads environment overrides: set CODEX_FEATURES_<NAME>=false to disable.
|
|
@@ -407,16 +452,18 @@ export function ensureFeatureFlags(toml, envOverrides = process.env) {
|
|
|
407
452
|
|
|
408
453
|
/**
|
|
409
454
|
* Build the sandbox_permissions top-level key.
|
|
410
|
-
* Default:
|
|
455
|
+
* Default: "disk-full-write-access" for agentic workloads.
|
|
456
|
+
*
|
|
457
|
+
* Codex CLI expects sandbox_permissions as a plain string, NOT an array.
|
|
411
458
|
*
|
|
412
459
|
* @param {string} [envValue] CODEX_SANDBOX_PERMISSIONS env var value
|
|
413
460
|
* @returns {string} TOML line(s)
|
|
414
461
|
*/
|
|
415
462
|
export function buildSandboxPermissions(envValue) {
|
|
416
|
-
const
|
|
417
|
-
? envValue.split(",").map((s) =>
|
|
418
|
-
:
|
|
419
|
-
return `\n# Sandbox permissions (added by bosun)\nsandbox_permissions =
|
|
463
|
+
const perm = envValue
|
|
464
|
+
? envValue.split(",").map((s) => s.trim()).filter(Boolean).join(",")
|
|
465
|
+
: "disk-full-write-access";
|
|
466
|
+
return `\n# Sandbox permissions (added by bosun)\nsandbox_permissions = "${perm}"\n`;
|
|
420
467
|
}
|
|
421
468
|
|
|
422
469
|
function parseTomlArrayLiteral(raw) {
|
|
@@ -1289,10 +1336,13 @@ export function ensureCodexConfig({
|
|
|
1289
1336
|
|
|
1290
1337
|
// ── 1e. Ensure sandbox permissions ────────────────────────
|
|
1291
1338
|
|
|
1292
|
-
|
|
1339
|
+
{
|
|
1293
1340
|
const envPerms = env.CODEX_SANDBOX_PERMISSIONS || "";
|
|
1294
|
-
|
|
1295
|
-
|
|
1341
|
+
const ensured = ensureTopLevelSandboxPermissions(toml, envPerms);
|
|
1342
|
+
if (ensured.changed) {
|
|
1343
|
+
toml = ensured.toml;
|
|
1344
|
+
result.sandboxAdded = true;
|
|
1345
|
+
}
|
|
1296
1346
|
}
|
|
1297
1347
|
|
|
1298
1348
|
// ── 1f. Ensure sandbox workspace-write defaults ───────────
|
package/codex-model-profiles.mjs
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
|
|
1
5
|
const DEFAULT_ACTIVE_PROFILE = "xl";
|
|
2
6
|
const DEFAULT_SUBAGENT_PROFILE = "m";
|
|
3
7
|
|
|
@@ -63,6 +67,19 @@ function profileRecord(env, profileName, globalProvider) {
|
|
|
63
67
|
};
|
|
64
68
|
}
|
|
65
69
|
|
|
70
|
+
function readCodexConfigTopLevelModel() {
|
|
71
|
+
try {
|
|
72
|
+
const configPath = resolve(homedir(), ".codex", "config.toml");
|
|
73
|
+
if (!existsSync(configPath)) return "";
|
|
74
|
+
const content = readFileSync(configPath, "utf8");
|
|
75
|
+
const head = content.split(/\n\[/)[0] || "";
|
|
76
|
+
const match = head.match(/^\s*model\s*=\s*"([^"]+)"/m);
|
|
77
|
+
return match ? match[1].trim() : "";
|
|
78
|
+
} catch {
|
|
79
|
+
return "";
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
66
83
|
/**
|
|
67
84
|
* Resolve codex model/provider profile configuration from env vars.
|
|
68
85
|
* Applies active profile values onto runtime env keys (`CODEX_MODEL`,
|
|
@@ -86,6 +103,8 @@ export function resolveCodexProfileRuntime(envInput = process.env) {
|
|
|
86
103
|
|
|
87
104
|
const env = { ...sourceEnv };
|
|
88
105
|
|
|
106
|
+
const configModel = readCodexConfigTopLevelModel();
|
|
107
|
+
|
|
89
108
|
if (active.model) {
|
|
90
109
|
env.CODEX_MODEL = active.model;
|
|
91
110
|
}
|
|
@@ -96,6 +115,21 @@ export function resolveCodexProfileRuntime(envInput = process.env) {
|
|
|
96
115
|
const profileApiKey = active.apiKey;
|
|
97
116
|
const resolvedProvider = active.provider || globalProvider;
|
|
98
117
|
|
|
118
|
+
// Azure deployments often differ from default model names.
|
|
119
|
+
// If the env is using Azure and the model is still the default,
|
|
120
|
+
// prefer the top-level ~/.codex/config.toml model when present.
|
|
121
|
+
const activeModelExplicit =
|
|
122
|
+
Boolean(readProfileField(sourceEnv, activeProfile, "MODEL")) ||
|
|
123
|
+
Boolean(clean(sourceEnv.CODEX_MODEL));
|
|
124
|
+
if (
|
|
125
|
+
resolvedProvider === "azure" &&
|
|
126
|
+
configModel &&
|
|
127
|
+
(!activeModelExplicit || clean(env.CODEX_MODEL) === "gpt-5.3-codex")
|
|
128
|
+
) {
|
|
129
|
+
env.CODEX_MODEL = configModel;
|
|
130
|
+
active.model = configModel;
|
|
131
|
+
}
|
|
132
|
+
|
|
99
133
|
if (profileApiKey) {
|
|
100
134
|
if (resolvedProvider === "azure") {
|
|
101
135
|
env.AZURE_OPENAI_API_KEY = profileApiKey;
|
package/config.mjs
CHANGED
|
@@ -265,17 +265,33 @@ function isEnvEnabled(value, defaultValue = false) {
|
|
|
265
265
|
|
|
266
266
|
// ── Git helpers ──────────────────────────────────────────────────────────────
|
|
267
267
|
|
|
268
|
-
function detectRepoSlug() {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
268
|
+
function detectRepoSlug(repoRoot = "") {
|
|
269
|
+
const tryResolve = (cwd) => {
|
|
270
|
+
try {
|
|
271
|
+
const remote = execSync("git remote get-url origin", {
|
|
272
|
+
cwd,
|
|
273
|
+
encoding: "utf8",
|
|
274
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
275
|
+
}).trim();
|
|
276
|
+
const match = remote.match(/github\.com[/:]([^/]+\/[^/.]+)/);
|
|
277
|
+
return match ? match[1] : null;
|
|
278
|
+
} catch {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// First try current working directory
|
|
284
|
+
const direct = tryResolve(process.cwd());
|
|
285
|
+
if (direct) return direct;
|
|
286
|
+
|
|
287
|
+
// Fall back to detected repo root if provided (or detectable)
|
|
288
|
+
const root = repoRoot || detectRepoRoot();
|
|
289
|
+
if (root) {
|
|
290
|
+
const viaRoot = tryResolve(root);
|
|
291
|
+
if (viaRoot) return viaRoot;
|
|
278
292
|
}
|
|
293
|
+
|
|
294
|
+
return null;
|
|
279
295
|
}
|
|
280
296
|
|
|
281
297
|
function detectRepoRoot() {
|
package/desktop/launch.mjs
CHANGED
|
@@ -15,6 +15,8 @@ const chromeSandbox = resolve(
|
|
|
15
15
|
"chrome-sandbox",
|
|
16
16
|
);
|
|
17
17
|
|
|
18
|
+
process.title = "bosun-desktop-launcher";
|
|
19
|
+
|
|
18
20
|
function shouldDisableSandbox() {
|
|
19
21
|
if (process.env.BOSUN_DESKTOP_DISABLE_SANDBOX === "1") return true;
|
|
20
22
|
if (process.platform !== "linux") return false;
|
package/desktop/main.mjs
CHANGED
package/github-reconciler.mjs
CHANGED
|
@@ -216,6 +216,9 @@ export class GitHubReconciler {
|
|
|
216
216
|
if (backend !== "github") {
|
|
217
217
|
return { status: "skipped", reason: `backend=${backend || "unknown"}` };
|
|
218
218
|
}
|
|
219
|
+
if (!this.repoSlug || this.repoSlug === "unknown/unknown") {
|
|
220
|
+
return { status: "skipped", reason: "missing-repo" };
|
|
221
|
+
}
|
|
219
222
|
|
|
220
223
|
const summary = {
|
|
221
224
|
status: "ok",
|
|
@@ -350,6 +353,10 @@ export class GitHubReconciler {
|
|
|
350
353
|
start() {
|
|
351
354
|
if (this.running) return this;
|
|
352
355
|
this.running = true;
|
|
356
|
+
if (!this.repoSlug || this.repoSlug === "unknown/unknown") {
|
|
357
|
+
console.warn(`${TAG} disabled (missing repo slug)`);
|
|
358
|
+
return this;
|
|
359
|
+
}
|
|
353
360
|
console.log(
|
|
354
361
|
`${TAG} started (repo=${this.repoSlug}, interval=${this.intervalMs}ms, lookback=${this.mergedLookbackHours}h)`,
|
|
355
362
|
);
|
package/hook-profiles.mjs
CHANGED
|
@@ -552,6 +552,10 @@ function buildDisableEnv(hookConfig) {
|
|
|
552
552
|
};
|
|
553
553
|
}
|
|
554
554
|
|
|
555
|
+
function normalizePathForOutput(value) {
|
|
556
|
+
return String(value || "").replace(/\\/g, "/");
|
|
557
|
+
}
|
|
558
|
+
|
|
555
559
|
export function scaffoldAgentHookFiles(repoRoot, options = {}) {
|
|
556
560
|
const root = resolve(repoRoot || process.cwd());
|
|
557
561
|
const targets = normalizeHookTargets(options.targets);
|
|
@@ -585,13 +589,13 @@ export function scaffoldAgentHookFiles(repoRoot, options = {}) {
|
|
|
585
589
|
const codexPath = resolve(root, ".codex", "hooks.json");
|
|
586
590
|
const existedBefore = existsSync(codexPath);
|
|
587
591
|
if (existedBefore && !overwriteExisting) {
|
|
588
|
-
result.skipped.push(relative(root, codexPath));
|
|
592
|
+
result.skipped.push(normalizePathForOutput(relative(root, codexPath)));
|
|
589
593
|
} else {
|
|
590
594
|
writeJson(codexPath, codexHookConfig);
|
|
591
595
|
if (existedBefore) {
|
|
592
|
-
result.updated.push(relative(root, codexPath));
|
|
596
|
+
result.updated.push(normalizePathForOutput(relative(root, codexPath)));
|
|
593
597
|
} else {
|
|
594
|
-
result.written.push(relative(root, codexPath));
|
|
598
|
+
result.written.push(normalizePathForOutput(relative(root, codexPath)));
|
|
595
599
|
}
|
|
596
600
|
}
|
|
597
601
|
}
|
|
@@ -610,18 +614,18 @@ export function scaffoldAgentHookFiles(repoRoot, options = {}) {
|
|
|
610
614
|
hasLegacyBridgeInCopilotConfig(existingCopilot);
|
|
611
615
|
|
|
612
616
|
if (existedBefore && !overwriteExisting && !forceLegacyMigration) {
|
|
613
|
-
result.skipped.push(relative(root, copilotPath));
|
|
617
|
+
result.skipped.push(normalizePathForOutput(relative(root, copilotPath)));
|
|
614
618
|
} else {
|
|
615
619
|
writeJson(copilotPath, config);
|
|
616
620
|
if (existedBefore) {
|
|
617
|
-
result.updated.push(relative(root, copilotPath));
|
|
621
|
+
result.updated.push(normalizePathForOutput(relative(root, copilotPath)));
|
|
618
622
|
if (forceLegacyMigration) {
|
|
619
623
|
result.warnings.push(
|
|
620
|
-
`${relative(root, copilotPath)} contained legacy bridge path and was auto-updated`,
|
|
624
|
+
`${normalizePathForOutput(relative(root, copilotPath))} contained legacy bridge path and was auto-updated`,
|
|
621
625
|
);
|
|
622
626
|
}
|
|
623
627
|
} else {
|
|
624
|
-
result.written.push(relative(root, copilotPath));
|
|
628
|
+
result.written.push(normalizePathForOutput(relative(root, copilotPath)));
|
|
625
629
|
}
|
|
626
630
|
}
|
|
627
631
|
}
|
|
@@ -634,15 +638,15 @@ export function scaffoldAgentHookFiles(repoRoot, options = {}) {
|
|
|
634
638
|
|
|
635
639
|
if (existing === null && existsSync(claudePath)) {
|
|
636
640
|
result.warnings.push(
|
|
637
|
-
`${relative(root, claudePath)} exists but is not valid JSON; skipped`,
|
|
641
|
+
`${normalizePathForOutput(relative(root, claudePath))} exists but is not valid JSON; skipped`,
|
|
638
642
|
);
|
|
639
643
|
} else {
|
|
640
644
|
const merged = mergeClaudeSettings(existing, generated);
|
|
641
645
|
writeJson(claudePath, merged);
|
|
642
646
|
if (existedBefore) {
|
|
643
|
-
result.updated.push(relative(root, claudePath));
|
|
647
|
+
result.updated.push(normalizePathForOutput(relative(root, claudePath)));
|
|
644
648
|
} else {
|
|
645
|
-
result.written.push(relative(root, claudePath));
|
|
649
|
+
result.written.push(normalizePathForOutput(relative(root, claudePath)));
|
|
646
650
|
}
|
|
647
651
|
}
|
|
648
652
|
}
|
package/monitor.mjs
CHANGED
|
@@ -216,6 +216,7 @@ import {
|
|
|
216
216
|
setKanbanBackend,
|
|
217
217
|
listTasks as listKanbanTasks,
|
|
218
218
|
updateTaskStatus as updateKanbanTaskStatus,
|
|
219
|
+
updateTask as updateKanbanTask,
|
|
219
220
|
listProjects as listKanbanProjects,
|
|
220
221
|
createTask as createKanbanTask,
|
|
221
222
|
} from "./kanban-adapter.mjs";
|
|
@@ -5863,7 +5864,7 @@ const dependabotMergeAttempted = new Set();
|
|
|
5863
5864
|
*/
|
|
5864
5865
|
async function checkAndMergeDependabotPRs() {
|
|
5865
5866
|
if (!dependabotAutoMerge) return;
|
|
5866
|
-
if (!repoSlug) {
|
|
5867
|
+
if (!repoSlug || repoSlug === "unknown/unknown") {
|
|
5867
5868
|
console.warn("[dependabot] auto-merge disabled — no repo slug configured");
|
|
5868
5869
|
return;
|
|
5869
5870
|
}
|
|
@@ -9026,8 +9027,20 @@ async function triggerTaskPlannerViaKanban(
|
|
|
9026
9027
|
`[monitor] task planner task already exists in backlog — skipping: "${existingPlanner.title}" (${existingPlanner.id})`,
|
|
9027
9028
|
);
|
|
9028
9029
|
// Best-effort: keep backlog task aligned with current requirements
|
|
9029
|
-
//
|
|
9030
|
-
|
|
9030
|
+
// Update description if the backend supports it, so the agent gets fresh context
|
|
9031
|
+
try {
|
|
9032
|
+
await updateKanbanTask(existingPlanner.id, {
|
|
9033
|
+
description: desiredDescription,
|
|
9034
|
+
});
|
|
9035
|
+
console.log(
|
|
9036
|
+
`[monitor] updated description of existing planner task: "${existingPlanner.title}" (${existingPlanner.id})`,
|
|
9037
|
+
);
|
|
9038
|
+
} catch (updateErr) {
|
|
9039
|
+
// Not all backends support partial description updates — log and continue
|
|
9040
|
+
console.log(
|
|
9041
|
+
`[monitor] could not update existing planner task description (${updateErr.message || updateErr}) — skipping`,
|
|
9042
|
+
);
|
|
9043
|
+
}
|
|
9031
9044
|
|
|
9032
9045
|
const taskUrl = buildTaskUrl(existingPlanner, projectId);
|
|
9033
9046
|
if (notify) {
|
|
@@ -12035,6 +12048,10 @@ function restartGitHubReconciler() {
|
|
|
12035
12048
|
: "") ||
|
|
12036
12049
|
repoSlug ||
|
|
12037
12050
|
"unknown/unknown";
|
|
12051
|
+
if (!repo || repo === "unknown/unknown") {
|
|
12052
|
+
console.warn("[gh-reconciler] disabled — missing repo slug");
|
|
12053
|
+
return;
|
|
12054
|
+
}
|
|
12038
12055
|
|
|
12039
12056
|
ghReconciler = startGitHubReconciler({
|
|
12040
12057
|
repoSlug: repo,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bosun",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.7",
|
|
4
4
|
"description": "AI-powered orchestrator supervisor — manages AI agent executors with failover, auto-restarts on failure, analyzes crashes with Codex SDK, creates PRs via Vibe-Kanban API, and sends Telegram notifications. Supports N executors with weighted distribution, multi-repo projects, and auto-setup.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache 2.0",
|
|
@@ -151,6 +151,7 @@
|
|
|
151
151
|
"prepublish-check.mjs",
|
|
152
152
|
"presence.mjs",
|
|
153
153
|
"primary-agent.mjs",
|
|
154
|
+
"repo-config.mjs",
|
|
154
155
|
"repo-root.mjs",
|
|
155
156
|
"restart-controller.mjs",
|
|
156
157
|
"review-agent.mjs",
|