Skip to content

feat: publish an image or a video with stele attach - #9

Merged
ProJedi1234 merged 1 commit into
feat/attachment-content-typesfrom
feat/stele-attach
Aug 25, 2026
Merged

ProJedi1234 merged 1 commit into
feat/attachment-content-typesfrom
feat/stele-attach

Conversation

@ProJedi1234

@ProJedi1234 ProJedi1234 commented Aug 24, 2026 •

Copy link
Copy Markdown
Owner

The server has stored attachments since it learned to, and its skill document — the one stele skill proxies live — already tells agents to run stele attach. Until now that command did not parse, so the live contract named a tool that did not exist. It is the same failure --ttl had, in the other direction: last time the document was stale, this time the client was.

The request is a publish with a binary content type, because on the server an attachment is a page whose body is bytes. What earns it a command of its own is the answer, not the request. An attachment has two URLs — the bytes under /static/, and a viewer page about the file — and mixing them up fails silently, because an <img> pointed at the viewer renders nothing and both answer 200. So stdout is the bytes, which is the value src=$(stele attach chart.png) captures and the one a page-writing agent needs; the viewer goes to stderr under the deadline, for the human who wants to send someone a link.

--json names both and calls neither of them url. That key means "the viewer" on every other command, so whichever meaning it took here would be wrong half the time and wrong silently — a caller reaching for .url would get a working URL of the wrong kind. Absent, they get nothing, which is a failure they can see.

An unrecognised extension is refused before the upload rather than guessed at. publish may fall through to text/html because that is what it is for; the same shrug on a JPEG spends the upload and comes back as a 415 naming a type nobody chose. --filename defaults to the file's own basename and is omitted rather than refused when that name is one the server would reject — the caller never typed it, and the server's fallback is a working download where a 400 about an unpassed parameter is not. An explicit --filename travels untouched, because there the caller has an opinion and the server is the one that gets to answer it.

Exit 6 and its two messages widen to cover an attachment. The status is the same and the code stays one, because the caller's next move has the same shape either way; what differs is the advice, and 413 now carries both halves rather than telling someone with an oversized video to drop their inline images.

Version goes to 0.4.0 in this commit. The server version-gates on the User-Agent, and a capability its skill document references should be a version an operator can require.

Checks

swift test — 168 tests in 18 suites, all passing. swift build -c release clean, no warnings.

The executable has no test target, so the command itself was verified by driving the built binary against a stand-in server that records what it was sent. Confirmed on the wire: POST /pages with Content-Type: image/png, ?filename= derived from the basename, PNG magic bytes intact, ?slug=/?ttl=/?filename= all travelling together, an explicit --content-type overriding inference, and publish still sending text/html with no filename. Confirmed in the output: stdout is one line and it is the /static/ URL, stderr carries the deadline and the viewer, --json emits bytes/viewer/slug/expires and nothing on stderr, and an unknown extension exits 1 before any request is built.

Against the real deployment stele attach currently exits 6 — stele.olympus.lan predates the attachment support and answers Allowed: text/css, text/html, text/markdown, text/plain. The error path is therefore verified end to end against a real server; the success path is not, and cannot be until that deployment is updated.

scripts/integration-smoke.sh gains section 4c, which covers the whole of it — byte-identical fetch-back, both URLs and their content types, the Content-Disposition filename, the refusal, and stele delete reaching an attachment. It has not been run, for the same reason.

🤖 Generated with Claude Code

The server has stored attachments since it learned to, and its skill
document already tells agents to run this command — so until now the
live contract named a command that did not parse. That is the same
failure `--ttl` had, in the other direction: last time the document was
stale, this time the client was.

The request is a `publish` with a binary content type, because on the
server an attachment is a page whose body is bytes. What earns it a
command of its own is the answer, not the request. An attachment has two
URLs — the bytes under `/static/`, and a viewer page about the file —
and mixing them up fails silently, because an `<img>` pointed at the
viewer renders nothing and both answer 200. So stdout is the bytes,
which is the value `src=$(stele attach chart.png)` captures and the one
a page-writing agent needs; the viewer goes to stderr under the
deadline, for the human who wants to send someone a link.

`--json` names both and calls neither of them `url`. That key means "the
viewer" on every other command, so whichever meaning it took here would
be wrong half the time and wrong silently — a caller reaching for `.url`
would get a working URL of the wrong kind. Absent, they get nothing,
which is a failure they can see.

An unrecognised extension is refused before the upload rather than
guessed at. `publish` may fall through to `text/html` because that is
what it is for; the same shrug on a JPEG spends the upload and comes
back as a 415 naming a type nobody chose. `--filename` defaults to the
file's own basename and is omitted rather than refused when that name is
one the server would reject — the caller never typed it, and the
server's fallback is a working download where a 400 about an unpassed
parameter is not. An explicit `--filename` travels untouched.

Exit 6 and its two messages widen to cover an attachment. The status is
the same and the code stays one, because the caller's next move has the
same shape either way; what differs is the advice, and 413 now carries
both halves rather than telling someone with an oversized video to drop
their inline images.

0.4.0, in this commit: the server version-gates on the User-Agent, and a
capability its skill references should be a version an operator can
require.

Co-Authored-By: Claude Opus 5 <[email protected]>
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: f3a9aa71-b86f-4e09-9baf-101bfaaaa52d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/settings/billing.

Comment @coderabbitai help to get the list of available commands.

@ProJedi1234
ProJedi1234 merged commit 4a70240 into main Aug 25, 2026
2 checks passed
@ProJedi1234
ProJedi1234 deleted the feat/stele-attach branch August 25, 2026 21:26
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