@spacemolt/lib 4.6.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.
@@ -0,0 +1,305 @@
1
+ /**
2
+ * Multi-account client.
3
+ *
4
+ * Manages N authenticated `Account` connections, persisting their credentials
5
+ * through a pluggable `CredentialStore`. Connecting many accounts is staggered
6
+ * to stay under the server's per-IP login/connection rate limits — the server
7
+ * itself advises "reuse one session per bot for its lifetime", so the intended
8
+ * pattern is: add accounts once, `connectAll()` at startup, keep them open.
9
+ */
10
+ import { Account } from './account.js';
11
+ import { type AuthCredentials, type CredentialStore, type StoredAccount } from './auth/credentials.js';
12
+ import type { WebSocketFactory } from './transport/socket.js';
13
+ import type { ReconnectOptions, RegisterParams, RegisterResult } from './account.js';
14
+ import { CatalogCache } from './data/catalog.js';
15
+ import { MapCache } from './data/map.js';
16
+ import { type ClerkPlayer } from './auth/clerk.js';
17
+ import { type ConnectionClosedError } from './errors.js';
18
+ export interface SpacemoltClientOptions {
19
+ /** WebSocket URL of the v2 endpoint. */
20
+ url?: string;
21
+ /** Where credentials live. Defaults to an in-memory store. */
22
+ store?: CredentialStore;
23
+ /** Inject a WebSocket implementation (tests, custom runtimes). */
24
+ webSocketFactory?: WebSocketFactory;
25
+ /** Seed each account's state cache after auth (see Account). Default true. */
26
+ seedState?: boolean;
27
+ /**
28
+ * Delay between connecting accounts. Default is derived from
29
+ * `connectBatchSize` (`60_000 / connectBatchSize` — 600ms for the default
30
+ * batch size of 100), guaranteeing a batch can't finish in under a minute
31
+ * regardless of how fast each connect() actually completes — a flat
32
+ * 250ms doesn't guarantee that (see `connectIds`). Set explicitly to
33
+ * override that computed default with your own value (trusted as-is, not
34
+ * a floor) — e.g. for a smaller test fleet where finishing fast is fine.
35
+ */
36
+ connectStaggerMs?: number;
37
+ /**
38
+ * Max accounts to connect before pausing for `connectBatchWaitMs`, so a
39
+ * fleet never actually trips the server's per-IP WS-connection rate limit
40
+ * (hitting it risks an IP-level timeout/ban on repeat offense — better to
41
+ * never ask). Matches the server's default connection cap (100/min), so a
42
+ * fleet at or under this size behaves exactly like a single stagger pass,
43
+ * same as before; a larger fleet connects in batches of this size instead.
44
+ * Default 100.
45
+ */
46
+ connectBatchSize?: number;
47
+ /**
48
+ * Pause between batches once `connectBatchSize` is exceeded, letting the
49
+ * server's per-IP rate-limit window (1 minute by default) fully roll over
50
+ * before the next batch starts. Default 65000 (65s — a margin over the
51
+ * server's 1-minute window).
52
+ */
53
+ connectBatchWaitMs?: number;
54
+ /**
55
+ * Reconnect an account after an unexpected disconnect (a dropped
56
+ * WebSocket after it was already connected — see `connectRetry` for the
57
+ * initial connect instead). Default `true`. Driven by the client itself
58
+ * (not each `Account`'s own standalone reconnect logic), reconnecting the
59
+ * *same* `Account` instance in place (`Account.reconnectOnce` — fresh
60
+ * socket, re-authenticated, subscriptions restored) rather than replacing
61
+ * it, so anything holding a direct reference (e.g. a running loop) keeps
62
+ * working transparently. Paced through the same rate-limited queue used
63
+ * for the initial connect — see `handleAccountDisconnected` — so a mass
64
+ * disconnect (e.g. every account dropped at once by a game-server restart)
65
+ * reconnects the fleet through the same `connectBatchSize`/
66
+ * `connectStaggerMs`/`connectBatchWaitMs` pacing, instead of every account
67
+ * racing an independent timer. Pass `false` to leave a dropped account
68
+ * disconnected instead (see `onAccountDisconnected`). If you pass a
69
+ * `ReconnectOptions` object, only its truthiness matters — reconnect
70
+ * pacing comes from `connectRetry` (the same backoff config used for the
71
+ * initial connect), not this option's own `maxRetries`/`baseDelayMs`/
72
+ * `maxDelayMs`. Never reconnects after a deliberate `close()`/`remove()`,
73
+ * a `session_replaced`, or an `auth_timeout`. Token-only accounts can't
74
+ * reconnect (the token is single-use).
75
+ */
76
+ reconnect?: boolean | ReconnectOptions;
77
+ /** HTTP origin for bulk data fetches. Defaults to the origin of `url`. */
78
+ httpBaseUrl?: string;
79
+ /**
80
+ * Clerk API key for `connectOwned()` / `listOwnedPlayers()` — connect every
81
+ * game account the Clerk user owns without storing per-account passwords.
82
+ * Generate one from the website; keep it secret.
83
+ */
84
+ clerkApiKey?: string;
85
+ /** Inject a `fetch` implementation (tests, custom runtimes). */
86
+ fetchImpl?: typeof fetch;
87
+ /**
88
+ * How long to wait for the server's `welcome` frame (post-WS-upgrade) and
89
+ * for a `logged_in`/error response to an auth attempt, before giving up.
90
+ * Without this, a connection the server silently never completes (accepts
91
+ * the WS upgrade but never responds) hangs forever with no error and no
92
+ * retry — this can't be told apart from "still connecting" from the
93
+ * outside. Default 15000 (15s — generous over the ~150-300ms a healthy
94
+ * connect actually takes, per live measurement).
95
+ */
96
+ connectTimeoutMs?: number;
97
+ /**
98
+ * How long to wait for a query's response before giving up. Unlike a
99
+ * mutation (which can legitimately take minutes, e.g. a travel/jump's
100
+ * transit time), a query has no legitimate reason to take long — bounding
101
+ * it turns a silently-dropped response into a clean error instead of a
102
+ * permanent hang. Default 15000 (15s).
103
+ */
104
+ queryTimeoutMs?: number;
105
+ /**
106
+ * How long to wait, after a mutation's pending ack arrives, for its final
107
+ * `action_result`/`action_error` outcome before giving up. Generous by
108
+ * design — some mutations legitimately take many ticks (a jump/travel's
109
+ * transit time is distance-based and can run minutes) — but not infinite:
110
+ * without this, a single silently-dropped outcome frame hangs forever AND
111
+ * wedges every subsequent mutation on that account behind it (mutations
112
+ * are serialized per account). Default 600000 (10 minutes).
113
+ */
114
+ mutationTimeoutMs?: number;
115
+ /**
116
+ * Fallback safety net: retry a failed `connect()` (the raw WebSocket
117
+ * handshake, before any auth frame) with backoff instead of dropping the
118
+ * account. `connectBatchSize`/`connectBatchWaitMs` above are the primary
119
+ * defense — they're sized to avoid ever tripping the server's per-IP
120
+ * WS-connection rate limit in the first place — but this covers the
121
+ * unexpected case anyway (e.g. other traffic sharing the IP eating into
122
+ * the budget), since a 429 on the handshake surfaces as a plain
123
+ * `ConnectionClosedError` — not a `SpacemoltError` with `code:
124
+ * 'rate_limited'` — so it can't be told apart from a genuine connection
125
+ * failure and isn't covered by `Account`'s targeted rate-limit retry.
126
+ * Default `true` (enabled). Pass `false` to disable and fail fast instead.
127
+ */
128
+ connectRetry?: boolean | ReconnectOptions;
129
+ }
130
+ export declare class SpacemoltClient {
131
+ private readonly opts;
132
+ private readonly store;
133
+ private readonly connected;
134
+ private catalogCache?;
135
+ private mapCache?;
136
+ private clerkSource?;
137
+ private readonly accountConnectedListeners;
138
+ private readonly accountReconnectedListeners;
139
+ private readonly accountDisconnectedListeners;
140
+ /** Paced by connectBatchSize/connectStaggerMs/connectBatchWaitMs — the same limiter drains both the initial fleet connect and later reconnects, see `enqueueRateLimited`. */
141
+ private readonly rateLimitedQueue;
142
+ private rateLimitedQueueDraining;
143
+ /**
144
+ * Ids currently mid-reconnect. Guards against a socket dropping again
145
+ * *during* `reconnectAccountInPlace` (e.g. another rate-limit close before
146
+ * auth completes) re-firing `handleAccountDisconnected` and enqueuing a
147
+ * second, parallel reconnect chain for the same id — the drop instead
148
+ * surfaces as a failure inside the one already-running `withConnectRetry`
149
+ * loop, which retries it itself.
150
+ */
151
+ private readonly reconnectingIds;
152
+ constructor(opts?: SpacemoltClientOptions);
153
+ /**
154
+ * Fires the first time an id becomes a connected+authenticated `Account`
155
+ * instance — the initial connect (`connect`/`connectAll`/`connectOwned`/
156
+ * `register`). Does NOT fire again for a later reconnect after an
157
+ * unexpected disconnect — that reuses the same instance in place (see
158
+ * `Account.reconnectOnce`) rather than creating a new one, so there's
159
+ * nothing new to index/re-wire; see `onAccountReconnected` for that event
160
+ * instead. Unlike `connectAll`'s/`connectOwned`'s per-call `onConnect`
161
+ * option (scoped to that one call), this is a persistent subscription.
162
+ * Returns an unsubscribe function.
163
+ */
164
+ onAccountConnected(listener: (account: Account) => void): () => void;
165
+ /**
166
+ * Fires after an already-connected account's connection is restored
167
+ * following an unexpected disconnect — the same `Account` instance
168
+ * throughout, reconnected in place. Purely informational (e.g. logging);
169
+ * nothing needs re-indexing since the instance never changed. Returns an
170
+ * unsubscribe function.
171
+ */
172
+ onAccountReconnected(listener: (account: Account) => void): () => void;
173
+ /**
174
+ * Fires when an account is dropped for good: a terminal close (session
175
+ * replaced by another connection, or an auth timeout) that this client
176
+ * deliberately does not reconnect after, or a reconnect attempt that
177
+ * exhausted its retries. Silent otherwise today — this is the only
178
+ * visibility a caller has into that. Returns an unsubscribe function.
179
+ */
180
+ onAccountDisconnected(listener: (id: string, err: ConnectionClosedError) => void): () => void;
181
+ private notifyAccountConnected;
182
+ private notifyAccountReconnected;
183
+ private notifyAccountDisconnected;
184
+ /** HTTP origin used for bulk data fetches (catalog, map). */
185
+ get httpBaseUrl(): string;
186
+ /** The bulk catalog, fetched once and cached. Pass `force` to refetch. */
187
+ catalog(force?: boolean): Promise<CatalogCache>;
188
+ /** The static galaxy map, fetched once and cached. Pass `force` to refetch. */
189
+ map(force?: boolean): Promise<MapCache>;
190
+ /** The credential store backing this client. */
191
+ get credentialStore(): CredentialStore;
192
+ /** Persist username/password credentials. Returns the account id (username). */
193
+ addLogin(username: string, password: string): Promise<string>;
194
+ /** Persist a login-token credential under an explicit id. */
195
+ addToken(id: string, token: string): Promise<string>;
196
+ /**
197
+ * Register a brand-new account: connect, register, and persist the generated
198
+ * password as login credentials (keyed by username) for future sessions.
199
+ */
200
+ register(params: RegisterParams): Promise<{
201
+ account: Account;
202
+ result: RegisterResult;
203
+ }>;
204
+ /** Resolves `connectRetry` into a concrete backoff config. */
205
+ private connectRetryConfig;
206
+ /**
207
+ * Retry `attempt` with the shared `connectRetry` backoff, honoring a 4003
208
+ * `connection_rate_limited` close's `retry_after` hint over the
209
+ * exponential fallback. Shared by the initial connect (`connect`) and an
210
+ * in-place reconnect (`reconnectAccountInPlace`) so both back off
211
+ * identically.
212
+ */
213
+ private withConnectRetry;
214
+ /**
215
+ * Connect and authenticate one stored account. Idempotent per id.
216
+ *
217
+ * Retries the whole connect+authenticate sequence with backoff on failure
218
+ * (see `connectRetry`) — a fleet large enough to exceed the per-IP
219
+ * WS-connection rate limit sees some accounts rejected at the handshake,
220
+ * which self-heals once the server's rate-limit window rolls over.
221
+ */
222
+ connect(id: string): Promise<Account>;
223
+ /** Connect every stored account, staggered to respect rate limits. */
224
+ connectAll(opts?: {
225
+ onConnect?: (account: Account) => void;
226
+ }): Promise<Account[]>;
227
+ /** List the player accounts the Clerk user owns. Requires `clerkApiKey`. */
228
+ listOwnedPlayers(): Promise<ClerkPlayer[]>;
229
+ /**
230
+ * Connect every account the Clerk user owns (optionally filtered), staggered
231
+ * to respect rate limits. Stores a `clerk` credential per player — each
232
+ * account mints a fresh single-use WS token on connect and reconnect, so no
233
+ * passwords are persisted. Requires `clerkApiKey`.
234
+ *
235
+ * A fleet-wide call can legitimately take minutes (see `connectIds`'s
236
+ * pacing) — `onConnect` fires as each account finishes, so a caller can
237
+ * index/use accounts incrementally instead of only after the whole batch
238
+ * settles (the returned promise still doesn't resolve until every account
239
+ * has been attempted).
240
+ */
241
+ connectOwned(opts?: {
242
+ filter?: (player: ClerkPlayer) => boolean;
243
+ onConnect?: (account: Account) => void;
244
+ }): Promise<Account[]>;
245
+ private requireClerkSource;
246
+ /**
247
+ * Connect the given stored ids, staggered to respect rate limits. At or
248
+ * under `connectBatchSize` accounts this is a single stagger pass — the
249
+ * same behavior as before batching existed. Past that, it pauses for
250
+ * `connectBatchWaitMs` between batches so the fleet never actually asks
251
+ * for more connections than the server's per-IP window allows. A single
252
+ * account failing to connect must not abort the rest of the batch — it's
253
+ * logged and skipped so the caller still gets every account that succeeded.
254
+ *
255
+ * `onConnect` fires synchronously as each id finishes (success only) so a
256
+ * caller managing many accounts (e.g. indexing them for lookup) doesn't
257
+ * have to wait for the entire — potentially minutes-long — batch before
258
+ * any single account becomes usable.
259
+ *
260
+ * Pacing itself is `enqueueRateLimited` — the same queue a later
261
+ * reconnect (see `handleAccountDisconnected`) competes for a slot in, so a
262
+ * reconnect landing mid-batch is paced fairly alongside the rest of the
263
+ * fleet instead of racing it on a separate timer.
264
+ */
265
+ private connectIds;
266
+ /**
267
+ * Enqueues `task`, paced by `connectBatchSize`/`connectStaggerMs`/
268
+ * `connectBatchWaitMs` — the single rate limiter shared by the initial
269
+ * fleet connect (`connectIds`) and every later reconnect
270
+ * (`handleAccountDisconnected`), so the server's per-IP WS-connection cap
271
+ * is respected by one mechanism, not a parallel one for each case. Returns
272
+ * a promise settling with `task`'s own outcome once its turn comes up.
273
+ */
274
+ private enqueueRateLimited;
275
+ private drainRateLimitedQueue;
276
+ /** The connected account for an id, if connected. */
277
+ account(id: string): Account | undefined;
278
+ /** All currently-connected accounts. */
279
+ accounts(): Account[];
280
+ /** Ids of all currently-connected accounts. */
281
+ ids(): string[];
282
+ /** Close and forget an account, and remove its stored credentials. */
283
+ remove(id: string): Promise<void>;
284
+ /** Close every connection (credentials remain in the store). */
285
+ closeAll(): void;
286
+ /**
287
+ * Reconnection for a client-managed account is driven here, not by the
288
+ * `Account`'s own (disabled — see `createAccount`) reconnect logic: an
289
+ * unexpected disconnect closes every affected account at once (e.g. a
290
+ * game-server restart), and reconnecting through the same rate-limited
291
+ * queue used for the initial connect — instead of each account racing its
292
+ * own independent timer — is what keeps a mass reconnect from re-tripping
293
+ * the server's per-IP WS-connection rate limit right as it recovers.
294
+ *
295
+ * Reconnects in place (`Account.reconnectOnce`, same instance) rather than
296
+ * replacing the account with a new one: anything holding a direct
297
+ * reference to this `Account` (e.g. a running loop, which captures it once
298
+ * at start) keeps working transparently once the connection is restored,
299
+ * instead of being welded to a now-dead instance nobody is driving.
300
+ */
301
+ private handleAccountDisconnected;
302
+ private createAccount;
303
+ private capturePlayerId;
304
+ }
305
+ export type { AuthCredentials, CredentialStore, StoredAccount };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Local copy of the bulk game catalog (`GET /api/catalog.json`).
3
+ *
4
+ * Reference data — ships, items, recipes, skills, facilities — that changes
5
+ * only on a server release. Fetched once over HTTP and cached; the entry
6
+ * shapes aren't in the v2 OpenAPI spec, so they're typed loosely with `id`-keyed
7
+ * lookups. Public, ETag-cached, ~1h max-age on the server.
8
+ */
9
+ export interface CatalogEntry {
10
+ id?: string;
11
+ [key: string]: unknown;
12
+ }
13
+ export interface Catalog {
14
+ version?: string;
15
+ ships: CatalogEntry[];
16
+ items: CatalogEntry[];
17
+ recipes: CatalogEntry[];
18
+ skills: CatalogEntry[];
19
+ facilities: CatalogEntry[];
20
+ }
21
+ export declare function fetchCatalog(httpBaseUrl: string): Promise<Catalog>;
22
+ export declare class CatalogCache {
23
+ readonly catalog: Catalog;
24
+ private readonly indexes;
25
+ constructor(catalog: Catalog);
26
+ /** Fetch the catalog and wrap it in a cache. */
27
+ static load(httpBaseUrl: string): Promise<CatalogCache>;
28
+ get version(): string | undefined;
29
+ ship(id: string): CatalogEntry | undefined;
30
+ item(id: string): CatalogEntry | undefined;
31
+ recipe(id: string): CatalogEntry | undefined;
32
+ skill(id: string): CatalogEntry | undefined;
33
+ facility(id: string): CatalogEntry | undefined;
34
+ get ships(): readonly CatalogEntry[];
35
+ get items(): readonly CatalogEntry[];
36
+ get recipes(): readonly CatalogEntry[];
37
+ get skills(): readonly CatalogEntry[];
38
+ get facilities(): readonly CatalogEntry[];
39
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Local copy of the static galaxy map (`GET /api/map`).
3
+ *
4
+ * Returns `{ systems, empires }` where `empires` maps an empire id to a display
5
+ * colour. System shapes aren't in the v2 spec, so they're typed loosely with
6
+ * `id`-keyed lookups. Static per release; the separate `/api/map/activity`
7
+ * overlay (online counts, battles) changes frequently and is not cached here.
8
+ */
9
+ export interface MapSystem {
10
+ id?: string;
11
+ [key: string]: unknown;
12
+ }
13
+ export interface GalaxyMap {
14
+ systems: MapSystem[];
15
+ empires: Record<string, string>;
16
+ }
17
+ export declare function fetchMap(httpBaseUrl: string): Promise<GalaxyMap>;
18
+ export declare class MapCache {
19
+ readonly map: GalaxyMap;
20
+ private readonly bySystemId;
21
+ constructor(map: GalaxyMap);
22
+ /** Fetch the galaxy map and wrap it in a cache. */
23
+ static load(httpBaseUrl: string): Promise<MapCache>;
24
+ system(id: string): MapSystem | undefined;
25
+ get systems(): readonly MapSystem[];
26
+ /** Empire id -> display colour. */
27
+ get empires(): Record<string, string>;
28
+ }
29
+ /** Derive the HTTP origin from a WebSocket URL (wss://host/ws/v2 -> https://host). */
30
+ export declare function httpBaseFromWs(wsUrl: string): string;
@@ -0,0 +1,52 @@
1
+ /** Error types surfaced by the library. */
2
+ import type { ActionErrorFrame, ErrorFrame } from './protocol.js';
3
+ /**
4
+ * A server-reported error. Carries the machine-readable `code` from the
5
+ * server's error/action_error frame plus any structured details.
6
+ */
7
+ export declare class SpacemoltError extends Error {
8
+ readonly code: string;
9
+ readonly details?: Record<string, unknown>;
10
+ readonly requestId?: string;
11
+ /** Set when the failure came from a two-phase mutation outcome. */
12
+ readonly command?: string;
13
+ /** Game tick of the failure, for action_error outcomes. */
14
+ readonly tick?: number;
15
+ /** On `action_pending` errors: the mutation already queued. */
16
+ readonly pendingCommand?: string;
17
+ constructor(code: string, message: string, opts?: {
18
+ details?: Record<string, unknown>;
19
+ requestId?: string;
20
+ command?: string;
21
+ tick?: number;
22
+ pendingCommand?: string;
23
+ });
24
+ }
25
+ export declare function errorFromFrame(frame: ErrorFrame): SpacemoltError;
26
+ export declare function errorFromActionFrame(frame: ActionErrorFrame): SpacemoltError;
27
+ /** Raised against every in-flight request when the socket closes. */
28
+ export declare class ConnectionClosedError extends Error {
29
+ readonly code?: number;
30
+ readonly reason?: string;
31
+ constructor(message?: string, code?: number, reason?: string);
32
+ }
33
+ /**
34
+ * Custom WS close codes the server documents (as of gameserver v0.471.4):
35
+ * `4001` session_replaced (another connection took your slot — don't
36
+ * reconnect, you'd just fight it), `4002` auth_timeout (connected but didn't
37
+ * authenticate in time), `4003` connection_rate_limited (exceeded the
38
+ * 100/min-per-IP WS-connection cap — the close reason carries a
39
+ * `retry_after=<seconds>` hint; honor it before reconnecting).
40
+ */
41
+ export declare const CLOSE_CODE: {
42
+ readonly SESSION_REPLACED: 4001;
43
+ readonly AUTH_TIMEOUT: 4002;
44
+ readonly CONNECTION_RATE_LIMITED: 4003;
45
+ };
46
+ /**
47
+ * Parses the `retry_after=<seconds>` hint the server includes on a `4003`
48
+ * (connection_rate_limited) close reason. Returns the wait time in
49
+ * milliseconds, or `undefined` if `err` isn't a rate-limited close or carries
50
+ * no parseable hint.
51
+ */
52
+ export declare function retryAfterMsFromClose(err: unknown): number | undefined;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Typed event dispatch for server push frames.
3
+ *
4
+ * Callbacks (`on`/`onAny`) and async iterators (`stream`/`anyStream`) are both
5
+ * supported. The Account facade adds the per-msg_type payload typing on top of
6
+ * this (loosely-typed) core.
7
+ */
8
+ import type { RawFrame } from '../protocol.js';
9
+ type PayloadHandler = (payload: unknown) => void;
10
+ type FrameHandler = (frame: RawFrame) => void;
11
+ /**
12
+ * An async iterator over events of one kind, with internal buffering so a slow
13
+ * consumer doesn't drop frames. Breaking out of a `for await` calls `return()`,
14
+ * which unsubscribes via the `onClose` callback.
15
+ */
16
+ export declare class EventStream<T> implements AsyncIterableIterator<T> {
17
+ private readonly onClose;
18
+ private readonly buffer;
19
+ private readonly waiting;
20
+ private ended;
21
+ constructor(onClose: () => void);
22
+ /** Internal: deliver a value to the consumer or buffer it. */
23
+ push(value: T): void;
24
+ /** Internal: end the stream (e.g. on disconnect). */
25
+ end(): void;
26
+ next(): Promise<IteratorResult<T>>;
27
+ return(): Promise<IteratorResult<T>>;
28
+ [Symbol.asyncIterator](): AsyncIterableIterator<T>;
29
+ }
30
+ export declare class TypedEmitter {
31
+ private readonly handlers;
32
+ private readonly anyHandlers;
33
+ private readonly streams;
34
+ private readonly anyStreams;
35
+ /** Listen for one msg_type. Returns an unsubscribe function. */
36
+ on(type: string, handler: PayloadHandler): () => void;
37
+ /** Listen for every push frame. Returns an unsubscribe function. */
38
+ onAny(handler: FrameHandler): () => void;
39
+ /** Async-iterate one msg_type's payloads. */
40
+ stream(type: string): EventStream<unknown>;
41
+ /** Async-iterate every push frame. */
42
+ anyStream(): EventStream<RawFrame>;
43
+ /**
44
+ * Dispatch a push frame to all matching listeners and streams. Each
45
+ * callback is isolated with try/catch — this is the single funnel every
46
+ * `account.on(type, ...)`/`onAny` consumer runs through, invoked directly
47
+ * from `routeFrame`'s `default:` case with nothing above it to contain a
48
+ * throw. Without isolation, one listener throwing would skip every other
49
+ * listener/stream for this frame and propagate out through `routeFrame`
50
+ * into the WebSocket's `message` event handler, which has no try/catch of
51
+ * its own either.
52
+ */
53
+ emit(frame: RawFrame): void;
54
+ private safeCall;
55
+ /** End every open stream (on disconnect). Callback listeners are kept. */
56
+ closeStreams(): void;
57
+ }
58
+ export {};
@@ -0,0 +1,25 @@
1
+ /** The gameserver spec version this command surface was generated from. */
2
+ export declare const GENERATED_SPEC_VERSION = "v0.485.0";
3
+ export type ToolName = "spacemolt" | "spacemolt_auth" | "spacemolt_battle" | "spacemolt_citizenship" | "spacemolt_drone" | "spacemolt_facility" | "spacemolt_faction" | "spacemolt_faction_admin" | "spacemolt_faction_commerce" | "spacemolt_fleet" | "spacemolt_intel" | "spacemolt_market" | "spacemolt_salvage" | "spacemolt_ship" | "spacemolt_social" | "spacemolt_storage" | "spacemolt_transfer";
4
+ export type ActionName = "abandon_mission" | "accept" | "accept_ally" | "accept_invite" | "accept_mission" | "accept_peace" | "advance" | "allow_faction" | "allow_player" | "analyze_market" | "apply" | "attack" | "ban" | "base_cost" | "board" | "browse_for_sale" | "browse_ships" | "build" | "buy" | "buy_listed_ship" | "buy_listing" | "buy_ship_license" | "cancel_commission" | "cancel_listing" | "cancel_mission" | "cancel_order" | "cancel_ship_buy_order" | "cancel_ship_listing" | "captains_log_add" | "captains_log_delete" | "captains_log_get" | "captains_log_list" | "chat" | "claim" | "cloak" | "commission_quote" | "commission_ship" | "commission_status" | "complete_mission" | "completed_missions" | "craft" | "create" | "create_buy_order" | "create_note" | "create_role" | "create_sell_order" | "declare_war" | "decline" | "decline_invite" | "decline_mission" | "delete_note" | "delete_role" | "delete_room" | "deploy" | "deploy_outpost" | "deposit" | "disband" | "disembark" | "dismantle" | "distress_signal" | "dock" | "edit" | "edit_role" | "engage" | "espionage" | "estimate_purchase" | "facility_set_description" | "faction_build" | "faction_dismantle" | "faction_list" | "faction_owned" | "faction_upgrade" | "find_route" | "forum_create_thread" | "forum_delete_reply" | "forum_delete_thread" | "forum_get_thread" | "forum_list" | "forum_reply" | "forum_upvote" | "found_station" | "garages" | "get" | "get_achievements" | "get_action_log" | "get_active_missions" | "get_base" | "get_cargo" | "get_chat_history" | "get_commands" | "get_empire_info" | "get_faction_achievements" | "get_guide" | "get_invites" | "get_location" | "get_map" | "get_missions" | "get_nearby" | "get_notes" | "get_notification_settings" | "get_notifications" | "get_player" | "get_poi" | "get_queue" | "get_ship" | "get_skills" | "get_state" | "get_status" | "get_system" | "get_system_agents" | "get_tax_estimate" | "get_trades" | "get_version" | "hunt" | "info" | "install_mod" | "insure" | "intel_status" | "invite" | "jettison" | "job_add" | "job_cancel" | "job_list" | "job_reorder" | "join" | "jump" | "kick" | "leave" | "list" | "list_for_sale" | "list_missions" | "list_passengers" | "list_ship_for_sale" | "list_ships" | "list_station_passengers" | "load" | "load_passenger" | "login" | "login_token" | "logout" | "loot" | "mine" | "modify_order" | "mute_notifications" | "name" | "owned" | "personal_build" | "personal_decorate" | "personal_visit" | "petition" | "place_ship_buy_order" | "policies" | "post_mission" | "prepay_tax" | "promote" | "propose_ally" | "propose_peace" | "query_intel" | "query_trade_intel" | "quote" | "read_note" | "recall" | "recycle" | "refit_ship" | "refuel" | "register" | "release" | "reload" | "remove_ally" | "remove_enemy" | "remove_faction" | "remove_player" | "rename_ship" | "renounce" | "repair" | "repair_module" | "retreat" | "rooms" | "scan" | "scan_poi" | "scrap" | "scrap_ship" | "search_systems" | "self_destruct" | "sell" | "sell_ship" | "sell_ship_to_order" | "set_access" | "set_build_policy" | "set_colors" | "set_description" | "set_enemy" | "set_home" | "set_market_fee" | "set_name" | "set_output_price" | "set_public" | "set_refuel_price" | "set_repair_price" | "set_service_access" | "set_status" | "stance" | "station_info" | "station_set_name" | "status" | "submit_intel" | "submit_trade_intel" | "subscribe_market" | "subscribe_observation" | "supply_commission" | "survey_system" | "switch_ship" | "target" | "tax_estimate" | "tow" | "trade_accept" | "trade_cancel" | "trade_decline" | "trade_intel_status" | "trade_offer" | "transfer" | "travel" | "types" | "unban" | "undock" | "uninstall_mod" | "unload" | "unload_passenger" | "unmute_notifications" | "unsubscribe_market" | "unsubscribe_observation" | "upgrade" | "upgrades" | "upload" | "use_item" | "view" | "view_completed_mission" | "view_market" | "view_orders" | "view_ship_buy_orders" | "visit_room" | "withdraw" | "withdraw_invite" | "wrecks" | "write_note" | "write_room";
5
+ export interface ActionParam {
6
+ readonly name: string;
7
+ readonly type: string;
8
+ readonly required: boolean;
9
+ readonly description?: string;
10
+ readonly enumValues?: readonly string[];
11
+ readonly positional?: number;
12
+ }
13
+ export interface ActionDef {
14
+ readonly tool: ToolName;
15
+ readonly action: ActionName;
16
+ readonly kind: 'query' | 'mutation';
17
+ readonly summary?: string;
18
+ readonly params: readonly ActionParam[];
19
+ /** TS type name of the query's structuredContent response (queries with a published response schema). */
20
+ readonly responseType?: string;
21
+ /** TS type name of the mutation's delta.details response (mutations with a published details schema). */
22
+ readonly detailsType?: string;
23
+ }
24
+ /** Every v2 command, keyed by "tool/action". */
25
+ export declare const ACTIONS: Record<string, ActionDef>;