bosun 0.40.6 → 0.40.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bosun",
3
- "version": "0.40.6",
3
+ "version": "0.40.7",
4
4
  "description": "Bosun Autonomous Engineering — manages AI agent executors with failover, extremely powerful workflow builder, and a massive amount of included default workflow templates for autonomous engineering, 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",
@@ -59,11 +59,118 @@ function makeIsolatedGitEnv(extra = {}) {
59
59
  return env;
60
60
  }
61
61
 
62
- function execGitSync(command, options = {}) {
63
- return execSync(command, {
64
- ...options,
65
- env: makeIsolatedGitEnv(options.env),
66
- });
62
+ function resolveGitCandidates(env = process.env) {
63
+ const candidates = [];
64
+ const envGitExe = env?.GIT_EXE || process.env.GIT_EXE;
65
+ if (envGitExe) candidates.push(envGitExe);
66
+ if (process.platform === "win32") {
67
+ candidates.push(
68
+ "C:\\Program Files\\Git\\cmd\\git.exe",
69
+ "C:\\Program Files\\Git\\bin\\git.exe",
70
+ "C:\\Program Files (x86)\\Git\\cmd\\git.exe",
71
+ "C:\\Program Files (x86)\\Git\\bin\\git.exe",
72
+ );
73
+ } else {
74
+ candidates.push(
75
+ "/usr/bin/git",
76
+ "/usr/local/bin/git",
77
+ "/bin/git",
78
+ "/opt/homebrew/bin/git",
79
+ );
80
+ }
81
+
82
+ if (process.platform === "win32") {
83
+ try {
84
+ const whereOutput = execFileSync("where.exe", ["git"], {
85
+ encoding: "utf8",
86
+ env,
87
+ stdio: ["ignore", "pipe", "ignore"],
88
+ windowsHide: true,
89
+ });
90
+ for (const line of String(whereOutput || "").split(/\r?\n/)) {
91
+ const candidate = line.trim();
92
+ if (!candidate) continue;
93
+ candidates.push(candidate);
94
+ }
95
+ } catch {
96
+ /* best-effort */
97
+ }
98
+ }
99
+
100
+ candidates.push("git");
101
+ const deduped = [];
102
+ const seen = new Set();
103
+ for (const candidate of candidates) {
104
+ const key = process.platform === "win32"
105
+ ? String(candidate || "").toLowerCase()
106
+ : String(candidate || "");
107
+ if (!candidate || seen.has(key)) continue;
108
+ seen.add(key);
109
+ deduped.push(candidate);
110
+ }
111
+ return deduped;
112
+ }
113
+
114
+ function buildGitExecutionEnv(baseEnv, gitBinary) {
115
+ if (process.platform !== "win32") return baseEnv;
116
+ const normalizedBinary = String(gitBinary || "").replace(/\//g, "\\");
117
+ if (!normalizedBinary.includes("\\") || !normalizedBinary.toLowerCase().endsWith("\\git.exe")) {
118
+ return baseEnv;
119
+ }
120
+ const env = { ...baseEnv };
121
+ const pathKey = Object.prototype.hasOwnProperty.call(env, "Path")
122
+ ? "Path"
123
+ : "PATH";
124
+ const existing = String(env[pathKey] ?? env.PATH ?? env.Path ?? "");
125
+ const parts = existing
126
+ .split(";")
127
+ .map((part) => part.trim())
128
+ .filter(Boolean);
129
+ const seen = new Set(parts.map((part) => part.toLowerCase()));
130
+ const binaryDir = dirname(normalizedBinary);
131
+ const gitRoot = dirname(binaryDir);
132
+ for (const dir of [
133
+ binaryDir,
134
+ `${gitRoot}\\cmd`,
135
+ `${gitRoot}\\bin`,
136
+ `${gitRoot}\\mingw64\\bin`,
137
+ `${gitRoot}\\usr\\bin`,
138
+ ]) {
139
+ const normalizedDir = String(dir || "").replace(/\//g, "\\");
140
+ if (!normalizedDir || seen.has(normalizedDir.toLowerCase())) continue;
141
+ seen.add(normalizedDir.toLowerCase());
142
+ parts.unshift(normalizedDir);
143
+ }
144
+ env[pathKey] = parts.join(";");
145
+ if (pathKey === "PATH") env.Path = env[pathKey];
146
+ else env.PATH = env[pathKey];
147
+ return env;
148
+ }
149
+
150
+ function execGitArgsSync(args, options = {}) {
151
+ if (!Array.isArray(args) || !args.length) {
152
+ throw new Error("execGitArgsSync requires a non-empty args array");
153
+ }
154
+ const env = makeIsolatedGitEnv(options.env);
155
+ const gitArgs = args.map((arg) => String(arg));
156
+ let lastEnoent = null;
157
+ for (const gitBinary of resolveGitCandidates(env)) {
158
+ if (gitBinary !== "git" && !existsSync(gitBinary)) continue;
159
+ try {
160
+ return execFileSync(gitBinary, gitArgs, {
161
+ ...options,
162
+ env: buildGitExecutionEnv(env, gitBinary),
163
+ });
164
+ } catch (error) {
165
+ if (error?.code === "ENOENT") {
166
+ lastEnoent = error;
167
+ continue;
168
+ }
169
+ throw error;
170
+ }
171
+ }
172
+ if (lastEnoent) throw lastEnoent;
173
+ throw new Error("Git executable not found");
67
174
  }
68
175
 
69
176
  function trimLogText(value, max = 180) {
@@ -8537,7 +8644,7 @@ registerNodeType("action.detect_new_commits", {
8537
8644
  // Get current HEAD
8538
8645
  let postExecHead = "";
8539
8646
  try {
8540
- postExecHead = execGitSync("git rev-parse HEAD", {
8647
+ postExecHead = execGitArgsSync(["rev-parse", "HEAD"], {
8541
8648
  cwd: worktreePath, encoding: "utf8", timeout: 5000,
8542
8649
  }).trim();
8543
8650
  } catch (err) {
@@ -8551,7 +8658,7 @@ registerNodeType("action.detect_new_commits", {
8551
8658
  let hasUnpushed = false;
8552
8659
  let commitCount = 0;
8553
8660
  try {
8554
- const log = execGitSync(`git log --oneline ${baseBranch}..HEAD`, {
8661
+ const log = execGitArgsSync(["log", "--oneline", `${baseBranch}..HEAD`], {
8555
8662
  cwd: worktreePath, encoding: "utf8", timeout: 10000,
8556
8663
  stdio: ["ignore", "pipe", "pipe"],
8557
8664
  }).trim();
@@ -8565,7 +8672,7 @@ registerNodeType("action.detect_new_commits", {
8565
8672
  let diffStats = null;
8566
8673
  if (hasNewCommits || hasUnpushed) {
8567
8674
  try {
8568
- const statOutput = execGitSync(`git diff --stat ${baseBranch}..HEAD`, {
8675
+ const statOutput = execGitArgsSync(["diff", "--stat", `${baseBranch}..HEAD`], {
8569
8676
  cwd: worktreePath, encoding: "utf8", timeout: 10000,
8570
8677
  stdio: ["ignore", "pipe", "pipe"],
8571
8678
  }).trim();