History

feat: API contract, server handler, and frontend with effect-atom

- packages/core/api: HttpApi contract with ToolsApi + SecretsApi groups SDK tagged errors pass through directly with status annotations (ToolNotFoundError→404, PolicyDeniedError→403, etc.) - apps/server: API handler implementation via HttpApiBuilder Exports createApiHandler() for reuse (no baked-in server runtime) Handlers are pure pass-through — no manual error catching - apps/web: Vite + React frontend served on same port as API Vite plugin forwards /v1/*, /docs, /openapi.json to Effect handler effect-atom integration: AtomHttpApi.Tag typed client, query/mutation atoms RegistryProvider + useAtomValue with Result pattern matching - Catalog pins @effect/platform, platform-bun, platform-node, cluster - All 9 packages typecheck clean

Rhys Sullivan committed 7 months ago

Commit 8babea81 parent 00219a926 files · +1022 −32Browse files

Showing the changes to apps/web/src/api (3 of 26 files). Show all

Unified · Split

3 files
  1. apps/web/src/api/atoms.ts +40 −0
  2. apps/web/src/api/client.ts +15 −0
  3. apps/web/src/api/index.ts +10 −0
apps/web/src/api/atoms.ts+40−0
@@ -0,0 +1,40 @@
1+import { ScopeId, ToolId, SecretId } from "@executor/sdk";
2+import { ExecutorClient } from "./client";
3+
4+// ---------------------------------------------------------------------------
5+// Query atoms — typed, cached, reactive
6+// ---------------------------------------------------------------------------
7+
8+export const toolsAtom = (scopeId: ScopeId = ScopeId.make("default")) =>
9+ ExecutorClient.query("tools", "list", {
10+ path: { scopeId },
11+ timeToLive: "30 seconds",
12+ });
13+
14+export const toolSchemaAtom = (scopeId: ScopeId, toolId: ToolId) =>
15+ ExecutorClient.query("tools", "schema", {
16+ path: { scopeId, toolId },
17+ timeToLive: "1 minute",
18+ });
19+
20+export const secretsAtom = (scopeId: ScopeId = ScopeId.make("default")) =>
21+ ExecutorClient.query("secrets", "list", {
22+ path: { scopeId },
23+ timeToLive: "30 seconds",
24+ });
25+
26+export const secretStatusAtom = (scopeId: ScopeId, secretId: SecretId) =>
27+ ExecutorClient.query("secrets", "status", {
28+ path: { scopeId, secretId },
29+ timeToLive: "15 seconds",
30+ });
31+
32+// ---------------------------------------------------------------------------
33+// Mutation atoms — fire-and-forget style
34+// ---------------------------------------------------------------------------
35+
36+export const invokeTool = ExecutorClient.mutation("tools", "invoke");
37+
38+export const setSecret = ExecutorClient.mutation("secrets", "set");
39+
40+export const removeSecret = ExecutorClient.mutation("secrets", "remove");
apps/web/src/api/client.ts+15−0
@@ -0,0 +1,15 @@
1+import { AtomHttpApi } from "@effect-atom/atom-react";
2+import { FetchHttpClient } from "@effect/platform";
3+import { ExecutorApi } from "@executor/api";
4+
5+// ---------------------------------------------------------------------------
6+// Typed HTTP API client — uses effect-atom's AtomHttpApi.Tag
7+// ---------------------------------------------------------------------------
8+
9+class ExecutorClient extends AtomHttpApi.Tag<ExecutorClient>()("ExecutorClient", {
10+ api: ExecutorApi,
11+ httpClient: FetchHttpClient.layer,
12+ baseUrl: "/",
13+}) {}
14+
15+export { ExecutorClient };
apps/web/src/api/index.ts+10−0
@@ -0,0 +1,10 @@
1+export { ExecutorClient } from "./client";
2+export {
3+ toolsAtom,
4+ toolSchemaAtom,
5+ secretsAtom,
6+ secretStatusAtom,
7+ invokeTool,
8+ setSecret,
9+ removeSecret,
10+} from "./atoms";

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t