diff --git a/mateclaw-ui/src/api/index.ts b/mateclaw-ui/src/api/index.ts index 0da4b940..53db0867 100644 --- a/mateclaw-ui/src/api/index.ts +++ b/mateclaw-ui/src/api/index.ts @@ -203,6 +203,24 @@ export const skillApi = { getLessons: (id: string | number) => http.get(`/skills/${id}/lessons`), clearLessons: (id: string | number) => http.post(`/skills/${id}/lessons/clear`), employees: (id: string | number) => http.get(`/skills/${id}/employees`), + /** + * Per-skill secrets — env-var-shaped key/value pairs that get injected + * into the script subprocess at runtime. Plaintext values never leave + * the server; list returns masked previews only. + */ + listSecrets: (id: string | number) => http.get(`/skills/${id}/secrets`), + putSecret: (id: string | number, key: string, value: string) => + http.post(`/skills/${id}/secrets`, { key, value }), + deleteSecret: (id: string | number, key: string) => + http.delete(`/skills/${id}/secrets/${encodeURIComponent(key)}`), +} + +/** Shape returned by GET /skills/{id}/secrets. */ +export interface SkillSecretSummary { + key: string + /** Masked preview, e.g. "abc...xyz". Plaintext is never shipped. */ + preview: string + updatedAt: string } // ==================== Activity Feed (RFC-090 §4.5) ==================== diff --git a/mateclaw-ui/src/components/skill/SkillSecretsPanel.vue b/mateclaw-ui/src/components/skill/SkillSecretsPanel.vue new file mode 100644 index 00000000..350f15e2 --- /dev/null +++ b/mateclaw-ui/src/components/skill/SkillSecretsPanel.vue @@ -0,0 +1,433 @@ + + + + + diff --git a/mateclaw-ui/src/i18n/locales/en-US.ts b/mateclaw-ui/src/i18n/locales/en-US.ts index a556685a..560cecb6 100644 --- a/mateclaw-ui/src/i18n/locales/en-US.ts +++ b/mateclaw-ui/src/i18n/locales/en-US.ts @@ -2967,6 +2967,7 @@ export default { features: 'Features', security: 'Security', lessons: 'Lessons', + secrets: 'Secrets', memory: 'Memory', scanStatus: 'Scan status', missingDeps: 'Missing dependencies', @@ -3000,6 +3001,26 @@ export default { bindingImplicit: 'Implicit', clearLessons: 'Clear all', clearLessonsConfirm: 'Delete all recorded lessons for this skill? This cannot be undone.', + secretsHint: 'Key/value pairs injected as environment variables into this skill\'s script subprocess. Stored AES-encrypted; plaintext is never returned over the API. Example: tencent-meeting needs TENCENT_MEETING_TOKEN.', + secretsEmpty: 'No secrets configured for this skill.', + secretKey: 'Key', + secretValue: 'Value', + secretUpdatedAt: 'Last updated', + secretAdd: '+ Add secret', + secretAddTitle: 'Add secret', + secretEditTitle: 'Edit secret', + secretEdit: 'Edit', + secretDelete: 'Delete', + secretDialogHint: 'Saved immediately and injected into the script subprocess on next skill execution.', + secretKeyPlaceholder: 'e.g. TENCENT_MEETING_TOKEN', + secretKeyRule: 'Letters, digits and underscore only; first char must be a letter or underscore; ≤128 chars.', + secretValuePlaceholder: 'Paste the value (AES-encrypted server-side on submit)', + secretSaveSuccess: 'Secret saved', + secretSaveFailed: 'Failed to save', + secretDeleteConfirm: 'Delete secret "{key}"? This skill\'s scripts will no longer receive this environment variable.', + secretDeleteSuccess: 'Secret deleted', + secretDeleteFailed: 'Failed to delete', + secretLoadFailed: 'Failed to load secrets', }, runtime: { disabled: 'Disabled', diff --git a/mateclaw-ui/src/i18n/locales/zh-CN.ts b/mateclaw-ui/src/i18n/locales/zh-CN.ts index db577580..5fc5e23b 100644 --- a/mateclaw-ui/src/i18n/locales/zh-CN.ts +++ b/mateclaw-ui/src/i18n/locales/zh-CN.ts @@ -3059,6 +3059,7 @@ export default { features: '特性', security: '安全', lessons: '经验', + secrets: '密钥', memory: '记忆', scanStatus: '扫描状态', missingDeps: '缺失依赖', @@ -3092,6 +3093,26 @@ export default { bindingImplicit: '隐式可见', clearLessons: '全部清空', clearLessonsConfirm: '删除该 skill 的全部经验?操作不可撤销。', + secretsHint: '这些键值会作为环境变量注入 skill 脚本子进程。明文加密落盘,从不通过接口回传。例如腾讯会议技能需要 TENCENT_MEETING_TOKEN。', + secretsEmpty: '该 skill 还没有配置任何密钥。', + secretKey: '键名', + secretValue: '值', + secretUpdatedAt: '最近更新', + secretAdd: '+ 新增密钥', + secretAddTitle: '新增密钥', + secretEditTitle: '修改密钥', + secretEdit: '修改', + secretDelete: '删除', + secretDialogHint: '保存后立即生效,下次执行 skill 脚本时注入到子进程环境。', + secretKeyPlaceholder: '例如 TENCENT_MEETING_TOKEN', + secretKeyRule: '允许字母、数字与下划线;首字符不能是数字;长度 ≤ 128。', + secretValuePlaceholder: '粘贴值(提交后服务端 AES 加密保存)', + secretSaveSuccess: '密钥已保存', + secretSaveFailed: '保存失败', + secretDeleteConfirm: '删除密钥 "{key}"?此 skill 后续脚本将不再收到此环境变量。', + secretDeleteSuccess: '密钥已删除', + secretDeleteFailed: '删除失败', + secretLoadFailed: '加载密钥列表失败', }, runtime: { disabled: '已停用', diff --git a/mateclaw-ui/src/views/SkillMarket.vue b/mateclaw-ui/src/views/SkillMarket.vue index 6508d0ea..2a0fa40a 100644 --- a/mateclaw-ui/src/views/SkillMarket.vue +++ b/mateclaw-ui/src/views/SkillMarket.vue @@ -291,6 +291,9 @@ +