@dsh-plus/secret-env 0.1.10 → 0.1.12
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 +25 -16
- package/lib/index.d.ts +17 -4
- package/lib/index.js +0 -1
- package/package.json +14 -14
- package/src/client/api.ts +12 -2
- package/src/client/client.ts +2 -1
- package/src/client/command.ts +2 -2
- package/src/client/common.ts +5 -4
- package/src/client/i18n.ts +15 -3
- package/src/client/menu.tsx +2 -1
- package/src/client/panel-host.tsx +2 -2
- package/src/client/panel.tsx +4 -7
- package/src/client/section.tsx +3 -2
- package/src/config.ts +17 -9
- package/src/inventory.ts +2 -1
package/lib/client.js
CHANGED
|
@@ -223,28 +223,24 @@ window.__ModuleLoader__.load({
|
|
|
223
223
|
}
|
|
224
224
|
//#endregion
|
|
225
225
|
//#region ../shared/src/client/fetch.ts
|
|
226
|
-
|
|
227
|
-
* 同源自定义端点的最小 fetch 封装(各插件 client/api.ts 的公共收编版)。
|
|
228
|
-
* 仅同源(credentials: same-origin),错误统一抛 Error(body.error)。
|
|
229
|
-
* @module @dsh-plus/shared/client/fetch
|
|
230
|
-
*/
|
|
231
|
-
async function parse(res) {
|
|
226
|
+
async function parse(res, guard) {
|
|
232
227
|
const body = await res.json();
|
|
233
228
|
if (!res.ok) throw new Error(body.error ?? `HTTP ${res.status}`);
|
|
229
|
+
if (guard !== void 0 && !guard(body)) throw new Error(`响应结构不符合预期(${res.url})`);
|
|
234
230
|
return body;
|
|
235
231
|
}
|
|
236
232
|
/** GET 同源 JSON 端点。 */
|
|
237
|
-
async function getJson(route) {
|
|
238
|
-
return parse(await fetch(route, { credentials: "same-origin" }));
|
|
233
|
+
async function getJson(route, guard) {
|
|
234
|
+
return parse(await fetch(route, { credentials: "same-origin" }), guard);
|
|
239
235
|
}
|
|
240
236
|
/** POST 同源 JSON 端点(body 可省略)。 */
|
|
241
|
-
async function postJson(route, body) {
|
|
237
|
+
async function postJson(route, body, guard) {
|
|
242
238
|
return parse(await fetch(route, {
|
|
243
239
|
method: "POST",
|
|
244
240
|
credentials: "same-origin",
|
|
245
241
|
headers: { "content-type": "application/json" },
|
|
246
|
-
body
|
|
247
|
-
}));
|
|
242
|
+
...body === void 0 ? {} : { body: JSON.stringify(body) }
|
|
243
|
+
}), guard);
|
|
248
244
|
}
|
|
249
245
|
//#endregion
|
|
250
246
|
//#region ../shared/src/client/i18n.ts
|
|
@@ -283,7 +279,10 @@ window.__ModuleLoader__.load({
|
|
|
283
279
|
confirm: "Confirm",
|
|
284
280
|
cancel: "Cancel"
|
|
285
281
|
};
|
|
286
|
-
/**
|
|
282
|
+
/**
|
|
283
|
+
* own 覆盖同键合并(返回新对象,入参不可变)。
|
|
284
|
+
* 返回类型保留两侧键集合,调用方据此派生 DictKey。
|
|
285
|
+
*/
|
|
287
286
|
function mergeDict(base, own) {
|
|
288
287
|
return {
|
|
289
288
|
...base,
|
|
@@ -519,6 +518,11 @@ ${extra}
|
|
|
519
518
|
"error.method": "Method not allowed.",
|
|
520
519
|
"error.internal": "Internal error; check the host log."
|
|
521
520
|
};
|
|
521
|
+
/** 服务端错误码 → 文案键的运行期收窄:码来自端点响应,不可当作编译期已知键。 */
|
|
522
|
+
function errorKeyOf(code) {
|
|
523
|
+
const key = `error.${code}`;
|
|
524
|
+
return Object.hasOwn(ownZh, key) ? key : null;
|
|
525
|
+
}
|
|
522
526
|
const zh = mergeDict(commonZh, ownZh);
|
|
523
527
|
const en = mergeDict(commonEn, ownEn);
|
|
524
528
|
//#endregion
|
|
@@ -535,8 +539,14 @@ ${extra}
|
|
|
535
539
|
this.code = code;
|
|
536
540
|
}
|
|
537
541
|
};
|
|
542
|
+
/** 端点应答形状守卫:三个数组字段缺一即判非法(渲染期直接 map,不能靠断言)。 */
|
|
543
|
+
function isSecretList(value) {
|
|
544
|
+
if (typeof value !== "object" || value === null) return false;
|
|
545
|
+
const list = value;
|
|
546
|
+
return Array.isArray(list.global) && Array.isArray(list.session) && Array.isArray(list.inherited);
|
|
547
|
+
}
|
|
538
548
|
async function fetchSecrets(sessionId) {
|
|
539
|
-
return getJson(`/dsh-plus/secret-env/list${sessionId === void 0 ? "" : `?sessionId=${encodeURIComponent(sessionId)}`}
|
|
549
|
+
return getJson(`/dsh-plus/secret-env/list${sessionId === void 0 ? "" : `?sessionId=${encodeURIComponent(sessionId)}`}`, isSecretList);
|
|
540
550
|
}
|
|
541
551
|
async function write(path, body) {
|
|
542
552
|
try {
|
|
@@ -908,9 +918,8 @@ ${extra}
|
|
|
908
918
|
*/
|
|
909
919
|
/** 端点错误 → 本地化文案(未知码回落 internal)。 */
|
|
910
920
|
function errorText(t, error) {
|
|
911
|
-
const
|
|
912
|
-
|
|
913
|
-
return text === `error.${code}` ? t("error.internal") : text;
|
|
921
|
+
const key = errorKeyOf(error instanceof ApiError ? error.code : "internal");
|
|
922
|
+
return key === null ? t("error.internal") : t(key);
|
|
914
923
|
}
|
|
915
924
|
/** 名称输入的实时规范化:键入即转大写(去空白交给保存时的 normalizeSuffix)。 */
|
|
916
925
|
function liveName(raw) {
|
package/lib/index.d.ts
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
2
|
import { Context, Service } from "@deepseek-ai/cordis";
|
|
3
3
|
//#region src/config.d.ts
|
|
4
|
+
/** 一条全局密钥的元数据(值不在此处)。 */
|
|
5
|
+
interface SecretMeta {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
createdAt: string;
|
|
9
|
+
}
|
|
10
|
+
/** `secrets` 条目入参形态(schema 各字段均有 default,故调用方可省略)。 */
|
|
11
|
+
interface SecretMetaInput {
|
|
12
|
+
name?: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
createdAt?: string;
|
|
15
|
+
}
|
|
4
16
|
declare const Config: z<Schemastery.ObjectS<{
|
|
5
|
-
secrets: z<
|
|
17
|
+
secrets: z<SecretMetaInput[], SecretMeta[]>;
|
|
6
18
|
masked: z<string[], string[]>;
|
|
7
19
|
}>, Schemastery.ObjectT<{
|
|
8
|
-
secrets: z<
|
|
20
|
+
secrets: z<SecretMetaInput[], SecretMeta[]>;
|
|
9
21
|
masked: z<string[], string[]>;
|
|
10
22
|
}>>;
|
|
11
23
|
type SecretEnvConfig = Schemastery.TypeT<typeof Config>;
|
|
@@ -17,7 +29,8 @@ interface GlobalEntry {
|
|
|
17
29
|
envName: string;
|
|
18
30
|
description: string;
|
|
19
31
|
configured: boolean;
|
|
20
|
-
|
|
32
|
+
/** 并上 undefined:构造处可能无来源(服务端),wire 上 JSON 会省略该键,故与缺席等价。 */
|
|
33
|
+
source?: string | undefined;
|
|
21
34
|
writable: boolean;
|
|
22
35
|
/** 会话视图:该变量在本会话被屏蔽(无 sessionId 的调用恒为 false)。 */
|
|
23
36
|
masked: boolean;
|
|
@@ -121,7 +134,7 @@ declare class SecretEnvError extends Error {
|
|
|
121
134
|
//#endregion
|
|
122
135
|
//#region src/index.d.ts
|
|
123
136
|
declare const name = "dsh-plus-secret-env";
|
|
124
|
-
declare const inject: readonly [
|
|
137
|
+
declare const inject: readonly ['credentials', 'shellEnv'];
|
|
125
138
|
declare module '@deepseek-ai/cordis' {
|
|
126
139
|
interface Context {
|
|
127
140
|
secretEnv: SecretEnvService;
|
package/lib/index.js
CHANGED
|
@@ -17,7 +17,6 @@ const SETTINGS_NS = "dsh-plus-secret-env";
|
|
|
17
17
|
* 值永远走 dsh-credentials seam($DSH_HOME/.credentials.yaml),不进本文件。
|
|
18
18
|
* @module secret-env/config
|
|
19
19
|
*/
|
|
20
|
-
/** 一条全局密钥的元数据(值不在此处)。 */
|
|
21
20
|
const SecretMetaSchema = z.object({
|
|
22
21
|
name: z.string().min(1).description("变量名后缀(如 GITHUB_TOKEN)").default(""),
|
|
23
22
|
description: z.string().description("用途描述(仅人读,不进提示词)").default(""),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-plus/secret-env",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
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,26 +19,26 @@
|
|
|
19
19
|
"src"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@dsh-plus/shared": "0.1.
|
|
22
|
+
"@dsh-plus/shared": "0.1.17"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
25
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
26
|
-
"@deepseek-ai/dsh-credentials": "^0.1.5-rc.
|
|
27
|
-
"@deepseek-ai/dsh-host-webserver": "^0.1.5-rc.
|
|
28
|
-
"@deepseek-ai/dsh-settings": "^0.1.5-rc.
|
|
29
|
-
"@deepseek-ai/dsh-shell-env": "^0.1.5-rc.
|
|
26
|
+
"@deepseek-ai/dsh-credentials": "^0.1.5-rc.2",
|
|
27
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.5-rc.2",
|
|
28
|
+
"@deepseek-ai/dsh-settings": "^0.1.5-rc.2",
|
|
29
|
+
"@deepseek-ai/dsh-shell-env": "^0.1.5-rc.2",
|
|
30
30
|
"@deepseek-ai/schemastery": "^3.18.2"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@deepseek-ai/cordis": "4.0.2",
|
|
34
|
-
"@deepseek-ai/dsh-client-ui-commands": "0.1.5-rc.
|
|
35
|
-
"@deepseek-ai/dsh-client-ui-primitives": "0.1.5-rc.
|
|
36
|
-
"@deepseek-ai/dsh-credentials": "0.1.5-rc.
|
|
37
|
-
"@deepseek-ai/dsh-host-webserver": "0.1.5-rc.
|
|
38
|
-
"@deepseek-ai/dsh-session": "0.1.5-rc.
|
|
39
|
-
"@deepseek-ai/dsh-settings": "0.1.5-rc.
|
|
40
|
-
"@deepseek-ai/dsh-shell-env": "0.1.5-rc.
|
|
41
|
-
"@deepseek-ai/dsh-tools": "0.1.5-rc.
|
|
34
|
+
"@deepseek-ai/dsh-client-ui-commands": "0.1.5-rc.2",
|
|
35
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.5-rc.2",
|
|
36
|
+
"@deepseek-ai/dsh-credentials": "0.1.5-rc.2",
|
|
37
|
+
"@deepseek-ai/dsh-host-webserver": "0.1.5-rc.2",
|
|
38
|
+
"@deepseek-ai/dsh-session": "0.1.5-rc.2",
|
|
39
|
+
"@deepseek-ai/dsh-settings": "0.1.5-rc.2",
|
|
40
|
+
"@deepseek-ai/dsh-shell-env": "0.1.5-rc.2",
|
|
41
|
+
"@deepseek-ai/dsh-tools": "0.1.5-rc.2",
|
|
42
42
|
"@deepseek-ai/schemastery": "3.18.2",
|
|
43
43
|
"@types/node": "26.4.0",
|
|
44
44
|
"@types/react": "~18.3.31",
|
package/src/client/api.ts
CHANGED
|
@@ -47,14 +47,24 @@ interface WriteResult {
|
|
|
47
47
|
|
|
48
48
|
/** 端点错误:携带结构化错误码,UI 按码映射文案。 */
|
|
49
49
|
export class ApiError extends Error {
|
|
50
|
-
|
|
50
|
+
readonly code: string
|
|
51
|
+
|
|
52
|
+
constructor(code: string) {
|
|
51
53
|
super(code)
|
|
54
|
+
this.code = code
|
|
52
55
|
}
|
|
53
56
|
}
|
|
54
57
|
|
|
58
|
+
/** 端点应答形状守卫:三个数组字段缺一即判非法(渲染期直接 map,不能靠断言)。 */
|
|
59
|
+
function isSecretList(value: unknown): value is SecretList {
|
|
60
|
+
if (typeof value !== 'object' || value === null) return false
|
|
61
|
+
const list = value as { global?: unknown; session?: unknown; inherited?: unknown }
|
|
62
|
+
return Array.isArray(list.global) && Array.isArray(list.session) && Array.isArray(list.inherited)
|
|
63
|
+
}
|
|
64
|
+
|
|
55
65
|
export async function fetchSecrets(sessionId?: string): Promise<SecretList> {
|
|
56
66
|
const query = sessionId === undefined ? '' : `?sessionId=${encodeURIComponent(sessionId)}`
|
|
57
|
-
return getJson<SecretList>(`/dsh-plus/secret-env/list${query}
|
|
67
|
+
return getJson<SecretList>(`/dsh-plus/secret-env/list${query}`, isSecretList)
|
|
58
68
|
}
|
|
59
69
|
|
|
60
70
|
async function write(path: string, body: Record<string, unknown>): Promise<void> {
|
package/src/client/client.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import type { Context } from '@deepseek-ai/cordis'
|
|
12
12
|
|
|
13
13
|
import { registerVarCommand } from './command.ts'
|
|
14
|
+
import type { Translate } from './i18n.ts'
|
|
14
15
|
import { en, NS, zh } from './i18n.ts'
|
|
15
16
|
import { SecretMenu, type TokenSpanLike } from './menu.tsx'
|
|
16
17
|
import { SessionPanelHost } from './panel-host.tsx'
|
|
@@ -29,7 +30,7 @@ interface SlotsLike {
|
|
|
29
30
|
|
|
30
31
|
interface LocaleLike {
|
|
31
32
|
register(ns: string, dict: { zh: Record<string, string>; en: Record<string, string> }): () => void
|
|
32
|
-
bind(ns: string):
|
|
33
|
+
bind(ns: string): Translate
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
/** 会话作用域句柄的结构子集(dsh-api-session-controller 客户端 sessions 服务)。 */
|
package/src/client/command.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @module secret-env/client/command
|
|
8
8
|
*/
|
|
9
9
|
import type { CommandContribution } from '@deepseek-ai/dsh-client-ui-commands/client'
|
|
10
|
-
|
|
10
|
+
import type { Translate } from './i18n.ts'
|
|
11
11
|
import { requestOpenSessionPanel } from './panel-bus.ts'
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -37,7 +37,7 @@ interface SessionsAvailabilityLike {
|
|
|
37
37
|
export function registerVarCommand(
|
|
38
38
|
ctx: CommandHostContext,
|
|
39
39
|
sessions: SessionsAvailabilityLike,
|
|
40
|
-
t:
|
|
40
|
+
t: Translate,
|
|
41
41
|
): void {
|
|
42
42
|
ctx.inject(['commandUi'], (scope) => {
|
|
43
43
|
scope.effect(
|
package/src/client/common.ts
CHANGED
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { type NameError, normalizeSuffix, validateSuffix } from '../names.ts'
|
|
7
7
|
import { ApiError } from './api.ts'
|
|
8
|
+
import { errorKeyOf, type Translate } from './i18n.ts'
|
|
8
9
|
|
|
9
10
|
/** 端点错误 → 本地化文案(未知码回落 internal)。 */
|
|
10
|
-
export function errorText(t:
|
|
11
|
+
export function errorText(t: Translate, error: unknown): string {
|
|
11
12
|
const code = error instanceof ApiError ? error.code : 'internal'
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return
|
|
13
|
+
// 错误码来自端点响应,运行期收窄到字典已知键;未知码回落 internal。
|
|
14
|
+
const key = errorKeyOf(code)
|
|
15
|
+
return key === null ? t('error.internal') : t(key)
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
/** 名称输入的实时规范化:键入即转大写(去空白交给保存时的 normalizeSuffix)。 */
|
package/src/client/i18n.ts
CHANGED
|
@@ -74,7 +74,7 @@ const ownZh = {
|
|
|
74
74
|
'error.no-session': '缺少会话标识,请刷新后重试。',
|
|
75
75
|
'error.method': '请求方法不允许。',
|
|
76
76
|
'error.internal': '服务内部错误,请查看宿主日志。',
|
|
77
|
-
}
|
|
77
|
+
} as const
|
|
78
78
|
|
|
79
79
|
const ownEn = {
|
|
80
80
|
nav: 'Env Variables',
|
|
@@ -140,7 +140,19 @@ const ownEn = {
|
|
|
140
140
|
'error.no-session': 'Missing session id; refresh and retry.',
|
|
141
141
|
'error.method': 'Method not allowed.',
|
|
142
142
|
'error.internal': 'Internal error; check the host log.',
|
|
143
|
+
} as const
|
|
144
|
+
|
|
145
|
+
export type DictKey = keyof typeof commonZh | keyof typeof ownZh
|
|
146
|
+
|
|
147
|
+
/** 卡片/面板共用的翻译函数类型(slot 注入的 bind 结果按此消费)。 */
|
|
148
|
+
export type Translate = (key: DictKey) => string
|
|
149
|
+
|
|
150
|
+
/** 服务端错误码 → 文案键的运行期收窄:码来自端点响应,不可当作编译期已知键。 */
|
|
151
|
+
export function errorKeyOf(code: string): DictKey | null {
|
|
152
|
+
const key = `error.${code}`
|
|
153
|
+
return Object.hasOwn(ownZh, key) ? (key as DictKey) : null
|
|
143
154
|
}
|
|
144
155
|
|
|
145
|
-
|
|
146
|
-
export const
|
|
156
|
+
// 标注为 Record<DictKey, string>:en 缺任一键即编译期报错(中英强制对齐)。
|
|
157
|
+
export const zh: Record<DictKey, string> = mergeDict(commonZh, ownZh)
|
|
158
|
+
export const en: Record<DictKey, string> = mergeDict(commonEn, ownEn)
|
package/src/client/menu.tsx
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { type ReactElement, useEffect, useRef, useState } from 'react'
|
|
11
11
|
|
|
12
12
|
import { fetchSecrets, type SecretList } from './api.ts'
|
|
13
|
+
import type { Translate } from './i18n.ts'
|
|
13
14
|
import {
|
|
14
15
|
applyChipCorrection,
|
|
15
16
|
type ChipCorrection,
|
|
@@ -38,7 +39,7 @@ export interface TokenSpanLike {
|
|
|
38
39
|
export interface SecretMenuProps {
|
|
39
40
|
/** 插槽 inject 工厂注入的当前会话 id。 */
|
|
40
41
|
sessionId: string
|
|
41
|
-
t
|
|
42
|
+
t: Translate
|
|
42
43
|
/** 框架标准钩子:输入状态快照订阅(SnapshotSelectorHook)。 */
|
|
43
44
|
useInput?<S>(selector: (state: InputStateLike) => S): S
|
|
44
45
|
/** inject 面注入:经官方 scoped bail 通道替换令牌文本;返回是否被编辑器应用。 */
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
* @module secret-env/client/panel-host
|
|
7
7
|
*/
|
|
8
8
|
import { type ReactElement, useEffect, useRef, useState } from 'react'
|
|
9
|
-
|
|
9
|
+
import type { Translate } from './i18n.ts'
|
|
10
10
|
import { SessionSecretsPanel } from './panel.tsx'
|
|
11
11
|
import { onOpenSessionPanel } from './panel-bus.ts'
|
|
12
12
|
|
|
13
13
|
export interface SessionPanelHostProps {
|
|
14
14
|
/** 插槽 inject 工厂注入的当前会话 id。 */
|
|
15
15
|
sessionId: string
|
|
16
|
-
t
|
|
16
|
+
t: Translate
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export function SessionPanelHost(props: SessionPanelHostProps): ReactElement | null {
|
package/src/client/panel.tsx
CHANGED
|
@@ -28,10 +28,11 @@ import {
|
|
|
28
28
|
unsetSession,
|
|
29
29
|
} from './api.ts'
|
|
30
30
|
import { copyText, errorText, liveName, nameErrorOf } from './common.ts'
|
|
31
|
+
import type { Translate } from './i18n.ts'
|
|
31
32
|
|
|
32
33
|
export interface SessionSecretsPanelProps {
|
|
33
34
|
sessionId: string
|
|
34
|
-
t
|
|
35
|
+
t: Translate
|
|
35
36
|
/** 提供时渲染头栏(标题/刷新/关闭),供 overlay 弹层模式使用。 */
|
|
36
37
|
onClose?(): void
|
|
37
38
|
}
|
|
@@ -46,7 +47,7 @@ interface FormState {
|
|
|
46
47
|
const EMPTY_FORM: FormState = { name: '', value: '', description: '', once: false }
|
|
47
48
|
|
|
48
49
|
/** 复制按钮(复制成功短暂亮勾)。 */
|
|
49
|
-
function CopyButton(props: { t
|
|
50
|
+
function CopyButton(props: { t: Translate; text: string }): ReactElement {
|
|
50
51
|
const { t } = props
|
|
51
52
|
const [copied, setCopied] = useState(false)
|
|
52
53
|
const timer = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
@@ -77,11 +78,7 @@ function CopyButton(props: { t(key: string): string; text: string }): ReactEleme
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
/** 会话内屏蔽开关(眼睛语义:可见=注入,划掉=本会话屏蔽)。 */
|
|
80
|
-
function MaskToggle(props: {
|
|
81
|
-
t(key: string): string
|
|
82
|
-
masked: boolean
|
|
83
|
-
onToggle(): void
|
|
84
|
-
}): ReactElement {
|
|
81
|
+
function MaskToggle(props: { t: Translate; masked: boolean; onToggle(): void }): ReactElement {
|
|
85
82
|
const { t, masked } = props
|
|
86
83
|
return (
|
|
87
84
|
<button
|
package/src/client/section.tsx
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
unsetGlobal,
|
|
28
28
|
} from './api.ts'
|
|
29
29
|
import { copyText, errorText, liveName, nameErrorOf } from './common.ts'
|
|
30
|
+
import type { Translate } from './i18n.ts'
|
|
30
31
|
import { SessionSecretsPanel } from './panel.tsx'
|
|
31
32
|
|
|
32
33
|
/** sessions 服务的列表快照面(结构子集;ObservableSnapshot 契约)。 */
|
|
@@ -39,7 +40,7 @@ export interface SessionsListLike {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
export interface SectionProps {
|
|
42
|
-
t
|
|
43
|
+
t: Translate
|
|
43
44
|
sessions?: SessionsListLike
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -52,7 +53,7 @@ interface FormState {
|
|
|
52
53
|
const EMPTY_FORM: FormState = { name: '', value: '', description: '' }
|
|
53
54
|
|
|
54
55
|
/** 复制按钮(复制成功短暂亮勾)。 */
|
|
55
|
-
function CopyButton(props: { t
|
|
56
|
+
function CopyButton(props: { t: Translate; text: string }): ReactElement {
|
|
56
57
|
const { t } = props
|
|
57
58
|
const [copied, setCopied] = useState(false)
|
|
58
59
|
const timer = useRef<ReturnType<typeof setTimeout> | null>(null)
|
package/src/config.ts
CHANGED
|
@@ -13,8 +13,23 @@ import { SETTINGS_NS as NS_LITERAL } from './ns.ts'
|
|
|
13
13
|
export const SETTINGS_NS = NS_LITERAL
|
|
14
14
|
|
|
15
15
|
/** 一条全局密钥的元数据(值不在此处)。 */
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
export interface SecretMeta {
|
|
17
|
+
name: string
|
|
18
|
+
description: string
|
|
19
|
+
createdAt: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** `secrets` 条目入参形态(schema 各字段均有 default,故调用方可省略)。 */
|
|
23
|
+
export interface SecretMetaInput {
|
|
24
|
+
name?: string
|
|
25
|
+
description?: string
|
|
26
|
+
createdAt?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 显式标注而非 `any`:z.object() 的推断类型含 cosmokit 的 `& Dict` 索引签名,
|
|
30
|
+
// 直接导出会触发 TS2883(inferred type cannot be named / not portable)并使
|
|
31
|
+
// tsdown 的 dts 生成失败。标注成具名契约后既保住类型、又让产物可移植。
|
|
32
|
+
const SecretMetaSchema: z<SecretMetaInput, SecretMeta> = z.object({
|
|
18
33
|
name: z.string().min(1).description('变量名后缀(如 GITHUB_TOKEN)').default(''),
|
|
19
34
|
description: z.string().description('用途描述(仅人读,不进提示词)').default(''),
|
|
20
35
|
createdAt: z.string().description('创建时间 ISO').default(''),
|
|
@@ -29,10 +44,3 @@ export const Config = z.object({
|
|
|
29
44
|
})
|
|
30
45
|
|
|
31
46
|
export type SecretEnvConfig = Schemastery.TypeT<typeof Config>
|
|
32
|
-
|
|
33
|
-
/** 一条元数据的运行时形态。 */
|
|
34
|
-
export interface SecretMeta {
|
|
35
|
-
name: string
|
|
36
|
-
description: string
|
|
37
|
-
createdAt: string
|
|
38
|
-
}
|
package/src/inventory.ts
CHANGED
|
@@ -20,7 +20,8 @@ export interface GlobalEntry {
|
|
|
20
20
|
envName: string
|
|
21
21
|
description: string
|
|
22
22
|
configured: boolean
|
|
23
|
-
|
|
23
|
+
/** 并上 undefined:构造处可能无来源(服务端),wire 上 JSON 会省略该键,故与缺席等价。 */
|
|
24
|
+
source?: string | undefined
|
|
24
25
|
writable: boolean
|
|
25
26
|
/** 会话视图:该变量在本会话被屏蔽(无 sessionId 的调用恒为 false)。 */
|
|
26
27
|
masked: boolean
|