@vertesia/tools-sdk 0.79.1 → 0.80.0-dev.20251121
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/package.json +42 -37
- package/src/InteractionCollection.ts +53 -0
- package/src/ToolCollection.ts +6 -28
- package/src/auth.ts +12 -7
- package/src/index.ts +2 -1
- package/src/types.ts +24 -0
- package/src/utils.ts +3 -0
package/package.json
CHANGED
|
@@ -1,39 +1,44 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
"name": "@vertesia/tools-sdk",
|
|
3
|
+
"version": "0.80.0-dev.20251121",
|
|
4
|
+
"description": "Tools SDK - utilities for building remote tools",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"types": "./lib/types/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"lib",
|
|
9
|
+
"src"
|
|
10
|
+
],
|
|
11
|
+
"license": "Apache-2.0",
|
|
12
|
+
"exports": {
|
|
6
13
|
"types": "./lib/types/index.d.ts",
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
}
|
|
14
|
+
"import": "./lib/esm/index.js",
|
|
15
|
+
"require": "./lib/cjs/index.js"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"ts-dual-module": "^0.6.3",
|
|
19
|
+
"typescript": "^5.0.2",
|
|
20
|
+
"vitest": "^3.0.9"
|
|
21
|
+
},
|
|
22
|
+
"ts_dual_module": {
|
|
23
|
+
"outDir": "lib"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"hono": "^4.9.6"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"jose": "^6.0.11",
|
|
30
|
+
"@vertesia/common": "0.80.0-dev.20251121",
|
|
31
|
+
"@llumiverse/common": "0.23.0-dev.20251121",
|
|
32
|
+
"@vertesia/client": "0.80.0-dev.20251121"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/vertesia/composableai.git",
|
|
37
|
+
"directory": "packages/tools-sdk"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"build": "pnpm exec tsmod build",
|
|
42
|
+
"clean": "rimraf ./node_modules ./lib ./tsconfig.tsbuildinfo"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { InteractionSpec } from "@vertesia/common";
|
|
2
|
+
import { ICollection, CollectionProperties } from "./types.js";
|
|
3
|
+
import { kebabCaseToTitle } from "./utils.js";
|
|
4
|
+
|
|
5
|
+
export interface InteractionCollectionProps extends CollectionProperties {
|
|
6
|
+
interactions: InteractionSpec[];
|
|
7
|
+
}
|
|
8
|
+
export class InteractionCollection implements ICollection<InteractionSpec> {
|
|
9
|
+
interactions: InteractionSpec[];
|
|
10
|
+
name: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
icon?: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
constructor({
|
|
15
|
+
name, title, icon, description, interactions
|
|
16
|
+
}: InteractionCollectionProps) {
|
|
17
|
+
this.name = name;
|
|
18
|
+
this.title = title || kebabCaseToTitle(name);
|
|
19
|
+
this.icon = icon;
|
|
20
|
+
this.description = description;
|
|
21
|
+
this.interactions = interactions;
|
|
22
|
+
}
|
|
23
|
+
addInteraction(interaction: any) {
|
|
24
|
+
this.interactions.push(interaction);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getInteractions() {
|
|
28
|
+
return this.interactions;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
[Symbol.iterator](): Iterator<InteractionSpec> {
|
|
32
|
+
let index = 0;
|
|
33
|
+
const interactions = this.interactions;
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
next(): IteratorResult<InteractionSpec> {
|
|
37
|
+
if (index < interactions.length) {
|
|
38
|
+
return { value: interactions[index++], done: false };
|
|
39
|
+
} else {
|
|
40
|
+
return { done: true, value: undefined };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
map<U>(callback: (interaction: InteractionSpec, index: number) => U): U[] {
|
|
47
|
+
return this.interactions.map(callback);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
getInteractionByName(name: string): InteractionSpec | undefined {
|
|
51
|
+
return this.interactions.find(interaction => interaction.name === name);
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/ToolCollection.ts
CHANGED
|
@@ -2,29 +2,10 @@ import { Context } from "hono";
|
|
|
2
2
|
import { HTTPException } from "hono/http-exception";
|
|
3
3
|
import { authorize } from "./auth.js";
|
|
4
4
|
import { ToolRegistry } from "./ToolRegistry.js";
|
|
5
|
-
import type { Tool, ToolDefinition, ToolExecutionPayload, ToolExecutionResponse, ToolExecutionResponseError } from "./types.js";
|
|
6
|
-
|
|
7
|
-
export interface ToolCollectionProperties {
|
|
8
|
-
/**
|
|
9
|
-
* A kebab case collection name. Must only contains alphanumeric and dash characters,
|
|
10
|
-
* The name can be used to generate the path where the collection is exposed.
|
|
11
|
-
* Example: my-collection
|
|
12
|
-
*/
|
|
13
|
-
name: string;
|
|
14
|
-
/**
|
|
15
|
-
* Optional title for UI display.
|
|
16
|
-
* If not provided the pascal case version of the name will be used
|
|
17
|
-
*/
|
|
18
|
-
title?: string;
|
|
19
|
-
/**
|
|
20
|
-
* Optional icon for UI display
|
|
21
|
-
*/
|
|
22
|
-
icon?: string;
|
|
23
|
-
/**
|
|
24
|
-
* A short description
|
|
25
|
-
*/
|
|
26
|
-
description: string;
|
|
5
|
+
import type { CollectionProperties, ICollection, Tool, ToolDefinition, ToolExecutionPayload, ToolExecutionResponse, ToolExecutionResponseError } from "./types.js";
|
|
6
|
+
import { kebabCaseToTitle } from "./utils.js";
|
|
27
7
|
|
|
8
|
+
export interface ToolCollectionProperties extends CollectionProperties {
|
|
28
9
|
/**
|
|
29
10
|
* The tools
|
|
30
11
|
*/
|
|
@@ -34,7 +15,7 @@ export interface ToolCollectionProperties {
|
|
|
34
15
|
/**
|
|
35
16
|
* Implements a tools collection endpoint
|
|
36
17
|
*/
|
|
37
|
-
export class ToolCollection implements
|
|
18
|
+
export class ToolCollection implements ICollection<Tool<any>> {
|
|
38
19
|
|
|
39
20
|
/**
|
|
40
21
|
* A kebab case collection name. Must only contains alphanumeric and dash characters,
|
|
@@ -54,7 +35,7 @@ export class ToolCollection implements Iterable<Tool<any>> {
|
|
|
54
35
|
/**
|
|
55
36
|
* A short description
|
|
56
37
|
*/
|
|
57
|
-
description
|
|
38
|
+
description?: string;
|
|
58
39
|
/**
|
|
59
40
|
* The tool registry
|
|
60
41
|
*/
|
|
@@ -89,7 +70,7 @@ export class ToolCollection implements Iterable<Tool<any>> {
|
|
|
89
70
|
return this.tools.getTools().map(callback);
|
|
90
71
|
}
|
|
91
72
|
|
|
92
|
-
async execute(ctx: Context) {
|
|
73
|
+
async execute(ctx: Context): Promise<Response> {
|
|
93
74
|
let payload: ToolExecutionPayload<any> | undefined;
|
|
94
75
|
try {
|
|
95
76
|
payload = await readPayload(ctx);
|
|
@@ -126,6 +107,3 @@ async function readPayload(ctx: Context) {
|
|
|
126
107
|
}
|
|
127
108
|
}
|
|
128
109
|
|
|
129
|
-
function kebabCaseToTitle(name: string) {
|
|
130
|
-
return name.split('-').map(p => p[0].toUpperCase() + p.substring(1)).join(' ');
|
|
131
|
-
}
|
package/src/auth.ts
CHANGED
|
@@ -24,21 +24,22 @@ export async function getJwks(url: string) {
|
|
|
24
24
|
|
|
25
25
|
export async function verifyToken(token: string) {
|
|
26
26
|
const decodedJwt = decodeJwt(token);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
throw new Error("No studio endpoint found in JWT");
|
|
27
|
+
if (!decodedJwt.iss) {
|
|
28
|
+
throw new Error("No issuer URL found in JWT");
|
|
30
29
|
}
|
|
31
|
-
|
|
30
|
+
if (!isAllowedIssuer(decodedJwt.iss)) {
|
|
31
|
+
throw new Error("Issuer is not allowed: " + decodedJwt.iss);
|
|
32
|
+
}
|
|
33
|
+
const jwks = await getJwks(`${decodedJwt.iss}/.well-known/jwks`);
|
|
32
34
|
return await jwtVerify<AuthTokenPayload>(token, jwks);
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
|
|
36
|
-
|
|
37
38
|
export async function authorize(ctx: Context) {
|
|
38
39
|
const auth = ctx.req.header('Authorization');
|
|
39
40
|
if (!auth) {
|
|
40
41
|
throw new HTTPException(401, {
|
|
41
|
-
message: `Missing Authorization header
|
|
42
|
+
message: `Missing Authorization header`
|
|
42
43
|
});
|
|
43
44
|
}
|
|
44
45
|
const [scheme, value] = auth.trim().split(' ');
|
|
@@ -84,4 +85,8 @@ export class AuthSession implements ToolExecutionContext {
|
|
|
84
85
|
}
|
|
85
86
|
return this._client;
|
|
86
87
|
}
|
|
87
|
-
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function isAllowedIssuer(iss: string) {
|
|
91
|
+
return iss.endsWith(".vertesia.io") || iss.endsWith(".becomposable.com");
|
|
92
|
+
}
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -2,6 +2,30 @@ import type { ToolDefinition, ToolUse } from "@llumiverse/common";
|
|
|
2
2
|
import { VertesiaClient } from "@vertesia/client";
|
|
3
3
|
import { AuthTokenPayload, ToolResult, ToolResultContent } from "@vertesia/common";
|
|
4
4
|
|
|
5
|
+
export type ICollection<T = any> = CollectionProperties & Iterable<T>
|
|
6
|
+
|
|
7
|
+
export interface CollectionProperties {
|
|
8
|
+
/**
|
|
9
|
+
* A kebab case collection name. Must only contains alphanumeric and dash characters,
|
|
10
|
+
* The name can be used to generate the path where the collection is exposed.
|
|
11
|
+
* Example: my-collection
|
|
12
|
+
*/
|
|
13
|
+
name: string;
|
|
14
|
+
/**
|
|
15
|
+
* Optional title for UI display.
|
|
16
|
+
* If not provided the pascal case version of the name will be used
|
|
17
|
+
*/
|
|
18
|
+
title?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Optional icon for UI display
|
|
21
|
+
*/
|
|
22
|
+
icon?: string;
|
|
23
|
+
/**
|
|
24
|
+
* A short description
|
|
25
|
+
*/
|
|
26
|
+
description?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
5
29
|
export interface ToolExecutionContext {
|
|
6
30
|
/**
|
|
7
31
|
* The raw JWT token to the tool execution request
|
package/src/utils.ts
ADDED