@kognitivedev/cloud-voice 0.2.29

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/dist/server.js ADDED
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCloudVoiceTools = createCloudVoiceTools;
4
+ exports.syncCloudVoiceCodeDefinedTools = syncCloudVoiceCodeDefinedTools;
5
+ const node_crypto_1 = require("node:crypto");
6
+ const client_1 = require("./client");
7
+ function json(data, status = 200) {
8
+ return new Response(JSON.stringify(data), {
9
+ status,
10
+ headers: { "Content-Type": "application/json" },
11
+ });
12
+ }
13
+ function verifySignature(input) {
14
+ var _a;
15
+ if (!input.secret)
16
+ return;
17
+ if (!input.timestamp || !((_a = input.signature) === null || _a === void 0 ? void 0 : _a.startsWith("sha256="))) {
18
+ throw new Error("Missing Kognitive signature");
19
+ }
20
+ const timestampMs = Date.parse(input.timestamp);
21
+ if (!Number.isFinite(timestampMs) || Math.abs(Date.now() - timestampMs) > input.toleranceMs) {
22
+ throw new Error("Kognitive signature timestamp is outside the allowed window");
23
+ }
24
+ const expected = `sha256=${(0, node_crypto_1.createHmac)("sha256", input.secret).update(`${input.timestamp}.${input.body}`).digest("hex")}`;
25
+ const actualBuffer = Buffer.from(input.signature);
26
+ const expectedBuffer = Buffer.from(expected);
27
+ if (actualBuffer.length !== expectedBuffer.length || !(0, node_crypto_1.timingSafeEqual)(actualBuffer, expectedBuffer)) {
28
+ throw new Error("Invalid Kognitive signature");
29
+ }
30
+ }
31
+ function resolveTool(tools, toolId) {
32
+ var _a, _b;
33
+ return (_b = (_a = tools[toolId]) !== null && _a !== void 0 ? _a : tools[toolId.replace(/-/g, "_")]) !== null && _b !== void 0 ? _b : tools[toolId.replace(/_/g, "-")];
34
+ }
35
+ function createCloudVoiceTools(options) {
36
+ return async function handleCloudVoiceTool(request) {
37
+ var _a, _b;
38
+ const rawBody = await request.text();
39
+ try {
40
+ verifySignature({
41
+ body: rawBody,
42
+ timestamp: request.headers.get("x-kognitive-timestamp"),
43
+ signature: request.headers.get("x-kognitive-signature"),
44
+ secret: options.secret,
45
+ toleranceMs: (_a = options.signatureToleranceMs) !== null && _a !== void 0 ? _a : 5 * 60 * 1000,
46
+ });
47
+ const payload = JSON.parse(rawBody || "{}");
48
+ const toolId = payload.toolId || payload.toolName || "";
49
+ const tool = resolveTool(options.tools, toolId);
50
+ if (!tool) {
51
+ return json({ error: `Unknown Cloud Voice tool: ${toolId}` }, 404);
52
+ }
53
+ const definition = typeof tool === "function" ? { execute: tool } : tool;
54
+ const input = ((_b = definition.input) === null || _b === void 0 ? void 0 : _b.parse) ? definition.input.parse(payload.input) : payload.input;
55
+ const result = await definition.execute(input, {
56
+ request: payload,
57
+ abortSignal: request.signal,
58
+ toolCallId: payload.toolCallId,
59
+ session: payload.session,
60
+ });
61
+ return json({ result });
62
+ }
63
+ catch (error) {
64
+ return json({ error: error instanceof Error ? error.message : "Cloud Voice tool failed" }, 400);
65
+ }
66
+ };
67
+ }
68
+ async function syncCloudVoiceCodeDefinedTools(input) {
69
+ var _a, _b, _c, _d;
70
+ const client = (_a = input.client) !== null && _a !== void 0 ? _a : new client_1.KognitiveCloudVoiceClient({
71
+ baseUrl: (_c = (_b = input.baseUrl) !== null && _b !== void 0 ? _b : process.env.COGNITIVE_API_URL) !== null && _c !== void 0 ? _c : "http://localhost:3001",
72
+ apiKey: (_d = input.apiKey) !== null && _d !== void 0 ? _d : process.env.COGNITIVE_API_KEY,
73
+ });
74
+ const payload = {
75
+ sourceKey: input.sourceKey,
76
+ name: input.name,
77
+ bridgeUrl: input.bridgeUrl,
78
+ bridgeAuthorization: input.bridgeAuthorization,
79
+ bridgeToken: input.bridgeToken,
80
+ metadata: input.metadata,
81
+ tools: input.kognitive.getToolMetadataList().map((tool) => ({
82
+ id: tool.id,
83
+ name: tool.id,
84
+ description: tool.description,
85
+ inputSchema: tool.inputSchema && typeof tool.inputSchema === "object" && !Array.isArray(tool.inputSchema)
86
+ ? tool.inputSchema
87
+ : { type: "object", additionalProperties: true },
88
+ requireApproval: Boolean(tool.requireApproval),
89
+ })),
90
+ };
91
+ return client.tools.catalog.sync(payload);
92
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_crypto_1 = require("node:crypto");
4
+ const vitest_1 = require("vitest");
5
+ const server_1 = require("./server");
6
+ function signedRequest(body, secret) {
7
+ const timestamp = new Date().toISOString();
8
+ const raw = JSON.stringify(body);
9
+ const signature = `sha256=${(0, node_crypto_1.createHmac)("sha256", secret).update(`${timestamp}.${raw}`).digest("hex")}`;
10
+ return new Request("https://example.com/api/voice-tools", {
11
+ method: "POST",
12
+ headers: {
13
+ "content-type": "application/json",
14
+ "x-kognitive-timestamp": timestamp,
15
+ "x-kognitive-signature": signature,
16
+ },
17
+ body: raw,
18
+ });
19
+ }
20
+ (0, vitest_1.describe)("createCloudVoiceTools", () => {
21
+ (0, vitest_1.it)("routes a signed cloud voice tool request to the matching handler", async () => {
22
+ const handler = (0, server_1.createCloudVoiceTools)({
23
+ secret: "secret",
24
+ tools: {
25
+ lookup_order: async (input, ctx) => ({
26
+ orderNumber: input.orderNumber,
27
+ userId: ctx.session.userId,
28
+ }),
29
+ },
30
+ });
31
+ const response = await handler(signedRequest({
32
+ type: "cloud_voice.tool.execute",
33
+ toolCallId: "tool_call_1",
34
+ toolId: "lookup_order",
35
+ input: { orderNumber: "A-1024" },
36
+ session: {
37
+ id: "session-db-id",
38
+ sessionId: "voice_session_1",
39
+ agentSlug: "support",
40
+ channel: "iframe",
41
+ userId: "user_1",
42
+ resourceId: { userId: "user_1" },
43
+ metadata: {},
44
+ },
45
+ }, "secret"));
46
+ await (0, vitest_1.expect)(response.json()).resolves.toEqual({
47
+ result: {
48
+ orderNumber: "A-1024",
49
+ userId: "user_1",
50
+ },
51
+ });
52
+ });
53
+ (0, vitest_1.it)("rejects invalid signatures", async () => {
54
+ const handler = (0, server_1.createCloudVoiceTools)({
55
+ secret: "secret",
56
+ tools: {
57
+ lookup_order: async () => ({ ok: true }),
58
+ },
59
+ });
60
+ const response = await handler(signedRequest({
61
+ type: "cloud_voice.tool.execute",
62
+ toolCallId: "tool_call_1",
63
+ toolId: "lookup_order",
64
+ input: {},
65
+ session: {
66
+ id: "session-db-id",
67
+ sessionId: "voice_session_1",
68
+ agentSlug: "support",
69
+ channel: "iframe",
70
+ userId: "user_1",
71
+ resourceId: {},
72
+ metadata: {},
73
+ },
74
+ }, "wrong"));
75
+ (0, vitest_1.expect)(response.status).toBe(400);
76
+ await (0, vitest_1.expect)(response.json()).resolves.toEqual({ error: "Invalid Kognitive signature" });
77
+ });
78
+ });
package/dist/sse.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export interface ParsedSSEEvent {
2
+ event?: string;
3
+ data: string;
4
+ id?: string;
5
+ retry?: number;
6
+ }
7
+ export declare function readSSEStream(stream: ReadableStream<Uint8Array>): AsyncGenerator<ParsedSSEEvent>;
package/dist/sse.js ADDED
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
3
+ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
4
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
5
+ var g = generator.apply(thisArg, _arguments || []), i, q = [];
6
+ return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
7
+ function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
8
+ function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
9
+ function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
10
+ function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
11
+ function fulfill(value) { resume("next", value); }
12
+ function reject(value) { resume("throw", value); }
13
+ function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
14
+ };
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.readSSEStream = readSSEStream;
17
+ function readSSEStream(stream) {
18
+ return __asyncGenerator(this, arguments, function* readSSEStream_1() {
19
+ var _a;
20
+ const reader = stream.getReader();
21
+ const decoder = new TextDecoder();
22
+ let buffer = "";
23
+ let event = "";
24
+ let data = [];
25
+ let id;
26
+ let retry;
27
+ function flush() {
28
+ if (!event && data.length === 0 && !id && retry === undefined)
29
+ return null;
30
+ const frame = {
31
+ event: event || undefined,
32
+ data: data.join("\n"),
33
+ id,
34
+ retry,
35
+ };
36
+ event = "";
37
+ data = [];
38
+ id = undefined;
39
+ retry = undefined;
40
+ return frame;
41
+ }
42
+ while (true) {
43
+ const { value, done } = yield __await(reader.read());
44
+ if (done)
45
+ break;
46
+ buffer += decoder.decode(value, { stream: true });
47
+ const lines = buffer.split(/\r\n|\r|\n/);
48
+ buffer = (_a = lines.pop()) !== null && _a !== void 0 ? _a : "";
49
+ for (const line of lines) {
50
+ if (line === "") {
51
+ const frame = flush();
52
+ if (frame)
53
+ yield yield __await(frame);
54
+ continue;
55
+ }
56
+ if (line.startsWith(":"))
57
+ continue;
58
+ const separator = line.indexOf(":");
59
+ const field = separator === -1 ? line : line.slice(0, separator);
60
+ const rawValue = separator === -1 ? "" : line.slice(separator + 1).replace(/^ /, "");
61
+ if (field === "event")
62
+ event = rawValue;
63
+ if (field === "data")
64
+ data.push(rawValue);
65
+ if (field === "id")
66
+ id = rawValue;
67
+ if (field === "retry")
68
+ retry = Number(rawValue);
69
+ }
70
+ }
71
+ const tail = flush();
72
+ if (tail)
73
+ yield yield __await(tail);
74
+ });
75
+ }