@swarm-tips/client 0.0.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -1
- package/SBOM.spdx.json +1558 -0
- package/dist/cli/swarm-tx.d.ts +2 -0
- package/dist/cli/swarm-tx.js +46 -0
- package/dist/cli/vow-verify.d.ts +2 -0
- package/dist/cli/vow-verify.js +69 -0
- package/dist/contracts/generated/coordination_game.d.ts +6107 -0
- package/dist/contracts/generated/coordination_game.js +1 -0
- package/dist/contracts/generated/shillbot.d.ts +4209 -0
- package/dist/contracts/generated/shillbot.js +1 -0
- package/dist/contracts/idl/coordination_game.json +6101 -0
- package/dist/contracts/idl/shillbot.json +4203 -0
- package/dist/contracts/index.d.ts +33 -0
- package/dist/contracts/index.js +84 -0
- package/dist/coordination-game/battery.d.ts +40 -0
- package/dist/coordination-game/battery.js +82 -0
- package/dist/coordination-game/index.d.ts +4 -0
- package/dist/coordination-game/index.js +3 -0
- package/dist/coordination-game/outcome-oracle.d.ts +53 -0
- package/dist/coordination-game/outcome-oracle.js +136 -0
- package/dist/evm/index.d.ts +3 -0
- package/dist/evm/index.js +6 -0
- package/dist/evm/sign.d.ts +12 -0
- package/dist/evm/sign.js +23 -0
- package/dist/evm/testing.d.ts +21 -0
- package/dist/evm/testing.js +34 -0
- package/dist/evm/tx.d.ts +27 -0
- package/dist/evm/tx.js +18 -0
- package/dist/evm/wallet.d.ts +58 -0
- package/dist/evm/wallet.js +182 -0
- package/dist/inbox/client.d.ts +189 -0
- package/dist/inbox/client.js +334 -0
- package/dist/inbox/index.d.ts +2 -0
- package/dist/inbox/index.js +4 -0
- package/dist/inbox/solana.d.ts +11 -0
- package/dist/inbox/solana.js +13 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/shillbot/index.d.ts +115 -0
- package/dist/shillbot/index.js +327 -0
- package/dist/shillbot/payout-oracle.d.ts +63 -0
- package/dist/shillbot/payout-oracle.js +172 -0
- package/dist/shillbot/switchboard.d.ts +27 -0
- package/dist/shillbot/switchboard.js +205 -0
- package/dist/vow/decoders/extension.d.ts +62 -0
- package/dist/vow/decoders/extension.js +48 -0
- package/dist/vow/decoders/shillbot.d.ts +59 -0
- package/dist/vow/decoders/shillbot.js +130 -0
- package/dist/vow/discriminator.d.ts +12 -0
- package/dist/vow/discriminator.js +25 -0
- package/dist/vow/index.d.ts +7 -0
- package/dist/vow/index.js +5 -0
- package/dist/vow/schema.d.ts +14 -0
- package/dist/vow/schema.js +132 -0
- package/dist/vow/types.d.ts +107 -0
- package/dist/vow/types.js +1 -0
- package/dist/vow/verify.d.ts +23 -0
- package/dist/vow/verify.js +145 -0
- package/package.json +99 -6
- package/index.js +0 -1
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { createConfig } from "wagmi";
|
|
2
|
+
import { injected, walletConnect } from "wagmi/connectors";
|
|
3
|
+
import { createWalletClient, fallback, http as viemHttp, } from "viem";
|
|
4
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
5
|
+
import { e2eTestModeEnabled } from "./testing.js";
|
|
6
|
+
/**
|
|
7
|
+
* Resilient transport: a viem `fallback` across the given RPC URLs (fails over
|
|
8
|
+
* to the next on a transient RPC error), or the chain default when none are
|
|
9
|
+
* given. Public testnet RPCs (e.g. sepolia.base.org) intermittently reject
|
|
10
|
+
* `eth_sendRawTransaction` with -32602; failover keeps commit/reveal from
|
|
11
|
+
* dying on one bad endpoint.
|
|
12
|
+
*/
|
|
13
|
+
function buildTransport(rpcUrls) {
|
|
14
|
+
if (rpcUrls && rpcUrls.length > 0) {
|
|
15
|
+
return fallback(rpcUrls.map((url) => viemHttp(url)));
|
|
16
|
+
}
|
|
17
|
+
return viemHttp();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Read an E2E test private key from a `?<param>=<0x-hex>` query param, if
|
|
21
|
+
* present (default param `evmTestWallet`). Mirrors the Solana `?testWallet=`
|
|
22
|
+
* pattern so Playwright can drive the EVM path headlessly. Returns null off the
|
|
23
|
+
* happy path (no window, missing/malformed param) AND whenever the page isn't
|
|
24
|
+
* in E2E test mode — so a crafted prod link can't activate the connector.
|
|
25
|
+
*/
|
|
26
|
+
export function getEvmTestWalletKey(param = "evmTestWallet") {
|
|
27
|
+
if (typeof window === "undefined")
|
|
28
|
+
return null;
|
|
29
|
+
if (!e2eTestModeEnabled())
|
|
30
|
+
return null;
|
|
31
|
+
const raw = new URLSearchParams(window.location.search).get(param);
|
|
32
|
+
if (!raw)
|
|
33
|
+
return null;
|
|
34
|
+
const hex = raw.startsWith("0x") ? raw : `0x${raw}`;
|
|
35
|
+
// A secp256k1 private key is 32 bytes → 64 hex chars after the 0x prefix.
|
|
36
|
+
if (!/^0x[0-9a-fA-F]{64}$/.test(hex)) {
|
|
37
|
+
console.error("[evm-kit] ignoring malformed test wallet param");
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return hex;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Read an E2E RPC override from `?evmRpc=<https-url>`. Lets Playwright point the
|
|
44
|
+
* page at a reliable provider (e.g. QuickNode) WITHOUT baking the key into the
|
|
45
|
+
* public bundle. Caller gates this to test mode (test wallet present) so a
|
|
46
|
+
* crafted link can't redirect a real user's wallet to a hostile RPC. Only https
|
|
47
|
+
* URLs are accepted. Also gated on E2E test mode (defense in depth — its only
|
|
48
|
+
* caller already requires a test wallet, which is itself test-mode-gated).
|
|
49
|
+
*/
|
|
50
|
+
export function getEvmRpcOverride(param = "evmRpc") {
|
|
51
|
+
if (typeof window === "undefined")
|
|
52
|
+
return null;
|
|
53
|
+
if (!e2eTestModeEnabled())
|
|
54
|
+
return null;
|
|
55
|
+
const raw = new URLSearchParams(window.location.search).get(param);
|
|
56
|
+
if (!raw)
|
|
57
|
+
return null;
|
|
58
|
+
try {
|
|
59
|
+
const url = new URL(raw);
|
|
60
|
+
if (url.protocol !== "https:") {
|
|
61
|
+
// Log for the same reason the malformed branch below does: a rejected
|
|
62
|
+
// override is either a hostile downgrade attempt or an e2e run pointed
|
|
63
|
+
// at http://, and silently returning null leaves no trace of either.
|
|
64
|
+
console.error(`[evm-kit] ignoring non-https evmRpc param (protocol ${url.protocol})`);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return url.toString();
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
console.error("[evm-kit] ignoring malformed evmRpc param");
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A wagmi connector backed by a raw private key via a viem wallet client.
|
|
76
|
+
* Auto-connects and signs txs + messages without a browser extension, so
|
|
77
|
+
* Playwright can drive an EVM flow headlessly. Test-only — gated behind the
|
|
78
|
+
* test-wallet query param at config-build time.
|
|
79
|
+
*/
|
|
80
|
+
function evmTestConnector(key, chain, transport) {
|
|
81
|
+
const account = privateKeyToAccount(key);
|
|
82
|
+
const client = createWalletClient({ account, chain, transport });
|
|
83
|
+
// An EIP-1193 shim routing the wallet methods wagmi calls to the viem client;
|
|
84
|
+
// everything else falls through to the chain transport.
|
|
85
|
+
const request = async (args) => {
|
|
86
|
+
const params = args.params ?? [];
|
|
87
|
+
switch (args.method) {
|
|
88
|
+
case "eth_accounts":
|
|
89
|
+
case "eth_requestAccounts":
|
|
90
|
+
return [account.address];
|
|
91
|
+
case "eth_chainId":
|
|
92
|
+
return chain.id;
|
|
93
|
+
case "personal_sign": {
|
|
94
|
+
const [message] = params;
|
|
95
|
+
return client.signMessage({ message: { raw: message } });
|
|
96
|
+
}
|
|
97
|
+
case "eth_sendTransaction": {
|
|
98
|
+
// wagmi hands EIP-1193 params in RPC-hex form (gas/value/nonce as 0x
|
|
99
|
+
// strings). Passing those straight into viem's TYPED sendTransaction
|
|
100
|
+
// produces a signed tx that strict RPCs reject ("failed to decode signed
|
|
101
|
+
// transaction"). Take only the essential fields and let viem re-prepare
|
|
102
|
+
// gas/fees/nonce itself — the path a plain viem walletClient uses.
|
|
103
|
+
const [tx] = params;
|
|
104
|
+
return client.sendTransaction({
|
|
105
|
+
to: tx.to,
|
|
106
|
+
data: tx.data ?? tx.input,
|
|
107
|
+
value: tx.value ? BigInt(tx.value) : undefined,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
default:
|
|
111
|
+
return client.request({
|
|
112
|
+
method: args.method,
|
|
113
|
+
params,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
// wagmi's injected connector subscribes to EIP-1193 events; provide no-op
|
|
118
|
+
// on/removeListener so connect() doesn't crash on the private-key provider.
|
|
119
|
+
return injected({
|
|
120
|
+
target: {
|
|
121
|
+
id: "evmTestWallet",
|
|
122
|
+
name: "EVM Test Wallet",
|
|
123
|
+
provider: { request, on: () => { }, removeListener: () => { } },
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Choose the connector set for a config. Pure, so the precedence rule is
|
|
129
|
+
* unit-testable: the E2E test connector always wins (a live sweep must never be
|
|
130
|
+
* hijacked by a real wallet), then app-supplied connectors, then the built-in
|
|
131
|
+
* injected/WalletConnect pair.
|
|
132
|
+
*/
|
|
133
|
+
export function selectConnectors(testConnector, appConnectors, builtIn) {
|
|
134
|
+
if (testConnector)
|
|
135
|
+
return [testConnector];
|
|
136
|
+
if (appConnectors && appConnectors.length > 0)
|
|
137
|
+
return appConnectors;
|
|
138
|
+
return builtIn;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Build a wagmi config for one or several EVM chains. Multi-chain lets the
|
|
142
|
+
* wallet switch chains (e.g. Base Sepolia ↔ Ethereum Sepolia) so the same site
|
|
143
|
+
* plays each — each chain gets its own resilient transport. The `injected`
|
|
144
|
+
* connector serves humans; the private-key test connector serves E2E when the
|
|
145
|
+
* test-wallet query param is present (bound to the `?evmChain=<caip2>` chain,
|
|
146
|
+
* or the first listed chain).
|
|
147
|
+
*/
|
|
148
|
+
export function buildWagmiConfig(chains, opts = {}) {
|
|
149
|
+
const { walletConnectProjectId } = opts;
|
|
150
|
+
const list = chains;
|
|
151
|
+
const key = getEvmTestWalletKey();
|
|
152
|
+
// A `?evmRpc=` override is honored ONLY in test mode (test wallet present), so
|
|
153
|
+
// a crafted link can't point a real user's wallet at a hostile RPC. When set,
|
|
154
|
+
// use that single reliable endpoint (no fallback) — fail-fast-or-succeed.
|
|
155
|
+
const override = key ? getEvmRpcOverride() : null;
|
|
156
|
+
const transports = Object.fromEntries(list.map(({ chain, rpcUrls: chainRpcs }) => [
|
|
157
|
+
chain.id,
|
|
158
|
+
override ? viemHttp(override) : buildTransport(chainRpcs),
|
|
159
|
+
]));
|
|
160
|
+
// Test connector binds to one chain: the `?evmChain=<eip155:id>` selection if
|
|
161
|
+
// it matches a listed chain, else the first. `useSwitchChain` in the game hook
|
|
162
|
+
// moves the wallet to the chain the player picked before any staking tx.
|
|
163
|
+
const testChain = pickTestChain(list) ?? list[0].chain;
|
|
164
|
+
const connectors = selectConnectors(key ? evmTestConnector(key, testChain, transports[testChain.id]) : null, opts.connectors, walletConnectProjectId
|
|
165
|
+
? [injected(), walletConnect({ projectId: walletConnectProjectId })]
|
|
166
|
+
: [injected()]);
|
|
167
|
+
const chainTuple = list.map((c) => c.chain);
|
|
168
|
+
return createConfig({ chains: chainTuple, connectors, transports });
|
|
169
|
+
}
|
|
170
|
+
/** Which listed chain the E2E test wallet should bind to: the `?evmChain=`
|
|
171
|
+
* eip155 selection when it matches, else null (caller defaults to first). */
|
|
172
|
+
function pickTestChain(list) {
|
|
173
|
+
if (typeof window === "undefined")
|
|
174
|
+
return null;
|
|
175
|
+
const raw = new URLSearchParams(window.location.search).get("evmChain");
|
|
176
|
+
if (!raw)
|
|
177
|
+
return null;
|
|
178
|
+
const id = raw.startsWith("eip155:")
|
|
179
|
+
? Number(raw.slice("eip155:".length))
|
|
180
|
+
: Number(raw);
|
|
181
|
+
return list.find((c) => c.chain.id === id)?.chain ?? null;
|
|
182
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REST client for the agent-inbox twins on mcp-server
|
|
3
|
+
* (`/internal/inbox/*` on mcp.swarm.tips). The twins share the MCP inbox's
|
|
4
|
+
* storage layer, so shapes mirror the `agent_*` MCP tools exactly
|
|
5
|
+
* (`from_wallet` is CAIP-10, `msg_id` doubles as the paging/ack cursor).
|
|
6
|
+
*
|
|
7
|
+
* Session + request flow (mirrors scripts/seed-inbox.ts):
|
|
8
|
+
*
|
|
9
|
+
* createSession(wallet, signNonce)
|
|
10
|
+
* │
|
|
11
|
+
* ├─ localStorage hit? ──────────────► use cached {session_id, tier}
|
|
12
|
+
* │ │
|
|
13
|
+
* └─ POST /session {wallet} → {nonce} │
|
|
14
|
+
* signNonce(nonce) (ONE wallet popup) │
|
|
15
|
+
* POST /session {wallet,nonce,sig} │
|
|
16
|
+
* → {session_id, tier} → cache ────┤
|
|
17
|
+
* ▼
|
|
18
|
+
* getMessages / send / ack ── X-Inbox-Session header
|
|
19
|
+
* │
|
|
20
|
+
* └─ 401? → drop cache → re-mint (popup) → retry ONCE → else throw
|
|
21
|
+
*
|
|
22
|
+
* The `signNonce` callback is chain-agnostic: it receives the raw nonce
|
|
23
|
+
* string and must return the wire-format signature string. Solana =
|
|
24
|
+
* bs58(ed25519 detached over the nonce UTF-8 bytes) — use `solanaNonceSigner`
|
|
25
|
+
* from this package; EVM = the `personal_sign` hex string as-is.
|
|
26
|
+
*/
|
|
27
|
+
export type NonceSigner = (nonce: string) => Promise<string>;
|
|
28
|
+
export interface InboxSession {
|
|
29
|
+
session_id: string;
|
|
30
|
+
tier: string;
|
|
31
|
+
}
|
|
32
|
+
export interface InboxMessage {
|
|
33
|
+
/** Message id; also the paging + ack cursor (lexically ordered). */
|
|
34
|
+
msg_id: string;
|
|
35
|
+
/** CAIP-10 sender, e.g. "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:BASE58". */
|
|
36
|
+
from_wallet: string;
|
|
37
|
+
thread_id: string;
|
|
38
|
+
intent: string | null;
|
|
39
|
+
body: string;
|
|
40
|
+
/** RFC 3339 send timestamp. */
|
|
41
|
+
sent_at: string;
|
|
42
|
+
/** True for org seed-wallet traffic (server-tagged). */
|
|
43
|
+
seed?: boolean;
|
|
44
|
+
/** Present only under `include_sent` reads: "sent" = a mirror copy the
|
|
45
|
+
* connected wallet wrote, "received" = an inbound message. Absent on
|
|
46
|
+
* inbound-only reads (treat as "received"). */
|
|
47
|
+
direction?: "received" | "sent";
|
|
48
|
+
}
|
|
49
|
+
export interface MessagePage {
|
|
50
|
+
messages: InboxMessage[];
|
|
51
|
+
next_cursor: string | null;
|
|
52
|
+
}
|
|
53
|
+
/** One post on a public topic board (open-challenge / subcontract). Bodies
|
|
54
|
+
* are third-party text — render as text, never HTML. */
|
|
55
|
+
export interface TopicPost {
|
|
56
|
+
post_id: string;
|
|
57
|
+
topic_id: string;
|
|
58
|
+
/** CAIP-10 author id. */
|
|
59
|
+
author_wallet: string;
|
|
60
|
+
body: string;
|
|
61
|
+
/** post_id this replies to, or null for a root post. */
|
|
62
|
+
reply_to: string | null;
|
|
63
|
+
intent: string | null;
|
|
64
|
+
/** Unsigned-tx-flow / game / task id this post points at, or null. Drives
|
|
65
|
+
* the join/claim CTA. */
|
|
66
|
+
ref_id: string | null;
|
|
67
|
+
reported_count: number;
|
|
68
|
+
/** RFC 3339 creation timestamp. */
|
|
69
|
+
created_at: string;
|
|
70
|
+
seed?: boolean;
|
|
71
|
+
}
|
|
72
|
+
export interface TopicPage {
|
|
73
|
+
topic_id: string;
|
|
74
|
+
posts: TopicPost[];
|
|
75
|
+
next_cursor: string | null;
|
|
76
|
+
/** Server-side count of posts dropped by the auto-hide moderation flag. */
|
|
77
|
+
filtered_hidden?: number;
|
|
78
|
+
/** Posts dropped for author trust below the requested `min_trust`. */
|
|
79
|
+
filtered_below_min_trust?: number;
|
|
80
|
+
}
|
|
81
|
+
export interface PublishReceipt {
|
|
82
|
+
published: boolean;
|
|
83
|
+
post_id: string;
|
|
84
|
+
posts_remaining_today?: number;
|
|
85
|
+
}
|
|
86
|
+
export interface ReportReceipt {
|
|
87
|
+
reported: boolean;
|
|
88
|
+
reported_count: number;
|
|
89
|
+
hidden: boolean;
|
|
90
|
+
already_reported: boolean;
|
|
91
|
+
}
|
|
92
|
+
/** Known public topic boards. `open-challenge` = game matchmaking,
|
|
93
|
+
* `subcontract` = Shillbot task handoff. */
|
|
94
|
+
export type TopicId = "open-challenge" | "subcontract";
|
|
95
|
+
export interface SendReceipt {
|
|
96
|
+
msg_id: string;
|
|
97
|
+
thread_id: string;
|
|
98
|
+
sends_remaining_today?: number;
|
|
99
|
+
}
|
|
100
|
+
/** Non-2xx responses and client-side boundary rejections. `status` is the
|
|
101
|
+
* HTTP status, or 0 for errors raised before any request was made. */
|
|
102
|
+
export declare class InboxApiError extends Error {
|
|
103
|
+
readonly status: number;
|
|
104
|
+
constructor(status: number, message: string);
|
|
105
|
+
}
|
|
106
|
+
interface StorageLike {
|
|
107
|
+
getItem(key: string): string | null;
|
|
108
|
+
setItem(key: string, value: string): void;
|
|
109
|
+
removeItem(key: string): void;
|
|
110
|
+
}
|
|
111
|
+
export interface InboxClientOpts {
|
|
112
|
+
/** REST base URL; default https://mcp.swarm.tips. Frontends thread their
|
|
113
|
+
* VITE_ env override through this (same pattern as API_BASE_URL). */
|
|
114
|
+
baseUrl?: string;
|
|
115
|
+
/** Session cache. Defaults to window.localStorage; pass null to disable. */
|
|
116
|
+
storage?: StorageLike | null;
|
|
117
|
+
/** fetch implementation — tests inject a mock. */
|
|
118
|
+
fetchFn?: typeof fetch;
|
|
119
|
+
}
|
|
120
|
+
export declare class InboxClient {
|
|
121
|
+
private readonly baseUrl;
|
|
122
|
+
private readonly storage;
|
|
123
|
+
private readonly fetchFn;
|
|
124
|
+
private session;
|
|
125
|
+
private wallet;
|
|
126
|
+
private signNonce;
|
|
127
|
+
constructor(opts?: InboxClientOpts);
|
|
128
|
+
/** True when a cached session exists for `wallet` — opening the inbox will
|
|
129
|
+
* not prompt a wallet signature. */
|
|
130
|
+
hasCachedSession(wallet: string): boolean;
|
|
131
|
+
/** Mint (or restore from cache) the inbox session for `wallet`. Signing
|
|
132
|
+
* happens at most once per call — zero times on a cache hit. The wallet +
|
|
133
|
+
* signer are retained for transparent re-mint when a request 401s. */
|
|
134
|
+
createSession(wallet: string, signNonce: NonceSigner): Promise<InboxSession>;
|
|
135
|
+
/** Drop the in-memory and cached session (e.g. on wallet disconnect). */
|
|
136
|
+
clearSession(wallet?: string): void;
|
|
137
|
+
/** Read a page of messages, newest first. Omit `threadId` for the whole
|
|
138
|
+
* mailbox; pass the previous page's `next_cursor` to page older. */
|
|
139
|
+
getMessages(opts?: {
|
|
140
|
+
threadId?: string;
|
|
141
|
+
cursor?: string;
|
|
142
|
+
limit?: number;
|
|
143
|
+
/** Merge the sender's own mirror copies into the page so a thread read
|
|
144
|
+
* shows both sides. Maps to `include_sent=true`. Sent messages carry
|
|
145
|
+
* `direction:"sent"`. */
|
|
146
|
+
includeSent?: boolean;
|
|
147
|
+
}): Promise<MessagePage>;
|
|
148
|
+
/** Read one thread (e.g. `task:{task_id}` / `game:{id}`), newest first. Pass
|
|
149
|
+
* `includeSent` to fold in the connected wallet's own replies (full
|
|
150
|
+
* two-way conversation). */
|
|
151
|
+
getThread(threadId: string, opts?: {
|
|
152
|
+
cursor?: string;
|
|
153
|
+
limit?: number;
|
|
154
|
+
includeSent?: boolean;
|
|
155
|
+
}): Promise<MessagePage>;
|
|
156
|
+
/** Send a message. Rejects empty / oversize bodies client-side; quota and
|
|
157
|
+
* mute rejections surface as InboxApiError with the server's message. */
|
|
158
|
+
send(toWallet: string, body: string, threadId?: string, intent?: string): Promise<SendReceipt>;
|
|
159
|
+
/** Advance the read watermark up to a msg_id cursor. NOTE: the watermark is
|
|
160
|
+
* mailbox-global — acking a cursor also acks every older message. */
|
|
161
|
+
ack(upToCursor: string): Promise<void>;
|
|
162
|
+
/** Read a page of a public topic board, newest first. OPEN — no session
|
|
163
|
+
* needed. `minTrust` drops posts whose author trust is below it
|
|
164
|
+
* server-side. Hidden (auto-moderated) posts are always dropped. */
|
|
165
|
+
readTopic(topicId: TopicId, opts?: {
|
|
166
|
+
cursor?: string;
|
|
167
|
+
limit?: number;
|
|
168
|
+
minTrust?: number;
|
|
169
|
+
}): Promise<TopicPage>;
|
|
170
|
+
/** Publish a post to a topic board. Session-gated (same X-Inbox-Session as
|
|
171
|
+
* inbox send). Rejects empty / oversize bodies client-side; quota
|
|
172
|
+
* rejections surface as InboxApiError with the server's message. */
|
|
173
|
+
publishPost(topicId: TopicId, body: string, opts?: {
|
|
174
|
+
replyTo?: string;
|
|
175
|
+
intent?: string;
|
|
176
|
+
refId?: string;
|
|
177
|
+
}): Promise<PublishReceipt>;
|
|
178
|
+
/** Report a post for moderation. Session-gated. Idempotent per reporter —
|
|
179
|
+
* a repeat report returns `already_reported: true`. */
|
|
180
|
+
reportPost(topicId: TopicId, postId: string): Promise<ReportReceipt>;
|
|
181
|
+
private getUnauthed;
|
|
182
|
+
private mintSession;
|
|
183
|
+
private postUnauthed;
|
|
184
|
+
private authed;
|
|
185
|
+
private doFetch;
|
|
186
|
+
private readCachedSession;
|
|
187
|
+
private writeCachedSession;
|
|
188
|
+
}
|
|
189
|
+
export {};
|