mirror of
https://github.com/hansjone/netxops.git
synced 2026-10-20 13:30:46 +08:00
Fix preset publication, volatile settings and client form binding
This commit is contained in:
parent
fe0fe70d33
commit
0b9a3a82bc
66 changed files with 8546 additions and 3690 deletions
103
test/preset-install-safety.test.mjs
Normal file
103
test/preset-install-safety.test.mjs
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { composeNetxopsPresetArtifacts, hostResolveBases, installDirectoryPreset, resolveActiveProfile, resolveStandardPresetDir, syncDeclarativeIntoProfilePatch } from '../src/agent-preset-install.ts'
|
||||
|
||||
const log = { info() {}, warn() {}, error() {} }
|
||||
const patch = '- insert:\n - id: preset-netxops\n name: preset-plugin\n'
|
||||
function home(t) {
|
||||
const previous = process.env.DSH_HOME
|
||||
const root = mkdtempSync(join(tmpdir(), 'netxops-install-'))
|
||||
process.env.DSH_HOME = root
|
||||
t.after(() => { if (previous === undefined) delete process.env.DSH_HOME; else process.env.DSH_HOME = previous })
|
||||
return root
|
||||
}
|
||||
const artifacts = { standardSource: 'fixture', standardKind: 'desktop-patch', standardDir: null,
|
||||
agentCordis: '- id: netxops-tools\n name: dsh-netxops/tools\n', name: 'Netx Ops', description: '', order: '50', declarativePatch: patch }
|
||||
|
||||
test('updating Desktop patch leaves Web patch intact', t => {
|
||||
const root = home(t)
|
||||
for (const profile of ['desktop', 'web']) {
|
||||
mkdirSync(join(root, 'profiles', profile), { recursive: true })
|
||||
writeFileSync(join(root, 'profiles', profile, 'cordis.patch.yml'), '# user configuration\n[]\n')
|
||||
}
|
||||
const web = join(root, 'profiles', 'web', 'cordis.patch.yml'), original = readFileSync(web, 'utf8')
|
||||
syncDeclarativeIntoProfilePatch('desktop', patch, log)
|
||||
assert.equal(readFileSync(web, 'utf8'), original)
|
||||
assert.match(readFileSync(join(root, 'profiles', 'desktop', 'cordis.patch.yml'), 'utf8'), /preset-netxops/)
|
||||
})
|
||||
|
||||
test('unterminated managed block does not truncate user configuration', t => {
|
||||
const root = home(t), folder = join(root, 'profiles', 'web')
|
||||
mkdirSync(folder, { recursive: true })
|
||||
const path = join(folder, 'cordis.patch.yml')
|
||||
const original = '# BEGIN dsh-netxops-preset (managed)\n- id: preserve-me\n'
|
||||
writeFileSync(path, original)
|
||||
assert.throws(() => syncDeclarativeIntoProfilePatch('web', patch, log), /unterminated/)
|
||||
assert.equal(readFileSync(path, 'utf8'), original)
|
||||
})
|
||||
|
||||
test('unmanaged preset declaration remains authoritative', t => {
|
||||
const root = home(t), folder = join(root, 'profiles', 'web')
|
||||
mkdirSync(folder, { recursive: true })
|
||||
const path = join(folder, 'cordis.patch.yml'), original = '- insert:\n - id: preset-netxops\n name: custom-preset\n'
|
||||
writeFileSync(path, original)
|
||||
syncDeclarativeIntoProfilePatch('web', patch, log)
|
||||
assert.equal(readFileSync(path, 'utf8'), original)
|
||||
})
|
||||
|
||||
test('managed directory update preserves edits and extra files', t => {
|
||||
const root = home(t), folder = join(root, '.agent-presets', 'netxops')
|
||||
installDirectoryPreset(artifacts, log)
|
||||
writeFileSync(join(folder, 'notes.md'), 'user notes')
|
||||
writeFileSync(join(folder, 'agent.cordis.yml'), '- id: user-edited\n')
|
||||
installDirectoryPreset({ ...artifacts, agentCordis: '- id: updated\n' }, log)
|
||||
assert.equal(readFileSync(join(folder, 'notes.md'), 'utf8'), 'user notes')
|
||||
assert.equal(readFileSync(join(folder, 'agent.cordis.yml'), 'utf8'), '- id: user-edited\n')
|
||||
assert.equal(existsSync(join(folder, 'skills')), false, 'runtime-controlled skills are not copied into auto-scanned roots')
|
||||
})
|
||||
|
||||
test('profile identity follows the running profile path', () => {
|
||||
const previous = process.env.DSH_PROFILE
|
||||
delete process.env.DSH_PROFILE
|
||||
try {
|
||||
assert.equal(resolveActiveProfile('file:///C:/dsh/profiles/desktop/cordis.yml'), 'desktop')
|
||||
assert.equal(resolveActiveProfile('C:\\dsh\\profiles\\web\\cordis.yml'), 'web')
|
||||
} finally { if (previous !== undefined) process.env.DSH_PROFILE = previous }
|
||||
})
|
||||
|
||||
test('fresh install overlays the seeded standard composition', t => {
|
||||
const root = home(t), standard = join(root, 'standard')
|
||||
mkdirSync(standard)
|
||||
writeFileSync(join(standard, 'agent.cordis.yml'), '- id: standard-only\n')
|
||||
installDirectoryPreset({ ...artifacts, standardDir: standard }, log)
|
||||
assert.equal(readFileSync(join(root, '.agent-presets', 'netxops', 'agent.cordis.yml'), 'utf8'), artifacts.agentCordis)
|
||||
})
|
||||
|
||||
test('each Host resolves its own standard before shared dependencies', t => {
|
||||
const root = home(t)
|
||||
for (const profile of ['desktop', 'web']) {
|
||||
const folder = join(root, 'profiles', profile, 'node_modules', '@deepseek-ai', 'dsh-agent-presets', 'presets', 'standard')
|
||||
mkdirSync(folder, { recursive: true })
|
||||
writeFileSync(join(folder, 'agent.cordis.yml'), `- id: ${profile}\n`)
|
||||
writeFileSync(join(root, 'profiles', profile, 'package.json'), '{}')
|
||||
assert.equal(resolveStandardPresetDir(profile), folder)
|
||||
const other = profile === 'desktop' ? 'web' : 'desktop'
|
||||
assert.equal(hostResolveBases(profile).some(path => path.startsWith(join(root, 'profiles', other))), false)
|
||||
}
|
||||
})
|
||||
|
||||
test('runtime composition preserves the Host standard dialect and workflow engine', t => {
|
||||
home(t)
|
||||
const original = "- id: tool-fs\n name: '@host-only/fs'\n disabled: !!js process.platform === 'win32'\n- id: workflow-worker-thread\n name: '@deepseek-ai/dsh-workflow-worker-thread'\n"
|
||||
const composed = composeNetxopsPresetArtifacts('web', original)
|
||||
assert.equal(composed.standardKind, 'host-registry')
|
||||
assert.match(composed.agentCordis, /@host-only\/fs/)
|
||||
assert.match(composed.agentCordis, /disabled: !!js process.platform === 'win32'/)
|
||||
assert.match(composed.agentCordis, /workflow-worker-thread/)
|
||||
assert.doesNotMatch(composed.agentCordis, /workflow-ptc/)
|
||||
assert.match(composed.agentCordis, /name: dsh-netxops\/tools/)
|
||||
assert.match(composed.agentCordis, /- id: persona/)
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue