Files
ssh-manager/docs/superpowers/plans/2026-03-23-remote-to-many-multi-file.md
liumangmang c8fa3de679 docs: 添加 superpowers 规格与计划文档
记录 Remote->Many 多文件与相关实现计划/设计,便于后续追踪与复盘。
2026-03-24 13:43:08 +08:00

8.1 KiB

Remote -> Many Multi-File (Files Only) Implementation Plan

For agentic workers: REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Allow Transfers -> Remote -> Many to select multiple source files (files only) and transfer them to many targets.

Architecture: Frontend-only change. Expand sourcePaths x targetConnectionIds into a queue of items; each item uses existing backend /sftp/transfer-remote/tasks and SSE progress tracking.

Tech Stack: Vue 3 + TypeScript (Vite), Pinia, Spring Boot backend unchanged.


File Map

Modify:

  • frontend/src/views/TransfersView.vue (Remote -> Many UI + state)
  • frontend/src/components/SftpFilePickerModal.vue (add multi-select mode)
  • frontend/src/stores/transfers.ts (support sourcePaths[], targetMode, improve SSE unsubscription)

No backend changes required.


Task 1: Add multi-select mode to SftpFilePickerModal

Files:

  • Modify: frontend/src/components/SftpFilePickerModal.vue

  • Step 1: Update component API (props + emits)

    • Add optional prop: multiple?: boolean (default false).
    • Keep existing event: select for single selection.
    • Add new event: select-many for multi selection (payload: string[]).
  • Step 2: Add selection state and deterministic ordering

    • Track selected paths in selection-time order (array) + a set for O(1) membership.
    • File click behavior:
      • Directory: navigate into directory.
      • File:
        • if multiple: toggle selection.
        • else: emit select(path) and close (keep existing).
    • Persist selection across directory navigation within the modal session.
    • If a file is toggled off and later toggled on again, re-add it at the end of the array (selection-time order).
  • Step 2.1: Add a visible selection indicator

    • When multiple, render a checkbox in each file row (checked = selected).
    • Directory rows never render a checkbox.
  • Step 3: Add modal footer controls

    • Add footer actions when multiple:
      • Confirm (N) (disabled when N=0) emits select-many(paths) then closes.
      • Cancel closes without emitting.
    • Add a "Selected (N)" compact summary area with remove (x) for each selected file.
  • Step 4: Manual verification

    • Run: npm --prefix frontend run build
    • Expected: build succeeds.
    • Manual:
      • Open picker, select multiple files across different directories, ensure selections persist.
      • Remove items in the "Selected" summary.
      • Confirm emits all paths in selection-time order.
      • Verify search still filters entries correctly.
      • Verify hidden-files toggle still works as before.
  • Step 5: Commit (optional)

    • If doing commits:
      • Run: git add frontend/src/components/SftpFilePickerModal.vue
      • Run: git commit -m "feat: add multi-select mode to SFTP file picker"

Task 2: Update TransfersView Remote -> Many UI for multi-file selection

Files:

  • Modify: frontend/src/views/TransfersView.vue

  • Step 1: Update remote source model

    • Replace remoteSourcePath: string with remoteSourcePaths: string[].
    • UI:
      • Show count + list of selected paths (basename + full path tooltip).
      • Provide remove per item + clear all.
  • Step 1.1: Add client-side validation for typed paths

    • Before starting Remote -> Many:
      • validate each remoteSourcePaths[i] is non-empty
      • validate it does not end with / (heuristic directory indicator)
    • If invalid: show inline error and block Start.
  • Step 2: Wire picker in multi mode

    • Call <SftpFilePickerModal :multiple="true" ... />.
    • Listen for select-many and merge paths into remoteSourcePaths:
      • append + de-duplicate by full path while preserving first-seen order.
  • Step 3: Add target path mode toggle

    • Add UI control: Directory (default) vs Exact Path.
    • Behavior:
      • Directory treats input as directory; normalize to end with /; append source filename.
      • Exact Path uses raw input as full file path (only allowed when exactly one source file is selected).
    • Disable Exact Path when remoteSourcePaths.length > 1 (force directory mode).
  • Step 3.1: Update Start button enablement

    • Require remoteSourceConnectionId != null.
    • Require remoteSourcePaths.length > 0.
    • Require remoteSelectedTargets.length > 0.
    • Also require validation in Step 1.1 passes.
  • Step 4: Add basename collision warning

    • When Directory mode and remoteSourcePaths contains duplicate basenames:
      • show a warning dialog/banner before starting (acknowledge to proceed).
      • message mentions overwrite will happen on each selected target connection.
  • Step 5: Source connection change behavior

    • When remoteSourceConnectionId changes:
      • clear remoteSourcePaths
      • remove source id from remoteSelectedTargets if present
  • Step 6: Manual verification

    • Run: npm --prefix frontend run build
    • Manual:
      • Pick 2 files, choose 2 targets -> queue shows 4 items.
      • Toggle Directory/Exact Path:
        • multi-file forces Directory
        • single-file allows Exact Path
      • Change source connection -> selected files cleared.
  • Step 7: Commit (optional)

    • If doing commits:
      • Run: git add frontend/src/views/TransfersView.vue frontend/src/components/SftpFilePickerModal.vue
      • Run: git commit -m "feat: allow selecting multiple remote source files"

Task 3: Update transfers store to support multi-file Remote -> Many + safe SSE unsubscription

Files:

  • Modify: frontend/src/stores/transfers.ts

  • Step 1: Update store API and types

    • Change startRemoteToMany params:
      • from: sourcePath: string
      • to: sourcePaths: string[]
    • Add param: targetMode: 'dir' | 'path'.
  • Step 2: Implement target path resolution in store

    • For each sourcePath:
      • compute filename as basename.
      • if targetMode === 'dir': normalize targetDirOrPath to end with / and append filename.
      • if targetMode === 'path': require sourcePaths.length === 1 and use raw targetDirOrPath.
  • Step 3: Expand items as sourcePaths x targets

    • Create a TransferItem per pair.
    • Label includes both source path and target connection.
    • Concurrency limiter applies across the full list.
  • Step 4: Fix SSE subscription cleanup

    • In waitForRemoteTransfer and upload-wait logic:
      • ensure the returned unsubscribe is called on terminal states (success/error/cancel).
      • still store unsubscribe in the run controller so cancelRun can close any active subscriptions.
  • Step 5: Manual verification

    • Run: npm --prefix frontend run build
    • Manual:
      • Start a small run and ensure completion does not leave SSE connections open (observe via browser network if needed).
      • Cancel a run and confirm subscriptions close promptly.
  • Step 6: Commit (optional)

    • If doing commits:
      • Run: git add frontend/src/stores/transfers.ts
      • Run: git commit -m "fix: close SSE subscriptions for transfer tasks"

Task 4: End-to-end verification

Files:

  • Verify: frontend/src/views/TransfersView.vue

  • Verify: frontend/src/components/SftpFilePickerModal.vue

  • Verify: frontend/src/stores/transfers.ts

  • Step 1: Frontend build

    • Run: npm --prefix frontend run build
    • Expected: success.
  • Step 2: Smoke test (manual)

    • Start app (if needed):
      • Backend: mvn -f backend/pom.xml spring-boot:run
      • Frontend: npm --prefix frontend run dev
    • If deps aren't installed:
      • Frontend: npm --prefix frontend install
    • Use Transfers:
      • Remote -> Many: pick 2-3 files, 2 targets, Directory mode.
      • Verify files arrive at target directory with correct names.
      • Verify errors are per-item, run continues for other items.
  • Step 3: Commit (optional)

    • If doing a final commit:
      • Run: git add frontend/src/views/TransfersView.vue frontend/src/components/SftpFilePickerModal.vue frontend/src/stores/transfers.ts
      • Run: git commit -m "feat: support multi-file Remote -> Many transfers"