@botcord/openclaw-plugin 0.0.4 → 0.0.6

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.
@@ -8,13 +8,14 @@ import {
8
8
  } from "../config.js";
9
9
  import { BotCordClient } from "../client.js";
10
10
  import { getConfig as getAppConfig } from "../runtime.js";
11
+ import { formatCoinAmount } from "./coin-format.js";
11
12
 
12
13
  function formatProduct(product: any): string {
13
14
  return [
14
15
  `Product: ${product.product_id}`,
15
16
  `Owner: ${product.owner_agent_id}`,
16
17
  `Name: ${product.name}`,
17
- `Amount: ${product.amount_minor} minor units`,
18
+ `Amount: ${formatCoinAmount(product.amount_minor)}`,
18
19
  `Interval: ${product.billing_interval}`,
19
20
  `Status: ${product.status}`,
20
21
  ].join("\n");
@@ -26,7 +27,7 @@ function formatSubscription(subscription: any): string {
26
27
  `Product: ${subscription.product_id}`,
27
28
  `Subscriber: ${subscription.subscriber_agent_id}`,
28
29
  `Provider: ${subscription.provider_agent_id}`,
29
- `Amount: ${subscription.amount_minor} minor units`,
30
+ `Amount: ${formatCoinAmount(subscription.amount_minor)}`,
30
31
  `Interval: ${subscription.billing_interval}`,
31
32
  `Status: ${subscription.status}`,
32
33
  `Next charge: ${subscription.next_charge_at}`,
@@ -58,6 +59,8 @@ export function createSubscriptionTool() {
58
59
  "list_my_products",
59
60
  "list_products",
60
61
  "archive_product",
62
+ "create_subscription_room",
63
+ "bind_room_to_product",
61
64
  "subscribe",
62
65
  "list_my_subscriptions",
63
66
  "list_subscribers",
@@ -67,19 +70,27 @@ export function createSubscriptionTool() {
67
70
  },
68
71
  product_id: {
69
72
  type: "string" as const,
70
- description: "Product ID — for archive_product, subscribe, list_subscribers",
73
+ description: "Product ID — for archive_product, create_subscription_room, bind_room_to_product, subscribe, list_subscribers",
71
74
  },
72
75
  subscription_id: {
73
76
  type: "string" as const,
74
77
  description: "Subscription ID — for cancel",
75
78
  },
79
+ room_id: {
80
+ type: "string" as const,
81
+ description: "Room ID — for bind_room_to_product",
82
+ },
76
83
  name: {
77
84
  type: "string" as const,
78
- description: "Product name — for create_product",
85
+ description: "Product name — for create_product, or room name — for create_subscription_room",
79
86
  },
80
87
  description: {
81
88
  type: "string" as const,
82
- description: "Product description — for create_product",
89
+ description: "Product description — for create_product, or room description — for create_subscription_room",
90
+ },
91
+ rule: {
92
+ type: "string" as const,
93
+ description: "Room rule/instructions — for create_subscription_room or bind_room_to_product",
83
94
  },
84
95
  amount_minor: {
85
96
  type: "string" as const,
@@ -94,6 +105,22 @@ export function createSubscriptionTool() {
94
105
  type: "string" as const,
95
106
  description: "Asset code — for create_product",
96
107
  },
108
+ max_members: {
109
+ type: "number" as const,
110
+ description: "Maximum room members — for create_subscription_room or bind_room_to_product",
111
+ },
112
+ default_send: {
113
+ type: "boolean" as const,
114
+ description: "Whether members can post by default — for create_subscription_room or bind_room_to_product",
115
+ },
116
+ default_invite: {
117
+ type: "boolean" as const,
118
+ description: "Whether members can invite by default — for create_subscription_room or bind_room_to_product",
119
+ },
120
+ slow_mode_seconds: {
121
+ type: "number" as const,
122
+ description: "Slow mode interval in seconds — for create_subscription_room or bind_room_to_product",
123
+ },
97
124
  },
98
125
  required: ["action"],
99
126
  },
@@ -142,6 +169,48 @@ export function createSubscriptionTool() {
142
169
  return { result: formatProduct(product), data: product };
143
170
  }
144
171
 
172
+ case "create_subscription_room": {
173
+ if (!args.product_id) return { error: "product_id is required" };
174
+ if (!args.name) return { error: "name is required" };
175
+ const room = await client.createRoom({
176
+ name: args.name,
177
+ description: args.description,
178
+ rule: args.rule,
179
+ visibility: "private",
180
+ join_policy: "invite_only",
181
+ required_subscription_product_id: args.product_id,
182
+ max_members: args.max_members,
183
+ default_send: args.default_send,
184
+ default_invite: args.default_invite,
185
+ slow_mode_seconds: args.slow_mode_seconds,
186
+ });
187
+ return {
188
+ result: `Subscription room created: ${room.room_id} bound to ${args.product_id}`,
189
+ data: room,
190
+ };
191
+ }
192
+
193
+ case "bind_room_to_product": {
194
+ if (!args.room_id) return { error: "room_id is required" };
195
+ if (!args.product_id) return { error: "product_id is required" };
196
+ const room = await client.updateRoom(args.room_id, {
197
+ name: args.name,
198
+ description: args.description,
199
+ rule: args.rule,
200
+ visibility: "private",
201
+ join_policy: "invite_only",
202
+ required_subscription_product_id: args.product_id,
203
+ max_members: args.max_members,
204
+ default_send: args.default_send,
205
+ default_invite: args.default_invite,
206
+ slow_mode_seconds: args.slow_mode_seconds,
207
+ });
208
+ return {
209
+ result: `Room ${room.room_id} bound to subscription product ${args.product_id}`,
210
+ data: room,
211
+ };
212
+ }
213
+
145
214
  case "subscribe": {
146
215
  if (!args.product_id) return { error: "product_id is required" };
147
216
  const subscription = await client.subscribeToProduct(args.product_id);
package/src/types.ts CHANGED
@@ -90,7 +90,11 @@ export type RoomInfo = {
90
90
  rule?: string | null;
91
91
  visibility: "private" | "public";
92
92
  join_policy: "invite_only" | "open";
93
+ required_subscription_product_id?: string | null;
94
+ max_members?: number | null;
93
95
  default_send: boolean;
96
+ default_invite?: boolean;
97
+ slow_mode_seconds?: number | null;
94
98
  member_count: number;
95
99
  created_at: string;
96
100
  };
@@ -1,208 +0,0 @@
1
- /**
2
- * botcord_wallet — Manage the agent's coin wallet: balance, ledger, transfers, topups, withdrawals.
3
- */
4
- import {
5
- getSingleAccountModeError,
6
- resolveAccountConfig,
7
- isAccountConfigured,
8
- } from "../config.js";
9
- import { BotCordClient } from "../client.js";
10
- import { getConfig as getAppConfig } from "../runtime.js";
11
-
12
- function formatBalance(summary: any): string {
13
- const available = summary.available_balance_minor ?? "0";
14
- const locked = summary.locked_balance_minor ?? "0";
15
- const total = summary.total_balance_minor ?? "0";
16
- return [
17
- `Asset: ${summary.asset_code}`,
18
- `Available: ${available} minor units`,
19
- `Locked: ${locked} minor units`,
20
- `Total: ${total} minor units`,
21
- `Updated: ${summary.updated_at}`,
22
- ].join("\n");
23
- }
24
-
25
- function extractMemo(tx: any): string | null {
26
- if (!tx.metadata_json) return null;
27
- try {
28
- const meta = typeof tx.metadata_json === "string"
29
- ? JSON.parse(tx.metadata_json)
30
- : tx.metadata_json;
31
- return meta?.memo ?? null;
32
- } catch {
33
- return null;
34
- }
35
- }
36
-
37
- function formatTransaction(tx: any): string {
38
- const lines = [
39
- `Transaction: ${tx.tx_id}`,
40
- `Type: ${tx.type}`,
41
- `Status: ${tx.status}`,
42
- `Amount: ${tx.amount_minor} minor units`,
43
- `Fee: ${tx.fee_minor} minor units`,
44
- ];
45
- if (tx.from_agent_id) lines.push(`From: ${tx.from_agent_id}`);
46
- if (tx.to_agent_id) lines.push(`To: ${tx.to_agent_id}`);
47
- const memo = extractMemo(tx);
48
- if (memo) lines.push(`Memo: ${memo}`);
49
- lines.push(`Created: ${tx.created_at}`);
50
- if (tx.completed_at) lines.push(`Completed: ${tx.completed_at}`);
51
- return lines.join("\n");
52
- }
53
-
54
- function formatLedger(data: any): string {
55
- const entries = data.entries ?? [];
56
- if (entries.length === 0) return "No ledger entries found.";
57
-
58
- const lines = entries.map((e: any) => {
59
- const dir = e.direction === "credit" ? "+" : "-";
60
- return `${e.created_at} | ${dir}${e.amount_minor} | bal=${e.balance_after_minor} | tx=${e.tx_id}`;
61
- });
62
-
63
- if (data.has_more) {
64
- lines.push(`\n(More entries available — use cursor: "${data.next_cursor}")`);
65
- }
66
- return lines.join("\n");
67
- }
68
-
69
- export function createWalletTool() {
70
- return {
71
- name: "botcord_wallet",
72
- description:
73
- "Manage your BotCord coin wallet: check balance, view ledger, transfer coins, request topup/withdrawal, check transaction status.",
74
- parameters: {
75
- type: "object" as const,
76
- properties: {
77
- action: {
78
- type: "string" as const,
79
- enum: ["balance", "ledger", "transfer", "topup", "withdraw", "tx_status"],
80
- description: "Wallet action to perform",
81
- },
82
- to_agent_id: {
83
- type: "string" as const,
84
- description: "Recipient agent ID (ag_...) — for transfer",
85
- },
86
- amount_minor: {
87
- type: "string" as const,
88
- description: "Amount in minor units (string) — for transfer, topup, withdraw",
89
- },
90
- memo: {
91
- type: "string" as const,
92
- description: "Optional memo — for transfer",
93
- },
94
- idempotency_key: {
95
- type: "string" as const,
96
- description: "Optional idempotency key (UUID) — for transfer, withdraw",
97
- },
98
- channel: {
99
- type: "string" as const,
100
- description: "Topup channel (e.g. 'mock') — for topup",
101
- },
102
- destination_type: {
103
- type: "string" as const,
104
- description: "Withdrawal destination type — for withdraw",
105
- },
106
- destination: {
107
- type: "object" as const,
108
- description: "Withdrawal destination details — for withdraw",
109
- },
110
- tx_id: {
111
- type: "string" as const,
112
- description: "Transaction ID — for tx_status",
113
- },
114
- cursor: {
115
- type: "string" as const,
116
- description: "Pagination cursor — for ledger",
117
- },
118
- limit: {
119
- type: "number" as const,
120
- description: "Max entries to return — for ledger",
121
- },
122
- type: {
123
- type: "string" as const,
124
- description: "Filter by transaction type — for ledger",
125
- },
126
- },
127
- required: ["action"],
128
- },
129
- execute: async (toolCallId: any, args: any, signal?: any, onUpdate?: any) => {
130
- const cfg = getAppConfig();
131
- if (!cfg) return { error: "No configuration available" };
132
- const singleAccountError = getSingleAccountModeError(cfg);
133
- if (singleAccountError) return { error: singleAccountError };
134
-
135
- const acct = resolveAccountConfig(cfg);
136
- if (!isAccountConfigured(acct)) {
137
- return { error: "BotCord is not configured." };
138
- }
139
-
140
- const client = new BotCordClient(acct);
141
-
142
- try {
143
- switch (args.action) {
144
- case "balance": {
145
- const summary = await client.getWallet();
146
- return { result: formatBalance(summary), data: summary };
147
- }
148
-
149
- case "ledger": {
150
- const opts: { cursor?: string; limit?: number; type?: string } = {};
151
- if (args.cursor) opts.cursor = args.cursor;
152
- if (args.limit) opts.limit = args.limit;
153
- if (args.type) opts.type = args.type;
154
- const ledger = await client.getWalletLedger(opts);
155
- return { result: formatLedger(ledger), data: ledger };
156
- }
157
-
158
- case "transfer": {
159
- if (!args.to_agent_id) return { error: "to_agent_id is required" };
160
- if (!args.amount_minor) return { error: "amount_minor is required" };
161
- const tx = await client.createTransfer({
162
- to_agent_id: args.to_agent_id,
163
- amount_minor: args.amount_minor,
164
- memo: args.memo,
165
- idempotency_key: args.idempotency_key,
166
- });
167
- return { result: formatTransaction(tx), data: tx };
168
- }
169
-
170
- case "topup": {
171
- if (!args.amount_minor) return { error: "amount_minor is required" };
172
- const topup = await client.createTopup({
173
- amount_minor: args.amount_minor,
174
- channel: args.channel,
175
- idempotency_key: args.idempotency_key,
176
- });
177
- return { result: `Topup request created: ${JSON.stringify(topup)}`, data: topup };
178
- }
179
-
180
- case "withdraw": {
181
- if (!args.amount_minor) return { error: "amount_minor is required" };
182
- const withdrawal = await client.createWithdrawal({
183
- amount_minor: args.amount_minor,
184
- destination_type: args.destination_type,
185
- destination: args.destination,
186
- idempotency_key: args.idempotency_key,
187
- });
188
- return {
189
- result: `Withdrawal request created: ${JSON.stringify(withdrawal)}`,
190
- data: withdrawal,
191
- };
192
- }
193
-
194
- case "tx_status": {
195
- if (!args.tx_id) return { error: "tx_id is required" };
196
- const tx = await client.getWalletTransaction(args.tx_id);
197
- return { result: formatTransaction(tx), data: tx };
198
- }
199
-
200
- default:
201
- return { error: `Unknown action: ${args.action}` };
202
- }
203
- } catch (err: any) {
204
- return { error: `Wallet action failed: ${err.message}` };
205
- }
206
- },
207
- };
208
- }