summaryrefslogtreecommitdiff
path: root/makima/frontend/src/lib/api.ts
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-03 23:19:40 +0000
committersoryu <soryu@soryu.co>2026-02-03 23:19:40 +0000
commitbfa3af9ef16fd5e255bdb606a99a5ebb535ba7cc (patch)
tree53da855b4ca61a5c0856fc15112daa7a3748c637 /makima/frontend/src/lib/api.ts
parent1ce281adb89683a5fccfd153706383b14b944f32 (diff)
parentdcbf8c834626870a43b633b099f409d69d4f9b87 (diff)
downloadsoryu-makima/discuss-contract-feature.tar.gz
soryu-makima/discuss-contract-feature.zip
fix: Resolve merge conflict in server/mod.rsmakima/discuss-contract-feature
Combine imports from both branches - include both chains and contract_discuss handlers. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'makima/frontend/src/lib/api.ts')
-rw-r--r--makima/frontend/src/lib/api.ts235
1 files changed, 235 insertions, 0 deletions
diff --git a/makima/frontend/src/lib/api.ts b/makima/frontend/src/lib/api.ts
index f507c7a..bdaedf9 100644
--- a/makima/frontend/src/lib/api.ts
+++ b/makima/frontend/src/lib/api.ts
@@ -3002,3 +3002,238 @@ export async function listTaskPatches(taskId: string, contractId: string): Promi
}
return res.json();
}
+
+// =============================================================================
+// Chain Types and API
+// =============================================================================
+
+/** Chain status */
+export type ChainStatus = "active" | "completed" | "archived";
+
+/** Chain summary for list view */
+export interface ChainSummary {
+ id: string;
+ name: string;
+ description: string | null;
+ status: ChainStatus;
+ contractCount: number;
+ completedContractCount: number;
+ loopEnabled: boolean;
+ loopCurrentIteration: number | null;
+ loopMaxIterations: number | null;
+ createdAt: string;
+ updatedAt: string;
+}
+
+/** Full chain with contracts */
+export interface Chain {
+ id: string;
+ ownerId: string;
+ name: string;
+ description: string | null;
+ status: ChainStatus;
+ loopEnabled: boolean;
+ loopMaxIterations: number | null;
+ loopCurrentIteration: number | null;
+ loopProgressCheck: string | null;
+ repositoryUrl: string | null;
+ localPath: string | null;
+ version: number;
+ createdAt: string;
+ updatedAt: string;
+}
+
+/** Contract detail within a chain */
+export interface ChainContractDetail {
+ id: string;
+ chainId: string;
+ contractId: string;
+ contractName: string;
+ contractStatus: string;
+ contractPhase: string;
+ dependsOn: string[];
+ orderIndex: number;
+ editorX: number | null;
+ editorY: number | null;
+ createdAt: string;
+}
+
+/** Chain with contracts (chain fields are flattened via serde(flatten)) */
+export interface ChainWithContracts extends Chain {
+ contracts: ChainContractDetail[];
+}
+
+/** Node in chain graph visualization */
+export interface ChainGraphNode {
+ id: string;
+ contractId: string;
+ name: string;
+ status: string;
+ phase: string;
+ x: number;
+ y: number;
+}
+
+/** Edge in chain graph */
+export interface ChainGraphEdge {
+ from: string;
+ to: string;
+}
+
+/** Chain graph response */
+export interface ChainGraphResponse {
+ chainId: string;
+ chainName: string;
+ chainStatus: string;
+ nodes: ChainGraphNode[];
+ edges: ChainGraphEdge[];
+}
+
+/** Chain event */
+export interface ChainEvent {
+ id: string;
+ chainId: string;
+ eventType: string;
+ contractId: string | null;
+ eventData: Record<string, unknown> | null;
+ createdAt: string;
+}
+
+/** Chain list response */
+export interface ChainListResponse {
+ chains: ChainSummary[];
+ total: number;
+}
+
+/** Create chain request */
+export interface CreateChainRequest {
+ name: string;
+ description?: string;
+ repositoryUrl?: string;
+ localPath?: string;
+ loopEnabled?: boolean;
+ loopMaxIterations?: number;
+ loopProgressCheck?: string;
+ contracts?: CreateChainContractRequest[];
+}
+
+/** Create chain contract request */
+export interface CreateChainContractRequest {
+ name: string;
+ description?: string;
+ contractType?: string;
+ initialPhase?: string;
+ phases?: string[];
+ dependsOn?: string[];
+ tasks?: { name: string; plan: string }[];
+ deliverables?: { id: string; name: string; priority?: string }[];
+ editorX?: number;
+ editorY?: number;
+}
+
+/** Update chain request */
+export interface UpdateChainRequest {
+ name?: string;
+ description?: string;
+ status?: ChainStatus;
+ loopEnabled?: boolean;
+ loopMaxIterations?: number;
+ loopProgressCheck?: string;
+ version?: number;
+}
+
+/** List chains */
+export async function listChains(
+ status?: ChainStatus,
+ limit = 50,
+ offset = 0
+): Promise<ChainListResponse> {
+ const params = new URLSearchParams();
+ if (status) params.set("status", status);
+ params.set("limit", String(limit));
+ params.set("offset", String(offset));
+
+ const res = await authFetch(`${API_BASE}/api/v1/chains?${params}`);
+ if (!res.ok) {
+ throw new Error(`Failed to list chains: ${res.statusText}`);
+ }
+ return res.json();
+}
+
+/** Get chain by ID */
+export async function getChain(chainId: string): Promise<ChainWithContracts> {
+ const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}`);
+ if (!res.ok) {
+ throw new Error(`Failed to get chain: ${res.statusText}`);
+ }
+ return res.json();
+}
+
+/** Create a new chain */
+export async function createChain(req: CreateChainRequest): Promise<Chain> {
+ const res = await authFetch(`${API_BASE}/api/v1/chains`, {
+ method: "POST",
+ body: JSON.stringify(req),
+ });
+ if (!res.ok) {
+ const errorText = await res.text();
+ throw new Error(`Failed to create chain: ${errorText || res.statusText}`);
+ }
+ return res.json();
+}
+
+/** Update a chain */
+export async function updateChain(
+ chainId: string,
+ req: UpdateChainRequest
+): Promise<Chain> {
+ const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}`, {
+ method: "PUT",
+ body: JSON.stringify(req),
+ });
+ if (!res.ok) {
+ const errorText = await res.text();
+ throw new Error(`Failed to update chain: ${errorText || res.statusText}`);
+ }
+ return res.json();
+}
+
+/** Archive a chain */
+export async function archiveChain(chainId: string): Promise<{ archived: boolean }> {
+ const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}`, {
+ method: "DELETE",
+ });
+ if (!res.ok) {
+ throw new Error(`Failed to archive chain: ${res.statusText}`);
+ }
+ return res.json();
+}
+
+/** Get chain contracts */
+export async function getChainContracts(
+ chainId: string
+): Promise<ChainContractDetail[]> {
+ const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}/contracts`);
+ if (!res.ok) {
+ throw new Error(`Failed to get chain contracts: ${res.statusText}`);
+ }
+ return res.json();
+}
+
+/** Get chain graph for visualization */
+export async function getChainGraph(chainId: string): Promise<ChainGraphResponse> {
+ const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}/graph`);
+ if (!res.ok) {
+ throw new Error(`Failed to get chain graph: ${res.statusText}`);
+ }
+ return res.json();
+}
+
+/** Get chain events */
+export async function getChainEvents(chainId: string): Promise<ChainEvent[]> {
+ const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}/events`);
+ if (!res.ok) {
+ throw new Error(`Failed to get chain events: ${res.statusText}`);
+ }
+ return res.json();
+}