@vertesia/tools-sdk 1.0.0-dev.20260128.144200 → 1.0.0-dev.20260225.024852Z
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/cjs/ContentTypesCollection.js +43 -0
- package/lib/cjs/ContentTypesCollection.js.map +1 -0
- package/lib/cjs/InteractionCollection.js +0 -121
- package/lib/cjs/InteractionCollection.js.map +1 -1
- package/lib/cjs/SkillCollection.js +10 -2
- package/lib/cjs/SkillCollection.js.map +1 -1
- package/lib/cjs/ToolCollection.js +6 -13
- package/lib/cjs/ToolCollection.js.map +1 -1
- package/lib/cjs/ToolRegistry.js +16 -32
- package/lib/cjs/ToolRegistry.js.map +1 -1
- package/lib/cjs/index.js +1 -0
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/server/app-package.js +130 -0
- package/lib/cjs/server/app-package.js.map +1 -0
- package/lib/cjs/server/content-types.js +73 -0
- package/lib/cjs/server/content-types.js.map +1 -0
- package/lib/cjs/server/interactions.js +26 -3
- package/lib/cjs/server/interactions.js.map +1 -1
- package/lib/cjs/server/site.js +7 -1
- package/lib/cjs/server/site.js.map +1 -1
- package/lib/cjs/server/tools.js +2 -19
- package/lib/cjs/server/tools.js.map +1 -1
- package/lib/cjs/server/widgets.js.map +1 -1
- package/lib/cjs/server.js +4 -0
- package/lib/cjs/server.js.map +1 -1
- package/lib/cjs/site/styles.js +55 -0
- package/lib/cjs/site/styles.js.map +1 -1
- package/lib/cjs/site/templates.js +104 -85
- package/lib/cjs/site/templates.js.map +1 -1
- package/lib/cjs/utils.js +13 -0
- package/lib/cjs/utils.js.map +1 -1
- package/lib/esm/ContentTypesCollection.js +39 -0
- package/lib/esm/ContentTypesCollection.js.map +1 -0
- package/lib/esm/InteractionCollection.js +0 -86
- package/lib/esm/InteractionCollection.js.map +1 -1
- package/lib/esm/SkillCollection.js +10 -2
- package/lib/esm/SkillCollection.js.map +1 -1
- package/lib/esm/ToolCollection.js +7 -14
- package/lib/esm/ToolCollection.js.map +1 -1
- package/lib/esm/ToolRegistry.js +16 -32
- package/lib/esm/ToolRegistry.js.map +1 -1
- package/lib/esm/index.js +1 -0
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/server/app-package.js +127 -0
- package/lib/esm/server/app-package.js.map +1 -0
- package/lib/esm/server/content-types.js +70 -0
- package/lib/esm/server/content-types.js.map +1 -0
- package/lib/esm/server/interactions.js +26 -3
- package/lib/esm/server/interactions.js.map +1 -1
- package/lib/esm/server/site.js +8 -2
- package/lib/esm/server/site.js.map +1 -1
- package/lib/esm/server/tools.js +2 -19
- package/lib/esm/server/tools.js.map +1 -1
- package/lib/esm/server/widgets.js.map +1 -1
- package/lib/esm/server.js +4 -0
- package/lib/esm/server.js.map +1 -1
- package/lib/esm/site/styles.js +55 -0
- package/lib/esm/site/styles.js.map +1 -1
- package/lib/esm/site/templates.js +103 -85
- package/lib/esm/site/templates.js.map +1 -1
- package/lib/esm/utils.js +12 -0
- package/lib/esm/utils.js.map +1 -1
- package/lib/types/ContentTypesCollection.d.ts +18 -0
- package/lib/types/ContentTypesCollection.d.ts.map +1 -0
- package/lib/types/InteractionCollection.d.ts +1 -31
- package/lib/types/InteractionCollection.d.ts.map +1 -1
- package/lib/types/SkillCollection.d.ts +3 -2
- package/lib/types/SkillCollection.d.ts.map +1 -1
- package/lib/types/ToolCollection.d.ts +5 -10
- package/lib/types/ToolCollection.d.ts.map +1 -1
- package/lib/types/ToolRegistry.d.ts +5 -24
- package/lib/types/ToolRegistry.d.ts.map +1 -1
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/server/app-package.d.ts +4 -0
- package/lib/types/server/app-package.d.ts.map +1 -0
- package/lib/types/server/content-types.d.ts +4 -0
- package/lib/types/server/content-types.d.ts.map +1 -0
- package/lib/types/server/interactions.d.ts.map +1 -1
- package/lib/types/server/site.d.ts.map +1 -1
- package/lib/types/server/tools.d.ts.map +1 -1
- package/lib/types/server/types.d.ts +22 -0
- package/lib/types/server/types.d.ts.map +1 -1
- package/lib/types/server/widgets.d.ts +0 -5
- package/lib/types/server/widgets.d.ts.map +1 -1
- package/lib/types/server.d.ts.map +1 -1
- package/lib/types/site/styles.d.ts +1 -1
- package/lib/types/site/styles.d.ts.map +1 -1
- package/lib/types/site/templates.d.ts +5 -0
- package/lib/types/site/templates.d.ts.map +1 -1
- package/lib/types/types.d.ts +29 -16
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/utils.d.ts +6 -0
- package/lib/types/utils.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/ContentTypesCollection.ts +51 -0
- package/src/InteractionCollection.ts +1 -94
- package/src/SkillCollection.ts +13 -3
- package/src/ToolCollection.ts +11 -18
- package/src/ToolRegistry.ts +22 -55
- package/src/index.ts +1 -0
- package/src/server/app-package.ts +147 -0
- package/src/server/content-types.ts +89 -0
- package/src/server/interactions.ts +29 -3
- package/src/server/site.ts +9 -0
- package/src/server/tools.ts +3 -22
- package/src/server/types.ts +26 -0
- package/src/server/widgets.ts +2 -6
- package/src/server.ts +5 -0
- package/src/site/styles.ts +55 -0
- package/src/site/templates.ts +107 -84
- package/src/types.ts +33 -16
- package/src/utils.ts +15 -1
package/src/ToolRegistry.ts
CHANGED
|
@@ -1,27 +1,16 @@
|
|
|
1
|
+
import { AgentToolDefinition } from "@vertesia/common";
|
|
1
2
|
import { HTTPException } from "hono/http-exception";
|
|
2
|
-
import { Tool,
|
|
3
|
+
import { Tool, ToolExecutionContext, ToolExecutionPayload, ToolExecutionResult, ToolUseContext } from "./types.js";
|
|
3
4
|
|
|
4
|
-
/**
|
|
5
|
-
* Options for filtering tool definitions
|
|
6
|
-
*/
|
|
7
|
-
export interface ToolFilterOptions {
|
|
8
|
-
/**
|
|
9
|
-
* If true, only return tools that are available by default (default !== false).
|
|
10
|
-
* If false or undefined, return all tools.
|
|
11
|
-
*/
|
|
12
|
-
defaultOnly?: boolean;
|
|
13
|
-
/**
|
|
14
|
-
* List of tool names that are unlocked (available even if default: false).
|
|
15
|
-
* These tools will be included even when defaultOnly is true.
|
|
16
|
-
*/
|
|
17
|
-
unlockedTools?: string[];
|
|
18
|
-
}
|
|
19
5
|
|
|
20
6
|
export class ToolRegistry {
|
|
21
7
|
|
|
8
|
+
// The category name usinfg this registry
|
|
9
|
+
category: string;
|
|
22
10
|
registry: Record<string, Tool<any>> = {};
|
|
23
11
|
|
|
24
|
-
constructor(tools: Tool<any>[] = []) {
|
|
12
|
+
constructor(category: string, tools: Tool<any>[] = []) {
|
|
13
|
+
this.category = category;
|
|
25
14
|
for (const tool of tools) {
|
|
26
15
|
this.registry[tool.name] = tool;
|
|
27
16
|
}
|
|
@@ -32,44 +21,22 @@ export class ToolRegistry {
|
|
|
32
21
|
* @param options - Filtering options
|
|
33
22
|
* @returns Filtered tool definitions
|
|
34
23
|
*/
|
|
35
|
-
getDefinitions(
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
description: tool.description,
|
|
52
|
-
input_schema: tool.input_schema,
|
|
53
|
-
default: tool.default,
|
|
54
|
-
}));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Get tools that are in reserve (default: false and not unlocked).
|
|
59
|
-
* @param unlockedTools - List of tool names that are unlocked
|
|
60
|
-
* @returns Tool definitions for reserve tools
|
|
61
|
-
*/
|
|
62
|
-
getReserveTools(unlockedTools: string[] = []): ToolDefinitionWithDefault[] {
|
|
63
|
-
const unlockedSet = new Set(unlockedTools);
|
|
64
|
-
|
|
65
|
-
return Object.values(this.registry)
|
|
66
|
-
.filter(tool => tool.default === false && !unlockedSet.has(tool.name))
|
|
67
|
-
.map(tool => ({
|
|
68
|
-
name: tool.name,
|
|
69
|
-
description: tool.description,
|
|
70
|
-
input_schema: tool.input_schema,
|
|
71
|
-
default: tool.default,
|
|
72
|
-
}));
|
|
24
|
+
getDefinitions(context?: ToolUseContext): AgentToolDefinition[] {
|
|
25
|
+
const mapTool = (tool: Tool<any>): AgentToolDefinition => ({
|
|
26
|
+
url: `tools/${this.category}`,
|
|
27
|
+
name: tool.name,
|
|
28
|
+
description: tool.description,
|
|
29
|
+
input_schema: tool.input_schema,
|
|
30
|
+
category: this.category,
|
|
31
|
+
default: tool.default,
|
|
32
|
+
});
|
|
33
|
+
let tools = Object.values(this.registry);
|
|
34
|
+
if (context) {
|
|
35
|
+
tools = tools.filter(tool => {
|
|
36
|
+
return tool.isEnabled ? tool.isEnabled(context) : true;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return tools.map(mapTool);
|
|
73
40
|
}
|
|
74
41
|
|
|
75
42
|
getTool<ParamsT extends Record<string, any>>(name: string): Tool<ParamsT> {
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { AppPackage, AppPackageScope, AppWidgetInfo, CatalogInteractionRef, InCodeTypeDefinition } from "@vertesia/common";
|
|
2
|
+
import { Context, Hono } from "hono";
|
|
3
|
+
import { ToolUseContext } from "../types.js";
|
|
4
|
+
import { ToolServerConfig } from "./types.js";
|
|
5
|
+
|
|
6
|
+
function getRequestPayload<T>(c: Context): Promise<T | undefined> {
|
|
7
|
+
return c.req.method === "POST" ? c.req.json<T>() : Promise.resolve(undefined);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const builders: Record<Exclude<AppPackageScope, 'all'>, (pkg: AppPackage, config: ToolServerConfig, c: Context) => Promise<void>> = {
|
|
11
|
+
async tools(pkg: AppPackage, config: ToolServerConfig, c: Context) {
|
|
12
|
+
const { tools: toolCollections = [], skills: skillCollections = [] } = config;
|
|
13
|
+
|
|
14
|
+
const filterContext = await getRequestPayload<ToolUseContext>(c);
|
|
15
|
+
|
|
16
|
+
// Aggregate all tools from all collections
|
|
17
|
+
const allTools = toolCollections.flatMap(collection =>
|
|
18
|
+
collection.getToolDefinitions(filterContext)
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
// same for skills
|
|
22
|
+
const allSkills = skillCollections.flatMap(collection =>
|
|
23
|
+
collection.getToolDefinitions(filterContext)
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// Deduplicate by tool name (skills listed first take priority)
|
|
27
|
+
const seen = new Set<string>();
|
|
28
|
+
const combined = allSkills.concat(allTools);
|
|
29
|
+
pkg.tools = combined.filter(tool => {
|
|
30
|
+
if (seen.has(tool.name)) {
|
|
31
|
+
console.warn(`[app-package] Duplicate tool name "${tool.name}", skipping`);
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
seen.add(tool.name);
|
|
35
|
+
return true;
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
async interactions(pkg: AppPackage, config: ToolServerConfig) {
|
|
39
|
+
const allInteractions: CatalogInteractionRef[] = [];
|
|
40
|
+
for (const coll of (config.interactions || [])) {
|
|
41
|
+
for (const inter of coll.interactions) {
|
|
42
|
+
allInteractions.push({
|
|
43
|
+
type: "app",
|
|
44
|
+
id: coll.name + ":" + inter.name,
|
|
45
|
+
name: inter.name,
|
|
46
|
+
title: inter.title || inter.name,
|
|
47
|
+
description: inter.description,
|
|
48
|
+
tags: inter.tags || [],
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
pkg.interactions = allInteractions;
|
|
53
|
+
},
|
|
54
|
+
async types(pkg: AppPackage, config: ToolServerConfig) {
|
|
55
|
+
const allTypes: InCodeTypeDefinition[] = [];
|
|
56
|
+
for (const coll of config.types || []) {
|
|
57
|
+
for (const type of coll.types) {
|
|
58
|
+
allTypes.push({
|
|
59
|
+
...type,
|
|
60
|
+
id: coll.name + ":" + type.name
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
pkg.types = allTypes;
|
|
65
|
+
},
|
|
66
|
+
async widgets(pkg: AppPackage, config: ToolServerConfig) {
|
|
67
|
+
const { skills: skillCollections = [] } = config;
|
|
68
|
+
const widgets: Record<string, AppWidgetInfo> = {};
|
|
69
|
+
for (const coll of skillCollections) {
|
|
70
|
+
for (const skill of coll.getSkillDefinitions()) {
|
|
71
|
+
if (skill.widgets && skill.widgets.length > 0) {
|
|
72
|
+
widgets[skill.name] = {
|
|
73
|
+
skill: skill.name,
|
|
74
|
+
collection: coll.name,
|
|
75
|
+
url: `/widgets/${skill.widgets[0]}.js`
|
|
76
|
+
} satisfies AppWidgetInfo;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
pkg.widgets = widgets;
|
|
81
|
+
},
|
|
82
|
+
async ui(pkg: AppPackage, config: ToolServerConfig, c: Context) {
|
|
83
|
+
if (config.uiConfig) {
|
|
84
|
+
pkg.ui = { ...config.uiConfig };
|
|
85
|
+
const origin = new URL(c.req.url).origin;
|
|
86
|
+
pkg.ui.src = new URL(pkg.ui.src, origin).toString();
|
|
87
|
+
if (!pkg.ui.isolation) {
|
|
88
|
+
pkg.ui.isolation = "shadow";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
async settings(pkg: AppPackage, config: ToolServerConfig) {
|
|
93
|
+
if (config.settings) {
|
|
94
|
+
pkg.settings_schema = { ...config.settings };
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
async function handlePackageRequest(c: Context, config: ToolServerConfig) {
|
|
101
|
+
const scope = c.req.query('scope') || 'all';
|
|
102
|
+
const pkg: AppPackage = {};
|
|
103
|
+
|
|
104
|
+
const scopes = new Set<AppPackageScope>(scope.split(',') as AppPackageScope[]);
|
|
105
|
+
// TODO build pkg based on the query param scope
|
|
106
|
+
if (scopes.has('all')) {
|
|
107
|
+
await builders.tools(pkg, config, c);
|
|
108
|
+
await builders.interactions(pkg, config, c);
|
|
109
|
+
await builders.types(pkg, config, c);
|
|
110
|
+
await builders.widgets(pkg, config, c);
|
|
111
|
+
await builders.ui(pkg, config, c);
|
|
112
|
+
await builders.settings(pkg, config, c);
|
|
113
|
+
} else {
|
|
114
|
+
if (scopes.has('tools')) {
|
|
115
|
+
await builders.tools(pkg, config, c);
|
|
116
|
+
}
|
|
117
|
+
if (scopes.has('interactions')) {
|
|
118
|
+
await builders.interactions(pkg, config, c);
|
|
119
|
+
}
|
|
120
|
+
if (scopes.has('types')) {
|
|
121
|
+
await builders.types(pkg, config, c);
|
|
122
|
+
}
|
|
123
|
+
if (scopes.has('widgets')) {
|
|
124
|
+
await builders.widgets(pkg, config, c);
|
|
125
|
+
}
|
|
126
|
+
if (scopes.has('ui')) {
|
|
127
|
+
await builders.ui(pkg, config, c);
|
|
128
|
+
}
|
|
129
|
+
if (scopes.has('settings')) {
|
|
130
|
+
builders.settings(pkg, config, c);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return c.json(pkg);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function createPackageRoute(app: Hono, basePath: string, config: ToolServerConfig) {
|
|
138
|
+
|
|
139
|
+
app.get(basePath, (c: Context) => {
|
|
140
|
+
return handlePackageRequest(c, config);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
app.post(basePath, (c: Context) => {
|
|
144
|
+
return handlePackageRequest(c, config);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// ================== Content Type Endpoints ==================
|
|
2
|
+
|
|
3
|
+
import { InCodeTypeDefinition } from "@vertesia/common";
|
|
4
|
+
import { Context, Hono } from "hono";
|
|
5
|
+
import { HTTPException } from "hono/http-exception";
|
|
6
|
+
import { ContentTypesCollection } from "../ContentTypesCollection.js";
|
|
7
|
+
import { ToolServerConfig } from "./types.js";
|
|
8
|
+
import { toPathName } from "../utils.js";
|
|
9
|
+
|
|
10
|
+
export function createContentTypesRoute(app: Hono, basePath: string, config: ToolServerConfig) {
|
|
11
|
+
const { types = [] } = config;
|
|
12
|
+
|
|
13
|
+
// GET /api/types - Returns all interactions from all collections
|
|
14
|
+
app.get(basePath, (c) => {
|
|
15
|
+
const allTypes: InCodeTypeDefinition[] = [];
|
|
16
|
+
|
|
17
|
+
for (const coll of types) {
|
|
18
|
+
for (const type of coll.types) {
|
|
19
|
+
allTypes.push({ ...type, id: coll.name + ":" + toPathName(type.name) });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return c.json({
|
|
24
|
+
title: 'All Content Types',
|
|
25
|
+
description: 'All available content types across all collections',
|
|
26
|
+
types: allTypes,
|
|
27
|
+
collections: types.map(i => ({
|
|
28
|
+
name: i.name,
|
|
29
|
+
title: i.title,
|
|
30
|
+
description: i.description,
|
|
31
|
+
})),
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Create interaction collection endpoints
|
|
36
|
+
for (const coll of types) {
|
|
37
|
+
app.route(`${basePath}/${coll.name}`, createContentTypeEndpoints(coll));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// GET /api/types/:name - Direct access to content type
|
|
41
|
+
app.get(`${basePath}/:name`, async (c) => {
|
|
42
|
+
const name = c.req.param('name');
|
|
43
|
+
const parts = name.split(':');
|
|
44
|
+
if (parts.length !== 2) {
|
|
45
|
+
throw new HTTPException(400, {
|
|
46
|
+
message: "Invalid content type name. Expected format 'collection:type'"
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const collName = parts[0];
|
|
50
|
+
const typeName = toPathName(parts[1]);
|
|
51
|
+
const ctype = types.find(t => t.name === collName)?.getTypeByName(typeName);
|
|
52
|
+
if (ctype) {
|
|
53
|
+
return c.json({ ...ctype, id: collName + ":" + typeName });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
throw new HTTPException(404, {
|
|
57
|
+
message: "No content type found with name: " + name
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
function createContentTypeEndpoints(coll: ContentTypesCollection): Hono {
|
|
67
|
+
const endpoint = new Hono();
|
|
68
|
+
|
|
69
|
+
endpoint.get('/', (c: Context) => {
|
|
70
|
+
return c.json(coll.types.map(t => ({ ...t, id: coll.name + ":" + toPathName(t.name) })));
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
endpoint.get('/:name', (c: Context) => {
|
|
74
|
+
const name = c.req.param('name');
|
|
75
|
+
const ctype = coll.types.find(t => toPathName(t.name) === name);
|
|
76
|
+
if (!ctype) {
|
|
77
|
+
throw new HTTPException(404, {
|
|
78
|
+
message: "No content type found with name: " + name
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return c.json({
|
|
82
|
+
...ctype,
|
|
83
|
+
id: coll.name + ":" + toPathName(ctype.name)
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
return endpoint;
|
|
89
|
+
}
|
|
@@ -18,7 +18,7 @@ export function createInteractionsRoute(app: Hono, basePath: string, config: Too
|
|
|
18
18
|
for (const inter of coll.interactions) {
|
|
19
19
|
allInteractions.push({
|
|
20
20
|
type: "app",
|
|
21
|
-
id: inter.name,
|
|
21
|
+
id: coll.name + ":" + inter.name,
|
|
22
22
|
name: inter.name,
|
|
23
23
|
title: inter.title || inter.name,
|
|
24
24
|
description: inter.description,
|
|
@@ -44,6 +44,29 @@ export function createInteractionsRoute(app: Hono, basePath: string, config: Too
|
|
|
44
44
|
app.route(`${basePath}/${coll.name}`, createInteractionEndpoints(coll));
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
// GET /api/interactions/:name - Direct access to interaction by name (searches all collections)
|
|
48
|
+
app.get(`${basePath}/:name`, async (c) => {
|
|
49
|
+
await authorize(c);
|
|
50
|
+
const name = c.req.param('name');
|
|
51
|
+
|
|
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 });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
throw new HTTPException(404, {
|
|
66
|
+
message: "No interaction found with name: " + name
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
47
70
|
}
|
|
48
71
|
|
|
49
72
|
|
|
@@ -55,7 +78,7 @@ function createInteractionEndpoints(coll: InteractionCollection): Hono {
|
|
|
55
78
|
endpoint.get('/', (c: Context) => {
|
|
56
79
|
return c.json(coll.interactions.map(inter => ({
|
|
57
80
|
type: "app",
|
|
58
|
-
id: inter.name,
|
|
81
|
+
id: coll.name + ":" + inter.name,
|
|
59
82
|
name: inter.name,
|
|
60
83
|
title: inter.title || inter.name,
|
|
61
84
|
description: inter.description,
|
|
@@ -72,7 +95,10 @@ function createInteractionEndpoints(coll: InteractionCollection): Hono {
|
|
|
72
95
|
message: "No interaction found with name: " + name
|
|
73
96
|
});
|
|
74
97
|
}
|
|
75
|
-
return c.json(
|
|
98
|
+
return c.json({
|
|
99
|
+
...inter,
|
|
100
|
+
id: coll.name + ":" + inter.name,
|
|
101
|
+
});
|
|
76
102
|
});
|
|
77
103
|
|
|
78
104
|
return endpoint;
|
package/src/server/site.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import {
|
|
3
|
+
contentTypeCollectionPage,
|
|
3
4
|
indexPage,
|
|
4
5
|
interactionCollectionPage,
|
|
5
6
|
skillCollectionPage,
|
|
@@ -12,6 +13,7 @@ export function createSiteRoute(app: Hono, basePath: string, config: ToolServerC
|
|
|
12
13
|
const {
|
|
13
14
|
tools = [],
|
|
14
15
|
interactions = [],
|
|
16
|
+
types = [],
|
|
15
17
|
skills = [],
|
|
16
18
|
} = config;
|
|
17
19
|
|
|
@@ -41,4 +43,11 @@ export function createSiteRoute(app: Hono, basePath: string, config: ToolServerC
|
|
|
41
43
|
});
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
// Content type collection pages
|
|
47
|
+
for (const coll of types) {
|
|
48
|
+
app.get(`${basePath}/types/${coll.name}`, (c) => {
|
|
49
|
+
return c.html(contentTypeCollectionPage(coll));
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
44
53
|
}
|
package/src/server/tools.ts
CHANGED
|
@@ -22,20 +22,11 @@ export function createToolsRoute(app: Hono, basePath: string, config: ToolServer
|
|
|
22
22
|
|
|
23
23
|
app.get(basePath, (c) => {
|
|
24
24
|
const url = new URL(c.req.url);
|
|
25
|
-
const defaultOnly = c.req.query('defaultOnly') === 'true';
|
|
26
|
-
const unlockedParam = c.req.query('unlocked');
|
|
27
|
-
const unlockedTools = unlockedParam ? unlockedParam.split(',').map(t => t.trim()).filter(Boolean) : [];
|
|
28
|
-
|
|
29
|
-
const filterOptions = defaultOnly ? { defaultOnly, unlockedTools } : undefined;
|
|
30
25
|
|
|
31
26
|
const allTools: ToolDefinition[] = [];
|
|
32
|
-
let reserveToolCount = 0;
|
|
33
27
|
|
|
34
28
|
for (const coll of tools) {
|
|
35
|
-
allTools.push(...coll.getToolDefinitions(
|
|
36
|
-
if (defaultOnly) {
|
|
37
|
-
reserveToolCount += coll.getReserveTools(unlockedTools).length;
|
|
38
|
-
}
|
|
29
|
+
allTools.push(...coll.getToolDefinitions());
|
|
39
30
|
}
|
|
40
31
|
|
|
41
32
|
return c.json({
|
|
@@ -43,7 +34,6 @@ export function createToolsRoute(app: Hono, basePath: string, config: ToolServer
|
|
|
43
34
|
title: 'All Tools',
|
|
44
35
|
description: 'All available tools across all collections',
|
|
45
36
|
tools: allTools,
|
|
46
|
-
reserveToolCount: defaultOnly ? reserveToolCount : undefined,
|
|
47
37
|
collections: tools.map(t => ({
|
|
48
38
|
name: t.name,
|
|
49
39
|
title: t.title,
|
|
@@ -97,27 +87,18 @@ function createToolEndpoints(coll: ToolCollection): Hono {
|
|
|
97
87
|
// - unlocked=tool1,tool2: Comma-separated list of unlocked tool names
|
|
98
88
|
endpoint.get('/', (c) => {
|
|
99
89
|
const importSourceUrl = c.req.query('import') != null;
|
|
100
|
-
const defaultOnly = c.req.query('defaultOnly') === 'true';
|
|
101
|
-
const unlockedParam = c.req.query('unlocked');
|
|
102
|
-
const unlockedTools = unlockedParam ? unlockedParam.split(',').map(t => t.trim()).filter(Boolean) : [];
|
|
103
90
|
|
|
104
|
-
const filterOptions = defaultOnly ? { defaultOnly, unlockedTools } : undefined;
|
|
105
91
|
const url = new URL(c.req.url);
|
|
106
92
|
|
|
107
|
-
const response: ToolCollectionDefinition
|
|
93
|
+
const response: ToolCollectionDefinition = {
|
|
108
94
|
src: importSourceUrl
|
|
109
95
|
? `${url.origin}/libs/vertesia-tools-${coll.name}.js`
|
|
110
96
|
: `${url.origin}${url.pathname}`,
|
|
111
97
|
title: coll.title || coll.name,
|
|
112
98
|
description: coll.description || '',
|
|
113
|
-
tools: coll.getToolDefinitions(
|
|
99
|
+
tools: coll.getToolDefinitions()
|
|
114
100
|
};
|
|
115
101
|
|
|
116
|
-
// Include reserve count when filtering
|
|
117
|
-
if (defaultOnly) {
|
|
118
|
-
response.reserveToolCount = coll.getReserveTools(unlockedTools).length;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
102
|
return c.json(response);
|
|
122
103
|
});
|
|
123
104
|
|
package/src/server/types.ts
CHANGED
|
@@ -3,6 +3,9 @@ import { InteractionCollection } from "../InteractionCollection.js";
|
|
|
3
3
|
import { SkillCollection } from "../SkillCollection.js";
|
|
4
4
|
import { ToolCollection } from "../ToolCollection.js";
|
|
5
5
|
import { ToolExecutionPayload } from "../types.js";
|
|
6
|
+
import { JSONSchema } from "@llumiverse/common";
|
|
7
|
+
import { AppUIConfig, ProjectConfiguration } from "@vertesia/common";
|
|
8
|
+
import { ContentTypesCollection } from "../ContentTypesCollection.js";
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* Extended context with parsed payload for tool/skill execution
|
|
@@ -49,6 +52,10 @@ export interface ToolServerConfig {
|
|
|
49
52
|
* Interaction collections to expose
|
|
50
53
|
*/
|
|
51
54
|
interactions?: InteractionCollection[];
|
|
55
|
+
/**
|
|
56
|
+
* Content type collections to expose
|
|
57
|
+
*/
|
|
58
|
+
types?: ContentTypesCollection[];
|
|
52
59
|
/**
|
|
53
60
|
* Skill collections to expose
|
|
54
61
|
*/
|
|
@@ -57,6 +64,17 @@ export interface ToolServerConfig {
|
|
|
57
64
|
* MCP providers to expose
|
|
58
65
|
*/
|
|
59
66
|
mcpProviders?: MCPProviderConfig[];
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A JSON schema defining settings for the application using this server
|
|
70
|
+
*/
|
|
71
|
+
settings?: JSONSchema;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The UI configuration for the application using this server
|
|
75
|
+
*/
|
|
76
|
+
uiConfig?: AppUIConfig;
|
|
77
|
+
|
|
60
78
|
/**
|
|
61
79
|
* Disable HTML pages (default: false)
|
|
62
80
|
*/
|
|
@@ -65,5 +83,13 @@ export interface ToolServerConfig {
|
|
|
65
83
|
* Hide UI app links on the index page (default: false)
|
|
66
84
|
*/
|
|
67
85
|
hideUILinks?: boolean;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* If a filter is provided, it will be called with the project configuration when requesting tool definitions, and can be used to filter which tools are returned based on the project configuration.
|
|
89
|
+
* This allows for dynamic enabling/disabling of tools based on project settings.
|
|
90
|
+
* @param projectConfig
|
|
91
|
+
* @returns
|
|
92
|
+
*/
|
|
93
|
+
toolFilter?: (projectConfig: ProjectConfiguration) => boolean;
|
|
68
94
|
}
|
|
69
95
|
|
package/src/server/widgets.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
+
import { AppWidgetInfo } from "@vertesia/common";
|
|
1
2
|
import { Hono } from "hono";
|
|
2
3
|
import { ToolServerConfig } from "./types.js";
|
|
3
4
|
|
|
4
|
-
export interface WidgetInfo {
|
|
5
|
-
collection: string;
|
|
6
|
-
skill: string;
|
|
7
|
-
url: string;
|
|
8
|
-
}
|
|
9
5
|
|
|
10
6
|
export function createWidgetsRoute(app: Hono, basePath: string, config: ToolServerConfig) {
|
|
11
7
|
|
|
@@ -15,7 +11,7 @@ export function createWidgetsRoute(app: Hono, basePath: string, config: ToolServ
|
|
|
15
11
|
app.get(basePath, (c) => {
|
|
16
12
|
const url = new URL(c.req.url);
|
|
17
13
|
|
|
18
|
-
const widgets: Record<string,
|
|
14
|
+
const widgets: Record<string, AppWidgetInfo> = {};
|
|
19
15
|
for (const coll of skills) {
|
|
20
16
|
const collWidgets = coll.getWidgets();
|
|
21
17
|
for (const widget of collWidgets) {
|
package/src/server.ts
CHANGED
|
@@ -10,6 +10,8 @@ import { createToolsRoute } from "./server/tools.js";
|
|
|
10
10
|
import { ToolContext, ToolServerConfig } from "./server/types.js";
|
|
11
11
|
import { ToolExecutionPayload } from "./types.js";
|
|
12
12
|
import { createWidgetsRoute } from "./server/widgets.js";
|
|
13
|
+
import { createPackageRoute } from "./server/app-package.js";
|
|
14
|
+
import { createContentTypesRoute } from "./server/content-types.js";
|
|
13
15
|
|
|
14
16
|
// Schema for tool execution payload
|
|
15
17
|
const ToolExecutionPayloadSchema = z.object({
|
|
@@ -98,10 +100,13 @@ export function createToolServer(config: ToolServerConfig): Hono {
|
|
|
98
100
|
});
|
|
99
101
|
});
|
|
100
102
|
|
|
103
|
+
|
|
104
|
+
createPackageRoute(app, `${prefix}/package`, config);
|
|
101
105
|
createToolsRoute(app, `${prefix}/tools`, config);
|
|
102
106
|
createSkillsRoute(app, `${prefix}/skills`, config);
|
|
103
107
|
createWidgetsRoute(app, `${prefix}/widgets`, config);
|
|
104
108
|
createInteractionsRoute(app, `${prefix}/interactions`, config);
|
|
109
|
+
createContentTypesRoute(app, `${prefix}/types`, config);
|
|
105
110
|
createMcpRoute(app, `${prefix}/mcp`, config);
|
|
106
111
|
|
|
107
112
|
|
package/src/site/styles.ts
CHANGED
|
@@ -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;
|