Repository navigation
Conversation
The list commands dumped the full JSON body of every record and accepted no options beyond the project id, so answering "how many observations do we have" meant piping 135 KB through jq by hand. Add a client-side query layer (filter -> sort -> paginate -> group -> render) and apply it to observations, projects and alerts: - --count prints a bare number; --group-by splits it by category, preset, day, device, media or any tag field - --summary prints totals, date range, devices and media counts - filters: --category, --tag key=value (OR with |), --preset, --since, --until, --bbox, --has, --missing, --search, --sort, --reverse, --limit/--offset, --columns - --format table|json|ndjson|csv, defaulting to table on a TTY and json when piped so existing `| jq` pipelines are unaffected - -p accepts a project name as well as a 52-character id, and reports ambiguous names instead of guessing Fixes found along the way: - export-geojson dropped every tag: the properties interface declared categoryId/name/notes but the object never set them, so exports lost all categories and preset fields - export-geojson wrote to a fixed tmp_export/ in the cwd and leaked it on failure; now mkdtemp + finally - export-geojson accepted -o out.geojson but wrote a ZIP; the extension now decides (zip bundles media, geojson is metadata only) - audio attachments were written into images/ with a .jpg extension; they now go to audio/ as .mp3 - --version was hardcoded 1.0.0 while package.json said 1.0.3 - get-attachment passed an arbitrary --variant straight through - healthcheck printed an empty JSON body; it now prints "ok" Deleted records are excluded by default. The API returns tombstones mixed in with live records (includeDeleted: true server-side), which made counts misleading: 72 returned records were really 56 observations. Use --include-deleted / --only-deleted to see them. Error handling maps 401/403, 404, timeouts and refused connections to actionable messages, sets exit code 2 for usage errors and 3 for not found, and no longer calls process.exit() mid-write (which truncated piped output). Split src/index.ts (462 lines, every command inline) into commands/ and lib/, with filters.ts pure and unit-tested. Tests cover the predicates, grouping maths and renderers against a synthetic fixture; no real field data is committed. 77 tests, run under bun (chalk 5 is ESM-only, so `npm test` now points at `bun test`).
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.
Why
The list commands printed the full JSON body of every record and took no options beyond the project id. Answering "how many observations do we have?" meant piping ~135 KB through
jqby hand, and the answer was wrong anyway: the API returns tombstones mixed in with live records, so a project that reported 72 records actually had 56 observations.What
A client-side query layer — filter → sort → paginate → group → render — applied to observations, projects and alerts.
--countprints a bare number (composes in scripts);--group-bysplits it bycategory,preset,day,device,mediaortag:<field>.--summaryreports totals, date range, device and media counts.--category,--tag key=value(v1|v2for OR, arrays matched),--preset,--since/--until,--bbox,--has,--missing,--search,--sort,--reverse,--limit/--offset,--columns.--format table|json|ndjson|csv, defaulting totableon a TTY andjsonwhen piped — so existing| jqpipelines keep working with no flags.-paccepts a project name as well as a 52-character id; ambiguous names list the candidates and exit2.All filtering is client-side because
/projects/:id/observationsaccepts no query parameters (verified against a live server and againstaddDatatypeGetterindigidem/comapeo-cloud). The payload is small enough that this is the honest implementation.Fixes
export-geojsondropped every tag. Thepropertiesinterface declaredcategoryId/name/notes, but the emitted object never set them, so every export lost its categories and preset field data.export-geojsonwrote to a fixedtmp_export/in the cwd and leaked it on failure →mkdtemp+finally.-o out.geojsonwas accepted but produced a ZIP → the extension now decides:.zipbundles media,.geojsonis metadata-only.images/with a.jpgextension → nowaudio/with.mp3.--versionprinted a hardcoded1.0.0whilepackage.jsonsaid1.0.3→ read frompackage.json.get-attachment --variant <anything>was passed straight through → validated.healthcheckprinted an empty JSON body → printsok.console.log("processing", att)noise removed; downloads are capped at 6 concurrent instead of a 112-widePromise.all.Breaking changes
--include-deletedrestores the old view;--only-deletedshows tombstones only.healthcheckprintsokinstead of an empty body.Version bumped to
1.1.0.Structure
src/index.tswas 462 lines with every command inline. Split intosrc/commands/*andsrc/lib/*, withfilters.tsandgroup.tspure and unit-testable — no network needed to test the query layer.npm testnow points atbun test, which is what CI already ran:chalk5 is ESM-only and does not load in jest's CJS runtime, so the new unit tests could not run under jest. 77 tests pass under bun.Testing
src/__tests__/fixtures/observations.ts). No real field observations are committed — the projects this CLI targets hold sensitive survey data.--count(56),--count --include-deleted(72), per-category counts,--has audio(21), bbox and date-range filters,--summary(112 photos / 21 audio / 3 devices), plain and zipped exports (tags present inproperties, audio inaudio/),get-attachment --variant thumbnailproducing a valid JPEG,list-alerts,list-projects --count, and JSON/ndjson/csv/table formats.Not included
Presets can only be grouped by docId prefix (
preset:abb1b841): the deployed servers return404for/presets, so there is no preset name to resolve. Tracks and fields are 404 on those servers too, so no commands were added for them.