@mercury-fw/channel-http 0.12.0 → 0.13.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,12 @@
1
1
  # @mercury-fw/channel-http
2
2
 
3
+ ## 0.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7c9a038: - `GET /version` answers the framework's version (`{ ok: true, version: "0.59.0" }`) with no token, for a UI to show which Mercury it's talking to.
8
+ - The channel now needs the framework at 0.59 or later (peer range `>=0.59.0 <1.0.0`), the first whose core gives channels its version.
9
+
3
10
  ## 0.12.0
4
11
 
5
12
  ### Minor Changes
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gives a [Mercury](https://github.com/lucabro81/mercury-fw) agent an HTTP surface to build a custom UI on: one conversational turn per request, streamed back as Server-Sent Events, plus confirmation and read-only routes over what the agent holds (conversations, the wiki, memory, its plugins).
4
4
 
5
- This version is written for version 6 of the channel contract, the one that brings the channels' texts in the person's language, and needs the framework at 0.54 or later: a core with an older contract skips it, and the log says why. `X-Time-Zone` and `PATCH /me` need the framework at 0.56 or later: an older core ignores the header and offers no `PATCH /me`.
5
+ This version is written for version 6 of the channel contract, the one that brings the channels' texts in the person's language, and needs the framework at 0.59 or later, the first whose core gives channels its version (`GET /version`). A core with an older contract skips it, and the log says why.
6
6
 
7
7
  ```bash
8
8
  bun add @mercury-fw/channel-http
@@ -29,7 +29,7 @@ It needs an auth provider next to it, or several (`auth: [a, b]`, tried in order
29
29
 
30
30
  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>`.
31
31
 
32
- **Authentication.** Every route except `GET /openapi.yaml`, `GET /login/callback` and the `OPTIONS` preflights needs `Authorization: Bearer <token>`, and the auth providers decide who the token belongs to: the first that recognizes it. A missing or refused token gets `401` with `WWW-Authenticate: Bearer` before anything runs. Then the core decides whether it talks to that person at all: someone the app's directory doesn't know gets `403` (unless the app lets unknown people in), and `503` when the directory can't be reached, both with `{ ok: false, error, reason }`, where `error` is what to tell them.
32
+ **Authentication.** Every route except `GET /openapi.yaml`, `GET /version`, `GET /login/callback` and the `OPTIONS` preflights needs `Authorization: Bearer <token>`, and the auth providers decide who the token belongs to: the first that recognizes it. A missing or refused token gets `401` with `WWW-Authenticate: Bearer` before anything runs. Then the core decides whether it talks to that person at all: someone the app's directory doesn't know gets `403` (unless the app lets unknown people in), and `503` when the directory can't be reached, both with `{ ok: false, error, reason }`, where `error` is what to tell them.
33
33
 
34
34
  **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.
35
35
 
@@ -119,3 +119,7 @@ All `GET`, all JSON, all reporting state already held in-process. They need an a
119
119
  | `GET /openapi.yaml` | the OpenAPI document for this surface (`text/yaml`) |
120
120
 
121
121
  Conversation history and the conversation list are backed by the durable verbatim archive; they degrade to empty when the vector store is unreachable.
122
+
123
+ ## `GET /version`
124
+
125
+ Which Mercury this is, for a UI to show: `{ ok: true, version }`, the framework's version as `major.minor.patch` (`"0.59.0"`), the one of `@mercury-fw/core`, not this channel's own. It needs no token.
@@ -42,6 +42,8 @@ export type HttpProviderDeps = {
42
42
  assistantName?: string;
43
43
  /** The texts in each caller's language (`ctx.i18n`). */
44
44
  i18n?: ChannelI18n;
45
+ /** The framework's version, served on `/version` (`ctx.frameworkVersion`). */
46
+ version?: string;
45
47
  };
46
48
  export declare function createHttpProvider(deps: HttpProviderDeps): Provider & {
47
49
  stop(): Promise<void>;
@@ -15,7 +15,7 @@
15
15
  * `/login/callback` is where a person's browser comes back after logging in to
16
16
  * a service a plugin acts on as them; the single-use state is its credential.
17
17
  *
18
- * Every route but the OpenAPI document, the login callback and the CORS
18
+ * Every route but the OpenAPI document, `/version`, the login callback and the CORS
19
19
  * preflights asks the injected `authenticate` (the auth provider the app declares) who is calling
20
20
  * before anything runs, and answers 401 when nobody is. A conversation belongs
21
21
  * to whoever opened it: the session key is `<principal.id>:<conversationId>`.
@@ -157,6 +157,8 @@ export type HttpServerDeps = TurnRequestDeps & {
157
157
  settings?: ChannelSettings;
158
158
  /** What the pages people read call the assistant. */
159
159
  assistantName?: string;
160
+ /** The framework's version; `/version` is mounted only with it. */
161
+ version?: string;
160
162
  };
161
163
  /** Starts the HTTP surface: `POST /turn` (4a) plus the read-only routes (4b)
162
164
  * when `reads` is supplied. */
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The HTTP channel plugin: an opt-in conversational HTTP surface (SSE `/turn`,
3
- * `/confirm`, read-only introspection routes, the OpenAPI doc) that a custom UI
3
+ * `/confirm`, read-only introspection routes, the OpenAPI doc, `/version`) that a custom UI
4
4
  * calls. Exported as a `ChannelPlugin` the core's channel loader consumes; it
5
5
  * does not import the app. Enabled by listing it in `mercury.config.ts` — there
6
6
  * is no runtime inert condition (unlike Google Chat's subscription): if it's
package/http-provider.ts CHANGED
@@ -39,6 +39,8 @@ export type HttpProviderDeps = {
39
39
  assistantName?: string;
40
40
  /** The texts in each caller's language (`ctx.i18n`). */
41
41
  i18n?: ChannelI18n;
42
+ /** The framework's version, served on `/version` (`ctx.frameworkVersion`). */
43
+ version?: string;
42
44
  };
43
45
 
44
46
  export function createHttpProvider(deps: HttpProviderDeps): Provider & { stop(): Promise<void> } {
@@ -60,6 +62,7 @@ export function createHttpProvider(deps: HttpProviderDeps): Provider & { stop():
60
62
  ...(deps.resumeMaxChars === undefined ? {} : { resumeMaxChars: deps.resumeMaxChars }),
61
63
  ...(deps.assistantName === undefined ? {} : { assistantName: deps.assistantName }),
62
64
  ...(deps.i18n === undefined ? {} : { i18n: deps.i18n }),
65
+ ...(deps.version === undefined ? {} : { version: deps.version }),
63
66
  });
64
67
  deps.logins?.accept(deps.logins.callbackUrl);
65
68
  },
package/http-server.ts CHANGED
@@ -15,7 +15,7 @@
15
15
  * `/login/callback` is where a person's browser comes back after logging in to
16
16
  * a service a plugin acts on as them; the single-use state is its credential.
17
17
  *
18
- * Every route but the OpenAPI document, the login callback and the CORS
18
+ * Every route but the OpenAPI document, `/version`, the login callback and the CORS
19
19
  * preflights asks the injected `authenticate` (the auth provider the app declares) who is calling
20
20
  * before anything runs, and answers 401 when nobody is. A conversation belongs
21
21
  * to whoever opened it: the session key is `<principal.id>:<conversationId>`.
@@ -579,6 +579,19 @@ export function readRoutes(
579
579
  };
580
580
  }
581
581
 
582
+ /**
583
+ * `GET /version`: which Mercury this is (the framework's version), for any
584
+ * front end to show. Nobody is asked who they are, like `/openapi.yaml`.
585
+ */
586
+ function versionRoutes(version: string, corsOrigin = "*"): Record<string, ReadRoute> {
587
+ return {
588
+ "/version": {
589
+ GET: () => Response.json({ ok: true, version }, { headers: corsHeaders(corsOrigin) }),
590
+ OPTIONS: () => preflight(corsOrigin),
591
+ },
592
+ };
593
+ }
594
+
582
595
  export type HttpServerDeps = TurnRequestDeps & {
583
596
  port: number;
584
597
  reads?: ChannelHostReads;
@@ -592,6 +605,8 @@ export type HttpServerDeps = TurnRequestDeps & {
592
605
  settings?: ChannelSettings;
593
606
  /** What the pages people read call the assistant. */
594
607
  assistantName?: string;
608
+ /** The framework's version; `/version` is mounted only with it. */
609
+ version?: string;
595
610
  };
596
611
 
597
612
  /** Starts the HTTP surface: `POST /turn` (4a) plus the read-only routes (4b)
@@ -623,6 +638,7 @@ export function startHttpServer(deps: HttpServerDeps): ReturnType<typeof Bun.ser
623
638
  OPTIONS: () => preflight(origin),
624
639
  },
625
640
  "/openapi.yaml": { GET: () => openApiResponse(origin), OPTIONS: () => preflight(origin) },
641
+ ...(deps.version === undefined ? {} : versionRoutes(deps.version, origin)),
626
642
  ...(deps.reads ? readRoutes(deps.reads, deps.authenticate, origin, deps.admit, deps.settings) : {}),
627
643
  ...(deps.completeLogin ? loginRoutes(deps.completeLogin, deps.assistantName, deps.i18n) : {}),
628
644
  ...(deps.linking ? linkRoutes({ ...deps, start: deps.linking.start, corsOrigin: origin }) : {}),
package/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The HTTP channel plugin: an opt-in conversational HTTP surface (SSE `/turn`,
3
- * `/confirm`, read-only introspection routes, the OpenAPI doc) that a custom UI
3
+ * `/confirm`, read-only introspection routes, the OpenAPI doc, `/version`) that a custom UI
4
4
  * calls. Exported as a `ChannelPlugin` the core's channel loader consumes; it
5
5
  * does not import the app. Enabled by listing it in `mercury.config.ts` — there
6
6
  * is no runtime inert condition (unlike Google Chat's subscription): if it's
@@ -51,6 +51,7 @@ export const httpChannel: ChannelPlugin = {
51
51
  ...(ctx.settings === undefined ? {} : { settings: ctx.settings }),
52
52
  ...(resumeMaxChars === undefined ? {} : { resumeMaxChars }),
53
53
  ...(ctx.assistantName === undefined ? {} : { assistantName: ctx.assistantName }),
54
+ ...(ctx.frameworkVersion === undefined ? {} : { version: ctx.frameworkVersion }),
54
55
  ...(logins ? { logins: { callbackUrl: `${publicUrl}/login/callback`, accept: logins.accept, complete: logins.complete } } : {}),
55
56
  });
56
57
  },
package/openapi.yaml CHANGED
@@ -14,7 +14,7 @@ info:
14
14
 
15
15
  ## Authentication
16
16
 
17
- Every route except this document and the `OPTIONS` preflights needs
17
+ Every route except this document, `/version` and the `OPTIONS` preflights needs
18
18
  `Authorization: Bearer <token>`. The app's auth provider decides what a
19
19
  valid token is (an OpenID Connect access token, a static test token); a
20
20
  missing or refused one gets `401` with `WWW-Authenticate: Bearer`, before
@@ -589,6 +589,27 @@ paths:
589
589
  content:
590
590
  text/plain:
591
591
  schema: { type: string }
592
+ /version:
593
+ get:
594
+ summary: Which Mercury this is
595
+ description: >
596
+ The framework's version, `major.minor.patch`, for a UI to show. Needs
597
+ no bearer token.
598
+ security: []
599
+ responses:
600
+ "200":
601
+ description: The framework's version.
602
+ content:
603
+ application/json:
604
+ schema:
605
+ type: object
606
+ required: [ok, version]
607
+ properties:
608
+ ok: { type: boolean, const: true }
609
+ version:
610
+ type: string
611
+ pattern: '^\d+\.\d+\.\d+$'
612
+ examples: ["0.59.0"]
592
613
  /openapi.yaml:
593
614
  get:
594
615
  summary: This OpenAPI document
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mercury-fw/channel-http",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,11 +18,11 @@
18
18
  "access": "public"
19
19
  },
20
20
  "peerDependencies": {
21
- "@mercury-fw/channel-types": ">=0.54.0 <1.0.0"
21
+ "@mercury-fw/channel-types": ">=0.59.0 <1.0.0"
22
22
  },
23
23
  "devDependencies": {
24
- "@mercury-fw/channel-types": "0.56.0",
25
- "@mercury-fw/plugin-types": "0.56.0",
24
+ "@mercury-fw/channel-types": "0.59.0",
25
+ "@mercury-fw/plugin-types": "0.59.0",
26
26
  "@mercury-fw/typescript-config": "*",
27
27
  "@types/bun": "^1.4.2",
28
28
  "typescript": "^6.0.3"