bosun 0.26.3

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.
Files changed (122) hide show
  1. package/.env.example +918 -0
  2. package/LICENSE +190 -0
  3. package/README.md +98 -0
  4. package/agent-endpoint.mjs +918 -0
  5. package/agent-hook-bridge.mjs +230 -0
  6. package/agent-hooks.mjs +1188 -0
  7. package/agent-pool.mjs +2403 -0
  8. package/agent-prompts.mjs +689 -0
  9. package/agent-sdk.mjs +141 -0
  10. package/anomaly-detector.mjs +1195 -0
  11. package/autofix.mjs +1294 -0
  12. package/bosun.config.example.json +115 -0
  13. package/bosun.schema.json +465 -0
  14. package/claude-shell.mjs +708 -0
  15. package/cli.mjs +1028 -0
  16. package/codex-config.mjs +1274 -0
  17. package/codex-model-profiles.mjs +135 -0
  18. package/codex-shell.mjs +762 -0
  19. package/compat.mjs +286 -0
  20. package/config-doctor.mjs +613 -0
  21. package/config.mjs +1724 -0
  22. package/conflict-resolver.mjs +248 -0
  23. package/container-runner.mjs +450 -0
  24. package/copilot-shell.mjs +827 -0
  25. package/daemon-restart-policy.mjs +56 -0
  26. package/diff-stats.mjs +282 -0
  27. package/error-detector.mjs +829 -0
  28. package/fetch-runtime.mjs +34 -0
  29. package/fleet-coordinator.mjs +838 -0
  30. package/get-telegram-chat-id.mjs +71 -0
  31. package/git-safety.mjs +170 -0
  32. package/github-reconciler.mjs +403 -0
  33. package/hook-profiles.mjs +651 -0
  34. package/kanban-adapter.mjs +4491 -0
  35. package/lib/logger.mjs +645 -0
  36. package/maintenance.mjs +828 -0
  37. package/merge-strategy.mjs +1171 -0
  38. package/monitor.mjs +12237 -0
  39. package/package.json +209 -0
  40. package/postinstall.mjs +187 -0
  41. package/pr-cleanup-daemon.mjs +978 -0
  42. package/preflight.mjs +408 -0
  43. package/prepublish-check.mjs +90 -0
  44. package/presence.mjs +328 -0
  45. package/primary-agent.mjs +290 -0
  46. package/publish.mjs +241 -0
  47. package/repo-root.mjs +29 -0
  48. package/restart-controller.mjs +100 -0
  49. package/review-agent.mjs +557 -0
  50. package/rotate-agent-logs.sh +133 -0
  51. package/sdk-conflict-resolver.mjs +973 -0
  52. package/session-tracker.mjs +880 -0
  53. package/setup.mjs +3946 -0
  54. package/shared-knowledge.mjs +410 -0
  55. package/shared-state-manager.mjs +841 -0
  56. package/shared-workspace-cli.mjs +199 -0
  57. package/shared-workspace-registry.mjs +537 -0
  58. package/shared-workspaces.json +18 -0
  59. package/startup-service.mjs +1070 -0
  60. package/sync-engine.mjs +1063 -0
  61. package/task-archiver.mjs +801 -0
  62. package/task-assessment.mjs +550 -0
  63. package/task-claims.mjs +924 -0
  64. package/task-complexity.mjs +581 -0
  65. package/task-executor.mjs +5111 -0
  66. package/task-store.mjs +753 -0
  67. package/telegram-bot.mjs +9683 -0
  68. package/telegram-sentinel.mjs +2010 -0
  69. package/ui/app.js +867 -0
  70. package/ui/app.legacy.js +1464 -0
  71. package/ui/app.monolith.js +2488 -0
  72. package/ui/components/charts.js +226 -0
  73. package/ui/components/chat-view.js +567 -0
  74. package/ui/components/command-palette.js +587 -0
  75. package/ui/components/diff-viewer.js +190 -0
  76. package/ui/components/forms.js +357 -0
  77. package/ui/components/kanban-board.js +451 -0
  78. package/ui/components/session-list.js +305 -0
  79. package/ui/components/shared.js +525 -0
  80. package/ui/demo.html +640 -0
  81. package/ui/index.html +70 -0
  82. package/ui/modules/api.js +297 -0
  83. package/ui/modules/icons.js +461 -0
  84. package/ui/modules/router.js +81 -0
  85. package/ui/modules/settings-schema.js +261 -0
  86. package/ui/modules/state.js +679 -0
  87. package/ui/modules/telegram.js +331 -0
  88. package/ui/modules/utils.js +270 -0
  89. package/ui/styles/animations.css +140 -0
  90. package/ui/styles/base.css +98 -0
  91. package/ui/styles/components.css +2032 -0
  92. package/ui/styles/kanban.css +286 -0
  93. package/ui/styles/layout.css +810 -0
  94. package/ui/styles/sessions.css +841 -0
  95. package/ui/styles/variables.css +188 -0
  96. package/ui/styles.css +141 -0
  97. package/ui/styles.monolith.css +1046 -0
  98. package/ui/tabs/agents.js +1417 -0
  99. package/ui/tabs/chat.js +75 -0
  100. package/ui/tabs/control.js +892 -0
  101. package/ui/tabs/dashboard.js +515 -0
  102. package/ui/tabs/infra.js +537 -0
  103. package/ui/tabs/logs.js +783 -0
  104. package/ui/tabs/settings.js +1509 -0
  105. package/ui/tabs/tasks.js +1385 -0
  106. package/ui-server.mjs +4084 -0
  107. package/update-check.mjs +471 -0
  108. package/utils.mjs +172 -0
  109. package/ve-kanban.mjs +654 -0
  110. package/ve-kanban.ps1 +1365 -0
  111. package/ve-kanban.sh +18 -0
  112. package/ve-orchestrator.mjs +340 -0
  113. package/ve-orchestrator.ps1 +6546 -0
  114. package/ve-orchestrator.sh +18 -0
  115. package/vibe-kanban-wrapper.mjs +41 -0
  116. package/vk-error-resolver.mjs +470 -0
  117. package/vk-log-stream.mjs +914 -0
  118. package/whatsapp-channel.mjs +520 -0
  119. package/workspace-monitor.mjs +581 -0
  120. package/workspace-reaper.mjs +405 -0
  121. package/workspace-registry.mjs +238 -0
  122. package/worktree-manager.mjs +1266 -0
package/publish.mjs ADDED
@@ -0,0 +1,241 @@
1
+ #!/usr/bin/env node
2
+ //
3
+ // Usage:
4
+ // node publish.mjs # publish at current version (prompts login if no token)
5
+ // node publish.mjs --bump minor # 0.25.0 → 0.25.1 (patch digit)
6
+ // node publish.mjs --bump major # 0.25.0 → 0.26.0 (minor digit)
7
+ // node publish.mjs --bump minor --dry-run
8
+ //
9
+ // To set an automation token (Linux/macOS bash — NOT setx/PowerShell):
10
+ // export NPM_ACCESS_TOKEN=npm_xxxx && node publish.mjs --bump minor
11
+ // # or inline:
12
+ // NPM_ACCESS_TOKEN=npm_xxxx npm run publish:minor
13
+ //
14
+ // Terminology matches the bosun convention (not semver):
15
+ // minor = last digit (semver patch)
16
+ // major = middle digit (semver minor)
17
+
18
+ import { spawnSync } from "node:child_process";
19
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
20
+ import { tmpdir } from "node:os";
21
+ import { join, resolve } from "node:path";
22
+ import { fileURLToPath } from "node:url";
23
+
24
+ const SCRIPT_DIR = resolve(fileURLToPath(new URL(".", import.meta.url)));
25
+
26
+ function hasArg(flag) {
27
+ return process.argv.includes(flag);
28
+ }
29
+
30
+ function getRegistryUrl() {
31
+ const raw =
32
+ process.env.NPM_REGISTRY_URL ||
33
+ process.env.npm_config_registry ||
34
+ "https://registry.npmjs.org/";
35
+ const parsed = new URL(raw);
36
+ if (!parsed.pathname.endsWith("/")) {
37
+ parsed.pathname = `${parsed.pathname}/`;
38
+ }
39
+ return parsed.toString();
40
+ }
41
+
42
+ function createEphemeralNpmrc(registryUrl, token) {
43
+ const folder = mkdtempSync(join(tmpdir(), "bosun-npmrc-"));
44
+ const npmrcPath = join(folder, ".npmrc");
45
+ const parsed = new URL(registryUrl);
46
+ const authPath = parsed.pathname || "/";
47
+ const authLine = `//${parsed.host}${authPath}:_authToken=${token}`;
48
+ const content = [
49
+ `registry=${registryUrl}`,
50
+ "always-auth=true",
51
+ authLine,
52
+ ].join("\n");
53
+ writeFileSync(npmrcPath, `${content}\n`, "utf8");
54
+ return { folder, npmrcPath };
55
+ }
56
+
57
+ function run(command, args, env) {
58
+ const result = spawnSync(command, args, {
59
+ stdio: "inherit",
60
+ cwd: SCRIPT_DIR,
61
+ env,
62
+ shell: true,
63
+ });
64
+ if (result.error) {
65
+ console.error(`[publish] Failed to execute ${command}: ${result.error.message}`);
66
+ return 1;
67
+ }
68
+ return result.status ?? 1;
69
+ }
70
+
71
+ function runCheck(label, command, args, env) {
72
+ console.log(`[publish] Running ${label}...`);
73
+ const status = run(command, args, env);
74
+ if (status !== 0) {
75
+ console.error(`[publish] ${label} failed (exit ${status}). Aborting publish.`);
76
+ }
77
+ return status;
78
+ }
79
+
80
+ const NPM_BIN = "npm";
81
+
82
+ /**
83
+ * Bump the version in package.json and sync package-lock.json.
84
+ * type: "minor" → patch digit (0.25.0 → 0.25.1)
85
+ * "major" → middle digit (0.25.0 → 0.26.0)
86
+ * Returns the new version string.
87
+ */
88
+ function bumpVersion(type, dryRun) {
89
+ const pkgPath = resolve(SCRIPT_DIR, "package.json");
90
+ const lockPath = resolve(SCRIPT_DIR, "package-lock.json");
91
+
92
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
93
+ const [maj, mid, pat] = pkg.version.split(".").map(Number);
94
+
95
+ let newVersion;
96
+ if (type === "minor") {
97
+ // minor = patch digit (last number)
98
+ newVersion = `${maj}.${mid}.${pat + 1}`;
99
+ } else if (type === "major") {
100
+ // major = middle digit, reset patch
101
+ newVersion = `${maj}.${mid + 1}.0`;
102
+ } else {
103
+ console.error(
104
+ `[publish] Unknown bump type "${type}". Use "minor" (0.25.0→0.25.1) or "major" (0.25.0→0.26.0).`,
105
+ );
106
+ process.exit(1);
107
+ }
108
+
109
+ console.log(`[publish] Bumping version: ${pkg.version} → ${newVersion}`);
110
+
111
+ if (!dryRun) {
112
+ pkg.version = newVersion;
113
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
114
+
115
+ // Update package-lock.json in-place
116
+ try {
117
+ const lock = JSON.parse(readFileSync(lockPath, "utf8"));
118
+ lock.version = newVersion;
119
+ if (lock.packages?.[""] != null) {
120
+ lock.packages[""].version = newVersion;
121
+ }
122
+ writeFileSync(lockPath, JSON.stringify(lock, null, 2) + "\n", "utf8");
123
+ console.log(
124
+ `[publish] package.json + package-lock.json updated to ${newVersion}`,
125
+ );
126
+ } catch {
127
+ console.warn(
128
+ "[publish] Could not update package-lock.json — regenerating...",
129
+ );
130
+ spawnSync(NPM_BIN, ["install", "--package-lock-only", "--ignore-scripts"], {
131
+ stdio: "inherit",
132
+ cwd: SCRIPT_DIR,
133
+ shell: true,
134
+ });
135
+ }
136
+ } else {
137
+ console.log(
138
+ `[publish] dry-run: would write version ${newVersion} to package.json & package-lock.json`,
139
+ );
140
+ }
141
+
142
+ return newVersion;
143
+ }
144
+
145
+ function getBumpArg() {
146
+ const idx = process.argv.indexOf("--bump");
147
+ if (idx !== -1 && process.argv[idx + 1]) return process.argv[idx + 1];
148
+ const flag = process.argv.find((a) => a.startsWith("--bump="));
149
+ if (flag) return flag.slice(7);
150
+ return null;
151
+ }
152
+
153
+ function main() {
154
+ const bumpType = getBumpArg();
155
+ const dryRun = hasArg("--dry-run");
156
+ if (bumpType) bumpVersion(bumpType, dryRun);
157
+
158
+ const tag = process.env.NPM_PUBLISH_TAG || "latest";
159
+ const otp = process.env.NPM_OTP || "";
160
+ const access = process.env.NPM_PUBLISH_ACCESS || "public";
161
+ const registry = getRegistryUrl();
162
+ const token = process.env.NPM_ACCESS_TOKEN || process.env.NODE_AUTH_TOKEN || "";
163
+
164
+ if (!dryRun && !token) {
165
+ console.log(
166
+ "[publish] No NPM_ACCESS_TOKEN found — npm will prompt for login.",
167
+ );
168
+ } else if (!dryRun && token) {
169
+ console.log("[publish] Token found — using NPM_ACCESS_TOKEN for auth.");
170
+ }
171
+
172
+ console.log(
173
+ `[publish] Running validation gates (${dryRun ? "dry-run" : "publish"})...`,
174
+ );
175
+ const prepushStatus = runCheck(
176
+ "pre-push checks",
177
+ NPM_BIN,
178
+ ["run", "prepush:check"],
179
+ process.env,
180
+ );
181
+ if (prepushStatus !== 0) {
182
+ process.exit(prepushStatus);
183
+ }
184
+
185
+ const prepublishStatus = runCheck(
186
+ "prepublish checks",
187
+ NPM_BIN,
188
+ ["run", "prepublishOnly"],
189
+ process.env,
190
+ );
191
+ if (prepublishStatus !== 0) {
192
+ process.exit(prepublishStatus);
193
+ }
194
+
195
+ let tempConfig = null;
196
+ try {
197
+ const env = { ...process.env };
198
+ if (!dryRun && token) {
199
+ tempConfig = createEphemeralNpmrc(registry, token);
200
+ env.NPM_CONFIG_USERCONFIG = tempConfig.npmrcPath;
201
+ env.NODE_AUTH_TOKEN = token;
202
+ }
203
+
204
+ const publishArgs = [
205
+ "publish",
206
+ "--registry",
207
+ registry,
208
+ "--access",
209
+ access,
210
+ "--tag",
211
+ tag,
212
+ ];
213
+
214
+ if (dryRun) {
215
+ publishArgs.push("--dry-run");
216
+ }
217
+ if (otp) {
218
+ publishArgs.push("--otp", otp);
219
+ }
220
+
221
+ console.log(
222
+ `[publish] npm ${publishArgs.join(" ")} (token via env/userconfig, redacted)`,
223
+ );
224
+ const status = run(NPM_BIN, publishArgs, env);
225
+ if (status === 0 && !dryRun) {
226
+ console.log(
227
+ "\n[publish] REMINDER: deprecate the legacy npm package to redirect users:\n" +
228
+ " npm deprecate bosun@'*' \"Renamed to @virtengine/bosun. Install: npm install -g @virtengine/bosun\"\n" +
229
+ " # If a scoped legacy package exists:\n" +
230
+ " npm deprecate @virtengine/bosun@'*' \"Renamed to @virtengine/bosun. Install: npm install -g @virtengine/bosun\"\n",
231
+ );
232
+ }
233
+ process.exit(status);
234
+ } finally {
235
+ if (tempConfig?.folder) {
236
+ rmSync(tempConfig.folder, { recursive: true, force: true });
237
+ }
238
+ }
239
+ }
240
+
241
+ main();
package/repo-root.mjs ADDED
@@ -0,0 +1,29 @@
1
+ import { execSync } from "node:child_process";
2
+ import { resolve } from "node:path";
3
+
4
+ /**
5
+ * Resolve the repo root for bosun.
6
+ *
7
+ * Priority:
8
+ * 1. Explicit REPO_ROOT env var.
9
+ * 2. git rev-parse --show-toplevel (relative to cwd).
10
+ * 3. process.cwd().
11
+ */
12
+ export function resolveRepoRoot(options = {}) {
13
+ const envRoot = process.env.REPO_ROOT;
14
+ if (envRoot) return resolve(envRoot);
15
+
16
+ const cwd = options.cwd || process.cwd();
17
+ try {
18
+ const gitRoot = execSync("git rev-parse --show-toplevel", {
19
+ encoding: "utf8",
20
+ cwd,
21
+ stdio: ["ignore", "pipe", "ignore"],
22
+ }).trim();
23
+ if (gitRoot) return gitRoot;
24
+ } catch {
25
+ // ignore - fall back to cwd
26
+ }
27
+
28
+ return resolve(cwd);
29
+ }
@@ -0,0 +1,100 @@
1
+ const DEFAULT_MUTEX_BACKOFF_STEP_MS = 30_000;
2
+ const DEFAULT_MUTEX_BACKOFF_MAX_MS = 90_000;
3
+ const DEFAULT_MIN_RESTART_INTERVAL_MS = 15_000;
4
+ const DEFAULT_QUICK_EXIT_THRESHOLD_MS = 20_000;
5
+
6
+ export class RestartController {
7
+ constructor(options = {}) {
8
+ this.mutexBackoffStepMs =
9
+ options.mutexBackoffStepMs ?? DEFAULT_MUTEX_BACKOFF_STEP_MS;
10
+ this.mutexBackoffMaxMs =
11
+ options.mutexBackoffMaxMs ?? DEFAULT_MUTEX_BACKOFF_MAX_MS;
12
+ this.minRestartIntervalMs =
13
+ options.minRestartIntervalMs ?? DEFAULT_MIN_RESTART_INTERVAL_MS;
14
+ this.quickExitThresholdMs =
15
+ options.quickExitThresholdMs ?? DEFAULT_QUICK_EXIT_THRESHOLD_MS;
16
+
17
+ this.mutexHeldDetected = false;
18
+ this.mutexBackoffMs = 0;
19
+ this.lastProcessStartAt = 0;
20
+ this.consecutiveQuickExits = 0;
21
+ }
22
+
23
+ noteLogLine(line) {
24
+ if (!line) return;
25
+ if (
26
+ line.includes("Another orchestrator instance is already running") ||
27
+ line.includes("mutex held")
28
+ ) {
29
+ this.mutexHeldDetected = true;
30
+ }
31
+ }
32
+
33
+ noteProcessStarted(nowMs = Date.now()) {
34
+ this.mutexHeldDetected = false;
35
+ this.lastProcessStartAt = nowMs;
36
+ }
37
+
38
+ getMinRestartDelay(nowMs = Date.now()) {
39
+ if (this.lastProcessStartAt <= 0) return 0;
40
+ const sinceLast = nowMs - this.lastProcessStartAt;
41
+ if (sinceLast < this.minRestartIntervalMs) {
42
+ return this.minRestartIntervalMs - sinceLast;
43
+ }
44
+ return 0;
45
+ }
46
+
47
+ shouldSuppressRestart(reason) {
48
+ return reason === "file-change" && this.mutexBackoffMs > 0;
49
+ }
50
+
51
+ resetBackoff() {
52
+ this.mutexBackoffMs = 0;
53
+ }
54
+
55
+ getRestartDelay() {
56
+ return this.mutexBackoffMs;
57
+ }
58
+
59
+ recordExit(runDurationMs, isMutexHeld) {
60
+ const hadBackoff = this.mutexBackoffMs > 0;
61
+ const isQuickExit = runDurationMs < this.quickExitThresholdMs;
62
+
63
+ if (isQuickExit) {
64
+ this.consecutiveQuickExits += 1;
65
+ } else {
66
+ this.consecutiveQuickExits = 0;
67
+ if (this.mutexBackoffMs > 0) {
68
+ this.mutexBackoffMs = 0;
69
+ }
70
+ }
71
+
72
+ if (isMutexHeld) {
73
+ this.mutexHeldDetected = false;
74
+ this.mutexBackoffMs = Math.min(
75
+ this.mutexBackoffMs + this.mutexBackoffStepMs,
76
+ this.mutexBackoffMaxMs,
77
+ );
78
+ return {
79
+ isMutexHeld: true,
80
+ backoffMs: this.mutexBackoffMs,
81
+ consecutiveQuickExits: this.consecutiveQuickExits,
82
+ backoffReset: hadBackoff && !isQuickExit,
83
+ };
84
+ }
85
+
86
+ return {
87
+ isMutexHeld: false,
88
+ backoffMs: this.mutexBackoffMs,
89
+ consecutiveQuickExits: this.consecutiveQuickExits,
90
+ backoffReset: hadBackoff && !isQuickExit,
91
+ };
92
+ }
93
+ }
94
+
95
+ export const RESTART_DEFAULTS = {
96
+ mutexBackoffStepMs: DEFAULT_MUTEX_BACKOFF_STEP_MS,
97
+ mutexBackoffMaxMs: DEFAULT_MUTEX_BACKOFF_MAX_MS,
98
+ minRestartIntervalMs: DEFAULT_MIN_RESTART_INTERVAL_MS,
99
+ quickExitThresholdMs: DEFAULT_QUICK_EXIT_THRESHOLD_MS,
100
+ };