diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index 05daa2d0..79c4f506 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -402,7 +402,7 @@ importers: version: link:../../packages/realtime-api '@pmmmwh/react-refresh-webpack-plugin': specifier: ^0.5.3 - version: 0.5.15(react-refresh@0.11.0)(type-fest@0.21.3)(webpack-dev-server@4.15.2(webpack@5.95.0))(webpack@5.95.0) + version: 0.5.15(react-refresh@0.11.0)(type-fest@0.21.3)(webpack-dev-server@4.15.2(webpack@5.95.0(@swc/core@1.7.39)))(webpack@5.95.0(@swc/core@1.7.39)) '@svgr/webpack': specifier: ^5.5.0 version: 5.5.0 @@ -414,7 +414,7 @@ importers: version: 27.5.1(@babel/core@7.25.9) babel-loader: specifier: ^8.2.3 - version: 8.4.1(@babel/core@7.25.9)(webpack@5.95.0) + version: 8.4.1(@babel/core@7.25.9)(webpack@5.95.0(@swc/core@1.7.39)) babel-plugin-named-asset-import: specifier: ^0.3.8 version: 0.3.8(@babel/core@7.25.9) @@ -435,10 +435,10 @@ importers: version: 2.4.0 css-loader: specifier: ^6.5.1 - version: 6.11.0(webpack@5.95.0) + version: 6.11.0(webpack@5.95.0(@swc/core@1.7.39)) css-minimizer-webpack-plugin: specifier: ^3.2.0 - version: 3.4.1(webpack@5.95.0) + version: 3.4.1(webpack@5.95.0(@swc/core@1.7.39)) dotenv: specifier: ^10.0.0 version: 10.0.0 @@ -447,19 +447,19 @@ importers: version: 5.1.0 file-loader: specifier: ^6.2.0 - version: 6.2.0(webpack@5.95.0) + version: 6.2.0(webpack@5.95.0(@swc/core@1.7.39)) fs-extra: specifier: ^10.0.0 version: 10.1.0 html-webpack-plugin: specifier: ^5.5.0 - version: 5.6.3(webpack@5.95.0) + version: 5.6.3(webpack@5.95.0(@swc/core@1.7.39)) identity-obj-proxy: specifier: ^3.0.0 version: 3.0.0 mini-css-extract-plugin: specifier: ^2.4.5 - version: 2.9.1(webpack@5.95.0) + version: 2.9.1(webpack@5.95.0(@swc/core@1.7.39)) postcss: specifier: ^8.4.4 version: 8.4.47 @@ -468,7 +468,7 @@ importers: version: 5.0.2(postcss@8.4.47) postcss-loader: specifier: ^6.2.1 - version: 6.2.1(postcss@8.4.47)(webpack@5.95.0) + version: 6.2.1(postcss@8.4.47)(webpack@5.95.0(@swc/core@1.7.39)) postcss-normalize: specifier: ^10.0.1 version: 10.0.1(browserslist@4.24.2)(postcss@8.4.47) @@ -486,7 +486,7 @@ importers: version: 3.0.0 react-dev-utils: specifier: ^12.0.1 - version: 12.0.1(eslint@8.57.1)(typescript@5.6.3)(webpack@5.95.0) + version: 12.0.1(eslint@8.57.1)(typescript@5.6.3)(webpack@5.95.0(@swc/core@1.7.39)) react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) @@ -501,37 +501,37 @@ importers: version: 4.0.0 sass-loader: specifier: ^12.3.0 - version: 12.6.0(webpack@5.95.0) + version: 12.6.0(webpack@5.95.0(@swc/core@1.7.39)) semver: specifier: ^7.3.5 version: 7.6.3 source-map-loader: specifier: ^3.0.0 - version: 3.0.2(webpack@5.95.0) + version: 3.0.2(webpack@5.95.0(@swc/core@1.7.39)) style-loader: specifier: ^3.3.1 - version: 3.3.4(webpack@5.95.0) + version: 3.3.4(webpack@5.95.0(@swc/core@1.7.39)) tailwindcss: specifier: ^3.0.2 - version: 3.4.14 + version: 3.4.14(ts-node@10.9.2(@swc/core@1.7.39)(@types/node@20.16.15)(typescript@5.6.3)) terser-webpack-plugin: specifier: ^5.2.5 - version: 5.3.10(webpack@5.95.0) + version: 5.3.10(@swc/core@1.7.39)(webpack@5.95.0(@swc/core@1.7.39)) web-vitals: specifier: ^2.1.4 version: 2.1.4 webpack: specifier: ^5.64.4 - version: 5.95.0 + version: 5.95.0(@swc/core@1.7.39) webpack-dev-server: specifier: ^4.6.0 - version: 4.15.2(webpack@5.95.0) + version: 4.15.2(webpack@5.95.0(@swc/core@1.7.39)) webpack-manifest-plugin: specifier: ^4.0.2 - version: 4.1.1(webpack@5.95.0) + version: 4.1.1(webpack@5.95.0(@swc/core@1.7.39)) workbox-webpack-plugin: specifier: ^6.4.1 - version: 6.6.0(@types/babel__core@7.20.5)(webpack@5.95.0) + version: 6.6.0(@types/babel__core@7.20.5)(webpack@5.95.0(@swc/core@1.7.39)) devDependencies: '@coze-infra/eslint-config': specifier: workspace:* diff --git a/config/vitest-config/src/preset-default.ts b/config/vitest-config/src/preset-default.ts index 79bbce74..9ee640a9 100644 --- a/config/vitest-config/src/preset-default.ts +++ b/config/vitest-config/src/preset-default.ts @@ -20,7 +20,7 @@ export const defaultVitestConfig: ViteUserConfig = { all: true, exclude: coverageConfigDefaults.exclude, provider: 'v8', - reporter: ['text', 'html', 'clover', 'json', 'v8', 'istanbul'], + reporter: ['cobertura', 'text', 'html', 'clover', 'json', 'json-summary'], }, }, }; diff --git a/examples/coze-js-node/src/auth/auth-oauth-device.ts b/examples/coze-js-node/src/auth/auth-oauth-device.ts index 5b631505..78567a62 100644 --- a/examples/coze-js-node/src/auth/auth-oauth-device.ts +++ b/examples/coze-js-node/src/auth/auth-oauth-device.ts @@ -7,7 +7,12 @@ * The process involves obtaining a device code, displaying it to the user, and then polling for the access token. */ -import { APIError, getDeviceCode, getDeviceToken } from '@coze/api'; +import { + APIError, + getDeviceCode, + getDeviceToken, + refreshOAuthToken, +} from '@coze/api'; import config from '../config/config'; import { sleep } from '../client'; @@ -44,9 +49,18 @@ while (true) { deviceCode: deviceCode.device_code, }); - // If successful, log the token and exit the loop if (deviceToken.access_token) { console.log('deviceToken', deviceToken); + + // You can refresh the access token if it expires + const refreshToken = deviceToken.refresh_token; + const refreshTokenResult = await refreshOAuthToken({ + baseURL, + clientId, + refreshToken, + }); + console.log('refreshTokenResult', refreshTokenResult); + break; } } catch (error) { diff --git a/packages/coze-js/src/auth.ts b/packages/coze-js/src/auth.ts index 68752d53..bf243fb1 100644 --- a/packages/coze-js/src/auth.ts +++ b/packages/coze-js/src/auth.ts @@ -64,6 +64,12 @@ export const getPKCEAuthenticationUrl = async ( code_challenge: codeChallenge, code_challenge_method: config.code_challenge_method || 'S256', }); + if (config.workspaceId) { + return { + url: `${baseUrl}/api/permission/oauth2/workspace_id/${config.workspaceId}/authorize?${params.toString()}`, + codeVerifier, + }; + } return { url: `${baseUrl}/api/permission/oauth2/authorize?${params.toString()}`, codeVerifier, @@ -151,7 +157,12 @@ export const getDeviceCode = async ( } const api = new APIClient({ token: '', baseURL: config.baseURL }); - const apiUrl = '/api/permission/oauth2/device/code'; + let apiUrl; + if (config.workspaceId) { + apiUrl = `/api/permission/oauth2/workspace_id/${config.workspaceId}/device/code`; + } else { + apiUrl = '/api/permission/oauth2/device/code'; + } const payload = { client_id: config.clientId, }; @@ -264,6 +275,7 @@ export interface WebAuthenticationConfig { export interface PKCEAuthenticationConfig extends WebAuthenticationConfig { code_challenge_method?: string; + workspaceId?: string; } export interface WebOAuthTokenConfig { @@ -292,6 +304,7 @@ export interface RefreshOAuthTokenConfig { export interface DeviceCodeConfig { baseURL?: string; clientId: string; + workspaceId?: string; } export interface DeviceTokenConfig { diff --git a/packages/coze-js/test/auth.spec.ts b/packages/coze-js/test/auth.spec.ts index 680227f4..f70a635d 100644 --- a/packages/coze-js/test/auth.spec.ts +++ b/packages/coze-js/test/auth.spec.ts @@ -126,6 +126,18 @@ describe('Auth functions', () => { expect(url).toContain('&code_challenge_method=S256'); expect(codeVerifier).toBeTruthy(); }); + + it('should return the correct PKCE authentication URL with workspaceId', async () => { + const { url, codeVerifier } = await getPKCEAuthenticationUrl({ + ...mockConfig, + workspaceId: '123', + }); + expect(url).toContain( + 'https://www.coze.com/api/permission/oauth2/workspace_id/123/authorize?response_type=code&client_id=test-client-id&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback&state=test-state&code_challenge=', + ); + expect(url).toContain('&code_challenge_method=S256'); + expect(codeVerifier).toBeTruthy(); + }); }); describe('getOAuthToken', () => { @@ -185,6 +197,29 @@ describe('Auth functions', () => { undefined, ); }); + it('should return the correct device code with workspaceId', async () => { + const mockPost = vi + .fn() + .mockResolvedValue({ device_code: 'test-device-code' }); + (APIClient as unknown as vi.Mock).mockImplementation(() => ({ + post: mockPost, + })); + + await getDeviceCode({ + clientId: mockConfig.clientId, + baseURL: mockConfig.baseURL, + workspaceId: '123', + }); + + expect(mockPost).toHaveBeenCalledWith( + '/api/permission/oauth2/workspace_id/123/device/code', + { + client_id: mockConfig.clientId, + }, + false, + undefined, + ); + }); }); describe('getDeviceToken', () => { diff --git a/packages/realtime-api/test/client.spec.ts b/packages/realtime-api/test/client.spec.ts index 3a7bda46..1c65d527 100644 --- a/packages/realtime-api/test/client.spec.ts +++ b/packages/realtime-api/test/client.spec.ts @@ -1,5 +1,6 @@ import VERTC from '@volcengine/rtc'; +import * as RealtimeUtils from '../src/utils'; import { RealtimeAPIError } from '../src/error'; import { EngineClient } from '../src/client'; @@ -96,14 +97,14 @@ describe('EngineClient', () => { describe('getDevices', () => { it('should return audio input devices', async () => { - const devices = await client.getDevices(); + const devices = await RealtimeUtils.getAudioDevices(); expect(devices.audioInputs).toHaveLength(2); }); it('should handle device enumeration errors', async () => { (VERTC.enumerateDevices as vi.Mock).mockRejectedValue( new Error('Enumeration failed'), ); - await expect(client.getDevices()).rejects.toThrow(Error); + await expect(RealtimeUtils.getAudioDevices()).rejects.toThrow(Error); }); });