Repository navigation
feat(api): search, grep and call any public Base44 API from the spec - #663
amitbardos wants to merge 1 commit into
Conversation
🚀 Package Preview Available!Install this PR's preview build with npm: npm i @base44-preview/cli@0.1.32-pr.663.43d14f8Prefer not to change any import paths? Install using npm alias so your code still imports npm i "base44@npm:@base44-preview/cli@0.1.32-pr.663.43d14f8"Or add it to your {
"dependencies": {
"base44": "npm:@base44-preview/cli@0.1.32-pr.663.43d14f8"
}
}
Preview published to npm registry — try new features instantly! |
|
Commands, cache behaviour and timings. The description is regenerated by CommandsEvery listing identifies an operation as
Cache
TimingsMeasured on the production spec (2.31 MB, 491 operations, about 390 KB compressed on the wire), Node 22, cold process, median of 9 runs:
End to end, Dependency
Known issue (not from this PR)On Node 20.11, which is below the package's Test status
HistoryThe branch is squashed into one commit for review. The step-by-step history (20 commits, review rounds included) is kept on |
f4ad008 to
660f759
Compare
|
Nice work. Blocking
Worth fixing
Nits
|
660f759 to
82055e6
Compare
base44 api catalog | search | grep | fetch, driven at runtime by the
server's OpenAPI spec (GET /api/openapi.json), cached per server under
~/.base44/cache/<server>/ for an hour (--refresh forces a download).
- Listings print {endpoint, summary}, as the MCP bridge does; --read-only
filters to operations a read-only token may call.
- catalog lists areas (the path segment after {app_id}, else after /api),
or one area's operations, capped at 200.
- search (MiniSearch) ranks summaries holding every query word first.
- grep matches each operation's contract with schemas inlined.
- fetch sends the path as is through base44Client, with no timeout and no
local validation; --help after a path prints the operation's contract;
--read-only refuses unsafe operations before sending; the body comes from
--body JSON, @file or @-, or --form for multipart (file uploads);
--output saves the response. A rate-limited call shows Retry-After.
- The path is normalized once (dot segments resolved as the request URL
resolves them), and that path is both checked by --read-only and sent.
- JSON is passed through byte for byte when piped (no int64 rounding) and
pretty-printed only in a terminal; bytes that aren't UTF-8 with no
content type are described as binary.
- Component $refs to parameters, request bodies and responses resolve.
- A malformed route in the spec is skipped, not fatal.
- A failed call reports no request or response body, --body, --form or
query string to telemetry.
The step-by-step history is on amitb/api-command-history.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
82055e6 to
2893932
Compare
|
@ronnyrin thanks, all addressed in 2893932:
|
| const operations = listOperations(spec); | ||
| const readOnly = (ops: typeof operations) => | ||
| options.readOnly ? ops.filter((op) => op.readOnly) : ops; | ||
| if (area === undefined) { |
There was a problem hiding this comment.
Ouch, many undefined checks around the code
| const readOnly = (ops: typeof operations) => | ||
| options.readOnly ? ops.filter((op) => op.readOnly) : ops; | ||
| if (area === undefined) { | ||
| return { stdout: formatJson(catalogIndex(readOnly(operations))) }; |
There was a problem hiding this comment.
Not a fan of the naming but not critical
|
|
||
| function assertValidRegex(pattern: string): void { | ||
| try { | ||
| new RegExp(pattern); |
| /** Stdin is a shell pipe or a redirected file; checking reads nothing from it. */ | ||
| function isPipedStdin(): boolean { | ||
| try { | ||
| const stats = fstatSync(0); |
There was a problem hiding this comment.
Feels like extreme to me. Not sure we need to know who called us and how
| }); | ||
| return { | ||
| status: response.status, | ||
| contentType: response.headers.get("content-type") ?? "", |
There was a problem hiding this comment.
Don't we need all headers, like request id?
| // A cache that can't be written (full or read-only disk) only costs speed. | ||
| const partial = `${path}.${process.pid}.tmp`; | ||
| try { | ||
| await writeFile(partial, text); |
There was a problem hiding this comment.
Do we write logs whenever we download it (or fail to save)? It would be interesting to know if we need to support partial download of specific area
| path: string, | ||
| { refresh, flag }: { refresh?: boolean; flag: "--help" | "--read-only" }, | ||
| ) { | ||
| const { spec } = await loadSpecTask(ctx, refresh); |
There was a problem hiding this comment.
I must say that the term spec is a little confusing to me. Is it the standard? In wix it's called schema afaik
| assertValidRegex(pattern); | ||
| const { spec } = await loadSpecTask(ctx, options.refresh); | ||
| let operations = listOperations(spec); | ||
| if (options.area !== undefined) { |
There was a problem hiding this comment.
Is "area" the right term?
Note
Description
Adds a
base44 apicommand group —catalog,search,grepandfetch— that lets a human or an agent discover and call any public Base44 API operation without per-endpoint CLI code. Everything is driven at runtime by the server's OpenAPI document (GET /api/openapi.json), cached per server under~/.base44/cache/<server>/openapi.jsonfor one hour (--refreshforces a download). The spec logic lives insrc/core/api-spec/; the commands insrc/cli/commands/api/are thin wrappers over it, matching the existing core/cli layering.Related Issue
None
Type of Change
Changes Made
src/core/api-spec/(new): Zod-validated OpenAPI parsing (schema.ts,operations.ts), per-server spec cache with write-then-rename (spec.ts), area catalog (catalog.ts), MiniSearch-backed search (search.ts), regex grep over contracts (grep.ts),$ref-inlining contract builder (contract.ts) and the authenticated request sender (request.ts).base44 api catalog [area]: lists the API's areas (the path segment after{app_id}, else after/api) or one area's operations, capped at 200.base44 api search <text>: MiniSearch ranking with per-field boosts, stopword filtering and summary coverage/precision, so operations whose summary holds every query word rank first.base44 api grep <pattern>: regular-expression match against each operation's contract with component schemas inlined, so a field name such aswebhook_secretfinds the operations that send or return it.base44 api fetch <METHOD> <path>: sends the path as is throughbase44Clientwith no timeout and no local validation (the server validates);--helpafter a path prints the operation's contract;--read-onlyrefuses unsafe operations before anything is sent; body from--body <json>,--body @file,--body @-, or--form name=value/name=@filefor multipart uploads;--output <file>saves the response. Retries are limited to GET/HEAD, and a 429 surfacesRetry-After.--read-onlyand sent.fetchreports to telemetry without the request or response body,--body,--formfields or the query string — any endpoint can carry secrets. The server's raw reply still reaches the caller in the error details.securitylist (PersonalAccessTokenAuth: ["read_only"], the backend's@read_safe); the server stays the real guard.$refs to parameters, request bodies and responses resolve.toJsonStdout()gained acompactoption,getBase44GlobalDir()is now exported,writeFile()acceptsUint8Array,minisearch@7.1.2was added todevDependencies(bundled, per the zero-dependency distribution rule), and the group is registered inprogram.ts.docs/commands.mdand four rows in the CLI README command table.Testing
npm test)Checklist
docs/(AGENTS.md) if I made architectural changesAdditional Notes
About 1,400 lines of new tests:
tests/core/api-spec.spec.ts(operation listing, read-only detection, search ranking, path-template matching, contract inlining, grep, catalog grouping, cache paths and TTL),tests/cli/api.spec.ts(end-to-end command behavior) andtests/cli/api_redaction.spec.ts(nothing secret-bearing reaches telemetry), plus anopenapi.jsonfixture. The suite,typecheckandlintwere not run in this description-generation environment, so the two unchecked testing boxes and the warnings box are unverified here rather than known failures — CI covers them.fetchdeliberately disables Commander's help option so--helpafter a method and path can print the operation's contract, and it reads stdin only for--body @-(an agent harness can hold stdin open forever, so reading it unasked would hang the call). The commit notes that the step-by-step history lives onamitb/api-command-history.🤖 Generated by Claude | 2026-10-17 09:41 UTC | 2893932