@pipeline-moe/client-core 0.1.0 → 0.1.2

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.
Files changed (3) hide show
  1. package/README.md +57 -0
  2. package/dist/store.js +22 -7
  3. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @pipeline-moe/client-core
2
+
3
+ Framework-agnostic client library for a
4
+ [pipeline-moe](https://github.com/DAXZEIT/pipeline-moe) server — the shared
5
+ core behind both official clients (the
6
+ [`pmoe` TUI](https://www.npmjs.com/package/@pipeline-moe/tui) and the bundled
7
+ web UI).
8
+
9
+ ## What's inside
10
+
11
+ - **Typed REST surface** — `createApi(apiBase)` wraps every server endpoint
12
+ (rooms, messages, lineup, presets, providers) with typed requests/responses.
13
+ - **Pure SSE reducer** — `reduce(state, event) -> { state, effects }` turns the
14
+ server's event stream into room state deterministically; trivially unit
15
+ testable.
16
+ - **Effectful room store** — `createRoomStore` binds the reducer to a live
17
+ `EventSource`, with subscribe/notify semantics that plug straight into
18
+ `useSyncExternalStore` (web) or any render loop (TUI).
19
+ - **Injectable `EventSourceFactory`** — the browser uses the global
20
+ `EventSource`; Node clients inject one (e.g. the
21
+ [`eventsource`](https://www.npmjs.com/package/eventsource) package).
22
+
23
+ Ships as plain ESM with type declarations — no framework, no build-tool
24
+ assumptions.
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ npm i @pipeline-moe/client-core
30
+ ```
31
+
32
+ ## Usage sketch
33
+
34
+ ```ts
35
+ import { createRoomStore } from "@pipeline-moe/client-core"
36
+
37
+ const store = createRoomStore({
38
+ apiBase: "http://localhost:5300",
39
+ roomId: "default",
40
+ // In the browser, omit eventSourceFactory (global EventSource is used).
41
+ // In Node, inject one built on the `eventsource` package:
42
+ eventSourceFactory: nodeEventSourceFactory,
43
+ })
44
+
45
+ const unsubscribe = store.subscribe(() => {
46
+ const state = store.getSnapshot() // roster, transcript, streaming buffers…
47
+ })
48
+ store.start() // loads the snapshot and opens the SSE stream
49
+ ```
50
+
51
+ A Node factory is ~10 lines over the `EventSourceFactory` seam — see
52
+ [`packages/tui/src/nodeEventSource.ts`](https://github.com/DAXZEIT/pipeline-moe/blob/main/packages/tui/src/nodeEventSource.ts)
53
+ for the reference implementation.
54
+
55
+ ## License
56
+
57
+ MIT — see the [repository](https://github.com/DAXZEIT/pipeline-moe).
package/dist/store.js CHANGED
@@ -82,11 +82,13 @@ export function createRoomStore(opts) {
82
82
  applyEffects(result.effects);
83
83
  };
84
84
  // ── Lifecycle ─────────────────────────────────────────────────────────────
85
- /** Load the REST snapshot and open the SSE stream. Idempotent-safe to call once. */
86
- const start = () => {
87
- // Clear transient fields before (re)loading a reused store must not show a
88
- // prior room's in-flight turn. Fresh stores are already clean; this is cheap.
89
- set(resetTransient(state));
85
+ /**
86
+ * Fetch the full REST snapshot. Runs at start() and again on every SSE open:
87
+ * if the client came up before the server (or the server restarted), the
88
+ * initial fetches failed silently re-running them on (re)connect is what
89
+ * lets providers/settings/conversations self-heal instead of staying empty.
90
+ */
91
+ const loadSnapshot = () => {
90
92
  rApi.transcript().then((m) => patch({ messages: m })).catch(() => { });
91
93
  rApi.workspace().then((w) => patch({ workspace: w })).catch(() => { });
92
94
  rApi.roster().then((r) => patch({ roster: r })).catch(() => { });
@@ -115,8 +117,18 @@ export function createRoomStore(opts) {
115
117
  }).catch(() => { });
116
118
  rApi.conversations().then((c) => patch({ conversations: c.list, currentConversationId: c.currentId })).catch(() => { });
117
119
  api.providers().then((d) => patch({ providers: d.providers, explicitlyEnabled: d.explicitlyEnabled })).catch(() => { });
120
+ };
121
+ /** Load the REST snapshot and open the SSE stream. Idempotent-safe to call once. */
122
+ const start = () => {
123
+ // Clear transient fields before (re)loading — a reused store must not show a
124
+ // prior room's in-flight turn. Fresh stores are already clean; this is cheap.
125
+ set(resetTransient(state));
126
+ loadSnapshot();
118
127
  conn = makeEventSource(sseUrl, {
119
- onOpen: () => patch({ connected: true }),
128
+ onOpen: () => {
129
+ patch({ connected: true });
130
+ loadSnapshot();
131
+ },
120
132
  onError: () => patch({ connected: false }),
121
133
  onEvent,
122
134
  });
@@ -253,8 +265,10 @@ export function createRoomStore(opts) {
253
265
  addProvider: (name, key) => {
254
266
  api.addProvider(name, key).then(() => {
255
267
  pushNotice(`Provider "${name}" configured.`);
256
- // Refresh models so the dropdown picks up new models.
268
+ // Refresh models so the dropdown picks up new models, and the provider
269
+ // list so the configured flag flips without a reconnect.
257
270
  api.models().catch(() => { });
271
+ api.providers().then((d) => patch({ providers: d.providers, explicitlyEnabled: d.explicitlyEnabled })).catch(() => { });
258
272
  }).catch(fail);
259
273
  },
260
274
  removeProvider: (name) => {
@@ -264,6 +278,7 @@ export function createRoomStore(opts) {
264
278
  msg += ` Note: ${r.agentsUsing.join(", ")} may need model reassigned.`;
265
279
  }
266
280
  pushNotice(msg);
281
+ api.providers().then((d) => patch({ providers: d.providers, explicitlyEnabled: d.explicitlyEnabled })).catch(() => { });
267
282
  }).catch(fail);
268
283
  },
269
284
  loginProvider: (name) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipeline-moe/client-core",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Framework-agnostic client for a pipeline-moe server: typed REST surface, pure SSE reducer, and an effectful room store. Consumed by the web and terminal clients.",