mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-08-19 13:45:28 +00:00
Add App-scope IHostProcessService in os/interface with a node-local backend, then make ISessionProcessRunner delegate to it instead of calling child_process directly. Session/process keeps cwd/env overlay semantics while os/ owns spawn/kill/cross-platform process-tree cleanup. - add os/interface/hostProcess.ts and hostProcessService.ts - update os/interface and os/backends barrels - migrate session/process/processRunnerService.ts to inject IHostProcessService - delete session/process/spawnedProcess.ts - add tests for HostProcessService and update processRunnerService tests
83 lines
2.6 KiB
TypeScript
83 lines
2.6 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
|
|
import { mkdtemp, rm } from 'node:fs/promises';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { Readable } from 'node:stream';
|
|
|
|
import { InstantiationType } from '#/_base/di/extensions';
|
|
import {
|
|
LifecycleScope,
|
|
_clearScopedRegistryForTests,
|
|
registerScopedService,
|
|
} from '#/_base/di/scope';
|
|
import { createScopedTestHost, stubPair } from '#/_base/di/test';
|
|
import { IHostProcessService } from '#/os/interface/hostProcess';
|
|
import { HostProcessService } from '#/os/backends/node-local/hostProcessService';
|
|
import { createExecContext, IExecContext } from '#/session/execContext';
|
|
import { ISessionProcessRunner, SessionProcessRunner } from '#/session/process';
|
|
|
|
async function collect(stream: Readable): Promise<string> {
|
|
const chunks: Buffer[] = [];
|
|
for await (const chunk of stream) {
|
|
chunks.push(chunk as Buffer);
|
|
}
|
|
return Buffer.concat(chunks).toString('utf8');
|
|
}
|
|
|
|
describe('SessionProcessRunner (backed by IExecContext)', () => {
|
|
let dir: string;
|
|
|
|
beforeEach(async () => {
|
|
_clearScopedRegistryForTests();
|
|
registerScopedService(
|
|
LifecycleScope.App,
|
|
IHostProcessService,
|
|
HostProcessService,
|
|
InstantiationType.Delayed,
|
|
'hostProcess',
|
|
);
|
|
registerScopedService(
|
|
LifecycleScope.Session,
|
|
ISessionProcessRunner,
|
|
SessionProcessRunner,
|
|
InstantiationType.Delayed,
|
|
'process',
|
|
);
|
|
dir = await mkdtemp(join(tmpdir(), 'procrunner-'));
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await rm(dir, { recursive: true, force: true });
|
|
});
|
|
|
|
async function makeRunner(): Promise<ISessionProcessRunner> {
|
|
const host = createScopedTestHost();
|
|
const session = host.child(
|
|
LifecycleScope.Session,
|
|
's',
|
|
[stubPair(IExecContext, createExecContext(dir))],
|
|
);
|
|
return session.accessor.get(ISessionProcessRunner);
|
|
}
|
|
|
|
it('exec runs a command and captures stdout + exit code', async () => {
|
|
const runner = await makeRunner();
|
|
const proc = await runner.exec(['node', '-e', 'process.stdout.write("ok")']);
|
|
const out = await collect(proc.stdout);
|
|
expect(out).toBe('ok');
|
|
expect(await proc.wait()).toBe(0);
|
|
expect(proc.exitCode).toBe(0);
|
|
});
|
|
|
|
it('exec overlays per-call env', async () => {
|
|
const runner = await makeRunner();
|
|
const proc = await runner.exec(
|
|
['node', '-e', 'process.stdout.write(process.env.FOO ?? "")'],
|
|
{ env: { FOO: 'bar' } },
|
|
);
|
|
const out = await collect(proc.stdout);
|
|
expect(out).toBe('bar');
|
|
expect(await proc.wait()).toBe(0);
|
|
});
|
|
});
|