@jini-ai/cli 0.4.0 → 0.4.1

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,11 @@
1
1
  # @jini-ai/cli
2
2
 
3
+ ## 0.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Tovu clean-up release (2026-10-08): code Tovu moved into Jini, plus the Jini clean-up (see the commit log since 0.4.0).
8
+
3
9
  ## 0.4.0 — 2026-10-02
4
10
 
5
11
  ### BREAKING
@@ -19,6 +19,7 @@
19
19
  * line per event on stdout, stop at a terminal `'end'` event — a clean
20
20
  * pattern that had no route to call until now).
21
21
  */
22
+ import { decodeSseFrames } from '@jini-ai/core/primitives';
22
23
  import { parseFlags, positionalArgs } from './flags.js';
23
24
  import { exitWithStructuredError, structuredHttpFailure } from './errors.js';
24
25
  import { getJsonFromDaemon, postJsonToDaemon } from './http.js';
@@ -140,47 +141,28 @@ export async function runCancelCommand({ args, resolveBaseUrl: resolveBaseUrlPor
140
141
  const result = await postJsonToDaemon({ base: baseUrl, route: `/api/runs/${encodeURIComponent(runId)}/cancel`, body }, transportOptions(deps));
141
142
  printJsonResult(deps, result);
142
143
  }
143
- /** Cap on total buffered-but-not-yet-frame-terminated SSE bytes — a daemon that never sends a blank-line frame terminator must not grow this without bound (mirrors `http.ts`'s `readJsonWithLimit` byte cap). */
144
- const MAX_SSE_BUFFER_BYTES = 10 * 1024 * 1024;
145
- function parseSseFrame(rawFrame) {
146
- let id;
147
- let eventName;
148
- const dataLines = [];
149
- for (const line of rawFrame.split('\n')) {
150
- if (line.startsWith('id: '))
151
- id = line.slice(4);
152
- else if (line.startsWith('event: '))
153
- eventName = line.slice(7);
154
- else if (line.startsWith('data: '))
155
- dataLines.push(line.slice(6));
156
- }
157
- if (dataLines.length === 0)
158
- return null;
159
- return { ...(id !== undefined ? { id } : {}), ...(eventName !== undefined ? { event: eventName } : {}), data: dataLines.join('\n') };
160
- }
161
- /** Reads `body` as an SSE byte stream and yields one {@link SseFrame} per blank-line-terminated frame. */
144
+ /** Cap on total buffered-but-not-yet-frame-terminated SSE text — a daemon that never sends a blank-line frame terminator must not grow this without bound (retains the historical 10 MiB-sized text cap; TextDecoder output is measured in UTF-16 units). */
145
+ const MAX_SSE_BUFFER_CHARS = 10 * 1024 * 1024;
146
+ /** Reads complete records; this adapter releases the lock without canceling the host's body. */
162
147
  async function* readSseFrames(body) {
163
148
  const reader = body.getReader();
164
- const decoder = new TextDecoder();
165
- let buffer = '';
166
- try {
149
+ async function* chunks() {
167
150
  for (;;) {
168
151
  const { done, value } = await reader.read();
169
152
  if (done)
170
- break;
153
+ return;
171
154
  if (value !== undefined)
172
- buffer += decoder.decode(value, { stream: true });
173
- if (buffer.length > MAX_SSE_BUFFER_BYTES) {
174
- throw new Error(`SSE stream exceeded the ${MAX_SSE_BUFFER_BYTES}-byte unterminated-frame buffer limit`);
175
- }
176
- let sepIndex;
177
- while ((sepIndex = buffer.indexOf('\n\n')) >= 0) {
178
- const rawFrame = buffer.slice(0, sepIndex);
179
- buffer = buffer.slice(sepIndex + 2);
180
- const frame = parseSseFrame(rawFrame);
181
- if (frame !== null)
182
- yield frame;
183
- }
155
+ yield value;
156
+ }
157
+ }
158
+ try {
159
+ for await (const frame of decodeSseFrames({ source: chunks() }, {
160
+ flushFinalFrame: false, maxBufferChars: MAX_SSE_BUFFER_CHARS,
161
+ })) {
162
+ if (frame.dataLines.length === 0)
163
+ continue;
164
+ yield { ...(frame.id !== undefined ? { id: frame.id } : {}),
165
+ ...(frame.event !== null ? { event: frame.event } : {}), data: frame.data };
184
166
  }
185
167
  }
186
168
  finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jini-ai/cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "CLI transport shell for driving a Jini daemon over HTTP: flag parsing, daemon-URL resolution, local daemon discovery, and a pluggable command registry.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -52,13 +52,26 @@
52
52
  "access": "public",
53
53
  "registry": "https://registry.npmjs.org"
54
54
  },
55
+ "devDependencies": {
56
+ "commander": "^15.0.0",
57
+ "@jini-ai/core": "0.4.2",
58
+ "@jini-ai/sidecar": "0.4.1",
59
+ "@jini-ai/platform": "0.5.2"
60
+ },
55
61
  "dependencies": {
56
- "@jini-ai/core": "0.4.0",
57
- "@jini-ai/platform": "0.5.0",
58
- "@jini-ai/sidecar": "0.4.0"
62
+ "@jini-ai/core": "^0.4.2"
59
63
  },
60
- "devDependencies": {
61
- "commander": "^15.0.0"
64
+ "peerDependencies": {
65
+ "@jini-ai/platform": "^0.5.1",
66
+ "@jini-ai/sidecar": "^0.4.0"
67
+ },
68
+ "peerDependenciesMeta": {
69
+ "@jini-ai/platform": {
70
+ "optional": true
71
+ },
72
+ "@jini-ai/sidecar": {
73
+ "optional": true
74
+ }
62
75
  },
63
76
  "scripts": {
64
77
  "build": "tsc -p tsconfig.json && chmod +x dist/main.js",