@lvy-jsd/dsh-tool-manager 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -2
- package/cordis.patch.yml +13 -0
- package/lib/exempt.d.ts +45 -0
- package/lib/exempt.d.ts.map +1 -0
- package/lib/exempt.js +122 -0
- package/lib/exempt.js.map +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +243 -34
- package/lib/index.js.map +1 -1
- package/lib/manifest.d.ts +21 -0
- package/lib/manifest.d.ts.map +1 -0
- package/lib/manifest.js +112 -0
- package/lib/manifest.js.map +1 -0
- package/lib/metadata.d.ts +38 -0
- package/lib/metadata.d.ts.map +1 -0
- package/lib/metadata.js +102 -0
- package/lib/metadata.js.map +1 -0
- package/lib/router/bm25.d.ts +21 -0
- package/lib/router/bm25.d.ts.map +1 -0
- package/lib/router/bm25.js +63 -0
- package/lib/router/bm25.js.map +1 -0
- package/lib/router/index.d.ts +9 -0
- package/lib/router/index.d.ts.map +1 -0
- package/lib/router/index.js +422 -0
- package/lib/router/index.js.map +1 -0
- package/lib/router/sidecar.d.ts +34 -0
- package/lib/router/sidecar.d.ts.map +1 -0
- package/lib/router/sidecar.js +156 -0
- package/lib/router/sidecar.js.map +1 -0
- package/lib/router/types.d.ts +164 -0
- package/lib/router/types.d.ts.map +1 -0
- package/lib/router/types.js +7 -0
- package/lib/router/types.js.map +1 -0
- package/lib/settings.d.ts +32 -0
- package/lib/settings.d.ts.map +1 -0
- package/lib/settings.js +93 -0
- package/lib/settings.js.map +1 -0
- package/lib/types.d.ts +137 -1
- package/lib/types.d.ts.map +1 -1
- package/package.json +8 -4
- package/sidecar/bin/tool-router-sidecar +0 -0
- package/src/exempt.ts +148 -0
- package/src/index.ts +245 -35
- package/src/manifest.ts +116 -0
- package/src/metadata.ts +123 -0
- package/src/router/bm25.ts +68 -0
- package/src/router/index.ts +440 -0
- package/src/router/sidecar.ts +159 -0
- package/src/router/types.ts +149 -0
- package/src/settings.ts +108 -0
- package/src/types.ts +134 -3
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@ tools_ls({ query: "pull request" }) # search the whole tree in one step
|
|
|
14
14
|
tools_load(["/code/github"]) # callable from the next step, for the rest of the session
|
|
15
15
|
tools_load(["mcp__github__get_me"]) # or one tool by exact name
|
|
16
16
|
tools_unload(["/code/github"])
|
|
17
|
+
tools_find("send an email") # ranked matches by intent (BM25), no loading
|
|
18
|
+
tools_route("send an email") # retrieve, re-rank with the model, then load
|
|
17
19
|
```
|
|
18
20
|
|
|
19
21
|
Loaded tools are **native**: the model receives their full parameter schema
|
|
@@ -35,7 +37,15 @@ built-in rules and config overrides, or write your own classifier.
|
|
|
35
37
|
- Loading a group subscribes to it: tools the group gains later become visible
|
|
36
38
|
too.
|
|
37
39
|
- Restrictions from other plugins intersect with this one, so a hard deny (for
|
|
38
|
-
example the MCP connector's governance policy) always wins.
|
|
40
|
+
example the MCP connector's governance policy) always wins. A pinned or
|
|
41
|
+
loaded tool that another layer still hides is marked `[denied]` in
|
|
42
|
+
`tools_ls`, and `tools_load` reports it as not callable instead of listing it
|
|
43
|
+
(with one log warning per tool, pointing at connector governance rules and
|
|
44
|
+
per-session injection).
|
|
45
|
+
- DSH's PTC mode (`DSH_TOOLS_MODE=ptc`) works unchanged: the model gets only
|
|
46
|
+
`run_code`, and the SDK the host declares for it is built from the same
|
|
47
|
+
narrowed view, so loads show up there from the next step. `run_code` itself
|
|
48
|
+
is a host transport and never enters the directory.
|
|
39
49
|
- Every agent is restricted separately. A subagent starts from the pinned set
|
|
40
50
|
plus whatever the parent loaded with `forSubagents: true`.
|
|
41
51
|
- Loaded state is in memory. Install
|
|
@@ -44,6 +54,38 @@ built-in rules and config overrides, or write your own classifier.
|
|
|
44
54
|
- If the plugin is unloaded, all its restrictions are lifted (fail open to
|
|
45
55
|
"every tool visible", never to "no tools").
|
|
46
56
|
|
|
57
|
+
## Finding and routing by intent
|
|
58
|
+
|
|
59
|
+
The directory above is browsable, but the model still has to guess the right
|
|
60
|
+
keywords and spend turns searching. The core now ships a retrieval + routing
|
|
61
|
+
layer that makes an unbounded catalog usable without that guesswork:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
tools_find("send an email") # ranked matches (BM25), no loading
|
|
65
|
+
tools_route("send an email") # retrieve, re-rank with the model, then load
|
|
66
|
+
skills_find("write a PR") # ranked skills, no loading
|
|
67
|
+
skills_route("write a PR") # retrieve, re-rank, then load the top skill's body
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Retrieval is BM25 by default: no model, no network, works offline, and scales
|
|
71
|
+
to a large catalog. The BM25 index lives in a Rust sidecar process
|
|
72
|
+
(`sidecar/`, tantivy over a Unix socket) for headroom — the hot path is native
|
|
73
|
+
and the TypeScript here is a thin client. A dependency-free JS BM25 index is
|
|
74
|
+
kept as the always-available fallback, so retrieval never breaks if the sidecar
|
|
75
|
+
binary is missing or the process dies.
|
|
76
|
+
|
|
77
|
+
When a model is configured (`config.model { provider, model }`), `tools_route`
|
|
78
|
+
and `skills_route` re-rank the BM25 candidates with the model, so a
|
|
79
|
+
natural-language intent that shares no words with a tool name still finds it —
|
|
80
|
+
the RAG-MCP pattern: retrieve a small candidate set, then rank it. Any model
|
|
81
|
+
failure falls back to the BM25 order, so the router never breaks on a model
|
|
82
|
+
hiccup.
|
|
83
|
+
|
|
84
|
+
The router reuses the core's `toolCatalog` service: it indexes the same
|
|
85
|
+
catalog, loads through the same path, and reports the same token budget. The
|
|
86
|
+
sidecar is spawned by the plugin and exits with it (a parent-process monitor
|
|
87
|
+
kills it on a DSH restart).
|
|
88
|
+
|
|
47
89
|
## Configuration
|
|
48
90
|
|
|
49
91
|
```yaml
|
|
@@ -56,6 +98,60 @@ built-in rules and config overrides, or write your own classifier.
|
|
|
56
98
|
highRisk: [] # never included in group loads; load individually
|
|
57
99
|
```
|
|
58
100
|
|
|
101
|
+
### Override files
|
|
102
|
+
|
|
103
|
+
Three files under `$DSH_HOME/tool-manager/` override the config above. Each is
|
|
104
|
+
read at startup and re-read when the settings panel saves through the API (or
|
|
105
|
+
`reload*` is called); write them from the panel, or by hand and then reload.
|
|
106
|
+
|
|
107
|
+
| File | Config key it replaces | What it holds |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| `settings.json` | `pinned`, `unpin`, `highRisk` | the lists an operator edits most often |
|
|
110
|
+
| `metadata.json` | `annotate()` for the same tool | `{ "<tool>": { "group", "category", "risk", "summary" } }` |
|
|
111
|
+
| `exempt.json` | `exemptProviders` | `{ "providers": [...], "sessions": [...] }` |
|
|
112
|
+
|
|
113
|
+
### The tool manifest
|
|
114
|
+
|
|
115
|
+
A client that receives its tool list once per session — Claude Code, driven by
|
|
116
|
+
`dsh-oh-my-claude` — cannot discover the directory by asking, because nothing
|
|
117
|
+
tells it the directory exists. So the catalog is also written out as text, to
|
|
118
|
+
`~/.claude/dsh-tool-catalog.md`, after every rebuild:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
## device (14 tools, Devices)
|
|
122
|
+
|
|
123
|
+
### adb — load with `tools_load({ targets: ["/device/adb"] })`
|
|
124
|
+
- adb_install — Install an APK on a device.
|
|
125
|
+
- adb_shell ! — Run a shell command on a device.
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The file opens by saying the listed tools are **not callable yet**, then gives
|
|
129
|
+
the two calls that change that (`tools_ls`, `tools_load`) and the rule to load
|
|
130
|
+
the smallest thing that covers the task. A `!` marks a high-risk tool. Reading
|
|
131
|
+
it runs nothing.
|
|
132
|
+
|
|
133
|
+
Add one line to `~/.claude/CLAUDE.md` so every Claude session reads it:
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
@~/.claude/dsh-tool-catalog.md
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The write is skipped when the text did not change, so a rebuild that moves
|
|
140
|
+
nothing does not cost a prompt re-read. `manifestFile` points it elsewhere, and
|
|
141
|
+
`''` turns it off.
|
|
142
|
+
|
|
143
|
+
The files win as a whole, not key by key: once `exempt.json` exists, the config
|
|
144
|
+
list is ignored rather than merged, so what the file shows is what is in force.
|
|
145
|
+
A missing file means "no opinion, use the config"; a corrupt file is reported as
|
|
146
|
+
a warning and treated as empty rather than silently restoring the config.
|
|
147
|
+
`sessions` exempts one session by id for its remaining lifetime, for checking
|
|
148
|
+
whether a client handles late loading without de-exempting the whole route.
|
|
149
|
+
|
|
150
|
+
A tool's `metadata.json` entry replaces the previous entry as a whole:
|
|
151
|
+
submitting the form with a field blank drops that override, and the package
|
|
152
|
+
declaration (or a classifier) applies again. `parameters` are deliberately not
|
|
153
|
+
part of it — the host owns the schema sent to the model.
|
|
154
|
+
|
|
59
155
|
## Extending: the `toolCatalog` service
|
|
60
156
|
|
|
61
157
|
Declare `inject: ['toolCatalog']` (or use `ctx.inject(['toolCatalog'], …)` for
|
|
@@ -193,7 +289,8 @@ ctx.effect(() => ctx.toolCatalog.registerVisibilityFilter({
|
|
|
193
289
|
### Reading the catalog
|
|
194
290
|
|
|
195
291
|
`ctx.toolCatalog.get()` returns `{ tools, groups, categories }`;
|
|
196
|
-
`resolve(target)`, `stateOf(agent)`, `allowFor(agent)`, `
|
|
292
|
+
`resolve(target)`, `stateOf(agent)`, `allowFor(agent)`, `deniedFor(agent)`
|
|
293
|
+
(allowed here but hidden by another layer), `isExempt(agent)`,
|
|
197
294
|
`packages()` (enabled plugin packages: `{ name, dir, manifest }`) and
|
|
198
295
|
`refresh()` are also available.
|
|
199
296
|
|
package/cordis.patch.yml
CHANGED
|
@@ -14,3 +14,16 @@
|
|
|
14
14
|
# Loading high-risk tools also needs a risk gate plugin
|
|
15
15
|
# (@lvy-jsd/dsh-tool-manager-risk-gate) to confirm them.
|
|
16
16
|
highRisk: []
|
|
17
|
+
# The catalog as text, for a client that cannot lazy-load (Claude Code
|
|
18
|
+
# reads it through a ~/.claude/CLAUDE.md `@` line). '' turns it off.
|
|
19
|
+
# manifestFile: ~/.claude/dsh-tool-catalog.md
|
|
20
|
+
# --- retrieval + routing (tools_find / tools_route / skills_find / skills_route) ---
|
|
21
|
+
# Model used to re-rank tools_route / skills_route candidates. Leave
|
|
22
|
+
# unset for pure BM25 (no model, works offline).
|
|
23
|
+
# model: { provider: deepseek, model: deepseek-chat }
|
|
24
|
+
# maxTokens: 24
|
|
25
|
+
# topK: 8
|
|
26
|
+
# maxBudget: 20000
|
|
27
|
+
# k1: 1.5
|
|
28
|
+
# b: 0.75
|
|
29
|
+
# sidecar: { bin: '', socket: '', timeoutMs: 2000 }
|
package/lib/exempt.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Logger } from './types.js';
|
|
2
|
+
/** An exemption list: routes and sessions. Both may be empty. */
|
|
3
|
+
export interface ExemptConfig {
|
|
4
|
+
providers: string[];
|
|
5
|
+
sessions: string[];
|
|
6
|
+
}
|
|
7
|
+
/** Where the exemption file lives unless a deployment points elsewhere. */
|
|
8
|
+
export declare function defaultExemptFile(): string;
|
|
9
|
+
/** The whole file, with unknown keys dropped. */
|
|
10
|
+
export declare function pickExempt(raw: unknown): ExemptConfig;
|
|
11
|
+
/** Read the exemption file; missing or corrupt is "no opinion", never a crash. */
|
|
12
|
+
export declare function readExemptFile(file: string, logger?: Logger): ExemptConfig | undefined;
|
|
13
|
+
/** Write the file atomically, so a crash cannot leave a half-written list. */
|
|
14
|
+
export declare function writeExemptFile(file: string, config: ExemptConfig, logger?: Logger): void;
|
|
15
|
+
/** Why a session is exempt, for the panel to show. */
|
|
16
|
+
export interface ExemptReason {
|
|
17
|
+
exempt: boolean;
|
|
18
|
+
/** 'file:provider' | 'file:session' | 'config:provider' | '' */
|
|
19
|
+
source: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ExemptStore {
|
|
22
|
+
/** File the store reads and writes; the file itself is the override switch. */
|
|
23
|
+
file: string;
|
|
24
|
+
/** True when the file exists, so the config provider list is ignored. */
|
|
25
|
+
present(): boolean;
|
|
26
|
+
/** The lists in force: the file when it exists, else the plugin config. */
|
|
27
|
+
effective(): ExemptConfig;
|
|
28
|
+
/** Why one session is exempt, if it is. */
|
|
29
|
+
reasonFor(provider: string | undefined, sessionId: string | undefined): ExemptReason;
|
|
30
|
+
/** Add or remove one session, writing the file. Returns the lists in force. */
|
|
31
|
+
setSession(sessionId: string, exempt: boolean): ExemptConfig;
|
|
32
|
+
/** Re-read the file, for after an external edit. Returns the lists in force. */
|
|
33
|
+
reload(): ExemptConfig;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Exemption store over one file, with the plugin config as the fallback layer.
|
|
37
|
+
* The file wins as a whole rather than per key: an operator writing the file
|
|
38
|
+
* means it, and a partial merge would leave entries they cannot see.
|
|
39
|
+
*/
|
|
40
|
+
export declare function createExemptStore({ file, configProviders, logger }: {
|
|
41
|
+
file: string;
|
|
42
|
+
configProviders?: readonly string[];
|
|
43
|
+
logger?: Logger;
|
|
44
|
+
}): ExemptStore;
|
|
45
|
+
//# sourceMappingURL=exempt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exempt.d.ts","sourceRoot":"","sources":["../src/exempt.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAExC,iEAAiE;AACjE,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,IAAI,MAAM,CAG1C;AAQD,iDAAiD;AACjD,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,CAMrD;AAED,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAkBtF;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAUzF;AAED,sDAAsD;AACtD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,CAAA;IACf,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAA;IACZ,yEAAyE;IACzE,OAAO,IAAI,OAAO,CAAA;IAClB,2EAA2E;IAC3E,SAAS,IAAI,YAAY,CAAA;IACzB,2CAA2C;IAC3C,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,CAAA;IACpF,+EAA+E;IAC/E,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,YAAY,CAAA;IAC5D,gFAAgF;IAChF,MAAM,IAAI,YAAY,CAAA;CACvB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,eAAoB,EAAE,MAAM,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,CA+B7J"}
|
package/lib/exempt.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// File-based exemption list for dsh-tool-manager.
|
|
2
|
+
//
|
|
3
|
+
// An exempt session is never narrowed: a route whose tool list is handed to an
|
|
4
|
+
// external client once at session start (Claude Code, say) cannot load a tool
|
|
5
|
+
// that was hidden from the first request, so hiding anything there only loses
|
|
6
|
+
// tools. The provider list originally came from `exemptProviders` in
|
|
7
|
+
// cordis.patch.yml, which a user cannot edit from the browser and which is
|
|
8
|
+
// awkward to change per session.
|
|
9
|
+
//
|
|
10
|
+
// This file replaces it: when <DSH_HOME>/tool-manager/exempt.json exists it wins
|
|
11
|
+
// over the plugin config, the same way the settings file does. Two entry forms
|
|
12
|
+
// are supported, because they answer different questions:
|
|
13
|
+
//
|
|
14
|
+
// { "providers": ["claude-code"], "sessions": ["s_abc123"] }
|
|
15
|
+
//
|
|
16
|
+
// providers model routes never narrowed, for every session (durable)
|
|
17
|
+
// sessions one session exempted for its remaining lifetime, e.g. while the
|
|
18
|
+
// user checks whether a client handles lazy loading (temporary)
|
|
19
|
+
//
|
|
20
|
+
// Sessions are matched against the agent's session id and, as a fallback, the
|
|
21
|
+
// agent id, so an entry can be removed by hand without knowing which of the two
|
|
22
|
+
// the host populated.
|
|
23
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
24
|
+
import { homedir } from 'node:os';
|
|
25
|
+
import { dirname, join } from 'node:path';
|
|
26
|
+
/** Where the exemption file lives unless a deployment points elsewhere. */
|
|
27
|
+
export function defaultExemptFile() {
|
|
28
|
+
const dshHome = process.env.DSH_HOME || join(homedir(), '.config', 'deepseek-harness');
|
|
29
|
+
return join(dshHome, 'tool-manager', 'exempt.json');
|
|
30
|
+
}
|
|
31
|
+
/** Keep only non-empty strings; a number or object in the file is a typo, not a name. */
|
|
32
|
+
function pickNames(value) {
|
|
33
|
+
if (!Array.isArray(value))
|
|
34
|
+
return [];
|
|
35
|
+
return value.filter((name) => typeof name === 'string' && name.trim().length > 0).map((name) => name.trim());
|
|
36
|
+
}
|
|
37
|
+
/** The whole file, with unknown keys dropped. */
|
|
38
|
+
export function pickExempt(raw) {
|
|
39
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
|
|
40
|
+
return { providers: [], sessions: [] };
|
|
41
|
+
const record = raw;
|
|
42
|
+
// `providers` is required: a file that omits it says "no route is exempt",
|
|
43
|
+
// not "keep the old config list", because the file is the override.
|
|
44
|
+
return { providers: pickNames(record.providers), sessions: pickNames(record.sessions) };
|
|
45
|
+
}
|
|
46
|
+
/** Read the exemption file; missing or corrupt is "no opinion", never a crash. */
|
|
47
|
+
export function readExemptFile(file, logger) {
|
|
48
|
+
let text;
|
|
49
|
+
try {
|
|
50
|
+
text = readFileSync(file, 'utf8');
|
|
51
|
+
}
|
|
52
|
+
catch (caught) {
|
|
53
|
+
const error = caught;
|
|
54
|
+
// ENOENT is the normal first run: no file means the plugin config decides.
|
|
55
|
+
if (error.code !== 'ENOENT')
|
|
56
|
+
logger?.warn?.(`tool-manager: ignoring unreadable exemptions ${file}: ${error.message}`);
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
return pickExempt(JSON.parse(text));
|
|
61
|
+
}
|
|
62
|
+
catch (caught) {
|
|
63
|
+
// A corrupt file must not silently restore the config list: the user
|
|
64
|
+
// intended the file to be in charge, so report and treat it as "no exempt".
|
|
65
|
+
logger?.warn?.(`tool-manager: unreadable exemptions ${file}: ${caught.message}`);
|
|
66
|
+
return { providers: [], sessions: [] };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** Write the file atomically, so a crash cannot leave a half-written list. */
|
|
70
|
+
export function writeExemptFile(file, config, logger) {
|
|
71
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
72
|
+
const tmp = `${file}.tmp`;
|
|
73
|
+
try {
|
|
74
|
+
writeFileSync(tmp, `${JSON.stringify({ providers: config.providers, sessions: config.sessions }, null, 2)}\n`);
|
|
75
|
+
renameSync(tmp, file);
|
|
76
|
+
}
|
|
77
|
+
catch (caught) {
|
|
78
|
+
logger?.warn?.(`tool-manager: writing exemptions ${file} failed: ${caught.message}`);
|
|
79
|
+
throw caught;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Exemption store over one file, with the plugin config as the fallback layer.
|
|
84
|
+
* The file wins as a whole rather than per key: an operator writing the file
|
|
85
|
+
* means it, and a partial merge would leave entries they cannot see.
|
|
86
|
+
*/
|
|
87
|
+
export function createExemptStore({ file, configProviders = [], logger }) {
|
|
88
|
+
let fromFile = readExemptFile(file, logger);
|
|
89
|
+
const effective = () => fromFile ?? { providers: [...configProviders], sessions: [] };
|
|
90
|
+
return {
|
|
91
|
+
file,
|
|
92
|
+
present: () => fromFile !== undefined,
|
|
93
|
+
effective,
|
|
94
|
+
reasonFor(provider, sessionId) {
|
|
95
|
+
const current = effective();
|
|
96
|
+
if (provider !== undefined && current.providers.includes(provider)) {
|
|
97
|
+
return { exempt: true, source: fromFile !== undefined ? 'file:provider' : 'config:provider' };
|
|
98
|
+
}
|
|
99
|
+
if (sessionId !== undefined && current.sessions.includes(sessionId))
|
|
100
|
+
return { exempt: true, source: 'file:session' };
|
|
101
|
+
return { exempt: false, source: '' };
|
|
102
|
+
},
|
|
103
|
+
setSession(sessionId, exempt) {
|
|
104
|
+
// Editing starts from what is in force, so the first panel click writes a
|
|
105
|
+
// file that keeps the exemptions the user could already see.
|
|
106
|
+
const current = effective();
|
|
107
|
+
const sessions = new Set(current.sessions);
|
|
108
|
+
if (exempt)
|
|
109
|
+
sessions.add(sessionId);
|
|
110
|
+
else
|
|
111
|
+
sessions.delete(sessionId);
|
|
112
|
+
fromFile = { providers: [...current.providers], sessions: [...sessions] };
|
|
113
|
+
writeExemptFile(file, fromFile, logger);
|
|
114
|
+
return effective();
|
|
115
|
+
},
|
|
116
|
+
reload() {
|
|
117
|
+
fromFile = readExemptFile(file, logger);
|
|
118
|
+
return effective();
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=exempt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exempt.js","sourceRoot":"","sources":["../src/exempt.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export type { VisibilityController, VisibilityOptions } from './visibility.js';
|
|
|
7
7
|
export declare const name = "tool-manager";
|
|
8
8
|
export declare const inject: string[];
|
|
9
9
|
export declare const ENTRY_TOOLS: string[];
|
|
10
|
+
export declare const HOST_RESERVED: string[];
|
|
10
11
|
export declare const DEFAULT_PINNED: string[];
|
|
11
12
|
export declare function apply(ctx: HostContext, config?: CoreConfig): void;
|
|
12
13
|
/** Options for toolCatalog.load / tools_load. */
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAA2C,UAAU,EAAY,WAAW,EAA0F,MAAM,YAAY,CAAA;AAEpM,mBAAmB,YAAY,CAAA;AAC/B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,kBAAkB,EAAE,aAAa,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAC7L,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAChD,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC1E,YAAY,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAE9E,eAAO,MAAM,IAAI,iBAAiB,CAAA;AAClC,eAAO,MAAM,MAAM,UAAY,CAAA;AAE/B,eAAO,MAAM,WAAW,UAA6C,CAAA;AAMrE,eAAO,MAAM,aAAa,UAAe,CAAA;AAKzC,eAAO,MAAM,cAAc,UAY1B,CAAA;AAeD,wBAAgB,KAAK,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,GAAE,UAAe,QAmtB9D;AAED,iDAAiD;AACjD,MAAM,WAAW,WAAW;IAC1B,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;;;;;;AAOD,wBAAsC"}
|