dsh-perm-gate 5.0.4 → 5.1.2-compat.7
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 -2
- package/CHANGELOG.ko.md +0 -2
- package/CHANGELOG.md +4 -2
- package/INSTALL.ja.md +0 -15
- package/INSTALL.ko.md +0 -15
- package/INSTALL.md +0 -15
- package/INSTALL.zh.md +0 -15
- package/README.ja.md +6 -18
- package/README.ko.md +6 -18
- package/README.md +40 -19
- package/README.zh.md +34 -18
- package/assets/appcontainer-launcher.cs +703 -0
- package/dsh.plugin.json +2 -2
- package/lib/appcontainer/compile.d.ts +17 -0
- package/lib/appcontainer/compile.js +74 -0
- package/lib/appcontainer/exempt.d.ts +18 -0
- package/lib/appcontainer/exempt.js +71 -0
- package/lib/appcontainer/index.d.ts +85 -0
- package/lib/appcontainer/index.js +286 -0
- package/lib/appcontainer/probe.d.ts +44 -0
- package/lib/appcontainer/probe.js +66 -0
- package/lib/appcontainer/sid.d.ts +23 -0
- package/lib/appcontainer/sid.js +33 -0
- package/lib/appcontainer/wrap.d.ts +22 -0
- package/lib/appcontainer/wrap.js +40 -0
- package/lib/bridge.d.ts +34 -0
- package/lib/bridge.js +125 -0
- package/lib/client.js +1663 -1107
- package/lib/client.js.map +1 -1
- package/lib/config.d.ts +41 -1
- package/lib/config.js +6 -0
- package/lib/debug-generation.d.ts +21 -0
- package/lib/debug-generation.js +44 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.js +157 -7
- package/lib/runtime.d.ts +22 -0
- package/lib/runtime.js +24 -0
- package/locale/de.json +1 -0
- package/locale/en.json +1 -0
- package/locale/es.json +1 -0
- package/locale/fr.json +1 -0
- package/locale/it.json +1 -0
- package/locale/ja.json +1 -0
- package/locale/ko.json +1 -0
- package/locale/ru.json +1 -0
- package/locale/zh.json +1 -0
- package/package.json +25 -14
- package/README.de.md +0 -487
- package/README.es.md +0 -489
- package/README.fr.md +0 -498
- package/README.it.md +0 -490
- package/README.ru.md +0 -478
package/lib/config.d.ts
CHANGED
|
@@ -191,6 +191,38 @@ export interface PermGateConfig {
|
|
|
191
191
|
* Default 1800000 (30 min).
|
|
192
192
|
*/
|
|
193
193
|
readonly networkGrantTtlMs?: number;
|
|
194
|
+
/**
|
|
195
|
+
* Run gate-allowed shell commands inside a Windows AppContainer. The
|
|
196
|
+
* container token carries NO network capability, so non-loopback egress is
|
|
197
|
+
* denied by Windows Firewall (WFP) itself; the container SID is derived
|
|
198
|
+
* deterministically so the loopback exemption is a one-time step. Default
|
|
199
|
+
* false — enabling changes how allowed commands execute.
|
|
200
|
+
*/
|
|
201
|
+
readonly sandboxEnabled?: boolean;
|
|
202
|
+
/**
|
|
203
|
+
* Filesystem posture inside the container: 'workspace-write' grants the
|
|
204
|
+
* workspace Modify + temp FullControl; 'read-only' grants RX only.
|
|
205
|
+
* System directories stay readable through the stock ALL APPLICATION
|
|
206
|
+
* PACKAGES ACEs. Default 'workspace-write'.
|
|
207
|
+
*/
|
|
208
|
+
readonly sandboxMode?: 'read-only' | 'workspace-write';
|
|
209
|
+
/**
|
|
210
|
+
* THE proxy parameter: the URL injected as HTTP(S)_PROXY / ALL_PROXY into
|
|
211
|
+
* sandboxed processes. Empty (default) follows the builtin filtered proxy
|
|
212
|
+
* (its actual bound 127.0.0.1 port) when networkEnabled; an explicit value
|
|
213
|
+
* must be loopback-bound (the container cannot reach anything else) and the
|
|
214
|
+
* destination filtering is that proxy's job. Neither set → no proxy env;
|
|
215
|
+
* direct egress stays WFP-blocked either way.
|
|
216
|
+
*/
|
|
217
|
+
readonly sandboxProxy?: string;
|
|
218
|
+
/**
|
|
219
|
+
* Attempt the one-time `CheckNetIsolation LoopbackExempt -a -p=<sid>` for
|
|
220
|
+
* the container SID so sandboxed processes can reach 127.0.0.1 (the proxy
|
|
221
|
+
* and the host webServer). Denied without elevation → warning with the
|
|
222
|
+
* exact command; the sandbox still runs, only loopback stays closed.
|
|
223
|
+
* Default 'exempt'.
|
|
224
|
+
*/
|
|
225
|
+
readonly sandboxLoopback?: 'exempt' | 'off';
|
|
194
226
|
/** @deprecated Rules now live in the `dsh-perm-gate-rules` settings namespace; file watching was removed. Accepted (ignored) for composition compatibility. */
|
|
195
227
|
readonly watch?: boolean;
|
|
196
228
|
/** @deprecated See {@link watch}. */
|
|
@@ -349,6 +381,10 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
349
381
|
networkInjectEnv: z<boolean, boolean, "volatile-defined">;
|
|
350
382
|
networkAskTimeoutMs: z<number, number, "volatile-defined">;
|
|
351
383
|
networkGrantTtlMs: z<number, number, "volatile-defined">;
|
|
384
|
+
sandboxEnabled: z<boolean, boolean, "volatile-defined">;
|
|
385
|
+
sandboxMode: z<"read-only" | "workspace-write", "read-only" | "workspace-write", "volatile-defined">;
|
|
386
|
+
sandboxProxy: z<string, string, "volatile-defined">;
|
|
387
|
+
sandboxLoopback: z<"exempt" | "off", "exempt" | "off", "volatile-defined">;
|
|
352
388
|
rules: z<NoInfer<RulesConfig>, NoInfer<RulesConfig>, "volatile">;
|
|
353
389
|
watch: z<boolean, boolean, "defined">;
|
|
354
390
|
watchDebounceMs: z<number, number, "defined">;
|
|
@@ -404,6 +440,10 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
404
440
|
networkInjectEnv: z<boolean, boolean, "volatile-defined">;
|
|
405
441
|
networkAskTimeoutMs: z<number, number, "volatile-defined">;
|
|
406
442
|
networkGrantTtlMs: z<number, number, "volatile-defined">;
|
|
443
|
+
sandboxEnabled: z<boolean, boolean, "volatile-defined">;
|
|
444
|
+
sandboxMode: z<"read-only" | "workspace-write", "read-only" | "workspace-write", "volatile-defined">;
|
|
445
|
+
sandboxProxy: z<string, string, "volatile-defined">;
|
|
446
|
+
sandboxLoopback: z<"exempt" | "off", "exempt" | "off", "volatile-defined">;
|
|
407
447
|
rules: z<NoInfer<RulesConfig>, NoInfer<RulesConfig>, "volatile">;
|
|
408
448
|
watch: z<boolean, boolean, "defined">;
|
|
409
449
|
watchDebounceMs: z<number, number, "defined">;
|
|
@@ -411,7 +451,7 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
411
451
|
/** Live reference the 0.1.7 loader hands `apply` for `.volatile()` config fields. */
|
|
412
452
|
export type VolatileRef<T> = Volatile<T>;
|
|
413
453
|
/** The config fields marked `.volatile()` — live refs inside `apply`'s config. */
|
|
414
|
-
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"];
|
|
454
|
+
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", "sandboxEnabled", "sandboxMode", "sandboxProxy", "sandboxLoopback"];
|
|
415
455
|
/** Resolve one possibly-volatile field: a live ref on 0.1.7+, a plain value otherwise. */
|
|
416
456
|
export declare function readVolatileValue<T>(value: T | Volatile<T> | undefined): VolatileSnapshot<T> | undefined;
|
|
417
457
|
/** Shallow-resolve every volatile field into a plain snapshot (one per read). */
|
package/lib/config.js
CHANGED
|
@@ -199,6 +199,11 @@ export const Config = z.object({
|
|
|
199
199
|
networkInjectEnv: z.boolean().default(true).volatile(),
|
|
200
200
|
networkAskTimeoutMs: z.number().min(1000).max(600_000).default(120_000).volatile(),
|
|
201
201
|
networkGrantTtlMs: z.number().min(0).max(24 * 60 * 60_000).default(30 * 60_000).volatile(),
|
|
202
|
+
// AppContainer sandbox (Windows; goal 2)
|
|
203
|
+
sandboxEnabled: z.boolean().default(false).volatile(),
|
|
204
|
+
sandboxMode: z.union(['read-only', 'workspace-write']).default('workspace-write').volatile(),
|
|
205
|
+
sandboxProxy: z.string().default('').volatile(),
|
|
206
|
+
sandboxLoopback: z.union(['exempt', 'off']).default('exempt').volatile(),
|
|
202
207
|
// 0.1.7: the rules document lives on this entry as a volatile whole-object
|
|
203
208
|
// field (a second settings namespace is no longer projectable). Unconfigured
|
|
204
209
|
// (bare defaults) -> the rules file remains the source.
|
|
@@ -217,6 +222,7 @@ export const VOLATILE_CONFIG_KEYS = [
|
|
|
217
222
|
'networkEnabled', 'networkMode', 'networkUnlisted', 'networkUnattributed', 'networkLoopback',
|
|
218
223
|
'networkBind', 'networkPort', 'networkNoProxy', 'networkInjectEnv', 'networkAskTimeoutMs',
|
|
219
224
|
'networkGrantTtlMs', 'rules',
|
|
225
|
+
'sandboxEnabled', 'sandboxMode', 'sandboxProxy', 'sandboxLoopback',
|
|
220
226
|
];
|
|
221
227
|
/** Resolve one possibly-volatile field: a live ref on 0.1.7+, a plain value otherwise. */
|
|
222
228
|
export function readVolatileValue(value) {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* debug-generation — T10b served 集合诊断端点(临时,上线前删,spec served 七步第 2 步)。
|
|
3
|
+
*
|
|
4
|
+
* GET /api/dsh-perm-gate/debug-generation
|
|
5
|
+
* 返回插件持有的 settings 服务实例自检:全量 describe 的 ns 列表 + 方法面。
|
|
6
|
+
* 判读:ns 列表若同时含 dsh-perm-gate / dsh-session-guard / dsh-free-search-settings
|
|
7
|
+
* 等其他插件命名空间 → 持有的是宿主共享单例(identitySame=TRUE,H3 孤儿假设死);
|
|
8
|
+
* 若只有自家 ns → 孤儿实例坐实(H3 成立)。
|
|
9
|
+
*/
|
|
10
|
+
export interface SettingsServiceLike {
|
|
11
|
+
update(ns: string, patch: object): Promise<void>;
|
|
12
|
+
register?(ns: string, schema: unknown, options?: {
|
|
13
|
+
base?: unknown;
|
|
14
|
+
}): unknown;
|
|
15
|
+
installSection?(owner: unknown, ns: string, schema: unknown, entry: unknown, hooks: unknown): unknown;
|
|
16
|
+
describe?(): Array<{
|
|
17
|
+
ns: unknown;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
export declare const DEBUG_GENERATION_ROUTE = "/api/dsh-perm-gate/debug-generation";
|
|
21
|
+
export declare function registerDebugGenerationRoute(server: unknown, getSettingsSvc: () => SettingsServiceLike | undefined): (() => void) | undefined;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const DEBUG_GENERATION_ROUTE = '/api/dsh-perm-gate/debug-generation';
|
|
2
|
+
export function registerDebugGenerationRoute(server, getSettingsSvc) {
|
|
3
|
+
if (typeof server !== 'object' || server === null)
|
|
4
|
+
return undefined;
|
|
5
|
+
const ws = typeof server.register === 'function' ? server : undefined;
|
|
6
|
+
if (ws === undefined || typeof ws.register !== 'function')
|
|
7
|
+
return undefined;
|
|
8
|
+
return ws.register({
|
|
9
|
+
kind: 'exact',
|
|
10
|
+
path: DEBUG_GENERATION_ROUTE,
|
|
11
|
+
handler: (rawReq, rawRes) => {
|
|
12
|
+
const req = rawReq;
|
|
13
|
+
const res = rawRes;
|
|
14
|
+
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
15
|
+
res.writeHead(405);
|
|
16
|
+
res.end();
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const svc = getSettingsSvc();
|
|
20
|
+
let namespaces = [];
|
|
21
|
+
let describeError;
|
|
22
|
+
if (svc !== undefined && typeof svc.describe === 'function') {
|
|
23
|
+
try {
|
|
24
|
+
const descriptors = svc.describe();
|
|
25
|
+
namespaces = descriptors.map((d) => String(d.ns));
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
describeError = e instanceof Error ? e.message : String(e);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
32
|
+
res.end(JSON.stringify({
|
|
33
|
+
hasSvc: svc !== undefined,
|
|
34
|
+
describeError,
|
|
35
|
+
namespaces,
|
|
36
|
+
methods: svc === undefined ? null : {
|
|
37
|
+
register: typeof svc.register === 'function',
|
|
38
|
+
installSection: typeof svc.installSection === 'function',
|
|
39
|
+
update: typeof svc.update === 'function',
|
|
40
|
+
},
|
|
41
|
+
}, null, 2));
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
package/lib/index.d.ts
CHANGED
|
@@ -53,5 +53,5 @@ export declare function makeApprovalAnswerer(runtime: Pick<PermGateRuntime, 'set
|
|
|
53
53
|
* `riskTimeoutMs` (default 20 s), and a grader failure keeps the original ask
|
|
54
54
|
* (fail-closed).
|
|
55
55
|
*/
|
|
56
|
-
export declare function makePreExecuteListener(runtime: Pick<PermGateRuntime, 'decideExecution' | 'refineAsk' | 'beginShellExecution'>): (exec: ToolExecutionLike, next: () => Promise<unknown>) => Promise<unknown>;
|
|
56
|
+
export declare function makePreExecuteListener(runtime: Pick<PermGateRuntime, 'decideExecution' | 'refineAsk' | 'beginShellExecution'> & Partial<Pick<PermGateRuntime, 'sandboxRewrite'>>): (exec: ToolExecutionLike, next: () => Promise<unknown>) => Promise<unknown>;
|
|
57
57
|
export declare function apply(ctx: Context, config?: Record<string, unknown>): PermGateRuntime;
|
package/lib/index.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { SandboxController } from './appcontainer/index.js';
|
|
4
|
+
import { Config, ensureDataDir, readRulesFromSettings, readVolatileValue, resolveVolatileConfig, resolveConfig, resolveDshHome, resolveDataDir, resolveGatePresets, resolvePermissiveStrategies, resolveRulesFile } from './config.js';
|
|
4
5
|
import { appendAllowToSettings, readRulesFileDoc, replaceAllowInSettings } from './allowlist.js';
|
|
5
6
|
import { runDryRun } from './dry-run.js';
|
|
6
7
|
import { registerDryRunRoute, registerEventsRoute, registerHealthRoute, registerLearningRoute, registerNetworkRoute, registerReceiverRoute, registerReviewRoutes, registerRulesRoute } from './events.js';
|
|
8
|
+
// T12 设置数据桥(describe/mutate;client BridgeDocHandle 消费)
|
|
9
|
+
import { registerSettingsBridgeRoutes } from './bridge.js';
|
|
7
10
|
import { buildReceiverInfo } from './receiver-info.js';
|
|
8
11
|
import { readRulesView, readRulesViewFromSettings } from './rules-view.js';
|
|
9
12
|
import { PermGateRuntime } from './runtime.js';
|
|
@@ -142,6 +145,22 @@ export function makePreExecuteListener(runtime) {
|
|
|
142
145
|
// The call proceeds: register it so a network connection made by its
|
|
143
146
|
// child process can be attributed back to this session.
|
|
144
147
|
runtime.beginShellExecution(exec);
|
|
148
|
+
// AppContainer sandbox (goal 2): rewrite the allowed shell call into
|
|
149
|
+
// the launcher invocation, or fail closed. The host executes THIS exec
|
|
150
|
+
// object after the waterfall resolves, so the in-place arguments swap
|
|
151
|
+
// is the sanctioned seam; the base64 wrapper keeps every pipe/&&
|
|
152
|
+
// inside the container (outer shell sees a plain exe call).
|
|
153
|
+
if (typeof runtime.sandboxRewrite === 'function') {
|
|
154
|
+
const rewrite = await runtime.sandboxRewrite(exec);
|
|
155
|
+
if (rewrite !== undefined) {
|
|
156
|
+
if (rewrite.kind === 'deny')
|
|
157
|
+
return { kind: 'deny', reason: rewrite.reason };
|
|
158
|
+
exec.arguments = {
|
|
159
|
+
...exec.arguments,
|
|
160
|
+
command: rewrite.command,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
145
164
|
return next();
|
|
146
165
|
}
|
|
147
166
|
if (decision.kind !== 'ask')
|
|
@@ -171,7 +190,7 @@ export function makePreExecuteListener(runtime) {
|
|
|
171
190
|
export function apply(ctx, config = {}) {
|
|
172
191
|
// Runtime-adjustable config: 0.1.7 hands `.volatile()` fields to apply() as
|
|
173
192
|
// live refs; `current()` shallow-resolves one snapshot per read.
|
|
174
|
-
|
|
193
|
+
let current = () => resolveVolatileConfig(config);
|
|
175
194
|
// ─── Rules (volatile `rules` field on this entry) ─────────────────────────
|
|
176
195
|
// The rules document is a `.volatile()` whole-object field of the plugin's
|
|
177
196
|
// own entry (0.1.7 has no second projectable namespace). Unconfigured
|
|
@@ -190,17 +209,104 @@ export function apply(ctx, config = {}) {
|
|
|
190
209
|
ctx.inject(['settings'], (child) => {
|
|
191
210
|
const svc = child.settings;
|
|
192
211
|
settingsRef.svc = svc;
|
|
212
|
+
if (svc === undefined)
|
|
213
|
+
return;
|
|
214
|
+
// setSource 换绑运行时读取源:source 可能是 () => values(≤0.1.5 scope.get)
|
|
215
|
+
// 或普通对象;base 与运行时读分用两个 resolve(手册 §P1-3)。
|
|
216
|
+
const rebindFrom = (source) => {
|
|
217
|
+
current = () => resolveVolatileConfig((typeof source === 'function' ? source() : (source ?? config)));
|
|
218
|
+
};
|
|
219
|
+
const seedDoc = () => ({ ...readRulesFileDoc(rulesFilePath), initialized: true });
|
|
220
|
+
const seedUpdate = () => svc.update(ENTRY_ID, { rules: seedDoc() });
|
|
221
|
+
// F4 加固(spec served 段判「无论如何无条件做」):宿主 installSection 在
|
|
222
|
+
// register 之后同步调 setSource(dsh-settings 0.1.5 lib/index.js:332),换绑
|
|
223
|
+
// 或 onChange 抛错会从 installSection 一路抛穿插件加载、连带注册失败——
|
|
224
|
+
// 这里兜住异常保住注册(卡派发依赖 ns∈served,注册比换绑成败更金贵)。
|
|
225
|
+
const guardedSetSource = (source) => {
|
|
226
|
+
try {
|
|
227
|
+
rebindFrom(source);
|
|
228
|
+
hooks.onChange();
|
|
229
|
+
}
|
|
230
|
+
catch (e) {
|
|
231
|
+
console.warn('[dsh-perm-gate] setSource rebind failed (registration kept):', e);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
const hooks = {
|
|
235
|
+
setSource: guardedSetSource,
|
|
236
|
+
onChange: () => { applyVolatileUpdate(); },
|
|
237
|
+
};
|
|
193
238
|
// Activation-time seed: a never-configured `rules` field is populated once
|
|
194
239
|
// — from the rules file when one exists (implicit migration), otherwise a
|
|
195
240
|
// bare starter document — and marked `initialized`, so the panel shows a
|
|
196
241
|
// live rules document from the first boot instead of a missing-file
|
|
197
242
|
// fallback. The field watcher below picks the seed up via reload.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
243
|
+
// ── 三代 waist:三分支执行体互斥(P1-1/P1-2),register-retry 仅存活于
|
|
244
|
+
// 兜底分支且在 rc.1+ 永不命中;rc.1+ 不引入 configure(P1-5)。
|
|
245
|
+
if (typeof svc.installSection === 'function') {
|
|
246
|
+
console.warn('[dsh-perm-gate] settings generation = service.installSection (0.1.2/0.1.5 line)');
|
|
247
|
+
svc.installSection(ctx, ENTRY_ID, Config, resolveConfig(config), {
|
|
248
|
+
setSource: guardedSetSource,
|
|
249
|
+
onChange: () => { applyVolatileUpdate(); },
|
|
202
250
|
});
|
|
251
|
+
if (readRulesDocument() === undefined) {
|
|
252
|
+
void seedUpdate().catch((e) => {
|
|
253
|
+
console.warn('[dsh-perm-gate] legacy seed update failed:', e);
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
return;
|
|
203
257
|
}
|
|
258
|
+
void (async () => {
|
|
259
|
+
try {
|
|
260
|
+
// 变量说明符:dsh-settings 现为 optional peer(T35,2026-10-07——dependencies
|
|
261
|
+
// 硬钉 0.1.1-rc.2 已删:六线运行时它本来就无效——0.1.5 分支互斥不加载、老线
|
|
262
|
+
// nested 链接悬空后动态 import 经 node 向上解析落宿主自身副本,见
|
|
263
|
+
// cross-version-adaptation-all/findings.md T10b 附录)。变量形式让 TS 按 any
|
|
264
|
+
// 解析不做模块查证;另注意 ctx.get 不跨插件隔离上下文(index.ts:44),
|
|
265
|
+
// legacy 分支只能动态 import,不能 ctx.get 直取宿主单例。
|
|
266
|
+
const legacySpecifier = '@deepseek-ai/dsh-settings';
|
|
267
|
+
const legacy = (await import(legacySpecifier));
|
|
268
|
+
if (typeof legacy.installSettingsSection === 'function' && typeof legacy.settingsNamespace === 'function') {
|
|
269
|
+
console.warn('[dsh-perm-gate] settings generation = module.installSettingsSection (0.1.0/0.1.1 line)');
|
|
270
|
+
legacy.installSettingsSection(ctx, legacy.settingsNamespace(ENTRY_ID), Config, resolveConfig(config), {
|
|
271
|
+
setSource: (source) => {
|
|
272
|
+
rebindFrom(source);
|
|
273
|
+
hooks.onChange();
|
|
274
|
+
},
|
|
275
|
+
onChange: () => { applyVolatileUpdate(); },
|
|
276
|
+
});
|
|
277
|
+
if (readRulesDocument() === undefined) {
|
|
278
|
+
await seedUpdate().catch((e) => {
|
|
279
|
+
console.warn('[dsh-perm-gate] legacy seed update failed:', e);
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
// 模块级 legacy API 缺失/导入失败 → 落到 rc.1 兜底
|
|
287
|
+
}
|
|
288
|
+
console.warn('[dsh-perm-gate] settings generation = rc1.loader (rc.1+: loader 自动注册 ns,update 直接成功;register-retry 兜底永不命中)');
|
|
289
|
+
if (readRulesDocument() === undefined) {
|
|
290
|
+
void (async () => {
|
|
291
|
+
try {
|
|
292
|
+
await seedUpdate();
|
|
293
|
+
}
|
|
294
|
+
catch (e) {
|
|
295
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
296
|
+
// register-retry 兜底:rc.1+ 上 ns 已被 loader 占用,永不命中
|
|
297
|
+
if (!/not registered/i.test(message) || typeof svc.register !== 'function') {
|
|
298
|
+
throw e;
|
|
299
|
+
}
|
|
300
|
+
console.warn(`[dsh-perm-gate] legacy fallback: registering settings namespace ${ENTRY_ID}`);
|
|
301
|
+
svc.register(ENTRY_ID, Config, { base: { rules: seedDoc() } });
|
|
302
|
+
await seedUpdate();
|
|
303
|
+
console.warn('[dsh-perm-gate] legacy fallback registered + seeded');
|
|
304
|
+
}
|
|
305
|
+
})().catch((e) => {
|
|
306
|
+
console.warn('[dsh-perm-gate] rules seed write failed:', e);
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
})();
|
|
204
310
|
});
|
|
205
311
|
const settingsWrite = (write) => {
|
|
206
312
|
const svc = settingsRef.svc;
|
|
@@ -461,7 +567,7 @@ export function apply(ctx, config = {}) {
|
|
|
461
567
|
// The host no longer seeds settings (there is no write path): an empty
|
|
462
568
|
// `allowlist` keeps the rules-file default via `runtime.allowlist()`.
|
|
463
569
|
let lastRulesDocKey;
|
|
464
|
-
|
|
570
|
+
function applyVolatileUpdate() {
|
|
465
571
|
const next = asSurface(current());
|
|
466
572
|
if (next !== undefined) {
|
|
467
573
|
if (Array.isArray(next.allowlist)) {
|
|
@@ -489,6 +595,10 @@ export function apply(ctx, config = {}) {
|
|
|
489
595
|
if (changed)
|
|
490
596
|
runtime.reload();
|
|
491
597
|
}
|
|
598
|
+
}
|
|
599
|
+
;
|
|
600
|
+
ctx.on('loader/volatile-update', () => {
|
|
601
|
+
applyVolatileUpdate();
|
|
492
602
|
});
|
|
493
603
|
// Event feed HTTP API (best effort): the dsh webServer service exposes the
|
|
494
604
|
// JSONL decision events to the browser half; without it events stay on disk.
|
|
@@ -529,6 +639,10 @@ export function apply(ctx, config = {}) {
|
|
|
529
639
|
const offHealth = registerHealthRoute(webServer, { check: () => runtime.healthCheck() });
|
|
530
640
|
if (offHealth !== undefined)
|
|
531
641
|
ctx.effect(() => () => { offHealth(); }, 'dsh-perm-gate: health route');
|
|
642
|
+
// T12: 设置数据桥(client 卡在 settingsScope 不解析的线上由此取数/写回)
|
|
643
|
+
for (const offBridge of registerSettingsBridgeRoutes(webServer, () => settingsRef.svc, ENTRY_ID)) {
|
|
644
|
+
ctx.effect(() => () => { offBridge(); }, 'dsh-perm-gate: settings bridge route');
|
|
645
|
+
}
|
|
532
646
|
// Network diagnostics: mode, bind, port, proxy liveness, env injection,
|
|
533
647
|
// block counters and recent blocks. Read-only.
|
|
534
648
|
const offNetwork = registerNetworkRoute(webServer, {
|
|
@@ -709,6 +823,42 @@ export function apply(ctx, config = {}) {
|
|
|
709
823
|
networkRefs.snapshot = () => networkLifecycle.snapshot();
|
|
710
824
|
if (networkRefs.appliedKey === undefined)
|
|
711
825
|
networkRefs.appliedKey = networkKeyOf(current());
|
|
826
|
+
// ─── AppContainer sandbox (goal 2, Windows) ─────────────────────────
|
|
827
|
+
// The gate-aware rewrite hook: an ALLOWED shell call is rewritten into the
|
|
828
|
+
// launcher invocation (or denied, fail-closed). Settings are read per call
|
|
829
|
+
// so the card's switches apply without a reload; the controller memoizes
|
|
830
|
+
// the launcher compile + loopback exemption.
|
|
831
|
+
const readSandboxSettings = () => {
|
|
832
|
+
const live = current();
|
|
833
|
+
const str = (key, fallback) => typeof live[key] === 'string' && live[key] !== '' ? live[key] : fallback;
|
|
834
|
+
const bool = (key, fallback) => typeof live[key] === 'boolean' ? live[key] : fallback;
|
|
835
|
+
return {
|
|
836
|
+
enabled: bool('sandboxEnabled', false),
|
|
837
|
+
mode: str('sandboxMode', 'workspace-write'),
|
|
838
|
+
proxy: str('sandboxProxy', ''),
|
|
839
|
+
loopback: str('sandboxLoopback', 'exempt'),
|
|
840
|
+
};
|
|
841
|
+
};
|
|
842
|
+
const sandboxController = new SandboxController({
|
|
843
|
+
cwdRoot: () => process.cwd(),
|
|
844
|
+
builtinProxyPort: () => {
|
|
845
|
+
try {
|
|
846
|
+
const snap = networkLifecycle.snapshot();
|
|
847
|
+
return snap.proxyActive === true && typeof snap.port === 'number' ? snap.port : undefined;
|
|
848
|
+
}
|
|
849
|
+
catch {
|
|
850
|
+
return undefined;
|
|
851
|
+
}
|
|
852
|
+
},
|
|
853
|
+
log: (message) => loggerWarn(message),
|
|
854
|
+
});
|
|
855
|
+
runtime.setSandboxHook((exec) => sandboxController.wrap(readSandboxSettings(), exec));
|
|
856
|
+
// The sandbox is Windows-only: on any other platform an enabled switch is a
|
|
857
|
+
// misconfiguration — warn once and treat it as off (wrap() also guards).
|
|
858
|
+
if (readSandboxSettings().enabled && process.platform !== 'win32') {
|
|
859
|
+
loggerWarn('[dsh-perm-gate] sandbox: sandboxEnabled is set but this host is not Windows — the AppContainer sandbox stays OFF');
|
|
860
|
+
}
|
|
861
|
+
ctx.effect(() => () => { void sandboxController.dispose(); }, 'dsh-perm-gate: sandbox scratch cleanup');
|
|
712
862
|
// ─── Hot reload watcher (Phase 3, T3.6) ────────────────────────────
|
|
713
863
|
const watchEnabled = typeof config.watch === 'boolean' ? config.watch : true;
|
|
714
864
|
if (watchEnabled) {
|
package/lib/runtime.d.ts
CHANGED
|
@@ -12,6 +12,14 @@ export interface PreToolDecisionLike {
|
|
|
12
12
|
kind: 'deny' | 'ask';
|
|
13
13
|
reason: string;
|
|
14
14
|
}
|
|
15
|
+
/** The sandbox hook's verdict for one allowed shell call (appcontainer seam). */
|
|
16
|
+
export type SandboxRewriteOutcome = {
|
|
17
|
+
readonly kind: 'wrap';
|
|
18
|
+
readonly command: string;
|
|
19
|
+
} | {
|
|
20
|
+
readonly kind: 'deny';
|
|
21
|
+
readonly reason: string;
|
|
22
|
+
};
|
|
15
23
|
/**
|
|
16
24
|
* The P2 rule chain's own verdict for one call, as reported by
|
|
17
25
|
* {@link PermGateRuntime.explainRules}. Distinct from the gate's effective
|
|
@@ -349,6 +357,20 @@ export declare class PermGateRuntime {
|
|
|
349
357
|
private auditNetworkAsk;
|
|
350
358
|
/** The compiled ruleset (read-only view for network module). */
|
|
351
359
|
get compiledRuleset(): CompiledRuleset;
|
|
360
|
+
/**
|
|
361
|
+
* The sandbox hook installed by the plugin: gate-aware, fail-closed rewrite
|
|
362
|
+
* of an allowed shell call. Undefined → the feature is off entirely.
|
|
363
|
+
*/
|
|
364
|
+
private sandboxHook;
|
|
365
|
+
/** Install/clear the sandbox rewrite hook (plugin wiring). */
|
|
366
|
+
setSandboxHook(hook: ((exec: ToolExecutionLike) => Promise<SandboxRewriteOutcome | undefined>) | undefined): void;
|
|
367
|
+
/**
|
|
368
|
+
* Gate-aware sandbox rewrite for the pre-execute waterfall's allow path.
|
|
369
|
+
* Mirrors {@link decideExecution}'s stand-down: outside the gate's presets
|
|
370
|
+
* the sandbox must not silently confine either — same scope, same fence.
|
|
371
|
+
* Returns undefined when not applicable.
|
|
372
|
+
*/
|
|
373
|
+
sandboxRewrite(exec: ToolExecutionLike): Promise<SandboxRewriteOutcome | undefined>;
|
|
352
374
|
private liveRiskLearning;
|
|
353
375
|
private liveRiskSediment;
|
|
354
376
|
private liveClassifySource;
|
package/lib/runtime.js
CHANGED
|
@@ -581,6 +581,30 @@ export class PermGateRuntime {
|
|
|
581
581
|
get compiledRuleset() {
|
|
582
582
|
return this.ruleset;
|
|
583
583
|
}
|
|
584
|
+
// ─── AppContainer sandbox seam (goal 2) ─────────────────────────────
|
|
585
|
+
/**
|
|
586
|
+
* The sandbox hook installed by the plugin: gate-aware, fail-closed rewrite
|
|
587
|
+
* of an allowed shell call. Undefined → the feature is off entirely.
|
|
588
|
+
*/
|
|
589
|
+
sandboxHook;
|
|
590
|
+
/** Install/clear the sandbox rewrite hook (plugin wiring). */
|
|
591
|
+
setSandboxHook(hook) {
|
|
592
|
+
this.sandboxHook = hook;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Gate-aware sandbox rewrite for the pre-execute waterfall's allow path.
|
|
596
|
+
* Mirrors {@link decideExecution}'s stand-down: outside the gate's presets
|
|
597
|
+
* the sandbox must not silently confine either — same scope, same fence.
|
|
598
|
+
* Returns undefined when not applicable.
|
|
599
|
+
*/
|
|
600
|
+
async sandboxRewrite(exec) {
|
|
601
|
+
const hook = this.sandboxHook;
|
|
602
|
+
if (hook === undefined)
|
|
603
|
+
return undefined;
|
|
604
|
+
if (!this.gateActive(this.presetOf(exec)))
|
|
605
|
+
return undefined;
|
|
606
|
+
return hook(exec);
|
|
607
|
+
}
|
|
584
608
|
liveRiskLearning() {
|
|
585
609
|
const read = this.options.readRiskLearning;
|
|
586
610
|
if (read !== undefined)
|
package/locale/de.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "meta": { "title": "Berechtigungsschleuse", "description": "Deterministisch-zuerst, fehlgeschlossene Berechtigungsschleuse für DeepSeek Harness: P0 harte Ablehnung → P1 Sitzungsfreigabe → P2 statische Regelkette → P3 optionaler LLM-Semantik-Klassifikator → P4 manuelle Bestätigung, mit Befehls-Whitelist und -Blacklist." } }
|
package/locale/en.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "meta": { "title": "Permission Gate", "description": "Deterministic-first, fail-closed permission gate for DeepSeek Harness: P0 hard-deny → P1 session grant → P2 static rules → P3 LLM classifier → P4 ask, with command whitelist and blacklist." } }
|
package/locale/es.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "meta": { "title": "Puerta de permisos", "description": "Puerta de permisos determinista y de cierre seguro para DeepSeek Harness: P0 denegación estricta → P1 concesión de sesión → P2 cadena de reglas estáticas → P3 clasificador semántico LLM opcional → P4 confirmación manual, con lista blanca y lista negra de comandos." } }
|
package/locale/fr.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "meta": { "title": "Passerelle de permissions", "description": "Passerelle de permissions déterministe et à échec fermé pour DeepSeek Harness : P0 refus strict → P1 autorisation de session → P2 chaîne de règles statiques → P3 classifieur sémantique LLM optionnel → P4 confirmation manuelle, avec liste blanche et liste noire de commandes." } }
|
package/locale/it.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "meta": { "title": "Gateway dei permessi", "description": "Gateway dei permessi deterministico e fail-closed per DeepSeek Harness: P0 rifiuto rigido → P1 concessione di sessione → P2 catena di regole statiche → P3 classificatore semantico LLM opzionale → P4 conferma manuale, con whitelist e blacklist dei comandi." } }
|
package/locale/ja.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "meta": { "title": "権限ゲート", "description": "DeepSeek Harness 用の決定論優先・フェイルクローズド権限ゲート:P0 ハード拒否 → P1 セッション許可 → P2 静的ルールチェーン → P3 オプション LLM セマンティック分類器 → P4 人手確認、コマンドホワイトリスト/ブラックリスト付き。" } }
|
package/locale/ko.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "meta": { "title": "권한 게이트", "description": "DeepSeek Harness용 결정론 우선·실패 시 차단 권한 게이트: P0 하드 거부 → P1 세션 승인 → P2 정적 규칙 체인 → P3 선택적 LLM 시맨틱 분류기 → P4 수동 확인, 명령 화이트리스트/블랙리스트 포함." } }
|
package/locale/ru.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "meta": { "title": "Шлюз разрешений", "description": "Детерминированный шлюз разрешений с закрытием при сбое для DeepSeek Harness: P0 жёсткий запрет → P1 сессионное разрешение → P2 цепочка статических правил → P3 необязательный LLM-семантический классификатор → P4 ручной запрос, с белым и чёрным списком команд." } }
|
package/locale/zh.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "meta": { "title": "权限门禁", "description": "面向 DeepSeek Harness 的确定性优先、fail-closed 权限门禁:P0 硬拒绝 → P1 会话授权 → P2 静态规则链 → P3 可选 LLM 语义分类器 → P4 人工确认,含命令白名单与黑名单。" } }
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-perm-gate",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.2-compat.7",
|
|
4
4
|
"publishConfig": {
|
|
5
|
-
"tag": "
|
|
5
|
+
"tag": "compat"
|
|
6
6
|
},
|
|
7
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.",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"files": [
|
|
10
10
|
"lib",
|
|
11
|
+
"locale",
|
|
11
12
|
"scripts/patch-permission-glyph.mjs",
|
|
12
13
|
"cordis.patch.yml",
|
|
13
14
|
"dsh.plugin.json",
|
|
@@ -57,6 +58,7 @@
|
|
|
57
58
|
"./client": {
|
|
58
59
|
"default": "./lib/client.js"
|
|
59
60
|
},
|
|
61
|
+
"./locale/*.json": "./locale/*.json",
|
|
60
62
|
"./package.json": "./package.json"
|
|
61
63
|
},
|
|
62
64
|
"bin": {
|
|
@@ -65,13 +67,15 @@
|
|
|
65
67
|
},
|
|
66
68
|
"engines": {
|
|
67
69
|
"node": ">=24",
|
|
68
|
-
"dsh": "
|
|
70
|
+
"dsh": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2"
|
|
69
71
|
},
|
|
70
72
|
"peerDependencies": {
|
|
71
73
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
72
|
-
"@deepseek-ai/dsh-client-locale": "
|
|
73
|
-
"@deepseek-ai/dsh-client-ui-renderer": "
|
|
74
|
-
"@deepseek-ai/dsh-client-ui-settings": "
|
|
74
|
+
"@deepseek-ai/dsh-client-locale": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
75
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
76
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
77
|
+
"@deepseek-ai/dsh-settings": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
78
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2"
|
|
75
79
|
},
|
|
76
80
|
"dependencies": {
|
|
77
81
|
"@deepseek-ai/schemastery": "^3.18.4",
|
|
@@ -80,10 +84,10 @@
|
|
|
80
84
|
},
|
|
81
85
|
"devDependencies": {
|
|
82
86
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
83
|
-
"@deepseek-ai/dsh-client-locale": "0.2.0-rc.
|
|
84
|
-
"@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.
|
|
85
|
-
"@deepseek-ai/dsh-client-ui-settings": "0.2.0-rc.
|
|
86
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.
|
|
87
|
+
"@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
|
|
88
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
|
|
89
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.2.0-rc.2",
|
|
90
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
|
|
87
91
|
"@eslint/js": "^10.0.1",
|
|
88
92
|
"@types/node": "^22.0.0",
|
|
89
93
|
"@types/react": "~18.3.1",
|
|
@@ -92,24 +96,25 @@
|
|
|
92
96
|
"tsdown": "^0.22.14",
|
|
93
97
|
"typescript": "^5.6.0",
|
|
94
98
|
"typescript-eslint": "^8.69.0",
|
|
95
|
-
"vitest": "^3.0.0"
|
|
99
|
+
"vitest": "^3.0.0",
|
|
100
|
+
"@deepseek-ai/dsh-settings": "0.1.1-rc.2"
|
|
96
101
|
},
|
|
97
102
|
"scripts": {
|
|
98
103
|
"build": "tsc -p tsconfig.build.json && tsdown",
|
|
104
|
+
"prepack": "npm run build",
|
|
99
105
|
"prepublishOnly": "npm run build",
|
|
100
106
|
"typecheck": "tsc --noEmit && tsc -p tsconfig.client.json --noEmit",
|
|
101
107
|
"lint": "eslint src test scripts tsdown.config.ts eslint.config.mjs",
|
|
102
108
|
"test": "vitest run",
|
|
103
109
|
"patch:glyph": "node scripts/patch-permission-glyph.mjs",
|
|
104
110
|
"patch:glyph:check": "node scripts/patch-permission-glyph.mjs --check",
|
|
105
|
-
"verify:line": "node scripts/verify-line.mjs
|
|
111
|
+
"verify:line": "node scripts/verify-line.mjs new",
|
|
106
112
|
"verify:lines": "node scripts/verify-line.mjs both",
|
|
107
113
|
"check:line": "node scripts/sync-0.1.5-line.mjs check",
|
|
108
114
|
"release:latest": "npm publish --tag latest",
|
|
109
115
|
"release:2x": "npm publish --tag 2.x",
|
|
110
116
|
"release:legacy": "npm publish --tag legacy",
|
|
111
|
-
"release:3x": "npm publish --tag dsh-0.1.5"
|
|
112
|
-
"release:5x": "npm publish --tag dsh-0.2.0"
|
|
117
|
+
"release:3x": "npm publish --tag dsh-0.1.5"
|
|
113
118
|
},
|
|
114
119
|
"license": "MIT",
|
|
115
120
|
"repository": {
|
|
@@ -153,6 +158,12 @@
|
|
|
153
158
|
},
|
|
154
159
|
"@deepseek-ai/dsh-client-ui-settings": {
|
|
155
160
|
"optional": true
|
|
161
|
+
},
|
|
162
|
+
"@deepseek-ai/dsh-client-ui-conversation": {
|
|
163
|
+
"optional": true
|
|
164
|
+
},
|
|
165
|
+
"@deepseek-ai/dsh-settings": {
|
|
166
|
+
"optional": true
|
|
156
167
|
}
|
|
157
168
|
}
|
|
158
169
|
}
|