fix: flush CLI stdio before forced exit

This commit is contained in:
clawsweeper 2026-06-23 10:27:20 +00:00
parent ac1e1125f8
commit fb24965011
2 changed files with 38 additions and 2 deletions

View File

@ -1,5 +1,6 @@
import { spawn } from 'node:child_process';
import fs from 'node:fs/promises';
import path from 'node:path';
import type { ServerDefinition, ServerSource } from './config.js';
export type CliArtifactKind = 'template' | 'bundle' | 'binary';
@ -98,7 +99,8 @@ export async function readCliMetadata(artifactPath: string): Promise<CliArtifact
async function readMetadataFromCli(artifactPath: string): Promise<CliArtifactMetadata> {
return await new Promise<CliArtifactMetadata>((resolve, reject) => {
const child = spawn(artifactPath, ['__mcporter_inspect'], {
const inspectCommand = resolveMetadataInspectCommand(artifactPath);
const child = spawn(inspectCommand.command, inspectCommand.args, {
stdio: ['ignore', 'pipe', 'pipe'],
});
let stdout = '';
@ -139,6 +141,18 @@ async function readMetadataFromCli(artifactPath: string): Promise<CliArtifactMet
});
}
function resolveMetadataInspectCommand(artifactPath: string): { readonly command: string; readonly args: string[] } {
if (process.platform === 'win32' && shouldRunArtifactWithNode(artifactPath)) {
return { command: process.execPath, args: [artifactPath, '__mcporter_inspect'] };
}
return { command: artifactPath, args: ['__mcporter_inspect'] };
}
function shouldRunArtifactWithNode(artifactPath: string): boolean {
const extension = path.extname(artifactPath).toLowerCase();
return extension === '' || extension === '.js' || extension === '.mjs' || extension === '.cjs' || extension === '.ts';
}
function isErrno(error: unknown, code: string): error is NodeJS.ErrnoException {
return Boolean(error && typeof error === 'object' && (error as NodeJS.ErrnoException).code === code);
}

View File

@ -1,7 +1,7 @@
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { describe, expect, it } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
import { metadataPathForArtifact, readCliMetadata } from '../src/cli-metadata.js';
describe('readCliMetadata', () => {
@ -22,6 +22,28 @@ describe('readCliMetadata', () => {
server: { name: 'embedded' },
});
});
it('reads embedded metadata from extensionless JavaScript artifacts on Windows', async () => {
const platformSpy = vi.spyOn(process, 'platform', 'get').mockReturnValue('win32');
try {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mcporter-metadata-win-'));
const artifact = path.join(tempDir, 'artifact');
const embedded = metadataPayload('embedded');
const sidecar = metadataPayload('sidecar');
await fs.writeFile(
artifact,
`#!/usr/bin/env node\nconsole.log(${JSON.stringify(JSON.stringify(embedded))});\n`,
'utf8'
);
await fs.writeFile(metadataPathForArtifact(artifact), JSON.stringify(sidecar), 'utf8');
await expect(readCliMetadata(artifact)).resolves.toMatchObject({
server: { name: 'embedded' },
});
} finally {
platformSpy.mockRestore();
}
});
});
function metadataPayload(name: string) {