mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-10-06 14:33:11 +08:00
docs: add API access rules to AGENTS.md and code review skill (#506)
Document the two mandatory API access conventions that are enforced by the CI test src/api/no-direct-agent-server-calls.test.ts: 1. All agent-server calls must use typed @openhands/typescript-client classes (ConversationClient, FileClient, VSCodeClient, ServerClient, RemoteWorkspace, RemoteEventsList) instantiated via getAgentServerClientOptions() -- never raw axios/fetch. 2. All cloud SaaS and runtime-sandbox calls must go through callCloudProxy() in src/api/cloud/proxy.ts to avoid CORS, using hostOverride for runtime-sandbox URLs and authMode='session-api-key' for those endpoints. AGENTS.md gets a full '## API Access Rules' section with client listings, option helper references, CORRECT/WRONG code examples, and the allowed-exceptions list. The custom-codereview-guide.md skill gets a '## Frontend API Access Conventions' section with DO NOT APPROVE triggers, forbidden pattern lists, correct examples, and a note about the silent hostOverride bug. Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
committed by
GitHub
co-authored by
openhands
parent
acb04fd9ba
commit
43e6db6919
@@ -168,6 +168,78 @@ pydantic_core.ValidationError: Extra inputs are not permitted
|
||||
|
||||
**This is a production-breaking change.** Do not approve PRs that modify event types without proper backward compatibility handling and tests.
|
||||
|
||||
## Frontend API Access Conventions
|
||||
|
||||
These two rules are enforced by the CI test `src/api/no-direct-agent-server-calls.test.ts`.
|
||||
**Flag any PR that introduces a violation** -- these are correctness bugs, not style nits.
|
||||
|
||||
### Rule 1 -- All agent-server calls must use `@openhands/typescript-client`
|
||||
|
||||
**DO NOT APPROVE** a PR that introduces raw `axios`, `fetch`, or the shared `openHands`
|
||||
axios instance to call an agent-server endpoint (`/api/*`, `/server_info`). All such
|
||||
calls must go through typed client classes from `@openhands/typescript-client`,
|
||||
instantiated with options from `getAgentServerClientOptions()` or
|
||||
`getAgentServerHttpClientOptions()` in `src/api/agent-server-client-options.ts`.
|
||||
|
||||
Forbidden patterns (caught by the CI guard):
|
||||
- `openHands.<method>(...)` -- shared axios instance
|
||||
- `createHttpClient(...)` -- creates a raw HTTP client
|
||||
- `axios(...)` / `axios.get/post/etc.(...)` (except in the two allowed files)
|
||||
- `fetch('/api/...')` or `fetch(\`${host}/api/...\`)`
|
||||
|
||||
Correct pattern:
|
||||
```ts
|
||||
new ConversationClient(getAgentServerClientOptions()).getConversation(id)
|
||||
new FileClient(getAgentServerClientOptions()).downloadTextFile(path)
|
||||
new ServerClient(getAgentServerHttpClientOptions()).getServerInfo()
|
||||
new RemoteWorkspace(getAgentServerClientOptions()).gitChanges({ ref: "HEAD" })
|
||||
```
|
||||
|
||||
Allowed exceptions (explicitly listed in `ALLOWED_AD_HOC_HTTP_FILES`):
|
||||
- `src/api/automation-service/automation-service.api.ts`
|
||||
- `src/api/cloud/proxy.ts`
|
||||
|
||||
If a PR adds a new file to `ALLOWED_AD_HOC_HTTP_FILES` without a strong reason,
|
||||
flag it -- the allowlist should not grow casually.
|
||||
|
||||
### Rule 2 -- All cloud backend calls must go through `callCloudProxy`
|
||||
|
||||
**DO NOT APPROVE** a PR that issues a direct browser `fetch` or `axios` call to the
|
||||
cloud SaaS backend (`app.all-hands.dev`) or a cloud runtime sandbox
|
||||
(`*.prod-runtime.all-hands.dev`). Both origins block CORS from `localhost`. Cloud calls
|
||||
must go through `callCloudProxy()` in `src/api/cloud/proxy.ts`, which routes them
|
||||
server-side through `/api/cloud-proxy` on the local agent-server.
|
||||
|
||||
Correct pattern -- cloud SaaS:
|
||||
```ts
|
||||
callCloudProxy({ backend, method: "GET", path: "/api/v1/app-conversations/search?..." })
|
||||
```
|
||||
|
||||
Correct pattern -- cloud runtime sandbox (use `hostOverride` + `authMode: "session-api-key"`):
|
||||
```ts
|
||||
callCloudProxy({
|
||||
backend,
|
||||
method: "GET",
|
||||
hostOverride: buildHttpBaseUrl(conversationUrl),
|
||||
path: `/api/conversations/${id}`,
|
||||
authMode: "session-api-key",
|
||||
sessionApiKey,
|
||||
})
|
||||
```
|
||||
|
||||
Standard branch structure every cloud-aware service method should follow:
|
||||
```ts
|
||||
if (getActiveBackend().backend.kind === "cloud") {
|
||||
return callCloudProxy({ backend: active, ... });
|
||||
}
|
||||
// local path: typed typescript-client
|
||||
return new ConversationClient(getAgentServerClientOptions()).someMethod(...);
|
||||
```
|
||||
|
||||
Missing the `hostOverride` on a runtime-sandbox call is a silent bug: the proxy
|
||||
will target `backend.host` (the SaaS API) instead of the actual runtime URL.
|
||||
Flag any `callCloudProxy` call that targets a runtime URL without `hostOverride`.
|
||||
|
||||
## SDK Architecture Conventions
|
||||
|
||||
These conventions codify patterns that are easy to violate when adding new features. Each was learned from a real bug.
|
||||
|
||||
Reference in New Issue
Block a user