@goodandready/dsh-dsml-artifact-guard 0.2.4 → 0.2.5

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
@@ -7,7 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
- ## 0.2.4
10
+ ## [0.2.5] - 2026-09-24
11
+
12
+ ### Fixed
13
+ - **Preflight & Locale Contract**: eliminated nonexistent `locale.get()` call in `lib/client.js`, using injected `props.t` with canonical `ctx.locale.bind(NS)` fallback (#36).
14
+ - **Streaming Buffer & Tag Balancing**: replaced fixed 64-character buffer slicing with delimiter-based tag boundary scanning (`<` and `>`), ensuring DSML parameter tags of arbitrary length preserve open/close tag balance without premature truncation (#37).
15
+ - **One-Click Updater LAN Support**: updated `isTrustedUpdateRequest` to allow same-origin requests matching Host/Origin, enabling one-click updates from LAN and reverse-proxy Web UIs while retaining strict loopback verification for local endpoints (#38).
16
+ - **Dead Code Elimination**: connected `checkRequestMethod` helper to updater POST handler (#39).
17
+
18
+ ## [0.2.4] - 2026-09-22
11
19
 
12
20
  ### Fixed
13
21
  - The settings card no longer waits for the removed `settingsScope` service. It uses `configForms` on current DeepSeek Harness (#41).
package/lib/client.js CHANGED
@@ -528,10 +528,10 @@ window.__ModuleLoader__.load({
528
528
  // title) and view:'page' (the form, bare). The older seat keeps the card.
529
529
  function PluginItem(props) {
530
530
  if (props && props.view === 'summary') {
531
- const ctx = props && props.ctx;
532
- const lang = (ctx && ctx.locale && typeof ctx.locale.get === 'function' ? ctx.locale.get() : null) || 'en';
533
- const dict = String(lang).startsWith('zh') ? zh : en;
534
- const tt = typeof props.t === 'function' ? props.t : (k) => dict[k] || en[k] || k;
531
+ const tBind = (props && props.ctx && typeof props.ctx.locale?.bind === 'function')
532
+ ? props.ctx.locale.bind(NS)
533
+ : null;
534
+ const tt = typeof props.t === 'function' ? props.t : (typeof tBind === 'function' ? tBind : (k) => en[k] || k);
535
535
  return React.createElement('div', {
536
536
  'data-dsh-plugin': NS,
537
537
  className: 'dsh-dsml-artifact-guard-sub',
package/lib/index.js CHANGED
@@ -5,7 +5,6 @@ const TAIL = /(?:<\/|DSML|parameter>\s*)?(?:<\/|DSML|invoke>\s*)?<\/|D
5
5
  const OPEN_TAG = /<|DSML|(?:tool_calls|invoke|parameter)[^>]*>/gu;
6
6
  const CLOSE_TAG = /<\/|DSML|(?:tool_calls|invoke|parameter)>/gu;
7
7
  const KEEP = 96;
8
- const SCAN_BUFFER_SAFE = 64;
9
8
 
10
9
  export const name = '@goodandready/dsh-dsml-artifact-guard';
11
10
  export const inject = ['llm', 'settings', 'webServer'];
@@ -68,10 +67,22 @@ export async function* sanitizeDsmlArtifacts(s, { mode = 'sanitize', onArtifact
68
67
  p += c.text;
69
68
 
70
69
  scanBuffer += c.text;
71
- if (scanBuffer.length > SCAN_BUFFER_SAFE) {
72
- const safe = scanBuffer.slice(0, scanBuffer.length - SCAN_BUFFER_SAFE);
73
- scanBuffer = scanBuffer.slice(scanBuffer.length - SCAN_BUFFER_SAFE);
74
- countTags(safe);
70
+ const lastOpen = scanBuffer.lastIndexOf('<');
71
+ const lastClose = scanBuffer.lastIndexOf('>');
72
+ if (lastOpen > lastClose) {
73
+ if (lastOpen > 0) {
74
+ const safe = scanBuffer.slice(0, lastOpen);
75
+ scanBuffer = scanBuffer.slice(lastOpen);
76
+ countTags(safe);
77
+ }
78
+ if (scanBuffer.length > 2048) {
79
+ const cut = scanBuffer.length - 1024;
80
+ countTags(scanBuffer.slice(0, cut));
81
+ scanBuffer = scanBuffer.slice(cut);
82
+ }
83
+ } else if (scanBuffer.length > 0) {
84
+ countTags(scanBuffer);
85
+ scanBuffer = '';
75
86
  }
76
87
 
77
88
  if (p.length > KEEP) {
package/lib/updater.js CHANGED
@@ -4,6 +4,7 @@ import { readFile } from 'node:fs/promises';
4
4
  import { homedir } from 'node:os';
5
5
  import { basename, dirname, isAbsolute, resolve } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
+ import { checkRequestMethod } from './index.js';
7
8
 
8
9
  const UPDATE_HEADER = 'x-dsh-plugin-update';
9
10
  const UPDATE_TIMEOUT_MS = 10 * 60000;
@@ -24,7 +25,6 @@ export function isLoopback(value) {
24
25
 
25
26
  export function isTrustedUpdateRequest(request) {
26
27
  if (header(request, UPDATE_HEADER) !== '1') return false;
27
- if (!isLoopback(request.socket?.remoteAddress)) return false;
28
28
  const site = header(request, 'sec-fetch-site');
29
29
  if (site !== undefined && site !== 'same-origin') return false;
30
30
  const origin = header(request, 'origin');
@@ -32,8 +32,12 @@ export function isTrustedUpdateRequest(request) {
32
32
  if (origin === undefined || host === undefined) return false;
33
33
  try {
34
34
  const url = new URL(origin);
35
- return (url.protocol === 'http:' || url.protocol === 'https:')
36
- && isLoopback(url.hostname) && url.host === host;
35
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') return false;
36
+ if (url.host.toLowerCase() !== host.toLowerCase()) return false;
37
+ if (isLoopback(url.hostname)) {
38
+ return isLoopback(request.socket?.remoteAddress);
39
+ }
40
+ return site === 'same-origin';
37
41
  } catch {
38
42
  return false;
39
43
  }
@@ -218,7 +222,7 @@ export function registerPluginUpdater(ctx, options) {
218
222
  res.end(req.method === 'HEAD' ? undefined : JSON.stringify(payload));
219
223
  return;
220
224
  }
221
- if (req.method !== 'POST') {
225
+ if (!checkRequestMethod(req, 'POST')) {
222
226
  res.writeHead(405, { allow: 'GET, HEAD, POST' });
223
227
  res.end();
224
228
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-dsml-artifact-guard",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Sanitizes leaked DeepSeek DSML closing tags from model text streams",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",