@zapier/zapier-sdk-cli 0.49.1 → 0.51.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/dist/index.cjs CHANGED
@@ -1180,10 +1180,18 @@ async function runOauthFlow({
1180
1180
 
1181
1181
  // src/utils/auth/client-credentials.ts
1182
1182
  var CREDENTIALS_SCOPES = ["external", "credentials"];
1183
- async function createCredentialsOnServer(api2, name) {
1183
+ var EMPTY_POLICY = {
1184
+ version: 2,
1185
+ statements: []
1186
+ };
1187
+ async function createCredentialsOnServer(api2, name, policy) {
1184
1188
  const response = await api2.post(
1185
1189
  "/api/v0/client-credentials",
1186
- { name, allowed_scopes: CREDENTIALS_SCOPES },
1190
+ {
1191
+ name,
1192
+ allowed_scopes: CREDENTIALS_SCOPES,
1193
+ ...policy !== void 0 && { policy }
1194
+ },
1187
1195
  { authRequired: true, requiredScopes: ["credentials"] }
1188
1196
  );
1189
1197
  return {
@@ -1200,9 +1208,14 @@ async function deleteCredentialsOnServer(api2, clientId) {
1200
1208
  async function setupClientCredentials({
1201
1209
  api: api2,
1202
1210
  name,
1203
- credentialsBaseUrl
1211
+ credentialsBaseUrl,
1212
+ policy
1204
1213
  }) {
1205
- const { clientId, clientSecret } = await createCredentialsOnServer(api2, name);
1214
+ const { clientId, clientSecret } = await createCredentialsOnServer(
1215
+ api2,
1216
+ name,
1217
+ policy
1218
+ );
1206
1219
  try {
1207
1220
  await withRetry({
1208
1221
  action: () => storeClientCredentials({
@@ -1244,7 +1257,10 @@ async function resolveCredentialsBaseUrl(context) {
1244
1257
  return getBaseUrlFromResolvedCredentials(resolvedCredentials) ?? getBaseUrlFromOptionsCredentials(context.options?.credentials) ?? context.options?.baseUrl;
1245
1258
  }
1246
1259
  var LoginSchema = zod.z.object({
1247
- timeout: zod.z.string().optional().describe("Login timeout in seconds (default: 300)")
1260
+ timeout: zod.z.string().optional().describe("Login timeout in seconds (default: 300)"),
1261
+ useApprovals: zod.z.boolean().optional().describe(
1262
+ "Require approvals for actions performed with these credentials"
1263
+ )
1248
1264
  }).describe("Log in to Zapier to access your account");
1249
1265
 
1250
1266
  // src/plugins/login/index.ts
@@ -1338,7 +1354,9 @@ function emitLoginSuccess({
1338
1354
  sdk.context.eventEmission.emit(
1339
1355
  "platform.sdk.ApplicationLifecycleEvent",
1340
1356
  zapierSdk.buildApplicationLifecycleEvent(
1341
- { lifecycle_event_type: "login_success" },
1357
+ {
1358
+ lifecycle_event_type: "login_success"
1359
+ },
1342
1360
  {
1343
1361
  customuser_id: profile.user_id,
1344
1362
  account_id: profile.roles[0]?.account_id ?? null
@@ -1410,15 +1428,20 @@ var loginPlugin = zapierSdk.definePlugin(
1410
1428
  profile.email,
1411
1429
  credentialsBaseUrl
1412
1430
  );
1431
+ const useApprovals = options.useApprovals === true;
1413
1432
  await setupClientCredentials({
1414
1433
  api: scopedApi,
1415
1434
  name: credentialName,
1416
- credentialsBaseUrl
1435
+ credentialsBaseUrl,
1436
+ ...useApprovals && { policy: EMPTY_POLICY }
1417
1437
  });
1418
1438
  await bestEffortClearLegacyJwtState();
1419
1439
  console.log(
1420
1440
  `\u2705 Credentials "${credentialName}" created and set as default. You are ready to use the Zapier SDK.`
1421
1441
  );
1442
+ if (useApprovals) {
1443
+ console.log("\u{1F510} Approvals are enabled for these credentials.");
1444
+ }
1422
1445
  emitLoginSuccess({ sdk: sdk2, profile });
1423
1446
  }
1424
1447
  })
@@ -3962,7 +3985,7 @@ zapierSdk.definePlugin(
3962
3985
  // package.json with { type: 'json' }
3963
3986
  var package_default = {
3964
3987
  name: "@zapier/zapier-sdk-cli",
3965
- version: "0.49.1"};
3988
+ version: "0.51.0"};
3966
3989
 
3967
3990
  // src/sdk.ts
3968
3991
  zapierSdk.injectCliLogin(login_exports);
@@ -3990,7 +4013,7 @@ function createZapierCliSdk(options = {}) {
3990
4013
 
3991
4014
  // package.json
3992
4015
  var package_default2 = {
3993
- version: "0.49.1"};
4016
+ version: "0.51.0"};
3994
4017
 
3995
4018
  // src/telemetry/builders.ts
3996
4019
  function createCliBaseEvent(context = {}) {
package/dist/index.mjs CHANGED
@@ -1144,10 +1144,18 @@ async function runOauthFlow({
1144
1144
 
1145
1145
  // src/utils/auth/client-credentials.ts
1146
1146
  var CREDENTIALS_SCOPES = ["external", "credentials"];
1147
- async function createCredentialsOnServer(api2, name) {
1147
+ var EMPTY_POLICY = {
1148
+ version: 2,
1149
+ statements: []
1150
+ };
1151
+ async function createCredentialsOnServer(api2, name, policy) {
1148
1152
  const response = await api2.post(
1149
1153
  "/api/v0/client-credentials",
1150
- { name, allowed_scopes: CREDENTIALS_SCOPES },
1154
+ {
1155
+ name,
1156
+ allowed_scopes: CREDENTIALS_SCOPES,
1157
+ ...policy !== void 0 && { policy }
1158
+ },
1151
1159
  { authRequired: true, requiredScopes: ["credentials"] }
1152
1160
  );
1153
1161
  return {
@@ -1164,9 +1172,14 @@ async function deleteCredentialsOnServer(api2, clientId) {
1164
1172
  async function setupClientCredentials({
1165
1173
  api: api2,
1166
1174
  name,
1167
- credentialsBaseUrl
1175
+ credentialsBaseUrl,
1176
+ policy
1168
1177
  }) {
1169
- const { clientId, clientSecret } = await createCredentialsOnServer(api2, name);
1178
+ const { clientId, clientSecret } = await createCredentialsOnServer(
1179
+ api2,
1180
+ name,
1181
+ policy
1182
+ );
1170
1183
  try {
1171
1184
  await withRetry({
1172
1185
  action: () => storeClientCredentials({
@@ -1208,7 +1221,10 @@ async function resolveCredentialsBaseUrl(context) {
1208
1221
  return getBaseUrlFromResolvedCredentials(resolvedCredentials) ?? getBaseUrlFromOptionsCredentials(context.options?.credentials) ?? context.options?.baseUrl;
1209
1222
  }
1210
1223
  var LoginSchema = z.object({
1211
- timeout: z.string().optional().describe("Login timeout in seconds (default: 300)")
1224
+ timeout: z.string().optional().describe("Login timeout in seconds (default: 300)"),
1225
+ useApprovals: z.boolean().optional().describe(
1226
+ "Require approvals for actions performed with these credentials"
1227
+ )
1212
1228
  }).describe("Log in to Zapier to access your account");
1213
1229
 
1214
1230
  // src/plugins/login/index.ts
@@ -1302,7 +1318,9 @@ function emitLoginSuccess({
1302
1318
  sdk.context.eventEmission.emit(
1303
1319
  "platform.sdk.ApplicationLifecycleEvent",
1304
1320
  buildApplicationLifecycleEvent(
1305
- { lifecycle_event_type: "login_success" },
1321
+ {
1322
+ lifecycle_event_type: "login_success"
1323
+ },
1306
1324
  {
1307
1325
  customuser_id: profile.user_id,
1308
1326
  account_id: profile.roles[0]?.account_id ?? null
@@ -1374,15 +1392,20 @@ var loginPlugin = definePlugin(
1374
1392
  profile.email,
1375
1393
  credentialsBaseUrl
1376
1394
  );
1395
+ const useApprovals = options.useApprovals === true;
1377
1396
  await setupClientCredentials({
1378
1397
  api: scopedApi,
1379
1398
  name: credentialName,
1380
- credentialsBaseUrl
1399
+ credentialsBaseUrl,
1400
+ ...useApprovals && { policy: EMPTY_POLICY }
1381
1401
  });
1382
1402
  await bestEffortClearLegacyJwtState();
1383
1403
  console.log(
1384
1404
  `\u2705 Credentials "${credentialName}" created and set as default. You are ready to use the Zapier SDK.`
1385
1405
  );
1406
+ if (useApprovals) {
1407
+ console.log("\u{1F510} Approvals are enabled for these credentials.");
1408
+ }
1386
1409
  emitLoginSuccess({ sdk: sdk2, profile });
1387
1410
  }
1388
1411
  })
@@ -3926,7 +3949,7 @@ definePlugin(
3926
3949
  // package.json with { type: 'json' }
3927
3950
  var package_default = {
3928
3951
  name: "@zapier/zapier-sdk-cli",
3929
- version: "0.49.1"};
3952
+ version: "0.51.0"};
3930
3953
 
3931
3954
  // src/sdk.ts
3932
3955
  injectCliLogin(login_exports);
@@ -3954,7 +3977,7 @@ function createZapierCliSdk(options = {}) {
3954
3977
 
3955
3978
  // package.json
3956
3979
  var package_default2 = {
3957
- version: "0.49.1"};
3980
+ version: "0.51.0"};
3958
3981
 
3959
3982
  // src/telemetry/builders.ts
3960
3983
  function createCliBaseEvent(context = {}) {
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapier/zapier-sdk-cli",
3
- "version": "0.49.1",
3
+ "version": "0.51.0",
4
4
  "description": "Command line interface for Zapier SDK",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -78,6 +78,7 @@
78
78
  "access": "public"
79
79
  },
80
80
  "dependencies": {
81
+ "@zapier/policy-schema": "0.11.0",
81
82
  "@inquirer/search": "^3.2.2",
82
83
  "@zapier/zapier-sdk": "workspace:*",
83
84
  "@zapier/zapier-sdk-mcp": "workspace:*",
@@ -22,6 +22,7 @@ export declare const loginPlugin: (sdk: {
22
22
  }) => {
23
23
  login: (options?: {
24
24
  timeout?: string | undefined;
25
+ useApprovals?: boolean | undefined;
25
26
  } | undefined) => Promise<void>;
26
27
  } & {
27
28
  context: {
@@ -5,7 +5,7 @@ import { clearLegacyJwtState, hasLegacyJwtConfig, } from "../../login/legacy-jwt
5
5
  import { getActiveCredentials, credentialsNameExists, deleteStoredClientCredentials, } from "../../login/credentials-store";
6
6
  import { revokeCredentials } from "../../login/credentials-revoke";
7
7
  import { runOauthFlow } from "../../utils/auth/oauth-flow";
8
- import { setupClientCredentials } from "../../utils/auth/client-credentials";
8
+ import { EMPTY_POLICY, setupClientCredentials, } from "../../utils/auth/client-credentials";
9
9
  import { resolveCredentialsBaseUrl } from "../auth/credentials-base-url";
10
10
  import { LoginSchema } from "./schemas";
11
11
  function toPkceCredentials(credentials) {
@@ -98,7 +98,9 @@ async function promptCredentialsName(email, baseUrl) {
98
98
  return credentialName;
99
99
  }
100
100
  function emitLoginSuccess({ sdk, profile, }) {
101
- sdk.context.eventEmission.emit("platform.sdk.ApplicationLifecycleEvent", buildApplicationLifecycleEvent({ lifecycle_event_type: "login_success" }, {
101
+ sdk.context.eventEmission.emit("platform.sdk.ApplicationLifecycleEvent", buildApplicationLifecycleEvent({
102
+ lifecycle_event_type: "login_success",
103
+ }, {
102
104
  customuser_id: profile.user_id,
103
105
  account_id: profile.roles[0]?.account_id ?? null,
104
106
  }));
@@ -168,13 +170,18 @@ export const loginPlugin = definePlugin((sdk) => createPluginMethod(sdk, {
168
170
  console.log(`👤 Logged in as ${profile.email}`);
169
171
  console.log("\nGenerating credentials so this machine can make authenticated requests on your behalf.");
170
172
  const credentialName = await promptCredentialsName(profile.email, credentialsBaseUrl);
173
+ const useApprovals = options.useApprovals === true;
171
174
  await setupClientCredentials({
172
175
  api: scopedApi,
173
176
  name: credentialName,
174
177
  credentialsBaseUrl,
178
+ ...(useApprovals && { policy: EMPTY_POLICY }),
175
179
  });
176
180
  await bestEffortClearLegacyJwtState();
177
181
  console.log(`✅ Credentials "${credentialName}" created and set as default. You are ready to use the Zapier SDK.`);
182
+ if (useApprovals) {
183
+ console.log("🔐 Approvals are enabled for these credentials.");
184
+ }
178
185
  emitLoginSuccess({ sdk, profile });
179
186
  },
180
187
  }));
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  export declare const LoginSchema: z.ZodObject<{
3
3
  timeout: z.ZodOptional<z.ZodString>;
4
+ useApprovals: z.ZodOptional<z.ZodBoolean>;
4
5
  }, z.core.$strip>;
5
6
  export type LoginOptions = z.infer<typeof LoginSchema>;
@@ -6,5 +6,9 @@ export const LoginSchema = z
6
6
  .string()
7
7
  .optional()
8
8
  .describe("Login timeout in seconds (default: 300)"),
9
+ useApprovals: z
10
+ .boolean()
11
+ .optional()
12
+ .describe("Require approvals for actions performed with these credentials"),
9
13
  })
10
14
  .describe("Log in to Zapier to access your account");
@@ -1,8 +1,18 @@
1
+ import type { Policy } from "@zapier/policy-schema";
1
2
  import type { ApiClient } from "@zapier/zapier-sdk";
3
+ /**
4
+ * The canonical empty approvals policy. Attaching this to a credential at
5
+ * creation time tells the server that requests made with these credentials
6
+ * are subject to approval. Server-side enforcement decides which specific
7
+ * requests trigger the approval flow; the SDK only attaches the policy.
8
+ */
9
+ export declare const EMPTY_POLICY: Policy;
2
10
  export interface SetupClientCredentialsOptions {
3
11
  api: ApiClient;
4
12
  name: string;
5
13
  credentialsBaseUrl?: string;
14
+ /** Optional policy document to attach to the new credential. */
15
+ policy?: Policy;
6
16
  }
7
17
  export interface SetupClientCredentialsResult {
8
18
  clientId: string;
@@ -13,4 +23,4 @@ export interface SetupClientCredentialsResult {
13
23
  * to roll back the orphan; if that rollback also fails, surface a
14
24
  * manual-fix message and re-throw the original store error.
15
25
  */
16
- export declare function setupClientCredentials({ api, name, credentialsBaseUrl, }: SetupClientCredentialsOptions): Promise<SetupClientCredentialsResult>;
26
+ export declare function setupClientCredentials({ api, name, credentialsBaseUrl, policy, }: SetupClientCredentialsOptions): Promise<SetupClientCredentialsResult>;
@@ -3,10 +3,24 @@ import { withRetry } from "../retry";
3
3
  // "external" allows SDK operations (listing, running actions, etc.);
4
4
  // "credentials" allows managing client credentials (list, create, delete).
5
5
  const CREDENTIALS_SCOPES = ["external", "credentials"];
6
- async function createCredentialsOnServer(api, name) {
6
+ /**
7
+ * The canonical empty approvals policy. Attaching this to a credential at
8
+ * creation time tells the server that requests made with these credentials
9
+ * are subject to approval. Server-side enforcement decides which specific
10
+ * requests trigger the approval flow; the SDK only attaches the policy.
11
+ */
12
+ export const EMPTY_POLICY = {
13
+ version: 2,
14
+ statements: [],
15
+ };
16
+ async function createCredentialsOnServer(api, name, policy) {
7
17
  // The server wraps the credential in `{ data: { ... } }`; mirror the
8
18
  // shape the SDK's own createClientCredentials plugin reads.
9
- const response = await api.post("/api/v0/client-credentials", { name, allowed_scopes: CREDENTIALS_SCOPES }, { authRequired: true, requiredScopes: ["credentials"] });
19
+ const response = await api.post("/api/v0/client-credentials", {
20
+ name,
21
+ allowed_scopes: CREDENTIALS_SCOPES,
22
+ ...(policy !== undefined && { policy }),
23
+ }, { authRequired: true, requiredScopes: ["credentials"] });
10
24
  return {
11
25
  clientId: response.data.client_id,
12
26
  clientSecret: response.data.client_secret,
@@ -24,8 +38,8 @@ async function deleteCredentialsOnServer(api, clientId) {
24
38
  * to roll back the orphan; if that rollback also fails, surface a
25
39
  * manual-fix message and re-throw the original store error.
26
40
  */
27
- export async function setupClientCredentials({ api, name, credentialsBaseUrl, }) {
28
- const { clientId, clientSecret } = await createCredentialsOnServer(api, name);
41
+ export async function setupClientCredentials({ api, name, credentialsBaseUrl, policy, }) {
42
+ const { clientId, clientSecret } = await createCredentialsOnServer(api, name, policy);
29
43
  try {
30
44
  await withRetry({
31
45
  action: () => storeClientCredentials({
@@ -1,5 +1,26 @@
1
1
  import { z } from "zod";
2
2
  import { type ZapierSdk } from "@zapier/zapier-sdk";
3
+ /**
4
+ * Append a choice's `hint` to its name as dimmed parens, returning a new
5
+ * choice object. An array hint is joined with ", ". Used by every prompt
6
+ * backend that renders resolver-supplied choices so the dim styling lives
7
+ * in one place.
8
+ *
9
+ * Default-when-unset: if `hint` is not provided and the choice's `value`
10
+ * is a primitive (string or number), the value is auto-rendered as the
11
+ * hint. So a resolver returning `{ name: "Slack", value: "SlackCLIAPI" }`
12
+ * with no `hint` renders as `Slack (SlackCLIAPI)`. To opt out of the
13
+ * auto-default, set `hint: ""` or `hint: []` explicitly.
14
+ *
15
+ * Skip-when-equal: a hint that matches the rendered name verbatim is
16
+ * suppressed so primitive-enum resolvers (where `name === String(value)`)
17
+ * don't render "read (read)". Case differences still render both.
18
+ */
19
+ export declare function renderChoiceLabel<T extends {
20
+ name: string;
21
+ value: unknown;
22
+ hint?: string | string[];
23
+ }>(choice: T): T;
3
24
  export declare class SchemaParameterResolver {
4
25
  private debug;
5
26
  private spinner;
@@ -87,6 +87,40 @@ function coerceToSchemaType(value, schema) {
87
87
  // ============================================================================
88
88
  // Schema Parameter Resolver
89
89
  // ============================================================================
90
+ /**
91
+ * Append a choice's `hint` to its name as dimmed parens, returning a new
92
+ * choice object. An array hint is joined with ", ". Used by every prompt
93
+ * backend that renders resolver-supplied choices so the dim styling lives
94
+ * in one place.
95
+ *
96
+ * Default-when-unset: if `hint` is not provided and the choice's `value`
97
+ * is a primitive (string or number), the value is auto-rendered as the
98
+ * hint. So a resolver returning `{ name: "Slack", value: "SlackCLIAPI" }`
99
+ * with no `hint` renders as `Slack (SlackCLIAPI)`. To opt out of the
100
+ * auto-default, set `hint: ""` or `hint: []` explicitly.
101
+ *
102
+ * Skip-when-equal: a hint that matches the rendered name verbatim is
103
+ * suppressed so primitive-enum resolvers (where `name === String(value)`)
104
+ * don't render "read (read)". Case differences still render both.
105
+ */
106
+ export function renderChoiceLabel(choice) {
107
+ let effectiveHint = choice.hint;
108
+ if (effectiveHint === undefined &&
109
+ (typeof choice.value === "string" || typeof choice.value === "number")) {
110
+ effectiveHint = String(choice.value);
111
+ }
112
+ if (!effectiveHint ||
113
+ (Array.isArray(effectiveHint) && effectiveHint.length === 0)) {
114
+ return choice;
115
+ }
116
+ const hintText = Array.isArray(effectiveHint)
117
+ ? effectiveHint.join(", ")
118
+ : effectiveHint;
119
+ if (hintText === choice.name) {
120
+ return choice;
121
+ }
122
+ return { ...choice, name: `${choice.name} ${chalk.dim(`(${hintText})`)}` };
123
+ }
90
124
  export class SchemaParameterResolver {
91
125
  constructor() {
92
126
  this.debug = false;
@@ -513,7 +547,7 @@ export class SchemaParameterResolver {
513
547
  }
514
548
  const { items } = await this.unpackFetchResult(fetchResult, promptLabel);
515
549
  const choicesConfig = resolver.prompt(items, searchParams);
516
- const dataChoices = choicesConfig.choices ?? [];
550
+ const dataChoices = (choicesConfig.choices ?? []).map(renderChoiceLabel);
517
551
  const capabilityHintMessages = await this.computeCapabilityHints(resolver, context);
518
552
  const selected = await search({
519
553
  message: choicesConfig.message,
@@ -730,6 +764,12 @@ export class SchemaParameterResolver {
730
764
  while (true) {
731
765
  const promptConfig = dynamicResolver.prompt(items, context.resolvedParams);
732
766
  promptConfig.name = promptName;
767
+ // Fold any per-choice `hint` into the rendered name once, here, so
768
+ // both the search-backed list path and the checkbox path below
769
+ // pick up the dim styling without each having to know about hints.
770
+ if (promptConfig.choices) {
771
+ promptConfig.choices = promptConfig.choices.map(renderChoiceLabel);
772
+ }
733
773
  // Friendlier than inquirer's default "No selectable choices. All
734
774
  // choices are disabled." when the resolver returns no items (or
735
775
  // filters everything out in `prompt`). Use ZapierCliValidationError