salesprompter-cli 0.1.63 → 0.1.64
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 +11 -2
- package/dist/cli.js +196 -44
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -96,10 +96,17 @@ salesprompter affiliate:launch \
|
|
|
96
96
|
salesprompter affiliate:run \
|
|
97
97
|
--affiliate-link "https://example.com/?ref=you" \
|
|
98
98
|
--linkedin-url "https://www.linkedin.com/sales/search/people?query=..." \
|
|
99
|
-
--max-results 100
|
|
99
|
+
--max-results 100 \
|
|
100
|
+
--steps 3 \
|
|
101
|
+
--timing-mode custom \
|
|
102
|
+
--daily-limit 25 \
|
|
103
|
+
--send-from 09:00 \
|
|
104
|
+
--send-to 16:00
|
|
100
105
|
|
|
101
106
|
# Review the persisted sequence and enrichment summary, then activate explicitly
|
|
107
|
+
salesprompter affiliate:list
|
|
102
108
|
salesprompter affiliate:status "$AFFILIATE_RUN_ID"
|
|
109
|
+
salesprompter affiliate:analytics "$AFFILIATE_RUN_ID"
|
|
103
110
|
salesprompter affiliate:regenerate-sequence "$AFFILIATE_RUN_ID"
|
|
104
111
|
salesprompter affiliate:enrich "$AFFILIATE_RUN_ID"
|
|
105
112
|
salesprompter affiliate:enrich "$AFFILIATE_RUN_ID" \
|
|
@@ -134,7 +141,9 @@ the app's CLI imports view.
|
|
|
134
141
|
- Use your own authorized data access and workspace credentials.
|
|
135
142
|
- Respect provider terms and customer data boundaries.
|
|
136
143
|
- `affiliate:run` uses direct email enrichment first, then Phantombuster Email Finder for unresolved people before creating a draft Instantly campaign.
|
|
137
|
-
- Affiliate preparation
|
|
144
|
+
- Affiliate preparation uses exactly three emails with three observable variants per step; `affiliate:regenerate-sequence` rebuilds and syncs them through the Salesprompter app.
|
|
145
|
+
- `affiliate:list` shows safe workspace summaries. `affiliate:analytics` shows aggregate and step/variant outcomes without exposing lead data; unsupported per-step meeting and won fields render as `n/a`.
|
|
146
|
+
- `affiliate:run` defaults to `--timing-mode custom`, so its daily limit and weekday sending window are honored. Use `--timing-mode auto` only when capacity-derived scheduling is intended.
|
|
138
147
|
- `affiliate:enrich` starts durable email recovery and adds only newly found addresses to an existing campaign. It returns a background-processing status for large audiences instead of blocking the terminal. Pass `--source-audience-run-id` to merge a newer compatible audience into that campaign without creating a duplicate campaign.
|
|
139
148
|
- A draft campaign does not send until `affiliate:activate` is run.
|
|
140
149
|
- The CLI is designed for interactive users and agent-assisted workflows.
|
package/dist/cli.js
CHANGED
|
@@ -166,7 +166,42 @@ const AffiliateCampaignLocalResultSchema = z
|
|
|
166
166
|
})
|
|
167
167
|
.passthrough();
|
|
168
168
|
const DatabaseTimestampSchema = z.string().min(1);
|
|
169
|
-
const
|
|
169
|
+
const AffiliateSequenceVariantSchema = z.object({
|
|
170
|
+
subject: z.string(),
|
|
171
|
+
body: z.string()
|
|
172
|
+
});
|
|
173
|
+
const AffiliateHistoricalSequenceStepSchema = z.object({
|
|
174
|
+
step: z.number().int().positive(),
|
|
175
|
+
subject: z.string(),
|
|
176
|
+
body: z.string(),
|
|
177
|
+
rationale: z.string(),
|
|
178
|
+
delayDays: z.number().int().nonnegative(),
|
|
179
|
+
variants: z.array(AffiliateSequenceVariantSchema).min(1).max(3).optional()
|
|
180
|
+
});
|
|
181
|
+
const AffiliateStrictSequenceStepSchema = AffiliateHistoricalSequenceStepSchema.extend({
|
|
182
|
+
variants: z.array(AffiliateSequenceVariantSchema).length(3)
|
|
183
|
+
});
|
|
184
|
+
const AffiliateStrictSequenceSchema = z.tuple([
|
|
185
|
+
AffiliateStrictSequenceStepSchema.extend({ step: z.literal(1) }),
|
|
186
|
+
AffiliateStrictSequenceStepSchema.extend({ step: z.literal(2) }),
|
|
187
|
+
AffiliateStrictSequenceStepSchema.extend({ step: z.literal(3) })
|
|
188
|
+
]);
|
|
189
|
+
const AffiliateOutreachLeadSchema = z.object({
|
|
190
|
+
rowIndex: z.number().int().nonnegative(),
|
|
191
|
+
fullName: z.string().min(1),
|
|
192
|
+
firstName: z.string().min(1),
|
|
193
|
+
lastName: z.string().min(1),
|
|
194
|
+
jobTitle: z.string().nullable(),
|
|
195
|
+
companyName: z.string().min(1),
|
|
196
|
+
companyDomain: z.string().min(1),
|
|
197
|
+
location: z.string().nullable(),
|
|
198
|
+
profileUrl: z.string().nullable(),
|
|
199
|
+
email: z.string().email(),
|
|
200
|
+
emailScore: z.number().min(0).max(100),
|
|
201
|
+
acceptAll: z.boolean(),
|
|
202
|
+
emailSource: z.enum(["hunter", "phantombuster"]).optional()
|
|
203
|
+
});
|
|
204
|
+
const AffiliateOutreachRunBaseSchema = z.object({
|
|
170
205
|
id: z.string().uuid(),
|
|
171
206
|
audience_run_id: z.string().uuid(),
|
|
172
207
|
organization_id: z.string().min(1),
|
|
@@ -174,36 +209,8 @@ const AffiliateOutreachRunSchema = z.object({
|
|
|
174
209
|
provider: z.literal("instantly"),
|
|
175
210
|
instantly_campaign_id: z.string().min(1).nullable(),
|
|
176
211
|
instantly_campaign_name: z.string().min(1).nullable(),
|
|
177
|
-
sequence: z.array(
|
|
178
|
-
|
|
179
|
-
subject: z.string(),
|
|
180
|
-
body: z.string(),
|
|
181
|
-
rationale: z.string(),
|
|
182
|
-
delayDays: z.number().int().nonnegative(),
|
|
183
|
-
variants: z
|
|
184
|
-
.array(z.object({
|
|
185
|
-
subject: z.string(),
|
|
186
|
-
body: z.string()
|
|
187
|
-
}))
|
|
188
|
-
.min(1)
|
|
189
|
-
.max(3)
|
|
190
|
-
.optional()
|
|
191
|
-
})),
|
|
192
|
-
enriched_leads: z.array(z.object({
|
|
193
|
-
rowIndex: z.number().int().nonnegative(),
|
|
194
|
-
fullName: z.string().min(1),
|
|
195
|
-
firstName: z.string().min(1),
|
|
196
|
-
lastName: z.string().min(1),
|
|
197
|
-
jobTitle: z.string().nullable(),
|
|
198
|
-
companyName: z.string().min(1),
|
|
199
|
-
companyDomain: z.string().min(1),
|
|
200
|
-
location: z.string().nullable(),
|
|
201
|
-
profileUrl: z.string().nullable(),
|
|
202
|
-
email: z.string().email(),
|
|
203
|
-
emailScore: z.number().min(0).max(100),
|
|
204
|
-
acceptAll: z.boolean(),
|
|
205
|
-
emailSource: z.enum(["hunter", "phantombuster"]).optional()
|
|
206
|
-
})),
|
|
212
|
+
sequence: z.array(AffiliateHistoricalSequenceStepSchema),
|
|
213
|
+
enriched_leads: z.array(AffiliateOutreachLeadSchema),
|
|
207
214
|
stats: z.record(z.string(), z.unknown()),
|
|
208
215
|
settings: z.record(z.string(), z.unknown()),
|
|
209
216
|
error_message: z.string().nullable(),
|
|
@@ -211,10 +218,85 @@ const AffiliateOutreachRunSchema = z.object({
|
|
|
211
218
|
created_at: DatabaseTimestampSchema,
|
|
212
219
|
updated_at: DatabaseTimestampSchema
|
|
213
220
|
});
|
|
221
|
+
const AffiliateOutreachRunSchema = z.discriminatedUnion("status", [
|
|
222
|
+
AffiliateOutreachRunBaseSchema.extend({ status: z.literal("preparing") }),
|
|
223
|
+
AffiliateOutreachRunBaseSchema.extend({ status: z.literal("failed") }),
|
|
224
|
+
AffiliateOutreachRunBaseSchema.extend({
|
|
225
|
+
status: z.literal("ready"),
|
|
226
|
+
sequence: AffiliateStrictSequenceSchema
|
|
227
|
+
}),
|
|
228
|
+
AffiliateOutreachRunBaseSchema.extend({
|
|
229
|
+
status: z.literal("active"),
|
|
230
|
+
sequence: AffiliateStrictSequenceSchema
|
|
231
|
+
})
|
|
232
|
+
]);
|
|
214
233
|
const AffiliateOutreachResponseSchema = z.object({
|
|
215
234
|
status: z.literal("ok"),
|
|
216
235
|
outreach: AffiliateOutreachRunSchema
|
|
217
236
|
});
|
|
237
|
+
const AffiliateOutreachSummarySchema = AffiliateOutreachRunBaseSchema.omit({
|
|
238
|
+
enriched_leads: true
|
|
239
|
+
});
|
|
240
|
+
const AffiliateAudienceSummarySchema = z.object({
|
|
241
|
+
public_token: z.string().min(1),
|
|
242
|
+
status: z.enum(["launching", "running", "finished", "failed"]),
|
|
243
|
+
affiliate_link: z.string().url(),
|
|
244
|
+
product_domain: z.string().min(1),
|
|
245
|
+
source_type: z.string().min(1),
|
|
246
|
+
linkedin_url: z.string().url(),
|
|
247
|
+
max_results: z.number().int().positive(),
|
|
248
|
+
error_message: z.string().nullable(),
|
|
249
|
+
organization_id: z.string().nullable(),
|
|
250
|
+
created_at: DatabaseTimestampSchema,
|
|
251
|
+
updated_at: DatabaseTimestampSchema
|
|
252
|
+
});
|
|
253
|
+
const AffiliateOutreachListResponseSchema = z.object({
|
|
254
|
+
status: z.literal("ok"),
|
|
255
|
+
audiences: z.array(AffiliateAudienceSummarySchema),
|
|
256
|
+
outreach: z.array(AffiliateOutreachSummarySchema)
|
|
257
|
+
});
|
|
258
|
+
const AffiliateAnalyticsAggregateSchema = z.object({
|
|
259
|
+
leadsCount: z.number().int().nonnegative(),
|
|
260
|
+
contactedCount: z.number().int().nonnegative(),
|
|
261
|
+
emailsSentCount: z.number().int().nonnegative(),
|
|
262
|
+
replyCount: z.number().int().nonnegative(),
|
|
263
|
+
automaticReplyCount: z.number().int().nonnegative(),
|
|
264
|
+
humanReplyCount: z.number().int().nonnegative(),
|
|
265
|
+
humanReplyRate: z.number().nonnegative(),
|
|
266
|
+
bouncedCount: z.number().int().nonnegative(),
|
|
267
|
+
unsubscribedCount: z.number().int().nonnegative(),
|
|
268
|
+
opportunityCount: z.number().int().nonnegative(),
|
|
269
|
+
meetingCount: z.number().int().nonnegative(),
|
|
270
|
+
meetingCompletedCount: z.number().int().nonnegative(),
|
|
271
|
+
wonCount: z.number().int().nonnegative()
|
|
272
|
+
});
|
|
273
|
+
const AffiliateAnalyticsStepSchema = z.object({
|
|
274
|
+
step: z.number().int().positive(),
|
|
275
|
+
variant: z.number().int().nonnegative(),
|
|
276
|
+
variantLabel: z.string().trim().min(1).max(20),
|
|
277
|
+
sentCount: z.number().int().nonnegative(),
|
|
278
|
+
replyCount: z.number().int().nonnegative(),
|
|
279
|
+
automaticReplyCount: z.number().int().nonnegative(),
|
|
280
|
+
humanReplyCount: z.number().int().nonnegative(),
|
|
281
|
+
humanReplyRate: z.number().nonnegative(),
|
|
282
|
+
opportunityCount: z.number().int().nonnegative(),
|
|
283
|
+
meetingCount: z.number().int().nonnegative().nullable(),
|
|
284
|
+
wonCount: z.number().int().nonnegative().nullable(),
|
|
285
|
+
learning: z.object({
|
|
286
|
+
ready: z.boolean(),
|
|
287
|
+
reason: z.string().min(1)
|
|
288
|
+
})
|
|
289
|
+
});
|
|
290
|
+
const AffiliateOutreachAnalyticsResponseSchema = z.object({
|
|
291
|
+
status: z.literal("ok"),
|
|
292
|
+
runId: z.string().uuid(),
|
|
293
|
+
runStatus: z.string().min(1),
|
|
294
|
+
campaignId: z.string().min(1),
|
|
295
|
+
campaignName: z.string().min(1).nullable(),
|
|
296
|
+
campaignStatus: z.number().nullable(),
|
|
297
|
+
aggregate: AffiliateAnalyticsAggregateSchema,
|
|
298
|
+
steps: z.array(AffiliateAnalyticsStepSchema)
|
|
299
|
+
});
|
|
218
300
|
const AffiliateOutreachEnrichStartResponseSchema = z.object({
|
|
219
301
|
status: z.literal("accepted"),
|
|
220
302
|
audienceRunId: z.string().uuid(),
|
|
@@ -547,7 +629,10 @@ const cliPacks = [
|
|
|
547
629
|
summary: "Prepare and sync qualified leads into downstream systems.",
|
|
548
630
|
commands: [
|
|
549
631
|
"affiliate:run",
|
|
632
|
+
"affiliate:list",
|
|
550
633
|
"affiliate:status",
|
|
634
|
+
"affiliate:analytics",
|
|
635
|
+
"affiliate:regenerate-sequence",
|
|
551
636
|
"affiliate:enrich",
|
|
552
637
|
"affiliate:activate",
|
|
553
638
|
"affiliate:launch",
|
|
@@ -602,7 +687,10 @@ const helpVisibleCommandNames = new Set([
|
|
|
602
687
|
"leads:score",
|
|
603
688
|
"leads:pipeline",
|
|
604
689
|
"affiliate:run",
|
|
690
|
+
"affiliate:list",
|
|
605
691
|
"affiliate:status",
|
|
692
|
+
"affiliate:analytics",
|
|
693
|
+
"affiliate:regenerate-sequence",
|
|
606
694
|
"affiliate:enrich",
|
|
607
695
|
"affiliate:activate",
|
|
608
696
|
"affiliate:launch",
|
|
@@ -6621,6 +6709,14 @@ async function prepareAffiliateOutreachViaApp(session, payload) {
|
|
|
6621
6709
|
}), AffiliateOutreachResponseSchema);
|
|
6622
6710
|
return value.outreach;
|
|
6623
6711
|
}
|
|
6712
|
+
async function listAffiliateOutreachViaApp(session) {
|
|
6713
|
+
const { value } = await fetchCliJson(session, (currentSession) => fetch(`${currentSession.apiBaseUrl}/api/cli/affiliate-outreach`, {
|
|
6714
|
+
headers: {
|
|
6715
|
+
Authorization: `Bearer ${currentSession.accessToken}`
|
|
6716
|
+
}
|
|
6717
|
+
}), AffiliateOutreachListResponseSchema);
|
|
6718
|
+
return value;
|
|
6719
|
+
}
|
|
6624
6720
|
async function getAffiliateOutreachViaApp(session, audienceRunId) {
|
|
6625
6721
|
const { value } = await fetchCliJson(session, (currentSession) => fetch(`${currentSession.apiBaseUrl}/api/cli/affiliate-outreach/${encodeURIComponent(audienceRunId)}`, {
|
|
6626
6722
|
headers: {
|
|
@@ -6629,6 +6725,14 @@ async function getAffiliateOutreachViaApp(session, audienceRunId) {
|
|
|
6629
6725
|
}), AffiliateOutreachResponseSchema);
|
|
6630
6726
|
return value.outreach;
|
|
6631
6727
|
}
|
|
6728
|
+
async function getAffiliateOutreachAnalyticsViaApp(session, audienceRunId) {
|
|
6729
|
+
const { value } = await fetchCliJson(session, (currentSession) => fetch(`${currentSession.apiBaseUrl}/api/cli/affiliate-outreach/${encodeURIComponent(audienceRunId)}/analytics`, {
|
|
6730
|
+
headers: {
|
|
6731
|
+
Authorization: `Bearer ${currentSession.accessToken}`
|
|
6732
|
+
}
|
|
6733
|
+
}), AffiliateOutreachAnalyticsResponseSchema);
|
|
6734
|
+
return value;
|
|
6735
|
+
}
|
|
6632
6736
|
async function activateAffiliateOutreachViaApp(session, audienceRunId) {
|
|
6633
6737
|
const { value } = await fetchCliJson(session, (currentSession) => fetch(`${currentSession.apiBaseUrl}/api/cli/affiliate-outreach/${encodeURIComponent(audienceRunId)}/activate`, {
|
|
6634
6738
|
method: "POST",
|
|
@@ -13661,6 +13765,44 @@ program
|
|
|
13661
13765
|
}
|
|
13662
13766
|
});
|
|
13663
13767
|
});
|
|
13768
|
+
function parseAffiliateOutreachCommandOptions(options) {
|
|
13769
|
+
const parsedSteps = z.coerce.number().int().safeParse(options.steps);
|
|
13770
|
+
if (!parsedSteps.success || parsedSteps.data !== 3) {
|
|
13771
|
+
throw new Error("Affiliate outreach currently requires exactly 3 sequence emails. Use --steps 3.");
|
|
13772
|
+
}
|
|
13773
|
+
const parsedTimingMode = z.enum(["auto", "custom"]).safeParse(options.timingMode);
|
|
13774
|
+
if (!parsedTimingMode.success) {
|
|
13775
|
+
throw new Error("--timing-mode must be either auto or custom.");
|
|
13776
|
+
}
|
|
13777
|
+
const trimmedCampaignName = options.campaignName?.trim();
|
|
13778
|
+
const campaignName = trimmedCampaignName
|
|
13779
|
+
? z.string().max(120, "--campaign-name must be 120 characters or fewer.").parse(trimmedCampaignName)
|
|
13780
|
+
: undefined;
|
|
13781
|
+
return {
|
|
13782
|
+
campaignName,
|
|
13783
|
+
language: z.string().trim().min(1).max(40).parse(options.language),
|
|
13784
|
+
numberOfSteps: 3,
|
|
13785
|
+
minEmailScore: z.coerce.number().int().min(0).max(100).parse(options.minEmailScore),
|
|
13786
|
+
allowAcceptAll: Boolean(options.allowAcceptAll),
|
|
13787
|
+
timingMode: parsedTimingMode.data,
|
|
13788
|
+
dailyLimit: z.coerce.number().int().min(1).max(1000).parse(options.dailyLimit),
|
|
13789
|
+
timezone: z.string().trim().min(1).max(100).parse(options.timezone),
|
|
13790
|
+
sendFrom: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/).parse(options.sendFrom),
|
|
13791
|
+
sendTo: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/).parse(options.sendTo)
|
|
13792
|
+
};
|
|
13793
|
+
}
|
|
13794
|
+
function formatAffiliateAnalyticsForOutput(analytics) {
|
|
13795
|
+
if (runtimeOutputOptions.json)
|
|
13796
|
+
return analytics;
|
|
13797
|
+
return {
|
|
13798
|
+
...analytics,
|
|
13799
|
+
steps: analytics.steps.map((step) => ({
|
|
13800
|
+
...step,
|
|
13801
|
+
meetingCount: step.meetingCount ?? "n/a",
|
|
13802
|
+
wonCount: step.wonCount ?? "n/a"
|
|
13803
|
+
}))
|
|
13804
|
+
};
|
|
13805
|
+
}
|
|
13664
13806
|
function getLocalSalesNavigatorPeopleCheckpointPath(sourceQueryUrl, maxResultsPerSearch, pageSize) {
|
|
13665
13807
|
const key = createHash("sha256")
|
|
13666
13808
|
.update(`${sourceQueryUrl}\n${maxResultsPerSearch}\n${pageSize}`)
|
|
@@ -14741,15 +14883,17 @@ addAffiliateAudienceOptions(program
|
|
|
14741
14883
|
.description("Build an affiliate audience and prepare a reviewed Instantly campaign end to end."))
|
|
14742
14884
|
.option("--campaign-name <name>", "Optional Instantly campaign name")
|
|
14743
14885
|
.option("--language <language>", "Sequence language", "English")
|
|
14744
|
-
.option("--steps <number>", "
|
|
14886
|
+
.option("--steps <number>", "Exactly 3 sequence emails (fixed)", "3")
|
|
14745
14887
|
.option("--min-email-score <number>", "Minimum Hunter confidence score", "80")
|
|
14746
14888
|
.option("--allow-accept-all", "Include catch-all domains that meet the score threshold", false)
|
|
14889
|
+
.option("--timing-mode <mode>", "Schedule mode: custom honors the daily limit and sending window; auto derives them from capacity", "custom")
|
|
14747
14890
|
.option("--daily-limit <number>", "Maximum new leads contacted per day", "25")
|
|
14748
14891
|
.option("--timezone <timezone>", "Instantly sending timezone", "America/Detroit")
|
|
14749
14892
|
.option("--send-from <time>", "Weekday sending window start (HH:MM)", "09:00")
|
|
14750
14893
|
.option("--send-to <time>", "Weekday sending window end (HH:MM)", "16:00")
|
|
14751
14894
|
.option("--activate", "Activate the prepared Instantly campaign after review data is returned", false)
|
|
14752
14895
|
.action(async (options) => {
|
|
14896
|
+
const outreachOptions = parseAffiliateOutreachCommandOptions(options);
|
|
14753
14897
|
const launched = await runAffiliateLaunchCommand(options);
|
|
14754
14898
|
if (options.dryRun) {
|
|
14755
14899
|
printOutput({
|
|
@@ -14757,7 +14901,8 @@ addAffiliateAudienceOptions(program
|
|
|
14757
14901
|
outreach: {
|
|
14758
14902
|
status: "dry-run",
|
|
14759
14903
|
provider: "instantly",
|
|
14760
|
-
activation: "not_requested"
|
|
14904
|
+
activation: "not_requested",
|
|
14905
|
+
settings: outreachOptions
|
|
14761
14906
|
}
|
|
14762
14907
|
});
|
|
14763
14908
|
return;
|
|
@@ -14771,15 +14916,7 @@ addAffiliateAudienceOptions(program
|
|
|
14771
14916
|
}
|
|
14772
14917
|
const outreach = await prepareAffiliateOutreachViaApp(launched.session, {
|
|
14773
14918
|
audienceRunId: z.string().uuid().parse(runId),
|
|
14774
|
-
|
|
14775
|
-
language: z.string().trim().min(1).max(40).parse(options.language),
|
|
14776
|
-
numberOfSteps: z.coerce.number().int().min(1).max(6).parse(options.steps),
|
|
14777
|
-
minEmailScore: z.coerce.number().int().min(0).max(100).parse(options.minEmailScore),
|
|
14778
|
-
allowAcceptAll: Boolean(options.allowAcceptAll),
|
|
14779
|
-
dailyLimit: z.coerce.number().int().min(1).max(1000).parse(options.dailyLimit),
|
|
14780
|
-
timezone: z.string().trim().min(1).max(100).parse(options.timezone),
|
|
14781
|
-
sendFrom: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/).parse(options.sendFrom),
|
|
14782
|
-
sendTo: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/).parse(options.sendTo)
|
|
14919
|
+
...outreachOptions
|
|
14783
14920
|
});
|
|
14784
14921
|
const finalOutreach = options.activate
|
|
14785
14922
|
? await activateAffiliateOutreachViaApp(launched.session, runId)
|
|
@@ -14793,6 +14930,13 @@ addAffiliateAudienceOptions(program
|
|
|
14793
14930
|
: "The Instantly campaign is active."
|
|
14794
14931
|
});
|
|
14795
14932
|
});
|
|
14933
|
+
program
|
|
14934
|
+
.command("affiliate:list")
|
|
14935
|
+
.description("List affiliate audiences and Instantly outreach runs in the active workspace.")
|
|
14936
|
+
.action(async () => {
|
|
14937
|
+
const session = await requireAuthSession();
|
|
14938
|
+
printOutput(await listAffiliateOutreachViaApp(session));
|
|
14939
|
+
});
|
|
14796
14940
|
program
|
|
14797
14941
|
.command("affiliate:status <run-id>")
|
|
14798
14942
|
.description("Show enrichment, sequence, Instantly, and activation status for an affiliate run.")
|
|
@@ -14801,16 +14945,24 @@ program
|
|
|
14801
14945
|
const outreach = await getAffiliateOutreachViaApp(session, z.string().uuid().parse(runId));
|
|
14802
14946
|
printOutput({ status: "ok", outreach });
|
|
14803
14947
|
});
|
|
14948
|
+
program
|
|
14949
|
+
.command("affiliate:analytics <run-id>")
|
|
14950
|
+
.description("Show safe campaign and step/variant performance metrics for an affiliate run.")
|
|
14951
|
+
.action(async (runId) => {
|
|
14952
|
+
const session = await requireAuthSession();
|
|
14953
|
+
const analytics = await getAffiliateOutreachAnalyticsViaApp(session, z.string().uuid().parse(runId));
|
|
14954
|
+
printOutput(formatAffiliateAnalyticsForOutput(analytics));
|
|
14955
|
+
});
|
|
14804
14956
|
program
|
|
14805
14957
|
.command("affiliate:regenerate-sequence <run-id>")
|
|
14806
|
-
.description("Regenerate sequence variants
|
|
14958
|
+
.description("Regenerate observable sequence variants, then sync them to Instantly.")
|
|
14807
14959
|
.action(async (runId) => {
|
|
14808
14960
|
const session = await requireAuthSession();
|
|
14809
14961
|
const outreach = await regenerateAffiliateOutreachSequenceViaApp(session, z.string().uuid().parse(runId));
|
|
14810
14962
|
printOutput({
|
|
14811
14963
|
status: "ok",
|
|
14812
14964
|
outreach,
|
|
14813
|
-
message: "
|
|
14965
|
+
message: "Observable sequence variants were regenerated and synced to Instantly."
|
|
14814
14966
|
});
|
|
14815
14967
|
});
|
|
14816
14968
|
program
|
package/package.json
CHANGED