Skip to content

feat(camera): close the gap between @rawback/ccapi-js and rawback camera - #87

Merged
AnnatarHe merged 7 commits into
mainfrom
feat/camera-ccapi-parity
Sep 27, 2026
Merged

AnnatarHe merged 7 commits into
mainfrom
feat/camera-ccapi-parity

Conversation

@AnnatarHe

@AnnatarHe AnnatarHe commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The camera api catalogue already reached 231 of the library's 269 client methods; the gaps were an outdated pin, a few CLI bugs, binary endpoints the JSON catalogue cannot carry, and everyday controls with no command of their own. One commit per phase:

  • chore(deps): bump @rawback/ccapi-js to 1.1.3. 1.1.2 read recordable from the wrong keys (so camera status always reported null), sent GPS and object-valued settings in shapes cameras reject, and kept non-event units in monitoring. contents list --order now requires --all, because the camera rejects order on a single page.
  • fix(camera). settings list printed camelCase names that settings get/set then sent as paths the camera does not have; it now reads the raw aggregate, so every row round-trips. Picture-style parameters are checked by name instead of silently dropped. The catalogue's event polling uses hold, which works on every version.
  • feat(camera): catalogue parity. Adds the remaining non-deprecated JSON endpoints: picture-style base, sound-recording settings, crop depth composite, focus-bracketing flash interval, power-zoom setting, typed still-image quality and WB shift, XMP description, and the network setters. The drift guard now fails when synthetic arguments never reach the client.
  • fix(ui): any --json document over 64 KiB was cut off when piped. Once Ink loads, Bun's console.log drops what a pipe buffer does not take. CommandOutput now writes through the process streams; a test reproduces the bug through a real sh … | cat pipe.
  • feat(camera): binary endpoints.
    • camera cert saves the root certificate and prints the command that shows its fingerprint.
    • camera rtp start|stop|status|sdp streams live view over RTP.
    • liveview frame --detail reads the frame's AF frames, level, zoom and histogram.
    • liveview stream falls back to the scroll stream on bodies with no multipart stream.
  • feat(camera): controls.
    • New commands: record, focus, zoom, clock [sync], and owner [set|clear].
    • status gains batteries, lens and powerZoom as additive JSON keys.
  • feat(camera): content edits.
    • contents protect|archive|rate|rotate|xmp|geotag|rmdir.
    • card format, which must be typed back unless --force is given.

Every command that changes the camera confirms first, and needs --force together with --json.

Needs attention before merging

  • CI will fail at bun install --frozen-lockfile until @rawback/sdk 0.3.4 is published. This is already the case on main, whose package.json pins 0.3.4. Merge rawback-app/sdk#46, then run bun install on this branch and commit the lockfile.
  • To check on hardware:
    • clock sync sends the host's local time with its full UTC offset and the camera's daylight-saving flag off, then reads the clock back. This choice avoids adding an hour twice; please confirm it on a body while daylight saving is in effect.
    • rtp, liveview frame --detail and the scroll fallback were only exercised against the fake camera.

Test plan

  • bun test test/camera*: 267 pass
  • bun run lint, bun run format:check
  • The full suite fails the same way as main: photos/spots need SDK 0.3.4, and bootstrapConfig fails when a long temp path wraps
  • bun run check once SDK 0.3.4 is installed
  • On a real body:
    • camera status --json: recordable is not null
    • camera settings list, then pass a listed name to settings get
    • camera clock sync
    • camera liveview frame f.jpg --detail
    • camera rtp sdp s.sdp
    • camera contents rate <locator> 3

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

AnnatarHe and others added 7 commits September 27, 2026 12:41
1.1.3 fixes several wire-format bugs the CLI was hitting on real bodies:
`camera status` reported recordable as null (1.1.2 read the wrong keys),
monitoring kept non-event units, and GPS was sent in a shape the camera
rejects.

- The catalogue swaps the deprecated liveview.getRTP (always empty) for
  getRTPStatus, and requestAngleInformation takes start|stop.
- contents.setContentGPS accepts the { degree, minute, second } and
  { hour, minute, second } rationals 1.1.3 sends, as well as flat arrays,
  and its placeholder shows the real shape.
- contents list rejects --order without --all: the camera refuses order
  on a single page and 1.1.3 now drops it silently.
- Fixtures send recordable as recordableshots/remainingtime and
  monitoring events as 0x02 units, as cameras do.

The lockfile still resolves @rawback/sdk 0.3.3 because 0.3.4 is not
published yet; that mismatch predates this change.

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

- `settings list` read the decoded aggregate, whose camelCase keys
  (`shootingMode`) became paths the camera does not have when passed to
  `settings get`/`set`, and which dropped every setting it does not model.
  It now reads the raw aggregate, so every row round-trips; range
  abilities report `range`, and structured values print as JSON.
- `shooting.setPictureStyleDetail` told users to send snake_case keys,
  which the library silently drops. Keys are now checked against the
  library's parameters, and the camera's spellings point at theirs.
- `event.getPolling` in the catalogue takes `hold` instead of `continue`,
  which ver110+ bodies reject; `timeout` is limited to short|long.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`camera api` and the explorer now reach every non-deprecated JSON
endpoint in @rawback/ccapi-js 1.1.3:

- shooting: getShootingSettingsRaw, picture-style base, sound-recording
  mode/wind filter/attenuator, crop depth composite, the focus-bracketing
  flash interval, and the power-zoom adapter setting.
- stillimagequality and wbshift are object-valued, and the camera rejects
  the string PUT the catalogue used to send; they now take a JSON object
  and read through the typed getters.
- contents.setContentXMPDescription.
- network: the connection, communication, function, Wi-Fi and connect
  setting setters, loading the deep network tables first as the getters do.

JSON members are checked before the camera sees them, since the library
drops unknown keys. The drift guard now counts an accessor rejecting its
synthetic arguments as a failure, so every entry provably reaches the
client.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Once Ink has loaded, Bun's console.log makes a single write to a pipe
and drops whatever the pipe buffer did not take, so any `--json` document
over 64 KiB was cut off when piped (`rawback camera api --list --json |
jq` read truncated JSON). CommandOutput now writes to process.stdout and
process.stderr, whose writes are queued and drained before exit.

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

The binary CCAPI endpoints the JSON catalogue cannot carry now have
commands:

- `camera cert <output>` saves the root TLS certificate and prints the
  openssl command that shows its fingerprint, so it can be checked against
  the camera before reaching for --insecure.
- `camera rtp start|stop|status|sdp` streams live view over RTP and saves
  the session description a player needs.
- `camera liveview frame --detail` reads flipdetail and reports the AF
  frames, level, zoom and histogram alongside the saved frame.
- `camera liveview stream` falls back to the chunked scroll stream on a
  body that serves no multipart stream.

Supporting changes: CameraSession.requireSupport refuses an unadvertised
endpoint by name before any request (and `camera api` now uses it), and
the overwrite guard moved to camera-files.ts so every file-writing
command shares it. The fake camera can answer with a raw Response for
binary bodies.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The controls a photographer reaches for most were only reachable as
`camera api <id> --arg k=v`; they now have commands:

- `camera record start|stop|status`, refusing to start outside movie mode
  unless --movie-mode switches it on first.
- `camera focus af|stop|near|far [--steps 1-3]`.
- `camera zoom [position|wide|tele|stop]`, reading every zoom control the
  body has when given no value.
- `camera clock [sync]`, which sets the host's local time and full UTC
  offset with the camera's DST flag off, then reads the clock back.
- `camera owner [set|clear]` for the copyright, author, owner name and
  nickname written into every file; set checks every field is supported
  before writing any.

`camera status` also reports every battery including a grip's, the
mounted lens, and a Power Zoom Adapter's state, as additive JSON keys.

Every command that changes the camera confirms first unless --force.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- `camera contents protect|archive|rate|rotate|xmp|geotag` change one file
  on the card. `geotag` builds the full EXIF GPS block the camera
  requires from decimal degrees (DMS rationals rounded in hundredths of
  an arcsecond, so 59.999" carries into the minute), with the UTC time
  and date of --time. `xmp` inserts raw attributes into the file's XMP
  rdf:Description tag, which is what the camera's xmp_description action
  does — it is not a free-text caption.
- `camera contents rmdir <storage> <directory>` deletes a directory
  through its ver140 folder.
- `camera card format <storage>` checks the storage against the camera's
  own list, and without --force has the name typed back before erasing.

Every edit confirms first unless --force, and values are validated
before connecting. Also adds the missing tests for `contents dirs` and
`contents info`, and brings the agent guide's command and test counts up
to date.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-27T05:36:28.014390Z f97a6fb PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f97a6fb0dd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/camera-rtp.ts

await withCameraSession(options, dependencies, async (session) => {
session.requireSupport(RTP, 'rawback camera rtp start')
await session.client.liveview.setRTP('start', options.ip)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop RTP in generic live-view cleanup

After camera rtp start, the documented recovery command camera liveview stop still releases only the multipart and scroll streams in runCameraLiveviewStop. Consequently, it can report success while the camera continues transmitting RTP and remains occupied; either include setRTP('stop') when the endpoint is advertised or narrow the documented cleanup guarantee.

AGENTS.md reference: AGENTS.md:L126-L127

Useful? React with 👍 / 👎.

Comment thread src/camera-settings.ts
function rangeOf(ability: unknown): SettingRow['range'] {
if (typeof ability !== 'object' || ability === null || Array.isArray(ability)) return null
const { min, max, step } = ability as Record<string, unknown>
if (typeof min !== 'number' && typeof max !== 'number') return null

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve all-null locked ranges in settings list

When the aggregate settings response contains the documented locked shape ability: { min: null, max: null, step: null }, this condition discards it because neither bound is numeric. camera settings list therefore emits range: null and renders a dash instead of identifying the setting as locked, despite the presenter already supporting an all-null range; recognize objects containing the range keys even when every value is null.

Useful? React with 👍 / 👎.

@AnnatarHe
AnnatarHe merged commit 4604fbc into main Sep 27, 2026
5 of 11 checks passed
@AnnatarHe
AnnatarHe deleted the feat/camera-ccapi-parity branch September 27, 2026 05:48
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