dsh-perm-gate 4.1.3 → 4.2.0-beta.2
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/CHANGELOG.ja.md +0 -26
- package/CHANGELOG.ko.md +0 -24
- package/CHANGELOG.md +0 -26
- package/INSTALL.ja.md +14 -28
- package/INSTALL.ko.md +11 -23
- package/INSTALL.md +16 -32
- package/INSTALL.zh.md +11 -22
- package/README.ja.md +7 -34
- package/README.ko.md +7 -34
- package/README.md +7 -34
- package/README.zh.md +7 -29
- package/cordis.patch.yml +8 -11
- package/dsh.plugin.json +2 -2
- package/lib/client.js +8 -8
- package/lib/client.js.map +1 -1
- package/lib/config.d.ts +120 -1
- package/lib/config.js +71 -35
- package/lib/index.js +42 -77
- package/package.json +22 -20
- package/scripts/patch-permission-glyph.mjs +42 -225
- package/assets/allow1.png +0 -0
- package/assets/allow2.png +0 -0
- package/assets/gateoff.png +0 -0
- package/assets/llms.png +0 -0
- package/assets/permlist.png +0 -0
- package/assets/setting-head.png +0 -0
- package/assets/setting-rules.png +0 -0
package/lib/config.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import z from '@deepseek-ai/schemastery';
|
|
2
|
+
import type { Volatile, VolatileSnapshot } from '@deepseek-ai/cosmokit';
|
|
2
3
|
import type { RuleAction } from './rule.js';
|
|
3
4
|
import { DEFAULT_GATE_PRESETS, resolveGatePresets } from './preset.js';
|
|
4
5
|
/**
|
|
@@ -293,7 +294,125 @@ export declare function isRulesConfigured(value: unknown): boolean;
|
|
|
293
294
|
export declare function readRulesFromSettings(scope: {
|
|
294
295
|
get(): unknown;
|
|
295
296
|
} | undefined): RulesConfig | undefined;
|
|
296
|
-
export declare const Config: z<
|
|
297
|
+
export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
298
|
+
rulesFile: z<string, string, "plain">;
|
|
299
|
+
dshHome: z<string, string, "plain">;
|
|
300
|
+
defaultAction: z<"allow" | "ask" | "deny", "allow" | "ask" | "deny", "volatile-defined">;
|
|
301
|
+
caseInsensitivePaths: z<boolean, boolean, "volatile-defined">;
|
|
302
|
+
classifierEnabled: z<boolean, boolean, "volatile-defined">;
|
|
303
|
+
classifierEndpoint: z<string, string, "volatile">;
|
|
304
|
+
classifierModel: z<string, string, "volatile-defined">;
|
|
305
|
+
classifierApiKey: z<string, string, "volatile">;
|
|
306
|
+
riskTimeoutMs: z<number, number, "volatile-defined">;
|
|
307
|
+
riskLearning: z<boolean, boolean, "volatile-defined">;
|
|
308
|
+
riskSediment: z<boolean, boolean, "volatile-defined">;
|
|
309
|
+
classifierSource: z<"host" | "custom", "host" | "custom", "volatile-defined">;
|
|
310
|
+
classifierProvider: z<string, string, "volatile">;
|
|
311
|
+
riskThreshold: z<number, number, "volatile-defined">;
|
|
312
|
+
learningFile: z<string, string, "plain">;
|
|
313
|
+
eventsFile: z<string, string, "plain">;
|
|
314
|
+
grantTtlMs: z<number, number, "volatile-defined">;
|
|
315
|
+
grantMaxUses: z<number, number, "volatile-defined">;
|
|
316
|
+
permissive: z<boolean, boolean, "volatile-defined">;
|
|
317
|
+
gatePresets: z<string[], string[], "plain">;
|
|
318
|
+
permissiveStrategies: z<NoInfer<Schemastery.ObjectS<NoInfer<{
|
|
319
|
+
trustAutoAllow: z<boolean, boolean, "defined">;
|
|
320
|
+
alwaysConfirm: z<boolean, boolean, "defined">;
|
|
321
|
+
llmAssist: z<boolean, boolean, "defined">;
|
|
322
|
+
trustEscalation: z<boolean, boolean, "defined">;
|
|
323
|
+
}>>>, NoInfer<Schemastery.ObjectT<NoInfer<{
|
|
324
|
+
trustAutoAllow: z<boolean, boolean, "defined">;
|
|
325
|
+
alwaysConfirm: z<boolean, boolean, "defined">;
|
|
326
|
+
llmAssist: z<boolean, boolean, "defined">;
|
|
327
|
+
trustEscalation: z<boolean, boolean, "defined">;
|
|
328
|
+
}>>>, "volatile">;
|
|
329
|
+
allowlist: z<NoInfer<string[]>, NoInfer<string[]>, "volatile">;
|
|
330
|
+
denyKeywords: z<NoInfer<string[]>, NoInfer<string[]>, "volatile">;
|
|
331
|
+
autoAllowTools: z<NoInfer<string[]>, NoInfer<string[]>, "volatile">;
|
|
332
|
+
sessionSweep: z<boolean, boolean, "volatile-defined">;
|
|
333
|
+
workspaceStoreFile: z<string, string, "plain">;
|
|
334
|
+
searchUp: z<boolean, boolean, "volatile-defined">;
|
|
335
|
+
fallbackPath: z<string, string, "plain">;
|
|
336
|
+
badFilePolicy: z<"fail" | "warn", "fail" | "warn", "volatile-defined">;
|
|
337
|
+
maxChainLength: z<number, number, "volatile-defined">;
|
|
338
|
+
networkEnabled: z<boolean, boolean, "volatile-defined">;
|
|
339
|
+
networkMode: z<"deny-all" | "whitelist" | "allow-all", "deny-all" | "whitelist" | "allow-all", "volatile-defined">;
|
|
340
|
+
networkUnlisted: z<"ask" | "deny", "ask" | "deny", "volatile-defined">;
|
|
341
|
+
networkUnattributed: z<"allow" | "deny", "allow" | "deny", "volatile-defined">;
|
|
342
|
+
networkLoopback: z<"allow" | "policy", "allow" | "policy", "volatile-defined">;
|
|
343
|
+
networkBind: z<string, string, "volatile-defined">;
|
|
344
|
+
networkPort: z<number, number, "volatile-defined">;
|
|
345
|
+
networkNoProxy: z<"clear" | "preserve", "clear" | "preserve", "volatile-defined">;
|
|
346
|
+
networkInjectEnv: z<boolean, boolean, "volatile-defined">;
|
|
347
|
+
networkAskTimeoutMs: z<number, number, "volatile-defined">;
|
|
348
|
+
networkGrantTtlMs: z<number, number, "volatile-defined">;
|
|
349
|
+
rules: z<NoInfer<RulesConfig>, NoInfer<RulesConfig>, "volatile">;
|
|
350
|
+
watch: z<boolean, boolean, "defined">;
|
|
351
|
+
watchDebounceMs: z<number, number, "defined">;
|
|
352
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
353
|
+
rulesFile: z<string, string, "plain">;
|
|
354
|
+
dshHome: z<string, string, "plain">;
|
|
355
|
+
defaultAction: z<"allow" | "ask" | "deny", "allow" | "ask" | "deny", "volatile-defined">;
|
|
356
|
+
caseInsensitivePaths: z<boolean, boolean, "volatile-defined">;
|
|
357
|
+
classifierEnabled: z<boolean, boolean, "volatile-defined">;
|
|
358
|
+
classifierEndpoint: z<string, string, "volatile">;
|
|
359
|
+
classifierModel: z<string, string, "volatile-defined">;
|
|
360
|
+
classifierApiKey: z<string, string, "volatile">;
|
|
361
|
+
riskTimeoutMs: z<number, number, "volatile-defined">;
|
|
362
|
+
riskLearning: z<boolean, boolean, "volatile-defined">;
|
|
363
|
+
riskSediment: z<boolean, boolean, "volatile-defined">;
|
|
364
|
+
classifierSource: z<"host" | "custom", "host" | "custom", "volatile-defined">;
|
|
365
|
+
classifierProvider: z<string, string, "volatile">;
|
|
366
|
+
riskThreshold: z<number, number, "volatile-defined">;
|
|
367
|
+
learningFile: z<string, string, "plain">;
|
|
368
|
+
eventsFile: z<string, string, "plain">;
|
|
369
|
+
grantTtlMs: z<number, number, "volatile-defined">;
|
|
370
|
+
grantMaxUses: z<number, number, "volatile-defined">;
|
|
371
|
+
permissive: z<boolean, boolean, "volatile-defined">;
|
|
372
|
+
gatePresets: z<string[], string[], "plain">;
|
|
373
|
+
permissiveStrategies: z<NoInfer<Schemastery.ObjectS<NoInfer<{
|
|
374
|
+
trustAutoAllow: z<boolean, boolean, "defined">;
|
|
375
|
+
alwaysConfirm: z<boolean, boolean, "defined">;
|
|
376
|
+
llmAssist: z<boolean, boolean, "defined">;
|
|
377
|
+
trustEscalation: z<boolean, boolean, "defined">;
|
|
378
|
+
}>>>, NoInfer<Schemastery.ObjectT<NoInfer<{
|
|
379
|
+
trustAutoAllow: z<boolean, boolean, "defined">;
|
|
380
|
+
alwaysConfirm: z<boolean, boolean, "defined">;
|
|
381
|
+
llmAssist: z<boolean, boolean, "defined">;
|
|
382
|
+
trustEscalation: z<boolean, boolean, "defined">;
|
|
383
|
+
}>>>, "volatile">;
|
|
384
|
+
allowlist: z<NoInfer<string[]>, NoInfer<string[]>, "volatile">;
|
|
385
|
+
denyKeywords: z<NoInfer<string[]>, NoInfer<string[]>, "volatile">;
|
|
386
|
+
autoAllowTools: z<NoInfer<string[]>, NoInfer<string[]>, "volatile">;
|
|
387
|
+
sessionSweep: z<boolean, boolean, "volatile-defined">;
|
|
388
|
+
workspaceStoreFile: z<string, string, "plain">;
|
|
389
|
+
searchUp: z<boolean, boolean, "volatile-defined">;
|
|
390
|
+
fallbackPath: z<string, string, "plain">;
|
|
391
|
+
badFilePolicy: z<"fail" | "warn", "fail" | "warn", "volatile-defined">;
|
|
392
|
+
maxChainLength: z<number, number, "volatile-defined">;
|
|
393
|
+
networkEnabled: z<boolean, boolean, "volatile-defined">;
|
|
394
|
+
networkMode: z<"deny-all" | "whitelist" | "allow-all", "deny-all" | "whitelist" | "allow-all", "volatile-defined">;
|
|
395
|
+
networkUnlisted: z<"ask" | "deny", "ask" | "deny", "volatile-defined">;
|
|
396
|
+
networkUnattributed: z<"allow" | "deny", "allow" | "deny", "volatile-defined">;
|
|
397
|
+
networkLoopback: z<"allow" | "policy", "allow" | "policy", "volatile-defined">;
|
|
398
|
+
networkBind: z<string, string, "volatile-defined">;
|
|
399
|
+
networkPort: z<number, number, "volatile-defined">;
|
|
400
|
+
networkNoProxy: z<"clear" | "preserve", "clear" | "preserve", "volatile-defined">;
|
|
401
|
+
networkInjectEnv: z<boolean, boolean, "volatile-defined">;
|
|
402
|
+
networkAskTimeoutMs: z<number, number, "volatile-defined">;
|
|
403
|
+
networkGrantTtlMs: z<number, number, "volatile-defined">;
|
|
404
|
+
rules: z<NoInfer<RulesConfig>, NoInfer<RulesConfig>, "volatile">;
|
|
405
|
+
watch: z<boolean, boolean, "defined">;
|
|
406
|
+
watchDebounceMs: z<number, number, "defined">;
|
|
407
|
+
}>>, "plain">;
|
|
408
|
+
/** Live reference the 0.1.7 loader hands `apply` for `.volatile()` config fields. */
|
|
409
|
+
export type VolatileRef<T> = Volatile<T>;
|
|
410
|
+
/** The config fields marked `.volatile()` — live refs inside `apply`'s config. */
|
|
411
|
+
export declare const VOLATILE_CONFIG_KEYS: readonly ["defaultAction", "caseInsensitivePaths", "classifierEnabled", "classifierEndpoint", "classifierModel", "classifierApiKey", "riskTimeoutMs", "riskLearning", "riskSediment", "classifierSource", "classifierProvider", "riskThreshold", "grantTtlMs", "grantMaxUses", "permissive", "permissiveStrategies", "allowlist", "denyKeywords", "autoAllowTools", "sessionSweep", "searchUp", "badFilePolicy", "maxChainLength", "networkEnabled", "networkMode", "networkUnlisted", "networkUnattributed", "networkLoopback", "networkBind", "networkPort", "networkNoProxy", "networkInjectEnv", "networkAskTimeoutMs", "networkGrantTtlMs", "rules"];
|
|
412
|
+
/** Resolve one possibly-volatile field: a live ref on 0.1.7+, a plain value otherwise. */
|
|
413
|
+
export declare function readVolatileValue<T>(value: T | Volatile<T> | undefined): VolatileSnapshot<T> | undefined;
|
|
414
|
+
/** Shallow-resolve every volatile field into a plain snapshot (one per read). */
|
|
415
|
+
export declare function resolveVolatileConfig(config: object): Record<string, unknown>;
|
|
297
416
|
export type ResolvedPermGateConfig = Required<Pick<PermGateConfig, 'caseInsensitivePaths' | 'grantTtlMs' | 'grantMaxUses' | 'permissive' | 'riskTimeoutMs' | 'riskLearning' | 'riskThreshold'>> & Pick<PermGateConfig, 'rulesFile' | 'dshHome' | 'defaultAction' | 'classifierEnabled' | 'classifierEndpoint' | 'classifierModel' | 'classifierApiKey' | 'learningFile' | 'eventsFile'> & {
|
|
298
417
|
readonly permissiveStrategies: PermissiveStrategies;
|
|
299
418
|
readonly gatePresets: readonly string[];
|
package/lib/config.js
CHANGED
|
@@ -146,58 +146,94 @@ export function readRulesFromSettings(scope) {
|
|
|
146
146
|
export const Config = z.object({
|
|
147
147
|
rulesFile: z.string(),
|
|
148
148
|
dshHome: z.string(),
|
|
149
|
-
defaultAction: z.union(['allow', 'ask', 'deny']).default('ask'),
|
|
150
|
-
caseInsensitivePaths: z.boolean().default(true),
|
|
151
|
-
classifierEnabled: z.boolean().default(false),
|
|
152
|
-
classifierEndpoint: z.string(),
|
|
153
|
-
classifierModel: z.string().default('deepseek-chat'),
|
|
154
|
-
classifierApiKey: z.string(),
|
|
155
|
-
riskTimeoutMs: z.number().min(1000).default(20_000),
|
|
156
|
-
riskLearning: z.boolean().default(true),
|
|
157
|
-
riskSediment: z.boolean().default(true),
|
|
158
|
-
classifierSource: z.union(['custom', 'host']).default('custom'),
|
|
159
|
-
classifierProvider: z.string(),
|
|
160
|
-
riskThreshold: z.number().min(1).max(10).default(1),
|
|
149
|
+
defaultAction: z.union(['allow', 'ask', 'deny']).default('ask').volatile(),
|
|
150
|
+
caseInsensitivePaths: z.boolean().default(true).volatile(),
|
|
151
|
+
classifierEnabled: z.boolean().default(false).volatile(),
|
|
152
|
+
classifierEndpoint: z.string().volatile(),
|
|
153
|
+
classifierModel: z.string().default('deepseek-chat').volatile(),
|
|
154
|
+
classifierApiKey: z.string().volatile(),
|
|
155
|
+
riskTimeoutMs: z.number().min(1000).default(20_000).volatile(),
|
|
156
|
+
riskLearning: z.boolean().default(true).volatile(),
|
|
157
|
+
riskSediment: z.boolean().default(true).volatile(),
|
|
158
|
+
classifierSource: z.union(['custom', 'host']).default('custom').volatile(),
|
|
159
|
+
classifierProvider: z.string().volatile(),
|
|
160
|
+
riskThreshold: z.number().min(1).max(10).default(1).volatile(),
|
|
161
161
|
learningFile: z.string(),
|
|
162
162
|
eventsFile: z.string(),
|
|
163
|
-
grantTtlMs: z.number().min(1).default(5 * 60_000),
|
|
164
|
-
grantMaxUses: z.number().min(1).default(1),
|
|
165
|
-
permissive: z.boolean().default(false),
|
|
163
|
+
grantTtlMs: z.number().min(1).default(5 * 60_000).volatile(),
|
|
164
|
+
grantMaxUses: z.number().min(1).default(1).volatile(),
|
|
165
|
+
permissive: z.boolean().default(false).volatile(),
|
|
166
166
|
gatePresets: z.array(z.string()),
|
|
167
167
|
permissiveStrategies: z.object({
|
|
168
168
|
trustAutoAllow: z.boolean().default(true),
|
|
169
169
|
alwaysConfirm: z.boolean().default(false),
|
|
170
170
|
llmAssist: z.boolean().default(false),
|
|
171
171
|
trustEscalation: z.boolean().default(true),
|
|
172
|
-
}),
|
|
173
|
-
allowlist: z.array(z.string()),
|
|
174
|
-
denyKeywords: z.array(z.string()),
|
|
175
|
-
autoAllowTools: z.array(z.string()),
|
|
176
|
-
sessionSweep: z.boolean().default(true),
|
|
172
|
+
}).volatile(),
|
|
173
|
+
allowlist: z.array(z.string()).volatile(),
|
|
174
|
+
denyKeywords: z.array(z.string()).volatile(),
|
|
175
|
+
autoAllowTools: z.array(z.string()).volatile(),
|
|
176
|
+
sessionSweep: z.boolean().default(true).volatile(),
|
|
177
177
|
workspaceStoreFile: z.string(),
|
|
178
178
|
// Rule chain (T1.9)
|
|
179
|
-
searchUp: z.boolean().default(false),
|
|
179
|
+
searchUp: z.boolean().default(false).volatile(),
|
|
180
180
|
fallbackPath: z.string(),
|
|
181
|
-
badFilePolicy: z.union(['fail', 'warn']).default('fail'),
|
|
182
|
-
maxChainLength: z.number().min(1).max(50).default(10),
|
|
181
|
+
badFilePolicy: z.union(['fail', 'warn']).default('fail').volatile(),
|
|
182
|
+
maxChainLength: z.number().min(1).max(50).default(10).volatile(),
|
|
183
183
|
// Network (Phase 2)
|
|
184
|
-
networkEnabled: z.boolean().default(false),
|
|
185
|
-
networkMode: z.union(['deny-all', 'whitelist', 'allow-all']).default('whitelist'),
|
|
186
|
-
networkUnlisted: z.union(['ask', 'deny']).default('ask'),
|
|
187
|
-
networkUnattributed: z.union(['allow', 'deny']).default('allow'),
|
|
188
|
-
networkLoopback: z.union(['allow', 'policy']).default('allow'),
|
|
189
|
-
networkBind: z.string().default('127.0.0.1'),
|
|
190
|
-
networkPort: z.number().min(0).max(65535).default(0),
|
|
191
|
-
networkNoProxy: z.union(['clear', 'preserve']).default('clear'),
|
|
192
|
-
networkInjectEnv: z.boolean().default(true),
|
|
193
|
-
networkAskTimeoutMs: z.number().min(1000).max(600_000).default(120_000),
|
|
194
|
-
networkGrantTtlMs: z.number().min(0).max(24 * 60 * 60_000).default(30 * 60_000),
|
|
184
|
+
networkEnabled: z.boolean().default(false).volatile(),
|
|
185
|
+
networkMode: z.union(['deny-all', 'whitelist', 'allow-all']).default('whitelist').volatile(),
|
|
186
|
+
networkUnlisted: z.union(['ask', 'deny']).default('ask').volatile(),
|
|
187
|
+
networkUnattributed: z.union(['allow', 'deny']).default('allow').volatile(),
|
|
188
|
+
networkLoopback: z.union(['allow', 'policy']).default('allow').volatile(),
|
|
189
|
+
networkBind: z.string().default('127.0.0.1').volatile(),
|
|
190
|
+
networkPort: z.number().min(0).max(65535).default(0).volatile(),
|
|
191
|
+
networkNoProxy: z.union(['clear', 'preserve']).default('clear').volatile(),
|
|
192
|
+
networkInjectEnv: z.boolean().default(true).volatile(),
|
|
193
|
+
networkAskTimeoutMs: z.number().min(1000).max(600_000).default(120_000).volatile(),
|
|
194
|
+
networkGrantTtlMs: z.number().min(0).max(24 * 60 * 60_000).default(30 * 60_000).volatile(),
|
|
195
|
+
// 0.1.7: the rules document lives on this entry as a volatile whole-object
|
|
196
|
+
// field (a second settings namespace is no longer projectable). Unconfigured
|
|
197
|
+
// (bare defaults) -> the rules file remains the source.
|
|
198
|
+
rules: RulesSchema.volatile(),
|
|
195
199
|
// Hot reload (Phase 3)
|
|
196
200
|
watch: z.boolean().default(false),
|
|
197
201
|
watchDebounceMs: z.number().min(50).max(5000).default(300),
|
|
198
202
|
});
|
|
203
|
+
/** The config fields marked `.volatile()` — live refs inside `apply`'s config. */
|
|
204
|
+
export const VOLATILE_CONFIG_KEYS = [
|
|
205
|
+
'defaultAction', 'caseInsensitivePaths', 'classifierEnabled', 'classifierEndpoint',
|
|
206
|
+
'classifierModel', 'classifierApiKey', 'riskTimeoutMs', 'riskLearning', 'riskSediment',
|
|
207
|
+
'classifierSource', 'classifierProvider', 'riskThreshold', 'grantTtlMs', 'grantMaxUses',
|
|
208
|
+
'permissive', 'permissiveStrategies', 'allowlist', 'denyKeywords', 'autoAllowTools',
|
|
209
|
+
'sessionSweep', 'searchUp', 'badFilePolicy', 'maxChainLength',
|
|
210
|
+
'networkEnabled', 'networkMode', 'networkUnlisted', 'networkUnattributed', 'networkLoopback',
|
|
211
|
+
'networkBind', 'networkPort', 'networkNoProxy', 'networkInjectEnv', 'networkAskTimeoutMs',
|
|
212
|
+
'networkGrantTtlMs', 'rules',
|
|
213
|
+
];
|
|
214
|
+
/** Resolve one possibly-volatile field: a live ref on 0.1.7+, a plain value otherwise. */
|
|
215
|
+
export function readVolatileValue(value) {
|
|
216
|
+
if (value !== null && typeof value === 'object' && typeof value.get === 'function') {
|
|
217
|
+
return value.get();
|
|
218
|
+
}
|
|
219
|
+
return value;
|
|
220
|
+
}
|
|
221
|
+
/** Shallow-resolve every volatile field into a plain snapshot (one per read). */
|
|
222
|
+
export function resolveVolatileConfig(config) {
|
|
223
|
+
const out = { ...config };
|
|
224
|
+
for (const key of VOLATILE_CONFIG_KEYS) {
|
|
225
|
+
const v = out[key];
|
|
226
|
+
if (v !== null && typeof v === 'object' && typeof v.get === 'function') {
|
|
227
|
+
out[key] = v.get();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return out;
|
|
231
|
+
}
|
|
199
232
|
export function resolveConfig(config = {}) {
|
|
200
|
-
|
|
233
|
+
// 0.1.7: the apply-time config carries live refs for `.volatile()` fields —
|
|
234
|
+
// resolve them before validation; schemastery's own schema call also wraps
|
|
235
|
+
// volatile fields into fresh refs, so resolve the output as well.
|
|
236
|
+
const parsed = resolveVolatileConfig(Config(resolveVolatileConfig(config)));
|
|
201
237
|
return {
|
|
202
238
|
rulesFile: parsed.rulesFile,
|
|
203
239
|
dshHome: parsed.dshHome,
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { Config,
|
|
3
|
+
import { Config, ensureDataDir, readRulesFromSettings, readVolatileValue, resolveVolatileConfig, resolveDshHome, resolveDataDir, resolveGatePresets, resolvePermissiveStrategies, resolveRulesFile } from './config.js';
|
|
4
4
|
import { runDryRun } from './dry-run.js';
|
|
5
5
|
import { registerDryRunRoute, registerEventsRoute, registerHealthRoute, registerLearningRoute, registerNetworkRoute, registerReceiverRoute, registerReviewRoutes, registerRulesRoute } from './events.js';
|
|
6
6
|
import { buildReceiverInfo } from './receiver-info.js';
|
|
@@ -36,27 +36,6 @@ export { Config };
|
|
|
36
36
|
const APPROVAL_OUTCOMES = new Set(['allowed-once', 'rejected', 'cancelled', 'unavailable']);
|
|
37
37
|
/** Runtime settings namespace: `permissive` + `permissiveStrategies` (editable in the UI). */
|
|
38
38
|
export const PERMISSIVE_NAMESPACE = 'dsh-perm-gate';
|
|
39
|
-
/**
|
|
40
|
-
* Inline equivalent of the official `installSettingsSection` helper: register
|
|
41
|
-
* the namespace through the `settings` service, layer the composition entry as
|
|
42
|
-
* `base`, and keep the runtime source live so the UI card applies immediately.
|
|
43
|
-
* `onScope` receives the live scope after registration (for seeding/syncing
|
|
44
|
-
* host-owned fields like the allowlist).
|
|
45
|
-
*/
|
|
46
|
-
function installSettingsSection(ctx, ns, schema, entry, hooks) {
|
|
47
|
-
;
|
|
48
|
-
ctx.inject(['settings'], (sctx) => {
|
|
49
|
-
const scope = sctx.settings.register(ns, schema, { base: entry });
|
|
50
|
-
hooks.setSource(() => scope.get());
|
|
51
|
-
hooks.onChange();
|
|
52
|
-
hooks.onScope?.(scope);
|
|
53
|
-
sctx.effect(() => () => {
|
|
54
|
-
hooks.setSource(() => entry);
|
|
55
|
-
hooks.onChange();
|
|
56
|
-
});
|
|
57
|
-
scope.watch(() => hooks.onChange());
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
39
|
/** Column a settings-scope value into the typed live face. */
|
|
61
40
|
function asSurface(value) {
|
|
62
41
|
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
@@ -189,14 +168,14 @@ export function makePreExecuteListener(runtime) {
|
|
|
189
168
|
};
|
|
190
169
|
}
|
|
191
170
|
export function apply(ctx, config = {}) {
|
|
192
|
-
// Runtime-adjustable config:
|
|
193
|
-
//
|
|
194
|
-
|
|
195
|
-
// ─── Rules
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
const readRulesDocument = () => readRulesFromSettings(
|
|
171
|
+
// Runtime-adjustable config: 0.1.7 hands `.volatile()` fields to apply() as
|
|
172
|
+
// live refs; `current()` shallow-resolves one snapshot per read.
|
|
173
|
+
const current = () => resolveVolatileConfig(config);
|
|
174
|
+
// ─── Rules (volatile `rules` field on this entry) ─────────────────────────
|
|
175
|
+
// The rules document is a `.volatile()` whole-object field of the plugin's
|
|
176
|
+
// own entry (0.1.7 has no second projectable namespace). Unconfigured
|
|
177
|
+
// (bare defaults) -> undefined -> the rules file stays the source.
|
|
178
|
+
const readRulesDocument = () => readRulesFromSettings({ get: () => readVolatileValue(config.rules) });
|
|
200
179
|
// Plugin-owned data files live under $DSH_HOME (node_modules may be
|
|
201
180
|
// read-only). The default resolves to `$DSH_HOME` / `~/.dsh`, so a profile
|
|
202
181
|
// entry that omits `config` still records events, snapshots and learning.
|
|
@@ -429,54 +408,40 @@ export function apply(ctx, config = {}) {
|
|
|
429
408
|
// the settings watch only ever runs on a later user action. Held in an
|
|
430
409
|
// object so the mutable slots stay lint-clean and the intent is explicit.
|
|
431
410
|
const networkRefs = {};
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
if (
|
|
442
|
-
|
|
443
|
-
if (patterns.length > 0)
|
|
444
|
-
void scope.update({ allowlist: patterns.slice() });
|
|
411
|
+
// ─── 0.1.7 declarative settings ───────────────────────────────────────────
|
|
412
|
+
// The volatile fields render the settings form and arrive as live refs;
|
|
413
|
+
// each committed edit raises ONE `loader/volatile-update` (no remount).
|
|
414
|
+
// The host no longer seeds settings (there is no write path): an empty
|
|
415
|
+
// `allowlist` keeps the rules-file default via `runtime.allowlist()`.
|
|
416
|
+
let lastRulesDocKey;
|
|
417
|
+
ctx.on('loader/volatile-update', () => {
|
|
418
|
+
const next = asSurface(current());
|
|
419
|
+
if (next !== undefined) {
|
|
420
|
+
if (Array.isArray(next.allowlist)) {
|
|
421
|
+
runtime.setAllowlist(next.allowlist.filter((x) => typeof x === 'string'));
|
|
445
422
|
}
|
|
446
|
-
//
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
// loads rules from settings (when configured) instead of the rules file.
|
|
469
|
-
installSettingsSection(ctx, RULES_NAMESPACE, RulesSchema, {}, {
|
|
470
|
-
setSource: (source) => {
|
|
471
|
-
rulesScope = { get: source };
|
|
472
|
-
},
|
|
473
|
-
onChange: () => { },
|
|
474
|
-
onScope: (scope) => {
|
|
475
|
-
rulesScope = scope;
|
|
476
|
-
// When rules change in settings, reload the runtime so the new ruleset
|
|
477
|
-
// takes effect on the next tool call.
|
|
478
|
-
scope.watch(() => { runtime.reload(); });
|
|
479
|
-
},
|
|
423
|
+
// A network knob changed: re-mount the proxy so the card's switches
|
|
424
|
+
// take effect without a plugin reload. Only a rebind-relevant edit
|
|
425
|
+
// triggers it.
|
|
426
|
+
const key = networkKeyOf(next);
|
|
427
|
+
if (networkRefs.appliedKey !== undefined && networkRefs.appliedKey !== key) {
|
|
428
|
+
networkRefs.appliedKey = key;
|
|
429
|
+
networkRefs.rebind?.();
|
|
430
|
+
}
|
|
431
|
+
else if (networkRefs.appliedKey === undefined) {
|
|
432
|
+
networkRefs.appliedKey = key;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
// A rules edit reloads the runtime so the new ruleset takes effect on the
|
|
436
|
+
// next tool call (allowlist/network edits must not pay for a reload).
|
|
437
|
+
const rulesDoc = readRulesDocument();
|
|
438
|
+
const rulesKey = rulesDoc === undefined ? '' : JSON.stringify(rulesDoc);
|
|
439
|
+
if (rulesKey !== lastRulesDocKey) {
|
|
440
|
+
const changed = lastRulesDocKey !== undefined;
|
|
441
|
+
lastRulesDocKey = rulesKey;
|
|
442
|
+
if (changed)
|
|
443
|
+
runtime.reload();
|
|
444
|
+
}
|
|
480
445
|
});
|
|
481
446
|
// Event feed HTTP API (best effort): the dsh webServer service exposes the
|
|
482
447
|
// JSONL decision events to the browser half; without it events stay on disk.
|
package/package.json
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-perm-gate",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0-beta.2",
|
|
4
4
|
"publishConfig": {
|
|
5
|
-
"
|
|
6
|
-
"access": "public",
|
|
7
|
-
"tag": "dsh-0.1.5"
|
|
5
|
+
"tag": "dsh-0.1.7"
|
|
8
6
|
},
|
|
9
7
|
"description": "DSH permission-gate for the DeepSeek Harness 0.1.5 line: a single self-sufficient, deterministic-first, fail-closed gate covering P0 hard-deny -> P1 session grant -> P2 static rule (allow/deny) chain -> P3 optional LLM semantic classifier -> P4 ask, with command whitelist/blacklist.",
|
|
10
8
|
"type": "module",
|
|
@@ -67,32 +65,25 @@
|
|
|
67
65
|
},
|
|
68
66
|
"engines": {
|
|
69
67
|
"node": ">=24",
|
|
70
|
-
"dsh": ">=0.1.
|
|
68
|
+
"dsh": ">=0.1.7-rc.1 <0.1.8-0"
|
|
71
69
|
},
|
|
72
70
|
"peerDependencies": {
|
|
73
71
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
74
|
-
"@deepseek-ai/dsh-client-locale": ">=0.1.
|
|
75
|
-
"@deepseek-ai/dsh-client-ui-
|
|
76
|
-
"@deepseek-ai/dsh-client-ui-
|
|
77
|
-
"@deepseek-ai/dsh-client-ui-conversation": ">=0.1.5-rc.1 <0.1.6-0"
|
|
78
|
-
},
|
|
79
|
-
"peerDependenciesMeta": {
|
|
80
|
-
"@deepseek-ai/dsh-client-locale": { "optional": true },
|
|
81
|
-
"@deepseek-ai/dsh-client-ui-settings": { "optional": true },
|
|
82
|
-
"@deepseek-ai/dsh-client-ui-renderer": { "optional": true },
|
|
83
|
-
"@deepseek-ai/dsh-client-ui-conversation": { "optional": true }
|
|
72
|
+
"@deepseek-ai/dsh-client-locale": ">=0.1.7-rc.1 <0.1.8-0",
|
|
73
|
+
"@deepseek-ai/dsh-client-ui-renderer": ">=0.1.7-rc.1 <0.1.8-0",
|
|
74
|
+
"@deepseek-ai/dsh-client-ui-settings": ">=0.1.7-rc.1 <0.1.8-0"
|
|
84
75
|
},
|
|
85
76
|
"dependencies": {
|
|
86
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
77
|
+
"@deepseek-ai/schemastery": "^3.18.4",
|
|
87
78
|
"chokidar": "^5.0.0",
|
|
88
79
|
"yaml": "^2.5.0"
|
|
89
80
|
},
|
|
90
81
|
"devDependencies": {
|
|
91
82
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
92
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.
|
|
93
|
-
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.
|
|
94
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.
|
|
95
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.
|
|
83
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.7-rc.1",
|
|
84
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-rc.1",
|
|
85
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.7-rc.1",
|
|
86
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.7-rc.1",
|
|
96
87
|
"@eslint/js": "^10.0.1",
|
|
97
88
|
"@types/node": "^22.0.0",
|
|
98
89
|
"@types/react": "~18.3.1",
|
|
@@ -151,5 +142,16 @@
|
|
|
151
142
|
],
|
|
152
143
|
"platform": "web"
|
|
153
144
|
}
|
|
145
|
+
},
|
|
146
|
+
"peerDependenciesMeta": {
|
|
147
|
+
"@deepseek-ai/dsh-client-locale": {
|
|
148
|
+
"optional": true
|
|
149
|
+
},
|
|
150
|
+
"@deepseek-ai/dsh-client-ui-renderer": {
|
|
151
|
+
"optional": true
|
|
152
|
+
},
|
|
153
|
+
"@deepseek-ai/dsh-client-ui-settings": {
|
|
154
|
+
"optional": true
|
|
155
|
+
}
|
|
154
156
|
}
|
|
155
157
|
}
|