salesprompter-cli 0.1.63 → 0.1.65
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 +178 -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,27 @@ 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 AffiliateOutreachRunBaseSchema = z.object({
|
|
170
190
|
id: z.string().uuid(),
|
|
171
191
|
audience_run_id: z.string().uuid(),
|
|
172
192
|
organization_id: z.string().min(1),
|
|
@@ -174,36 +194,7 @@ const AffiliateOutreachRunSchema = z.object({
|
|
|
174
194
|
provider: z.literal("instantly"),
|
|
175
195
|
instantly_campaign_id: z.string().min(1).nullable(),
|
|
176
196
|
instantly_campaign_name: z.string().min(1).nullable(),
|
|
177
|
-
sequence: z.array(
|
|
178
|
-
step: z.number().int().positive(),
|
|
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
|
-
})),
|
|
197
|
+
sequence: z.array(AffiliateHistoricalSequenceStepSchema),
|
|
207
198
|
stats: z.record(z.string(), z.unknown()),
|
|
208
199
|
settings: z.record(z.string(), z.unknown()),
|
|
209
200
|
error_message: z.string().nullable(),
|
|
@@ -211,10 +202,83 @@ const AffiliateOutreachRunSchema = z.object({
|
|
|
211
202
|
created_at: DatabaseTimestampSchema,
|
|
212
203
|
updated_at: DatabaseTimestampSchema
|
|
213
204
|
});
|
|
205
|
+
const AffiliateOutreachRunSchema = z.discriminatedUnion("status", [
|
|
206
|
+
AffiliateOutreachRunBaseSchema.extend({ status: z.literal("preparing") }),
|
|
207
|
+
AffiliateOutreachRunBaseSchema.extend({ status: z.literal("failed") }),
|
|
208
|
+
AffiliateOutreachRunBaseSchema.extend({
|
|
209
|
+
status: z.literal("ready"),
|
|
210
|
+
sequence: AffiliateStrictSequenceSchema
|
|
211
|
+
}),
|
|
212
|
+
AffiliateOutreachRunBaseSchema.extend({
|
|
213
|
+
status: z.literal("active"),
|
|
214
|
+
sequence: AffiliateStrictSequenceSchema
|
|
215
|
+
})
|
|
216
|
+
]);
|
|
214
217
|
const AffiliateOutreachResponseSchema = z.object({
|
|
215
218
|
status: z.literal("ok"),
|
|
216
219
|
outreach: AffiliateOutreachRunSchema
|
|
217
220
|
});
|
|
221
|
+
const AffiliateOutreachSummarySchema = AffiliateOutreachRunBaseSchema;
|
|
222
|
+
const AffiliateAudienceSummarySchema = z.object({
|
|
223
|
+
public_token: z.string().min(1),
|
|
224
|
+
status: z.enum(["launching", "running", "finished", "failed"]),
|
|
225
|
+
affiliate_link: z.string().url(),
|
|
226
|
+
product_domain: z.string().min(1),
|
|
227
|
+
source_type: z.string().min(1),
|
|
228
|
+
linkedin_url: z.string().url(),
|
|
229
|
+
max_results: z.number().int().positive(),
|
|
230
|
+
error_message: z.string().nullable(),
|
|
231
|
+
organization_id: z.string().nullable(),
|
|
232
|
+
created_at: DatabaseTimestampSchema,
|
|
233
|
+
updated_at: DatabaseTimestampSchema
|
|
234
|
+
});
|
|
235
|
+
const AffiliateOutreachListResponseSchema = z.object({
|
|
236
|
+
status: z.literal("ok"),
|
|
237
|
+
audiences: z.array(AffiliateAudienceSummarySchema),
|
|
238
|
+
outreach: z.array(AffiliateOutreachSummarySchema)
|
|
239
|
+
});
|
|
240
|
+
const AffiliateAnalyticsAggregateSchema = z.object({
|
|
241
|
+
leadsCount: z.number().int().nonnegative(),
|
|
242
|
+
contactedCount: z.number().int().nonnegative(),
|
|
243
|
+
emailsSentCount: z.number().int().nonnegative(),
|
|
244
|
+
replyCount: z.number().int().nonnegative(),
|
|
245
|
+
automaticReplyCount: z.number().int().nonnegative(),
|
|
246
|
+
humanReplyCount: z.number().int().nonnegative(),
|
|
247
|
+
humanReplyRate: z.number().nonnegative(),
|
|
248
|
+
bouncedCount: z.number().int().nonnegative(),
|
|
249
|
+
unsubscribedCount: z.number().int().nonnegative(),
|
|
250
|
+
opportunityCount: z.number().int().nonnegative(),
|
|
251
|
+
meetingCount: z.number().int().nonnegative(),
|
|
252
|
+
meetingCompletedCount: z.number().int().nonnegative(),
|
|
253
|
+
wonCount: z.number().int().nonnegative()
|
|
254
|
+
});
|
|
255
|
+
const AffiliateAnalyticsStepSchema = z.object({
|
|
256
|
+
step: z.number().int().positive(),
|
|
257
|
+
variant: z.number().int().nonnegative(),
|
|
258
|
+
variantLabel: z.string().trim().min(1).max(20),
|
|
259
|
+
sentCount: z.number().int().nonnegative(),
|
|
260
|
+
replyCount: z.number().int().nonnegative(),
|
|
261
|
+
automaticReplyCount: z.number().int().nonnegative(),
|
|
262
|
+
humanReplyCount: z.number().int().nonnegative(),
|
|
263
|
+
humanReplyRate: z.number().nonnegative(),
|
|
264
|
+
opportunityCount: z.number().int().nonnegative(),
|
|
265
|
+
meetingCount: z.number().int().nonnegative().nullable(),
|
|
266
|
+
wonCount: z.number().int().nonnegative().nullable(),
|
|
267
|
+
learning: z.object({
|
|
268
|
+
ready: z.boolean(),
|
|
269
|
+
reason: z.string().min(1)
|
|
270
|
+
})
|
|
271
|
+
});
|
|
272
|
+
const AffiliateOutreachAnalyticsResponseSchema = z.object({
|
|
273
|
+
status: z.literal("ok"),
|
|
274
|
+
runId: z.string().uuid(),
|
|
275
|
+
runStatus: z.string().min(1),
|
|
276
|
+
campaignId: z.string().min(1),
|
|
277
|
+
campaignName: z.string().min(1).nullable(),
|
|
278
|
+
campaignStatus: z.number().nullable(),
|
|
279
|
+
aggregate: AffiliateAnalyticsAggregateSchema,
|
|
280
|
+
steps: z.array(AffiliateAnalyticsStepSchema)
|
|
281
|
+
});
|
|
218
282
|
const AffiliateOutreachEnrichStartResponseSchema = z.object({
|
|
219
283
|
status: z.literal("accepted"),
|
|
220
284
|
audienceRunId: z.string().uuid(),
|
|
@@ -547,7 +611,10 @@ const cliPacks = [
|
|
|
547
611
|
summary: "Prepare and sync qualified leads into downstream systems.",
|
|
548
612
|
commands: [
|
|
549
613
|
"affiliate:run",
|
|
614
|
+
"affiliate:list",
|
|
550
615
|
"affiliate:status",
|
|
616
|
+
"affiliate:analytics",
|
|
617
|
+
"affiliate:regenerate-sequence",
|
|
551
618
|
"affiliate:enrich",
|
|
552
619
|
"affiliate:activate",
|
|
553
620
|
"affiliate:launch",
|
|
@@ -602,7 +669,10 @@ const helpVisibleCommandNames = new Set([
|
|
|
602
669
|
"leads:score",
|
|
603
670
|
"leads:pipeline",
|
|
604
671
|
"affiliate:run",
|
|
672
|
+
"affiliate:list",
|
|
605
673
|
"affiliate:status",
|
|
674
|
+
"affiliate:analytics",
|
|
675
|
+
"affiliate:regenerate-sequence",
|
|
606
676
|
"affiliate:enrich",
|
|
607
677
|
"affiliate:activate",
|
|
608
678
|
"affiliate:launch",
|
|
@@ -6621,6 +6691,14 @@ async function prepareAffiliateOutreachViaApp(session, payload) {
|
|
|
6621
6691
|
}), AffiliateOutreachResponseSchema);
|
|
6622
6692
|
return value.outreach;
|
|
6623
6693
|
}
|
|
6694
|
+
async function listAffiliateOutreachViaApp(session) {
|
|
6695
|
+
const { value } = await fetchCliJson(session, (currentSession) => fetch(`${currentSession.apiBaseUrl}/api/cli/affiliate-outreach`, {
|
|
6696
|
+
headers: {
|
|
6697
|
+
Authorization: `Bearer ${currentSession.accessToken}`
|
|
6698
|
+
}
|
|
6699
|
+
}), AffiliateOutreachListResponseSchema);
|
|
6700
|
+
return value;
|
|
6701
|
+
}
|
|
6624
6702
|
async function getAffiliateOutreachViaApp(session, audienceRunId) {
|
|
6625
6703
|
const { value } = await fetchCliJson(session, (currentSession) => fetch(`${currentSession.apiBaseUrl}/api/cli/affiliate-outreach/${encodeURIComponent(audienceRunId)}`, {
|
|
6626
6704
|
headers: {
|
|
@@ -6629,6 +6707,14 @@ async function getAffiliateOutreachViaApp(session, audienceRunId) {
|
|
|
6629
6707
|
}), AffiliateOutreachResponseSchema);
|
|
6630
6708
|
return value.outreach;
|
|
6631
6709
|
}
|
|
6710
|
+
async function getAffiliateOutreachAnalyticsViaApp(session, audienceRunId) {
|
|
6711
|
+
const { value } = await fetchCliJson(session, (currentSession) => fetch(`${currentSession.apiBaseUrl}/api/cli/affiliate-outreach/${encodeURIComponent(audienceRunId)}/analytics`, {
|
|
6712
|
+
headers: {
|
|
6713
|
+
Authorization: `Bearer ${currentSession.accessToken}`
|
|
6714
|
+
}
|
|
6715
|
+
}), AffiliateOutreachAnalyticsResponseSchema);
|
|
6716
|
+
return value;
|
|
6717
|
+
}
|
|
6632
6718
|
async function activateAffiliateOutreachViaApp(session, audienceRunId) {
|
|
6633
6719
|
const { value } = await fetchCliJson(session, (currentSession) => fetch(`${currentSession.apiBaseUrl}/api/cli/affiliate-outreach/${encodeURIComponent(audienceRunId)}/activate`, {
|
|
6634
6720
|
method: "POST",
|
|
@@ -13661,6 +13747,44 @@ program
|
|
|
13661
13747
|
}
|
|
13662
13748
|
});
|
|
13663
13749
|
});
|
|
13750
|
+
function parseAffiliateOutreachCommandOptions(options) {
|
|
13751
|
+
const parsedSteps = z.coerce.number().int().safeParse(options.steps);
|
|
13752
|
+
if (!parsedSteps.success || parsedSteps.data !== 3) {
|
|
13753
|
+
throw new Error("Affiliate outreach currently requires exactly 3 sequence emails. Use --steps 3.");
|
|
13754
|
+
}
|
|
13755
|
+
const parsedTimingMode = z.enum(["auto", "custom"]).safeParse(options.timingMode);
|
|
13756
|
+
if (!parsedTimingMode.success) {
|
|
13757
|
+
throw new Error("--timing-mode must be either auto or custom.");
|
|
13758
|
+
}
|
|
13759
|
+
const trimmedCampaignName = options.campaignName?.trim();
|
|
13760
|
+
const campaignName = trimmedCampaignName
|
|
13761
|
+
? z.string().max(120, "--campaign-name must be 120 characters or fewer.").parse(trimmedCampaignName)
|
|
13762
|
+
: undefined;
|
|
13763
|
+
return {
|
|
13764
|
+
campaignName,
|
|
13765
|
+
language: z.string().trim().min(1).max(40).parse(options.language),
|
|
13766
|
+
numberOfSteps: 3,
|
|
13767
|
+
minEmailScore: z.coerce.number().int().min(0).max(100).parse(options.minEmailScore),
|
|
13768
|
+
allowAcceptAll: Boolean(options.allowAcceptAll),
|
|
13769
|
+
timingMode: parsedTimingMode.data,
|
|
13770
|
+
dailyLimit: z.coerce.number().int().min(1).max(1000).parse(options.dailyLimit),
|
|
13771
|
+
timezone: z.string().trim().min(1).max(100).parse(options.timezone),
|
|
13772
|
+
sendFrom: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/).parse(options.sendFrom),
|
|
13773
|
+
sendTo: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/).parse(options.sendTo)
|
|
13774
|
+
};
|
|
13775
|
+
}
|
|
13776
|
+
function formatAffiliateAnalyticsForOutput(analytics) {
|
|
13777
|
+
if (runtimeOutputOptions.json)
|
|
13778
|
+
return analytics;
|
|
13779
|
+
return {
|
|
13780
|
+
...analytics,
|
|
13781
|
+
steps: analytics.steps.map((step) => ({
|
|
13782
|
+
...step,
|
|
13783
|
+
meetingCount: step.meetingCount ?? "n/a",
|
|
13784
|
+
wonCount: step.wonCount ?? "n/a"
|
|
13785
|
+
}))
|
|
13786
|
+
};
|
|
13787
|
+
}
|
|
13664
13788
|
function getLocalSalesNavigatorPeopleCheckpointPath(sourceQueryUrl, maxResultsPerSearch, pageSize) {
|
|
13665
13789
|
const key = createHash("sha256")
|
|
13666
13790
|
.update(`${sourceQueryUrl}\n${maxResultsPerSearch}\n${pageSize}`)
|
|
@@ -14741,15 +14865,17 @@ addAffiliateAudienceOptions(program
|
|
|
14741
14865
|
.description("Build an affiliate audience and prepare a reviewed Instantly campaign end to end."))
|
|
14742
14866
|
.option("--campaign-name <name>", "Optional Instantly campaign name")
|
|
14743
14867
|
.option("--language <language>", "Sequence language", "English")
|
|
14744
|
-
.option("--steps <number>", "
|
|
14868
|
+
.option("--steps <number>", "Exactly 3 sequence emails (fixed)", "3")
|
|
14745
14869
|
.option("--min-email-score <number>", "Minimum Hunter confidence score", "80")
|
|
14746
14870
|
.option("--allow-accept-all", "Include catch-all domains that meet the score threshold", false)
|
|
14871
|
+
.option("--timing-mode <mode>", "Schedule mode: custom honors the daily limit and sending window; auto derives them from capacity", "custom")
|
|
14747
14872
|
.option("--daily-limit <number>", "Maximum new leads contacted per day", "25")
|
|
14748
14873
|
.option("--timezone <timezone>", "Instantly sending timezone", "America/Detroit")
|
|
14749
14874
|
.option("--send-from <time>", "Weekday sending window start (HH:MM)", "09:00")
|
|
14750
14875
|
.option("--send-to <time>", "Weekday sending window end (HH:MM)", "16:00")
|
|
14751
14876
|
.option("--activate", "Activate the prepared Instantly campaign after review data is returned", false)
|
|
14752
14877
|
.action(async (options) => {
|
|
14878
|
+
const outreachOptions = parseAffiliateOutreachCommandOptions(options);
|
|
14753
14879
|
const launched = await runAffiliateLaunchCommand(options);
|
|
14754
14880
|
if (options.dryRun) {
|
|
14755
14881
|
printOutput({
|
|
@@ -14757,7 +14883,8 @@ addAffiliateAudienceOptions(program
|
|
|
14757
14883
|
outreach: {
|
|
14758
14884
|
status: "dry-run",
|
|
14759
14885
|
provider: "instantly",
|
|
14760
|
-
activation: "not_requested"
|
|
14886
|
+
activation: "not_requested",
|
|
14887
|
+
settings: outreachOptions
|
|
14761
14888
|
}
|
|
14762
14889
|
});
|
|
14763
14890
|
return;
|
|
@@ -14771,15 +14898,7 @@ addAffiliateAudienceOptions(program
|
|
|
14771
14898
|
}
|
|
14772
14899
|
const outreach = await prepareAffiliateOutreachViaApp(launched.session, {
|
|
14773
14900
|
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)
|
|
14901
|
+
...outreachOptions
|
|
14783
14902
|
});
|
|
14784
14903
|
const finalOutreach = options.activate
|
|
14785
14904
|
? await activateAffiliateOutreachViaApp(launched.session, runId)
|
|
@@ -14793,6 +14912,13 @@ addAffiliateAudienceOptions(program
|
|
|
14793
14912
|
: "The Instantly campaign is active."
|
|
14794
14913
|
});
|
|
14795
14914
|
});
|
|
14915
|
+
program
|
|
14916
|
+
.command("affiliate:list")
|
|
14917
|
+
.description("List affiliate audiences and Instantly outreach runs in the active workspace.")
|
|
14918
|
+
.action(async () => {
|
|
14919
|
+
const session = await requireAuthSession();
|
|
14920
|
+
printOutput(await listAffiliateOutreachViaApp(session));
|
|
14921
|
+
});
|
|
14796
14922
|
program
|
|
14797
14923
|
.command("affiliate:status <run-id>")
|
|
14798
14924
|
.description("Show enrichment, sequence, Instantly, and activation status for an affiliate run.")
|
|
@@ -14801,16 +14927,24 @@ program
|
|
|
14801
14927
|
const outreach = await getAffiliateOutreachViaApp(session, z.string().uuid().parse(runId));
|
|
14802
14928
|
printOutput({ status: "ok", outreach });
|
|
14803
14929
|
});
|
|
14930
|
+
program
|
|
14931
|
+
.command("affiliate:analytics <run-id>")
|
|
14932
|
+
.description("Show safe campaign and step/variant performance metrics for an affiliate run.")
|
|
14933
|
+
.action(async (runId) => {
|
|
14934
|
+
const session = await requireAuthSession();
|
|
14935
|
+
const analytics = await getAffiliateOutreachAnalyticsViaApp(session, z.string().uuid().parse(runId));
|
|
14936
|
+
printOutput(formatAffiliateAnalyticsForOutput(analytics));
|
|
14937
|
+
});
|
|
14804
14938
|
program
|
|
14805
14939
|
.command("affiliate:regenerate-sequence <run-id>")
|
|
14806
|
-
.description("Regenerate sequence variants
|
|
14940
|
+
.description("Regenerate observable sequence variants, then sync them to Instantly.")
|
|
14807
14941
|
.action(async (runId) => {
|
|
14808
14942
|
const session = await requireAuthSession();
|
|
14809
14943
|
const outreach = await regenerateAffiliateOutreachSequenceViaApp(session, z.string().uuid().parse(runId));
|
|
14810
14944
|
printOutput({
|
|
14811
14945
|
status: "ok",
|
|
14812
14946
|
outreach,
|
|
14813
|
-
message: "
|
|
14947
|
+
message: "Observable sequence variants were regenerated and synced to Instantly."
|
|
14814
14948
|
});
|
|
14815
14949
|
});
|
|
14816
14950
|
program
|
package/package.json
CHANGED