Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions apps/chat-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@
"dist/*": "dist"
},
"dependencies": {
"rocketride": "workspace:*",
"@types/ws": "^8.18.1",
"chart.js": "^4.4.0",
"lucide-react": "^0.577.0",
"react": "^18.2.0",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18.2.0",
"react-markdown": "^10.1.0",
"react-syntax-highlighter": "^15.6.6",
"chart.js": "^4.4.0",
"react-chartjs-2": "^5.2.0",
"recharts": "^2.8.0",
"rehype-raw": "^7.0.0",
"remark-gfm": "^4.0.1",
"rocketride": "workspace:*",
"web-vitals": "^5.2.0",
"ws": "^8.20.1"
},
Expand All @@ -29,8 +29,8 @@
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/forms": "^0.5.10",
"@tailwindcss/typography": "^0.5.16",
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
"@types/react": "~18.3.31",
"@types/react-dom": "~18.3.7",
"@types/react-syntax-highlighter": "^15.5.13",
"@types/ws": "^8.18.1",
"autoprefixer": "^10.5.0",
Expand Down
6 changes: 3 additions & 3 deletions apps/dropper-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"dist/*": "dist"
},
"dependencies": {
"rocketride": "workspace:*",
"@textea/json-viewer": "^4.0.1",
"@types/ws": "^8.18.1",
"lucide-react": "^0.577.0",
Expand All @@ -18,6 +17,7 @@
"recharts": "^2.8.0",
"rehype-raw": "^7.0.0",
"remark-gfm": "^4.0.1",
"rocketride": "workspace:*",
"web-vitals": "^5.2.0",
"ws": "^8.20.1"
},
Expand All @@ -28,8 +28,8 @@
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/forms": "^0.5.10",
"@tailwindcss/typography": "^0.5.16",
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
"@types/react": "~18.3.31",
"@types/react-dom": "~18.3.7",
"@types/react-syntax-highlighter": "^15.5.13",
"@types/ws": "^8.18.1",
"autoprefixer": "^10.5.0",
Expand Down
10 changes: 7 additions & 3 deletions apps/explorer-ui/src/ExplorerSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ const ExplorerSidebar: React.FC = () => {
const refresh = useCallback(async () => {
if (!client || !isConnected) { setEntries([]); return; }
try {
const allEntries = await listRecursive(client, '');
const allEntries = await listRecursive(client, '@');
Comment thread
coderabbitai[bot] marked this conversation as resolved.
setEntries(allEntries);
} catch {
setEntries([]);
Expand Down Expand Up @@ -193,7 +193,10 @@ const ExplorerSidebar: React.FC = () => {
if (!client) return;
try {
const name = sourcePath.includes('/') ? sourcePath.substring(sourcePath.lastIndexOf('/') + 1) : sourcePath;
const newPath = targetDir ? `${targetDir}/${name}` : name;
// The tree is rooted at the '@' mount (listRecursive above), so an
// empty targetDir IS that root — carry '@' through instead of
// letting a bare name silently target the plain user store.
const newPath = `${targetDir || '@'}/${name}`;
if (newPath === sourcePath) return;
await client.fsRename(sourcePath, newPath);
// Keep open editor tabs in sync with the move. A moved file must
Expand Down Expand Up @@ -228,7 +231,8 @@ const ExplorerSidebar: React.FC = () => {
if (!client) return;
try {
for (const file of files) {
const path = targetDir ? `${targetDir}/${file.name}` : file.name;
// Same root rule as handleMove: '' targetDir is the '@' mount.
const path = `${targetDir || '@'}/${file.name}`;
const { handle } = await client.fsOpen(path, 'w');
const chunkSize = 4 * 1024 * 1024; // 4 MB
try {
Expand Down
4 changes: 2 additions & 2 deletions apps/shell-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
"@rsbuild/plugin-basic-ssl": "^1.2.3",
"@rsbuild/plugin-react": "~2.0.1",
"@rsbuild/plugin-type-check": "~1.3.5",
"@types/react": "~18.2.21",
"@types/react-dom": "~18.2.7",
"@types/react": "~18.3.31",
"@types/react-dom": "~18.3.7",
"dts-bundle-generator": "^9.5.1"
}
}
4 changes: 2 additions & 2 deletions apps/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -544,8 +544,8 @@
"@types/mocha": "^10.0.10",
"@types/node": "20.x",
"@types/proper-lockfile": "^4.1.4",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@types/react": "^18.3.31",
"@types/react-dom": "^18.3.7",
"@types/vscode": "~1.99.0",
"@types/vscode-webview": "^1.57.5",
"@vscode/debugprotocol": "^1.68.0",
Expand Down
10 changes: 7 additions & 3 deletions nodes/src/nodes/anonymize/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
gliner
# onnxruntime is an indirect dep (via gliner), not imported here.
# Pin 1.20.1 to match the one shared install (rtmlib needs exactly that).
onnxruntime-gpu==1.20.1; platform_system != 'Darwin'
onnxruntime==1.20.1; platform_system == 'Darwin'
# One shared version across every onnxruntime consumer, so the ~200 MB runtime installs once.
# rtmlib/gliner declare it unpinned and faster-whisper wants <2,>=1.14 -- the exact number is
# ours to choose, but all these files must agree. 1.20.1 was withdrawn from PyPI for the -gpu
# build (the CPU build of it survives), which is why this moved; 1.20.2 is the mirror trap --
# it exists only for -gpu, so pinning it would break macOS.
onnxruntime-gpu==1.22.0; platform_system != 'Darwin'
onnxruntime==1.22.0; platform_system == 'Darwin'
10 changes: 7 additions & 3 deletions nodes/src/nodes/audio_transcribe/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ tokenizers
huggingface-hub
tqdm
# onnxruntime is an indirect dep (via faster-whisper), not imported here.
# Pin 1.20.1 to match the one shared install (rtmlib needs exactly that).
onnxruntime-gpu==1.20.1; platform_system != 'Darwin'
onnxruntime==1.20.1; platform_system == 'Darwin'
# One shared version across every onnxruntime consumer, so the ~200 MB runtime installs once.
# rtmlib/gliner declare it unpinned and faster-whisper wants <2,>=1.14 -- the exact number is
# ours to choose, but all these files must agree. 1.20.1 was withdrawn from PyPI for the -gpu
# build (the CPU build of it survives), which is why this moved; 1.20.2 is the mirror trap --
# it exists only for -gpu, so pinning it would break macOS.
onnxruntime-gpu==1.22.0; platform_system != 'Darwin'
onnxruntime==1.22.0; platform_system == 'Darwin'
44 changes: 27 additions & 17 deletions nodes/src/nodes/tool_filesystem/IGlobal.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@
"""
File system tool node — global (shared) state.

Resolves the account's ``client_id`` from the ``ROCKETRIDE_CLIENT_ID`` env var
(injected by the task engine in ``task_engine.py``) and builds a single
``FileStore`` scoped to ``users/<client_id>/files/``. The instance exposes
Resolves the account identity and the storage anchor from the TASK FILE the
engine published (``rocketlib.getTask()``: the ``identity`` and ``storage``
blocks, see task_engine._build_task) and builds a single ``FileStore``
rooted at that anchor — the owning user's tree
(``users/<userId>/files/``) for development runs, a task-specific team
subtree (``teams/<teamId>/files/tasks/<projectId>/``) for deployed runs —
so node-level paths are identical in both modes. The instance exposes
per-operation allow-flags and a path whitelist that IInstance enforces before
every tool call.

Expand All @@ -37,10 +41,8 @@

from __future__ import annotations

import os
import re

from ai.account.store import Store
from ai.common.config import Config
from rocketlib import IGlobalBase, OPEN_MODE, warning

Expand Down Expand Up @@ -76,24 +78,32 @@ def beginGlobal(self) -> None:
self.path_patterns = self._build_path_patterns(cfg)
self.target_dir, self.emit_url, self.url_expires_in = self._sink_config(cfg)

client_id = os.environ.get('ROCKETRIDE_CLIENT_ID', '').strip()
if not client_id:
warning(
'tool_filesystem: ROCKETRIDE_CLIENT_ID env var is missing; tool methods will be disabled. This usually means the node is running outside the task engine.'
)
self.client_id = None
self.file_store = None
return

try:
store = Store.create()
self.file_store = store.get_file_store(client_id)
self.client_id = client_id
# Engine identity: this node runs INSIDE the engine subprocess
# with no account session, and its paths come from LLM tool
# calls — so it gets the most restricted context: plain paths
# only, every @/Team, @/Org and @/User path and reserved subtree
# (.logs, .deployments) rejected by the store. It must never
# hold an internal identity (that would let a pipeline address
# any team's storage unchecked). Identity and the storage anchor
# (the owner's tree for dev runs, a task-specific team subtree
# for deploy runs) come from the task file the engine published
# via rocketlib.getTask() — never the environment — and the
# store resolves them itself, so this node needs no plumbing.
from ai.account.store import Store

self.file_store = Store.engine_file_store()
self.client_id = getattr(self.file_store, '_client_id', None)
except Exception as e:
warning(f'tool_filesystem: failed to initialise FileStore: {e}')
self.client_id = None
self.file_store = None

if self.file_store is None:
warning(
'tool_filesystem: no running task with an identity (rocketlib.getTask); tool methods will be disabled. This usually means the node is running outside the task engine.'
)

@staticmethod
def _build_path_patterns(cfg: dict) -> list[re.Pattern]:
"""Parse the repeated ``whitelistPattern`` rows into compiled regexes."""
Expand Down
30 changes: 18 additions & 12 deletions nodes/src/nodes/tool_filesystem/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ A RocketRide tool node that gives an AI agent read/write access to the account-s
## What it does

Exposes the account file store, the same storage area the client SDK reaches via its
`fs_*` methods, to an agent as a set of callable tools. All paths are relative to
`users/<client_id>/files/`, so files written by the agent are visible to the client SDK
and vice versa. The account is resolved automatically from the `ROCKETRIDE_CLIENT_ID`
env var injected by the task engine, no account configuration is needed on the node.
If that env var is missing or the account store fails to initialise, a warning is logged
and **all** tool methods are hidden from the agent.
`fs_*` methods, to an agent as a set of callable tools. All paths are plain and
relative to the task's **storage anchor**, which the task file provides: the owning
user's file tree (`users/<client_id>/files/`) for development runs, or a task-specific
subtree of the deployment team's storage (`teams/<teamId>/files/tasks/<projectId>/`)
for deployed runs — so files written by the agent are visible in the file browser and
vice versa, and node behavior is identical in both modes. Identity and the anchor are
resolved automatically from the running task (`rocketlib.getTask()`), never from the
environment; no account configuration is needed on the node. If no task identity is
available or the account store fails to initialise, a warning is logged and **all**
tool methods are hidden from the agent.

The node plays two roles. As a **tool** it is connected to agents via the `tool` invoke
channel (see *Available tools*). As a **pipeline sink** it also accepts data lanes and
Expand Down Expand Up @@ -39,7 +43,7 @@ may touch.
| `emitUrl` | boolean | Default false. Also attach a time-limited signed download URL to the emitted document metadata. |
| `urlExpiresIn` | integer | Default 3600 (max 3600). TTL in seconds for the signed URL when `emitUrl` is on. |
| `whitelistPattern` | string | Default empty. |
| `pathWhitelist` | array | Regex patterns applied to the relative path of every operation using re.search semantics, a partial match anywhere in the path is enough, so a pattern like 'secret' will also match 'notsecret/file.txt'. Anchor with ^ and $ if you need a full-path match (e.g. '^docs/.*$'). If non-empty, a path must match at least one pattern. If empty, all paths under users/<client_id>/files/ are allowed. |
| `pathWhitelist` | array | Regex patterns applied to the relative path of every operation using re.search semantics, a partial match anywhere in the path is enough, so a pattern like 'secret' will also match 'notsecret/file.txt'. Anchor with ^ and $ if you need a full-path match (e.g. '^docs/.*$'). If non-empty, a path must match at least one pattern. If empty, all paths under the task's storage anchor are allowed. |


### Path whitelist
Expand Down Expand Up @@ -143,15 +147,17 @@ subprocess long before the LLM ever sees the result.

## Storage location

Files land under the configured storage backend (defaults to `~/.rocketlib/store/`). For
the default filesystem backend the absolute path is:
Files land under the configured storage backend (defaults to `~/.rocketlib/store/`).
For the default filesystem backend the absolute path is the task's storage anchor
plus the relative path:

```text
<store>/users/<client_id>/files/<path>
<store>/users/<client_id>/files/<path> # development runs
<store>/teams/<teamId>/files/tasks/<projectId>/<path> # deployed runs
```

Each account gets its own isolated `files/` directory, the node picks up the current
account automatically, no configuration needed.
The anchor comes from the task file the engine wrote at spawn; the node picks up
the current task automatically, no configuration needed.

---

Expand Down
2 changes: 1 addition & 1 deletion nodes/src/nodes/tool_filesystem/services.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
},
"filesystem.pathWhitelist": {
"title": "Path Whitelist",
"description": "Regex patterns applied to the relative path of every operation using re.search semantics: a partial match anywhere in the path is enough, so a pattern like 'secret' will also match 'notsecret/file.txt'. Anchor with ^ and $ if you need a full-path match (e.g. '^docs/.*$'). If non-empty, a path must match at least one pattern. If empty, all paths under users/<client_id>/files/ are allowed.",
"description": "Regex patterns applied to the relative path of every operation using re.search semantics: a partial match anywhere in the path is enough, so a pattern like 'secret' will also match 'notsecret/file.txt'. Anchor with ^ and $ if you need a full-path match (e.g. '^docs/.*$'). If non-empty, a path must match at least one pattern. If empty, all paths under the task's storage anchor (the owning user's file tree for development runs, the task's team subtree for deployed runs) are allowed.",
"type": "array",
"optional": true,
"minItems": 0,
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@
"@eslint/js": "^9.39.4",
"@homebridge/node-pty-prebuilt-multiarch": "^0.13.1",
"@types/node": "^20.19.41",
"@types/react": "18.2.79",
"@types/react-dom": "18.2.25",
"@types/react": "~18.3.31",
"@types/react-dom": "~18.3.7",
"adm-zip": "^0.5.17",
"concurrently": "^8.2.2",
"dotenv": "^16.4.5",
Expand All @@ -131,12 +131,12 @@
"gitignore-globs": "^0.1.1",
"glob": "^11.0.0",
"globals": "^15.12.0",
"lefthook": "^2.1.8",
"listr2": "^9.0.5",
"npm-run-all": "^4.1.5",
"prettier": "^3.8.3",
"rimraf": "^5.0.5",
"tar": "^7.5.15",
"lefthook": "^2.1.8",
"typescript": "^5.3.3",
"typescript-eslint": "^8.60.0"
}
Expand Down
3 changes: 2 additions & 1 deletion packages/ai/src/ai/account/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
from .report import Reporter
from .store import Store, IStore, StorageError, VersionMismatchError, STORE_MAX_RETRY_ATTEMPTS, LOG_PAGE_SIZE
from .deployment_store import DeploymentStore
from .models import AccountInfo, DeploymentRecord, resolve_team_permissions
from .models import AccountInfo, DeploymentRecord, RequestContext, resolve_team_permissions

__all__ = [
'Account',
Expand All @@ -79,6 +79,7 @@
'KeyStore',
'Reporter',
'Store',
'RequestContext',
'IStore',
'StorageError',
'VersionMismatchError',
Expand Down
Loading
Loading