@vertesia/tools-sdk 1.0.0-dev.20260305.083323Z → 1.0.0-dev.20260331.091034Z

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 (68) hide show
  1. package/lib/cjs/ActivityCollection.js +93 -0
  2. package/lib/cjs/ActivityCollection.js.map +1 -0
  3. package/lib/cjs/auth.js +3 -3
  4. package/lib/cjs/auth.js.map +1 -1
  5. package/lib/cjs/index.js +1 -0
  6. package/lib/cjs/index.js.map +1 -1
  7. package/lib/cjs/server/activities.js +103 -0
  8. package/lib/cjs/server/activities.js.map +1 -0
  9. package/lib/cjs/server/app-package.js +14 -1
  10. package/lib/cjs/server/app-package.js.map +1 -1
  11. package/lib/cjs/server/interactions.js +21 -9
  12. package/lib/cjs/server/interactions.js.map +1 -1
  13. package/lib/cjs/server/site.js +7 -1
  14. package/lib/cjs/server/site.js.map +1 -1
  15. package/lib/cjs/server/skills.js +10 -0
  16. package/lib/cjs/server/skills.js.map +1 -1
  17. package/lib/cjs/server.js +4 -1
  18. package/lib/cjs/server.js.map +1 -1
  19. package/lib/cjs/site/templates.js +138 -3
  20. package/lib/cjs/site/templates.js.map +1 -1
  21. package/lib/esm/ActivityCollection.js +89 -0
  22. package/lib/esm/ActivityCollection.js.map +1 -0
  23. package/lib/esm/auth.js +3 -3
  24. package/lib/esm/auth.js.map +1 -1
  25. package/lib/esm/index.js +1 -0
  26. package/lib/esm/index.js.map +1 -1
  27. package/lib/esm/server/activities.js +100 -0
  28. package/lib/esm/server/activities.js.map +1 -0
  29. package/lib/esm/server/app-package.js +14 -1
  30. package/lib/esm/server/app-package.js.map +1 -1
  31. package/lib/esm/server/interactions.js +21 -9
  32. package/lib/esm/server/interactions.js.map +1 -1
  33. package/lib/esm/server/site.js +8 -2
  34. package/lib/esm/server/site.js.map +1 -1
  35. package/lib/esm/server/skills.js +10 -0
  36. package/lib/esm/server/skills.js.map +1 -1
  37. package/lib/esm/server.js +4 -1
  38. package/lib/esm/server.js.map +1 -1
  39. package/lib/esm/site/templates.js +136 -3
  40. package/lib/esm/site/templates.js.map +1 -1
  41. package/lib/types/ActivityCollection.d.ts +55 -0
  42. package/lib/types/ActivityCollection.d.ts.map +1 -0
  43. package/lib/types/index.d.ts +1 -0
  44. package/lib/types/index.d.ts.map +1 -1
  45. package/lib/types/server/activities.d.ts +4 -0
  46. package/lib/types/server/activities.d.ts.map +1 -0
  47. package/lib/types/server/app-package.d.ts.map +1 -1
  48. package/lib/types/server/interactions.d.ts.map +1 -1
  49. package/lib/types/server/site.d.ts.map +1 -1
  50. package/lib/types/server/types.d.ts +5 -0
  51. package/lib/types/server/types.d.ts.map +1 -1
  52. package/lib/types/server.d.ts.map +1 -1
  53. package/lib/types/site/templates.d.ts +10 -0
  54. package/lib/types/site/templates.d.ts.map +1 -1
  55. package/package.json +5 -5
  56. package/src/ActivityCollection.test.ts +161 -0
  57. package/src/ActivityCollection.ts +136 -0
  58. package/src/auth.ts +3 -3
  59. package/src/index.ts +1 -0
  60. package/src/server/activities.test.ts +165 -0
  61. package/src/server/activities.ts +114 -0
  62. package/src/server/app-package.ts +15 -2
  63. package/src/server/interactions.ts +22 -11
  64. package/src/server/site.ts +9 -0
  65. package/src/server/skills.ts +10 -0
  66. package/src/server/types.ts +5 -0
  67. package/src/server.ts +4 -0
  68. package/src/site/templates.ts +143 -2
@@ -50,16 +50,22 @@ export function createInteractionsRoute(app: Hono, basePath: string, config: Too
50
50
  const name = c.req.param('name');
51
51
 
52
52
  const parts = name.split(':');
53
- if (parts.length !== 2) {
54
- throw new HTTPException(400, {
55
- message: "Invalid interaction name. Expected format 'collection:interaction'"
56
- });
57
- }
58
- const collName = parts[0];
59
- const interName = parts[1];
60
- const inter = interactions.find(t => t.name === collName)?.getInteractionByName(interName);
61
- if (inter) {
62
- return c.json({ ...inter, id: collName + ":" + interName });
53
+ if (parts.length === 2) {
54
+ // Explicit collection:interaction format
55
+ const collName = parts[0];
56
+ const interName = parts[1];
57
+ const inter = interactions.find(t => t.name === collName)?.getInteractionByName(interName);
58
+ if (inter) {
59
+ return c.json({ ...inter, id: collName + ":" + interName });
60
+ }
61
+ } else {
62
+ // Search all collections for the interaction by name
63
+ for (const coll of interactions) {
64
+ const inter = coll.getInteractionByName(name);
65
+ if (inter) {
66
+ return c.json({ ...inter, id: coll.name + ":" + name });
67
+ }
68
+ }
63
69
  }
64
70
 
65
71
  throw new HTTPException(404, {
@@ -89,6 +95,11 @@ function createInteractionEndpoints(coll: InteractionCollection): Hono {
89
95
  endpoint.get('/:name', async (c: Context) => {
90
96
  await authorize(c);
91
97
  const name = c.req.param('name');
98
+ if (!name) {
99
+ throw new HTTPException(400, {
100
+ message: 'Interaction name is required'
101
+ });
102
+ }
92
103
  const inter = coll.getInteractionByName(name);
93
104
  if (!inter) {
94
105
  throw new HTTPException(404, {
@@ -102,4 +113,4 @@ function createInteractionEndpoints(coll: InteractionCollection): Hono {
102
113
  });
103
114
 
104
115
  return endpoint;
105
- }
116
+ }
@@ -1,5 +1,6 @@
1
1
  import { Hono } from "hono";
2
2
  import {
3
+ activityCollectionPage,
3
4
  contentTypeCollectionPage,
4
5
  indexPage,
5
6
  interactionCollectionPage,
@@ -13,6 +14,7 @@ import { ToolServerConfig } from "./types.js";
13
14
  export function createSiteRoute(app: Hono, basePath: string, config: ToolServerConfig) {
14
15
  const {
15
16
  tools = [],
17
+ activities = [],
16
18
  interactions = [],
17
19
  types = [],
18
20
  skills = [],
@@ -31,6 +33,13 @@ export function createSiteRoute(app: Hono, basePath: string, config: ToolServerC
31
33
  });
32
34
  }
33
35
 
36
+ // Activity collection pages
37
+ for (const coll of activities) {
38
+ app.get(`${basePath}/activities/${coll.name}`, (c) => {
39
+ return c.html(activityCollectionPage(coll));
40
+ });
41
+ }
42
+
34
43
  // Skill collection pages
35
44
  for (const coll of skills) {
36
45
  app.get(`${basePath}/skills/${coll.name}`, (c) => {
@@ -95,6 +95,11 @@ function createSkillEndpoints(coll: SkillCollection): Hono {
95
95
  // Returns all scripts bundled with the skill
96
96
  endpoint.get('/:name/scripts', (c: Context) => {
97
97
  const name = c.req.param('name');
98
+ if (!name) {
99
+ throw new HTTPException(400, {
100
+ message: 'Skill name is required'
101
+ });
102
+ }
98
103
  const skillName = name.startsWith('learn_') ? name.slice(6) : name;
99
104
  const skill = coll.getSkill(skillName);
100
105
  if (!skill) {
@@ -113,6 +118,11 @@ function createSkillEndpoints(coll: SkillCollection): Hono {
113
118
  // Get a specific skill by name
114
119
  endpoint.get('/:name', (c: Context) => {
115
120
  const name = c.req.param('name');
121
+ if (!name) {
122
+ throw new HTTPException(400, {
123
+ message: 'Skill name is required'
124
+ });
125
+ }
116
126
  // Handle both "learn_name" and "name" formats
117
127
  const skillName = name.startsWith('learn_') ? name.slice(6) : name;
118
128
  const skill = coll.getSkill(skillName);
@@ -1,4 +1,5 @@
1
1
  import { Context } from "hono";
2
+ import { ActivityCollection } from "../ActivityCollection.js";
2
3
  import { InteractionCollection } from "../InteractionCollection.js";
3
4
  import { SkillCollection } from "../SkillCollection.js";
4
5
  import { RenderingTemplateCollection } from "../RenderingTemplateCollection.js";
@@ -49,6 +50,10 @@ export interface ToolServerConfig {
49
50
  * Tool collections to expose
50
51
  */
51
52
  tools?: ToolCollection[];
53
+ /**
54
+ * Activity collections to expose for DSL workflows
55
+ */
56
+ activities?: ActivityCollection[];
52
57
  /**
53
58
  * Interaction collections to expose
54
59
  */
package/src/server.ts CHANGED
@@ -2,6 +2,7 @@ import { Context, Hono } from "hono";
2
2
  import { cors } from "hono/cors";
3
3
  import { HTTPException } from "hono/http-exception";
4
4
  import { z } from "zod";
5
+ import { createActivitiesRoute } from "./server/activities.js";
5
6
  import { createInteractionsRoute } from "./server/interactions.js";
6
7
  import { createMcpRoute } from "./server/mcp.js";
7
8
  import { createSiteRoute } from "./server/site.js";
@@ -48,6 +49,7 @@ export function createToolServer(config: ToolServerConfig): Hono {
48
49
  interactions = [],
49
50
  skills = [],
50
51
  templates = [],
52
+ activities = [],
51
53
  mcpProviders = [],
52
54
  disableHtml = false,
53
55
  } = config;
@@ -98,6 +100,7 @@ export function createToolServer(config: ToolServerConfig): Hono {
98
100
  tools: allToolEndpoints,
99
101
  interactions: interactions.map(col => `${prefix}/interactions/${col.name}`),
100
102
  templates: templates.map(col => `${prefix}/templates/${col.name}`),
103
+ activities: activities.map(col => `${prefix}/activities/${col.name}`),
101
104
  mcp: mcpProviders.map(p => `${prefix}/mcp/${p.name}`),
102
105
  }
103
106
  });
@@ -108,6 +111,7 @@ export function createToolServer(config: ToolServerConfig): Hono {
108
111
  createToolsRoute(app, `${prefix}/tools`, config);
109
112
  createSkillsRoute(app, `${prefix}/skills`, config);
110
113
  createWidgetsRoute(app, `${prefix}/widgets`, config);
114
+ createActivitiesRoute(app, `${prefix}/activities`, config);
111
115
  createInteractionsRoute(app, `${prefix}/interactions`, config);
112
116
  createTemplatesRoute(app, `${prefix}/templates`, config);
113
117
  createContentTypesRoute(app, `${prefix}/types`, config);
@@ -1,3 +1,5 @@
1
+ import type { RemoteActivityDefinition } from "@vertesia/common";
2
+ import type { ActivityCollection } from "../ActivityCollection.js";
1
3
  import type { InteractionCollection } from "../InteractionCollection.js";
2
4
  import { ToolServerConfig } from "../server/types.js";
3
5
  import type { SkillCollection } from "../SkillCollection.js";
@@ -35,6 +37,11 @@ const templateIcon = /*html*/`
35
37
  <polyline points="10 9 9 9 8 9"/>
36
38
  </svg>`;
37
39
 
40
+ const activityIcon = /*html*/`
41
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
42
+ <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
43
+ </svg>`;
44
+
38
45
  /**
39
46
  * Extended styles for detail pages
40
47
  */
@@ -822,6 +829,7 @@ export function indexPage(
822
829
  const {
823
830
  title = 'Tools Server',
824
831
  tools = [],
832
+ activities = [],
825
833
  interactions = [],
826
834
  skills = [],
827
835
  templates = [],
@@ -851,10 +859,11 @@ export function indexPage(
851
859
  <p class="hero-eyebrow">Tools Server</p>
852
860
  <h1 class="hero-title">${title}</h1>
853
861
  <p class="hero-tagline">
854
- Discover the tools, skills, interactions, and content types exposed by this server.
862
+ Discover the tools, activities, skills, interactions, and content types exposed by this server.
855
863
  </p>
856
864
  <div class="hero-summary">
857
865
  ${tools.length ? /*html*/`<span><dot></dot> ${tools.length} tool collection${tools.length !== 1 ? 's' : ''}</span>` : ''}
866
+ ${activities.length ? /*html*/`<span><dot></dot> ${activities.length} activity collection${activities.length !== 1 ? 's' : ''}</span>` : ''}
858
867
  ${skills.length ? /*html*/`<span><dot></dot> ${skills.length} skill collection${skills.length !== 1 ? 's' : ''}</span>` : ''}
859
868
  ${interactions.length ? /*html*/`<span><dot></dot> ${interactions.length} interaction collection${interactions.length !== 1 ? 's' : ''}</span>` : ''}
860
869
  ${types.length ? /*html*/`<span><dot></dot> ${types.length} content type collection${types.length !== 1 ? 's' : ''}</span>` : ''}
@@ -886,7 +895,7 @@ export function indexPage(
886
895
  type="search"
887
896
  id="collection-search"
888
897
  class="search-input"
889
- placeholder="Search tools, skills, interactions, types, templates..."
898
+ placeholder="Search tools, activities, skills, interactions, types, templates..."
890
899
  aria-label="Search collections"
891
900
  autocomplete="off"
892
901
  />
@@ -910,6 +919,22 @@ export function indexPage(
910
919
  </section>
911
920
  ` : ''}
912
921
 
922
+ ${activities.length > 0 ? /*html*/`
923
+ <section data-section="activities">
924
+ <hr>
925
+ <div class="section-header">
926
+ <h2>Activity Collections</h2>
927
+ <p class="section-subtitle">Remote activities for DSL workflows, invoked via HTTP.</p>
928
+ </div>
929
+ <div class="card-grid">
930
+ ${activities.map((a: ActivityCollection) => {
931
+ const count = a.getActivityDefinitions().length;
932
+ return collectionCard(a, 'activities', `${count} activit${count !== 1 ? 'ies' : 'y'}`);
933
+ }).join('')}
934
+ </div>
935
+ </section>
936
+ ` : ''}
937
+
913
938
  ${skills.length > 0 ? /*html*/`
914
939
  <section data-section="skills">
915
940
  <hr>
@@ -1227,3 +1252,119 @@ export function contentTypeCollectionPage(collection: ContentTypesCollection): s
1227
1252
  </body>
1228
1253
  </html>`;
1229
1254
  }
1255
+
1256
+ /**
1257
+ * Render a detailed activity card
1258
+ */
1259
+ export function activityDetailCard(activity: RemoteActivityDefinition, collectionName: string): string {
1260
+ const schema = activity.input_schema;
1261
+ const properties = schema?.properties as Record<string, unknown> | undefined;
1262
+ const required = (schema as Record<string, unknown>)?.required as string[] | undefined;
1263
+
1264
+ return /*html*/`
1265
+ <div class="detail-card">
1266
+ <div class="detail-header">
1267
+ <div>
1268
+ <h3 class="detail-title">${activity.name}</h3>
1269
+ <p class="detail-desc">${activity.description || 'No description'}</p>
1270
+ </div>
1271
+ <div class="detail-badges">
1272
+ <span class="badge" style="background: #ec4899; color: white;">Activity</span>
1273
+ </div>
1274
+ </div>
1275
+ <div class="detail-body">
1276
+ <div class="detail-section">
1277
+ <h4 class="detail-section-title">Endpoint</h4>
1278
+ <div class="endpoint-box">
1279
+ <code>POST ${activity.url || `/api/activities/${collectionName}`}</code>
1280
+ <button class="copy-btn" onclick="navigator.clipboard.writeText('${activity.url || `/api/activities/${collectionName}`}')" title="Copy">
1281
+ ${copyIcon}
1282
+ </button>
1283
+ </div>
1284
+ </div>
1285
+
1286
+ ${schema ? /*html*/`
1287
+ <div class="detail-section">
1288
+ <h4 class="detail-section-title">Input Schema</h4>
1289
+ ${properties ? /*html*/`
1290
+ <div class="info-grid" style="margin-bottom: 1rem;">
1291
+ ${Object.entries(properties).map(([key, value]) => {
1292
+ const prop = value as Record<string, unknown>;
1293
+ const isRequired = required?.includes(key);
1294
+ return /*html*/`
1295
+ <div class="info-item">
1296
+ <div class="info-label">${key}${isRequired ? ' *' : ''}</div>
1297
+ <div class="info-value">
1298
+ <code>${prop.type || 'any'}</code>
1299
+ ${prop.description ? `<br><span style="color: #6b7280; font-size: 0.85rem;">${prop.description}</span>` : ''}
1300
+ </div>
1301
+ </div>`;
1302
+ }).join('')}
1303
+ </div>
1304
+ ` : ''}
1305
+ <details>
1306
+ <summary style="cursor: pointer; color: #6b7280; font-size: 0.85rem;">View full schema</summary>
1307
+ <div class="schema-block" style="margin-top: 0.75rem;">${highlightJson(schema)}</div>
1308
+ </details>
1309
+ </div>
1310
+ ` : /*html*/`
1311
+ <div class="detail-section">
1312
+ <div class="empty-state">No input schema defined</div>
1313
+ </div>
1314
+ `}
1315
+
1316
+ ${activity.output_schema ? /*html*/`
1317
+ <div class="detail-section">
1318
+ <h4 class="detail-section-title">Output Schema</h4>
1319
+ <details>
1320
+ <summary style="cursor: pointer; color: #6b7280; font-size: 0.85rem;">View output schema</summary>
1321
+ <div class="schema-block" style="margin-top: 0.75rem;">${highlightJson(activity.output_schema)}</div>
1322
+ </details>
1323
+ </div>
1324
+ ` : ''}
1325
+ </div>
1326
+ </div>`;
1327
+ }
1328
+
1329
+ /**
1330
+ * Render an activity collection detail page
1331
+ */
1332
+ export function activityCollectionPage(collection: ActivityCollection): string {
1333
+ const activitiesArray = collection.getActivityDefinitions();
1334
+ return /*html*/`
1335
+ <!DOCTYPE html>
1336
+ <html lang="en">
1337
+ <head>
1338
+ <meta charset="UTF-8">
1339
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1340
+ <title>${collection.title || collection.name} - Activities</title>
1341
+ <style>${detailStyles}</style>
1342
+ </head>
1343
+ <body>
1344
+ <nav class="nav">
1345
+ <a href="/">${backArrow} Back to all collections</a>
1346
+ </nav>
1347
+
1348
+ <div class="header">
1349
+ <div class="header-icon">${collection.icon || activityIcon}</div>
1350
+ <div>
1351
+ <h1>${collection.title || collection.name}</h1>
1352
+ <p style="color: #6b7280; margin: 0.25rem 0 0 0;">${collection.description || ''}</p>
1353
+ <div class="endpoint-box">
1354
+ <code>/api/activities/${collection.name}</code>
1355
+ <button class="copy-btn" onclick="navigator.clipboard.writeText(window.location.origin + '/api/activities/${collection.name}')" title="Copy endpoint URL">
1356
+ ${copyIcon}
1357
+ </button>
1358
+ </div>
1359
+ </div>
1360
+ </div>
1361
+
1362
+ <h2>${activitiesArray.length} Activit${activitiesArray.length !== 1 ? 'ies' : 'y'}</h2>
1363
+
1364
+ ${activitiesArray.length > 0 ?
1365
+ activitiesArray.map(activity => activityDetailCard(activity, collection.name)).join('') :
1366
+ '<div class="empty-state">No activities in this collection</div>'
1367
+ }
1368
+ </body>
1369
+ </html>`;
1370
+ }