@mercury-fw/channel-http 0.4.0 → 0.5.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @mercury-fw/channel-http
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 0739f77: A plugin acts as the person Mercury is talking to, who logs in to the service through Mercury.
8
+
9
+ - Plugin contract version 4: a plugin declares `actsAs: "person" | "mercury"`, and one acting as the person contributes a `login`; its tools get the turn's `person` and `requireLogin` in their context.
10
+ - A person is offered only the plugins acting as the person: their prompt fragments, skills and tools. A plugin acting as Mercury, or declaring nothing, is offered on the terminal only until people can be allowed to make Mercury act as itself; the startup log names it.
11
+ - `createCliTool` adds `--user` with the person's id to every command, staged confirmations included, and when the CLI exits with code 3 (not logged in) returns their login instead. `createCliPersonLogin` builds a plugin's `login` on a CLI's two-step remote login. A failed `runCli` carries `exitCode`.
12
+ - Channel contract version 4: channels get `logins` (`accept` a callback URL, `complete` a login) and `detectLoginRequired`.
13
+ - The HTTP channel, with `HTTP_SURFACE_PUBLIC_URL` set, streams a `login` event with the link to open and takes the person back at `GET /login/callback`, public and protected by the single-use state.
14
+ - `mercury.cliCredentials` can declare `userSetup`, run by `mfw credentials setup <plugin> --user-app`: the app people log in through, set up without logging anyone in.
15
+ - plugin-jira and plugin-bitbucket act as the person, on CLIs 2.3.0: set up their people's app with `--user-app` and register `<HTTP_SURFACE_PUBLIC_URL>/login/callback` on it. Jira's skill now says `currentUser()` is the person.
16
+ - plugin-atlassian-admin acts as Mercury, on CLI 0.2.0, whose `init` asks for the key without echoing it: until permissions exist it's available on the terminal only.
17
+ - A third-party plugin needs `apiVersion: 4` to load, and `actsAs: "person"` with a `login` to be offered to people.
18
+
3
19
  ## 0.4.0
4
20
 
5
21
  ### Minor Changes
package/README.md CHANGED
@@ -20,12 +20,13 @@ It needs an auth provider next to it: [`@mercury-fw/auth-oidc`](../../auth/auth-
20
20
  |---|---|
21
21
  | `HTTP_SURFACE_PORT` | The port (default `4100`). |
22
22
  | `HTTP_SURFACE_CORS_ORIGIN` | The origin a browser UI calls from (default `*`). |
23
+ | `HTTP_SURFACE_PUBLIC_URL` | The address people's browsers reach the surface at (e.g. `https://mercury.example.com`). With it, people log in here to the services plugins act on as them (see [`GET /login/callback`](#get-logincallback)); without it, nobody can. |
23
24
 
24
25
  ## API
25
26
 
26
27
  It's active whenever it's declared in `mercury.config.ts`'s `channels` with an auth provider; remove it there to turn the surface off. It listens on `HTTP_SURFACE_PORT` (default `4100`). Base URL `http://<host>:<port>`.
27
28
 
28
- **Authentication.** Every route except `GET /openapi.yaml` and the `OPTIONS` preflights needs `Authorization: Bearer <token>`, and the auth provider decides who the token belongs to. A missing or refused token gets `401` with `WWW-Authenticate: Bearer` before anything runs.
29
+ **Authentication.** Every route except `GET /openapi.yaml`, `GET /login/callback` and the `OPTIONS` preflights needs `Authorization: Bearer <token>`, and the auth provider decides who the token belongs to. A missing or refused token gets `401` with `WWW-Authenticate: Bearer` before anything runs.
29
30
 
30
31
  **Conversations belong to whoever opened them.** The session key is `<caller id>:<conversationId>`, so the same `conversationId` sent by someone else is a conversation of their own, and a confirmation token staged in your conversation can't be confirmed from theirs. Each authenticated caller also gets their own episodic memory and wiki area, as on Google Chat.
31
32
 
@@ -60,6 +61,7 @@ Reasoning and answer text arrive as **incremental deltas** — never one finishe
60
61
  | `tool_finish` | `{ toolCallId, outcome }` | a tool call settles (`outcome`: `success` \| `failed` \| `pending`) |
61
62
  | `text` | `{ chunk }` | an answer-text delta |
62
63
  | `pending` | `{ command, token }` | a confirm-required action was staged; send `token` back to confirm it — as a later `/turn` `text`, or via `POST /confirm` |
64
+ | `login` | `{ service, url }` | the caller isn't logged in to `service` yet, which a plugin acts on as them: show them `url` to open (the model never gets it) |
63
65
  | `final` | `{ text }` | the complete answer (also emitted for a token confirmation, with no model turn) |
64
66
  | `error` | `{ message }` | the turn failed mid-stream |
65
67
 
@@ -70,6 +72,12 @@ curl -N -X POST http://localhost:4100/turn \
70
72
  -d '{"text":"Quante issue nel progetto KAN?","conversationId":"c1"}'
71
73
  ```
72
74
 
75
+ ## `GET /login/callback`
76
+
77
+ Where a person comes back after logging in to a service a plugin acts on as them (Jira, Bitbucket): the `login` event's `url` sends them to the service, and the service sends their browser here with `code` and `state`. Mounted only with `HTTP_SURFACE_PUBLIC_URL`, at `<HTTP_SURFACE_PUBLIC_URL>/login/callback`, which is the URL to register as a callback on the service's app (the Atlassian 3LO app, the Bitbucket consumer people log in through). No bearer token: the browser has none here, and the `state` Mercury issued for that person, single-use and valid for ten minutes, is what ties the request to their login. Answers with a plain-text page saying how it went.
78
+
79
+ Whoever opens the link and accepts decides which account gets linked: a link belongs to the person it was shown to, and someone else accepting it would link their own account to that person's Mercury identity. A UI should say so next to the link. Checking the linked account against the person is [#179](https://github.com/lucabro81/mercury-fw/issues/179).
80
+
73
81
  ## `POST /confirm`
74
82
 
75
83
  Explicit alternative to re-sending a token as `/turn` `text`. Body `{ token, conversationId }`; returns `{ ok: true, resolved: true, text }` when the token was a pending confirmation, `{ ok: true, resolved: false }` otherwise. `400` if `token` or `conversationId` is missing, or the id has other characters than `/turn` accepts. Never invokes the model.
@@ -10,7 +10,7 @@
10
10
  * (that stays with Google Chat). `stop` closes the socket on shutdown. Confirm is
11
11
  * injected (`confirm`/`resolveConfirmation`), so this package never imports the app.
12
12
  */
13
- import { type ConfirmFn, type ResolveConfirmationFn } from "./http-server.ts";
13
+ import { type CompleteLoginFn, type ConfirmFn, type ResolveConfirmationFn } from "./http-server.ts";
14
14
  import type { Authenticate, Provider, ChannelHostReads } from "@mercury-fw/channel-types";
15
15
  export type HttpProviderDeps = {
16
16
  port: number;
@@ -23,6 +23,13 @@ export type HttpProviderDeps = {
23
23
  authenticate: Authenticate;
24
24
  /** Allowed CORS origin echoed to a browser UI; defaults to `*` in the server. */
25
25
  corsOrigin?: string;
26
+ /** People's logins, when the surface has a public URL to be sent back to:
27
+ * the callback is offered (`accept`) once the server listens. */
28
+ logins?: {
29
+ callbackUrl: string;
30
+ accept: (callbackUrl: string) => void;
31
+ complete: CompleteLoginFn;
32
+ };
26
33
  };
27
34
  export declare function createHttpProvider(deps: HttpProviderDeps): Provider & {
28
35
  stop(): Promise<void>;
@@ -12,14 +12,17 @@
12
12
  * is unit-tested without standing up a socket. The read-only routes (4b) mount
13
13
  * alongside `/turn` here, driven by the injected `reads` getters.
14
14
  *
15
- * Every route but the OpenAPI document and the CORS preflights asks the
16
- * injected `authenticate` (the auth provider the app declares) who is calling
15
+ * `/login/callback` is where a person's browser comes back after logging in to
16
+ * a service a plugin acts on as them; the single-use state is its credential.
17
+ *
18
+ * Every route but the OpenAPI document, the login callback and the CORS
19
+ * preflights asks the injected `authenticate` (the auth provider the app declares) who is calling
17
20
  * before anything runs, and answers 401 when nobody is. A conversation belongs
18
21
  * to whoever opened it: the session key is `<principal.id>:<conversationId>`.
19
22
  * Confirm resolution is injected too (`confirm`/`resolveConfirmation`), so this
20
23
  * package never imports the app.
21
24
  */
22
- import { type HandleTurn, type Authenticate, type ChannelHostReads, type ConfirmOutcome, type Principal } from "@mercury-fw/channel-types";
25
+ import { type HandleTurn, type Authenticate, type ChannelHostReads, type ConfirmOutcome, type LoginOutcome, type Principal } from "@mercury-fw/channel-types";
23
26
  /** Resolves a bare confirmation token to a reply string, or `null` if the input isn't a token. Injected by the core (`ctx.confirm`). */
24
27
  export type ConfirmFn = (token: string, sessionKey: string, principal: Principal) => Promise<string | null>;
25
28
  /** The structured sibling of {@link ConfirmFn}, for the `/confirm` `resolved` flag. Injected by the core (`ctx.resolveConfirmation`). */
@@ -72,6 +75,18 @@ export type ConfirmRequestDeps = {
72
75
  * even token-shaped). Never touches the model.
73
76
  */
74
77
  export declare function handleConfirmRequest(req: Request, deps: ConfirmRequestDeps): Promise<Response>;
78
+ /** Finishes a person's login, injected by the core (`ChannelLogins.complete`). */
79
+ export type CompleteLoginFn = (state: string, code: string) => Promise<LoginOutcome>;
80
+ /**
81
+ * `GET /login/callback`: where the provider sends a person back after they log
82
+ * in, with `code` and `state` (or `error`, when they refused). Public: the
83
+ * person's browser carries no token here, and the single-use `state` the core
84
+ * issued for them is what ties the request to their login. Answers with a
85
+ * plain page telling them how it went.
86
+ */
87
+ export declare function handleLoginCallback(req: Request, deps: {
88
+ complete: CompleteLoginFn;
89
+ }): Promise<Response>;
75
90
  /** A single read route: its `GET` handler plus the shared `OPTIONS` preflight. */
76
91
  type ReadRoute = {
77
92
  GET: (req: Request) => Response | Promise<Response>;
@@ -94,6 +109,8 @@ export type HttpServerDeps = TurnRequestDeps & {
94
109
  reads?: ChannelHostReads;
95
110
  /** The `/confirm` endpoint's structured resolver, injected by the core. */
96
111
  resolveConfirmation: ResolveConfirmationFn;
112
+ /** Finishes people's logins; `/login/callback` is mounted only with it. */
113
+ completeLogin?: CompleteLoginFn;
97
114
  };
98
115
  /** Starts the HTTP surface: `POST /turn` (4a) plus the read-only routes (4b)
99
116
  * when `reads` is supplied. */
package/dist/index.d.ts CHANGED
@@ -6,11 +6,13 @@
6
6
  * is no runtime inert condition (unlike Google Chat's subscription): if it's
7
7
  * declared, it starts.
8
8
  *
9
- * `build` reads its port and CORS origin from `env` and takes the confirm
9
+ * `build` reads its port, CORS origin and public URL from `env` and takes the confirm
10
10
  * capabilities, the in-process reads and the app's auth provider from the
11
11
  * injected context (dependency inversion). Those are optional on the contract
12
12
  * but required here, so a context missing them throws — the loader isolates it
13
- * fail-soft, and without an auth provider the surface never opens.
13
+ * fail-soft, and without an auth provider the surface never opens. With a
14
+ * public URL (`HTTP_SURFACE_PUBLIC_URL`) it also takes people's logins: it
15
+ * mounts `<url>/login/callback` and, once it's listening, offers it to the core.
14
16
  */
15
17
  import type { ChannelPlugin } from "@mercury-fw/channel-types";
16
18
  export { createHttpProvider, type HttpProviderDeps } from "./http-provider.ts";
package/http-provider.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  * (that stays with Google Chat). `stop` closes the socket on shutdown. Confirm is
11
11
  * injected (`confirm`/`resolveConfirmation`), so this package never imports the app.
12
12
  */
13
- import { startHttpServer, type ConfirmFn, type ResolveConfirmationFn } from "./http-server.ts";
13
+ import { startHttpServer, type CompleteLoginFn, type ConfirmFn, type ResolveConfirmationFn } from "./http-server.ts";
14
14
  import type { Authenticate, Provider, HandleTurn, ChannelHostReads } from "@mercury-fw/channel-types";
15
15
 
16
16
  export type HttpProviderDeps = {
@@ -24,6 +24,9 @@ export type HttpProviderDeps = {
24
24
  authenticate: Authenticate;
25
25
  /** Allowed CORS origin echoed to a browser UI; defaults to `*` in the server. */
26
26
  corsOrigin?: string;
27
+ /** People's logins, when the surface has a public URL to be sent back to:
28
+ * the callback is offered (`accept`) once the server listens. */
29
+ logins?: { callbackUrl: string; accept: (callbackUrl: string) => void; complete: CompleteLoginFn };
27
30
  };
28
31
 
29
32
  export function createHttpProvider(deps: HttpProviderDeps): Provider & { stop(): Promise<void> } {
@@ -38,7 +41,9 @@ export function createHttpProvider(deps: HttpProviderDeps): Provider & { stop():
38
41
  reads: deps.reads,
39
42
  authenticate: deps.authenticate,
40
43
  corsOrigin: deps.corsOrigin,
44
+ completeLogin: deps.logins?.complete,
41
45
  });
46
+ deps.logins?.accept(deps.logins.callbackUrl);
42
47
  },
43
48
  async notify(): Promise<{ sessionKey: string }> {
44
49
  // No proactive push channel over HTTP (request/response only).
package/http-server.ts CHANGED
@@ -12,14 +12,18 @@
12
12
  * is unit-tested without standing up a socket. The read-only routes (4b) mount
13
13
  * alongside `/turn` here, driven by the injected `reads` getters.
14
14
  *
15
- * Every route but the OpenAPI document and the CORS preflights asks the
16
- * injected `authenticate` (the auth provider the app declares) who is calling
15
+ * `/login/callback` is where a person's browser comes back after logging in to
16
+ * a service a plugin acts on as them; the single-use state is its credential.
17
+ *
18
+ * Every route but the OpenAPI document, the login callback and the CORS
19
+ * preflights asks the injected `authenticate` (the auth provider the app declares) who is calling
17
20
  * before anything runs, and answers 401 when nobody is. A conversation belongs
18
21
  * to whoever opened it: the session key is `<principal.id>:<conversationId>`.
19
22
  * Confirm resolution is injected too (`confirm`/`resolveConfirmation`), so this
20
23
  * package never imports the app.
21
24
  */
22
25
  import {
26
+ detectLoginRequired,
23
27
  detectPendingConfirmation,
24
28
  PENDING_CONFIRMATION_NOTE,
25
29
  type HandleTurn,
@@ -27,6 +31,7 @@ import {
27
31
  type Authenticate,
28
32
  type ChannelHostReads,
29
33
  type ConfirmOutcome,
34
+ type LoginOutcome,
30
35
  type Principal,
31
36
  } from "@mercury-fw/channel-types";
32
37
 
@@ -188,6 +193,8 @@ export async function handleTurnRequest(req: Request, deps: TurnRequestDeps): Pr
188
193
  onStep: (step) => {
189
194
  const pending = detectPendingConfirmation(step);
190
195
  if (pending) send("pending", { command: pending.summary, token: pending.token });
196
+ const login = detectLoginRequired(step);
197
+ if (login) send("login", { service: login.service, url: login.url });
191
198
  },
192
199
  onUsage: () => {},
193
200
  finalize: async (finalText) => send("final", { text: finalText }),
@@ -276,6 +283,46 @@ export async function handleConfirmRequest(req: Request, deps: ConfirmRequestDep
276
283
  }
277
284
  }
278
285
 
286
+ /** Finishes a person's login, injected by the core (`ChannelLogins.complete`). */
287
+ export type CompleteLoginFn = (state: string, code: string) => Promise<LoginOutcome>;
288
+
289
+ /** A plain-text page for the person's browser, at the end of their login. */
290
+ function loginPage(text: string, status: number): Response {
291
+ // The URL carried the code and the state: neither kept nor passed on.
292
+ return new Response(text, {
293
+ status,
294
+ headers: { "content-type": "text/plain; charset=utf-8", "cache-control": "no-store", "referrer-policy": "no-referrer" },
295
+ });
296
+ }
297
+
298
+ /**
299
+ * `GET /login/callback`: where the provider sends a person back after they log
300
+ * in, with `code` and `state` (or `error`, when they refused). Public: the
301
+ * person's browser carries no token here, and the single-use `state` the core
302
+ * issued for them is what ties the request to their login. Answers with a
303
+ * plain page telling them how it went.
304
+ */
305
+ export async function handleLoginCallback(req: Request, deps: { complete: CompleteLoginFn }): Promise<Response> {
306
+ const params = new URL(req.url).searchParams;
307
+ const error = params.get("error");
308
+ if (error !== null) {
309
+ const reason = error.replace(/[^A-Za-z0-9_.-]/g, "").slice(0, 64);
310
+ return loginPage(`The login didn't happen (${reason}). Ask Mercury again for a new link.`, 400);
311
+ }
312
+ const code = params.get("code");
313
+ const state = params.get("state");
314
+ if (!code || !state) return loginPage("The login link came back without a code or a state. Ask Mercury again for a new link.", 400);
315
+ const outcome = await deps.complete(state, code);
316
+ return outcome.ok
317
+ ? loginPage(`You're logged in to ${outcome.service}. Go back to the conversation and ask again.`, 200)
318
+ : loginPage(outcome.error, 400);
319
+ }
320
+
321
+ /** The login callback route, mounted when the server can complete logins. */
322
+ function loginRoutes(complete: CompleteLoginFn): Record<string, { GET: (req: Request) => Promise<Response> }> {
323
+ return { "/login/callback": { GET: (req) => handleLoginCallback(req, { complete }) } };
324
+ }
325
+
279
326
  /** A single read route: its `GET` handler plus the shared `OPTIONS` preflight. */
280
327
  type ReadRoute = {
281
328
  GET: (req: Request) => Response | Promise<Response>;
@@ -363,6 +410,8 @@ export type HttpServerDeps = TurnRequestDeps & {
363
410
  reads?: ChannelHostReads;
364
411
  /** The `/confirm` endpoint's structured resolver, injected by the core. */
365
412
  resolveConfirmation: ResolveConfirmationFn;
413
+ /** Finishes people's logins; `/login/callback` is mounted only with it. */
414
+ completeLogin?: CompleteLoginFn;
366
415
  };
367
416
 
368
417
  /** Starts the HTTP surface: `POST /turn` (4a) plus the read-only routes (4b)
@@ -389,6 +438,7 @@ export function startHttpServer(deps: HttpServerDeps): ReturnType<typeof Bun.ser
389
438
  },
390
439
  "/openapi.yaml": { GET: () => openApiResponse(origin), OPTIONS: () => preflight(origin) },
391
440
  ...(deps.reads ? readRoutes(deps.reads, deps.authenticate, origin) : {}),
441
+ ...(deps.completeLogin ? loginRoutes(deps.completeLogin) : {}),
392
442
  },
393
443
  error: (err) =>
394
444
  Response.json(
package/index.ts CHANGED
@@ -6,11 +6,13 @@
6
6
  * is no runtime inert condition (unlike Google Chat's subscription): if it's
7
7
  * declared, it starts.
8
8
  *
9
- * `build` reads its port and CORS origin from `env` and takes the confirm
9
+ * `build` reads its port, CORS origin and public URL from `env` and takes the confirm
10
10
  * capabilities, the in-process reads and the app's auth provider from the
11
11
  * injected context (dependency inversion). Those are optional on the contract
12
12
  * but required here, so a context missing them throws — the loader isolates it
13
- * fail-soft, and without an auth provider the surface never opens.
13
+ * fail-soft, and without an auth provider the surface never opens. With a
14
+ * public URL (`HTTP_SURFACE_PUBLIC_URL`) it also takes people's logins: it
15
+ * mounts `<url>/login/callback` and, once it's listening, offers it to the core.
14
16
  */
15
17
  import type { ChannelPlugin } from "@mercury-fw/channel-types";
16
18
  import { createHttpProvider } from "./http-provider.ts";
@@ -20,7 +22,7 @@ export { createHttpProvider, type HttpProviderDeps } from "./http-provider.ts";
20
22
  export const httpChannel: ChannelPlugin = {
21
23
  // The contract this channel is written for, as a literal: importing
22
24
  // CHANNEL_API_VERSION would report whichever contract is installed.
23
- apiVersion: 3,
25
+ apiVersion: 4,
24
26
  name: "http",
25
27
  build: (ctx) => {
26
28
  if (!ctx.resolveConfirmation || !ctx.reads) {
@@ -29,6 +31,10 @@ export const httpChannel: ChannelPlugin = {
29
31
  if (!ctx.authenticate) {
30
32
  throw new Error("http channel requires an auth provider (auth in mercury.config.ts)");
31
33
  }
34
+ // The provider sends a person back here after they log in, so it needs
35
+ // the address their browser reaches the surface at.
36
+ const publicUrl = ctx.env.HTTP_SURFACE_PUBLIC_URL?.trim().replace(/\/+$/, "");
37
+ const logins = publicUrl ? ctx.logins : undefined;
32
38
  return createHttpProvider({
33
39
  port: Number(ctx.env.HTTP_SURFACE_PORT ?? "4100"),
34
40
  corsOrigin: ctx.env.HTTP_SURFACE_CORS_ORIGIN ?? "*",
@@ -36,6 +42,7 @@ export const httpChannel: ChannelPlugin = {
36
42
  resolveConfirmation: ctx.resolveConfirmation,
37
43
  reads: ctx.reads,
38
44
  authenticate: ctx.authenticate,
45
+ ...(logins ? { logins: { callbackUrl: `${publicUrl}/login/callback`, accept: logins.accept, complete: logins.complete } } : {}),
39
46
  });
40
47
  },
41
48
  };
package/openapi.yaml CHANGED
@@ -87,7 +87,9 @@ paths:
87
87
  An SSE stream. Each event is one of the types in `TurnEvent`:
88
88
  `reasoning`/`reasoning_end`, `text` (incremental deltas), `tool`/
89
89
  `tool_finish` (status of a tool call), `pending` (a staged
90
- confirm-required action, with its command and token), `final` (the
90
+ confirm-required action, with its command and token), `login` (a
91
+ service the caller has to log in to first, with the link to open),
92
+ `final` (the
91
93
  authoritative complete answer), and `error`.
92
94
  content:
93
95
  text/event-stream:
@@ -339,6 +341,30 @@ paths:
339
341
  application/json:
340
342
  schema:
341
343
  $ref: "#/components/schemas/Error"
344
+ /login/callback:
345
+ get:
346
+ summary: Where a person comes back after logging in to a service
347
+ description: >
348
+ Mounted when `HTTP_SURFACE_PUBLIC_URL` is set. The service sends the
349
+ person's browser here with `code` and `state` (or `error`); the
350
+ single-use state is the credential, so no bearer token is needed.
351
+ Answers with a plain-text page.
352
+ security: []
353
+ parameters:
354
+ - { name: code, in: query, schema: { type: string } }
355
+ - { name: state, in: query, schema: { type: string } }
356
+ - { name: error, in: query, schema: { type: string } }
357
+ responses:
358
+ "200":
359
+ description: Logged in.
360
+ content:
361
+ text/plain:
362
+ schema: { type: string }
363
+ "400":
364
+ description: Refused, expired, already used, or missing code/state.
365
+ content:
366
+ text/plain:
367
+ schema: { type: string }
342
368
  /openapi.yaml:
343
369
  get:
344
370
  summary: This OpenAPI document
@@ -416,6 +442,12 @@ components:
416
442
  token:
417
443
  type: string
418
444
  description: On `pending` — the one-time confirmation token.
445
+ service:
446
+ type: string
447
+ description: On `login` — the service the caller has to log in to.
448
+ url:
449
+ type: string
450
+ description: On `login` — the link the caller opens to log in; it comes back to `/login/callback`.
419
451
  text:
420
452
  type: string
421
453
  description: On `final` — the authoritative complete answer.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mercury-fw/channel-http",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,8 +21,8 @@
21
21
  "@mercury-fw/channel-types": ">=0.36.0 <1.0.0"
22
22
  },
23
23
  "devDependencies": {
24
- "@mercury-fw/channel-types": "0.36.0",
25
- "@mercury-fw/plugin-types": "0.36.0",
24
+ "@mercury-fw/channel-types": "0.40.0",
25
+ "@mercury-fw/plugin-types": "0.40.0",
26
26
  "@mercury-fw/typescript-config": "*",
27
27
  "@types/bun": "^1.4.2",
28
28
  "typescript": "^6.0.3"