@zapier/zapier-sdk-cli 0.55.2 → 0.55.3
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/CHANGELOG.md +6 -0
- package/dist/cli.cjs +30 -6
- package/dist/cli.mjs +30 -6
- package/dist/experimental.cjs +57 -31
- package/dist/experimental.mjs +57 -31
- package/dist/index.cjs +58 -32
- package/dist/index.mjs +58 -32
- package/dist/login.cjs +2 -0
- package/dist/login.mjs +2 -0
- package/dist/package.json +1 -1
- package/dist/src/login/credentials-store.d.ts +22 -0
- package/dist/src/login/credentials-store.js +33 -0
- package/dist/src/utils/auth/account-auth.js +11 -6
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -11,6 +11,28 @@ export type CredentialsEntry = z.infer<typeof CredentialsEntrySchema>;
|
|
|
11
11
|
export declare function getActiveCredentials(options?: {
|
|
12
12
|
baseUrl?: string;
|
|
13
13
|
}): CredentialsEntry | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Pick the first credential name not already present in `taken`. Returns
|
|
16
|
+
* `baseName` if it is free, otherwise appends an incrementing `-1`, `-2`, …
|
|
17
|
+
* suffix to the literal base — a base ending in a digit is left intact, so
|
|
18
|
+
* `acme-1` collides into `acme-1-1`. Storing a credential under a name that
|
|
19
|
+
* already exists locally silently replaces that entry (and deletes its
|
|
20
|
+
* keychain secret), so non-interactive login uses this to avoid clobbering a
|
|
21
|
+
* dormant credential of the same name.
|
|
22
|
+
*/
|
|
23
|
+
export declare function firstAvailableCredentialName({ baseName, taken, }: {
|
|
24
|
+
baseName: string;
|
|
25
|
+
taken: ReadonlySet<string>;
|
|
26
|
+
}): string;
|
|
27
|
+
/**
|
|
28
|
+
* Resolve a locally-unique credential name for the given baseUrl. Local
|
|
29
|
+
* credentials are keyed by (name, baseUrl), so name collisions — and the
|
|
30
|
+
* suffixing that avoids them — are scoped per baseUrl.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveAvailableCredentialName({ baseName, baseUrl, }: {
|
|
33
|
+
baseName: string;
|
|
34
|
+
baseUrl?: string;
|
|
35
|
+
}): string;
|
|
14
36
|
export declare function storeClientCredentials({ name, clientId, clientSecret, scopes, baseUrl, }: {
|
|
15
37
|
name: string;
|
|
16
38
|
clientId: string;
|
|
@@ -3,6 +3,7 @@ import { getPassword, setPassword, deletePassword } from "cross-keychain";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { DEFAULT_AUTH_BASE_URL, getConfig } from "./config";
|
|
5
5
|
import { enqueue, getBackendInfo } from "./keychain";
|
|
6
|
+
import { ZapierCliValidationError } from "../utils/errors";
|
|
6
7
|
const SERVICE = "zapier-sdk-cli";
|
|
7
8
|
const CREDENTIALS_KEY = "credentials";
|
|
8
9
|
const REGISTRY_KEY = "credentialsRegistry";
|
|
@@ -41,6 +42,38 @@ export function getActiveCredentials(options) {
|
|
|
41
42
|
return undefined;
|
|
42
43
|
return findEntry(readRegistry(), name, normalizeBaseUrl(options?.baseUrl));
|
|
43
44
|
}
|
|
45
|
+
const MAX_CREDENTIAL_NAME_ATTEMPTS = 500;
|
|
46
|
+
/**
|
|
47
|
+
* Pick the first credential name not already present in `taken`. Returns
|
|
48
|
+
* `baseName` if it is free, otherwise appends an incrementing `-1`, `-2`, …
|
|
49
|
+
* suffix to the literal base — a base ending in a digit is left intact, so
|
|
50
|
+
* `acme-1` collides into `acme-1-1`. Storing a credential under a name that
|
|
51
|
+
* already exists locally silently replaces that entry (and deletes its
|
|
52
|
+
* keychain secret), so non-interactive login uses this to avoid clobbering a
|
|
53
|
+
* dormant credential of the same name.
|
|
54
|
+
*/
|
|
55
|
+
export function firstAvailableCredentialName({ baseName, taken, }) {
|
|
56
|
+
if (!taken.has(baseName))
|
|
57
|
+
return baseName;
|
|
58
|
+
for (let i = 1; i <= MAX_CREDENTIAL_NAME_ATTEMPTS; i++) {
|
|
59
|
+
const candidate = `${baseName}-${i}`;
|
|
60
|
+
if (!taken.has(candidate))
|
|
61
|
+
return candidate;
|
|
62
|
+
}
|
|
63
|
+
throw new ZapierCliValidationError(`Could not find an available credential name for "${baseName}" after ${MAX_CREDENTIAL_NAME_ATTEMPTS} attempts.`);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Resolve a locally-unique credential name for the given baseUrl. Local
|
|
67
|
+
* credentials are keyed by (name, baseUrl), so name collisions — and the
|
|
68
|
+
* suffixing that avoids them — are scoped per baseUrl.
|
|
69
|
+
*/
|
|
70
|
+
export function resolveAvailableCredentialName({ baseName, baseUrl, }) {
|
|
71
|
+
const resolvedBaseUrl = normalizeBaseUrl(baseUrl);
|
|
72
|
+
const taken = new Set(readRegistry()
|
|
73
|
+
.filter((e) => e.baseUrl === resolvedBaseUrl)
|
|
74
|
+
.map((e) => e.name));
|
|
75
|
+
return firstAvailableCredentialName({ baseName, taken });
|
|
76
|
+
}
|
|
44
77
|
export async function storeClientCredentials({ name, clientId, clientSecret, scopes, baseUrl, }) {
|
|
45
78
|
if (!name || typeof name !== "string") {
|
|
46
79
|
throw new Error("storeClientCredentials: name is required");
|
|
@@ -2,7 +2,7 @@ import { hostname } from "node:os";
|
|
|
2
2
|
import inquirer from "inquirer";
|
|
3
3
|
import { buildApplicationLifecycleEvent, getOrCreateApiClient, isCredentialsObject, } from "@zapier/zapier-sdk";
|
|
4
4
|
import { revokeCredentials } from "../../login/credentials-revoke";
|
|
5
|
-
import { deleteStoredClientCredentials, getActiveCredentials, } from "../../login/credentials-store";
|
|
5
|
+
import { deleteStoredClientCredentials, getActiveCredentials, resolveAvailableCredentialName, } from "../../login/credentials-store";
|
|
6
6
|
import { clearLegacyJwtState, hasLegacyJwtConfig, } from "../../login/legacy-jwt";
|
|
7
7
|
import { resolveCredentialsBaseUrl } from "../../plugins/auth/credentials-base-url";
|
|
8
8
|
import { resolveNonInteractive } from "../non-interactive";
|
|
@@ -242,13 +242,18 @@ export async function runAccountAuth({ sdk, options, entryPoint, }) {
|
|
|
242
242
|
const profile = await getProfile(scopedApi);
|
|
243
243
|
console.log(getProfileMessage(entryPoint, profile.email));
|
|
244
244
|
console.log("\nGenerating credentials so this machine can make authenticated requests on your behalf.");
|
|
245
|
-
const
|
|
246
|
-
?
|
|
247
|
-
email,
|
|
245
|
+
const baseName = interactive
|
|
246
|
+
? await promptCredentialsName({
|
|
247
|
+
email: profile.email,
|
|
248
248
|
promptMessage: getCredentialsPromptMessage(entryPoint),
|
|
249
249
|
})
|
|
250
|
-
: resolveDefaultCredentialsName;
|
|
251
|
-
|
|
250
|
+
: resolveDefaultCredentialsName({ email: profile.email });
|
|
251
|
+
// Non-interactive login can't prompt for a new name, so a default name that
|
|
252
|
+
// already exists locally would silently overwrite that credential. Suffix it
|
|
253
|
+
// instead. Interactive callers chose the name themselves, so leave it as-is.
|
|
254
|
+
const credentialName = interactive
|
|
255
|
+
? baseName
|
|
256
|
+
: resolveAvailableCredentialName({ baseName, baseUrl: credentialsBaseUrl });
|
|
252
257
|
const useApprovals = options.useApprovals === true;
|
|
253
258
|
await saveClientCredentials({
|
|
254
259
|
api: scopedApi,
|