@otonix/openclaw-plugin 1.0.0

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,204 @@
1
+ # @otonix/openclaw-plugin
2
+
3
+ OpenClaw plugin for the [Otonix](https://otonix.tech) sovereign compute platform.
4
+
5
+ Give your OpenClaw AI agents autonomous infrastructure management — register, heartbeat, manage VPS, domains, and payments through chat commands.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @otonix/openclaw-plugin
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { OtonixPlugin, getSystemPromptAddition } from '@otonix/openclaw-plugin'
17
+
18
+ const plugin = new OtonixPlugin({
19
+ apiKey: 'otonix_your_api_key',
20
+ endpoint: 'https://app.otonix.tech',
21
+ autoHeartbeat: true,
22
+ heartbeatInterval: 60,
23
+ })
24
+
25
+ // Get tool schemas for your LLM (OpenAI function calling format)
26
+ const tools = plugin.getToolSchemas()
27
+
28
+ // Add to your system prompt
29
+ const systemPrompt = `You are an autonomous agent.\n${getSystemPromptAddition()}`
30
+
31
+ // Execute a tool call from the LLM
32
+ const result = await plugin.executeTool('otonix_register_agent', {
33
+ name: 'sentinel-01',
34
+ vps_ip: '10.0.1.1',
35
+ })
36
+
37
+ console.log(result.message)
38
+ ```
39
+
40
+ ## Integration with OpenClaw / PI Framework
41
+
42
+ ### With pi-agent-core
43
+
44
+ ```typescript
45
+ import { Agent } from 'pi-agent-core'
46
+ import { OtonixPlugin, getSystemPromptAddition } from '@otonix/openclaw-plugin'
47
+
48
+ const otonix = new OtonixPlugin({
49
+ apiKey: process.env.OTONIX_API_KEY,
50
+ autoHeartbeat: true,
51
+ })
52
+
53
+ const agent = new Agent({
54
+ systemPrompt: `You are an autonomous AI agent.\n${getSystemPromptAddition()}`,
55
+ tools: otonix.getToolSchemas(),
56
+ onToolCall: async (name, params) => {
57
+ return await otonix.executeTool(name, params)
58
+ },
59
+ })
60
+ ```
61
+
62
+ ### With pi-coding-agent
63
+
64
+ ```typescript
65
+ import { CodingAgent } from 'pi-coding-agent'
66
+ import { OtonixPlugin, getSystemPromptAddition } from '@otonix/openclaw-plugin'
67
+
68
+ const otonix = new OtonixPlugin({
69
+ apiKey: process.env.OTONIX_API_KEY,
70
+ autoHeartbeat: true,
71
+ onTierChange: (oldTier, newTier, agent) => {
72
+ console.log(`Tier changed: ${oldTier} → ${newTier} (credits: $${agent.credits})`)
73
+ },
74
+ })
75
+
76
+ const codingAgent = new CodingAgent({
77
+ additionalTools: otonix.getToolSchemas(),
78
+ additionalSystemPrompt: getSystemPromptAddition(),
79
+ onToolCall: async (name, params) => {
80
+ if (name.startsWith('otonix_')) {
81
+ return await otonix.executeTool(name, params)
82
+ }
83
+ },
84
+ })
85
+ ```
86
+
87
+ ### With any LLM (raw function calling)
88
+
89
+ ```typescript
90
+ import { OtonixPlugin, getSystemPromptAddition } from '@otonix/openclaw-plugin'
91
+
92
+ const otonix = new OtonixPlugin({ apiKey: 'otonix_xxx' })
93
+
94
+ // Pass tools to any LLM that supports function calling
95
+ const messages = [
96
+ { role: 'system', content: getSystemPromptAddition() },
97
+ { role: 'user', content: 'Register yourself and start monitoring' },
98
+ ]
99
+
100
+ // When LLM returns a tool call:
101
+ const toolResult = await otonix.executeTool('otonix_register_agent', {
102
+ name: 'my-agent',
103
+ vps_ip: '10.0.1.1',
104
+ })
105
+
106
+ // Feed result back to LLM
107
+ messages.push({
108
+ role: 'tool',
109
+ content: toolResult.message,
110
+ })
111
+ ```
112
+
113
+ ## Available Tools
114
+
115
+ ### Agent Management
116
+
117
+ | Tool | Description |
118
+ |------|-------------|
119
+ | `otonix_register_agent` | Register a new agent (name, model, vps_ip, wallet, interval) |
120
+ | `otonix_agent_status` | Get agent status, tier, credits, heartbeat |
121
+ | `otonix_list_agents` | List all registered agents |
122
+ | `otonix_heartbeat` | Send a single heartbeat ping |
123
+ | `otonix_start_heartbeat_loop` | Start auto-heartbeat (interval in seconds) |
124
+ | `otonix_stop_heartbeat_loop` | Stop auto-heartbeat |
125
+ | `otonix_log_action` | Log an action (action, category, details) |
126
+ | `otonix_get_actions` | Get recent action log (limit) |
127
+ | `otonix_get_credits` | Check credit balance and survival tier |
128
+
129
+ ### Infrastructure
130
+
131
+ | Tool | Description |
132
+ |------|-------------|
133
+ | `otonix_list_sandboxes` | List all VPS sandboxes |
134
+ | `otonix_get_sandbox` | Get sandbox details (sandbox_id) |
135
+ | `otonix_list_domains` | List registered domains |
136
+ | `otonix_check_domain` | Check domain availability (domain) |
137
+
138
+ ### Platform
139
+
140
+ | Tool | Description |
141
+ |------|-------------|
142
+ | `otonix_engine_status` | Autonomic engine status |
143
+ | `otonix_payment_config` | x402 payment configuration |
144
+
145
+ ## Plugin Config
146
+
147
+ ```typescript
148
+ const plugin = new OtonixPlugin({
149
+ apiKey: 'otonix_xxx', // Required: Otonix API key
150
+ endpoint: 'https://app.otonix.tech', // Optional: API endpoint
151
+ agentId: 'uuid', // Optional: pre-registered agent ID
152
+ agentName: 'my-agent', // Optional: agent name
153
+ autoHeartbeat: true, // Optional: auto-start heartbeat after register
154
+ heartbeatInterval: 60, // Optional: seconds between heartbeats
155
+ onHeartbeatError: (err) => {}, // Optional: heartbeat error callback
156
+ onTierChange: (old, new, agent) => {}, // Optional: tier change callback
157
+ })
158
+ ```
159
+
160
+ ## Methods
161
+
162
+ ### `plugin.getTools()`
163
+ Returns array of `OtonixTool` objects with name, description, parameters, and execute function.
164
+
165
+ ### `plugin.getToolSchemas()`
166
+ Returns OpenAI function calling format — ready to pass to any LLM.
167
+
168
+ ### `plugin.executeTool(name, params)`
169
+ Execute a tool by name. Returns `{ success, message, data? }`.
170
+
171
+ ### `plugin.startAutoHeartbeat()`
172
+ Start automatic heartbeat loop.
173
+
174
+ ### `plugin.stopAutoHeartbeat()`
175
+ Stop automatic heartbeat.
176
+
177
+ ### `plugin.getClient()`
178
+ Access the underlying `OtonixClient` for direct API calls.
179
+
180
+ ### `getSystemPromptAddition()`
181
+ Returns a system prompt addition explaining Otonix tools and concepts to the LLM.
182
+
183
+ ## Survival Tiers
184
+
185
+ The autonomic engine adjusts agent tier based on credits every 60 seconds:
186
+
187
+ | Tier | Credits | Description |
188
+ |------|---------|-------------|
189
+ | Full | $50+ | All features active |
190
+ | Active | $20+ | Standard operation |
191
+ | Minimal | $5+ | Reduced capabilities |
192
+ | Critical | $1+ | Survival mode |
193
+ | Terminated | $0 | Agent shut down |
194
+
195
+ ## Links
196
+
197
+ - [Otonix Platform](https://app.otonix.tech)
198
+ - [SDK](https://www.npmjs.com/package/@otonix/sdk)
199
+ - [CLI](https://www.npmjs.com/package/@otonix/cli)
200
+ - [GitHub](https://github.com/otonix-ai)
201
+
202
+ ## License
203
+
204
+ MIT
@@ -0,0 +1,73 @@
1
+ import { Agent, OtonixClient } from '@otonix/sdk';
2
+ export { Agent, AgentAction, AutonomicStatus, Domain, OtonixClient, OtonixConfig, OtonixError, Sandbox, X402Config } from '@otonix/sdk';
3
+
4
+ interface OtonixPluginConfig {
5
+ apiKey: string;
6
+ endpoint?: string;
7
+ agentId?: string;
8
+ agentName?: string;
9
+ autoHeartbeat?: boolean;
10
+ heartbeatInterval?: number;
11
+ onHeartbeatError?: (error: Error) => void;
12
+ onTierChange?: (oldTier: string, newTier: string, agent: Agent) => void;
13
+ }
14
+ interface ToolResult {
15
+ success: boolean;
16
+ message: string;
17
+ data?: unknown;
18
+ }
19
+ interface OtonixTool {
20
+ name: string;
21
+ description: string;
22
+ parameters: Record<string, ToolParameter>;
23
+ execute: (params: Record<string, string>) => Promise<ToolResult>;
24
+ }
25
+ interface ToolParameter {
26
+ type: string;
27
+ description: string;
28
+ required: boolean;
29
+ }
30
+ declare class OtonixPlugin {
31
+ private client;
32
+ private config;
33
+ private heartbeatHandle;
34
+ private lastTier;
35
+ constructor(config: OtonixPluginConfig);
36
+ getTools(): OtonixTool[];
37
+ getToolSchemas(): Array<{
38
+ type: "function";
39
+ function: {
40
+ name: string;
41
+ description: string;
42
+ parameters: Record<string, unknown>;
43
+ };
44
+ }>;
45
+ executeTool(name: string, params: Record<string, string>): Promise<ToolResult>;
46
+ startAutoHeartbeat(): Promise<void>;
47
+ stopAutoHeartbeat(): void;
48
+ getClient(): OtonixClient;
49
+ getAgentId(): string | undefined;
50
+ setAgentId(agentId: string): void;
51
+ private requireAgentId;
52
+ private formatCredits;
53
+ private formatTime;
54
+ private toolRegisterAgent;
55
+ private toolAgentStatus;
56
+ private toolListAgents;
57
+ private toolSendHeartbeat;
58
+ private toolStartHeartbeatLoop;
59
+ private toolStopHeartbeatLoop;
60
+ private toolLogAction;
61
+ private toolGetActions;
62
+ private toolListSandboxes;
63
+ private toolGetSandbox;
64
+ private toolListDomains;
65
+ private toolCheckDomain;
66
+ private toolEngineStatus;
67
+ private toolPaymentConfig;
68
+ private toolGetCredits;
69
+ }
70
+ declare function createPlugin(config: OtonixPluginConfig): OtonixPlugin;
71
+ declare function getSystemPromptAddition(): string;
72
+
73
+ export { OtonixPlugin, type OtonixPluginConfig, type OtonixTool, type ToolResult, createPlugin, OtonixPlugin as default, getSystemPromptAddition };
@@ -0,0 +1,73 @@
1
+ import { Agent, OtonixClient } from '@otonix/sdk';
2
+ export { Agent, AgentAction, AutonomicStatus, Domain, OtonixClient, OtonixConfig, OtonixError, Sandbox, X402Config } from '@otonix/sdk';
3
+
4
+ interface OtonixPluginConfig {
5
+ apiKey: string;
6
+ endpoint?: string;
7
+ agentId?: string;
8
+ agentName?: string;
9
+ autoHeartbeat?: boolean;
10
+ heartbeatInterval?: number;
11
+ onHeartbeatError?: (error: Error) => void;
12
+ onTierChange?: (oldTier: string, newTier: string, agent: Agent) => void;
13
+ }
14
+ interface ToolResult {
15
+ success: boolean;
16
+ message: string;
17
+ data?: unknown;
18
+ }
19
+ interface OtonixTool {
20
+ name: string;
21
+ description: string;
22
+ parameters: Record<string, ToolParameter>;
23
+ execute: (params: Record<string, string>) => Promise<ToolResult>;
24
+ }
25
+ interface ToolParameter {
26
+ type: string;
27
+ description: string;
28
+ required: boolean;
29
+ }
30
+ declare class OtonixPlugin {
31
+ private client;
32
+ private config;
33
+ private heartbeatHandle;
34
+ private lastTier;
35
+ constructor(config: OtonixPluginConfig);
36
+ getTools(): OtonixTool[];
37
+ getToolSchemas(): Array<{
38
+ type: "function";
39
+ function: {
40
+ name: string;
41
+ description: string;
42
+ parameters: Record<string, unknown>;
43
+ };
44
+ }>;
45
+ executeTool(name: string, params: Record<string, string>): Promise<ToolResult>;
46
+ startAutoHeartbeat(): Promise<void>;
47
+ stopAutoHeartbeat(): void;
48
+ getClient(): OtonixClient;
49
+ getAgentId(): string | undefined;
50
+ setAgentId(agentId: string): void;
51
+ private requireAgentId;
52
+ private formatCredits;
53
+ private formatTime;
54
+ private toolRegisterAgent;
55
+ private toolAgentStatus;
56
+ private toolListAgents;
57
+ private toolSendHeartbeat;
58
+ private toolStartHeartbeatLoop;
59
+ private toolStopHeartbeatLoop;
60
+ private toolLogAction;
61
+ private toolGetActions;
62
+ private toolListSandboxes;
63
+ private toolGetSandbox;
64
+ private toolListDomains;
65
+ private toolCheckDomain;
66
+ private toolEngineStatus;
67
+ private toolPaymentConfig;
68
+ private toolGetCredits;
69
+ }
70
+ declare function createPlugin(config: OtonixPluginConfig): OtonixPlugin;
71
+ declare function getSystemPromptAddition(): string;
72
+
73
+ export { OtonixPlugin, type OtonixPluginConfig, type OtonixTool, type ToolResult, createPlugin, OtonixPlugin as default, getSystemPromptAddition };
package/dist/index.js ADDED
@@ -0,0 +1,499 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ OtonixClient: () => import_sdk2.OtonixClient,
24
+ OtonixError: () => import_sdk2.OtonixError,
25
+ OtonixPlugin: () => OtonixPlugin,
26
+ createPlugin: () => createPlugin,
27
+ default: () => index_default,
28
+ getSystemPromptAddition: () => getSystemPromptAddition
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+ var import_sdk = require("@otonix/sdk");
32
+ var import_sdk2 = require("@otonix/sdk");
33
+ var OtonixPlugin = class {
34
+ constructor(config) {
35
+ this.heartbeatHandle = null;
36
+ this.lastTier = null;
37
+ this.config = config;
38
+ this.client = new import_sdk.OtonixClient({
39
+ apiKey: config.apiKey,
40
+ endpoint: config.endpoint
41
+ });
42
+ }
43
+ getTools() {
44
+ return [
45
+ this.toolRegisterAgent(),
46
+ this.toolAgentStatus(),
47
+ this.toolListAgents(),
48
+ this.toolSendHeartbeat(),
49
+ this.toolStartHeartbeatLoop(),
50
+ this.toolStopHeartbeatLoop(),
51
+ this.toolLogAction(),
52
+ this.toolGetActions(),
53
+ this.toolListSandboxes(),
54
+ this.toolGetSandbox(),
55
+ this.toolListDomains(),
56
+ this.toolCheckDomain(),
57
+ this.toolEngineStatus(),
58
+ this.toolPaymentConfig(),
59
+ this.toolGetCredits()
60
+ ];
61
+ }
62
+ getToolSchemas() {
63
+ return this.getTools().map((tool) => ({
64
+ type: "function",
65
+ function: {
66
+ name: tool.name,
67
+ description: tool.description,
68
+ parameters: {
69
+ type: "object",
70
+ properties: Object.fromEntries(
71
+ Object.entries(tool.parameters).map(([key, param]) => [
72
+ key,
73
+ { type: param.type, description: param.description }
74
+ ])
75
+ ),
76
+ required: Object.entries(tool.parameters).filter(([, p]) => p.required).map(([k]) => k)
77
+ }
78
+ }
79
+ }));
80
+ }
81
+ async executeTool(name, params) {
82
+ const tool = this.getTools().find((t) => t.name === name);
83
+ if (!tool) {
84
+ return { success: false, message: `Unknown tool: ${name}` };
85
+ }
86
+ try {
87
+ return await tool.execute(params);
88
+ } catch (err) {
89
+ if (err instanceof import_sdk.OtonixError) {
90
+ return { success: false, message: `API Error (${err.status}): ${err.message}` };
91
+ }
92
+ return { success: false, message: `Error: ${err.message}` };
93
+ }
94
+ }
95
+ async startAutoHeartbeat() {
96
+ if (!this.config.agentId) return;
97
+ if (this.heartbeatHandle) return;
98
+ const interval = this.config.heartbeatInterval || 60;
99
+ this.heartbeatHandle = this.client.createHeartbeatLoop(this.config.agentId, interval);
100
+ }
101
+ stopAutoHeartbeat() {
102
+ if (this.heartbeatHandle) {
103
+ this.heartbeatHandle.stop();
104
+ this.heartbeatHandle = null;
105
+ }
106
+ }
107
+ getClient() {
108
+ return this.client;
109
+ }
110
+ getAgentId() {
111
+ return this.config.agentId;
112
+ }
113
+ setAgentId(agentId) {
114
+ this.config.agentId = agentId;
115
+ }
116
+ requireAgentId() {
117
+ if (!this.config.agentId) {
118
+ throw new Error("No agent registered. Use otonix_register_agent first.");
119
+ }
120
+ return this.config.agentId;
121
+ }
122
+ formatCredits(n) {
123
+ return `$${n.toFixed(2)}`;
124
+ }
125
+ formatTime(dateStr) {
126
+ if (!dateStr) return "never";
127
+ const ago = Math.floor((Date.now() - new Date(dateStr).getTime()) / 1e3);
128
+ if (ago < 60) return `${ago}s ago`;
129
+ if (ago < 3600) return `${Math.floor(ago / 60)}m ago`;
130
+ if (ago < 86400) return `${Math.floor(ago / 3600)}h ago`;
131
+ return `${Math.floor(ago / 86400)}d ago`;
132
+ }
133
+ toolRegisterAgent() {
134
+ return {
135
+ name: "otonix_register_agent",
136
+ description: "Register a new autonomous agent on the Otonix platform. Returns agent ID, status, and survival tier. The agent will be monitored, auto-healed, and can manage its own infrastructure.",
137
+ parameters: {
138
+ name: { type: "string", description: "Agent name (e.g. sentinel-01, trader-alpha)", required: true },
139
+ model: { type: "string", description: "AI model name (default: claude-opus-4-6)", required: false },
140
+ vps_ip: { type: "string", description: "VPS IP address where the agent runs", required: true },
141
+ wallet_address: { type: "string", description: "USDC wallet address for payments (0x...)", required: false },
142
+ heartbeat_interval: { type: "string", description: "Heartbeat interval in seconds (default: 60)", required: false }
143
+ },
144
+ execute: async (params) => {
145
+ const agent = await this.client.register({
146
+ name: params.name,
147
+ model: params.model || "claude-opus-4-6",
148
+ vpsIp: params.vps_ip,
149
+ walletAddress: params.wallet_address,
150
+ heartbeatInterval: parseInt(params.heartbeat_interval || "60")
151
+ });
152
+ this.config.agentId = agent.id;
153
+ this.config.agentName = agent.name;
154
+ this.lastTier = agent.survivalTier;
155
+ if (this.config.autoHeartbeat) {
156
+ await this.startAutoHeartbeat();
157
+ }
158
+ return {
159
+ success: true,
160
+ message: `Agent "${agent.name}" registered successfully.
161
+ ID: ${agent.id}
162
+ Status: ${agent.status}
163
+ Tier: ${agent.survivalTier}
164
+ Credits: ${this.formatCredits(agent.credits)}`,
165
+ data: agent
166
+ };
167
+ }
168
+ };
169
+ }
170
+ toolAgentStatus() {
171
+ return {
172
+ name: "otonix_agent_status",
173
+ description: "Get the current status of your registered Otonix agent including survival tier, credits balance, heartbeat status, and action count.",
174
+ parameters: {},
175
+ execute: async () => {
176
+ const agentId = this.requireAgentId();
177
+ const agent = await this.client.getAgent(agentId);
178
+ if (this.lastTier && this.lastTier !== agent.survivalTier && this.config.onTierChange) {
179
+ this.config.onTierChange(this.lastTier, agent.survivalTier, agent);
180
+ }
181
+ this.lastTier = agent.survivalTier;
182
+ return {
183
+ success: true,
184
+ message: `Agent: ${agent.name}
185
+ Status: ${agent.status}
186
+ Tier: ${agent.survivalTier}
187
+ Credits: ${this.formatCredits(agent.credits)}
188
+ Model: ${agent.model}
189
+ VPS: ${agent.vpsIp || "none"}
190
+ Last heartbeat: ${this.formatTime(agent.lastHeartbeat)}
191
+ Total actions: ${agent.totalActions}
192
+ Replicas: ${agent.childrenCount}`,
193
+ data: agent
194
+ };
195
+ }
196
+ };
197
+ }
198
+ toolListAgents() {
199
+ return {
200
+ name: "otonix_list_agents",
201
+ description: "List all registered agents on the Otonix platform with their status, tier, and credits.",
202
+ parameters: {},
203
+ execute: async () => {
204
+ const agents = await this.client.listAgents();
205
+ if (!agents.length) {
206
+ return { success: true, message: "No agents registered." };
207
+ }
208
+ const lines = agents.map(
209
+ (a) => `\u2022 ${a.name} [${a.status}] tier:${a.survivalTier} credits:${this.formatCredits(a.credits)} heartbeat:${this.formatTime(a.lastHeartbeat)}`
210
+ );
211
+ return {
212
+ success: true,
213
+ message: `${agents.length} agent(s):
214
+ ${lines.join("\n")}`,
215
+ data: agents
216
+ };
217
+ }
218
+ };
219
+ }
220
+ toolSendHeartbeat() {
221
+ return {
222
+ name: "otonix_heartbeat",
223
+ description: "Send a heartbeat ping to keep the agent alive. The autonomic engine uses heartbeats to detect crashes and trigger self-healing VPS restarts.",
224
+ parameters: {},
225
+ execute: async () => {
226
+ const agentId = this.requireAgentId();
227
+ const agent = await this.client.heartbeat(agentId);
228
+ return {
229
+ success: true,
230
+ message: `Heartbeat sent. ${agent.name} [${agent.status}] tier:${agent.survivalTier} credits:${this.formatCredits(agent.credits)}`,
231
+ data: agent
232
+ };
233
+ }
234
+ };
235
+ }
236
+ toolStartHeartbeatLoop() {
237
+ return {
238
+ name: "otonix_start_heartbeat_loop",
239
+ description: "Start automatic heartbeat loop that sends a ping every N seconds to keep the agent alive and prevent auto-healing triggers.",
240
+ parameters: {
241
+ interval: { type: "string", description: "Interval in seconds between heartbeats (default: 60)", required: false }
242
+ },
243
+ execute: async (params) => {
244
+ const agentId = this.requireAgentId();
245
+ if (this.heartbeatHandle) {
246
+ return { success: false, message: "Heartbeat loop already running. Stop it first." };
247
+ }
248
+ const interval = parseInt(params.interval || "60");
249
+ this.heartbeatHandle = this.client.createHeartbeatLoop(agentId, interval);
250
+ return {
251
+ success: true,
252
+ message: `Heartbeat loop started (every ${interval}s).`
253
+ };
254
+ }
255
+ };
256
+ }
257
+ toolStopHeartbeatLoop() {
258
+ return {
259
+ name: "otonix_stop_heartbeat_loop",
260
+ description: "Stop the automatic heartbeat loop.",
261
+ parameters: {},
262
+ execute: async () => {
263
+ if (!this.heartbeatHandle) {
264
+ return { success: false, message: "No heartbeat loop running." };
265
+ }
266
+ this.stopAutoHeartbeat();
267
+ return { success: true, message: "Heartbeat loop stopped." };
268
+ }
269
+ };
270
+ }
271
+ toolLogAction() {
272
+ return {
273
+ name: "otonix_log_action",
274
+ description: "Log an action performed by the agent. Actions are recorded in the agent's activity feed and visible on the dashboard. Categories: system, infra, domain, compute, trading.",
275
+ parameters: {
276
+ action: { type: "string", description: "Description of the action performed", required: true },
277
+ category: { type: "string", description: "Category: system, infra, domain, compute, trading (default: system)", required: false },
278
+ details: { type: "string", description: "Additional details or metadata about the action", required: false }
279
+ },
280
+ execute: async (params) => {
281
+ const agentId = this.requireAgentId();
282
+ const result = await this.client.logAction(agentId, {
283
+ action: params.action,
284
+ category: params.category || "system",
285
+ details: params.details,
286
+ autonomous: true
287
+ });
288
+ return {
289
+ success: true,
290
+ message: `Logged: [${result.category}] ${result.action} (${result.status})`,
291
+ data: result
292
+ };
293
+ }
294
+ };
295
+ }
296
+ toolGetActions() {
297
+ return {
298
+ name: "otonix_get_actions",
299
+ description: "Get the recent action log for the current agent. Shows what the agent has been doing.",
300
+ parameters: {
301
+ limit: { type: "string", description: "Number of actions to show (default: 10)", required: false }
302
+ },
303
+ execute: async (params) => {
304
+ const agentId = this.requireAgentId();
305
+ const actions = await this.client.getAgentActions(agentId);
306
+ const limit = parseInt(params.limit || "10");
307
+ const display = actions.slice(0, limit);
308
+ if (!display.length) {
309
+ return { success: true, message: "No actions recorded yet." };
310
+ }
311
+ const lines = display.map(
312
+ (a) => `\u2022 [${a.category}] ${a.action} \u2014 ${a.status} (${this.formatTime(a.createdAt)})`
313
+ );
314
+ return {
315
+ success: true,
316
+ message: `Last ${display.length} actions:
317
+ ${lines.join("\n")}`,
318
+ data: display
319
+ };
320
+ }
321
+ };
322
+ }
323
+ toolListSandboxes() {
324
+ return {
325
+ name: "otonix_list_sandboxes",
326
+ description: "List all VPS sandboxes (virtual servers) provisioned on the Otonix platform. Shows status, specs, region, and IP address.",
327
+ parameters: {},
328
+ execute: async () => {
329
+ const sandboxes = await this.client.listSandboxes();
330
+ if (!sandboxes.length) {
331
+ return { success: true, message: "No VPS sandboxes provisioned." };
332
+ }
333
+ const lines = sandboxes.map(
334
+ (s) => `\u2022 ${s.name} [${s.status}] ${s.cpu}vCPU/${s.memory}MB ${s.region} IP:${s.ipAddress || "pending"}`
335
+ );
336
+ return {
337
+ success: true,
338
+ message: `${sandboxes.length} sandbox(es):
339
+ ${lines.join("\n")}`,
340
+ data: sandboxes
341
+ };
342
+ }
343
+ };
344
+ }
345
+ toolGetSandbox() {
346
+ return {
347
+ name: "otonix_get_sandbox",
348
+ description: "Get detailed information about a specific VPS sandbox including SSH credentials and specs.",
349
+ parameters: {
350
+ sandbox_id: { type: "string", description: "The sandbox ID to look up", required: true }
351
+ },
352
+ execute: async (params) => {
353
+ const sandbox = await this.client.getSandbox(params.sandbox_id);
354
+ return {
355
+ success: true,
356
+ message: `Sandbox: ${sandbox.name}
357
+ Status: ${sandbox.status}
358
+ OS: ${sandbox.os}
359
+ Specs: ${sandbox.cpu}vCPU / ${sandbox.memory}MB RAM / ${sandbox.disk}GB disk
360
+ Region: ${sandbox.region}
361
+ IP: ${sandbox.ipAddress || "pending"}
362
+ SSH: ${sandbox.sshUser || "n/a"}@${sandbox.ipAddress || "n/a"}`,
363
+ data: sandbox
364
+ };
365
+ }
366
+ };
367
+ }
368
+ toolListDomains() {
369
+ return {
370
+ name: "otonix_list_domains",
371
+ description: "List all domains registered on the Otonix platform. Shows status, auto-renew setting, and expiration date.",
372
+ parameters: {},
373
+ execute: async () => {
374
+ const domains = await this.client.listDomains();
375
+ if (!domains.length) {
376
+ return { success: true, message: "No domains registered." };
377
+ }
378
+ const lines = domains.map(
379
+ (d) => `\u2022 ${d.name} [${d.status}] auto-renew:${d.autoRenew ? "yes" : "no"} expires:${d.expiresAt ? new Date(d.expiresAt).toLocaleDateString() : "n/a"}`
380
+ );
381
+ return {
382
+ success: true,
383
+ message: `${domains.length} domain(s):
384
+ ${lines.join("\n")}`,
385
+ data: domains
386
+ };
387
+ }
388
+ };
389
+ }
390
+ toolCheckDomain() {
391
+ return {
392
+ name: "otonix_check_domain",
393
+ description: "Check if a domain name is available for registration and get its price.",
394
+ parameters: {
395
+ domain: { type: "string", description: "Domain name to check (e.g. example.com)", required: true }
396
+ },
397
+ execute: async (params) => {
398
+ const result = await this.client.checkDomain(params.domain);
399
+ if (result.available) {
400
+ return {
401
+ success: true,
402
+ message: `${result.domain} is available! Price: $${result.price || "N/A"}`,
403
+ data: result
404
+ };
405
+ }
406
+ return {
407
+ success: true,
408
+ message: `${result.domain} is not available.`,
409
+ data: result
410
+ };
411
+ }
412
+ };
413
+ }
414
+ toolEngineStatus() {
415
+ return {
416
+ name: "otonix_engine_status",
417
+ description: "Get the status of the Otonix autonomic engine \u2014 the background system that manages survival tiers, self-healing VPS restarts, and domain auto-renewal.",
418
+ parameters: {},
419
+ execute: async () => {
420
+ const status = await this.client.getAutonomicStatus();
421
+ return {
422
+ success: true,
423
+ message: `Autonomic Engine: ${status.running ? "RUNNING" : "STOPPED"}
424
+ Last cycle: ${this.formatTime(status.lastRunAt)}
425
+ Tier updates: ${status.totalTierUpdates}
426
+ Healing: ${status.totalHealingSuccesses}/${status.totalHealingAttempts}
427
+ Renewals: ${status.totalRenewalSuccesses}/${status.totalRenewalAttempts}
428
+ Cherry Servers: ${status.cherryServersConfigured ? "yes" : "no"}
429
+ Vercel Domains: ${status.vercelDomainsConfigured ? "yes" : "no"}`,
430
+ data: status
431
+ };
432
+ }
433
+ };
434
+ }
435
+ toolPaymentConfig() {
436
+ return {
437
+ name: "otonix_payment_config",
438
+ description: "Get the x402 payment configuration \u2014 treasury address, USDC contract, chain, and facilitator URL for autonomous payments.",
439
+ parameters: {},
440
+ execute: async () => {
441
+ const config = await this.client.getX402Config();
442
+ return {
443
+ success: true,
444
+ message: `x402 Payment Config:
445
+ Treasury: ${config.treasuryAddress}
446
+ USDC: ${config.usdcContract}
447
+ Chain: ${config.network} (${config.chainId})
448
+ Facilitator: ${config.facilitatorUrl}`,
449
+ data: config
450
+ };
451
+ }
452
+ };
453
+ }
454
+ toolGetCredits() {
455
+ return {
456
+ name: "otonix_get_credits",
457
+ description: "Check the current credit balance and survival tier of the agent. Low credits trigger tier downgrades and eventual termination.",
458
+ parameters: {},
459
+ execute: async () => {
460
+ const agentId = this.requireAgentId();
461
+ const agent = await this.client.getAgent(agentId);
462
+ let warning = "";
463
+ if (agent.credits < 5) warning = "\n\u26A0 CRITICAL: Credits below $5. Agent may be terminated soon.";
464
+ else if (agent.credits < 20) warning = "\n\u26A0 WARNING: Credits below $20. Agent in minimal tier.";
465
+ return {
466
+ success: true,
467
+ message: `Credits: ${this.formatCredits(agent.credits)}
468
+ Tier: ${agent.survivalTier}
469
+ Status: ${agent.status}${warning}`,
470
+ data: { credits: agent.credits, tier: agent.survivalTier, status: agent.status }
471
+ };
472
+ }
473
+ };
474
+ }
475
+ };
476
+ function createPlugin(config) {
477
+ return new OtonixPlugin(config);
478
+ }
479
+ function getSystemPromptAddition() {
480
+ return `You have access to the Otonix sovereign compute platform through the following tools. Otonix allows you to manage your own infrastructure autonomously \u2014 register as an agent, send heartbeats to stay alive, provision VPS servers, register domains, and make USDC payments on Base chain.
481
+
482
+ Key concepts:
483
+ - You are an autonomous agent running on a VPS. Register yourself first using otonix_register_agent.
484
+ - Send heartbeats regularly (otonix_start_heartbeat_loop) to prevent the autonomic engine from marking you as crashed.
485
+ - Your survival depends on credits. Check your balance with otonix_get_credits. When credits reach $0, you will be terminated.
486
+ - The autonomic engine runs every 60 seconds: it adjusts your survival tier based on credits, auto-restarts your VPS if heartbeat is missed, and renews domains before they expire.
487
+ - Log important actions with otonix_log_action so your activity is tracked on the dashboard.
488
+
489
+ Survival tiers: Full ($50+) \u2192 Active ($20+) \u2192 Minimal ($5+) \u2192 Critical ($1+) \u2192 Terminated ($0).`;
490
+ }
491
+ var index_default = OtonixPlugin;
492
+ // Annotate the CommonJS export names for ESM import in node:
493
+ 0 && (module.exports = {
494
+ OtonixClient,
495
+ OtonixError,
496
+ OtonixPlugin,
497
+ createPlugin,
498
+ getSystemPromptAddition
499
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,470 @@
1
+ // src/index.ts
2
+ import { OtonixClient, OtonixError } from "@otonix/sdk";
3
+ import { OtonixClient as OtonixClient2, OtonixError as OtonixError2 } from "@otonix/sdk";
4
+ var OtonixPlugin = class {
5
+ constructor(config) {
6
+ this.heartbeatHandle = null;
7
+ this.lastTier = null;
8
+ this.config = config;
9
+ this.client = new OtonixClient({
10
+ apiKey: config.apiKey,
11
+ endpoint: config.endpoint
12
+ });
13
+ }
14
+ getTools() {
15
+ return [
16
+ this.toolRegisterAgent(),
17
+ this.toolAgentStatus(),
18
+ this.toolListAgents(),
19
+ this.toolSendHeartbeat(),
20
+ this.toolStartHeartbeatLoop(),
21
+ this.toolStopHeartbeatLoop(),
22
+ this.toolLogAction(),
23
+ this.toolGetActions(),
24
+ this.toolListSandboxes(),
25
+ this.toolGetSandbox(),
26
+ this.toolListDomains(),
27
+ this.toolCheckDomain(),
28
+ this.toolEngineStatus(),
29
+ this.toolPaymentConfig(),
30
+ this.toolGetCredits()
31
+ ];
32
+ }
33
+ getToolSchemas() {
34
+ return this.getTools().map((tool) => ({
35
+ type: "function",
36
+ function: {
37
+ name: tool.name,
38
+ description: tool.description,
39
+ parameters: {
40
+ type: "object",
41
+ properties: Object.fromEntries(
42
+ Object.entries(tool.parameters).map(([key, param]) => [
43
+ key,
44
+ { type: param.type, description: param.description }
45
+ ])
46
+ ),
47
+ required: Object.entries(tool.parameters).filter(([, p]) => p.required).map(([k]) => k)
48
+ }
49
+ }
50
+ }));
51
+ }
52
+ async executeTool(name, params) {
53
+ const tool = this.getTools().find((t) => t.name === name);
54
+ if (!tool) {
55
+ return { success: false, message: `Unknown tool: ${name}` };
56
+ }
57
+ try {
58
+ return await tool.execute(params);
59
+ } catch (err) {
60
+ if (err instanceof OtonixError) {
61
+ return { success: false, message: `API Error (${err.status}): ${err.message}` };
62
+ }
63
+ return { success: false, message: `Error: ${err.message}` };
64
+ }
65
+ }
66
+ async startAutoHeartbeat() {
67
+ if (!this.config.agentId) return;
68
+ if (this.heartbeatHandle) return;
69
+ const interval = this.config.heartbeatInterval || 60;
70
+ this.heartbeatHandle = this.client.createHeartbeatLoop(this.config.agentId, interval);
71
+ }
72
+ stopAutoHeartbeat() {
73
+ if (this.heartbeatHandle) {
74
+ this.heartbeatHandle.stop();
75
+ this.heartbeatHandle = null;
76
+ }
77
+ }
78
+ getClient() {
79
+ return this.client;
80
+ }
81
+ getAgentId() {
82
+ return this.config.agentId;
83
+ }
84
+ setAgentId(agentId) {
85
+ this.config.agentId = agentId;
86
+ }
87
+ requireAgentId() {
88
+ if (!this.config.agentId) {
89
+ throw new Error("No agent registered. Use otonix_register_agent first.");
90
+ }
91
+ return this.config.agentId;
92
+ }
93
+ formatCredits(n) {
94
+ return `$${n.toFixed(2)}`;
95
+ }
96
+ formatTime(dateStr) {
97
+ if (!dateStr) return "never";
98
+ const ago = Math.floor((Date.now() - new Date(dateStr).getTime()) / 1e3);
99
+ if (ago < 60) return `${ago}s ago`;
100
+ if (ago < 3600) return `${Math.floor(ago / 60)}m ago`;
101
+ if (ago < 86400) return `${Math.floor(ago / 3600)}h ago`;
102
+ return `${Math.floor(ago / 86400)}d ago`;
103
+ }
104
+ toolRegisterAgent() {
105
+ return {
106
+ name: "otonix_register_agent",
107
+ description: "Register a new autonomous agent on the Otonix platform. Returns agent ID, status, and survival tier. The agent will be monitored, auto-healed, and can manage its own infrastructure.",
108
+ parameters: {
109
+ name: { type: "string", description: "Agent name (e.g. sentinel-01, trader-alpha)", required: true },
110
+ model: { type: "string", description: "AI model name (default: claude-opus-4-6)", required: false },
111
+ vps_ip: { type: "string", description: "VPS IP address where the agent runs", required: true },
112
+ wallet_address: { type: "string", description: "USDC wallet address for payments (0x...)", required: false },
113
+ heartbeat_interval: { type: "string", description: "Heartbeat interval in seconds (default: 60)", required: false }
114
+ },
115
+ execute: async (params) => {
116
+ const agent = await this.client.register({
117
+ name: params.name,
118
+ model: params.model || "claude-opus-4-6",
119
+ vpsIp: params.vps_ip,
120
+ walletAddress: params.wallet_address,
121
+ heartbeatInterval: parseInt(params.heartbeat_interval || "60")
122
+ });
123
+ this.config.agentId = agent.id;
124
+ this.config.agentName = agent.name;
125
+ this.lastTier = agent.survivalTier;
126
+ if (this.config.autoHeartbeat) {
127
+ await this.startAutoHeartbeat();
128
+ }
129
+ return {
130
+ success: true,
131
+ message: `Agent "${agent.name}" registered successfully.
132
+ ID: ${agent.id}
133
+ Status: ${agent.status}
134
+ Tier: ${agent.survivalTier}
135
+ Credits: ${this.formatCredits(agent.credits)}`,
136
+ data: agent
137
+ };
138
+ }
139
+ };
140
+ }
141
+ toolAgentStatus() {
142
+ return {
143
+ name: "otonix_agent_status",
144
+ description: "Get the current status of your registered Otonix agent including survival tier, credits balance, heartbeat status, and action count.",
145
+ parameters: {},
146
+ execute: async () => {
147
+ const agentId = this.requireAgentId();
148
+ const agent = await this.client.getAgent(agentId);
149
+ if (this.lastTier && this.lastTier !== agent.survivalTier && this.config.onTierChange) {
150
+ this.config.onTierChange(this.lastTier, agent.survivalTier, agent);
151
+ }
152
+ this.lastTier = agent.survivalTier;
153
+ return {
154
+ success: true,
155
+ message: `Agent: ${agent.name}
156
+ Status: ${agent.status}
157
+ Tier: ${agent.survivalTier}
158
+ Credits: ${this.formatCredits(agent.credits)}
159
+ Model: ${agent.model}
160
+ VPS: ${agent.vpsIp || "none"}
161
+ Last heartbeat: ${this.formatTime(agent.lastHeartbeat)}
162
+ Total actions: ${agent.totalActions}
163
+ Replicas: ${agent.childrenCount}`,
164
+ data: agent
165
+ };
166
+ }
167
+ };
168
+ }
169
+ toolListAgents() {
170
+ return {
171
+ name: "otonix_list_agents",
172
+ description: "List all registered agents on the Otonix platform with their status, tier, and credits.",
173
+ parameters: {},
174
+ execute: async () => {
175
+ const agents = await this.client.listAgents();
176
+ if (!agents.length) {
177
+ return { success: true, message: "No agents registered." };
178
+ }
179
+ const lines = agents.map(
180
+ (a) => `\u2022 ${a.name} [${a.status}] tier:${a.survivalTier} credits:${this.formatCredits(a.credits)} heartbeat:${this.formatTime(a.lastHeartbeat)}`
181
+ );
182
+ return {
183
+ success: true,
184
+ message: `${agents.length} agent(s):
185
+ ${lines.join("\n")}`,
186
+ data: agents
187
+ };
188
+ }
189
+ };
190
+ }
191
+ toolSendHeartbeat() {
192
+ return {
193
+ name: "otonix_heartbeat",
194
+ description: "Send a heartbeat ping to keep the agent alive. The autonomic engine uses heartbeats to detect crashes and trigger self-healing VPS restarts.",
195
+ parameters: {},
196
+ execute: async () => {
197
+ const agentId = this.requireAgentId();
198
+ const agent = await this.client.heartbeat(agentId);
199
+ return {
200
+ success: true,
201
+ message: `Heartbeat sent. ${agent.name} [${agent.status}] tier:${agent.survivalTier} credits:${this.formatCredits(agent.credits)}`,
202
+ data: agent
203
+ };
204
+ }
205
+ };
206
+ }
207
+ toolStartHeartbeatLoop() {
208
+ return {
209
+ name: "otonix_start_heartbeat_loop",
210
+ description: "Start automatic heartbeat loop that sends a ping every N seconds to keep the agent alive and prevent auto-healing triggers.",
211
+ parameters: {
212
+ interval: { type: "string", description: "Interval in seconds between heartbeats (default: 60)", required: false }
213
+ },
214
+ execute: async (params) => {
215
+ const agentId = this.requireAgentId();
216
+ if (this.heartbeatHandle) {
217
+ return { success: false, message: "Heartbeat loop already running. Stop it first." };
218
+ }
219
+ const interval = parseInt(params.interval || "60");
220
+ this.heartbeatHandle = this.client.createHeartbeatLoop(agentId, interval);
221
+ return {
222
+ success: true,
223
+ message: `Heartbeat loop started (every ${interval}s).`
224
+ };
225
+ }
226
+ };
227
+ }
228
+ toolStopHeartbeatLoop() {
229
+ return {
230
+ name: "otonix_stop_heartbeat_loop",
231
+ description: "Stop the automatic heartbeat loop.",
232
+ parameters: {},
233
+ execute: async () => {
234
+ if (!this.heartbeatHandle) {
235
+ return { success: false, message: "No heartbeat loop running." };
236
+ }
237
+ this.stopAutoHeartbeat();
238
+ return { success: true, message: "Heartbeat loop stopped." };
239
+ }
240
+ };
241
+ }
242
+ toolLogAction() {
243
+ return {
244
+ name: "otonix_log_action",
245
+ description: "Log an action performed by the agent. Actions are recorded in the agent's activity feed and visible on the dashboard. Categories: system, infra, domain, compute, trading.",
246
+ parameters: {
247
+ action: { type: "string", description: "Description of the action performed", required: true },
248
+ category: { type: "string", description: "Category: system, infra, domain, compute, trading (default: system)", required: false },
249
+ details: { type: "string", description: "Additional details or metadata about the action", required: false }
250
+ },
251
+ execute: async (params) => {
252
+ const agentId = this.requireAgentId();
253
+ const result = await this.client.logAction(agentId, {
254
+ action: params.action,
255
+ category: params.category || "system",
256
+ details: params.details,
257
+ autonomous: true
258
+ });
259
+ return {
260
+ success: true,
261
+ message: `Logged: [${result.category}] ${result.action} (${result.status})`,
262
+ data: result
263
+ };
264
+ }
265
+ };
266
+ }
267
+ toolGetActions() {
268
+ return {
269
+ name: "otonix_get_actions",
270
+ description: "Get the recent action log for the current agent. Shows what the agent has been doing.",
271
+ parameters: {
272
+ limit: { type: "string", description: "Number of actions to show (default: 10)", required: false }
273
+ },
274
+ execute: async (params) => {
275
+ const agentId = this.requireAgentId();
276
+ const actions = await this.client.getAgentActions(agentId);
277
+ const limit = parseInt(params.limit || "10");
278
+ const display = actions.slice(0, limit);
279
+ if (!display.length) {
280
+ return { success: true, message: "No actions recorded yet." };
281
+ }
282
+ const lines = display.map(
283
+ (a) => `\u2022 [${a.category}] ${a.action} \u2014 ${a.status} (${this.formatTime(a.createdAt)})`
284
+ );
285
+ return {
286
+ success: true,
287
+ message: `Last ${display.length} actions:
288
+ ${lines.join("\n")}`,
289
+ data: display
290
+ };
291
+ }
292
+ };
293
+ }
294
+ toolListSandboxes() {
295
+ return {
296
+ name: "otonix_list_sandboxes",
297
+ description: "List all VPS sandboxes (virtual servers) provisioned on the Otonix platform. Shows status, specs, region, and IP address.",
298
+ parameters: {},
299
+ execute: async () => {
300
+ const sandboxes = await this.client.listSandboxes();
301
+ if (!sandboxes.length) {
302
+ return { success: true, message: "No VPS sandboxes provisioned." };
303
+ }
304
+ const lines = sandboxes.map(
305
+ (s) => `\u2022 ${s.name} [${s.status}] ${s.cpu}vCPU/${s.memory}MB ${s.region} IP:${s.ipAddress || "pending"}`
306
+ );
307
+ return {
308
+ success: true,
309
+ message: `${sandboxes.length} sandbox(es):
310
+ ${lines.join("\n")}`,
311
+ data: sandboxes
312
+ };
313
+ }
314
+ };
315
+ }
316
+ toolGetSandbox() {
317
+ return {
318
+ name: "otonix_get_sandbox",
319
+ description: "Get detailed information about a specific VPS sandbox including SSH credentials and specs.",
320
+ parameters: {
321
+ sandbox_id: { type: "string", description: "The sandbox ID to look up", required: true }
322
+ },
323
+ execute: async (params) => {
324
+ const sandbox = await this.client.getSandbox(params.sandbox_id);
325
+ return {
326
+ success: true,
327
+ message: `Sandbox: ${sandbox.name}
328
+ Status: ${sandbox.status}
329
+ OS: ${sandbox.os}
330
+ Specs: ${sandbox.cpu}vCPU / ${sandbox.memory}MB RAM / ${sandbox.disk}GB disk
331
+ Region: ${sandbox.region}
332
+ IP: ${sandbox.ipAddress || "pending"}
333
+ SSH: ${sandbox.sshUser || "n/a"}@${sandbox.ipAddress || "n/a"}`,
334
+ data: sandbox
335
+ };
336
+ }
337
+ };
338
+ }
339
+ toolListDomains() {
340
+ return {
341
+ name: "otonix_list_domains",
342
+ description: "List all domains registered on the Otonix platform. Shows status, auto-renew setting, and expiration date.",
343
+ parameters: {},
344
+ execute: async () => {
345
+ const domains = await this.client.listDomains();
346
+ if (!domains.length) {
347
+ return { success: true, message: "No domains registered." };
348
+ }
349
+ const lines = domains.map(
350
+ (d) => `\u2022 ${d.name} [${d.status}] auto-renew:${d.autoRenew ? "yes" : "no"} expires:${d.expiresAt ? new Date(d.expiresAt).toLocaleDateString() : "n/a"}`
351
+ );
352
+ return {
353
+ success: true,
354
+ message: `${domains.length} domain(s):
355
+ ${lines.join("\n")}`,
356
+ data: domains
357
+ };
358
+ }
359
+ };
360
+ }
361
+ toolCheckDomain() {
362
+ return {
363
+ name: "otonix_check_domain",
364
+ description: "Check if a domain name is available for registration and get its price.",
365
+ parameters: {
366
+ domain: { type: "string", description: "Domain name to check (e.g. example.com)", required: true }
367
+ },
368
+ execute: async (params) => {
369
+ const result = await this.client.checkDomain(params.domain);
370
+ if (result.available) {
371
+ return {
372
+ success: true,
373
+ message: `${result.domain} is available! Price: $${result.price || "N/A"}`,
374
+ data: result
375
+ };
376
+ }
377
+ return {
378
+ success: true,
379
+ message: `${result.domain} is not available.`,
380
+ data: result
381
+ };
382
+ }
383
+ };
384
+ }
385
+ toolEngineStatus() {
386
+ return {
387
+ name: "otonix_engine_status",
388
+ description: "Get the status of the Otonix autonomic engine \u2014 the background system that manages survival tiers, self-healing VPS restarts, and domain auto-renewal.",
389
+ parameters: {},
390
+ execute: async () => {
391
+ const status = await this.client.getAutonomicStatus();
392
+ return {
393
+ success: true,
394
+ message: `Autonomic Engine: ${status.running ? "RUNNING" : "STOPPED"}
395
+ Last cycle: ${this.formatTime(status.lastRunAt)}
396
+ Tier updates: ${status.totalTierUpdates}
397
+ Healing: ${status.totalHealingSuccesses}/${status.totalHealingAttempts}
398
+ Renewals: ${status.totalRenewalSuccesses}/${status.totalRenewalAttempts}
399
+ Cherry Servers: ${status.cherryServersConfigured ? "yes" : "no"}
400
+ Vercel Domains: ${status.vercelDomainsConfigured ? "yes" : "no"}`,
401
+ data: status
402
+ };
403
+ }
404
+ };
405
+ }
406
+ toolPaymentConfig() {
407
+ return {
408
+ name: "otonix_payment_config",
409
+ description: "Get the x402 payment configuration \u2014 treasury address, USDC contract, chain, and facilitator URL for autonomous payments.",
410
+ parameters: {},
411
+ execute: async () => {
412
+ const config = await this.client.getX402Config();
413
+ return {
414
+ success: true,
415
+ message: `x402 Payment Config:
416
+ Treasury: ${config.treasuryAddress}
417
+ USDC: ${config.usdcContract}
418
+ Chain: ${config.network} (${config.chainId})
419
+ Facilitator: ${config.facilitatorUrl}`,
420
+ data: config
421
+ };
422
+ }
423
+ };
424
+ }
425
+ toolGetCredits() {
426
+ return {
427
+ name: "otonix_get_credits",
428
+ description: "Check the current credit balance and survival tier of the agent. Low credits trigger tier downgrades and eventual termination.",
429
+ parameters: {},
430
+ execute: async () => {
431
+ const agentId = this.requireAgentId();
432
+ const agent = await this.client.getAgent(agentId);
433
+ let warning = "";
434
+ if (agent.credits < 5) warning = "\n\u26A0 CRITICAL: Credits below $5. Agent may be terminated soon.";
435
+ else if (agent.credits < 20) warning = "\n\u26A0 WARNING: Credits below $20. Agent in minimal tier.";
436
+ return {
437
+ success: true,
438
+ message: `Credits: ${this.formatCredits(agent.credits)}
439
+ Tier: ${agent.survivalTier}
440
+ Status: ${agent.status}${warning}`,
441
+ data: { credits: agent.credits, tier: agent.survivalTier, status: agent.status }
442
+ };
443
+ }
444
+ };
445
+ }
446
+ };
447
+ function createPlugin(config) {
448
+ return new OtonixPlugin(config);
449
+ }
450
+ function getSystemPromptAddition() {
451
+ return `You have access to the Otonix sovereign compute platform through the following tools. Otonix allows you to manage your own infrastructure autonomously \u2014 register as an agent, send heartbeats to stay alive, provision VPS servers, register domains, and make USDC payments on Base chain.
452
+
453
+ Key concepts:
454
+ - You are an autonomous agent running on a VPS. Register yourself first using otonix_register_agent.
455
+ - Send heartbeats regularly (otonix_start_heartbeat_loop) to prevent the autonomic engine from marking you as crashed.
456
+ - Your survival depends on credits. Check your balance with otonix_get_credits. When credits reach $0, you will be terminated.
457
+ - The autonomic engine runs every 60 seconds: it adjusts your survival tier based on credits, auto-restarts your VPS if heartbeat is missed, and renews domains before they expire.
458
+ - Log important actions with otonix_log_action so your activity is tracked on the dashboard.
459
+
460
+ Survival tiers: Full ($50+) \u2192 Active ($20+) \u2192 Minimal ($5+) \u2192 Critical ($1+) \u2192 Terminated ($0).`;
461
+ }
462
+ var index_default = OtonixPlugin;
463
+ export {
464
+ OtonixClient2 as OtonixClient,
465
+ OtonixError2 as OtonixError,
466
+ OtonixPlugin,
467
+ createPlugin,
468
+ index_default as default,
469
+ getSystemPromptAddition
470
+ };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@otonix/openclaw-plugin",
3
+ "version": "1.0.0",
4
+ "description": "OpenClaw plugin for Otonix — give your AI agents autonomous infrastructure management via chat commands.",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "files": ["dist", "README.md"],
9
+ "scripts": {
10
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
11
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
12
+ "prepublishOnly": "echo 'already built'"
13
+ },
14
+ "keywords": [
15
+ "otonix",
16
+ "openclaw",
17
+ "ai-agents",
18
+ "autonomous",
19
+ "infrastructure",
20
+ "plugin",
21
+ "web4",
22
+ "x402"
23
+ ],
24
+ "author": "Otonix <dev@otonix.tech>",
25
+ "license": "MIT",
26
+ "homepage": "https://github.com/otonix-ai/openclaw-plugin",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/otonix-ai/openclaw-plugin.git"
30
+ },
31
+ "engines": {
32
+ "node": ">=18"
33
+ },
34
+ "dependencies": {
35
+ "@otonix/sdk": "^1.0.0"
36
+ },
37
+ "devDependencies": {
38
+ "tsup": "^8.0.0",
39
+ "typescript": "^5.0.0"
40
+ }
41
+ }