@thisispamela/cli 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +140 -0
- package/package.json +33 -0
- package/src/index.ts +156 -0
- package/tsconfig.json +17 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const sdk_1 = require("@thisispamela/sdk");
|
|
6
|
+
const outputJson = (data) => {
|
|
7
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
|
|
8
|
+
};
|
|
9
|
+
const outputError = (error) => {
|
|
10
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11
|
+
const name = error instanceof Error ? error.name : 'Error';
|
|
12
|
+
process.stderr.write(`${JSON.stringify({ error: { name, message } }, null, 2)}\n`);
|
|
13
|
+
};
|
|
14
|
+
const parseNumber = (value, label) => {
|
|
15
|
+
const parsed = Number(value);
|
|
16
|
+
if (!Number.isFinite(parsed)) {
|
|
17
|
+
throw new Error(`${label} must be a valid number`);
|
|
18
|
+
}
|
|
19
|
+
return parsed;
|
|
20
|
+
};
|
|
21
|
+
const getClient = (options) => {
|
|
22
|
+
const apiKey = options.apiKey || process.env.PAMELA_API_KEY;
|
|
23
|
+
if (!apiKey) {
|
|
24
|
+
throw new Error('Missing API key. Provide --api-key or set PAMELA_API_KEY.');
|
|
25
|
+
}
|
|
26
|
+
return new sdk_1.PamelaClient({
|
|
27
|
+
apiKey,
|
|
28
|
+
baseUrl: options.baseUrl,
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
const program = new commander_1.Command();
|
|
32
|
+
program
|
|
33
|
+
.name('thisispamela')
|
|
34
|
+
.description('Pamela Voice API CLI')
|
|
35
|
+
.option('--api-key <key>', 'Pamela API key (falls back to PAMELA_API_KEY)')
|
|
36
|
+
.option('--base-url <url>', 'Pamela API base URL', 'https://api.thisispamela.com');
|
|
37
|
+
program
|
|
38
|
+
.command('create-call')
|
|
39
|
+
.description('Create a new call')
|
|
40
|
+
.requiredOption('--to <e164>', 'Destination phone number (E.164)')
|
|
41
|
+
.requiredOption('--task <text>', 'Task for the agent to perform')
|
|
42
|
+
.option('--voice <voice>', 'Voice selection (male, female, auto)')
|
|
43
|
+
.option('--agent-name <name>', 'Agent display name')
|
|
44
|
+
.option('--locale <locale>', 'Locale, e.g. en-US')
|
|
45
|
+
.option('--max-duration-seconds <number>', 'Maximum call duration in seconds', (value) => parseNumber(value, 'max-duration-seconds'))
|
|
46
|
+
.option('--caller-name <name>', 'Caller display name')
|
|
47
|
+
.action(async (options) => {
|
|
48
|
+
try {
|
|
49
|
+
const client = getClient(program.opts());
|
|
50
|
+
const payload = {
|
|
51
|
+
to: options.to,
|
|
52
|
+
task: options.task,
|
|
53
|
+
};
|
|
54
|
+
if (options.voice)
|
|
55
|
+
payload.voice = options.voice;
|
|
56
|
+
if (options.agentName)
|
|
57
|
+
payload.agent_name = options.agentName;
|
|
58
|
+
if (options.locale)
|
|
59
|
+
payload.locale = options.locale;
|
|
60
|
+
if (options.maxDurationSeconds !== undefined) {
|
|
61
|
+
payload.max_duration_seconds = options.maxDurationSeconds;
|
|
62
|
+
}
|
|
63
|
+
if (options.callerName)
|
|
64
|
+
payload.caller_name = options.callerName;
|
|
65
|
+
const response = await client.createCall(payload);
|
|
66
|
+
outputJson(response);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
outputError(error);
|
|
70
|
+
process.exitCode = 1;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
program
|
|
74
|
+
.command('get-call')
|
|
75
|
+
.description('Get call status and details')
|
|
76
|
+
.argument('<callId>', 'Call ID')
|
|
77
|
+
.action(async (callId) => {
|
|
78
|
+
try {
|
|
79
|
+
const client = getClient(program.opts());
|
|
80
|
+
const response = await client.getCall(callId);
|
|
81
|
+
outputJson(response);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
outputError(error);
|
|
85
|
+
process.exitCode = 1;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
program
|
|
89
|
+
.command('list-calls')
|
|
90
|
+
.description('List calls')
|
|
91
|
+
.option('--status <status>', 'Filter by status')
|
|
92
|
+
.option('--limit <number>', 'Limit number of results', (value) => parseNumber(value, 'limit'))
|
|
93
|
+
.action(async (options) => {
|
|
94
|
+
try {
|
|
95
|
+
const client = getClient(program.opts());
|
|
96
|
+
const response = await client.listCalls({
|
|
97
|
+
status: options.status,
|
|
98
|
+
limit: options.limit,
|
|
99
|
+
});
|
|
100
|
+
outputJson(response);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
outputError(error);
|
|
104
|
+
process.exitCode = 1;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
program
|
|
108
|
+
.command('cancel-call')
|
|
109
|
+
.description('Cancel an in-progress call')
|
|
110
|
+
.argument('<callId>', 'Call ID')
|
|
111
|
+
.action(async (callId) => {
|
|
112
|
+
try {
|
|
113
|
+
const client = getClient(program.opts());
|
|
114
|
+
const response = await client.cancelCall(callId);
|
|
115
|
+
outputJson(response);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
outputError(error);
|
|
119
|
+
process.exitCode = 1;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
program
|
|
123
|
+
.command('hangup-call')
|
|
124
|
+
.description('Force hangup an in-progress call')
|
|
125
|
+
.argument('<callId>', 'Call ID')
|
|
126
|
+
.action(async (callId) => {
|
|
127
|
+
try {
|
|
128
|
+
const client = getClient(program.opts());
|
|
129
|
+
const response = await client.hangupCall(callId);
|
|
130
|
+
outputJson(response);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
outputError(error);
|
|
134
|
+
process.exitCode = 1;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
program.parseAsync(process.argv).catch((error) => {
|
|
138
|
+
outputError(error);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@thisispamela/cli",
|
|
3
|
+
"version": "1.0.4",
|
|
4
|
+
"description": "Pamela Enterprise Voice API CLI",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"thisispamela": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"pamela",
|
|
16
|
+
"voice",
|
|
17
|
+
"api",
|
|
18
|
+
"enterprise",
|
|
19
|
+
"phone",
|
|
20
|
+
"calling",
|
|
21
|
+
"cli"
|
|
22
|
+
],
|
|
23
|
+
"author": "Pamela",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@thisispamela/sdk": "^1.0.4",
|
|
27
|
+
"commander": "^14.0.3"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^20.0.0",
|
|
31
|
+
"typescript": "^5.0.0"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { PamelaClient } from '@thisispamela/sdk';
|
|
4
|
+
|
|
5
|
+
type GlobalOptions = {
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const outputJson = (data: unknown) => {
|
|
11
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const outputError = (error: unknown) => {
|
|
15
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
16
|
+
const name = error instanceof Error ? error.name : 'Error';
|
|
17
|
+
process.stderr.write(
|
|
18
|
+
`${JSON.stringify({ error: { name, message } }, null, 2)}\n`
|
|
19
|
+
);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const parseNumber = (value: string, label: string) => {
|
|
23
|
+
const parsed = Number(value);
|
|
24
|
+
if (!Number.isFinite(parsed)) {
|
|
25
|
+
throw new Error(`${label} must be a valid number`);
|
|
26
|
+
}
|
|
27
|
+
return parsed;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const getClient = (options: GlobalOptions) => {
|
|
31
|
+
const apiKey = options.apiKey || process.env.PAMELA_API_KEY;
|
|
32
|
+
if (!apiKey) {
|
|
33
|
+
throw new Error('Missing API key. Provide --api-key or set PAMELA_API_KEY.');
|
|
34
|
+
}
|
|
35
|
+
return new PamelaClient({
|
|
36
|
+
apiKey,
|
|
37
|
+
baseUrl: options.baseUrl,
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const program = new Command();
|
|
42
|
+
|
|
43
|
+
program
|
|
44
|
+
.name('thisispamela')
|
|
45
|
+
.description('Pamela Voice API CLI')
|
|
46
|
+
.option('--api-key <key>', 'Pamela API key (falls back to PAMELA_API_KEY)')
|
|
47
|
+
.option('--base-url <url>', 'Pamela API base URL', 'https://api.thisispamela.com');
|
|
48
|
+
|
|
49
|
+
program
|
|
50
|
+
.command('create-call')
|
|
51
|
+
.description('Create a new call')
|
|
52
|
+
.requiredOption('--to <e164>', 'Destination phone number (E.164)')
|
|
53
|
+
.requiredOption('--task <text>', 'Task for the agent to perform')
|
|
54
|
+
.option('--voice <voice>', 'Voice selection (male, female, auto)')
|
|
55
|
+
.option('--agent-name <name>', 'Agent display name')
|
|
56
|
+
.option('--locale <locale>', 'Locale, e.g. en-US')
|
|
57
|
+
.option(
|
|
58
|
+
'--max-duration-seconds <number>',
|
|
59
|
+
'Maximum call duration in seconds',
|
|
60
|
+
(value) => parseNumber(value, 'max-duration-seconds')
|
|
61
|
+
)
|
|
62
|
+
.option('--caller-name <name>', 'Caller display name')
|
|
63
|
+
.action(async (options) => {
|
|
64
|
+
try {
|
|
65
|
+
const client = getClient(program.opts<GlobalOptions>());
|
|
66
|
+
const payload: Record<string, unknown> = {
|
|
67
|
+
to: options.to,
|
|
68
|
+
task: options.task,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
if (options.voice) payload.voice = options.voice;
|
|
72
|
+
if (options.agentName) payload.agent_name = options.agentName;
|
|
73
|
+
if (options.locale) payload.locale = options.locale;
|
|
74
|
+
if (options.maxDurationSeconds !== undefined) {
|
|
75
|
+
payload.max_duration_seconds = options.maxDurationSeconds;
|
|
76
|
+
}
|
|
77
|
+
if (options.callerName) payload.caller_name = options.callerName;
|
|
78
|
+
|
|
79
|
+
const response = await client.createCall(payload as any);
|
|
80
|
+
outputJson(response);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
outputError(error);
|
|
83
|
+
process.exitCode = 1;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
program
|
|
88
|
+
.command('get-call')
|
|
89
|
+
.description('Get call status and details')
|
|
90
|
+
.argument('<callId>', 'Call ID')
|
|
91
|
+
.action(async (callId: string) => {
|
|
92
|
+
try {
|
|
93
|
+
const client = getClient(program.opts<GlobalOptions>());
|
|
94
|
+
const response = await client.getCall(callId);
|
|
95
|
+
outputJson(response);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
outputError(error);
|
|
98
|
+
process.exitCode = 1;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
program
|
|
103
|
+
.command('list-calls')
|
|
104
|
+
.description('List calls')
|
|
105
|
+
.option('--status <status>', 'Filter by status')
|
|
106
|
+
.option('--limit <number>', 'Limit number of results', (value) =>
|
|
107
|
+
parseNumber(value, 'limit')
|
|
108
|
+
)
|
|
109
|
+
.action(async (options) => {
|
|
110
|
+
try {
|
|
111
|
+
const client = getClient(program.opts<GlobalOptions>());
|
|
112
|
+
const response = await client.listCalls({
|
|
113
|
+
status: options.status,
|
|
114
|
+
limit: options.limit,
|
|
115
|
+
});
|
|
116
|
+
outputJson(response);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
outputError(error);
|
|
119
|
+
process.exitCode = 1;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
program
|
|
124
|
+
.command('cancel-call')
|
|
125
|
+
.description('Cancel an in-progress call')
|
|
126
|
+
.argument('<callId>', 'Call ID')
|
|
127
|
+
.action(async (callId: string) => {
|
|
128
|
+
try {
|
|
129
|
+
const client = getClient(program.opts<GlobalOptions>());
|
|
130
|
+
const response = await client.cancelCall(callId);
|
|
131
|
+
outputJson(response);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
outputError(error);
|
|
134
|
+
process.exitCode = 1;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
program
|
|
139
|
+
.command('hangup-call')
|
|
140
|
+
.description('Force hangup an in-progress call')
|
|
141
|
+
.argument('<callId>', 'Call ID')
|
|
142
|
+
.action(async (callId: string) => {
|
|
143
|
+
try {
|
|
144
|
+
const client = getClient(program.opts<GlobalOptions>());
|
|
145
|
+
const response = await client.hangupCall(callId);
|
|
146
|
+
outputJson(response);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
outputError(error);
|
|
149
|
+
process.exitCode = 1;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
program.parseAsync(process.argv).catch((error) => {
|
|
154
|
+
outputError(error);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"resolveJsonModule": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*"],
|
|
16
|
+
"exclude": ["node_modules", "dist"]
|
|
17
|
+
}
|