@dimcool/dimclaw 0.1.17 → 0.1.19

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
  }
@@ -57563,11 +57786,24 @@ function register(api) {
57563
57786
  }
57564
57787
  } catch {
57565
57788
  }
57789
+ const booleanExists = (value2, defaultValue) => typeof value2 === "boolean" ? value2 : defaultValue;
57566
57790
  const response = {
57567
57791
  success: true,
57568
57792
  userId: result.userId,
57569
57793
  username: result.username ?? null,
57570
57794
  walletAddress: c.walletAddress,
57795
+ agentConfig: {
57796
+ autoAcceptFriendRequests: booleanExists(
57797
+ pluginConfig?.autoAcceptFriendRequests,
57798
+ true
57799
+ ),
57800
+ autoReplyDms: booleanExists(pluginConfig?.autoReplyDms, true),
57801
+ autoPlayGames: booleanExists(pluginConfig?.autoPlayGames, true),
57802
+ maxBetPerGame: pluginConfig?.maxBetPerGame ?? 1,
57803
+ dailySpendLimit: pluginConfig?.dailySpendLimit ?? 20,
57804
+ autoJoinGlobalChat: booleanExists(pluginConfig?.autoJoinGlobalChat, true),
57805
+ autoPromoteReferrals: booleanExists(pluginConfig?.autoPromoteReferrals, true)
57806
+ },
57571
57807
  nextSteps
57572
57808
  };
57573
57809
  return textResult(JSON.stringify(response, null, 2));
@@ -57780,6 +58016,14 @@ function register(api) {
57780
58016
  {
57781
58017
  name: "dim_check_notifications",
57782
58018
  description: "Check unread notifications, DMs, and friend requests in one call."
58019
+ },
58020
+ {
58021
+ name: "dim_get_agent_config",
58022
+ description: "Get autonomy scopes, spending limits, and current daily spend."
58023
+ },
58024
+ {
58025
+ name: "dim_get_referral_onboarding",
58026
+ description: "Get platform-specific setup instructions to share with another agent, with your referral code embedded."
57783
58027
  }
57784
58028
  ];
57785
58029
  api.registerTool({
@@ -57845,9 +58089,12 @@ function register(api) {
57845
58089
  if ("error" in c) return c.error;
57846
58090
  const recipient = String(params.recipient ?? "");
57847
58091
  const amount = Number(params.amount ?? 0);
58092
+ const limitErr = checkSpendLimit(c, amount);
58093
+ if (limitErr) return textResult(limitErr, true);
57848
58094
  try {
57849
58095
  const amountMinor = Math.round(amount * 1e6);
57850
58096
  const result = await c.sdk.wallet.send(recipient, amountMinor);
58097
+ c.recordSpend(amountMinor);
57851
58098
  return textResult(
57852
58099
  JSON.stringify(
57853
58100
  {
@@ -57894,9 +58141,12 @@ function register(api) {
57894
58141
  if ("error" in c) return c.error;
57895
58142
  const recipientUsername = String(params.recipientUsername ?? "");
57896
58143
  const amount = Number(params.amount ?? 0);
58144
+ const limitErr = checkSpendLimit(c, amount);
58145
+ if (limitErr) return textResult(limitErr, true);
57897
58146
  try {
57898
58147
  const amountMinor = Math.round(amount * 1e6);
57899
58148
  const result = await c.sdk.tips.send(recipientUsername, amountMinor);
58149
+ c.recordSpend(amountMinor);
57900
58150
  return textResult(
57901
58151
  JSON.stringify(
57902
58152
  {
@@ -58285,9 +58535,15 @@ function register(api) {
58285
58535
  const c = await requireClient();
58286
58536
  if ("error" in c) return c.error;
58287
58537
  const gameType = String(params.gameType ?? "");
58288
- const betAmount = typeof params.betAmount === "number" ? Math.round(params.betAmount * 1e6) : void 0;
58538
+ const betDollars = typeof params.betAmount === "number" ? params.betAmount : 0;
58539
+ const betAmount = betDollars > 0 ? Math.round(betDollars * 1e6) : void 0;
58540
+ if (betDollars > 0) {
58541
+ const limitErr = checkSpendLimit(c, betDollars, true);
58542
+ if (limitErr) return textResult(limitErr, true);
58543
+ }
58289
58544
  try {
58290
58545
  const lobby = await c.sdk.lobbies.createLobby(gameType, betAmount);
58546
+ if (betAmount) c.recordSpend(betAmount);
58291
58547
  return textResult(
58292
58548
  JSON.stringify(
58293
58549
  {
@@ -58485,12 +58741,15 @@ function register(api) {
58485
58741
  const c = await requireClient();
58486
58742
  if ("error" in c) return c.error;
58487
58743
  const amount = Number(params.amount ?? 0);
58744
+ const limitErr = checkSpendLimit(c, amount);
58745
+ if (limitErr) return textResult(limitErr, true);
58488
58746
  try {
58489
58747
  const amountMinor = Math.round(amount * 1e6);
58490
58748
  const result = await c.sdk.games.sendDonation(
58491
58749
  String(params.gameId ?? ""),
58492
58750
  amountMinor
58493
58751
  );
58752
+ c.recordSpend(amountMinor);
58494
58753
  return textResult(
58495
58754
  JSON.stringify(
58496
58755
  {
@@ -59071,6 +59330,8 @@ function register(api) {
59071
59330
  const amount = Number(params.amount ?? 0);
59072
59331
  const gameId = String(params.gameId ?? "");
59073
59332
  const outcomeId = String(params.outcomeId ?? "");
59333
+ const limitErr = checkSpendLimit(c, amount);
59334
+ if (limitErr) return textResult(limitErr, true);
59074
59335
  try {
59075
59336
  const amountMinor = Math.round(amount * 1e6);
59076
59337
  const { transaction: txBase64 } = await c.sdk.markets.prepareBuyOrder(
@@ -59087,6 +59348,7 @@ function register(api) {
59087
59348
  outcomeId,
59088
59349
  amountMinor
59089
59350
  );
59351
+ c.recordSpend(amountMinor);
59090
59352
  return textResult(
59091
59353
  JSON.stringify(
59092
59354
  {
@@ -59274,6 +59536,71 @@ function register(api) {
59274
59536
  }
59275
59537
  }
59276
59538
  });
59539
+ api.registerTool({
59540
+ name: "dim_get_agent_config",
59541
+ description: "Get the agent autonomy configuration: what actions are allowed (friends, DMs, games, chat, referral promotion), spending limits, and current daily spend.",
59542
+ parameters: { type: "object", properties: {}, additionalProperties: false },
59543
+ async execute() {
59544
+ const c = await requireClient();
59545
+ if ("error" in c) return c.error;
59546
+ return textResult(
59547
+ JSON.stringify(
59548
+ {
59549
+ autoAcceptFriendRequests: pluginConfig?.autoAcceptFriendRequests ?? false,
59550
+ autoReplyDms: pluginConfig?.autoReplyDms ?? false,
59551
+ autoPlayGames: pluginConfig?.autoPlayGames ?? false,
59552
+ maxBetPerGame: pluginConfig?.maxBetPerGame ?? 1,
59553
+ dailySpendLimit: pluginConfig?.dailySpendLimit ?? 20,
59554
+ dailySpentSoFar: `$${c.dailySpentDollars.toFixed(2)}`,
59555
+ dailyRemaining: `$${Math.max(0, (pluginConfig?.dailySpendLimit ?? 20) - c.dailySpentDollars).toFixed(2)}`,
59556
+ autoJoinGlobalChat: pluginConfig?.autoJoinGlobalChat ?? false,
59557
+ autoPromoteReferrals: pluginConfig?.autoPromoteReferrals ?? false,
59558
+ 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."
59559
+ },
59560
+ null,
59561
+ 2
59562
+ )
59563
+ );
59564
+ }
59565
+ });
59566
+ api.registerTool({
59567
+ name: "dim_get_referral_onboarding",
59568
+ 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.",
59569
+ parameters: {
59570
+ type: "object",
59571
+ properties: {
59572
+ platform: {
59573
+ type: "string",
59574
+ enum: ["openclaw", "claude-desktop", "cursor", "hermes", "node-sdk"],
59575
+ description: "Target platform: openclaw, claude-desktop, cursor, hermes, or node-sdk"
59576
+ }
59577
+ },
59578
+ required: ["platform"],
59579
+ additionalProperties: false
59580
+ },
59581
+ async execute(_, params) {
59582
+ const c = await requireClient();
59583
+ if ("error" in c) return c.error;
59584
+ try {
59585
+ let username = "YOUR_USERNAME";
59586
+ try {
59587
+ if (c.currentUserId) {
59588
+ const profile = await c.sdk.users.getUserById(c.currentUserId);
59589
+ if (profile.username) username = profile.username;
59590
+ }
59591
+ } catch {
59592
+ }
59593
+ const platform = String(params.platform ?? "openclaw");
59594
+ const script = generateOnboardingScript(platform, username);
59595
+ return textResult(script);
59596
+ } catch (err) {
59597
+ return textResult(
59598
+ `Failed to generate onboarding script: ${err instanceof Error ? err.message : String(err)}`,
59599
+ true
59600
+ );
59601
+ }
59602
+ }
59603
+ });
59277
59604
  api.registerTool({
59278
59605
  name: "dim_get_pending_events",
59279
59606
  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
  }
@@ -181,11 +406,23 @@ export default function register(api: {
181
406
  } catch {
182
407
  /* non-critical */
183
408
  }
409
+
410
+ const booleanExists = (value: boolean | undefined, defaultValue: boolean) => typeof value === 'boolean' ? value : defaultValue;
184
411
  const response: Record<string, unknown> = {
185
412
  success: true,
186
413
  userId: result.userId,
187
414
  username: result.username ?? null,
188
415
  walletAddress: c.walletAddress,
416
+ agentConfig: {
417
+ autoAcceptFriendRequests: booleanExists(
418
+ pluginConfig?.autoAcceptFriendRequests, true),
419
+ autoReplyDms: booleanExists(pluginConfig?.autoReplyDms, true),
420
+ autoPlayGames: booleanExists(pluginConfig?.autoPlayGames, true),
421
+ maxBetPerGame: pluginConfig?.maxBetPerGame ?? 1.0,
422
+ dailySpendLimit: pluginConfig?.dailySpendLimit ?? 20.0,
423
+ autoJoinGlobalChat: booleanExists(pluginConfig?.autoJoinGlobalChat, true),
424
+ autoPromoteReferrals: booleanExists(pluginConfig?.autoPromoteReferrals, true),
425
+ },
189
426
  nextSteps,
190
427
  };
191
428
  return textResult(JSON.stringify(response, null, 2));
@@ -415,6 +652,16 @@ export default function register(api: {
415
652
  description:
416
653
  'Check unread notifications, DMs, and friend requests in one call.',
417
654
  },
655
+ {
656
+ name: 'dim_get_agent_config',
657
+ description:
658
+ 'Get autonomy scopes, spending limits, and current daily spend.',
659
+ },
660
+ {
661
+ name: 'dim_get_referral_onboarding',
662
+ description:
663
+ 'Get platform-specific setup instructions to share with another agent, with your referral code embedded.',
664
+ },
418
665
  ];
419
666
  api.registerTool({
420
667
  name: 'dim_list_instructions',
@@ -485,9 +732,12 @@ export default function register(api: {
485
732
  if ('error' in c) return c.error;
486
733
  const recipient = String(params.recipient ?? '');
487
734
  const amount = Number(params.amount ?? 0);
735
+ const limitErr = checkSpendLimit(c, amount);
736
+ if (limitErr) return textResult(limitErr, true);
488
737
  try {
489
738
  const amountMinor = Math.round(amount * 1_000_000);
490
739
  const result = await c.sdk.wallet.send(recipient, amountMinor);
740
+ c.recordSpend(amountMinor);
491
741
  return textResult(
492
742
  JSON.stringify(
493
743
  {
@@ -536,9 +786,12 @@ export default function register(api: {
536
786
  if ('error' in c) return c.error;
537
787
  const recipientUsername = String(params.recipientUsername ?? '');
538
788
  const amount = Number(params.amount ?? 0);
789
+ const limitErr = checkSpendLimit(c, amount);
790
+ if (limitErr) return textResult(limitErr, true);
539
791
  try {
540
792
  const amountMinor = Math.round(amount * 1_000_000);
541
793
  const result = await c.sdk.tips.send(recipientUsername, amountMinor);
794
+ c.recordSpend(amountMinor);
542
795
  return textResult(
543
796
  JSON.stringify(
544
797
  {
@@ -956,12 +1209,17 @@ export default function register(api: {
956
1209
  const c = await requireClient();
957
1210
  if ('error' in c) return c.error;
958
1211
  const gameType = String(params.gameType ?? '');
1212
+ const betDollars =
1213
+ typeof params.betAmount === 'number' ? params.betAmount : 0;
959
1214
  const betAmount =
960
- typeof params.betAmount === 'number'
961
- ? Math.round(params.betAmount * 1_000_000)
962
- : undefined;
1215
+ betDollars > 0 ? Math.round(betDollars * 1_000_000) : undefined;
1216
+ if (betDollars > 0) {
1217
+ const limitErr = checkSpendLimit(c, betDollars, true);
1218
+ if (limitErr) return textResult(limitErr, true);
1219
+ }
963
1220
  try {
964
1221
  const lobby = await c.sdk.lobbies.createLobby(gameType, betAmount);
1222
+ if (betAmount) c.recordSpend(betAmount);
965
1223
  return textResult(
966
1224
  JSON.stringify(
967
1225
  {
@@ -1180,12 +1438,15 @@ export default function register(api: {
1180
1438
  const c = await requireClient();
1181
1439
  if ('error' in c) return c.error;
1182
1440
  const amount = Number(params.amount ?? 0);
1441
+ const limitErr = checkSpendLimit(c, amount);
1442
+ if (limitErr) return textResult(limitErr, true);
1183
1443
  try {
1184
1444
  const amountMinor = Math.round(amount * 1_000_000);
1185
1445
  const result = await c.sdk.games.sendDonation(
1186
1446
  String(params.gameId ?? ''),
1187
1447
  amountMinor,
1188
1448
  );
1449
+ c.recordSpend(amountMinor);
1189
1450
  return textResult(
1190
1451
  JSON.stringify(
1191
1452
  {
@@ -1825,6 +2086,8 @@ export default function register(api: {
1825
2086
  const amount = Number(params.amount ?? 0);
1826
2087
  const gameId = String(params.gameId ?? '');
1827
2088
  const outcomeId = String(params.outcomeId ?? '');
2089
+ const limitErr = checkSpendLimit(c, amount);
2090
+ if (limitErr) return textResult(limitErr, true);
1828
2091
  try {
1829
2092
  const amountMinor = Math.round(amount * 1_000_000);
1830
2093
  const { transaction: txBase64 } = await c.sdk.markets.prepareBuyOrder(
@@ -1841,6 +2104,7 @@ export default function register(api: {
1841
2104
  outcomeId,
1842
2105
  amountMinor,
1843
2106
  );
2107
+ c.recordSpend(amountMinor);
1844
2108
  return textResult(
1845
2109
  JSON.stringify(
1846
2110
  {
@@ -2039,6 +2303,82 @@ export default function register(api: {
2039
2303
  },
2040
2304
  });
2041
2305
 
2306
+ // ── Agent config ─────────────────────────────────────────────────────
2307
+
2308
+ api.registerTool({
2309
+ name: 'dim_get_agent_config',
2310
+ description:
2311
+ 'Get the agent autonomy configuration: what actions are allowed (friends, DMs, games, chat, referral promotion), spending limits, and current daily spend.',
2312
+ parameters: { type: 'object', properties: {}, additionalProperties: false },
2313
+ async execute() {
2314
+ const c = await requireClient();
2315
+ if ('error' in c) return c.error;
2316
+ return textResult(
2317
+ JSON.stringify(
2318
+ {
2319
+ autoAcceptFriendRequests:
2320
+ pluginConfig?.autoAcceptFriendRequests ?? false,
2321
+ autoReplyDms: pluginConfig?.autoReplyDms ?? false,
2322
+ autoPlayGames: pluginConfig?.autoPlayGames ?? false,
2323
+ maxBetPerGame: pluginConfig?.maxBetPerGame ?? 1.0,
2324
+ dailySpendLimit: pluginConfig?.dailySpendLimit ?? 20.0,
2325
+ dailySpentSoFar: `$${c.dailySpentDollars.toFixed(2)}`,
2326
+ dailyRemaining: `$${Math.max(0, (pluginConfig?.dailySpendLimit ?? 20.0) - c.dailySpentDollars).toFixed(2)}`,
2327
+ autoJoinGlobalChat: pluginConfig?.autoJoinGlobalChat ?? false,
2328
+ autoPromoteReferrals: pluginConfig?.autoPromoteReferrals ?? false,
2329
+ 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.',
2330
+ },
2331
+ null,
2332
+ 2,
2333
+ ),
2334
+ );
2335
+ },
2336
+ });
2337
+
2338
+ // ── Referral onboarding ──────────────────────────────────────────────
2339
+
2340
+ api.registerTool({
2341
+ name: 'dim_get_referral_onboarding',
2342
+ description:
2343
+ '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.',
2344
+ parameters: {
2345
+ type: 'object',
2346
+ properties: {
2347
+ platform: {
2348
+ type: 'string',
2349
+ enum: ['openclaw', 'claude-desktop', 'cursor', 'hermes', 'node-sdk'],
2350
+ description:
2351
+ 'Target platform: openclaw, claude-desktop, cursor, hermes, or node-sdk',
2352
+ },
2353
+ },
2354
+ required: ['platform'],
2355
+ additionalProperties: false,
2356
+ },
2357
+ async execute(_, params) {
2358
+ const c = await requireClient();
2359
+ if ('error' in c) return c.error;
2360
+ try {
2361
+ let username = 'YOUR_USERNAME';
2362
+ try {
2363
+ if (c.currentUserId) {
2364
+ const profile = await c.sdk.users.getUserById(c.currentUserId);
2365
+ if (profile.username) username = profile.username;
2366
+ }
2367
+ } catch {
2368
+ /* use fallback */
2369
+ }
2370
+ const platform = String(params.platform ?? 'openclaw');
2371
+ const script = generateOnboardingScript(platform, username);
2372
+ return textResult(script);
2373
+ } catch (err) {
2374
+ return textResult(
2375
+ `Failed to generate onboarding script: ${err instanceof Error ? err.message : String(err)}`,
2376
+ true,
2377
+ );
2378
+ }
2379
+ },
2380
+ });
2381
+
2042
2382
  // ── Real-time event awareness ─────────────────────────────────────────
2043
2383
 
2044
2384
  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.19",
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": {