dsh-perm-gate 5.2.0 → 5.3.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/CHANGELOG.ja.md +54 -1
- package/CHANGELOG.ko.md +54 -1
- package/CHANGELOG.md +134 -4
- package/README.ja.md +43 -0
- package/README.ko.md +43 -1
- package/README.md +73 -0
- package/README.zh.md +41 -0
- package/cordis.patch.yml +16 -1
- package/dsh.plugin.json +1 -1
- package/lib/ask-context.d.ts +49 -1
- package/lib/ask-context.js +89 -5
- package/lib/client.js +714 -86
- package/lib/client.js.map +1 -1
- package/lib/config.d.ts +152 -1
- package/lib/config.js +20 -1
- package/lib/credential-paths.d.ts +35 -0
- package/lib/credential-paths.js +119 -0
- package/lib/engine.d.ts +4 -6
- package/lib/engine.js +53 -42
- package/lib/evaluate.d.ts +20 -0
- package/lib/evaluate.js +34 -11
- package/lib/feedback.d.ts +42 -0
- package/lib/feedback.js +85 -0
- package/lib/index.d.ts +2 -2
- package/lib/index.js +119 -17
- package/lib/irreversible.js +109 -1
- package/lib/path.d.ts +31 -0
- package/lib/path.js +35 -0
- package/lib/preset.d.ts +8 -5
- package/lib/preset.js +15 -6
- package/lib/risk.d.ts +29 -1
- package/lib/risk.js +36 -2
- package/lib/runtime.d.ts +164 -0
- package/lib/runtime.js +423 -54
- package/lib/scan-text.d.ts +69 -0
- package/lib/scan-text.js +162 -0
- package/lib/script-review.d.ts +50 -0
- package/lib/script-review.js +202 -0
- package/lib/shell.d.ts +18 -0
- package/lib/shell.js +103 -1
- package/lib/unattended.d.ts +61 -0
- package/lib/unattended.js +77 -0
- package/package.json +1 -1
- package/scripts/patch-permission-glyph.mjs +27 -13
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unattended tiers — which human-free implementations exist, and which permission preset
|
|
3
|
+
* each one owns.
|
|
4
|
+
*
|
|
5
|
+
* The shape, and why it is shaped this way: a mode is a property of the preset the user
|
|
6
|
+
* picks in the composer, never a background flow the gate adds to a session it was not
|
|
7
|
+
* asked to change. So each implementation has exactly one preset and exactly one switch
|
|
8
|
+
* that says whether it exists at all — one switch, one implementation, one tier. They do
|
|
9
|
+
* not cross: turning `safenetPass` off cannot make 自动审查 behave differently, and
|
|
10
|
+
* selecting `permissive-mech` does nothing unless that implementation is switched on.
|
|
11
|
+
*
|
|
12
|
+
* | switch | preset | who settles an ask | model |
|
|
13
|
+
* | ------------ | ----------------- | ---------------------------------------------------- | ----- |
|
|
14
|
+
* | `permissive` | permissive(-full) | machine screen + grader refine, then a PERSON | yes |
|
|
15
|
+
* | `safenetAsk` | permissive-snet | machine screen, then a PERSON — no grader at all | no |
|
|
16
|
+
* | `safenetPass`| permissive-mech | the machine screen alone: named danger blocked+fed | no |
|
|
17
|
+
* | `yolo` | permissive-yolo | the grader answers in place of the person, always | yes |
|
|
18
|
+
*
|
|
19
|
+
* `yolo` is the model-backed tier and says so: with no reviewer in seat it is NOT silently
|
|
20
|
+
* a pass-through. The caller still has to decide what a missing grader means (see
|
|
21
|
+
* {@link YOLO_WITHOUT_REVIEWER}) but it must record that the tier could not do the thing it
|
|
22
|
+
* advertises, and the settings face must say so out loud.
|
|
23
|
+
*
|
|
24
|
+
* Everything here is pure string/enum work so the browser half can reuse the same truth.
|
|
25
|
+
*/
|
|
26
|
+
/** The three human-free implementations, named by what they do with an ask. */
|
|
27
|
+
export type UnattendedMode = 'safety-net-ask' | 'safety-net-passthrough' | 'yolo';
|
|
28
|
+
/** The preset each implementation owns. One switch, one preset — they never cross. */
|
|
29
|
+
export declare const MODE_PRESETS: Readonly<Record<UnattendedMode, string>>;
|
|
30
|
+
/** Every accepted mode, in the order the settings face should offer them. */
|
|
31
|
+
export declare const UNATTENDED_MODES: readonly UnattendedMode[];
|
|
32
|
+
/** The switches that decide whether each implementation exists. */
|
|
33
|
+
export interface UnattendedSwitches {
|
|
34
|
+
readonly safenetAsk?: boolean;
|
|
35
|
+
readonly safenetPass?: boolean;
|
|
36
|
+
readonly yolo?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The mode for one call, from the session's current permission preset plus which
|
|
40
|
+
* implementations are switched on.
|
|
41
|
+
*
|
|
42
|
+
* `null` means no human-free implementation owns this preset: the ordinary flow runs, and
|
|
43
|
+
* an ask goes to a person. A preset whose switch is off also returns `null` — the tier may
|
|
44
|
+
* still exist in the picker (profiles are not rewritten here), it simply has no
|
|
45
|
+
* implementation behind it, which is the honest reading of "the switch controls whether the
|
|
46
|
+
* implementation exists".
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolveTierMode(preset: string | undefined, switches: UnattendedSwitches): UnattendedMode | null;
|
|
49
|
+
/**
|
|
50
|
+
* Whether a mode takes the human out of the loop. `safety-net-ask` keeps them in — it
|
|
51
|
+
* exists to grade without a model, not to stop asking — so the decision seam only branches
|
|
52
|
+
* on the other two, and `safety-net-ask` instead suppresses the grader refine pass.
|
|
53
|
+
*/
|
|
54
|
+
export declare function isHumanFree(mode: UnattendedMode): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* What YOLO does when its grader is not in seat. It stays human-free (that is the tier's
|
|
57
|
+
* promise), settles on the mechanical screen alone, and the caller records that the model
|
|
58
|
+
* never answered — a tier that quietly becomes a different tier is the failure mode this
|
|
59
|
+
* whole module exists to avoid.
|
|
60
|
+
*/
|
|
61
|
+
export declare const YOLO_WITHOUT_REVIEWER: "settle-mechanically-and-label";
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unattended tiers — which human-free implementations exist, and which permission preset
|
|
3
|
+
* each one owns.
|
|
4
|
+
*
|
|
5
|
+
* The shape, and why it is shaped this way: a mode is a property of the preset the user
|
|
6
|
+
* picks in the composer, never a background flow the gate adds to a session it was not
|
|
7
|
+
* asked to change. So each implementation has exactly one preset and exactly one switch
|
|
8
|
+
* that says whether it exists at all — one switch, one implementation, one tier. They do
|
|
9
|
+
* not cross: turning `safenetPass` off cannot make 自动审查 behave differently, and
|
|
10
|
+
* selecting `permissive-mech` does nothing unless that implementation is switched on.
|
|
11
|
+
*
|
|
12
|
+
* | switch | preset | who settles an ask | model |
|
|
13
|
+
* | ------------ | ----------------- | ---------------------------------------------------- | ----- |
|
|
14
|
+
* | `permissive` | permissive(-full) | machine screen + grader refine, then a PERSON | yes |
|
|
15
|
+
* | `safenetAsk` | permissive-snet | machine screen, then a PERSON — no grader at all | no |
|
|
16
|
+
* | `safenetPass`| permissive-mech | the machine screen alone: named danger blocked+fed | no |
|
|
17
|
+
* | `yolo` | permissive-yolo | the grader answers in place of the person, always | yes |
|
|
18
|
+
*
|
|
19
|
+
* `yolo` is the model-backed tier and says so: with no reviewer in seat it is NOT silently
|
|
20
|
+
* a pass-through. The caller still has to decide what a missing grader means (see
|
|
21
|
+
* {@link YOLO_WITHOUT_REVIEWER}) but it must record that the tier could not do the thing it
|
|
22
|
+
* advertises, and the settings face must say so out loud.
|
|
23
|
+
*
|
|
24
|
+
* Everything here is pure string/enum work so the browser half can reuse the same truth.
|
|
25
|
+
*/
|
|
26
|
+
/** The preset each implementation owns. One switch, one preset — they never cross. */
|
|
27
|
+
export const MODE_PRESETS = {
|
|
28
|
+
'safety-net-ask': 'permissive-snet',
|
|
29
|
+
'safety-net-passthrough': 'permissive-mech',
|
|
30
|
+
yolo: 'permissive-yolo',
|
|
31
|
+
};
|
|
32
|
+
/** Every accepted mode, in the order the settings face should offer them. */
|
|
33
|
+
export const UNATTENDED_MODES = [
|
|
34
|
+
'safety-net-ask',
|
|
35
|
+
'safety-net-passthrough',
|
|
36
|
+
'yolo',
|
|
37
|
+
];
|
|
38
|
+
/**
|
|
39
|
+
* The mode for one call, from the session's current permission preset plus which
|
|
40
|
+
* implementations are switched on.
|
|
41
|
+
*
|
|
42
|
+
* `null` means no human-free implementation owns this preset: the ordinary flow runs, and
|
|
43
|
+
* an ask goes to a person. A preset whose switch is off also returns `null` — the tier may
|
|
44
|
+
* still exist in the picker (profiles are not rewritten here), it simply has no
|
|
45
|
+
* implementation behind it, which is the honest reading of "the switch controls whether the
|
|
46
|
+
* implementation exists".
|
|
47
|
+
*/
|
|
48
|
+
export function resolveTierMode(preset, switches) {
|
|
49
|
+
if (preset === undefined)
|
|
50
|
+
return null;
|
|
51
|
+
for (const mode of UNATTENDED_MODES) {
|
|
52
|
+
if (MODE_PRESETS[mode] !== preset)
|
|
53
|
+
continue;
|
|
54
|
+
const on = mode === 'safety-net-ask'
|
|
55
|
+
? switches.safenetAsk === true
|
|
56
|
+
: mode === 'safety-net-passthrough'
|
|
57
|
+
? switches.safenetPass === true
|
|
58
|
+
: switches.yolo === true;
|
|
59
|
+
return on ? mode : null;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Whether a mode takes the human out of the loop. `safety-net-ask` keeps them in — it
|
|
65
|
+
* exists to grade without a model, not to stop asking — so the decision seam only branches
|
|
66
|
+
* on the other two, and `safety-net-ask` instead suppresses the grader refine pass.
|
|
67
|
+
*/
|
|
68
|
+
export function isHumanFree(mode) {
|
|
69
|
+
return mode === 'safety-net-passthrough' || mode === 'yolo';
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* What YOLO does when its grader is not in seat. It stays human-free (that is the tier's
|
|
73
|
+
* promise), settles on the mechanical screen alone, and the caller records that the model
|
|
74
|
+
* never answered — a tier that quietly becomes a different tier is the failure mode this
|
|
75
|
+
* whole module exists to avoid.
|
|
76
|
+
*/
|
|
77
|
+
export const YOLO_WITHOUT_REVIEWER = 'settle-mechanically-and-label';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Add
|
|
3
|
+
* Add the plugin's own permission tiers to the DSH composer's permission-glyph map.
|
|
4
4
|
*
|
|
5
5
|
* WHY THIS EXISTS
|
|
6
6
|
* ---------------
|
|
@@ -52,9 +52,11 @@ import { homedir } from 'node:os'
|
|
|
52
52
|
import { dirname, join, resolve } from 'node:path'
|
|
53
53
|
import { pathToFileURL } from 'node:url'
|
|
54
54
|
|
|
55
|
-
/** The map key whose entry is cloned, and
|
|
55
|
+
/** The map key whose entry is cloned, and every key it must be cloned onto. */
|
|
56
56
|
export const SOURCE_KEY = 'permissive'
|
|
57
|
-
export const
|
|
57
|
+
export const TARGET_KEYS = ['permissive-full', 'permissive-snet', 'permissive-mech', 'permissive-yolo']
|
|
58
|
+
/** The first target, kept as a named export because the spec and the docs quote it. */
|
|
59
|
+
export const TARGET_KEY = TARGET_KEYS[0]
|
|
58
60
|
|
|
59
61
|
/** Relative path from a package root to the bundle holding the map. */
|
|
60
62
|
const BUNDLE = join('@deepseek-ai', 'dsh-client-ui-conversation', 'lib', 'client.js')
|
|
@@ -117,20 +119,31 @@ export function sliceEntry(source, key) {
|
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
/**
|
|
120
|
-
* Clone the `permissive` glyph entry onto
|
|
122
|
+
* Clone the `permissive` glyph entry onto every plugin-contributed tier that lacks one.
|
|
123
|
+
*
|
|
124
|
+
* The composer renders "outside the design set get no icon" for any key the closed map does
|
|
125
|
+
* not hold, so all four of this plugin's tiers would otherwise read as bare text next to the
|
|
126
|
+
* built-in shields. Each missing key gets a byte-identical clone under its own name; keys
|
|
127
|
+
* already present are left alone, so re-running is a no-op.
|
|
121
128
|
*
|
|
122
129
|
* @param {string} source the bundle's text
|
|
123
|
-
* @returns {{ changed: boolean, source: string, entry: string | null }}
|
|
124
|
-
* `changed: false` means
|
|
130
|
+
* @returns {{ changed: boolean, source: string, entry: string | null, added: string[] }}
|
|
131
|
+
* `changed: false` means every target key was already present (idempotent no-op).
|
|
125
132
|
*/
|
|
126
133
|
export function applyGlyphPatch(source) {
|
|
127
|
-
|
|
134
|
+
const missing = TARGET_KEYS.filter((key) => !source.includes(`["${key}",`))
|
|
135
|
+
if (missing.length === 0) return { changed: false, source, entry: null, added: [] }
|
|
128
136
|
const sourceEntry = sliceEntry(source, SOURCE_KEY)
|
|
129
137
|
if (!sourceEntry.includes('svg')) {
|
|
130
138
|
throw new GlyphPatchError(`the ["${SOURCE_KEY}",] entry does not look like a glyph`)
|
|
131
139
|
}
|
|
132
|
-
const
|
|
133
|
-
return {
|
|
140
|
+
const clones = missing.map((key) => sourceEntry.replace(`["${SOURCE_KEY}",`, `["${key}",`))
|
|
141
|
+
return {
|
|
142
|
+
changed: true,
|
|
143
|
+
source: source.replace(sourceEntry, [sourceEntry, ...clones].join(',\n\t\t\t')),
|
|
144
|
+
entry: clones[0] ?? null,
|
|
145
|
+
added: missing,
|
|
146
|
+
}
|
|
134
147
|
}
|
|
135
148
|
|
|
136
149
|
/** Resolve the bundle to patch, or throw with an explanation. */
|
|
@@ -161,15 +174,16 @@ function main(argv) {
|
|
|
161
174
|
|
|
162
175
|
const bundle = findBundle(argv, where)
|
|
163
176
|
const original = readFileSync(bundle, 'utf8')
|
|
164
|
-
const { changed, source: patched } = applyGlyphPatch(original)
|
|
177
|
+
const { changed, source: patched, added } = applyGlyphPatch(original)
|
|
178
|
+
const listed = added.length > 0 ? added.join('", ["') : TARGET_KEYS.join('", ["')
|
|
165
179
|
|
|
166
180
|
if (!changed) {
|
|
167
|
-
process.stdout.write(`patch-permission-glyph: ["${
|
|
181
|
+
process.stdout.write(`patch-permission-glyph: ["${TARGET_KEYS.join('", ["')}",] already present in\n ${bundle}\n`)
|
|
168
182
|
return 0
|
|
169
183
|
}
|
|
170
184
|
if (check) {
|
|
171
185
|
process.stdout.write(
|
|
172
|
-
`patch-permission-glyph: ["${
|
|
186
|
+
`patch-permission-glyph: ["${listed}",] is MISSING from\n ${bundle}\n` +
|
|
173
187
|
' (--check: nothing written; re-run without --check to apply)\n',
|
|
174
188
|
)
|
|
175
189
|
return 1
|
|
@@ -189,7 +203,7 @@ function main(argv) {
|
|
|
189
203
|
}
|
|
190
204
|
|
|
191
205
|
process.stdout.write(
|
|
192
|
-
`patch-permission-glyph: added ["${
|
|
206
|
+
`patch-permission-glyph: added ["${listed}",] to\n ${bundle}\n` +
|
|
193
207
|
` backup: ${backup}\n` +
|
|
194
208
|
' NOTE: this edits a DSH package — re-run after any DSH upgrade or reinstall.\n',
|
|
195
209
|
)
|