@ragbits/api-client 1.4.0-dev.202512090236 → 1.4.0-dev.202602261352

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.
@@ -47,6 +47,7 @@ export type TaskStatus = TypeFrom<typeof TaskStatus>;
47
47
  */
48
48
  export declare const AuthType: {
49
49
  readonly Credentials: "credentials";
50
+ readonly Oauth2: "oauth2";
50
51
  };
51
52
  export type AuthType = TypeFrom<typeof AuthType>;
52
53
  /**
@@ -66,6 +67,10 @@ export interface ChatContext {
66
67
  confirmed_tools: {
67
68
  [k: string]: unknown;
68
69
  }[] | null;
70
+ /**
71
+ * User's timezone in IANA format (e.g., 'Europe/Warsaw', 'America/New_York')
72
+ */
73
+ timezone: string | null;
69
74
  [k: string]: unknown;
70
75
  }
71
76
  /**
@@ -220,9 +225,9 @@ export interface UsageContent {
220
225
  };
221
226
  }
222
227
  /**
223
- * Todo item content wrapper.
228
+ * Plan item content wrapper.
224
229
  */
225
- export interface TodoItemContent {
230
+ export interface PlanItemContent {
226
231
  task: Task;
227
232
  }
228
233
  /**
@@ -310,6 +315,10 @@ export interface ConfigResponse {
310
315
  */
311
316
  customization: UICustomization | null;
312
317
  user_settings: UserSettings;
318
+ /**
319
+ * Flag indicating whether API supports file upload
320
+ */
321
+ supports_upload: boolean;
313
322
  /**
314
323
  * Debug mode flag
315
324
  */
@@ -333,6 +342,48 @@ export interface FeedbackResponse {
333
342
  */
334
343
  status: string;
335
344
  }
345
+ /**
346
+ * Response for OAuth2 authorization URL request
347
+ */
348
+ export interface OAuth2AuthorizeResponse {
349
+ /**
350
+ * URL to redirect user to for OAuth2 authorization
351
+ */
352
+ authorize_url: string;
353
+ /**
354
+ * State parameter for CSRF protection
355
+ */
356
+ state: string;
357
+ }
358
+ /**
359
+ * Configuration for an OAuth2 provider including visual configuration.
360
+ */
361
+ export interface OAuth2ProviderConfig {
362
+ /**
363
+ * Provider name (e.g., 'discord')
364
+ */
365
+ name: string;
366
+ /**
367
+ * Display name for the provider (e.g., 'Discord')
368
+ */
369
+ display_name: string | null;
370
+ /**
371
+ * Brand color for the provider (e.g., '#5865F2')
372
+ */
373
+ color: string | null;
374
+ /**
375
+ * Button background color (defaults to color)
376
+ */
377
+ button_color: string | null;
378
+ /**
379
+ * Button text color (defaults to white)
380
+ */
381
+ text_color: string | null;
382
+ /**
383
+ * SVG icon as string
384
+ */
385
+ icon_svg: string | null;
386
+ }
336
387
  /**
337
388
  * Client-side chat request interface.
338
389
  */
@@ -383,45 +434,30 @@ export interface AuthenticationConfig {
383
434
  * List of available authentication types
384
435
  */
385
436
  auth_types: AuthType[];
386
- }
387
- /**
388
- * Request body for user login
389
- */
390
- export interface CredentialsLoginRequest {
391
437
  /**
392
- * Username
438
+ * List of available OAuth2 providers
393
439
  */
394
- username: string;
395
- /**
396
- * Password
397
- */
398
- password: string;
440
+ oauth2_providers: OAuth2ProviderConfig[];
399
441
  }
400
442
  /**
401
- * Represents a JWT authentication jwt_token.
443
+ * Represents user login credentials.
402
444
  */
403
- export interface JWTToken {
404
- access_token: string;
405
- token_type: string;
406
- expires_in: number;
407
- refresh_token: string | null;
408
- user: User;
445
+ export interface UserCredentials {
446
+ username: string;
447
+ password: string;
409
448
  }
410
449
  /**
411
- * Request body for user login
450
+ * Represents user login credentials.
412
451
  */
413
452
  export interface LoginRequest {
414
- /**
415
- * Username
416
- */
417
453
  username: string;
418
- /**
419
- * Password
420
- */
421
454
  password: string;
422
455
  }
423
456
  /**
424
- * Response body for successful login
457
+ * Response body for login with session-based authentication.
458
+ *
459
+ * The session ID is set as an HTTP-only cookie by the backend.
460
+ * Frontend only receives user information.
425
461
  */
426
462
  export interface LoginResponse {
427
463
  /**
@@ -436,19 +472,6 @@ export interface LoginResponse {
436
472
  * Error message if login failed
437
473
  */
438
474
  error_message: string | null;
439
- /**
440
- * Access jwt_token
441
- */
442
- jwt_token: JWTToken | null;
443
- }
444
- /**
445
- * Request body for user logout
446
- */
447
- export interface LogoutRequest {
448
- /**
449
- * Session ID to logout
450
- */
451
- token: string;
452
475
  }
453
476
  /**
454
477
  * Represents an authenticated user.
@@ -506,9 +529,9 @@ export interface ClearMessageChatResponse {
506
529
  type: 'clear_message';
507
530
  content: unknown;
508
531
  }
509
- export interface TodoItemChatResonse {
510
- type: 'todo_item';
511
- content: TodoItemContent;
532
+ export interface PlanItemChatResponse {
533
+ type: 'plan_item';
534
+ content: PlanItemContent;
512
535
  }
513
536
  export interface ConversationSummaryResponse {
514
537
  type: 'conversation_summary';
@@ -529,4 +552,4 @@ export interface ChunkedChatResponse {
529
552
  /**
530
553
  * Typed chat response union
531
554
  */
532
- export type ChatResponse = TextChatResponse | ReferenceChatResponse | MessageIdChatResponse | ConversationIdChatResponse | StateUpdateChatResponse | LiveUpdateChatResponse | FollowupMessagesChatResponse | ImageChatResponse | MessageUsageChatResponse | ClearMessageChatResponse | TodoItemChatResonse | ConversationSummaryResponse | ConfirmationRequestChatResponse | ErrorChatResponse;
555
+ export type ChatResponse = TextChatResponse | ReferenceChatResponse | MessageIdChatResponse | ConversationIdChatResponse | StateUpdateChatResponse | LiveUpdateChatResponse | FollowupMessagesChatResponse | ImageChatResponse | MessageUsageChatResponse | ClearMessageChatResponse | PlanItemChatResponse | ConversationSummaryResponse | ConfirmationRequestChatResponse | ErrorChatResponse;
package/dist/index.cjs CHANGED
@@ -52,7 +52,8 @@ var TaskStatus = {
52
52
  Retrying: "retrying"
53
53
  };
54
54
  var AuthType = {
55
- Credentials: "credentials"
55
+ Credentials: "credentials",
56
+ Oauth2: "oauth2"
56
57
  };
57
58
 
58
59
  // src/index.ts
@@ -99,6 +100,9 @@ var RagbitsClient = class {
99
100
  const defaultHeaders = {
100
101
  "Content-Type": "application/json"
101
102
  };
103
+ if (options.body instanceof FormData) {
104
+ delete defaultHeaders["Content-Type"];
105
+ }
102
106
  const headers = {
103
107
  ...defaultHeaders,
104
108
  ...this.normalizeHeaders(options.headers)
@@ -106,7 +110,11 @@ var RagbitsClient = class {
106
110
  if (this.auth?.getToken) {
107
111
  headers["Authorization"] = `Bearer ${this.auth.getToken()}`;
108
112
  }
109
- const response = await fetch(url, { ...options, headers });
113
+ const response = await fetch(url, {
114
+ ...options,
115
+ headers,
116
+ ...this.auth?.credentials ? { credentials: this.auth?.credentials } : {}
117
+ });
110
118
  if (response.status === 401) {
111
119
  this.auth?.onUnauthorized?.();
112
120
  }
@@ -136,10 +144,17 @@ var RagbitsClient = class {
136
144
  ...restOptions
137
145
  };
138
146
  if (body && method !== "GET") {
139
- requestOptions.body = typeof body === "string" ? body : JSON.stringify(body);
147
+ if (body instanceof FormData) {
148
+ requestOptions.body = body;
149
+ if (requestOptions.headers && "Content-Type" in requestOptions.headers) {
150
+ delete requestOptions.headers["Content-Type"];
151
+ }
152
+ } else {
153
+ requestOptions.body = typeof body === "string" ? body : JSON.stringify(body);
154
+ }
140
155
  }
141
156
  let url = endpoint.toString();
142
- if (pathParams) {
157
+ if (pathParams && typeof pathParams === "object") {
143
158
  url = url.replace(/:([^/]+)/g, (_, paramName) => {
144
159
  if (paramName in pathParams) {
145
160
  const value = pathParams[paramName];
@@ -191,9 +206,14 @@ var RagbitsClient = class {
191
206
  if (!line.startsWith("data: ")) continue;
192
207
  try {
193
208
  const jsonString = line.replace("data: ", "").trim();
194
- const parsedData = JSON.parse(
195
- jsonString
196
- );
209
+ const parsedData = JSON.parse(jsonString);
210
+ if (!this.isChatResponse(parsedData)) {
211
+ console.warn(
212
+ "Received response that isn't ChatResponse, skipping.",
213
+ parsedData
214
+ );
215
+ continue;
216
+ }
197
217
  if (parsedData.type === "chunked_content") {
198
218
  this.handleChunkedContent(parsedData, callbacks);
199
219
  continue;
@@ -238,7 +258,8 @@ var RagbitsClient = class {
238
258
  method: "POST",
239
259
  headers,
240
260
  body: JSON.stringify(data),
241
- signal
261
+ signal,
262
+ ...this.auth?.credentials ? { credentials: this.auth?.credentials } : {}
242
263
  }
243
264
  );
244
265
  if (response.status === 401) {
@@ -267,6 +288,9 @@ var RagbitsClient = class {
267
288
  isCancelled = true;
268
289
  };
269
290
  }
291
+ isChatResponse(response) {
292
+ return response !== null && typeof response === "object" && "type" in response && "content" in response;
293
+ }
270
294
  normalizeHeaders(init) {
271
295
  if (!init) return {};
272
296
  if (init instanceof Headers) {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ClientConfig, StreamCallbacks, BaseApiEndpoints, EndpointDefinition, EndpointResponse, BaseStreamingEndpoints, EndpointRequest, MakeRequestOptions } from './types';
1
+ import type { ClientConfig, StreamCallbacks, BaseApiEndpoints, EndpointResponse, BaseStreamingEndpoints, EndpointRequest, MakeRequestOptions, AnyEndpoints } from './types';
2
2
  /**
3
3
  * Client for communicating with the Ragbits API
4
4
  */
@@ -29,9 +29,7 @@ export declare class RagbitsClient {
29
29
  * @param endpoint - API endpoint path
30
30
  * @param options - Typed request options for the specific endpoint
31
31
  */
32
- makeRequest<Endpoints extends {
33
- [K in keyof Endpoints]: EndpointDefinition<any, any, any, any>;
34
- } = BaseApiEndpoints, URL extends keyof Endpoints = keyof Endpoints>(endpoint: URL, ...args: MakeRequestOptions<URL, Endpoints>): Promise<EndpointResponse<URL, Endpoints>>;
32
+ makeRequest<Endpoints extends AnyEndpoints<Endpoints> = BaseApiEndpoints, URL extends keyof Endpoints = keyof Endpoints>(endpoint: URL, ...args: MakeRequestOptions<URL, Endpoints>): Promise<EndpointResponse<URL, Endpoints>>;
35
33
  /**
36
34
  * Method for streaming requests to known endpoints only
37
35
  * @param endpoint - Streaming endpoint path
@@ -39,9 +37,8 @@ export declare class RagbitsClient {
39
37
  * @param callbacks - Stream callbacks
40
38
  * @param signal - Optional AbortSignal for cancelling the request
41
39
  */
42
- makeStreamRequest<Endpoints extends {
43
- [K in keyof Endpoints]: EndpointDefinition<any, any, any, any>;
44
- } = BaseStreamingEndpoints, URL extends keyof Endpoints = keyof Endpoints>(endpoint: URL, data: EndpointRequest<URL, Endpoints>, callbacks: StreamCallbacks<EndpointResponse<URL, Endpoints>>, signal?: AbortSignal, customHeaders?: Record<string, string>): () => void;
40
+ makeStreamRequest<Endpoints extends AnyEndpoints<Endpoints> = BaseStreamingEndpoints, URL extends keyof Endpoints = keyof Endpoints>(endpoint: URL, data: EndpointRequest<URL, Endpoints>, callbacks: StreamCallbacks<EndpointResponse<URL, Endpoints>>, signal?: AbortSignal, customHeaders?: Record<string, string>): () => void;
41
+ private isChatResponse;
45
42
  private normalizeHeaders;
46
43
  private handleChunkedContent;
47
44
  }
package/dist/index.js CHANGED
@@ -21,7 +21,8 @@ var TaskStatus = {
21
21
  Retrying: "retrying"
22
22
  };
23
23
  var AuthType = {
24
- Credentials: "credentials"
24
+ Credentials: "credentials",
25
+ Oauth2: "oauth2"
25
26
  };
26
27
 
27
28
  // src/index.ts
@@ -68,6 +69,9 @@ var RagbitsClient = class {
68
69
  const defaultHeaders = {
69
70
  "Content-Type": "application/json"
70
71
  };
72
+ if (options.body instanceof FormData) {
73
+ delete defaultHeaders["Content-Type"];
74
+ }
71
75
  const headers = {
72
76
  ...defaultHeaders,
73
77
  ...this.normalizeHeaders(options.headers)
@@ -75,7 +79,11 @@ var RagbitsClient = class {
75
79
  if (this.auth?.getToken) {
76
80
  headers["Authorization"] = `Bearer ${this.auth.getToken()}`;
77
81
  }
78
- const response = await fetch(url, { ...options, headers });
82
+ const response = await fetch(url, {
83
+ ...options,
84
+ headers,
85
+ ...this.auth?.credentials ? { credentials: this.auth?.credentials } : {}
86
+ });
79
87
  if (response.status === 401) {
80
88
  this.auth?.onUnauthorized?.();
81
89
  }
@@ -105,10 +113,17 @@ var RagbitsClient = class {
105
113
  ...restOptions
106
114
  };
107
115
  if (body && method !== "GET") {
108
- requestOptions.body = typeof body === "string" ? body : JSON.stringify(body);
116
+ if (body instanceof FormData) {
117
+ requestOptions.body = body;
118
+ if (requestOptions.headers && "Content-Type" in requestOptions.headers) {
119
+ delete requestOptions.headers["Content-Type"];
120
+ }
121
+ } else {
122
+ requestOptions.body = typeof body === "string" ? body : JSON.stringify(body);
123
+ }
109
124
  }
110
125
  let url = endpoint.toString();
111
- if (pathParams) {
126
+ if (pathParams && typeof pathParams === "object") {
112
127
  url = url.replace(/:([^/]+)/g, (_, paramName) => {
113
128
  if (paramName in pathParams) {
114
129
  const value = pathParams[paramName];
@@ -160,9 +175,14 @@ var RagbitsClient = class {
160
175
  if (!line.startsWith("data: ")) continue;
161
176
  try {
162
177
  const jsonString = line.replace("data: ", "").trim();
163
- const parsedData = JSON.parse(
164
- jsonString
165
- );
178
+ const parsedData = JSON.parse(jsonString);
179
+ if (!this.isChatResponse(parsedData)) {
180
+ console.warn(
181
+ "Received response that isn't ChatResponse, skipping.",
182
+ parsedData
183
+ );
184
+ continue;
185
+ }
166
186
  if (parsedData.type === "chunked_content") {
167
187
  this.handleChunkedContent(parsedData, callbacks);
168
188
  continue;
@@ -207,7 +227,8 @@ var RagbitsClient = class {
207
227
  method: "POST",
208
228
  headers,
209
229
  body: JSON.stringify(data),
210
- signal
230
+ signal,
231
+ ...this.auth?.credentials ? { credentials: this.auth?.credentials } : {}
211
232
  }
212
233
  );
213
234
  if (response.status === 401) {
@@ -236,6 +257,9 @@ var RagbitsClient = class {
236
257
  isCancelled = true;
237
258
  };
238
259
  }
260
+ isChatResponse(response) {
261
+ return response !== null && typeof response === "object" && "type" in response && "content" in response;
262
+ }
239
263
  normalizeHeaders(init) {
240
264
  if (!init) return {};
241
265
  if (init instanceof Headers) {
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ConfigResponse, FeedbackRequest, FeedbackResponse, ChatRequest, ChatResponse, LogoutRequest, LoginRequest, LoginResponse } from './autogen.types';
1
+ import { ConfigResponse, FeedbackRequest, FeedbackResponse, ChatRequest, ChatResponse, LoginRequest, LoginResponse, User, OAuth2AuthorizeResponse } from './autogen.types';
2
2
  export interface GenericResponse {
3
3
  success: boolean;
4
4
  }
@@ -10,6 +10,7 @@ export interface ClientConfig {
10
10
  auth?: {
11
11
  getToken?: () => string;
12
12
  onUnauthorized?: () => Promise<void> | void;
13
+ credentials?: RequestCredentials;
13
14
  };
14
15
  }
15
16
  /**
@@ -20,7 +21,7 @@ export interface StreamCallbacks<T, E = Error> {
20
21
  onError: (error: E) => void | Promise<void>;
21
22
  onClose?: () => void | Promise<void>;
22
23
  }
23
- export interface EndpointDefinition<Req = any, Res = any, PathParams = never, QueryParams = never> {
24
+ export interface EndpointDefinition<Req = unknown, Res = unknown, PathParams = never, QueryParams = never> {
24
25
  method: string;
25
26
  request: Req;
26
27
  response: Res;
@@ -34,8 +35,16 @@ export interface BaseApiEndpoints {
34
35
  '/api/config': EndpointDefinition<never, ConfigResponse>;
35
36
  '/api/feedback': EndpointDefinition<FeedbackRequest, FeedbackResponse>;
36
37
  '/api/auth/login': EndpointDefinition<LoginRequest, LoginResponse>;
37
- '/api/auth/logout': EndpointDefinition<LogoutRequest, GenericResponse>;
38
+ '/api/auth/logout': EndpointDefinition<never, GenericResponse>;
39
+ '/api/auth/authorize/:provider': EndpointDefinition<never, OAuth2AuthorizeResponse, {
40
+ provider: string;
41
+ }>;
42
+ '/api/user': EndpointDefinition<never, User>;
38
43
  '/api/theme': EndpointDefinition<never, string>;
44
+ '/api/upload': EndpointDefinition<FormData, {
45
+ status: string;
46
+ filename: string;
47
+ }>;
39
48
  }
40
49
  /**
41
50
  * Streaming API endpoint definitions with their request/stream response types
@@ -43,36 +52,32 @@ export interface BaseApiEndpoints {
43
52
  export interface BaseStreamingEndpoints {
44
53
  '/api/chat': EndpointDefinition<ChatRequest, ChatResponse>;
45
54
  }
55
+ type AnyEndpointDefinition = EndpointDefinition<unknown, unknown, unknown, unknown>;
56
+ export type AnyEndpoints<T> = {
57
+ [K in keyof T]: AnyEndpointDefinition;
58
+ };
46
59
  /**
47
60
  * Extract endpoint paths as a union type
48
61
  */
49
- export type EndpointPath<Endpoints extends {
50
- [K in keyof Endpoints]: EndpointDefinition<any, any, any, any>;
51
- }> = keyof Endpoints;
62
+ export type EndpointPath<Endpoints extends AnyEndpoints<Endpoints>> = keyof Endpoints;
52
63
  /**
53
64
  * Extract request type for a specific API endpoint
54
65
  */
55
- export type EndpointRequest<URL extends keyof Endpoints, Endpoints extends {
56
- [K in keyof Endpoints]: EndpointDefinition<any, any, any, any>;
57
- }> = Endpoints[URL]['request'];
66
+ export type EndpointRequest<URL extends keyof Endpoints, Endpoints extends AnyEndpoints<Endpoints>> = Endpoints[URL]['request'];
58
67
  /**
59
68
  * Extract response type for a specific API endpoint
60
69
  */
61
- export type EndpointResponse<URL extends keyof Endpoints, Endpoints extends {
62
- [K in keyof Endpoints]: EndpointDefinition<any, any, any, any>;
63
- }> = Endpoints[URL]['response'];
70
+ export type EndpointResponse<URL extends keyof Endpoints, Endpoints extends AnyEndpoints<Endpoints>> = Endpoints[URL]['response'];
64
71
  /**
65
72
  * Extract HTTP method for a specific API endpoint
66
73
  */
67
- export type EndpointMethod<URL extends keyof Endpoints, Endpoints extends {
68
- [K in keyof Endpoints]: EndpointDefinition<any, any, any, any>;
69
- }> = Endpoints[URL]['method'];
74
+ export type EndpointMethod<URL extends keyof Endpoints, Endpoints extends AnyEndpoints<Endpoints>> = Endpoints[URL]['method'];
70
75
  /**
71
76
  * Check if an object type has any required properties
72
77
  * - {} extends T means all properties are optional → false
73
78
  * - {} doesn't extend T means at least one property is required → true
74
79
  */
75
- export type HasRequiredKeys<T> = [T] extends [never] ? false : {} extends T ? false : true;
80
+ export type HasRequiredKeys<T> = [T] extends [never] ? false : NonNullable<unknown> extends T ? false : true;
76
81
  /**
77
82
  * Generic request options for API endpoints with typed methods, path params, query params, and body
78
83
  * - pathParams is REQUIRED when PathParams is not never
@@ -81,9 +86,7 @@ export type HasRequiredKeys<T> = [T] extends [never] ? false : {} extends T ? fa
81
86
  * - body is REQUIRED when it's a specific type (not never, not undefined)
82
87
  * - body is OPTIONAL when it's never or undefined
83
88
  */
84
- export type RequestOptions<URL extends keyof Endpoints, Endpoints extends {
85
- [K in keyof Endpoints]: EndpointDefinition<any, any, any, any>;
86
- }> = {
89
+ export type RequestOptions<URL extends keyof Endpoints, Endpoints extends AnyEndpoints<Endpoints>> = {
87
90
  method?: Endpoints[URL]['method'];
88
91
  headers?: Record<string, string>;
89
92
  signal?: AbortSignal;
@@ -116,9 +119,7 @@ export type IsRequired<T> = [T] extends [never] ? false : T extends undefined ?
116
119
  * - queryParams has any required keys inside, OR
117
120
  * - body/request is required (not never, not undefined)
118
121
  */
119
- export type HasRequiredParams<URL extends keyof Endpoints, Endpoints extends {
120
- [K in keyof Endpoints]: EndpointDefinition<any, any, any, any>;
121
- }> = Endpoints[URL]['pathParams'] extends never ? HasRequiredKeys<Endpoints[URL]['queryParams']> extends true ? true : IsRequired<Endpoints[URL]['request']> : true;
122
+ export type HasRequiredParams<URL extends keyof Endpoints, Endpoints extends AnyEndpoints<Endpoints>> = Endpoints[URL]['pathParams'] extends never ? HasRequiredKeys<Endpoints[URL]['queryParams']> extends true ? true : IsRequired<Endpoints[URL]['request']> : true;
122
123
  /**
123
124
  * Conditional options parameter for makeRequest
124
125
  * - Required if endpoint has:
@@ -127,6 +128,5 @@ export type HasRequiredParams<URL extends keyof Endpoints, Endpoints extends {
127
128
  * - body with specific type (not undefined)
128
129
  * - Optional otherwise
129
130
  */
130
- export type MakeRequestOptions<URL extends keyof Endpoints, Endpoints extends {
131
- [K in keyof Endpoints]: EndpointDefinition<any, any, any, any>;
132
- }> = HasRequiredParams<URL, Endpoints> extends true ? [options: RequestOptions<URL, Endpoints>] : [options?: RequestOptions<URL, Endpoints>];
131
+ export type MakeRequestOptions<URL extends keyof Endpoints, Endpoints extends AnyEndpoints<Endpoints>> = HasRequiredParams<URL, Endpoints> extends true ? [options: RequestOptions<URL, Endpoints>] : [options?: RequestOptions<URL, Endpoints>];
132
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ragbits/api-client",
3
- "version": "1.4.0-dev.202512090236",
3
+ "version": "1.4.0-dev.202602261352",
4
4
  "description": "JavaScript client for the Ragbits API",
5
5
  "repository": {
6
6
  "type": "git",