Merge branch 'main' into feat/mattermost-channel

This commit is contained in:
Dominic Damoah
2026-01-22 18:17:40 -05:00
committed by GitHub
149 changed files with 5513 additions and 672 deletions

View File

@@ -358,14 +358,20 @@ function resolveExecutablePath(rawExecutable: string, cwd?: string, env?: NodeJS
const candidate = path.resolve(base, expanded);
return isExecutableFile(candidate) ? candidate : undefined;
}
const envPath = env?.PATH ?? process.env.PATH ?? "";
const envPath = env?.PATH ?? env?.Path ?? process.env.PATH ?? process.env.Path ?? "";
const entries = envPath.split(path.delimiter).filter(Boolean);
const hasExtension = process.platform === "win32" && path.extname(expanded).length > 0;
const extensions =
process.platform === "win32"
? hasExtension
? [""]
: (env?.PATHEXT ?? process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM")
: (
env?.PATHEXT ??
env?.Pathext ??
process.env.PATHEXT ??
process.env.Pathext ??
".EXE;.CMD;.BAT;.COM"
)
.split(";")
.map((ext) => ext.toLowerCase())
: [""];
@@ -459,6 +465,21 @@ function matchesPattern(pattern: string, target: string): boolean {
return regex.test(normalizedTarget);
}
function resolveAllowlistCandidatePath(
resolution: CommandResolution | null,
cwd?: string,
): string | undefined {
if (!resolution) return undefined;
if (resolution.resolvedPath) return resolution.resolvedPath;
const raw = resolution.rawExecutable?.trim();
if (!raw) return undefined;
const expanded = raw.startsWith("~") ? expandHome(raw) : raw;
if (!expanded.includes("/") && !expanded.includes("\\")) return undefined;
if (path.isAbsolute(expanded)) return expanded;
const base = cwd && cwd.trim() ? cwd.trim() : process.cwd();
return path.resolve(base, expanded);
}
export function matchAllowlist(
entries: ExecAllowlistEntry[],
resolution: CommandResolution | null,
@@ -513,7 +534,7 @@ function splitShellPipeline(command: string): { ok: boolean; reason?: string; se
escaped = false;
continue;
}
if (!inSingle && ch === "\\") {
if (!inSingle && !inDouble && ch === "\\") {
escaped = true;
buf += ch;
continue;
@@ -589,7 +610,7 @@ function tokenizeShellSegment(segment: string): string[] | null {
escaped = false;
continue;
}
if (!inSingle && ch === "\\") {
if (!inSingle && !inDouble && ch === "\\") {
escaped = true;
continue;
}
@@ -764,7 +785,12 @@ export function evaluateExecAllowlist(params: {
}
const allowSkills = params.autoAllowSkills === true && (params.skillBins?.size ?? 0) > 0;
const allowlistSatisfied = params.analysis.segments.every((segment) => {
const match = matchAllowlist(params.allowlist, segment.resolution);
const candidatePath = resolveAllowlistCandidatePath(segment.resolution, params.cwd);
const candidateResolution =
candidatePath && segment.resolution
? { ...segment.resolution, resolvedPath: candidatePath }
: segment.resolution;
const match = matchAllowlist(params.allowlist, candidateResolution);
if (match) allowlistMatches.push(match);
const safe = isSafeBinUsage({
argv: segment.argv,

View File

@@ -184,9 +184,19 @@ export async function loadCostUsageSummary(params?: {
const sessionsDir = resolveSessionTranscriptsDirForAgent(params?.agentId);
const entries = await fs.promises.readdir(sessionsDir, { withFileTypes: true }).catch(() => []);
const files = entries
.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl"))
.map((entry) => path.join(sessionsDir, entry.name));
const files = (
await Promise.all(
entries
.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl"))
.map(async (entry) => {
const filePath = path.join(sessionsDir, entry.name);
const stats = await fs.promises.stat(filePath).catch(() => null);
if (!stats) return null;
if (stats.mtimeMs < sinceTime) return null;
return filePath;
}),
)
).filter((filePath): filePath is string => Boolean(filePath));
for (const filePath of files) {
await scanUsageFile({