diff --git a/skills/resend-cli/SKILL.md b/skills/resend-cli/SKILL.md index d0890d36..54036f9d 100644 --- a/skills/resend-cli/SKILL.md +++ b/skills/resend-cli/SKILL.md @@ -11,7 +11,7 @@ description: > license: MIT metadata: author: resend - version: "1.11.0" + version: "1.12.0" homepage: https://resend.com/docs/cli-agents source: https://github.com/resend/resend-cli openclaw: @@ -140,7 +140,7 @@ Auth resolves: `--api-key` flag > `RESEND_API_KEY` env > config file (`resend lo | `broadcasts` | create, send, update, delete, list | | `contacts` | create, update, delete, segments, topics | | `contact-properties` | create, update, delete, list | -| `segments` | create, get, list, delete | +| `segments` | create, get, list, delete, contacts | | `templates` | create, publish, duplicate, delete, list | | `topics` | create, update, delete, list | | `webhooks` | create, update, listen, delete, list | diff --git a/skills/resend-cli/references/segments.md b/skills/resend-cli/references/segments.md index a7bbc325..353a2df8 100644 --- a/skills/resend-cli/references/segments.md +++ b/skills/resend-cli/references/segments.md @@ -37,3 +37,17 @@ Detailed flag specifications for `resend segments` commands. | `--yes` | boolean | Yes (non-interactive) | Skip confirmation | Deleting a segment does NOT delete its contacts. + +--- + +## segments contacts + +**Argument:** `[segmentId]` — Segment UUID (interactive picker if omitted) + +| Flag | Type | Default | Description | +|------|------|---------|-------------| +| `--limit ` | number | 10 | Max results (1-100) | +| `--after ` | string | — | Forward pagination | +| `--before ` | string | — | Backward pagination | + +Lists contacts belonging to a segment. Uses `resend.contacts.list({ segmentId })` which maps to `GET /segments/:segment_id/contacts`. diff --git a/src/commands/segments/contacts.ts b/src/commands/segments/contacts.ts new file mode 100644 index 00000000..4ca28811 --- /dev/null +++ b/src/commands/segments/contacts.ts @@ -0,0 +1,69 @@ +import { Command } from '@commander-js/extra-typings'; +import { runList } from '../../lib/actions'; +import type { GlobalOpts } from '../../lib/client'; +import { buildHelpText } from '../../lib/help-text'; +import { + buildPaginationOpts, + parseLimitOpt, + printPaginationHint, +} from '../../lib/pagination'; +import { pickId } from '../../lib/prompts'; +import { renderContactsTable } from '../contacts/utils'; +import { segmentPickerConfig } from './utils'; + +export const segmentContactsCommand = new Command('contacts') + .description('List contacts belonging to a segment') + .argument('[segmentId]', 'ID of the segment') + .option('--limit ', 'Maximum number of contacts to return (1-100)', '10') + .option('--after ', 'Return contacts after this cursor (next page)') + .option( + '--before ', + 'Return contacts before this cursor (previous page)', + ) + .addHelpText( + 'after', + buildHelpText({ + context: `Lists all contacts that belong to the given segment. + +If no segment ID is provided interactively, a picker will prompt you to choose one. + +Pagination: use --after or --before with a contact ID as the cursor. + Only one of --after or --before may be used at a time. + The response includes has_more: true when additional pages exist.`, + output: ` {"object":"list","data":[{"id":"...","email":"...","first_name":"...","last_name":"...","unsubscribed":false}],"has_more":false}`, + errorCodes: ['auth_error', 'invalid_limit', 'list_error'], + examples: [ + 'resend segments contacts 78261eea-8f8b-4381-83c6-79fa7120f1cf', + 'resend segments contacts 78261eea-8f8b-4381-83c6-79fa7120f1cf --limit 25 --json', + 'resend segments contacts 78261eea-8f8b-4381-83c6-79fa7120f1cf --after 479e3145-dd38-4932-8c0c-e58b548c9e76', + ], + }), + ) + .action(async (idArg, opts, cmd) => { + const globalOpts = cmd.optsWithGlobals() as GlobalOpts; + const segmentId = await pickId(idArg, segmentPickerConfig, globalOpts); + const limit = parseLimitOpt(opts.limit, globalOpts); + const paginationOpts = buildPaginationOpts( + limit, + opts.after, + opts.before, + globalOpts, + ); + await runList( + { + loading: 'Fetching segment contacts...', + sdkCall: (resend) => + resend.contacts.list({ segmentId, ...paginationOpts }), + onInteractive: (list) => { + console.log(renderContactsTable(list.data)); + printPaginationHint(list, `segments contacts ${segmentId}`, { + limit, + before: opts.before, + apiKey: globalOpts.apiKey, + profile: globalOpts.profile, + }); + }, + }, + globalOpts, + ); + }); diff --git a/src/commands/segments/index.ts b/src/commands/segments/index.ts index 85f1ef96..d4ee6a9e 100644 --- a/src/commands/segments/index.ts +++ b/src/commands/segments/index.ts @@ -1,5 +1,6 @@ import { Command } from '@commander-js/extra-typings'; import { buildHelpText } from '../../lib/help-text'; +import { segmentContactsCommand } from './contacts'; import { createSegmentCommand } from './create'; import { deleteSegmentCommand } from './delete'; import { getSegmentCommand } from './get'; @@ -24,10 +25,12 @@ There is no "update" endpoint — to rename a segment, delete it and recreate.`, 'resend segments list', 'resend segments create --name "Newsletter Subscribers"', 'resend segments get 78261eea-8f8b-4381-83c6-79fa7120f1cf', + 'resend segments contacts 78261eea-8f8b-4381-83c6-79fa7120f1cf', 'resend segments delete 78261eea-8f8b-4381-83c6-79fa7120f1cf --yes', ], }), ) + .addCommand(segmentContactsCommand) .addCommand(createSegmentCommand) .addCommand(getSegmentCommand) .addCommand(listSegmentsCommand, { isDefault: true })