crewly 1.20.127 → 1.20.129
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/config/templates/growth-marketing-team.json +12 -1
- package/config/templates/personal-assistant-team.json +28 -0
- package/dist/backend/backend/src/constants.d.ts +75 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +81 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts +49 -0
- package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat/chat.controller.js +135 -106
- package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/onboarding/index.d.ts +1 -0
- package/dist/backend/backend/src/controllers/onboarding/index.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/onboarding/index.js +1 -0
- package/dist/backend/backend/src/controllers/onboarding/index.js.map +1 -1
- package/dist/backend/backend/src/controllers/onboarding/onboarding-checklist.controller.d.ts +38 -0
- package/dist/backend/backend/src/controllers/onboarding/onboarding-checklist.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/onboarding/onboarding-checklist.controller.js +106 -0
- package/dist/backend/backend/src/controllers/onboarding/onboarding-checklist.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/onboarding/onboarding-checklist.routes.d.ts +24 -0
- package/dist/backend/backend/src/controllers/onboarding/onboarding-checklist.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/onboarding/onboarding-checklist.routes.js +34 -0
- package/dist/backend/backend/src/controllers/onboarding/onboarding-checklist.routes.js.map +1 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +48 -1
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +5 -0
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.d.ts +33 -0
- package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.js +63 -0
- package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/relogin-agent-resumer.service.d.ts +76 -0
- package/dist/backend/backend/src/services/agent/relogin-agent-resumer.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/agent/relogin-agent-resumer.service.js +108 -0
- package/dist/backend/backend/src/services/agent/relogin-agent-resumer.service.js.map +1 -0
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js +10 -0
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js.map +1 -1
- package/dist/backend/backend/src/services/harness/harness-relogin.service.d.ts +361 -0
- package/dist/backend/backend/src/services/harness/harness-relogin.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-relogin.service.js +756 -0
- package/dist/backend/backend/src/services/harness/harness-relogin.service.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness.service.d.ts +16 -1
- package/dist/backend/backend/src/services/harness/harness.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/harness/harness.service.js +28 -1
- package/dist/backend/backend/src/services/harness/harness.service.js.map +1 -1
- package/dist/backend/backend/src/services/harness/harness.types.d.ts +16 -1
- package/dist/backend/backend/src/services/harness/harness.types.d.ts.map +1 -1
- package/dist/backend/backend/src/services/harness/harness.types.js.map +1 -1
- package/dist/backend/backend/src/services/harness/login-expiry-rules.d.ts +75 -0
- package/dist/backend/backend/src/services/harness/login-expiry-rules.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/login-expiry-rules.js +159 -0
- package/dist/backend/backend/src/services/harness/login-expiry-rules.js.map +1 -0
- package/dist/backend/backend/src/services/onboarding/onboarding-checklist.factory.d.ts +45 -0
- package/dist/backend/backend/src/services/onboarding/onboarding-checklist.factory.d.ts.map +1 -0
- package/dist/backend/backend/src/services/onboarding/onboarding-checklist.factory.js +103 -0
- package/dist/backend/backend/src/services/onboarding/onboarding-checklist.factory.js.map +1 -0
- package/dist/backend/backend/src/services/onboarding/onboarding-checklist.service.d.ts +301 -0
- package/dist/backend/backend/src/services/onboarding/onboarding-checklist.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/onboarding/onboarding-checklist.service.js +365 -0
- package/dist/backend/backend/src/services/onboarding/onboarding-checklist.service.js.map +1 -0
- package/dist/backend/backend/src/services/onboarding/onboarding-state.store.d.ts +89 -0
- package/dist/backend/backend/src/services/onboarding/onboarding-state.store.d.ts.map +1 -0
- package/dist/backend/backend/src/services/onboarding/onboarding-state.store.js +122 -0
- package/dist/backend/backend/src/services/onboarding/onboarding-state.store.js.map +1 -0
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +21 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +39 -0
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.d.ts +80 -0
- package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.js +127 -0
- package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.js.map +1 -0
- package/dist/backend/backend/src/services/template/template.service.d.ts +15 -1
- package/dist/backend/backend/src/services/template/template.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/template/template.service.js +22 -1
- package/dist/backend/backend/src/services/template/template.service.js.map +1 -1
- package/dist/backend/backend/src/types/team-template.types.d.ts +30 -0
- package/dist/backend/backend/src/types/team-template.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/team-template.types.js +24 -0
- package/dist/backend/backend/src/types/team-template.types.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +75 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +81 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.d.ts +33 -0
- package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.js +63 -0
- package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.js.map +1 -1
- package/dist/cli/backend/src/services/harness/harness-relogin.service.d.ts +361 -0
- package/dist/cli/backend/src/services/harness/harness-relogin.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-relogin.service.js +756 -0
- package/dist/cli/backend/src/services/harness/harness-relogin.service.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness.service.d.ts +16 -1
- package/dist/cli/backend/src/services/harness/harness.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/harness/harness.service.js +28 -1
- package/dist/cli/backend/src/services/harness/harness.service.js.map +1 -1
- package/dist/cli/backend/src/services/harness/harness.types.d.ts +16 -1
- package/dist/cli/backend/src/services/harness/harness.types.d.ts.map +1 -1
- package/dist/cli/backend/src/services/harness/harness.types.js.map +1 -1
- package/dist/cli/backend/src/services/harness/login-expiry-rules.d.ts +75 -0
- package/dist/cli/backend/src/services/harness/login-expiry-rules.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/login-expiry-rules.js +159 -0
- package/dist/cli/backend/src/services/harness/login-expiry-rules.js.map +1 -0
- package/dist/cli/backend/src/services/onboarding/onboarding-checklist.service.d.ts +301 -0
- package/dist/cli/backend/src/services/onboarding/onboarding-checklist.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/onboarding/onboarding-checklist.service.js +365 -0
- package/dist/cli/backend/src/services/onboarding/onboarding-checklist.service.js.map +1 -0
- package/dist/cli/backend/src/services/onboarding/onboarding-state.store.d.ts +89 -0
- package/dist/cli/backend/src/services/onboarding/onboarding-state.store.d.ts.map +1 -0
- package/dist/cli/backend/src/services/onboarding/onboarding-state.store.js +122 -0
- package/dist/cli/backend/src/services/onboarding/onboarding-state.store.js.map +1 -0
- package/dist/cli/backend/src/services/skill/skill-tier.service.d.ts +116 -0
- package/dist/cli/backend/src/services/skill/skill-tier.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/skill/skill-tier.service.js +155 -0
- package/dist/cli/backend/src/services/skill/skill-tier.service.js.map +1 -0
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts +21 -1
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js +39 -0
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
- package/dist/cli/backend/src/services/template/template.service.d.ts +152 -0
- package/dist/cli/backend/src/services/template/template.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/template/template.service.js +506 -0
- package/dist/cli/backend/src/services/template/template.service.js.map +1 -0
- package/dist/cli/backend/src/types/team-template.types.d.ts +30 -0
- package/dist/cli/backend/src/types/team-template.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/team-template.types.js +24 -0
- package/dist/cli/backend/src/types/team-template.types.js.map +1 -1
- package/dist/cli/cli/src/commands/onboard-checklist.d.ts +152 -0
- package/dist/cli/cli/src/commands/onboard-checklist.d.ts.map +1 -0
- package/dist/cli/cli/src/commands/onboard-checklist.js +241 -0
- package/dist/cli/cli/src/commands/onboard-checklist.js.map +1 -0
- package/dist/cli/cli/src/commands/onboard.d.ts +87 -6
- package/dist/cli/cli/src/commands/onboard.d.ts.map +1 -1
- package/dist/cli/cli/src/commands/onboard.js +203 -37
- package/dist/cli/cli/src/commands/onboard.js.map +1 -1
- package/dist/cli/cli/src/constants.d.ts +5 -0
- package/dist/cli/cli/src/constants.d.ts.map +1 -1
- package/dist/cli/cli/src/constants.js +5 -0
- package/dist/cli/cli/src/constants.js.map +1 -1
- package/dist/cli/cli/src/index.js +2 -1
- package/dist/cli/cli/src/index.js.map +1 -1
- package/dist/cli/cli/src/utils/templates.d.ts +27 -0
- package/dist/cli/cli/src/utils/templates.d.ts.map +1 -1
- package/dist/cli/cli/src/utils/templates.js +29 -0
- package/dist/cli/cli/src/utils/templates.js.map +1 -1
- package/frontend/dist/assets/index-2bdb57b9.css +42 -0
- package/frontend/dist/assets/{index-aaf65055.js → index-5b98bf77.js} +285 -285
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/index-0a2f783c.css +0 -42
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Onboarding checklist — the first-run steps after the harness is ready
|
|
3
|
+
* (specs/onboarding-harness-login.md, Phase 3):
|
|
4
|
+
*
|
|
5
|
+
* harness → first team (starter template or Blank) → first task → Cloud → Slack
|
|
6
|
+
*
|
|
7
|
+
* Every step's `done` is read from the real system, not from UI flags:
|
|
8
|
+
*
|
|
9
|
+
* - `harness`: the orchestrator's harness is recorded, installed and not
|
|
10
|
+
* known to be logged out (the same rule as the web setup redirect);
|
|
11
|
+
* - `team`: at least one team exists, or the owner chose Blank;
|
|
12
|
+
* - `first_task`: the owner has sent at least one message on any surface
|
|
13
|
+
* (chat-v2 `user` rows an agent did not write), or a first task was handed
|
|
14
|
+
* to the orchestrator from setup;
|
|
15
|
+
* - `cloud`: this instance is connected to Crewly Cloud;
|
|
16
|
+
* - `slack`: Slack is connected (Cloud-installed app or self-hosted tokens).
|
|
17
|
+
*
|
|
18
|
+
* The only stored flags are the dashboard card's `dismissed`, the Blank
|
|
19
|
+
* choice and the first-task record (`onboarding-state.store.ts`).
|
|
20
|
+
*
|
|
21
|
+
* Shared by the REST routes (`/api/onboarding/*`, also reachable from the
|
|
22
|
+
* phone / portal over the relay) and `crewly onboard`.
|
|
23
|
+
*
|
|
24
|
+
* @module services/onboarding/onboarding-checklist.service
|
|
25
|
+
*/
|
|
26
|
+
import type { Team } from '../../types/index.js';
|
|
27
|
+
import type { TeamTemplate } from '../../types/team-template.types.js';
|
|
28
|
+
import type { CreateFromTemplateResult } from '../template/template.service.js';
|
|
29
|
+
import { OnboardingStateStore } from './onboarding-state.store.js';
|
|
30
|
+
/** Checklist step ids, in display order. */
|
|
31
|
+
export type ChecklistStepId = 'harness' | 'team' | 'first_task' | 'cloud' | 'slack';
|
|
32
|
+
/** Harness step detail. */
|
|
33
|
+
export interface HarnessStepDetail {
|
|
34
|
+
/** The orchestrator's harness (null until chosen) */
|
|
35
|
+
orcHarness: string | null;
|
|
36
|
+
installed: boolean;
|
|
37
|
+
loginState: 'logged_in' | 'logged_out' | 'unknown' | null;
|
|
38
|
+
error?: string;
|
|
39
|
+
}
|
|
40
|
+
/** Team step detail. */
|
|
41
|
+
export interface TeamStepDetail {
|
|
42
|
+
teams: Array<{
|
|
43
|
+
id: string;
|
|
44
|
+
name: string;
|
|
45
|
+
templateId: string | null;
|
|
46
|
+
}>;
|
|
47
|
+
/** The owner chose Blank (the orchestrator only) */
|
|
48
|
+
blank: boolean;
|
|
49
|
+
error?: string;
|
|
50
|
+
}
|
|
51
|
+
/** First-task step detail. */
|
|
52
|
+
export interface FirstTaskStepDetail {
|
|
53
|
+
/** When setup handed a first task to the orchestrator */
|
|
54
|
+
sentAt: string | null;
|
|
55
|
+
/** The owner has written to Crewly on some surface */
|
|
56
|
+
ownerMessageSeen: boolean;
|
|
57
|
+
/** A first task from `crewly onboard` waits for the backend */
|
|
58
|
+
pending: boolean;
|
|
59
|
+
error?: string;
|
|
60
|
+
}
|
|
61
|
+
/** Cloud step detail. */
|
|
62
|
+
export interface CloudStepDetail {
|
|
63
|
+
connected: boolean;
|
|
64
|
+
tier: string | null;
|
|
65
|
+
/**
|
|
66
|
+
* Phone-friendly sign-in: Google sign-in on Crewly Cloud that ends on the
|
|
67
|
+
* portal's token page, whose token + refresh token are pasted back into
|
|
68
|
+
* setup (`POST /api/cloud/connect`).
|
|
69
|
+
*/
|
|
70
|
+
tokenPageSignInUrl: string;
|
|
71
|
+
error?: string;
|
|
72
|
+
}
|
|
73
|
+
/** Slack step detail. */
|
|
74
|
+
export interface SlackStepDetail {
|
|
75
|
+
connected: boolean;
|
|
76
|
+
/** Slack is installed through Crewly Cloud, so Cloud comes first */
|
|
77
|
+
cloudConnected: boolean;
|
|
78
|
+
error?: string;
|
|
79
|
+
}
|
|
80
|
+
/** One checklist step. */
|
|
81
|
+
export type ChecklistStep = {
|
|
82
|
+
id: 'harness';
|
|
83
|
+
done: boolean;
|
|
84
|
+
detail: HarnessStepDetail;
|
|
85
|
+
} | {
|
|
86
|
+
id: 'team';
|
|
87
|
+
done: boolean;
|
|
88
|
+
detail: TeamStepDetail;
|
|
89
|
+
} | {
|
|
90
|
+
id: 'first_task';
|
|
91
|
+
done: boolean;
|
|
92
|
+
detail: FirstTaskStepDetail;
|
|
93
|
+
} | {
|
|
94
|
+
id: 'cloud';
|
|
95
|
+
done: boolean;
|
|
96
|
+
detail: CloudStepDetail;
|
|
97
|
+
} | {
|
|
98
|
+
id: 'slack';
|
|
99
|
+
done: boolean;
|
|
100
|
+
detail: SlackStepDetail;
|
|
101
|
+
};
|
|
102
|
+
/** `GET /api/onboarding/checklist` payload. */
|
|
103
|
+
export interface OnboardingChecklist {
|
|
104
|
+
steps: ChecklistStep[];
|
|
105
|
+
doneCount: number;
|
|
106
|
+
total: number;
|
|
107
|
+
allDone: boolean;
|
|
108
|
+
/** The owner hid the dashboard card */
|
|
109
|
+
dismissed: boolean;
|
|
110
|
+
dismissedAt: string | null;
|
|
111
|
+
}
|
|
112
|
+
/** A starter team the owner can pick (a template, or Blank). */
|
|
113
|
+
export interface OnboardingStarter {
|
|
114
|
+
/** Template id, or `blank` */
|
|
115
|
+
id: string;
|
|
116
|
+
name: string;
|
|
117
|
+
/** Short Chinese display name */
|
|
118
|
+
label: string;
|
|
119
|
+
tagline: string;
|
|
120
|
+
description: string;
|
|
121
|
+
recommended: boolean;
|
|
122
|
+
members: Array<{
|
|
123
|
+
name: string;
|
|
124
|
+
role: string;
|
|
125
|
+
}>;
|
|
126
|
+
/** Example first tasks */
|
|
127
|
+
suggestions: string[];
|
|
128
|
+
}
|
|
129
|
+
/** Result of creating the starter team. */
|
|
130
|
+
export interface StarterTeamResult {
|
|
131
|
+
starterId: string;
|
|
132
|
+
/** The team (null for Blank) */
|
|
133
|
+
team: Team | null;
|
|
134
|
+
/** False when a team from this starter already existed */
|
|
135
|
+
created: boolean;
|
|
136
|
+
}
|
|
137
|
+
/** Result of handing over the first task. */
|
|
138
|
+
export interface FirstTaskResult {
|
|
139
|
+
/** True when the orchestrator received it or it is queued for it */
|
|
140
|
+
forwarded: boolean;
|
|
141
|
+
/** Queued until the orchestrator is online */
|
|
142
|
+
queued: boolean;
|
|
143
|
+
conversationId: string | null;
|
|
144
|
+
teamId: string | null;
|
|
145
|
+
sentAt: string | null;
|
|
146
|
+
/** Why it was not forwarded, or a note (e.g. orchestrator offline) */
|
|
147
|
+
message: string | null;
|
|
148
|
+
}
|
|
149
|
+
/** Error codes of this service. */
|
|
150
|
+
export type OnboardingErrorCode = 'unknown_starter' | 'invalid_task' | 'unknown_team';
|
|
151
|
+
/** A request the checklist cannot fulfil. */
|
|
152
|
+
export declare class OnboardingError extends Error {
|
|
153
|
+
readonly code: OnboardingErrorCode;
|
|
154
|
+
/**
|
|
155
|
+
* @param code - Machine-readable code (mapped to an HTTP status by the controller)
|
|
156
|
+
* @param message - Human-readable message
|
|
157
|
+
*/
|
|
158
|
+
constructor(code: OnboardingErrorCode, message: string);
|
|
159
|
+
}
|
|
160
|
+
/** The template operations the checklist needs. */
|
|
161
|
+
export interface StarterTemplateSource {
|
|
162
|
+
listOnboardingStarters(): TeamTemplate[];
|
|
163
|
+
getTemplate(id: string): TeamTemplate | null;
|
|
164
|
+
createTeamFromTemplate(templateId: string, teamName: string): CreateFromTemplateResult | null;
|
|
165
|
+
}
|
|
166
|
+
/** What handing a message to the orchestrator returns. */
|
|
167
|
+
export interface OrchestratorSendResult {
|
|
168
|
+
conversationId: string | null;
|
|
169
|
+
forwarded: boolean;
|
|
170
|
+
queued: boolean;
|
|
171
|
+
error: string | null;
|
|
172
|
+
}
|
|
173
|
+
/** Dependencies (all injectable for tests). */
|
|
174
|
+
export interface OnboardingChecklistDeps {
|
|
175
|
+
store: OnboardingStateStore;
|
|
176
|
+
/** Harness step */
|
|
177
|
+
getHarnessState(): Promise<HarnessStepDetail>;
|
|
178
|
+
listTeams(): Promise<Team[]>;
|
|
179
|
+
saveTeam(team: Team): Promise<void>;
|
|
180
|
+
templates(): StarterTemplateSource;
|
|
181
|
+
/** The orchestrator's harness, used as the runtime of new starter members */
|
|
182
|
+
getOrcHarness(): Promise<string | null>;
|
|
183
|
+
/** Whether the owner has written on any surface */
|
|
184
|
+
hasOwnerMessage(): boolean;
|
|
185
|
+
getCloudState(): {
|
|
186
|
+
connected: boolean;
|
|
187
|
+
tier: string | null;
|
|
188
|
+
};
|
|
189
|
+
isSlackConnected(): boolean;
|
|
190
|
+
/** Record the owner's message and hand it to the orchestrator */
|
|
191
|
+
sendToOrchestrator(content: string, metadata: Record<string, unknown>): Promise<OrchestratorSendResult>;
|
|
192
|
+
now(): Date;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Lower-case slug of ASCII letters, digits and dashes.
|
|
196
|
+
*
|
|
197
|
+
* @param value - Any text
|
|
198
|
+
* @returns Slug (may be empty for non-ASCII text)
|
|
199
|
+
*/
|
|
200
|
+
export declare function toSessionSlug(value: string): string;
|
|
201
|
+
/**
|
|
202
|
+
* The Crewly Cloud sign-in that works from any device: Google sign-in on
|
|
203
|
+
* Cloud, ending on the portal's token page (token + refresh token to paste).
|
|
204
|
+
*
|
|
205
|
+
* @returns Absolute URL
|
|
206
|
+
*/
|
|
207
|
+
export declare function buildTokenPageSignInUrl(): string;
|
|
208
|
+
/**
|
|
209
|
+
* The message the orchestrator receives for a first task.
|
|
210
|
+
*
|
|
211
|
+
* @param text - The owner's words
|
|
212
|
+
* @param team - Team it is meant for, or null for the orchestrator itself
|
|
213
|
+
* @returns Message content
|
|
214
|
+
*/
|
|
215
|
+
export declare function buildFirstTaskMessage(text: string, team: Pick<Team, 'id' | 'name'> | null): string;
|
|
216
|
+
/**
|
|
217
|
+
* First-run checklist: step states, starter teams, the first task, dismissal.
|
|
218
|
+
*/
|
|
219
|
+
export declare class OnboardingChecklistService {
|
|
220
|
+
private readonly deps;
|
|
221
|
+
/**
|
|
222
|
+
* @param deps - Dependencies
|
|
223
|
+
*/
|
|
224
|
+
constructor(deps: OnboardingChecklistDeps);
|
|
225
|
+
/**
|
|
226
|
+
* Read every step from the live system.
|
|
227
|
+
*
|
|
228
|
+
* A step whose source fails reads as not done, with `detail.error` set;
|
|
229
|
+
* the rest of the checklist is still returned.
|
|
230
|
+
*
|
|
231
|
+
* @returns The checklist
|
|
232
|
+
*/
|
|
233
|
+
getChecklist(): Promise<OnboardingChecklist>;
|
|
234
|
+
/**
|
|
235
|
+
* The starter teams: templates marked `onboarding` (by `order`), then Blank.
|
|
236
|
+
*
|
|
237
|
+
* @returns Starters, recommended first
|
|
238
|
+
*/
|
|
239
|
+
listStarters(): OnboardingStarter[];
|
|
240
|
+
/**
|
|
241
|
+
* Create the first team from a starter, or record the Blank choice.
|
|
242
|
+
*
|
|
243
|
+
* Idempotent: a team already created from the same template is returned
|
|
244
|
+
* (a double tap on a phone must not make two teams). Members run on the
|
|
245
|
+
* orchestrator's harness, the only one first-time setup installs.
|
|
246
|
+
*
|
|
247
|
+
* @param starterId - Template id of a starter, or `blank`
|
|
248
|
+
* @returns The team (null for Blank) and whether it was created now
|
|
249
|
+
* @throws OnboardingError `unknown_starter`
|
|
250
|
+
*/
|
|
251
|
+
createStarterTeam(starterId: string): Promise<StarterTeamResult>;
|
|
252
|
+
/**
|
|
253
|
+
* Hand the owner's first task to the orchestrator (through the chat path,
|
|
254
|
+
* so it is a normal owner message: ticket intake, queue while offline).
|
|
255
|
+
*
|
|
256
|
+
* @param text - The owner's words
|
|
257
|
+
* @param teamId - Team it is meant for (omit for the orchestrator itself)
|
|
258
|
+
* @returns Delivery result
|
|
259
|
+
* @throws OnboardingError `invalid_task` | `unknown_team`
|
|
260
|
+
*/
|
|
261
|
+
sendFirstTask(text: unknown, teamId?: unknown): Promise<FirstTaskResult>;
|
|
262
|
+
/**
|
|
263
|
+
* Keep a first task typed while the backend was down (`crewly onboard`).
|
|
264
|
+
*
|
|
265
|
+
* @param text - The owner's words
|
|
266
|
+
* @param teamId - Team it is meant for, or null
|
|
267
|
+
* @throws OnboardingError `invalid_task`
|
|
268
|
+
*/
|
|
269
|
+
queuePendingFirstTask(text: string, teamId: string | null): Promise<void>;
|
|
270
|
+
/**
|
|
271
|
+
* Deliver a pending first task (called when the backend starts). A team
|
|
272
|
+
* that no longer exists sends it to the orchestrator itself.
|
|
273
|
+
*
|
|
274
|
+
* @returns The delivery result, or null when nothing was pending
|
|
275
|
+
*/
|
|
276
|
+
deliverPendingFirstTask(): Promise<FirstTaskResult | null>;
|
|
277
|
+
/**
|
|
278
|
+
* Hide or show the dashboard checklist card.
|
|
279
|
+
*
|
|
280
|
+
* @param dismissed - True to hide
|
|
281
|
+
* @returns The checklist after the change
|
|
282
|
+
*/
|
|
283
|
+
setDismissed(dismissed: boolean): Promise<OnboardingChecklist>;
|
|
284
|
+
/** @returns Harness step */
|
|
285
|
+
private harnessStep;
|
|
286
|
+
/**
|
|
287
|
+
* @param state - Stored onboarding state
|
|
288
|
+
* @returns Team step
|
|
289
|
+
*/
|
|
290
|
+
private teamStep;
|
|
291
|
+
/**
|
|
292
|
+
* @param state - Stored onboarding state
|
|
293
|
+
* @returns First-task step
|
|
294
|
+
*/
|
|
295
|
+
private firstTaskStep;
|
|
296
|
+
/** @returns Cloud step */
|
|
297
|
+
private cloudStep;
|
|
298
|
+
/** @returns Slack step */
|
|
299
|
+
private slackStep;
|
|
300
|
+
}
|
|
301
|
+
//# sourceMappingURL=onboarding-checklist.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboarding-checklist.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/onboarding/onboarding-checklist.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Onboarding checklist — the first-run steps after the harness is ready
|
|
3
|
+
* (specs/onboarding-harness-login.md, Phase 3):
|
|
4
|
+
*
|
|
5
|
+
* harness → first team (starter template or Blank) → first task → Cloud → Slack
|
|
6
|
+
*
|
|
7
|
+
* Every step's `done` is read from the real system, not from UI flags:
|
|
8
|
+
*
|
|
9
|
+
* - `harness`: the orchestrator's harness is recorded, installed and not
|
|
10
|
+
* known to be logged out (the same rule as the web setup redirect);
|
|
11
|
+
* - `team`: at least one team exists, or the owner chose Blank;
|
|
12
|
+
* - `first_task`: the owner has sent at least one message on any surface
|
|
13
|
+
* (chat-v2 `user` rows an agent did not write), or a first task was handed
|
|
14
|
+
* to the orchestrator from setup;
|
|
15
|
+
* - `cloud`: this instance is connected to Crewly Cloud;
|
|
16
|
+
* - `slack`: Slack is connected (Cloud-installed app or self-hosted tokens).
|
|
17
|
+
*
|
|
18
|
+
* The only stored flags are the dashboard card's `dismissed`, the Blank
|
|
19
|
+
* choice and the first-task record (`onboarding-state.store.ts`).
|
|
20
|
+
*
|
|
21
|
+
* Shared by the REST routes (`/api/onboarding/*`, also reachable from the
|
|
22
|
+
* phone / portal over the relay) and `crewly onboard`.
|
|
23
|
+
*
|
|
24
|
+
* @module services/onboarding/onboarding-checklist.service
|
|
25
|
+
*/
|
|
26
|
+
import { ONBOARDING_CONSTANTS, HARNESS_CONSTANTS, CLOUD_CONSTANTS } from '../../constants.js';
|
|
27
|
+
/** A request the checklist cannot fulfil. */
|
|
28
|
+
export class OnboardingError extends Error {
|
|
29
|
+
code;
|
|
30
|
+
/**
|
|
31
|
+
* @param code - Machine-readable code (mapped to an HTTP status by the controller)
|
|
32
|
+
* @param message - Human-readable message
|
|
33
|
+
*/
|
|
34
|
+
constructor(code, message) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.code = code;
|
|
37
|
+
this.name = 'OnboardingError';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// =============================================================================
|
|
41
|
+
// Helpers
|
|
42
|
+
// =============================================================================
|
|
43
|
+
/**
|
|
44
|
+
* Lower-case slug of ASCII letters, digits and dashes.
|
|
45
|
+
*
|
|
46
|
+
* @param value - Any text
|
|
47
|
+
* @returns Slug (may be empty for non-ASCII text)
|
|
48
|
+
*/
|
|
49
|
+
export function toSessionSlug(value) {
|
|
50
|
+
return value
|
|
51
|
+
.toLowerCase()
|
|
52
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
53
|
+
.replace(/^-+|-+$/g, '');
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The Crewly Cloud sign-in that works from any device: Google sign-in on
|
|
57
|
+
* Cloud, ending on the portal's token page (token + refresh token to paste).
|
|
58
|
+
*
|
|
59
|
+
* @returns Absolute URL
|
|
60
|
+
*/
|
|
61
|
+
export function buildTokenPageSignInUrl() {
|
|
62
|
+
const tokenPage = `${ONBOARDING_CONSTANTS.CLOUD.CONSOLE_URL}${ONBOARDING_CONSTANTS.CLOUD.CLI_TOKEN_PATH}`;
|
|
63
|
+
return `${CLOUD_CONSTANTS.DEFAULT_CLOUD_URL}${ONBOARDING_CONSTANTS.CLOUD.GOOGLE_START_PATH}?redirect=${encodeURIComponent(tokenPage)}`;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The message the orchestrator receives for a first task.
|
|
67
|
+
*
|
|
68
|
+
* @param text - The owner's words
|
|
69
|
+
* @param team - Team it is meant for, or null for the orchestrator itself
|
|
70
|
+
* @returns Message content
|
|
71
|
+
*/
|
|
72
|
+
export function buildFirstTaskMessage(text, team) {
|
|
73
|
+
const lines = [ONBOARDING_CONSTANTS.FIRST_TASK_HEADER];
|
|
74
|
+
if (team) {
|
|
75
|
+
lines.push(`请交给团队「${team.name}」(team id: ${team.id}) 来做;团队还没启动的话先启动它。`);
|
|
76
|
+
}
|
|
77
|
+
lines.push('', text.trim());
|
|
78
|
+
return lines.join('\n');
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Starter DTO for a template.
|
|
82
|
+
*
|
|
83
|
+
* @param template - Template with onboarding metadata
|
|
84
|
+
* @param onboarding - Its onboarding metadata
|
|
85
|
+
* @returns Starter
|
|
86
|
+
*/
|
|
87
|
+
function templateToStarter(template, onboarding) {
|
|
88
|
+
return {
|
|
89
|
+
id: template.id,
|
|
90
|
+
name: template.name,
|
|
91
|
+
label: onboarding.label,
|
|
92
|
+
tagline: onboarding.tagline,
|
|
93
|
+
description: template.description,
|
|
94
|
+
recommended: onboarding.recommended,
|
|
95
|
+
members: template.roles.flatMap((role) => Array.from({ length: Math.max(1, role.count) }, (_, i) => ({
|
|
96
|
+
name: role.count > 1 ? `${role.defaultName}${i + 1}` : role.defaultName,
|
|
97
|
+
role: role.role,
|
|
98
|
+
}))),
|
|
99
|
+
suggestions: [...onboarding.suggestions],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Error text for a failed step read.
|
|
104
|
+
*
|
|
105
|
+
* @param error - Thrown value
|
|
106
|
+
* @returns Message
|
|
107
|
+
*/
|
|
108
|
+
function errorText(error) {
|
|
109
|
+
return error instanceof Error ? error.message : String(error);
|
|
110
|
+
}
|
|
111
|
+
// =============================================================================
|
|
112
|
+
// Service
|
|
113
|
+
// =============================================================================
|
|
114
|
+
/**
|
|
115
|
+
* First-run checklist: step states, starter teams, the first task, dismissal.
|
|
116
|
+
*/
|
|
117
|
+
export class OnboardingChecklistService {
|
|
118
|
+
deps;
|
|
119
|
+
/**
|
|
120
|
+
* @param deps - Dependencies
|
|
121
|
+
*/
|
|
122
|
+
constructor(deps) {
|
|
123
|
+
this.deps = deps;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Read every step from the live system.
|
|
127
|
+
*
|
|
128
|
+
* A step whose source fails reads as not done, with `detail.error` set;
|
|
129
|
+
* the rest of the checklist is still returned.
|
|
130
|
+
*
|
|
131
|
+
* @returns The checklist
|
|
132
|
+
*/
|
|
133
|
+
async getChecklist() {
|
|
134
|
+
const state = await this.deps.store.read();
|
|
135
|
+
const steps = await Promise.all([
|
|
136
|
+
this.harnessStep(),
|
|
137
|
+
this.teamStep(state),
|
|
138
|
+
this.firstTaskStep(state),
|
|
139
|
+
this.cloudStep(),
|
|
140
|
+
this.slackStep(),
|
|
141
|
+
]);
|
|
142
|
+
const doneCount = steps.filter((s) => s.done).length;
|
|
143
|
+
return {
|
|
144
|
+
steps,
|
|
145
|
+
doneCount,
|
|
146
|
+
total: steps.length,
|
|
147
|
+
allDone: doneCount === steps.length,
|
|
148
|
+
dismissed: state.dismissedAt !== null,
|
|
149
|
+
dismissedAt: state.dismissedAt,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* The starter teams: templates marked `onboarding` (by `order`), then Blank.
|
|
154
|
+
*
|
|
155
|
+
* @returns Starters, recommended first
|
|
156
|
+
*/
|
|
157
|
+
listStarters() {
|
|
158
|
+
const starters = this.deps
|
|
159
|
+
.templates()
|
|
160
|
+
.listOnboardingStarters()
|
|
161
|
+
.filter((t) => !!t.onboarding)
|
|
162
|
+
.map((t) => templateToStarter(t, t.onboarding));
|
|
163
|
+
const blank = ONBOARDING_CONSTANTS.BLANK_STARTER;
|
|
164
|
+
starters.push({
|
|
165
|
+
id: ONBOARDING_CONSTANTS.BLANK_STARTER_ID,
|
|
166
|
+
name: blank.NAME,
|
|
167
|
+
label: blank.LABEL,
|
|
168
|
+
tagline: blank.TAGLINE,
|
|
169
|
+
description: blank.TAGLINE,
|
|
170
|
+
recommended: false,
|
|
171
|
+
members: [],
|
|
172
|
+
suggestions: [...blank.SUGGESTIONS],
|
|
173
|
+
});
|
|
174
|
+
return starters;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Create the first team from a starter, or record the Blank choice.
|
|
178
|
+
*
|
|
179
|
+
* Idempotent: a team already created from the same template is returned
|
|
180
|
+
* (a double tap on a phone must not make two teams). Members run on the
|
|
181
|
+
* orchestrator's harness, the only one first-time setup installs.
|
|
182
|
+
*
|
|
183
|
+
* @param starterId - Template id of a starter, or `blank`
|
|
184
|
+
* @returns The team (null for Blank) and whether it was created now
|
|
185
|
+
* @throws OnboardingError `unknown_starter`
|
|
186
|
+
*/
|
|
187
|
+
async createStarterTeam(starterId) {
|
|
188
|
+
if (starterId === ONBOARDING_CONSTANTS.BLANK_STARTER_ID) {
|
|
189
|
+
const now = this.deps.now().toISOString();
|
|
190
|
+
await this.deps.store.update((cur) => ({ blankChosenAt: cur.blankChosenAt ?? now }));
|
|
191
|
+
return { starterId, team: null, created: false };
|
|
192
|
+
}
|
|
193
|
+
const source = this.deps.templates();
|
|
194
|
+
const template = source.getTemplate(starterId);
|
|
195
|
+
if (!template || !template.onboarding) {
|
|
196
|
+
throw new OnboardingError('unknown_starter', `"${starterId}" is not a starter team`);
|
|
197
|
+
}
|
|
198
|
+
const existing = (await this.deps.listTeams()).find((t) => t.templateId === template.id);
|
|
199
|
+
if (existing)
|
|
200
|
+
return { starterId, team: existing, created: false };
|
|
201
|
+
const result = source.createTeamFromTemplate(template.id, template.name);
|
|
202
|
+
if (!result)
|
|
203
|
+
throw new OnboardingError('unknown_starter', `"${starterId}" is not a starter team`);
|
|
204
|
+
const runtime = (await this.deps.getOrcHarness()) ?? HARNESS_CONSTANTS.DEFAULT_ORC_HARNESS;
|
|
205
|
+
const teamSlug = toSessionSlug(template.id);
|
|
206
|
+
for (const member of result.team.members) {
|
|
207
|
+
member.runtimeType = runtime;
|
|
208
|
+
member.sessionName = `${teamSlug}-${toSessionSlug(member.name) || 'member'}-${member.id.slice(0, 8)}`;
|
|
209
|
+
}
|
|
210
|
+
await this.deps.saveTeam(result.team);
|
|
211
|
+
return { starterId, team: result.team, created: true };
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Hand the owner's first task to the orchestrator (through the chat path,
|
|
215
|
+
* so it is a normal owner message: ticket intake, queue while offline).
|
|
216
|
+
*
|
|
217
|
+
* @param text - The owner's words
|
|
218
|
+
* @param teamId - Team it is meant for (omit for the orchestrator itself)
|
|
219
|
+
* @returns Delivery result
|
|
220
|
+
* @throws OnboardingError `invalid_task` | `unknown_team`
|
|
221
|
+
*/
|
|
222
|
+
async sendFirstTask(text, teamId) {
|
|
223
|
+
if (typeof text !== 'string' || text.trim().length === 0) {
|
|
224
|
+
throw new OnboardingError('invalid_task', 'Write what the team should do first');
|
|
225
|
+
}
|
|
226
|
+
if (text.length > ONBOARDING_CONSTANTS.FIRST_TASK_MAX_LENGTH) {
|
|
227
|
+
throw new OnboardingError('invalid_task', `Keep the first task under ${ONBOARDING_CONSTANTS.FIRST_TASK_MAX_LENGTH} characters`);
|
|
228
|
+
}
|
|
229
|
+
let team = null;
|
|
230
|
+
if (teamId !== undefined && teamId !== null && teamId !== '') {
|
|
231
|
+
team = (await this.deps.listTeams()).find((t) => t.id === teamId) ?? null;
|
|
232
|
+
if (!team)
|
|
233
|
+
throw new OnboardingError('unknown_team', `Team "${String(teamId)}" not found`);
|
|
234
|
+
}
|
|
235
|
+
const sent = await this.deps.sendToOrchestrator(buildFirstTaskMessage(text, team), {
|
|
236
|
+
source: ONBOARDING_CONSTANTS.FIRST_TASK_SOURCE,
|
|
237
|
+
...(team ? { teamId: team.id } : {}),
|
|
238
|
+
});
|
|
239
|
+
let sentAt = null;
|
|
240
|
+
if (sent.forwarded) {
|
|
241
|
+
sentAt = this.deps.now().toISOString();
|
|
242
|
+
const record = { sentAt, teamId: team?.id ?? null, conversationId: sent.conversationId };
|
|
243
|
+
await this.deps.store.update(() => ({ firstTask: record, pendingFirstTask: null }));
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
forwarded: sent.forwarded,
|
|
247
|
+
queued: sent.queued,
|
|
248
|
+
conversationId: sent.conversationId,
|
|
249
|
+
teamId: team?.id ?? null,
|
|
250
|
+
sentAt,
|
|
251
|
+
message: sent.error,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Keep a first task typed while the backend was down (`crewly onboard`).
|
|
256
|
+
*
|
|
257
|
+
* @param text - The owner's words
|
|
258
|
+
* @param teamId - Team it is meant for, or null
|
|
259
|
+
* @throws OnboardingError `invalid_task`
|
|
260
|
+
*/
|
|
261
|
+
async queuePendingFirstTask(text, teamId) {
|
|
262
|
+
if (text.trim().length === 0 || text.length > ONBOARDING_CONSTANTS.FIRST_TASK_MAX_LENGTH) {
|
|
263
|
+
throw new OnboardingError('invalid_task', 'Write what the team should do first');
|
|
264
|
+
}
|
|
265
|
+
const createdAt = this.deps.now().toISOString();
|
|
266
|
+
await this.deps.store.update(() => ({ pendingFirstTask: { text: text.trim(), teamId, createdAt } }));
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Deliver a pending first task (called when the backend starts). A team
|
|
270
|
+
* that no longer exists sends it to the orchestrator itself.
|
|
271
|
+
*
|
|
272
|
+
* @returns The delivery result, or null when nothing was pending
|
|
273
|
+
*/
|
|
274
|
+
async deliverPendingFirstTask() {
|
|
275
|
+
const { pendingFirstTask } = await this.deps.store.read();
|
|
276
|
+
if (!pendingFirstTask)
|
|
277
|
+
return null;
|
|
278
|
+
const teams = await this.deps.listTeams();
|
|
279
|
+
const teamId = pendingFirstTask.teamId && teams.some((t) => t.id === pendingFirstTask.teamId) ? pendingFirstTask.teamId : undefined;
|
|
280
|
+
return this.sendFirstTask(pendingFirstTask.text, teamId);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Hide or show the dashboard checklist card.
|
|
284
|
+
*
|
|
285
|
+
* @param dismissed - True to hide
|
|
286
|
+
* @returns The checklist after the change
|
|
287
|
+
*/
|
|
288
|
+
async setDismissed(dismissed) {
|
|
289
|
+
const now = this.deps.now().toISOString();
|
|
290
|
+
await this.deps.store.update((cur) => ({ dismissedAt: dismissed ? cur.dismissedAt ?? now : null }));
|
|
291
|
+
return this.getChecklist();
|
|
292
|
+
}
|
|
293
|
+
// ---------------------------------------------------------------------------
|
|
294
|
+
// Steps
|
|
295
|
+
// ---------------------------------------------------------------------------
|
|
296
|
+
/** @returns Harness step */
|
|
297
|
+
async harnessStep() {
|
|
298
|
+
try {
|
|
299
|
+
const detail = await this.deps.getHarnessState();
|
|
300
|
+
const done = detail.orcHarness !== null && detail.installed && detail.loginState !== 'logged_out';
|
|
301
|
+
return { id: 'harness', done, detail };
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
return { id: 'harness', done: false, detail: { orcHarness: null, installed: false, loginState: null, error: errorText(error) } };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* @param state - Stored onboarding state
|
|
309
|
+
* @returns Team step
|
|
310
|
+
*/
|
|
311
|
+
async teamStep(state) {
|
|
312
|
+
const blank = state.blankChosenAt !== null;
|
|
313
|
+
try {
|
|
314
|
+
const teams = (await this.deps.listTeams()).map((t) => ({ id: t.id, name: t.name, templateId: t.templateId ?? null }));
|
|
315
|
+
return { id: 'team', done: teams.length > 0 || blank, detail: { teams, blank } };
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
return { id: 'team', done: blank, detail: { teams: [], blank, error: errorText(error) } };
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* @param state - Stored onboarding state
|
|
323
|
+
* @returns First-task step
|
|
324
|
+
*/
|
|
325
|
+
async firstTaskStep(state) {
|
|
326
|
+
const sentAt = state.firstTask?.sentAt ?? null;
|
|
327
|
+
const pending = state.pendingFirstTask !== null;
|
|
328
|
+
try {
|
|
329
|
+
const ownerMessageSeen = this.deps.hasOwnerMessage();
|
|
330
|
+
return { id: 'first_task', done: sentAt !== null || ownerMessageSeen, detail: { sentAt, ownerMessageSeen, pending } };
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
return { id: 'first_task', done: sentAt !== null, detail: { sentAt, ownerMessageSeen: false, pending, error: errorText(error) } };
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
/** @returns Cloud step */
|
|
337
|
+
async cloudStep() {
|
|
338
|
+
const tokenPageSignInUrl = buildTokenPageSignInUrl();
|
|
339
|
+
try {
|
|
340
|
+
const { connected, tier } = this.deps.getCloudState();
|
|
341
|
+
return { id: 'cloud', done: connected, detail: { connected, tier: connected ? tier : null, tokenPageSignInUrl } };
|
|
342
|
+
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
return { id: 'cloud', done: false, detail: { connected: false, tier: null, tokenPageSignInUrl, error: errorText(error) } };
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/** @returns Slack step */
|
|
348
|
+
async slackStep() {
|
|
349
|
+
let cloudConnected = false;
|
|
350
|
+
try {
|
|
351
|
+
cloudConnected = this.deps.getCloudState().connected;
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
// Reported by the Cloud step.
|
|
355
|
+
}
|
|
356
|
+
try {
|
|
357
|
+
const connected = this.deps.isSlackConnected();
|
|
358
|
+
return { id: 'slack', done: connected, detail: { connected, cloudConnected } };
|
|
359
|
+
}
|
|
360
|
+
catch (error) {
|
|
361
|
+
return { id: 'slack', done: false, detail: { connected: false, cloudConnected, error: errorText(error) } };
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
//# sourceMappingURL=onboarding-checklist.service.js.map
|