@shin1ohno/sage 0.2.4 → 0.5.3
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/dist/cli/http-server-with-config.d.ts +38 -0
- package/dist/cli/http-server-with-config.d.ts.map +1 -0
- package/dist/cli/http-server-with-config.js +458 -0
- package/dist/cli/http-server-with-config.js.map +1 -0
- package/dist/cli/http-server.d.ts +74 -0
- package/dist/cli/http-server.d.ts.map +1 -0
- package/dist/cli/http-server.js +407 -0
- package/dist/cli/http-server.js.map +1 -0
- package/dist/cli/jwt-middleware.d.ts +36 -0
- package/dist/cli/jwt-middleware.d.ts.map +1 -0
- package/dist/cli/jwt-middleware.js +99 -0
- package/dist/cli/jwt-middleware.js.map +1 -0
- package/dist/cli/main-entry.d.ts +41 -0
- package/dist/cli/main-entry.d.ts.map +1 -0
- package/dist/cli/main-entry.js +80 -0
- package/dist/cli/main-entry.js.map +1 -0
- package/dist/cli/mcp-handler.d.ts +56 -0
- package/dist/cli/mcp-handler.d.ts.map +1 -0
- package/dist/cli/mcp-handler.js +2189 -0
- package/dist/cli/mcp-handler.js.map +1 -0
- package/dist/cli/parser.d.ts +43 -0
- package/dist/cli/parser.d.ts.map +1 -0
- package/dist/cli/parser.js +162 -0
- package/dist/cli/parser.js.map +1 -0
- package/dist/cli/remote-config-loader.d.ts +85 -0
- package/dist/cli/remote-config-loader.d.ts.map +1 -0
- package/dist/cli/remote-config-loader.js +129 -0
- package/dist/cli/remote-config-loader.js.map +1 -0
- package/dist/cli/secret-auth.d.ts +47 -0
- package/dist/cli/secret-auth.d.ts.map +1 -0
- package/dist/cli/secret-auth.js +165 -0
- package/dist/cli/secret-auth.js.map +1 -0
- package/dist/cli/sse-stream-handler.d.ts +45 -0
- package/dist/cli/sse-stream-handler.d.ts.map +1 -0
- package/dist/cli/sse-stream-handler.js +125 -0
- package/dist/cli/sse-stream-handler.js.map +1 -0
- package/dist/index.js +885 -209
- package/dist/index.js.map +1 -1
- package/dist/integrations/calendar-event-creator.d.ts +152 -0
- package/dist/integrations/calendar-event-creator.d.ts.map +1 -0
- package/dist/integrations/calendar-event-creator.js +507 -0
- package/dist/integrations/calendar-event-creator.js.map +1 -0
- package/dist/integrations/calendar-event-deleter.d.ts +137 -0
- package/dist/integrations/calendar-event-deleter.d.ts.map +1 -0
- package/dist/integrations/calendar-event-deleter.js +378 -0
- package/dist/integrations/calendar-event-deleter.js.map +1 -0
- package/dist/integrations/calendar-event-response.d.ts +213 -0
- package/dist/integrations/calendar-event-response.d.ts.map +1 -0
- package/dist/integrations/calendar-event-response.js +560 -0
- package/dist/integrations/calendar-event-response.js.map +1 -0
- package/dist/integrations/calendar-service.d.ts +85 -10
- package/dist/integrations/calendar-service.d.ts.map +1 -1
- package/dist/integrations/calendar-service.js +317 -35
- package/dist/integrations/calendar-service.js.map +1 -1
- package/dist/oauth/client-store.d.ts +36 -0
- package/dist/oauth/client-store.d.ts.map +1 -0
- package/dist/oauth/client-store.js +104 -0
- package/dist/oauth/client-store.js.map +1 -0
- package/dist/oauth/code-store.d.ts +48 -0
- package/dist/oauth/code-store.d.ts.map +1 -0
- package/dist/oauth/code-store.js +89 -0
- package/dist/oauth/code-store.js.map +1 -0
- package/dist/oauth/index.d.ts +13 -0
- package/dist/oauth/index.d.ts.map +1 -0
- package/dist/oauth/index.js +21 -0
- package/dist/oauth/index.js.map +1 -0
- package/dist/oauth/oauth-handler.d.ts +101 -0
- package/dist/oauth/oauth-handler.d.ts.map +1 -0
- package/dist/oauth/oauth-handler.js +577 -0
- package/dist/oauth/oauth-handler.js.map +1 -0
- package/dist/oauth/oauth-server.d.ts +165 -0
- package/dist/oauth/oauth-server.d.ts.map +1 -0
- package/dist/oauth/oauth-server.js +489 -0
- package/dist/oauth/oauth-server.js.map +1 -0
- package/dist/oauth/pkce.d.ts +48 -0
- package/dist/oauth/pkce.d.ts.map +1 -0
- package/dist/oauth/pkce.js +106 -0
- package/dist/oauth/pkce.js.map +1 -0
- package/dist/oauth/refresh-token-store.d.ts +45 -0
- package/dist/oauth/refresh-token-store.d.ts.map +1 -0
- package/dist/oauth/refresh-token-store.js +98 -0
- package/dist/oauth/refresh-token-store.js.map +1 -0
- package/dist/oauth/token-service.d.ts +46 -0
- package/dist/oauth/token-service.d.ts.map +1 -0
- package/dist/oauth/token-service.js +199 -0
- package/dist/oauth/token-service.js.map +1 -0
- package/dist/oauth/types.d.ts +264 -0
- package/dist/oauth/types.d.ts.map +1 -0
- package/dist/oauth/types.js +37 -0
- package/dist/oauth/types.js.map +1 -0
- package/dist/version.d.ts +9 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/manifest.json +12 -20
- package/package.json +1 -1
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret Authentication for Remote MCP Server
|
|
3
|
+
* Requirements: 15.4, 15.5, 15.6
|
|
4
|
+
*
|
|
5
|
+
* Provides JWT-based authentication using a shared secret.
|
|
6
|
+
*/
|
|
7
|
+
import { createHmac, randomBytes } from 'crypto';
|
|
8
|
+
/**
|
|
9
|
+
* Authentication error
|
|
10
|
+
*/
|
|
11
|
+
export class AuthError extends Error {
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'AuthError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Minimum secret length
|
|
19
|
+
*/
|
|
20
|
+
const MIN_SECRET_LENGTH = 32;
|
|
21
|
+
/**
|
|
22
|
+
* Parse expiresIn string to seconds
|
|
23
|
+
*/
|
|
24
|
+
function parseExpiresIn(expiresIn) {
|
|
25
|
+
const match = expiresIn.match(/^(\d+)([smhdw])$/);
|
|
26
|
+
if (!match) {
|
|
27
|
+
return 86400; // Default to 24h
|
|
28
|
+
}
|
|
29
|
+
const value = parseInt(match[1], 10);
|
|
30
|
+
const unit = match[2];
|
|
31
|
+
switch (unit) {
|
|
32
|
+
case 's':
|
|
33
|
+
return value;
|
|
34
|
+
case 'm':
|
|
35
|
+
return value * 60;
|
|
36
|
+
case 'h':
|
|
37
|
+
return value * 3600;
|
|
38
|
+
case 'd':
|
|
39
|
+
return value * 86400;
|
|
40
|
+
case 'w':
|
|
41
|
+
return value * 604800;
|
|
42
|
+
default:
|
|
43
|
+
return 86400;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Base64url encode
|
|
48
|
+
*/
|
|
49
|
+
function base64urlEncode(data) {
|
|
50
|
+
const buffer = typeof data === 'string' ? Buffer.from(data) : data;
|
|
51
|
+
return buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Base64url decode
|
|
55
|
+
*/
|
|
56
|
+
function base64urlDecode(data) {
|
|
57
|
+
const base64 = data.replace(/-/g, '+').replace(/_/g, '/');
|
|
58
|
+
const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);
|
|
59
|
+
return Buffer.from(padded, 'base64').toString('utf8');
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Create JWT header
|
|
63
|
+
*/
|
|
64
|
+
function createHeader() {
|
|
65
|
+
return base64urlEncode(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Create JWT payload
|
|
69
|
+
*/
|
|
70
|
+
function createPayload(expiresInSeconds) {
|
|
71
|
+
const now = Math.floor(Date.now() / 1000);
|
|
72
|
+
const jti = randomBytes(16).toString('hex');
|
|
73
|
+
return base64urlEncode(JSON.stringify({
|
|
74
|
+
iat: now,
|
|
75
|
+
exp: now + expiresInSeconds,
|
|
76
|
+
jti,
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Create HMAC signature
|
|
81
|
+
*/
|
|
82
|
+
function createSignature(data, secret) {
|
|
83
|
+
const hmac = createHmac('sha256', secret);
|
|
84
|
+
hmac.update(data);
|
|
85
|
+
return base64urlEncode(hmac.digest());
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Verify HMAC signature
|
|
89
|
+
*/
|
|
90
|
+
function verifySignature(data, signature, secret) {
|
|
91
|
+
const expectedSignature = createSignature(data, secret);
|
|
92
|
+
return signature === expectedSignature;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Create a secret authenticator
|
|
96
|
+
*/
|
|
97
|
+
export function createSecretAuthenticator(config) {
|
|
98
|
+
if (!config.secret || config.secret.length < MIN_SECRET_LENGTH) {
|
|
99
|
+
throw new AuthError('Secret must be at least 32 characters');
|
|
100
|
+
}
|
|
101
|
+
const expiresInSeconds = parseExpiresIn(config.expiresIn);
|
|
102
|
+
return {
|
|
103
|
+
async authenticate(providedSecret) {
|
|
104
|
+
// Validate provided secret
|
|
105
|
+
if (!providedSecret || providedSecret !== config.secret) {
|
|
106
|
+
return {
|
|
107
|
+
success: false,
|
|
108
|
+
error: 'Invalid secret',
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
// Generate JWT
|
|
112
|
+
const header = createHeader();
|
|
113
|
+
const payload = createPayload(expiresInSeconds);
|
|
114
|
+
const signature = createSignature(`${header}.${payload}`, config.secret);
|
|
115
|
+
const token = `${header}.${payload}.${signature}`;
|
|
116
|
+
return {
|
|
117
|
+
success: true,
|
|
118
|
+
token,
|
|
119
|
+
expiresIn: expiresInSeconds,
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
async verifyToken(token) {
|
|
123
|
+
if (!token || !token.includes('.')) {
|
|
124
|
+
return {
|
|
125
|
+
valid: false,
|
|
126
|
+
error: 'Invalid token format',
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const parts = token.split('.');
|
|
130
|
+
if (parts.length !== 3) {
|
|
131
|
+
return {
|
|
132
|
+
valid: false,
|
|
133
|
+
error: 'Invalid token format',
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const [header, payload, signature] = parts;
|
|
137
|
+
// Verify signature
|
|
138
|
+
if (!verifySignature(`${header}.${payload}`, signature, config.secret)) {
|
|
139
|
+
return {
|
|
140
|
+
valid: false,
|
|
141
|
+
error: 'Token signature invalid',
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
// Parse and verify payload
|
|
145
|
+
try {
|
|
146
|
+
const payloadData = JSON.parse(base64urlDecode(payload));
|
|
147
|
+
const now = Math.floor(Date.now() / 1000);
|
|
148
|
+
if (payloadData.exp && payloadData.exp < now) {
|
|
149
|
+
return {
|
|
150
|
+
valid: false,
|
|
151
|
+
error: 'Token expired',
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return { valid: true };
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return {
|
|
158
|
+
valid: false,
|
|
159
|
+
error: 'Invalid token payload',
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=secret-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-auth.js","sourceRoot":"","sources":["../../src/cli/secret-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE Stream Handler for Streamable HTTP Transport
|
|
3
|
+
* Requirements: 20.1, 20.2, 20.3, 20.4, 20.5, 20.6, 20.7, 20.8, 20.9, 20.10
|
|
4
|
+
*
|
|
5
|
+
* Implements Server-Sent Events (SSE) streaming for the MCP Streamable HTTP protocol.
|
|
6
|
+
* Handles GET /mcp requests by establishing SSE streams with keepalive support.
|
|
7
|
+
*/
|
|
8
|
+
import { IncomingMessage, ServerResponse } from 'http';
|
|
9
|
+
/**
|
|
10
|
+
* Options for SSE stream handler
|
|
11
|
+
*/
|
|
12
|
+
export interface SSEStreamHandlerOptions {
|
|
13
|
+
/** Keepalive interval in milliseconds (default: 30000) */
|
|
14
|
+
keepaliveInterval?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* SSE Stream Handler interface
|
|
18
|
+
*/
|
|
19
|
+
export interface SSEStreamHandler {
|
|
20
|
+
/**
|
|
21
|
+
* Handle SSE request (GET /mcp)
|
|
22
|
+
*/
|
|
23
|
+
handleSSERequest(req: IncomingMessage, res: ServerResponse): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Send event to a specific session
|
|
26
|
+
*/
|
|
27
|
+
sendEvent(eventType: string, data: unknown, sessionId?: string): void;
|
|
28
|
+
/**
|
|
29
|
+
* Broadcast event to all connected clients
|
|
30
|
+
*/
|
|
31
|
+
broadcast(data: unknown): void;
|
|
32
|
+
/**
|
|
33
|
+
* Get number of active connections
|
|
34
|
+
*/
|
|
35
|
+
getActiveConnections(): number;
|
|
36
|
+
/**
|
|
37
|
+
* Cleanup all connections
|
|
38
|
+
*/
|
|
39
|
+
cleanup(): void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create SSE stream handler
|
|
43
|
+
*/
|
|
44
|
+
export declare function createSSEStreamHandler(options?: SSEStreamHandlerOptions): SSEStreamHandler;
|
|
45
|
+
//# sourceMappingURL=sse-stream-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-stream-handler.d.ts","sourceRoot":"","sources":["../../src/cli/sse-stream-handler.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAGvD;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,0DAA0D;IAC1D,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAWD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,gBAAgB,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3E;;OAEG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEtE;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAE/B;;OAEG;IACH,oBAAoB,IAAI,MAAM,CAAC;IAE/B;;OAEG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB;AAqID;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,uBAA4B,GACpC,gBAAgB,CAElB"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE Stream Handler for Streamable HTTP Transport
|
|
3
|
+
* Requirements: 20.1, 20.2, 20.3, 20.4, 20.5, 20.6, 20.7, 20.8, 20.9, 20.10
|
|
4
|
+
*
|
|
5
|
+
* Implements Server-Sent Events (SSE) streaming for the MCP Streamable HTTP protocol.
|
|
6
|
+
* Handles GET /mcp requests by establishing SSE streams with keepalive support.
|
|
7
|
+
*/
|
|
8
|
+
import { randomUUID } from 'crypto';
|
|
9
|
+
/**
|
|
10
|
+
* Default keepalive interval (30 seconds)
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_KEEPALIVE_INTERVAL = 30000;
|
|
13
|
+
/**
|
|
14
|
+
* SSE Stream Handler Implementation
|
|
15
|
+
*/
|
|
16
|
+
class SSEStreamHandlerImpl {
|
|
17
|
+
connections = new Map();
|
|
18
|
+
keepaliveInterval;
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
this.keepaliveInterval = options.keepaliveInterval ?? DEFAULT_KEEPALIVE_INTERVAL;
|
|
21
|
+
}
|
|
22
|
+
async handleSSERequest(_req, res) {
|
|
23
|
+
const sessionId = randomUUID();
|
|
24
|
+
// Set SSE headers
|
|
25
|
+
// Requirement 20.2: Content-Type text/event-stream
|
|
26
|
+
// Requirement 20.5: Cache-Control no-cache
|
|
27
|
+
// Requirement 20.6: Connection keep-alive
|
|
28
|
+
res.writeHead(200, {
|
|
29
|
+
'Content-Type': 'text/event-stream',
|
|
30
|
+
'Cache-Control': 'no-cache',
|
|
31
|
+
'Connection': 'keep-alive',
|
|
32
|
+
'X-Accel-Buffering': 'no',
|
|
33
|
+
// Requirement 20.4: CORS headers
|
|
34
|
+
'Access-Control-Allow-Origin': '*',
|
|
35
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
36
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
37
|
+
});
|
|
38
|
+
// Create connection record
|
|
39
|
+
const connection = {
|
|
40
|
+
sessionId,
|
|
41
|
+
response: res,
|
|
42
|
+
keepaliveTimer: null,
|
|
43
|
+
};
|
|
44
|
+
this.connections.set(sessionId, connection);
|
|
45
|
+
// Requirement 20.1: Send endpoint event on connection
|
|
46
|
+
this.sendEndpointEvent(connection);
|
|
47
|
+
// Requirement 20.3: Start keepalive timer
|
|
48
|
+
this.startKeepalive(connection);
|
|
49
|
+
// Requirement 20.7: Handle client disconnect
|
|
50
|
+
res.on('close', () => {
|
|
51
|
+
this.removeConnection(sessionId);
|
|
52
|
+
});
|
|
53
|
+
res.on('error', () => {
|
|
54
|
+
this.removeConnection(sessionId);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
sendEvent(eventType, data, sessionId) {
|
|
58
|
+
const payload = this.formatSSEEvent(eventType, data);
|
|
59
|
+
if (sessionId) {
|
|
60
|
+
const connection = this.connections.get(sessionId);
|
|
61
|
+
if (connection) {
|
|
62
|
+
connection.response.write(payload);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// Send to all connections
|
|
67
|
+
for (const connection of this.connections.values()) {
|
|
68
|
+
connection.response.write(payload);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
broadcast(data) {
|
|
73
|
+
const payload = this.formatSSEEvent('message', data);
|
|
74
|
+
for (const connection of this.connections.values()) {
|
|
75
|
+
connection.response.write(payload);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
getActiveConnections() {
|
|
79
|
+
return this.connections.size;
|
|
80
|
+
}
|
|
81
|
+
cleanup() {
|
|
82
|
+
for (const [sessionId, connection] of this.connections.entries()) {
|
|
83
|
+
if (connection.keepaliveTimer) {
|
|
84
|
+
clearInterval(connection.keepaliveTimer);
|
|
85
|
+
}
|
|
86
|
+
this.connections.delete(sessionId);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
sendEndpointEvent(connection) {
|
|
90
|
+
const data = {
|
|
91
|
+
type: 'endpoint',
|
|
92
|
+
url: '/mcp',
|
|
93
|
+
sessionId: connection.sessionId,
|
|
94
|
+
};
|
|
95
|
+
const payload = this.formatSSEEvent('endpoint', data);
|
|
96
|
+
connection.response.write(payload);
|
|
97
|
+
}
|
|
98
|
+
startKeepalive(connection) {
|
|
99
|
+
connection.keepaliveTimer = setInterval(() => {
|
|
100
|
+
// Requirement 20.3: Send keepalive comment every 30 seconds
|
|
101
|
+
// SSE comment format: ": comment\n\n"
|
|
102
|
+
connection.response.write(': keepalive\n\n');
|
|
103
|
+
}, this.keepaliveInterval);
|
|
104
|
+
}
|
|
105
|
+
removeConnection(sessionId) {
|
|
106
|
+
const connection = this.connections.get(sessionId);
|
|
107
|
+
if (connection) {
|
|
108
|
+
if (connection.keepaliveTimer) {
|
|
109
|
+
clearInterval(connection.keepaliveTimer);
|
|
110
|
+
}
|
|
111
|
+
this.connections.delete(sessionId);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
formatSSEEvent(eventType, data) {
|
|
115
|
+
const jsonData = JSON.stringify(data);
|
|
116
|
+
return `event: ${eventType}\ndata: ${jsonData}\n\n`;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Create SSE stream handler
|
|
121
|
+
*/
|
|
122
|
+
export function createSSEStreamHandler(options = {}) {
|
|
123
|
+
return new SSEStreamHandlerImpl(options);
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=sse-stream-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-stream-handler.js","sourceRoot":"","sources":["../../src/cli/sse-stream-handler.ts"],"names":[],"mappings":"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"}
|