* refactor(query): deepen command definition seam and fold fallback mapping cleanup * refactor(query): add shared dispatch formatting module seam * fix(query): restore QueryResult type import in dispatch deps * test/query: align raw-output policy and definition normalization contracts * refactor(query): deepen diagnosis, invariant report, and error taxonomy seams * refactor(query): deepen dispatch plan, fallback bridge, policy snapshot, and hints seams * refactor(query): deepen validation, fallback policy, capability, and result builder seams * refactor(query): deepen resolution strategy, output classifier, observability, and policy-capability seams * refactor(query): finalize deep strategy/classifier/observability/capability seams * test/query: address coderabbit inline and out-of-diff dispatch nits * fix(query): address remaining coderabbit input-validation and bridge stderr threads * fix(query): address remaining coderabbit dispatch and strategy/output nits
55 lines
1.8 KiB
TypeScript
55 lines
1.8 KiB
TypeScript
import { execFile } from 'node:child_process';
|
|
import { classifyFallbackOutput } from './query-fallback-output-classifier.js';
|
|
|
|
export interface FallbackBridgeRunInput {
|
|
projectDir: string;
|
|
gsdToolsPath: string;
|
|
normCmd: string;
|
|
normArgs: string[];
|
|
ws?: string;
|
|
}
|
|
|
|
export interface FallbackBridgeOutput {
|
|
mode: 'json' | 'text';
|
|
output: unknown;
|
|
stderr: string;
|
|
}
|
|
|
|
function dottedCommandToCjsArgv(normCmd: string, normArgs: string[]): string[] {
|
|
if (normCmd.includes('.')) return [...normCmd.split('.'), ...normArgs];
|
|
return [normCmd, ...normArgs];
|
|
}
|
|
|
|
function execBridge(input: FallbackBridgeRunInput): Promise<{ stdout: string; stderr: string }> {
|
|
const cjsArgv = dottedCommandToCjsArgv(input.normCmd, input.normArgs);
|
|
const wsSuffix = input.ws ? ['--ws', input.ws] : [];
|
|
const fullArgv = [input.gsdToolsPath, ...cjsArgv, ...wsSuffix];
|
|
|
|
return new Promise((resolve, reject) => {
|
|
execFile(
|
|
process.execPath,
|
|
fullArgv,
|
|
{ cwd: input.projectDir, maxBuffer: 10 * 1024 * 1024, timeout: 30_000, killSignal: 'SIGKILL', env: { ...process.env } },
|
|
(err, stdout, stderr) => {
|
|
const stdoutText = stdout?.toString() ?? '';
|
|
const stderrText = stderr?.toString() ?? '';
|
|
if (err) {
|
|
if (stderrText.trim()) {
|
|
reject(new Error(`${err.message}\n${stderrText.trimEnd()}`));
|
|
return;
|
|
}
|
|
reject(err);
|
|
return;
|
|
}
|
|
resolve({ stdout: stdoutText, stderr: stderrText });
|
|
},
|
|
);
|
|
});
|
|
}
|
|
|
|
export async function runFallbackBridge(input: FallbackBridgeRunInput): Promise<FallbackBridgeOutput> {
|
|
const { stdout, stderr } = await execBridge(input);
|
|
const classified = await classifyFallbackOutput(stdout, input.projectDir);
|
|
return { ...classified, stderr };
|
|
}
|