Files
ClawSkills/openclaw-plugin-dev/docs/state.md
zhi 5a8f490cc2 feat: add openclaw-plugin-dev skill
Plugin development reference and workflows based on real development
experience (Dirigent, ContractorAgent, PrismFacet).

Docs: structure, entry-point, hooks, tools, state, config, debugging
Workflows: create-plugin, add-hook

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 17:25:07 +00:00

1.4 KiB

State Management

Where to Store What

Data Type Location Reason
Business state (maps, sets, caches) globalThis Module vars reset on hot reload
Event dedup (WeakSet/Set) globalThis Same
Gateway lifecycle flags globalThis Prevent double init
Connection flags (WebSocket/TCP) globalThis Prevent duplicate connections
Runtime references globalThis Closures need living instance
Cross-plugin API objects (__pluginId) globalThis Other plugins access via globalThis
Pure utility functions Module-level No state needed
Persistent data File + memory cache Survives gateway restart

Cross-Plugin API Pattern

Provider side

const _G = globalThis as Record<string, unknown>;

// Init shared objects once
if (!(_G["_myRegistry"] instanceof MyRegistry)) {
  _G["_myRegistry"] = new MyRegistry();
}

// Overwrite public API every register() (updates closures)
_G["__myPlugin"] = {
  registry: _G["_myRegistry"],
  send: (msg) => (_G["_myRuntime"] as Runtime)?.send(msg) ?? false,
};

Consumer side

const provider = (globalThis as any)["__myPlugin"];
if (!provider) {
  console.error("[consumer] __myPlugin not found");
  return;
}
provider.registry.register("my_rule", handler);

Load Order

Provider must be listed before consumer in plugins.allow. Consumer must defend against provider not being loaded.