@yahaha-studio/kichi-forwarder 0.0.1-alpha.30 → 0.0.1-alpha.31

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/index.ts CHANGED
@@ -58,17 +58,14 @@ const KICHI_WORLD_DIR = path.join(os.homedir(), ".openclaw", "kichi-world");
58
58
  const RUNTIME_CONFIG_PATH = path.join(KICHI_WORLD_DIR, "kichi-runtime-config.json");
59
59
  const LEGACY_SKILLS_CONFIG_PATH = path.join(KICHI_WORLD_DIR, "skills-config.json");
60
60
  const IDENTITY_PATH = path.join(KICHI_WORLD_DIR, "identity.json");
61
+ const RUNTIME_ALBUM_CONFIG_PATH = path.join(KICHI_WORLD_DIR, "album-config.json");
61
62
  const MAX_NOTEBOARD_TEXT_LENGTH = 200;
62
- const ALBUM_CONFIG_PATH = new URL("./config/album-config.json", import.meta.url);
63
- const DEFAULT_ALBUM_CONFIG = loadAlbumConfig();
64
- const MUSIC_TITLE_LOOKUP = new Map(
65
- DEFAULT_ALBUM_CONFIG.track.map((item) => [item.name.toLowerCase(), item.name] as const),
66
- );
67
- const MUSIC_TITLE_ENUM = DEFAULT_ALBUM_CONFIG.track.map((item) => item.name);
68
- const MUSIC_TITLE_EXAMPLES = DEFAULT_ALBUM_CONFIG.track.slice(0, 10).map((item) => item.name);
63
+ const BUNDLED_ALBUM_CONFIG_PATH = new URL("./config/album-config.json", import.meta.url);
69
64
  let cachedConfig: KichiRuntimeConfig | null = null;
70
65
  let cachedConfigMtime = 0;
71
66
  let cachedConfigPath = "";
67
+ let cachedAlbumConfig: Album | null = null;
68
+ let cachedAlbumConfigMtime = 0;
72
69
  let service: KichiForwarderService | null = null;
73
70
  let pluginApi: OpenClawPluginApi | null = null;
74
71
 
@@ -93,15 +90,55 @@ function isAlbumConfig(value: unknown): value is Album {
93
90
  });
94
91
  }
95
92
 
96
- function loadAlbumConfig(): Album {
97
- const raw = fs.readFileSync(ALBUM_CONFIG_PATH, "utf-8");
93
+ function loadAlbumConfigFromPath(configPath: string | URL): Album {
94
+ const raw = fs.readFileSync(configPath, "utf-8");
98
95
  const parsed = JSON.parse(raw) as unknown;
99
96
  if (!isAlbumConfig(parsed)) {
100
- throw new Error("Invalid album config at config/album-config.json");
97
+ throw new Error(`Invalid album config at ${String(configPath)}`);
101
98
  }
102
99
  return parsed;
103
100
  }
104
101
 
102
+ function ensureRuntimeAlbumConfig(): void {
103
+ fs.mkdirSync(KICHI_WORLD_DIR, { recursive: true });
104
+ if (!fs.existsSync(RUNTIME_ALBUM_CONFIG_PATH)) {
105
+ fs.copyFileSync(BUNDLED_ALBUM_CONFIG_PATH, RUNTIME_ALBUM_CONFIG_PATH);
106
+ pluginApi?.logger.debug("[kichi] seeded runtime album config from bundled config");
107
+ return;
108
+ }
109
+
110
+ try {
111
+ loadAlbumConfigFromPath(RUNTIME_ALBUM_CONFIG_PATH);
112
+ } catch (error) {
113
+ pluginApi?.logger.warn(`[kichi] invalid runtime album config, resetting from bundled config: ${error}`);
114
+ fs.copyFileSync(BUNDLED_ALBUM_CONFIG_PATH, RUNTIME_ALBUM_CONFIG_PATH);
115
+ }
116
+ }
117
+
118
+ function loadRuntimeAlbumConfig(): Album {
119
+ ensureRuntimeAlbumConfig();
120
+ const stat = fs.statSync(RUNTIME_ALBUM_CONFIG_PATH);
121
+ if (!cachedAlbumConfig || stat.mtimeMs !== cachedAlbumConfigMtime) {
122
+ cachedAlbumConfig = loadAlbumConfigFromPath(RUNTIME_ALBUM_CONFIG_PATH);
123
+ cachedAlbumConfigMtime = stat.mtimeMs;
124
+ }
125
+ return cachedAlbumConfig;
126
+ }
127
+
128
+ function getMusicTitleLookup(): Map<string, string> {
129
+ return new Map(
130
+ loadRuntimeAlbumConfig().track.map((item) => [item.name.toLowerCase(), item.name] as const),
131
+ );
132
+ }
133
+
134
+ function getMusicTitleEnum(): string[] {
135
+ return loadRuntimeAlbumConfig().track.map((item) => item.name);
136
+ }
137
+
138
+ function getMusicTitleExamples(): string[] {
139
+ return loadRuntimeAlbumConfig().track.slice(0, 10).map((item) => item.name);
140
+ }
141
+
105
142
  function sanitizeActions(value: unknown, fallback: string[]): string[] {
106
143
  if (!Array.isArray(value)) {
107
144
  return fallback;
@@ -406,6 +443,7 @@ function normalizeMusicTitles(value: unknown): { titles: string[]; invalidTitles
406
443
  return { titles: [], invalidTitles: [] };
407
444
  }
408
445
 
446
+ const musicTitleLookup = getMusicTitleLookup();
409
447
  const titles: string[] = [];
410
448
  const invalidTitles: string[] = [];
411
449
  const seen = new Set<string>();
@@ -421,7 +459,7 @@ function normalizeMusicTitles(value: unknown): { titles: string[]; invalidTitles
421
459
  }
422
460
 
423
461
  const key = trimmed.toLowerCase();
424
- const canonicalTitle = MUSIC_TITLE_LOOKUP.get(key);
462
+ const canonicalTitle = musicTitleLookup.get(key);
425
463
  if (!canonicalTitle) {
426
464
  invalidTitles.push(trimmed);
427
465
  continue;
@@ -439,13 +477,13 @@ function normalizeMusicTitles(value: unknown): { titles: string[]; invalidTitles
439
477
  function buildMusicAlbumToolDescription(): string {
440
478
  return [
441
479
  "Create a custom Kichi music album.",
442
- "Query status first, then choose track names from the local config/album-config.json that match weather, time, and personality.",
480
+ "Query status first, then choose track names from the runtime album config: Linux/macOS `~/.openclaw/kichi-world/album-config.json`; Windows `%USERPROFILE%\\.openclaw\\kichi-world\\album-config.json`.",
443
481
  ].join("\n");
444
482
  }
445
483
 
446
484
  function buildMusicTitlesDescription(): string {
447
485
  return [
448
- "Track names chosen from the local config/album-config.json.",
486
+ "Track names chosen from the runtime album config: Linux/macOS `~/.openclaw/kichi-world/album-config.json`; Windows `%USERPROFILE%\\.openclaw\\kichi-world\\album-config.json`.",
449
487
  "Use exact names only; the available titles are injected into this tool schema.",
450
488
  ].join(" ");
451
489
  }
@@ -473,7 +511,7 @@ function buildKichiPrompt(): string {
473
511
  "When to use `kichi_music_album_create`:",
474
512
  "- Call `kichi_query_status` first.",
475
513
  "- Recommend a variable-length playlist based on weather, time, and your own personality.",
476
- "- `albumTitle` is user-defined and `musicTitles` must be exact track names from the local album-config.json.",
514
+ "- `albumTitle` is user-defined and `musicTitles` must be exact track names from the runtime album config under the user's home directory.",
477
515
  "",
478
516
  "Skip all sync if:",
479
517
  "- User says 'don't sync to Kichi' or similar",
@@ -491,7 +529,9 @@ const plugin = {
491
529
 
492
530
  register(api: OpenClawPluginApi) {
493
531
  pluginApi = api;
532
+ ensureRuntimeAlbumConfig();
494
533
  registerPluginHooks(api);
534
+ const musicTitleEnum = getMusicTitleEnum();
495
535
 
496
536
  api.registerService({
497
537
  id: "kichi-forwarder",
@@ -843,7 +883,7 @@ const plugin = {
843
883
  description: buildMusicTitlesDescription(),
844
884
  items: {
845
885
  type: "string",
846
- enum: MUSIC_TITLE_ENUM,
886
+ enum: musicTitleEnum,
847
887
  },
848
888
  },
849
889
  },
@@ -875,15 +915,15 @@ const plugin = {
875
915
  return {
876
916
  success: false,
877
917
  error: "musicTitles must contain at least one valid track name from album-config",
878
- examples: MUSIC_TITLE_EXAMPLES,
918
+ examples: getMusicTitleExamples(),
879
919
  };
880
920
  }
881
921
  if (invalidTitles.length > 0) {
882
922
  return {
883
923
  success: false,
884
924
  error: `Unknown musicTitles: ${invalidTitles.join(", ")}`,
885
- hint: "Use exact track names from config/album-config.json",
886
- examples: MUSIC_TITLE_EXAMPLES,
925
+ hint: "Use exact track names from the runtime album config under the user's home directory",
926
+ examples: getMusicTitleExamples(),
887
927
  };
888
928
  }
889
929
  if (!service?.hasValidIdentity() || !service?.isConnected()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yahaha-studio/kichi-forwarder",
3
- "version": "0.0.1-alpha.30",
3
+ "version": "0.0.1-alpha.31",
4
4
  "description": "Forward OpenClaw agent events to external WebSocket server for visualization",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -259,7 +259,7 @@ Parameters:
259
259
 
260
260
  Track source rule:
261
261
 
262
- - `musicTitles` must use exact track names from `config/album-config.json` -> `track[].name`
262
+ - `musicTitles` must use exact track names from the runtime album config file: Linux/macOS `~/.openclaw/kichi-world/album-config.json`; Windows `%USERPROFILE%\.openclaw\kichi-world\album-config.json`
263
263
  - do not use album names in `musicTitles`
264
264
 
265
265
  Before create:
@@ -305,7 +305,7 @@ Hard rules:
305
305
 
306
306
  1. Query first with `kichi_query_status`.
307
307
  2. Playlist length is flexible (not fixed), but avoid empty or repetitive selections.
308
- 3. Select tracks from `config/album-config.json` track name list only.
308
+ 3. Select tracks from the runtime album config file only: Linux/macOS `~/.openclaw/kichi-world/album-config.json`; Windows `%USERPROFILE%\.openclaw\kichi-world\album-config.json`.
309
309
  4. Recommendation must reflect `environmentWeather` + `environmentTime` + your personality (not random picks).
310
310
  5. Use a user-meaningful custom `albumTitle`.
311
311
  6. If `kichi_query_status` fails or returns empty/insufficient context, skip creation.
@@ -334,6 +334,7 @@ Files:
334
334
 
335
335
  - `identity.json`: `avatarId`, `authKey`
336
336
  - `kichi-runtime-config.json`: runtime action list and `llmRuntimeEnabled`
337
+ - `album-config.json`: music track list for `kichi_music_album_create`; Linux/macOS path is `~/.openclaw/kichi-world/album-config.json`, Windows path is `%USERPROFILE%\.openclaw\kichi-world\album-config.json`. If missing at startup, the plugin seeds it from bundled `config/album-config.json`
337
338
  - `skills-config.json`: legacy filename still readable for backward compatibility
338
339
 
339
340
  ## Runtime Behavior
@@ -115,4 +115,5 @@ Files:
115
115
 
116
116
  - `identity.json`: `avatarId`, `authKey`
117
117
  - `kichi-runtime-config.json`: runtime action list and `llmRuntimeEnabled`
118
+ - `album-config.json`: music track list used by music album creation; Linux/macOS path is `~/.openclaw/kichi-world/album-config.json`, Windows path is `%USERPROFILE%\.openclaw\kichi-world\album-config.json`. If missing at startup, the plugin seeds it from bundled `config/album-config.json`
118
119
  - `skills-config.json`: legacy filename still readable for backward compatibility