refactor: deepen transport policy and output projection paths

This commit is contained in:
Tom Boucher
2026-05-04 20:25:21 -04:00
parent 969cfcf998
commit c6a35d6398

View File

@@ -32,33 +32,48 @@ export class GSDTransport {
) {}
async run(request: TransportRequest, policy: TransportPolicyLike): Promise<unknown> {
const forceSubprocess = Boolean(request.workstream);
if (!forceSubprocess && policy.preferNative && this.registry.has(request.registryCommand)) {
if (this.shouldUseNative(request, policy)) {
try {
const native = await this.adapters.dispatchNative(request);
if (request.mode === 'raw') {
if (this.adapters.formatNativeRaw) {
return this.adapters.formatNativeRaw(request.registryCommand, native.data).trim();
}
return this.toRaw(native.data);
}
return native.data;
return this.projectNativeOutput(request, native.data);
} catch (error) {
if (!policy.allowFallbackToSubprocess) throw error;
// Do not subprocess-fallback after a timed-out native dispatch:
// the timeout does not cancel the native handler, so falling through
// would run the same command twice (double-execution race).
if (toFailureSignal(error).kind === 'timeout') throw error;
if (this.shouldRethrowNativeError(error, policy)) throw error;
}
}
return this.dispatchSubprocess(request);
}
private shouldUseNative(request: TransportRequest, policy: TransportPolicyLike): boolean {
const forceSubprocess = Boolean(request.workstream);
return !forceSubprocess && policy.preferNative && this.registry.has(request.registryCommand);
}
private shouldRethrowNativeError(error: unknown, policy: TransportPolicyLike): boolean {
if (!policy.allowFallbackToSubprocess) return true;
// Do not subprocess-fallback after a timed-out native dispatch:
// the timeout does not cancel the native handler, so falling through
// would run the same command twice (double-execution race).
return toFailureSignal(error).kind === 'timeout';
}
private dispatchSubprocess(request: TransportRequest): Promise<unknown> {
if (request.mode === 'raw') {
return this.adapters.execSubprocessRaw(request.legacyCommand, request.legacyArgs);
}
return this.adapters.execSubprocessJson(request.legacyCommand, request.legacyArgs);
}
private projectNativeOutput(request: TransportRequest, data: unknown): unknown {
if (request.mode === 'raw') {
if (this.adapters.formatNativeRaw) {
return this.adapters.formatNativeRaw(request.registryCommand, data).trim();
}
return this.toRaw(data);
}
return data;
}
private toRaw(data: unknown): string {
if (typeof data === 'string') return data.trim();
const json = JSON.stringify(data, null, 2);