@yahaha-studio/kichi-forwarder 0.1.2-beta.12 → 0.1.2-beta.13

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/dist/index.js CHANGED
@@ -961,6 +961,10 @@ const plugin = {
961
961
  description: "Optional list of OpenClaw self-perceived personality tags",
962
962
  items: { type: "string" },
963
963
  },
964
+ source: {
965
+ type: "string",
966
+ description: "Optional join source identifier. Defaults to Kichi World join-source.json, then openclaw.",
967
+ },
964
968
  },
965
969
  required: ["botName", "bio"],
966
970
  },
@@ -974,6 +978,7 @@ const plugin = {
974
978
  let avatarId = params?.avatarId;
975
979
  const botName = params?.botName?.trim();
976
980
  const bio = params?.bio?.trim();
981
+ const rawSource = params?.source;
977
982
  const { tags, error: tagsError } = normalizeJoinTags(params?.tags);
978
983
  if (!avatarId) {
979
984
  avatarId = service.readSavedAvatarId() ?? undefined;
@@ -987,10 +992,22 @@ const plugin = {
987
992
  if (!bio) {
988
993
  return jsonResult({ success: false, error: "No bio" });
989
994
  }
995
+ let source;
996
+ try {
997
+ source = rawSource === undefined
998
+ ? service.readConfiguredJoinSource() ?? "openclaw"
999
+ : trimOptionalString(rawSource);
1000
+ }
1001
+ catch (err) {
1002
+ return jsonResult({ success: false, error: err instanceof Error ? err.message : String(err) });
1003
+ }
1004
+ if (!source) {
1005
+ return jsonResult({ success: false, error: "source must be a non-empty string" });
1006
+ }
990
1007
  if (tagsError) {
991
1008
  return jsonResult({ success: false, error: tagsError });
992
1009
  }
993
- const result = await service.join(avatarId, botName, bio, tags ?? []);
1010
+ const result = await service.join(avatarId, botName, bio, tags ?? [], source);
994
1011
  if (result.success) {
995
1012
  return jsonResult({ success: true, authKey: result.authKey });
996
1013
  }
@@ -4,6 +4,7 @@ import * as path from "path";
4
4
  import { randomUUID } from "node:crypto";
5
5
  const MAX_NOTEBOARD_TEXT_LENGTH = 200;
6
6
  const DEFAULT_LLM_RUNTIME_ENABLED = true;
7
+ const JOIN_SOURCE_FILE_NAME = "join-source.json";
7
8
  export class KichiForwarderService {
8
9
  logger;
9
10
  options;
@@ -63,7 +64,7 @@ export class KichiForwarderService {
63
64
  }
64
65
  return this.getConnectionStatus();
65
66
  }
66
- async join(avatarId, botName, bio, tags) {
67
+ async join(avatarId, botName, bio, tags, source) {
67
68
  if (!this.host) {
68
69
  return { success: false, error: "No Kichi host configured. Run kichi_switch_host first." };
69
70
  }
@@ -78,7 +79,7 @@ export class KichiForwarderService {
78
79
  this.identity = { avatarId };
79
80
  this.saveIdentity();
80
81
  this.joinResolve = resolve;
81
- const payload = { type: "join", avatarId, botName, bio, tags };
82
+ const payload = { type: "join", avatarId, botName, bio, tags, source };
82
83
  const sendJoin = () => this.ws?.send(JSON.stringify(payload));
83
84
  if (this.ws?.readyState === WebSocket.OPEN) {
84
85
  sendJoin();
@@ -274,6 +275,24 @@ export class KichiForwarderService {
274
275
  getRuntimeDir() {
275
276
  return this.options.runtimeDir;
276
277
  }
278
+ getJoinSourcePath() {
279
+ return path.join(this.getKichiWorldRootDir(), JOIN_SOURCE_FILE_NAME);
280
+ }
281
+ readConfiguredJoinSource() {
282
+ const sourcePath = this.getJoinSourcePath();
283
+ if (!fs.existsSync(sourcePath)) {
284
+ return null;
285
+ }
286
+ const data = JSON.parse(fs.readFileSync(sourcePath, "utf-8"));
287
+ if (!data || typeof data !== "object" || Array.isArray(data)) {
288
+ throw new Error(`${JOIN_SOURCE_FILE_NAME} must contain a JSON object`);
289
+ }
290
+ const source = data.source;
291
+ if (typeof source !== "string" || !source.trim()) {
292
+ throw new Error(`${JOIN_SOURCE_FILE_NAME} must contain a non-empty string source`);
293
+ }
294
+ return source.trim();
295
+ }
277
296
  getStatePath() {
278
297
  return path.join(this.options.runtimeDir, "state.json");
279
298
  }
@@ -621,6 +640,9 @@ export class KichiForwarderService {
621
640
  }
622
641
  return path.join(this.options.runtimeDir, "hosts", encodeURIComponent(this.host));
623
642
  }
643
+ getKichiWorldRootDir() {
644
+ return path.dirname(path.dirname(this.options.runtimeDir));
645
+ }
624
646
  getWsUrl() {
625
647
  if (!this.host) {
626
648
  throw new Error("No Kichi host configured");
package/index.ts CHANGED
@@ -1179,6 +1179,10 @@ const plugin = {
1179
1179
  description: "Optional list of OpenClaw self-perceived personality tags",
1180
1180
  items: { type: "string" },
1181
1181
  },
1182
+ source: {
1183
+ type: "string",
1184
+ description: "Optional join source identifier. Defaults to Kichi World join-source.json, then openclaw.",
1185
+ },
1182
1186
  },
1183
1187
  required: ["botName", "bio"],
1184
1188
  },
@@ -1192,6 +1196,7 @@ const plugin = {
1192
1196
  let avatarId = (params as { avatarId?: string } | null)?.avatarId;
1193
1197
  const botName = (params as { botName?: string } | null)?.botName?.trim();
1194
1198
  const bio = (params as { bio?: string } | null)?.bio?.trim();
1199
+ const rawSource = (params as { source?: unknown } | null)?.source;
1195
1200
  const { tags, error: tagsError } = normalizeJoinTags(
1196
1201
  (params as { tags?: unknown } | null)?.tags,
1197
1202
  );
@@ -1207,10 +1212,21 @@ const plugin = {
1207
1212
  if (!bio) {
1208
1213
  return jsonResult({ success: false, error: "No bio" });
1209
1214
  }
1215
+ let source: string | null | undefined;
1216
+ try {
1217
+ source = rawSource === undefined
1218
+ ? service.readConfiguredJoinSource() ?? "openclaw"
1219
+ : trimOptionalString(rawSource);
1220
+ } catch (err) {
1221
+ return jsonResult({ success: false, error: err instanceof Error ? err.message : String(err) });
1222
+ }
1223
+ if (!source) {
1224
+ return jsonResult({ success: false, error: "source must be a non-empty string" });
1225
+ }
1210
1226
  if (tagsError) {
1211
1227
  return jsonResult({ success: false, error: tagsError });
1212
1228
  }
1213
- const result = await service.join(avatarId, botName, bio, tags ?? []);
1229
+ const result = await service.join(avatarId, botName, bio, tags ?? [], source);
1214
1230
  if (result.success) {
1215
1231
  return jsonResult({ success: true, authKey: result.authKey });
1216
1232
  }
@@ -1979,4 +1995,3 @@ const plugin = {
1979
1995
  };
1980
1996
 
1981
1997
  export default plugin;
1982
-
@@ -2,7 +2,7 @@
2
2
  "id": "kichi-forwarder",
3
3
  "name": "Kichi Forwarder",
4
4
  "description": "Native OpenClaw plugin for Kichi World with direct avatar control, status sync, timers, notes, and music tools",
5
- "version": "0.1.2-beta.12",
5
+ "version": "0.1.2-beta.13",
6
6
  "author": "OpenClaw",
7
7
  "skills": ["./skills/kichi-forwarder"],
8
8
  "contracts": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yahaha-studio/kichi-forwarder",
3
- "version": "0.1.2-beta.12",
3
+ "version": "0.1.2-beta.13",
4
4
  "description": "Native OpenClaw plugin for Kichi World with direct avatar control, status sync, timers, notes, and music tools",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/service.ts CHANGED
@@ -33,6 +33,7 @@ import type {
33
33
 
34
34
  const MAX_NOTEBOARD_TEXT_LENGTH = 200;
35
35
  const DEFAULT_LLM_RUNTIME_ENABLED = true;
36
+ const JOIN_SOURCE_FILE_NAME = "join-source.json";
36
37
 
37
38
  type AckFailureResult = {
38
39
  success: false;
@@ -138,6 +139,7 @@ export class KichiForwarderService {
138
139
  botName: string,
139
140
  bio: string,
140
141
  tags: string[],
142
+ source: string,
141
143
  ): Promise<JoinResult> {
142
144
  if (!this.host) {
143
145
  return { success: false, error: "No Kichi host configured. Run kichi_switch_host first." };
@@ -153,7 +155,7 @@ export class KichiForwarderService {
153
155
  this.identity = { avatarId };
154
156
  this.saveIdentity();
155
157
  this.joinResolve = resolve;
156
- const payload: JoinPayload = { type: "join", avatarId, botName, bio, tags };
158
+ const payload: JoinPayload = { type: "join", avatarId, botName, bio, tags, source };
157
159
  const sendJoin = () => this.ws?.send(JSON.stringify(payload));
158
160
  if (this.ws?.readyState === WebSocket.OPEN) {
159
161
  sendJoin();
@@ -381,6 +383,29 @@ export class KichiForwarderService {
381
383
  return this.options.runtimeDir;
382
384
  }
383
385
 
386
+ getJoinSourcePath(): string {
387
+ return path.join(this.getKichiWorldRootDir(), JOIN_SOURCE_FILE_NAME);
388
+ }
389
+
390
+ readConfiguredJoinSource(): string | null {
391
+ const sourcePath = this.getJoinSourcePath();
392
+ if (!fs.existsSync(sourcePath)) {
393
+ return null;
394
+ }
395
+
396
+ const data = JSON.parse(fs.readFileSync(sourcePath, "utf-8")) as unknown;
397
+ if (!data || typeof data !== "object" || Array.isArray(data)) {
398
+ throw new Error(`${JOIN_SOURCE_FILE_NAME} must contain a JSON object`);
399
+ }
400
+
401
+ const source = (data as { source?: unknown }).source;
402
+ if (typeof source !== "string" || !source.trim()) {
403
+ throw new Error(`${JOIN_SOURCE_FILE_NAME} must contain a non-empty string source`);
404
+ }
405
+
406
+ return source.trim();
407
+ }
408
+
384
409
  getStatePath(): string {
385
410
  return path.join(this.options.runtimeDir, "state.json");
386
411
  }
@@ -765,6 +790,10 @@ export class KichiForwarderService {
765
790
  return path.join(this.options.runtimeDir, "hosts", encodeURIComponent(this.host));
766
791
  }
767
792
 
793
+ private getKichiWorldRootDir(): string {
794
+ return path.dirname(path.dirname(this.options.runtimeDir));
795
+ }
796
+
768
797
  private getWsUrl(): string {
769
798
  if (!this.host) {
770
799
  throw new Error("No Kichi host configured");
package/src/types.ts CHANGED
@@ -86,6 +86,7 @@ export type JoinPayload = {
86
86
  botName: string;
87
87
  bio: string;
88
88
  tags: string[];
89
+ source: string;
89
90
  };
90
91
 
91
92
  export type JoinAckPayload = {