poe-code 3.0.433 → 3.0.434

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-code",
3
- "version": "3.0.433",
3
+ "version": "3.0.434",
4
4
  "description": "CLI tool to configure Poe API for developer workflows.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -219,6 +219,18 @@ export async function authorizeBearerRequest(req, options) {
219
219
  authorizationServers: options.authorizationServers.map(toUrlString),
220
220
  requiredScopes,
221
221
  });
222
+ const verifiedScopes = new Set(verifiedToken.scopes);
223
+ if (requiredScopes.some((scope) => !verifiedScopes.has(scope))) {
224
+ return {
225
+ ok: false,
226
+ statusCode: 403,
227
+ challenge: createBearerChallenge(req, {
228
+ error: "insufficient_scope",
229
+ errorDescription: "insufficient scope",
230
+ scope: requiredScopes,
231
+ }, options.protectedResourcePath, options.trustedProxy),
232
+ };
233
+ }
222
234
  const auth = toRequestAuthInfo(verifiedToken, toUrlString(options.resource));
223
235
  req.auth = auth;
224
236
  return {
@@ -62,6 +62,10 @@ export function createExpressOAuthHandlers(options) {
62
62
  path,
63
63
  }),
64
64
  mcpMiddleware: async (req, res, next) => {
65
+ if (req.method === "OPTIONS") {
66
+ await mcpMiddleware(req, res, next);
67
+ return;
68
+ }
65
69
  const authorization = await authorizeBearerRequest(req, {
66
70
  ...options.oauth,
67
71
  protectedResourcePath: path,
@@ -106,7 +106,7 @@ export function createHttpServer(options) {
106
106
  request: {}
107
107
  };
108
108
  const authorizeHttpRequest = async (req, res, protectedResourcePath) => {
109
- if (options.oauth === undefined) {
109
+ if (options.oauth === undefined || req.method === "OPTIONS") {
110
110
  return true;
111
111
  }
112
112
  const authenticatedRequest = req;
@@ -99,6 +99,7 @@ export declare class StreamableHttpTransport {
99
99
  private readonly trustedProxy;
100
100
  private readonly sessionMessages;
101
101
  private readonly sseStreams;
102
+ private readonly sseExpiryTimers;
102
103
  private readonly sseEventHistory;
103
104
  private readonly responseRequestIds;
104
105
  private readonly responseOrigins;
@@ -130,6 +131,8 @@ export declare class StreamableHttpTransport {
130
131
  private createLocalMessageSession;
131
132
  private ensureLocalMessageSession;
132
133
  private closeStreamsForSession;
134
+ private scheduleSseExpiry;
135
+ private clearSseExpiryTimer;
133
136
  private startSseKeepAlive;
134
137
  private stopSseKeepAliveIfIdle;
135
138
  private stopSseKeepAlive;
@@ -39,6 +39,7 @@ export class StreamableHttpTransport {
39
39
  trustedProxy;
40
40
  sessionMessages = new Map();
41
41
  sseStreams = new Map();
42
+ sseExpiryTimers = new Map();
42
43
  sseEventHistory = new Map();
43
44
  responseRequestIds = new WeakMap();
44
45
  responseOrigins = new WeakMap();
@@ -165,6 +166,10 @@ export class StreamableHttpTransport {
165
166
  this.sessionExpiryInterval = undefined;
166
167
  }
167
168
  this.stopSseKeepAlive();
169
+ for (const timer of this.sseExpiryTimers.values()) {
170
+ clearTimeout(timer);
171
+ }
172
+ this.sseExpiryTimers.clear();
168
173
  for (const sessionId of [...this.sseStreams.keys()]) {
169
174
  this.closeStreamsForSession(sessionId);
170
175
  }
@@ -411,6 +416,7 @@ export class StreamableHttpTransport {
411
416
  streamCount: streams.size
412
417
  });
413
418
  const cleanup = () => {
419
+ this.clearSseExpiryTimer(res);
414
420
  const activeStreams = this.sseStreams.get(sessionId);
415
421
  if (activeStreams === undefined) {
416
422
  return;
@@ -431,6 +437,10 @@ export class StreamableHttpTransport {
431
437
  req.on("close", cleanup);
432
438
  res.on("close", cleanup);
433
439
  res.on("finish", cleanup);
440
+ const expiresAt = req.auth?.expiresAt;
441
+ if (expiresAt !== undefined) {
442
+ this.scheduleSseExpiry(res, expiresAt);
443
+ }
434
444
  res.writeHead(200, this.withSessionHeader(SSE_HEADERS, sessionId, res));
435
445
  this.replaySseEvents(req, res, sessionId);
436
446
  res.flushHeaders();
@@ -605,6 +615,7 @@ export class StreamableHttpTransport {
605
615
  return;
606
616
  }
607
617
  for (const response of streams) {
618
+ this.clearSseExpiryTimer(response);
608
619
  if (!response.writableEnded) {
609
620
  response.end();
610
621
  }
@@ -612,6 +623,25 @@ export class StreamableHttpTransport {
612
623
  this.sseStreams.delete(sessionId);
613
624
  this.stopSseKeepAliveIfIdle();
614
625
  }
626
+ scheduleSseExpiry(response, expiresAt) {
627
+ const remainingMs = Math.max(0, expiresAt * 1_000 - Date.now());
628
+ const timer = setTimeout(() => {
629
+ this.sseExpiryTimers.delete(response);
630
+ if (!response.writableEnded) {
631
+ response.end();
632
+ }
633
+ }, remainingMs);
634
+ timer.unref();
635
+ this.sseExpiryTimers.set(response, timer);
636
+ }
637
+ clearSseExpiryTimer(response) {
638
+ const timer = this.sseExpiryTimers.get(response);
639
+ if (timer === undefined) {
640
+ return;
641
+ }
642
+ clearTimeout(timer);
643
+ this.sseExpiryTimers.delete(response);
644
+ }
615
645
  startSseKeepAlive() {
616
646
  if (this.sseKeepAliveMs === 0 || this.sseKeepAliveInterval !== undefined) {
617
647
  return;