@evident-ai/cli 3.5.2-dev.35de651 → 3.5.2-dev.3f3949d
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 +431 -113
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -315,14 +315,14 @@ function blank() {
|
|
|
315
315
|
console.log();
|
|
316
316
|
}
|
|
317
317
|
function waitForEnter(prompt = "Press Enter to continue...") {
|
|
318
|
-
return new Promise((
|
|
318
|
+
return new Promise((resolve5) => {
|
|
319
319
|
process.stdout.write(chalk.dim(prompt));
|
|
320
320
|
const handler = () => {
|
|
321
321
|
process.stdin.removeListener("data", handler);
|
|
322
322
|
process.stdin.setRawMode?.(false);
|
|
323
323
|
process.stdin.pause();
|
|
324
324
|
console.log();
|
|
325
|
-
|
|
325
|
+
resolve5();
|
|
326
326
|
};
|
|
327
327
|
if (process.stdin.isTTY) {
|
|
328
328
|
process.stdin.setRawMode?.(true);
|
|
@@ -332,7 +332,7 @@ function waitForEnter(prompt = "Press Enter to continue...") {
|
|
|
332
332
|
});
|
|
333
333
|
}
|
|
334
334
|
function sleep(ms) {
|
|
335
|
-
return new Promise((
|
|
335
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
336
336
|
}
|
|
337
337
|
|
|
338
338
|
// src/commands/login.ts
|
|
@@ -410,19 +410,19 @@ async function tokenLogin() {
|
|
|
410
410
|
);
|
|
411
411
|
blank();
|
|
412
412
|
process.stdout.write("Paste token: ");
|
|
413
|
-
const token = await new Promise((
|
|
413
|
+
const token = await new Promise((resolve5) => {
|
|
414
414
|
let data = "";
|
|
415
415
|
process.stdin.setEncoding("utf8");
|
|
416
416
|
process.stdin.on("data", (chunk) => {
|
|
417
417
|
data += chunk;
|
|
418
418
|
});
|
|
419
419
|
process.stdin.on("end", () => {
|
|
420
|
-
|
|
420
|
+
resolve5(data.trim());
|
|
421
421
|
});
|
|
422
422
|
if (process.stdin.isTTY) {
|
|
423
423
|
process.stdin.once("data", (chunk) => {
|
|
424
424
|
process.stdin.pause();
|
|
425
|
-
|
|
425
|
+
resolve5(chunk.toString().trim());
|
|
426
426
|
});
|
|
427
427
|
process.stdin.resume();
|
|
428
428
|
}
|
|
@@ -1158,7 +1158,7 @@ async function claudeUsage() {
|
|
|
1158
1158
|
// src/commands/run.ts
|
|
1159
1159
|
import { chmodSync as chmodSync3, existsSync as existsSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "node:fs";
|
|
1160
1160
|
import { homedir as homedir7 } from "node:os";
|
|
1161
|
-
import { isAbsolute as
|
|
1161
|
+
import { isAbsolute as isAbsolute4, join as join12, parse, resolve as resolvePath2 } from "node:path";
|
|
1162
1162
|
import chalk7 from "chalk";
|
|
1163
1163
|
|
|
1164
1164
|
// ../../packages/types/src/agents/index.ts
|
|
@@ -1322,7 +1322,7 @@ import ora4 from "ora";
|
|
|
1322
1322
|
import { select as select4 } from "@inquirer/prompts";
|
|
1323
1323
|
|
|
1324
1324
|
// src/lib/telemetry.ts
|
|
1325
|
-
var CLI_VERSION = (true ? "3.5.2-dev.
|
|
1325
|
+
var CLI_VERSION = (true ? "3.5.2-dev.3f3949d" : void 0) ?? process.env.npm_package_version ?? "unknown";
|
|
1326
1326
|
function getCliVersion() {
|
|
1327
1327
|
return CLI_VERSION;
|
|
1328
1328
|
}
|
|
@@ -1810,6 +1810,263 @@ async function reclaimSessionDbSpace(input) {
|
|
|
1810
1810
|
}
|
|
1811
1811
|
}
|
|
1812
1812
|
|
|
1813
|
+
// src/lib/opencode/dependency-install-diagnostic.ts
|
|
1814
|
+
import { lstatSync, realpathSync } from "node:fs";
|
|
1815
|
+
import { basename, dirname as dirname3, isAbsolute, join as join2, relative, resolve, sep } from "node:path";
|
|
1816
|
+
var DEPENDENCY_INSTALL_FAILURE_MESSAGE = "background dependency install failed";
|
|
1817
|
+
var MAX_DISTINCT_DIAGNOSTICS = 32;
|
|
1818
|
+
var EFFECT = "OpenCode could not install background dependencies for this scope; its plugins and tools may be unavailable";
|
|
1819
|
+
function readOpenCodeLogField(line, key) {
|
|
1820
|
+
const match = line.match(new RegExp(`(?:^|\\s)${key}=(?:"((?:\\\\.|[^"])*)"|(\\S+))`));
|
|
1821
|
+
if (!match) return void 0;
|
|
1822
|
+
if (match[1] === void 0) return match[2];
|
|
1823
|
+
return match[1].replace(/\\(["\\n])/g, (_match, escaped) => {
|
|
1824
|
+
return escaped === "n" ? "\n" : escaped;
|
|
1825
|
+
});
|
|
1826
|
+
}
|
|
1827
|
+
function parseTrigger(line) {
|
|
1828
|
+
if (readOpenCodeLogField(line, "message") !== DEPENDENCY_INSTALL_FAILURE_MESSAGE) {
|
|
1829
|
+
return null;
|
|
1830
|
+
}
|
|
1831
|
+
const error2 = readOpenCodeLogField(line, "error");
|
|
1832
|
+
if (!error2 || !/\bEACCES\b/.test(error2)) return null;
|
|
1833
|
+
const errnoMatch = error2.match(/\bEACCES:\s*permission denied,\s*([a-z_]+)\s+'([^']*)'/);
|
|
1834
|
+
const capturedPath = errnoMatch?.[2];
|
|
1835
|
+
return {
|
|
1836
|
+
dir: readOpenCodeLogField(line, "dir"),
|
|
1837
|
+
op: errnoMatch?.[1],
|
|
1838
|
+
path: capturedPath && isAbsolute(capturedPath) ? capturedPath : void 0
|
|
1839
|
+
};
|
|
1840
|
+
}
|
|
1841
|
+
function getErrorCode(error2) {
|
|
1842
|
+
if (typeof error2 === "object" && error2 !== null && "code" in error2) {
|
|
1843
|
+
const code = error2.code;
|
|
1844
|
+
if (typeof code === "string") return code;
|
|
1845
|
+
}
|
|
1846
|
+
return "UNKNOWN";
|
|
1847
|
+
}
|
|
1848
|
+
function isWithin(path, root) {
|
|
1849
|
+
const candidate = resolve(path);
|
|
1850
|
+
const resolvedRoot = resolve(root);
|
|
1851
|
+
return candidate === resolvedRoot || candidate.startsWith(`${resolvedRoot}${sep}`);
|
|
1852
|
+
}
|
|
1853
|
+
function rootSpecs(context) {
|
|
1854
|
+
return [
|
|
1855
|
+
{ token: "~", configured: resolve(context.homeDir) },
|
|
1856
|
+
{ token: "$WORKSPACE", configured: resolve(context.cwd) }
|
|
1857
|
+
];
|
|
1858
|
+
}
|
|
1859
|
+
function resolveRoots(context) {
|
|
1860
|
+
const resolved = [];
|
|
1861
|
+
const unresolved = [];
|
|
1862
|
+
for (const spec of rootSpecs(context)) {
|
|
1863
|
+
try {
|
|
1864
|
+
resolved.push({ ...spec, real: realpathSync(spec.configured) });
|
|
1865
|
+
} catch (_error) {
|
|
1866
|
+
unresolved.push(spec);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
return { resolved, unresolved };
|
|
1870
|
+
}
|
|
1871
|
+
function resolveNearestAncestor(failingPath, remaining, error2) {
|
|
1872
|
+
let ancestor = dirname3(failingPath);
|
|
1873
|
+
const suffix = [basename(failingPath), ...remaining];
|
|
1874
|
+
while (ancestor) {
|
|
1875
|
+
try {
|
|
1876
|
+
return {
|
|
1877
|
+
path: resolve(realpathSync(ancestor), ...suffix),
|
|
1878
|
+
failure: { path: resolve(failingPath), error: error2 }
|
|
1879
|
+
};
|
|
1880
|
+
} catch (ancestorError) {
|
|
1881
|
+
if (ancestor === dirname3(ancestor) || !getErrorCode(ancestorError).startsWith("E")) {
|
|
1882
|
+
return null;
|
|
1883
|
+
}
|
|
1884
|
+
suffix.unshift(basename(ancestor));
|
|
1885
|
+
ancestor = dirname3(ancestor);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
return null;
|
|
1889
|
+
}
|
|
1890
|
+
function resolveExistingPath(absolutePath) {
|
|
1891
|
+
if (!isAbsolute(absolutePath)) return null;
|
|
1892
|
+
const candidate = resolve(absolutePath);
|
|
1893
|
+
const remaining = [];
|
|
1894
|
+
let current = candidate;
|
|
1895
|
+
while (current) {
|
|
1896
|
+
try {
|
|
1897
|
+
const stat2 = lstatSync(current);
|
|
1898
|
+
if (stat2.isSymbolicLink()) {
|
|
1899
|
+
if (remaining.length === 0) {
|
|
1900
|
+
return { path: join2(realpathSync(dirname3(current)), basename(current)) };
|
|
1901
|
+
}
|
|
1902
|
+
return { path: resolve(realpathSync(current), ...remaining) };
|
|
1903
|
+
}
|
|
1904
|
+
return { path: resolve(realpathSync(current), ...remaining) };
|
|
1905
|
+
} catch (error2) {
|
|
1906
|
+
const code = getErrorCode(error2);
|
|
1907
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") {
|
|
1908
|
+
return resolveNearestAncestor(current, remaining, error2);
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
if (current === dirname3(current)) return null;
|
|
1912
|
+
remaining.unshift(basename(current));
|
|
1913
|
+
current = dirname3(current);
|
|
1914
|
+
}
|
|
1915
|
+
return null;
|
|
1916
|
+
}
|
|
1917
|
+
function tokenise(absolutePath, roots) {
|
|
1918
|
+
const pathResolution = resolveExistingPath(absolutePath);
|
|
1919
|
+
if (!pathResolution) return null;
|
|
1920
|
+
const root = roots.reduce((selected, candidate) => {
|
|
1921
|
+
if (!isWithin(pathResolution.path, candidate.real)) return selected;
|
|
1922
|
+
if (!selected || candidate.real.length > selected.real.length || candidate.real.length === selected.real.length && candidate.token === "$WORKSPACE") {
|
|
1923
|
+
return candidate;
|
|
1924
|
+
}
|
|
1925
|
+
return selected;
|
|
1926
|
+
}, null);
|
|
1927
|
+
if (!root) return null;
|
|
1928
|
+
const suffix = relative(root.real, pathResolution.path);
|
|
1929
|
+
return {
|
|
1930
|
+
value: suffix ? `${root.token}/${suffix.split(sep).join("/")}` : root.token,
|
|
1931
|
+
root,
|
|
1932
|
+
resolutionFailure: pathResolution.failure
|
|
1933
|
+
};
|
|
1934
|
+
}
|
|
1935
|
+
function classifyScope(dir, roots) {
|
|
1936
|
+
if (!dir) return "other";
|
|
1937
|
+
const tokenisedDir = tokenise(dir, roots.resolved);
|
|
1938
|
+
if (tokenisedDir) return tokenisedDir.root.token === "$WORKSPACE" ? "workspace" : "global";
|
|
1939
|
+
for (const spec of roots.unresolved) {
|
|
1940
|
+
if (isWithin(dir, spec.configured)) {
|
|
1941
|
+
return spec.token === "$WORKSPACE" ? "workspace" : "global";
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
return "other";
|
|
1945
|
+
}
|
|
1946
|
+
function unresolvedRootForPath(path, unresolved) {
|
|
1947
|
+
return unresolved.find((spec) => isWithin(path, spec.configured));
|
|
1948
|
+
}
|
|
1949
|
+
function statType(stat2) {
|
|
1950
|
+
if (stat2.isDirectory()) return "d";
|
|
1951
|
+
if (stat2.isFile()) return "f";
|
|
1952
|
+
if (stat2.isSymbolicLink()) return "l";
|
|
1953
|
+
return "o";
|
|
1954
|
+
}
|
|
1955
|
+
function renderStat(name, stat2) {
|
|
1956
|
+
const mode = (stat2.mode & 4095).toString(8).padStart(4, "0");
|
|
1957
|
+
return `${name}:${statType(stat2)}:${stat2.uid}:${stat2.gid}:${mode}`;
|
|
1958
|
+
}
|
|
1959
|
+
function renderStatError(name, error2) {
|
|
1960
|
+
const code = getErrorCode(error2);
|
|
1961
|
+
return `${name}:${renderErrorCode(code)}`;
|
|
1962
|
+
}
|
|
1963
|
+
function renderErrorCode(code) {
|
|
1964
|
+
if (code === "ENOENT") return "missing";
|
|
1965
|
+
return code.startsWith("E") ? code : `E${code}`;
|
|
1966
|
+
}
|
|
1967
|
+
function inspectChain(rootReal, rootToken, absolutePath, resolutionFailure) {
|
|
1968
|
+
const target = resolve(absolutePath);
|
|
1969
|
+
const relativePath = relative(rootReal, target);
|
|
1970
|
+
const components = relativePath ? relativePath.split(sep) : [];
|
|
1971
|
+
const entries = [];
|
|
1972
|
+
let current = rootReal;
|
|
1973
|
+
let status2 = "complete";
|
|
1974
|
+
let finalStat;
|
|
1975
|
+
for (const name of [rootToken, ...components]) {
|
|
1976
|
+
if (name !== rootToken) current = join2(current, name);
|
|
1977
|
+
if (resolutionFailure && resolve(current) === resolve(resolutionFailure.path)) {
|
|
1978
|
+
entries.push(renderStatError(name, resolutionFailure.error));
|
|
1979
|
+
status2 = "stat_failed";
|
|
1980
|
+
break;
|
|
1981
|
+
}
|
|
1982
|
+
try {
|
|
1983
|
+
const stat2 = lstatSync(current);
|
|
1984
|
+
entries.push(renderStat(name, stat2));
|
|
1985
|
+
finalStat = stat2;
|
|
1986
|
+
} catch (error2) {
|
|
1987
|
+
entries.push(renderStatError(name, error2));
|
|
1988
|
+
if (getErrorCode(error2) !== "ENOENT") status2 = "stat_failed";
|
|
1989
|
+
break;
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
let symlinkTarget;
|
|
1993
|
+
let symlinkMeta;
|
|
1994
|
+
if (finalStat?.isSymbolicLink() && entries.length === components.length + 1) {
|
|
1995
|
+
try {
|
|
1996
|
+
symlinkTarget = realpathSync(current);
|
|
1997
|
+
try {
|
|
1998
|
+
symlinkMeta = renderStat("", lstatSync(symlinkTarget)).slice(1);
|
|
1999
|
+
} catch (error2) {
|
|
2000
|
+
symlinkMeta = renderErrorCode(getErrorCode(error2));
|
|
2001
|
+
status2 = "stat_failed";
|
|
2002
|
+
}
|
|
2003
|
+
} catch (error2) {
|
|
2004
|
+
symlinkTarget = "<unresolved>";
|
|
2005
|
+
symlinkMeta = renderErrorCode(getErrorCode(error2));
|
|
2006
|
+
status2 = "stat_failed";
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
return { status: status2, chain: entries.join(","), symlinkTarget, symlinkMeta };
|
|
2010
|
+
}
|
|
2011
|
+
function buildDiagnostic(line, context) {
|
|
2012
|
+
const trigger = parseTrigger(line);
|
|
2013
|
+
if (!trigger) return null;
|
|
2014
|
+
const roots = resolveRoots(context);
|
|
2015
|
+
const scope = classifyScope(trigger.dir, roots);
|
|
2016
|
+
const pathSource = trigger.path ? "error" : trigger.dir ? "dir" : "none";
|
|
2017
|
+
const inspectedPath = trigger.path ?? trigger.dir;
|
|
2018
|
+
const microvmId = context.microvmId?.trim() || "unknown";
|
|
2019
|
+
const euid = context.euid === void 0 ? "unavailable" : String(context.euid);
|
|
2020
|
+
const egid = context.egid === void 0 ? "unavailable" : String(context.egid);
|
|
2021
|
+
const op = trigger.op ?? "unknown";
|
|
2022
|
+
if (!inspectedPath) {
|
|
2023
|
+
return {
|
|
2024
|
+
event: `OPENCODE-DEPENDENCY-INSTALL-EACCES: status=no_path path_source=${pathSource} scope=${scope} microvm_id=${microvmId} op=${op} path=unknown cli_euid=${euid} cli_egid=${egid} effect="${EFFECT}" chain=unavailable`,
|
|
2025
|
+
localDetail: null
|
|
2026
|
+
};
|
|
2027
|
+
}
|
|
2028
|
+
const tokenisedPath = tokenise(inspectedPath, roots.resolved);
|
|
2029
|
+
if (!tokenisedPath) {
|
|
2030
|
+
const unresolvedRoot = unresolvedRootForPath(inspectedPath, roots.unresolved);
|
|
2031
|
+
if (unresolvedRoot) {
|
|
2032
|
+
return {
|
|
2033
|
+
event: `OPENCODE-DEPENDENCY-INSTALL-EACCES: status=root_unresolved path_source=${pathSource} scope=${scope} microvm_id=${microvmId} op=${op} path=unknown cli_euid=${euid} cli_egid=${egid} effect="${EFFECT}" chain=unavailable`,
|
|
2034
|
+
localDetail: null
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
return {
|
|
2038
|
+
event: `OPENCODE-DEPENDENCY-INSTALL-EACCES: status=outside_roots path_source=${pathSource} scope=${scope} microvm_id=${microvmId} op=${op} path=<outside-approved-roots> cli_euid=${euid} cli_egid=${egid} effect="${EFFECT}" chain=unavailable`,
|
|
2039
|
+
localDetail: `OPENCODE-DEPENDENCY-INSTALL-EACCES (local only): outside approved roots: ${inspectedPath}`
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
const inspection = inspectChain(
|
|
2043
|
+
tokenisedPath.root.real,
|
|
2044
|
+
tokenisedPath.root.token,
|
|
2045
|
+
inspectedPath,
|
|
2046
|
+
tokenisedPath.resolutionFailure
|
|
2047
|
+
);
|
|
2048
|
+
const optionalSymlinkFields = inspection.symlinkTarget ? ` symlink_target=${tokenise(inspection.symlinkTarget, roots.resolved)?.value ?? "<outside-approved-roots>"}` + (inspection.symlinkMeta ? ` symlink_meta=${inspection.symlinkMeta}` : "") : "";
|
|
2049
|
+
return {
|
|
2050
|
+
event: `OPENCODE-DEPENDENCY-INSTALL-EACCES: status=${inspection.status} path_source=${pathSource} scope=${scope} microvm_id=${microvmId} op=${op} path=${tokenisedPath.value} cli_euid=${euid} cli_egid=${egid} effect="${EFFECT}" chain=${inspection.chain}${optionalSymlinkFields}`,
|
|
2051
|
+
localDetail: null
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
function createDependencyInstallDiagnosticReporter({
|
|
2055
|
+
context,
|
|
2056
|
+
emitEvent: emitEvent2,
|
|
2057
|
+
emitLocal
|
|
2058
|
+
}) {
|
|
2059
|
+
const emittedEvents = /* @__PURE__ */ new Set();
|
|
2060
|
+
return (line) => {
|
|
2061
|
+
const diagnostic = buildDiagnostic(line, context);
|
|
2062
|
+
if (!diagnostic || emittedEvents.has(diagnostic.event)) return;
|
|
2063
|
+
if (emittedEvents.size >= MAX_DISTINCT_DIAGNOSTICS) return;
|
|
2064
|
+
emittedEvents.add(diagnostic.event);
|
|
2065
|
+
emitEvent2(diagnostic.event);
|
|
2066
|
+
if (diagnostic.localDetail) emitLocal(diagnostic.localDetail);
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
|
|
1813
2070
|
// src/lib/runner-activity-telemetry.ts
|
|
1814
2071
|
var FORWARDED_LEVELS_BY_SOURCE = {
|
|
1815
2072
|
"cli.run": /* @__PURE__ */ new Set(["warn", "error"]),
|
|
@@ -1841,7 +2098,17 @@ function parseOpenCodeLogLine(line) {
|
|
|
1841
2098
|
}
|
|
1842
2099
|
var MAX_LINE_BUFFER_BYTES = 16 * 1024;
|
|
1843
2100
|
var REPEAT_SUPPRESSION_WINDOW_MS = 6e4;
|
|
1844
|
-
function
|
|
2101
|
+
function sanitiseDependencyInstallWarning(line) {
|
|
2102
|
+
const withoutDirectory = line.replace(
|
|
2103
|
+
/((?:^|\s)dir=)(?:"(?:\\.|[^"])*"|\/[^\s)"']+|\S+)/g,
|
|
2104
|
+
"$1<path>"
|
|
2105
|
+
);
|
|
2106
|
+
return withoutDirectory.replace(
|
|
2107
|
+
/((?:^|\s)error=)"((?:\\.|[^"])*)"/,
|
|
2108
|
+
(_match, prefix, error2) => `${prefix}"${error2.replace(/'\/[^']*'/g, "'<path>'")}"`
|
|
2109
|
+
);
|
|
2110
|
+
}
|
|
2111
|
+
function createOpenCodeActivityForwarder(getContext, onSessionStoreWriteFailure, onDependencyInstallFailure) {
|
|
1845
2112
|
let buffer = Buffer.alloc(0);
|
|
1846
2113
|
const repeatStates = /* @__PURE__ */ new Map();
|
|
1847
2114
|
const forwardLine = (line, parsed, repeatsSuppressed = 0) => {
|
|
@@ -1853,7 +2120,7 @@ function createOpenCodeActivityForwarder(getContext, onSessionStoreWriteFailure)
|
|
|
1853
2120
|
forwardRunnerActivity(
|
|
1854
2121
|
{
|
|
1855
2122
|
level: parsed.level,
|
|
1856
|
-
error: line,
|
|
2123
|
+
error: parsed.message === DEPENDENCY_INSTALL_FAILURE_MESSAGE ? sanitiseDependencyInstallWarning(line) : line,
|
|
1857
2124
|
metadata: Object.keys(metadata).length > 0 ? metadata : void 0,
|
|
1858
2125
|
source: "opencode"
|
|
1859
2126
|
},
|
|
@@ -1871,24 +2138,7 @@ function createOpenCodeActivityForwarder(getContext, onSessionStoreWriteFailure)
|
|
|
1871
2138
|
repeatStates.delete(key);
|
|
1872
2139
|
}
|
|
1873
2140
|
};
|
|
1874
|
-
const
|
|
1875
|
-
const parsed = parseOpenCodeLogLine(line);
|
|
1876
|
-
const isDiskFailure = isSessionStoreWriteFailure(line);
|
|
1877
|
-
const cause = sessionStoreCauseToken(line);
|
|
1878
|
-
if (onSessionStoreWriteFailure && (isDiskFailure || cause !== null)) {
|
|
1879
|
-
try {
|
|
1880
|
-
onSessionStoreWriteFailure({
|
|
1881
|
-
isDiskFailure,
|
|
1882
|
-
sessionId: parsed?.sessionID ?? null,
|
|
1883
|
-
cause
|
|
1884
|
-
});
|
|
1885
|
-
} catch (err) {
|
|
1886
|
-
reportTelemetryDiagnostic(
|
|
1887
|
-
`Runner activity telemetry could not record session-store write failure: ${err instanceof Error ? err.message : String(err)}`
|
|
1888
|
-
);
|
|
1889
|
-
}
|
|
1890
|
-
}
|
|
1891
|
-
if (!parsed) return;
|
|
2141
|
+
const forwardWithRepeatSuppression = (line, parsed) => {
|
|
1892
2142
|
const now = Date.now();
|
|
1893
2143
|
const key = parsed.message === void 0 ? void 0 : `${parsed.level}\0${parsed.message}\0${parsed.sessionID ?? ""}\0${parsed.messageID ?? ""}`;
|
|
1894
2144
|
expireRepeatStates(now, key);
|
|
@@ -1911,6 +2161,35 @@ function createOpenCodeActivityForwarder(getContext, onSessionStoreWriteFailure)
|
|
|
1911
2161
|
previous.parsed = parsed;
|
|
1912
2162
|
previous.repeatsSuppressed++;
|
|
1913
2163
|
};
|
|
2164
|
+
const flushLine = (line) => {
|
|
2165
|
+
const parsed = parseOpenCodeLogLine(line);
|
|
2166
|
+
const isDiskFailure = isSessionStoreWriteFailure(line);
|
|
2167
|
+
const cause = sessionStoreCauseToken(line);
|
|
2168
|
+
if (onSessionStoreWriteFailure && (isDiskFailure || cause !== null)) {
|
|
2169
|
+
try {
|
|
2170
|
+
onSessionStoreWriteFailure({
|
|
2171
|
+
isDiskFailure,
|
|
2172
|
+
sessionId: parsed?.sessionID ?? null,
|
|
2173
|
+
cause
|
|
2174
|
+
});
|
|
2175
|
+
} catch (err) {
|
|
2176
|
+
reportTelemetryDiagnostic(
|
|
2177
|
+
`Runner activity telemetry could not record session-store write failure: ${err instanceof Error ? err.message : String(err)}`
|
|
2178
|
+
);
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
if (!parsed) return;
|
|
2182
|
+
forwardWithRepeatSuppression(line, parsed);
|
|
2183
|
+
if (onDependencyInstallFailure && parsed.message === DEPENDENCY_INSTALL_FAILURE_MESSAGE) {
|
|
2184
|
+
try {
|
|
2185
|
+
onDependencyInstallFailure(line);
|
|
2186
|
+
} catch (err) {
|
|
2187
|
+
reportTelemetryDiagnostic(
|
|
2188
|
+
`Runner activity telemetry could not record dependency-install failure diagnostic: ${err instanceof Error ? err.message : String(err)}`
|
|
2189
|
+
);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
};
|
|
1914
2193
|
const forward = ((chunk) => {
|
|
1915
2194
|
buffer = Buffer.concat([buffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, "utf-8")]);
|
|
1916
2195
|
let newlineIndex;
|
|
@@ -1950,6 +2229,23 @@ function truncate(message) {
|
|
|
1950
2229
|
const remaining = MAX_MESSAGE_LENGTH - marker.length;
|
|
1951
2230
|
const headLength = Math.ceil(remaining * 0.6);
|
|
1952
2231
|
const tailLength = remaining - headLength;
|
|
2232
|
+
const effectMatch = message.startsWith("OPENCODE-DEPENDENCY-INSTALL-EACCES:") ? message.match(/ effect="[^"]*"/) : null;
|
|
2233
|
+
if (effectMatch?.index !== void 0) {
|
|
2234
|
+
const effectEnd = effectMatch.index + effectMatch[0].length;
|
|
2235
|
+
if (effectEnd > headLength && effectEnd < message.length) {
|
|
2236
|
+
const effectLength = effectMatch[0].length;
|
|
2237
|
+
const nonEffectLength = remaining - effectLength;
|
|
2238
|
+
const preservedTailLength = Math.min(
|
|
2239
|
+
message.length - effectEnd,
|
|
2240
|
+
nonEffectLength - Math.min(effectMatch.index, Math.ceil(nonEffectLength * 0.6))
|
|
2241
|
+
);
|
|
2242
|
+
const preservedHeadLength = Math.min(
|
|
2243
|
+
effectMatch.index,
|
|
2244
|
+
nonEffectLength - preservedTailLength
|
|
2245
|
+
);
|
|
2246
|
+
return message.slice(0, preservedHeadLength) + marker + message.slice(effectMatch.index, effectEnd) + message.slice(message.length - preservedTailLength);
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
1953
2249
|
return message.slice(0, headLength) + marker + message.slice(message.length - tailLength);
|
|
1954
2250
|
}
|
|
1955
2251
|
function sanitiseMetadata(metadata) {
|
|
@@ -2027,10 +2323,10 @@ function forwardRunnerActivity(entry, context) {
|
|
|
2027
2323
|
|
|
2028
2324
|
// src/lib/opencode/session-db-recovery-report.ts
|
|
2029
2325
|
import { readFileSync as readFileSync2, unlinkSync } from "node:fs";
|
|
2030
|
-
import { join as
|
|
2326
|
+
import { join as join3 } from "node:path";
|
|
2031
2327
|
function sessionDbRecoveryReportPath(homeDir, env) {
|
|
2032
2328
|
const override = env.EVIDENT_SESSION_DB_RECOVERY_REPORT?.trim();
|
|
2033
|
-
return override ||
|
|
2329
|
+
return override || join3(homeDir, ".local", "state", "evident", "session-db-recovery.jsonl");
|
|
2034
2330
|
}
|
|
2035
2331
|
function drainSessionDbRecoveryReport({
|
|
2036
2332
|
homeDir,
|
|
@@ -2303,7 +2599,7 @@ async function waitForOpenCodeHealth(port, timeoutMs = 3e4) {
|
|
|
2303
2599
|
if (health.healthy || health.authFailed) {
|
|
2304
2600
|
return health;
|
|
2305
2601
|
}
|
|
2306
|
-
await new Promise((
|
|
2602
|
+
await new Promise((resolve5) => setTimeout(resolve5, 1e3));
|
|
2307
2603
|
}
|
|
2308
2604
|
return { healthy: false, error: "Timeout waiting for OpenCode to be healthy" };
|
|
2309
2605
|
}
|
|
@@ -2314,7 +2610,7 @@ async function waitForOpenCode2Health(port, password, timeoutMs = 3e4) {
|
|
|
2314
2610
|
if (health.healthy || health.authFailed) {
|
|
2315
2611
|
return health;
|
|
2316
2612
|
}
|
|
2317
|
-
await new Promise((
|
|
2613
|
+
await new Promise((resolve5) => setTimeout(resolve5, 1e3));
|
|
2318
2614
|
}
|
|
2319
2615
|
return { healthy: false, error: "Timeout waiting for OpenCode to be healthy" };
|
|
2320
2616
|
}
|
|
@@ -2904,7 +3200,7 @@ async function sendPromptAsync(port, sessionId, content, options, attachments, c
|
|
|
2904
3200
|
return dispatchedUserMessageId;
|
|
2905
3201
|
}
|
|
2906
3202
|
if (attempt < READ_BACK_ATTEMPTS - 1) {
|
|
2907
|
-
await new Promise((
|
|
3203
|
+
await new Promise((resolve5) => setTimeout(resolve5, READ_BACK_DELAY_MS));
|
|
2908
3204
|
}
|
|
2909
3205
|
}
|
|
2910
3206
|
return null;
|
|
@@ -3076,9 +3372,11 @@ function sumAssistantUsage(messages) {
|
|
|
3076
3372
|
let cacheWriteSum = 0;
|
|
3077
3373
|
let costSum = 0;
|
|
3078
3374
|
let sawCost = false;
|
|
3375
|
+
let toolCallCount = 0;
|
|
3079
3376
|
let modelId = null;
|
|
3080
3377
|
let providerId = null;
|
|
3081
3378
|
for (const m of selected) {
|
|
3379
|
+
toolCallCount += m.parts?.filter((part) => part.type === "tool").length ?? 0;
|
|
3082
3380
|
const info = m.info;
|
|
3083
3381
|
if (!info) continue;
|
|
3084
3382
|
const tokens = info.tokens;
|
|
@@ -3115,7 +3413,8 @@ function sumAssistantUsage(messages) {
|
|
|
3115
3413
|
usage_tokens_cache_write: cacheWriteSum,
|
|
3116
3414
|
// NULL means OpenCode never reported a cost; it is distinct from a genuine
|
|
3117
3415
|
// zero-cost message, which sets `sawCost` with `costSum === 0`.
|
|
3118
|
-
usage_cost_usd: sawCost ? costSum : null
|
|
3416
|
+
usage_cost_usd: sawCost ? costSum : null,
|
|
3417
|
+
turn_tool_calls: toolCallCount
|
|
3119
3418
|
};
|
|
3120
3419
|
}
|
|
3121
3420
|
function messageUsage(messages, userMessageId) {
|
|
@@ -3964,12 +4263,12 @@ async function readV2SessionErrorStream(client, options) {
|
|
|
3964
4263
|
|
|
3965
4264
|
// src/lib/opencode/credential-import-v2.ts
|
|
3966
4265
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
3967
|
-
import { join as
|
|
4266
|
+
import { join as join5 } from "node:path";
|
|
3968
4267
|
|
|
3969
4268
|
// src/lib/openai-usage.ts
|
|
3970
4269
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
3971
4270
|
import { homedir as homedir3 } from "node:os";
|
|
3972
|
-
import { join as
|
|
4271
|
+
import { join as join4 } from "node:path";
|
|
3973
4272
|
var CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
3974
4273
|
var OPENCODE_AUTH_SEGMENTS = [".local", "share", "opencode", "auth.json"];
|
|
3975
4274
|
var OpenAiUsageError = class extends Error {
|
|
@@ -3983,7 +4282,7 @@ function isLocalCredentialProblem2(err) {
|
|
|
3983
4282
|
}
|
|
3984
4283
|
function readOpenCodeChatGptCredentials() {
|
|
3985
4284
|
try {
|
|
3986
|
-
const raw = readFileSync3(
|
|
4285
|
+
const raw = readFileSync3(join4(homedir3(), ...OPENCODE_AUTH_SEGMENTS), "utf-8");
|
|
3987
4286
|
let parsed;
|
|
3988
4287
|
try {
|
|
3989
4288
|
parsed = JSON.parse(raw);
|
|
@@ -4209,13 +4508,13 @@ var INTEGRATION_LIST_RETRY_DELAY_MS = 1e3;
|
|
|
4209
4508
|
function isRecord3(value) {
|
|
4210
4509
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4211
4510
|
}
|
|
4212
|
-
var sleep2 = (milliseconds) => new Promise((
|
|
4511
|
+
var sleep2 = (milliseconds) => new Promise((resolve5) => setTimeout(resolve5, milliseconds));
|
|
4213
4512
|
function emptyResult(unsupported = [], outcome = "completed") {
|
|
4214
4513
|
return { outcome, imported: [], alreadyConnected: [], unsupported, failed: [] };
|
|
4215
4514
|
}
|
|
4216
4515
|
function readOpenCodeCredentials(homeDir, log3) {
|
|
4217
4516
|
try {
|
|
4218
|
-
const raw = readFileSync4(
|
|
4517
|
+
const raw = readFileSync4(join5(homeDir, ...OPENCODE_AUTH_SEGMENTS), "utf8");
|
|
4219
4518
|
let parsed;
|
|
4220
4519
|
try {
|
|
4221
4520
|
parsed = JSON.parse(raw);
|
|
@@ -4417,7 +4716,7 @@ async function importOpenCodeV2Credentials({
|
|
|
4417
4716
|
import { spawn as spawn2 } from "node:child_process";
|
|
4418
4717
|
import { mkdirSync, statSync as statSync3, unlinkSync as unlinkSync2, writeFileSync } from "node:fs";
|
|
4419
4718
|
import { homedir as homedir4 } from "node:os";
|
|
4420
|
-
import { dirname as
|
|
4719
|
+
import { dirname as dirname4, resolve as resolvePath } from "node:path";
|
|
4421
4720
|
|
|
4422
4721
|
// src/lib/runner-synchroniser.ts
|
|
4423
4722
|
import { spawn } from "node:child_process";
|
|
@@ -4427,7 +4726,7 @@ function appendError(stderr, error2) {
|
|
|
4427
4726
|
${message}`;
|
|
4428
4727
|
}
|
|
4429
4728
|
function runSynchroniser(args, opts) {
|
|
4430
|
-
return new Promise((
|
|
4729
|
+
return new Promise((resolve5) => {
|
|
4431
4730
|
let child;
|
|
4432
4731
|
let stdout = "";
|
|
4433
4732
|
let stderr = "";
|
|
@@ -4441,7 +4740,7 @@ function runSynchroniser(args, opts) {
|
|
|
4441
4740
|
if (timer.handle) clearTimeout(timer.handle);
|
|
4442
4741
|
if (abortListener) opts.signal?.removeEventListener("abort", abortListener);
|
|
4443
4742
|
if (spawnListener) child.removeListener("spawn", spawnListener);
|
|
4444
|
-
|
|
4743
|
+
resolve5(result);
|
|
4445
4744
|
};
|
|
4446
4745
|
try {
|
|
4447
4746
|
child = spawn("runner-synchroniser", args, {
|
|
@@ -4545,7 +4844,7 @@ function clearMarker(options) {
|
|
|
4545
4844
|
function markNoReplicate(options, message) {
|
|
4546
4845
|
if (options.noReplicateMarker) {
|
|
4547
4846
|
try {
|
|
4548
|
-
mkdirSync(
|
|
4847
|
+
mkdirSync(dirname4(options.noReplicateMarker), { recursive: true });
|
|
4549
4848
|
writeFileSync(options.noReplicateMarker, "");
|
|
4550
4849
|
} catch (error2) {
|
|
4551
4850
|
options.log(
|
|
@@ -4604,7 +4903,7 @@ function parseSynchroniserEnv(stdout) {
|
|
|
4604
4903
|
return values;
|
|
4605
4904
|
}
|
|
4606
4905
|
function runCommand(command, args, options) {
|
|
4607
|
-
return new Promise((
|
|
4906
|
+
return new Promise((resolve5) => {
|
|
4608
4907
|
let child;
|
|
4609
4908
|
let stdout = "";
|
|
4610
4909
|
let stderr = "";
|
|
@@ -4613,7 +4912,7 @@ function runCommand(command, args, options) {
|
|
|
4613
4912
|
if (settled) return;
|
|
4614
4913
|
settled = true;
|
|
4615
4914
|
if (timer) clearTimeout(timer);
|
|
4616
|
-
|
|
4915
|
+
resolve5(result);
|
|
4617
4916
|
};
|
|
4618
4917
|
try {
|
|
4619
4918
|
child = spawn2(command, args, {
|
|
@@ -4621,7 +4920,7 @@ function runCommand(command, args, options) {
|
|
|
4621
4920
|
stdio: ["ignore", "pipe", "pipe"]
|
|
4622
4921
|
});
|
|
4623
4922
|
} catch (error2) {
|
|
4624
|
-
|
|
4923
|
+
resolve5({
|
|
4625
4924
|
code: null,
|
|
4626
4925
|
stdout,
|
|
4627
4926
|
stderr: error2 instanceof Error ? error2.message : String(error2),
|
|
@@ -4700,7 +4999,7 @@ async function ensureLitestreamConfig(options, env) {
|
|
|
4700
4999
|
return null;
|
|
4701
5000
|
}
|
|
4702
5001
|
try {
|
|
4703
|
-
mkdirSync(
|
|
5002
|
+
mkdirSync(dirname4(configPath), { recursive: true });
|
|
4704
5003
|
writeFileSync(configPath, rendered.stdout);
|
|
4705
5004
|
} catch (error2) {
|
|
4706
5005
|
options.log(
|
|
@@ -4967,7 +5266,7 @@ async function restoreAndVerifySessionDb(options) {
|
|
|
4967
5266
|
// src/lib/opencode/session-db-provenance.ts
|
|
4968
5267
|
import { createRequire } from "node:module";
|
|
4969
5268
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "node:fs";
|
|
4970
|
-
import { dirname as
|
|
5269
|
+
import { dirname as dirname5, join as join6 } from "node:path";
|
|
4971
5270
|
var require2 = createRequire(import.meta.url);
|
|
4972
5271
|
function readSessionDbMigrationIds(dbPath) {
|
|
4973
5272
|
let db;
|
|
@@ -5004,7 +5303,7 @@ function readSessionDbMigrationIds(dbPath) {
|
|
|
5004
5303
|
}
|
|
5005
5304
|
function sessionDbProvenanceStatePath(homeDir, env) {
|
|
5006
5305
|
const override = env.EVIDENT_SESSION_DB_PROVENANCE_STATE?.trim();
|
|
5007
|
-
return override ||
|
|
5306
|
+
return override || join6(homeDir, ".local", "state", "evident", "session-db-provenance.json");
|
|
5008
5307
|
}
|
|
5009
5308
|
function loadSessionDbProvenanceState(path) {
|
|
5010
5309
|
let value;
|
|
@@ -5033,7 +5332,7 @@ function loadSessionDbProvenanceState(path) {
|
|
|
5033
5332
|
}
|
|
5034
5333
|
function saveSessionDbProvenanceState(path, state) {
|
|
5035
5334
|
try {
|
|
5036
|
-
mkdirSync2(
|
|
5335
|
+
mkdirSync2(dirname5(path), { recursive: true });
|
|
5037
5336
|
writeFileSync2(path, `${JSON.stringify(state, null, 2)}
|
|
5038
5337
|
`, "utf8");
|
|
5039
5338
|
} catch (error2) {
|
|
@@ -5124,7 +5423,7 @@ async function stopProcessAndWait(child, timeoutMs, sendTerm, sendKill) {
|
|
|
5124
5423
|
if (child.exitCode !== null || child.signalCode !== null) {
|
|
5125
5424
|
return { outcome: "exited", code: child.exitCode, signal: child.signalCode };
|
|
5126
5425
|
}
|
|
5127
|
-
return new Promise((
|
|
5426
|
+
return new Promise((resolve5, reject) => {
|
|
5128
5427
|
let forced = false;
|
|
5129
5428
|
let settled = false;
|
|
5130
5429
|
const timer = setTimeout(() => {
|
|
@@ -5144,7 +5443,7 @@ async function stopProcessAndWait(child, timeoutMs, sendTerm, sendKill) {
|
|
|
5144
5443
|
settled = true;
|
|
5145
5444
|
clearTimeout(timer);
|
|
5146
5445
|
child.removeListener("exit", onExit);
|
|
5147
|
-
|
|
5446
|
+
resolve5(result);
|
|
5148
5447
|
};
|
|
5149
5448
|
const fail = (error2) => {
|
|
5150
5449
|
if (settled) return;
|
|
@@ -5620,7 +5919,7 @@ function selectSessionsToDelete(sessions, opts) {
|
|
|
5620
5919
|
);
|
|
5621
5920
|
}
|
|
5622
5921
|
var DEFAULT_INTERVAL = "1h";
|
|
5623
|
-
function
|
|
5922
|
+
function resolve2(flag, envValue, fallback) {
|
|
5624
5923
|
return flag ?? envValue ?? fallback;
|
|
5625
5924
|
}
|
|
5626
5925
|
function parseMaxCount(input) {
|
|
@@ -5636,9 +5935,9 @@ function parseMaxCount(input) {
|
|
|
5636
5935
|
}
|
|
5637
5936
|
function resolveSessionCleanupConfig(flags, env = process.env) {
|
|
5638
5937
|
const warnings = [];
|
|
5639
|
-
const maxAgeRaw =
|
|
5640
|
-
const maxCountRaw =
|
|
5641
|
-
const intervalRaw =
|
|
5938
|
+
const maxAgeRaw = resolve2(flags.maxAge, env.EVIDENT_SESSION_CLEANUP_MAX_AGE);
|
|
5939
|
+
const maxCountRaw = resolve2(flags.maxCount, env.EVIDENT_SESSION_CLEANUP_MAX_COUNT);
|
|
5940
|
+
const intervalRaw = resolve2(
|
|
5642
5941
|
flags.interval,
|
|
5643
5942
|
env.EVIDENT_SESSION_CLEANUP_INTERVAL,
|
|
5644
5943
|
DEFAULT_INTERVAL
|
|
@@ -5678,10 +5977,10 @@ function resolveSessionCleanupConfig(flags, env = process.env) {
|
|
|
5678
5977
|
|
|
5679
5978
|
// src/lib/opencode/session-db-size.ts
|
|
5680
5979
|
import { statSync as statSync4 } from "node:fs";
|
|
5681
|
-
import { join as
|
|
5980
|
+
import { join as join7 } from "node:path";
|
|
5682
5981
|
var LARGE_DB_THRESHOLD_BYTES = 268435456;
|
|
5683
5982
|
function statSessionDbBytes(homeDir) {
|
|
5684
|
-
const dbPath =
|
|
5983
|
+
const dbPath = join7(homeDir, ".local", "share", "opencode", "opencode.db");
|
|
5685
5984
|
try {
|
|
5686
5985
|
return statSync4(dbPath).size;
|
|
5687
5986
|
} catch (err) {
|
|
@@ -5711,12 +6010,12 @@ function buildSessionStoreSizeWarning(input) {
|
|
|
5711
6010
|
// src/lib/opencode/log-tail.ts
|
|
5712
6011
|
import { statSync as statSync5 } from "node:fs";
|
|
5713
6012
|
import { homedir as homedir5 } from "node:os";
|
|
5714
|
-
import { join as
|
|
6013
|
+
import { join as join8 } from "node:path";
|
|
5715
6014
|
import { open as open2, stat } from "node:fs/promises";
|
|
5716
6015
|
var DEFAULT_POLL_INTERVAL_MS = 1e3;
|
|
5717
6016
|
function resolveOpenCodeLogPath(homeDir = homedir5(), env = process.env) {
|
|
5718
|
-
const dataDir = env.XDG_DATA_HOME ||
|
|
5719
|
-
return
|
|
6017
|
+
const dataDir = env.XDG_DATA_HOME || join8(homeDir, ".local", "share");
|
|
6018
|
+
return join8(dataDir, "opencode", "log", "opencode.log");
|
|
5720
6019
|
}
|
|
5721
6020
|
function isEnoent(error2) {
|
|
5722
6021
|
return error2?.code === "ENOENT";
|
|
@@ -5871,8 +6170,8 @@ var StreamForwarder = class {
|
|
|
5871
6170
|
pauseStream(sid, stream) {
|
|
5872
6171
|
if (!stream.paused) {
|
|
5873
6172
|
stream.paused = true;
|
|
5874
|
-
stream.pausePromise = new Promise((
|
|
5875
|
-
stream.resume =
|
|
6173
|
+
stream.pausePromise = new Promise((resolve5) => {
|
|
6174
|
+
stream.resume = resolve5;
|
|
5876
6175
|
});
|
|
5877
6176
|
}
|
|
5878
6177
|
if (stream.pauseTimer !== null) clearTimeout(stream.pauseTimer);
|
|
@@ -5939,12 +6238,12 @@ var StreamForwarder = class {
|
|
|
5939
6238
|
let endBody;
|
|
5940
6239
|
if (has_body) {
|
|
5941
6240
|
const chunks = [];
|
|
5942
|
-
bodyPromise = new Promise((
|
|
6241
|
+
bodyPromise = new Promise((resolve5) => {
|
|
5943
6242
|
pushBody = (buf) => {
|
|
5944
6243
|
chunks.push(buf);
|
|
5945
6244
|
};
|
|
5946
6245
|
endBody = () => {
|
|
5947
|
-
|
|
6246
|
+
resolve5(Buffer.concat(chunks));
|
|
5948
6247
|
};
|
|
5949
6248
|
});
|
|
5950
6249
|
}
|
|
@@ -6115,7 +6414,7 @@ function connectTunnel(options) {
|
|
|
6115
6414
|
} = options;
|
|
6116
6415
|
const tunnelUrl = getTunnelUrlConfig();
|
|
6117
6416
|
const url = `${tunnelUrl}/tunnel/${agentId}/connect`;
|
|
6118
|
-
return new Promise((
|
|
6417
|
+
return new Promise((resolve5, reject) => {
|
|
6119
6418
|
const ws = new WebSocket2(url, {
|
|
6120
6419
|
headers: {
|
|
6121
6420
|
Authorization: authHeader,
|
|
@@ -6224,7 +6523,7 @@ function connectTunnel(options) {
|
|
|
6224
6523
|
forwarder.setBinaryFramesSupported(message.binary_frames === true);
|
|
6225
6524
|
const connectedAgentId = message.agent_id ?? agentId;
|
|
6226
6525
|
onConnected?.(connectedAgentId);
|
|
6227
|
-
|
|
6526
|
+
resolve5({
|
|
6228
6527
|
ws,
|
|
6229
6528
|
close: () => ws.close(1e3, "CLI shutdown")
|
|
6230
6529
|
});
|
|
@@ -6779,12 +7078,12 @@ function createSessionStoreHealth(options = {}) {
|
|
|
6779
7078
|
import { homedir as homedir6 } from "node:os";
|
|
6780
7079
|
|
|
6781
7080
|
// src/lib/runner-file-sync.ts
|
|
6782
|
-
import { join as
|
|
7081
|
+
import { join as join10 } from "node:path";
|
|
6783
7082
|
|
|
6784
7083
|
// src/lib/file-push.ts
|
|
6785
7084
|
import { randomUUID } from "node:crypto";
|
|
6786
7085
|
import { chmod, mkdir, open as open3, realpath, rename, unlink } from "node:fs/promises";
|
|
6787
|
-
import { basename, dirname as
|
|
7086
|
+
import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute2, join as join9, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
|
|
6788
7087
|
var FILE_MODE = 384;
|
|
6789
7088
|
var DIRECTORY_MODE = 448;
|
|
6790
7089
|
async function writePushedFile(request2) {
|
|
@@ -6815,9 +7114,9 @@ async function writePushedFile(request2) {
|
|
|
6815
7114
|
}
|
|
6816
7115
|
try {
|
|
6817
7116
|
const { existingAncestor, missingSegments } = await resolveNearestExistingAncestor(
|
|
6818
|
-
|
|
7117
|
+
dirname6(candidate)
|
|
6819
7118
|
);
|
|
6820
|
-
const realTarget =
|
|
7119
|
+
const realTarget = join9(existingAncestor, ...missingSegments, basename2(candidate));
|
|
6821
7120
|
const allowedDirectory = await findContainingAllowedDirectory(allowedDirectories, realTarget);
|
|
6822
7121
|
if (allowedDirectory === null) {
|
|
6823
7122
|
return refuse("path_not_allowed", "The runner does not allow writing to that location.", {
|
|
@@ -6827,8 +7126,8 @@ async function writePushedFile(request2) {
|
|
|
6827
7126
|
}
|
|
6828
7127
|
if (missingSegments.length > 0) {
|
|
6829
7128
|
await createMissingDirectories(existingAncestor, missingSegments);
|
|
6830
|
-
const realParent = await realpath(
|
|
6831
|
-
if (realParent !==
|
|
7129
|
+
const realParent = await realpath(dirname6(realTarget));
|
|
7130
|
+
if (realParent !== dirname6(realTarget) || !contains(allowedDirectory, realTarget)) {
|
|
6832
7131
|
return refuse("path_not_allowed", "The runner does not allow writing to that location.", {
|
|
6833
7132
|
path: realTarget,
|
|
6834
7133
|
bytes,
|
|
@@ -6853,15 +7152,15 @@ function expandAndValidate(requestedPath, homeDir) {
|
|
|
6853
7152
|
if (requestedPath.trim() === "" || requestedPath.includes("\0")) {
|
|
6854
7153
|
return null;
|
|
6855
7154
|
}
|
|
6856
|
-
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ?
|
|
7155
|
+
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join9(homeDir, requestedPath.slice(2)) : requestedPath;
|
|
6857
7156
|
if (expanded.split(/[/\\]/).includes("..")) {
|
|
6858
7157
|
return null;
|
|
6859
7158
|
}
|
|
6860
|
-
if (!
|
|
7159
|
+
if (!isAbsolute2(expanded)) {
|
|
6861
7160
|
return null;
|
|
6862
7161
|
}
|
|
6863
|
-
const candidate =
|
|
6864
|
-
const name =
|
|
7162
|
+
const candidate = resolve3(expanded);
|
|
7163
|
+
const name = basename2(candidate);
|
|
6865
7164
|
return name === "" || name === "." || name === ".." ? null : candidate;
|
|
6866
7165
|
}
|
|
6867
7166
|
async function resolveNearestExistingAncestor(directory) {
|
|
@@ -6871,18 +7170,18 @@ async function resolveNearestExistingAncestor(directory) {
|
|
|
6871
7170
|
try {
|
|
6872
7171
|
return { existingAncestor: await realpath(current), missingSegments };
|
|
6873
7172
|
} catch (err) {
|
|
6874
|
-
const parent =
|
|
7173
|
+
const parent = dirname6(current);
|
|
6875
7174
|
if (err.code !== "ENOENT" || parent === current) {
|
|
6876
7175
|
throw err;
|
|
6877
7176
|
}
|
|
6878
|
-
missingSegments.unshift(
|
|
7177
|
+
missingSegments.unshift(basename2(current));
|
|
6879
7178
|
current = parent;
|
|
6880
7179
|
}
|
|
6881
7180
|
}
|
|
6882
7181
|
}
|
|
6883
7182
|
async function findContainingAllowedDirectory(allowedDirectories, realTarget) {
|
|
6884
7183
|
for (const directory of allowedDirectories) {
|
|
6885
|
-
if (!
|
|
7184
|
+
if (!isAbsolute2(directory)) {
|
|
6886
7185
|
log("warn", "file_push_allowed_directory_skipped", { directory, reason: "not_absolute" });
|
|
6887
7186
|
continue;
|
|
6888
7187
|
}
|
|
@@ -6920,19 +7219,19 @@ async function realpathCreatingIfMissing(directory) {
|
|
|
6920
7219
|
}
|
|
6921
7220
|
}
|
|
6922
7221
|
function contains(realDirectory, realTarget) {
|
|
6923
|
-
const rel =
|
|
6924
|
-
return rel !== "" && rel !== ".." && !rel.startsWith(`..${
|
|
7222
|
+
const rel = relative2(realDirectory, realTarget);
|
|
7223
|
+
return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep2}`) && !isAbsolute2(rel);
|
|
6925
7224
|
}
|
|
6926
7225
|
async function createMissingDirectories(existingAncestor, missingSegments) {
|
|
6927
7226
|
let current = existingAncestor;
|
|
6928
7227
|
for (const segment of missingSegments) {
|
|
6929
|
-
current =
|
|
7228
|
+
current = join9(current, segment);
|
|
6930
7229
|
await mkdir(current, { recursive: true, mode: DIRECTORY_MODE });
|
|
6931
7230
|
await chmod(current, DIRECTORY_MODE);
|
|
6932
7231
|
}
|
|
6933
7232
|
}
|
|
6934
7233
|
async function writeAtomically(realTarget, content) {
|
|
6935
|
-
const temporaryPath =
|
|
7234
|
+
const temporaryPath = join9(dirname6(realTarget), `.evident-push-${randomUUID()}.tmp`);
|
|
6936
7235
|
let handle;
|
|
6937
7236
|
try {
|
|
6938
7237
|
handle = await open3(temporaryPath, "wx", FILE_MODE);
|
|
@@ -7062,12 +7361,12 @@ var NOT_APPLIED = {
|
|
|
7062
7361
|
opencodeAuthApplied: false
|
|
7063
7362
|
};
|
|
7064
7363
|
function isClaudeCredentialPath(requestedPath, homeDir) {
|
|
7065
|
-
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ?
|
|
7066
|
-
return expanded ===
|
|
7364
|
+
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join10(homeDir, requestedPath.slice(2)) : requestedPath;
|
|
7365
|
+
return expanded === join10(homeDir, ...CLAUDE_CREDENTIALS_SEGMENTS);
|
|
7067
7366
|
}
|
|
7068
7367
|
function isOpenCodeAuthPath(requestedPath, homeDir) {
|
|
7069
|
-
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ?
|
|
7070
|
-
return expanded ===
|
|
7368
|
+
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join10(homeDir, requestedPath.slice(2)) : requestedPath;
|
|
7369
|
+
return expanded === join10(homeDir, ...OPENCODE_AUTH_SEGMENTS);
|
|
7071
7370
|
}
|
|
7072
7371
|
async function applyOne(options, file) {
|
|
7073
7372
|
const label = `${file.id.slice(0, 8)} (${file.path})`;
|
|
@@ -7238,7 +7537,7 @@ var SESSION_ERROR_BUFFER_TTL_MS = SESSION_ERROR_STREAM_HEALTHY_MS;
|
|
|
7238
7537
|
var MAX_BUFFERED_SESSION_ERRORS = 256;
|
|
7239
7538
|
var SESSION_ERROR_RETENTION_TTL_MS = 6e4;
|
|
7240
7539
|
var SESSION_STORE_UNWRITABLE_ERROR = "The runner session database could not be written, so this turn did not finish. Nothing was lost from the queue. Clear disk space and retry.";
|
|
7241
|
-
var SESSION_ERROR_RETAINED_DETAIL = "
|
|
7540
|
+
var SESSION_ERROR_RETAINED_DETAIL = "Runner-observed OpenCode session error: ";
|
|
7242
7541
|
var SESSION_ERROR_OBSERVED_CAUSE = "Runner-observed OpenCode session-store cause: ";
|
|
7243
7542
|
var DEFAULT_PAUSED_POLL_INTERVAL_MS = 2e3;
|
|
7244
7543
|
var DEFAULT_PAUSED_MAX_WAIT_MS = 10 * 60 * 1e3;
|
|
@@ -9853,7 +10152,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9853
10152
|
if (!inFlight.done) {
|
|
9854
10153
|
const messageErrorText = messageError(messages, inFlight.opencodeMessageId);
|
|
9855
10154
|
const retainedReason = this.readRetainedSessionError(sessionId);
|
|
9856
|
-
const retainedError = retainedReason === null ? null : `${SESSION_ERROR_RETAINED_DETAIL}${retainedReason}`;
|
|
10155
|
+
const retainedError = retainedReason === null || messageErrorText !== null && messageErrorText.includes(retainedReason) ? null : `${SESSION_ERROR_RETAINED_DETAIL}${retainedReason}`;
|
|
9857
10156
|
const observedCause = this.sessionStoreHealth?.readCause(sessionId);
|
|
9858
10157
|
const causeError = observedCause === null || observedCause === void 0 ? null : `${SESSION_ERROR_OBSERVED_CAUSE}${observedCause}`;
|
|
9859
10158
|
const storeError = this.sessionStoreHealth?.isUnwritable() ? SESSION_STORE_UNWRITABLE_ERROR : null;
|
|
@@ -11562,14 +11861,14 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
11562
11861
|
const fetchChildWithoutBlocking = async (sessionId) => {
|
|
11563
11862
|
const pending = fetchChildMessages(sessionId);
|
|
11564
11863
|
let timer;
|
|
11565
|
-
const timeout = new Promise((
|
|
11864
|
+
const timeout = new Promise((resolve5) => {
|
|
11566
11865
|
timer = setTimeout(() => {
|
|
11567
11866
|
this.log({
|
|
11568
11867
|
level: "warn",
|
|
11569
11868
|
message: `Best-effort subagent session fetch for message ${messageId.slice(0, 8)} and child session ${sessionId.slice(0, 8)} was slow \u2014 omitting invocation telemetry without delaying completion`,
|
|
11570
11869
|
message_id: messageId
|
|
11571
11870
|
});
|
|
11572
|
-
|
|
11871
|
+
resolve5(null);
|
|
11573
11872
|
}, SUBAGENT_TELEMETRY_FETCH_TIMEOUT_MS);
|
|
11574
11873
|
});
|
|
11575
11874
|
try {
|
|
@@ -12811,7 +13110,7 @@ function commandError2(result) {
|
|
|
12811
13110
|
return result.stderr.trim() || `process exited with code ${result.code ?? "null"}`;
|
|
12812
13111
|
}
|
|
12813
13112
|
var runCommand2 = (command, args, opts) => {
|
|
12814
|
-
return new Promise((
|
|
13113
|
+
return new Promise((resolve5) => {
|
|
12815
13114
|
let child;
|
|
12816
13115
|
let stdout = "";
|
|
12817
13116
|
let stderr = "";
|
|
@@ -12821,7 +13120,7 @@ var runCommand2 = (command, args, opts) => {
|
|
|
12821
13120
|
if (settled) return;
|
|
12822
13121
|
settled = true;
|
|
12823
13122
|
if (timer.handle) clearTimeout(timer.handle);
|
|
12824
|
-
|
|
13123
|
+
resolve5(result);
|
|
12825
13124
|
};
|
|
12826
13125
|
try {
|
|
12827
13126
|
child = spawn5(command, args, { env: opts.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -13077,7 +13376,7 @@ async function configureGitHubAccess({ env, log: log3 }) {
|
|
|
13077
13376
|
// src/lib/opencode/config-overlay.ts
|
|
13078
13377
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
13079
13378
|
import { copyFileSync, existsSync as existsSync2, statSync as statSync6 } from "node:fs";
|
|
13080
|
-
import { isAbsolute as
|
|
13379
|
+
import { isAbsolute as isAbsolute3, join as join11, resolve as resolve4 } from "node:path";
|
|
13081
13380
|
function isFile(filePath) {
|
|
13082
13381
|
return existsSync2(filePath) && statSync6(filePath).isFile();
|
|
13083
13382
|
}
|
|
@@ -13090,8 +13389,8 @@ function applyRunnerOpenCodeConfig({
|
|
|
13090
13389
|
log3("runner OpenCode config is not configured; using the baked project config", "debug");
|
|
13091
13390
|
return;
|
|
13092
13391
|
}
|
|
13093
|
-
const source =
|
|
13094
|
-
const target = isFile(
|
|
13392
|
+
const source = isAbsolute3(overlayPath) ? overlayPath : resolve4(cwd, overlayPath);
|
|
13393
|
+
const target = isFile(join11(cwd, "opencode.jsonc")) ? "opencode.jsonc" : "opencode.json";
|
|
13095
13394
|
if (!isFile(source)) {
|
|
13096
13395
|
log3(
|
|
13097
13396
|
`RUNNER-OPENCODE-CONFIG-MISSING: ${source} is not a file; headless turns will wedge on the first external-directory permission prompt (#563)`,
|
|
@@ -13099,7 +13398,7 @@ function applyRunnerOpenCodeConfig({
|
|
|
13099
13398
|
);
|
|
13100
13399
|
return;
|
|
13101
13400
|
}
|
|
13102
|
-
copyFileSync(source,
|
|
13401
|
+
copyFileSync(source, join11(cwd, target));
|
|
13103
13402
|
try {
|
|
13104
13403
|
execFileSync2("git", ["-C", cwd, "update-index", "--skip-worktree", target], {
|
|
13105
13404
|
stdio: "ignore"
|
|
@@ -13108,7 +13407,7 @@ function applyRunnerOpenCodeConfig({
|
|
|
13108
13407
|
const detail = error2 instanceof Error ? error2.message : String(error2);
|
|
13109
13408
|
log3(`could not mark ${target} skip-worktree: ${detail}; it may show as a local change`, "warn");
|
|
13110
13409
|
}
|
|
13111
|
-
log3(`Applied runner OpenCode config ${source} to ${
|
|
13410
|
+
log3(`Applied runner OpenCode config ${source} to ${join11(cwd, target)}`);
|
|
13112
13411
|
}
|
|
13113
13412
|
|
|
13114
13413
|
// src/lib/credential-sync.ts
|
|
@@ -13126,14 +13425,14 @@ function errorMessage2(error2) {
|
|
|
13126
13425
|
return error2 instanceof Error ? error2.message : String(error2);
|
|
13127
13426
|
}
|
|
13128
13427
|
function waitForSettlement(promise, timeoutMs) {
|
|
13129
|
-
return new Promise((
|
|
13428
|
+
return new Promise((resolve5) => {
|
|
13130
13429
|
let settled = false;
|
|
13131
13430
|
const timer = setTimeout(() => finish(false), Math.max(0, timeoutMs));
|
|
13132
13431
|
const finish = (value) => {
|
|
13133
13432
|
if (settled) return;
|
|
13134
13433
|
settled = true;
|
|
13135
13434
|
clearTimeout(timer);
|
|
13136
|
-
|
|
13435
|
+
resolve5(value);
|
|
13137
13436
|
};
|
|
13138
13437
|
promise.then(
|
|
13139
13438
|
() => finish(true),
|
|
@@ -13384,8 +13683,8 @@ function resolveFileSyncDirectories(raw, homeDir) {
|
|
|
13384
13683
|
if (trimmed === "") {
|
|
13385
13684
|
throw new Error("--enable-file-sync-to requires a directory path (got an empty value)");
|
|
13386
13685
|
}
|
|
13387
|
-
const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ?
|
|
13388
|
-
if (!
|
|
13686
|
+
const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ? join12(homeDir, trimmed.slice(2)) : trimmed;
|
|
13687
|
+
if (!isAbsolute4(expanded)) {
|
|
13389
13688
|
throw new Error(`--enable-file-sync-to requires an absolute directory path; got "${entry}"`);
|
|
13390
13689
|
}
|
|
13391
13690
|
const normalized = resolvePath2(expanded);
|
|
@@ -13485,8 +13784,7 @@ function resolveMaxActiveSessions(options, env = process.env) {
|
|
|
13485
13784
|
function meetsThreshold(state, level) {
|
|
13486
13785
|
return LOG_LEVELS[level] >= LOG_LEVELS[state.logLevel];
|
|
13487
13786
|
}
|
|
13488
|
-
function
|
|
13489
|
-
if (!meetsThreshold(state, level)) return;
|
|
13787
|
+
function writeConsoleLog(state, message, level) {
|
|
13490
13788
|
if (state.json) {
|
|
13491
13789
|
console.log(
|
|
13492
13790
|
JSON.stringify({
|
|
@@ -13495,10 +13793,18 @@ function log2(state, message, level = "info") {
|
|
|
13495
13793
|
message
|
|
13496
13794
|
})
|
|
13497
13795
|
);
|
|
13498
|
-
|
|
13499
|
-
const prefix = level === "error" ? chalk7.red("\u2717") : level === "warn" ? chalk7.yellow("!") : level === "debug" ? chalk7.dim("\xB7") : chalk7.green("\u2022");
|
|
13500
|
-
console.log(`${prefix} ${message}`);
|
|
13796
|
+
return;
|
|
13501
13797
|
}
|
|
13798
|
+
const prefix = level === "error" ? chalk7.red("\u2717") : level === "warn" ? chalk7.yellow("!") : level === "debug" ? chalk7.dim("\xB7") : chalk7.green("\u2022");
|
|
13799
|
+
console.log(`${prefix} ${message}`);
|
|
13800
|
+
}
|
|
13801
|
+
function log2(state, message, level = "info") {
|
|
13802
|
+
if (!meetsThreshold(state, level)) return;
|
|
13803
|
+
if (state.json || !state.interactive) writeConsoleLog(state, message, level);
|
|
13804
|
+
}
|
|
13805
|
+
function logLocalOnly(state, message, level = "info") {
|
|
13806
|
+
if (!meetsThreshold(state, level)) return;
|
|
13807
|
+
writeConsoleLog(state, message, level);
|
|
13502
13808
|
}
|
|
13503
13809
|
function logActivity(state, entry) {
|
|
13504
13810
|
const level = entry.level ?? (entry.type === "error" ? "error" : "info");
|
|
@@ -13800,7 +14106,7 @@ async function driveChannels(state, driver) {
|
|
|
13800
14106
|
}
|
|
13801
14107
|
}
|
|
13802
14108
|
}
|
|
13803
|
-
await new Promise((
|
|
14109
|
+
await new Promise((resolve5) => setTimeout(resolve5, CHANNEL_POLL_INTERVAL_MS));
|
|
13804
14110
|
const cycleMs = performance.now() - cycleStartedAtMs;
|
|
13805
14111
|
if (idleThisCycle) idleMs += cycleMs;
|
|
13806
14112
|
if (unreachableThisCycle) unreachableMs += cycleMs;
|
|
@@ -13835,7 +14141,7 @@ function shouldWarnForReclaimSkip(reason) {
|
|
|
13835
14141
|
return major > 22 || major === 22 && (minor > 13 || minor === 13 && patch >= 0);
|
|
13836
14142
|
}
|
|
13837
14143
|
function sessionDbPath() {
|
|
13838
|
-
return
|
|
14144
|
+
return join12(homedir7(), ".local", "share", "opencode", "opencode.db");
|
|
13839
14145
|
}
|
|
13840
14146
|
function logSessionCleanupActivity(state, phase, entry) {
|
|
13841
14147
|
logActivity(state, {
|
|
@@ -14515,8 +14821,8 @@ async function beginGracefulShutdown(state, trigger) {
|
|
|
14515
14821
|
return true;
|
|
14516
14822
|
}
|
|
14517
14823
|
);
|
|
14518
|
-
const timedOut = new Promise((
|
|
14519
|
-
timer = setTimeout(() =>
|
|
14824
|
+
const timedOut = new Promise((resolve5) => {
|
|
14825
|
+
timer = setTimeout(() => resolve5(false), telemetryBudgetMs);
|
|
14520
14826
|
});
|
|
14521
14827
|
if (!await Promise.race([flushed, timedOut])) {
|
|
14522
14828
|
log2(state, `Telemetry flush exceeded ${telemetryBudgetMs}ms \u2014 exiting anyway`, "warn");
|
|
@@ -14804,6 +15110,17 @@ async function run(options) {
|
|
|
14804
15110
|
}
|
|
14805
15111
|
if (githubTokenPopulated) await configureGitHubAccess(credentialContext);
|
|
14806
15112
|
state.credentialSync?.arm();
|
|
15113
|
+
const dependencyInstallDiagnosticReporter = createDependencyInstallDiagnosticReporter({
|
|
15114
|
+
context: {
|
|
15115
|
+
homeDir: homedir7(),
|
|
15116
|
+
cwd: process.cwd(),
|
|
15117
|
+
microvmId: process.env.MICROVM_ID?.trim(),
|
|
15118
|
+
euid: process.getuid?.(),
|
|
15119
|
+
egid: process.getgid?.()
|
|
15120
|
+
},
|
|
15121
|
+
emitEvent: (message) => logActivity(state, { type: "info", level: "warn", message }),
|
|
15122
|
+
emitLocal: (message) => logLocalOnly(state, message, "warn")
|
|
15123
|
+
});
|
|
14807
15124
|
const openCodeActivityForwarder = createOpenCodeActivityForwarder(
|
|
14808
15125
|
() => ({
|
|
14809
15126
|
agentId: state.agentId,
|
|
@@ -14814,7 +15131,8 @@ async function run(options) {
|
|
|
14814
15131
|
if (cause !== null && sessionId !== null) {
|
|
14815
15132
|
state.sessionStoreHealth.observeCause(sessionId, cause);
|
|
14816
15133
|
}
|
|
14817
|
-
}
|
|
15134
|
+
},
|
|
15135
|
+
dependencyInstallDiagnosticReporter
|
|
14818
15136
|
);
|
|
14819
15137
|
const openCodeLogTail = tailOpenCodeLogFile(
|
|
14820
15138
|
resolveOpenCodeLogPath(homedir7(), process.env),
|