mirror of
https://github.com/hansjone/netxops.git
synced 2026-10-20 21:53:28 +08:00
Netx tools: load from netx remote /mcp, add per-tool toggles
- Drop the TypeScript netx tool implementations and the hard-coded TOOLS_BY_GROUP list; register netx__* tools from the server's tools/list (group from _meta.netx.group) and forward tools/call. - Thin Streamable HTTP client on standard fetch (fetchImpl injectable); cached catalog when netx is unreachable, refresh to reconnect. - disabledTools (deny list; new server tools follow their group) and readOnlyOnly (only readOnlyHint=true tools), re-registered live. - Settings card: per-group expandable tool rows with switches and a write marker; skills append a disabled-tools note. - Toggles only control model visibility; netx enforces security. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
2def53e7c7
commit
2ef3a43c63
38 changed files with 5049 additions and 17029 deletions
|
|
@ -11,6 +11,7 @@ import { registerGroupSkills } from '../src/netx/group-skills.ts'
|
|||
import { publishNetxConnection } from '../src/netx/runtime.ts'
|
||||
import { getToolMountStatuses, publishToolMountStatus, resetToolMountStatus } from '../src/netx/tool-mount-status.ts'
|
||||
import { asToolsMountView } from '../src/client/tools-status-view.ts'
|
||||
import { OPS_TOOL_NAMES, clearMcpCatalogSeed, seedMcpCatalog } from './mcp-test-catalog.mjs'
|
||||
|
||||
const log = { info() {}, warn() {}, error() {} }
|
||||
const pause = () => new Promise(resolve => setTimeout(resolve, 40))
|
||||
|
|
@ -37,7 +38,12 @@ function fakeContext(failAt = -1) {
|
|||
function diagnosticHome(t) {
|
||||
const previous = process.env.DSH_HOME
|
||||
process.env.DSH_HOME = mkdtempSync(join(tmpdir(), 'netxops-publication-'))
|
||||
t.after(() => { if (previous === undefined) delete process.env.DSH_HOME; else process.env.DSH_HOME = previous })
|
||||
seedMcpCatalog()
|
||||
t.after(() => {
|
||||
clearMcpCatalogSeed()
|
||||
if (previous === undefined) delete process.env.DSH_HOME
|
||||
else process.env.DSH_HOME = previous
|
||||
})
|
||||
}
|
||||
|
||||
test('selected presets are isolated and global publication is inherited once', () => {
|
||||
|
|
@ -67,7 +73,7 @@ test('failed tool registration rolls back every earlier tool', t => {
|
|||
const { ctx, tools } = fakeContext(3)
|
||||
assert.throws(() => registerNetxTools(ctx, connection(), { plane: 'preset' }), /registration refused/)
|
||||
assert.equal(tools.size, 0)
|
||||
assert.ok(getToolMountStatuses().some(row => row.error === 'registration refused'))
|
||||
assert.ok(getToolMountStatuses().some(row => String(row.error ?? '').includes('registration refused')))
|
||||
resetToolMountStatus()
|
||||
})
|
||||
|
||||
|
|
@ -87,7 +93,7 @@ test('turning off a mounted group revokes tools, KB context and pending skill lo
|
|||
publishNetxConnection(connection({ groupBizMonitorInPreset: false }))
|
||||
const mount = fakeContext()
|
||||
applyGroupToolsPlugin(mount.ctx, { name: 'lifecycle', mode: 'settings' })
|
||||
assert.equal(mount.tools.size, 15)
|
||||
assert.equal(mount.tools.size, OPS_TOOL_NAMES.length)
|
||||
assert.equal(mount.skills.has('kb-context'), true)
|
||||
publishNetxConnection(connection({ toolsEnabled: false }))
|
||||
await pause()
|
||||
|
|
@ -95,7 +101,7 @@ test('turning off a mounted group revokes tools, KB context and pending skill lo
|
|||
assert.equal(mount.skills.size, 0)
|
||||
publishNetxConnection(connection({ groupBizMonitorInPreset: false }))
|
||||
await pause()
|
||||
assert.equal(mount.tools.size, 15)
|
||||
assert.equal(mount.tools.size, OPS_TOOL_NAMES.length)
|
||||
assert.equal(mount.skills.has('netx-ops'), true)
|
||||
mount.dispose()
|
||||
await pause()
|
||||
|
|
@ -123,9 +129,9 @@ test('mount inventory survives cross-bundle publishes and reports all scopes', a
|
|||
entry.apply(mount.ctx)
|
||||
const statuses = getToolMountStatuses()
|
||||
assert.ok(statuses.some(row => row.id === 'host-test'))
|
||||
assert.ok(statuses.some(row => row.count === 15))
|
||||
assert.ok(statuses.some(row => row.count === OPS_TOOL_NAMES.length))
|
||||
const view = asToolsMountView({ mounts: statuses, connection: { toolsEnabled: true } })
|
||||
assert.equal(view.count, 15)
|
||||
assert.equal(view.count, OPS_TOOL_NAMES.length)
|
||||
mount.dispose()
|
||||
assert.equal(getToolMountStatuses().some(row => row.count > 0), false)
|
||||
resetToolMountStatus()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue