File size: 4,559 Bytes
66d0c75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// Optional server bridge for the Blueprint Details workspace.
//
// The workspace is driven entirely by the in-browser engine + store (C0/C1/C2) — these fetch
// wrappers are the OPTIONAL enhancement path: when a control plane is configured they sync /
// fetch real designer output, and on any failure they fall back to the local engine. Nothing on
// the render path depends on them.

import { apiBaseUrl } from "./api-client";
import { authHeaders } from "./auth-token";
import * as engine from "@/lib/blueprint-engine";

export type {
  ArchitectureNode,
  BlueprintDetailsData,
  DesignerBatch,
  DesignerCandidate,
  DesignerChatMessage,
  FilePlanItem,
} from "@/types/blueprint-state";

import type { BlueprintDetailsData, DesignerCandidate } from "@/types/blueprint-state";

// Local engine derivations re-exported for back-compat with existing callers.
export const deriveLocalDetails = engine.generate;
export const deriveLocalCandidates = engine.generateCandidates;

/** The 3 blueprint cards. `useDesigner=false` (toggle off) / offline → local engine. */
export async function fetchDesignerCandidates(
  idea: string,
  opts: { useDesigner?: boolean } = {},
): Promise<DesignerCandidate[]> {
  if (opts.useDesigner === false) return engine.generateCandidates(idea);
  try {
    const url = `${apiBaseUrl}/api/v1/blueprints/designer-candidates?idea=${encodeURIComponent(idea)}`;
    const res = await fetch(url, { headers: { ...authHeaders() } });
    if (!res.ok) throw new Error(`designer-candidates ${res.status}`);
    const body = (await res.json()) as { candidates?: DesignerCandidate[] };
    return body.candidates && body.candidates.length ? body.candidates : engine.generateCandidates(idea);
  } catch {
    return engine.generateCandidates(idea);
  }
}

/** Optional: fetch real designer Details. Offline / toggle off → local engine. */
export async function fetchBlueprintDetails(
  candidateId: string,
  idea: string,
  opts: { useDesigner?: boolean } = {},
): Promise<BlueprintDetailsData> {
  if (opts.useDesigner === false) return engine.generate(candidateId, idea);
  try {
    const url = `${apiBaseUrl}/api/v1/blueprints/${candidateId}/details?idea=${encodeURIComponent(idea)}`;
    const res = await fetch(url, { headers: { ...authHeaders() } });
    if (!res.ok) throw new Error(`details ${res.status}`);
    return (await res.json()) as BlueprintDetailsData;
  } catch {
    return engine.generate(candidateId, idea);
  }
}

/** Optional server refinement; the workspace already applied the local engine result. */
export async function sendBlueprintChat(
  candidateId: string,
  idea: string,
  message: string,
): Promise<{ reply: string; details: BlueprintDetailsData }> {
  try {
    const url = `${apiBaseUrl}/api/v1/blueprints/${candidateId}/chat`;
    const res = await fetch(url, {
      method: "POST",
      headers: { "Content-Type": "application/json", ...authHeaders() },
      body: JSON.stringify({ idea, message }),
    });
    if (!res.ok) throw new Error(`chat ${res.status}`);
    return (await res.json()) as { reply: string; details: BlueprintDetailsData };
  } catch {
    const res = engine.apply(engine.generate(candidateId, idea), message);
    return { reply: res.reply, details: res.data };
  }
}

export async function saveBlueprintDetails(
  candidateId: string,
  idea: string,
  details: BlueprintDetailsData,
  buildId?: string,
): Promise<BlueprintDetailsData> {
  try {
    const url = `${apiBaseUrl}/api/v1/blueprints/${candidateId}/save`;
    const res = await fetch(url, {
      method: "POST",
      headers: { "Content-Type": "application/json", ...authHeaders() },
      body: JSON.stringify({ idea, build_id: buildId, details }),
    });
    if (!res.ok) throw new Error(`save ${res.status}`);
    return (await res.json()) as BlueprintDetailsData;
  } catch {
    return details; // offline: kept locally (localStorage persistence is C4)
  }
}

/** Restore a previously-saved blueprint + chat for a build (owner-scoped). */
export async function fetchSavedBlueprint(
  candidateId: string,
  buildId: string,
): Promise<BlueprintDetailsData | null> {
  try {
    const url = `${apiBaseUrl}/api/v1/blueprints/${candidateId}/saved?build_id=${encodeURIComponent(buildId)}`;
    const res = await fetch(url, { headers: { ...authHeaders() } });
    if (!res.ok) throw new Error(`saved ${res.status}`);
    const body = (await res.json()) as { found: boolean; details: BlueprintDetailsData | null };
    return body.found ? body.details : null;
  } catch {
    return null;
  }
}