@dench.com/cli 3.8.0-staging.389 → 3.8.1-staging.390
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 +132 -27
- package/campaign.ts +335 -54
- package/chat.ts +2 -4
- package/lib/api-schemas.ts +186 -20
- package/lib/app-sdk-catalog.generated.ts +3 -3
- package/lib/campaign-steps.ts +474 -12
- package/lib/campaign-timeline.ts +69 -4
- package/lib/command-registry.ts +3 -3
- package/lib/references.generated.json +14 -14
- package/package.json +1 -1
package/lib/campaign-timeline.ts
CHANGED
|
@@ -8,6 +8,8 @@ export type TimelineRun = {
|
|
|
8
8
|
startedAt?: number;
|
|
9
9
|
waitUntil?: number;
|
|
10
10
|
retryAt?: number;
|
|
11
|
+
/** A wait for acceptance: when it stops waiting either way. */
|
|
12
|
+
timeoutAt?: number;
|
|
11
13
|
completedAt?: number;
|
|
12
14
|
emailMessageId?: string;
|
|
13
15
|
lastError?: string;
|
|
@@ -49,6 +51,8 @@ export type TimelineResult = {
|
|
|
49
51
|
type: string;
|
|
50
52
|
order: number;
|
|
51
53
|
waitHours?: number;
|
|
54
|
+
/** If / else and Wait until they accept steps: what they check. */
|
|
55
|
+
label?: string;
|
|
52
56
|
}>;
|
|
53
57
|
runs: TimelineRun[];
|
|
54
58
|
emailMessages: TimelineEmail[];
|
|
@@ -63,6 +67,10 @@ export type TimelineStep = {
|
|
|
63
67
|
outcome: string;
|
|
64
68
|
at: number | null;
|
|
65
69
|
detail: string[];
|
|
70
|
+
/** An If / else or a wait for acceptance: the path it took. */
|
|
71
|
+
branch?: "yes" | "no";
|
|
72
|
+
/** Still waiting for them to accept: when it stops waiting. */
|
|
73
|
+
acceptanceDeadline?: number;
|
|
66
74
|
subject?: string;
|
|
67
75
|
text?: string;
|
|
68
76
|
error?: string;
|
|
@@ -101,16 +109,66 @@ const EMAIL_STATUS: Record<string, string> = {
|
|
|
101
109
|
failed: "could not send",
|
|
102
110
|
};
|
|
103
111
|
|
|
112
|
+
function resultField(result: unknown, key: string): unknown {
|
|
113
|
+
return result && typeof result === "object"
|
|
114
|
+
? (result as Record<string, unknown>)[key]
|
|
115
|
+
: undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
104
118
|
function skipReason(result: unknown): string {
|
|
105
|
-
const reason =
|
|
106
|
-
result && typeof result === "object"
|
|
107
|
-
? (result as { reason?: unknown }).reason
|
|
108
|
-
: undefined;
|
|
119
|
+
const reason = resultField(result, "reason");
|
|
109
120
|
return (typeof reason === "string" && SKIP_REASONS[reason]) || "Skipped";
|
|
110
121
|
}
|
|
111
122
|
|
|
123
|
+
/** Why an If / else or a wait for acceptance took its No path. */
|
|
124
|
+
const NO_REASONS: Record<string, string> = {
|
|
125
|
+
missing_linkedin_identity: "no LinkedIn profile",
|
|
126
|
+
missing_email_identity: "no email address",
|
|
127
|
+
linkedin_connection_timeout: "they did not accept in time",
|
|
128
|
+
linkedin_invite_withdrawn: "the connection request was withdrawn",
|
|
129
|
+
linkedin_invite_not_sent: "no connection request went out",
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const LOGIC_TYPES = new Set(["condition", "wait_for_connection"]);
|
|
133
|
+
|
|
134
|
+
export function branchOf(run: TimelineRun): "yes" | "no" | undefined {
|
|
135
|
+
if (!LOGIC_TYPES.has(run.type) || run.status !== "sent") return undefined;
|
|
136
|
+
const branch = resultField(run.result, "branch");
|
|
137
|
+
return branch === "yes" || branch === "no" ? branch : undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** What the editor calls each exit: Yes / No, or Accepted / Not accepted. */
|
|
141
|
+
export function branchExitLabel(type: string, branch: "yes" | "no"): string {
|
|
142
|
+
if (type === "wait_for_connection")
|
|
143
|
+
return branch === "yes" ? "Accepted" : "Not accepted";
|
|
144
|
+
return branch === "yes" ? "Yes" : "No";
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function logicOutcome(run: TimelineRun): string | undefined {
|
|
148
|
+
if (!LOGIC_TYPES.has(run.type)) return undefined;
|
|
149
|
+
const branch = branchOf(run);
|
|
150
|
+
if (branch) {
|
|
151
|
+
const reason = resultField(run.result, "reason");
|
|
152
|
+
const why =
|
|
153
|
+
branch === "no" && typeof reason === "string" ? NO_REASONS[reason] : "";
|
|
154
|
+
return `${branchExitLabel(run.type, branch)}${why ? ` (${why})` : ""}`;
|
|
155
|
+
}
|
|
156
|
+
if (run.status === "sent") return "Done";
|
|
157
|
+
if (run.type === "wait_for_connection")
|
|
158
|
+
return run.status === "waiting"
|
|
159
|
+
? "Waiting for them to accept"
|
|
160
|
+
: run.status === "queued" || run.status === "running"
|
|
161
|
+
? "Checking whether they accepted"
|
|
162
|
+
: undefined;
|
|
163
|
+
return ["queued", "running", "waiting"].includes(run.status)
|
|
164
|
+
? "Checking"
|
|
165
|
+
: undefined;
|
|
166
|
+
}
|
|
167
|
+
|
|
112
168
|
function outcomeOf(run: TimelineRun): string {
|
|
113
169
|
if (run.status === "skipped") return skipReason(run.result);
|
|
170
|
+
const logic = logicOutcome(run);
|
|
171
|
+
if (logic) return logic;
|
|
114
172
|
if (run.status === "sent") {
|
|
115
173
|
if (run.type === "wait") return "Waited";
|
|
116
174
|
if (run.type === "linkedin_visit") return "Visited";
|
|
@@ -153,6 +211,11 @@ export function timelineSteps(result: TimelineResult): TimelineStep[] {
|
|
|
153
211
|
run.status === "sent" || run.status === "skipped"
|
|
154
212
|
? undefined
|
|
155
213
|
: (run.lastError ?? email?.lastError)?.trim();
|
|
214
|
+
const branch = branchOf(run);
|
|
215
|
+
const waitingForAccept =
|
|
216
|
+
run.type === "wait_for_connection" &&
|
|
217
|
+
run.status === "waiting" &&
|
|
218
|
+
typeof run.timeoutAt === "number";
|
|
156
219
|
return {
|
|
157
220
|
step: (step?.order ?? run.stepIndex) + 1,
|
|
158
221
|
stepId: String(run.stepId),
|
|
@@ -167,6 +230,8 @@ export function timelineSteps(result: TimelineResult): TimelineStep[] {
|
|
|
167
230
|
run.scheduledAt ??
|
|
168
231
|
null,
|
|
169
232
|
detail,
|
|
233
|
+
...(branch ? { branch } : {}),
|
|
234
|
+
...(waitingForAccept ? { acceptanceDeadline: run.timeoutAt } : {}),
|
|
170
235
|
...(email?.subject ? { subject: email.subject } : {}),
|
|
171
236
|
...(text ? { text } : {}),
|
|
172
237
|
...(problem ? { error: problem } : {}),
|
package/lib/command-registry.ts
CHANGED
|
@@ -4183,7 +4183,7 @@ const rawApiOperations = [
|
|
|
4183
4183
|
id: "campaigns.analytics",
|
|
4184
4184
|
group: "campaigns",
|
|
4185
4185
|
summary:
|
|
4186
|
-
"Campaign funnel: enrolled, contacted, invited, accepted, messaged, replied, opens and clicks.",
|
|
4186
|
+
"Campaign funnel: enrolled, contacted, invited, accepted, messaged, replied, opens and clicks, and for a workflow how many people took each If / else and Wait for them to accept path (branchStats).",
|
|
4187
4187
|
cli: "dench campaign get",
|
|
4188
4188
|
method: "GET",
|
|
4189
4189
|
path: "/campaigns/{sequenceId}/analytics",
|
|
@@ -4237,7 +4237,7 @@ const rawApiOperations = [
|
|
|
4237
4237
|
id: "campaigns.steps.replace",
|
|
4238
4238
|
group: "campaigns",
|
|
4239
4239
|
summary:
|
|
4240
|
-
"Replace every step of a draft campaign (email, wait, LinkedIn visit, connection request, message, post reaction, InMail). Steps are checked first: a field the step type would drop, missing text
|
|
4240
|
+
"Replace every step of a draft campaign (email, wait, LinkedIn visit, connection request, message, post reaction, InMail, If / else, Wait for them to accept). A workflow (stepModel workflow, or steps with key, next, yesNext or noNext) branches: condition and wait_for_connection steps continue on yesNext or noNext, other steps on next; each points at another step's key or end. Steps are checked first: a field the step type would drop, missing text, {TOKEN} single braces or paths that don't hold together are rejected, not saved. Configure sender pools on the campaign; step sender ids are legacy-only.",
|
|
4241
4241
|
cli: "dench campaign steps set",
|
|
4242
4242
|
method: "PUT",
|
|
4243
4243
|
path: "/campaigns/{sequenceId}/steps",
|
|
@@ -4249,7 +4249,7 @@ const rawApiOperations = [
|
|
|
4249
4249
|
id: "campaigns.steps.update",
|
|
4250
4250
|
group: "campaigns",
|
|
4251
4251
|
summary:
|
|
4252
|
-
"Edit the text, waits and conditions of a launched (or paused) campaign's steps in place. Each step keeps its id, type and place; pass the stepId and the updatedAt you read, and only the fields you change. Messages already sent or being prepared keep their text; drafts use campaigns.steps.replace.",
|
|
4252
|
+
"Edit the text, waits and conditions of a launched (or paused) campaign's steps in place. Each step keeps its id, type and place; pass the stepId and the updatedAt you read, and only the fields you change. A workflow keeps its paths and what each If / else checks (key, next, yesNext, noNext, check, conditionField, conditionOperator); conditionValue and acceptance waits stay editable. Messages already sent or being prepared keep their text; drafts use campaigns.steps.replace.",
|
|
4253
4253
|
cli: "dench campaign steps edit",
|
|
4254
4254
|
method: "PATCH",
|
|
4255
4255
|
path: "/campaigns/{sequenceId}/steps",
|