perf(main): cache runtime command arg matches
This commit is contained in:
parent
5a3e59f765
commit
228ebd6454
2 changed files with 66 additions and 1 deletions
|
|
@ -46,7 +46,9 @@ const SHELL_COMMAND_NAMES = new Set(['sh', 'bash', 'zsh', 'fish', 'dash', 'login
|
||||||
const SECRET_FLAG_PATTERN =
|
const SECRET_FLAG_PATTERN =
|
||||||
/(--(?:api-key|token|password|secret|authorization|auth-token)(?:=|\s+))("[^"]*"|'[^']*'|\S+)/gi;
|
/(--(?:api-key|token|password|secret|authorization|auth-token)(?:=|\s+))("[^"]*"|'[^']*'|\S+)/gi;
|
||||||
const CLI_ARG_VALUES_CACHE_MAX_COMMANDS = 1_000;
|
const CLI_ARG_VALUES_CACHE_MAX_COMMANDS = 1_000;
|
||||||
|
const CLI_ARG_EQUALS_CACHE_MAX_KEYS_PER_COMMAND = 100;
|
||||||
const cliArgValuesCache = new Map<string, Map<string, string[]>>();
|
const cliArgValuesCache = new Map<string, Map<string, string[]>>();
|
||||||
|
const cliArgEqualsCache = new Map<string, Map<string, boolean>>();
|
||||||
|
|
||||||
function basenameCommand(command: string | undefined): string {
|
function basenameCommand(command: string | undefined): string {
|
||||||
const firstToken = command?.trim().split(/\s+/, 1)[0] ?? '';
|
const firstToken = command?.trim().split(/\s+/, 1)[0] ?? '';
|
||||||
|
|
@ -108,6 +110,51 @@ function getCachedCliArgValues(command: string, argName: string): readonly strin
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCachedCliArgEquals(
|
||||||
|
command: string,
|
||||||
|
argName: string,
|
||||||
|
normalizedExpected: string
|
||||||
|
): boolean | undefined {
|
||||||
|
const cachedByKey = cliArgEqualsCache.get(command);
|
||||||
|
if (!cachedByKey) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const cacheKey = `${argName}\0${normalizedExpected}`;
|
||||||
|
const cached = cachedByKey.get(cacheKey);
|
||||||
|
if (cached !== undefined) {
|
||||||
|
cliArgEqualsCache.delete(command);
|
||||||
|
cliArgEqualsCache.set(command, cachedByKey);
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCachedCliArgEquals(
|
||||||
|
command: string,
|
||||||
|
argName: string,
|
||||||
|
normalizedExpected: string,
|
||||||
|
value: boolean
|
||||||
|
): void {
|
||||||
|
let cachedByKey = cliArgEqualsCache.get(command);
|
||||||
|
if (!cachedByKey) {
|
||||||
|
cachedByKey = new Map<string, boolean>();
|
||||||
|
}
|
||||||
|
const cacheKey = `${argName}\0${normalizedExpected}`;
|
||||||
|
if (!cachedByKey.has(cacheKey) && cachedByKey.size >= CLI_ARG_EQUALS_CACHE_MAX_KEYS_PER_COMMAND) {
|
||||||
|
const oldestKey = cachedByKey.keys().next().value;
|
||||||
|
if (oldestKey !== undefined) {
|
||||||
|
cachedByKey.delete(oldestKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cachedByKey.set(cacheKey, value);
|
||||||
|
cliArgEqualsCache.delete(command);
|
||||||
|
cliArgEqualsCache.set(command, cachedByKey);
|
||||||
|
while (cliArgEqualsCache.size > CLI_ARG_VALUES_CACHE_MAX_COMMANDS) {
|
||||||
|
const oldestCommand = cliArgEqualsCache.keys().next().value;
|
||||||
|
if (oldestCommand === undefined) break;
|
||||||
|
cliArgEqualsCache.delete(oldestCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function extractCliArgValues(command: string, argName: string): string[] {
|
export function extractCliArgValues(command: string, argName: string): string[] {
|
||||||
const values = getCachedCliArgValues(command, argName);
|
const values = getCachedCliArgValues(command, argName);
|
||||||
return [...values];
|
return [...values];
|
||||||
|
|
@ -121,7 +168,15 @@ export function commandArgEquals(
|
||||||
const normalizedExpected = expected?.trim();
|
const normalizedExpected = expected?.trim();
|
||||||
if (!normalizedExpected) return false;
|
if (!normalizedExpected) return false;
|
||||||
if (!command.includes(argName)) return false;
|
if (!command.includes(argName)) return false;
|
||||||
return getCachedCliArgValues(command, argName).some((value) => value === normalizedExpected);
|
const cached = getCachedCliArgEquals(command, argName, normalizedExpected);
|
||||||
|
if (cached !== undefined) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
const value = getCachedCliArgValues(command, argName).some(
|
||||||
|
(argValue) => argValue === normalizedExpected
|
||||||
|
);
|
||||||
|
setCachedCliArgEquals(command, argName, normalizedExpected, value);
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function collectDescendants(
|
function collectDescendants(
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
commandArgEquals,
|
||||||
extractCliArgValues,
|
extractCliArgValues,
|
||||||
resolveTeamMemberRuntimeLiveness,
|
resolveTeamMemberRuntimeLiveness,
|
||||||
sanitizeProcessCommandForDiagnostics,
|
sanitizeProcessCommandForDiagnostics,
|
||||||
|
|
@ -285,4 +286,13 @@ describe('resolveTeamMemberRuntimeLiveness', () => {
|
||||||
it('returns no CLI arg values when the flag is absent', () => {
|
it('returns no CLI arg values when the flag is absent', () => {
|
||||||
expect(extractCliArgValues('node runtime --other value', '--agent-id')).toEqual([]);
|
expect(extractCliArgValues('node runtime --other value', '--agent-id')).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('matches CLI arg values repeatedly without changing extraction results', () => {
|
||||||
|
const command = 'node runtime --team-name demo --agent-id "agent alice"';
|
||||||
|
|
||||||
|
expect(commandArgEquals(command, '--agent-id', 'agent alice')).toBe(true);
|
||||||
|
expect(commandArgEquals(command, '--agent-id', 'agent-bob')).toBe(false);
|
||||||
|
expect(commandArgEquals(command, '--agent-id', 'agent alice')).toBe(true);
|
||||||
|
expect(extractCliArgValues(command, '--agent-id')).toEqual(['agent alice']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue