dsh-kaomoji 0.1.2 → 0.1.4

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,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/lang/zh-CN/
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.4] - 2026-09-25
11
+
12
+ ### Changed
13
+
14
+ - 设置卡片在保存失败时直接显示 Host 返回的真实原因(例如文件不可写、来源不受信任),
15
+ 不再只显示通用的“保存失败”,便于定位服务器端问题。
16
+
17
+ ## [0.1.3] - 2026-09-13
18
+
19
+ ### Fixed
20
+
21
+ - `maxPerTurn` 之前对输出没有实际约束力:提示词写死「一个」,调到 2/3 也不会变。
22
+ 现在 `frequent` 会按该上限在回复里分散放置多个颜文字,`auto` 在存在多个
23
+ 情绪点时也会用到上限;回复很短时自动少放。
24
+
10
25
  ## [0.1.2] - 2026-09-10
11
26
 
12
27
  ### Changed
package/README.en.md CHANGED
@@ -104,7 +104,7 @@ Example `cordis.patch.yml`:
104
104
  | --- | --- | --- | --- |
105
105
  | `mode` | `'off' \| 'auto' \| 'frequent'` | `'auto'` | `off` disables; `auto` adds one to greetings, casual chat, recommendations and empathetic replies; `frequent` adds one to every conversational reply (code-only/formal replies excluded) |
106
106
  | `placement` | `'inline' \| 'end'` | `'inline'` | Put the kaomoji after the best-matching sentence, or at the reply end |
107
- | `maxPerTurn` | `number` (1–5) | `1` | Max kaomoji per reply |
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
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 |
package/README.md CHANGED
@@ -120,7 +120,7 @@ pnpm add file:C:\path\to\dsh-kaomoji
120
120
  | --- | --- | --- | --- |
121
121
  | `mode` | `'off' \| 'auto' \| 'frequent'` | `'auto'` | `off` 关闭;`auto` 寒暄/闲聊/推荐建议/共情回复都会带一个;`frequent` 每条对话回复都带一个(两者都跳过纯代码 / 正式技术交付) |
122
122
  | `placement` | `'inline' \| 'end'` | `'inline'` | 放在情绪最贴切的句子/短段后,或固定在回复末尾 |
123
- | `maxPerTurn` | `number`(1–5) | `1` | 每条回复最多允许的颜文字数量 |
123
+ | `maxPerTurn` | `number`(1–5) | `1` | 每条回复的颜文字数量上限;`frequent` 会按这个数量分散放在不同句子后,回复太短时自动减少 |
124
124
  | `customPrompt` | `string` | `''` | 附加风格/场景说明;不能改变模式、白名单或数量上限 |
125
125
  | `settingsFile` | `string` | `~/.dsh/dsh-kaomoji.json` | (进阶)用户设置持久化文件路径 |
126
126
  | `settingsAuthority` | `'trusted-host' \| 'loopback'` | `'trusted-host'` | 设置 RPC 信任范围:默认允许本机与 Host 声明的受信主机(Tailscale/局域网)保存设置;改成 `loopback` 则仅本机可改 |
package/lib/client.js CHANGED
@@ -230,7 +230,9 @@ window.__ModuleLoader__.load({
230
230
  };
231
231
 
232
232
  const publish = (patch) => {
233
- snapshot = { ...snapshot, ...patch };
233
+ snapshot = patch.saveError === null
234
+ ? { ...snapshot, ...patch, saveErrorMessage: null }
235
+ : { ...snapshot, ...patch };
234
236
  emit();
235
237
  };
236
238
 
@@ -240,11 +242,12 @@ window.__ModuleLoader__.load({
240
242
  return task;
241
243
  };
242
244
 
243
- const fail = (code) => {
245
+ const fail = (code, message) => {
244
246
  publish({
245
247
  status: "unavailable",
246
248
  writable: false,
247
249
  saveError: code,
250
+ saveErrorMessage: message || null,
248
251
  justSaved: false,
249
252
  });
250
253
  };
@@ -253,11 +256,17 @@ window.__ModuleLoader__.load({
253
256
  try {
254
257
  const result = await rpc.call(RPC_CHANNEL, endpoint, payload);
255
258
  if (result && result.ok === true) return { value: result.value };
256
- const code = result && result.error && result.error.code;
257
- return { error: code === "forbidden" ? "forbidden" : "rejected" };
259
+ const error = (result && result.error) || {};
260
+ return {
261
+ error: error.code === "forbidden" ? "forbidden" : "rejected",
262
+ message: typeof error.message === "string" ? error.message : undefined,
263
+ };
258
264
  } catch (error) {
259
265
  const message = String((error && error.message) || error || "");
260
- return { error: /403|forbidden/i.test(message) ? "forbidden" : "unreachable" };
266
+ return {
267
+ error: /403|forbidden/i.test(message) ? "forbidden" : "unreachable",
268
+ message,
269
+ };
261
270
  }
262
271
  };
263
272
 
@@ -270,7 +279,7 @@ window.__ModuleLoader__.load({
270
279
  async refresh() {
271
280
  const result = await callRpc("get", {});
272
281
  if (result.error !== undefined) {
273
- fail(result.error === "forbidden" ? "forbidden" : "load");
282
+ fail(result.error === "forbidden" ? "forbidden" : "load", result.message);
274
283
  return;
275
284
  }
276
285
  const value = result.value;
@@ -297,7 +306,7 @@ window.__ModuleLoader__.load({
297
306
  expectedRevision: snapshot.revision,
298
307
  });
299
308
  if (result.error !== undefined) {
300
- fail(result.error === "forbidden" ? "forbidden" : "save");
309
+ fail(result.error === "forbidden" ? "forbidden" : "save", result.message);
301
310
  return;
302
311
  }
303
312
  const value = result.value;
@@ -316,7 +325,7 @@ window.__ModuleLoader__.load({
316
325
  publish({ saveError: null, justSaved: false });
317
326
  const result = await callRpc("reset", { expectedRevision: snapshot.revision });
318
327
  if (result.error !== undefined) {
319
- fail(result.error === "forbidden" ? "forbidden" : "save");
328
+ fail(result.error === "forbidden" ? "forbidden" : "save", result.message);
320
329
  return;
321
330
  }
322
331
  const value = result.value;
@@ -348,7 +357,9 @@ window.__ModuleLoader__.load({
348
357
  const statusText = state.saveError
349
358
  ? state.saveError === "forbidden"
350
359
  ? t("status.forbidden")
351
- : t("status.error")
360
+ : state.saveErrorMessage
361
+ ? `${t("status.error")} ${state.saveErrorMessage}`
362
+ : t("status.error")
352
363
  : state.status === "loading"
353
364
  ? t("status.loading")
354
365
  : !state.writable
package/lib/index.js CHANGED
@@ -144,20 +144,32 @@ export function buildGuidance(config) {
144
144
  const settings = normalizeConfig(config);
145
145
  if (settings.mode === "off") return "";
146
146
 
147
+ const cap = settings.maxPerTurn;
147
148
  const modeLines = {
148
149
  auto: [
149
- "Most conversational replies should include one fitting kaomoji — greetings, casual chat, empathy, everyday recommendations or shopping advice, and practical how-to answers.",
150
+ "Most conversational replies should include a fitting kaomoji — greetings, casual chat, empathy, everyday recommendations or shopping advice, and practical how-to answers.",
151
+ cap === 1
152
+ ? "Use one kaomoji in those replies."
153
+ : `Use one kaomoji when the mood fits, and up to ${String(cap)} when the reply has several distinct emotional beats.`,
150
154
  "Skip it only when the reply is purely code, a formal/technical deliverable, or serious/high-stakes content.",
151
- "One per reply is usually enough; never replace real content with it.",
152
155
  ],
153
156
  frequent: [
154
- "In every conversational reply — except code-only output, formal/technical deliverables, or tool calls — include one fitting kaomoji.",
157
+ "In every conversational reply — except code-only output, formal/technical deliverables, or tool calls — include fitting kaomoji.",
158
+ cap === 1
159
+ ? "Use exactly one kaomoji."
160
+ : `Use ${String(cap)} kaomoji, one after each of ${String(cap)} different emotion-carrying sentences; use fewer only when the reply is very short.`,
155
161
  ],
156
162
  };
157
- const placementLine =
158
- settings.placement === "end"
163
+ const placementLine = settings.placement === "end"
164
+ ? cap === 1
159
165
  ? "Put the kaomoji at the very end of the reply (same line after a space, or on its own line)."
160
- : "Put it right after the sentence or short paragraph whose mood it matches best.";
166
+ : "Put each kaomoji at the end of a different paragraph (after the sentence it matches), not all on the final line."
167
+ : cap === 1
168
+ ? "Put it right after the sentence or short paragraph whose mood it matches best."
169
+ : "Put each kaomoji right after a different sentence or short paragraph whose mood it matches best.";
170
+ const spreadLine = cap > 1
171
+ ? "Distribute the kaomoji across the reply; never put two in the same sentence, and do not stack them all at the very end."
172
+ : "Never replace real content with a kaomoji.";
161
173
 
162
174
  const poolLines = CATALOG.map(
163
175
  (group) => `${group.label}: ${group.examples.join(" | ")}`,
@@ -168,7 +180,8 @@ export function buildGuidance(config) {
168
180
  "[dsh-kaomoji] Kaomoji guidance for this reply:",
169
181
  ...modeLines[settings.mode],
170
182
  placementLine,
171
- `Use at most ${String(settings.maxPerTurn)} kaomoji per reply.`,
183
+ spreadLine,
184
+ `Never exceed ${String(cap)} kaomoji in one reply.`,
172
185
  "Never place a kaomoji inside code blocks, inline code, links, tables, or tool output.",
173
186
  "Use only the literal kaomoji listed below, copied character-for-character; do not invent, mutate, or re-combine them.",
174
187
  ...poolLines,
@@ -359,10 +372,9 @@ function createSettingsRpcHandler(ctx, settingsFile, getState, commit) {
359
372
  return rpcError("bad-request", `Unknown dsh-kaomoji settings operation: ${endpoint}`);
360
373
  });
361
374
  } catch (error) {
362
- ctx.logger?.warn?.(
363
- `[dsh-kaomoji] settings rpc failed: ${error instanceof Error ? error.message : String(error)}`,
364
- );
365
- return rpcError("settings-rejected", "The Host rejected the kaomoji settings.");
375
+ const reason = error instanceof Error ? error.message : String(error);
376
+ console.warn(`[dsh-kaomoji] settings rpc failed: ${reason}`);
377
+ return rpcError("settings-rejected", `The Host rejected the kaomoji settings: ${reason}`);
366
378
  }
367
379
  };
368
380
  }
@@ -410,7 +422,7 @@ export function apply(ctx, config = {}) {
410
422
 
411
423
  // 设置卡片通过 loopback RPC 读写 ~/.dsh/dsh-kaomoji.json;写入后立即
412
424
  // 更新提示词段(emit system-prompt/change),无需重启。
413
- ctx.inject(["connection"], (connectionCtx) => {
425
+ const registerSettingsRpc = (connectionCtx) => {
414
426
  const handler = createSettingsRpcHandler(ctx, settingsFile, getState, adoptSettings);
415
427
  connectionCtx.effect(
416
428
  () =>
@@ -419,9 +431,18 @@ export function apply(ctx, config = {}) {
419
431
  }),
420
432
  "dsh-kaomoji: settings rpc",
421
433
  );
422
- });
434
+ console.log(
435
+ `[dsh-kaomoji] settings rpc 已注册(channel=${SETTINGS_RPC_CHANNEL}, authority=${authority})`,
436
+ );
437
+ };
438
+ // connection 服务在多数部署里此刻已可用;拿不到再等 inject(旧版行为)。
439
+ if (typeof ctx.get === "function" && ctx.get("connection") !== undefined) {
440
+ registerSettingsRpc(ctx);
441
+ } else {
442
+ ctx.inject(["connection"], registerSettingsRpc);
443
+ }
423
444
 
424
- ctx.logger?.info?.(
445
+ console.log(
425
446
  `[dsh-kaomoji] 已挂载(mode=${currentSettings.mode}, placement=${currentSettings.placement}, authority=${authority}, settingsFile=${settingsFile})`,
426
447
  );
427
448
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-kaomoji",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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",