refactor(layout): items 33+35 — module homes and workspace/config placement
Block E items 33 (T3-3 module homes) and 35 (T3-2 workspace/config
placement) from the cleanup plan. Item 34 (T3-1 runtime layout) was in
scope but is already closed on main; the verification is in the PR body.
Layout parity gate: 286 -> 269 unexcused divergences, baseline lowered.
Item 33 — module homes:
- py utils/fingerprint.py -> watch/fingerprint.py (all six consumers
are watch machinery; TS is already at watch/fingerprint.ts)
- py workspace/provision/rollup.py -> provision/rollup.py (no workspace
imports; TS is at the leaf provision/rollup.ts)
- py workspace/provision/builtins.py deleted (one function, one caller;
TS keeps it module-level in provision_node.ts)
- py 9 snapshot key enums -> new workspace/snapshot/keys.py
- py IndexType -> cache/index/config.py, CacheType -> cache/file/config.py
- ts node/cache/redis/file.ts -> node/cache/file/redis.ts, taking
add.lua with it (read relatively by its only consumer and copied by
an explicit tsup path, so tsup.config.ts moves with them)
- ts new core/src/concurrency/limiter.ts, replacing the private
Semaphore in accessor/dify.ts
NodeMetaKey deliberately stays beside NodeMeta: it names NodeMeta's own
fields, and importing the snapshot package from mount/namespace closes a
real cycle (namespace -> snapshot.keys -> snapshot/__init__ -> api ->
state -> namespace).
Item 35 — workspace/config placement:
- ts core/workspace/workspace.ts -> core/workspace/workspace/workspace.ts
- ts node/workspace/mount_spec.ts -> core/workspace/mount/spec.ts
- ts new core/workspace/workspace/guard.ts (three call sites converged
onto it from their own typeof === 'string' tests)
- ts new node/workspace/workspace/kernel_mounts.ts (NodeWorkspace
211 -> 169 lines, now delegations)
- ts server/src/config.ts -> node/src/config.ts, exported from the node
barrel so a mirage-node consumer can load a YAML workspace config
Three bugs fixed in the dify semaphore on the way, each pinned by a test
verified to fail against the old code: release() returned the permit to
the pool before waking the waiter (two holders at capacity 1), double
release inflated the count, and maxConcurrency < 1 was silently clamped
where python raises.
Also drops `cache: pnpm` from the ts-audit job added in #831. That job
runs `pnpm audit` and never installs, so the pnpm store does not exist;
setup-node's post-run save then fails path validation on every cache
miss and silently skips on a hit, which is why it was green on main and
red here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -165,13 +165,14 @@ jobs:
|
||||
|
||||
# 24 rather than the 22 the test job uses: this step runs no project
|
||||
# code, so it is free to track the newer line the integ workflow is
|
||||
# already on.
|
||||
# already on. No `cache: pnpm` here on purpose: the audit step below
|
||||
# resolves straight from the lockfile and never installs, so the pnpm
|
||||
# store does not exist and the post-run save fails path validation on
|
||||
# every cache miss.
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: "24"
|
||||
cache: pnpm
|
||||
cache-dependency-path: typescript/pnpm-lock.yaml
|
||||
|
||||
# Advisories read against the resolved lockfile, which is what the 27
|
||||
# hand-written pnpm.overrides in typescript/package.json pin. Those
|
||||
|
||||
Vendored
+6
-1
@@ -12,9 +12,14 @@
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from mirage.types import CacheType
|
||||
|
||||
class CacheType(str, Enum):
|
||||
RAM = "ram"
|
||||
REDIS = "redis"
|
||||
|
||||
|
||||
class CacheConfig(BaseModel):
|
||||
|
||||
Vendored
+5
-2
@@ -17,14 +17,17 @@ from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from mirage.types import IndexType
|
||||
|
||||
|
||||
class ResourceType(str, Enum):
|
||||
FILE = "file"
|
||||
FOLDER = "folder"
|
||||
|
||||
|
||||
class IndexType(str, Enum):
|
||||
RAM = "ram"
|
||||
REDIS = "redis"
|
||||
|
||||
|
||||
class LookupStatus(str, Enum):
|
||||
EXPIRED = "expired"
|
||||
NOT_FOUND = "not_found"
|
||||
|
||||
@@ -20,10 +20,10 @@ from pathlib import Path
|
||||
from mirage.accessor.disk import DiskAccessor
|
||||
from mirage.core.timeutil import epoch_to_iso
|
||||
from mirage.types import PathSpec, WalkEntry
|
||||
from mirage.utils.fingerprint import stat_fingerprint
|
||||
from mirage.utils.key_prefix import mount_prefix_of
|
||||
from mirage.watch.base import DeltaHook
|
||||
from mirage.watch.delta import ListingDeltaHook
|
||||
from mirage.watch.fingerprint import stat_fingerprint
|
||||
|
||||
|
||||
def resolve(root: Path, path: str) -> Path:
|
||||
|
||||
@@ -19,10 +19,10 @@ from mirage.core.dropbox.api import list_folder
|
||||
from mirage.core.dropbox.client import DropboxApiError
|
||||
from mirage.core.dropbox.paths import dropbox_path_of
|
||||
from mirage.types import PathSpec, WalkEntry
|
||||
from mirage.utils.fingerprint import stat_fingerprint
|
||||
from mirage.utils.key_prefix import mount_prefix_of
|
||||
from mirage.watch.base import DeltaHook
|
||||
from mirage.watch.delta import ListingDeltaHook
|
||||
from mirage.watch.fingerprint import stat_fingerprint
|
||||
|
||||
|
||||
class DropboxWalk:
|
||||
|
||||
@@ -20,10 +20,10 @@ from opendal.types import Metadata
|
||||
|
||||
from mirage.core.opendal.types import OperatorAccessor
|
||||
from mirage.types import PathSpec, WalkEntry
|
||||
from mirage.utils.fingerprint import stat_fingerprint
|
||||
from mirage.utils.key_prefix import mount_prefix_of
|
||||
from mirage.watch.base import DeltaHook
|
||||
from mirage.watch.delta import ListingDeltaHook
|
||||
from mirage.watch.fingerprint import stat_fingerprint
|
||||
|
||||
|
||||
class OpendalWalk:
|
||||
|
||||
@@ -19,10 +19,10 @@ from mirage.core.s3.client import (_client_kwargs, _key, _strip_prefix,
|
||||
async_session)
|
||||
from mirage.core.timeutil import to_iso_z
|
||||
from mirage.types import PathSpec, WalkEntry
|
||||
from mirage.utils.fingerprint import stat_fingerprint
|
||||
from mirage.utils.key_prefix import mount_prefix_of
|
||||
from mirage.watch.base import DeltaHook
|
||||
from mirage.watch.delta import ListingDeltaHook
|
||||
from mirage.watch.fingerprint import stat_fingerprint
|
||||
from mirage.watch.walk import synth_dirs
|
||||
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@ from mirage.core.ssh.client import _abs
|
||||
from mirage.core.ssh.config import SSHConfig
|
||||
from mirage.core.timeutil import epoch_to_iso
|
||||
from mirage.types import PathSpec, WalkEntry
|
||||
from mirage.utils.fingerprint import stat_fingerprint
|
||||
from mirage.utils.key_prefix import mount_prefix_of
|
||||
from mirage.watch.base import DeltaHook
|
||||
from mirage.watch.delta import ListingDeltaHook
|
||||
from mirage.watch.fingerprint import stat_fingerprint
|
||||
|
||||
|
||||
async def _descend(
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
from mirage.provision import (Precision, ProvisionResult, combine_alternative,
|
||||
combine_sum)
|
||||
from mirage.provision.types import (Precision, ProvisionResult,
|
||||
combine_alternative, combine_sum)
|
||||
|
||||
|
||||
def rollup_pipe(children: list[ProvisionResult]) -> ProvisionResult:
|
||||
@@ -19,9 +19,10 @@ from mirage.server.version.state_tree import (CONTROL_PREFIX, META_PATH,
|
||||
blob_to_meta, meta_to_blob,
|
||||
to_state, tree_inputs_from_state)
|
||||
from mirage.server.version.store import VersionStore
|
||||
from mirage.types import DriftPolicy, StateKey
|
||||
from mirage.types import DriftPolicy
|
||||
from mirage.workspace.snapshot import (apply_state_dict, install_fingerprints,
|
||||
to_state_dict)
|
||||
from mirage.workspace.snapshot.keys import StateKey
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from mirage.workspace.workspace import Workspace
|
||||
|
||||
@@ -17,10 +17,11 @@ from typing import TYPE_CHECKING, Any
|
||||
from mirage.server.version.api import read_version, resolve_ref
|
||||
from mirage.server.version.state_tree import CATEGORIES, to_state
|
||||
from mirage.server.version.store import VersionStore
|
||||
from mirage.types import DriftPolicy, MountKey, ResourceStateKey, StateKey
|
||||
from mirage.types import DriftPolicy
|
||||
from mirage.utils.path import norm
|
||||
from mirage.workspace.snapshot import (apply_state_dict, install_fingerprints,
|
||||
to_state_dict)
|
||||
from mirage.workspace.snapshot.keys import MountKey, ResourceStateKey, StateKey
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from mirage.workspace.workspace import Workspace
|
||||
|
||||
@@ -17,7 +17,7 @@ from typing import Any
|
||||
from mirage.server.version.api import read_version, resolve_ref, version_diff
|
||||
from mirage.server.version.state_tree import to_state
|
||||
from mirage.server.version.store import VersionStore
|
||||
from mirage.types import SessionKey, StateKey
|
||||
from mirage.workspace.snapshot.keys import SessionKey, StateKey
|
||||
|
||||
|
||||
def _dict_delta(before: dict[str, Any], after: dict[str,
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
from mirage.types import CacheKey, MountKey, ResourceStateKey, StateKey
|
||||
from mirage.workspace.snapshot.keys import (CacheKey, MountKey,
|
||||
ResourceStateKey, StateKey)
|
||||
from mirage.workspace.snapshot.tar_io import _json_default
|
||||
from mirage.workspace.snapshot.utils import FORMAT_VERSION
|
||||
|
||||
|
||||
+1
-109
@@ -564,33 +564,6 @@ def word_text(word: "str | PathSpec") -> str:
|
||||
return word
|
||||
|
||||
|
||||
class IndexType(str, Enum):
|
||||
RAM = "ram"
|
||||
REDIS = "redis"
|
||||
|
||||
|
||||
class CacheType(str, Enum):
|
||||
RAM = "ram"
|
||||
REDIS = "redis"
|
||||
|
||||
|
||||
class StateKey(StrEnum):
|
||||
VERSION = "version"
|
||||
MIRAGE_VERSION = "mirage_version"
|
||||
MOUNTS = "mounts"
|
||||
SESSIONS = "sessions"
|
||||
DEFAULT_SESSION_ID = "default_session_id"
|
||||
DEFAULT_AGENT_ID = "default_agent_id"
|
||||
CURRENT_AGENT_ID = "current_agent_id"
|
||||
CACHE = "cache"
|
||||
HISTORY = "history"
|
||||
JOBS = "jobs"
|
||||
FINGERPRINTS = "fingerprints"
|
||||
LIVE_ONLY_MOUNTS = "live_only_mounts"
|
||||
NODES = "nodes"
|
||||
CLIS = "clis"
|
||||
|
||||
|
||||
class FileChangeKind(StrEnum):
|
||||
"""Kind of an externally observed file change.
|
||||
|
||||
@@ -685,7 +658,7 @@ class WalkEntry:
|
||||
virtual (str): Workspace-virtual path of the entry.
|
||||
is_dir (bool): Whether the entry is a directory.
|
||||
fingerprint (str | None): Content fingerprint (see
|
||||
``mirage.utils.fingerprint.stat_fingerprint``). None means
|
||||
``mirage.watch.fingerprint.stat_fingerprint``). None means
|
||||
only create/delete are detectable for this entry.
|
||||
size (int | None): Content size in bytes, when the listing
|
||||
carries it.
|
||||
@@ -727,84 +700,3 @@ class DriftPolicy(StrEnum):
|
||||
"""
|
||||
STRICT = "strict"
|
||||
OFF = "off"
|
||||
|
||||
|
||||
class FingerprintKey(StrEnum):
|
||||
PATH = "path"
|
||||
MOUNT_PREFIX = "mount_prefix"
|
||||
FINGERPRINT = "fingerprint"
|
||||
REVISION = "revision"
|
||||
|
||||
|
||||
class MountKey(StrEnum):
|
||||
INDEX = "index"
|
||||
PREFIX = "prefix"
|
||||
MODE = "mode"
|
||||
CONSISTENCY = "consistency"
|
||||
RESOURCE_CLASS = "resource_class"
|
||||
RESOURCE_STATE = "resource_state"
|
||||
|
||||
|
||||
class CLIKey(StrEnum):
|
||||
NAME = "name"
|
||||
SPEC = "spec"
|
||||
CONFIG = "config"
|
||||
SCRIPT = "script"
|
||||
RUNTIME = "runtime"
|
||||
|
||||
|
||||
class ScriptKey(StrEnum):
|
||||
SOURCE = "source"
|
||||
LANGUAGE = "language"
|
||||
MODULE = "module"
|
||||
|
||||
|
||||
class CacheKey(StrEnum):
|
||||
LIMIT = "limit"
|
||||
MAX_DRAIN_BYTES = "max_drain_bytes"
|
||||
ENTRIES = "entries"
|
||||
KEY = "key"
|
||||
DATA = "data"
|
||||
FINGERPRINT = "fingerprint"
|
||||
TTL = "ttl"
|
||||
CACHED_AT = "cached_at"
|
||||
SIZE = "size"
|
||||
|
||||
|
||||
class JobKey(StrEnum):
|
||||
ID = "id"
|
||||
COMMAND = "command"
|
||||
CWD = "cwd"
|
||||
STATUS = "status"
|
||||
STDOUT = "stdout"
|
||||
STDERR = "stderr"
|
||||
EXIT_CODE = "exit_code"
|
||||
CREATED_AT = "created_at"
|
||||
AGENT = "agent"
|
||||
SESSION_ID = "session_id"
|
||||
|
||||
|
||||
class NodeMetaKey(StrEnum):
|
||||
TARGET = "target"
|
||||
MTIME = "mtime"
|
||||
MODE = "mode"
|
||||
UID = "uid"
|
||||
GID = "gid"
|
||||
ATIME = "atime"
|
||||
OBSERVED_MTIME = "observed_mtime"
|
||||
|
||||
|
||||
class SessionKey(StrEnum):
|
||||
SESSION_ID = "session_id"
|
||||
CWD = "cwd"
|
||||
ENV = "env"
|
||||
LAST_EXIT_CODE = "last_exit_code"
|
||||
|
||||
|
||||
class ResourceStateKey(StrEnum):
|
||||
TYPE = "type"
|
||||
CONFIG = "config"
|
||||
FILES = "files"
|
||||
DIRS = "dirs"
|
||||
MODIFIED = "modified"
|
||||
KEY_PREFIX = "key_prefix"
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
|
||||
from mirage.types import (Delta, FileChangeKind, FileEvent, FileMetadata,
|
||||
WalkEntry, WalkFn)
|
||||
from mirage.utils.fingerprint import stat_fingerprint
|
||||
from mirage.watch.base import DeltaHook, WatchRuntime
|
||||
from mirage.watch.delta import ListingDeltaHook
|
||||
from mirage.watch.fingerprint import stat_fingerprint
|
||||
from mirage.watch.queue import (OverflowPolicy, QueueClosed, QueueFactory,
|
||||
QueueOverflowError, RAMWatchQueue, WatchQueue)
|
||||
from mirage.watch.watcher import Watcher
|
||||
|
||||
@@ -18,8 +18,8 @@ from collections.abc import (AsyncIterator, Awaitable, Callable, Iterable,
|
||||
from mirage.cache.index import IndexCacheStore
|
||||
from mirage.cache.index.ram import RAMIndexCacheStore
|
||||
from mirage.types import FileStat, FileType, PathSpec, WalkEntry
|
||||
from mirage.utils.fingerprint import stat_fingerprint
|
||||
from mirage.utils.key_prefix import mount_key, mount_prefix_of
|
||||
from mirage.watch.fingerprint import stat_fingerprint
|
||||
|
||||
ReaddirFn = Callable[[PathSpec, IndexCacheStore], Awaitable[list[str]]]
|
||||
StatFn = Callable[[PathSpec, IndexCacheStore], Awaitable[FileStat]]
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
import asyncio
|
||||
from collections.abc import Iterable
|
||||
from dataclasses import dataclass
|
||||
from enum import StrEnum
|
||||
|
||||
from mirage.core.timeutil import epoch_to_iso
|
||||
from mirage.resource.base import BaseResource
|
||||
from mirage.types import (LINK_TARGET_KEY, FileStat, FileType, MountMode,
|
||||
NodeMetaKey)
|
||||
from mirage.types import LINK_TARGET_KEY, FileStat, FileType, MountMode
|
||||
from mirage.utils.path import glob_prefix_match, resolve_symlinks
|
||||
from mirage.workspace.mount.mount import MountEntry
|
||||
from mirage.workspace.mount.namespace.ram import RAMNamespaceStore
|
||||
@@ -27,6 +27,16 @@ from mirage.workspace.mount.namespace.store import NamespaceStore, NodeFields
|
||||
from mirage.workspace.mount.registry import MountRegistry
|
||||
|
||||
|
||||
class NodeMetaKey(StrEnum):
|
||||
TARGET = "target"
|
||||
MTIME = "mtime"
|
||||
MODE = "mode"
|
||||
UID = "uid"
|
||||
GID = "gid"
|
||||
ATIME = "atime"
|
||||
OBSERVED_MTIME = "observed_mtime"
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class NodeMeta:
|
||||
"""Per-path namespace metadata.
|
||||
|
||||
@@ -17,6 +17,7 @@ from functools import partial
|
||||
from typing import Any, Callable
|
||||
|
||||
from mirage.provision import Precision, ProvisionResult
|
||||
from mirage.provision.rollup import rollup_list, rollup_pipe
|
||||
from mirage.runtime.types import DispatchFn
|
||||
from mirage.shell.node_kind import NodeKind, node_kind
|
||||
from mirage.shell.types import FunctionBody
|
||||
@@ -28,7 +29,6 @@ from mirage.workspace.expand import (classify_parts, expand_and_classify,
|
||||
expand_parts, expand_redirects)
|
||||
from mirage.workspace.mount import MountRegistry
|
||||
from mirage.workspace.mount.namespace import Namespace
|
||||
from mirage.workspace.provision.builtins import handle_builtin_provision
|
||||
from mirage.workspace.provision.command import handle_command_provision
|
||||
from mirage.workspace.provision.control import (handle_for_provision,
|
||||
handle_function_provision,
|
||||
@@ -37,7 +37,6 @@ from mirage.workspace.provision.control import (handle_for_provision,
|
||||
from mirage.workspace.provision.pipes import (handle_connection_provision,
|
||||
handle_pipe_provision)
|
||||
from mirage.workspace.provision.redirect import handle_redirect_provision
|
||||
from mirage.workspace.provision.rollup import rollup_list, rollup_pipe
|
||||
from mirage.workspace.session import Session
|
||||
|
||||
from mirage.shell.helpers import ( # isort: skip
|
||||
@@ -92,6 +91,11 @@ class PlanScope:
|
||||
planning: set[str] = field(default_factory=set)
|
||||
|
||||
|
||||
async def handle_builtin_provision() -> ProvisionResult:
|
||||
"""Plan for shell builtins (cd, export, etc.): zero cost."""
|
||||
return ProvisionResult(precision=Precision.EXACT)
|
||||
|
||||
|
||||
async def _provision_redirected(
|
||||
recurse: Callable[..., Any],
|
||||
registry: MountRegistry,
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
from mirage.provision import Precision, ProvisionResult
|
||||
|
||||
|
||||
async def handle_builtin_provision() -> ProvisionResult:
|
||||
"""Plan for shell builtins (cd, export, etc.): zero cost."""
|
||||
return ProvisionResult(precision=Precision.EXACT)
|
||||
@@ -15,7 +15,7 @@
|
||||
from typing import Any
|
||||
|
||||
from mirage.provision import Precision, ProvisionResult
|
||||
from mirage.workspace.provision.rollup import rollup_list
|
||||
from mirage.provision.rollup import rollup_list
|
||||
from mirage.workspace.session import Session
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
from typing import Any
|
||||
|
||||
from mirage.provision import ProvisionResult
|
||||
from mirage.workspace.provision.rollup import rollup_list, rollup_pipe
|
||||
from mirage.provision.rollup import rollup_list, rollup_pipe
|
||||
from mirage.workspace.session import Session
|
||||
|
||||
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
from typing import Any
|
||||
|
||||
from mirage.provision import Precision, ProvisionResult
|
||||
from mirage.provision.rollup import rollup_list
|
||||
from mirage.shell.types import RedirectKind
|
||||
from mirage.types import PathSpec
|
||||
from mirage.workspace.mount import MountRegistry
|
||||
from mirage.workspace.mount.namespace import Namespace
|
||||
from mirage.workspace.provision.command import handle_command_provision
|
||||
from mirage.workspace.provision.rollup import rollup_list
|
||||
from mirage.workspace.session import Session
|
||||
|
||||
|
||||
|
||||
@@ -16,8 +16,9 @@ import asyncio
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any, Callable
|
||||
|
||||
from mirage.types import DriftPolicy, FingerprintKey
|
||||
from mirage.types import DriftPolicy
|
||||
from mirage.workspace.mount.mount import MountEntry
|
||||
from mirage.workspace.snapshot.keys import FingerprintKey
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from mirage.workspace.workspace import Workspace
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
from enum import StrEnum
|
||||
|
||||
|
||||
class StateKey(StrEnum):
|
||||
VERSION = "version"
|
||||
MIRAGE_VERSION = "mirage_version"
|
||||
MOUNTS = "mounts"
|
||||
SESSIONS = "sessions"
|
||||
DEFAULT_SESSION_ID = "default_session_id"
|
||||
DEFAULT_AGENT_ID = "default_agent_id"
|
||||
CURRENT_AGENT_ID = "current_agent_id"
|
||||
CACHE = "cache"
|
||||
HISTORY = "history"
|
||||
JOBS = "jobs"
|
||||
FINGERPRINTS = "fingerprints"
|
||||
LIVE_ONLY_MOUNTS = "live_only_mounts"
|
||||
NODES = "nodes"
|
||||
CLIS = "clis"
|
||||
|
||||
|
||||
class MountKey(StrEnum):
|
||||
INDEX = "index"
|
||||
PREFIX = "prefix"
|
||||
MODE = "mode"
|
||||
CONSISTENCY = "consistency"
|
||||
RESOURCE_CLASS = "resource_class"
|
||||
RESOURCE_STATE = "resource_state"
|
||||
|
||||
|
||||
class CacheKey(StrEnum):
|
||||
LIMIT = "limit"
|
||||
MAX_DRAIN_BYTES = "max_drain_bytes"
|
||||
ENTRIES = "entries"
|
||||
KEY = "key"
|
||||
DATA = "data"
|
||||
FINGERPRINT = "fingerprint"
|
||||
TTL = "ttl"
|
||||
CACHED_AT = "cached_at"
|
||||
SIZE = "size"
|
||||
|
||||
|
||||
class JobKey(StrEnum):
|
||||
ID = "id"
|
||||
COMMAND = "command"
|
||||
CWD = "cwd"
|
||||
STATUS = "status"
|
||||
STDOUT = "stdout"
|
||||
STDERR = "stderr"
|
||||
EXIT_CODE = "exit_code"
|
||||
CREATED_AT = "created_at"
|
||||
AGENT = "agent"
|
||||
SESSION_ID = "session_id"
|
||||
|
||||
|
||||
class ResourceStateKey(StrEnum):
|
||||
TYPE = "type"
|
||||
CONFIG = "config"
|
||||
FILES = "files"
|
||||
DIRS = "dirs"
|
||||
MODIFIED = "modified"
|
||||
KEY_PREFIX = "key_prefix"
|
||||
|
||||
|
||||
# The four below name sub-shapes that typescript spells as literals at
|
||||
# the point of use rather than as a frozen table (`keys.ts` stops at
|
||||
# ResourceStateKey). They belong to the same snapshot vocabulary, so
|
||||
# they live here too instead of staying behind in `types.py`.
|
||||
# NodeMetaKey is the exception: it names NodeMeta's own fields, so it
|
||||
# lives beside NodeMeta in `mount/namespace/namespace.py`. Snapshot
|
||||
# serializes the namespace, not the other way round, and importing
|
||||
# this package from there closes an import cycle.
|
||||
class FingerprintKey(StrEnum):
|
||||
PATH = "path"
|
||||
MOUNT_PREFIX = "mount_prefix"
|
||||
FINGERPRINT = "fingerprint"
|
||||
REVISION = "revision"
|
||||
|
||||
|
||||
class CLIKey(StrEnum):
|
||||
NAME = "name"
|
||||
SPEC = "spec"
|
||||
CONFIG = "config"
|
||||
SCRIPT = "script"
|
||||
RUNTIME = "runtime"
|
||||
|
||||
|
||||
class ScriptKey(StrEnum):
|
||||
SOURCE = "source"
|
||||
LANGUAGE = "language"
|
||||
MODULE = "module"
|
||||
|
||||
|
||||
class SessionKey(StrEnum):
|
||||
SESSION_ID = "session_id"
|
||||
CWD = "cwd"
|
||||
ENV = "env"
|
||||
LAST_EXIT_CODE = "last_exit_code"
|
||||
@@ -14,8 +14,9 @@
|
||||
|
||||
from typing import Any
|
||||
|
||||
from mirage.types import (CacheKey, JobKey, MountKey, ResourceName,
|
||||
ResourceStateKey, StateKey)
|
||||
from mirage.types import ResourceName
|
||||
from mirage.workspace.snapshot.keys import (CacheKey, JobKey, MountKey,
|
||||
ResourceStateKey, StateKey)
|
||||
from mirage.workspace.snapshot.utils import BLOB_REF_KEY, is_safe_blob_path
|
||||
|
||||
|
||||
|
||||
@@ -28,9 +28,7 @@ from mirage.runtime.types import Language, ScriptSource
|
||||
from mirage.shell.console import (KILLED_OUTCOME, Channel, ConsoleChunk,
|
||||
JobConsole, RAMConsoleStore, exit_outcome)
|
||||
from mirage.shell.job_table import Job, JobStatus
|
||||
from mirage.types import (CacheKey, CLIKey, ConsistencyPolicy, JobKey,
|
||||
JsonValue, MountKey, MountMode, ResourceName,
|
||||
ResourceStateKey, ScriptKey, SessionKey, StateKey)
|
||||
from mirage.types import ConsistencyPolicy, JsonValue, MountMode, ResourceName
|
||||
from mirage.version import __version__
|
||||
from mirage.workspace.mount.namespace import NodeMeta
|
||||
from mirage.workspace.session.session import Session
|
||||
@@ -38,6 +36,9 @@ from mirage.workspace.session.shell_dirs import set_cwd
|
||||
from mirage.workspace.snapshot.config import MountArgs
|
||||
from mirage.workspace.snapshot.drift import (capture_fingerprints,
|
||||
live_only_mount_prefixes)
|
||||
from mirage.workspace.snapshot.keys import (CacheKey, CLIKey, JobKey, MountKey,
|
||||
ResourceStateKey, ScriptKey,
|
||||
SessionKey, StateKey)
|
||||
from mirage.workspace.snapshot.utils import FORMAT_VERSION, norm_mount_prefix
|
||||
|
||||
# A per-name override for restoring installed CLIs: a plain mapping is a
|
||||
|
||||
@@ -40,7 +40,7 @@ from mirage.runtime.resolver import PrefixResolver
|
||||
from mirage.shell.job_table import ConsoleFactory, JobTable
|
||||
from mirage.types import (ConsistencyPolicy, DriftPolicy, FileEvent, FileStat,
|
||||
JsonValue, MountBackend, MountMode, PathSpec,
|
||||
StateKey, parse_mount_mode)
|
||||
parse_mount_mode)
|
||||
from mirage.utils.ids import new_session_id, new_workspace_id
|
||||
from mirage.workspace.cli import CLIInstall
|
||||
from mirage.workspace.dispatcher import Dispatcher
|
||||
@@ -56,6 +56,7 @@ from mirage.workspace.snapshot import (DriftQueue, apply_state_dict,
|
||||
read_tar)
|
||||
from mirage.workspace.snapshot import snapshot as _write_snapshot
|
||||
from mirage.workspace.snapshot import to_state_dict
|
||||
from mirage.workspace.snapshot.keys import StateKey
|
||||
from mirage.workspace.snapshot.state import (CLIOverrides, reusable_clis,
|
||||
reusable_resources)
|
||||
from mirage.workspace.store import WorkspaceStateStore
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
from mirage.cache.index import (IndexConfig, IndexEntry, ListResult,
|
||||
LookupResult, LookupStatus, RedisIndexConfig,
|
||||
ResourceType)
|
||||
from mirage.types import IndexType
|
||||
from mirage.cache.index.config import IndexType
|
||||
|
||||
|
||||
def test_index_entry_defaults():
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
from mirage.provision import Precision, ProvisionResult
|
||||
from mirage.workspace.provision.rollup import rollup_list, rollup_pipe
|
||||
from mirage.provision.rollup import rollup_list, rollup_pipe
|
||||
|
||||
|
||||
def test_rollup_pipe_sums():
|
||||
|
||||
@@ -23,10 +23,11 @@ from mirage.server.version.backend import LocalBackend
|
||||
from mirage.server.version.errors import NoSuchBranchError
|
||||
from mirage.server.version.state_tree import META_PATH
|
||||
from mirage.server.version.store import VersionStore
|
||||
from mirage.types import CacheKey, MountMode, StateKey
|
||||
from mirage.types import MountMode
|
||||
from mirage.workspace import Workspace
|
||||
from mirage.workspace.session.state import seed_var
|
||||
from mirage.workspace.snapshot import to_state_dict
|
||||
from mirage.workspace.snapshot.keys import CacheKey, StateKey
|
||||
|
||||
|
||||
async def status(store, ws, branch="main"):
|
||||
|
||||
@@ -19,9 +19,10 @@ import pytest
|
||||
from mirage.resource.ram import RAMResource
|
||||
from mirage.server.version.state_tree import (blob_to_meta, meta_to_blob,
|
||||
to_state, tree_inputs_from_state)
|
||||
from mirage.types import (CacheKey, FingerprintKey, MountKey, MountMode,
|
||||
SessionKey, StateKey)
|
||||
from mirage.types import MountMode
|
||||
from mirage.workspace import Workspace
|
||||
from mirage.workspace.snapshot.keys import (CacheKey, FingerprintKey, MountKey,
|
||||
SessionKey, StateKey)
|
||||
from mirage.workspace.snapshot.manifest import split_manifest_and_blobs
|
||||
from mirage.workspace.snapshot.state import to_state_dict
|
||||
from mirage.workspace.snapshot.tar_io import read_tar, write_tar
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from mirage.utils.fingerprint import stat_fingerprint
|
||||
from mirage.watch.fingerprint import stat_fingerprint
|
||||
|
||||
|
||||
def test_stat_fingerprint_prefers_etag():
|
||||
@@ -0,0 +1,57 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import re
|
||||
from enum import StrEnum
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from mirage.workspace.snapshot import keys
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[4]
|
||||
KEYS_TS = (REPO_ROOT / "typescript" / "packages" / "core" / "src" /
|
||||
"workspace" / "snapshot" / "keys.ts")
|
||||
|
||||
# These five have a typescript twin in `keys.ts`; the rest of the module
|
||||
# is spelled as literals over there, so only these can be diffed.
|
||||
SHARED = ["StateKey", "MountKey", "CacheKey", "JobKey", "ResourceStateKey"]
|
||||
|
||||
|
||||
def _typescript_tables() -> dict[str, dict[str, str]]:
|
||||
source = KEYS_TS.read_text()
|
||||
tables: dict[str, dict[str, str]] = {}
|
||||
pattern = r"export const (\w+) = Object\.freeze\(\{(.*?)\}\s*as const\)"
|
||||
for name, body in re.findall(pattern, source, re.DOTALL):
|
||||
tables[name] = dict(re.findall(r"(\w+): '([^']*)'", body))
|
||||
return tables
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", SHARED)
|
||||
def test_shared_tables_match_typescript(name: str):
|
||||
"""A snapshot key is a wire name, so a one-sided rename breaks restore."""
|
||||
table = _typescript_tables()[name]
|
||||
enum: type[StrEnum] = getattr(keys, name)
|
||||
assert {member.name: member.value for member in enum} == table
|
||||
|
||||
|
||||
def test_every_key_is_a_lowercase_wire_name():
|
||||
enums = [
|
||||
value for value in vars(keys).values() if isinstance(value, type)
|
||||
and issubclass(value, StrEnum) and value is not StrEnum
|
||||
]
|
||||
assert len(enums) == 9
|
||||
for enum in enums:
|
||||
for member in enum:
|
||||
assert member.value == member.name.lower()
|
||||
@@ -15,7 +15,7 @@
|
||||
import io
|
||||
from typing import Any
|
||||
|
||||
from mirage.types import CacheKey, JobKey, MountKey, StateKey
|
||||
from mirage.workspace.snapshot.keys import CacheKey, JobKey, MountKey, StateKey
|
||||
from mirage.workspace.snapshot.manifest import split_manifest_and_blobs
|
||||
from mirage.workspace.snapshot.tar_io import read_tar, write_tar
|
||||
from mirage.workspace.snapshot.utils import BLOB_REF_KEY
|
||||
|
||||
@@ -28,9 +28,10 @@ from mirage.resource.ram import RAMResource
|
||||
from mirage.resource.s3 import S3Config, S3Resource
|
||||
from mirage.resource.secrets import REDACTED_SECRET
|
||||
from mirage.runtime.types import ScriptSource
|
||||
from mirage.types import CLIKey, MountMode, ScriptKey, StateKey
|
||||
from mirage.types import MountMode
|
||||
from mirage.workspace import Workspace
|
||||
from mirage.workspace.snapshot import to_state_dict
|
||||
from mirage.workspace.snapshot.keys import CLIKey, ScriptKey, StateKey
|
||||
from mirage.workspace.snapshot.utils import FORMAT_VERSION
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"baseline": 286,
|
||||
"baseline": 269,
|
||||
"baseline_reason": "Every divergence below the excused ones predates the gate and each needs its own decision, so --strict fails on a rise rather than demanding zero. Lower this number whenever a divergence is closed; the gate fails on a drop too, so an improvement cannot be silently spent. Items 31-37 of the cleanup plan are scoped from this report. The unit is one module, never one directory: a one-sided directory counts once per module inside it, so it cannot absorb new modules without moving the number. An excused directory is the deliberate exception -- it excuses its whole subtree, because the excuse is that there is no counterpart to mirror, which makes growth inside it expected rather than drift.",
|
||||
"directories": {
|
||||
"python_only": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import type { Options } from '@anthropic-ai/claude-agent-sdk'
|
||||
import { buildSystemPrompt } from '../prompt.ts'
|
||||
import { MirageServer } from './server.ts'
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import { VERSION } from '@struktoai/mirage-core/version'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk'
|
||||
import { z } from 'zod'
|
||||
import {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import { createHash } from 'node:crypto'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
|
||||
export class StaleMirageFileError extends Error {
|
||||
readonly path: string
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { ExecuteResult } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import { ExecuteResult } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import { decode, ioToStr } from './io-text.ts'
|
||||
|
||||
const enc = (s: string): Uint8Array => new TextEncoder().encode(s)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import type { ExecuteResult } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { ExecuteResult } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
|
||||
export function decode(value: Uint8Array | null | undefined): string {
|
||||
if (value === null || value === undefined) return ''
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import { gnuDirname } from '@struktoai/mirage-core/utils/path'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import { createTool } from '@mastra/core/tools'
|
||||
import { z } from 'zod'
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import { VERSION } from '@struktoai/mirage-core/version'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
||||
import { z } from 'zod'
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import { gnuDirname } from '@struktoai/mirage-core/utils/path'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import { applyDiff } from '@openai/agents'
|
||||
import type { ApplyPatchOperation, ApplyPatchResult, Editor } from '@openai/agents'
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import { tool } from '@openai/agents'
|
||||
import { z } from 'zod'
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import {
|
||||
tool,
|
||||
type ToolOutputFileContent,
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import type { Shell, ShellAction, ShellResult, ShellOutputResult } from '@openai/agents'
|
||||
|
||||
export class MirageShell implements Shell {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import {
|
||||
createBashToolDefinition,
|
||||
createEditToolDefinition,
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import { rstripSlash } from '@struktoai/mirage-core/utils/slash'
|
||||
import type { ExecuteResult, Workspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { ExecuteResult, Workspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import type {
|
||||
BashOperations,
|
||||
EditOperations,
|
||||
|
||||
@@ -16,7 +16,7 @@ import { describe, expect, it } from 'vitest'
|
||||
import { OpsRegistry } from '@struktoai/mirage-core/ops/registry'
|
||||
import { RAMResource } from '@struktoai/mirage-core/resource/ram/ram'
|
||||
import { MountMode } from '@struktoai/mirage-core/types'
|
||||
import { Workspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import { Workspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import { MIRAGE_SYSTEM_PROMPT, buildSystemPrompt } from './prompt.ts'
|
||||
|
||||
function mkWs(): Workspace {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
|
||||
export const MIRAGE_SYSTEM_PROMPT = `Your filesystem is powered by Mirage — a virtual filesystem that mounts cloud storage, local files, and in-memory data as a unified file tree.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { detectFileType } from '@struktoai/mirage-core/commands/builtin/file_helper'
|
||||
import { FileType } from '@struktoai/mirage-core/types'
|
||||
import type { FileStat } from '@struktoai/mirage-core/types'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import {
|
||||
MIME_FOR_EXTENSION,
|
||||
MIME_FOR_FILE_TYPE,
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import { gnuDirname } from '@struktoai/mirage-core/utils/path'
|
||||
import type { ExecuteResult, Workspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { ExecuteResult, Workspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import { FileVersionTracker, StaleMirageFileError } from './file-version.ts'
|
||||
import { decode, ioToStr } from './io-text.ts'
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
import { encodeBase64 } from '@struktoai/mirage-core/utils/base64'
|
||||
import { gnuDirname } from '@struktoai/mirage-core/utils/path'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { Workspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import { tool, type ToolSet } from 'ai'
|
||||
import { z } from 'zod'
|
||||
import { readWorkspaceFile } from '../read-file.ts'
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
import type { Resource } from '@struktoai/mirage-core/resource/base'
|
||||
import { createShellParser } from '@struktoai/mirage-core/shell/parse'
|
||||
import type { ShellParser } from '@struktoai/mirage-core/shell/parse'
|
||||
import { Workspace as CoreWorkspace } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import type { WorkspaceOptions } from '@struktoai/mirage-core/workspace/workspace'
|
||||
import { Workspace as CoreWorkspace } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import type { WorkspaceOptions } from '@struktoai/mirage-core/workspace/workspace/workspace'
|
||||
import { ENGINE_WASM_BASE64, GRAMMAR_WASM_BASE64 } from './generated/wasm.ts'
|
||||
|
||||
let cachedParser: Promise<ShellParser> | null = null
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import { ConcurrencyLimiter } from '../concurrency/limiter.ts'
|
||||
import { Accessor } from './base.ts'
|
||||
import type { DifyConfigResolved } from '../resource/dify/config.ts'
|
||||
|
||||
@@ -20,44 +21,14 @@ export interface DifyRequestOptions {
|
||||
json?: unknown
|
||||
}
|
||||
|
||||
class Semaphore {
|
||||
private available: number
|
||||
private readonly waiters: (() => void)[] = []
|
||||
|
||||
constructor(max: number) {
|
||||
this.available = Math.max(1, max)
|
||||
}
|
||||
|
||||
async acquire(): Promise<() => void> {
|
||||
const release = (): void => {
|
||||
this.release()
|
||||
}
|
||||
if (this.available > 0) {
|
||||
this.available -= 1
|
||||
return release
|
||||
}
|
||||
await new Promise<void>((resolve) => {
|
||||
this.waiters.push(resolve)
|
||||
})
|
||||
this.available -= 1
|
||||
return release
|
||||
}
|
||||
|
||||
private release(): void {
|
||||
this.available += 1
|
||||
const next = this.waiters.shift()
|
||||
if (next !== undefined) next()
|
||||
}
|
||||
}
|
||||
|
||||
export class DifyAccessor extends Accessor {
|
||||
readonly config: DifyConfigResolved
|
||||
private readonly limiter: Semaphore
|
||||
private readonly limiter: ConcurrencyLimiter
|
||||
|
||||
constructor(config: DifyConfigResolved) {
|
||||
super()
|
||||
this.config = config
|
||||
this.limiter = new Semaphore(config.maxConcurrency)
|
||||
this.limiter = new ConcurrencyLimiter(config.maxConcurrency)
|
||||
}
|
||||
|
||||
async request(
|
||||
|
||||
@@ -18,7 +18,7 @@ import { BaseResource } from '../../../resource/base.ts'
|
||||
import { RAMResource } from '../../../resource/ram/ram.ts'
|
||||
import { MountMode } from '../../../types.ts'
|
||||
import { getTestParser } from '../../../workspace/fixtures/workspace_fixture.ts'
|
||||
import { Workspace } from '../../../workspace/workspace.ts'
|
||||
import { Workspace } from '../../../workspace/workspace/workspace.ts'
|
||||
|
||||
const ENC = new TextEncoder()
|
||||
const DEC = new TextDecoder()
|
||||
|
||||
@@ -17,7 +17,7 @@ import { OpsRegistry } from '../../../ops/registry.ts'
|
||||
import { RAMResource } from '../../../resource/ram/ram.ts'
|
||||
import { MountMode } from '../../../types.ts'
|
||||
import { getTestParser } from '../../../workspace/fixtures/workspace_fixture.ts'
|
||||
import { Workspace } from '../../../workspace/workspace.ts'
|
||||
import { Workspace } from '../../../workspace/workspace/workspace.ts'
|
||||
|
||||
async function makeWs(): Promise<Workspace> {
|
||||
const parser = await getTestParser()
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
PathSpec,
|
||||
} from '../../../types.ts'
|
||||
import { getTestParser } from '../../../workspace/fixtures/workspace_fixture.ts'
|
||||
import { Workspace } from '../../../workspace/workspace.ts'
|
||||
import { Workspace } from '../../../workspace/workspace/workspace.ts'
|
||||
import { statGeneric } from './stat.ts'
|
||||
|
||||
const MTIME = '2026-01-02T15:30:45Z'
|
||||
|
||||
@@ -24,7 +24,7 @@ import { IOResult } from '../../../../io/types.ts'
|
||||
import { OpsRegistry } from '../../../../ops/registry.ts'
|
||||
import { RAMResource } from '../../../../resource/ram/ram.ts'
|
||||
import { MountMode } from '../../../../types.ts'
|
||||
import { Workspace } from '../../../../workspace/workspace.ts'
|
||||
import { Workspace } from '../../../../workspace/workspace/workspace.ts'
|
||||
import { gitFs } from './fs.ts'
|
||||
import { ensureDir } from './io.ts'
|
||||
import type { Dispatch } from './types.ts'
|
||||
|
||||
@@ -25,7 +25,7 @@ import { OpsRegistry } from '../../../../ops/registry.ts'
|
||||
import { RAMResource } from '../../../../resource/ram/ram.ts'
|
||||
import { createShellParser, type ShellParser } from '../../../../shell/parse.ts'
|
||||
import { MountMode } from '../../../../types.ts'
|
||||
import { Workspace } from '../../../../workspace/workspace.ts'
|
||||
import { Workspace } from '../../../../workspace/workspace/workspace.ts'
|
||||
import { GIT } from './index.ts'
|
||||
import { ensureDir } from './io.ts'
|
||||
import type { Dispatch } from './types.ts'
|
||||
|
||||
@@ -33,7 +33,7 @@ import { OpsRegistry } from '../../../../ops/registry.ts'
|
||||
import { RAMResource } from '../../../../resource/ram/ram.ts'
|
||||
import { createShellParser, type ShellParser } from '../../../../shell/parse.ts'
|
||||
import { MountMode } from '../../../../types.ts'
|
||||
import { Workspace } from '../../../../workspace/workspace.ts'
|
||||
import { Workspace } from '../../../../workspace/workspace/workspace.ts'
|
||||
import { GIT } from './index.ts'
|
||||
import { ensureDir, readNames, readOptional } from './io.ts'
|
||||
import type { Dispatch } from './types.ts'
|
||||
|
||||
@@ -33,7 +33,7 @@ import { OpsRegistry } from '../../../../ops/registry.ts'
|
||||
import { RAMResource } from '../../../../resource/ram/ram.ts'
|
||||
import { createShellParser, type ShellParser } from '../../../../shell/parse.ts'
|
||||
import { MountMode } from '../../../../types.ts'
|
||||
import { Workspace } from '../../../../workspace/workspace.ts'
|
||||
import { Workspace } from '../../../../workspace/workspace/workspace.ts'
|
||||
import { GIT } from './index.ts'
|
||||
import { ensureDir } from './io.ts'
|
||||
import type { Dispatch } from './types.ts'
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { ConcurrencyLimiter } from './limiter.ts'
|
||||
|
||||
interface ConcurrencyState {
|
||||
active: number
|
||||
peak: number
|
||||
}
|
||||
|
||||
async function holdPermit(
|
||||
limiter: ConcurrencyLimiter,
|
||||
state: ConcurrencyState,
|
||||
entered: string[],
|
||||
gate: Promise<void>,
|
||||
): Promise<void> {
|
||||
const release = await limiter.acquire()
|
||||
state.active += 1
|
||||
state.peak = Math.max(state.peak, state.active)
|
||||
entered.push('in')
|
||||
try {
|
||||
await gate
|
||||
} finally {
|
||||
state.active -= 1
|
||||
release()
|
||||
}
|
||||
}
|
||||
|
||||
describe('ConcurrencyLimiter', () => {
|
||||
it.each([[0], [-1]])('rejects a non-positive capacity (%i)', (capacity) => {
|
||||
expect(() => new ConcurrencyLimiter(capacity)).toThrow('at least 1')
|
||||
})
|
||||
|
||||
it('limits concurrent operations to the capacity', async () => {
|
||||
const limiter = new ConcurrencyLimiter(2)
|
||||
const state: ConcurrencyState = { active: 0, peak: 0 }
|
||||
const entered: string[] = []
|
||||
let open = (): void => undefined
|
||||
const gate = new Promise<void>((resolve) => {
|
||||
open = resolve
|
||||
})
|
||||
|
||||
const held = Array.from({ length: 5 }, () => holdPermit(limiter, state, entered, gate))
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
expect(entered.length).toBe(2)
|
||||
|
||||
open()
|
||||
await Promise.all(held)
|
||||
expect(state.peak).toBe(2)
|
||||
})
|
||||
|
||||
it('a throwing holder still returns its permit', async () => {
|
||||
const limiter = new ConcurrencyLimiter(1)
|
||||
const release = await limiter.acquire()
|
||||
try {
|
||||
throw new Error('boom')
|
||||
} catch {
|
||||
release()
|
||||
}
|
||||
const second = await limiter.acquire()
|
||||
expect(typeof second).toBe('function')
|
||||
})
|
||||
|
||||
// The bug the hand-off in `release` exists to prevent. Returning the
|
||||
// permit to the pool before waking the waiter leaves it visible for the
|
||||
// rest of that tick, so a caller arriving in the same tick takes it --
|
||||
// and then the woken waiter decrements too, and both run at once. The
|
||||
// late acquire has to happen in the same tick as the release, which is
|
||||
// why this drives the limiter directly instead of through holdPermit.
|
||||
it('a caller arriving during a release cannot barge past the queue', async () => {
|
||||
const limiter = new ConcurrencyLimiter(1)
|
||||
const first = await limiter.acquire()
|
||||
|
||||
let queuedGranted = false
|
||||
const queued = limiter.acquire().then((release) => {
|
||||
queuedGranted = true
|
||||
return release
|
||||
})
|
||||
|
||||
first()
|
||||
let lateGranted = false
|
||||
const late = limiter.acquire().then((release) => {
|
||||
lateGranted = true
|
||||
return release
|
||||
})
|
||||
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
expect(queuedGranted).toBe(true)
|
||||
expect(lateGranted).toBe(false)
|
||||
;(await queued)()
|
||||
;(await late)()
|
||||
})
|
||||
|
||||
it('releasing twice does not inflate the permit count', async () => {
|
||||
const limiter = new ConcurrencyLimiter(1)
|
||||
const release = await limiter.acquire()
|
||||
release()
|
||||
release()
|
||||
|
||||
const state: ConcurrencyState = { active: 0, peak: 0 }
|
||||
const entered: string[] = []
|
||||
await Promise.all([
|
||||
holdPermit(limiter, state, entered, Promise.resolve()),
|
||||
holdPermit(limiter, state, entered, Promise.resolve()),
|
||||
])
|
||||
expect(state.peak).toBe(1)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,66 @@
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
/**
|
||||
* Limit concurrent async operations within one process.
|
||||
*
|
||||
* Twin of python's `concurrency/limiter.py`, which wraps
|
||||
* `asyncio.Semaphore`. Node has no built-in semaphore, so the queue is
|
||||
* explicit -- but the two contracts match: a permit count below one is
|
||||
* rejected rather than clamped, and a release hands its permit straight
|
||||
* to the longest-waiting caller instead of returning it to the pool.
|
||||
* Handing off matters: returning it first would let a caller arriving in
|
||||
* the same tick barge ahead of the queue and drive the count negative.
|
||||
*/
|
||||
export class ConcurrencyLimiter {
|
||||
private available: number
|
||||
private readonly waiters: (() => void)[] = []
|
||||
|
||||
constructor(maxConcurrency: number) {
|
||||
if (maxConcurrency < 1) throw new Error('maxConcurrency must be at least 1')
|
||||
this.available = maxConcurrency
|
||||
}
|
||||
|
||||
/**
|
||||
* Take one permit, waiting for a free one, and return its release.
|
||||
*
|
||||
* The release is idempotent so a caller that unwinds twice (a `finally`
|
||||
* plus an explicit call) cannot inflate the permit count -- python gets
|
||||
* that from `async with` and the shape here has to supply it.
|
||||
*/
|
||||
async acquire(): Promise<() => void> {
|
||||
if (this.available > 0) {
|
||||
this.available -= 1
|
||||
} else {
|
||||
await new Promise<void>((resolve) => {
|
||||
this.waiters.push(resolve)
|
||||
})
|
||||
}
|
||||
let released = false
|
||||
return () => {
|
||||
if (released) return
|
||||
released = true
|
||||
this.release()
|
||||
}
|
||||
}
|
||||
|
||||
private release(): void {
|
||||
const next = this.waiters.shift()
|
||||
if (next !== undefined) {
|
||||
next()
|
||||
return
|
||||
}
|
||||
this.available += 1
|
||||
}
|
||||
}
|
||||
@@ -95,5 +95,5 @@ export { SessionStore } from './workspace/session/store.ts'
|
||||
export { ContentDriftError } from './workspace/snapshot/drift.ts'
|
||||
export { toStateDict } from './workspace/snapshot/state.ts'
|
||||
export { S3WorkspaceStateStore } from './workspace/store/s3.ts'
|
||||
export { Workspace } from './workspace/workspace.ts'
|
||||
export type { MountSpec } from './workspace/workspace.ts'
|
||||
export { Workspace } from './workspace/workspace/workspace.ts'
|
||||
export type { MountSpec } from './workspace/workspace/workspace.ts'
|
||||
|
||||
@@ -16,7 +16,7 @@ import { describe, expect, it } from 'vitest'
|
||||
import { OpsRegistry } from '../ops/registry.ts'
|
||||
import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { MountMode, PathSpec, ResourceName } from '../types.ts'
|
||||
import { Workspace } from '../workspace/workspace.ts'
|
||||
import { Workspace } from '../workspace/workspace/workspace.ts'
|
||||
import { runWithRecording } from './context.ts'
|
||||
|
||||
function call(
|
||||
|
||||
@@ -22,7 +22,7 @@ import type { Action, OpsContext, OpsResultContext } from '../policy/types.ts'
|
||||
import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { FileType, Limit, MountMode, OnExceed } from '../types.ts'
|
||||
import { enotdir } from '../utils/errors.ts'
|
||||
import { Workspace } from '../workspace/workspace.ts'
|
||||
import { Workspace } from '../workspace/workspace/workspace.ts'
|
||||
|
||||
const DEC = new TextDecoder()
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { createShellParser, type ShellParser } from '../shell/parse.ts'
|
||||
import { Limit, MountMode, OnExceed, PathSpec } from '../types.ts'
|
||||
import { MountRegistry } from '../workspace/mount/registry.ts'
|
||||
import { Workspace } from '../workspace/workspace.ts'
|
||||
import { Workspace } from '../workspace/workspace/workspace.ts'
|
||||
import type { Policy } from './base.ts'
|
||||
import { MountRootPolicy } from './builtin/mount_root.ts'
|
||||
import { PolicyDenied } from './errors.ts'
|
||||
|
||||
@@ -16,7 +16,7 @@ import { describe, expect, it } from 'vitest'
|
||||
import { OpsRegistry } from '../../ops/registry.ts'
|
||||
import { MountMode, PathSpec, ResourceName } from '../../types.ts'
|
||||
import { getTestParser } from '../../workspace/fixtures/workspace_fixture.ts'
|
||||
import { Workspace } from '../../workspace/workspace.ts'
|
||||
import { Workspace } from '../../workspace/workspace/workspace.ts'
|
||||
import { RAMResource } from '../ram/ram.ts'
|
||||
import { DevResource } from './dev.ts'
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import type { Accessor } from '../../accessor/base.ts'
|
||||
import { mkdir as coreMkdir } from '../../core/ram/mkdir.ts'
|
||||
import { OpsRegistry } from '../../ops/registry.ts'
|
||||
import { FileType, MountMode, PathSpec, ResourceName } from '../../types.ts'
|
||||
import { Workspace } from '../../workspace/workspace.ts'
|
||||
import { Workspace } from '../../workspace/workspace/workspace.ts'
|
||||
import { RAMResource } from './ram.ts'
|
||||
|
||||
function setup(): { ram: RAMResource; registry: OpsRegistry; ws: Workspace } {
|
||||
|
||||
@@ -17,7 +17,7 @@ import { OpsRegistry } from '../ops/registry.ts'
|
||||
import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { MountMode } from '../types.ts'
|
||||
import { getTestParser, stderrStr, stdoutStr } from '../workspace/fixtures/workspace_fixture.ts'
|
||||
import { Workspace } from '../workspace/workspace.ts'
|
||||
import { Workspace } from '../workspace/workspace/workspace.ts'
|
||||
import { MontyRuntime } from './python/monty/index.ts'
|
||||
import { PyodideRuntime } from './python/pyodide.ts'
|
||||
import { QuickJsRuntime } from './js/quickjs.ts'
|
||||
|
||||
@@ -17,7 +17,7 @@ import { OpsRegistry } from '../ops/registry.ts'
|
||||
import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { FileType, MountMode } from '../types.ts'
|
||||
import { getTestParser, stderrStr, stdoutStr } from '../workspace/fixtures/workspace_fixture.ts'
|
||||
import { Workspace } from '../workspace/workspace.ts'
|
||||
import { Workspace } from '../workspace/workspace/workspace.ts'
|
||||
import { QuickJsRuntime } from './js/quickjs.ts'
|
||||
import { MontyRuntime } from './python/monty/index.ts'
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import { buildRuntime } from '../../table.ts'
|
||||
import { getTestParser } from '../../../workspace/fixtures/workspace_fixture.ts'
|
||||
import { RAMResource } from '../../../resource/ram/ram.ts'
|
||||
import { MountMode } from '../../../types.ts'
|
||||
import { Workspace } from '../../../workspace/workspace.ts'
|
||||
import { Workspace } from '../../../workspace/workspace/workspace.ts'
|
||||
import { PrefixResolver } from '../../resolver.ts'
|
||||
|
||||
function makeBridge(seed: Record<string, Uint8Array>): {
|
||||
|
||||
@@ -16,7 +16,7 @@ import { describe, expect, it } from 'vitest'
|
||||
import { getTestParser } from '../../workspace/fixtures/workspace_fixture.ts'
|
||||
import { RAMResource } from '../../resource/ram/ram.ts'
|
||||
import { Limit, MountMode } from '../../types.ts'
|
||||
import { Workspace } from '../../workspace/workspace.ts'
|
||||
import { Workspace } from '../../workspace/workspace/workspace.ts'
|
||||
import { RemoteSandbox } from './base.ts'
|
||||
import { isLineExecutor } from '../mixin.ts'
|
||||
import type { RunResult, RuntimeOptions } from '../types.ts'
|
||||
|
||||
@@ -17,7 +17,7 @@ import { OpsRegistry } from '../ops/registry.ts'
|
||||
import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { MountMode } from '../types.ts'
|
||||
import { getTestParser, stdoutStr } from '../workspace/fixtures/workspace_fixture.ts'
|
||||
import { Workspace } from '../workspace/workspace.ts'
|
||||
import { Workspace } from '../workspace/workspace/workspace.ts'
|
||||
|
||||
// Port of tests/shell/test_background_jobs.py::test_background_does_not_consume_stdin.
|
||||
// A backgrounded command must NOT read from the shell's stdin — otherwise the
|
||||
|
||||
@@ -17,7 +17,7 @@ import { OpsRegistry } from '../ops/registry.ts'
|
||||
import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { MountMode } from '../types.ts'
|
||||
import { getTestParser, stderrStr, stdoutStr } from '../workspace/fixtures/workspace_fixture.ts'
|
||||
import { Workspace } from '../workspace/workspace.ts'
|
||||
import { Workspace } from '../workspace/workspace/workspace.ts'
|
||||
|
||||
// Direct port of tests/shell/test_quoting_coverage.py.
|
||||
// Each test is one realistic agent pattern — failures surface as parser,
|
||||
|
||||
@@ -17,7 +17,7 @@ import { OpsRegistry } from '../ops/registry.ts'
|
||||
import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { MountMode } from '../types.ts'
|
||||
import { getTestParser, stdoutStr } from './fixtures/workspace_fixture.ts'
|
||||
import { Workspace } from './workspace.ts'
|
||||
import { Workspace } from './workspace/workspace.ts'
|
||||
|
||||
// GNU find/tree/du/ls typed bare behave exactly as if `.` had been
|
||||
// typed: ./-prefixed walk lines, `.:` ls -R headers, du rows ending in
|
||||
|
||||
@@ -18,7 +18,7 @@ import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import type { BridgeDispatchFn } from '../runtime/types.ts'
|
||||
import type { VFSEntry } from '../runtime/vfs.ts'
|
||||
import { MountMode } from '../types.ts'
|
||||
import { Workspace } from './workspace.ts'
|
||||
import { Workspace } from './workspace/workspace.ts'
|
||||
|
||||
function bridgeOn(ws: Workspace): BridgeDispatchFn {
|
||||
return (ws as unknown as { buildWorkspaceBridge(): BridgeDispatchFn }).buildWorkspaceBridge()
|
||||
|
||||
@@ -24,7 +24,7 @@ import { describe, expect, it } from 'vitest'
|
||||
import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { createShellParser } from '../shell/parse.ts'
|
||||
import { MountMode } from '../types.ts'
|
||||
import { Workspace } from './workspace.ts'
|
||||
import { Workspace } from './workspace/workspace.ts'
|
||||
|
||||
const DEC = new TextDecoder()
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
@@ -19,7 +19,7 @@ import { cachesReads } from '../resource/base.ts'
|
||||
import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { createShellParser } from '../shell/parse.ts'
|
||||
import { ConsistencyPolicy, MountMode, PathSpec } from '../types.ts'
|
||||
import { Workspace } from './workspace.ts'
|
||||
import { Workspace } from './workspace/workspace.ts'
|
||||
|
||||
const ENC = new TextEncoder()
|
||||
const DEC = new TextDecoder()
|
||||
|
||||
@@ -27,7 +27,7 @@ import { createShellParser, type ShellParser } from '../shell/parse.ts'
|
||||
import { MountMode } from '../types.ts'
|
||||
import { ScriptSource } from '../runtime/policy/types.ts'
|
||||
import type { RuntimeLanguage } from '../runtime/types.ts'
|
||||
import { Workspace } from './workspace.ts'
|
||||
import { Workspace } from './workspace/workspace.ts'
|
||||
|
||||
// Mirrors python/tests/e2e/test_cli_dispatch.py.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { PathSpec } from '../types.ts'
|
||||
import { RAMSessionStore } from './session/ram.ts'
|
||||
import { toStateDict } from './snapshot/state.ts'
|
||||
import { Workspace } from './workspace.ts'
|
||||
import { Workspace } from './workspace/workspace.ts'
|
||||
|
||||
class ProbeRAMResource extends RAMResource {
|
||||
closeCalls = 0
|
||||
|
||||
@@ -19,7 +19,7 @@ import { OpsRegistry } from '../ops/registry.ts'
|
||||
import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { createShellParser, type ShellParser } from '../shell/parse.ts'
|
||||
import { Limit, MountMode, OnExceed } from '../types.ts'
|
||||
import { Workspace } from './workspace.ts'
|
||||
import { Workspace } from './workspace/workspace.ts'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
const engineWasm = readFileSync(require.resolve('web-tree-sitter/web-tree-sitter.wasm'))
|
||||
|
||||
@@ -22,7 +22,7 @@ import { OpsRegistry } from '../ops/registry.ts'
|
||||
import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { createShellParser, type ShellParser } from '../shell/parse.ts'
|
||||
import { Limit, MountMode } from '../types.ts'
|
||||
import { Workspace } from './workspace.ts'
|
||||
import { Workspace } from './workspace/workspace.ts'
|
||||
|
||||
class SignalProbeRuntime extends LanguageRuntime {
|
||||
readonly language = 'python'
|
||||
|
||||
@@ -19,7 +19,7 @@ import { OpsRegistry } from '../ops/registry.ts'
|
||||
import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { createShellParser, type ShellParser } from '../shell/parse.ts'
|
||||
import { Limit, MountMode, OnExceed } from '../types.ts'
|
||||
import { Workspace } from './workspace.ts'
|
||||
import { Workspace } from './workspace/workspace.ts'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
const engineWasm = readFileSync(require.resolve('web-tree-sitter/web-tree-sitter.wasm'))
|
||||
|
||||
@@ -21,8 +21,8 @@ import { ProvisionResult } from '../provision/types.ts'
|
||||
import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { MountMode, ResourceName } from '../types.ts'
|
||||
import { getTestParser, stderrStr } from './fixtures/workspace_fixture.ts'
|
||||
import type { ExecuteResult } from './workspace.ts'
|
||||
import { Workspace } from './workspace.ts'
|
||||
import type { ExecuteResult } from './workspace/workspace.ts'
|
||||
import { Workspace } from './workspace/workspace.ts'
|
||||
|
||||
const ENC = new TextEncoder()
|
||||
const SPEC = new CommandSpec({ rest: new Operand({ type: 'path' }) })
|
||||
|
||||
@@ -17,7 +17,7 @@ import { OpsRegistry } from '../ops/registry.ts'
|
||||
import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { MountMode } from '../types.ts'
|
||||
import { getTestParser, stderrStr, stdoutStr } from './fixtures/workspace_fixture.ts'
|
||||
import { Workspace } from './workspace.ts'
|
||||
import { Workspace } from './workspace/workspace.ts'
|
||||
|
||||
// Direct port of tests/workspace/test_cwd_integration.py.
|
||||
// Exercises cwd tracking + `cd`/`pwd`/`ls` across quoting/escaping of
|
||||
|
||||
@@ -19,7 +19,7 @@ import type { RAMFileCacheStore } from '../cache/file/ram.ts'
|
||||
import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { createShellParser } from '../shell/parse.ts'
|
||||
import { MountMode, PathSpec } from '../types.ts'
|
||||
import { Workspace } from './workspace.ts'
|
||||
import { Workspace } from './workspace/workspace.ts'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
const engineWasm = readFileSync(require.resolve('web-tree-sitter/web-tree-sitter.wasm'))
|
||||
|
||||
@@ -17,7 +17,7 @@ import { OpsRegistry } from '../../ops/registry.ts'
|
||||
import { RAMResource } from '../../resource/ram/ram.ts'
|
||||
import { Limit, MountMode, PathSpec } from '../../types.ts'
|
||||
import { getTestParser } from '../fixtures/workspace_fixture.ts'
|
||||
import { Workspace } from '../workspace.ts'
|
||||
import { Workspace } from '../workspace/workspace.ts'
|
||||
|
||||
const ENC = new TextEncoder()
|
||||
const DEC = new TextDecoder()
|
||||
|
||||
@@ -19,7 +19,7 @@ import { OpsRegistry } from '../ops/registry.ts'
|
||||
import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { createShellParser, type ShellParser } from '../shell/parse.ts'
|
||||
import { MountMode } from '../types.ts'
|
||||
import { Workspace } from './workspace.ts'
|
||||
import { Workspace } from './workspace/workspace.ts'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
const engineWasm = readFileSync(require.resolve('web-tree-sitter/web-tree-sitter.wasm'))
|
||||
|
||||
@@ -18,8 +18,8 @@ import { RAMResource } from '../resource/ram/ram.ts'
|
||||
import { MountMode } from '../types.ts'
|
||||
import { Channel, JobConsole } from '../shell/console/index.ts'
|
||||
import { getTestParser, stdoutStr } from './fixtures/workspace_fixture.ts'
|
||||
import type { ExecuteResult } from './workspace.ts'
|
||||
import { Workspace } from './workspace.ts'
|
||||
import type { ExecuteResult } from './workspace/workspace.ts'
|
||||
import { Workspace } from './workspace/workspace.ts'
|
||||
|
||||
const DEC = new TextDecoder()
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import { RAMResource } from '../../resource/ram/ram.ts'
|
||||
import { MountMode } from '../../types.ts'
|
||||
import { gzip } from '../../utils/compress.ts'
|
||||
import { getTestParser, stderrStr, stdoutStr } from '../fixtures/workspace_fixture.ts'
|
||||
import { Workspace } from '../workspace.ts'
|
||||
import { Workspace } from '../workspace/workspace.ts'
|
||||
|
||||
// Direct port of tests/workspace/executor/test_archive_relay.py: member
|
||||
// selectors stay off routing, extraction lands in the cwd or -C across
|
||||
|
||||
@@ -17,7 +17,7 @@ import { RAMResource } from '../../../resource/ram/ram.ts'
|
||||
import { CapacityState, MountMode } from '../../../types.ts'
|
||||
import type { CapacityResult } from '../../../types.ts'
|
||||
import { getTestParser } from '../../fixtures/workspace_fixture.ts'
|
||||
import { Workspace } from '../../workspace.ts'
|
||||
import { Workspace } from '../../workspace/workspace.ts'
|
||||
|
||||
// RAM backend that reports a fixed quota, standing in for a real filesystem
|
||||
// / a provider that exposes storage numbers (real disk free space is
|
||||
|
||||
@@ -18,7 +18,7 @@ import { RAMResource } from '../../../resource/ram/ram.ts'
|
||||
import type { FileStat } from '../../../types.ts'
|
||||
import { MountMode } from '../../../types.ts'
|
||||
import { getTestParser } from '../../fixtures/workspace_fixture.ts'
|
||||
import { Workspace } from '../../workspace.ts'
|
||||
import { Workspace } from '../../workspace/workspace.ts'
|
||||
import { parseGroup, parseOwner, parseTouchStamp } from './metadata.ts'
|
||||
|
||||
describe('parseOwner', () => {
|
||||
|
||||
@@ -17,7 +17,7 @@ import { IOResult } from '../../../io/types.ts'
|
||||
import { RAMResource } from '../../../resource/ram/ram.ts'
|
||||
import { MountMode, PathSpec } from '../../../types.ts'
|
||||
import { getTestParser } from '../../fixtures/workspace_fixture.ts'
|
||||
import { Workspace } from '../../workspace.ts'
|
||||
import { Workspace } from '../../workspace/workspace.ts'
|
||||
import {
|
||||
absPath,
|
||||
expandOperands,
|
||||
|
||||
@@ -18,7 +18,7 @@ import { describe, expect, it } from 'vitest'
|
||||
import { RAMResource } from '../../../resource/ram/ram.ts'
|
||||
import { createShellParser } from '../../../shell/parse.ts'
|
||||
import { ConsistencyPolicy, MountMode, PathSpec, ResourceName } from '../../../types.ts'
|
||||
import { Workspace } from '../../workspace.ts'
|
||||
import { Workspace } from '../../workspace/workspace.ts'
|
||||
import { dropServiceCaches } from './run.ts'
|
||||
|
||||
const ENC = new TextEncoder()
|
||||
|
||||
@@ -17,7 +17,7 @@ import { OpsRegistry } from '../../ops/registry.ts'
|
||||
import { RAMResource } from '../../resource/ram/ram.ts'
|
||||
import { MountMode } from '../../types.ts'
|
||||
import { getTestParser, stderrStr, stdoutStr } from '../fixtures/workspace_fixture.ts'
|
||||
import { Workspace } from '../workspace.ts'
|
||||
import { Workspace } from '../workspace/workspace.ts'
|
||||
|
||||
// Direct port of tests/workspace/test_cross_mount_errors.py.
|
||||
// Exercises error paths for cross-mount head/tail — cross_mount.test.ts
|
||||
|
||||
@@ -24,7 +24,7 @@ import { describe, expect, it } from 'vitest'
|
||||
import { RAMResource } from '../../resource/ram/ram.ts'
|
||||
import { createShellParser } from '../../shell/parse.ts'
|
||||
import { MountMode } from '../../types.ts'
|
||||
import { Workspace } from '../workspace.ts'
|
||||
import { Workspace } from '../workspace/workspace.ts'
|
||||
|
||||
const DEC = new TextDecoder()
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user