opencara 0.18.5 → 0.18.6

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 +66 -13
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -16,15 +16,24 @@ function isDedupRole(role) {
16
16
  function isTriageRole(role) {
17
17
  return role === "pr_triage" || role === "issue_triage";
18
18
  }
19
- function isRepoAllowed(repoConfig, targetOwner, targetRepo, agentOwner) {
19
+ function isRepoAllowed(repoConfig, targetOwner, targetRepo, agentOwner, userOrgs) {
20
20
  if (!repoConfig)
21
21
  return true;
22
22
  const fullRepo = `${targetOwner}/${targetRepo}`;
23
23
  switch (repoConfig.mode) {
24
24
  case "public":
25
25
  return true;
26
- case "private":
27
- return agentOwner === targetOwner;
26
+ case "private": {
27
+ const normalizedTarget = targetOwner.toLowerCase();
28
+ const normalizedOwner = agentOwner?.toLowerCase();
29
+ const hasAccess = normalizedOwner === normalizedTarget || userOrgs != null && userOrgs.has(normalizedTarget);
30
+ if (!hasAccess)
31
+ return false;
32
+ if (repoConfig.list && repoConfig.list.length > 0) {
33
+ return repoConfig.list.includes(fullRepo);
34
+ }
35
+ return true;
36
+ }
28
37
  case "whitelist":
29
38
  return (repoConfig.list ?? []).includes(fullRepo);
30
39
  case "blacklist":
@@ -1189,6 +1198,30 @@ async function resolveUser(token, fetchFn = fetch) {
1189
1198
  }
1190
1199
  return { login: data.login, id: data.id };
1191
1200
  }
1201
+ async function fetchUserOrgs(token, fetchFn = fetch) {
1202
+ try {
1203
+ const res = await fetchFn("https://api.github.com/user/orgs?per_page=100", {
1204
+ headers: {
1205
+ Authorization: `Bearer ${token}`,
1206
+ Accept: "application/vnd.github+json",
1207
+ "X-GitHub-Api-Version": "2022-11-28"
1208
+ }
1209
+ });
1210
+ if (!res.ok) {
1211
+ return /* @__PURE__ */ new Set();
1212
+ }
1213
+ const data = await res.json();
1214
+ const orgs = /* @__PURE__ */ new Set();
1215
+ for (const org of data) {
1216
+ if (typeof org.login === "string") {
1217
+ orgs.add(org.login.toLowerCase());
1218
+ }
1219
+ }
1220
+ return orgs;
1221
+ } catch {
1222
+ return /* @__PURE__ */ new Set();
1223
+ }
1224
+ }
1192
1225
 
1193
1226
  // src/http.ts
1194
1227
  var HttpError = class extends Error {
@@ -3207,7 +3240,9 @@ async function pollLoop(client, agentId, reviewDeps, consumptionDeps, agentInfo,
3207
3240
  synthesizeRepos,
3208
3241
  signal,
3209
3242
  cleanupTracker,
3210
- verbose
3243
+ verbose,
3244
+ agentOwner,
3245
+ userOrgs
3211
3246
  } = options;
3212
3247
  const { log, logError, logWarn } = logger;
3213
3248
  log(`${icons.polling} Polling every ${pollIntervalMs / 1e3}s...`);
@@ -3239,7 +3274,9 @@ async function pollLoop(client, agentId, reviewDeps, consumptionDeps, agentInfo,
3239
3274
  const pollResponse = await client.post("/api/tasks/poll", pollBody);
3240
3275
  consecutiveAuthErrors = 0;
3241
3276
  consecutiveErrors = 0;
3242
- const eligibleTasks = repoConfig ? pollResponse.tasks.filter((t) => isRepoAllowed(repoConfig, t.owner, t.repo)) : pollResponse.tasks;
3277
+ const eligibleTasks = repoConfig ? pollResponse.tasks.filter(
3278
+ (t) => isRepoAllowed(repoConfig, t.owner, t.repo, agentOwner, userOrgs)
3279
+ ) : pollResponse.tasks;
3243
3280
  const task = eligibleTasks.find(
3244
3281
  (t) => (diffFailCounts.get(t.task_id) ?? 0) < MAX_DIFF_FETCH_ATTEMPTS
3245
3282
  );
@@ -3904,7 +3941,7 @@ function sleep2(ms, signal) {
3904
3941
  async function startAgent(agentId, platformUrl, agentInfo, reviewDeps, consumptionDeps, options) {
3905
3942
  const client = new ApiClient(platformUrl, {
3906
3943
  authToken: options?.authToken,
3907
- cliVersion: "0.18.5",
3944
+ cliVersion: "0.18.6",
3908
3945
  versionOverride: options?.versionOverride,
3909
3946
  onTokenRefresh: options?.onTokenRefresh
3910
3947
  });
@@ -3972,7 +4009,9 @@ async function startAgent(agentId, platformUrl, agentInfo, reviewDeps, consumpti
3972
4009
  synthesizeRepos: options?.synthesizeRepos,
3973
4010
  signal: abortController.signal,
3974
4011
  cleanupTracker,
3975
- verbose: options?.verbose
4012
+ verbose: options?.verbose,
4013
+ agentOwner: options?.agentOwner,
4014
+ userOrgs: options?.userOrgs
3976
4015
  });
3977
4016
  if (cleanupTracker && cleanupTracker.size > 0) {
3978
4017
  const finalSwept = await cleanupTracker.sweep(cleanupWorktree);
@@ -4014,9 +4053,12 @@ async function startAgentRouter() {
4014
4053
  throw err;
4015
4054
  }
4016
4055
  const storedAuth = loadAuth();
4056
+ const agentOwner = storedAuth?.github_username;
4017
4057
  if (storedAuth) {
4018
4058
  logger.log(`Authenticated as ${storedAuth.github_username}`);
4019
4059
  }
4060
+ const repoConfig = agentConfig?.repos;
4061
+ const userOrgs = repoConfig?.mode === "private" ? await fetchUserOrgs(oauthToken) : /* @__PURE__ */ new Set();
4020
4062
  const codebaseDir = resolveCodebaseDir(agentConfig?.codebase_dir, config.codebaseDir);
4021
4063
  const reviewDeps = {
4022
4064
  commandTemplate: commandTemplate ?? "",
@@ -4046,12 +4088,14 @@ async function startAgentRouter() {
4046
4088
  maxConsecutiveErrors: config.maxConsecutiveErrors,
4047
4089
  routerRelay: router,
4048
4090
  reviewOnly: agentConfig?.review_only,
4049
- repoConfig: agentConfig?.repos,
4091
+ repoConfig,
4050
4092
  roles,
4051
4093
  synthesizeRepos: agentConfig?.synthesize_repos,
4052
4094
  label,
4053
4095
  authToken: oauthToken,
4054
4096
  onTokenRefresh: () => getValidToken(config.platformUrl),
4097
+ agentOwner,
4098
+ userOrgs,
4055
4099
  usageLimits: config.usageLimits,
4056
4100
  versionOverride,
4057
4101
  codebaseTtl: config.codebaseTtl
@@ -4059,7 +4103,7 @@ async function startAgentRouter() {
4059
4103
  );
4060
4104
  router.stop();
4061
4105
  }
4062
- function startAgentByIndex(config, agentIndex, pollIntervalMs, oauthToken, versionOverride, verbose, instancesOverride) {
4106
+ function startAgentByIndex(config, agentIndex, pollIntervalMs, oauthToken, versionOverride, verbose, instancesOverride, agentOwner, userOrgs) {
4063
4107
  let commandTemplate;
4064
4108
  let agentConfig;
4065
4109
  if (config.agents && config.agents.length > agentIndex) {
@@ -4122,7 +4166,9 @@ function startAgentByIndex(config, agentIndex, pollIntervalMs, oauthToken, versi
4122
4166
  usageLimits: config.usageLimits,
4123
4167
  versionOverride,
4124
4168
  codebaseTtl: config.codebaseTtl,
4125
- verbose
4169
+ verbose,
4170
+ agentOwner,
4171
+ userOrgs
4126
4172
  }
4127
4173
  ).finally(() => {
4128
4174
  routerRelay?.stop();
@@ -4161,9 +4207,12 @@ agentCommand.command("start").description("Start agents in polling mode").option
4161
4207
  throw err;
4162
4208
  }
4163
4209
  const storedAuth = loadAuth();
4210
+ const agentOwner = storedAuth?.github_username;
4164
4211
  if (storedAuth) {
4165
4212
  console.log(`Authenticated as ${storedAuth.github_username}`);
4166
4213
  }
4214
+ const needsOrgs = config.agents?.some((a) => a.repos?.mode === "private") ?? false;
4215
+ const userOrgs = needsOrgs ? await fetchUserOrgs(oauthToken) : /* @__PURE__ */ new Set();
4167
4216
  if (opts.all) {
4168
4217
  if (!config.agents || config.agents.length === 0) {
4169
4218
  console.error("No agents configured in ~/.opencara/config.toml");
@@ -4181,7 +4230,9 @@ agentCommand.command("start").description("Start agents in polling mode").option
4181
4230
  oauthToken,
4182
4231
  versionOverride,
4183
4232
  opts.verbose,
4184
- instancesOverride
4233
+ instancesOverride,
4234
+ agentOwner,
4235
+ userOrgs
4185
4236
  );
4186
4237
  if (agentPromises) {
4187
4238
  promises.push(...agentPromises);
@@ -4226,7 +4277,9 @@ agentCommand.command("start").description("Start agents in polling mode").option
4226
4277
  oauthToken,
4227
4278
  versionOverride,
4228
4279
  opts.verbose,
4229
- instancesOverride
4280
+ instancesOverride,
4281
+ agentOwner,
4282
+ userOrgs
4230
4283
  );
4231
4284
  if (!agentPromises) {
4232
4285
  process.exit(1);
@@ -4942,7 +4995,7 @@ var statusCommand = new Command4("status").description("Show agent config, conne
4942
4995
  });
4943
4996
 
4944
4997
  // src/index.ts
4945
- var program = new Command5().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version("0.18.5");
4998
+ var program = new Command5().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version("0.18.6");
4946
4999
  program.addCommand(agentCommand);
4947
5000
  program.addCommand(authCommand());
4948
5001
  program.addCommand(dedupCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencara",
3
- "version": "0.18.5",
3
+ "version": "0.18.6",
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",