@retell-ai/retell-cli 0.7.0 → 0.9.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 +4 -2
- package/dist/src/generated/operations.js +2 -2
- package/dist/src/index.js +7 -1
- package/dist/src/runtime/auth.js +50 -14
- package/dist/src/runtime/identity.js +38 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,9 @@ retell auth whoami
|
|
|
40
40
|
retell auth logout
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
`retell auth login` reports any current credential by source and
|
|
43
|
+
`retell auth login` reports any current credential by source, and its org and API key name if it's still valid. Press Enter to keep an existing keychain/config credential, or enter a new key to replace it. A new key is validated against the Retell API before it's stored — invalid keys are rejected and nothing is written to disk. If `RETELL_API_KEY` is set, it remains authoritative; run `unset RETELL_API_KEY` in the current shell before logging in with a different active key.
|
|
44
|
+
|
|
45
|
+
`retell auth whoami` shows the org, API key name, and credential source, without printing the raw key value.
|
|
44
46
|
|
|
45
47
|
`retell auth logout` clears keychain/config credentials and verifies that they are gone. It cannot unset `RETELL_API_KEY` in the parent shell, so it prints a warning when the environment variable keeps authentication active.
|
|
46
48
|
|
|
@@ -65,4 +67,4 @@ For password, token, or API-key request fields, prefer `--body -` or stdin so th
|
|
|
65
67
|
|
|
66
68
|
Use `--dry-run` to inspect a redacted request without sending it.
|
|
67
69
|
|
|
68
|
-
`--base-url` is intended for trusted development servers and proxies. The CLI sends the configured Retell bearer credential to the overridden URL, matching the Retell SDK behavior. API requests time out after 60 seconds, except multipart/file-upload requests, which time out after 300 seconds.
|
|
70
|
+
`--base-url` is intended for trusted development servers and proxies. The CLI sends the configured Retell bearer credential to the overridden URL, matching the Retell SDK behavior. API requests time out after 60 seconds, except multipart/file-upload requests, which time out after 300 seconds.
|
|
@@ -3,7 +3,7 @@ export const catalogMetadata = {
|
|
|
3
3
|
"title": "Retell SDK",
|
|
4
4
|
"apiVersion": "3.0.0",
|
|
5
5
|
"server": "https://api.retellai.com",
|
|
6
|
-
"sourceSha256": "
|
|
6
|
+
"sourceSha256": "bf6aa757ac4071a5740c53352c070217f94e2fd1d4631e529c53dabc3461b237",
|
|
7
7
|
"operationCount": 81
|
|
8
8
|
};
|
|
9
9
|
export const schemaDefinitions = {
|
|
@@ -9437,7 +9437,7 @@ export const operations = [
|
|
|
9437
9437
|
"deprecated": false,
|
|
9438
9438
|
"authenticated": true,
|
|
9439
9439
|
"parameters": [],
|
|
9440
|
-
"responseContractHash": "
|
|
9440
|
+
"responseContractHash": "83a410882e0a73c1d43433f23611e69ad2d9b5ceb5ab1b370c5799d7f2c0c14a"
|
|
9441
9441
|
},
|
|
9442
9442
|
{
|
|
9443
9443
|
"operationId": "getBatchTest",
|
package/dist/src/index.js
CHANGED
|
@@ -8,7 +8,10 @@ import { sendRequest } from "./runtime/http.js";
|
|
|
8
8
|
import { renderOutput, resolveOutputFormat } from "./runtime/output.js";
|
|
9
9
|
import { dryRunOutput, prepareRequest } from "./runtime/request.js";
|
|
10
10
|
import { cliVersion } from "./runtime/version.js";
|
|
11
|
-
|
|
11
|
+
// getApiKeyInfo backs `auth login`/`auth whoami` internally; hidden from direct
|
|
12
|
+
// use so it isn't a general-purpose way to dump org_id/api_key_name.
|
|
13
|
+
const HIDDEN_OPERATION_IDS = new Set(["getApiKeyInfo"]);
|
|
14
|
+
const operations = generatedOperations.filter((operation) => !HIDDEN_OPERATION_IDS.has(operation.operationId));
|
|
12
15
|
async function main() {
|
|
13
16
|
const [first, ...rest] = process.argv.slice(2);
|
|
14
17
|
if (!first || first === "--help" || first === "-h" || first === "help" && !rest[0]) {
|
|
@@ -24,6 +27,9 @@ async function main() {
|
|
|
24
27
|
return;
|
|
25
28
|
}
|
|
26
29
|
const commandName = first === "help" ? rest.shift() : first;
|
|
30
|
+
if (commandName === "get-api-key-info") {
|
|
31
|
+
throw new Error("get-api-key-info isn't a standalone command. Use 'retell auth login' or 'retell auth whoami' instead.");
|
|
32
|
+
}
|
|
27
33
|
const operation = operations.find((candidate) => candidate.command === commandName);
|
|
28
34
|
if (!operation)
|
|
29
35
|
throw new Error(`Unknown command: ${commandName}. Run 'retell --help' to list commands.`);
|
package/dist/src/runtime/auth.js
CHANGED
|
@@ -1,16 +1,48 @@
|
|
|
1
|
-
import { clearApiKey,
|
|
1
|
+
import { clearApiKey, resolveApiKey, storeApiKey } from "./credentials.js";
|
|
2
|
+
import { catalogMetadata } from "../generated/operations.js";
|
|
3
|
+
import { fetchApiKeyIdentity } from "./identity.js";
|
|
4
|
+
const defaultBaseUrl = catalogMetadata.server;
|
|
2
5
|
function authHelp() {
|
|
3
6
|
return `Manage Retell API authentication.\n\n` +
|
|
4
7
|
`Usage:\n` +
|
|
5
|
-
` retell auth login [--with-token]\n` +
|
|
6
|
-
` retell auth whoami\n` +
|
|
8
|
+
` retell auth login [--with-token] [--base-url <URL>]\n` +
|
|
9
|
+
` retell auth whoami [--base-url <URL>]\n` +
|
|
7
10
|
` retell auth logout\n\n` +
|
|
8
11
|
`Commands:\n` +
|
|
9
|
-
` login
|
|
10
|
-
` whoami Show
|
|
12
|
+
` login Validate and securely store a Retell API key\n` +
|
|
13
|
+
` whoami Show authentication status and the org/key it belongs to\n` +
|
|
11
14
|
` logout Delete the stored Retell API key\n\n` +
|
|
12
15
|
`RETELL_API_KEY overrides any stored credential.\n`;
|
|
13
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Prints org/key identity first (or a soft warning if the live check fails),
|
|
19
|
+
* then a blank line, then the given status line and credential source.
|
|
20
|
+
* Shared by login's "Already authenticated" message and `whoami`.
|
|
21
|
+
*/
|
|
22
|
+
async function printCredentialStatus(credential, baseUrl, statusLine) {
|
|
23
|
+
if (credential.value) {
|
|
24
|
+
try {
|
|
25
|
+
const identity = await fetchApiKeyIdentity(credential.value, baseUrl);
|
|
26
|
+
process.stdout.write(`Org: ${identity.orgName}\n`);
|
|
27
|
+
if (identity.apiKeyName)
|
|
28
|
+
process.stdout.write(`API key name: ${identity.apiKeyName}\n`);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
process.stdout.write(`Could not verify this key with the Retell API: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
process.stdout.write(`${statusLine}\nSource: ${credential.source}\n`);
|
|
35
|
+
}
|
|
36
|
+
/** Extracts `--base-url <URL>` from an auth subcommand's options, returning the remaining options. */
|
|
37
|
+
function takeBaseUrl(options) {
|
|
38
|
+
const index = options.indexOf("--base-url");
|
|
39
|
+
if (index === -1)
|
|
40
|
+
return { rest: options };
|
|
41
|
+
const value = options[index + 1];
|
|
42
|
+
if (value === undefined)
|
|
43
|
+
throw new Error("--base-url requires a value");
|
|
44
|
+
return { baseUrl: value, rest: [...options.slice(0, index), ...options.slice(index + 2)] };
|
|
45
|
+
}
|
|
14
46
|
async function readAllStdin() {
|
|
15
47
|
const chunks = [];
|
|
16
48
|
for await (const chunk of process.stdin)
|
|
@@ -61,42 +93,46 @@ export async function handleAuth(tokens) {
|
|
|
61
93
|
return;
|
|
62
94
|
}
|
|
63
95
|
if (command === "login") {
|
|
64
|
-
|
|
65
|
-
|
|
96
|
+
const { baseUrl, rest: loginOptions } = takeBaseUrl(options);
|
|
97
|
+
if (loginOptions.some((option) => option !== "--with-token" && option !== "--help" && option !== "-h")) {
|
|
98
|
+
throw new Error(`Unknown auth login option: ${loginOptions.find((option) => !["--with-token", "--help", "-h"].includes(option))}`);
|
|
66
99
|
}
|
|
67
|
-
if (
|
|
100
|
+
if (loginOptions.includes("--help") || loginOptions.includes("-h")) {
|
|
68
101
|
process.stdout.write(`Store a Retell API key.\n\nUsage:\n retell auth login\n printf '%s' "$RETELL_API_KEY" | retell auth login --with-token\n`);
|
|
69
102
|
return;
|
|
70
103
|
}
|
|
71
104
|
const current = await resolveApiKey();
|
|
72
105
|
if (current.source !== "unset") {
|
|
73
|
-
|
|
106
|
+
await printCredentialStatus(current, baseUrl ?? defaultBaseUrl, "Already authenticated.");
|
|
74
107
|
}
|
|
75
108
|
if (current.source === "env") {
|
|
76
109
|
const guidance = "RETELL_API_KEY overrides stored credentials. Run 'unset RETELL_API_KEY' in this shell before logging in with a different key.";
|
|
77
|
-
if (
|
|
110
|
+
if (loginOptions.includes("--with-token"))
|
|
78
111
|
throw new Error(guidance);
|
|
79
112
|
process.stdout.write(`${guidance}\n`);
|
|
80
113
|
return;
|
|
81
114
|
}
|
|
82
|
-
const token =
|
|
115
|
+
const token = loginOptions.includes("--with-token")
|
|
83
116
|
? await readAllStdin()
|
|
84
117
|
: await promptHidden(current.source === "unset" ? "Retell API key: " : "New Retell API key (press Enter to keep current): ");
|
|
85
118
|
if (!token.trim() && current.source !== "unset") {
|
|
86
119
|
process.stdout.write("Existing Retell API key kept unchanged.\n");
|
|
87
120
|
return;
|
|
88
121
|
}
|
|
122
|
+
const identity = await fetchApiKeyIdentity(token.trim(), baseUrl ?? defaultBaseUrl);
|
|
89
123
|
const source = await storeApiKey(token);
|
|
90
124
|
process.stdout.write(source === "keyring"
|
|
91
125
|
? "Retell API key stored in the OS keychain.\n"
|
|
92
126
|
: "OS keychain unavailable; Retell API key stored in ~/.config/retell/credentials.json with mode 0600.\n");
|
|
127
|
+
process.stdout.write(`Logged in to ${identity.orgName}${identity.apiKeyName ? ` as key "${identity.apiKeyName}"` : ""}.\n`);
|
|
93
128
|
return;
|
|
94
129
|
}
|
|
95
130
|
if (command === "whoami") {
|
|
131
|
+
const { baseUrl, rest: whoamiOptions } = takeBaseUrl(options);
|
|
132
|
+
if (whoamiOptions.length)
|
|
133
|
+
throw new Error(`Unknown auth whoami option: ${whoamiOptions[0]}`);
|
|
96
134
|
const credential = await resolveApiKey();
|
|
97
|
-
|
|
98
|
-
process.stdout.write(`Source: ${credential.source}\n`);
|
|
99
|
-
process.stdout.write(`API key: ${maskApiKey(credential.value)}\n`);
|
|
135
|
+
await printCredentialStatus(credential, baseUrl ?? defaultBaseUrl, `Authentication: ${credential.source === "unset" ? "not configured" : "configured"}`);
|
|
100
136
|
return;
|
|
101
137
|
}
|
|
102
138
|
if (command === "logout") {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { operations } from "../generated/operations.js";
|
|
2
|
+
import { sendRequest } from "./http.js";
|
|
3
|
+
import { prepareRequest } from "./request.js";
|
|
4
|
+
const getApiKeyInfoOperation = operations.find((operation) => operation.operationId === "getApiKeyInfo");
|
|
5
|
+
function emptyArgs() {
|
|
6
|
+
return {
|
|
7
|
+
pathValues: new Map(),
|
|
8
|
+
queryValues: new Map(),
|
|
9
|
+
headerValues: new Map(),
|
|
10
|
+
cookieValues: new Map(),
|
|
11
|
+
bodyValues: new Map(),
|
|
12
|
+
multipartValues: new Map(),
|
|
13
|
+
dryRun: false,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/** Calls GET /get-api-key-info to confirm an API key is valid and fetch its org/key identity. */
|
|
17
|
+
export async function fetchApiKeyIdentity(apiKey, baseUrl, fetchImplementation = fetch) {
|
|
18
|
+
if (!getApiKeyInfoOperation)
|
|
19
|
+
throw new Error("getApiKeyInfo operation is missing from the generated catalog.");
|
|
20
|
+
const request = await prepareRequest(getApiKeyInfoOperation, emptyArgs(), apiKey, baseUrl);
|
|
21
|
+
const response = await sendRequest(getApiKeyInfoOperation, request.url, request.init, fetchImplementation);
|
|
22
|
+
const text = await response.text();
|
|
23
|
+
const value = text ? (() => { try {
|
|
24
|
+
return JSON.parse(text);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return text;
|
|
28
|
+
} })() : null;
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
if (response.status === 401)
|
|
31
|
+
throw new Error("This API key was rejected by the Retell API.");
|
|
32
|
+
throw new Error(`Retell API request failed (${response.status}): ${typeof value === "string" ? value : JSON.stringify(value)}`);
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
orgName: value.org_name,
|
|
36
|
+
apiKeyName: value.api_key_name ?? null,
|
|
37
|
+
};
|
|
38
|
+
}
|