convoai 1.6.2 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -2
- package/dist/src/api/calls.d.ts +33 -36
- package/dist/src/api/calls.js +12 -7
- package/dist/src/api/calls.js.map +1 -1
- package/dist/src/api/numbers.d.ts +63 -0
- package/dist/src/api/numbers.js +27 -0
- package/dist/src/api/numbers.js.map +1 -0
- package/dist/src/api/types.d.ts +6 -0
- package/dist/src/commands/call/hangup.js +9 -16
- package/dist/src/commands/call/hangup.js.map +1 -1
- package/dist/src/commands/call/initiate.js +69 -112
- package/dist/src/commands/call/initiate.js.map +1 -1
- package/dist/src/commands/call/status.js +14 -32
- package/dist/src/commands/call/status.js.map +1 -1
- package/dist/src/commands/completion.js +2 -1
- package/dist/src/commands/completion.js.map +1 -1
- package/dist/src/commands/go.js +64 -0
- package/dist/src/commands/go.js.map +1 -1
- package/dist/src/commands/phone/_dashboard.d.ts +31 -0
- package/dist/src/commands/phone/_dashboard.js +127 -0
- package/dist/src/commands/phone/_dashboard.js.map +1 -0
- package/dist/src/commands/phone/_helpers.d.ts +30 -0
- package/dist/src/commands/phone/_helpers.js +133 -0
- package/dist/src/commands/phone/_helpers.js.map +1 -0
- package/dist/src/commands/phone/_modes/agent.d.ts +22 -0
- package/dist/src/commands/phone/_modes/agent.js +59 -0
- package/dist/src/commands/phone/_modes/agent.js.map +1 -0
- package/dist/src/commands/phone/_modes/free.d.ts +29 -0
- package/dist/src/commands/phone/_modes/free.js +44 -0
- package/dist/src/commands/phone/_modes/free.js.map +1 -0
- package/dist/src/commands/phone/_modes/translate.d.ts +30 -0
- package/dist/src/commands/phone/_modes/translate.js +89 -0
- package/dist/src/commands/phone/_modes/translate.js.map +1 -0
- package/dist/src/commands/phone/get.d.ts +2 -0
- package/dist/src/commands/phone/get.js +34 -0
- package/dist/src/commands/phone/get.js.map +1 -0
- package/dist/src/commands/phone/go.d.ts +2 -0
- package/dist/src/commands/phone/go.js +235 -0
- package/dist/src/commands/phone/go.js.map +1 -0
- package/dist/src/commands/phone/hangup.d.ts +2 -0
- package/dist/src/commands/phone/hangup.js +20 -0
- package/dist/src/commands/phone/hangup.js.map +1 -0
- package/dist/src/commands/phone/history.d.ts +2 -0
- package/dist/src/commands/phone/history.js +56 -0
- package/dist/src/commands/phone/history.js.map +1 -0
- package/dist/src/commands/phone/import.d.ts +2 -0
- package/dist/src/commands/phone/import.js +88 -0
- package/dist/src/commands/phone/import.js.map +1 -0
- package/dist/src/commands/phone/numbers.d.ts +2 -0
- package/dist/src/commands/phone/numbers.js +34 -0
- package/dist/src/commands/phone/numbers.js.map +1 -0
- package/dist/src/commands/phone/remove.d.ts +2 -0
- package/dist/src/commands/phone/remove.js +31 -0
- package/dist/src/commands/phone/remove.js.map +1 -0
- package/dist/src/commands/phone/send.d.ts +2 -0
- package/dist/src/commands/phone/send.js +228 -0
- package/dist/src/commands/phone/send.js.map +1 -0
- package/dist/src/commands/phone/status.d.ts +2 -0
- package/dist/src/commands/phone/status.js +34 -0
- package/dist/src/commands/phone/status.js.map +1 -0
- package/dist/src/commands/phone/update.d.ts +2 -0
- package/dist/src/commands/phone/update.js +41 -0
- package/dist/src/commands/phone/update.js.map +1 -0
- package/dist/src/commands/quickstart.js +85 -19
- package/dist/src/commands/quickstart.js.map +1 -1
- package/dist/src/config/schema.d.ts +23 -0
- package/dist/src/config/schema.js +6 -0
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/index.js +42 -3
- package/dist/src/index.js.map +1 -1
- package/package.json +1 -1
- package/src/web/phone-dashboard.html +180 -0
package/README.md
CHANGED
|
@@ -28,8 +28,9 @@ Start, manage, and monitor conversational AI agents from your terminal. One comm
|
|
|
28
28
|
- **Real-time agent monitoring** -- live status, latency, and conversation turns in the runtime panel
|
|
29
29
|
- **Built-in presets** -- start with OpenAI, Anthropic, Gemini, or Realtime in one flag
|
|
30
30
|
- **Profile management** -- switch between dev, staging, and prod with a single option
|
|
31
|
+
- **Phone calls** -- `convoai phone send` makes outbound calls with SIP, `--wait` shows live status
|
|
32
|
+
- **Number management** -- import, list, update, remove phone numbers (Twilio or BYO SIP)
|
|
31
33
|
- **Shell completions** -- tab-complete commands in bash, zsh, and fish
|
|
32
|
-
- **Telephony support (beta)** -- initiate and manage phone calls through ConvoAI
|
|
33
34
|
|
|
34
35
|
---
|
|
35
36
|
|
|
@@ -47,6 +48,9 @@ convoai dev
|
|
|
47
48
|
# Or instant voice chat — zero setup
|
|
48
49
|
convoai go
|
|
49
50
|
|
|
51
|
+
# Or make a phone call
|
|
52
|
+
convoai phone send
|
|
53
|
+
|
|
50
54
|
# Override the model on the fly
|
|
51
55
|
convoai go --model gpt-4o
|
|
52
56
|
|
|
@@ -93,6 +97,37 @@ my-app/
|
|
|
93
97
|
|
|
94
98
|
---
|
|
95
99
|
|
|
100
|
+
## Phone Go (v1.8.0)
|
|
101
|
+
|
|
102
|
+
One-command phone experience — translate calls, agent outbound, or free calls with a live dashboard:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# Interactive — choose mode, fill in details
|
|
106
|
+
convoai phone go
|
|
107
|
+
|
|
108
|
+
# Translate call — each side speaks their own language
|
|
109
|
+
convoai phone go --mode translate --lang zh:ja --to +81312345678
|
|
110
|
+
|
|
111
|
+
# Agent outbound — AI completes a task autonomously
|
|
112
|
+
convoai phone go --mode agent --task "Book dinner for 2 at 7pm" --task-lang ja --to +81312345678
|
|
113
|
+
|
|
114
|
+
# Free call — quick dial with optional AI assistance
|
|
115
|
+
convoai phone go --mode free --to +15559876543
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Live browser dashboard shows call status, translation text, and a hang-up button.
|
|
119
|
+
|
|
120
|
+
**Phone calls & number management:**
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
convoai phone send # Make an outbound call (flag mode)
|
|
124
|
+
convoai phone import # Import a number (Twilio or BYO SIP)
|
|
125
|
+
convoai phone numbers # List your numbers
|
|
126
|
+
convoai phone remove <num> # Remove a number
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
96
131
|
## Installation
|
|
97
132
|
|
|
98
133
|
### npm (recommended)
|
|
@@ -187,8 +222,9 @@ Commands are organized into scenario groups:
|
|
|
187
222
|
|---|---|
|
|
188
223
|
| **Start** | `go`, `quickstart` |
|
|
189
224
|
| **Agent** | `agent start`, `agent stop`, `agent status`, `agent list`, `agent update`, `agent speak`, `agent interrupt`, `agent history`, `agent turns`, `agent join` |
|
|
225
|
+
| **Phone** | `phone go`, `phone send`, `phone numbers`, `phone import`, `phone number`, `phone update`, `phone remove`, `phone hangup`, `phone status`, `phone history` |
|
|
190
226
|
| **Config** | `config init`, `config set`, `config get`, `config show`, `config path`, `auth login`, `auth logout`, `auth status` |
|
|
191
|
-
| **More** | `preset list`, `preset use`, `template save/list/show/delete/use`, `
|
|
227
|
+
| **More** | `preset list`, `preset use`, `template save/list/show/delete/use`, `token`, `completion` |
|
|
192
228
|
|
|
193
229
|
---
|
|
194
230
|
|
package/dist/src/api/calls.d.ts
CHANGED
|
@@ -1,53 +1,50 @@
|
|
|
1
1
|
import type { AxiosInstance } from 'axios';
|
|
2
|
-
export interface
|
|
2
|
+
export interface SendCallRequest {
|
|
3
3
|
name: string;
|
|
4
|
+
sip: {
|
|
5
|
+
to_number: string;
|
|
6
|
+
from_number: string;
|
|
7
|
+
rtc_uid: string;
|
|
8
|
+
rtc_token: string;
|
|
9
|
+
};
|
|
4
10
|
properties: {
|
|
5
11
|
channel: string;
|
|
6
|
-
|
|
7
|
-
agent_rtc_uid
|
|
12
|
+
token: string;
|
|
13
|
+
agent_rtc_uid: string;
|
|
14
|
+
remote_rtc_uids: string[];
|
|
8
15
|
idle_timeout?: number;
|
|
9
|
-
llm?:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
system_messages?: Array<{
|
|
13
|
-
role: string;
|
|
14
|
-
content: string;
|
|
15
|
-
}>;
|
|
16
|
-
greeting_message?: string;
|
|
17
|
-
params?: {
|
|
18
|
-
model?: string;
|
|
19
|
-
max_tokens?: number;
|
|
20
|
-
temperature?: number;
|
|
21
|
-
};
|
|
22
|
-
};
|
|
23
|
-
tts?: {
|
|
24
|
-
vendor?: string;
|
|
25
|
-
params?: Record<string, unknown>;
|
|
26
|
-
};
|
|
27
|
-
asr?: {
|
|
28
|
-
language?: string;
|
|
29
|
-
vendor?: string;
|
|
30
|
-
};
|
|
16
|
+
llm?: Record<string, unknown>;
|
|
17
|
+
tts?: Record<string, unknown>;
|
|
18
|
+
asr?: Record<string, unknown>;
|
|
31
19
|
};
|
|
32
20
|
}
|
|
21
|
+
export interface SendCallResponse {
|
|
22
|
+
agent_id: string;
|
|
23
|
+
}
|
|
33
24
|
export interface CallStatusResponse {
|
|
34
25
|
agent_id: string;
|
|
35
26
|
status: string;
|
|
36
|
-
direction: 'inbound' | 'outbound';
|
|
37
|
-
phone_number?: string;
|
|
38
27
|
start_ts: number;
|
|
39
|
-
|
|
28
|
+
stop_ts?: number;
|
|
29
|
+
channel?: string;
|
|
30
|
+
message?: string;
|
|
40
31
|
}
|
|
41
32
|
export declare class CallAPI {
|
|
42
33
|
private readonly client;
|
|
43
34
|
constructor(client: AxiosInstance);
|
|
44
|
-
/** Initiate an outbound phone call. */
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
status: string;
|
|
48
|
-
}>;
|
|
49
|
-
/** Hang up an active call. */
|
|
50
|
-
hangup(agentId: string): Promise<void>;
|
|
51
|
-
/** Get the current status of a call. */
|
|
35
|
+
/** Initiate an outbound phone call via SIP. */
|
|
36
|
+
send(req: SendCallRequest): Promise<SendCallResponse>;
|
|
37
|
+
/** Get the current status of a call (reuses agent status endpoint). */
|
|
52
38
|
status(agentId: string): Promise<CallStatusResponse>;
|
|
39
|
+
/** Hang up an active call (reuses agent leave endpoint). */
|
|
40
|
+
hangup(agentId: string): Promise<void>;
|
|
41
|
+
/** List calls (reuses agent list, caller can filter). */
|
|
42
|
+
list(params?: {
|
|
43
|
+
limit?: number;
|
|
44
|
+
state?: number;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
data: {
|
|
47
|
+
list: CallStatusResponse[];
|
|
48
|
+
};
|
|
49
|
+
}>;
|
|
53
50
|
}
|
package/dist/src/api/calls.js
CHANGED
|
@@ -4,18 +4,23 @@ export class CallAPI {
|
|
|
4
4
|
constructor(client) {
|
|
5
5
|
this.client = client;
|
|
6
6
|
}
|
|
7
|
-
/** Initiate an outbound phone call. */
|
|
8
|
-
async
|
|
7
|
+
/** Initiate an outbound phone call via SIP. */
|
|
8
|
+
async send(req) {
|
|
9
9
|
const { data } = await this.client.post('/call', req);
|
|
10
10
|
return data;
|
|
11
11
|
}
|
|
12
|
-
/**
|
|
12
|
+
/** Get the current status of a call (reuses agent status endpoint). */
|
|
13
|
+
async status(agentId) {
|
|
14
|
+
const { data } = await this.client.get(`/agents/${agentId}`);
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
/** Hang up an active call (reuses agent leave endpoint). */
|
|
13
18
|
async hangup(agentId) {
|
|
14
|
-
await this.client.post(`/
|
|
19
|
+
await this.client.post(`/agents/${agentId}/leave`);
|
|
15
20
|
}
|
|
16
|
-
/**
|
|
17
|
-
async
|
|
18
|
-
const { data } = await this.client.get(
|
|
21
|
+
/** List calls (reuses agent list, caller can filter). */
|
|
22
|
+
async list(params) {
|
|
23
|
+
const { data } = await this.client.get('/agents', { params });
|
|
19
24
|
return data;
|
|
20
25
|
}
|
|
21
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calls.js","sourceRoot":"","sources":["../../../src/api/calls.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"calls.js","sourceRoot":"","sources":["../../../src/api/calls.ts"],"names":[],"mappings":"AAsCA,+EAA+E;AAE/E,MAAM,OAAO,OAAO;IACW;IAA7B,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAEtD,+CAA+C;IAC/C,KAAK,CAAC,IAAI,CAAC,GAAoB;QAC7B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAmB,OAAO,EAAE,GAAG,CAAC,CAAC;QACxE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,uEAAuE;IACvE,KAAK,CAAC,MAAM,CAAC,OAAe;QAC1B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAqB,WAAW,OAAO,EAAE,CAAC,CAAC;QACjF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,4DAA4D;IAC5D,KAAK,CAAC,MAAM,CAAC,OAAe;QAC1B,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,OAAO,QAAQ,CAAC,CAAC;IACrD,CAAC;IAED,yDAAyD;IACzD,KAAK,CAAC,IAAI,CAAC,MAA2C;QACpD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAC9D,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { AxiosInstance } from 'axios';
|
|
2
|
+
export interface PhoneNumber {
|
|
3
|
+
phone_number: string;
|
|
4
|
+
label: string;
|
|
5
|
+
provider: 'byo' | 'twilio';
|
|
6
|
+
inbound: boolean;
|
|
7
|
+
outbound: boolean;
|
|
8
|
+
associated_pipeline: {
|
|
9
|
+
pipeline_id: string;
|
|
10
|
+
pipeline_name: string;
|
|
11
|
+
} | null;
|
|
12
|
+
inbound_config: {
|
|
13
|
+
allowed_addresses?: string[];
|
|
14
|
+
} | null;
|
|
15
|
+
outbound_config: {
|
|
16
|
+
address?: string;
|
|
17
|
+
transport?: 'tls' | 'tcp' | 'udp';
|
|
18
|
+
prefix?: string;
|
|
19
|
+
user?: string;
|
|
20
|
+
password?: string;
|
|
21
|
+
} | null;
|
|
22
|
+
}
|
|
23
|
+
export interface ImportNumberRequest {
|
|
24
|
+
provider: 'byo' | 'twilio';
|
|
25
|
+
phone_number: string;
|
|
26
|
+
label: string;
|
|
27
|
+
inbound?: boolean;
|
|
28
|
+
outbound?: boolean;
|
|
29
|
+
inbound_config?: {
|
|
30
|
+
allowed_addresses?: string[];
|
|
31
|
+
};
|
|
32
|
+
outbound_config: {
|
|
33
|
+
address: string;
|
|
34
|
+
transport: 'tls' | 'tcp' | 'udp';
|
|
35
|
+
prefix?: string;
|
|
36
|
+
user?: string;
|
|
37
|
+
password?: string;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export interface UpdateNumberRequest {
|
|
41
|
+
label?: string;
|
|
42
|
+
inbound?: boolean;
|
|
43
|
+
outbound?: boolean;
|
|
44
|
+
inbound_config?: {
|
|
45
|
+
allowed_addresses?: string[];
|
|
46
|
+
};
|
|
47
|
+
outbound_config?: {
|
|
48
|
+
address?: string;
|
|
49
|
+
transport?: 'tls' | 'tcp' | 'udp';
|
|
50
|
+
prefix?: string;
|
|
51
|
+
user?: string;
|
|
52
|
+
password?: string;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export declare class NumberAPI {
|
|
56
|
+
private readonly client;
|
|
57
|
+
constructor(client: AxiosInstance);
|
|
58
|
+
list(): Promise<PhoneNumber[]>;
|
|
59
|
+
import(req: ImportNumberRequest): Promise<PhoneNumber>;
|
|
60
|
+
get(phoneNumber: string): Promise<PhoneNumber>;
|
|
61
|
+
update(phoneNumber: string, req: UpdateNumberRequest): Promise<PhoneNumber>;
|
|
62
|
+
delete(phoneNumber: string): Promise<void>;
|
|
63
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// ─── Number API ─────────────────────────────────────────────────────────────
|
|
2
|
+
export class NumberAPI {
|
|
3
|
+
client;
|
|
4
|
+
constructor(client) {
|
|
5
|
+
this.client = client;
|
|
6
|
+
}
|
|
7
|
+
async list() {
|
|
8
|
+
const { data } = await this.client.get('/phone-numbers');
|
|
9
|
+
return data;
|
|
10
|
+
}
|
|
11
|
+
async import(req) {
|
|
12
|
+
const { data } = await this.client.post('/phone-numbers', req);
|
|
13
|
+
return data;
|
|
14
|
+
}
|
|
15
|
+
async get(phoneNumber) {
|
|
16
|
+
const { data } = await this.client.get(`/phone-numbers/${encodeURIComponent(phoneNumber)}`);
|
|
17
|
+
return data;
|
|
18
|
+
}
|
|
19
|
+
async update(phoneNumber, req) {
|
|
20
|
+
const { data } = await this.client.patch(`/phone-numbers/${encodeURIComponent(phoneNumber)}`, req);
|
|
21
|
+
return data;
|
|
22
|
+
}
|
|
23
|
+
async delete(phoneNumber) {
|
|
24
|
+
await this.client.delete(`/phone-numbers/${encodeURIComponent(phoneNumber)}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=numbers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"numbers.js","sourceRoot":"","sources":["../../../src/api/numbers.ts"],"names":[],"mappings":"AAoDA,+EAA+E;AAE/E,MAAM,OAAO,SAAS;IACS;IAA7B,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAEtD,KAAK,CAAC,IAAI;QACR,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAgB,gBAAgB,CAAC,CAAC;QACxE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAwB;QACnC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAc,gBAAgB,EAAE,GAAG,CAAC,CAAC;QAC5E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,WAAmB;QAC3B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAc,kBAAkB,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QACzG,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,WAAmB,EAAE,GAAwB;QACxD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAc,kBAAkB,kBAAkB,CAAC,WAAW,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAChH,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,WAAmB;QAC9B,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,kBAAkB,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAChF,CAAC;CACF"}
|
package/dist/src/api/types.d.ts
CHANGED
|
@@ -191,6 +191,11 @@ export interface ApiErrorResponse {
|
|
|
191
191
|
detail: string;
|
|
192
192
|
reason: string;
|
|
193
193
|
}
|
|
194
|
+
export interface VoiceProfile {
|
|
195
|
+
id?: string;
|
|
196
|
+
provider?: string;
|
|
197
|
+
voice_id?: string;
|
|
198
|
+
}
|
|
194
199
|
export interface ConvoAIConfig {
|
|
195
200
|
app_id?: string;
|
|
196
201
|
app_certificate?: string;
|
|
@@ -200,6 +205,7 @@ export interface ConvoAIConfig {
|
|
|
200
205
|
region?: 'global' | 'cn';
|
|
201
206
|
default_profile?: string;
|
|
202
207
|
profiles?: Record<string, ProfileConfig>;
|
|
208
|
+
voice_profile?: VoiceProfile;
|
|
203
209
|
}
|
|
204
210
|
export interface ProfileConfig {
|
|
205
211
|
app_id?: string;
|
|
@@ -1,28 +1,21 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { printSuccess
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { getCallAPI } from '../phone/_helpers.js';
|
|
3
|
+
import { printSuccess } from '../../ui/output.js';
|
|
4
4
|
import { handleError } from '../../utils/errors.js';
|
|
5
|
-
import { shortId } from '../../utils/hints.js';
|
|
6
|
-
// ─── Command Registration ──────────────────────────────────────────────────
|
|
7
5
|
export function registerCallHangup(program) {
|
|
8
6
|
program
|
|
9
7
|
.command('hangup <agent-id>')
|
|
10
|
-
.description('
|
|
11
|
-
.option('--profile <name>', 'Config profile
|
|
12
|
-
.option('--json', 'Output result as JSON')
|
|
8
|
+
.description('(deprecated) Use "phone hangup" instead')
|
|
9
|
+
.option('--profile <name>', 'Config profile')
|
|
13
10
|
.action(async (agentId, opts) => {
|
|
11
|
+
console.log(chalk.yellow(' ⚠ "call hangup" is deprecated. Use "convoai phone hangup" instead.'));
|
|
14
12
|
try {
|
|
15
13
|
const api = getCallAPI(opts.profile);
|
|
16
|
-
await
|
|
17
|
-
|
|
18
|
-
console.log(JSON.stringify({ agent_id: agentId, status: 'HUNG_UP' }, null, 2));
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
printSuccess(`Call ${shortId(agentId)} hung up.`);
|
|
22
|
-
printHint('Run `convoai call initiate --phone <number>` to start a new call.');
|
|
14
|
+
await api.hangup(agentId);
|
|
15
|
+
printSuccess('Call ended');
|
|
23
16
|
}
|
|
24
17
|
catch (error) {
|
|
25
|
-
handleError(error
|
|
18
|
+
handleError(error);
|
|
26
19
|
}
|
|
27
20
|
});
|
|
28
21
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hangup.js","sourceRoot":"","sources":["../../../../src/commands/call/hangup.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"hangup.js","sourceRoot":"","sources":["../../../../src/commands/call/hangup.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,MAAM,UAAU,kBAAkB,CAAC,OAAgB;IACjD,OAAO;SACJ,OAAO,CAAC,mBAAmB,CAAC;SAC5B,WAAW,CAAC,yCAAyC,CAAC;SACtD,MAAM,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;SAC5C,MAAM,CAAC,KAAK,EAAE,OAAe,EAAE,IAAI,EAAE,EAAE;QACtC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,sEAAsE,CAAC,CAAC,CAAC;QAClG,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACrC,MAAM,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC1B,YAAY,CAAC,YAAY,CAAC,CAAC;QAC7B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -1,133 +1,90 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { getCallAPI } from '
|
|
3
|
-
import {
|
|
4
|
-
import { printKeyValue } from '../../ui/table.js';
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { getCallAPI, getNumberAPI, getConfig, validateE164, pickOutboundNumber } from '../phone/_helpers.js';
|
|
3
|
+
import { generateRtcToken } from '../../utils/token.js';
|
|
5
4
|
import { printSuccess, printError, printHint } from '../../ui/output.js';
|
|
5
|
+
import { withSpinner } from '../../ui/spinner.js';
|
|
6
6
|
import { handleError } from '../../utils/errors.js';
|
|
7
|
-
import {
|
|
8
|
-
// ─── Name Generation ───────────────────────────────────────────────────────
|
|
9
|
-
function generateCallName() {
|
|
10
|
-
const timestamp = Date.now();
|
|
11
|
-
const random = Math.random().toString(36).substring(2, 6);
|
|
12
|
-
return `call-${timestamp}-${random}`;
|
|
13
|
-
}
|
|
14
|
-
function generateChannelName() {
|
|
15
|
-
const timestamp = Date.now();
|
|
16
|
-
const random = Math.random().toString(36).substring(2, 6);
|
|
17
|
-
return `call-ch-${timestamp}-${random}`;
|
|
18
|
-
}
|
|
19
|
-
// ─── Interactive Prompts ───────────────────────────────────────────────────
|
|
20
|
-
async function promptForPhone() {
|
|
21
|
-
const { default: inquirer } = await import('inquirer');
|
|
22
|
-
const { phone } = await inquirer.prompt([
|
|
23
|
-
{
|
|
24
|
-
type: 'input',
|
|
25
|
-
name: 'phone',
|
|
26
|
-
message: 'Phone number (E.164 format, e.g. +15551234567):',
|
|
27
|
-
},
|
|
28
|
-
]);
|
|
29
|
-
return phone;
|
|
30
|
-
}
|
|
31
|
-
// ─── Request Builder ───────────────────────────────────────────────────────
|
|
32
|
-
function buildRequest(opts) {
|
|
33
|
-
const config = resolveConfig(opts.profile);
|
|
34
|
-
const properties = {
|
|
35
|
-
channel: opts.channel ?? generateChannelName(),
|
|
36
|
-
phone_number: opts.phone,
|
|
37
|
-
};
|
|
38
|
-
// Build LLM config from CLI flags and config defaults
|
|
39
|
-
const llm = {};
|
|
40
|
-
let hasLlm = false;
|
|
41
|
-
if (opts.model) {
|
|
42
|
-
llm.params = { model: opts.model };
|
|
43
|
-
hasLlm = true;
|
|
44
|
-
}
|
|
45
|
-
if (opts.systemMessage) {
|
|
46
|
-
llm.system_messages = [{ role: 'system', content: opts.systemMessage }];
|
|
47
|
-
hasLlm = true;
|
|
48
|
-
}
|
|
49
|
-
if (opts.greeting) {
|
|
50
|
-
llm.greeting_message = opts.greeting;
|
|
51
|
-
hasLlm = true;
|
|
52
|
-
}
|
|
53
|
-
// Apply config-level LLM defaults if present
|
|
54
|
-
if (config.llm) {
|
|
55
|
-
if (config.llm.url && !llm.params) {
|
|
56
|
-
llm.url = config.llm.url;
|
|
57
|
-
hasLlm = true;
|
|
58
|
-
}
|
|
59
|
-
if (config.llm.api_key) {
|
|
60
|
-
llm.api_key = config.llm.api_key;
|
|
61
|
-
hasLlm = true;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
if (hasLlm) {
|
|
65
|
-
properties.llm = llm;
|
|
66
|
-
}
|
|
67
|
-
// Apply TTS from config
|
|
68
|
-
if (config.tts) {
|
|
69
|
-
properties.tts = config.tts;
|
|
70
|
-
}
|
|
71
|
-
// Apply ASR from config
|
|
72
|
-
if (config.asr) {
|
|
73
|
-
properties.asr = config.asr;
|
|
74
|
-
}
|
|
75
|
-
return {
|
|
76
|
-
name: generateCallName(),
|
|
77
|
-
properties,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
// ─── Command Registration ──────────────────────────────────────────────────
|
|
7
|
+
import { loadConfig } from '../../config/manager.js';
|
|
81
8
|
export function registerCallInitiate(program) {
|
|
82
9
|
program
|
|
83
10
|
.command('initiate')
|
|
84
|
-
.description('
|
|
85
|
-
.option('--phone <number>', 'Phone number
|
|
86
|
-
.option('
|
|
87
|
-
.option('--model <model>', 'LLM model
|
|
88
|
-
.option('--system-message <msg>', 'System prompt
|
|
89
|
-
.option('--greeting <msg>', 'Greeting message
|
|
90
|
-
.option('--profile <name>', 'Config profile
|
|
91
|
-
.option('--json', '
|
|
92
|
-
.option('--dry-run', '
|
|
11
|
+
.description('(deprecated) Use "phone send" instead')
|
|
12
|
+
.option('--phone <number>', 'Phone number (maps to --to)')
|
|
13
|
+
.option('--channel <name>', 'Channel name (ignored)')
|
|
14
|
+
.option('--model <model>', 'LLM model')
|
|
15
|
+
.option('--system-message <msg>', 'System prompt (maps to --task)')
|
|
16
|
+
.option('--greeting <msg>', 'Greeting message')
|
|
17
|
+
.option('--profile <name>', 'Config profile')
|
|
18
|
+
.option('--json', 'JSON output')
|
|
19
|
+
.option('--dry-run', 'Dry run')
|
|
93
20
|
.action(async (opts) => {
|
|
21
|
+
console.log(chalk.yellow(' ⚠ "call initiate" is deprecated. Use "convoai phone send" instead.'));
|
|
94
22
|
try {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
23
|
+
const config = getConfig(opts.profile);
|
|
24
|
+
const numberApi = getNumberAPI(opts.profile);
|
|
25
|
+
const callApi = getCallAPI(opts.profile);
|
|
26
|
+
// Map old flags to new
|
|
27
|
+
let toNumber = opts.phone;
|
|
28
|
+
const task = opts.systemMessage;
|
|
29
|
+
// If no phone flag, try interactive
|
|
30
|
+
if (!toNumber && process.stdin.isTTY) {
|
|
31
|
+
const { default: inquirer } = await import('inquirer');
|
|
32
|
+
const ans = await inquirer.prompt([{
|
|
33
|
+
type: 'input', name: 'to', message: 'To number (E.164):',
|
|
34
|
+
validate: (v) => /^\+[1-9]\d{1,14}$/.test(v.trim()) || 'Invalid E.164',
|
|
35
|
+
}]);
|
|
36
|
+
toNumber = ans.to;
|
|
37
|
+
}
|
|
38
|
+
if (!toNumber) {
|
|
39
|
+
printError('--phone is required.');
|
|
40
|
+
process.exit(1);
|
|
99
41
|
}
|
|
100
|
-
|
|
101
|
-
|
|
42
|
+
toNumber = validateE164(toNumber);
|
|
43
|
+
// Pick from number
|
|
44
|
+
const numbers = await numberApi.list();
|
|
45
|
+
if (numbers.length === 0) {
|
|
46
|
+
printError('No phone numbers. Run: convoai phone import');
|
|
102
47
|
process.exit(1);
|
|
103
48
|
}
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
49
|
+
const picked = await pickOutboundNumber(numbers);
|
|
50
|
+
const channelName = `call-${Date.now().toString(36)}`;
|
|
51
|
+
const configObj = loadConfig();
|
|
52
|
+
const appCert = process.env.AGORA_APP_CERTIFICATE ?? configObj.app_certificate;
|
|
53
|
+
const agentToken = await generateRtcToken(channelName, 0, 86400, config.app_id, appCert);
|
|
54
|
+
const sipToken = await generateRtcToken(channelName, 1, 86400, config.app_id, appCert);
|
|
55
|
+
if (!agentToken || !sipToken) {
|
|
56
|
+
printError('Token generation failed.');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
const llm = { ...(config.llm ?? {}) };
|
|
60
|
+
if (task)
|
|
61
|
+
llm.system_messages = [{ role: 'system', content: task }];
|
|
62
|
+
if (opts.greeting)
|
|
63
|
+
llm.greeting_message = opts.greeting;
|
|
64
|
+
if (opts.model) {
|
|
65
|
+
if (!llm.params)
|
|
66
|
+
llm.params = {};
|
|
67
|
+
llm.params.model = opts.model;
|
|
68
|
+
}
|
|
69
|
+
const request = {
|
|
70
|
+
name: `call-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
|
|
71
|
+
sip: { to_number: toNumber, from_number: picked.phone_number, rtc_uid: '1', rtc_token: sipToken },
|
|
72
|
+
properties: {
|
|
73
|
+
channel: channelName, token: agentToken, agent_rtc_uid: '0', remote_rtc_uids: ['1'],
|
|
74
|
+
idle_timeout: 600, llm, tts: config.tts ?? {}, asr: config.asr ?? {},
|
|
75
|
+
},
|
|
76
|
+
};
|
|
113
77
|
if (opts.dryRun) {
|
|
114
78
|
console.log(JSON.stringify(request, null, 2));
|
|
115
79
|
return;
|
|
116
80
|
}
|
|
117
|
-
const
|
|
118
|
-
const result = await withSpinner('Initiating call...', () => api.initiate(request));
|
|
81
|
+
const result = await withSpinner('Initiating call...', () => callApi.send(request));
|
|
119
82
|
if (opts.json) {
|
|
120
83
|
console.log(JSON.stringify(result, null, 2));
|
|
121
84
|
return;
|
|
122
85
|
}
|
|
123
|
-
printSuccess(
|
|
124
|
-
|
|
125
|
-
['Agent ID', result.agent_id],
|
|
126
|
-
['Status', result.status],
|
|
127
|
-
['Phone', phone],
|
|
128
|
-
['Channel', request.properties.channel],
|
|
129
|
-
]);
|
|
130
|
-
printHint(`Run \`convoai call status ${shortId(result.agent_id)}\` to check call status.`);
|
|
86
|
+
printSuccess(`Call initiated (${result.agent_id})`);
|
|
87
|
+
printHint(`convoai phone status ${result.agent_id}`);
|
|
131
88
|
}
|
|
132
89
|
catch (error) {
|
|
133
90
|
handleError(error, { json: opts.json });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initiate.js","sourceRoot":"","sources":["../../../../src/commands/call/initiate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"initiate.js","sourceRoot":"","sources":["../../../../src/commands/call/initiate.ts"],"names":[],"mappings":"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"}
|