opencara 0.23.10 → 0.23.11

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 +76 -152
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -34,15 +34,12 @@ function isRepoAllowed(repoConfig, targetOwner, targetRepo, agentOwner, userOrgs
34
34
  case "public":
35
35
  return true;
36
36
  case "private": {
37
+ if (repoConfig.list && repoConfig.list.length > 0 && repoConfig.list.includes(fullRepo)) {
38
+ return true;
39
+ }
37
40
  const normalizedTarget = targetOwner.toLowerCase();
38
41
  const normalizedOwner = agentOwner?.toLowerCase();
39
- const hasAccess = normalizedOwner === normalizedTarget || userOrgs != null && userOrgs.has(normalizedTarget);
40
- if (!hasAccess)
41
- return false;
42
- if (repoConfig.list && repoConfig.list.length > 0) {
43
- return repoConfig.list.includes(fullRepo);
44
- }
45
- return true;
42
+ return normalizedOwner === normalizedTarget || userOrgs != null && userOrgs.has(normalizedTarget);
46
43
  }
47
44
  case "whitelist":
48
45
  return (repoConfig.list ?? []).includes(fullRepo);
@@ -53,84 +50,6 @@ function isRepoAllowed(repoConfig, targetOwner, targetRepo, agentOwner, userOrgs
53
50
  }
54
51
  }
55
52
 
56
- // ../shared/dist/api.js
57
- var DEFAULT_REGISTRY = {
58
- tools: [
59
- {
60
- name: "claude",
61
- displayName: "Claude",
62
- binary: "claude",
63
- commandTemplate: "claude --model ${MODEL} --allowedTools '*' --print",
64
- tokenParser: "claude"
65
- },
66
- {
67
- name: "codex",
68
- displayName: "Codex",
69
- binary: "codex",
70
- commandTemplate: "codex --model ${MODEL} exec",
71
- tokenParser: "codex"
72
- },
73
- {
74
- name: "gemini",
75
- displayName: "Gemini",
76
- binary: "gemini",
77
- commandTemplate: "gemini -m ${MODEL}",
78
- tokenParser: "gemini"
79
- },
80
- {
81
- name: "qwen",
82
- displayName: "Qwen",
83
- binary: "qwen",
84
- commandTemplate: "qwen --model ${MODEL} -y",
85
- tokenParser: "qwen"
86
- }
87
- ],
88
- models: [
89
- {
90
- name: "claude-opus-4-6",
91
- displayName: "Claude Opus 4.6",
92
- tools: ["claude"]
93
- },
94
- {
95
- name: "claude-opus-4-6[1m]",
96
- displayName: "Claude Opus 4.6 (1M context)",
97
- tools: ["claude"]
98
- },
99
- {
100
- name: "claude-sonnet-4-6",
101
- displayName: "Claude Sonnet 4.6",
102
- tools: ["claude"]
103
- },
104
- {
105
- name: "claude-sonnet-4-6[1m]",
106
- displayName: "Claude Sonnet 4.6 (1M context)",
107
- tools: ["claude"]
108
- },
109
- {
110
- name: "gpt-5-codex",
111
- displayName: "GPT-5 Codex",
112
- tools: ["codex"]
113
- },
114
- {
115
- name: "gemini-2.5-pro",
116
- displayName: "Gemini 2.5 Pro",
117
- tools: ["gemini"]
118
- },
119
- {
120
- name: "qwen3.5-plus",
121
- displayName: "Qwen 3.5 Plus",
122
- tools: ["qwen"]
123
- },
124
- { name: "glm-5", displayName: "GLM-5", tools: ["qwen"] },
125
- { name: "kimi-k2.5", displayName: "Kimi K2.5", tools: ["qwen"] },
126
- {
127
- name: "minimax-m2.5",
128
- displayName: "Minimax M2.5",
129
- tools: ["qwen"]
130
- }
131
- ]
132
- };
133
-
134
53
  // ../shared/dist/review-config.js
135
54
  import { parse as parseToml } from "smol-toml";
136
55
  function isObject(value) {
@@ -552,6 +471,26 @@ import * as fs from "fs";
552
471
  import * as path from "path";
553
472
  import * as os from "os";
554
473
  import { parse as parseToml2, stringify as stringifyToml } from "smol-toml";
474
+
475
+ // src/tool-defs.ts
476
+ var _cache = null;
477
+ function loadToolDefs() {
478
+ if (!_cache) {
479
+ _cache = JSON.parse('[{"name":"claude","binary":"claude","models":["claude-sonnet-4-6","claude-opus-4-6"],"command":"claude --model ${MODEL} --allowedTools \'*\' --print","scannable":true,"installLink":"https://docs.anthropic.com/en/docs/claude-code"},{"name":"codex","binary":"codex","models":["gpt-5-codex"],"command":"codex --model ${MODEL} exec","scannable":true,"installLink":"https://github.com/openai/codex"},{"name":"gemini","binary":"gemini","models":["gemini-2.5-pro"],"command":"gemini -m ${MODEL}","scannable":true,"installLink":"https://github.com/google-gemini/gemini-cli"},{"name":"qwen","binary":"qwen","models":["qwen3.5-plus","glm-5","kimi-k2.5","minimax-m2.5"],"command":"qwen --model ${MODEL} -y","scannable":false}]');
480
+ }
481
+ return _cache;
482
+ }
483
+ function getToolDef(name) {
484
+ return loadToolDefs().find((t) => t.name === name);
485
+ }
486
+ function getScannableTools() {
487
+ return loadToolDefs().filter((t) => t.scannable);
488
+ }
489
+ function getKnownToolNames() {
490
+ return new Set(loadToolDefs().map((t) => t.name));
491
+ }
492
+
493
+ // src/config.ts
555
494
  var DEFAULT_PLATFORM_URL = "https://api.opencara.com";
556
495
  var CONFIG_DIR = path.join(os.homedir(), ".opencara");
557
496
  var CONFIG_FILE = process.env.OPENCARA_CONFIG && process.env.OPENCARA_CONFIG.trim() ? path.resolve(process.env.OPENCARA_CONFIG) : path.join(CONFIG_DIR, "config.toml");
@@ -589,7 +528,7 @@ var ConfigValidationError = class extends Error {
589
528
  this.name = "ConfigValidationError";
590
529
  }
591
530
  };
592
- var KNOWN_TOOL_NAMES = new Set(DEFAULT_REGISTRY.tools.map((t) => t.name));
531
+ var KNOWN_TOOL_NAMES = getKnownToolNames();
593
532
  var TOOL_ALIASES = {
594
533
  "claude-code": "claude"
595
534
  };
@@ -4416,17 +4355,6 @@ function countReviewComments(commentsText) {
4416
4355
  import { execFileSync as execFileSync8 } from "child_process";
4417
4356
  import * as fs9 from "fs";
4418
4357
  import * as readline2 from "readline";
4419
- var SCANNABLE_TOOLS = ["claude", "codex", "gemini"];
4420
- var DEFAULT_MODELS = {
4421
- claude: "claude-sonnet-4-6",
4422
- codex: "gpt-5-codex",
4423
- gemini: "gemini-2.5-pro"
4424
- };
4425
- var INSTALL_LINKS = {
4426
- claude: "https://docs.anthropic.com/en/docs/claude-code",
4427
- codex: "https://github.com/openai/codex",
4428
- gemini: "https://github.com/google-gemini/gemini-cli"
4429
- };
4430
4358
  function checkPrerequisites() {
4431
4359
  const gitInstalled = validateCommandBinary("git");
4432
4360
  const ghInstalled = validateCommandBinary("gh");
@@ -4450,21 +4378,13 @@ function checkPrerequisites() {
4450
4378
  }
4451
4379
  function discoverTools() {
4452
4380
  const results = [];
4453
- for (const toolName of SCANNABLE_TOOLS) {
4454
- if (validateCommandBinary(toolName)) {
4455
- const defaultModel = resolveDefaultModel(toolName);
4456
- results.push({ toolName, defaultModel });
4381
+ for (const tool of getScannableTools()) {
4382
+ if (validateCommandBinary(tool.binary)) {
4383
+ results.push({ toolName: tool.name, defaultModel: tool.models[0] });
4457
4384
  }
4458
4385
  }
4459
4386
  return results;
4460
4387
  }
4461
- function resolveDefaultModel(toolName) {
4462
- if (DEFAULT_MODELS[toolName]) {
4463
- return DEFAULT_MODELS[toolName];
4464
- }
4465
- const registryModel = DEFAULT_REGISTRY.models.find((m) => m.tools.includes(toolName));
4466
- return registryModel?.name ?? toolName;
4467
- }
4468
4388
  function generateConfig(tools) {
4469
4389
  const lines = [
4470
4390
  "# Auto-generated by opencara \u2014 edit to customize",
@@ -4541,22 +4461,29 @@ No config found at ${CONFIG_FILE}
4541
4461
  process.stdout.write("Continuing without gh \u2014 some features may be limited.\n");
4542
4462
  }
4543
4463
  process.stdout.write("\nScanning for AI tools...\n");
4464
+ const scannableTools = getScannableTools();
4544
4465
  const found = discoverTools();
4545
- for (const tool of SCANNABLE_TOOLS) {
4546
- const disc = found.find((t) => t.toolName === tool);
4466
+ for (const tool of scannableTools) {
4467
+ const disc = found.find((t) => t.toolName === tool.name);
4547
4468
  if (disc) {
4548
- process.stdout.write(` \u2713 ${tool} (${disc.defaultModel})
4469
+ process.stdout.write(` \u2713 ${tool.name} (${disc.defaultModel})
4549
4470
  `);
4550
4471
  } else {
4551
- process.stdout.write(` \u2717 ${tool} (not found)
4472
+ process.stdout.write(` \u2717 ${tool.name} (not found)
4552
4473
  `);
4553
4474
  }
4554
4475
  }
4555
4476
  if (found.length === 0) {
4556
- process.stdout.write("\nNo AI tools found. Install one of: claude, codex, gemini\n");
4557
- for (const tool of SCANNABLE_TOOLS) {
4558
- process.stdout.write(` ${tool}: ${INSTALL_LINKS[tool]}
4477
+ process.stdout.write(
4478
+ `
4479
+ No AI tools found. Install one of: ${scannableTools.map((t) => t.name).join(", ")}
4480
+ `
4481
+ );
4482
+ for (const tool of scannableTools) {
4483
+ if (tool.installLink) {
4484
+ process.stdout.write(` ${tool.name}: ${tool.installLink}
4559
4485
  `);
4486
+ }
4560
4487
  }
4561
4488
  return false;
4562
4489
  }
@@ -4690,9 +4617,9 @@ function resolveCommandTemplate(agentConfig, globalCommand) {
4690
4617
  if (agentConfig?.command) return agentConfig.command;
4691
4618
  if (globalCommand) return globalCommand;
4692
4619
  if (agentConfig?.tool) {
4693
- const registryTool = DEFAULT_REGISTRY.tools.find((t) => t.name === agentConfig.tool);
4694
- if (registryTool) {
4695
- return registryTool.commandTemplate.replaceAll("${MODEL}", agentConfig.model ?? "");
4620
+ const toolDef = getToolDef(agentConfig.tool);
4621
+ if (toolDef) {
4622
+ return toolDef.command.replaceAll("${MODEL}", agentConfig.model ?? "");
4696
4623
  }
4697
4624
  }
4698
4625
  return void 0;
@@ -5750,7 +5677,7 @@ function sleep2(ms, signal) {
5750
5677
  async function startAgent(agentId, platformUrl, agentInfo, reviewDeps, consumptionDeps, options) {
5751
5678
  const client = new ApiClient(platformUrl, {
5752
5679
  authToken: options?.authToken,
5753
- cliVersion: "0.23.10",
5680
+ cliVersion: "0.23.11",
5754
5681
  versionOverride: options?.versionOverride,
5755
5682
  onTokenRefresh: options?.onTokenRefresh
5756
5683
  });
@@ -6046,7 +5973,7 @@ async function startBatchAgents(config, agents, pollIntervalMs, oauthToken, opti
6046
5973
  const { versionOverride, verbose, instancesOverride, agentOwner, userOrgs } = options;
6047
5974
  const client = new ApiClient(config.platformUrl, {
6048
5975
  authToken: oauthToken,
6049
- cliVersion: "0.23.10",
5976
+ cliVersion: "0.23.11",
6050
5977
  versionOverride,
6051
5978
  onTokenRefresh: () => getValidToken(config.platformUrl, { configPath: config.authFile })
6052
5979
  });
@@ -6217,12 +6144,11 @@ async function startBatchAgents(config, agents, pollIntervalMs, oauthToken, opti
6217
6144
  async function startAgentRouter() {
6218
6145
  const config = loadConfig();
6219
6146
  const agentId = crypto2.randomUUID();
6220
- let commandTemplate;
6221
6147
  let agentConfig;
6222
6148
  if (config.agents && config.agents.length > 0) {
6223
6149
  agentConfig = config.agents.find((a) => a.router) ?? config.agents[0];
6224
6150
  }
6225
- commandTemplate = resolveCommandTemplate(agentConfig, config.agentCommand);
6151
+ const commandTemplate = resolveCommandTemplate(agentConfig, config.agentCommand);
6226
6152
  const router = new RouterRelay();
6227
6153
  router.start();
6228
6154
  const logger = createLogger(agentConfig?.name ?? "agent[0]");
@@ -6294,12 +6220,11 @@ async function startAgentRouter() {
6294
6220
  router.stop();
6295
6221
  }
6296
6222
  function startAgentByIndex(config, agentIndex, pollIntervalMs, oauthToken, versionOverride, verbose, instancesOverride, agentOwner, userOrgs) {
6297
- let commandTemplate;
6298
6223
  let agentConfig;
6299
6224
  if (config.agents && config.agents.length > agentIndex) {
6300
6225
  agentConfig = config.agents[agentIndex];
6301
6226
  }
6302
- commandTemplate = resolveCommandTemplate(agentConfig, config.agentCommand);
6227
+ const commandTemplate = resolveCommandTemplate(agentConfig, config.agentCommand);
6303
6228
  const label = agentConfig?.name ?? `agent[${agentIndex}]`;
6304
6229
  if (!commandTemplate) {
6305
6230
  console.error(`[${label}] No command configured. Skipping.`);
@@ -6375,7 +6300,7 @@ function startAgentByIndex(config, agentIndex, pollIntervalMs, oauthToken, versi
6375
6300
  return promises;
6376
6301
  }
6377
6302
  var agentCommand = new Command("agent").description("Manage review agents");
6378
- agentCommand.command("start").description("Start agents in polling mode").option("--poll-interval <seconds>", "Poll interval in seconds", "10").option("--agent <index>", "Agent index from config.toml (0-based)", "0").option("--all", "Start all configured agents concurrently").option(
6303
+ agentCommand.command("start").description("Start agents in polling mode").option("--poll-interval <seconds>", "Poll interval in seconds", "10").option("--agent <index>", "Start a single agent by index from config.toml (0-based)").option("--all", "Start all configured agents concurrently (default when --agent is not set)").option(
6379
6304
  "--version-override <value>",
6380
6305
  "Cloudflare Workers version override (e.g. opencara-server=abc123)"
6381
6306
  ).option("-v, --verbose", "Log tool stdout/stderr after each review/summary for debugging").option("--instances <count>", "Number of concurrent instances per agent (overrides config)").action(
@@ -6393,7 +6318,7 @@ agentCommand.command("start").description("Start agents in polling mode").option
6393
6318
  }
6394
6319
  config = loadConfig();
6395
6320
  }
6396
- console.log(formatVersionBanner("0.23.10", "3fd8827"));
6321
+ console.log(formatVersionBanner("0.23.11", "1ab6f70"));
6397
6322
  if (config.agents && config.agents.length > 0) {
6398
6323
  const toolEntries = config.agents.map((a) => ({
6399
6324
  tool: a.tool,
@@ -6462,21 +6387,7 @@ agentCommand.command("start").description("Start agents in polling mode").option
6462
6387
  } else if (needsOrgs && userOrgs.size > 0) {
6463
6388
  console.log(`Org memberships: ${[...userOrgs].join(", ")}`);
6464
6389
  }
6465
- if (opts.all) {
6466
- if (!config.agents || config.agents.length === 0) {
6467
- console.error("No agents configured in ~/.opencara/config.toml");
6468
- process.exit(1);
6469
- return;
6470
- }
6471
- console.log(`Starting ${config.agents.length} agent config(s) in batch mode...`);
6472
- await startBatchAgents(config, config.agents, pollIntervalMs, oauthToken, {
6473
- versionOverride,
6474
- verbose: opts.verbose,
6475
- instancesOverride,
6476
- agentOwner,
6477
- userOrgs
6478
- });
6479
- } else {
6390
+ if (opts.agent != null) {
6480
6391
  const maxIndex = (config.agents?.length ?? 0) - 1;
6481
6392
  const agentIndex = Number(opts.agent);
6482
6393
  if (!Number.isInteger(agentIndex) || agentIndex < 0 || agentIndex > maxIndex) {
@@ -6509,6 +6420,20 @@ agentCommand.command("start").description("Start agents in polling mode").option
6509
6420
  }
6510
6421
  process.exit(1);
6511
6422
  }
6423
+ } else {
6424
+ if (!config.agents || config.agents.length === 0) {
6425
+ console.error("No agents configured in ~/.opencara/config.toml");
6426
+ process.exit(1);
6427
+ return;
6428
+ }
6429
+ console.log(`Starting ${config.agents.length} agent config(s) in batch mode...`);
6430
+ await startBatchAgents(config, config.agents, pollIntervalMs, oauthToken, {
6431
+ versionOverride,
6432
+ verbose: opts.verbose,
6433
+ instancesOverride,
6434
+ agentOwner,
6435
+ userOrgs
6436
+ });
6512
6437
  }
6513
6438
  }
6514
6439
  );
@@ -6799,11 +6724,10 @@ function resolveAgentCommand(toolName) {
6799
6724
  if (cmd) return cmd;
6800
6725
  }
6801
6726
  }
6802
- const registryTool = DEFAULT_REGISTRY.tools.find((t) => t.name === toolName);
6803
- if (registryTool) {
6804
- const defaultModel = DEFAULT_REGISTRY.models.find((m) => m.tools.includes(toolName));
6805
- const modelName = defaultModel?.name ?? "";
6806
- return registryTool.commandTemplate.replaceAll("${MODEL}", modelName);
6727
+ const toolDef = getToolDef(toolName);
6728
+ if (toolDef) {
6729
+ const modelName = toolDef.models[0] ?? "";
6730
+ return toolDef.command.replaceAll("${MODEL}", modelName);
6807
6731
  }
6808
6732
  return null;
6809
6733
  }
@@ -7050,7 +6974,7 @@ async function runDedupInit(options, deps = {}) {
7050
6974
  const cmd = resolveCmd(options.agent);
7051
6975
  if (!cmd) {
7052
6976
  logError(
7053
- `${icons.error} Unknown agent tool "${options.agent}". Available: ${DEFAULT_REGISTRY.tools.map((t) => t.name).join(", ")}`
6977
+ `${icons.error} Unknown agent tool "${options.agent}". Available: ${loadToolDefs().map((t) => t.name).join(", ")}`
7054
6978
  );
7055
6979
  process.exitCode = 1;
7056
6980
  return;
@@ -7105,13 +7029,13 @@ function agentRoleLabel(agent) {
7105
7029
  return "reviewer+synthesizer";
7106
7030
  }
7107
7031
  function resolveToolBinary(toolName) {
7108
- const entry = DEFAULT_REGISTRY.tools.find((t) => t.name === toolName);
7109
- return entry?.binary ?? toolName;
7032
+ const def = getToolDef(toolName);
7033
+ return def?.binary ?? toolName;
7110
7034
  }
7111
7035
  function resolveCommand(agent) {
7112
7036
  if (agent.command) return agent.command;
7113
- const entry = DEFAULT_REGISTRY.tools.find((t) => t.name === agent.tool);
7114
- return entry?.commandTemplate ?? null;
7037
+ const def = getToolDef(agent.tool);
7038
+ return def?.command ?? null;
7115
7039
  }
7116
7040
  async function checkConnectivity(platformUrl, fetchFn = fetch) {
7117
7041
  const start = Date.now();
@@ -7216,7 +7140,7 @@ var statusCommand = new Command4("status").description("Show agent config, conne
7216
7140
  });
7217
7141
 
7218
7142
  // src/index.ts
7219
- var program = new Command5().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version(`${"0.23.10"} (${"3fd8827"})`);
7143
+ var program = new Command5().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version(`${"0.23.11"} (${"1ab6f70"})`);
7220
7144
  program.addCommand(agentCommand);
7221
7145
  program.addCommand(authCommand());
7222
7146
  program.addCommand(dedupCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencara",
3
- "version": "0.23.10",
3
+ "version": "0.23.11",
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",