mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-04 06:29:42 +02:00
fix(files): serve remote-case attachments, the path a click takes outside the case
A clicked path that points OUTSIDE the case directory goes through the attachment routes (the frontend's `_isExternalPreviewPath` sends every absolute path not under `workingDir` to `POST /attachments`), and those had the same local-`fs` assumption as file-raw: `realpathSync`/`fs.stat` on a path that only exists on the remote host, so the file never opened — the case the #415 report was actually about. - `registerExternalAttachment()` accepts `remote` and resolves through `remoteProbePaths` (canonical path, size/mtime, kind, plus the workspace root for the confinement check). Everything around it — blocklist, extension allowlist, workspace confinement, registry/dedupe — is now shared by both branches, so the remote path cannot drift from the local one. - The by-id routes (`raw`, `preview`, `thumbnail`), the metadata poll and the attachment history list resolve over ssh too. `raw` streams with the same Range contract as file-raw; `preview` (office) and `thumbnail` answer 400 for a remote record; an unreachable host answers 502, a vanished file 404. - Which host a record is read from follows the SESSION, never the path string: the same absolute path is a different file on each host, and a remote session never falls back to a local file with that name. - Codex generated artifacts keep force-workspace confinement for a remote case: the well-known artifact directories are anchored at THIS host's home, so only a file inside the remote workspace is trusted. Still local-only by design: writes, office conversion, thumbnails, the file tree/picker and tail-file.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { vi } from 'vitest';
|
||||
import type { SessionStatus, SessionRemote } from '../../src/types.js';
|
||||
import type { SessionAttachmentHistoryItem, SessionStatus, SessionRemote } from '../../src/types.js';
|
||||
|
||||
/**
|
||||
* Enhanced mock session for testing RespawnController.
|
||||
@@ -19,6 +19,12 @@ export class MockSession extends EventEmitter {
|
||||
* over ssh instead of with local `fs` (#415).
|
||||
*/
|
||||
remote?: SessionRemote;
|
||||
/** Mirrors Session.attachmentHistory (the attachment panel's source of truth). */
|
||||
attachmentHistory: SessionAttachmentHistoryItem[] = [];
|
||||
/** Mirrors Session.getAttachmentHistoryForPersist(). */
|
||||
getAttachmentHistoryForPersist(): SessionAttachmentHistoryItem[] {
|
||||
return this.attachmentHistory;
|
||||
}
|
||||
/**
|
||||
* The REAL union, deliberately. This used to be `'idle' | 'working'`, and
|
||||
* `'working'` is not a `SessionStatus` at all — so `signalForStatus()` fell to its
|
||||
|
||||
Reference in New Issue
Block a user