poe-code 3.0.301 → 3.0.302

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.301",
3
+ "version": "3.0.302",
4
4
  "description": "CLI tool to configure Poe API for developer workflows.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -66,6 +66,19 @@ function formatScope(scope) {
66
66
  }
67
67
  return scope.join(" ");
68
68
  }
69
+ function hasBearerTokenWhitespace(value) {
70
+ for (const character of value) {
71
+ if (character === " "
72
+ || character === "\t"
73
+ || character === "\n"
74
+ || character === "\r"
75
+ || character === "\f"
76
+ || character === "\v") {
77
+ return true;
78
+ }
79
+ }
80
+ return false;
81
+ }
69
82
  function readBearerToken(req) {
70
83
  const authorization = readSingleHeaderValue(req.headers.authorization);
71
84
  if (authorization === undefined || authorization.length === 0) {
@@ -80,7 +93,7 @@ function readBearerToken(req) {
80
93
  }
81
94
  const scheme = authorization.slice(0, separatorIndex);
82
95
  const token = authorization.slice(separatorIndex + 1).trim();
83
- if (scheme.toLowerCase() !== "bearer" || token.length === 0 || token.includes(" ")) {
96
+ if (scheme.toLowerCase() !== "bearer" || token.length === 0 || hasBearerTokenWhitespace(token)) {
84
97
  return {
85
98
  kind: "malformed",
86
99
  errorDescription: "malformed bearer token",
@@ -153,7 +166,14 @@ function toRequestAuthInfo(verifiedToken, resource) {
153
166
  };
154
167
  }
155
168
  export function getProtectedResourceMetadataUrl(req, protectedResourcePath, trustedProxy = false) {
156
- return new URL(`${PROTECTED_RESOURCE_METADATA_PATH}${normalizeProtectedResourcePath(protectedResourcePath)}`, `${getRequestProtocol(req, trustedProxy)}://${getRequestHost(req, trustedProxy)}`).toString();
169
+ const path = `${PROTECTED_RESOURCE_METADATA_PATH}${normalizeProtectedResourcePath(protectedResourcePath)}`;
170
+ const protocol = getRequestProtocol(req, trustedProxy);
171
+ try {
172
+ return new URL(path, `${protocol}://${getRequestHost(req, trustedProxy)}`).toString();
173
+ }
174
+ catch {
175
+ return new URL(path, `${protocol}://127.0.0.1`).toString();
176
+ }
157
177
  }
158
178
  export function createBearerChallenge(req, options = {}, protectedResourcePath, trustedProxy = false) {
159
179
  const parts = [
@@ -73,7 +73,7 @@ function parsePort(value) {
73
73
  if (value === undefined) {
74
74
  return 3000;
75
75
  }
76
- const port = Number(value);
76
+ const port = parseDecimalInteger(value, "--port");
77
77
  if (!Number.isInteger(port) || port < 0 || port > 65535) {
78
78
  throw new Error("--port must be an integer between 0 and 65535.");
79
79
  }
@@ -95,11 +95,24 @@ function parseOrigin(value, flagName) {
95
95
  throw new Error(`${flagName} must be an absolute URL.`);
96
96
  }
97
97
  }
98
+ function parseDecimalInteger(value, flagName) {
99
+ const trimmed = value.trim();
100
+ if (trimmed.length === 0) {
101
+ throw new Error(`${flagName} must be an integer.`);
102
+ }
103
+ for (const character of trimmed) {
104
+ const codePoint = character.codePointAt(0);
105
+ if (codePoint === undefined || codePoint < 48 || codePoint > 57) {
106
+ throw new Error(`${flagName} must be an integer.`);
107
+ }
108
+ }
109
+ return Number(trimmed);
110
+ }
98
111
  function parseOptionalInteger(value, flagName, minimum) {
99
112
  if (value === undefined) {
100
113
  return undefined;
101
114
  }
102
- const parsed = Number(value);
115
+ const parsed = parseDecimalInteger(value, flagName);
103
116
  if (!Number.isInteger(parsed) || parsed < minimum) {
104
117
  throw new Error(`${flagName} must be an integer greater than or equal to ${minimum}.`);
105
118
  }
@@ -116,6 +129,23 @@ function hasConfiguredOAuthFlag(values) {
116
129
  values["oauth-verifier-export"],
117
130
  ].some((value) => value !== undefined);
118
131
  }
132
+ function parseRepeatableStrings(value, flagName) {
133
+ if (!Array.isArray(value) || value.length === 0) {
134
+ return undefined;
135
+ }
136
+ const normalized = [];
137
+ for (const item of value) {
138
+ if (typeof item !== "string") {
139
+ throw new Error(`${flagName} must be provided as a string.`);
140
+ }
141
+ const trimmed = item.trim();
142
+ if (trimmed.length === 0) {
143
+ throw new Error(`${flagName} must not be blank.`);
144
+ }
145
+ normalized.push(trimmed);
146
+ }
147
+ return normalized;
148
+ }
119
149
  function parseCliOAuthOptions(values) {
120
150
  const resource = values["oauth-resource"];
121
151
  const authorizationServers = values["oauth-authorization-server"];
@@ -134,21 +164,15 @@ function parseCliOAuthOptions(values) {
134
164
  if (typeof verifierModule !== "string" || verifierModule.length === 0) {
135
165
  throw new Error("--oauth-verifier-module is required when --oauth-resource is set.");
136
166
  }
137
- const supportedScopes = values["oauth-supported-scope"];
138
- const requiredScopes = values["oauth-required-scope"];
139
- const bearerMethods = values["oauth-bearer-method"];
167
+ const supportedScopes = parseRepeatableStrings(values["oauth-supported-scope"], "--oauth-supported-scope");
168
+ const requiredScopes = parseRepeatableStrings(values["oauth-required-scope"], "--oauth-required-scope");
169
+ const bearerMethods = parseRepeatableStrings(values["oauth-bearer-method"], "--oauth-bearer-method");
140
170
  return {
141
171
  resource: parseAbsoluteUrl(resource, "--oauth-resource"),
142
172
  authorizationServers: authorizationServers.map((value) => parseAbsoluteUrl(value, "--oauth-authorization-server")),
143
- ...(Array.isArray(requiredScopes) && requiredScopes.length > 0
144
- ? { requiredScopes: [...requiredScopes] }
145
- : {}),
146
- ...(Array.isArray(supportedScopes) && supportedScopes.length > 0
147
- ? { scopesSupported: [...supportedScopes] }
148
- : {}),
149
- ...(Array.isArray(bearerMethods) && bearerMethods.length > 0
150
- ? { bearerMethodsSupported: [...bearerMethods] }
151
- : {}),
173
+ ...(requiredScopes === undefined ? {} : { requiredScopes }),
174
+ ...(supportedScopes === undefined ? {} : { scopesSupported: supportedScopes }),
175
+ ...(bearerMethods === undefined ? {} : { bearerMethodsSupported: bearerMethods }),
152
176
  verifierModule,
153
177
  verifierExport: typeof verifierExport === "string" && verifierExport.length > 0
154
178
  ? verifierExport
@@ -134,6 +134,7 @@ export declare class StreamableHttpTransport {
134
134
  private acceptsHost;
135
135
  private normalizeHost;
136
136
  private acceptsConfiguredResponse;
137
+ private acceptsResponseType;
137
138
  private isValidNewSessionId;
138
139
  private isRequest;
139
140
  private isToolCallOk;
@@ -8,6 +8,15 @@ const ALLOWED_METHODS = "POST, GET, DELETE, OPTIONS";
8
8
  const MCP_SESSION_ID_HEADER = "Mcp-Session-Id";
9
9
  const LOCAL_HOSTS = ["localhost", "127.0.0.1", "::1"];
10
10
  const DEFAULT_ALLOWED_HEADERS = "Accept, Authorization, Content-Type, Mcp-Session-Id, MCP-Protocol-Version";
11
+ function validateOptionalIntegerOption(name, value, minimum) {
12
+ if (value === undefined) {
13
+ return undefined;
14
+ }
15
+ if (!Number.isInteger(value) || value < minimum) {
16
+ throw new Error(`${name} must be an integer greater than or equal to ${minimum}.`);
17
+ }
18
+ return value;
19
+ }
11
20
  export class StreamableHttpTransport {
12
21
  server;
13
22
  runWithRequestContext;
@@ -44,13 +53,14 @@ export class StreamableHttpTransport {
44
53
  this.enableJsonResponse = options.enableJsonResponse ?? false;
45
54
  this.allowedOrigins = new Set(options.allowedOrigins ?? []);
46
55
  this.allowedHosts = new Set((options.allowedHosts ?? LOCAL_HOSTS).map((host) => this.normalizeHost(host)));
47
- this.maxRequestBytes = options.maxRequestBytes;
48
- this.maxBatchSize = options.maxBatchSize;
49
- this.maxSessions = options.maxSessions;
50
- this.sessionTtlMs = options.sessionTtlMs;
51
- this.maxStreamsPerSession = options.maxStreamsPerSession;
52
- this.maxSseEventHistory = options.maxSseEventHistory ?? 100;
53
- this.maxConcurrentToolCalls = options.maxConcurrentToolCalls;
56
+ this.maxRequestBytes = validateOptionalIntegerOption("maxRequestBytes", options.maxRequestBytes, 1);
57
+ this.maxBatchSize = validateOptionalIntegerOption("maxBatchSize", options.maxBatchSize, 1);
58
+ this.maxSessions = validateOptionalIntegerOption("maxSessions", options.maxSessions, 1);
59
+ this.sessionTtlMs = validateOptionalIntegerOption("sessionTtlMs", options.sessionTtlMs, 1);
60
+ this.maxStreamsPerSession = validateOptionalIntegerOption("maxStreamsPerSession", options.maxStreamsPerSession, 1);
61
+ this.maxSseEventHistory =
62
+ validateOptionalIntegerOption("maxSseEventHistory", options.maxSseEventHistory, 0) ?? 100;
63
+ this.maxConcurrentToolCalls = validateOptionalIntegerOption("maxConcurrentToolCalls", options.maxConcurrentToolCalls, 1);
54
64
  this.sessionStore = options.sessionStore ?? createSessionStore();
55
65
  this.requestIdGenerator =
56
66
  options.requestIdGenerator ?? (() => `req-${this.nextRequestId++}`);
@@ -328,6 +338,10 @@ export class StreamableHttpTransport {
328
338
  });
329
339
  return;
330
340
  }
341
+ if (!this.acceptsResponseType(req, "text/event-stream")) {
342
+ this.respondWithStatus(res, 406);
343
+ return;
344
+ }
331
345
  const sessionId = this.readSessionId(req);
332
346
  if (sessionId === undefined) {
333
347
  this.respondWithStatus(res, 400);
@@ -673,11 +687,14 @@ export class StreamableHttpTransport {
673
687
  : normalized;
674
688
  }
675
689
  acceptsConfiguredResponse(req) {
690
+ const expectedType = this.enableJsonResponse ? "application/json" : "text/event-stream";
691
+ return this.acceptsResponseType(req, expectedType);
692
+ }
693
+ acceptsResponseType(req, expectedType) {
676
694
  const accept = req.headers.accept;
677
695
  if (accept === undefined) {
678
696
  return true;
679
697
  }
680
- const expectedType = this.enableJsonResponse ? "application/json" : "text/event-stream";
681
698
  const value = Array.isArray(accept) ? accept.join(",") : accept;
682
699
  return value
683
700
  .split(",")