@robosystems/client 0.1.20 → 0.1.22

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.
Files changed (52) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +10 -423
  3. package/bin/create-feature +7 -17
  4. package/extensions/CopyClient.d.ts +1 -0
  5. package/extensions/CopyClient.js +3 -0
  6. package/extensions/CopyClient.ts +5 -0
  7. package/extensions/OperationClient.d.ts +1 -0
  8. package/extensions/OperationClient.ts +2 -0
  9. package/extensions/QueryClient.d.ts +1 -0
  10. package/extensions/QueryClient.js +1 -0
  11. package/extensions/QueryClient.ts +3 -0
  12. package/extensions/SSEClient.d.ts +14 -0
  13. package/extensions/SSEClient.js +7 -1
  14. package/extensions/SSEClient.ts +22 -1
  15. package/extensions/config.d.ts +25 -0
  16. package/extensions/config.js +78 -0
  17. package/extensions/config.ts +84 -0
  18. package/extensions/hooks.js +16 -0
  19. package/extensions/hooks.ts +27 -1
  20. package/extensions/index.d.ts +2 -0
  21. package/extensions/index.js +14 -0
  22. package/extensions/index.ts +28 -1
  23. package/package.json +2 -2
  24. package/sdk/sdk.gen.d.ts +40 -15
  25. package/sdk/sdk.gen.js +100 -33
  26. package/sdk/sdk.gen.ts +99 -32
  27. package/sdk/types.gen.d.ts +748 -100
  28. package/sdk/types.gen.ts +785 -108
  29. package/sdk-extensions/CopyClient.d.ts +1 -0
  30. package/sdk-extensions/CopyClient.js +3 -0
  31. package/sdk-extensions/CopyClient.ts +5 -0
  32. package/sdk-extensions/OperationClient.d.ts +1 -0
  33. package/sdk-extensions/OperationClient.ts +2 -0
  34. package/sdk-extensions/QueryClient.d.ts +1 -0
  35. package/sdk-extensions/QueryClient.js +1 -0
  36. package/sdk-extensions/QueryClient.ts +3 -0
  37. package/sdk-extensions/SSEClient.d.ts +14 -0
  38. package/sdk-extensions/SSEClient.js +7 -1
  39. package/sdk-extensions/SSEClient.ts +22 -1
  40. package/sdk-extensions/config.d.ts +25 -0
  41. package/sdk-extensions/config.js +78 -0
  42. package/sdk-extensions/config.ts +84 -0
  43. package/sdk-extensions/hooks.js +16 -0
  44. package/sdk-extensions/hooks.ts +27 -1
  45. package/sdk-extensions/index.d.ts +2 -0
  46. package/sdk-extensions/index.js +14 -0
  47. package/sdk-extensions/index.ts +28 -1
  48. package/sdk.gen.d.ts +40 -15
  49. package/sdk.gen.js +100 -33
  50. package/sdk.gen.ts +99 -32
  51. package/types.gen.d.ts +748 -100
  52. package/types.gen.ts +785 -108
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Harbinger FinLab
3
+ Copyright (c) 2025 RFS LLC
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -3,451 +3,38 @@
3
3
  [![npm version](https://badge.fury.io/js/@robosystems%2Fclient.svg)](https://www.npmjs.com/package/@robosystems/client)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- Official TypeScript Client for the RoboSystems Financial Knowledge Graph API. Access comprehensive financial data including accounting records, SEC filings, and advanced graph analytics through a type-safe, modern TypeScript interface.
6
+ Official TypeScript Client for the RoboSystems Financial Knowledge Graph API. Access comprehensive financial data including accounting transactions, financial reports, and advanced graph analytics through a type-safe, modern TypeScript interface.
7
7
 
8
8
  ## Features
9
9
 
10
10
  - **Type-safe API client** with full TypeScript types
11
- - **Auto-generated from OpenAPI** for always up-to-date types
12
11
  - **Browser & Node.js support** with different auth strategies
13
12
  - **React hooks** for seamless UI integration
14
- - **Queue handling** for long-running operations
15
- - **Streaming support** for large datasets
16
- - **Financial AI Agent** integration
13
+ - **Streaming support** for memory-efficient processing of large result sets
14
+ - **Financial AI Agent** integration for natural language queries
17
15
  - **Comprehensive error handling** with typed errors
18
16
 
19
17
  ## Installation
20
18
 
21
19
  ```bash
22
20
  npm install @robosystems/client
23
- # or
24
- yarn add @robosystems/client
25
- # or
26
- pnpm add @robosystems/client
27
- ```
28
-
29
- ## Quick Start
30
-
31
- ### Browser Usage (with cookies)
32
-
33
- ```typescript
34
- import { client, getUserMe, listCompanies, executeCypherQuery } from '@robosystems/client'
35
-
36
- // Configure the client
37
- client.setConfig({
38
- baseUrl: 'https://api.robosystems.ai',
39
- credentials: 'include',
40
- })
41
-
42
- // Use with cookie-based authentication (browser)
43
- const { data: user, error } = await getCurrentUser()
44
- if (user) {
45
- console.log('Logged in as:', user.email)
46
- }
47
-
48
- // List companies in a graph
49
- const { data: companies } = await listCompanies({
50
- path: { graph_id: 'your-graph-id' },
51
- query: { limit: 10 },
52
- })
53
-
54
- // Execute a Cypher query
55
- const { data: result } = await executeCypherQuery({
56
- path: { graph_id: 'your-graph-id' },
57
- body: {
58
- query: 'MATCH (c:Company)-[:HAS_FILING]->(f:Filing) RETURN c.name, f.form_type LIMIT 5',
59
- },
60
- })
61
- ```
62
-
63
- ### Server Usage (with API key)
64
-
65
- ```typescript
66
- import { client, getUserGraphs, listCompanies } from '@robosystems/client'
67
-
68
- // Configure with API key for server-side usage
69
- client.setConfig({
70
- baseUrl: 'https://api.robosystems.ai',
71
- headers: {
72
- 'X-API-Key': process.env.ROBOSYSTEMS_API_KEY!,
73
- },
74
- })
75
-
76
- // Fetch user's graphs
77
- const { data: graphs, error } = await getUserGraphs()
78
-
79
- // Work with financial data
80
- if (graphs?.graphs.length) {
81
- const graphId = graphs.graphs[0].graph_id
82
- const { data: companies } = await listCompanies({
83
- path: { graph_id: graphId },
84
- })
85
- }
86
- ```
87
-
88
- ## Key API Endpoints
89
-
90
- ### Authentication & User Management
91
-
92
- ```typescript
93
- import {
94
- registerUser,
95
- loginUser,
96
- logoutUser,
97
- getCurrentUser,
98
- createUserApiKey,
99
- } from '@robosystems/client'
100
-
101
- // Register a new user
102
- const { data, error } = await registerUser({
103
- body: {
104
- email: 'user@example.com',
105
- password: 'secure-password',
106
- name: 'John Doe',
107
- },
108
- })
109
-
110
- // Sign in
111
- const { data: session } = await loginUser({
112
- body: {
113
- username: 'user@example.com',
114
- password: 'secure-password',
115
- },
116
- })
117
-
118
- // Get current user
119
- const { data: user } = await getCurrentUser()
120
-
121
- // Create API key for programmatic access
122
- const { data: apiKey } = await createUserApiKey({
123
- body: {
124
- key_name: 'Production Server',
125
- key_type: 'user',
126
- },
127
- })
128
- console.log('Save this key securely:', apiKey.key)
129
-
130
- // Sign out
131
- await logoutUser()
132
- ```
133
-
134
- ### Company & Financial Data
135
-
136
- ```typescript
137
- import {
138
- listCompanies,
139
- getCompany,
140
- createCompany,
141
- createConnection,
142
- syncConnection,
143
- } from '@robosystems/client'
144
-
145
- // List companies with pagination
146
- const { data: companies } = await listCompanies({
147
- path: { graph_id: 'your-graph-id' },
148
- query: { limit: 20, offset: 0 },
149
- })
150
- console.log(`Found ${companies.total} companies`)
151
-
152
- // Get specific company details
153
- const { data: company } = await getCompany({
154
- path: {
155
- graph_id: 'your-graph-id',
156
- company_id: 'AAPL',
157
- },
158
- })
159
-
160
- // Create new company
161
- const { data: newCompany } = await createCompany({
162
- path: { graph_id: 'your-graph-id' },
163
- body: {
164
- identifier: 'MSFT',
165
- name: 'Microsoft Corporation',
166
- metadata: { industry: 'Technology' },
167
- },
168
- })
169
-
170
- // Create data connection (QuickBooks, etc.)
171
- const { data: connection } = await createConnection({
172
- path: { graph_id: 'your-graph-id' },
173
- body: {
174
- name: 'QuickBooks Integration',
175
- connection_type: 'quickbooks',
176
- config: { company_id: '123456' },
177
- },
178
- })
179
-
180
- // Sync data from connection
181
- const { data: syncResult } = await syncConnection({
182
- path: {
183
- graph_id: 'your-graph-id',
184
- connection_id: connection.id,
185
- },
186
- })
187
- ```
188
-
189
- ### Graph Queries & Analytics
190
-
191
- ```typescript
192
- import {
193
- executeCypherQuery,
194
- executeReadOnlyCypherQuery,
195
- getGraphMetrics,
196
- } from '@robosystems/client'
197
-
198
- // Execute parameterized Cypher queries
199
- const { data: results } = await executeCypherQuery({
200
- path: { graph_id: 'your-graph-id' },
201
- body: {
202
- query: `
203
- MATCH (c:Company {ticker: $ticker})-[:HAS_METRIC]->(m:Metric)
204
- WHERE m.fiscal_year >= $start_year
205
- RETURN m.name, m.value, m.fiscal_year
206
- ORDER BY m.fiscal_year DESC
207
- `,
208
- parameters: { ticker: 'AAPL', start_year: 2020 },
209
- },
210
- })
211
-
212
- // Read-only queries for better performance
213
- const { data: readOnlyResult } = await executeReadOnlyCypherQuery({
214
- path: { graph_id: 'your-graph-id' },
215
- body: { query: 'MATCH (n) RETURN count(n) as total' },
216
- })
217
-
218
- // Get graph analytics
219
- const { data: metrics } = await getGraphMetrics({
220
- path: { graph_id: 'your-graph-id' },
221
- })
222
- console.log(`Total nodes: ${metrics.total_nodes}`)
223
- console.log(`Total relationships: ${metrics.total_relationships}`)
224
- ```
225
-
226
- ### Financial AI Agent
227
-
228
- ```typescript
229
- import { queryFinancialAgent } from '@robosystems/client'
230
-
231
- // Natural language financial queries
232
- const { data: agentResponse } = await queryFinancialAgent({
233
- path: { graph_id: 'your-graph-id' },
234
- body: {
235
- query: "What was Apple's revenue growth over the last 3 years?",
236
- include_reasoning: true,
237
- max_tokens: 1000,
238
- },
239
- })
240
-
241
- console.log('Answer:', agentResponse.answer)
242
- if (agentResponse.reasoning) {
243
- console.log('Reasoning:', agentResponse.reasoning)
244
- }
245
- ```
246
-
247
- ## Advanced Features
248
-
249
- ### Billing & Credit Management
250
-
251
- ```typescript
252
- import { getCreditSummary, checkCreditBalance, getCurrentGraphBill } from '@robosystems/client'
253
-
254
- // Monitor credits and usage for a specific graph
255
- const { data: creditSummary } = await getCreditSummary({
256
- path: { graph_id: 'your-graph-id' },
257
- })
258
- console.log(`Available credits: ${creditSummary.available_credits.toLocaleString()}`)
259
- console.log(
260
- `Monthly usage: ${creditSummary.used_credits.toLocaleString()}/${creditSummary.total_credits.toLocaleString()}`
261
- )
262
-
263
- // Check credit requirements before operations
264
- const { data: creditCheck } = await checkCreditBalance({
265
- path: { graph_id: 'your-graph-id' },
266
- body: {
267
- operation_type: 'query',
268
- estimated_credits: 100,
269
- },
270
- })
271
-
272
- if (creditCheck.has_sufficient_credits) {
273
- // Proceed with operation
274
- }
275
-
276
- // Get billing information
277
- const { data: currentBill } = await getCurrentGraphBill({
278
- path: { graph_id: 'your-graph-id' },
279
- })
280
- ```
281
-
282
- ### MCP Tools Integration
283
-
284
- ```typescript
285
- import { listMcpTools, callMcpTool } from '@robosystems/client'
286
-
287
- // List available MCP tools
288
- const { data: tools } = await listMcpTools({
289
- path: { graph_id: 'your-graph-id' },
290
- })
291
- tools.tools.forEach((tool) => {
292
- console.log(`${tool.name}: ${tool.description}`)
293
- })
294
-
295
- // Call an MCP tool
296
- const { data: toolResult } = await callMcpTool({
297
- path: { graph_id: 'your-graph-id' },
298
- body: {
299
- name: 'analyze_financial_statement',
300
- arguments: {
301
- company_id: 'AAPL',
302
- statement_type: 'income',
303
- fiscal_year: 2023,
304
- },
305
- },
306
- })
307
- console.log('Analysis result:', toolResult.content)
308
- ```
309
-
310
- ## Advanced Features
311
-
312
- ### Error Handling
313
-
314
- ```typescript
315
- import { listCompanies } from '@robosystems/client'
316
- import type { ErrorResponse } from '@robosystems/client'
317
-
318
- try {
319
- const { data, error } = await listCompanies({
320
- path: { graph_id: 'your-graph-id' },
321
- })
322
-
323
- if (error) {
324
- const apiError = error as ErrorResponse
325
-
326
- switch (apiError.status) {
327
- case 400:
328
- console.error('Validation error:', apiError.detail)
329
- break
330
- case 401:
331
- console.error('Authentication failed - check your API key')
332
- break
333
- case 403:
334
- console.error('Permission denied - check graph access')
335
- break
336
- case 429:
337
- console.error('Rate limit exceeded - retry later')
338
- break
339
- case 503:
340
- console.error('Service temporarily unavailable')
341
- break
342
- default:
343
- console.error('API Error:', apiError.detail || apiError.message)
344
- }
345
- } else if (data) {
346
- console.log(`Found ${data.total} companies`)
347
- }
348
- } catch (err) {
349
- // Network errors
350
- console.error('Network Error:', err)
351
- }
352
- ```
353
-
354
- ### TypeScript Types
355
-
356
- All API responses and requests are fully typed:
357
-
358
- ```typescript
359
- import type {
360
- User,
361
- Graph,
362
- Company,
363
- CompanyCreate,
364
- CypherQueryRequest,
365
- CypherQueryResponse,
366
- AgentQueryRequest,
367
- AgentQueryResponse,
368
- ErrorResponse,
369
- PaginatedResponse,
370
- } from '@robosystems/client'
371
-
372
- // Type-safe function example
373
- function processCompany(company: Company): void {
374
- console.log(`Company: ${company.name} (${company.identifier})`)
375
- if (company.metadata) {
376
- console.log('Industry:', company.metadata.industry)
377
- }
378
- }
379
-
380
- // Type-safe query builder
381
- function buildMetricQuery(ticker: string, startYear: number): CypherQueryRequest {
382
- return {
383
- query: `
384
- MATCH (c:Company {ticker: $ticker})-[:HAS_METRIC]->(m:Metric)
385
- WHERE m.fiscal_year >= $start_year
386
- RETURN m
387
- `,
388
- parameters: { ticker, start_year: startYear },
389
- }
390
- }
391
- ```
392
-
393
- ## Environment Configuration
394
-
395
- ### Next.js App Router
396
-
397
- ```typescript
398
- // app/api/robosystems/route.ts
399
- import { client } from '@robosystems/client'
400
-
401
- // Configure for server-side API routes
402
- client.setConfig({
403
- baseUrl: process.env.ROBOSYSTEMS_API_URL || 'https://api.robosystems.ai',
404
- headers: {
405
- 'X-API-Key': process.env.ROBOSYSTEMS_API_KEY!,
406
- },
407
- })
408
- ```
409
-
410
- ### React SPA
411
-
412
- ```typescript
413
- // src/lib/robosystems.ts
414
- import { client } from '@robosystems/client'
415
-
416
- // Configure for browser with cookies
417
- client.setConfig({
418
- baseUrl: import.meta.env.VITE_ROBOSYSTEMS_API_URL || 'https://api.robosystems.ai',
419
- credentials: 'include',
420
- })
421
- ```
422
-
423
- ### Node.js Script
424
-
425
- ```typescript
426
- // scripts/analyze.ts
427
- import { client } from '@robosystems/client'
428
- import dotenv from 'dotenv'
429
-
430
- dotenv.config()
431
-
432
- client.setConfig({
433
- baseUrl: process.env.ROBOSYSTEMS_API_URL || 'https://api.robosystems.ai',
434
- headers: {
435
- 'X-API-Key': process.env.ROBOSYSTEMS_API_KEY!,
436
- },
437
- })
438
21
  ```
439
22
 
440
23
  ## API Reference
441
24
 
442
- - Full API documentation: [https://api.robosystems.ai](https://api.robosystems.ai)
25
+ - API reference: [https://api.robosystems.ai](https://api.robosystems.ai)
26
+ - API documentation: [https://api.robosystems.ai/docs](https://api.robosystems.ai/docs)
443
27
  - OpenAPI specification: [https://api.robosystems.ai/openapi.json](https://api.robosystems.ai/openapi.json)
444
28
 
445
29
  ## Support
446
30
 
447
- - Issues: [GitHub Issues](https://github.com/RoboFinSystems/robosystems-typescript-client/issues)
31
+ - Issues: [Issues](https://github.com/RoboFinSystems/robosystems-typescript-client/issues)
32
+ - Discussions: [Discussions](https://github.com/RoboFinSystems/robosystems-typescript-client/discussions)
33
+ - Projects: [Projects](https://github.com/RoboFinSystems/robosystems-typescript-client/projects)
34
+ - Wiki: [Wiki](https://github.com/RoboFinSystems/robosystems-typescript-client/wiki)
448
35
 
449
36
  ## License
450
37
 
451
38
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
452
39
 
453
- MIT © 2025 Harbinger FinLab
40
+ MIT © 2025 RFS LLC
@@ -39,16 +39,9 @@ echo ""
39
39
 
40
40
  # Check for uncommitted changes
41
41
  if ! git diff --quiet || ! git diff --cached --quiet; then
42
- echo "⚠️ You have uncommitted changes."
43
- read -p "Do you want to stash them? (y/N): " -n 1 -r
44
- echo
45
- if [[ $REPLY =~ ^[Yy]$ ]]; then
46
- git stash push -m "Auto-stash before creating branch $FULL_BRANCH"
47
- echo "✅ Changes stashed"
48
- else
49
- echo "❌ Please commit or stash your changes first"
50
- exit 1
51
- fi
42
+ echo "⚠️ You have uncommitted changes. Auto-stashing..."
43
+ git stash push -m "Auto-stash before creating branch $FULL_BRANCH"
44
+ echo "✅ Changes stashed"
52
45
  fi
53
46
 
54
47
  # Fetch latest changes from remote
@@ -92,13 +85,10 @@ echo " 2. Push your changes: git push"
92
85
  echo " 3. Create a PR: gh pr create --base $BASE_BRANCH --title \"Your PR title\" --body \"Your PR description\""
93
86
  echo " or use: npm run pr:create"
94
87
 
95
- # Check if we had stashed changes
88
+ # Check if we had stashed changes and auto-apply them
96
89
  if git stash list | grep -q "Auto-stash before creating branch $FULL_BRANCH"; then
97
90
  echo ""
98
- read -p "Do you want to apply your stashed changes? (y/N): " -n 1 -r
99
- echo
100
- if [[ $REPLY =~ ^[Yy]$ ]]; then
101
- git stash pop
102
- echo "✅ Stashed changes applied"
103
- fi
91
+ echo "Auto-applying stashed changes..."
92
+ git stash pop
93
+ echo "✅ Stashed changes applied"
104
94
  fi
@@ -34,6 +34,7 @@ export declare class CopyClient {
34
34
  baseUrl: string;
35
35
  credentials?: 'include' | 'same-origin' | 'omit';
36
36
  headers?: Record<string, string>;
37
+ token?: string;
37
38
  });
38
39
  /**
39
40
  * Copy data from S3 to graph database
@@ -39,6 +39,9 @@ class CopyClient {
39
39
  url: '/v1/{graph_id}/copy',
40
40
  path: { graph_id: graphId },
41
41
  body: request,
42
+ query: {
43
+ token: this.config.token, // Pass JWT token for SSE authentication
44
+ },
42
45
  };
43
46
  try {
44
47
  // Execute the copy request
@@ -53,12 +53,14 @@ export class CopyClient {
53
53
  baseUrl: string
54
54
  credentials?: 'include' | 'same-origin' | 'omit'
55
55
  headers?: Record<string, string>
56
+ token?: string // JWT token for authentication
56
57
  }
57
58
 
58
59
  constructor(config: {
59
60
  baseUrl: string
60
61
  credentials?: 'include' | 'same-origin' | 'omit'
61
62
  headers?: Record<string, string>
63
+ token?: string // JWT token for authentication
62
64
  }) {
63
65
  this.config = config
64
66
  }
@@ -111,6 +113,9 @@ export class CopyClient {
111
113
  url: '/v1/{graph_id}/copy' as const,
112
114
  path: { graph_id: graphId },
113
115
  body: request,
116
+ query: {
117
+ token: this.config.token, // Pass JWT token for SSE authentication
118
+ },
114
119
  }
115
120
 
116
121
  try {
@@ -24,6 +24,7 @@ export declare class OperationClient {
24
24
  baseUrl: string;
25
25
  credentials?: 'include' | 'same-origin' | 'omit';
26
26
  headers?: Record<string, string>;
27
+ token?: string;
27
28
  maxRetries?: number;
28
29
  retryDelay?: number;
29
30
  });
@@ -34,6 +34,7 @@ export class OperationClient {
34
34
  baseUrl: string
35
35
  credentials?: 'include' | 'same-origin' | 'omit'
36
36
  headers?: Record<string, string>
37
+ token?: string // JWT token for authentication
37
38
  maxRetries?: number
38
39
  retryDelay?: number
39
40
  }
@@ -44,6 +45,7 @@ export class OperationClient {
44
45
  baseUrl: string
45
46
  credentials?: 'include' | 'same-origin' | 'omit'
46
47
  headers?: Record<string, string>
48
+ token?: string // JWT token for authentication
47
49
  maxRetries?: number
48
50
  retryDelay?: number
49
51
  }) {
@@ -33,6 +33,7 @@ export declare class QueryClient {
33
33
  baseUrl: string;
34
34
  credentials?: 'include' | 'same-origin' | 'omit';
35
35
  headers?: Record<string, string>;
36
+ token?: string;
36
37
  });
37
38
  executeQuery(graphId: string, request: QueryRequest, options?: QueryOptions): Promise<QueryResult | AsyncIterableIterator<any>>;
38
39
  private streamQueryResults;
@@ -23,6 +23,7 @@ class QueryClient {
23
23
  query: {
24
24
  mode: options.mode,
25
25
  test_mode: options.testMode,
26
+ token: this.config.token, // Pass JWT token for SSE authentication
26
27
  },
27
28
  };
28
29
  // Execute the query
@@ -47,12 +47,14 @@ export class QueryClient {
47
47
  baseUrl: string
48
48
  credentials?: 'include' | 'same-origin' | 'omit'
49
49
  headers?: Record<string, string>
50
+ token?: string // JWT token for authentication
50
51
  }
51
52
 
52
53
  constructor(config: {
53
54
  baseUrl: string
54
55
  credentials?: 'include' | 'same-origin' | 'omit'
55
56
  headers?: Record<string, string>
57
+ token?: string // JWT token for authentication
56
58
  }) {
57
59
  this.config = config
58
60
  }
@@ -72,6 +74,7 @@ export class QueryClient {
72
74
  query: {
73
75
  mode: options.mode,
74
76
  test_mode: options.testMode,
77
+ token: this.config.token, // Pass JWT token for SSE authentication
75
78
  },
76
79
  }
77
80
 
@@ -1,11 +1,25 @@
1
1
  /**
2
2
  * Core SSE (Server-Sent Events) client for RoboSystems API
3
3
  * Provides automatic reconnection, event replay, and type-safe event handling
4
+ *
5
+ * SECURITY NOTE: When using JWT authentication, tokens are passed as query parameters
6
+ * due to EventSource API limitations. This means tokens may appear in:
7
+ * - Server access logs
8
+ * - Proxy logs
9
+ * - Browser history
10
+ * - Referer headers
11
+ *
12
+ * For production environments with sensitive data, consider:
13
+ * - Using cookie-based authentication instead
14
+ * - Implementing a WebSocket-based alternative
15
+ * - Using short-lived tokens that expire quickly
16
+ * - Ensuring all connections use HTTPS
4
17
  */
5
18
  export interface SSEConfig {
6
19
  baseUrl: string;
7
20
  credentials?: 'include' | 'same-origin' | 'omit';
8
21
  headers?: Record<string, string>;
22
+ token?: string;
9
23
  maxRetries?: number;
10
24
  retryDelay?: number;
11
25
  heartbeatInterval?: number;
@@ -28,7 +28,13 @@ class SSEClient {
28
28
  }
29
29
  async connect(operationId, fromSequence = 0) {
30
30
  return new Promise((resolve, reject) => {
31
- const url = `${this.config.baseUrl}/v1/operations/${operationId}/stream?from_sequence=${fromSequence}`;
31
+ let url = `${this.config.baseUrl}/v1/operations/${operationId}/stream?from_sequence=${fromSequence}`;
32
+ // Add JWT token as query parameter if provided
33
+ // WARNING: EventSource API doesn't support custom headers, so tokens are passed via query param
34
+ // This has security implications - see class documentation
35
+ if (this.config.token) {
36
+ url += `&token=${encodeURIComponent(this.config.token)}`;
37
+ }
32
38
  this.eventSource = new EventSource(url, {
33
39
  withCredentials: this.config.credentials === 'include',
34
40
  });