import type { Accessor } from '../accessor/base.ts'; import { type IndexConfig } from '../cache/index/config.ts'; import type { IndexCacheStore } from '../cache/index/store.ts'; import type { PredNode } from '../commands/builtin/find_eval.ts'; import type { RegisteredCommand } from '../commands/config.ts'; import type { RegisteredOp } from '../ops/registry.ts'; import type { CapacityResult, FileStat, PathSpec } from '../types.ts'; import type { DeltaHook } from '../watch/base.ts'; export interface FindOptions { name?: string | null; type?: string | null; minSize?: number | null; maxSize?: number | null; maxDepth?: number | null; minDepth?: number | null; nameExclude?: string | null; orNames?: string[] | null; iname?: string | null; pathPattern?: string | null; empty?: boolean | null; tree?: PredNode | null; mtimeMin?: number | null; mtimeMax?: number | null; } /** * The two keys the snapshot machinery reads out of a resource's state. * * `type` is the registry name, and it is what rebuilds the resource: * Python's `_resource_class_for` looks it up in the registry first and * only falls back to the mount's `resource_class` import path when it * misses. `config` is what `resourceStateRequiresOverride` scans for the * `` marker, which is what makes load demand a fresh config * instead of silently substituting an empty mount. * * This lived as a private interface in `workspace/snapshot/types.ts`, * where `ResourceState` still widens it with each backend's own keys; it * moved here so the `Resource` contract and the snapshot format name one * shape rather than two identical ones. Python needs no such type — * `get_state` is annotated `dict[str, Any]` — but a TS interface is not * assignable to `Record` (no implicit index signature), * so the literal twin would reject every named `XResourceState`. */ export interface ResourceStateBase { type: string; config?: unknown; needs_override?: boolean; } export declare function recordResourceRef(resource: Resource, ref: string): void; export declare function resourceRefOf(resource: Resource): string | null; export interface Resource { readonly kind: string; readonly prompt?: string; readonly writePrompt?: string; readonly indexTtl?: number; /** * Whether reads of this resource may be served from / written to the * local file cache. Defaults to false. A network-backed resource whose * content is read-mostly (e.g. object storage) sets this to true so * reads can be cached; a resource whose content is live (e.g. a * database collection) leaves it false so reads always hit the backend * and live follows (`tail -f`) are not masked by a cached snapshot. */ readonly cachesReads?: boolean; /** * Whether this resource carries enough version information for * snapshot+replay drift detection. When true, the resource's * {@link Resource.stat} must populate {@link FileStat.fingerprint} * (and optionally {@link FileStat.revision}) with stable per-path * markers. When false (the default), reads are treated as live-only * at replay time: no fingerprint is captured at snapshot, no drift * check fires at load. */ readonly supportsSnapshot?: boolean; /** * Whether {@link Resource.stat} can size every regular file without * fetching its content, i.e. {@link FileStat.size} is null only for * directories. True for byte stores that keep a length in their * metadata (ram, disk, redis, s3, gridfs); false for resources that * render content on read, where the size is unknowable until the bytes * exist (slack, gmail, notion, postgres rows.jsonl, dify documents). * * FUSE does not need this: direct_io + attrTimeout '0' + hydrate-on-open * make size-unknown files read correctly anyway. FSKit has no direct_io * equivalent, so a mount there is driven entirely by the reported size * and a false resource would serve silent empty files. Mirrors Python's * `BaseResource.SIZES_ALWAYS_KNOWN`. */ readonly sizesAlwaysKnown?: boolean; readonly index?: IndexCacheStore; readonly accessor?: Accessor; readonly opsMap?: Record; setIndex?(config?: IndexConfig): void; open(): Promise; close(): Promise; getState(): ResourceStateBase | Promise; loadState(state: ResourceStateBase): void | Promise; ops?(): readonly RegisteredOp[]; commands?(): readonly RegisteredCommand[]; streamPath?(path: PathSpec): AsyncIterable; readFile?(path: PathSpec): Promise; writeFile?(path: PathSpec, data: Uint8Array): Promise; appendFile?(path: PathSpec, data: Uint8Array): Promise; readdir?(path: PathSpec): Promise; stat?(path: PathSpec): Promise; exists?(path: PathSpec): Promise; mkdir?(path: PathSpec, options?: { recursive?: boolean; }): Promise; rmdir?(path: PathSpec): Promise; unlink?(path: PathSpec): Promise; rename?(src: PathSpec, dst: PathSpec): Promise; truncate?(path: PathSpec, length: number): Promise; copy?(src: PathSpec, dst: PathSpec): Promise; rmR?(path: PathSpec): Promise; du?(path: PathSpec): Promise; find?(path: PathSpec, options?: FindOptions): Promise; glob?(paths: readonly PathSpec[], prefix?: string): Promise; statfs?(): Promise; storageId?(): string; deltaHook?(): DeltaHook; } export declare function cachesReads(resource: Resource): boolean; export declare function sizesAlwaysKnown(resource: Resource): boolean; export declare abstract class BaseResource { #private; abstract readonly kind: string; readonly indexTtl: number; protected _index?: IndexCacheStore; get index(): IndexCacheStore; setIndex(config?: IndexConfig): void; private makeIndex; storageId(): string; statfs(): Promise; /** * The snapshot state of a resource that holds nothing of its own: the * class name, so `Workspace.load` can rebuild it. Storage-backed * resources override this to carry their bytes, config-backed ones to * carry their (redacted) config. Mirrors Python * `BaseResource.get_state`. */ getState(): ResourceStateBase | Promise; /** * Take back what {@link BaseResource.getState} put out. A no-op by * default, because the bare `{type}` carries nothing to restore. * Mirrors Python `BaseResource.load_state`. */ loadState(_state: ResourceStateBase): void | Promise; /** * Release what this resource owns, exactly once. The base teardown is * the index store: a mount configured `index: {type: redis}` holds a * client that nothing else closes, so without this a Node process * stays alive after `closeWorkspace`. * * A backend with its own handles (a db pool, an ssh channel) overrides * this and calls `super.close()` — its accessor is its own to close, * since the Accessor seam carries no lifecycle of its own. * * Mirrors Python `BaseResource.close` (`resource/base.py`). */ close(): Promise; } //# sourceMappingURL=base.d.ts.map