feat(skills): add optional shell command runner
This commit is contained in:
@@ -23,6 +23,7 @@ import {
|
||||
mergeInstallerExecutionResults,
|
||||
runInstallerCommandsWithPolicy,
|
||||
noOpSkillInstallerCommandRunner,
|
||||
createShellSkillInstallerCommandRunner,
|
||||
runSkillInstallAction,
|
||||
} from './skills.js';
|
||||
import type { Skill } from '../skills/index.js';
|
||||
@@ -376,6 +377,33 @@ describe('skills CLI helpers', () => {
|
||||
expect(runner.run).toHaveBeenCalledWith(['brew install jq']);
|
||||
});
|
||||
|
||||
it('shell command runner reports succeeded command status', () => {
|
||||
const runner = createShellSkillInstallerCommandRunner();
|
||||
|
||||
const results = runner.run(['node -e "process.exit(0)"']);
|
||||
|
||||
expect(results).toEqual([
|
||||
{
|
||||
command: 'node -e "process.exit(0)"',
|
||||
status: 'succeeded',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('shell command runner reports failed command with exit code reason', () => {
|
||||
const runner = createShellSkillInstallerCommandRunner();
|
||||
|
||||
const results = runner.run(['node -e "process.exit(7)"']);
|
||||
|
||||
expect(results).toEqual([
|
||||
{
|
||||
command: 'node -e "process.exit(7)"',
|
||||
status: 'failed',
|
||||
reason: 'exit_code_7',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('maps runner command results into structured per-step statuses', () => {
|
||||
const attempted = [
|
||||
{ installer_type: 'brew', command: 'brew install jq' },
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Command } from 'commander';
|
||||
import { resolve } from 'path';
|
||||
import { homedir } from 'os';
|
||||
import { spawnSync } from 'child_process';
|
||||
import type { Skill } from '../skills/index.js';
|
||||
import { loadAllSkills, SkillInstaller, buildInstallerPlan, loadSkill } from '../skills/index.js';
|
||||
import { loadConfigSafe } from './shared.js';
|
||||
@@ -78,6 +79,57 @@ export const noOpSkillInstallerCommandRunner: SkillInstallerCommandRunner = {
|
||||
},
|
||||
};
|
||||
|
||||
export function createShellSkillInstallerCommandRunner(): SkillInstallerCommandRunner {
|
||||
return {
|
||||
run(commands: string[]): SkillInstallerCommandRunResult[] {
|
||||
return commands.map((command) => {
|
||||
const result = spawnSync(command, {
|
||||
shell: true,
|
||||
stdio: 'pipe',
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
return {
|
||||
command,
|
||||
status: 'failed',
|
||||
reason: `spawn_error:${result.error.message}`,
|
||||
};
|
||||
}
|
||||
|
||||
if (result.status === 0) {
|
||||
return {
|
||||
command,
|
||||
status: 'succeeded',
|
||||
};
|
||||
}
|
||||
|
||||
if (result.status !== null) {
|
||||
return {
|
||||
command,
|
||||
status: 'failed',
|
||||
reason: `exit_code_${result.status}`,
|
||||
};
|
||||
}
|
||||
|
||||
if (result.signal) {
|
||||
return {
|
||||
command,
|
||||
status: 'failed',
|
||||
reason: `signal_${result.signal}`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
command,
|
||||
status: 'failed',
|
||||
reason: 'unknown_failure',
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function toInstallerExecutionStepEnvelopes(
|
||||
steps: Array<{ installerType: string; command: string }>,
|
||||
policy: SkillInstallerExecutionPolicy,
|
||||
|
||||
Reference in New Issue
Block a user