@swarm-tips/client 0.2.3 → 0.2.5

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/SBOM.spdx.json CHANGED
@@ -2,10 +2,10 @@
2
2
  "spdxVersion": "SPDX-2.3",
3
3
  "dataLicense": "CC0-1.0",
4
4
  "SPDXID": "SPDXRef-DOCUMENT",
5
- "name": "@swarm-tips/client-0.2.3",
6
- "documentNamespace": "https://swarm.tips/sbom/client/0.2.3",
5
+ "name": "@swarm-tips/client-0.2.5",
6
+ "documentNamespace": "https://swarm.tips/sbom/client/0.2.5",
7
7
  "creationInfo": {
8
- "created": "2026-09-09T05:14:05.500Z",
8
+ "created": "2026-09-10T05:02:05.529Z",
9
9
  "creators": [
10
10
  "Tool: sdk/client/scripts/generate-sbom.mjs",
11
11
  "Organization: Swarm Tips"
@@ -15,8 +15,8 @@
15
15
  {
16
16
  "name": "@swarm-tips/client",
17
17
  "SPDXID": "SPDXRef-Package",
18
- "versionInfo": "0.2.3",
19
- "downloadLocation": "https://www.npmjs.com/package/@swarm-tips/client/v/0.2.3",
18
+ "versionInfo": "0.2.5",
19
+ "downloadLocation": "https://www.npmjs.com/package/@swarm-tips/client/v/0.2.5",
20
20
  "filesAnalyzed": false,
21
21
  "licenseConcluded": "MIT",
22
22
  "licenseDeclared": "MIT"
package/dist/http.js CHANGED
@@ -27,9 +27,14 @@ export class HttpClient {
27
27
  const operation = `${init.method ?? "GET"} ${path}`;
28
28
  const token = init.token ?? this.getToken?.();
29
29
  const headers = {
30
- "Content-Type": "application/json",
31
30
  ...init.headers,
32
31
  };
32
+ // A Content-Type header on a bodyless GET turns an otherwise simple
33
+ // cross-origin read into a CORS preflight. Public browser reads should not
34
+ // require OPTIONS for a body that does not exist.
35
+ if (init.body !== undefined && !headers["Content-Type"]) {
36
+ headers["Content-Type"] = "application/json";
37
+ }
33
38
  if (token)
34
39
  headers.Authorization = `Bearer ${token}`;
35
40
  let response;
@@ -46,6 +46,69 @@ export interface InboxMessage {
46
46
  * inbound-only reads (treat as "received"). */
47
47
  direction?: "received" | "sent";
48
48
  }
49
+ export interface MessageReference {
50
+ msg_id: string;
51
+ direction?: "received" | "sent";
52
+ }
53
+ export interface MessageEnvelope {
54
+ msg_id: string;
55
+ direction: "received" | "sent";
56
+ from_wallet: string;
57
+ sent_at: string;
58
+ /** Inbox messages do not expire. */
59
+ expires_at: null;
60
+ body_bytes: number;
61
+ intent: "game_invite" | "task_offer" | "task_clarification" | null;
62
+ /** Stable mailbox-local grouping label, not a threadId filter. */
63
+ thread_ref: string;
64
+ acknowledged: boolean | null;
65
+ /** Untrusted excerpt; absent unless explicitly requested. */
66
+ preview?: string;
67
+ preview_truncated?: boolean;
68
+ }
69
+ export interface InboxListOptions {
70
+ cursor?: string;
71
+ limit?: number;
72
+ threadId?: string;
73
+ minTrust?: number;
74
+ status?: "pending" | "all";
75
+ includeSent?: boolean;
76
+ preview?: boolean;
77
+ }
78
+ export interface InboxListPage {
79
+ messages: MessageEnvelope[];
80
+ count: number;
81
+ /** Continue even when messages is empty. */
82
+ next_cursor: string | null;
83
+ filtered_acknowledged: number;
84
+ filtered_muted: number;
85
+ filtered_below_min_trust: number;
86
+ }
87
+ export type OpenMessageResult = {
88
+ msg_id: string;
89
+ direction: "received" | "sent";
90
+ status: "opened";
91
+ message: InboxMessage & Omit<MessageEnvelope, "acknowledged">;
92
+ } | {
93
+ msg_id: string;
94
+ direction: "received" | "sent";
95
+ status: "unavailable";
96
+ } | {
97
+ msg_id: string;
98
+ direction: "received" | "sent";
99
+ status: "error";
100
+ error: "storage_error";
101
+ };
102
+ export type AckMessageResult = {
103
+ msg_id: string;
104
+ direction: "received";
105
+ status: "acknowledged" | "unavailable";
106
+ } | {
107
+ msg_id: string;
108
+ direction: "received";
109
+ status: "error";
110
+ error: "storage_error";
111
+ };
49
112
  export interface MessagePage {
50
113
  messages: InboxMessage[];
51
114
  next_cursor: string | null;
@@ -145,6 +208,16 @@ export declare class InboxClient {
145
208
  * `direction:"sent"`. */
146
209
  includeSent?: boolean;
147
210
  }): Promise<MessagePage>;
211
+ /** Metadata first; listing never acknowledges. Old bulk status is independent. */
212
+ listMessages(opts?: InboxListOptions): Promise<InboxListPage>;
213
+ /** Load only selected bodies. This never acknowledges or follows message links. */
214
+ openMessages(messages: MessageReference[]): Promise<{
215
+ results: OpenMessageResult[];
216
+ }>;
217
+ /** Mark only these received IDs handled/dismissed. Per-ID errors can be retried. */
218
+ ackMessageIds(messageIds: string[]): Promise<{
219
+ results: AckMessageResult[];
220
+ }>;
148
221
  /** Read one thread (e.g. `task:{task_id}` / `game:{id}`), newest first. Pass
149
222
  * `includeSent` to fold in the connected wallet's own replies (full
150
223
  * two-way conversation). */
@@ -145,6 +145,32 @@ export class InboxClient {
145
145
  next_cursor: page.next_cursor ?? null,
146
146
  };
147
147
  }
148
+ /** Metadata first; listing never acknowledges. Old bulk status is independent. */
149
+ async listMessages(opts = {}) {
150
+ if (opts.includeSent && opts.status !== "all")
151
+ throw new InboxApiError(0, "includeSent requires status=all");
152
+ if (opts.limit !== undefined && (!Number.isInteger(opts.limit) || opts.limit < 1 || opts.limit > 50))
153
+ throw new InboxApiError(0, "limit must be in 1..50");
154
+ if (opts.minTrust !== undefined && (!Number.isFinite(opts.minTrust) || opts.minTrust < 0 || opts.minTrust > 1))
155
+ throw new InboxApiError(0, "minTrust must be in [0,1]");
156
+ const query = new URLSearchParams();
157
+ for (const [key, value] of Object.entries({ cursor: opts.cursor, limit: opts.limit, thread_id: opts.threadId, min_trust: opts.minTrust, status: opts.status, include_sent: opts.includeSent, preview: opts.preview })) {
158
+ if (value !== undefined)
159
+ query.set(key, String(value));
160
+ }
161
+ const suffix = query.toString();
162
+ return this.authed(`/internal/inbox/list${suffix ? `?${suffix}` : ""}`, { method: "GET" });
163
+ }
164
+ /** Load only selected bodies. This never acknowledges or follows message links. */
165
+ async openMessages(messages) {
166
+ validateSelectedIds(messages.map(m => m.msg_id));
167
+ return this.authed("/internal/inbox/open", { method: "POST", body: JSON.stringify({ messages }) });
168
+ }
169
+ /** Mark only these received IDs handled/dismissed. Per-ID errors can be retried. */
170
+ async ackMessageIds(messageIds) {
171
+ validateSelectedIds(messageIds);
172
+ return this.authed("/internal/inbox/ack-ids", { method: "POST", body: JSON.stringify({ message_ids: messageIds }) });
173
+ }
148
174
  /** Read one thread (e.g. `task:{task_id}` / `game:{id}`), newest first. Pass
149
175
  * `includeSent` to fold in the connected wallet's own replies (full
150
176
  * two-way conversation). */
@@ -332,3 +358,9 @@ export class InboxClient {
332
358
  }
333
359
  }
334
360
  }
361
+ function validateSelectedIds(ids) {
362
+ if (ids.length < 1 || ids.length > 50)
363
+ throw new InboxApiError(0, "select 1..50 message IDs");
364
+ if (ids.some(id => !/^\d{20}_[0-9a-fA-F]{8}$/.test(id)))
365
+ throw new InboxApiError(0, "msg_id must be a server-issued message ID");
366
+ }
@@ -1,2 +1,2 @@
1
- export { InboxClient, InboxApiError, type InboxClientOpts, type InboxMessage, type InboxSession, type MessagePage, type NonceSigner, type SendReceipt, type TopicId, type TopicPost, type TopicPage, type PublishReceipt, type ReportReceipt, } from "./client.js";
1
+ export { InboxClient, InboxApiError, type InboxClientOpts, type InboxMessage, type MessageReference, type MessageEnvelope, type InboxListOptions, type InboxListPage, type OpenMessageResult, type AckMessageResult, type InboxSession, type MessagePage, type NonceSigner, type SendReceipt, type TopicId, type TopicPost, type TopicPage, type PublishReceipt, type ReportReceipt, } from "./client.js";
2
2
  export { solanaNonceSigner } from "./solana.js";
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** The unified client package version. Functional APIs live in typed subpaths. */
2
- export declare const CLIENT_VERSION: "0.2.3";
2
+ export declare const CLIENT_VERSION: "0.2.4";
3
3
  export declare const CLIENT_MANIFEST_SCHEMA: "swarm.client-manifest/v1";
4
4
  export { SwarmClientError } from "./errors.js";
5
5
  export type { SwarmClientErrorCode, SwarmClientErrorOptions } from "./errors.js";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  /** The unified client package version. Functional APIs live in typed subpaths. */
2
- export const CLIENT_VERSION = "0.2.3";
2
+ export const CLIENT_VERSION = "0.2.4";
3
3
  export const CLIENT_MANIFEST_SCHEMA = "swarm.client-manifest/v1";
4
4
  export { SwarmClientError } from "./errors.js";
@@ -49,7 +49,7 @@ export class SwarmClient {
49
49
  async toolCount() {
50
50
  const headers = { "Content-Type": "application/json", Accept: "application/json, text/event-stream" };
51
51
  try {
52
- const init = await this.fetchImpl(`${this.baseUrl}/mcp`, { method: "POST", headers, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2025-03-26", capabilities: {}, clientInfo: { name: "swarm-client", version: "0.2.3" } } }) });
52
+ const init = await this.fetchImpl(`${this.baseUrl}/mcp`, { method: "POST", headers, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2025-03-26", capabilities: {}, clientInfo: { name: "swarm-client", version: "0.2.4" } } }) });
53
53
  if (!init.ok)
54
54
  return null;
55
55
  const session = init.headers.get("mcp-session-id");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swarm-tips/client",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Typed browser and Node.js clients for Swarm Tips, Shillbot, and Coordination Game",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",