opencara 0.23.7 → 0.23.9
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 +107 -23
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -562,6 +562,15 @@ function ensureConfigDir() {
|
|
|
562
562
|
var DEFAULT_MAX_DIFF_SIZE_KB = 100;
|
|
563
563
|
var DEFAULT_MAX_CONSECUTIVE_ERRORS = 10;
|
|
564
564
|
var DEFAULT_MAX_REPO_SIZE_MB = 100;
|
|
565
|
+
var DEFAULT_COMMAND_TEST_TIMEOUT_MS = 1e4;
|
|
566
|
+
function parseDurationMs(value) {
|
|
567
|
+
if (typeof value !== "string") return null;
|
|
568
|
+
const secMatch = value.match(/^(\d+)s$/);
|
|
569
|
+
if (secMatch) return parseInt(secMatch[1], 10) * 1e3;
|
|
570
|
+
const minMatch = value.match(/^(\d+)m$/);
|
|
571
|
+
if (minMatch) return parseInt(minMatch[1], 10) * 6e4;
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
565
574
|
var VALID_REPO_MODES = ["public", "private", "whitelist", "blacklist"];
|
|
566
575
|
var REPO_PATTERN = /^[^/]+\/[^/]+$/;
|
|
567
576
|
var REPO_MODE_ALIASES = {
|
|
@@ -714,6 +723,20 @@ function parseAgents(data) {
|
|
|
714
723
|
agent.maxTasksPerDay = v;
|
|
715
724
|
}
|
|
716
725
|
}
|
|
726
|
+
if (typeof obj.liveness_timeout === "number") {
|
|
727
|
+
if (obj.liveness_timeout === 0) {
|
|
728
|
+
agent.livenessTimeout = 0;
|
|
729
|
+
} else {
|
|
730
|
+
const v = parsePositiveInt(obj.liveness_timeout);
|
|
731
|
+
if (v === null) {
|
|
732
|
+
console.warn(
|
|
733
|
+
`\u26A0 Config warning: agents[${i}].liveness_timeout must be a non-negative integer (seconds), got ${obj.liveness_timeout}. Value ignored.`
|
|
734
|
+
);
|
|
735
|
+
} else {
|
|
736
|
+
agent.livenessTimeout = v;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
717
740
|
const repoConfig = parseRepoConfig(obj, i);
|
|
718
741
|
if (repoConfig) agent.repos = repoConfig;
|
|
719
742
|
const synthesizeRepoConfig = parseRepoConfig(obj, i, "synthesize_repos");
|
|
@@ -783,6 +806,7 @@ function loadConfig() {
|
|
|
783
806
|
maxRepoSizeMb: DEFAULT_MAX_REPO_SIZE_MB,
|
|
784
807
|
codebaseDir: null,
|
|
785
808
|
codebaseTtl: null,
|
|
809
|
+
commandTestTimeoutMs: DEFAULT_COMMAND_TEST_TIMEOUT_MS,
|
|
786
810
|
agentCommand: null,
|
|
787
811
|
agents: null,
|
|
788
812
|
usageLimits: {
|
|
@@ -841,6 +865,23 @@ function loadConfig() {
|
|
|
841
865
|
maxRepoSizeMb: overrides.maxRepoSizeMb ?? (typeof data.max_repo_size_mb === "number" ? data.max_repo_size_mb : DEFAULT_MAX_REPO_SIZE_MB),
|
|
842
866
|
codebaseDir: typeof data.codebase_dir === "string" ? data.codebase_dir : null,
|
|
843
867
|
codebaseTtl: typeof data.codebase_ttl === "string" ? data.codebase_ttl : null,
|
|
868
|
+
commandTestTimeoutMs: (() => {
|
|
869
|
+
if (data.command_test_timeout === void 0) return DEFAULT_COMMAND_TEST_TIMEOUT_MS;
|
|
870
|
+
const ms = parseDurationMs(data.command_test_timeout);
|
|
871
|
+
if (ms === null) {
|
|
872
|
+
console.warn(
|
|
873
|
+
`\u26A0 Config warning: command_test_timeout must be a duration string like "10s" or "1m", got "${data.command_test_timeout}", using default (${DEFAULT_COMMAND_TEST_TIMEOUT_MS / 1e3}s)`
|
|
874
|
+
);
|
|
875
|
+
return DEFAULT_COMMAND_TEST_TIMEOUT_MS;
|
|
876
|
+
}
|
|
877
|
+
if (ms <= 0) {
|
|
878
|
+
console.warn(
|
|
879
|
+
`\u26A0 Config warning: command_test_timeout must be a positive duration, got "${data.command_test_timeout}", using default (${DEFAULT_COMMAND_TEST_TIMEOUT_MS / 1e3}s)`
|
|
880
|
+
);
|
|
881
|
+
return DEFAULT_COMMAND_TEST_TIMEOUT_MS;
|
|
882
|
+
}
|
|
883
|
+
return ms;
|
|
884
|
+
})(),
|
|
844
885
|
agentCommand: typeof data.agent_command === "string" ? data.agent_command : null,
|
|
845
886
|
agents: parseAgents(data),
|
|
846
887
|
usageLimits: {
|
|
@@ -1750,6 +1791,7 @@ var ToolTimeoutError = class extends Error {
|
|
|
1750
1791
|
};
|
|
1751
1792
|
var SIGKILL_GRACE_MS = 5e3;
|
|
1752
1793
|
var MIN_PARTIAL_RESULT_LENGTH = 50;
|
|
1794
|
+
var STDOUT_LIVENESS_TIMEOUT_MS = 3e5;
|
|
1753
1795
|
var MAX_STDERR_LENGTH = 1e3;
|
|
1754
1796
|
function validateCommandBinary(commandTemplate) {
|
|
1755
1797
|
const { command } = parseCommandTemplate(commandTemplate);
|
|
@@ -1846,7 +1888,7 @@ function parseTokenUsage(stdout, stderr) {
|
|
|
1846
1888
|
const estimated = estimateTokens(stdout);
|
|
1847
1889
|
return { tokens: estimated, parsed: false, input: 0, output: estimated };
|
|
1848
1890
|
}
|
|
1849
|
-
function executeTool(commandTemplate, prompt2, timeoutMs, signal, vars, cwd) {
|
|
1891
|
+
function executeTool(commandTemplate, prompt2, timeoutMs, signal, vars, cwd, livenessTimeoutMs) {
|
|
1850
1892
|
const promptViaArg = commandTemplate.includes("${PROMPT}");
|
|
1851
1893
|
const allVars = { ...vars, PROMPT: prompt2 };
|
|
1852
1894
|
if (cwd && !allVars["CODEBASE_DIR"]) {
|
|
@@ -1866,7 +1908,12 @@ function executeTool(commandTemplate, prompt2, timeoutMs, signal, vars, cwd) {
|
|
|
1866
1908
|
let stderr = "";
|
|
1867
1909
|
let settled = false;
|
|
1868
1910
|
let sigkillTimer;
|
|
1911
|
+
let killedByLiveness = false;
|
|
1912
|
+
const effectiveLivenessMs = livenessTimeoutMs === void 0 ? STDOUT_LIVENESS_TIMEOUT_MS : livenessTimeoutMs;
|
|
1913
|
+
let killScheduled = false;
|
|
1869
1914
|
function scheduleKillEscalation() {
|
|
1915
|
+
if (killScheduled) return;
|
|
1916
|
+
killScheduled = true;
|
|
1870
1917
|
child.kill("SIGTERM");
|
|
1871
1918
|
if (sigkillTimer) clearTimeout(sigkillTimer);
|
|
1872
1919
|
sigkillTimer = setTimeout(() => {
|
|
@@ -1876,8 +1923,26 @@ function executeTool(commandTemplate, prompt2, timeoutMs, signal, vars, cwd) {
|
|
|
1876
1923
|
}, SIGKILL_GRACE_MS);
|
|
1877
1924
|
}
|
|
1878
1925
|
const timer = setTimeout(scheduleKillEscalation, timeoutMs);
|
|
1926
|
+
let livenessTimer;
|
|
1927
|
+
if (effectiveLivenessMs > 0) {
|
|
1928
|
+
livenessTimer = setTimeout(() => {
|
|
1929
|
+
if (!settled) {
|
|
1930
|
+
killedByLiveness = true;
|
|
1931
|
+
scheduleKillEscalation();
|
|
1932
|
+
}
|
|
1933
|
+
}, effectiveLivenessMs);
|
|
1934
|
+
}
|
|
1879
1935
|
child.stdout?.on("data", (chunk) => {
|
|
1880
1936
|
stdout += chunk.toString();
|
|
1937
|
+
if (livenessTimer) {
|
|
1938
|
+
clearTimeout(livenessTimer);
|
|
1939
|
+
livenessTimer = setTimeout(() => {
|
|
1940
|
+
if (!settled) {
|
|
1941
|
+
killedByLiveness = true;
|
|
1942
|
+
scheduleKillEscalation();
|
|
1943
|
+
}
|
|
1944
|
+
}, effectiveLivenessMs);
|
|
1945
|
+
}
|
|
1881
1946
|
});
|
|
1882
1947
|
child.stderr?.on("data", (chunk) => {
|
|
1883
1948
|
stderr += chunk.toString();
|
|
@@ -1893,6 +1958,7 @@ function executeTool(commandTemplate, prompt2, timeoutMs, signal, vars, cwd) {
|
|
|
1893
1958
|
}
|
|
1894
1959
|
function cleanup() {
|
|
1895
1960
|
clearTimeout(timer);
|
|
1961
|
+
if (livenessTimer) clearTimeout(livenessTimer);
|
|
1896
1962
|
if (sigkillTimer) clearTimeout(sigkillTimer);
|
|
1897
1963
|
if (onAbort && signal) {
|
|
1898
1964
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -1917,11 +1983,19 @@ function executeTool(commandTemplate, prompt2, timeoutMs, signal, vars, cwd) {
|
|
|
1917
1983
|
return;
|
|
1918
1984
|
}
|
|
1919
1985
|
if (sig === "SIGTERM" || sig === "SIGKILL") {
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1986
|
+
if (killedByLiveness) {
|
|
1987
|
+
reject(
|
|
1988
|
+
new ToolTimeoutError(
|
|
1989
|
+
`Tool "${command}" killed: no stdout for ${Math.round(effectiveLivenessMs / 1e3)}s (process may be stuck)`
|
|
1990
|
+
)
|
|
1991
|
+
);
|
|
1992
|
+
} else {
|
|
1993
|
+
reject(
|
|
1994
|
+
new ToolTimeoutError(
|
|
1995
|
+
`Tool "${command}" timed out after ${Math.round(timeoutMs / 1e3)}s`
|
|
1996
|
+
)
|
|
1997
|
+
);
|
|
1998
|
+
}
|
|
1925
1999
|
return;
|
|
1926
2000
|
}
|
|
1927
2001
|
if (code !== 0) {
|
|
@@ -1968,11 +2042,11 @@ function executeTool(commandTemplate, prompt2, timeoutMs, signal, vars, cwd) {
|
|
|
1968
2042
|
});
|
|
1969
2043
|
}
|
|
1970
2044
|
var TEST_COMMAND_PROMPT = "Respond with: OK";
|
|
1971
|
-
var
|
|
1972
|
-
async function testCommand(commandTemplate) {
|
|
2045
|
+
var DEFAULT_TEST_COMMAND_TIMEOUT_MS = 1e4;
|
|
2046
|
+
async function testCommand(commandTemplate, timeoutMs = DEFAULT_TEST_COMMAND_TIMEOUT_MS) {
|
|
1973
2047
|
const start = Date.now();
|
|
1974
2048
|
try {
|
|
1975
|
-
await executeTool(commandTemplate, TEST_COMMAND_PROMPT,
|
|
2049
|
+
await executeTool(commandTemplate, TEST_COMMAND_PROMPT, timeoutMs);
|
|
1976
2050
|
return { ok: true, elapsedMs: Date.now() - start };
|
|
1977
2051
|
} catch (err) {
|
|
1978
2052
|
const elapsed = Date.now() - start;
|
|
@@ -1980,7 +2054,7 @@ async function testCommand(commandTemplate) {
|
|
|
1980
2054
|
return {
|
|
1981
2055
|
ok: false,
|
|
1982
2056
|
elapsedMs: elapsed,
|
|
1983
|
-
error: `command timed out after ${
|
|
2057
|
+
error: `command timed out after ${timeoutMs / 1e3}s`
|
|
1984
2058
|
};
|
|
1985
2059
|
}
|
|
1986
2060
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -2500,7 +2574,8 @@ ${userMessage}`;
|
|
|
2500
2574
|
effectiveTimeout,
|
|
2501
2575
|
abortController.signal,
|
|
2502
2576
|
void 0,
|
|
2503
|
-
deps.codebaseDir ?? void 0
|
|
2577
|
+
deps.codebaseDir ?? void 0,
|
|
2578
|
+
deps.livenessTimeoutMs
|
|
2504
2579
|
);
|
|
2505
2580
|
const { verdict, review } = extractVerdict(result.stdout);
|
|
2506
2581
|
const inputTokens = result.tokensParsed ? 0 : estimateTokens(fullPrompt);
|
|
@@ -2615,7 +2690,8 @@ ${userMessage}`;
|
|
|
2615
2690
|
effectiveTimeout,
|
|
2616
2691
|
abortController.signal,
|
|
2617
2692
|
void 0,
|
|
2618
|
-
deps.codebaseDir ?? void 0
|
|
2693
|
+
deps.codebaseDir ?? void 0,
|
|
2694
|
+
deps.livenessTimeoutMs
|
|
2619
2695
|
);
|
|
2620
2696
|
const { verdict, review } = extractVerdict(result.stdout);
|
|
2621
2697
|
const flaggedReviews = extractFlaggedReviews(result.stdout);
|
|
@@ -5663,7 +5739,7 @@ function sleep2(ms, signal) {
|
|
|
5663
5739
|
async function startAgent(agentId, platformUrl, agentInfo, reviewDeps, consumptionDeps, options) {
|
|
5664
5740
|
const client = new ApiClient(platformUrl, {
|
|
5665
5741
|
authToken: options?.authToken,
|
|
5666
|
-
cliVersion: "0.23.
|
|
5742
|
+
cliVersion: "0.23.9",
|
|
5667
5743
|
versionOverride: options?.versionOverride,
|
|
5668
5744
|
onTokenRefresh: options?.onTokenRefresh
|
|
5669
5745
|
});
|
|
@@ -5697,7 +5773,7 @@ async function startAgent(agentId, platformUrl, agentInfo, reviewDeps, consumpti
|
|
|
5697
5773
|
}
|
|
5698
5774
|
if (reviewDeps.commandTemplate && !options?.routerRelay) {
|
|
5699
5775
|
log("Testing command...");
|
|
5700
|
-
const result = await testCommand(reviewDeps.commandTemplate);
|
|
5776
|
+
const result = await testCommand(reviewDeps.commandTemplate, options?.commandTestTimeoutMs);
|
|
5701
5777
|
if (result.ok) {
|
|
5702
5778
|
log(`${icons.success} Command test ok (${(result.elapsedMs / 1e3).toFixed(1)}s)`);
|
|
5703
5779
|
} else {
|
|
@@ -5959,7 +6035,7 @@ async function startBatchAgents(config, agents, pollIntervalMs, oauthToken, opti
|
|
|
5959
6035
|
const { versionOverride, verbose, instancesOverride, agentOwner, userOrgs } = options;
|
|
5960
6036
|
const client = new ApiClient(config.platformUrl, {
|
|
5961
6037
|
authToken: oauthToken,
|
|
5962
|
-
cliVersion: "0.23.
|
|
6038
|
+
cliVersion: "0.23.9",
|
|
5963
6039
|
versionOverride,
|
|
5964
6040
|
onTokenRefresh: () => getValidToken(config.platformUrl, { configPath: config.authFile })
|
|
5965
6041
|
});
|
|
@@ -6005,7 +6081,8 @@ async function startBatchAgents(config, agents, pollIntervalMs, oauthToken, opti
|
|
|
6005
6081
|
commandTemplate,
|
|
6006
6082
|
maxDiffSizeKb: config.maxDiffSizeKb,
|
|
6007
6083
|
maxRepoSizeMb: config.maxRepoSizeMb,
|
|
6008
|
-
codebaseDir
|
|
6084
|
+
codebaseDir,
|
|
6085
|
+
livenessTimeoutMs: agentConfig.livenessTimeout != null ? agentConfig.livenessTimeout * 1e3 : void 0
|
|
6009
6086
|
};
|
|
6010
6087
|
const session = createSessionTracker();
|
|
6011
6088
|
const usageTracker = new UsageTracker();
|
|
@@ -6054,7 +6131,10 @@ async function startBatchAgents(config, agents, pollIntervalMs, oauthToken, opti
|
|
|
6054
6131
|
await Promise.all(
|
|
6055
6132
|
agentStates.filter((state) => state.reviewDeps.commandTemplate && !state.routerRelay).map(async (state) => {
|
|
6056
6133
|
state.logger.log("Testing command...");
|
|
6057
|
-
const result = await testCommand(
|
|
6134
|
+
const result = await testCommand(
|
|
6135
|
+
state.reviewDeps.commandTemplate,
|
|
6136
|
+
config.commandTestTimeoutMs
|
|
6137
|
+
);
|
|
6058
6138
|
if (result.ok) {
|
|
6059
6139
|
state.logger.log(
|
|
6060
6140
|
`${icons.success} Command test ok (${(result.elapsedMs / 1e3).toFixed(1)}s)`
|
|
@@ -6161,7 +6241,8 @@ async function startAgentRouter() {
|
|
|
6161
6241
|
commandTemplate: commandTemplate ?? "",
|
|
6162
6242
|
maxDiffSizeKb: config.maxDiffSizeKb,
|
|
6163
6243
|
maxRepoSizeMb: config.maxRepoSizeMb,
|
|
6164
|
-
codebaseDir
|
|
6244
|
+
codebaseDir,
|
|
6245
|
+
livenessTimeoutMs: agentConfig?.livenessTimeout != null ? agentConfig.livenessTimeout * 1e3 : void 0
|
|
6165
6246
|
};
|
|
6166
6247
|
const session = createSessionTracker();
|
|
6167
6248
|
const usageTracker = new UsageTracker();
|
|
@@ -6197,7 +6278,8 @@ async function startAgentRouter() {
|
|
|
6197
6278
|
userOrgs,
|
|
6198
6279
|
usageLimits: config.usageLimits,
|
|
6199
6280
|
versionOverride,
|
|
6200
|
-
codebaseTtl: config.codebaseTtl
|
|
6281
|
+
codebaseTtl: config.codebaseTtl,
|
|
6282
|
+
commandTestTimeoutMs: config.commandTestTimeoutMs
|
|
6201
6283
|
}
|
|
6202
6284
|
);
|
|
6203
6285
|
router.stop();
|
|
@@ -6228,7 +6310,8 @@ function startAgentByIndex(config, agentIndex, pollIntervalMs, oauthToken, versi
|
|
|
6228
6310
|
commandTemplate,
|
|
6229
6311
|
maxDiffSizeKb: config.maxDiffSizeKb,
|
|
6230
6312
|
maxRepoSizeMb: config.maxRepoSizeMb,
|
|
6231
|
-
codebaseDir
|
|
6313
|
+
codebaseDir,
|
|
6314
|
+
livenessTimeoutMs: agentConfig?.livenessTimeout != null ? agentConfig.livenessTimeout * 1e3 : void 0
|
|
6232
6315
|
};
|
|
6233
6316
|
const model = agentConfig?.model ?? "unknown";
|
|
6234
6317
|
const tool = agentConfig?.tool ?? "unknown";
|
|
@@ -6274,7 +6357,8 @@ function startAgentByIndex(config, agentIndex, pollIntervalMs, oauthToken, versi
|
|
|
6274
6357
|
codebaseTtl: config.codebaseTtl,
|
|
6275
6358
|
verbose,
|
|
6276
6359
|
agentOwner,
|
|
6277
|
-
userOrgs
|
|
6360
|
+
userOrgs,
|
|
6361
|
+
commandTestTimeoutMs: config.commandTestTimeoutMs
|
|
6278
6362
|
}
|
|
6279
6363
|
).finally(() => {
|
|
6280
6364
|
routerRelay?.stop();
|
|
@@ -6302,7 +6386,7 @@ agentCommand.command("start").description("Start agents in polling mode").option
|
|
|
6302
6386
|
}
|
|
6303
6387
|
config = loadConfig();
|
|
6304
6388
|
}
|
|
6305
|
-
console.log(formatVersionBanner("0.23.
|
|
6389
|
+
console.log(formatVersionBanner("0.23.9", "84eda5a"));
|
|
6306
6390
|
if (config.agents && config.agents.length > 0) {
|
|
6307
6391
|
const toolEntries = config.agents.map((a) => ({
|
|
6308
6392
|
tool: a.tool,
|
|
@@ -7125,7 +7209,7 @@ var statusCommand = new Command4("status").description("Show agent config, conne
|
|
|
7125
7209
|
});
|
|
7126
7210
|
|
|
7127
7211
|
// src/index.ts
|
|
7128
|
-
var program = new Command5().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version(`${"0.23.
|
|
7212
|
+
var program = new Command5().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version(`${"0.23.9"} (${"84eda5a"})`);
|
|
7129
7213
|
program.addCommand(agentCommand);
|
|
7130
7214
|
program.addCommand(authCommand());
|
|
7131
7215
|
program.addCommand(dedupCommand());
|