dsh-caveman 0.1.3 → 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/lib/client.js CHANGED
@@ -6,6 +6,110 @@ window.__ModuleLoader__.load({
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
7
  const React = require("react");
8
8
 
9
+ // ---- Settings → General row: caveman default mode --------------------
10
+ // Reads and writes the host's global default through /caveman/default
11
+ // (the settings-document namespace 'caveman'). New sessions start with
12
+ // this default; the badge still cycles the per-session value. Mirrors
13
+ // dsh-rtk's row — without this client-side slot registration the settings
14
+ // page shows no caveman entry at all (the host namespace alone is not
15
+ // enough; the panel enumerates slot items, not namespaces).
16
+ const CAVEMAN_MODES = [
17
+ "off",
18
+ "lite",
19
+ "full",
20
+ "ultra",
21
+ "wenyan-lite",
22
+ "wenyan-full",
23
+ "wenyan-ultra",
24
+ ];
25
+ function CavemanDefaultRow() {
26
+ const [mode, setMode] = React.useState(null);
27
+ React.useEffect(() => {
28
+ let cancelled = false;
29
+ fetch("/caveman/default")
30
+ .then((r) => (r.ok ? r.json() : null))
31
+ .then((data) => {
32
+ if (!cancelled && data && CAVEMAN_MODES.indexOf(data.defaultMode) !== -1) {
33
+ setMode(data.defaultMode);
34
+ }
35
+ })
36
+ .catch(() => {});
37
+ return () => {
38
+ cancelled = true;
39
+ };
40
+ }, []);
41
+ const change = (next) => {
42
+ fetch("/caveman/default", {
43
+ method: "POST",
44
+ headers: { "content-type": "application/json" },
45
+ body: JSON.stringify({ defaultMode: next }),
46
+ })
47
+ .then((r) => (r.ok ? r.json() : null))
48
+ .then((data) => {
49
+ if (data && data.ok) setMode(data.defaultMode);
50
+ })
51
+ .catch(() => {});
52
+ };
53
+ return React.createElement(
54
+ "div",
55
+ {
56
+ style: {
57
+ borderBottom: "1px solid var(--dsw-alias-border-l2)",
58
+ alignItems: "center",
59
+ gap: "8px",
60
+ padding: "16px 0",
61
+ display: "flex",
62
+ },
63
+ },
64
+ React.createElement(
65
+ "div",
66
+ {
67
+ style: {
68
+ flexDirection: "column",
69
+ flex: "1",
70
+ gap: "4px",
71
+ minWidth: "0",
72
+ paddingRight: "48px",
73
+ display: "flex",
74
+ },
75
+ },
76
+ React.createElement(
77
+ "div",
78
+ { style: { color: "var(--dsw-alias-label-primary)", fontSize: "14px", lineHeight: "22px" } },
79
+ "caveman 默认档位",
80
+ ),
81
+ React.createElement(
82
+ "div",
83
+ { style: { color: "var(--dsw-alias-label-tertiary)", fontSize: "12px", lineHeight: "18px" } },
84
+ "新会话默认档位(默认 full);徽章仍按会话切换",
85
+ ),
86
+ ),
87
+ React.createElement(
88
+ "select",
89
+ {
90
+ value: mode === null ? "" : mode,
91
+ disabled: mode === null,
92
+ onChange: (e) => change(e.target.value),
93
+ title: mode === null ? "loading" : mode,
94
+ style: {
95
+ background: "var(--dsw-alias-bg-module-platform)",
96
+ height: "36px",
97
+ color: "var(--dsw-alias-label-primary)",
98
+ cursor: "pointer",
99
+ border: "none",
100
+ borderRadius: "18px",
101
+ padding: "0 14px",
102
+ fontSize: "14px",
103
+ lineHeight: "22px",
104
+ },
105
+ },
106
+ mode === null
107
+ ? React.createElement("option", { value: "" }, "…")
108
+ : CAVEMAN_MODES.map((m) => React.createElement("option", { key: m, value: m }, m)),
109
+ ),
110
+ );
111
+ }
112
+
9
113
  // Badge display is centralized in dsh-badges: this plugin only registers
10
114
  // a badge descriptor (fetch-mode: initial GET + toggle-response self-
11
115
  // update + 5s re-sync). No session projection is involved; per-session
@@ -74,6 +178,24 @@ window.__ModuleLoader__.load({
74
178
  if (register()) return;
75
179
  Promise.resolve().then(() => tryRegister(500));
76
180
  });
181
+
182
+ // Settings → General row: registered when the settings section is
183
+ // declared (slots.inject defers until then). The row owns its own
184
+ // read/write via /caveman/default — no inject face needed.
185
+ ctx.slots.inject("settings.general.item", () => {
186
+ try {
187
+ ctx.slots.register(
188
+ {
189
+ name: "settings.general.item",
190
+ id: "caveman-default",
191
+ order: 11,
192
+ },
193
+ CavemanDefaultRow,
194
+ );
195
+ } catch (e) {
196
+ console.error("[dsh-caveman] settings row registration failed:", e);
197
+ }
198
+ });
77
199
  }
78
200
 
79
201
  exports.apply = apply;
package/lib/index.js CHANGED
@@ -21,7 +21,8 @@ import { appendFileSync, mkdirSync, readFileSync, renameSync, writeFileSync } fr
21
21
  import { dirname, join } from 'node:path'
22
22
  import { KNOWN_SESSION_EVENT_TYPES } from '@deepseek-ai/dsh-session'
23
23
  import { dshHomePath } from '@deepseek-ai/dsh-home-paths'
24
- import { settingsNamespace } from '@deepseek-ai/dsh-settings'
24
+ // NOTE: no `settingsNamespace` import — the brand helper was removed from
25
+ // dsh-settings@0.1.2+ (see dsh-rtk for the same fix). Namespace is a literal.
25
26
  import zs from '@deepseek-ai/schemastery'
26
27
 
27
28
  const require = createRequire(import.meta.url)
@@ -82,7 +83,7 @@ export function apply(ctx) {
82
83
  if (settings !== undefined) {
83
84
  try {
84
85
  cavemanScope = settings.register(
85
- settingsNamespace('caveman'),
86
+ 'caveman',
86
87
  zs.object({ defaultMode: zs.union(VALID.map((v) => zs.const(v))).default('full') }),
87
88
  { applies: 'live' },
88
89
  )
@@ -255,6 +256,67 @@ export function apply(ctx) {
255
256
  }
256
257
  }
257
258
 
259
+ // ---- Settings row read/write: GET/POST /caveman/default -----------------
260
+ // The Settings → General row ('caveman-default') reads and writes the global
261
+ // default mode through this route; all state lives in the settings document
262
+ // via the registered 'caveman' namespace (same shape as dsh-rtk's
263
+ // /rtk/default, which is the only reason rtk showed a row and caveman did
264
+ // not — the row is a CLIENT registration, not an automatic consequence of
265
+ // the namespace).
266
+ if (webServer !== undefined) {
267
+ try {
268
+ webServer.register({
269
+ kind: 'exact',
270
+ path: '/caveman/default',
271
+ handler: async (req, res) => {
272
+ const send = (status, body) => {
273
+ res.writeHead(status, { 'content-type': 'application/json' })
274
+ res.end(JSON.stringify(body))
275
+ }
276
+ if (req.method === 'GET') {
277
+ send(200, { defaultMode })
278
+ return
279
+ }
280
+ if (req.method !== 'POST') {
281
+ send(405, { ok: false, error: 'method not allowed' })
282
+ return
283
+ }
284
+ if (cavemanScope === null) {
285
+ send(503, { ok: false, error: 'settings unavailable' })
286
+ return
287
+ }
288
+ let raw = ''
289
+ try {
290
+ for await (const chunk of req) raw += chunk
291
+ } catch (e) {
292
+ send(400, { ok: false, error: 'bad body' })
293
+ return
294
+ }
295
+ let next
296
+ try {
297
+ next = JSON.parse(raw || '{}').defaultMode
298
+ } catch (e) {
299
+ next = undefined
300
+ }
301
+ if (typeof next !== 'string' || !VALID.includes(next)) {
302
+ send(400, { ok: false, error: 'defaultMode must be one of: ' + VALID.join(', ') })
303
+ return
304
+ }
305
+ try {
306
+ await cavemanScope.update({ defaultMode: next })
307
+ const stored = cavemanScope.get()
308
+ defaultMode = stored && VALID.includes(stored.defaultMode) ? stored.defaultMode : 'full'
309
+ send(200, { ok: true, defaultMode })
310
+ } catch (e) {
311
+ send(500, { ok: false, error: 'persist failed' })
312
+ }
313
+ },
314
+ })
315
+ } catch (e) {
316
+ console.error('[caveman] /caveman/default route registration failed:', e && e.message)
317
+ }
318
+ }
319
+
258
320
  const systemPrompt = ctx.get('systemPrompt')
259
321
  if (systemPrompt !== undefined) {
260
322
  systemPrompt.section({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-caveman",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "main": "lib/index.js",
6
6
  "exports": {
@@ -8,7 +8,7 @@
8
8
  "./client": "./lib/client.js",
9
9
  "./package.json": "./package.json"
10
10
  },
11
- "description": "Per-session caveman mode state + composer dock badge + /caveman command + prompt-section injection. Writes NO session events: mode state lives in a $DSH_HOME/plugin-state sidecar keyed by session id (default from the 'caveman' settings namespace), and the badge reads GET /caveman/state (fetch mode). The KNOWN event-type registration is kept read-only for legacy sessions. Caveman skills ship from the deployment shared skill library (see ~/.dsh/skills), not from this package.",
11
+ "description": "Per-session caveman mode state + composer dock badge + /caveman command + prompt-section injection. Writes NO session events: mode state lives in a $DSH_HOME/plugin-state sidecar keyed by session id (default from the 'caveman' settings namespace), and the badge reads GET /caveman/state (fetch mode). The KNOWN event-type registration is kept read-only for legacy sessions. Caveman skills ship from the deployment shared skill library (see ~/.dsh/skills), not from this package. Compatible with @deepseek-ai/dsh-settings 0.1.2+: the namespace is passed as a literal string because the settingsNamespace brand helper was removed upstream (broken named import on 0.1.2-rc.1 boot). 0.1.5 adds the Settings -> General row (client slot settings.general.item + host route GET/POST /caveman/default) so the default mode is editable in the UI; a registered host namespace alone does NOT produce a settings row.",
12
12
  "license": "MIT",
13
13
  "keywords": [
14
14
  "dsh",