opencara 0.19.6 → 0.19.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.
Files changed (2) hide show
  1. package/dist/index.js +113 -119
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5260,7 +5260,7 @@ function sleep2(ms, signal) {
5260
5260
  async function startAgent(agentId, platformUrl, agentInfo, reviewDeps, consumptionDeps, options) {
5261
5261
  const client = new ApiClient(platformUrl, {
5262
5262
  authToken: options?.authToken,
5263
- cliVersion: "0.19.6",
5263
+ cliVersion: "0.19.7",
5264
5264
  versionOverride: options?.versionOverride,
5265
5265
  onTokenRefresh: options?.onTokenRefresh
5266
5266
  });
@@ -5547,7 +5547,7 @@ async function startBatchAgents(config, agents, pollIntervalMs, oauthToken, opti
5547
5547
  const { versionOverride, verbose, instancesOverride, agentOwner, userOrgs } = options;
5548
5548
  const client = new ApiClient(config.platformUrl, {
5549
5549
  authToken: oauthToken,
5550
- cliVersion: "0.19.6",
5550
+ cliVersion: "0.19.7",
5551
5551
  versionOverride,
5552
5552
  onTokenRefresh: () => getValidToken(config.platformUrl, { configPath: config.authFile })
5553
5553
  });
@@ -6143,104 +6143,114 @@ function authCommand() {
6143
6143
  }
6144
6144
 
6145
6145
  // src/commands/dedup.ts
6146
+ import { execFileSync as execFileSync8 } from "child_process";
6146
6147
  import { Command as Command3 } from "commander";
6147
6148
  import pc3 from "picocolors";
6148
6149
  var DEFAULT_RECENT_DAYS = 30;
6149
- var PER_PAGE = 100;
6150
6150
  var OPEN_MARKER = "<!-- opencara-dedup-index:open -->";
6151
6151
  var RECENT_MARKER = "<!-- opencara-dedup-index:recent -->";
6152
6152
  var ARCHIVED_MARKER = "<!-- opencara-dedup-index:archived -->";
6153
- async function fetchRepoFile(owner, repo, path10, token, fetchFn = fetch) {
6154
- const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path10}`;
6155
- const res = await fetchFn(url, {
6156
- headers: {
6157
- Authorization: `Bearer ${token}`,
6158
- Accept: "application/vnd.github.raw+json"
6159
- }
6153
+ function defaultExecGh(args) {
6154
+ return execFileSync8("gh", args, {
6155
+ encoding: "utf-8",
6156
+ timeout: 3e4,
6157
+ maxBuffer: 50 * 1024 * 1024,
6158
+ stdio: ["ignore", "pipe", "pipe"]
6160
6159
  });
6161
- if (res.status === 404) return null;
6162
- if (!res.ok) throw new Error(`GitHub API error: ${res.status} fetching ${path10}`);
6163
- return res.text();
6164
6160
  }
6165
- async function fetchAllPRs(owner, repo, token, fetchFn = fetch, log) {
6166
- const items = [];
6167
- let page = 1;
6168
- while (true) {
6169
- const url = `https://api.github.com/repos/${owner}/${repo}/pulls?state=all&per_page=${PER_PAGE}&page=${page}&sort=created&direction=desc`;
6170
- const res = await fetchFn(url, {
6171
- headers: {
6172
- Authorization: `Bearer ${token}`,
6173
- Accept: "application/vnd.github+json"
6174
- }
6175
- });
6176
- if (!res.ok) throw new Error(`GitHub API error: ${res.status} fetching PRs page ${page}`);
6177
- const batch = await res.json();
6178
- items.push(...batch);
6179
- if (log) log(` Fetched ${items.length} PRs...`);
6180
- if (batch.length < PER_PAGE) break;
6181
- page++;
6182
- }
6161
+ function fetchRepoFile(owner, repo, path10, execGh = defaultExecGh) {
6162
+ try {
6163
+ return execGh([
6164
+ "api",
6165
+ `repos/${owner}/${repo}/contents/${path10}`,
6166
+ "-H",
6167
+ "Accept: application/vnd.github.raw+json"
6168
+ ]);
6169
+ } catch (err) {
6170
+ const message = String(err.stderr ?? err);
6171
+ if (message.includes("404") || message.includes("Not Found")) return null;
6172
+ throw new Error(`gh API error fetching ${path10}: ${message}`);
6173
+ }
6174
+ }
6175
+ function fetchAllPRs(owner, repo, execGh = defaultExecGh, log) {
6176
+ const output = execGh([
6177
+ "pr",
6178
+ "list",
6179
+ "--repo",
6180
+ `${owner}/${repo}`,
6181
+ "--state",
6182
+ "all",
6183
+ "--limit",
6184
+ "9999",
6185
+ "--json",
6186
+ "number,title,state,labels,closedAt,mergedAt"
6187
+ ]);
6188
+ const raw = JSON.parse(output);
6189
+ const items = raw.map((pr) => ({
6190
+ number: pr.number,
6191
+ title: pr.title,
6192
+ state: pr.state === "MERGED" ? "closed" : pr.state.toLowerCase(),
6193
+ labels: pr.labels,
6194
+ closed_at: pr.closedAt || null,
6195
+ merged_at: pr.mergedAt || null
6196
+ }));
6197
+ if (log) log(` Fetched ${items.length} PRs...`);
6183
6198
  return items;
6184
6199
  }
6185
- async function fetchAllIssues(owner, repo, token, fetchFn = fetch, log) {
6186
- const items = [];
6187
- let page = 1;
6188
- while (true) {
6189
- const url = `https://api.github.com/repos/${owner}/${repo}/issues?state=all&per_page=${PER_PAGE}&page=${page}&sort=created&direction=desc`;
6190
- const res = await fetchFn(url, {
6191
- headers: {
6192
- Authorization: `Bearer ${token}`,
6193
- Accept: "application/vnd.github+json"
6194
- }
6195
- });
6196
- if (!res.ok) throw new Error(`GitHub API error: ${res.status} fetching issues page ${page}`);
6197
- const batch = await res.json();
6198
- const issuesOnly = batch.filter((item) => !item.pull_request);
6199
- items.push(...issuesOnly);
6200
- if (log) log(` Fetched ${items.length} issues...`);
6201
- if (batch.length < PER_PAGE) break;
6202
- page++;
6203
- }
6200
+ function fetchAllIssues(owner, repo, execGh = defaultExecGh, log) {
6201
+ const output = execGh([
6202
+ "issue",
6203
+ "list",
6204
+ "--repo",
6205
+ `${owner}/${repo}`,
6206
+ "--state",
6207
+ "all",
6208
+ "--limit",
6209
+ "9999",
6210
+ "--json",
6211
+ "number,title,state,labels,closedAt"
6212
+ ]);
6213
+ const raw = JSON.parse(output);
6214
+ const items = raw.map((issue) => ({
6215
+ number: issue.number,
6216
+ title: issue.title,
6217
+ state: issue.state.toLowerCase(),
6218
+ labels: issue.labels,
6219
+ closed_at: issue.closedAt || null
6220
+ }));
6221
+ if (log) log(` Fetched ${items.length} issues...`);
6204
6222
  return items;
6205
6223
  }
6206
- async function fetchIssueComments2(owner, repo, issueNumber, token, fetchFn = fetch) {
6207
- const url = `https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}/comments?per_page=100`;
6208
- const res = await fetchFn(url, {
6209
- headers: {
6210
- Authorization: `Bearer ${token}`,
6211
- Accept: "application/vnd.github+json"
6212
- }
6213
- });
6214
- if (!res.ok) throw new Error(`GitHub API error: ${res.status} fetching comments`);
6215
- return await res.json();
6216
- }
6217
- async function createIssueComment(owner, repo, issueNumber, body, token, fetchFn = fetch) {
6218
- const url = `https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}/comments`;
6219
- const res = await fetchFn(url, {
6220
- method: "POST",
6221
- headers: {
6222
- Authorization: `Bearer ${token}`,
6223
- Accept: "application/vnd.github+json",
6224
- "Content-Type": "application/json"
6225
- },
6226
- body: JSON.stringify({ body })
6227
- });
6228
- if (!res.ok) throw new Error(`GitHub API error: ${res.status} creating comment`);
6229
- const data = await res.json();
6230
- return data.id;
6231
- }
6232
- async function updateIssueComment(owner, repo, commentId, body, token, fetchFn = fetch) {
6233
- const url = `https://api.github.com/repos/${owner}/${repo}/issues/comments/${commentId}`;
6234
- const res = await fetchFn(url, {
6235
- method: "PATCH",
6236
- headers: {
6237
- Authorization: `Bearer ${token}`,
6238
- Accept: "application/vnd.github+json",
6239
- "Content-Type": "application/json"
6240
- },
6241
- body: JSON.stringify({ body })
6242
- });
6243
- if (!res.ok) throw new Error(`GitHub API error: ${res.status} updating comment`);
6224
+ function fetchIssueComments2(owner, repo, issueNumber, execGh = defaultExecGh) {
6225
+ const output = execGh([
6226
+ "api",
6227
+ "--paginate",
6228
+ `repos/${owner}/${repo}/issues/${issueNumber}/comments`
6229
+ ]);
6230
+ return JSON.parse(output);
6231
+ }
6232
+ function createIssueComment(owner, repo, issueNumber, body, execGh = defaultExecGh) {
6233
+ const output = execGh([
6234
+ "api",
6235
+ `repos/${owner}/${repo}/issues/${issueNumber}/comments`,
6236
+ "-X",
6237
+ "POST",
6238
+ "-f",
6239
+ `body=${body}`,
6240
+ "--jq",
6241
+ ".id"
6242
+ ]);
6243
+ return parseInt(output.trim(), 10);
6244
+ }
6245
+ function updateIssueComment(owner, repo, commentId, body, execGh = defaultExecGh) {
6246
+ execGh([
6247
+ "api",
6248
+ `repos/${owner}/${repo}/issues/comments/${commentId}`,
6249
+ "-X",
6250
+ "PATCH",
6251
+ "-f",
6252
+ `body=${body}`
6253
+ ]);
6244
6254
  }
6245
6255
  function formatEntry(item, compact = false) {
6246
6256
  if (compact) {
@@ -6350,19 +6360,19 @@ function findIndexComments(comments) {
6350
6360
  return { open, recent, archived };
6351
6361
  }
6352
6362
  async function initIndex(opts) {
6353
- const { owner, repo, indexIssue, kind, recentDays, dryRun, token } = opts;
6354
- const fetchFn = opts.fetchFn ?? fetch;
6363
+ const { owner, repo, indexIssue, kind, recentDays, dryRun } = opts;
6364
+ const execGh = opts.execGh ?? defaultExecGh;
6355
6365
  const log = opts.log ?? (() => {
6356
6366
  });
6357
6367
  const runTool = opts.runTool ?? executeTool;
6358
6368
  log(`Scanning ${kind}...`);
6359
- const items = kind === "prs" ? await fetchAllPRs(owner, repo, token, fetchFn, log) : await fetchAllIssues(owner, repo, token, fetchFn, log);
6369
+ const items = kind === "prs" ? fetchAllPRs(owner, repo, execGh, log) : fetchAllIssues(owner, repo, execGh, log);
6360
6370
  log(`${icons.info} Found ${items.length} ${kind}.`);
6361
6371
  const { open, recentlyClosed, archived } = categorizeItems(items, recentDays);
6362
6372
  log(
6363
6373
  ` ${open.length} open, ${recentlyClosed.length} recently closed, ${archived.length} archived`
6364
6374
  );
6365
- const comments = await fetchIssueComments2(owner, repo, indexIssue, token, fetchFn);
6375
+ const comments = fetchIssueComments2(owner, repo, indexIssue, execGh);
6366
6376
  const found = findIndexComments(comments);
6367
6377
  const existingOpen = found.open ? parseExistingNumbers(found.open.body) : /* @__PURE__ */ new Set();
6368
6378
  const existingRecent = found.recent ? parseExistingNumbers(found.recent.body) : /* @__PURE__ */ new Set();
@@ -6431,19 +6441,19 @@ ${icons.info} Dry run \u2014 would update index issue #${indexIssue}:`);
6431
6441
  }
6432
6442
  log(`Populating index issue #${indexIssue}...`);
6433
6443
  if (found.open) {
6434
- await updateIssueComment(owner, repo, found.open.id, openBody, token, fetchFn);
6444
+ updateIssueComment(owner, repo, found.open.id, openBody, execGh);
6435
6445
  } else {
6436
- await createIssueComment(owner, repo, indexIssue, openBody, token, fetchFn);
6446
+ createIssueComment(owner, repo, indexIssue, openBody, execGh);
6437
6447
  }
6438
6448
  if (found.recent) {
6439
- await updateIssueComment(owner, repo, found.recent.id, recentBody, token, fetchFn);
6449
+ updateIssueComment(owner, repo, found.recent.id, recentBody, execGh);
6440
6450
  } else {
6441
- await createIssueComment(owner, repo, indexIssue, recentBody, token, fetchFn);
6451
+ createIssueComment(owner, repo, indexIssue, recentBody, execGh);
6442
6452
  }
6443
6453
  if (found.archived) {
6444
- await updateIssueComment(owner, repo, found.archived.id, archivedBody, token, fetchFn);
6454
+ updateIssueComment(owner, repo, found.archived.id, archivedBody, execGh);
6445
6455
  } else {
6446
- await createIssueComment(owner, repo, indexIssue, archivedBody, token, fetchFn);
6456
+ createIssueComment(owner, repo, indexIssue, archivedBody, execGh);
6447
6457
  }
6448
6458
  log(
6449
6459
  `${icons.success} Index populated: ${open.length} open, ${recentlyClosed.length} recent, ${archived.length} archived (${newEntries} new entries)`
@@ -6456,22 +6466,10 @@ ${icons.info} Dry run \u2014 would update index issue #${indexIssue}:`);
6456
6466
  };
6457
6467
  }
6458
6468
  async function runDedupInit(options, deps = {}) {
6459
- const fetchFn = deps.fetchFn ?? fetch;
6469
+ const execGh = deps.execGh ?? defaultExecGh;
6460
6470
  const log = deps.log ?? console.log;
6461
6471
  const logError = deps.logError ?? console.error;
6462
6472
  const resolveCmd = deps.resolveAgentCommandFn ?? resolveAgentCommand;
6463
- const ensureAuthFn = deps.ensureAuthFn ?? (() => ensureAuth("https://opencara.workers.dev"));
6464
- let token;
6465
- try {
6466
- token = await ensureAuthFn();
6467
- } catch (err) {
6468
- if (err instanceof AuthError) {
6469
- logError(`${icons.error} ${err.message}`);
6470
- process.exitCode = 1;
6471
- return;
6472
- }
6473
- throw err;
6474
- }
6475
6473
  if (!options.repo) {
6476
6474
  logError(`${icons.error} --repo is required. Usage: opencara dedup init --repo owner/repo`);
6477
6475
  process.exitCode = 1;
@@ -6490,7 +6488,7 @@ async function runDedupInit(options, deps = {}) {
6490
6488
  return;
6491
6489
  }
6492
6490
  log(`Fetching .opencara.toml from ${options.repo}...`);
6493
- const tomlContent = await fetchRepoFile(owner, repo, ".opencara.toml", token, fetchFn);
6491
+ const tomlContent = fetchRepoFile(owner, repo, ".opencara.toml", execGh);
6494
6492
  if (!tomlContent) {
6495
6493
  logError(`${icons.error} No .opencara.toml found in ${options.repo}`);
6496
6494
  process.exitCode = 1;
@@ -6552,9 +6550,8 @@ ${pc3.bold(`Initializing ${target.kind} dedup index (issue #${target.indexIssue}
6552
6550
  kind: target.kind,
6553
6551
  recentDays,
6554
6552
  dryRun: options.dryRun ?? false,
6555
- token,
6556
6553
  agentCommandTemplate,
6557
- fetchFn,
6554
+ execGh,
6558
6555
  log,
6559
6556
  runTool: deps.runTool
6560
6557
  });
@@ -6567,10 +6564,7 @@ function dedupCommand() {
6567
6564
  "Use AI agent to generate enriched descriptions (e.g., claude, codex, gemini, qwen)"
6568
6565
  ).action(
6569
6566
  async (options) => {
6570
- const config = loadConfig();
6571
- await runDedupInit(options, {
6572
- ensureAuthFn: () => ensureAuth(config.platformUrl, { configPath: config.authFile })
6573
- });
6567
+ await runDedupInit(options);
6574
6568
  }
6575
6569
  );
6576
6570
  return dedup;
@@ -6704,7 +6698,7 @@ var statusCommand = new Command4("status").description("Show agent config, conne
6704
6698
  });
6705
6699
 
6706
6700
  // src/index.ts
6707
- var program = new Command5().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version("0.19.6");
6701
+ var program = new Command5().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version("0.19.7");
6708
6702
  program.addCommand(agentCommand);
6709
6703
  program.addCommand(authCommand());
6710
6704
  program.addCommand(dedupCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencara",
3
- "version": "0.19.6",
3
+ "version": "0.19.7",
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",