@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 CHANGED
@@ -1,39 +1,44 @@
1
1
  {
2
- "name": "@vertesia/tools-sdk",
3
- "version": "0.79.1",
4
- "description": "Tools SDK - utilities for building remote tools",
5
- "type": "module",
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
- "files": [
8
- "lib",
9
- "src"
10
- ],
11
- "license": "Apache-2.0",
12
- "scripts": {
13
- "test": "vitest run",
14
- "build": "pnpm exec tsmod build",
15
- "clean": "rimraf ./node_modules ./lib ./tsconfig.tsbuildinfo"
16
- },
17
- "exports": {
18
- "types": "./lib/types/index.d.ts",
19
- "import": "./lib/esm/index.js",
20
- "require": "./lib/cjs/index.js"
21
- },
22
- "devDependencies": {
23
- "ts-dual-module": "^0.6.3",
24
- "typescript": "^5.0.2",
25
- "vitest": "^3.0.9"
26
- },
27
- "ts_dual_module": {
28
- "outDir": "lib"
29
- },
30
- "peerDependencies": {
31
- "hono": "^4.0.0"
32
- },
33
- "dependencies": {
34
- "@llumiverse/common": "workspace:*",
35
- "@vertesia/client": "workspace:*",
36
- "@vertesia/common": "workspace:*",
37
- "jose": "^6.0.11"
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
+ }
@@ -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 Iterable<Tool<any>> {
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: string;
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
- const { studio } = decodeEndpoints(decodedJwt.endpoints as any);
28
- if (!studio) {
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
- const jwks = await getJwks(`${studio}/.well-known/jwks`);
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
@@ -1,4 +1,5 @@
1
+ export { authorize, AuthSession } from "./auth.js";
2
+ export * from "./InteractionCollection.js";
1
3
  export * from "./ToolCollection.js";
2
4
  export * from "./ToolRegistry.js";
3
5
  export * from "./types.js";
4
- export { AuthSession, authorize } from "./auth.js";
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
@@ -0,0 +1,3 @@
1
+ export function kebabCaseToTitle(name: string) {
2
+ return name.split('-').map(p => p[0].toUpperCase() + p.substring(1)).join(' ');
3
+ }