Skip to content

Commit da8b09e

Browse files
mrubensellipsis-dev[bot]jrcte
authored
Sharing improvements (#5082)
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Co-authored-by: John Richmond <[email protected]> Co-authored-by: cte <[email protected]>
1 parent ba5033e commit da8b09e

File tree

89 files changed

+2206
-362
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+2206
-362
lines changed

packages/build/src/__tests__/index.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ describe("generatePackageJson", () => {
7070
{
7171
command: "roo-cline.accountButtonClicked",
7272
group: "navigation@6",
73-
when: "activeWebviewPanelId == roo-cline.TabPanelProvider && config.roo-cline.rooCodeCloudEnabled",
73+
when: "activeWebviewPanelId == roo-cline.TabPanelProvider",
7474
},
7575
],
7676
},
@@ -183,7 +183,7 @@ describe("generatePackageJson", () => {
183183
{
184184
command: "roo-code-nightly.accountButtonClicked",
185185
group: "navigation@6",
186-
when: "activeWebviewPanelId == roo-code-nightly.TabPanelProvider && config.roo-code-nightly.rooCodeCloudEnabled",
186+
when: "activeWebviewPanelId == roo-code-nightly.TabPanelProvider",
187187
},
188188
],
189189
},

packages/cloud/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
"dependencies": {
1414
"@roo-code/telemetry": "workspace:^",
1515
"@roo-code/types": "workspace:^",
16-
"axios": "^1.7.4",
1716
"zod": "^3.25.61"
1817
},
1918
"devDependencies": {

packages/cloud/src/AuthService.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ const clerkCreateSessionTokenResponseSchema = z.object({
4242

4343
const clerkMeResponseSchema = z.object({
4444
response: z.object({
45-
first_name: z.string().optional(),
46-
last_name: z.string().optional(),
45+
first_name: z.string().optional().nullable(),
46+
last_name: z.string().optional().nullable(),
4747
image_url: z.string().optional(),
4848
primary_email_address_id: z.string().optional(),
4949
email_addresses: z
@@ -531,7 +531,8 @@ export class AuthService extends EventEmitter<AuthServiceEvents> {
531531

532532
const userInfo: CloudUserInfo = {}
533533

534-
userInfo.name = `${userData.first_name} ${userData.last_name}`
534+
const names = [userData.first_name, userData.last_name].filter((name) => !!name)
535+
userInfo.name = names.length > 0 ? names.join(" ") : undefined
535536
const primaryEmailAddressId = userData.primary_email_address_id
536537
const emailAddresses = userData.email_addresses
537538

packages/cloud/src/CloudService.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
import * as vscode from "vscode"
22

3-
import type { CloudUserInfo, TelemetryEvent, OrganizationAllowList } from "@roo-code/types"
3+
import type {
4+
CloudUserInfo,
5+
TelemetryEvent,
6+
OrganizationAllowList,
7+
ClineMessage,
8+
ShareVisibility,
9+
} from "@roo-code/types"
410
import { TelemetryService } from "@roo-code/telemetry"
511

612
import { CloudServiceCallbacks } from "./types"
713
import { AuthService } from "./AuthService"
814
import { SettingsService } from "./SettingsService"
915
import { TelemetryClient } from "./TelemetryClient"
10-
import { ShareService } from "./ShareService"
16+
import { ShareService, TaskNotFoundError } from "./ShareService"
1117

1218
export class CloudService {
1319
private static _instance: CloudService | null = null
@@ -161,9 +167,23 @@ export class CloudService {
161167

162168
// ShareService
163169

164-
public async shareTask(taskId: string, visibility: "organization" | "public" = "organization") {
170+
public async shareTask(
171+
taskId: string,
172+
visibility: ShareVisibility = "organization",
173+
clineMessages?: ClineMessage[],
174+
) {
165175
this.ensureInitialized()
166-
return this.shareService!.shareTask(taskId, visibility)
176+
177+
try {
178+
return await this.shareService!.shareTask(taskId, visibility)
179+
} catch (error) {
180+
if (error instanceof TaskNotFoundError && clineMessages) {
181+
// Backfill messages and retry
182+
await this.telemetryClient!.backfillMessages(clineMessages, taskId)
183+
return await this.shareService!.shareTask(taskId, visibility)
184+
}
185+
throw error
186+
}
167187
}
168188

169189
public async canShareTask(): Promise<boolean> {

packages/cloud/src/ShareService.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import axios from "axios"
21
import * as vscode from "vscode"
32

43
import { shareResponseSchema } from "@roo-code/types"
@@ -9,6 +8,13 @@ import { getUserAgent } from "./utils"
98

109
export type ShareVisibility = "organization" | "public"
1110

11+
export class TaskNotFoundError extends Error {
12+
constructor(taskId?: string) {
13+
super(taskId ? `Task '${taskId}' not found` : "Task not found")
14+
Object.setPrototypeOf(this, TaskNotFoundError.prototype)
15+
}
16+
}
17+
1218
export class ShareService {
1319
private authService: AuthService
1420
private settingsService: SettingsService
@@ -31,19 +37,25 @@ export class ShareService {
3137
throw new Error("Authentication required")
3238
}
3339

34-
const response = await axios.post(
35-
`${getRooCodeApiUrl()}/api/extension/share`,
36-
{ taskId, visibility },
37-
{
38-
headers: {
39-
"Content-Type": "application/json",
40-
Authorization: `Bearer ${sessionToken}`,
41-
"User-Agent": getUserAgent(),
42-
},
40+
const response = await fetch(`${getRooCodeApiUrl()}/api/extension/share`, {
41+
method: "POST",
42+
headers: {
43+
"Content-Type": "application/json",
44+
Authorization: `Bearer ${sessionToken}`,
45+
"User-Agent": getUserAgent(),
4346
},
44-
)
47+
body: JSON.stringify({ taskId, visibility }),
48+
signal: AbortSignal.timeout(10000),
49+
})
50+
51+
if (!response.ok) {
52+
if (response.status === 404) {
53+
throw new TaskNotFoundError(taskId)
54+
}
55+
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
56+
}
4557

46-
const data = shareResponseSchema.parse(response.data)
58+
const data = shareResponseSchema.parse(await response.json())
4759
this.log("[share] Share link created successfully:", data)
4860

4961
if (data.success && data.shareUrl) {

packages/cloud/src/TelemetryClient.ts

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { TelemetryEventName, type TelemetryEvent, rooCodeTelemetryEventSchema } from "@roo-code/types"
1+
import {
2+
TelemetryEventName,
3+
type TelemetryEvent,
4+
rooCodeTelemetryEventSchema,
5+
type ClineMessage,
6+
} from "@roo-code/types"
27
import { BaseTelemetryClient } from "@roo-code/telemetry"
38

49
import { getRooCodeApiUrl } from "./Config"
@@ -79,6 +84,66 @@ export class TelemetryClient extends BaseTelemetryClient {
7984
}
8085
}
8186

87+
public async backfillMessages(messages: ClineMessage[], taskId: string): Promise<void> {
88+
if (!this.authService.isAuthenticated()) {
89+
if (this.debug) {
90+
console.info(`[TelemetryClient#backfillMessages] Skipping: Not authenticated`)
91+
}
92+
return
93+
}
94+
95+
const token = this.authService.getSessionToken()
96+
97+
if (!token) {
98+
console.error(`[TelemetryClient#backfillMessages] Unauthorized: No session token available.`)
99+
return
100+
}
101+
102+
try {
103+
const mergedProperties = await this.getEventProperties({
104+
event: TelemetryEventName.TASK_MESSAGE,
105+
properties: { taskId },
106+
})
107+
108+
const formData = new FormData()
109+
formData.append("taskId", taskId)
110+
formData.append("properties", JSON.stringify(mergedProperties))
111+
112+
formData.append(
113+
"file",
114+
new File([JSON.stringify(messages)], "task.json", {
115+
type: "application/json",
116+
}),
117+
)
118+
119+
if (this.debug) {
120+
console.info(
121+
`[TelemetryClient#backfillMessages] Uploading ${messages.length} messages for task ${taskId}`,
122+
)
123+
}
124+
125+
// Custom fetch for multipart - don't set Content-Type header (let browser set it)
126+
const response = await fetch(`${getRooCodeApiUrl()}/api/events/backfill`, {
127+
method: "POST",
128+
headers: {
129+
Authorization: `Bearer ${token}`,
130+
// Note: No Content-Type header - browser will set multipart/form-data with boundary
131+
},
132+
body: formData,
133+
})
134+
135+
if (!response.ok) {
136+
console.error(
137+
`[TelemetryClient#backfillMessages] POST events/backfill -> ${response.status} ${response.statusText}`,
138+
)
139+
} else if (this.debug) {
140+
console.info(`[TelemetryClient#backfillMessages] Successfully uploaded messages for task ${taskId}`)
141+
}
142+
} catch (error) {
143+
console.error(`[TelemetryClient#backfillMessages] Error uploading messages: ${error}`)
144+
}
145+
}
146+
82147
public override updateTelemetryState(_didUserOptIn: boolean) {}
83148

84149
public override isTelemetryEnabled(): boolean {

0 commit comments

Comments
 (0)