Ins0mn1a commited on
Commit
0787a7f
·
verified ·
1 Parent(s): ada41b1

Upload 14 files

Browse files
Files changed (14) hide show
  1. .dockerignore +1 -0
  2. Dockerfile +18 -0
  3. common/db.js +5 -0
  4. config/utils.js +17 -0
  5. dao/agent.js +42 -0
  6. dao/call.js +35 -0
  7. dao/trunk.js +26 -0
  8. eslint.config.js +11 -0
  9. index.js +23 -0
  10. package.json +28 -0
  11. pnpm-lock.yaml +905 -0
  12. routes/agent.js +109 -0
  13. routes/trunk.js +144 -0
  14. schemas/index.js +50 -0
.dockerignore ADDED
@@ -0,0 +1 @@
 
 
1
+ node_modules
Dockerfile ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:24-slim
2
+ RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/*
3
+
4
+ RUN corepack enable && corepack prepare pnpm@latest --activate
5
+
6
+ WORKDIR /app
7
+
8
+ COPY package.json pnpm-lock.yaml ./
9
+ RUN pnpm install --frozen-lockfile --prod
10
+
11
+ COPY . .
12
+ RUN adduser --disabled-password --gecos "" appuser
13
+ RUN chown -R appuser:appuser /app
14
+ USER appuser
15
+
16
+ EXPOSE 3000
17
+
18
+ CMD ["pnpm", "run", "start"]
common/db.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ import pg from 'pg'
2
+ import { env } from '../config/utils.js'
3
+
4
+ const pool = new pg.Pool({ connectionString: env.DATABASE_URL })
5
+ export const query = (sql, params) => pool.query(sql, params)
config/utils.js ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { AgentDispatchClient, SipClient } from 'livekit-server-sdk'
2
+
3
+ import { z } from 'zod'
4
+
5
+ const env = z.object({
6
+ LIVEKIT_API_KEY: z.string().min(1),
7
+ LIVEKIT_API_SECRET: z.string().min(1),
8
+ LIVEKIT_URL: z.string().url(),
9
+ API_PORT_NUM: z.coerce.number().default(3000),
10
+ DATABASE_URL: z.string().url(),
11
+ }).parse(process.env)
12
+
13
+ const lkEnvArgs = [env.LIVEKIT_URL, env.LIVEKIT_API_KEY, env.LIVEKIT_API_SECRET]
14
+ const agentDispatchClient = new AgentDispatchClient(...lkEnvArgs)
15
+ const sipClient = new SipClient(...lkEnvArgs)
16
+
17
+ export { env, lkEnvArgs, agentDispatchClient, sipClient }
dao/agent.js ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { query } from '../common/db.js'
2
+
3
+ export async function createAgent(name, decodedPrompt, stt, llm, tts, variablesSchema) {
4
+ const { rows } = await query(
5
+ 'INSERT INTO agent (name, system_prompt, stt, llm, tts, variables_schema) \
6
+ VALUES ($1, $2, $3, $4, $5, $6) RETURNING id',
7
+ [name, decodedPrompt, JSON.stringify(stt), JSON.stringify(llm), JSON.stringify(tts), JSON.stringify(variablesSchema)]
8
+ )
9
+
10
+ return { agentId: rows[0].id }
11
+ }
12
+
13
+ export async function getAgent(agentId) {
14
+ const { rows: agent } = await query('SELECT name FROM agent WHERE id = $1', [agentId])
15
+ return agent
16
+ }
17
+
18
+
19
+ export async function listAgents() {
20
+ const { rows } = await query('SELECT id, name, created_at \
21
+ FROM agent \
22
+ ORDER BY created_at DESC')
23
+
24
+ console.log(JSON.stringify(rows, null, 2))
25
+ return rows
26
+ }
27
+
28
+
29
+ export async function updateAgent(fields, values, i) {
30
+ const { rows } = await query(
31
+ `UPDATE agent SET ${fields.join(', ')} WHERE id = $${i} RETURNING id`,
32
+ values
33
+ )
34
+ return rows
35
+ }
36
+
37
+
38
+ export async function deleteAgent(agentId) {
39
+ const { rows } = await query('DELETE FROM agent WHERE id = $1 RETURNING id', [agentId])
40
+ if (!rows.length) return { error: 'Agent not found', code:404 }
41
+ return rows
42
+ }
dao/call.js ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { agentDispatchClient, sipClient } from '../config/utils.js'
2
+
3
+ import { query } from '../common/db.js'
4
+
5
+ export async function createCall(formattedNumber, agentId, variables) {
6
+ const { rows: agentRows } = await query(
7
+ 'SELECT name FROM agent WHERE id = $1',
8
+ [agentId]
9
+ )
10
+
11
+ if (!agentRows.length) return { error: 'Agent not found !', code: 404 }
12
+ const { name: agentName } = agentRows[0]
13
+
14
+ const { rows: outboundTrunkRows } = await query(
15
+ `SELECT lk_trunk_id FROM trunk where direction='outbound'`
16
+ )
17
+ if (!outboundTrunkRows.length) return {error: 'Outbound trunk not found !', code: 404}
18
+ const {lk_trunk_id: lkTrunkId} = outboundTrunkRows[0]
19
+ console.log('Using lkTrunkId: ', lkTrunkId)
20
+
21
+ const roomName = `room_${Math.floor(Math.random() * 10_000)}`
22
+ const participantIdentity = `caller_${formattedNumber}`
23
+ const outboundDispatch = await agentDispatchClient.createDispatch(
24
+ roomName, agentName, {
25
+ metadata: JSON.stringify({ agentId, phoneNumber: formattedNumber, variables })
26
+ }
27
+ )
28
+ console.log('Outbound dispatch is: ', JSON.stringify(outboundDispatch, null, 2))
29
+
30
+ const sipParticipant = await sipClient.createSipParticipant(lkTrunkId, formattedNumber, roomName, { participantIdentity })
31
+ console.log('sipParticipant is: ', JSON.stringify(sipParticipant, null, 2))
32
+
33
+ return { success: true, roomId: roomName, dispatchId: outboundDispatch.id, sipParticipantId: sipParticipant.participantId }
34
+
35
+ }
dao/trunk.js ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { query } from '../common/db.js'
2
+
3
+ export async function createSipTrunk(name, trunkType, sipTrunkId, address) {
4
+ if (trunkType === 'outbound' && address) {
5
+ const { rows } = await query(
6
+ 'INSERT INTO trunk(name, direction, lk_trunk_id, address) VALUES($1, $2, $3, $4) RETURNING id',
7
+ [name, trunkType, sipTrunkId, address]
8
+ )
9
+ return rows
10
+ } else {
11
+ const { rows } = await query(
12
+ 'INSERT INTO trunk(name, direction, lk_trunk_id) VALUES($1, $2, $3) RETURNING id',
13
+ [name, trunkType, sipTrunkId]
14
+ )
15
+ return rows
16
+ }
17
+ }
18
+
19
+
20
+ export async function getInboundTrunk(sipTrunkId) {
21
+ const { rows: inboundTrunk } = await query(`SELECT lk_trunk_id
22
+ FROM trunk
23
+ WHERE direction = 'inbound'
24
+ AND id = $1`, [sipTrunkId])
25
+ return inboundTrunk
26
+ }
eslint.config.js ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import globals from 'globals'
2
+
3
+ export default [{
4
+ languageOptions: {globals: globals.node},
5
+ rules: {
6
+ 'no-undef': 'error',
7
+ 'no-unused-vars': 'error',
8
+ 'semi': ['warn', 'never'],
9
+ 'quotes': ['error', 'single', { allowTemplateLiterals: true }],
10
+ }
11
+ }]
index.js ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Hono } from 'hono'
2
+ import { serve } from '@hono/node-server'
3
+
4
+
5
+ import { agentRouter } from './routes/agent.js'
6
+ import { trunkRouter } from './routes/trunk.js'
7
+
8
+ import { env } from './config/utils.js'
9
+
10
+ const app = new Hono()
11
+
12
+ app.route('/agents', agentRouter)
13
+ app.route('/trunks', trunkRouter)
14
+
15
+
16
+ serve(
17
+ {
18
+ fetch: app.fetch,
19
+ port: env.API_PORT_NUM
20
+ }, () => {
21
+ console.log(`API server running on ${env.API_PORT_NUM} ...`)
22
+ }
23
+ )
package.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "livekit-custom-api",
3
+ "version": "1.0.0",
4
+ "description": "API wrapper for LiveKit SIP calls with agent dispatch",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "scripts": {
8
+ "start": "node index.js",
9
+ "dev": "eslint . && node --env-file=../.env --watch index.js",
10
+ "build": "echo 'No build step required for Node.js'"
11
+ },
12
+ "dependencies": {
13
+ "@hono/node-server": "^1.19.11",
14
+ "@livekit/protocol": "^1.45.0",
15
+ "hono": "^4.0.0",
16
+ "livekit-server-sdk": "^2.11.0",
17
+ "pg": "^8.20.0",
18
+ "zod": "^3.23.8"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "^22.13.17",
22
+ "eslint": "^9.0.0",
23
+ "globals": "^15.0.0"
24
+ },
25
+ "engines": {
26
+ "node": ">=20.0.0"
27
+ }
28
+ }
pnpm-lock.yaml ADDED
@@ -0,0 +1,905 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ lockfileVersion: '9.0'
2
+
3
+ settings:
4
+ autoInstallPeers: true
5
+ excludeLinksFromLockfile: false
6
+
7
+ importers:
8
+
9
+ .:
10
+ dependencies:
11
+ '@hono/node-server':
12
+ specifier: ^1.19.11
13
+ version: 1.19.11(hono@4.12.5)
14
+ '@livekit/protocol':
15
+ specifier: ^1.45.0
16
+ version: 1.45.0
17
+ hono:
18
+ specifier: ^4.0.0
19
+ version: 4.12.5
20
+ livekit-server-sdk:
21
+ specifier: ^2.11.0
22
+ version: 2.15.0
23
+ pg:
24
+ specifier: ^8.20.0
25
+ version: 8.20.0
26
+ zod:
27
+ specifier: ^3.23.8
28
+ version: 3.25.76
29
+ devDependencies:
30
+ '@types/node':
31
+ specifier: ^22.13.17
32
+ version: 22.19.15
33
+ eslint:
34
+ specifier: ^9.0.0
35
+ version: 9.39.4
36
+ globals:
37
+ specifier: ^15.0.0
38
+ version: 15.15.0
39
+
40
+ packages:
41
+
42
+ '@bufbuild/protobuf@1.10.1':
43
+ resolution: {integrity: sha512-wJ8ReQbHxsAfXhrf9ixl0aYbZorRuOWpBNzm8pL8ftmSxQx/wnJD5Eg861NwJU/czy2VXFIebCeZnZrI9rktIQ==}
44
+
45
+ '@eslint-community/eslint-utils@4.9.1':
46
+ resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==}
47
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
48
+ peerDependencies:
49
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
50
+
51
+ '@eslint-community/regexpp@4.12.2':
52
+ resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
53
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
54
+
55
+ '@eslint/config-array@0.21.2':
56
+ resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==}
57
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
58
+
59
+ '@eslint/config-helpers@0.4.2':
60
+ resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==}
61
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
62
+
63
+ '@eslint/core@0.17.0':
64
+ resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==}
65
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
66
+
67
+ '@eslint/eslintrc@3.3.5':
68
+ resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==}
69
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
70
+
71
+ '@eslint/js@9.39.4':
72
+ resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==}
73
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
74
+
75
+ '@eslint/object-schema@2.1.7':
76
+ resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==}
77
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
78
+
79
+ '@eslint/plugin-kit@0.4.1':
80
+ resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
81
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
82
+
83
+ '@hono/node-server@1.19.11':
84
+ resolution: {integrity: sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==}
85
+ engines: {node: '>=18.14.1'}
86
+ peerDependencies:
87
+ hono: ^4
88
+
89
+ '@humanfs/core@0.19.1':
90
+ resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
91
+ engines: {node: '>=18.18.0'}
92
+
93
+ '@humanfs/node@0.16.7':
94
+ resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==}
95
+ engines: {node: '>=18.18.0'}
96
+
97
+ '@humanwhocodes/module-importer@1.0.1':
98
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
99
+ engines: {node: '>=12.22'}
100
+
101
+ '@humanwhocodes/retry@0.4.3':
102
+ resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
103
+ engines: {node: '>=18.18'}
104
+
105
+ '@livekit/protocol@1.45.0':
106
+ resolution: {integrity: sha512-z22Ej7RRBFm5uVZpU7kBHOdDwZV6Hz+1crCOrse2g7yx8TcHXG0bKnOKwyN/meD233nEDlU2IHNCoT8Vq8lvtg==}
107
+
108
+ '@types/estree@1.0.8':
109
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
110
+
111
+ '@types/json-schema@7.0.15':
112
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
113
+
114
+ '@types/node@22.19.15':
115
+ resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==}
116
+
117
+ acorn-jsx@5.3.2:
118
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
119
+ peerDependencies:
120
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
121
+
122
+ acorn@8.16.0:
123
+ resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
124
+ engines: {node: '>=0.4.0'}
125
+ hasBin: true
126
+
127
+ ajv@6.14.0:
128
+ resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==}
129
+
130
+ ansi-styles@4.3.0:
131
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
132
+ engines: {node: '>=8'}
133
+
134
+ argparse@2.0.1:
135
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
136
+
137
+ balanced-match@1.0.2:
138
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
139
+
140
+ brace-expansion@1.1.12:
141
+ resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
142
+
143
+ callsites@3.1.0:
144
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
145
+ engines: {node: '>=6'}
146
+
147
+ camelcase-keys@9.1.3:
148
+ resolution: {integrity: sha512-Rircqi9ch8AnZscQcsA1C47NFdaO3wukpmIRzYcDOrmvgt78hM/sj5pZhZNec2NM12uk5vTwRHZ4anGcrC4ZTg==}
149
+ engines: {node: '>=16'}
150
+
151
+ camelcase@8.0.0:
152
+ resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==}
153
+ engines: {node: '>=16'}
154
+
155
+ chalk@4.1.2:
156
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
157
+ engines: {node: '>=10'}
158
+
159
+ color-convert@2.0.1:
160
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
161
+ engines: {node: '>=7.0.0'}
162
+
163
+ color-name@1.1.4:
164
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
165
+
166
+ concat-map@0.0.1:
167
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
168
+
169
+ cross-spawn@7.0.6:
170
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
171
+ engines: {node: '>= 8'}
172
+
173
+ debug@4.4.3:
174
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
175
+ engines: {node: '>=6.0'}
176
+ peerDependencies:
177
+ supports-color: '*'
178
+ peerDependenciesMeta:
179
+ supports-color:
180
+ optional: true
181
+
182
+ deep-is@0.1.4:
183
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
184
+
185
+ escape-string-regexp@4.0.0:
186
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
187
+ engines: {node: '>=10'}
188
+
189
+ eslint-scope@8.4.0:
190
+ resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
191
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
192
+
193
+ eslint-visitor-keys@3.4.3:
194
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
195
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
196
+
197
+ eslint-visitor-keys@4.2.1:
198
+ resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
199
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
200
+
201
+ eslint@9.39.4:
202
+ resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==}
203
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
204
+ hasBin: true
205
+ peerDependencies:
206
+ jiti: '*'
207
+ peerDependenciesMeta:
208
+ jiti:
209
+ optional: true
210
+
211
+ espree@10.4.0:
212
+ resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
213
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
214
+
215
+ esquery@1.7.0:
216
+ resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==}
217
+ engines: {node: '>=0.10'}
218
+
219
+ esrecurse@4.3.0:
220
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
221
+ engines: {node: '>=4.0'}
222
+
223
+ estraverse@5.3.0:
224
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
225
+ engines: {node: '>=4.0'}
226
+
227
+ esutils@2.0.3:
228
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
229
+ engines: {node: '>=0.10.0'}
230
+
231
+ fast-deep-equal@3.1.3:
232
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
233
+
234
+ fast-json-stable-stringify@2.1.0:
235
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
236
+
237
+ fast-levenshtein@2.0.6:
238
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
239
+
240
+ file-entry-cache@8.0.0:
241
+ resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
242
+ engines: {node: '>=16.0.0'}
243
+
244
+ find-up@5.0.0:
245
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
246
+ engines: {node: '>=10'}
247
+
248
+ flat-cache@4.0.1:
249
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
250
+ engines: {node: '>=16'}
251
+
252
+ flatted@3.4.1:
253
+ resolution: {integrity: sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==}
254
+
255
+ glob-parent@6.0.2:
256
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
257
+ engines: {node: '>=10.13.0'}
258
+
259
+ globals@14.0.0:
260
+ resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
261
+ engines: {node: '>=18'}
262
+
263
+ globals@15.15.0:
264
+ resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==}
265
+ engines: {node: '>=18'}
266
+
267
+ has-flag@4.0.0:
268
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
269
+ engines: {node: '>=8'}
270
+
271
+ hono@4.12.5:
272
+ resolution: {integrity: sha512-3qq+FUBtlTHhtYxbxheZgY8NIFnkkC/MR8u5TTsr7YZ3wixryQ3cCwn3iZbg8p8B88iDBBAYSfZDS75t8MN7Vg==}
273
+ engines: {node: '>=16.9.0'}
274
+
275
+ ignore@5.3.2:
276
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
277
+ engines: {node: '>= 4'}
278
+
279
+ import-fresh@3.3.1:
280
+ resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
281
+ engines: {node: '>=6'}
282
+
283
+ imurmurhash@0.1.4:
284
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
285
+ engines: {node: '>=0.8.19'}
286
+
287
+ is-extglob@2.1.1:
288
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
289
+ engines: {node: '>=0.10.0'}
290
+
291
+ is-glob@4.0.3:
292
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
293
+ engines: {node: '>=0.10.0'}
294
+
295
+ isexe@2.0.0:
296
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
297
+
298
+ jose@5.10.0:
299
+ resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==}
300
+
301
+ js-yaml@4.1.1:
302
+ resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
303
+ hasBin: true
304
+
305
+ json-buffer@3.0.1:
306
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
307
+
308
+ json-schema-traverse@0.4.1:
309
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
310
+
311
+ json-stable-stringify-without-jsonify@1.0.1:
312
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
313
+
314
+ keyv@4.5.4:
315
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
316
+
317
+ levn@0.4.1:
318
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
319
+ engines: {node: '>= 0.8.0'}
320
+
321
+ livekit-server-sdk@2.15.0:
322
+ resolution: {integrity: sha512-HmzjWnwEwwShu8yUf7VGFXdc+BuMJR5pnIY4qsdlhqI9d9wDgq+4cdTEHg0NEBaiGnc6PCOBiaTYgmIyVJ0S9w==}
323
+ engines: {node: '>=18'}
324
+
325
+ locate-path@6.0.0:
326
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
327
+ engines: {node: '>=10'}
328
+
329
+ lodash.merge@4.6.2:
330
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
331
+
332
+ map-obj@5.0.0:
333
+ resolution: {integrity: sha512-2L3MIgJynYrZ3TYMriLDLWocz15okFakV6J12HXvMXDHui2x/zgChzg1u9mFFGbbGWE+GsLpQByt4POb9Or+uA==}
334
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
335
+
336
+ minimatch@3.1.5:
337
+ resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==}
338
+
339
+ ms@2.1.3:
340
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
341
+
342
+ natural-compare@1.4.0:
343
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
344
+
345
+ optionator@0.9.4:
346
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
347
+ engines: {node: '>= 0.8.0'}
348
+
349
+ p-limit@3.1.0:
350
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
351
+ engines: {node: '>=10'}
352
+
353
+ p-locate@5.0.0:
354
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
355
+ engines: {node: '>=10'}
356
+
357
+ parent-module@1.0.1:
358
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
359
+ engines: {node: '>=6'}
360
+
361
+ path-exists@4.0.0:
362
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
363
+ engines: {node: '>=8'}
364
+
365
+ path-key@3.1.1:
366
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
367
+ engines: {node: '>=8'}
368
+
369
+ pg-cloudflare@1.3.0:
370
+ resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==}
371
+
372
+ pg-connection-string@2.12.0:
373
+ resolution: {integrity: sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==}
374
+
375
+ pg-int8@1.0.1:
376
+ resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
377
+ engines: {node: '>=4.0.0'}
378
+
379
+ pg-pool@3.13.0:
380
+ resolution: {integrity: sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==}
381
+ peerDependencies:
382
+ pg: '>=8.0'
383
+
384
+ pg-protocol@1.13.0:
385
+ resolution: {integrity: sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==}
386
+
387
+ pg-types@2.2.0:
388
+ resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
389
+ engines: {node: '>=4'}
390
+
391
+ pg@8.20.0:
392
+ resolution: {integrity: sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==}
393
+ engines: {node: '>= 16.0.0'}
394
+ peerDependencies:
395
+ pg-native: '>=3.0.1'
396
+ peerDependenciesMeta:
397
+ pg-native:
398
+ optional: true
399
+
400
+ pgpass@1.0.5:
401
+ resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
402
+
403
+ postgres-array@2.0.0:
404
+ resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
405
+ engines: {node: '>=4'}
406
+
407
+ postgres-bytea@1.0.1:
408
+ resolution: {integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==}
409
+ engines: {node: '>=0.10.0'}
410
+
411
+ postgres-date@1.0.7:
412
+ resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
413
+ engines: {node: '>=0.10.0'}
414
+
415
+ postgres-interval@1.2.0:
416
+ resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
417
+ engines: {node: '>=0.10.0'}
418
+
419
+ prelude-ls@1.2.1:
420
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
421
+ engines: {node: '>= 0.8.0'}
422
+
423
+ punycode@2.3.1:
424
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
425
+ engines: {node: '>=6'}
426
+
427
+ quick-lru@6.1.2:
428
+ resolution: {integrity: sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==}
429
+ engines: {node: '>=12'}
430
+
431
+ resolve-from@4.0.0:
432
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
433
+ engines: {node: '>=4'}
434
+
435
+ shebang-command@2.0.0:
436
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
437
+ engines: {node: '>=8'}
438
+
439
+ shebang-regex@3.0.0:
440
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
441
+ engines: {node: '>=8'}
442
+
443
+ split2@4.2.0:
444
+ resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
445
+ engines: {node: '>= 10.x'}
446
+
447
+ strip-json-comments@3.1.1:
448
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
449
+ engines: {node: '>=8'}
450
+
451
+ supports-color@7.2.0:
452
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
453
+ engines: {node: '>=8'}
454
+
455
+ type-check@0.4.0:
456
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
457
+ engines: {node: '>= 0.8.0'}
458
+
459
+ type-fest@4.41.0:
460
+ resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
461
+ engines: {node: '>=16'}
462
+
463
+ undici-types@6.21.0:
464
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
465
+
466
+ uri-js@4.4.1:
467
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
468
+
469
+ which@2.0.2:
470
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
471
+ engines: {node: '>= 8'}
472
+ hasBin: true
473
+
474
+ word-wrap@1.2.5:
475
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
476
+ engines: {node: '>=0.10.0'}
477
+
478
+ xtend@4.0.2:
479
+ resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
480
+ engines: {node: '>=0.4'}
481
+
482
+ yocto-queue@0.1.0:
483
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
484
+ engines: {node: '>=10'}
485
+
486
+ zod@3.25.76:
487
+ resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
488
+
489
+ snapshots:
490
+
491
+ '@bufbuild/protobuf@1.10.1': {}
492
+
493
+ '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4)':
494
+ dependencies:
495
+ eslint: 9.39.4
496
+ eslint-visitor-keys: 3.4.3
497
+
498
+ '@eslint-community/regexpp@4.12.2': {}
499
+
500
+ '@eslint/config-array@0.21.2':
501
+ dependencies:
502
+ '@eslint/object-schema': 2.1.7
503
+ debug: 4.4.3
504
+ minimatch: 3.1.5
505
+ transitivePeerDependencies:
506
+ - supports-color
507
+
508
+ '@eslint/config-helpers@0.4.2':
509
+ dependencies:
510
+ '@eslint/core': 0.17.0
511
+
512
+ '@eslint/core@0.17.0':
513
+ dependencies:
514
+ '@types/json-schema': 7.0.15
515
+
516
+ '@eslint/eslintrc@3.3.5':
517
+ dependencies:
518
+ ajv: 6.14.0
519
+ debug: 4.4.3
520
+ espree: 10.4.0
521
+ globals: 14.0.0
522
+ ignore: 5.3.2
523
+ import-fresh: 3.3.1
524
+ js-yaml: 4.1.1
525
+ minimatch: 3.1.5
526
+ strip-json-comments: 3.1.1
527
+ transitivePeerDependencies:
528
+ - supports-color
529
+
530
+ '@eslint/js@9.39.4': {}
531
+
532
+ '@eslint/object-schema@2.1.7': {}
533
+
534
+ '@eslint/plugin-kit@0.4.1':
535
+ dependencies:
536
+ '@eslint/core': 0.17.0
537
+ levn: 0.4.1
538
+
539
+ '@hono/node-server@1.19.11(hono@4.12.5)':
540
+ dependencies:
541
+ hono: 4.12.5
542
+
543
+ '@humanfs/core@0.19.1': {}
544
+
545
+ '@humanfs/node@0.16.7':
546
+ dependencies:
547
+ '@humanfs/core': 0.19.1
548
+ '@humanwhocodes/retry': 0.4.3
549
+
550
+ '@humanwhocodes/module-importer@1.0.1': {}
551
+
552
+ '@humanwhocodes/retry@0.4.3': {}
553
+
554
+ '@livekit/protocol@1.45.0':
555
+ dependencies:
556
+ '@bufbuild/protobuf': 1.10.1
557
+
558
+ '@types/estree@1.0.8': {}
559
+
560
+ '@types/json-schema@7.0.15': {}
561
+
562
+ '@types/node@22.19.15':
563
+ dependencies:
564
+ undici-types: 6.21.0
565
+
566
+ acorn-jsx@5.3.2(acorn@8.16.0):
567
+ dependencies:
568
+ acorn: 8.16.0
569
+
570
+ acorn@8.16.0: {}
571
+
572
+ ajv@6.14.0:
573
+ dependencies:
574
+ fast-deep-equal: 3.1.3
575
+ fast-json-stable-stringify: 2.1.0
576
+ json-schema-traverse: 0.4.1
577
+ uri-js: 4.4.1
578
+
579
+ ansi-styles@4.3.0:
580
+ dependencies:
581
+ color-convert: 2.0.1
582
+
583
+ argparse@2.0.1: {}
584
+
585
+ balanced-match@1.0.2: {}
586
+
587
+ brace-expansion@1.1.12:
588
+ dependencies:
589
+ balanced-match: 1.0.2
590
+ concat-map: 0.0.1
591
+
592
+ callsites@3.1.0: {}
593
+
594
+ camelcase-keys@9.1.3:
595
+ dependencies:
596
+ camelcase: 8.0.0
597
+ map-obj: 5.0.0
598
+ quick-lru: 6.1.2
599
+ type-fest: 4.41.0
600
+
601
+ camelcase@8.0.0: {}
602
+
603
+ chalk@4.1.2:
604
+ dependencies:
605
+ ansi-styles: 4.3.0
606
+ supports-color: 7.2.0
607
+
608
+ color-convert@2.0.1:
609
+ dependencies:
610
+ color-name: 1.1.4
611
+
612
+ color-name@1.1.4: {}
613
+
614
+ concat-map@0.0.1: {}
615
+
616
+ cross-spawn@7.0.6:
617
+ dependencies:
618
+ path-key: 3.1.1
619
+ shebang-command: 2.0.0
620
+ which: 2.0.2
621
+
622
+ debug@4.4.3:
623
+ dependencies:
624
+ ms: 2.1.3
625
+
626
+ deep-is@0.1.4: {}
627
+
628
+ escape-string-regexp@4.0.0: {}
629
+
630
+ eslint-scope@8.4.0:
631
+ dependencies:
632
+ esrecurse: 4.3.0
633
+ estraverse: 5.3.0
634
+
635
+ eslint-visitor-keys@3.4.3: {}
636
+
637
+ eslint-visitor-keys@4.2.1: {}
638
+
639
+ eslint@9.39.4:
640
+ dependencies:
641
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4)
642
+ '@eslint-community/regexpp': 4.12.2
643
+ '@eslint/config-array': 0.21.2
644
+ '@eslint/config-helpers': 0.4.2
645
+ '@eslint/core': 0.17.0
646
+ '@eslint/eslintrc': 3.3.5
647
+ '@eslint/js': 9.39.4
648
+ '@eslint/plugin-kit': 0.4.1
649
+ '@humanfs/node': 0.16.7
650
+ '@humanwhocodes/module-importer': 1.0.1
651
+ '@humanwhocodes/retry': 0.4.3
652
+ '@types/estree': 1.0.8
653
+ ajv: 6.14.0
654
+ chalk: 4.1.2
655
+ cross-spawn: 7.0.6
656
+ debug: 4.4.3
657
+ escape-string-regexp: 4.0.0
658
+ eslint-scope: 8.4.0
659
+ eslint-visitor-keys: 4.2.1
660
+ espree: 10.4.0
661
+ esquery: 1.7.0
662
+ esutils: 2.0.3
663
+ fast-deep-equal: 3.1.3
664
+ file-entry-cache: 8.0.0
665
+ find-up: 5.0.0
666
+ glob-parent: 6.0.2
667
+ ignore: 5.3.2
668
+ imurmurhash: 0.1.4
669
+ is-glob: 4.0.3
670
+ json-stable-stringify-without-jsonify: 1.0.1
671
+ lodash.merge: 4.6.2
672
+ minimatch: 3.1.5
673
+ natural-compare: 1.4.0
674
+ optionator: 0.9.4
675
+ transitivePeerDependencies:
676
+ - supports-color
677
+
678
+ espree@10.4.0:
679
+ dependencies:
680
+ acorn: 8.16.0
681
+ acorn-jsx: 5.3.2(acorn@8.16.0)
682
+ eslint-visitor-keys: 4.2.1
683
+
684
+ esquery@1.7.0:
685
+ dependencies:
686
+ estraverse: 5.3.0
687
+
688
+ esrecurse@4.3.0:
689
+ dependencies:
690
+ estraverse: 5.3.0
691
+
692
+ estraverse@5.3.0: {}
693
+
694
+ esutils@2.0.3: {}
695
+
696
+ fast-deep-equal@3.1.3: {}
697
+
698
+ fast-json-stable-stringify@2.1.0: {}
699
+
700
+ fast-levenshtein@2.0.6: {}
701
+
702
+ file-entry-cache@8.0.0:
703
+ dependencies:
704
+ flat-cache: 4.0.1
705
+
706
+ find-up@5.0.0:
707
+ dependencies:
708
+ locate-path: 6.0.0
709
+ path-exists: 4.0.0
710
+
711
+ flat-cache@4.0.1:
712
+ dependencies:
713
+ flatted: 3.4.1
714
+ keyv: 4.5.4
715
+
716
+ flatted@3.4.1: {}
717
+
718
+ glob-parent@6.0.2:
719
+ dependencies:
720
+ is-glob: 4.0.3
721
+
722
+ globals@14.0.0: {}
723
+
724
+ globals@15.15.0: {}
725
+
726
+ has-flag@4.0.0: {}
727
+
728
+ hono@4.12.5: {}
729
+
730
+ ignore@5.3.2: {}
731
+
732
+ import-fresh@3.3.1:
733
+ dependencies:
734
+ parent-module: 1.0.1
735
+ resolve-from: 4.0.0
736
+
737
+ imurmurhash@0.1.4: {}
738
+
739
+ is-extglob@2.1.1: {}
740
+
741
+ is-glob@4.0.3:
742
+ dependencies:
743
+ is-extglob: 2.1.1
744
+
745
+ isexe@2.0.0: {}
746
+
747
+ jose@5.10.0: {}
748
+
749
+ js-yaml@4.1.1:
750
+ dependencies:
751
+ argparse: 2.0.1
752
+
753
+ json-buffer@3.0.1: {}
754
+
755
+ json-schema-traverse@0.4.1: {}
756
+
757
+ json-stable-stringify-without-jsonify@1.0.1: {}
758
+
759
+ keyv@4.5.4:
760
+ dependencies:
761
+ json-buffer: 3.0.1
762
+
763
+ levn@0.4.1:
764
+ dependencies:
765
+ prelude-ls: 1.2.1
766
+ type-check: 0.4.0
767
+
768
+ livekit-server-sdk@2.15.0:
769
+ dependencies:
770
+ '@bufbuild/protobuf': 1.10.1
771
+ '@livekit/protocol': 1.45.0
772
+ camelcase-keys: 9.1.3
773
+ jose: 5.10.0
774
+
775
+ locate-path@6.0.0:
776
+ dependencies:
777
+ p-locate: 5.0.0
778
+
779
+ lodash.merge@4.6.2: {}
780
+
781
+ map-obj@5.0.0: {}
782
+
783
+ minimatch@3.1.5:
784
+ dependencies:
785
+ brace-expansion: 1.1.12
786
+
787
+ ms@2.1.3: {}
788
+
789
+ natural-compare@1.4.0: {}
790
+
791
+ optionator@0.9.4:
792
+ dependencies:
793
+ deep-is: 0.1.4
794
+ fast-levenshtein: 2.0.6
795
+ levn: 0.4.1
796
+ prelude-ls: 1.2.1
797
+ type-check: 0.4.0
798
+ word-wrap: 1.2.5
799
+
800
+ p-limit@3.1.0:
801
+ dependencies:
802
+ yocto-queue: 0.1.0
803
+
804
+ p-locate@5.0.0:
805
+ dependencies:
806
+ p-limit: 3.1.0
807
+
808
+ parent-module@1.0.1:
809
+ dependencies:
810
+ callsites: 3.1.0
811
+
812
+ path-exists@4.0.0: {}
813
+
814
+ path-key@3.1.1: {}
815
+
816
+ pg-cloudflare@1.3.0:
817
+ optional: true
818
+
819
+ pg-connection-string@2.12.0: {}
820
+
821
+ pg-int8@1.0.1: {}
822
+
823
+ pg-pool@3.13.0(pg@8.20.0):
824
+ dependencies:
825
+ pg: 8.20.0
826
+
827
+ pg-protocol@1.13.0: {}
828
+
829
+ pg-types@2.2.0:
830
+ dependencies:
831
+ pg-int8: 1.0.1
832
+ postgres-array: 2.0.0
833
+ postgres-bytea: 1.0.1
834
+ postgres-date: 1.0.7
835
+ postgres-interval: 1.2.0
836
+
837
+ pg@8.20.0:
838
+ dependencies:
839
+ pg-connection-string: 2.12.0
840
+ pg-pool: 3.13.0(pg@8.20.0)
841
+ pg-protocol: 1.13.0
842
+ pg-types: 2.2.0
843
+ pgpass: 1.0.5
844
+ optionalDependencies:
845
+ pg-cloudflare: 1.3.0
846
+
847
+ pgpass@1.0.5:
848
+ dependencies:
849
+ split2: 4.2.0
850
+
851
+ postgres-array@2.0.0: {}
852
+
853
+ postgres-bytea@1.0.1: {}
854
+
855
+ postgres-date@1.0.7: {}
856
+
857
+ postgres-interval@1.2.0:
858
+ dependencies:
859
+ xtend: 4.0.2
860
+
861
+ prelude-ls@1.2.1: {}
862
+
863
+ punycode@2.3.1: {}
864
+
865
+ quick-lru@6.1.2: {}
866
+
867
+ resolve-from@4.0.0: {}
868
+
869
+ shebang-command@2.0.0:
870
+ dependencies:
871
+ shebang-regex: 3.0.0
872
+
873
+ shebang-regex@3.0.0: {}
874
+
875
+ split2@4.2.0: {}
876
+
877
+ strip-json-comments@3.1.1: {}
878
+
879
+ supports-color@7.2.0:
880
+ dependencies:
881
+ has-flag: 4.0.0
882
+
883
+ type-check@0.4.0:
884
+ dependencies:
885
+ prelude-ls: 1.2.1
886
+
887
+ type-fest@4.41.0: {}
888
+
889
+ undici-types@6.21.0: {}
890
+
891
+ uri-js@4.4.1:
892
+ dependencies:
893
+ punycode: 2.3.1
894
+
895
+ which@2.0.2:
896
+ dependencies:
897
+ isexe: 2.0.0
898
+
899
+ word-wrap@1.2.5: {}
900
+
901
+ xtend@4.0.2: {}
902
+
903
+ yocto-queue@0.1.0: {}
904
+
905
+ zod@3.25.76: {}
routes/agent.js ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Hono } from 'hono'
2
+ import { query } from '../common/db.js'
3
+
4
+ import { createAgent, listAgents, updateAgent, deleteAgent } from '../dao/agent.js'
5
+ import { createCall } from '../dao/call.js'
6
+
7
+ import { callSchema, agentSchema } from '../schemas/index.js'
8
+
9
+ export const agentRouter = new Hono()
10
+
11
+ //create call
12
+ agentRouter.post('/call', async (c) => {
13
+ // console.log('got here: ', JSON.stringify(await c.req.text()))
14
+ let oriqReqTxt = await c.req.text()
15
+ // console.log('oriqReqTxt is of type: ', typeof(oriqReqTxt))
16
+ let fixedReq = oriqReqTxt.replaceAll(': }', ': \{\} }')
17
+ const result = callSchema.safeParse(JSON.parse(fixedReq))
18
+ console.log('result.data is: ', JSON.stringify(result.data, null, 2))
19
+
20
+ if (!result.success) return c.json({ error: result.error.issues[0].message }, 400)
21
+
22
+ const { callTo: formattedNumber, agentId, variables } = result.data
23
+ try {
24
+ let resp = await createCall(formattedNumber, agentId, variables)
25
+ if (resp.error) {
26
+ return c.json(resp.error, resp.code)
27
+ }
28
+ return c.json(resp)
29
+ } catch (error) {
30
+ return c.json({ error: error instanceof Error ? error.message : 'Failed to create call' }, 500)
31
+ }
32
+ })
33
+
34
+ //create agent
35
+ agentRouter.post('/', async (c) => {
36
+ const result = agentSchema.safeParse(await c.req.json())
37
+ if (!result.success) return c.json({ error: result.error.issues[0].message }, 400)
38
+ const { name, systemPrompt, stt, llm, tts, variablesSchema } = result.data
39
+ const decodedPrompt = Buffer.from(systemPrompt, 'base64').toString('utf8')
40
+
41
+ let resp = await createAgent(name, decodedPrompt, stt, llm, tts, variablesSchema)
42
+
43
+ return c.json(resp, 201)
44
+ })
45
+
46
+ //get agent
47
+ agentRouter.get('/:agentId', async (c) => {
48
+ // const { rows } = await query(`SELECT * FROM AGENT WHERE id = $1`, [c.req.param('agentId')])
49
+ const { rows } = await query('SELECT * FROM agent WHERE id = $1', [c.req.param('agentId')])
50
+ if (!rows.length) return c.json({ error: 'Agent not found' }, 404)
51
+ return c.json(rows[0])
52
+ })
53
+
54
+ //list agents
55
+ agentRouter.get('/', async (c) => {
56
+ let resp = await listAgents()
57
+
58
+ return c.json(resp)
59
+ })
60
+
61
+ agentRouter.put('/:agentId', async (c) => {
62
+ const result = agentSchema.partial().safeParse(await c.req.json())
63
+ if (!result.success) return c.json({ error: result.error.issues[0].message }, 400)
64
+ const { name, systemPrompt, stt, llm, tts, variablesSchema } = result.data
65
+ const decodedPrompt = systemPrompt ? Buffer.from(systemPrompt, 'base64').toString('utf8') : undefined
66
+
67
+ const fields = []
68
+ const values = []
69
+
70
+ let i = 1
71
+
72
+ if (name) { fields.push(`name = $${i++}`); values.push(name) }
73
+ // if (systemPrompt) { fields.push(`system_prompt = $${i++}`); values.push(systemPrompt) }
74
+ if (decodedPrompt) { fields.push(`system_prompt = $${i++}`); values.push(decodedPrompt) }
75
+ if (stt) { fields.push(`stt = $${i++}`); values.push(JSON.stringify(stt)) }
76
+ if (llm) { fields.push(`llm = $${i++}`); values.push(JSON.stringify(llm)) }
77
+ if (tts) { fields.push(`tts = $${i++}`); values.push(JSON.stringify(tts)) }
78
+ if (variablesSchema) { fields.push(`variables_schema = $${i++}`); values.push(JSON.stringify(variablesSchema)) }
79
+ if (!fields.length) return c.json({ error: 'No fields to update' }, 400)
80
+
81
+ fields.push(`updated_at = now()`)
82
+ values.push(c.req.param('agentId'))
83
+
84
+ let respRows = await updateAgent(fields, values, i)
85
+
86
+ if (!respRows.length) return c.json({ error: 'Agent not found' }, 404)
87
+ return c.json({ agentId: respRows[0].id })
88
+ })
89
+
90
+ agentRouter.delete('/:agentId', async (c) => {
91
+ let respRows = await deleteAgent(c.req.param('agentId'))
92
+
93
+ if (respRows.error) {
94
+ return c.json(respRows.error, 404)
95
+ }
96
+
97
+ return c.json({ deleted: respRows[0].id })
98
+ })
99
+
100
+
101
+
102
+
103
+
104
+
105
+
106
+
107
+
108
+
109
+
routes/trunk.js ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Hono } from 'hono'
2
+ import { SipClient } from 'livekit-server-sdk'
3
+ import { SIPTransport } from '@livekit/protocol'
4
+ import { query } from '../common/db.js'
5
+ import { createSipTrunk, getInboundTrunk } from '../dao/trunk.js'
6
+ import { getAgent } from '../dao/agent.js'
7
+ import { env } from '../config/utils.js'
8
+
9
+ import { trunkSchema, dispatchSchema } from '../schemas/index.js'
10
+
11
+ export const trunkRouter = new Hono()
12
+
13
+ const sipClient = new SipClient(env.LIVEKIT_URL, env.LIVEKIT_API_KEY, env.LIVEKIT_API_SECRET)
14
+
15
+ //List all trunks from LK; not the DB !!!
16
+ trunkRouter.get('/', async (c) => {
17
+ try {
18
+ const [inboundTrunks, outboundTrunks] = await Promise.all([
19
+ sipClient.listSipInboundTrunk({ page: { limit: 100 } }),
20
+ sipClient.listSipOutboundTrunk({ page: { limit: 100 } })
21
+ ])
22
+
23
+ const inboundTrunkIds = inboundTrunks.map(trunk => ({
24
+ id: trunk.sipTrunkId,
25
+ type: 'inbound',
26
+ name: trunk.name
27
+ }))
28
+
29
+ const outboundTrunkIds = outboundTrunks.map(trunk => ({
30
+ id: trunk.sipTrunkId,
31
+ type: 'outbound',
32
+ name: trunk.name
33
+ }))
34
+
35
+ return c.json({
36
+ trunks: [...inboundTrunkIds, ...outboundTrunkIds]
37
+ })
38
+ } catch (error) {
39
+ return c.json({ error: error.message+'. Check LK server conn' }, 500)
40
+ }
41
+ })
42
+
43
+ //create OUTBOUND/INBOUND trunk
44
+ trunkRouter.post('/', async (c) => {
45
+ const result = trunkSchema.safeParse(await c.req.json())
46
+ if (!result.success) return c.json({ error: result.error.issues[0].message }, 400)
47
+
48
+ const { name, address, numbers, authUsername, authPassword } = result.data
49
+
50
+ if (address) {
51
+ console.log('Creating OUTBOUND trunk ...')
52
+ // Create LK SIP outbound trunk
53
+ const outbound = await sipClient.createSipOutboundTrunk(
54
+ name,
55
+ address,
56
+ numbers,
57
+ {
58
+ authUsername,
59
+ authPassword,
60
+ transport: SIPTransport.SIP_TRANSPORT_AUTO
61
+ }
62
+ )
63
+
64
+ let respRows = await createSipTrunk(name, 'outbound', outbound.sipTrunkId, address)
65
+ return c.json({
66
+ outboundTrunkId: respRows[0].id
67
+ }, 201)
68
+ } else {
69
+ console.log('Creating INBOUND trunk ...')
70
+ // Create inbound trunk (no caller restrictions)
71
+ const inbound = await sipClient.createSipInboundTrunk(
72
+ name,
73
+ numbers,
74
+ {
75
+ authUsername,
76
+ authPassword,
77
+ transport: SIPTransport.SIP_TRANSPORT_AUTO
78
+ }
79
+ )
80
+
81
+ let respRows = await createSipTrunk(name, 'inbound', inbound.sipTrunkId)
82
+
83
+ return c.json({
84
+ inboundTrunkId: respRows[0].id
85
+ }, 201)
86
+ }
87
+ })
88
+
89
+ //Create INBOUND dispatch with associated agent
90
+ trunkRouter.post('/dispatch', async (c) => {
91
+ const result = dispatchSchema.safeParse(await c.req.json())
92
+ if (!result.success) return c.json({ error: result.error.issues[0].message }, 400)
93
+
94
+ const { agentId, sipTrunkId } = result.data
95
+ let inboundTrunk = await getInboundTrunk(sipTrunkId)
96
+ console.log(JSON.stringify(inboundTrunk, null, 2))
97
+
98
+ if (!inboundTrunk.length) return c.json({ error: 'Inbound trunk not found' }, 404)
99
+
100
+ let agent = await getAgent(agentId)
101
+ if (!agent.length) return c.json({ error: 'Agent not found' }, 404)
102
+
103
+ // Each inbound call gets its own room, routed to this agent
104
+ const rule = await sipClient.createSipDispatchRule(
105
+ {
106
+ type: 'individual',
107
+ roomPrefix: 'call_',
108
+ },
109
+ {
110
+ name: 'inbound-dispatch-rule',
111
+ trunkIds: [inboundTrunk[0].lk_trunk_id],
112
+ }
113
+ )
114
+
115
+ await query(
116
+ 'UPDATE agent SET lk_dispatch_rule_id = $1, updated_at = now() WHERE id = $2',
117
+ [rule.sipDispatchRuleId, agentId]
118
+ )
119
+
120
+ return c.json({ dispatchRuleId: rule.sipDispatchRuleId }, 201)
121
+ })
122
+
123
+
124
+
125
+ // trunkRouter.delete('/:agentId', async (c) => {
126
+ // const { rows } = await query(
127
+ // 'SELECT lk_trunk_id, lk_inbound_trunk_id FROM agent WHERE id = $1',
128
+ // [c.req.param('agentId')]
129
+ // )
130
+ // if (!rows.length) return c.json({ error: 'Agent not found' }, 404)
131
+
132
+ // const { lk_trunk_id, lk_inbound_trunk_id } = rows[0]
133
+ // if (!lk_trunk_id && !lk_inbound_trunk_id) return c.json({ error: 'No trunks found' }, 404)
134
+
135
+ // if (lk_trunk_id) await sipClient.deleteSipTrunk(lk_trunk_id)
136
+ // if (lk_inbound_trunk_id) await sipClient.deleteSipTrunk(lk_inbound_trunk_id)
137
+
138
+ // await query(
139
+ // 'UPDATE agent SET lk_trunk_id = NULL, lk_inbound_trunk_id = NULL, updated_at = now() WHERE id = $1',
140
+ // [c.req.param('agentId')]
141
+ // )
142
+
143
+ // return c.json({ deleted: { outbound: lk_trunk_id, inbound: lk_inbound_trunk_id } })
144
+ // })
schemas/index.js ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { z } from 'zod'
2
+
3
+ const e164Regex = /^\+[1-9]\d{6,14}$/
4
+
5
+ export const callSchema = z.object({
6
+ callTo: z.string({ required_error: 'callTo is required' })
7
+ .transform(n => `+${n.replace(/\D/g, '')}`)
8
+ .refine(n => e164Regex.test(n), {
9
+ message: 'Invalid phone number. Use E.164 format (e.g. +14155552671)'
10
+ }),
11
+ agentId: z.string({ required_error: 'agentId is required' }).uuid('agentId must be a valid UUID'),
12
+ variables: z.record(z.string().default({})).optional()
13
+ })
14
+
15
+ export const agentSchema = z.object({
16
+ name: z.string().min(1),
17
+ // systemPrompt: z.string().min(1),
18
+ systemPrompt: z.string().min(1), // base64 encoded
19
+ stt: z.object({
20
+ provider: z.enum(['deepgram', 'azure']),
21
+ model: z.string().min(1),
22
+ language: z.string().default('en-US'),
23
+ }),
24
+ llm: z.object({
25
+ provider: z.enum(['azure', 'litellm']),
26
+ endpoint: z.string().url(),
27
+ model: z.string().min(1),
28
+ apiKey: z.string().min(1),
29
+ apiVersion: z.string().optional(),
30
+ }),
31
+ tts: z.object({
32
+ provider: z.enum(['deepgram', 'azure']),
33
+ model: z.string().min(1),
34
+ voice: z.string().optional(),
35
+ }),
36
+ variablesSchema: z.record(z.string()).optional().default({}),
37
+ })
38
+
39
+ export const trunkSchema = z.object({
40
+ name: z.string().min(1),
41
+ address: z.string().optional(),
42
+ numbers: z.array(z.string()).min(1), // e.g. ["+17753176886"]
43
+ authUsername: z.string().min(1),
44
+ authPassword: z.string().min(1),
45
+ })
46
+
47
+ export const dispatchSchema = z.object({
48
+ agentId: z.string().uuid(),
49
+ sipTrunkId: z.string().uuid()
50
+ })