feat: vault core/CLI/workbench, vaultd token path, filetree view-state cleanup

Land password-vault dedicated workbench and mnote-vault-core/CLI, agent token
read path design, vault transport split, and retire obsolete filetree smokes.
Ignore local vault reimport scripts that trip secret scanners.
This commit is contained in:
Agent Board
2026-07-24 11:36:06 +08:00
parent b798f628ee
commit bc6f8488ee
41 changed files with 13072 additions and 2316 deletions
@@ -183,6 +183,49 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
runtimeState.activeTreeContextMenu = null;
}
/**
* Product clipboard used by menu paste + keyboard paste.
* Keep keyboard-module clipboard and runtimeState in lockstep so
* 「粘贴到此处」enables after either Ctrl+C/X or menu cut/copy.
*/
function currentSidebarFileTreeClipboard() {
if (runtimeState.sidebarFileTreeClipboard
&& Array.isArray(runtimeState.sidebarFileTreeClipboard.rowIds)
&& runtimeState.sidebarFileTreeClipboard.rowIds.length) {
return runtimeState.sidebarFileTreeClipboard;
}
var kb = window.__mnoteFileTreeKeyboardRuntime;
var fromKb = kb && typeof kb.getSidebarFileTreeClipboard === 'function'
? kb.getSidebarFileTreeClipboard()
: (kb && kb.sidebarFileTreeClipboard) || null;
if (fromKb && Array.isArray(fromKb.rowIds) && fromKb.rowIds.length) {
// Heal dual-clipboard drift (keyboard module wrote first).
runtimeState.sidebarFileTreeClipboard = fromKb;
return fromKb;
}
return null;
}
function setSidebarFileTreeClipboard(action, rowIds) {
var next = action && Array.isArray(rowIds) && rowIds.length
? { action: action, rowIds: rowIds.slice() }
: null;
runtimeState.sidebarFileTreeClipboard = next;
var kb = window.__mnoteFileTreeKeyboardRuntime;
if (kb && typeof kb.setSidebarFileTreeClipboard === 'function') {
if (next) {
kb.setSidebarFileTreeClipboard(next.action, next.rowIds);
} else if (typeof kb.clearSidebarFileTreeClipboard === 'function') {
kb.clearSidebarFileTreeClipboard();
}
} else if (next) {
document.documentElement.setAttribute('data-mnote-filetree-clipboard-action', next.action);
} else {
document.documentElement.removeAttribute('data-mnote-filetree-clipboard-action');
}
return next;
}
function copyTreeContextValue(value, actionName) {
var text = String(value || '');
var done = function() {
@@ -618,6 +661,30 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
});
return;
}
if (action === 'cut' || action === 'copy') {
var clipboardRows = selectedSidebarFileTreeRows();
if (!clipboardRows.length && trigger && trigger.closest) {
var single = trigger.closest('.tree-row[data-shell-mode="filetree"]');
if (single instanceof HTMLElement) clipboardRows = [single];
}
var clipboardRowIds = clipboardRows.map(function(row) {
return String(row.getAttribute('data-row-id') || '').trim();
}).filter(Boolean);
if (!clipboardRowIds.length) {
recordFileTreeActionStatus('skipped', Object.assign({}, detail, { reason: 'empty-selection' }));
return;
}
setSidebarFileTreeClipboard(action, clipboardRowIds);
recordFileTreeAction(action, Object.assign({}, detail, {
sourceRowIds: clipboardRowIds,
clipboardAction: action,
}));
recordFileTreeActionStatus('applied', Object.assign({}, detail, {
sourceRowIds: clipboardRowIds,
clipboardAction: action,
}));
return;
}
if (action === 'paste-into') {
var pasteRow = trigger && trigger.closest ? trigger.closest('.tree-row[data-shell-mode="filetree"]') : trigger;
recordFileTreeAction('paste-into', detail);
@@ -1228,7 +1295,10 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
{ action: 'new-file', icon: 'note_add', label: 'New File', when: '!workspace.readonly' },
{ action: 'new-folder', icon: 'create_new_folder', label: 'New Folder', disabled: currentSourceKind() !== 'local_folder', title: currentSourceKind() === 'local_folder' ? '在当前目录下创建子文件夹' : '仅 local folder 支持创建文件夹', when: '!workspace.readonly' },
{ action: 'toggle-sidebar-folder-shortcut', icon: 'star', label: '加入/取消星标置顶', disabled: currentSourceKind() !== 'local_folder' || (detail.rowKind !== 'folder' && detail.rowKind !== 'directory'), title: currentSourceKind() === 'local_folder' ? '把当前文件夹加入或移出星标置顶' : '仅 local folder 文件夹支持星标置顶' },
{ action: 'paste-into', icon: 'content_paste', label: '粘贴到此处', disabled: !runtimeState.sidebarFileTreeClipboard, title: runtimeState.sidebarFileTreeClipboard ? '粘贴到当前文件树目标' : '剪贴板为空', when: '!workspace.readonly' },
{ separator: true },
{ action: 'cut', icon: 'content_cut', label: '剪切', shortcut: 'Ctrl+X', when: '!workspace.readonly' },
{ action: 'copy', icon: 'content_copy', label: '复制', shortcut: 'Ctrl+C' },
{ action: 'paste-into', icon: 'content_paste', label: '粘贴到此处', shortcut: 'Ctrl+V', disabled: !currentSidebarFileTreeClipboard(), title: currentSidebarFileTreeClipboard() ? '粘贴到当前文件树目标' : '剪贴板为空', when: '!workspace.readonly' },
{ action: 'refresh', icon: 'refresh', label: 'Refresh' },
{ action: 'collapse-all', icon: 'unfold_less', label: 'Collapse All' },
{ action: 'reveal', icon: 'my_location', label: 'Reveal' },
@@ -1929,9 +1999,16 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
var runtimeFn = fileTreeRuntimeFunction('fileTreeChildCount');
if (runtimeFn) return runtimeFn(documentId, fileTreeRuntimeDeps());
if (!documentId) return 0;
var row = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape('doc:' + documentId) + '"]');
var row = document.querySelector(
'#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape('doc:' + documentId) + '"],'
+ '#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape(documentId) + '"],'
+ '#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-document-id="' + cssEscape(documentId) + '"],'
+ '#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-node-id="' + cssEscape(documentId) + '"]'
);
var node = row ? row.closest('.tree-node') : null;
var children = node ? node.querySelectorAll(':scope > .tree-children > .tree-node > .tree-row[data-shell-mode="filetree"][data-row-kind="document"], :scope > .tree-children > .tree-node > .tree-row[data-shell-mode="filetree"][data-row-kind="doc"]') : [];
var children = node
? node.querySelectorAll(':scope > .tree-children > .tree-node > .tree-row[data-shell-mode="filetree"]')
: [];
return children.length;
}
@@ -1944,6 +2021,45 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
}, targetRow) || null;
}
function fileTreeParentIdFromAttr(parentAttr) {
var raw = String(parentAttr || '').trim();
if (!raw) return null;
if (raw.indexOf('doc:') === 0) return raw.slice(4);
return raw;
}
/**
* Wolai-style move target:
* - inside → nest under target (folder/dir/doc)
* - before/after → same parent as target, sortOrder = sibling index (+1 for after)
*/
function resolveFileTreeMoveTarget(targetRow, position) {
if (!(targetRow instanceof HTMLElement)) {
return { parentId: null, sortOrder: 0 };
}
var pos = position || 'inside';
if (pos === 'inside') {
var nestParentId = fileTreeMoveTargetParentId(targetRow);
return {
parentId: nestParentId,
sortOrder: nestParentId ? fileTreeChildCount(nestParentId) : 0,
};
}
var parentAttr = targetRow.getAttribute('data-parent-id') || '';
var parentId = fileTreeParentIdFromAttr(parentAttr);
var siblings = Array.from(
document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"]')
).filter(function(row) {
return (row.getAttribute('data-parent-id') || '') === parentAttr;
});
var index = siblings.indexOf(targetRow);
if (index < 0) index = 0;
return {
parentId: parentId,
sortOrder: Math.max(0, index + (pos === 'after' ? 1 : 0)),
};
}
function fileTreeMoveSourceId(row) {
if (!(row instanceof HTMLElement)) return '';
return fileTreeRowDocumentId(row)
@@ -1956,7 +2072,10 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
var rows = fileTreeRowsByRowIds(rowIds || []);
if (rows.length === 0) return false;
var copy = Boolean(options && options.copy);
var targetParentId = fileTreeMoveTargetParentId(targetRow);
var dropPosition = (options && options.dropPosition) || 'inside';
var resolved = resolveFileTreeMoveTarget(targetRow, dropPosition);
var targetParentId = resolved.parentId;
var baseSortOrder = typeof resolved.sortOrder === 'number' ? resolved.sortOrder : 0;
var workspaceId = resolveWorkspaceId(targetRow || document.body);
var writable = await ensureFileTreeWritableTarget('move', targetRow, rowIds || [], copy);
if (!writable) return false;
@@ -1983,7 +2102,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
workspaceId: workspaceId,
documentId: sourceId,
parentId: targetParentId,
sortOrder: targetParentId ? fileTreeChildCount(targetParentId) + i : i
sortOrder: baseSortOrder + i
});
} catch (error) {
failures.push(sourceId + ': ' + (error && error.message ? error.message : '移动失败'));
@@ -2014,21 +2133,24 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
}
async function pasteSidebarFileTreeClipboard(trigger) {
if (!runtimeState.sidebarFileTreeClipboard || !Array.isArray(runtimeState.sidebarFileTreeClipboard.rowIds) || runtimeState.sidebarFileTreeClipboard.rowIds.length === 0) return false;
var clipboard = currentSidebarFileTreeClipboard();
if (!clipboard) return false;
var targetRow = trigger instanceof HTMLElement ? trigger : null;
if (!targetRow && sidebarFileTreeSelection.focusedRowId) {
targetRow = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape(sidebarFileTreeSelection.focusedRowId) + '"]');
}
var targetDocumentId = fileTreeMoveTargetParentId(targetRow) || currentDocumentId();
var action = runtimeState.sidebarFileTreeClipboard.action === 'cut' ? 'move' : 'copy';
var action = clipboard.action === 'cut' ? 'move' : 'copy';
recordFileTreeAction('paste', {
rowId: targetRow ? targetRow.getAttribute('data-row-id') || '' : '',
documentId: targetDocumentId,
sourceRowIds: runtimeState.sidebarFileTreeClipboard.rowIds,
clipboardAction: runtimeState.sidebarFileTreeClipboard.action
sourceRowIds: clipboard.rowIds,
clipboardAction: clipboard.action
});
var ok = await moveSidebarFileTreeRows(runtimeState.sidebarFileTreeClipboard.rowIds, targetRow, { copy: action === 'copy' });
if (ok && action === 'move') runtimeState.sidebarFileTreeClipboard = null;
var ok = await moveSidebarFileTreeRows(clipboard.rowIds, targetRow, { copy: action === 'copy' });
if (ok && action === 'move') {
setSidebarFileTreeClipboard(null, []);
}
return ok;
}
@@ -2206,6 +2328,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
postSidebarFileTreeJson,
fileTreeRowsByRowIds,
fileTreeChildCount,
resolveFileTreeMoveTarget,
moveSidebarFileTreeRows,
pasteSidebarFileTreeClipboard,
deleteSelectedSidebarFileTreeRows,