feat(loader): interpolate the entry disabled field
The Windows platform layer disables tool-bash and inserts the pwsh stack, but the shipped presets each mount a tool-bash row that re-enabled the tool on win32 — the session had both a PowerShell-backed bash tool and tool-pwsh, silently, because no spec pinned the composed preset layer. The Loader now evaluates a disabled: !!js expression against the loader context at every mount decision; disabled is the only interpolated metadata field, and the raw node stays in the options so write-back keeps the !!js form. The standard/code/cordis presets gate tool-bash with process.platform === 'win32', verify-cordis-config allows expressions in disabled only, and the windows-shell spec pins the preset-level invariant.
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { mkdtempSync, writeFileSync, rmSync, mkdirSync, readFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import yaml from 'js-yaml'
|
||||
import { entryListSchema } from '@deepseek-ai/cordis-plugin-include'
|
||||
import { evaluate } from '@deepseek-ai/cordis-plugin-loader'
|
||||
import type { ProfileLayer } from '@deepseek-ai/dsh-app-boot'
|
||||
import { composeEntries, initProfile, loadProfile, PROFILES_DIR } from '@deepseek-ai/dsh-app-boot'
|
||||
import {
|
||||
@@ -137,3 +140,38 @@ describe('the shipped Windows composition (real bundle layers)', () => {
|
||||
expect(baseWarnings).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('shipped agent presets keep tool-bash off the win32 roster', () => {
|
||||
const presetRoot = resolve(fileURLToPath(new URL('../package.json', import.meta.url)), '..', 'config', 'agent-presets')
|
||||
|
||||
it.each(['standard', 'code', 'cordis'])('preset %s gates its tool-bash row by platform', (preset) => {
|
||||
const entries: unknown = yaml.load(
|
||||
readFileSync(join(presetRoot, preset, 'agent.cordis.yml'), 'utf8'),
|
||||
{ schema: entryListSchema },
|
||||
)
|
||||
if (!Array.isArray(entries)) throw new TypeError(`preset ${preset} must parse to an entry array`)
|
||||
const row = entries.find((entry): entry is Record<string, unknown> => (
|
||||
typeof entry === 'object' && entry !== null && (entry as Record<string, unknown>).id === 'tool-bash'
|
||||
))
|
||||
if (row === undefined) throw new TypeError(`preset ${preset} must mount tool-bash`)
|
||||
expect(row.disabled).toMatchObject({ __jsExpr: expect.any(String) as string })
|
||||
// The platform patch disables the host's tool-bash row on win32; the
|
||||
// preset row must not re-enable it there. Evaluate the shipped expression
|
||||
// with a platform-scoped context (the `with` scope shadows the global
|
||||
// `process`) so both outcomes pin on every host.
|
||||
const expression = (row.disabled as { __jsExpr: string }).__jsExpr
|
||||
expect(Boolean(evaluate({ process: { platform: 'win32' } }, expression))).toBe(true)
|
||||
expect(Boolean(evaluate({ process: { platform: 'linux' } }, expression))).toBe(false)
|
||||
})
|
||||
|
||||
it('minimal mounts no tool-bash row at all (its shell is the PTY stack)', () => {
|
||||
const entries: unknown = yaml.load(
|
||||
readFileSync(join(presetRoot, 'minimal', 'agent.cordis.yml'), 'utf8'),
|
||||
{ schema: entryListSchema },
|
||||
)
|
||||
if (!Array.isArray(entries)) throw new TypeError('minimal preset must parse to an entry array')
|
||||
expect(entries.some(entry => (
|
||||
typeof entry === 'object' && entry !== null && (entry as Record<string, unknown>).id === 'tool-bash'
|
||||
))).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user