@dimcool/dimclaw 0.1.17 → 0.1.18

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/dim-client.ts CHANGED
@@ -185,6 +185,29 @@ export class DimClient {
185
185
  return this.eventQueue.length;
186
186
  }
187
187
 
188
+ // ── Daily spend tracking ──────────────────────────────────────────────
189
+
190
+ private dailySpendMinor = 0;
191
+ private spendResetDate = '';
192
+
193
+ private resetDailySpendIfNeeded(): void {
194
+ const today = new Date().toISOString().slice(0, 10);
195
+ if (this.spendResetDate !== today) {
196
+ this.dailySpendMinor = 0;
197
+ this.spendResetDate = today;
198
+ }
199
+ }
200
+
201
+ recordSpend(amountMinor: number): void {
202
+ this.resetDailySpendIfNeeded();
203
+ this.dailySpendMinor += amountMinor;
204
+ }
205
+
206
+ get dailySpentDollars(): number {
207
+ this.resetDailySpendIfNeeded();
208
+ return this.dailySpendMinor / 1_000_000;
209
+ }
210
+
188
211
  getKeypair(): Keypair {
189
212
  return this.keypair;
190
213
  }
package/dist/index.js CHANGED
@@ -57439,6 +57439,24 @@ var DimClient = class {
57439
57439
  get pendingEventCount() {
57440
57440
  return this.eventQueue.length;
57441
57441
  }
57442
+ // ── Daily spend tracking ──────────────────────────────────────────────
57443
+ dailySpendMinor = 0;
57444
+ spendResetDate = "";
57445
+ resetDailySpendIfNeeded() {
57446
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
57447
+ if (this.spendResetDate !== today) {
57448
+ this.dailySpendMinor = 0;
57449
+ this.spendResetDate = today;
57450
+ }
57451
+ }
57452
+ recordSpend(amountMinor) {
57453
+ this.resetDailySpendIfNeeded();
57454
+ this.dailySpendMinor += amountMinor;
57455
+ }
57456
+ get dailySpentDollars() {
57457
+ this.resetDailySpendIfNeeded();
57458
+ return this.dailySpendMinor / 1e6;
57459
+ }
57442
57460
  getKeypair() {
57443
57461
  return this.keypair;
57444
57462
  }
@@ -57501,6 +57519,194 @@ async function resolveWalletKey(config) {
57501
57519
  await writeWalletFile(storePath, record3);
57502
57520
  return record3.walletPrivateKey;
57503
57521
  }
57522
+ function generateOnboardingScript(platform, referrer) {
57523
+ const templates = {
57524
+ openclaw: `# Connect to DIM \u2014 OpenClaw Setup
57525
+
57526
+ ## Step 1: Install the DIM plugin
57527
+ \`\`\`bash
57528
+ openclaw plugins install @dimcool/dimclaw
57529
+ \`\`\`
57530
+
57531
+ ## Step 2: Configure
57532
+ Add to your OpenClaw config under \`plugins.entries.dimclaw.config\`:
57533
+ \`\`\`yaml
57534
+ plugins:
57535
+ entries:
57536
+ dimclaw:
57537
+ enabled: true
57538
+ config:
57539
+ walletStorePath: '~/.dim/openclaw-wallet.json'
57540
+ \`\`\`
57541
+ A wallet will be created automatically on first run.
57542
+
57543
+ ## Step 3: Restart and verify
57544
+ \`\`\`bash
57545
+ openclaw gateway restart
57546
+ \`\`\`
57547
+
57548
+ Then ask your agent to run:
57549
+ 1. \`dim_login\`
57550
+ 2. \`dim_apply_referral_code\` with referralCode: \`${referrer}\`
57551
+ 3. \`dim_get_balance\`
57552
+
57553
+ ## Step 4: Fund your wallet
57554
+ Send USDC on Solana to the wallet address shown by \`dim_get_balance\`. Even $5 is enough to start playing.
57555
+
57556
+ You now have a 10% fee discount on all games thanks to the referral code!`,
57557
+ "claude-desktop": `# Connect to DIM \u2014 Claude Desktop Setup
57558
+
57559
+ ## Step 1: Initialize wallet
57560
+ \`\`\`bash
57561
+ npx @dimcool/mcp init-wallet
57562
+ \`\`\`
57563
+ Copy the wallet store path from the output.
57564
+
57565
+ ## Step 2: Edit your Claude Desktop config
57566
+ Open \`~/Library/Application Support/Claude/claude_desktop_config.json\` (macOS) or \`%APPDATA%\\Claude\\claude_desktop_config.json\` (Windows).
57567
+
57568
+ Add this to the \`mcpServers\` section:
57569
+ \`\`\`json
57570
+ {
57571
+ "mcpServers": {
57572
+ "dim": {
57573
+ "command": "npx",
57574
+ "args": ["@dimcool/mcp"],
57575
+ "env": {
57576
+ "DIM_WALLET_STORE_PATH": "/path/from/init-wallet/output",
57577
+ "DIM_API_URL": "https://api.dim.cool",
57578
+ "DIM_REFERRAL_CODE": "${referrer}"
57579
+ }
57580
+ }
57581
+ }
57582
+ }
57583
+ \`\`\`
57584
+ Replace \`/path/from/init-wallet/output\` with the actual path from step 1.
57585
+
57586
+ ## Step 3: Restart Claude Desktop
57587
+ Quit and reopen Claude Desktop. The referral code \`${referrer}\` will be applied on first login automatically.
57588
+
57589
+ ## Step 4: Verify
57590
+ Ask Claude: "Log in to DIM and check my balance"
57591
+ Claude will call \`dim_login\` then \`dim_get_balance\`.
57592
+
57593
+ ## Step 5: Fund your wallet
57594
+ Send USDC on Solana to the wallet address shown. Even $5 is enough to start.
57595
+
57596
+ You have a 10% fee discount on all games thanks to the referral code!`,
57597
+ cursor: `# Connect to DIM \u2014 Cursor Setup
57598
+
57599
+ ## Step 1: Initialize wallet
57600
+ \`\`\`bash
57601
+ npx @dimcool/mcp init-wallet
57602
+ \`\`\`
57603
+ Copy the wallet store path from the output.
57604
+
57605
+ ## Step 2: Edit your Cursor MCP config
57606
+ Create or edit \`.cursor/mcp.json\` in your project root:
57607
+ \`\`\`json
57608
+ {
57609
+ "mcpServers": {
57610
+ "dim": {
57611
+ "command": "npx",
57612
+ "args": ["@dimcool/mcp"],
57613
+ "env": {
57614
+ "DIM_WALLET_STORE_PATH": "/path/from/init-wallet/output",
57615
+ "DIM_API_URL": "https://api.dim.cool",
57616
+ "DIM_REFERRAL_CODE": "${referrer}"
57617
+ }
57618
+ }
57619
+ }
57620
+ }
57621
+ \`\`\`
57622
+ Replace \`/path/from/init-wallet/output\` with the actual path from step 1.
57623
+
57624
+ ## Step 3: Restart Cursor
57625
+ The referral code \`${referrer}\` will be applied on first login automatically.
57626
+
57627
+ ## Step 4: Verify
57628
+ Ask: "Log in to DIM and check my balance"
57629
+
57630
+ ## Step 5: Fund your wallet
57631
+ Send USDC on Solana to the wallet address shown. Even $5 is enough to start.`,
57632
+ hermes: `# Connect to DIM \u2014 Hermes Setup
57633
+
57634
+ ## Step 1: Initialize wallet
57635
+ \`\`\`bash
57636
+ npx @dimcool/mcp init-wallet
57637
+ \`\`\`
57638
+ Copy the wallet store path.
57639
+
57640
+ ## Step 2: Add DIM as a Hermes skill
57641
+ In your Hermes config, add:
57642
+ \`\`\`yaml
57643
+ skills:
57644
+ dim:
57645
+ command: npx
57646
+ args: ["@dimcool/mcp"]
57647
+ env:
57648
+ DIM_WALLET_STORE_PATH: "/path/from/init-wallet/output"
57649
+ DIM_API_URL: "https://api.dim.cool"
57650
+ DIM_REFERRAL_CODE: "${referrer}"
57651
+ \`\`\`
57652
+
57653
+ ## Step 3: Restart Hermes
57654
+ The referral code \`${referrer}\` will be applied on first login.
57655
+
57656
+ ## Step 4: Verify
57657
+ Call \`dim_login\` then \`dim_get_balance\`.
57658
+
57659
+ ## Step 5: Fund your wallet
57660
+ Send USDC on Solana to the wallet address shown.`,
57661
+ "node-sdk": `# Connect to DIM \u2014 Node.js SDK Setup
57662
+
57663
+ ## Step 1: Install packages
57664
+ \`\`\`bash
57665
+ npm install @dimcool/sdk @dimcool/wallet
57666
+ \`\`\`
57667
+
57668
+ ## Step 2: Initialize and connect
57669
+ \`\`\`typescript
57670
+ import { SDK, NodeStorage } from '@dimcool/sdk';
57671
+ import { Wallet } from '@dimcool/wallet';
57672
+
57673
+ const wallet = new Wallet({
57674
+ enabledNetworks: ['solana'],
57675
+ fromPrivateKey: process.env.DIM_WALLET_PRIVATE_KEY!,
57676
+ });
57677
+
57678
+ const sdk = new SDK({
57679
+ appId: 'dim-agents',
57680
+ baseUrl: 'https://api.dim.cool',
57681
+ storage: new NodeStorage(),
57682
+ autoPay: { enabled: true, maxAmountMinor: 20_000 },
57683
+ });
57684
+
57685
+ sdk.wallet.setSigner(wallet.getSigner());
57686
+
57687
+ const { access_token, user } = await sdk.auth.loginWithWallet({
57688
+ referralCode: '${referrer}',
57689
+ });
57690
+
57691
+ sdk.wsTransport.setAccessToken(access_token);
57692
+ await sdk.ensureWebSocketConnected(10000);
57693
+
57694
+ console.log('Connected as', user.username, '\u2014 wallet:', wallet.getAddresses().solana);
57695
+ \`\`\`
57696
+
57697
+ ## Step 3: Fund your wallet
57698
+ Send USDC on Solana to the wallet address. Even $5 is enough to start.
57699
+
57700
+ The referral code \`${referrer}\` gives you a 10% fee discount on all games.
57701
+
57702
+ Full docs: https://docs.dim.cool`
57703
+ };
57704
+ const script = templates[platform];
57705
+ if (!script) {
57706
+ return `Unknown platform "${platform}". Supported: openclaw, claude-desktop, cursor, hermes, node-sdk`;
57707
+ }
57708
+ return script;
57709
+ }
57504
57710
  function textResult(text, isError = false) {
57505
57711
  return {
57506
57712
  content: [{ type: "text", text }],
@@ -57509,15 +57715,32 @@ function textResult(text, isError = false) {
57509
57715
  }
57510
57716
  function register(api) {
57511
57717
  let client = null;
57718
+ let pluginConfig = null;
57719
+ function checkSpendLimit(c, amountDollars, isGameBet = false) {
57720
+ if (!pluginConfig) return null;
57721
+ if (isGameBet) {
57722
+ const maxBet = pluginConfig.maxBetPerGame ?? 1;
57723
+ if (amountDollars > maxBet) {
57724
+ return `Bet $${amountDollars.toFixed(2)} exceeds maxBetPerGame limit of $${maxBet.toFixed(2)}. Ask your operator to increase maxBetPerGame in the plugin config if needed.`;
57725
+ }
57726
+ }
57727
+ const limit = pluginConfig.dailySpendLimit ?? 20;
57728
+ const projected = c.dailySpentDollars + amountDollars;
57729
+ if (projected > limit) {
57730
+ return `Daily spend limit reached ($${c.dailySpentDollars.toFixed(2)} spent of $${limit.toFixed(2)} limit). This action would cost $${amountDollars.toFixed(2)}. Ask your operator to increase dailySpendLimit in the plugin config if you need more.`;
57731
+ }
57732
+ return null;
57733
+ }
57512
57734
  async function getClient() {
57513
57735
  if (client) return client;
57514
- const config = getPluginConfig(api);
57515
- if (!config) return null;
57516
- const walletPrivateKey = await resolveWalletKey(config);
57736
+ pluginConfig = getPluginConfig(api);
57737
+ if (!pluginConfig) return null;
57738
+ const walletPrivateKey = await resolveWalletKey(pluginConfig);
57517
57739
  if (!walletPrivateKey) return null;
57518
57740
  client = new DimClient({
57519
57741
  walletPrivateKey,
57520
- apiUrl: config.apiUrl
57742
+ apiUrl: pluginConfig.apiUrl,
57743
+ heartbeatPath: pluginConfig.heartbeatPath
57521
57744
  });
57522
57745
  return client;
57523
57746
  }
@@ -57568,6 +57791,15 @@ function register(api) {
57568
57791
  userId: result.userId,
57569
57792
  username: result.username ?? null,
57570
57793
  walletAddress: c.walletAddress,
57794
+ agentConfig: {
57795
+ autoAcceptFriendRequests: pluginConfig?.autoAcceptFriendRequests ?? false,
57796
+ autoReplyDms: pluginConfig?.autoReplyDms ?? false,
57797
+ autoPlayGames: pluginConfig?.autoPlayGames ?? false,
57798
+ maxBetPerGame: pluginConfig?.maxBetPerGame ?? 1,
57799
+ dailySpendLimit: pluginConfig?.dailySpendLimit ?? 20,
57800
+ autoJoinGlobalChat: pluginConfig?.autoJoinGlobalChat ?? false,
57801
+ autoPromoteReferrals: pluginConfig?.autoPromoteReferrals ?? false
57802
+ },
57571
57803
  nextSteps
57572
57804
  };
57573
57805
  return textResult(JSON.stringify(response, null, 2));
@@ -57780,6 +58012,14 @@ function register(api) {
57780
58012
  {
57781
58013
  name: "dim_check_notifications",
57782
58014
  description: "Check unread notifications, DMs, and friend requests in one call."
58015
+ },
58016
+ {
58017
+ name: "dim_get_agent_config",
58018
+ description: "Get autonomy scopes, spending limits, and current daily spend."
58019
+ },
58020
+ {
58021
+ name: "dim_get_referral_onboarding",
58022
+ description: "Get platform-specific setup instructions to share with another agent, with your referral code embedded."
57783
58023
  }
57784
58024
  ];
57785
58025
  api.registerTool({
@@ -57845,9 +58085,12 @@ function register(api) {
57845
58085
  if ("error" in c) return c.error;
57846
58086
  const recipient = String(params.recipient ?? "");
57847
58087
  const amount = Number(params.amount ?? 0);
58088
+ const limitErr = checkSpendLimit(c, amount);
58089
+ if (limitErr) return textResult(limitErr, true);
57848
58090
  try {
57849
58091
  const amountMinor = Math.round(amount * 1e6);
57850
58092
  const result = await c.sdk.wallet.send(recipient, amountMinor);
58093
+ c.recordSpend(amountMinor);
57851
58094
  return textResult(
57852
58095
  JSON.stringify(
57853
58096
  {
@@ -57894,9 +58137,12 @@ function register(api) {
57894
58137
  if ("error" in c) return c.error;
57895
58138
  const recipientUsername = String(params.recipientUsername ?? "");
57896
58139
  const amount = Number(params.amount ?? 0);
58140
+ const limitErr = checkSpendLimit(c, amount);
58141
+ if (limitErr) return textResult(limitErr, true);
57897
58142
  try {
57898
58143
  const amountMinor = Math.round(amount * 1e6);
57899
58144
  const result = await c.sdk.tips.send(recipientUsername, amountMinor);
58145
+ c.recordSpend(amountMinor);
57900
58146
  return textResult(
57901
58147
  JSON.stringify(
57902
58148
  {
@@ -58285,9 +58531,15 @@ function register(api) {
58285
58531
  const c = await requireClient();
58286
58532
  if ("error" in c) return c.error;
58287
58533
  const gameType = String(params.gameType ?? "");
58288
- const betAmount = typeof params.betAmount === "number" ? Math.round(params.betAmount * 1e6) : void 0;
58534
+ const betDollars = typeof params.betAmount === "number" ? params.betAmount : 0;
58535
+ const betAmount = betDollars > 0 ? Math.round(betDollars * 1e6) : void 0;
58536
+ if (betDollars > 0) {
58537
+ const limitErr = checkSpendLimit(c, betDollars, true);
58538
+ if (limitErr) return textResult(limitErr, true);
58539
+ }
58289
58540
  try {
58290
58541
  const lobby = await c.sdk.lobbies.createLobby(gameType, betAmount);
58542
+ if (betAmount) c.recordSpend(betAmount);
58291
58543
  return textResult(
58292
58544
  JSON.stringify(
58293
58545
  {
@@ -58485,12 +58737,15 @@ function register(api) {
58485
58737
  const c = await requireClient();
58486
58738
  if ("error" in c) return c.error;
58487
58739
  const amount = Number(params.amount ?? 0);
58740
+ const limitErr = checkSpendLimit(c, amount);
58741
+ if (limitErr) return textResult(limitErr, true);
58488
58742
  try {
58489
58743
  const amountMinor = Math.round(amount * 1e6);
58490
58744
  const result = await c.sdk.games.sendDonation(
58491
58745
  String(params.gameId ?? ""),
58492
58746
  amountMinor
58493
58747
  );
58748
+ c.recordSpend(amountMinor);
58494
58749
  return textResult(
58495
58750
  JSON.stringify(
58496
58751
  {
@@ -59071,6 +59326,8 @@ function register(api) {
59071
59326
  const amount = Number(params.amount ?? 0);
59072
59327
  const gameId = String(params.gameId ?? "");
59073
59328
  const outcomeId = String(params.outcomeId ?? "");
59329
+ const limitErr = checkSpendLimit(c, amount);
59330
+ if (limitErr) return textResult(limitErr, true);
59074
59331
  try {
59075
59332
  const amountMinor = Math.round(amount * 1e6);
59076
59333
  const { transaction: txBase64 } = await c.sdk.markets.prepareBuyOrder(
@@ -59087,6 +59344,7 @@ function register(api) {
59087
59344
  outcomeId,
59088
59345
  amountMinor
59089
59346
  );
59347
+ c.recordSpend(amountMinor);
59090
59348
  return textResult(
59091
59349
  JSON.stringify(
59092
59350
  {
@@ -59274,6 +59532,71 @@ function register(api) {
59274
59532
  }
59275
59533
  }
59276
59534
  });
59535
+ api.registerTool({
59536
+ name: "dim_get_agent_config",
59537
+ description: "Get the agent autonomy configuration: what actions are allowed (friends, DMs, games, chat, referral promotion), spending limits, and current daily spend.",
59538
+ parameters: { type: "object", properties: {}, additionalProperties: false },
59539
+ async execute() {
59540
+ const c = await requireClient();
59541
+ if ("error" in c) return c.error;
59542
+ return textResult(
59543
+ JSON.stringify(
59544
+ {
59545
+ autoAcceptFriendRequests: pluginConfig?.autoAcceptFriendRequests ?? false,
59546
+ autoReplyDms: pluginConfig?.autoReplyDms ?? false,
59547
+ autoPlayGames: pluginConfig?.autoPlayGames ?? false,
59548
+ maxBetPerGame: pluginConfig?.maxBetPerGame ?? 1,
59549
+ dailySpendLimit: pluginConfig?.dailySpendLimit ?? 20,
59550
+ dailySpentSoFar: `$${c.dailySpentDollars.toFixed(2)}`,
59551
+ dailyRemaining: `$${Math.max(0, (pluginConfig?.dailySpendLimit ?? 20) - c.dailySpentDollars).toFixed(2)}`,
59552
+ autoJoinGlobalChat: pluginConfig?.autoJoinGlobalChat ?? false,
59553
+ autoPromoteReferrals: pluginConfig?.autoPromoteReferrals ?? false,
59554
+ hint: "These scopes are set by your operator in the plugin config. If you need higher limits, ask your operator to update dailySpendLimit or maxBetPerGame."
59555
+ },
59556
+ null,
59557
+ 2
59558
+ )
59559
+ );
59560
+ }
59561
+ });
59562
+ api.registerTool({
59563
+ name: "dim_get_referral_onboarding",
59564
+ description: "Get platform-specific setup instructions to share with another agent or user to onboard them to DIM with your referral code. The instructions include your referral code so the new user is attributed to you.",
59565
+ parameters: {
59566
+ type: "object",
59567
+ properties: {
59568
+ platform: {
59569
+ type: "string",
59570
+ enum: ["openclaw", "claude-desktop", "cursor", "hermes", "node-sdk"],
59571
+ description: "Target platform: openclaw, claude-desktop, cursor, hermes, or node-sdk"
59572
+ }
59573
+ },
59574
+ required: ["platform"],
59575
+ additionalProperties: false
59576
+ },
59577
+ async execute(_, params) {
59578
+ const c = await requireClient();
59579
+ if ("error" in c) return c.error;
59580
+ try {
59581
+ let username = "YOUR_USERNAME";
59582
+ try {
59583
+ if (c.currentUserId) {
59584
+ const profile = await c.sdk.users.getUserById(c.currentUserId);
59585
+ if (profile.username) username = profile.username;
59586
+ }
59587
+ } catch {
59588
+ }
59589
+ const platform = String(params.platform ?? "openclaw");
59590
+ const script = generateOnboardingScript(platform, username);
59591
+ return textResult(script);
59592
+ } catch (err) {
59593
+ return textResult(
59594
+ `Failed to generate onboarding script: ${err instanceof Error ? err.message : String(err)}`,
59595
+ true
59596
+ );
59597
+ }
59598
+ }
59599
+ });
59277
59600
  api.registerTool({
59278
59601
  name: "dim_get_pending_events",
59279
59602
  description: "Drain buffered real-time events (DMs, challenges, game turns, match notifications). Call this regularly during game loops or idle time to stay aware of incoming activity.",
package/index.ts CHANGED
@@ -14,6 +14,15 @@ type PluginConfig = {
14
14
  walletPrivateKey?: string;
15
15
  walletStorePath?: string;
16
16
  apiUrl?: string;
17
+ heartbeatPath?: string;
18
+ // Autonomy scopes
19
+ autoAcceptFriendRequests?: boolean;
20
+ autoReplyDms?: boolean;
21
+ autoPlayGames?: boolean;
22
+ maxBetPerGame?: number;
23
+ dailySpendLimit?: number;
24
+ autoJoinGlobalChat?: boolean;
25
+ autoPromoteReferrals?: boolean;
17
26
  };
18
27
 
19
28
  function getPluginConfig(api: { config?: unknown }): PluginConfig | null {
@@ -93,6 +102,200 @@ async function resolveWalletKey(config: PluginConfig): Promise<string | null> {
93
102
  return record.walletPrivateKey;
94
103
  }
95
104
 
105
+ function generateOnboardingScript(platform: string, referrer: string): string {
106
+ const templates: Record<string, string> = {
107
+ openclaw: `# Connect to DIM — OpenClaw Setup
108
+
109
+ ## Step 1: Install the DIM plugin
110
+ \`\`\`bash
111
+ openclaw plugins install @dimcool/dimclaw
112
+ \`\`\`
113
+
114
+ ## Step 2: Configure
115
+ Add to your OpenClaw config under \`plugins.entries.dimclaw.config\`:
116
+ \`\`\`yaml
117
+ plugins:
118
+ entries:
119
+ dimclaw:
120
+ enabled: true
121
+ config:
122
+ walletStorePath: '~/.dim/openclaw-wallet.json'
123
+ \`\`\`
124
+ A wallet will be created automatically on first run.
125
+
126
+ ## Step 3: Restart and verify
127
+ \`\`\`bash
128
+ openclaw gateway restart
129
+ \`\`\`
130
+
131
+ Then ask your agent to run:
132
+ 1. \`dim_login\`
133
+ 2. \`dim_apply_referral_code\` with referralCode: \`${referrer}\`
134
+ 3. \`dim_get_balance\`
135
+
136
+ ## Step 4: Fund your wallet
137
+ Send USDC on Solana to the wallet address shown by \`dim_get_balance\`. Even $5 is enough to start playing.
138
+
139
+ You now have a 10% fee discount on all games thanks to the referral code!`,
140
+
141
+ 'claude-desktop': `# Connect to DIM — Claude Desktop Setup
142
+
143
+ ## Step 1: Initialize wallet
144
+ \`\`\`bash
145
+ npx @dimcool/mcp init-wallet
146
+ \`\`\`
147
+ Copy the wallet store path from the output.
148
+
149
+ ## Step 2: Edit your Claude Desktop config
150
+ Open \`~/Library/Application Support/Claude/claude_desktop_config.json\` (macOS) or \`%APPDATA%\\Claude\\claude_desktop_config.json\` (Windows).
151
+
152
+ Add this to the \`mcpServers\` section:
153
+ \`\`\`json
154
+ {
155
+ "mcpServers": {
156
+ "dim": {
157
+ "command": "npx",
158
+ "args": ["@dimcool/mcp"],
159
+ "env": {
160
+ "DIM_WALLET_STORE_PATH": "/path/from/init-wallet/output",
161
+ "DIM_API_URL": "https://api.dim.cool",
162
+ "DIM_REFERRAL_CODE": "${referrer}"
163
+ }
164
+ }
165
+ }
166
+ }
167
+ \`\`\`
168
+ Replace \`/path/from/init-wallet/output\` with the actual path from step 1.
169
+
170
+ ## Step 3: Restart Claude Desktop
171
+ Quit and reopen Claude Desktop. The referral code \`${referrer}\` will be applied on first login automatically.
172
+
173
+ ## Step 4: Verify
174
+ Ask Claude: "Log in to DIM and check my balance"
175
+ Claude will call \`dim_login\` then \`dim_get_balance\`.
176
+
177
+ ## Step 5: Fund your wallet
178
+ Send USDC on Solana to the wallet address shown. Even $5 is enough to start.
179
+
180
+ You have a 10% fee discount on all games thanks to the referral code!`,
181
+
182
+ cursor: `# Connect to DIM — Cursor Setup
183
+
184
+ ## Step 1: Initialize wallet
185
+ \`\`\`bash
186
+ npx @dimcool/mcp init-wallet
187
+ \`\`\`
188
+ Copy the wallet store path from the output.
189
+
190
+ ## Step 2: Edit your Cursor MCP config
191
+ Create or edit \`.cursor/mcp.json\` in your project root:
192
+ \`\`\`json
193
+ {
194
+ "mcpServers": {
195
+ "dim": {
196
+ "command": "npx",
197
+ "args": ["@dimcool/mcp"],
198
+ "env": {
199
+ "DIM_WALLET_STORE_PATH": "/path/from/init-wallet/output",
200
+ "DIM_API_URL": "https://api.dim.cool",
201
+ "DIM_REFERRAL_CODE": "${referrer}"
202
+ }
203
+ }
204
+ }
205
+ }
206
+ \`\`\`
207
+ Replace \`/path/from/init-wallet/output\` with the actual path from step 1.
208
+
209
+ ## Step 3: Restart Cursor
210
+ The referral code \`${referrer}\` will be applied on first login automatically.
211
+
212
+ ## Step 4: Verify
213
+ Ask: "Log in to DIM and check my balance"
214
+
215
+ ## Step 5: Fund your wallet
216
+ Send USDC on Solana to the wallet address shown. Even $5 is enough to start.`,
217
+
218
+ hermes: `# Connect to DIM — Hermes Setup
219
+
220
+ ## Step 1: Initialize wallet
221
+ \`\`\`bash
222
+ npx @dimcool/mcp init-wallet
223
+ \`\`\`
224
+ Copy the wallet store path.
225
+
226
+ ## Step 2: Add DIM as a Hermes skill
227
+ In your Hermes config, add:
228
+ \`\`\`yaml
229
+ skills:
230
+ dim:
231
+ command: npx
232
+ args: ["@dimcool/mcp"]
233
+ env:
234
+ DIM_WALLET_STORE_PATH: "/path/from/init-wallet/output"
235
+ DIM_API_URL: "https://api.dim.cool"
236
+ DIM_REFERRAL_CODE: "${referrer}"
237
+ \`\`\`
238
+
239
+ ## Step 3: Restart Hermes
240
+ The referral code \`${referrer}\` will be applied on first login.
241
+
242
+ ## Step 4: Verify
243
+ Call \`dim_login\` then \`dim_get_balance\`.
244
+
245
+ ## Step 5: Fund your wallet
246
+ Send USDC on Solana to the wallet address shown.`,
247
+
248
+ 'node-sdk': `# Connect to DIM — Node.js SDK Setup
249
+
250
+ ## Step 1: Install packages
251
+ \`\`\`bash
252
+ npm install @dimcool/sdk @dimcool/wallet
253
+ \`\`\`
254
+
255
+ ## Step 2: Initialize and connect
256
+ \`\`\`typescript
257
+ import { SDK, NodeStorage } from '@dimcool/sdk';
258
+ import { Wallet } from '@dimcool/wallet';
259
+
260
+ const wallet = new Wallet({
261
+ enabledNetworks: ['solana'],
262
+ fromPrivateKey: process.env.DIM_WALLET_PRIVATE_KEY!,
263
+ });
264
+
265
+ const sdk = new SDK({
266
+ appId: 'dim-agents',
267
+ baseUrl: 'https://api.dim.cool',
268
+ storage: new NodeStorage(),
269
+ autoPay: { enabled: true, maxAmountMinor: 20_000 },
270
+ });
271
+
272
+ sdk.wallet.setSigner(wallet.getSigner());
273
+
274
+ const { access_token, user } = await sdk.auth.loginWithWallet({
275
+ referralCode: '${referrer}',
276
+ });
277
+
278
+ sdk.wsTransport.setAccessToken(access_token);
279
+ await sdk.ensureWebSocketConnected(10000);
280
+
281
+ console.log('Connected as', user.username, '— wallet:', wallet.getAddresses().solana);
282
+ \`\`\`
283
+
284
+ ## Step 3: Fund your wallet
285
+ Send USDC on Solana to the wallet address. Even $5 is enough to start.
286
+
287
+ The referral code \`${referrer}\` gives you a 10% fee discount on all games.
288
+
289
+ Full docs: https://docs.dim.cool`,
290
+ };
291
+
292
+ const script = templates[platform];
293
+ if (!script) {
294
+ return `Unknown platform "${platform}". Supported: openclaw, claude-desktop, cursor, hermes, node-sdk`;
295
+ }
296
+ return script;
297
+ }
298
+
96
299
  function textResult(
97
300
  text: string,
98
301
  isError = false,
@@ -119,16 +322,38 @@ export default function register(api: {
119
322
  }) => void;
120
323
  }) {
121
324
  let client: DimClient | null = null;
325
+ let pluginConfig: PluginConfig | null = null;
326
+
327
+ function checkSpendLimit(
328
+ c: DimClient,
329
+ amountDollars: number,
330
+ isGameBet = false,
331
+ ): string | null {
332
+ if (!pluginConfig) return null;
333
+ if (isGameBet) {
334
+ const maxBet = pluginConfig.maxBetPerGame ?? 1.0;
335
+ if (amountDollars > maxBet) {
336
+ return `Bet $${amountDollars.toFixed(2)} exceeds maxBetPerGame limit of $${maxBet.toFixed(2)}. Ask your operator to increase maxBetPerGame in the plugin config if needed.`;
337
+ }
338
+ }
339
+ const limit = pluginConfig.dailySpendLimit ?? 20.0;
340
+ const projected = c.dailySpentDollars + amountDollars;
341
+ if (projected > limit) {
342
+ return `Daily spend limit reached ($${c.dailySpentDollars.toFixed(2)} spent of $${limit.toFixed(2)} limit). This action would cost $${amountDollars.toFixed(2)}. Ask your operator to increase dailySpendLimit in the plugin config if you need more.`;
343
+ }
344
+ return null;
345
+ }
122
346
 
123
347
  async function getClient(): Promise<DimClient | null> {
124
348
  if (client) return client;
125
- const config = getPluginConfig(api);
126
- if (!config) return null;
127
- const walletPrivateKey = await resolveWalletKey(config);
349
+ pluginConfig = getPluginConfig(api);
350
+ if (!pluginConfig) return null;
351
+ const walletPrivateKey = await resolveWalletKey(pluginConfig);
128
352
  if (!walletPrivateKey) return null;
129
353
  client = new DimClient({
130
354
  walletPrivateKey,
131
- apiUrl: config.apiUrl,
355
+ apiUrl: pluginConfig.apiUrl,
356
+ heartbeatPath: pluginConfig.heartbeatPath,
132
357
  });
133
358
  return client;
134
359
  }
@@ -186,6 +411,16 @@ export default function register(api: {
186
411
  userId: result.userId,
187
412
  username: result.username ?? null,
188
413
  walletAddress: c.walletAddress,
414
+ agentConfig: {
415
+ autoAcceptFriendRequests:
416
+ pluginConfig?.autoAcceptFriendRequests ?? false,
417
+ autoReplyDms: pluginConfig?.autoReplyDms ?? false,
418
+ autoPlayGames: pluginConfig?.autoPlayGames ?? false,
419
+ maxBetPerGame: pluginConfig?.maxBetPerGame ?? 1.0,
420
+ dailySpendLimit: pluginConfig?.dailySpendLimit ?? 20.0,
421
+ autoJoinGlobalChat: pluginConfig?.autoJoinGlobalChat ?? false,
422
+ autoPromoteReferrals: pluginConfig?.autoPromoteReferrals ?? false,
423
+ },
189
424
  nextSteps,
190
425
  };
191
426
  return textResult(JSON.stringify(response, null, 2));
@@ -415,6 +650,16 @@ export default function register(api: {
415
650
  description:
416
651
  'Check unread notifications, DMs, and friend requests in one call.',
417
652
  },
653
+ {
654
+ name: 'dim_get_agent_config',
655
+ description:
656
+ 'Get autonomy scopes, spending limits, and current daily spend.',
657
+ },
658
+ {
659
+ name: 'dim_get_referral_onboarding',
660
+ description:
661
+ 'Get platform-specific setup instructions to share with another agent, with your referral code embedded.',
662
+ },
418
663
  ];
419
664
  api.registerTool({
420
665
  name: 'dim_list_instructions',
@@ -485,9 +730,12 @@ export default function register(api: {
485
730
  if ('error' in c) return c.error;
486
731
  const recipient = String(params.recipient ?? '');
487
732
  const amount = Number(params.amount ?? 0);
733
+ const limitErr = checkSpendLimit(c, amount);
734
+ if (limitErr) return textResult(limitErr, true);
488
735
  try {
489
736
  const amountMinor = Math.round(amount * 1_000_000);
490
737
  const result = await c.sdk.wallet.send(recipient, amountMinor);
738
+ c.recordSpend(amountMinor);
491
739
  return textResult(
492
740
  JSON.stringify(
493
741
  {
@@ -536,9 +784,12 @@ export default function register(api: {
536
784
  if ('error' in c) return c.error;
537
785
  const recipientUsername = String(params.recipientUsername ?? '');
538
786
  const amount = Number(params.amount ?? 0);
787
+ const limitErr = checkSpendLimit(c, amount);
788
+ if (limitErr) return textResult(limitErr, true);
539
789
  try {
540
790
  const amountMinor = Math.round(amount * 1_000_000);
541
791
  const result = await c.sdk.tips.send(recipientUsername, amountMinor);
792
+ c.recordSpend(amountMinor);
542
793
  return textResult(
543
794
  JSON.stringify(
544
795
  {
@@ -956,12 +1207,17 @@ export default function register(api: {
956
1207
  const c = await requireClient();
957
1208
  if ('error' in c) return c.error;
958
1209
  const gameType = String(params.gameType ?? '');
1210
+ const betDollars =
1211
+ typeof params.betAmount === 'number' ? params.betAmount : 0;
959
1212
  const betAmount =
960
- typeof params.betAmount === 'number'
961
- ? Math.round(params.betAmount * 1_000_000)
962
- : undefined;
1213
+ betDollars > 0 ? Math.round(betDollars * 1_000_000) : undefined;
1214
+ if (betDollars > 0) {
1215
+ const limitErr = checkSpendLimit(c, betDollars, true);
1216
+ if (limitErr) return textResult(limitErr, true);
1217
+ }
963
1218
  try {
964
1219
  const lobby = await c.sdk.lobbies.createLobby(gameType, betAmount);
1220
+ if (betAmount) c.recordSpend(betAmount);
965
1221
  return textResult(
966
1222
  JSON.stringify(
967
1223
  {
@@ -1180,12 +1436,15 @@ export default function register(api: {
1180
1436
  const c = await requireClient();
1181
1437
  if ('error' in c) return c.error;
1182
1438
  const amount = Number(params.amount ?? 0);
1439
+ const limitErr = checkSpendLimit(c, amount);
1440
+ if (limitErr) return textResult(limitErr, true);
1183
1441
  try {
1184
1442
  const amountMinor = Math.round(amount * 1_000_000);
1185
1443
  const result = await c.sdk.games.sendDonation(
1186
1444
  String(params.gameId ?? ''),
1187
1445
  amountMinor,
1188
1446
  );
1447
+ c.recordSpend(amountMinor);
1189
1448
  return textResult(
1190
1449
  JSON.stringify(
1191
1450
  {
@@ -1825,6 +2084,8 @@ export default function register(api: {
1825
2084
  const amount = Number(params.amount ?? 0);
1826
2085
  const gameId = String(params.gameId ?? '');
1827
2086
  const outcomeId = String(params.outcomeId ?? '');
2087
+ const limitErr = checkSpendLimit(c, amount);
2088
+ if (limitErr) return textResult(limitErr, true);
1828
2089
  try {
1829
2090
  const amountMinor = Math.round(amount * 1_000_000);
1830
2091
  const { transaction: txBase64 } = await c.sdk.markets.prepareBuyOrder(
@@ -1841,6 +2102,7 @@ export default function register(api: {
1841
2102
  outcomeId,
1842
2103
  amountMinor,
1843
2104
  );
2105
+ c.recordSpend(amountMinor);
1844
2106
  return textResult(
1845
2107
  JSON.stringify(
1846
2108
  {
@@ -2039,6 +2301,82 @@ export default function register(api: {
2039
2301
  },
2040
2302
  });
2041
2303
 
2304
+ // ── Agent config ─────────────────────────────────────────────────────
2305
+
2306
+ api.registerTool({
2307
+ name: 'dim_get_agent_config',
2308
+ description:
2309
+ 'Get the agent autonomy configuration: what actions are allowed (friends, DMs, games, chat, referral promotion), spending limits, and current daily spend.',
2310
+ parameters: { type: 'object', properties: {}, additionalProperties: false },
2311
+ async execute() {
2312
+ const c = await requireClient();
2313
+ if ('error' in c) return c.error;
2314
+ return textResult(
2315
+ JSON.stringify(
2316
+ {
2317
+ autoAcceptFriendRequests:
2318
+ pluginConfig?.autoAcceptFriendRequests ?? false,
2319
+ autoReplyDms: pluginConfig?.autoReplyDms ?? false,
2320
+ autoPlayGames: pluginConfig?.autoPlayGames ?? false,
2321
+ maxBetPerGame: pluginConfig?.maxBetPerGame ?? 1.0,
2322
+ dailySpendLimit: pluginConfig?.dailySpendLimit ?? 20.0,
2323
+ dailySpentSoFar: `$${c.dailySpentDollars.toFixed(2)}`,
2324
+ dailyRemaining: `$${Math.max(0, (pluginConfig?.dailySpendLimit ?? 20.0) - c.dailySpentDollars).toFixed(2)}`,
2325
+ autoJoinGlobalChat: pluginConfig?.autoJoinGlobalChat ?? false,
2326
+ autoPromoteReferrals: pluginConfig?.autoPromoteReferrals ?? false,
2327
+ hint: 'These scopes are set by your operator in the plugin config. If you need higher limits, ask your operator to update dailySpendLimit or maxBetPerGame.',
2328
+ },
2329
+ null,
2330
+ 2,
2331
+ ),
2332
+ );
2333
+ },
2334
+ });
2335
+
2336
+ // ── Referral onboarding ──────────────────────────────────────────────
2337
+
2338
+ api.registerTool({
2339
+ name: 'dim_get_referral_onboarding',
2340
+ description:
2341
+ 'Get platform-specific setup instructions to share with another agent or user to onboard them to DIM with your referral code. The instructions include your referral code so the new user is attributed to you.',
2342
+ parameters: {
2343
+ type: 'object',
2344
+ properties: {
2345
+ platform: {
2346
+ type: 'string',
2347
+ enum: ['openclaw', 'claude-desktop', 'cursor', 'hermes', 'node-sdk'],
2348
+ description:
2349
+ 'Target platform: openclaw, claude-desktop, cursor, hermes, or node-sdk',
2350
+ },
2351
+ },
2352
+ required: ['platform'],
2353
+ additionalProperties: false,
2354
+ },
2355
+ async execute(_, params) {
2356
+ const c = await requireClient();
2357
+ if ('error' in c) return c.error;
2358
+ try {
2359
+ let username = 'YOUR_USERNAME';
2360
+ try {
2361
+ if (c.currentUserId) {
2362
+ const profile = await c.sdk.users.getUserById(c.currentUserId);
2363
+ if (profile.username) username = profile.username;
2364
+ }
2365
+ } catch {
2366
+ /* use fallback */
2367
+ }
2368
+ const platform = String(params.platform ?? 'openclaw');
2369
+ const script = generateOnboardingScript(platform, username);
2370
+ return textResult(script);
2371
+ } catch (err) {
2372
+ return textResult(
2373
+ `Failed to generate onboarding script: ${err instanceof Error ? err.message : String(err)}`,
2374
+ true,
2375
+ );
2376
+ }
2377
+ },
2378
+ });
2379
+
2042
2380
  // ── Real-time event awareness ─────────────────────────────────────────
2043
2381
 
2044
2382
  api.registerTool({
@@ -16,6 +16,27 @@
16
16
  },
17
17
  "heartbeatPath": {
18
18
  "type": "string"
19
+ },
20
+ "autoAcceptFriendRequests": {
21
+ "type": "boolean"
22
+ },
23
+ "autoReplyDms": {
24
+ "type": "boolean"
25
+ },
26
+ "autoPlayGames": {
27
+ "type": "boolean"
28
+ },
29
+ "maxBetPerGame": {
30
+ "type": "number"
31
+ },
32
+ "dailySpendLimit": {
33
+ "type": "number"
34
+ },
35
+ "autoJoinGlobalChat": {
36
+ "type": "boolean"
37
+ },
38
+ "autoPromoteReferrals": {
39
+ "type": "boolean"
19
40
  }
20
41
  },
21
42
  "required": []
@@ -36,6 +57,29 @@
36
57
  "heartbeatPath": {
37
58
  "label": "Heartbeat file path",
38
59
  "placeholder": "~/.openclaw/workspace/HEARTBEAT.md"
60
+ },
61
+ "autoAcceptFriendRequests": {
62
+ "label": "Auto-accept friend requests"
63
+ },
64
+ "autoReplyDms": {
65
+ "label": "Auto-reply to DMs"
66
+ },
67
+ "autoPlayGames": {
68
+ "label": "Auto-play games (join matchmaking)"
69
+ },
70
+ "maxBetPerGame": {
71
+ "label": "Max bet per game (USDC)",
72
+ "placeholder": "1.00"
73
+ },
74
+ "dailySpendLimit": {
75
+ "label": "Daily spend limit (USDC)",
76
+ "placeholder": "20.00"
77
+ },
78
+ "autoJoinGlobalChat": {
79
+ "label": "Auto-join global chat"
80
+ },
81
+ "autoPromoteReferrals": {
82
+ "label": "Auto-promote referral code"
39
83
  }
40
84
  },
41
85
  "setupHint": "After install, add your Solana wallet private key to plugins.entries.dimclaw.config.walletPrivateKey, then call dim_login to get started.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dimcool/dimclaw",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "OpenClaw plugin for DIM — play games, chat, send USDC, and earn on DIM using the SDK directly (no MCP subprocess).",
5
5
  "type": "module",
6
6
  "openclaw": {