dsh-codex-approval 0.3.0 → 0.4.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/lib/client.js ADDED
@@ -0,0 +1,429 @@
1
+ window.__ModuleLoader__.load({ id: "dsh-codex-approval", factory: (require) => { var module = { exports: {} }; var exports = module.exports;
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/client/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ apply: () => apply,
34
+ inject: () => inject
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+
38
+ // src/client/DshCodexApprovalCard.tsx
39
+ var import_react = __toESM(require("react"), 1);
40
+ var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
41
+
42
+ // client-model-picker.js
43
+ var MAX_FALLBACKS = 4;
44
+ function optionKey(provider, model) {
45
+ return `${provider}\0${model}`;
46
+ }
47
+ function buildModelOptions(catalog, fallbackModels2 = []) {
48
+ const groups = Array.isArray(catalog?.groups) ? catalog.groups : [];
49
+ const failures = new Map((Array.isArray(catalog?.failures) ? catalog.failures : []).map((failure) => [failure.id, failure.message]));
50
+ const routable = Array.isArray(catalog?.routableProviders) ? new Set(catalog.routableProviders) : void 0;
51
+ const options = [];
52
+ for (const group of groups) {
53
+ if (group === null || typeof group !== "object") continue;
54
+ const failure = failures.get(group.id);
55
+ const available = failure === void 0 && (routable === void 0 || routable.has(group.id));
56
+ for (const model of Array.isArray(group.models) ? group.models : []) {
57
+ if (model === null || typeof model !== "object") continue;
58
+ options.push({
59
+ provider: group.id,
60
+ model: model.id,
61
+ label: `${group.name ?? group.id} / ${model.name ?? model.id}`,
62
+ available,
63
+ ...failure === void 0 ? {} : { note: failure }
64
+ });
65
+ }
66
+ }
67
+ if (options.length === 0) {
68
+ for (const item of fallbackModels2) {
69
+ options.push({ provider: item.provider, model: item.model, label: `${item.provider} / ${item.model}`, available: true });
70
+ }
71
+ }
72
+ return options;
73
+ }
74
+ function splitByAvailability(options) {
75
+ return {
76
+ available: options.filter((option) => option.available),
77
+ unavailable: options.filter((option) => !option.available)
78
+ };
79
+ }
80
+ function findOption(options, provider, model) {
81
+ if (provider === void 0 || model === void 0) return void 0;
82
+ return options.find((option) => option.provider === provider && option.model === model);
83
+ }
84
+ function readChain(settings, max = MAX_FALLBACKS) {
85
+ if (!Array.isArray(settings)) return [];
86
+ const chain = [];
87
+ for (const entry of settings) {
88
+ if (entry === null || typeof entry !== "object") continue;
89
+ if (typeof entry.provider !== "string" || entry.provider === "") continue;
90
+ if (typeof entry.model !== "string" || entry.model === "") continue;
91
+ chain.push({ provider: entry.provider, model: entry.model });
92
+ if (chain.length === max) break;
93
+ }
94
+ return chain;
95
+ }
96
+ function validateChain(chain, primary) {
97
+ if (chain.length > MAX_FALLBACKS) return `\u515C\u5E95\u6700\u591A ${MAX_FALLBACKS} \u9879`;
98
+ const seen = /* @__PURE__ */ new Set();
99
+ if (primary?.provider !== void 0 && primary?.model !== void 0) seen.add(optionKey(primary.provider, primary.model));
100
+ for (const entry of chain) {
101
+ if (typeof entry?.provider !== "string" || entry.provider === "" || typeof entry?.model !== "string" || entry.model === "") {
102
+ return "\u6BCF\u4E2A\u515C\u5E95\u6761\u76EE\u90FD\u8981\u9009 provider \u4E0E model";
103
+ }
104
+ const key = optionKey(entry.provider, entry.model);
105
+ if (seen.has(key)) return `\u91CD\u590D\u7684\u5019\u9009\uFF1A${entry.provider} / ${entry.model}`;
106
+ seen.add(key);
107
+ }
108
+ return "";
109
+ }
110
+ function buildChainSummary(primary, chain) {
111
+ const parts = [];
112
+ if (primary?.provider !== void 0 && primary?.model !== void 0) parts.push(`${primary.provider} / ${primary.model}`);
113
+ for (const entry of chain) {
114
+ const label = `${entry.provider} / ${entry.model}`;
115
+ if (!parts.includes(label)) parts.push(label);
116
+ }
117
+ return parts;
118
+ }
119
+
120
+ // src/client/DshCodexApprovalCard.tsx
121
+ var fallbackModels = [
122
+ { provider: "cpa-wx301", model: "command/deepseek/deepseek-v4.1-flash" },
123
+ { provider: "deepseek-official", model: "deepseek-flash" }
124
+ ];
125
+ var NUL = "\0";
126
+ var TOLERANCES = [
127
+ { value: "low", label: "low \xB7 \u5C3D\u91CF\u653E\u884C" },
128
+ { value: "medium", label: "medium \xB7 \u5E73\u8861\uFF08\u9ED8\u8BA4\uFF09" },
129
+ { value: "high", label: "high \xB7 \u5C3D\u91CF\u8BE2\u95EE" }
130
+ ];
131
+ var FAIL_OPEN = [
132
+ { value: "ask", label: "ask \xB7 \u4EA4\u7ED9\u4EBA\u786E\u8BA4\uFF08\u9ED8\u8BA4\uFF09" },
133
+ { value: "deny", label: "deny \xB7 \u62D2\u7EDD" },
134
+ { value: "allow", label: "allow \xB7 \u653E\u884C" }
135
+ ];
136
+ var MODE3_ON_ASK = [
137
+ { value: "deny", label: "deny \xB7 \u62D2\u7EDD\uFF08\u9ED8\u8BA4\uFF09" },
138
+ { value: "allow", label: "allow \xB7 \u653E\u884C" }
139
+ ];
140
+ function Field({ label, hint, children }) {
141
+ return /* @__PURE__ */ import_react.default.createElement("div", { className: "dsh-ca-field" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dsh-ca-fieldHead" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "dsh-ca-label" }, label)), children, hint !== void 0 ? /* @__PURE__ */ import_react.default.createElement("p", { className: "dsh-ca-hint" }, hint) : null);
142
+ }
143
+ function ModelSelect({ options, provider, model, disabled, onChange }) {
144
+ const current = findOption(options, provider, model);
145
+ const { available, unavailable } = splitByAvailability(options);
146
+ const value = current ? optionKey(current.provider, current.model) : optionKey(String(provider ?? ""), String(model ?? ""));
147
+ const renderOption = (item) => /* @__PURE__ */ import_react.default.createElement("option", { key: optionKey(item.provider, item.model), value: optionKey(item.provider, item.model) }, item.available ? item.label : `\u26A0 ${item.label}`);
148
+ return /* @__PURE__ */ import_react.default.createElement("select", { className: "dsh-ca-select", value, disabled, onChange: (event) => {
149
+ const [nextProvider, nextModel] = event.target.value.split(NUL);
150
+ onChange(nextProvider, nextModel);
151
+ } }, current === void 0 ? /* @__PURE__ */ import_react.default.createElement("option", { value }, `${String(provider ?? "")} / ${String(model ?? "")}\uFF08\u4E0D\u5728\u6A21\u578B\u76EE\u5F55\u4E2D\uFF09`) : null, /* @__PURE__ */ import_react.default.createElement("optgroup", { label: "\u53EF\u7528" }, available.map(renderOption)), unavailable.length > 0 ? /* @__PURE__ */ import_react.default.createElement("optgroup", { label: "\u4E0D\u53EF\u7528\uFF08\u6E20\u9053\u5931\u8D25\u6216\u672A\u8DEF\u7531\uFF09" }, unavailable.map(renderOption)) : null);
152
+ }
153
+ function DshCodexApprovalCard({ settingsScope, loadModelCatalog }) {
154
+ const [snapshot, setSnapshot] = (0, import_react.useState)(() => settingsScope.getSnapshot());
155
+ const [catalog, setCatalog] = (0, import_react.useState)(null);
156
+ const [draft, setDraft] = (0, import_react.useState)({});
157
+ const [message, setMessage] = (0, import_react.useState)("");
158
+ const [open, setOpen] = (0, import_react.useState)(false);
159
+ const [saving, setSaving] = (0, import_react.useState)(false);
160
+ (0, import_react.useEffect)(() => settingsScope.subscribe(() => setSnapshot(() => settingsScope.getSnapshot())), [settingsScope]);
161
+ (0, import_react.useEffect)(() => {
162
+ if (loadModelCatalog === void 0) return;
163
+ void loadModelCatalog().then((result) => {
164
+ if (result?.ok) setCatalog(result.value);
165
+ }).catch(() => void 0);
166
+ }, [loadModelCatalog]);
167
+ const value = { ...snapshot.value ?? {}, ...draft };
168
+ const options = (0, import_react.useMemo)(() => buildModelOptions(catalog, fallbackModels), [catalog]);
169
+ const chain = (0, import_react.useMemo)(() => readChain(draft.fallbacks ?? value.fallbacks), [draft.fallbacks, value.fallbacks]);
170
+ const chainError = validateChain(chain, { provider: value.provider, model: value.model });
171
+ const dirty = Object.keys(draft).length > 0;
172
+ const writable = snapshot.writable !== false;
173
+ const disabled = !writable || saving;
174
+ const primaryFailure = catalog?.failures?.find((item) => item.id === value.provider);
175
+ const primaryRoutable = catalog?.routableProviders === void 0 ? true : catalog.routableProviders.includes(String(value.provider ?? ""));
176
+ const judgeOrder = buildChainSummary({ provider: value.provider, model: value.model }, chain);
177
+ const setField = (field, next) => setDraft((current) => ({ ...current, [field]: next }));
178
+ const setChain = (next) => setField("fallbacks", next);
179
+ const updateChainAt = (index, provider, model) => {
180
+ setChain(chain.map((entry, i) => i === index ? { provider, model } : entry));
181
+ };
182
+ const moveChain = (index, delta) => {
183
+ const target = index + delta;
184
+ if (target < 0 || target >= chain.length) return;
185
+ const next = [...chain];
186
+ const [entry] = next.splice(index, 1);
187
+ next.splice(target, 0, entry);
188
+ setChain(next);
189
+ };
190
+ const addChain = () => {
191
+ const used = new Set(judgeOrder);
192
+ const candidate = options.find((item) => item.available && !used.has(`${item.provider} / ${item.model}`));
193
+ setChain([...chain, candidate === void 0 ? { provider: "", model: "" } : { provider: candidate.provider, model: candidate.model }]);
194
+ };
195
+ const save = async () => {
196
+ if (chainError !== "") {
197
+ setMessage(`\u65E0\u6CD5\u4FDD\u5B58\uFF1A${chainError}`);
198
+ return;
199
+ }
200
+ setSaving(true);
201
+ try {
202
+ const ops = Object.entries(draft).map(([path, next]) => ({ op: "set", path: [path], value: next }));
203
+ if (ops.length > 0) await settingsScope.mutate(ops, snapshot.revision);
204
+ setDraft({});
205
+ setMessage("\u5DF2\u4FDD\u5B58");
206
+ } catch (error) {
207
+ setMessage(`\u4FDD\u5B58\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`);
208
+ } finally {
209
+ setSaving(false);
210
+ }
211
+ };
212
+ const discard = () => {
213
+ setDraft({});
214
+ setMessage("");
215
+ };
216
+ if (snapshot.status === "loading") {
217
+ return /* @__PURE__ */ import_react.default.createElement("li", { className: "dsh-ca-card" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dsh-ca-header" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "dsh-ca-headText" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "dsh-ca-name" }, "\u5BA1\u6279\u6A21\u578B"), /* @__PURE__ */ import_react.default.createElement("span", { className: "dsh-ca-description" }, "\u6B63\u5728\u8BFB\u53D6\u914D\u7F6E\u2026"))));
218
+ }
219
+ return /* @__PURE__ */ import_react.default.createElement("li", { className: open ? "dsh-ca-card dsh-ca-cardOpen" : "dsh-ca-card" }, /* @__PURE__ */ import_react.default.createElement("button", { type: "button", className: "dsh-ca-header", "aria-expanded": open, onClick: () => setOpen(!open) }, /* @__PURE__ */ import_react.default.createElement("span", { className: "dsh-ca-headText" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "dsh-ca-name" }, "\u5BA1\u6279\u6A21\u578B"), /* @__PURE__ */ import_react.default.createElement("span", { className: "dsh-ca-description" }, `AI \u5BA1\u5224\u6A21\u578B\uFF1A\u4E3B\u6A21\u578B\u5931\u8D25\u540E\u4F9D\u6B21\u5C1D\u8BD5\u515C\u5E95\u5019\u9009\uFF08${chain.length === 0 ? "\u672A\u914D\u7F6E\u515C\u5E95" : `${chain.length} \u9879`}\uFF09`)), dirty ? /* @__PURE__ */ import_react.default.createElement(import_dsh_client_ui_primitives.Tag, { tone: "neutral", className: "dsh-ca-pending" }, "\u672A\u4FDD\u5B58") : null, /* @__PURE__ */ import_react.default.createElement(import_dsh_client_ui_primitives.IconChevronDownOutline14, { className: open ? "dsh-ca-chevron dsh-ca-chevronOpen" : "dsh-ca-chevron" })), open ? /* @__PURE__ */ import_react.default.createElement("div", { className: "dsh-ca-body" }, snapshot.status === "unavailable" ? /* @__PURE__ */ import_react.default.createElement("p", { className: "dsh-ca-readOnly" }, "\u5F53\u524D Host \u672A\u66B4\u9732\u5BA1\u6279\u914D\u7F6E namespace\u3002") : null, writable ? null : /* @__PURE__ */ import_react.default.createElement("p", { className: "dsh-ca-readOnly" }, "\u672C\u90E8\u7F72\u7684\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\u3002"), /* @__PURE__ */ import_react.default.createElement(Field, { label: "\u4E3B\u6A21\u578B" }, /* @__PURE__ */ import_react.default.createElement(
220
+ ModelSelect,
221
+ {
222
+ options,
223
+ provider: value.provider,
224
+ model: value.model,
225
+ disabled,
226
+ onChange: (provider, model) => {
227
+ setField("provider", provider);
228
+ setField("model", model);
229
+ }
230
+ }
231
+ ), primaryFailure !== void 0 ? /* @__PURE__ */ import_react.default.createElement("p", { className: "dsh-ca-invalid" }, "\u8BE5 provider \u5F53\u524D\u4E0D\u53EF\u7528\uFF1A", primaryFailure.message) : null, primaryFailure === void 0 && !primaryRoutable ? /* @__PURE__ */ import_react.default.createElement("p", { className: "dsh-ca-invalid" }, "\u8BE5 provider \u5F53\u524D\u4E0D\u53EF\u8DEF\u7531\uFF0C\u8C03\u7528\u4F1A\u76F4\u63A5\u5931\u8D25\u3002") : null), /* @__PURE__ */ import_react.default.createElement(Field, { label: `\u515C\u5E95\u5019\u9009\uFF08\u6309\u987A\u5E8F\u5C1D\u8BD5\uFF0C\u6700\u591A ${MAX_FALLBACKS} \u9879\uFF09` }, chain.length === 0 ? /* @__PURE__ */ import_react.default.createElement("p", { className: "dsh-ca-hint" }, "\u672A\u914D\u7F6E\u515C\u5E95\uFF1A\u4E3B\u6A21\u578B\u5931\u8D25\u65F6\u76F4\u63A5\u8D70\u201CAI \u6545\u969C\u65F6\u201D\u7684\u7B56\u7565\u3002") : null, chain.map((entry, index) => {
232
+ const failure = catalog?.failures?.find((item) => item.id === entry.provider);
233
+ return /* @__PURE__ */ import_react.default.createElement("div", { className: "dsh-ca-row", key: `${index}-${optionKey(entry.provider, entry.model)}` }, /* @__PURE__ */ import_react.default.createElement("span", { className: "dsh-ca-rowIndex" }, index + 1, "."), /* @__PURE__ */ import_react.default.createElement(
234
+ ModelSelect,
235
+ {
236
+ options,
237
+ provider: entry.provider,
238
+ model: entry.model,
239
+ disabled,
240
+ onChange: (provider, model) => updateChainAt(index, provider, model)
241
+ }
242
+ ), failure !== void 0 ? /* @__PURE__ */ import_react.default.createElement("span", { className: "dsh-ca-rowUnavailable" }, "\u4E0D\u53EF\u7528") : null, /* @__PURE__ */ import_react.default.createElement(
243
+ "button",
244
+ {
245
+ type: "button",
246
+ className: "dsh-ca-iconButton",
247
+ title: "\u4E0A\u79FB",
248
+ "aria-label": "\u4E0A\u79FB",
249
+ disabled: disabled || index === 0,
250
+ onClick: () => moveChain(index, -1)
251
+ },
252
+ /* @__PURE__ */ import_react.default.createElement(import_dsh_client_ui_primitives.IconChevronUpOutline14, null)
253
+ ), /* @__PURE__ */ import_react.default.createElement(
254
+ "button",
255
+ {
256
+ type: "button",
257
+ className: "dsh-ca-iconButton",
258
+ title: "\u4E0B\u79FB",
259
+ "aria-label": "\u4E0B\u79FB",
260
+ disabled: disabled || index === chain.length - 1,
261
+ onClick: () => moveChain(index, 1)
262
+ },
263
+ /* @__PURE__ */ import_react.default.createElement(import_dsh_client_ui_primitives.IconChevronDownOutline14, null)
264
+ ), /* @__PURE__ */ import_react.default.createElement(
265
+ "button",
266
+ {
267
+ type: "button",
268
+ className: "dsh-ca-iconButton",
269
+ title: "\u5220\u9664",
270
+ "aria-label": "\u5220\u9664",
271
+ disabled,
272
+ onClick: () => setChain(chain.filter((_, i) => i !== index))
273
+ },
274
+ /* @__PURE__ */ import_react.default.createElement(import_dsh_client_ui_primitives.IconTrashOutline16, null)
275
+ ));
276
+ }), /* @__PURE__ */ import_react.default.createElement("div", { className: "dsh-ca-row" }, /* @__PURE__ */ import_react.default.createElement("button", { type: "button", className: "dsh-ca-ghostButton", disabled: disabled || chain.length >= MAX_FALLBACKS, onClick: addChain }, /* @__PURE__ */ import_react.default.createElement(import_dsh_client_ui_primitives.IconPlusOutline16, null), " \u6DFB\u52A0\u515C\u5E95\u5019\u9009")), chainError !== "" ? /* @__PURE__ */ import_react.default.createElement("p", { className: "dsh-ca-invalid" }, chainError) : null, /* @__PURE__ */ import_react.default.createElement("p", { className: "dsh-ca-order" }, "\u8C03\u7528\u987A\u5E8F\uFF1A", judgeOrder.length === 0 ? "\uFF08\u672A\u9009\u62E9\u6A21\u578B\uFF09" : judgeOrder.join(" \u2192 "))), /* @__PURE__ */ import_react.default.createElement("div", { className: "dsh-ca-grid" }, /* @__PURE__ */ import_react.default.createElement(Field, { label: "\u98CE\u9669\u5BB9\u5FCD\u5EA6" }, /* @__PURE__ */ import_react.default.createElement(
277
+ "select",
278
+ {
279
+ className: "dsh-ca-select",
280
+ value: String(value.riskTolerance ?? "medium"),
281
+ disabled,
282
+ onChange: (event) => setField("riskTolerance", event.target.value)
283
+ },
284
+ TOLERANCES.map((item) => /* @__PURE__ */ import_react.default.createElement("option", { key: item.value, value: item.value }, item.label))
285
+ )), /* @__PURE__ */ import_react.default.createElement(Field, { label: "AI \u6545\u969C\u65F6" }, /* @__PURE__ */ import_react.default.createElement(
286
+ "select",
287
+ {
288
+ className: "dsh-ca-select",
289
+ value: String(value.failOpen ?? "ask"),
290
+ disabled,
291
+ onChange: (event) => setField("failOpen", event.target.value)
292
+ },
293
+ FAIL_OPEN.map((item) => /* @__PURE__ */ import_react.default.createElement("option", { key: item.value, value: item.value }, item.label))
294
+ )), /* @__PURE__ */ import_react.default.createElement(Field, { label: "ai-auto \u6A21\u5F0F\u4E0B\u9047\u5230 ask" }, /* @__PURE__ */ import_react.default.createElement(
295
+ "select",
296
+ {
297
+ className: "dsh-ca-select",
298
+ value: String(value.mode3OnAsk ?? "deny"),
299
+ disabled,
300
+ onChange: (event) => setField("mode3OnAsk", event.target.value)
301
+ },
302
+ MODE3_ON_ASK.map((item) => /* @__PURE__ */ import_react.default.createElement("option", { key: item.value, value: item.value }, item.label))
303
+ )), /* @__PURE__ */ import_react.default.createElement(Field, { label: "\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09", hint: "\u6BCF\u4E2A\u5019\u9009\u5404\u81EA\u8BA1\u65F6\uFF0C\u9ED8\u8BA4 15000" }, /* @__PURE__ */ import_react.default.createElement(
304
+ "input",
305
+ {
306
+ className: "dsh-ca-input",
307
+ type: "number",
308
+ min: "1",
309
+ value: Number(value.timeoutMs ?? 15e3),
310
+ disabled,
311
+ onChange: (event) => setField("timeoutMs", Number(event.target.value))
312
+ }
313
+ )), /* @__PURE__ */ import_react.default.createElement(Field, { label: "\u6700\u5927\u8F93\u51FA token", hint: "\u542B\u601D\u8003 token \u4F59\u91CF\uFF0C\u9ED8\u8BA4 512" }, /* @__PURE__ */ import_react.default.createElement(
314
+ "input",
315
+ {
316
+ className: "dsh-ca-input",
317
+ type: "number",
318
+ min: "1",
319
+ value: Number(value.maxTokens ?? 512),
320
+ disabled,
321
+ onChange: (event) => setField("maxTokens", Number(event.target.value))
322
+ }
323
+ ))), /* @__PURE__ */ import_react.default.createElement("div", { className: "dsh-ca-toggleRow" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "dsh-ca-label" }, "\u62D2\u7EDD\u540E\u5411\u4E3B agent \u6CE8\u5165\u5F52\u56E0\u53CD\u9988"), /* @__PURE__ */ import_react.default.createElement(
324
+ import_dsh_client_ui_primitives.Switch,
325
+ {
326
+ checked: Boolean(value.denyFeedback ?? true),
327
+ label: "\u62D2\u7EDD\u540E\u5411\u4E3B agent \u6CE8\u5165\u5F52\u56E0\u53CD\u9988",
328
+ disabled,
329
+ onChange: (next) => setField("denyFeedback", next)
330
+ }
331
+ )), /* @__PURE__ */ import_react.default.createElement("div", { className: "dsh-ca-footer" }, message !== "" ? /* @__PURE__ */ import_react.default.createElement("p", { className: message.startsWith("\u4FDD\u5B58\u5931\u8D25") || message.startsWith("\u65E0\u6CD5\u4FDD\u5B58") ? "dsh-ca-failed" : "dsh-ca-hint", role: "status" }, message) : null, /* @__PURE__ */ import_react.default.createElement("button", { type: "button", className: "dsh-ca-discard", disabled: !dirty || saving, onClick: discard }, "\u653E\u5F03"), /* @__PURE__ */ import_react.default.createElement("button", { type: "button", className: "dsh-ca-save", disabled: !dirty || chainError !== "" || saving, onClick: () => void save() }, saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58"))) : null);
332
+ }
333
+
334
+ // client-remote.js
335
+ function resolveModelCatalogLoader(scope) {
336
+ const candidates = [
337
+ () => scope?.remote?.session,
338
+ () => scope?.["remote.session"]
339
+ ];
340
+ for (const read of candidates) {
341
+ let namespace;
342
+ try {
343
+ namespace = read();
344
+ } catch {
345
+ continue;
346
+ }
347
+ if (namespace !== void 0 && namespace !== null && typeof namespace.modelCatalog === "function") {
348
+ return () => namespace.modelCatalog();
349
+ }
350
+ }
351
+ return void 0;
352
+ }
353
+
354
+ // client-card-style.js
355
+ var CARD_STYLE_ID = "dsh-codex-approval/card.css";
356
+ var CARD_CSS = `
357
+ .dsh-ca-card{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);border-radius:16px;list-style:none;transition:border-color .16s,background .16s}
358
+ .dsh-ca-card:hover{border-color:var(--dsw-alias-label-dimmed)}
359
+ .dsh-ca-cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}
360
+ .dsh-ca-header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}
361
+ .dsh-ca-header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}
362
+ .dsh-ca-headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}
363
+ .dsh-ca-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}
364
+ .dsh-ca-description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}
365
+ .dsh-ca-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}
366
+ .dsh-ca-chevronOpen{transform:rotate(180deg)}
367
+ .dsh-ca-pending{flex:none}
368
+ .dsh-ca-body{border-top:.5px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}
369
+ .dsh-ca-readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}
370
+ .dsh-ca-field{margin-top:14px;display:flex;flex-direction:column;gap:6px}
371
+ .dsh-ca-fieldHead{align-items:center;gap:8px;display:flex}
372
+ .dsh-ca-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}
373
+ .dsh-ca-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}
374
+ .dsh-ca-invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}
375
+ .dsh-ca-input,.dsh-ca-select{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}
376
+ .dsh-ca-input{width:120px}
377
+ .dsh-ca-select{min-width:0;width:100%}
378
+ .dsh-ca-input:focus-visible,.dsh-ca-select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}
379
+ .dsh-ca-input:disabled,.dsh-ca-select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}
380
+ .dsh-ca-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px 16px;margin-top:4px}
381
+ .dsh-ca-row{align-items:center;gap:8px;display:flex}
382
+ .dsh-ca-rowIndex{color:var(--dsw-alias-label-tertiary);flex:none;min-width:14px;font-size:12px;font-variant-numeric:tabular-nums;line-height:1.5}
383
+ .dsh-ca-rowUnavailable{color:var(--dsw-alias-label-error);flex:none;font-size:12px;line-height:1.5}
384
+ .dsh-ca-iconButton{appearance:none;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:1px solid transparent;border-radius:8px;flex:none;align-items:center;justify-content:center;width:28px;height:28px;display:inline-flex}
385
+ .dsh-ca-iconButton:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}
386
+ .dsh-ca-iconButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}
387
+ .dsh-ca-iconButton:disabled{opacity:.4;cursor:default}
388
+ .dsh-ca-ghostButton{appearance:none;font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;align-items:center;gap:6px;padding:5px 12px;font-size:13px;line-height:1.5;display:inline-flex}
389
+ .dsh-ca-ghostButton:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}
390
+ .dsh-ca-ghostButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}
391
+ .dsh-ca-ghostButton:disabled{opacity:.4;cursor:default}
392
+ .dsh-ca-toggleRow{align-items:center;justify-content:space-between;gap:12px;margin-top:14px;display:flex}
393
+ .dsh-ca-order{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5;overflow-wrap:anywhere}
394
+ .dsh-ca-footer{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}
395
+ .dsh-ca-failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}
396
+ .dsh-ca-discard,.dsh-ca-save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}
397
+ .dsh-ca-discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}
398
+ .dsh-ca-discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}
399
+ .dsh-ca-save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}
400
+ .dsh-ca-discard:disabled,.dsh-ca-save:disabled{opacity:.4;cursor:default}
401
+ .dsh-ca-discard:focus-visible,.dsh-ca-save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}
402
+ `.trim();
403
+ function ensureCardStyle(doc = typeof document === "undefined" ? void 0 : document) {
404
+ if (doc === void 0 || doc === null) return false;
405
+ if (doc.querySelector(`style[data-plugin-css=${JSON.stringify(CARD_STYLE_ID)}]`) !== null) return true;
406
+ const style = doc.createElement("style");
407
+ style.setAttribute("data-plugin-css", CARD_STYLE_ID);
408
+ style.textContent = CARD_CSS;
409
+ doc.head.appendChild(style);
410
+ return true;
411
+ }
412
+
413
+ // src/client/index.ts
414
+ var inject = ["locale", "settingsScope", "slots", "remote", "remote.session"];
415
+ function apply(ctx) {
416
+ ensureCardStyle();
417
+ ctx.inject(["slots", "settingsScope", "locale", "remote", "remote.session"], (scope) => {
418
+ const settingsScope = scope.settingsScope.bind({ namespace: "dsh-codex-approval-config" });
419
+ const loadModelCatalog = resolveModelCatalogLoader(scope);
420
+ scope.slots.inject("settings.plugin.item", () => scope.slots.register({
421
+ name: "settings.plugin.item",
422
+ key: "dsh-codex-approval-config",
423
+ locale: "dsh-codex-approval",
424
+ inject: () => ({ settingsScope, loadModelCatalog })
425
+ }, DshCodexApprovalCard));
426
+ });
427
+ }
428
+
429
+ return module.exports; } });
package/modes.js CHANGED
@@ -1,62 +1,62 @@
1
- /**
2
- * dsh-codex-approval — modes.js
3
- *
4
- * The approval-mode dimension, orthogonal to the dsh sandbox mode:
5
- *
6
- * manual — plugin fully bypassed (next() straight through, no decision,
7
- * no audit): the pre-plugin experience.
8
- * ai — rules, then AI judge, then human fallback for every "ask"
9
- * outcome (the default / v0.1.x behavior).
10
- * ai-auto — rules, then AI judge; "ask" is never routed to a human —
11
- * it resolves through mode3OnAsk (default deny).
12
- *
13
- * Pure functions only: parse/validate mode names, resolve the effective mode
14
- * (per-session override wins over the config default), and map an "ask"
15
- * outcome onto its effective action under the active mode.
16
- */
17
-
18
- /** The three approval modes. */
19
- export const MODES = ["manual", "ai", "ai-auto"];
20
- /** Numeric aliases mirroring the user-facing 1/2/3 choice. */
21
- export const MODE_ALIASES = { "1": "manual", "2": "ai", "3": "ai-auto" };
22
- /** Actions an "ask" may resolve to. */
23
- export const ASK_ACTIONS = ["ask", "deny", "allow"];
24
-
25
- /**
26
- * Parse and validate a mode name (or numeric alias).
27
- * @param input - "manual" | "ai" | "ai-auto" | "1" | "2" | "3"
28
- * @returns the canonical mode name, or null when invalid.
29
- */
30
- export function parseMode(input) {
31
- if (typeof input !== "string") return null;
32
- const trimmed = input.trim().toLowerCase();
33
- if (MODES.includes(trimmed)) return trimmed;
34
- if (MODE_ALIASES[trimmed] !== void 0) return MODE_ALIASES[trimmed];
35
- return null;
36
- }
37
-
38
- /**
39
- * Resolve the effective mode for one request: per-session override wins,
40
- * else the config default.
41
- * @param sessionOverride - mode from the per-session store (or undefined)
42
- * @param configDefault - the configured default mode
43
- * @returns a canonical mode name (never null when configDefault is valid).
44
- */
45
- export function resolveMode(sessionOverride, configDefault) {
46
- return parseMode(sessionOverride) ?? parseMode(configDefault) ?? "ai";
47
- }
48
-
49
- /**
50
- * Map an "ask" outcome onto its effective action under the active mode.
51
- * - manual: unreachable (handler bypasses); defensive "ask".
52
- * - ai: "ask" — route to the human (next()).
53
- * - ai-auto: mode3OnAsk — the human is never asked; default deny.
54
- * @param mode - effective mode
55
- * @param mode3OnAsk - "deny" | "allow" (validated config; anything else
56
- * falls back to "deny")
57
- * @returns "ask" | "deny" | "allow"
58
- */
59
- export function effectiveOnAsk(mode, mode3OnAsk) {
60
- if (mode === "ai-auto") return mode3OnAsk === "allow" ? "allow" : "deny";
61
- return "ask";
62
- }
1
+ /**
2
+ * dsh-codex-approval — modes.js
3
+ *
4
+ * The approval-mode dimension, orthogonal to the dsh sandbox mode:
5
+ *
6
+ * manual — plugin fully bypassed (next() straight through, no decision,
7
+ * no audit): the pre-plugin experience.
8
+ * ai — rules, then AI judge, then human fallback for every "ask"
9
+ * outcome (the default / v0.1.x behavior).
10
+ * ai-auto — rules, then AI judge; "ask" is never routed to a human —
11
+ * it resolves through mode3OnAsk (default deny).
12
+ *
13
+ * Pure functions only: parse/validate mode names, resolve the effective mode
14
+ * (per-session override wins over the config default), and map an "ask"
15
+ * outcome onto its effective action under the active mode.
16
+ */
17
+
18
+ /** The three approval modes. */
19
+ export const MODES = ["manual", "ai", "ai-auto"];
20
+ /** Numeric aliases mirroring the user-facing 1/2/3 choice. */
21
+ export const MODE_ALIASES = { "1": "manual", "2": "ai", "3": "ai-auto" };
22
+ /** Actions an "ask" may resolve to. */
23
+ export const ASK_ACTIONS = ["ask", "deny", "allow"];
24
+
25
+ /**
26
+ * Parse and validate a mode name (or numeric alias).
27
+ * @param input - "manual" | "ai" | "ai-auto" | "1" | "2" | "3"
28
+ * @returns the canonical mode name, or null when invalid.
29
+ */
30
+ export function parseMode(input) {
31
+ if (typeof input !== "string") return null;
32
+ const trimmed = input.trim().toLowerCase();
33
+ if (MODES.includes(trimmed)) return trimmed;
34
+ if (MODE_ALIASES[trimmed] !== void 0) return MODE_ALIASES[trimmed];
35
+ return null;
36
+ }
37
+
38
+ /**
39
+ * Resolve the effective mode for one request: per-session override wins,
40
+ * else the config default.
41
+ * @param sessionOverride - mode from the per-session store (or undefined)
42
+ * @param configDefault - the configured default mode
43
+ * @returns a canonical mode name (never null when configDefault is valid).
44
+ */
45
+ export function resolveMode(sessionOverride, configDefault) {
46
+ return parseMode(sessionOverride) ?? parseMode(configDefault) ?? "ai";
47
+ }
48
+
49
+ /**
50
+ * Map an "ask" outcome onto its effective action under the active mode.
51
+ * - manual: unreachable (handler bypasses); defensive "ask".
52
+ * - ai: "ask" — route to the human (next()).
53
+ * - ai-auto: mode3OnAsk — the human is never asked; default deny.
54
+ * @param mode - effective mode
55
+ * @param mode3OnAsk - "deny" | "allow" (validated config; anything else
56
+ * falls back to "deny")
57
+ * @returns "ask" | "deny" | "allow"
58
+ */
59
+ export function effectiveOnAsk(mode, mode3OnAsk) {
60
+ if (mode === "ai-auto") return mode3OnAsk === "allow" ? "allow" : "deny";
61
+ return "ask";
62
+ }