convoai 1.6.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/src/api/calls.d.ts +33 -36
  2. package/dist/src/api/calls.js +12 -7
  3. package/dist/src/api/calls.js.map +1 -1
  4. package/dist/src/api/numbers.d.ts +63 -0
  5. package/dist/src/api/numbers.js +27 -0
  6. package/dist/src/api/numbers.js.map +1 -0
  7. package/dist/src/commands/call/hangup.js +9 -16
  8. package/dist/src/commands/call/hangup.js.map +1 -1
  9. package/dist/src/commands/call/initiate.js +69 -112
  10. package/dist/src/commands/call/initiate.js.map +1 -1
  11. package/dist/src/commands/call/status.js +14 -32
  12. package/dist/src/commands/call/status.js.map +1 -1
  13. package/dist/src/commands/completion.js +2 -1
  14. package/dist/src/commands/completion.js.map +1 -1
  15. package/dist/src/commands/go.js +64 -0
  16. package/dist/src/commands/go.js.map +1 -1
  17. package/dist/src/commands/phone/_helpers.d.ts +12 -0
  18. package/dist/src/commands/phone/_helpers.js +98 -0
  19. package/dist/src/commands/phone/_helpers.js.map +1 -0
  20. package/dist/src/commands/phone/get.d.ts +2 -0
  21. package/dist/src/commands/phone/get.js +34 -0
  22. package/dist/src/commands/phone/get.js.map +1 -0
  23. package/dist/src/commands/phone/hangup.d.ts +2 -0
  24. package/dist/src/commands/phone/hangup.js +20 -0
  25. package/dist/src/commands/phone/hangup.js.map +1 -0
  26. package/dist/src/commands/phone/history.d.ts +2 -0
  27. package/dist/src/commands/phone/history.js +56 -0
  28. package/dist/src/commands/phone/history.js.map +1 -0
  29. package/dist/src/commands/phone/import.d.ts +2 -0
  30. package/dist/src/commands/phone/import.js +88 -0
  31. package/dist/src/commands/phone/import.js.map +1 -0
  32. package/dist/src/commands/phone/numbers.d.ts +2 -0
  33. package/dist/src/commands/phone/numbers.js +34 -0
  34. package/dist/src/commands/phone/numbers.js.map +1 -0
  35. package/dist/src/commands/phone/remove.d.ts +2 -0
  36. package/dist/src/commands/phone/remove.js +31 -0
  37. package/dist/src/commands/phone/remove.js.map +1 -0
  38. package/dist/src/commands/phone/send.d.ts +2 -0
  39. package/dist/src/commands/phone/send.js +236 -0
  40. package/dist/src/commands/phone/send.js.map +1 -0
  41. package/dist/src/commands/phone/status.d.ts +2 -0
  42. package/dist/src/commands/phone/status.js +34 -0
  43. package/dist/src/commands/phone/status.js.map +1 -0
  44. package/dist/src/commands/phone/update.d.ts +2 -0
  45. package/dist/src/commands/phone/update.js +41 -0
  46. package/dist/src/commands/phone/update.js.map +1 -0
  47. package/dist/src/commands/quickstart.js +85 -19
  48. package/dist/src/commands/quickstart.js.map +1 -1
  49. package/dist/src/index.js +40 -3
  50. package/dist/src/index.js.map +1 -1
  51. package/package.json +1 -1
@@ -0,0 +1,34 @@
1
+ import chalk from 'chalk';
2
+ import { getNumberAPI } from './_helpers.js';
3
+ import { handleError } from '../../utils/errors.js';
4
+ export function registerPhoneNumbers(phone) {
5
+ phone
6
+ .command('numbers')
7
+ .description('List imported phone numbers')
8
+ .option('--profile <name>', 'Config profile')
9
+ .option('--json', 'JSON output')
10
+ .action(async (opts) => {
11
+ try {
12
+ const api = getNumberAPI(opts.profile);
13
+ const numbers = await api.list();
14
+ if (opts.json) {
15
+ console.log(JSON.stringify(numbers, null, 2));
16
+ return;
17
+ }
18
+ if (numbers.length === 0) {
19
+ console.log(chalk.dim('\n No phone numbers. Run: convoai phone import\n'));
20
+ return;
21
+ }
22
+ console.log('');
23
+ for (const n of numbers) {
24
+ const dir = [n.outbound && 'outbound', n.inbound && 'inbound'].filter(Boolean).join('+');
25
+ console.log(` ${chalk.cyan(n.phone_number)} ${chalk.dim(dir)} ${n.label} ${chalk.dim(n.provider)}`);
26
+ }
27
+ console.log('');
28
+ }
29
+ catch (error) {
30
+ handleError(error, { json: opts.json });
31
+ }
32
+ });
33
+ }
34
+ //# sourceMappingURL=numbers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"numbers.js","sourceRoot":"","sources":["../../../../src/commands/phone/numbers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,KAAK;SACF,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,6BAA6B,CAAC;SAC1C,MAAM,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;SAC5C,MAAM,CAAC,QAAQ,EAAE,aAAa,CAAC;SAC/B,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvC,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAEjC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC9C,OAAO;YACT,CAAC;YAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC,CAAC;gBAC5E,OAAO;YACT,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;gBACxB,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,IAAI,UAAU,EAAE,CAAC,CAAC,OAAO,IAAI,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACzF,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAC1G,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerPhoneRemove(phone: Command): void;
@@ -0,0 +1,31 @@
1
+ import { getNumberAPI } from './_helpers.js';
2
+ import { printSuccess } from '../../ui/output.js';
3
+ import { handleError } from '../../utils/errors.js';
4
+ export function registerPhoneRemove(phone) {
5
+ phone
6
+ .command('remove <phone-number>')
7
+ .description('Remove a phone number')
8
+ .option('--force', 'Skip confirmation')
9
+ .option('--profile <name>', 'Config profile')
10
+ .action(async (phoneNumber, opts) => {
11
+ try {
12
+ if (!opts.force && process.stdin.isTTY) {
13
+ const { default: inquirer } = await import('inquirer');
14
+ const { confirm } = await inquirer.prompt([{
15
+ type: 'confirm', name: 'confirm',
16
+ message: `Remove ${phoneNumber}?`,
17
+ default: false,
18
+ }]);
19
+ if (!confirm)
20
+ return;
21
+ }
22
+ const api = getNumberAPI(opts.profile);
23
+ await api.delete(phoneNumber);
24
+ printSuccess(`Number removed: ${phoneNumber}`);
25
+ }
26
+ catch (error) {
27
+ handleError(error);
28
+ }
29
+ });
30
+ }
31
+ //# sourceMappingURL=remove.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove.js","sourceRoot":"","sources":["../../../../src/commands/phone/remove.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,KAAK;SACF,OAAO,CAAC,uBAAuB,CAAC;SAChC,WAAW,CAAC,uBAAuB,CAAC;SACpC,MAAM,CAAC,SAAS,EAAE,mBAAmB,CAAC;SACtC,MAAM,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;SAC5C,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE;QAClC,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBACvC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;gBACvD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;wBACzC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS;wBAChC,OAAO,EAAE,UAAU,WAAW,GAAG;wBACjC,OAAO,EAAE,KAAK;qBACf,CAAC,CAAC,CAAC;gBACJ,IAAI,CAAC,OAAO;oBAAE,OAAO;YACvB,CAAC;YAED,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvC,MAAM,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YAC9B,YAAY,CAAC,mBAAmB,WAAW,EAAE,CAAC,CAAC;QACjD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerPhoneSend(phone: Command): void;
@@ -0,0 +1,236 @@
1
+ import chalk from 'chalk';
2
+ import { getCallAPI, getNumberAPI, getConfig, validateE164, pickOutboundNumber } from './_helpers.js';
3
+ import { generateRtcToken } from '../../utils/token.js';
4
+ import { printSuccess, printError, printHint } from '../../ui/output.js';
5
+ import { withSpinner } from '../../ui/spinner.js';
6
+ import { handleError } from '../../utils/errors.js';
7
+ import { track } from '../../utils/telemetry.js';
8
+ async function runInlineImport(profileName) {
9
+ const { default: inquirer } = await import('inquirer');
10
+ const { getNumberAPI: getNumAPI, validateE164: valE164 } = await import('./_helpers.js');
11
+ console.log(chalk.yellow('\n No phone numbers found. Let\'s import one.\n'));
12
+ const ans = await inquirer.prompt([
13
+ { type: 'list', name: 'provider', message: 'Provider:', choices: ['twilio', 'byo'] },
14
+ { type: 'input', name: 'number', message: 'Phone number (E.164):', validate: (v) => /^\+[1-9]\d{1,14}$/.test(v.trim()) || 'Invalid format' },
15
+ { type: 'input', name: 'label', message: 'Label:', validate: (v) => v.trim().length > 0 || 'Required' },
16
+ { type: 'input', name: 'address', message: 'SIP address:', validate: (v) => v.trim().length > 0 || 'Required' },
17
+ { type: 'list', name: 'transport', message: 'Transport:', choices: ['tls', 'tcp', 'udp'], default: 'tls' },
18
+ { type: 'input', name: 'user', message: 'SIP username (optional):' },
19
+ { type: 'password', name: 'password', message: 'SIP password (optional):', mask: '*' },
20
+ ]);
21
+ const api = getNumAPI(profileName);
22
+ await api.import({
23
+ provider: ans.provider,
24
+ phone_number: valE164(ans.number),
25
+ label: ans.label,
26
+ outbound: true,
27
+ inbound: false,
28
+ outbound_config: { address: ans.address, transport: ans.transport, user: ans.user || undefined, password: ans.password || undefined },
29
+ });
30
+ printSuccess(`Number imported: ${ans.number}`);
31
+ console.log('');
32
+ }
33
+ export function registerPhoneSend(phone) {
34
+ phone
35
+ .command('send')
36
+ .description('Make an outbound phone call')
37
+ .option('--from <number>', 'Caller ID (E.164)')
38
+ .option('--to <number>', 'Target number (E.164)')
39
+ .option('--task <prompt>', 'What the AI should do')
40
+ .option('--greeting <text>', 'Opening line')
41
+ .option('--model <model>', 'LLM model override')
42
+ .option('--wait', 'Wait and show status until call ends')
43
+ .option('--max-duration <mins>', 'Max call length in minutes', '10')
44
+ .option('--profile <name>', 'Config profile')
45
+ .option('--json', 'JSON output')
46
+ .option('--dry-run', 'Show request without sending')
47
+ .action(async (opts) => {
48
+ try {
49
+ const config = getConfig(opts.profile);
50
+ const numberApi = getNumberAPI(opts.profile);
51
+ const callApi = getCallAPI(opts.profile);
52
+ // 1. Resolve "from" number
53
+ let fromNumber = opts.from;
54
+ if (!fromNumber && process.stdin.isTTY) {
55
+ let numbers = await numberApi.list();
56
+ if (numbers.length === 0) {
57
+ await runInlineImport(opts.profile);
58
+ numbers = await numberApi.list();
59
+ }
60
+ const picked = await pickOutboundNumber(numbers);
61
+ fromNumber = picked.phone_number;
62
+ }
63
+ if (!fromNumber) {
64
+ printError('--from is required. Provide a caller number or run interactively.');
65
+ process.exit(1);
66
+ }
67
+ fromNumber = validateE164(fromNumber);
68
+ // Validate from number is outbound-capable
69
+ try {
70
+ const numDetail = await numberApi.get(fromNumber);
71
+ if (!numDetail.outbound) {
72
+ printError(`${fromNumber} does not support outbound calls.`);
73
+ printHint('Import an outbound number: convoai phone import');
74
+ process.exit(1);
75
+ }
76
+ }
77
+ catch {
78
+ printError(`Number ${fromNumber} not found in your account.`);
79
+ printHint('List numbers: convoai phone numbers');
80
+ process.exit(1);
81
+ }
82
+ // 2. Resolve "to" number
83
+ let toNumber = opts.to;
84
+ if (!toNumber && process.stdin.isTTY) {
85
+ const { default: inquirer } = await import('inquirer');
86
+ const ans = await inquirer.prompt([{
87
+ type: 'input', name: 'to', message: 'To number (E.164):',
88
+ validate: (v) => /^\+[1-9]\d{1,14}$/.test(v.trim()) || 'Invalid E.164 format',
89
+ }]);
90
+ toNumber = ans.to;
91
+ }
92
+ if (!toNumber) {
93
+ printError('--to is required.');
94
+ process.exit(1);
95
+ }
96
+ toNumber = validateE164(toNumber);
97
+ // 3. Resolve task/prompt
98
+ let task = opts.task;
99
+ if (!task && process.stdin.isTTY) {
100
+ const { default: inquirer } = await import('inquirer');
101
+ const ans = await inquirer.prompt([{
102
+ type: 'input', name: 'task', message: 'Task/prompt:',
103
+ validate: (v) => v.trim().length > 0 || 'Required',
104
+ }]);
105
+ task = ans.task;
106
+ }
107
+ // 4. Confirm
108
+ if (process.stdin.isTTY && !opts.json && !opts.dryRun) {
109
+ const { default: inquirer } = await import('inquirer');
110
+ const { confirm } = await inquirer.prompt([{
111
+ type: 'confirm', name: 'confirm',
112
+ message: `Call ${toNumber} from ${fromNumber}?`,
113
+ default: true,
114
+ }]);
115
+ if (!confirm)
116
+ return;
117
+ }
118
+ // 5. Generate tokens
119
+ const channelName = `call-${Date.now().toString(36)}`;
120
+ const agentUid = 0;
121
+ const sipUid = 1;
122
+ const configObj = (await import('../../config/manager.js')).loadConfig();
123
+ const appCert = process.env.AGORA_APP_CERTIFICATE ?? configObj.app_certificate;
124
+ const agentToken = await generateRtcToken(channelName, agentUid, 86400, config.app_id, appCert);
125
+ const sipToken = await generateRtcToken(channelName, sipUid, 86400, config.app_id, appCert);
126
+ if (!agentToken || !sipToken) {
127
+ printError('Token generation failed. Check app_certificate.');
128
+ process.exit(1);
129
+ }
130
+ // 6. Build LLM config
131
+ const llm = { ...(config.llm ?? {}) };
132
+ if (task) {
133
+ llm.system_messages = [{ role: 'system', content: task }];
134
+ }
135
+ if (opts.greeting) {
136
+ llm.greeting_message = opts.greeting;
137
+ }
138
+ if (opts.model) {
139
+ if (!llm.params)
140
+ llm.params = {};
141
+ llm.params.model = opts.model;
142
+ }
143
+ // 7. Build request
144
+ const request = {
145
+ name: `call-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
146
+ sip: {
147
+ to_number: toNumber,
148
+ from_number: fromNumber,
149
+ rtc_uid: String(sipUid),
150
+ rtc_token: sipToken,
151
+ },
152
+ properties: {
153
+ channel: channelName,
154
+ token: agentToken,
155
+ agent_rtc_uid: String(agentUid),
156
+ remote_rtc_uids: [String(sipUid)],
157
+ idle_timeout: parseInt(opts.maxDuration, 10) * 60 || 600,
158
+ llm,
159
+ tts: config.tts ?? {},
160
+ asr: config.asr ?? {},
161
+ },
162
+ };
163
+ if (opts.dryRun) {
164
+ console.log(JSON.stringify(request, null, 2));
165
+ return;
166
+ }
167
+ // 8. Send call
168
+ const result = await withSpinner('Initiating call...', () => callApi.send(request));
169
+ track('phone_send');
170
+ if (opts.json) {
171
+ console.log(JSON.stringify(result, null, 2));
172
+ return;
173
+ }
174
+ printSuccess(`Call initiated (agent_id: ${result.agent_id})`);
175
+ // 9. --wait mode
176
+ if (opts.wait) {
177
+ const maxMs = (parseInt(opts.maxDuration, 10) || 10) * 60 * 1000;
178
+ const startTime = Date.now();
179
+ const { default: ora } = await import('ora');
180
+ const spinner = ora('Ringing...').start();
181
+ const cleanup = () => {
182
+ spinner.stop();
183
+ console.log('');
184
+ printHint(`Call still active. Run: convoai phone hangup ${result.agent_id}`);
185
+ process.exit(0);
186
+ };
187
+ process.on('SIGINT', cleanup);
188
+ let lastStatus = '';
189
+ while (Date.now() - startTime < maxMs) {
190
+ await new Promise(r => setTimeout(r, 2000));
191
+ try {
192
+ const status = await callApi.status(result.agent_id);
193
+ const elapsed = Math.floor((Date.now() - startTime) / 1000);
194
+ const mm = String(Math.floor(elapsed / 60));
195
+ const ss = String(elapsed % 60).padStart(2, '0');
196
+ if (status.status !== lastStatus) {
197
+ lastStatus = status.status;
198
+ if (status.status === 'RUNNING')
199
+ spinner.text = `In conversation (${mm}:${ss})`;
200
+ else if (status.status === 'STARTING')
201
+ spinner.text = 'Ringing...';
202
+ else if (status.status === 'STOPPED' || status.status === 'FAILED')
203
+ break;
204
+ else
205
+ spinner.text = `${status.status} (${mm}:${ss})`;
206
+ }
207
+ else {
208
+ spinner.text = spinner.text.replace(/\(\d+:\d+\)/, `(${mm}:${ss})`);
209
+ }
210
+ }
211
+ catch { /* ignore poll errors */ }
212
+ }
213
+ process.removeListener('SIGINT', cleanup);
214
+ const totalSec = Math.floor((Date.now() - startTime) / 1000);
215
+ const totalMm = String(Math.floor(totalSec / 60));
216
+ const totalSs = String(totalSec % 60).padStart(2, '0');
217
+ if (lastStatus === 'STOPPED' || lastStatus === 'FAILED') {
218
+ spinner.succeed(`Call ended (duration: ${totalMm}:${totalSs})`);
219
+ }
220
+ else {
221
+ // Wait timed out but call may still be active
222
+ spinner.warn(`Wait timeout (${totalMm}:${totalSs}). Call may still be active.`);
223
+ printHint(`convoai phone status ${result.agent_id}`);
224
+ printHint(`convoai phone hangup ${result.agent_id}`);
225
+ }
226
+ }
227
+ else {
228
+ printHint(`Run: convoai phone status ${result.agent_id}`);
229
+ }
230
+ }
231
+ catch (error) {
232
+ handleError(error, { json: opts.json });
233
+ }
234
+ });
235
+ }
236
+ //# sourceMappingURL=send.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send.js","sourceRoot":"","sources":["../../../../src/commands/phone/send.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACtG,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAGjD,KAAK,UAAU,eAAe,CAAC,WAAoB;IACjD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;IACvD,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;IAEzF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,kDAAkD,CAAC,CAAC,CAAC;IAE9E,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;QAChC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE;QACpF,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,uBAAuB,EAAE,QAAQ,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,gBAAgB,EAAE;QACpJ,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,EAAE;QAC/G,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,EAAE;QACvH,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE;QAC1G,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,0BAA0B,EAAE;QACpE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,0BAA0B,EAAE,IAAI,EAAE,GAAG,EAAE;KACvF,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;IACnC,MAAM,GAAG,CAAC,MAAM,CAAC;QACf,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;QACjC,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,KAAK;QACd,eAAe,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,SAAS,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,SAAS,EAAE;KACtI,CAAC,CAAC;IAEH,YAAY,CAAC,oBAAoB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,KAAK;SACF,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,6BAA6B,CAAC;SAC1C,MAAM,CAAC,iBAAiB,EAAE,mBAAmB,CAAC;SAC9C,MAAM,CAAC,eAAe,EAAE,uBAAuB,CAAC;SAChD,MAAM,CAAC,iBAAiB,EAAE,uBAAuB,CAAC;SAClD,MAAM,CAAC,mBAAmB,EAAE,cAAc,CAAC;SAC3C,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC;SAC/C,MAAM,CAAC,QAAQ,EAAE,sCAAsC,CAAC;SACxD,MAAM,CAAC,uBAAuB,EAAE,4BAA4B,EAAE,IAAI,CAAC;SACnE,MAAM,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;SAC5C,MAAM,CAAC,QAAQ,EAAE,aAAa,CAAC;SAC/B,MAAM,CAAC,WAAW,EAAE,8BAA8B,CAAC;SACnD,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvC,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC7C,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAEzC,2BAA2B;YAC3B,IAAI,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;YAC3B,IAAI,CAAC,UAAU,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBACvC,IAAI,OAAO,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;gBACrC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACzB,MAAM,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBACpC,OAAO,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;gBACnC,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,OAAO,CAAC,CAAC;gBACjD,UAAU,GAAG,MAAM,CAAC,YAAY,CAAC;YACnC,CAAC;YACD,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,UAAU,CAAC,mEAAmE,CAAC,CAAC;gBAChF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YACD,UAAU,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;YAEtC,2CAA2C;YAC3C,IAAI,CAAC;gBACH,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBAClD,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;oBACxB,UAAU,CAAC,GAAG,UAAU,mCAAmC,CAAC,CAAC;oBAC7D,SAAS,CAAC,iDAAiD,CAAC,CAAC;oBAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAClB,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,UAAU,CAAC,UAAU,UAAU,6BAA6B,CAAC,CAAC;gBAC9D,SAAS,CAAC,qCAAqC,CAAC,CAAC;gBACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,yBAAyB;YACzB,IAAI,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBACrC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;gBACvD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;wBACjC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,oBAAoB;wBACxD,QAAQ,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,sBAAsB;qBACtF,CAAC,CAAC,CAAC;gBACJ,QAAQ,GAAG,GAAG,CAAC,EAAE,CAAC;YACpB,CAAC;YACD,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,UAAU,CAAC,mBAAmB,CAAC,CAAC;gBAChC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YACD,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YAElC,yBAAyB;YACzB,IAAI,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YACrB,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBACjC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;gBACvD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;wBACjC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc;wBACpD,QAAQ,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU;qBAC3D,CAAC,CAAC,CAAC;gBACJ,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;YAClB,CAAC;YAED,aAAa;YACb,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACtD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;gBACvD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;wBACzC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS;wBAChC,OAAO,EAAE,QAAQ,QAAQ,SAAS,UAAU,GAAG;wBAC/C,OAAO,EAAE,IAAI;qBACd,CAAC,CAAC,CAAC;gBACJ,IAAI,CAAC,OAAO;oBAAE,OAAO;YACvB,CAAC;YAED,qBAAqB;YACrB,MAAM,WAAW,GAAG,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;YACtD,MAAM,QAAQ,GAAG,CAAC,CAAC;YACnB,MAAM,MAAM,GAAG,CAAC,CAAC;YAEjB,MAAM,SAAS,GAAG,CAAC,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;YACzE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,SAAS,CAAC,eAAe,CAAC;YAE/E,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAChG,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAE5F,IAAI,CAAC,UAAU,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC7B,UAAU,CAAC,iDAAiD,CAAC,CAAC;gBAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,sBAAsB;YACtB,MAAM,GAAG,GAA4B,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,CAAC;YAC/D,IAAI,IAAI,EAAE,CAAC;gBACT,GAAG,CAAC,eAAe,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5D,CAAC;YACD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAClB,GAAG,CAAC,gBAAgB,GAAG,IAAI,CAAC,QAAQ,CAAC;YACvC,CAAC;YACD,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,GAAG,CAAC,MAAM;oBAAE,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC;gBAChC,GAAG,CAAC,MAAkC,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YAC7D,CAAC;YAED,mBAAmB;YACnB,MAAM,OAAO,GAAoB;gBAC/B,IAAI,EAAE,QAAQ,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;gBACpE,GAAG,EAAE;oBACH,SAAS,EAAE,QAAQ;oBACnB,WAAW,EAAE,UAAU;oBACvB,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC;oBACvB,SAAS,EAAE,QAAQ;iBACpB;gBACD,UAAU,EAAE;oBACV,OAAO,EAAE,WAAW;oBACpB,KAAK,EAAE,UAAU;oBACjB,aAAa,EAAE,MAAM,CAAC,QAAQ,CAAC;oBAC/B,eAAe,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;oBACjC,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,GAAG,EAAE,IAAI,GAAG;oBACxD,GAAG;oBACH,GAAG,EAAE,MAAM,CAAC,GAAG,IAAI,EAAE;oBACrB,GAAG,EAAE,MAAM,CAAC,GAAG,IAAI,EAAE;iBACtB;aACF,CAAC;YAEF,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC9C,OAAO;YACT,CAAC;YAED,eAAe;YACf,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;YACpF,KAAK,CAAC,YAAY,CAAC,CAAC;YAEpB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC7C,OAAO;YACT,CAAC;YAED,YAAY,CAAC,6BAA6B,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC;YAE9D,iBAAiB;YACjB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,MAAM,KAAK,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;gBACjE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC7B,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC7C,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,KAAK,EAAE,CAAC;gBAE1C,MAAM,OAAO,GAAG,GAAG,EAAE;oBACnB,OAAO,CAAC,IAAI,EAAE,CAAC;oBACf,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBAChB,SAAS,CAAC,gDAAgD,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;oBAC7E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAClB,CAAC,CAAC;gBACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBAE9B,IAAI,UAAU,GAAG,EAAE,CAAC;gBACpB,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,KAAK,EAAE,CAAC;oBACtC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;oBAC5C,IAAI,CAAC;wBACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;wBACrD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;wBAC5D,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC;wBAC5C,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;wBAEjD,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;4BACjC,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC;4BAC3B,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;gCAAE,OAAO,CAAC,IAAI,GAAG,oBAAoB,EAAE,IAAI,EAAE,GAAG,CAAC;iCAC3E,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU;gCAAE,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC;iCAC9D,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ;gCAAE,MAAM;;gCACrE,OAAO,CAAC,IAAI,GAAG,GAAG,MAAM,CAAC,MAAM,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC;wBACvD,CAAC;6BAAM,CAAC;4BACN,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;wBACtE,CAAC;oBACH,CAAC;oBAAC,MAAM,CAAC,CAAC,wBAAwB,CAAC,CAAC;gBACtC,CAAC;gBAED,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;gBAC7D,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC;gBAClD,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;gBAEvD,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;oBACxD,OAAO,CAAC,OAAO,CAAC,yBAAyB,OAAO,IAAI,OAAO,GAAG,CAAC,CAAC;gBAClE,CAAC;qBAAM,CAAC;oBACN,8CAA8C;oBAC9C,OAAO,CAAC,IAAI,CAAC,iBAAiB,OAAO,IAAI,OAAO,8BAA8B,CAAC,CAAC;oBAChF,SAAS,CAAC,wBAAwB,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;oBACrD,SAAS,CAAC,wBAAwB,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACvD,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,SAAS,CAAC,6BAA6B,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerPhoneStatus(phone: Command): void;
@@ -0,0 +1,34 @@
1
+ import { getCallAPI } from './_helpers.js';
2
+ import { printKeyValue } from '../../ui/table.js';
3
+ import { handleError } from '../../utils/errors.js';
4
+ export function registerPhoneStatus(phone) {
5
+ phone
6
+ .command('status <agent-id>')
7
+ .description('Check call status')
8
+ .option('--profile <name>', 'Config profile')
9
+ .option('--json', 'JSON output')
10
+ .action(async (agentId, opts) => {
11
+ try {
12
+ const api = getCallAPI(opts.profile);
13
+ const status = await api.status(agentId);
14
+ if (opts.json) {
15
+ console.log(JSON.stringify(status, null, 2));
16
+ return;
17
+ }
18
+ const duration = status.stop_ts
19
+ ? `${Math.floor((status.stop_ts - status.start_ts) / 60)}:${String((status.stop_ts - status.start_ts) % 60).padStart(2, '0')}`
20
+ : `${Math.floor((Math.floor(Date.now() / 1000) - status.start_ts) / 60)}:${String((Math.floor(Date.now() / 1000) - status.start_ts) % 60).padStart(2, '0')}`;
21
+ console.log('');
22
+ printKeyValue([
23
+ ['Agent ID', status.agent_id],
24
+ ['Status', status.status],
25
+ ['Duration', duration],
26
+ ['Channel', status.channel ?? '-'],
27
+ ]);
28
+ }
29
+ catch (error) {
30
+ handleError(error, { json: opts.json });
31
+ }
32
+ });
33
+ }
34
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../../src/commands/phone/status.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,KAAK;SACF,OAAO,CAAC,mBAAmB,CAAC;SAC5B,WAAW,CAAC,mBAAmB,CAAC;SAChC,MAAM,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;SAC5C,MAAM,CAAC,QAAQ,EAAE,aAAa,CAAC;SAC/B,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QAC9B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACrC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAEzC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC7C,OAAO;YACT,CAAC;YAED,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO;gBAC7B,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;gBAC9H,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;YAE/J,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,aAAa,CAAC;gBACZ,CAAC,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC;gBAC7B,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC;gBACzB,CAAC,UAAU,EAAE,QAAQ,CAAC;gBACtB,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,IAAI,GAAG,CAAC;aACnC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerPhoneUpdate(phone: Command): void;
@@ -0,0 +1,41 @@
1
+ import { getNumberAPI } from './_helpers.js';
2
+ import { printSuccess } from '../../ui/output.js';
3
+ import { handleError } from '../../utils/errors.js';
4
+ export function registerPhoneUpdate(phone) {
5
+ phone
6
+ .command('update <phone-number>')
7
+ .description('Update phone number configuration')
8
+ .option('--label <label>', 'New label')
9
+ .option('--sip-address <address>', 'New SIP address')
10
+ .option('--sip-transport <transport>', 'New transport (tls/tcp/udp)')
11
+ .option('--sip-user <user>', 'New SIP username')
12
+ .option('--sip-password <password>', 'New SIP password')
13
+ .option('--profile <name>', 'Config profile')
14
+ .option('--json', 'JSON output')
15
+ .action(async (phoneNumber, opts) => {
16
+ try {
17
+ const api = getNumberAPI(opts.profile);
18
+ const req = {};
19
+ if (opts.label)
20
+ req.label = opts.label;
21
+ if (opts.sipAddress || opts.sipTransport || opts.sipUser || opts.sipPassword) {
22
+ req.outbound_config = {
23
+ ...(opts.sipAddress && { address: opts.sipAddress }),
24
+ ...(opts.sipTransport && { transport: opts.sipTransport }),
25
+ ...(opts.sipUser && { user: opts.sipUser }),
26
+ ...(opts.sipPassword && { password: opts.sipPassword }),
27
+ };
28
+ }
29
+ const result = await api.update(phoneNumber, req);
30
+ if (opts.json) {
31
+ console.log(JSON.stringify(result, null, 2));
32
+ return;
33
+ }
34
+ printSuccess(`Updated: ${result.phone_number}`);
35
+ }
36
+ catch (error) {
37
+ handleError(error, { json: opts.json });
38
+ }
39
+ });
40
+ }
41
+ //# sourceMappingURL=update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.js","sourceRoot":"","sources":["../../../../src/commands/phone/update.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,KAAK;SACF,OAAO,CAAC,uBAAuB,CAAC;SAChC,WAAW,CAAC,mCAAmC,CAAC;SAChD,MAAM,CAAC,iBAAiB,EAAE,WAAW,CAAC;SACtC,MAAM,CAAC,yBAAyB,EAAE,iBAAiB,CAAC;SACpD,MAAM,CAAC,6BAA6B,EAAE,6BAA6B,CAAC;SACpE,MAAM,CAAC,mBAAmB,EAAE,kBAAkB,CAAC;SAC/C,MAAM,CAAC,2BAA2B,EAAE,kBAAkB,CAAC;SACvD,MAAM,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;SAC5C,MAAM,CAAC,QAAQ,EAAE,aAAa,CAAC;SAC/B,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE;QAClC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvC,MAAM,GAAG,GAA4B,EAAE,CAAC;YAExC,IAAI,IAAI,CAAC,KAAK;gBAAE,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YAEvC,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBAC7E,GAAG,CAAC,eAAe,GAAG;oBACpB,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;oBACpD,GAAG,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC;oBAC1D,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;oBAC3C,GAAG,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC;iBACxD,CAAC;YACJ,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;YAElD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC7C,OAAO;YACT,CAAC;YAED,YAAY,CAAC,YAAY,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -674,7 +674,7 @@ async function quickstartAction() {
674
674
  // ═══════════════════════════════════════════════════════════════════════
675
675
  console.log('');
676
676
  showStep(str.step5, 5, TOTAL_STEPS);
677
- // Detect OpenClaw and offer voice integration
677
+ // Detect OpenClaw
678
678
  let hasOpenClaw = false;
679
679
  try {
680
680
  const { execSync: exec } = await import('node:child_process');
@@ -682,28 +682,94 @@ async function quickstartAction() {
682
682
  hasOpenClaw = true;
683
683
  }
684
684
  catch { /* not installed */ }
685
+ // Always offer mode choice
686
+ const modeChoices = [
687
+ { name: lang === 'cn' ? '🎙 语音对话 (浏览器)' : '🎙 Voice chat (browser)', value: 'voice' },
688
+ { name: lang === 'cn' ? '📞 打电话' : '📞 Make a phone call', value: 'phone' },
689
+ ];
685
690
  if (hasOpenClaw) {
686
- const { mode } = await inquirer.prompt([
687
- {
688
- type: 'list',
689
- name: 'mode',
690
- message: str.launchMode + ':',
691
- choices: [
692
- { name: str.launchConvoai, value: 'convoai' },
693
- { name: str.launchOpenclaw, value: 'openclaw' },
694
- ],
695
- },
696
- ]);
697
- if (mode === 'openclaw') {
698
- printSuccess('Launching OpenClaw voice mode...');
699
- track('qs_openclaw');
700
- const { execSync: exec } = await import('node:child_process');
701
- // Hand off to convoai openclaw — exec replaces this process
702
- const convoaiBin = process.argv[1];
703
- exec(`node ${convoaiBin} openclaw`, { stdio: 'inherit' });
691
+ modeChoices.push({ name: '🦞 OpenClaw voice mode', value: 'openclaw' });
692
+ }
693
+ const { mode } = await inquirer.prompt([{
694
+ type: 'list', name: 'mode',
695
+ message: lang === 'cn' ? '选择体验方式:' : 'How to experience?',
696
+ choices: modeChoices,
697
+ }]);
698
+ if (mode === 'openclaw') {
699
+ printSuccess('Launching OpenClaw voice mode...');
700
+ track('qs_openclaw');
701
+ const { execSync: exec } = await import('node:child_process');
702
+ const convoaiBin = process.argv[1];
703
+ exec(`node ${convoaiBin} openclaw`, { stdio: 'inherit' });
704
+ process.exit(0);
705
+ }
706
+ if (mode === 'phone') {
707
+ // Phone call flow
708
+ try {
709
+ const { getCallAPI, getNumberAPI, getConfig, validateE164, pickOutboundNumber } = await import('./phone/_helpers.js');
710
+ const { generateRtcToken } = await import('../utils/token.js');
711
+ const numberApi = getNumberAPI();
712
+ const callApi = getCallAPI();
713
+ let numbers = await numberApi.list();
714
+ if (numbers.length === 0) {
715
+ printHint(lang === 'cn' ? '没有电话号码,请先导入' : 'No phone numbers. Import one first.');
716
+ printHint('convoai phone import');
717
+ process.exit(0);
718
+ }
719
+ const picked = await pickOutboundNumber(numbers);
720
+ const { to } = await inquirer.prompt([{
721
+ type: 'input', name: 'to',
722
+ message: lang === 'cn' ? '拨打号码 (E.164):' : 'To number (E.164):',
723
+ validate: (v) => /^\+[1-9]\d{1,14}$/.test(v.trim()) || 'Invalid E.164',
724
+ }]);
725
+ const channelName = `qs-call-${Date.now().toString(36)}`;
726
+ const appCert = process.env.AGORA_APP_CERTIFICATE ?? config.app_certificate;
727
+ const agentToken = await generateRtcToken(channelName, 0, 86400, config.app_id, appCert);
728
+ const sipToken = await generateRtcToken(channelName, 1, 86400, config.app_id, appCert);
729
+ if (!agentToken || !sipToken) {
730
+ printError(lang === 'cn' ? 'Token 生成失败' : 'Token generation failed');
731
+ process.exit(1);
732
+ }
733
+ const llm = { ...(profile.llm ?? {}) };
734
+ const request = {
735
+ name: `qs-call-${Date.now()}`,
736
+ sip: { to_number: validateE164(to), from_number: picked.phone_number, rtc_uid: '1', rtc_token: sipToken },
737
+ properties: {
738
+ channel: channelName, token: agentToken, agent_rtc_uid: '0', remote_rtc_uids: ['1'],
739
+ idle_timeout: 600, llm, tts: profile.tts ?? {}, asr: profile.asr ?? {},
740
+ },
741
+ };
742
+ const result = await withSpinner(lang === 'cn' ? '正在拨号...' : 'Calling...', () => callApi.send(request));
743
+ printSuccess(`${lang === 'cn' ? '呼叫已发起' : 'Call initiated'} (${result.agent_id})`);
744
+ track('qs_step5_phone');
745
+ // Wait for call to finish
746
+ const { default: ora } = await import('ora');
747
+ const spinner = ora(lang === 'cn' ? '通话中...' : 'In call...').start();
748
+ const startTime = Date.now();
749
+ const maxMs = 10 * 60 * 1000;
750
+ while (Date.now() - startTime < maxMs) {
751
+ await new Promise(r => setTimeout(r, 2000));
752
+ try {
753
+ const status = await callApi.status(result.agent_id);
754
+ const elapsed = Math.floor((Date.now() - startTime) / 1000);
755
+ const mm = String(Math.floor(elapsed / 60));
756
+ const ss = String(elapsed % 60).padStart(2, '0');
757
+ spinner.text = `${lang === 'cn' ? '通话中' : 'In call'} (${mm}:${ss})`;
758
+ if (status.status === 'STOPPED' || status.status === 'FAILED')
759
+ break;
760
+ }
761
+ catch { /* ignore */ }
762
+ }
763
+ const totalSec = Math.floor((Date.now() - startTime) / 1000);
764
+ spinner.succeed(`${lang === 'cn' ? '通话结束' : 'Call ended'} (${Math.floor(totalSec / 60)}:${String(totalSec % 60).padStart(2, '0')})`);
704
765
  process.exit(0);
705
766
  }
767
+ catch (err) {
768
+ handleError(err);
769
+ process.exit(1);
770
+ }
706
771
  }
772
+ // mode === 'voice': continue with existing voice chat flow below
707
773
  const client = createClient({
708
774
  appId: config.app_id,
709
775
  customerId: config.customer_id,