@vellumai/assistant 0.4.35 → 0.4.37
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/AGENTS.md +1 -1
- package/ARCHITECTURE.md +44 -49
- package/README.md +32 -20
- package/docs/architecture/keychain-broker.md +186 -0
- package/docs/architecture/security.md +110 -116
- package/docs/runbook-trusted-contacts.md +2 -2
- package/docs/skills.md +25 -25
- package/package.json +5 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +11 -2
- package/src/__tests__/actor-token-service.test.ts +1 -0
- package/src/__tests__/amazon-cdp-integration.test.ts +74 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +38 -9
- package/src/__tests__/assistant-id-boundary-guard.test.ts +29 -0
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/bundle-scanner.test.ts +1 -1
- package/src/__tests__/channel-guardian.test.ts +102 -102
- package/src/__tests__/channel-invite-transport.test.ts +155 -256
- package/src/__tests__/channel-readiness-routes.test.ts +336 -0
- package/src/__tests__/checker.test.ts +6 -6
- package/src/__tests__/chrome-cdp.test.ts +350 -0
- package/src/__tests__/computer-use-session-lifecycle.test.ts +3 -3
- package/src/__tests__/computer-use-session-working-dir.test.ts +86 -52
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +1 -1
- package/src/__tests__/config-loader-migration.test.ts +85 -0
- package/src/__tests__/conversation-pairing.test.ts +370 -5
- package/src/__tests__/credential-broker-browser-fill.test.ts +1 -10
- package/src/__tests__/credential-broker-server-use.test.ts +1 -10
- package/src/__tests__/credential-security-e2e.test.ts +7 -1
- package/src/__tests__/credential-security-invariants.test.ts +14 -20
- package/src/__tests__/credential-vault-unit.test.ts +1 -11
- package/src/__tests__/credential-vault.test.ts +5 -19
- package/src/__tests__/credentials-cli.test.ts +814 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +23 -4
- package/src/__tests__/email-invite-adapter.test.ts +78 -0
- package/src/__tests__/email-service-config-fallback.test.ts +102 -0
- package/src/__tests__/encrypted-store.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +3 -3
- package/src/__tests__/gateway-only-enforcement.test.ts +5 -1
- package/src/__tests__/guardian-actions-endpoint.test.ts +70 -12
- package/src/__tests__/guardian-outbound-http.test.ts +53 -47
- package/src/__tests__/handle-user-message-secret-resume.test.ts +23 -0
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +32 -23
- package/src/__tests__/handlers-telegram-config.test.ts +8 -2
- package/src/__tests__/handlers-twitter-config.test.ts +2 -2
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +108 -7
- package/src/__tests__/ingress-reconcile.test.ts +6 -0
- package/src/__tests__/intent-routing.test.ts +23 -4
- package/src/__tests__/invite-routes-http.test.ts +12 -0
- package/src/__tests__/ipc-snapshot.test.ts +8 -2
- package/src/__tests__/keychain-broker-client.test.ts +543 -0
- package/src/__tests__/llm-usage-store.test.ts +344 -0
- package/src/__tests__/mcp-client-auth.test.ts +2 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
- package/src/__tests__/migration-transport.test.ts +49 -0
- package/src/__tests__/notification-broadcaster.test.ts +205 -5
- package/src/__tests__/notification-deep-link.test.ts +365 -1
- package/src/__tests__/oauth-connect-handler.test.ts +2 -2
- package/src/__tests__/onboarding-starter-tasks.test.ts +17 -4
- package/src/__tests__/proxy-approval-callback.test.ts +1 -1
- package/src/__tests__/recording-handler.test.ts +1 -1
- package/src/__tests__/recording-intent-handler.test.ts +6 -1
- package/src/__tests__/recording-state-machine.test.ts +1 -1
- package/src/__tests__/relay-server.test.ts +9 -1
- package/src/__tests__/ride-shotgun-handler.test.ts +499 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +160 -1
- package/src/__tests__/script-proxy-injection-runtime.test.ts +299 -2
- package/src/__tests__/script-proxy-profile-template-fallback.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +8 -2
- package/src/__tests__/secure-keys.test.ts +175 -216
- package/src/__tests__/session-confirmation-signals.test.ts +1 -1
- package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -1
- package/src/__tests__/session-queue.test.ts +2 -1
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +2 -2
- package/src/__tests__/skill-feature-flags-integration.test.ts +29 -4
- package/src/__tests__/skill-feature-flags.test.ts +12 -9
- package/src/__tests__/skill-load-feature-flag.test.ts +26 -5
- package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
- package/src/__tests__/skills.test.ts +34 -4
- package/src/__tests__/slack-channel-config.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +26 -4
- package/src/__tests__/telegram-bot-username-resolution.test.ts +212 -0
- package/src/__tests__/telegram-invite-adapter.test.ts +164 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-permission-simulate-handler.test.ts +8 -2
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +9 -1
- package/src/__tests__/twitter-auth-handler.test.ts +2 -2
- package/src/__tests__/twitter-oauth-client.test.ts +1 -1
- package/src/__tests__/usage-routes.test.ts +339 -0
- package/src/__tests__/whatsapp-invite-adapter.test.ts +94 -0
- package/src/agent/loop.ts +3 -0
- package/src/amazon/checkout.ts +0 -1
- package/src/approvals/guardian-request-resolvers.ts +9 -1
- package/src/bundler/app-bundler.ts +28 -12
- package/src/bundler/bundle-scanner.ts +1 -1
- package/src/bundler/bundle-signer.ts +3 -3
- package/src/bundler/manifest.ts +1 -1
- package/src/bundler/signature-verifier.ts +3 -3
- package/src/channels/config.ts +1 -1
- package/src/cli/AGENTS.md +63 -0
- package/src/cli/__tests__/notifications.test.ts +470 -0
- package/src/cli/amazon.ts +344 -167
- package/src/cli/audit.ts +85 -0
- package/src/cli/autonomy.ts +369 -0
- package/src/cli/channels.ts +51 -0
- package/src/cli/completions.ts +208 -0
- package/src/cli/config.ts +220 -0
- package/src/cli/contacts.ts +471 -0
- package/src/cli/credentials.ts +564 -0
- package/src/cli/default-action.ts +14 -0
- package/src/cli/dev.ts +131 -0
- package/src/cli/doctor.ts +398 -0
- package/src/cli/email.ts +494 -0
- package/src/cli/influencer.ts +72 -0
- package/src/cli/integrations.ts +248 -57
- package/src/cli/keys.ts +114 -0
- package/src/cli/map.ts +46 -54
- package/src/cli/mcp.ts +111 -3
- package/src/cli/{config-commands.ts → memory.ts} +134 -245
- package/src/cli/notifications.ts +407 -0
- package/src/cli/program.ts +65 -0
- package/src/cli/reference.ts +48 -0
- package/src/cli/sequence.ts +154 -0
- package/src/cli/sessions.ts +262 -0
- package/src/cli/trust.ts +175 -0
- package/src/cli/twitter.ts +323 -106
- package/src/config/__tests__/build-cli-reference-section.test.ts +49 -0
- package/src/config/bundled-skills/amazon/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/TOOLS.json +26 -0
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +13 -0
- package/src/config/bundled-skills/contacts/SKILL.md +178 -10
- package/src/config/bundled-skills/doordash/doordash-cli.ts +23 -168
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +135 -34
- package/src/config/bundled-skills/messaging/tools/shared.ts +4 -1
- package/src/config/bundled-skills/twilio-setup/SKILL.md +70 -17
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/core-schema.ts +7 -0
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/loader.ts +26 -0
- package/src/config/schema.ts +4 -0
- package/src/config/skill-state.ts +0 -13
- package/src/config/system-prompt.ts +27 -0
- package/src/contacts/contact-store.ts +25 -0
- package/src/daemon/computer-use-session.ts +1 -1
- package/src/daemon/handlers/apps.ts +1 -0
- package/src/daemon/handlers/config-channels.ts +3 -3
- package/src/daemon/handlers/config-dispatch.ts +29 -0
- package/src/daemon/handlers/config-inbox.ts +4 -3
- package/src/daemon/handlers/config.ts +3 -43
- package/src/daemon/handlers/contacts.ts +34 -0
- package/src/daemon/handlers/index.ts +17 -3
- package/src/daemon/handlers/session-user-message.ts +7 -0
- package/src/daemon/handlers/sessions.ts +21 -2
- package/src/daemon/handlers/shared.ts +17 -0
- package/src/daemon/ipc-contract/apps.ts +2 -0
- package/src/daemon/ipc-contract/computer-use.ts +9 -0
- package/src/daemon/ipc-contract/contacts.ts +3 -3
- package/src/daemon/ipc-contract/inbox.ts +2 -0
- package/src/daemon/ipc-contract/messages.ts +4 -0
- package/src/daemon/ipc-contract/sessions.ts +8 -0
- package/src/daemon/ipc-contract-inventory.json +1 -0
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/ride-shotgun-handler.ts +139 -25
- package/src/daemon/session-agent-loop-handlers.ts +100 -0
- package/src/daemon/session-agent-loop.ts +72 -0
- package/src/daemon/session-tool-setup.ts +7 -0
- package/src/daemon/session.ts +23 -1
- package/src/daemon/tool-side-effects.ts +39 -1
- package/src/email/service.ts +59 -2
- package/src/index.ts +2 -60
- package/src/mcp/mcp-oauth-provider.ts +90 -8
- package/src/media/app-icon-generator.ts +86 -0
- package/src/memory/db-init.ts +11 -0
- package/src/memory/llm-usage-store.ts +186 -0
- package/src/memory/migrations/137-usage-dashboard-indexes.ts +26 -0
- package/src/memory/migrations/139-drop-usage-composite-indexes.ts +30 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema-migration.ts +1 -0
- package/src/memory/shared-app-links-store.ts +1 -1
- package/src/messaging/registry.ts +27 -0
- package/src/notifications/README.md +79 -70
- package/src/notifications/broadcaster.ts +2 -1
- package/src/notifications/conversation-pairing.ts +147 -13
- package/src/notifications/copy-composer.ts +7 -3
- package/src/notifications/destination-resolver.ts +14 -1
- package/src/notifications/emit-signal.ts +3 -2
- package/src/notifications/signal.ts +105 -1
- package/src/notifications/types.ts +16 -0
- package/src/permissions/checker.ts +29 -3
- package/src/permissions/prompter.ts +11 -3
- package/src/runtime/access-request-helper.ts +2 -1
- package/src/runtime/auth/route-policy.ts +7 -1
- package/src/runtime/channel-invite-transport.ts +40 -63
- package/src/runtime/channel-invite-transports/email.ts +13 -39
- package/src/runtime/channel-invite-transports/slack.ts +5 -34
- package/src/runtime/channel-invite-transports/sms.ts +8 -29
- package/src/runtime/channel-invite-transports/telegram.ts +69 -28
- package/src/runtime/channel-invite-transports/voice.ts +0 -7
- package/src/runtime/channel-invite-transports/whatsapp.ts +43 -0
- package/src/runtime/channel-readiness-service.ts +202 -45
- package/src/runtime/confirmation-request-guardian-bridge.ts +2 -1
- package/src/runtime/guardian-outbound-actions.ts +8 -5
- package/src/runtime/http-server.ts +2 -0
- package/src/runtime/invite-instruction-generator.ts +178 -0
- package/src/runtime/invite-service.ts +22 -25
- package/src/runtime/migrations/migration-transport.ts +13 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +8 -7
- package/src/runtime/routes/channel-readiness-routes.ts +30 -11
- package/src/runtime/routes/contact-routes.ts +54 -26
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +1 -1
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +2 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +2 -1
- package/src/runtime/routes/integration-routes.ts +1 -1
- package/src/runtime/routes/invite-routes.ts +1 -1
- package/src/runtime/routes/secret-routes.ts +31 -7
- package/src/runtime/routes/twilio-routes.ts +32 -1
- package/src/runtime/routes/usage-routes.ts +114 -0
- package/src/runtime/tool-grant-request-helper.ts +2 -1
- package/src/security/encrypted-store.ts +9 -5
- package/src/security/keychain-broker-client.ts +393 -0
- package/src/security/secure-keys.ts +106 -321
- package/src/tools/apps/executors.ts +73 -0
- package/src/tools/browser/auto-navigate.ts +15 -6
- package/src/tools/browser/chrome-cdp.ts +211 -0
- package/src/tools/browser/network-recorder.test.ts +83 -0
- package/src/tools/browser/network-recorder.ts +8 -7
- package/src/tools/browser/x-auto-navigate.ts +12 -6
- package/src/tools/credentials/policy-types.ts +24 -0
- package/src/tools/credentials/vault.ts +22 -27
- package/src/tools/network/script-proxy/session-manager.ts +47 -3
- package/src/tools/permission-checker.ts +1 -0
- package/src/tools/types.ts +2 -0
- package/src/tools/ui-surface/definitions.ts +1 -2
- package/src/tools/watch/watch-state.ts +2 -0
- package/src/__tests__/key-migration.test.ts +0 -240
- package/src/__tests__/keychain.test.ts +0 -286
- package/src/cli/core-commands.ts +0 -899
- package/src/security/keychain-to-encrypted-migration.ts +0 -66
- package/src/security/keychain.ts +0 -490
package/src/cli/email.ts
CHANGED
|
@@ -68,6 +68,31 @@ export function registerEmailCommand(program: Command): void {
|
|
|
68
68
|
.description("Email operations (provider-agnostic)")
|
|
69
69
|
.option("--json", "Machine-readable JSON output");
|
|
70
70
|
|
|
71
|
+
email.addHelpText(
|
|
72
|
+
"after",
|
|
73
|
+
`
|
|
74
|
+
Email commands are provider-agnostic — the same CLI works regardless of
|
|
75
|
+
the configured email provider (e.g. agentmail, resend). Use "email provider"
|
|
76
|
+
to switch between providers.
|
|
77
|
+
|
|
78
|
+
Outbound emails follow a draft-based sending model:
|
|
79
|
+
1. Create a draft with "email draft create"
|
|
80
|
+
2. Approve and send with "email draft approve-send"
|
|
81
|
+
3. Optionally reject with "email draft reject"
|
|
82
|
+
|
|
83
|
+
Guardrails (outbound pause, daily send cap, address allow/block rules) are
|
|
84
|
+
enforced at send time. If a guardrail blocks sending, exit code 2 is returned.
|
|
85
|
+
|
|
86
|
+
Exit codes: 0 = success, 1 = error, 2 = guardrail blocked.
|
|
87
|
+
|
|
88
|
+
Examples:
|
|
89
|
+
$ vellum email status
|
|
90
|
+
$ vellum email draft create --from hello@example.com --to user@test.com --subject "Hello" --body "Hi there"
|
|
91
|
+
$ vellum email draft approve-send --draft-id d_abc123 --confirm
|
|
92
|
+
$ vellum email guardrails set --daily-cap 50
|
|
93
|
+
$ vellum email setup domain --domain example.com`,
|
|
94
|
+
);
|
|
95
|
+
|
|
71
96
|
const svc = getEmailService();
|
|
72
97
|
|
|
73
98
|
// =========================================================================
|
|
@@ -77,9 +102,32 @@ export function registerEmailCommand(program: Command): void {
|
|
|
77
102
|
.command("provider")
|
|
78
103
|
.description("Manage email provider");
|
|
79
104
|
|
|
105
|
+
provider.addHelpText(
|
|
106
|
+
"after",
|
|
107
|
+
`
|
|
108
|
+
Switch between email providers without changing the rest of your email
|
|
109
|
+
configuration. The active provider determines which backend handles domain
|
|
110
|
+
setup, inbox creation, DNS records, and message delivery.
|
|
111
|
+
|
|
112
|
+
Examples:
|
|
113
|
+
$ vellum email provider get
|
|
114
|
+
$ vellum email provider set agentmail`,
|
|
115
|
+
);
|
|
116
|
+
|
|
80
117
|
provider
|
|
81
118
|
.command("get")
|
|
82
119
|
.description("Show the active email provider")
|
|
120
|
+
.addHelpText(
|
|
121
|
+
"after",
|
|
122
|
+
`
|
|
123
|
+
Returns the name of the currently active email provider (e.g. agentmail,
|
|
124
|
+
resend). Use this to confirm which backend is handling email operations
|
|
125
|
+
before making changes.
|
|
126
|
+
|
|
127
|
+
Examples:
|
|
128
|
+
$ vellum email provider get
|
|
129
|
+
$ vellum email provider get --json`,
|
|
130
|
+
)
|
|
83
131
|
.action((_opts: unknown, cmd: Command) => {
|
|
84
132
|
output({ ok: true, provider: svc.getProviderName() }, getJson(cmd));
|
|
85
133
|
});
|
|
@@ -89,6 +137,18 @@ export function registerEmailCommand(program: Command): void {
|
|
|
89
137
|
.description(
|
|
90
138
|
`Set the active email provider (${SUPPORTED_PROVIDERS.join(", ")})`,
|
|
91
139
|
)
|
|
140
|
+
.addHelpText(
|
|
141
|
+
"after",
|
|
142
|
+
`
|
|
143
|
+
Arguments:
|
|
144
|
+
provider The email provider to activate. Supported values: ${SUPPORTED_PROVIDERS.join(", ")}
|
|
145
|
+
|
|
146
|
+
Persists the provider selection to config. All subsequent email commands
|
|
147
|
+
(setup, inbox, draft, guardrails) will route through the selected provider.
|
|
148
|
+
|
|
149
|
+
Examples:
|
|
150
|
+
$ vellum email provider set agentmail`,
|
|
151
|
+
)
|
|
92
152
|
.action((name: string, _opts: unknown, cmd: Command) => {
|
|
93
153
|
if (!SUPPORTED_PROVIDERS.includes(name as SupportedProvider)) {
|
|
94
154
|
exitError(
|
|
@@ -106,6 +166,19 @@ export function registerEmailCommand(program: Command): void {
|
|
|
106
166
|
email
|
|
107
167
|
.command("status")
|
|
108
168
|
.description("Show provider health, inboxes, and guardrail state")
|
|
169
|
+
.addHelpText(
|
|
170
|
+
"after",
|
|
171
|
+
`
|
|
172
|
+
Returns a combined view of the email subsystem: active provider and its health
|
|
173
|
+
status, configured inboxes with their addresses, and current guardrail state
|
|
174
|
+
(paused flag, daily send cap, today's send count).
|
|
175
|
+
|
|
176
|
+
Use this to verify the email stack is fully configured before sending.
|
|
177
|
+
|
|
178
|
+
Examples:
|
|
179
|
+
$ vellum email status
|
|
180
|
+
$ vellum email status --json`,
|
|
181
|
+
)
|
|
109
182
|
.action(async (_opts: unknown, cmd: Command) => {
|
|
110
183
|
await run(cmd, async () => {
|
|
111
184
|
const status = await svc.status();
|
|
@@ -120,11 +193,42 @@ export function registerEmailCommand(program: Command): void {
|
|
|
120
193
|
.command("setup")
|
|
121
194
|
.description("Domain, inbox, and webhook setup");
|
|
122
195
|
|
|
196
|
+
setup.addHelpText(
|
|
197
|
+
"after",
|
|
198
|
+
`
|
|
199
|
+
The setup workflow configures the email stack in order:
|
|
200
|
+
1. domain — Register a sending domain with the provider
|
|
201
|
+
2. dns — Retrieve SPF, DKIM, and DMARC records to add to your DNS
|
|
202
|
+
3. verify — Check that DNS records have propagated and are valid
|
|
203
|
+
4. inboxes — Create standard inboxes (hello@, support@, ops@)
|
|
204
|
+
5. webhook — Register an inbound webhook for receiving email
|
|
205
|
+
|
|
206
|
+
Run each step in sequence. "verify" will fail until DNS records propagate.
|
|
207
|
+
|
|
208
|
+
Examples:
|
|
209
|
+
$ vellum email setup domain --domain example.com
|
|
210
|
+
$ vellum email setup dns --domain example.com
|
|
211
|
+
$ vellum email setup verify --domain example.com`,
|
|
212
|
+
);
|
|
213
|
+
|
|
123
214
|
setup
|
|
124
215
|
.command("domain")
|
|
125
216
|
.description("Create/register a domain")
|
|
126
217
|
.requiredOption("--domain <domain>", "Domain name")
|
|
127
218
|
.option("--dry-run", "Preview without creating")
|
|
219
|
+
.addHelpText(
|
|
220
|
+
"after",
|
|
221
|
+
`
|
|
222
|
+
Registers a sending domain with the active email provider. The domain must
|
|
223
|
+
be a valid, publicly resolvable domain you control.
|
|
224
|
+
|
|
225
|
+
Use --dry-run to preview the registration without creating it. This returns
|
|
226
|
+
the DNS records that would need to be configured without committing changes.
|
|
227
|
+
|
|
228
|
+
Examples:
|
|
229
|
+
$ vellum email setup domain --domain example.com
|
|
230
|
+
$ vellum email setup domain --domain example.com --dry-run`,
|
|
231
|
+
)
|
|
128
232
|
.action(
|
|
129
233
|
async (opts: { domain: string; dryRun?: boolean }, cmd: Command) => {
|
|
130
234
|
await run(cmd, async () => {
|
|
@@ -138,6 +242,19 @@ export function registerEmailCommand(program: Command): void {
|
|
|
138
242
|
.command("dns")
|
|
139
243
|
.description("Get DNS records (SPF/DKIM/DMARC) for a domain")
|
|
140
244
|
.requiredOption("--domain <domain>", "Domain name")
|
|
245
|
+
.addHelpText(
|
|
246
|
+
"after",
|
|
247
|
+
`
|
|
248
|
+
Returns the SPF, DKIM, and DMARC DNS records that must be added to your
|
|
249
|
+
domain's DNS zone. These records authorize the email provider to send on
|
|
250
|
+
behalf of your domain and improve deliverability.
|
|
251
|
+
|
|
252
|
+
Add all returned records to your DNS provider before running "setup verify".
|
|
253
|
+
|
|
254
|
+
Examples:
|
|
255
|
+
$ vellum email setup dns --domain example.com
|
|
256
|
+
$ vellum email setup dns --domain example.com --json`,
|
|
257
|
+
)
|
|
141
258
|
.action(async (opts: { domain: string }, cmd: Command) => {
|
|
142
259
|
await run(cmd, async () => {
|
|
143
260
|
const records = await svc.getDomainDnsRecords(opts.domain);
|
|
@@ -149,6 +266,19 @@ export function registerEmailCommand(program: Command): void {
|
|
|
149
266
|
.command("verify")
|
|
150
267
|
.description("Verify domain after DNS is configured")
|
|
151
268
|
.requiredOption("--domain <domain>", "Domain name")
|
|
269
|
+
.addHelpText(
|
|
270
|
+
"after",
|
|
271
|
+
`
|
|
272
|
+
Checks that the required DNS records (SPF, DKIM, DMARC) have propagated and
|
|
273
|
+
are correctly configured. DNS propagation can take minutes to hours depending
|
|
274
|
+
on your DNS provider's TTL settings.
|
|
275
|
+
|
|
276
|
+
Run this after adding all records returned by "setup dns". If verification
|
|
277
|
+
fails, wait for propagation and retry.
|
|
278
|
+
|
|
279
|
+
Examples:
|
|
280
|
+
$ vellum email setup verify --domain example.com`,
|
|
281
|
+
)
|
|
152
282
|
.action(async (opts: { domain: string }, cmd: Command) => {
|
|
153
283
|
await run(cmd, async () => {
|
|
154
284
|
const domain = await svc.verifyDomain(opts.domain);
|
|
@@ -160,6 +290,17 @@ export function registerEmailCommand(program: Command): void {
|
|
|
160
290
|
.command("inboxes")
|
|
161
291
|
.description("Create standard inboxes (hello@, support@, ops@)")
|
|
162
292
|
.requiredOption("--domain <domain>", "Domain name")
|
|
293
|
+
.addHelpText(
|
|
294
|
+
"after",
|
|
295
|
+
`
|
|
296
|
+
Creates the standard set of inboxes (hello@, support@, ops@) on the
|
|
297
|
+
specified domain. Idempotent — re-running skips already existing inboxes.
|
|
298
|
+
|
|
299
|
+
The domain must be registered and verified before creating inboxes.
|
|
300
|
+
|
|
301
|
+
Examples:
|
|
302
|
+
$ vellum email setup inboxes --domain example.com`,
|
|
303
|
+
)
|
|
163
304
|
.action(async (opts: { domain: string }, cmd: Command) => {
|
|
164
305
|
await run(cmd, async () => {
|
|
165
306
|
const inboxes = await svc.ensureInboxes(opts.domain);
|
|
@@ -172,6 +313,20 @@ export function registerEmailCommand(program: Command): void {
|
|
|
172
313
|
.description("Register inbound webhook")
|
|
173
314
|
.requiredOption("--url <url>", "Webhook URL")
|
|
174
315
|
.option("--secret <secret>", "Webhook signing secret")
|
|
316
|
+
.addHelpText(
|
|
317
|
+
"after",
|
|
318
|
+
`
|
|
319
|
+
Registers a webhook URL with the email provider to receive inbound messages.
|
|
320
|
+
The provider will POST incoming emails to this URL as JSON payloads.
|
|
321
|
+
|
|
322
|
+
If --secret is provided, the provider signs each webhook payload with the
|
|
323
|
+
secret so you can verify authenticity. If omitted, the provider may generate
|
|
324
|
+
one automatically (provider-dependent).
|
|
325
|
+
|
|
326
|
+
Examples:
|
|
327
|
+
$ vellum email setup webhook --url https://example.com/api/email/inbound
|
|
328
|
+
$ vellum email setup webhook --url https://example.com/api/email/inbound --secret whsec_abc123`,
|
|
329
|
+
)
|
|
175
330
|
.action(async (opts: { url: string; secret?: string }, cmd: Command) => {
|
|
176
331
|
await run(cmd, async () => {
|
|
177
332
|
const webhook = await svc.setupWebhook(opts.url, opts.secret);
|
|
@@ -184,6 +339,18 @@ export function registerEmailCommand(program: Command): void {
|
|
|
184
339
|
// =========================================================================
|
|
185
340
|
const inbox = email.command("inbox").description("Manage inboxes");
|
|
186
341
|
|
|
342
|
+
inbox.addHelpText(
|
|
343
|
+
"after",
|
|
344
|
+
`
|
|
345
|
+
Inboxes are email addresses that can send and receive messages through the
|
|
346
|
+
configured provider. Each inbox has a username (local part), domain, and
|
|
347
|
+
optional display name.
|
|
348
|
+
|
|
349
|
+
Examples:
|
|
350
|
+
$ vellum email inbox list
|
|
351
|
+
$ vellum email inbox create --username sam --domain example.com --display-name "Samwise"`,
|
|
352
|
+
);
|
|
353
|
+
|
|
187
354
|
inbox
|
|
188
355
|
.command("create")
|
|
189
356
|
.description("Create a new inbox")
|
|
@@ -193,6 +360,20 @@ export function registerEmailCommand(program: Command): void {
|
|
|
193
360
|
'Domain (e.g. "agentmail.to"). Omit for provider default.',
|
|
194
361
|
)
|
|
195
362
|
.option("--display-name <name>", 'Display name (e.g. "Samwise")')
|
|
363
|
+
.addHelpText(
|
|
364
|
+
"after",
|
|
365
|
+
`
|
|
366
|
+
Creates a new inbox with the given username (local part) on the specified
|
|
367
|
+
domain. If --domain is omitted, the provider's default domain is used.
|
|
368
|
+
|
|
369
|
+
The --display-name sets the friendly name shown in the "From" header
|
|
370
|
+
(e.g. "Samwise <sam@example.com>").
|
|
371
|
+
|
|
372
|
+
Examples:
|
|
373
|
+
$ vellum email inbox create --username sam --domain example.com
|
|
374
|
+
$ vellum email inbox create --username support --domain example.com --display-name "Support Team"
|
|
375
|
+
$ vellum email inbox create --username hello`,
|
|
376
|
+
)
|
|
196
377
|
.action(
|
|
197
378
|
async (
|
|
198
379
|
opts: { username: string; domain?: string; displayName?: string },
|
|
@@ -212,6 +393,19 @@ export function registerEmailCommand(program: Command): void {
|
|
|
212
393
|
inbox
|
|
213
394
|
.command("list")
|
|
214
395
|
.description("List all inboxes")
|
|
396
|
+
.addHelpText(
|
|
397
|
+
"after",
|
|
398
|
+
`
|
|
399
|
+
Lists all inboxes configured on the active email provider. Each inbox
|
|
400
|
+
entry includes its address, display name, and inbox ID.
|
|
401
|
+
|
|
402
|
+
Use this to verify which inboxes are available before creating drafts or
|
|
403
|
+
configuring inbound webhooks.
|
|
404
|
+
|
|
405
|
+
Examples:
|
|
406
|
+
$ vellum email inbox list
|
|
407
|
+
$ vellum email inbox list --json`,
|
|
408
|
+
)
|
|
215
409
|
.action(async (_opts: unknown, cmd: Command) => {
|
|
216
410
|
await run(cmd, async () => {
|
|
217
411
|
const inboxes = await svc.listInboxes();
|
|
@@ -224,6 +418,24 @@ export function registerEmailCommand(program: Command): void {
|
|
|
224
418
|
// =========================================================================
|
|
225
419
|
const draft = email.command("draft").description("Manage email drafts");
|
|
226
420
|
|
|
421
|
+
draft.addHelpText(
|
|
422
|
+
"after",
|
|
423
|
+
`
|
|
424
|
+
Drafts follow a lifecycle: create -> approve-send or reject.
|
|
425
|
+
|
|
426
|
+
1. "draft create" stages an outbound email without sending it
|
|
427
|
+
2. "draft approve-send" runs guardrail checks and sends if allowed
|
|
428
|
+
3. "draft reject" permanently deletes a draft (will not be sent)
|
|
429
|
+
|
|
430
|
+
Drafts can also be listed, inspected by ID, or deleted. Use "draft list
|
|
431
|
+
--status pending" to see drafts awaiting approval.
|
|
432
|
+
|
|
433
|
+
Examples:
|
|
434
|
+
$ vellum email draft create --from hello@example.com --to user@test.com --subject "Hi" --body "Hello"
|
|
435
|
+
$ vellum email draft list --status pending
|
|
436
|
+
$ vellum email draft approve-send --draft-id d_abc123 --confirm`,
|
|
437
|
+
);
|
|
438
|
+
|
|
227
439
|
draft
|
|
228
440
|
.command("create")
|
|
229
441
|
.description("Create a new draft")
|
|
@@ -233,6 +445,27 @@ export function registerEmailCommand(program: Command): void {
|
|
|
233
445
|
.requiredOption("--body <body>", "Email body (plain text)")
|
|
234
446
|
.option("--cc <address>", "CC address")
|
|
235
447
|
.option("--in-reply-to <messageId>", "Message ID to reply to")
|
|
448
|
+
.addHelpText(
|
|
449
|
+
"after",
|
|
450
|
+
`
|
|
451
|
+
Creates an outbound email draft without sending it. The draft must be
|
|
452
|
+
explicitly approved via "draft approve-send" before it is delivered.
|
|
453
|
+
|
|
454
|
+
Required fields:
|
|
455
|
+
--from Sender address (must match a configured inbox) or inbox ID
|
|
456
|
+
--to Recipient email address
|
|
457
|
+
--subject Email subject line
|
|
458
|
+
--body Email body in plain text
|
|
459
|
+
|
|
460
|
+
Optional fields:
|
|
461
|
+
--cc CC recipient address
|
|
462
|
+
--in-reply-to Message ID of the email being replied to (for threading)
|
|
463
|
+
|
|
464
|
+
Examples:
|
|
465
|
+
$ vellum email draft create --from hello@example.com --to user@test.com --subject "Hello" --body "Hi there"
|
|
466
|
+
$ vellum email draft create --from hello@example.com --to user@test.com --subject "Re: Question" --body "Sure thing" --in-reply-to msg_abc123
|
|
467
|
+
$ vellum email draft create --from hello@example.com --to user@test.com --subject "Update" --body "FYI" --cc manager@test.com`,
|
|
468
|
+
)
|
|
236
469
|
.action(
|
|
237
470
|
async (
|
|
238
471
|
opts: {
|
|
@@ -259,6 +492,27 @@ export function registerEmailCommand(program: Command): void {
|
|
|
259
492
|
"--status <status>",
|
|
260
493
|
"Filter by status (pending|approved|sent|rejected)",
|
|
261
494
|
)
|
|
495
|
+
.addHelpText(
|
|
496
|
+
"after",
|
|
497
|
+
`
|
|
498
|
+
Lists all email drafts, optionally filtered by status. Returns an array
|
|
499
|
+
of draft objects with their IDs, recipients, subjects, and current status.
|
|
500
|
+
|
|
501
|
+
Use --status to narrow results to a specific lifecycle stage:
|
|
502
|
+
pending — created but not yet approved
|
|
503
|
+
approved — approved and queued for sending
|
|
504
|
+
sent — successfully delivered
|
|
505
|
+
rejected — delivery failed by the provider (e.g. bounce or send error)
|
|
506
|
+
|
|
507
|
+
Note: drafts rejected via "draft reject" are permanently deleted and will
|
|
508
|
+
not appear here. The "rejected" status only applies to provider-side
|
|
509
|
+
delivery failures.
|
|
510
|
+
|
|
511
|
+
Examples:
|
|
512
|
+
$ vellum email draft list
|
|
513
|
+
$ vellum email draft list --status pending
|
|
514
|
+
$ vellum email draft list --status sent --json`,
|
|
515
|
+
)
|
|
262
516
|
.action(async (opts: { status?: string }, cmd: Command) => {
|
|
263
517
|
await run(cmd, async () => {
|
|
264
518
|
const drafts = await svc.listDrafts(opts.status);
|
|
@@ -270,6 +524,21 @@ export function registerEmailCommand(program: Command): void {
|
|
|
270
524
|
.command("get <draftId>")
|
|
271
525
|
.description("Get a draft by ID")
|
|
272
526
|
.option("--inbox <id>", "Inbox ID (for multi-inbox setups)")
|
|
527
|
+
.addHelpText(
|
|
528
|
+
"after",
|
|
529
|
+
`
|
|
530
|
+
Arguments:
|
|
531
|
+
draftId The ID of the draft to retrieve (e.g. d_abc123)
|
|
532
|
+
|
|
533
|
+
Returns the full draft object including sender, recipient, subject, body,
|
|
534
|
+
status, and timestamps. Use --inbox to scope the lookup in multi-inbox
|
|
535
|
+
setups.
|
|
536
|
+
|
|
537
|
+
Examples:
|
|
538
|
+
$ vellum email draft get d_abc123
|
|
539
|
+
$ vellum email draft get d_abc123 --inbox inbox_456
|
|
540
|
+
$ vellum email draft get d_abc123 --json`,
|
|
541
|
+
)
|
|
273
542
|
.action(async (draftId: string, opts: { inbox?: string }, cmd: Command) => {
|
|
274
543
|
await run(cmd, async () => {
|
|
275
544
|
const d = await svc.getDraft(draftId, opts.inbox);
|
|
@@ -285,6 +554,23 @@ export function registerEmailCommand(program: Command): void {
|
|
|
285
554
|
.requiredOption("--draft-id <id>", "Draft ID to send")
|
|
286
555
|
.option("--inbox <id>", "Inbox ID (for multi-inbox setups)")
|
|
287
556
|
.requiredOption("--confirm", "Explicit confirmation flag (required)")
|
|
557
|
+
.addHelpText(
|
|
558
|
+
"after",
|
|
559
|
+
`
|
|
560
|
+
Runs guardrail checks (outbound pause, daily send cap, address block/allow
|
|
561
|
+
rules) and sends the draft if all checks pass. The --confirm flag is required
|
|
562
|
+
as an explicit safety gate.
|
|
563
|
+
|
|
564
|
+
If a guardrail blocks the send, the command exits with code 2 and returns
|
|
565
|
+
the guardrail error details in the response JSON. The draft remains in
|
|
566
|
+
pending state and can be retried after adjusting guardrails.
|
|
567
|
+
|
|
568
|
+
Aliases: "draft send", "draft approve"
|
|
569
|
+
|
|
570
|
+
Examples:
|
|
571
|
+
$ vellum email draft approve-send --draft-id d_abc123 --confirm
|
|
572
|
+
$ vellum email draft approve-send --draft-id d_abc123 --confirm --json`,
|
|
573
|
+
)
|
|
288
574
|
.action(
|
|
289
575
|
async (
|
|
290
576
|
opts: { draftId: string; inbox?: string; confirm: boolean },
|
|
@@ -311,6 +597,18 @@ export function registerEmailCommand(program: Command): void {
|
|
|
311
597
|
.requiredOption("--draft-id <id>", "Draft ID to reject")
|
|
312
598
|
.option("--inbox <id>", "Inbox ID (for multi-inbox setups)")
|
|
313
599
|
.option("--reason <text>", "Reason for rejection")
|
|
600
|
+
.addHelpText(
|
|
601
|
+
"after",
|
|
602
|
+
`
|
|
603
|
+
Rejects a pending draft by permanently deleting it so it will not be
|
|
604
|
+
sent. The --reason flag is accepted for logging but the draft itself is
|
|
605
|
+
removed from the provider and cannot be recovered.
|
|
606
|
+
|
|
607
|
+
Examples:
|
|
608
|
+
$ vellum email draft reject --draft-id d_abc123
|
|
609
|
+
$ vellum email draft reject --draft-id d_abc123 --reason "Wrong recipient"
|
|
610
|
+
$ vellum email draft reject --draft-id d_abc123 --inbox inbox_456`,
|
|
611
|
+
)
|
|
314
612
|
.action(
|
|
315
613
|
async (
|
|
316
614
|
opts: { draftId: string; inbox?: string; reason?: string },
|
|
@@ -327,6 +625,20 @@ export function registerEmailCommand(program: Command): void {
|
|
|
327
625
|
.command("delete <draftId>")
|
|
328
626
|
.description("Delete a draft")
|
|
329
627
|
.option("--inbox <id>", "Inbox ID (for multi-inbox setups)")
|
|
628
|
+
.addHelpText(
|
|
629
|
+
"after",
|
|
630
|
+
`
|
|
631
|
+
Arguments:
|
|
632
|
+
draftId The ID of the draft to delete (e.g. d_abc123)
|
|
633
|
+
|
|
634
|
+
Permanently removes a draft from the system. Both "reject" and "delete"
|
|
635
|
+
result in permanent deletion; use "reject" when you want to log a reason
|
|
636
|
+
for not sending. Use --inbox to scope the deletion in multi-inbox setups.
|
|
637
|
+
|
|
638
|
+
Examples:
|
|
639
|
+
$ vellum email draft delete d_abc123
|
|
640
|
+
$ vellum email draft delete d_abc123 --inbox inbox_456`,
|
|
641
|
+
)
|
|
330
642
|
.action(async (draftId: string, opts: { inbox?: string }, cmd: Command) => {
|
|
331
643
|
await run(cmd, async () => {
|
|
332
644
|
await svc.deleteDraft(draftId, opts.inbox);
|
|
@@ -339,11 +651,39 @@ export function registerEmailCommand(program: Command): void {
|
|
|
339
651
|
// =========================================================================
|
|
340
652
|
const inbound = email.command("inbound").description("View inbound messages");
|
|
341
653
|
|
|
654
|
+
inbound.addHelpText(
|
|
655
|
+
"after",
|
|
656
|
+
`
|
|
657
|
+
View messages received by your inboxes. Inbound messages are emails sent
|
|
658
|
+
to your configured inbox addresses by external senders.
|
|
659
|
+
|
|
660
|
+
Use "inbound list" to browse received messages and "inbound get" to
|
|
661
|
+
retrieve the full content of a specific message.
|
|
662
|
+
|
|
663
|
+
Examples:
|
|
664
|
+
$ vellum email inbound list
|
|
665
|
+
$ vellum email inbound list --thread-id thr_abc123
|
|
666
|
+
$ vellum email inbound get msg_def456`,
|
|
667
|
+
);
|
|
668
|
+
|
|
342
669
|
inbound
|
|
343
670
|
.command("list")
|
|
344
671
|
.description("List inbound messages")
|
|
345
672
|
.option("--thread-id <id>", "Filter by thread ID")
|
|
346
673
|
.option("--inbox <id>", "Inbox ID (for multi-inbox setups)")
|
|
674
|
+
.addHelpText(
|
|
675
|
+
"after",
|
|
676
|
+
`
|
|
677
|
+
Lists inbound messages received by your inboxes. Optionally filter by
|
|
678
|
+
thread ID to see only messages belonging to a specific conversation, or
|
|
679
|
+
by inbox ID to scope to a particular inbox.
|
|
680
|
+
|
|
681
|
+
Examples:
|
|
682
|
+
$ vellum email inbound list
|
|
683
|
+
$ vellum email inbound list --thread-id thr_abc123
|
|
684
|
+
$ vellum email inbound list --inbox inbox_456
|
|
685
|
+
$ vellum email inbound list --json`,
|
|
686
|
+
)
|
|
347
687
|
.action(
|
|
348
688
|
async (opts: { threadId?: string; inbox?: string }, cmd: Command) => {
|
|
349
689
|
await run(cmd, async () => {
|
|
@@ -356,6 +696,19 @@ export function registerEmailCommand(program: Command): void {
|
|
|
356
696
|
inbound
|
|
357
697
|
.command("get <messageId>")
|
|
358
698
|
.description("Get a specific inbound message")
|
|
699
|
+
.addHelpText(
|
|
700
|
+
"after",
|
|
701
|
+
`
|
|
702
|
+
Arguments:
|
|
703
|
+
messageId The ID of the inbound message to retrieve (e.g. msg_abc123)
|
|
704
|
+
|
|
705
|
+
Returns the full inbound message including sender, recipients, subject,
|
|
706
|
+
body, headers, and timestamps.
|
|
707
|
+
|
|
708
|
+
Examples:
|
|
709
|
+
$ vellum email inbound get msg_abc123
|
|
710
|
+
$ vellum email inbound get msg_abc123 --json`,
|
|
711
|
+
)
|
|
359
712
|
.action(async (messageId: string, _opts: unknown, cmd: Command) => {
|
|
360
713
|
await run(cmd, async () => {
|
|
361
714
|
const message = await svc.getMessage(messageId);
|
|
@@ -368,9 +721,33 @@ export function registerEmailCommand(program: Command): void {
|
|
|
368
721
|
// =========================================================================
|
|
369
722
|
const thread = email.command("thread").description("View email threads");
|
|
370
723
|
|
|
724
|
+
thread.addHelpText(
|
|
725
|
+
"after",
|
|
726
|
+
`
|
|
727
|
+
Threads group related emails (original message and replies) into a single
|
|
728
|
+
conversation. Each thread has a unique ID and contains one or more messages.
|
|
729
|
+
|
|
730
|
+
Use "thread list" to browse all threads and "thread get" to retrieve
|
|
731
|
+
the full conversation history for a specific thread.
|
|
732
|
+
|
|
733
|
+
Examples:
|
|
734
|
+
$ vellum email thread list
|
|
735
|
+
$ vellum email thread get thr_abc123`,
|
|
736
|
+
);
|
|
737
|
+
|
|
371
738
|
thread
|
|
372
739
|
.command("list")
|
|
373
740
|
.description("List threads")
|
|
741
|
+
.addHelpText(
|
|
742
|
+
"after",
|
|
743
|
+
`
|
|
744
|
+
Lists all email threads. Each thread entry includes its ID, subject,
|
|
745
|
+
participant addresses, message count, and timestamps.
|
|
746
|
+
|
|
747
|
+
Examples:
|
|
748
|
+
$ vellum email thread list
|
|
749
|
+
$ vellum email thread list --json`,
|
|
750
|
+
)
|
|
374
751
|
.action(async (_opts: unknown, cmd: Command) => {
|
|
375
752
|
await run(cmd, async () => {
|
|
376
753
|
const threads = await svc.listThreads();
|
|
@@ -381,6 +758,19 @@ export function registerEmailCommand(program: Command): void {
|
|
|
381
758
|
thread
|
|
382
759
|
.command("get <threadId>")
|
|
383
760
|
.description("Get a specific thread")
|
|
761
|
+
.addHelpText(
|
|
762
|
+
"after",
|
|
763
|
+
`
|
|
764
|
+
Arguments:
|
|
765
|
+
threadId The ID of the thread to retrieve (e.g. thr_abc123)
|
|
766
|
+
|
|
767
|
+
Returns the full thread including all messages (inbound and outbound),
|
|
768
|
+
participants, subject, and timestamps. Messages are ordered chronologically.
|
|
769
|
+
|
|
770
|
+
Examples:
|
|
771
|
+
$ vellum email thread get thr_abc123
|
|
772
|
+
$ vellum email thread get thr_abc123 --json`,
|
|
773
|
+
)
|
|
384
774
|
.action(async (threadId: string, _opts: unknown, cmd: Command) => {
|
|
385
775
|
await run(cmd, async () => {
|
|
386
776
|
const t = await svc.getThread(threadId);
|
|
@@ -395,9 +785,41 @@ export function registerEmailCommand(program: Command): void {
|
|
|
395
785
|
.command("guardrails")
|
|
396
786
|
.description("Manage email guardrails");
|
|
397
787
|
|
|
788
|
+
guardrails.addHelpText(
|
|
789
|
+
"after",
|
|
790
|
+
`
|
|
791
|
+
Guardrails are safety controls enforced at send time (during "draft
|
|
792
|
+
approve-send"). Three types of guardrails exist:
|
|
793
|
+
|
|
794
|
+
1. Outbound pause — when paused=true, all sends are blocked
|
|
795
|
+
2. Daily send cap — limits the total number of emails sent per day
|
|
796
|
+
3. Address rules — block or allow patterns (e.g. *@spam.com)
|
|
797
|
+
|
|
798
|
+
When a guardrail blocks a send, exit code 2 is returned with the specific
|
|
799
|
+
guardrail error in the response.
|
|
800
|
+
|
|
801
|
+
Examples:
|
|
802
|
+
$ vellum email guardrails get
|
|
803
|
+
$ vellum email guardrails set --paused true
|
|
804
|
+
$ vellum email guardrails set --daily-cap 100
|
|
805
|
+
$ vellum email guardrails block "*@spam.com"`,
|
|
806
|
+
);
|
|
807
|
+
|
|
398
808
|
guardrails
|
|
399
809
|
.command("get")
|
|
400
810
|
.description("Show current guardrail settings")
|
|
811
|
+
.addHelpText(
|
|
812
|
+
"after",
|
|
813
|
+
`
|
|
814
|
+
Returns the current guardrail configuration: outbound pause state,
|
|
815
|
+
daily send cap, today's send count, and a summary of address rules.
|
|
816
|
+
|
|
817
|
+
Use this to verify guardrail settings before sending emails.
|
|
818
|
+
|
|
819
|
+
Examples:
|
|
820
|
+
$ vellum email guardrails get
|
|
821
|
+
$ vellum email guardrails get --json`,
|
|
822
|
+
)
|
|
401
823
|
.action((_opts: unknown, cmd: Command) => {
|
|
402
824
|
output({ ok: true, ...svc.getGuardrails() }, getJson(cmd));
|
|
403
825
|
});
|
|
@@ -407,6 +829,22 @@ export function registerEmailCommand(program: Command): void {
|
|
|
407
829
|
.description("Update guardrail settings")
|
|
408
830
|
.option("--paused <value>", "Pause outbound (true/false)")
|
|
409
831
|
.option("--daily-cap <n>", "Daily send cap")
|
|
832
|
+
.addHelpText(
|
|
833
|
+
"after",
|
|
834
|
+
`
|
|
835
|
+
Updates one or both guardrail settings. Omitted flags leave the existing
|
|
836
|
+
value unchanged.
|
|
837
|
+
|
|
838
|
+
--paused true/false Enable or disable the outbound pause. When paused,
|
|
839
|
+
all "approve-send" calls are blocked with exit code 2.
|
|
840
|
+
--daily-cap <n> Set the maximum number of emails that can be sent per
|
|
841
|
+
calendar day. Set to 0 to disable sending entirely.
|
|
842
|
+
|
|
843
|
+
Examples:
|
|
844
|
+
$ vellum email guardrails set --paused true
|
|
845
|
+
$ vellum email guardrails set --paused false --daily-cap 50
|
|
846
|
+
$ vellum email guardrails set --daily-cap 0`,
|
|
847
|
+
)
|
|
410
848
|
.action((opts: { paused?: string; dailyCap?: string }, cmd: Command) => {
|
|
411
849
|
const updates: { paused?: boolean; dailyCap?: number } = {};
|
|
412
850
|
if (opts.paused !== undefined) {
|
|
@@ -427,6 +865,21 @@ export function registerEmailCommand(program: Command): void {
|
|
|
427
865
|
guardrails
|
|
428
866
|
.command("block <pattern>")
|
|
429
867
|
.description("Block addresses matching pattern (e.g., *@spam.com)")
|
|
868
|
+
.addHelpText(
|
|
869
|
+
"after",
|
|
870
|
+
`
|
|
871
|
+
Arguments:
|
|
872
|
+
pattern Glob pattern matching email addresses to block. Supports * as
|
|
873
|
+
wildcard. Examples: "*@spam.com", "user@*", "*@*.example.com"
|
|
874
|
+
|
|
875
|
+
Creates a block rule. Any "approve-send" to a recipient matching this
|
|
876
|
+
pattern will be rejected with exit code 2. Rules are evaluated in order;
|
|
877
|
+
block rules take precedence over allow rules for the same address.
|
|
878
|
+
|
|
879
|
+
Examples:
|
|
880
|
+
$ vellum email guardrails block "*@spam.com"
|
|
881
|
+
$ vellum email guardrails block "marketing@*"`,
|
|
882
|
+
)
|
|
430
883
|
.action((pattern: string, _opts: unknown, cmd: Command) => {
|
|
431
884
|
const rule = svc.addRule("block", pattern);
|
|
432
885
|
output({ ok: true, rule }, getJson(cmd));
|
|
@@ -435,6 +888,21 @@ export function registerEmailCommand(program: Command): void {
|
|
|
435
888
|
guardrails
|
|
436
889
|
.command("allow <pattern>")
|
|
437
890
|
.description("Allow addresses matching pattern")
|
|
891
|
+
.addHelpText(
|
|
892
|
+
"after",
|
|
893
|
+
`
|
|
894
|
+
Arguments:
|
|
895
|
+
pattern Glob pattern matching email addresses to allow. Supports * as
|
|
896
|
+
wildcard. Examples: "*@partner.com", "vip@*", "*@*.trusted.com"
|
|
897
|
+
|
|
898
|
+
Creates an allow rule. Addresses matching this pattern will pass the
|
|
899
|
+
address-rule guardrail check during "approve-send". Note that block rules
|
|
900
|
+
take precedence over allow rules for the same address.
|
|
901
|
+
|
|
902
|
+
Examples:
|
|
903
|
+
$ vellum email guardrails allow "*@partner.com"
|
|
904
|
+
$ vellum email guardrails allow "vip@example.com"`,
|
|
905
|
+
)
|
|
438
906
|
.action((pattern: string, _opts: unknown, cmd: Command) => {
|
|
439
907
|
const rule = svc.addRule("allow", pattern);
|
|
440
908
|
output({ ok: true, rule }, getJson(cmd));
|
|
@@ -443,6 +911,18 @@ export function registerEmailCommand(program: Command): void {
|
|
|
443
911
|
guardrails
|
|
444
912
|
.command("rules")
|
|
445
913
|
.description("List all address rules")
|
|
914
|
+
.addHelpText(
|
|
915
|
+
"after",
|
|
916
|
+
`
|
|
917
|
+
Lists all configured address rules (both block and allow). Each rule
|
|
918
|
+
entry includes its ID, type (block or allow), and the glob pattern.
|
|
919
|
+
|
|
920
|
+
Use the rule ID with "guardrails unrule" to remove a specific rule.
|
|
921
|
+
|
|
922
|
+
Examples:
|
|
923
|
+
$ vellum email guardrails rules
|
|
924
|
+
$ vellum email guardrails rules --json`,
|
|
925
|
+
)
|
|
446
926
|
.action((_opts: unknown, cmd: Command) => {
|
|
447
927
|
output({ ok: true, rules: svc.listAddressRules() }, getJson(cmd));
|
|
448
928
|
});
|
|
@@ -450,6 +930,20 @@ export function registerEmailCommand(program: Command): void {
|
|
|
450
930
|
guardrails
|
|
451
931
|
.command("unrule <ruleId>")
|
|
452
932
|
.description("Remove an address rule by ID")
|
|
933
|
+
.addHelpText(
|
|
934
|
+
"after",
|
|
935
|
+
`
|
|
936
|
+
Arguments:
|
|
937
|
+
ruleId The ID of the address rule to remove. Use "guardrails rules" to
|
|
938
|
+
list all rules and their IDs.
|
|
939
|
+
|
|
940
|
+
Permanently removes a block or allow rule. The rule takes effect immediately —
|
|
941
|
+
subsequent "approve-send" calls will no longer be affected by the removed rule.
|
|
942
|
+
|
|
943
|
+
Examples:
|
|
944
|
+
$ vellum email guardrails unrule rule_abc123
|
|
945
|
+
$ vellum email guardrails rules # list rules to find the ID first`,
|
|
946
|
+
)
|
|
453
947
|
.action((ruleId: string, _opts: unknown, cmd: Command) => {
|
|
454
948
|
if (svc.removeRule(ruleId)) {
|
|
455
949
|
output({ ok: true, ruleId, action: "removed" }, getJson(cmd));
|