opencara 0.23.10 → 0.23.12
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/dist/index.js +99 -156
- 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
|
-
|
|
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 =
|
|
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
|
|
4454
|
-
if (validateCommandBinary(
|
|
4455
|
-
|
|
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
|
|
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(
|
|
4557
|
-
|
|
4558
|
-
|
|
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
|
}
|
|
@@ -4649,21 +4576,39 @@ function buildBatchPollRequest(agents) {
|
|
|
4649
4576
|
});
|
|
4650
4577
|
return { agents: batchAgents };
|
|
4651
4578
|
}
|
|
4652
|
-
function filterTasksForAgent(tasks, agent, maxDiffSizeKb, diffFailCounts, maxDiffFetchAttempts = 3, accessibleRepos) {
|
|
4579
|
+
function filterTasksForAgent(tasks, agent, maxDiffSizeKb, diffFailCounts, maxDiffFetchAttempts = 3, accessibleRepos, log) {
|
|
4653
4580
|
return tasks.filter((t) => {
|
|
4654
|
-
|
|
4581
|
+
const repo = `${t.owner}/${t.repo}`;
|
|
4582
|
+
if (accessibleRepos && !accessibleRepos.has(repo)) {
|
|
4583
|
+
log?.(
|
|
4584
|
+
`Skipping task ${t.task_id.slice(0, 8)}\u2026 (${repo} PR#${t.pr_number}) \u2014 repo not in accessible set`
|
|
4585
|
+
);
|
|
4655
4586
|
return false;
|
|
4656
4587
|
}
|
|
4657
4588
|
if (agent.repoConfig && !isRepoAllowed(agent.repoConfig, t.owner, t.repo, agent.agentOwner, agent.userOrgs)) {
|
|
4589
|
+
log?.(
|
|
4590
|
+
`Skipping task ${t.task_id.slice(0, 8)}\u2026 (${repo} PR#${t.pr_number}) \u2014 repo not allowed by config (mode=${agent.repoConfig.mode})`
|
|
4591
|
+
);
|
|
4658
4592
|
return false;
|
|
4659
4593
|
}
|
|
4660
4594
|
if (agent.synthesizeRepos && !isRepoAllowed(agent.synthesizeRepos, t.owner, t.repo, agent.agentOwner, agent.userOrgs)) {
|
|
4595
|
+
log?.(
|
|
4596
|
+
`Skipping task ${t.task_id.slice(0, 8)}\u2026 (${repo} PR#${t.pr_number}) \u2014 repo not allowed by synthesize_repos config`
|
|
4597
|
+
);
|
|
4661
4598
|
return false;
|
|
4662
4599
|
}
|
|
4663
|
-
|
|
4600
|
+
const isExplicitlyListed = agent.repoConfig?.list?.includes(repo) ?? false;
|
|
4601
|
+
if (!isExplicitlyListed && maxDiffSizeKb && t.diff_size != null && t.diff_size * ESTIMATED_BYTES_PER_DIFF_LINE / 1024 > maxDiffSizeKb) {
|
|
4602
|
+
const estimatedKb = Math.round(t.diff_size * ESTIMATED_BYTES_PER_DIFF_LINE / 1024);
|
|
4603
|
+
log?.(
|
|
4604
|
+
`Skipping task ${t.task_id.slice(0, 8)}\u2026 (${repo} PR#${t.pr_number}) \u2014 estimated diff ${estimatedKb}KB exceeds max_diff_size_kb (${maxDiffSizeKb}KB)`
|
|
4605
|
+
);
|
|
4664
4606
|
return false;
|
|
4665
4607
|
}
|
|
4666
4608
|
if (diffFailCounts && (diffFailCounts.get(t.task_id) ?? 0) >= maxDiffFetchAttempts) {
|
|
4609
|
+
log?.(
|
|
4610
|
+
`Skipping task ${t.task_id.slice(0, 8)}\u2026 (${repo} PR#${t.pr_number}) \u2014 diff fetch failed ${maxDiffFetchAttempts} times`
|
|
4611
|
+
);
|
|
4667
4612
|
return false;
|
|
4668
4613
|
}
|
|
4669
4614
|
return true;
|
|
@@ -4690,9 +4635,9 @@ function resolveCommandTemplate(agentConfig, globalCommand) {
|
|
|
4690
4635
|
if (agentConfig?.command) return agentConfig.command;
|
|
4691
4636
|
if (globalCommand) return globalCommand;
|
|
4692
4637
|
if (agentConfig?.tool) {
|
|
4693
|
-
const
|
|
4694
|
-
if (
|
|
4695
|
-
return
|
|
4638
|
+
const toolDef = getToolDef(agentConfig.tool);
|
|
4639
|
+
if (toolDef) {
|
|
4640
|
+
return toolDef.command.replaceAll("${MODEL}", agentConfig.model ?? "");
|
|
4696
4641
|
}
|
|
4697
4642
|
}
|
|
4698
4643
|
return void 0;
|
|
@@ -5750,7 +5695,7 @@ function sleep2(ms, signal) {
|
|
|
5750
5695
|
async function startAgent(agentId, platformUrl, agentInfo, reviewDeps, consumptionDeps, options) {
|
|
5751
5696
|
const client = new ApiClient(platformUrl, {
|
|
5752
5697
|
authToken: options?.authToken,
|
|
5753
|
-
cliVersion: "0.23.
|
|
5698
|
+
cliVersion: "0.23.12",
|
|
5754
5699
|
versionOverride: options?.versionOverride,
|
|
5755
5700
|
onTokenRefresh: options?.onTokenRefresh
|
|
5756
5701
|
});
|
|
@@ -5935,7 +5880,8 @@ async function batchPollLoop(client, agentStates, options) {
|
|
|
5935
5880
|
state.reviewDeps.maxDiffSizeKb,
|
|
5936
5881
|
state.diffFailCounts,
|
|
5937
5882
|
MAX_DIFF_FETCH_ATTEMPTS,
|
|
5938
|
-
accessibleRepos
|
|
5883
|
+
accessibleRepos,
|
|
5884
|
+
(msg) => state.logger.logWarn(`${icons.warn} ${msg}`)
|
|
5939
5885
|
);
|
|
5940
5886
|
const task = eligible[0];
|
|
5941
5887
|
if (!task) continue;
|
|
@@ -6046,7 +5992,7 @@ async function startBatchAgents(config, agents, pollIntervalMs, oauthToken, opti
|
|
|
6046
5992
|
const { versionOverride, verbose, instancesOverride, agentOwner, userOrgs } = options;
|
|
6047
5993
|
const client = new ApiClient(config.platformUrl, {
|
|
6048
5994
|
authToken: oauthToken,
|
|
6049
|
-
cliVersion: "0.23.
|
|
5995
|
+
cliVersion: "0.23.12",
|
|
6050
5996
|
versionOverride,
|
|
6051
5997
|
onTokenRefresh: () => getValidToken(config.platformUrl, { configPath: config.authFile })
|
|
6052
5998
|
});
|
|
@@ -6217,12 +6163,11 @@ async function startBatchAgents(config, agents, pollIntervalMs, oauthToken, opti
|
|
|
6217
6163
|
async function startAgentRouter() {
|
|
6218
6164
|
const config = loadConfig();
|
|
6219
6165
|
const agentId = crypto2.randomUUID();
|
|
6220
|
-
let commandTemplate;
|
|
6221
6166
|
let agentConfig;
|
|
6222
6167
|
if (config.agents && config.agents.length > 0) {
|
|
6223
6168
|
agentConfig = config.agents.find((a) => a.router) ?? config.agents[0];
|
|
6224
6169
|
}
|
|
6225
|
-
commandTemplate = resolveCommandTemplate(agentConfig, config.agentCommand);
|
|
6170
|
+
const commandTemplate = resolveCommandTemplate(agentConfig, config.agentCommand);
|
|
6226
6171
|
const router = new RouterRelay();
|
|
6227
6172
|
router.start();
|
|
6228
6173
|
const logger = createLogger(agentConfig?.name ?? "agent[0]");
|
|
@@ -6294,12 +6239,11 @@ async function startAgentRouter() {
|
|
|
6294
6239
|
router.stop();
|
|
6295
6240
|
}
|
|
6296
6241
|
function startAgentByIndex(config, agentIndex, pollIntervalMs, oauthToken, versionOverride, verbose, instancesOverride, agentOwner, userOrgs) {
|
|
6297
|
-
let commandTemplate;
|
|
6298
6242
|
let agentConfig;
|
|
6299
6243
|
if (config.agents && config.agents.length > agentIndex) {
|
|
6300
6244
|
agentConfig = config.agents[agentIndex];
|
|
6301
6245
|
}
|
|
6302
|
-
commandTemplate = resolveCommandTemplate(agentConfig, config.agentCommand);
|
|
6246
|
+
const commandTemplate = resolveCommandTemplate(agentConfig, config.agentCommand);
|
|
6303
6247
|
const label = agentConfig?.name ?? `agent[${agentIndex}]`;
|
|
6304
6248
|
if (!commandTemplate) {
|
|
6305
6249
|
console.error(`[${label}] No command configured. Skipping.`);
|
|
@@ -6375,7 +6319,7 @@ function startAgentByIndex(config, agentIndex, pollIntervalMs, oauthToken, versi
|
|
|
6375
6319
|
return promises;
|
|
6376
6320
|
}
|
|
6377
6321
|
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>", "
|
|
6322
|
+
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
6323
|
"--version-override <value>",
|
|
6380
6324
|
"Cloudflare Workers version override (e.g. opencara-server=abc123)"
|
|
6381
6325
|
).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 +6337,7 @@ agentCommand.command("start").description("Start agents in polling mode").option
|
|
|
6393
6337
|
}
|
|
6394
6338
|
config = loadConfig();
|
|
6395
6339
|
}
|
|
6396
|
-
console.log(formatVersionBanner("0.23.
|
|
6340
|
+
console.log(formatVersionBanner("0.23.12", "d2879c9"));
|
|
6397
6341
|
if (config.agents && config.agents.length > 0) {
|
|
6398
6342
|
const toolEntries = config.agents.map((a) => ({
|
|
6399
6343
|
tool: a.tool,
|
|
@@ -6462,21 +6406,7 @@ agentCommand.command("start").description("Start agents in polling mode").option
|
|
|
6462
6406
|
} else if (needsOrgs && userOrgs.size > 0) {
|
|
6463
6407
|
console.log(`Org memberships: ${[...userOrgs].join(", ")}`);
|
|
6464
6408
|
}
|
|
6465
|
-
if (opts.
|
|
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 {
|
|
6409
|
+
if (opts.agent != null) {
|
|
6480
6410
|
const maxIndex = (config.agents?.length ?? 0) - 1;
|
|
6481
6411
|
const agentIndex = Number(opts.agent);
|
|
6482
6412
|
if (!Number.isInteger(agentIndex) || agentIndex < 0 || agentIndex > maxIndex) {
|
|
@@ -6509,6 +6439,20 @@ agentCommand.command("start").description("Start agents in polling mode").option
|
|
|
6509
6439
|
}
|
|
6510
6440
|
process.exit(1);
|
|
6511
6441
|
}
|
|
6442
|
+
} else {
|
|
6443
|
+
if (!config.agents || config.agents.length === 0) {
|
|
6444
|
+
console.error("No agents configured in ~/.opencara/config.toml");
|
|
6445
|
+
process.exit(1);
|
|
6446
|
+
return;
|
|
6447
|
+
}
|
|
6448
|
+
console.log(`Starting ${config.agents.length} agent config(s) in batch mode...`);
|
|
6449
|
+
await startBatchAgents(config, config.agents, pollIntervalMs, oauthToken, {
|
|
6450
|
+
versionOverride,
|
|
6451
|
+
verbose: opts.verbose,
|
|
6452
|
+
instancesOverride,
|
|
6453
|
+
agentOwner,
|
|
6454
|
+
userOrgs
|
|
6455
|
+
});
|
|
6512
6456
|
}
|
|
6513
6457
|
}
|
|
6514
6458
|
);
|
|
@@ -6799,11 +6743,10 @@ function resolveAgentCommand(toolName) {
|
|
|
6799
6743
|
if (cmd) return cmd;
|
|
6800
6744
|
}
|
|
6801
6745
|
}
|
|
6802
|
-
const
|
|
6803
|
-
if (
|
|
6804
|
-
const
|
|
6805
|
-
|
|
6806
|
-
return registryTool.commandTemplate.replaceAll("${MODEL}", modelName);
|
|
6746
|
+
const toolDef = getToolDef(toolName);
|
|
6747
|
+
if (toolDef) {
|
|
6748
|
+
const modelName = toolDef.models[0] ?? "";
|
|
6749
|
+
return toolDef.command.replaceAll("${MODEL}", modelName);
|
|
6807
6750
|
}
|
|
6808
6751
|
return null;
|
|
6809
6752
|
}
|
|
@@ -7050,7 +6993,7 @@ async function runDedupInit(options, deps = {}) {
|
|
|
7050
6993
|
const cmd = resolveCmd(options.agent);
|
|
7051
6994
|
if (!cmd) {
|
|
7052
6995
|
logError(
|
|
7053
|
-
`${icons.error} Unknown agent tool "${options.agent}". Available: ${
|
|
6996
|
+
`${icons.error} Unknown agent tool "${options.agent}". Available: ${loadToolDefs().map((t) => t.name).join(", ")}`
|
|
7054
6997
|
);
|
|
7055
6998
|
process.exitCode = 1;
|
|
7056
6999
|
return;
|
|
@@ -7105,13 +7048,13 @@ function agentRoleLabel(agent) {
|
|
|
7105
7048
|
return "reviewer+synthesizer";
|
|
7106
7049
|
}
|
|
7107
7050
|
function resolveToolBinary(toolName) {
|
|
7108
|
-
const
|
|
7109
|
-
return
|
|
7051
|
+
const def = getToolDef(toolName);
|
|
7052
|
+
return def?.binary ?? toolName;
|
|
7110
7053
|
}
|
|
7111
7054
|
function resolveCommand(agent) {
|
|
7112
7055
|
if (agent.command) return agent.command;
|
|
7113
|
-
const
|
|
7114
|
-
return
|
|
7056
|
+
const def = getToolDef(agent.tool);
|
|
7057
|
+
return def?.command ?? null;
|
|
7115
7058
|
}
|
|
7116
7059
|
async function checkConnectivity(platformUrl, fetchFn = fetch) {
|
|
7117
7060
|
const start = Date.now();
|
|
@@ -7216,7 +7159,7 @@ var statusCommand = new Command4("status").description("Show agent config, conne
|
|
|
7216
7159
|
});
|
|
7217
7160
|
|
|
7218
7161
|
// src/index.ts
|
|
7219
|
-
var program = new Command5().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version(`${"0.23.
|
|
7162
|
+
var program = new Command5().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version(`${"0.23.12"} (${"d2879c9"})`);
|
|
7220
7163
|
program.addCommand(agentCommand);
|
|
7221
7164
|
program.addCommand(authCommand());
|
|
7222
7165
|
program.addCommand(dedupCommand());
|