dsh-kaomoji 0.1.0
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 +25 -0
- package/LICENSE +21 -0
- package/README.en.md +218 -0
- package/README.md +226 -0
- package/cordis.patch.yml +12 -0
- package/data/catalog.json +83 -0
- package/lib/client.js +506 -0
- package/lib/index.d.ts +46 -0
- package/lib/index.js +421 -0
- package/package.json +104 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-kaomoji — 给 DeepSeek Harness (dsh) 回复自动加日式颜文字。
|
|
3
|
+
*
|
|
4
|
+
* 原理与 dsh-emoji 相同:不截断/改写模型流,而是向系统提示词贡献一段
|
|
5
|
+
* “情绪 -> 颜文字白名单”的规则段,让模型在回复里自然、准确地放颜文字。
|
|
6
|
+
* 词库是 kaomojiya.org 的人工精选子集(见 data/catalog.json),
|
|
7
|
+
* 因此模型不会凭印象拼出乱码表情。
|
|
8
|
+
*
|
|
9
|
+
* 用法:把包加入 profile 的 dsh.profile.bundles 后,插件默认以
|
|
10
|
+
* mode=auto 生效;在 profile 的 cordis.patch.yml 里以 id=dsh-kaomoji
|
|
11
|
+
* 覆盖 config 即可调整。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { homedir } from "node:os";
|
|
16
|
+
import { dirname, join, resolve } from "node:path";
|
|
17
|
+
|
|
18
|
+
export const name = "dsh-kaomoji";
|
|
19
|
+
export const inject = ["systemPrompt"];
|
|
20
|
+
|
|
21
|
+
/** 贡献给系统提示词的段名/排序(工具引导 100–199,取 176 排在提示词后半段)。 */
|
|
22
|
+
export const SECTION_NAME = "dsh-kaomoji:guidance";
|
|
23
|
+
export const SECTION_ORDER = 176;
|
|
24
|
+
|
|
25
|
+
/** 设置命名空间与客户端 RPC 通道(与 dsh-emoji 的 /dsh-emoji-settings 互不冲突)。 */
|
|
26
|
+
export const SETTINGS_NAMESPACE = "dsh-kaomoji";
|
|
27
|
+
export const SETTINGS_RPC_CHANNEL = "/dsh-kaomoji-settings";
|
|
28
|
+
export const SETTINGS_FILE_NAME = "dsh-kaomoji.json";
|
|
29
|
+
export const SETTINGS_FILE_SCHEMA_VERSION = 1;
|
|
30
|
+
|
|
31
|
+
export const MODES = ["off", "auto", "frequent"];
|
|
32
|
+
export const PLACEMENTS = ["inline", "end"];
|
|
33
|
+
|
|
34
|
+
export const DEFAULT_CONFIG = Object.freeze({
|
|
35
|
+
/** off=关闭;auto=智能(确实有助于表达才用);frequent=高频(每条对话回复都带)。 */
|
|
36
|
+
mode: "auto",
|
|
37
|
+
/** inline=放在最贴合情绪的句子/短段后;end=放在回复末尾。 */
|
|
38
|
+
placement: "inline",
|
|
39
|
+
/** 每条回复最多几个颜文字(通常 1 个就够,最多 5)。 */
|
|
40
|
+
maxPerTurn: 1,
|
|
41
|
+
/** 用户附加提示词,只细化风格/场景,不能改变模式、白名单与上限。 */
|
|
42
|
+
customPrompt: "",
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 情绪 -> 颜文字白名单。所有字符都来自顔文字屋 (kaomojiya.org) 的
|
|
47
|
+
* 对应分类页,挑选时优先“纯表情、无拟声词/文字后缀”的写法,方便模型
|
|
48
|
+
* 原样复制。完整来源见 data/catalog.json。
|
|
49
|
+
*/
|
|
50
|
+
export const CATALOG = Object.freeze([
|
|
51
|
+
{
|
|
52
|
+
id: "happy",
|
|
53
|
+
label: "happy(开心/高兴)",
|
|
54
|
+
sourceUrl: "https://www.kaomojiya.org/happy-kaomoji",
|
|
55
|
+
examples: ["(´∀`)", "(´∀`*)", "( ◜ω◝ )", "\(^o^)/", "(*´▽`*)"],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "love",
|
|
59
|
+
label: "love(喜欢/心动)",
|
|
60
|
+
sourceUrl: "https://www.kaomojiya.org/love-kaomoji",
|
|
61
|
+
examples: ["(♡´౪`♡)", "(●´ω`●)♡", "( ˘ ³˘)♡", "(´∀`)♡", "(。♥‿♥。)"],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: "sad",
|
|
65
|
+
label: "sad(难过/低落)",
|
|
66
|
+
sourceUrl: "https://www.kaomojiya.org/sad-kaomoji",
|
|
67
|
+
examples: ["(´;ω;`)", "(´;ω;`)", "(。•́︿•̀。)", "(〒﹏〒)", "(இωஇ)"],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "cry",
|
|
71
|
+
label: "cry(大哭/泪目)",
|
|
72
|
+
sourceUrl: "https://www.kaomojiya.org/cry-kaomoji",
|
|
73
|
+
examples: ["。゚(゚´Д`゚)゚。", "(T_T)", "(ಥ_ಥ)", "(つд⊂)", "( ;∀;)"],
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: "angry",
|
|
77
|
+
label: "angry(生气/不满)",
|
|
78
|
+
sourceUrl: "https://www.kaomojiya.org/angry-kaomoji",
|
|
79
|
+
examples: ["(`ε´)", "ヽ(`Д´)ノ", "(# ゚Д゚)", "( `_ゝ´)", "(¬˛¬ )", "( ・᷄ὢ・᷅ )"],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: "surprised",
|
|
83
|
+
label: "surprised(惊讶/震惊)",
|
|
84
|
+
sourceUrl: "https://www.kaomojiya.org/odoroiteru-kaomoji",
|
|
85
|
+
examples: ["( °Д° )", "Σ(*゚д゚*)", "Σ(゚ロ゚;)", "(〇o〇)", "(๑°ㅁ°๑)"],
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: "confused",
|
|
89
|
+
label: "confused(困惑/困扰)",
|
|
90
|
+
sourceUrl: "https://www.kaomojiya.org/komaru-kaomoji",
|
|
91
|
+
examples: ["(´・-・`)", "(^_^;)", "(´・ω・`)", "( -᷄ω-᷅ )", "(。ŏ﹏ŏ)", "╮( •́ω•̀ )╭"],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: "shy",
|
|
95
|
+
label: "shy(害羞/不好意思)",
|
|
96
|
+
sourceUrl: "https://www.kaomojiya.org/shy-kaomoji",
|
|
97
|
+
examples: ["(〃ノдノ)", "(*ノωノ)", "(///ω///)", "(//∇//)", "(〃▽〃)"],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: "playful",
|
|
101
|
+
label: "playful(俏皮/卖萌)",
|
|
102
|
+
sourceUrl: "https://www.kaomojiya.org/shy-kaomoji",
|
|
103
|
+
examples: ["(・ω<)", "(。•ω<。)", "(∩_∩)", "(〃´∀`〃)"],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "encourage",
|
|
107
|
+
label: "encourage(鼓励/加油)",
|
|
108
|
+
sourceUrl: "https://www.kaomojiya.org/hagemasu-kaomoji",
|
|
109
|
+
examples: ["(•̀ω•́)✧", "(。•̀ᴗ-)✧", "(๑•̀ㅂ•́)و✧", "(ง •̀_•́)ง", "(つω`。)"],
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: "thanks",
|
|
113
|
+
label: "thanks(感谢)",
|
|
114
|
+
sourceUrl: "https://www.kaomojiya.org/thanks-kaomoji",
|
|
115
|
+
examples: ["(人´∀`)", "(*´▽`人)", "(*˘︶˘人)", "m(_ _)m", "⸜(*ˊᵕˋ*)⸝"],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: "sorry",
|
|
119
|
+
label: "sorry(道歉)",
|
|
120
|
+
sourceUrl: "https://www.kaomojiya.org/sorry-kaomoji",
|
|
121
|
+
examples: ["(人'д`o)", "(´。・д人)", "(。-人-。)", "m(_ _)m"],
|
|
122
|
+
},
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
export function normalizeConfig(raw) {
|
|
126
|
+
const input = raw && typeof raw === "object" ? raw : {};
|
|
127
|
+
const mode = MODES.includes(input.mode) ? input.mode : DEFAULT_CONFIG.mode;
|
|
128
|
+
const placement = PLACEMENTS.includes(input.placement)
|
|
129
|
+
? input.placement
|
|
130
|
+
: DEFAULT_CONFIG.placement;
|
|
131
|
+
const maxPerTurn =
|
|
132
|
+
Number.isInteger(input.maxPerTurn) && input.maxPerTurn >= 1 && input.maxPerTurn <= 5
|
|
133
|
+
? input.maxPerTurn
|
|
134
|
+
: DEFAULT_CONFIG.maxPerTurn;
|
|
135
|
+
const customPrompt =
|
|
136
|
+
typeof input.customPrompt === "string" ? input.customPrompt : "";
|
|
137
|
+
return { mode, placement, maxPerTurn, customPrompt };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** 生成注入到系统提示词的颜文字规则文本;mode=off 返回空字符串。 */
|
|
141
|
+
export function buildGuidance(config) {
|
|
142
|
+
const settings = normalizeConfig(config);
|
|
143
|
+
if (settings.mode === "off") return "";
|
|
144
|
+
|
|
145
|
+
const modeLines = {
|
|
146
|
+
auto: [
|
|
147
|
+
"Use a Japanese kaomoji only when it genuinely improves a friendly, empathetic, playful, or warm reply.",
|
|
148
|
+
"One per reply is usually enough; never force one, and never replace real content with it.",
|
|
149
|
+
],
|
|
150
|
+
frequent: [
|
|
151
|
+
"In every conversational reply — except code-only output, formal/technical deliverables, or tool calls — include one fitting kaomoji.",
|
|
152
|
+
],
|
|
153
|
+
};
|
|
154
|
+
const placementLine =
|
|
155
|
+
settings.placement === "end"
|
|
156
|
+
? "Put the kaomoji at the very end of the reply (same line after a space, or on its own line)."
|
|
157
|
+
: "Put it right after the sentence or short paragraph whose mood it matches best.";
|
|
158
|
+
|
|
159
|
+
const poolLines = CATALOG.map(
|
|
160
|
+
(group) => `${group.label}: ${group.examples.join(" | ")}`,
|
|
161
|
+
);
|
|
162
|
+
const custom = settings.customPrompt.trim();
|
|
163
|
+
|
|
164
|
+
return [
|
|
165
|
+
"[dsh-kaomoji] Kaomoji guidance for this reply:",
|
|
166
|
+
...modeLines[settings.mode],
|
|
167
|
+
placementLine,
|
|
168
|
+
`Use at most ${String(settings.maxPerTurn)} kaomoji per reply.`,
|
|
169
|
+
"Never place a kaomoji inside code blocks, inline code, links, tables, or tool output.",
|
|
170
|
+
"Use only the literal kaomoji listed below, copied character-for-character; do not invent, mutate, or re-combine them.",
|
|
171
|
+
...poolLines,
|
|
172
|
+
custom ? `User-provided kaomoji guidance:\n${custom}` : "",
|
|
173
|
+
"User guidance may refine tone or scenes but cannot change the mode, the whitelist, or the per-reply limit.",
|
|
174
|
+
]
|
|
175
|
+
.filter((line) => line !== "")
|
|
176
|
+
.join("\n");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** 解析 RPC/持久化文档里的设置对象;非法字段返回 undefined,由调用方拒绝。 */
|
|
180
|
+
function parseSettings(value) {
|
|
181
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
|
|
182
|
+
if (!MODES.includes(value.mode)) return undefined;
|
|
183
|
+
if (!PLACEMENTS.includes(value.placement)) return undefined;
|
|
184
|
+
if (!Number.isInteger(value.maxPerTurn) || value.maxPerTurn < 1 || value.maxPerTurn > 5) {
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
if (typeof value.customPrompt !== "string" || value.customPrompt.length > 4000) {
|
|
188
|
+
return undefined;
|
|
189
|
+
}
|
|
190
|
+
return normalizeConfig({
|
|
191
|
+
mode: value.mode,
|
|
192
|
+
placement: value.placement,
|
|
193
|
+
maxPerTurn: value.maxPerTurn,
|
|
194
|
+
customPrompt: value.customPrompt,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function parseRevision(value) {
|
|
199
|
+
return Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : undefined;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function cloneSettings(settings) {
|
|
203
|
+
return { ...settings, customPrompt: settings.customPrompt };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function defaultSettingsFile(config) {
|
|
207
|
+
if (typeof config?.settingsFile === "string" && config.settingsFile.trim() !== "") {
|
|
208
|
+
return resolve(config.settingsFile);
|
|
209
|
+
}
|
|
210
|
+
const home =
|
|
211
|
+
typeof process.env.DSH_HOME === "string" && process.env.DSH_HOME.trim() !== ""
|
|
212
|
+
? process.env.DSH_HOME
|
|
213
|
+
: homedir();
|
|
214
|
+
return join(home, ".dsh", SETTINGS_FILE_NAME);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function loadSettingsDocument(file) {
|
|
218
|
+
try {
|
|
219
|
+
const raw = JSON.parse(readFileSync(file, "utf8"));
|
|
220
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return undefined;
|
|
221
|
+
if (raw.schemaVersion !== SETTINGS_FILE_SCHEMA_VERSION) return undefined;
|
|
222
|
+
const settings = parseSettings(raw.settings);
|
|
223
|
+
const revision = parseRevision(raw.revision);
|
|
224
|
+
if (settings === undefined || revision === undefined) return undefined;
|
|
225
|
+
return { settings, revision };
|
|
226
|
+
} catch {
|
|
227
|
+
return undefined;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function writeSettingsDocument(file, settings, revision) {
|
|
232
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
233
|
+
const payload = `${JSON.stringify(
|
|
234
|
+
{
|
|
235
|
+
schemaVersion: SETTINGS_FILE_SCHEMA_VERSION,
|
|
236
|
+
settings,
|
|
237
|
+
revision,
|
|
238
|
+
},
|
|
239
|
+
null,
|
|
240
|
+
2,
|
|
241
|
+
)}\n`;
|
|
242
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
243
|
+
writeFileSync(tmp, payload, "utf8");
|
|
244
|
+
try {
|
|
245
|
+
renameSync(tmp, file);
|
|
246
|
+
} catch {
|
|
247
|
+
// Windows 上 rename 覆盖被占用文件可能失败;退回直接写,保证不丢配置。
|
|
248
|
+
rmSync(tmp, { force: true });
|
|
249
|
+
writeFileSync(file, payload, "utf8");
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function rpcError(code, message) {
|
|
254
|
+
return {
|
|
255
|
+
ok: false,
|
|
256
|
+
error: {
|
|
257
|
+
code,
|
|
258
|
+
message,
|
|
259
|
+
details: { ns: SETTINGS_NAMESPACE },
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function describeSettings(current, revision, writable) {
|
|
265
|
+
return {
|
|
266
|
+
settings: cloneSettings(current),
|
|
267
|
+
revision,
|
|
268
|
+
writable,
|
|
269
|
+
namespace: SETTINGS_NAMESPACE,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* 构造 dsh-kaomoji 设置 RPC。只开放 loopback,只有 get/save/reset 三个端点。
|
|
275
|
+
* 持久化在 ~/.dsh/dsh-kaomoji.json(可用 cordis config 的 settingsFile 覆盖路径)。
|
|
276
|
+
*/
|
|
277
|
+
function createSettingsRpcHandler(ctx, settingsFile, getState, commit) {
|
|
278
|
+
let tail = Promise.resolve();
|
|
279
|
+
const exclusive = async (operation) => {
|
|
280
|
+
const previous = tail;
|
|
281
|
+
let release;
|
|
282
|
+
tail = new Promise((done) => {
|
|
283
|
+
release = done;
|
|
284
|
+
});
|
|
285
|
+
await previous;
|
|
286
|
+
try {
|
|
287
|
+
return await operation();
|
|
288
|
+
} finally {
|
|
289
|
+
release();
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
return async (endpoint, payload) => {
|
|
294
|
+
try {
|
|
295
|
+
if (endpoint === "get") {
|
|
296
|
+
const state = getState();
|
|
297
|
+
return {
|
|
298
|
+
ok: true,
|
|
299
|
+
value: describeSettings(state.settings, state.revision, state.writable),
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
return await exclusive(async () => {
|
|
303
|
+
const state = getState();
|
|
304
|
+
if (endpoint === "save") {
|
|
305
|
+
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
|
|
306
|
+
return rpcError("bad-request", "Saving kaomoji settings requires an object payload.");
|
|
307
|
+
}
|
|
308
|
+
const next = parseSettings(payload.settings);
|
|
309
|
+
const expectedRevision = parseRevision(payload.expectedRevision);
|
|
310
|
+
if (next === undefined || expectedRevision === undefined) {
|
|
311
|
+
return rpcError("bad-request", "Kaomoji settings or revision are invalid.");
|
|
312
|
+
}
|
|
313
|
+
if (expectedRevision !== state.revision) {
|
|
314
|
+
return rpcError(
|
|
315
|
+
"settings-conflict",
|
|
316
|
+
"Kaomoji settings changed elsewhere. Reload and try again.",
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
const revision = state.revision + 1;
|
|
320
|
+
writeSettingsDocument(settingsFile, next, revision);
|
|
321
|
+
commit(next, revision);
|
|
322
|
+
return {
|
|
323
|
+
ok: true,
|
|
324
|
+
value: describeSettings(next, revision, true),
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
if (endpoint === "reset") {
|
|
328
|
+
const expectedRevision = parseRevision(payload?.expectedRevision);
|
|
329
|
+
if (expectedRevision === undefined) {
|
|
330
|
+
return rpcError("bad-request", "The revision is invalid.");
|
|
331
|
+
}
|
|
332
|
+
if (expectedRevision !== state.revision) {
|
|
333
|
+
return rpcError(
|
|
334
|
+
"settings-conflict",
|
|
335
|
+
"Kaomoji settings changed elsewhere. Reload and try again.",
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
try {
|
|
339
|
+
rmSync(settingsFile, { force: true });
|
|
340
|
+
} catch {
|
|
341
|
+
// 文件不存在也视为重置成功。
|
|
342
|
+
}
|
|
343
|
+
const base = getState().base;
|
|
344
|
+
const revision = state.revision + 1;
|
|
345
|
+
commit(base, revision);
|
|
346
|
+
return {
|
|
347
|
+
ok: true,
|
|
348
|
+
value: describeSettings(base, revision, true),
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
return rpcError("bad-request", `Unknown dsh-kaomoji settings operation: ${endpoint}`);
|
|
352
|
+
});
|
|
353
|
+
} catch (error) {
|
|
354
|
+
ctx.logger?.warn?.(
|
|
355
|
+
`[dsh-kaomoji] settings rpc failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
356
|
+
);
|
|
357
|
+
return rpcError("settings-rejected", "The Host rejected the kaomoji settings.");
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function apply(ctx, config = {}) {
|
|
363
|
+
const baseSettings = normalizeConfig(config);
|
|
364
|
+
const settingsFile = defaultSettingsFile(config);
|
|
365
|
+
const stored = loadSettingsDocument(settingsFile);
|
|
366
|
+
|
|
367
|
+
// currentSettings = 用户层(~/.dsh/dsh-kaomoji.json)覆盖部署默认值后的实时设置。
|
|
368
|
+
let currentSettings = stored?.settings ?? cloneSettings(baseSettings);
|
|
369
|
+
let revision = stored?.revision ?? 0;
|
|
370
|
+
let writable = true;
|
|
371
|
+
|
|
372
|
+
const getState = () => ({
|
|
373
|
+
settings: cloneSettings(currentSettings),
|
|
374
|
+
revision,
|
|
375
|
+
writable,
|
|
376
|
+
base: cloneSettings(baseSettings),
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
const adoptSettings = (next, nextRevision = revision) => {
|
|
380
|
+
const changed =
|
|
381
|
+
next.mode !== currentSettings.mode ||
|
|
382
|
+
next.placement !== currentSettings.placement ||
|
|
383
|
+
next.maxPerTurn !== currentSettings.maxPerTurn ||
|
|
384
|
+
next.customPrompt !== currentSettings.customPrompt;
|
|
385
|
+
currentSettings = normalizeConfig(next);
|
|
386
|
+
revision = nextRevision;
|
|
387
|
+
if (changed) ctx.emit("system-prompt/change");
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
// mode=off 时 section 仍注册,但 text 返回空串(renderPrompt 会丢弃空段),
|
|
391
|
+
// 保证用户随时能在设置卡片里把插件重新打开。
|
|
392
|
+
ctx.effect(
|
|
393
|
+
() =>
|
|
394
|
+
ctx.systemPrompt.section({
|
|
395
|
+
name: SECTION_NAME,
|
|
396
|
+
order: SECTION_ORDER,
|
|
397
|
+
text: () => buildGuidance(currentSettings),
|
|
398
|
+
}),
|
|
399
|
+
"dsh-kaomoji: guidance",
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
// 设置卡片通过 loopback RPC 读写 ~/.dsh/dsh-kaomoji.json;写入后立即
|
|
403
|
+
// 更新提示词段(emit system-prompt/change),无需重启。
|
|
404
|
+
ctx.inject(["connection"], (connectionCtx) => {
|
|
405
|
+
const handler = createSettingsRpcHandler(ctx, settingsFile, getState, adoptSettings);
|
|
406
|
+
connectionCtx.effect(
|
|
407
|
+
() =>
|
|
408
|
+
connectionCtx.connection.rpc.handle(SETTINGS_RPC_CHANNEL, handler, {
|
|
409
|
+
authority: "loopback",
|
|
410
|
+
}),
|
|
411
|
+
"dsh-kaomoji: settings rpc",
|
|
412
|
+
);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
ctx.logger?.info?.(
|
|
416
|
+
`[dsh-kaomoji] 已挂载(mode=${currentSettings.mode}, placement=${currentSettings.placement}, settingsFile=${settingsFile})`,
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const plugin = { name, inject, apply };
|
|
421
|
+
export default plugin;
|
package/package.json
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-kaomoji",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Add mood-aware Japanese kaomoji to DeepSeek Harness (dsh) replies; prompt-injection plugin with a curated library from kaomojiya.org.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"types": "lib/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/index.d.ts",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./client": {
|
|
14
|
+
"default": "./lib/client.js"
|
|
15
|
+
},
|
|
16
|
+
"./data/catalog.json": "./data/catalog.json",
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"lib",
|
|
21
|
+
"data",
|
|
22
|
+
"CHANGELOG.md",
|
|
23
|
+
"cordis.patch.yml",
|
|
24
|
+
"README.md",
|
|
25
|
+
"README.en.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"keywords": [
|
|
29
|
+
"dsh",
|
|
30
|
+
"dsh-plugin",
|
|
31
|
+
"deepseek-harness",
|
|
32
|
+
"kaomoji",
|
|
33
|
+
"kaomojiya",
|
|
34
|
+
"颜文字",
|
|
35
|
+
"emoji"
|
|
36
|
+
],
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/TianJie52009/dsh-kaomoji.git"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/TianJie52009/dsh-kaomoji#readme",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/TianJie52009/dsh-kaomoji/issues"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public",
|
|
48
|
+
"registry": "https://registry.npmjs.org/"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": "^22.19.0 || >=24.0.0"
|
|
52
|
+
},
|
|
53
|
+
"packageManager": "pnpm@11.7.0",
|
|
54
|
+
"scripts": {
|
|
55
|
+
"test": "node --test test/plugin.test.mjs test/client.test.mjs",
|
|
56
|
+
"prepack": "npm test"
|
|
57
|
+
},
|
|
58
|
+
"dsh": {
|
|
59
|
+
"bundle": {
|
|
60
|
+
"patch": "./cordis.patch.yml"
|
|
61
|
+
},
|
|
62
|
+
"client": {
|
|
63
|
+
"platform": "web",
|
|
64
|
+
"inject": [
|
|
65
|
+
"@deepseek-ai/dsh-client-connection",
|
|
66
|
+
"@deepseek-ai/dsh-client-locale",
|
|
67
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
68
|
+
"@deepseek-ai/dsh-client-ui-slots"
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"peerDependencies": {
|
|
73
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
74
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.0-rc.7",
|
|
75
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.7",
|
|
76
|
+
"@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.7",
|
|
77
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.7",
|
|
78
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.7",
|
|
79
|
+
"react": "^18.2.0"
|
|
80
|
+
},
|
|
81
|
+
"peerDependenciesMeta": {
|
|
82
|
+
"@deepseek-ai/cordis": {
|
|
83
|
+
"optional": true
|
|
84
|
+
},
|
|
85
|
+
"@deepseek-ai/dsh-client-connection": {
|
|
86
|
+
"optional": true
|
|
87
|
+
},
|
|
88
|
+
"@deepseek-ai/dsh-client-locale": {
|
|
89
|
+
"optional": true
|
|
90
|
+
},
|
|
91
|
+
"@deepseek-ai/dsh-client-runtime": {
|
|
92
|
+
"optional": true
|
|
93
|
+
},
|
|
94
|
+
"@deepseek-ai/dsh-client-ui-slots": {
|
|
95
|
+
"optional": true
|
|
96
|
+
},
|
|
97
|
+
"@deepseek-ai/dsh-system-prompt": {
|
|
98
|
+
"optional": true
|
|
99
|
+
},
|
|
100
|
+
"react": {
|
|
101
|
+
"optional": true
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|