@agentick/server 0.0.1
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/LICENSE +21 -0
- package/README.md +114 -0
- package/dist/auth.d.ts +62 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +51 -0
- package/dist/auth.js.map +1 -0
- package/dist/event-bridge.d.ts +91 -0
- package/dist/event-bridge.d.ts.map +1 -0
- package/dist/event-bridge.js +301 -0
- package/dist/event-bridge.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/session-handler.d.ts +108 -0
- package/dist/session-handler.d.ts.map +1 -0
- package/dist/session-handler.js +197 -0
- package/dist/session-handler.js.map +1 -0
- package/dist/session-store.d.ts +62 -0
- package/dist/session-store.d.ts.map +1 -0
- package/dist/session-store.js +82 -0
- package/dist/session-store.js.map +1 -0
- package/dist/sse.d.ts +71 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +137 -0
- package/dist/sse.js.map +1 -0
- package/dist/types.d.ts +30 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +46 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @agentick/server - Server SDK for Agentick
|
|
3
|
+
*
|
|
4
|
+
* Provides server-side components for running Agentick applications:
|
|
5
|
+
* - SSE streaming utilities
|
|
6
|
+
* - SSE utilities for streaming
|
|
7
|
+
*
|
|
8
|
+
* This package provides hooks and handlers that your web framework
|
|
9
|
+
* routes can call into. It does NOT define routes - that's your
|
|
10
|
+
* application's responsibility.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { createSSEWriter, setSSEHeaders } from '@agentick/server';
|
|
15
|
+
*
|
|
16
|
+
* app.get('/events', (req, res) => {
|
|
17
|
+
* setSSEHeaders(res);
|
|
18
|
+
* const writer = createSSEWriter(res);
|
|
19
|
+
* writer.writeEvent({ channel: 'session:events', type: 'ping', payload: {} });
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @module @agentick/server
|
|
24
|
+
*/
|
|
25
|
+
export { createSSEWriter, streamToSSE, setSSEHeaders } from "./sse";
|
|
26
|
+
export { extractToken, validateAuth, type AuthConfig, type AuthResult } from "./auth";
|
|
27
|
+
export type { ChannelEvent, ChannelEventMetadata, ConnectionMetadata, ProtocolError, SessionResultPayload, ToolConfirmationRequest, ToolConfirmationResponse, SessionState, CreateSessionResponse, SSEWriter, SSEWriterOptions, } from "./types";
|
|
28
|
+
export { FrameworkChannels, ErrorCodes } from "./types";
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAGpE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGtF,YAAY,EAEV,YAAY,EACZ,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,YAAY,EACZ,qBAAqB,EAGrB,SAAS,EACT,gBAAgB,GACjB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @agentick/server - Server SDK for Agentick
|
|
3
|
+
*
|
|
4
|
+
* Provides server-side components for running Agentick applications:
|
|
5
|
+
* - SSE streaming utilities
|
|
6
|
+
* - SSE utilities for streaming
|
|
7
|
+
*
|
|
8
|
+
* This package provides hooks and handlers that your web framework
|
|
9
|
+
* routes can call into. It does NOT define routes - that's your
|
|
10
|
+
* application's responsibility.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { createSSEWriter, setSSEHeaders } from '@agentick/server';
|
|
15
|
+
*
|
|
16
|
+
* app.get('/events', (req, res) => {
|
|
17
|
+
* setSSEHeaders(res);
|
|
18
|
+
* const writer = createSSEWriter(res);
|
|
19
|
+
* writer.writeEvent({ channel: 'session:events', type: 'ping', payload: {} });
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @module @agentick/server
|
|
24
|
+
*/
|
|
25
|
+
// SSE utilities
|
|
26
|
+
export { createSSEWriter, streamToSSE, setSSEHeaders } from "./sse";
|
|
27
|
+
// Auth utilities
|
|
28
|
+
export { extractToken, validateAuth } from "./auth";
|
|
29
|
+
// Re-export constants
|
|
30
|
+
export { FrameworkChannels, ErrorCodes } from "./types";
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,gBAAgB;AAChB,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEpE,iBAAiB;AACjB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAoC,MAAM,QAAQ,CAAC;AAoBtF,sBAAsB;AACtB,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Handler - Orchestrates session lifecycle.
|
|
3
|
+
*
|
|
4
|
+
* The session handler provides core operations for managing sessions.
|
|
5
|
+
* It does NOT define routes - your web framework routes call these methods.
|
|
6
|
+
*
|
|
7
|
+
* @module @tentickle/server/session-handler
|
|
8
|
+
*/
|
|
9
|
+
import type { Session, SendResult } from "@tentickle/core/app";
|
|
10
|
+
import type { StreamEvent } from "@tentickle/shared";
|
|
11
|
+
import type { SessionHandler, SessionHandlerConfig, CreateSessionInput, SendInput, SessionStateInfo } from "./types.js";
|
|
12
|
+
/**
|
|
13
|
+
* Session Handler implementation.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // Create the handler
|
|
18
|
+
* const sessionHandler = createSessionHandler({
|
|
19
|
+
* app: myApp,
|
|
20
|
+
* store: new InMemorySessionStore(),
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Use in your routes
|
|
24
|
+
* app.post('/sessions', async (req, res) => {
|
|
25
|
+
* const { sessionId } = await sessionHandler.create(req.body);
|
|
26
|
+
* res.json({ sessionId, status: 'created' });
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* app.post('/sessions/:id/messages', async (req, res) => {
|
|
30
|
+
* const result = await sessionHandler.send(req.params.id, {
|
|
31
|
+
* messages: [req.body],
|
|
32
|
+
* });
|
|
33
|
+
* res.json(result);
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* app.get('/sessions/:id/stream', async (req, res) => {
|
|
37
|
+
* res.setHeader('Content-Type', 'text/event-stream');
|
|
38
|
+
* for await (const event of sessionHandler.stream(req.params.id, {})) {
|
|
39
|
+
* res.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
40
|
+
* }
|
|
41
|
+
* res.end();
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare class SessionHandlerImpl implements SessionHandler {
|
|
46
|
+
private app;
|
|
47
|
+
private store;
|
|
48
|
+
private defaultSessionOptions;
|
|
49
|
+
constructor(config: SessionHandlerConfig);
|
|
50
|
+
/**
|
|
51
|
+
* Create a new session.
|
|
52
|
+
*/
|
|
53
|
+
create(input: CreateSessionInput): Promise<{
|
|
54
|
+
sessionId: string;
|
|
55
|
+
session: Session;
|
|
56
|
+
}>;
|
|
57
|
+
/**
|
|
58
|
+
* Send to a session and wait for result.
|
|
59
|
+
*/
|
|
60
|
+
send(sessionId: string, input: SendInput): Promise<SendResult>;
|
|
61
|
+
/**
|
|
62
|
+
* Stream events from a session.
|
|
63
|
+
*/
|
|
64
|
+
stream(sessionId: string, input: SendInput): AsyncIterable<StreamEvent>;
|
|
65
|
+
/**
|
|
66
|
+
* Get session by ID.
|
|
67
|
+
*/
|
|
68
|
+
getSession(sessionId: string): Session | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* Get session state.
|
|
71
|
+
*/
|
|
72
|
+
getState(sessionId: string): SessionStateInfo | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* Delete session.
|
|
75
|
+
*/
|
|
76
|
+
delete(sessionId: string): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* List all session IDs.
|
|
79
|
+
*/
|
|
80
|
+
list(): string[];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Create a session handler.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const sessionHandler = createSessionHandler({
|
|
88
|
+
* app,
|
|
89
|
+
* store: new InMemorySessionStore(),
|
|
90
|
+
* });
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export declare function createSessionHandler(config: SessionHandlerConfig): SessionHandler;
|
|
94
|
+
/**
|
|
95
|
+
* Session not found error.
|
|
96
|
+
*/
|
|
97
|
+
export declare class SessionNotFoundError extends Error {
|
|
98
|
+
readonly code = "SESSION_NOT_FOUND";
|
|
99
|
+
constructor(sessionId: string);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Session closed error.
|
|
103
|
+
*/
|
|
104
|
+
export declare class SessionClosedError extends Error {
|
|
105
|
+
readonly code = "SESSION_CLOSED";
|
|
106
|
+
constructor(sessionId: string);
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=session-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-handler.d.ts","sourceRoot":"","sources":["../src/session-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAO,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,KAAK,EACV,cAAc,EACd,oBAAoB,EAEpB,kBAAkB,EAClB,SAAS,EACT,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAgBpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAAa,kBAAmB,YAAW,cAAc;IACvD,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,qBAAqB,CAA0B;gBAE3C,MAAM,EAAE,oBAAoB;IAMxC;;OAEG;IACG,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IA2BzF;;OAEG;IACG,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;IAkBpE;;OAEG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,aAAa,CAAC,WAAW,CAAC;IAoBvE;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAIlD;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAgBzD;;OAEG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIlC;;OAEG;IACH,IAAI,IAAI,MAAM,EAAE;CAGjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,cAAc,CAEjF;AAMD;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,uBAAuB;gBAExB,SAAS,EAAE,MAAM;CAI9B;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,oBAAoB;gBAErB,SAAS,EAAE,MAAM;CAI9B"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Handler - Orchestrates session lifecycle.
|
|
3
|
+
*
|
|
4
|
+
* The session handler provides core operations for managing sessions.
|
|
5
|
+
* It does NOT define routes - your web framework routes call these methods.
|
|
6
|
+
*
|
|
7
|
+
* @module @tentickle/server/session-handler
|
|
8
|
+
*/
|
|
9
|
+
import { InMemorySessionStore } from "./session-store.js";
|
|
10
|
+
// Generate UUID (works in Node.js)
|
|
11
|
+
function generateId() {
|
|
12
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
13
|
+
return crypto.randomUUID();
|
|
14
|
+
}
|
|
15
|
+
// Fallback
|
|
16
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
17
|
+
const r = (Math.random() * 16) | 0;
|
|
18
|
+
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
19
|
+
return v.toString(16);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Session Handler implementation.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* // Create the handler
|
|
28
|
+
* const sessionHandler = createSessionHandler({
|
|
29
|
+
* app: myApp,
|
|
30
|
+
* store: new InMemorySessionStore(),
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* // Use in your routes
|
|
34
|
+
* app.post('/sessions', async (req, res) => {
|
|
35
|
+
* const { sessionId } = await sessionHandler.create(req.body);
|
|
36
|
+
* res.json({ sessionId, status: 'created' });
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* app.post('/sessions/:id/messages', async (req, res) => {
|
|
40
|
+
* const result = await sessionHandler.send(req.params.id, {
|
|
41
|
+
* messages: [req.body],
|
|
42
|
+
* });
|
|
43
|
+
* res.json(result);
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* app.get('/sessions/:id/stream', async (req, res) => {
|
|
47
|
+
* res.setHeader('Content-Type', 'text/event-stream');
|
|
48
|
+
* for await (const event of sessionHandler.stream(req.params.id, {})) {
|
|
49
|
+
* res.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
50
|
+
* }
|
|
51
|
+
* res.end();
|
|
52
|
+
* });
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export class SessionHandlerImpl {
|
|
56
|
+
app;
|
|
57
|
+
store;
|
|
58
|
+
defaultSessionOptions;
|
|
59
|
+
constructor(config) {
|
|
60
|
+
this.app = config.app;
|
|
61
|
+
this.store = config.store ?? new InMemorySessionStore();
|
|
62
|
+
this.defaultSessionOptions = config.defaultSessionOptions ?? {};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Create a new session.
|
|
66
|
+
*/
|
|
67
|
+
async create(input) {
|
|
68
|
+
const sessionId = input.sessionId ?? generateId();
|
|
69
|
+
// Check if session already exists
|
|
70
|
+
if (this.store.has(sessionId)) {
|
|
71
|
+
const existing = this.store.get(sessionId);
|
|
72
|
+
return { sessionId, session: existing };
|
|
73
|
+
}
|
|
74
|
+
// Create session from app
|
|
75
|
+
const session = this.app.createSession({
|
|
76
|
+
...this.defaultSessionOptions,
|
|
77
|
+
});
|
|
78
|
+
// Store session
|
|
79
|
+
this.store.set(sessionId, session);
|
|
80
|
+
// Queue initial messages if provided
|
|
81
|
+
if (input.messages && input.messages.length > 0) {
|
|
82
|
+
for (const msg of input.messages) {
|
|
83
|
+
session.queueMessage(msg);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { sessionId, session };
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Send to a session and wait for result.
|
|
90
|
+
*/
|
|
91
|
+
async send(sessionId, input) {
|
|
92
|
+
const session = this.store.get(sessionId);
|
|
93
|
+
if (!session) {
|
|
94
|
+
throw new SessionNotFoundError(sessionId);
|
|
95
|
+
}
|
|
96
|
+
// Queue messages if provided
|
|
97
|
+
if (input.messages && input.messages.length > 0) {
|
|
98
|
+
for (const msg of input.messages) {
|
|
99
|
+
session.queueMessage(msg);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Execute tick and await the result
|
|
103
|
+
const handle = session.tick(input.props);
|
|
104
|
+
return await handle.result;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Stream events from a session.
|
|
108
|
+
*/
|
|
109
|
+
stream(sessionId, input) {
|
|
110
|
+
const session = this.store.get(sessionId);
|
|
111
|
+
if (!session) {
|
|
112
|
+
throw new SessionNotFoundError(sessionId);
|
|
113
|
+
}
|
|
114
|
+
// Queue messages if provided
|
|
115
|
+
if (input.messages && input.messages.length > 0) {
|
|
116
|
+
for (const msg of input.messages) {
|
|
117
|
+
session.queueMessage(msg);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Execute tick and return event stream
|
|
121
|
+
const handle = session.tick(input.props);
|
|
122
|
+
// Return async iterable from handle
|
|
123
|
+
return handle;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get session by ID.
|
|
127
|
+
*/
|
|
128
|
+
getSession(sessionId) {
|
|
129
|
+
return this.store.get(sessionId);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get session state.
|
|
133
|
+
*/
|
|
134
|
+
getState(sessionId) {
|
|
135
|
+
const session = this.store.get(sessionId);
|
|
136
|
+
if (!session) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
const inspection = session.inspect();
|
|
140
|
+
return {
|
|
141
|
+
sessionId,
|
|
142
|
+
status: inspection.status,
|
|
143
|
+
tick: inspection.currentTick,
|
|
144
|
+
queuedMessages: inspection.queuedMessages.length,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Delete session.
|
|
149
|
+
*/
|
|
150
|
+
delete(sessionId) {
|
|
151
|
+
return this.store.delete(sessionId);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* List all session IDs.
|
|
155
|
+
*/
|
|
156
|
+
list() {
|
|
157
|
+
return this.store.list();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Create a session handler.
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```typescript
|
|
165
|
+
* const sessionHandler = createSessionHandler({
|
|
166
|
+
* app,
|
|
167
|
+
* store: new InMemorySessionStore(),
|
|
168
|
+
* });
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
export function createSessionHandler(config) {
|
|
172
|
+
return new SessionHandlerImpl(config);
|
|
173
|
+
}
|
|
174
|
+
// ============================================================================
|
|
175
|
+
// Errors
|
|
176
|
+
// ============================================================================
|
|
177
|
+
/**
|
|
178
|
+
* Session not found error.
|
|
179
|
+
*/
|
|
180
|
+
export class SessionNotFoundError extends Error {
|
|
181
|
+
code = "SESSION_NOT_FOUND";
|
|
182
|
+
constructor(sessionId) {
|
|
183
|
+
super(`Session not found: ${sessionId}`);
|
|
184
|
+
this.name = "SessionNotFoundError";
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Session closed error.
|
|
189
|
+
*/
|
|
190
|
+
export class SessionClosedError extends Error {
|
|
191
|
+
code = "SESSION_CLOSED";
|
|
192
|
+
constructor(sessionId) {
|
|
193
|
+
super(`Session is closed: ${sessionId}`);
|
|
194
|
+
this.name = "SessionClosedError";
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=session-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-handler.js","sourceRoot":"","sources":["../src/session-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Store implementations.
|
|
3
|
+
*
|
|
4
|
+
* @module @tentickle/server/session-store
|
|
5
|
+
*/
|
|
6
|
+
import type { Session } from "@tentickle/core/app";
|
|
7
|
+
import type { SessionStore } from "./types.js";
|
|
8
|
+
/**
|
|
9
|
+
* In-memory session store.
|
|
10
|
+
*
|
|
11
|
+
* Suitable for:
|
|
12
|
+
* - Development
|
|
13
|
+
* - Testing
|
|
14
|
+
* - Single-instance deployments
|
|
15
|
+
*
|
|
16
|
+
* NOT suitable for:
|
|
17
|
+
* - Production multi-instance deployments
|
|
18
|
+
* - Persistent sessions across restarts
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const store = new InMemorySessionStore();
|
|
23
|
+
*
|
|
24
|
+
* // Use with session handler
|
|
25
|
+
* const handler = createSessionHandler({
|
|
26
|
+
* app,
|
|
27
|
+
* store,
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare class InMemorySessionStore implements SessionStore {
|
|
32
|
+
private sessions;
|
|
33
|
+
/**
|
|
34
|
+
* Get session by ID.
|
|
35
|
+
*/
|
|
36
|
+
get(id: string): Session | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Store session.
|
|
39
|
+
*/
|
|
40
|
+
set(id: string, session: Session): void;
|
|
41
|
+
/**
|
|
42
|
+
* Delete session.
|
|
43
|
+
*/
|
|
44
|
+
delete(id: string): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* List all session IDs.
|
|
47
|
+
*/
|
|
48
|
+
list(): string[];
|
|
49
|
+
/**
|
|
50
|
+
* Check if session exists.
|
|
51
|
+
*/
|
|
52
|
+
has(id: string): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Get number of sessions.
|
|
55
|
+
*/
|
|
56
|
+
get size(): number;
|
|
57
|
+
/**
|
|
58
|
+
* Clear all sessions.
|
|
59
|
+
*/
|
|
60
|
+
clear(): void;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=session-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,oBAAqB,YAAW,YAAY;IACvD,OAAO,CAAC,QAAQ,CAA8B;IAE9C;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAIpC;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAIvC;;OAEG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAS3B;;OAEG;IACH,IAAI,IAAI,MAAM,EAAE;IAIhB;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIxB;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACH,KAAK,IAAI,IAAI;CAMd"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Store implementations.
|
|
3
|
+
*
|
|
4
|
+
* @module @tentickle/server/session-store
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* In-memory session store.
|
|
8
|
+
*
|
|
9
|
+
* Suitable for:
|
|
10
|
+
* - Development
|
|
11
|
+
* - Testing
|
|
12
|
+
* - Single-instance deployments
|
|
13
|
+
*
|
|
14
|
+
* NOT suitable for:
|
|
15
|
+
* - Production multi-instance deployments
|
|
16
|
+
* - Persistent sessions across restarts
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const store = new InMemorySessionStore();
|
|
21
|
+
*
|
|
22
|
+
* // Use with session handler
|
|
23
|
+
* const handler = createSessionHandler({
|
|
24
|
+
* app,
|
|
25
|
+
* store,
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export class InMemorySessionStore {
|
|
30
|
+
sessions = new Map();
|
|
31
|
+
/**
|
|
32
|
+
* Get session by ID.
|
|
33
|
+
*/
|
|
34
|
+
get(id) {
|
|
35
|
+
return this.sessions.get(id);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Store session.
|
|
39
|
+
*/
|
|
40
|
+
set(id, session) {
|
|
41
|
+
this.sessions.set(id, session);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Delete session.
|
|
45
|
+
*/
|
|
46
|
+
delete(id) {
|
|
47
|
+
const session = this.sessions.get(id);
|
|
48
|
+
if (session) {
|
|
49
|
+
session.close();
|
|
50
|
+
return this.sessions.delete(id);
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* List all session IDs.
|
|
56
|
+
*/
|
|
57
|
+
list() {
|
|
58
|
+
return Array.from(this.sessions.keys());
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if session exists.
|
|
62
|
+
*/
|
|
63
|
+
has(id) {
|
|
64
|
+
return this.sessions.has(id);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get number of sessions.
|
|
68
|
+
*/
|
|
69
|
+
get size() {
|
|
70
|
+
return this.sessions.size;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Clear all sessions.
|
|
74
|
+
*/
|
|
75
|
+
clear() {
|
|
76
|
+
for (const session of this.sessions.values()) {
|
|
77
|
+
session.close();
|
|
78
|
+
}
|
|
79
|
+
this.sessions.clear();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=session-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-store.js","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,OAAO,oBAAoB;IACvB,QAAQ,GAAG,IAAI,GAAG,EAAmB,CAAC;IAE9C;;OAEG;IACH,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,EAAU,EAAE,OAAgB;QAC9B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,EAAU;QACf,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,KAAK,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACH,IAAI;QACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,KAAK;QACH,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC7C,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;CACF"}
|
package/dist/sse.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE (Server-Sent Events) utilities.
|
|
3
|
+
*
|
|
4
|
+
* Provides helpers for streaming events to clients via SSE.
|
|
5
|
+
* Works with any web framework that supports writable streams.
|
|
6
|
+
*
|
|
7
|
+
* @module @agentick/server/sse
|
|
8
|
+
*/
|
|
9
|
+
import type { SSEWriter, SSEWriterOptions } from "./types";
|
|
10
|
+
/**
|
|
11
|
+
* Create an SSE writer for a response stream.
|
|
12
|
+
*
|
|
13
|
+
* Works with any writable stream that has write() and end() methods
|
|
14
|
+
* (Node.js response, Express response, etc.).
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* // Express
|
|
19
|
+
* app.get('/events', (req, res) => {
|
|
20
|
+
* setSSEHeaders(res);
|
|
21
|
+
* const writer = createSSEWriter(res);
|
|
22
|
+
*
|
|
23
|
+
* // Write events
|
|
24
|
+
* writer.writeEvent({
|
|
25
|
+
* channel: 'session:events',
|
|
26
|
+
* type: 'content_delta',
|
|
27
|
+
* payload: { delta: 'Hello' },
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* // Close when done
|
|
31
|
+
* writer.close();
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare function createSSEWriter(stream: {
|
|
36
|
+
write: (data: string) => void;
|
|
37
|
+
end: () => void;
|
|
38
|
+
}, options?: SSEWriterOptions): SSEWriter;
|
|
39
|
+
/**
|
|
40
|
+
* Stream an async iterable to SSE.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* app.get('/sessions/:id/stream', async (req, res) => {
|
|
45
|
+
* setSSEHeaders(res);
|
|
46
|
+
*
|
|
47
|
+
* const stream = sessionHandler.stream(req.params.id, {});
|
|
48
|
+
* await streamToSSE(res, stream, 'session:events');
|
|
49
|
+
* });
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare function streamToSSE<T>(stream: {
|
|
53
|
+
write: (data: string) => void;
|
|
54
|
+
end: () => void;
|
|
55
|
+
}, events: AsyncIterable<T>, channel: string, options?: SSEWriterOptions): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Set SSE headers on a response.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* app.get('/events', (req, res) => {
|
|
62
|
+
* setSSEHeaders(res);
|
|
63
|
+
* // ... write events
|
|
64
|
+
* });
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function setSSEHeaders(res: {
|
|
68
|
+
setHeader: (name: string, value: string) => void;
|
|
69
|
+
flushHeaders?: () => void;
|
|
70
|
+
}): void;
|
|
71
|
+
//# sourceMappingURL=sse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE;IAAE,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,IAAI,CAAA;CAAE,EAC1D,OAAO,GAAE,gBAAqB,GAC7B,SAAS,CAyDX;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAAC,CAAC,EACjC,MAAM,EAAE;IAAE,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,IAAI,CAAA;CAAE,EAC1D,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EACxB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAcf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE;IACjC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B,GAAG,IAAI,CAWP"}
|