Skip to content

feat(skills): rocket-file-browser skill (full scope)#137

Open
afernandez-stratio wants to merge 6 commits into
Stratio:mainfrom
afernandez-stratio:feature/rocket-file-browser-skill
Open

feat(skills): rocket-file-browser skill (full scope)#137
afernandez-stratio wants to merge 6 commits into
Stratio:mainfrom
afernandez-stratio:feature/rocket-file-browser-skill

Conversation

@afernandez-stratio

@afernandez-stratio afernandez-stratio commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Qué

Skill compartida rocket-file-browser: gestiona ficheros en el File Browser HDFS de Rocket (/fileBrowser/*) desde el sandbox de OpenCode, sin el roundtrip manual por la UI.

Autenticación e identidad

  • Solo mTLS: cert cliente del sandbox (/vault/secrets/cert.crt, CN = usuario operador, p.ej. s000001-user) contra el listener mutual-TLS de Rocket (puerto 7777).
  • Rocket en Oauth2Mutual; el ingress público (443) es solo oauth2/cookie e ignora el cert → ROCKET_API_URL apunta al mutual por DNS de servicio interno (host:port, sin path): https://<rocket-instance>.<rocket-namespace>:7777 (p.ej. https://rocket.s000001-rocket:7777).
  • Sin token, sin impersonación — permisos propios del usuario (Gosec por CN). Verificado end-to-end en el entorno s000001.

Alcance (completo)

Router (estilo cowork-api): SKILL.md + tasks/ + cliente Python scripts/rocket_file_browser.py.

Op Endpoint
download POST /fileBrowser/download (streaming a disco)
upload 2 fases uploadLocalFile + putLocalFileToHadoopFs (+ retry HTTP 420)
ls POST /fileBrowser/findByPath
cp / mv PUT /fileBrowser/copy / /fileBrowser/update
rm DELETE /fileBrowser/delete
mkdir POST /fileBrowser/createDir
compress / extract PUT /fileBrowser/compress (codecs ZStandard/Lz4/Snappy/Gzip/Bzip2/Zip/TarGz) / /fileBrowser/extract
filesystems GET /fileBrowser/getFilesystems (auto-resuelve si solo hay uno)

Rechaza raíces restringidas de Rocket en cliente, muestra HTTP code + body verbatim en error. Espejo español bajo es/. Tests pytest en scripts/test_rocket_file_browser.py.

Dependencia

ROCKET_API_URL la inyecta la plataforma en el pod del sandbox — ver Stratio/genai-api#507 y Stratio/genai-agents-sandbox#71.

Test plan

Probado end-to-end contra Rocket real (port-forward + certs locales): los 9 endpoints OK — filesystems, mkdir, upload, ls, cp, mv, compress, extract, download (diff idéntico), rm. Ejemplo:

  • ROCKET_API_URL=https://rocket.s000001-rocket:7777
  • python3 scripts/rocket_file_browser.py filesystems

🤖 Generated with Claude Code

@afernandez-stratio afernandez-stratio changed the title feat(skills): rocket-file-browser skill (MVP: download/upload) feat(skills): rocket-file-browser skill (full scope) Jun 30, 2026
afernandez-stratio and others added 6 commits July 11, 2026 22:43
New shared skill that moves files between the Stratio sandbox and Rocket's
HDFS File Browser (/rocket/fileBrowser/*), authenticating with the sandbox
client certificate over mTLS against Rocket's mutual-TLS listener (port 7777).
Calls run with the operating user's own Rocket permissions (no token, no
impersonation).

Router layout (like cowork-api): minimal SKILL.md + tasks/ + a single Python
client scripts/rocket_file_browser.py.

MVP scope:
- filesystems: GET /fileBrowser/getFilesystems (discover id/type, auto-resolve
  when only one exists)
- download: POST /fileBrowser/download, streamed to a local path
- upload: two-phase (POST /fileBrowser/uploadLocalFile then
  /putLocalFileToHadoopFs) with automatic retry on HTTP 420

Reuses guides/external-api-calls.md for mTLS/env/error conventions; the only
delta is ROCKET_API_URL on the mutual-TLS port. Refuses Rocket-restricted roots
client-side and surfaces HTTP code + body verbatim on failure. Spanish mirror
under es/. Planned extension: ls/cp/mv/rm/mkdir/compress/extract + pytest.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ess/extract)

Extend the MVP (download/upload/filesystems) to the complete File Browser
surface, all through the same Python client:

- ls       -> POST /fileBrowser/findByPath
- mkdir    -> POST /fileBrowser/createDir
- rm       -> DELETE /fileBrowser/delete (one or more paths)
- cp / mv  -> PUT /fileBrowser/copy / /fileBrowser/update
- compress -> PUT /fileBrowser/compress (codecs: ZStandard, Lz4, Snappy, Gzip,
              Bzip2, Zip [default], TarGz)
- extract  -> PUT /fileBrowser/extract

Each op reuses the existing mTLS/filesystem-resolution/restricted-path-guard
helpers and surfaces HTTP code + body verbatim on failure. Added a task file per
operation (EN + es mirror), updated the SKILL.md router index and README, and
added a pytest suite (payload construction, fs auto-resolution, restricted-path
guard, error surfacing, 420 upload-commit retry).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sion

End-to-end testing against a live Rocket showed the compress endpoint appends
the codec extension to compressedFile (e.g. out.zip -> out.zip.zip), which then
breaks extract if the original name is reused. Document the behaviour in the
compress task (EN + es) and emit a note from the client so the user knows the
real archive name to extract.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…amples

ROCKET_API_URL resolves with the short service form
https://<rocket-instance>.<rocket-namespace>:7777/rocket (same style genai-api
produces for it via discovery, resolved through the pod search domain). Replace
the FQDN examples (svc.<cluster-domain>) accordingly in SKILL.md, README and the
client docstring/error message (EN + es).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The trailing "/rocket" was mistaken for a fixed path prefix; it was actually the
instance name (always "rocket" in the examples). On the mutual listener the
client calls /fileBrowser/... directly, so ROCKET_API_URL must be just the
host:port, e.g. https://rocket.s000001-rocket:7777 (verified with another
instance: https://rocket-dlc.s000001-rocket:7777/fileBrowser/getFilesystems).
Update SKILL.md, README and the client docstring/error message (EN + es).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review pass over the whole feature before merge:

- guard_path did a raw string prefix check, so "/data/../backups/x" (and other
  "." / ".." / repeated-slash forms) slipped past the restricted-root guard
  even though it resolves to a restricted path. Normalize with posixpath.normpath
  before comparing. Careful with normpath's POSIX quirk of preserving an exactly
  double leading slash verbatim -- lstrip first so re-prepending "/" doesn't
  accidentally produce that case.
- Add tests for the traversal forms this fixes, plus legit paths that must
  still pass (including a look-alike prefix like /backups-not-really/x).
- Drop a redundant double .strip('"') in the upload docker_path parsing.
- SKILL.md/README (EN+es) still described the endpoint as "/rocket/fileBrowser/*"
  in the frontmatter description and opening paragraph -- stale from before the
  host:port-no-path fix. Corrected to "/fileBrowser/*".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@afernandez-stratio
afernandez-stratio force-pushed the feature/rocket-file-browser-skill branch from 66f9432 to 2573a97 Compare July 11, 2026 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant