bosun 0.41.2 → 0.41.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.
- package/.env.example +1 -1
- package/agent/agent-prompt-catalog.mjs +971 -0
- package/agent/agent-prompts.mjs +2 -970
- package/agent/agent-supervisor.mjs +6 -3
- package/agent/autofix-git.mjs +33 -0
- package/agent/autofix-prompts.mjs +151 -0
- package/agent/autofix.mjs +11 -175
- package/agent/bosun-skills.mjs +3 -2
- package/bosun.config.example.json +17 -0
- package/bosun.schema.json +87 -188
- package/cli.mjs +34 -1
- package/config/config-doctor.mjs +5 -250
- package/config/config-file-names.mjs +5 -0
- package/config/config.mjs +89 -493
- package/config/executor-config.mjs +493 -0
- package/config/repo-root.mjs +1 -2
- package/config/workspace-health.mjs +242 -0
- package/git/git-safety.mjs +15 -0
- package/github/github-oauth-portal.mjs +46 -0
- package/infra/library-manager-utils.mjs +22 -0
- package/infra/library-manager-well-known-sources.mjs +578 -0
- package/infra/library-manager.mjs +512 -1030
- package/infra/monitor.mjs +28 -9
- package/infra/session-tracker.mjs +10 -7
- package/kanban/kanban-adapter.mjs +17 -1
- package/lib/codebase-audit-manifests.mjs +117 -0
- package/lib/codebase-audit.mjs +18 -115
- package/package.json +18 -3
- package/server/ui-server.mjs +1194 -79
- package/shell/codex-config-file.mjs +178 -0
- package/shell/codex-config.mjs +538 -575
- package/task/task-cli.mjs +54 -3
- package/task/task-executor.mjs +143 -13
- package/task/task-store.mjs +409 -1
- package/telegram/telegram-bot.mjs +127 -0
- package/tools/apply-pr-suggestions.mjs +401 -0
- package/tools/syntax-check.mjs +21 -9
- package/ui/app.js +3 -14
- package/ui/components/kanban-board.js +227 -4
- package/ui/components/session-list.js +85 -5
- package/ui/demo-defaults.js +334 -80
- package/ui/demo.html +155 -0
- package/ui/modules/session-api.js +96 -0
- package/ui/modules/settings-schema.js +1 -2
- package/ui/modules/state.js +21 -3
- package/ui/setup.html +4 -5
- package/ui/styles/components.css +58 -4
- package/ui/tabs/agents.js +12 -15
- package/ui/tabs/control.js +1 -0
- package/ui/tabs/library.js +484 -22
- package/ui/tabs/manual-flows.js +105 -29
- package/ui/tabs/tasks.js +785 -140
- package/ui/tabs/telemetry.js +129 -11
- package/ui/tabs/workflow-canvas-utils.mjs +130 -0
- package/ui/tabs/workflows.js +293 -23
- package/voice/voice-tool-definitions.mjs +757 -0
- package/voice/voice-tools.mjs +34 -778
- package/workflow/manual-flow-audit.mjs +165 -0
- package/workflow/manual-flows.mjs +164 -259
- package/workflow/workflow-engine.mjs +147 -58
- package/workflow/workflow-nodes/definitions.mjs +1207 -0
- package/workflow/workflow-nodes/transforms.mjs +612 -0
- package/workflow/workflow-nodes.mjs +304 -52
- package/workflow/workflow-templates.mjs +313 -191
- package/workflow-templates/_helpers.mjs +154 -0
- package/workflow-templates/agents.mjs +61 -4
- package/workflow-templates/code-quality.mjs +7 -7
- package/workflow-templates/github.mjs +20 -10
- package/workflow-templates/task-batch.mjs +20 -9
- package/workflow-templates/task-lifecycle.mjs +31 -6
- package/workspace/worktree-manager.mjs +277 -3
package/config/config-doctor.mjs
CHANGED
|
@@ -2,16 +2,15 @@ import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
|
2
2
|
import { resolve, dirname, isAbsolute, relative, join } from "node:path";
|
|
3
3
|
import { execSync, spawnSync } from "node:child_process";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { homedir } from "node:os";
|
|
6
5
|
import { ensureTestRuntimeSandbox } from "../infra/test-runtime.mjs";
|
|
7
6
|
import { getWorkflowContract } from "../workflow/workflow-contract.mjs";
|
|
7
|
+
import { CONFIG_FILES } from "./config-file-names.mjs";
|
|
8
|
+
export {
|
|
9
|
+
runWorkspaceHealthCheck,
|
|
10
|
+
formatWorkspaceHealthReport,
|
|
11
|
+
} from "./workspace-health.mjs";
|
|
8
12
|
|
|
9
13
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
const CONFIG_FILES = [
|
|
11
|
-
"bosun.config.json",
|
|
12
|
-
".bosun.json",
|
|
13
|
-
"bosun.json",
|
|
14
|
-
];
|
|
15
14
|
const WORKFLOW_CONTRACT_NODE_TYPES = Object.freeze({
|
|
16
15
|
read: new Set(["read-workflow-contract", "action.read_workflow_contract"]),
|
|
17
16
|
validate: new Set(["workflow-contract-validation", "action.workflow_contract_validation"]),
|
|
@@ -794,247 +793,3 @@ export function formatConfigDoctorReport(result) {
|
|
|
794
793
|
return lines.join("\n");
|
|
795
794
|
}
|
|
796
795
|
|
|
797
|
-
/**
|
|
798
|
-
* Run workspace-specific health checks.
|
|
799
|
-
* Validates workspace repos, git status, writable roots, worktree health.
|
|
800
|
-
* @param {{ configDir?: string, repoRoot?: string }} options
|
|
801
|
-
* @returns {{ ok: boolean, workspaces: Array, issues: { errors: Array, warnings: Array, infos: Array } }}
|
|
802
|
-
*/
|
|
803
|
-
export function runWorkspaceHealthCheck(options = {}) {
|
|
804
|
-
const configDir = options.configDir || process.env.BOSUN_DIR || join(homedir(), "bosun");
|
|
805
|
-
const repoRoot = options.repoRoot || process.cwd();
|
|
806
|
-
const issues = { errors: [], warnings: [], infos: [] };
|
|
807
|
-
const workspaceResults = [];
|
|
808
|
-
|
|
809
|
-
// 1. Check if workspaces are configured
|
|
810
|
-
let workspaces = [];
|
|
811
|
-
try {
|
|
812
|
-
const configPath = join(configDir, "bosun.config.json");
|
|
813
|
-
if (existsSync(configPath)) {
|
|
814
|
-
const config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
815
|
-
workspaces = config.workspaces || [];
|
|
816
|
-
}
|
|
817
|
-
} catch (err) {
|
|
818
|
-
issues.errors.push({
|
|
819
|
-
code: "WS_CONFIG_READ_FAILED",
|
|
820
|
-
message: `Failed to read workspace config: ${err.message}`,
|
|
821
|
-
fix: "Check bosun.config.json is valid JSON",
|
|
822
|
-
});
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
if (workspaces.length === 0) {
|
|
826
|
-
issues.infos.push({
|
|
827
|
-
code: "WS_NONE_CONFIGURED",
|
|
828
|
-
message: "No workspaces configured — agents use developer repo directly.",
|
|
829
|
-
fix: "Run 'bosun --workspace-add <name>' to create a workspace for isolated agent execution.",
|
|
830
|
-
});
|
|
831
|
-
return { ok: true, workspaces: workspaceResults, issues };
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
// 2. Check each workspace
|
|
835
|
-
for (const ws of workspaces) {
|
|
836
|
-
const wsResult = {
|
|
837
|
-
id: ws.id || "unknown",
|
|
838
|
-
name: ws.name || ws.id || "unnamed",
|
|
839
|
-
path: ws.path || "",
|
|
840
|
-
repos: [],
|
|
841
|
-
ok: true,
|
|
842
|
-
};
|
|
843
|
-
|
|
844
|
-
// 2a. Workspace directory exists
|
|
845
|
-
const wsPath = ws.path || join(configDir, "workspaces", ws.id || ws.name);
|
|
846
|
-
if (!existsSync(wsPath)) {
|
|
847
|
-
issues.warnings.push({
|
|
848
|
-
code: "WS_DIR_MISSING",
|
|
849
|
-
message: `Workspace "${wsResult.name}" directory missing: ${wsPath}`,
|
|
850
|
-
fix: `Run 'bosun --setup' or mkdir -p "${wsPath}"`,
|
|
851
|
-
});
|
|
852
|
-
wsResult.ok = false;
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
// 2b. Check repos in workspace
|
|
856
|
-
for (const repo of ws.repos || []) {
|
|
857
|
-
const repoName = repo.name || repo.slug || "unknown";
|
|
858
|
-
const repoPath = join(wsPath, repoName);
|
|
859
|
-
const repoStatus = { name: repoName, path: repoPath, ok: true, issues: [] };
|
|
860
|
-
|
|
861
|
-
if (!existsSync(repoPath)) {
|
|
862
|
-
repoStatus.ok = false;
|
|
863
|
-
repoStatus.issues.push("directory missing");
|
|
864
|
-
issues.errors.push({
|
|
865
|
-
code: "WS_REPO_MISSING",
|
|
866
|
-
message: `Workspace repo "${repoName}" not found at ${repoPath}`,
|
|
867
|
-
fix: `Run 'bosun --workspace-add-repo <url>' or 'bosun --setup' to clone it`,
|
|
868
|
-
});
|
|
869
|
-
} else {
|
|
870
|
-
const gitPath = join(repoPath, ".git");
|
|
871
|
-
if (!existsSync(gitPath)) {
|
|
872
|
-
repoStatus.ok = false;
|
|
873
|
-
repoStatus.issues.push(".git missing");
|
|
874
|
-
issues.errors.push({
|
|
875
|
-
code: "WS_REPO_NO_GIT",
|
|
876
|
-
message: `Workspace repo "${repoName}" has no .git at ${repoPath}`,
|
|
877
|
-
fix: `Clone the repo: git clone <url> "${repoPath}"`,
|
|
878
|
-
});
|
|
879
|
-
} else {
|
|
880
|
-
// Check remote connectivity (quick)
|
|
881
|
-
try {
|
|
882
|
-
const branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
883
|
-
cwd: repoPath, encoding: "utf8", timeout: 5000, stdio: ["pipe", "pipe", "pipe"],
|
|
884
|
-
}).trim();
|
|
885
|
-
repoStatus.branch = branch;
|
|
886
|
-
repoStatus.issues.push(`on branch: ${branch}`);
|
|
887
|
-
} catch {
|
|
888
|
-
repoStatus.issues.push("git status check failed");
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
// Check for uncommitted changes
|
|
892
|
-
try {
|
|
893
|
-
const status = execSync("git status --porcelain", {
|
|
894
|
-
cwd: repoPath, encoding: "utf8", timeout: 5000, stdio: ["pipe", "pipe", "pipe"],
|
|
895
|
-
}).trim();
|
|
896
|
-
if (status) {
|
|
897
|
-
const lines = status.split("\n").length;
|
|
898
|
-
repoStatus.issues.push(`${lines} uncommitted change(s)`);
|
|
899
|
-
issues.infos.push({
|
|
900
|
-
code: "WS_REPO_DIRTY",
|
|
901
|
-
message: `Workspace repo "${repoName}" has ${lines} uncommitted change(s)`,
|
|
902
|
-
fix: null,
|
|
903
|
-
});
|
|
904
|
-
}
|
|
905
|
-
} catch { /* ignore */ }
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
wsResult.repos.push(repoStatus);
|
|
910
|
-
if (!repoStatus.ok) wsResult.ok = false;
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
workspaceResults.push(wsResult);
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
// 3. Check Codex sandbox writable_roots coverage
|
|
917
|
-
const codexConfigPath = join(homedir(), ".codex", "config.toml");
|
|
918
|
-
if (existsSync(codexConfigPath)) {
|
|
919
|
-
try {
|
|
920
|
-
const toml = readFileSync(codexConfigPath, "utf8");
|
|
921
|
-
const rootsMatch = toml.match(/writable_roots\s*=\s*\[([^\]]*)\]/);
|
|
922
|
-
if (rootsMatch) {
|
|
923
|
-
const roots = rootsMatch[1].split(",").map(r => r.trim().replace(/^"|"$/g, "")).filter(Boolean);
|
|
924
|
-
for (const ws of workspaces) {
|
|
925
|
-
const wsPath = ws.path || join(configDir, "workspaces", ws.id || ws.name);
|
|
926
|
-
for (const repo of ws.repos || []) {
|
|
927
|
-
const repoPath = join(wsPath, repo.name || repo.slug || "");
|
|
928
|
-
const gitPath = join(repoPath, ".git");
|
|
929
|
-
if (existsSync(gitPath) && !roots.some(r => gitPath.startsWith(r) || r === gitPath)) {
|
|
930
|
-
issues.warnings.push({
|
|
931
|
-
code: "WS_SANDBOX_MISSING_ROOT",
|
|
932
|
-
message: `Workspace repo .git not in Codex writable_roots: ${gitPath}`,
|
|
933
|
-
fix: `Run 'bosun --setup' to update Codex sandbox config, or add "${gitPath}" to writable_roots in ~/.codex/config.toml`,
|
|
934
|
-
});
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
// Check for phantom/relative writable roots
|
|
940
|
-
for (const root of roots) {
|
|
941
|
-
if (!root.startsWith("/")) {
|
|
942
|
-
issues.warnings.push({
|
|
943
|
-
code: "WS_SANDBOX_RELATIVE_ROOT",
|
|
944
|
-
message: `Relative path in Codex writable_roots: "${root}" — may resolve incorrectly`,
|
|
945
|
-
fix: `Remove "${root}" from writable_roots in ~/.codex/config.toml and run 'bosun --setup'`,
|
|
946
|
-
});
|
|
947
|
-
} else if (!existsSync(root) && root !== "/tmp") {
|
|
948
|
-
issues.infos.push({
|
|
949
|
-
code: "WS_SANDBOX_PHANTOM_ROOT",
|
|
950
|
-
message: `Codex writable_root path does not exist: ${root}`,
|
|
951
|
-
fix: null,
|
|
952
|
-
});
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
} catch { /* ignore parse errors */ }
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
// 4. Check BOSUN_AGENT_REPO_ROOT
|
|
960
|
-
const agentRoot = process.env.BOSUN_AGENT_REPO_ROOT || "";
|
|
961
|
-
if (agentRoot) {
|
|
962
|
-
if (!existsSync(agentRoot)) {
|
|
963
|
-
issues.warnings.push({
|
|
964
|
-
code: "WS_AGENT_ROOT_MISSING",
|
|
965
|
-
message: `BOSUN_AGENT_REPO_ROOT points to non-existent path: ${agentRoot}`,
|
|
966
|
-
fix: "Run 'bosun --setup' to bootstrap workspace repos",
|
|
967
|
-
});
|
|
968
|
-
} else if (!existsSync(join(agentRoot, ".git"))) {
|
|
969
|
-
issues.warnings.push({
|
|
970
|
-
code: "WS_AGENT_ROOT_NO_GIT",
|
|
971
|
-
message: `BOSUN_AGENT_REPO_ROOT has no .git: ${agentRoot}`,
|
|
972
|
-
fix: "Clone the repo at the workspace path or update BOSUN_AGENT_REPO_ROOT",
|
|
973
|
-
});
|
|
974
|
-
} else {
|
|
975
|
-
issues.infos.push({
|
|
976
|
-
code: "WS_AGENT_ROOT_OK",
|
|
977
|
-
message: `Agent repo root: ${agentRoot}`,
|
|
978
|
-
fix: null,
|
|
979
|
-
});
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
const hasErrors = issues.errors.length > 0;
|
|
984
|
-
return { ok: !hasErrors, workspaces: workspaceResults, issues };
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
/**
|
|
988
|
-
* Format workspace health report for CLI output.
|
|
989
|
-
* @param {{ ok: boolean, workspaces: Array, issues: object }} result
|
|
990
|
-
* @returns {string}
|
|
991
|
-
*/
|
|
992
|
-
export function formatWorkspaceHealthReport(result) {
|
|
993
|
-
const lines = [];
|
|
994
|
-
lines.push("=== bosun workspace health ===");
|
|
995
|
-
lines.push(`Status: ${result.ok ? "HEALTHY" : "ISSUES FOUND"}`);
|
|
996
|
-
lines.push("");
|
|
997
|
-
|
|
998
|
-
if (result.workspaces.length === 0) {
|
|
999
|
-
lines.push(" No workspaces configured.");
|
|
1000
|
-
lines.push("");
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
for (const ws of result.workspaces) {
|
|
1004
|
-
const icon = ws.ok ? "✓" : "✗";
|
|
1005
|
-
lines.push(` ${icon} ${ws.name} (${ws.id})`);
|
|
1006
|
-
for (const repo of ws.repos) {
|
|
1007
|
-
const rIcon = repo.ok ? "✓" : "✗";
|
|
1008
|
-
const details = repo.issues.length > 0 ? ` — ${repo.issues.join(", ")}` : "";
|
|
1009
|
-
lines.push(` ${rIcon} ${repo.name}${details}`);
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
lines.push("");
|
|
1013
|
-
|
|
1014
|
-
if (result.issues.errors.length > 0) {
|
|
1015
|
-
lines.push("Errors:");
|
|
1016
|
-
for (const e of result.issues.errors) {
|
|
1017
|
-
lines.push(` ✗ ${e.message}`);
|
|
1018
|
-
if (e.fix) lines.push(` fix: ${e.fix}`);
|
|
1019
|
-
}
|
|
1020
|
-
lines.push("");
|
|
1021
|
-
}
|
|
1022
|
-
if (result.issues.warnings.length > 0) {
|
|
1023
|
-
lines.push("Warnings:");
|
|
1024
|
-
for (const w of result.issues.warnings) {
|
|
1025
|
-
lines.push(` :alert: ${w.message}`);
|
|
1026
|
-
if (w.fix) lines.push(` fix: ${w.fix}`);
|
|
1027
|
-
}
|
|
1028
|
-
lines.push("");
|
|
1029
|
-
}
|
|
1030
|
-
if (result.issues.infos.length > 0) {
|
|
1031
|
-
lines.push("Info:");
|
|
1032
|
-
for (const i of result.issues.infos) {
|
|
1033
|
-
lines.push(` :help: ${i.message}`);
|
|
1034
|
-
}
|
|
1035
|
-
lines.push("");
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
return lines.join("\n");
|
|
1039
|
-
}
|
|
1040
|
-
|