dsh-perm-gate 4.2.0-beta.4 → 4.2.0-beta.5
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/dsh.plugin.json +1 -1
- package/lib/allowlist.d.ts +7 -0
- package/lib/allowlist.js +17 -0
- package/lib/client.js +4 -4
- package/lib/client.js.map +1 -1
- package/lib/index.js +38 -4
- package/lib/rules-view.d.ts +1 -1
- package/lib/rules-view.js +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { Config, ensureDataDir, readRulesFromSettings, readVolatileValue, resolveVolatileConfig, resolveDshHome, resolveDataDir, resolveGatePresets, resolvePermissiveStrategies, resolveRulesFile } from './config.js';
|
|
4
|
+
import { appendAllowToSettings, readRulesFileDoc, replaceAllowInSettings } from './allowlist.js';
|
|
4
5
|
import { runDryRun } from './dry-run.js';
|
|
5
6
|
import { registerDryRunRoute, registerEventsRoute, registerHealthRoute, registerLearningRoute, registerNetworkRoute, registerReceiverRoute, registerReviewRoutes, registerRulesRoute } from './events.js';
|
|
6
7
|
import { buildReceiverInfo } from './receiver-info.js';
|
|
@@ -176,10 +177,37 @@ export function apply(ctx, config = {}) {
|
|
|
176
177
|
// own entry (0.1.7 has no second projectable namespace). Unconfigured
|
|
177
178
|
// (bare defaults) -> undefined -> the rules file stays the source.
|
|
178
179
|
const readRulesDocument = () => readRulesFromSettings({ get: () => readVolatileValue(config.rules) });
|
|
180
|
+
// ─── Host settings seam (allowlist write-back) ────────────────────────────
|
|
181
|
+
// The `settings` service mutates this entry's own config in the active
|
|
182
|
+
// profile patch (agent-default-model injects the same service). Its writes
|
|
183
|
+
// land in the `rules` volatile field, whose change watcher below reloads the
|
|
184
|
+
// runtime — so an "allow always" / allowlist edit takes effect on the next
|
|
185
|
+
// tool call even when the rules document (not the file) is the live source.
|
|
186
|
+
// The inject is a child effect: a host without the settings service leaves
|
|
187
|
+
// the ref unset and the runtime falls back to its rules-file write paths.
|
|
188
|
+
const ENTRY_ID = 'dsh-perm-gate';
|
|
189
|
+
const settingsRef = {};
|
|
190
|
+
ctx.inject(['settings'], (child) => {
|
|
191
|
+
settingsRef.svc = child.settings;
|
|
192
|
+
});
|
|
193
|
+
const settingsWrite = (write) => {
|
|
194
|
+
const svc = settingsRef.svc;
|
|
195
|
+
if (svc === undefined)
|
|
196
|
+
return false;
|
|
197
|
+
const scope = {
|
|
198
|
+
get: () => readVolatileValue(config.rules),
|
|
199
|
+
update: async (section) => { await svc.update(ENTRY_ID, { rules: section }); },
|
|
200
|
+
};
|
|
201
|
+
void write(scope, readRulesFileDoc(rulesFilePath)).catch((e) => {
|
|
202
|
+
console.warn('[dsh-perm-gate] settings rules write failed:', e);
|
|
203
|
+
});
|
|
204
|
+
return true;
|
|
205
|
+
};
|
|
179
206
|
// Plugin-owned data files live under $DSH_HOME (node_modules may be
|
|
180
207
|
// read-only). The default resolves to `$DSH_HOME` / `~/.dsh`, so a profile
|
|
181
208
|
// entry that omits `config` still records events, snapshots and learning.
|
|
182
209
|
const dataDir = resolveDataDir(typeof config.dshHome === 'string' ? config.dshHome : undefined);
|
|
210
|
+
const rulesFilePath = resolveRulesFile(typeof config.rulesFile === 'string' ? config.rulesFile : undefined, dataDir);
|
|
183
211
|
// Host model-group services (typed minimally; supplied by the dsh runtime
|
|
184
212
|
// through the loader `inject` — dsh-approval-gate demonstrates the same
|
|
185
213
|
// contract). The llm service backs the `host` receiver; agentDefaultModel
|
|
@@ -321,9 +349,16 @@ export function apply(ctx, config = {}) {
|
|
|
321
349
|
// The rules document lives in the plugin's data dir by default, so a
|
|
322
350
|
// `$DSH_HOME/perm-gate/rules.yml` the user writes is actually loaded without
|
|
323
351
|
// also having to declare `rulesFile` in the composition entry.
|
|
324
|
-
rulesFile:
|
|
352
|
+
rulesFile: rulesFilePath,
|
|
325
353
|
// Settings-sourced rules take precedence over the file when present.
|
|
326
354
|
readRulesDocument,
|
|
355
|
+
// Allowlist write-back through the settings service (this entry's own
|
|
356
|
+
// `rules` config field, seeded from the file document on first write);
|
|
357
|
+
// a falsy verdict (service not wired) falls back to the rules file.
|
|
358
|
+
allowlistWriter: {
|
|
359
|
+
append: (pattern, reason) => settingsWrite((scope, seed) => appendAllowToSettings(scope, pattern, reason, seed)),
|
|
360
|
+
replace: (patterns, reason) => settingsWrite((scope, seed) => replaceAllowInSettings(scope, patterns, reason, seed)),
|
|
361
|
+
},
|
|
327
362
|
// The whole gate is scoped to the presets that opt into it (default: the
|
|
328
363
|
// `permissive` tier this plugin adds); elsewhere it stands down entirely.
|
|
329
364
|
gatePresets: resolveGatePresets(Array.isArray(config.gatePresets) ? config.gatePresets : undefined),
|
|
@@ -505,9 +540,8 @@ export function apply(ctx, config = {}) {
|
|
|
505
540
|
});
|
|
506
541
|
if (offDryRun !== undefined)
|
|
507
542
|
ctx.effect(() => () => { offDryRun(); }, 'dsh-perm-gate: dry-run route');
|
|
508
|
-
// Permissions view: prefer the
|
|
509
|
-
//
|
|
510
|
-
const rulesFilePath = resolveRulesFile(typeof config.rulesFile === 'string' ? config.rulesFile : undefined, dataDir);
|
|
543
|
+
// Permissions view: prefer the entry's `rules` config field when it
|
|
544
|
+
// carries a real configuration; fall back to the rules file.
|
|
511
545
|
const offRules = registerRulesRoute(webServer, {
|
|
512
546
|
view: () => {
|
|
513
547
|
const settingsDoc = readRulesDocument();
|
package/lib/rules-view.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ export interface RulesView {
|
|
|
37
37
|
*/
|
|
38
38
|
export declare function readRulesView(rulesFile: string): RulesView;
|
|
39
39
|
/** The pseudo-path a settings view reports (there is no file to name). */
|
|
40
|
-
export declare const SETTINGS_RULES_VIEW_PATH = "
|
|
40
|
+
export declare const SETTINGS_RULES_VIEW_PATH = "config:dsh-perm-gate#rules";
|
|
41
41
|
/**
|
|
42
42
|
* Render the settings-sourced rules for display — the read-only face of the
|
|
43
43
|
* `dsh-perm-gate-rules` namespace, the twin of {@link readRulesView}.
|
package/lib/rules-view.js
CHANGED
|
@@ -79,7 +79,7 @@ export function readRulesView(rulesFile) {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
/** The pseudo-path a settings view reports (there is no file to name). */
|
|
82
|
-
export const SETTINGS_RULES_VIEW_PATH = '
|
|
82
|
+
export const SETTINGS_RULES_VIEW_PATH = 'config:dsh-perm-gate#rules';
|
|
83
83
|
/**
|
|
84
84
|
* Render the settings-sourced rules for display — the read-only face of the
|
|
85
85
|
* `dsh-perm-gate-rules` namespace, the twin of {@link readRulesView}.
|