@vertesia/tools-sdk 1.2.0 → 1.4.0-dev.20260615.042549Z
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.
- package/lib/{types/ActivityCollection.d.ts → ActivityCollection.d.ts} +7 -7
- package/lib/ActivityCollection.d.ts.map +1 -0
- package/lib/{esm/ActivityCollection.js → ActivityCollection.js} +6 -6
- package/lib/ActivityCollection.js.map +1 -0
- package/lib/{types/ContentTypesCollection.d.ts → ContentTypesCollection.d.ts} +2 -2
- package/lib/ContentTypesCollection.d.ts.map +1 -0
- package/lib/{esm/ContentTypesCollection.js → ContentTypesCollection.js} +3 -3
- package/lib/ContentTypesCollection.js.map +1 -0
- package/lib/{types/InteractionCollection.d.ts → InteractionCollection.d.ts} +2 -2
- package/lib/InteractionCollection.d.ts.map +1 -0
- package/lib/{esm/InteractionCollection.js → InteractionCollection.js} +3 -3
- package/lib/InteractionCollection.js.map +1 -0
- package/lib/{types/RenderingTemplateCollection.d.ts → RenderingTemplateCollection.d.ts} +1 -1
- package/lib/RenderingTemplateCollection.d.ts.map +1 -0
- package/lib/{esm/RenderingTemplateCollection.js → RenderingTemplateCollection.js} +3 -3
- package/lib/RenderingTemplateCollection.js.map +1 -0
- package/lib/{types/SkillCollection.d.ts → SkillCollection.d.ts} +5 -5
- package/lib/SkillCollection.d.ts.map +1 -0
- package/lib/{esm/SkillCollection.js → SkillCollection.js} +68 -51
- package/lib/SkillCollection.js.map +1 -0
- package/lib/{types/ToolCollection.d.ts → ToolCollection.d.ts} +10 -10
- package/lib/ToolCollection.d.ts.map +1 -0
- package/lib/{esm/ToolCollection.js → ToolCollection.js} +21 -18
- package/lib/ToolCollection.js.map +1 -0
- package/lib/ToolRegistry.d.ts +22 -0
- package/lib/ToolRegistry.d.ts.map +1 -0
- package/lib/{esm/ToolRegistry.js → ToolRegistry.js} +6 -36
- package/lib/ToolRegistry.js.map +1 -0
- package/lib/{types/auth.d.ts → auth.d.ts} +5 -5
- package/lib/auth.d.ts.map +1 -0
- package/lib/{esm/auth.js → auth.js} +23 -17
- package/lib/auth.js.map +1 -0
- package/lib/build/validate.d.ts.map +1 -0
- package/lib/build/validate.js.map +1 -0
- package/lib/copy-assets.d.ts.map +1 -0
- package/lib/{esm/copy-assets.js → copy-assets.js} +4 -7
- package/lib/copy-assets.js.map +1 -0
- package/lib/index.d.ts +16 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +15 -0
- package/lib/index.js.map +1 -0
- package/lib/server/activities.d.ts +4 -0
- package/lib/server/activities.d.ts.map +1 -0
- package/lib/{esm/server → server}/activities.js +12 -9
- package/lib/server/activities.js.map +1 -0
- package/lib/server/app-package.d.ts +12 -0
- package/lib/server/app-package.d.ts.map +1 -0
- package/lib/server/app-package.js +197 -0
- package/lib/server/app-package.js.map +1 -0
- package/lib/server/content-types.d.ts +4 -0
- package/lib/server/content-types.d.ts.map +1 -0
- package/lib/server/content-types.js +100 -0
- package/lib/server/content-types.js.map +1 -0
- package/lib/server/dashboards.d.ts +4 -0
- package/lib/server/dashboards.d.ts.map +1 -0
- package/lib/server/dashboards.js +25 -0
- package/lib/server/dashboards.js.map +1 -0
- package/lib/server/interactions.d.ts +4 -0
- package/lib/server/interactions.d.ts.map +1 -0
- package/lib/{esm/server → server}/interactions.js +16 -16
- package/lib/server/interactions.js.map +1 -0
- package/lib/server/mcp.d.ts +4 -0
- package/lib/server/mcp.d.ts.map +1 -0
- package/lib/{esm/server → server}/mcp.js +5 -4
- package/lib/server/mcp.js.map +1 -0
- package/lib/server/processes.d.ts +4 -0
- package/lib/server/processes.d.ts.map +1 -0
- package/lib/server/processes.js +26 -0
- package/lib/server/processes.js.map +1 -0
- package/lib/server/site.d.ts +4 -0
- package/lib/server/site.d.ts.map +1 -0
- package/lib/{esm/server → server}/site.js +4 -2
- package/lib/server/site.js.map +1 -0
- package/lib/server/skills.d.ts +4 -0
- package/lib/server/skills.d.ts.map +1 -0
- package/lib/{esm/server → server}/skills.js +14 -12
- package/lib/server/skills.js.map +1 -0
- package/lib/server/templates.d.ts +4 -0
- package/lib/server/templates.d.ts.map +1 -0
- package/lib/{esm/server → server}/templates.js +10 -10
- package/lib/server/templates.js.map +1 -0
- package/lib/server/tools.d.ts +4 -0
- package/lib/server/tools.d.ts.map +1 -0
- package/lib/{esm/server → server}/tools.js +7 -9
- package/lib/server/tools.js.map +1 -0
- package/lib/{types/server → server}/types.d.ts +21 -16
- package/lib/server/types.d.ts.map +1 -0
- package/lib/{cjs → server}/types.js.map +1 -1
- package/lib/server/widgets.d.ts +4 -0
- package/lib/server/widgets.d.ts.map +1 -0
- package/lib/server/widgets.js.map +1 -0
- package/lib/{types/server.d.ts → server.d.ts} +3 -3
- package/lib/server.d.ts.map +1 -0
- package/lib/{esm/server.js → server.js} +28 -22
- package/lib/server.js.map +1 -0
- package/lib/site/styles.d.ts.map +1 -0
- package/lib/{esm/site → site}/styles.js.map +1 -1
- package/lib/{types/site → site}/templates.d.ts +9 -9
- package/lib/site/templates.d.ts.map +1 -0
- package/lib/{esm/site → site}/templates.js +152 -98
- package/lib/site/templates.js.map +1 -0
- package/lib/{types/types.d.ts → types.d.ts} +29 -16
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js.map +1 -0
- package/lib/utils.d.ts.map +1 -0
- package/lib/{esm/utils.js → utils.js} +11 -5
- package/lib/utils.js.map +1 -0
- package/package.json +21 -23
- package/src/ActivityCollection.test.ts +60 -59
- package/src/ActivityCollection.ts +27 -20
- package/src/ContentTypesCollection.ts +6 -9
- package/src/InteractionCollection.ts +6 -8
- package/src/RenderingTemplateCollection.ts +49 -51
- package/src/SkillCollection.ts +91 -72
- package/src/ToolCollection.ts +52 -43
- package/src/ToolRegistry.ts +20 -50
- package/src/auth.ts +35 -27
- package/src/copy-assets.ts +5 -12
- package/src/index.ts +15 -15
- package/src/server/activities.test.ts +70 -67
- package/src/server/activities.ts +17 -13
- package/src/server/app-package.test.ts +140 -0
- package/src/server/app-package.ts +115 -52
- package/src/server/content-types.test.ts +64 -0
- package/src/server/content-types.ts +53 -25
- package/src/server/dashboards.ts +31 -0
- package/src/server/interactions.ts +29 -28
- package/src/server/mcp.ts +16 -16
- package/src/server/processes.ts +35 -0
- package/src/server/site.ts +7 -15
- package/src/server/skills.ts +19 -18
- package/src/server/templates.ts +82 -80
- package/src/server/tools.ts +12 -16
- package/src/server/types.ts +29 -20
- package/src/server/widgets.ts +5 -9
- package/src/server.ts +55 -47
- package/src/site/styles.ts +1 -1
- package/src/site/templates.ts +259 -157
- package/src/types.ts +55 -31
- package/src/utils.ts +11 -6
- package/lib/cjs/ActivityCollection.js +0 -93
- package/lib/cjs/ActivityCollection.js.map +0 -1
- package/lib/cjs/ContentTypesCollection.js +0 -43
- package/lib/cjs/ContentTypesCollection.js.map +0 -1
- package/lib/cjs/InteractionCollection.js +0 -43
- package/lib/cjs/InteractionCollection.js.map +0 -1
- package/lib/cjs/RenderingTemplateCollection.js +0 -43
- package/lib/cjs/RenderingTemplateCollection.js.map +0 -1
- package/lib/cjs/SkillCollection.js +0 -384
- package/lib/cjs/SkillCollection.js.map +0 -1
- package/lib/cjs/ToolCollection.js +0 -221
- package/lib/cjs/ToolCollection.js.map +0 -1
- package/lib/cjs/ToolRegistry.js +0 -95
- package/lib/cjs/ToolRegistry.js.map +0 -1
- package/lib/cjs/auth.js +0 -131
- package/lib/cjs/auth.js.map +0 -1
- package/lib/cjs/build/validate.js +0 -7
- package/lib/cjs/build/validate.js.map +0 -1
- package/lib/cjs/copy-assets.js +0 -84
- package/lib/cjs/copy-assets.js.map +0 -1
- package/lib/cjs/index.js +0 -34
- package/lib/cjs/index.js.map +0 -1
- package/lib/cjs/package.json +0 -3
- package/lib/cjs/server/activities.js +0 -103
- package/lib/cjs/server/activities.js.map +0 -1
- package/lib/cjs/server/app-package.js +0 -154
- package/lib/cjs/server/app-package.js.map +0 -1
- package/lib/cjs/server/content-types.js +0 -73
- package/lib/cjs/server/content-types.js.map +0 -1
- package/lib/cjs/server/interactions.js +0 -101
- package/lib/cjs/server/interactions.js.map +0 -1
- package/lib/cjs/server/mcp.js +0 -45
- package/lib/cjs/server/mcp.js.map +0 -1
- package/lib/cjs/server/site.js +0 -48
- package/lib/cjs/server/site.js.map +0 -1
- package/lib/cjs/server/skills.js +0 -124
- package/lib/cjs/server/skills.js.map +0 -1
- package/lib/cjs/server/templates.js +0 -67
- package/lib/cjs/server/templates.js.map +0 -1
- package/lib/cjs/server/tools.js +0 -87
- package/lib/cjs/server/tools.js.map +0 -1
- package/lib/cjs/server/types.js +0 -3
- package/lib/cjs/server/types.js.map +0 -1
- package/lib/cjs/server/widgets.js +0 -27
- package/lib/cjs/server/widgets.js.map +0 -1
- package/lib/cjs/server.js +0 -142
- package/lib/cjs/server.js.map +0 -1
- package/lib/cjs/site/styles.js +0 -692
- package/lib/cjs/site/styles.js.map +0 -1
- package/lib/cjs/site/templates.js +0 -1320
- package/lib/cjs/site/templates.js.map +0 -1
- package/lib/cjs/types.js +0 -3
- package/lib/cjs/utils.js +0 -44
- package/lib/cjs/utils.js.map +0 -1
- package/lib/esm/ActivityCollection.js.map +0 -1
- package/lib/esm/ContentTypesCollection.js.map +0 -1
- package/lib/esm/InteractionCollection.js.map +0 -1
- package/lib/esm/RenderingTemplateCollection.js.map +0 -1
- package/lib/esm/SkillCollection.js.map +0 -1
- package/lib/esm/ToolCollection.js.map +0 -1
- package/lib/esm/ToolRegistry.js.map +0 -1
- package/lib/esm/auth.js.map +0 -1
- package/lib/esm/build/validate.js.map +0 -1
- package/lib/esm/copy-assets.js.map +0 -1
- package/lib/esm/index.js +0 -14
- package/lib/esm/index.js.map +0 -1
- package/lib/esm/server/activities.js.map +0 -1
- package/lib/esm/server/app-package.js +0 -151
- package/lib/esm/server/app-package.js.map +0 -1
- package/lib/esm/server/content-types.js +0 -70
- package/lib/esm/server/content-types.js.map +0 -1
- package/lib/esm/server/interactions.js.map +0 -1
- package/lib/esm/server/mcp.js.map +0 -1
- package/lib/esm/server/site.js.map +0 -1
- package/lib/esm/server/skills.js.map +0 -1
- package/lib/esm/server/templates.js.map +0 -1
- package/lib/esm/server/tools.js.map +0 -1
- package/lib/esm/server/types.js.map +0 -1
- package/lib/esm/server/widgets.js.map +0 -1
- package/lib/esm/server.js.map +0 -1
- package/lib/esm/site/templates.js.map +0 -1
- package/lib/esm/types.js.map +0 -1
- package/lib/esm/utils.js.map +0 -1
- package/lib/types/ActivityCollection.d.ts.map +0 -1
- package/lib/types/ContentTypesCollection.d.ts.map +0 -1
- package/lib/types/InteractionCollection.d.ts.map +0 -1
- package/lib/types/RenderingTemplateCollection.d.ts.map +0 -1
- package/lib/types/SkillCollection.d.ts.map +0 -1
- package/lib/types/ToolCollection.d.ts.map +0 -1
- package/lib/types/ToolRegistry.d.ts +0 -22
- package/lib/types/ToolRegistry.d.ts.map +0 -1
- package/lib/types/auth.d.ts.map +0 -1
- package/lib/types/build/validate.d.ts.map +0 -1
- package/lib/types/copy-assets.d.ts.map +0 -1
- package/lib/types/index.d.ts +0 -14
- package/lib/types/index.d.ts.map +0 -1
- package/lib/types/server/activities.d.ts +0 -4
- package/lib/types/server/activities.d.ts.map +0 -1
- package/lib/types/server/app-package.d.ts +0 -4
- package/lib/types/server/app-package.d.ts.map +0 -1
- package/lib/types/server/content-types.d.ts +0 -4
- package/lib/types/server/content-types.d.ts.map +0 -1
- package/lib/types/server/interactions.d.ts +0 -4
- package/lib/types/server/interactions.d.ts.map +0 -1
- package/lib/types/server/mcp.d.ts +0 -4
- package/lib/types/server/mcp.d.ts.map +0 -1
- package/lib/types/server/site.d.ts +0 -4
- package/lib/types/server/site.d.ts.map +0 -1
- package/lib/types/server/skills.d.ts +0 -4
- package/lib/types/server/skills.d.ts.map +0 -1
- package/lib/types/server/templates.d.ts +0 -4
- package/lib/types/server/templates.d.ts.map +0 -1
- package/lib/types/server/tools.d.ts +0 -4
- package/lib/types/server/tools.d.ts.map +0 -1
- package/lib/types/server/types.d.ts.map +0 -1
- package/lib/types/server/widgets.d.ts +0 -4
- package/lib/types/server/widgets.d.ts.map +0 -1
- package/lib/types/server.d.ts.map +0 -1
- package/lib/types/site/styles.d.ts.map +0 -1
- package/lib/types/site/templates.d.ts.map +0 -1
- package/lib/types/types.d.ts.map +0 -1
- package/lib/types/utils.d.ts.map +0 -1
- /package/lib/{types/build → build}/validate.d.ts +0 -0
- /package/lib/{esm/build → build}/validate.js +0 -0
- /package/lib/{types/copy-assets.d.ts → copy-assets.d.ts} +0 -0
- /package/lib/{esm/server → server}/types.js +0 -0
- /package/lib/{esm/server → server}/widgets.js +0 -0
- /package/lib/{types/site → site}/styles.d.ts +0 -0
- /package/lib/{esm/site → site}/styles.js +0 -0
- /package/lib/{esm/types.js → types.js} +0 -0
- /package/lib/{types/utils.d.ts → utils.d.ts} +0 -0
package/src/ToolRegistry.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import { AgentToolDefinition } from
|
|
2
|
-
import { HTTPException } from
|
|
3
|
-
import { Tool, ToolExecutionContext, ToolExecutionPayload, ToolExecutionResult, ToolUseContext } from
|
|
4
|
-
|
|
1
|
+
import type { AgentToolDefinition } from '@vertesia/common';
|
|
2
|
+
import { HTTPException } from 'hono/http-exception';
|
|
3
|
+
import type { Tool, ToolExecutionContext, ToolExecutionPayload, ToolExecutionResult, ToolUseContext } from './types.js';
|
|
5
4
|
|
|
6
5
|
export class ToolRegistry {
|
|
7
|
-
|
|
8
6
|
// The category name usinfg this registry
|
|
9
7
|
category: string;
|
|
10
|
-
registry: Record<string, Tool
|
|
8
|
+
registry: Record<string, Tool> = {};
|
|
11
9
|
|
|
12
|
-
constructor(category: string, tools: Tool
|
|
10
|
+
constructor(category: string, tools: Tool[] = []) {
|
|
13
11
|
this.category = category;
|
|
14
12
|
for (const tool of tools) {
|
|
15
13
|
this.registry[tool.name] = tool;
|
|
@@ -22,81 +20,53 @@ export class ToolRegistry {
|
|
|
22
20
|
* @returns Filtered tool definitions
|
|
23
21
|
*/
|
|
24
22
|
getDefinitions(context?: ToolUseContext): AgentToolDefinition[] {
|
|
25
|
-
const mapTool = (tool: Tool
|
|
23
|
+
const mapTool = (tool: Tool): AgentToolDefinition => ({
|
|
26
24
|
url: `tools/${this.category}`,
|
|
27
25
|
name: tool.name,
|
|
28
26
|
description: tool.description,
|
|
29
27
|
input_schema: tool.input_schema,
|
|
30
28
|
category: this.category,
|
|
31
29
|
default: tool.default,
|
|
30
|
+
...(tool.annotations ? { annotations: tool.annotations } : {}),
|
|
31
|
+
...(tool.requires_user_confirmation ? { requires_user_confirmation: true } : {}),
|
|
32
32
|
});
|
|
33
33
|
let tools = Object.values(this.registry);
|
|
34
34
|
if (context) {
|
|
35
|
-
tools = tools.filter(tool => {
|
|
35
|
+
tools = tools.filter((tool) => {
|
|
36
36
|
return tool.isEnabled ? tool.isEnabled(context) : true;
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
39
|
return tools.map(mapTool);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
getTool<ParamsT extends
|
|
43
|
-
const tool = this.registry[name]
|
|
42
|
+
getTool<ParamsT extends object>(name: string): Tool<ParamsT> {
|
|
43
|
+
const tool = this.registry[name];
|
|
44
44
|
if (tool === undefined) {
|
|
45
45
|
throw new ToolNotFoundError(name);
|
|
46
46
|
}
|
|
47
|
-
return tool
|
|
47
|
+
return tool as unknown as Tool<ParamsT>;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
getTools() {
|
|
51
51
|
return Object.values(this.registry);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
registerTool<ParamsT extends
|
|
55
|
-
this.registry[tool.name] = tool;
|
|
54
|
+
registerTool<ParamsT extends object>(tool: Tool<ParamsT>): void {
|
|
55
|
+
this.registry[tool.name] = tool as unknown as Tool;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
runTool<ParamsT extends
|
|
58
|
+
runTool<ParamsT extends object>(
|
|
59
|
+
payload: ToolExecutionPayload<ParamsT>,
|
|
60
|
+
context: ToolExecutionContext,
|
|
61
|
+
): Promise<ToolExecutionResult> {
|
|
59
62
|
const toolName = payload.tool_use.tool_name;
|
|
60
|
-
const toolUseId = payload.tool_use.id;
|
|
61
|
-
const runId = payload.metadata?.run_id;
|
|
62
|
-
console.log(`[ToolRegistry] Executing tool: ${toolName}`, {
|
|
63
|
-
toolUseId,
|
|
64
|
-
runId,
|
|
65
|
-
input: sanitizeInput(payload.tool_use.tool_input),
|
|
66
|
-
});
|
|
67
63
|
return this.getTool(toolName).run(payload, context);
|
|
68
64
|
}
|
|
69
|
-
|
|
70
65
|
}
|
|
71
66
|
|
|
72
|
-
|
|
73
67
|
export class ToolNotFoundError extends HTTPException {
|
|
74
68
|
constructor(name: string) {
|
|
75
|
-
super(404, { message:
|
|
76
|
-
this.name =
|
|
69
|
+
super(404, { message: `Tool function not found: ${name}` });
|
|
70
|
+
this.name = 'ToolNotFoundError';
|
|
77
71
|
}
|
|
78
72
|
}
|
|
79
|
-
|
|
80
|
-
const SENSITIVE_KEYS = new Set([
|
|
81
|
-
'apikey', 'api_key', 'token', 'secret', 'password', 'credential', 'credentials',
|
|
82
|
-
'authorization', 'auth', 'key', 'private_key', 'access_token', 'refresh_token'
|
|
83
|
-
]);
|
|
84
|
-
|
|
85
|
-
function sanitizeInput(input: Record<string, any> | null | undefined): Record<string, any> | null {
|
|
86
|
-
if (!input) return null;
|
|
87
|
-
|
|
88
|
-
const sanitized: Record<string, any> = {};
|
|
89
|
-
for (const [key, value] of Object.entries(input)) {
|
|
90
|
-
const lowerKey = key.toLowerCase();
|
|
91
|
-
if (SENSITIVE_KEYS.has(lowerKey) || lowerKey.includes('key') || lowerKey.includes('token') || lowerKey.includes('secret')) {
|
|
92
|
-
sanitized[key] = '[REDACTED]';
|
|
93
|
-
} else if (typeof value === 'string' && value.length > 50) {
|
|
94
|
-
sanitized[key] = value.slice(0, 50) + '...';
|
|
95
|
-
} else if (typeof value === 'object' && value !== null) {
|
|
96
|
-
sanitized[key] = Array.isArray(value) ? `[Array(${value.length})]` : '[Object]';
|
|
97
|
-
} else {
|
|
98
|
-
sanitized[key] = value;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return sanitized;
|
|
102
|
-
}
|
package/src/auth.ts
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
|
-
import { decodeEndpoints, VertesiaClient } from
|
|
2
|
-
import { AuthTokenPayload } from
|
|
3
|
-
import { Context } from
|
|
4
|
-
import { HTTPException } from
|
|
5
|
-
import { createLocalJWKSet, decodeJwt, JSONWebKeySet,
|
|
6
|
-
import { ToolExecutionContext } from
|
|
1
|
+
import { decodeEndpoints, VertesiaClient } from '@vertesia/client';
|
|
2
|
+
import type { AuthTokenPayload } from '@vertesia/common';
|
|
3
|
+
import type { Context } from 'hono';
|
|
4
|
+
import { HTTPException } from 'hono/http-exception';
|
|
5
|
+
import { createLocalJWKSet, decodeJwt, type JSONWebKeySet, type JWTVerifyGetKey, jwtVerify } from 'jose';
|
|
6
|
+
import type { ToolExecutionContext } from './types.js';
|
|
7
|
+
|
|
7
8
|
const cache: Record<string, JWTVerifyGetKey> = {};
|
|
8
9
|
|
|
9
10
|
export async function getJwks(url: string) {
|
|
10
11
|
if (!cache[url]) {
|
|
11
12
|
console.log('JWKS cache miss for: ', url);
|
|
12
|
-
const jwks = await fetch(url)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
const jwks = await fetch(url)
|
|
14
|
+
.then((r) => {
|
|
15
|
+
if (r.ok) {
|
|
16
|
+
return r.json() as Promise<JSONWebKeySet>;
|
|
17
|
+
}
|
|
18
|
+
throw new Error(`Fetching jwks failed with code: ${r.status}`);
|
|
19
|
+
})
|
|
20
|
+
.catch((err) => {
|
|
21
|
+
throw new Error(`Failed to fetch jwks: ${err.message}`);
|
|
22
|
+
});
|
|
20
23
|
cache[url] = createLocalJWKSet(jwks);
|
|
21
24
|
}
|
|
22
25
|
return cache[url];
|
|
@@ -25,16 +28,15 @@ export async function getJwks(url: string) {
|
|
|
25
28
|
export async function verifyToken(token: string) {
|
|
26
29
|
const decodedJwt = decodeJwt(token);
|
|
27
30
|
if (!decodedJwt.iss) {
|
|
28
|
-
throw new Error(
|
|
31
|
+
throw new Error('No issuer URL found in JWT');
|
|
29
32
|
}
|
|
30
33
|
if (!isAllowedIssuer(decodedJwt.iss)) {
|
|
31
|
-
throw new Error(
|
|
34
|
+
throw new Error(`Issuer is not allowed: ${decodedJwt.iss}`);
|
|
32
35
|
}
|
|
33
36
|
const jwks = await getJwks(`${decodedJwt.iss}/.well-known/jwks`);
|
|
34
37
|
return await jwtVerify<AuthTokenPayload>(token, jwks);
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
|
|
38
40
|
export interface EndpointOverrides {
|
|
39
41
|
studio?: string;
|
|
40
42
|
store?: string;
|
|
@@ -51,31 +53,32 @@ export async function authorize(ctx: Context, endpointOverrides?: EndpointOverri
|
|
|
51
53
|
const auth = ctx.req.header('Authorization');
|
|
52
54
|
if (!auth) {
|
|
53
55
|
throw new HTTPException(401, {
|
|
54
|
-
message: `Missing Authorization header
|
|
56
|
+
message: `Missing Authorization header`,
|
|
55
57
|
});
|
|
56
58
|
}
|
|
57
59
|
const [scheme, value] = auth.trim().split(' ');
|
|
58
60
|
if (scheme.toLowerCase() !== 'bearer') {
|
|
59
61
|
throw new HTTPException(401, {
|
|
60
|
-
message: `Authorization scheme ${scheme} is not supported
|
|
62
|
+
message: `Authorization scheme ${scheme} is not supported`,
|
|
61
63
|
});
|
|
62
64
|
}
|
|
63
65
|
if (!value) {
|
|
64
66
|
throw new HTTPException(401, {
|
|
65
|
-
message: `Missing bearer token value
|
|
67
|
+
message: `Missing bearer token value`,
|
|
66
68
|
});
|
|
67
69
|
}
|
|
68
70
|
try {
|
|
69
71
|
const { payload } = await verifyToken(value);
|
|
70
72
|
assertAllowedOrg(payload);
|
|
71
73
|
const session = new AuthSession(value, payload, endpointOverrides, toolContext);
|
|
72
|
-
ctx.set(
|
|
74
|
+
ctx.set('auth', session);
|
|
73
75
|
return session;
|
|
74
|
-
} catch (err:
|
|
76
|
+
} catch (err: unknown) {
|
|
75
77
|
if (err instanceof HTTPException) throw err;
|
|
78
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
76
79
|
throw new HTTPException(401, {
|
|
77
|
-
message
|
|
78
|
-
cause: err
|
|
80
|
+
message,
|
|
81
|
+
cause: err,
|
|
79
82
|
});
|
|
80
83
|
}
|
|
81
84
|
}
|
|
@@ -93,7 +96,7 @@ export class AuthSession implements ToolExecutionContext {
|
|
|
93
96
|
public token: string,
|
|
94
97
|
public payload: AuthTokenPayload,
|
|
95
98
|
endpointOverrides?: EndpointOverrides,
|
|
96
|
-
toolContext?: ToolContext
|
|
99
|
+
toolContext?: ToolContext,
|
|
97
100
|
) {
|
|
98
101
|
const decoded = decodeEndpoints(payload.endpoints);
|
|
99
102
|
// Use overrides from workflow config if provided, falling back to JWT endpoints
|
|
@@ -121,7 +124,7 @@ export class AuthSession implements ToolExecutionContext {
|
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
function isAllowedIssuer(iss: string) {
|
|
124
|
-
return iss.endsWith(
|
|
127
|
+
return iss.endsWith('.vertesia.io') || iss.endsWith('.becomposable.com');
|
|
125
128
|
}
|
|
126
129
|
|
|
127
130
|
/**
|
|
@@ -133,7 +136,12 @@ function getAllowedOrgs(): Set<string> | null {
|
|
|
133
136
|
if (_allowedOrgs === undefined) {
|
|
134
137
|
const raw = process.env.VERTESIA_ALLOWED_ORGS;
|
|
135
138
|
_allowedOrgs = raw
|
|
136
|
-
? new Set(
|
|
139
|
+
? new Set(
|
|
140
|
+
raw
|
|
141
|
+
.split(',')
|
|
142
|
+
.map((s) => s.trim())
|
|
143
|
+
.filter(Boolean),
|
|
144
|
+
)
|
|
137
145
|
: null;
|
|
138
146
|
}
|
|
139
147
|
return _allowedOrgs;
|
package/src/copy-assets.ts
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* import { copyRuntimeAssets } from '@vertesia/tools-sdk';
|
|
11
11
|
* copyRuntimeAssets('./src', './dist');
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
14
|
-
import { dirname, join } from
|
|
13
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'node:fs';
|
|
14
|
+
import { dirname, join } from 'node:path';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Recursively copy files matching a filter
|
|
@@ -50,11 +50,7 @@ export interface CopyAssetsOptions {
|
|
|
50
50
|
* Copy runtime assets (skills, interactions) from src to dist
|
|
51
51
|
*/
|
|
52
52
|
export function copyRuntimeAssets(options: CopyAssetsOptions = {}): void {
|
|
53
|
-
const {
|
|
54
|
-
srcDir = './src',
|
|
55
|
-
distDir = './dist',
|
|
56
|
-
verbose = true
|
|
57
|
-
} = options;
|
|
53
|
+
const { srcDir = './src', distDir = './dist', verbose = true } = options;
|
|
58
54
|
|
|
59
55
|
if (verbose) {
|
|
60
56
|
console.log('Copying runtime assets to dist...');
|
|
@@ -66,9 +62,7 @@ export function copyRuntimeAssets(options: CopyAssetsOptions = {}): void {
|
|
|
66
62
|
|
|
67
63
|
if (existsSync(skillsSrc)) {
|
|
68
64
|
copyFilesRecursive(skillsSrc, skillsDest, (filename) => {
|
|
69
|
-
return filename === 'SKILL.md' ||
|
|
70
|
-
filename === 'SKILL.jst' ||
|
|
71
|
-
filename.endsWith('.py');
|
|
65
|
+
return filename === 'SKILL.md' || filename === 'SKILL.jst' || filename.endsWith('.py');
|
|
72
66
|
});
|
|
73
67
|
if (verbose) {
|
|
74
68
|
console.log(' ✓ Skills assets (SKILL.md, SKILL.jst, *.py)');
|
|
@@ -81,8 +75,7 @@ export function copyRuntimeAssets(options: CopyAssetsOptions = {}): void {
|
|
|
81
75
|
|
|
82
76
|
if (existsSync(interactionsSrc)) {
|
|
83
77
|
copyFilesRecursive(interactionsSrc, interactionsDest, (filename) => {
|
|
84
|
-
return filename === 'prompt.jst' ||
|
|
85
|
-
filename === 'prompt.md';
|
|
78
|
+
return filename === 'prompt.jst' || filename === 'prompt.md';
|
|
86
79
|
});
|
|
87
80
|
if (verbose) {
|
|
88
81
|
console.log(' ✓ Interaction assets (prompt.jst, prompt.md)');
|
package/src/index.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export {
|
|
3
|
-
export * from
|
|
4
|
-
export
|
|
5
|
-
export
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export
|
|
9
|
-
export
|
|
10
|
-
export * from
|
|
11
|
-
export * from
|
|
12
|
-
export * from
|
|
13
|
-
export * from
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
export * from './ActivityCollection.js';
|
|
2
|
+
export { AuthSession, authorize } from './auth.js';
|
|
3
|
+
export * from './ContentTypesCollection.js';
|
|
4
|
+
export { copyRuntimeAssets } from './copy-assets.js';
|
|
5
|
+
export * from './InteractionCollection.js';
|
|
6
|
+
export * from './RenderingTemplateCollection.js';
|
|
7
|
+
export * from './SkillCollection.js';
|
|
8
|
+
export type { BuildAppPackageOptions } from './server/app-package.js';
|
|
9
|
+
export { buildAppPackage } from './server/app-package.js';
|
|
10
|
+
export * from './server/types.js';
|
|
11
|
+
export * from './server.js';
|
|
12
|
+
export * from './site/templates.js';
|
|
13
|
+
export * from './ToolCollection.js';
|
|
14
|
+
export * from './ToolRegistry.js';
|
|
15
|
+
export * from './types.js';
|
|
@@ -1,123 +1,126 @@
|
|
|
1
|
-
import { Hono } from
|
|
2
|
-
import { describe, expect, it, vi } from
|
|
3
|
-
import { ActivityCollection, ActivityDefinition } from
|
|
4
|
-
import { createActivitiesRoute } from
|
|
5
|
-
import { ToolServerConfig } from
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { ActivityCollection, type ActivityDefinition } from '../ActivityCollection.js';
|
|
4
|
+
import { createActivitiesRoute } from './activities.js';
|
|
5
|
+
import type { ToolServerConfig } from './types.js';
|
|
6
|
+
|
|
7
|
+
/** Permissive recursive type for navigating JSON response bodies in tests. */
|
|
8
|
+
type Tree = { readonly [key: string]: Tree };
|
|
6
9
|
|
|
7
10
|
// Mock authorize to avoid JWT verification
|
|
8
|
-
vi.mock(
|
|
9
|
-
authorize: vi.fn().mockResolvedValue({ token:
|
|
11
|
+
vi.mock('../auth.js', () => ({
|
|
12
|
+
authorize: vi.fn().mockResolvedValue({ token: 'test-token' }),
|
|
10
13
|
AuthSession: vi.fn(),
|
|
11
14
|
}));
|
|
12
15
|
|
|
13
16
|
const mockActivity: ActivityDefinition = {
|
|
14
|
-
name:
|
|
15
|
-
title:
|
|
16
|
-
description:
|
|
17
|
-
input_schema: { type:
|
|
17
|
+
name: 'analyze_sentiment',
|
|
18
|
+
title: 'Analyze Sentiment',
|
|
19
|
+
description: 'Analyzes text sentiment',
|
|
20
|
+
input_schema: { type: 'object', properties: { text: { type: 'string' } } },
|
|
18
21
|
run: vi.fn().mockResolvedValue({ score: 0.95 }),
|
|
19
22
|
};
|
|
20
23
|
|
|
21
24
|
const mockActivity2: ActivityDefinition = {
|
|
22
|
-
name:
|
|
23
|
-
description:
|
|
24
|
-
run: vi.fn().mockResolvedValue({ entities: [
|
|
25
|
+
name: 'extract_entities',
|
|
26
|
+
description: 'Extracts entities',
|
|
27
|
+
run: vi.fn().mockResolvedValue({ entities: ['foo'] }),
|
|
25
28
|
};
|
|
26
29
|
|
|
27
30
|
function createApp(activities: ActivityCollection[] = []) {
|
|
28
31
|
const app = new Hono();
|
|
29
32
|
const config = { activities } as ToolServerConfig;
|
|
30
|
-
createActivitiesRoute(app,
|
|
33
|
+
createActivitiesRoute(app, '/api/activities', config);
|
|
31
34
|
return app;
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
function createTestCollections() {
|
|
35
38
|
const coll1 = new ActivityCollection({
|
|
36
|
-
name:
|
|
37
|
-
description:
|
|
39
|
+
name: 'nlp',
|
|
40
|
+
description: 'NLP activities',
|
|
38
41
|
activities: [mockActivity],
|
|
39
42
|
});
|
|
40
43
|
const coll2 = new ActivityCollection({
|
|
41
|
-
name:
|
|
42
|
-
description:
|
|
44
|
+
name: 'extraction',
|
|
45
|
+
description: 'Extraction activities',
|
|
43
46
|
activities: [mockActivity2],
|
|
44
47
|
});
|
|
45
48
|
return [coll1, coll2];
|
|
46
49
|
}
|
|
47
50
|
|
|
48
|
-
describe(
|
|
49
|
-
describe(
|
|
50
|
-
it(
|
|
51
|
+
describe('Activities server routes', () => {
|
|
52
|
+
describe('GET /api/activities', () => {
|
|
53
|
+
it('returns all activity definitions across collections', async () => {
|
|
51
54
|
const app = createApp(createTestCollections());
|
|
52
|
-
const res = await app.request(
|
|
53
|
-
const body = await res.json() as
|
|
55
|
+
const res = await app.request('/api/activities');
|
|
56
|
+
const body = (await res.json()) as unknown as Tree;
|
|
54
57
|
|
|
55
58
|
expect(res.status).toBe(200);
|
|
56
59
|
expect(body.activities).toHaveLength(2);
|
|
57
|
-
expect(body.activities[0].name).toBe(
|
|
58
|
-
expect(body.activities[1].name).toBe(
|
|
60
|
+
expect(body.activities[0].name).toBe('analyze_sentiment');
|
|
61
|
+
expect(body.activities[1].name).toBe('extract_entities');
|
|
59
62
|
expect(body.collections).toHaveLength(2);
|
|
60
63
|
});
|
|
61
64
|
|
|
62
|
-
it(
|
|
65
|
+
it('returns empty when no collections configured', async () => {
|
|
63
66
|
const app = createApp([]);
|
|
64
|
-
const res = await app.request(
|
|
65
|
-
const body = await res.json() as
|
|
67
|
+
const res = await app.request('/api/activities');
|
|
68
|
+
const body = (await res.json()) as unknown as Tree;
|
|
66
69
|
|
|
67
70
|
expect(res.status).toBe(200);
|
|
68
71
|
expect(body.activities).toHaveLength(0);
|
|
69
72
|
});
|
|
70
73
|
});
|
|
71
74
|
|
|
72
|
-
describe(
|
|
73
|
-
it(
|
|
75
|
+
describe('GET /api/activities/{collection}', () => {
|
|
76
|
+
it('returns activities for a specific collection', async () => {
|
|
74
77
|
const app = createApp(createTestCollections());
|
|
75
|
-
const res = await app.request(
|
|
76
|
-
const body = await res.json() as
|
|
78
|
+
const res = await app.request('/api/activities/nlp');
|
|
79
|
+
const body = (await res.json()) as unknown as Tree;
|
|
77
80
|
|
|
78
81
|
expect(res.status).toBe(200);
|
|
79
|
-
expect(body.name).toBe(
|
|
82
|
+
expect(body.name).toBe('nlp');
|
|
80
83
|
expect(body.activities).toHaveLength(1);
|
|
81
|
-
expect(body.activities[0].name).toBe(
|
|
84
|
+
expect(body.activities[0].name).toBe('analyze_sentiment');
|
|
82
85
|
});
|
|
83
86
|
});
|
|
84
87
|
|
|
85
|
-
describe(
|
|
86
|
-
it(
|
|
88
|
+
describe('POST /api/activities', () => {
|
|
89
|
+
it('executes activity by name', async () => {
|
|
87
90
|
const app = createApp(createTestCollections());
|
|
88
|
-
const res = await app.request(
|
|
89
|
-
method:
|
|
91
|
+
const res = await app.request('/api/activities', {
|
|
92
|
+
method: 'POST',
|
|
90
93
|
headers: {
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
'Content-Type': 'application/json',
|
|
95
|
+
Authorization: 'Bearer test-token',
|
|
93
96
|
},
|
|
94
97
|
body: JSON.stringify({
|
|
95
|
-
activity_name:
|
|
96
|
-
params: { text:
|
|
98
|
+
activity_name: 'analyze_sentiment',
|
|
99
|
+
params: { text: 'hello' },
|
|
97
100
|
metadata: {
|
|
98
|
-
workflow_name:
|
|
99
|
-
account_id:
|
|
100
|
-
project_id:
|
|
101
|
+
workflow_name: 'wf',
|
|
102
|
+
account_id: 'acc',
|
|
103
|
+
project_id: 'proj',
|
|
101
104
|
},
|
|
102
105
|
}),
|
|
103
106
|
});
|
|
104
107
|
|
|
105
108
|
expect(res.status).toBe(200);
|
|
106
|
-
const body = await res.json() as
|
|
109
|
+
const body = (await res.json()) as unknown as Tree;
|
|
107
110
|
expect(body.result).toEqual({ score: 0.95 });
|
|
108
111
|
expect(body.is_error).toBe(false);
|
|
109
112
|
});
|
|
110
113
|
|
|
111
|
-
it(
|
|
114
|
+
it('returns 404 for unknown activity name', async () => {
|
|
112
115
|
const app = createApp(createTestCollections());
|
|
113
|
-
const res = await app.request(
|
|
114
|
-
method:
|
|
116
|
+
const res = await app.request('/api/activities', {
|
|
117
|
+
method: 'POST',
|
|
115
118
|
headers: {
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
'Content-Type': 'application/json',
|
|
120
|
+
Authorization: 'Bearer test-token',
|
|
118
121
|
},
|
|
119
122
|
body: JSON.stringify({
|
|
120
|
-
activity_name:
|
|
123
|
+
activity_name: 'nonexistent',
|
|
121
124
|
params: {},
|
|
122
125
|
metadata: {},
|
|
123
126
|
}),
|
|
@@ -126,13 +129,13 @@ describe("Activities server routes", () => {
|
|
|
126
129
|
expect(res.status).toBe(404);
|
|
127
130
|
});
|
|
128
131
|
|
|
129
|
-
it(
|
|
132
|
+
it('returns 400 for missing activity_name', async () => {
|
|
130
133
|
const app = createApp(createTestCollections());
|
|
131
|
-
const res = await app.request(
|
|
132
|
-
method:
|
|
134
|
+
const res = await app.request('/api/activities', {
|
|
135
|
+
method: 'POST',
|
|
133
136
|
headers: {
|
|
134
|
-
|
|
135
|
-
|
|
137
|
+
'Content-Type': 'application/json',
|
|
138
|
+
Authorization: 'Bearer test-token',
|
|
136
139
|
},
|
|
137
140
|
body: JSON.stringify({ params: {} }),
|
|
138
141
|
});
|
|
@@ -141,25 +144,25 @@ describe("Activities server routes", () => {
|
|
|
141
144
|
});
|
|
142
145
|
});
|
|
143
146
|
|
|
144
|
-
describe(
|
|
145
|
-
it(
|
|
147
|
+
describe('POST /api/activities/{collection}', () => {
|
|
148
|
+
it('routes to correct collection', async () => {
|
|
146
149
|
const app = createApp(createTestCollections());
|
|
147
|
-
const res = await app.request(
|
|
148
|
-
method:
|
|
150
|
+
const res = await app.request('/api/activities/extraction', {
|
|
151
|
+
method: 'POST',
|
|
149
152
|
headers: {
|
|
150
|
-
|
|
151
|
-
|
|
153
|
+
'Content-Type': 'application/json',
|
|
154
|
+
Authorization: 'Bearer test-token',
|
|
152
155
|
},
|
|
153
156
|
body: JSON.stringify({
|
|
154
|
-
activity_name:
|
|
157
|
+
activity_name: 'extract_entities',
|
|
155
158
|
params: {},
|
|
156
159
|
metadata: {},
|
|
157
160
|
}),
|
|
158
161
|
});
|
|
159
162
|
|
|
160
163
|
expect(res.status).toBe(200);
|
|
161
|
-
const body = await res.json() as
|
|
162
|
-
expect(body.result).toEqual({ entities: [
|
|
164
|
+
const body = (await res.json()) as unknown as Tree;
|
|
165
|
+
expect(body.result).toEqual({ entities: ['foo'] });
|
|
163
166
|
});
|
|
164
167
|
});
|
|
165
168
|
});
|
package/src/server/activities.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { RemoteActivityDefinition, RemoteActivityExecutionPayload } from
|
|
2
|
-
import { Context, Hono } from
|
|
3
|
-
import { HTTPException } from
|
|
4
|
-
import { ActivityCollection } from
|
|
5
|
-
import { ToolServerConfig } from
|
|
1
|
+
import type { RemoteActivityDefinition, RemoteActivityExecutionPayload } from '@vertesia/common';
|
|
2
|
+
import { type Context, Hono } from 'hono';
|
|
3
|
+
import { HTTPException } from 'hono/http-exception';
|
|
4
|
+
import type { ActivityCollection } from '../ActivityCollection.js';
|
|
5
|
+
import type { ToolServerConfig } from './types.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Safely parse JSON from a request body. Throws HTTPException(400) on invalid JSON.
|
|
@@ -12,11 +12,15 @@ async function safeParseJson(c: Context): Promise<unknown> {
|
|
|
12
12
|
return await c.req.json();
|
|
13
13
|
} catch {
|
|
14
14
|
throw new HTTPException(400, {
|
|
15
|
-
message: 'Invalid JSON in request body.'
|
|
15
|
+
message: 'Invalid JSON in request body.',
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
21
|
+
return !!value && typeof value === 'object';
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
/**
|
|
21
25
|
* Validates the structure of a RemoteActivityExecutionPayload.
|
|
22
26
|
* Returns the parsed payload or throws HTTPException(400).
|
|
@@ -24,19 +28,19 @@ async function safeParseJson(c: Context): Promise<unknown> {
|
|
|
24
28
|
function parseActivityPayload(body: unknown): RemoteActivityExecutionPayload {
|
|
25
29
|
if (!body || typeof body !== 'object') {
|
|
26
30
|
throw new HTTPException(400, {
|
|
27
|
-
message: 'Invalid or missing activity execution payload.'
|
|
31
|
+
message: 'Invalid or missing activity execution payload.',
|
|
28
32
|
});
|
|
29
33
|
}
|
|
30
|
-
const obj = body as Record<string,
|
|
34
|
+
const obj = body as Record<string, unknown>;
|
|
31
35
|
if (typeof obj.activity_name !== 'string' || !obj.activity_name) {
|
|
32
36
|
throw new HTTPException(400, {
|
|
33
|
-
message: 'Missing required field: activity_name'
|
|
37
|
+
message: 'Missing required field: activity_name',
|
|
34
38
|
});
|
|
35
39
|
}
|
|
36
40
|
return {
|
|
37
41
|
activity_name: obj.activity_name,
|
|
38
|
-
params: obj.params
|
|
39
|
-
metadata: obj.metadata
|
|
42
|
+
params: isRecord(obj.params) ? obj.params : {},
|
|
43
|
+
metadata: isRecord(obj.metadata) ? obj.metadata : {},
|
|
40
44
|
};
|
|
41
45
|
}
|
|
42
46
|
|
|
@@ -61,7 +65,7 @@ export function createActivitiesRoute(app: Hono, basePath: string, config: ToolS
|
|
|
61
65
|
title: 'All Activities',
|
|
62
66
|
description: 'All available remote activities across all collections',
|
|
63
67
|
activities: allActivities,
|
|
64
|
-
collections: activities.map(a => ({
|
|
68
|
+
collections: activities.map((a) => ({
|
|
65
69
|
name: a.name,
|
|
66
70
|
title: a.title,
|
|
67
71
|
description: a.description,
|
|
@@ -77,7 +81,7 @@ export function createActivitiesRoute(app: Hono, basePath: string, config: ToolS
|
|
|
77
81
|
const collection = activityToCollection.get(payload.activity_name);
|
|
78
82
|
if (!collection) {
|
|
79
83
|
throw new HTTPException(404, {
|
|
80
|
-
message: `Activity not found: ${payload.activity_name}. Available: ${Array.from(activityToCollection.keys()).join(', ')}
|
|
84
|
+
message: `Activity not found: ${payload.activity_name}. Available: ${Array.from(activityToCollection.keys()).join(', ')}`,
|
|
81
85
|
});
|
|
82
86
|
}
|
|
83
87
|
|