poe-code 3.0.437 → 3.0.439

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.437",
3
+ "version": "3.0.439",
4
4
  "description": "CLI tool to configure Poe API for developer workflows.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -12,6 +12,8 @@ interface RunCliDependencies {
12
12
  stdout?: Pick<NodeJS.WriteStream, "write">;
13
13
  stderr?: Pick<NodeJS.WriteStream, "write">;
14
14
  waitForShutdown?: (shutdown: () => Promise<void>) => Promise<void>;
15
+ listenForShutdownSignals?: (listener: () => void) => () => void;
16
+ scheduleShutdownGrace?: (listener: () => void, graceMs: number) => () => void;
15
17
  }
16
18
  export declare function isCliInvocation(argv: string[], moduleUrl: string, realpath?: (path: string) => string): boolean;
17
19
  export declare function runCli(args?: string[], dependencies?: RunCliDependencies): Promise<number>;
@@ -57,6 +57,8 @@ const HELP_TEXT = [
57
57
  " Node HTTP headers timeout",
58
58
  " --keep-alive-timeout-ms <ms>",
59
59
  " Node HTTP keep-alive timeout",
60
+ " --shutdown-grace-ms <ms>",
61
+ " Grace period before force-closing connections (default: 10000)",
60
62
  " --oauth-resource <uri> Enable OAuth mode with this canonical resource URI",
61
63
  " --oauth-authorization-server <issuer>",
62
64
  " Authorization server issuer URL (repeatable)",
@@ -70,6 +72,7 @@ const HELP_TEXT = [
70
72
  " Module that exports the TokenVerifier implementation",
71
73
  " --oauth-verifier-export <name>",
72
74
  " Export name to load from the verifier module (default: default)",
75
+ " --version Show the package version",
73
76
  " -h, --help Show this help message"
74
77
  ].join("\n");
75
78
  function parsePort(value) {
@@ -206,6 +209,7 @@ function parseCliOptions(args) {
206
209
  "request-timeout-ms": { type: "string" },
207
210
  "headers-timeout-ms": { type: "string" },
208
211
  "keep-alive-timeout-ms": { type: "string" },
212
+ "shutdown-grace-ms": { type: "string" },
209
213
  "oauth-resource": { type: "string" },
210
214
  "oauth-authorization-server": { type: "string", multiple: true },
211
215
  "oauth-supported-scope": { type: "string", multiple: true },
@@ -213,7 +217,8 @@ function parseCliOptions(args) {
213
217
  "oauth-bearer-method": { type: "string", multiple: true },
214
218
  "oauth-verifier-module": { type: "string" },
215
219
  "oauth-verifier-export": { type: "string" },
216
- help: { type: "boolean", short: "h" }
220
+ help: { type: "boolean", short: "h" },
221
+ version: { type: "boolean" }
217
222
  }
218
223
  });
219
224
  const maxRequestBytes = parseOptionalInteger(values["max-request-bytes"], "--max-request-bytes", 1);
@@ -228,8 +233,10 @@ function parseCliOptions(args) {
228
233
  const requestTimeoutMs = parseOptionalInteger(values["request-timeout-ms"], "--request-timeout-ms", 0);
229
234
  const headersTimeoutMs = parseOptionalInteger(values["headers-timeout-ms"], "--headers-timeout-ms", 0);
230
235
  const keepAliveTimeoutMs = parseOptionalInteger(values["keep-alive-timeout-ms"], "--keep-alive-timeout-ms", 0);
236
+ const shutdownGraceMs = parseOptionalInteger(values["shutdown-grace-ms"], "--shutdown-grace-ms", 0) ?? 10_000;
231
237
  return {
232
238
  help: values.help ?? false,
239
+ version: values.version ?? false,
233
240
  port: parsePort(values.port),
234
241
  hostname: values.hostname ?? "127.0.0.1",
235
242
  path: values.path ?? "/mcp",
@@ -256,18 +263,67 @@ function parseCliOptions(args) {
256
263
  ...(requestTimeoutMs === undefined ? {} : { requestTimeoutMs }),
257
264
  ...(headersTimeoutMs === undefined ? {} : { headersTimeoutMs }),
258
265
  ...(keepAliveTimeoutMs === undefined ? {} : { keepAliveTimeoutMs }),
266
+ shutdownGraceMs,
259
267
  oauth: parseCliOAuthOptions(values)
260
268
  };
261
269
  }
262
- function waitForShutdown(shutdown) {
270
+ function listenForShutdownSignals(listener) {
271
+ process.on("SIGINT", listener);
272
+ process.on("SIGTERM", listener);
273
+ return () => {
274
+ process.off("SIGINT", listener);
275
+ process.off("SIGTERM", listener);
276
+ };
277
+ }
278
+ function scheduleShutdownGrace(listener, graceMs) {
279
+ const timer = setTimeout(listener, graceMs);
280
+ return () => clearTimeout(timer);
281
+ }
282
+ function waitForShutdown(shutdown, forceShutdown, graceMs, listenForSignals, scheduleGrace) {
263
283
  return new Promise((resolve, reject) => {
284
+ let shutdownStarted = false;
285
+ let settled = false;
286
+ let cancelGrace = () => undefined;
287
+ let removeSignalListeners = () => undefined;
288
+ const finish = (forced) => {
289
+ if (settled) {
290
+ return;
291
+ }
292
+ settled = true;
293
+ cancelGrace();
294
+ removeSignalListeners();
295
+ resolve(forced);
296
+ };
297
+ const force = () => {
298
+ if (settled) {
299
+ return;
300
+ }
301
+ try {
302
+ forceShutdown();
303
+ finish(true);
304
+ }
305
+ catch {
306
+ finish(true);
307
+ }
308
+ };
264
309
  const onSignal = () => {
265
- process.off("SIGINT", onSignal);
266
- process.off("SIGTERM", onSignal);
267
- void shutdown().then(resolve, reject);
310
+ if (shutdownStarted) {
311
+ force();
312
+ return;
313
+ }
314
+ shutdownStarted = true;
315
+ cancelGrace = scheduleGrace(force, graceMs);
316
+ void shutdown().then(() => finish(false), (error) => {
317
+ if (settled) {
318
+ return;
319
+ }
320
+ settled = true;
321
+ cancelGrace();
322
+ removeSignalListeners();
323
+ reject(error);
324
+ });
268
325
  };
269
- process.once("SIGINT", onSignal);
270
- process.once("SIGTERM", onSignal);
326
+ removeSignalListeners = listenForSignals(onSignal);
271
327
  });
272
328
  }
273
329
  export function isCliInvocation(argv, moduleUrl, realpath = realpathSync) {
@@ -290,13 +346,28 @@ export async function runCli(args = process.argv.slice(2), dependencies = {}) {
290
346
  const stdout = dependencies.stdout ?? process.stdout;
291
347
  const stderr = dependencies.stderr ?? process.stderr;
292
348
  const customWaitForShutdown = dependencies.waitForShutdown;
349
+ const listenForSignals = dependencies.listenForShutdownSignals ?? listenForShutdownSignals;
350
+ const scheduleGrace = dependencies.scheduleShutdownGrace ?? scheduleShutdownGrace;
293
351
  let handle;
352
+ let shutdownStarted = false;
353
+ let options;
354
+ try {
355
+ options = parseCliOptions(args);
356
+ }
357
+ catch (error) {
358
+ const message = error instanceof Error ? error.message : String(error);
359
+ stderr.write(`${message}\nRun with --help for usage.\n`);
360
+ return 1;
361
+ }
294
362
  try {
295
- const options = parseCliOptions(args);
296
363
  if (options.help) {
297
364
  stdout.write(`${HELP_TEXT}\n`);
298
365
  return 0;
299
366
  }
367
+ if (options.version) {
368
+ stdout.write(`${packageInfo.version}\n`);
369
+ return 0;
370
+ }
300
371
  const oauth = options.oauth === undefined
301
372
  ? undefined
302
373
  : {
@@ -360,12 +431,19 @@ export async function runCli(args = process.argv.slice(2), dependencies = {}) {
360
431
  : { keepAliveTimeoutMs: options.keepAliveTimeoutMs })
361
432
  });
362
433
  const shutdown = async () => {
434
+ shutdownStarted = true;
363
435
  await handle?.close();
364
436
  };
365
- const shutdownPromise = customWaitForShutdown === undefined ? waitForShutdown(shutdown) : undefined;
437
+ const forceShutdown = () => {
438
+ handle?.closeAllConnections();
439
+ };
440
+ const shutdownPromise = customWaitForShutdown === undefined
441
+ ? waitForShutdown(shutdown, forceShutdown, options.shutdownGraceMs, listenForSignals, scheduleGrace)
442
+ : undefined;
366
443
  stdout.write(`${handle.url}\n`);
367
444
  if (customWaitForShutdown === undefined) {
368
- await shutdownPromise;
445
+ const forced = await shutdownPromise;
446
+ return forced ? 1 : 0;
369
447
  }
370
448
  else {
371
449
  await customWaitForShutdown(shutdown);
@@ -375,7 +453,12 @@ export async function runCli(args = process.argv.slice(2), dependencies = {}) {
375
453
  catch (error) {
376
454
  if (handle !== undefined) {
377
455
  try {
378
- await handle.close();
456
+ if (shutdownStarted) {
457
+ handle.closeAllConnections();
458
+ }
459
+ else {
460
+ await handle.close();
461
+ }
379
462
  }
380
463
  catch {
381
464
  // Preserve the original CLI failure below.
@@ -28,6 +28,7 @@ export interface HttpServerHandle {
28
28
  url: string;
29
29
  port: number;
30
30
  close(): Promise<void>;
31
+ closeAllConnections(): void;
31
32
  }
32
33
  export interface HttpServer extends Omit<Server, "tool" | "registerTool"> {
33
34
  tool<TIn, TOut = never>(name: string, description: string, inputSchema: TypedSchema<TIn>, handler: HttpToolHandler<TIn, TOut>, outputSchema?: TypedSchema<TOut>): HttpServer;
@@ -37,6 +38,8 @@ export interface HttpServer extends Omit<Server, "tool" | "registerTool"> {
37
38
  }
38
39
  export interface HttpToolContext {
39
40
  request: AuthenticatedIncomingMessage;
41
+ sessionId?: string;
42
+ auth?: RequestAuthInfo;
40
43
  }
41
44
  export type HttpToolHandler<T = Record<string, unknown>, TOut = ToolReturn> = (args: T, context: HttpToolContext) => Promise<TOut | CallToolResult> | TOut | CallToolResult;
42
45
  export declare function createProtectedResourceMetadataDocument(options: ProtectedResourceMetadataOptions): Record<string, unknown>;
@@ -94,7 +94,11 @@ export function createHttpServer(options) {
94
94
  supportResourceSubscriptions: supportsSessions
95
95
  });
96
96
  const transport = new StreamableHttpTransport(server, options, async (req, callback) => requestContextStorage.run({
97
- request: req
97
+ request: req,
98
+ sessionId: Array.isArray(req.headers["mcp-session-id"])
99
+ ? req.headers["mcp-session-id"][0]
100
+ : req.headers["mcp-session-id"],
101
+ auth: req.auth
98
102
  }, callback));
99
103
  const protectedResourceMetadataBody = options.oauth === undefined
100
104
  ? undefined
@@ -103,7 +107,7 @@ export function createHttpServer(options) {
103
107
  const registerTool = server.tool.bind(server);
104
108
  const registerRichTool = server.registerTool.bind(server);
105
109
  const defaultContext = {
106
- request: {}
110
+ request: { headers: {}, socket: {} }
107
111
  };
108
112
  const authorizeHttpRequest = async (req, res, protectedResourcePath) => {
109
113
  if (options.oauth === undefined || req.method === "OPTIONS") {
@@ -238,7 +242,10 @@ export function createHttpServer(options) {
238
242
  return {
239
243
  url: buildUrl(hostname, resolvedPort, path),
240
244
  port: resolvedPort,
241
- close
245
+ close,
246
+ closeAllConnections: () => {
247
+ nodeServer.closeAllConnections();
248
+ }
242
249
  };
243
250
  };
244
251
  httpServer.handleRequest = async (req, res) => {