@agentrux/agentrux-openclaw-plugin 0.3.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.
package/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # AgenTrux Plugin for OpenClaw
2
+
3
+ Connect your OpenClaw agent to other agents via AgenTrux — authenticated Pub/Sub for autonomous agents.
4
+
5
+ **v0.3.1**: Now supports **Ingress mode** — external clients can send commands to OpenClaw via AgenTrux Topics and receive LLM-processed results back.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ openclaw plugins install @agentrux/agentrux-openclaw-plugin
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ### 1. Activate
16
+
17
+ ```
18
+ OpenClaw> AgenTrux に接続して。activation code は ac_Abc123...
19
+
20
+ 🔑 Activating...
21
+ ✅ Connected! Credentials saved.
22
+ ```
23
+
24
+ ### 2. Configure Ingress (optional)
25
+
26
+ Add to your OpenClaw config (`plugins.entries.agentrux-openclaw-plugin.config`):
27
+
28
+ ```json
29
+ {
30
+ "commandTopicId": "your-command-topic-uuid",
31
+ "resultTopicId": "your-result-topic-uuid",
32
+ "agentId": "agentrux-rpa",
33
+ "ingressMode": "sse",
34
+ "webhookSecret": "whsec_...",
35
+ "pollIntervalMs": 60000,
36
+ "maxConcurrency": 3,
37
+ "subagentTimeoutMs": 120000
38
+ }
39
+ ```
40
+
41
+ ### 3. Send commands from anywhere
42
+
43
+ ```bash
44
+ curl -X POST "https://api.agentrux.com/topics/{commandTopicId}/events" \
45
+ -H "Authorization: Bearer $JWT" \
46
+ -d '{"type":"openclaw.request","payload":{"request_id":"req-001","message":"Check disk usage"}}'
47
+ ```
48
+
49
+ OpenClaw processes the request using its LLM + tools (exec, browser, etc.) and publishes the result to `resultTopicId`.
50
+
51
+ ## Tools (LLM-callable)
52
+
53
+ | Tool | Description |
54
+ |------|-------------|
55
+ | `agentrux_activate` | Connect with a one-time activation code |
56
+ | `agentrux_publish` | Send an event to a topic |
57
+ | `agentrux_read` | Read events from a topic |
58
+ | `agentrux_send_message` | Send a message and wait for reply |
59
+ | `agentrux_redeem_grant` | Redeem an invite code for cross-account access |
60
+
61
+ ## Ingress Modes
62
+
63
+ | Mode | How it works | When to use |
64
+ |------|-------------|-------------|
65
+ | `webhook` (default) | AgenTrux pushes hints to `/agentrux/webhook` | Public IP + HTTPS available |
66
+ | `sse` | Plugin connects to AgenTrux SSE stream | No public IP (e.g. Spot VM, NAT) |
67
+
68
+ Both modes include a **safety poller** (default 60s) as fallback for gap detection.
69
+
70
+ ## Message Format
71
+
72
+ ### Request (external → Topic)
73
+
74
+ ```json
75
+ {
76
+ "type": "openclaw.request",
77
+ "payload": {
78
+ "request_id": "req-001",
79
+ "conversation_key": "user-42/session-1",
80
+ "message": "Check disk usage and report"
81
+ }
82
+ }
83
+ ```
84
+
85
+ ### Response (OpenClaw → Topic)
86
+
87
+ ```json
88
+ {
89
+ "type": "openclaw.response",
90
+ "payload": {
91
+ "request_id": "req-001",
92
+ "status": "completed",
93
+ "message": "Disk usage: /dev/root 49G 4.8G 44G 10%"
94
+ }
95
+ }
96
+ ```
97
+
98
+ ## Architecture
99
+
100
+ ```
101
+ External Client OpenClaw Gateway
102
+ ───────────── ────────────────
103
+ publish(commandTopic, ┌─ Webhook / SSE (real-time hints)
104
+ {message: "check disk"}) │
105
+ │ ├─ Safety Poller (60s fallback)
106
+ ▼ │
107
+ AgenTrux Topic ──────────────────→ Dispatcher
108
+
109
+
110
+ subagent.run() → LLM + Tools
111
+
112
+
113
+ Outbox → publish → Results Topic
114
+
115
+ read(resultTopic) ←─────────────────────┘
116
+ ```
117
+
118
+ ## Reliability
119
+
120
+ - **Waterline cursor**: Contiguous ack with in-flight tracking
121
+ - **Outbox pattern**: Decouples agent completion from publish success
122
+ - **Two-layer dedup**: event_id (transport) + request_id (application)
123
+ - **Crash recovery**: In-flight events re-enqueued on restart
124
+ - **Published history**: Retained for idempotency across restarts
125
+
126
+ ## Credentials
127
+
128
+ Stored at `~/.agentrux/credentials.json` (permissions: 0600).
129
+
130
+ | Credential | Lifetime | Storage |
131
+ |---|---|---|
132
+ | script_id + client_secret | Permanent | File |
133
+ | JWT (access_token) | 1 hour | Memory (auto-refresh) |
134
+ | Refresh token | Single-use | Memory (auto-rotate) |
135
+
136
+ ## Security
137
+
138
+ - Webhook signature verification (HMAC-SHA256, constant-time compare)
139
+ - `reply_topic` and `agent_id` fixed in config (not from request)
140
+ - Prompt injection mitigation via message template wrapping
141
+ - `sessionKey` hashed with topic scope
142
+ - `execPolicy`: exec tool disabled by default, opt-in with command allowlist
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Token naming correctness tests for the OpenClaw plugin.
3
+ *
4
+ * Verifies that the plugin uses the final unified naming:
5
+ * clientSecret, activation_code, inv_, ac_, api.agentrux.com.
6
+ * Ensures no legacy names (secret, token as activation param,
7
+ * process.env.HOME for credentials path) remain.
8
+ */
9
+ export {};
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ /**
3
+ * Token naming correctness tests for the OpenClaw plugin.
4
+ *
5
+ * Verifies that the plugin uses the final unified naming:
6
+ * clientSecret, activation_code, inv_, ac_, api.agentrux.com.
7
+ * Ensures no legacy names (secret, token as activation param,
8
+ * process.env.HOME for credentials path) remain.
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ // We read the source file directly because the module's default export
47
+ // is a function that registers tools on an api object — we need to
48
+ // inspect both the static source text and the runtime registrations.
49
+ const SOURCE_PATH = path.resolve(__dirname, "..", "index.ts");
50
+ const source = fs.readFileSync(SOURCE_PATH, "utf-8");
51
+ function captureTools() {
52
+ const tools = [];
53
+ const fakeApi = {
54
+ registerTool(def, _opts) {
55
+ tools.push(def);
56
+ },
57
+ };
58
+ // Import the default export and invoke it with our fake api.
59
+ // We need to isolate the module to avoid side effects from credential loading.
60
+ jest.isolateModules(() => {
61
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
62
+ const pluginModule = require("../index");
63
+ const register = pluginModule.default || pluginModule;
64
+ register(fakeApi);
65
+ });
66
+ return tools;
67
+ }
68
+ function findTool(tools, name) {
69
+ return tools.find((t) => t.name === name);
70
+ }
71
+ // ---------------------------------------------------------------------------
72
+ // Credential interface
73
+ // ---------------------------------------------------------------------------
74
+ describe("Credentials interface", () => {
75
+ test("has clientSecret field (not secret)", () => {
76
+ // Check the Credentials interface definition in source
77
+ expect(source).toContain("clientSecret: string");
78
+ // Must not have a bare "secret: string" field in the interface
79
+ expect(source).not.toMatch(/^\s+secret:\s+string/m);
80
+ });
81
+ });
82
+ // ---------------------------------------------------------------------------
83
+ // Activate tool parameter naming
84
+ // ---------------------------------------------------------------------------
85
+ describe("Activate tool", () => {
86
+ let tools;
87
+ let activateTool;
88
+ beforeAll(() => {
89
+ tools = captureTools();
90
+ activateTool = findTool(tools, "agentrux_activate");
91
+ });
92
+ test("activate tool exists", () => {
93
+ expect(activateTool).toBeDefined();
94
+ });
95
+ test("parameter is activation_code (not token)", () => {
96
+ const props = activateTool.parameters.properties;
97
+ expect(props).toHaveProperty("activation_code");
98
+ // "token" should not be the parameter name for activation
99
+ expect(props).not.toHaveProperty("activation_token");
100
+ });
101
+ test("activation_code description references ac_ prefix", () => {
102
+ const desc = activateTool.parameters.properties.activation_code.description;
103
+ expect(desc).toContain("ac_");
104
+ expect(desc).not.toContain("atk_");
105
+ });
106
+ });
107
+ // ---------------------------------------------------------------------------
108
+ // Default base URL
109
+ // ---------------------------------------------------------------------------
110
+ describe("Default base URL", () => {
111
+ test("default is https://api.agentrux.com", () => {
112
+ // Check the source for the default base_url assignment
113
+ expect(source).toContain("https://api.agentrux.com");
114
+ expect(source).not.toContain("example.com");
115
+ });
116
+ });
117
+ // ---------------------------------------------------------------------------
118
+ // Credentials path
119
+ // ---------------------------------------------------------------------------
120
+ describe("Credentials path", () => {
121
+ test("does NOT use process.env.HOME", () => {
122
+ expect(source).not.toContain("process.env.HOME");
123
+ });
124
+ test("uses .agentrux directory", () => {
125
+ expect(source).toContain(".agentrux");
126
+ });
127
+ });
128
+ // ---------------------------------------------------------------------------
129
+ // No legacy names in source
130
+ // ---------------------------------------------------------------------------
131
+ describe("No legacy names in source", () => {
132
+ test("no old token prefixes", () => {
133
+ expect(source).not.toContain("atk_");
134
+ expect(source).not.toContain("gtk_");
135
+ });
136
+ test("no old placeholder domains", () => {
137
+ expect(source).not.toContain("example.com");
138
+ expect(source).not.toContain("your-org");
139
+ });
140
+ test("invite code uses inv_ prefix", () => {
141
+ expect(source).toContain("inv_");
142
+ });
143
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * AgenTrux credential management.
3
+ * Credentials persisted to ~/.agentrux/credentials.json (0600).
4
+ */
5
+ export interface Credentials {
6
+ base_url: string;
7
+ script_id: string;
8
+ clientSecret: string;
9
+ }
10
+ export declare function loadCredentials(): Credentials | null;
11
+ export declare function saveCredentials(creds: Credentials): void;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ /**
3
+ * AgenTrux credential management.
4
+ * Credentials persisted to ~/.agentrux/credentials.json (0600).
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.loadCredentials = loadCredentials;
41
+ exports.saveCredentials = saveCredentials;
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ const HOME = process.env.HOME || process.env.USERPROFILE || "~";
45
+ const CREDENTIALS_DIR = path.join(HOME, ".agentrux");
46
+ const CREDENTIALS_PATH = path.join(CREDENTIALS_DIR, "credentials.json");
47
+ function loadCredentials() {
48
+ try {
49
+ if (fs.existsSync(CREDENTIALS_PATH)) {
50
+ return JSON.parse(fs.readFileSync(CREDENTIALS_PATH, "utf-8"));
51
+ }
52
+ }
53
+ catch { }
54
+ return null;
55
+ }
56
+ function saveCredentials(creds) {
57
+ if (!fs.existsSync(CREDENTIALS_DIR)) {
58
+ fs.mkdirSync(CREDENTIALS_DIR, { recursive: true, mode: 0o700 });
59
+ }
60
+ fs.writeFileSync(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), {
61
+ mode: 0o600,
62
+ });
63
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Waterline cursor with atomic persistence.
3
+ * Tracks: waterline (contiguous ack), inFlight, completed, processedEvents.
4
+ */
5
+ export interface CursorState {
6
+ version: number;
7
+ waterline: number;
8
+ inFlight: Set<number>;
9
+ completed: Set<number>;
10
+ processedEvents: Map<string, number>;
11
+ }
12
+ export declare function createEmptyCursor(): CursorState;
13
+ export declare function loadCursor(): CursorState;
14
+ export declare function persistCursor(state: CursorState): void;
15
+ export declare function markInFlight(state: CursorState, seq: number): void;
16
+ export declare function markCompleted(state: CursorState, seq: number): void;
17
+ /**
18
+ * Mark as completed for waterline purposes (used for dead_letter too).
19
+ * Dead letter items are treated as completed to avoid head-of-line blocking.
20
+ */
21
+ export declare function markDeadLetter(state: CursorState, seq: number): void;
22
+ export declare function advanceWaterline(state: CursorState): void;
23
+ export declare function isEventProcessed(state: CursorState, eventId: string): boolean;
24
+ export declare function recordProcessedEvent(state: CursorState, eventId: string): void;
25
+ export declare function cleanupExpiredEvents(state: CursorState): void;
package/dist/cursor.js ADDED
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ /**
3
+ * Waterline cursor with atomic persistence.
4
+ * Tracks: waterline (contiguous ack), inFlight, completed, processedEvents.
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.createEmptyCursor = createEmptyCursor;
41
+ exports.loadCursor = loadCursor;
42
+ exports.persistCursor = persistCursor;
43
+ exports.markInFlight = markInFlight;
44
+ exports.markCompleted = markCompleted;
45
+ exports.markDeadLetter = markDeadLetter;
46
+ exports.advanceWaterline = advanceWaterline;
47
+ exports.isEventProcessed = isEventProcessed;
48
+ exports.recordProcessedEvent = recordProcessedEvent;
49
+ exports.cleanupExpiredEvents = cleanupExpiredEvents;
50
+ const fs = __importStar(require("fs"));
51
+ const path = __importStar(require("path"));
52
+ const HOME = process.env.HOME || process.env.USERPROFILE || "~";
53
+ const CURSOR_PATH = path.join(HOME, ".agentrux", "cursor.json");
54
+ const MAX_PROCESSED_EVENTS = 10_000;
55
+ const EVENT_TTL_MS = 24 * 60 * 60 * 1000; // 24h
56
+ function createEmptyCursor() {
57
+ return {
58
+ version: 1,
59
+ waterline: 0,
60
+ inFlight: new Set(),
61
+ completed: new Set(),
62
+ processedEvents: new Map(),
63
+ };
64
+ }
65
+ function loadCursor() {
66
+ try {
67
+ if (fs.existsSync(CURSOR_PATH)) {
68
+ const raw = JSON.parse(fs.readFileSync(CURSOR_PATH, "utf-8"));
69
+ return {
70
+ version: raw.version || 1,
71
+ waterline: raw.waterline || 0,
72
+ inFlight: new Set(raw.inFlight || []),
73
+ completed: new Set(raw.completed || []),
74
+ processedEvents: new Map(Object.entries(raw.processedEvents || {})),
75
+ };
76
+ }
77
+ }
78
+ catch { }
79
+ return createEmptyCursor();
80
+ }
81
+ function persistCursor(state) {
82
+ const dir = path.dirname(CURSOR_PATH);
83
+ if (!fs.existsSync(dir)) {
84
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
85
+ }
86
+ const data = JSON.stringify({
87
+ version: state.version,
88
+ waterline: state.waterline,
89
+ inFlight: [...state.inFlight],
90
+ completed: [...state.completed],
91
+ processedEvents: Object.fromEntries(state.processedEvents),
92
+ }, null, 2);
93
+ // Atomic write: temp → fsync → rename
94
+ const tmpPath = CURSOR_PATH + ".tmp." + process.pid;
95
+ fs.writeFileSync(tmpPath, data, { mode: 0o600 });
96
+ fs.fsyncSync(fs.openSync(tmpPath, "r"));
97
+ fs.renameSync(tmpPath, CURSOR_PATH);
98
+ }
99
+ function markInFlight(state, seq) {
100
+ state.inFlight.add(seq);
101
+ }
102
+ function markCompleted(state, seq) {
103
+ state.inFlight.delete(seq);
104
+ state.completed.add(seq);
105
+ advanceWaterline(state);
106
+ }
107
+ /**
108
+ * Mark as completed for waterline purposes (used for dead_letter too).
109
+ * Dead letter items are treated as completed to avoid head-of-line blocking.
110
+ */
111
+ function markDeadLetter(state, seq) {
112
+ markCompleted(state, seq);
113
+ }
114
+ function advanceWaterline(state) {
115
+ while (state.completed.has(state.waterline + 1)) {
116
+ state.waterline++;
117
+ state.completed.delete(state.waterline);
118
+ state.inFlight.delete(state.waterline);
119
+ }
120
+ persistCursor(state);
121
+ }
122
+ function isEventProcessed(state, eventId) {
123
+ return state.processedEvents.has(eventId);
124
+ }
125
+ function recordProcessedEvent(state, eventId) {
126
+ state.processedEvents.set(eventId, Date.now());
127
+ }
128
+ function cleanupExpiredEvents(state) {
129
+ const now = Date.now();
130
+ for (const [id, ts] of state.processedEvents) {
131
+ if (now - ts > EVENT_TTL_MS) {
132
+ state.processedEvents.delete(id);
133
+ }
134
+ }
135
+ // Cap size
136
+ if (state.processedEvents.size > MAX_PROCESSED_EVENTS) {
137
+ const entries = [...state.processedEvents.entries()]
138
+ .sort((a, b) => a[1] - b[1]);
139
+ const toRemove = entries.slice(0, entries.length - MAX_PROCESSED_EVENTS);
140
+ for (const [id] of toRemove) {
141
+ state.processedEvents.delete(id);
142
+ }
143
+ }
144
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Dispatcher: async loop.
3
+ * Consumes queue → Pull → dedup → subagent.run() → outbox → waterline.
4
+ */
5
+ import { type Credentials } from "./credentials";
6
+ import { type BoundedQueue } from "./queue";
7
+ import { type CursorState } from "./cursor";
8
+ export interface DispatcherConfig {
9
+ commandTopicId: string;
10
+ resultTopicId: string;
11
+ agentId: string;
12
+ maxConcurrency: number;
13
+ subagentTimeoutMs: number;
14
+ gatewayPort: number;
15
+ }
16
+ export declare class Dispatcher {
17
+ private config;
18
+ private creds;
19
+ private cursor;
20
+ private queue;
21
+ private logger;
22
+ private running;
23
+ private stopped;
24
+ private processedRequestIds;
25
+ private processingSeqs;
26
+ private statusPublished;
27
+ constructor(config: DispatcherConfig, creds: Credentials, cursor: CursorState, queue: BoundedQueue, logger: {
28
+ info: (...a: any[]) => void;
29
+ error: (...a: any[]) => void;
30
+ warn: (...a: any[]) => void;
31
+ });
32
+ start(): Promise<void>;
33
+ stop(): void;
34
+ private loop;
35
+ private tick;
36
+ private processEvent;
37
+ private callDispatchEndpoint;
38
+ }