@vertesia/tools-sdk 0.81.1 → 1.0.0-dev.20260203.130115Z

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 (101) hide show
  1. package/package.json +9 -8
  2. package/src/ContentTypesCollection.ts +51 -0
  3. package/src/InteractionCollection.ts +1 -94
  4. package/src/SkillCollection.ts +88 -15
  5. package/src/ToolCollection.ts +65 -17
  6. package/src/ToolRegistry.ts +101 -9
  7. package/src/auth.ts +37 -6
  8. package/src/index.ts +7 -3
  9. package/src/server/app-package.ts +102 -0
  10. package/src/server/conyent-types.ts +71 -0
  11. package/src/server/interactions.ts +100 -0
  12. package/src/server/mcp.ts +51 -0
  13. package/src/server/site.ts +53 -0
  14. package/src/server/skills.ts +133 -0
  15. package/src/server/tools.ts +128 -0
  16. package/src/server/types.ts +87 -0
  17. package/src/server/widgets.ts +38 -0
  18. package/src/server.ts +80 -359
  19. package/src/site/styles.ts +71 -0
  20. package/src/site/templates.ts +215 -119
  21. package/src/types.ts +22 -18
  22. package/src/utils.ts +20 -0
  23. package/lib/cjs/InteractionCollection.js +0 -164
  24. package/lib/cjs/InteractionCollection.js.map +0 -1
  25. package/lib/cjs/SkillCollection.js +0 -318
  26. package/lib/cjs/SkillCollection.js.map +0 -1
  27. package/lib/cjs/ToolCollection.js +0 -192
  28. package/lib/cjs/ToolCollection.js.map +0 -1
  29. package/lib/cjs/ToolRegistry.js +0 -44
  30. package/lib/cjs/ToolRegistry.js.map +0 -1
  31. package/lib/cjs/auth.js +0 -89
  32. package/lib/cjs/auth.js.map +0 -1
  33. package/lib/cjs/build/validate.js +0 -7
  34. package/lib/cjs/build/validate.js.map +0 -1
  35. package/lib/cjs/copy-assets.js +0 -84
  36. package/lib/cjs/copy-assets.js.map +0 -1
  37. package/lib/cjs/index.js +0 -30
  38. package/lib/cjs/index.js.map +0 -1
  39. package/lib/cjs/package.json +0 -3
  40. package/lib/cjs/server.js +0 -327
  41. package/lib/cjs/server.js.map +0 -1
  42. package/lib/cjs/site/styles.js +0 -621
  43. package/lib/cjs/site/styles.js.map +0 -1
  44. package/lib/cjs/site/templates.js +0 -932
  45. package/lib/cjs/site/templates.js.map +0 -1
  46. package/lib/cjs/types.js +0 -3
  47. package/lib/cjs/types.js.map +0 -1
  48. package/lib/cjs/utils.js +0 -7
  49. package/lib/cjs/utils.js.map +0 -1
  50. package/lib/esm/InteractionCollection.js +0 -125
  51. package/lib/esm/InteractionCollection.js.map +0 -1
  52. package/lib/esm/SkillCollection.js +0 -311
  53. package/lib/esm/SkillCollection.js.map +0 -1
  54. package/lib/esm/ToolCollection.js +0 -154
  55. package/lib/esm/ToolCollection.js.map +0 -1
  56. package/lib/esm/ToolRegistry.js +0 -39
  57. package/lib/esm/ToolRegistry.js.map +0 -1
  58. package/lib/esm/auth.js +0 -82
  59. package/lib/esm/auth.js.map +0 -1
  60. package/lib/esm/build/validate.js +0 -4
  61. package/lib/esm/build/validate.js.map +0 -1
  62. package/lib/esm/copy-assets.js +0 -81
  63. package/lib/esm/copy-assets.js.map +0 -1
  64. package/lib/esm/index.js +0 -10
  65. package/lib/esm/index.js.map +0 -1
  66. package/lib/esm/server.js +0 -323
  67. package/lib/esm/server.js.map +0 -1
  68. package/lib/esm/site/styles.js +0 -618
  69. package/lib/esm/site/styles.js.map +0 -1
  70. package/lib/esm/site/templates.js +0 -920
  71. package/lib/esm/site/templates.js.map +0 -1
  72. package/lib/esm/types.js +0 -2
  73. package/lib/esm/types.js.map +0 -1
  74. package/lib/esm/utils.js +0 -4
  75. package/lib/esm/utils.js.map +0 -1
  76. package/lib/types/InteractionCollection.d.ts +0 -48
  77. package/lib/types/InteractionCollection.d.ts.map +0 -1
  78. package/lib/types/SkillCollection.d.ts +0 -111
  79. package/lib/types/SkillCollection.d.ts.map +0 -1
  80. package/lib/types/ToolCollection.d.ts +0 -61
  81. package/lib/types/ToolCollection.d.ts.map +0 -1
  82. package/lib/types/ToolRegistry.d.ts +0 -15
  83. package/lib/types/ToolRegistry.d.ts.map +0 -1
  84. package/lib/types/auth.d.ts +0 -20
  85. package/lib/types/auth.d.ts.map +0 -1
  86. package/lib/types/build/validate.d.ts +0 -2
  87. package/lib/types/build/validate.d.ts.map +0 -1
  88. package/lib/types/copy-assets.d.ts +0 -14
  89. package/lib/types/copy-assets.d.ts.map +0 -1
  90. package/lib/types/index.d.ts +0 -10
  91. package/lib/types/index.d.ts.map +0 -1
  92. package/lib/types/server.d.ts +0 -72
  93. package/lib/types/server.d.ts.map +0 -1
  94. package/lib/types/site/styles.d.ts +0 -5
  95. package/lib/types/site/styles.d.ts.map +0 -1
  96. package/lib/types/site/templates.d.ts +0 -54
  97. package/lib/types/site/templates.d.ts.map +0 -1
  98. package/lib/types/types.d.ts +0 -262
  99. package/lib/types/types.d.ts.map +0 -1
  100. package/lib/types/utils.d.ts +0 -2
  101. package/lib/types/utils.d.ts.map +0 -1
package/src/server.ts CHANGED
@@ -1,69 +1,29 @@
1
- import { CatalogInteractionRef } from "@vertesia/common";
2
1
  import { Context, Hono } from "hono";
3
2
  import { cors } from "hono/cors";
4
3
  import { HTTPException } from "hono/http-exception";
5
- import { authorize } from "./auth.js";
6
- import { InteractionCollection } from "./InteractionCollection.js";
7
- import {
8
- indexPage,
9
- interactionCollectionPage,
10
- skillCollectionPage,
11
- toolCollectionPage
12
- } from "./site/templates.js";
13
- import { SkillCollection } from "./SkillCollection.js";
14
- import { ToolCollection } from "./ToolCollection.js";
15
- import type {
16
- SkillDefinition,
17
- ToolCollectionDefinition,
18
- ToolDefinition
19
- } from "./types.js";
4
+ import { z } from "zod";
5
+ import { createInteractionsRoute } from "./server/interactions.js";
6
+ import { createMcpRoute } from "./server/mcp.js";
7
+ import { createSiteRoute } from "./server/site.js";
8
+ import { createSkillsRoute } from "./server/skills.js";
9
+ import { createToolsRoute } from "./server/tools.js";
10
+ import { ToolContext, ToolServerConfig } from "./server/types.js";
11
+ import { ToolExecutionPayload } from "./types.js";
12
+ import { createWidgetsRoute } from "./server/widgets.js";
13
+ import { createPackageRoute } from "./server/app-package.js";
14
+ import { createContentTypesRoute } from "./server/conyent-types.js";
15
+
16
+ // Schema for tool execution payload
17
+ const ToolExecutionPayloadSchema = z.object({
18
+ tool_use: z.object({
19
+ id: z.string(),
20
+ tool_name: z.string(),
21
+ tool_input: z.record(z.string(), z.any()).default({}),
22
+ }),
23
+ metadata: z.record(z.string(), z.any()).optional(),
24
+ });
20
25
 
21
- /**
22
- * MCP Provider interface for server configuration
23
- */
24
- export interface MCPProviderConfig {
25
- name: string;
26
- description?: string;
27
- createMCPConnection: (session: any, config: Record<string, any>) => Promise<{
28
- name: string;
29
- url: string;
30
- token: string;
31
- }>;
32
- }
33
26
 
34
- /**
35
- * Server configuration options
36
- */
37
- export interface ToolServerConfig {
38
- /**
39
- * Server title for HTML pages (default: 'Tools Server')
40
- */
41
- title?: string;
42
- /**
43
- * API prefix (default: '/api')
44
- */
45
- prefix?: string;
46
- /**
47
- * Tool collections to expose
48
- */
49
- tools?: ToolCollection[];
50
- /**
51
- * Interaction collections to expose
52
- */
53
- interactions?: InteractionCollection[];
54
- /**
55
- * Skill collections to expose
56
- */
57
- skills?: SkillCollection[];
58
- /**
59
- * MCP providers to expose
60
- */
61
- mcpProviders?: MCPProviderConfig[];
62
- /**
63
- * Disable HTML pages (default: false)
64
- */
65
- disableHtml?: boolean;
66
- }
67
27
 
68
28
  /**
69
29
  * Create a Hono server for tools, interactions, and skills.
@@ -82,13 +42,12 @@ export interface ToolServerConfig {
82
42
  */
83
43
  export function createToolServer(config: ToolServerConfig): Hono {
84
44
  const {
85
- title = 'Tools Server',
86
- prefix = '/api',
87
- tools = [],
88
- interactions = [],
89
- skills = [],
90
- mcpProviders = [],
91
- disableHtml = false,
45
+ prefix = '/api',
46
+ tools = [],
47
+ interactions = [],
48
+ skills = [],
49
+ mcpProviders = [],
50
+ disableHtml = false,
92
51
  } = config;
93
52
 
94
53
  const app = new Hono();
@@ -96,33 +55,31 @@ export function createToolServer(config: ToolServerConfig): Hono {
96
55
  // Add CORS middleware globally
97
56
  app.use('*', cors({ origin: '*', allowMethods: ['GET', 'POST', 'OPTIONS'] }));
98
57
 
99
- // HTML pages (unless disabled)
100
- if (!disableHtml) {
101
- // Index page
102
- app.get('/', (c) => {
103
- return c.html(indexPage(tools, skills, interactions, mcpProviders, title));
104
- });
105
-
106
- // Tool collection pages
107
- for (const coll of tools) {
108
- app.get(`/tools/${coll.name}`, (c) => {
109
- return c.html(toolCollectionPage(coll));
110
- });
111
- }
112
-
113
- // Skill collection pages
114
- for (const coll of skills) {
115
- app.get(`/skills/${coll.name}`, (c) => {
116
- return c.html(skillCollectionPage(coll));
117
- });
58
+ // Middleware to parse and validate body, store on context for reuse
59
+ app.use('*', async (c, next) => {
60
+ const ctx = c as unknown as ToolContext;
61
+ if (c.req.method === 'POST') {
62
+ try {
63
+ const text = await c.req.text();
64
+ const body = JSON.parse(text);
65
+ const result = ToolExecutionPayloadSchema.safeParse(body);
66
+ if (result.success) {
67
+ ctx.payload = result.data as ToolExecutionPayload<any>;
68
+ ctx.toolUseId = result.data.tool_use.id;
69
+ ctx.toolName = result.data.tool_use.tool_name;
70
+ }
71
+ // If validation fails, still store raw body for error reporting
72
+ // but don't set payload - handlers will return validation error
73
+ } catch {
74
+ // Ignore parsing errors - body might not be JSON
75
+ }
118
76
  }
77
+ await next();
78
+ });
119
79
 
120
- // Interaction collection pages
121
- for (const coll of interactions) {
122
- app.get(`/interactions/${coll.name}`, (c) => {
123
- return c.html(interactionCollectionPage(coll));
124
- });
125
- }
80
+ // HTML pages (unless disabled)
81
+ if (!disableHtml) {
82
+ createSiteRoute(app, '', config);
126
83
  }
127
84
 
128
85
  // Add base API route
@@ -143,292 +100,56 @@ export function createToolServer(config: ToolServerConfig): Hono {
143
100
  });
144
101
  });
145
102
 
146
- // ================== Aggregate Endpoints ==================
147
- // These must be registered BEFORE collection-specific routes
148
-
149
- // GET /api/skills - Returns all skills from all collections
150
- app.get(`${prefix}/skills`, (c) => {
151
- const url = new URL(c.req.url);
152
- const allSkills: ToolDefinition[] = [];
153
-
154
- for (const coll of skills) {
155
- allSkills.push(...coll.getToolDefinitions());
156
- }
157
-
158
- return c.json({
159
- src: `${url.origin}${url.pathname}`,
160
- title: 'All Skills',
161
- description: 'All available skills across all collections',
162
- tools: allSkills,
163
- collections: skills.map(s => ({
164
- name: s.name,
165
- title: s.title,
166
- description: s.description,
167
- })),
168
- } satisfies ToolCollectionDefinition & { collections: any[] });
169
- });
170
-
171
- // GET /api/tools - Returns all tools from all collections
172
- app.get(`${prefix}/tools`, (c) => {
173
- const url = new URL(c.req.url);
174
- const allTools: ToolDefinition[] = [];
175
-
176
- for (const coll of tools) {
177
- allTools.push(...coll.getToolDefinitions());
178
- }
179
-
180
- return c.json({
181
- src: `${url.origin}${url.pathname}`,
182
- title: 'All Tools',
183
- description: 'All available tools across all collections',
184
- tools: allTools,
185
- collections: tools.map(t => ({
186
- name: t.name,
187
- title: t.title,
188
- description: t.description,
189
- })),
190
- } satisfies ToolCollectionDefinition & { collections: any[] });
191
- });
192
-
193
- // GET /api/interactions - Returns all interactions from all collections
194
- app.get(`${prefix}/interactions`, (c) => {
195
- const allInteractions: CatalogInteractionRef[] = [];
196
-
197
- for (const coll of interactions) {
198
- for (const inter of coll.interactions) {
199
- allInteractions.push({
200
- type: "app",
201
- id: inter.name,
202
- name: inter.name,
203
- title: inter.title || inter.name,
204
- description: inter.description,
205
- tags: inter.tags || [],
206
- });
207
- }
208
- }
209
-
210
- return c.json({
211
- title: 'All Interactions',
212
- description: 'All available interactions across all collections',
213
- interactions: allInteractions,
214
- collections: interactions.map(i => ({
215
- name: i.name,
216
- title: i.title,
217
- description: i.description,
218
- })),
219
- });
220
- });
221
-
222
- // Create tool collection endpoints
223
- for (const coll of tools) {
224
- app.route(`${prefix}/tools/${coll.name}`, createToolEndpoints(coll));
225
- // Also expose at root for backwards compatibility
226
- app.route(`${prefix}/${coll.name}`, createToolEndpoints(coll));
227
- }
228
103
 
229
- // Create interaction collection endpoints
230
- for (const coll of interactions) {
231
- app.route(`${prefix}/interactions/${coll.name}`, createInteractionEndpoints(coll));
232
- }
104
+ createPackageRoute(app, `${prefix}/package`, config);
105
+ createToolsRoute(app, `${prefix}/tools`, config);
106
+ createSkillsRoute(app, `${prefix}/skills`, config);
107
+ createWidgetsRoute(app, `${prefix}/widgets`, config);
108
+ createInteractionsRoute(app, `${prefix}/interactions`, config);
109
+ createContentTypesRoute(app, `${prefix}/types`, config);
110
+ createMcpRoute(app, `${prefix}/mcp`, config);
233
111
 
234
- // Create skill collection endpoints (exposed as tools)
235
- for (const coll of skills) {
236
- app.route(`${prefix}/skills/${coll.name}`, createSkillEndpoints(coll));
237
- }
238
112
 
239
- // Create MCP provider endpoints
240
- if (mcpProviders.length > 0) {
241
- app.route(`${prefix}/mcp`, createMCPEndpoints(mcpProviders));
242
- }
243
-
244
- // Global error handler
113
+ // Global error handler - returns ToolExecutionResponseError format
245
114
  app.onError((err, c) => {
246
- if (err instanceof HTTPException) {
247
- return c.json({ error: err.message }, err.status);
248
- }
249
- console.error('Uncaught Error:', err);
250
- return c.json({ error: 'Internal Server Error' }, 500);
251
- });
252
-
253
- return app;
254
- }
255
-
256
- // ================== Tool Endpoints ==================
257
-
258
- function createToolEndpoints(coll: ToolCollection): Hono {
259
- const endpoint = new Hono();
260
-
261
- endpoint.post('/', (c: Context) => {
262
- return coll.execute(c);
263
- });
264
-
265
- endpoint.get('/', (c) => {
266
- const importSourceUrl = c.req.query('import') != null;
267
- const url = new URL(c.req.url);
268
- return c.json({
269
- src: importSourceUrl
270
- ? `${url.origin}/libs/vertesia-tools-${coll.name}.js`
271
- : `${url.origin}${url.pathname}`,
272
- title: coll.title || coll.name,
273
- description: coll.description || '',
274
- tools: coll.getToolDefinitions()
275
- } satisfies ToolCollectionDefinition);
276
- });
115
+ const ctx = c as unknown as ToolContext;
116
+ const status = err instanceof HTTPException ? err.status : 500;
117
+ const errorMessage = err instanceof HTTPException ? err.message : 'Internal Server Error';
277
118
 
278
- return endpoint;
279
- }
280
-
281
- // ================== Interaction Endpoints ==================
282
-
283
- function createInteractionEndpoints(coll: InteractionCollection): Hono {
284
- const endpoint = new Hono();
285
-
286
- endpoint.get('/', (c: Context) => {
287
- return c.json(coll.interactions.map(inter => ({
288
- type: "app",
289
- id: inter.name,
290
- name: inter.name,
291
- title: inter.title || inter.name,
292
- description: inter.description,
293
- tags: inter.tags || [],
294
- } satisfies CatalogInteractionRef)));
295
- });
296
-
297
- endpoint.get('/:name', async (c: Context) => {
298
- await authorize(c);
299
- const name = c.req.param('name');
300
- const inter = coll.getInteractionByName(name);
301
- if (!inter) {
302
- throw new HTTPException(404, {
303
- message: "No interaction found with name: " + name
304
- });
119
+ if (!(err instanceof HTTPException)) {
120
+ console.error('Uncaught Error:', err);
305
121
  }
306
- return c.json(inter);
307
- });
308
-
309
- return endpoint;
310
- }
311
-
312
- // ================== Skill Endpoints ==================
313
-
314
- function createSkillEndpoints(coll: SkillCollection): Hono {
315
- const endpoint = new Hono();
316
122
 
317
- // List skills as tool definitions (tool collection format)
318
- // This allows skills to be used exactly like tools
319
- endpoint.get('/', (c: Context) => {
320
- const url = new URL(c.req.url);
321
123
  return c.json({
322
- src: `${url.origin}${url.pathname}`,
323
- title: coll.title || coll.name,
324
- description: coll.description || '',
325
- tools: coll.getToolDefinitions()
326
- } satisfies ToolCollectionDefinition);
124
+ tool_use_id: ctx.toolUseId || 'unknown',
125
+ status,
126
+ error: errorMessage,
127
+ data: ctx.toolName ? { tool_name: ctx.toolName } : undefined,
128
+ }, status);
327
129
  });
328
130
 
329
- // Get scripts for a specific skill
330
- // Returns all scripts bundled with the skill
331
- endpoint.get('/:name/scripts', (c: Context) => {
332
- const name = c.req.param('name');
333
- const skillName = name.startsWith('skill_') ? name.slice(6) : name;
334
- const skill = coll.getSkill(skillName);
335
- if (!skill) {
336
- throw new HTTPException(404, {
337
- message: `Skill not found: ${skillName}`
338
- });
339
- }
131
+ // Not found handler - returns ToolExecutionResponseError format
132
+ app.notFound((c) => {
133
+ const ctx = c as unknown as ToolContext;
340
134
  return c.json({
341
- skill_name: skill.name,
342
- scripts: skill.scripts || []
343
- });
344
- });
345
-
346
- // Get a specific script file
347
- endpoint.get('/:name/scripts/:filename', (c: Context) => {
348
- const name = c.req.param('name');
349
- const filename = c.req.param('filename');
350
- const skillName = name.startsWith('skill_') ? name.slice(6) : name;
351
- const skill = coll.getSkill(skillName);
352
- if (!skill) {
353
- throw new HTTPException(404, {
354
- message: `Skill not found: ${skillName}`
355
- });
356
- }
357
- const script = skill.scripts?.find(s => s.name === filename);
358
- if (!script) {
359
- throw new HTTPException(404, {
360
- message: `Script not found: ${filename}`
361
- });
362
- }
363
- // Return as plain text with appropriate content type
364
- const contentType = filename.endsWith('.py') ? 'text/x-python'
365
- : filename.endsWith('.sh') ? 'text/x-shellscript'
366
- : filename.endsWith('.js') ? 'text/javascript'
367
- : 'text/plain';
368
- return c.text(script.content, 200, { 'Content-Type': contentType });
369
- });
370
-
371
- // Get a specific skill by name
372
- endpoint.get('/:name', (c: Context) => {
373
- const name = c.req.param('name');
374
- // Handle both "skill_name" and "name" formats
375
- const skillName = name.startsWith('skill_') ? name.slice(6) : name;
376
- const skill = coll.getSkill(skillName);
377
- if (!skill) {
378
- throw new HTTPException(404, {
379
- message: `Skill not found: ${skillName}`
380
- });
381
- }
382
- return c.json(skill satisfies SkillDefinition);
383
- });
384
-
385
- // Execute skill (standard tool execution format)
386
- endpoint.post('/', (c: Context) => {
387
- return coll.execute(c);
135
+ tool_use_id: ctx.toolUseId || 'unknown',
136
+ status: 404,
137
+ error: `Not found: ${c.req.method} ${c.req.path}`,
138
+ data: ctx.toolName ? { tool_name: ctx.toolName } : undefined,
139
+ }, 404);
388
140
  });
389
141
 
390
- return endpoint;
142
+ return app;
391
143
  }
392
144
 
393
- // ================== MCP Endpoints ==================
394
-
395
- function createMCPEndpoints(providers: MCPProviderConfig[]): Hono {
396
- const endpoint = new Hono();
397
-
398
- for (const p of providers) {
399
- endpoint.post(`/${p.name}`, async (c: Context) => {
400
- const session = await authorize(c);
401
- const config = await readJsonBody(c);
402
- const info = await p.createMCPConnection(session, config);
403
- return c.json(info);
404
- });
405
-
406
- endpoint.get(`/${p.name}`, (c: Context) => c.json({
407
- name: p.name,
408
- description: p.description,
409
- }));
410
- }
411
145
 
412
- return endpoint;
413
- }
414
-
415
- async function readJsonBody(ctx: Context): Promise<Record<string, any>> {
416
- try {
417
- const text = await ctx.req.text();
418
- const jsonContent = text?.trim() || '';
419
- if (!jsonContent) return {};
420
- return JSON.parse(jsonContent) as Record<string, any>;
421
- } catch (err: any) {
422
- throw new HTTPException(400, {
423
- message: "Failed to parse JSON body: " + err.message
424
- });
425
- }
426
- }
427
146
 
428
147
  // ================== Server Utilities ==================
429
148
 
430
149
  /**
431
- * Simple development server with static file handling
150
+ * Simple development server with static fimesale handling
151
+ *
152
+ * @deprecated Use tools server template
432
153
  */
433
154
  export function createDevServer(config: ToolServerConfig & {
434
155
  staticHandler?: (c: Context, next: () => Promise<void>) => Promise<Response | void>;
@@ -396,6 +396,43 @@ hr {
396
396
  font-family: ui-monospace, monospace;
397
397
  }
398
398
 
399
+ .endpoint-box {
400
+ display: flex;
401
+ align-items: center;
402
+ gap: 0.75rem;
403
+ background: #f3f4f6;
404
+ padding: 0.75rem 1rem;
405
+ border-radius: 8px;
406
+ margin-top: 0.5rem;
407
+ }
408
+
409
+ .endpoint-box code {
410
+ flex: 1;
411
+ font-family: ui-monospace, monospace;
412
+ font-size: 0.875rem;
413
+ color: #1f2937;
414
+ }
415
+
416
+ .copy-btn {
417
+ background: #e5e7eb;
418
+ border: none;
419
+ padding: 0.5rem;
420
+ border-radius: 6px;
421
+ cursor: pointer;
422
+ color: #6b7280;
423
+ transition: all 0.15s;
424
+ }
425
+
426
+ .copy-btn:hover {
427
+ background: #d1d5db;
428
+ color: #374151;
429
+ }
430
+
431
+ .copy-btn svg {
432
+ width: 16px;
433
+ height: 16px;
434
+ }
435
+
399
436
  .badge {
400
437
  display: inline-block;
401
438
  font-size: 0.75rem;
@@ -583,6 +620,24 @@ hr {
583
620
  background: rgba(15, 23, 42, 0.95);
584
621
  }
585
622
 
623
+ .endpoint-box {
624
+ background: rgba(31, 41, 55, 0.95);
625
+ }
626
+
627
+ .endpoint-box code {
628
+ color: #e5e7eb;
629
+ }
630
+
631
+ .copy-btn {
632
+ background: rgba(55, 65, 81, 0.95);
633
+ color: #e5e7eb;
634
+ }
635
+
636
+ .copy-btn:hover {
637
+ background: rgba(75, 85, 99, 0.98);
638
+ color: #f9fafb;
639
+ }
640
+
586
641
  .badge {
587
642
  background: rgba(15, 23, 42, 0.96);
588
643
  color: #e5e7eb;
@@ -613,5 +668,21 @@ hr {
613
668
  .logo-dark {
614
669
  display: block;
615
670
  }
671
+
672
+ .plugin-link-primary:hover {
673
+ background: #2563eb !important;
674
+ }
675
+
676
+ .plugin-link-secondary:hover {
677
+ background: #059669 !important;
678
+ }
679
+ }
680
+
681
+ .plugin-link-primary:hover {
682
+ background: #2563eb;
683
+ }
684
+
685
+ .plugin-link-secondary:hover {
686
+ background: #059669;
616
687
  }
617
688
  `;