opencara 0.24.3 → 0.25.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.
Files changed (2) hide show
  1. package/dist/index.js +84 -27
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1880,18 +1880,71 @@ function gitExec(command, args, cwd, opts) {
1880
1880
  throw new Error(sanitizeTokens(message));
1881
1881
  }
1882
1882
  }
1883
- function diffFromWorktree(bareRepoPath, worktreePath, baseRef, ghAvailable, maxDiffBytes = 128 * 1024 * 1024) {
1884
- if (!/^[A-Za-z0-9_./-]+$/.test(baseRef) || baseRef.startsWith("-")) {
1885
- throw new Error(`Invalid base ref: ${baseRef}`);
1886
- }
1883
+ var DEFAULT_BRANCH_FALLBACKS = ["main", "master"];
1884
+ function isValidBranchName(name) {
1885
+ if (!name) return false;
1886
+ if (name.startsWith("-")) return false;
1887
+ if (name.includes("..")) return false;
1888
+ return /^[A-Za-z0-9_./-]+$/.test(name);
1889
+ }
1890
+ function fetchBranch(bareRepoPath, branch, ghAvailable) {
1887
1891
  const credArgs = ghAvailable ? ["-c", `credential.helper=${GH_CREDENTIAL_HELPER}`] : [];
1888
1892
  gitExec(
1889
1893
  "git",
1890
- [...credArgs, "fetch", "--force", "origin", `${baseRef}:refs/remotes/origin/${baseRef}`],
1894
+ [...credArgs, "fetch", "--force", "origin", `${branch}:refs/remotes/origin/${branch}`],
1891
1895
  bareRepoPath
1892
1896
  );
1897
+ }
1898
+ function isRemoteRefMissingError(err) {
1899
+ const msg = err instanceof Error ? err.message : String(err);
1900
+ return /couldn't find remote ref/i.test(msg) || /couldnt find remote ref/i.test(msg) || /no such ref/i.test(msg) || /remote ref.*not found/i.test(msg) || /unknown revision or path not in the working tree/i.test(msg);
1901
+ }
1902
+ function deriveDefaultBranch(bareRepoPath, ghAvailable) {
1903
+ try {
1904
+ const out = gitExec("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], bareRepoPath).trim();
1905
+ const prefix = "refs/remotes/origin/";
1906
+ if (out.startsWith(prefix)) {
1907
+ const branch = out.slice(prefix.length);
1908
+ if (isValidBranchName(branch)) {
1909
+ try {
1910
+ fetchBranch(bareRepoPath, branch, ghAvailable);
1911
+ return branch;
1912
+ } catch {
1913
+ }
1914
+ }
1915
+ }
1916
+ } catch {
1917
+ }
1918
+ for (const candidate of DEFAULT_BRANCH_FALLBACKS) {
1919
+ try {
1920
+ fetchBranch(bareRepoPath, candidate, ghAvailable);
1921
+ return candidate;
1922
+ } catch {
1923
+ }
1924
+ }
1925
+ throw new Error("Cannot derive default branch: origin/HEAD, main, and master all failed");
1926
+ }
1927
+ function diffFromWorktree(bareRepoPath, worktreePath, baseRef, ghAvailable, maxDiffBytes = 128 * 1024 * 1024) {
1928
+ let resolvedBaseRef;
1929
+ if (baseRef) {
1930
+ if (!isValidBranchName(baseRef)) {
1931
+ throw new Error(`Invalid base ref: ${baseRef}`);
1932
+ }
1933
+ try {
1934
+ fetchBranch(bareRepoPath, baseRef, ghAvailable);
1935
+ resolvedBaseRef = baseRef;
1936
+ } catch (err) {
1937
+ if (!isRemoteRefMissingError(err)) {
1938
+ throw err;
1939
+ }
1940
+ resolvedBaseRef = void 0;
1941
+ }
1942
+ }
1943
+ if (!resolvedBaseRef) {
1944
+ resolvedBaseRef = deriveDefaultBranch(bareRepoPath, ghAvailable);
1945
+ }
1893
1946
  try {
1894
- return gitExec("git", ["diff", `origin/${baseRef}...HEAD`], worktreePath, {
1947
+ return gitExec("git", ["diff", `origin/${resolvedBaseRef}...HEAD`], worktreePath, {
1895
1948
  maxBuffer: maxDiffBytes
1896
1949
  });
1897
1950
  } catch (err) {
@@ -4687,6 +4740,12 @@ function registerShutdownHandlers(controller, log, graceMs = SHUTDOWN_GRACE_MS)
4687
4740
  };
4688
4741
  }
4689
4742
  var NON_RETRYABLE_STATUSES = /* @__PURE__ */ new Set([401, 403, 404]);
4743
+ function build404Hint(isGhAuthenticated) {
4744
+ if (isGhAuthenticated) {
4745
+ return ". Diff fetch returned 404. Possible causes: (a) PR not found, (b) the installation can't access this repo, or (c) a transient GitHub outage. See any `[fetchDiffViaGh]` warning above for the underlying `gh api` error.";
4746
+ }
4747
+ return ". If this is a private repo, ensure gh CLI is installed and authenticated: gh auth login";
4748
+ }
4690
4749
  function toApiDiffUrl(webUrl) {
4691
4750
  const match = webUrl.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)(?:\.diff)?$/);
4692
4751
  if (!match) return null;
@@ -4748,7 +4807,7 @@ function computeRoles(agent) {
4748
4807
  return ["review", "summary", "implement", "fix"];
4749
4808
  }
4750
4809
  var DIFF_FETCH_TIMEOUT_MS = 6e4;
4751
- async function fetchDiffHttp(url, headers, signal, maxDiffSizeKb) {
4810
+ async function fetchDiffHttp(url, headers, signal, maxDiffSizeKb, isGhAuthenticatedFn = isGhAvailable) {
4752
4811
  const maxBytes = maxDiffSizeKb ? maxDiffSizeKb * 1024 : Infinity;
4753
4812
  const controller = new AbortController();
4754
4813
  const timer = setTimeout(() => controller.abort(), DIFF_FETCH_TIMEOUT_MS);
@@ -4769,7 +4828,7 @@ async function fetchDiffHttp(url, headers, signal, maxDiffSizeKb) {
4769
4828
  clearTimeout(timer);
4770
4829
  signal?.removeEventListener("abort", onParentAbort);
4771
4830
  if (!response.ok) {
4772
- const hint = response.status === 404 ? ". If this is a private repo, ensure gh CLI is installed and authenticated: gh auth login" : "";
4831
+ const hint = response.status === 404 ? build404Hint(isGhAuthenticatedFn()) : "";
4773
4832
  const msg = `Failed to fetch diff: ${response.status} ${response.statusText}${hint}`;
4774
4833
  if (NON_RETRYABLE_STATUSES.has(response.status)) {
4775
4834
  throw new NonRetryableError(msg);
@@ -5054,7 +5113,12 @@ async function handleTask(client, agentId, task, reviewDeps, consumptionDeps, ag
5054
5113
  );
5055
5114
  taskReviewDeps = { ...reviewDeps, codebaseDir: null };
5056
5115
  }
5057
- if (taskCheckoutPath && taskBareRepoPath && base_ref) {
5116
+ if (taskCheckoutPath && taskBareRepoPath) {
5117
+ if (!base_ref) {
5118
+ logWarn(
5119
+ ` Warning: task ${task_id} has no base_ref \u2014 deriving default branch from worktree`
5120
+ );
5121
+ }
5058
5122
  try {
5059
5123
  const ghAvailable = isGhAvailable();
5060
5124
  const maxDiffBytes = reviewDeps.maxDiffSizeKb ? reviewDeps.maxDiffSizeKb * 1024 : void 0;
@@ -5077,23 +5141,16 @@ async function handleTask(client, agentId, task, reviewDeps, consumptionDeps, ag
5077
5141
  diffContent = gitDiff;
5078
5142
  log(` Diff generated via git (${Math.round(diffContent.length / 1024)}KB)`);
5079
5143
  } catch (err) {
5144
+ const message = err.message;
5080
5145
  if (err instanceof DiffTooLargeError) {
5081
- logError(` ${err.message}`);
5082
- await safeReject(
5083
- client,
5084
- task_id,
5085
- agentId,
5086
- `Cannot access diff: ${err.message}`,
5087
- logger
5088
- );
5089
- return { diffFetchFailed: true };
5146
+ logError(` ${message}`);
5147
+ } else {
5148
+ logError(` git diff failed for task ${task_id}: ${message}`);
5090
5149
  }
5091
- logWarn(
5092
- ` Warning: git diff failed (${err.message}) \u2014 falling back to API fetch`
5093
- );
5150
+ await safeReject(client, task_id, agentId, `Cannot access diff: ${message}`, logger);
5151
+ return { diffFetchFailed: true };
5094
5152
  }
5095
- }
5096
- if (!diffContent) {
5153
+ } else {
5097
5154
  try {
5098
5155
  const result = await fetchDiff(diff_url, owner, repo, pr_number, {
5099
5156
  githubToken: client.currentToken,
@@ -5736,7 +5793,7 @@ function sleep2(ms, signal) {
5736
5793
  async function startAgent(agentId, platformUrl, agentInfo, reviewDeps, consumptionDeps, options) {
5737
5794
  const client = new ApiClient(platformUrl, {
5738
5795
  authToken: options?.authToken,
5739
- cliVersion: "0.24.3",
5796
+ cliVersion: "0.25.0",
5740
5797
  versionOverride: options?.versionOverride,
5741
5798
  onTokenRefresh: options?.onTokenRefresh
5742
5799
  });
@@ -6101,7 +6158,7 @@ async function startBatchAgents(config, agents, pollIntervalMs, oauthToken, opti
6101
6158
  const { versionOverride, verbose, instancesOverride, agentOwner, userOrgs } = options;
6102
6159
  const client = new ApiClient(config.platformUrl, {
6103
6160
  authToken: oauthToken,
6104
- cliVersion: "0.24.3",
6161
+ cliVersion: "0.25.0",
6105
6162
  versionOverride,
6106
6163
  onTokenRefresh: () => getValidToken(config.platformUrl, { configPath: config.authFile })
6107
6164
  });
@@ -6450,7 +6507,7 @@ agentCommand.command("start").description("Start agents in polling mode").option
6450
6507
  }
6451
6508
  config = loadConfig();
6452
6509
  }
6453
- console.log(formatVersionBanner("0.24.3", "c08a9b3"));
6510
+ console.log(formatVersionBanner("0.25.0", "a984ae2"));
6454
6511
  if (config.agents && config.agents.length > 0) {
6455
6512
  const toolEntries = config.agents.map((a) => ({
6456
6513
  tool: a.tool,
@@ -7272,7 +7329,7 @@ var statusCommand = new Command4("status").description("Show agent config, conne
7272
7329
  });
7273
7330
 
7274
7331
  // src/index.ts
7275
- var program = new Command5().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version(`${"0.24.3"} (${"c08a9b3"})`);
7332
+ var program = new Command5().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version(`${"0.25.0"} (${"a984ae2"})`);
7276
7333
  program.addCommand(agentCommand);
7277
7334
  program.addCommand(authCommand());
7278
7335
  program.addCommand(dedupCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencara",
3
- "version": "0.24.3",
3
+ "version": "0.25.0",
4
4
  "description": "Distributed AI code review agent — poll, review, and submit PR reviews using your own AI tools",
5
5
  "type": "module",
6
6
  "license": "MIT",