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.
Files changed (153) hide show
  1. package/config/templates/growth-marketing-team.json +12 -1
  2. package/config/templates/personal-assistant-team.json +28 -0
  3. package/dist/backend/backend/src/constants.d.ts +75 -0
  4. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  5. package/dist/backend/backend/src/constants.js +81 -0
  6. package/dist/backend/backend/src/constants.js.map +1 -1
  7. package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts +49 -0
  8. package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
  9. package/dist/backend/backend/src/controllers/chat/chat.controller.js +135 -106
  10. package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
  11. package/dist/backend/backend/src/controllers/onboarding/index.d.ts +1 -0
  12. package/dist/backend/backend/src/controllers/onboarding/index.d.ts.map +1 -1
  13. package/dist/backend/backend/src/controllers/onboarding/index.js +1 -0
  14. package/dist/backend/backend/src/controllers/onboarding/index.js.map +1 -1
  15. package/dist/backend/backend/src/controllers/onboarding/onboarding-checklist.controller.d.ts +38 -0
  16. package/dist/backend/backend/src/controllers/onboarding/onboarding-checklist.controller.d.ts.map +1 -0
  17. package/dist/backend/backend/src/controllers/onboarding/onboarding-checklist.controller.js +106 -0
  18. package/dist/backend/backend/src/controllers/onboarding/onboarding-checklist.controller.js.map +1 -0
  19. package/dist/backend/backend/src/controllers/onboarding/onboarding-checklist.routes.d.ts +24 -0
  20. package/dist/backend/backend/src/controllers/onboarding/onboarding-checklist.routes.d.ts.map +1 -0
  21. package/dist/backend/backend/src/controllers/onboarding/onboarding-checklist.routes.js +34 -0
  22. package/dist/backend/backend/src/controllers/onboarding/onboarding-checklist.routes.js.map +1 -0
  23. package/dist/backend/backend/src/index.d.ts.map +1 -1
  24. package/dist/backend/backend/src/index.js +48 -1
  25. package/dist/backend/backend/src/index.js.map +1 -1
  26. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  27. package/dist/backend/backend/src/routes/api.routes.js +5 -0
  28. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  29. package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.d.ts +33 -0
  30. package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.d.ts.map +1 -1
  31. package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.js +63 -0
  32. package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.js.map +1 -1
  33. package/dist/backend/backend/src/services/agent/relogin-agent-resumer.service.d.ts +76 -0
  34. package/dist/backend/backend/src/services/agent/relogin-agent-resumer.service.d.ts.map +1 -0
  35. package/dist/backend/backend/src/services/agent/relogin-agent-resumer.service.js +108 -0
  36. package/dist/backend/backend/src/services/agent/relogin-agent-resumer.service.js.map +1 -0
  37. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.d.ts.map +1 -1
  38. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js +10 -0
  39. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js.map +1 -1
  40. package/dist/backend/backend/src/services/harness/harness-relogin.service.d.ts +361 -0
  41. package/dist/backend/backend/src/services/harness/harness-relogin.service.d.ts.map +1 -0
  42. package/dist/backend/backend/src/services/harness/harness-relogin.service.js +756 -0
  43. package/dist/backend/backend/src/services/harness/harness-relogin.service.js.map +1 -0
  44. package/dist/backend/backend/src/services/harness/harness.service.d.ts +16 -1
  45. package/dist/backend/backend/src/services/harness/harness.service.d.ts.map +1 -1
  46. package/dist/backend/backend/src/services/harness/harness.service.js +28 -1
  47. package/dist/backend/backend/src/services/harness/harness.service.js.map +1 -1
  48. package/dist/backend/backend/src/services/harness/harness.types.d.ts +16 -1
  49. package/dist/backend/backend/src/services/harness/harness.types.d.ts.map +1 -1
  50. package/dist/backend/backend/src/services/harness/harness.types.js.map +1 -1
  51. package/dist/backend/backend/src/services/harness/login-expiry-rules.d.ts +75 -0
  52. package/dist/backend/backend/src/services/harness/login-expiry-rules.d.ts.map +1 -0
  53. package/dist/backend/backend/src/services/harness/login-expiry-rules.js +159 -0
  54. package/dist/backend/backend/src/services/harness/login-expiry-rules.js.map +1 -0
  55. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.factory.d.ts +45 -0
  56. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.factory.d.ts.map +1 -0
  57. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.factory.js +103 -0
  58. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.factory.js.map +1 -0
  59. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.service.d.ts +301 -0
  60. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.service.d.ts.map +1 -0
  61. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.service.js +365 -0
  62. package/dist/backend/backend/src/services/onboarding/onboarding-checklist.service.js.map +1 -0
  63. package/dist/backend/backend/src/services/onboarding/onboarding-state.store.d.ts +89 -0
  64. package/dist/backend/backend/src/services/onboarding/onboarding-state.store.d.ts.map +1 -0
  65. package/dist/backend/backend/src/services/onboarding/onboarding-state.store.js +122 -0
  66. package/dist/backend/backend/src/services/onboarding/onboarding-state.store.js.map +1 -0
  67. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +21 -1
  68. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  69. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +39 -0
  70. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  71. package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.d.ts +80 -0
  72. package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.d.ts.map +1 -0
  73. package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.js +127 -0
  74. package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.js.map +1 -0
  75. package/dist/backend/backend/src/services/template/template.service.d.ts +15 -1
  76. package/dist/backend/backend/src/services/template/template.service.d.ts.map +1 -1
  77. package/dist/backend/backend/src/services/template/template.service.js +22 -1
  78. package/dist/backend/backend/src/services/template/template.service.js.map +1 -1
  79. package/dist/backend/backend/src/types/team-template.types.d.ts +30 -0
  80. package/dist/backend/backend/src/types/team-template.types.d.ts.map +1 -1
  81. package/dist/backend/backend/src/types/team-template.types.js +24 -0
  82. package/dist/backend/backend/src/types/team-template.types.js.map +1 -1
  83. package/dist/backend/build-info.json +2 -2
  84. package/dist/cli/backend/src/constants.d.ts +75 -0
  85. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  86. package/dist/cli/backend/src/constants.js +81 -0
  87. package/dist/cli/backend/src/constants.js.map +1 -1
  88. package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.d.ts +33 -0
  89. package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.d.ts.map +1 -1
  90. package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.js +63 -0
  91. package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.js.map +1 -1
  92. package/dist/cli/backend/src/services/harness/harness-relogin.service.d.ts +361 -0
  93. package/dist/cli/backend/src/services/harness/harness-relogin.service.d.ts.map +1 -0
  94. package/dist/cli/backend/src/services/harness/harness-relogin.service.js +756 -0
  95. package/dist/cli/backend/src/services/harness/harness-relogin.service.js.map +1 -0
  96. package/dist/cli/backend/src/services/harness/harness.service.d.ts +16 -1
  97. package/dist/cli/backend/src/services/harness/harness.service.d.ts.map +1 -1
  98. package/dist/cli/backend/src/services/harness/harness.service.js +28 -1
  99. package/dist/cli/backend/src/services/harness/harness.service.js.map +1 -1
  100. package/dist/cli/backend/src/services/harness/harness.types.d.ts +16 -1
  101. package/dist/cli/backend/src/services/harness/harness.types.d.ts.map +1 -1
  102. package/dist/cli/backend/src/services/harness/harness.types.js.map +1 -1
  103. package/dist/cli/backend/src/services/harness/login-expiry-rules.d.ts +75 -0
  104. package/dist/cli/backend/src/services/harness/login-expiry-rules.d.ts.map +1 -0
  105. package/dist/cli/backend/src/services/harness/login-expiry-rules.js +159 -0
  106. package/dist/cli/backend/src/services/harness/login-expiry-rules.js.map +1 -0
  107. package/dist/cli/backend/src/services/onboarding/onboarding-checklist.service.d.ts +301 -0
  108. package/dist/cli/backend/src/services/onboarding/onboarding-checklist.service.d.ts.map +1 -0
  109. package/dist/cli/backend/src/services/onboarding/onboarding-checklist.service.js +365 -0
  110. package/dist/cli/backend/src/services/onboarding/onboarding-checklist.service.js.map +1 -0
  111. package/dist/cli/backend/src/services/onboarding/onboarding-state.store.d.ts +89 -0
  112. package/dist/cli/backend/src/services/onboarding/onboarding-state.store.d.ts.map +1 -0
  113. package/dist/cli/backend/src/services/onboarding/onboarding-state.store.js +122 -0
  114. package/dist/cli/backend/src/services/onboarding/onboarding-state.store.js.map +1 -0
  115. package/dist/cli/backend/src/services/skill/skill-tier.service.d.ts +116 -0
  116. package/dist/cli/backend/src/services/skill/skill-tier.service.d.ts.map +1 -0
  117. package/dist/cli/backend/src/services/skill/skill-tier.service.js +155 -0
  118. package/dist/cli/backend/src/services/skill/skill-tier.service.js.map +1 -0
  119. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts +21 -1
  120. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  121. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js +39 -0
  122. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  123. package/dist/cli/backend/src/services/template/template.service.d.ts +152 -0
  124. package/dist/cli/backend/src/services/template/template.service.d.ts.map +1 -0
  125. package/dist/cli/backend/src/services/template/template.service.js +506 -0
  126. package/dist/cli/backend/src/services/template/template.service.js.map +1 -0
  127. package/dist/cli/backend/src/types/team-template.types.d.ts +30 -0
  128. package/dist/cli/backend/src/types/team-template.types.d.ts.map +1 -1
  129. package/dist/cli/backend/src/types/team-template.types.js +24 -0
  130. package/dist/cli/backend/src/types/team-template.types.js.map +1 -1
  131. package/dist/cli/cli/src/commands/onboard-checklist.d.ts +152 -0
  132. package/dist/cli/cli/src/commands/onboard-checklist.d.ts.map +1 -0
  133. package/dist/cli/cli/src/commands/onboard-checklist.js +241 -0
  134. package/dist/cli/cli/src/commands/onboard-checklist.js.map +1 -0
  135. package/dist/cli/cli/src/commands/onboard.d.ts +87 -6
  136. package/dist/cli/cli/src/commands/onboard.d.ts.map +1 -1
  137. package/dist/cli/cli/src/commands/onboard.js +203 -37
  138. package/dist/cli/cli/src/commands/onboard.js.map +1 -1
  139. package/dist/cli/cli/src/constants.d.ts +5 -0
  140. package/dist/cli/cli/src/constants.d.ts.map +1 -1
  141. package/dist/cli/cli/src/constants.js +5 -0
  142. package/dist/cli/cli/src/constants.js.map +1 -1
  143. package/dist/cli/cli/src/index.js +2 -1
  144. package/dist/cli/cli/src/index.js.map +1 -1
  145. package/dist/cli/cli/src/utils/templates.d.ts +27 -0
  146. package/dist/cli/cli/src/utils/templates.d.ts.map +1 -1
  147. package/dist/cli/cli/src/utils/templates.js +29 -0
  148. package/dist/cli/cli/src/utils/templates.js.map +1 -1
  149. package/frontend/dist/assets/index-2bdb57b9.css +42 -0
  150. package/frontend/dist/assets/{index-aaf65055.js → index-5b98bf77.js} +285 -285
  151. package/frontend/dist/index.html +2 -2
  152. package/package.json +1 -1
  153. 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