apteva 0.4.57 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +216 -54
  2. package/cli.js +35 -0
  3. package/install.js +92 -0
  4. package/package.json +12 -79
  5. package/LICENSE +0 -63
  6. package/bin/apteva.js +0 -196
  7. package/dist/ActivityPage.kxzzb4yc.js +0 -3
  8. package/dist/ApiDocsPage.zq998hbm.js +0 -4
  9. package/dist/App.55rea8mn.js +0 -61
  10. package/dist/App.5ywb23z4.js +0 -53
  11. package/dist/App.6thds120.js +0 -4
  12. package/dist/App.9tctxzqm.js +0 -8
  13. package/dist/App.a8r8ttaz.js +0 -4
  14. package/dist/App.agsv5bje.js +0 -4
  15. package/dist/App.cepapqmx.js +0 -4
  16. package/dist/App.dp041gb3.js +0 -221
  17. package/dist/App.fds72zb5.js +0 -4
  18. package/dist/App.fg9qj2dq.js +0 -4
  19. package/dist/App.ndfejbm9.js +0 -4
  20. package/dist/App.nxmfmq1h.js +0 -13
  21. package/dist/App.qdfyt8ba.js +0 -4
  22. package/dist/App.x2d0ygt6.js +0 -4
  23. package/dist/App.yt9p4nr3.js +0 -20
  24. package/dist/App.zn4mw16t.js +0 -1
  25. package/dist/ConnectionsPage.8r96ryw7.js +0 -3
  26. package/dist/McpPage.3cwh0gnd.js +0 -3
  27. package/dist/SettingsPage.ykgdh5ev.js +0 -3
  28. package/dist/SkillsPage.4np1s65b.js +0 -3
  29. package/dist/TasksPage.4g08t7p6.js +0 -3
  30. package/dist/TelemetryPage.72w9pwcp.js +0 -3
  31. package/dist/TestsPage.z4fk3r7r.js +0 -3
  32. package/dist/ThreadsPage.63tcajeh.js +0 -3
  33. package/dist/apteva-kit.css +0 -1
  34. package/dist/icon.png +0 -0
  35. package/dist/index.html +0 -16
  36. package/dist/styles.css +0 -1
  37. package/scripts/postinstall.mjs +0 -102
  38. package/src/auth/index.ts +0 -394
  39. package/src/auth/middleware.ts +0 -213
  40. package/src/binary.ts +0 -536
  41. package/src/channels/index.ts +0 -40
  42. package/src/channels/telegram.ts +0 -311
  43. package/src/crypto.ts +0 -301
  44. package/src/db-tests.ts +0 -174
  45. package/src/db.ts +0 -3133
  46. package/src/integrations/agentdojo.ts +0 -559
  47. package/src/integrations/composio.ts +0 -437
  48. package/src/integrations/index.ts +0 -87
  49. package/src/integrations/skillsmp.ts +0 -318
  50. package/src/mcp-client.ts +0 -605
  51. package/src/mcp-handler.ts +0 -394
  52. package/src/mcp-platform.ts +0 -2403
  53. package/src/openapi.ts +0 -2410
  54. package/src/providers.ts +0 -597
  55. package/src/routes/api/agent-utils.ts +0 -890
  56. package/src/routes/api/agents.ts +0 -916
  57. package/src/routes/api/api-keys.ts +0 -95
  58. package/src/routes/api/channels.ts +0 -182
  59. package/src/routes/api/helpers.ts +0 -12
  60. package/src/routes/api/integrations.ts +0 -639
  61. package/src/routes/api/mcp.ts +0 -574
  62. package/src/routes/api/meta-agent.ts +0 -195
  63. package/src/routes/api/projects.ts +0 -112
  64. package/src/routes/api/providers.ts +0 -424
  65. package/src/routes/api/skills.ts +0 -537
  66. package/src/routes/api/system.ts +0 -333
  67. package/src/routes/api/telemetry.ts +0 -203
  68. package/src/routes/api/tests.ts +0 -148
  69. package/src/routes/api/triggers.ts +0 -518
  70. package/src/routes/api/users.ts +0 -148
  71. package/src/routes/api/webhooks.ts +0 -171
  72. package/src/routes/api.ts +0 -53
  73. package/src/routes/auth.ts +0 -251
  74. package/src/routes/share.ts +0 -86
  75. package/src/routes/static.ts +0 -131
  76. package/src/server.ts +0 -642
  77. package/src/test-runner.ts +0 -598
  78. package/src/triggers/agentdojo.ts +0 -253
  79. package/src/triggers/composio.ts +0 -264
  80. package/src/triggers/index.ts +0 -71
  81. package/src/tui/AgentList.tsx +0 -145
  82. package/src/tui/App.tsx +0 -102
  83. package/src/tui/Login.tsx +0 -104
  84. package/src/tui/api.ts +0 -72
  85. package/src/tui/index.tsx +0 -7
  86. package/src/web/App.tsx +0 -455
  87. package/src/web/components/activity/ActivityPage.tsx +0 -314
  88. package/src/web/components/activity/index.ts +0 -1
  89. package/src/web/components/agents/AgentCard.tsx +0 -189
  90. package/src/web/components/agents/AgentPanel.tsx +0 -2244
  91. package/src/web/components/agents/AgentsView.tsx +0 -180
  92. package/src/web/components/agents/CreateAgentModal.tsx +0 -475
  93. package/src/web/components/agents/index.ts +0 -4
  94. package/src/web/components/api/ApiDocsPage.tsx +0 -842
  95. package/src/web/components/auth/CreateAccountStep.tsx +0 -176
  96. package/src/web/components/auth/LoginPage.tsx +0 -91
  97. package/src/web/components/auth/index.ts +0 -2
  98. package/src/web/components/common/Icons.tsx +0 -250
  99. package/src/web/components/common/LoadingSpinner.tsx +0 -44
  100. package/src/web/components/common/Modal.tsx +0 -199
  101. package/src/web/components/common/Select.tsx +0 -97
  102. package/src/web/components/common/index.ts +0 -20
  103. package/src/web/components/connections/ConnectionsPage.tsx +0 -54
  104. package/src/web/components/connections/IntegrationsTab.tsx +0 -170
  105. package/src/web/components/connections/OverviewTab.tsx +0 -137
  106. package/src/web/components/connections/TriggersTab.tsx +0 -1346
  107. package/src/web/components/dashboard/Dashboard.tsx +0 -572
  108. package/src/web/components/dashboard/index.ts +0 -1
  109. package/src/web/components/index.ts +0 -21
  110. package/src/web/components/layout/ErrorBanner.tsx +0 -18
  111. package/src/web/components/layout/Header.tsx +0 -332
  112. package/src/web/components/layout/Sidebar.tsx +0 -231
  113. package/src/web/components/layout/index.ts +0 -3
  114. package/src/web/components/mcp/IntegrationsPanel.tsx +0 -857
  115. package/src/web/components/mcp/McpPage.tsx +0 -2515
  116. package/src/web/components/mcp/index.ts +0 -1
  117. package/src/web/components/meta-agent/MetaAgent.tsx +0 -245
  118. package/src/web/components/onboarding/OnboardingWizard.tsx +0 -404
  119. package/src/web/components/onboarding/index.ts +0 -1
  120. package/src/web/components/settings/SettingsPage.tsx +0 -2776
  121. package/src/web/components/settings/index.ts +0 -1
  122. package/src/web/components/skills/SkillsPage.tsx +0 -1200
  123. package/src/web/components/tasks/TasksPage.tsx +0 -1116
  124. package/src/web/components/tasks/index.ts +0 -1
  125. package/src/web/components/telemetry/TelemetryPage.tsx +0 -1129
  126. package/src/web/components/tests/TestsPage.tsx +0 -594
  127. package/src/web/components/threads/ThreadsPage.tsx +0 -315
  128. package/src/web/context/AuthContext.tsx +0 -242
  129. package/src/web/context/ProjectContext.tsx +0 -214
  130. package/src/web/context/TelemetryContext.tsx +0 -299
  131. package/src/web/context/ThemeContext.tsx +0 -90
  132. package/src/web/context/UIModeContext.tsx +0 -49
  133. package/src/web/context/index.ts +0 -12
  134. package/src/web/hooks/index.ts +0 -3
  135. package/src/web/hooks/useAgents.ts +0 -115
  136. package/src/web/hooks/useOnboarding.ts +0 -20
  137. package/src/web/hooks/useProviders.ts +0 -75
  138. package/src/web/icon.png +0 -0
  139. package/src/web/index.html +0 -16
  140. package/src/web/styles.css +0 -118
  141. package/src/web/themes.ts +0 -162
  142. package/src/web/types.ts +0 -298
@@ -1,842 +0,0 @@
1
- import { useState, useEffect } from "react";
2
- import { useAuth } from "../../context";
3
-
4
- interface OpenApiPath {
5
- [method: string]: {
6
- tags?: string[];
7
- summary?: string;
8
- description?: string;
9
- parameters?: Array<{
10
- name: string;
11
- in: string;
12
- required?: boolean;
13
- schema?: { type: string };
14
- description?: string;
15
- }>;
16
- requestBody?: {
17
- required?: boolean;
18
- content?: {
19
- [mediaType: string]: {
20
- schema?: any;
21
- };
22
- };
23
- };
24
- responses?: {
25
- [code: string]: {
26
- description?: string;
27
- content?: any;
28
- };
29
- };
30
- };
31
- }
32
-
33
- interface OpenApiSpec {
34
- info: {
35
- title: string;
36
- description: string;
37
- version: string;
38
- };
39
- tags?: Array<{ name: string; description: string }>;
40
- paths: { [path: string]: OpenApiPath };
41
- components?: {
42
- schemas?: { [name: string]: any };
43
- };
44
- }
45
-
46
- const METHOD_COLORS: Record<string, string> = {
47
- get: "#61affe",
48
- post: "#49cc90",
49
- put: "#fca130",
50
- delete: "#f93e3e",
51
- patch: "#50e3c2",
52
- };
53
-
54
- // Try It Out component
55
- function TryItOut({
56
- method,
57
- path,
58
- parameters,
59
- requestBody,
60
- authFetch,
61
- }: {
62
- method: string;
63
- path: string;
64
- parameters?: Array<{
65
- name: string;
66
- in: string;
67
- required?: boolean;
68
- schema?: { type: string };
69
- }>;
70
- requestBody?: any;
71
- authFetch: (url: string, options?: RequestInit) => Promise<Response>;
72
- }) {
73
- const [paramValues, setParamValues] = useState<Record<string, string>>({});
74
- const [bodyValue, setBodyValue] = useState("");
75
- const [response, setResponse] = useState<{ status: number; data: any } | null>(null);
76
- const [loading, setLoading] = useState(false);
77
- const [error, setError] = useState<string | null>(null);
78
-
79
- // Initialize body with example if available
80
- useEffect(() => {
81
- if (requestBody?.content?.["application/json"]?.schema) {
82
- const schema = requestBody.content["application/json"].schema;
83
- if (schema.example) {
84
- setBodyValue(JSON.stringify(schema.example, null, 2));
85
- } else if (schema.properties) {
86
- // Generate example from properties
87
- const example: Record<string, any> = {};
88
- for (const [key, prop] of Object.entries(schema.properties) as [string, any][]) {
89
- if (prop.example !== undefined) {
90
- example[key] = prop.example;
91
- } else if (prop.type === "string") {
92
- example[key] = "";
93
- } else if (prop.type === "number" || prop.type === "integer") {
94
- example[key] = 0;
95
- } else if (prop.type === "boolean") {
96
- example[key] = false;
97
- } else if (prop.type === "array") {
98
- example[key] = [];
99
- } else if (prop.type === "object") {
100
- example[key] = {};
101
- }
102
- }
103
- setBodyValue(JSON.stringify(example, null, 2));
104
- }
105
- }
106
- }, [requestBody]);
107
-
108
- const execute = async () => {
109
- setLoading(true);
110
- setError(null);
111
- setResponse(null);
112
-
113
- try {
114
- // Build URL with path parameters
115
- let url = path;
116
- const queryParams: string[] = [];
117
-
118
- for (const param of parameters || []) {
119
- const value = paramValues[param.name] || "";
120
- if (param.in === "path") {
121
- url = url.replace(`{${param.name}}`, encodeURIComponent(value));
122
- } else if (param.in === "query" && value) {
123
- queryParams.push(`${param.name}=${encodeURIComponent(value)}`);
124
- }
125
- }
126
-
127
- if (queryParams.length > 0) {
128
- url += `?${queryParams.join("&")}`;
129
- }
130
-
131
- const options: RequestInit = {
132
- method: method.toUpperCase(),
133
- };
134
-
135
- if (bodyValue && ["post", "put", "patch"].includes(method)) {
136
- options.headers = { "Content-Type": "application/json" };
137
- options.body = bodyValue;
138
- }
139
-
140
- const res = await authFetch(`/api${url}`, options);
141
- let data;
142
- const contentType = res.headers.get("content-type");
143
- if (contentType?.includes("application/json")) {
144
- data = await res.json();
145
- } else {
146
- data = await res.text();
147
- }
148
-
149
- setResponse({ status: res.status, data });
150
- } catch (err: any) {
151
- setError(err.message || "Request failed");
152
- } finally {
153
- setLoading(false);
154
- }
155
- };
156
-
157
- const pathParams = parameters?.filter((p) => p.in === "path") || [];
158
- const queryParams = parameters?.filter((p) => p.in === "query") || [];
159
- const hasBody = ["post", "put", "patch"].includes(method) && requestBody;
160
-
161
- return (
162
- <div style={{ marginTop: 16, padding: 16, background: "var(--color-bg)", borderRadius: 6, border: "1px solid var(--color-border-light)" }}>
163
- <h4 style={{ fontSize: 13, color: "var(--color-accent)", marginBottom: 12, fontWeight: 600 }}>Try it out</h4>
164
-
165
- {/* Path Parameters */}
166
- {pathParams.length > 0 && (
167
- <div style={{ marginBottom: 12 }}>
168
- <div style={{ fontSize: 11, color: "var(--color-text-muted)", marginBottom: 6 }}>Path Parameters</div>
169
- {pathParams.map((param) => (
170
- <div key={param.name} style={{ marginBottom: 8 }}>
171
- <label style={{ fontSize: 12, color: "var(--color-text-secondary)", display: "block", marginBottom: 4 }}>
172
- {param.name} {param.required && <span style={{ color: "#f66" }}>*</span>}
173
- </label>
174
- <input
175
- type="text"
176
- value={paramValues[param.name] || ""}
177
- onChange={(e) => setParamValues({ ...paramValues, [param.name]: e.target.value })}
178
- placeholder={param.schema?.type || "string"}
179
- style={{
180
- width: "100%",
181
- padding: "8px 12px",
182
- background: "var(--color-surface)",
183
- border: "1px solid var(--color-border-light)",
184
- borderRadius: 4,
185
- color: "var(--color-text)",
186
- fontSize: 13,
187
- fontFamily: "monospace",
188
- }}
189
- />
190
- </div>
191
- ))}
192
- </div>
193
- )}
194
-
195
- {/* Query Parameters */}
196
- {queryParams.length > 0 && (
197
- <div style={{ marginBottom: 12 }}>
198
- <div style={{ fontSize: 11, color: "var(--color-text-muted)", marginBottom: 6 }}>Query Parameters</div>
199
- {queryParams.map((param) => (
200
- <div key={param.name} style={{ marginBottom: 8 }}>
201
- <label style={{ fontSize: 12, color: "var(--color-text-secondary)", display: "block", marginBottom: 4 }}>
202
- {param.name} {param.required && <span style={{ color: "#f66" }}>*</span>}
203
- </label>
204
- <input
205
- type="text"
206
- value={paramValues[param.name] || ""}
207
- onChange={(e) => setParamValues({ ...paramValues, [param.name]: e.target.value })}
208
- placeholder={param.schema?.type || "string"}
209
- style={{
210
- width: "100%",
211
- padding: "8px 12px",
212
- background: "var(--color-surface)",
213
- border: "1px solid var(--color-border-light)",
214
- borderRadius: 4,
215
- color: "var(--color-text)",
216
- fontSize: 13,
217
- fontFamily: "monospace",
218
- }}
219
- />
220
- </div>
221
- ))}
222
- </div>
223
- )}
224
-
225
- {/* Request Body */}
226
- {hasBody && (
227
- <div style={{ marginBottom: 12 }}>
228
- <div style={{ fontSize: 11, color: "var(--color-text-muted)", marginBottom: 6 }}>Request Body (JSON)</div>
229
- <textarea
230
- value={bodyValue}
231
- onChange={(e) => setBodyValue(e.target.value)}
232
- rows={6}
233
- style={{
234
- width: "100%",
235
- padding: "8px 12px",
236
- background: "var(--color-surface)",
237
- border: "1px solid var(--color-border-light)",
238
- borderRadius: 4,
239
- color: "var(--color-text)",
240
- fontSize: 12,
241
- fontFamily: "monospace",
242
- resize: "vertical",
243
- }}
244
- />
245
- </div>
246
- )}
247
-
248
- {/* Execute Button */}
249
- <button
250
- onClick={execute}
251
- disabled={loading}
252
- style={{
253
- padding: "10px 20px",
254
- background: loading ? "var(--color-border-light)" : "var(--color-accent)",
255
- color: loading ? "var(--color-text-muted)" : "#000",
256
- border: "none",
257
- borderRadius: 4,
258
- cursor: loading ? "not-allowed" : "pointer",
259
- fontSize: 13,
260
- fontWeight: 600,
261
- }}
262
- >
263
- {loading ? "Executing..." : "Execute"}
264
- </button>
265
-
266
- {/* Error */}
267
- {error && (
268
- <div style={{ marginTop: 12, padding: 12, background: "#2a1515", borderRadius: 4, color: "#f66", fontSize: 12 }}>
269
- {error}
270
- </div>
271
- )}
272
-
273
- {/* Response */}
274
- {response && (
275
- <div style={{ marginTop: 12 }}>
276
- <div style={{ fontSize: 11, color: "var(--color-text-muted)", marginBottom: 6 }}>
277
- Response{" "}
278
- <span style={{ color: response.status >= 200 && response.status < 300 ? "#49cc90" : "#f66" }}>
279
- {response.status}
280
- </span>
281
- </div>
282
- <pre
283
- style={{
284
- padding: 12,
285
- background: "var(--color-surface)",
286
- borderRadius: 4,
287
- color: "var(--color-text-secondary)",
288
- fontSize: 11,
289
- fontFamily: "monospace",
290
- overflow: "auto",
291
- maxHeight: 300,
292
- whiteSpace: "pre-wrap",
293
- wordBreak: "break-word",
294
- }}
295
- >
296
- {typeof response.data === "string" ? response.data : JSON.stringify(response.data, null, 2)}
297
- </pre>
298
- </div>
299
- )}
300
- </div>
301
- );
302
- }
303
-
304
- export function ApiDocsPage() {
305
- const { authFetch } = useAuth();
306
- const [spec, setSpec] = useState<OpenApiSpec | null>(null);
307
- const [loading, setLoading] = useState(true);
308
- const [expandedPaths, setExpandedPaths] = useState<Set<string>>(new Set());
309
- const [selectedTag, setSelectedTag] = useState<string | null>(null);
310
- const [copied, setCopied] = useState(false);
311
-
312
- useEffect(() => {
313
- loadSpec();
314
- }, []);
315
-
316
- async function copyToClipboard() {
317
- if (!spec) return;
318
- try {
319
- await navigator.clipboard.writeText(JSON.stringify(spec, null, 2));
320
- setCopied(true);
321
- setTimeout(() => setCopied(false), 2000);
322
- } catch (err) {
323
- console.error("Failed to copy:", err);
324
- }
325
- }
326
-
327
- function downloadJson() {
328
- if (!spec) return;
329
- const blob = new Blob([JSON.stringify(spec, null, 2)], { type: "application/json" });
330
- const url = URL.createObjectURL(blob);
331
- const a = document.createElement("a");
332
- a.href = url;
333
- a.download = "apteva-openapi.json";
334
- a.click();
335
- URL.revokeObjectURL(url);
336
- }
337
-
338
- async function loadSpec() {
339
- try {
340
- const res = await authFetch("/api/openapi");
341
- if (res.ok) {
342
- const data = await res.json();
343
- setSpec(data);
344
- }
345
- } catch (err) {
346
- console.error("Failed to load OpenAPI spec:", err);
347
- } finally {
348
- setLoading(false);
349
- }
350
- }
351
-
352
- function togglePath(pathKey: string) {
353
- setExpandedPaths((prev) => {
354
- const next = new Set(prev);
355
- if (next.has(pathKey)) {
356
- next.delete(pathKey);
357
- } else {
358
- next.add(pathKey);
359
- }
360
- return next;
361
- });
362
- }
363
-
364
- function getSchemaPreview(schema: any, depth = 0): string {
365
- if (!schema) return "{}";
366
- if (depth > 2) return "...";
367
-
368
- if (schema.$ref) {
369
- const refName = schema.$ref.split("/").pop();
370
- return refName || "Object";
371
- }
372
-
373
- if (schema.type === "array") {
374
- const itemType = getSchemaPreview(schema.items, depth + 1);
375
- return `${itemType}[]`;
376
- }
377
-
378
- if (schema.type === "object" && schema.properties) {
379
- const props = Object.entries(schema.properties)
380
- .slice(0, 3)
381
- .map(([k, v]: [string, any]) => `${k}: ${v.type || "any"}`)
382
- .join(", ");
383
- const more = Object.keys(schema.properties).length > 3 ? ", ..." : "";
384
- return `{ ${props}${more} }`;
385
- }
386
-
387
- return schema.type || "any";
388
- }
389
-
390
- if (loading) {
391
- return (
392
- <div style={{ padding: 24 }}>
393
- <p style={{ color: "var(--color-text-secondary)" }}>Loading API documentation...</p>
394
- </div>
395
- );
396
- }
397
-
398
- if (!spec) {
399
- return (
400
- <div style={{ padding: 24 }}>
401
- <p style={{ color: "#f66" }}>Failed to load API documentation</p>
402
- </div>
403
- );
404
- }
405
-
406
- const tags = spec.tags || [];
407
- const paths = Object.entries(spec.paths);
408
-
409
- // Extract schema names referenced by a method
410
- function getReferencedSchemas(method: any): Set<string> {
411
- const refs = new Set<string>();
412
-
413
- function extractRefs(obj: any) {
414
- if (!obj) return;
415
- if (typeof obj === "object") {
416
- if (obj.$ref) {
417
- const name = obj.$ref.split("/").pop();
418
- if (name) refs.add(name);
419
- }
420
- for (const value of Object.values(obj)) {
421
- extractRefs(value);
422
- }
423
- }
424
- }
425
-
426
- extractRefs(method.requestBody);
427
- extractRefs(method.responses);
428
- return refs;
429
- }
430
-
431
- // Get all schemas referenced by filtered endpoints
432
- function getFilteredSchemas(): string[] {
433
- if (!selectedTag || !spec.components?.schemas) {
434
- return Object.keys(spec.components?.schemas || {});
435
- }
436
-
437
- const usedSchemas = new Set<string>();
438
-
439
- for (const [_, methods] of filteredPaths) {
440
- for (const [method, details] of Object.entries(methods)) {
441
- if (!["get", "post", "put", "delete", "patch"].includes(method)) continue;
442
- if (details.tags?.includes(selectedTag)) {
443
- const refs = getReferencedSchemas(details);
444
- refs.forEach(r => usedSchemas.add(r));
445
- }
446
- }
447
- }
448
-
449
- return Array.from(usedSchemas);
450
- }
451
-
452
- // Filter paths by selected tag
453
- const filteredPaths = selectedTag
454
- ? paths.filter(([_, methods]) =>
455
- Object.values(methods).some((m) => m.tags?.includes(selectedTag))
456
- )
457
- : paths;
458
-
459
- return (
460
- <div style={{ padding: 24, maxWidth: 1000, height: "100%", overflowY: "auto" }}>
461
- <div style={{ marginBottom: 24 }}>
462
- <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 8 }}>
463
- <h1 style={{ fontSize: 24, fontWeight: 600 }}>
464
- {spec.info.title}
465
- </h1>
466
- <div style={{ display: "flex", gap: 8 }}>
467
- <button
468
- onClick={copyToClipboard}
469
- style={{
470
- padding: "8px 16px",
471
- borderRadius: 4,
472
- border: "1px solid var(--color-border-light)",
473
- background: copied ? "#49cc90" : "var(--color-surface-raised)",
474
- color: copied ? "#000" : "var(--color-text)",
475
- cursor: "pointer",
476
- fontSize: 12,
477
- fontFamily: "inherit",
478
- }}
479
- >
480
- {copied ? "Copied!" : "Copy JSON"}
481
- </button>
482
- <button
483
- onClick={downloadJson}
484
- style={{
485
- padding: "8px 16px",
486
- borderRadius: 4,
487
- border: "1px solid var(--color-border-light)",
488
- background: "var(--color-surface-raised)",
489
- color: "var(--color-text)",
490
- cursor: "pointer",
491
- fontSize: 12,
492
- fontFamily: "inherit",
493
- }}
494
- >
495
- Download
496
- </button>
497
- </div>
498
- </div>
499
- <p style={{ color: "var(--color-text-secondary)", marginBottom: 8 }}>
500
- {spec.info.description.split("\n")[0]}
501
- </p>
502
- <p style={{ color: "var(--color-text-muted)", fontSize: 12 }}>Version: {spec.info.version}</p>
503
- </div>
504
-
505
- {/* Base URL */}
506
- <div
507
- style={{
508
- background: "var(--color-surface-raised)",
509
- padding: 12,
510
- borderRadius: 6,
511
- marginBottom: 24,
512
- fontFamily: "monospace",
513
- }}
514
- >
515
- <span style={{ color: "var(--color-text-secondary)" }}>Base URL: </span>
516
- <span style={{ color: "#61affe" }}>
517
- {window.location.origin}/api
518
- </span>
519
- </div>
520
-
521
- {/* Tag filters */}
522
- <div style={{ marginBottom: 24, display: "flex", flexWrap: "wrap", gap: 8 }}>
523
- <button
524
- onClick={() => setSelectedTag(null)}
525
- style={{
526
- padding: "6px 12px",
527
- borderRadius: 4,
528
- border: "1px solid var(--color-border-light)",
529
- background: selectedTag === null ? "var(--color-border-light)" : "transparent",
530
- color: selectedTag === null ? "var(--color-text)" : "var(--color-text-secondary)",
531
- cursor: "pointer",
532
- fontSize: 12,
533
- }}
534
- >
535
- All
536
- </button>
537
- {tags.map((tag) => (
538
- <button
539
- key={tag.name}
540
- onClick={() => setSelectedTag(tag.name)}
541
- style={{
542
- padding: "6px 12px",
543
- borderRadius: 4,
544
- border: "1px solid var(--color-border-light)",
545
- background: selectedTag === tag.name ? "var(--color-border-light)" : "transparent",
546
- color: selectedTag === tag.name ? "var(--color-text)" : "var(--color-text-secondary)",
547
- cursor: "pointer",
548
- fontSize: 12,
549
- }}
550
- title={tag.description}
551
- >
552
- {tag.name}
553
- </button>
554
- ))}
555
- </div>
556
-
557
- {/* Endpoints */}
558
- <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
559
- {filteredPaths.map(([path, methods]) =>
560
- Object.entries(methods)
561
- .filter(([method]) => ["get", "post", "put", "delete", "patch"].includes(method))
562
- .map(([method, details]) => {
563
- const pathKey = `${method}:${path}`;
564
- const isExpanded = expandedPaths.has(pathKey);
565
- const methodUpper = method.toUpperCase();
566
- const color = METHOD_COLORS[method] || "var(--color-text-secondary)";
567
-
568
- return (
569
- <div
570
- key={pathKey}
571
- style={{
572
- border: "1px solid var(--color-border-light)",
573
- borderRadius: 6,
574
- overflow: "hidden",
575
- }}
576
- >
577
- {/* Header */}
578
- <div
579
- onClick={() => togglePath(pathKey)}
580
- style={{
581
- display: "flex",
582
- alignItems: "center",
583
- gap: 12,
584
- padding: "12px 16px",
585
- background: isExpanded ? "var(--color-surface-raised)" : "transparent",
586
- cursor: "pointer",
587
- }}
588
- >
589
- <span
590
- style={{
591
- background: color,
592
- color: "#000",
593
- padding: "4px 8px",
594
- borderRadius: 4,
595
- fontSize: 11,
596
- fontWeight: 600,
597
- minWidth: 60,
598
- textAlign: "center",
599
- }}
600
- >
601
- {methodUpper}
602
- </span>
603
- <span style={{ fontFamily: "monospace", color: "var(--color-text)" }}>
604
- {path}
605
- </span>
606
- <span style={{ color: "var(--color-text-secondary)", flex: 1, fontSize: 13 }}>
607
- {details.summary}
608
- </span>
609
- <span style={{ color: "var(--color-text-muted)", fontSize: 12 }}>
610
- {isExpanded ? "[-]" : "[+]"}
611
- </span>
612
- </div>
613
-
614
- {/* Expanded details */}
615
- {isExpanded && (
616
- <div
617
- style={{
618
- padding: 16,
619
- background: "var(--color-bg)",
620
- borderTop: "1px solid var(--color-border-light)",
621
- }}
622
- >
623
- {details.description && (
624
- <p style={{ color: "var(--color-text-secondary)", marginBottom: 16, fontSize: 13 }}>
625
- {details.description}
626
- </p>
627
- )}
628
-
629
- {/* Parameters */}
630
- {details.parameters && details.parameters.length > 0 && (
631
- <div style={{ marginBottom: 16 }}>
632
- <h4 style={{ fontSize: 13, color: "var(--color-text-secondary)", marginBottom: 8 }}>
633
- Parameters
634
- </h4>
635
- <div
636
- style={{
637
- background: "var(--color-surface-raised)",
638
- borderRadius: 4,
639
- padding: 12,
640
- }}
641
- >
642
- {details.parameters.map((param) => (
643
- <div
644
- key={param.name}
645
- style={{
646
- display: "flex",
647
- gap: 12,
648
- marginBottom: 8,
649
- fontSize: 12,
650
- }}
651
- >
652
- <span style={{ color: "#61affe", minWidth: 100 }}>
653
- {param.name}
654
- {param.required && (
655
- <span style={{ color: "#f66" }}>*</span>
656
- )}
657
- </span>
658
- <span style={{ color: "var(--color-text-muted)" }}>({param.in})</span>
659
- <span style={{ color: "var(--color-text-secondary)" }}>
660
- {param.schema?.type || "string"}
661
- </span>
662
- {param.description && (
663
- <span style={{ color: "var(--color-text-muted)" }}>
664
- - {param.description}
665
- </span>
666
- )}
667
- </div>
668
- ))}
669
- </div>
670
- </div>
671
- )}
672
-
673
- {/* Request Body */}
674
- {details.requestBody && (
675
- <div style={{ marginBottom: 16 }}>
676
- <h4 style={{ fontSize: 13, color: "var(--color-text-secondary)", marginBottom: 8 }}>
677
- Request Body
678
- {details.requestBody.required && (
679
- <span style={{ color: "#f66" }}> (required)</span>
680
- )}
681
- </h4>
682
- <div
683
- style={{
684
- background: "var(--color-surface-raised)",
685
- borderRadius: 4,
686
- padding: 12,
687
- fontFamily: "monospace",
688
- fontSize: 12,
689
- color: "#49cc90",
690
- }}
691
- >
692
- {Object.entries(details.requestBody.content || {}).map(
693
- ([mediaType, content]) => (
694
- <div key={mediaType}>
695
- <span style={{ color: "var(--color-text-muted)" }}>{mediaType}: </span>
696
- {getSchemaPreview(content.schema)}
697
- </div>
698
- )
699
- )}
700
- </div>
701
- </div>
702
- )}
703
-
704
- {/* Responses */}
705
- {details.responses && (
706
- <div>
707
- <h4 style={{ fontSize: 13, color: "var(--color-text-secondary)", marginBottom: 8 }}>
708
- Responses
709
- </h4>
710
- <div
711
- style={{
712
- background: "var(--color-surface-raised)",
713
- borderRadius: 4,
714
- padding: 12,
715
- }}
716
- >
717
- {Object.entries(details.responses).map(([code, resp]) => {
718
- const respContent = resp.content?.["application/json"]?.schema;
719
- const schemaRef = respContent?.$ref?.split("/").pop();
720
- const schemaType = respContent?.type;
721
- const schemaItems = respContent?.items?.$ref?.split("/").pop();
722
-
723
- return (
724
- <div
725
- key={code}
726
- style={{
727
- marginBottom: 12,
728
- fontSize: 12,
729
- }}
730
- >
731
- <div style={{ display: "flex", gap: 12, marginBottom: 4 }}>
732
- <span
733
- style={{
734
- color: code.startsWith("2") ? "#49cc90" : "#f66",
735
- minWidth: 40,
736
- }}
737
- >
738
- {code}
739
- </span>
740
- <span style={{ color: "var(--color-text-secondary)" }}>{resp.description}</span>
741
- </div>
742
- {respContent && (
743
- <div
744
- style={{
745
- marginLeft: 52,
746
- padding: "8px 12px",
747
- background: "var(--color-bg)",
748
- borderRadius: 4,
749
- fontFamily: "monospace",
750
- }}
751
- >
752
- {schemaRef ? (
753
- <span style={{ color: "#61affe" }}>{schemaRef}</span>
754
- ) : schemaType === "array" && schemaItems ? (
755
- <span style={{ color: "#61affe" }}>{schemaItems}[]</span>
756
- ) : schemaType === "array" ? (
757
- <span style={{ color: "var(--color-text-secondary)" }}>array</span>
758
- ) : (
759
- <span style={{ color: "var(--color-text-secondary)" }}>{getSchemaPreview(respContent)}</span>
760
- )}
761
- </div>
762
- )}
763
- </div>
764
- );
765
- })}
766
- </div>
767
- </div>
768
- )}
769
-
770
- {/* Try It Out */}
771
- <TryItOut
772
- method={method}
773
- path={path}
774
- parameters={details.parameters}
775
- requestBody={details.requestBody}
776
- authFetch={authFetch}
777
- />
778
- </div>
779
- )}
780
- </div>
781
- );
782
- })
783
- )}
784
- </div>
785
-
786
- {/* Schemas section */}
787
- {spec.components?.schemas && getFilteredSchemas().length > 0 && (
788
- <div style={{ marginTop: 32 }}>
789
- <h2 style={{ fontSize: 18, fontWeight: 600, marginBottom: 16 }}>
790
- Schemas {selectedTag && <span style={{ color: "var(--color-text-muted)", fontSize: 14 }}>({selectedTag})</span>}
791
- </h2>
792
- <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
793
- {getFilteredSchemas().map((name) => {
794
- const schema = spec.components!.schemas![name];
795
- if (!schema) return null;
796
- return (
797
- <div
798
- key={name}
799
- style={{
800
- border: "1px solid var(--color-border-light)",
801
- borderRadius: 6,
802
- padding: 12,
803
- }}
804
- >
805
- <h3 style={{ fontSize: 14, color: "#61affe", marginBottom: 8 }}>
806
- {name}
807
- </h3>
808
- {schema.properties && (
809
- <div style={{ fontSize: 12 }}>
810
- {Object.entries(schema.properties).map(([prop, propSchema]: [string, any]) => (
811
- <div
812
- key={prop}
813
- style={{
814
- display: "flex",
815
- gap: 8,
816
- marginBottom: 4,
817
- fontFamily: "monospace",
818
- }}
819
- >
820
- <span style={{ color: "var(--color-text)", minWidth: 120 }}>{prop}</span>
821
- <span style={{ color: "var(--color-text-secondary)" }}>
822
- {propSchema.type || (propSchema.$ref ? propSchema.$ref.split("/").pop() : "any")}
823
- {propSchema.nullable && " | null"}
824
- </span>
825
- {propSchema.enum && (
826
- <span style={{ color: "var(--color-text-muted)" }}>
827
- [{propSchema.enum.join(" | ")}]
828
- </span>
829
- )}
830
- </div>
831
- ))}
832
- </div>
833
- )}
834
- </div>
835
- );
836
- })}
837
- </div>
838
- </div>
839
- )}
840
- </div>
841
- );
842
- }