typescript-agi-ipcom 0.0.8-beta.8 → 0.0.9-beta.1

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.
@@ -4,9 +4,15 @@
4
4
  "mcp__zen__analyze",
5
5
  "mcp__zen__chat",
6
6
  "Bash(npm run build:*)",
7
- "mcp__zen__challenge"
7
+ "mcp__zen__challenge",
8
+ "Bash(git log:*)",
9
+ "Bash(git show:*)",
10
+ "mcp__zen__planner",
11
+ "mcp__zen__debug",
12
+ "Read(//Users/fabiotheodoro/IPCOM/DEV/newBackend/src/services/Asterisk/DialplanFunctions/**)",
13
+ "Read(//Users/fabiotheodoro/IPCOM/DEV/newBackend/**)"
8
14
  ],
9
15
  "deny": [],
10
16
  "ask": []
11
17
  }
12
- }
18
+ }
package/CLAUDE.md CHANGED
@@ -4,7 +4,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
4
4
 
5
5
  ## Project Overview
6
6
 
7
- This is `typescript-agi`, a TypeScript library for building Asterisk AGI (Asterisk Gateway Interface) servers. It provides a type-safe interface for interacting with Asterisk PBX systems through the AGI protocol.
7
+ This is `typescript-agi-ipcom`, a TypeScript library for building Asterisk AGI (Asterisk Gateway Interface) servers. It provides a type-safe, production-ready interface for interacting with Asterisk PBX systems through the AGI protocol.
8
+
9
+ **This is an enhanced fork** of the original [typescript-agi](https://github.com/brandonlehmann/typescript-agi) by Brandon Lehmann, maintained by Fabio Theodoro / IPCOM with significant reliability and functionality improvements.
8
10
 
9
11
  ## Build System
10
12
 
@@ -84,11 +86,43 @@ npm run start
84
86
  - `close`: Socket closed
85
87
  - `error`: Socket error
86
88
  - `timeout`: Socket timeout
89
+ - `commandQueued`: Command added to queue (includes command and queue size)
90
+ - `commandProcessed`: Command completed successfully (includes duration)
91
+ - `commandFailed`: Command execution failed (includes error)
92
+ - `queueEmpty`: All commands processed
93
+ - `queueCleared`: Queue manually or automatically cleared
87
94
 
88
95
  ## Module System
89
96
 
90
97
  The project is configured as an ES module (`"type": "module"` in package.json) with dual ESM/CJS exports. The build process ensures compatibility with both module systems.
91
98
 
99
+ ## Production Improvements (v0.0.8+)
100
+
101
+ ### FIFO Command Queue System
102
+ - **Location:** `src/Channel.ts` (lines 100-105, 1463-1724)
103
+ - **Problem Solved:** Eliminates race conditions where concurrent commands could receive wrong responses
104
+ - **Implementation:** Sequential FIFO queue with backpressure control (max 100 commands)
105
+ - **APIs:** `getQueueStats()`, `clearCommandQueue()`
106
+
107
+ ### Intelligent Timeout System
108
+ - **Location:** `src/Channel.ts:getCommandTimeout()` (lines 1511-1546)
109
+ - **Fast commands (10s):** ANSWER, HANGUP, GET/SET VARIABLE, DATABASE
110
+ - **Audio commands (60s):** STREAM FILE, SAY, GET DATA, GET OPTION
111
+ - **Recording (10min):** RECORD FILE
112
+ - **Call control (6h):** EXEC (Dial, Queue, VoiceMail)
113
+ - **Override:** Pass custom timeout to `exec()` method
114
+
115
+ ### Inter-Digit Timeout Support
116
+ - **Location:** `src/Channel.ts:getDataWithInterDigitTimeout()` (lines 629-696)
117
+ - **Feature:** `getData()` can collect digits with configurable inter-digit timeout
118
+ - **Usage:** `await channel.getData(file, totalTimeout, maxDigits, interDigitTimeout)`
119
+ - **Behavior:** Audio plays first (interruptible), then digits collected with timeout between each
120
+
121
+ ### Channel Lifecycle Management
122
+ - **channelAlive flag:** Tracks connection validity, rejects commands when false
123
+ - **Cleanup handlers:** `handleChannelClose()`, `handleHangup()` reject pending commands
124
+ - **Backpressure:** Queue limit prevents memory exhaustion
125
+
92
126
  ## Development Notes
93
127
 
94
128
  - All public methods on `Channel` correspond to AGI protocol commands
@@ -96,3 +130,4 @@ The project is configured as an ES module (`"type": "module"` in package.json) w
96
130
  - Timeouts in methods are typically converted from seconds to milliseconds internally
97
131
  - Error handling: Commands throw errors when AGI returns failure codes
98
132
  - The library handles the low-level AGI protocol; applications focus on call logic
133
+ - **Queue processing:** Commands execute sequentially (FIFO), never concurrently
package/README.md CHANGED
@@ -1,72 +1,235 @@
1
- # Welcome to typescript-agi 👋
2
- ![Version](https://img.shields.io/badge/version-0.0.5-blue.svg?cacheSeconds=2592000)
3
- [![Documentation](https://img.shields.io/badge/documentation-yes-brightgreen.svg)](https://brandonlehmann.github.io/typescript-agi/)
4
- [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/brandonlehmann/typescript-agi/graphs/commit-activity)
5
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/brandonlehmann/typescript-agi/blob/master/LICENSE)
1
+ # typescript-agi-ipcom
6
2
 
7
- [![NPM](https://nodei.co/npm/typescript-agi.png?downloads=true&stars=true)](https://nodei.co/npm/typescript-agi/)
3
+ ![Version](https://img.shields.io/badge/version-0.0.8-blue.svg?cacheSeconds=2592000)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/ipcomtelecom/typescript-agi-ipcom/blob/master/LICENSE)
5
+ [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/ipcomtelecom/typescript-agi-ipcom/graphs/commit-activity)
8
6
 
9
- > NodeJS TypeScript Library for Asterisk AGI Interfaces
7
+ [![NPM](https://nodei.co/npm/typescript-agi-ipcom.png?downloads=true&stars=true)](https://nodei.co/npm/typescript-agi-ipcom/)
10
8
 
11
- ### 🏠 [Homepage](https://brandonlehmann.github.io/typescript-agi/)
9
+ > Enhanced TypeScript library for Asterisk AGI (Asterisk Gateway Interface) with production-ready improvements
12
10
 
13
- ## Install
11
+ This is a fork of [typescript-agi](https://github.com/brandonlehmann/typescript-agi) with significant reliability and functionality enhancements for production environments.
14
12
 
15
- ```sh
16
- npm install typescript-agi
17
- ```
13
+ ## Features
18
14
 
19
- ## Run tests
15
+ - **Production-Ready FIFO Command Queue**: Eliminates race conditions through sequential command processing
16
+ - **Intelligent Timeout System**: Context-aware timeouts (10 seconds to 6 hours) based on command type
17
+ - **Inter-Digit Timeout Support**: Enhanced `getData()` with configurable delays between digit inputs
18
+ - **Queue Management APIs**: Monitor and control command queue with `getQueueStats()` and `clearCommandQueue()`
19
+ - **Comprehensive Event System**: New events for command lifecycle tracking
20
+ - **Backpressure Control**: Configurable queue limits (default: 100 commands) prevent memory exhaustion
21
+ - **Channel Lifecycle Management**: `channelAlive` flag ensures proper connection state tracking
22
+ - **TypeScript Support**: Full type definitions with ESM and CommonJS outputs
23
+
24
+ ## Installation
20
25
 
21
26
  ```sh
22
- npm run test
27
+ npm install typescript-agi-ipcom
23
28
  ```
24
29
 
25
- ## Example Usage
30
+ ## Quick Start
31
+
32
+ ### Basic Usage
26
33
 
27
34
  ```typescript
28
- import {
29
- AGIServer,
30
- Channel
31
- } from 'typescript-agi';
35
+ import { AGIServer, Channel } from 'typescript-agi-ipcom';
36
+
37
+ const agiServer = new AGIServer(3000, '0.0.0.0');
38
+
39
+ agiServer.on('channel', async (channel: Channel) => {
40
+ try {
41
+ await channel.answer();
42
+ await channel.sayNumber(12345);
43
+ await channel.hangup();
44
+ } catch (error) {
45
+ console.error('Error handling channel:', error);
46
+ }
47
+ });
48
+
49
+ agiServer.start();
50
+ ```
51
+
52
+ ## Advanced Features
32
53
 
33
- const agiServer = new AGIServer();
54
+ ### Inter-Digit Timeout for DTMF Collection
34
55
 
35
- agiServer.on('channel', async(channel: Channel) => {
56
+ Collect multi-digit input with configurable inter-digit timeout:
57
+
58
+ ```typescript
59
+ agiServer.on('channel', async (channel: Channel) => {
36
60
  await channel.answer();
37
- await channel.sayNumber(12345);
38
- await channel.hangup();
61
+
62
+ // Collect up to 4 digits with 3-second timeout between digits
63
+ // Total timeout: 10 seconds after audio finishes
64
+ const result = await channel.getData(
65
+ 'enter-account-number', // audio file
66
+ 10000, // total timeout (10s)
67
+ 4, // max digits
68
+ 3000 // inter-digit timeout (3s)
69
+ );
70
+
71
+ if (result.timeout) {
72
+ await channel.streamFile('timeout');
73
+ } else {
74
+ await channel.verbose(`Collected: ${result.digits}`);
75
+ }
39
76
  });
77
+ ```
40
78
 
41
- agiServer.start();
42
- ````
79
+ ### Custom Command Timeouts
80
+
81
+ Override default timeouts for long-running operations:
82
+
83
+ ```typescript
84
+ // Execute Dial with custom 2-hour timeout
85
+ await channel.exec(
86
+ 'Dial',
87
+ 'PJSIP/user@trunk,3600,tT',
88
+ 7200000 // 2 hours in milliseconds
89
+ );
90
+
91
+ // Use Infinity for no timeout (relies on channelAlive flag)
92
+ await channel.exec('Queue', 'support,,,,,', Infinity);
93
+ ```
94
+
95
+ ### Queue Monitoring
96
+
97
+ Monitor command queue health in real-time:
98
+
99
+ ```typescript
100
+ channel.on('commandQueued', ({ command, queueSize }) => {
101
+ console.log(`Queued: ${command}, Queue size: ${queueSize}`);
102
+ });
103
+
104
+ channel.on('commandProcessed', ({ command, duration }) => {
105
+ console.log(`Processed: ${command} in ${duration}ms`);
106
+ });
107
+
108
+ channel.on('commandFailed', ({ command, error }) => {
109
+ console.error(`Failed: ${command}`, error);
110
+ });
43
111
 
44
- ## Documentation
112
+ // Get current queue statistics
113
+ const stats = channel.getQueueStats();
114
+ console.log(`Queue: ${stats.size}, Processing: ${stats.isProcessing}`);
115
+ ```
116
+
117
+ ## Intelligent Timeout System
118
+
119
+ Commands automatically use appropriate timeouts based on their type:
120
+
121
+ | Command Type | Default Timeout | Examples |
122
+ |-------------|-----------------|----------|
123
+ | Fast commands | 10 seconds | ANSWER, HANGUP, GET/SET VARIABLE, DATABASE |
124
+ | Audio commands | 60 seconds | STREAM FILE, SAY, GET DATA, GET OPTION |
125
+ | Recording | 10 minutes | RECORD FILE |
126
+ | Call control | 6 hours | EXEC (Dial, Queue, VoiceMail) |
127
+
128
+ You can override any timeout by passing a custom value to methods that support it.
129
+
130
+ ## Event System
131
+
132
+ ### AGIServer Events
133
+
134
+ - `channel`: New channel ready for interaction
135
+ - `listening`: Server started successfully
136
+ - `error`: Server error occurred
137
+ - `close`: Server stopped
138
+
139
+ ### Channel Events
140
+
141
+ Standard events:
142
+ - `ready`: Channel initialized and ready for commands
143
+ - `send`: Data sent to Asterisk
144
+ - `recv`: Raw response received from Asterisk
145
+ - `response`: Parsed response object
146
+ - `hangup`: Call hung up
147
+ - `close`: Socket closed
148
+ - `error`: Socket error
149
+ - `timeout`: Socket timeout
150
+
151
+ Queue management events:
152
+ - `commandQueued`: Command added to queue
153
+ - `commandProcessed`: Command completed successfully
154
+ - `commandFailed`: Command execution failed
155
+ - `queueEmpty`: All commands processed
156
+ - `queueCleared`: Queue manually or automatically cleared
157
+
158
+ ## API Documentation
159
+
160
+ Full API documentation is available in the [TypeDoc documentation](https://ipcomtelecom.github.io/typescript-agi-ipcom/).
161
+
162
+ ### Core Classes
45
163
 
46
- Library documentation is available at [https://brandonlehmann.github.io/typescript-agi/](https://brandonlehmann.github.io/typescript-agi/)
164
+ - **AGIServer**: TCP server that listens for AGI connections from Asterisk
165
+ - **Channel**: Represents an active AGI channel with full protocol support
166
+ - **ChannelState**: Enum for channel states (DOWN_AVAILABLE, OFF_HOOK, UP, etc.)
167
+ - **DialStatus**: Enum for Dial() results (ANSWER, BUSY, NOANSWER, etc.)
168
+ - **PlaybackStatus**: Enum for playback results (SUCCESS, USER_STOPPED, etc.)
47
169
 
48
- ## Author
170
+ ## Improvements Over Original
49
171
 
50
- 👤 **Brandon Lehmann <brandonlehmann@gmail.com>**
172
+ This fork includes the following enhancements:
51
173
 
52
- * Github: [@brandonlehmann](https://github.com/brandonlehmann)
174
+ 1. **FIFO Command Queue**: Original had race condition issues with concurrent commands receiving wrong responses. Now uses sequential queue processing.
53
175
 
54
- ## 🤝 Contributing
176
+ 2. **Intelligent Timeouts**: Original used fixed 10-second timeout for all commands. Now uses context-aware timeouts (10s - 6 hours) based on command type.
177
+
178
+ 3. **Inter-Digit Timeout**: Original `getData()` couldn't handle digit-by-digit collection with delays. New implementation supports configurable inter-digit timeouts.
179
+
180
+ 4. **Production Reliability**:
181
+ - Backpressure control (queue size limits)
182
+ - Channel lifecycle tracking (`channelAlive` flag)
183
+ - Proper cleanup on hangup/close
184
+ - Comprehensive error handling
185
+
186
+ 5. **Queue Management**: New APIs for monitoring and controlling the command queue (`getQueueStats()`, `clearCommandQueue()`).
187
+
188
+ 6. **Enhanced Events**: Additional events for command lifecycle tracking and queue management.
189
+
190
+ ## Build from Source
191
+
192
+ ```sh
193
+ # Install dependencies
194
+ npm install
195
+
196
+ # Build ESM, CommonJS, and type definitions
197
+ npm run build
198
+
199
+ # Generate TypeDoc documentation
200
+ npm run docs
201
+ ```
202
+
203
+ ## Contributing
55
204
 
56
205
  Contributions, issues and feature requests are welcome!
57
206
 
58
- Feel free to check [issues page](https://github.com/brandonlehmann/typescript-agi/issues).
207
+ Feel free to check the [issues page](https://github.com/ipcomtelecom/typescript-agi-ipcom/issues).
208
+
209
+ ## Authors
210
+
211
+ **Current Maintainer:**
212
+ - Fabio Theodoro <fabio@ipcom.com.br>
213
+ - GitHub: [@ipcomtelecom](https://github.com/ipcomtelecom)
214
+ - Company: [IPCOM](https://ipcom.com.br)
59
215
 
60
- ## Show your support
216
+ **Original Author:**
217
+ - Brandon Lehmann <brandonlehmann@gmail.com>
218
+ - GitHub: [@brandonlehmann](https://github.com/brandonlehmann)
219
+ - Original project: [typescript-agi](https://github.com/brandonlehmann/typescript-agi)
61
220
 
62
- Give a ⭐️ if this project helped you!
221
+ ## License
63
222
 
223
+ Copyright 2020 Brandon Lehmann (original work)
224
+ Copyright 2025 Fabio Theodoro / IPCOM (enhancements)
64
225
 
65
- ## 📝 License
226
+ This project is [MIT](https://github.com/ipcomtelecom/typescript-agi-ipcom/blob/master/LICENSE) licensed.
66
227
 
67
- Copyright © 2020 [Brandon Lehmann <brandonlehmann@gmail.com>](https://github.com/brandonlehmann).
228
+ ---
68
229
 
69
- This project is [MIT](https://github.com/brandonlehmann/typescript-agi/blob/master/LICENSE) licensed.
230
+ ## Support
70
231
 
71
- ***
72
- _This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_
232
+ If this project helped you, please consider:
233
+ - Starring the repository on [GitHub](https://github.com/ipcomtelecom/typescript-agi-ipcom)
234
+ - Reporting issues or suggesting features
235
+ - Contributing improvements via pull requests
@@ -1,8 +1,8 @@
1
- "use strict";var T=Object.create;var g=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var P=Object.getPrototypeOf,D=Object.prototype.hasOwnProperty;var S=(c,e)=>{for(var t in e)g(c,t,{get:e[t],enumerable:!0})},w=(c,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of I(e))!D.call(c,r)&&r!==t&&g(c,r,{get:()=>e[r],enumerable:!(s=_(e,r))||s.enumerable});return c};var R=(c,e,t)=>(t=c!=null?T(P(c)):{},w(e||!c||!c.__esModule?g(t,"default",{value:c,enumerable:!0}):t,c)),N=c=>w(g({},"__esModule",{value:!0}),c);var O={};S(O,{AGIServer:()=>p,Channel:()=>d,ChannelState:()=>E,DialStatus:()=>b,PlaybackStatus:()=>C});module.exports=N(O);var A=R(require("net"),1),v=require("events");var f=require("node:events"),n=require("node:util");var h=class{m_args=[];addArgument(e,t){this.m_args.push({key:e,value:t})}string(e){for(let t of this.m_args)if(t.key===e)return t.value;return""}number(e){for(let t of this.m_args)if(t.key===e)return parseInt(t.value,10);return 0}boolean(e){for(let t of this.m_args)if(t.key===e)return t.value;return!1}char(e){let t=this.number(e);return t!==0?String.fromCharCode(t):""}nokey(){for(let e of this.m_args)if(e.key!=="result"&&e.value)return e.key;return""}};var E=(a=>(a[a.DOWN_AVAILABLE=0]="DOWN_AVAILABLE",a[a.DOWN_RESERVED=1]="DOWN_RESERVED",a[a.OFF_HOOK=2]="OFF_HOOK",a[a.DIGITS_DIALED=3]="DIGITS_DIALED",a[a.RINGING=4]="RINGING",a[a.REMOTE_RINGING=5]="REMOTE_RINGING",a[a.UP=6]="UP",a[a.BUSY=7]="BUSY",a))(E||{}),b=(m=>(m[m.ANSWER=0]="ANSWER",m[m.BUSY=1]="BUSY",m[m.NOANSWER=2]="NOANSWER",m[m.CANCEL=3]="CANCEL",m[m.CONGESTION=4]="CONGESTION",m[m.CHANUNAVAIL=5]="CHANUNAVAIL",m[m.DONTCALL=6]="DONTCALL",m[m.TORTURE=7]="TORTURE",m[m.INVALIDARGS=8]="INVALIDARGS",m))(b||{}),C=(r=>(r[r.SUCCESS=0]="SUCCESS",r[r.USER_STOPPED=1]="USER_STOPPED",r[r.REMOTE_STOPPED=2]="REMOTE_STOPPED",r[r.ERROR=3]="ERROR",r))(C||{}),d=class extends f.EventEmitter{m_connection;m_state;m_message="";m_network="";m_network_script="";m_request="";m_channel="";m_language="";m_type="";m_uniqueid="";m_version="";m_callerid="";m_calleridname="";m_callingpres="";m_callingani2="";m_callington="";m_callingtns="";m_dnid="";m_rdnis="";m_context="";m_extension="";m_priority="";m_enhanced="";m_accountcode="";m_threadid="";commandQueue=[];isProcessingCommand=!1;channelAlive=!0;maxQueueSize=100;defaultCommandTimeout=1e4;maxCallDuration=216e5;constructor(e){super(),this.setMaxListeners(10),this.m_connection=e,this.m_state=0,this.m_connection.on("data",t=>this.read(t)),this.m_connection.on("close",()=>{this.handleChannelClose(),this.emit("close")}),this.m_connection.on("error",t=>this.emit("error",t)),this.m_connection.on("timeout",()=>this.emit("timeout")),this.on("hangup",()=>{this.handleHangup(),this.close()})}on(e,t){return super.on(e,t)}get network(){return this.m_network.toLowerCase()==="yes"}get network_script(){return this.m_network_script}get version(){return this.m_version}get request(){return this.m_request}get channel(){return this.m_channel}get language(){return this.m_language}get type(){return this.m_type}get uniqueid(){return this.m_uniqueid}get callerid(){return this.m_callerid}get calleridname(){return this.m_calleridname}get callingpres(){return this.m_callingpres}get callingani2(){return this.m_callingani2}get callington(){return this.m_callington}get callingtns(){return this.m_callingtns}get dnid(){return this.m_dnid}get rdnis(){return this.m_rdnis}get context(){return this.m_context}get extension(){return this.m_extension}get priority(){return this.m_priority}get enhanced(){return this.m_enhanced}get accountcode(){return this.m_accountcode}get threadid(){return this.m_threadid}async answer(){let e=await this.sendCommand("ANSWER");if(e.code!==200||e.result!==0)throw new Error("Could not answer call")}async break(){let e=await this.sendCommand("ASYNCAGI BREAK");if(e.code!==200||e.result!==0)throw new Error("Could not interrupt processing");return this.close()}async channelStatus(e){let t=await this.sendCommand((0,n.format)("CHANNEL STATUS %s",e||""));if(t.code!==200||t.result===-1)throw new Error("Could not get channel status");return t.result}async controlStreamFile(e,t="",s,r,o,i){let u=await this.sendCommand((0,n.format)('CONTROL STREAM FILE %s "%s" %s %s %s %s',e,t,s||"",r||"",o||"",i||""));if(u.code!==200||u.result===-1)throw new Error("Could not control stream file");let a=await this.getVariable("CPLAYBACKSTATUS "),m=await this.getVariable("CPLAYBACKOFFSET "),l=3;switch(a.toUpperCase()){case"SUCCESS":l=0;break;case"USERSTOPPED":l=1;break;case"REMOTESTOPPED":l=2;break}return{digit:u.arguments.char("result"),playbackStatus:l,playbackOffset:parseInt(m,10)}}async dial(e,t=30,s){switch(await this.exec("Dial",(0,n.format)("%s,%s,%s",e,t,s||"")),(await this.getVariable("DIALSTATUS")).toUpperCase()){case"ANSWER":return 0;case"BUSY":return 1;case"NOANSWER":return 2;case"CANCEL":return 3;case"CONGESTION":return 4;case"CHANUNAVAIL":return 5;case"DONTCALL":return 6;case"TORTURE":return 7;case"INVALIDARGS":return 8;default:throw new Error("Unknown dial status")}}async databaseDel(e,t){let s=await this.sendCommand((0,n.format)("DATABASE DEL %s %s",e,t));if(s.code!==200||s.result===0)throw new Error("Could not delete from the database")}async databaseDelTree(e,t){let s=await this.sendCommand((0,n.format)("DATABASE DELTREE %s %s",e,t||""));if(s.code!==200)throw new Error("Could not delete tree from database");return s.result===0}async databaseGet(e,t){let s=await this.sendCommand((0,n.format)("DATABASE GET %s %s",e,t));if(s.code!==200||s.result===0)throw new Error("Database key not set");return s.arguments.nokey()}async databasePut(e,t,s){let r=await this.sendCommand((0,n.format)("DATABASE PUT %s %s %s",e,t,s));if(r.code!==200||r.result===0)throw new Error("Database key not set");return r.arguments.string("value")}async exec(e,t,s){let r=await this.sendCommand((0,n.format)("EXEC %s %s",e,t),s);if(r.code!==200||r.result===-2)throw new Error("Could not execute application");return r.result}async getData(e,t=5e3,s,r){if(s&&s>1&&r)return this.getDataWithInterDigitTimeout(e,t,s,r);let o=s!==void 0?(0,n.format)("GET DATA %s %s %s",e,t,s):(0,n.format)("GET DATA %s %s",e,t);console.log("[getData] DEBUG - Command:",o),console.log("[getData] DEBUG - Params:",{soundFile:e,timeout:t,maxDigits:s});let i=await this.sendCommand(o);if(console.log("[getData] DEBUG - Response:",{code:i.code,result:i.result,arguments:i.arguments,rawResult:i.arguments.string("result"),rawValue:i.arguments.string("value")}),i.code!==200||i.result===-1)throw new Error("Could not get data from channel");let u={digits:i.arguments.string("result"),timeout:i.arguments.string("value")==="(timeout)"};return console.log("[getData] DEBUG - Final result:",u),u}async getDataWithInterDigitTimeout(e,t,s,r){console.log("[getDataWithInterDigitTimeout] DEBUG - Starting collection",{soundFile:e,totalTimeout:t,maxDigits:s,interDigitTimeout:r});let o="";try{let u=await this.sendCommand((0,n.format)('STREAM FILE %s "%s"',e,"0123456789*#"));if(u.code===200&&u.result!==-1){let a=u.arguments.char("result");if(a&&(o+=a,console.log("[getDataWithInterDigitTimeout] DEBUG - Digit pressed during playback:",a),o.length===s))return console.log("[getDataWithInterDigitTimeout] DEBUG - Max digits reached during playback"),{digits:o,timeout:!1}}}catch(u){console.log("[getDataWithInterDigitTimeout] DEBUG - Error playing file:",u)}let i=Date.now();for(console.log("[getDataWithInterDigitTimeout] DEBUG - Audio finished, waiting for remaining digits",{collectedDigits:o,totalTimeout:t,interDigitTimeout:r});o.length<s;){let u=Date.now()-i,a=t-u;if(a<=0)return console.log("[getDataWithInterDigitTimeout] DEBUG - Total timeout exceeded"),{digits:o,timeout:o.length===0};let m=Math.min(r,a);console.log("[getDataWithInterDigitTimeout] DEBUG - Waiting for digit",{collectedSoFar:o,timeoutToUse:m,remainingTotalTimeout:a});try{let l=await this.waitForDigit(m/1e3);if(!l)return console.log("[getDataWithInterDigitTimeout] DEBUG - Inter-digit timeout"),{digits:o,timeout:o.length===0};if(o+=l,console.log("[getDataWithInterDigitTimeout] DEBUG - Digit collected:",l),o.length===s)return console.log("[getDataWithInterDigitTimeout] DEBUG - Max digits reached"),{digits:o,timeout:!1}}catch(l){return console.log("[getDataWithInterDigitTimeout] DEBUG - Error waiting for digit:",l),{digits:o,timeout:!0}}}return{digits:o,timeout:!1}}async getFullVariable(e,t){let s=await this.sendCommand((0,n.format)("GET FULL VARIABLE %s %s",e.toUpperCase(),t||""));if(s.code!==200||s.result===0)throw new Error("Variable not set");return s.arguments.nokey()}async getOption(e,t="#",s=5){let r=await this.sendCommand((0,n.format)('GET OPTION %s "%s" %s',e,t,s*1e3));if(r.code!==200||r.result===-1)throw new Error("Could not get option");if(r.arguments.number("endpos")===0)throw new Error("Could Not play file");return{digit:r.arguments.char("result"),endpos:r.arguments.number("endpos")}}async getVariable(e){let t=await this.sendCommand((0,n.format)("GET VARIABLE %s",e.toUpperCase()));if(t.code!==200||t.result===0)throw new Error("Variable not set");return t.arguments.nokey()}async goSub(e,t,s,r){let o=await this.sendCommand((0,n.format)("GOSUB %s %s %s %s",e,t,s,r||""));if(o.code!==200||o.result!==0)throw new Error("Could not execute gosub")}async hangup(e){let t=await this.sendCommand((0,n.format)("HANGUP %s",e||""));if(t.code!==200||t.result!==1)throw new Error("Could not hang up call")}async noop(){let e=await this.sendCommand("NOOP");if(e.code!==200||e.result!==0)throw new Error("Could not NOOP")}async receiveChar(e=5){let t=await this.sendCommand((0,n.format)("RECEIVE CHAR %s",e*1e3));if(t.code!==200||t.result===-1)throw new Error("Could not get data from channel");return{char:t.arguments.char("result"),timeout:t.arguments.boolean("timeout")}}async receiveText(e=5){let t=await this.sendCommand((0,n.format)("RECEIVE TEXT %s",e*1e3));if(t.code!==200||t.result===-1)throw new Error("Could not get data from channel");return t.arguments.string("result")}async recordFile(e,t="gsm",s="#",r=10,o,i,u){let a=await this.sendCommand((0,n.format)('RECORD FILE %s %s "%s" %s %s %s %s',e,t,s,r*1e3,u||"",o?"BEEP":"",i?(0,n.format)("s=%s",i):""));if(a.code!==200||a.result===-1)throw new Error("Could not record file");return{digit:a.arguments.char("result"),endpos:a.arguments.number("endpos"),timeout:a.arguments.boolean("timeout")}}async sayAlpha(e,t="#"){let s=await this.sendCommand((0,n.format)('SAY ALPHA %s "%s"',e,t));if(s.code!==200||s.result===-1)throw new Error("Could not say alpha");return s.arguments.char("result")}async sayDate(e,t="#"){let s=await this.sendCommand((0,n.format)('SAY DATE %s "%s"',typeof e=="number"?e:Math.floor(e.getTime()/1e3),t));if(s.code!==200||s.result===-1)throw new Error("Could not say date");return s.arguments.char("result")}async sayDateTime(e,t="#",s,r){let o=await this.sendCommand((0,n.format)('SAY DATETIME %s "%s" %s %s',typeof e=="number"?e:Math.floor(e.getTime()/1e3),t,s||"",r||""));if(o.code!==200||o.result===-1)throw new Error("Could not say date time");return o.arguments.char("result")}async sayDigits(e,t="#"){let s=await this.sendCommand((0,n.format)('SAY DIGITS %s "%s"',e,t));if(s.code!==200||s.result===-1)throw new Error("Could not say digits");return s.arguments.char("result")}async sayNumber(e,t="#"){let s=await this.sendCommand((0,n.format)('SAY NUMBER %s "%s"',e,t));if(s.code!==200||s.result===-1)throw new Error("Could not say number");return s.arguments.char("result")}async sayPhonetic(e,t="#"){let s=await this.sendCommand((0,n.format)('SAY PHONETIC %s "%s"',e,t));if(s.code!==200||s.result===-1)throw new Error("Could not say phonetic");return s.arguments.char("result")}async sayTime(e,t="#"){let s=await this.sendCommand((0,n.format)('SAY TIME %s "%s"',typeof e=="number"?e:Math.floor(e.getTime()/1e3),t));if(s.code!==200||s.result===-1)throw new Error("Could not say time");return s.arguments.char("result")}async sendImage(e){let t=await this.sendCommand((0,n.format)("SEND IMAGE %s",e));if(t.code!==200||t.result!==0)throw new Error("Could not send image")}async sendText(e){let t=await this.sendCommand((0,n.format)('SEND TEXT "%s"',e));if(t.code!==200||t.result!==0)throw new Error("Could not send text")}async setAutoHangup(e=60){let t=await this.sendCommand((0,n.format)("SET AUTOHANGUP %s",e));if(t.code!==200||t.result!==0)throw new Error("Could not set auto hangup")}async setCallerID(e,t){let s=t?(0,n.format)('"%s"<%s>',t,e):e,r=await this.sendCommand((0,n.format)("SET CALLERID %s",s));if(r.code!==200||r.result!==1)throw new Error("Could not set caller id")}async setContext(e){let t=await this.sendCommand((0,n.format)("SET CONTEXT %s",e));if(t.code!==200||t.result!==0)throw new Error("Could not set context")}async setExtension(e){let t=await this.sendCommand((0,n.format)("SET EXTENSION %s",e));if(t.code!==200||t.result!==0)throw new Error("Could not set extension")}async setMusic(e=!0,t){let s=await this.sendCommand((0,n.format)("SET MUSIC %s %s",e?"ON":"OFF",t||""));if(s.code!==200||s.result!==0)throw new Error("Could not set priority")}async setPriority(e){let t=await this.sendCommand((0,n.format)("SET PRIORITY %s",e));if(t.code!==200||t.result!==0)throw new Error("Could not set priority")}async setVariable(e,t){let s=await this.sendCommand((0,n.format)('SET VARIABLE %s "%s"',e.toUpperCase(),t));if(s.code!==200||s.result!==1)throw new Error("Could not set variable")}async speechActivateGrammar(e){return this.sendCommand((0,n.format)("SPEECH ACTIVATE GRAMMAR %s",e))}async speechCreate(e){return this.sendCommand((0,n.format)("SPEECH CREATE ENGINE %s",e))}async speechDeactivateGrammar(e){return this.sendCommand((0,n.format)("SPEECH DEACTIVATE GRAMMAR %s",e))}async speechDestroy(){return this.sendCommand("SPEECH DESTROY")}async speechLoadGrammar(e,t){return this.sendCommand((0,n.format)("SPEECH LOAD GRAMMAR %s %s",e,t))}async speechRecognize(e,t=5,s){return this.sendCommand((0,n.format)("SPEECH RECOGNIZE %s %s %s",e,t*1e3,s))}async speechSet(e,t){return this.sendCommand((0,n.format)("SPEECH SET %s %s",e,t))}async speedUnloadGrammar(e){return this.sendCommand((0,n.format)("SPEECH UNLOAD GRAMMAR %s",e))}async streamFile(e,t="#",s){let r=await this.sendCommand((0,n.format)('STREAM FILE %s "%s" %s',e,t,s||""));if(r.code!==200||r.result===-1)throw new Error("Could not stream file");if((await this.getVariable("PLAYBACKSTATUS ")).toUpperCase()!=="SUCCESS")throw new Error("Could not stream file");return{digit:r.arguments.char("result"),endpos:r.arguments.number("endpos")}}async tddMode(e){let t=await this.sendCommand((0,n.format)("TDD MODE %s",e?"ON":"OFF"));if(t.code!==200||t.result!==1)throw new Error("Could not set TDD mode")}async verbose(e,t){let s=await this.sendCommand((0,n.format)('VERBOSE "%s" %s',e,t||""));if(s.code!==200||s.result!==1)throw new Error("Could not send logging message")}async waitForDigit(e=5){let t=await this.sendCommand((0,n.format)("WAIT FOR DIGIT %s",e*1e3));if(t.code!==200||t.result===-1)throw new Error("Could not wait for digit");return t.arguments.char("result")}close(){this.m_connection.destroy()}read(e){if(e.length!==0){if(this.m_message+=e.toString(),this.m_state===0){if(this.m_message.indexOf(`
1
+ "use strict";var _=Object.create;var h=Object.defineProperty;var T=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var I=Object.getPrototypeOf,S=Object.prototype.hasOwnProperty;var R=(c,e)=>{for(var t in e)h(c,t,{get:e[t],enumerable:!0})},w=(c,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of P(e))!S.call(c,r)&&r!==t&&h(c,r,{get:()=>e[r],enumerable:!(s=T(e,r))||s.enumerable});return c};var N=(c,e,t)=>(t=c!=null?_(I(c)):{},w(e||!c||!c.__esModule?h(t,"default",{value:c,enumerable:!0}):t,c)),O=c=>w(h({},"__esModule",{value:!0}),c);var D={};R(D,{AGIServer:()=>p,Channel:()=>d,ChannelState:()=>b,DialStatus:()=>E,PlaybackStatus:()=>C});module.exports=O(D);var A=N(require("net"),1),v=require("events");var f=require("node:events"),n=require("node:util");var g=class{m_args=[];addArgument(e,t){this.m_args.push({key:e,value:t})}string(e){for(let t of this.m_args)if(t.key===e)return t.value;return""}number(e){for(let t of this.m_args)if(t.key===e)return parseInt(t.value,10);return 0}boolean(e){for(let t of this.m_args)if(t.key===e)return t.value;return!1}char(e){let t=this.number(e);return t!==0?String.fromCharCode(t):""}nokey(){for(let e of this.m_args)if(e.key!=="result"&&e.value)return e.key;return""}};var b=(a=>(a[a.DOWN_AVAILABLE=0]="DOWN_AVAILABLE",a[a.DOWN_RESERVED=1]="DOWN_RESERVED",a[a.OFF_HOOK=2]="OFF_HOOK",a[a.DIGITS_DIALED=3]="DIGITS_DIALED",a[a.RINGING=4]="RINGING",a[a.REMOTE_RINGING=5]="REMOTE_RINGING",a[a.UP=6]="UP",a[a.BUSY=7]="BUSY",a))(b||{}),E=(u=>(u[u.ANSWER=0]="ANSWER",u[u.BUSY=1]="BUSY",u[u.NOANSWER=2]="NOANSWER",u[u.CANCEL=3]="CANCEL",u[u.CONGESTION=4]="CONGESTION",u[u.CHANUNAVAIL=5]="CHANUNAVAIL",u[u.DONTCALL=6]="DONTCALL",u[u.TORTURE=7]="TORTURE",u[u.INVALIDARGS=8]="INVALIDARGS",u))(E||{}),C=(r=>(r[r.SUCCESS=0]="SUCCESS",r[r.USER_STOPPED=1]="USER_STOPPED",r[r.REMOTE_STOPPED=2]="REMOTE_STOPPED",r[r.ERROR=3]="ERROR",r))(C||{}),d=class extends f.EventEmitter{m_connection;m_state;m_message="";m_network="";m_network_script="";m_request="";m_channel="";m_language="";m_type="";m_uniqueid="";m_version="";m_callerid="";m_calleridname="";m_callingpres="";m_callingani2="";m_callington="";m_callingtns="";m_dnid="";m_rdnis="";m_context="";m_extension="";m_priority="";m_enhanced="";m_accountcode="";m_threadid="";commandQueue=[];isProcessingCommand=!1;channelAlive=!0;maxQueueSize=100;defaultCommandTimeout=1e4;maxCallDuration=216e5;constructor(e){super(),this.setMaxListeners(10),this.m_connection=e,this.m_state=0,this.m_connection.on("data",t=>this.read(t)),this.m_connection.on("close",()=>{this.handleChannelClose(),this.emit("close")}),this.m_connection.on("error",t=>this.emit("error",t)),this.m_connection.on("timeout",()=>this.emit("timeout")),this.on("hangup",()=>{this.handleHangup(),this.close()})}on(e,t){return super.on(e,t)}get network(){return this.m_network.toLowerCase()==="yes"}get network_script(){return this.m_network_script}get version(){return this.m_version}get request(){return this.m_request}get channel(){return this.m_channel}get language(){return this.m_language}get type(){return this.m_type}get uniqueid(){return this.m_uniqueid}get callerid(){return this.m_callerid}get calleridname(){return this.m_calleridname}get callingpres(){return this.m_callingpres}get callingani2(){return this.m_callingani2}get callington(){return this.m_callington}get callingtns(){return this.m_callingtns}get dnid(){return this.m_dnid}get rdnis(){return this.m_rdnis}get context(){return this.m_context}get extension(){return this.m_extension}get priority(){return this.m_priority}get enhanced(){return this.m_enhanced}get accountcode(){return this.m_accountcode}get threadid(){return this.m_threadid}async answer(){let e=await this.sendCommand("ANSWER");if(e.code!==200||e.result!==0)throw new Error("Could not answer call")}async break(){let e=await this.sendCommand("ASYNCAGI BREAK");if(e.code!==200||e.result!==0)throw new Error("Could not interrupt processing");return this.close()}async channelStatus(e){let t=await this.sendCommand((0,n.format)("CHANNEL STATUS %s",e||""));if(t.code!==200||t.result===-1)throw new Error("Could not get channel status");return t.result}async controlStreamFile(e,t="",s,r,o,i){let m=await this.sendCommand((0,n.format)('CONTROL STREAM FILE %s "%s" %s %s %s %s',e,t,s||"",r||"",o||"",i||""));if(m.code!==200||m.result===-1)throw new Error("Could not control stream file");let a=await this.getVariable("CPLAYBACKSTATUS "),u=await this.getVariable("CPLAYBACKOFFSET "),l=3;switch(a.toUpperCase()){case"SUCCESS":l=0;break;case"USERSTOPPED":l=1;break;case"REMOTESTOPPED":l=2;break}return{digit:m.arguments.char("result"),playbackStatus:l,playbackOffset:parseInt(u,10)}}async dial(e,t=30,s){switch(await this.exec("Dial",(0,n.format)("%s,%s,%s",e,t,s||"")),(await this.getVariable("DIALSTATUS")).toUpperCase()){case"ANSWER":return 0;case"BUSY":return 1;case"NOANSWER":return 2;case"CANCEL":return 3;case"CONGESTION":return 4;case"CHANUNAVAIL":return 5;case"DONTCALL":return 6;case"TORTURE":return 7;case"INVALIDARGS":return 8;default:throw new Error("Unknown dial status")}}async databaseDel(e,t){let s=await this.sendCommand((0,n.format)("DATABASE DEL %s %s",e,t));if(s.code!==200||s.result===0)throw new Error("Could not delete from the database")}async databaseDelTree(e,t){let s=await this.sendCommand((0,n.format)("DATABASE DELTREE %s %s",e,t||""));if(s.code!==200)throw new Error("Could not delete tree from database");return s.result===0}async databaseGet(e,t){let s=await this.sendCommand((0,n.format)("DATABASE GET %s %s",e,t));if(s.code!==200||s.result===0)throw new Error("Database key not set");return s.arguments.nokey()}async databasePut(e,t,s){let r=await this.sendCommand((0,n.format)("DATABASE PUT %s %s %s",e,t,s));if(r.code!==200||r.result===0)throw new Error("Database key not set");return r.arguments.string("value")}async exec(e,t,s){let r=await this.sendCommand((0,n.format)("EXEC %s %s",e,t),s);if(r.code!==200||r.result===-2)throw new Error("Could not execute application");return r.result}async getData(e,t=5e3,s,r){if(s&&s>1&&r)return this.getDataWithInterDigitTimeout(e,t,s,r);let o=s!==void 0?(0,n.format)("GET DATA %s %s %s",e,t,s):(0,n.format)("GET DATA %s %s",e,t),i=await this.sendCommand(o);if(i.code!==200||i.result===-1)throw new Error("Could not get data from channel");return{digits:i.arguments.string("result"),timeout:i.arguments.string("value")==="(timeout)"}}async getDataWithInterDigitTimeout(e,t,s,r){let o="";try{let m=await this.sendCommand((0,n.format)('STREAM FILE %s "%s"',e,"0123456789*#"));if(m.code===200&&m.result!==-1){let a=m.arguments.char("result");if(a&&(o+=a,o.length===s))return{digits:o,timeout:!1}}}catch{}let i=Date.now();for(;o.length<s;){let m=Date.now()-i,a=t-m;if(a<=0)return{digits:o,timeout:o.length===0};let u=Math.min(r,a);try{let l=await this.waitForDigit(u/1e3);if(!l)return{digits:o,timeout:o.length===0};if(o+=l,o.length===s)return{digits:o,timeout:!1}}catch{return{digits:o,timeout:!0}}}return{digits:o,timeout:!1}}async getFullVariable(e,t){let s=await this.sendCommand((0,n.format)("GET FULL VARIABLE %s %s",e.toUpperCase(),t||""));if(s.code!==200||s.result===0)throw new Error("Variable not set");return s.arguments.nokey()}async getOption(e,t="#",s=5){let r=await this.sendCommand((0,n.format)('GET OPTION %s "%s" %s',e,t,s*1e3));if(r.code!==200||r.result===-1)throw new Error("Could not get option");if(r.arguments.number("endpos")===0)throw new Error("Could Not play file");return{digit:r.arguments.char("result"),endpos:r.arguments.number("endpos")}}async getVariable(e){let t=await this.sendCommand((0,n.format)("GET VARIABLE %s",e.toUpperCase()));if(t.code!==200||t.result===0)throw new Error("Variable not set");return t.arguments.nokey()}async goSub(e,t,s,r){let o=await this.sendCommand((0,n.format)("GOSUB %s %s %s %s",e,t,s,r||""));if(o.code!==200||o.result!==0)throw new Error("Could not execute gosub")}async hangup(e){let t=await this.sendCommand((0,n.format)("HANGUP %s",e||""));if(t.code!==200||t.result!==1)throw new Error("Could not hang up call")}async noop(){let e=await this.sendCommand("NOOP");if(e.code!==200||e.result!==0)throw new Error("Could not NOOP")}async receiveChar(e=5){let t=await this.sendCommand((0,n.format)("RECEIVE CHAR %s",e*1e3));if(t.code!==200||t.result===-1)throw new Error("Could not get data from channel");return{char:t.arguments.char("result"),timeout:t.arguments.boolean("timeout")}}async receiveText(e=5){let t=await this.sendCommand((0,n.format)("RECEIVE TEXT %s",e*1e3));if(t.code!==200||t.result===-1)throw new Error("Could not get data from channel");return t.arguments.string("result")}async recordFile(e,t="gsm",s="#",r=10,o,i,m){let a=await this.sendCommand((0,n.format)('RECORD FILE %s %s "%s" %s %s %s %s',e,t,s,r*1e3,m||"",o?"BEEP":"",i?(0,n.format)("s=%s",i):""));if(a.code!==200||a.result===-1)throw new Error("Could not record file");return{digit:a.arguments.char("result"),endpos:a.arguments.number("endpos"),timeout:a.arguments.boolean("timeout")}}async sayAlpha(e,t="#"){let s=await this.sendCommand((0,n.format)('SAY ALPHA %s "%s"',e,t));if(s.code!==200||s.result===-1)throw new Error("Could not say alpha");return s.arguments.char("result")}async sayDate(e,t="#"){let s=await this.sendCommand((0,n.format)('SAY DATE %s "%s"',typeof e=="number"?e:Math.floor(e.getTime()/1e3),t));if(s.code!==200||s.result===-1)throw new Error("Could not say date");return s.arguments.char("result")}async sayDateTime(e,t="#",s,r){let o=await this.sendCommand((0,n.format)('SAY DATETIME %s "%s" %s %s',typeof e=="number"?e:Math.floor(e.getTime()/1e3),t,s||"",r||""));if(o.code!==200||o.result===-1)throw new Error("Could not say date time");return o.arguments.char("result")}async sayDigits(e,t="#"){let s=await this.sendCommand((0,n.format)('SAY DIGITS %s "%s"',e,t));if(s.code!==200||s.result===-1)throw new Error("Could not say digits");return s.arguments.char("result")}async sayNumber(e,t="#"){let s=await this.sendCommand((0,n.format)('SAY NUMBER %s "%s"',e,t));if(s.code!==200||s.result===-1)throw new Error("Could not say number");return s.arguments.char("result")}async sayPhonetic(e,t="#"){let s=await this.sendCommand((0,n.format)('SAY PHONETIC %s "%s"',e,t));if(s.code!==200||s.result===-1)throw new Error("Could not say phonetic");return s.arguments.char("result")}async sayTime(e,t="#"){let s=await this.sendCommand((0,n.format)('SAY TIME %s "%s"',typeof e=="number"?e:Math.floor(e.getTime()/1e3),t));if(s.code!==200||s.result===-1)throw new Error("Could not say time");return s.arguments.char("result")}async sendImage(e){let t=await this.sendCommand((0,n.format)("SEND IMAGE %s",e));if(t.code!==200||t.result!==0)throw new Error("Could not send image")}async sendText(e){let t=await this.sendCommand((0,n.format)('SEND TEXT "%s"',e));if(t.code!==200||t.result!==0)throw new Error("Could not send text")}async setAutoHangup(e=60){let t=await this.sendCommand((0,n.format)("SET AUTOHANGUP %s",e));if(t.code!==200||t.result!==0)throw new Error("Could not set auto hangup")}async setCallerID(e,t){let s=t?(0,n.format)('"%s"<%s>',t,e):e,r=await this.sendCommand((0,n.format)("SET CALLERID %s",s));if(r.code!==200||r.result!==1)throw new Error("Could not set caller id")}async setContext(e){let t=await this.sendCommand((0,n.format)("SET CONTEXT %s",e));if(t.code!==200||t.result!==0)throw new Error("Could not set context")}async setExtension(e){let t=await this.sendCommand((0,n.format)("SET EXTENSION %s",e));if(t.code!==200||t.result!==0)throw new Error("Could not set extension")}async setMusic(e=!0,t){let s=await this.sendCommand((0,n.format)("SET MUSIC %s %s",e?"ON":"OFF",t||""));if(s.code!==200||s.result!==0)throw new Error("Could not set priority")}async setPriority(e){let t=await this.sendCommand((0,n.format)("SET PRIORITY %s",e));if(t.code!==200||t.result!==0)throw new Error("Could not set priority")}async setVariable(e,t){let s=await this.sendCommand((0,n.format)('SET VARIABLE %s "%s"',e.toUpperCase(),t));if(s.code!==200||s.result!==1)throw new Error("Could not set variable")}async speechActivateGrammar(e){return this.sendCommand((0,n.format)("SPEECH ACTIVATE GRAMMAR %s",e))}async speechCreate(e){return this.sendCommand((0,n.format)("SPEECH CREATE ENGINE %s",e))}async speechDeactivateGrammar(e){return this.sendCommand((0,n.format)("SPEECH DEACTIVATE GRAMMAR %s",e))}async speechDestroy(){return this.sendCommand("SPEECH DESTROY")}async speechLoadGrammar(e,t){return this.sendCommand((0,n.format)("SPEECH LOAD GRAMMAR %s %s",e,t))}async speechRecognize(e,t=5,s){return this.sendCommand((0,n.format)("SPEECH RECOGNIZE %s %s %s",e,t*1e3,s))}async speechSet(e,t){return this.sendCommand((0,n.format)("SPEECH SET %s %s",e,t))}async speedUnloadGrammar(e){return this.sendCommand((0,n.format)("SPEECH UNLOAD GRAMMAR %s",e))}async streamFile(e,t="#",s){let r=await this.sendCommand((0,n.format)('STREAM FILE %s "%s" %s',e,t,s||""));if(r.code!==200||r.result===-1)throw new Error("Could not stream file");if((await this.getVariable("PLAYBACKSTATUS ")).toUpperCase()!=="SUCCESS")throw new Error("Could not stream file");return{digit:r.arguments.char("result"),endpos:r.arguments.number("endpos")}}async tddMode(e){let t=await this.sendCommand((0,n.format)("TDD MODE %s",e?"ON":"OFF"));if(t.code!==200||t.result!==1)throw new Error("Could not set TDD mode")}async verbose(e,t){let s=await this.sendCommand((0,n.format)('VERBOSE "%s" %s',e,t||""));if(s.code!==200||s.result!==1)throw new Error("Could not send logging message")}async waitForDigit(e=5){let t=e*1e3+2e3,s=await this.sendCommand((0,n.format)("WAIT FOR DIGIT %s",e*1e3),t);if(s.code!==200||s.result===-1)throw new Error("Could not wait for digit");return s.arguments.char("result")}close(){this.m_connection.destroy()}read(e){if(e.length!==0){if(this.m_message+=e.toString(),this.m_state===0){if(this.m_message.indexOf(`
2
2
 
3
3
  `)===-1)return;this.readVariables(this.m_message)}else if(this.m_state===2){if(this.m_message.indexOf(`
4
4
  `)===-1)return;this.readResponse(this.m_message)}this.m_message=""}}readVariables(e){e.split(`
5
5
  `).map(s=>{let r=s.split(":"),o=(r[0]||"").trim(),i=(r[1]||"").trim();switch(o.substring(4)){case"network":return this.m_network=i;case"network_script":return this.m_network_script=i;case"request":return this.m_request=i;case"channel":return this.m_channel=i;case"language":return this.m_language=i;case"type":return this.m_type=i;case"uniqueid":return this.m_uniqueid=i;case"version":return this.m_version=i;case"callerid":return this.m_callerid=i;case"calleridname":return this.m_calleridname=i;case"callingpres":return this.m_callingpres=i;case"callingani2":return this.m_callingani2=i;case"callington":return this.m_callington=i;case"callingtns":return this.m_callingtns=i;case"dnid":return this.m_dnid=i;case"rdnis":return this.m_rdnis=i;case"context":return this.m_context=i;case"extension":return this.m_extension=i;case"priority":return this.m_priority=i;case"enhanced":return this.m_enhanced=i;case"accountcode":return this.m_accountcode=i;case"threadid":return this.m_threadid=i}}),this.m_state=2,this.emit("ready")}readResponse(e){e.split(`
6
- `).map(s=>this.readResponseLine(s))}readResponseLine(e){if(!e)return;this.emit("recv",e);let t=e.split(" ");if(!t||t[0]==="HANGUP")return this.emit("hangup");let s=parseInt(t[0],10);t.shift();let r=new h;for(let i of t)if(i.indexOf("=")!==-1){let u=i.split("=",2),a=u[0].trim(),m=u[1].trim();r.addArgument(a,m)}else if(i.indexOf("(")!==-1){let u=i.substring(1,i.length-1);r.addArgument(u,!0)}else r.addArgument("value",i);let o={code:s,result:r.number("result"),arguments:r};this.emit("response",o)}async send(e){return new Promise((t,s)=>{if(!this.m_connection||this.m_connection.destroyed)return s(new Error("Stream has been destroyed"));this.emit("send",e),this.m_connection.write(e,r=>{if(r)return s(r);t()})})}async sendCommand(e,t){return new Promise((s,r)=>{if(!this.channelAlive)return r(new Error("Channel is no longer alive"));if(this.commandQueue.length>=this.maxQueueSize)return r(new Error(`Command queue full (${this.maxQueueSize}). Command rejected: ${e}`));let o={command:e,resolve:s,reject:r,timeout:t??this.getCommandTimeout(e),timestamp:Date.now()};this.commandQueue.push(o),this.emit("commandQueued",{command:e,queueSize:this.commandQueue.length}),this.isProcessingCommand||this.processCommandQueue()})}getCommandTimeout(e){return e.startsWith("ANSWER")||e.startsWith("HANGUP")||e.startsWith("CHANNEL STATUS")||e.startsWith("GET VARIABLE")||e.startsWith("SET VARIABLE")||e.startsWith("DATABASE")?1e4:e.startsWith("STREAM FILE")||e.startsWith("SAY ")||e.startsWith("GET DATA")||e.startsWith("GET OPTION")?6e4:e.startsWith("RECORD FILE")?6e5:e.startsWith("EXEC")?this.maxCallDuration:this.defaultCommandTimeout}async processCommandQueue(){if(this.commandQueue.length===0){this.isProcessingCommand=!1,this.emit("queueEmpty");return}this.isProcessingCommand=!0;let e=this.commandQueue.shift();try{let t=await this.sendCommandInternal(e.command,e.timeout);this.emit("commandProcessed",{command:e.command,duration:Date.now()-e.timestamp}),e.resolve(t)}catch(t){this.emit("commandFailed",{command:e.command,error:t}),e.reject(t)}finally{setImmediate(()=>this.processCommandQueue())}}sendCommandInternal(e,t){return new Promise((s,r)=>{if(!this.channelAlive||!this.m_connection||this.m_connection.destroyed)return r(new Error("Channel is no longer alive"));console.log("[sendCommandInternal] DEBUG - Sending command:",e),console.log("[sendCommandInternal] DEBUG - Timeout:",t);let o=null,i=a=>{console.log("[sendCommandInternal] DEBUG - Received response:",a),u(),s(a)},u=()=>{this.removeListener("response",i),o&&(clearTimeout(o),o=null)};this.once("response",i),t>0&&t!==1/0&&(o=setTimeout(()=>{u(),r(new Error(`Command timeout after ${t}ms: ${e}`))},t)),this.send((0,n.format)(`%s
7
- `,e.trim())).catch(a=>{u(),r(a)})})}handleChannelClose(){this.channelAlive=!1;let e=this.commandQueue.length;if(e>0)for(this.emit("queueCleared",{reason:"channel_closed",commandsCleared:e});this.commandQueue.length>0;)this.commandQueue.shift().reject(new Error("Channel closed before command could be sent"));this.isProcessingCommand=!1}handleHangup(){this.channelAlive=!1;let e=this.commandQueue.length;if(e>0)for(this.emit("queueCleared",{reason:"hangup",commandsCleared:e});this.commandQueue.length>0;)this.commandQueue.shift().reject(new Error("Call hung up before command could be executed"));this.isProcessingCommand=!1}clearCommandQueue(){let e=this.commandQueue.length;for(;this.commandQueue.length>0;)this.commandQueue.shift().reject(new Error("Command queue manually cleared"));return this.isProcessingCommand=!1,this.emit("queueCleared",{reason:"manual",commandsCleared:e}),e}getQueueStats(){let e=this.commandQueue[0];return{size:this.commandQueue.length,isProcessing:this.isProcessingCommand,oldestCommandAge:e?Date.now()-e.timestamp:null}}};var y=require("util"),p=class extends v.EventEmitter{m_port;m_ip;m_server=A.createServer();constructor(e=3e3,t="0.0.0.0"){super(),this.setMaxListeners(10),this.m_port=e,this.m_ip=t,this.m_server.on("connection",s=>{let r=new d(s);r.on("ready",()=>this.emit("channel",r))})}on(e,t){return super.on(e,t)}async start(){this.m_server.on("error",e=>this.emit("error",new Error((0,y.format)("Internal TCP server error: %s",e.toString())))),this.m_server.on("close",()=>this.emit("close")),this.m_server.listen(this.m_port,this.m_ip,()=>{this.emit("listening",this.m_port,this.m_ip)})}async stop(){return new Promise((e,t)=>{this.m_server.close(s=>s?t(s):e())})}};0&&(module.exports={AGIServer,Channel,ChannelState,DialStatus,PlaybackStatus});
6
+ `).map(s=>this.readResponseLine(s))}readResponseLine(e){if(!e)return;this.emit("recv",e);let t=e.split(" ");if(!t||t[0]==="HANGUP")return this.emit("hangup");let s=parseInt(t[0],10);t.shift();let r=new g;for(let i of t)if(i.indexOf("=")!==-1){let m=i.split("=",2),a=m[0].trim(),u=m[1].trim();r.addArgument(a,u)}else if(i.indexOf("(")!==-1){let m=i.substring(1,i.length-1);r.addArgument(m,!0)}else r.addArgument("value",i);let o={code:s,result:r.number("result"),arguments:r};this.emit("response",o)}async send(e){return new Promise((t,s)=>{if(!this.m_connection||this.m_connection.destroyed)return s(new Error("Stream has been destroyed"));this.emit("send",e),this.m_connection.write(e,r=>{if(r)return s(r);t()})})}async sendCommand(e,t){return new Promise((s,r)=>{if(!this.channelAlive)return r(new Error("Channel is no longer alive"));if(this.commandQueue.length>=this.maxQueueSize)return r(new Error(`Command queue full (${this.maxQueueSize}). Command rejected: ${e}`));let o={command:e,resolve:s,reject:r,timeout:t??this.getCommandTimeout(e),timestamp:Date.now()};this.commandQueue.push(o),this.emit("commandQueued",{command:e,queueSize:this.commandQueue.length}),this.isProcessingCommand||this.processCommandQueue()})}getCommandTimeout(e){return e.startsWith("ANSWER")||e.startsWith("HANGUP")||e.startsWith("CHANNEL STATUS")||e.startsWith("GET VARIABLE")||e.startsWith("SET VARIABLE")||e.startsWith("DATABASE")?1e4:e.startsWith("STREAM FILE")||e.startsWith("SAY ")||e.startsWith("GET DATA")||e.startsWith("GET OPTION")?6e4:e.startsWith("RECORD FILE")?6e5:e.startsWith("EXEC")?this.maxCallDuration:this.defaultCommandTimeout}async processCommandQueue(){if(this.commandQueue.length===0){this.isProcessingCommand=!1,this.emit("queueEmpty");return}this.isProcessingCommand=!0;let e=this.commandQueue.shift();try{let t=await this.sendCommandInternal(e.command,e.timeout);this.emit("commandProcessed",{command:e.command,duration:Date.now()-e.timestamp}),e.resolve(t)}catch(t){this.emit("commandFailed",{command:e.command,error:t}),e.reject(t)}finally{setImmediate(()=>this.processCommandQueue())}}sendCommandInternal(e,t){return new Promise((s,r)=>{if(!this.channelAlive||!this.m_connection||this.m_connection.destroyed)return r(new Error("Channel is no longer alive"));let o=null,i=a=>{m(),s(a)},m=()=>{this.removeListener("response",i),o&&(clearTimeout(o),o=null)};this.once("response",i),t>0&&t!==1/0&&(o=setTimeout(()=>{m(),r(new Error(`Command timeout after ${t}ms: ${e}`))},t)),this.send((0,n.format)(`%s
7
+ `,e.trim())).catch(a=>{m(),r(a)})})}handleChannelClose(){this.channelAlive=!1;let e=this.commandQueue.length;if(e>0)for(this.emit("queueCleared",{reason:"channel_closed",commandsCleared:e});this.commandQueue.length>0;)this.commandQueue.shift().reject(new Error("Channel closed before command could be sent"));this.isProcessingCommand=!1}handleHangup(){this.channelAlive=!1;let e=this.commandQueue.length;if(e>0)for(this.emit("queueCleared",{reason:"hangup",commandsCleared:e});this.commandQueue.length>0;)this.commandQueue.shift().reject(new Error("Call hung up before command could be executed"));this.isProcessingCommand=!1}clearCommandQueue(){let e=this.commandQueue.length;for(;this.commandQueue.length>0;)this.commandQueue.shift().reject(new Error("Command queue manually cleared"));return this.isProcessingCommand=!1,this.emit("queueCleared",{reason:"manual",commandsCleared:e}),e}getQueueStats(){let e=this.commandQueue[0];return{size:this.commandQueue.length,isProcessing:this.isProcessingCommand,oldestCommandAge:e?Date.now()-e.timestamp:null}}};var y=require("util"),p=class extends v.EventEmitter{m_port;m_ip;m_server=A.createServer();constructor(e=3e3,t="0.0.0.0"){super(),this.setMaxListeners(10),this.m_port=e,this.m_ip=t,this.m_server.on("connection",s=>{let r=new d(s);r.on("ready",()=>this.emit("channel",r))})}on(e,t){return super.on(e,t)}async start(){this.m_server.on("error",e=>this.emit("error",new Error((0,y.format)("Internal TCP server error: %s",e.toString())))),this.m_server.on("close",()=>this.emit("close")),this.m_server.listen(this.m_port,this.m_ip,()=>{this.emit("listening",this.m_port,this.m_ip)})}async stop(){return new Promise((e,t)=>{this.m_server.close(s=>s?t(s):e())})}};0&&(module.exports={AGIServer,Channel,ChannelState,DialStatus,PlaybackStatus});
8
8
  //# sourceMappingURL=index.cjs.map