@dench.com/cli 3.8.2-staging.391 → 3.8.3-staging.398
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/app-sdk.d.ts +246 -28
- package/campaign.ts +31 -9
- package/lib/api-schemas.ts +205 -9
- package/lib/app-sdk-catalog.generated.ts +23 -2
- package/lib/app-sdk-surface.ts +65 -5
- package/lib/campaign-agent.ts +69 -4
- package/lib/command-registry.ts +150 -8
- package/lib/references.generated.json +10 -10
- package/package.json +1 -1
package/lib/api-schemas.ts
CHANGED
|
@@ -2428,6 +2428,19 @@ const campaignSignal = z.enum([
|
|
|
2428
2428
|
"funding",
|
|
2429
2429
|
"launch",
|
|
2430
2430
|
"new_leader",
|
|
2431
|
+
"voice_engagement",
|
|
2432
|
+
"company_engagement",
|
|
2433
|
+
"brand_engagement",
|
|
2434
|
+
"lookalike",
|
|
2435
|
+
]);
|
|
2436
|
+
/** Signals that take terms: topics, roles, or LinkedIn profile/page URLs. */
|
|
2437
|
+
const campaignTermSignal = z.enum([
|
|
2438
|
+
"post_engagement",
|
|
2439
|
+
"keyword",
|
|
2440
|
+
"hiring",
|
|
2441
|
+
"voice_engagement",
|
|
2442
|
+
"company_engagement",
|
|
2443
|
+
"brand_engagement",
|
|
2431
2444
|
]);
|
|
2432
2445
|
const campaignTargeting = z.object({
|
|
2433
2446
|
titles: z.array(z.string()),
|
|
@@ -2436,12 +2449,104 @@ const campaignTargeting = z.object({
|
|
|
2436
2449
|
industries: z.array(z.string()),
|
|
2437
2450
|
excludes: z.array(z.string()),
|
|
2438
2451
|
companies: z.array(z.string()).optional(),
|
|
2452
|
+
companyTypes: z.array(z.string()).optional(),
|
|
2439
2453
|
});
|
|
2440
2454
|
const campaignSignalTerms = z.object({
|
|
2441
|
-
post_engagement: z
|
|
2455
|
+
post_engagement: z
|
|
2456
|
+
.array(z.string())
|
|
2457
|
+
.optional()
|
|
2458
|
+
.describe(
|
|
2459
|
+
'Post topics, phrased as what the buyer does ("using AI for outbound"), not bare product names.',
|
|
2460
|
+
),
|
|
2442
2461
|
keyword: z.array(z.string()).optional(),
|
|
2443
|
-
hiring: z.array(z.string()).optional(),
|
|
2462
|
+
hiring: z.array(z.string()).optional().describe("Roles being hired for."),
|
|
2463
|
+
voice_engagement: z
|
|
2464
|
+
.array(z.string())
|
|
2465
|
+
.optional()
|
|
2466
|
+
.describe("LinkedIn profile URLs of market voices whose posts to watch."),
|
|
2467
|
+
company_engagement: z
|
|
2468
|
+
.array(z.string())
|
|
2469
|
+
.optional()
|
|
2470
|
+
.describe("LinkedIn company page URLs of companies and competitors."),
|
|
2471
|
+
brand_engagement: z
|
|
2472
|
+
.array(z.string())
|
|
2473
|
+
.optional()
|
|
2474
|
+
.describe("The sender's own LinkedIn company page URL."),
|
|
2475
|
+
exact: z
|
|
2476
|
+
.array(z.enum(["post_engagement", "keyword"]))
|
|
2477
|
+
.optional()
|
|
2478
|
+
.describe(
|
|
2479
|
+
"Topic signals searched by their exact keywords only (no drafted phrases).",
|
|
2480
|
+
),
|
|
2481
|
+
});
|
|
2482
|
+
const campaignDraftSource = z
|
|
2483
|
+
.enum(["signals", "lookalike", "existing", "crm", "search"])
|
|
2484
|
+
.describe(
|
|
2485
|
+
"How people come in: buying signals, people like one best lead, a pasted list or CSV, CRM records, or a people search.",
|
|
2486
|
+
);
|
|
2487
|
+
const campaignDraftScreen = z.enum([
|
|
2488
|
+
"source",
|
|
2489
|
+
"signals",
|
|
2490
|
+
"lookalike",
|
|
2491
|
+
"target",
|
|
2492
|
+
"messages",
|
|
2493
|
+
"outreach",
|
|
2494
|
+
"review",
|
|
2495
|
+
]);
|
|
2496
|
+
const campaignLookalike = z.object({
|
|
2497
|
+
seed: z.object({
|
|
2498
|
+
name: z.string(),
|
|
2499
|
+
linkedinUrl: z.string(),
|
|
2500
|
+
title: z.string().optional(),
|
|
2501
|
+
companyName: z.string().optional(),
|
|
2502
|
+
companyDomain: z.string().optional(),
|
|
2503
|
+
avatarUrl: z.string().optional(),
|
|
2504
|
+
}),
|
|
2505
|
+
title: z.string(),
|
|
2506
|
+
similarRoles: z.boolean(),
|
|
2507
|
+
roles: z.array(z.string()),
|
|
2508
|
+
location: z.string().optional(),
|
|
2509
|
+
industry: z.string().optional(),
|
|
2510
|
+
relatedIndustries: z.boolean(),
|
|
2511
|
+
industries: z.array(z.string()),
|
|
2512
|
+
companySizes: z.array(z.string()),
|
|
2513
|
+
companies: z.array(z.object({ name: z.string(), domain: z.string() })),
|
|
2444
2514
|
});
|
|
2515
|
+
const campaignOutreachBrief = z.object({
|
|
2516
|
+
goal: z.string().describe("What a reply should lead to, e.g. a demo."),
|
|
2517
|
+
helpWith: z.string().describe("The problem the product solves for them."),
|
|
2518
|
+
tone: z.enum(["conversational", "warm", "direct", "formal"]),
|
|
2519
|
+
channels: z.enum(["both", "email", "linkedin"]),
|
|
2520
|
+
});
|
|
2521
|
+
const campaignDraftStep = z.object({
|
|
2522
|
+
type: z.enum(["email", "wait", "linkedin_connection", "linkedin_message"]),
|
|
2523
|
+
waitHours: z.number().optional(),
|
|
2524
|
+
emailSubject: z.string().optional(),
|
|
2525
|
+
emailTextBody: z.string().optional(),
|
|
2526
|
+
emailReplyToPrevious: z.boolean().optional(),
|
|
2527
|
+
onlyIf: z.enum(["always", "connected", "not_connected"]).optional(),
|
|
2528
|
+
linkedinMessageBody: z.string().optional(),
|
|
2529
|
+
linkedinMessageMode: z
|
|
2530
|
+
.enum(["skip_if_not_connected", "wait_until_connected"])
|
|
2531
|
+
.optional(),
|
|
2532
|
+
connectionTimeoutHours: z.number().optional(),
|
|
2533
|
+
});
|
|
2534
|
+
const campaignDraftSending = z.object({
|
|
2535
|
+
emailMailboxAccountIds: z.array(z.string()),
|
|
2536
|
+
linkedinAccountIds: z.array(z.string()),
|
|
2537
|
+
timezone: z.string(),
|
|
2538
|
+
startHour: z.number().int().min(0).max(24),
|
|
2539
|
+
endHour: z.number().int().min(0).max(24),
|
|
2540
|
+
weekdays: z
|
|
2541
|
+
.array(z.number().int().min(0).max(6))
|
|
2542
|
+
.describe("0 is Sunday, 6 is Saturday."),
|
|
2543
|
+
dailySendCap: z.number().int().min(1).max(100).optional(),
|
|
2544
|
+
});
|
|
2545
|
+
const campaignApprovalMode = z
|
|
2546
|
+
.enum(["auto", "manual"])
|
|
2547
|
+
.describe(
|
|
2548
|
+
"auto: new people a signal finds start the sequence on their own. manual: they wait for approval.",
|
|
2549
|
+
);
|
|
2445
2550
|
const campaignClientRequestId = z
|
|
2446
2551
|
.string()
|
|
2447
2552
|
.trim()
|
|
@@ -3047,18 +3152,30 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
|
|
|
3047
3152
|
status: z.enum(["active", "paused"]).optional(),
|
|
3048
3153
|
cadence: z.enum(["daily", "weekly", "monthly"]).optional(),
|
|
3049
3154
|
findMoreMatching: z.boolean().optional(),
|
|
3155
|
+
approvalMode: campaignApprovalMode.optional(),
|
|
3050
3156
|
targeting: campaignTargeting.optional(),
|
|
3051
3157
|
signal: z
|
|
3052
3158
|
.object({ signal: campaignSignal, enabled: z.boolean() })
|
|
3053
3159
|
.optional(),
|
|
3054
3160
|
terms: z
|
|
3055
3161
|
.object({
|
|
3056
|
-
signal:
|
|
3162
|
+
signal: campaignTermSignal,
|
|
3057
3163
|
values: z.array(z.string()),
|
|
3058
3164
|
})
|
|
3059
3165
|
.optional(),
|
|
3060
3166
|
})
|
|
3061
3167
|
.strict(),
|
|
3168
|
+
"campaigns.sources.run": z.object({ sequenceId: z.string() }).strict(),
|
|
3169
|
+
"campaigns.sources.overview": z.object({ sequenceId: z.string() }).strict(),
|
|
3170
|
+
"campaigns.sources.pending": z.object({ sequenceId: z.string() }).strict(),
|
|
3171
|
+
"campaigns.sources.review": z
|
|
3172
|
+
.object({
|
|
3173
|
+
sequenceId: z.string(),
|
|
3174
|
+
enrollmentIds: z.array(z.string()).min(1),
|
|
3175
|
+
decision: z.enum(["approve", "reject"]),
|
|
3176
|
+
})
|
|
3177
|
+
.strict(),
|
|
3178
|
+
"campaigns.queue": z.object({ sequenceId: z.string() }).strict(),
|
|
3062
3179
|
"campaigns.sources.preview": z
|
|
3063
3180
|
.object({
|
|
3064
3181
|
sequenceId: z.string(),
|
|
@@ -3073,6 +3190,18 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
|
|
|
3073
3190
|
targeting: campaignTargeting,
|
|
3074
3191
|
signals: z.array(campaignSignal),
|
|
3075
3192
|
signalTerms: campaignSignalTerms.optional(),
|
|
3193
|
+
findMoreMatching: z
|
|
3194
|
+
.boolean()
|
|
3195
|
+
.optional()
|
|
3196
|
+
.describe("Also add people who match the audience without a signal."),
|
|
3197
|
+
name: z.string().max(120).optional(),
|
|
3198
|
+
source: campaignDraftSource.optional(),
|
|
3199
|
+
screen: campaignDraftScreen.optional(),
|
|
3200
|
+
lookalike: campaignLookalike
|
|
3201
|
+
.optional()
|
|
3202
|
+
.describe(
|
|
3203
|
+
'For source lookalike: the edited result of builder lookalike, with signals ["lookalike"].',
|
|
3204
|
+
),
|
|
3076
3205
|
})
|
|
3077
3206
|
.strict(),
|
|
3078
3207
|
"campaigns.builder.get": z.object({ draftId: z.string() }).strict(),
|
|
@@ -3082,6 +3211,59 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
|
|
|
3082
3211
|
draftId: z.string(),
|
|
3083
3212
|
name: z.string().max(120).optional(),
|
|
3084
3213
|
timezone: z.string().optional(),
|
|
3214
|
+
paused: z
|
|
3215
|
+
.boolean()
|
|
3216
|
+
.optional()
|
|
3217
|
+
.describe("Save paused: signals wait to run until launch."),
|
|
3218
|
+
})
|
|
3219
|
+
.strict(),
|
|
3220
|
+
"campaigns.builder.drafts": noInput,
|
|
3221
|
+
"campaigns.builder.resume": z.object({ draftId: z.string() }).strict(),
|
|
3222
|
+
"campaigns.builder.discard": z.object({ draftId: z.string() }).strict(),
|
|
3223
|
+
"campaigns.builder.outreach": z
|
|
3224
|
+
.object({
|
|
3225
|
+
draftId: z.string(),
|
|
3226
|
+
outreachMode: z
|
|
3227
|
+
.enum(["ai", "manual"])
|
|
3228
|
+
.optional()
|
|
3229
|
+
.describe(
|
|
3230
|
+
"ai: generate the sequence from the brief. manual: write steps.",
|
|
3231
|
+
),
|
|
3232
|
+
brief: campaignOutreachBrief.optional(),
|
|
3233
|
+
steps: z.array(campaignDraftStep).optional(),
|
|
3234
|
+
sending: campaignDraftSending.optional(),
|
|
3235
|
+
approvalMode: campaignApprovalMode.optional(),
|
|
3236
|
+
})
|
|
3237
|
+
.strict(),
|
|
3238
|
+
"campaigns.builder.sequence": z
|
|
3239
|
+
.object({ draftId: z.string(), brief: campaignOutreachBrief })
|
|
3240
|
+
.strict(),
|
|
3241
|
+
"campaigns.builder.lookalike": z
|
|
3242
|
+
.object({
|
|
3243
|
+
linkedinUrl: z
|
|
3244
|
+
.string()
|
|
3245
|
+
.max(300)
|
|
3246
|
+
.optional()
|
|
3247
|
+
.describe("The best lead's LinkedIn profile URL."),
|
|
3248
|
+
suggest: z
|
|
3249
|
+
.object({ title: z.string(), industry: z.string().optional() })
|
|
3250
|
+
.optional()
|
|
3251
|
+
.describe("Suggest similar roles and related industries again."),
|
|
3252
|
+
rescope: z
|
|
3253
|
+
.object({
|
|
3254
|
+
seed: campaignLookalike.shape.seed,
|
|
3255
|
+
company: z.string(),
|
|
3256
|
+
title: z.string().optional(),
|
|
3257
|
+
location: z.string().optional(),
|
|
3258
|
+
})
|
|
3259
|
+
.optional()
|
|
3260
|
+
.describe("Re-scope the lookalike to a different company."),
|
|
3261
|
+
})
|
|
3262
|
+
.strict(),
|
|
3263
|
+
"campaigns.builder.voices": z
|
|
3264
|
+
.object({
|
|
3265
|
+
topics: z.array(z.string()).optional(),
|
|
3266
|
+
market: z.string().max(300).optional(),
|
|
3085
3267
|
})
|
|
3086
3268
|
.strict(),
|
|
3087
3269
|
"campaigns.builder.opener": z
|
|
@@ -3090,9 +3272,6 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
|
|
|
3090
3272
|
"campaigns.builder.autofill": z
|
|
3091
3273
|
.object({ description: z.string().min(3).max(300) })
|
|
3092
3274
|
.strict(),
|
|
3093
|
-
"campaigns.builder.terms": z
|
|
3094
|
-
.object({ targeting: campaignTargeting })
|
|
3095
|
-
.strict(),
|
|
3096
3275
|
"campaigns.builder.estimate": z
|
|
3097
3276
|
.object({
|
|
3098
3277
|
kind: z.enum(["people", "week"]),
|
|
@@ -5091,6 +5270,11 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
|
|
|
5091
5270
|
"campaigns.sources.signals": z.record(z.string(), z.unknown()).nullable(),
|
|
5092
5271
|
"campaigns.sources.update": z.null(),
|
|
5093
5272
|
"campaigns.sources.preview": z.object({ ok: z.boolean() }).passthrough(),
|
|
5273
|
+
"campaigns.sources.run": z.null(),
|
|
5274
|
+
"campaigns.sources.overview": z.record(z.string(), z.unknown()),
|
|
5275
|
+
"campaigns.sources.pending": z.array(z.record(z.string(), z.unknown())),
|
|
5276
|
+
"campaigns.sources.review": z.record(z.string(), z.unknown()),
|
|
5277
|
+
"campaigns.queue": z.record(z.string(), z.unknown()).nullable(),
|
|
5094
5278
|
"campaigns.builder.profile": z
|
|
5095
5279
|
.object({ hasProfile: z.boolean(), targeting: campaignTargeting })
|
|
5096
5280
|
.passthrough(),
|
|
@@ -5112,12 +5296,24 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
|
|
|
5112
5296
|
enrolled: z.number(),
|
|
5113
5297
|
}),
|
|
5114
5298
|
"campaigns.builder.opener": z.null(),
|
|
5299
|
+
"campaigns.builder.drafts": z.array(z.record(z.string(), z.unknown())),
|
|
5300
|
+
"campaigns.builder.resume": z
|
|
5301
|
+
.object({
|
|
5302
|
+
clientRequestId: z.string(),
|
|
5303
|
+
status: z.string(),
|
|
5304
|
+
targeting: campaignTargeting,
|
|
5305
|
+
signals: z.array(campaignSignal),
|
|
5306
|
+
})
|
|
5307
|
+
.passthrough()
|
|
5308
|
+
.nullable(),
|
|
5309
|
+
"campaigns.builder.discard": z.null(),
|
|
5310
|
+
"campaigns.builder.outreach": z.null(),
|
|
5311
|
+
"campaigns.builder.sequence": z.record(z.string(), z.unknown()),
|
|
5312
|
+
"campaigns.builder.lookalike": z.record(z.string(), z.unknown()),
|
|
5313
|
+
"campaigns.builder.voices": z.record(z.string(), z.unknown()),
|
|
5115
5314
|
"campaigns.builder.autofill": z
|
|
5116
5315
|
.object({ targeting: campaignTargeting })
|
|
5117
5316
|
.passthrough(),
|
|
5118
|
-
"campaigns.builder.terms": z
|
|
5119
|
-
.object({ signalTerms: campaignSignalTerms })
|
|
5120
|
-
.passthrough(),
|
|
5121
5317
|
"campaigns.builder.estimate": z
|
|
5122
5318
|
.object({ count: z.number().nullable() })
|
|
5123
5319
|
.passthrough(),
|
|
@@ -104,14 +104,25 @@ export const APP_SDK_OPERATIONS: readonly AppSdkOperationInfo[] = [
|
|
|
104
104
|
{"id":"campaigns.sources.signals","group":"campaigns","summary":"Read signal choices, suggested terms and current source configuration.","cli":"dench campaign sources signals","method":"GET","path":"/campaigns/{sequenceId}/sources/signals","permission":"campaigns:read","pathParams":["sequenceId"]},
|
|
105
105
|
{"id":"campaigns.sources.update","group":"campaigns","summary":"Update source cadence, pause/resume, targeting, signals or terms. Active sources may perform billable future work.","cli":"dench campaign sources set","method":"PATCH","path":"/campaigns/{sequenceId}/sources","permission":"campaigns:write","pathParams":["sequenceId"]},
|
|
106
106
|
{"id":"campaigns.sources.preview","group":"campaigns","summary":"Preview a signal source using its current audience. Provider credits may apply; does not enroll people.","cli":"dench campaign sources preview","method":"POST","path":"/campaigns/{sequenceId}/sources/preview","permission":"campaigns:write","pathParams":["sequenceId"]},
|
|
107
|
+
{"id":"campaigns.sources.run","group":"campaigns","summary":"Run a live campaign's signals now instead of waiting for the schedule (Run now). Provider credits apply; limited to 6 runs an hour.","cli":"dench campaign sources run","method":"POST","path":"/campaigns/{sequenceId}/sources/run","permission":"campaigns:write","pathParams":["sequenceId"]},
|
|
108
|
+
{"id":"campaigns.sources.overview","group":"campaigns","summary":"Read the Sources tab: each signal's status, last and next run, people found and anything needing attention.","cli":"dench campaign sources overview","method":"GET","path":"/campaigns/{sequenceId}/sources/overview","permission":"campaigns:read","pathParams":["sequenceId"]},
|
|
109
|
+
{"id":"campaigns.sources.pending","group":"campaigns","summary":"List people a signal found who are waiting for approval (manual approval mode).","cli":"dench campaign sources pending","method":"GET","path":"/campaigns/{sequenceId}/sources/pending","permission":"campaigns:read","pathParams":["sequenceId"]},
|
|
110
|
+
{"id":"campaigns.sources.review","group":"campaigns","summary":"Approve people waiting for review (they start the sequence) or reject them.","cli":"dench campaign sources approve|reject","method":"POST","path":"/campaigns/{sequenceId}/sources/review","permission":"campaigns:write","pathParams":["sequenceId"]},
|
|
111
|
+
{"id":"campaigns.queue","group":"campaigns","summary":"Today's queue: who the campaign plans to contact today, warmest first, within the senders' limits.","cli":"dench campaign queue","method":"GET","path":"/campaigns/{sequenceId}/queue","permission":"campaigns:read","pathParams":["sequenceId"]},
|
|
107
112
|
{"id":"campaigns.builder.profile","group":"campaigns","summary":"Read the company profile and suggested campaign audience.","cli":"dench campaign builder profile","method":"GET","path":"/campaigns/builder/profile","permission":"campaigns:read","pathParams":[]},
|
|
108
113
|
{"id":"campaigns.builder.save","group":"campaigns","summary":"Save a signal campaign's audience, signals and terms idempotently. Does not start a build.","cli":"dench campaign builder save","method":"POST","path":"/campaigns/builder/drafts","permission":"campaigns:write","pathParams":[]},
|
|
109
114
|
{"id":"campaigns.builder.get","group":"campaigns","summary":"Read signal build progress, preview people, messages and errors.","cli":"dench campaign builder get","method":"GET","path":"/campaigns/builder/drafts/{draftId}","permission":"campaigns:read","pathParams":["draftId"]},
|
|
110
115
|
{"id":"campaigns.builder.build","group":"campaigns","summary":"Start the same billable signal/audience/message build used by the UI; poll the draft for progress.","cli":"dench campaign builder build","method":"POST","path":"/campaigns/builder/drafts/{draftId}/build","permission":"campaigns:write","pathParams":["draftId"]},
|
|
111
116
|
{"id":"campaigns.builder.create","group":"campaigns","summary":"Create a campaign from a reviewed signal draft. Does not launch the campaign.","cli":"dench campaign builder create","method":"POST","path":"/campaigns/builder/drafts/{draftId}/create","permission":"campaigns:write","pathParams":["draftId"]},
|
|
112
117
|
{"id":"campaigns.builder.opener","group":"campaigns","summary":"Edit or reset one preview person's opening line before creating the campaign.","cli":"dench campaign builder opener","method":"PUT","path":"/campaigns/builder/drafts/{draftId}/people/{leadId}/opener","permission":"campaigns:write","pathParams":["draftId","leadId"]},
|
|
118
|
+
{"id":"campaigns.builder.drafts","group":"campaigns","summary":"List saved, unfinished campaign drafts that can be resumed.","cli":"dench campaign builder drafts","method":"GET","path":"/campaigns/builder/drafts","permission":"campaigns:read","pathParams":[]},
|
|
119
|
+
{"id":"campaigns.builder.resume","group":"campaigns","summary":"Read a saved draft's setup (source, name, targeting, signals, terms, lookalike) to resume it.","cli":"dench campaign builder resume","method":"GET","path":"/campaigns/builder/drafts/{draftId}/setup","permission":"campaigns:read","pathParams":["draftId"]},
|
|
120
|
+
{"id":"campaigns.builder.discard","group":"campaigns","summary":"Discard a saved draft that has not become a campaign.","cli":"dench campaign builder discard","method":"DELETE","path":"/campaigns/builder/drafts/{draftId}","permission":"campaigns:write","pathParams":["draftId"]},
|
|
121
|
+
{"id":"campaigns.builder.outreach","group":"campaigns","summary":"Save a draft's Outreach and Review choices: AI or manual steps, the brief, the steps, who sends and when, and auto or manual approval.","cli":"dench campaign builder outreach","method":"PATCH","path":"/campaigns/builder/drafts/{draftId}/outreach","permission":"campaigns:write","pathParams":["draftId"]},
|
|
122
|
+
{"id":"campaigns.builder.sequence","group":"campaigns","summary":"Generate the draft's sequence from a brief (goal, problem, tone, channels), as Generate sequence does. AI credits apply.","cli":"dench campaign builder sequence","method":"POST","path":"/campaigns/builder/drafts/{draftId}/sequence","permission":"campaigns:write","pathParams":["draftId"]},
|
|
123
|
+
{"id":"campaigns.builder.lookalike","group":"campaigns","summary":"Set up a lookalike from the best lead's LinkedIn URL (role, similar roles, place, industry, similar companies), suggest roles again, or re-scope to a company. Nothing is saved. Provider credits apply.","cli":"dench campaign builder lookalike","method":"POST","path":"/campaigns/builder/lookalike","permission":"campaigns:write","pathParams":[]},
|
|
124
|
+
{"id":"campaigns.builder.voices","group":"campaigns","summary":"Suggest market voices (LinkedIn experts) for the campaign's topics, to watch with voice_engagement. AI credits apply.","cli":"dench campaign builder voices","method":"POST","path":"/campaigns/builder/voices","permission":"campaigns:write","pathParams":[]},
|
|
113
125
|
{"id":"campaigns.builder.autofill","group":"campaigns","summary":"Turn an audience description into targeting filters. AI credits apply.","cli":"dench campaign builder autofill","method":"POST","path":"/campaigns/builder/autofill","permission":"campaigns:write","pathParams":[]},
|
|
114
|
-
{"id":"campaigns.builder.terms","group":"campaigns","summary":"Suggest signal terms for the selected audience. AI credits apply.","cli":"dench campaign builder terms","method":"POST","path":"/campaigns/builder/terms","permission":"campaigns:write","pathParams":[]},
|
|
115
126
|
{"id":"campaigns.builder.estimate","group":"campaigns","summary":"Estimate audience or weekly signal volume using the UI's cached estimator. Provider credits may apply.","cli":"dench campaign builder estimate","method":"POST","path":"/campaigns/builder/estimate","permission":"campaigns:write","pathParams":[]},
|
|
116
127
|
{"id":"commands.list","group":"meta","summary":"List all CLI/API operations and parity classifications.","cli":"dench help","method":"GET","path":"/commands","permission":null,"pathParams":[]},
|
|
117
128
|
{"id":"auth.signin.create","group":"auth","summary":"Create an agent sign-in request.","cli":"dench signin","method":"POST","path":"/auth/signin-requests","permission":"auth:write","pathParams":[]},
|
|
@@ -607,7 +618,17 @@ export const APP_SDK_METHODS: Readonly<Record<string, AppSdkMethodInfo>> = {
|
|
|
607
618
|
"campaigns.createFromDraft": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.create","doc":"Create a campaign from a reviewed signal draft, without launching."},
|
|
608
619
|
"campaigns.setDraftOpener": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.opener","doc":"Edit a preview person's opening line."},
|
|
609
620
|
"campaigns.autofill": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.autofill","doc":"Translate a description into targeting. AI credits apply."},
|
|
610
|
-
"campaigns.
|
|
621
|
+
"campaigns.savedDrafts": {"kind":"op","permission":"campaigns:read","op":"campaigns.builder.drafts","doc":"List saved, unfinished campaign drafts."},
|
|
622
|
+
"campaigns.resumeDraft": {"kind":"op","permission":"campaigns:read","op":"campaigns.builder.resume","doc":"Read a saved draft's setup to resume it."},
|
|
623
|
+
"campaigns.discardDraft": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.discard","doc":"Discard a saved draft."},
|
|
624
|
+
"campaigns.saveOutreach": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.outreach","doc":"Save a draft's steps, brief, senders, schedule and approval mode."},
|
|
625
|
+
"campaigns.generateSequence": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.sequence","doc":"Generate a draft's sequence from a brief. AI credits apply."},
|
|
626
|
+
"campaigns.lookalike": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.lookalike","doc":"Set up a lookalike from a best lead's LinkedIn URL. Provider credits apply."},
|
|
627
|
+
"campaigns.suggestVoices": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.voices","doc":"Suggest LinkedIn experts to watch for the topics. AI credits apply."},
|
|
628
|
+
"campaigns.runSources": {"kind":"op","permission":"campaigns:write","op":"campaigns.sources.run","doc":"Run a live campaign's signals now. Provider credits apply."},
|
|
629
|
+
"campaigns.pendingReview": {"kind":"op","permission":"campaigns:read","op":"campaigns.sources.pending","doc":"People a signal found who wait for approval."},
|
|
630
|
+
"campaigns.reviewPeople": {"kind":"op","permission":"campaigns:write","op":"campaigns.sources.review","doc":"Approve or reject people waiting for review."},
|
|
631
|
+
"campaigns.todayQueue": {"kind":"op","permission":"campaigns:read","op":"campaigns.queue","doc":"Who the campaign plans to contact today."},
|
|
611
632
|
"campaigns.estimate": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.estimate","doc":"Estimate audience or signal volume."},
|
|
612
633
|
"campaigns.mailboxes": {"kind":"op","permission":"campaigns:read","op":"campaigns.accounts.email","doc":"Mailboxes campaigns can send from, with warm-up and daily cap."},
|
|
613
634
|
"campaigns.linkedinAccounts": {"kind":"op","permission":"campaigns:read","op":"campaigns.accounts.linkedin","doc":"LinkedIn accounts campaigns can send from, with capacity."},
|
package/lib/app-sdk-surface.ts
CHANGED
|
@@ -725,11 +725,71 @@ export const APP_SDK_SURFACE: AppSdkSurface = {
|
|
|
725
725
|
args: `opts: ${A("campaigns.builder.autofill")}`,
|
|
726
726
|
returns: R("campaigns.builder.autofill"),
|
|
727
727
|
},
|
|
728
|
-
|
|
729
|
-
op: "campaigns.builder.
|
|
730
|
-
doc: "
|
|
731
|
-
args:
|
|
732
|
-
returns: R("campaigns.builder.
|
|
728
|
+
savedDrafts: {
|
|
729
|
+
op: "campaigns.builder.drafts",
|
|
730
|
+
doc: "List saved, unfinished campaign drafts.",
|
|
731
|
+
args: "",
|
|
732
|
+
returns: R("campaigns.builder.drafts"),
|
|
733
|
+
},
|
|
734
|
+
resumeDraft: {
|
|
735
|
+
op: "campaigns.builder.resume",
|
|
736
|
+
doc: "Read a saved draft's setup to resume it.",
|
|
737
|
+
args: "draftId: string",
|
|
738
|
+
returns: R("campaigns.builder.resume"),
|
|
739
|
+
},
|
|
740
|
+
discardDraft: {
|
|
741
|
+
op: "campaigns.builder.discard",
|
|
742
|
+
doc: "Discard a saved draft.",
|
|
743
|
+
args: "draftId: string",
|
|
744
|
+
returns: R("campaigns.builder.discard"),
|
|
745
|
+
},
|
|
746
|
+
saveOutreach: {
|
|
747
|
+
op: "campaigns.builder.outreach",
|
|
748
|
+
doc: "Save a draft's steps, brief, senders, schedule and approval mode.",
|
|
749
|
+
args: `opts: ${A("campaigns.builder.outreach")}`,
|
|
750
|
+
returns: R("campaigns.builder.outreach"),
|
|
751
|
+
},
|
|
752
|
+
generateSequence: {
|
|
753
|
+
op: "campaigns.builder.sequence",
|
|
754
|
+
doc: "Generate a draft's sequence from a brief. AI credits apply.",
|
|
755
|
+
args: `opts: ${A("campaigns.builder.sequence")}`,
|
|
756
|
+
returns: R("campaigns.builder.sequence"),
|
|
757
|
+
},
|
|
758
|
+
lookalike: {
|
|
759
|
+
op: "campaigns.builder.lookalike",
|
|
760
|
+
doc: "Set up a lookalike from a best lead's LinkedIn URL. Provider credits apply.",
|
|
761
|
+
args: `opts: ${A("campaigns.builder.lookalike")}`,
|
|
762
|
+
returns: R("campaigns.builder.lookalike"),
|
|
763
|
+
},
|
|
764
|
+
suggestVoices: {
|
|
765
|
+
op: "campaigns.builder.voices",
|
|
766
|
+
doc: "Suggest LinkedIn experts to watch for the topics. AI credits apply.",
|
|
767
|
+
args: `opts: ${A("campaigns.builder.voices")}`,
|
|
768
|
+
returns: R("campaigns.builder.voices"),
|
|
769
|
+
},
|
|
770
|
+
runSources: {
|
|
771
|
+
op: "campaigns.sources.run",
|
|
772
|
+
doc: "Run a live campaign's signals now. Provider credits apply.",
|
|
773
|
+
args: "sequenceId: string",
|
|
774
|
+
returns: R("campaigns.sources.run"),
|
|
775
|
+
},
|
|
776
|
+
pendingReview: {
|
|
777
|
+
op: "campaigns.sources.pending",
|
|
778
|
+
doc: "People a signal found who wait for approval.",
|
|
779
|
+
args: "sequenceId: string",
|
|
780
|
+
returns: R("campaigns.sources.pending"),
|
|
781
|
+
},
|
|
782
|
+
reviewPeople: {
|
|
783
|
+
op: "campaigns.sources.review",
|
|
784
|
+
doc: "Approve or reject people waiting for review.",
|
|
785
|
+
args: `opts: ${A("campaigns.sources.review")}`,
|
|
786
|
+
returns: R("campaigns.sources.review"),
|
|
787
|
+
},
|
|
788
|
+
todayQueue: {
|
|
789
|
+
op: "campaigns.queue",
|
|
790
|
+
doc: "Who the campaign plans to contact today.",
|
|
791
|
+
args: "sequenceId: string",
|
|
792
|
+
returns: R("campaigns.queue"),
|
|
733
793
|
},
|
|
734
794
|
estimate: {
|
|
735
795
|
op: "campaigns.builder.estimate",
|
package/lib/campaign-agent.ts
CHANGED
|
@@ -1501,6 +1501,12 @@ export async function runCampaignAgentCommand(
|
|
|
1501
1501
|
searchSummary(page),
|
|
1502
1502
|
);
|
|
1503
1503
|
}
|
|
1504
|
+
if (command === "queue") {
|
|
1505
|
+
const reference = shift(ctx.args, "campaign id or name");
|
|
1506
|
+
finishArgs(ctx.args, "queue");
|
|
1507
|
+
const sequenceId = (await ctx.resolveCampaign(reference))._id;
|
|
1508
|
+
return print(ctx, await invoke(ctx, "campaigns.queue", { sequenceId }));
|
|
1509
|
+
}
|
|
1504
1510
|
const verb = shift(ctx.args, `${command} subcommand`);
|
|
1505
1511
|
const input = await objectInput(ctx.args);
|
|
1506
1512
|
if (command === "sources") {
|
|
@@ -1509,12 +1515,26 @@ export async function runCampaignAgentCommand(
|
|
|
1509
1515
|
signals: "signals",
|
|
1510
1516
|
set: "update",
|
|
1511
1517
|
preview: "preview",
|
|
1518
|
+
run: "run",
|
|
1519
|
+
overview: "overview",
|
|
1520
|
+
pending: "pending",
|
|
1521
|
+
approve: "review",
|
|
1522
|
+
reject: "review",
|
|
1512
1523
|
};
|
|
1513
1524
|
const operation = operations[verb];
|
|
1514
1525
|
if (!operation)
|
|
1515
|
-
throw new Error(
|
|
1526
|
+
throw new Error(
|
|
1527
|
+
"Use campaign sources get|signals|overview|set|preview|run|pending|approve|reject",
|
|
1528
|
+
);
|
|
1516
1529
|
const signal = value(ctx.args, "--signal");
|
|
1517
1530
|
if (signal) input.signal = signal;
|
|
1531
|
+
const approval = value(ctx.args, "--approval");
|
|
1532
|
+
if (approval) input.approvalMode = approval;
|
|
1533
|
+
if (operation === "review") {
|
|
1534
|
+
input.decision = verb;
|
|
1535
|
+
const people = value(ctx.args, "--people");
|
|
1536
|
+
if (people) input.enrollmentIds = splitListValue(people);
|
|
1537
|
+
}
|
|
1518
1538
|
const reference = shift(ctx.args, "campaign id or name");
|
|
1519
1539
|
finishArgs(ctx.args, "sources");
|
|
1520
1540
|
validate(`campaigns.sources.${operation}`, {
|
|
@@ -1538,12 +1558,18 @@ export async function runCampaignAgentCommand(
|
|
|
1538
1558
|
"create",
|
|
1539
1559
|
"opener",
|
|
1540
1560
|
"autofill",
|
|
1541
|
-
"terms",
|
|
1542
1561
|
"estimate",
|
|
1562
|
+
"drafts",
|
|
1563
|
+
"resume",
|
|
1564
|
+
"discard",
|
|
1565
|
+
"outreach",
|
|
1566
|
+
"sequence",
|
|
1567
|
+
"lookalike",
|
|
1568
|
+
"voices",
|
|
1543
1569
|
]);
|
|
1544
1570
|
if (!verbs.has(verb))
|
|
1545
1571
|
throw new Error(
|
|
1546
|
-
"Use campaign builder profile|save|get|build|create|
|
|
1572
|
+
"Use campaign builder profile|autofill|estimate|lookalike|voices|save|get|build|opener|outreach|sequence|create|drafts|resume|discard",
|
|
1547
1573
|
);
|
|
1548
1574
|
const name = value(ctx.args, "--name");
|
|
1549
1575
|
const timezone = value(ctx.args, "--timezone");
|
|
@@ -1553,7 +1579,46 @@ export async function runCampaignAgentCommand(
|
|
|
1553
1579
|
if (timezone) input.timezone = timezone;
|
|
1554
1580
|
if (description) input.description = description;
|
|
1555
1581
|
if (text !== undefined) input.opener = text;
|
|
1556
|
-
if (
|
|
1582
|
+
if (verb === "create" && hasFlag(ctx.args, "--paused")) input.paused = true;
|
|
1583
|
+
if (verb === "lookalike") {
|
|
1584
|
+
const url = value(ctx.args, "--linkedin-url");
|
|
1585
|
+
if (url) input.linkedinUrl = url;
|
|
1586
|
+
}
|
|
1587
|
+
if (verb === "voices") {
|
|
1588
|
+
const topics = value(ctx.args, "--topics");
|
|
1589
|
+
const market = value(ctx.args, "--market");
|
|
1590
|
+
if (topics) input.topics = splitListValue(topics);
|
|
1591
|
+
if (market) input.market = market;
|
|
1592
|
+
}
|
|
1593
|
+
if (verb === "outreach") {
|
|
1594
|
+
const approval = value(ctx.args, "--approval");
|
|
1595
|
+
const mode = value(ctx.args, "--mode");
|
|
1596
|
+
if (approval) input.approvalMode = approval;
|
|
1597
|
+
if (mode) input.outreachMode = mode;
|
|
1598
|
+
}
|
|
1599
|
+
if (verb === "sequence" && !input.brief) {
|
|
1600
|
+
const brief = {
|
|
1601
|
+
goal: value(ctx.args, "--goal"),
|
|
1602
|
+
helpWith: value(ctx.args, "--help-with"),
|
|
1603
|
+
tone: value(ctx.args, "--tone") ?? "conversational",
|
|
1604
|
+
channels: value(ctx.args, "--channels") ?? "both",
|
|
1605
|
+
};
|
|
1606
|
+
if (brief.goal || brief.helpWith)
|
|
1607
|
+
input.brief = { ...brief, helpWith: brief.helpWith ?? "" };
|
|
1608
|
+
}
|
|
1609
|
+
if (
|
|
1610
|
+
[
|
|
1611
|
+
"get",
|
|
1612
|
+
"build",
|
|
1613
|
+
"create",
|
|
1614
|
+
"opener",
|
|
1615
|
+
"resume",
|
|
1616
|
+
"discard",
|
|
1617
|
+
"outreach",
|
|
1618
|
+
"sequence",
|
|
1619
|
+
].includes(verb) &&
|
|
1620
|
+
!input.draftId
|
|
1621
|
+
)
|
|
1557
1622
|
input.draftId = shift(ctx.args, "draft id");
|
|
1558
1623
|
if (verb === "opener" && !input.leadId)
|
|
1559
1624
|
input.leadId = shift(ctx.args, "draft lead id");
|