@dsh-plus/secret-env 0.1.28 → 0.1.30

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 CHANGED
@@ -108,7 +108,7 @@ window.__ModuleLoader__.load({
108
108
  unsaved: "未保存",
109
109
  loading: "加载中…",
110
110
  unavailable: "配置服务不可用。",
111
- readOnly: "当前部署无 settings provider,配置为只读;请编辑 settings.yaml。",
111
+ readOnly: "当前部署无 settings provider,配置为只读;请在 profile 的 cordis.patch.yml 行级配置。",
112
112
  invalidNumber: "请输入正整数。",
113
113
  collapse: "收起",
114
114
  expand: "展开",
@@ -125,7 +125,7 @@ window.__ModuleLoader__.load({
125
125
  unsaved: "Unsaved",
126
126
  loading: "Loading…",
127
127
  unavailable: "Settings service unavailable.",
128
- readOnly: "No settings provider in this deployment; config is read-only. Edit settings.yaml.",
128
+ readOnly: "No settings provider in this deployment; config is read-only. Configure it at the profile row in cordis.patch.yml.",
129
129
  invalidNumber: "Enter a positive integer.",
130
130
  collapse: "Collapse",
131
131
  expand: "Expand",
@@ -754,6 +754,105 @@ ${extra}
754
754
  }))
755
755
  ];
756
756
  }
757
+ /** 菜单展开期的按键处理(捕获阶段先於 Lexical/官方管线截获,已消费则阻止冒泡)。 */
758
+ function handleMenuKey(event, ctx) {
759
+ const list = ctx.candidates;
760
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
761
+ event.preventDefault();
762
+ event.stopPropagation();
763
+ const delta = event.key === "ArrowDown" ? 1 : -1;
764
+ ctx.setHighlight((prev) => (prev + delta + list.length) % list.length);
765
+ return;
766
+ }
767
+ if (event.key === "Enter" || event.key === "Tab") {
768
+ const entry = list[ctx.highlight] ?? list[0];
769
+ if (entry !== void 0) {
770
+ event.preventDefault();
771
+ event.stopPropagation();
772
+ ctx.pick(entry);
773
+ }
774
+ return;
775
+ }
776
+ if (event.key === "Escape") {
777
+ event.preventDefault();
778
+ event.stopPropagation();
779
+ ctx.suppressed.current = ctx.hit;
780
+ ctx.setHit(null);
781
+ }
782
+ }
783
+ /** IME 组合期间不介入:在编辑器根上挂组合态监听,返回解绑函数。 */
784
+ function bindCompositionGuards(root, composing) {
785
+ const onStart = () => {
786
+ composing.current = true;
787
+ };
788
+ const onEnd = () => {
789
+ composing.current = false;
790
+ };
791
+ root.addEventListener("compositionstart", onStart);
792
+ root.addEventListener("compositionend", onEnd);
793
+ return () => {
794
+ root.removeEventListener("compositionstart", onStart);
795
+ root.removeEventListener("compositionend", onEnd);
796
+ };
797
+ }
798
+ /** composer 卡片之外按下即关闭(对齐官方菜单的外部 dismiss 语义)。 */
799
+ function bindOutsideDismiss(wrap, dismiss) {
800
+ const onDown = (event) => {
801
+ if (!(event.target instanceof Node)) return;
802
+ if ((wrap?.closest("[data-composer-card]"))?.contains(event.target) === true) return;
803
+ dismiss();
804
+ };
805
+ document.addEventListener("pointerdown", onDown, true);
806
+ return () => document.removeEventListener("pointerdown", onDown, true);
807
+ }
808
+ /** 展开态候选面板(闭合态的常驻锚点容器由调用方渲染)。 */
809
+ function SecretPanel(props) {
810
+ const { candidates, highlight, wrapRef, t, onHighlight, onPick } = props;
811
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
812
+ className: "dse-menuWrap",
813
+ ref: wrapRef,
814
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
815
+ className: "dse-menu",
816
+ role: "listbox",
817
+ "aria-label": t("menu.aria"),
818
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
819
+ className: "dse-menuTitle",
820
+ children: t("menu.title")
821
+ }), candidates.map((entry, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
822
+ type: "button",
823
+ role: "option",
824
+ "aria-selected": index === highlight,
825
+ title: `$${entry.envName}`,
826
+ className: `dse-menuItem${index === highlight ? " dse-menuItemActive" : ""}`,
827
+ onMouseEnter: () => onHighlight(index),
828
+ onMouseDown: (event) => {
829
+ event.preventDefault();
830
+ onPick(entry);
831
+ },
832
+ children: [
833
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
834
+ className: "dse-menuName",
835
+ children: entry.name
836
+ }),
837
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
838
+ className: "dse-menuDesc",
839
+ children: entry.description
840
+ }),
841
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
842
+ className: "dse-menuBadges",
843
+ children: [entry.once === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
844
+ className: "dse-badge",
845
+ children: t("scopeOnce")
846
+ }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
847
+ className: "dse-badge dse-badgeDim",
848
+ children: entry.scope === "session" ? t("scopeSession") : entry.scope === "inherited" ? t("scopeInherited") : t("scopeGlobal")
849
+ })]
850
+ })
851
+ ]
852
+ }, `${entry.scope}:${entry.name}`))]
853
+ })
854
+ });
855
+ }
757
856
  function SecretMenu(props) {
758
857
  const { sessionId, t, insertToken } = props;
759
858
  const input = (props.useInput ?? ((_selector) => FALLBACK_INPUT))((state) => state);
@@ -799,18 +898,7 @@ ${extra}
799
898
  (0, react.useEffect)(() => {
800
899
  const root = editorRootOf(wrapRef.current);
801
900
  if (root === null) return;
802
- const onStart = () => {
803
- composingRef.current = true;
804
- };
805
- const onEnd = () => {
806
- composingRef.current = false;
807
- };
808
- root.addEventListener("compositionstart", onStart);
809
- root.addEventListener("compositionend", onEnd);
810
- return () => {
811
- root.removeEventListener("compositionstart", onStart);
812
- root.removeEventListener("compositionend", onEnd);
813
- };
901
+ return bindCompositionGuards(root, composingRef);
814
902
  }, []);
815
903
  const pick = (entry) => {
816
904
  if (hit === null) return;
@@ -834,42 +922,22 @@ ${extra}
834
922
  const root = editorRootOf(wrapRef.current);
835
923
  if (root === null) return;
836
924
  const onKey = (event) => {
837
- const list = candidatesRef.current;
838
- if (event.key === "ArrowDown" || event.key === "ArrowUp") {
839
- event.preventDefault();
840
- event.stopPropagation();
841
- const delta = event.key === "ArrowDown" ? 1 : -1;
842
- setHighlight((prev) => (prev + delta + list.length) % list.length);
843
- return;
844
- }
845
- if (event.key === "Enter" || event.key === "Tab") {
846
- const entry = list[highlightRef.current] ?? list[0];
847
- if (entry !== void 0) {
848
- event.preventDefault();
849
- event.stopPropagation();
850
- pickRef.current(entry);
851
- }
852
- return;
853
- }
854
- if (event.key === "Escape") {
855
- event.preventDefault();
856
- event.stopPropagation();
857
- suppressedRef.current = hit;
858
- setHit(null);
859
- }
925
+ handleMenuKey(event, {
926
+ candidates: candidatesRef.current,
927
+ highlight: highlightRef.current,
928
+ hit,
929
+ suppressed: suppressedRef,
930
+ pick: (entry) => pickRef.current(entry),
931
+ setHighlight,
932
+ setHit
933
+ });
860
934
  };
861
935
  root.addEventListener("keydown", onKey, true);
862
936
  return () => root.removeEventListener("keydown", onKey, true);
863
937
  }, [open, hit]);
864
938
  (0, react.useEffect)(() => {
865
939
  if (!open) return;
866
- const onDown = (event) => {
867
- if (!(event.target instanceof Node)) return;
868
- if ((wrapRef.current?.closest("[data-composer-card]"))?.contains(event.target) === true) return;
869
- setHit(null);
870
- };
871
- document.addEventListener("pointerdown", onDown, true);
872
- return () => document.removeEventListener("pointerdown", onDown, true);
940
+ return bindOutsideDismiss(wrapRef.current, () => setHit(null));
873
941
  }, [open]);
874
942
  (0, react.useEffect)(() => {
875
943
  setHighlight((prev) => candidates.length === 0 ? 0 : Math.min(prev, candidates.length - 1));
@@ -878,49 +946,13 @@ ${extra}
878
946
  className: "dse-menuWrap",
879
947
  ref: wrapRef
880
948
  });
881
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
882
- className: "dse-menuWrap",
883
- ref: wrapRef,
884
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
885
- className: "dse-menu",
886
- role: "listbox",
887
- "aria-label": t("menu.aria"),
888
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
889
- className: "dse-menuTitle",
890
- children: t("menu.title")
891
- }), candidates.map((entry, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
892
- type: "button",
893
- role: "option",
894
- "aria-selected": index === highlight,
895
- title: `$${entry.envName}`,
896
- className: `dse-menuItem${index === highlight ? " dse-menuItemActive" : ""}`,
897
- onMouseEnter: () => setHighlight(index),
898
- onMouseDown: (event) => {
899
- event.preventDefault();
900
- pick(entry);
901
- },
902
- children: [
903
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
904
- className: "dse-menuName",
905
- children: entry.name
906
- }),
907
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
908
- className: "dse-menuDesc",
909
- children: entry.description
910
- }),
911
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
912
- className: "dse-menuBadges",
913
- children: [entry.once === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
914
- className: "dse-badge",
915
- children: t("scopeOnce")
916
- }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
917
- className: "dse-badge dse-badgeDim",
918
- children: entry.scope === "session" ? t("scopeSession") : entry.scope === "inherited" ? t("scopeInherited") : t("scopeGlobal")
919
- })]
920
- })
921
- ]
922
- }, `${entry.scope}:${entry.name}`))]
923
- })
949
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SecretPanel, {
950
+ candidates,
951
+ highlight,
952
+ wrapRef,
953
+ t,
954
+ onHighlight: setHighlight,
955
+ onPick: pick
924
956
  });
925
957
  }
926
958
  //#endregion
package/lib/index.d.ts CHANGED
@@ -21,7 +21,7 @@ declare const Config: z<Schemastery.ObjectS<NoInfer<{
21
21
  secrets: z<NoInfer<SecretMetaInput[]>, NoInfer<SecretMeta[]>, "volatile-defined">;
22
22
  masked: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
23
23
  }>>, "plain">;
24
- /** 活动字段形态(0.1.7 loader 解析产物:volatile 字段为活动引用)。 */
24
+ /** 活动字段形态(loader 解析产物:volatile 字段为活动引用)。 */
25
25
  type SecretEnvConfigFields = Schemastery.TypeT<typeof Config>;
26
26
  /** 平面配置形态(消费面的读取形态,由活动引用解包得到)。 */
27
27
  type SecretEnvConfig = UnwrapVolatile<SecretEnvConfigFields>;
package/lib/index.js CHANGED
@@ -13,7 +13,7 @@ const SETTINGS_NS = "dsh-plus-secret-env";
13
13
  //#region src/config.ts
14
14
  /**
15
15
  * 配置单一事实源:cordis 行级 Config(组合默认值)与 settings namespace
16
- * (用户层,$DSH_HOME/settings.yaml 热生效)共用同一 schemastery schema。
16
+ * (用户层=profile 行级覆盖层 cordis.patch.yml,热生效)共用同一 schemastery schema。
17
17
  * `secrets` 只是全局密钥的**元数据索引**(后缀/描述/创建时间),
18
18
  * 值永远走 dsh-credentials seam($DSH_HOME/.credentials.yaml),不进本文件。
19
19
  * @module secret-env/config
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-plus/secret-env",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "description": "dsh-plus service+ui plugin: 密钥环境变量——以 $DSH_VAR_* 变量名向 agent 暴露密钥(全局持久/会话级/一次性),执行期经 dsh-shell-env 原生注入,值不进消息流,不影响缓存率",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -19,7 +19,7 @@
19
19
  "src"
20
20
  ],
21
21
  "dependencies": {
22
- "@dsh-plus/shared": "0.1.34"
22
+ "@dsh-plus/shared": "0.1.36"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "@deepseek-ai/cordis": "^4.0.5-alpha.1",
@@ -7,7 +7,15 @@
7
7
  * bail 通道 'slash/input-insert-text'(span + draftRev CAS,编辑器内应用)。
8
8
  * @module secret-env/client/menu
9
9
  */
10
- import { type ReactElement, useEffect, useRef, useState } from 'react'
10
+ import {
11
+ type Dispatch,
12
+ type MutableRefObject,
13
+ type ReactElement,
14
+ type SetStateAction,
15
+ useEffect,
16
+ useRef,
17
+ useState,
18
+ } from 'react'
11
19
 
12
20
  import { fetchSecrets, type SecretList } from './api.ts'
13
21
  import type { Translate } from './i18n.ts'
@@ -116,6 +124,123 @@ function toCandidates(list: SecretList): SecretCandidate[] {
116
124
  ]
117
125
  }
118
126
 
127
+ interface MenuKeyContext {
128
+ candidates: SecretCandidate[]
129
+ highlight: number
130
+ hit: SecretHit | null
131
+ suppressed: MutableRefObject<SecretHit | null>
132
+ pick: (entry: SecretCandidate) => void
133
+ setHighlight: Dispatch<SetStateAction<number>>
134
+ setHit: Dispatch<SetStateAction<SecretHit | null>>
135
+ }
136
+
137
+ /** 菜单展开期的按键处理(捕获阶段先於 Lexical/官方管线截获,已消费则阻止冒泡)。 */
138
+ function handleMenuKey(event: KeyboardEvent, ctx: MenuKeyContext): void {
139
+ const list = ctx.candidates
140
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
141
+ event.preventDefault()
142
+ event.stopPropagation()
143
+ const delta = event.key === 'ArrowDown' ? 1 : -1
144
+ ctx.setHighlight((prev) => (prev + delta + list.length) % list.length)
145
+ return
146
+ }
147
+ if (event.key === 'Enter' || event.key === 'Tab') {
148
+ const entry = list[ctx.highlight] ?? list[0]
149
+ if (entry !== undefined) {
150
+ event.preventDefault()
151
+ event.stopPropagation()
152
+ ctx.pick(entry)
153
+ }
154
+ return
155
+ }
156
+ if (event.key === 'Escape') {
157
+ event.preventDefault()
158
+ event.stopPropagation()
159
+ ctx.suppressed.current = ctx.hit
160
+ ctx.setHit(null)
161
+ }
162
+ }
163
+
164
+ /** IME 组合期间不介入:在编辑器根上挂组合态监听,返回解绑函数。 */
165
+ function bindCompositionGuards(
166
+ root: HTMLElement,
167
+ composing: MutableRefObject<boolean>,
168
+ ): () => void {
169
+ const onStart = (): void => {
170
+ composing.current = true
171
+ }
172
+ const onEnd = (): void => {
173
+ composing.current = false
174
+ }
175
+ root.addEventListener('compositionstart', onStart)
176
+ root.addEventListener('compositionend', onEnd)
177
+ return () => {
178
+ root.removeEventListener('compositionstart', onStart)
179
+ root.removeEventListener('compositionend', onEnd)
180
+ }
181
+ }
182
+
183
+ /** composer 卡片之外按下即关闭(对齐官方菜单的外部 dismiss 语义)。 */
184
+ function bindOutsideDismiss(wrap: HTMLElement | null, dismiss: () => void): () => void {
185
+ const onDown = (event: PointerEvent): void => {
186
+ if (!(event.target instanceof Node)) return
187
+ const card = wrap?.closest('[data-composer-card]')
188
+ if (card?.contains(event.target) === true) return
189
+ dismiss()
190
+ }
191
+ document.addEventListener('pointerdown', onDown, true)
192
+ return () => document.removeEventListener('pointerdown', onDown, true)
193
+ }
194
+
195
+ interface SecretPanelProps {
196
+ candidates: SecretCandidate[]
197
+ highlight: number
198
+ wrapRef: MutableRefObject<HTMLDivElement | null>
199
+ t: Translate
200
+ onHighlight: (index: number) => void
201
+ onPick: (entry: SecretCandidate) => void
202
+ }
203
+
204
+ /** 展开态候选面板(闭合态的常驻锚点容器由调用方渲染)。 */
205
+ function SecretPanel(props: SecretPanelProps): ReactElement {
206
+ const { candidates, highlight, wrapRef, t, onHighlight, onPick } = props
207
+ return (
208
+ <div className="dse-menuWrap" ref={wrapRef}>
209
+ <div className="dse-menu" role="listbox" aria-label={t('menu.aria')}>
210
+ <div className="dse-menuTitle">{t('menu.title')}</div>
211
+ {candidates.map((entry, index) => (
212
+ <button
213
+ key={`${entry.scope}:${entry.name}`}
214
+ type="button"
215
+ role="option"
216
+ aria-selected={index === highlight}
217
+ title={`$${entry.envName}`}
218
+ className={`dse-menuItem${index === highlight ? ' dse-menuItemActive' : ''}`}
219
+ onMouseEnter={() => onHighlight(index)}
220
+ onMouseDown={(event) => {
221
+ event.preventDefault()
222
+ onPick(entry)
223
+ }}
224
+ >
225
+ <span className="dse-menuName">{entry.name}</span>
226
+ <span className="dse-menuDesc">{entry.description}</span>
227
+ <span className="dse-menuBadges">
228
+ {entry.once === true ? <span className="dse-badge">{t('scopeOnce')}</span> : null}
229
+ <span className="dse-badge dse-badgeDim">
230
+ {entry.scope === 'session'
231
+ ? t('scopeSession')
232
+ : entry.scope === 'inherited'
233
+ ? t('scopeInherited')
234
+ : t('scopeGlobal')}
235
+ </span>
236
+ </span>
237
+ </button>
238
+ ))}
239
+ </div>
240
+ </div>
241
+ )
242
+ }
243
+
119
244
  export function SecretMenu(props: SecretMenuProps): ReactElement | null {
120
245
  const { sessionId, t, insertToken } = props
121
246
  // useInput 是标准钩子 prop,必须无条件调用;旧壳缺席时回落常量(菜单永不展开)。
@@ -182,18 +307,7 @@ export function SecretMenu(props: SecretMenuProps): ReactElement | null {
182
307
  useEffect(() => {
183
308
  const root = editorRootOf(wrapRef.current)
184
309
  if (root === null) return
185
- const onStart = (): void => {
186
- composingRef.current = true
187
- }
188
- const onEnd = (): void => {
189
- composingRef.current = false
190
- }
191
- root.addEventListener('compositionstart', onStart)
192
- root.addEventListener('compositionend', onEnd)
193
- return () => {
194
- root.removeEventListener('compositionstart', onStart)
195
- root.removeEventListener('compositionend', onEnd)
196
- }
310
+ return bindCompositionGuards(root, composingRef)
197
311
  }, [])
198
312
 
199
313
  const pick = (entry: SecretCandidate): void => {
@@ -217,29 +331,15 @@ export function SecretMenu(props: SecretMenuProps): ReactElement | null {
217
331
  const root = editorRootOf(wrapRef.current)
218
332
  if (root === null) return
219
333
  const onKey = (event: KeyboardEvent): void => {
220
- const list = candidatesRef.current
221
- if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
222
- event.preventDefault()
223
- event.stopPropagation()
224
- const delta = event.key === 'ArrowDown' ? 1 : -1
225
- setHighlight((prev) => (prev + delta + list.length) % list.length)
226
- return
227
- }
228
- if (event.key === 'Enter' || event.key === 'Tab') {
229
- const entry = list[highlightRef.current] ?? list[0]
230
- if (entry !== undefined) {
231
- event.preventDefault()
232
- event.stopPropagation()
233
- pickRef.current(entry)
234
- }
235
- return
236
- }
237
- if (event.key === 'Escape') {
238
- event.preventDefault()
239
- event.stopPropagation()
240
- suppressedRef.current = hit
241
- setHit(null)
242
- }
334
+ handleMenuKey(event, {
335
+ candidates: candidatesRef.current,
336
+ highlight: highlightRef.current,
337
+ hit,
338
+ suppressed: suppressedRef,
339
+ pick: (entry) => pickRef.current(entry),
340
+ setHighlight,
341
+ setHit,
342
+ })
243
343
  }
244
344
  root.addEventListener('keydown', onKey, true)
245
345
  return () => root.removeEventListener('keydown', onKey, true)
@@ -248,14 +348,7 @@ export function SecretMenu(props: SecretMenuProps): ReactElement | null {
248
348
  // 点 composer 卡片之外处关闭(对齐官方菜单的外部 dismiss 语义)。
249
349
  useEffect(() => {
250
350
  if (!open) return
251
- const onDown = (event: PointerEvent): void => {
252
- if (!(event.target instanceof Node)) return
253
- const card = wrapRef.current?.closest('[data-composer-card]')
254
- if (card?.contains(event.target) === true) return
255
- setHit(null)
256
- }
257
- document.addEventListener('pointerdown', onDown, true)
258
- return () => document.removeEventListener('pointerdown', onDown, true)
351
+ return bindOutsideDismiss(wrapRef.current, () => setHit(null))
259
352
  }, [open])
260
353
 
261
354
  // 高亮随候选集变化收敛到合法范围。
@@ -267,39 +360,15 @@ export function SecretMenu(props: SecretMenuProps): ReactElement | null {
267
360
  // 锚点常驻:闭合态也要挂载容器,检测效果依赖它定位 composer 编辑器根。
268
361
  return <div className="dse-menuWrap" ref={wrapRef} />
269
362
  }
363
+
270
364
  return (
271
- <div className="dse-menuWrap" ref={wrapRef}>
272
- <div className="dse-menu" role="listbox" aria-label={t('menu.aria')}>
273
- <div className="dse-menuTitle">{t('menu.title')}</div>
274
- {candidates.map((entry, index) => (
275
- <button
276
- key={`${entry.scope}:${entry.name}`}
277
- type="button"
278
- role="option"
279
- aria-selected={index === highlight}
280
- title={`$${entry.envName}`}
281
- className={`dse-menuItem${index === highlight ? ' dse-menuItemActive' : ''}`}
282
- onMouseEnter={() => setHighlight(index)}
283
- onMouseDown={(event) => {
284
- event.preventDefault()
285
- pick(entry)
286
- }}
287
- >
288
- <span className="dse-menuName">{entry.name}</span>
289
- <span className="dse-menuDesc">{entry.description}</span>
290
- <span className="dse-menuBadges">
291
- {entry.once === true ? <span className="dse-badge">{t('scopeOnce')}</span> : null}
292
- <span className="dse-badge dse-badgeDim">
293
- {entry.scope === 'session'
294
- ? t('scopeSession')
295
- : entry.scope === 'inherited'
296
- ? t('scopeInherited')
297
- : t('scopeGlobal')}
298
- </span>
299
- </span>
300
- </button>
301
- ))}
302
- </div>
303
- </div>
365
+ <SecretPanel
366
+ candidates={candidates}
367
+ highlight={highlight}
368
+ wrapRef={wrapRef}
369
+ t={t}
370
+ onHighlight={setHighlight}
371
+ onPick={pick}
372
+ />
304
373
  )
305
374
  }
package/src/config.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * 配置单一事实源:cordis 行级 Config(组合默认值)与 settings namespace
3
- * (用户层,$DSH_HOME/settings.yaml 热生效)共用同一 schemastery schema。
3
+ * (用户层=profile 行级覆盖层 cordis.patch.yml,热生效)共用同一 schemastery schema。
4
4
  * `secrets` 只是全局密钥的**元数据索引**(后缀/描述/创建时间),
5
5
  * 值永远走 dsh-credentials seam($DSH_HOME/.credentials.yaml),不进本文件。
6
6
  * @module secret-env/config
@@ -10,7 +10,7 @@ import type { UnwrapVolatile } from '@dsh-plus/shared'
10
10
 
11
11
  import { SETTINGS_NS as NS_LITERAL } from './ns.ts'
12
12
 
13
- /** settings 命名空间(字面量即合法命名空间,0.1.2-alpha.2 起编译期校验)。 */
13
+ /** settings 命名空间(字面量即合法命名空间,编译期校验)。 */
14
14
  export const SETTINGS_NS = NS_LITERAL
15
15
 
16
16
  /** 一条全局密钥的元数据(值不在此处)。 */
@@ -36,7 +36,7 @@ const SecretMetaSchema: z<SecretMetaInput, SecretMeta> = z.object({
36
36
  createdAt: z.string().description('创建时间 ISO').default(''),
37
37
  })
38
38
 
39
- // 0.1.7:全字段 `.volatile()`——条目进入 settings describe 视图(卡片可读写),
39
+ // 全字段 `.volatile()`——条目进入 settings describe 视图(卡片可读写),
40
40
  // loader 原位提交活动引用,索引读取现取即热。
41
41
  export const Config = z.object({
42
42
  secrets: z
@@ -51,7 +51,7 @@ export const Config = z.object({
51
51
  .volatile(),
52
52
  })
53
53
 
54
- /** 活动字段形态(0.1.7 loader 解析产物:volatile 字段为活动引用)。 */
54
+ /** 活动字段形态(loader 解析产物:volatile 字段为活动引用)。 */
55
55
  export type SecretEnvConfigFields = Schemastery.TypeT<typeof Config>
56
56
  /** 平面配置形态(消费面的读取形态,由活动引用解包得到)。 */
57
57
  export type SecretEnvConfig = UnwrapVolatile<SecretEnvConfigFields>
package/src/service.ts CHANGED
@@ -39,7 +39,7 @@ import {
39
39
  import { envNameOf, suffixOf } from './names.ts'
40
40
  import { SETTINGS_NS } from './ns.ts'
41
41
 
42
- /** settings 服务窄面(0.1.7 只需 update 写入用户层;describe 读由 loader 并入 config)。 */
42
+ /** settings 服务窄面(只需 update 写入用户层;describe 读由 loader 并入 config)。 */
43
43
  interface SettingsLike {
44
44
  update(ns: string, patch: Record<string, unknown>): Promise<void>
45
45
  }
@@ -69,7 +69,7 @@ export class SecretEnvService extends Service {
69
69
 
70
70
  constructor(ctx: Context, config: SecretEnvConfig | SecretEnvConfigFields | undefined) {
71
71
  super(ctx, 'secretEnv')
72
- // 0.1.7 替代 installSection/setSource:活动引用原位提交——索引读取经
72
+ // 替代 installSection/setSource:活动引用原位提交——索引读取经
73
73
  // readIndex 现取平面快照,用户层写入走 loader 落盘 + 原位提交热生效。
74
74
  this.base = config !== undefined ? unwrapVolatile(config) : { secrets: [], masked: [] }
75
75
  this.readIndex = config !== undefined ? () => unwrapVolatile(config) : undefined