Navigace
ELOWEN · Dokumentace

Poslední aktualizace: 20. října 2026

Procházet dokumentaci · files implementation

Tento článek zatím není přeložen. Je dostupný v angličtině.

Developer reference

files implementation

Cross-target transfers

lib/transfer.mjs defines the TransferFile tool; index.mjs registers it. The tool delegates admission and byte movement to ctx.host.projectFiles().transfer, restricted by core to Files with reads:['project-files']. Core reuses Sandbox local/project operations and the shared guestFiles stream/upload client. Both endpoints are {kind:'project',projectId,path}, including paired computers; no endpoint names a selected computer. A computer endpoint uses a daemon binding, native path resolution and member-only canExecute admission, including for administrators. The default permission is ask. Source reads and optional source removal use the same observed version. A destination is create-only unless its explicit replacement version is supplied. Source removal follows confirmed commit, and cancellation, changed files or an unknown commit never trigger a second attempt. Progress uses onUpdate with acknowledged destination bytes. No whole-file buffer or persisted job is created. The shared local path resolver also serves ordinary local Files operations.

Host Glob traversal

lib/search.mjs::walkFiles(root, limit) returns a promise for { files, unreadableDirectories }. Glob awaits the sequential depth-first walk, preserving directory entry order, ignored directories and the existing rule that descendant symlinks are not followed. A root symlink retains the existing stat behavior. Glob requests WALK_CAP + 1 to distinguish an exact-cap tree from an incomplete walk. A descendant EACCES skips that directory and records its path; Glob marks details.walkIncomplete, includes details.unreadableDirectories and displays an incompleteness notice. Root permission failures and all other I/O errors propagate through the existing tool error path. Directory reads yield through filesystem promises; traversal remains sequential and has no new cache, timeout or cap. Each directory listing is still materialized as a whole. Glob's matching, modification-time sort and result shaping keep their existing main-loop cost after the walk.

PDF reads and target versions

Read uses the opening guest-file block only for classification, then runs Poppler through managed.mjs::exec on the managed guest or paired computer. The PDF is not downloaded for hashing. The opening content-backed version and a fresh stat after conversion must agree. Only a fully visible render records markFileVersionRead and emits details.fileVersion; selected pages, truncation and omitted images do not authorize mutation. Host PDFs retain their whole-file SHA-256 baseline.

readState.mjs stores either a hash or a target version in its existing bounded map. The spawn replay hook accepts a version only with a local or managed projectRef, using the same target/path key as the live read. Write and Edit pass the newly read target version to readGuardError, then retain the provider's compare-and-swap write. Another computer or a host path cannot reuse that authorization. Concurrent changes during conversion, after Read or between the guard and CAS are refused. No new persistent state or transfer protocol is introduced.

documents.mjs reports missing converters at all PDF stages with the target OS's install command. Local execution loss keeps LOCAL_EXECUTION_LOST; cancellation and permission errors retain their typed failure metadata, and the adapter cancels and releases the execution lease. Conversion never falls back to a server process. Local and managed Glob errors use their own cwd and never call the host sibling-suggestion reader.

File mutation serialization

File mutations: Write and Edit share the file-local queuedMutation entry. Host mutations serialize through withFileMutationQueue and recheck path authority inside the queue. Managed mutations use provider CAS without a guest-local queue; read-before-write guards and result details remain tool-owned.

Diff computations

Registration binds lib/diff.mjs through ctx.host.computation with the input-bounded local option. It calls only lib/editCore.mjs::displayDiff/unifiedPatch, once each in one job; Edit planning and all existing 200-line display and 800-line patch rules stay in editCore. The wrapper returns [diff] when the original patch is undefined and [diff, patch] otherwise. Write, ordinary Edit and create-only Edit use this one binding in both host and managed execution. Core owns the local/worker decision, private async string transport and lifecycle cancellation.

The read guard, write and read-state update complete before projection. The host file queue remains held until the diff result settles, so another mutation of the same file cannot overtake it; other files remain independent. Post-write projection does not receive the tool signal: an already aborted call, abort while waiting in the host queue, abort at the write or during diff still returns the original successful result after a completed mutation. Queue admission and mutation cancellation remain unchanged. Plugin lifecycle stop still cancels the computation. A genuine worker error after writing never retries or falls back to synchronous work; it reports that the file was written and asks the caller to read current contents before another mutation. Success text, hashes, file-access metadata and optional diff/patch fields retain their original shapes. No new Write size guard or projection timeout is imposed.

File access metadata

A Read, Write or Edit result that shows or changes a file carries details.fileAccess.path, the absolute path that core reads to activate a .elowen/rules instruction module (src/brain/session/instructionActivation.ts). Refusals, errors, reads past the end of a file and a PDF render that is not fully visible carry no fileAccess, so no module loads from a file the model did not see; an empty file read still carries it.

Internal ownership

This section is for maintainers of the plugin. It describes which module owns each behavior; end users do not need it.

index.mjs registers the tools and the session-spawn replay hook. It owns path authorization, host/managed selection, result path metadata and mutation ordering. Helpers import their concrete dependencies directly; they do not register tools or widen access.

  • lib/editCore.mjs owns exact and opt-in fuzzy edit planning, BOM/line-ending preservation, and the existing display-diff and unified-patch caps.
  • lib/diff.mjs is the computation entry bound through ctx.host.computation: one job renders the display diff and the unified patch that Write and Edit attach to their result.
  • lib/transfer.mjs owns the TransferFile tool definition; admission, streaming and the move commit belong to core's project-file seam.
  • lib/documents.mjs owns PDF page selection, bounded Poppler execution and notebook rendering. The Read handler supplies the resolved read cap, PDF page cap and image support, then decides whether the visible result authorizes mutation.
  • lib/readState.mjs owns the single bounded session/file map, the classification probe readFileProbe, whole-file SHA-256 or target-version baselines, range deduplication, history replay, and streamed text snapshots. Read, Write, Edit and the spawn hook use this same module instance. Replay retains distinct host paths, managed:<projectId>\0<path> keys and local:<projectId>\0<path> keys, the last one case-folded on Windows; missing sessions keep the existing inert guard behavior. State is capped at 64 sessions and 512 files per session. Host text reads retain the selected page while hashing and counting the complete file through sequential asynchronous 64 KiB file-handle reads, allowing the event loop to serve other work between chunks. Even a small page still scans the whole file to preserve the SHA-256 mutation baseline and total line count. The same open handle is checked with bigint stats before and after the scan; changed device, inode, size or nanosecond modification/change times reject the snapshot. The classification probe must also match, and the handle closes on success and on errors before Read can record authorization. Managed reads consume readGuestStream from elowen-plugin-shared/guestFiles over their authorized provider binding. A complete opening read is the same snapshot's final page, so a file fitting one 128 KiB block costs exactly one guest read, including classification and text formatting. Larger snapshots pin subsequent pages to the opening version; the final read rehashes and needs no closing stat. A later write still compares against a freshly hashed CAS version.
  • lib/search.mjs owns Glob matching/traversal and the shared runRg executor for Search, Grep and --type-list, including stable paging, the host type-name cache and transport-failure classification. It preserves the five-second timeout, 500-column result bound and shared traversal cap. Missing-path messages stay with the tool handlers.
  • lib/imageFormats.mjs owns the frozen magic-byte parser and inline image limits shared by Read and document rendering.
  • lib/toolResults.mjs owns Files' result metadata wrappers over elowen-plugin-shared/toolResult, preserving successful results, actionable refusals and error shapes.

managed.mjs owns guest path resolution, live provider binding, cancellation, stored-output integrity routing and guest command execution. All file I/O sequences use elowen-plugin-shared/guestFiles: the opening read is reused by whole-file or streamed reads, small CAS writes use one operation, larger writes use shared uploads, parents are created only after parent_missing, and listing pages and walk use the same client. Tool bounds, read-before-write authorization and result wording stay with Files.

Tests import helpers from their owning modules. The plugin entry exports only register; there are no compatibility re-exports or additional public plugin seams.