A PlcProject uploaded as a single .st/.xml file (not a zip) was stored as a
content-addressed blob with no extension, so the PLC scanner's extension-based
file discovery skipped it — an uploaded project produced zero findings.
When an "extractable" blob turns out not to be an archive, materialize it into
the artifact work dir under its original file name (from source_ref, which the
upload handler sets to the uploaded filename). analyze_tree then discovers it by
extension and reports a readable path. Falls back to the raw blob if the copy
fails. Covers the demo's upload -> scan path end to end.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a real file-upload path so PLC projects (and other blob artifacts:
firmware images, source archives, mobile packages) can be delivered to
the agent instead of referencing a local path.
- agent: POST /api/v1/targets/{id}/artifacts/upload (axum multipart);
stores bytes under {artifact_store_base_path}/uploads/{id}/... and
$push-es the artifact (with stored_path + size_bytes) onto the target.
- dashboard: upload_target_artifact server fn (reqwest multipart) + wizard
file input for blob artifact kinds, with a PLC format selector.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>