Repository navigation
Add a vault upload dialog with optional ingest and serve /api/upload-vault - #14
Merged
Merged
Conversation
The graph sidebar's upload control (edit.js, forked from CE's viewer)
posts the chosen files to /api/upload-vault. CE's viewer_server serves
that route, but the Switchbay daemon never did, so the built-in path
answered 405.
Add the route to the daemon with CE's behaviour: multipart, every part
with a filename is saved, names are sanitised with CE's rules, files are
written to <workspace>/vault/raw/ with no ingest run, and the reply is
{ok, saved: [names]}. Each file is capped at 50 MB, the same cap as CE's
other upload route and /api/ingest/from-upload.
Keep the path on the daemon when skill embeds are proxied. Through
/embed/ce the request hits the proxy's 1 MiB body limit and any normal
PDF fails with 413; the daemon route writes to the same vault/raw/.
Signed-off-by: benjsmith <benjamincharlessmith@gmail.com>
The + buttons in the Files browser, the graph sidebar and the embedded CE sidebar now open one Switchbay dialog: pick several files, choose whether to ingest them, upload. POST /api/upload-vault takes an ingest form field; when set, saved files are queued as ingest runs that drain through CE's local ingest when CE is installed, or through the ingest agent when a model provider is ready. With neither, files are saved and the response says why nothing was ingested. The path is reserved for the daemon so the embedded CE sidebar reaches it too. Signed-off-by: benjsmith <benjamincharlessmith@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The graph sidebar's
+button in the vendored CE editor (edit.js) posts to/api/upload-vault, but the Switchbay daemon never served that path. In the built-in sidebar the button was handled separately and always sent one file straight to an ingest agent. In the embedded CE sidebar the request went through the/embed/ceproxy to CE's own viewer, which works for small files but fails with 413 above 1 MiB, the proxy's body limit.This adds a daemon route for
POST /api/upload-vaultand a single Switchbay upload dialog that every+button opens.Route. It follows CE's
viewer_server.pyhandler: multipart form, every part with a filename is saved into<workspace>/vault/raw/, names are sanitised with CE's rules (basename only, anything outside[A-Za-z0-9._-]becomes_, empty or dot-leading names are refused), an existing file of the same name is replaced, and the reply is{"ok": true, "saved": [...]}. Files are streamed to a temporary file and moved into place, with a 50 MB per-file limit (413 with the names already saved). An optionalingestform field (true/false, default false) asks for the saved files to be ingested. They are queued as ingest runs on the existing drain queue, in one batch: through CE's deterministic local ingest when CE is installed, or through the ingest agent (one run per file) when CE is absent and the default model provider is ready. With neither, the files are still saved and the reply'singest.messagesays why nothing was ingested. When ingest is requested the reply also carriesingest: {backend, queued, runs: [{file, run_id, vault_path}]}.Reserved path.
/api/upload-vaultis added to the daemon-reserved API list, so the embedded CE sidebar's own upload also reaches this route instead of CE's viewer through the proxy, which removes the 1 MiB limit for uploads.Dialog.
UploadVaultDialogis a Switchbay React component in the shell, not part of edit.js. It lets you pick several files (and add or remove them before uploading), has an "Ingest automatically" checkbox, and uses the theme variables, so it follows light and dark mode. Switchbay has no global auto-ingest setting, so the checkbox starts off and remembers the last choice in localStorage (sy.uploadVault.ingest). After the upload a toast reports what was saved and whether ingest was queued. For agent ingest it opens the run in the Agents view, as the old+did.Entry points. A new
+in the Files browser toolbar opens the dialog. It is always there, with or without CE, so upload keeps working when the Graph tab is hidden. The built-in sidebar and Zen graph+buttons open the same dialog. CE's embed has no hook or message for its+, so the embedded CE sidebar's+is intercepted on the Switchbay side (a capture-phase click listener on the sidebar slot) and opens the dialog too. If CE's+ever runs its own handler, it posts a plain upload to the same reserved route. edit.js is unchanged. Dropping files onto the sidebar still stages them and starts ingest agents, as before.Tests.
tests/unit/test_upload_vault.pychecks that the daemon resolves edit.js's literal request, and covers multi-file uploads, same-name replacement, CE's sanitising cases (including../../escape.txt,/etc/passwd,.envand..), that path traversal stays insidevault/raw/, the size cap, non-multipart and file-less requests,ingest=false,ingest=truewith CE present (draining through local ingest), with CE absent and a provider ready (agent runs), and with no backend (message, no runs).frontend/src/lib/uploadVault.test.tscovers the dialog's request (ingest flag first, onefilefield per file), the stored checkbox preference, and the result messages.embedMount.test.tschecks that the path is reserved.