fix(skills): align planner and cli tests on curl download commands
This commit is contained in:
@@ -3633,6 +3633,19 @@
|
|||||||
"docs/plans/state.json"
|
"docs/plans/state.json"
|
||||||
],
|
],
|
||||||
"test_status": "pnpm typecheck passing"
|
"test_status": "pnpm typecheck passing"
|
||||||
|
},
|
||||||
|
"installer-planner-cli-curl-reconciliation": {
|
||||||
|
"status": "completed",
|
||||||
|
"date": "2026-02-17",
|
||||||
|
"updated": "2026-02-17",
|
||||||
|
"summary": "Reconciled installer planning and CLI output/tests to use executable curl-based download commands (`curl -fsSL -o <dest> <url>` and `curl -fsSL <url>` when destination is omitted), replacing legacy placeholder command strings while preserving install/execute flow semantics.",
|
||||||
|
"files_modified": [
|
||||||
|
"src/skills/planner.ts",
|
||||||
|
"src/skills/planner.test.ts",
|
||||||
|
"src/cli/skills.test.ts",
|
||||||
|
"docs/plans/state.json"
|
||||||
|
],
|
||||||
|
"test_status": "pnpm test:run src/skills/planner.test.ts src/cli/skills.test.ts + pnpm typecheck passing"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"overall_progress": {
|
"overall_progress": {
|
||||||
|
|||||||
+30
-30
@@ -426,7 +426,7 @@ describe('skills CLI helpers', () => {
|
|||||||
|
|
||||||
expect(output).toContain('Installer plan mode: dry-run');
|
expect(output).toContain('Installer plan mode: dry-run');
|
||||||
expect(output).toContain('Installer planned steps:');
|
expect(output).toContain('Installer planned steps:');
|
||||||
expect(output).toContain('[download] download https://example.com/tool.tgz -> /tmp/tool.tgz');
|
expect(output).toContain('[download] curl -fsSL -o /tmp/tool.tgz https://example.com/tool.tgz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('builds installer plan view for automation output', () => {
|
it('builds installer plan view for automation output', () => {
|
||||||
@@ -452,13 +452,13 @@ describe('skills CLI helpers', () => {
|
|||||||
const output = renderSkillInstallerPlan({
|
const output = renderSkillInstallerPlan({
|
||||||
skill: { name: 'plan-target', tier: 'bundled', version: '1.0.0' },
|
skill: { name: 'plan-target', tier: 'bundled', version: '1.0.0' },
|
||||||
mode: 'dry-run',
|
mode: 'dry-run',
|
||||||
steps: [{ installerType: 'download', command: 'download https://example.com/tool -> /tmp/tool' }],
|
steps: [{ installerType: 'download', command: 'curl -fsSL -o /tmp/tool https://example.com/tool' }],
|
||||||
skipped: [{ installerType: 'brew', reason: 'brew not available in PATH' }],
|
skipped: [{ installerType: 'brew', reason: 'brew not available in PATH' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(output).toContain("Installer plan for 'plan-target'");
|
expect(output).toContain("Installer plan for 'plan-target'");
|
||||||
expect(output).toContain('Planned steps:');
|
expect(output).toContain('Planned steps:');
|
||||||
expect(output).toContain('[download] download https://example.com/tool -> /tmp/tool');
|
expect(output).toContain('[download] curl -fsSL -o /tmp/tool https://example.com/tool');
|
||||||
expect(output).toContain('Skipped steps:');
|
expect(output).toContain('Skipped steps:');
|
||||||
expect(output).toContain('[brew] brew not available in PATH');
|
expect(output).toContain('[brew] brew not available in PATH');
|
||||||
});
|
});
|
||||||
@@ -504,13 +504,13 @@ describe('skills CLI helpers', () => {
|
|||||||
sourcePath: '/tmp/source-skill',
|
sourcePath: '/tmp/source-skill',
|
||||||
skill: { name: 'preflight-skill', tier: 'managed', version: '1.0.0' },
|
skill: { name: 'preflight-skill', tier: 'managed', version: '1.0.0' },
|
||||||
mode: 'dry-run',
|
mode: 'dry-run',
|
||||||
steps: [{ installerType: 'download', command: 'download https://example.com/tool.tgz -> <default destination>' }],
|
steps: [{ installerType: 'download', command: 'curl -fsSL https://example.com/tool.tgz' }],
|
||||||
skipped: [],
|
skipped: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(output).toContain("Install preflight for 'preflight-skill' from /tmp/source-skill");
|
expect(output).toContain("Install preflight for 'preflight-skill' from /tmp/source-skill");
|
||||||
expect(output).toContain('Planned installer steps:');
|
expect(output).toContain('Planned installer steps:');
|
||||||
expect(output).toContain('[download] download https://example.com/tool.tgz -> <default destination>');
|
expect(output).toContain('[download] curl -fsSL https://example.com/tool.tgz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('builds installer execution stub view from skill plan', () => {
|
it('builds installer execution stub view from skill plan', () => {
|
||||||
@@ -535,16 +535,16 @@ describe('skills CLI helpers', () => {
|
|||||||
expect(view.attempted.length).toBe(1);
|
expect(view.attempted.length).toBe(1);
|
||||||
expect(view.attempted[0]).toEqual({
|
expect(view.attempted[0]).toEqual({
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: expect.stringContaining('download https://example.com/tool.tgz'),
|
command: expect.stringContaining('curl -fsSL https://example.com/tool.tgz'),
|
||||||
});
|
});
|
||||||
expect(view.results[0]).toEqual({
|
expect(view.results[0]).toEqual({
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: expect.stringContaining('download https://example.com/tool.tgz'),
|
command: expect.stringContaining('curl -fsSL https://example.com/tool.tgz'),
|
||||||
status: 'skipped',
|
status: 'skipped',
|
||||||
reason: 'execution_disabled',
|
reason: 'execution_disabled',
|
||||||
});
|
});
|
||||||
expect(view.wouldRun.length).toBe(1);
|
expect(view.wouldRun.length).toBe(1);
|
||||||
expect(view.wouldRun[0]).toContain('download https://example.com/tool.tgz');
|
expect(view.wouldRun[0]).toContain('curl -fsSL https://example.com/tool.tgz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders installer execution stub output text', () => {
|
it('renders installer execution stub output text', () => {
|
||||||
@@ -613,7 +613,7 @@ describe('skills CLI helpers', () => {
|
|||||||
sourcePath: '/tmp/source-skill',
|
sourcePath: '/tmp/source-skill',
|
||||||
skill: { name: 'exec-stub', tier: 'managed' as const, version: '1.0.0' },
|
skill: { name: 'exec-stub', tier: 'managed' as const, version: '1.0.0' },
|
||||||
mode: 'dry-run' as const,
|
mode: 'dry-run' as const,
|
||||||
steps: [{ installerType: 'download', command: 'download https://example.com/a.tgz -> /tmp/a.tgz' }],
|
steps: [{ installerType: 'download', command: 'curl -fsSL -o /tmp/a.tgz https://example.com/a.tgz' }],
|
||||||
skipped: [],
|
skipped: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -628,18 +628,18 @@ describe('skills CLI helpers', () => {
|
|||||||
expect(view.attempted).toEqual([
|
expect(view.attempted).toEqual([
|
||||||
{
|
{
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: 'download https://example.com/a.tgz -> /tmp/a.tgz',
|
command: 'curl -fsSL -o /tmp/a.tgz https://example.com/a.tgz',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
expect(view.results).toEqual([
|
expect(view.results).toEqual([
|
||||||
{
|
{
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: 'download https://example.com/a.tgz -> /tmp/a.tgz',
|
command: 'curl -fsSL -o /tmp/a.tgz https://example.com/a.tgz',
|
||||||
status: 'skipped',
|
status: 'skipped',
|
||||||
reason: 'execution_disabled',
|
reason: 'execution_disabled',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
expect(view.wouldRun).toEqual(['download https://example.com/a.tgz -> /tmp/a.tgz']);
|
expect(view.wouldRun).toEqual(['curl -fsSL -o /tmp/a.tgz https://example.com/a.tgz']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('builds blocked step envelopes when confirmation is required', () => {
|
it('builds blocked step envelopes when confirmation is required', () => {
|
||||||
@@ -720,7 +720,7 @@ describe('skills CLI helpers', () => {
|
|||||||
results: [
|
results: [
|
||||||
{
|
{
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: 'download https://example.com/pkg.tgz -> <default destination>',
|
command: 'curl -fsSL https://example.com/pkg.tgz',
|
||||||
status: 'failed',
|
status: 'failed',
|
||||||
reason: 'allowlist_blocked',
|
reason: 'allowlist_blocked',
|
||||||
},
|
},
|
||||||
@@ -738,14 +738,14 @@ describe('skills CLI helpers', () => {
|
|||||||
);
|
);
|
||||||
expect(logger.skillsInstallerCommandResult).toHaveBeenCalledWith(
|
expect(logger.skillsInstallerCommandResult).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
command: hashSkillInstallerAuditCommand('download https://example.com/pkg.tgz -> <default destination>'),
|
command: hashSkillInstallerAuditCommand('curl -fsSL https://example.com/pkg.tgz'),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
expect(logger.skillsInstallerExecutionBlocked).not.toHaveBeenCalled();
|
expect(logger.skillsInstallerExecutionBlocked).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('hashes audit command values deterministically for non-sensitive commands', () => {
|
it('hashes audit command values deterministically for non-sensitive commands', () => {
|
||||||
const command = 'download https://example.com/tool.tgz -> <default destination>';
|
const command = 'curl -fsSL https://example.com/tool.tgz';
|
||||||
expect(hashSkillInstallerAuditCommand(command)).toBe(hashSkillInstallerAuditCommand(command));
|
expect(hashSkillInstallerAuditCommand(command)).toBe(hashSkillInstallerAuditCommand(command));
|
||||||
expect(hashSkillInstallerAuditCommand(command)).toMatch(/^sha256:[a-f0-9]{64}$/);
|
expect(hashSkillInstallerAuditCommand(command)).toMatch(/^sha256:[a-f0-9]{64}$/);
|
||||||
});
|
});
|
||||||
@@ -819,7 +819,7 @@ describe('skills CLI helpers', () => {
|
|||||||
skill_name: 'audit-skill',
|
skill_name: 'audit-skill',
|
||||||
phase: 'install',
|
phase: 'install',
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: 'download https://example.com/pkg.tgz',
|
command: 'curl -fsSL https://example.com/pkg.tgz',
|
||||||
status: 'failed',
|
status: 'failed',
|
||||||
reason: 'allowlist_blocked',
|
reason: 'allowlist_blocked',
|
||||||
},
|
},
|
||||||
@@ -1550,13 +1550,13 @@ describe('skills CLI helpers', () => {
|
|||||||
expect(payload.execution.attempted).toEqual([
|
expect(payload.execution.attempted).toEqual([
|
||||||
{
|
{
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: 'download https://example.com/plan.tgz -> <default destination>',
|
command: 'curl -fsSL https://example.com/plan.tgz',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
expect(payload.execution.results).toEqual([
|
expect(payload.execution.results).toEqual([
|
||||||
{
|
{
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: 'download https://example.com/plan.tgz -> <default destination>',
|
command: 'curl -fsSL https://example.com/plan.tgz',
|
||||||
status: 'skipped',
|
status: 'skipped',
|
||||||
reason: 'execution_disabled',
|
reason: 'execution_disabled',
|
||||||
},
|
},
|
||||||
@@ -1599,13 +1599,13 @@ describe('skills CLI helpers', () => {
|
|||||||
expect(payload.execution.attempted).toEqual([
|
expect(payload.execution.attempted).toEqual([
|
||||||
{
|
{
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: 'download https://example.com/install.tgz -> <default destination>',
|
command: 'curl -fsSL https://example.com/install.tgz',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
expect(payload.execution.results).toEqual([
|
expect(payload.execution.results).toEqual([
|
||||||
{
|
{
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: 'download https://example.com/install.tgz -> <default destination>',
|
command: 'curl -fsSL https://example.com/install.tgz',
|
||||||
status: 'blocked',
|
status: 'blocked',
|
||||||
reason: 'confirmation_required',
|
reason: 'confirmation_required',
|
||||||
},
|
},
|
||||||
@@ -1645,7 +1645,7 @@ describe('skills CLI helpers', () => {
|
|||||||
expect(payload.execution.results).toEqual([
|
expect(payload.execution.results).toEqual([
|
||||||
{
|
{
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: 'download https://example.com/install-confirmed.tgz -> <default destination>',
|
command: 'curl -fsSL https://example.com/install-confirmed.tgz',
|
||||||
status: 'skipped',
|
status: 'skipped',
|
||||||
reason: 'execution_disabled',
|
reason: 'execution_disabled',
|
||||||
},
|
},
|
||||||
@@ -1694,11 +1694,11 @@ describe('skills CLI helpers', () => {
|
|||||||
const payload = JSON.parse(String(logSpy.mock.calls[logSpy.mock.calls.length - 1]?.[0]));
|
const payload = JSON.parse(String(logSpy.mock.calls[logSpy.mock.calls.length - 1]?.[0]));
|
||||||
expect(payload.execution.execution_enabled).toBe(true);
|
expect(payload.execution.execution_enabled).toBe(true);
|
||||||
expect(payload.execution.reason).toBe('execution_enabled');
|
expect(payload.execution.reason).toBe('execution_enabled');
|
||||||
expect(payload.execution.executed).toEqual(['download https://example.com/install-exec.tgz -> <default destination>']);
|
expect(payload.execution.executed).toEqual(['curl -fsSL https://example.com/install-exec.tgz']);
|
||||||
expect(payload.execution.results).toEqual([
|
expect(payload.execution.results).toEqual([
|
||||||
{
|
{
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: 'download https://example.com/install-exec.tgz -> <default destination>',
|
command: 'curl -fsSL https://example.com/install-exec.tgz',
|
||||||
status: 'succeeded',
|
status: 'succeeded',
|
||||||
reason: 'runner_reported_success',
|
reason: 'runner_reported_success',
|
||||||
},
|
},
|
||||||
@@ -1739,7 +1739,7 @@ describe('skills CLI helpers', () => {
|
|||||||
expect(runner.run).toHaveBeenCalledTimes(1);
|
expect(runner.run).toHaveBeenCalledTimes(1);
|
||||||
const payload = JSON.parse(String(logSpy.mock.calls[0]?.[0]));
|
const payload = JSON.parse(String(logSpy.mock.calls[0]?.[0]));
|
||||||
expect(payload.execution_enabled).toBe(true);
|
expect(payload.execution_enabled).toBe(true);
|
||||||
expect(payload.executed).toEqual(['download https://example.com/execute.tgz -> <default destination>']);
|
expect(payload.executed).toEqual(['curl -fsSL https://example.com/execute.tgz']);
|
||||||
|
|
||||||
logSpy.mockRestore();
|
logSpy.mockRestore();
|
||||||
});
|
});
|
||||||
@@ -2286,7 +2286,7 @@ describe('skills CLI helpers', () => {
|
|||||||
expect(payload.execution.results).toEqual([
|
expect(payload.execution.results).toEqual([
|
||||||
{
|
{
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: 'download https://example.com/cli-install-allowlist-blocked.tgz -> <default destination>',
|
command: 'curl -fsSL https://example.com/cli-install-allowlist-blocked.tgz',
|
||||||
status: 'failed',
|
status: 'failed',
|
||||||
reason: 'allowlist_blocked',
|
reason: 'allowlist_blocked',
|
||||||
},
|
},
|
||||||
@@ -2379,7 +2379,7 @@ describe('skills CLI helpers', () => {
|
|||||||
expect(payload.execution.results).toEqual([
|
expect(payload.execution.results).toEqual([
|
||||||
{
|
{
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: 'download https://example.com/cli-install-no-exec.tgz -> <default destination>',
|
command: 'curl -fsSL https://example.com/cli-install-no-exec.tgz',
|
||||||
status: 'skipped',
|
status: 'skipped',
|
||||||
reason: 'execution_disabled',
|
reason: 'execution_disabled',
|
||||||
},
|
},
|
||||||
@@ -2428,7 +2428,7 @@ describe('skills CLI helpers', () => {
|
|||||||
expect(payload.execution.results).toEqual([
|
expect(payload.execution.results).toEqual([
|
||||||
{
|
{
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: 'download https://example.com/cli-install-no-confirm.tgz -> <default destination>',
|
command: 'curl -fsSL https://example.com/cli-install-no-confirm.tgz',
|
||||||
status: 'blocked',
|
status: 'blocked',
|
||||||
reason: 'confirmation_required',
|
reason: 'confirmation_required',
|
||||||
},
|
},
|
||||||
@@ -2563,7 +2563,7 @@ describe('skills CLI helpers', () => {
|
|||||||
expect(payload.results).toEqual([
|
expect(payload.results).toEqual([
|
||||||
{
|
{
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: 'download https://example.com/cli-exec-policy-disabled.tgz -> <default destination>',
|
command: 'curl -fsSL https://example.com/cli-exec-policy-disabled.tgz',
|
||||||
status: 'skipped',
|
status: 'skipped',
|
||||||
reason: 'execution_policy_disabled',
|
reason: 'execution_policy_disabled',
|
||||||
},
|
},
|
||||||
@@ -2613,7 +2613,7 @@ describe('skills CLI helpers', () => {
|
|||||||
expect(payload.results).toEqual([
|
expect(payload.results).toEqual([
|
||||||
{
|
{
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: 'download https://example.com/cli-exec-no-exec.tgz -> <default destination>',
|
command: 'curl -fsSL https://example.com/cli-exec-no-exec.tgz',
|
||||||
status: 'skipped',
|
status: 'skipped',
|
||||||
reason: 'execution_disabled',
|
reason: 'execution_disabled',
|
||||||
},
|
},
|
||||||
@@ -2763,7 +2763,7 @@ describe('skills CLI helpers', () => {
|
|||||||
expect(payload.results).toEqual([
|
expect(payload.results).toEqual([
|
||||||
{
|
{
|
||||||
installer_type: 'download',
|
installer_type: 'download',
|
||||||
command: 'download https://example.com/cli-exec-allowlist-blocked.tgz -> <default destination>',
|
command: 'curl -fsSL https://example.com/cli-exec-allowlist-blocked.tgz',
|
||||||
status: 'failed',
|
status: 'failed',
|
||||||
reason: 'allowlist_blocked',
|
reason: 'allowlist_blocked',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -60,15 +60,15 @@ describe('buildInstallerPlan', () => {
|
|||||||
expect(plan.skipped).toEqual([]);
|
expect(plan.skipped).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('skips download installer when destination is missing', () => {
|
it('plans download installer without destination as stdout curl', () => {
|
||||||
const plan = buildInstallerPlan(
|
const plan = buildInstallerPlan(
|
||||||
[{ type: 'download', url: 'https://example.com/tool.tgz' }],
|
[{ type: 'download', url: 'https://example.com/tool.tgz' }],
|
||||||
{ hasBinary: () => true },
|
{ hasBinary: () => true },
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(plan.steps).toEqual([]);
|
expect(plan.steps).toEqual([
|
||||||
expect(plan.skipped).toEqual([
|
{ installerType: 'download', command: 'curl -fsSL https://example.com/tool.tgz' },
|
||||||
{ installerType: 'download', reason: 'download destination is required for executable install plans' },
|
|
||||||
]);
|
]);
|
||||||
|
expect(plan.skipped).toEqual([]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -73,13 +73,11 @@ export function buildInstallerPlan(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!installer.destination) {
|
|
||||||
skipped.push({ installerType: 'download', reason: 'download destination is required for executable install plans' });
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
steps.push({
|
steps.push({
|
||||||
installerType: 'download',
|
installerType: 'download',
|
||||||
command: `curl -fsSL -o ${installer.destination} ${installer.url}`,
|
command: installer.destination
|
||||||
|
? `curl -fsSL -o ${installer.destination} ${installer.url}`
|
||||||
|
: `curl -fsSL ${installer.url}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user