api2key-base-sdk 0.1.5 → 0.1.7

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/README.md CHANGED
@@ -16,7 +16,8 @@
16
16
  6. `orders`
17
17
  7. `payment`
18
18
  8. `ai`
19
- 9. `admin`
19
+ 9. `speech`
20
+ 10. `admin`
20
21
 
21
22
  安装原则:
22
23
 
@@ -36,7 +37,8 @@
36
37
  6. `orders`
37
38
  7. `payment`
38
39
  8. `ai`
39
- 9. `admin`
40
+ 9. `speech`
41
+ 10. `admin`
40
42
 
41
43
  ### 1. `auth`
42
44
 
@@ -76,9 +78,18 @@
76
78
 
77
79
  负责用户级设置的读取与更新。
78
80
 
79
- ### 5. `projects / orders / payment / ai`
81
+ ### 5. `projects / orders / payment / ai / speech`
80
82
 
81
- 负责项目商品、会员、订单、支付、AI 等运行时能力。
83
+ 负责项目商品、会员、订单、支付、AI 以及语音运行时能力。
84
+
85
+ 其中 `speech` 对齐当前 `api2key-base-api` 的内置语音接口:
86
+
87
+ 1. `listVoices(...)`
88
+ 2. `synthesize(...)`
89
+ 3. `transcribe(...)`
90
+ 4. `transcribeSrt(...)`
91
+ 5. `getAsrTask(...)`
92
+ 6. `downloadAudio(...)`
82
93
 
83
94
  ### 6. `admin`
84
95
 
@@ -132,6 +143,7 @@ await baseClient.auth.resetPassword({
132
143
  const projectClient = createProjectPlatformClient({
133
144
  baseUrl: process.env.API2KEY_BASE_URL!,
134
145
  getAccessToken: () => process.env.ACCESS_TOKEN,
146
+ getApiKey: () => process.env.API_KEY,
135
147
  getProjectId: () => process.env.PROJECT_ID,
136
148
  });
137
149
 
@@ -141,9 +153,185 @@ const adminClient = createAdminPlatformClient({
141
153
  });
142
154
 
143
155
  const products = await projectClient.projects.getCatalogProducts(process.env.PROJECT_ID!);
156
+ const voices = await projectClient.speech.listVoices({
157
+ provider: 'azure',
158
+ locale: 'zh-CN',
159
+ });
144
160
  const projects = await adminClient.admin.listProjects();
145
161
  ```
146
162
 
163
+ 说明:
164
+
165
+ 1. SDK 仍然接收单一 `baseUrl`
166
+ 2. `/api/v1/*` 接口继续基于 `baseUrl` 访问
167
+ 3. `speech` 客户端会自动基于同一域名访问根路径下的 `/api/*` 语音接口,无需额外配置第二个 URL
168
+
169
+ ## Speech 示例
170
+
171
+ 推荐在项目级客户端上使用:
172
+
173
+ ```ts
174
+ import { createProjectPlatformClient } from 'api2key-base-sdk';
175
+
176
+ const client = createProjectPlatformClient({
177
+ baseUrl: process.env.API2KEY_BASE_URL!,
178
+ getAccessToken: () => process.env.ACCESS_TOKEN,
179
+ getApiKey: () => process.env.API_KEY,
180
+ getProjectId: () => process.env.PROJECT_ID,
181
+ });
182
+ ```
183
+
184
+ ### 1. 获取声音列表
185
+
186
+ ```ts
187
+ const voices = await client.speech.listVoices({
188
+ provider: 'azure',
189
+ locale: 'zh-CN',
190
+ });
191
+
192
+ console.log(voices.provider);
193
+ console.log(voices.total);
194
+ console.log(voices.voices[0]?.shortName);
195
+ ```
196
+
197
+ ### 2. 文本转音频
198
+
199
+ ```ts
200
+ const synthesized = await client.speech.synthesize({
201
+ provider: 'azure',
202
+ text: '你好,欢迎使用 api2key speech client。',
203
+ locale: 'zh-CN',
204
+ voice: 'zh-CN-XiaoxiaoNeural',
205
+ format: 'audio-24khz-96kbitrate-mono-mp3',
206
+ rate: 1,
207
+ volume: 100,
208
+ pitch: 0,
209
+ });
210
+
211
+ console.log(synthesized.contentType);
212
+ console.log(synthesized.creditsCharged);
213
+ console.log(synthesized.storageKey);
214
+ console.log(synthesized.downloadUrl);
215
+ console.log(synthesized.audio.byteLength);
216
+ ```
217
+
218
+ 如果你要把返回音频写入文件,在 Node.js 中可以这样处理:
219
+
220
+ ```ts
221
+ import { writeFile } from 'node:fs/promises';
222
+
223
+ await writeFile('speech.mp3', Buffer.from(synthesized.audio));
224
+ ```
225
+
226
+ ### 3. 简洁音频转文本
227
+
228
+ ```ts
229
+ import { readFile } from 'node:fs/promises';
230
+
231
+ const audioBytes = await readFile('./sample.mp3');
232
+ const audioBlob = new Blob([audioBytes], { type: 'audio/mpeg' });
233
+
234
+ const transcript = await client.speech.transcribe({
235
+ provider: 'groq',
236
+ file: audioBlob,
237
+ fileName: 'sample.mp3',
238
+ });
239
+
240
+ console.log(transcript.language);
241
+ console.log(transcript.full_text);
242
+ console.log(transcript.segments.length);
243
+ ```
244
+
245
+ 也可以直接传远程地址:
246
+
247
+ ```ts
248
+ const transcript = await client.speech.transcribe({
249
+ provider: 'groq',
250
+ audioUrl: 'https://example.com/sample.mp3',
251
+ });
252
+ ```
253
+
254
+ ### 4. 音频转 SRT
255
+
256
+ 同步模式:
257
+
258
+ ```ts
259
+ const srtResult = await client.speech.transcribeSrt({
260
+ provider: 'tencent',
261
+ file: audioBlob,
262
+ fileName: 'sample.mp3',
263
+ async: false,
264
+ });
265
+
266
+ if ('srt' in srtResult) {
267
+ console.log(srtResult.text);
268
+ console.log(srtResult.srt);
269
+ }
270
+ ```
271
+
272
+ 异步模式:
273
+
274
+ ```ts
275
+ const submitted = await client.speech.transcribeSrt({
276
+ provider: 'tencent',
277
+ file: audioBlob,
278
+ fileName: 'sample.mp3',
279
+ async: true,
280
+ });
281
+
282
+ if ('taskId' in submitted && !('srt' in submitted)) {
283
+ console.log(submitted.taskId);
284
+ console.log(submitted.statusStr);
285
+ }
286
+ ```
287
+
288
+ ### 5. 查询异步字幕任务
289
+
290
+ ```ts
291
+ const task = await client.speech.getAsrTask({
292
+ provider: 'tencent',
293
+ taskId: 123456,
294
+ });
295
+
296
+ console.log(task.status);
297
+ console.log(task.statusStr);
298
+ console.log(task.retryAfterSeconds);
299
+ console.log(task.srt);
300
+ ```
301
+
302
+ ### 6. 通过存储 key 下载合成音频
303
+
304
+ ```ts
305
+ const downloaded = await client.speech.downloadAudio({
306
+ key: synthesized.storageKey!,
307
+ });
308
+
309
+ console.log(downloaded.contentType);
310
+ console.log(downloaded.fileName);
311
+ console.log(downloaded.audio.byteLength);
312
+ ```
313
+
314
+ ### 7. 鉴权建议
315
+
316
+ 推荐方式:
317
+
318
+ 1. 服务端项目调用时,通过 `getApiKey()` 提供 `sk-...`
319
+ 2. 用户登录态场景,通过 `getAccessToken()` 提供 JWT
320
+ 3. 大多数项目建议同时提供 `getProjectId()`,保持语音接口和项目上下文一致
321
+
322
+ ### 8. 路径说明
323
+
324
+ 你只需要传一个 `baseUrl`,例如:
325
+
326
+ ```ts
327
+ baseUrl: 'https://open.api2key.com/api/v1'
328
+ ```
329
+
330
+ SDK 会自动处理:
331
+
332
+ 1. 普通平台接口继续访问 `/api/v1/*`
333
+ 2. speech client 自动访问同域名下的 `/api/*`
334
+
147
335
  ## 设计原则
148
336
 
149
337
  1. 新项目默认只依赖这个包
@@ -50,7 +50,7 @@ export declare class AdminClient {
50
50
  }>;
51
51
  }>;
52
52
  createMembership(input: {
53
- scopeType: 'project' | 'global';
53
+ scopeType: 'project' | 'account';
54
54
  userId?: string;
55
55
  email?: string;
56
56
  projectId?: string;
@@ -63,11 +63,11 @@ export declare class AdminClient {
63
63
  autoRenew?: boolean;
64
64
  }, accessToken?: string): Promise<{
65
65
  membershipId: string;
66
- scopeType: 'project' | 'global';
66
+ scopeType: 'project' | 'account';
67
67
  }>;
68
68
  updateMembership(input: {
69
69
  id: string;
70
- scopeType: 'project' | 'global';
70
+ scopeType: 'project' | 'account';
71
71
  projectId?: string;
72
72
  role?: string;
73
73
  tier?: string;
@@ -79,7 +79,7 @@ export declare class AdminClient {
79
79
  }, accessToken?: string): Promise<{
80
80
  id: string;
81
81
  }>;
82
- deleteMembership(id: string, scopeType: 'project' | 'global', accessToken?: string): Promise<null>;
82
+ deleteMembership(id: string, scopeType: 'project' | 'account', accessToken?: string): Promise<null>;
83
83
  listUsers(filters?: {
84
84
  search?: string;
85
85
  projectId?: string;
@@ -1,8 +1,9 @@
1
1
  import { PlatformHttpClient } from '../core/client';
2
- import type { AuthChangePasswordInput, AuthPasswordResetDispatchResult, AuthRegisterResult, AuthSession, AuthUser, AuthVerifyEmailInput, AuthResetPasswordInput } from '../core/types';
2
+ import type { AuthChangePasswordInput, AuthMeResponse, AuthPasswordResetDispatchResult, AuthRegisterResult, AuthSession, AuthVerifyEmailInput, AuthResetPasswordInput } from '../core/types';
3
3
  export declare class AuthClient {
4
4
  private readonly http;
5
5
  constructor(http: PlatformHttpClient);
6
+ private normalizeAuthOptions;
6
7
  login(input: {
7
8
  email: string;
8
9
  password: string;
@@ -22,10 +23,10 @@ export declare class AuthClient {
22
23
  email: string;
23
24
  }): Promise<AuthPasswordResetDispatchResult | null>;
24
25
  resetPassword(input: AuthResetPasswordInput): Promise<null>;
25
- me(accessToken?: string): Promise<{
26
- user: AuthUser;
27
- membership?: unknown;
28
- }>;
26
+ me(options?: string | {
27
+ accessToken?: string;
28
+ projectId?: string;
29
+ }): Promise<AuthMeResponse>;
29
30
  logout(accessToken?: string): Promise<null>;
30
31
  sendChangePasswordCode(accessToken?: string): Promise<AuthPasswordResetDispatchResult>;
31
32
  changePassword(input: AuthChangePasswordInput, accessToken?: string): Promise<null>;
@@ -2,6 +2,11 @@ export class AuthClient {
2
2
  constructor(http) {
3
3
  this.http = http;
4
4
  }
5
+ normalizeAuthOptions(options) {
6
+ return typeof options === 'string'
7
+ ? { accessToken: options, projectId: undefined }
8
+ : (options ?? {});
9
+ }
5
10
  login(input) {
6
11
  return this.http.request('/api/v1/auth/login', {
7
12
  method: 'POST',
@@ -38,9 +43,14 @@ export class AuthClient {
38
43
  body: input,
39
44
  });
40
45
  }
41
- me(accessToken) {
46
+ me(options) {
47
+ const normalizedOptions = this.normalizeAuthOptions(options);
42
48
  return this.http.request('/api/v1/auth/me', {
43
- accessToken,
49
+ accessToken: normalizedOptions.accessToken,
50
+ projectId: normalizedOptions.projectId,
51
+ query: {
52
+ projectId: normalizedOptions.projectId,
53
+ },
44
54
  });
45
55
  }
46
56
  logout(accessToken) {
@@ -4,6 +4,8 @@ export declare class PlatformHttpClient {
4
4
  private readonly fetchImpl;
5
5
  private readonly options;
6
6
  constructor(options: PlatformClientOptions);
7
+ private resolveBaseUrl;
8
+ private buildUrl;
7
9
  private send;
8
10
  request<T>(path: string, requestOptions?: PlatformRequestOptions): Promise<T>;
9
11
  requestRaw(path: string, requestOptions?: PlatformRequestOptions): Promise<Response>;
@@ -18,9 +18,29 @@ export class PlatformHttpClient {
18
18
  const fetchImpl = options.fetchImpl ?? fetch;
19
19
  this.fetchImpl = fetchImpl.bind(globalThis);
20
20
  }
21
+ resolveBaseUrl(mode = 'default') {
22
+ if (mode === 'origin') {
23
+ return new URL(this.baseUrl).origin;
24
+ }
25
+ return this.baseUrl;
26
+ }
27
+ buildUrl(path, requestOptions) {
28
+ if (/^https?:\/\//.test(path)) {
29
+ return `${path}${buildQueryString(requestOptions.query)}`;
30
+ }
31
+ return `${this.resolveBaseUrl(requestOptions.baseUrlMode)}${path}${buildQueryString(requestOptions.query)}`;
32
+ }
21
33
  async send(path, requestOptions = {}) {
22
34
  const headers = new Headers(this.options.defaultHeaders);
23
- headers.set('Content-Type', 'application/json');
35
+ if (requestOptions.contentType === null) {
36
+ headers.delete('Content-Type');
37
+ }
38
+ else if (requestOptions.contentType) {
39
+ headers.set('Content-Type', requestOptions.contentType);
40
+ }
41
+ else if (requestOptions.rawBody === undefined) {
42
+ headers.set('Content-Type', 'application/json');
43
+ }
24
44
  if (requestOptions.headers) {
25
45
  for (const [key, value] of Object.entries(requestOptions.headers)) {
26
46
  headers.set(key, value);
@@ -30,6 +50,10 @@ export class PlatformHttpClient {
30
50
  if (accessToken) {
31
51
  headers.set('Authorization', `Bearer ${accessToken}`);
32
52
  }
53
+ const apiKey = requestOptions.apiKey ?? (await this.options.getApiKey?.());
54
+ if (apiKey) {
55
+ headers.set('x-api-key', apiKey);
56
+ }
33
57
  const projectId = requestOptions.projectId ?? (await this.options.getProjectId?.());
34
58
  if (projectId) {
35
59
  headers.set('X-Project-Id', projectId);
@@ -41,13 +65,13 @@ export class PlatformHttpClient {
41
65
  }
42
66
  headers.set('X-Service-Secret', serviceSecret);
43
67
  }
44
- const url = `${this.baseUrl}${path}${buildQueryString(requestOptions.query)}`;
68
+ const url = this.buildUrl(path, requestOptions);
45
69
  let response;
46
70
  try {
47
71
  response = await this.fetchImpl(url, {
48
72
  method: requestOptions.method ?? 'GET',
49
73
  headers,
50
- body: requestOptions.body == null ? undefined : JSON.stringify(requestOptions.body),
74
+ body: requestOptions.rawBody ?? (requestOptions.body == null ? undefined : JSON.stringify(requestOptions.body)),
51
75
  });
52
76
  }
53
77
  catch (error) {
@@ -9,17 +9,22 @@ export interface PlatformClientOptions {
9
9
  fetchImpl?: typeof fetch;
10
10
  defaultHeaders?: Record<string, string>;
11
11
  getAccessToken?: () => MaybePromise<string | undefined>;
12
+ getApiKey?: () => MaybePromise<string | undefined>;
12
13
  getServiceSecret?: () => MaybePromise<string | undefined>;
13
14
  getProjectId?: () => MaybePromise<string | undefined>;
14
15
  }
15
16
  export interface PlatformRequestOptions {
16
17
  method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
17
18
  body?: unknown;
19
+ rawBody?: BodyInit;
18
20
  query?: Record<string, string | number | boolean | undefined | null>;
19
21
  headers?: Record<string, string>;
22
+ contentType?: string | null;
20
23
  accessToken?: string;
24
+ apiKey?: string;
21
25
  projectId?: string;
22
26
  useServiceSecret?: boolean;
27
+ baseUrlMode?: 'default' | 'origin';
23
28
  }
24
29
  export interface AuthUser {
25
30
  id: string;
@@ -29,12 +34,14 @@ export interface AuthUser {
29
34
  role: 'user' | 'admin' | 'super_admin';
30
35
  emailVerified?: boolean;
31
36
  emailVerifiedAt?: number | null;
37
+ credits?: number;
32
38
  creditsBalance?: number;
33
39
  projectId?: string | null;
34
40
  projectName?: string | null;
35
41
  projectSlug?: string | null;
36
42
  membership?: {
37
43
  tier: string;
44
+ status?: string;
38
45
  power?: number;
39
46
  endDate?: number | null;
40
47
  } | null;
@@ -58,6 +65,17 @@ export interface AuthSession {
58
65
  refreshToken?: string;
59
66
  expiresIn?: number;
60
67
  }
68
+ export interface ProjectScope {
69
+ type: 'account' | 'project';
70
+ projectId?: string | null;
71
+ projectName?: string | null;
72
+ projectSlug?: string | null;
73
+ }
74
+ export interface AuthMeResponse {
75
+ user: AuthUser;
76
+ membership?: unknown;
77
+ scope?: ProjectScope;
78
+ }
61
79
  export interface AuthRegistrationPending {
62
80
  requiresEmailVerification: true;
63
81
  email: string;
@@ -124,11 +142,26 @@ export interface MembershipView {
124
142
  is_active?: boolean;
125
143
  days_remaining?: number;
126
144
  }
145
+ export interface CreditsAccountSummary {
146
+ balance: number;
147
+ reserved: number;
148
+ totalEarned: number;
149
+ totalSpent: number;
150
+ }
151
+ export interface CreditsBalanceScope {
152
+ type: ProjectScope['type'];
153
+ projectId?: ProjectScope['projectId'];
154
+ projectName?: ProjectScope['projectName'];
155
+ projectSlug?: ProjectScope['projectSlug'];
156
+ }
127
157
  export interface CreditsBalance {
128
158
  balance: number;
129
- reserved?: number;
130
- total_earned?: number;
131
- total_spent?: number;
159
+ reserved: number;
160
+ total_earned: number;
161
+ total_spent: number;
162
+ account: CreditsAccountSummary;
163
+ scope: CreditsBalanceScope;
164
+ user: AuthUser;
132
165
  }
133
166
  export interface CreditsLedgerItem {
134
167
  id: string;
@@ -219,6 +252,174 @@ export interface AiModelSummary {
219
252
  outputPricing?: number;
220
253
  locked?: boolean;
221
254
  }
255
+ export type SpeechTtsProvider = 'azure' | 'tencent';
256
+ export type SpeechAsrProvider = 'tencent' | 'groq' | 'bcut';
257
+ export interface SpeechVoice {
258
+ provider: SpeechTtsProvider;
259
+ shortName: string;
260
+ displayName: string;
261
+ localName: string;
262
+ locale: string;
263
+ localeName: string;
264
+ gender: string;
265
+ voiceType?: string;
266
+ styles: string[];
267
+ roles: string[];
268
+ sampleRateHertz?: string;
269
+ sampleRates?: string[];
270
+ status?: string;
271
+ wordsPerMinute?: string;
272
+ secondaryLocales: string[];
273
+ recommendedScene?: string;
274
+ supportedLanguages?: string[];
275
+ emotions?: string[];
276
+ }
277
+ export interface SpeechAccessUser {
278
+ id: string;
279
+ email: string;
280
+ role: string;
281
+ }
282
+ export interface SpeechAccessProject {
283
+ id: string;
284
+ name: string;
285
+ slug: string;
286
+ }
287
+ export interface SpeechAccessMembership {
288
+ tier: string;
289
+ status: string;
290
+ end_date: number | null;
291
+ is_active: boolean;
292
+ }
293
+ export interface SpeechVoiceListResult {
294
+ provider: SpeechTtsProvider;
295
+ user: SpeechAccessUser;
296
+ project: SpeechAccessProject;
297
+ membership: SpeechAccessMembership;
298
+ total: number;
299
+ voices: SpeechVoice[];
300
+ }
301
+ export interface SpeechSynthesisRequest {
302
+ provider?: SpeechTtsProvider;
303
+ text: string;
304
+ voice?: string;
305
+ locale?: string;
306
+ rate?: number;
307
+ volume?: number;
308
+ pitch?: number;
309
+ style?: string;
310
+ styleDegree?: number;
311
+ role?: string;
312
+ emotionCategory?: string;
313
+ emotionIntensity?: number;
314
+ format?: string;
315
+ }
316
+ export interface SpeechSynthesisResult {
317
+ audio: ArrayBuffer;
318
+ contentType: string;
319
+ fileName: string | null;
320
+ provider: string | null;
321
+ voice: string | null;
322
+ locale: string | null;
323
+ format: string | null;
324
+ creditsCharged: number | null;
325
+ storageKey: string | null;
326
+ downloadUrl: string | null;
327
+ projectId: string | null;
328
+ providerRequestId: string | null;
329
+ headers: Headers;
330
+ }
331
+ export interface SpeechSimpleTranscriptSegment {
332
+ start: number;
333
+ end: number;
334
+ text: string;
335
+ }
336
+ export interface SpeechTranscribeResult {
337
+ language: string;
338
+ full_text: string;
339
+ segments: SpeechSimpleTranscriptSegment[];
340
+ srt: string;
341
+ }
342
+ export interface SpeechAsrSegment {
343
+ id: number;
344
+ startMs: number;
345
+ endMs: number;
346
+ startTimecode: string;
347
+ endTimecode: string;
348
+ text: string;
349
+ speakerId?: number;
350
+ emotionTypes?: string[];
351
+ }
352
+ export interface SpeechAsrSrtResult {
353
+ user: SpeechAccessUser;
354
+ project: SpeechAccessProject;
355
+ membership: SpeechAccessMembership;
356
+ provider: SpeechAsrProvider;
357
+ format: 'srt';
358
+ engineModelType: string;
359
+ taskId: number | null;
360
+ requestId?: string | null;
361
+ language: string;
362
+ durationMs: number;
363
+ sourceType: 'file' | 'url';
364
+ sourceName?: string | null;
365
+ sourceSize?: number | null;
366
+ text: string;
367
+ srt: string;
368
+ segments: SpeechAsrSegment[];
369
+ }
370
+ export interface SpeechAsrTaskSubmission {
371
+ user: SpeechAccessUser;
372
+ project: SpeechAccessProject;
373
+ membership: SpeechAccessMembership;
374
+ provider: SpeechAsrProvider;
375
+ taskId: number;
376
+ requestId?: string | null;
377
+ engineModelType: string;
378
+ status: number;
379
+ statusStr: string;
380
+ sourceType: 'file' | 'url';
381
+ sourceName?: string | null;
382
+ sourceSize?: number | null;
383
+ }
384
+ export interface SpeechAsrTaskResult {
385
+ user: SpeechAccessUser;
386
+ project: SpeechAccessProject;
387
+ membership: SpeechAccessMembership;
388
+ provider: SpeechAsrProvider;
389
+ format: 'srt';
390
+ taskId: number;
391
+ requestId?: string | null;
392
+ status: number;
393
+ statusStr: string;
394
+ retryAfterSeconds?: number | null;
395
+ durationMs: number;
396
+ language: string;
397
+ audioDurationSeconds?: number | null;
398
+ errorMessage?: string | null;
399
+ text: string;
400
+ srt: string;
401
+ segments: SpeechAsrSegment[];
402
+ }
403
+ export interface SpeechAsrRequestInput {
404
+ provider?: SpeechAsrProvider;
405
+ audioUrl?: string;
406
+ file?: Blob;
407
+ fileName?: string;
408
+ async?: boolean;
409
+ language?: string;
410
+ prompt?: string;
411
+ temperature?: number;
412
+ engineModelType?: string;
413
+ channelNum?: number;
414
+ speakerDiarization?: number;
415
+ speakerNumber?: number;
416
+ sentenceMaxLength?: number;
417
+ emotionRecognition?: number;
418
+ filterDirty?: number;
419
+ filterModal?: number;
420
+ filterPunc?: number;
421
+ convertNumMode?: number;
422
+ }
222
423
  export interface AdminProject {
223
424
  id: string;
224
425
  name: string;
@@ -251,7 +452,7 @@ export interface AdminProduct {
251
452
  }
252
453
  export interface AdminMembershipRecord {
253
454
  id: string;
254
- scopeType: 'project' | 'global';
455
+ scopeType: 'project' | 'account';
255
456
  role: string;
256
457
  tier: string;
257
458
  status: string;
@@ -276,7 +477,7 @@ export interface AdminMembershipRecord {
276
477
  } | null;
277
478
  }
278
479
  export interface AdminMembershipFilters {
279
- scope?: 'project' | 'global';
480
+ scope?: 'project' | 'account';
280
481
  projectId?: string;
281
482
  status?: string;
282
483
  tier?: string;
@@ -295,7 +496,7 @@ export interface AdminUserRecord {
295
496
  name: string;
296
497
  slug: string;
297
498
  } | null;
298
- globalMembership: {
499
+ accountMembership: {
299
500
  id: string;
300
501
  tier: string | null;
301
502
  status: string | null;
@@ -375,7 +576,26 @@ export interface AdminUserCreditsSummary {
375
576
  total_earned: number;
376
577
  total_spent: number;
377
578
  };
378
- recentLedger: CreditsLedgerItem[];
579
+ projectAccounts?: Array<{
580
+ id: string;
581
+ projectId: string;
582
+ projectName?: string | null;
583
+ projectSlug?: string | null;
584
+ role: string;
585
+ tier: string;
586
+ status: string;
587
+ credits: number;
588
+ startDate?: number | null;
589
+ endDate?: number | null;
590
+ autoRenew: boolean;
591
+ }>;
592
+ recentLedger: Array<CreditsLedgerItem & {
593
+ scopeType?: 'account' | 'project';
594
+ projectId?: string | null;
595
+ projectName?: string | null;
596
+ projectSlug?: string | null;
597
+ refId?: string | null;
598
+ }>;
379
599
  }
380
600
  export interface AdminModelRecord {
381
601
  id: string;
@@ -1,9 +1,13 @@
1
1
  import { PlatformHttpClient } from '../core/client';
2
2
  import type { CreditsBalance, CreditsLedgerItem } from '../core/types';
3
+ type GetBalanceOptions = {
4
+ accessToken?: string;
5
+ projectId?: string;
6
+ };
3
7
  export declare class CreditsClient {
4
8
  private readonly http;
5
9
  constructor(http: PlatformHttpClient);
6
- getBalance(accessToken?: string): Promise<CreditsBalance>;
10
+ getBalance(options?: string | GetBalanceOptions): Promise<CreditsBalance>;
7
11
  getLedger(input: {
8
12
  accessToken?: string;
9
13
  page?: number;
@@ -38,3 +42,4 @@ export declare class CreditsClient {
38
42
  ok: boolean;
39
43
  }>;
40
44
  }
45
+ export {};
@@ -2,8 +2,17 @@ export class CreditsClient {
2
2
  constructor(http) {
3
3
  this.http = http;
4
4
  }
5
- getBalance(accessToken) {
6
- return this.http.request('/api/v1/credits/balance', { accessToken });
5
+ getBalance(options) {
6
+ const normalizedOptions = typeof options === 'string'
7
+ ? { accessToken: options }
8
+ : (options ?? {});
9
+ return this.http.request('/api/v1/credits/balance', {
10
+ accessToken: normalizedOptions.accessToken,
11
+ projectId: normalizedOptions.projectId,
12
+ query: {
13
+ projectId: normalizedOptions.projectId,
14
+ },
15
+ });
7
16
  }
8
17
  getLedger(input) {
9
18
  return this.http.request('/api/v1/credits/ledger', {
package/dist/index.d.ts CHANGED
@@ -7,10 +7,11 @@ import { CreditsClient } from './credits/client';
7
7
  import { OrdersClient } from './orders/client';
8
8
  import { PaymentClient } from './payment/client';
9
9
  import { ProjectsClient } from './projects/client';
10
+ import { SpeechClient } from './speech/client';
10
11
  import { UserApiKeysClient } from './user/api-keys-client';
11
12
  import { UserSettingsClient } from './user/settings-client';
12
13
  export * from './core/errors';
13
- export type { AdminConfigItem, AdminMembershipFilters, AdminMembershipRecord, AdminModelInput, AdminModelRecord, AdminOrderFilters, AdminOrderRecord, AdminProduct, AdminProductBackfillResult, AdminProductListResult, AdminProject, AdminProjectExportFormat, AdminUserCreditsSummary, AdminUserRecord, AiModelSummary, AuthChangePasswordInput, AuthPasswordResetDispatchResult, AuthRegisterResult, AuthRegistrationPending, AuthSession, AuthUser, AuthVerificationDispatchResult, AuthVerifyEmailInput, AuthResetPasswordInput, CreditsBalance, CreditsLedgerItem, MembershipView, OrderDetail, OrderSummary, PaymentCreateResponse, PaymentQueryResponse, PlatformClientOptions, PlatformEnvelope, PlatformRequestOptions, ProductSummary, ProjectSummary, UpdateAdminUserInput, UserApiKeyRecord, UserApiKeySecret, UserSettingDefinition, UserSettingsPayload, } from './core/types';
14
+ export type { AdminConfigItem, AdminMembershipFilters, AdminMembershipRecord, AdminModelInput, AdminModelRecord, AdminOrderFilters, AdminOrderRecord, AdminProduct, AdminProductBackfillResult, AdminProductListResult, AdminProject, AdminProjectExportFormat, AdminUserCreditsSummary, AdminUserRecord, AiModelSummary, AuthChangePasswordInput, AuthMeResponse, AuthPasswordResetDispatchResult, AuthRegisterResult, AuthRegistrationPending, AuthSession, AuthUser, AuthVerificationDispatchResult, AuthVerifyEmailInput, CreditsAccountSummary, AuthResetPasswordInput, CreditsBalance, CreditsBalanceScope, CreditsLedgerItem, MembershipView, OrderDetail, OrderSummary, PaymentCreateResponse, PaymentQueryResponse, PlatformClientOptions, PlatformEnvelope, PlatformRequestOptions, ProjectScope, ProductSummary, ProjectSummary, SpeechAccessMembership, SpeechAccessProject, SpeechAccessUser, SpeechAsrProvider, SpeechAsrRequestInput, SpeechAsrSegment, SpeechAsrSrtResult, SpeechAsrTaskResult, SpeechAsrTaskSubmission, SpeechSimpleTranscriptSegment, SpeechSynthesisRequest, SpeechSynthesisResult, SpeechTranscribeResult, SpeechTtsProvider, SpeechVoice, SpeechVoiceListResult, UpdateAdminUserInput, UserApiKeyRecord, UserApiKeySecret, UserSettingDefinition, UserSettingsPayload, } from './core/types';
14
15
  export declare class BasePlatformClient {
15
16
  readonly http: PlatformHttpClient;
16
17
  readonly auth: AuthClient;
@@ -26,6 +27,7 @@ export declare class ProjectPlatformClient {
26
27
  readonly orders: OrdersClient;
27
28
  readonly payment: PaymentClient;
28
29
  readonly ai: AiClient;
30
+ readonly speech: SpeechClient;
29
31
  constructor(options: PlatformClientOptions);
30
32
  }
31
33
  export declare function createProjectPlatformClient(options: PlatformClientOptions): ProjectPlatformClient;
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { CreditsClient } from './credits/client.js';
6
6
  import { OrdersClient } from './orders/client.js';
7
7
  import { PaymentClient } from './payment/client.js';
8
8
  import { ProjectsClient } from './projects/client.js';
9
+ import { SpeechClient } from './speech/client.js';
9
10
  import { UserApiKeysClient } from './user/api-keys-client.js';
10
11
  import { UserSettingsClient } from './user/settings-client.js';
11
12
  export * from './core/errors.js';
@@ -28,6 +29,7 @@ export class ProjectPlatformClient {
28
29
  this.orders = new OrdersClient(this.http);
29
30
  this.payment = new PaymentClient(this.http);
30
31
  this.ai = new AiClient(this.http);
32
+ this.speech = new SpeechClient(this.http);
31
33
  }
32
34
  }
33
35
  export function createProjectPlatformClient(options) {
@@ -0,0 +1,32 @@
1
+ import { PlatformHttpClient } from '../core/client';
2
+ import type { SpeechAsrRequestInput, SpeechAsrSrtResult, SpeechAsrTaskResult, SpeechAsrTaskSubmission, SpeechSynthesisRequest, SpeechSynthesisResult, SpeechTranscribeResult, SpeechVoiceListResult } from '../core/types';
3
+ type SpeechRequestAuth = {
4
+ accessToken?: string;
5
+ apiKey?: string;
6
+ projectId?: string;
7
+ };
8
+ export declare class SpeechClient {
9
+ private readonly http;
10
+ constructor(http: PlatformHttpClient);
11
+ listVoices(input?: SpeechRequestAuth & {
12
+ provider?: 'azure' | 'tencent';
13
+ locale?: string;
14
+ search?: string;
15
+ }): Promise<SpeechVoiceListResult>;
16
+ synthesize(input: SpeechRequestAuth & SpeechSynthesisRequest): Promise<SpeechSynthesisResult>;
17
+ transcribe(input: SpeechRequestAuth & SpeechAsrRequestInput): Promise<SpeechTranscribeResult>;
18
+ transcribeSrt(input: SpeechRequestAuth & SpeechAsrRequestInput): Promise<SpeechAsrSrtResult | SpeechAsrTaskSubmission>;
19
+ getAsrTask(input: SpeechRequestAuth & {
20
+ taskId: number | string;
21
+ provider?: 'tencent' | 'groq' | 'bcut';
22
+ }): Promise<SpeechAsrTaskResult>;
23
+ downloadAudio(input: {
24
+ key: string;
25
+ }): Promise<{
26
+ audio: ArrayBuffer;
27
+ contentType: string;
28
+ fileName: string | null;
29
+ headers: Headers;
30
+ }>;
31
+ }
32
+ export {};
@@ -0,0 +1,169 @@
1
+ import { PlatformApiError, PlatformTransportError } from '../core/errors.js';
2
+ function parseContentDispositionFileName(value) {
3
+ if (!value) {
4
+ return null;
5
+ }
6
+ const utf8Match = value.match(/filename\*=UTF-8''([^;]+)/i);
7
+ if (utf8Match) {
8
+ try {
9
+ return decodeURIComponent(utf8Match[1]);
10
+ }
11
+ catch {
12
+ return utf8Match[1];
13
+ }
14
+ }
15
+ const plainMatch = value.match(/filename="?([^";]+)"?/i);
16
+ return plainMatch?.[1] ?? null;
17
+ }
18
+ async function throwPlatformError(response) {
19
+ let payload = null;
20
+ try {
21
+ payload = (await response.json());
22
+ }
23
+ catch {
24
+ const detail = await response.text().catch(() => '');
25
+ throw new PlatformTransportError(`Platform API returned a non-JSON error response for ${response.url}: ${detail}`);
26
+ }
27
+ throw new PlatformApiError(payload.message, response.status, payload.code, payload.data);
28
+ }
29
+ function buildAsrFormData(input) {
30
+ const formData = new FormData();
31
+ const scalarEntries = [
32
+ ['provider', input.provider],
33
+ ['audioUrl', input.audioUrl],
34
+ ['async', input.async],
35
+ ['language', input.language],
36
+ ['prompt', input.prompt],
37
+ ['temperature', input.temperature],
38
+ ['engineModelType', input.engineModelType],
39
+ ['channelNum', input.channelNum],
40
+ ['speakerDiarization', input.speakerDiarization],
41
+ ['speakerNumber', input.speakerNumber],
42
+ ['sentenceMaxLength', input.sentenceMaxLength],
43
+ ['emotionRecognition', input.emotionRecognition],
44
+ ['filterDirty', input.filterDirty],
45
+ ['filterModal', input.filterModal],
46
+ ['filterPunc', input.filterPunc],
47
+ ['convertNumMode', input.convertNumMode],
48
+ ];
49
+ for (const [key, value] of scalarEntries) {
50
+ if (value == null) {
51
+ continue;
52
+ }
53
+ formData.append(key, String(value));
54
+ }
55
+ if (input.file) {
56
+ formData.append('file', input.file, input.fileName ?? 'audio');
57
+ }
58
+ return formData;
59
+ }
60
+ export class SpeechClient {
61
+ constructor(http) {
62
+ this.http = http;
63
+ }
64
+ listVoices(input = {}) {
65
+ return this.http.request('/api/voices', {
66
+ accessToken: input.accessToken,
67
+ apiKey: input.apiKey,
68
+ baseUrlMode: 'origin',
69
+ query: {
70
+ projectId: input.projectId,
71
+ provider: input.provider,
72
+ locale: input.locale,
73
+ search: input.search,
74
+ },
75
+ });
76
+ }
77
+ async synthesize(input) {
78
+ const response = await this.http.requestRaw('/api/speech', {
79
+ method: 'POST',
80
+ accessToken: input.accessToken,
81
+ apiKey: input.apiKey,
82
+ baseUrlMode: 'origin',
83
+ query: { projectId: input.projectId },
84
+ body: {
85
+ provider: input.provider,
86
+ text: input.text,
87
+ voice: input.voice,
88
+ locale: input.locale,
89
+ rate: input.rate,
90
+ volume: input.volume,
91
+ pitch: input.pitch,
92
+ style: input.style,
93
+ styleDegree: input.styleDegree,
94
+ role: input.role,
95
+ emotionCategory: input.emotionCategory,
96
+ emotionIntensity: input.emotionIntensity,
97
+ format: input.format,
98
+ },
99
+ });
100
+ if (!response.ok) {
101
+ return throwPlatformError(response);
102
+ }
103
+ return {
104
+ audio: await response.arrayBuffer(),
105
+ contentType: response.headers.get('Content-Type') ?? 'application/octet-stream',
106
+ fileName: parseContentDispositionFileName(response.headers.get('Content-Disposition')),
107
+ provider: response.headers.get('X-TTS-Provider'),
108
+ voice: response.headers.get('X-TTS-Voice'),
109
+ locale: response.headers.get('X-TTS-Locale'),
110
+ format: response.headers.get('X-TTS-Format'),
111
+ creditsCharged: response.headers.get('X-TTS-Credits-Charged') == null
112
+ ? null
113
+ : Number(response.headers.get('X-TTS-Credits-Charged')),
114
+ storageKey: response.headers.get('X-TTS-Storage-Key'),
115
+ downloadUrl: response.headers.get('X-TTS-Download-Url'),
116
+ projectId: response.headers.get('X-TTS-Project-Id'),
117
+ providerRequestId: response.headers.get('X-TTS-Provider-Request-Id'),
118
+ headers: response.headers,
119
+ };
120
+ }
121
+ transcribe(input) {
122
+ return this.http.request('/api/asr/transcribe', {
123
+ method: 'POST',
124
+ accessToken: input.accessToken,
125
+ apiKey: input.apiKey,
126
+ baseUrlMode: 'origin',
127
+ query: { projectId: input.projectId },
128
+ rawBody: buildAsrFormData(input),
129
+ contentType: null,
130
+ });
131
+ }
132
+ transcribeSrt(input) {
133
+ return this.http.request('/api/asr/srt', {
134
+ method: 'POST',
135
+ accessToken: input.accessToken,
136
+ apiKey: input.apiKey,
137
+ baseUrlMode: 'origin',
138
+ query: { projectId: input.projectId },
139
+ rawBody: buildAsrFormData(input),
140
+ contentType: null,
141
+ });
142
+ }
143
+ getAsrTask(input) {
144
+ return this.http.request(`/api/asr/tasks/${input.taskId}`, {
145
+ accessToken: input.accessToken,
146
+ apiKey: input.apiKey,
147
+ baseUrlMode: 'origin',
148
+ query: {
149
+ projectId: input.projectId,
150
+ provider: input.provider,
151
+ },
152
+ });
153
+ }
154
+ async downloadAudio(input) {
155
+ const response = await this.http.requestRaw('/api/files/download', {
156
+ baseUrlMode: 'origin',
157
+ query: { key: input.key },
158
+ });
159
+ if (!response.ok) {
160
+ return throwPlatformError(response);
161
+ }
162
+ return {
163
+ audio: await response.arrayBuffer(),
164
+ contentType: response.headers.get('Content-Type') ?? 'application/octet-stream',
165
+ fileName: parseContentDispositionFileName(response.headers.get('Content-Disposition')),
166
+ headers: response.headers,
167
+ };
168
+ }
169
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api2key-base-sdk",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "TypeScript SDK for consuming the Api2Key API stable APIs",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -45,4 +45,4 @@
45
45
  "devDependencies": {
46
46
  "typescript": "^5.3.3"
47
47
  }
48
- }
48
+ }