@xfxstudio/claworld 2026.6.10-testing.1 → 2026.6.10-testing.2

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.
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "name": "Claworld Persona Relay",
19
19
  "description": "Claworld relay world channel plugin for OpenClaw.",
20
- "version": "2026.6.10-testing.1",
20
+ "version": "2026.6.10-testing.2",
21
21
  "configSchema": {
22
22
  "type": "object",
23
23
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfxstudio/claworld",
3
- "version": "2026.6.10-testing.1",
3
+ "version": "2026.6.10-testing.2",
4
4
  "description": "Claworld channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -866,15 +866,63 @@ function parseViewerRoute(req) {
866
866
  };
867
867
  }
868
868
 
869
- async function fetchBackendJson(fetchImpl, runtimeConfig = {}, pathName) {
869
+ const DEFAULT_SNAPSHOT_FETCH_TIMEOUT_MS = 4500;
870
+
871
+ function timeoutError(message) {
872
+ const error = new Error(message);
873
+ error.name = 'TimeoutError';
874
+ return error;
875
+ }
876
+
877
+ async function withTimeout(promise, timeoutMs, onTimeout) {
878
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return promise;
879
+ let timeoutId = null;
880
+ try {
881
+ return await Promise.race([
882
+ promise,
883
+ new Promise((_resolve, reject) => {
884
+ timeoutId = setTimeout(() => {
885
+ try {
886
+ reject(onTimeout());
887
+ } catch (error) {
888
+ reject(error);
889
+ }
890
+ }, timeoutMs);
891
+ }),
892
+ ]);
893
+ } finally {
894
+ if (timeoutId) clearTimeout(timeoutId);
895
+ }
896
+ }
897
+
898
+ async function fetchBackendJson(fetchImpl, runtimeConfig = {}, pathName, {
899
+ timeoutMs = DEFAULT_SNAPSHOT_FETCH_TIMEOUT_MS,
900
+ } = {}) {
870
901
  const baseUrl = normalizeRelayHttpBaseUrl(runtimeConfig.serverUrl);
871
- const response = await fetchImpl(`${baseUrl}${pathName}`, {
902
+ const controller = typeof AbortController === 'function' ? new AbortController() : null;
903
+ const targetUrl = `${baseUrl}${pathName}`;
904
+ const fetchPromise = fetchImpl(targetUrl, {
872
905
  method: 'GET',
873
906
  headers: {
874
907
  ...(runtimeConfig.apiKey ? { 'x-api-key': runtimeConfig.apiKey } : {}),
875
908
  ...buildRuntimeAuthHeaders(runtimeConfig),
876
909
  },
910
+ ...(controller ? { signal: controller.signal } : {}),
877
911
  });
912
+ let response = null;
913
+ try {
914
+ response = await withTimeout(fetchPromise, timeoutMs, () => {
915
+ controller?.abort?.();
916
+ throw timeoutError(`backend request timed out after ${timeoutMs}ms`);
917
+ });
918
+ } catch (error) {
919
+ return {
920
+ ok: false,
921
+ status: 0,
922
+ error: error?.message || String(error),
923
+ path: pathName,
924
+ };
925
+ }
878
926
  let body = null;
879
927
  try {
880
928
  body = await response.json();
@@ -885,6 +933,7 @@ async function fetchBackendJson(fetchImpl, runtimeConfig = {}, pathName) {
885
933
  ok: response.ok,
886
934
  status: response.status,
887
935
  body,
936
+ path: pathName,
888
937
  };
889
938
  }
890
939
 
@@ -974,8 +1023,10 @@ export async function loadConversationViewerSnapshot({
974
1023
  manifest,
975
1024
  runtimeConfig,
976
1025
  fetchImpl = globalThis.fetch,
1026
+ timeoutMs = DEFAULT_SNAPSHOT_FETCH_TIMEOUT_MS,
977
1027
  } = {}) {
978
1028
  const fetchedAt = new Date().toISOString();
1029
+ const diagnostics = [];
979
1030
  const viewerAgentId = normalizeText(manifest.viewerAgentId, runtimeConfig?.relay?.agentId || null);
980
1031
  const inboxQuery = queryString({
981
1032
  agentId: viewerAgentId,
@@ -983,15 +1034,27 @@ export async function loadConversationViewerSnapshot({
983
1034
  conversationKey: manifest.conversationKey,
984
1035
  localSessionKey: manifest.localSessionKey,
985
1036
  });
986
- const inboxResult = await fetchBackendJson(fetchImpl, runtimeConfig, `/v1/chat-requests${inboxQuery}`);
1037
+ let inboxResult = await fetchBackendJson(fetchImpl, runtimeConfig, `/v1/chat-requests${inboxQuery}`, { timeoutMs });
1038
+ if (!inboxResult.ok && inboxQuery) {
1039
+ diagnostics.push({
1040
+ source: 'chat_requests_filtered',
1041
+ status: inboxResult.status,
1042
+ error: inboxResult.error || null,
1043
+ path: inboxResult.path || null,
1044
+ });
1045
+ const fallbackQuery = queryString({ agentId: viewerAgentId });
1046
+ inboxResult = await fetchBackendJson(fetchImpl, runtimeConfig, `/v1/chat-requests${fallbackQuery}`, { timeoutMs });
1047
+ }
987
1048
  if (!inboxResult.ok) {
988
1049
  return {
989
1050
  ok: false,
990
1051
  error: 'chat_inbox_snapshot_failed',
991
1052
  status: inboxResult.status,
1053
+ message: inboxResult.error || null,
992
1054
  fetchedAt,
993
1055
  viewer: buildPublicConversationViewer(manifest),
994
1056
  body: inboxResult.body,
1057
+ diagnostics,
995
1058
  };
996
1059
  }
997
1060
  const inbox = normalizeObject(inboxResult.body, {});
@@ -1014,14 +1077,33 @@ export async function loadConversationViewerSnapshot({
1014
1077
  if (conversationKey) {
1015
1078
  const suffix = queryString({ agentId: viewerAgentId });
1016
1079
  const [conversationResult, turnsResult, participantsResult] = await Promise.all([
1017
- fetchBackendJson(fetchImpl, runtimeConfig, `/v1/conversations/${encodeURIComponent(conversationKey)}${suffix}`),
1018
- fetchBackendJson(fetchImpl, runtimeConfig, `/v1/conversations/${encodeURIComponent(conversationKey)}/turns${suffix}`),
1019
- fetchBackendJson(fetchImpl, runtimeConfig, `/v1/conversations/${encodeURIComponent(conversationKey)}/participants${suffix}`),
1080
+ fetchBackendJson(fetchImpl, runtimeConfig, `/v1/conversations/${encodeURIComponent(conversationKey)}${suffix}`, { timeoutMs }),
1081
+ fetchBackendJson(fetchImpl, runtimeConfig, `/v1/conversations/${encodeURIComponent(conversationKey)}/turns${suffix}`, { timeoutMs }),
1082
+ fetchBackendJson(fetchImpl, runtimeConfig, `/v1/conversations/${encodeURIComponent(conversationKey)}/participants${suffix}`, { timeoutMs }),
1020
1083
  ]);
1021
1084
  if (conversationResult.ok) conversation = conversationResult.body || null;
1085
+ else diagnostics.push({
1086
+ source: 'conversation',
1087
+ status: conversationResult.status,
1088
+ error: conversationResult.error || null,
1089
+ path: conversationResult.path || null,
1090
+ });
1022
1091
  if (turnsResult.ok) turns = collectTurns(turnsResult.body);
1092
+ else diagnostics.push({
1093
+ source: 'turns',
1094
+ status: turnsResult.status,
1095
+ error: turnsResult.error || null,
1096
+ path: turnsResult.path || null,
1097
+ });
1023
1098
  if (participantsResult.ok && Array.isArray(participantsResult.body?.items)) {
1024
1099
  participants = participantsResult.body.items;
1100
+ } else if (!participantsResult.ok) {
1101
+ diagnostics.push({
1102
+ source: 'participants',
1103
+ status: participantsResult.status,
1104
+ error: participantsResult.error || null,
1105
+ path: participantsResult.path || null,
1106
+ });
1025
1107
  }
1026
1108
  }
1027
1109
  const peer = normalizeObject(chat?.counterparty, null)
@@ -1059,6 +1141,7 @@ export async function loadConversationViewerSnapshot({
1059
1141
  counts: inbox.counts || null,
1060
1142
  filters: inbox.filters || null,
1061
1143
  },
1144
+ diagnostics,
1062
1145
  };
1063
1146
  }
1064
1147