@vertesia/tools-sdk 0.74.0

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 (45) hide show
  1. package/LICENSE +13 -0
  2. package/lib/cjs/ToolCollection.js +96 -0
  3. package/lib/cjs/ToolCollection.js.map +1 -0
  4. package/lib/cjs/ToolRegistry.js +44 -0
  5. package/lib/cjs/ToolRegistry.js.map +1 -0
  6. package/lib/cjs/auth.js +84 -0
  7. package/lib/cjs/auth.js.map +1 -0
  8. package/lib/cjs/build/validate.js +7 -0
  9. package/lib/cjs/build/validate.js.map +1 -0
  10. package/lib/cjs/index.js +20 -0
  11. package/lib/cjs/index.js.map +1 -0
  12. package/lib/cjs/package.json +3 -0
  13. package/lib/cjs/types.js +3 -0
  14. package/lib/cjs/types.js.map +1 -0
  15. package/lib/esm/ToolCollection.js +92 -0
  16. package/lib/esm/ToolCollection.js.map +1 -0
  17. package/lib/esm/ToolRegistry.js +39 -0
  18. package/lib/esm/ToolRegistry.js.map +1 -0
  19. package/lib/esm/auth.js +77 -0
  20. package/lib/esm/auth.js.map +1 -0
  21. package/lib/esm/build/validate.js +4 -0
  22. package/lib/esm/build/validate.js.map +1 -0
  23. package/lib/esm/index.js +4 -0
  24. package/lib/esm/index.js.map +1 -0
  25. package/lib/esm/types.js +2 -0
  26. package/lib/esm/types.js.map +1 -0
  27. package/lib/types/ToolCollection.d.ts +73 -0
  28. package/lib/types/ToolCollection.d.ts.map +1 -0
  29. package/lib/types/ToolRegistry.d.ts +15 -0
  30. package/lib/types/ToolRegistry.d.ts.map +1 -0
  31. package/lib/types/auth.d.ts +20 -0
  32. package/lib/types/auth.d.ts.map +1 -0
  33. package/lib/types/build/validate.d.ts +2 -0
  34. package/lib/types/build/validate.d.ts.map +1 -0
  35. package/lib/types/index.d.ts +4 -0
  36. package/lib/types/index.d.ts.map +1 -0
  37. package/lib/types/types.d.ts +70 -0
  38. package/lib/types/types.d.ts.map +1 -0
  39. package/package.json +39 -0
  40. package/src/ToolCollection.ts +131 -0
  41. package/src/ToolRegistry.ts +49 -0
  42. package/src/auth.ts +87 -0
  43. package/src/build/validate.ts +3 -0
  44. package/src/index.ts +3 -0
  45. package/src/types.ts +78 -0
package/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2024 Composable
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ToolCollection = void 0;
4
+ const http_exception_1 = require("hono/http-exception");
5
+ const auth_js_1 = require("./auth.js");
6
+ const ToolRegistry_js_1 = require("./ToolRegistry.js");
7
+ /**
8
+ * Implements a tools colection endpoint
9
+ */
10
+ class ToolCollection {
11
+ /**
12
+ * A kebab case collection name. Must only contains alphanumeric and dash characters,
13
+ * The name can be used to generate the path where the collection is exposed.
14
+ * Example: my-collection
15
+ */
16
+ name;
17
+ /**
18
+ * Optional title for UI display.
19
+ * If not provided the title will be generated form the kebab case name by replacing - with spaces and upper casing first letter in words.
20
+ */
21
+ title;
22
+ /**
23
+ * Optional icon for UI display
24
+ */
25
+ icon;
26
+ /**
27
+ * A short description
28
+ */
29
+ description;
30
+ /**
31
+ * The tool registry
32
+ */
33
+ tools;
34
+ constructor({ name, title, icon, description, tools }) {
35
+ this.name = name;
36
+ this.title = title || kebabCaseToTitle(name);
37
+ this.icon = icon;
38
+ this.description = description;
39
+ this.tools = new ToolRegistry_js_1.ToolRegistry(tools);
40
+ }
41
+ [Symbol.iterator]() {
42
+ let index = 0;
43
+ const tools = this.tools.getTools();
44
+ return {
45
+ next() {
46
+ if (index < tools.length) {
47
+ return { value: tools[index++], done: false };
48
+ }
49
+ else {
50
+ return { done: true, value: undefined };
51
+ }
52
+ }
53
+ };
54
+ }
55
+ map(callback) {
56
+ return this.tools.getTools().map(callback);
57
+ }
58
+ async execute(ctx) {
59
+ let payload;
60
+ try {
61
+ payload = await readPayload(ctx);
62
+ const session = await (0, auth_js_1.authorize)(ctx);
63
+ const r = await this.tools.runTool(payload, session);
64
+ return ctx.json({
65
+ ...r,
66
+ tool_use_id: payload.tool_use.id
67
+ });
68
+ }
69
+ catch (err) { // HTTPException ?
70
+ const status = err.status || 500;
71
+ return ctx.json({
72
+ tool_use_id: payload?.tool_use.id || "undefined",
73
+ error: err.message || "Error executing tool",
74
+ status
75
+ }, status);
76
+ }
77
+ }
78
+ getToolDefinitions() {
79
+ return this.tools.getDefinitions();
80
+ }
81
+ }
82
+ exports.ToolCollection = ToolCollection;
83
+ async function readPayload(ctx) {
84
+ try {
85
+ return await ctx.req.json();
86
+ }
87
+ catch (err) {
88
+ throw new http_exception_1.HTTPException(500, {
89
+ message: "Failed to load execution request payload: " + err.message
90
+ });
91
+ }
92
+ }
93
+ function kebabCaseToTitle(name) {
94
+ return name.split('-').map(p => p[0].toUpperCase() + p.substring(1)).join(' ');
95
+ }
96
+ //# sourceMappingURL=ToolCollection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolCollection.js","sourceRoot":"","sources":["../../src/ToolCollection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ToolNotFoundError = exports.ToolRegistry = void 0;
4
+ const http_exception_1 = require("hono/http-exception");
5
+ class ToolRegistry {
6
+ registry = {};
7
+ constructor(tools = []) {
8
+ for (const tool of tools) {
9
+ this.registry[tool.name] = tool;
10
+ }
11
+ }
12
+ getDefinitions() {
13
+ return Object.values(this.registry).map(tool => ({
14
+ name: tool.name,
15
+ description: tool.description,
16
+ input_schema: tool.input_schema
17
+ }));
18
+ }
19
+ getTool(name) {
20
+ const tool = this.registry[name];
21
+ if (tool === undefined) {
22
+ throw new ToolNotFoundError(name);
23
+ }
24
+ return tool;
25
+ }
26
+ getTools() {
27
+ return Object.values(this.registry);
28
+ }
29
+ registerTool(tool) {
30
+ this.registry[tool.name] = tool;
31
+ }
32
+ runTool(payload, context) {
33
+ return this.getTool(payload.tool_use.tool_name).run(payload, context);
34
+ }
35
+ }
36
+ exports.ToolRegistry = ToolRegistry;
37
+ class ToolNotFoundError extends http_exception_1.HTTPException {
38
+ constructor(name) {
39
+ super(404, { message: "Tool function not found: " + name });
40
+ this.name = "ToolNotFoundError";
41
+ }
42
+ }
43
+ exports.ToolNotFoundError = ToolNotFoundError;
44
+ //# sourceMappingURL=ToolRegistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolRegistry.js","sourceRoot":"","sources":["../../src/ToolRegistry.ts"],"names":[],"mappings":";;;AAAA,wDAAoD;AAEpD,MAAa,YAAY;IAErB,QAAQ,GAA8B,EAAE,CAAC;IAEzC,YAAY,QAAqB,EAAE;QAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACpC,CAAC;IACL,CAAC;IAED,cAAc;QACV,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC7C,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,YAAY,EAAE,IAAI,CAAC,YAAY;SAClC,CAAC,CAAC,CAAC;IACR,CAAC;IAED,OAAO,CAAsC,IAAY;QACrD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QAChC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACrB,MAAM,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,QAAQ;QACJ,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,YAAY,CAAsC,IAAmB;QACjE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACpC,CAAC;IAED,OAAO,CAAsC,OAAsC,EAAE,OAA6B;QAC9G,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC1E,CAAC;CAEJ;AAtCD,oCAsCC;AAGD,MAAa,iBAAkB,SAAQ,8BAAa;IAChD,YAAY,IAAY;QACpB,KAAK,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,2BAA2B,GAAG,IAAI,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IACpC,CAAC;CACJ;AALD,8CAKC"}
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthSession = void 0;
4
+ exports.getJwks = getJwks;
5
+ exports.verifyToken = verifyToken;
6
+ exports.authorize = authorize;
7
+ const client_1 = require("@vertesia/client");
8
+ const http_exception_1 = require("hono/http-exception");
9
+ const jose_1 = require("jose");
10
+ const cache = {};
11
+ async function getJwks(url) {
12
+ if (!cache.url) {
13
+ console.log('JWKS cache miss for: ', url);
14
+ const jwks = await fetch(url).then(r => {
15
+ if (r.ok) {
16
+ return r.json();
17
+ }
18
+ throw new Error("Fetching jwks failed with code: " + r.status);
19
+ }).catch(err => {
20
+ throw new Error("Failed to fetch jwks: " + err.message);
21
+ });
22
+ cache.url = (0, jose_1.createLocalJWKSet)(jwks);
23
+ }
24
+ return cache.url;
25
+ }
26
+ async function verifyToken(token) {
27
+ const decodedJwt = (0, jose_1.decodeJwt)(token);
28
+ const { studio } = (0, client_1.decodeEndpoints)(decodedJwt.endpoints);
29
+ if (!studio) {
30
+ throw new Error("No studio endpoint found in JWT");
31
+ }
32
+ const jwks = await getJwks(`${studio}/.well-known/jwks`);
33
+ return await (0, jose_1.jwtVerify)(token, jwks);
34
+ }
35
+ async function authorize(ctx) {
36
+ const auth = ctx.req.header('Authorization');
37
+ if (!auth) {
38
+ throw new http_exception_1.HTTPException(401, {
39
+ message: `Missing Authorization header'`
40
+ });
41
+ }
42
+ const [scheme, value] = auth.trim().split(' ');
43
+ if (scheme.toLowerCase() !== 'bearer') {
44
+ throw new http_exception_1.HTTPException(401, {
45
+ message: `Authorization scheme ${scheme} is not supported`
46
+ });
47
+ }
48
+ if (!value) {
49
+ throw new http_exception_1.HTTPException(401, {
50
+ message: `Missing bearer token value`
51
+ });
52
+ }
53
+ try {
54
+ const { payload } = await verifyToken(value);
55
+ const session = new AuthSession(value, payload);
56
+ ctx.set("auth", session);
57
+ return session;
58
+ }
59
+ catch (err) {
60
+ throw new http_exception_1.HTTPException(401, {
61
+ message: err.message,
62
+ cause: err
63
+ });
64
+ }
65
+ }
66
+ class AuthSession {
67
+ token;
68
+ payload;
69
+ _client;
70
+ endpoints;
71
+ constructor(token, payload) {
72
+ this.token = token;
73
+ this.payload = payload;
74
+ this.endpoints = (0, client_1.decodeEndpoints)(payload.endpoints);
75
+ }
76
+ async getClient() {
77
+ if (!this._client) {
78
+ this._client = await client_1.VertesiaClient.fromAuthToken(this.token, this.payload);
79
+ }
80
+ return this._client;
81
+ }
82
+ }
83
+ exports.AuthSession = AuthSession;
84
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validate = validate;
4
+ function validate() {
5
+ //TODO
6
+ }
7
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/build/validate.ts"],"names":[],"mappings":";;AAAA,4BAEC;AAFD,SAAgB,QAAQ;IACpB,MAAM;AACV,CAAC"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./ToolCollection.js"), exports);
18
+ __exportStar(require("./ToolRegistry.js"), exports);
19
+ __exportStar(require("./types.js"), exports);
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,sDAAoC;AACpC,oDAAkC;AAClC,6CAA2B"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,92 @@
1
+ import { HTTPException } from "hono/http-exception";
2
+ import { authorize } from "./auth.js";
3
+ import { ToolRegistry } from "./ToolRegistry.js";
4
+ /**
5
+ * Implements a tools colection endpoint
6
+ */
7
+ export class ToolCollection {
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;
14
+ /**
15
+ * Optional title for UI display.
16
+ * If not provided the title will be generated form the kebab case name by replacing - with spaces and upper casing first letter in words.
17
+ */
18
+ title;
19
+ /**
20
+ * Optional icon for UI display
21
+ */
22
+ icon;
23
+ /**
24
+ * A short description
25
+ */
26
+ description;
27
+ /**
28
+ * The tool registry
29
+ */
30
+ tools;
31
+ constructor({ name, title, icon, description, tools }) {
32
+ this.name = name;
33
+ this.title = title || kebabCaseToTitle(name);
34
+ this.icon = icon;
35
+ this.description = description;
36
+ this.tools = new ToolRegistry(tools);
37
+ }
38
+ [Symbol.iterator]() {
39
+ let index = 0;
40
+ const tools = this.tools.getTools();
41
+ return {
42
+ next() {
43
+ if (index < tools.length) {
44
+ return { value: tools[index++], done: false };
45
+ }
46
+ else {
47
+ return { done: true, value: undefined };
48
+ }
49
+ }
50
+ };
51
+ }
52
+ map(callback) {
53
+ return this.tools.getTools().map(callback);
54
+ }
55
+ async execute(ctx) {
56
+ let payload;
57
+ try {
58
+ payload = await readPayload(ctx);
59
+ const session = await authorize(ctx);
60
+ const r = await this.tools.runTool(payload, session);
61
+ return ctx.json({
62
+ ...r,
63
+ tool_use_id: payload.tool_use.id
64
+ });
65
+ }
66
+ catch (err) { // HTTPException ?
67
+ const status = err.status || 500;
68
+ return ctx.json({
69
+ tool_use_id: payload?.tool_use.id || "undefined",
70
+ error: err.message || "Error executing tool",
71
+ status
72
+ }, status);
73
+ }
74
+ }
75
+ getToolDefinitions() {
76
+ return this.tools.getDefinitions();
77
+ }
78
+ }
79
+ async function readPayload(ctx) {
80
+ try {
81
+ return await ctx.req.json();
82
+ }
83
+ catch (err) {
84
+ throw new HTTPException(500, {
85
+ message: "Failed to load execution request payload: " + err.message
86
+ });
87
+ }
88
+ }
89
+ function kebabCaseToTitle(name) {
90
+ return name.split('-').map(p => p[0].toUpperCase() + p.substring(1)).join(' ');
91
+ }
92
+ //# sourceMappingURL=ToolCollection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolCollection.js","sourceRoot":"","sources":["../../src/ToolCollection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { HTTPException } from "hono/http-exception";
2
+ export class ToolRegistry {
3
+ registry = {};
4
+ constructor(tools = []) {
5
+ for (const tool of tools) {
6
+ this.registry[tool.name] = tool;
7
+ }
8
+ }
9
+ getDefinitions() {
10
+ return Object.values(this.registry).map(tool => ({
11
+ name: tool.name,
12
+ description: tool.description,
13
+ input_schema: tool.input_schema
14
+ }));
15
+ }
16
+ getTool(name) {
17
+ const tool = this.registry[name];
18
+ if (tool === undefined) {
19
+ throw new ToolNotFoundError(name);
20
+ }
21
+ return tool;
22
+ }
23
+ getTools() {
24
+ return Object.values(this.registry);
25
+ }
26
+ registerTool(tool) {
27
+ this.registry[tool.name] = tool;
28
+ }
29
+ runTool(payload, context) {
30
+ return this.getTool(payload.tool_use.tool_name).run(payload, context);
31
+ }
32
+ }
33
+ export class ToolNotFoundError extends HTTPException {
34
+ constructor(name) {
35
+ super(404, { message: "Tool function not found: " + name });
36
+ this.name = "ToolNotFoundError";
37
+ }
38
+ }
39
+ //# sourceMappingURL=ToolRegistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolRegistry.js","sourceRoot":"","sources":["../../src/ToolRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,OAAO,YAAY;IAErB,QAAQ,GAA8B,EAAE,CAAC;IAEzC,YAAY,QAAqB,EAAE;QAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACpC,CAAC;IACL,CAAC;IAED,cAAc;QACV,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC7C,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,YAAY,EAAE,IAAI,CAAC,YAAY;SAClC,CAAC,CAAC,CAAC;IACR,CAAC;IAED,OAAO,CAAsC,IAAY;QACrD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QAChC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACrB,MAAM,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,QAAQ;QACJ,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,YAAY,CAAsC,IAAmB;QACjE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACpC,CAAC;IAED,OAAO,CAAsC,OAAsC,EAAE,OAA6B;QAC9G,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC1E,CAAC;CAEJ;AAGD,MAAM,OAAO,iBAAkB,SAAQ,aAAa;IAChD,YAAY,IAAY;QACpB,KAAK,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,2BAA2B,GAAG,IAAI,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IACpC,CAAC;CACJ"}
@@ -0,0 +1,77 @@
1
+ import { decodeEndpoints, VertesiaClient } from "@vertesia/client";
2
+ import { HTTPException } from "hono/http-exception";
3
+ import { createLocalJWKSet, decodeJwt, jwtVerify } from "jose";
4
+ const cache = {};
5
+ export async function getJwks(url) {
6
+ if (!cache.url) {
7
+ console.log('JWKS cache miss for: ', url);
8
+ const jwks = await fetch(url).then(r => {
9
+ if (r.ok) {
10
+ return r.json();
11
+ }
12
+ throw new Error("Fetching jwks failed with code: " + r.status);
13
+ }).catch(err => {
14
+ throw new Error("Failed to fetch jwks: " + err.message);
15
+ });
16
+ cache.url = createLocalJWKSet(jwks);
17
+ }
18
+ return cache.url;
19
+ }
20
+ export async function verifyToken(token) {
21
+ const decodedJwt = decodeJwt(token);
22
+ const { studio } = decodeEndpoints(decodedJwt.endpoints);
23
+ if (!studio) {
24
+ throw new Error("No studio endpoint found in JWT");
25
+ }
26
+ const jwks = await getJwks(`${studio}/.well-known/jwks`);
27
+ return await jwtVerify(token, jwks);
28
+ }
29
+ export async function authorize(ctx) {
30
+ const auth = ctx.req.header('Authorization');
31
+ if (!auth) {
32
+ throw new HTTPException(401, {
33
+ message: `Missing Authorization header'`
34
+ });
35
+ }
36
+ const [scheme, value] = auth.trim().split(' ');
37
+ if (scheme.toLowerCase() !== 'bearer') {
38
+ throw new HTTPException(401, {
39
+ message: `Authorization scheme ${scheme} is not supported`
40
+ });
41
+ }
42
+ if (!value) {
43
+ throw new HTTPException(401, {
44
+ message: `Missing bearer token value`
45
+ });
46
+ }
47
+ try {
48
+ const { payload } = await verifyToken(value);
49
+ const session = new AuthSession(value, payload);
50
+ ctx.set("auth", session);
51
+ return session;
52
+ }
53
+ catch (err) {
54
+ throw new HTTPException(401, {
55
+ message: err.message,
56
+ cause: err
57
+ });
58
+ }
59
+ }
60
+ export class AuthSession {
61
+ token;
62
+ payload;
63
+ _client;
64
+ endpoints;
65
+ constructor(token, payload) {
66
+ this.token = token;
67
+ this.payload = payload;
68
+ this.endpoints = decodeEndpoints(payload.endpoints);
69
+ }
70
+ async getClient() {
71
+ if (!this._client) {
72
+ this._client = await VertesiaClient.fromAuthToken(this.token, this.payload);
73
+ }
74
+ return this._client;
75
+ }
76
+ }
77
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export function validate() {
2
+ //TODO
3
+ }
4
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/build/validate.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,QAAQ;IACpB,MAAM;AACV,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from "./ToolCollection.js";
2
+ export * from "./ToolRegistry.js";
3
+ export * from "./types.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,73 @@
1
+ import { Context } from "hono";
2
+ import { ToolRegistry } from "./ToolRegistry.js";
3
+ import type { Tool, ToolDefinition } from "./types.js";
4
+ export interface ToolCollectionProperties {
5
+ /**
6
+ * A kebab case collection name. Must only contains alphanumeric and dash characters,
7
+ * The name can be used to generate the path where the collection is exposed.
8
+ * Example: my-collection
9
+ */
10
+ name: string;
11
+ /**
12
+ * Optional title for UI display.
13
+ * If not provided the pascal case version of the name will be used
14
+ */
15
+ title?: string;
16
+ /**
17
+ * Optional icon for UI display
18
+ */
19
+ icon?: string;
20
+ /**
21
+ * A short description
22
+ */
23
+ description: string;
24
+ /**
25
+ * The tools
26
+ */
27
+ tools: Tool<any>[];
28
+ }
29
+ /**
30
+ * Implements a tools colection endpoint
31
+ */
32
+ export declare class ToolCollection implements Iterable<Tool<any>> {
33
+ /**
34
+ * A kebab case collection name. Must only contains alphanumeric and dash characters,
35
+ * The name can be used to generate the path where the collection is exposed.
36
+ * Example: my-collection
37
+ */
38
+ name: string;
39
+ /**
40
+ * Optional title for UI display.
41
+ * If not provided the title will be generated form the kebab case name by replacing - with spaces and upper casing first letter in words.
42
+ */
43
+ title?: string;
44
+ /**
45
+ * Optional icon for UI display
46
+ */
47
+ icon?: string;
48
+ /**
49
+ * A short description
50
+ */
51
+ description: string;
52
+ /**
53
+ * The tool registry
54
+ */
55
+ tools: ToolRegistry;
56
+ constructor({ name, title, icon, description, tools }: ToolCollectionProperties);
57
+ [Symbol.iterator](): Iterator<Tool<any>>;
58
+ map<U>(callback: (tool: Tool<any>, index: number) => U): U[];
59
+ execute(ctx: Context): Promise<(Response & import("hono").TypedResponse<{
60
+ tool_use_id: string;
61
+ metadata?: {
62
+ [x: string]: any;
63
+ } | undefined;
64
+ content: string;
65
+ files?: string[] | undefined;
66
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
67
+ tool_use_id: string;
68
+ error: any;
69
+ status: any;
70
+ }, any, "json">)>;
71
+ getToolDefinitions(): ToolDefinition[];
72
+ }
73
+ //# sourceMappingURL=ToolCollection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolCollection.d.ts","sourceRoot":"","sources":["../../src/ToolCollection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAG/B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAA2E,MAAM,YAAY,CAAC;AAEhI,MAAM,WAAW,wBAAwB;IACrC;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;CACtB;AAED;;GAEG;AACH,qBAAa,cAAe,YAAW,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEtD;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,KAAK,EAAE,YAAY,CAAC;gBAER,EACR,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EACxC,EAAE,wBAAwB;IAQ3B,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAexC,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC,EAAE;IAItD,OAAO,CAAC,GAAG,EAAE,OAAO;;;;;;;;;;;;IAoB1B,kBAAkB,IAAI,cAAc,EAAE;CAIzC"}
@@ -0,0 +1,15 @@
1
+ import { HTTPException } from "hono/http-exception";
2
+ import { Tool, ToolDefinition, ToolExecutionContext, ToolExecutionPayload, ToolExecutionResult } from "./types.js";
3
+ export declare class ToolRegistry {
4
+ registry: Record<string, Tool<any>>;
5
+ constructor(tools?: Tool<any>[]);
6
+ getDefinitions(): ToolDefinition[];
7
+ getTool<ParamsT extends Record<string, any>>(name: string): Tool<ParamsT>;
8
+ getTools(): Tool<any>[];
9
+ registerTool<ParamsT extends Record<string, any>>(tool: Tool<ParamsT>): void;
10
+ runTool<ParamsT extends Record<string, any>>(payload: ToolExecutionPayload<ParamsT>, context: ToolExecutionContext): Promise<ToolExecutionResult>;
11
+ }
12
+ export declare class ToolNotFoundError extends HTTPException {
13
+ constructor(name: string);
14
+ }
15
+ //# sourceMappingURL=ToolRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolRegistry.d.ts","sourceRoot":"","sources":["../../src/ToolRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACnH,qBAAa,YAAY;IAErB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM;gBAE7B,KAAK,GAAE,IAAI,CAAC,GAAG,CAAC,EAAO;IAMnC,cAAc,IAAI,cAAc,EAAE;IAQlC,OAAO,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC;IAQzE,QAAQ;IAIR,YAAY,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI;IAI5E,OAAO,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,oBAAoB,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC;CAIpJ;AAGD,qBAAa,iBAAkB,SAAQ,aAAa;gBACpC,IAAI,EAAE,MAAM;CAI3B"}
@@ -0,0 +1,20 @@
1
+ import { VertesiaClient } from "@vertesia/client";
2
+ import { AuthTokenPayload } from "@vertesia/common";
3
+ import { Context } from "hono";
4
+ import { JWTVerifyGetKey } from "jose";
5
+ import { ToolExecutionContext } from "./types.js";
6
+ export declare function getJwks(url: string): Promise<JWTVerifyGetKey>;
7
+ export declare function verifyToken(token: string): Promise<import("jose").JWTVerifyResult<AuthTokenPayload> & import("jose").ResolvedKey>;
8
+ export declare function authorize(ctx: Context): Promise<AuthSession>;
9
+ export declare class AuthSession implements ToolExecutionContext {
10
+ token: string;
11
+ payload: AuthTokenPayload;
12
+ _client: VertesiaClient | undefined;
13
+ endpoints: {
14
+ studio: string;
15
+ store: string;
16
+ };
17
+ constructor(token: string, payload: AuthTokenPayload);
18
+ getClient(): Promise<VertesiaClient>;
19
+ }
20
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE/B,OAAO,EAA0D,eAAe,EAAE,MAAM,MAAM,CAAC;AAC/F,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGlD,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,4BAcxC;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,0FAQ9C;AAID,wBAAsB,SAAS,CAAC,GAAG,EAAE,OAAO,wBA6B3C;AAED,qBAAa,WAAY,YAAW,oBAAoB;IAOjC,KAAK,EAAE,MAAM;IAAS,OAAO,EAAE,gBAAgB;IANlE,OAAO,EAAE,cAAc,GAAG,SAAS,CAAC;IACpC,SAAS,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;KACjB,CAAC;gBAEiB,KAAK,EAAE,MAAM,EAAS,OAAO,EAAE,gBAAgB;IAM5D,SAAS;CAMlB"}
@@ -0,0 +1,2 @@
1
+ export declare function validate(): void;
2
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../src/build/validate.ts"],"names":[],"mappings":"AAAA,wBAAgB,QAAQ,SAEvB"}
@@ -0,0 +1,4 @@
1
+ export * from "./ToolCollection.js";
2
+ export * from "./ToolRegistry.js";
3
+ export * from "./types.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC"}
@@ -0,0 +1,70 @@
1
+ import type { ToolDefinition, ToolUse } from "@llumiverse/common";
2
+ import { VertesiaClient } from "@vertesia/client";
3
+ import { AuthTokenPayload, ToolResult, ToolResultContent } from "@vertesia/common";
4
+ export interface ToolExecutionContext {
5
+ /**
6
+ * The raw JWT token to the tool execution request
7
+ */
8
+ token: string;
9
+ /**
10
+ * The decoded JWT token
11
+ */
12
+ payload: AuthTokenPayload;
13
+ /**
14
+ * Vertesia client factory using the current auth token.
15
+ * @returns a vertesia client instance
16
+ */
17
+ getClient: () => Promise<VertesiaClient>;
18
+ }
19
+ export interface ToolExecutionResult extends ToolResultContent {
20
+ /**
21
+ * Medata can be used to return more info on the tool execution like stats or user messages.
22
+ */
23
+ metadata?: Record<string, any>;
24
+ }
25
+ export interface ToolExecutionResponse extends ToolExecutionResult, ToolResult {
26
+ /**
27
+ * The tool use id of the tool use request. For traceability.
28
+ */
29
+ tool_use_id: string;
30
+ }
31
+ export interface ToolExecutionResponseError {
32
+ /**
33
+ * The tool use id of the tool use request. For traceability.
34
+ */
35
+ tool_use_id: string;
36
+ /**
37
+ * The http status code
38
+ */
39
+ status: number;
40
+ /**
41
+ * the error message
42
+ */
43
+ error: string;
44
+ /**
45
+ * Additional context information
46
+ */
47
+ data?: Record<string, any>;
48
+ }
49
+ export interface ToolExecutionPayload<ParamsT extends Record<string, any>> {
50
+ tool_use: ToolUse<ParamsT>;
51
+ /**
52
+ * Optional metadata related to the current execution request
53
+ */
54
+ metadata?: Record<string, any>;
55
+ }
56
+ export type ToolFn<ParamsT extends Record<string, any>> = (payload: ToolExecutionPayload<ParamsT>, context: ToolExecutionContext) => Promise<ToolExecutionResult>;
57
+ export interface Tool<ParamsT extends Record<string, any>> extends ToolDefinition {
58
+ run: ToolFn<ParamsT>;
59
+ }
60
+ /**
61
+ * The interface that should be return when requesting a collection endpoint using a GET
62
+ */
63
+ export interface ToolCollectionDefinition {
64
+ title: string;
65
+ description: string;
66
+ src: string;
67
+ tools: ToolDefinition[];
68
+ }
69
+ export type { ToolDefinition };
70
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAEnF,MAAM,WAAW,oBAAoB;IACjC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,OAAO,EAAE,gBAAgB,CAAC;IAC1B;;;OAGG;IACH,SAAS,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC1D;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB,EAAE,UAAU;IAC1E;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,0BAA0B;IACvC;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IACrE,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3B;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC;AAED,MAAM,MAAM,MAAM,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,oBAAoB,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAElK,MAAM,WAAW,IAAI,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAE,SAAQ,cAAc;IAC7E,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,YAAY,EAAE,cAAc,EAAE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@vertesia/tools-sdk",
3
+ "version": "0.74.0",
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": {
13
+ "types": "./lib/types/index.d.ts",
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.0.0"
27
+ },
28
+ "dependencies": {
29
+ "jose": "^6.0.11",
30
+ "@llumiverse/common": "0.22.0",
31
+ "@vertesia/client": "0.74.0",
32
+ "@vertesia/common": "0.74.0"
33
+ },
34
+ "scripts": {
35
+ "test": "vitest run",
36
+ "build": "pnpm exec tsmod build",
37
+ "clean": "rimraf ./node_modules ./lib ./tsconfig.tsbuildinfo"
38
+ }
39
+ }
@@ -0,0 +1,131 @@
1
+ import { Context } from "hono";
2
+ import { HTTPException } from "hono/http-exception";
3
+ import { authorize } from "./auth.js";
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;
27
+
28
+ /**
29
+ * The tools
30
+ */
31
+ tools: Tool<any>[];
32
+ }
33
+
34
+ /**
35
+ * Implements a tools colection endpoint
36
+ */
37
+ export class ToolCollection implements Iterable<Tool<any>> {
38
+
39
+ /**
40
+ * A kebab case collection name. Must only contains alphanumeric and dash characters,
41
+ * The name can be used to generate the path where the collection is exposed.
42
+ * Example: my-collection
43
+ */
44
+ name: string;
45
+ /**
46
+ * Optional title for UI display.
47
+ * If not provided the title will be generated form the kebab case name by replacing - with spaces and upper casing first letter in words.
48
+ */
49
+ title?: string;
50
+ /**
51
+ * Optional icon for UI display
52
+ */
53
+ icon?: string;
54
+ /**
55
+ * A short description
56
+ */
57
+ description: string;
58
+ /**
59
+ * The tool registry
60
+ */
61
+ tools: ToolRegistry;
62
+
63
+ constructor({
64
+ name, title, icon, description, tools
65
+ }: ToolCollectionProperties) {
66
+ this.name = name;
67
+ this.title = title || kebabCaseToTitle(name);
68
+ this.icon = icon;
69
+ this.description = description;
70
+ this.tools = new ToolRegistry(tools);
71
+ }
72
+
73
+ [Symbol.iterator](): Iterator<Tool<any>> {
74
+ let index = 0;
75
+ const tools = this.tools.getTools();
76
+
77
+ return {
78
+ next(): IteratorResult<Tool<any>> {
79
+ if (index < tools.length) {
80
+ return { value: tools[index++], done: false };
81
+ } else {
82
+ return { done: true, value: undefined };
83
+ }
84
+ }
85
+ };
86
+ }
87
+
88
+ map<U>(callback: (tool: Tool<any>, index: number) => U): U[] {
89
+ return this.tools.getTools().map(callback);
90
+ }
91
+
92
+ async execute(ctx: Context) {
93
+ let payload: ToolExecutionPayload<any> | undefined;
94
+ try {
95
+ payload = await readPayload(ctx);
96
+ const session = await authorize(ctx);
97
+ const r = await this.tools.runTool(payload, session);
98
+ return ctx.json({
99
+ ...r,
100
+ tool_use_id: payload.tool_use.id
101
+ } satisfies ToolExecutionResponse);
102
+ } catch (err: any) { // HTTPException ?
103
+ const status = err.status || 500;
104
+ return ctx.json({
105
+ tool_use_id: payload?.tool_use.id || "undefined",
106
+ error: err.message || "Error executing tool",
107
+ status
108
+ } satisfies ToolExecutionResponseError, status)
109
+ }
110
+ }
111
+
112
+ getToolDefinitions(): ToolDefinition[] {
113
+ return this.tools.getDefinitions();
114
+ }
115
+
116
+ }
117
+
118
+
119
+ async function readPayload(ctx: Context) {
120
+ try {
121
+ return await ctx.req.json() as ToolExecutionPayload<any>;
122
+ } catch (err: any) {
123
+ throw new HTTPException(500, {
124
+ message: "Failed to load execution request payload: " + err.message
125
+ });
126
+ }
127
+ }
128
+
129
+ function kebabCaseToTitle(name: string) {
130
+ return name.split('-').map(p => p[0].toUpperCase() + p.substring(1)).join(' ');
131
+ }
@@ -0,0 +1,49 @@
1
+ import { HTTPException } from "hono/http-exception";
2
+ import { Tool, ToolDefinition, ToolExecutionContext, ToolExecutionPayload, ToolExecutionResult } from "./types.js";
3
+ export class ToolRegistry {
4
+
5
+ registry: Record<string, Tool<any>> = {};
6
+
7
+ constructor(tools: Tool<any>[] = []) {
8
+ for (const tool of tools) {
9
+ this.registry[tool.name] = tool;
10
+ }
11
+ }
12
+
13
+ getDefinitions(): ToolDefinition[] {
14
+ return Object.values(this.registry).map(tool => ({
15
+ name: tool.name,
16
+ description: tool.description,
17
+ input_schema: tool.input_schema
18
+ }));
19
+ }
20
+
21
+ getTool<ParamsT extends Record<string, any>>(name: string): Tool<ParamsT> {
22
+ const tool = this.registry[name]
23
+ if (tool === undefined) {
24
+ throw new ToolNotFoundError(name);
25
+ }
26
+ return tool;
27
+ }
28
+
29
+ getTools() {
30
+ return Object.values(this.registry);
31
+ }
32
+
33
+ registerTool<ParamsT extends Record<string, any>>(tool: Tool<ParamsT>): void {
34
+ this.registry[tool.name] = tool;
35
+ }
36
+
37
+ runTool<ParamsT extends Record<string, any>>(payload: ToolExecutionPayload<ParamsT>, context: ToolExecutionContext): Promise<ToolExecutionResult> {
38
+ return this.getTool(payload.tool_use.tool_name).run(payload, context);
39
+ }
40
+
41
+ }
42
+
43
+
44
+ export class ToolNotFoundError extends HTTPException {
45
+ constructor(name: string) {
46
+ super(404, { message: "Tool function not found: " + name });
47
+ this.name = "ToolNotFoundError";
48
+ }
49
+ }
package/src/auth.ts ADDED
@@ -0,0 +1,87 @@
1
+ import { decodeEndpoints, VertesiaClient } from "@vertesia/client";
2
+ import { AuthTokenPayload } from "@vertesia/common";
3
+ import { Context } from "hono";
4
+ import { HTTPException } from "hono/http-exception";
5
+ import { createLocalJWKSet, decodeJwt, JSONWebKeySet, jwtVerify, JWTVerifyGetKey } from "jose";
6
+ import { ToolExecutionContext } from "./types.js";
7
+ const cache: Record<string, JWTVerifyGetKey> = {};
8
+
9
+ export async function getJwks(url: string) {
10
+ if (!cache.url) {
11
+ console.log('JWKS cache miss for: ', url);
12
+ const jwks = await fetch(url).then(r => {
13
+ if (r.ok) {
14
+ return r.json() as Promise<JSONWebKeySet>;
15
+ }
16
+ throw new Error("Fetching jwks failed with code: " + r.status);
17
+ }).catch(err => {
18
+ throw new Error("Failed to fetch jwks: " + err.message);
19
+ })
20
+ cache.url = createLocalJWKSet(jwks);
21
+ }
22
+ return cache.url;
23
+ }
24
+
25
+ export async function verifyToken(token: string) {
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");
30
+ }
31
+ const jwks = await getJwks(`${studio}/.well-known/jwks`);
32
+ return await jwtVerify<AuthTokenPayload>(token, jwks);
33
+ }
34
+
35
+
36
+
37
+ export async function authorize(ctx: Context) {
38
+ const auth = ctx.req.header('Authorization');
39
+ if (!auth) {
40
+ throw new HTTPException(401, {
41
+ message: `Missing Authorization header'`
42
+ });
43
+ }
44
+ const [scheme, value] = auth.trim().split(' ');
45
+ if (scheme.toLowerCase() !== 'bearer') {
46
+ throw new HTTPException(401, {
47
+ message: `Authorization scheme ${scheme} is not supported`
48
+ });
49
+ }
50
+ if (!value) {
51
+ throw new HTTPException(401, {
52
+ message: `Missing bearer token value`
53
+ });
54
+ }
55
+ try {
56
+ const { payload } = await verifyToken(value);
57
+ const session = new AuthSession(value, payload);
58
+ ctx.set("auth", session);
59
+ return session;
60
+ } catch (err: any) {
61
+ throw new HTTPException(401, {
62
+ message: err.message,
63
+ cause: err
64
+ });
65
+ }
66
+ }
67
+
68
+ export class AuthSession implements ToolExecutionContext {
69
+ _client: VertesiaClient | undefined;
70
+ endpoints: {
71
+ studio: string;
72
+ store: string;
73
+ };
74
+
75
+ constructor(public token: string, public payload: AuthTokenPayload) {
76
+ this.endpoints = decodeEndpoints(payload.endpoints) as {
77
+ studio: string, store: string
78
+ };
79
+ }
80
+
81
+ async getClient() {
82
+ if (!this._client) {
83
+ this._client = await VertesiaClient.fromAuthToken(this.token, this.payload);
84
+ }
85
+ return this._client;
86
+ }
87
+ }
@@ -0,0 +1,3 @@
1
+ export function validate() {
2
+ //TODO
3
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./ToolCollection.js";
2
+ export * from "./ToolRegistry.js";
3
+ export * from "./types.js";
package/src/types.ts ADDED
@@ -0,0 +1,78 @@
1
+ import type { ToolDefinition, ToolUse } from "@llumiverse/common";
2
+ import { VertesiaClient } from "@vertesia/client";
3
+ import { AuthTokenPayload, ToolResult, ToolResultContent } from "@vertesia/common";
4
+
5
+ export interface ToolExecutionContext {
6
+ /**
7
+ * The raw JWT token to the tool execution request
8
+ */
9
+ token: string;
10
+ /**
11
+ * The decoded JWT token
12
+ */
13
+ payload: AuthTokenPayload;
14
+ /**
15
+ * Vertesia client factory using the current auth token.
16
+ * @returns a vertesia client instance
17
+ */
18
+ getClient: () => Promise<VertesiaClient>;
19
+ }
20
+
21
+ export interface ToolExecutionResult extends ToolResultContent {
22
+ /**
23
+ * Medata can be used to return more info on the tool execution like stats or user messages.
24
+ */
25
+ metadata?: Record<string, any>;
26
+ }
27
+
28
+ export interface ToolExecutionResponse extends ToolExecutionResult, ToolResult {
29
+ /**
30
+ * The tool use id of the tool use request. For traceability.
31
+ */
32
+ tool_use_id: string;
33
+ }
34
+
35
+ export interface ToolExecutionResponseError {
36
+ /**
37
+ * The tool use id of the tool use request. For traceability.
38
+ */
39
+ tool_use_id: string;
40
+ /**
41
+ * The http status code
42
+ */
43
+ status: number;
44
+ /**
45
+ * the error message
46
+ */
47
+ error: string;
48
+ /**
49
+ * Additional context information
50
+ */
51
+ data?: Record<string, any>;
52
+ }
53
+
54
+ export interface ToolExecutionPayload<ParamsT extends Record<string, any>> {
55
+ tool_use: ToolUse<ParamsT>,
56
+ /**
57
+ * Optional metadata related to the current execution request
58
+ */
59
+ metadata?: Record<string, any>,
60
+ }
61
+
62
+ export type ToolFn<ParamsT extends Record<string, any>> = (payload: ToolExecutionPayload<ParamsT>, context: ToolExecutionContext) => Promise<ToolExecutionResult>;
63
+
64
+ export interface Tool<ParamsT extends Record<string, any>> extends ToolDefinition {
65
+ run: ToolFn<ParamsT>;
66
+ }
67
+
68
+ /**
69
+ * The interface that should be return when requesting a collection endpoint using a GET
70
+ */
71
+ export interface ToolCollectionDefinition {
72
+ title: string;
73
+ description: string;
74
+ src: string;
75
+ tools: ToolDefinition[];
76
+ }
77
+
78
+ export type { ToolDefinition };