dsh-kaomoji 0.1.4 → 0.1.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,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/lang/zh-CN/
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.5] - 2026-09-25
11
+
12
+ ### Fixed
13
+
14
+ - 设置通道从 `connection.rpc` 改为插件自有的 `webServer` 路由
15
+ (`POST /dsh-kaomoji-settings`)。部分部署里 `connection` 服务不会进入插件
16
+ 作用域,通道永远注册不上,浏览器侧只看到 `HTTP 405`,设置卡片因此无法保存。
17
+
10
18
  ## [0.1.4] - 2026-09-25
11
19
 
12
20
  ### Changed
package/README.en.md CHANGED
@@ -44,7 +44,7 @@ largest Japanese kaomoji sites.
44
44
  3. The model emits one of the listed kaomoji where it fits. Because kaomoji
45
45
  are plain text, no custom rendering is needed in the chat UI.
46
46
 
47
- Settings use a dedicated loopback RPC channel (`/dsh-kaomoji-settings`):
47
+ Settings use a same-origin route registered on the web server (`POST /dsh-kaomoji-settings`):
48
48
 
49
49
  1. The Settings → General card reads/writes `~/.dsh/dsh-kaomoji.json`.
50
50
  2. The Host updates the prompt section and emits `system-prompt/change`
@@ -107,19 +107,16 @@ Example `cordis.patch.yml`:
107
107
  | `maxPerTurn` | `number` (1–5) | `1` | Max kaomoji per reply; `frequent` distributes that many across different sentences (fewer in very short replies) |
108
108
  | `customPrompt` | `string` | `''` | Extra style/scene guidance; cannot change mode, whitelist or limits |
109
109
  | `settingsFile` | `string` | `~/.dsh/dsh-kaomoji.json` | (advanced) user-settings file path |
110
- | `settingsAuthority` | `'trusted-host' \| 'loopback'` | `'trusted-host'` | Settings RPC trust scope: by default the local Host plus declared trusted hosts (Tailscale/LAN) may write; set `loopback` to restrict to the local machine |
111
-
112
110
  Card edits take effect immediately; deployment-default edits in
113
111
  `cordis.patch.yml` require a dsh restart.
114
112
 
115
113
  ### Remote access (Tailscale / LAN)
116
114
 
117
- - If the dsh page is reachable remotely, the Host already trusts that origin;
118
- with the default `settingsAuthority: trusted-host` the General card can save
119
- settings from the remote session.
120
- - If the card reports that the origin is not trusted, add the host to the
121
- server’s `trustedHosts` (dsh-client-connection) or temporarily switch to
122
- `settingsAuthority: loopback` and edit on the server itself.
115
+ - The settings route is same-origin with the page (`POST /dsh-kaomoji-settings`),
116
+ so pages opened over Tailscale, LAN or an SSH tunnel can all save settings —
117
+ no `trustedHosts` configuration needed.
118
+ - The only requirement is that the page can reach that Host's HTTP port; any
119
+ failure now shows the server's original error in the card (0.1.4+).
123
120
 
124
121
  ## Kaomoji library & attribution
125
122
 
package/README.md CHANGED
@@ -31,7 +31,7 @@
31
31
  - 用户附加的 `customPrompt`(可选)。
32
32
  3. 模型生成时在合适位置直接输出列表内的颜文字——因为它只是普通文本,聊天界面无需任何自定义渲染即可显示。
33
33
 
34
- 设置部分走独立的 loopback RPC(`/dsh-kaomoji-settings`):
34
+ 设置部分由插件在 webServer 上注册同源路由 `POST /dsh-kaomoji-settings`:
35
35
 
36
36
  1. 「设置 → 通用设置」的卡片读取/写入 `~/.dsh/dsh-kaomoji.json`;
37
37
  2. Host 收到写入后立刻更新提示词段并发出 `system-prompt/change`,**下一次回复就生效**,不需要重启。
@@ -123,14 +123,12 @@ pnpm add file:C:\path\to\dsh-kaomoji
123
123
  | `maxPerTurn` | `number`(1–5) | `1` | 每条回复的颜文字数量上限;`frequent` 会按这个数量分散放在不同句子后,回复太短时自动减少 |
124
124
  | `customPrompt` | `string` | `''` | 附加风格/场景说明;不能改变模式、白名单或数量上限 |
125
125
  | `settingsFile` | `string` | `~/.dsh/dsh-kaomoji.json` | (进阶)用户设置持久化文件路径 |
126
- | `settingsAuthority` | `'trusted-host' \| 'loopback'` | `'trusted-host'` | 设置 RPC 信任范围:默认允许本机与 Host 声明的受信主机(Tailscale/局域网)保存设置;改成 `loopback` 则仅本机可改 |
127
-
128
126
  卡片里的修改即时写入用户层并热生效;修改 `cordis.patch.yml` 的部署默认值后需重启 dsh。
129
127
 
130
128
  ### 远程访问(Tailscale / 局域网)
131
129
 
132
- - 只要能正常远程打开 dsh 页面,说明 Host 已信任该来源;默认 `settingsAuthority: trusted-host` 下,通用设置卡片可以直接修改并保存。
133
- - 如果卡片提示「当前来源不在 Host 的信任列表里」,说明服务端 `@deepseek-ai/dsh-client-connection` 没有把该域名/IP 加进 `trustedHosts`;先在服务端配置受信主机,或临时改用 `settingsAuthority: loopback` 并到服务器本机编辑。
130
+ - 设置路由和页面同源(`POST /dsh-kaomoji-settings`),所以 Tailscale、局域网或 SSH 端口转发打开的页面都能直接改设置,不需要配置 `trustedHosts`。
131
+ - 唯一要求是页面能访问该 Host 的 HTTP 端口;如果卡片报错,看提示里的具体原因(0.1.4+ 会显示服务端原始错误)。
134
132
 
135
133
  ## 词库来源与许可
136
134
 
package/lib/client.js CHANGED
@@ -213,7 +213,8 @@ window.__ModuleLoader__.load({
213
213
  * 轻量快照 store(getSnapshot/subscribe),slot runtime 会把它包装成
214
214
  * useKaomoji(selector) hook。所有写操作都串行经过 loopback RPC。
215
215
  */
216
- function createSettingsStore(rpc) {
216
+ function createSettingsStore(fetchImpl) {
217
+ const doFetch = typeof fetchImpl === "function" ? fetchImpl : (...args) => fetch(...args);
217
218
  let snapshot = {
218
219
  status: "loading",
219
220
  settings: cloneSettings(DEFAULT_SETTINGS),
@@ -254,12 +255,26 @@ window.__ModuleLoader__.load({
254
255
 
255
256
  const callRpc = async (endpoint, payload) => {
256
257
  try {
257
- const result = await rpc.call(RPC_CHANNEL, endpoint, payload);
258
+ const response = await doFetch(RPC_CHANNEL, {
259
+ method: "POST",
260
+ headers: { "Content-Type": "application/json" },
261
+ body: JSON.stringify({ endpoint, payload }),
262
+ });
263
+ const status = Number((response && response.status) || 0);
264
+ let result;
265
+ try {
266
+ result = await response.json();
267
+ } catch {
268
+ result = undefined;
269
+ }
258
270
  if (result && result.ok === true) return { value: result.value };
259
271
  const error = (result && result.error) || {};
272
+ if (status === 403 || error.code === "forbidden") {
273
+ return { error: "forbidden", message: error.message || "forbidden" };
274
+ }
260
275
  return {
261
- error: error.code === "forbidden" ? "forbidden" : "rejected",
262
- message: typeof error.message === "string" ? error.message : undefined,
276
+ error: "rejected",
277
+ message: typeof error.message === "string" ? error.message : `HTTP ${String(status)}`,
263
278
  };
264
279
  } catch (error) {
265
280
  const message = String((error && error.message) || error || "");
@@ -485,15 +500,14 @@ window.__ModuleLoader__.load({
485
500
  );
486
501
  }
487
502
 
488
- const inject = ["slots", "locale", "connection"];
503
+ const inject = ["slots", "locale"];
489
504
 
490
505
  function apply(ctx) {
491
506
  ctx.effect(
492
507
  () => ctx.locale.register(NS, { zh, en }),
493
508
  "dsh-kaomoji: dictionaries",
494
509
  );
495
- const connection = ctx.get("connection");
496
- const controller = createSettingsStore(connection.rpc);
510
+ const controller = createSettingsStore();
497
511
  void controller.refresh();
498
512
 
499
513
  ctx.effect(() => {
package/lib/index.js CHANGED
@@ -30,8 +30,6 @@ export const SETTINGS_FILE_SCHEMA_VERSION = 1;
30
30
 
31
31
  export const MODES = ["off", "auto", "frequent"];
32
32
  export const PLACEMENTS = ["inline", "end"];
33
- /** 设置 RPC 的信任范围:trusted-host = 本机 + 服务端声明的受信主机(如 Tailscale)。 */
34
- export const SETTINGS_AUTHORITIES = ["trusted-host", "loopback"];
35
33
 
36
34
  export const DEFAULT_CONFIG = Object.freeze({
37
35
  /** off=关闭;auto=智能(确实有助于表达才用);frequent=高频(每条对话回复都带)。 */
@@ -219,11 +217,6 @@ function cloneSettings(settings) {
219
217
  return { ...settings, customPrompt: settings.customPrompt };
220
218
  }
221
219
 
222
- /** 设置 RPC 的信任范围;未显式配置时用 trusted-host(本机 + 服务端声明的受信主机)。 */
223
- function resolveAuthority(config) {
224
- return config?.settingsAuthority === "loopback" ? "loopback" : "trusted-host";
225
- }
226
-
227
220
  function defaultSettingsFile(config) {
228
221
  if (typeof config?.settingsFile === "string" && config.settingsFile.trim() !== "") {
229
222
  return resolve(config.settingsFile);
@@ -382,7 +375,6 @@ function createSettingsRpcHandler(ctx, settingsFile, getState, commit) {
382
375
  export function apply(ctx, config = {}) {
383
376
  const baseSettings = normalizeConfig(config);
384
377
  const settingsFile = defaultSettingsFile(config);
385
- const authority = resolveAuthority(config);
386
378
  const stored = loadSettingsDocument(settingsFile);
387
379
 
388
380
  // currentSettings = 用户层(~/.dsh/dsh-kaomoji.json)覆盖部署默认值后的实时设置。
@@ -420,30 +412,59 @@ export function apply(ctx, config = {}) {
420
412
  "dsh-kaomoji: guidance",
421
413
  );
422
414
 
423
- // 设置卡片通过 loopback RPC 读写 ~/.dsh/dsh-kaomoji.json;写入后立即
424
- // 更新提示词段(emit system-prompt/change),无需重启。
425
- const registerSettingsRpc = (connectionCtx) => {
426
- const handler = createSettingsRpcHandler(ctx, settingsFile, getState, adoptSettings);
427
- connectionCtx.effect(
415
+ // 设置读写走 webServer 直连路由,不用 connection.rpc:后者在部分部署里
416
+ // connection 服务不进入插件作用域,通道永远注册不上(浏览器侧表现为 405)。
417
+ const settingsHandler = createSettingsRpcHandler(ctx, settingsFile, getState, adoptSettings);
418
+ const attachSettingsRoute = (scope) => {
419
+ scope.effect(
428
420
  () =>
429
- connectionCtx.connection.rpc.handle(SETTINGS_RPC_CHANNEL, handler, {
430
- authority,
421
+ scope.webServer.register({
422
+ kind: "exact",
423
+ path: SETTINGS_RPC_CHANNEL,
424
+ handler: async (request, response) => {
425
+ const reply = (status, value) => {
426
+ response.writeHead(status, {
427
+ "Content-Type": "application/json; charset=utf-8",
428
+ "Cache-Control": "no-store",
429
+ });
430
+ response.end(JSON.stringify(value));
431
+ };
432
+ if (request.method !== "POST") {
433
+ reply(405, rpcError("bad-request", "POST required"));
434
+ return;
435
+ }
436
+ let raw = "";
437
+ try {
438
+ for await (const chunk of request) raw += chunk;
439
+ } catch {
440
+ // 客户端中断:按空 body 处理,下面的解析会返回 400。
441
+ }
442
+ let endpoint = "";
443
+ let payload;
444
+ try {
445
+ const parsed = JSON.parse(raw === "" ? "{}" : raw);
446
+ endpoint = typeof parsed.endpoint === "string" ? parsed.endpoint : "";
447
+ payload = parsed.payload;
448
+ } catch {
449
+ reply(400, rpcError("bad-request", "invalid JSON body"));
450
+ return;
451
+ }
452
+ reply(200, await settingsHandler(endpoint, payload));
453
+ },
431
454
  }),
432
- "dsh-kaomoji: settings rpc",
433
- );
434
- console.log(
435
- `[dsh-kaomoji] settings rpc 已注册(channel=${SETTINGS_RPC_CHANNEL}, authority=${authority})`,
455
+ "dsh-kaomoji: settings route",
436
456
  );
457
+ console.log(`[dsh-kaomoji] settings route 已注册(path=${SETTINGS_RPC_CHANNEL})`);
437
458
  };
438
- // connection 服务在多数部署里此刻已可用;拿不到再等 inject(旧版行为)。
439
- if (typeof ctx.get === "function" && ctx.get("connection") !== undefined) {
440
- registerSettingsRpc(ctx);
459
+ const webServer = typeof ctx.get === "function" ? ctx.get("webServer") : undefined;
460
+ if (webServer !== undefined) {
461
+ attachSettingsRoute(ctx);
441
462
  } else {
442
- ctx.inject(["connection"], registerSettingsRpc);
463
+ ctx.inject(["webServer"], attachSettingsRoute);
443
464
  }
444
465
 
445
466
  console.log(
446
- `[dsh-kaomoji] 已挂载(mode=${currentSettings.mode}, placement=${currentSettings.placement}, authority=${authority}, settingsFile=${settingsFile})`,
467
+ `[dsh-kaomoji] 已挂载(mode=${currentSettings.mode}, placement=${currentSettings.placement}, settingsFile=${settingsFile})`,
447
468
  );
448
469
  }
449
470
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-kaomoji",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Add mood-aware Japanese kaomoji to DeepSeek Harness (dsh) replies; prompt-injection plugin with a curated library from kaomojiya.org.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",