From 373b462195b80ff8d44200720f781d1ca9d8bfd6 Mon Sep 17 00:00:00 2001 From: matevip Date: Sat, 16 May 2026 14:51:31 +0800 Subject: [PATCH] feat(ui): export and import buttons for agent memory snapshot --- mateclaw-ui/src/api/index.ts | 38 +++ mateclaw-ui/src/i18n/locales/en-US.ts | 22 ++ mateclaw-ui/src/i18n/locales/zh-CN.ts | 22 ++ mateclaw-ui/src/views/AgentContext.vue | 441 ++++++++++++++++++++++++- 4 files changed, 522 insertions(+), 1 deletion(-) diff --git a/mateclaw-ui/src/api/index.ts b/mateclaw-ui/src/api/index.ts index 4da7abcb..127233f6 100644 --- a/mateclaw-ui/src/api/index.ts +++ b/mateclaw-ui/src/api/index.ts @@ -583,6 +583,44 @@ export const agentContextApi = { http.get(`/agents/${agentId}/workspace/prompt-files`), setPromptFiles: (agentId: string | number, files: string[]) => http.put(`/agents/${agentId}/workspace/prompt-files`, { files }), + + // Memory snapshot — export downloads a ZIP via native fetch (axios R + // interceptor would mis-handle the binary body); import + preview go + // through axios with multipart so the standard auth / workspace headers + // flow automatically. + exportMemorySnapshot: async (agentId: string | number): Promise => { + const token = localStorage.getItem('token') + const workspaceId = localStorage.getItem('mc-workspace-id') + const headers: Record = {} + if (token) headers.Authorization = `Bearer ${token}` + if (workspaceId) headers['X-Workspace-Id'] = workspaceId + const url = `/api/v1/agents/${agentId}/workspace/memory/export` + const response = await fetch(url, { headers }) + if (!response.ok) { + // Try to parse the standard R error envelope for a useful message. + let detail = `HTTP ${response.status}` + try { + const body = await response.json() + if (body && typeof body.msg === 'string') detail = body.msg + } catch { /* ignore — non-JSON body */ } + throw new Error(detail) + } + return response.blob() + }, + previewImportMemorySnapshot: (agentId: string | number, file: File) => { + const form = new FormData() + form.append('file', file) + return http.post(`/agents/${agentId}/workspace/memory/import/preview`, form, { + headers: { 'Content-Type': 'multipart/form-data' }, + }) + }, + applyImportMemorySnapshot: (agentId: string | number, file: File) => { + const form = new FormData() + form.append('file', file) + return http.post(`/agents/${agentId}/workspace/memory/import`, form, { + headers: { 'Content-Type': 'multipart/form-data' }, + }) + }, } // ==================== Security ==================== diff --git a/mateclaw-ui/src/i18n/locales/en-US.ts b/mateclaw-ui/src/i18n/locales/en-US.ts index 129f4314..8ad5eb1f 100644 --- a/mateclaw-ui/src/i18n/locales/en-US.ts +++ b/mateclaw-ui/src/i18n/locales/en-US.ts @@ -1021,6 +1021,28 @@ export default { invalidFilename: 'Please enter a valid .md filename', loadFailed: 'Failed to load file list', loadFileFailed: 'Failed to load file content', + exportSnapshot: 'Export snapshot', + exportSnapshotTitle: 'Export memory snapshot', + exportSnapshotDesc: "Bundle this agent's AGENTS / MEMORY / PROFILE / SOUL / KNOWLEDGE.md plus the memory/ daily ledger into a ZIP, named with the current timestamp. The enabled / sortOrder flags are not exported.", + exportSuccess: 'Snapshot exported', + exportFailed: 'Export failed', + importSnapshot: 'Import snapshot', + importSnapshotTitle: 'Import memory snapshot', + importSelectPrompt: 'Pick a ZIP snapshot to import', + importPreviewing: 'Inspecting snapshot...', + importPreviewFailed: 'Failed to inspect snapshot', + importApplying: 'Applying...', + importApplied: 'Applied {applied}, skipped {skipped}', + importFailed: 'Import failed', + importPreviewWillCreate: 'Will create', + importPreviewWillUpdate: 'Will overwrite', + importPreviewWillSkip: 'Skipped', + importPreviewEmpty: 'Nothing to import', + importPreviewCreateNone: '(none)', + importPreviewUpdateMeta: '{old} B → {new} B', + importPreviewSkipReason: 'reason: {reason}', + importConfirmHint: 'Confirming will overwrite existing files with the same name.', + importApply: 'Confirm import', }, agents: { kicker: 'Employee Studio', diff --git a/mateclaw-ui/src/i18n/locales/zh-CN.ts b/mateclaw-ui/src/i18n/locales/zh-CN.ts index 03329867..d2468b97 100644 --- a/mateclaw-ui/src/i18n/locales/zh-CN.ts +++ b/mateclaw-ui/src/i18n/locales/zh-CN.ts @@ -913,6 +913,28 @@ export default { invalidFilename: '请输入有效的 .md 文件名', loadFailed: '加载文件列表失败', loadFileFailed: '加载文件内容失败', + exportSnapshot: '导出快照', + exportSnapshotTitle: '导出记忆快照', + exportSnapshotDesc: '把当前智能体的 AGENTS / MEMORY / PROFILE / SOUL / KNOWLEDGE.md 以及 memory/ 下的每日日记打包成 ZIP,按当前时间命名下载。enabled / sortOrder 不会导出。', + exportSuccess: '快照已导出', + exportFailed: '导出失败', + importSnapshot: '导入快照', + importSnapshotTitle: '导入记忆快照', + importSelectPrompt: '请选择 ZIP 格式的记忆快照', + importPreviewing: '正在解析快照...', + importPreviewFailed: '解析快照失败', + importApplying: '正在应用...', + importApplied: '已应用 {applied} 个,跳过 {skipped} 个', + importFailed: '导入失败', + importPreviewWillCreate: '将创建', + importPreviewWillUpdate: '将覆盖', + importPreviewWillSkip: '跳过', + importPreviewEmpty: '没有可导入的文件', + importPreviewCreateNone: '无', + importPreviewUpdateMeta: '{old} 字节 → {new} 字节', + importPreviewSkipReason: '原因:{reason}', + importConfirmHint: '确认导入会覆盖现有同名文件。', + importApply: '确认导入', }, agents: { kicker: '员工工作室', diff --git a/mateclaw-ui/src/views/AgentContext.vue b/mateclaw-ui/src/views/AgentContext.vue index 47446745..1b04ba52 100644 --- a/mateclaw-ui/src/views/AgentContext.vue +++ b/mateclaw-ui/src/views/AgentContext.vue @@ -34,12 +34,41 @@ + + +

{{ t('agentContext.coreFilesDesc') }}

@@ -167,6 +196,99 @@ + + +
+ +
+
+