Scope netx__* tools to the Netx Ops preset only.

Register tools from the preset standing mount so standard and other agents no longer see them without Ops skills.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-05 00:11:15 +08:00
parent ed219842d3
commit 0f30d92302
11 changed files with 180 additions and 697 deletions

54
src/netx/runtime.ts Normal file
View file

@ -0,0 +1,54 @@
/**
* Process-local Netx Ops connection snapshot shared between the host settings
* bridge and the Ops-preset-scoped tool plugin.
*
* Uses `Symbol.for` on `globalThis` so host + agent-tools bundles share one store
* even when Bun emits them as separate ESM files.
*/
import type { NetxToolConnection } from './tools.ts'
type Listener = () => void
interface Store {
connection: NetxToolConnection | undefined
listeners: Set<Listener>
}
const STORE_KEY = Symbol.for('dsh-netxops.connection-store')
function store(): Store {
const root = globalThis as typeof globalThis & { [STORE_KEY]?: Store }
let current = root[STORE_KEY]
if (current === undefined) {
current = { connection: undefined, listeners: new Set() }
root[STORE_KEY] = current
}
return current
}
/**
* Publish the latest API URL / token / lang / timeout for Ops tool mounts.
* @param next - connection used by the next `registerNetxTools` call.
*/
export function publishNetxConnection(next: NetxToolConnection): void {
const state = store()
state.connection = next
for (const listener of state.listeners) listener()
}
/** @returns the last published connection, if any. */
export function getNetxConnection(): NetxToolConnection | undefined {
return store().connection
}
/**
* Subscribe to connection publishes (settings / credential remounts).
* @param listener - called synchronously after each publish.
* @returns disposer.
*/
export function watchNetxConnection(listener: Listener): () => void {
const state = store()
state.listeners.add(listener)
return () => { state.listeners.delete(listener) }
}