Config Explorer: in-UI file manager for shared agent config #8
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "config-explorer"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #5.
Summary
Adds the Config Explorer — an in-UI file manager for the shareable agent-config subtree (
skills/,agents/,commands/,hooks/,output-styles/,settings.json,CLAUDE.md) of theclaude-sessionDocker volume. Operators edit these from the web UI; workers symlink the five directories into~/.claude/at boot and copy the two single files.The feature name is intentionally tool-agnostic so the surface stays sensible if the worker agent is ever swapped.
What's included
claude-sessiondeclared in the top-levelvolumes:block; bind-mounted into Nexus at/shared-config; removed thedocker.createVolumelazy-create fallback so compose owns the lifecycle.nexus/src/lib/server/config-explorer/volume-fs.ts(path-safety guards, atomic writes, JSON validation, 256 KiB cap, symlink refusal); six SvelteKit routes at/api/config-explorer/{tree,file,mkdir,rename,path,upload}; auth automatic viahooks.server.tspath-prefix gate. New env varSHARED_CONFIG_DIR(default/shared-config/config).DataTransfer.typessniffed), multi-select, F2 rename, Delete key, context menu, recursive folder delete with confirm-with-preview. CodeMirror 6 in the editor pane via granular imports + dynamicimport()insideonMountso it ships in a route-specific chunk. Persistent yellow restart-required banner wheneversettings.json/CLAUDE.mdare open.AGENTS.mdfact #3 reworked to document the two subtrees on the shared volume; newAGENTS.md"Environment variables" reference table covering every var Nexus + worker consume;worker/README.mdmount table updated.Verification
pnpm typecheck— 0 errors, 0 warnings, 685 files checked.pnpm test— 113 passing, 23 skipped (Windows symlink + better-sqlite3 binding cases; CI's node:22-alpine runs them).volume-fs.test.tscovers every traversal vector, the 256 KiB boundary, binary/NUL detection, JSON validation, mkdir/rename/unlink semantics, listTree sorting + auto-mkdir of the base, atomicity under simulated rename failure, and symlink refusal across listTree/readFile/unlink (auto-skipped on win32 per AGENTS.md convention).docker compose up -d --build→ unlock vault → open Config → createskills/test/SKILL.md→ Save.docker exec <worker> readlink /root/.claude/skillsshould return/session/config/skills.settings.jsonin UI → restart worker → content present andstat -c '%F'reportsregular file.PUT /api/config-explorer/filewithpath: "../../etc/passwd"should return 400.Operator note
Workers already running when this lands won't see the new symlink seeding until they're recreated (the entrypoint changes only run at container start). Either recycle the fleet or accept that existing workers don't get config propagation until next spawn.
CodeMirror 6 loaded via dynamic import() inside an $effect, so the @codemirror/* modules ship in a route-specific chunk rather than the entry bundle. Granular packages only (no umbrella) so tree-shaking works. Features: - Markdown / JSON syntax highlighting; YAML and shell via legacy-modes + StreamLanguage; plain text fallback. - Dark theme keyed to the existing CSS tokens (--bg, --surface, --surface-2, --border, --accent, --muted, --text). - Persistent yellow restart banner whenever the path is settings.json or CLAUDE.md (those files are copied, not symlinked, into workers). - Binary placeholder when the server flagged the file as binary. - onChange callback fires on every doc edit; the parent owns dirty state and the Save action. CodeMirror injects DOM (.cm-editor) that Svelte's style scoper cannot reach, so the relevant rules use :global(...) inside the editor host. Adds @codemirror/{state,view,language,lang-markdown,lang-json,legacy-modes} to nexus dependencies (granular only; no codemirror umbrella).Actually, the shared configuration directory should have a simpler design and only symlink to ~/.claude, as Claude Code itself supports concurrent processes and the folder is single-user-only.
Replace the piecemeal "credentials copied + five subdirs symlinked + two files copied" boot sequence with a single symlink: ln -sfn /session/dot-claude /root/.claude Every worker's ~/.claude IS the same directory on the shared claude-session volume. The previous model was broken for credentials -- claude-code refreshes the OAuth token by writing back to ~/.claude/.credentials.json, but the per-worker copy meant refreshes leaked locally and the next worker spawn read a stale token. Now the file is shared, refreshes propagate fleet-wide, and the sibling .credentials.lock claude-code uses for concurrent-refresh coordination also lives on the shared filesystem so it works across containers. Per-session state (projects/<cwd>) stays distinct because each session runs in its own --worktree NAME with a unique cwd path, so the known claude-code JSONL concurrency bugs don't trip -- different sessions write to different projects/<cwd>/ subdirectories. settings.json and CLAUDE.md are now live-shared too. Drop the restart-required banner/toast/help-text from FileEditor and the page; edits in the UI are visible to running workers immediately. Existing deployments: the entrypoint auto-migrates pre-refactor volumes by moving /session/credentials.json into /session/dot-claude/ on first boot. No re-bootstrap needed. Files touched: - worker/entrypoint.sh -- collapse the boot block to one symlink - worker/bootstrap-claude-auth.sh -- pre-symlink so login writes directly to the volume - nexus/src/lib/server/config.ts -- SHARED_CONFIG_DIR default -> /shared-config/dot-claude - nexus/src/lib/server/workers/session-probe.ts -- probe the new path - nexus/src/lib/components/config-explorer/FileEditor.svelte -- drop restart banner - nexus/src/routes/config-explorer/+page.svelte -- drop restart-required language from help and save toast - AGENTS.md fact #3 -- rewritten for the whole-dir share - worker/README.md -- mount table + entrypoint-steps list updated- Allow leading-dot segments in the path-safety guard. Path traversal (".", "..") is still rejected; the segment whitelist regex always accepted dotfiles, only the explicit startsWith('.') check blocked them. .credentials.json, .credentials.lock, .last-cleanup etc. are now visible and editable in the UI. - Clicking empty space inside the tree container (or pressing Escape while it has focus) clears multi-select and closes the open file. Discard-changes prompt still gates the close when there are dirty edits.