Skip to content

Commit 4bad1fa

Browse files
committed
Merge branch 'main' into feat/issue-4258-latex-rendering
2 parents 8425a9d + 48c9bd5 commit 4bad1fa

File tree

343 files changed

+13509
-16812
lines changed

Some content is hidden

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

343 files changed

+13509
-16812
lines changed

.github/actions/setup-node-pnpm/action.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,21 @@ runs:
2727
uses: pnpm/action-setup@v4
2828
with:
2929
version: ${{ inputs.pnpm-version }}
30+
- name: Get pnpm store directory
31+
shell: bash
32+
run: |
33+
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
34+
- name: Setup pnpm cache
35+
uses: actions/cache@v4
36+
with:
37+
path: ${{ env.STORE_PATH }}
38+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
39+
restore-keys: |
40+
${{ runner.os }}-pnpm-store-
3041
- name: Setup Node.js
3142
uses: actions/setup-node@v4
3243
with:
3344
node-version: ${{ inputs.node-version }}
34-
cache: "pnpm"
3545
- name: Install dependencies
3646
if: ${{ inputs.skip-install != 'true' }}
3747
shell: bash

.roo/rules/rules.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
- Before attempting completion, always make sure that any code changes have test coverage
66
- Ensure all tests pass before submitting changes
7+
- The vitest framework is used for testing; the `describe`, `test`, `it`, etc functions are defined by default in `tsconfig.json` and therefore don't need to be imported
8+
- Tests must be run from the same directory as the `package.json` file that specifies `vitest` in `devDependencies`
79

810
2. Lint Rules:
911

apps/web-evals/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,6 @@
5454
"@types/react": "^18.3.23",
5555
"@types/react-dom": "^18.3.5",
5656
"tailwindcss": "^4",
57-
"vitest": "^3.2.1"
57+
"vitest": "^3.2.3"
5858
}
5959
}

packages/build/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@
1919
"@roo-code/config-eslint": "workspace:^",
2020
"@roo-code/config-typescript": "workspace:^",
2121
"@types/node": "20.x",
22-
"vitest": "^3.1.3"
22+
"vitest": "^3.2.3"
2323
}
2424
}

packages/cloud/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@
2121
"@roo-code/config-typescript": "workspace:^",
2222
"@types/node": "20.x",
2323
"@types/vscode": "^1.84.0",
24-
"vitest": "^3.1.3"
24+
"vitest": "^3.2.3"
2525
}
2626
}

packages/cloud/src/AuthService.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import axios from "axios"
55
import * as vscode from "vscode"
66
import { z } from "zod"
77

8-
import type { CloudUserInfo } from "@roo-code/types"
8+
import type { CloudUserInfo, CloudOrganizationMembership } from "@roo-code/types"
99

1010
import { getClerkBaseUrl, getRooCodeApiUrl } from "./Config"
1111
import { RefreshTimer } from "./RefreshTimer"
@@ -420,9 +420,42 @@ export class AuthService extends EventEmitter<AuthServiceEvents> {
420420
}
421421

422422
userInfo.picture = userData?.image_url
423+
424+
// Fetch organization memberships separately
425+
try {
426+
const orgMemberships = await this.clerkGetOrganizationMemberships()
427+
if (orgMemberships && orgMemberships.length > 0) {
428+
// Get the first (or active) organization membership
429+
const primaryOrgMembership = orgMemberships[0]
430+
const organization = primaryOrgMembership?.organization
431+
432+
if (organization) {
433+
userInfo.organizationId = organization.id
434+
userInfo.organizationName = organization.name
435+
userInfo.organizationRole = primaryOrgMembership.role
436+
}
437+
}
438+
} catch (error) {
439+
this.log("[auth] Failed to fetch organization memberships:", error)
440+
// Don't throw - organization info is optional
441+
}
442+
423443
return userInfo
424444
}
425445

446+
private async clerkGetOrganizationMemberships(): Promise<CloudOrganizationMembership[]> {
447+
const response = await axios.get(`${getClerkBaseUrl()}/v1/me/organization_memberships`, {
448+
headers: {
449+
Authorization: `Bearer ${this.credentials!.clientToken}`,
450+
"User-Agent": this.userAgent(),
451+
},
452+
})
453+
454+
// The response structure is: { response: [...] }
455+
// Extract the organization memberships from the response.response array
456+
return response.data?.response || []
457+
}
458+
426459
private async clerkLogout(credentials: AuthCredentials): Promise<void> {
427460
const formData = new URLSearchParams()
428461
formData.append("_is_native", "1")

packages/cloud/src/CloudService.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,24 @@ export class CloudService {
9292
return this.authService!.getUserInfo()
9393
}
9494

95+
public getOrganizationId(): string | null {
96+
this.ensureInitialized()
97+
const userInfo = this.authService!.getUserInfo()
98+
return userInfo?.organizationId || null
99+
}
100+
101+
public getOrganizationName(): string | null {
102+
this.ensureInitialized()
103+
const userInfo = this.authService!.getUserInfo()
104+
return userInfo?.organizationName || null
105+
}
106+
107+
public getOrganizationRole(): string | null {
108+
this.ensureInitialized()
109+
const userInfo = this.authService!.getUserInfo()
110+
return userInfo?.organizationRole || null
111+
}
112+
95113
public getAuthState(): string {
96114
this.ensureInitialized()
97115
return this.authService!.getState()

packages/cloud/src/Config.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
1-
export const getClerkBaseUrl = () => process.env.CLERK_BASE_URL || "https://clerk.roocode.com"
2-
export const getRooCodeApiUrl = () => process.env.ROO_CODE_API_URL || "https://app.roocode.com"
1+
// Production constants
2+
export const PRODUCTION_CLERK_BASE_URL = "https://clerk.roocode.com"
3+
export const PRODUCTION_ROO_CODE_API_URL = "https://app.roocode.com"
4+
5+
// Functions with environment variable fallbacks
6+
export const getClerkBaseUrl = () => process.env.CLERK_BASE_URL || PRODUCTION_CLERK_BASE_URL
7+
export const getRooCodeApiUrl = () => process.env.ROO_CODE_API_URL || PRODUCTION_ROO_CODE_API_URL

packages/cloud/src/__mocks__/vscode.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
2-
import { vi } from "vitest"
32

43
export const window = {
54
showInformationMessage: vi.fn(),

packages/cloud/src/__tests__/CloudService.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,72 @@ describe("CloudService", () => {
184184
expect(mockAuthService.getUserInfo).toHaveBeenCalled()
185185
})
186186

187+
it("should return organization ID from user info", () => {
188+
const mockUserInfo = {
189+
name: "Test User",
190+
191+
organizationId: "org_123",
192+
organizationName: "Test Org",
193+
organizationRole: "admin",
194+
}
195+
mockAuthService.getUserInfo.mockReturnValue(mockUserInfo)
196+
197+
const result = cloudService.getOrganizationId()
198+
expect(mockAuthService.getUserInfo).toHaveBeenCalled()
199+
expect(result).toBe("org_123")
200+
})
201+
202+
it("should return null when no organization ID available", () => {
203+
mockAuthService.getUserInfo.mockReturnValue(null)
204+
205+
const result = cloudService.getOrganizationId()
206+
expect(result).toBe(null)
207+
})
208+
209+
it("should return organization name from user info", () => {
210+
const mockUserInfo = {
211+
name: "Test User",
212+
213+
organizationId: "org_123",
214+
organizationName: "Test Org",
215+
organizationRole: "admin",
216+
}
217+
mockAuthService.getUserInfo.mockReturnValue(mockUserInfo)
218+
219+
const result = cloudService.getOrganizationName()
220+
expect(mockAuthService.getUserInfo).toHaveBeenCalled()
221+
expect(result).toBe("Test Org")
222+
})
223+
224+
it("should return null when no organization name available", () => {
225+
mockAuthService.getUserInfo.mockReturnValue(null)
226+
227+
const result = cloudService.getOrganizationName()
228+
expect(result).toBe(null)
229+
})
230+
231+
it("should return organization role from user info", () => {
232+
const mockUserInfo = {
233+
name: "Test User",
234+
235+
organizationId: "org_123",
236+
organizationName: "Test Org",
237+
organizationRole: "admin",
238+
}
239+
mockAuthService.getUserInfo.mockReturnValue(mockUserInfo)
240+
241+
const result = cloudService.getOrganizationRole()
242+
expect(mockAuthService.getUserInfo).toHaveBeenCalled()
243+
expect(result).toBe("admin")
244+
})
245+
246+
it("should return null when no organization role available", () => {
247+
mockAuthService.getUserInfo.mockReturnValue(null)
248+
249+
const result = cloudService.getOrganizationRole()
250+
expect(result).toBe(null)
251+
})
252+
187253
it("should delegate getAuthState to AuthService", () => {
188254
const result = cloudService.getAuthState()
189255
expect(mockAuthService.getState).toHaveBeenCalled()

0 commit comments

Comments
 (0)