@openclawcity/openclawcity 1.0.11 → 1.0.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.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { OpenClawPluginApi } from 'openclaw/plugin-sdk';
2
+ export declare function sanitizeReplyText(text: string): string | null;
2
3
  declare const plugin: {
3
4
  id: string;
4
5
  name: string;
package/dist/index.js CHANGED
@@ -4072,7 +4072,52 @@ var OpenClawCityAdapter = class {
4072
4072
 
4073
4073
  // .tsc-out/index.js
4074
4074
  var CHANNEL_ID = "openclawcity";
4075
+ var DEFAULT_API_BASE = "https://api.openbotcity.com";
4076
+ var HEARTBEAT_CACHE_MS = 5 * 60 * 1e3;
4077
+ function deriveApiBase(gatewayUrl) {
4078
+ if (!gatewayUrl)
4079
+ return DEFAULT_API_BASE;
4080
+ try {
4081
+ const url = new URL(gatewayUrl);
4082
+ const protocol = url.protocol === "wss:" ? "https:" : "http:";
4083
+ return `${protocol}//${url.host}`;
4084
+ } catch {
4085
+ return DEFAULT_API_BASE;
4086
+ }
4087
+ }
4088
+ var TOOL_CALL_MARKUP_RE = /<PLHD\d*>[\s\S]*?<PLHD\d*>/g;
4089
+ function sanitizeReplyText(text) {
4090
+ let cleaned = text.replace(TOOL_CALL_MARKUP_RE, "");
4091
+ cleaned = cleaned.trim();
4092
+ return cleaned || null;
4093
+ }
4075
4094
  var adapters = /* @__PURE__ */ new Map();
4095
+ var heartbeatCache = /* @__PURE__ */ new Map();
4096
+ async function fetchHeartbeatContext(apiBase, jwt, accountId, log) {
4097
+ const cached = heartbeatCache.get(accountId);
4098
+ const now = Date.now();
4099
+ if (cached && now - cached.fetchedAt < HEARTBEAT_CACHE_MS) {
4100
+ log?.info?.(`[OCC] Heartbeat cache hit (age=${Math.round((now - cached.fetchedAt) / 1e3)}s)`);
4101
+ return cached.data;
4102
+ }
4103
+ try {
4104
+ log?.info?.(`[OCC] Fetching heartbeat context from ${apiBase}/world/heartbeat`);
4105
+ const resp = await fetch(`${apiBase}/world/heartbeat`, {
4106
+ headers: { "Authorization": `Bearer ${jwt}` }
4107
+ });
4108
+ if (!resp.ok) {
4109
+ log?.error?.(`[OCC] Heartbeat fetch failed: ${resp.status} ${resp.statusText}`);
4110
+ return cached?.data ?? null;
4111
+ }
4112
+ const data = await resp.text();
4113
+ heartbeatCache.set(accountId, { data, fetchedAt: now });
4114
+ log?.info?.(`[OCC] Heartbeat fetched (${data.length} bytes)`);
4115
+ return data;
4116
+ } catch (err) {
4117
+ log?.error?.(`[OCC] Heartbeat fetch error: ${String(err)}`);
4118
+ return cached?.data ?? null;
4119
+ }
4120
+ }
4076
4121
  var occPlugin = {
4077
4122
  id: CHANNEL_ID,
4078
4123
  meta: {
@@ -4117,10 +4162,13 @@ var occPlugin = {
4117
4162
  if (!adapter) {
4118
4163
  return { ok: false };
4119
4164
  }
4165
+ const text = sanitizeReplyText(ctx.text ?? "");
4166
+ if (!text)
4167
+ return { ok: true };
4120
4168
  const reply = {
4121
4169
  type: "agent_reply",
4122
4170
  action: "dm_reply",
4123
- text: ctx.text,
4171
+ text,
4124
4172
  conversationId: ctx.to
4125
4173
  };
4126
4174
  adapter.sendReply(reply);
@@ -4142,6 +4190,16 @@ var occPlugin = {
4142
4190
  signal: abortSignal,
4143
4191
  onMessage: async (envelope) => {
4144
4192
  log?.info?.(`[OCC] Event received: ${envelope.id} from=${envelope.sender.name} type=${envelope.metadata.eventType}`);
4193
+ const apiBase = deriveApiBase(account.gatewayUrl);
4194
+ const cityCtx = await fetchHeartbeatContext(apiBase, account.apiKey, accountId, log);
4195
+ if (cityCtx) {
4196
+ envelope.content.text = `[CITY CONTEXT]
4197
+ ${cityCtx}
4198
+ [/CITY CONTEXT]
4199
+
4200
+ ${envelope.content.text}`;
4201
+ log?.info?.(`[OCC] City context prepended (${cityCtx.length} bytes)`);
4202
+ }
4145
4203
  log?.info?.(`[OCC] Step 1: resolveAgentRoute...`);
4146
4204
  let route;
4147
4205
  try {
@@ -4220,8 +4278,12 @@ var occPlugin = {
4220
4278
  ctx: msgCtx,
4221
4279
  cfg,
4222
4280
  dispatcherOptions: {
4223
- deliver: async (payload) => {
4224
- const text = payload.text;
4281
+ deliver: async (payload, info) => {
4282
+ if (info.kind === "tool") {
4283
+ log?.info?.(`[OCC] Deliver callback: skipping tool-kind payload`);
4284
+ return;
4285
+ }
4286
+ const text = sanitizeReplyText(payload.text ?? "");
4225
4287
  log?.info?.(`[OCC] Deliver callback: text=${text ? text.slice(0, 80) + "..." : "(empty)"}`);
4226
4288
  if (!text)
4227
4289
  return;
@@ -4340,5 +4402,6 @@ var plugin = {
4340
4402
  };
4341
4403
  var index_default = plugin;
4342
4404
  export {
4343
- index_default as default
4405
+ index_default as default,
4406
+ sanitizeReplyText
4344
4407
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclawcity/openclawcity",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "OpenClawCity channel plugin for OpenClaw — live city events for AI agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",