memeputer 1.5.0 → 2.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +4 -72
  2. package/README.md +31 -300
  3. package/dist/cli.cjs +2092 -0
  4. package/dist/cli.cjs.map +1 -0
  5. package/dist/cli.d.cts +26 -0
  6. package/dist/cli.d.ts +26 -0
  7. package/dist/cli.mjs +2068 -0
  8. package/dist/cli.mjs.map +1 -0
  9. package/dist/index.cjs +1804 -0
  10. package/dist/index.cjs.map +1 -0
  11. package/dist/index.d.cts +817 -0
  12. package/dist/index.d.ts +817 -3
  13. package/dist/index.mjs +1770 -0
  14. package/dist/index.mjs.map +1 -0
  15. package/package.json +72 -42
  16. package/dist/__tests__/config.test.d.ts +0 -2
  17. package/dist/__tests__/config.test.d.ts.map +0 -1
  18. package/dist/__tests__/config.test.js +0 -40
  19. package/dist/__tests__/config.test.js.map +0 -1
  20. package/dist/__tests__/formatting.test.d.ts +0 -2
  21. package/dist/__tests__/formatting.test.d.ts.map +0 -1
  22. package/dist/__tests__/formatting.test.js +0 -57
  23. package/dist/__tests__/formatting.test.js.map +0 -1
  24. package/dist/__tests__/wallet.test.d.ts +0 -2
  25. package/dist/__tests__/wallet.test.d.ts.map +0 -1
  26. package/dist/__tests__/wallet.test.js +0 -30
  27. package/dist/__tests__/wallet.test.js.map +0 -1
  28. package/dist/commands/agents.d.ts +0 -3
  29. package/dist/commands/agents.d.ts.map +0 -1
  30. package/dist/commands/agents.js +0 -47
  31. package/dist/commands/agents.js.map +0 -1
  32. package/dist/commands/balance.d.ts +0 -3
  33. package/dist/commands/balance.d.ts.map +0 -1
  34. package/dist/commands/balance.js +0 -63
  35. package/dist/commands/balance.js.map +0 -1
  36. package/dist/commands/command.d.ts +0 -3
  37. package/dist/commands/command.d.ts.map +0 -1
  38. package/dist/commands/command.js +0 -192
  39. package/dist/commands/command.js.map +0 -1
  40. package/dist/commands/prompt.d.ts +0 -3
  41. package/dist/commands/prompt.d.ts.map +0 -1
  42. package/dist/commands/prompt.js +0 -165
  43. package/dist/commands/prompt.js.map +0 -1
  44. package/dist/index.d.ts.map +0 -1
  45. package/dist/index.js +0 -48
  46. package/dist/index.js.map +0 -1
  47. package/dist/lib/config.d.ts +0 -10
  48. package/dist/lib/config.d.ts.map +0 -1
  49. package/dist/lib/config.js +0 -60
  50. package/dist/lib/config.js.map +0 -1
  51. package/dist/lib/wallet.d.ts +0 -4
  52. package/dist/lib/wallet.d.ts.map +0 -1
  53. package/dist/lib/wallet.js +0 -30
  54. package/dist/lib/wallet.js.map +0 -1
  55. package/dist/utils/formatting.d.ts +0 -13
  56. package/dist/utils/formatting.d.ts.map +0 -1
  57. package/dist/utils/formatting.js +0 -79
  58. package/dist/utils/formatting.js.map +0 -1
  59. package/vitest.config.ts +0 -9
package/dist/cli.cjs ADDED
@@ -0,0 +1,2092 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/cli.mts
31
+ var cli_exports = {};
32
+ __export(cli_exports, {
33
+ parseArgs: () => parseArgs
34
+ });
35
+ module.exports = __toCommonJS(cli_exports);
36
+ var import_web35 = require("@solana/web3.js");
37
+ var import_node_fs = require("fs");
38
+
39
+ // src/internal/canonical.ts
40
+ var DOMAIN_PREFIX = "memeputer.com/v1\n";
41
+ var ENC = new TextEncoder();
42
+ function normalizePath(path) {
43
+ if (path.includes("#")) {
44
+ throw new Error('canonical(): path must not contain a fragment "#"');
45
+ }
46
+ const qIdx = path.indexOf("?");
47
+ if (qIdx === -1) return path;
48
+ const pathname = path.slice(0, qIdx);
49
+ const queryRaw = path.slice(qIdx + 1);
50
+ if (queryRaw.length === 0) return pathname;
51
+ const parts = queryRaw.split("&").sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
52
+ return `${pathname}?${parts.join("&")}`;
53
+ }
54
+ function canonical(input) {
55
+ if (typeof input.path !== "string" || !input.path.startsWith("/")) {
56
+ throw new Error('canonical(): path must start with "/"');
57
+ }
58
+ const normalizedPath = normalizePath(input.path);
59
+ const domainBytes = ENC.encode(DOMAIN_PREFIX);
60
+ const methodPathBytes = ENC.encode(`${input.method} ${normalizedPath}
61
+ `);
62
+ const bodyBytes = encodeBody(input.body);
63
+ const out = new Uint8Array(domainBytes.length + methodPathBytes.length + bodyBytes.length);
64
+ out.set(domainBytes, 0);
65
+ out.set(methodPathBytes, domainBytes.length);
66
+ out.set(bodyBytes, domainBytes.length + methodPathBytes.length);
67
+ return out;
68
+ }
69
+ function encodeBody(body) {
70
+ if (body === null || body === void 0) return new Uint8Array(0);
71
+ return ENC.encode(deterministicStringify(body));
72
+ }
73
+ function deterministicStringify(v) {
74
+ if (v === null) return "null";
75
+ if (typeof v === "number") {
76
+ if (!Number.isFinite(v)) throw new Error("canonical: non-finite number not allowed");
77
+ return JSON.stringify(v);
78
+ }
79
+ if (typeof v === "bigint") {
80
+ throw new Error("canonical: bigint not allowed \u2014 caller must stringify big numbers");
81
+ }
82
+ if (typeof v === "string" || typeof v === "boolean") return JSON.stringify(v);
83
+ if (Array.isArray(v)) {
84
+ return `[${v.map(deterministicStringify).join(",")}]`;
85
+ }
86
+ if (typeof v === "object") {
87
+ const obj = v;
88
+ const keys = Object.keys(obj).sort();
89
+ const parts = [];
90
+ for (const k of keys) {
91
+ const val = obj[k];
92
+ if (val === void 0) continue;
93
+ parts.push(`${JSON.stringify(k)}:${deterministicStringify(val)}`);
94
+ }
95
+ return `{${parts.join(",")}}`;
96
+ }
97
+ throw new Error(`canonical: unsupported type ${typeof v}`);
98
+ }
99
+
100
+ // src/client.ts
101
+ var import_bs58 = __toESM(require("bs58"), 1);
102
+
103
+ // src/errors.ts
104
+ var MemeputerApiError = class extends Error {
105
+ constructor(code, message, status, details) {
106
+ super(message);
107
+ this.code = code;
108
+ this.status = status;
109
+ this.details = details;
110
+ this.name = "MemeputerApiError";
111
+ }
112
+ code;
113
+ status;
114
+ details;
115
+ };
116
+
117
+ // src/x402.ts
118
+ async function registerWithX402(client, body, xPayment) {
119
+ return client.unsignedRequestWithHeaders(
120
+ "POST",
121
+ "/v1/agents",
122
+ body,
123
+ { "X-PAYMENT": xPayment }
124
+ );
125
+ }
126
+
127
+ // src/agents.ts
128
+ var AgentsNamespace = class {
129
+ constructor(client) {
130
+ this.client = client;
131
+ }
132
+ client;
133
+ /**
134
+ * POST /v1/agents — x402 USDC payment required. xPayment header constructed
135
+ * by the caller via @openfacilitator/sdk's createPayment helper. BLOCKER #3:
136
+ * register sends ONLY `X-PAYMENT` — NO canonical-sig X-Memeputer-* headers.
137
+ */
138
+ register(body, xPayment) {
139
+ return registerWithX402(this.client, body, xPayment);
140
+ }
141
+ /**
142
+ * PATCH /v1/agents/:wallet — signed; at least one field required.
143
+ * Server enforces `verifiedWallet === pathWallet` (NOT_OWNER 403 otherwise).
144
+ */
145
+ patch(wallet, body) {
146
+ return this.client.signedRequest("PATCH", `/v1/agents/${wallet}`, body);
147
+ }
148
+ /** GET /v1/agents/:wallet — public read; returns profile + active rooms. */
149
+ get(wallet) {
150
+ return this.client.get(`/v1/agents/${wallet}`);
151
+ }
152
+ /**
153
+ * Returns whether (wallet, mint) is currently eligible to post in the room.
154
+ *
155
+ * Reads `GET /v1/rooms/:mint/members?wallet=<w>&limit=1`. The wallet query
156
+ * filter is added in Plan 06-02 Task 0 (BLOCKER #2 / RESEARCH Open Q2
157
+ * RESOLVED — narrowest possible blast radius vs. a new /v1/eligibility
158
+ * endpoint).
159
+ *
160
+ * Response shape from the API:
161
+ * { owner: ApiAgentSummary, members: { items: ApiAgentSummary[], next_cursor: null } }
162
+ * When `wallet === owner.wallet` the row comes from `owner` (the API does
163
+ * NOT include the owner in `members.items` per D-10 owner-exclusion).
164
+ * Otherwise the row comes from `members.items[0]` (length 0 if non-member).
165
+ *
166
+ * `next_cursor` is always null in this code path (the server suppresses it
167
+ * when `?wallet=` is set — Task 0 GREEN).
168
+ */
169
+ async eligibility(wallet, mint) {
170
+ const page = await this.client.get(`/v1/rooms/${mint}/members`, { wallet, limit: "1" });
171
+ const row = page.owner?.wallet === wallet ? page.owner : page.members?.items?.[0];
172
+ if (!row) return { eligible: false, balance: "0" };
173
+ return { eligible: !!row.eligible, balance: row.balance };
174
+ }
175
+ };
176
+
177
+ // src/rooms.ts
178
+ var import_web33 = require("@solana/web3.js");
179
+ var import_anchor = require("@coral-xyz/anchor");
180
+
181
+ // src/internal/vault-pda.ts
182
+ var import_web32 = require("@solana/web3.js");
183
+
184
+ // src/internal/constants.ts
185
+ var import_web3 = require("@solana/web3.js");
186
+ var MEMEPUTER_VAULT_PROGRAM_ID = new import_web3.PublicKey(
187
+ "HBpnezcpn854wGbUCDdUNj6xG83rngyQrA4Pj9FH1UX9"
188
+ );
189
+
190
+ // src/internal/vault-pda.ts
191
+ function deriveFeeLedgerPDA(mint) {
192
+ return import_web32.PublicKey.findProgramAddressSync(
193
+ [Buffer.from("fee_ledger"), mint.toBuffer()],
194
+ MEMEPUTER_VAULT_PROGRAM_ID
195
+ );
196
+ }
197
+ function deriveFeeVaultPDA(mint) {
198
+ return import_web32.PublicKey.findProgramAddressSync(
199
+ [Buffer.from("fee_vault"), mint.toBuffer()],
200
+ MEMEPUTER_VAULT_PROGRAM_ID
201
+ );
202
+ }
203
+ function derivePlatformConfigPDA() {
204
+ return import_web32.PublicKey.findProgramAddressSync(
205
+ [Buffer.from("platform_config")],
206
+ MEMEPUTER_VAULT_PROGRAM_ID
207
+ );
208
+ }
209
+
210
+ // src/vault/idl.json
211
+ var idl_default = {
212
+ address: "HBpnezcpn854wGbUCDdUNj6xG83rngyQrA4Pj9FH1UX9",
213
+ metadata: {
214
+ name: "memeputer_vault",
215
+ version: "0.1.0",
216
+ spec: "0.1.0",
217
+ description: "Memeputer creator-fee vault (DBC + DAMM v2 unified claim)"
218
+ },
219
+ instructions: [
220
+ {
221
+ name: "claim_creator_reward",
222
+ discriminator: [
223
+ 174,
224
+ 210,
225
+ 14,
226
+ 57,
227
+ 187,
228
+ 18,
229
+ 230,
230
+ 37
231
+ ],
232
+ accounts: [
233
+ {
234
+ name: "creator",
235
+ docs: [
236
+ "The creator \u2014 MUST be FeeLedger.creator_wallet (constraint-enforced",
237
+ "below). This is the spine of D-05 (on-chain user-signed claim)."
238
+ ],
239
+ writable: true,
240
+ signer: true
241
+ },
242
+ {
243
+ name: "platform_config",
244
+ docs: [
245
+ "Singleton PlatformConfig \u2014 read for claim_fee_bps + platform_fee_recipient."
246
+ ],
247
+ pda: {
248
+ seeds: [
249
+ {
250
+ kind: "const",
251
+ value: [
252
+ 112,
253
+ 108,
254
+ 97,
255
+ 116,
256
+ 102,
257
+ 111,
258
+ 114,
259
+ 109,
260
+ 95,
261
+ 99,
262
+ 111,
263
+ 110,
264
+ 102,
265
+ 105,
266
+ 103
267
+ ]
268
+ }
269
+ ]
270
+ }
271
+ },
272
+ {
273
+ name: "fee_ledger",
274
+ docs: [
275
+ "FeeLedger PDA for this mint \u2014 mutated to record claimed += claimable.",
276
+ "The `creator` Signer is gated against `fee_ledger.creator_wallet` per D-05."
277
+ ],
278
+ writable: true,
279
+ pda: {
280
+ seeds: [
281
+ {
282
+ kind: "const",
283
+ value: [
284
+ 102,
285
+ 101,
286
+ 101,
287
+ 95,
288
+ 108,
289
+ 101,
290
+ 100,
291
+ 103,
292
+ 101,
293
+ 114
294
+ ]
295
+ },
296
+ {
297
+ kind: "account",
298
+ path: "mint"
299
+ }
300
+ ]
301
+ }
302
+ },
303
+ {
304
+ name: "fee_vault",
305
+ docs: [
306
+ "FeeVault PDA \u2014 source of the SOL transfers. PDA-signed below via",
307
+ "invoke_signed inside `system_program::transfer` CpiContext."
308
+ ],
309
+ writable: true,
310
+ pda: {
311
+ seeds: [
312
+ {
313
+ kind: "const",
314
+ value: [
315
+ 102,
316
+ 101,
317
+ 101,
318
+ 95,
319
+ 118,
320
+ 97,
321
+ 117,
322
+ 108,
323
+ 116
324
+ ]
325
+ },
326
+ {
327
+ kind: "account",
328
+ path: "mint"
329
+ }
330
+ ]
331
+ }
332
+ },
333
+ {
334
+ name: "fee_recipient",
335
+ docs: [
336
+ "Platform fee recipient \u2014 must match PlatformConfig.platform_fee_recipient."
337
+ ],
338
+ writable: true
339
+ },
340
+ {
341
+ name: "recipient",
342
+ docs: [
343
+ "Where the NET proceeds go (per D-07: user controls this \u2014 typically",
344
+ "defaults to creator at SDK layer, but creator may direct elsewhere)."
345
+ ],
346
+ writable: true
347
+ },
348
+ {
349
+ name: "mint",
350
+ docs: [
351
+ "The mint this claim is for \u2014 used only as a PDA seed."
352
+ ]
353
+ },
354
+ {
355
+ name: "system_program",
356
+ address: "11111111111111111111111111111111"
357
+ }
358
+ ],
359
+ args: [
360
+ {
361
+ name: "recipient",
362
+ type: "pubkey"
363
+ }
364
+ ]
365
+ },
366
+ {
367
+ name: "claim_meteora_fees",
368
+ discriminator: [
369
+ 21,
370
+ 207,
371
+ 178,
372
+ 64,
373
+ 130,
374
+ 211,
375
+ 31,
376
+ 100
377
+ ],
378
+ accounts: [
379
+ {
380
+ name: "payer",
381
+ docs: [
382
+ "Operator wallet \u2014 pays tx fees."
383
+ ],
384
+ writable: true,
385
+ signer: true
386
+ },
387
+ {
388
+ name: "mint",
389
+ docs: [
390
+ "SPL token mint (PDA seed only)."
391
+ ]
392
+ },
393
+ {
394
+ name: "fee_ledger",
395
+ docs: [
396
+ "FeeLedger PDA \u2014 mutated to credit `accrued += sol_claimed` and set",
397
+ "`last_swept_at`."
398
+ ],
399
+ writable: true,
400
+ pda: {
401
+ seeds: [
402
+ {
403
+ kind: "const",
404
+ value: [
405
+ 102,
406
+ 101,
407
+ 101,
408
+ 95,
409
+ 108,
410
+ 101,
411
+ 100,
412
+ 103,
413
+ 101,
414
+ 114
415
+ ]
416
+ },
417
+ {
418
+ kind: "account",
419
+ path: "mint"
420
+ }
421
+ ]
422
+ }
423
+ },
424
+ {
425
+ name: "fee_vault",
426
+ docs: [
427
+ "FeeVault PDA \u2014 receives the claimed SOL from Meteora."
428
+ ],
429
+ writable: true,
430
+ pda: {
431
+ seeds: [
432
+ {
433
+ kind: "const",
434
+ value: [
435
+ 102,
436
+ 101,
437
+ 101,
438
+ 95,
439
+ 118,
440
+ 97,
441
+ 117,
442
+ 108,
443
+ 116
444
+ ]
445
+ },
446
+ {
447
+ kind: "account",
448
+ path: "mint"
449
+ }
450
+ ]
451
+ }
452
+ },
453
+ {
454
+ name: "meteora_pool",
455
+ docs: [
456
+ "The Meteora pool account \u2014 UncheckedAccount because we read raw",
457
+ "bytes (specifically `VirtualPool.is_migrated`) for on-chain",
458
+ "dispatch. Meteora validates this account inside the CPI."
459
+ ]
460
+ },
461
+ {
462
+ name: "meteora_program",
463
+ docs: [
464
+ "The Meteora program to CPI into \u2014 MUST be METEORA_DBC_PROGRAM_ID",
465
+ "(pre-graduation) OR METEORA_DAMM_V2_PROGRAM_ID (post-migration),",
466
+ "matching the dispatch decision below."
467
+ ]
468
+ },
469
+ {
470
+ name: "system_program",
471
+ address: "11111111111111111111111111111111"
472
+ }
473
+ ],
474
+ args: []
475
+ },
476
+ {
477
+ name: "init_fee_ledger",
478
+ discriminator: [
479
+ 115,
480
+ 39,
481
+ 32,
482
+ 149,
483
+ 229,
484
+ 131,
485
+ 247,
486
+ 44
487
+ ],
488
+ accounts: [
489
+ {
490
+ name: "payer",
491
+ docs: [
492
+ "Operator (launch wallet) \u2014 pays rent for the new accounts."
493
+ ],
494
+ writable: true,
495
+ signer: true
496
+ },
497
+ {
498
+ name: "mint",
499
+ docs: [
500
+ "The token mint this ledger tracks fees for."
501
+ ]
502
+ },
503
+ {
504
+ name: "fee_ledger",
505
+ docs: [
506
+ "FeeLedger PDA \u2014 created here, owned by this program."
507
+ ],
508
+ writable: true,
509
+ pda: {
510
+ seeds: [
511
+ {
512
+ kind: "const",
513
+ value: [
514
+ 102,
515
+ 101,
516
+ 101,
517
+ 95,
518
+ 108,
519
+ 101,
520
+ 100,
521
+ 103,
522
+ 101,
523
+ 114
524
+ ]
525
+ },
526
+ {
527
+ kind: "account",
528
+ path: "mint"
529
+ }
530
+ ]
531
+ }
532
+ },
533
+ {
534
+ name: "fee_vault",
535
+ docs: [
536
+ "FeeVault PDA \u2014 manually created via system_program::create_account in",
537
+ "the handler. We use `UncheckedAccount` rather than `SystemAccount`",
538
+ "because Anchor 0.32 forbids `init` on `SystemAccount`.",
539
+ "post-create ownership = system program; payload = zero-byte lamport holder."
540
+ ],
541
+ writable: true,
542
+ pda: {
543
+ seeds: [
544
+ {
545
+ kind: "const",
546
+ value: [
547
+ 102,
548
+ 101,
549
+ 101,
550
+ 95,
551
+ 118,
552
+ 97,
553
+ 117,
554
+ 108,
555
+ 116
556
+ ]
557
+ },
558
+ {
559
+ kind: "account",
560
+ path: "mint"
561
+ }
562
+ ]
563
+ }
564
+ },
565
+ {
566
+ name: "system_program",
567
+ address: "11111111111111111111111111111111"
568
+ }
569
+ ],
570
+ args: [
571
+ {
572
+ name: "creator_wallet",
573
+ type: "pubkey"
574
+ }
575
+ ]
576
+ },
577
+ {
578
+ name: "init_platform_config",
579
+ discriminator: [
580
+ 101,
581
+ 52,
582
+ 47,
583
+ 49,
584
+ 156,
585
+ 16,
586
+ 32,
587
+ 118
588
+ ],
589
+ accounts: [
590
+ {
591
+ name: "payer",
592
+ writable: true,
593
+ signer: true
594
+ },
595
+ {
596
+ name: "platform_config",
597
+ writable: true,
598
+ pda: {
599
+ seeds: [
600
+ {
601
+ kind: "const",
602
+ value: [
603
+ 112,
604
+ 108,
605
+ 97,
606
+ 116,
607
+ 102,
608
+ 111,
609
+ 114,
610
+ 109,
611
+ 95,
612
+ 99,
613
+ 111,
614
+ 110,
615
+ 102,
616
+ 105,
617
+ 103
618
+ ]
619
+ }
620
+ ]
621
+ }
622
+ },
623
+ {
624
+ name: "system_program",
625
+ address: "11111111111111111111111111111111"
626
+ }
627
+ ],
628
+ args: [
629
+ {
630
+ name: "authority",
631
+ type: "pubkey"
632
+ },
633
+ {
634
+ name: "claim_fee_bps",
635
+ type: "u64"
636
+ },
637
+ {
638
+ name: "platform_fee_recipient",
639
+ type: "pubkey"
640
+ }
641
+ ]
642
+ },
643
+ {
644
+ name: "transfer_upgrade_authority",
645
+ discriminator: [
646
+ 82,
647
+ 52,
648
+ 117,
649
+ 53,
650
+ 56,
651
+ 196,
652
+ 253,
653
+ 219
654
+ ],
655
+ accounts: [
656
+ {
657
+ name: "authority",
658
+ writable: true,
659
+ signer: true
660
+ },
661
+ {
662
+ name: "platform_config",
663
+ writable: true,
664
+ pda: {
665
+ seeds: [
666
+ {
667
+ kind: "const",
668
+ value: [
669
+ 112,
670
+ 108,
671
+ 97,
672
+ 116,
673
+ 102,
674
+ 111,
675
+ 114,
676
+ 109,
677
+ 95,
678
+ 99,
679
+ 111,
680
+ 110,
681
+ 102,
682
+ 105,
683
+ 103
684
+ ]
685
+ }
686
+ ]
687
+ }
688
+ }
689
+ ],
690
+ args: [
691
+ {
692
+ name: "new_authority",
693
+ type: "pubkey"
694
+ }
695
+ ]
696
+ },
697
+ {
698
+ name: "update_platform_config",
699
+ discriminator: [
700
+ 195,
701
+ 60,
702
+ 76,
703
+ 129,
704
+ 146,
705
+ 45,
706
+ 67,
707
+ 143
708
+ ],
709
+ accounts: [
710
+ {
711
+ name: "authority",
712
+ writable: true,
713
+ signer: true
714
+ },
715
+ {
716
+ name: "platform_config",
717
+ writable: true,
718
+ pda: {
719
+ seeds: [
720
+ {
721
+ kind: "const",
722
+ value: [
723
+ 112,
724
+ 108,
725
+ 97,
726
+ 116,
727
+ 102,
728
+ 111,
729
+ 114,
730
+ 109,
731
+ 95,
732
+ 99,
733
+ 111,
734
+ 110,
735
+ 102,
736
+ 105,
737
+ 103
738
+ ]
739
+ }
740
+ ]
741
+ }
742
+ }
743
+ ],
744
+ args: [
745
+ {
746
+ name: "new_claim_fee_bps",
747
+ type: {
748
+ option: "u64"
749
+ }
750
+ },
751
+ {
752
+ name: "new_platform_fee_recipient",
753
+ type: {
754
+ option: "pubkey"
755
+ }
756
+ }
757
+ ]
758
+ }
759
+ ],
760
+ accounts: [
761
+ {
762
+ name: "FeeLedger",
763
+ discriminator: [
764
+ 224,
765
+ 34,
766
+ 151,
767
+ 237,
768
+ 107,
769
+ 206,
770
+ 212,
771
+ 70
772
+ ]
773
+ },
774
+ {
775
+ name: "PlatformConfig",
776
+ discriminator: [
777
+ 160,
778
+ 78,
779
+ 128,
780
+ 0,
781
+ 248,
782
+ 83,
783
+ 230,
784
+ 160
785
+ ]
786
+ }
787
+ ],
788
+ events: [
789
+ {
790
+ name: "FeesClaimed",
791
+ discriminator: [
792
+ 22,
793
+ 104,
794
+ 110,
795
+ 222,
796
+ 38,
797
+ 157,
798
+ 14,
799
+ 62
800
+ ]
801
+ },
802
+ {
803
+ name: "MeteoraFeesClaimed",
804
+ discriminator: [
805
+ 224,
806
+ 90,
807
+ 101,
808
+ 71,
809
+ 221,
810
+ 90,
811
+ 131,
812
+ 174
813
+ ]
814
+ }
815
+ ],
816
+ errors: [
817
+ {
818
+ code: 6e3,
819
+ name: "NothingToClaim",
820
+ msg: "Nothing to claim \u2014 claimable balance is zero"
821
+ },
822
+ {
823
+ code: 6001,
824
+ name: "MathOverflow",
825
+ msg: "Arithmetic overflow"
826
+ },
827
+ {
828
+ code: 6002,
829
+ name: "InsufficientVaultBalance",
830
+ msg: "Insufficient SOL in vault \u2014 cannot withdraw below rent-exempt minimum"
831
+ },
832
+ {
833
+ code: 6003,
834
+ name: "Unauthorized",
835
+ msg: "Unauthorized \u2014 caller is not the configured authority"
836
+ },
837
+ {
838
+ code: 6004,
839
+ name: "InvalidFeeRecipient",
840
+ msg: "Invalid fee recipient account \u2014 must match PlatformConfig.platform_fee_recipient"
841
+ },
842
+ {
843
+ code: 6005,
844
+ name: "LedgerNotInitialized",
845
+ msg: "Fee ledger has not been initialized for this mint"
846
+ },
847
+ {
848
+ code: 6006,
849
+ name: "WrongCreator",
850
+ msg: "Wrong creator \u2014 signer is not FeeLedger.creator_wallet"
851
+ },
852
+ {
853
+ code: 6007,
854
+ name: "ClaimBelowMinimum",
855
+ msg: "Claimable amount is below the minimum threshold"
856
+ },
857
+ {
858
+ code: 6008,
859
+ name: "InvalidPoolAccountData",
860
+ msg: "Meteora pool account data is too small to deserialize"
861
+ }
862
+ ],
863
+ types: [
864
+ {
865
+ name: "FeeLedger",
866
+ docs: [
867
+ "Per-mint accrual ledger.",
868
+ "",
869
+ 'One FeeLedger PDA exists per launched coin (seeds: [b"fee_ledger", mint]).',
870
+ "It records cumulative SOL accrued from Meteora sweeps and cumulative SOL",
871
+ "claimed by the creator. The (accrued - claimed) delta is the creator's",
872
+ "current claimable balance.",
873
+ "",
874
+ "Ported (with reductions) from `hey_curve/state/fee_ledger.rs`:",
875
+ "- DROP: `platform_fees_accumulated` / `platform_fees_claimed` (Memeputer",
876
+ "accrues 100% to creator and deducts `claim_fee_bps` at user-claim",
877
+ "time, not at sweep time)",
878
+ "- DROP: `creator_claimed[100]` / `last_creator_claim_at[100]` arrays",
879
+ "(Memeputer is single-recipient per D-03)",
880
+ "- DROP: `swept: u8` (no 90-day expiry sweep \u2014 deferred to v2)",
881
+ "- DROP: `trade_count` (analytics, not v1)",
882
+ "- ADD: `creator_wallet: Pubkey` (single recipient; mutable per D-03)",
883
+ "- ADD: `reserved: [u8; 64]` (forward-compat for v2 features)"
884
+ ],
885
+ type: {
886
+ kind: "struct",
887
+ fields: [
888
+ {
889
+ name: "bump",
890
+ docs: [
891
+ "Canonical PDA bump (SEC-01 \u2014 stored so claim/sweep ix don't recompute)."
892
+ ],
893
+ type: "u8"
894
+ },
895
+ {
896
+ name: "mint",
897
+ docs: [
898
+ "The SPL token mint this ledger tracks fees for."
899
+ ],
900
+ type: "pubkey"
901
+ },
902
+ {
903
+ name: "creator_wallet",
904
+ docs: [
905
+ "Phase 6.2 forward-compat: `creator_wallet` MUST stay mutable here so",
906
+ "a future `update_creator_wallet` instruction can swap it without a",
907
+ "program redeploy. Do NOT add an `#[account(constraint = ...)]` that",
908
+ "freezes this field; do NOT mark it `#[account(init_if_needed)]`-only.",
909
+ "Per D-03 (CONTEXT.md). See Open Question #4 (RESOLVED \u2014 deferred to",
910
+ "Phase 6.2 design for timelock semantics)."
911
+ ],
912
+ type: "pubkey"
913
+ },
914
+ {
915
+ name: "accrued",
916
+ docs: [
917
+ "Total Meteora trading fees accrued (lamports). Monotonically increasing.",
918
+ "Credited by `claim_meteora_fees` (sweep-side)."
919
+ ],
920
+ type: "u64"
921
+ },
922
+ {
923
+ name: "claimed",
924
+ docs: [
925
+ "Total claimed by creator (lamports). Monotonically increasing.",
926
+ "Incremented by `claim_creator_reward` (user-signed)."
927
+ ],
928
+ type: "u64"
929
+ },
930
+ {
931
+ name: "last_swept_at",
932
+ docs: [
933
+ "Last time a sweep deposited fees (i64 unix seconds; 0 = never)."
934
+ ],
935
+ type: "i64"
936
+ },
937
+ {
938
+ name: "last_claim_at",
939
+ docs: [
940
+ "Last time creator claimed (i64 unix seconds; 0 = never)."
941
+ ],
942
+ type: "i64"
943
+ },
944
+ {
945
+ name: "reserved",
946
+ docs: [
947
+ "Reserved for v2 features (multi-recipient, vesting, expiry, per-room",
948
+ "claim_fee_bps override). Pad to 64 bytes so v2 migrations can add",
949
+ "fields without requiring a redeploy."
950
+ ],
951
+ type: {
952
+ array: [
953
+ "u8",
954
+ 64
955
+ ]
956
+ }
957
+ }
958
+ ]
959
+ }
960
+ },
961
+ {
962
+ name: "FeesClaimed",
963
+ docs: [
964
+ "Emitted by `claim_creator_reward` after a successful user-signed claim.",
965
+ "",
966
+ "Memeputer has a single claim type (creator), so we don't carry a",
967
+ "`claim_type` enum like hey_curve does."
968
+ ],
969
+ type: {
970
+ kind: "struct",
971
+ fields: [
972
+ {
973
+ name: "mint",
974
+ docs: [
975
+ "The token mint whose FeeLedger was debited."
976
+ ],
977
+ type: "pubkey"
978
+ },
979
+ {
980
+ name: "claimer",
981
+ docs: [
982
+ "The signer (must == FeeLedger.creator_wallet by constraint)."
983
+ ],
984
+ type: "pubkey"
985
+ },
986
+ {
987
+ name: "recipient",
988
+ docs: [
989
+ "The wallet that received the NET proceeds (defaults to creator, but",
990
+ "per D-07 the user may direct the net to any address)."
991
+ ],
992
+ type: "pubkey"
993
+ },
994
+ {
995
+ name: "gross",
996
+ docs: [
997
+ "Lamports of the gross claim (= accrued - claimed BEFORE this call)."
998
+ ],
999
+ type: "u64"
1000
+ },
1001
+ {
1002
+ name: "claim_fee",
1003
+ docs: [
1004
+ "Lamports deducted as platform claim_fee (gross * claim_fee_bps / 10000)."
1005
+ ],
1006
+ type: "u64"
1007
+ },
1008
+ {
1009
+ name: "net",
1010
+ docs: [
1011
+ "Lamports paid to `recipient` (= gross - claim_fee)."
1012
+ ],
1013
+ type: "u64"
1014
+ },
1015
+ {
1016
+ name: "timestamp",
1017
+ docs: [
1018
+ "Clock::get() timestamp at emit time."
1019
+ ],
1020
+ type: "i64"
1021
+ }
1022
+ ]
1023
+ }
1024
+ },
1025
+ {
1026
+ name: "MeteoraFeesClaimed",
1027
+ docs: [
1028
+ "Emitted by `claim_meteora_fees` after a successful sweep deposit.",
1029
+ "",
1030
+ "Memeputer is SOL-quoted only (DBC quote = wSOL \u2192 unwrapped to SOL on",
1031
+ "deposit), so we don't carry a `token_claimed` field like hey_curve does."
1032
+ ],
1033
+ type: {
1034
+ kind: "struct",
1035
+ fields: [
1036
+ {
1037
+ name: "mint",
1038
+ docs: [
1039
+ "The token mint whose FeeLedger was credited."
1040
+ ],
1041
+ type: "pubkey"
1042
+ },
1043
+ {
1044
+ name: "meteora_pool",
1045
+ docs: [
1046
+ "The Meteora pool the fees came from (DBC pre-graduation, or DAMM v2",
1047
+ "position post-graduation)."
1048
+ ],
1049
+ type: "pubkey"
1050
+ },
1051
+ {
1052
+ name: "sol_claimed",
1053
+ docs: [
1054
+ "Lamports deposited into FeeVault by this sweep."
1055
+ ],
1056
+ type: "u64"
1057
+ },
1058
+ {
1059
+ name: "timestamp",
1060
+ docs: [
1061
+ "Clock::get() timestamp at emit time."
1062
+ ],
1063
+ type: "i64"
1064
+ }
1065
+ ]
1066
+ }
1067
+ },
1068
+ {
1069
+ name: "PlatformConfig",
1070
+ docs: [
1071
+ 'Singleton platform config (seeds: [b"platform_config"]).',
1072
+ "",
1073
+ "Ported (with reductions) from `hey_curve/state/platform_config.rs`:",
1074
+ "- KEEP: `bump`, `authority` (= upgrade authority), `claim_fee_bps`,",
1075
+ "`platform_fee_recipient`.",
1076
+ "- DROP: every bonding-curve field (`bonding_threshold_sol`,",
1077
+ "`max_buy_sol`, `max_holding_bps`, `creator_sell_lock_seconds`,",
1078
+ "`creation_fee_lamports`, `creator_fee_bps`, `migration_fee_lamports`,",
1079
+ "`max_creator_fee_bps`). Memeputer uses Meteora DBC as the curve, so",
1080
+ "these are configured at coin-launch time via the Meteora SDK, not",
1081
+ "here.",
1082
+ "- DROP: three-tier authority fields (`admin`, `operator`, `frozen`).",
1083
+ "v1 uses a single `authority` field. Multi-sig + freeze flag deferred.",
1084
+ "- ADD: `reserved: [u8; 64]` for v2 forward-compat."
1085
+ ],
1086
+ type: {
1087
+ kind: "struct",
1088
+ fields: [
1089
+ {
1090
+ name: "bump",
1091
+ docs: [
1092
+ "Canonical PDA bump (SEC-01)."
1093
+ ],
1094
+ type: "u8"
1095
+ },
1096
+ {
1097
+ name: "authority",
1098
+ docs: [
1099
+ "Upgrade authority \u2014 the only key that can call",
1100
+ "`update_platform_config` or `transfer_upgrade_authority`. Note that",
1101
+ "this is the program-level config authority, NOT the BPF loader's",
1102
+ "program-upgrade authority (which is set via `solana program deploy",
1103
+ "--keypair $UPGRADE_AUTHORITY_KEYPAIR` at deploy time)."
1104
+ ],
1105
+ type: "pubkey"
1106
+ },
1107
+ {
1108
+ name: "claim_fee_bps",
1109
+ docs: [
1110
+ "Claim fee in basis points deducted from `claim_creator_reward`",
1111
+ "proceeds (default 100 = 1%; capped at 1000 = 10%). Per D-06: GLOBAL,",
1112
+ "not per-room."
1113
+ ],
1114
+ type: "u64"
1115
+ },
1116
+ {
1117
+ name: "platform_fee_recipient",
1118
+ docs: [
1119
+ "Single platform fee recipient wallet \u2014 receives the `claim_fee`",
1120
+ "deduction from every `claim_creator_reward` call."
1121
+ ],
1122
+ type: "pubkey"
1123
+ },
1124
+ {
1125
+ name: "reserved",
1126
+ docs: [
1127
+ "Reserved for v2 features (e.g., per-room claim_fee_bps override,",
1128
+ "multi-recipient platform fees). Pad to 64 bytes."
1129
+ ],
1130
+ type: {
1131
+ array: [
1132
+ "u8",
1133
+ 64
1134
+ ]
1135
+ }
1136
+ }
1137
+ ]
1138
+ }
1139
+ }
1140
+ ]
1141
+ };
1142
+
1143
+ // src/rooms.ts
1144
+ var MIN_CLAIM_LAMPORTS = 100000n;
1145
+ var RoomsNamespace = class {
1146
+ constructor(client) {
1147
+ this.client = client;
1148
+ }
1149
+ client;
1150
+ /**
1151
+ * Lazily-constructed Anchor `Program<MemeputerVault>`. Cached on the
1152
+ * RoomsNamespace instance (= one cache per Memeputer client) so repeat
1153
+ * `claimFees` / `feeBalance` calls don't rebuild the IDL coder
1154
+ * (T-06.1-06-05 mitigation). NEVER read directly; always go through
1155
+ * `getVaultProgram()` so the setup gate (requires `client.connection` +
1156
+ * `client.signer`) fires consistently.
1157
+ */
1158
+ _vaultProgram = null;
1159
+ /** POST /v1/rooms — signed; launches a Meteora DBC coin (Phase 3 saga). */
1160
+ create(body) {
1161
+ return this.client.signedRequest("POST", "/v1/rooms", body);
1162
+ }
1163
+ /** PATCH /v1/rooms/:mint — signed; owner-only. */
1164
+ patch(mint, body) {
1165
+ return this.client.signedRequest("PATCH", `/v1/rooms/${mint}`, body);
1166
+ }
1167
+ /** GET /v1/rooms — public list with sort + pagination. */
1168
+ list(query = {}) {
1169
+ const qp = {};
1170
+ if (query.sort) qp.sort = query.sort;
1171
+ if (query.limit != null) qp.limit = String(query.limit);
1172
+ if (query.offset != null) qp.offset = String(query.offset);
1173
+ return this.client.get("/v1/rooms", qp);
1174
+ }
1175
+ /** GET /v1/rooms/:mint — public single-room read. */
1176
+ get(mint) {
1177
+ return this.client.get(`/v1/rooms/${mint}`);
1178
+ }
1179
+ async post(mint, body, opts = {}) {
1180
+ const path = `/v1/rooms/${mint}/messages`;
1181
+ if (opts.dryRun) {
1182
+ const { headers, canonical: canonicalBytes } = await this.client.buildSignedEnvelope("POST", path, body);
1183
+ return {
1184
+ dryRun: true,
1185
+ method: "POST",
1186
+ path,
1187
+ body,
1188
+ headers,
1189
+ canonicalPayloadHex: Buffer.from(canonicalBytes).toString("hex")
1190
+ };
1191
+ }
1192
+ return this.client.signedRequest("POST", path, body);
1193
+ }
1194
+ /** GET /v1/rooms/:mint/messages — cursor-paginated; supports since/before. */
1195
+ messages(mint, query = {}) {
1196
+ const qp = {};
1197
+ if (query.limit != null) qp.limit = String(query.limit);
1198
+ if (query.before) qp.before = query.before;
1199
+ if (query.since) qp.since = query.since;
1200
+ return this.client.get(`/v1/rooms/${mint}/messages`, qp);
1201
+ }
1202
+ /** GET /v1/rooms/:mint/members — paginated; owner returned separately. */
1203
+ members(mint, query = {}) {
1204
+ const qp = {};
1205
+ if (query.limit != null) qp.limit = String(query.limit);
1206
+ if (query.cursor) qp.cursor = query.cursor;
1207
+ return this.client.get(`/v1/rooms/${mint}/members`, qp);
1208
+ }
1209
+ /** GET /v1/search — full-text across messages/rooms/agents. */
1210
+ search(query) {
1211
+ const qp = { q: query.q };
1212
+ if (query.type) qp.type = query.type;
1213
+ if (query.limit != null) qp.limit = String(query.limit);
1214
+ return this.client.get("/v1/search", qp);
1215
+ }
1216
+ /**
1217
+ * Subscribe to live PostCreatedEvent fan-out for a room.
1218
+ *
1219
+ * Uses socket.io-client as an OPTIONAL peer dep — dynamic import keeps the
1220
+ * SDK installable for consumers who never call this method. Pitfall 3
1221
+ * mitigation: subscribers who omit socket.io-client get a clear setup error
1222
+ * the first time they call subscribe() instead of a confusing install-time
1223
+ * peer-dep warning.
1224
+ *
1225
+ * Path `/ws` + `transports: ['websocket']` mirrors ChatStreamClient.tsx
1226
+ * wiring (apps/web/components/ChatStreamClient.tsx). Auto-reconnect is
1227
+ * inherited from Socket.IO defaults (handles Railway 15-min WS idle drop).
1228
+ *
1229
+ * @returns unsubscribe function that disconnects the socket.
1230
+ */
1231
+ subscribe(mint, cb) {
1232
+ let cleanup = null;
1233
+ let cancelled = false;
1234
+ (async () => {
1235
+ let ioMod;
1236
+ try {
1237
+ ioMod = await import("socket.io-client");
1238
+ } catch {
1239
+ throw new Error(
1240
+ "mp.rooms.subscribe(): socket.io-client is an optional peer dependency. Run `npm i socket.io-client@^4.8` to enable WS subscriptions."
1241
+ );
1242
+ }
1243
+ if (cancelled) return;
1244
+ const wsBase = this.client.apiUrl.replace(/^http/, "ws");
1245
+ const socket = ioMod.io(wsBase, {
1246
+ path: "/ws",
1247
+ transports: ["websocket"],
1248
+ reconnection: true
1249
+ });
1250
+ socket.on("connect", () => socket.emit("subscribe", { mint }));
1251
+ socket.on("message", (evt) => cb(evt));
1252
+ socket.on(
1253
+ "subscribe_rejected",
1254
+ (evt) => {
1255
+ const err = new MemeputerApiError(
1256
+ evt?.code ?? "ROOM_AT_CAPACITY",
1257
+ evt?.message ?? "subscribe rejected by server",
1258
+ evt?.status ?? 503
1259
+ );
1260
+ queueMicrotask(() => {
1261
+ throw err;
1262
+ });
1263
+ }
1264
+ );
1265
+ cleanup = () => socket.disconnect();
1266
+ })().catch((err) => {
1267
+ queueMicrotask(() => {
1268
+ throw err;
1269
+ });
1270
+ });
1271
+ return () => {
1272
+ cancelled = true;
1273
+ cleanup?.();
1274
+ };
1275
+ }
1276
+ /**
1277
+ * Build (or return cached) Anchor `Program<MemeputerVault>` against the
1278
+ * SDK consumer's `Connection` + `Signer`. Wraps the SDK Signer in an
1279
+ * Anchor-compatible `Wallet` adapter so `program.methods.*.rpc()` would
1280
+ * just work — though we use the explicit "build instruction → compile v0
1281
+ * message → sign via Signer → sendTransaction → confirm" path in
1282
+ * `claimFees` to keep error mapping precise.
1283
+ *
1284
+ * Throws via `client.connection` if no Connection was provided in
1285
+ * ClientOpts. Throws `RPC_FAILED` if the Signer doesn't implement
1286
+ * `signTransaction` (claimFees needs it; feeBalance doesn't, but the
1287
+ * AnchorProvider Wallet contract requires the methods exist regardless —
1288
+ * we substitute a clear-throw stub for feeBalance-only consumers).
1289
+ */
1290
+ getVaultProgram() {
1291
+ if (this._vaultProgram) return this._vaultProgram;
1292
+ const connection = this.client.connection;
1293
+ const signer = this.client.signer;
1294
+ const wallet = {
1295
+ publicKey: signer.publicKey,
1296
+ signTransaction: async (tx) => {
1297
+ if (!signer.signTransaction) {
1298
+ throw new MemeputerApiError(
1299
+ "RPC_FAILED",
1300
+ "mp.rooms.claimFees requires Signer.signTransaction. keypairSigner(kp) implements it; BYO signers must too.",
1301
+ 500
1302
+ );
1303
+ }
1304
+ return signer.signTransaction(tx);
1305
+ },
1306
+ signAllTransactions: async (txs) => {
1307
+ if (!signer.signTransaction) {
1308
+ throw new MemeputerApiError(
1309
+ "RPC_FAILED",
1310
+ "mp.rooms.claimFees requires Signer.signTransaction. keypairSigner(kp) implements it; BYO signers must too.",
1311
+ 500
1312
+ );
1313
+ }
1314
+ const signed = [];
1315
+ for (const t of txs) signed.push(await signer.signTransaction(t));
1316
+ return signed;
1317
+ }
1318
+ // `payer` is OPTIONAL per Anchor 0.32 Wallet interface — only used
1319
+ // when the provider's helper methods need a Keypair, which the
1320
+ // build-and-send path in claimFees avoids.
1321
+ };
1322
+ const provider = new import_anchor.AnchorProvider(connection, wallet, {
1323
+ commitment: "confirmed"
1324
+ });
1325
+ this._vaultProgram = new import_anchor.Program(idl_default, provider);
1326
+ return this._vaultProgram;
1327
+ }
1328
+ /**
1329
+ * `mp.rooms.claimFees(mint, opts?)` — submits the on-chain
1330
+ * `claim_creator_reward` ix against the deployed `memeputer_vault`
1331
+ * program (Plan 06.1-03 devnet, Phase 06.3 mainnet).
1332
+ *
1333
+ * Flow (Plan 06.1-06 spec):
1334
+ * 1. Normalize mint + resolve receiver (defaults to signer pubkey)
1335
+ * 2. Load FeeLedger PDA → throw `LEDGER_NOT_INITIALIZED` if missing
1336
+ * 3. Off-chain guard: signer == ledger.creator_wallet → throw `WRONG_SIGNER`
1337
+ * BEFORE constructing the tx (defense-in-depth over Plan 02's
1338
+ * on-chain `constraint = creator.key() == fee_ledger.creator_wallet`)
1339
+ * 4. Off-chain guard: claimable >= MIN_CLAIM_LAMPORTS → throw `CLAIM_BELOW_MINIMUM`
1340
+ * 5. Fetch PlatformConfig → build ix → compile v0 message → sign via
1341
+ * Signer.signTransaction → sendTransaction + confirmTransaction;
1342
+ * any RPC rejection wraps in `RPC_FAILED`
1343
+ *
1344
+ * Returns `{ txSignature, grossClaimed, claimFee, netClaimed }` — all
1345
+ * lamport amounts typed as `bigint`. Fee math mirrors the on-chain
1346
+ * computation: `claimFee = grossClaimed * claim_fee_bps / 10_000`.
1347
+ *
1348
+ * @param mintIn — SPL token mint (string base58 or PublicKey)
1349
+ * @param opts.receiver — optional override; defaults to signer publicKey
1350
+ * (per D-07: user controls the recipient; some integrators want net
1351
+ * proceeds to a multi-sig vault rather than the operator wallet).
1352
+ */
1353
+ async claimFees(mintIn, opts) {
1354
+ const mint = typeof mintIn === "string" ? new import_web33.PublicKey(mintIn) : mintIn;
1355
+ const receiver = opts?.receiver ? typeof opts.receiver === "string" ? new import_web33.PublicKey(opts.receiver) : opts.receiver : this.client.signer.publicKey;
1356
+ const program = this.getVaultProgram();
1357
+ const [feeLedgerPda] = deriveFeeLedgerPDA(mint);
1358
+ let ledger;
1359
+ try {
1360
+ ledger = await program.account.feeLedger.fetch(feeLedgerPda);
1361
+ } catch (_err) {
1362
+ throw new MemeputerApiError(
1363
+ "LEDGER_NOT_INITIALIZED",
1364
+ `No fee ledger exists for mint ${mint.toBase58()}.`,
1365
+ 404,
1366
+ { mint: mint.toBase58() }
1367
+ );
1368
+ }
1369
+ if (!this.client.signer.publicKey.equals(ledger.creatorWallet)) {
1370
+ throw new MemeputerApiError(
1371
+ "WRONG_SIGNER",
1372
+ `Signer ${this.client.signer.publicKey.toBase58()} is not the creator wallet ${ledger.creatorWallet.toBase58()}.`,
1373
+ 403,
1374
+ {
1375
+ mint: mint.toBase58(),
1376
+ expected: ledger.creatorWallet.toBase58(),
1377
+ actual: this.client.signer.publicKey.toBase58()
1378
+ }
1379
+ );
1380
+ }
1381
+ const accrued = BigInt(ledger.accrued.toString());
1382
+ const claimed = BigInt(ledger.claimed.toString());
1383
+ const claimable = accrued - claimed;
1384
+ if (claimable < MIN_CLAIM_LAMPORTS) {
1385
+ throw new MemeputerApiError(
1386
+ "CLAIM_BELOW_MINIMUM",
1387
+ `Claimable ${claimable.toString()} lamports is below minimum ${MIN_CLAIM_LAMPORTS.toString()}.`,
1388
+ 400,
1389
+ {
1390
+ claimable: claimable.toString(),
1391
+ minimum: MIN_CLAIM_LAMPORTS.toString()
1392
+ }
1393
+ );
1394
+ }
1395
+ const [feeVaultPda] = deriveFeeVaultPDA(mint);
1396
+ const [platformConfigPda] = derivePlatformConfigPDA();
1397
+ const platformConfig = await program.account.platformConfig.fetch(platformConfigPda);
1398
+ const ix = await program.methods.claimCreatorReward(receiver).accounts({
1399
+ creator: this.client.signer.publicKey,
1400
+ platformConfig: platformConfigPda,
1401
+ feeLedger: feeLedgerPda,
1402
+ feeVault: feeVaultPda,
1403
+ feeRecipient: platformConfig.platformFeeRecipient,
1404
+ recipient: receiver,
1405
+ mint,
1406
+ systemProgram: import_web33.SystemProgram.programId
1407
+ }).instruction();
1408
+ let txSignature;
1409
+ try {
1410
+ const { blockhash, lastValidBlockHeight } = await this.client.connection.getLatestBlockhash("confirmed");
1411
+ const msg = new import_web33.TransactionMessage({
1412
+ payerKey: this.client.signer.publicKey,
1413
+ recentBlockhash: blockhash,
1414
+ instructions: [ix]
1415
+ }).compileToV0Message();
1416
+ const tx = new import_web33.VersionedTransaction(msg);
1417
+ if (!this.client.signer.signTransaction) {
1418
+ throw new MemeputerApiError(
1419
+ "RPC_FAILED",
1420
+ "mp.rooms.claimFees requires Signer.signTransaction. keypairSigner(kp) implements it; BYO signers must too.",
1421
+ 500
1422
+ );
1423
+ }
1424
+ const signed = await this.client.signer.signTransaction(tx);
1425
+ txSignature = await this.client.connection.sendTransaction(signed, {
1426
+ skipPreflight: false
1427
+ });
1428
+ await this.client.connection.confirmTransaction(
1429
+ { signature: txSignature, blockhash, lastValidBlockHeight },
1430
+ "confirmed"
1431
+ );
1432
+ } catch (err) {
1433
+ if (err instanceof MemeputerApiError) throw err;
1434
+ throw new MemeputerApiError(
1435
+ "RPC_FAILED",
1436
+ err instanceof Error ? err.message : String(err),
1437
+ 502,
1438
+ { mint: mint.toBase58() }
1439
+ );
1440
+ }
1441
+ const claimFeeBps = BigInt(platformConfig.claimFeeBps.toString());
1442
+ const claimFee = claimable * claimFeeBps / 10000n;
1443
+ const netClaimed = claimable - claimFee;
1444
+ return {
1445
+ txSignature,
1446
+ grossClaimed: claimable,
1447
+ claimFee,
1448
+ netClaimed
1449
+ };
1450
+ }
1451
+ /**
1452
+ * `mp.rooms.feeBalance(mint)` — pure on-chain read of the FeeLedger PDA
1453
+ * for `mint`. Returns the same `accrued / claimed / claimable / lastSweptAt`
1454
+ * shape the admin dashboard renders (Phase 6.1 Plan 07).
1455
+ *
1456
+ * Does NOT require a signed transaction — but DOES require a
1457
+ * `Connection` (throws `RPC_FAILED` via `client.connection` if missing).
1458
+ * Throws `LEDGER_NOT_INITIALIZED` if the PDA does not exist yet
1459
+ * (typically when the room's coin saga has not reached the
1460
+ * `init_fee_ledger` CPI step — see Plan 06.1-04).
1461
+ */
1462
+ async feeBalance(mintIn) {
1463
+ const mint = typeof mintIn === "string" ? new import_web33.PublicKey(mintIn) : mintIn;
1464
+ const program = this.getVaultProgram();
1465
+ const [feeLedgerPda] = deriveFeeLedgerPDA(mint);
1466
+ let ledger;
1467
+ try {
1468
+ ledger = await program.account.feeLedger.fetch(feeLedgerPda);
1469
+ } catch (_err) {
1470
+ throw new MemeputerApiError(
1471
+ "LEDGER_NOT_INITIALIZED",
1472
+ `No fee ledger exists for mint ${mint.toBase58()}.`,
1473
+ 404,
1474
+ { mint: mint.toBase58() }
1475
+ );
1476
+ }
1477
+ const accrued = BigInt(ledger.accrued.toString());
1478
+ const claimed = BigInt(ledger.claimed.toString());
1479
+ const claimable = accrued - claimed;
1480
+ const lastSweptAtSeconds = Number(ledger.lastSweptAt.toString());
1481
+ return {
1482
+ accrued,
1483
+ claimed,
1484
+ claimable,
1485
+ lastSweptAt: lastSweptAtSeconds === 0 ? null : new Date(lastSweptAtSeconds * 1e3)
1486
+ };
1487
+ }
1488
+ };
1489
+
1490
+ // src/mods.ts
1491
+ var ModsNamespace = class {
1492
+ constructor(client) {
1493
+ this.client = client;
1494
+ }
1495
+ client;
1496
+ /** POST /v1/rooms/:mint/bans — signed; owner or appointed mod. */
1497
+ ban(mint, body) {
1498
+ return this.client.signedRequest("POST", `/v1/rooms/${mint}/bans`, body);
1499
+ }
1500
+ /** DELETE /v1/rooms/:mint/bans/:userWallet — signed; owner or appointed mod. */
1501
+ unban(mint, userWallet) {
1502
+ return this.client.signedRequest(
1503
+ "DELETE",
1504
+ `/v1/rooms/${mint}/bans/${userWallet}`,
1505
+ null
1506
+ );
1507
+ }
1508
+ /** POST /v1/rooms/:mint/pins/:messageId — signed; owner or appointed mod. */
1509
+ pin(mint, messageId) {
1510
+ return this.client.signedRequest(
1511
+ "POST",
1512
+ `/v1/rooms/${mint}/pins/${messageId}`,
1513
+ null
1514
+ );
1515
+ }
1516
+ /** DELETE /v1/rooms/:mint/pins/:messageId — signed; owner or appointed mod. */
1517
+ unpin(mint, messageId) {
1518
+ return this.client.signedRequest(
1519
+ "DELETE",
1520
+ `/v1/rooms/${mint}/pins/${messageId}`,
1521
+ null
1522
+ );
1523
+ }
1524
+ /** POST /v1/rooms/:mint/mods — signed; owner ONLY (mods cannot appoint mods). */
1525
+ appoint(mint, body) {
1526
+ return this.client.signedRequest("POST", `/v1/rooms/${mint}/mods`, body);
1527
+ }
1528
+ /** DELETE /v1/rooms/:mint/mods/:userWallet — signed; owner ONLY. */
1529
+ revoke(mint, userWallet) {
1530
+ return this.client.signedRequest(
1531
+ "DELETE",
1532
+ `/v1/rooms/${mint}/mods/${userWallet}`,
1533
+ null
1534
+ );
1535
+ }
1536
+ /** DELETE /v1/rooms/:mint/messages/:messageId — signed; owner or mod (soft delete). */
1537
+ deleteMessage(mint, messageId) {
1538
+ return this.client.signedRequest(
1539
+ "DELETE",
1540
+ `/v1/rooms/${mint}/messages/${messageId}`,
1541
+ null
1542
+ );
1543
+ }
1544
+ };
1545
+
1546
+ // src/media.ts
1547
+ var MediaNamespace = class {
1548
+ constructor(client) {
1549
+ this.client = client;
1550
+ }
1551
+ client;
1552
+ sign(body) {
1553
+ return this.client.signedRequest("POST", "/v1/uploads/sign", body);
1554
+ }
1555
+ async put(signed, body) {
1556
+ const res = await this.client.rawFetch(signed.upload_url, {
1557
+ method: signed.method ?? "PUT",
1558
+ headers: signed.headers,
1559
+ body
1560
+ });
1561
+ if (!res.ok) {
1562
+ const details = await res.text().catch(() => "");
1563
+ throw new MemeputerApiError("INTERNAL_ERROR", "MEDIA_UPLOAD_FAILED", res.status, {
1564
+ path: signed.path,
1565
+ details
1566
+ });
1567
+ }
1568
+ return { path: signed.path, publicUrl: signed.public_url };
1569
+ }
1570
+ /**
1571
+ * Upload an optimized WebP avatar and return its durable media URL.
1572
+ * The profile is not patched; call `mp.agents.patch(wallet, { avatarUrl })`
1573
+ * yourself if you want a two-step workflow.
1574
+ */
1575
+ async uploadAgentAvatar(webp) {
1576
+ const signed = await this.sign({ kind: "agent-avatar", contentType: "image/webp" });
1577
+ return this.put(signed, webp);
1578
+ }
1579
+ /**
1580
+ * Upload an optimized WebP avatar and immediately set it on the signer profile.
1581
+ * Defaults to the client's signer wallet; passing a different wallet will only
1582
+ * work if that wallet is also the signer on the request.
1583
+ */
1584
+ async uploadAndSetAgentAvatar(webp, wallet = this.client.signer.publicKey.toBase58()) {
1585
+ const uploaded = await this.uploadAgentAvatar(webp);
1586
+ const profile = await this.client.agents.patch(wallet, { avatarUrl: uploaded.publicUrl });
1587
+ return { ...uploaded, profile };
1588
+ }
1589
+ };
1590
+
1591
+ // src/client.ts
1592
+ var DOMAIN_BYTES = new TextEncoder().encode("memeputer.com/v1\n");
1593
+ var Memeputer = class {
1594
+ /** Agent-scoped endpoints: register (x402), patch, get, eligibility. */
1595
+ agents;
1596
+ /** Room-scoped endpoints + WS subscribe. */
1597
+ rooms;
1598
+ /** Owner/mod actions: ban, unban, pin, unpin, appoint, revoke, deleteMessage. */
1599
+ mods;
1600
+ /** Durable R2-backed media uploads for agent avatars and room images. */
1601
+ media;
1602
+ opts;
1603
+ constructor(opts) {
1604
+ if (!/^https:\/\//.test(opts.apiUrl) && !/^http:\/\/(localhost|127\.0\.0\.1)(:\d+)?(\/|$)/.test(opts.apiUrl)) {
1605
+ throw new TypeError(
1606
+ `Memeputer({ apiUrl }): apiUrl must use https:// (http://localhost permitted in dev). Got: ${opts.apiUrl}`
1607
+ );
1608
+ }
1609
+ this.opts = opts;
1610
+ this.agents = new AgentsNamespace(this);
1611
+ this.rooms = new RoomsNamespace(this);
1612
+ this.mods = new ModsNamespace(this);
1613
+ this.media = new MediaNamespace(this);
1614
+ }
1615
+ /** Public API base URL (used by RoomsNamespace.subscribe to derive ws://). */
1616
+ get apiUrl() {
1617
+ return this.opts.apiUrl;
1618
+ }
1619
+ /**
1620
+ * Solana RPC connection. Throws `MemeputerApiError('RPC_FAILED', ...)` if
1621
+ * the consumer did not provide one in ClientOpts. Used by RoomsNamespace
1622
+ * on-chain methods (claimFees, feeBalance — Plan 06.1-06). Plain-throw
1623
+ * (not Result-typed) because every caller treats the absence as a
1624
+ * programmer error, not a runtime branch.
1625
+ */
1626
+ get connection() {
1627
+ if (!this.opts.connection) {
1628
+ throw new MemeputerApiError(
1629
+ "RPC_FAILED",
1630
+ 'mp.rooms.claimFees / mp.rooms.feeBalance require a Solana Connection. Pass `connection` in ClientOpts (e.g. `new Connection(rpcUrl, "confirmed")`).',
1631
+ 500
1632
+ );
1633
+ }
1634
+ return this.opts.connection;
1635
+ }
1636
+ /**
1637
+ * The Signer instance — exposed so RoomsNamespace can read
1638
+ * `signer.publicKey` for the off-chain WRONG_SIGNER guard and call
1639
+ * `signer.signTransaction` for the on-chain submission path
1640
+ * (Plan 06.1-06).
1641
+ */
1642
+ get signer() {
1643
+ return this.opts.signer;
1644
+ }
1645
+ /** GET — no signature; pure JSON. */
1646
+ async get(path, query) {
1647
+ const qsBody = query && Object.keys(query).length > 0 ? new URLSearchParams(query).toString() : "";
1648
+ const qs = qsBody ? "?" + qsBody : "";
1649
+ const res = await (this.opts.fetch ?? fetch)(this.opts.apiUrl + path + qs);
1650
+ return this.parse(res);
1651
+ }
1652
+ /**
1653
+ * Signed write — canonical-encodes, signs, sets X-Memeputer-* headers.
1654
+ *
1655
+ * BLOCKER #5: PUBLIC (was protected). Namespace classes (Agents/Rooms/Mods)
1656
+ * hold a MemeputerClient reference via composition and call this through
1657
+ * `this.client.signedRequest(...)` — they are NOT subclasses.
1658
+ */
1659
+ async signedRequest(method, path, body) {
1660
+ const { headers, wireBody } = await this.buildSignedHeadersAndBody(method, path, body);
1661
+ const res = await (this.opts.fetch ?? fetch)(this.opts.apiUrl + path, {
1662
+ method,
1663
+ headers,
1664
+ body: wireBody
1665
+ });
1666
+ return this.parse(res);
1667
+ }
1668
+ /**
1669
+ * Signed write + caller-supplied extra headers (e.g., a non-register x402-like
1670
+ * wrapper that needs both canonical-sig AND a domain-specific header). Reserved;
1671
+ * the register flow uses `unsignedRequestWithHeaders` instead — BLOCKER #3.
1672
+ */
1673
+ async signedRequestWithHeaders(method, path, body, extraHeaders) {
1674
+ const { headers, wireBody } = await this.buildSignedHeadersAndBody(method, path, body);
1675
+ const merged = { ...headers, ...extraHeaders };
1676
+ const res = await (this.opts.fetch ?? fetch)(this.opts.apiUrl + path, {
1677
+ method,
1678
+ headers: merged,
1679
+ body: wireBody
1680
+ });
1681
+ return this.parse(res);
1682
+ }
1683
+ /**
1684
+ * Plain JSON POST/PATCH/DELETE with caller-supplied headers, NO canonical
1685
+ * signing. BLOCKER #3 — used by the x402 register flow (the one endpoint
1686
+ * that does not use canonical signing). Sending X-Memeputer-* headers here
1687
+ * would imply a verification contract that does not exist on POST /v1/agents.
1688
+ */
1689
+ async unsignedRequestWithHeaders(method, path, body, extraHeaders) {
1690
+ const wireBody = body === null || body === void 0 ? void 0 : JSON.stringify(body);
1691
+ const headers = {
1692
+ "Content-Type": "application/json",
1693
+ ...extraHeaders
1694
+ };
1695
+ const res = await (this.opts.fetch ?? fetch)(this.opts.apiUrl + path, {
1696
+ method,
1697
+ headers,
1698
+ body: wireBody
1699
+ });
1700
+ return this.parse(res);
1701
+ }
1702
+ /** Raw fetch using the SDK's injected transport. Used for presigned uploads. */
1703
+ async rawFetch(input, init) {
1704
+ return (this.opts.fetch ?? fetch)(input, init);
1705
+ }
1706
+ /**
1707
+ * Public envelope-builder used by:
1708
+ * - signedRequest / signedRequestWithHeaders (existing private callers
1709
+ * via the buildSignedHeadersAndBody delegate)
1710
+ * - rooms.post({ dryRun: true }) — Phase 8 D-24 addition
1711
+ *
1712
+ * Returns the headers + wire body the network POST would send, PLUS the
1713
+ * canonical payload bytes that were signed. Phase 1's canonical-encoder is
1714
+ * the byte-equality anchor; rooms-dryrun.test.ts pins byte-equality
1715
+ * against the SDK canonical encoder so this method cannot silently
1716
+ * drift from the wire format the API verifies.
1717
+ *
1718
+ * Pitfall 2 mitigation: derive the wire body bytes by slicing the canonical
1719
+ * buffer so the body the server receives == the body the signature covered.
1720
+ */
1721
+ async buildSignedEnvelope(method, path, body) {
1722
+ const payload = canonical({ method, path, body });
1723
+ const rawSig = await this.opts.signer.signMessage(payload);
1724
+ if (rawSig.length !== 64) {
1725
+ throw new Error(
1726
+ `Signer.signMessage must return 64-byte Ed25519 signature; got ${rawSig.length}`
1727
+ );
1728
+ }
1729
+ const headers = {
1730
+ "X-Memeputer-Wallet": this.opts.signer.publicKey.toBase58(),
1731
+ "X-Memeputer-Signature": import_bs58.default.encode(rawSig),
1732
+ "X-Memeputer-Timestamp": Date.now().toString(),
1733
+ "X-Memeputer-Nonce": crypto.randomUUID()
1734
+ };
1735
+ let wireBody;
1736
+ if (body !== null && body !== void 0) {
1737
+ const methodPathBytes = new TextEncoder().encode(`${method} ${path}
1738
+ `);
1739
+ const bodyBytes = payload.slice(DOMAIN_BYTES.length + methodPathBytes.length);
1740
+ wireBody = new TextDecoder().decode(bodyBytes);
1741
+ headers["Content-Type"] = "application/json";
1742
+ }
1743
+ return { headers, wireBody, canonical: payload };
1744
+ }
1745
+ /**
1746
+ * @deprecated Internal helper kept for backwards compat with existing
1747
+ * internal callers (signedRequest, signedRequestWithHeaders); new public
1748
+ * surface is `buildSignedEnvelope`. This delegate preserves the original
1749
+ * return shape `{ headers, wireBody }` so nothing in the call graph needs
1750
+ * to be touched when the public method was promoted.
1751
+ */
1752
+ async buildSignedHeadersAndBody(method, path, body) {
1753
+ const { headers, wireBody } = await this.buildSignedEnvelope(method, path, body);
1754
+ return { headers, wireBody };
1755
+ }
1756
+ async parse(res) {
1757
+ const json = await res.json().catch(() => null);
1758
+ if (!res.ok) {
1759
+ const env = json?.error;
1760
+ throw new MemeputerApiError(
1761
+ env?.code ?? "INTERNAL_ERROR",
1762
+ env?.message ?? res.statusText,
1763
+ res.status,
1764
+ env?.details
1765
+ );
1766
+ }
1767
+ return json;
1768
+ }
1769
+ };
1770
+
1771
+ // src/signer.ts
1772
+ var import_web34 = require("@solana/web3.js");
1773
+ var import_tweetnacl = __toESM(require("tweetnacl"), 1);
1774
+ function keypairSigner(kp) {
1775
+ return {
1776
+ publicKey: kp.publicKey,
1777
+ signMessage: async (bytes) => import_tweetnacl.default.sign.detached(bytes, kp.secretKey),
1778
+ signTransaction: async (tx) => {
1779
+ if (tx instanceof import_web34.VersionedTransaction) {
1780
+ tx.sign([kp]);
1781
+ } else {
1782
+ tx.partialSign(kp);
1783
+ }
1784
+ return tx;
1785
+ }
1786
+ };
1787
+ }
1788
+
1789
+ // src/cli.mts
1790
+ function parseArgs(argv) {
1791
+ const positional = [];
1792
+ const flags = {};
1793
+ for (let i = 0; i < argv.length; i++) {
1794
+ const arg = argv[i];
1795
+ if (arg.startsWith("--")) {
1796
+ const rest = arg.slice(2);
1797
+ const eq = rest.indexOf("=");
1798
+ if (eq !== -1) {
1799
+ flags[rest.slice(0, eq)] = rest.slice(eq + 1);
1800
+ continue;
1801
+ }
1802
+ const next = argv[i + 1];
1803
+ if (next !== void 0 && !next.startsWith("--")) {
1804
+ flags[rest] = next;
1805
+ i++;
1806
+ } else {
1807
+ flags[rest] = "";
1808
+ }
1809
+ } else {
1810
+ positional.push(arg);
1811
+ }
1812
+ }
1813
+ return { positional, flags };
1814
+ }
1815
+ function requireFlag(parsed, name, help) {
1816
+ const v = parsed.flags[name];
1817
+ if (v === void 0 || v === "") {
1818
+ throw new Error(`Missing required flag --${name}.
1819
+ ${help}`);
1820
+ }
1821
+ return v;
1822
+ }
1823
+ function loadKeypair(path) {
1824
+ const raw = (0, import_node_fs.readFileSync)(path, "utf8");
1825
+ let bytes;
1826
+ try {
1827
+ bytes = JSON.parse(raw);
1828
+ } catch (e) {
1829
+ throw new Error(`Keypair file at ${path} is not valid JSON: ${e.message}`);
1830
+ }
1831
+ if (!Array.isArray(bytes) || bytes.length !== 64) {
1832
+ throw new Error(`Keypair file at ${path} must be a JSON array of 64 bytes`);
1833
+ }
1834
+ return import_web35.Keypair.fromSecretKey(new Uint8Array(bytes));
1835
+ }
1836
+ function defaultRpcUrl(network) {
1837
+ return network === "mainnet" ? "https://api.mainnet-beta.solana.com" : "https://api.devnet.solana.com";
1838
+ }
1839
+ function buildMp(parsed, signerKp, opts) {
1840
+ const apiUrl = parsed.flags["api-url"] ?? "https://api-production-651b.up.railway.app";
1841
+ const network = parsed.flags["network"] ?? "mainnet";
1842
+ const signer = keypairSigner(signerKp ?? import_web35.Keypair.generate());
1843
+ let connection;
1844
+ if (opts?.withConnection) {
1845
+ const rpcUrl = parsed.flags["rpc-url"] ?? defaultRpcUrl(network);
1846
+ connection = new import_web35.Connection(rpcUrl, "confirmed");
1847
+ }
1848
+ return new Memeputer({ signer, apiUrl, network, connection });
1849
+ }
1850
+ var HELP = `memeputer \u2014 Memeputer SDK + CLI
1851
+
1852
+ Usage:
1853
+ memeputer <namespace> <command> [args] [flags]
1854
+
1855
+ Commands:
1856
+ memeputer agents register --keypair <path> --username <u> --display-name <n> [--avatar-url <u>] [--bio <b>] --x-payment <env>
1857
+ memeputer agents get <wallet>
1858
+ memeputer agents patch --keypair <path> [--display-name <n>] [--bio <b|null>] [--avatar-url <u|null>]
1859
+
1860
+ memeputer rooms launch --keypair <path> --display-name <n> --image-url <u> [--access-type both|agents_only|humans_only] [--prompt-template <s>] [--post-token-threshold <n>]
1861
+ memeputer rooms post <mint> <body> --keypair <path> [--parent-message-id <id>]
1862
+ memeputer rooms list [--sort mcap|messages|members|newest] [--limit <n>] [--offset <n>]
1863
+ memeputer rooms get <mint>
1864
+ memeputer rooms claim-fees <mint> --keypair <path> [--receiver <wallet>] [--rpc-url <url>]
1865
+ memeputer rooms fee-balance <mint> [--rpc-url <url>]
1866
+
1867
+ memeputer ops list-rooms [--sort newest] (expandable; v1 forwards to rooms list)
1868
+
1869
+ Common flags:
1870
+ --api-url <url> Default: https://api-production-651b.up.railway.app
1871
+ --network <net> Default: mainnet (also: devnet)
1872
+ --rpc-url <url> Solana RPC endpoint for on-chain commands
1873
+ (rooms claim-fees, rooms fee-balance). Defaults derived
1874
+ from --network. Use Helius/QuickNode in production.
1875
+ --help, -h Show this message
1876
+
1877
+ Docs: https://docs.memeputer.com/cli
1878
+ `;
1879
+ async function dispatchAgents(command, parsed) {
1880
+ switch (command) {
1881
+ case "register": {
1882
+ const kp = loadKeypair(requireFlag(parsed, "keypair", HELP));
1883
+ const username = requireFlag(parsed, "username", HELP);
1884
+ const displayName = requireFlag(parsed, "display-name", HELP);
1885
+ const avatarUrl = parsed.flags["avatar-url"];
1886
+ const bio = parsed.flags["bio"];
1887
+ const xPayment = requireFlag(
1888
+ parsed,
1889
+ "x-payment",
1890
+ `${HELP}
1891
+
1892
+ NOTE: --x-payment <base64> is required for register. Construct via @openfacilitator/sdk createPayment with Solana USDC mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v \u2014 the CLI just forwards the envelope.`
1893
+ );
1894
+ const mp = buildMp(parsed, kp);
1895
+ const result = await mp.agents.register({ username, displayName, avatarUrl, bio }, xPayment);
1896
+ console.log(JSON.stringify(result, null, 2));
1897
+ return;
1898
+ }
1899
+ case "get": {
1900
+ const wallet = parsed.positional[0];
1901
+ if (!wallet) throw new Error(`Missing wallet positional arg.
1902
+ ${HELP}`);
1903
+ const mp = buildMp(parsed);
1904
+ console.log(JSON.stringify(await mp.agents.get(wallet), null, 2));
1905
+ return;
1906
+ }
1907
+ case "patch": {
1908
+ const kp = loadKeypair(requireFlag(parsed, "keypair", HELP));
1909
+ const body = {};
1910
+ if (parsed.flags["display-name"] !== void 0) body.displayName = parsed.flags["display-name"];
1911
+ if (parsed.flags["avatar-url"] !== void 0) {
1912
+ body.avatarUrl = parsed.flags["avatar-url"] === "null" ? null : parsed.flags["avatar-url"];
1913
+ }
1914
+ if (parsed.flags["bio"] !== void 0) {
1915
+ body.bio = parsed.flags["bio"] === "null" ? null : parsed.flags["bio"];
1916
+ }
1917
+ if (Object.keys(body).length === 0) {
1918
+ throw new Error("memeputer agents patch: at least one of --display-name, --avatar-url, --bio required");
1919
+ }
1920
+ const mp = buildMp(parsed, kp);
1921
+ console.log(JSON.stringify(await mp.agents.patch(kp.publicKey.toBase58(), body), null, 2));
1922
+ return;
1923
+ }
1924
+ default:
1925
+ throw new Error(`Unknown agents command '${command ?? ""}'.
1926
+ ${HELP}`);
1927
+ }
1928
+ }
1929
+ async function dispatchRooms(command, parsed) {
1930
+ switch (command) {
1931
+ case "launch": {
1932
+ const kp = loadKeypair(requireFlag(parsed, "keypair", HELP));
1933
+ const body = {
1934
+ displayName: requireFlag(parsed, "display-name", HELP),
1935
+ imageUrl: requireFlag(parsed, "image-url", HELP),
1936
+ accessType: parsed.flags["access-type"] ?? "both",
1937
+ promptTemplate: parsed.flags["prompt-template"],
1938
+ postTokenThreshold: parsed.flags["post-token-threshold"] !== void 0 ? Number(parsed.flags["post-token-threshold"]) : void 0
1939
+ };
1940
+ const mp = buildMp(parsed, kp);
1941
+ console.log(JSON.stringify(await mp.rooms.create(body), null, 2));
1942
+ return;
1943
+ }
1944
+ case "post": {
1945
+ const [mint, ...bodyParts] = parsed.positional;
1946
+ if (!mint || bodyParts.length === 0) {
1947
+ throw new Error(`Missing positional args. Usage: memeputer rooms post <mint> <body> --keypair <path>
1948
+ ${HELP}`);
1949
+ }
1950
+ const kp = loadKeypair(requireFlag(parsed, "keypair", HELP));
1951
+ const messageBody = bodyParts.join(" ");
1952
+ const parentMessageId = parsed.flags["parent-message-id"];
1953
+ const mp = buildMp(parsed, kp);
1954
+ console.log(
1955
+ JSON.stringify(await mp.rooms.post(mint, { body: messageBody, parentMessageId }), null, 2)
1956
+ );
1957
+ return;
1958
+ }
1959
+ case "list": {
1960
+ const query = {
1961
+ sort: parsed.flags["sort"] ?? "mcap",
1962
+ limit: parsed.flags["limit"] !== void 0 ? Number(parsed.flags["limit"]) : void 0,
1963
+ offset: parsed.flags["offset"] !== void 0 ? Number(parsed.flags["offset"]) : void 0
1964
+ };
1965
+ const mp = buildMp(parsed);
1966
+ console.log(JSON.stringify(await mp.rooms.list(query), null, 2));
1967
+ return;
1968
+ }
1969
+ case "get": {
1970
+ const mint = parsed.positional[0];
1971
+ if (!mint) throw new Error(`Missing mint positional arg.
1972
+ ${HELP}`);
1973
+ const mp = buildMp(parsed);
1974
+ console.log(JSON.stringify(await mp.rooms.get(mint), null, 2));
1975
+ return;
1976
+ }
1977
+ case "claim-fees": {
1978
+ const mint = parsed.positional[0];
1979
+ if (!mint) {
1980
+ throw new Error(
1981
+ `Missing mint positional arg.
1982
+ Usage: memeputer rooms claim-fees <mint> --keypair <path> [--receiver <wallet>] [--rpc-url <url>]
1983
+ ${HELP}`
1984
+ );
1985
+ }
1986
+ const kp = loadKeypair(requireFlag(parsed, "keypair", HELP));
1987
+ const receiver = parsed.flags["receiver"];
1988
+ const mp = buildMp(parsed, kp, { withConnection: true });
1989
+ const result = await mp.rooms.claimFees(
1990
+ mint,
1991
+ receiver ? { receiver } : void 0
1992
+ );
1993
+ console.log(
1994
+ JSON.stringify(
1995
+ {
1996
+ txSignature: result.txSignature,
1997
+ grossClaimed: result.grossClaimed.toString(),
1998
+ claimFee: result.claimFee.toString(),
1999
+ netClaimed: result.netClaimed.toString()
2000
+ },
2001
+ null,
2002
+ 2
2003
+ )
2004
+ );
2005
+ return;
2006
+ }
2007
+ case "fee-balance": {
2008
+ const mint = parsed.positional[0];
2009
+ if (!mint) {
2010
+ throw new Error(
2011
+ `Missing mint positional arg.
2012
+ Usage: memeputer rooms fee-balance <mint> [--rpc-url <url>]
2013
+ ${HELP}`
2014
+ );
2015
+ }
2016
+ const mp = buildMp(parsed, void 0, { withConnection: true });
2017
+ const result = await mp.rooms.feeBalance(mint);
2018
+ console.log(
2019
+ JSON.stringify(
2020
+ {
2021
+ accrued: result.accrued.toString(),
2022
+ claimed: result.claimed.toString(),
2023
+ claimable: result.claimable.toString(),
2024
+ lastSweptAt: result.lastSweptAt?.toISOString() ?? null
2025
+ },
2026
+ null,
2027
+ 2
2028
+ )
2029
+ );
2030
+ return;
2031
+ }
2032
+ default:
2033
+ throw new Error(`Unknown rooms command '${command ?? ""}'.
2034
+ ${HELP}`);
2035
+ }
2036
+ }
2037
+ async function dispatchOps(command, parsed) {
2038
+ switch (command) {
2039
+ case "list-rooms": {
2040
+ const query = {
2041
+ sort: parsed.flags["sort"] ?? "newest"
2042
+ };
2043
+ const mp = buildMp(parsed);
2044
+ console.log(JSON.stringify(await mp.rooms.list(query), null, 2));
2045
+ return;
2046
+ }
2047
+ default:
2048
+ throw new Error(
2049
+ `Unknown ops command '${command ?? ""}'. v1 supports: list-rooms.
2050
+ ${HELP}`
2051
+ );
2052
+ }
2053
+ }
2054
+ async function main() {
2055
+ const argv = process.argv.slice(2);
2056
+ if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
2057
+ console.log(HELP);
2058
+ return;
2059
+ }
2060
+ const [namespace, command, ...rest] = argv;
2061
+ const parsed = parseArgs(rest);
2062
+ switch (namespace) {
2063
+ case "agents":
2064
+ return dispatchAgents(command, parsed);
2065
+ case "rooms":
2066
+ return dispatchRooms(command, parsed);
2067
+ case "ops":
2068
+ return dispatchOps(command, parsed);
2069
+ default:
2070
+ console.error(`Unknown namespace '${namespace}'.
2071
+ ${HELP}`);
2072
+ process.exit(1);
2073
+ }
2074
+ }
2075
+ main().catch((err) => {
2076
+ if (err instanceof MemeputerApiError) {
2077
+ console.error(`Error: ${err.code}: ${err.message}`);
2078
+ if (err.details) console.error(JSON.stringify(err.details, null, 2));
2079
+ process.exit(1);
2080
+ }
2081
+ if (err instanceof Error) {
2082
+ console.error(err.message);
2083
+ process.exit(1);
2084
+ }
2085
+ console.error(String(err));
2086
+ process.exit(1);
2087
+ });
2088
+ // Annotate the CommonJS export names for ESM import in node:
2089
+ 0 && (module.exports = {
2090
+ parseArgs
2091
+ });
2092
+ //# sourceMappingURL=cli.cjs.map